Merge tag '6.2-rc-smb3-client-fixes-part1' of git://git.samba.org/sfrench/cifs-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 15 Dec 2022 22:53:14 +0000 (14:53 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 15 Dec 2022 22:53:14 +0000 (14:53 -0800)
Pull cifs client updates from Steve French:

 - SMB3.1.1 POSIX Extensions fixes

 - remove use of generic_writepages() and ->cifs_writepage(), in favor
   of ->cifs_writepages() and ->migrate_folio()

 - memory management fixes

 - mount parm parsing fixes

 - minor cleanup fixes

* tag '6.2-rc-smb3-client-fixes-part1' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: Remove duplicated include in cifsglob.h
  cifs: fix oops during encryption
  cifs: print warning when conflicting soft vs. hard mount options specified
  cifs: fix missing display of three mount options
  cifs: fix various whitespace errors in headers
  cifs: minor cleanup of some headers
  cifs: skip alloc when request has no pages
  cifs: remove ->writepage
  cifs: stop using generic_writepages
  cifs: wire up >migrate_folio
  cifs: Parse owner/group for stat in smb311 posix extensions
  cifs: Add "extbuf" and "extbuflen" args to smb2_compound_op()
  Fix path in cifs/usage.rst

1  2 
fs/cifs/cifsfs.c
fs/cifs/cifsproto.h
fs/cifs/file.c
fs/cifs/smb2ops.c

diff --combined fs/cifs/cifsfs.c
@@@ -678,9 -678,15 +678,15 @@@ cifs_show_options(struct seq_file *s, s
        seq_printf(s, ",echo_interval=%lu",
                        tcon->ses->server->echo_interval / HZ);
  
-       /* Only display max_credits if it was overridden on mount */
+       /* Only display the following if overridden on mount */
        if (tcon->ses->server->max_credits != SMB2_MAX_CREDITS_AVAILABLE)
                seq_printf(s, ",max_credits=%u", tcon->ses->server->max_credits);
+       if (tcon->ses->server->tcp_nodelay)
+               seq_puts(s, ",tcpnodelay");
+       if (tcon->ses->server->noautotune)
+               seq_puts(s, ",noautotune");
+       if (tcon->ses->server->noblocksnd)
+               seq_puts(s, ",noblocksend");
  
        if (tcon->snapshot_time)
                seq_printf(s, ",snapshot=%llu", tcon->snapshot_time);
@@@ -1133,8 -1139,6 +1139,8 @@@ const struct inode_operations cifs_dir_
        .symlink = cifs_symlink,
        .mknod   = cifs_mknod,
        .listxattr = cifs_listxattr,
 +      .get_acl = cifs_get_acl,
 +      .set_acl = cifs_set_acl,
  };
  
  const struct inode_operations cifs_file_inode_ops = {
        .permission = cifs_permission,
        .listxattr = cifs_listxattr,
        .fiemap = cifs_fiemap,
 +      .get_acl = cifs_get_acl,
 +      .set_acl = cifs_set_acl,
  };
  
  const char *cifs_get_link(struct dentry *dentry, struct inode *inode,
diff --combined fs/cifs/cifsproto.h
@@@ -124,7 -124,7 +124,7 @@@ extern int SendReceive2(const unsigned 
                        struct kvec * /* resp vec */);
  extern int SendReceiveBlockingLock(const unsigned int xid,
                        struct cifs_tcon *ptcon,
-                       struct smb_hdr *in_buf ,
+                       struct smb_hdr *in_buf,
                        struct smb_hdr *out_buf,
                        int *bytes_returned);
  void
@@@ -224,10 -224,6 +224,10 @@@ extern struct cifs_ntsd *get_cifs_acl(s
                                      const char *, u32 *, u32);
  extern struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *,
                                const struct cifs_fid *, u32 *, u32);
 +extern struct posix_acl *cifs_get_acl(struct user_namespace *mnt_userns,
 +                                    struct dentry *dentry, int type);
 +extern int cifs_set_acl(struct user_namespace *mnt_userns,
 +                      struct dentry *dentry, struct posix_acl *acl, int type);
  extern int set_cifs_acl(struct cifs_ntsd *, __u32, struct inode *,
                                const char *, int);
  extern unsigned int setup_authusers_ACE(struct cifs_ace *pace);
