readdir.c 26.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4
/*
 *   fs/cifs/readdir.c
 *
 *   Directory search handling
S
Steve French 已提交
5
 *
S
Steve French 已提交
6
 *   Copyright (C) International Business Machines  Corp., 2004, 2008
L
Linus Torvalds 已提交
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *   Author(s): Steve French (sfrench@us.ibm.com)
 *
 *   This library is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published
 *   by the Free Software Foundation; either version 2.1 of the License, or
 *   (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with this library; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
#include <linux/fs.h>
D
Dave Kleikamp 已提交
24
#include <linux/pagemap.h>
25
#include <linux/slab.h>
L
Linus Torvalds 已提交
26 27 28 29 30 31 32 33 34
#include <linux/stat.h>
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
#include "cifs_unicode.h"
#include "cifs_debug.h"
#include "cifs_fs_sb.h"
#include "cifsfs.h"

35 36 37 38 39 40 41
/*
 * To be safe - for UCS to UTF-8 with strings loaded with the rare long
 * characters alloc more to account for such multibyte target UTF-8
 * characters.
 */
#define UNICODE_NAME_MAX ((4 * NAME_MAX) + 2)

42 43
#ifdef CONFIG_CIFS_DEBUG2
static void dump_cifs_file_struct(struct file *file, char *label)
L
Linus Torvalds 已提交
44
{
S
Steve French 已提交
45
	struct cifsFileInfo *cf;
L
Linus Torvalds 已提交
46

47
	if (file) {
L
Linus Torvalds 已提交
48
		cf = file->private_data;
49
		if (cf == NULL) {
50
			cFYI(1, "empty cifs private file data");
L
Linus Torvalds 已提交
51 52
			return;
		}
S
Steve French 已提交
53
		if (cf->invalidHandle)
54
			cFYI(1, "invalid handle");
S
Steve French 已提交
55
		if (cf->srch_inf.endOfSearch)
56
			cFYI(1, "end of search");
S
Steve French 已提交
57
		if (cf->srch_inf.emptyDir)
58
			cFYI(1, "empty dir");
L
Linus Torvalds 已提交
59
	}
60
}
61 62 63 64
#else
static inline void dump_cifs_file_struct(struct file *file, char *label)
{
}
65
#endif /* DEBUG2 */
L
Linus Torvalds 已提交
66

67 68 69 70 71 72 73 74 75 76 77 78
/*
 * Find the dentry that matches "name". If there isn't one, create one. If it's
 * a negative dentry or the uniqueid changed, then drop it and recreate it.
 */
static struct dentry *
cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
		    struct cifs_fattr *fattr)
{
	struct dentry *dentry, *alias;
	struct inode *inode;
	struct super_block *sb = parent->d_inode->i_sb;

79
	cFYI(1, "For %s", name->name);
80

81
	if (parent->d_op && parent->d_op->d_hash)
N
Nick Piggin 已提交
82
		parent->d_op->d_hash(parent, parent->d_inode, name);
83 84 85
	else
		name->hash = full_name_hash(name->name, name->len);

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
	dentry = d_lookup(parent, name);
	if (dentry) {
		/* FIXME: check for inode number changes? */
		if (dentry->d_inode != NULL)
			return dentry;
		d_drop(dentry);
		dput(dentry);
	}

	dentry = d_alloc(parent, name);
	if (dentry == NULL)
		return NULL;

	inode = cifs_iget(sb, fattr);
	if (!inode) {
		dput(dentry);
		return NULL;
	}

	alias = d_materialise_unique(dentry, inode);
	if (alias != NULL) {
		dput(dentry);
		if (IS_ERR(alias))
			return NULL;
		dentry = alias;
	}

	return dentry;
}

116 117
static void
cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
L
Linus Torvalds 已提交
118
{
119 120
	fattr->cf_uid = cifs_sb->mnt_uid;
	fattr->cf_gid = cifs_sb->mnt_gid;
L
Linus Torvalds 已提交
121

122 123 124
	if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
		fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
		fattr->cf_dtype = DT_DIR;
L
Linus Torvalds 已提交
125
	} else {
126 127
		fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
		fattr->cf_dtype = DT_REG;
L
Linus Torvalds 已提交
128 129
	}

130 131
	if (fattr->cf_cifsattrs & ATTR_READONLY)
		fattr->cf_mode &= ~S_IWUGO;
132

133 134 135 136 137 138
	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL &&
	    fattr->cf_cifsattrs & ATTR_SYSTEM) {
		if (fattr->cf_eof == 0)  {
			fattr->cf_mode &= ~S_IFMT;
			fattr->cf_mode |= S_IFIFO;
			fattr->cf_dtype = DT_FIFO;
139
		} else {
140
			/*
141 142 143
			 * trying to get the type and mode via SFU can be slow,
			 * so just call those regular files for now, and mark
			 * for reval
144
			 */
145
			fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
146 147
		}
	}
