- From be701adcd0257bea2ca6ee47c8a4f46f9071a414 Mon Sep 17 00:00:00 2001
- From: Roland Mainz <roland.mainz@nrubsig.org>
- Date: Mon, 18 Aug 2025 21:03:26 +0200
- Subject: [PATCH] daemon: Add support for NFSv4.2 (synchronous) COPY
- Add support for NFSv4.2 (synchronous) COPY.
- The codepath is currently "off" by default, can be enabled via
- |DUP_DATA_USE_NFSCOPY| in daemon/fsctl.c.
- Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
- ---
- daemon/fsctl.c | 36 +++++++++++++++-
- daemon/namespace.c | 2 +
- daemon/nfs41.h | 1 +
- daemon/nfs41_ops.h | 56 ++++++++++++++++++++++++
- daemon/nfs41_xdr.c | 2 +-
- daemon/nfs41_xdr.h | 2 +
- daemon/nfs42_ops.c | 104 +++++++++++++++++++++++++++++++++++++++++++++
- daemon/nfs42_xdr.c | 101 +++++++++++++++++++++++++++++++++++++++++++
- daemon/recovery.c | 3 ++
- 9 files changed, 304 insertions(+), 3 deletions(-)
- diff --git a/daemon/fsctl.c b/daemon/fsctl.c
- index 5fa19c8..e1c4f19 100644
- --- a/daemon/fsctl.c
- +++ b/daemon/fsctl.c
- @@ -27,6 +27,9 @@
- #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 */
- @@ -591,6 +594,20 @@ 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,
- @@ -600,14 +617,23 @@ int duplicate_sparsefile(nfs41_open_state *src_state,
- 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 */
- +
- DPRINTF(0/*DDLVL*/,
- ("duplicate_sparsefile("
- "src_state->path.path='%s' "
- "dst_state->path.path='%s'): "
- - "CLONE failed with '%s'\n",
- + "'%s' failed with '%s'\n",
- 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;
- @@ -668,7 +694,13 @@ int handle_duplicatedata(void *daemon_context,
- goto out;
- }
- /* NFS CLONE supported ? */
- - if (src_session->client->root->supports_nfs42_clone == false) {
- + 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;
- }
- diff --git a/daemon/namespace.c b/daemon/namespace.c
- index b77b499..75bc67f 100644
- --- a/daemon/namespace.c
- +++ b/daemon/namespace.c
- @@ -72,6 +72,7 @@ int nfs41_root_create(
- root->supports_nfs42_read_plus = false;
- root->supports_nfs42_seek = false;
- root->supports_nfs42_allocate = false;
- + root->supports_nfs42_copy = false;
- root->supports_nfs42_deallocate = false;
- root->supports_nfs42_clone = false;
- if (nfsvers == NFS_VERSION_AUTONEGOTIATION) {
- @@ -453,6 +454,7 @@ retry_nfs41_exchange_id:
- root->supports_nfs42_read_plus = true;
- root->supports_nfs42_seek = true;
- root->supports_nfs42_allocate = true;
- + root->supports_nfs42_copy = true;
- root->supports_nfs42_deallocate = true;
- root->supports_nfs42_clone = true;
- }
- diff --git a/daemon/nfs41.h b/daemon/nfs41.h
- index cd9fb33..242417e 100644
- --- a/daemon/nfs41.h
- +++ b/daemon/nfs41.h
- @@ -306,6 +306,7 @@ typedef struct __nfs41_root {
- bool supports_nfs42_read_plus;
- bool supports_nfs42_seek;
- bool supports_nfs42_allocate;
- + bool supports_nfs42_copy;
- bool supports_nfs42_deallocate;
- bool supports_nfs42_clone;
- DWORD nfsminorvers;
- diff --git a/daemon/nfs41_ops.h b/daemon/nfs41_ops.h
- index b36627d..dd9dcb4 100644
- --- a/daemon/nfs41_ops.h
- +++ b/daemon/nfs41_ops.h
- @@ -801,6 +801,50 @@ typedef struct __nfs42_allocate_res {
- uint32_t status;
- } nfs42_allocate_res;
- +/* OP_COPY */
- +typedef struct __nfs42_copy_args {
- + stateid_arg *src_stateid;
- + stateid_arg *dst_stateid;
- + uint64_t src_offset;
- + uint64_t dst_offset;
- + uint64_t count;
- + bool_t consecutive;
- + bool_t synchronous;
- +/*
- + * netloc4 ca_source_server<>; not implemented yet
- + */
- +} nfs42_copy_args;
- +
- +typedef struct __nfs42_write_response {
- + uint32_t callback_id_count;
- + stateid4 callback_id[1];
- + uint64_t count;
- + uint32_t committed; /* stable_how4 */
- + nfs41_write_verf *writeverf;
- +} nfs42_write_response;
- +
- +typedef struct __nfs42_copy_requirements {
- + bool_t consecutive;
- + bool_t synchronous;
- +} nfs42_copy_requirements;
- +
- +typedef struct __nfs42_copy_res_ok {
- + nfs42_write_response response;
- + nfs42_copy_requirements requirements;
- +} nfs42_copy_res_ok;
- +
- +typedef struct __nfs42_copy_res {
- + uint32_t status;
- + /* switch (status) */
- + union {
- + /* case NFS4_OK: */
- + nfs42_copy_res_ok resok4;
- + /* case NFS4ERR_OFFLOAD_NO_REQS: */
- + nfs42_copy_requirements requirements;
- + /* default: void; */
- + } u;
- +} nfs42_copy_res;
- +
- /* OP_DEALLOCATE */
- typedef struct __nfs42_deallocate_args {
- stateid_arg *stateid;
- @@ -1246,6 +1290,18 @@ int nfs42_allocate(
- IN uint64_t length,
- OUT nfs41_file_info *cinfo);
- +int nfs42_copy(
- + 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_write_verf *writeverf,
- + OUT nfs41_file_info *cinfo);
- +
- int nfs42_deallocate(
- IN nfs41_session *session,
- IN nfs41_path_fh *file,
- diff --git a/daemon/nfs41_xdr.c b/daemon/nfs41_xdr.c
- index 10975db..c1e22e5 100644
- --- a/daemon/nfs41_xdr.c
- +++ b/daemon/nfs41_xdr.c
- @@ -3674,7 +3674,7 @@ static const op_table_entry g_op_table[] = {
- /* new operations for NFSv4.2 */
- { encode_op_allocate, decode_op_allocate }, /* OP_ALLOCATE = 59, */
- - { NULL, NULL }, /* OP_COPY = 60, */
- + { encode_op_copy, decode_op_copy }, /* OP_COPY = 60, */
- { NULL, NULL }, /* OP_COPY_NOTIFY = 61, */
- { encode_op_deallocate, decode_op_deallocate }, /* OP_DEALLOCATE = 62, */
- { NULL, NULL }, /* OP_IO_ADVISE = 63, */
- diff --git a/daemon/nfs41_xdr.h b/daemon/nfs41_xdr.h
- index f2f2182..cdd6213 100644
- --- a/daemon/nfs41_xdr.h
- +++ b/daemon/nfs41_xdr.h
- @@ -36,6 +36,8 @@ bool_t xdr_stateid4(XDR *xdr, stateid4 *si);
- /* NFSv4.2 ops */
- bool_t encode_op_allocate(XDR *xdr, nfs_argop4 *argop);
- bool_t decode_op_allocate(XDR *xdr, nfs_resop4 *resop);
- +bool_t encode_op_copy(XDR *xdr, nfs_argop4 *argop);
- +bool_t decode_op_copy(XDR *xdr, nfs_resop4 *resop);
- bool_t encode_op_deallocate(XDR *xdr, nfs_argop4 *argop);
- bool_t decode_op_deallocate(XDR *xdr, nfs_resop4 *resop);
- bool_t encode_op_read_plus(XDR *xdr, nfs_argop4 *argop);
- diff --git a/daemon/nfs42_ops.c b/daemon/nfs42_ops.c
- index 4c6a35c..582a968 100644
- --- a/daemon/nfs42_ops.c
- +++ b/daemon/nfs42_ops.c
- @@ -113,6 +113,110 @@ out:
- return status;
- }
- +int nfs42_copy(
- + 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_write_verf *writeverf,
- + 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_copy_args copy_args;
- + nfs42_copy_res copy_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, "copy");
- +
- + 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_COPY, ©_args, ©_res);
- + copy_args.src_stateid = src_stateid;
- + copy_args.dst_stateid = dst_stateid;
- + copy_args.src_offset = src_offset;
- + copy_args.dst_offset = dst_offset;
- + copy_args.count = length;
- + copy_args.consecutive = TRUE;
- + copy_args.synchronous = TRUE;
- + copy_res.u.resok4.response.writeverf = writeverf;
- +
- + if (cinfo) {
- + pinfo = cinfo;
- + }
- + else {
- + (void)memset(&info, 0, sizeof(info));
- + pinfo = &info;
- + }
- +
- + /*
- + * NFSv4.2 COPY is some kind of "write" operation and
- + * affects the number of physical bytes allocated, so we have
- + * to do a GETATTR after COPY 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_ATTR;
- + 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);
- +
- + 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;
- + }
- +
- +out:
- + return status;
- +}
- +
- int nfs42_deallocate(
- IN nfs41_session *session,
- IN nfs41_path_fh *file,
- diff --git a/daemon/nfs42_xdr.c b/daemon/nfs42_xdr.c
- index e3da651..beb4907 100644
- --- a/daemon/nfs42_xdr.c
- +++ b/daemon/nfs42_xdr.c
- @@ -77,6 +77,107 @@ bool_t decode_op_allocate(
- return TRUE;
- }
- +/*
- + * OP_COPY
- + */
- +bool_t encode_op_copy(
- + XDR *xdr,
- + nfs_argop4 *argop)
- +{
- + nfs42_copy_args *args = (nfs42_copy_args *)argop->arg;
- +
- + if (unexpected_op(argop->op, OP_COPY))
- + return FALSE;
- +
- + if (!xdr_stateid4(xdr, &args->src_stateid->stateid))
- + return FALSE;
- +
- + if (!xdr_stateid4(xdr, &args->dst_stateid->stateid))
- + return FALSE;
- +
- + if (!xdr_uint64_t(xdr, &args->src_offset))
- + return FALSE;
- +
- + if (!xdr_uint64_t(xdr, &args->dst_offset))
- + return FALSE;
- +
- + if (!xdr_uint64_t(xdr, &args->count))
- + return FALSE;
- +
- + if (!xdr_bool(xdr, &args->consecutive))
- + return FALSE;
- +
- + if (!xdr_bool(xdr, &args->synchronous))
- + return FALSE;
- +
- + /*
- + * FIXME: We do not support server-to-server copy yet
- + * |source_server_count| means intra-server copy
- + */
- + uint32_t source_server_count = 0;
- + return xdr_uint32_t(xdr, &source_server_count);
- +}
- +
- +static bool_t decode_write_response(
- + XDR *xdr,
- + nfs42_write_response *restrict response)
- +{
- + if (!xdr_uint32_t(xdr, &response->callback_id_count))
- + return FALSE;
- + if (response->callback_id_count > 0) {
- + EASSERT(response->callback_id_count == 1);
- + if (response->callback_id_count > 1)
- + return FALSE;
- +
- + if (!xdr_stateid4(xdr, &response->callback_id[0]))
- + return FALSE;
- + }
- + if (!xdr_uint64_t(xdr, &response->count))
- + return FALSE;
- + if (!xdr_uint32_t(xdr, &response->committed))
- + return FALSE;
- + if (!xdr_opaque(xdr, (char *)response->writeverf, NFS4_VERIFIER_SIZE))
- + return FALSE;
- + return TRUE;
- +}
- +
- +static bool_t decode_copy_requirements(
- + XDR *xdr,
- + nfs42_copy_requirements *restrict requirements)
- +{
- + if (!xdr_bool(xdr, &requirements->consecutive))
- + return FALSE;
- + if (!xdr_bool(xdr, &requirements->synchronous))
- + return FALSE;
- + return TRUE;
- +}
- +
- +bool_t decode_op_copy(
- + XDR *xdr,
- + nfs_resop4 *resop)
- +{
- + nfs42_copy_res *res = (nfs42_copy_res *)resop->res;
- +
- + if (unexpected_op(resop->op, OP_COPY))
- + return FALSE;
- +
- + if (!xdr_uint32_t(xdr, &res->status))
- + return FALSE;
- +
- + if (res->status == NFS4_OK) {
- + if (!decode_write_response(xdr, &res->u.resok4.response))
- + return FALSE;
- + if (!decode_copy_requirements(xdr, &res->u.resok4.requirements))
- + return FALSE;
- + }
- + else if (res->status == NFS4ERR_OFFLOAD_NO_REQS) {
- + if (!decode_copy_requirements(xdr, &res->u.requirements))
- + return FALSE;
- + }
- +
- + return TRUE;
- +}
- +
- /*
- * OP_DEALLOCATE
- */
- diff --git a/daemon/recovery.c b/daemon/recovery.c
- index f0ee478..1106b89 100644
- --- a/daemon/recovery.c
- +++ b/daemon/recovery.c
- @@ -814,6 +814,9 @@ bool_t nfs41_recover_stateid(
- nfs42_allocate_args *allocate =
- (nfs42_allocate_args *)argop->arg;
- stateid = allocate->stateid;
- + } else if (argop->op == OP_COPY) {
- + nfs42_copy_args *copy = (nfs42_copy_args *)argop->arg;
- + stateid = copy->dst_stateid;
- } else if (argop->op == OP_DEALLOCATE) {
- nfs42_deallocate_args *deallocate =
- (nfs42_deallocate_args *)argop->arg;
- --
- 2.45.1
msnfs41client: Add support for NFSv4.2 (synchronous) COPY, 2025-08-18
Posted by Anonymous on Wed 20th Aug 2025 17:18
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.