diff options
-rw-r--r-- | fs/smb/client/cached_dir.c | 99 | ||||
-rw-r--r-- | fs/smb/client/cifs_spnego.c | 16 | ||||
-rw-r--r-- | fs/smb/client/cifsacl.c | 54 | ||||
-rw-r--r-- | fs/smb/client/cifsfs.c | 25 | ||||
-rw-r--r-- | fs/smb/client/cifsglob.h | 11 | ||||
-rw-r--r-- | fs/smb/client/cifsproto.h | 11 | ||||
-rw-r--r-- | fs/smb/client/cifssmb.c | 65 | ||||
-rw-r--r-- | fs/smb/client/connect.c | 20 | ||||
-rw-r--r-- | fs/smb/client/dfs_cache.c | 8 | ||||
-rw-r--r-- | fs/smb/client/fs_context.c | 39 | ||||
-rw-r--r-- | fs/smb/client/fs_context.h | 10 | ||||
-rw-r--r-- | fs/smb/client/inode.c | 26 | ||||
-rw-r--r-- | fs/smb/client/reparse.c | 5 | ||||
-rw-r--r-- | fs/smb/client/reparse.h | 2 | ||||
-rw-r--r-- | fs/smb/client/smb2ops.c | 39 | ||||
-rw-r--r-- | fs/smb/client/smb2pdu.c | 11 | ||||
-rw-r--r-- | fs/smb/client/smb2proto.h | 2 | ||||
-rw-r--r-- | fs/smb/client/smb2transport.c | 56 | ||||
-rw-r--r-- | fs/smb/client/transport.c | 40 |
19 files changed, 293 insertions, 246 deletions
diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c index 0ff2491c311d..06eb19dabb0e 100644 --- a/fs/smb/client/cached_dir.c +++ b/fs/smb/client/cached_dir.c @@ -59,6 +59,16 @@ static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids, list_add(&cfid->entry, &cfids->entries); cfid->on_list = true; kref_get(&cfid->refcount); + /* + * Set @cfid->has_lease to true during construction so that the lease + * reference can be put in cached_dir_lease_break() due to a potential + * lease break right after the request is sent or while @cfid is still + * being cached, or if a reconnection is triggered during construction. + * Concurrent processes won't be to use it yet due to @cfid->time being + * zero. + */ + cfid->has_lease = true; + spin_unlock(&cfids->cfid_list_lock); return cfid; } @@ -176,12 +186,12 @@ replay_again: return -ENOENT; } /* - * Return cached fid if it has a lease. Otherwise, it is either a new - * entry or laundromat worker removed it from @cfids->entries. Caller - * will put last reference if the latter. + * Return cached fid if it is valid (has a lease and has a time). + * Otherwise, it is either a new entry or laundromat worker removed it + * from @cfids->entries. Caller will put last reference if the latter. */ spin_lock(&cfids->cfid_list_lock); - if (cfid->has_lease) { + if (cfid->has_lease && cfid->time) { spin_unlock(&cfids->cfid_list_lock); *ret_cfid = cfid; kfree(utf16_path); @@ -267,15 +277,6 @@ replay_again: smb2_set_related(&rqst[1]); - /* - * Set @cfid->has_lease to true before sending out compounded request so - * its lease reference can be put in cached_dir_lease_break() due to a - * potential lease break right after the request is sent or while @cfid - * is still being cached. Concurrent processes won't be to use it yet - * due to @cfid->time being zero. - */ - cfid->has_lease = true; - if (retries) { smb2_set_replay(server, &rqst[0]); smb2_set_replay(server, &rqst[1]); @@ -347,6 +348,7 @@ oshr_free: SMB2_query_info_free(&rqst[1]); free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); +out: if (rc) { spin_lock(&cfids->cfid_list_lock); if (cfid->on_list) { @@ -358,23 +360,14 @@ oshr_free: /* * We are guaranteed to have two references at this * point. One for the caller and one for a potential - * lease. Release the Lease-ref so that the directory - * will be closed when the caller closes the cached - * handle. + * lease. Release one here, and the second below. */ cfid->has_lease = false; - spin_unlock(&cfids->cfid_list_lock); kref_put(&cfid->refcount, smb2_close_cached_fid); - goto out; } spin_unlock(&cfids->cfid_list_lock); - } -out: - if (rc) { - if (cfid->is_open) - SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid, - cfid->fid.volatile_fid); - free_cached_dir(cfid); + + kref_put(&cfid->refcount, smb2_close_cached_fid); } else { *ret_cfid = cfid; atomic_inc(&tcon->num_remote_opens); @@ -401,7 +394,7 @@ int open_cached_dir_by_dentry(struct cifs_tcon *tcon, spin_lock(&cfids->cfid_list_lock); list_for_each_entry(cfid, &cfids->entries, entry) { if (dentry && cfid->dentry == dentry) { - cifs_dbg(FYI, "found a cached root file handle by dentry\n"); + cifs_dbg(FYI, "found a cached file handle by dentry\n"); kref_get(&cfid->refcount); *ret_cfid = cfid; spin_unlock(&cfids->cfid_list_lock); @@ -512,25 +505,24 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon) cfids->num_entries--; cfid->is_open = false; cfid->on_list = false; - /* To prevent race with smb2_cached_lease_break() */ - kref_get(&cfid->refcount); + if (cfid->has_lease) { + /* + * The lease was never cancelled from the server, + * so steal that reference. + */ + cfid->has_lease = false; + } else + kref_get(&cfid->refcount); } spin_unlock(&cfids->cfid_list_lock); list_for_each_entry_safe(cfid, q, &entry, entry) { list_del(&cfid->entry); cancel_work_sync(&cfid->lease_break); - if (cfid->has_lease) { - /* - * We lease was never cancelled from the server so we - * need to drop the reference. - */ - spin_lock(&cfids->cfid_list_lock); - cfid->has_lease = false; - spin_unlock(&cfids->cfid_list_lock); - kref_put(&cfid->refcount, smb2_close_cached_fid); - } - /* Drop the extra reference opened above*/ + /* + * Drop the ref-count from above, either the lease-ref (if there + * was one) or the extra one acquired. + */ kref_put(&cfid->refcount, smb2_close_cached_fid); } } @@ -541,9 +533,6 @@ smb2_cached_lease_break(struct work_struct *work) struct cached_fid *cfid = container_of(work, struct cached_fid, lease_break); - spin_lock(&cfid->cfids->cfid_list_lock); - cfid->has_lease = false; - spin_unlock(&cfid->cfids->cfid_list_lock); kref_put(&cfid->refcount, smb2_close_cached_fid); } @@ -561,6 +550,7 @@ int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]) !memcmp(lease_key, cfid->fid.lease_key, SMB2_LEASE_KEY_SIZE)) { + cfid->has_lease = false; cfid->time = 0; /* * We found a lease remove it from the list @@ -638,8 +628,14 @@ static void cfids_laundromat_worker(struct work_struct *work) cfid->on_list = false; list_move(&cfid->entry, &entry); cfids->num_entries--; - /* To prevent race with smb2_cached_lease_break() */ - kref_get(&cfid->refcount); + if (cfid->has_lease) { + /* + * Our lease has not yet been cancelled from the + * server. Steal that reference. + */ + cfid->has_lease = false; + } else + kref_get(&cfid->refcount); } } spin_unlock(&cfids->cfid_list_lock); @@ -651,17 +647,10 @@ static void cfids_laundromat_worker(struct work_struct *work) * with it. */ cancel_work_sync(&cfid->lease_break); - if (cfid->has_lease) { - /* - * Our lease has not yet been cancelled from the server - * so we need to drop the reference. - */ - spin_lock(&cfids->cfid_list_lock); - cfid->has_lease = false; - spin_unlock(&cfids->cfid_list_lock); - kref_put(&cfid->refcount, smb2_close_cached_fid); - } - /* Drop the extra reference opened above */ + /* + * Drop the ref-count from above, either the lease-ref (if there + * was one) or the extra one acquired. + */ kref_put(&cfid->refcount, smb2_close_cached_fid); } queue_delayed_work(cifsiod_wq, &cfids->laundromat_work, diff --git a/fs/smb/client/cifs_spnego.c b/fs/smb/client/cifs_spnego.c index af7849e5974f..28f568b5fc27 100644 --- a/fs/smb/client/cifs_spnego.c +++ b/fs/smb/client/cifs_spnego.c @@ -82,6 +82,9 @@ struct key_type cifs_spnego_key_type = { /* strlen of ";pid=0x" */ #define PID_KEY_LEN 7 +/* strlen of ";upcall_target=" */ +#define UPCALL_TARGET_KEY_LEN 15 + /* get a key struct with a SPNEGO security blob, suitable for session setup */ struct key * cifs_get_spnego_key(struct cifs_ses *sesInfo, @@ -108,6 +111,11 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo, if (sesInfo->user_name) desc_len += USER_KEY_LEN + strlen(sesInfo->user_name); + if (sesInfo->upcall_target == UPTARGET_MOUNT) + desc_len += UPCALL_TARGET_KEY_LEN + 5; // strlen("mount") + else + desc_len += UPCALL_TARGET_KEY_LEN + 3; // strlen("app") + spnego_key = ERR_PTR(-ENOMEM); description = kzalloc(desc_len, GFP_KERNEL); if (description == NULL) @@ -156,6 +164,14 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo, dp = description + strlen(description); sprintf(dp, ";pid=0x%x", current->pid); + if (sesInfo->upcall_target == UPTARGET_MOUNT) { + dp = description + strlen(description); + sprintf(dp, ";upcall_target=mount"); + } else { + dp = description + strlen(description); + sprintf(dp, ";upcall_target=app"); + } + cifs_dbg(FYI, "key description = %s\n", description); saved_cred = override_creds(spnego_cred); spnego_key = request_key(&cifs_spnego_key_type, description, ""); diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c index 1d294d53f662..ba79aa2107cc 100644 --- a/fs/smb/client/cifsacl.c +++ b/fs/smb/client/cifsacl.c @@ -885,12 +885,17 @@ unsigned int setup_authusers_ACE(struct smb_ace *pntace) * Fill in the special SID based on the mode. See * https://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx */ -unsigned int setup_special_mode_ACE(struct smb_ace *pntace, __u64 nmode) +unsigned int setup_special_mode_ACE(struct smb_ace *pntace, + bool posix, + __u64 nmode) { int i; unsigned int ace_size = 28; - pntace->type = ACCESS_DENIED_ACE_TYPE; + if (posix) + pntace->type = ACCESS_ALLOWED_ACE_TYPE; + else + pntace->type = ACCESS_DENIED_ACE_TYPE; pntace->flags = 0x0; pntace->access_req = 0; pntace->sid.num_subauth = 3; @@ -933,7 +938,8 @@ static void populate_new_aces(char *nacl_base, struct smb_sid *pownersid, struct smb_sid *pgrpsid, __u64 *pnmode, u32 *pnum_aces, u16 *pnsize, - bool modefromsid) + bool modefromsid, + bool posix) { __u64 nmode; u32 num_aces = 0; @@ -950,13 +956,15 @@ static void populate_new_aces(char *nacl_base, num_aces = *pnum_aces; nsize = *pnsize; - if (modefromsid) { - pnntace = (struct smb_ace *) (nacl_base + nsize); - nsize += setup_special_mode_ACE(pnntace, nmode); - num_aces++; + if (modefromsid || posix) { pnntace = (struct smb_ace *) (nacl_base + nsize); - nsize += setup_authusers_ACE(pnntace); + nsize += setup_special_mode_ACE(pnntace, posix, nmode); num_aces++; + if (modefromsid) { + pnntace = (struct smb_ace *) (nacl_base + nsize); + nsize += setup_authusers_ACE(pnntace); + num_aces++; + } goto set_size; } @@ -1076,7 +1084,7 @@ static __u16 replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *p static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl, struct smb_sid *pownersid, struct smb_sid *pgrpsid, - __u64 *pnmode, bool mode_from_sid) + __u64 *pnmode, bool mode_from_sid, bool posix) { int i; u16 size = 0; @@ -1094,11 +1102,11 @@ static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl, nsize = sizeof(struct smb_acl); /* If pdacl is NULL, we don't have a src. Simply populate new ACL. */ - if (!pdacl) { + if (!pdacl || posix) { populate_new_aces(nacl_base, pownersid, pgrpsid, pnmode, &num_aces, &nsize, - mode_from_sid); + mode_from_sid, posix); goto finalize_dacl; } @@ -1115,7 +1123,7 @@ static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl, populate_new_aces(nacl_base, pownersid, pgrpsid, pnmode, &num_aces, &nsize, - mode_from_sid); + mode_from_sid, posix); new_aces_set = true; } @@ -1144,7 +1152,7 @@ next_ace: populate_new_aces(nacl_base, pownersid, pgrpsid, pnmode, &num_aces, &nsize, - mode_from_sid); + mode_from_sid, posix); new_aces_set = true; } @@ -1251,7 +1259,7 @@ 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 smb_ntsd *pntsd, struct smb_ntsd *pnntsd, __u32 secdesclen, __u32 *pnsecdesclen, __u64 *pnmode, kuid_t uid, kgid_t gid, - bool mode_from_sid, bool id_from_sid, int *aclflag) + bool mode_from_sid, bool id_from_sid, bool posix, int *aclflag) { int rc = 0; __u32 dacloffset; @@ -1288,7 +1296,7 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd, ndacl_ptr->num_aces = cpu_to_le32(0); rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, - pnmode, mode_from_sid); + pnmode, mode_from_sid, posix); sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); /* copy the non-dacl portion of secdesc */ @@ -1584,13 +1592,16 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, struct smb_ntsd *pntsd = NULL; /* acl obtained from server */ struct smb_ntsd *pnntsd = NULL; /* modified acl to be sent to server */ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + struct tcon_link *tlink; struct smb_version_operations *ops; bool mode_from_sid, id_from_sid; const u32 info = 0; + bool posix; + tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) return PTR_ERR(tlink); + posix = tlink_tcon(tlink)->posix_extensions; ops = tlink_tcon(tlink)->ses->server->ops; @@ -1622,12 +1633,13 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, id_from_sid = false; /* Potentially, five new ACEs can be added to the ACL for U,G,O mapping */ - nsecdesclen = secdesclen; if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */ - if (mode_from_sid) - nsecdesclen += 2 * sizeof(struct smb_ace); + if (posix) + nsecdesclen = 1 * sizeof(struct smb_ace); + else if (mode_from_sid) + nsecdesclen = secdesclen + (2 * sizeof(struct smb_ace)); else /* cifsacl */ - nsecdesclen += 5 * sizeof(struct smb_ace); + nsecdesclen = secdesclen + (5 * sizeof(struct smb_ace)); } else { /* chown */ /* When ownership changes, changes new owner sid length could be different */ nsecdesclen = sizeof(struct smb_ntsd) + (sizeof(struct smb_sid) * 2); @@ -1657,7 +1669,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, } rc = build_sec_desc(pntsd, pnntsd, secdesclen, &nsecdesclen, pnmode, uid, gid, - mode_from_sid, id_from_sid, &aclflag); + mode_from_sid, id_from_sid, posix, &aclflag); cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc); diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index 20cafdff5081..979853471027 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -546,6 +546,30 @@ static int cifs_show_devname(struct seq_file *m, struct dentry *root) return 0; } +static void +cifs_show_upcall_target(struct seq_file *s, struct cifs_sb_info *cifs_sb) +{ + if (cifs_sb->ctx->upcall_target == UPTARGET_UNSPECIFIED) { + seq_puts(s, ",upcall_target=app"); + return; + } + + seq_puts(s, ",upcall_target="); + + switch (cifs_sb->ctx->upcall_target) { + case UPTARGET_APP: + seq_puts(s, "app"); + break; + case UPTARGET_MOUNT: + seq_puts(s, "mount"); + break; + default: + /* shouldn't ever happen */ + seq_puts(s, "unknown"); + break; + } +} + /* * cifs_show_options() is for displaying mount options in /proc/mounts. * Not all settable options are displayed but most of the important @@ -562,6 +586,7 @@ cifs_show_options(struct seq_file *s, struct dentry *root) seq_show_option(s, "vers", tcon->ses->server->vals->version_string); cifs_show_security(s, tcon->ses); cifs_show_cache_flavor(s, cifs_sb); + cifs_show_upcall_target(s, cifs_sb); if (tcon->no_lease) seq_puts(s, ",nolease"); diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 5041b1ffc244..fc33dfe7e925 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -153,6 +153,12 @@ enum securityEnum { Kerberos, /* Kerberos via SPNEGO */ }; +enum upcall_target_enum { + UPTARGET_UNSPECIFIED, /* not specified, defaults to app */ + UPTARGET_MOUNT, /* upcall to the mount namespace */ + UPTARGET_APP, /* upcall to the application namespace which did the mount */ +}; + enum cifs_reparse_type { CIFS_REPARSE_TYPE_NFS, CIFS_REPARSE_TYPE_WSL, @@ -1084,6 +1090,7 @@ struct cifs_ses { struct session_key auth_key; struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ enum securityEnum sectype; /* what security flavor was specified? */ + enum upcall_target_enum upcall_target; /* what upcall target was specified? */ bool sign; /* is signing required? */ bool domainAuto:1; bool expired_pwd; /* track if access denied or expired pwd so can know if need to update */ @@ -2223,7 +2230,7 @@ static inline int cifs_get_num_sgs(const struct smb_rqst *rqst, struct kvec *iov = &rqst[i].rq_iov[j]; addr = (unsigned long)iov->iov_base + skip; - if (unlikely(is_vmalloc_addr((void *)addr))) { + if (is_vmalloc_or_module_addr((void *)addr)) { len = iov->iov_len - skip; nents += DIV_ROUND_UP(offset_in_page(addr) + len, PAGE_SIZE); @@ -2250,7 +2257,7 @@ static inline void cifs_sg_set_buf(struct sg_table *sgtable, unsigned int off = offset_in_page(addr); addr &= PAGE_MASK; - if (unlikely(is_vmalloc_addr((void *)addr))) { + if (is_vmalloc_or_module_addr((void *)addr)) { do { unsigned int len = min_t(unsigned int, buflen, PAGE_SIZE - off); diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h index 1d3470bca45e..075985bfb13a 100644 --- a/fs/smb/client/cifsproto.h +++ b/fs/smb/client/cifsproto.h @@ -244,7 +244,9 @@ extern int cifs_set_acl(struct mnt_idmap *idmap, extern int set_cifs_acl(struct smb_ntsd *pntsd, __u32 len, struct inode *ino, const char *path, int flag); extern unsigned int setup_authusers_ACE(struct smb_ace *pace); -extern unsigned int setup_special_mode_ACE(struct smb_ace *pace, __u64 nmode); +extern unsigned int setup_special_mode_ACE(struct smb_ace *pace, + bool posix, + __u64 nmode); extern unsigned int setup_special_user_owner_ACE(struct smb_ace *pace); extern void dequeue_mid(struct mid_q_entry *mid, bool malformed); @@ -549,13 +551,6 @@ extern int generate_smb311signingkey(struct cifs_ses *ses, struct TCP_Server_Info *server); #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -extern int CIFSSMBCopy(unsigned int xid, - struct cifs_tcon *source_tcon, - const char *fromName, - const __u16 target_tid, - const char *toName, const int flags, - const struct nls_table *nls_codepage, - int remap_special_chars); extern ssize_t CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon, const unsigned char *searchName, const unsigned char *ea_name, char *EAData, diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c index c6f15dbe860a..4858331ee918 100644 --- a/fs/smb/client/cifssmb.c +++ b/fs/smb/client/cifssmb.c @@ -2340,69 +2340,6 @@ int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *pTcon, } int -CIFSSMBCopy(const unsigned int xid, struct cifs_tcon *tcon, - const char *fromName, const __u16 target_tid, const char *toName, - const int flags, const struct nls_table *nls_codepage, int remap) -{ - int rc = 0; - COPY_REQ *pSMB = NULL; - COPY_RSP *pSMBr = NULL; - int bytes_returned; - int name_len, name_len2; - __u16 count; - - cifs_dbg(FYI, "In CIFSSMBCopy\n"); -copyRetry: - rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - pSMB->BufferFormat = 0x04; - pSMB->Tid2 = target_tid; - - pSMB->Flags = cpu_to_le16(flags & COPY_TREE); - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName, - fromName, PATH_MAX, nls_codepage, - remap); - name_len++; /* trailing null */ - name_len *= 2; - pSMB->OldFileName[name_len] = 0x04; /* pad */ - /* protocol requires ASCII signature byte on Unicode string */ - pSMB->OldFileName[name_len + 1] = 0x00; - name_len2 = - cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], - toName, PATH_MAX, nls_codepage, remap); - name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; - name_len2 *= 2; /* convert to bytes */ - } else { - name_len = copy_path_name(pSMB->OldFileName, fromName); - pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ - name_len2 = copy_path_name(pSMB->OldFileName+name_len+1, toName); - name_len2++; /* signature byte */ - } - - count = 1 /* 1st signature byte */ + name_len + name_len2; - inc_rfc1001_len(pSMB, count); - pSMB->ByteCount = cpu_to_le16(count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in copy = %d with %d files copied\n", - rc, le16_to_cpu(pSMBr->CopyCount)); - } - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto copyRetry; - - return rc; -} - -int CIFSUnixCreateSymLink(const unsigned int xid, struct cifs_tcon *tcon, const char *fromName, const char *toName, const struct nls_table *nls_codepage, int remap) @@ -5406,7 +5343,7 @@ SetTimesRetry: param_offset = offsetof(struct smb_com_transaction2_spi_req, InformationLevel) - 4; offset = param_offset + params; - data_offset = (char *) (&pSMB->hdr.Protocol) + offset; + data_offset = (char *)pSMB + offsetof(typeof(*pSMB), hdr.Protocol) + offset; pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->DataOffset = cpu_to_le16(offset); pSMB->SetupCount = 1; diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index 0ce2d704b1f3..0a97228c06b1 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -2339,6 +2339,26 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) ses->sectype = ctx->sectype; ses->sign = ctx->sign; + + /* + *Explicitly marking upcall_target mount option for easier handling + * by cifs_spnego.c and eventually cifs.upcall.c + */ + + switch (ctx->upcall_target) { + case UPTARGET_UNSPECIFIED: /* default to app */ + case UPTARGET_APP: + ses->upcall_target = UPTARGET_APP; + break; + case UPTARGET_MOUNT: + ses->upcall_target = UPTARGET_MOUNT; + break; + default: + // should never happen + ses->upcall_target = UPTARGET_APP; + break; + } + ses->local_nls = load_nls(ctx->local_nls->charset); /* add server as first channel */ diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c index 110f03df012a..00820f57b434 100644 --- a/fs/smb/client/dfs_cache.c +++ b/fs/smb/client/dfs_cache.c @@ -173,8 +173,8 @@ static int dfscache_proc_show(struct seq_file *m, void *v) "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n", ce->path, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl, ce->etime.tv_nsec, ce->hdr_flags, ce->ref_flags, - DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no", - ce->path_consumed, cache_entry_expired(ce) ? "yes" : "no"); + str_yes_no(DFS_INTERLINK(ce->hdr_flags)), + ce->path_consumed, str_yes_no(cache_entry_expired(ce))); list_for_each_entry(t, &ce->tlist, list) { seq_printf(m, " %s%s\n", @@ -242,9 +242,9 @@ static inline void dump_ce(const struct cache_entry *ce) ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl, ce->etime.tv_nsec, ce->hdr_flags, ce->ref_flags, - DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no", + str_yes_no(DFS_INTERLINK(ce->hdr_flags)), ce->path_consumed, - cache_entry_expired(ce) ? "yes" : "no"); + str_yes_no(cache_entry_expired(ce))); dump_tgts(ce); } diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c index 5c5a52019efa..c87879e4739b 100644 --- a/fs/smb/client/fs_context.c +++ b/fs/smb/client/fs_context.c @@ -67,6 +67,12 @@ static const match_table_t cifs_secflavor_tokens = { { Opt_sec_err, NULL } }; +static const match_table_t cifs_upcall_target = { + { Opt_upcall_target_mount, "mount" }, + { Opt_upcall_target_application, "app" }, + { Opt_upcall_target_err, NULL } +}; + const struct fs_parameter_spec smb3_fs_parameters[] = { /* Mount options that take no arguments */ fsparam_flag_no("user_xattr", Opt_user_xattr), @@ -178,6 +184,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = { fsparam_string("sec", Opt_sec), fsparam_string("cache", Opt_cache), fsparam_string("reparse", Opt_reparse), + fsparam_string("upcall_target", Opt_upcalltarget), /* Arguments that should be ignored */ fsparam_flag("guest", Opt_ignore), @@ -248,6 +255,29 @@ cifs_parse_security_flavors(struct fs_context *fc, char *value, struct smb3_fs_c return 0; } +static int +cifs_parse_upcall_target(struct fs_context *fc, char *value, struct smb3_fs_context *ctx) +{ + substring_t args[MAX_OPT_ARGS]; + + ctx->upcall_target = UPTARGET_UNSPECIFIED; + + switch (match_token(value, cifs_upcall_target, args)) { + case Opt_upcall_target_mount: + ctx->upcall_target = UPTARGET_MOUNT; + break; + case Opt_upcall_target_application: + ctx->upcall_target = UPTARGET_APP; + break; + + default: + cifs_errorf(fc, "bad upcall target: %s\n", value); + return 1; + } + + return 0; +} + static const match_table_t cifs_cacheflavor_tokens = { { Opt_cache_loose, "loose" }, { Opt_cache_strict, "strict" }, @@ -1450,6 +1480,10 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, if (cifs_parse_security_flavors(fc, param->string, ctx) != 0) goto cifs_parse_mount_err; break; + case Opt_upcalltarget: + if (cifs_parse_upcall_target(fc, param->string, ctx) != 0) + goto cifs_parse_mount_err; + break; case Opt_cache: if (cifs_parse_cache_flavor(fc, param->string, ctx) != 0) goto cifs_parse_mount_err; @@ -1627,6 +1661,11 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, } /* case Opt_ignore: - is ignored as expected ... */ + if (ctx->multiuser && ctx->upcall_target == UPTARGET_MOUNT) { + cifs_errorf(fc, "multiuser mount option not supported with upcalltarget set as 'mount'\n"); + goto cifs_parse_mount_err; + } + return 0; cifs_parse_mount_err: diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h index 890d6d9d4a59..67b7fc48ac58 100644 --- a/fs/smb/client/fs_context.h +++ b/fs/smb/client/fs_context.h @@ -61,6 +61,12 @@ enum cifs_sec_param { Opt_sec_err }; +enum cifs_upcall_target_param { + Opt_upcall_target_mount, + Opt_upcall_target_application, + Opt_upcall_target_err +}; + enum cifs_param { /* Mount options that take no arguments */ Opt_user_xattr, @@ -114,6 +120,8 @@ enum cifs_param { Opt_multichannel, Opt_compress, Opt_witness, + Opt_is_upcall_target_mount, + Opt_is_upcall_target_application, /* Mount options which take numeric value */ Opt_backupuid, @@ -157,6 +165,7 @@ enum cifs_param { Opt_sec, Opt_cache, Opt_reparse, + Opt_upcalltarget, /* Mount options to be ignored */ Opt_ignore, @@ -198,6 +207,7 @@ struct smb3_fs_context { umode_t file_mode; umode_t dir_mode; enum securityEnum sectype; /* sectype requested via mnt opts */ + enum upcall_target_enum upcall_target; /* where to upcall for mount */ bool sign; /* was signing requested via mnt opts? */ bool ignore_signature:1; bool retry:1; diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index eff3f57235ee..de8063b44072 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -598,6 +598,17 @@ cifs_sfu_type(struct cifs_fattr *fattr, const char *path, mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); fattr->cf_rdev = MKDEV(mjr, mnr); + } else if (bytes_read == 16) { + /* + * Windows NFS server before Windows Server 2012 + * stores major and minor number in SFU-modified + * style, just as 32-bit numbers. Recognize it. + */ + __u32 mjr; /* major */ + __u32 mnr; /* minor */ + mjr = le32_to_cpu(*(__le32 *)(pbuf+8)); + mnr = le32_to_cpu(*(__le32 *)(pbuf+12)); + fattr->cf_rdev = MKDEV(mjr, mnr); } } else if (memcmp("IntxCHR\0", pbuf, 8) == 0) { cifs_dbg(FYI, "Char device\n"); @@ -610,6 +621,17 @@ cifs_sfu_type(struct cifs_fattr *fattr, const char *path, mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); fattr->cf_rdev = MKDEV(mjr, mnr); + } else if (bytes_read == 16) { + /* + * Windows NFS server before Windows Server 2012 + * stores major and minor number in SFU-modified + * style, just as 32-bit numbers. Recognize it. + */ + __u32 mjr; /* major */ + __u32 mnr; /* minor */ + mjr = le32_to_cpu(*(__le32 *)(pbuf+8)); + mnr = le32_to_cpu(*(__le32 *)(pbuf+12)); + fattr->cf_rdev = MKDEV(mjr, mnr); } } else if (memcmp("LnxSOCK", pbuf, 8) == 0) { cifs_dbg(FYI, "Socket\n"); @@ -3062,6 +3084,7 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) int rc = -EACCES; __u32 dosattr = 0; __u64 mode = NO_CHANGE_64; + bool posix = cifs_sb_master_tcon(cifs_sb)->posix_extensions; xid = get_xid(); @@ -3152,7 +3175,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) mode = attrs->ia_mode; rc = 0; if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) { + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) || + posix) { rc = id_mode_to_cifs_acl(inode, full_path, &mode, INVALID_UID, INVALID_GID); if (rc) { diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c index 74abbdf5026c..90da1e2b6217 100644 --- a/fs/smb/client/reparse.c +++ b/fs/smb/client/reparse.c @@ -35,6 +35,9 @@ int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode, u16 len, plen; int rc = 0; + if (strlen(symname) > REPARSE_SYM_PATH_MAX) + return -ENAMETOOLONG; + sym = kstrdup(symname, GFP_KERNEL); if (!sym) return -ENOMEM; @@ -64,7 +67,7 @@ int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode, if (rc < 0) goto out; - plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX); + plen = 2 * UniStrnlen((wchar_t *)path, REPARSE_SYM_PATH_MAX); len = sizeof(*buf) + plen * 2; buf = kzalloc(len, GFP_KERNEL); if (!buf) { diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h index 158e7b7aae64..2a9f4f9f79de 100644 --- a/fs/smb/client/reparse.h +++ b/fs/smb/client/reparse.h @@ -12,6 +12,8 @@ #include "fs_context.h" #include "cifsglob.h" +#define REPARSE_SYM_PATH_MAX 4060 + /* * Used only by cifs.ko to ignore reparse points from files when client or * server doesn't support FSCTL_GET_REPARSE_POINT. diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 24a2aa04a108..fa96ebed8310 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -2606,7 +2606,7 @@ smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst) struct cifs_ses *ses = tcon->ses; struct TCP_Server_Info *server = ses->server; unsigned long len = smb_rqst_len(server, rqst); - int i, num_padding; + int num_padding; shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base); if (shdr == NULL) { @@ -2615,44 +2615,13 @@ smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst) } /* SMB headers in a compound are 8 byte aligned. */ - - /* No padding needed */ - if (!(len & 7)) - goto finished; - - num_padding = 8 - (len & 7); - if (!smb3_encryption_required(tcon)) { - /* - * If we do not have encryption then we can just add an extra - * iov for the padding. - */ + if (!IS_ALIGNED(len, 8)) { + num_padding = 8 - (len & 7); rqst->rq_iov[rqst->rq_nvec].iov_base = smb2_padding; rqst->rq_iov[rqst->rq_nvec].iov_len = num_padding; rqst->rq_nvec++; len += num_padding; - } else { - /* - * We can not add a small padding iov for the encryption case - * because the encryption framework can not handle the padding - * iovs. - * We have to flatten this into a single buffer and add - * the padding to it. - */ - for (i = 1; i < rqst->rq_nvec; i++) { - memcpy(rqst->rq_iov[0].iov_base + - rqst->rq_iov[0].iov_len, - rqst->rq_iov[i].iov_base, - rqst->rq_iov[i].iov_len); - rqst->rq_iov[0].iov_len += rqst->rq_iov[i].iov_len; - } - memset(rqst->rq_iov[0].iov_base + rqst->rq_iov[0].iov_len, - 0, num_padding); - rqst->rq_iov[0].iov_len += num_padding; - len += num_padding; - rqst->rq_nvec = 1; } - - finished: shdr->NextCommand = cpu_to_le32(len); } @@ -4080,7 +4049,7 @@ map_oplock_to_lease(u8 oplock) if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) return SMB2_LEASE_WRITE_CACHING_LE | SMB2_LEASE_READ_CACHING_LE; else if (oplock == SMB2_OPLOCK_LEVEL_II) - return SMB2_LEASE_READ_CACHING_LE; + return SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE; else if (oplock == SMB2_OPLOCK_LEVEL_BATCH) return SMB2_LEASE_HANDLE_CACHING_LE | SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_WRITE_CACHING_LE; diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 6584b5cddc28..055236835537 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -2683,7 +2683,7 @@ create_sd_buf(umode_t mode, bool set_owner, unsigned int *len) ptr += sizeof(struct smb3_acl); /* create one ACE to hold the mode embedded in reserved special SID */ - acelen = setup_special_mode_ACE((struct smb_ace *)ptr, (__u64)mode); + acelen = setup_special_mode_ACE((struct smb_ace *)ptr, false, (__u64)mode); ptr += acelen; acl_size = acelen + sizeof(struct smb3_acl); ace_count = 1; @@ -3313,15 +3313,6 @@ SMB2_ioctl_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, return rc; if (indatalen) { - unsigned int len; - - if (WARN_ON_ONCE(smb3_encryption_required(tcon) && - (check_add_overflow(total_len - 1, - ALIGN(indatalen, 8), &len) || - len > MAX_CIFS_SMALL_BUFFER_SIZE))) { - cifs_small_buf_release(req); - return -EIO; - } /* * indatalen is usually small at a couple of bytes max, so * just allocate through generic pool diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h index 6f9885e4f66c..71504b30909e 100644 --- a/fs/smb/client/smb2proto.h +++ b/fs/smb/client/smb2proto.h @@ -37,8 +37,6 @@ extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst); extern struct mid_q_entry *smb2_setup_async_request( struct TCP_Server_Info *server, struct smb_rqst *rqst); -extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server, - __u64 ses_id); extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid); extern int smb2_calc_signature(struct smb_rqst *rqst, diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c index b486b14bb330..475b36c27f65 100644 --- a/fs/smb/client/smb2transport.c +++ b/fs/smb/client/smb2transport.c @@ -74,7 +74,7 @@ err: static -int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key) +int smb3_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key) { struct cifs_chan *chan; struct TCP_Server_Info *pserver; @@ -168,16 +168,41 @@ smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id) return NULL; } -struct cifs_ses * -smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id) +static int smb2_get_sign_key(struct TCP_Server_Info *server, + __u64 ses_id, u8 *key) { struct cifs_ses *ses; + int rc = -ENOENT; + + if (SERVER_IS_CHAN(server)) + server = server->primary_server; spin_lock(&cifs_tcp_ses_lock); - ses = smb2_find_smb_ses_unlocked(server, ses_id); - spin_unlock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + if (ses->Suid != ses_id) + continue; - return ses; + rc = 0; + spin_lock(&ses->ses_lock); + switch (ses->ses_status) { + case SES_EXITING: /* SMB2_LOGOFF */ + case SES_GOOD: + if (likely(ses->auth_key.response)) { + memcpy(key, ses->auth_key.response, + SMB2_NTLMV2_SESSKEY_SIZE); + } else { + rc = -EIO; + } + break; + default: + rc = -EAGAIN; + break; + } + spin_unlock(&ses->ses_lock); + break; + } + spin_unlock(&cifs_tcp_ses_lock); + return rc; } static struct cifs_tcon * @@ -236,14 +261,16 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, unsigned char *sigptr = smb2_signature; struct kvec *iov = rqst->rq_iov; struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base; - struct cifs_ses *ses; struct shash_desc *shash = NULL; struct smb_rqst drqst; + __u64 sid = le64_to_cpu(shdr->SessionId); + u8 key[SMB2_NTLMV2_SESSKEY_SIZE]; - ses = smb2_find_smb_ses(server, le64_to_cpu(shdr->SessionId)); - if (unlikely(!ses)) { - cifs_server_dbg(FYI, "%s: Could not find session\n", __func__); - return -ENOENT; + rc = smb2_get_sign_key(server, sid, key); + if (unlikely(rc)) { + cifs_server_dbg(FYI, "%s: [sesid=0x%llx] couldn't find signing key: %d\n", + __func__, sid, rc); + return rc; } memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE); @@ -260,8 +287,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, shash = server->secmech.hmacsha256; } - rc = crypto_shash_setkey(shash->tfm, ses->auth_key.response, - SMB2_NTLMV2_SESSKEY_SIZE); + rc = crypto_shash_setkey(shash->tfm, key, sizeof(key)); if (rc) { cifs_server_dbg(VFS, "%s: Could not update with response\n", @@ -303,8 +329,6 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, out: if (allocate_crypto) cifs_free_hash(&shash); - if (ses) - cifs_put_smb_ses(ses); return rc; } @@ -570,7 +594,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, struct smb_rqst drqst; u8 key[SMB3_SIGN_KEY_SIZE]; - rc = smb2_get_sign_key(le64_to_cpu(shdr->SessionId), server, key); + rc = smb3_get_sign_key(le64_to_cpu(shdr->SessionId), server, key); if (unlikely(rc)) { cifs_server_dbg(FYI, "%s: Could not get signing key\n", __func__); return rc; diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c index 91812150186c..0dc80959ce48 100644 --- a/fs/smb/client/transport.c +++ b/fs/smb/client/transport.c @@ -418,19 +418,16 @@ out: return rc; } -struct send_req_vars { - struct smb2_transform_hdr tr_hdr; - struct smb_rqst rqst[MAX_COMPOUND]; - struct kvec iov; -}; - static int smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, struct smb_rqst *rqst, int flags) { - struct send_req_vars *vars; - struct smb_rqst *cur_rqst; - struct kvec *iov; + struct smb2_transform_hdr tr_hdr; + struct smb_rqst new_rqst[MAX_COMPOUND] = {}; + struct kvec iov = { + .iov_base = &tr_hdr, + .iov_len = sizeof(tr_hdr), + }; int rc; if (flags & CIFS_COMPRESS_REQ) @@ -447,26 +444,15 @@ smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, return -EIO; } - vars = kzalloc(sizeof(*vars), GFP_NOFS); - if (!vars) - return -ENOMEM; - cur_rqst = vars->rqst; - iov = &vars->iov; - - iov->iov_base = &vars->tr_hdr; - iov->iov_len = sizeof(vars->tr_hdr); - cur_rqst[0].rq_iov = iov; - cur_rqst[0].rq_nvec = 1; + new_rqst[0].rq_iov = &iov; + new_rqst[0].rq_nvec = 1; rc = server->ops->init_transform_rq(server, num_rqst + 1, - &cur_rqst[0], rqst); - if (rc) - goto out; - - rc = __smb_send_rqst(server, num_rqst + 1, &cur_rqst[0]); - smb3_free_compound_rqst(num_rqst, &cur_rqst[1]); -out: - kfree(vars); + new_rqst, rqst); + if (!rc) { + rc = __smb_send_rqst(server, num_rqst + 1, new_rqst); + smb3_free_compound_rqst(num_rqst, &new_rqst[1]); + } return rc; } |