OSDN Git Service

cifs: fix smb3_zero_range so it can expand the file-size when required
authorRonnie Sahlberg <lsahlber@redhat.com>
Wed, 13 Mar 2019 04:37:49 +0000 (14:37 +1000)
committerSteve French <stfrench@microsoft.com>
Fri, 15 Mar 2019 00:32:35 +0000 (19:32 -0500)
This allows fallocate -z to work against a Windows2016 share.

This is due to the SMB3 ZERO_RANGE command does not modify the filesize.
To address this we will now append a compounded SET-INFO to update the
end-of-file information.

This brings xfstests generic/469 closer to working against a windows share.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/smb2ops.c
fs/cifs/smb2pdu.c
fs/cifs/smb2pdu.h

index a338190..bd3d228 100644 (file)
@@ -2549,12 +2549,22 @@ get_smb2_acl(struct cifs_sb_info *cifs_sb,
 static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
                            loff_t offset, loff_t len, bool keep_size)
 {
+       struct cifs_ses *ses = tcon->ses;
        struct inode *inode;
        struct cifsInodeInfo *cifsi;
        struct cifsFileInfo *cfile = file->private_data;
        struct file_zero_data_information fsctl_buf;
+       struct smb_rqst rqst[2];
+       int resp_buftype[2];
+       struct kvec rsp_iov[2];
+       struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
+       struct kvec si_iov[1];
+       unsigned int size[1];
+       void *data[1];
        long rc;
        unsigned int xid;
+       int num = 0, flags = 0;
+       __le64 eof;
 
        xid = get_xid();
 
@@ -2579,28 +2589,60 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
                return rc;
        }
 
-       /*
-        * need to make sure we are not asked to extend the file since the SMB3
-        * fsctl does not change the file size. In the future we could change
-        * this to zero the first part of the range then set the file size
-        * which for a non sparse file would zero the newly extended range
-        */
-       if (keep_size == false)
-               if (i_size_read(inode) < offset + len) {
-                       rc = -EOPNOTSUPP;
-                       free_xid(xid);
-                       return rc;
-               }
-
        cifs_dbg(FYI, "offset %lld len %lld", offset, len);
 
        fsctl_buf.FileOffset = cpu_to_le64(offset);
        fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);
 
-       rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
-                       cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
-                       true /* is_fctl */, (char *)&fsctl_buf,
-                       sizeof(struct file_zero_data_information), NULL, NULL);
+       if (smb3_encryption_required(tcon))
+               flags |= CIFS_TRANSFORM_REQ;
+
+       memset(rqst, 0, sizeof(rqst));
+       resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
+       memset(rsp_iov, 0, sizeof(rsp_iov));
+
+
+       memset(&io_iov, 0, sizeof(io_iov));
+       rqst[num].rq_iov = io_iov;
+       rqst[num].rq_nvec = SMB2_IOCTL_IOV_SIZE;
+       rc = SMB2_ioctl_init(tcon, &rqst[num++], cfile->fid.persistent_fid,
+                            cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
+                            true /* is_fctl */, (char *)&fsctl_buf,
+                            sizeof(struct file_zero_data_information));
+       if (rc)
+               goto zero_range_exit;
+
+       /*
+        * do we also need to change the size of the file?
+        */
+       if (keep_size == false && i_size_read(inode) < offset + len) {
+               smb2_set_next_command(tcon, &rqst[0]);
+
+               memset(&si_iov, 0, sizeof(si_iov));
+               rqst[num].rq_iov = si_iov;
+               rqst[num].rq_nvec = 1;
+
+               eof = cpu_to_le64(offset + len);
+               size[0] = 8; /* sizeof __le64 */
+               data[0] = &eof;
+
+               rc = SMB2_set_info_init(tcon, &rqst[num++],
+                                       cfile->fid.persistent_fid,
+                                       cfile->fid.persistent_fid,
+                                       current->tgid,
+                                       FILE_END_OF_FILE_INFORMATION,
+                                       SMB2_O_INFO_FILE, 0, data, size);
+               smb2_set_related(&rqst[1]);
+       }
+
+       rc = compound_send_recv(xid, ses, flags, num, rqst,
+                               resp_buftype, rsp_iov);
+
+ zero_range_exit:
+       SMB2_ioctl_free(&rqst[0]);
+       SMB2_set_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);
        free_xid(xid);
        return rc;
 }
index 481d64d..c399e09 100644 (file)
@@ -2555,7 +2555,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
        struct smb_rqst rqst;
        struct smb2_ioctl_rsp *rsp = NULL;
        struct cifs_ses *ses;
-       struct kvec iov[2];
+       struct kvec iov[SMB2_IOCTL_IOV_SIZE];
        struct kvec rsp_iov = {NULL, 0};
        int resp_buftype = CIFS_NO_BUFFER;
        int rc = 0;
@@ -2584,7 +2584,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
        memset(&rqst, 0, sizeof(struct smb_rqst));
        memset(&iov, 0, sizeof(iov));
        rqst.rq_iov = iov;
-       rqst.rq_nvec = 2;
+       rqst.rq_nvec = SMB2_IOCTL_IOV_SIZE;
 
        rc = SMB2_ioctl_init(tcon, &rqst, persistent_fid, volatile_fid,
                             opcode, is_fsctl, in_data, indatalen);
index 0bd4d48..ee89776 100644 (file)
@@ -959,6 +959,13 @@ struct duplicate_extents_to_file {
        __le64 ByteCount;  /* Bytes to be copied */
 } __packed;
 
+/*
+ * Maximum number of iovs we need for an ioctl request.
+ * [0] : struct smb2_ioctl_req
+ * [1] : in_data
+ */
+#define SMB2_IOCTL_IOV_SIZE 2
+
 struct smb2_ioctl_req {
        struct smb2_sync_hdr sync_hdr;
        __le16 StructureSize;   /* Must be 57 */