xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_fsctl_copychunk.c (revision ca783257c986cddcc674ae22916a6766b98a2d36)
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 /*
1355f0a249SGordon Ross  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
1455f0a249SGordon Ross  */
1555f0a249SGordon Ross 
1655f0a249SGordon Ross /*
1755f0a249SGordon Ross  * Support functions for smb2_ioctl/fsctl codes:
1855f0a249SGordon Ross  * FSCTL_SRV_COPYCHUNK
1955f0a249SGordon Ross  * FSCTL_SRV_COPYCHUNK_WRITE
2055f0a249SGordon Ross  * (and related)
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 typedef struct chunk {
2855f0a249SGordon Ross 	uint64_t src_off;
2955f0a249SGordon Ross 	uint64_t dst_off;
3055f0a249SGordon Ross 	uint32_t length;
3155f0a249SGordon Ross 	uint32_t _reserved;
3255f0a249SGordon Ross } chunk_t;
3355f0a249SGordon Ross 
3455f0a249SGordon Ross struct copychunk_resp {
3555f0a249SGordon Ross 	uint32_t ChunksWritten;
3655f0a249SGordon Ross 	uint32_t ChunkBytesWritten;
3755f0a249SGordon Ross 	uint32_t TotalBytesWritten;
3855f0a249SGordon Ross };
3955f0a249SGordon Ross 
4055f0a249SGordon Ross typedef struct copychunk_args {
4155f0a249SGordon Ross 	smb_attr_t src_attr;
4255f0a249SGordon Ross 	void *buffer;
4355f0a249SGordon Ross 	size_t bufsize;
4455f0a249SGordon Ross 	uint32_t ccnt;
4555f0a249SGordon Ross 	chunk_t cvec[1]; /* actually longer */
4655f0a249SGordon Ross } copychunk_args_t;
4755f0a249SGordon Ross 
4855f0a249SGordon Ross uint32_t smb2_copychunk_max_cnt = 256;
4955f0a249SGordon Ross uint32_t smb2_copychunk_max_seg = (1<<20); /* 1M, == smb2_max_rwsize */
5055f0a249SGordon Ross uint32_t smb2_copychunk_max_total = (1<<24); /* 16M */
5155f0a249SGordon Ross 
5255f0a249SGordon Ross static uint32_t smb2_fsctl_copychunk_decode(smb_request_t *, mbuf_chain_t *);
5355f0a249SGordon Ross static uint32_t smb2_fsctl_copychunk_array(smb_request_t *, smb_ofile_t *,
5455f0a249SGordon Ross 	struct copychunk_resp *);
5555f0a249SGordon Ross static uint32_t smb2_fsctl_copychunk_aapl(smb_request_t *, smb_ofile_t *,
5655f0a249SGordon Ross 	struct copychunk_resp *);
5755f0a249SGordon Ross static uint32_t smb2_fsctl_copychunk_1(smb_request_t *, smb_ofile_t *,
5855f0a249SGordon Ross 	struct chunk *);
5955f0a249SGordon Ross static int smb2_fsctl_copychunk_meta(smb_request_t *, smb_ofile_t *);
6055f0a249SGordon Ross 
6155f0a249SGordon Ross /*
6255f0a249SGordon Ross  * FSCTL_SRV_COPYCHUNK
6355f0a249SGordon Ross  * FSCTL_SRV_COPYCHUNK_WRITE
6455f0a249SGordon Ross  *
6555f0a249SGordon Ross  * Copies from a source file identified by a "resume key"
6655f0a249SGordon Ross  * (previously returned by FSCTL_SRV_REQUEST_RESUME_KEY)
6755f0a249SGordon Ross  * to the file on which the ioctl is issues.
6855f0a249SGordon Ross  *
6955f0a249SGordon Ross  * The fsctl appears to _always_ respond with a data payload
7055f0a249SGordon Ross  * (struct copychunk_resp), even on fatal errors.  Note that
7155f0a249SGordon Ross  * smb2_ioctl also has special handling to allow that.
7255f0a249SGordon Ross  */
7355f0a249SGordon Ross uint32_t
smb2_fsctl_copychunk(smb_request_t * sr,smb_fsctl_t * fsctl)7455f0a249SGordon Ross smb2_fsctl_copychunk(smb_request_t *sr, smb_fsctl_t *fsctl)
7555f0a249SGordon Ross {
7655f0a249SGordon Ross 	struct copychunk_resp ccr;
7755f0a249SGordon Ross 	smb_ofile_t *dst_of = sr->fid_ofile;
7855f0a249SGordon Ross 	smb_ofile_t *src_of = NULL;
7955f0a249SGordon Ross 	copychunk_args_t *args = NULL;
8055f0a249SGordon Ross 	smb2fid_t smb2fid;
8155f0a249SGordon Ross 	uint32_t status = NT_STATUS_INVALID_PARAMETER;
8255f0a249SGordon Ross 	uint32_t desired_access; /* for dest */
8355f0a249SGordon Ross 	uint32_t chunk_cnt;
8455f0a249SGordon Ross 	int rc;
8555f0a249SGordon Ross 	boolean_t aapl_copyfile = B_FALSE;
8655f0a249SGordon Ross 
8755f0a249SGordon Ross 	bzero(&ccr, sizeof (ccr));
8855f0a249SGordon Ross 	if (fsctl->MaxOutputResp < sizeof (ccr)) {
8955f0a249SGordon Ross 		status = NT_STATUS_INVALID_PARAMETER;
9055f0a249SGordon Ross 		goto out;
9155f0a249SGordon Ross 	}
9255f0a249SGordon Ross 
9355f0a249SGordon Ross 	/*
9455f0a249SGordon Ross 	 * Make sure dst_of is open on a regular file, and
9555f0a249SGordon Ross 	 * granted access is sufficient for this operation.
9655f0a249SGordon Ross 	 * FSCTL_SRV_COPYCHUNK requires READ+WRITE
9755f0a249SGordon Ross 	 * FSCTL_SRV_COPYCHUNK_WRITE just WRITE
9855f0a249SGordon Ross 	 */
9955f0a249SGordon Ross 	if (!smb_node_is_file(dst_of->f_node)) {
10055f0a249SGordon Ross 		status = NT_STATUS_ACCESS_DENIED;
10155f0a249SGordon Ross 		goto out;
10255f0a249SGordon Ross 	}
10355f0a249SGordon Ross 	desired_access = FILE_WRITE_DATA;
10455f0a249SGordon Ross 	if (fsctl->CtlCode == FSCTL_SRV_COPYCHUNK)
10555f0a249SGordon Ross 		desired_access |= FILE_READ_DATA;
10655f0a249SGordon Ross 	status = smb_ofile_access(dst_of, dst_of->f_cr, desired_access);
10755f0a249SGordon Ross 	if (status != NT_STATUS_SUCCESS)
10855f0a249SGordon Ross 		goto out;
10955f0a249SGordon Ross 
11055f0a249SGordon Ross 	/*
11155f0a249SGordon Ross 	 * Decode the resume key (src file ID) and length of the
11255f0a249SGordon Ross 	 * "chunks" array.  Note the resume key is 24 bytes of
11355f0a249SGordon Ross 	 * opaque data from FSCTL_SRV_REQUEST_RESUME_KEY, but
11455f0a249SGordon Ross 	 * here know it's an smb2fid plus 8 bytes of padding.
11555f0a249SGordon Ross 	 */
11655f0a249SGordon Ross 	rc = smb_mbc_decodef(
11755f0a249SGordon Ross 	    fsctl->in_mbc, "qq8.l4.",
11855f0a249SGordon Ross 	    &smb2fid.persistent,	/* q */
11955f0a249SGordon Ross 	    &smb2fid.temporal,		/* q */
12055f0a249SGordon Ross 	    /* pad			  8. */
12155f0a249SGordon Ross 	    &chunk_cnt);		/* l */
12255f0a249SGordon Ross 	/*			reserved  4. */
12355f0a249SGordon Ross 	if (rc != 0) {
12455f0a249SGordon Ross 		status = NT_STATUS_INVALID_PARAMETER;
12555f0a249SGordon Ross 		goto out;
12655f0a249SGordon Ross 	}
12755f0a249SGordon Ross 
12855f0a249SGordon Ross 	/*
12955f0a249SGordon Ross 	 * Lookup the source ofile using the resume key,
13055f0a249SGordon Ross 	 * which smb2_fsctl_get_resume_key encoded as an
13155f0a249SGordon Ross 	 * smb2fid_t.  Similar to smb2sr_lookup_fid(),
13255f0a249SGordon Ross 	 * but different error code.
13355f0a249SGordon Ross 	 */
13455f0a249SGordon Ross 	src_of = smb_ofile_lookup_by_fid(sr, (uint16_t)smb2fid.temporal);
13555f0a249SGordon Ross 	if (src_of == NULL ||
13655f0a249SGordon Ross 	    src_of->f_persistid != smb2fid.persistent) {
13755f0a249SGordon Ross 		status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
13855f0a249SGordon Ross 		goto out;
13955f0a249SGordon Ross 	}
14055f0a249SGordon Ross 
14155f0a249SGordon Ross 	/*
14255f0a249SGordon Ross 	 * Make sure src_of is open on a regular file, and
14355f0a249SGordon Ross 	 * granted access includes READ_DATA
14455f0a249SGordon Ross 	 */
14555f0a249SGordon Ross 	if (!smb_node_is_file(src_of->f_node)) {
14655f0a249SGordon Ross 		status = NT_STATUS_ACCESS_DENIED;
14755f0a249SGordon Ross 		goto out;
14855f0a249SGordon Ross 	}
14955f0a249SGordon Ross 	status = smb_ofile_access(src_of, src_of->f_cr, FILE_READ_DATA);
15055f0a249SGordon Ross 	if (status != NT_STATUS_SUCCESS)
15155f0a249SGordon Ross 		goto out;
15255f0a249SGordon Ross 
15355f0a249SGordon Ross 	/*
15455f0a249SGordon Ross 	 * Before decoding the chunks array, check the size.  Note:
15555f0a249SGordon Ross 	 * When we offer the AAPL extensions, MacOS clients assume
15655f0a249SGordon Ross 	 * they can use chunk_cnt==0 to mean "copy the whole file".
15755f0a249SGordon Ross 	 */
15855f0a249SGordon Ross 	if (chunk_cnt == 0) {
15955f0a249SGordon Ross 		if ((sr->session->s_flags & SMB_SSN_AAPL_CCEXT) != 0) {
16055f0a249SGordon Ross 			aapl_copyfile = B_TRUE;
16155f0a249SGordon Ross 		} else {
16255f0a249SGordon Ross 			status = NT_STATUS_INVALID_PARAMETER;
16355f0a249SGordon Ross 			goto out;
16455f0a249SGordon Ross 		}
16555f0a249SGordon Ross 	}
16655f0a249SGordon Ross 	if (chunk_cnt > smb2_copychunk_max_cnt) {
16755f0a249SGordon Ross 		status = NT_STATUS_INVALID_PARAMETER;
16855f0a249SGordon Ross 		goto out;
16955f0a249SGordon Ross 	}
17055f0a249SGordon Ross 
17155f0a249SGordon Ross 	/*
17255f0a249SGordon Ross 	 * Get some memory for the array of chunks and decode it.
17355f0a249SGordon Ross 	 * Also checks the per-chunk and total size limits.
17455f0a249SGordon Ross 	 * Note that chunk_cnt may be zero here (MacOS).
17555f0a249SGordon Ross 	 */
17655f0a249SGordon Ross 	args = smb_srm_zalloc(sr, sizeof (*args) +
17755f0a249SGordon Ross 	    (chunk_cnt * sizeof (args->cvec)));
17855f0a249SGordon Ross 	args->ccnt = chunk_cnt;
17955f0a249SGordon Ross 	sr->arg.other = args;
18055f0a249SGordon Ross 	if (chunk_cnt > 0) {
18155f0a249SGordon Ross 		status = smb2_fsctl_copychunk_decode(sr, fsctl->in_mbc);
18255f0a249SGordon Ross 		if (status != 0)
18355f0a249SGordon Ross 			goto out;
18455f0a249SGordon Ross 	}
18555f0a249SGordon Ross 
18655f0a249SGordon Ross 	/*
18755f0a249SGordon Ross 	 * Normally need just the source file size, etc.  If doing
18855f0a249SGordon Ross 	 * Apple server-side copy, we want all the attributes.
18955f0a249SGordon Ross 	 */
19055f0a249SGordon Ross 	if (aapl_copyfile)
19155f0a249SGordon Ross 		args->src_attr.sa_mask = SMB_AT_ALL;
19255f0a249SGordon Ross 	else
19355f0a249SGordon Ross 		args->src_attr.sa_mask = SMB_AT_STANDARD;
19455f0a249SGordon Ross 	status = smb2_ofile_getattr(sr, src_of, &args->src_attr);
19555f0a249SGordon Ross 	if (status != 0)
19655f0a249SGordon Ross 		goto out;
19755f0a249SGordon Ross 
19855f0a249SGordon Ross 	/*
19955f0a249SGordon Ross 	 * Get a buffer used for copying, always
20055f0a249SGordon Ross 	 * smb2_copychunk_max_seg (1M)
20155f0a249SGordon Ross 	 *
20255f0a249SGordon Ross 	 * Rather than sleep for this relatively large allocation,
20355f0a249SGordon Ross 	 * allow the allocation to fail and return an error.
20455f0a249SGordon Ross 	 * The client should then fall back to normal copy.
20555f0a249SGordon Ross 	 */
20655f0a249SGordon Ross 	args->bufsize = smb2_copychunk_max_seg;
207*ca783257SDan McDonald 	args->buffer = kmem_alloc(args->bufsize, KM_NOSLEEP_LAZY);
20855f0a249SGordon Ross 	if (args->buffer == NULL) {
20955f0a249SGordon Ross 		status = NT_STATUS_INSUFF_SERVER_RESOURCES;
21055f0a249SGordon Ross 		goto out;
21155f0a249SGordon Ross 	}
21255f0a249SGordon Ross 
21355f0a249SGordon Ross 	/*
21455f0a249SGordon Ross 	 * Finally, do the I/O
21555f0a249SGordon Ross 	 */
21655f0a249SGordon Ross 	if (aapl_copyfile) {
21755f0a249SGordon Ross 		status = smb2_fsctl_copychunk_aapl(sr, src_of, &ccr);
21855f0a249SGordon Ross 	} else {
21955f0a249SGordon Ross 		status = smb2_fsctl_copychunk_array(sr, src_of, &ccr);
22055f0a249SGordon Ross 	}
22155f0a249SGordon Ross 
22255f0a249SGordon Ross out:
22355f0a249SGordon Ross 	if (args != NULL) {
22455f0a249SGordon Ross 		if (args->buffer != NULL) {
22555f0a249SGordon Ross 			kmem_free(args->buffer, args->bufsize);
22655f0a249SGordon Ross 		}
22755f0a249SGordon Ross 	}
22855f0a249SGordon Ross 
22955f0a249SGordon Ross 	if (src_of != NULL)
23055f0a249SGordon Ross 		smb_ofile_release(src_of);
23155f0a249SGordon Ross 
23255f0a249SGordon Ross 	if (status == NT_STATUS_INVALID_PARAMETER) {
23355f0a249SGordon Ross 		/*
23455f0a249SGordon Ross 		 * Tell the client our max chunk cnt, size, etc.
23555f0a249SGordon Ross 		 */
23655f0a249SGordon Ross 		ccr.ChunksWritten	= smb2_copychunk_max_cnt;
23755f0a249SGordon Ross 		ccr.ChunkBytesWritten	= smb2_copychunk_max_seg;
23855f0a249SGordon Ross 		ccr.TotalBytesWritten	= smb2_copychunk_max_total;
23955f0a249SGordon Ross 	}
24055f0a249SGordon Ross 
24155f0a249SGordon Ross 	/* Checked MaxOutputResp above, so ignore errors here */
24255f0a249SGordon Ross 	(void) smb_mbc_encodef(
24355f0a249SGordon Ross 	    fsctl->out_mbc, "lll",
24455f0a249SGordon Ross 	    ccr.ChunksWritten,
24555f0a249SGordon Ross 	    ccr.ChunkBytesWritten,
24655f0a249SGordon Ross 	    ccr.TotalBytesWritten);
24755f0a249SGordon Ross 
24855f0a249SGordon Ross 	sr->arg.other = NULL;
24955f0a249SGordon Ross 	/* smb_srm_fini will free args */
25055f0a249SGordon Ross 
25155f0a249SGordon Ross 	return (status);
25255f0a249SGordon Ross }
25355f0a249SGordon Ross 
25455f0a249SGordon Ross /*
25555f0a249SGordon Ross  * Decode the list of chunks and check each.
25655f0a249SGordon Ross  */
25755f0a249SGordon Ross static uint32_t
smb2_fsctl_copychunk_decode(smb_request_t * sr,mbuf_chain_t * mbc)25855f0a249SGordon Ross smb2_fsctl_copychunk_decode(smb_request_t *sr, mbuf_chain_t *mbc)
25955f0a249SGordon Ross {
26055f0a249SGordon Ross 	copychunk_args_t *args = sr->arg.other;
26155f0a249SGordon Ross 	chunk_t *cc;
26255f0a249SGordon Ross 	uint32_t status = NT_STATUS_INVALID_PARAMETER;
26355f0a249SGordon Ross 	uint32_t total_len = 0;
26455f0a249SGordon Ross 	int i, rc;
26555f0a249SGordon Ross 
26655f0a249SGordon Ross 	for (i = 0; i < args->ccnt; i++) {
26755f0a249SGordon Ross 		cc = &args->cvec[i];
26855f0a249SGordon Ross 		rc = smb_mbc_decodef(
26955f0a249SGordon Ross 		    mbc, "qqll",
27055f0a249SGordon Ross 		    &cc->src_off,	/* q */
27155f0a249SGordon Ross 		    &cc->dst_off,	/* q */
27255f0a249SGordon Ross 		    &cc->length,	/* l */
27355f0a249SGordon Ross 		    &cc->_reserved);	/* l */
27455f0a249SGordon Ross 		if (rc != 0 || cc->length == 0 ||
27555f0a249SGordon Ross 		    cc->length > smb2_copychunk_max_seg)
27655f0a249SGordon Ross 			goto out;
27755f0a249SGordon Ross 		total_len += cc->length;
27855f0a249SGordon Ross 	}
27955f0a249SGordon Ross 	if (total_len > smb2_copychunk_max_total)
28055f0a249SGordon Ross 		goto out;
28155f0a249SGordon Ross 	status = 0;
28255f0a249SGordon Ross 
28355f0a249SGordon Ross out:
28455f0a249SGordon Ross 	return (status);
28555f0a249SGordon Ross }
28655f0a249SGordon Ross 
28755f0a249SGordon Ross /*
28855f0a249SGordon Ross  * Run the actual I/O described by the copychunks array.
28955f0a249SGordon Ross  * (normal, non-apple case)
29055f0a249SGordon Ross  */
29155f0a249SGordon Ross static uint32_t
smb2_fsctl_copychunk_array(smb_request_t * sr,smb_ofile_t * src_of,struct copychunk_resp * ccr)29255f0a249SGordon Ross smb2_fsctl_copychunk_array(smb_request_t *sr, smb_ofile_t *src_of,
29355f0a249SGordon Ross 	struct copychunk_resp *ccr)
29455f0a249SGordon Ross {
29555f0a249SGordon Ross 	copychunk_args_t *args = sr->arg.other;
29655f0a249SGordon Ross 	chunk_t *cc;
29755f0a249SGordon Ross 	uint64_t src_size = args->src_attr.sa_vattr.va_size;
29855f0a249SGordon Ross 	uint32_t save_len;
29955f0a249SGordon Ross 	uint32_t copied;
30055f0a249SGordon Ross 	uint32_t status = 0;
30155f0a249SGordon Ross 	int i;
30255f0a249SGordon Ross 
30355f0a249SGordon Ross 	for (i = 0; i < args->ccnt; i++) {
30455f0a249SGordon Ross 		cc = &args->cvec[i];
30555f0a249SGordon Ross 
30655f0a249SGordon Ross 		/* Chunk must be entirely within file bounds. */
30755f0a249SGordon Ross 		if (cc->src_off > src_size ||
30855f0a249SGordon Ross 		    (cc->src_off + cc->length) < cc->src_off ||
30955f0a249SGordon Ross 		    (cc->src_off + cc->length) > src_size) {
31055f0a249SGordon Ross 			status = NT_STATUS_INVALID_VIEW_SIZE;
31155f0a249SGordon Ross 			goto out;
31255f0a249SGordon Ross 		}
31355f0a249SGordon Ross 
31455f0a249SGordon Ross 		save_len = cc->length;
31555f0a249SGordon Ross 		status = smb2_fsctl_copychunk_1(sr, src_of, cc);
31655f0a249SGordon Ross 		if (status != 0) {
31755f0a249SGordon Ross 			/* no part of this chunk written */
31855f0a249SGordon Ross 			break;
31955f0a249SGordon Ross 		}
32055f0a249SGordon Ross 		/*
32155f0a249SGordon Ross 		 * All or part of the chunk written.
32255f0a249SGordon Ross 		 * cc->length is now the resid count.
32355f0a249SGordon Ross 		 */
32455f0a249SGordon Ross 		copied = save_len - cc->length;
32555f0a249SGordon Ross 		ccr->TotalBytesWritten += copied;
32655f0a249SGordon Ross 		if (cc->length != 0) {
32755f0a249SGordon Ross 			/* Did not write the whole chunk */
32855f0a249SGordon Ross 			ccr->ChunkBytesWritten = copied;
32955f0a249SGordon Ross 			break;
33055f0a249SGordon Ross 		}
33155f0a249SGordon Ross 		/* Whole chunk moved. */
33255f0a249SGordon Ross 		ccr->ChunksWritten++;
33355f0a249SGordon Ross 	}
33455f0a249SGordon Ross 	if (ccr->ChunksWritten > 0)
33555f0a249SGordon Ross 		status = NT_STATUS_SUCCESS;
33655f0a249SGordon Ross 
33755f0a249SGordon Ross out:
33855f0a249SGordon Ross 	return (status);
33955f0a249SGordon Ross }
34055f0a249SGordon Ross 
34155f0a249SGordon Ross /*
34255f0a249SGordon Ross  * Helper for smb2_fsctl_copychunk, where MacOS uses chunk_cnt==0
34355f0a249SGordon Ross  * to mean "copy the whole file".  This interface does not have any
34455f0a249SGordon Ross  * way to report a partial copy (client ignores copychunk_resp) so
34555f0a249SGordon Ross  * if that happens we just report an error.
34655f0a249SGordon Ross  *
34755f0a249SGordon Ross  * This extension makes no provision for the server to impose any
34855f0a249SGordon Ross  * bound on the amount of data moved by one SMB copychunk request.
34955f0a249SGordon Ross  * We could impose a total size, but it's hard to know what size
35055f0a249SGordon Ross  * would be an appropriate limit because performance of various
35155f0a249SGordon Ross  * storage subsystems can vary quite a bit.  The best we can do is
35255f0a249SGordon Ross  * limit the time we spend in this copy, and allow cancellation.
35355f0a249SGordon Ross  */
35455f0a249SGordon Ross int smb2_fsctl_copychunk_aapl_timeout = 10;	/* sec */
35555f0a249SGordon Ross static uint32_t
smb2_fsctl_copychunk_aapl(smb_request_t * sr,smb_ofile_t * src_of,struct copychunk_resp * ccr)35655f0a249SGordon Ross smb2_fsctl_copychunk_aapl(smb_request_t *sr, smb_ofile_t *src_of,
35755f0a249SGordon Ross 	struct copychunk_resp *ccr)
35855f0a249SGordon Ross {
35955f0a249SGordon Ross 	copychunk_args_t *args = sr->arg.other;
36055f0a249SGordon Ross 	chunk_t *cc = args->cvec; /* always at least one element */
36155f0a249SGordon Ross 	uint64_t src_size = args->src_attr.sa_vattr.va_size;
36255f0a249SGordon Ross 	uint64_t off;
36355f0a249SGordon Ross 	uint32_t xfer;
36455f0a249SGordon Ross 	uint32_t status = 0;
36555f0a249SGordon Ross 	hrtime_t end_time = sr->sr_time_active +
36655f0a249SGordon Ross 	    (smb2_fsctl_copychunk_aapl_timeout * NANOSEC);
36755f0a249SGordon Ross 
36855f0a249SGordon Ross 	off = 0;
36955f0a249SGordon Ross 	while (off < src_size) {
37055f0a249SGordon Ross 		/*
37155f0a249SGordon Ross 		 * Check that (a) the request has not been cancelled,
37255f0a249SGordon Ross 		 * and (b) we've not run past the timeout.
37355f0a249SGordon Ross 		 */
37455f0a249SGordon Ross 		if (sr->sr_state != SMB_REQ_STATE_ACTIVE)
37555f0a249SGordon Ross 			return (NT_STATUS_CANCELLED);
37655f0a249SGordon Ross 		if (gethrtime() > end_time)
37755f0a249SGordon Ross 			return (NT_STATUS_IO_TIMEOUT);
37855f0a249SGordon Ross 
37955f0a249SGordon Ross 		xfer = smb2_copychunk_max_seg;
38055f0a249SGordon Ross 		if (off + xfer > src_size)
38155f0a249SGordon Ross 			xfer = (uint32_t)(src_size - off);
38255f0a249SGordon Ross 		cc->src_off = off;
38355f0a249SGordon Ross 		cc->dst_off = off;
38455f0a249SGordon Ross 		cc->length = xfer;
38555f0a249SGordon Ross 		status = smb2_fsctl_copychunk_1(sr, src_of, cc);
38655f0a249SGordon Ross 		if (status != 0)
38755f0a249SGordon Ross 			break;
38855f0a249SGordon Ross 		if (cc->length != 0) {
38955f0a249SGordon Ross 			status = NT_STATUS_PARTIAL_COPY;
39055f0a249SGordon Ross 			break;
39155f0a249SGordon Ross 		}
39255f0a249SGordon Ross 		/*
39355f0a249SGordon Ross 		 * Whole chunk moved.  It appears that MacOS clients
39455f0a249SGordon Ross 		 * ignore the response here, but let's put something
39555f0a249SGordon Ross 		 * meaningful in it anyway, so one can see how far
39655f0a249SGordon Ross 		 * the copy went by looking at a network trace.
39755f0a249SGordon Ross 		 */
39855f0a249SGordon Ross 		ccr->TotalBytesWritten += xfer;
39955f0a249SGordon Ross 		ccr->ChunksWritten++;
40055f0a249SGordon Ross 		off += xfer;
40155f0a249SGordon Ross 	}
40255f0a249SGordon Ross 
40355f0a249SGordon Ross 	/*
40455f0a249SGordon Ross 	 * MacOS servers also copy meta-data from the old to new file.
40555f0a249SGordon Ross 	 * We need to do this because Finder does not set the meta-data
40655f0a249SGordon Ross 	 * when copying a file with this interface.  If we fail to copy
40755f0a249SGordon Ross 	 * the meta-data, just log.  We'd rather not fail the entire
40855f0a249SGordon Ross 	 * copy job if this fails.
40955f0a249SGordon Ross 	 */
41055f0a249SGordon Ross 	if (status == 0) {
41155f0a249SGordon Ross 		int rc = smb2_fsctl_copychunk_meta(sr, src_of);
41255f0a249SGordon Ross 		if (rc != 0) {
41355f0a249SGordon Ross 			cmn_err(CE_NOTE, "smb2 copychunk meta, rc=%d", rc);
41455f0a249SGordon Ross 		}
41555f0a249SGordon Ross 	}
41655f0a249SGordon Ross 
41755f0a249SGordon Ross 	return (status);
41855f0a249SGordon Ross }
41955f0a249SGordon Ross 
42055f0a249SGordon Ross /*
42155f0a249SGordon Ross  * Helper for Apple copychunk, to copy meta-data
42255f0a249SGordon Ross  */
42355f0a249SGordon Ross static int
smb2_fsctl_copychunk_meta(smb_request_t * sr,smb_ofile_t * src_of)42455f0a249SGordon Ross smb2_fsctl_copychunk_meta(smb_request_t *sr, smb_ofile_t *src_of)
42555f0a249SGordon Ross {
42655f0a249SGordon Ross 	smb_fssd_t fs_sd;
42755f0a249SGordon Ross 	copychunk_args_t *args = sr->arg.other;
42855f0a249SGordon Ross 	smb_ofile_t *dst_of = sr->fid_ofile;
42955f0a249SGordon Ross 	uint32_t sd_flags = 0;
43055f0a249SGordon Ross 	uint32_t secinfo = SMB_DACL_SECINFO;
43155f0a249SGordon Ross 	int error;
43255f0a249SGordon Ross 
43355f0a249SGordon Ross 	/*
43455f0a249SGordon Ross 	 * Copy attributes.  We obtained SMB_AT_ALL above.
43555f0a249SGordon Ross 	 * Now correct the mask for what's settable.
43655f0a249SGordon Ross 	 */
43755f0a249SGordon Ross 	args->src_attr.sa_mask = SMB_AT_MODE | SMB_AT_SIZE |
43855f0a249SGordon Ross 	    SMB_AT_ATIME | SMB_AT_MTIME | SMB_AT_CTIME |
43955f0a249SGordon Ross 	    SMB_AT_DOSATTR | SMB_AT_ALLOCSZ;
44055f0a249SGordon Ross 	error = smb_node_setattr(sr, dst_of->f_node, sr->user_cr,
44155f0a249SGordon Ross 	    dst_of, &args->src_attr);
44255f0a249SGordon Ross 	if (error != 0)
44355f0a249SGordon Ross 		return (error);
44455f0a249SGordon Ross 
44555f0a249SGordon Ross 	/*
44655f0a249SGordon Ross 	 * Copy the ACL.  Unfortunately, the ofiles used by the Mac
44755f0a249SGordon Ross 	 * here don't generally have WRITE_DAC access (sigh) so we
44855f0a249SGordon Ross 	 * have to bypass ofile access checks for this operation.
44955f0a249SGordon Ross 	 * The file-system level still does its access checking.
450d11e14a7SMatt Barden 	 *
451d11e14a7SMatt Barden 	 * TODO: this should really copy the SACL, too.
45255f0a249SGordon Ross 	 */
45355f0a249SGordon Ross 	smb_fssd_init(&fs_sd, secinfo, sd_flags);
45455f0a249SGordon Ross 	sr->fid_ofile = NULL;
45555f0a249SGordon Ross 	error = smb_fsop_sdread(sr, sr->user_cr, src_of->f_node, &fs_sd);
45655f0a249SGordon Ross 	if (error == 0) {
45755f0a249SGordon Ross 		error = smb_fsop_sdwrite(sr, sr->user_cr, dst_of->f_node,
45855f0a249SGordon Ross 		    &fs_sd, 1);
45955f0a249SGordon Ross 	}
46055f0a249SGordon Ross 	sr->fid_ofile = dst_of;
46155f0a249SGordon Ross 	smb_fssd_term(&fs_sd);
46255f0a249SGordon Ross 
46355f0a249SGordon Ross 	return (error);
46455f0a249SGordon Ross }
46555f0a249SGordon Ross 
46655f0a249SGordon Ross /*
46755f0a249SGordon Ross  * Copy one chunk from src_of to sr->fid_ofile,
46855f0a249SGordon Ross  * with offsets and length from chunk *cc
46955f0a249SGordon Ross  */
47055f0a249SGordon Ross static uint32_t
smb2_fsctl_copychunk_1(smb_request_t * sr,smb_ofile_t * src_ofile,struct chunk * cc)47155f0a249SGordon Ross smb2_fsctl_copychunk_1(smb_request_t *sr, smb_ofile_t *src_ofile,
47255f0a249SGordon Ross     struct chunk *cc)
47355f0a249SGordon Ross {
47455f0a249SGordon Ross 	copychunk_args_t *args = sr->arg.other;
47555f0a249SGordon Ross 	smb_ofile_t *dst_ofile = sr->fid_ofile;
47655f0a249SGordon Ross 	uint32_t status;
47755f0a249SGordon Ross 
47855f0a249SGordon Ross 	if (cc->length > args->bufsize)
47955f0a249SGordon Ross 		return (NT_STATUS_INTERNAL_ERROR);
48055f0a249SGordon Ross 
48155f0a249SGordon Ross 	/*
48255f0a249SGordon Ross 	 * Check for lock conflicting with the read.
48355f0a249SGordon Ross 	 */
48455f0a249SGordon Ross 	status = smb_lock_range_access(sr, src_ofile->f_node,
48555f0a249SGordon Ross 	    cc->src_off, cc->length, B_FALSE);
48655f0a249SGordon Ross 	if (status != 0)
48755f0a249SGordon Ross 		return (status);
48855f0a249SGordon Ross 
48955f0a249SGordon Ross 	/*
49055f0a249SGordon Ross 	 * Check for lock conflicting with the write.
49155f0a249SGordon Ross 	 */
49255f0a249SGordon Ross 	status = smb_lock_range_access(sr, dst_ofile->f_node,
49355f0a249SGordon Ross 	    cc->dst_off, cc->length, B_TRUE);
49455f0a249SGordon Ross 	if (status != 0)
49555f0a249SGordon Ross 		return (status);
49655f0a249SGordon Ross 
49755f0a249SGordon Ross 	/*
49855f0a249SGordon Ross 	 * Copy src to dst for cc->length
49955f0a249SGordon Ross 	 */
50055f0a249SGordon Ross 	status = smb2_sparse_copy(sr, src_ofile, dst_ofile,
50155f0a249SGordon Ross 	    cc->src_off, cc->dst_off, &cc->length,
50255f0a249SGordon Ross 	    args->buffer, args->bufsize);
50355f0a249SGordon Ross 
50455f0a249SGordon Ross 	return (status);
50555f0a249SGordon Ross }
506