@@@ -541,14 -537,14 +541,14 @@@ extern int CIFSSMBGetCIFSACL(const unsi
                        __u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen);
  extern int CIFSSMBSetCIFSACL(const unsigned int, struct cifs_tcon *, __u16,
                        struct cifs_ntsd *, __u32, int);
 -extern int CIFSSMBGetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,
 -              const unsigned char *searchName,
 -              char *acl_inf, const int buflen, const int acl_type,
 -              const struct nls_table *nls_codepage, int remap_special_chars);
 -extern int CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,
 -              const unsigned char *fileName,
 -              const char *local_acl, const int buflen, const int acl_type,
 -              const struct nls_table *nls_codepage, int remap_special_chars);
 +extern int cifs_do_get_acl(const unsigned int xid, struct cifs_tcon *tcon,
 +                         const unsigned char *searchName,
 +                         struct posix_acl **acl, const int acl_type,
 +                         const struct nls_table *nls_codepage, int remap);
 +extern int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
 +                         const unsigned char *fileName,
 +                         const struct posix_acl *acl, const int acl_type,
 +                         const struct nls_table *nls_codepage, int remap);
  extern int CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon,
                        const int netfid, __u64 *pExtAttrBits, __u64 *pMask);
  #endif /* CIFS_ALLOW_INSECURE_LEGACY */
@@@ -604,8 -600,8 +604,8 @@@ int setup_aio_ctx_iter(struct cifs_aio_
  int cifs_alloc_hash(const char *name, struct shash_desc **sdesc);
  void cifs_free_hash(struct shash_desc **sdesc);
  
extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
-                               unsigned int *len, unsigned int *offset);
void rqst_page_get_length(const struct smb_rqst *rqst, unsigned int page,
+                         unsigned int *len, unsigned int *offset);
  struct cifs_chan *
  cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server);
  int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses);
