This is a note to let you know that I've just added the patch titled CIFS: Fix SMB2 readdir error handling to the 3.10-stable tree which can be found at: http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary The filename of the patch is: cifs-fix-smb2-readdir-error-handling.patch and it can be found in the queue-3.10 subdirectory. If you, or anyone else, feels it should not be added to the stable tree, please let <stable@xxxxxxxxxxxxxxx> know about it. >From 52755808d4525f4d5b86d112d36ffc7a46f3fb48 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky <pshilovsky@xxxxxxxxx> Date: Mon, 18 Aug 2014 20:49:57 +0400 Subject: CIFS: Fix SMB2 readdir error handling From: Pavel Shilovsky <pshilovsky@xxxxxxxxx> commit 52755808d4525f4d5b86d112d36ffc7a46f3fb48 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: Pavel Shilovsky <pshilovsky@xxxxxxxxx> Signed-off-by: Steve French <smfrench@xxxxxxxxx> Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> --- fs/cifs/cifsglob.h | 2 ++ fs/cifs/file.c | 2 +- fs/cifs/readdir.c | 2 +- fs/cifs/smb1ops.c | 7 +++++++ fs/cifs/smb2maperror.c | 2 +- fs/cifs/smb2ops.c | 8 ++++++++ fs/cifs/smb2pdu.c | 9 ++++----- 7 files changed, 24 insertions(+), 8 deletions(-) --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -375,6 +375,8 @@ struct smb_version_operations { const char *, u32 *); int (*set_acl)(struct cifs_ntsd *, __u32, struct inode *, const char *, int); + /* check if we need to issue closedir */ + bool (*dir_needs_close)(struct cifsFileInfo *); }; struct smb_version_values { --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -735,7 +735,7 @@ int cifs_closedir(struct inode *inode, s cifs_dbg(FYI, "Freeing private data in close dir\n"); spin_lock(&cifs_file_list_lock); - if (!cfile->srch_inf.endOfSearch && !cfile->invalidHandle) { + if (server->ops->dir_needs_close(cfile)) { cfile->invalidHandle = true; spin_unlock(&cifs_file_list_lock); if (server->ops->close_dir) --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -582,7 +582,7 @@ find_cifs_entry(const unsigned int xid, /* close and restart search */ cifs_dbg(FYI, "search backing up - close and restart search\n"); spin_lock(&cifs_file_list_lock); - if (!cfile->srch_inf.endOfSearch && !cfile->invalidHandle) { + if (server->ops->dir_needs_close(cfile)) { cfile->invalidHandle = true; spin_unlock(&cifs_file_list_lock); if (server->ops->close_dir) --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -885,6 +885,12 @@ cifs_mand_lock(const unsigned int xid, s (__u8)type, wait, 0); } +static bool +cifs_dir_needs_close(struct cifsFileInfo *cfile) +{ + return !cfile->srch_inf.endOfSearch && !cfile->invalidHandle; +} + struct smb_version_operations smb1_operations = { .send_cancel = send_nt_cancel, .compare_fids = cifs_compare_fids, @@ -948,6 +954,7 @@ struct smb_version_operations smb1_opera .mand_lock = cifs_mand_lock, .mand_unlock_range = cifs_unlock_range, .push_mand_locks = cifs_push_mandatory_locks, + .dir_needs_close = cifs_dir_needs_close, #ifdef CONFIG_CIFS_XATTR .query_all_EAs = CIFSSMBQAllEAs, .set_EA = CIFSSMBSetEA, --- a/fs/cifs/smb2maperror.c +++ b/fs/cifs/smb2maperror.c @@ -214,7 +214,7 @@ static const struct status_to_posix_erro {STATUS_BREAKPOINT, -EIO, "STATUS_BREAKPOINT"}, {STATUS_SINGLE_STEP, -EIO, "STATUS_SINGLE_STEP"}, {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_HANDLES_CLOSED, -EIO, "STATUS_HANDLES_CLOSED"}, {STATUS_NO_INHERITANCE, -EIO, "STATUS_NO_INHERITANCE"}, --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -554,6 +554,12 @@ smb2_new_lease_key(struct cifs_fid *fid) get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE); } +static bool +smb2_dir_needs_close(struct cifsFileInfo *cfile) +{ + return !cfile->invalidHandle; +} + struct smb_version_operations smb21_operations = { .compare_fids = smb2_compare_fids, .setup_request = smb2_setup_request, @@ -618,6 +624,7 @@ struct smb_version_operations smb21_oper .set_lease_key = smb2_set_lease_key, .new_lease_key = smb2_new_lease_key, .calc_signature = smb2_calc_signature, + .dir_needs_close = smb2_dir_needs_close, }; @@ -685,6 +692,7 @@ struct smb_version_operations smb30_oper .set_lease_key = smb2_set_lease_key, .new_lease_key = smb2_new_lease_key, .calc_signature = smb3_calc_signature, + .dir_needs_close = smb2_dir_needs_close, }; struct smb_version_values smb20_values = { --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -1800,6 +1800,10 @@ SMB2_query_directory(const unsigned int rsp = (struct smb2_query_directory_rsp *)iov[0].iov_base; 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); goto qdir_exit; } @@ -1837,11 +1841,6 @@ SMB2_query_directory(const unsigned int else 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; qdir_exit: Patches currently in stable-queue which might be from pshilovsky@xxxxxxxxx are queue-3.10/cifs-fix-directory-rename-error.patch queue-3.10/cifs-fix-smb2-readdir-error-handling.patch -- To unsubscribe from this list: send the line "unsubscribe stable" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html