readdir.c 25.8 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>
L
Linus Torvalds 已提交
25 26 27 28 29 30 31 32 33
#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"

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

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

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

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
/*
 * 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;

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

	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;
	}

	if (CIFS_SB(sb)->tcon->nocase)
		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;
}

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

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

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

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

S
Steve French 已提交
149
static void
150 151 152 153 154 155 156 157 158 159 160 161 162
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 已提交
163

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

170 171 172 173 174 175 176 177 178 179 180 181 182
	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 已提交
183 184
}

185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
/* 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;
	struct cifsTconInfo *ptcon = cifs_sb->tcon;
	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)) {
			cFYI(1, ("Error closing temporary reparsepoint open)"));
		}
	}
}
 */

L
Linus Torvalds 已提交
217 218 219
static int initiate_cifs_search(const int xid, struct file *file)
{
	int rc = 0;
S
Steve French 已提交
220 221
	char *full_path;
	struct cifsFileInfo *cifsFile;
L
Linus Torvalds 已提交
222 223 224
	struct cifs_sb_info *cifs_sb;
	struct cifsTconInfo *pTcon;

225
	if (file->private_data == NULL) {
S
Steve French 已提交
226 227
		file->private_data =
			kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
L
Linus Torvalds 已提交
228 229
	}

230
	if (file->private_data == NULL)
L
Linus Torvalds 已提交
231 232
		return -ENOMEM;
	cifsFile = file->private_data;
233 234
	cifsFile->invalidHandle = true;
	cifsFile->srch_inf.endOfSearch = false;
L
Linus Torvalds 已提交
235

236
	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
237
	if (cifs_sb == NULL)
L
Linus Torvalds 已提交
238 239 240
		return -EINVAL;

	pTcon = cifs_sb->tcon;
241
	if (pTcon == NULL)
L
Linus Torvalds 已提交
242 243
		return -EINVAL;

244
	full_path = build_path_from_dentry(file->f_path.dentry);
L
Linus Torvalds 已提交
245

S
Steve French 已提交
246
	if (full_path == NULL)
L
Linus Torvalds 已提交
247 248
		return -ENOMEM;

S
Steve French 已提交
249
	cFYI(1, ("Full path: %s start at: %lld", full_path, file->f_pos));
L
Linus Torvalds 已提交
250

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

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

S
Steve French 已提交
290
	for (len = 0; len <= PATH_MAX; len++) {
291
		if (ustr[len] == 0)
L
Linus Torvalds 已提交
292 293
			return len << 1;
	}
S
Steve French 已提交
294
	cFYI(1, ("Unicode string longer than PATH_MAX found"));
L
Linus Torvalds 已提交
295 296 297
	return len << 1;
}

298
static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level)
L
Linus Torvalds 已提交
299
{
S
Steve French 已提交
300
	char *new_entry;
S
Steve French 已提交
301
	FILE_DIRECTORY_INFO *pDirInfo = (FILE_DIRECTORY_INFO *)old_entry;
L
Linus Torvalds 已提交
302

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

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

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

	return rc;
}

408 409
/* 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 已提交
410
static int is_dir_changed(struct file *file)
411
{
412 413
	struct inode *inode = file->f_path.dentry->d_inode;
	struct cifsInodeInfo *cifsInfo = CIFS_I(inode);
414

415
	if (cifsInfo->time == 0)
416 417 418 419 420 421
		return 1; /* directory was changed, perhaps due to unlink */
	else
		return 0;

}

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 475 476 477 478 479 480 481 482 483 484 485
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 {
		cFYI(1, ("Unknown findfirst level %d", level));
		return -EINVAL;
	}
	cifsFile->srch_inf.resume_name_len = len;
	cifsFile->srch_inf.presume_name = filename;
	return rc;
}

L
Linus Torvalds 已提交
486 487 488 489 490 491 492
/* 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 已提交
493
	struct file *file, char **ppCurrentEntry, int *num_to_ret)
L
Linus Torvalds 已提交
494 495 496 497 498
{
	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 已提交
499
	struct cifsFileInfo *cifsFile = file->private_data;
L
Linus Torvalds 已提交
500
	/* check if index in the buffer */
501

S
Steve French 已提交
502
	if ((cifsFile == NULL) || (ppCurrentEntry == NULL) ||
503
	   (num_to_ret == NULL))
L
Linus Torvalds 已提交
504
		return -ENOENT;
505

L
Linus Torvalds 已提交
506
	*ppCurrentEntry = NULL;
S
Steve French 已提交
507 508
	first_entry_in_buffer =
		cifsFile->srch_inf.index_of_last_entry -
L
Linus Torvalds 已提交
509
			cifsFile->srch_inf.entries_in_buffer;
510 511 512 513 514 515 516

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

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

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

		current_entry = cifsFile->srch_inf.srch_entries_start;
L
Linus Torvalds 已提交
569 570 571
		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;
S
Steve French 已提交
572
		cFYI(1, ("found entry - pos_in_buf %d", pos_in_buf));
573

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

