readdir.c 24.4 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
C
Christoph Hellwig 已提交
7
 *   Copyright (C) Red Hat, Inc., 2011
L
Linus Torvalds 已提交
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *   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 已提交
25
#include <linux/pagemap.h>
26
#include <linux/slab.h>
L
Linus Torvalds 已提交
27 28 29 30 31 32 33 34 35
#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"

36 37 38 39 40 41 42
/*
 * 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)

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

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

68
/*
69 70
 * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT
 *
71 72 73
 * 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.
 */
74 75
static void
cifs_prime_dcache(struct dentry *parent, struct qstr *name,
76 77 78 79 80
		    struct cifs_fattr *fattr)
{
	struct dentry *dentry, *alias;
	struct inode *inode;
	struct super_block *sb = parent->d_inode->i_sb;
81
	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
82

83
	cifs_dbg(FYI, "%s: for %s\n", __func__, name->name);
84

85 86 87
	dentry = d_hash_and_lookup(parent, name);
	if (unlikely(IS_ERR(dentry)))
		return;
88

89
	if (dentry) {
90
		inode = dentry->d_inode;
91 92 93 94 95 96 97 98 99 100 101 102 103 104
		if (inode) {
			/*
			 * If we're generating inode numbers, then we don't
			 * want to clobber the existing one with the one that
			 * the readdir code created.
			 */
			if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM))
				fattr->cf_uniqueid = CIFS_I(inode)->uniqueid;

			/* update inode in place if i_ino didn't change */
			if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid) {
				cifs_fattr_to_inode(inode, fattr);
				goto out;
			}
105
		}
106
		d_invalidate(dentry);
107 108 109
		dput(dentry);
	}

110 111 112 113 114 115 116 117
	/*
	 * If we know that the inode will need to be revalidated immediately,
	 * then don't create a new dentry for it. We'll end up doing an on
	 * the wire call either way and this spares us an invalidation.
	 */
	if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL)
		return;

118
	dentry = d_alloc(parent, name);
119 120
	if (!dentry)
		return;
121 122

	inode = cifs_iget(sb, fattr);
123 124
	if (!inode)
		goto out;
125 126

	alias = d_materialise_unique(dentry, inode);
127 128 129 130
	if (alias && !IS_ERR(alias))
		dput(alias);
out:
	dput(dentry);
131 132
}

133 134
static void
cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
L
Linus Torvalds 已提交
135
{
136 137
	fattr->cf_uid = cifs_sb->mnt_uid;
	fattr->cf_gid = cifs_sb->mnt_gid;
L
Linus Torvalds 已提交
138

139 140 141
	if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
		fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
		fattr->cf_dtype = DT_DIR;
L
Linus Torvalds 已提交
142
	} else {
143 144
		fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
		fattr->cf_dtype = DT_REG;
L
Linus Torvalds 已提交
145 146
	}

P
Pavel Shilovsky 已提交
147 148 149 150 151 152 153 154
	/*
	 * We need to revalidate it further to make a decision about whether it
	 * is a symbolic link, DFS referral or a reparse point with a direct
	 * access like junctions, deduplicated files, NFS symlinks.
	 */
	if (fattr->cf_cifsattrs & ATTR_REPARSE)
		fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;

155 156 157
	/* non-unix readdir doesn't provide nlink */
	fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;

158 159
	if (fattr->cf_cifsattrs & ATTR_READONLY)
		fattr->cf_mode &= ~S_IWUGO;
160

161 162 163 164 165 166 167 168 169 170
	/*
	 * We of course don't get ACL info in FIND_FIRST/NEXT results, so
	 * mark it for revalidation so that "ls -l" will look right. It might
	 * be super-slow, but if we don't do this then the ownership of files
	 * may look wrong since the inodes may not have timed out by the time
	 * "ls" does a stat() call on them.
	 */
	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL)
		fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;

171 172 173 174 175 176
	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;
177
		} else {
178
			/*
179 180 181
			 * trying to get the type and mode via SFU can be slow,
			 * so just call those regular files for now, and mark
			 * for reval
182
			 */
183
			fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
184 185
		}
	}
186
}
L
Linus Torvalds 已提交
187

188
void
189 190 191 192 193 194 195
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);
196
	fattr->cf_createtime = le64_to_cpu(info->CreationTime);
