1/*
2 *   fs/cifs/readdir.c
3 *
4 *   Directory search handling
5 *
6 *   Copyright (C) International Business Machines  Corp., 2004, 2008
7 *   Author(s): Steve French (sfrench@us.ibm.com)
8 *
9 *   This library is free software; you can redistribute it and/or modify
10 *   it under the terms of the GNU Lesser General Public License as published
11 *   by the Free Software Foundation; either version 2.1 of the License, or
12 *   (at your option) any later version.
13 *
14 *   This library is distributed in the hope that it will be useful,
15 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
17 *   the GNU Lesser General Public License for more details.
18 *
19 *   You should have received a copy of the GNU Lesser General Public License
20 *   along with this library; if not, write to the Free Software
21 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23#include <linux/fs.h>
24#include <linux/pagemap.h>
25#include <linux/slab.h>
26#include <linux/stat.h>
27#include "cifspdu.h"
28#include "cifsglob.h"
29#include "cifsproto.h"
30#include "cifs_unicode.h"
31#include "cifs_debug.h"
32#include "cifs_fs_sb.h"
33#include "cifsfs.h"
34
35/*
36 * To be safe - for UCS to UTF-8 with strings loaded with the rare long
37 * characters alloc more to account for such multibyte target UTF-8
38 * characters.
39 */
40#define UNICODE_NAME_MAX ((4 * NAME_MAX) + 2)
41
42#ifdef CONFIG_CIFS_DEBUG2
43static void dump_cifs_file_struct(struct file *file, char *label)
44{
45	struct cifsFileInfo *cf;
46
47	if (file) {
48		cf = file->private_data;
49		if (cf == NULL) {
50			cFYI(1, "empty cifs private file data");
51			return;
52		}
53		if (cf->invalidHandle)
54			cFYI(1, "invalid handle");
55		if (cf->srch_inf.endOfSearch)
56			cFYI(1, "end of search");
57		if (cf->srch_inf.emptyDir)
58			cFYI(1, "empty dir");
59	}
60}
61#else
62static inline void dump_cifs_file_struct(struct file *file, char *label)
63{
64}
65#endif /* DEBUG2 */
66
67/*
68 * Find the dentry that matches "name". If there isn't one, create one. If it's
69 * a negative dentry or the uniqueid changed, then drop it and recreate it.
70 */
71static struct dentry *
72cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
73		    struct cifs_fattr *fattr)
74{
75	struct dentry *dentry, *alias;
76	struct inode *inode;
77	struct super_block *sb = parent->d_inode->i_sb;
78
79	cFYI(1, "For %s", name->name);
80
81	if (parent->d_op && parent->d_op->d_hash)
82		parent->d_op->d_hash(parent, name);
83	else
84		name->hash = full_name_hash(name->name, name->len);
85
86	dentry = d_lookup(parent, name);
87	if (dentry) {
88		if (dentry->d_inode != NULL)
89			return dentry;
90		d_drop(dentry);
91		dput(dentry);
92	}
93
94	dentry = d_alloc(parent, name);
95	if (dentry == NULL)
96		return NULL;
97
98	inode = cifs_iget(sb, fattr);
99	if (!inode) {
100		dput(dentry);
101		return NULL;
102	}
103
104	if (CIFS_SB(sb)->tcon->nocase)
105		dentry->d_op = &cifs_ci_dentry_ops;
106	else
107		dentry->d_op = &cifs_dentry_ops;
108
109	alias = d_materialise_unique(dentry, inode);
110	if (alias != NULL) {
111		dput(dentry);
112		if (IS_ERR(alias))
113			return NULL;
114		dentry = alias;
115	}
116
117	return dentry;
118}
119
120static void
121cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
122{
123	fattr->cf_uid = cifs_sb->mnt_uid;
124	fattr->cf_gid = cifs_sb->mnt_gid;
125
126	if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
127		fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
128		fattr->cf_dtype = DT_DIR;
129	} else {
130		fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
131		fattr->cf_dtype = DT_REG;
132	}
133
134	if (fattr->cf_cifsattrs & ATTR_READONLY)
135		fattr->cf_mode &= ~S_IWUGO;
136
137	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL &&
138	    fattr->cf_cifsattrs & ATTR_SYSTEM) {
139		if (fattr->cf_eof == 0)  {
140			fattr->cf_mode &= ~S_IFMT;
141			fattr->cf_mode |= S_IFIFO;
142			fattr->cf_dtype = DT_FIFO;
143		} else {
144			/*
145			 * trying to get the type and mode via SFU can be slow,
146			 * so just call those regular files for now, and mark
147			 * for reval
148			 */
149			fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
150		}
151	}
152}
153
154static void
155cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,
156		       struct cifs_sb_info *cifs_sb)
157{
158	memset(fattr, 0, sizeof(*fattr));
159	fattr->cf_cifsattrs = le32_to_cpu(info->ExtFileAttributes);
160	fattr->cf_eof = le64_to_cpu(info->EndOfFile);
161	fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
162	fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
163	fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
164	fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
165
166	cifs_fill_common_info(fattr, cifs_sb);
167}
168
169static void
170cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info,
171		       struct cifs_sb_info *cifs_sb)
172{
173	int offset = cifs_sb->tcon->ses->server->timeAdj;
174
175	memset(fattr, 0, sizeof(*fattr));
176	fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate,
177					    info->LastAccessTime, offset);
178	fattr->cf_ctime = cnvrtDosUnixTm(info->LastWriteDate,
179					    info->LastWriteTime, offset);
180	fattr->cf_mtime = cnvrtDosUnixTm(info->LastWriteDate,
181					    info->LastWriteTime, offset);
182
183	fattr->cf_cifsattrs = le16_to_cpu(info->Attributes);
184	fattr->cf_bytes = le32_to_cpu(info->AllocationSize);
185	fattr->cf_eof = le32_to_cpu(info->DataSize);
186
187	cifs_fill_common_info(fattr, cifs_sb);
188}
189
190/* BB eventually need to add the following helper function to
191      resolve NT_STATUS_STOPPED_ON_SYMLINK return code when
192      we try to do FindFirst on (NTFS) directory symlinks */
193/*
194int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb,
195			     int xid)
196{
197	__u16 fid;
198	int len;
199	int oplock = 0;
200	int rc;
201	struct cifsTconInfo *ptcon = cifs_sb->tcon;
202	char *tmpbuffer;
203
204	rc = CIFSSMBOpen(xid, ptcon, full_path, FILE_OPEN, GENERIC_READ,
205			OPEN_REPARSE_POINT, &fid, &oplock, NULL,
206			cifs_sb->local_nls,
207			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
208	if (!rc) {
209		tmpbuffer = kmalloc(maxpath);
210		rc = CIFSSMBQueryReparseLinkInfo(xid, ptcon, full_path,
211				tmpbuffer,
212				maxpath -1,
213				fid,
214				cifs_sb->local_nls);
215		if (CIFSSMBClose(xid, ptcon, fid)) {
216			cFYI(1, "Error closing temporary reparsepoint open");
217		}
218	}
219}
220 */
221
222static int initiate_cifs_search(const int xid, struct file *file)
223{
224	int rc = 0;
225	char *full_path;
226	struct cifsFileInfo *cifsFile;
227	struct cifs_sb_info *cifs_sb;
228	struct cifsTconInfo *pTcon;
229
230	if (file->private_data == NULL) {
231		file->private_data =
232			kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
233	}
234
235	if (file->private_data == NULL)
236		return -ENOMEM;
237	cifsFile = file->private_data;
238	cifsFile->invalidHandle = true;
239	cifsFile->srch_inf.endOfSearch = false;
240
241	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
242	if (cifs_sb == NULL)
243		return -EINVAL;
244
245	pTcon = cifs_sb->tcon;
246	if (pTcon == NULL)
247		return -EINVAL;
248
249	full_path = build_path_from_dentry(file->f_path.dentry);
250
251	if (full_path == NULL)
252		return -ENOMEM;
253
254	cFYI(1, "Full path: %s start at: %lld", full_path, file->f_pos);
255
256ffirst_retry:
257	/* test for Unix extensions */
258	/* but now check for them on the share/mount not on the SMB session */
259/*	if (pTcon->ses->capabilities & CAP_UNIX) { */
260	if (pTcon->unix_ext)
261		cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX;
262	else if ((pTcon->ses->capabilities &
263			(CAP_NT_SMBS | CAP_NT_FIND)) == 0) {
264		cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD;
265	} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
266		cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
267	} else {
268		cifsFile->srch_inf.info_level = SMB_FIND_FILE_DIRECTORY_INFO;
269	}
270
271	rc = CIFSFindFirst(xid, pTcon, full_path, cifs_sb->local_nls,
272		&cifsFile->netfid, &cifsFile->srch_inf,
273		cifs_sb->mnt_cifs_flags &
274			CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb));
275	if (rc == 0)
276		cifsFile->invalidHandle = false;
277	/* BB add following call to handle readdir on new NTFS symlink errors
278	else if STATUS_STOPPED_ON_SYMLINK
279		call get_symlink_reparse_path and retry with new path */
280	else if ((rc == -EOPNOTSUPP) &&
281		(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
282		cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM;
283		goto ffirst_retry;
284	}
285	kfree(full_path);
286	return rc;
287}
288
289/* return length of unicode string in bytes */
290static int cifs_unicode_bytelen(char *str)
291{
292	int len;
293	__le16 *ustr = (__le16 *)str;
294
295	for (len = 0; len <= PATH_MAX; len++) {
296		if (ustr[len] == 0)
297			return len << 1;
298	}
299	cFYI(1, "Unicode string longer than PATH_MAX found");
300	return len << 1;
301}
302
303static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level)
304{
305	char *new_entry;
306	FILE_DIRECTORY_INFO *pDirInfo = (FILE_DIRECTORY_INFO *)old_entry;
307
308	if (level == SMB_FIND_FILE_INFO_STANDARD) {
309		FIND_FILE_STANDARD_INFO *pfData;
310		pfData = (FIND_FILE_STANDARD_INFO *)pDirInfo;
311
312		new_entry = old_entry + sizeof(FIND_FILE_STANDARD_INFO) +
313				pfData->FileNameLength;
314	} else
315		new_entry = old_entry + le32_to_cpu(pDirInfo->NextEntryOffset);
316	cFYI(1, "new entry %p old entry %p", new_entry, old_entry);
317	/* validate that new_entry is not past end of SMB */
318	if (new_entry >= end_of_smb) {
319		cERROR(1, "search entry %p began after end of SMB %p old entry %p",
320			new_entry, end_of_smb, old_entry);
321		return NULL;
322	} else if (((level == SMB_FIND_FILE_INFO_STANDARD) &&
323		    (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb))
324		  || ((level != SMB_FIND_FILE_INFO_STANDARD) &&
325		   (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb)))  {
326		cERROR(1, "search entry %p extends after end of SMB %p",
327			new_entry, end_of_smb);
328		return NULL;
329	} else
330		return new_entry;
331
332}
333
334#define UNICODE_DOT cpu_to_le16(0x2e)
335
336/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */
337static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile)
338{
339	int rc = 0;
340	char *filename = NULL;
341	int len = 0;
342
343	if (cfile->srch_inf.info_level == SMB_FIND_FILE_UNIX) {
344		FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
345		filename = &pFindData->FileName[0];
346		if (cfile->srch_inf.unicode) {
347			len = cifs_unicode_bytelen(filename);
348		} else {
349			/* BB should we make this strnlen of PATH_MAX? */
350			len = strnlen(filename, 5);
351		}
352	} else if (cfile->srch_inf.info_level == SMB_FIND_FILE_DIRECTORY_INFO) {
353		FILE_DIRECTORY_INFO *pFindData =
354			(FILE_DIRECTORY_INFO *)current_entry;
355		filename = &pFindData->FileName[0];
356		len = le32_to_cpu(pFindData->FileNameLength);
357	} else if (cfile->srch_inf.info_level ==
358			SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
359		FILE_FULL_DIRECTORY_INFO *pFindData =
360			(FILE_FULL_DIRECTORY_INFO *)current_entry;
361		filename = &pFindData->FileName[0];
362		len = le32_to_cpu(pFindData->FileNameLength);
363	} else if (cfile->srch_inf.info_level ==
364			SMB_FIND_FILE_ID_FULL_DIR_INFO) {
365		SEARCH_ID_FULL_DIR_INFO *pFindData =
366			(SEARCH_ID_FULL_DIR_INFO *)current_entry;
367		filename = &pFindData->FileName[0];
368		len = le32_to_cpu(pFindData->FileNameLength);
369	} else if (cfile->srch_inf.info_level ==
370			SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
371		FILE_BOTH_DIRECTORY_INFO *pFindData =
372			(FILE_BOTH_DIRECTORY_INFO *)current_entry;
373		filename = &pFindData->FileName[0];
374		len = le32_to_cpu(pFindData->FileNameLength);
375	} else if (cfile->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) {
376		FIND_FILE_STANDARD_INFO *pFindData =
377			(FIND_FILE_STANDARD_INFO *)current_entry;
378		filename = &pFindData->FileName[0];
379		len = pFindData->FileNameLength;
380	} else {
381		cFYI(1, "Unknown findfirst level %d",
382			 cfile->srch_inf.info_level);
383	}
384
385	if (filename) {
386		if (cfile->srch_inf.unicode) {
387			__le16 *ufilename = (__le16 *)filename;
388			if (len == 2) {
389				/* check for . */
390				if (ufilename[0] == UNICODE_DOT)
391					rc = 1;
392			} else if (len == 4) {
393				/* check for .. */
394				if ((ufilename[0] == UNICODE_DOT)
395				   && (ufilename[1] == UNICODE_DOT))
396					rc = 2;
397			}
398		} else /* ASCII */ {
399			if (len == 1) {
400				if (filename[0] == '.')
401					rc = 1;
402			} else if (len == 2) {
403				if ((filename[0] == '.') && (filename[1] == '.'))
404					rc = 2;
405			}
406		}
407	}
408
409	return rc;
410}
411
412/* Check if directory that we are searching has changed so we can decide
413   whether we can use the cached search results from the previous search */
414static int is_dir_changed(struct file *file)
415{
416	struct inode *inode = file->f_path.dentry->d_inode;
417	struct cifsInodeInfo *cifsInfo = CIFS_I(inode);
418
419	if (cifsInfo->time == 0)
420		return 1; /* directory was changed, perhaps due to unlink */
421	else
422		return 0;
423
424}
425
426static int cifs_save_resume_key(const char *current_entry,
427	struct cifsFileInfo *cifsFile)
428{
429	int rc = 0;
430	unsigned int len = 0;
431	__u16 level;
432	char *filename;
433
434	if ((cifsFile == NULL) || (current_entry == NULL))
435		return -EINVAL;
436
437	level = cifsFile->srch_inf.info_level;
438
439	if (level == SMB_FIND_FILE_UNIX) {
440		FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
441
442		filename = &pFindData->FileName[0];
443		if (cifsFile->srch_inf.unicode) {
444			len = cifs_unicode_bytelen(filename);
445		} else {
446			/* BB should we make this strnlen of PATH_MAX? */
447			len = strnlen(filename, PATH_MAX);
448		}
449		cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
450	} else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
451		FILE_DIRECTORY_INFO *pFindData =
452			(FILE_DIRECTORY_INFO *)current_entry;
453		filename = &pFindData->FileName[0];
454		len = le32_to_cpu(pFindData->FileNameLength);
455		cifsFile->srch_inf.resume_key = pFindData->FileIndex;
456	} else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
457		FILE_FULL_DIRECTORY_INFO *pFindData =
458			(FILE_FULL_DIRECTORY_INFO *)current_entry;
459		filename = &pFindData->FileName[0];
460		len = le32_to_cpu(pFindData->FileNameLength);
461		cifsFile->srch_inf.resume_key = pFindData->FileIndex;
462	} else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
463		SEARCH_ID_FULL_DIR_INFO *pFindData =
464			(SEARCH_ID_FULL_DIR_INFO *)current_entry;
465		filename = &pFindData->FileName[0];
466		len = le32_to_cpu(pFindData->FileNameLength);
467		cifsFile->srch_inf.resume_key = pFindData->FileIndex;
468	} else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
469		FILE_BOTH_DIRECTORY_INFO *pFindData =
470			(FILE_BOTH_DIRECTORY_INFO *)current_entry;
471		filename = &pFindData->FileName[0];
472		len = le32_to_cpu(pFindData->FileNameLength);
473		cifsFile->srch_inf.resume_key = pFindData->FileIndex;
474	} else if (level == SMB_FIND_FILE_INFO_STANDARD) {
475		FIND_FILE_STANDARD_INFO *pFindData =
476			(FIND_FILE_STANDARD_INFO *)current_entry;
477		filename = &pFindData->FileName[0];
478		/* one byte length, no name conversion */
479		len = (unsigned int)pFindData->FileNameLength;
480		cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
481	} else {
482		cFYI(1, "Unknown findfirst level %d", level);
483		return -EINVAL;
484	}
485	cifsFile->srch_inf.resume_name_len = len;
486	cifsFile->srch_inf.presume_name = filename;
487	return rc;
488}
489
490/* find the corresponding entry in the search */
491/* Note that the SMB server returns search entries for . and .. which
492   complicates logic here if we choose to parse for them and we do not
493   assume that they are located in the findfirst return buffer.*/
494/* We start counting in the buffer with entry 2 and increment for every
495   entry (do not increment for . or .. entry) */
496static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
497	struct file *file, char **ppCurrentEntry, int *num_to_ret)
498{
499	int rc = 0;
500	int pos_in_buf = 0;
501	loff_t first_entry_in_buffer;
502	loff_t index_to_find = file->f_pos;
503	struct cifsFileInfo *cifsFile = file->private_data;
504	/* check if index in the buffer */
505
506	if ((cifsFile == NULL) || (ppCurrentEntry == NULL) ||
507	   (num_to_ret == NULL))
508		return -ENOENT;
509
510	*ppCurrentEntry = NULL;
511	first_entry_in_buffer =
512		cifsFile->srch_inf.index_of_last_entry -
513			cifsFile->srch_inf.entries_in_buffer;
514
515	/* if first entry in buf is zero then is first buffer
516	in search response data which means it is likely . and ..
517	will be in this buffer, although some servers do not return
518	. and .. for the root of a drive and for those we need
519	to start two entries earlier */
520
521	dump_cifs_file_struct(file, "In fce ");
522	if (((index_to_find < cifsFile->srch_inf.index_of_last_entry) &&
523	     is_dir_changed(file)) ||
524	   (index_to_find < first_entry_in_buffer)) {
525		/* close and restart search */
526		cFYI(1, "search backing up - close and restart search");
527		write_lock(&GlobalSMBSeslock);
528		if (!cifsFile->srch_inf.endOfSearch &&
529		    !cifsFile->invalidHandle) {
530			cifsFile->invalidHandle = true;
531			write_unlock(&GlobalSMBSeslock);
532			CIFSFindClose(xid, pTcon, cifsFile->netfid);
533		} else
534			write_unlock(&GlobalSMBSeslock);
535		if (cifsFile->srch_inf.ntwrk_buf_start) {
536			cFYI(1, "freeing SMB ff cache buf on search rewind");
537			if (cifsFile->srch_inf.smallBuf)
538				cifs_small_buf_release(cifsFile->srch_inf.
539						ntwrk_buf_start);
540			else
541				cifs_buf_release(cifsFile->srch_inf.
542						ntwrk_buf_start);
543			cifsFile->srch_inf.ntwrk_buf_start = NULL;
544		}
545		rc = initiate_cifs_search(xid, file);
546		if (rc) {
547			cFYI(1, "error %d reinitiating a search on rewind",
548				 rc);
549			return rc;
550		}
551		cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile);
552	}
553
554	while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) &&
555	      (rc == 0) && !cifsFile->srch_inf.endOfSearch) {
556		cFYI(1, "calling findnext2");
557		rc = CIFSFindNext(xid, pTcon, cifsFile->netfid,
558				  &cifsFile->srch_inf);
559		cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile);
560		if (rc)
561			return -ENOENT;
562	}
563	if (index_to_find < cifsFile->srch_inf.index_of_last_entry) {
564		/* we found the buffer that contains the entry */
565		/* scan and find it */
566		int i;
567		char *current_entry;
568		char *end_of_smb = cifsFile->srch_inf.ntwrk_buf_start +
569			smbCalcSize((struct smb_hdr *)
570				cifsFile->srch_inf.ntwrk_buf_start);
571
572		current_entry = cifsFile->srch_inf.srch_entries_start;
573		first_entry_in_buffer = cifsFile->srch_inf.index_of_last_entry
574					- cifsFile->srch_inf.entries_in_buffer;
575		pos_in_buf = index_to_find - first_entry_in_buffer;
576		cFYI(1, "found entry - pos_in_buf %d", pos_in_buf);
577
578		for (i = 0; (i < (pos_in_buf)) && (current_entry != NULL); i++) {
579			/* go entry by entry figuring out which is first */
580			current_entry = nxt_dir_entry(current_entry, end_of_smb,
581						cifsFile->srch_inf.info_level);
582		}
583		if ((current_entry == NULL) && (i < pos_in_buf)) {
584			cERROR(1, "reached end of buf searching for pos in buf"
585			  " %d index to find %lld rc %d",
586			  pos_in_buf, index_to_find, rc);
587		}
588		rc = 0;
589		*ppCurrentEntry = current_entry;
590	} else {
591		cFYI(1, "index not in buffer - could not findnext into it");
592		return 0;
593	}
594
595	if (pos_in_buf >= cifsFile->srch_inf.entries_in_buffer) {
596		cFYI(1, "can not return entries pos_in_buf beyond last");
597		*num_to_ret = 0;
598	} else
599		*num_to_ret = cifsFile->srch_inf.entries_in_buffer - pos_in_buf;
600
601	return rc;
602}
603
604/* inode num, inode type and filename returned */
605static int cifs_get_name_from_search_buf(struct qstr *pqst,
606	char *current_entry, __u16 level, unsigned int unicode,
607	struct cifs_sb_info *cifs_sb, unsigned int max_len, __u64 *pinum)
608{
609	int rc = 0;
610	unsigned int len = 0;
611	char *filename;
612	struct nls_table *nlt = cifs_sb->local_nls;
613
614	*pinum = 0;
615
616	if (level == SMB_FIND_FILE_UNIX) {
617		FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
618
619		filename = &pFindData->FileName[0];
620		if (unicode) {
621			len = cifs_unicode_bytelen(filename);
622		} else {
623			/* BB should we make this strnlen of PATH_MAX? */
624			len = strnlen(filename, PATH_MAX);
625		}
626
627		*pinum = le64_to_cpu(pFindData->basic.UniqueId);
628	} else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
629		FILE_DIRECTORY_INFO *pFindData =
630			(FILE_DIRECTORY_INFO *)current_entry;
631		filename = &pFindData->FileName[0];
632		len = le32_to_cpu(pFindData->FileNameLength);
633	} else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
634		FILE_FULL_DIRECTORY_INFO *pFindData =
635			(FILE_FULL_DIRECTORY_INFO *)current_entry;
636		filename = &pFindData->FileName[0];
637		len = le32_to_cpu(pFindData->FileNameLength);
638	} else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
639		SEARCH_ID_FULL_DIR_INFO *pFindData =
640			(SEARCH_ID_FULL_DIR_INFO *)current_entry;
641		filename = &pFindData->FileName[0];
642		len = le32_to_cpu(pFindData->FileNameLength);
643		*pinum = le64_to_cpu(pFindData->UniqueId);
644	} else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
645		FILE_BOTH_DIRECTORY_INFO *pFindData =
646			(FILE_BOTH_DIRECTORY_INFO *)current_entry;
647		filename = &pFindData->FileName[0];
648		len = le32_to_cpu(pFindData->FileNameLength);
649	} else if (level == SMB_FIND_FILE_INFO_STANDARD) {
650		FIND_FILE_STANDARD_INFO *pFindData =
651			(FIND_FILE_STANDARD_INFO *)current_entry;
652		filename = &pFindData->FileName[0];
653		/* one byte length, no name conversion */
654		len = (unsigned int)pFindData->FileNameLength;
655	} else {
656		cFYI(1, "Unknown findfirst level %d", level);
657		return -EINVAL;
658	}
659
660	if (len > max_len) {
661		cERROR(1, "bad search response length %d past smb end", len);
662		return -EINVAL;
663	}
664
665	if (unicode) {
666		pqst->len = cifs_from_ucs2((char *) pqst->name,
667					   (__le16 *) filename,
668					   UNICODE_NAME_MAX,
669					   min(len, max_len), nlt,
670					   cifs_sb->mnt_cifs_flags &
671						CIFS_MOUNT_MAP_SPECIAL_CHR);
672		pqst->len -= nls_nullsize(nlt);
673	} else {
674		pqst->name = filename;
675		pqst->len = len;
676	}
677	return rc;
678}
679
680static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir,
681			void *direntry, char *scratch_buf, unsigned int max_len)
682{
683	int rc = 0;
684	struct qstr qstring;
685	struct cifsFileInfo *pCifsF;
686	u64    inum;
687	ino_t  ino;
688	struct super_block *sb;
689	struct cifs_sb_info *cifs_sb;
690	struct dentry *tmp_dentry;
691	struct cifs_fattr fattr;
692
693	/* get filename and len into qstring */
694	/* get dentry */
695	/* decide whether to create and populate ionde */
696	if ((direntry == NULL) || (file == NULL))
697		return -EINVAL;
698
699	pCifsF = file->private_data;
700
701	if ((scratch_buf == NULL) || (pfindEntry == NULL) || (pCifsF == NULL))
702		return -ENOENT;
703
704	rc = cifs_entry_is_dot(pfindEntry, pCifsF);
705	/* skip . and .. since we added them first */
706	if (rc != 0)
707		return 0;
708
709	sb = file->f_path.dentry->d_sb;
710	cifs_sb = CIFS_SB(sb);
711
712	qstring.name = scratch_buf;
713	rc = cifs_get_name_from_search_buf(&qstring, pfindEntry,
714			pCifsF->srch_inf.info_level,
715			pCifsF->srch_inf.unicode, cifs_sb,
716			max_len, &inum /* returned */);
717
718	if (rc)
719		return rc;
720
721	if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX)
722		cifs_unix_basic_to_fattr(&fattr,
723				 &((FILE_UNIX_INFO *) pfindEntry)->basic,
724				 cifs_sb);
725	else if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD)
726		cifs_std_info_to_fattr(&fattr, (FIND_FILE_STANDARD_INFO *)
727					pfindEntry, cifs_sb);
728	else
729		cifs_dir_info_to_fattr(&fattr, (FILE_DIRECTORY_INFO *)
730					pfindEntry, cifs_sb);
731
732	if (inum && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
733		fattr.cf_uniqueid = inum;
734	} else {
735		fattr.cf_uniqueid = iunique(sb, ROOT_I);
736		cifs_autodisable_serverino(cifs_sb);
737	}
738
739	ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
740	tmp_dentry = cifs_readdir_lookup(file->f_dentry, &qstring, &fattr);
741
742	rc = filldir(direntry, qstring.name, qstring.len, file->f_pos,
743		     ino, fattr.cf_dtype);
744
745	if (rc) {
746		cFYI(1, "filldir rc = %d", rc);
747		rc = -EOVERFLOW;
748	}
749	dput(tmp_dentry);
750	return rc;
751}
752
753
754int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
755{
756	int rc = 0;
757	int xid, i;
758	struct cifs_sb_info *cifs_sb;
759	struct cifsTconInfo *pTcon;
760	struct cifsFileInfo *cifsFile = NULL;
761	char *current_entry;
762	int num_to_fill = 0;
763	char *tmp_buf = NULL;
764	char *end_of_smb;
765	unsigned int max_len;
766
767	xid = GetXid();
768
769	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
770	pTcon = cifs_sb->tcon;
771	if (pTcon == NULL)
772		return -EINVAL;
773
774	switch ((int) file->f_pos) {
775	case 0:
776		if (filldir(direntry, ".", 1, file->f_pos,
777		     file->f_path.dentry->d_inode->i_ino, DT_DIR) < 0) {
778			cERROR(1, "Filldir for current dir failed");
779			rc = -ENOMEM;
780			break;
781		}
782		file->f_pos++;
783	case 1:
784		if (filldir(direntry, "..", 2, file->f_pos,
785		     file->f_path.dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) {
786			cERROR(1, "Filldir for parent dir failed");
787			rc = -ENOMEM;
788			break;
789		}
790		file->f_pos++;
791	default:
792		/* 1) If search is active,
793			is in current search buffer?
794			if it before then restart search
795			if after then keep searching till find it */
796
797		if (file->private_data == NULL) {
798			rc = initiate_cifs_search(xid, file);
799			cFYI(1, "initiate cifs search rc %d", rc);
800			if (rc) {
801				FreeXid(xid);
802				return rc;
803			}
804		}
805		if (file->private_data == NULL) {
806			rc = -EINVAL;
807			FreeXid(xid);
808			return rc;
809		}
810		cifsFile = file->private_data;
811		if (cifsFile->srch_inf.endOfSearch) {
812			if (cifsFile->srch_inf.emptyDir) {
813				cFYI(1, "End of search, empty dir");
814				rc = 0;
815				break;
816			}
817		} /* else {
818			cifsFile->invalidHandle = true;
819			CIFSFindClose(xid, pTcon, cifsFile->netfid);
820		} */
821
822		rc = find_cifs_entry(xid, pTcon, file,
823				&current_entry, &num_to_fill);
824		if (rc) {
825			cFYI(1, "fce error %d", rc);
826			goto rddir2_exit;
827		} else if (current_entry != NULL) {
828			cFYI(1, "entry %lld found", file->f_pos);
829		} else {
830			cFYI(1, "could not find entry");
831			goto rddir2_exit;
832		}
833		cFYI(1, "loop through %d times filling dir for net buf %p",
834			num_to_fill, cifsFile->srch_inf.ntwrk_buf_start);
835		max_len = smbCalcSize((struct smb_hdr *)
836				cifsFile->srch_inf.ntwrk_buf_start);
837		end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len;
838
839		tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL);
840		if (tmp_buf == NULL) {
841			rc = -ENOMEM;
842			break;
843		}
844
845		for (i = 0; (i < num_to_fill) && (rc == 0); i++) {
846			if (current_entry == NULL) {
847				/* evaluate whether this case is an error */
848				cERROR(1, "past SMB end,  num to fill %d i %d",
849					  num_to_fill, i);
850				break;
851			}
852			/* if buggy server returns . and .. late do
853			we want to check for that here? */
854			rc = cifs_filldir(current_entry, file,
855					filldir, direntry, tmp_buf, max_len);
856			if (rc == -EOVERFLOW) {
857				rc = 0;
858				break;
859			}
860
861			file->f_pos++;
862			if (file->f_pos ==
863				cifsFile->srch_inf.index_of_last_entry) {
864				cFYI(1, "last entry in buf at pos %lld %s",
865					file->f_pos, tmp_buf);
866				cifs_save_resume_key(current_entry, cifsFile);
867				break;
868			} else
869				current_entry =
870					nxt_dir_entry(current_entry, end_of_smb,
871						cifsFile->srch_inf.info_level);
872		}
873		kfree(tmp_buf);
874		break;
875	} /* end switch */
876
877rddir2_exit:
878	FreeXid(xid);
879	return rc;
880}
881