Commit c857808d authored by Pavel Shilovsky's avatar Pavel Shilovsky Committed by Greg Kroah-Hartman

CIFS: Fix SMB2 readdir error handling

commit 52755808 upstream.

SMB2 servers indicates the end of a directory search with
STATUS_NO_MORE_FILE error code that is not processed now.
This causes generic/257 xfstest to fail. Fix this by triggering
the end of search by this error code in SMB2_query_directory.

Also when negotiating CIFS protocol we tell the server to close
the search automatically at the end and there is no need to do
it itself. In the case of SMB2 protocol, we need to close it
explicitly - separate close directory checks for different
protocols.
Signed-off-by: default avatarPavel Shilovsky <pshilovsky@samba.org>
Signed-off-by: default avatarSteve French <smfrench@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent b5cf3193
...@@ -399,6 +399,8 @@ struct smb_version_operations { ...@@ -399,6 +399,8 @@ struct smb_version_operations {
const struct cifs_fid *, u32 *); const struct cifs_fid *, u32 *);
int (*set_acl)(struct cifs_ntsd *, __u32, struct inode *, const char *, int (*set_acl)(struct cifs_ntsd *, __u32, struct inode *, const char *,
int); int);
/* check if we need to issue closedir */
bool (*dir_needs_close)(struct cifsFileInfo *);
}; };
struct smb_version_values { struct smb_version_values {
......
...@@ -762,7 +762,7 @@ int cifs_closedir(struct inode *inode, struct file *file) ...@@ -762,7 +762,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
cifs_dbg(FYI, "Freeing private data in close dir\n"); cifs_dbg(FYI, "Freeing private data in close dir\n");
spin_lock(&cifs_file_list_lock); spin_lock(&cifs_file_list_lock);
if (!cfile->srch_inf.endOfSearch && !cfile->invalidHandle) { if (server->ops->dir_needs_close(cfile)) {
cfile->invalidHandle = true; cfile->invalidHandle = true;
spin_unlock(&cifs_file_list_lock); spin_unlock(&cifs_file_list_lock);
if (server->ops->close_dir) if (server->ops->close_dir)
......
...@@ -593,7 +593,7 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, ...@@ -593,7 +593,7 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
/* close and restart search */ /* close and restart search */
cifs_dbg(FYI, "search backing up - close and restart search\n"); cifs_dbg(FYI, "search backing up - close and restart search\n");
spin_lock(&cifs_file_list_lock); spin_lock(&cifs_file_list_lock);
if (!cfile->srch_inf.endOfSearch && !cfile->invalidHandle) { if (server->ops->dir_needs_close(cfile)) {
cfile->invalidHandle = true; cfile->invalidHandle = true;
spin_unlock(&cifs_file_list_lock); spin_unlock(&cifs_file_list_lock);
if (server->ops->close_dir) if (server->ops->close_dir)
......
...@@ -1009,6 +1009,12 @@ cifs_is_read_op(__u32 oplock) ...@@ -1009,6 +1009,12 @@ cifs_is_read_op(__u32 oplock)
return oplock == OPLOCK_READ; return oplock == OPLOCK_READ;
} }
static bool
cifs_dir_needs_close(struct cifsFileInfo *cfile)
{
return !cfile->srch_inf.endOfSearch && !cfile->invalidHandle;
}
struct smb_version_operations smb1_operations = { struct smb_version_operations smb1_operations = {
.send_cancel = send_nt_cancel, .send_cancel = send_nt_cancel,
.compare_fids = cifs_compare_fids, .compare_fids = cifs_compare_fids,
...@@ -1078,6 +1084,7 @@ struct smb_version_operations smb1_operations = { ...@@ -1078,6 +1084,7 @@ struct smb_version_operations smb1_operations = {
.query_mf_symlink = cifs_query_mf_symlink, .query_mf_symlink = cifs_query_mf_symlink,
.create_mf_symlink = cifs_create_mf_symlink, .create_mf_symlink = cifs_create_mf_symlink,
.is_read_op = cifs_is_read_op, .is_read_op = cifs_is_read_op,
.dir_needs_close = cifs_dir_needs_close,
#ifdef CONFIG_CIFS_XATTR #ifdef CONFIG_CIFS_XATTR
.query_all_EAs = CIFSSMBQAllEAs, .query_all_EAs = CIFSSMBQAllEAs,
.set_EA = CIFSSMBSetEA, .set_EA = CIFSSMBSetEA,
......
...@@ -214,7 +214,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = { ...@@ -214,7 +214,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = {
{STATUS_BREAKPOINT, -EIO, "STATUS_BREAKPOINT"}, {STATUS_BREAKPOINT, -EIO, "STATUS_BREAKPOINT"},
{STATUS_SINGLE_STEP, -EIO, "STATUS_SINGLE_STEP"}, {STATUS_SINGLE_STEP, -EIO, "STATUS_SINGLE_STEP"},
{STATUS_BUFFER_OVERFLOW, -EIO, "STATUS_BUFFER_OVERFLOW"}, {STATUS_BUFFER_OVERFLOW, -EIO, "STATUS_BUFFER_OVERFLOW"},
{STATUS_NO_MORE_FILES, -EIO, "STATUS_NO_MORE_FILES"}, {STATUS_NO_MORE_FILES, -ENODATA, "STATUS_NO_MORE_FILES"},
{STATUS_WAKE_SYSTEM_DEBUGGER, -EIO, "STATUS_WAKE_SYSTEM_DEBUGGER"}, {STATUS_WAKE_SYSTEM_DEBUGGER, -EIO, "STATUS_WAKE_SYSTEM_DEBUGGER"},
{STATUS_HANDLES_CLOSED, -EIO, "STATUS_HANDLES_CLOSED"}, {STATUS_HANDLES_CLOSED, -EIO, "STATUS_HANDLES_CLOSED"},
{STATUS_NO_INHERITANCE, -EIO, "STATUS_NO_INHERITANCE"}, {STATUS_NO_INHERITANCE, -EIO, "STATUS_NO_INHERITANCE"},
......
...@@ -1104,6 +1104,12 @@ smb3_parse_lease_buf(void *buf, unsigned int *epoch) ...@@ -1104,6 +1104,12 @@ smb3_parse_lease_buf(void *buf, unsigned int *epoch)
return le32_to_cpu(lc->lcontext.LeaseState); return le32_to_cpu(lc->lcontext.LeaseState);
} }
static bool
smb2_dir_needs_close(struct cifsFileInfo *cfile)
{
return !cfile->invalidHandle;
}
struct smb_version_operations smb20_operations = { struct smb_version_operations smb20_operations = {
.compare_fids = smb2_compare_fids, .compare_fids = smb2_compare_fids,
.setup_request = smb2_setup_request, .setup_request = smb2_setup_request,
...@@ -1177,6 +1183,7 @@ struct smb_version_operations smb20_operations = { ...@@ -1177,6 +1183,7 @@ struct smb_version_operations smb20_operations = {
.create_lease_buf = smb2_create_lease_buf, .create_lease_buf = smb2_create_lease_buf,
.parse_lease_buf = smb2_parse_lease_buf, .parse_lease_buf = smb2_parse_lease_buf,
.clone_range = smb2_clone_range, .clone_range = smb2_clone_range,
.dir_needs_close = smb2_dir_needs_close,
}; };
struct smb_version_operations smb21_operations = { struct smb_version_operations smb21_operations = {
...@@ -1252,6 +1259,7 @@ struct smb_version_operations smb21_operations = { ...@@ -1252,6 +1259,7 @@ struct smb_version_operations smb21_operations = {
.create_lease_buf = smb2_create_lease_buf, .create_lease_buf = smb2_create_lease_buf,
.parse_lease_buf = smb2_parse_lease_buf, .parse_lease_buf = smb2_parse_lease_buf,
.clone_range = smb2_clone_range, .clone_range = smb2_clone_range,
.dir_needs_close = smb2_dir_needs_close,
}; };
struct smb_version_operations smb30_operations = { struct smb_version_operations smb30_operations = {
...@@ -1330,6 +1338,7 @@ struct smb_version_operations smb30_operations = { ...@@ -1330,6 +1338,7 @@ struct smb_version_operations smb30_operations = {
.parse_lease_buf = smb3_parse_lease_buf, .parse_lease_buf = smb3_parse_lease_buf,
.clone_range = smb2_clone_range, .clone_range = smb2_clone_range,
.validate_negotiate = smb3_validate_negotiate, .validate_negotiate = smb3_validate_negotiate,
.dir_needs_close = smb2_dir_needs_close,
}; };
struct smb_version_values smb20_values = { struct smb_version_values smb20_values = {
......
...@@ -2142,6 +2142,10 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2142,6 +2142,10 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
rsp = (struct smb2_query_directory_rsp *)iov[0].iov_base; rsp = (struct smb2_query_directory_rsp *)iov[0].iov_base;
if (rc) { if (rc) {
if (rc == -ENODATA && rsp->hdr.Status == STATUS_NO_MORE_FILES) {
srch_inf->endOfSearch = true;
rc = 0;
}
cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE); cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE);
goto qdir_exit; goto qdir_exit;
} }
...@@ -2179,11 +2183,6 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2179,11 +2183,6 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
else else
cifs_dbg(VFS, "illegal search buffer type\n"); cifs_dbg(VFS, "illegal search buffer type\n");
if (rsp->hdr.Status == STATUS_NO_MORE_FILES)
srch_inf->endOfSearch = 1;
else
srch_inf->endOfSearch = 0;
return rc; return rc;
qdir_exit: qdir_exit:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment