readdir.c 26.1 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 82 83 84 85
	if (parent->d_op && parent->d_op->d_hash)
		parent->d_op->d_hash(parent, name);
	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
	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;
	}

105
	if (cifs_sb_master_tcon(CIFS_SB(sb))->nocase)
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
		dentry->d_op = &cifs_ci_dentry_ops;
	else
		dentry->d_op = &cifs_dentry_ops;

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

	return dentry;
}

121 122
static void
cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
L
Linus Torvalds 已提交
123
{
124 125
	fattr->cf_uid = cifs_sb->mnt_uid;
	fattr->cf_gid = cifs_sb->mnt_gid;
L
Linus Torvalds 已提交
126

127 128 129
	if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
		fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
		fattr->cf_dtype = DT_DIR;
L
Linus Torvalds 已提交
130
	} else {
131 132
		fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
		fattr->cf_dtype = DT_REG;
L
Linus Torvalds 已提交
133 134
	}

135 136
	if (fattr->cf_cifsattrs & ATTR_READONLY)
		fattr->cf_mode &= ~S_IWUGO;
137

138 139 140 141 142 143
	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;
144
		} else {
145
			/*
146 147 148
			 * trying to get the type and mode via SFU can be slow,
			 * so just call those regular files for now, and mark
			 * for reval
149
			 */
150
			fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
151 152
		}
	}
153
}
L
Linus Torvalds 已提交
154

S
Steve French 已提交
155
static void
156 157 158 159 160 161 162 163 164 165 166 167 168
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);
	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 已提交
169

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

176 177 178 179 180 181 182 183 184 185 186 187 188
	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 已提交
189 190
}

191 192 193 194 195 196 197 198 199 200 201
/* 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;
202
	struct cifsTconInfo *ptcon = cifs_sb_tcon(cifs_sb);
203 204 205 206 207 208 209 210 211 212 213 214 215 216
	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)) {
217
			cFYI(1, "Error closing temporary reparsepoint open");
218 219 220 221 222
		}
	}
}
 */

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

232
	if (file->private_data == NULL) {
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
		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);
248
	}
L
Linus Torvalds 已提交
249

250 251
	cifsFile->invalidHandle = true;
	cifsFile->srch_inf.endOfSearch = false;
L
Linus Torvalds 已提交
252

253
	full_path = build_path_from_dentry(file->f_path.dentry);
254 255 256 257
	if (full_path == NULL) {
		rc = -ENOMEM;
		goto error_exit;
	}
L
Linus Torvalds 已提交
258

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

261
ffirst_retry:
L
Linus Torvalds 已提交
262
	/* test for Unix extensions */
263 264
	/* but now check for them on the share/mount not on the SMB session */
/*	if (pTcon->ses->capabilities & CAP_UNIX) { */
S
Steve French 已提交
265
	if (pTcon->unix_ext)
266
		cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX;
S
Steve French 已提交
267
	else if ((pTcon->ses->capabilities &
268 269
			(CAP_NT_SMBS | CAP_NT_FIND)) == 0) {
		cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD;
L
Linus Torvalds 已提交
270 271 272 273 274 275
	} 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 已提交
276
	rc = CIFSFindFirst(xid, pTcon, full_path, cifs_sb->local_nls,
277
		&cifsFile->netfid, &cifsFile->srch_inf,
S
Steve French 已提交
278
		cifs_sb->mnt_cifs_flags &
279
			CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb));
280
	if (rc == 0)
281
		cifsFile->invalidHandle = false;
S
Steve French 已提交
282
	/* BB add following call to handle readdir on new NTFS symlink errors
283 284 285
	else if STATUS_STOPPED_ON_SYMLINK
		call get_symlink_reparse_path and retry with new path */
	else if ((rc == -EOPNOTSUPP) &&
286 287 288 289
		(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
		cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM;
		goto ffirst_retry;
	}
290
error_exit:
L
Linus Torvalds 已提交
291
	kfree(full_path);
292
	cifs_put_tlink(tlink);
L
Linus Torvalds 已提交
293 294 295 296 297 298 299
	return rc;
}

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

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

310
static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level)
L
Linus Torvalds 已提交
311
{
S
Steve French 已提交
312
	char *new_entry;
S
Steve French 已提交
313
	FILE_DIRECTORY_INFO *pDirInfo = (FILE_DIRECTORY_INFO *)old_entry;
L
Linus Torvalds 已提交
314

315
	if (level == SMB_FIND_FILE_INFO_STANDARD) {
S
Steve French 已提交
316
		FIND_FILE_STANDARD_INFO *pfData;
317 318 319 320 321 322
		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);
323
	cFYI(1, "new entry %p old entry %p", new_entry, old_entry);
L
Linus Torvalds 已提交
324
	/* validate that new_entry is not past end of SMB */
325
	if (new_entry >= end_of_smb) {
326 327
		cERROR(1, "search entry %p began after end of SMB %p old entry %p",
			new_entry, end_of_smb, old_entry);
L
Linus Torvalds 已提交
328
		return NULL;
329
	} else if (((level == SMB_FIND_FILE_INFO_STANDARD) &&
S
Steve French 已提交
330 331
		    (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb))
		  || ((level != SMB_FIND_FILE_INFO_STANDARD) &&
332
		   (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb)))  {
333 334
		cERROR(1, "search entry %p extends after end of SMB %p",
			new_entry, end_of_smb);
335
		return NULL;
S
Steve French 已提交
336
	} else
L
Linus Torvalds 已提交
337 338 339 340 341 342 343 344 345 346
		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 已提交
347 348
	char *filename = NULL;
	int len = 0;
L
Linus Torvalds 已提交
349

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

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

	return rc;
}

