summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEnzo Matsumiya <ematsumiya@suse.de>2025-09-08 18:10:14 -0300
committerEnzo Matsumiya <ematsumiya@suse.de>2025-09-12 17:27:38 -0300
commit9e5e917c2d6dbab6ebfa4e5885740567c51c5da3 (patch)
tree57658ff8ba87ac233c47543b3d19e3af883ddfed
parent2ab89424bab5f5a7ae0e0e5e795a3d5b86939028 (diff)
downloadlinux-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.h1
-rw-r--r--fs/smb/client/readdir.c71
-rw-r--r--fs/smb/client/smb2ops.c2
-rw-r--r--fs/smb/client/smb2pdu.c11
-rw-r--r--fs/smb/client/smb2proto.h2
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,
- &current_entry, &num_to_fill);
- open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
+ &current_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,