197 198 199 200 201 202
	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 已提交
203

S
Steve French 已提交
204
static void
205 206 207
cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info,
		       struct cifs_sb_info *cifs_sb)
{
208
	int offset = cifs_sb_master_tcon(cifs_sb)->ses->server->timeAdj;
L
Linus Torvalds 已提交
209

210 211 212 213 214 215 216 217 218 219 220 221 222
	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 已提交
223 224
}

225 226 227 228 229
/* 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,
230
			     unsigned int xid)
231 232 233 234 235
{
	__u16 fid;
	int len;
	int oplock = 0;
	int rc;
236
	struct cifs_tcon *ptcon = cifs_sb_tcon(cifs_sb);
237 238 239 240 241 242 243 244 245 246 247 248 249 250
	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)) {
251
			cifs_dbg(FYI, "Error closing temporary reparsepoint open\n");
252 253 254 255 256
		}
	}
}
 */

257 258
static int
initiate_cifs_search(const unsigned int xid, struct file *file)
L
Linus Torvalds 已提交
259
{
260
	__u16 search_flags;
L
Linus Torvalds 已提交
261
	int rc = 0;
262
	char *full_path = NULL;
S
Steve French 已提交
263
	struct cifsFileInfo *cifsFile;
264
	struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
265
	struct tcon_link *tlink = NULL;
266
	struct cifs_tcon *tcon;
267
	struct TCP_Server_Info *server;
L
Linus Torvalds 已提交
268

269
	if (file->private_data == NULL) {
270 271 272 273 274 275 276 277 278 279 280
		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);
281
		tcon = tlink_tcon(tlink);
282 283
	} else {
		cifsFile = file->private_data;
284
		tcon = tlink_tcon(cifsFile->tlink);
285
	}
L
Linus Torvalds 已提交
286

287 288 289 290 291 292 293
	server = tcon->ses->server;

	if (!server->ops->query_dir_first) {
		rc = -ENOSYS;
		goto error_exit;
	}

294 295
	cifsFile->invalidHandle = true;
	cifsFile->srch_inf.endOfSearch = false;
L
Linus Torvalds 已提交
296

297
	full_path = build_path_from_dentry(file->f_path.dentry);
298 299 300 301
	if (full_path == NULL) {
		rc = -ENOMEM;
		goto error_exit;
	}
L
Linus Torvalds 已提交
302

303
	cifs_dbg(FYI, "Full path: %s start at: %lld\n", full_path, file->f_pos);
L
Linus Torvalds 已提交
304

305
ffirst_retry:
L
Linus Torvalds 已提交
306
	/* test for Unix extensions */
307
	/* but now check for them on the share/mount not on the SMB session */
308 309
	/* if (cap_unix(tcon->ses) { */
	if (tcon->unix_ext)
310
		cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX;
311 312
	else if ((tcon->ses->capabilities &
		  tcon->ses->server->vals->cap_nt_find) == 0) {
313
		cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD;
L
Linus Torvalds 已提交
314 315 316 317 318 319
	} 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;
	}

320 321 322 323
	search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
	if (backup_cred(cifs_sb))
		search_flags |= CIFS_SEARCH_BACKUP_SEARCH;

324 325 326 327
	rc = server->ops->query_dir_first(xid, tcon, full_path, cifs_sb,
					  &cifsFile->fid, search_flags,
					  &cifsFile->srch_inf);

328
	if (rc == 0)
329
		cifsFile->invalidHandle = false;
S
Steve French 已提交
330
	/* BB add following call to handle readdir on new NTFS symlink errors
331 332 333
	else if STATUS_STOPPED_ON_SYMLINK
		call get_symlink_reparse_path and retry with new path */
	else if ((rc == -EOPNOTSUPP) &&
334 335 336 337
		(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
		cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM;
		goto ffirst_retry;
	}
338
error_exit:
L
Linus Torvalds 已提交
339
	kfree(full_path);
340
	cifs_put_tlink(tlink);
L
Linus Torvalds 已提交
341 342 343 344
	return rc;
}

