- From da8ec06b4731a97db63b5ba4d4983a3ef9797365 Mon Sep 17 00:00:00 2001
- From: Roland Mainz <roland.mainz@nrubsig.org>
- Date: Mon, 14 Apr 2025 17:07:42 +0200
- Subject: [PATCH 1/4] tests: Directory for TMPDIR/TMP/TEMP should be created
- with chmod a+rwxt
- Directory for TMPDIR/TMP/TEMP should be created with chmod a+rwxt
- Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
- ---
- tests/manual_testing.txt | 2 +-
- tests/nfsbuildtest/nfsbuildtest.ksh93 | 3 +++
- 2 files changed, 4 insertions(+), 1 deletion(-)
- diff --git a/tests/manual_testing.txt b/tests/manual_testing.txt
- index ed124ff..4da80fe 100644
- --- a/tests/manual_testing.txt
- +++ b/tests/manual_testing.txt
- @@ -461,7 +461,7 @@ $ bash -c 'cd -@ "x1" && ls -la'
- #
- One-liner, should print two times lines with "test OK", stderr should be empty
- ---- snip ----
- -$ time ksh93 -c 'rm -Rf nfstmp1 gcc.x.strace.log x.c x.exe && mkdir nfstmp1 ; export TMPDIR="$PWD/nfstmp1" TEMP="$PWD/nfstmp1" TMP="$PWD/nfstmp1"; rm -f x.c x.exe && printf "#include <stdio.h>\n#include <stdlib.h>\nint main(int ac, char *av[]) { puts(\"hello world\"); return EXIT_SUCCESS;}\n" >x.c && strace -b $((512*1024)) -m syscall -o gcc.x.strace.log gcc x.c -o x.exe && ./x.exe && echo "# Compile Test OK" ; n="$(grep -E "/nfstmp1/.+\.o" gcc.x.strace.log | wc -l)" ; if (( n > 0 )) ; then echo "# Temp usage test OK: used by *.o files" ; else echo "# Test *.o FAILED" ; fi'
- +$ time ksh93 -c 'rm -Rf nfstmp1 gcc.x.strace.log x.c x.exe && mkdir nfstmp1 && chmod a+rwxt nfstmp1 ; export TMPDIR="$PWD/nfstmp1" TEMP="$PWD/nfstmp1" TMP="$PWD/nfstmp1"; rm -f x.c x.exe && printf "#include <stdio.h>\n#include <stdlib.h>\nint main(int ac, char *av[]) { puts(\"hello world\"); return EXIT_SUCCESS;}\n" >x.c && strace -b $((512*1024)) -m syscall -o gcc.x.strace.log gcc x.c -o x.exe && ./x.exe && echo "# Compile Test OK" ; n="$(grep -E "/nfstmp1/.+\.o" gcc.x.strace.log | wc -l)" ; if (( n > 0 )) ; then echo "# Temp usage test OK: used by *.o files" ; else echo "# Test *.o FAILED" ; fi'
- ---- snip ---
- #
- diff --git a/tests/nfsbuildtest/nfsbuildtest.ksh93 b/tests/nfsbuildtest/nfsbuildtest.ksh93
- index a12967d..ac5726e 100644
- --- a/tests/nfsbuildtest/nfsbuildtest.ksh93
- +++ b/tests/nfsbuildtest/nfsbuildtest.ksh93
- @@ -79,6 +79,7 @@ function gcc_build
- tmpdir="$PWD/tmpdir"
- mkdir -p "$tmpdir"
- + chmod a+rwxt "$tmpdir"
- if [[ -d "$tmpdir" && -w "$tmpdir" ]] ; then
- printf '#### Using temp dir %q\n' "$tmpdir"
- export TMPDIR="$tmpdir"
- @@ -255,6 +256,7 @@ function bash_build
- tmpdir="$PWD/tmpdir"
- mkdir -p "$tmpdir"
- + chmod a+rwxt "$tmpdir"
- if [[ -d "$tmpdir" && -w "$tmpdir" ]] ; then
- printf '#### Using temp dir %q\n' "$tmpdir"
- export TMPDIR="$tmpdir"
- @@ -423,6 +425,7 @@ function msnfs41client_build
- tmpdir="$PWD/tmpdir"
- mkdir -p "$tmpdir"
- + chmod a+rwxt "$tmpdir"
- if [[ -d "$tmpdir" && -w "$tmpdir" ]] ; then
- printf '#### Using temp dir %q\n' "$tmpdir"
- export TMPDIR="$tmpdir"
- --
- 2.45.1
- From 61fc10a93aef4d556b0eebad4b24c17bec1d2676 Mon Sep 17 00:00:00 2001
- From: Roland Mainz <roland.mainz@nrubsig.org>
- Date: Mon, 14 Apr 2025 18:02:39 +0200
- Subject: [PATCH 2/4] daemon: |get_superblock_attrs()| debug output should log
- |sparse_file_support|
- |get_superblock_attrs()| debug output should log |sparse_file_support|
- Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
- ---
- daemon/nfs41_superblock.c | 5 +++--
- 1 file changed, 3 insertions(+), 2 deletions(-)
- diff --git a/daemon/nfs41_superblock.c b/daemon/nfs41_superblock.c
- index db75c09..880b115 100644
- --- a/daemon/nfs41_superblock.c
- +++ b/daemon/nfs41_superblock.c
- @@ -154,14 +154,15 @@ static int get_superblock_attrs(
- "maxread=%llu, maxwrite=%llu, layout_types: 0x%X, "
- "cansettime=%u, time_delta={%llu,%u}, aclsupport=%u, "
- "link_support=%u, symlink_support=%u, case_preserving=%u, "
- - "case_insensitive=%u\n",
- + "case_insensitive=%u sparse_file_support=%u\n",
- superblock->fsid.major, superblock->fsid.minor,
- superblock->maxread, superblock->maxwrite,
- superblock->layout_types, superblock->cansettime,
- superblock->time_delta.seconds, superblock->time_delta.nseconds,
- superblock->aclsupport, superblock->link_support,
- superblock->symlink_support, superblock->case_preserving,
- - superblock->case_insensitive));
- + superblock->case_insensitive,
- + superblock->sparse_file_support));
- out:
- return status;
- }
- --
- 2.45.1
- From 6db298479fb8b151926d9cd7d8a425fb7ee705ec Mon Sep 17 00:00:00 2001
- From: Roland Mainz <roland.mainz@nrubsig.org>
- Date: Tue, 15 Apr 2025 15:38:19 +0200
- Subject: [PATCH 3/4] daemon,include,sys: Add upcall/downcall for
- |NFS41_SYSOP_FSCTL_DUPLICATE_DATA|
- Add upcall/downcall for |NFS41_SYSOP_FSCTL_DUPLICATE_DATA|
- Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
- ---
- daemon/daemon_debug.c | 1 +
- daemon/fsctl.c | 83 +++++++++++++
- daemon/nfs41.h | 1 +
- daemon/nfs41_superblock.c | 14 ++-
- daemon/upcall.c | 2 +
- daemon/upcall.h | 8 ++
- include/nfs41_driver.h | 1 +
- sys/nfs41sys_debug.c | 1 +
- sys/nfs41sys_driver.h | 14 +++
- sys/nfs41sys_fsctl.c | 248 ++++++++++++++++++++++++++++++++++++++
- sys/nfs41sys_updowncall.c | 7 ++
- 11 files changed, 378 insertions(+), 2 deletions(-)
- diff --git a/daemon/daemon_debug.c b/daemon/daemon_debug.c
- index 50856e9..10fd4b9 100644
- --- a/daemon/daemon_debug.c
- +++ b/daemon/daemon_debug.c
- @@ -468,6 +468,7 @@ const char* opcode2string(nfs41_opcodes opcode)
- NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_ACL_SET)
- 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_INVALID_OPCODE1)
- default: break;
- }
- diff --git a/daemon/fsctl.c b/daemon/fsctl.c
- index 87937cc..fa0c342 100644
- --- a/daemon/fsctl.c
- +++ b/daemon/fsctl.c
- @@ -29,6 +29,7 @@
- #define QARLVL 2 /* dprintf level for "query allocated ranges" logging */
- #define SZDLVL 2 /* dprintf level for "set zero data" logging */
- +#define DDLVL 0 /* dprintf level for "duplicate data" logging */
- static int parse_queryallocatedranges(unsigned char *buffer,
- uint32_t length, nfs41_upcall *upcall)
- @@ -429,3 +430,85 @@ const nfs41_upcall_op nfs41_op_setzerodata = {
- .marshall = marshall_setzerodata,
- .arg_size = sizeof(setzerodata_upcall_args)
- };
- +
- +static int parse_duplicatedata(unsigned char *buffer,
- + uint32_t length, nfs41_upcall *upcall)
- +{
- + int status;
- + duplicatedata_upcall_args *args = &upcall->args.duplicatedata;
- +
- + status = safe_read(&buffer, &length, &args->src_state,
- + sizeof(args->src_state));
- + if (status) goto out;
- + status = safe_read(&buffer, &length, &args->srcfileoffset,
- + sizeof(args->srcfileoffset));
- + if (status) goto out;
- + status = safe_read(&buffer, &length, &args->destfileoffset,
- + sizeof(args->destfileoffset));
- + if (status) goto out;
- + status = safe_read(&buffer, &length, &args->bytecount,
- + sizeof(args->bytecount));
- + if (status) goto out;
- +
- + DPRINTF(DDLVL, ("parse_duplicatedata: "
- + "parsing '%s' "
- + "duplicatedata=(src_state=0x%p srcfileoffset=%lld "
- + "destfileoffset=%lld bytecount=%lld)\n",
- + opcode2string(upcall->opcode),
- + (long long)args->src_state,
- + (long long)args->srcfileoffset,
- + (long long)args->destfileoffset,
- + (long long)args->bytecount));
- +out:
- + return status;
- +}
- +
- +static
- +int handle_duplicatedata(void *daemon_context,
- + nfs41_upcall *upcall)
- +{
- + int status = ERROR_INVALID_PARAMETER;
- + duplicatedata_upcall_args *args = &upcall->args.duplicatedata;
- + nfs41_open_state *state = upcall->state_ref;
- +// nfs41_session *session = state->session;
- +// nfs41_path_fh *file = &state->file;
- +
- + DPRINTF(DDLVL,
- + ("--> handle_duplicatedata("
- + "state->path.path='%s', "
- + "src_state->path.path='%s')\n",
- + state->path.path,
- + args->src_state->path.path));
- +
- + /* NFS CLONE requires NFSv4.2 */
- + if (state->session->client->root->nfsminorvers < 2) {
- + status = ERROR_NOT_SUPPORTED;
- + goto out;
- + }
- +
- + /* FIXME: Cloning not implemented yet */
- + status = ERROR_NOT_SUPPORTED;
- +
- +out:
- + DPRINTF(0,
- + ("<-- handle_duplicatedata(), status=0x%lx\n",
- + status));
- +
- + return status;
- +}
- +
- +static int marshall_duplicatedata(unsigned char *buffer,
- + uint32_t *length, nfs41_upcall *upcall)
- +{
- + setzerodata_upcall_args *args = &upcall->args.setzerodata;
- + int status;
- + status = safe_write(&buffer, length, &args->ctime, sizeof(args->ctime));
- + return status;
- +}
- +
- +const nfs41_upcall_op nfs41_op_duplicatedata = {
- + .parse = parse_duplicatedata,
- + .handle = handle_duplicatedata,
- + .marshall = marshall_duplicatedata,
- + .arg_size = sizeof(duplicatedata_upcall_args)
- +};
- diff --git a/daemon/nfs41.h b/daemon/nfs41.h
- index 7805659..9c2bdab 100644
- --- a/daemon/nfs41.h
- +++ b/daemon/nfs41.h
- @@ -59,6 +59,7 @@ typedef struct __nfs41_superblock {
- unsigned int case_preserving : 1;
- unsigned int case_insensitive : 1;
- unsigned int sparse_file_support : 1;
- + unsigned int block_clone_support : 1;
- /* variable filesystem attributes */
- uint64_t space_avail;
- diff --git a/daemon/nfs41_superblock.c b/daemon/nfs41_superblock.c
- index 880b115..c64f129 100644
- --- a/daemon/nfs41_superblock.c
- +++ b/daemon/nfs41_superblock.c
- @@ -125,6 +125,12 @@ static int get_superblock_attrs(
- superblock->case_preserving = info.case_preserving;
- superblock->case_insensitive = info.case_insensitive;
- superblock->sparse_file_support = 1; /* always ON for now */
- + if (session->client->root->nfsminorvers >= 2) {
- + superblock->block_clone_support = 1;
- + }
- + else {
- + superblock->block_clone_support = 0;
- + }
- if (bitmap_isset(&info.attrmask, 0, FATTR4_WORD0_CANSETTIME))
- superblock->cansettime = info.cansettime;
- @@ -154,7 +160,8 @@ static int get_superblock_attrs(
- "maxread=%llu, maxwrite=%llu, layout_types: 0x%X, "
- "cansettime=%u, time_delta={%llu,%u}, aclsupport=%u, "
- "link_support=%u, symlink_support=%u, case_preserving=%u, "
- - "case_insensitive=%u sparse_file_support=%u\n",
- + "case_insensitive=%u, sparse_file_support=%u, "
- + "block_clone_support=%u\n",
- superblock->fsid.major, superblock->fsid.minor,
- superblock->maxread, superblock->maxwrite,
- superblock->layout_types, superblock->cansettime,
- @@ -162,7 +169,8 @@ static int get_superblock_attrs(
- superblock->aclsupport, superblock->link_support,
- superblock->symlink_support, superblock->case_preserving,
- superblock->case_insensitive,
- - superblock->sparse_file_support));
- + superblock->sparse_file_support,
- + superblock->block_clone_support));
- out:
- return status;
- }
- @@ -197,6 +205,8 @@ void nfs41_superblock_fs_attributes(
- FsAttrs->FileSystemAttributes |= FILE_CASE_SENSITIVE_SEARCH;
- if (superblock->aclsupport)
- FsAttrs->FileSystemAttributes |= FILE_PERSISTENT_ACLS;
- + if (superblock->block_clone_support)
- + FsAttrs->FileSystemAttributes |= FILE_SUPPORTS_BLOCK_REFCOUNTING;
- /* gisburn: Fixme: We should someone query this (NFSv4.2 ?) */
- FsAttrs->MaximumComponentNameLength = NFS41_MAX_COMPONENT_LEN;
- diff --git a/daemon/upcall.c b/daemon/upcall.c
- index 8643fb5..fe19deb 100644
- --- a/daemon/upcall.c
- +++ b/daemon/upcall.c
- @@ -51,6 +51,7 @@ extern const nfs41_upcall_op nfs41_op_getacl;
- 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;
- /* |_nfs41_opcodes| and |g_upcall_op_table| must be in sync! */
- static const nfs41_upcall_op *g_upcall_op_table[] = {
- @@ -76,6 +77,7 @@ static const nfs41_upcall_op *g_upcall_op_table[] = {
- &nfs41_op_setacl,
- &nfs41_op_queryallocatedranges,
- &nfs41_op_setzerodata,
- + &nfs41_op_duplicatedata,
- NULL,
- NULL
- };
- diff --git a/daemon/upcall.h b/daemon/upcall.h
- index fbed276..158b8d4 100644
- --- a/daemon/upcall.h
- +++ b/daemon/upcall.h
- @@ -204,6 +204,13 @@ typedef struct __setzerodata_upcall_args {
- ULONGLONG ctime;
- } setzerodata_upcall_args;
- +typedef struct __duplicatedata_upcall_args {
- + nfs41_open_state *src_state;
- + LONGLONG srcfileoffset;
- + LONGLONG destfileoffset;
- + LONGLONG bytecount;
- +} duplicatedata_upcall_args;
- +
- typedef union __upcall_args {
- mount_upcall_args mount;
- open_upcall_args open;
- @@ -222,6 +229,7 @@ typedef union __upcall_args {
- setacl_upcall_args setacl;
- queryallocatedranges_upcall_args queryallocatedranges;
- setzerodata_upcall_args setzerodata;
- + duplicatedata_upcall_args duplicatedata;
- } upcall_args;
- typedef enum _nfs41_opcodes nfs41_opcodes;
- diff --git a/include/nfs41_driver.h b/include/nfs41_driver.h
- index 6dce9d9..3837793 100644
- --- a/include/nfs41_driver.h
- +++ b/include/nfs41_driver.h
- @@ -84,6 +84,7 @@ typedef enum _nfs41_opcodes {
- NFS41_SYSOP_ACL_SET,
- NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES,
- NFS41_SYSOP_FSCTL_SET_ZERO_DATA,
- + NFS41_SYSOP_FSCTL_DUPLICATE_DATA,
- NFS41_SYSOP_SHUTDOWN,
- NFS41_SYSOP_INVALID_OPCODE1
- } nfs41_opcodes;
- diff --git a/sys/nfs41sys_debug.c b/sys/nfs41sys_debug.c
- index d958706..4e0dac7 100644
- --- a/sys/nfs41sys_debug.c
- +++ b/sys/nfs41sys_debug.c
- @@ -679,6 +679,7 @@ const char *opcode2string(int opcode)
- case NFS41_SYSOP_ACL_SET: return "NFS41_SYSOP_ACL_SET";
- 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";
- default: return "UNKNOWN";
- }
- }
- diff --git a/sys/nfs41sys_driver.h b/sys/nfs41sys_driver.h
- index ab47b4e..a237071 100644
- --- a/sys/nfs41sys_driver.h
- +++ b/sys/nfs41sys_driver.h
- @@ -283,6 +283,12 @@ typedef struct _updowncall_entry {
- struct {
- FILE_ZERO_DATA_INFORMATION setzerodata;
- } SetZeroData;
- + struct {
- + void *src_state;
- + LONGLONG srcfileoffset;
- + LONGLONG destfileoffset;
- + LONGLONG bytecount;
- + } DuplicateData;
- } u;
- } nfs41_updowncall_entry;
- @@ -665,6 +671,14 @@ NTSTATUS marshal_nfs41_setzerodata(
- NTSTATUS unmarshal_nfs41_setzerodata(
- nfs41_updowncall_entry *cur,
- unsigned char **buf);
- +NTSTATUS marshal_nfs41_duplicatedata(
- + nfs41_updowncall_entry *entry,
- + unsigned char *buf,
- + ULONG buf_len,
- + ULONG *len);
- +NTSTATUS unmarshal_nfs41_duplicatedata(
- + nfs41_updowncall_entry *cur,
- + unsigned char **buf);
- /* nfs41sys_ioctl.c */
- NTSTATUS nfs41_IoCtl(
- diff --git a/sys/nfs41sys_fsctl.c b/sys/nfs41sys_fsctl.c
- index ede33e3..3f401ec 100644
- --- a/sys/nfs41sys_fsctl.c
- +++ b/sys/nfs41sys_fsctl.c
- @@ -585,6 +585,251 @@ NTSTATUS unmarshal_nfs41_setzerodata(
- return status;
- }
- +static
- +NTSTATUS check_nfs41_duplicatedata_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_DuplicateData(
- + 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 PDUPLICATE_EXTENTS_DATA duplicatedatabuffer =
- + (PDUPLICATE_EXTENTS_DATA)FsCtl->pInputBuffer;
- + __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
- + PFILE_OBJECT srcfo = NULL;
- +
- + DbgEn();
- +
- + RxContext->IoStatusBlock.Information = 0;
- +
- + status = check_nfs41_duplicatedata_args(RxContext);
- + if (status)
- + goto out;
- +
- + if (FsCtl->pInputBuffer == NULL) {
- + status = STATUS_INVALID_USER_BUFFER;
- + goto out;
- + }
- +
- + if (FsCtl->InputBufferLength <
- + sizeof(DUPLICATE_EXTENTS_DATA)) {
- + DbgP("nfs41_DuplicateData: "
- + "buffer to small\n");
- + status = STATUS_BUFFER_TOO_SMALL;
- + goto out;
- + }
- +
- + DbgP("nfs41_DuplicateData: "
- + "duplicatedatabuffer=(FileHandle=0x%p,"
- + "SourceFileOffset=%lld,"
- + "TargetFileOffset=%lld,"
- + "ByteCount=%lld)\n",
- + (void *)duplicatedatabuffer->FileHandle,
- + (long long)duplicatedatabuffer->SourceFileOffset.QuadPart,
- + (long long)duplicatedatabuffer->TargetFileOffset.QuadPart,
- + (long long)duplicatedatabuffer->ByteCount.QuadPart);
- +
- + if (duplicatedatabuffer->ByteCount.QuadPart == 0LL) {
- + status = STATUS_SUCCESS;
- + goto out;
- + }
- +
- + status = ObReferenceObjectByHandle(duplicatedatabuffer->FileHandle,
- + 0,
- + *IoFileObjectType,
- + RxContext->CurrentIrp->RequestorMode,
- + (void **)&srcfo,
- + NULL);
- + if (!NT_SUCCESS(status)) {
- + DbgP("nfs41_DuplicateData: "
- + "ObReferenceObjectByHandle returned 0x%lx\n",
- + status);
- + goto out;
- + }
- +
- + DbgP("nfs41_DuplicateData: "
- + "srcfo=0x%p srcfo->FileName='%wZ'\n",
- + srcfo,
- + srcfo->FileName);
- +
- + if (srcfo->DeviceObject !=
- + RxContext->CurrentIrpSp->FileObject->DeviceObject) {
- + DbgP("nfs41_DuplicateData: "
- + "source and destination are on different volumes\n");
- + status = STATUS_INVALID_PARAMETER;
- + goto out;
- + }
- +
- + PFCB srcfcb = srcfo->FsContext;
- + PFOBX srcfox = srcfo->FsContext2;
- + PNFS41_FCB nfs41_src_fcb = NFS41GetFcbExtension(srcfcb);
- + PNFS41_FOBX nfs41_src_fobx = NFS41GetFobxExtension(srcfox);
- +
- + if (!nfs41_src_fcb) {
- + DbgP("nfs41_DuplicateData: No nfs41_src_fcb\n");
- + status = STATUS_INVALID_PARAMETER;
- + goto out;
- + }
- +
- + if (!nfs41_src_fobx) {
- + DbgP("nfs41_DuplicateData: No nfs41_src_fobx\n");
- + status = STATUS_INVALID_PARAMETER;
- + goto out;
- + }
- +
- + /*
- + * Disable caching because NFSv4.2 DEALLOCATE 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 DEALLOCATE, to avoid that we
- + * have outstanding writes in the kernel cache at the same
- + * location where the DEALLOCATE should do it's work
- + */
- + ULONG flag = DISABLE_CACHING;
- + DbgP("nfs41_DuplicateData: disableing caching for file '%wZ'\n",
- + SrvOpen->pAlreadyPrefixedName);
- + RxChangeBufferingState((PSRV_OPEN)SrvOpen, ULongToPtr(flag), 1);
- +
- + status = nfs41_UpcallCreate(NFS41_SYSOP_FSCTL_DUPLICATE_DATA,
- + &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 =
- + duplicatedatabuffer->SourceFileOffset.QuadPart;
- + entry->u.DuplicateData.destfileoffset =
- + duplicatedatabuffer->TargetFileOffset.QuadPart;
- + entry->u.DuplicateData.bytecount =
- + duplicatedatabuffer->ByteCount.QuadPart;
- +
- + status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
- +
- + if (status) {
- + /* Timeout - |nfs41_downcall()| will free |entry|+contents */
- + goto out;
- + }
- +
- + if (entry->psec_ctx == &entry->sec_ctx) {
- + SeDeleteClientSecurity(entry->psec_ctx);
- + }
- + entry->psec_ctx = NULL;
- +
- + if (!entry->status) {
- + DbgP("nfs41_DuplicateData: SUCCESS\n");
- + RxContext->CurrentIrp->IoStatus.Status = STATUS_SUCCESS;
- + RxContext->IoStatusBlock.Information = 0;
- + }
- + else {
- + DbgP("nfs41_DuplicateData: "
- + "FAILURE, entry->status=0x%lx\n", entry->status);
- + status = map_setfile_error(entry->status);
- + RxContext->CurrentIrp->IoStatus.Status = status;
- + RxContext->IoStatusBlock.Information = 0;
- + }
- +
- + if (entry) {
- + nfs41_UpcallDestroy(entry);
- + }
- +
- +out:
- + if (srcfo) {
- + ObDereferenceObject(srcfo);
- + }
- +
- + DbgEx();
- + return status;
- +}
- +
- +NTSTATUS marshal_nfs41_duplicatedata(
- + nfs41_updowncall_entry *entry,
- + unsigned char *buf,
- + ULONG buf_len,
- + ULONG *len)
- +{
- + NTSTATUS status = STATUS_SUCCESS;
- + ULONG header_len = 0;
- + unsigned char *tmp = buf;
- +
- + status = marshal_nfs41_header(entry, tmp, buf_len, len);
- + if (status) goto out;
- + else tmp += *len;
- +
- + header_len = *len +
- + sizeof(void *) +
- + 3*sizeof(LONGLONG);
- + if (header_len > buf_len) {
- + status = STATUS_INSUFFICIENT_RESOURCES;
- + goto out;
- + }
- +
- + RtlCopyMemory(tmp, &entry->u.DuplicateData.src_state,
- + sizeof(entry->u.DuplicateData.src_state));
- + tmp += sizeof(entry->u.DuplicateData.src_state);
- + RtlCopyMemory(tmp, &entry->u.DuplicateData.srcfileoffset,
- + sizeof(entry->u.DuplicateData.srcfileoffset));
- + tmp += sizeof(entry->u.DuplicateData.srcfileoffset);
- + RtlCopyMemory(tmp, &entry->u.DuplicateData.destfileoffset,
- + sizeof(entry->u.DuplicateData.destfileoffset));
- + tmp += sizeof(entry->u.DuplicateData.destfileoffset);
- + RtlCopyMemory(tmp, &entry->u.DuplicateData.bytecount,
- + sizeof(entry->u.DuplicateData.bytecount));
- + tmp += sizeof(entry->u.DuplicateData.bytecount);
- + *len = header_len;
- +
- + DbgP("marshal_nfs41_duplicatedata: name='%wZ'\n",
- + entry->filename);
- +out:
- + return status;
- +}
- +
- +NTSTATUS unmarshal_nfs41_duplicatedata(
- + nfs41_updowncall_entry *cur,
- + unsigned char **buf)
- +{
- + NTSTATUS status = STATUS_SUCCESS;
- +
- + RtlCopyMemory(&cur->ChangeTime, *buf, sizeof(ULONGLONG));
- + DbgP("unmarshal_nfs41_duplicatedata: returned ChangeTime %llu\n",
- + cur->ChangeTime);
- +
- + return status;
- +}
- +
- NTSTATUS nfs41_FsCtl(
- IN OUT PRX_CONTEXT RxContext)
- {
- @@ -612,6 +857,9 @@ NTSTATUS nfs41_FsCtl(
- case FSCTL_SET_ZERO_DATA:
- status = nfs41_SetZeroData(RxContext);
- break;
- + case FSCTL_DUPLICATE_EXTENTS_TO_FILE:
- + status = nfs41_DuplicateData(RxContext);
- + break;
- default:
- break;
- }
- diff --git a/sys/nfs41sys_updowncall.c b/sys/nfs41sys_updowncall.c
- index e9e6eac..5e2edee 100644
- --- a/sys/nfs41sys_updowncall.c
- +++ b/sys/nfs41sys_updowncall.c
- @@ -310,6 +310,10 @@ NTSTATUS handle_upcall(
- status = marshal_nfs41_setzerodata(entry,
- pbOut, cbOut, len);
- break;
- + case NFS41_SYSOP_FSCTL_DUPLICATE_DATA:
- + status = marshal_nfs41_duplicatedata(entry,
- + pbOut, cbOut, len);
- + break;
- default:
- status = STATUS_INVALID_PARAMETER;
- print_error("Unknown nfs41 ops %d\n", entry->opcode);
- @@ -674,6 +678,9 @@ NTSTATUS nfs41_downcall(
- case NFS41_SYSOP_FSCTL_SET_ZERO_DATA:
- unmarshal_nfs41_setzerodata(cur, &buf);
- break;
- + case NFS41_SYSOP_FSCTL_DUPLICATE_DATA:
- + unmarshal_nfs41_duplicatedata(cur, &buf);
- + break;
- }
- }
- ExReleaseFastMutex(&cur->lock);
- --
- 2.45.1
- From 28936aedca676aaf430831f709533388e00accca Mon Sep 17 00:00:00 2001
- From: Roland Mainz <roland.mainz@nrubsig.org>
- Date: Tue, 15 Apr 2025 18:41:54 +0200
- Subject: [PATCH 4/4] daemon: Implement |FSCTL_DUPLICATE_EXTENTS_TO_FILE| via
- NFS CLONE
- Implement |FSCTL_DUPLICATE_EXTENTS_TO_FILE| via NFSv4.2 CLONE
- Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
- ---
- daemon/fsctl.c | 120 +++++++++++++++++++++++++++++++++++++++++----
- daemon/nfs41_ops.h | 24 +++++++++
- daemon/nfs41_xdr.c | 2 +-
- daemon/nfs41_xdr.h | 2 +
- daemon/nfs42_ops.c | 92 ++++++++++++++++++++++++++++++++++
- daemon/nfs42_xdr.c | 42 ++++++++++++++++
- daemon/recovery.c | 3 ++
- daemon/upcall.h | 1 +
- 8 files changed, 276 insertions(+), 10 deletions(-)
- diff --git a/daemon/fsctl.c b/daemon/fsctl.c
- index fa0c342..91f0763 100644
- --- a/daemon/fsctl.c
- +++ b/daemon/fsctl.c
- @@ -463,31 +463,133 @@ out:
- return status;
- }
- +/*
- + * CLONE_PUNCH_HOLE_IF_CLONESIZE_BIGGER_THAN_SRCFILESIZE - Punch
- + * a hole at the end of the file if the clone range is larger than
- + * the source file's size (Linux 6.6 nfsd will return
- + * |NFS4ERR_INVAL| in such cases)
- + *
- + * ToDo:
- + * - This should be replaced by a NFS SEEK loop to enumerate
- + * data ranges and hole ranges - Data ranges are handled via NFS
- + * CLONE, hole ranges via NFS DEALLOCATE
- + */
- +#define CLONE_PUNCH_HOLE_IF_CLONESIZE_BIGGER_THAN_SRCFILESIZE 1
- +
- static
- int handle_duplicatedata(void *daemon_context,
- nfs41_upcall *upcall)
- {
- int status = ERROR_INVALID_PARAMETER;
- duplicatedata_upcall_args *args = &upcall->args.duplicatedata;
- - nfs41_open_state *state = upcall->state_ref;
- -// nfs41_session *session = state->session;
- -// nfs41_path_fh *file = &state->file;
- + nfs41_open_state *src_state = args->src_state;
- + nfs41_open_state *dst_state = upcall->state_ref;
- + nfs41_session *session = dst_state->session;
- + nfs41_path_fh *src_file = &src_state->file;
- + nfs41_path_fh *dst_file = &dst_state->file;
- + nfs41_file_info info;
- + stateid_arg src_stateid;
- + stateid_arg dst_stateid;
- + int64_t bytecount;
- +
- + (void)memset(&info, 0, sizeof(info));
- DPRINTF(DDLVL,
- ("--> handle_duplicatedata("
- - "state->path.path='%s', "
- + "dst_state->path.path='%s', "
- "src_state->path.path='%s')\n",
- - state->path.path,
- - args->src_state->path.path));
- + dst_state->path.path,
- + src_state->path.path));
- /* NFS CLONE requires NFSv4.2 */
- - if (state->session->client->root->nfsminorvers < 2) {
- + if (dst_state->session->client->root->nfsminorvers < 2) {
- status = ERROR_NOT_SUPPORTED;
- goto out;
- }
- - /* FIXME: Cloning not implemented yet */
- - status = ERROR_NOT_SUPPORTED;
- + nfs41_open_stateid_arg(src_state, &src_stateid);
- + nfs41_open_stateid_arg(dst_state, &dst_stateid);
- +
- +#ifdef CLONE_PUNCH_HOLE_IF_CLONESIZE_BIGGER_THAN_SRCFILESIZE
- + int64_t src_file_size;
- + bitmap4 attr_request = { .count = 1, .arr[0] = FATTR4_WORD0_SIZE };
- + status = nfs41_getattr(session, src_file, &attr_request, &info);
- + if (status) {
- + eprintf("handle_duplicatedata: "
- + "nfs41_getattr() failed with '%s'\n",
- + nfs_error_string(status));
- + status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
- + goto out;
- + }
- + src_file_size = info.size;
- +
- + if (args->bytecount > src_file_size)
- + bytecount = src_file_size;
- + else
- + bytecount = args->bytecount;
- +#else
- + bytecount = args->bytecount;
- +#endif /* CLONE_PUNCH_HOLE_IF_CLONESIZE_BIGGER_THAN_SRCFILESIZE */
- +
- + status = nfs42_clone(session,
- + src_file,
- + dst_file,
- + &src_stateid,
- + &dst_stateid,
- + args->srcfileoffset,
- + args->destfileoffset,
- + bytecount,
- + &info);
- + if (status) {
- + DPRINTF(DDLVL,
- + ("handle_duplicatedata("
- + "src_state->path.path='%s' "
- + "dst_state->path.path='%s'): "
- + "CLONE failed with status=0x%x\n",
- + src_state->path.path,
- + dst_state->path.path,
- + status));
- + goto out;
- + }
- +
- +#ifdef CLONE_PUNCH_HOLE_IF_CLONESIZE_BIGGER_THAN_SRCFILESIZE
- + if (args->bytecount > src_file_size) {
- + DPRINTF(DDLVL,
- + ("handle_duplicatedata(): "
- + "Clone range (%llu) bigger than source file size "
- + "(%llu), adding hole at the end of dest file.\n",
- + args->bytecount,
- + src_file_size));
- +
- + status = nfs42_deallocate(session,
- + dst_file,
- + &dst_stateid,
- + src_file_size,
- + (args->bytecount - src_file_size),
- + &info);
- + if (status) {
- + DPRINTF(DDLVL,
- + ("handle_duplicatedata("
- + "src_state->path.path='%s' "
- + "dst_state->path.path='%s'): "
- + "DEALLOCATE failed with status=0x%x\n",
- + src_state->path.path,
- + dst_state->path.path,
- + status));
- + goto out;
- + }
- + }
- +#endif /* CLONE_PUNCH_HOLE_IF_CLONESIZE_BIGGER_THAN_SRCFILESIZE */
- +
- + /* Update ctime on success */
- + EASSERT(bitmap_isset(&info.attrmask, 0, FATTR4_WORD0_CHANGE));
- + args->ctime = info.change;
- +
- + DPRINTF(DDLVL,
- + ("handle_duplicatedata(dst_state->path.path='%s'): "
- + "args->ctime=%llu\n",
- + dst_state->path.path,
- + args->ctime));
- out:
- DPRINTF(0,
- diff --git a/daemon/nfs41_ops.h b/daemon/nfs41_ops.h
- index 149bc08..d49346b 100644
- --- a/daemon/nfs41_ops.h
- +++ b/daemon/nfs41_ops.h
- @@ -830,6 +830,19 @@ typedef struct __nfs42_seek_res {
- nfs42_seek_res_ok resok4;
- } nfs42_seek_res;
- +/* OP_CLONE */
- +typedef struct __nfs42_clone_args {
- + stateid_arg *src_stateid;
- + stateid_arg *dst_stateid;
- + uint64_t src_offset;
- + uint64_t dst_offset;
- + uint64_t count;
- +} nfs42_clone_args;
- +
- +typedef struct __nfs42_clone_res {
- + uint32_t status;
- +} nfs42_clone_res;
- +
- /* OP_READDIR */
- typedef struct __nfs41_readdir_args {
- nfs41_readdir_cookie cookie;
- @@ -1250,6 +1263,17 @@ int nfs42_seek(
- OUT bool_t *eof_out,
- OUT uint64_t *offset_out);
- +int nfs42_clone(
- + IN nfs41_session *session,
- + IN nfs41_path_fh *src_file,
- + IN nfs41_path_fh *dst_file,
- + IN stateid_arg *src_stateid,
- + IN stateid_arg *dst_stateid,
- + IN uint64_t src_offset,
- + IN uint64_t dst_offset,
- + IN uint64_t length,
- + OUT nfs41_file_info *cinfo);
- +
- int nfs41_commit(
- IN nfs41_session *session,
- IN nfs41_path_fh *file,
- diff --git a/daemon/nfs41_xdr.c b/daemon/nfs41_xdr.c
- index ecae512..2d651a4 100644
- --- a/daemon/nfs41_xdr.c
- +++ b/daemon/nfs41_xdr.c
- @@ -3672,7 +3672,7 @@ static const op_table_entry g_op_table[] = {
- { encode_op_read_plus, decode_op_read_plus }, /* OP_READ_PLUS = 68, */
- { encode_op_seek, decode_op_seek }, /* OP_SEEK = 69, */
- { NULL, NULL }, /* OP_WRITE_SAME = 70, */
- - { NULL, NULL }, /* OP_CLONE = 71, */
- + { encode_op_clone, decode_op_clone }, /* OP_CLONE = 71, */
- /* xattr support (RFC8726) */
- { NULL, NULL }, /* OP_GETXATTR = 72, */
- diff --git a/daemon/nfs41_xdr.h b/daemon/nfs41_xdr.h
- index 0019ec9..f2f2182 100644
- --- a/daemon/nfs41_xdr.h
- +++ b/daemon/nfs41_xdr.h
- @@ -42,5 +42,7 @@ bool_t encode_op_read_plus(XDR *xdr, nfs_argop4 *argop);
- bool_t decode_op_read_plus(XDR *xdr, nfs_resop4 *resop);
- bool_t encode_op_seek(XDR *xdr, nfs_argop4 *argop);
- bool_t decode_op_seek(XDR *xdr, nfs_resop4 *resop);
- +bool_t encode_op_clone(XDR *xdr, nfs_argop4 *argop);
- +bool_t decode_op_clone(XDR *xdr, nfs_resop4 *resop);
- #endif /* !__NFS41_NFS_XDR_H__ */
- diff --git a/daemon/nfs42_ops.c b/daemon/nfs42_ops.c
- index f416986..dc5a769 100644
- --- a/daemon/nfs42_ops.c
- +++ b/daemon/nfs42_ops.c
- @@ -302,3 +302,95 @@ int nfs42_seek(
- out:
- return status;
- }
- +
- +int nfs42_clone(
- + IN nfs41_session *session,
- + IN nfs41_path_fh *src_file,
- + IN nfs41_path_fh *dst_file,
- + IN stateid_arg *src_stateid,
- + IN stateid_arg *dst_stateid,
- + IN uint64_t src_offset,
- + IN uint64_t dst_offset,
- + IN uint64_t length,
- + OUT nfs41_file_info *cinfo)
- +{
- + int status;
- + nfs41_compound compound;
- + nfs_argop4 argops[7];
- + nfs_resop4 resops[7];
- + nfs41_sequence_args sequence_args;
- + nfs41_sequence_res sequence_res;
- + nfs41_putfh_args src_putfh_args;
- + nfs41_putfh_res src_putfh_res;
- + nfs41_savefh_res savefh_res;
- + nfs41_putfh_args dst_putfh_args;
- + nfs41_putfh_res dst_putfh_res;
- + nfs42_clone_args clone_args;
- + nfs42_clone_res clone_res;
- + nfs41_getattr_args getattr_args;
- + nfs41_getattr_res getattr_res = {0};
- + bitmap4 attr_request;
- + nfs41_file_info info, *pinfo;
- +
- + nfs41_superblock_getattr_mask(dst_file->fh.superblock, &attr_request);
- +
- + /* FIXME: What about DS in pNFS case ? */
- + compound_init(&compound, session->client->root->nfsminorvers,
- + argops, resops, "clone");
- +
- + compound_add_op(&compound, OP_SEQUENCE,
- + &sequence_args, &sequence_res);
- + nfs41_session_sequence(&sequence_args, session, 0);
- +
- + compound_add_op(&compound, OP_PUTFH, &src_putfh_args, &src_putfh_res);
- + src_putfh_args.file = src_file;
- + src_putfh_args.in_recovery = 0;
- +
- + compound_add_op(&compound, OP_SAVEFH, NULL, &savefh_res);
- +
- + compound_add_op(&compound, OP_PUTFH, &dst_putfh_args, &dst_putfh_res);
- + dst_putfh_args.file = dst_file;
- + dst_putfh_args.in_recovery = 0;
- +
- + compound_add_op(&compound, OP_CLONE, &clone_args, &clone_res);
- + clone_args.src_stateid = src_stateid;
- + clone_args.dst_stateid = dst_stateid;
- + clone_args.src_offset = src_offset;
- + clone_args.dst_offset = dst_offset;
- + clone_args.count = length;
- +
- + if (cinfo) {
- + pinfo = cinfo;
- + }
- + else {
- + (void)memset(&info, 0, sizeof(info));
- + pinfo = &info;
- + }
- +
- + /*
- + * NFSv4.2 CLONE is some kind of "write" operation and
- + * affects the number of physical bytes allocated, so we have
- + * to do a GETATTR after CLONE to get updates for our cache
- + */
- + compound_add_op(&compound, OP_GETATTR, &getattr_args, &getattr_res);
- + getattr_args.attr_request = &attr_request;
- + getattr_res.obj_attributes.attr_vals_len = NFS4_OPAQUE_LIMIT;
- + getattr_res.info = pinfo;
- +
- + status = compound_encode_send_decode(session, &compound, TRUE);
- + if (status)
- + goto out;
- +
- + if (compound_error(status = compound.res.status))
- + goto out;
- +
- + /* update the attribute cache */
- + bitmap4_cpy(&pinfo->attrmask, &getattr_res.obj_attributes.attrmask);
- + nfs41_attr_cache_update(session_name_cache(session),
- + dst_file->fh.fileid, pinfo);
- +
- + nfs41_superblock_space_changed(dst_file->fh.superblock);
- +
- +out:
- + return status;
- +}
- diff --git a/daemon/nfs42_xdr.c b/daemon/nfs42_xdr.c
- index 12d133b..ab7248e 100644
- --- a/daemon/nfs42_xdr.c
- +++ b/daemon/nfs42_xdr.c
- @@ -332,3 +332,45 @@ bool_t decode_op_seek(
- return TRUE;
- }
- +
- +/*
- + * OP_CLONE
- + */
- +bool_t encode_op_clone(
- + XDR *xdr,
- + nfs_argop4 *argop)
- +{
- + nfs42_clone_args *args = (nfs42_clone_args *)argop->arg;
- +
- + if (unexpected_op(argop->op, OP_CLONE))
- + return FALSE;
- +
- + if (!xdr_stateid4(xdr, &args->src_stateid->stateid))
- + return FALSE;
- +
- + if (!xdr_stateid4(xdr, &args->dst_stateid->stateid))
- + return FALSE;
- +
- + if (!xdr_u_hyper(xdr, &args->src_offset))
- + return FALSE;
- +
- + if (!xdr_u_hyper(xdr, &args->dst_offset))
- + return FALSE;
- +
- + return xdr_u_hyper(xdr, &args->count);
- +}
- +
- +bool_t decode_op_clone(
- + XDR *xdr,
- + nfs_resop4 *resop)
- +{
- + nfs42_clone_res *res = (nfs42_clone_res *)resop->res;
- +
- + if (unexpected_op(resop->op, OP_CLONE))
- + return FALSE;
- +
- + if (!xdr_u_int32_t(xdr, &res->status))
- + return FALSE;
- +
- + return TRUE;
- +}
- diff --git a/daemon/recovery.c b/daemon/recovery.c
- index 66bf25e..c3dd695 100644
- --- a/daemon/recovery.c
- +++ b/daemon/recovery.c
- @@ -820,6 +820,9 @@ bool_t nfs41_recover_stateid(
- } else if (argop->op == OP_SEEK) {
- nfs42_seek_args *seek = (nfs42_seek_args *)argop->arg;
- stateid = seek->stateid;
- + } else if (argop->op == OP_CLONE) {
- + nfs42_clone_args *clone = (nfs42_clone_args *)argop->arg;
- + stateid = clone->dst_stateid;
- } else if (argop->op == OP_WRITE) {
- nfs41_write_args *write = (nfs41_write_args*)argop->arg;
- stateid = write->stateid;
- diff --git a/daemon/upcall.h b/daemon/upcall.h
- index 158b8d4..c14adc7 100644
- --- a/daemon/upcall.h
- +++ b/daemon/upcall.h
- @@ -209,6 +209,7 @@ typedef struct __duplicatedata_upcall_args {
- LONGLONG srcfileoffset;
- LONGLONG destfileoffset;
- LONGLONG bytecount;
- + ULONGLONG ctime;
- } duplicatedata_upcall_args;
- typedef union __upcall_args {
- --
- 2.45.1
msnfs41client: Implement |FSCTL_DUPLICATE_EXTENTS_TO_FILE| vai NFSV4.2 CLONE+misc, 2025-04-15
Posted by Anonymous on Tue 15th Apr 2025 17:52
raw | new post
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.