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