/* return length of unicode string in bytes */
C
Christoph Hellwig 已提交
345
static int cifs_unicode_bytelen(const char *str)
L
Linus Torvalds 已提交
346 347
{
	int len;
C
Christoph Hellwig 已提交
348
	const __le16 *ustr = (const __le16 *)str;
L
Linus Torvalds 已提交
349

S
Steve French 已提交
350
	for (len = 0; len <= PATH_MAX; len++) {
351
		if (ustr[len] == 0)
L
Linus Torvalds 已提交
352 353
			return len << 1;
	}
354
	cifs_dbg(FYI, "Unicode string longer than PATH_MAX found\n");
L
Linus Torvalds 已提交
355 356 357
	return len << 1;
}

358
static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level)
L
Linus Torvalds 已提交
359
{
S
Steve French 已提交
360
	char *new_entry;
S
Steve French 已提交
361
	FILE_DIRECTORY_INFO *pDirInfo = (FILE_DIRECTORY_INFO *)old_entry;
L
Linus Torvalds 已提交
362

363
	if (level == SMB_FIND_FILE_INFO_STANDARD) {
S
Steve French 已提交
364
		FIND_FILE_STANDARD_INFO *pfData;
365 366 367 368 369 370
		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);
371
	cifs_dbg(FYI, "new entry %p old entry %p\n", new_entry, old_entry);
L
Linus Torvalds 已提交
372
	/* validate that new_entry is not past end of SMB */
373
	if (new_entry >= end_of_smb) {
374 375
		cifs_dbg(VFS, "search entry %p began after end of SMB %p old entry %p\n",
			 new_entry, end_of_smb, old_entry);
L
Linus Torvalds 已提交
376
		return NULL;
377
	} else if (((level == SMB_FIND_FILE_INFO_STANDARD) &&
S
Steve French 已提交
378 379
		    (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb))
		  || ((level != SMB_FIND_FILE_INFO_STANDARD) &&
380
		   (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb)))  {
381 382
		cifs_dbg(VFS, "search entry %p extends after end of SMB %p\n",
			 new_entry, end_of_smb);
383
		return NULL;
S
Steve French 已提交
384
	} else
L
Linus Torvalds 已提交
385 386 387 388
		return new_entry;

}

C
Christoph Hellwig 已提交
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 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
struct cifs_dirent {
	const char	*name;
	size_t		namelen;
	u32		resume_key;
	u64		ino;
};

static void cifs_fill_dirent_unix(struct cifs_dirent *de,
		const FILE_UNIX_INFO *info, bool is_unicode)
{
	de->name = &info->FileName[0];
	if (is_unicode)
		de->namelen = cifs_unicode_bytelen(de->name);
	else
		de->namelen = strnlen(de->name, PATH_MAX);
	de->resume_key = info->ResumeKey;
	de->ino = le64_to_cpu(info->basic.UniqueId);
}

static void cifs_fill_dirent_dir(struct cifs_dirent *de,
		const FILE_DIRECTORY_INFO *info)
{
	de->name = &info->FileName[0];
	de->namelen = le32_to_cpu(info->FileNameLength);
	de->resume_key = info->FileIndex;
}

static void cifs_fill_dirent_full(struct cifs_dirent *de,
		const FILE_FULL_DIRECTORY_INFO *info)
{
	de->name = &info->FileName[0];
	de->namelen = le32_to_cpu(info->FileNameLength);
	de->resume_key = info->FileIndex;
}

static void cifs_fill_dirent_search(struct cifs_dirent *de,
		const SEARCH_ID_FULL_DIR_INFO *info)
{
	de->name = &info->FileName[0];
	de->namelen = le32_to_cpu(info->FileNameLength);
	de->resume_key = info->FileIndex;
	de->ino = le64_to_cpu(info->UniqueId);
}

static void cifs_fill_dirent_both(struct cifs_dirent *de,
		const FILE_BOTH_DIRECTORY_INFO *info)
{
	de->name = &info->FileName[0];
	de->namelen = le32_to_cpu(info->FileNameLength);
	de->resume_key = info->FileIndex;
}

static void cifs_fill_dirent_std(struct cifs_dirent *de,
		const FIND_FILE_STANDARD_INFO *info)
{
	de->name = &info->FileName[0];
	/* one byte length, no endianess conversion */
	de->namelen = info->FileNameLength;
	de->resume_key = info->ResumeKey;
}