diff --combined fs/cifs/file.c
@@@ -1413,7 -1413,7 +1413,7 @@@ cifs_push_posix_locks(struct cifsFileIn
        struct inode *inode = d_inode(cfile->dentry);
        struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
        struct file_lock *flock;
 -      struct file_lock_context *flctx = inode->i_flctx;
 +      struct file_lock_context *flctx = locks_inode_context(inode);
        unsigned int count = 0, i;
        int rc = 0, xid, type;
        struct list_head locks_to_send, *el;
@@@ -2646,6 -2646,21 +2646,21 @@@ wdata_send_pages(struct cifs_writedata 
        return rc;
  }
  
+ static int
+ cifs_writepage_locked(struct page *page, struct writeback_control *wbc);
+ static int cifs_write_one_page(struct page *page, struct writeback_control *wbc,
+               void *data)
+ {
+       struct address_space *mapping = data;
+       int ret;
+       ret = cifs_writepage_locked(page, wbc);
+       unlock_page(page);
+       mapping_set_error(mapping, ret);
+       return ret;
+ }
  static int cifs_writepages(struct address_space *mapping,
                           struct writeback_control *wbc)
  {
  
        /*
         * If wsize is smaller than the page cache size, default to writing
-        * one page at a time via cifs_writepage
+        * one page at a time.
         */
        if (cifs_sb->ctx->wsize < PAGE_SIZE)
-               return generic_writepages(mapping, wbc);
+               return write_cache_pages(mapping, wbc, cifs_write_one_page,
+                               mapping);
  
        xid = get_xid();
        if (wbc->range_cyclic) {
@@@ -2852,13 -2868,6 +2868,6 @@@ retry_write
        return rc;
  }
  
- static int cifs_writepage(struct page *page, struct writeback_control *wbc)
- {
-       int rc = cifs_writepage_locked(page, wbc);
-       unlock_page(page);
-       return rc;
- }
  static int cifs_write_end(struct file *file, struct address_space *mapping,
                        loff_t pos, unsigned len, unsigned copied,
                        struct page *page, void *fsdata)
@@@ -3532,7 -3541,7 +3541,7 @@@ static ssize_t __cifs_writev
                ctx->iter = *from;
                ctx->len = len;
        } else {
 -              rc = setup_aio_ctx_iter(ctx, from, WRITE);
 +              rc = setup_aio_ctx_iter(ctx, from, ITER_SOURCE);
                if (rc) {
                        kref_put(&ctx->refcount, cifs_aio_ctx_release);
                        return rc;
@@@ -4276,7 -4285,7 +4285,7 @@@ static ssize_t __cifs_readv
                ctx->iter = *to;
                ctx->len = len;
        } else {
 -              rc = setup_aio_ctx_iter(ctx, to, READ);
 +              rc = setup_aio_ctx_iter(ctx, to, ITER_DEST);
                if (rc) {
                        kref_put(&ctx->refcount, cifs_aio_ctx_release);
                        return rc;
@@@ -5231,7 -5240,6 +5240,6 @@@ static bool cifs_dirty_folio(struct add
  const struct address_space_operations cifs_addr_ops = {
        .read_folio = cifs_read_folio,
        .readahead = cifs_readahead,
-       .writepage = cifs_writepage,
        .writepages = cifs_writepages,
        .write_begin = cifs_write_begin,
        .write_end = cifs_write_end,
        .direct_IO = cifs_direct_io,
        .invalidate_folio = cifs_invalidate_folio,
        .launder_folio = cifs_launder_folio,
+       .migrate_folio = filemap_migrate_folio,
        /*
-        * TODO: investigate and if useful we could add an cifs_migratePage
-        * helper (under an CONFIG_MIGRATION) in the future, and also
-        * investigate and add an is_dirty_writeback helper if needed
+        * TODO: investigate and if useful we could add an is_dirty_writeback
+        * helper if needed
         */
        .swap_activate = cifs_swap_activate,
        .swap_deactivate = cifs_swap_deactivate,
   */
  const struct address_space_operations cifs_addr_ops_smallbuf = {
        .read_folio = cifs_read_folio,
-       .writepage = cifs_writepage,
        .writepages = cifs_writepages,
        .write_begin = cifs_write_begin,
        .write_end = cifs_write_end,
        .release_folio = cifs_release_folio,
        .invalidate_folio = cifs_invalidate_folio,
        .launder_folio = cifs_launder_folio,
+       .migrate_folio = filemap_migrate_folio,
  };
diff --combined fs/cifs/smb2ops.c
@@@ -4204,69 -4204,82 +4204,82 @@@ fill_transform_hdr(struct smb2_transfor
        memcpy(&tr_hdr->SessionId, &shdr->SessionId, 8);
  }
  
- /* We can not use the normal sg_set_buf() as we will sometimes pass a
-  * stack object as buf.
-  */
- static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf,
-                                  unsigned int buflen)
+ static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst *rqst,
+                                int num_rqst, const u8 *sig, u8 **iv,
+                                struct aead_request **req, struct scatterlist **sgl,
+                                unsigned int *num_sgs)
  {
-       void *addr;
-       /*
-        * VMAP_STACK (at least) puts stack into the vmalloc address space
-        */
-       if (is_vmalloc_addr(buf))
-               addr = vmalloc_to_page(buf);
-       else
-               addr = virt_to_page(buf);
-       sg_set_page(sg, addr, buflen, offset_in_page(buf));
+       unsigned int req_size = sizeof(**req) + crypto_aead_reqsize(tfm);
+       unsigned int iv_size = crypto_aead_ivsize(tfm);
+       unsigned int len;
+       u8 *p;
+       *num_sgs = cifs_get_num_sgs(rqst, num_rqst, sig);
+       len = iv_size;
+       len += crypto_aead_alignmask(tfm) & ~(crypto_tfm_ctx_alignment() - 1);
+       len = ALIGN(len, crypto_tfm_ctx_alignment());
+       len += req_size;
+       len = ALIGN(len, __alignof__(struct scatterlist));
+       len += *num_sgs * sizeof(**sgl);
+       p = kmalloc(len, GFP_ATOMIC);
+       if (!p)
+               return NULL;
+       *iv = (u8 *)PTR_ALIGN(p, crypto_aead_alignmask(tfm) + 1);
+       *req = (struct aead_request *)PTR_ALIGN(*iv + iv_size,
+                                               crypto_tfm_ctx_alignment());
+       *sgl = (struct scatterlist *)PTR_ALIGN((u8 *)*req + req_size,
+                                              __alignof__(struct scatterlist));
+       return p;
  }
  
- /* Assumes the first rqst has a transform header as the first iov.
-  * I.e.
-  * rqst[0].rq_iov[0]  is transform header
-  * rqst[0].rq_iov[1+] data to be encrypted/decrypted
-  * rqst[1+].rq_iov[0+] data to be encrypted/decrypted
-  */
- static struct scatterlist *
- init_sg(int num_rqst, struct smb_rqst *rqst, u8 *sign)
+ static void *smb2_get_aead_req(struct crypto_aead *tfm, const struct smb_rqst *rqst,
+                              int num_rqst, const u8 *sig, u8 **iv,
+                              struct aead_request **req, struct scatterlist **sgl)
  {
-       unsigned int sg_len;
+       unsigned int off, len, skip;
        struct scatterlist *sg;
-       unsigned int i;
-       unsigned int j;
-       unsigned int idx = 0;
-       int skip;
-       sg_len = 1;
-       for (i = 0; i < num_rqst; i++)
-               sg_len += rqst[i].rq_nvec + rqst[i].rq_npages;
+       unsigned int num_sgs;
+       unsigned long addr;
+       int i, j;
+       void *p;
  
-       sg = kmalloc_array(sg_len, sizeof(struct scatterlist), GFP_KERNEL);
-       if (!sg)
+       p = smb2_aead_req_alloc(tfm, rqst, num_rqst, sig, iv, req, sgl, &num_sgs);
+       if (!p)
                return NULL;
  
-       sg_init_table(sg, sg_len);
+       sg_init_table(*sgl, num_sgs);
+       sg = *sgl;
+       /* Assumes the first rqst has a transform header as the first iov.
+        * I.e.
+        * rqst[0].rq_iov[0]  is transform header
+        * rqst[0].rq_iov[1+] data to be encrypted/decrypted
+        * rqst[1+].rq_iov[0+] data to be encrypted/decrypted
+        */
        for (i = 0; i < num_rqst; i++) {
+               /*
+                * The first rqst has a transform header where the
+                * first 20 bytes are not part of the encrypted blob.
+                */
                for (j = 0; j < rqst[i].rq_nvec; j++) {
-                       /*
-                        * The first rqst has a transform header where the
-                        * first 20 bytes are not part of the encrypted blob
-                        */
-                       skip = (i == 0) && (j == 0) ? 20 : 0;
-                       smb2_sg_set_buf(&sg[idx++],
-                                       rqst[i].rq_iov[j].iov_base + skip,
-                                       rqst[i].rq_iov[j].iov_len - skip);
-                       }
+                       struct kvec *iov = &rqst[i].rq_iov[j];
  
+                       skip = (i == 0) && (j == 0) ? 20 : 0;
+                       addr = (unsigned long)iov->iov_base + skip;
+                       len = iov->iov_len - skip;
+                       sg = cifs_sg_set_buf(sg, (void *)addr, len);
+               }
                for (j = 0; j < rqst[i].rq_npages; j++) {
-                       unsigned int len, offset;
-                       rqst_page_get_length(&rqst[i], j, &len, &offset);
-                       sg_set_page(&sg[idx++], rqst[i].rq_pages[j], len, offset);
+                       rqst_page_get_length(&rqst[i], j, &len, &off);
+                       sg_set_page(sg++, rqst[i].rq_pages[j], len, off);
                }
        }
-       smb2_sg_set_buf(&sg[idx], sign, SMB2_SIGNATURE_SIZE);
-       return sg;
+       cifs_sg_set_buf(sg, sig, SMB2_SIGNATURE_SIZE);
+       return p;
  }
  
  static int
@@@ -4314,11 -4327,11 +4327,11 @@@ crypt_message(struct TCP_Server_Info *s
        u8 sign[SMB2_SIGNATURE_SIZE] = {};
        u8 key[SMB3_ENC_DEC_KEY_SIZE];
        struct aead_request *req;
-       char *iv;
-       unsigned int iv_len;
+       u8 *iv;
        DECLARE_CRYPTO_WAIT(wait);
        struct crypto_aead *tfm;
        unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize);
+       void *creq;
  
        rc = smb2_get_enc_key(server, le64_to_cpu(tr_hdr->SessionId), enc, key);
        if (rc) {
                return rc;
        }
  
-       req = aead_request_alloc(tfm, GFP_KERNEL);
-       if (!req) {
-               cifs_server_dbg(VFS, "%s: Failed to alloc aead request\n", __func__);
+       creq = smb2_get_aead_req(tfm, rqst, num_rqst, sign, &iv, &req, &sg);
+       if (unlikely(!creq))
                return -ENOMEM;
-       }
  
        if (!enc) {
                memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE);
                crypt_len += SMB2_SIGNATURE_SIZE;
        }
  
-       sg = init_sg(num_rqst, rqst, sign);
-       if (!sg) {
-               cifs_server_dbg(VFS, "%s: Failed to init sg\n", __func__);
-               rc = -ENOMEM;
-               goto free_req;
-       }
-       iv_len = crypto_aead_ivsize(tfm);
-       iv = kzalloc(iv_len, GFP_KERNEL);
-       if (!iv) {
-               cifs_server_dbg(VFS, "%s: Failed to alloc iv\n", __func__);
-               rc = -ENOMEM;
-               goto free_sg;
-       }
        if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) ||
            (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM))
                memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE);
                memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE);
        }
  