419 420
/* 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 已提交
421
static int is_dir_changed(struct file *file)
422
{
423 424
	struct inode *inode = file->f_path.dentry->d_inode;
	struct cifsInodeInfo *cifsInfo = CIFS_I(inode);
425

426
	if (cifsInfo->time == 0)
427 428 429 430 431 432
		return 1; /* directory was changed, perhaps due to unlink */
	else
		return 0;

}

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 485 486 487 488
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 {
489
		cFYI(1, "Unknown findfirst level %d", level);
490 491 492 493 494 495 496
		return -EINVAL;
	}
	cifsFile->srch_inf.resume_name_len = len;
	cifsFile->srch_inf.presume_name = filename;
	return rc;
}

L
Linus Torvalds 已提交
497 498 499 500 501 502 503
/* 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) */
static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
S
Steve French 已提交
504
	struct file *file, char **ppCurrentEntry, int *num_to_ret)
L
Linus Torvalds 已提交
505 506 507 508 509
{
	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 已提交
510
	struct cifsFileInfo *cifsFile = file->private_data;
L
Linus Torvalds 已提交
511
	/* check if index in the buffer */
512

S
Steve French 已提交
513
	if ((cifsFile == NULL) || (ppCurrentEntry == NULL) ||
514
	   (num_to_ret == NULL))
L
Linus Torvalds 已提交
515
		return -ENOENT;
516

L
Linus Torvalds 已提交
517
	*ppCurrentEntry = NULL;
S
Steve French 已提交
518 519
	first_entry_in_buffer =
		cifsFile->srch_inf.index_of_last_entry -
L
Linus Torvalds 已提交
520
			cifsFile->srch_inf.entries_in_buffer;
521 522 523 524 525 526 527

	/* 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 */

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

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

		current_entry = cifsFile->srch_inf.srch_entries_start;
L
Linus Torvalds 已提交
580 581 582
		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;
583
		cFYI(1, "found entry - pos_in_buf %d", pos_in_buf);
584

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

S
Steve French 已提交
603
	if (pos_in_buf >= cifsFile->srch_inf.entries_in_buffer) {
604
		cFYI(1, "can not return entries pos_in_buf beyond last");
L
Linus Torvalds 已提交
605 606 607 608 609 610 611 612 613 614
		*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,
615
	struct cifs_sb_info *cifs_sb, unsigned int max_len, __u64 *pinum)
L
Linus Torvalds 已提交
616 617 618
{
	int rc = 0;
	unsigned int len = 0;
S
Steve French 已提交
619 620
	char *filename;
	struct nls_table *nlt = cifs_sb->local_nls;
L
Linus Torvalds 已提交
621 622 623

	*pinum = 0;

S
Steve French 已提交
624
	if (level == SMB_FIND_FILE_UNIX) {
S
Steve French 已提交
625
		FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
L
Linus Torvalds 已提交
626 627

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

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

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

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

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

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

	pCifsF = file->private_data;
708

S
Steve French 已提交
709
	if ((scratch_buf == NULL) || (pfindEntry == NULL) || (pCifsF == NULL))
L
Linus Torvalds 已提交
710 711
		return -ENOENT;

S
Steve French 已提交
712
	rc = cifs_entry_is_dot(pfindEntry, pCifsF);
713
	/* skip . and .. since we added them first */
S
Steve French 已提交
714
	if (rc != 0)
715 716
		return 0;

717 718
	sb = file->f_path.dentry->d_sb;
	cifs_sb = CIFS_SB(sb);
L
Linus Torvalds 已提交
719 720

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

S
Steve French 已提交
726
	if (rc)
L
Linus Torvalds 已提交
727 728
		return rc;

729
	if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX)
730 731 732
		cifs_unix_basic_to_fattr(&fattr,
				 &((FILE_UNIX_INFO *) pfindEntry)->basic,
				 cifs_sb);
733 734 735 736 737 738
	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);
739

740
	if (inum && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
741
		fattr.cf_uniqueid = inum;
742
	} else {
743
		fattr.cf_uniqueid = iunique(sb, ROOT_I);
744 745
		cifs_autodisable_serverino(cifs_sb);
	}
746

747 748 749 750 751 752 753 754 755
	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;

756 757
	ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
	tmp_dentry = cifs_readdir_lookup(file->f_dentry, &qstring, &fattr);
S
Steve French 已提交
758 759

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

L
Linus Torvalds 已提交
762 763 764 765 766 767 768 769
	dput(tmp_dentry);
	return rc;
}


int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
{
	int rc = 0;
S
Steve French 已提交
770
	int xid, i;
L
Linus Torvalds 已提交
771 772 773
	struct cifs_sb_info *cifs_sb;
	struct cifsTconInfo *pTcon;
	struct cifsFileInfo *cifsFile = NULL;
S
Steve French 已提交
774
	char *current_entry;
L
Linus Torvalds 已提交
775
	int num_to_fill = 0;
S
Steve French 已提交
776
	char *tmp_buf = NULL;
777
	char *end_of_smb;
778
	unsigned int max_len;
L
Linus Torvalds 已提交
779 780 781

	xid = GetXid();

782
	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
L
Linus Torvalds 已提交
783

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

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

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

853
		tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL);
854 855 856 857 858
		if (tmp_buf == NULL) {
			rc = -ENOMEM;
			break;
		}

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

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

rddir2_exit:
	FreeXid(xid);
	return rc;
}