static int cifs_fill_dirent(struct cifs_dirent *de, const void *info,
		u16 level, bool is_unicode)
{
	memset(de, 0, sizeof(*de));

	switch (level) {
	case SMB_FIND_FILE_UNIX:
		cifs_fill_dirent_unix(de, info, is_unicode);
		break;
	case SMB_FIND_FILE_DIRECTORY_INFO:
		cifs_fill_dirent_dir(de, info);
		break;
	case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
		cifs_fill_dirent_full(de, info);
		break;
	case SMB_FIND_FILE_ID_FULL_DIR_INFO:
		cifs_fill_dirent_search(de, info);
		break;
	case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
		cifs_fill_dirent_both(de, info);
		break;
	case SMB_FIND_FILE_INFO_STANDARD:
		cifs_fill_dirent_std(de, info);
		break;
	default:
475
		cifs_dbg(FYI, "Unknown findfirst level %d\n", level);
C
Christoph Hellwig 已提交
476 477 478 479 480 481
		return -EINVAL;
	}

	return 0;
}

L
Linus Torvalds 已提交
482 483 484
#define UNICODE_DOT cpu_to_le16(0x2e)

/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */
C
Christoph Hellwig 已提交
485
static int cifs_entry_is_dot(struct cifs_dirent *de, bool is_unicode)
L
Linus Torvalds 已提交
486 487 488
{
	int rc = 0;

C
Christoph Hellwig 已提交
489 490
	if (!de->name)
		return 0;
L
Linus Torvalds 已提交
491

C
Christoph Hellwig 已提交
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
	if (is_unicode) {
		__le16 *ufilename = (__le16 *)de->name;
		if (de->namelen == 2) {
			/* check for . */
			if (ufilename[0] == UNICODE_DOT)
				rc = 1;
		} else if (de->namelen == 4) {
			/* check for .. */
			if (ufilename[0] == UNICODE_DOT &&
			    ufilename[1] == UNICODE_DOT)
				rc = 2;
		}
	} else /* ASCII */ {
		if (de->namelen == 1) {
			if (de->name[0] == '.')
				rc = 1;
		} else if (de->namelen == 2) {
			if (de->name[0] == '.' && de->name[1] == '.')
				rc = 2;
L
Linus Torvalds 已提交
511 512 513 514 515 516
		}
	}

	return rc;
}

517 518
/* 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 已提交
519
static int is_dir_changed(struct file *file)
520
{
A
Al Viro 已提交
521
	struct inode *inode = file_inode(file);
522
	struct cifsInodeInfo *cifsInfo = CIFS_I(inode);
523

524
	if (cifsInfo->time == 0)
525 526 527 528 529 530
		return 1; /* directory was changed, perhaps due to unlink */
	else
		return 0;

}

531
static int cifs_save_resume_key(const char *current_entry,
532
	struct cifsFileInfo *file_info)
533
{
534 535
	struct cifs_dirent de;
	int rc;
536

537 538 539 540 541 542
	rc = cifs_fill_dirent(&de, current_entry, file_info->srch_inf.info_level,
			      file_info->srch_inf.unicode);
	if (!rc) {
		file_info->srch_inf.presume_name = de.name;
		file_info->srch_inf.resume_name_len = de.namelen;
		file_info->srch_inf.resume_key = de.resume_key;
543 544 545 546
	}
	return rc;
}

547 548 549 550 551 552 553 554
/*
 * 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
A
Al Viro 已提交
555
find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
556
		struct file *file, char **current_entry, int *num_to_ret)
L
Linus Torvalds 已提交
557
{
558
	__u16 search_flags;
L
Linus Torvalds 已提交
559 560 561
	int rc = 0;
	int pos_in_buf = 0;
	loff_t first_entry_in_buffer;
A
Al Viro 已提交
562
	loff_t index_to_find = pos;
563
	struct cifsFileInfo *cfile = file->private_data;
564
	struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
565
	struct TCP_Server_Info *server = tcon->ses->server;
L
Linus Torvalds 已提交
566
	/* check if index in the buffer */
567

568 569 570 571
	if (!server->ops->query_dir_first || !server->ops->query_dir_next)
		return -ENOSYS;

	if ((cfile == NULL) || (current_entry == NULL) || (num_to_ret == NULL))
L
Linus Torvalds 已提交
572
		return -ENOENT;
573

