readdir.c 24.6 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4
/*
 *   fs/cifs/readdir.c
 *
 *   Directory search handling
Steve French's avatar
Steve French committed
5
 *
6
 *   Copyright (C) International Business Machines  Corp., 2004, 2008
7
 *   Copyright (C) Red Hat, Inc., 2011
Linus Torvalds's avatar
Linus Torvalds committed
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>
Dave Kleikamp's avatar
Dave Kleikamp committed
25
#include <linux/pagemap.h>
26
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
45
{
Steve French's avatar
Steve French committed
46
	struct cifsFileInfo *cf;
Linus Torvalds's avatar
Linus Torvalds committed
47

48
	if (file) {
Linus Torvalds's avatar
Linus Torvalds committed
49
		cf = file->private_data;
50
		if (cf == NULL) {
51
			cifs_dbg(FYI, "empty cifs private file data\n");
Linus Torvalds's avatar
Linus Torvalds committed
52 53
			return;
		}
54
		if (cf->invalidHandle)
55
			cifs_dbg(FYI, "invalid handle\n");
56
		if (cf->srch_inf.endOfSearch)
57
			cifs_dbg(FYI, "end of search\n");
58
		if (cf->srch_inf.emptyDir)
59
			cifs_dbg(FYI, "empty dir\n");
Linus Torvalds's avatar
Linus Torvalds committed
60
	}
61
}
62 63 64 65
#else
static inline void dump_cifs_file_struct(struct file *file, char *label)
{
}
66
#endif /* DEBUG2 */
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
135
{
136 137
	fattr->cf_uid = cifs_sb->mnt_uid;
	fattr->cf_gid = cifs_sb->mnt_gid;
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
142
	} else {
143 144
		fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
		fattr->cf_dtype = DT_REG;
Linus Torvalds's avatar
Linus Torvalds committed
145 146
	}

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
}
Linus Torvalds's avatar
Linus Torvalds committed
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);
}
Linus Torvalds's avatar
Linus Torvalds committed
203

Steve French's avatar
Steve French committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
259
{
260
	__u16 search_flags;
Linus Torvalds's avatar
Linus Torvalds committed
261
	int rc = 0;
262
	char *full_path = NULL;
Steve French's avatar
Steve French committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
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
	}
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
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;
	}
Linus Torvalds's avatar
Linus Torvalds committed
302

303
	cifs_dbg(FYI, "Full path: %s start at: %lld\n", full_path, file->f_pos);
Linus Torvalds's avatar
Linus Torvalds committed
304

305
ffirst_retry:
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
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;
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:
Linus Torvalds's avatar
Linus Torvalds committed
339
	kfree(full_path);
340
	cifs_put_tlink(tlink);
Linus Torvalds's avatar
Linus Torvalds committed
341 342 343 344
	return rc;
}

/* return length of unicode string in bytes */
345
static int cifs_unicode_bytelen(const char *str)
Linus Torvalds's avatar
Linus Torvalds committed
346 347
{
	int len;
348
	const __le16 *ustr = (const __le16 *)str;
Linus Torvalds's avatar
Linus Torvalds committed
349

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

358
static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level)
Linus Torvalds's avatar
Linus Torvalds committed
359
{
Steve French's avatar
Steve French committed
360
	char *new_entry;
361
	FILE_DIRECTORY_INFO *pDirInfo = (FILE_DIRECTORY_INFO *)old_entry;
Linus Torvalds's avatar
Linus Torvalds committed
362

363
	if (level == SMB_FIND_FILE_INFO_STANDARD) {
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);
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
376
		return NULL;
377
	} else if (((level == SMB_FIND_FILE_INFO_STANDARD) &&
Steve French's avatar
Steve French committed
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;
Steve French's avatar
Steve French committed
384
	} else
Linus Torvalds's avatar
Linus Torvalds committed
385 386 387 388
		return new_entry;

}

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);
476 477 478 479 480 481
		return -EINVAL;
	}

	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
482 483 484
#define UNICODE_DOT cpu_to_le16(0x2e)

/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */
485
static int cifs_entry_is_dot(struct cifs_dirent *de, bool is_unicode)
Linus Torvalds's avatar
Linus Torvalds committed
486 487 488
{
	int rc = 0;

489 490
	if (!de->name)
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
491

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;
Linus Torvalds's avatar
Linus Torvalds committed
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 */
Steve French's avatar
Steve French committed
519
static int is_dir_changed(struct file *file)
520
{
Al Viro's avatar
Al Viro committed
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
Al Viro's avatar
Al Viro committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
557
{
558
	__u16 search_flags;
Linus Torvalds's avatar
Linus Torvalds committed
559 560 561
	int rc = 0;
	int pos_in_buf = 0;
	loff_t first_entry_in_buffer;
Al Viro's avatar
Al Viro committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
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))
Linus Torvalds's avatar
Linus Torvalds committed
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)) {
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
608
		}
Steve French's avatar
Steve French committed
609
		rc = initiate_cifs_search(xid, file);
610
		if (rc) {
611
			cifs_dbg(FYI, "error %d reinitiating a search on rewind\n",
612
				 rc);
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
634 635
			return -ENOENT;
	}
636
	if (index_to_find < cfile->srch_inf.index_of_last_entry) {
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
655
		}
656
		if ((cur_ent == NULL) && (i < pos_in_buf)) {
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
660 661
		}
		rc = 0;