+       aead_request_set_tfm(req, tfm);
        aead_request_set_crypt(req, sg, sg, crypt_len, iv);
        aead_request_set_ad(req, assoc_data_len);
  
        if (!rc && enc)
                memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE);
  
-       kfree_sensitive(iv);
- free_sg:
-       kfree_sensitive(sg);
- free_req:
-       kfree_sensitive(req);
+       kfree_sensitive(creq);
        return rc;
  }
  
@@@ -4445,21 -4438,27 +4438,27 @@@ smb3_init_transform_rq(struct TCP_Serve
        int rc = -ENOMEM;
  
        for (i = 1; i < num_rqst; i++) {
-               npages = old_rq[i - 1].rq_npages;
+               struct smb_rqst *old = &old_rq[i - 1];
+               struct smb_rqst *new = &new_rq[i];
+               orig_len += smb_rqst_len(server, old);
+               new->rq_iov = old->rq_iov;
+               new->rq_nvec = old->rq_nvec;
+               npages = old->rq_npages;
+               if (!npages)
+                       continue;
                pages = kmalloc_array(npages, sizeof(struct page *),
                                      GFP_KERNEL);
                if (!pages)
                        goto err_free;
  
-               new_rq[i].rq_pages = pages;
-               new_rq[i].rq_npages = npages;
-               new_rq[i].rq_offset = old_rq[i - 1].rq_offset;
-               new_rq[i].rq_pagesz = old_rq[i - 1].rq_pagesz;
-               new_rq[i].rq_tailsz = old_rq[i - 1].rq_tailsz;
-               new_rq[i].rq_iov = old_rq[i - 1].rq_iov;
-               new_rq[i].rq_nvec = old_rq[i - 1].rq_nvec;
-               orig_len += smb_rqst_len(server, &old_rq[i - 1]);
+               new->rq_pages = pages;
+               new->rq_npages = npages;
+               new->rq_offset = old->rq_offset;
+               new->rq_pagesz = old->rq_pagesz;
+               new->rq_tailsz = old->rq_tailsz;
  
                for (j = 0; j < npages; j++) {
                        pages[j] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
                        char *dst, *src;
                        unsigned int offset, len;
  
-                       rqst_page_get_length(&new_rq[i], j, &len, &offset);
+                       rqst_page_get_length(new, j, &len, &offset);
  
-                       dst = (char *) kmap(new_rq[i].rq_pages[j]) + offset;
-                       src = (char *) kmap(old_rq[i - 1].rq_pages[j]) + offset;
+                       dst = kmap_local_page(new->rq_pages[j]) + offset;
+                       src = kmap_local_page(old->rq_pages[j]) + offset;
  
                        memcpy(dst, src, len);
-                       kunmap(new_rq[i].rq_pages[j]);
-                       kunmap(old_rq[i - 1].rq_pages[j]);
+                       kunmap(new->rq_pages[j]);
+                       kunmap(old->rq_pages[j]);
                }
        }
  
@@@ -4723,13 -4722,13 +4722,13 @@@ handle_read_data(struct TCP_Server_Inf
                        return 0;
                }
  
 -              iov_iter_bvec(&iter, WRITE, bvec, npages, data_len);
 +              iov_iter_bvec(&iter, ITER_SOURCE, bvec, npages, data_len);
        } else if (buf_len >= data_offset + data_len) {
                /* read response payload is in buf */
                WARN_ONCE(npages > 0, "read data can be either in buf or in pages");
                iov.iov_base = buf + data_offset;
                iov.iov_len = data_len;
 -              iov_iter_kvec(&iter, WRITE, &iov, 1, data_len);
 +              iov_iter_kvec(&iter, ITER_SOURCE, &iov, 1, data_len);
        } else {
                /* read response payload cannot be in both buf and pages */
                WARN_ONCE(1, "buf can not contain only a part of read data");