574 575 576
	*current_entry = NULL;
	first_entry_in_buffer = cfile->srch_inf.index_of_last_entry -
					cfile->srch_inf.entries_in_buffer;
577

578 579 580 581 582 583 584
	/*
	 * 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.
	 */
585

586
	dump_cifs_file_struct(file, "In fce ");
587 588
	if (((index_to_find < cfile->srch_inf.index_of_last_entry) &&
	     is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) {
L
Linus Torvalds 已提交
589
		/* close and restart search */
590
		cifs_dbg(FYI, "search backing up - close and restart search\n");
591
		spin_lock(&cifs_file_list_lock);
592
		if (server->ops->dir_needs_close(cfile)) {
593
			cfile->invalidHandle = true;
594
			spin_unlock(&cifs_file_list_lock);
595 596
			if (server->ops->close_dir)
				server->ops->close_dir(xid, tcon, &cfile->fid);
597
		} else
598
			spin_unlock(&cifs_file_list_lock);
599
		if (cfile->srch_inf.ntwrk_buf_start) {
600
			cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n");
601 602
			if (cfile->srch_inf.smallBuf)
				cifs_small_buf_release(cfile->srch_inf.
603 604
						ntwrk_buf_start);
			else
605
				cifs_buf_release(cfile->srch_inf.
606
						ntwrk_buf_start);
607
			cfile->srch_inf.ntwrk_buf_start = NULL;
L
Linus Torvalds 已提交
608
		}
S
Steve French 已提交
609
		rc = initiate_cifs_search(xid, file);
610
		if (rc) {
611
			cifs_dbg(FYI, "error %d reinitiating a search on rewind\n",
612
				 rc);
L
Linus Torvalds 已提交
613 614
			return rc;
		}
615
		/* FindFirst/Next set last_entry to NULL on malformed reply */
616 617
		if (cfile->srch_inf.last_entry)
			cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);
L
Linus Torvalds 已提交
618 619
	}

620 621 622 623
	search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
	if (backup_cred(cifs_sb))
		search_flags |= CIFS_SEARCH_BACKUP_SEARCH;

624 625
	while ((index_to_find >= cfile->srch_inf.index_of_last_entry) &&
	       (rc == 0) && !cfile->srch_inf.endOfSearch) {
626
		cifs_dbg(FYI, "calling findnext2\n");
627 628 629
		rc = server->ops->query_dir_next(xid, tcon, &cfile->fid,
						 search_flags,
						 &cfile->srch_inf);
630
		/* FindFirst/Next set last_entry to NULL on malformed reply */
631 632
		if (cfile->srch_inf.last_entry)
			cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);
633
		if (rc)
L
Linus Torvalds 已提交
634 635
			return -ENOENT;
	}
636
	if (index_to_find < cfile->srch_inf.index_of_last_entry) {
L
Linus Torvalds 已提交
637 638 639
		/* we found the buffer that contains the entry */
		/* scan and find it */
		int i;
640 641 642 643 644 645 646 647
		char *cur_ent;
		char *end_of_smb = cfile->srch_inf.ntwrk_buf_start +
			server->ops->calc_smb_size(
					cfile->srch_inf.ntwrk_buf_start);

		cur_ent = cfile->srch_inf.srch_entries_start;
		first_entry_in_buffer = cfile->srch_inf.index_of_last_entry
					- cfile->srch_inf.entries_in_buffer;
L
Linus Torvalds 已提交
648
		pos_in_buf = index_to_find - first_entry_in_buffer;
649
		cifs_dbg(FYI, "found entry - pos_in_buf %d\n", pos_in_buf);
650

651
		for (i = 0; (i < (pos_in_buf)) && (cur_ent != NULL); i++) {
652
			/* go entry by entry figuring out which is first */
653 654
			cur_ent = nxt_dir_entry(cur_ent, end_of_smb,
						cfile->srch_inf.info_level);
L
Linus Torvalds 已提交
655
		}
656
		if ((cur_ent == NULL) && (i < pos_in_buf)) {
L
Linus Torvalds 已提交
657
			/* BB fixme - check if we should flag this error */
658 659
			cifs_dbg(VFS, "reached end of buf searching for pos in buf %d index to find %lld rc %d\n",
				 pos_in_buf, index_to_find, rc);
L
Linus Torvalds 已提交
660 661
		}
		rc = 0;
662
		*current_entry = cur_ent;
L
Linus Torvalds 已提交
663
	} else {
664
		cifs_dbg(FYI, "index not in buffer - could not findnext into it\n");
L
Linus Torvalds 已提交
665 666 667
		return 0;
	}

