- diff --git a/daemon/daemon_debug.c b/daemon/daemon_debug.c
- index 20fd420..02a0aec 100644
- --- a/daemon/daemon_debug.c
- +++ b/daemon/daemon_debug.c
- @@ -485,6 +485,7 @@ const char* opcode2string(nfs41_opcodes opcode)
- NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES)
- NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_FSCTL_SET_ZERO_DATA)
- NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_FSCTL_DUPLICATE_DATA)
- + NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY)
- NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_INVALID_OPCODE1)
- default: break;
- }
- diff --git a/daemon/fsctl.c b/daemon/fsctl.c
- index e1c4f19..fbbd8aa 100644
- --- a/daemon/fsctl.c
- +++ b/daemon/fsctl.c
- @@ -27,9 +27,6 @@
- #include "daemon_debug.h"
- #include "util.h"
- -/* Testing only: Use NFS COPY instead of NFS CLONE */
- -// #define DUP_DATA_USE_NFSCOPY 1
- -
- #define QARLVL 2 /* dprintf level for "query allocated ranges" logging */
- #define SZDLVL 2 /* dprintf level for "set zero data" logging */
- #define DDLVL 2 /* dprintf level for "duplicate data" logging */
- @@ -470,7 +467,8 @@ out:
- }
- static
- -int duplicate_sparsefile(nfs41_open_state *src_state,
- +int duplicate_sparsefile(nfs41_opcodes opcode,
- + nfs41_open_state *src_state,
- nfs41_open_state *dst_state,
- uint64_t srcfileoffset,
- uint64_t destfileoffset,
- @@ -498,7 +496,8 @@ int duplicate_sparsefile(nfs41_open_state *src_state,
- (void)memset(info, 0, sizeof(*info));
- DPRINTF(DDLVL,
- - ("--> duplicate_sparsefile(src_state->path.path='%s')\n",
- + ("--> duplicate_sparsefile(opcode='%s',src_state->path.path='%s')\n",
- + opcode2string(opcode),
- src_state->path.path));
- nfs41_open_stateid_arg(src_state, &src_stateid);
- @@ -518,9 +517,11 @@ int duplicate_sparsefile(nfs41_open_state *src_state,
- if (status) {
- DPRINTF(0/*DDLVL*/,
- ("duplicate_sparsefile("
- + "opcode='%s',"
- "src_state->path.path='%s' "
- "dst_state->path.path='%s'): "
- "DEALLOCATE failed with '%s'\n",
- + opcode2string(opcode),
- src_state->path.path,
- dst_state->path.path,
- nfs_error_string(status)));
- @@ -594,46 +595,60 @@ int duplicate_sparsefile(nfs41_open_state *src_state,
- (int)data_seek_sr_eof,
- (int)hole_seek_sr_eof));
- -#ifdef DUP_DATA_USE_NFSCOPY
- - nfs41_write_verf verf;
- - status = nfs42_copy(session,
- - src_file,
- - dst_file,
- - &src_stateid,
- - &dst_stateid,
- - data_seek_sr_offset,
- - destfileoffset + (data_seek_sr_offset-srcfileoffset),
- - data_size,
- - &verf,
- - info);
- - /* FIXME: What should we do with |verf| ? Should we COMMIT this ? */
- -#else
- - status = nfs42_clone(session,
- - src_file,
- - dst_file,
- - &src_stateid,
- - &dst_stateid,
- - data_seek_sr_offset,
- - destfileoffset + (data_seek_sr_offset-srcfileoffset),
- - data_size,
- - info);
- -#endif /* DUP_DATA_USE_NFSCOPY */
- - if (status) {
- - const char dup_op_name[] =
- -#ifdef DUP_DATA_USE_NFSCOPY
- - "COPY";
- -#else
- - "CLONE";
- -#endif /* DUP_DATA_USE_NFSCOPY */
- + if (opcode == NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY) {
- + uint64_t bytes_written;
- + uint64_t bytestowrite = data_size;
- + uint64_t writeoffset = 0ULL;
- +
- + do
- + {
- + nfs41_write_verf verf;
- + bytes_written = 0ULL;
- +
- + status = nfs42_copy(session,
- + src_file,
- + dst_file,
- + &src_stateid,
- + &dst_stateid,
- + (data_seek_sr_offset + writeoffset),
- + (destfileoffset + (data_seek_sr_offset-srcfileoffset) + writeoffset),
- + bytestowrite,
- + &bytes_written,
- + &verf,
- + info);
- + if (status)
- + break;
- +
- + bytestowrite -= bytes_written;
- + writeoffset += bytes_written;
- + /* FIXME: What should we do with |verf| ? Should we COMMIT this ? */
- + } while (bytestowrite > 0ULL);
- + }
- + else if (opcode == NFS41_SYSOP_FSCTL_DUPLICATE_DATA) {
- + status = nfs42_clone(session,
- + src_file,
- + dst_file,
- + &src_stateid,
- + &dst_stateid,
- + data_seek_sr_offset,
- + destfileoffset + (data_seek_sr_offset-srcfileoffset),
- + data_size,
- + info);
- + }
- + else {
- + EASSERT(0);
- + }
- + if (status) {
- DPRINTF(0/*DDLVL*/,
- ("duplicate_sparsefile("
- + "opcode='%s',"
- "src_state->path.path='%s' "
- "dst_state->path.path='%s'): "
- - "'%s' failed with '%s'\n",
- + "failed with '%s'\n",
- + opcode2string(opcode),
- src_state->path.path,
- dst_state->path.path,
- - dup_op_name,
- nfs_error_string(status)));
- status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
- goto out;
- @@ -688,25 +703,35 @@ int handle_duplicatedata(void *daemon_context,
- dst_state->path.path,
- src_state->path.path));
- - /* NFS SEEK supported ? */
- - if (src_session->client->root->supports_nfs42_seek == false) {
- - status = ERROR_NOT_SUPPORTED;
- - goto out;
- + /*
- + * Check whether we support the required NFS operations...
- + */
- + if (upcall->opcode == NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY) {
- + if ((src_session->client->root->supports_nfs42_seek == false) ||
- + (src_session->client->root->supports_nfs42_copy == false) ||
- + (src_session->client->root->supports_nfs42_deallocate == false)) {
- + status = ERROR_NOT_SUPPORTED;
- + goto out;
- + }
- }
- - /* NFS CLONE supported ? */
- - if (
- -#ifdef DUP_DATA_USE_NFSCOPY
- - src_session->client->root->supports_nfs42_copy == false
- -#else
- - src_session->client->root->supports_nfs42_clone == false
- -#endif /* DUP_DATA_USE_NFSCOPY */
- - ) {
- - status = ERROR_NOT_SUPPORTED;
- - goto out;
- + else if (upcall->opcode == NFS41_SYSOP_FSCTL_DUPLICATE_DATA) {
- + if ((src_session->client->root->supports_nfs42_seek == false) ||
- + (src_session->client->root->supports_nfs42_clone == false) ||
- + (src_session->client->root->supports_nfs42_deallocate == false)) {
- + status = ERROR_NOT_SUPPORTED;
- + goto out;
- + }
- }
- - /* NFS DEALLOCATE supported ? */
- - if (src_session->client->root->supports_nfs42_deallocate == false) {
- - status = ERROR_NOT_SUPPORTED;
- + else {
- + status = ERROR_INVALID_PARAMETER;
- + eprintf("duplicate_sparsefile("
- + "opcode='%s',"
- + "src_state->path.path='%s' "
- + "dst_state->path.path='%s'): "
- + "Unknown opcode.\n",
- + opcode2string(upcall->opcode),
- + src_state->path.path,
- + dst_state->path.path);
- goto out;
- }
- @@ -837,7 +862,8 @@ int handle_duplicatedata(void *daemon_context,
- (void)memset(&info, 0, sizeof(info));
- - status = duplicate_sparsefile(src_state,
- + status = duplicate_sparsefile(upcall->opcode,
- + src_state,
- dst_state,
- args->srcfileoffset,
- args->destfileoffset,
- @@ -879,3 +905,10 @@ const nfs41_upcall_op nfs41_op_duplicatedata = {
- .marshall = marshall_duplicatedata,
- .arg_size = sizeof(duplicatedata_upcall_args)
- };
- +
- +const nfs41_upcall_op nfs41_op_offload_datacopy = {
- + .parse = parse_duplicatedata,
- + .handle = handle_duplicatedata,
- + .marshall = marshall_duplicatedata,
- + .arg_size = sizeof(duplicatedata_upcall_args)
- +};
- diff --git a/daemon/nfs41_ops.h b/daemon/nfs41_ops.h
- index dd9dcb4..bd93fbf 100644
- --- a/daemon/nfs41_ops.h
- +++ b/daemon/nfs41_ops.h
- @@ -1299,6 +1299,7 @@ int nfs42_copy(
- IN uint64_t src_offset,
- IN uint64_t dst_offset,
- IN uint64_t length,
- + OUT uint64_t *bytes_written,
- OUT nfs41_write_verf *writeverf,
- OUT nfs41_file_info *cinfo);
- diff --git a/daemon/nfs42_ops.c b/daemon/nfs42_ops.c
- index 582a968..3494a3b 100644
- --- a/daemon/nfs42_ops.c
- +++ b/daemon/nfs42_ops.c
- @@ -122,6 +122,7 @@ int nfs42_copy(
- IN uint64_t src_offset,
- IN uint64_t dst_offset,
- IN uint64_t length,
- + OUT uint64_t *bytes_written,
- OUT nfs41_write_verf *writeverf,
- OUT nfs41_file_info *cinfo)
- {
- @@ -205,13 +206,7 @@ int nfs42_copy(
- nfs41_superblock_space_changed(dst_file->fh.superblock);
- - if (copy_res.u.resok4.response.count != length) {
- - DPRINTF(0,
- - ("nfs42_copy: "
- - "copy_res.u.resok4.response.count(=%lld) < length(=%lld)\n",
- - (long long)copy_res.u.resok4.response.count, (long long)length));
- - status = ERROR_NET_WRITE_FAULT;
- - }
- + *bytes_written = copy_res.u.resok4.response.count;
- out:
- return status;
- diff --git a/daemon/upcall.c b/daemon/upcall.c
- index 45e0729..53433c4 100644
- --- a/daemon/upcall.c
- +++ b/daemon/upcall.c
- @@ -52,6 +52,7 @@ extern const nfs41_upcall_op nfs41_op_setacl;
- extern const nfs41_upcall_op nfs41_op_queryallocatedranges;
- extern const nfs41_upcall_op nfs41_op_setzerodata;
- extern const nfs41_upcall_op nfs41_op_duplicatedata;
- +extern const nfs41_upcall_op nfs41_op_offload_datacopy;
- /* |_nfs41_opcodes| and |g_upcall_op_table| must be in sync! */
- static const nfs41_upcall_op *g_upcall_op_table[] = {
- @@ -78,6 +79,7 @@ static const nfs41_upcall_op *g_upcall_op_table[] = {
- &nfs41_op_queryallocatedranges,
- &nfs41_op_setzerodata,
- &nfs41_op_duplicatedata,
- + &nfs41_op_offload_datacopy,
- NULL,
- NULL
- };
- diff --git a/include/nfs41_driver.h b/include/nfs41_driver.h
- index bf956f4..7a4ddfc 100644
- --- a/include/nfs41_driver.h
- +++ b/include/nfs41_driver.h
- @@ -88,6 +88,7 @@ typedef enum _nfs41_opcodes {
- NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES,
- NFS41_SYSOP_FSCTL_SET_ZERO_DATA,
- NFS41_SYSOP_FSCTL_DUPLICATE_DATA,
- + NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY,
- NFS41_SYSOP_SHUTDOWN,
- NFS41_SYSOP_INVALID_OPCODE1
- } nfs41_opcodes;
- diff --git a/sys/nfs41sys_debug.c b/sys/nfs41sys_debug.c
- index ee95c9a..ffebee3 100644
- --- a/sys/nfs41sys_debug.c
- +++ b/sys/nfs41sys_debug.c
- @@ -683,6 +683,8 @@ const char *opcode2string(int opcode)
- case NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES: return "NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES";
- case NFS41_SYSOP_FSCTL_SET_ZERO_DATA: return "NFS41_SYSOP_FSCTL_SET_ZERO_DATA";
- case NFS41_SYSOP_FSCTL_DUPLICATE_DATA: return "NFS41_SYSOP_FSCTL_DUPLICATE_DATA";
- + case NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY:
- + return "NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY";
- default: return "UNKNOWN";
- }
- }
- diff --git a/sys/nfs41sys_driver.c b/sys/nfs41sys_driver.c
- index b079180..fd72305 100644
- --- a/sys/nfs41sys_driver.c
- +++ b/sys/nfs41sys_driver.c
- @@ -131,6 +131,8 @@ PRDBSS_DEVICE_OBJECT nfs41_dev;
- KEVENT upcallEvent;
- FAST_MUTEX upcallLock, downcallLock, fcblistLock;
- FAST_MUTEX openOwnerLock;
- +FAST_MUTEX offloadcontextLock;
- +nfs41_offloadcontext_list offloadcontext_list;
- LONGLONG xid = 0;
- LONG open_owner_id = 1;
- @@ -772,6 +774,7 @@ NTSTATUS nfs41_DeallocateForFobx(
- __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(pFobx);
- nfs41_invalidate_fobx_entry(pFobx);
- + nfs41_remove_offloadcontext_for_fobx(pFobx);
- if (nfs41_fobx->acl) {
- RxFreePool(nfs41_fobx->acl);
- @@ -1396,9 +1399,11 @@ NTSTATUS DriverEntry(
- ExInitializeFastMutex(&downcallLock);
- ExInitializeFastMutex(&openOwnerLock);
- ExInitializeFastMutex(&fcblistLock);
- + ExInitializeFastMutex(&offloadcontextLock);
- InitializeListHead(&upcall.head);
- InitializeListHead(&downcall.head);
- InitializeListHead(&openlist.head);
- + InitializeListHead(&offloadcontext_list.head);
- #ifdef USE_LOOKASIDELISTS_FOR_UPDOWNCALLENTRY_MEM
- /*
- * The |Depth| parameter is unfortunately ignored in Win10,
- diff --git a/sys/nfs41sys_driver.h b/sys/nfs41sys_driver.h
- index 597d207..3f8cc68 100644
- --- a/sys/nfs41sys_driver.h
- +++ b/sys/nfs41sys_driver.h
- @@ -507,6 +507,12 @@ typedef struct _nfs41_fcb_list {
- } nfs41_fcb_list;
- nfs41_fcb_list openlist;
- +typedef struct _nfs41_offloadcontext_list {
- + LIST_ENTRY head;
- +} nfs41_offloadcontext_list;
- +extern nfs41_offloadcontext_list offloadcontext_list;
- +extern FAST_MUTEX offloadcontextLock;
- +
- typedef enum _NULMRX_STORAGE_TYPE_CODES {
- NTC_NFS41_DEVICE_EXTENSION = (NODE_TYPE_CODE)0xFC00,
- } NFS41_STORAGE_TYPE_CODES;
- @@ -685,6 +691,8 @@ NTSTATUS marshal_nfs41_duplicatedata(
- NTSTATUS unmarshal_nfs41_duplicatedata(
- nfs41_updowncall_entry *cur,
- unsigned char **buf);
- +void nfs41_remove_offloadcontext_for_fobx(
- + IN PMRX_FOBX pFobx);
- /* nfs41sys_ioctl.c */
- NTSTATUS nfs41_IoCtl(
- diff --git a/sys/nfs41sys_fsctl.c b/sys/nfs41sys_fsctl.c
- index c1a3f2b..5e2eb9f 100644
- --- a/sys/nfs41sys_fsctl.c
- +++ b/sys/nfs41sys_fsctl.c
- @@ -56,7 +56,7 @@
- #include <rx.h>
- #include <windef.h>
- #include <winerror.h>
- -
- +#include <ntddstor.h>
- #include <Ntstrsafe.h>
- #include "nfs41sys_buildconfig.h"
- @@ -863,6 +863,434 @@ NTSTATUS unmarshal_nfs41_duplicatedata(
- return status;
- }
- +/*
- + * |offloadcontext_entry| - context to store |FSCTL_OFFLOAD_READ| token
- + * information
- + *
- + * * Notes:
- + * - These are stored in a global list, as |FSCTL_OFFLOAD_READ|+
- + * |FSCTL_OFFLOAD_WRITE| is intended to work for intra-server and
- + * inter-server copies, so |FSCTL_OFFLOAD_READ| might be done on one
- + * filesystem but |FSCTL_OFFLOAD_WRITE| might be done on a different
- + * one
- + *
- + * * FIXME:
- + * - Is it legal if one user passes a token to another user, or
- + * should this be prevented ?
- + */
- +typedef struct _offloadcontext_entry
- +{
- + LIST_ENTRY next;
- + /*
- + * r/w lock - shared access for |FSCTL_OFFLOAD_WRITE|, so one token can
- + * be used for multiple parallel writes, exclusive access for file delete
- + * (i.e. wait until all shared access before deleting the context)
- + */
- + ERESOURCE resource;
- + STORAGE_OFFLOAD_TOKEN token;
- + PNFS41_FOBX src_fobx;
- + ULONGLONG src_fileoffset;
- + ULONGLONG src_length;
- +} offloadcontext_entry;
- +
- +
- +void nfs41_remove_offloadcontext_for_fobx(
- + IN PMRX_FOBX pFobx)
- +{
- + PLIST_ENTRY pEntry;
- + offloadcontext_entry *cur, *found = NULL;
- + __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(pFobx);
- +
- + ExAcquireFastMutexUnsafe(&offloadcontextLock);
- +
- + pEntry = offloadcontext_list.head.Flink;
- + while (!IsListEmpty(&offloadcontext_list.head)) {
- + cur = (offloadcontext_entry *)CONTAINING_RECORD(pEntry,
- + offloadcontext_entry, next);
- + if (cur->src_fobx == nfs41_fobx) {
- + found = cur;
- + break;
- + }
- + if (pEntry->Flink == &offloadcontext_list.head) {
- + break;
- + }
- + pEntry = pEntry->Flink;
- + }
- +
- + if (found) {
- + DbgP("nfs41_remove_offloadcontext(pFobx=0x%p): "
- + "removing found=0x%p\n",
- + pFobx,
- + found);
- +
- + /* Wait for any shared access in |nfs41_OffloadWrite()| to finish */
- + (void)ExAcquireResourceExclusiveLite(&found->resource, TRUE);
- + ExReleaseResourceLite(&found->resource);
- +
- + RemoveEntryList(&found->next);
- +
- + (void)ExDeleteResourceLite(&found->resource);
- + RxFreePool(found);
- + }
- + else {
- + DbgP("nfs41_remove_offloadcontext(pFobx=0x%p): Nothing found.\n",
- + pFobx);
- + }
- +
- + ExReleaseFastMutexUnsafe(&offloadcontextLock);
- +}
- +
- +static
- +offloadcontext_entry *nfs41_find_offloadcontext_acquireshared(
- + IN offloadcontext_entry *unvalidated_oce)
- +{
- + PLIST_ENTRY pEntry;
- + offloadcontext_entry *cur, *found = NULL;
- +
- + ExAcquireFastMutexUnsafe(&offloadcontextLock);
- +
- + pEntry = offloadcontext_list.head.Flink;
- + while (!IsListEmpty(&offloadcontext_list.head)) {
- + cur = (offloadcontext_entry *)CONTAINING_RECORD(pEntry,
- + offloadcontext_entry, next);
- + if (cur == unvalidated_oce) {
- + found = cur;
- + break;
- + }
- + if (pEntry->Flink == &offloadcontext_list.head) {
- + break;
- + }
- + pEntry = pEntry->Flink;
- + }
- +
- + if (found) {
- + DbgP("nfs41_find_offloadcontext_acquireshared(unvalidated_oce=0x%p): "
- + "found=0x%p\n",
- + unvalidated_oce);
- +
- + (void)ExAcquireSharedStarveExclusive(&found->resource, TRUE);
- + ExReleaseFastMutexUnsafe(&offloadcontextLock);
- + return found;
- + }
- + else {
- + DbgP("nfs41_find_offloadcontext_acquireshared(unvalidated_oce=0x%p): "
- + "Nothing found.\n",
- + unvalidated_oce);
- + ExReleaseFastMutexUnsafe(&offloadcontextLock);
- + return NULL;
- + }
- +}
- +
- +static
- +NTSTATUS nfs41_OffloadRead(
- + IN OUT PRX_CONTEXT RxContext)
- +{
- + NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
- + __notnull XXCTL_LOWIO_COMPONENT *FsCtl =
- + &RxContext->LowIoContext.ParamsFor.FsCtl;
- + __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
- +
- + DbgEn();
- +
- + RxContext->IoStatusBlock.Information = 0;
- +
- + if (FsCtl->pInputBuffer == NULL) {
- + status = STATUS_INVALID_USER_BUFFER;
- + goto out;
- + }
- +
- + if (FsCtl->pOutputBuffer == NULL) {
- + status = STATUS_INVALID_USER_BUFFER;
- + goto out;
- + }
- +
- + if (FsCtl->InputBufferLength < sizeof(FSCTL_OFFLOAD_READ_INPUT)) {
- + DbgP("nfs41_OffloadRead: "
- + "buffer too small for FSCTL_OFFLOAD_READ_INPUT\n");
- + status = STATUS_BUFFER_TOO_SMALL;
- + goto out;
- + }
- + if (FsCtl->OutputBufferLength < sizeof(FSCTL_OFFLOAD_READ_OUTPUT)) {
- + DbgP("nfs41_OffloadRead: "
- + "buffer too small for FSCTL_OFFLOAD_READ_OUTPUT\n");
- + status = STATUS_BUFFER_TOO_SMALL;
- + goto out;
- + }
- +
- + PFSCTL_OFFLOAD_READ_INPUT ori =
- + (PFSCTL_OFFLOAD_READ_INPUT)FsCtl->pInputBuffer;
- + PFSCTL_OFFLOAD_READ_OUTPUT oro =
- + (PFSCTL_OFFLOAD_READ_OUTPUT)FsCtl->pOutputBuffer;
- +
- + DbgP("nfs41_OffloadRead: "
- + "ori->(Size=%lu, Flags=0x%lx, TokenTimeToLive=%lu, Reserved=%lu, "
- + "FileOffset=%llu, CopyLength=%llu)\n",
- + (unsigned long)ori->Size,
- + (unsigned long)ori->Flags,
- + (unsigned long)ori->TokenTimeToLive,
- + (unsigned long)ori->Reserved,
- + (unsigned long long)ori->FileOffset,
- + (unsigned long long)ori->CopyLength);
- +
- + offloadcontext_entry *oce = RxAllocatePoolWithTag(NonPagedPoolNx,
- + sizeof(offloadcontext_entry), NFS41_MM_POOLTAG);
- + if (oce == NULL) {
- + status = STATUS_INSUFFICIENT_RESOURCES;
- + goto out;
- + }
- +
- + DbgP("nfs41_OffloadRead: oce=0x%p\n", oce);
- +
- + (void)ExInitializeResourceLite(&oce->resource);
- +
- + (void)memset(&oce->token, 0, sizeof(oce->token));
- + /* Add safeguard to |TokenType| */
- + oce->token.TokenType[0] = 'N';
- + oce->token.TokenType[1] = 'F';
- + oce->token.TokenType[2] = 'S';
- + oce->token.TokenType[3] = '4';
- + /* FIXME: What about the endianness of |TokenIdLength| ? */
- + *((USHORT *)(&oce->token.TokenIdLength[0])) =
- + STORAGE_OFFLOAD_TOKEN_ID_LENGTH;
- + *((void **)(&oce->token.Token[0])) = oce;
- + oce->src_fobx = nfs41_fobx;
- + oce->src_fileoffset = ori->FileOffset;
- + oce->src_length = ori->CopyLength;
- +
- + oro->Size = sizeof(FSCTL_OFFLOAD_READ_OUTPUT);
- + oro->Flags = 0;
- + oro->TransferLength = ori->CopyLength;
- + (void)memcpy(&oro->Token[0], &oce->token, sizeof(oce->token));
- +
- + nfs41_AddEntry(offloadcontextLock, offloadcontext_list, oce);
- +
- + RxContext->CurrentIrp->IoStatus.Status = status = STATUS_SUCCESS;
- + RxContext->InformationToReturn = sizeof(FSCTL_OFFLOAD_READ_OUTPUT);
- +
- +out:
- + DbgEx();
- + return status;
- +}
- +
- +static
- +NTSTATUS check_nfs41_offload_write_args(
- + PRX_CONTEXT RxContext)
- +{
- + NTSTATUS status = STATUS_SUCCESS;
- + __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
- + __notnull PNFS41_V_NET_ROOT_EXTENSION VNetRootContext =
- + NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
- +
- + /* access checks */
- + if (VNetRootContext->read_only) {
- + status = STATUS_MEDIA_WRITE_PROTECTED;
- + goto out;
- + }
- + if (!(SrvOpen->DesiredAccess &
- + (FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES))) {
- + status = STATUS_ACCESS_DENIED;
- + goto out;
- + }
- +
- +out:
- + return status;
- +}
- +
- +static
- +NTSTATUS nfs41_OffloadWrite(
- + IN OUT PRX_CONTEXT RxContext)
- +{
- + NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
- + nfs41_updowncall_entry *entry = NULL;
- + __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
- + __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
- + NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
- + __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
- + NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
- + __notnull XXCTL_LOWIO_COMPONENT *FsCtl =
- + &RxContext->LowIoContext.ParamsFor.FsCtl;
- + __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
- + offloadcontext_entry *src_oce = NULL;
- +
- + struct {
- + LONGLONG srcfileoffset;
- + LONGLONG destfileoffset;
- + LONGLONG bytecount;
- + } dd;
- +
- + DbgEn();
- +
- + LONGLONG io_delay;
- + RxContext->IoStatusBlock.Information = 0;
- +
- + status = check_nfs41_offload_write_args(RxContext);
- + if (status)
- + goto out;
- +
- + if (FsCtl->pInputBuffer == NULL) {
- + status = STATUS_INVALID_USER_BUFFER;
- + goto out;
- + }
- + if (FsCtl->pOutputBuffer == NULL) {
- + status = STATUS_INVALID_USER_BUFFER;
- + goto out;
- + }
- + if (FsCtl->InputBufferLength < sizeof(FSCTL_OFFLOAD_WRITE_INPUT)) {
- + DbgP("nfs41_OffloadWrite: "
- + "buffer too small for FSCTL_OFFLOAD_WRITE_INPUT\n");
- + status = STATUS_BUFFER_TOO_SMALL;
- + goto out;
- + }
- + if (FsCtl->OutputBufferLength < sizeof(FSCTL_OFFLOAD_WRITE_OUTPUT)) {
- + DbgP("nfs41_OffloadWrite: "
- + "buffer too small for FSCTL_OFFLOAD_WRITE_OUTPUT\n");
- + status = STATUS_BUFFER_TOO_SMALL;
- + goto out;
- + }
- +
- + PFSCTL_OFFLOAD_WRITE_INPUT owi =
- + (PFSCTL_OFFLOAD_WRITE_INPUT)FsCtl->pInputBuffer;
- + PFSCTL_OFFLOAD_WRITE_OUTPUT owo =
- + (PFSCTL_OFFLOAD_WRITE_OUTPUT)FsCtl->pOutputBuffer;
- +
- + offloadcontext_entry *unvalidated_src_oce;
- +
- + /*
- + * Peel |offloadcontext_entry| pointer from token...
- + */
- + unvalidated_src_oce =
- + *((void **)(&(((STORAGE_OFFLOAD_TOKEN *)(&owi->Token[0]))->Token[0])));
- + DbgP("nfs41_OffloadWrite: "
- + "unvalidated_src_oce=0x%p\n", unvalidated_src_oce);
- +
- + /*
- + * ... and validate it (and take a shared lock if validation was
- + * successful, so nobody can delete the context while we use it)!
- + */
- + src_oce = nfs41_find_offloadcontext_acquireshared(unvalidated_src_oce);
- + if (src_oce == NULL) {
- + DbgP("nfs41_OffloadWrite: "
- + "nfs41_find_offloadcontext_acquireshared() failed\n");
- + status = STATUS_INVALID_PARAMETER;
- + goto out;
- + }
- +
- + DbgP("nfs41_OffloadWrite: src_oce=0x%p\n", src_oce);
- +
- + /* Check safeguard... */
- + if ((src_oce->token.TokenType[0] != 'N') ||
- + (src_oce->token.TokenType[1] != 'F') ||
- + (src_oce->token.TokenType[2] != 'S') ||
- + (src_oce->token.TokenType[3] != '4')) {
- + DbgP("nfs41_OffloadWrite: "
- + "token in src_oce=0x%p not a 'NFS4' token\n",
- + src_oce);
- + status = STATUS_INVALID_PARAMETER;
- + goto out;
- + }
- +
- + /*
- + * FIXME: We should validate the length passed as
- + * |FSCTL_OFFLOAD_READ_INPUT.CopyLength| here better, because it is
- + * also used as some kind of access control to different parts of a
- + * file
- + */
- + dd.srcfileoffset = src_oce->src_fileoffset + owi->TransferOffset;
- + dd.destfileoffset = owi->FileOffset;
- + dd.bytecount = owi->CopyLength;
- +
- + DbgP("nfs41_OffloadWrite: "
- + "dd=(srcfileoffset=%lld,"
- + "destfileoffset=%lld,"
- + "bytecount=%lld)\n",
- + (long long)dd.srcfileoffset,
- + (long long)dd.destfileoffset,
- + (long long)dd.bytecount);
- +
- + if (dd.bytecount == 0LL) {
- + status = STATUS_SUCCESS;
- + goto out;
- + }
- +
- + PNFS41_FOBX nfs41_src_fobx = src_oce->src_fobx;
- + if (!nfs41_src_fobx) {
- + DbgP("nfs41_OffloadWrite: No nfs41_src_fobx\n");
- + status = STATUS_INVALID_PARAMETER;
- + goto out;
- + }
- +
- + /*
- + * Disable caching because NFSv4.2 COPY is basically a
- + * "write" operation. AFAIK we should flush the cache and wait
- + * for the kernel lazy writer (which |RxChangeBufferingState()|
- + * AFAIK does) before doing the COPY, to avoid that we
- + * have outstanding writes in the kernel cache at the same
- + * location where the COPY should do it's work
- + */
- + ULONG flag = DISABLE_CACHING;
- + DbgP("nfs41_OffloadWrite: disableing caching for file '%wZ'\n",
- + SrvOpen->pAlreadyPrefixedName);
- + RxChangeBufferingState((PSRV_OPEN)SrvOpen, ULongToPtr(flag), 1);
- +
- + status = nfs41_UpcallCreate(NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY,
- + &nfs41_fobx->sec_ctx,
- + pVNetRootContext->session,
- + nfs41_fobx->nfs41_open_state,
- + pNetRootContext->nfs41d_version,
- + SrvOpen->pAlreadyPrefixedName,
- + &entry);
- +
- + if (status)
- + goto out;
- +
- + entry->u.DuplicateData.src_state = nfs41_src_fobx->nfs41_open_state;
- + entry->u.DuplicateData.srcfileoffset = dd.srcfileoffset;
- + entry->u.DuplicateData.destfileoffset = dd.destfileoffset;
- + entry->u.DuplicateData.bytecount = dd.bytecount;
- +
- + /* Add extra timeout depending on file size */
- + io_delay = pVNetRootContext->timeout +
- + EXTRA_TIMEOUT_PER_BYTE(entry->u.DuplicateData.bytecount);
- +
- + status = nfs41_UpcallWaitForReply(entry, io_delay);
- + if (status) {
- + /* Timeout - |nfs41_downcall()| will free |entry|+contents */
- + entry = NULL;
- + goto out;
- + }
- +
- + if (!entry->status) {
- + DbgP("nfs41_OffloadWrite: SUCCESS\n");
- +
- + owo->Size = sizeof(FSCTL_OFFLOAD_READ_OUTPUT);
- + owo->Flags = 0;
- + owo->LengthWritten = dd.bytecount;
- +
- + RxContext->CurrentIrp->IoStatus.Status = status = STATUS_SUCCESS;
- + RxContext->InformationToReturn = sizeof(FSCTL_OFFLOAD_READ_OUTPUT);
- + }
- + else {
- + DbgP("nfs41_OffloadWrite: "
- + "FAILURE, entry->status=0x%lx\n", entry->status);
- + status = map_setfile_error(entry->status);
- + RxContext->CurrentIrp->IoStatus.Status = status;
- + RxContext->IoStatusBlock.Information = 0;
- + }
- +
- +out:
- + if (src_oce) {
- + /* Release resource we obtained in shared mode */
- + ExReleaseResourceLite(&src_oce->resource);
- + }
- +
- + if (entry) {
- + nfs41_UpcallDestroy(entry);
- + }
- +
- + DbgEx();
- + return status;
- +}
- +
- NTSTATUS nfs41_FsCtl(
- IN OUT PRX_CONTEXT RxContext)
- {
- @@ -895,6 +1323,12 @@ NTSTATUS nfs41_FsCtl(
- case FSCTL_DUPLICATE_EXTENTS_TO_FILE:
- status = nfs41_DuplicateData(RxContext);
- break;
- + case FSCTL_OFFLOAD_READ:
- + status = nfs41_OffloadRead(RxContext);
- + break;
- + case FSCTL_OFFLOAD_WRITE:
- + status = nfs41_OffloadWrite(RxContext);
- + break;
- default:
- break;
- }
- diff --git a/sys/nfs41sys_updowncall.c b/sys/nfs41sys_updowncall.c
- index cf5be35..fc523a4 100644
- --- a/sys/nfs41sys_updowncall.c
- +++ b/sys/nfs41sys_updowncall.c
- @@ -321,6 +321,7 @@ NTSTATUS handle_upcall(
- pbOut, cbOut, len);
- break;
- case NFS41_SYSOP_FSCTL_DUPLICATE_DATA:
- + case NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY:
- status = marshal_nfs41_duplicatedata(entry,
- pbOut, cbOut, len);
- break;
- @@ -785,6 +786,7 @@ NTSTATUS nfs41_downcall(
- unmarshal_nfs41_setzerodata(cur, &buf);
- break;
- case NFS41_SYSOP_FSCTL_DUPLICATE_DATA:
- + case NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY:
- unmarshal_nfs41_duplicatedata(cur, &buf);
- break;
- }
Win32 offload copy prototype, 2025-08-19
Posted by Anonymous on Wed 20th Aug 2025 08:58
raw | new post
modification of post by Anonymous (view diff)
Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.