linux/fs/cifs/readdir.c
<<
>>
Prefs
   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, 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
 116static void
 117cifs_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
 150static void
 151cifs_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
 166static void
 167cifs_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/*
 191int 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
 219static 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
 257ffirst_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        }
 286error_exit:
 287        kfree(full_path);
 288        cifs_put_tlink(tlink);
 289        return rc;
 290}
 291
 292/* return length of unicode string in bytes */
 293static 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
 306static 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) */
 340static 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 */
 417static 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
 429static 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) */
 499static 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 */
 609static 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
 684static 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
 763int 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
 884rddir2_exit:
 885        FreeXid(xid);
 886        return rc;
 887}
 888