148
}
L
Linus Torvalds 已提交
149

S
Steve French 已提交
150
static void
151 152 153 154 155 156 157
cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,
		       struct cifs_sb_info *cifs_sb)
{
	memset(fattr, 0, sizeof(*fattr));
	fattr->cf_cifsattrs = le32_to_cpu(info->ExtFileAttributes);
	fattr->cf_eof = le64_to_cpu(info->EndOfFile);
	fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
158
	fattr->cf_createtime = le64_to_cpu(info->CreationTime);
159 160 161 162 163 164
	fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
	fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
	fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);

	cifs_fill_common_info(fattr, cifs_sb);
}
L
Linus Torvalds 已提交
165

S
Steve French 已提交
166
static void
167 168 169
cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info,
		       struct cifs_sb_info *cifs_sb)
{
170
	int offset = cifs_sb_master_tcon(cifs_sb)->ses->server->timeAdj;
L
Linus Torvalds 已提交
171

172 173 174 175 176 177 178 179 180 181 182 183 184
	memset(fattr, 0, sizeof(*fattr));
	fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate,
					    info->LastAccessTime, offset);
	fattr->cf_ctime = cnvrtDosUnixTm(info->LastWriteDate,
					    info->LastWriteTime, offset);
	fattr->cf_mtime = cnvrtDosUnixTm(info->LastWriteDate,
					    info->LastWriteTime, offset);

	fattr->cf_cifsattrs = le16_to_cpu(info->Attributes);
	fattr->cf_bytes = le32_to_cpu(info->AllocationSize);
	fattr->cf_eof = le32_to_cpu(info->DataSize);

	cifs_fill_common_info(fattr, cifs_sb);
L
Linus Torvalds 已提交
185 186
}

187 188 189 190 191 192 193 194 195 196 197
/* BB eventually need to add the following helper function to
      resolve NT_STATUS_STOPPED_ON_SYMLINK return code when
      we try to do FindFirst on (NTFS) directory symlinks */
/*
int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb,
			     int xid)
{
	__u16 fid;
	int len;
	int oplock = 0;
	int rc;
198
	struct cifs_tcon *ptcon = cifs_sb_tcon(cifs_sb);
199 200 201 202 203 204 205 206 207 208 209 210 211 212
	char *tmpbuffer;

	rc = CIFSSMBOpen(xid, ptcon, full_path, FILE_OPEN, GENERIC_READ,
			OPEN_REPARSE_POINT, &fid, &oplock, NULL,
			cifs_sb->local_nls,
			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
	if (!rc) {
		tmpbuffer = kmalloc(maxpath);
		rc = CIFSSMBQueryReparseLinkInfo(xid, ptcon, full_path,
				tmpbuffer,
				maxpath -1,
				fid,
				cifs_sb->local_nls);
		if (CIFSSMBClose(xid, ptcon, fid)) {
213
			cFYI(1, "Error closing temporary reparsepoint open");
214 215 216 217 218
		}
	}
}
 */

L
Linus Torvalds 已提交
219 220 221
static int initiate_cifs_search(const int xid, struct file *file)
{
	int rc = 0;
222
	char *full_path = NULL;
S
Steve French 已提交
223
	struct cifsFileInfo *cifsFile;
224
	struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
225
	struct tcon_link *tlink = NULL;
226
	struct cifs_tcon *pTcon;
L
Linus Torvalds 已提交
227

228
	if (file->private_data == NULL) {
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
		tlink = cifs_sb_tlink(cifs_sb);
		if (IS_ERR(tlink))
			return PTR_ERR(tlink);

		cifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
		if (cifsFile == NULL) {
			rc = -ENOMEM;
			goto error_exit;
		}
		file->private_data = cifsFile;
		cifsFile->tlink = cifs_get_tlink(tlink);
		pTcon = tlink_tcon(tlink);
	} else {
		cifsFile = file->private_data;
		pTcon = tlink_tcon(cifsFile->tlink);
244
	}
L
Linus Torvalds 已提交
245

246 247
	cifsFile->invalidHandle = true;
	cifsFile->srch_inf.endOfSearch = false;
L
Linus Torvalds 已提交
248

249
	full_path = build_path_from_dentry(file->f_path.dentry);
250 251 252 253
	if (full_path == NULL) {
		rc = -ENOMEM;
		goto error_exit;
	}
L
Linus Torvalds 已提交
254

255
	cFYI(1, "Full path: %s start at: %lld", full_path, file->f_pos);
L
Linus Torvalds 已提交
256

257
ffirst_retry:
L
Linus Torvalds 已提交
258
	/* test for Unix extensions */
259 260
	/* but now check for them on the share/mount not on the SMB session */
/*	if (pTcon->ses->capabilities & CAP_UNIX) { */
S
Steve French 已提交
261
	if (pTcon->unix_ext)
262
		cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX;
S
Steve French 已提交
263
	else if ((pTcon->ses->capabilities &
264 265
			(CAP_NT_SMBS | CAP_NT_FIND)) == 0) {
		cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD;
L
Linus Torvalds 已提交
266 267 268 269 270 271
	} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
		cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
	} else /* not srvinos - BB fixme add check for backlevel? */ {
		cifsFile->srch_inf.info_level = SMB_FIND_FILE_DIRECTORY_INFO;
	}

