diff options
| author | Enzo Matsumiya <ematsumiya@suse.de> | 2025-09-08 18:10:14 -0300 |
|---|---|---|
| committer | Enzo Matsumiya <ematsumiya@suse.de> | 2025-09-12 17:27:38 -0300 |
| commit | 9e5e917c2d6dbab6ebfa4e5885740567c51c5da3 (patch) | |
| tree | 57658ff8ba87ac233c47543b3d19e3af883ddfed | |
| parent | 2ab89424bab5f5a7ae0e0e5e795a3d5b86939028 (diff) | |
| download | linux-9e5e917c2d6dbab6ebfa4e5885740567c51c5da3.tar.gz linux-9e5e917c2d6dbab6ebfa4e5885740567c51c5da3.tar.bz2 linux-9e5e917c2d6dbab6ebfa4e5885740567c51c5da3.zip | |
smb: client: actually use cached dirs on readdir
Currently, even when we have a valid cached dir, cifs_readdir will not
make use of it for the Find request, and will reopen a separate handle
in query_dir_first.
Fix this by setting cifsFile->fid to cfid->fid and resetting search info
parameters. Also add cifs_search_info->reset_scan to indicate
SMB2_query_directory_init to include the SMB2_RESTART_SCANS flag.
With this, we use query_dir_next directly instead of query_dir_first.
This patch also keeps the cfid reference all through cifs_readdir().
To prevent bogus/invalid usage of it, check if cfid is still valid after
each possible network call (i.e. where possible reconnects may have
happened).
Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
| -rw-r--r-- | fs/smb/client/cifsglob.h | 1 | ||||
| -rw-r--r-- | fs/smb/client/readdir.c | 71 | ||||
| -rw-r--r-- | fs/smb/client/smb2ops.c | 2 | ||||
| -rw-r--r-- | fs/smb/client/smb2pdu.c | 11 | ||||
| -rw-r--r-- | fs/smb/client/smb2proto.h | 2 |
5 files changed, 51 insertions, 36 deletions
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 0fae95cf81c4..9a86efddc3c4 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -1434,6 +1434,7 @@ struct cifs_search_info { bool emptyDir:1; bool unicode:1; bool smallBuf:1; /* so we know which buf_release function to call */ + bool restart_scan; }; #define ACL_NO_MODE ((umode_t)(-1)) 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; diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 39e6dc13d2da..8842315d2526 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -2397,7 +2397,7 @@ replay_again: rc = SMB2_query_directory_init(xid, tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID, - 0, srch_inf->info_level); + 0, srch_inf); if (rc) goto qdf_free; diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index f54029ee2466..9b4afe4ef26a 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -5401,7 +5401,7 @@ int SMB2_query_directory_init(const unsigned int xid, struct TCP_Server_Info *server, struct smb_rqst *rqst, u64 persistent_fid, u64 volatile_fid, - int index, int info_level) + int index, struct cifs_search_info *search) { struct smb2_query_directory_req *req; unsigned char *bufptr; @@ -5418,7 +5418,7 @@ int SMB2_query_directory_init(const unsigned int xid, if (rc) return rc; - switch (info_level) { + switch (search->info_level) { case SMB_FIND_FILE_DIRECTORY_INFO: req->FileInformationClass = FILE_DIRECTORY_INFORMATION; break; @@ -5432,14 +5432,15 @@ int SMB2_query_directory_init(const unsigned int xid, req->FileInformationClass = FILE_FULL_DIRECTORY_INFORMATION; break; default: - cifs_tcon_dbg(VFS, "info level %u isn't supported\n", - info_level); + cifs_tcon_dbg(VFS, "info level %u isn't supported\n", search->info_level); return -EINVAL; } req->FileIndex = cpu_to_le32(index); req->PersistentFileId = persistent_fid; req->VolatileFileId = volatile_fid; + if (search->restart_scan) + req->Flags |= SMB2_RESTART_SCANS; len = 0x2; bufptr = req->Buffer; @@ -5586,7 +5587,7 @@ replay_again: rc = SMB2_query_directory_init(xid, tcon, server, &rqst, persistent_fid, volatile_fid, index, - srch_inf->info_level); + srch_inf); if (rc) goto qdir_exit; diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h index b3f1398c9f79..ac6db1f18b5b 100644 --- a/fs/smb/client/smb2proto.h +++ b/fs/smb/client/smb2proto.h @@ -231,7 +231,7 @@ extern int SMB2_query_directory_init(unsigned int xid, struct cifs_tcon *tcon, struct TCP_Server_Info *server, struct smb_rqst *rqst, u64 persistent_fid, u64 volatile_fid, - int index, int info_level); + int index, struct cifs_search_info *search); extern void SMB2_query_directory_free(struct smb_rqst *rqst); extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, u32 pid, |
