From becc2ba26ae65bfeaece35a1b67e8a54eb9677cf Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 24 Jul 2019 11:43:49 +1000 Subject: [PATCH 01/38] cifs: fix a comment for the timeouts when sending echos Clarify a trivial comment Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/connect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 5299effa6f7d..4fe559821aff 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -712,7 +712,7 @@ server_unresponsive(struct TCP_Server_Info *server) * We need to wait 3 echo intervals to make sure we handle such * situations right: * 1s client sends a normal SMB request - * 3s client gets a response + * 2s client gets a response * 30s echo workqueue job pops, and decides we got a response recently * and don't need to send another * ... From 1afdea4f19a97ed98f65c41fd11c77e5264838ae Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 23 Jul 2019 16:09:19 +0100 Subject: [PATCH 02/38] fs: cifs: cifsssmb: remove redundant assignment to variable ret The variable ret is being initialized however this is never read and later it is being reassigned to a new value. The initialization is redundant and hence can be removed. Addresses-Coverity: ("Unused Value") Signed-off-by: Colin Ian King Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 3907653e63c7..dbee2132e419 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1393,7 +1393,7 @@ int CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock, FILE_ALL_INFO *buf) { - int rc = -EACCES; + int rc; OPEN_REQ *req = NULL; OPEN_RSP *rsp = NULL; int bytes_returned; From e2f8fbfb8d09c06decde162090fac3ee220aa280 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 19 Jul 2019 06:30:07 +0000 Subject: [PATCH 03/38] cifs: get mode bits from special sid on stat When mounting with "modefromsid" retrieve mode bits from special SID (S-1-5-88-3) on stat. Subsequent patch will fix setattr (chmod) to save mode bits in S-1-5-88-3- Note that when an ACE matching S-1-5-88-3 is not found, we default the mode to an approximation based on the owner, group and everyone permissions (as with the "cifsacl" mount option). See See e.g. https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/hh509017(v=ws.10) Signed-off-by: Steve French --- fs/cifs/cifsacl.c | 37 ++++++++++++++++++++++++++----------- fs/cifs/cifsproto.h | 1 + fs/cifs/inode.c | 13 +++++++++++-- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 1d377b7f2860..7fe4509e6c03 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -701,10 +701,9 @@ static void dump_ace(struct cifs_ace *pace, char *end_of_acl) } #endif - static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, - struct cifs_fattr *fattr) + struct cifs_fattr *fattr, bool mode_from_special_sid) { int i; int num_aces = 0; @@ -757,22 +756,34 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, #ifdef CONFIG_CIFS_DEBUG2 dump_ace(ppace[i], end_of_acl); #endif - if (compare_sids(&(ppace[i]->sid), pownersid) == 0) + if (mode_from_special_sid && + (compare_sids(&(ppace[i]->sid), + &sid_unix_NFS_mode) == 0)) { + /* + * Full permissions are: + * 07777 = S_ISUID | S_ISGID | S_ISVTX | + * S_IRWXU | S_IRWXG | S_IRWXO + */ + fattr->cf_mode &= ~07777; + fattr->cf_mode |= + le32_to_cpu(ppace[i]->sid.sub_auth[2]); + break; + } else if (compare_sids(&(ppace[i]->sid), pownersid) == 0) access_flags_to_mode(ppace[i]->access_req, ppace[i]->type, &fattr->cf_mode, &user_mask); - if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0) + else if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0) access_flags_to_mode(ppace[i]->access_req, ppace[i]->type, &fattr->cf_mode, &group_mask); - if (compare_sids(&(ppace[i]->sid), &sid_everyone) == 0) + else if (compare_sids(&(ppace[i]->sid), &sid_everyone) == 0) access_flags_to_mode(ppace[i]->access_req, ppace[i]->type, &fattr->cf_mode, &other_mask); - if (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0) + else if (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0) access_flags_to_mode(ppace[i]->access_req, ppace[i]->type, &fattr->cf_mode, @@ -851,7 +862,8 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl) /* Convert CIFS ACL to POSIX form */ static int parse_sec_desc(struct cifs_sb_info *cifs_sb, - struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr) + struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr, + bool get_mode_from_special_sid) { int rc = 0; struct cifs_sid *owner_sid_ptr, *group_sid_ptr; @@ -900,7 +912,7 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb, if (dacloffset) parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, - group_sid_ptr, fattr); + group_sid_ptr, fattr, get_mode_from_special_sid); else cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */ @@ -1128,8 +1140,8 @@ int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen, /* Translate the CIFS ACL (similar to NTFS ACL) for a file into mode bits */ int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, - struct inode *inode, const char *path, - const struct cifs_fid *pfid) + struct inode *inode, bool mode_from_special_sid, + const char *path, const struct cifs_fid *pfid) { struct cifs_ntsd *pntsd = NULL; u32 acllen = 0; @@ -1156,8 +1168,11 @@ cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, if (IS_ERR(pntsd)) { rc = PTR_ERR(pntsd); cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc); + } else if (mode_from_special_sid) { + rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, true); } else { - rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr); + /* get approximated mode from ACL */ + rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, false); kfree(pntsd); if (rc) cifs_dbg(VFS, "parse sec desc failed rc = %d\n", rc); diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 592a6cea2b79..dd75746e60cd 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -197,6 +197,7 @@ extern int cifs_rename_pending_delete(const char *full_path, const unsigned int xid); extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, struct inode *inode, + bool get_mode_from_special_sid, const char *path, const struct cifs_fid *pfid); extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64, kuid_t, kgid_t); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 56ca4b8ccaba..c1e620ebcf7c 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -893,8 +893,17 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, } /* fill in 0777 bits from ACL */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { - rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, full_path, fid); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) { + rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, true, + full_path, fid); + if (rc) { + cifs_dbg(FYI, "%s: Get mode from SID failed. rc=%d\n", + __func__, rc); + goto cgii_exit; + } + } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { + rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, false, + full_path, fid); if (rc) { cifs_dbg(FYI, "%s: Getting ACL failed with error: %d\n", __func__, rc); From 22442179a5bb8c62c978cdb686cfb3a8f6d110d8 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 19 Jul 2019 08:15:55 +0000 Subject: [PATCH 04/38] cifs: allow chmod to set mode bits using special sid When mounting with "modefromsid" set mode bits (chmod) by adding ACE with special SID (S-1-5-88-3-) to the ACL. Subsequent patch will fix setting default mode on file create and mkdir. See See e.g. https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/hh509017(v=ws.10) Signed-off-by: Steve French --- fs/cifs/cifsacl.c | 42 +++++++++++++++++++++++++++++++++++++----- fs/cifs/inode.c | 6 ++++-- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 7fe4509e6c03..3e0c5ed9ca20 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -806,7 +806,7 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid, - struct cifs_sid *pgrpsid, __u64 nmode) + struct cifs_sid *pgrpsid, __u64 nmode, bool modefromsid) { u16 size = 0; struct cifs_acl *pnndacl; @@ -820,8 +820,33 @@ static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid, size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size), &sid_everyone, nmode, S_IRWXO); + /* TBD: Move this ACE to the top of ACE list instead of bottom */ + if (modefromsid) { + struct cifs_ace *pntace = + (struct cifs_ace *)((char *)pnndacl + size); + int i; + + pntace->type = ACCESS_DENIED; + pntace->flags = 0x0; + pntace->sid.num_subauth = 3; + pntace->sid.revision = 1; + /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4) */ + pntace->size = cpu_to_le16(28); + size += 28; + for (i = 0; i < NUM_AUTHS; i++) + pntace->sid.authority[i] = + sid_unix_NFS_mode.authority[i]; + pntace->sid.sub_auth[0] = sid_unix_NFS_mode.sub_auth[0]; + pntace->sid.sub_auth[1] = sid_unix_NFS_mode.sub_auth[1]; + pntace->sid.sub_auth[2] = cpu_to_le32(nmode & 07777); + + pndacl->num_aces = cpu_to_le32(4); + size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size), + &sid_unix_NFS_mode, nmode, S_IRWXO); + } else + pndacl->num_aces = cpu_to_le32(3); + pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl)); - pndacl->num_aces = cpu_to_le32(3); return 0; } @@ -921,7 +946,8 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb, /* Convert permission bits from mode to equivalent CIFS ACL */ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, - __u32 secdesclen, __u64 nmode, kuid_t uid, kgid_t gid, int *aclflag) + __u32 secdesclen, __u64 nmode, kuid_t uid, kgid_t gid, + bool mode_from_sid, int *aclflag) { int rc = 0; __u32 dacloffset; @@ -946,7 +972,7 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, ndacl_ptr->num_aces = 0; rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr, - nmode); + nmode, mode_from_sid); sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); /* copy sec desc control portion & owner and group sids */ copy_sec_desc(pntsd, pnntsd, sidsoffset); @@ -1196,6 +1222,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode, struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); struct smb_version_operations *ops; + bool mode_from_sid; if (IS_ERR(tlink)) return PTR_ERR(tlink); @@ -1233,8 +1260,13 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode, return -ENOMEM; } + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) + mode_from_sid = true; + else + mode_from_sid = false; + rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid, - &aclflag); + mode_from_sid, &aclflag); cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index c1e620ebcf7c..26cdfbf1e164 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -2489,7 +2489,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) if (attrs->ia_valid & ATTR_GID) gid = attrs->ia_gid; - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) { if (uid_valid(uid) || gid_valid(gid)) { rc = id_mode_to_cifs_acl(inode, full_path, NO_CHANGE_64, uid, gid); @@ -2510,7 +2511,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) if (attrs->ia_valid & ATTR_MODE) { mode = attrs->ia_mode; rc = 0; - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) { rc = id_mode_to_cifs_acl(inode, full_path, mode, INVALID_UID, INVALID_GID); if (rc) { From 86e14e12051a229070f43d6f80037cd064afa4a5 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 16 Jul 2019 15:07:08 +1000 Subject: [PATCH 05/38] cifs: prepare SMB2_Flush to be usable in compounds Create smb2_flush_init() and smb2_flush_free() so we can use the flush command in compounds. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/smb2pdu.c | 60 +++++++++++++++++++++++++++++++-------------- fs/cifs/smb2proto.h | 4 +++ 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 31e4a1b0b170..ee5d74988a9f 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -3287,44 +3287,64 @@ SMB2_echo(struct TCP_Server_Info *server) return rc; } -int -SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, - u64 volatile_fid) +void +SMB2_flush_free(struct smb_rqst *rqst) +{ + if (rqst && rqst->rq_iov) + cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ +} + +int +SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, + struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid) { - struct smb_rqst rqst; struct smb2_flush_req *req; - struct cifs_ses *ses = tcon->ses; - struct kvec iov[1]; - struct kvec rsp_iov; - int resp_buftype; - int rc = 0; - int flags = 0; + struct kvec *iov = rqst->rq_iov; unsigned int total_len; - - cifs_dbg(FYI, "Flush\n"); - - if (!ses || !(ses->server)) - return -EIO; + int rc; rc = smb2_plain_req_init(SMB2_FLUSH, tcon, (void **) &req, &total_len); if (rc) return rc; - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - req->PersistentFileId = persistent_fid; req->VolatileFileId = volatile_fid; iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; + return 0; +} + +int +SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + u64 volatile_fid) +{ + struct cifs_ses *ses = tcon->ses; + struct smb_rqst rqst; + struct kvec iov[1]; + struct kvec rsp_iov = {NULL, 0}; + int resp_buftype = CIFS_NO_BUFFER; + int flags = 0; + int rc = 0; + + cifs_dbg(FYI, "flush\n"); + if (!ses || !(ses->server)) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); rqst.rq_iov = iov; rqst.rq_nvec = 1; + rc = SMB2_flush_init(xid, &rqst, tcon, persistent_fid, volatile_fid); + if (rc) + goto flush_exit; + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); - cifs_small_buf_release(req); if (rc != 0) { cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE); @@ -3332,6 +3352,8 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, rc); } + flush_exit: + SMB2_flush_free(&rqst); free_rsp_buf(resp_buftype, rsp_iov.iov_base); return rc; } diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 07ca72486cfa..67a91b11fd59 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -158,6 +158,10 @@ extern int SMB2_close_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, extern void SMB2_close_free(struct smb_rqst *rqst); extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_file_id, u64 volatile_file_id); +extern int SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, + struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id); +extern void SMB2_flush_free(struct smb_rqst *rqst); extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_file_id, u64 volatile_file_id, struct smb2_file_all_info *data); From 0e90696dc2b33a2c390242c5b9f330a16ae2ac82 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 25 Jul 2019 13:08:43 +1000 Subject: [PATCH 06/38] cifs: add passthrough for smb2 setinfo Add support to send smb2 set-info commands from userspace. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French Reviewed-by: Paulo Alcantara --- fs/cifs/cifs_ioctl.h | 1 + fs/cifs/smb2ops.c | 29 +++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/fs/cifs/cifs_ioctl.h b/fs/cifs/cifs_ioctl.h index 086ddc5108af..6c3bd07868d7 100644 --- a/fs/cifs/cifs_ioctl.h +++ b/fs/cifs/cifs_ioctl.h @@ -46,6 +46,7 @@ struct smb_snapshot_array { /* query_info flags */ #define PASSTHRU_QUERY_INFO 0x00000000 #define PASSTHRU_FSCTL 0x00000001 +#define PASSTHRU_SET_INFO 0x00000002 struct smb_query_info { __u32 info_type; __u32 file_info_class; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 64a5864127be..461a68fdff1f 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -1369,7 +1369,10 @@ smb2_ioctl_query_info(const unsigned int xid, struct cifs_fid fid; struct kvec qi_iov[1]; struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; + struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; struct kvec close_iov[1]; + unsigned int size[2]; + void *data[2]; memset(rqst, 0, sizeof(rqst)); resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; @@ -1404,7 +1407,6 @@ smb2_ioctl_query_info(const unsigned int xid, 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; @@ -1413,9 +1415,6 @@ smb2_ioctl_query_info(const unsigned int xid, oparms.fid = &fid; oparms.reconnect = false; - /* - * FSCTL codes encode the special access they need in the fsctl code. - */ if (qi.flags & PASSTHRU_FSCTL) { switch (qi.info_type & FSCTL_DEVICE_ACCESS_MASK) { case FSCTL_DEVICE_ACCESS_FILE_READ_WRITE_ACCESS: @@ -1431,6 +1430,10 @@ smb2_ioctl_query_info(const unsigned int xid, oparms.desired_access = GENERIC_WRITE; break; } + } else if (qi.flags & PASSTHRU_SET_INFO) { + oparms.desired_access = GENERIC_WRITE; + } else { + oparms.desired_access = FILE_READ_ATTRIBUTES | READ_CONTROL; } rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, path); @@ -1454,6 +1457,24 @@ smb2_ioctl_query_info(const unsigned int xid, qi.output_buffer_length, CIFSMaxBufSize); } + } else if (qi.flags == PASSTHRU_SET_INFO) { + /* Can eventually relax perm check since server enforces too */ + if (!capable(CAP_SYS_ADMIN)) + rc = -EPERM; + else { + memset(&si_iov, 0, sizeof(si_iov)); + rqst[1].rq_iov = si_iov; + rqst[1].rq_nvec = 1; + + size[0] = 8; + data[0] = buffer; + + rc = SMB2_set_info_init(tcon, &rqst[1], + COMPOUND_FID, COMPOUND_FID, + current->tgid, + FILE_END_OF_FILE_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + } } else if (qi.flags == PASSTHRU_QUERY_INFO) { memset(&qi_iov, 0, sizeof(qi_iov)); rqst[1].rq_iov = qi_iov; From 59519803a9adb991876cd0bb055b4791d7e26cef Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 25 Jul 2019 18:19:42 -0500 Subject: [PATCH 07/38] smb3: add missing flag definitions SMB3 and 3.1.1 added two additional flags including the priority mask. Add them to our protocol definitions in smb2pdu.h. See MS-SMB2 2.2.1.2 Signed-off-by: Steve French Signed-off-by: Ronnie Sahlberg Reviewed-by: Pavel Shilovsky --- fs/cifs/smb2pdu.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 747de9317659..ea735d59c36e 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -143,7 +143,9 @@ struct smb2_transform_hdr { #define SMB2_FLAGS_ASYNC_COMMAND cpu_to_le32(0x00000002) #define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004) #define SMB2_FLAGS_SIGNED cpu_to_le32(0x00000008) +#define SMB2_FLAGS_PRIORITY_MASK cpu_to_le32(0x00000070) /* SMB3.1.1 */ #define SMB2_FLAGS_DFS_OPERATIONS cpu_to_le32(0x10000000) +#define SMB2_FLAGS_REPLAY_OPERATION cpu_to_le32(0x20000000) /* SMB3 & up */ /* * Definitions for SMB2 Protocol Data Units (network frames) From 1efd4fc72e1f1a7313aa66e1c0269f1041eba2ce Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 31 Jul 2019 10:05:26 +0100 Subject: [PATCH 08/38] cifs: remove redundant assignment to variable rc Variable rc is being initialized with a value that is never read and rc is being re-assigned a little later on. The assignment is redundant and hence can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Steve French --- fs/cifs/smb2pdu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index ee5d74988a9f..a653c429e8dc 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -3617,7 +3617,7 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, unsigned int *nbytes, char **buf, int *buf_type) { struct smb_rqst rqst; - int resp_buftype, rc = -EACCES; + int resp_buftype, rc; struct smb2_read_plain_req *req = NULL; struct smb2_read_rsp *rsp = NULL; struct kvec iov[1]; From 2617474bfa33ab6c47c515e57dfbe754f8970640 Mon Sep 17 00:00:00 2001 From: zhengbin Date: Tue, 20 Aug 2019 22:00:47 +0800 Subject: [PATCH 09/38] cifs: remove unused variable In smb3_punch_hole, variable cifsi set but not used, remove it. In cifs_lock, variable netfid set but not used, remove it. Reported-by: Hulk Robot Signed-off-by: zhengbin Signed-off-by: Steve French --- fs/cifs/file.c | 2 -- fs/cifs/smb2ops.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 97090693d182..ab07ae882e62 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1695,7 +1695,6 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock) struct cifs_tcon *tcon; struct cifsInodeInfo *cinode; struct cifsFileInfo *cfile; - __u16 netfid; __u32 type; rc = -EACCES; @@ -1711,7 +1710,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)); if (cap_unix(tcon->ses) && diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 461a68fdff1f..070d0b7b21dc 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -2960,7 +2960,6 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, loff_t offset, loff_t len) { struct inode *inode; - struct cifsInodeInfo *cifsi; struct cifsFileInfo *cfile = file->private_data; struct file_zero_data_information fsctl_buf; long rc; @@ -2970,7 +2969,6 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, xid = get_xid(); inode = d_inode(cfile->dentry); - cifsi = CIFS_I(inode); /* Need to make file sparse, if not already, before freeing range. */ /* Consider adding equivalent for compressed since it could also work */ From df58fae72428be113f041fe55c9605a9088bfb14 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 5 Aug 2019 17:07:26 -0500 Subject: [PATCH 10/38] smb3: Incorrect size for netname negotiate context It is not null terminated (length was off by two). Also see similar change to Samba: https://gitlab.com/samba-team/samba/merge_requests/666 Reported-by: Stefan Metzmacher Signed-off-by: Steve French --- fs/cifs/smb2pdu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index a653c429e8dc..a33cb91b4963 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -503,8 +503,7 @@ build_netname_ctxt(struct smb2_netname_neg_context *pneg_ctxt, char *hostname) pneg_ctxt->ContextType = SMB2_NETNAME_NEGOTIATE_CONTEXT_ID; /* copy up to max of first 100 bytes of server name to NetName field */ - pneg_ctxt->DataLength = cpu_to_le16(2 + - (2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp))); + pneg_ctxt->DataLength = cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp)); /* context size is DataLength + minimal smb2_neg_context */ return DIV_ROUND_UP(le16_to_cpu(pneg_ctxt->DataLength) + sizeof(struct smb2_neg_context), 8) * 8; From 31ebdc11340abfdb02b7cdbfcc5531b7f479d58d Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 23 Aug 2019 20:15:35 +0800 Subject: [PATCH 11/38] cifs: remove set but not used variables Fixes gcc '-Wunused-but-set-variable' warning: fs/cifs/file.c: In function cifs_lock: fs/cifs/file.c:1696:24: warning: variable cinode set but not used [-Wunused-but-set-variable] fs/cifs/file.c: In function cifs_write: fs/cifs/file.c:1765:23: warning: variable cifs_sb set but not used [-Wunused-but-set-variable] fs/cifs/file.c: In function collect_uncached_read_data: fs/cifs/file.c:3578:20: warning: variable tcon set but not used [-Wunused-but-set-variable] 'cinode' is never used since introduced by commit 03776f4516bc ("CIFS: Simplify byte range locking code") 'cifs_sb' is not used since commit cb7e9eabb2b5 ("CIFS: Use multicredits for SMB 2.1/3 writes"). 'tcon' is not used since commit d26e2903fc10 ("smb3: fix bytes_read statistics") Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: Steve French --- fs/cifs/file.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index ab07ae882e62..f16f6d2b5217 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1693,7 +1693,6 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock) bool posix_lck = false; struct cifs_sb_info *cifs_sb; struct cifs_tcon *tcon; - struct cifsInodeInfo *cinode; struct cifsFileInfo *cfile; __u32 type; @@ -1710,7 +1709,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); - cinode = CIFS_I(file_inode(file)); if (cap_unix(tcon->ses) && (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && @@ -1762,7 +1760,6 @@ cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data, int rc = 0; unsigned int bytes_written = 0; unsigned int total_written; - struct cifs_sb_info *cifs_sb; struct cifs_tcon *tcon; struct TCP_Server_Info *server; unsigned int xid; @@ -1770,8 +1767,6 @@ cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data, struct cifsInodeInfo *cifsi = CIFS_I(d_inode(dentry)); struct cifs_io_parms io_parms; - cifs_sb = CIFS_SB(dentry->d_sb); - cifs_dbg(FYI, "write %zd bytes to offset %lld of %pd\n", write_size, *offset, dentry); @@ -3575,10 +3570,8 @@ collect_uncached_read_data(struct cifs_aio_ctx *ctx) struct cifs_readdata *rdata, *tmp; struct iov_iter *to = &ctx->iter; struct cifs_sb_info *cifs_sb; - struct cifs_tcon *tcon; int rc; - tcon = tlink_tcon(ctx->cfile->tlink); cifs_sb = CIFS_SB(ctx->cfile->dentry->d_sb); mutex_lock(&ctx->aio_mutex); From 8de9e86c67baa71c661b9ba59a4e23210d98bc79 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Fri, 30 Aug 2019 08:25:46 +1000 Subject: [PATCH 12/38] cifs: create a helper to find a writeable handle by path name rename() takes a path for old_file and in SMB2 we used to just create a compound for create(old_path)/rename/close(). If we already have a writable handle we can avoid the create() and close() altogether and just use the existing handle. For this situation, as we avoid doing the create() we also avoid triggering an oplock break for the existing handle. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 2 + fs/cifs/dir.c | 2 +- fs/cifs/file.c | 35 +++++++++++++++++ fs/cifs/smb2inode.c | 92 +++++++++++++++++++++++++++++++++------------ 4 files changed, 105 insertions(+), 26 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index dd75746e60cd..7b69037bed24 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -137,6 +137,8 @@ extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool); extern int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, bool fsuid_only, struct cifsFileInfo **ret_file); +extern int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name, + struct cifsFileInfo **ret_file); extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool); extern unsigned int smbCalcSize(void *buf, struct TCP_Server_Info *server); extern int decode_negTokenInit(unsigned char *security_blob, int length, diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index be424e81e3ad..dd5ac841aefa 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -125,7 +125,7 @@ build_path_from_dentry_optional_prefix(struct dentry *direntry, bool prefix) } rcu_read_unlock(); - full_path = kmalloc(namelen+1, GFP_KERNEL); + full_path = kmalloc(namelen+1, GFP_ATOMIC); if (full_path == NULL) return full_path; full_path[namelen] = 0; /* trailing null */ diff --git a/fs/cifs/file.c b/fs/cifs/file.c index f16f6d2b5217..6124b1d1ab05 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1973,6 +1973,41 @@ find_writable_file(struct cifsInodeInfo *cifs_inode, bool fsuid_only) return cfile; } +int +cifs_get_writable_path(struct cifs_tcon *tcon, const char *name, + struct cifsFileInfo **ret_file) +{ + struct list_head *tmp; + struct cifsFileInfo *cfile; + struct cifsInodeInfo *cinode; + char *full_path; + + *ret_file = NULL; + + spin_lock(&tcon->open_file_lock); + list_for_each(tmp, &tcon->openFileList) { + cfile = list_entry(tmp, struct cifsFileInfo, + tlist); + full_path = build_path_from_dentry(cfile->dentry); + if (full_path == NULL) { + spin_unlock(&tcon->open_file_lock); + return -ENOMEM; + } + if (strcmp(full_path, name)) { + kfree(full_path); + continue; + } + + kfree(full_path); + cinode = CIFS_I(d_inode(cfile->dentry)); + spin_unlock(&tcon->open_file_lock); + return cifs_get_writable_file(cinode, 0, ret_file); + } + + spin_unlock(&tcon->open_file_lock); + return -ENOENT; +} + static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) { struct address_space *mapping = page->mapping; diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index d8d9cdfa30b6..939fc7b2234c 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -51,7 +51,8 @@ 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 *ptr, int command) + __u32 create_options, void *ptr, int command, + struct cifsFileInfo *cfile) { int rc; __le16 *utf16_path = NULL; @@ -83,10 +84,16 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; memset(rsp_iov, 0, sizeof(rsp_iov)); + /* We already have a handle so we can skip the open */ + if (cfile) + goto after_open; + /* Open */ utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); - if (!utf16_path) - return -ENOMEM; + if (!utf16_path) { + rc = -ENOMEM; + goto finished; + } oparms.tcon = tcon; oparms.desired_access = desired_access; @@ -106,7 +113,10 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, if (rc) goto finished; - smb2_set_next_command(tcon, &rqst[num_rqst++]); + smb2_set_next_command(tcon, &rqst[num_rqst]); + after_open: + num_rqst++; + rc = 0; /* Operation */ switch (command) { @@ -210,14 +220,23 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, 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, + if (cfile) + rc = SMB2_set_info_init(tcon, &rqst[num_rqst], + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + current->tgid, FILE_RENAME_INFORMATION, SMB2_O_INFO_FILE, 0, data, size); + else { + 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(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst]); + } if (rc) goto finished; - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst++]); + num_rqst++; trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path); break; case SMB2_OP_HARDLINK: @@ -254,20 +273,36 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, if (rc) goto finished; + /* We already have a handle so we can skip the close */ + if (cfile) + goto after_close; /* 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++]); + smb2_set_related(&rqst[num_rqst]); if (rc) goto finished; + after_close: + num_rqst++; - rc = compound_send_recv(xid, ses, flags, num_rqst, rqst, - resp_buftype, rsp_iov); + if (cfile) { + cifsFileInfo_put(cfile); + cfile = NULL; + rc = compound_send_recv(xid, ses, flags, num_rqst - 2, + &rqst[1], &resp_buftype[1], + &rsp_iov[1]); + } else + rc = compound_send_recv(xid, ses, flags, num_rqst, + rqst, resp_buftype, + rsp_iov); finished: + if (cfile) + cifsFileInfo_put(cfile); + SMB2_open_free(&rqst[0]); switch (command) { case SMB2_OP_QUERY_INFO: @@ -404,7 +439,7 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, create_options, - smb2_data, SMB2_OP_QUERY_INFO); + smb2_data, SMB2_OP_QUERY_INFO, NULL); if (rc == -EOPNOTSUPP) { *symlink = true; create_options |= OPEN_REPARSE_POINT; @@ -413,7 +448,7 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, create_options, smb2_data, - SMB2_OP_QUERY_INFO); + SMB2_OP_QUERY_INFO, NULL); } if (rc) goto out; @@ -430,7 +465,7 @@ smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, { return smb2_compound_op(xid, tcon, cifs_sb, name, FILE_WRITE_ATTRIBUTES, FILE_CREATE, - CREATE_NOT_FILE, NULL, SMB2_OP_MKDIR); + CREATE_NOT_FILE, NULL, SMB2_OP_MKDIR, NULL); } void @@ -449,7 +484,8 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name, data.Attributes = cpu_to_le32(dosattrs); tmprc = smb2_compound_op(xid, tcon, cifs_sb, name, FILE_WRITE_ATTRIBUTES, FILE_CREATE, - CREATE_NOT_FILE, &data, SMB2_OP_SET_INFO); + CREATE_NOT_FILE, &data, SMB2_OP_SET_INFO, + NULL); if (tmprc == 0) cifs_i->cifsAttrs = dosattrs; } @@ -460,7 +496,7 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, { return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, CREATE_NOT_FILE, - NULL, SMB2_OP_RMDIR); + NULL, SMB2_OP_RMDIR, NULL); } int @@ -469,13 +505,14 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, { return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, - NULL, SMB2_OP_DELETE); + NULL, SMB2_OP_DELETE, NULL); } static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb, __u32 access, int command) + struct cifs_sb_info *cifs_sb, __u32 access, int command, + struct cifsFileInfo *cfile) { __le16 *smb2_to_name = NULL; int rc; @@ -486,7 +523,7 @@ smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, goto smb2_rename_path; } rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, - FILE_OPEN, 0, smb2_to_name, command); + FILE_OPEN, 0, smb2_to_name, command, cfile); smb2_rename_path: kfree(smb2_to_name); return rc; @@ -497,8 +534,12 @@ smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon, const char *from_name, const char *to_name, struct cifs_sb_info *cifs_sb) { - return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, - DELETE, SMB2_OP_RENAME); + struct cifsFileInfo *cfile; + + cifs_get_writable_path(tcon, from_name, &cfile); + + return smb2_set_path_attr(xid, tcon, from_name, to_name, + cifs_sb, DELETE, SMB2_OP_RENAME, cfile); } int @@ -507,7 +548,8 @@ smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb) { return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, - FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK); + FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK, + NULL); } int @@ -519,7 +561,7 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, return smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_WRITE_DATA, FILE_OPEN, 0, &eof, - SMB2_OP_SET_EOF); + SMB2_OP_SET_EOF, NULL); } int @@ -541,7 +583,7 @@ smb2_set_file_info(struct inode *inode, const char *full_path, rc = smb2_compound_op(xid, tlink_tcon(tlink), cifs_sb, full_path, FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf, - SMB2_OP_SET_INFO); + SMB2_OP_SET_INFO, NULL); cifs_put_tlink(tlink); return rc; } From dc9300a670aebb22e9b04b3664da6b218b29ab60 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Fri, 30 Aug 2019 09:53:56 +1000 Subject: [PATCH 13/38] cifs: use existing handle for compound_op(OP_SET_INFO) when possible If we already have a writable handle for a path we want to set the attributes for then use that instead of a create/set-info/close compound. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/smb2inode.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 939fc7b2234c..9bb0a5bc38c9 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -192,14 +192,27 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, 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); + if (cfile) + rc = SMB2_set_info_init(tcon, &rqst[num_rqst], + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, current->tgid, + FILE_BASIC_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + else { + 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); + if (!rc) { + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst]); + } + } + if (rc) goto finished; - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst++]); + num_rqst++; trace_smb3_set_info_compound_enter(xid, ses->Suid, tcon->tid, full_path); break; @@ -231,8 +244,10 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, COMPOUND_FID, COMPOUND_FID, current->tgid, FILE_RENAME_INFORMATION, SMB2_O_INFO_FILE, 0, data, size); - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst]); + if (!rc) { + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst]); + } } if (rc) goto finished; @@ -475,6 +490,7 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name, { FILE_BASIC_INFO data; struct cifsInodeInfo *cifs_i; + struct cifsFileInfo *cfile; u32 dosattrs; int tmprc; @@ -482,10 +498,11 @@ 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); + cifs_get_writable_path(tcon, name, &cfile); tmprc = smb2_compound_op(xid, tcon, cifs_sb, name, FILE_WRITE_ATTRIBUTES, FILE_CREATE, CREATE_NOT_FILE, &data, SMB2_OP_SET_INFO, - NULL); + cfile); if (tmprc == 0) cifs_i->cifsAttrs = dosattrs; } From afe6f65353b644f55875d42d812ffe87f1887d68 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 28 Aug 2019 17:15:35 +1000 Subject: [PATCH 14/38] cifs: add new debugging macro cifs_server_dbg which can be used from contexts where we have a TCP_Server_Info *server. This new macro will prepend the debugging string with "Server: " which will help when debugging issues on hosts with many cifs connections to several different servers. Convert a bunch of cifs_dbg(VFS) calls to cifs_server_dbg(VFS) Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/cifs_debug.h | 31 +++++++++++ fs/cifs/connect.c | 47 ++++++++-------- fs/cifs/smb2pdu.c | 113 ++++++++++++++++++++------------------- fs/cifs/smb2transport.c | 61 ++++++++++----------- fs/cifs/transport.c | 115 +++++++++++++++++++++------------------- 5 files changed, 204 insertions(+), 163 deletions(-) diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h index 3d392620a2f4..567af916f103 100644 --- a/fs/cifs/cifs_debug.h +++ b/fs/cifs/cifs_debug.h @@ -80,6 +80,30 @@ do { \ type, fmt, ##__VA_ARGS__); \ } while (0) +#define cifs_server_dbg_func(ratefunc, type, fmt, ...) \ +do { \ + if ((type) & FYI && cifsFYI & CIFS_INFO) { \ + pr_debug_ ## ratefunc("%s: Server:%s " fmt, \ + __FILE__, server->hostname, ##__VA_ARGS__);\ + } else if ((type) & VFS) { \ + pr_err_ ## ratefunc("CIFS VFS: Server:%s " fmt, \ + server->hostname, ##__VA_ARGS__); \ + } else if ((type) & NOISY && (NOISY != 0)) { \ + pr_debug_ ## ratefunc("Server:%s " fmt, \ + server->hostname, ##__VA_ARGS__); \ + } \ +} while (0) + +#define cifs_server_dbg(type, fmt, ...) \ +do { \ + if ((type) & ONCE) \ + cifs_server_dbg_func(once, \ + type, fmt, ##__VA_ARGS__); \ + else \ + cifs_server_dbg_func(ratelimited, \ + type, fmt, ##__VA_ARGS__); \ +} while (0) + /* * debug OFF * --------- @@ -91,6 +115,13 @@ do { \ pr_debug(fmt, ##__VA_ARGS__); \ } while (0) +#define cifs_server_dbg(type, fmt, ...) \ +do { \ + if (0) \ + pr_debug("Server:%s " fmt, \ + server->hostname, ##__VA_ARGS__); \ +} while (0) + #define cifs_info(fmt, ...) \ do { \ pr_info("CIFS: "fmt, ##__VA_ARGS__); \ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 4fe559821aff..1efbbfffddba 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -489,7 +489,7 @@ cifs_reconnect(struct TCP_Server_Info *server) } else { rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list, &tgt_it); if (rc && (rc != -EOPNOTSUPP)) { - cifs_dbg(VFS, "%s: no target servers for DFS failover\n", + cifs_server_dbg(VFS, "%s: no target servers for DFS failover\n", __func__); } else { server->nr_targets = dfs_cache_get_nr_tgts(&tgt_list); @@ -617,12 +617,12 @@ cifs_reconnect(struct TCP_Server_Info *server) rc = dfs_cache_noreq_update_tgthint(cifs_sb->origin_fullpath + 1, tgt_it); if (rc) { - cifs_dbg(VFS, "%s: failed to update DFS target hint: rc = %d\n", + cifs_server_dbg(VFS, "%s: failed to update DFS target hint: rc = %d\n", __func__, rc); } rc = dfs_cache_update_vol(cifs_sb->origin_fullpath, server); if (rc) { - cifs_dbg(VFS, "%s: failed to update vol info in DFS cache: rc = %d\n", + cifs_server_dbg(VFS, "%s: failed to update vol info in DFS cache: rc = %d\n", __func__, rc); } dfs_cache_free_tgts(&tgt_list); @@ -678,7 +678,7 @@ allocate_buffers(struct TCP_Server_Info *server) if (!server->bigbuf) { server->bigbuf = (char *)cifs_buf_get(); if (!server->bigbuf) { - cifs_dbg(VFS, "No memory for large SMB response\n"); + cifs_server_dbg(VFS, "No memory for large SMB response\n"); msleep(3000); /* retry will check if exiting */ return false; @@ -691,7 +691,7 @@ allocate_buffers(struct TCP_Server_Info *server) if (!server->smallbuf) { server->smallbuf = (char *)cifs_small_buf_get(); if (!server->smallbuf) { - cifs_dbg(VFS, "No memory for SMB response\n"); + cifs_server_dbg(VFS, "No memory for SMB response\n"); msleep(1000); /* retry will check if exiting */ return false; @@ -722,8 +722,8 @@ server_unresponsive(struct TCP_Server_Info *server) if ((server->tcpStatus == CifsGood || server->tcpStatus == CifsNeedNegotiate) && time_after(jiffies, server->lstrp + 3 * server->echo_interval)) { - cifs_dbg(VFS, "Server %s has not responded in %lu seconds. Reconnecting...\n", - server->hostname, (3 * server->echo_interval) / HZ); + cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n", + (3 * server->echo_interval) / HZ); cifs_reconnect(server); wake_up(&server->response_q); return true; @@ -861,7 +861,7 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type) wake_up(&server->response_q); break; default: - cifs_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type); + cifs_server_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type); cifs_reconnect(server); } @@ -1008,7 +1008,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) /* make sure this will fit in a large buffer */ if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) - server->vals->header_preamble_size) { - cifs_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length); + cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length); cifs_reconnect(server); wake_up(&server->response_q); return -ECONNABORTED; @@ -1149,7 +1149,7 @@ cifs_demultiplex_thread(void *p) /* make sure we have enough to get to the MID */ if (server->pdu_size < HEADER_SIZE(server) - 1 - server->vals->header_preamble_size) { - cifs_dbg(VFS, "SMB response too short (%u bytes)\n", + cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n", server->pdu_size); cifs_reconnect(server); wake_up(&server->response_q); @@ -1222,7 +1222,7 @@ cifs_demultiplex_thread(void *p) smb2_add_credits_from_hdr(bufs[i], server); cifs_dbg(FYI, "Received oplock break\n"); } else { - cifs_dbg(VFS, "No task to wake, unknown frame " + cifs_server_dbg(VFS, "No task to wake, unknown frame " "received! NumMids %d\n", atomic_read(&midCount)); cifs_dump_mem("Received Data is: ", bufs[i], @@ -2840,16 +2840,17 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb_vol *volume_info) struct nls_table *nls_codepage; char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0}; bool seal = false; + struct TCP_Server_Info *server = ses->server; /* * If the mount request that resulted in the creation of the * session requires encryption, force IPC to be encrypted too. */ if (volume_info->seal) { - if (ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) + if (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) seal = true; else { - cifs_dbg(VFS, + cifs_server_dbg(VFS, "IPC: server doesn't support encryption\n"); return -EOPNOTSUPP; } @@ -2859,7 +2860,7 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb_vol *volume_info) if (tcon == NULL) return -ENOMEM; - scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", ses->server->hostname); + scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname); /* cannot fail */ nls_codepage = load_nls_default(); @@ -2868,11 +2869,11 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb_vol *volume_info) tcon->ses = ses; tcon->ipc = true; tcon->seal = seal; - rc = ses->server->ops->tree_connect(xid, ses, unc, tcon, nls_codepage); + rc = server->ops->tree_connect(xid, ses, unc, tcon, nls_codepage); free_xid(xid); if (rc) { - cifs_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc); + cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc); tconInfoFree(tcon); goto out; } @@ -2958,7 +2959,7 @@ void cifs_put_smb_ses(struct cifs_ses *ses) xid = get_xid(); rc = server->ops->logoff(xid, ses); if (rc) - cifs_dbg(VFS, "%s: Session Logoff failure rc=%d\n", + cifs_server_dbg(VFS, "%s: Session Logoff failure rc=%d\n", __func__, rc); _free_xid(xid); } @@ -3659,10 +3660,10 @@ bind_socket(struct TCP_Server_Info *server) saddr4 = (struct sockaddr_in *)&server->srcaddr; saddr6 = (struct sockaddr_in6 *)&server->srcaddr; if (saddr6->sin6_family == AF_INET6) - cifs_dbg(VFS, "Failed to bind to: %pI6c, error: %d\n", + cifs_server_dbg(VFS, "Failed to bind to: %pI6c, error: %d\n", &saddr6->sin6_addr, rc); else - cifs_dbg(VFS, "Failed to bind to: %pI4, error: %d\n", + cifs_server_dbg(VFS, "Failed to bind to: %pI4, error: %d\n", &saddr4->sin_addr.s_addr, rc); } } @@ -3766,7 +3767,7 @@ generic_ip_connect(struct TCP_Server_Info *server) rc = __sock_create(cifs_net_ns(server), sfamily, SOCK_STREAM, IPPROTO_TCP, &socket, 1); if (rc < 0) { - cifs_dbg(VFS, "Error %d creating socket\n", rc); + cifs_server_dbg(VFS, "Error %d creating socket\n", rc); server->ssocket = NULL; return rc; } @@ -4150,7 +4151,7 @@ static int mount_get_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb, if ((vol->persistent == true) && (!(ses->server->capabilities & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES))) { - cifs_dbg(VFS, "persistent handles not supported by server\n"); + cifs_server_dbg(VFS, "persistent handles not supported by server\n"); return -EOPNOTSUPP; } @@ -4588,7 +4589,7 @@ static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb_vol *vol, rc = cifs_are_all_path_components_accessible(server, xid, tcon, cifs_sb, full_path, tcon->Flags & SMB_SHARE_IS_IN_DFS); if (rc != 0) { - cifs_dbg(VFS, "cannot query dirs between root and final path, " + cifs_server_dbg(VFS, "cannot query dirs between root and final path, " "enabling CIFS_MOUNT_USE_PREFIX_PATH\n"); cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; rc = 0; @@ -5090,7 +5091,7 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, rc = server->ops->sess_setup(xid, ses, nls_info); if (rc) - cifs_dbg(VFS, "Send error in SessSetup = %d\n", rc); + cifs_server_dbg(VFS, "Send error in SessSetup = %d\n", rc); return rc; } diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index a33cb91b4963..dbc6ef50dd45 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -542,7 +542,7 @@ assemble_neg_contexts(struct smb2_negotiate_req *req, if (*total_len > 200) { /* In case length corrupted don't want to overrun smb buffer */ - cifs_dbg(VFS, "Bad frame length assembling neg contexts\n"); + cifs_server_dbg(VFS, "Bad frame length assembling neg contexts\n"); return; } @@ -660,7 +660,7 @@ static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp, cifs_dbg(FYI, "decoding %d negotiate contexts\n", ctxt_cnt); if (len_of_smb <= offset) { - cifs_dbg(VFS, "Invalid response: negotiate context offset\n"); + cifs_server_dbg(VFS, "Invalid response: negotiate context offset\n"); return -EINVAL; } @@ -692,7 +692,7 @@ static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp, else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) server->posix_ext_supported = true; else - cifs_dbg(VFS, "unknown negcontext of type %d ignored\n", + cifs_server_dbg(VFS, "unknown negcontext of type %d ignored\n", le16_to_cpu(pctx->ContextType)); if (rc) @@ -817,7 +817,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); req->DialectCount = cpu_to_le16(2); total_len += 4; - } else if (strcmp(ses->server->vals->version_string, + } else if (strcmp(server->vals->version_string, SMBDEFAULT_VERSION_STRING) == 0) { req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); @@ -840,16 +840,16 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) else req->SecurityMode = 0; - req->Capabilities = cpu_to_le32(ses->server->vals->req_capabilities); + req->Capabilities = cpu_to_le32(server->vals->req_capabilities); /* ClientGUID must be zero for SMB2.02 dialect */ - if (ses->server->vals->protocol_id == SMB20_PROT_ID) + if (server->vals->protocol_id == SMB20_PROT_ID) memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE); else { memcpy(req->ClientGUID, server->client_guid, SMB2_CLIENT_GUID_SIZE); - if ((ses->server->vals->protocol_id == SMB311_PROT_ID) || - (strcmp(ses->server->vals->version_string, + if ((server->vals->protocol_id == SMB311_PROT_ID) || + (strcmp(server->vals->version_string, SMBDEFAULT_VERSION_STRING) == 0)) assemble_neg_contexts(req, server, &total_len); } @@ -868,42 +868,42 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); */ if (rc == -EOPNOTSUPP) { - cifs_dbg(VFS, "Dialect not supported by server. Consider " + cifs_server_dbg(VFS, "Dialect not supported by server. Consider " "specifying vers=1.0 or vers=2.0 on mount for accessing" " older servers\n"); goto neg_exit; } else if (rc != 0) goto neg_exit; - if (strcmp(ses->server->vals->version_string, + if (strcmp(server->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { - cifs_dbg(VFS, + cifs_server_dbg(VFS, "SMB2 dialect returned but not requested\n"); return -EIO; } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { - cifs_dbg(VFS, + cifs_server_dbg(VFS, "SMB2.1 dialect returned but not requested\n"); return -EIO; } - } else if (strcmp(ses->server->vals->version_string, + } else if (strcmp(server->vals->version_string, SMBDEFAULT_VERSION_STRING) == 0) { if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { - cifs_dbg(VFS, + cifs_server_dbg(VFS, "SMB2 dialect returned but not requested\n"); return -EIO; } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { /* ops set to 3.0 by default for default so update */ - ses->server->ops = &smb21_operations; - ses->server->vals = &smb21_values; + server->ops = &smb21_operations; + server->vals = &smb21_values; } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { - ses->server->ops = &smb311_operations; - ses->server->vals = &smb311_values; + server->ops = &smb311_operations; + server->vals = &smb311_values; } } else if (le16_to_cpu(rsp->DialectRevision) != - ses->server->vals->protocol_id) { + server->vals->protocol_id) { /* if requested single dialect ensure returned dialect matched */ - cifs_dbg(VFS, "Illegal 0x%x dialect returned: not requested\n", + cifs_server_dbg(VFS, "Illegal 0x%x dialect returned: not requested\n", le16_to_cpu(rsp->DialectRevision)); return -EIO; } @@ -921,7 +921,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n"); else { - cifs_dbg(VFS, "Illegal dialect returned by server 0x%x\n", + cifs_server_dbg(VFS, "Illegal dialect returned by server 0x%x\n", le16_to_cpu(rsp->DialectRevision)); rc = -EIO; goto neg_exit; @@ -981,7 +981,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) rc = smb311_decode_neg_context(rsp, server, rsp_iov.iov_len); else - cifs_dbg(VFS, "Missing expected negotiate contexts\n"); + cifs_server_dbg(VFS, "Missing expected negotiate contexts\n"); } neg_exit: free_rsp_buf(resp_buftype, rsp); @@ -995,11 +995,12 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) struct validate_negotiate_info_rsp *pneg_rsp = NULL; u32 rsplen; u32 inbuflen; /* max of 4 dialects */ + struct TCP_Server_Info *server = tcon->ses->server; cifs_dbg(FYI, "validate negotiate\n"); /* In SMB3.11 preauth integrity supersedes validate negotiate */ - if (tcon->ses->server->dialect == SMB311_PROT_ID) + if (server->dialect == SMB311_PROT_ID) return 0; /* @@ -1018,15 +1019,15 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) } if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL) - cifs_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n"); + cifs_server_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n"); pneg_inbuf = kmalloc(sizeof(*pneg_inbuf), GFP_NOFS); if (!pneg_inbuf) return -ENOMEM; pneg_inbuf->Capabilities = - cpu_to_le32(tcon->ses->server->vals->req_capabilities); - memcpy(pneg_inbuf->Guid, tcon->ses->server->client_guid, + cpu_to_le32(server->vals->req_capabilities); + memcpy(pneg_inbuf->Guid, server->client_guid, SMB2_CLIENT_GUID_SIZE); if (tcon->ses->sign) @@ -1039,7 +1040,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) pneg_inbuf->SecurityMode = 0; - if (strcmp(tcon->ses->server->vals->version_string, + if (strcmp(server->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); @@ -1047,7 +1048,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) /* structure is big enough for 3 dialects, sending only 2 */ inbuflen = sizeof(*pneg_inbuf) - (2 * sizeof(pneg_inbuf->Dialects[0])); - } else if (strcmp(tcon->ses->server->vals->version_string, + } else if (strcmp(server->vals->version_string, SMBDEFAULT_VERSION_STRING) == 0) { pneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); pneg_inbuf->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); @@ -1059,7 +1060,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) } else { /* otherwise specific dialect was requested */ pneg_inbuf->Dialects[0] = - cpu_to_le16(tcon->ses->server->vals->protocol_id); + cpu_to_le16(server->vals->protocol_id); pneg_inbuf->DialectCount = cpu_to_le16(1); /* structure is big enough for 3 dialects, sending only 1 */ inbuflen = sizeof(*pneg_inbuf) - @@ -1075,18 +1076,18 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) * Old Windows versions or Netapp SMB server can return * not supported error. Client should accept it. */ - cifs_dbg(VFS, "Server does not support validate negotiate\n"); + cifs_server_dbg(VFS, "Server does not support validate negotiate\n"); rc = 0; goto out_free_inbuf; } else if (rc != 0) { - cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc); + cifs_server_dbg(VFS, "validate protocol negotiate failed: %d\n", rc); rc = -EIO; goto out_free_inbuf; } rc = -EIO; if (rsplen != sizeof(*pneg_rsp)) { - cifs_dbg(VFS, "invalid protocol negotiate response size: %d\n", + cifs_server_dbg(VFS, "invalid protocol negotiate response size: %d\n", rsplen); /* relax check since Mac returns max bufsize allowed on ioctl */ @@ -1095,16 +1096,16 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) } /* check validate negotiate info response matches what we got earlier */ - if (pneg_rsp->Dialect != cpu_to_le16(tcon->ses->server->dialect)) + if (pneg_rsp->Dialect != cpu_to_le16(server->dialect)) goto vneg_out; - if (pneg_rsp->SecurityMode != cpu_to_le16(tcon->ses->server->sec_mode)) + if (pneg_rsp->SecurityMode != cpu_to_le16(server->sec_mode)) goto vneg_out; /* do not validate server guid because not saved at negprot time yet */ if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND | - SMB2_LARGE_FILES) != tcon->ses->server->capabilities) + SMB2_LARGE_FILES) != server->capabilities) goto vneg_out; /* validate negotiate successful */ @@ -1113,7 +1114,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) goto out_free_rsp; vneg_out: - cifs_dbg(VFS, "protocol revalidation - security settings mismatch\n"); + cifs_server_dbg(VFS, "protocol revalidation - security settings mismatch\n"); out_free_rsp: kfree(pneg_rsp); out_free_inbuf: @@ -1567,7 +1568,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, sess_data->func(sess_data); if ((ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) && (ses->sign)) - cifs_dbg(VFS, "signing requested but authenticated as guest\n"); + cifs_server_dbg(VFS, "signing requested but authenticated as guest\n"); rc = sess_data->result; out: kfree(sess_data); @@ -1660,10 +1661,11 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, __le16 *unc_path = NULL; int flags = 0; unsigned int total_len; + struct TCP_Server_Info *server = ses->server; cifs_dbg(FYI, "TCON\n"); - if (!(ses->server) || !tree) + if (!server || !tree) return -EIO; unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL); @@ -1706,7 +1708,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, * unless it is guest or anonymous user. See MS-SMB2 3.2.5.3.1 * (Samba servers don't always set the flag so also check if null user) */ - if ((ses->server->dialect == SMB311_PROT_ID) && + if ((server->dialect == SMB311_PROT_ID) && !smb3_encryption_required(tcon) && !(ses->session_flags & (SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL)) && @@ -1745,7 +1747,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, cifs_dbg(FYI, "connection to printer\n"); break; default: - cifs_dbg(VFS, "unknown share type %d\n", rsp->ShareType); + cifs_server_dbg(VFS, "unknown share type %d\n", rsp->ShareType); rc = -EOPNOTSUPP; goto tcon_error_exit; } @@ -1760,15 +1762,15 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) - cifs_dbg(VFS, "DFS capability contradicts DFS flag\n"); + cifs_server_dbg(VFS, "DFS capability contradicts DFS flag\n"); if (tcon->seal && - !(tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) - cifs_dbg(VFS, "Encryption is requested but not supported\n"); + !(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) + cifs_server_dbg(VFS, "Encryption is requested but not supported\n"); init_copy_chunk_defaults(tcon); - if (tcon->ses->server->ops->validate_negotiate) - rc = tcon->ses->server->ops->validate_negotiate(xid, tcon); + if (server->ops->validate_negotiate) + rc = server->ops->validate_negotiate(xid, tcon); tcon_exit: free_rsp_buf(resp_buftype, rsp); @@ -1777,7 +1779,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, tcon_error_exit: if (rsp && rsp->sync_hdr.Status == STATUS_BAD_NETWORK_NAME) { - cifs_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); + cifs_server_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); } goto tcon_exit; } @@ -2741,6 +2743,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, int resp_buftype = CIFS_NO_BUFFER; int rc = 0; int flags = 0; + struct TCP_Server_Info *server; cifs_dbg(FYI, "SMB2 IOCTL\n"); @@ -2756,7 +2759,8 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, else return -EIO; - if (!ses || !(ses->server)) + server = ses->server; + if (!ses || !(server)) return -EIO; if (smb3_encryption_required(tcon)) @@ -2806,14 +2810,14 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, if (*plen == 0) goto ioctl_exit; /* server returned no data */ else if (*plen > rsp_iov.iov_len || *plen > 0xFF00) { - cifs_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); + cifs_server_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); *plen = 0; rc = -EIO; goto ioctl_exit; } if (rsp_iov.iov_len - *plen < le32_to_cpu(rsp->OutputOffset)) { - cifs_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, + cifs_server_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, le32_to_cpu(rsp->OutputOffset)); *plen = 0; rc = -EIO; @@ -3054,12 +3058,13 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, int rc = 0; int resp_buftype = CIFS_NO_BUFFER; struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = ses->server; int flags = 0; bool allocated = false; cifs_dbg(FYI, "Query Info\n"); - if (!ses || !(ses->server)) + if (!ses || !(server)) return -EIO; if (smb3_encryption_required(tcon)) @@ -3097,7 +3102,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, if (!*data) { *data = kmalloc(*dlen, GFP_KERNEL); if (!*data) { - cifs_dbg(VFS, + cifs_server_dbg(VFS, "Error %d allocating memory for acl\n", rc); *dlen = 0; @@ -3489,7 +3494,7 @@ smb2_readv_callback(struct mid_q_entry *mid) rc = smb2_verify_signature(&rqst, server); if (rc) - cifs_dbg(VFS, "SMB signature verification returned error = %d\n", + cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n", rc); } /* FIXME: should this be counted toward the initiating task? */ @@ -4079,7 +4084,7 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1; break; default: - cifs_dbg(VFS, "info level %u isn't supported\n", + cifs_server_dbg(VFS, "info level %u isn't supported\n", srch_inf->info_level); rc = -EINVAL; goto qdir_exit; @@ -4170,7 +4175,7 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, else if (resp_buftype == CIFS_SMALL_BUFFER) srch_inf->smallBuf = true; else - cifs_dbg(VFS, "illegal search buffer type\n"); + cifs_server_dbg(VFS, "illegal search buffer type\n"); trace_smb3_query_dir_done(xid, persistent_fid, tcon->tid, tcon->ses->Suid, index, srch_inf->entries_in_buffer); diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 1ccbcf9c2c3b..b02242eacb55 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -176,7 +176,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ses = smb2_find_smb_ses(server, shdr->SessionId); if (!ses) { - cifs_dbg(VFS, "%s: Could not find session\n", __func__); + cifs_server_dbg(VFS, "%s: Could not find session\n", __func__); return 0; } @@ -185,21 +185,21 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) rc = smb2_crypto_shash_allocate(server); if (rc) { - cifs_dbg(VFS, "%s: sha256 alloc failed\n", __func__); + cifs_server_dbg(VFS, "%s: sha256 alloc failed\n", __func__); return rc; } rc = crypto_shash_setkey(server->secmech.hmacsha256, ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); if (rc) { - cifs_dbg(VFS, "%s: Could not update with response\n", __func__); + cifs_server_dbg(VFS, "%s: Could not update with response\n", __func__); return rc; } shash = &server->secmech.sdeschmacsha256->shash; rc = crypto_shash_init(shash); if (rc) { - cifs_dbg(VFS, "%s: Could not init sha256", __func__); + cifs_server_dbg(VFS, "%s: Could not init sha256", __func__); return rc; } @@ -215,7 +215,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) rc = crypto_shash_update(shash, iov[0].iov_base, iov[0].iov_len); if (rc) { - cifs_dbg(VFS, "%s: Could not update with payload\n", + cifs_server_dbg(VFS, "%s: Could not update with payload\n", __func__); return rc; } @@ -239,68 +239,69 @@ static int generate_key(struct cifs_ses *ses, struct kvec label, int rc = 0; unsigned char prfhash[SMB2_HMACSHA256_SIZE]; unsigned char *hashptr = prfhash; + struct TCP_Server_Info *server = ses->server; memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); memset(key, 0x0, key_size); - rc = smb3_crypto_shash_allocate(ses->server); + rc = smb3_crypto_shash_allocate(server); if (rc) { - cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__); + cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__); goto smb3signkey_ret; } - rc = crypto_shash_setkey(ses->server->secmech.hmacsha256, + rc = crypto_shash_setkey(server->secmech.hmacsha256, ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); if (rc) { - cifs_dbg(VFS, "%s: Could not set with session key\n", __func__); + cifs_server_dbg(VFS, "%s: Could not set with session key\n", __func__); goto smb3signkey_ret; } - rc = crypto_shash_init(&ses->server->secmech.sdeschmacsha256->shash); + rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash); if (rc) { - cifs_dbg(VFS, "%s: Could not init sign hmac\n", __func__); + cifs_server_dbg(VFS, "%s: Could not init sign hmac\n", __func__); goto smb3signkey_ret; } - rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, + rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, i, 4); if (rc) { - cifs_dbg(VFS, "%s: Could not update with n\n", __func__); + cifs_server_dbg(VFS, "%s: Could not update with n\n", __func__); goto smb3signkey_ret; } - rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, + rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, label.iov_base, label.iov_len); if (rc) { - cifs_dbg(VFS, "%s: Could not update with label\n", __func__); + cifs_server_dbg(VFS, "%s: Could not update with label\n", __func__); goto smb3signkey_ret; } - rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, + rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, &zero, 1); if (rc) { - cifs_dbg(VFS, "%s: Could not update with zero\n", __func__); + cifs_server_dbg(VFS, "%s: Could not update with zero\n", __func__); goto smb3signkey_ret; } - rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, + rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, context.iov_base, context.iov_len); if (rc) { - cifs_dbg(VFS, "%s: Could not update with context\n", __func__); + cifs_server_dbg(VFS, "%s: Could not update with context\n", __func__); goto smb3signkey_ret; } - rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, + rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, L, 4); if (rc) { - cifs_dbg(VFS, "%s: Could not update with L\n", __func__); + cifs_server_dbg(VFS, "%s: Could not update with L\n", __func__); goto smb3signkey_ret; } - rc = crypto_shash_final(&ses->server->secmech.sdeschmacsha256->shash, + rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash, hashptr); if (rc) { - cifs_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__); + cifs_server_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__); goto smb3signkey_ret; } @@ -436,7 +437,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ses = smb2_find_smb_ses(server, shdr->SessionId); if (!ses) { - cifs_dbg(VFS, "%s: Could not find session\n", __func__); + cifs_server_dbg(VFS, "%s: Could not find session\n", __func__); return 0; } @@ -446,7 +447,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) rc = crypto_shash_setkey(server->secmech.cmacaes, ses->smb3signingkey, SMB2_CMACAES_SIZE); if (rc) { - cifs_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); + cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); return rc; } @@ -457,7 +458,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) */ rc = crypto_shash_init(shash); if (rc) { - cifs_dbg(VFS, "%s: Could not init cmac aes\n", __func__); + cifs_server_dbg(VFS, "%s: Could not init cmac aes\n", __func__); return rc; } @@ -473,7 +474,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) rc = crypto_shash_update(shash, iov[0].iov_base, iov[0].iov_len); if (rc) { - cifs_dbg(VFS, "%s: Could not update with payload\n", + cifs_server_dbg(VFS, "%s: Could not update with payload\n", __func__); return rc; } @@ -665,7 +666,7 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, rc = smb2_verify_signature(&rqst, server); if (rc) - cifs_dbg(VFS, "SMB signature verification returned error = %d\n", + cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n", rc); } @@ -739,7 +740,7 @@ smb3_crypto_aead_allocate(struct TCP_Server_Info *server) else tfm = crypto_alloc_aead("ccm(aes)", 0, 0); if (IS_ERR(tfm)) { - cifs_dbg(VFS, "%s: Failed to alloc encrypt aead\n", + cifs_server_dbg(VFS, "%s: Failed to alloc encrypt aead\n", __func__); return PTR_ERR(tfm); } @@ -754,7 +755,7 @@ smb3_crypto_aead_allocate(struct TCP_Server_Info *server) if (IS_ERR(tfm)) { crypto_free_aead(server->secmech.ccmaesencrypt); server->secmech.ccmaesencrypt = NULL; - cifs_dbg(VFS, "%s: Failed to alloc decrypt aead\n", + cifs_server_dbg(VFS, "%s: Failed to alloc decrypt aead\n", __func__); return PTR_ERR(tfm); } diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 5d6d44bfe10a..0d60bd2f4dca 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -118,7 +118,7 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) #ifdef CONFIG_CIFS_STATS2 now = jiffies; if (now < midEntry->when_alloc) - cifs_dbg(VFS, "invalid mid allocation time\n"); + cifs_server_dbg(VFS, "invalid mid allocation time\n"); roundtrip_time = now - midEntry->when_alloc; if (smb_cmd < NUMBER_OF_SMB2_COMMANDS) { @@ -232,7 +232,7 @@ smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg, retries++; if (retries >= 14 || (!server->noblocksnd && (retries > 2))) { - cifs_dbg(VFS, "sends on sock %p stuck for 15 seconds\n", + cifs_server_dbg(VFS, "sends on sock %p stuck for 15 seconds\n", ssocket); return -EAGAIN; } @@ -246,7 +246,7 @@ smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg, if (rc == 0) { /* should never happen, letting socket clear before retrying is our only obvious option here */ - cifs_dbg(VFS, "tcp sent no data\n"); + cifs_server_dbg(VFS, "tcp sent no data\n"); msleep(500); continue; } @@ -440,7 +440,7 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, } smbd_done: if (rc < 0 && rc != -EINTR) - cifs_dbg(VFS, "Error %d sending data on socket to server\n", + cifs_server_dbg(VFS, "Error %d sending data on socket to server\n", rc); else if (rc > 0) rc = 0; @@ -473,8 +473,8 @@ smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, cur_rqst[0].rq_nvec = 1; if (!server->ops->init_transform_rq) { - cifs_dbg(VFS, "Encryption requested but transform callback " - "is missing\n"); + cifs_server_dbg(VFS, "Encryption requested but transform " + "callback is missing\n"); return -EIO; } @@ -548,7 +548,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits, if (!rc) { trace_smb3_credit_timeout(server->CurrentMid, server->hostname, num_credits); - cifs_dbg(VFS, "wait timed out after %d ms\n", + cifs_server_dbg(VFS, "wait timed out after %d ms\n", timeout); return -ENOTSUPP; } @@ -589,7 +589,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits, trace_smb3_credit_timeout( server->CurrentMid, server->hostname, num_credits); - cifs_dbg(VFS, "wait timed out after %d ms\n", + cifs_server_dbg(VFS, "wait timed out after %d ms\n", timeout); return -ENOTSUPP; } @@ -869,7 +869,7 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) break; default: list_del_init(&mid->qhead); - cifs_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n", + cifs_server_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n", __func__, mid->mid, mid->mid_state); rc = -EIO; } @@ -910,7 +910,7 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, rc = cifs_verify_signature(&rqst, server, mid->sequence_number); if (rc) - cifs_dbg(VFS, "SMB signature verification returned error = %d\n", + cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n", rc); } @@ -1107,7 +1107,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, } if (rc != 0) { for (; i < num_rqst; i++) { - cifs_dbg(VFS, "Cancelling wait for mid %llu cmd: %d\n", + cifs_server_dbg(VFS, "Cancelling wait for mid %llu cmd: %d\n", midQ[i]->mid, le16_to_cpu(midQ[i]->command)); send_cancel(server, &rqst[i], midQ[i]); spin_lock(&GlobalMid_Lock); @@ -1242,17 +1242,18 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, struct kvec iov = { .iov_base = in_buf, .iov_len = len }; struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 }; struct cifs_credits credits = { .value = 1, .instance = 0 }; + struct TCP_Server_Info *server = ses->server; if (ses == NULL) { cifs_dbg(VFS, "Null smb session\n"); return -EIO; } - if (ses->server == NULL) { + if (server == NULL) { cifs_dbg(VFS, "Null tcp session\n"); return -EIO; } - if (ses->server->tcpStatus == CifsExiting) + if (server->tcpStatus == CifsExiting) return -ENOENT; /* Ensure that we do not send more than 50 overlapping requests @@ -1260,12 +1261,12 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, use ses->maxReq */ if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { - cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n", + cifs_server_dbg(VFS, "Illegal length, greater than maximum frame, %d\n", len); return -EIO; } - rc = wait_for_free_request(ses->server, flags, &credits.instance); + rc = wait_for_free_request(server, flags, &credits.instance); if (rc) return rc; @@ -1273,70 +1274,70 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, and avoid races inside tcp sendmsg code that could cause corruption of smb data */ - mutex_lock(&ses->server->srv_mutex); + mutex_lock(&server->srv_mutex); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { mutex_unlock(&ses->server->srv_mutex); /* Update # of requests on wire to server */ - add_credits(ses->server, &credits, 0); + add_credits(server, &credits, 0); return rc; } - rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); + rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number); if (rc) { - mutex_unlock(&ses->server->srv_mutex); + mutex_unlock(&server->srv_mutex); goto out; } midQ->mid_state = MID_REQUEST_SUBMITTED; - cifs_in_send_inc(ses->server); - rc = smb_send(ses->server, in_buf, len); - cifs_in_send_dec(ses->server); + cifs_in_send_inc(server); + rc = smb_send(server, in_buf, len); + cifs_in_send_dec(server); cifs_save_when_sent(midQ); if (rc < 0) - ses->server->sequence_number -= 2; + server->sequence_number -= 2; - mutex_unlock(&ses->server->srv_mutex); + mutex_unlock(&server->srv_mutex); if (rc < 0) goto out; - rc = wait_for_response(ses->server, midQ); + rc = wait_for_response(server, midQ); if (rc != 0) { - send_cancel(ses->server, &rqst, midQ); + send_cancel(server, &rqst, midQ); spin_lock(&GlobalMid_Lock); if (midQ->mid_state == MID_REQUEST_SUBMITTED) { /* no longer considered to be "in-flight" */ midQ->callback = DeleteMidQEntry; spin_unlock(&GlobalMid_Lock); - add_credits(ses->server, &credits, 0); + add_credits(server, &credits, 0); return rc; } spin_unlock(&GlobalMid_Lock); } - rc = cifs_sync_mid_result(midQ, ses->server); + rc = cifs_sync_mid_result(midQ, server); if (rc != 0) { - add_credits(ses->server, &credits, 0); + add_credits(server, &credits, 0); return rc; } if (!midQ->resp_buf || !out_buf || midQ->mid_state != MID_RESPONSE_RECEIVED) { rc = -EIO; - cifs_dbg(VFS, "Bad MID state?\n"); + cifs_server_dbg(VFS, "Bad MID state?\n"); goto out; } *pbytes_returned = get_rfc1002_length(midQ->resp_buf); memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); - rc = cifs_check_receive(midQ, ses->server, 0); + rc = cifs_check_receive(midQ, server, 0); out: cifs_delete_mid(midQ); - add_credits(ses->server, &credits, 0); + add_credits(server, &credits, 0); return rc; } @@ -1379,19 +1380,21 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, struct kvec iov = { .iov_base = in_buf, .iov_len = len }; struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 }; unsigned int instance; + struct TCP_Server_Info *server; if (tcon == NULL || tcon->ses == NULL) { cifs_dbg(VFS, "Null smb session\n"); return -EIO; } ses = tcon->ses; + server = ses->server; - if (ses->server == NULL) { + if (server == NULL) { cifs_dbg(VFS, "Null tcp session\n"); return -EIO; } - if (ses->server->tcpStatus == CifsExiting) + if (server->tcpStatus == CifsExiting) return -ENOENT; /* Ensure that we do not send more than 50 overlapping requests @@ -1399,12 +1402,12 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, use ses->maxReq */ if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { - cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n", + cifs_server_dbg(VFS, "Illegal length, greater than maximum frame, %d\n", len); return -EIO; } - rc = wait_for_free_request(ses->server, CIFS_BLOCKING_OP, &instance); + rc = wait_for_free_request(server, CIFS_BLOCKING_OP, &instance); if (rc) return rc; @@ -1412,31 +1415,31 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, and avoid races inside tcp sendmsg code that could cause corruption of smb data */ - mutex_lock(&ses->server->srv_mutex); + mutex_lock(&server->srv_mutex); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { - mutex_unlock(&ses->server->srv_mutex); + mutex_unlock(&server->srv_mutex); return rc; } - rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); + rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number); if (rc) { cifs_delete_mid(midQ); - mutex_unlock(&ses->server->srv_mutex); + mutex_unlock(&server->srv_mutex); return rc; } midQ->mid_state = MID_REQUEST_SUBMITTED; - cifs_in_send_inc(ses->server); - rc = smb_send(ses->server, in_buf, len); - cifs_in_send_dec(ses->server); + cifs_in_send_inc(server); + rc = smb_send(server, in_buf, len); + cifs_in_send_dec(server); cifs_save_when_sent(midQ); if (rc < 0) - ses->server->sequence_number -= 2; + server->sequence_number -= 2; - mutex_unlock(&ses->server->srv_mutex); + mutex_unlock(&server->srv_mutex); if (rc < 0) { cifs_delete_mid(midQ); @@ -1444,21 +1447,21 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, } /* Wait for a reply - allow signals to interrupt. */ - rc = wait_event_interruptible(ses->server->response_q, + rc = wait_event_interruptible(server->response_q, (!(midQ->mid_state == MID_REQUEST_SUBMITTED)) || - ((ses->server->tcpStatus != CifsGood) && - (ses->server->tcpStatus != CifsNew))); + ((server->tcpStatus != CifsGood) && + (server->tcpStatus != CifsNew))); /* Were we interrupted by a signal ? */ if ((rc == -ERESTARTSYS) && (midQ->mid_state == MID_REQUEST_SUBMITTED) && - ((ses->server->tcpStatus == CifsGood) || - (ses->server->tcpStatus == CifsNew))) { + ((server->tcpStatus == CifsGood) || + (server->tcpStatus == CifsNew))) { if (in_buf->Command == SMB_COM_TRANSACTION2) { /* POSIX lock. We send a NT_CANCEL SMB to cause the blocking lock to return. */ - rc = send_cancel(ses->server, &rqst, midQ); + rc = send_cancel(server, &rqst, midQ); if (rc) { cifs_delete_mid(midQ); return rc; @@ -1477,9 +1480,9 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, } } - rc = wait_for_response(ses->server, midQ); + rc = wait_for_response(server, midQ); if (rc) { - send_cancel(ses->server, &rqst, midQ); + send_cancel(server, &rqst, midQ); spin_lock(&GlobalMid_Lock); if (midQ->mid_state == MID_REQUEST_SUBMITTED) { /* no longer considered to be "in-flight" */ @@ -1494,20 +1497,20 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, rstart = 1; } - rc = cifs_sync_mid_result(midQ, ses->server); + rc = cifs_sync_mid_result(midQ, server); if (rc != 0) return rc; /* rcvd frame is ok */ if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) { rc = -EIO; - cifs_dbg(VFS, "Bad MID state?\n"); + cifs_server_dbg(VFS, "Bad MID state?\n"); goto out; } *pbytes_returned = get_rfc1002_length(midQ->resp_buf); memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); - rc = cifs_check_receive(midQ, ses->server, 0); + rc = cifs_check_receive(midQ, server, 0); out: cifs_delete_mid(midQ); if (rstart && rc == -EACCES) From ac6ad7a8c9f6f1fd43262b2273a45ec1fdd3a981 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 2 Sep 2019 16:10:59 +0100 Subject: [PATCH 15/38] cifs: fix dereference on ses before it is null checked The assignment of pointer server dereferences pointer ses, however, this dereference occurs before ses is null checked and hence we have a potential null pointer dereference. Fix this by only dereferencing ses after it has been null checked. Addresses-Coverity: ("Dereference before null check") Fixes: 2808c6639104 ("cifs: add new debugging macro cifs_server_dbg") Signed-off-by: Colin Ian King Signed-off-by: Steve French --- fs/cifs/smb2pdu.c | 11 ++++++++--- fs/cifs/transport.c | 3 ++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index dbc6ef50dd45..0e92983de0b7 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2759,8 +2759,10 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, else return -EIO; + if (!ses) + return -EIO; server = ses->server; - if (!ses || !(server)) + if (!server) return -EIO; if (smb3_encryption_required(tcon)) @@ -3058,13 +3060,16 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, int rc = 0; int resp_buftype = CIFS_NO_BUFFER; struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = ses->server; + struct TCP_Server_Info *server; int flags = 0; bool allocated = false; cifs_dbg(FYI, "Query Info\n"); - if (!ses || !(server)) + if (!ses) + return -EIO; + server = ses->server; + if (!server) return -EIO; if (smb3_encryption_required(tcon)) diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 0d60bd2f4dca..a90bd4d75b4d 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -1242,12 +1242,13 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, struct kvec iov = { .iov_base = in_buf, .iov_len = len }; struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 }; struct cifs_credits credits = { .value = 1, .instance = 0 }; - struct TCP_Server_Info *server = ses->server; + struct TCP_Server_Info *server; if (ses == NULL) { cifs_dbg(VFS, "Null smb session\n"); return -EIO; } + server = ses->server; if (server == NULL) { cifs_dbg(VFS, "Null tcp session\n"); return -EIO; From 83bbfa706dda668deb60e96df20327fc79e1716f Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 27 Aug 2019 23:58:54 -0500 Subject: [PATCH 16/38] smb3: add mount option to allow forced caching of read only share If a share is immutable (at least for the period that it will be mounted) it would be helpful to not have to revalidate dentries repeatedly that we know can not be changed remotely. Add "cache=" option (cache=ro) for mounting read only shares in order to improve performance in cases in which we know that the share will not be changing while it is in use. Signed-off-by: Steve French --- fs/cifs/cifs_fs_sb.h | 1 + fs/cifs/cifsfs.c | 2 ++ fs/cifs/cifsglob.h | 6 ++++-- fs/cifs/connect.c | 14 ++++++++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index b326d2ca3765..286a104c4761 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -53,6 +53,7 @@ #define CIFS_MOUNT_NO_HANDLE_CACHE 0x4000000 /* disable caching dir handles */ #define CIFS_MOUNT_NO_DFS 0x8000000 /* disable DFS resolving */ #define CIFS_MOUNT_MODE_FROM_SID 0x10000000 /* retrieve mode from special ACE */ +#define CIFS_MOUNT_RO_CACHE 0x20000000 /* assumes share will not change */ struct cifs_sb_info { struct rb_root tlink_tree; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 3289b566463f..970251bc0661 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -400,6 +400,8 @@ cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb) seq_puts(s, "strict"); else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) seq_puts(s, "none"); + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) + seq_puts(s, "ro"); /* read only caching assumed */ else seq_puts(s, "loose"); } diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index fe610e7e3670..3f12da7f2f7f 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -559,6 +559,7 @@ struct smb_vol { bool server_ino:1; /* use inode numbers from server ie UniqueId */ bool direct_io:1; bool strict_io:1; /* strict cache behavior */ + bool cache_ro:1; bool remap:1; /* set to remap seven reserved chars in filenames */ bool sfu_remap:1; /* remap seven reserved chars ala SFU */ bool posix_paths:1; /* unset to not ask for posix pathnames. */ @@ -620,7 +621,8 @@ struct smb_vol { CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO | \ CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID | \ CIFS_MOUNT_UID_FROM_ACL | CIFS_MOUNT_NO_HANDLE_CACHE | \ - CIFS_MOUNT_NO_DFS | CIFS_MOUNT_MODE_FROM_SID) + CIFS_MOUNT_NO_DFS | CIFS_MOUNT_MODE_FROM_SID | \ + CIFS_MOUNT_RO_CACHE) /** * Generic VFS superblock mount flags (s_flags) to consider when @@ -1366,7 +1368,7 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file); #define CIFS_CACHE_RW_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG) #define CIFS_CACHE_RHW_FLG (CIFS_CACHE_RW_FLG | CIFS_CACHE_HANDLE_FLG) -#define CIFS_CACHE_READ(cinode) (cinode->oplock & CIFS_CACHE_READ_FLG) +#define CIFS_CACHE_READ(cinode) ((cinode->oplock & CIFS_CACHE_READ_FLG) || (CIFS_SB(cinode->vfs_inode.i_sb)->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE)) #define CIFS_CACHE_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG) #define CIFS_CACHE_WRITE(cinode) (cinode->oplock & CIFS_CACHE_WRITE_FLG) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 1efbbfffddba..4e622953dd5a 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -298,6 +298,7 @@ enum { Opt_cache_loose, Opt_cache_strict, Opt_cache_none, + Opt_cache_ro, Opt_cache_err }; @@ -305,6 +306,7 @@ static const match_table_t cifs_cacheflavor_tokens = { { Opt_cache_loose, "loose" }, { Opt_cache_strict, "strict" }, { Opt_cache_none, "none" }, + { Opt_cache_ro, "ro" }, { Opt_cache_err, NULL } }; @@ -1418,14 +1420,22 @@ cifs_parse_cache_flavor(char *value, struct smb_vol *vol) case Opt_cache_loose: vol->direct_io = false; vol->strict_io = false; + vol->cache_ro = false; break; case Opt_cache_strict: vol->direct_io = false; vol->strict_io = true; + vol->cache_ro = false; break; case Opt_cache_none: vol->direct_io = true; vol->strict_io = false; + vol->cache_ro = false; + break; + case Opt_cache_ro: + vol->direct_io = false; + vol->strict_io = false; + vol->cache_ro = true; break; default: cifs_dbg(VFS, "bad cache= option: %s\n", value); @@ -4041,6 +4051,10 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info, cifs_dbg(FYI, "mounting share using direct i/o\n"); cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; } + if (pvolume_info->cache_ro) { + cifs_dbg(VFS, "mounting share with read only caching. Ensure that the share will not be modified while in use.\n"); + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RO_CACHE; + } if (pvolume_info->mfsymlinks) { if (pvolume_info->sfu_emul) { /* From 1981ebaabd88a9b3e550b6163c570a498c32a1f5 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 29 Aug 2019 22:33:38 -0500 Subject: [PATCH 17/38] smb3: add some more descriptive messages about share when mounting cache=ro Add some additional logging so the user can see if the share they mounted with cache=ro is considered read only by the server CIFS: Attempting to mount //localhost/test CIFS VFS: mounting share with read only caching. Ensure that the share will not be modified while in use. CIFS VFS: read only mount of RW share CIFS: Attempting to mount //localhost/test-ro CIFS VFS: mounting share with read only caching. Ensure that the share will not be modified while in use. CIFS VFS: mounted to read only share Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/connect.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 4e622953dd5a..072c01f4e9c1 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -4197,8 +4197,16 @@ static int mount_get_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb, tcon->unix_ext = 0; /* server does not support them */ /* do not care if a following call succeed - informational */ - if (!tcon->pipe && server->ops->qfs_tcon) + if (!tcon->pipe && server->ops->qfs_tcon) { server->ops->qfs_tcon(*xid, tcon); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) { + if (tcon->fsDevInfo.DeviceCharacteristics & + FILE_READ_ONLY_DEVICE) + cifs_dbg(VFS, "mounted to read only share\n"); + else + cifs_dbg(VFS, "read only mount of RW share\n"); + } + } cifs_sb->wsize = server->ops->negotiate_wsize(tcon, vol); cifs_sb->rsize = server->ops->negotiate_rsize(tcon, vol); From 41e033fecdc891da629113c4f8ee80500b7656d6 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 30 Aug 2019 02:12:41 -0500 Subject: [PATCH 18/38] smb3: add mount option to allow RW caching of share accessed by only 1 client If a share is known to be only to be accessed by one client, we can aggressively cache writes not just reads to it. Add "cache=" option (cache=singleclient) for mounting read write shares (that will not be read or written to from other clients while we have it mounted) in order to improve performance. Signed-off-by: Steve French --- fs/cifs/cifs_fs_sb.h | 1 + fs/cifs/cifsfs.c | 2 ++ fs/cifs/cifsglob.h | 5 +++-- fs/cifs/connect.c | 20 +++++++++++++++++++- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 286a104c4761..6e7c4427369d 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -54,6 +54,7 @@ #define CIFS_MOUNT_NO_DFS 0x8000000 /* disable DFS resolving */ #define CIFS_MOUNT_MODE_FROM_SID 0x10000000 /* retrieve mode from special ACE */ #define CIFS_MOUNT_RO_CACHE 0x20000000 /* assumes share will not change */ +#define CIFS_MOUNT_RW_CACHE 0x40000000 /* assumes only client accessing */ struct cifs_sb_info { struct rb_root tlink_tree; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 970251bc0661..de90e665ef11 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -400,6 +400,8 @@ cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb) seq_puts(s, "strict"); else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) seq_puts(s, "none"); + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE) + seq_puts(s, "singleclient"); /* assume only one client access */ else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) seq_puts(s, "ro"); /* read only caching assumed */ else diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 3f12da7f2f7f..fa5abe3a8514 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -560,6 +560,7 @@ struct smb_vol { bool direct_io:1; bool strict_io:1; /* strict cache behavior */ bool cache_ro:1; + bool cache_rw:1; bool remap:1; /* set to remap seven reserved chars in filenames */ bool sfu_remap:1; /* remap seven reserved chars ala SFU */ bool posix_paths:1; /* unset to not ask for posix pathnames. */ @@ -622,7 +623,7 @@ struct smb_vol { CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID | \ CIFS_MOUNT_UID_FROM_ACL | CIFS_MOUNT_NO_HANDLE_CACHE | \ CIFS_MOUNT_NO_DFS | CIFS_MOUNT_MODE_FROM_SID | \ - CIFS_MOUNT_RO_CACHE) + CIFS_MOUNT_RO_CACHE | CIFS_MOUNT_RW_CACHE) /** * Generic VFS superblock mount flags (s_flags) to consider when @@ -1370,7 +1371,7 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file); #define CIFS_CACHE_READ(cinode) ((cinode->oplock & CIFS_CACHE_READ_FLG) || (CIFS_SB(cinode->vfs_inode.i_sb)->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE)) #define CIFS_CACHE_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG) -#define CIFS_CACHE_WRITE(cinode) (cinode->oplock & CIFS_CACHE_WRITE_FLG) +#define CIFS_CACHE_WRITE(cinode) ((cinode->oplock & CIFS_CACHE_WRITE_FLG) || (CIFS_SB(cinode->vfs_inode.i_sb)->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE)) /* * One of these for each file inode diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 072c01f4e9c1..d9a995588c74 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -299,6 +299,7 @@ enum { Opt_cache_strict, Opt_cache_none, Opt_cache_ro, + Opt_cache_rw, Opt_cache_err }; @@ -307,6 +308,7 @@ static const match_table_t cifs_cacheflavor_tokens = { { Opt_cache_strict, "strict" }, { Opt_cache_none, "none" }, { Opt_cache_ro, "ro" }, + { Opt_cache_rw, "singleclient" }, { Opt_cache_err, NULL } }; @@ -1421,21 +1423,31 @@ cifs_parse_cache_flavor(char *value, struct smb_vol *vol) vol->direct_io = false; vol->strict_io = false; vol->cache_ro = false; + vol->cache_rw = false; break; case Opt_cache_strict: vol->direct_io = false; vol->strict_io = true; vol->cache_ro = false; + vol->cache_rw = false; break; case Opt_cache_none: vol->direct_io = true; vol->strict_io = false; vol->cache_ro = false; + vol->cache_rw = false; break; case Opt_cache_ro: vol->direct_io = false; vol->strict_io = false; vol->cache_ro = true; + vol->cache_rw = false; + break; + case Opt_cache_rw: + vol->direct_io = false; + vol->strict_io = false; + vol->cache_ro = false; + vol->cache_rw = true; break; default: cifs_dbg(VFS, "bad cache= option: %s\n", value); @@ -4054,6 +4066,10 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info, if (pvolume_info->cache_ro) { cifs_dbg(VFS, "mounting share with read only caching. Ensure that the share will not be modified while in use.\n"); cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RO_CACHE; + } else if (pvolume_info->cache_rw) { + cifs_dbg(VFS, "mounting share in single client RW caching mode. Ensure that no other systems will be accessing the share.\n"); + cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_RO_CACHE | + CIFS_MOUNT_RW_CACHE); } if (pvolume_info->mfsymlinks) { if (pvolume_info->sfu_emul) { @@ -4203,8 +4219,10 @@ static int mount_get_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb, if (tcon->fsDevInfo.DeviceCharacteristics & FILE_READ_ONLY_DEVICE) cifs_dbg(VFS, "mounted to read only share\n"); - else + else if ((cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_RW_CACHE) == 0) cifs_dbg(VFS, "read only mount of RW share\n"); + /* no need to log a RW mount of a typical RW share */ } } From cae53f70f812c845fcedb577b0d40ec97ee4b920 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 3 Sep 2019 17:49:46 -0500 Subject: [PATCH 19/38] smb3: log warning if CSC policy conflicts with cache mount option If the server config (e.g. Samba smb.conf "csc policy = disable) for the share indicates that the share should not be cached, log a warning message if forced client side caching ("cache=ro" or "cache=singleclient") is requested on mount. Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/connect.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index d9a995588c74..85f8d943a05a 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3478,6 +3478,14 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) tcon->use_resilient = true; } + /* If the user really knows what they are doing they can override */ + if (tcon->share_flags & SMB2_SHAREFLAG_NO_CACHING) { + if (volume_info->cache_ro) + cifs_dbg(VFS, "cache=ro requested on mount but NO_CACHING flag set on share\n"); + else if (volume_info->cache_rw) + cifs_dbg(VFS, "cache=singleclient requested on mount but NO_CACHING flag set on share\n"); + } + /* * We can have only one retry value for a connection to a share so for * resources mounted more than once to the same server share the last From f90f979726bc37275995035dbbd55ce8f9b0acde Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 3 Sep 2019 18:35:42 -0500 Subject: [PATCH 20/38] smb3: add dynamic tracepoints for flush and close We only had dynamic tracepoints on errors in flush and close, but may be helpful to trace enter and non-error exits for those. Sample trace examples (excerpts) from "cp" and "dd" show two of the new tracepoints. cp-22823 [002] .... 123439.179701: smb3_enter: _cifsFileInfo_put: xid=10 cp-22823 [002] .... 123439.179705: smb3_close_enter: xid=10 sid=0x98871327 tid=0xfcd585ff fid=0xc7f84682 cp-22823 [002] .... 123439.179711: smb3_cmd_enter: sid=0x98871327 tid=0xfcd585ff cmd=6 mid=43 cp-22823 [002] .... 123439.180175: smb3_cmd_done: sid=0x98871327 tid=0xfcd585ff cmd=6 mid=43 cp-22823 [002] .... 123439.180179: smb3_close_done: xid=10 sid=0x98871327 tid=0xfcd585ff fid=0xc7f84682 dd-22981 [003] .... 123696.946011: smb3_flush_enter: xid=24 sid=0x98871327 tid=0xfcd585ff fid=0x1917736f dd-22981 [003] .... 123696.946013: smb3_cmd_enter: sid=0x98871327 tid=0xfcd585ff cmd=7 mid=123 dd-22981 [003] .... 123696.956639: smb3_cmd_done: sid=0x98871327 tid=0x0 cmd=7 mid=123 dd-22981 [003] .... 123696.956644: smb3_flush_done: xid=24 sid=0x98871327 tid=0xfcd585ff fid=0x1917736f Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/smb2pdu.c | 10 ++++++++-- fs/cifs/trace.h | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 0e92983de0b7..4c9c34cdf05f 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2918,6 +2918,7 @@ SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon, rqst.rq_iov = iov; rqst.rq_nvec = 1; + trace_smb3_close_enter(xid, persistent_fid, tcon->tid, ses->Suid); rc = SMB2_close_init(tcon, &rqst, persistent_fid, volatile_fid); if (rc) goto close_exit; @@ -2930,7 +2931,9 @@ SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon, trace_smb3_close_err(xid, persistent_fid, tcon->tid, ses->Suid, rc); goto close_exit; - } + } else + trace_smb3_close_done(xid, persistent_fid, tcon->tid, + ses->Suid); atomic_dec(&tcon->num_remote_opens); @@ -3353,13 +3356,16 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, if (rc) goto flush_exit; + trace_smb3_flush_enter(xid, persistent_fid, tcon->tid, ses->Suid); rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); if (rc != 0) { cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE); trace_smb3_flush_err(xid, persistent_fid, tcon->tid, ses->Suid, rc); - } + } else + trace_smb3_flush_done(xid, persistent_fid, tcon->tid, + ses->Suid); flush_exit: SMB2_flush_free(&rqst); diff --git a/fs/cifs/trace.h b/fs/cifs/trace.h index 99c4d799c24b..f1339f4a2d07 100644 --- a/fs/cifs/trace.h +++ b/fs/cifs/trace.h @@ -117,6 +117,41 @@ DEFINE_SMB3_RW_DONE_EVENT(falloc_done); /* * For handle based calls other than read and write, and get/set info */ +DECLARE_EVENT_CLASS(smb3_fd_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid), + TP_ARGS(xid, fid, tid, sesid), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + ), + TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid) +) + +#define DEFINE_SMB3_FD_EVENT(name) \ +DEFINE_EVENT(smb3_fd_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid), \ + TP_ARGS(xid, fid, tid, sesid)) + +DEFINE_SMB3_FD_EVENT(flush_enter); +DEFINE_SMB3_FD_EVENT(flush_done); +DEFINE_SMB3_FD_EVENT(close_enter); +DEFINE_SMB3_FD_EVENT(close_done); + DECLARE_EVENT_CLASS(smb3_fd_err_class, TP_PROTO(unsigned int xid, __u64 fid, From 4f5c10f1ad45ae453822113dd0fe39ed757037fb Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 3 Sep 2019 21:18:49 -0500 Subject: [PATCH 21/38] smb3: allow skipping signature verification for perf sensitive configurations Add new mount option "signloosely" which enables signing but skips the sometimes expensive signing checks in the responses (signatures are calculated and sent correctly in the SMB2/SMB3 requests even with this mount option but skipped in the responses). Although weaker for security (and also data integrity in case a packet were corrupted), this can provide enough of a performance benefit (calculating the signature to verify a packet can be expensive especially for large packets) to be useful in some cases. Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/cifsglob.h | 2 ++ fs/cifs/connect.c | 13 ++++++++++--- fs/cifs/smb2transport.c | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index fa5abe3a8514..1f53dee211d8 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -542,6 +542,7 @@ struct smb_vol { umode_t dir_mode; enum securityEnum sectype; /* sectype requested via mnt opts */ bool sign; /* was signing requested via mnt opts? */ + bool ignore_signature:1; bool retry:1; bool intr:1; bool setuids:1; @@ -681,6 +682,7 @@ struct TCP_Server_Info { char server_GUID[16]; __u16 sec_mode; bool sign; /* is signing enabled on this connection? */ + bool ignore_signature:1; /* skip validation of signatures in SMB2/3 rsp */ bool session_estab; /* mark when very first sess is established */ int echo_credits; /* echo reserved slots */ int oplock_credits; /* oplock break reserved slots */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 85f8d943a05a..17882cede197 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -91,7 +91,7 @@ enum { Opt_serverino, Opt_noserverino, Opt_rwpidforward, Opt_cifsacl, Opt_nocifsacl, Opt_acl, Opt_noacl, Opt_locallease, - Opt_sign, Opt_seal, Opt_noac, + Opt_sign, Opt_ignore_signature, Opt_seal, Opt_noac, Opt_fsc, Opt_mfsymlinks, Opt_multiuser, Opt_sloppy, Opt_nosharesock, Opt_persistent, Opt_nopersistent, @@ -183,6 +183,7 @@ static const match_table_t cifs_mount_option_tokens = { { Opt_noacl, "noacl" }, { Opt_locallease, "locallease" }, { Opt_sign, "sign" }, + { Opt_ignore_signature, "signloosely" }, { Opt_seal, "seal" }, { Opt_noac, "noac" }, { Opt_fsc, "fsc" }, @@ -1877,6 +1878,10 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, case Opt_sign: vol->sign = true; break; + case Opt_ignore_signature: + vol->sign = true; + vol->ignore_signature = true; + break; case Opt_seal: /* we do not do the following in secFlags because seal * is a per tree connection (mount) not a per socket @@ -2608,6 +2613,9 @@ static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol) if (server->rdma != vol->rdma) return 0; + if (server->ignore_signature != vol->ignore_signature) + return 0; + return 1; } @@ -2785,7 +2793,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) tcp_ses->tcpStatus = CifsNeedNegotiate; tcp_ses->nr_targets = 1; - + tcp_ses->ignore_signature = volume_info->ignore_signature; /* thread spawned, put it on the list */ spin_lock(&cifs_tcp_ses_lock); list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); @@ -3235,7 +3243,6 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) ses->sectype = volume_info->sectype; ses->sign = volume_info->sign; - mutex_lock(&ses->session_mutex); rc = cifs_negotiate_protocol(xid, ses); if (!rc) diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index b02242eacb55..148d7942c796 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -522,6 +522,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) if ((shdr->Command == SMB2_NEGOTIATE) || (shdr->Command == SMB2_SESSION_SETUP) || (shdr->Command == SMB2_OPLOCK_BREAK) || + server->ignore_signature || (!server->session_estab)) return 0; From 46f17d17687e8140f2e4e517d7dfa65e62fcc5f4 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 4 Sep 2019 23:07:52 -0500 Subject: [PATCH 22/38] smb3: fix signing verification of large reads Code cleanup in the 5.1 kernel changed the array passed into signing verification on large reads leading to warning messages being logged when copying files to local systems from remote. SMB signature verification returned error = -5 This changeset fixes verification of SMB3 signatures of large reads. Suggested-by: Pavel Shilovsky Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- 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 4c9c34cdf05f..9a65e517df30 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -3483,8 +3483,8 @@ smb2_readv_callback(struct mid_q_entry *mid) struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)rdata->iov[0].iov_base; struct cifs_credits credits = { .value = 0, .instance = 0 }; - struct smb_rqst rqst = { .rq_iov = rdata->iov, - .rq_nvec = 2, + struct smb_rqst rqst = { .rq_iov = &rdata->iov[1], + .rq_nvec = 1, .rq_pages = rdata->pages, .rq_offset = rdata->page_offset, .rq_npages = rdata->nr_pages, From 3175eb9b577e82b44a25ad2d515ec9418ae06c04 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 4 Sep 2019 12:32:41 +1000 Subject: [PATCH 23/38] cifs: add a debug macro that prints \\server\share for errors Where we have a tcon available we can log \\server\share as part of the message. Only do this for the VFS log level. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/cifs_debug.h | 50 ++++++++++++++++++++++++++++++++++------ fs/cifs/smb2ops.c | 54 ++++++++++++++++++++++---------------------- fs/cifs/smb2pdu.c | 28 +++++++++++------------ fs/cifs/transport.c | 4 ++-- 4 files changed, 86 insertions(+), 50 deletions(-) diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h index 567af916f103..100b0056a369 100644 --- a/fs/cifs/cifs_debug.h +++ b/fs/cifs/cifs_debug.h @@ -82,15 +82,18 @@ do { \ #define cifs_server_dbg_func(ratefunc, type, fmt, ...) \ do { \ + const char *sn = ""; \ + if (server && server->hostname) \ + sn = server->hostname; \ if ((type) & FYI && cifsFYI & CIFS_INFO) { \ - pr_debug_ ## ratefunc("%s: Server:%s " fmt, \ - __FILE__, server->hostname, ##__VA_ARGS__);\ + pr_debug_ ## ratefunc("%s: \\\\%s " fmt, \ + __FILE__, sn, ##__VA_ARGS__); \ } else if ((type) & VFS) { \ - pr_err_ ## ratefunc("CIFS VFS: Server:%s " fmt, \ - server->hostname, ##__VA_ARGS__); \ + pr_err_ ## ratefunc("CIFS VFS: \\\\%s " fmt, \ + sn, ##__VA_ARGS__); \ } else if ((type) & NOISY && (NOISY != 0)) { \ - pr_debug_ ## ratefunc("Server:%s " fmt, \ - server->hostname, ##__VA_ARGS__); \ + pr_debug_ ## ratefunc("\\\\%s " fmt, \ + sn, ##__VA_ARGS__); \ } \ } while (0) @@ -104,6 +107,33 @@ do { \ type, fmt, ##__VA_ARGS__); \ } while (0) +#define cifs_tcon_dbg_func(ratefunc, type, fmt, ...) \ +do { \ + const char *tn = ""; \ + if (tcon && tcon->treeName) \ + tn = tcon->treeName; \ + if ((type) & FYI && cifsFYI & CIFS_INFO) { \ + pr_debug_ ## ratefunc("%s: %s " fmt, \ + __FILE__, tn, ##__VA_ARGS__); \ + } else if ((type) & VFS) { \ + pr_err_ ## ratefunc("CIFS VFS: %s " fmt, \ + tn, ##__VA_ARGS__); \ + } else if ((type) & NOISY && (NOISY != 0)) { \ + pr_debug_ ## ratefunc("%s " fmt, \ + tn, ##__VA_ARGS__); \ + } \ +} while (0) + +#define cifs_tcon_dbg(type, fmt, ...) \ +do { \ + if ((type) & ONCE) \ + cifs_tcon_dbg_func(once, \ + type, fmt, ##__VA_ARGS__); \ + else \ + cifs_tcon_dbg_func(ratelimited, \ + type, fmt, ##__VA_ARGS__); \ +} while (0) + /* * debug OFF * --------- @@ -118,10 +148,16 @@ do { \ #define cifs_server_dbg(type, fmt, ...) \ do { \ if (0) \ - pr_debug("Server:%s " fmt, \ + pr_debug("\\\\%s " fmt, \ server->hostname, ##__VA_ARGS__); \ } while (0) +#define cifs_tcon_dbg(type, fmt, ...) \ +do { \ + if (0) \ + pr_debug("%s " fmt, tcon->treeName, ##__VA_ARGS__); \ +} while (0) + #define cifs_info(fmt, ...) \ do { \ pr_info("CIFS: "fmt, ##__VA_ARGS__); \ diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 070d0b7b21dc..83b02d74d48e 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -109,10 +109,10 @@ smb2_add_credits(struct TCP_Server_Info *server, /* change_conf hasn't been executed */ break; case 0: - cifs_dbg(VFS, "Possible client or server bug - zero credits\n"); + cifs_server_dbg(VFS, "Possible client or server bug - zero credits\n"); break; case 1: - cifs_dbg(VFS, "disabling echoes and oplocks\n"); + cifs_server_dbg(VFS, "disabling echoes and oplocks\n"); break; case 2: cifs_dbg(FYI, "disabling oplocks\n"); @@ -230,7 +230,7 @@ smb2_adjust_credits(struct TCP_Server_Info *server, if (server->reconnect_instance != credits->instance) { spin_unlock(&server->req_lock); - cifs_dbg(VFS, "trying to return %d credits to old session\n", + cifs_server_dbg(VFS, "trying to return %d credits to old session\n", credits->value - new_val); return -EAGAIN; } @@ -270,7 +270,7 @@ smb2_find_mid(struct TCP_Server_Info *server, char *buf) __u64 wire_mid = le64_to_cpu(shdr->MessageId); if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { - cifs_dbg(VFS, "Encrypted frame parsing not supported yet\n"); + cifs_server_dbg(VFS, "Encrypted frame parsing not supported yet\n"); return NULL; } @@ -294,10 +294,10 @@ smb2_dump_detail(void *buf, struct TCP_Server_Info *server) #ifdef CONFIG_CIFS_DEBUG2 struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; - cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n", + cifs_server_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n", shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId, shdr->ProcessId); - cifs_dbg(VFS, "smb buf %p len %u\n", buf, + cifs_server_dbg(VFS, "smb buf %p len %u\n", buf, server->ops->calc_smb_size(buf, server)); #endif } @@ -576,7 +576,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) "server does not support query network interfaces\n"); goto out; } else if (rc != 0) { - cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc); + cifs_tcon_dbg(VFS, "error %d on ioctl to get interface list\n", rc); goto out; } @@ -1330,11 +1330,11 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon, (char **)&res_key, &ret_data_len); if (rc) { - cifs_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc); + cifs_tcon_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc); goto req_res_key_exit; } if (ret_data_len < sizeof(struct resume_key_req)) { - cifs_dbg(VFS, "Invalid refcopy resume key length\n"); + cifs_tcon_dbg(VFS, "Invalid refcopy resume key length\n"); rc = -EINVAL; goto req_res_key_exit; } @@ -1486,7 +1486,7 @@ smb2_ioctl_query_info(const unsigned int xid, qi.input_buffer_length, qi.output_buffer_length, buffer); } else { /* unknown flags */ - cifs_dbg(VFS, "invalid passthru query flags: 0x%x\n", qi.flags); + cifs_tcon_dbg(VFS, "invalid passthru query flags: 0x%x\n", qi.flags); rc = -EINVAL; } @@ -1613,7 +1613,7 @@ smb2_copychunk_range(const unsigned int xid, if (rc == 0) { if (ret_data_len != sizeof(struct copychunk_ioctl_rsp)) { - cifs_dbg(VFS, "invalid cchunk response size\n"); + cifs_tcon_dbg(VFS, "invalid cchunk response size\n"); rc = -EIO; goto cchunk_out; } @@ -1627,12 +1627,12 @@ smb2_copychunk_range(const unsigned int xid, */ if (le32_to_cpu(retbuf->TotalBytesWritten) > le32_to_cpu(pcchunk->Length)) { - cifs_dbg(VFS, "invalid copy chunk response\n"); + cifs_tcon_dbg(VFS, "invalid copy chunk response\n"); rc = -EIO; goto cchunk_out; } if (le32_to_cpu(retbuf->ChunksWritten) != 1) { - cifs_dbg(VFS, "invalid num chunks written\n"); + cifs_tcon_dbg(VFS, "invalid num chunks written\n"); rc = -EIO; goto cchunk_out; } @@ -2422,7 +2422,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, if (rc) { if ((rc != -ENOENT) && (rc != -EOPNOTSUPP)) - cifs_dbg(VFS, "ioctl error in %s rc=%d\n", __func__, rc); + cifs_tcon_dbg(VFS, "ioctl error in %s rc=%d\n", __func__, rc); goto out; } @@ -2431,7 +2431,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, nls_codepage, remap, search_name, true /* is_unicode */); if (rc) { - cifs_dbg(VFS, "parse error in %s rc=%d\n", __func__, rc); + cifs_tcon_dbg(VFS, "parse error in %s rc=%d\n", __func__, rc); goto out; } @@ -2661,7 +2661,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) > rsp_iov[1].iov_len) { - cifs_dbg(VFS, "srv returned invalid ioctl len: %d\n", + cifs_tcon_dbg(VFS, "srv returned invalid ioctl len: %d\n", plen); rc = -EIO; goto querty_exit; @@ -3614,14 +3614,14 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst, rc = smb2_get_enc_key(server, tr_hdr->SessionId, enc, key); if (rc) { - cifs_dbg(VFS, "%s: Could not get %scryption key\n", __func__, + cifs_server_dbg(VFS, "%s: Could not get %scryption key\n", __func__, enc ? "en" : "de"); return 0; } rc = smb3_crypto_aead_allocate(server); if (rc) { - cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__); + cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__); return rc; } @@ -3629,19 +3629,19 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst, server->secmech.ccmaesdecrypt; rc = crypto_aead_setkey(tfm, key, SMB3_SIGN_KEY_SIZE); if (rc) { - cifs_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc); + cifs_server_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc); return rc; } rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); if (rc) { - cifs_dbg(VFS, "%s: Failed to set authsize %d\n", __func__, rc); + cifs_server_dbg(VFS, "%s: Failed to set authsize %d\n", __func__, rc); return rc; } req = aead_request_alloc(tfm, GFP_KERNEL); if (!req) { - cifs_dbg(VFS, "%s: Failed to alloc aead request\n", __func__); + cifs_server_dbg(VFS, "%s: Failed to alloc aead request\n", __func__); return -ENOMEM; } @@ -3652,7 +3652,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst, sg = init_sg(num_rqst, rqst, sign); if (!sg) { - cifs_dbg(VFS, "%s: Failed to init sg\n", __func__); + cifs_server_dbg(VFS, "%s: Failed to init sg\n", __func__); rc = -ENOMEM; goto free_req; } @@ -3660,7 +3660,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst, iv_len = crypto_aead_ivsize(tfm); iv = kzalloc(iv_len, GFP_KERNEL); if (!iv) { - cifs_dbg(VFS, "%s: Failed to alloc iv\n", __func__); + cifs_server_dbg(VFS, "%s: Failed to alloc iv\n", __func__); rc = -ENOMEM; goto free_sg; } @@ -3902,7 +3902,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, bool use_rdma_mr = false; if (shdr->Command != SMB2_READ) { - cifs_dbg(VFS, "only big read responses are supported\n"); + cifs_server_dbg(VFS, "only big read responses are supported\n"); return -ENOTSUPP; } @@ -4148,7 +4148,7 @@ receive_encrypted_standard(struct TCP_Server_Info *server, } if (*num_mids >= MAX_COMPOUND) { - cifs_dbg(VFS, "too many PDUs in compound\n"); + cifs_server_dbg(VFS, "too many PDUs in compound\n"); return -1; } bufs[*num_mids] = buf; @@ -4194,7 +4194,7 @@ smb3_receive_transform(struct TCP_Server_Info *server, if (pdu_length < sizeof(struct smb2_transform_hdr) + sizeof(struct smb2_sync_hdr)) { - cifs_dbg(VFS, "Transform message is too small (%u)\n", + cifs_server_dbg(VFS, "Transform message is too small (%u)\n", pdu_length); cifs_reconnect(server); wake_up(&server->response_q); @@ -4202,7 +4202,7 @@ smb3_receive_transform(struct TCP_Server_Info *server, } if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) { - cifs_dbg(VFS, "Transform message is broken\n"); + cifs_server_dbg(VFS, "Transform message is broken\n"); cifs_reconnect(server); wake_up(&server->response_q); return -ECONNABORTED; diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 9a65e517df30..cab696ac68ab 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -1019,7 +1019,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) } if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL) - cifs_server_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n"); + cifs_tcon_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n"); pneg_inbuf = kmalloc(sizeof(*pneg_inbuf), GFP_NOFS); if (!pneg_inbuf) @@ -1076,18 +1076,18 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) * Old Windows versions or Netapp SMB server can return * not supported error. Client should accept it. */ - cifs_server_dbg(VFS, "Server does not support validate negotiate\n"); + cifs_tcon_dbg(VFS, "Server does not support validate negotiate\n"); rc = 0; goto out_free_inbuf; } else if (rc != 0) { - cifs_server_dbg(VFS, "validate protocol negotiate failed: %d\n", rc); + cifs_tcon_dbg(VFS, "validate protocol negotiate failed: %d\n", rc); rc = -EIO; goto out_free_inbuf; } rc = -EIO; if (rsplen != sizeof(*pneg_rsp)) { - cifs_server_dbg(VFS, "invalid protocol negotiate response size: %d\n", + cifs_tcon_dbg(VFS, "invalid protocol negotiate response size: %d\n", rsplen); /* relax check since Mac returns max bufsize allowed on ioctl */ @@ -1114,7 +1114,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) goto out_free_rsp; vneg_out: - cifs_server_dbg(VFS, "protocol revalidation - security settings mismatch\n"); + cifs_tcon_dbg(VFS, "protocol revalidation - security settings mismatch\n"); out_free_rsp: kfree(pneg_rsp); out_free_inbuf: @@ -1762,11 +1762,11 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) - cifs_server_dbg(VFS, "DFS capability contradicts DFS flag\n"); + cifs_tcon_dbg(VFS, "DFS capability contradicts DFS flag\n"); if (tcon->seal && !(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) - cifs_server_dbg(VFS, "Encryption is requested but not supported\n"); + cifs_tcon_dbg(VFS, "Encryption is requested but not supported\n"); init_copy_chunk_defaults(tcon); if (server->ops->validate_negotiate) @@ -1779,7 +1779,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, tcon_error_exit: if (rsp && rsp->sync_hdr.Status == STATUS_BAD_NETWORK_NAME) { - cifs_server_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); + cifs_tcon_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); } goto tcon_exit; } @@ -2812,14 +2812,14 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, if (*plen == 0) goto ioctl_exit; /* server returned no data */ else if (*plen > rsp_iov.iov_len || *plen > 0xFF00) { - cifs_server_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); + cifs_tcon_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); *plen = 0; rc = -EIO; goto ioctl_exit; } if (rsp_iov.iov_len - *plen < le32_to_cpu(rsp->OutputOffset)) { - cifs_server_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, + cifs_tcon_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, le32_to_cpu(rsp->OutputOffset)); *plen = 0; rc = -EIO; @@ -3110,7 +3110,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, if (!*data) { *data = kmalloc(*dlen, GFP_KERNEL); if (!*data) { - cifs_server_dbg(VFS, + cifs_tcon_dbg(VFS, "Error %d allocating memory for acl\n", rc); *dlen = 0; @@ -3505,7 +3505,7 @@ smb2_readv_callback(struct mid_q_entry *mid) rc = smb2_verify_signature(&rqst, server); if (rc) - cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n", + cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n", rc); } /* FIXME: should this be counted toward the initiating task? */ @@ -4095,7 +4095,7 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1; break; default: - cifs_server_dbg(VFS, "info level %u isn't supported\n", + cifs_tcon_dbg(VFS, "info level %u isn't supported\n", srch_inf->info_level); rc = -EINVAL; goto qdir_exit; @@ -4186,7 +4186,7 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, else if (resp_buftype == CIFS_SMALL_BUFFER) srch_inf->smallBuf = true; else - cifs_server_dbg(VFS, "illegal search buffer type\n"); + cifs_tcon_dbg(VFS, "illegal search buffer type\n"); trace_smb3_query_dir_done(xid, persistent_fid, tcon->tid, tcon->ses->Suid, index, srch_inf->entries_in_buffer); diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index a90bd4d75b4d..4fccb90492e9 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -1403,7 +1403,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, use ses->maxReq */ if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { - cifs_server_dbg(VFS, "Illegal length, greater than maximum frame, %d\n", + cifs_tcon_dbg(VFS, "Illegal length, greater than maximum frame, %d\n", len); return -EIO; } @@ -1505,7 +1505,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, /* rcvd frame is ok */ if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) { rc = -EIO; - cifs_server_dbg(VFS, "Bad MID state?\n"); + cifs_tcon_dbg(VFS, "Bad MID state?\n"); goto out; } From 35cf94a397280b9e27576ac1480f631bdd3e7b70 Mon Sep 17 00:00:00 2001 From: Steve French Date: Sat, 7 Sep 2019 01:09:49 -0500 Subject: [PATCH 24/38] smb3: allow parallelizing decryption of reads decrypting large reads on encrypted shares can be slow (e.g. adding multiple milliseconds per-read on non-GCM capable servers or when mounting with dialects prior to SMB3.1.1) - allow parallelizing of read decryption by launching worker threads. Testing to Samba on localhost showed 25% improvement. Testing to remote server showed very large improvement when doing more than one 'cp' command was called at one time. Signed-off-by: Steve French Signed-off-by: Ronnie Sahlberg --- fs/cifs/cifsfs.c | 17 +++++++++- fs/cifs/cifsglob.h | 1 + fs/cifs/smb2ops.c | 83 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 97 insertions(+), 4 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index de90e665ef11..b0ea332af35c 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -109,6 +109,7 @@ extern mempool_t *cifs_req_poolp; extern mempool_t *cifs_mid_poolp; struct workqueue_struct *cifsiod_wq; +struct workqueue_struct *decrypt_wq; struct workqueue_struct *cifsoplockd_wq; __u32 cifs_lock_secret; @@ -1499,11 +1500,22 @@ init_cifs(void) goto out_clean_proc; } + /* + * BB Consider setting limit!=0 maybe to min(num_of_cores - 1, 3) so we + * don't launch too many worker threads + */ + decrypt_wq = alloc_workqueue("smb3decryptd", + WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); + if (!decrypt_wq) { + rc = -ENOMEM; + goto out_destroy_cifsiod_wq; + } + cifsoplockd_wq = alloc_workqueue("cifsoplockd", WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); if (!cifsoplockd_wq) { rc = -ENOMEM; - goto out_destroy_cifsiod_wq; + goto out_destroy_decrypt_wq; } rc = cifs_fscache_register(); @@ -1569,6 +1581,8 @@ init_cifs(void) cifs_fscache_unregister(); out_destroy_cifsoplockd_wq: destroy_workqueue(cifsoplockd_wq); +out_destroy_decrypt_wq: + destroy_workqueue(decrypt_wq); out_destroy_cifsiod_wq: destroy_workqueue(cifsiod_wq); out_clean_proc: @@ -1595,6 +1609,7 @@ exit_cifs(void) cifs_destroy_inodecache(); cifs_fscache_unregister(); destroy_workqueue(cifsoplockd_wq); + destroy_workqueue(decrypt_wq); destroy_workqueue(cifsiod_wq); cifs_proc_clean(); } diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 1f53dee211d8..d66106ac031a 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1892,6 +1892,7 @@ void cifs_queue_oplock_break(struct cifsFileInfo *cfile); extern const struct slow_work_ops cifs_oplock_break_ops; extern struct workqueue_struct *cifsiod_wq; +extern struct workqueue_struct *decrypt_wq; extern struct workqueue_struct *cifsoplockd_wq; extern __u32 cifs_lock_secret; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 83b02d74d48e..c74284484947 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -4017,8 +4017,58 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, return length; } +struct smb2_decrypt_work { + struct work_struct decrypt; + struct TCP_Server_Info *server; + struct page **ppages; + char *buf; + unsigned int npages; + unsigned int len; +}; + + +static void smb2_decrypt_offload(struct work_struct *work) +{ + struct smb2_decrypt_work *dw = container_of(work, + struct smb2_decrypt_work, decrypt); + int i, rc; + struct mid_q_entry *mid; + + rc = decrypt_raw_data(dw->server, dw->buf, dw->server->vals->read_rsp_size, + dw->ppages, dw->npages, dw->len); + if (rc) { + cifs_dbg(VFS, "error decrypting rc=%d\n", rc); + goto free_pages; + } + + mid = smb2_find_mid(dw->server, dw->buf); + if (mid == NULL) + cifs_dbg(FYI, "mid not found\n"); + else { + mid->decrypted = true; + rc = handle_read_data(dw->server, mid, dw->buf, + dw->server->vals->read_rsp_size, + dw->ppages, dw->npages, dw->len); + } + + dw->server->lstrp = jiffies; + + mid->callback(mid); + + cifs_mid_q_entry_release(mid); + +free_pages: + for (i = dw->npages-1; i >= 0; i--) + put_page(dw->ppages[i]); + + kfree(dw->ppages); + cifs_small_buf_release(dw->buf); +} + + static int -receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid) +receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid, + int *num_mids) { char *buf = server->smallbuf; struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; @@ -4028,7 +4078,9 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid) unsigned int buflen = server->pdu_size; int rc; int i = 0; + struct smb2_decrypt_work *dw; + *num_mids = 1; len = min_t(unsigned int, buflen, server->vals->read_rsp_size + sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1; @@ -4064,6 +4116,32 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid) if (rc) goto free_pages; + /* + * For large reads, offload to different thread for better performance, + * use more cores decrypting which can be expensive + */ + + /* TODO: make the size limit to enable decrypt offload configurable */ + if (server->pdu_size > (512 * 1024)) { + dw = kmalloc(sizeof(struct smb2_decrypt_work), GFP_KERNEL); + if (dw == NULL) + goto non_offloaded_decrypt; + + dw->buf = server->smallbuf; + server->smallbuf = (char *)cifs_small_buf_get(); + + INIT_WORK(&dw->decrypt, smb2_decrypt_offload); + + dw->npages = npages; + dw->server = server; + dw->ppages = pages; + dw->len = len; + queue_work(cifsiod_wq, &dw->decrypt); + *num_mids = 0; /* worker thread takes care of finding mid */ + return -1; + } + +non_offloaded_decrypt: rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size, pages, npages, len); if (rc) @@ -4210,8 +4288,7 @@ smb3_receive_transform(struct TCP_Server_Info *server, /* TODO: add support for compounds containing READ. */ if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) { - *num_mids = 1; - return receive_encrypted_read(server, &mids[0]); + return receive_encrypted_read(server, &mids[0], num_mids); } return receive_encrypted_standard(server, mids, bufs, num_mids); From 563317ec3083f7e126d7e30821ff8505ac338ee5 Mon Sep 17 00:00:00 2001 From: Steve French Date: Sun, 8 Sep 2019 23:22:02 -0500 Subject: [PATCH 25/38] smb3: enable offload of decryption of large reads via mount option Disable offload of the decryption of encrypted read responses by default (equivalent to setting this new mount option "esize=0"). Allow setting the minimum encrypted read response size that we will choose to offload to a worker thread - it is now configurable via on a new mount option "esize=" Depending on which encryption mechanism (GCM vs. CCM) and the number of reads that will be issued in parallel and the performance of the network and CPU on the client, it may make sense to enable this since it can provide substantial benefit when multiple large reads are in flight at the same time. Signed-off-by: Steve French Signed-off-by: Ronnie Sahlberg --- fs/cifs/cifsfs.c | 2 ++ fs/cifs/cifsglob.h | 2 ++ fs/cifs/connect.c | 13 +++++++++++++ fs/cifs/smb2ops.c | 4 ++-- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index b0ea332af35c..ebf85a5d95e4 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -559,6 +559,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root) seq_printf(s, ",rsize=%u", cifs_sb->rsize); seq_printf(s, ",wsize=%u", cifs_sb->wsize); seq_printf(s, ",bsize=%u", cifs_sb->bsize); + if (tcon->ses->server->min_offload) + seq_printf(s, ",esize=%u", tcon->ses->server->min_offload); seq_printf(s, ",echo_interval=%lu", tcon->ses->server->echo_interval / HZ); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index d66106ac031a..6987fbc5a24a 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -592,6 +592,7 @@ struct smb_vol { unsigned int bsize; unsigned int rsize; unsigned int wsize; + unsigned int min_offload; bool sockopt_tcp_nodelay:1; unsigned long actimeo; /* attribute cache timeout (jiffies) */ struct smb_version_operations *ops; @@ -745,6 +746,7 @@ struct TCP_Server_Info { #endif /* STATS2 */ unsigned int max_read; unsigned int max_write; + unsigned int min_offload; __le16 compress_algorithm; __le16 cipher_type; /* save initital negprot hash */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 17882cede197..e70112d67b0e 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -103,6 +103,7 @@ enum { Opt_backupuid, Opt_backupgid, Opt_uid, Opt_cruid, Opt_gid, Opt_file_mode, Opt_dirmode, Opt_port, + Opt_min_enc_offload, Opt_blocksize, Opt_rsize, Opt_wsize, Opt_actimeo, Opt_echo_interval, Opt_max_credits, Opt_handletimeout, Opt_snapshot, @@ -207,6 +208,7 @@ static const match_table_t cifs_mount_option_tokens = { { Opt_dirmode, "dirmode=%s" }, { Opt_dirmode, "dir_mode=%s" }, { Opt_port, "port=%s" }, + { Opt_min_enc_offload, "esize=%s" }, { Opt_blocksize, "bsize=%s" }, { Opt_rsize, "rsize=%s" }, { Opt_wsize, "wsize=%s" }, @@ -2016,6 +2018,13 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, } port = (unsigned short)option; break; + case Opt_min_enc_offload: + if (get_option_ul(args, &option)) { + cifs_dbg(VFS, "Invalid minimum encrypted read offload size (esize)\n"); + goto cifs_parse_mount_err; + } + vol->min_offload = option; + break; case Opt_blocksize: if (get_option_ul(args, &option)) { cifs_dbg(VFS, "%s: Invalid blocksize value\n", @@ -2616,6 +2625,9 @@ static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol) if (server->ignore_signature != vol->ignore_signature) return 0; + if (server->min_offload != vol->min_offload) + return 0; + return 1; } @@ -2790,6 +2802,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) module_put(THIS_MODULE); goto out_err_crypto_release; } + tcp_ses->min_offload = volume_info->min_offload; tcp_ses->tcpStatus = CifsNeedNegotiate; tcp_ses->nr_targets = 1; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index c74284484947..1cfb8d518132 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -4121,8 +4121,8 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid, * use more cores decrypting which can be expensive */ - /* TODO: make the size limit to enable decrypt offload configurable */ - if (server->pdu_size > (512 * 1024)) { + if ((server->min_offload) && + (server->pdu_size >= server->min_offload)) { dw = kmalloc(sizeof(struct smb2_decrypt_work), GFP_KERNEL); if (dw == NULL) goto non_offloaded_decrypt; From 496902dc173dead0e5eeba1f2fd4abd9ba6f2da0 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 9 Sep 2019 15:30:00 +1000 Subject: [PATCH 26/38] cifs: add a helper to find an existing readable handle to a file and convert smb2_query_path_info() to use it. This will eliminate the need for a SMB2_Create when we already have an open handle that can be used. This will also prevent a oplock break in case the other handle holds a lease. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 2 ++ fs/cifs/file.c | 36 ++++++++++++++++++++++++++++++++++++ fs/cifs/smb2inode.c | 28 +++++++++++++++++++++++----- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 7b69037bed24..99b1b1ef558c 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -140,6 +140,8 @@ extern int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, extern int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name, struct cifsFileInfo **ret_file); extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool); +extern int cifs_get_readable_path(struct cifs_tcon *tcon, const char *name, + struct cifsFileInfo **ret_file); extern unsigned int smbCalcSize(void *buf, struct TCP_Server_Info *server); extern int decode_negTokenInit(unsigned char *security_blob, int length, struct TCP_Server_Info *server); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 6124b1d1ab05..4b95700c507c 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2008,6 +2008,42 @@ cifs_get_writable_path(struct cifs_tcon *tcon, const char *name, return -ENOENT; } +int +cifs_get_readable_path(struct cifs_tcon *tcon, const char *name, + struct cifsFileInfo **ret_file) +{ + struct list_head *tmp; + struct cifsFileInfo *cfile; + struct cifsInodeInfo *cinode; + char *full_path; + + *ret_file = NULL; + + spin_lock(&tcon->open_file_lock); + list_for_each(tmp, &tcon->openFileList) { + cfile = list_entry(tmp, struct cifsFileInfo, + tlist); + full_path = build_path_from_dentry(cfile->dentry); + if (full_path == NULL) { + spin_unlock(&tcon->open_file_lock); + return -ENOMEM; + } + if (strcmp(full_path, name)) { + kfree(full_path); + continue; + } + + kfree(full_path); + cinode = CIFS_I(d_inode(cfile->dentry)); + spin_unlock(&tcon->open_file_lock); + *ret_file = find_readable_file(cinode, 0); + return *ret_file ? 0 : -ENOENT; + } + + spin_unlock(&tcon->open_file_lock); + return -ENOENT; +} + static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) { struct address_space *mapping = page->mapping; diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 9bb0a5bc38c9..c866555b6278 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -125,15 +125,31 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, 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, + if (cfile) + rc = SMB2_query_info_init(tcon, &rqst[num_rqst], + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + FILE_ALL_INFORMATION, SMB2_O_INFO_FILE, 0, sizeof(struct smb2_file_all_info) + PATH_MAX * 2, 0, NULL); + else { + 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, 0, NULL); + if (!rc) { + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst]); + } + } + if (rc) goto finished; - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst++]); + num_rqst++; trace_smb3_query_info_compound_enter(xid, ses->Suid, tcon->tid, full_path); break; @@ -421,6 +437,7 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, __u32 create_options = 0; struct cifs_fid fid; bool no_cached_open = tcon->nohandlecache; + struct cifsFileInfo *cfile; *adjust_tz = false; *symlink = false; @@ -452,9 +469,10 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, if (backup_cred(cifs_sb)) create_options |= CREATE_OPEN_BACKUP_INTENT; + cifs_get_readable_path(tcon, full_path, &cfile); rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, create_options, - smb2_data, SMB2_OP_QUERY_INFO, NULL); + smb2_data, SMB2_OP_QUERY_INFO, cfile); if (rc == -EOPNOTSUPP) { *symlink = true; create_options |= OPEN_REPARSE_POINT; From 10328c44cc1506dd82fd835efcaafd519866c464 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 9 Sep 2019 13:30:15 -0500 Subject: [PATCH 27/38] smb3: only offload decryption of read responses if multiple requests No point in offloading read decryption if no other requests on the wire Signed-off-by: Steve French Signed-off-by: Ronnie Sahlberg --- fs/cifs/cifsfs.c | 9 ++++++--- fs/cifs/smb2ops.c | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index ebf85a5d95e4..c1b685072063 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -1503,11 +1503,14 @@ init_cifs(void) } /* - * BB Consider setting limit!=0 maybe to min(num_of_cores - 1, 3) so we - * don't launch too many worker threads + * Consider in future setting limit!=0 maybe to min(num_of_cores - 1, 3) + * so that we don't launch too many worker threads but + * Documentation/workqueue.txt recommends setting it to 0 */ + + /* WQ_UNBOUND allows decrypt tasks to run on any CPU */ decrypt_wq = alloc_workqueue("smb3decryptd", - WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); + WQ_UNBOUND|WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); if (!decrypt_wq) { rc = -ENOMEM; goto out_destroy_cifsiod_wq; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 1cfb8d518132..72b3e39d7f4f 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -4121,7 +4121,7 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid, * use more cores decrypting which can be expensive */ - if ((server->min_offload) && + if ((server->min_offload) && (server->in_flight > 1) && (server->pdu_size >= server->min_offload)) { dw = kmalloc(sizeof(struct smb2_decrypt_work), GFP_KERNEL); if (dw == NULL) From 1b63f1840e787516bf2d3e5c27c2d22fe23cfb37 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 9 Sep 2019 22:57:11 -0500 Subject: [PATCH 28/38] smb3: display max smb3 requests in flight at any one time Displayed in /proc/fs/cifs/Stats once for each socket we are connected to. This allows us to find out what the maximum number of requests that had been in flight (at any one time). Note that /proc/fs/cifs/Stats can be reset if you want to look for maximum over a small period of time. Sample output (immediately after mount): 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: 5 maximum at one time: 2 Max requests in flight: 2 1) \\localhost\scratch SMBs: 18 Bytes read: 0 Bytes written: 0 ... Signed-off-by: Steve French Reviewed-by: Pavel Shilovsky --- fs/cifs/cifs_debug.c | 2 ++ fs/cifs/cifsglob.h | 1 + fs/cifs/connect.c | 1 + fs/cifs/smb2ops.c | 2 ++ fs/cifs/transport.c | 4 ++++ 5 files changed, 10 insertions(+) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index a38d796f5ffe..0b4eee3bed66 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -452,6 +452,7 @@ static ssize_t cifs_stats_proc_write(struct file *file, list_for_each(tmp1, &cifs_tcp_ses_list) { server = list_entry(tmp1, struct TCP_Server_Info, tcp_ses_list); + server->max_in_flight = 0; #ifdef CONFIG_CIFS_STATS2 for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { atomic_set(&server->num_cmds[i], 0); @@ -526,6 +527,7 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v) list_for_each(tmp1, &cifs_tcp_ses_list) { server = list_entry(tmp1, struct TCP_Server_Info, tcp_ses_list); + seq_printf(m, "\nMax requests in flight: %d", server->max_in_flight); #ifdef CONFIG_CIFS_STATS2 seq_puts(m, "\nTotal time spent processing by command. Time "); seq_printf(m, "units are jiffies (%d per second)\n", HZ); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 6987fbc5a24a..ef2199913217 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -677,6 +677,7 @@ struct TCP_Server_Info { unsigned int credits; /* send no more requests at once */ unsigned int max_credits; /* can override large 32000 default at mnt */ unsigned int in_flight; /* number of requests on the wire to server */ + unsigned int max_in_flight; /* max number of requests that were on wire */ spinlock_t req_lock; /* protect the two values above */ struct mutex srv_mutex; struct task_struct *tsk; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index e70112d67b0e..df1ccb581828 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2728,6 +2728,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) tcp_ses->tcp_nodelay = volume_info->sockopt_tcp_nodelay; tcp_ses->rdma = volume_info->rdma; tcp_ses->in_flight = 0; + tcp_ses->max_in_flight = 0; tcp_ses->credits = 1; init_waitqueue_head(&tcp_ses->response_q); init_waitqueue_head(&tcp_ses->request_q); diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 72b3e39d7f4f..9b74149b471f 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -203,6 +203,8 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, credits->instance = server->reconnect_instance; server->credits -= credits->value; server->in_flight++; + if (server->in_flight > server->max_in_flight) + server->max_in_flight = server->in_flight; break; } } diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 4fccb90492e9..308ad0f495e1 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -532,6 +532,8 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits, if ((flags & CIFS_TIMEOUT_MASK) == CIFS_NON_BLOCKING) { /* oplock breaks must not be held up */ server->in_flight++; + if (server->in_flight > server->max_in_flight) + server->max_in_flight = server->in_flight; *credits -= 1; *instance = server->reconnect_instance; spin_unlock(&server->req_lock); @@ -608,6 +610,8 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits, if ((flags & CIFS_TIMEOUT_MASK) != CIFS_BLOCKING_OP) { *credits -= num_credits; server->in_flight += num_credits; + if (server->in_flight > server->max_in_flight) + server->max_in_flight = server->in_flight; *instance = server->reconnect_instance; } spin_unlock(&server->req_lock); From 7dcc82c2dfd5f12eba19d19d39c50bff70b4f94a Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 11 Sep 2019 00:07:36 -0500 Subject: [PATCH 29/38] smb3: improve handling of share deleted (and share recreated) When a share is deleted, returning EIO is confusing and no useful information is logged. Improve the handling of this case by at least logging a better error for this (and also mapping the error differently to EREMCHG). See e.g. the new messages that would be logged: [55243.639530] server share \\192.168.1.219\scratch deleted [55243.642568] CIFS VFS: \\192.168.1.219\scratch BAD_NETWORK_NAME: \\192.168.1.219\scratch In addition for the case where a share is deleted and then recreated with the same name, have now fixed that so it works. This is sometimes done for example, because the admin had to move a share to a different, bigger local drive when a share is running low on space. Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/smb2inode.c | 6 ++++++ fs/cifs/smb2maperror.c | 2 +- fs/cifs/smb2ops.c | 13 ++++++++++++- fs/cifs/smb2pdu.c | 5 +++++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index c866555b6278..d2a3fb7e5c8d 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -335,6 +335,12 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, cifsFileInfo_put(cfile); SMB2_open_free(&rqst[0]); + if (rc == -EREMCHG) { + printk_once(KERN_WARNING "server share %s deleted\n", + tcon->treeName); + tcon->need_reconnect = true; + } + switch (command) { case SMB2_OP_QUERY_INFO: if (rc == 0) { diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c index 82ade16c9501..7fde3775cb57 100644 --- a/fs/cifs/smb2maperror.c +++ b/fs/cifs/smb2maperror.c @@ -511,7 +511,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = { {STATUS_PRINT_QUEUE_FULL, -EIO, "STATUS_PRINT_QUEUE_FULL"}, {STATUS_NO_SPOOL_SPACE, -EIO, "STATUS_NO_SPOOL_SPACE"}, {STATUS_PRINT_CANCELLED, -EIO, "STATUS_PRINT_CANCELLED"}, - {STATUS_NETWORK_NAME_DELETED, -EIO, "STATUS_NETWORK_NAME_DELETED"}, + {STATUS_NETWORK_NAME_DELETED, -EREMCHG, "STATUS_NETWORK_NAME_DELETED"}, {STATUS_NETWORK_ACCESS_DENIED, -EACCES, "STATUS_NETWORK_ACCESS_DENIED"}, {STATUS_BAD_DEVICE_TYPE, -EIO, "STATUS_BAD_DEVICE_TYPE"}, {STATUS_BAD_NETWORK_NAME, -ENOENT, "STATUS_BAD_NETWORK_NAME"}, diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 9b74149b471f..3672ce0bfbaf 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -741,8 +741,14 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid) /* Cached root is still invalid, continue normaly */ - if (rc) + if (rc) { + if (rc == -EREMCHG) { + tcon->need_reconnect = true; + printk_once(KERN_WARNING "server share %s deleted\n", + tcon->treeName); + } goto oshr_exit; + } o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; oparms.fid->persistent_fid = o_rsp->PersistentFileId; @@ -2237,6 +2243,11 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon, resp_buftype, rsp_iov); if (rc) { free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + if (rc == -EREMCHG) { + tcon->need_reconnect = true; + printk_once(KERN_WARNING "server share %s deleted\n", + tcon->treeName); + } goto qic_exit; } *rsp = rsp_iov[1]; diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index cab696ac68ab..01d5c4af2458 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2595,6 +2595,11 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, } trace_smb3_open_err(xid, tcon->tid, ses->Suid, oparms->create_options, oparms->desired_access, rc); + if (rc == -EREMCHG) { + printk_once(KERN_WARNING "server share %s deleted\n", + tcon->treeName); + tcon->need_reconnect = true; + } goto creat_exit; } else trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, From 3e7a02d47872081f4b6234a9f72500f1d10f060c Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 11 Sep 2019 21:46:20 -0500 Subject: [PATCH 30/38] smb3: allow disabling requesting leases In some cases to work around server bugs or performance problems it can be helpful to be able to disable requesting SMB2.1/SMB3 leases on a particular mount (not to all servers and all shares we are mounted to). Add new mount parm "nolease" which turns off requesting leases on directory or file opens. Currently the only way to disable leases is globally through a module load parameter. This is more granular. Suggested-by: Pavel Shilovsky Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg Reviewed-by: Pavel Shilovsky CC: Stable --- fs/cifs/cifsfs.c | 2 ++ fs/cifs/cifsglob.h | 2 ++ fs/cifs/connect.c | 9 ++++++++- fs/cifs/smb2pdu.c | 2 +- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index c1b685072063..69601a9b29ad 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -438,6 +438,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root) cifs_show_security(s, tcon->ses); cifs_show_cache_flavor(s, cifs_sb); + if (tcon->no_lease) + seq_puts(s, ",nolease"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) seq_puts(s, ",multiuser"); else if (tcon->ses->user_name) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index ef2199913217..09b60ec5de3e 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -579,6 +579,7 @@ struct smb_vol { bool noblocksnd:1; bool noautotune:1; bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ + bool no_lease:1; /* disable requesting leases */ bool fsc:1; /* enable fscache */ bool mfsymlinks:1; /* use Minshall+French Symlinks */ bool multiuser:1; @@ -1090,6 +1091,7 @@ struct cifs_tcon { bool need_reopen_files:1; /* need to reopen tcon file handles */ bool use_resilient:1; /* use resilient instead of durable handles */ bool use_persistent:1; /* use persistent instead of durable handles */ + bool no_lease:1; /* Do not request leases on files or directories */ __le32 capabilities; __u32 share_flags; __u32 maximal_access; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index df1ccb581828..e16b6cc1e31b 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -74,7 +74,7 @@ enum { Opt_user_xattr, Opt_nouser_xattr, Opt_forceuid, Opt_noforceuid, Opt_forcegid, Opt_noforcegid, - Opt_noblocksend, Opt_noautotune, + Opt_noblocksend, Opt_noautotune, Opt_nolease, Opt_hard, Opt_soft, Opt_perm, Opt_noperm, Opt_mapposix, Opt_nomapposix, Opt_mapchars, Opt_nomapchars, Opt_sfu, @@ -135,6 +135,7 @@ static const match_table_t cifs_mount_option_tokens = { { Opt_noforcegid, "noforcegid" }, { Opt_noblocksend, "noblocksend" }, { Opt_noautotune, "noautotune" }, + { Opt_nolease, "nolease" }, { Opt_hard, "hard" }, { Opt_soft, "soft" }, { Opt_perm, "perm" }, @@ -1738,6 +1739,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, case Opt_noautotune: vol->noautotune = 1; break; + case Opt_nolease: + vol->no_lease = 1; + break; case Opt_hard: vol->retry = 1; break; @@ -3294,6 +3298,8 @@ static int match_tcon(struct cifs_tcon *tcon, struct smb_vol *volume_info) return 0; if (tcon->handle_timeout != volume_info->handle_timeout) return 0; + if (tcon->no_lease != volume_info->no_lease) + return 0; return 1; } @@ -3516,6 +3522,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) tcon->nocase = volume_info->nocase; tcon->nohandlecache = volume_info->nohandlecache; tcon->local_lease = volume_info->local_lease; + tcon->no_lease = volume_info->no_lease; INIT_LIST_HEAD(&tcon->pending_opens); spin_lock(&cifs_tcp_ses_lock); diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 01d5c4af2458..ce647cfdc04f 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2459,7 +2459,7 @@ SMB2_open_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, __u8 *oplock, iov[1].iov_len = uni_path_len; iov[1].iov_base = path; - if (!server->oplocks) + if ((!server->oplocks) || (tcon->no_lease)) *oplock = SMB2_OPLOCK_LEVEL_NONE; if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) || From 96d9f7ed00b86104bf03adeffc8980897e9694ab Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 12 Sep 2019 17:52:54 -0500 Subject: [PATCH 31/38] smb3: fix unmount hang in open_shroot An earlier patch "CIFS: fix deadlock in cached root handling" did not completely address the deadlock in open_shroot. This patch addresses the deadlock. In testing the recent patch: smb3: improve handling of share deleted (and share recreated) we were able to reproduce the open_shroot deadlock to one of the target servers in unmount in a delete share scenario. Fixes: 7e5a70ad88b1e ("CIFS: fix deadlock in cached root handling") This is version 2 of this patch. An earlier version of this patch "smb3: fix unmount hang in open_shroot" had a problem found by Dan. Reported-by: kbuild test robot Reported-by: Dan Carpenter Suggested-by: Pavel Shilovsky Reviewed-by: Pavel Shilovsky Signed-off-by: Steve French CC: Aurelien Aptel CC: Stable --- fs/cifs/smb2ops.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 3672ce0bfbaf..5776d7b0a97e 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -658,6 +658,15 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid) return 0; } + /* + * We do not hold the lock for the open because in case + * SMB2_open needs to reconnect, it will end up calling + * cifs_mark_open_files_invalid() which takes the lock again + * thus causing a deadlock + */ + + mutex_unlock(&tcon->crfid.fid_mutex); + if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; @@ -679,7 +688,7 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid) rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, &utf16_path); if (rc) - goto oshr_exit; + goto oshr_free; smb2_set_next_command(tcon, &rqst[0]); memset(&qi_iov, 0, sizeof(qi_iov)); @@ -692,18 +701,10 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid) sizeof(struct smb2_file_all_info) + PATH_MAX * 2, 0, NULL); if (rc) - goto oshr_exit; + goto oshr_free; smb2_set_related(&rqst[1]); - /* - * We do not hold the lock for the open because in case - * SMB2_open needs to reconnect, it will end up calling - * cifs_mark_open_files_invalid() which takes the lock again - * thus causing a deadlock - */ - - mutex_unlock(&tcon->crfid.fid_mutex); rc = compound_send_recv(xid, ses, flags, 2, rqst, resp_buftype, rsp_iov); mutex_lock(&tcon->crfid.fid_mutex); From 2255397c33425b9c43e36ab01479842258dedccb Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 13 Sep 2019 16:47:31 -0500 Subject: [PATCH 32/38] smb3: fix potential null dereference in decrypt offload commit a091c5f67c99 ("smb3: allow parallelizing decryption of reads") had a potential null dereference Reported-by: kbuild test robot Reported-by: Dan Carpenter Suggested-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/smb2ops.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 5776d7b0a97e..eaed18061314 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -4055,6 +4055,7 @@ static void smb2_decrypt_offload(struct work_struct *work) goto free_pages; } + dw->server->lstrp = jiffies; mid = smb2_find_mid(dw->server, dw->buf); if (mid == NULL) cifs_dbg(FYI, "mid not found\n"); @@ -4063,14 +4064,10 @@ static void smb2_decrypt_offload(struct work_struct *work) rc = handle_read_data(dw->server, mid, dw->buf, dw->server->vals->read_rsp_size, dw->ppages, dw->npages, dw->len); + mid->callback(mid); + cifs_mid_q_entry_release(mid); } - dw->server->lstrp = jiffies; - - mid->callback(mid); - - cifs_mid_q_entry_release(mid); - free_pages: for (i = dw->npages-1; i >= 0; i--) put_page(dw->ppages[i]); From 0892ba693f304ce3e9ca1f0a64a5cfc051454996 Mon Sep 17 00:00:00 2001 From: Aurelien Aptel Date: Mon, 16 Sep 2019 04:28:36 +0200 Subject: [PATCH 33/38] cifs: modefromsid: make room for 4 ACE when mounting with modefromsid, we end up writing 4 ACE in a security descriptor that only has room for 3, thus triggering an out-of-bounds write. fix this by changing the min size of a security descriptor. Signed-off-by: Aurelien Aptel Signed-off-by: Steve French --- fs/cifs/cifsacl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h index dd95a6fa24bf..eb428349f29a 100644 --- a/fs/cifs/cifsacl.h +++ b/fs/cifs/cifsacl.h @@ -45,7 +45,7 @@ */ #define DEFAULT_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + \ sizeof(struct cifs_acl) + \ - (sizeof(struct cifs_ace) * 3)) + (sizeof(struct cifs_ace) * 4)) /* * Maximum size of a string representation of a SID: From 8eecd1c2e5bc73d33f3a544751305679dbf88eb4 Mon Sep 17 00:00:00 2001 From: "Paulo Alcantara (SUSE)" Date: Tue, 16 Jul 2019 19:04:50 -0300 Subject: [PATCH 34/38] cifs: Add support for root file systems Introduce a new CONFIG_CIFS_ROOT option to handle root file systems over a SMB share. In order to mount the root file system during the init process, make cifs.ko perform non-blocking socket operations while mounting and accessing it. Cc: Steve French Reviewed-by: Aurelien Aptel Signed-off-by: Paulo Alcantara (SUSE) Signed-off-by: Steve French --- Documentation/filesystems/cifs/cifsroot.txt | 97 +++++++++++++++++++++ fs/cifs/Kconfig | 8 ++ fs/cifs/Makefile | 2 + fs/cifs/cifsglob.h | 2 + fs/cifs/cifsroot.c | 83 ++++++++++++++++++ fs/cifs/connect.c | 17 +++- include/linux/root_dev.h | 1 + 7 files changed, 207 insertions(+), 3 deletions(-) create mode 100644 Documentation/filesystems/cifs/cifsroot.txt create mode 100644 fs/cifs/cifsroot.c diff --git a/Documentation/filesystems/cifs/cifsroot.txt b/Documentation/filesystems/cifs/cifsroot.txt new file mode 100644 index 000000000000..0fa1a2c36a40 --- /dev/null +++ b/Documentation/filesystems/cifs/cifsroot.txt @@ -0,0 +1,97 @@ +Mounting root file system via SMB (cifs.ko) +=========================================== + +Written 2019 by Paulo Alcantara +Written 2019 by Aurelien Aptel + +The CONFIG_CIFS_ROOT option enables experimental root file system +support over the SMB protocol via cifs.ko. + +It introduces a new kernel command-line option called 'cifsroot=' +which will tell the kernel to mount the root file system over the +network by utilizing SMB or CIFS protocol. + +In order to mount, the network stack will also need to be set up by +using 'ip=' config option. For more details, see +Documentation/filesystems/nfs/nfsroot.txt. + +A CIFS root mount currently requires the use of SMB1+UNIX Extensions +which is only supported by the Samba server. SMB1 is the older +deprecated version of the protocol but it has been extended to support +POSIX features (See [1]). The equivalent extensions for the newer +recommended version of the protocol (SMB3) have not been fully +implemented yet which means SMB3 doesn't support some required POSIX +file system objects (e.g. block devices, pipes, sockets). + +As a result, a CIFS root will default to SMB1 for now but the version +to use can nonetheless be changed via the 'vers=' mount option. This +default will change once the SMB3 POSIX extensions are fully +implemented. + +Server configuration +==================== + +To enable SMB1+UNIX extensions you will need to set these global +settings in Samba smb.conf: + + [global] + server min protocol = NT1 + unix extension = yes # default + +Kernel command line +=================== + +root=/dev/cifs + +This is just a virtual device that basically tells the kernel to mount +the root file system via SMB protocol. + +cifsroot=///[,options] + +Enables the kernel to mount the root file system via SMB that are +located in the and specified in this option. + +The default mount options are set in fs/cifs/cifsroot.c. + +server-ip + IPv4 address of the server. + +share + Path to SMB share (rootfs). + +options + Optional mount options. For more information, see mount.cifs(8). + +Examples +======== + +Export root file system as a Samba share in smb.conf file. + +... +[linux] + path = /path/to/rootfs + read only = no + guest ok = yes + force user = root + force group = root + browseable = yes + writeable = yes + admin users = root + public = yes + create mask = 0777 + directory mask = 0777 +... + +Restart smb service. + +# systemctl restart smb + +Test it under QEMU on a kernel built with CONFIG_CIFS_ROOT and +CONFIG_IP_PNP options enabled. + +# qemu-system-x86_64 -enable-kvm -cpu host -m 1024 \ + -kernel /path/to/linux/arch/x86/boot/bzImage -nographic \ + -append "root=/dev/cifs rw ip=dhcp cifsroot=//10.0.2.2/linux,username=foo,password=bar console=ttyS0 3" + + +1: https://wiki.samba.org/index.php/UNIX_Extensions diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index b16219e5dac9..99a22e57660e 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -211,3 +211,11 @@ config CIFS_FSCACHE Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data to be cached locally on disk through the general filesystem cache manager. If unsure, say N. + +config CIFS_ROOT + bool "SMB root file system (Experimental)" + depends on CIFS=y && IP_PNP + help + Enables root file system support over SMB protocol. + + Most people say N here. diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index 41332f20055b..51bae9340842 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -21,3 +21,5 @@ cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o dfs_cache.o cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o + +cifs-$(CONFIG_CIFS_ROOT) += cifsroot.o diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 09b60ec5de3e..54e204589cb9 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -607,6 +607,7 @@ struct smb_vol { __u32 handle_timeout; /* persistent and durable handle timeout in ms */ unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */ __u16 compression; /* compression algorithm 0xFFFF default 0=disabled */ + bool rootfs:1; /* if it's a SMB root file system */ }; /** @@ -764,6 +765,7 @@ struct TCP_Server_Info { * reconnect. */ int nr_targets; + bool noblockcnt; /* use non-blocking connect() */ }; struct cifs_credits { diff --git a/fs/cifs/cifsroot.c b/fs/cifs/cifsroot.c new file mode 100644 index 000000000000..8760d9cbf25e --- /dev/null +++ b/fs/cifs/cifsroot.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SMB root file system support + * + * Copyright (c) 2019 Paulo Alcantara + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_MNT_OPTS \ + "vers=1.0,cifsacl,mfsymlinks,rsize=1048576,wsize=65536,uid=0,gid=0," \ + "hard,rootfs" + +static char root_dev[2048] __initdata = ""; +static char root_opts[1024] __initdata = DEFAULT_MNT_OPTS; + +static __be32 __init parse_srvaddr(char *start, char *end) +{ + char addr[sizeof("aaa.bbb.ccc.ddd")]; + int i = 0; + + while (start < end && i < sizeof(addr) - 1) { + if (isdigit(*start) || *start == '.') + addr[i++] = *start; + start++; + } + addr[i] = '\0'; + return in_aton(addr); +} + +/* cifsroot=///[,options] */ +static int __init cifs_root_setup(char *line) +{ + char *s; + int len; + __be32 srvaddr = htonl(INADDR_NONE); + + ROOT_DEV = Root_CIFS; + + if (strlen(line) > 3 && line[0] == '/' && line[1] == '/') { + s = strchr(&line[2], '/'); + if (!s || s[1] == '\0') + return 1; + + s = strchrnul(s, ','); + len = s - line + 1; + if (len <= sizeof(root_dev)) { + strlcpy(root_dev, line, len); + srvaddr = parse_srvaddr(&line[2], s); + if (*s) { + snprintf(root_opts, sizeof(root_opts), "%s,%s", + DEFAULT_MNT_OPTS, s + 1); + } + } + } + + root_server_addr = srvaddr; + + return 1; +} + +__setup("cifsroot=", cifs_root_setup); + +int __init cifs_root_data(char **dev, char **opts) +{ + if (!root_dev[0] || root_server_addr == htonl(INADDR_NONE)) { + printk(KERN_ERR "Root-CIFS: no SMB server address\n"); + return -1; + } + + *dev = root_dev; + *opts = root_opts; + + return 0; +} diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index e16b6cc1e31b..2850c3ce4391 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -96,7 +96,7 @@ enum { Opt_multiuser, Opt_sloppy, Opt_nosharesock, Opt_persistent, Opt_nopersistent, Opt_resilient, Opt_noresilient, - Opt_domainauto, Opt_rdma, Opt_modesid, + Opt_domainauto, Opt_rdma, Opt_modesid, Opt_rootfs, Opt_compress, /* Mount options which take numeric value */ @@ -266,6 +266,7 @@ static const match_table_t cifs_mount_option_tokens = { { Opt_ignore, "nomand" }, { Opt_ignore, "relatime" }, { Opt_ignore, "_netdev" }, + { Opt_rootfs, "rootfs" }, { Opt_err, NULL } }; @@ -1777,6 +1778,11 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, case Opt_nodfs: vol->nodfs = 1; break; + case Opt_rootfs: +#ifdef CONFIG_CIFS_ROOT + vol->rootfs = true; +#endif + break; case Opt_posixpaths: vol->posix_paths = 1; break; @@ -2727,7 +2733,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info) goto out_err_crypto_release; } - tcp_ses->noblocksnd = volume_info->noblocksnd; + tcp_ses->noblockcnt = volume_info->rootfs; + tcp_ses->noblocksnd = volume_info->noblocksnd || volume_info->rootfs; tcp_ses->noautotune = volume_info->noautotune; tcp_ses->tcp_nodelay = volume_info->sockopt_tcp_nodelay; tcp_ses->rdma = volume_info->rdma; @@ -3873,7 +3880,11 @@ generic_ip_connect(struct TCP_Server_Info *server) socket->sk->sk_sndbuf, socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo); - rc = socket->ops->connect(socket, saddr, slen, 0); + rc = socket->ops->connect(socket, saddr, slen, + server->noblockcnt ? O_NONBLOCK : 0); + + if (rc == -EINPROGRESS) + rc = 0; if (rc < 0) { cifs_dbg(FYI, "Error %d connecting to server\n", rc); sock_release(socket); diff --git a/include/linux/root_dev.h b/include/linux/root_dev.h index bab671b0782f..4e78651371ba 100644 --- a/include/linux/root_dev.h +++ b/include/linux/root_dev.h @@ -8,6 +8,7 @@ enum { Root_NFS = MKDEV(UNNAMED_MAJOR, 255), + Root_CIFS = MKDEV(UNNAMED_MAJOR, 254), Root_RAM0 = MKDEV(RAMDISK_MAJOR, 0), Root_RAM1 = MKDEV(RAMDISK_MAJOR, 1), Root_FD0 = MKDEV(FLOPPY_MAJOR, 0), From c3498185b7898d770f9f7148c78b9daade5e54ec Mon Sep 17 00:00:00 2001 From: Steve French Date: Sun, 15 Sep 2019 22:38:52 -0500 Subject: [PATCH 35/38] smb3: add missing worker function for SMB3 change notify SMB3 change notify is important to allow applications to wait on directory change events of different types (e.g. adding and deleting files from others systems). Add worker functions for this. Acked-by: Aurelien Aptel Signed-off-by: Steve French --- fs/cifs/smb2pdu.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/trace.h | 3 ++ 2 files changed, 88 insertions(+) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index ce647cfdc04f..87066f1af12c 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -3175,6 +3175,91 @@ SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, (void **)&uniqueid, NULL); } +/* + * CHANGE_NOTIFY Request is sent to get notifications on changes to a directory + * See MS-SMB2 2.2.35 and 2.2.36 + */ + +int +SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst, + struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, + u32 completion_filter, bool watch_tree) +{ + struct smb2_change_notify_req *req; + struct kvec *iov = rqst->rq_iov; + unsigned int total_len; + int rc; + + rc = smb2_plain_req_init(SMB2_CHANGE_NOTIFY, tcon, (void **) &req, &total_len); + if (rc) + return rc; + + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + req->OutputBufferLength = SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE; + req->CompletionFilter = cpu_to_le32(completion_filter); + if (watch_tree) + req->Flags = cpu_to_le16(SMB2_WATCH_TREE); + else + req->Flags = 0; + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + return 0; +} + +int +SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, bool watch_tree, + u32 completion_filter) +{ + struct cifs_ses *ses = tcon->ses; + struct smb_rqst rqst; + struct kvec iov[1]; + struct kvec rsp_iov = {NULL, 0}; + int resp_buftype = CIFS_NO_BUFFER; + int flags = 0; + int rc = 0; + + cifs_dbg(FYI, "change notify\n"); + if (!ses || !(ses->server)) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = SMB2_notify_init(xid, &rqst, tcon, persistent_fid, volatile_fid, + completion_filter, watch_tree); + if (rc) + goto cnotify_exit; + + trace_smb3_notify_enter(xid, persistent_fid, tcon->tid, ses->Suid, + (u8)watch_tree, completion_filter); + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); + + if (rc != 0) { + cifs_stats_fail_inc(tcon, SMB2_CHANGE_NOTIFY_HE); + trace_smb3_notify_err(xid, persistent_fid, tcon->tid, ses->Suid, + (u8)watch_tree, completion_filter, rc); + } else + trace_smb3_notify_done(xid, persistent_fid, tcon->tid, + ses->Suid, (u8)watch_tree, completion_filter); + + cnotify_exit: + if (rqst.rq_iov) + cifs_small_buf_release(rqst.rq_iov[0].iov_base); /* request */ + free_rsp_buf(resp_buftype, rsp_iov.iov_base); + return rc; +} + + + /* * This is a no-op for now. We're not really interested in the reply, but * rather in the fact that the server sent one and that server->lstrp diff --git a/fs/cifs/trace.h b/fs/cifs/trace.h index f1339f4a2d07..e7e350b13d6a 100644 --- a/fs/cifs/trace.h +++ b/fs/cifs/trace.h @@ -235,6 +235,8 @@ DEFINE_EVENT(smb3_inf_enter_class, smb3_##name, \ DEFINE_SMB3_INF_ENTER_EVENT(query_info_enter); DEFINE_SMB3_INF_ENTER_EVENT(query_info_done); +DEFINE_SMB3_INF_ENTER_EVENT(notify_enter); +DEFINE_SMB3_INF_ENTER_EVENT(notify_done); DECLARE_EVENT_CLASS(smb3_inf_err_class, TP_PROTO(unsigned int xid, @@ -281,6 +283,7 @@ DEFINE_EVENT(smb3_inf_err_class, smb3_##name, \ DEFINE_SMB3_INF_ERR_EVENT(query_info_err); DEFINE_SMB3_INF_ERR_EVENT(set_info_err); +DEFINE_SMB3_INF_ERR_EVENT(notify_err); DEFINE_SMB3_INF_ERR_EVENT(fsctl_err); DECLARE_EVENT_CLASS(smb3_inf_compound_enter_class, From 352f2c9a570d38bced287a273e79dccae7c5ef41 Mon Sep 17 00:00:00 2001 From: Aurelien Aptel Date: Mon, 16 Sep 2019 05:45:42 +0200 Subject: [PATCH 36/38] cifs: cifsroot: add more err checking make cifs more verbose about buffer size errors and add some comments Signed-off-by: Aurelien Aptel Signed-off-by: Steve French --- fs/cifs/cifsroot.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/fs/cifs/cifsroot.c b/fs/cifs/cifsroot.c index 8760d9cbf25e..37edbfb8e096 100644 --- a/fs/cifs/cifsroot.c +++ b/fs/cifs/cifsroot.c @@ -24,6 +24,7 @@ static char root_opts[1024] __initdata = DEFAULT_MNT_OPTS; static __be32 __init parse_srvaddr(char *start, char *end) { + /* TODO: ipv6 support */ char addr[sizeof("aaa.bbb.ccc.ddd")]; int i = 0; @@ -50,14 +51,24 @@ static int __init cifs_root_setup(char *line) if (!s || s[1] == '\0') return 1; + /* make s point to ',' or '\0' at end of line */ s = strchrnul(s, ','); + /* len is strlen(unc) + '\0' */ len = s - line + 1; - if (len <= sizeof(root_dev)) { - strlcpy(root_dev, line, len); - srvaddr = parse_srvaddr(&line[2], s); - if (*s) { - snprintf(root_opts, sizeof(root_opts), "%s,%s", + if (len > sizeof(root_dev)) { + printk(KERN_ERR "Root-CIFS: UNC path too long\n"); + return 1; + } + strlcpy(root_dev, line, len); + srvaddr = parse_srvaddr(&line[2], s); + if (*s) { + int n = snprintf(root_opts, + sizeof(root_opts), "%s,%s", DEFAULT_MNT_OPTS, s + 1); + if (n >= sizeof(root_opts)) { + printk(KERN_ERR "Root-CIFS: mount options string too long\n"); + root_opts[sizeof(root_opts)-1] = '\0'; + return 1; } } } From e37a02c7ebb9fa9af301c47c8c5a579cc51b509b Mon Sep 17 00:00:00 2001 From: Aurelien Aptel Date: Tue, 17 Sep 2019 01:47:27 +0200 Subject: [PATCH 37/38] cifs: modefromsid: write mode ACE first DACL should start with mode ACE first but we are putting it at the end. reorder them to put it first. Signed-off-by: Aurelien Aptel Signed-off-by: Steve French --- fs/cifs/cifsacl.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 3e0c5ed9ca20..f842944a5c76 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -809,30 +809,21 @@ static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, __u64 nmode, bool modefromsid) { u16 size = 0; + u32 num_aces = 0; struct cifs_acl *pnndacl; pnndacl = (struct cifs_acl *)((char *)pndacl + sizeof(struct cifs_acl)); - size += fill_ace_for_sid((struct cifs_ace *) ((char *)pnndacl + size), - pownersid, nmode, S_IRWXU); - size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size), - pgrpsid, nmode, S_IRWXG); - size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size), - &sid_everyone, nmode, S_IRWXO); - - /* TBD: Move this ACE to the top of ACE list instead of bottom */ if (modefromsid) { struct cifs_ace *pntace = (struct cifs_ace *)((char *)pnndacl + size); int i; - pntace->type = ACCESS_DENIED; + pntace->type = ACCESS_ALLOWED; pntace->flags = 0x0; + pntace->access_req = 0; pntace->sid.num_subauth = 3; pntace->sid.revision = 1; - /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4) */ - pntace->size = cpu_to_le16(28); - size += 28; for (i = 0; i < NUM_AUTHS; i++) pntace->sid.authority[i] = sid_unix_NFS_mode.authority[i]; @@ -840,12 +831,23 @@ static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid, pntace->sid.sub_auth[1] = sid_unix_NFS_mode.sub_auth[1]; pntace->sid.sub_auth[2] = cpu_to_le32(nmode & 07777); - pndacl->num_aces = cpu_to_le32(4); - size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size), - &sid_unix_NFS_mode, nmode, S_IRWXO); - } else - pndacl->num_aces = cpu_to_le32(3); + /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ + pntace->size = cpu_to_le16(28); + size += 28; + num_aces++; + } + size += fill_ace_for_sid((struct cifs_ace *) ((char *)pnndacl + size), + pownersid, nmode, S_IRWXU); + num_aces++; + size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size), + pgrpsid, nmode, S_IRWXG); + num_aces++; + size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size), + &sid_everyone, nmode, S_IRWXO); + num_aces++; + + pndacl->num_aces = cpu_to_le32(num_aces); pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl)); return 0; From 4d6bcba70aeb4a512ead9c9eaf9edc6bbab00b14 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 13 Sep 2019 16:59:21 -0500 Subject: [PATCH 38/38] cifs: update internal module version number To 2.23 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 99caf77df4a2..bc4ca94137f2 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -152,5 +152,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.22" +#define CIFS_VERSION "2.23" #endif /* _CIFSFS_H */