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 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; 20755f0a249SGordon Ross args->buffer = kmem_alloc(args->bufsize, KM_NOSLEEP | KM_NORMALPRI); 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 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 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 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 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. 450*d11e14a7SMatt Barden * 451*d11e14a7SMatt 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 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