662
		*current_entry = cur_ent;
Linus Torvalds's avatar
Linus Torvalds committed
663
	} else {
664
		cifs_dbg(FYI, "index not in buffer - could not findnext into it\n");
Linus Torvalds's avatar
Linus Torvalds committed
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");
Linus Torvalds's avatar
Linus Torvalds committed
670 671
		*num_to_ret = 0;
	} else
672
		*num_to_ret = cfile->srch_inf.entries_in_buffer - pos_in_buf;
Linus Torvalds's avatar
Linus Torvalds committed
673 674 675 676

	return rc;
}

Al Viro's avatar
Al Viro committed
677 678 679
static int cifs_filldir(char *find_entry, struct file *file,
		struct dir_context *ctx,
		char *scratch_buf, unsigned int max_len)
Linus Torvalds's avatar
Linus Torvalds committed
680
{
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);
684
	struct cifs_dirent de = { NULL, };
685
	struct cifs_fattr fattr;
686
	struct qstr name;
Linus Torvalds's avatar
Linus Torvalds committed
687
	int rc = 0;
688
	ino_t ino;
Linus Torvalds's avatar
Linus Torvalds committed
689

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 */
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;
707 708 709 710 711 712 713 714
		int map_type;

		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR)
			map_type = SFM_MAP_UNI_RSVD;
		else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR)
			map_type = SFU_MAP_UNI_RSVD;
		else
			map_type = NO_MAP_UNI_RSVD;
Linus Torvalds's avatar
Linus Torvalds committed
715

716 717
		name.name = scratch_buf;
		name.len =
718 719 720
			cifs_from_utf16((char *)name.name, (__le16 *)de.name,
					UNICODE_NAME_MAX,
					min_t(size_t, de.namelen,
721
					      (size_t)max_len), nlt, map_type);
722 723 724 725 726
		name.len -= nls_nullsize(nlt);
	} else {
		name.name = de.name;
		name.len = de.namelen;
	}
Linus Torvalds's avatar
Linus Torvalds committed
727

728 729
	switch (file_info->srch_inf.info_level) {
	case SMB_FIND_FILE_UNIX:
730
		cifs_unix_basic_to_fattr(&fattr,
731 732 733 734 735 736 737 738 739 740 741 742 743 744
					 &((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;
	}
745

746 747
	if (de.ino && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
		fattr.cf_uniqueid = de.ino;
748
	} else {
749
		fattr.cf_uniqueid = iunique(sb, ROOT_I);
750 751
		cifs_autodisable_serverino(cifs_sb);
	}
752

753
	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) &&
754
	    couldbe_mf_symlink(&fattr))
755 756 757 758 759 760 761
		/*
		 * 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;

762
	cifs_prime_dcache(file->f_dentry, &name, &fattr);
Steve French's avatar
Steve French committed
763

764
	ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
Al Viro's avatar
Al Viro committed
765
	return !dir_emit(ctx, name.name, name.len, ino, fattr.cf_dtype);
Linus Torvalds's avatar
Linus Torvalds committed
766 767 768
}


Al Viro's avatar
Al Viro committed
769
int cifs_readdir(struct file *file, struct dir_context *ctx)
Linus Torvalds's avatar
Linus Torvalds committed
770 771
{
	int rc = 0;
772 773
	unsigned int xid;
	int i;
774
	struct cifs_tcon *tcon;
Linus Torvalds's avatar
Linus Torvalds committed
775
	struct cifsFileInfo *cifsFile = NULL;
Steve French's avatar
Steve French committed
776
	char *current_entry;
Linus Torvalds's avatar
Linus Torvalds committed
777
	int num_to_fill = 0;
Steve French's avatar
Steve French committed
778
	char *tmp_buf = NULL;
779
	char *end_of_smb;
780
	unsigned int max_len;
Linus Torvalds's avatar
Linus Torvalds committed
781

782
	xid = get_xid();
Linus Torvalds's avatar
Linus Torvalds committed
783

784 785 786 787 788 789
	/*
	 * 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);
790
		cifs_dbg(FYI, "initiate cifs search rc %d\n", rc);
791 792 793 794
		if (rc)
			goto rddir2_exit;
	}

Al Viro's avatar
Al Viro committed
795 796
	if (!dir_emit_dots(file, ctx))
		goto rddir2_exit;
Linus Torvalds's avatar
Linus Torvalds committed
797

Al Viro's avatar
Al Viro committed
798 799 800 801 802 803 804 805 806 807 808 809 810 811
	/* 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;
Linus Torvalds's avatar
Linus Torvalds committed
812 813
			goto rddir2_exit;
		}
Al Viro's avatar
Al Viro committed
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 842 843 844 845 846 847
	} /* 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);
848 849
			break;
		}
Al Viro's avatar
Al Viro committed
850 851 852 853 854 855 856 857
		/*
		 * 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)
858
				rc = 0;
Al Viro's avatar
Al Viro committed
859
			break;
Linus Torvalds's avatar
Linus Torvalds committed
860
		}
Al Viro's avatar
Al Viro committed
861 862 863 864 865 866 867 868 869 870 871 872 873 874

		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);
Linus Torvalds's avatar
Linus Torvalds committed
875 876

rddir2_exit:
877
	free_xid(xid);
Linus Torvalds's avatar
Linus Torvalds committed
878 879
	return rc;
}