S
Steve French 已提交
272
	rc = CIFSFindFirst(xid, pTcon, full_path, cifs_sb->local_nls,
273
		&cifsFile->netfid, &cifsFile->srch_inf,
S
Steve French 已提交
274
		cifs_sb->mnt_cifs_flags &
275
			CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb));
276
	if (rc == 0)
277
		cifsFile->invalidHandle = false;
S
Steve French 已提交
278
	/* BB add following call to handle readdir on new NTFS symlink errors
279 280 281
	else if STATUS_STOPPED_ON_SYMLINK
		call get_symlink_reparse_path and retry with new path */
	else if ((rc == -EOPNOTSUPP) &&
282 283 284 285
		(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
		cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM;
		goto ffirst_retry;
	}
286
error_exit:
L
Linus Torvalds 已提交
287
	kfree(full_path);
288
	cifs_put_tlink(tlink);
L
Linus Torvalds 已提交
289 290 291 292 293 294 295
	return rc;
}

/* return length of unicode string in bytes */
static int cifs_unicode_bytelen(char *str)
{
	int len;
296
	__le16 *ustr = (__le16 *)str;
L
Linus Torvalds 已提交
297

S
Steve French 已提交
298
	for (len = 0; len <= PATH_MAX; len++) {
299
		if (ustr[len] == 0)
L
Linus Torvalds 已提交
300 301
			return len << 1;
	}
302
	cFYI(1, "Unicode string longer than PATH_MAX found");
L
Linus Torvalds 已提交
303 304 305
	return len << 1;
}

306
static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level)
L
Linus Torvalds 已提交
307
{
S
Steve French 已提交
308
	char *new_entry;
S
Steve French 已提交
309
	FILE_DIRECTORY_INFO *pDirInfo = (FILE_DIRECTORY_INFO *)old_entry;
L
Linus Torvalds 已提交
310

311
	if (level == SMB_FIND_FILE_INFO_STANDARD) {
S
Steve French 已提交
312
		FIND_FILE_STANDARD_INFO *pfData;
313 314 315 316 317 318
		pfData = (FIND_FILE_STANDARD_INFO *)pDirInfo;

		new_entry = old_entry + sizeof(FIND_FILE_STANDARD_INFO) +
				pfData->FileNameLength;
	} else
		new_entry = old_entry + le32_to_cpu(pDirInfo->NextEntryOffset);
319
	cFYI(1, "new entry %p old entry %p", new_entry, old_entry);
L
Linus Torvalds 已提交
320
	/* validate that new_entry is not past end of SMB */
321
	if (new_entry >= end_of_smb) {
322 323
		cERROR(1, "search entry %p began after end of SMB %p old entry %p",
			new_entry, end_of_smb, old_entry);
L
Linus Torvalds 已提交
324
		return NULL;
325
	} else if (((level == SMB_FIND_FILE_INFO_STANDARD) &&
S
Steve French 已提交
326 327
		    (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb))
		  || ((level != SMB_FIND_FILE_INFO_STANDARD) &&
328
		   (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb)))  {
329 330
		cERROR(1, "search entry %p extends after end of SMB %p",
			new_entry, end_of_smb);
331
		return NULL;
S
Steve French 已提交
332
	} else
L
Linus Torvalds 已提交
333 334 335 336 337 338 339 340 341 342
		return new_entry;

}

#define UNICODE_DOT cpu_to_le16(0x2e)