668
	if (pos_in_buf >= cfile->srch_inf.entries_in_buffer) {
669
		cifs_dbg(FYI, "can not return entries pos_in_buf beyond last\n");
L
Linus Torvalds 已提交
670 671
		*num_to_ret = 0;
	} else
672
		*num_to_ret = cfile->srch_inf.entries_in_buffer - pos_in_buf;
L
Linus Torvalds 已提交
673 674 675 676

	return rc;
}

A
Al Viro 已提交
677 678 679
static int cifs_filldir(char *find_entry, struct file *file,
		struct dir_context *ctx,
		char *scratch_buf, unsigned int max_len)
L
Linus Torvalds 已提交
680
{
C
Christoph Hellwig 已提交
681 682 683
	struct cifsFileInfo *file_info = file->private_data;
	struct super_block *sb = file->f_path.dentry->d_sb;
	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
C
Christoph Hellwig 已提交
684
	struct cifs_dirent de = { NULL, };
685
	struct cifs_fattr fattr;
C
Christoph Hellwig 已提交
686
	struct qstr name;
L
Linus Torvalds 已提交
687
	int rc = 0;
C
Christoph Hellwig 已提交
688
	ino_t ino;
L
Linus Torvalds 已提交
689

C
Christoph Hellwig 已提交
690 691 692 693
	rc = cifs_fill_dirent(&de, find_entry, file_info->srch_inf.info_level,
			      file_info->srch_inf.unicode);
	if (rc)
		return rc;
694

695
	if (de.namelen > max_len) {
696 697
		cifs_dbg(VFS, "bad search response length %zd past smb end\n",
			 de.namelen);
698 699 700
		return -EINVAL;
	}

701
	/* skip . and .. since we added them first */
C
Christoph Hellwig 已提交
702
	if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode))
703 704
		return 0;

705 706
	if (file_info->srch_inf.unicode) {
		struct nls_table *nlt = cifs_sb->local_nls;
L
Linus Torvalds 已提交
707

708 709
		name.name = scratch_buf;
		name.len =
710 711 712 713 714
			cifs_from_utf16((char *)name.name, (__le16 *)de.name,
					UNICODE_NAME_MAX,
					min_t(size_t, de.namelen,
					      (size_t)max_len), nlt,
					cifs_sb->mnt_cifs_flags &
715 716 717 718 719 720
						CIFS_MOUNT_MAP_SPECIAL_CHR);
		name.len -= nls_nullsize(nlt);
	} else {
		name.name = de.name;
		name.len = de.namelen;
	}
L
Linus Torvalds 已提交
721

C
Christoph Hellwig 已提交
722 723
	switch (file_info->srch_inf.info_level) {
	case SMB_FIND_FILE_UNIX:
724
		cifs_unix_basic_to_fattr(&fattr,
C
Christoph Hellwig 已提交
725 726 727 728 729 730 731 732 733 734 735 736 737 738
					 &((FILE_UNIX_INFO *)find_entry)->basic,
					 cifs_sb);
		break;
	case SMB_FIND_FILE_INFO_STANDARD:
		cifs_std_info_to_fattr(&fattr,
				       (FIND_FILE_STANDARD_INFO *)find_entry,
				       cifs_sb);
		break;
	default:
		cifs_dir_info_to_fattr(&fattr,
				       (FILE_DIRECTORY_INFO *)find_entry,
				       cifs_sb);
		break;
	}
739

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

747
	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) &&
748
	    couldbe_mf_symlink(&fattr))
