From d42c8a87d18b7f08325a695d00afa83cad876631 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 13 Sep 2018 15:12:34 -0500 Subject: [PATCH 01/46] smb3: do not display confusing message on mount to Azure servers Some servers (e.g. Azure) return "STATUS_NOT_IMPLEMENTED" rather than "STATUS_INVALID_DEVICE_REQUEST" on query network interface info at mount. This shouldn't cause us to log a warning message automatically. Don't log this unless noisier cifsFYI is enabled. Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/smb2maperror.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c index 20a2d304c603..d47b7f5dfa6c 100644 --- a/fs/cifs/smb2maperror.c +++ b/fs/cifs/smb2maperror.c @@ -288,7 +288,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = { {STATUS_FLT_BUFFER_TOO_SMALL, -ENOBUFS, "STATUS_FLT_BUFFER_TOO_SMALL"}, {STATUS_FVE_PARTIAL_METADATA, -EIO, "STATUS_FVE_PARTIAL_METADATA"}, {STATUS_UNSUCCESSFUL, -EIO, "STATUS_UNSUCCESSFUL"}, - {STATUS_NOT_IMPLEMENTED, -ENOSYS, "STATUS_NOT_IMPLEMENTED"}, + {STATUS_NOT_IMPLEMENTED, -EOPNOTSUPP, "STATUS_NOT_IMPLEMENTED"}, {STATUS_INVALID_INFO_CLASS, -EIO, "STATUS_INVALID_INFO_CLASS"}, {STATUS_INFO_LENGTH_MISMATCH, -EIO, "STATUS_INFO_LENGTH_MISMATCH"}, {STATUS_ACCESS_VIOLATION, -EACCES, "STATUS_ACCESS_VIOLATION"}, From d034feeb44b8a8fb473f1ba805db97787f54bf3a Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Mon, 10 Sep 2018 01:33:06 +0000 Subject: [PATCH 02/46] cifs: Use kmemdup rather than duplicating its implementation in smb311_posix_mkdir() Use kmemdup rather than duplicating its implementation Signed-off-by: YueHaibing Signed-off-by: Steve French --- fs/cifs/smb2pdu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index f54d07bda067..d7a41141ed38 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2474,13 +2474,13 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, goto ioctl_exit; } - *out_data = kmalloc(*plen, GFP_KERNEL); + *out_data = kmemdup((char *)rsp + le32_to_cpu(rsp->OutputOffset), + *plen, GFP_KERNEL); if (*out_data == NULL) { rc = -ENOMEM; goto ioctl_exit; } - memcpy(*out_data, (char *)rsp + le32_to_cpu(rsp->OutputOffset), *plen); ioctl_exit: free_rsp_buf(resp_buftype, rsp); return rc; From ce7fb50f92d78cf9cd35a520d293a4b791c8d7a1 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 6 Sep 2018 01:50:43 +0000 Subject: [PATCH 03/46] cifs: remove set but not used variable 'cifs_sb' Fixes gcc '-Wunused-but-set-variable' warning: fs/cifs/ioctl.c: In function 'cifs_ioctl': fs/cifs/ioctl.c:164:23: warning: variable 'cifs_sb' set but not used [-Wunused-but-set-variable] Signed-off-by: YueHaibing Signed-off-by: Steve French --- fs/cifs/ioctl.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 54f32f9143a9..565cf7d6f81f 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c @@ -123,7 +123,6 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) struct inode *inode = file_inode(filep); int rc = -ENOTTY; /* strange error - but the precedent */ unsigned int xid; - struct cifs_sb_info *cifs_sb; struct cifsFileInfo *pSMBFile = filep->private_data; struct cifs_tcon *tcon; __u64 ExtAttrBits = 0; @@ -131,7 +130,6 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) xid = get_xid(); - cifs_sb = CIFS_SB(inode->i_sb); cifs_dbg(FYI, "cifs ioctl 0x%x\n", command); switch (command) { case FS_IOC_GETFLAGS: From b340a4d4aa18691a6d8b0e747840b0cf30dc01cb Mon Sep 17 00:00:00 2001 From: Steve French Date: Sat, 1 Sep 2018 01:10:17 -0500 Subject: [PATCH 04/46] smb3: add tracepoint to catch cases where credit refund of failed op overlaps reconnect Add tracepoint to catch potential cases where a pending operation overlapping a reconnect could fail and incorrectly refund its credits causing the client to think it has more credits available than the server thinks it does. Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/smb2ops.c | 6 ++++++ fs/cifs/trace.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 89985a0a6819..831fa60ef886 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -74,6 +74,12 @@ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add, int *val, rc = 0; spin_lock(&server->req_lock); val = server->ops->get_credits_field(server, optype); + + /* eg found case where write overlapping reconnect messed up credits */ + if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0)) + trace_smb3_reconnect_with_invalid_credits(server->CurrentMid, + server->hostname, *val); + *val += add; if (*val > 65000) { *val = 65000; /* Don't get near 64K credits, avoid srv bugs */ diff --git a/fs/cifs/trace.h b/fs/cifs/trace.h index d4aed5217a56..43e5bd417b81 100644 --- a/fs/cifs/trace.h +++ b/fs/cifs/trace.h @@ -486,6 +486,36 @@ DEFINE_EVENT(smb3_reconnect_class, smb3_##name, \ DEFINE_SMB3_RECONNECT_EVENT(reconnect); DEFINE_SMB3_RECONNECT_EVENT(partial_send_reconnect); +DECLARE_EVENT_CLASS(smb3_credit_class, + TP_PROTO(__u64 currmid, + char *hostname, + int credits), + TP_ARGS(currmid, hostname, credits), + TP_STRUCT__entry( + __field(__u64, currmid) + __field(char *, hostname) + __field(int, credits) + ), + TP_fast_assign( + __entry->currmid = currmid; + __entry->hostname = hostname; + __entry->credits = credits; + ), + TP_printk("server=%s current_mid=0x%llx credits=%d", + __entry->hostname, + __entry->currmid, + __entry->credits) +) + +#define DEFINE_SMB3_CREDIT_EVENT(name) \ +DEFINE_EVENT(smb3_credit_class, smb3_##name, \ + TP_PROTO(__u64 currmid, \ + char *hostname, \ + int credits), \ + TP_ARGS(currmid, hostname, credits)) + +DEFINE_SMB3_CREDIT_EVENT(reconnect_with_invalid_credits); + #endif /* _CIFS_TRACE_H */ #undef TRACE_INCLUDE_PATH From cb5c2e63948451d38c977685fffc06e23beb4517 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 10 Oct 2018 15:29:06 +1000 Subject: [PATCH 05/46] cifs: fix a credits leak for compund commands When processing the mids for compounds we would only add credits based on the last successful mid in the compound which would leak credits and eventually triggering a re-connect. Fix this by splitting the mid processing part into two loops instead of one where the first loop just waits for all mids and then counts how many credits we were granted for the whole compound. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/transport.c | 61 +++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index b48f43963da6..333729cf46cd 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -786,7 +786,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, int i, j, rc = 0; int timeout, optype; struct mid_q_entry *midQ[MAX_COMPOUND]; - unsigned int credits = 1; + unsigned int credits = 0; char *buf; timeout = flags & CIFS_TIMEOUT_MASK; @@ -851,17 +851,20 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, mutex_unlock(&ses->server->srv_mutex); + if (rc < 0) + goto out; + + /* + * Compounding is never used during session establish. + */ + if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) + smb311_update_preauth_hash(ses, rqst[0].rq_iov, + rqst[0].rq_nvec); + + if (timeout == CIFS_ASYNC_OP) + goto out; + for (i = 0; i < num_rqst; i++) { - if (rc < 0) - goto out; - - if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) - smb311_update_preauth_hash(ses, rqst[i].rq_iov, - rqst[i].rq_nvec); - - if (timeout == CIFS_ASYNC_OP) - goto out; - rc = wait_for_response(ses->server, midQ[i]); if (rc != 0) { cifs_dbg(FYI, "Cancelling wait for mid %llu\n", @@ -877,10 +880,21 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, } spin_unlock(&GlobalMid_Lock); } + } + + for (i = 0; i < num_rqst; i++) + if (midQ[i]->resp_buf) + credits += ses->server->ops->get_credits(midQ[i]); + if (!credits) + credits = 1; + + for (i = 0; i < num_rqst; i++) { + if (rc < 0) + goto out; rc = cifs_sync_mid_result(midQ[i], ses->server); if (rc != 0) { - add_credits(ses->server, 1, optype); + add_credits(ses->server, credits, optype); return rc; } @@ -901,23 +915,26 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, else resp_buf_type[i] = CIFS_SMALL_BUFFER; - if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) { - struct kvec iov = { - .iov_base = resp_iov[i].iov_base, - .iov_len = resp_iov[i].iov_len - }; - smb311_update_preauth_hash(ses, &iov, 1); - } - - credits = ses->server->ops->get_credits(midQ[i]); - rc = ses->server->ops->check_receive(midQ[i], ses->server, flags & CIFS_LOG_ERROR); /* mark it so buf will not be freed by cifs_delete_mid */ if ((flags & CIFS_NO_RESP) == 0) midQ[i]->resp_buf = NULL; + } + + /* + * Compounding is never used during session establish. + */ + if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) { + struct kvec iov = { + .iov_base = resp_iov[0].iov_base, + .iov_len = resp_iov[0].iov_len + }; + smb311_update_preauth_hash(ses, &iov, 1); + } + out: /* * This will dequeue all mids. After this it is important that the From c5a5f38f075cf7656a1d7ad2b40cbc19e48e9805 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 3 Sep 2018 13:33:41 +1000 Subject: [PATCH 06/46] cifs: add a smb2_compound_op and change QUERY_INFO to use it This turns most open/query-info/close patterns in cifs.ko to become compounds. This changes stat from using 3 roundtrips to just a single one. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/smb2inode.c | 131 ++++++++++++++++++++++++++++++++++++++++---- fs/cifs/smb2ops.c | 6 +- fs/cifs/smb2pdu.c | 14 ++--- fs/cifs/smb2proto.h | 7 +++ 4 files changed, 136 insertions(+), 22 deletions(-) diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 1eef1791d0c4..9d3ada937715 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -37,6 +37,118 @@ #include "smb2pdu.h" #include "smb2proto.h" +static int +smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + __u32 desired_access, __u32 create_disposition, + __u32 create_options, void *data, int command) +{ + int rc; + __le16 *utf16_path = NULL; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct cifs_fid fid; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = ses->server; + int num_rqst = 0; + struct smb_rqst rqst[3]; + int resp_buftype[3]; + struct kvec rsp_iov[3]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct kvec qi_iov[1]; + struct kvec close_iov[1]; + struct smb2_query_info_rsp *rsp = NULL; + int flags = 0; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(rqst, 0, sizeof(rqst)); + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; + memset(rsp_iov, 0, sizeof(rsp_iov)); + + /* Open */ + utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + oparms.tcon = tcon; + oparms.desired_access = desired_access; + oparms.disposition = create_disposition; + oparms.create_options = create_options; + oparms.fid = &fid; + oparms.reconnect = false; + + memset(&open_iov, 0, sizeof(open_iov)); + rqst[num_rqst].rq_iov = open_iov; + rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE; + rc = SMB2_open_init(tcon, &rqst[num_rqst], &oplock, &oparms, + utf16_path); + kfree(utf16_path); + if (rc) + goto finished; + + smb2_set_next_command(server, &rqst[num_rqst++]); + + /* Operation */ + switch (command) { + case SMB2_OP_QUERY_INFO: + memset(&qi_iov, 0, sizeof(qi_iov)); + rqst[num_rqst].rq_iov = qi_iov; + rqst[num_rqst].rq_nvec = 1; + + rc = SMB2_query_info_init(tcon, &rqst[num_rqst], COMPOUND_FID, + COMPOUND_FID, FILE_ALL_INFORMATION, + SMB2_O_INFO_FILE, 0, + sizeof(struct smb2_file_all_info) + + PATH_MAX * 2); + smb2_set_next_command(server, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst++]); + break; + default: + cifs_dbg(VFS, "Invalid command\n"); + rc = -EINVAL; + } + if (rc) + goto finished; + + /* Close */ + memset(&close_iov, 0, sizeof(close_iov)); + rqst[num_rqst].rq_iov = close_iov; + rqst[num_rqst].rq_nvec = 1; + rc = SMB2_close_init(tcon, &rqst[num_rqst], COMPOUND_FID, + COMPOUND_FID); + smb2_set_related(&rqst[num_rqst++]); + if (rc) + goto finished; + + rc = compound_send_recv(xid, ses, flags, num_rqst, rqst, + resp_buftype, rsp_iov); + + finished: + SMB2_open_free(&rqst[0]); + switch (command) { + case SMB2_OP_QUERY_INFO: + if (rc == 0) { + rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; + rc = smb2_validate_and_copy_iov( + le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), + &rsp_iov[1], sizeof(struct smb2_file_all_info), + data); + } + if (rqst[1].rq_iov) + SMB2_query_info_free(&rqst[1]); + if (rqst[2].rq_iov) + SMB2_close_free(&rqst[2]); + break; + } + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); + return rc; +} + static int smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const char *full_path, @@ -82,11 +194,6 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, switch (command) { case SMB2_OP_DELETE: break; - case SMB2_OP_QUERY_INFO: - tmprc = SMB2_query_info(xid, tcon, fid.persistent_fid, - fid.volatile_fid, - (struct smb2_file_all_info *)data); - break; case SMB2_OP_MKDIR: /* * Directories are created through parameters in the @@ -156,16 +263,16 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, if (smb2_data == NULL) return -ENOMEM; - rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path, - FILE_READ_ATTRIBUTES, FILE_OPEN, 0, - smb2_data, SMB2_OP_QUERY_INFO); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, 0, + smb2_data, SMB2_OP_QUERY_INFO); if (rc == -EOPNOTSUPP) { *symlink = true; /* Failed on a symbolic link - query a reparse point info */ - rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path, - FILE_READ_ATTRIBUTES, FILE_OPEN, - OPEN_REPARSE_POINT, smb2_data, - SMB2_OP_QUERY_INFO); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, + OPEN_REPARSE_POINT, smb2_data, + SMB2_OP_QUERY_INFO); } if (rc) goto out; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 831fa60ef886..55ae6918be25 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -1562,7 +1562,7 @@ smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid, CIFS_CACHE_READ(cinode) ? 1 : 0); } -static void +void smb2_set_related(struct smb_rqst *rqst) { struct smb2_sync_hdr *shdr; @@ -1573,7 +1573,7 @@ smb2_set_related(struct smb_rqst *rqst) char smb2_padding[7] = {0, 0, 0, 0, 0, 0, 0}; -static void +void smb2_set_next_command(struct TCP_Server_Info *server, struct smb_rqst *rqst) { struct smb2_sync_hdr *shdr; @@ -1616,7 +1616,7 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, flags |= CIFS_TRANSFORM_REQ; memset(rqst, 0, sizeof(rqst)); - memset(resp_buftype, 0, sizeof(resp_buftype)); + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; memset(rsp_iov, 0, sizeof(rsp_iov)); memset(&open_iov, 0, sizeof(open_iov)); diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index d7a41141ed38..181ada4f2bd6 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2627,10 +2627,10 @@ smb2_validate_iov(unsigned int offset, unsigned int buffer_length, * If SMB buffer fields are valid, copy into temporary buffer to hold result. * Caller must free buffer. */ -static int -validate_and_copy_iov(unsigned int offset, unsigned int buffer_length, - struct kvec *iov, unsigned int minbufsize, - char *data) +int +smb2_validate_and_copy_iov(unsigned int offset, unsigned int buffer_length, + struct kvec *iov, unsigned int minbufsize, + char *data) { char *begin_of_buf = offset + (char *)iov->iov_base; int rc; @@ -2746,9 +2746,9 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, } } - rc = validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), - &rsp_iov, min_len, *data); + rc = smb2_validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), + &rsp_iov, min_len, *data); qinf_exit: SMB2_query_info_free(&rqst); diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index b4076577eeb7..d27e23e6f6e9 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -116,6 +116,9 @@ extern void smb2_reconnect_server(struct work_struct *work); extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server); extern unsigned long smb_rqst_len(struct TCP_Server_Info *server, struct smb_rqst *rqst); +extern void smb2_set_next_command(struct TCP_Server_Info *server, + struct smb_rqst *rqst); +extern void smb2_set_related(struct smb_rqst *rqst); /* * SMB2 Worker functions - most of protocol specific implementation details @@ -232,6 +235,10 @@ extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *, extern int smb3_encryption_required(const struct cifs_tcon *tcon); extern int smb2_validate_iov(unsigned int offset, unsigned int buffer_length, struct kvec *iov, unsigned int min_buf_size); +extern int smb2_validate_and_copy_iov(unsigned int offset, + unsigned int buffer_length, + struct kvec *iov, + unsigned int minbufsize, char *data); extern void smb2_copy_fs_info_to_kstatfs( struct smb2_fs_full_size_info *pfs_inf, struct kstatfs *kst); From f733e3936da462265a155f4dd44a333e56d197ed Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 3 Sep 2018 13:33:42 +1000 Subject: [PATCH 07/46] cifs: change mkdir to use a compound This with the previous patch changes mkdir() from needing 6 roundtrips to just 3. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/smb2inode.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 9d3ada937715..9a742164b5f1 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -105,6 +105,12 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, smb2_set_next_command(server, &rqst[num_rqst]); smb2_set_related(&rqst[num_rqst++]); break; + case SMB2_OP_MKDIR: + /* + * Directories are created through parameters in the + * SMB2_open() call. + */ + break; default: cifs_dbg(VFS, "Invalid command\n"); rc = -EINVAL; @@ -142,6 +148,10 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, if (rqst[2].rq_iov) SMB2_close_free(&rqst[2]); break; + case SMB2_OP_MKDIR: + if (rqst[1].rq_iov) + SMB2_close_free(&rqst[1]); + break; } free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); @@ -194,12 +204,6 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, switch (command) { case SMB2_OP_DELETE: break; - case SMB2_OP_MKDIR: - /* - * Directories are created through parameters in the - * SMB2_open() call. - */ - break; case SMB2_OP_RMDIR: tmprc = SMB2_rmdir(xid, tcon, fid.persistent_fid, fid.volatile_fid); @@ -287,9 +291,9 @@ int smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, struct cifs_sb_info *cifs_sb) { - return smb2_open_op_close(xid, tcon, cifs_sb, name, - FILE_WRITE_ATTRIBUTES, FILE_CREATE, - CREATE_NOT_FILE, NULL, SMB2_OP_MKDIR); + return smb2_compound_op(xid, tcon, cifs_sb, name, + FILE_WRITE_ATTRIBUTES, FILE_CREATE, + CREATE_NOT_FILE, NULL, SMB2_OP_MKDIR); } void From 47dd9597df41f0f5cc6d86cffbbccce2d9e3a788 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 3 Sep 2018 13:33:43 +1000 Subject: [PATCH 08/46] cifs: change unlink to use a compound This,and previous patches, drops the number of roundtrips from five to two for unlink() Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/smb2inode.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 9a742164b5f1..78e511c42788 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -105,6 +105,8 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, smb2_set_next_command(server, &rqst[num_rqst]); smb2_set_related(&rqst[num_rqst++]); break; + case SMB2_OP_DELETE: + break; case SMB2_OP_MKDIR: /* * Directories are created through parameters in the @@ -148,6 +150,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, if (rqst[2].rq_iov) SMB2_close_free(&rqst[2]); break; + case SMB2_OP_DELETE: case SMB2_OP_MKDIR: if (rqst[1].rq_iov) SMB2_close_free(&rqst[1]); @@ -202,8 +205,6 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, } switch (command) { - case SMB2_OP_DELETE: - break; case SMB2_OP_RMDIR: tmprc = SMB2_rmdir(xid, tcon, fid.persistent_fid, fid.volatile_fid); @@ -330,9 +331,9 @@ int smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, struct cifs_sb_info *cifs_sb) { - return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, - CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, - NULL, SMB2_OP_DELETE); + return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, + CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, + NULL, SMB2_OP_DELETE); } static int From ba8ca116854abe19893b8287d15c781e73e72405 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 3 Sep 2018 13:33:44 +1000 Subject: [PATCH 09/46] cifs: create helpers for SMB2_set_info_init/free() so that we can use these later for compounded set-info calls. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/smb2inode.c | 6 +-- fs/cifs/smb2pdu.c | 91 ++++++++++++++++++++++++++++----------------- fs/cifs/smb2proto.h | 11 ++++-- 3 files changed, 68 insertions(+), 40 deletions(-) diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 78e511c42788..a32dacd4a767 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -223,9 +223,9 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, (__le64 *)data, false); break; case SMB2_OP_SET_INFO: - tmprc = SMB2_set_info(xid, tcon, fid.persistent_fid, - fid.volatile_fid, - (FILE_BASIC_INFO *)data); + tmprc = SMB2_set_basic_info(xid, tcon, fid.persistent_fid, + fid.volatile_fid, + (FILE_BASIC_INFO *)data); break; default: cifs_dbg(VFS, "Invalid command\n"); diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 181ada4f2bd6..6c5249302574 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -3754,45 +3754,22 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, return rc; } -static int -send_set_info(const unsigned int xid, struct cifs_tcon *tcon, +int +SMB2_set_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class, - u8 info_type, u32 additional_info, unsigned int num, + u8 info_type, u32 additional_info, void **data, unsigned int *size) { - struct smb_rqst rqst; struct smb2_set_info_req *req; - struct smb2_set_info_rsp *rsp = NULL; - struct kvec *iov; - struct kvec rsp_iov; - int rc = 0; - int resp_buftype; - unsigned int i; - struct cifs_ses *ses = tcon->ses; - int flags = 0; - unsigned int total_len; - - if (!ses || !(ses->server)) - return -EIO; - - if (!num) - return -EINVAL; - - iov = kmalloc_array(num, sizeof(struct kvec), GFP_KERNEL); - if (!iov) - return -ENOMEM; + struct kvec *iov = rqst->rq_iov; + unsigned int i, total_len; + int rc; rc = smb2_plain_req_init(SMB2_SET_INFO, tcon, (void **) &req, &total_len); - if (rc) { - kfree(iov); + if (rc) return rc; - } - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; req->sync_hdr.ProcessId = cpu_to_le32(pid); - req->InfoType = info_type; req->FileInfoClass = info_class; req->PersistentFileId = persistent_fid; @@ -3810,19 +3787,65 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon, /* 1 for Buffer */ iov[0].iov_len = total_len - 1; - for (i = 1; i < num; i++) { + for (i = 1; i < rqst->rq_nvec; i++) { le32_add_cpu(&req->BufferLength, size[i]); iov[i].iov_base = (char *)data[i]; iov[i].iov_len = size[i]; } + return 0; +} + +void +SMB2_set_info_free(struct smb_rqst *rqst) +{ + cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */ +} + +static int +send_set_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class, + u8 info_type, u32 additional_info, unsigned int num, + void **data, unsigned int *size) +{ + struct smb_rqst rqst; + struct smb2_set_info_rsp *rsp = NULL; + struct kvec *iov; + struct kvec rsp_iov; + int rc = 0; + int resp_buftype; + struct cifs_ses *ses = tcon->ses; + int flags = 0; + + if (!ses || !(ses->server)) + return -EIO; + + if (!num) + return -EINVAL; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + iov = kmalloc_array(num, sizeof(struct kvec), GFP_KERNEL); + if (!iov) + return -ENOMEM; + memset(&rqst, 0, sizeof(struct smb_rqst)); rqst.rq_iov = iov; rqst.rq_nvec = num; + rc = SMB2_set_info_init(tcon, &rqst, persistent_fid, volatile_fid, pid, + info_class, info_type, additional_info, + data, size); + if (rc) { + kfree(iov); + return rc; + } + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); - cifs_buf_release(req); + SMB2_set_info_free(&rqst); rsp = (struct smb2_set_info_rsp *)rsp_iov.iov_base; if (rc != 0) { @@ -3940,8 +3963,8 @@ SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, } int -SMB2_set_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, FILE_BASIC_INFO *buf) +SMB2_set_basic_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, FILE_BASIC_INFO *buf) { unsigned int size; size = sizeof(FILE_BASIC_INFO); diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index d27e23e6f6e9..d75dd884cd2b 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -193,9 +193,14 @@ extern int SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, u32 pid, __le64 *eof, bool is_fallocate); -extern int SMB2_set_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - FILE_BASIC_INFO *buf); +extern int SMB2_set_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, u32 pid, + u8 info_class, u8 info_type, u32 additional_info, + void **data, unsigned int *size); +extern void SMB2_set_info_free(struct smb_rqst *rqst); +extern int SMB2_set_basic_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + FILE_BASIC_INFO *buf); extern int SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, struct cifs_ntsd *pnntsd, int pacllen, int aclflag); From c2e0fe3f5aae1f70a1ab402778819d2cacbaefa6 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 3 Sep 2018 13:33:45 +1000 Subject: [PATCH 10/46] cifs: make rmdir() use compounding This and previous patches drop the number of roundtrips we need for rmdir() from 6 to 2. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/smb2inode.c | 48 +++++++++++++++++++++++++++++++++------------ fs/cifs/smb2pdu.c | 16 --------------- fs/cifs/smb2proto.h | 2 -- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index a32dacd4a767..25f057c587ea 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -41,7 +41,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const char *full_path, __u32 desired_access, __u32 create_disposition, - __u32 create_options, void *data, int command) + __u32 create_options, void *ptr, int command) { int rc; __le16 *utf16_path = NULL; @@ -56,9 +56,13 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, struct kvec rsp_iov[3]; struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; struct kvec qi_iov[1]; + struct kvec si_iov[2]; /* 1 + potential padding. */ struct kvec close_iov[1]; - struct smb2_query_info_rsp *rsp = NULL; + struct smb2_query_info_rsp *qi_rsp = NULL; int flags = 0; + __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0}; + unsigned int size[1]; + void *data[1]; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; @@ -113,6 +117,21 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, * SMB2_open() call. */ break; + case SMB2_OP_RMDIR: + memset(&si_iov, 0, sizeof(si_iov)); + rqst[num_rqst].rq_iov = si_iov; + rqst[num_rqst].rq_nvec = 1; + + size[0] = 8; + data[0] = &delete_pending[0]; + + rc = SMB2_set_info_init(tcon, &rqst[num_rqst], COMPOUND_FID, + COMPOUND_FID, current->tgid, + FILE_DISPOSITION_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + smb2_set_next_command(server, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst++]); + break; default: cifs_dbg(VFS, "Invalid command\n"); rc = -EINVAL; @@ -138,12 +157,13 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, switch (command) { case SMB2_OP_QUERY_INFO: if (rc == 0) { - rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; + qi_rsp = (struct smb2_query_info_rsp *) + rsp_iov[1].iov_base; rc = smb2_validate_and_copy_iov( - le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), + le16_to_cpu(qi_rsp->OutputBufferOffset), + le32_to_cpu(qi_rsp->OutputBufferLength), &rsp_iov[1], sizeof(struct smb2_file_all_info), - data); + ptr); } if (rqst[1].rq_iov) SMB2_query_info_free(&rqst[1]); @@ -155,6 +175,12 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, if (rqst[1].rq_iov) SMB2_close_free(&rqst[1]); break; + case SMB2_OP_RMDIR: + if (rqst[1].rq_iov) + SMB2_set_info_free(&rqst[1]); + if (rqst[2].rq_iov) + SMB2_close_free(&rqst[2]); + break; } free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); @@ -205,10 +231,6 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, } switch (command) { - case SMB2_OP_RMDIR: - tmprc = SMB2_rmdir(xid, tcon, fid.persistent_fid, - fid.volatile_fid); - break; case SMB2_OP_RENAME: tmprc = SMB2_rename(xid, tcon, fid.persistent_fid, fid.volatile_fid, (__le16 *)data); @@ -322,9 +344,9 @@ int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, struct cifs_sb_info *cifs_sb) { - return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, - CREATE_NOT_FILE, - NULL, SMB2_OP_RMDIR); + return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, + CREATE_NOT_FILE, + NULL, SMB2_OP_RMDIR); } int diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 6c5249302574..8d2efca9814f 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -3891,22 +3891,6 @@ SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon, return rc; } -int -SMB2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid) -{ - __u8 delete_pending = 1; - void *data; - unsigned int size; - - data = &delete_pending; - size = 1; /* sizeof __u8 */ - - return send_set_info(xid, tcon, persistent_fid, volatile_fid, - current->tgid, FILE_DISPOSITION_INFORMATION, SMB2_O_INFO_FILE, - 0, 1, &data, &size); -} - int SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, __le16 *target_file) diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index d75dd884cd2b..9367439f1dd9 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -185,8 +185,6 @@ extern int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, __le16 *target_file); -extern int SMB2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid); extern int SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, __le16 *target_file); From f7bfe04bf0dbc422cbb3d5cffe1601518116dad6 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 3 Sep 2018 13:33:46 +1000 Subject: [PATCH 11/46] cifs: change SMB2_OP_SET_EOF to use compounding This changes SMB2_OP_SET_EOF to use compounding in some situations. This is part of the path based API to truncate a file. Most of the time this will however not be invoked for SMB2 since cifs_set_file_size() will as far as I can tell almost always just open the file synchronously and switch to the handle based truncate code path, thus bypassing the compounding we add here. Rewriting cifs_set_file_size() and make that whole pile of code more compounding friendly, and also easier to read and understand, is a different project though and not for this patch. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/smb2inode.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 25f057c587ea..e9794a2adaac 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -132,6 +132,21 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, smb2_set_next_command(server, &rqst[num_rqst]); smb2_set_related(&rqst[num_rqst++]); break; + case SMB2_OP_SET_EOF: + memset(&si_iov, 0, sizeof(si_iov)); + rqst[num_rqst].rq_iov = si_iov; + rqst[num_rqst].rq_nvec = 1; + + size[0] = 8; /* sizeof __le64 */ + data[0] = ptr; + + rc = SMB2_set_info_init(tcon, &rqst[num_rqst], COMPOUND_FID, + COMPOUND_FID, current->tgid, + FILE_END_OF_FILE_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + smb2_set_next_command(server, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst++]); + break; default: cifs_dbg(VFS, "Invalid command\n"); rc = -EINVAL; @@ -175,6 +190,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, if (rqst[1].rq_iov) SMB2_close_free(&rqst[1]); break; + case SMB2_OP_SET_EOF: case SMB2_OP_RMDIR: if (rqst[1].rq_iov) SMB2_set_info_free(&rqst[1]); @@ -239,11 +255,6 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, tmprc = SMB2_set_hardlink(xid, tcon, fid.persistent_fid, fid.volatile_fid, (__le16 *)data); break; - case SMB2_OP_SET_EOF: - tmprc = SMB2_set_eof(xid, tcon, fid.persistent_fid, - fid.volatile_fid, current->tgid, - (__le64 *)data, false); - break; case SMB2_OP_SET_INFO: tmprc = SMB2_set_basic_info(xid, tcon, fid.persistent_fid, fid.volatile_fid, @@ -403,9 +414,10 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, bool set_alloc) { __le64 eof = cpu_to_le64(size); - return smb2_open_op_close(xid, tcon, cifs_sb, full_path, - FILE_WRITE_DATA, FILE_OPEN, 0, &eof, - SMB2_OP_SET_EOF); + + return smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_WRITE_DATA, FILE_OPEN, 0, &eof, + SMB2_OP_SET_EOF); } int From dcbf91035709e9b8490bba96995998f48ce85947 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 3 Sep 2018 13:33:48 +1000 Subject: [PATCH 12/46] cifs: change SMB2_OP_SET_INFO to use compounding Cuts number of network roundtrips significantly for some common syscalls Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/smb2inode.c | 35 +++++++++++++++++++++++------------ fs/cifs/smb2pdu.c | 11 ----------- fs/cifs/smb2proto.h | 3 --- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index e9794a2adaac..007bd35dc941 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -147,6 +147,21 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, smb2_set_next_command(server, &rqst[num_rqst]); smb2_set_related(&rqst[num_rqst++]); break; + case SMB2_OP_SET_INFO: + memset(&si_iov, 0, sizeof(si_iov)); + rqst[num_rqst].rq_iov = si_iov; + rqst[num_rqst].rq_nvec = 1; + + size[0] = sizeof(FILE_BASIC_INFO); + data[0] = ptr; + + rc = SMB2_set_info_init(tcon, &rqst[num_rqst], COMPOUND_FID, + COMPOUND_FID, current->tgid, + FILE_BASIC_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + smb2_set_next_command(server, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst++]); + break; default: cifs_dbg(VFS, "Invalid command\n"); rc = -EINVAL; @@ -190,8 +205,9 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, if (rqst[1].rq_iov) SMB2_close_free(&rqst[1]); break; - case SMB2_OP_SET_EOF: case SMB2_OP_RMDIR: + case SMB2_OP_SET_EOF: + case SMB2_OP_SET_INFO: if (rqst[1].rq_iov) SMB2_set_info_free(&rqst[1]); if (rqst[2].rq_iov) @@ -255,11 +271,6 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, tmprc = SMB2_set_hardlink(xid, tcon, fid.persistent_fid, fid.volatile_fid, (__le16 *)data); break; - case SMB2_OP_SET_INFO: - tmprc = SMB2_set_basic_info(xid, tcon, fid.persistent_fid, - fid.volatile_fid, - (FILE_BASIC_INFO *)data); - break; default: cifs_dbg(VFS, "Invalid command\n"); break; @@ -344,9 +355,9 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name, cifs_i = CIFS_I(inode); dosattrs = cifs_i->cifsAttrs | ATTR_READONLY; data.Attributes = cpu_to_le32(dosattrs); - tmprc = smb2_open_op_close(xid, tcon, cifs_sb, name, - FILE_WRITE_ATTRIBUTES, FILE_CREATE, - CREATE_NOT_FILE, &data, SMB2_OP_SET_INFO); + tmprc = smb2_compound_op(xid, tcon, cifs_sb, name, + FILE_WRITE_ATTRIBUTES, FILE_CREATE, + CREATE_NOT_FILE, &data, SMB2_OP_SET_INFO); if (tmprc == 0) cifs_i->cifsAttrs = dosattrs; } @@ -437,9 +448,9 @@ smb2_set_file_info(struct inode *inode, const char *full_path, if (IS_ERR(tlink)) return PTR_ERR(tlink); - rc = smb2_open_op_close(xid, tlink_tcon(tlink), cifs_sb, full_path, - FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf, - SMB2_OP_SET_INFO); + rc = smb2_compound_op(xid, tlink_tcon(tlink), cifs_sb, full_path, + FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf, + SMB2_OP_SET_INFO); cifs_put_tlink(tlink); return rc; } diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 8d2efca9814f..29ed580d6629 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -3946,17 +3946,6 @@ SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, 0, 1, &data, &size); } -int -SMB2_set_basic_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, FILE_BASIC_INFO *buf) -{ - unsigned int size; - size = sizeof(FILE_BASIC_INFO); - return send_set_info(xid, tcon, persistent_fid, volatile_fid, - current->tgid, FILE_BASIC_INFORMATION, SMB2_O_INFO_FILE, - 0, 1, (void **)&buf, &size); -} - int SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 9367439f1dd9..5881213a7290 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -196,9 +196,6 @@ extern int SMB2_set_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, u8 info_class, u8 info_type, u32 additional_info, void **data, unsigned int *size); extern void SMB2_set_info_free(struct smb_rqst *rqst); -extern int SMB2_set_basic_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - FILE_BASIC_INFO *buf); extern int SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, struct cifs_ntsd *pnntsd, int pacllen, int aclflag); From 3764cbd179f061ddb308cd58c1e9bbf987505a18 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 3 Sep 2018 13:33:47 +1000 Subject: [PATCH 13/46] cifs: remove the is_falloc argument to SMB2_set_eof We never pass is_falloc==true here anyway and if we ever need to support is_falloc in the future, SMB2_set_eof is such a trivial wrapper around send_set_info() that we can/should just create a differently named wrapper for that new functionality. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/smb2ops.c | 2 +- fs/cifs/smb2pdu.c | 9 ++------- fs/cifs/smb2proto.h | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 55ae6918be25..11b4f397a713 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -1307,7 +1307,7 @@ smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, } return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, cfile->pid, &eof, false); + cfile->fid.volatile_fid, cfile->pid, &eof); } static int diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 29ed580d6629..3c7574513283 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -3925,7 +3925,7 @@ SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon, int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, - u64 volatile_fid, u32 pid, __le64 *eof, bool is_falloc) + u64 volatile_fid, u32 pid, __le64 *eof) { struct smb2_file_eof_info info; void *data; @@ -3936,12 +3936,7 @@ SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, data = &info; size = sizeof(struct smb2_file_eof_info); - if (is_falloc) - return send_set_info(xid, tcon, persistent_fid, volatile_fid, - pid, FILE_ALLOCATION_INFORMATION, SMB2_O_INFO_FILE, - 0, 1, &data, &size); - else - return send_set_info(xid, tcon, persistent_fid, volatile_fid, + return send_set_info(xid, tcon, persistent_fid, volatile_fid, pid, FILE_END_OF_FILE_INFORMATION, SMB2_O_INFO_FILE, 0, 1, &data, &size); } diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 5881213a7290..91c261a71059 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -190,7 +190,7 @@ extern int SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon, __le16 *target_file); extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, u32 pid, - __le64 *eof, bool is_fallocate); + __le64 *eof); extern int SMB2_set_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class, u8 info_type, u32 additional_info, From bb435512cec806a8e4e1af2700a7a7848baed0b5 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 3 Sep 2018 13:33:49 +1000 Subject: [PATCH 14/46] cifs: change SMB2_OP_RENAME and SMB2_OP_HARDLINK to use compounding Get rid of smb2_open_op_close() as all operations are now migrated to smb2_compound_op(). Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/smb2glob.h | 2 +- fs/cifs/smb2inode.c | 130 ++++++++++++++++++++------------------------ fs/cifs/smb2pdu.c | 64 ---------------------- fs/cifs/smb2proto.h | 6 -- 4 files changed, 60 insertions(+), 142 deletions(-) diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h index 0ffa18094335..dd10f0ce4cd5 100644 --- a/fs/cifs/smb2glob.h +++ b/fs/cifs/smb2glob.h @@ -33,7 +33,7 @@ /* * Identifiers for functions that use the open, operation, close pattern - * in smb2inode.c:smb2_open_op_close() + * in smb2inode.c:smb2_compound_op() */ #define SMB2_OP_SET_DELETE 1 #define SMB2_OP_SET_INFO 2 diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 007bd35dc941..cfa89494e4b0 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -56,13 +56,16 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, struct kvec rsp_iov[3]; struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; struct kvec qi_iov[1]; - struct kvec si_iov[2]; /* 1 + potential padding. */ + struct kvec si_iov[3]; /* 2 + potential padding. */ struct kvec close_iov[1]; struct smb2_query_info_rsp *qi_rsp = NULL; int flags = 0; __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0}; - unsigned int size[1]; - void *data[1]; + unsigned int size[2]; + void *data[2]; + struct smb2_file_rename_info rename_info; + struct smb2_file_link_info link_info; + int len; if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; @@ -152,6 +155,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, rqst[num_rqst].rq_iov = si_iov; rqst[num_rqst].rq_nvec = 1; + size[0] = sizeof(FILE_BASIC_INFO); data[0] = ptr; @@ -162,6 +166,54 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, smb2_set_next_command(server, &rqst[num_rqst]); smb2_set_related(&rqst[num_rqst++]); break; + case SMB2_OP_RENAME: + memset(&si_iov, 0, sizeof(si_iov)); + rqst[num_rqst].rq_iov = si_iov; + rqst[num_rqst].rq_nvec = 2; + + len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX)); + + rename_info.ReplaceIfExists = 1; + rename_info.RootDirectory = 0; + rename_info.FileNameLength = cpu_to_le32(len); + + size[0] = sizeof(struct smb2_file_rename_info); + data[0] = &rename_info; + + size[1] = len + 2 /* null */; + data[1] = (__le16 *)ptr; + + rc = SMB2_set_info_init(tcon, &rqst[num_rqst], COMPOUND_FID, + COMPOUND_FID, current->tgid, + FILE_RENAME_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + smb2_set_next_command(server, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst++]); + break; + case SMB2_OP_HARDLINK: + memset(&si_iov, 0, sizeof(si_iov)); + rqst[num_rqst].rq_iov = si_iov; + rqst[num_rqst].rq_nvec = 2; + + len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX)); + + link_info.ReplaceIfExists = 0; + link_info.RootDirectory = 0; + link_info.FileNameLength = cpu_to_le32(len); + + size[0] = sizeof(struct smb2_file_link_info); + data[0] = &link_info; + + size[1] = len + 2 /* null */; + data[1] = (__le16 *)ptr; + + rc = SMB2_set_info_init(tcon, &rqst[num_rqst], COMPOUND_FID, + COMPOUND_FID, current->tgid, + FILE_LINK_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + smb2_set_next_command(server, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst++]); + break; default: cifs_dbg(VFS, "Invalid command\n"); rc = -EINVAL; @@ -205,6 +257,8 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, if (rqst[1].rq_iov) SMB2_close_free(&rqst[1]); break; + case SMB2_OP_HARDLINK: + case SMB2_OP_RENAME: case SMB2_OP_RMDIR: case SMB2_OP_SET_EOF: case SMB2_OP_SET_INFO: @@ -220,72 +274,6 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, return rc; } -static int -smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - __u32 desired_access, __u32 create_disposition, - __u32 create_options, void *data, int command) -{ - int rc, tmprc = 0; - __le16 *utf16_path = NULL; - __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_open_parms oparms; - struct cifs_fid fid; - bool use_cached_root_handle = false; - - if ((strcmp(full_path, "") == 0) && (create_options == 0) && - (desired_access == FILE_READ_ATTRIBUTES) && - (create_disposition == FILE_OPEN) && - (tcon->nohandlecache == false)) { - rc = open_shroot(xid, tcon, &fid); - if (rc == 0) - use_cached_root_handle = true; - } - - if (use_cached_root_handle == false) { - utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - oparms.tcon = tcon; - oparms.desired_access = desired_access; - oparms.disposition = create_disposition; - oparms.create_options = create_options; - oparms.fid = &fid; - oparms.reconnect = false; - - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, - NULL); - if (rc) { - kfree(utf16_path); - return rc; - } - } - - switch (command) { - case SMB2_OP_RENAME: - tmprc = SMB2_rename(xid, tcon, fid.persistent_fid, - fid.volatile_fid, (__le16 *)data); - break; - case SMB2_OP_HARDLINK: - tmprc = SMB2_set_hardlink(xid, tcon, fid.persistent_fid, - fid.volatile_fid, (__le16 *)data); - break; - default: - cifs_dbg(VFS, "Invalid command\n"); - break; - } - - if (use_cached_root_handle) - close_shroot(&tcon->crfid); - else - rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); - if (tmprc) - rc = tmprc; - kfree(utf16_path); - return rc; -} - void move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src) { @@ -394,8 +382,8 @@ smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, goto smb2_rename_path; } - rc = smb2_open_op_close(xid, tcon, cifs_sb, from_name, access, - FILE_OPEN, 0, smb2_to_name, command); + rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, + FILE_OPEN, 0, smb2_to_name, command); smb2_rename_path: kfree(smb2_to_name); return rc; diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 3c7574513283..5f1d9994fd7a 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -3859,70 +3859,6 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon, return rc; } -int -SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, __le16 *target_file) -{ - struct smb2_file_rename_info info; - void **data; - unsigned int size[2]; - int rc; - int len = (2 * UniStrnlen((wchar_t *)target_file, PATH_MAX)); - - data = kmalloc_array(2, sizeof(void *), GFP_KERNEL); - if (!data) - return -ENOMEM; - - info.ReplaceIfExists = 1; /* 1 = replace existing target with new */ - /* 0 = fail if target already exists */ - info.RootDirectory = 0; /* MBZ for network ops (why does spec say?) */ - info.FileNameLength = cpu_to_le32(len); - - data[0] = &info; - size[0] = sizeof(struct smb2_file_rename_info); - - data[1] = target_file; - size[1] = len + 2 /* null */; - - rc = send_set_info(xid, tcon, persistent_fid, volatile_fid, - current->tgid, FILE_RENAME_INFORMATION, SMB2_O_INFO_FILE, - 0, 2, data, size); - kfree(data); - return rc; -} - -int -SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, __le16 *target_file) -{ - struct smb2_file_link_info info; - void **data; - unsigned int size[2]; - int rc; - int len = (2 * UniStrnlen((wchar_t *)target_file, PATH_MAX)); - - data = kmalloc_array(2, sizeof(void *), GFP_KERNEL); - if (!data) - return -ENOMEM; - - info.ReplaceIfExists = 0; /* 1 = replace existing link with new */ - /* 0 = fail if link already exists */ - info.RootDirectory = 0; /* MBZ for network ops (why does spec say?) */ - info.FileNameLength = cpu_to_le32(len); - - data[0] = &info; - size[0] = sizeof(struct smb2_file_link_info); - - data[1] = target_file; - size[1] = len + 2 /* null */; - - rc = send_set_info(xid, tcon, persistent_fid, volatile_fid, - current->tgid, FILE_LINK_INFORMATION, SMB2_O_INFO_FILE, - 0, 2, data, size); - kfree(data); - return rc; -} - int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, u32 pid, __le64 *eof) diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 91c261a71059..48c5c61702f1 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -182,12 +182,6 @@ extern int SMB2_echo(struct TCP_Server_Info *server); extern int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, int index, struct cifs_search_info *srch_inf); -extern int SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - __le16 *target_file); -extern int SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - __le16 *target_file); extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, u32 pid, __le64 *eof); From 14e562ada2b152cdeb49a1aec40fac402d19f3ce Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 3 Sep 2018 13:33:50 +1000 Subject: [PATCH 15/46] cifs: create a define for the max number of iov we need for a SMB2 set_info So we don't overflow the io vector arrays accidentally Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/smb2inode.c | 2 +- fs/cifs/smb2pdu.h | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index cfa89494e4b0..247cc0b32539 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -56,7 +56,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, struct kvec rsp_iov[3]; struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; struct kvec qi_iov[1]; - struct kvec si_iov[3]; /* 2 + potential padding. */ + struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; struct kvec close_iov[1]; struct smb2_query_info_rsp *qi_rsp = NULL; int flags = 0; diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 8fb7887f2b3d..600e8fe1461b 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -1174,6 +1174,15 @@ struct smb2_query_info_rsp { __u8 Buffer[1]; } __packed; +/* + * Maximum number of iovs we need for a set-info request. + * The largest one is rename/hardlink + * [0] : struct smb2_set_info_req + smb2_file_[rename|link]_info + * [1] : path + * [2] : compound padding + */ +#define SMB2_SET_INFO_IOV_SIZE 3 + struct smb2_set_info_req { struct smb2_sync_hdr sync_hdr; __le16 StructureSize; /* Must be 33 */ From 4d5bdf28690a304e089ce750efc8b7dd718945c7 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 28 Aug 2018 16:14:21 -0500 Subject: [PATCH 16/46] SMB3: Backup intent flag missing from compounded ops When "backup intent" is requested on the mount (e.g. backupuid or backupgid mount options), the corresponding flag was missing from some of the new compounding operations as well (now that open_query_close is gone). Related to kernel bugzilla #200953 Reported-and-tested-by: Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/smb2inode.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 247cc0b32539..c0b3622a7b53 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -83,6 +83,8 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, oparms.desired_access = desired_access; oparms.disposition = create_disposition; oparms.create_options = create_options; + if (backup_cred(cifs_sb)) + oparms.create_options |= CREATE_OPEN_BACKUP_INTENT; oparms.fid = &fid; oparms.reconnect = false; From 2c887635cd6ab3af619dc2be94e5bf8f2e172b78 Mon Sep 17 00:00:00 2001 From: Steve French Date: Sat, 15 Sep 2018 23:04:41 -0500 Subject: [PATCH 17/46] smb3: allow stats which track session and share reconnects to be reset Currently, "echo 0 > /proc/fs/cifs/Stats" resets all of the stats except the session and share reconnect counts. Fix it to reset those as well. CC: Stable Signed-off-by: Steve French Reviewed-by: Aurelien Aptel --- fs/cifs/cifs_debug.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index f1fbea947fef..06576797cf31 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -383,6 +383,9 @@ static ssize_t cifs_stats_proc_write(struct file *file, atomic_set(&totBufAllocCount, 0); atomic_set(&totSmBufAllocCount, 0); #endif /* CONFIG_CIFS_STATS2 */ + atomic_set(&tcpSesReconnectCount, 0); + atomic_set(&tconInfoReconnectCount, 0); + spin_lock(&GlobalMid_Lock); GlobalMaxActiveXid = 0; GlobalCurrentXid = 0; From 5a519bead49938aa0e3086bc753930d610fbd705 Mon Sep 17 00:00:00 2001 From: Steve French Date: Sat, 15 Sep 2018 14:07:09 -0500 Subject: [PATCH 18/46] cifs: protect against server returning invalid file system block size For a network file system we generally prefer large i/o, but if the server returns invalid file system block/sector sizes in cifs (vers=1.0) QFSInfo then set block size to a default of a reasonable minimum (4K). Reported-by: Dan Carpenter Signed-off-by: Steve French Reviewed-by: Aurelien Aptel --- fs/cifs/cifssmb.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 5657b79dbc99..41329f41a599 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -5027,6 +5027,13 @@ SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, le16_to_cpu(response_data->BytesPerSector) * le32_to_cpu(response_data-> SectorsPerAllocationUnit); + /* + * much prefer larger but if server doesn't report + * a valid size than 4K is a reasonable minimum + */ + if (FSData->f_bsize < 512) + FSData->f_bsize = 4096; + FSData->f_blocks = le32_to_cpu(response_data->TotalAllocationUnits); FSData->f_bfree = FSData->f_bavail = @@ -5107,6 +5114,13 @@ CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, le32_to_cpu(response_data->BytesPerSector) * le32_to_cpu(response_data-> SectorsPerAllocationUnit); + /* + * much prefer larger but if server doesn't report + * a valid size than 4K is a reasonable minimum + */ + if (FSData->f_bsize < 512) + FSData->f_bsize = 4096; + FSData->f_blocks = le64_to_cpu(response_data->TotalAllocationUnits); FSData->f_bfree = FSData->f_bavail = @@ -5470,6 +5484,13 @@ CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon, data_offset); FSData->f_bsize = le32_to_cpu(response_data->BlockSize); + /* + * much prefer larger but if server doesn't report + * a valid size than 4K is a reasonable minimum + */ + if (FSData->f_bsize < 512) + FSData->f_bsize = 4096; + FSData->f_blocks = le64_to_cpu(response_data->TotalBlocks); FSData->f_bfree = From 1c3a13a38a28c6ed84ff9220c1b8dadf5fbcf8a5 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 18 Sep 2018 04:07:45 -0500 Subject: [PATCH 19/46] cifs: minor updates to module description for cifs.ko note smb3 (and common more modern servers) in the module description Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/cifsfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 7065426b3280..af6c2e1795ef 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -1538,11 +1538,11 @@ exit_cifs(void) cifs_proc_clean(); } -MODULE_AUTHOR("Steve French "); +MODULE_AUTHOR("Steve French"); MODULE_LICENSE("GPL"); /* combination of LGPL + GPL source behaves as GPL */ MODULE_DESCRIPTION - ("VFS to access servers complying with the SNIA CIFS Specification " - "e.g. Samba and Windows"); + ("VFS to access SMB3 servers e.g. Samba, Macs, Azure and Windows (and " + "also older servers complying with the SNIA CIFS Specification)"); MODULE_VERSION(CIFS_VERSION); MODULE_SOFTDEP("pre: arc4"); MODULE_SOFTDEP("pre: des"); From 00778e2294d560361e2ed4e94cc003351a2d82d9 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 18 Sep 2018 14:05:18 -0500 Subject: [PATCH 20/46] smb3: add way to control slow response threshold for logging and stats /proc/fs/cifs/Stats when CONFIG_CIFS_STATS2 is enabled logs 'slow' responses, but depending on the server you are debugging a one second timeout may be too fast, so allow setting it to a larger number of seconds via new module parameter /sys/module/cifs/parameters/slow_rsp_threshold or via modprobe: slow_rsp_threshold:Amount of time (in seconds) to wait before logging that a response is delayed. Default: 1 (if set to 0 disables msg). (uint) Recommended values are 0 (disabled) to 32767 (9 hours) with the default remaining as 1 second. Signed-off-by: Steve French Reviewed-by: Aurelien Aptel --- fs/cifs/cifsfs.c | 13 +++++++++++++ fs/cifs/cifsglob.h | 1 + fs/cifs/transport.c | 15 ++++++++++++--- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index af6c2e1795ef..ba0054604f85 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -81,6 +81,14 @@ module_param(cifs_max_pending, uint, 0444); MODULE_PARM_DESC(cifs_max_pending, "Simultaneous requests to server for " "CIFS/SMB1 dialect (N/A for SMB3) " "Default: 32767 Range: 2 to 32767."); +#ifdef CONFIG_CIFS_STATS2 +unsigned int slow_rsp_threshold = 1; +module_param(slow_rsp_threshold, uint, 0644); +MODULE_PARM_DESC(slow_rsp_threshold, "Amount of time (in seconds) to wait " + "before logging that a response is delayed. " + "Default: 1 (if set to 0 disables msg)."); +#endif /* STATS2 */ + module_param(enable_oplocks, bool, 0644); MODULE_PARM_DESC(enable_oplocks, "Enable or disable oplocks. Default: y/Y/1"); @@ -1418,6 +1426,11 @@ init_cifs(void) #ifdef CONFIG_CIFS_STATS2 atomic_set(&totBufAllocCount, 0); atomic_set(&totSmBufAllocCount, 0); + if (slow_rsp_threshold < 1) + cifs_dbg(FYI, "slow_response_threshold msgs disabled\n"); + else if (slow_rsp_threshold > 32767) + cifs_dbg(VFS, + "slow response threshold set higher than recommended (0 to 32767)\n"); #endif /* CONFIG_CIFS_STATS2 */ atomic_set(&midCount, 0); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 9dcaed031843..d59588e4aeb4 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1715,6 +1715,7 @@ GLOBAL_EXTERN atomic_t bufAllocCount; /* current number allocated */ #ifdef CONFIG_CIFS_STATS2 GLOBAL_EXTERN atomic_t totBufAllocCount; /* total allocated over all time */ GLOBAL_EXTERN atomic_t totSmBufAllocCount; +extern unsigned int slow_rsp_threshold; /* number of secs before logging */ #endif GLOBAL_EXTERN atomic_t smBufAllocCount; GLOBAL_EXTERN atomic_t midCount; diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 333729cf46cd..a1a1199578d5 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -113,9 +113,18 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) cifs_small_buf_release(midEntry->resp_buf); #ifdef CONFIG_CIFS_STATS2 now = jiffies; - /* commands taking longer than one second are indications that - something is wrong, unless it is quite a slow link or server */ - if (time_after(now, midEntry->when_alloc + HZ) && + /* + * commands taking longer than one second (default) can be indications + * that something is wrong, unless it is quite a slow link or a very + * busy server. Note that this calc is unlikely or impossible to wrap + * as long as slow_rsp_threshold is not set way above recommended max + * value (32767 ie 9 hours) and is generally harmless even if wrong + * since only affects debug counters - so leaving the calc as simple + * comparison rather than doing multiple conversions and overflow + * checks + */ + if ((slow_rsp_threshold != 0) && + time_after(now, midEntry->when_alloc + (slow_rsp_threshold * HZ)) && (midEntry->command != command)) { /* smb2slowcmd[NUMBER_OF_SMB2_COMMANDS] counts by command */ if ((le16_to_cpu(midEntry->command) < NUMBER_OF_SMB2_COMMANDS) && From 37e6a7057607121485099e405eb528d416a5ced4 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 19 Sep 2018 02:02:58 -0500 Subject: [PATCH 21/46] smb3: minor missing defines relating to reparse points Previously reserved dpen response field changed in smb3 Signed-off-by: Steve French Reviewed-by: Aurelien Aptel --- fs/cifs/smb2pdu.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 600e8fe1461b..f753f424d7f1 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -613,6 +613,8 @@ struct smb2_tree_disconnect_rsp { #define SVHDX_OPEN_DEVICE_CONTEX 0x9CCBCF9E04C1E643980E158DA1F6EC83 #define SMB2_CREATE_TAG_POSIX 0x93AD25509CB411E7B42383DE968BCD7C +/* Flag (SMB3 open response) values */ +#define SMB2_CREATE_FLAG_REPARSEPOINT 0x01 /* * Maximum number of iovs we need for an open/create request. @@ -650,7 +652,7 @@ struct smb2_create_rsp { struct smb2_sync_hdr sync_hdr; __le16 StructureSize; /* Must be 89 */ __u8 OplockLevel; - __u8 Reserved; + __u8 Flag; /* 0x01 if reparse point */ __le32 CreateAction; __le64 CreationTime; __le64 LastAccessTime; From 9e1a37dad4f3b9f1d36d9fd27021bbd67fb8c85f Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 19 Sep 2018 02:38:17 -0500 Subject: [PATCH 22/46] smb3: track the instance of each session for debugging Each time we reconnect to the same server, bump an instance counter (and display in /proc/fs/cifs/DebugData) to make it easier to debug. Signed-off-by: Steve French Reviewed-by: Aurelien Aptel Reviewed-by: Pavel Shilovsky --- fs/cifs/cifs_debug.c | 10 ++++++---- fs/cifs/cifsglob.h | 1 + fs/cifs/connect.c | 1 + fs/cifs/smb2ops.c | 2 ++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 06576797cf31..3ae62ec74c8d 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -285,7 +285,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) if ((ses->serverDomain == NULL) || (ses->serverOS == NULL) || (ses->serverNOS == NULL)) { - seq_printf(m, "\n%d) Name: %s Uses: %d Capability: 0x%x\tSession Status: %d\t", + seq_printf(m, "\n%d) Name: %s Uses: %d Capability: 0x%x\tSession Status: %d ", i, ses->serverName, ses->ses_count, ses->capabilities, ses->status); if (ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) @@ -296,16 +296,18 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) seq_printf(m, "\n%d) Name: %s Domain: %s Uses: %d OS:" " %s\n\tNOS: %s\tCapability: 0x%x\n\tSMB" - " session status: %d\t", + " session status: %d ", i, ses->serverName, ses->serverDomain, ses->ses_count, ses->serverOS, ses->serverNOS, ses->capabilities, ses->status); } if (server->rdma) seq_printf(m, "RDMA\n\t"); - seq_printf(m, "TCP status: %d\n\tLocal Users To " + seq_printf(m, "TCP status: %d Instance: %d\n\tLocal Users To " "Server: %d SecMode: 0x%x Req On Wire: %d", - server->tcpStatus, server->srv_count, + server->tcpStatus, + server->reconnect_instance, + server->srv_count, server->sec_mode, in_flight(server)); #ifdef CONFIG_CIFS_STATS2 diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index d59588e4aeb4..b2f7cbd587f9 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -654,6 +654,7 @@ struct TCP_Server_Info { /* 16th byte of RFC1001 workstation name is always null */ char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; __u32 sequence_number; /* for signing, protected by srv_mutex */ + __u32 reconnect_instance; /* incremented on each reconnect */ struct session_key session_key; unsigned long lstrp; /* when we got last response from this server */ struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 52d71b64c0c6..106d3a85f77b 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2396,6 +2396,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); tcp_ses->session_estab = false; tcp_ses->sequence_number = 0; + tcp_ses->reconnect_instance = 0; tcp_ses->lstrp = jiffies; spin_lock_init(&tcp_ses->req_lock); INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 11b4f397a713..41342a8dc53e 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -110,6 +110,8 @@ smb2_set_credits(struct TCP_Server_Info *server, const int val) { spin_lock(&server->req_lock); server->credits = val; + if (val == 1) + server->reconnect_instance++; spin_unlock(&server->req_lock); } From 8393072bab060f0a05888ee31543175d44b5dde0 Mon Sep 17 00:00:00 2001 From: Aurelien Aptel Date: Thu, 20 Sep 2018 18:10:25 -0700 Subject: [PATCH 23/46] CIFS: make 'nodfs' mount opt a superblock flag tcon->Flags is only used by SMB1 code and changing it is not permanent (you lose the setting on tcon reconnect). * Move the setting to superblock flags (per mount-points). * Make automount callback exit early when flag present * Make dfs resolving happening in mount syscall exit early if flag present Signed-off-by: Aurelien Aptel Signed-off-by: Steve French Acked-by: Pavel Shilovsky --- fs/cifs/cifs_dfs_ref.c | 7 ++++++- fs/cifs/cifs_fs_sb.h | 1 + fs/cifs/cifsfs.c | 2 ++ fs/cifs/connect.c | 9 +++++---- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index 6b61df117fd4..b97c74efd04a 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -304,12 +304,17 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) */ mnt = ERR_PTR(-ENOMEM); + cifs_sb = CIFS_SB(mntpt->d_sb); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) { + mnt = ERR_PTR(-EREMOTE); + goto cdda_exit; + } + /* always use tree name prefix */ full_path = build_path_from_dentry_optional_prefix(mntpt, true); if (full_path == NULL) goto cdda_exit; - cifs_sb = CIFS_SB(mntpt->d_sb); tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) { mnt = ERR_CAST(tlink); diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 9731d0d891e7..63d7530f2e1d 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -51,6 +51,7 @@ */ #define CIFS_MOUNT_UID_FROM_ACL 0x2000000 /* try to get UID via special SID */ #define CIFS_MOUNT_NO_HANDLE_CACHE 0x4000000 /* disable caching dir handles */ +#define CIFS_MOUNT_NO_DFS 0x8000000 /* disable DFS resolving */ struct cifs_sb_info { struct rb_root tlink_tree; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index ba0054604f85..d2f9bc48ffac 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -500,6 +500,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root) seq_puts(s, ",unix"); else seq_puts(s, ",nounix"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) + seq_puts(s, ",nodfs"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) seq_puts(s, ",posixpaths"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 106d3a85f77b..6221aef45ff5 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3086,10 +3086,6 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) if (rc) goto out_fail; - if (volume_info->nodfs) { - tcon->Flags &= ~SMB_SHARE_IS_IN_DFS; - cifs_dbg(FYI, "DFS disabled (%d)\n", tcon->Flags); - } tcon->use_persistent = false; /* check if SMB2 or later, CIFS does not support persistent handles */ if (volume_info->persistent) { @@ -3664,6 +3660,8 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info, cifs_sb->actimeo = pvolume_info->actimeo; cifs_sb->local_nls = pvolume_info->local_nls; + if (pvolume_info->nodfs) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_DFS; if (pvolume_info->noperm) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; if (pvolume_info->setuids) @@ -3820,6 +3818,9 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses, struct dfs_info3_param *referrals = NULL; char *full_path = NULL, *ref_path = NULL, *mdata = NULL; + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) + return -EREMOTE; + full_path = build_unc_path_to_root(volume_info, cifs_sb); if (IS_ERR(full_path)) return PTR_ERR(full_path); From 6e4d3bbe92120f8482d31e4138e5fc2983caec3e Mon Sep 17 00:00:00 2001 From: Steve French Date: Sat, 22 Sep 2018 11:25:04 -0500 Subject: [PATCH 24/46] smb3: Add debug message later in smb2/smb3 reconnect path As we reset credits later in the reconnect path, useful to have optional (cifsFYI) debug message. Signed-off-by: Steve French Reviewed-by: Pavel Shilovsky --- fs/cifs/connect.c | 2 +- fs/cifs/smb2ops.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 6221aef45ff5..1605bf250691 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -347,7 +347,7 @@ cifs_reconnect(struct TCP_Server_Info *server) server->maxBuf = 0; server->max_read = 0; - cifs_dbg(FYI, "Reconnecting tcp session\n"); + cifs_dbg(FYI, "Mark tcp session as need reconnect\n"); trace_smb3_reconnect(server->CurrentMid, server->hostname); /* before reconnecting the tcp session, mark the smb session (uid) diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 41342a8dc53e..c599fc6acba6 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -113,6 +113,9 @@ smb2_set_credits(struct TCP_Server_Info *server, const int val) if (val == 1) server->reconnect_instance++; spin_unlock(&server->req_lock); + /* don't log while holding the lock */ + if (val == 1) + cifs_dbg(FYI, "set credits to 1 due to smb2 reconnect\n"); } static int * From 3d621230b8a0c6616f32b86ec3f0bc3ead9eb5b8 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 25 Sep 2018 15:33:47 -0500 Subject: [PATCH 25/46] smb3: update default requested iosize to 4MB from 1MB for recent dialects Modern servers often support 8MB as maximum i/o size, and we see some performance benefits (my testing showed 1 to 13% on write paths, and 1 to 3% on read paths for increasing the default to 4MB). If server doesn't support larger i/o size, during negotiate protocol it is already set correctly to the server's maximum if lower than 4MB. Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/cifsglob.h | 1 + fs/cifs/smb2ops.c | 58 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index b2f7cbd587f9..2a9c4ab3ec5c 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -799,6 +799,7 @@ compare_mid(__u16 mid, const struct smb_hdr *smb) * a single wsize request with a single call. */ #define CIFS_DEFAULT_IOSIZE (1024 * 1024) +#define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024) /* * Windows only supports a max of 60kb reads and 65535 byte writes. Default to diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index c599fc6acba6..b744221f685d 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -280,6 +280,31 @@ smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) return wsize; } +static unsigned int +smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) +{ + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int wsize; + + /* start with specified wsize, or default */ + wsize = volume_info->wsize ? volume_info->wsize : SMB3_DEFAULT_IOSIZE; + wsize = min_t(unsigned int, wsize, server->max_write); +#ifdef CONFIG_CIFS_SMB_DIRECT + if (server->rdma) { + if (server->sign) + wsize = min_t(unsigned int, + wsize, server->smbd_conn->max_fragmented_send_size); + else + wsize = min_t(unsigned int, + wsize, server->smbd_conn->max_readwrite_size); + } +#endif + if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) + wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); + + return wsize; +} + static unsigned int smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) { @@ -306,6 +331,31 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) return rsize; } +static unsigned int +smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) +{ + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int rsize; + + /* start with specified rsize, or default */ + rsize = volume_info->rsize ? volume_info->rsize : SMB3_DEFAULT_IOSIZE; + rsize = min_t(unsigned int, rsize, server->max_read); +#ifdef CONFIG_CIFS_SMB_DIRECT + if (server->rdma) { + if (server->sign) + rsize = min_t(unsigned int, + rsize, server->smbd_conn->max_fragmented_recv_size); + else + rsize = min_t(unsigned int, + rsize, server->smbd_conn->max_readwrite_size); + } +#endif + + if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) + rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); + + return rsize; +} static int parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, @@ -3436,8 +3486,8 @@ struct smb_version_operations smb30_operations = { .downgrade_oplock = smb2_downgrade_oplock, .need_neg = smb2_need_neg, .negotiate = smb2_negotiate, - .negotiate_wsize = smb2_negotiate_wsize, - .negotiate_rsize = smb2_negotiate_rsize, + .negotiate_wsize = smb3_negotiate_wsize, + .negotiate_rsize = smb3_negotiate_rsize, .sess_setup = SMB2_sess_setup, .logoff = SMB2_logoff, .tree_connect = SMB2_tcon, @@ -3540,8 +3590,8 @@ struct smb_version_operations smb311_operations = { .downgrade_oplock = smb2_downgrade_oplock, .need_neg = smb2_need_neg, .negotiate = smb2_negotiate, - .negotiate_wsize = smb2_negotiate_wsize, - .negotiate_rsize = smb2_negotiate_rsize, + .negotiate_wsize = smb3_negotiate_wsize, + .negotiate_rsize = smb3_negotiate_rsize, .sess_setup = SMB2_sess_setup, .logoff = SMB2_logoff, .tree_connect = SMB2_tcon, From 9b9c5bea0b960616d638711d0ecc270c3a074e7f Mon Sep 17 00:00:00 2001 From: Steve French Date: Sat, 22 Sep 2018 12:07:06 -0500 Subject: [PATCH 26/46] cifs: do not return atime less than mtime In network file system it is fairly easy for server and client atime vs. mtime to get confused (and atime updated less frequently) which we noticed broke some apps which expect atime >= mtime Also ignore relatime mount option (rather than error on it) since relatime is basically what some network server fs are doing (relatime). Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/connect.c | 1 + fs/cifs/file.c | 8 ++++++-- fs/cifs/inode.c | 6 +++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 1605bf250691..d82f0cc71755 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -250,6 +250,7 @@ static const match_table_t cifs_mount_option_tokens = { { Opt_ignore, "dev" }, { Opt_ignore, "mand" }, { Opt_ignore, "nomand" }, + { Opt_ignore, "relatime" }, { Opt_ignore, "_netdev" }, { Opt_err, NULL } diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 8d41ca7bfcf1..fa723e7a08d7 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -3889,8 +3889,12 @@ static int cifs_readpage_worker(struct file *file, struct page *page, else cifs_dbg(FYI, "Bytes read %d\n", rc); - file_inode(file)->i_atime = - current_time(file_inode(file)); + /* we do not want atime to be less than mtime, it broke some apps */ + file_inode(file)->i_atime = current_time(file_inode(file)); + if (timespec64_compare(&(file_inode(file)->i_atime), &(file_inode(file)->i_mtime))) + file_inode(file)->i_atime = file_inode(file)->i_mtime; + else + file_inode(file)->i_atime = current_time(file_inode(file)); if (PAGE_SIZE > rc) memset(read_data + rc, 0, PAGE_SIZE - rc); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 6e8765f44508..0945d40030eb 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -162,7 +162,11 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) cifs_revalidate_cache(inode, fattr); spin_lock(&inode->i_lock); - inode->i_atime = fattr->cf_atime; + /* we do not want atime to be less than mtime, it broke some apps */ + if (timespec64_compare(&fattr->cf_atime, &fattr->cf_mtime)) + inode->i_atime = fattr->cf_mtime; + else + inode->i_atime = fattr->cf_atime; inode->i_mtime = fattr->cf_mtime; inode->i_ctime = fattr->cf_ctime; inode->i_rdev = fattr->cf_rdev; From 179e44d49c2f6f4e90479f0a98d896e415742c96 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 28 Sep 2018 19:44:23 -0500 Subject: [PATCH 27/46] smb3: add tracepoint for sending lease break responses to server Be able to log a ftrace message on success and/or failure of sending a lease break response to the server. Example output: TASK-PID CPU# |||| TIMESTAMP FUNCTION | | | |||| | | kworker/1:1-5681 [001] .... 11123.530457: smb3_lease_done: sid=0x291e3e0f tid=0x8ba43071 lease_key=0x1852ca0d3ecd9b55847750a86716fde lease_state=0x0 Signed-off-by: Steve French Reviewed-by: Aurelien Aptel --- fs/cifs/smb2pdu.c | 10 +++++- fs/cifs/trace.h | 79 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 5f1d9994fd7a..3d331db65785 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -4277,6 +4277,8 @@ SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, struct kvec iov[1]; struct kvec rsp_iov; int resp_buf_type; + __u64 *please_key_high; + __u64 *please_key_low; cifs_dbg(FYI, "SMB2_lease_break\n"); rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, (void **) &req, @@ -4306,10 +4308,16 @@ SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, rc = cifs_send_recv(xid, ses, &rqst, &resp_buf_type, flags, &rsp_iov); cifs_small_buf_release(req); + please_key_low = (__u64 *)req->LeaseKey; + please_key_high = (__u64 *)(req->LeaseKey+8); if (rc) { cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); + trace_smb3_lease_err(le32_to_cpu(lease_state), tcon->tid, + ses->Suid, *please_key_low, *please_key_high, rc); cifs_dbg(FYI, "Send error in Lease Break = %d\n", rc); - } + } else + trace_smb3_lease_done(le32_to_cpu(lease_state), tcon->tid, + ses->Suid, *please_key_low, *please_key_high); return rc; } diff --git a/fs/cifs/trace.h b/fs/cifs/trace.h index 43e5bd417b81..cce8414fe7ec 100644 --- a/fs/cifs/trace.h +++ b/fs/cifs/trace.h @@ -460,6 +460,85 @@ DEFINE_EVENT(smb3_open_done_class, smb3_##name, \ DEFINE_SMB3_OPEN_DONE_EVENT(open_done); DEFINE_SMB3_OPEN_DONE_EVENT(posix_mkdir_done); + +DECLARE_EVENT_CLASS(smb3_lease_done_class, + TP_PROTO(__u32 lease_state, + __u32 tid, + __u64 sesid, + __u64 lease_key_low, + __u64 lease_key_high), + TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high), + TP_STRUCT__entry( + __field(__u32, lease_state) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u64, lease_key_low) + __field(__u64, lease_key_high) + ), + TP_fast_assign( + __entry->lease_state = lease_state; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->lease_key_low = lease_key_low; + __entry->lease_key_high = lease_key_high; + ), + TP_printk("sid=0x%llx tid=0x%x lease_key=0x%llx%llx lease_state=0x%x", + __entry->sesid, __entry->tid, __entry->lease_key_high, + __entry->lease_key_low, __entry->lease_state) +) + +#define DEFINE_SMB3_LEASE_DONE_EVENT(name) \ +DEFINE_EVENT(smb3_lease_done_class, smb3_##name, \ + TP_PROTO(__u32 lease_state, \ + __u32 tid, \ + __u64 sesid, \ + __u64 lease_key_low, \ + __u64 lease_key_high), \ + TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high)) + +DEFINE_SMB3_LEASE_DONE_EVENT(lease_done); + +DECLARE_EVENT_CLASS(smb3_lease_err_class, + TP_PROTO(__u32 lease_state, + __u32 tid, + __u64 sesid, + __u64 lease_key_low, + __u64 lease_key_high, + int rc), + TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high, rc), + TP_STRUCT__entry( + __field(__u32, lease_state) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u64, lease_key_low) + __field(__u64, lease_key_high) + __field(int, rc) + ), + TP_fast_assign( + __entry->lease_state = lease_state; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->lease_key_low = lease_key_low; + __entry->lease_key_high = lease_key_high; + __entry->rc = rc; + ), + TP_printk("sid=0x%llx tid=0x%x lease_key=0x%llx%llx lease_state=0x%x rc=%d", + __entry->sesid, __entry->tid, __entry->lease_key_high, + __entry->lease_key_low, __entry->lease_state, __entry->rc) +) + +#define DEFINE_SMB3_LEASE_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_lease_err_class, smb3_##name, \ + TP_PROTO(__u32 lease_state, \ + __u32 tid, \ + __u64 sesid, \ + __u64 lease_key_low, \ + __u64 lease_key_high, \ + int rc), \ + TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high, rc)) + +DEFINE_SMB3_LEASE_ERR_EVENT(lease_err); + DECLARE_EVENT_CLASS(smb3_reconnect_class, TP_PROTO(__u64 currmid, char *hostname), From ef2298a06d012973bbc592b86fe5ff730d4d0c63 Mon Sep 17 00:00:00 2001 From: Garry McNulty Date: Wed, 3 Oct 2018 20:51:21 +0100 Subject: [PATCH 28/46] fs/cifs: fix uninitialised variable warnings In some error conditions, resp_buftype can be passed uninitialised to free_rsp_buf(), potentially resulting in a spurious debug message. If resp_buftype randomly had the value 1 (CIFS_SMALL_BUFFER) then this would log a debug message. The rsp pointer is initialised to NULL so there is no other side-effect. Detected by CoverityScan, CID 1438585 ("Uninitialized scalar variable") Detected by CoverityScan, CID 1438667 ("Uninitialized scalar variable") Detected by CoverityScan, CID 1438764 ("Uninitialized scalar variable") Signed-off-by: Garry McNulty Signed-off-by: Steve French Reviewed-by: Aurelien Aptel --- fs/cifs/smb2pdu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 3d331db65785..dacc3400eac6 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2261,7 +2261,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, struct cifs_ses *ses = tcon->ses; struct kvec iov[SMB2_CREATE_IOV_SIZE]; struct kvec rsp_iov = {NULL, 0}; - int resp_buftype; + int resp_buftype = CIFS_NO_BUFFER; int rc = 0; int flags = 0; @@ -2547,7 +2547,7 @@ SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_ses *ses = tcon->ses; struct kvec iov[1]; struct kvec rsp_iov; - int resp_buftype; + int resp_buftype = CIFS_NO_BUFFER; int rc = 0; cifs_dbg(FYI, "Close\n"); @@ -2699,7 +2699,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, struct kvec iov[1]; struct kvec rsp_iov; int rc = 0; - int resp_buftype; + int resp_buftype = CIFS_NO_BUFFER; struct cifs_ses *ses = tcon->ses; int flags = 0; From 6d3adb23becb7b14995f6171c1d9f7f3baab1c79 Mon Sep 17 00:00:00 2001 From: Long Li Date: Thu, 20 Sep 2018 21:18:38 +0000 Subject: [PATCH 29/46] CIFS: pass page offsets on SMB1 read/write When issuing SMB1 read/write, pass the page offset to transport. Signed-off-by: Long Li Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 41329f41a599..f82fd342bca5 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1607,6 +1607,7 @@ cifs_readv_callback(struct mid_q_entry *mid) struct smb_rqst rqst = { .rq_iov = rdata->iov, .rq_nvec = 2, .rq_pages = rdata->pages, + .rq_offset = rdata->page_offset, .rq_npages = rdata->nr_pages, .rq_pagesz = rdata->pagesz, .rq_tailsz = rdata->tailsz }; @@ -2210,6 +2211,7 @@ cifs_async_writev(struct cifs_writedata *wdata, rqst.rq_iov = iov; rqst.rq_nvec = 2; rqst.rq_pages = wdata->pages; + rqst.rq_offset = wdata->page_offset; rqst.rq_npages = wdata->nr_pages; rqst.rq_pagesz = wdata->pagesz; rqst.rq_tailsz = wdata->tailsz; From ff526d86051f3689fe41e235ab962389d43999a9 Mon Sep 17 00:00:00 2001 From: Long Li Date: Thu, 20 Sep 2018 21:18:39 +0000 Subject: [PATCH 30/46] CIFS: SMBD: Do not call ib_dereg_mr on invalidated memory registration It is not necessary to deregister a memory registration after it has been successfully invalidated. Signed-off-by: Long Li Signed-off-by: Steve French --- fs/cifs/smbdirect.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/fs/cifs/smbdirect.c b/fs/cifs/smbdirect.c index 5fdb9a509a97..5e282368cc4a 100644 --- a/fs/cifs/smbdirect.c +++ b/fs/cifs/smbdirect.c @@ -2295,8 +2295,12 @@ static void smbd_mr_recovery_work(struct work_struct *work) int rc; list_for_each_entry(smbdirect_mr, &info->mr_list, list) { - if (smbdirect_mr->state == MR_INVALIDATED || - smbdirect_mr->state == MR_ERROR) { + if (smbdirect_mr->state == MR_INVALIDATED) + ib_dma_unmap_sg( + info->id->device, smbdirect_mr->sgl, + smbdirect_mr->sgl_count, + smbdirect_mr->dir); + else if (smbdirect_mr->state == MR_ERROR) { /* recover this MR entry */ rc = ib_dereg_mr(smbdirect_mr->mr); @@ -2320,25 +2324,21 @@ static void smbd_mr_recovery_work(struct work_struct *work) smbd_disconnect_rdma_connection(info); continue; } + } else + /* This MR is being used, don't recover it */ + continue; - if (smbdirect_mr->state == MR_INVALIDATED) - ib_dma_unmap_sg( - info->id->device, smbdirect_mr->sgl, - smbdirect_mr->sgl_count, - smbdirect_mr->dir); + smbdirect_mr->state = MR_READY; - smbdirect_mr->state = MR_READY; - - /* smbdirect_mr->state is updated by this function - * and is read and updated by I/O issuing CPUs trying - * to get a MR, the call to atomic_inc_return - * implicates a memory barrier and guarantees this - * value is updated before waking up any calls to - * get_mr() from the I/O issuing CPUs - */ - if (atomic_inc_return(&info->mr_ready_count) == 1) - wake_up_interruptible(&info->wait_mr); - } + /* smbdirect_mr->state is updated by this function + * and is read and updated by I/O issuing CPUs trying + * to get a MR, the call to atomic_inc_return + * implicates a memory barrier and guarantees this + * value is updated before waking up any calls to + * get_mr() from the I/O issuing CPUs + */ + if (atomic_inc_return(&info->mr_ready_count) == 1) + wake_up_interruptible(&info->wait_mr); } } From 9645759ce6b39013231f4fa312834935c93fe5bc Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 4 Oct 2018 09:24:38 +1000 Subject: [PATCH 31/46] cifs: OFD locks do not conflict with eachothers RHBZ 1484130 Update cifs_find_fid_lock_conflict() to recognize that ODF locks do not conflict with eachother. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 3 ++- fs/cifs/cifsproto.h | 2 +- fs/cifs/file.c | 35 ++++++++++++++++++++++------------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 2a9c4ab3ec5c..819ead41bbe2 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1074,7 +1074,8 @@ struct cifsLockInfo { __u64 offset; __u64 length; __u32 pid; - __u32 type; + __u16 type; + __u16 flags; }; /* diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 20adda4de83b..fa361bc00602 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -219,7 +219,7 @@ extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); extern void cifs_reopen_persistent_handles(struct cifs_tcon *tcon); extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, - __u64 length, __u8 type, + __u64 length, __u8 type, __u16 flags, struct cifsLockInfo **conf_lock, int rw_check); extern void cifs_add_pending_open(struct cifs_fid *fid, diff --git a/fs/cifs/file.c b/fs/cifs/file.c index fa723e7a08d7..00abaa1dce02 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -864,7 +864,7 @@ int cifs_closedir(struct inode *inode, struct file *file) } static struct cifsLockInfo * -cifs_lock_init(__u64 offset, __u64 length, __u8 type) +cifs_lock_init(__u64 offset, __u64 length, __u8 type, __u16 flags) { struct cifsLockInfo *lock = kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL); @@ -874,6 +874,7 @@ cifs_lock_init(__u64 offset, __u64 length, __u8 type) lock->length = length; lock->type = type; lock->pid = current->tgid; + lock->flags = flags; INIT_LIST_HEAD(&lock->blist); init_waitqueue_head(&lock->block_q); return lock; @@ -896,7 +897,8 @@ cifs_del_lock_waiters(struct cifsLockInfo *lock) /* @rw_check : 0 - no op, 1 - read, 2 - write */ static bool cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset, - __u64 length, __u8 type, struct cifsFileInfo *cfile, + __u64 length, __u8 type, __u16 flags, + struct cifsFileInfo *cfile, struct cifsLockInfo **conf_lock, int rw_check) { struct cifsLockInfo *li; @@ -918,6 +920,10 @@ cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset, ((server->ops->compare_fids(cfile, cur_cfile) && current->tgid == li->pid) || type == li->type)) continue; + if (rw_check == CIFS_LOCK_OP && + (flags & FL_OFDLCK) && (li->flags & FL_OFDLCK) && + server->ops->compare_fids(cfile, cur_cfile)) + continue; if (conf_lock) *conf_lock = li; return true; @@ -927,8 +933,8 @@ cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset, bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length, - __u8 type, struct cifsLockInfo **conf_lock, - int rw_check) + __u8 type, __u16 flags, + struct cifsLockInfo **conf_lock, int rw_check) { bool rc = false; struct cifs_fid_locks *cur; @@ -936,7 +942,8 @@ cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length, list_for_each_entry(cur, &cinode->llist, llist) { rc = cifs_find_fid_lock_conflict(cur, offset, length, type, - cfile, conf_lock, rw_check); + flags, cfile, conf_lock, + rw_check); if (rc) break; } @@ -964,7 +971,8 @@ cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length, down_read(&cinode->lock_sem); exist = cifs_find_lock_conflict(cfile, offset, length, type, - &conf_lock, CIFS_LOCK_OP); + flock->fl_flags, &conf_lock, + CIFS_LOCK_OP); if (exist) { flock->fl_start = conf_lock->offset; flock->fl_end = conf_lock->offset + conf_lock->length - 1; @@ -1011,7 +1019,8 @@ cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock, down_write(&cinode->lock_sem); exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length, - lock->type, &conf_lock, CIFS_LOCK_OP); + lock->type, lock->flags, &conf_lock, + CIFS_LOCK_OP); if (!exist && cinode->can_cache_brlcks) { list_add_tail(&lock->llist, &cfile->llist->locks); up_write(&cinode->lock_sem); @@ -1321,7 +1330,7 @@ cifs_read_flock(struct file_lock *flock, __u32 *type, int *lock, int *unlock, cifs_dbg(FYI, "Lease on file - not implemented yet\n"); if (flock->fl_flags & (~(FL_POSIX | FL_FLOCK | FL_SLEEP | - FL_ACCESS | FL_LEASE | FL_CLOSE))) + FL_ACCESS | FL_LEASE | FL_CLOSE | FL_OFDLCK))) cifs_dbg(FYI, "Unknown lock flags 0x%x\n", flock->fl_flags); *type = server->vals->large_lock_type; @@ -1584,7 +1593,8 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, if (lock) { struct cifsLockInfo *lock; - lock = cifs_lock_init(flock->fl_start, length, type); + lock = cifs_lock_init(flock->fl_start, length, type, + flock->fl_flags); if (!lock) return -ENOMEM; @@ -1653,7 +1663,6 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock) cifs_read_flock(flock, &type, &lock, &unlock, &wait_flag, tcon->ses->server); - cifs_sb = CIFS_FILE_SB(file); netfid = cfile->fid.netfid; cinode = CIFS_I(file_inode(file)); @@ -2817,8 +2826,8 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from) goto out; if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(from), - server->vals->exclusive_lock_type, NULL, - CIFS_WRITE_OP)) + server->vals->exclusive_lock_type, 0, + NULL, CIFS_WRITE_OP)) rc = __generic_file_write_iter(iocb, from); else rc = -EACCES; @@ -3388,7 +3397,7 @@ cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to) down_read(&cinode->lock_sem); if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(to), tcon->ses->server->vals->shared_lock_type, - NULL, CIFS_READ_OP)) + 0, NULL, CIFS_READ_OP)) rc = generic_file_read_iter(iocb, to); up_read(&cinode->lock_sem); return rc; From 9a0efeccfa7ea6a82585bb2def161de006396b7d Mon Sep 17 00:00:00 2001 From: Rodrigo Freire Date: Sun, 7 Oct 2018 12:21:25 -0300 Subject: [PATCH 32/46] CIFS: Adds information-level logging function Currently, CIFS lacks a internal logging function that prints out data when CIFS_DEBUG=n. When CIFS_DEBUG=y, the only message level for CIFS events are KERN_ERR or KERN_DEBUG. This patch creates cifs_info(), which is useful for printing non-critical event messges, at either CIFS_DEBUG state. Signed-off-by: Rodrigo Freire Signed-off-by: Steve French --- fs/cifs/cifs_debug.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h index f4f3f0853c6e..72db2987e066 100644 --- a/fs/cifs/cifs_debug.h +++ b/fs/cifs/cifs_debug.h @@ -47,6 +47,17 @@ extern int cifsFYI; */ #ifdef CONFIG_CIFS_DEBUG +/* Information level messages, minor events */ +#define cifs_info_func(ratefunc, fmt, ...) \ +do { \ + pr_info_ ## ratefunc("CIFS: " fmt, ##__VA_ARGS__); \ +} while (0) + +#define cifs_info(fmt, ...) \ +do { \ + cifs_info_func(ratelimited, fmt, ##__VA_ARGS__); \ +} while (0) + /* information message: e.g., configuration, major event */ #define cifs_dbg_func(ratefunc, type, fmt, ...) \ do { \ @@ -81,6 +92,11 @@ do { \ if (0) \ pr_debug(fmt, ##__VA_ARGS__); \ } while (0) + +#define cifs_info(fmt, ...) \ +do { \ + pr_info("CIFS: "fmt, ##__VA_ARGS__); \ +} while (0) #endif #endif /* _H_CIFS_DEBUG */ From f80eaedd6c5978fdd31549446cfa4a7212522b8d Mon Sep 17 00:00:00 2001 From: Rodrigo Freire Date: Sun, 7 Oct 2018 12:21:26 -0300 Subject: [PATCH 33/46] CIFS: Print message when attempting a mount Currently, no messages are printed when mounting a CIFS filesystem and no debug configuration is enabled. However, a CIFS mount information is valuable when troubleshooting and/or forensic analyzing a system and finding out if was a CIFS endpoint mount attempted. Other filesystems such as XFS, EXT* does issue a printk() when mounting their filesystems. A terse log message is printed only if cifsFYI is not enabled. Otherwise, the default full debug message is printed. In order to not clutter and classify correctly the event messages, these are logged as KERN_INFO level. Sample mount operations: [root@corinthians ~]# mount -o user=administrator //172.25.250.18/c$ /mnt (non-existent system) [root@corinthians ~]# mount -o user=administrator //172.25.250.19/c$ /mnt (Valid system) Kernel message log for the mount operations: [ 450.464543] CIFS: Attempting to mount //172.25.250.18/c$ [ 456.478186] CIFS VFS: Error connecting to socket. Aborting operation. [ 456.478381] CIFS VFS: cifs_mount failed w/return code = -113 [ 467.688866] CIFS: Attempting to mount //172.25.250.19/c$ Signed-off-by: Rodrigo Freire Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 7 ++++++- fs/cifs/transport.c | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index d2f9bc48ffac..d78a31560995 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -717,7 +717,12 @@ cifs_smb3_do_mount(struct file_system_type *fs_type, struct cifs_mnt_data mnt_data; struct dentry *root; - cifs_dbg(FYI, "Devname: %s flags: %d\n", dev_name, flags); + /* Prints in Kernel / CIFS log the attempted mount operation * + * IF CIFS_DEBUG && cifs_FYI */ + if (cifsFYI) + cifs_dbg(FYI, "Devname: %s flags: %d\n", dev_name, flags); + else + cifs_info("Attempting to mount %s\n", dev_name); volume_info = cifs_get_volume_info((char *)data, dev_name, is_smb3); if (IS_ERR(volume_info)) diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index a1a1199578d5..fd80749c7838 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -137,7 +137,7 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) if (cifsFYI & CIFS_TIMER) { pr_debug(" CIFS slow rsp: cmd %d mid %llu", midEntry->command, midEntry->mid); - pr_info(" A: 0x%lx S: 0x%lx R: 0x%lx\n", + cifs_info(" A: 0x%lx S: 0x%lx R: 0x%lx\n", now - midEntry->when_alloc, now - midEntry->when_sent, now - midEntry->when_received); From 8c1beb9801293b175cfa0341e5df89581a87dc02 Mon Sep 17 00:00:00 2001 From: Steve French Date: Sun, 7 Oct 2018 13:52:18 -0500 Subject: [PATCH 34/46] cifs: minor clarification in comments Clarify meaning (in comments) meaning of various options for debug messages in cifs.ko. Also fixed trivial formatting/style issue with previous patch. Signed-off-by: Steve French --- fs/cifs/cifs_debug.h | 12 ++++++++++++ fs/cifs/cifsfs.c | 6 ++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h index 72db2987e066..631dc1bb21c1 100644 --- a/fs/cifs/cifs_debug.h +++ b/fs/cifs/cifs_debug.h @@ -47,6 +47,18 @@ extern int cifsFYI; */ #ifdef CONFIG_CIFS_DEBUG + +/* + * When adding tracepoints and debug messages we have various choices. + * Some considerations: + * + * Use cifs_dbg(VFS, ...) for things we always want logged, and the user to see + * cifs_info(...) slightly less important, admin can filter via loglevel > 6 + * cifs_dbg(FYI, ...) minor debugging messages, off by default + * trace_smb3_* ftrace functions are preferred for complex debug messages + * intended for developers or experienced admins, off by default + */ + /* Information level messages, minor events */ #define cifs_info_func(ratefunc, fmt, ...) \ do { \ diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index d78a31560995..7de9603c54f1 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -717,8 +717,10 @@ cifs_smb3_do_mount(struct file_system_type *fs_type, struct cifs_mnt_data mnt_data; struct dentry *root; - /* Prints in Kernel / CIFS log the attempted mount operation * - * IF CIFS_DEBUG && cifs_FYI */ + /* + * Prints in Kernel / CIFS log the attempted mount operation + * If CIFS_DEBUG && cifs_FYI + */ if (cifsFYI) cifs_dbg(FYI, "Devname: %s flags: %d\n", dev_name, flags); else From f5b05d622a3e99e6a97a189fe500414be802a05c Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sun, 7 Oct 2018 19:19:58 -0500 Subject: [PATCH 35/46] cifs: add IOCTL for QUERY_INFO passthrough to userspace This allows userspace tools to query the raw info levels for cifs files and process the response in userspace. In particular this is useful for many of those data where there is no corresponding native data structure in linux. For example querying the security descriptor for a file and extract the SIDs. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/cifs_ioctl.h | 11 ++++++ fs/cifs/cifsglob.h | 4 ++ fs/cifs/ioctl.c | 21 +++++++++++ fs/cifs/smb2inode.c | 2 +- fs/cifs/smb2ops.c | 87 +++++++++++++++++++++++++++++++++++++++++++- fs/cifs/smb2pdu.c | 17 +++++---- fs/cifs/smb2proto.h | 3 +- 7 files changed, 134 insertions(+), 11 deletions(-) diff --git a/fs/cifs/cifs_ioctl.h b/fs/cifs/cifs_ioctl.h index 57ff0756e30c..d8bce2f862de 100644 --- a/fs/cifs/cifs_ioctl.h +++ b/fs/cifs/cifs_ioctl.h @@ -43,8 +43,19 @@ struct smb_snapshot_array { /* snapshots[]; */ } __packed; +struct smb_query_info { + __u32 info_type; + __u32 file_info_class; + __u32 additional_information; + __u32 flags; + __u32 input_buffer_length; + __u32 output_buffer_length; + /* char buffer[]; */ +} __packed; + #define CIFS_IOCTL_MAGIC 0xCF #define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int) #define CIFS_IOC_SET_INTEGRITY _IO(CIFS_IOCTL_MAGIC, 4) #define CIFS_IOC_GET_MNT_INFO _IOR(CIFS_IOCTL_MAGIC, 5, struct smb_mnt_fs_info) #define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array) +#define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 819ead41bbe2..73801254cc21 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -465,6 +465,10 @@ struct smb_version_operations { enum securityEnum (*select_sectype)(struct TCP_Server_Info *, enum securityEnum); int (*next_header)(char *); + /* ioctl passthrough for query_info */ + int (*ioctl_query_info)(const unsigned int xid, + struct cifsFileInfo *file, + unsigned long p); }; struct smb_version_values { diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 565cf7d6f81f..77c7a5796dfd 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c @@ -34,6 +34,24 @@ #include "cifs_ioctl.h" #include +static long cifs_ioctl_query_info(unsigned int xid, struct file *filep, + unsigned long p) +{ + struct cifsFileInfo *pSMBFile = filep->private_data; + struct cifs_tcon *tcon; + + cifs_dbg(FYI, "%s %p\n", __func__, pSMBFile); + if (pSMBFile == NULL) + return -EISDIR; + tcon = tlink_tcon(pSMBFile->tlink); + + if (tcon->ses->server->ops->ioctl_query_info) + return tcon->ses->server->ops->ioctl_query_info( + xid, pSMBFile, p); + else + return -EOPNOTSUPP; +} + static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file, unsigned long srcfd) { @@ -194,6 +212,9 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) case CIFS_IOC_COPYCHUNK_FILE: rc = cifs_ioctl_copychunk(xid, filep, arg); break; + case CIFS_QUERY_INFO: + rc = cifs_ioctl_query_info(xid, filep, arg); + break; case CIFS_IOC_SET_INTEGRITY: if (pSMBFile == NULL) break; diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index c0b3622a7b53..50d4a9afb36a 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -110,7 +110,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, COMPOUND_FID, FILE_ALL_INFORMATION, SMB2_O_INFO_FILE, 0, sizeof(struct smb2_file_all_info) + - PATH_MAX * 2); + PATH_MAX * 2, 0, NULL); smb2_set_next_command(server, &rqst[num_rqst]); smb2_set_related(&rqst[num_rqst++]); break; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index b744221f685d..c6c6450d0f38 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -1118,6 +1118,86 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon, return rc; } +static int +smb2_ioctl_query_info(const unsigned int xid, + struct cifsFileInfo *file, + unsigned long p) +{ + struct cifs_tcon *tcon = tlink_tcon(file->tlink); + struct cifs_ses *ses = tcon->ses; + char __user *arg = (char __user *)p; + struct smb_query_info qi; + struct smb_query_info __user *pqi; + int rc = 0; + int flags = 0; + struct smb_rqst rqst; + struct kvec iov[1]; + struct kvec rsp_iov; + int resp_buftype; + struct smb2_query_info_rsp *rsp = NULL; + void *buffer; + + if (copy_from_user(&qi, arg, sizeof(struct smb_query_info))) + return -EFAULT; + + if (qi.output_buffer_length > 1024) + return -EINVAL; + + if (!ses || !(ses->server)) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + buffer = kmalloc(qi.output_buffer_length, GFP_KERNEL); + if (buffer == NULL) + return -ENOMEM; + + if (copy_from_user(buffer, arg + sizeof(struct smb_query_info), + qi.output_buffer_length)) { + kfree(buffer); + return -EFAULT; + } + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = SMB2_query_info_init(tcon, &rqst, file->fid.persistent_fid, + file->fid.volatile_fid, + qi.file_info_class, qi.info_type, + qi.additional_information, + qi.input_buffer_length, + qi.output_buffer_length, buffer); + kfree(buffer); + if (rc) + goto iqinf_exit; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); + rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; + if (rc) + goto iqinf_exit; + + pqi = (struct smb_query_info __user *)arg; + if (le32_to_cpu(rsp->OutputBufferLength) < qi.input_buffer_length) + qi.input_buffer_length = le32_to_cpu(rsp->OutputBufferLength); + if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length, + sizeof(qi.input_buffer_length))) { + rc = -EFAULT; + goto iqinf_exit; + } + if (copy_to_user(pqi + 1, rsp->Buffer, qi.input_buffer_length)) { + rc = -EFAULT; + goto iqinf_exit; + } + + iqinf_exit: + SMB2_query_info_free(&rqst); + free_rsp_buf(resp_buftype, rsp); + return rc; +} + static ssize_t smb2_copychunk_range(const unsigned int xid, struct cifsFileInfo *srcfile, @@ -1697,7 +1777,8 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, rc = SMB2_query_info_init(tcon, &rqst[1], COMPOUND_FID, COMPOUND_FID, FS_FULL_SIZE_INFORMATION, SMB2_O_INFO_FILESYSTEM, 0, - sizeof(struct smb2_fs_full_size_info)); + sizeof(struct smb2_fs_full_size_info), 0, + NULL); if (rc) goto qfs_exit; smb2_set_next_command(server, &rqst[1]); @@ -3364,6 +3445,7 @@ struct smb_version_operations smb20_operations = { .set_acl = set_smb2_acl, #endif /* CIFS_ACL */ .next_header = smb2_next_header, + .ioctl_query_info = smb2_ioctl_query_info, }; struct smb_version_operations smb21_operations = { @@ -3459,6 +3541,7 @@ struct smb_version_operations smb21_operations = { .set_acl = set_smb2_acl, #endif /* CIFS_ACL */ .next_header = smb2_next_header, + .ioctl_query_info = smb2_ioctl_query_info, }; struct smb_version_operations smb30_operations = { @@ -3563,6 +3646,7 @@ struct smb_version_operations smb30_operations = { .set_acl = set_smb2_acl, #endif /* CIFS_ACL */ .next_header = smb2_next_header, + .ioctl_query_info = smb2_ioctl_query_info, }; struct smb_version_operations smb311_operations = { @@ -3668,6 +3752,7 @@ struct smb_version_operations smb311_operations = { .set_acl = set_smb2_acl, #endif /* CIFS_ACL */ .next_header = smb2_next_header, + .ioctl_query_info = smb2_ioctl_query_info, }; struct smb_version_values smb20_values = { diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index dacc3400eac6..1310bb355e32 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2651,7 +2651,7 @@ int SMB2_query_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type, u32 additional_info, - size_t output_len) + size_t output_len, size_t input_len, void *input) { struct smb2_query_info_req *req; struct kvec *iov = rqst->rq_iov; @@ -2669,16 +2669,17 @@ SMB2_query_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, req->VolatileFileId = volatile_fid; req->AdditionalInformation = cpu_to_le32(additional_info); - /* - * We do not use the input buffer (do not send extra byte) - */ - req->InputBufferOffset = 0; - req->OutputBufferLength = cpu_to_le32(output_len); + if (input_len) { + req->InputBufferLength = cpu_to_le32(input_len); + /* total_len for smb query request never close to le16 max */ + req->InputBufferOffset = cpu_to_le16(total_len - 1); + memcpy(req->Buffer, input, input_len); + } iov[0].iov_base = (char *)req; /* 1 for Buffer */ - iov[0].iov_len = total_len - 1; + iov[0].iov_len = total_len - 1 + input_len; return 0; } @@ -2718,7 +2719,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, rc = SMB2_query_info_init(tcon, &rqst, persistent_fid, volatile_fid, info_class, info_type, additional_info, - output_len); + output_len, 0, NULL); if (rc) goto qinf_exit; diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 48c5c61702f1..9f4e9ed9ce53 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -163,7 +163,8 @@ extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_query_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type, - u32 additional_info, size_t output_len); + u32 additional_info, size_t output_len, + size_t input_len, void *input); extern void SMB2_query_info_free(struct smb_rqst *rqst); extern int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_file_id, u64 volatile_file_id, From f70556c8cae976e50ba9c27dd4c1a86b57efc17d Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 10 Oct 2018 00:02:20 +0200 Subject: [PATCH 36/46] smb2: fix uninitialized variable bug in smb2_ioctl_query_info There is a potential execution path in which variable *resp_buftype* is passed as an argument to function free_rsp_buf(), in which it is used in a comparison without being properly initialized previously. Fix this by initializing variable *resp_buftype* to CIFS_NO_BUFFER in order to avoid unpredictable or unintended results. Addresses-Coverity-ID: 1473971 ("Uninitialized scalar variable") Fixes: c5d25bdb2967 ("cifs: add IOCTL for QUERY_INFO passthrough to userspace") Signed-off-by: Gustavo A. R. Silva Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/smb2ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index c6c6450d0f38..8472cb0ae06c 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -1133,7 +1133,7 @@ smb2_ioctl_query_info(const unsigned int xid, struct smb_rqst rqst; struct kvec iov[1]; struct kvec rsp_iov; - int resp_buftype; + int resp_buftype = CIFS_NO_BUFFER; struct smb2_query_info_rsp *rsp = NULL; void *buffer; From 0cb012d1a0a9ead759112da141698a2078fbf2ce Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 11 Oct 2018 01:01:02 -0500 Subject: [PATCH 37/46] cifs: track writepages in vfs operation counters writepages and readpages operations did not call get/free_xid so the statistics for file copy could get confusing with "vfs operations" not increasing. Add get_xid and free_xid to cifs readpages and writepages functions. Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/file.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 00abaa1dce02..84477e995246 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2107,6 +2107,7 @@ static int cifs_writepages(struct address_space *mapping, pgoff_t end, index; struct cifs_writedata *wdata; int rc = 0; + unsigned int xid; /* * If wsize is smaller than the page cache size, default to writing @@ -2115,6 +2116,7 @@ static int cifs_writepages(struct address_space *mapping, if (cifs_sb->wsize < PAGE_SIZE) return generic_writepages(mapping, wbc); + xid = get_xid(); if (wbc->range_cyclic) { index = mapping->writeback_index; /* Start from prev offset */ end = -1; @@ -2208,6 +2210,7 @@ static int cifs_writepages(struct address_space *mapping, if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) mapping->writeback_index = index; + free_xid(xid); return rc; } @@ -3752,7 +3755,9 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); struct TCP_Server_Info *server; pid_t pid; + unsigned int xid; + xid = get_xid(); /* * Reads as many pages as possible from fscache. Returns -ENOBUFS * immediately if the cookie is negative @@ -3762,8 +3767,10 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, */ rc = cifs_readpages_from_fscache(mapping->host, mapping, page_list, &num_pages); - if (rc == 0) + if (rc == 0) { + free_xid(xid); return rc; + } if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) pid = open_file->pid; @@ -3807,6 +3814,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, */ if (unlikely(rsize < PAGE_SIZE)) { add_credits_and_wake_if(server, credits, 0); + free_xid(xid); return 0; } @@ -3871,6 +3879,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, * allocator. */ cifs_fscache_readpages_cancel(mapping->host, page_list); + free_xid(xid); return rc; } From 61351d6d54e651ec4098445afa5ddc2092c4741a Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 19 Oct 2018 00:32:41 -0500 Subject: [PATCH 38/46] smb3: send backup intent on compounded query info When mounting with backupuid set, we should be setting CREATE_OPEN_BACKUP_INTENT flag on compounded opens as well, especially the case of compounded smb2_query_path_info. Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/smb2inode.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 50d4a9afb36a..9e7ef7ec2d70 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -293,6 +293,7 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, { int rc; struct smb2_file_all_info *smb2_data; + __u32 create_options = 0; *adjust_tz = false; *symlink = false; @@ -301,16 +302,20 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, GFP_KERNEL); if (smb2_data == NULL) return -ENOMEM; + if (backup_cred(cifs_sb)) + create_options |= CREATE_OPEN_BACKUP_INTENT; rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, - FILE_READ_ATTRIBUTES, FILE_OPEN, 0, + FILE_READ_ATTRIBUTES, FILE_OPEN, create_options, smb2_data, SMB2_OP_QUERY_INFO); if (rc == -EOPNOTSUPP) { *symlink = true; + create_options |= OPEN_REPARSE_POINT; + /* Failed on a symbolic link - query a reparse point info */ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, - OPEN_REPARSE_POINT, smb2_data, + create_options, smb2_data, SMB2_OP_QUERY_INFO); } if (rc) From 1e77a8c204c9d1b655c61751b8ad0fde22421dbb Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 19 Oct 2018 00:45:21 -0500 Subject: [PATCH 39/46] smb3: do not attempt cifs operation in smb3 query info error path If backupuid mount option is sent, we can incorrectly retry (on access denied on query info) with a cifs (FindFirst) operation on an smb3 mount which causes the server to force the session close. We set backup intent on open so no need for this fallback. See kernel bugzilla 201435 Signed-off-by: Steve French CC: Stable Reviewed-by: Ronnie Sahlberg --- fs/cifs/inode.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 0945d40030eb..7483c09317b9 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -781,7 +781,15 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, } else if (rc == -EREMOTE) { cifs_create_dfs_fattr(&fattr, sb); rc = 0; - } else if (rc == -EACCES && backup_cred(cifs_sb)) { + } else if ((rc == -EACCES) && backup_cred(cifs_sb) && + (strcmp(server->vals->version_string, SMB1_VERSION_STRING) + == 0)) { + /* + * For SMB2 and later the backup intent flag is already + * sent if needed on open and there is no path based + * FindFirst operation to use to retry with + */ + srchinf = kzalloc(sizeof(struct cifs_search_info), GFP_KERNEL); if (srchinf == NULL) { From 3b7960caceafdfc2cdfe2850487f8d091eb41144 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 19 Oct 2018 01:58:22 -0500 Subject: [PATCH 40/46] cifs: fallback to older infolevels on findfirst queryinfo retry In cases where queryinfo fails, we have cases in cifs (vers=1.0) where with backupuid mounts we retry the query info with findfirst. This doesn't work to some NetApp servers which don't support WindowsXP (and later) infolevel 261 (SMB_FIND_FILE_ID_FULL_DIR_INFO) so in this case use other info levels (in this case it will usually be level 257, SMB_FIND_FILE_DIRECTORY_INFO). (Also fixes some indentation) See kernel bugzilla 201435 Signed-off-by: Steve French --- fs/cifs/inode.c | 67 +++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 7483c09317b9..1023d78673fb 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -784,43 +784,50 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, } else if ((rc == -EACCES) && backup_cred(cifs_sb) && (strcmp(server->vals->version_string, SMB1_VERSION_STRING) == 0)) { - /* - * For SMB2 and later the backup intent flag is already - * sent if needed on open and there is no path based - * FindFirst operation to use to retry with - */ + /* + * For SMB2 and later the backup intent flag is already + * sent if needed on open and there is no path based + * FindFirst operation to use to retry with + */ - srchinf = kzalloc(sizeof(struct cifs_search_info), - GFP_KERNEL); - if (srchinf == NULL) { - rc = -ENOMEM; - goto cgii_exit; - } + srchinf = kzalloc(sizeof(struct cifs_search_info), + GFP_KERNEL); + if (srchinf == NULL) { + rc = -ENOMEM; + goto cgii_exit; + } - srchinf->endOfSearch = false; + srchinf->endOfSearch = false; + if (tcon->unix_ext) + srchinf->info_level = SMB_FIND_FILE_UNIX; + else if ((tcon->ses->capabilities & + tcon->ses->server->vals->cap_nt_find) == 0) + srchinf->info_level = SMB_FIND_FILE_INFO_STANDARD; + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) srchinf->info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; + else /* no srvino useful for fallback to some netapp */ + srchinf->info_level = SMB_FIND_FILE_DIRECTORY_INFO; - srchflgs = CIFS_SEARCH_CLOSE_ALWAYS | - CIFS_SEARCH_CLOSE_AT_END | - CIFS_SEARCH_BACKUP_SEARCH; + srchflgs = CIFS_SEARCH_CLOSE_ALWAYS | + CIFS_SEARCH_CLOSE_AT_END | + CIFS_SEARCH_BACKUP_SEARCH; - rc = CIFSFindFirst(xid, tcon, full_path, - cifs_sb, NULL, srchflgs, srchinf, false); - if (!rc) { - data = - (FILE_ALL_INFO *)srchinf->srch_entries_start; + rc = CIFSFindFirst(xid, tcon, full_path, + cifs_sb, NULL, srchflgs, srchinf, false); + if (!rc) { + data = (FILE_ALL_INFO *)srchinf->srch_entries_start; - cifs_dir_info_to_fattr(&fattr, - (FILE_DIRECTORY_INFO *)data, cifs_sb); - fattr.cf_uniqueid = le64_to_cpu( - ((SEARCH_ID_FULL_DIR_INFO *)data)->UniqueId); - validinum = true; + cifs_dir_info_to_fattr(&fattr, + (FILE_DIRECTORY_INFO *)data, cifs_sb); + fattr.cf_uniqueid = le64_to_cpu( + ((SEARCH_ID_FULL_DIR_INFO *)data)->UniqueId); + validinum = true; - cifs_buf_release(srchinf->ntwrk_buf_start); - } - kfree(srchinf); - if (rc) - goto cgii_exit; + cifs_buf_release(srchinf->ntwrk_buf_start); + } + kfree(srchinf); + if (rc) + goto cgii_exit; } else goto cgii_exit; From 8d8b26e58432cb2840048b9f8aea286be6f75de5 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 17 Oct 2018 05:47:58 +1000 Subject: [PATCH 41/46] cifs: add support for ioctl on directories We do not call cifs_open_file() for directories and thus we do not have a pSMBFile we can extract the FIDs from. Solve this by instead always using a compounded open/query/close for the passthrough ioctl. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 4 ++- fs/cifs/ioctl.c | 43 ++++++++++++++++++----- fs/cifs/smb2ops.c | 85 +++++++++++++++++++++++++++++++++++----------- 3 files changed, 102 insertions(+), 30 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 73801254cc21..26f497bd97df 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -33,6 +33,7 @@ #define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */ +#define SMB_PATH_MAX 260 #define CIFS_PORT 445 #define RFC1001_PORT 139 @@ -467,7 +468,8 @@ struct smb_version_operations { int (*next_header)(char *); /* ioctl passthrough for query_info */ int (*ioctl_query_info)(const unsigned int xid, - struct cifsFileInfo *file, + struct cifs_tcon *tcon, + __le16 *path, int is_dir, unsigned long p); }; diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 77c7a5796dfd..76ddd98b6298 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c @@ -32,24 +32,49 @@ #include "cifs_debug.h" #include "cifsfs.h" #include "cifs_ioctl.h" +#include "smb2proto.h" #include static long cifs_ioctl_query_info(unsigned int xid, struct file *filep, unsigned long p) { - struct cifsFileInfo *pSMBFile = filep->private_data; - struct cifs_tcon *tcon; + struct inode *inode = file_inode(filep); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct dentry *dentry = filep->f_path.dentry; + unsigned char *path; + __le16 *utf16_path = NULL, root_path; + int rc = 0; - cifs_dbg(FYI, "%s %p\n", __func__, pSMBFile); - if (pSMBFile == NULL) - return -EISDIR; - tcon = tlink_tcon(pSMBFile->tlink); + path = build_path_from_dentry(dentry); + if (path == NULL) + return -ENOMEM; + + cifs_dbg(FYI, "%s %s\n", __func__, path); + + if (!path[0]) { + root_path = 0; + utf16_path = &root_path; + } else { + utf16_path = cifs_convert_path_to_utf16(path + 1, cifs_sb); + if (!utf16_path) { + rc = -ENOMEM; + goto ici_exit; + } + } if (tcon->ses->server->ops->ioctl_query_info) - return tcon->ses->server->ops->ioctl_query_info( - xid, pSMBFile, p); + rc = tcon->ses->server->ops->ioctl_query_info( + xid, tcon, utf16_path, + filep->private_data ? 0 : 1, p); else - return -EOPNOTSUPP; + rc = -EOPNOTSUPP; + + ici_exit: + if (utf16_path != &root_path) + kfree(utf16_path); + kfree(path); + return rc; } static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file, diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 8472cb0ae06c..e35a753eca20 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -1120,22 +1120,31 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon, static int smb2_ioctl_query_info(const unsigned int xid, - struct cifsFileInfo *file, + struct cifs_tcon *tcon, + __le16 *path, int is_dir, unsigned long p) { - struct cifs_tcon *tcon = tlink_tcon(file->tlink); struct cifs_ses *ses = tcon->ses; char __user *arg = (char __user *)p; struct smb_query_info qi; struct smb_query_info __user *pqi; int rc = 0; int flags = 0; - struct smb_rqst rqst; - struct kvec iov[1]; - struct kvec rsp_iov; - int resp_buftype = CIFS_NO_BUFFER; struct smb2_query_info_rsp *rsp = NULL; - void *buffer; + void *buffer = NULL; + struct smb_rqst rqst[3]; + int resp_buftype[3]; + struct kvec rsp_iov[3]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct cifs_open_parms oparms; + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_fid fid; + struct kvec qi_iov[1]; + struct kvec close_iov[1]; + + memset(rqst, 0, sizeof(rqst)); + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; + memset(rsp_iov, 0, sizeof(rsp_iov)); if (copy_from_user(&qi, arg, sizeof(struct smb_query_info))) return -EFAULT; @@ -1155,31 +1164,62 @@ smb2_ioctl_query_info(const unsigned int xid, if (copy_from_user(buffer, arg + sizeof(struct smb_query_info), qi.output_buffer_length)) { - kfree(buffer); - return -EFAULT; + rc = -EFAULT; + goto iqinf_exit; } - memset(&rqst, 0, sizeof(struct smb_rqst)); - memset(&iov, 0, sizeof(iov)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; + /* Open */ + memset(&open_iov, 0, sizeof(open_iov)); + rqst[0].rq_iov = open_iov; + rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; - rc = SMB2_query_info_init(tcon, &rqst, file->fid.persistent_fid, - file->fid.volatile_fid, + memset(&oparms, 0, sizeof(oparms)); + oparms.tcon = tcon; + oparms.desired_access = FILE_READ_ATTRIBUTES | READ_CONTROL; + oparms.disposition = FILE_OPEN; + if (is_dir) + oparms.create_options = CREATE_NOT_FILE; + else + oparms.create_options = CREATE_NOT_DIR; + oparms.fid = &fid; + oparms.reconnect = false; + + rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, path); + if (rc) + goto iqinf_exit; + smb2_set_next_command(ses->server, &rqst[0]); + + /* Query */ + memset(&qi_iov, 0, sizeof(qi_iov)); + rqst[1].rq_iov = qi_iov; + rqst[1].rq_nvec = 1; + + rc = SMB2_query_info_init(tcon, &rqst[1], COMPOUND_FID, COMPOUND_FID, qi.file_info_class, qi.info_type, qi.additional_information, qi.input_buffer_length, qi.output_buffer_length, buffer); - kfree(buffer); if (rc) goto iqinf_exit; + smb2_set_next_command(ses->server, &rqst[1]); + smb2_set_related(&rqst[1]); - rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); - rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; + /* Close */ + memset(&close_iov, 0, sizeof(close_iov)); + rqst[2].rq_iov = close_iov; + rqst[2].rq_nvec = 1; + + rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID); if (rc) goto iqinf_exit; + smb2_set_related(&rqst[2]); + rc = compound_send_recv(xid, ses, flags, 3, rqst, + resp_buftype, rsp_iov); + if (rc) + goto iqinf_exit; pqi = (struct smb_query_info __user *)arg; + rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; if (le32_to_cpu(rsp->OutputBufferLength) < qi.input_buffer_length) qi.input_buffer_length = le32_to_cpu(rsp->OutputBufferLength); if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length, @@ -1193,8 +1233,13 @@ smb2_ioctl_query_info(const unsigned int xid, } iqinf_exit: - SMB2_query_info_free(&rqst); - free_rsp_buf(resp_buftype, rsp); + kfree(buffer); + SMB2_open_free(&rqst[0]); + SMB2_query_info_free(&rqst[1]); + SMB2_close_free(&rqst[2]); + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); return rc; } From fae8044c03c3c03e06b384a6122b78a6c3a0b6a7 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 19 Oct 2018 17:14:32 -0500 Subject: [PATCH 42/46] smb3: show number of current open files in /proc/fs/cifs/Stats To allow better debugging (for example applications with handle leaks, or complex reconnect scenarios) display the number of open files (on the client) and number of open server file handles for each tcon in /proc/fs/cifs/Stats. Note that open files on server is one larger than local due to handle caching (in this case of the root of the share). In this example there are two local open files, and three (two file and one directory handle) open on the server. Sample output: $ cat /proc/fs/cifs/Stats Resources in use CIFS Session: 1 Share (unique mount targets): 2 SMB Request/Response Buffer: 1 Pool size: 5 SMB Small Req/Resp Buffer: 1 Pool size: 30 Operations (MIDs): 0 0 session 0 share reconnects Total vfs operations: 36 maximum at one time: 2 1) \\localhost\test SMBs: 69 Bytes read: 27 Bytes written: 0 Open files: 2 total (local), 3 open on server TreeConnects: 1 total 0 failed TreeDisconnects: 0 total 0 failed Creates: 19 total 0 failed Closes: 16 total 0 failed ... Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 2 ++ fs/cifs/file.c | 2 ++ fs/cifs/misc.c | 2 ++ fs/cifs/smb2ops.c | 3 +++ fs/cifs/smb2pdu.c | 5 ++++- 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 26f497bd97df..ed1e0fcb69e3 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -932,6 +932,8 @@ struct cifs_tcon { struct list_head tcon_list; int tc_count; struct list_head rlist; /* reconnect list */ + atomic_t num_local_opens; /* num of all opens including disconnected */ + atomic_t num_remote_opens; /* num of all network opens on server */ struct list_head openFileList; spinlock_t open_file_lock; /* protects list above */ struct cifs_ses *ses; /* pointer to session associated with */ diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 84477e995246..c620d4b5d5d4 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -334,6 +334,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, server->ops->set_fid(cfile, fid, oplock); list_add(&cfile->tlist, &tcon->openFileList); + atomic_inc(&tcon->num_local_opens); /* if readable file instance put first in list*/ if (file->f_mode & FMODE_READ) @@ -395,6 +396,7 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) /* remove it from the lists */ list_del(&cifs_file->flist); list_del(&cifs_file->tlist); + atomic_dec(&tcon->num_local_opens); if (list_empty(&cifsi->openFileList)) { cifs_dbg(FYI, "closing last open instance for inode %p\n", diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 6926685e513c..fc43d5d25d1d 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -123,6 +123,8 @@ tconInfoAlloc(void) ret_buf->crfid.fid = kzalloc(sizeof(struct cifs_fid), GFP_KERNEL); spin_lock_init(&ret_buf->stat_lock); + atomic_set(&ret_buf->num_local_opens, 0); + atomic_set(&ret_buf->num_remote_opens, 0); } return ret_buf; } diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index e35a753eca20..f85fc5aa2710 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -1023,6 +1023,9 @@ smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon) seq_printf(m, "\nBytes read: %llu Bytes written: %llu", (long long)(tcon->bytes_read), (long long)(tcon->bytes_written)); + seq_printf(m, "\nOpen files: %d total (local), %d open on server", + atomic_read(&tcon->num_local_opens), + atomic_read(&tcon->num_remote_opens)); seq_printf(m, "\nTreeConnects: %d total %d failed", atomic_read(&sent[SMB2_TREE_CONNECT_HE]), atomic_read(&failed[SMB2_TREE_CONNECT_HE])); diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 1310bb355e32..5a08c8f6a322 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -1478,7 +1478,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, /* SMB2 TREE_CONNECT request must be called with TreeId == 0 */ tcon->tid = 0; - + atomic_set(&tcon->num_remote_opens, 0); rc = smb2_plain_req_init(SMB2_TREE_CONNECT, tcon, (void **) &req, &total_len); if (rc) { @@ -2303,6 +2303,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, ses->Suid, oparms->create_options, oparms->desired_access); + atomic_inc(&tcon->num_remote_opens); oparms->fid->persistent_fid = rsp->PersistentFileId; oparms->fid->volatile_fid = rsp->VolatileFileId; @@ -2577,6 +2578,8 @@ SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon, goto close_exit; } + atomic_dec(&tcon->num_remote_opens); + /* BB FIXME - decode close response, update inode for caching */ close_exit: From 35a908072311b8efce8467161ea870cd410a1d68 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 19 Oct 2018 21:21:37 -0500 Subject: [PATCH 43/46] smb3 - clean up debug output displaying network interfaces Make the output of /proc/fs/cifs/DebugData a little easier to read by cleaning up the listing of network interfaces removing a wasted line break. Here is a comparison of the network interface information that from be viewed at the end of output from "cat /proc/fs/cifs/DebugData" Before: Server interfaces: 8 0) Speed: 10000000000 bps Capabilities: rss IPv6: fe80:0000:0000:0000:2cf5:407e:84b0:21dd 1) Speed: 1000000000 bps Capabilities: IPv6: fe80:0000:0000:0000:61cd:6147:3d0c:f484 vs. after: Server interfaces: 11 0) Speed: 10000000000 bps Capabilities: rss IPv6: fe80:0000:0000:0000:2cf5:407e:84b0:21dd 1) Speed: 2000000000 bps Capabilities: IPv6: fe80:0000:0000:0000:3d76:2d05:dcf8:ed10 Signed-off-by: Steve French --- fs/cifs/cifs_debug.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 3ae62ec74c8d..3e812428ac8d 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -132,7 +132,7 @@ cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface) struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr; struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr; - seq_printf(m, "\t\tSpeed: %zu bps\n", iface->speed); + seq_printf(m, "\tSpeed: %zu bps\n", iface->speed); seq_puts(m, "\t\tCapabilities: "); if (iface->rdma_capable) seq_puts(m, "rdma "); @@ -354,7 +354,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) seq_printf(m, "\n\tServer interfaces: %zu\n", ses->iface_count); for (j = 0; j < ses->iface_count; j++) { - seq_printf(m, "\t%d)\n", j); + seq_printf(m, "\t%d)", j); cifs_dump_iface(m, &ses->iface_list[j]); } spin_unlock(&ses->iface_lock); From 32a1fb36f6e50183871c2c1fcf5493c633e84732 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 24 Oct 2018 11:50:33 +1000 Subject: [PATCH 44/46] cifs: allow calling SMB2_xxx_free(NULL) Change these free functions to allow passing NULL as the argument and treat it as a no-op just like free(NULL) would. Or, if rqst->rq_iov is NULL. The second scenario could happen for smb2_queryfs() if the call to SMB2_query_info_init() fails and we go to qfs_exit to clean up and free all resources. In that case we have not yet assigned rqst[2].rq_iov and thus the rq_iov dereference in SMB2_close_free() will cause a NULL pointer dereference. Fixes: 1eb9fb52040f ("cifs: create SMB2_open_init()/SMB2_open_free() helpers") Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French Reviewed-by: Aurelien Aptel CC: Stable --- fs/cifs/smb2pdu.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 5a08c8f6a322..7d7b016fe8bb 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2243,10 +2243,12 @@ SMB2_open_free(struct smb_rqst *rqst) { int i; - cifs_small_buf_release(rqst->rq_iov[0].iov_base); - for (i = 1; i < rqst->rq_nvec; i++) - if (rqst->rq_iov[i].iov_base != smb2_padding) - kfree(rqst->rq_iov[i].iov_base); + if (rqst && rqst->rq_iov) { + cifs_small_buf_release(rqst->rq_iov[0].iov_base); + for (i = 1; i < rqst->rq_nvec; i++) + if (rqst->rq_iov[i].iov_base != smb2_padding) + kfree(rqst->rq_iov[i].iov_base); + } } int @@ -2536,7 +2538,8 @@ SMB2_close_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, void SMB2_close_free(struct smb_rqst *rqst) { - cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ + if (rqst && rqst->rq_iov) + cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ } int @@ -2689,7 +2692,8 @@ SMB2_query_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, void SMB2_query_info_free(struct smb_rqst *rqst) { - cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ + if (rqst && rqst->rq_iov) + cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ } static int @@ -3803,7 +3807,8 @@ SMB2_set_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, void SMB2_set_info_free(struct smb_rqst *rqst) { - cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */ + if (rqst && rqst->rq_iov) + cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */ } static int From 43de1db36424fd21bbcfbe11a018f25c1566f246 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 23 Oct 2018 21:04:57 -0500 Subject: [PATCH 45/46] smb3: add debug for unexpected mid cancellation We have hit this intermittently, increase the verbosity of warning message on unexpected mid cancellation. Signed-off-by: Steve French --- fs/cifs/transport.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index fd80749c7838..f8112433f0c8 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -876,8 +876,8 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, for (i = 0; i < num_rqst; i++) { rc = wait_for_response(ses->server, midQ[i]); if (rc != 0) { - cifs_dbg(FYI, "Cancelling wait for mid %llu\n", - midQ[i]->mid); + cifs_dbg(VFS, "Cancelling wait for mid %llu cmd: %d\n", + midQ[i]->mid, le16_to_cpu(midQ[i]->command)); send_cancel(ses->server, &rqst[i], midQ[i]); spin_lock(&GlobalMid_Lock); if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) { From 38f876bb2d621bfbcc6636c0317121137e9f2afc Mon Sep 17 00:00:00 2001 From: Steve French Date: Sun, 23 Sep 2018 19:06:33 -0500 Subject: [PATCH 46/46] cifs: update internal module version number for cifs.ko to 2.14 Update version reported in "modinfo cifs" Signed-off-by: Steve French --- fs/cifs/cifsfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index f047e87871a1..24e265a51874 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -148,5 +148,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); extern const struct export_operations cifs_export_ops; #endif /* CONFIG_CIFS_NFSD_EXPORT */ -#define CIFS_VERSION "2.13" +#define CIFS_VERSION "2.14" #endif /* _CIFSFS_H */