155f0a249SGordon Ross /*
255f0a249SGordon Ross * This file and its contents are supplied under the terms of the
355f0a249SGordon Ross * Common Development and Distribution License ("CDDL"), version 1.0.
455f0a249SGordon Ross * You may only use this file in accordance with the terms of version
555f0a249SGordon Ross * 1.0 of the CDDL.
655f0a249SGordon Ross *
755f0a249SGordon Ross * A full copy of the text of the CDDL should have accompanied this
855f0a249SGordon Ross * source. A copy of the CDDL is also available via the Internet at
955f0a249SGordon Ross * http://www.illumos.org/license/CDDL.
1055f0a249SGordon Ross */
1155f0a249SGordon Ross
1255f0a249SGordon Ross /*
13*1baeef30SPrashanth Badari * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
1455f0a249SGordon Ross */
1555f0a249SGordon Ross
1655f0a249SGordon Ross /*
1755f0a249SGordon Ross * Support functions for smb2_ioctl/fsctl codes:
1855f0a249SGordon Ross * FSCTL_SET_SPARSE
1955f0a249SGordon Ross * FSCTL_SET_ZERO_DATA
2055f0a249SGordon Ross * FSCTL_QUERY_ALLOCATED_RANGES
2155f0a249SGordon Ross */
2255f0a249SGordon Ross
2355f0a249SGordon Ross #include <smbsrv/smb2_kproto.h>
2455f0a249SGordon Ross #include <smbsrv/smb_fsops.h>
2555f0a249SGordon Ross #include <smb/winioctl.h>
2655f0a249SGordon Ross
2755f0a249SGordon Ross /*
2855f0a249SGordon Ross * FSCTL_SET_SPARSE
2955f0a249SGordon Ross *
3055f0a249SGordon Ross * In args: one byte flag (optional: default TRUE)
3155f0a249SGordon Ross */
3255f0a249SGordon Ross uint32_t
smb2_fsctl_set_sparse(smb_request_t * sr,smb_fsctl_t * fsctl)3355f0a249SGordon Ross smb2_fsctl_set_sparse(smb_request_t *sr, smb_fsctl_t *fsctl)
3455f0a249SGordon Ross {
3555f0a249SGordon Ross smb_attr_t attr;
3655f0a249SGordon Ross smb_ofile_t *ofile = sr->fid_ofile;
3755f0a249SGordon Ross cred_t *kcr;
3855f0a249SGordon Ross uint32_t amask;
3955f0a249SGordon Ross uint32_t status;
4055f0a249SGordon Ross uint8_t flag;
4155f0a249SGordon Ross int rc;
4255f0a249SGordon Ross
4355f0a249SGordon Ross rc = smb_mbc_decodef(fsctl->in_mbc, "b", &flag);
4455f0a249SGordon Ross if (rc != 0)
4555f0a249SGordon Ross flag = 0xff;
4655f0a249SGordon Ross
4755f0a249SGordon Ross if (!smb_node_is_file(ofile->f_node))
4855f0a249SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
4955f0a249SGordon Ross
5055f0a249SGordon Ross /*
5155f0a249SGordon Ross * Allow if we have any of FILE_WRITE_ATTRIBUTES,
5255f0a249SGordon Ross * FILE_WRITE_DATA, FILE_APPEND_DATA
5355f0a249SGordon Ross */
5455f0a249SGordon Ross amask = FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | FILE_APPEND_DATA;
5555f0a249SGordon Ross if ((ofile->f_granted_access & amask) == 0)
5655f0a249SGordon Ross return (NT_STATUS_ACCESS_DENIED);
5755f0a249SGordon Ross
5855f0a249SGordon Ross /*
5955f0a249SGordon Ross * Need the current DOS attributes
6055f0a249SGordon Ross */
6155f0a249SGordon Ross bzero(&attr, sizeof (attr));
6255f0a249SGordon Ross attr.sa_mask = SMB_AT_DOSATTR;
6355f0a249SGordon Ross kcr = zone_kcred();
6455f0a249SGordon Ross status = smb_node_getattr(sr, ofile->f_node, kcr, ofile, &attr);
6555f0a249SGordon Ross if (status != NT_STATUS_SUCCESS)
6655f0a249SGordon Ross return (status);
6755f0a249SGordon Ross
6855f0a249SGordon Ross if (flag != 0) {
6955f0a249SGordon Ross /* Set "sparse" */
7055f0a249SGordon Ross if (attr.sa_dosattr & FILE_ATTRIBUTE_SPARSE_FILE)
7155f0a249SGordon Ross return (0);
7255f0a249SGordon Ross attr.sa_dosattr |= FILE_ATTRIBUTE_SPARSE_FILE;
7355f0a249SGordon Ross } else {
7455f0a249SGordon Ross /* Clear "sparse" */
7555f0a249SGordon Ross if ((attr.sa_dosattr & FILE_ATTRIBUTE_SPARSE_FILE) == 0)
7655f0a249SGordon Ross return (0);
7755f0a249SGordon Ross attr.sa_dosattr &= ~FILE_ATTRIBUTE_SPARSE_FILE;
7855f0a249SGordon Ross }
7955f0a249SGordon Ross
8055f0a249SGordon Ross attr.sa_mask = SMB_AT_DOSATTR;
8155f0a249SGordon Ross status = smb_node_setattr(sr, ofile->f_node, kcr, ofile, &attr);
8255f0a249SGordon Ross return (status);
8355f0a249SGordon Ross }
8455f0a249SGordon Ross
8555f0a249SGordon Ross /*
8655f0a249SGordon Ross * FSCTL_SET_ZERO_DATA
8755f0a249SGordon Ross *
8855f0a249SGordon Ross * In args: uint64_t start_off, end_off
8955f0a249SGordon Ross */
9055f0a249SGordon Ross uint32_t
smb2_fsctl_set_zero_data(smb_request_t * sr,smb_fsctl_t * fsctl)9155f0a249SGordon Ross smb2_fsctl_set_zero_data(smb_request_t *sr, smb_fsctl_t *fsctl)
9255f0a249SGordon Ross {
9355f0a249SGordon Ross smb_attr_t attr;
9455f0a249SGordon Ross smb_ofile_t *ofile = sr->fid_ofile;
9555f0a249SGordon Ross uint64_t start_off, end_off, zero_len;
9655f0a249SGordon Ross uint32_t status;
9755f0a249SGordon Ross int rc;
9855f0a249SGordon Ross
9955f0a249SGordon Ross rc = smb_mbc_decodef(fsctl->in_mbc, "qq",
10055f0a249SGordon Ross &start_off, &end_off);
10155f0a249SGordon Ross if (rc != 0)
10255f0a249SGordon Ross return (NT_STATUS_BUFFER_TOO_SMALL);
10355f0a249SGordon Ross
10455f0a249SGordon Ross /*
10555f0a249SGordon Ross * The given offsets are actually int64_t (signed).
10655f0a249SGordon Ross */
10755f0a249SGordon Ross if (start_off > INT64_MAX ||
10855f0a249SGordon Ross end_off > INT64_MAX ||
10955f0a249SGordon Ross start_off > end_off)
11055f0a249SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
11155f0a249SGordon Ross
11255f0a249SGordon Ross if (!smb_node_is_file(ofile->f_node))
11355f0a249SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
11455f0a249SGordon Ross
11555f0a249SGordon Ross /*
11655f0a249SGordon Ross * This operation is effectively a write (of zeros)
11755f0a249SGordon Ross */
11855f0a249SGordon Ross status = smb_ofile_access(ofile, ofile->f_cr, FILE_WRITE_DATA);
11955f0a249SGordon Ross if (status != NT_STATUS_SUCCESS)
12055f0a249SGordon Ross return (status);
12155f0a249SGordon Ross
12255f0a249SGordon Ross /*
12355f0a249SGordon Ross * Need the file size
12455f0a249SGordon Ross */
12555f0a249SGordon Ross bzero(&attr, sizeof (attr));
12655f0a249SGordon Ross attr.sa_mask = SMB_AT_SIZE;
12755f0a249SGordon Ross status = smb_node_getattr(sr, ofile->f_node, ofile->f_cr,
12855f0a249SGordon Ross ofile, &attr);
12955f0a249SGordon Ross if (status != NT_STATUS_SUCCESS)
13055f0a249SGordon Ross return (status);
13155f0a249SGordon Ross
13255f0a249SGordon Ross /*
13355f0a249SGordon Ross * Ignore any zero-ing beyond EOF
13455f0a249SGordon Ross */
13555f0a249SGordon Ross if (end_off > attr.sa_vattr.va_size)
13655f0a249SGordon Ross end_off = attr.sa_vattr.va_size;
13755f0a249SGordon Ross if (start_off >= end_off)
13855f0a249SGordon Ross return (0);
13955f0a249SGordon Ross zero_len = end_off - start_off;
14055f0a249SGordon Ross
14155f0a249SGordon Ross /*
14255f0a249SGordon Ross * Check for lock conflicting with the write.
14355f0a249SGordon Ross */
14455f0a249SGordon Ross status = smb_lock_range_access(sr, ofile->f_node,
14555f0a249SGordon Ross start_off, zero_len, B_TRUE);
14655f0a249SGordon Ross if (status != 0)
14755f0a249SGordon Ross return (status); /* == FILE_LOCK_CONFLICT */
14855f0a249SGordon Ross
14955f0a249SGordon Ross rc = smb_fsop_freesp(sr, ofile->f_cr, ofile,
15055f0a249SGordon Ross start_off, zero_len);
15155f0a249SGordon Ross if (rc != 0)
15255f0a249SGordon Ross status = smb_errno2status(rc);
15355f0a249SGordon Ross
15455f0a249SGordon Ross return (status);
15555f0a249SGordon Ross }
15655f0a249SGordon Ross
15755f0a249SGordon Ross /*
15855f0a249SGordon Ross * FSCTL_QUERY_ALLOCATED_RANGES
15955f0a249SGordon Ross *
16055f0a249SGordon Ross * Incoming args: uint64_t start_off, end_off
16155f0a249SGordon Ross */
16255f0a249SGordon Ross struct alloc_range {
16355f0a249SGordon Ross off64_t off;
16455f0a249SGordon Ross off64_t len;
16555f0a249SGordon Ross };
16655f0a249SGordon Ross uint32_t
smb2_fsctl_query_alloc_ranges(smb_request_t * sr,smb_fsctl_t * fsctl)16755f0a249SGordon Ross smb2_fsctl_query_alloc_ranges(smb_request_t *sr, smb_fsctl_t *fsctl)
16855f0a249SGordon Ross {
16955f0a249SGordon Ross smb_attr_t attr;
17055f0a249SGordon Ross cred_t *kcr;
17155f0a249SGordon Ross smb_ofile_t *ofile = sr->fid_ofile;
17255f0a249SGordon Ross struct alloc_range arg, res;
17355f0a249SGordon Ross off64_t cur_off, end_off;
17455f0a249SGordon Ross uint32_t status;
17555f0a249SGordon Ross int err, rc;
17655f0a249SGordon Ross
17755f0a249SGordon Ross /*
17855f0a249SGordon Ross * Most ioctls return NT_STATUS_BUFFER_TOO_SMALL for
17955f0a249SGordon Ross * short in/out buffers, but for this one, MS-FSA
18055f0a249SGordon Ross * says short input returns invalid parameter.
18155f0a249SGordon Ross */
18255f0a249SGordon Ross rc = smb_mbc_decodef(fsctl->in_mbc, "qq", &arg.off, &arg.len);
18355f0a249SGordon Ross if (rc != 0)
18455f0a249SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
18555f0a249SGordon Ross
18655f0a249SGordon Ross /*
18755f0a249SGordon Ross * The given offsets are actually int64_t (signed).
18855f0a249SGordon Ross */
18955f0a249SGordon Ross end_off = arg.off + arg.len;
19055f0a249SGordon Ross if (arg.off > INT64_MAX || arg.len < 0 ||
19155f0a249SGordon Ross end_off > INT64_MAX || end_off < arg.off)
19255f0a249SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
19355f0a249SGordon Ross
19455f0a249SGordon Ross if (!smb_node_is_file(ofile->f_node))
19555f0a249SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
19655f0a249SGordon Ross
19755f0a249SGordon Ross /*
19855f0a249SGordon Ross * This operation is effectively a read
19955f0a249SGordon Ross */
20055f0a249SGordon Ross status = smb_ofile_access(ofile, ofile->f_cr, FILE_READ_DATA);
20155f0a249SGordon Ross if (status != NT_STATUS_SUCCESS)
20255f0a249SGordon Ross return (status);
20355f0a249SGordon Ross
20455f0a249SGordon Ross if (arg.len == 0) {
20555f0a249SGordon Ross /* MS-FSA says empty result for this. */
20655f0a249SGordon Ross return (0);
20755f0a249SGordon Ross }
20855f0a249SGordon Ross
20955f0a249SGordon Ross /*
21055f0a249SGordon Ross * Need the file size and dosattr
21155f0a249SGordon Ross */
21255f0a249SGordon Ross bzero(&attr, sizeof (attr));
21355f0a249SGordon Ross attr.sa_mask = SMB_AT_SIZE | SMB_AT_DOSATTR;
21455f0a249SGordon Ross kcr = zone_kcred();
21555f0a249SGordon Ross status = smb_node_getattr(sr, ofile->f_node, kcr, ofile, &attr);
21655f0a249SGordon Ross if (status != NT_STATUS_SUCCESS)
21755f0a249SGordon Ross return (status);
21855f0a249SGordon Ross if (end_off > attr.sa_vattr.va_size)
21955f0a249SGordon Ross end_off = attr.sa_vattr.va_size;
22055f0a249SGordon Ross
22155f0a249SGordon Ross /*
22255f0a249SGordon Ross * Only sparse files should present un-allocated ranges.
22355f0a249SGordon Ross * If non-sparse, MS-FSA says (just return one range).
22455f0a249SGordon Ross */
22555f0a249SGordon Ross if ((attr.sa_dosattr & FILE_ATTRIBUTE_SPARSE_FILE) == 0) {
22655f0a249SGordon Ross if (arg.off < end_off) {
22755f0a249SGordon Ross res.off = arg.off;
22855f0a249SGordon Ross res.len = end_off - arg.off;
22955f0a249SGordon Ross rc = smb_mbc_encodef(fsctl->out_mbc, "qq",
23055f0a249SGordon Ross res.off, res.len);
23155f0a249SGordon Ross if (rc != 0)
23255f0a249SGordon Ross return (NT_STATUS_BUFFER_TOO_SMALL);
23355f0a249SGordon Ross }
23455f0a249SGordon Ross return (0);
23555f0a249SGordon Ross }
23655f0a249SGordon Ross
23755f0a249SGordon Ross cur_off = arg.off;
23855f0a249SGordon Ross while (cur_off < end_off) {
23955f0a249SGordon Ross off64_t data, hole;
24055f0a249SGordon Ross
24155f0a249SGordon Ross data = cur_off;
24255f0a249SGordon Ross err = smb_fsop_next_alloc_range(kcr, ofile->f_node,
24355f0a249SGordon Ross &data, &hole);
24455f0a249SGordon Ross if (err != 0)
24555f0a249SGordon Ross break;
24655f0a249SGordon Ross
24755f0a249SGordon Ross /* sanity check data (ensure progress) */
24855f0a249SGordon Ross if (data < cur_off) {
24955f0a249SGordon Ross ASSERT(0);
25055f0a249SGordon Ross data = cur_off;
25155f0a249SGordon Ross }
25255f0a249SGordon Ross
25355f0a249SGordon Ross /* Normal termination */
25455f0a249SGordon Ross if (data >= end_off)
25555f0a249SGordon Ross break;
25655f0a249SGordon Ross
25755f0a249SGordon Ross /* sanity check hole (ensure progress) */
25855f0a249SGordon Ross if (hole <= data)
25955f0a249SGordon Ross hole = end_off;
26055f0a249SGordon Ross
26155f0a249SGordon Ross /* Trim this range as needed. */
26255f0a249SGordon Ross if (hole > end_off)
26355f0a249SGordon Ross hole = end_off;
26455f0a249SGordon Ross
26555f0a249SGordon Ross res.off = data;
26655f0a249SGordon Ross res.len = hole - data;
26755f0a249SGordon Ross
26855f0a249SGordon Ross if (res.len > 0) {
26955f0a249SGordon Ross rc = smb_mbc_encodef(fsctl->out_mbc, "qq",
27055f0a249SGordon Ross res.off, res.len);
27155f0a249SGordon Ross if (rc != 0)
27255f0a249SGordon Ross return (NT_STATUS_BUFFER_TOO_SMALL);
27355f0a249SGordon Ross }
27455f0a249SGordon Ross
27555f0a249SGordon Ross cur_off = hole;
27655f0a249SGordon Ross }
27755f0a249SGordon Ross
27855f0a249SGordon Ross return (0);
27955f0a249SGordon Ross }
28055f0a249SGordon Ross
28155f0a249SGordon Ross /*
28255f0a249SGordon Ross * Copy a segment of a file, preserving sparseness.
28355f0a249SGordon Ross * Uses a caller-provided buffer for read/write.
28455f0a249SGordon Ross * Caller should already have checked for locks.
28555f0a249SGordon Ross *
28655f0a249SGordon Ross * On entry, *residp is the length to copy.
28755f0a249SGordon Ross * On return, it's the "resid" (amount not copied)
28855f0a249SGordon Ross *
28955f0a249SGordon Ross * If this gets an error from any I/O, return it, even if some data
29055f0a249SGordon Ross * have already been copied. The caller should normally ignore an
29155f0a249SGordon Ross * error when some data have been copied.
29255f0a249SGordon Ross */
29355f0a249SGordon Ross uint32_t
smb2_sparse_copy(smb_request_t * sr,smb_ofile_t * src_ofile,smb_ofile_t * dst_ofile,off64_t src_off,off64_t dst_off,uint32_t * residp,void * buffer,size_t bufsize)29455f0a249SGordon Ross smb2_sparse_copy(
29555f0a249SGordon Ross smb_request_t *sr,
29655f0a249SGordon Ross smb_ofile_t *src_ofile, smb_ofile_t *dst_ofile,
29755f0a249SGordon Ross off64_t src_off, off64_t dst_off, uint32_t *residp,
29855f0a249SGordon Ross void *buffer, size_t bufsize)
29955f0a249SGordon Ross {
30055f0a249SGordon Ross iovec_t iov;
30155f0a249SGordon Ross uio_t uio;
30255f0a249SGordon Ross off64_t data, hole;
30355f0a249SGordon Ross uint32_t xfer;
30455f0a249SGordon Ross uint32_t status = 0;
30555f0a249SGordon Ross int rc;
30655f0a249SGordon Ross
30755f0a249SGordon Ross while (*residp > 0) {
30855f0a249SGordon Ross
309*1baeef30SPrashanth Badari if (sr->sr_state != SMB_REQ_STATE_ACTIVE)
310*1baeef30SPrashanth Badari break;
311*1baeef30SPrashanth Badari
31255f0a249SGordon Ross data = src_off;
31355f0a249SGordon Ross rc = smb_fsop_next_alloc_range(src_ofile->f_cr,
31455f0a249SGordon Ross src_ofile->f_node, &data, &hole);
31555f0a249SGordon Ross switch (rc) {
31655f0a249SGordon Ross case 0:
31755f0a249SGordon Ross /* Found data, hole */
31855f0a249SGordon Ross break;
31955f0a249SGordon Ross case ENXIO:
32055f0a249SGordon Ross /* No data after here (will skip below). */
32155f0a249SGordon Ross data = hole = (src_off + *residp);
32255f0a249SGordon Ross break;
32355f0a249SGordon Ross default:
32455f0a249SGordon Ross cmn_err(CE_NOTE,
32555f0a249SGordon Ross "smb_fsop_next_alloc_range: rc=%d", rc);
32655f0a249SGordon Ross /* FALLTHROUGH */
32755f0a249SGordon Ross case ENOSYS: /* FS does not support VOP_IOCTL... */
32855f0a249SGordon Ross case ENOTTY: /* ... or _FIO_SEEK_DATA, _HOLE */
32955f0a249SGordon Ross data = src_off;
33055f0a249SGordon Ross hole = src_off + *residp;
33155f0a249SGordon Ross break;
33255f0a249SGordon Ross }
33355f0a249SGordon Ross
33455f0a249SGordon Ross /*
33555f0a249SGordon Ross * Don't try to go past (src_off + *residp)
33655f0a249SGordon Ross */
33755f0a249SGordon Ross if (hole > (src_off + *residp))
33855f0a249SGordon Ross hole = src_off + *residp;
33955f0a249SGordon Ross if (data > hole)
34055f0a249SGordon Ross data = hole;
34155f0a249SGordon Ross
34255f0a249SGordon Ross /*
34355f0a249SGordon Ross * If there's a gap (src_off .. data)
34455f0a249SGordon Ross * skip in src_ofile, zero in dst_ofile
34555f0a249SGordon Ross */
34655f0a249SGordon Ross if (src_off < data) {
34755f0a249SGordon Ross off64_t skip = data - src_off;
34855f0a249SGordon Ross rc = smb_fsop_freesp(sr, dst_ofile->f_cr,
34955f0a249SGordon Ross dst_ofile, dst_off, skip);
35055f0a249SGordon Ross if (rc == 0) {
35155f0a249SGordon Ross src_off += skip;
35255f0a249SGordon Ross dst_off += skip;
35355f0a249SGordon Ross *residp -= (uint32_t)skip;
35455f0a249SGordon Ross } else {
35555f0a249SGordon Ross /* Fall back to regular copy */
35655f0a249SGordon Ross data = src_off;
35755f0a249SGordon Ross }
35855f0a249SGordon Ross }
35955f0a249SGordon Ross ASSERT(src_off == data);
36055f0a249SGordon Ross
36155f0a249SGordon Ross /*
36255f0a249SGordon Ross * Copy this segment: src_off .. hole
36355f0a249SGordon Ross */
36455f0a249SGordon Ross while (src_off < hole) {
36555f0a249SGordon Ross ssize_t tsize = hole - src_off;
36655f0a249SGordon Ross if (tsize > bufsize)
36755f0a249SGordon Ross tsize = bufsize;
36855f0a249SGordon Ross
36955f0a249SGordon Ross /*
37055f0a249SGordon Ross * Read src_ofile into buffer
37155f0a249SGordon Ross */
37255f0a249SGordon Ross iov.iov_base = buffer;
37355f0a249SGordon Ross iov.iov_len = tsize;
37455f0a249SGordon Ross bzero(&uio, sizeof (uio));
37555f0a249SGordon Ross uio.uio_iov = &iov;
37655f0a249SGordon Ross uio.uio_iovcnt = 1;
37755f0a249SGordon Ross uio.uio_resid = tsize;
37855f0a249SGordon Ross uio.uio_loffset = src_off;
37955f0a249SGordon Ross uio.uio_segflg = UIO_SYSSPACE;
38055f0a249SGordon Ross uio.uio_extflg = UIO_COPY_DEFAULT;
38155f0a249SGordon Ross
38255f0a249SGordon Ross rc = smb_fsop_read(sr, src_ofile->f_cr,
383dfa42fabSMatt Barden src_ofile->f_node, src_ofile, &uio, 0);
38455f0a249SGordon Ross if (rc != 0) {
38555f0a249SGordon Ross status = smb_errno2status(rc);
38655f0a249SGordon Ross return (status);
38755f0a249SGordon Ross }
38855f0a249SGordon Ross /* Note: Could be partial read. */
38955f0a249SGordon Ross tsize -= uio.uio_resid;
39055f0a249SGordon Ross ASSERT(tsize > 0);
39155f0a249SGordon Ross
39255f0a249SGordon Ross /*
39355f0a249SGordon Ross * Write buffer to dst_ofile
39455f0a249SGordon Ross */
39555f0a249SGordon Ross iov.iov_base = buffer;
39655f0a249SGordon Ross iov.iov_len = tsize;
39755f0a249SGordon Ross bzero(&uio, sizeof (uio));
39855f0a249SGordon Ross uio.uio_iov = &iov;
39955f0a249SGordon Ross uio.uio_iovcnt = 1;
40055f0a249SGordon Ross uio.uio_resid = tsize;
40155f0a249SGordon Ross uio.uio_loffset = dst_off;
40255f0a249SGordon Ross uio.uio_segflg = UIO_SYSSPACE;
40355f0a249SGordon Ross uio.uio_extflg = UIO_COPY_DEFAULT;
40455f0a249SGordon Ross
40555f0a249SGordon Ross rc = smb_fsop_write(sr, dst_ofile->f_cr,
40655f0a249SGordon Ross dst_ofile->f_node, dst_ofile, &uio, &xfer, 0);
40755f0a249SGordon Ross if (rc != 0) {
40855f0a249SGordon Ross status = smb_errno2status(rc);
40955f0a249SGordon Ross return (status);
41055f0a249SGordon Ross }
41155f0a249SGordon Ross ASSERT(xfer <= tsize);
41255f0a249SGordon Ross
41355f0a249SGordon Ross src_off += xfer;
41455f0a249SGordon Ross dst_off += xfer;
41555f0a249SGordon Ross *residp -= xfer;
41655f0a249SGordon Ross }
41755f0a249SGordon Ross ASSERT(src_off == hole);
41855f0a249SGordon Ross }
41955f0a249SGordon Ross
42055f0a249SGordon Ross return (status);
42155f0a249SGordon Ross }
422252bc4b2SGordon Ross
423252bc4b2SGordon Ross /*
424252bc4b2SGordon Ross * Not sure what header this might go in.
425252bc4b2SGordon Ross */
426252bc4b2SGordon Ross #define FILE_REGION_USAGE_VALID_CACHED_DATA 1
427252bc4b2SGordon Ross #define FILE_REGION_USAGE_VALID_NONCACHED_DATA 2
428252bc4b2SGordon Ross #define FILE_REGION_USAGE_VALID__MASK 3
429252bc4b2SGordon Ross
430252bc4b2SGordon Ross typedef struct _FILE_REGION_INFO {
431252bc4b2SGordon Ross uint64_t off;
432252bc4b2SGordon Ross uint64_t len;
433252bc4b2SGordon Ross uint32_t usage;
434252bc4b2SGordon Ross uint32_t reserved;
435252bc4b2SGordon Ross } FILE_REGION_INFO;
436252bc4b2SGordon Ross
437252bc4b2SGordon Ross
438252bc4b2SGordon Ross /*
439252bc4b2SGordon Ross * FSCTL_QUERY_FILE_REGIONS
440252bc4b2SGordon Ross *
441252bc4b2SGordon Ross * [MS-FSCC] 2.3.39 FSCTL_QUERY_FILE_REGIONS Request
442252bc4b2SGordon Ross * [MS-FSCC] 2.3.40.1 FILE_REGION_INFO
443252bc4b2SGordon Ross *
444252bc4b2SGordon Ross * Looks like Hyper-V uses this to query the "valid data length",
445252bc4b2SGordon Ross * which to us is the beginning offset of the last "hole".
446252bc4b2SGordon Ross * Similar logic as smb2_sparse_copy()
447252bc4b2SGordon Ross */
448252bc4b2SGordon Ross uint32_t
smb2_fsctl_query_file_regions(smb_request_t * sr,smb_fsctl_t * fsctl)449252bc4b2SGordon Ross smb2_fsctl_query_file_regions(smb_request_t *sr, smb_fsctl_t *fsctl)
450252bc4b2SGordon Ross {
451252bc4b2SGordon Ross smb_attr_t attr;
452252bc4b2SGordon Ross cred_t *kcr;
453252bc4b2SGordon Ross smb_ofile_t *ofile = sr->fid_ofile;
454252bc4b2SGordon Ross FILE_REGION_INFO arg;
455252bc4b2SGordon Ross off64_t cur_off, end_off, eof;
456252bc4b2SGordon Ross off64_t data, hole;
457252bc4b2SGordon Ross uint32_t tot_regions, put_regions;
458252bc4b2SGordon Ross uint32_t status;
459252bc4b2SGordon Ross int rc;
460252bc4b2SGordon Ross
461252bc4b2SGordon Ross if (fsctl->InputCount == 0) {
462252bc4b2SGordon Ross arg.off = 0;
463252bc4b2SGordon Ross arg.len = INT64_MAX;
464252bc4b2SGordon Ross arg.usage = FILE_REGION_USAGE_VALID_CACHED_DATA;
465252bc4b2SGordon Ross arg.reserved = 0;
466252bc4b2SGordon Ross } else {
467252bc4b2SGordon Ross /* min size check: reserved is optional */
468252bc4b2SGordon Ross rc = smb_mbc_decodef(fsctl->in_mbc, "qql",
469252bc4b2SGordon Ross &arg.off, &arg.len, &arg.usage);
470252bc4b2SGordon Ross if (rc != 0)
471252bc4b2SGordon Ross return (NT_STATUS_BUFFER_TOO_SMALL);
472252bc4b2SGordon Ross
473252bc4b2SGordon Ross /*
474252bc4b2SGordon Ross * The given offset and length are int64_t (signed).
475252bc4b2SGordon Ross */
476252bc4b2SGordon Ross if (arg.off > INT64_MAX || arg.len > INT64_MAX)
477252bc4b2SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
478252bc4b2SGordon Ross if ((arg.off + arg.len) > INT64_MAX)
479252bc4b2SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
480252bc4b2SGordon Ross if ((arg.usage & FILE_REGION_USAGE_VALID__MASK) == 0)
481252bc4b2SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
482252bc4b2SGordon Ross arg.reserved = 0;
483252bc4b2SGordon Ross }
484252bc4b2SGordon Ross
485252bc4b2SGordon Ross if (fsctl->MaxOutputResp < (16 + sizeof (FILE_REGION_INFO)))
486252bc4b2SGordon Ross return (NT_STATUS_BUFFER_TOO_SMALL);
487252bc4b2SGordon Ross
488252bc4b2SGordon Ross if (!smb_node_is_file(ofile->f_node))
489252bc4b2SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
490252bc4b2SGordon Ross
491252bc4b2SGordon Ross /*
492252bc4b2SGordon Ross * This operation is effectively a read
493252bc4b2SGordon Ross */
494252bc4b2SGordon Ross status = smb_ofile_access(ofile, ofile->f_cr, FILE_READ_DATA);
495252bc4b2SGordon Ross if (status != NT_STATUS_SUCCESS)
496252bc4b2SGordon Ross return (status);
497252bc4b2SGordon Ross
498252bc4b2SGordon Ross /*
499252bc4b2SGordon Ross * Need the file size and dosattr
500252bc4b2SGordon Ross */
501252bc4b2SGordon Ross bzero(&attr, sizeof (attr));
502252bc4b2SGordon Ross attr.sa_mask = SMB_AT_SIZE | SMB_AT_DOSATTR;
503252bc4b2SGordon Ross kcr = zone_kcred();
504252bc4b2SGordon Ross status = smb_node_getattr(sr, ofile->f_node, kcr, ofile, &attr);
505252bc4b2SGordon Ross if (status != NT_STATUS_SUCCESS)
506252bc4b2SGordon Ross return (status);
507252bc4b2SGordon Ross
508252bc4b2SGordon Ross cur_off = arg.off;
509252bc4b2SGordon Ross end_off = arg.off + arg.len;
510252bc4b2SGordon Ross eof = attr.sa_vattr.va_size;
511252bc4b2SGordon Ross
512252bc4b2SGordon Ross /*
513252bc4b2SGordon Ross * If (InputRegion.FileOffset > Eof) OR
514252bc4b2SGordon Ross * ((InputRegion.FileOffset == Eof) AND (Eof > 0)),
515252bc4b2SGordon Ross * the operation MUST return STATUS_SUCCESS, with
516252bc4b2SGordon Ross * BytesReturned set to 0 (empty data response)
517252bc4b2SGordon Ross */
518252bc4b2SGordon Ross if ((arg.off > eof) || (arg.off == eof && eof > 0))
519252bc4b2SGordon Ross return (NT_STATUS_SUCCESS);
520252bc4b2SGordon Ross if (end_off > eof)
521252bc4b2SGordon Ross end_off = eof;
522252bc4b2SGordon Ross
523252bc4b2SGordon Ross /*
524252bc4b2SGordon Ross * We're going to return at least one region. Put place-holder
525252bc4b2SGordon Ross * data for the fixed part of the response. Will overwrite this
526252bc4b2SGordon Ross * later, when we know how many regions there are and how many
527252bc4b2SGordon Ross * of those fit in the allowed response buffer space. These are:
528252bc4b2SGordon Ross * Flags, TotalRegionEntryCount, RegionEntryCount, Reserved
529252bc4b2SGordon Ross */
530252bc4b2SGordon Ross rc = smb_mbc_encodef(fsctl->out_mbc, "llll", 0, 0, 0, 0);
531252bc4b2SGordon Ross if (rc != 0)
532252bc4b2SGordon Ross return (NT_STATUS_BUFFER_TOO_SMALL);
533252bc4b2SGordon Ross tot_regions = put_regions = 0;
534252bc4b2SGordon Ross
535252bc4b2SGordon Ross /*
536252bc4b2SGordon Ross * Get the first pair of (data, hole) offsets at or after
537252bc4b2SGordon Ross * the current offset (cur_off).
538252bc4b2SGordon Ross */
539252bc4b2SGordon Ross data = hole = cur_off;
540252bc4b2SGordon Ross rc = smb_fsop_next_alloc_range(ofile->f_cr,
541252bc4b2SGordon Ross ofile->f_node, &data, &hole);
542252bc4b2SGordon Ross switch (rc) {
543252bc4b2SGordon Ross case 0:
544252bc4b2SGordon Ross /* Found (data, hole) */
545252bc4b2SGordon Ross break;
546252bc4b2SGordon Ross case ENXIO:
547252bc4b2SGordon Ross /* No more data after cur_off. */
548252bc4b2SGordon Ross break;
549252bc4b2SGordon Ross default:
550252bc4b2SGordon Ross cmn_err(CE_NOTE, "smb_fsop_next_alloc_range: rc=%d", rc);
551252bc4b2SGordon Ross /* FALLTHROUGH */
552252bc4b2SGordon Ross case ENOSYS: /* FS does not support VOP_IOCTL... */
553252bc4b2SGordon Ross case ENOTTY: /* ... or _FIO_SEEK_DATA, _HOLE */
554252bc4b2SGordon Ross data = cur_off;
555252bc4b2SGordon Ross hole = eof;
556252bc4b2SGordon Ross break;
557252bc4b2SGordon Ross }
558252bc4b2SGordon Ross DTRACE_PROBE2(range0, uint64_t, data, uint64_t, hole);
559252bc4b2SGordon Ross
560252bc4b2SGordon Ross /*
561252bc4b2SGordon Ross * Only sparse files should present un-allocated regions.
562252bc4b2SGordon Ross * If non-sparse, MS-FSA says to just return one region.
563252bc4b2SGordon Ross * There still can be a "hole" but only one, starting at
564252bc4b2SGordon Ross * "valid data length" (VDL) and ending at end of file.
565252bc4b2SGordon Ross * To determine VDL, find the last (data,hole) pair, then
566252bc4b2SGordon Ross * VDL is the last "hole" offset. Note that the above
567252bc4b2SGordon Ross * smb_fsop_next_alloc_range may have set data somewhere
568252bc4b2SGordon Ross * above cur_off, so we we have to reset that here.
569252bc4b2SGordon Ross */
570252bc4b2SGordon Ross if ((attr.sa_dosattr & FILE_ATTRIBUTE_SPARSE_FILE) == 0) {
571252bc4b2SGordon Ross /*
572252bc4b2SGordon Ross * This works, but it's rather inefficient, and
573252bc4b2SGordon Ross * usually just finds VDL==EOF. Should look into
574252bc4b2SGordon Ross * whether there's a faster way to find the VDL.
575252bc4b2SGordon Ross */
576252bc4b2SGordon Ross #if 0
577252bc4b2SGordon Ross off64_t next_data, next_hole;
578252bc4b2SGordon Ross data = cur_off;
579252bc4b2SGordon Ross do {
580252bc4b2SGordon Ross next_data = next_hole = hole;
581252bc4b2SGordon Ross rc = smb_fsop_next_alloc_range(ofile->f_cr,
582252bc4b2SGordon Ross ofile->f_node, &next_data, &next_hole);
583252bc4b2SGordon Ross if (rc == 0) {
584252bc4b2SGordon Ross hole = next_hole;
585252bc4b2SGordon Ross }
586252bc4b2SGordon Ross } while (rc == 0);
587252bc4b2SGordon Ross #else
588252bc4b2SGordon Ross /* Assume no "holes" anywhere (VDL==EOF) */
589252bc4b2SGordon Ross data = cur_off;
590252bc4b2SGordon Ross hole = eof;
591252bc4b2SGordon Ross #endif
592252bc4b2SGordon Ross DTRACE_PROBE2(range1, uint64_t, data, uint64_t, hole);
593252bc4b2SGordon Ross }
594252bc4b2SGordon Ross
595252bc4b2SGordon Ross /*
596252bc4b2SGordon Ross * Loop terminates in the middle, continuing
597252bc4b2SGordon Ross * while (cur_off < end_off)
598252bc4b2SGordon Ross */
599252bc4b2SGordon Ross for (;;) {
600252bc4b2SGordon Ross /*
601252bc4b2SGordon Ross * We have a data region that covers (data, hole).
602252bc4b2SGordon Ross * It could be partially or entirely beyond the range
603252bc4b2SGordon Ross * the caller asked about (if so trim it).
604252bc4b2SGordon Ross */
605252bc4b2SGordon Ross if (hole > end_off)
606252bc4b2SGordon Ross hole = end_off;
607252bc4b2SGordon Ross if (data > hole)
608252bc4b2SGordon Ross data = hole;
609252bc4b2SGordon Ross
610252bc4b2SGordon Ross /*
611252bc4b2SGordon Ross * If cur_off < data encode a "hole" region
612252bc4b2SGordon Ross * (cur_off,data) and advance cur_off.
613252bc4b2SGordon Ross */
614252bc4b2SGordon Ross if (cur_off < data) {
615252bc4b2SGordon Ross rc = smb_mbc_encodef(fsctl->out_mbc, "qqll",
616252bc4b2SGordon Ross cur_off,
617252bc4b2SGordon Ross (data - cur_off),
618252bc4b2SGordon Ross 0, // usage (hole)
619252bc4b2SGordon Ross 0); // reserved
620252bc4b2SGordon Ross cur_off = data;
621252bc4b2SGordon Ross if (rc == 0)
622252bc4b2SGordon Ross put_regions++;
623252bc4b2SGordon Ross tot_regions++;
624252bc4b2SGordon Ross }
625252bc4b2SGordon Ross
626252bc4b2SGordon Ross /*
627252bc4b2SGordon Ross * If cur_off < hole encode a "data" region
628252bc4b2SGordon Ross * (cur_off,hole) and advance cur_off.
629252bc4b2SGordon Ross */
630252bc4b2SGordon Ross if (cur_off < hole) {
631252bc4b2SGordon Ross rc = smb_mbc_encodef(fsctl->out_mbc, "qqll",
632252bc4b2SGordon Ross cur_off,
633252bc4b2SGordon Ross (hole - cur_off),
634252bc4b2SGordon Ross FILE_REGION_USAGE_VALID_CACHED_DATA,
635252bc4b2SGordon Ross 0); // reserved
636252bc4b2SGordon Ross cur_off = hole;
637252bc4b2SGordon Ross if (rc == 0)
638252bc4b2SGordon Ross put_regions++;
639252bc4b2SGordon Ross tot_regions++;
640252bc4b2SGordon Ross }
641252bc4b2SGordon Ross
642252bc4b2SGordon Ross /*
643252bc4b2SGordon Ross * Normal loop termination
644252bc4b2SGordon Ross */
645252bc4b2SGordon Ross if (cur_off >= end_off)
646252bc4b2SGordon Ross break;
647252bc4b2SGordon Ross
648252bc4b2SGordon Ross /*
649252bc4b2SGordon Ross * Get the next region (data, hole) starting on or after
650252bc4b2SGordon Ross * the current offset (cur_off).
651252bc4b2SGordon Ross */
652252bc4b2SGordon Ross data = hole = cur_off;
653252bc4b2SGordon Ross rc = smb_fsop_next_alloc_range(ofile->f_cr,
654252bc4b2SGordon Ross ofile->f_node, &data, &hole);
655252bc4b2SGordon Ross switch (rc) {
656252bc4b2SGordon Ross case 0:
657252bc4b2SGordon Ross /* Found (data, hole) */
658252bc4b2SGordon Ross break;
659252bc4b2SGordon Ross case ENXIO:
660252bc4b2SGordon Ross /*
661252bc4b2SGordon Ross * No more data after cur_off.
662252bc4b2SGordon Ross * Will encode one last hole.
663252bc4b2SGordon Ross */
664252bc4b2SGordon Ross data = hole = eof;
665252bc4b2SGordon Ross break;
666252bc4b2SGordon Ross default:
667252bc4b2SGordon Ross cmn_err(CE_NOTE, "smb_fsop_next_alloc_range: rc=%d",
668252bc4b2SGordon Ross rc);
669252bc4b2SGordon Ross /* FALLTHROUGH */
670252bc4b2SGordon Ross case ENOSYS: /* FS does not support VOP_IOCTL... */
671252bc4b2SGordon Ross case ENOTTY: /* ... or _FIO_SEEK_DATA, _HOLE */
672252bc4b2SGordon Ross data = cur_off;
673252bc4b2SGordon Ross hole = eof;
674252bc4b2SGordon Ross break;
675252bc4b2SGordon Ross }
676252bc4b2SGordon Ross DTRACE_PROBE2(range2, uint64_t, data, uint64_t, hole);
677252bc4b2SGordon Ross }
678252bc4b2SGordon Ross
679252bc4b2SGordon Ross /*
680252bc4b2SGordon Ross * Overwrite the fixed part of the response with the
681252bc4b2SGordon Ross * final numbers of regions etc.
682252bc4b2SGordon Ross * Flags, TotalRegionEntryCount, RegionEntryCount, Reserved
683252bc4b2SGordon Ross */
684252bc4b2SGordon Ross (void) smb_mbc_poke(fsctl->out_mbc, 0, "llll",
685252bc4b2SGordon Ross 0, // flags
686252bc4b2SGordon Ross tot_regions,
687252bc4b2SGordon Ross put_regions,
688252bc4b2SGordon Ross 0); // reserved
689252bc4b2SGordon Ross
690252bc4b2SGordon Ross if (put_regions < tot_regions)
691252bc4b2SGordon Ross return (NT_STATUS_BUFFER_OVERFLOW);
692252bc4b2SGordon Ross
693252bc4b2SGordon Ross return (NT_STATUS_SUCCESS);
694252bc4b2SGordon Ross }
695