749 750 751 752 753 754 755
		/*
		 * 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
	cifs_prime_dcache(file->f_dentry, &name, &fattr);
S
Steve French 已提交
757

758
	ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
A
Al Viro 已提交
759
	return !dir_emit(ctx, name.name, name.len, ino, fattr.cf_dtype);
L
Linus Torvalds 已提交
760 761 762
}


A
Al Viro 已提交
763
int cifs_readdir(struct file *file, struct dir_context *ctx)
L
Linus Torvalds 已提交
764 765
{
	int rc = 0;
766 767
	unsigned int xid;
	int i;
768
	struct cifs_tcon *tcon;
L
Linus Torvalds 已提交
769
	struct cifsFileInfo *cifsFile = NULL;
S
Steve French 已提交
770
	char *current_entry;
L
Linus Torvalds 已提交
771
	int num_to_fill = 0;
S
Steve French 已提交
772
	char *tmp_buf = NULL;
773
	char *end_of_smb;
774
	unsigned int max_len;
L
Linus Torvalds 已提交
775

776
	xid = get_xid();
L
Linus Torvalds 已提交
777

778 779 780 781 782 783
	/*
	 * 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);
784
		cifs_dbg(FYI, "initiate cifs search rc %d\n", rc);
785 786 787 788
		if (rc)
			goto rddir2_exit;
	}

A
Al Viro 已提交
789 790
	if (!dir_emit_dots(file, ctx))
		goto rddir2_exit;
L
Linus Torvalds 已提交
791

A
Al Viro 已提交
792 793 794 795 796 797 798 799 800 801 802 803 804 805
	/* 1) If search is active,
		is in current search buffer?
		if it before then restart search
		if after then keep searching till find it */

	if (file->private_data == NULL) {
		rc = -EINVAL;
		goto rddir2_exit;
	}
	cifsFile = file->private_data;
	if (cifsFile->srch_inf.endOfSearch) {
		if (cifsFile->srch_inf.emptyDir) {
			cifs_dbg(FYI, "End of search, empty dir\n");
			rc = 0;
L
Linus Torvalds 已提交
806 807
			goto rddir2_exit;
		}
A
Al Viro 已提交
808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841
	} /* else {
		cifsFile->invalidHandle = true;
		tcon->ses->server->close(xid, tcon, &cifsFile->fid);
	} */

	tcon = tlink_tcon(cifsFile->tlink);
	rc = find_cifs_entry(xid, tcon, ctx->pos, file, &current_entry,
			     &num_to_fill);
	if (rc) {
		cifs_dbg(FYI, "fce error %d\n", rc);
		goto rddir2_exit;
	} else if (current_entry != NULL) {
		cifs_dbg(FYI, "entry %lld found\n", ctx->pos);
	} else {
		cifs_dbg(FYI, "could not find entry\n");
		goto rddir2_exit;
	}
	cifs_dbg(FYI, "loop through %d times filling dir for net buf %p\n",
		 num_to_fill, cifsFile->srch_inf.ntwrk_buf_start);
	max_len = tcon->ses->server->ops->calc_smb_size(
			cifsFile->srch_inf.ntwrk_buf_start);
	end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len;

	tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL);
	if (tmp_buf == NULL) {
		rc = -ENOMEM;
		goto rddir2_exit;
	}

	for (i = 0; i < num_to_fill; i++) {
		if (current_entry == NULL) {
			/* evaluate whether this case is an error */
			cifs_dbg(VFS, "past SMB end,  num to fill %d i %d\n",
				 num_to_fill, i);
842 843
			break;
		}
A
Al Viro 已提交
844 845 846 847 848 849 850 851
		/*
		 * if buggy server returns . and .. late do we want to
		 * check for that here?
		 */
		rc = cifs_filldir(current_entry, file, ctx,
				  tmp_buf, max_len);
		if (rc) {
			if (rc > 0)
852
				rc = 0;
A
Al Viro 已提交
853
			break;
L
Linus Torvalds 已提交
854
		}
A
Al Viro 已提交
855 856 857 858 859 860 861 862 863 864 865 866 867 868

		ctx->pos++;
		if (ctx->pos ==
			cifsFile->srch_inf.index_of_last_entry) {
			cifs_dbg(FYI, "last entry in buf at pos %lld %s\n",
				 ctx->pos, tmp_buf);
			cifs_save_resume_key(current_entry, cifsFile);
			break;
		} else
			current_entry =
				nxt_dir_entry(current_entry, end_of_smb,
					cifsFile->srch_inf.info_level);
	}
	kfree(tmp_buf);
L
Linus Torvalds 已提交
869 870

rddir2_exit:
871
	free_xid(xid);
L
Linus Torvalds 已提交
872 873
	return rc;
}