/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */
static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile)
{
	int rc = 0;
S
Steve French 已提交
343 344
	char *filename = NULL;
	int len = 0;
L
Linus Torvalds 已提交
345

346
	if (cfile->srch_inf.info_level == SMB_FIND_FILE_UNIX) {
S
Steve French 已提交
347
		FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
L
Linus Torvalds 已提交
348
		filename = &pFindData->FileName[0];
349
		if (cfile->srch_inf.unicode) {
L
Linus Torvalds 已提交
350 351 352 353 354
			len = cifs_unicode_bytelen(filename);
		} else {
			/* BB should we make this strnlen of PATH_MAX? */
			len = strnlen(filename, 5);
		}
355
	} else if (cfile->srch_inf.info_level == SMB_FIND_FILE_DIRECTORY_INFO) {
S
Steve French 已提交
356
		FILE_DIRECTORY_INFO *pFindData =
L
Linus Torvalds 已提交
357 358 359
			(FILE_DIRECTORY_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
S
Steve French 已提交
360
	} else if (cfile->srch_inf.info_level ==
361
			SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
S
Steve French 已提交
362
		FILE_FULL_DIRECTORY_INFO *pFindData =
L
Linus Torvalds 已提交
363 364 365
			(FILE_FULL_DIRECTORY_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
366
	} else if (cfile->srch_inf.info_level ==
367
			SMB_FIND_FILE_ID_FULL_DIR_INFO) {
S
Steve French 已提交
368
		SEARCH_ID_FULL_DIR_INFO *pFindData =
L
Linus Torvalds 已提交
369 370 371
			(SEARCH_ID_FULL_DIR_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
S
Steve French 已提交
372
	} else if (cfile->srch_inf.info_level ==
373
			SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
S
Steve French 已提交
374
		FILE_BOTH_DIRECTORY_INFO *pFindData =
L
Linus Torvalds 已提交
375 376 377
			(FILE_BOTH_DIRECTORY_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
378
	} else if (cfile->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) {
S
Steve French 已提交
379
		FIND_FILE_STANDARD_INFO *pFindData =
380 381
			(FIND_FILE_STANDARD_INFO *)current_entry;
		filename = &pFindData->FileName[0];
382
		len = pFindData->FileNameLength;
L
Linus Torvalds 已提交
383
	} else {
384 385
		cFYI(1, "Unknown findfirst level %d",
			 cfile->srch_inf.info_level);
L
Linus Torvalds 已提交
386 387
	}

388 389
	if (filename) {
		if (cfile->srch_inf.unicode) {
L
Linus Torvalds 已提交
390
			__le16 *ufilename = (__le16 *)filename;
391
			if (len == 2) {
L
Linus Torvalds 已提交
392
				/* check for . */
393
				if (ufilename[0] == UNICODE_DOT)
L
Linus Torvalds 已提交
394
					rc = 1;
395
			} else if (len == 4) {
L
Linus Torvalds 已提交
396
				/* check for .. */
397
				if ((ufilename[0] == UNICODE_DOT)
S
Steve French 已提交
398
				   && (ufilename[1] == UNICODE_DOT))
L
Linus Torvalds 已提交
399 400 401
					rc = 2;
			}
		} else /* ASCII */ {
402
			if (len == 1) {
S
Steve French 已提交
403
				if (filename[0] == '.')
L
Linus Torvalds 已提交
404
					rc = 1;
405
			} else if (len == 2) {
S
Steve French 已提交
406
				if ((filename[0] == '.') && (filename[1] == '.'))
L
Linus Torvalds 已提交
407 408 409 410 411 412 413 414
					rc = 2;
			}
		}
	}

	return rc;
}

415 416
/* Check if directory that we are searching has changed so we can decide
   whether we can use the cached search results from the previous search */
S
Steve French 已提交
417
static int is_dir_changed(struct file *file)
418
{
419 420
	struct inode *inode = file->f_path.dentry->d_inode;
	struct cifsInodeInfo *cifsInfo = CIFS_I(inode);
421

422
	if (cifsInfo->time == 0)
423 424 425 426 427 428
		return 1; /* directory was changed, perhaps due to unlink */
	else
		return 0;

}

429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
static int cifs_save_resume_key(const char *current_entry,
	struct cifsFileInfo *cifsFile)
{
	int rc = 0;
	unsigned int len = 0;
	__u16 level;
	char *filename;

	if ((cifsFile == NULL) || (current_entry == NULL))
		return -EINVAL;

	level = cifsFile->srch_inf.info_level;

	if (level == SMB_FIND_FILE_UNIX) {
		FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;

		filename = &pFindData->FileName[0];
		if (cifsFile->srch_inf.unicode) {
			len = cifs_unicode_bytelen(filename);
		} else {
			/* BB should we make this strnlen of PATH_MAX? */
			len = strnlen(filename, PATH_MAX);
		}
		cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
	} else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
		FILE_DIRECTORY_INFO *pFindData =
			(FILE_DIRECTORY_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
		cifsFile->srch_inf.resume_key = pFindData->FileIndex;
	} else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
		FILE_FULL_DIRECTORY_INFO *pFindData =
			(FILE_FULL_DIRECTORY_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
		cifsFile->srch_inf.resume_key = pFindData->FileIndex;
	} else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
		SEARCH_ID_FULL_DIR_INFO *pFindData =
			(SEARCH_ID_FULL_DIR_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
		cifsFile->srch_inf.resume_key = pFindData->FileIndex;
	} else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
		FILE_BOTH_DIRECTORY_INFO *pFindData =
			(FILE_BOTH_DIRECTORY_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
		cifsFile->srch_inf.resume_key = pFindData->FileIndex;
	} else if (level == SMB_FIND_FILE_INFO_STANDARD) {
		FIND_FILE_STANDARD_INFO *pFindData =
			(FIND_FILE_STANDARD_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		/* one byte length, no name conversion */
		len = (unsigned int)pFindData->FileNameLength;
		cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
	} else {
485
		cFYI(1, "Unknown findfirst level %d", level);
486 487 488 489 490 491 492
		return -EINVAL;
	}
	cifsFile->srch_inf.resume_name_len = len;
	cifsFile->srch_inf.presume_name = filename;
	return rc;
}

L
Linus Torvalds 已提交
493 494 495 496 497 498
/* find the corresponding entry in the search */
/* Note that the SMB server returns search entries for . and .. which
   complicates logic here if we choose to parse for them and we do not
   assume that they are located in the findfirst return buffer.*/
/* We start counting in the buffer with entry 2 and increment for every
   entry (do not increment for . or .. entry) */
499
static int find_cifs_entry(const int xid, struct cifs_tcon *pTcon,
S
Steve French 已提交
500
	struct file *file, char **ppCurrentEntry, int *num_to_ret)
L
Linus Torvalds 已提交
501 502 503 504 505
{
	int rc = 0;
	int pos_in_buf = 0;
	loff_t first_entry_in_buffer;
	loff_t index_to_find = file->f_pos;
S
Steve French 已提交
506
	struct cifsFileInfo *cifsFile = file->private_data;
L
Linus Torvalds 已提交
507
	/* check if index in the buffer */
508

S
Steve French 已提交
509
	if ((cifsFile == NULL) || (ppCurrentEntry == NULL) ||
510
	   (num_to_ret == NULL))
L
Linus Torvalds 已提交
511
		return -ENOENT;
512

L
Linus Torvalds 已提交
513
	*ppCurrentEntry = NULL;
S
Steve French 已提交
514 515
	first_entry_in_buffer =
		cifsFile->srch_inf.index_of_last_entry -
L
Linus Torvalds 已提交
516
			cifsFile->srch_inf.entries_in_buffer;
517 518 519 520 521 522 523

	/* if first entry in buf is zero then is first buffer
	in search response data which means it is likely . and ..
	will be in this buffer, although some servers do not return
	. and .. for the root of a drive and for those we need
	to start two entries earlier */

524
	dump_cifs_file_struct(file, "In fce ");
S
Steve French 已提交
525 526
	if (((index_to_find < cifsFile->srch_inf.index_of_last_entry) &&
	     is_dir_changed(file)) ||
527
	   (index_to_find < first_entry_in_buffer)) {
L
Linus Torvalds 已提交
528
		/* close and restart search */
529
		cFYI(1, "search backing up - close and restart search");
530
		spin_lock(&cifs_file_list_lock);
531 532 533
		if (!cifsFile->srch_inf.endOfSearch &&
		    !cifsFile->invalidHandle) {
			cifsFile->invalidHandle = true;
534
			spin_unlock(&cifs_file_list_lock);
535
			CIFSFindClose(xid, pTcon, cifsFile->netfid);
536
		} else
537
			spin_unlock(&cifs_file_list_lock);
538
		if (cifsFile->srch_inf.ntwrk_buf_start) {
539
			cFYI(1, "freeing SMB ff cache buf on search rewind");
540
			if (cifsFile->srch_inf.smallBuf)
541 542 543 544 545
				cifs_small_buf_release(cifsFile->srch_inf.
						ntwrk_buf_start);
			else
				cifs_buf_release(cifsFile->srch_inf.
						ntwrk_buf_start);
546
			cifsFile->srch_inf.ntwrk_buf_start = NULL;
L
Linus Torvalds 已提交
547
		}
S
Steve French 已提交
548
		rc = initiate_cifs_search(xid, file);
549
		if (rc) {
550 551
			cFYI(1, "error %d reinitiating a search on rewind",
				 rc);
L
Linus Torvalds 已提交
552 553
			return rc;
		}
554
		cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile);
L
Linus Torvalds 已提交
555 556
	}

S
Steve French 已提交
557
	while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) &&
558
	      (rc == 0) && !cifsFile->srch_inf.endOfSearch) {
559
		cFYI(1, "calling findnext2");
S
Steve French 已提交
560
		rc = CIFSFindNext(xid, pTcon, cifsFile->netfid,
561
				  &cifsFile->srch_inf);
562
		cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile);
563
		if (rc)
L
Linus Torvalds 已提交
564 565
			return -ENOENT;
	}
566
	if (index_to_find < cifsFile->srch_inf.index_of_last_entry) {
L
Linus Torvalds 已提交
567 568 569
		/* we found the buffer that contains the entry */
		/* scan and find it */
		int i;
S
Steve French 已提交
570 571
		char *current_entry;
		char *end_of_smb = cifsFile->srch_inf.ntwrk_buf_start +
L
Linus Torvalds 已提交
572 573
			smbCalcSize((struct smb_hdr *)
				cifsFile->srch_inf.ntwrk_buf_start);
574 575

		current_entry = cifsFile->srch_inf.srch_entries_start;
L
Linus Torvalds 已提交
576 577 578
		first_entry_in_buffer = cifsFile->srch_inf.index_of_last_entry
					- cifsFile->srch_inf.entries_in_buffer;
		pos_in_buf = index_to_find - first_entry_in_buffer;
579
		cFYI(1, "found entry - pos_in_buf %d", pos_in_buf);
580

S
Steve French 已提交
581
		for (i = 0; (i < (pos_in_buf)) && (current_entry != NULL); i++) {
582
			/* go entry by entry figuring out which is first */
S
Steve French 已提交
583
			current_entry = nxt_dir_entry(current_entry, end_of_smb,
584
						cifsFile->srch_inf.info_level);
L
Linus Torvalds 已提交
585
		}
S
Steve French 已提交
586
		if ((current_entry == NULL) && (i < pos_in_buf)) {
L
Linus Torvalds 已提交
587
			/* BB fixme - check if we should flag this error */
588
			cERROR(1, "reached end of buf searching for pos in buf"
L
Linus Torvalds 已提交
589
			  " %d index to find %lld rc %d",
590
			  pos_in_buf, index_to_find, rc);
L
Linus Torvalds 已提交
591 592 593 594
		}
		rc = 0;
		*ppCurrentEntry = current_entry;
	} else {
595
		cFYI(1, "index not in buffer - could not findnext into it");
L
Linus Torvalds 已提交
596 597 598
		return 0;
	}

S
Steve French 已提交
599
	if (pos_in_buf >= cifsFile->srch_inf.entries_in_buffer) {
600
		cFYI(1, "can not return entries pos_in_buf beyond last");
L
Linus Torvalds 已提交
601 602 603 604 605 606 607 608 609 610
		*num_to_ret = 0;
	} else
		*num_to_ret = cifsFile->srch_inf.entries_in_buffer - pos_in_buf;

	return rc;
}

/* inode num, inode type and filename returned */
static int cifs_get_name_from_search_buf(struct qstr *pqst,
	char *current_entry, __u16 level, unsigned int unicode,
611
	struct cifs_sb_info *cifs_sb, unsigned int max_len, __u64 *pinum)
L
Linus Torvalds 已提交
612 613 614
{
	int rc = 0;
	unsigned int len = 0;
S
Steve French 已提交
615 616
	char *filename;
	struct nls_table *nlt = cifs_sb->local_nls;
L
Linus Torvalds 已提交
617 618 619

	*pinum = 0;

S
Steve French 已提交
620
	if (level == SMB_FIND_FILE_UNIX) {
S
Steve French 已提交
621
		FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
L
Linus Torvalds 已提交
622 623

		filename = &pFindData->FileName[0];
S
Steve French 已提交
624
		if (unicode) {
L
Linus Torvalds 已提交
625 626 627 628 629 630
			len = cifs_unicode_bytelen(filename);
		} else {
			/* BB should we make this strnlen of PATH_MAX? */
			len = strnlen(filename, PATH_MAX);
		}

631
		*pinum = le64_to_cpu(pFindData->basic.UniqueId);
S
Steve French 已提交
632
	} else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
S
Steve French 已提交
633
		FILE_DIRECTORY_INFO *pFindData =
L
Linus Torvalds 已提交
634 635 636
			(FILE_DIRECTORY_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
S
Steve French 已提交
637
	} else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
S
Steve French 已提交
638
		FILE_FULL_DIRECTORY_INFO *pFindData =
L
Linus Torvalds 已提交
639 640 641
			(FILE_FULL_DIRECTORY_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
S
Steve French 已提交
642
	} else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
S
Steve French 已提交
643
		SEARCH_ID_FULL_DIR_INFO *pFindData =
L
Linus Torvalds 已提交
644 645 646
			(SEARCH_ID_FULL_DIR_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
647
		*pinum = le64_to_cpu(pFindData->UniqueId);
S
Steve French 已提交
648
	} else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
S
Steve French 已提交
649
		FILE_BOTH_DIRECTORY_INFO *pFindData =
L
Linus Torvalds 已提交
650 651 652
			(FILE_BOTH_DIRECTORY_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
S
Steve French 已提交
653
	} else if (level == SMB_FIND_FILE_INFO_STANDARD) {
S
Steve French 已提交
654
		FIND_FILE_STANDARD_INFO *pFindData =
655 656 657 658
			(FIND_FILE_STANDARD_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		/* one byte length, no name conversion */
		len = (unsigned int)pFindData->FileNameLength;
L
Linus Torvalds 已提交
659
	} else {
660
		cFYI(1, "Unknown findfirst level %d", level);
L
Linus Torvalds 已提交
661 662
		return -EINVAL;
	}
663

S
Steve French 已提交
664
	if (len > max_len) {
665
		cERROR(1, "bad search response length %d past smb end", len);
666 667 668
		return -EINVAL;
	}

S
Steve French 已提交
669
	if (unicode) {
670 671
		pqst->len = cifs_from_ucs2((char *) pqst->name,
					   (__le16 *) filename,
672 673
					   UNICODE_NAME_MAX,
					   min(len, max_len), nlt,
674 675
					   cifs_sb->mnt_cifs_flags &
						CIFS_MOUNT_MAP_SPECIAL_CHR);
676
		pqst->len -= nls_nullsize(nlt);
L
Linus Torvalds 已提交
677 678 679 680 681 682 683
	} else {
		pqst->name = filename;
		pqst->len = len;
	}
	return rc;
}

684 685
static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir,
			void *direntry, char *scratch_buf, unsigned int max_len)
L
Linus Torvalds 已提交
686 687 688
{
	int rc = 0;
	struct qstr qstring;
S
Steve French 已提交
689
	struct cifsFileInfo *pCifsF;
690
	u64    inum;
691
	ino_t  ino;
692
	struct super_block *sb;
S
Steve French 已提交
693
	struct cifs_sb_info *cifs_sb;
L
Linus Torvalds 已提交
694
	struct dentry *tmp_dentry;
695
	struct cifs_fattr fattr;
L
Linus Torvalds 已提交
696 697 698 699

	/* get filename and len into qstring */
	/* get dentry */
	/* decide whether to create and populate ionde */
S
Steve French 已提交
700
	if ((direntry == NULL) || (file == NULL))
L
Linus Torvalds 已提交
701 702 703
		return -EINVAL;

	pCifsF = file->private_data;
704

S
Steve French 已提交
705
	if ((scratch_buf == NULL) || (pfindEntry == NULL) || (pCifsF == NULL))
L
Linus Torvalds 已提交
706 707
		return -ENOENT;

S
Steve French 已提交
708
	rc = cifs_entry_is_dot(pfindEntry, pCifsF);
709
	/* skip . and .. since we added them first */
S
Steve French 已提交
710
	if (rc != 0)
711 712
		return 0;

713 714
	sb = file->f_path.dentry->d_sb;
	cifs_sb = CIFS_SB(sb);
L
Linus Torvalds 已提交
715 716

	qstring.name = scratch_buf;
S
Steve French 已提交
717
	rc = cifs_get_name_from_search_buf(&qstring, pfindEntry,
L
Linus Torvalds 已提交
718
			pCifsF->srch_inf.info_level,
S
Steve French 已提交
719
			pCifsF->srch_inf.unicode, cifs_sb,
720
			max_len, &inum /* returned */);
L
Linus Torvalds 已提交
721

S
Steve French 已提交
722
	if (rc)
L
Linus Torvalds 已提交
723 724
		return rc;

725
	if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX)
726 727 728
		cifs_unix_basic_to_fattr(&fattr,
				 &((FILE_UNIX_INFO *) pfindEntry)->basic,
				 cifs_sb);
729 730 731 732 733 734
	else if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD)
		cifs_std_info_to_fattr(&fattr, (FIND_FILE_STANDARD_INFO *)
					pfindEntry, cifs_sb);
	else
		cifs_dir_info_to_fattr(&fattr, (FILE_DIRECTORY_INFO *)
					pfindEntry, cifs_sb);
