Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 72c419d9 authored by Ronnie Sahlberg's avatar Ronnie Sahlberg Committed by Steve French
Browse files

cifs: fix smb3_zero_range so it can expand the file-size when required



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: default avatarRonnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent ccdc77a3
Loading
Loading
Loading
Loading
+59 −17
Original line number Diff line number Diff line
@@ -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,
	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), NULL, NULL);
			     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;
}
+2 −2
Original line number Diff line number Diff line
@@ -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);
+7 −0
Original line number Diff line number Diff line
@@ -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 */