diff options
Diffstat (limited to 'fs/smb/client/readdir.c')
| -rw-r--r-- | fs/smb/client/readdir.c | 71 |
1 files changed, 42 insertions, 29 deletions
diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c index cc6762d950d2..0b2efd680fe6 100644 --- a/fs/smb/client/readdir.c +++ b/fs/smb/client/readdir.c @@ -344,7 +344,7 @@ cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info, static int _initiate_cifs_search(const unsigned int xid, struct file *file, - const char *full_path) + const char *full_path, struct cached_fid *cfid) { __u16 search_flags; int rc = 0; @@ -374,7 +374,6 @@ _initiate_cifs_search(const unsigned int xid, struct file *file, } server = tcon->ses->server; - if (!server->ops->query_dir_first) { rc = -ENOSYS; goto error_exit; @@ -382,6 +381,13 @@ _initiate_cifs_search(const unsigned int xid, struct file *file, cifsFile->invalidHandle = true; cifsFile->srch_inf.endOfSearch = false; + if (cfid) { + cifsFile->srch_inf.restart_scan = true; + cifsFile->srch_inf.entries_in_buffer = 0; + cifsFile->srch_inf.index_of_last_entry = 2; + cifsFile->fid.persistent_fid = cfid->fid.persistent_fid; + cifsFile->fid.volatile_fid = cfid->fid.volatile_fid; + } cifs_dbg(FYI, "Full path: %s start at: %lld\n", full_path, file->f_pos); @@ -406,12 +412,16 @@ ffirst_retry: if (backup_cred(cifs_sb)) search_flags |= CIFS_SEARCH_BACKUP_SEARCH; - rc = server->ops->query_dir_first(xid, tcon, full_path, cifs_sb, - &cifsFile->fid, search_flags, - &cifsFile->srch_inf); - + if (cfid) + rc = server->ops->query_dir_next(xid, tcon, &cifsFile->fid, 0, &cifsFile->srch_inf); + else + rc = server->ops->query_dir_first(xid, tcon, full_path, cifs_sb, &cifsFile->fid, + search_flags, &cifsFile->srch_inf); if (rc == 0) { - cifsFile->invalidHandle = false; + if (cfid) + cifsFile->srch_inf.restart_scan = false; + else + cifsFile->invalidHandle = false; } else if ((rc == -EOPNOTSUPP) && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { cifs_autodisable_serverino(cifs_sb); @@ -424,12 +434,12 @@ error_exit: static int initiate_cifs_search(const unsigned int xid, struct file *file, - const char *full_path) + const char *full_path, struct cached_fid *cfid) { int rc, retry_count = 0; do { - rc = _initiate_cifs_search(xid, file, full_path); + rc = _initiate_cifs_search(xid, file, full_path, cfid); /* * If we don't have enough credits to start reading the * directory just try again after short wait. @@ -683,7 +693,7 @@ static int cifs_save_resume_key(const char *current_entry, static int find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, struct file *file, const char *full_path, - char **current_entry, int *num_to_ret) + char **current_entry, int *num_to_ret, struct cached_fid *cfid) { __u16 search_flags; int rc = 0; @@ -739,7 +749,7 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, cfile->srch_inf.srch_entries_start = NULL; cfile->srch_inf.last_entry = NULL; } - rc = initiate_cifs_search(xid, file, full_path); + rc = initiate_cifs_search(xid, file, full_path, cfid); if (rc) { cifs_dbg(FYI, "error %d reinitiating a search on rewind\n", rc); @@ -1028,7 +1038,6 @@ static int cifs_filldir(char *find_entry, struct file *file, &fattr, cfid, file); } - int cifs_readdir(struct file *file, struct dir_context *ctx) { int rc = 0; @@ -1036,7 +1045,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx) int i; struct tcon_link *tlink = NULL; struct cifs_tcon *tcon; - struct cifsFileInfo *cifsFile; + struct cifsFileInfo *cifsFile = NULL; char *current_entry; int num_to_fill = 0; char *tmp_buf = NULL; @@ -1095,23 +1104,22 @@ int cifs_readdir(struct file *file, struct dir_context *ctx) } mutex_unlock(&cfid->dirents.de_mutex); - /* Drop the cache while calling initiate_cifs_search and - * find_cifs_entry in case there will be reconnects during - * query_directory. - */ - close_cached_dir(cfid); - cfid = NULL; - - cache_not_found: + /* keep our cfid ref, but check if still valid after network calls */ +cache_not_found: /* * Ensure FindFirst doesn't fail before doing filldir() for '.' and * '..'. Otherwise we won't be able to notify VFS in case of failure. */ if (file->private_data == NULL) { - rc = initiate_cifs_search(xid, file, full_path); + rc = initiate_cifs_search(xid, file, full_path, cfid); cifs_dbg(FYI, "initiate cifs search rc %d\n", rc); if (rc) goto rddir2_exit; + + if (tcon->status != TID_GOOD || (cfid && !cfid_is_valid(cfid))) { + rc = -ENOENT; + goto rddir2_exit; + } } if (!dir_emit_dots(file, ctx)) @@ -1128,15 +1136,17 @@ int cifs_readdir(struct file *file, struct dir_context *ctx) rc = 0; goto rddir2_exit; } - } /* else { - cifsFile->invalidHandle = true; - tcon->ses->server->close(xid, tcon, &cifsFile->fid); - } */ + } tcon = tlink_tcon(cifsFile->tlink); rc = find_cifs_entry(xid, tcon, ctx->pos, file, full_path, - ¤t_entry, &num_to_fill); - open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid); + ¤t_entry, &num_to_fill, cfid); + + if (tcon->status != TID_GOOD || (cfid && !cfid_is_valid(cfid))) { + rc = -ENOENT; + goto rddir2_exit; + } + if (rc) { cifs_dbg(FYI, "fce error %d\n", rc); goto rddir2_exit; @@ -1204,8 +1214,11 @@ int cifs_readdir(struct file *file, struct dir_context *ctx) kfree(tmp_buf); rddir2_exit: - if (cfid) + if (cfid) { + if (cifsFile) + cifsFile->invalidHandle = true; close_cached_dir(cfid); + } free_dentry_path(page); free_xid(xid); return rc; |