S
Steve French 已提交
592 593
	if (pos_in_buf >= cifsFile->srch_inf.entries_in_buffer) {
		cFYI(1, ("can not return entries pos_in_buf beyond last"));
L
Linus Torvalds 已提交
594 595 596 597 598 599 600 601 602 603
		*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,
604
	struct cifs_sb_info *cifs_sb, unsigned int max_len, __u64 *pinum)
L
Linus Torvalds 已提交
605 606 607
{
	int rc = 0;
	unsigned int len = 0;
S
Steve French 已提交
608 609
	char *filename;
	struct nls_table *nlt = cifs_sb->local_nls;
L
Linus Torvalds 已提交
610 611 612

	*pinum = 0;

S
Steve French 已提交
613
	if (level == SMB_FIND_FILE_UNIX) {
S
Steve French 已提交
614
		FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
L
Linus Torvalds 已提交
615 616

		filename = &pFindData->FileName[0];
S
Steve French 已提交
617
		if (unicode) {
L
Linus Torvalds 已提交
618 619 620 621 622 623
			len = cifs_unicode_bytelen(filename);
		} else {
			/* BB should we make this strnlen of PATH_MAX? */
			len = strnlen(filename, PATH_MAX);
		}

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

S
Steve French 已提交
657
	if (len > max_len) {
S
Steve French 已提交
658
		cERROR(1, ("bad search response length %d past smb end", len));
659 660 661
		return -EINVAL;
	}

S
Steve French 已提交
662
	if (unicode) {
663 664
		pqst->len = cifs_from_ucs2((char *) pqst->name,
					   (__le16 *) filename,
665 666
					   UNICODE_NAME_MAX,
					   min(len, max_len), nlt,
667 668
					   cifs_sb->mnt_cifs_flags &
						CIFS_MOUNT_MAP_SPECIAL_CHR);
669
		pqst->len -= nls_nullsize(nlt);
L
Linus Torvalds 已提交
670 671 672 673
	} else {
		pqst->name = filename;
		pqst->len = len;
	}
S
Steve French 已提交
674
	pqst->hash = full_name_hash(pqst->name, pqst->len);
S
Steve French 已提交
675
/*	cFYI(1, ("filldir on %s",pqst->name));  */
L
Linus Torvalds 已提交
676 677 678
	return rc;
}

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

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

	pCifsF = file->private_data;
699

S
Steve French 已提交
700
	if ((scratch_buf == NULL) || (pfindEntry == NULL) || (pCifsF == NULL))
L
Linus Torvalds 已提交
701 702
		return -ENOENT;

S
Steve French 已提交
703
	rc = cifs_entry_is_dot(pfindEntry, pCifsF);
704
	/* skip . and .. since we added them first */
S
Steve French 已提交
705
	if (rc != 0)
706 707
		return 0;

708 709
	sb = file->f_path.dentry->d_sb;
	cifs_sb = CIFS_SB(sb);
L
Linus Torvalds 已提交
710 711

	qstring.name = scratch_buf;
S
Steve French 已提交
712
	rc = cifs_get_name_from_search_buf(&qstring, pfindEntry,
L
Linus Torvalds 已提交
713
			pCifsF->srch_inf.info_level,
S
Steve French 已提交
714
			pCifsF->srch_inf.unicode, cifs_sb,
715
			max_len, &inum /* returned */);
L
Linus Torvalds 已提交
716

S
Steve French 已提交
717
	if (rc)
L
Linus Torvalds 已提交
718 719
		return rc;

720
	if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX)
721 722 723
		cifs_unix_basic_to_fattr(&fattr,
				 &((FILE_UNIX_INFO *) pfindEntry)->basic,
				 cifs_sb);
724 725 726 727 728 729
	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);
730

731
	if (inum && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
732
		fattr.cf_uniqueid = inum;
733
	} else {
734
		fattr.cf_uniqueid = iunique(sb, ROOT_I);
735 736
		cifs_autodisable_serverino(cifs_sb);
	}
737

738 739
	ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
	tmp_dentry = cifs_readdir_lookup(file->f_dentry, &qstring, &fattr);
S
Steve French 已提交
740 741

	rc = filldir(direntry, qstring.name, qstring.len, file->f_pos,
742 743 744 745 746 747 748 749 750 751
		     ino, fattr.cf_dtype);

	/*
	 * we can not return filldir errors to the caller since they are
	 * "normal" when the stat blocksize is too small - we return remapped
	 * error instead
	 *
	 * FIXME: This looks bogus. filldir returns -EOVERFLOW in the above
	 * case already. Why should we be clobbering other errors from it?
	 */
S
Steve French 已提交
752 753
	if (rc) {
		cFYI(1, ("filldir rc = %d", rc));
754
		rc = -EOVERFLOW;
L
Linus Torvalds 已提交
755 756 757 758 759 760 761 762 763
	}
	dput(tmp_dentry);
	return rc;
}


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

	xid = GetXid();

776
	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
L
Linus Torvalds 已提交
777
	pTcon = cifs_sb->tcon;
S
Steve French 已提交
778
	if (pTcon == NULL)
L
Linus Torvalds 已提交
779 780 781 782
		return -EINVAL;

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

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

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

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

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

rddir2_exit:
	FreeXid(xid);
	return rc;
}