pastebin - collaborative debugging tool
rovema.kpaste.net RSS


msnfs41client: Add support for NFSv4.2 (synchronous) COPY, 2025-08-18
Posted by Anonymous on Wed 20th Aug 2025 17:18
raw | new post

  1. From be701adcd0257bea2ca6ee47c8a4f46f9071a414 Mon Sep 17 00:00:00 2001
  2. From: Roland Mainz <roland.mainz@nrubsig.org>
  3. Date: Mon, 18 Aug 2025 21:03:26 +0200
  4. Subject: [PATCH] daemon: Add support for NFSv4.2 (synchronous) COPY
  5.  
  6. Add support for NFSv4.2 (synchronous) COPY.
  7.  
  8. The codepath is currently "off" by default, can be enabled via
  9. |DUP_DATA_USE_NFSCOPY| in daemon/fsctl.c.
  10.  
  11. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  12. ---
  13. daemon/fsctl.c     |  36 +++++++++++++++-
  14.  daemon/namespace.c |   2 +
  15.  daemon/nfs41.h     |   1 +
  16.  daemon/nfs41_ops.h |  56 ++++++++++++++++++++++++
  17.  daemon/nfs41_xdr.c |   2 +-
  18.  daemon/nfs41_xdr.h |   2 +
  19.  daemon/nfs42_ops.c | 104 +++++++++++++++++++++++++++++++++++++++++++++
  20.  daemon/nfs42_xdr.c | 101 +++++++++++++++++++++++++++++++++++++++++++
  21.  daemon/recovery.c  |   3 ++
  22.  9 files changed, 304 insertions(+), 3 deletions(-)
  23.  
  24. diff --git a/daemon/fsctl.c b/daemon/fsctl.c
  25. index 5fa19c8..e1c4f19 100644
  26. --- a/daemon/fsctl.c
  27. +++ b/daemon/fsctl.c
  28. @@ -27,6 +27,9 @@
  29.  #include "daemon_debug.h"
  30.  #include "util.h"
  31.  
  32. +/* Testing only: Use NFS COPY instead of NFS CLONE */
  33. +// #define DUP_DATA_USE_NFSCOPY 1
  34. +
  35.  #define QARLVL 2 /* dprintf level for "query allocated ranges" logging */
  36.  #define SZDLVL 2 /* dprintf level for "set zero data" logging */
  37.  #define DDLVL  2 /* dprintf level for "duplicate data" logging */
  38. @@ -591,6 +594,20 @@ int duplicate_sparsefile(nfs41_open_state *src_state,
  39.              (int)data_seek_sr_eof,
  40.              (int)hole_seek_sr_eof));
  41.  
  42. +#ifdef DUP_DATA_USE_NFSCOPY
  43. +        nfs41_write_verf verf;
  44. +        status = nfs42_copy(session,
  45. +            src_file,
  46. +            dst_file,
  47. +            &src_stateid,
  48. +            &dst_stateid,
  49. +            data_seek_sr_offset,
  50. +            destfileoffset + (data_seek_sr_offset-srcfileoffset),
  51. +            data_size,
  52. +            &verf,
  53. +            info);
  54. +        /* FIXME: What should we do with |verf| ? Should we COMMIT this ? */
  55. +#else
  56.          status = nfs42_clone(session,
  57.              src_file,
  58.              dst_file,
  59. @@ -600,14 +617,23 @@ int duplicate_sparsefile(nfs41_open_state *src_state,
  60.              destfileoffset + (data_seek_sr_offset-srcfileoffset),
  61.              data_size,
  62.              info);
  63. +#endif /* DUP_DATA_USE_NFSCOPY */
  64.          if (status) {
  65. +            const char dup_op_name[] =
  66. +#ifdef DUP_DATA_USE_NFSCOPY
  67. +                "COPY";
  68. +#else
  69. +                "CLONE";
  70. +#endif /* DUP_DATA_USE_NFSCOPY */
  71. +
  72.              DPRINTF(0/*DDLVL*/,
  73.                  ("duplicate_sparsefile("
  74.                  "src_state->path.path='%s' "
  75.                  "dst_state->path.path='%s'): "
  76. -                "CLONE failed with '%s'\n",
  77. +                "'%s' failed with '%s'\n",
  78.                  src_state->path.path,
  79.                  dst_state->path.path,
  80. +                dup_op_name,
  81.                  nfs_error_string(status)));
  82.              status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
  83.              goto out;
  84. @@ -668,7 +694,13 @@ int handle_duplicatedata(void *daemon_context,
  85.          goto out;
  86.      }
  87.      /* NFS CLONE supported ? */
  88. -    if (src_session->client->root->supports_nfs42_clone == false) {
  89. +    if (
  90. +#ifdef DUP_DATA_USE_NFSCOPY
  91. +        src_session->client->root->supports_nfs42_copy == false
  92. +#else
  93. +        src_session->client->root->supports_nfs42_clone == false
  94. +#endif /* DUP_DATA_USE_NFSCOPY */
  95. +        ) {
  96.          status = ERROR_NOT_SUPPORTED;
  97.          goto out;
  98.      }
  99. diff --git a/daemon/namespace.c b/daemon/namespace.c
  100. index b77b499..75bc67f 100644
  101. --- a/daemon/namespace.c
  102. +++ b/daemon/namespace.c
  103. @@ -72,6 +72,7 @@ int nfs41_root_create(
  104.      root->supports_nfs42_read_plus  = false;
  105.      root->supports_nfs42_seek       = false;
  106.      root->supports_nfs42_allocate   = false;
  107. +    root->supports_nfs42_copy       = false;
  108.      root->supports_nfs42_deallocate = false;
  109.      root->supports_nfs42_clone      = false;
  110.      if (nfsvers == NFS_VERSION_AUTONEGOTIATION) {
  111. @@ -453,6 +454,7 @@ retry_nfs41_exchange_id:
  112.          root->supports_nfs42_read_plus  = true;
  113.          root->supports_nfs42_seek       = true;
  114.          root->supports_nfs42_allocate   = true;
  115. +        root->supports_nfs42_copy       = true;
  116.          root->supports_nfs42_deallocate = true;
  117.          root->supports_nfs42_clone      = true;
  118.      }
  119. diff --git a/daemon/nfs41.h b/daemon/nfs41.h
  120. index cd9fb33..242417e 100644
  121. --- a/daemon/nfs41.h
  122. +++ b/daemon/nfs41.h
  123. @@ -306,6 +306,7 @@ typedef struct __nfs41_root {
  124.      bool supports_nfs42_read_plus;
  125.      bool supports_nfs42_seek;
  126.      bool supports_nfs42_allocate;
  127. +    bool supports_nfs42_copy;
  128.      bool supports_nfs42_deallocate;
  129.      bool supports_nfs42_clone;
  130.      DWORD nfsminorvers;
  131. diff --git a/daemon/nfs41_ops.h b/daemon/nfs41_ops.h
  132. index b36627d..dd9dcb4 100644
  133. --- a/daemon/nfs41_ops.h
  134. +++ b/daemon/nfs41_ops.h
  135. @@ -801,6 +801,50 @@ typedef struct __nfs42_allocate_res {
  136.      uint32_t    status;
  137.  } nfs42_allocate_res;
  138.  
  139. +/* OP_COPY */
  140. +typedef struct __nfs42_copy_args {
  141. +    stateid_arg     *src_stateid;
  142. +    stateid_arg     *dst_stateid;
  143. +    uint64_t        src_offset;
  144. +    uint64_t        dst_offset;
  145. +    uint64_t        count;
  146. +    bool_t          consecutive;
  147. +    bool_t          synchronous;
  148. +/*
  149. + *  netloc4 ca_source_server<>; not implemented yet
  150. + */
  151. +} nfs42_copy_args;
  152. +
  153. +typedef struct __nfs42_write_response {
  154. +    uint32_t            callback_id_count;
  155. +    stateid4            callback_id[1];
  156. +    uint64_t            count;
  157. +    uint32_t            committed; /* stable_how4 */
  158. +    nfs41_write_verf    *writeverf;
  159. +} nfs42_write_response;
  160. +
  161. +typedef struct __nfs42_copy_requirements {
  162. +    bool_t      consecutive;
  163. +    bool_t      synchronous;
  164. +} nfs42_copy_requirements;
  165. +
  166. +typedef struct __nfs42_copy_res_ok {
  167. +    nfs42_write_response        response;
  168. +    nfs42_copy_requirements     requirements;
  169. +} nfs42_copy_res_ok;
  170. +
  171. +typedef struct __nfs42_copy_res {
  172. +    uint32_t    status;
  173. +    /* switch (status) */
  174. +    union {
  175. +    /* case NFS4_OK: */
  176. +        nfs42_copy_res_ok           resok4;
  177. +    /* case NFS4ERR_OFFLOAD_NO_REQS: */
  178. +        nfs42_copy_requirements     requirements;
  179. +    /* default: void; */
  180. +    } u;
  181. +} nfs42_copy_res;
  182. +
  183.  /* OP_DEALLOCATE */
  184.  typedef struct __nfs42_deallocate_args {
  185.      stateid_arg     *stateid;
  186. @@ -1246,6 +1290,18 @@ int nfs42_allocate(
  187.      IN uint64_t length,
  188.      OUT nfs41_file_info *cinfo);
  189.  
  190. +int nfs42_copy(
  191. +    IN nfs41_session *session,
  192. +    IN nfs41_path_fh *src_file,
  193. +    IN nfs41_path_fh *dst_file,
  194. +    IN stateid_arg *src_stateid,
  195. +    IN stateid_arg *dst_stateid,
  196. +    IN uint64_t src_offset,
  197. +    IN uint64_t dst_offset,
  198. +    IN uint64_t length,
  199. +    OUT nfs41_write_verf *writeverf,
  200. +    OUT nfs41_file_info *cinfo);
  201. +
  202.  int nfs42_deallocate(
  203.      IN nfs41_session *session,
  204.      IN nfs41_path_fh *file,
  205. diff --git a/daemon/nfs41_xdr.c b/daemon/nfs41_xdr.c
  206. index 10975db..c1e22e5 100644
  207. --- a/daemon/nfs41_xdr.c
  208. +++ b/daemon/nfs41_xdr.c
  209. @@ -3674,7 +3674,7 @@ static const op_table_entry g_op_table[] = {
  210.  
  211.      /* new operations for NFSv4.2 */
  212.      { encode_op_allocate, decode_op_allocate }, /* OP_ALLOCATE = 59, */
  213. -    { NULL, NULL }, /* OP_COPY = 60, */
  214. +    { encode_op_copy, decode_op_copy }, /* OP_COPY = 60, */
  215.      { NULL, NULL }, /* OP_COPY_NOTIFY = 61, */
  216.      { encode_op_deallocate, decode_op_deallocate }, /* OP_DEALLOCATE = 62, */
  217.      { NULL, NULL }, /* OP_IO_ADVISE = 63, */
  218. diff --git a/daemon/nfs41_xdr.h b/daemon/nfs41_xdr.h
  219. index f2f2182..cdd6213 100644
  220. --- a/daemon/nfs41_xdr.h
  221. +++ b/daemon/nfs41_xdr.h
  222. @@ -36,6 +36,8 @@ bool_t xdr_stateid4(XDR *xdr, stateid4 *si);
  223.  /* NFSv4.2 ops */
  224.  bool_t encode_op_allocate(XDR *xdr, nfs_argop4 *argop);
  225.  bool_t decode_op_allocate(XDR *xdr, nfs_resop4 *resop);
  226. +bool_t encode_op_copy(XDR *xdr, nfs_argop4 *argop);
  227. +bool_t decode_op_copy(XDR *xdr, nfs_resop4 *resop);
  228.  bool_t encode_op_deallocate(XDR *xdr, nfs_argop4 *argop);
  229.  bool_t decode_op_deallocate(XDR *xdr, nfs_resop4 *resop);
  230.  bool_t encode_op_read_plus(XDR *xdr, nfs_argop4 *argop);
  231. diff --git a/daemon/nfs42_ops.c b/daemon/nfs42_ops.c
  232. index 4c6a35c..582a968 100644
  233. --- a/daemon/nfs42_ops.c
  234. +++ b/daemon/nfs42_ops.c
  235. @@ -113,6 +113,110 @@ out:
  236.      return status;
  237.  }
  238.  
  239. +int nfs42_copy(
  240. +    IN nfs41_session *session,
  241. +    IN nfs41_path_fh *src_file,
  242. +    IN nfs41_path_fh *dst_file,
  243. +    IN stateid_arg *src_stateid,
  244. +    IN stateid_arg *dst_stateid,
  245. +    IN uint64_t src_offset,
  246. +    IN uint64_t dst_offset,
  247. +    IN uint64_t length,
  248. +    OUT nfs41_write_verf *writeverf,
  249. +    OUT nfs41_file_info *cinfo)
  250. +{
  251. +    int status;
  252. +    nfs41_compound compound;
  253. +    nfs_argop4 argops[7];
  254. +    nfs_resop4 resops[7];
  255. +    nfs41_sequence_args sequence_args;
  256. +    nfs41_sequence_res sequence_res;
  257. +    nfs41_putfh_args src_putfh_args;
  258. +    nfs41_putfh_res src_putfh_res;
  259. +    nfs41_savefh_res savefh_res;
  260. +    nfs41_putfh_args dst_putfh_args;
  261. +    nfs41_putfh_res dst_putfh_res;
  262. +    nfs42_copy_args copy_args;
  263. +    nfs42_copy_res copy_res;
  264. +    nfs41_getattr_args getattr_args;
  265. +    nfs41_getattr_res getattr_res = {0};
  266. +    bitmap4 attr_request;
  267. +    nfs41_file_info info, *pinfo;
  268. +
  269. +    nfs41_superblock_getattr_mask(dst_file->fh.superblock, &attr_request);
  270. +
  271. +    /* FIXME: What about DS in pNFS case ? */
  272. +    compound_init(&compound, session->client->root->nfsminorvers,
  273. +        argops, resops, "copy");
  274. +
  275. +    compound_add_op(&compound, OP_SEQUENCE,
  276. +        &sequence_args, &sequence_res);
  277. +    nfs41_session_sequence(&sequence_args, session, 0);
  278. +
  279. +    compound_add_op(&compound, OP_PUTFH, &src_putfh_args, &src_putfh_res);
  280. +    src_putfh_args.file = src_file;
  281. +    src_putfh_args.in_recovery = 0;
  282. +
  283. +    compound_add_op(&compound, OP_SAVEFH, NULL, &savefh_res);
  284. +
  285. +    compound_add_op(&compound, OP_PUTFH, &dst_putfh_args, &dst_putfh_res);
  286. +    dst_putfh_args.file = dst_file;
  287. +    dst_putfh_args.in_recovery = 0;
  288. +
  289. +    compound_add_op(&compound, OP_COPY, &copy_args, &copy_res);
  290. +    copy_args.src_stateid = src_stateid;
  291. +    copy_args.dst_stateid = dst_stateid;
  292. +    copy_args.src_offset = src_offset;
  293. +    copy_args.dst_offset = dst_offset;
  294. +    copy_args.count = length;
  295. +    copy_args.consecutive = TRUE;
  296. +    copy_args.synchronous = TRUE;
  297. +    copy_res.u.resok4.response.writeverf = writeverf;
  298. +
  299. +    if (cinfo) {
  300. +        pinfo = cinfo;
  301. +    }
  302. +    else {
  303. +        (void)memset(&info, 0, sizeof(info));
  304. +        pinfo = &info;
  305. +    }
  306. +
  307. +    /*
  308. +     * NFSv4.2 COPY is some kind of "write" operation and
  309. +     * affects the number of physical bytes allocated, so we have
  310. +     * to do a GETATTR after COPY to get updates for our cache
  311. +     */
  312. +    compound_add_op(&compound, OP_GETATTR, &getattr_args, &getattr_res);
  313. +    getattr_args.attr_request = &attr_request;
  314. +    getattr_res.obj_attributes.attr_vals_len = NFS4_OPAQUE_LIMIT_ATTR;
  315. +    getattr_res.info = pinfo;
  316. +
  317. +    status = compound_encode_send_decode(session, &compound, TRUE);
  318. +    if (status)
  319. +        goto out;
  320. +
  321. +    if (compound_error(status = compound.res.status))
  322. +        goto out;
  323. +
  324. +    /* update the attribute cache */
  325. +    bitmap4_cpy(&pinfo->attrmask, &getattr_res.obj_attributes.attrmask);
  326. +    nfs41_attr_cache_update(session_name_cache(session),
  327. +        dst_file->fh.fileid, pinfo);
  328. +
  329. +    nfs41_superblock_space_changed(dst_file->fh.superblock);
  330. +
  331. +    if (copy_res.u.resok4.response.count != length) {
  332. +        DPRINTF(0,
  333. +            ("nfs42_copy: "
  334. +            "copy_res.u.resok4.response.count(=%lld) < length(=%lld)\n",
  335. +            (long long)copy_res.u.resok4.response.count, (long long)length));
  336. +        status = ERROR_NET_WRITE_FAULT;
  337. +    }
  338. +
  339. +out:
  340. +    return status;
  341. +}
  342. +
  343.  int nfs42_deallocate(
  344.      IN nfs41_session *session,
  345.      IN nfs41_path_fh *file,
  346. diff --git a/daemon/nfs42_xdr.c b/daemon/nfs42_xdr.c
  347. index e3da651..beb4907 100644
  348. --- a/daemon/nfs42_xdr.c
  349. +++ b/daemon/nfs42_xdr.c
  350. @@ -77,6 +77,107 @@ bool_t decode_op_allocate(
  351.      return TRUE;
  352.  }
  353.  
  354. +/*
  355. + * OP_COPY
  356. + */
  357. +bool_t encode_op_copy(
  358. +    XDR *xdr,
  359. +    nfs_argop4 *argop)
  360. +{
  361. +    nfs42_copy_args *args = (nfs42_copy_args *)argop->arg;
  362. +
  363. +    if (unexpected_op(argop->op, OP_COPY))
  364. +        return FALSE;
  365. +
  366. +    if (!xdr_stateid4(xdr, &args->src_stateid->stateid))
  367. +        return FALSE;
  368. +
  369. +    if (!xdr_stateid4(xdr, &args->dst_stateid->stateid))
  370. +        return FALSE;
  371. +
  372. +    if (!xdr_uint64_t(xdr, &args->src_offset))
  373. +        return FALSE;
  374. +
  375. +    if (!xdr_uint64_t(xdr, &args->dst_offset))
  376. +        return FALSE;
  377. +
  378. +    if (!xdr_uint64_t(xdr, &args->count))
  379. +        return FALSE;
  380. +
  381. +    if (!xdr_bool(xdr, &args->consecutive))
  382. +        return FALSE;
  383. +
  384. +    if (!xdr_bool(xdr, &args->synchronous))
  385. +        return FALSE;
  386. +
  387. +    /*
  388. +     * FIXME: We do not support server-to-server copy yet
  389. +    * |source_server_count| means intra-server copy
  390. +    */
  391. +    uint32_t source_server_count = 0;
  392. +    return xdr_uint32_t(xdr, &source_server_count);
  393. +}
  394. +
  395. +static bool_t decode_write_response(
  396. +    XDR *xdr,
  397. +    nfs42_write_response *restrict response)
  398. +{
  399. +    if (!xdr_uint32_t(xdr, &response->callback_id_count))
  400. +        return FALSE;
  401. +    if (response->callback_id_count > 0) {
  402. +        EASSERT(response->callback_id_count == 1);
  403. +        if (response->callback_id_count > 1)
  404. +            return FALSE;
  405. +
  406. +        if (!xdr_stateid4(xdr, &response->callback_id[0]))
  407. +            return FALSE;
  408. +    }
  409. +    if (!xdr_uint64_t(xdr, &response->count))
  410. +        return FALSE;
  411. +    if (!xdr_uint32_t(xdr, &response->committed))
  412. +        return FALSE;
  413. +    if (!xdr_opaque(xdr, (char *)response->writeverf, NFS4_VERIFIER_SIZE))
  414. +        return FALSE;
  415. +    return TRUE;
  416. +}
  417. +
  418. +static bool_t decode_copy_requirements(
  419. +    XDR *xdr,
  420. +    nfs42_copy_requirements *restrict requirements)
  421. +{
  422. +    if (!xdr_bool(xdr, &requirements->consecutive))
  423. +        return FALSE;
  424. +    if (!xdr_bool(xdr, &requirements->synchronous))
  425. +        return FALSE;
  426. +    return TRUE;
  427. +}
  428. +
  429. +bool_t decode_op_copy(
  430. +    XDR *xdr,
  431. +    nfs_resop4 *resop)
  432. +{
  433. +    nfs42_copy_res *res = (nfs42_copy_res *)resop->res;
  434. +
  435. +    if (unexpected_op(resop->op, OP_COPY))
  436. +        return FALSE;
  437. +
  438. +    if (!xdr_uint32_t(xdr, &res->status))
  439. +        return FALSE;
  440. +
  441. +    if (res->status == NFS4_OK) {
  442. +        if (!decode_write_response(xdr, &res->u.resok4.response))
  443. +            return FALSE;
  444. +        if (!decode_copy_requirements(xdr, &res->u.resok4.requirements))
  445. +            return FALSE;
  446. +    }
  447. +    else if (res->status == NFS4ERR_OFFLOAD_NO_REQS) {
  448. +        if (!decode_copy_requirements(xdr, &res->u.requirements))
  449. +            return FALSE;
  450. +    }
  451. +
  452. +    return TRUE;
  453. +}
  454. +
  455.  /*
  456.   * OP_DEALLOCATE
  457.   */
  458. diff --git a/daemon/recovery.c b/daemon/recovery.c
  459. index f0ee478..1106b89 100644
  460. --- a/daemon/recovery.c
  461. +++ b/daemon/recovery.c
  462. @@ -814,6 +814,9 @@ bool_t nfs41_recover_stateid(
  463.          nfs42_allocate_args *allocate =
  464.              (nfs42_allocate_args *)argop->arg;
  465.          stateid = allocate->stateid;
  466. +    } else if (argop->op == OP_COPY) {
  467. +        nfs42_copy_args *copy = (nfs42_copy_args *)argop->arg;
  468. +        stateid = copy->dst_stateid;
  469.      } else if (argop->op == OP_DEALLOCATE) {
  470.          nfs42_deallocate_args *deallocate =
  471.              (nfs42_deallocate_args *)argop->arg;
  472. --
  473. 2.45.1

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.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at