735

736
	if (inum && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
737
		fattr.cf_uniqueid = inum;
738
	} else {
739
		fattr.cf_uniqueid = iunique(sb, ROOT_I);
740 741
		cifs_autodisable_serverino(cifs_sb);
	}
742

743 744 745 746 747 748 749 750 751
	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) &&
	    CIFSCouldBeMFSymlink(&fattr))
		/*
		 * trying to get the type and mode can be slow,
		 * so just call those regular files for now, and mark
		 * for reval
		 */
		fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;

752 753
	ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
	tmp_dentry = cifs_readdir_lookup(file->f_dentry, &qstring, &fattr);
S
Steve French 已提交
754 755

	rc = filldir(direntry, qstring.name, qstring.len, file->f_pos,
756 757
		     ino, fattr.cf_dtype);

L
Linus Torvalds 已提交
758 759 760 761 762 763 764 765
	dput(tmp_dentry);
	return rc;
}


int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
{
	int rc = 0;
S
Steve French 已提交
766
	int xid, i;
767
	struct cifs_tcon *pTcon;
L
Linus Torvalds 已提交
768
	struct cifsFileInfo *cifsFile = NULL;
S
Steve French 已提交
769
	char *current_entry;
L
Linus Torvalds 已提交
770
	int num_to_fill = 0;
S
Steve French 已提交
771
	char *tmp_buf = NULL;
772
	char *end_of_smb;
773
	unsigned int max_len;
L
Linus Torvalds 已提交
774 775 776

	xid = GetXid();

777 778 779 780 781 782 783 784 785 786 787
	/*
	 * Ensure FindFirst doesn't fail before doing filldir() for '.' and
	 * '..'. Otherwise we won't be able to notify VFS in case of failure.
	 */
	if (file->private_data == NULL) {
		rc = initiate_cifs_search(xid, file);
		cFYI(1, "initiate cifs search rc %d", rc);
		if (rc)
			goto rddir2_exit;
	}

L
Linus Torvalds 已提交
788 789
	switch ((int) file->f_pos) {
	case 0:
790
		if (filldir(direntry, ".", 1, file->f_pos,
791
		     file->f_path.dentry->d_inode->i_ino, DT_DIR) < 0) {
792
			cERROR(1, "Filldir for current dir failed");
L
Linus Torvalds 已提交
793 794 795
			rc = -ENOMEM;
			break;
		}
796
		file->f_pos++;
L
Linus Torvalds 已提交
797
	case 1:
798
		if (filldir(direntry, "..", 2, file->f_pos,
799
		     parent_ino(file->f_path.dentry), DT_DIR) < 0) {
800
			cERROR(1, "Filldir for parent dir failed");
L
Linus Torvalds 已提交
801 802 803
			rc = -ENOMEM;
			break;
		}
804 805
		file->f_pos++;
	default:
S
Steve French 已提交
806 807
		/* 1) If search is active,
			is in current search buffer?
L
Linus Torvalds 已提交
808 809 810
			if it before then restart search
			if after then keep searching till find it */

S
Steve French 已提交
811
		if (file->private_data == NULL) {
L
Linus Torvalds 已提交
812 813 814 815 816 817
			rc = -EINVAL;
			FreeXid(xid);
			return rc;
		}
		cifsFile = file->private_data;
		if (cifsFile->srch_inf.endOfSearch) {
S
Steve French 已提交
818
			if (cifsFile->srch_inf.emptyDir) {
819
				cFYI(1, "End of search, empty dir");
L
Linus Torvalds 已提交
820 821 822 823
				rc = 0;
				break;
			}
		} /* else {
824
			cifsFile->invalidHandle = true;
L
Linus Torvalds 已提交
825
			CIFSFindClose(xid, pTcon, cifsFile->netfid);
S
Steve French 已提交
826
		} */
L
Linus Torvalds 已提交
827

828
		pTcon = tlink_tcon(cifsFile->tlink);
S
Steve French 已提交
829 830
		rc = find_cifs_entry(xid, pTcon, file,
				&current_entry, &num_to_fill);
S
Steve French 已提交
831
		if (rc) {
832
			cFYI(1, "fce error %d", rc);
L
Linus Torvalds 已提交
833 834
			goto rddir2_exit;
		} else if (current_entry != NULL) {
835
			cFYI(1, "entry %lld found", file->f_pos);
L
Linus Torvalds 已提交
836
		} else {
837
			cFYI(1, "could not find entry");
L
Linus Torvalds 已提交
838 839
			goto rddir2_exit;
		}
840 841
		cFYI(1, "loop through %d times filling dir for net buf %p",
			num_to_fill, cifsFile->srch_inf.ntwrk_buf_start);
842 843 844 845
		max_len = smbCalcSize((struct smb_hdr *)
				cifsFile->srch_inf.ntwrk_buf_start);
		end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len;

846
		tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL);
847 848 849 850 851
		if (tmp_buf == NULL) {
			rc = -ENOMEM;
			break;
		}

S
Steve French 已提交
852 853
		for (i = 0; (i < num_to_fill) && (rc == 0); i++) {
			if (current_entry == NULL) {
L
Linus Torvalds 已提交
854
				/* evaluate whether this case is an error */
855 856
				cERROR(1, "past SMB end,  num to fill %d i %d",
					  num_to_fill, i);
L
Linus Torvalds 已提交
857 858
				break;
			}
859 860
			/* if buggy server returns . and .. late do
			we want to check for that here? */
861 862
			rc = cifs_filldir(current_entry, file,
					filldir, direntry, tmp_buf, max_len);
S
Steve French 已提交
863
			if (rc == -EOVERFLOW) {
864 865 866 867
				rc = 0;
				break;
			}

L
Linus Torvalds 已提交
868
			file->f_pos++;
S
Steve French 已提交
869
			if (file->f_pos ==
870
				cifsFile->srch_inf.index_of_last_entry) {
871 872
				cFYI(1, "last entry in buf at pos %lld %s",
					file->f_pos, tmp_buf);
S
Steve French 已提交
873
				cifs_save_resume_key(current_entry, cifsFile);
L
Linus Torvalds 已提交
874
				break;
S
Steve French 已提交
875 876
			} else
				current_entry =
877 878
					nxt_dir_entry(current_entry, end_of_smb,
						cifsFile->srch_inf.info_level);
L
Linus Torvalds 已提交
879 880 881 882 883 884 885 886 887
		}
		kfree(tmp_buf);
		break;
	} /* end switch */

rddir2_exit:
	FreeXid(xid);
	return rc;
}