pastebin - collaborative debugging tool
rovema.kpaste.net RSS


msnfs41client: Patch for |FSCTL_SET_ZERO_DATA|, NFSv4.2 ALLOCATE+DEALLOCATE, workaround for Linux nfsd SEEK bug, tests+misc, 2025-02-14
Posted by Anonymous on Fri 14th Feb 2025 16:11
raw | new post

  1. From 0a8bb72df688fe68fab6e207d8afc5fd87f3309b Mon Sep 17 00:00:00 2001
  2. From: Roland Mainz <roland.mainz@nrubsig.org>
  3. Date: Fri, 14 Feb 2025 11:46:46 +0100
  4. Subject: [PATCH 1/3] daemon,tests: Add workaround for Linux nfsd NFSv4.2 SEEK
  5.  |NFS4ERR_NXIO| bug
  6.  
  7. Add a workaround for the Linux NFSv4.2 nfsd SEEK bug which returns
  8. |NFS4ERR_NXIO| if it cannot find a data block in a sparse file
  9. (i.e. a file which consists only of a single hole and no data),
  10.  
  11. The NFSv4.2 RFC says in
  12. https://datatracker.ietf.org/doc/html/rfc7862#section-15.11.3
  13. that "... If the server cannot find a corresponding sa_what,
  14. then the status will still be NFS4_OK, but sr_eof would be
  15. TRUE. ...", but the Linux nfsd SEEK does not behave that way.
  16.  
  17. Question is... which offset should a conforming NFSv4.2
  18. SEEK_DATA return if there is no data block (i.e. sparse
  19. file which only consists of one hole) ?
  20.  
  21. Also add tests "hole-only" sparse files and sparse files with
  22. a hole at the end.
  23.  
  24. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  25. ---
  26. daemon/daemon_debug.c                 |   4 +
  27.  daemon/fsctl.c                        |  28 +++++++
  28.  tests/sparsefiles/testsparsefile1.ksh | 112 +++++++++++++++++++++++---
  29.  3 files changed, 132 insertions(+), 12 deletions(-)
  30.  
  31. diff --git a/daemon/daemon_debug.c b/daemon/daemon_debug.c
  32. index fa0f90c..ab7f567 100644
  33. --- a/daemon/daemon_debug.c
  34. +++ b/daemon/daemon_debug.c
  35. @@ -1183,6 +1183,10 @@ void debug_list_sparsefile_holes(nfs41_open_state *state)
  36.       * https://datatracker.ietf.org/doc/html/rfc7862#section-15.11.3
  37.       * states "If the sa_offset is beyond the end of the file, then
  38.       * SEEK MUST return NFS4ERR_NXIO."
  39. +     *
  40. +     * Question is... which offset should a conforming NFSv4.2
  41. +     * SEEK_DATA return if there is no data block (i.e. sparse
  42. +     * file which only consists of one hole) ?
  43.       */
  44.  #define LINUX_NFSD_SEEK_NXIO_BUG_WORKAROUND 1
  45.  
  46. diff --git a/daemon/fsctl.c b/daemon/fsctl.c
  47. index 61b9dca..421909d 100644
  48. --- a/daemon/fsctl.c
  49. +++ b/daemon/fsctl.c
  50. @@ -97,6 +97,34 @@ int query_sparsefile_datasections(nfs41_open_state *state,
  51.              NFS4_CONTENT_DATA,
  52.              &data_seek_sr_eof,
  53.              &data_seek_sr_offset);
  54. +
  55. +        /*
  56. +         * 1. Note that Linux returns |NFS4ERR_NXIO| if it cannot find
  57. +         * a data block, but
  58. +         * https://datatracker.ietf.org/doc/html/rfc7862#section-15.11.3
  59. +         * says "... If the server cannot find a corresponding sa_what,
  60. +         * then the status will still be NFS4_OK, but sr_eof would be
  61. +         * TRUE. ..."
  62. +         * 2. NFSv4.2 spec bug:
  63. +         * https://datatracker.ietf.org/doc/html/rfc7862#section-11.2
  64. +         * section "SEEK" does not list |NFS4ERR_NXIO| as valid error
  65. +         * for SEEK, but
  66. +         * https://datatracker.ietf.org/doc/html/rfc7862#section-15.11.3
  67. +         * states "If the sa_offset is beyond the end of the file, then
  68. +         * SEEK MUST return NFS4ERR_NXIO."
  69. +         *
  70. +         * Question is... which offset should a conforming NFSv4.2
  71. +         * SEEK_DATA return if there is no data block (i.e. sparse
  72. +         * file which only consists of one hole) ?
  73. +         */
  74. +#define LINUX_NFSD_SEEK_NXIO_BUG_WORKAROUND 1
  75. +
  76. +#ifdef LINUX_NFSD_SEEK_NXIO_BUG_WORKAROUND
  77. +        if (data_seek_status == NFS4ERR_NXIO) {
  78. +            DPRINTF(QARLVL, ("SEEK_DATA failed with NFS4ERR_NXIO\n"));
  79. +            goto out;
  80. +        }
  81. +#endif
  82.          if (data_seek_status) {
  83.              status = nfs_to_windows_error(data_seek_status,
  84.                  ERROR_INVALID_PARAMETER);
  85. diff --git a/tests/sparsefiles/testsparsefile1.ksh b/tests/sparsefiles/testsparsefile1.ksh
  86. index ea43f74..2860165 100644
  87. --- a/tests/sparsefiles/testsparsefile1.ksh
  88. +++ b/tests/sparsefiles/testsparsefile1.ksh
  89. @@ -31,12 +31,69 @@
  90.  #
  91.  
  92.  
  93. +function test_sparse_holeonly
  94. +{
  95. +    set -o errexit
  96. +    set -o nounset
  97. +    #set -o xtrace
  98.  
  99. -PATH='/bin:/usr/bin'
  100. +    rm -f 'sparse_file_hole_only'
  101. +    dd if='/dev/null' of='sparse_file_hole_only' bs=1 count=1 seek=$((65536*1024))
  102.  
  103. -builtin rm
  104. +    ls -l 'sparse_file_hole_only'
  105. +    /cygdrive/c/Windows/system32/fsutil sparse queryrange 'sparse_file_hole_only'
  106. +
  107. +    integer fsutil_num_data_sections="$(/cygdrive/c/Windows/system32/fsutil sparse queryrange 'sparse_file_hole_only' | wc -l)"
  108. +
  109. +    #
  110. +    # test whether the file is OK
  111. +    #
  112. +    if (( fsutil_num_data_sections != 0 )) ; then
  113. +        printf "# TEST failed, found %d data sections, expceted %d\n" \
  114. +            fsutil_num_data_sections \
  115. +            0
  116. +        return 1
  117. +    fi
  118. +
  119. +    printf "\n#\n# TEST %q OK, found %d data sections\n#\n" \
  120. +        "$0" \
  121. +        fsutil_num_data_sections
  122. +
  123. +    return 0
  124. +}
  125. +
  126. +function test_normal_file
  127. +{
  128. +    set -o errexit
  129. +    set -o nounset
  130. +    #set -o xtrace
  131. +
  132. +    rm -f 'test_normal_file'
  133. +    dd if='/dev/zero' of='test_normal_file' bs=1024 count=1024
  134. +
  135. +    ls -l 'test_normal_file'
  136. +    /cygdrive/c/Windows/system32/fsutil sparse queryrange 'test_normal_file'
  137.  
  138. -function test_sparsefile1
  139. +    integer fsutil_num_data_sections="$(/cygdrive/c/Windows/system32/fsutil sparse queryrange 'test_normal_file' | wc -l)"
  140. +
  141. +    #
  142. +    # test whether the file is OK
  143. +    #
  144. +    if (( fsutil_num_data_sections != 1 )) ; then
  145. +        printf "# TEST failed, found %d data sections, expceted %d\n" \
  146. +            fsutil_num_data_sections \
  147. +            0
  148. +        return 1
  149. +    fi
  150. +
  151. +    printf "\n#\n# TEST %q OK, found %d data sections\n#\n" \
  152. +        "$0" \
  153. +        fsutil_num_data_sections
  154. +
  155. +    return 0
  156. +}
  157. +
  158. +function test_multihole_sparsefile1
  159.  {
  160.      set -o errexit
  161.      set -o nounset
  162. @@ -47,6 +104,7 @@ function test_sparsefile1
  163.      integer c.fsblocksize=$1
  164.      integer c.start_data_section=$2
  165.      integer c.end_data_section=$3
  166. +    typeset c.holeatend=$4
  167.  
  168.      integer i
  169.      compound -a c.filecontent
  170. @@ -61,7 +119,6 @@ function test_sparsefile1
  171.          )
  172.      done
  173.  
  174. -
  175.      #
  176.      # generate sparse file
  177.      #
  178. @@ -72,13 +129,28 @@ function test_sparsefile1
  179.          dd of='mysparsefile' bs=1 conv=notrunc seek=${c.filecontent[$i].pos} status=none <<<"${c.filecontent[$i].data}"
  180.      done
  181.  
  182. +    # if we want a hole at the end, make a hole so the file itself is 8GB large
  183. +    if ${c.holeatend} ; then
  184. +        integer new_filesize=8*1024*1024*1024
  185. +        integer stat_filsize
  186. +
  187. +        truncate -s ${new_filesize} 'mysparsefile'
  188. +
  189. +        stat_filsize=$(stat --printf '%s\n' 'mysparsefile')
  190. +
  191. +        if (( new_filesize != stat_filsize )) ; then
  192. +            printf 'Filesize after extening via truncate -s %d, expected %d\n' \
  193. +                stat_filsize new_filesize
  194. +            return 1
  195. +        fi
  196. +    fi
  197.  
  198.      #
  199.      # print results
  200.      #
  201.      printf '#\n# Results:\n#\n'
  202.  
  203. -    ls -l mysparsefile
  204. +    ls -l 'mysparsefile'
  205.  
  206.      /cygdrive/c/Windows/system32/fsutil sparse queryrange 'mysparsefile'
  207.  
  208. @@ -95,7 +167,8 @@ function test_sparsefile1
  209.          return 1
  210.      fi
  211.  
  212. -    printf "\n#\n# TEST OK, found %d data sections\n#\n" \
  213. +    printf "\n#\n# TEST %q OK, found %d data sections\n#\n" \
  214. +        "$0" \
  215.          fsutil_num_data_sections
  216.      return 0
  217.  }
  218. @@ -105,14 +178,29 @@ function test_sparsefile1
  219.  # main
  220.  #
  221.  set -o errexit
  222. -test_sparsefile1 1024 0 4
  223. -test_sparsefile1 1024 1 4
  224. -test_sparsefile1 1024 0 32
  225. -test_sparsefile1 1024 2 32
  226. +
  227. +PATH='/bin:/usr/bin'
  228. +
  229. +builtin basename
  230. +builtin rm
  231. +builtin wc
  232. +
  233. +test_sparse_holeonly
  234. +test_normal_file
  235. +
  236. +test_multihole_sparsefile1 1024 0 4  false
  237. +test_multihole_sparsefile1 1024 1 4  false
  238. +test_multihole_sparsefile1 1024 0 32 false
  239. +test_multihole_sparsefile1 1024 2 32 false
  240. +
  241. +test_multihole_sparsefile1 1024 0 4  true
  242. +test_multihole_sparsefile1 1024 1 4  true
  243.  
  244.  # 512 does not work, as Win10 fsutil can only handle 64 data sections
  245. -# test_sparsefile1 1024 2 512
  246. +# test_multihole_sparsefile1 1024 2 512 false
  247. +
  248. +printf '#\n# done\n#\n\n'
  249.  
  250. -printf '%s: All tests OK\n' "$0"
  251. +printf '%s: All tests OK\n' "$(basename $0)"
  252.  exit 0
  253.  # EOF.
  254. --
  255. 2.45.1
  256.  
  257. From 68d0a7ab17dcafc5a9e657d3ea3cf4a23eeb50c5 Mon Sep 17 00:00:00 2001
  258. From: Roland Mainz <roland.mainz@nrubsig.org>
  259. Date: Fri, 14 Feb 2025 13:41:43 +0100
  260. Subject: [PATCH 2/3] RFE: Implement NFSv4.2 ALLOCATE+DEALLOCATE support
  261.  
  262. Implement NFSv4.2 ALLOCATE+DEALLOCATE support for sparse file
  263. support.
  264.  
  265. Reported-by: Lionel Cons <lionelcons1972@gmail.com>
  266. Reported-by: Cedric Blancher <cedric.blancher@gmail.com>
  267. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  268. ---
  269. daemon/nfs41_ops.h |  38 +++++++++++
  270.  daemon/nfs41_xdr.c |   4 +-
  271.  daemon/nfs41_xdr.h |   4 ++
  272.  daemon/nfs42_ops.c | 158 +++++++++++++++++++++++++++++++++++++++++++++
  273.  daemon/nfs42_xdr.c |  75 +++++++++++++++++++++
  274.  daemon/readwrite.c |  55 +++++++++++++++-
  275.  daemon/recovery.c  |   8 +++
  276.  7 files changed, 338 insertions(+), 4 deletions(-)
  277.  
  278. diff --git a/daemon/nfs41_ops.h b/daemon/nfs41_ops.h
  279. index f9bf445..bdb07a9 100644
  280. --- a/daemon/nfs41_ops.h
  281. +++ b/daemon/nfs41_ops.h
  282. @@ -788,6 +788,28 @@ typedef struct __nfs42_read_plus_res {
  283.      nfs42_read_plus_res_ok  resok4;
  284.  } nfs42_read_plus_res;
  285.  
  286. +/* OP_ALLOCATE */
  287. +typedef struct __nfs42_allocate_args {
  288. +    stateid_arg     *stateid;
  289. +    uint64_t        offset;
  290. +    uint64_t        length;
  291. +} nfs42_allocate_args;
  292. +
  293. +typedef struct __nfs42_allocate_res {
  294. +    uint32_t    status;
  295. +} nfs42_allocate_res;
  296. +
  297. +/* OP_DEALLOCATE */
  298. +typedef struct __nfs42_deallocate_args {
  299. +    stateid_arg     *stateid;
  300. +    uint64_t        offset;
  301. +    uint64_t        length;
  302. +} nfs42_deallocate_args;
  303. +
  304. +typedef struct __nfs42_deallocate_res {
  305. +    uint32_t    status;
  306. +} nfs42_deallocate_res;
  307. +
  308.  #if 1
  309.  /* OP_SEEK */
  310.  typedef struct __nfs42_seek_args {
  311. @@ -1203,6 +1225,22 @@ int nfs42_read_plus(
  312.      OUT uint32_t *data_len_out,
  313.      OUT bool_t *eof_out);
  314.  
  315. +int nfs42_allocate(
  316. +    IN nfs41_session *session,
  317. +    IN nfs41_path_fh *file,
  318. +    IN stateid_arg *stateid,
  319. +    IN uint64_t offset,
  320. +    IN uint64_t length,
  321. +    OUT nfs41_file_info *cinfo);
  322. +
  323. +int nfs42_deallocate(
  324. +    IN nfs41_session *session,
  325. +    IN nfs41_path_fh *file,
  326. +    IN stateid_arg *stateid,
  327. +    IN uint64_t offset,
  328. +    IN uint64_t length,
  329. +    OUT nfs41_file_info *cinfo);
  330. +
  331.  int nfs42_seek(
  332.      IN nfs41_session *session,
  333.      IN nfs41_path_fh *file,
  334. diff --git a/daemon/nfs41_xdr.c b/daemon/nfs41_xdr.c
  335. index afc4fbb..84c5f7f 100644
  336. --- a/daemon/nfs41_xdr.c
  337. +++ b/daemon/nfs41_xdr.c
  338. @@ -3654,10 +3654,10 @@ static const op_table_entry g_op_table[] = {
  339.      { encode_op_reclaim_complete, decode_op_reclaim_complete }, /* OP_RECLAIM_COMPLETE = 58 */
  340.  
  341.      /* new operations for NFSv4.2 */
  342. -    { NULL, NULL }, /* OP_ALLOCATE = 59, */
  343. +    { encode_op_allocate, decode_op_allocate }, /* OP_ALLOCATE = 59, */
  344.      { NULL, NULL }, /* OP_COPY = 60, */
  345.      { NULL, NULL }, /* OP_COPY_NOTIFY = 61, */
  346. -    { NULL, NULL }, /* OP_DEALLOCATE = 62, */
  347. +    { encode_op_deallocate, decode_op_deallocate }, /* OP_DEALLOCATE = 62, */
  348.      { NULL, NULL }, /* OP_IO_ADVISE = 63, */
  349.      { NULL, NULL }, /* OP_LAYOUTERROR = 64, */
  350.      { NULL, NULL }, /* OP_LAYOUTSTATS = 65, */
  351. diff --git a/daemon/nfs41_xdr.h b/daemon/nfs41_xdr.h
  352. index d6f217f..d89548c 100644
  353. --- a/daemon/nfs41_xdr.h
  354. +++ b/daemon/nfs41_xdr.h
  355. @@ -34,6 +34,10 @@ void nfsacl41_free(nfsacl41 *acl);
  356.  bool_t xdr_stateid4(XDR *xdr, stateid4 *si);
  357.  
  358.  /* NFSv4.2 ops */
  359. +bool_t encode_op_allocate(XDR *xdr, nfs_argop4 *argop);
  360. +bool_t decode_op_allocate(XDR *xdr, nfs_resop4 *resop);
  361. +bool_t encode_op_deallocate(XDR *xdr, nfs_argop4 *argop);
  362. +bool_t decode_op_deallocate(XDR *xdr, nfs_resop4 *resop);
  363.  bool_t encode_op_read_plus(XDR *xdr, nfs_argop4 *argop);
  364.  bool_t decode_op_read_plus(XDR *xdr, nfs_resop4 *resop);
  365.  bool_t encode_op_seek(XDR *xdr, nfs_argop4 *argop);
  366. diff --git a/daemon/nfs42_ops.c b/daemon/nfs42_ops.c
  367. index 39e946d..f416986 100644
  368. --- a/daemon/nfs42_ops.c
  369. +++ b/daemon/nfs42_ops.c
  370. @@ -34,6 +34,164 @@
  371.  #include "daemon_debug.h"
  372.  #include "util.h"
  373.  
  374. +int nfs42_allocate(
  375. +    IN nfs41_session *session,
  376. +    IN nfs41_path_fh *file,
  377. +    IN stateid_arg *stateid,
  378. +    IN uint64_t offset,
  379. +    IN uint64_t length,
  380. +    OUT nfs41_file_info *cinfo)
  381. +{
  382. +    int status;
  383. +    nfs41_compound compound;
  384. +    nfs_argop4 argops[4];
  385. +    nfs_resop4 resops[4];
  386. +    nfs41_sequence_args sequence_args;
  387. +    nfs41_sequence_res sequence_res;
  388. +    nfs41_putfh_args putfh_args;
  389. +    nfs41_putfh_res putfh_res;
  390. +    nfs42_allocate_args allocate_args;
  391. +    nfs42_allocate_res allocate_res;
  392. +    nfs41_getattr_args getattr_args;
  393. +    nfs41_getattr_res getattr_res = {0};
  394. +    bitmap4 attr_request;
  395. +    nfs41_file_info info, *pinfo;
  396. +
  397. +    nfs41_superblock_getattr_mask(file->fh.superblock, &attr_request);
  398. +
  399. +    /* FIXME: What about DS in pNFS case ? */
  400. +    compound_init(&compound, session->client->root->nfsminorvers,
  401. +        argops, resops, "allocate");
  402. +
  403. +    compound_add_op(&compound, OP_SEQUENCE,
  404. +        &sequence_args, &sequence_res);
  405. +    nfs41_session_sequence(&sequence_args, session, 0);
  406. +
  407. +    compound_add_op(&compound, OP_PUTFH, &putfh_args, &putfh_res);
  408. +    putfh_args.file = file;
  409. +    putfh_args.in_recovery = 0;
  410. +
  411. +    compound_add_op(&compound, OP_ALLOCATE,
  412. +        &allocate_args, &allocate_res);
  413. +    allocate_args.stateid = stateid;
  414. +    allocate_args.offset = offset;
  415. +    allocate_args.length = length;
  416. +
  417. +    if (cinfo) {
  418. +        pinfo = cinfo;
  419. +    }
  420. +    else {
  421. +        (void)memset(&info, 0, sizeof(info));
  422. +        pinfo = &info;
  423. +    }
  424. +
  425. +    /*
  426. +     * NFSv4.2 ALLOCATE is some kind of "write" operation and
  427. +     * affects the number of physical bytes allocated, so we have
  428. +     * to do a GETATTR after ALLOCATE to get updates for our cache
  429. +     */
  430. +    compound_add_op(&compound, OP_GETATTR, &getattr_args, &getattr_res);
  431. +    getattr_args.attr_request = &attr_request;
  432. +    getattr_res.obj_attributes.attr_vals_len = NFS4_OPAQUE_LIMIT;
  433. +    getattr_res.info = pinfo;
  434. +
  435. +    status = compound_encode_send_decode(session, &compound, TRUE);
  436. +    if (status)
  437. +        goto out;
  438. +
  439. +    if (compound_error(status = compound.res.status))
  440. +        goto out;
  441. +
  442. +    /* update the attribute cache */
  443. +    bitmap4_cpy(&pinfo->attrmask, &getattr_res.obj_attributes.attrmask);
  444. +    nfs41_attr_cache_update(session_name_cache(session),
  445. +        file->fh.fileid, pinfo);
  446. +
  447. +    nfs41_superblock_space_changed(file->fh.superblock);
  448. +
  449. +out:
  450. +    return status;
  451. +}
  452. +
  453. +int nfs42_deallocate(
  454. +    IN nfs41_session *session,
  455. +    IN nfs41_path_fh *file,
  456. +    IN stateid_arg *stateid,
  457. +    IN uint64_t offset,
  458. +    IN uint64_t length,
  459. +    OUT nfs41_file_info *cinfo)
  460. +{
  461. +    int status;
  462. +    nfs41_compound compound;
  463. +    nfs_argop4 argops[4];
  464. +    nfs_resop4 resops[4];
  465. +    nfs41_sequence_args sequence_args;
  466. +    nfs41_sequence_res sequence_res;
  467. +    nfs41_putfh_args putfh_args;
  468. +    nfs41_putfh_res putfh_res;
  469. +    nfs42_deallocate_args deallocate_args;
  470. +    nfs42_deallocate_res deallocate_res;
  471. +    nfs41_getattr_args getattr_args;
  472. +    nfs41_getattr_res getattr_res = {0};
  473. +    bitmap4 attr_request;
  474. +    nfs41_file_info info, *pinfo;
  475. +
  476. +    nfs41_superblock_getattr_mask(file->fh.superblock, &attr_request);
  477. +
  478. +    /* FIXME: What about DS in pNFS case ? */
  479. +    compound_init(&compound, session->client->root->nfsminorvers,
  480. +        argops, resops, "deallocate");
  481. +
  482. +    compound_add_op(&compound, OP_SEQUENCE,
  483. +        &sequence_args, &sequence_res);
  484. +    nfs41_session_sequence(&sequence_args, session, 0);
  485. +
  486. +    compound_add_op(&compound, OP_PUTFH, &putfh_args, &putfh_res);
  487. +    putfh_args.file = file;
  488. +    putfh_args.in_recovery = 0;
  489. +
  490. +    compound_add_op(&compound, OP_DEALLOCATE,
  491. +        &deallocate_args, &deallocate_res);
  492. +    deallocate_args.stateid = stateid;
  493. +    deallocate_args.offset = offset;
  494. +    deallocate_args.length = length;
  495. +
  496. +    if (cinfo) {
  497. +        pinfo = cinfo;
  498. +    }
  499. +    else {
  500. +        (void)memset(&info, 0, sizeof(info));
  501. +        pinfo = &info;
  502. +    }
  503. +
  504. +    /*
  505. +     * NFSv4.2 DEALLOCATE is some kind of "write" operation and
  506. +     * affects the number of physical bytes allocated, so we have
  507. +     * to do a GETATTR after DEALLOCATE to get updates for our cache
  508. +     */
  509. +    compound_add_op(&compound, OP_GETATTR, &getattr_args, &getattr_res);
  510. +    getattr_args.attr_request = &attr_request;
  511. +    getattr_res.obj_attributes.attr_vals_len = NFS4_OPAQUE_LIMIT;
  512. +    getattr_res.info = pinfo;
  513. +
  514. +    status = compound_encode_send_decode(session, &compound, TRUE);
  515. +    if (status)
  516. +        goto out;
  517. +
  518. +    if (compound_error(status = compound.res.status))
  519. +        goto out;
  520. +
  521. +    /* update the attribute cache */
  522. +    bitmap4_cpy(&pinfo->attrmask, &getattr_res.obj_attributes.attrmask);
  523. +    nfs41_attr_cache_update(session_name_cache(session),
  524. +        file->fh.fileid, pinfo);
  525. +
  526. +    nfs41_superblock_space_changed(file->fh.superblock);
  527. +
  528. +out:
  529. +    return status;
  530. +}
  531. +
  532.  int nfs42_read_plus(
  533.      IN nfs41_session *session,
  534.      IN nfs41_path_fh *file,
  535. diff --git a/daemon/nfs42_xdr.c b/daemon/nfs42_xdr.c
  536. index 9120461..210adb1 100644
  537. --- a/daemon/nfs42_xdr.c
  538. +++ b/daemon/nfs42_xdr.c
  539. @@ -40,6 +40,81 @@ static __inline int unexpected_op(uint32_t op, uint32_t expected)
  540.      return 1;
  541.  }
  542.  
  543. +/*
  544. + * OP_ALLOCATE
  545. + */
  546. +bool_t encode_op_allocate(
  547. +    XDR *xdr,
  548. +    nfs_argop4 *argop)
  549. +{
  550. +    nfs42_allocate_args *args = (nfs42_allocate_args *)argop->arg;
  551. +
  552. +    if (unexpected_op(argop->op, OP_ALLOCATE))
  553. +        return FALSE;
  554. +
  555. +    if (!xdr_stateid4(xdr, &args->stateid->stateid))
  556. +        return FALSE;
  557. +
  558. +    if (!xdr_u_hyper(xdr, &args->offset))
  559. +        return FALSE;
  560. +
  561. +    return xdr_u_hyper(xdr, &args->length);
  562. +}
  563. +
  564. +
  565. +bool_t decode_op_allocate(
  566. +    XDR *xdr,
  567. +    nfs_resop4 *resop)
  568. +{
  569. +    nfs42_allocate_res *res = (nfs42_allocate_res *)resop->res;
  570. +
  571. +    if (unexpected_op(resop->op, OP_ALLOCATE))
  572. +        return FALSE;
  573. +
  574. +    if (!xdr_u_int32_t(xdr, &res->status))
  575. +        return FALSE;
  576. +
  577. +    return TRUE;
  578. +}
  579. +
  580. +/*
  581. + * OP_DEALLOCATE
  582. + */
  583. +bool_t encode_op_deallocate(
  584. +    XDR *xdr,
  585. +    nfs_argop4 *argop)
  586. +{
  587. +    nfs42_deallocate_args *args = (nfs42_deallocate_args *)argop->arg;
  588. +
  589. +    if (unexpected_op(argop->op, OP_DEALLOCATE))
  590. +        return FALSE;
  591. +
  592. +    if (!xdr_stateid4(xdr, &args->stateid->stateid))
  593. +        return FALSE;
  594. +
  595. +    if (!xdr_u_hyper(xdr, &args->offset))
  596. +        return FALSE;
  597. +
  598. +    return xdr_u_hyper(xdr, &args->length);
  599. +}
  600. +
  601. +
  602. +bool_t decode_op_deallocate(
  603. +    XDR *xdr,
  604. +    nfs_resop4 *resop)
  605. +{
  606. +    nfs42_deallocate_res *res = (nfs42_deallocate_res *)resop->res;
  607. +
  608. +    if (unexpected_op(resop->op, OP_DEALLOCATE))
  609. +        return FALSE;
  610. +
  611. +    if (!xdr_u_int32_t(xdr, &res->status))
  612. +        return FALSE;
  613. +
  614. +    return TRUE;
  615. +}
  616. +
  617. +
  618.  /*
  619.   * OP_READ_PLUS
  620.   */
  621. diff --git a/daemon/readwrite.c b/daemon/readwrite.c
  622. index b285490..bce3e51 100644
  623. --- a/daemon/readwrite.c
  624. +++ b/daemon/readwrite.c
  625. @@ -189,8 +189,9 @@ static int write_to_mds(
  626.      IN nfs41_upcall *upcall,
  627.      IN stateid_arg *stateid)
  628.  {
  629. -    nfs41_session *session = upcall->state_ref->session;
  630. -    nfs41_path_fh *file = &upcall->state_ref->file;
  631. +    nfs41_open_state *state = upcall->state_ref;
  632. +    nfs41_session *session = state->session;
  633. +    nfs41_path_fh *file = &state->file;
  634.      readwrite_upcall_args *args = &upcall->args.rw;
  635.      nfs41_write_verf verf;
  636.      enum stable_how4 stable, committed;
  637. @@ -204,6 +205,56 @@ static int write_to_mds(
  638.  
  639.      (void)memset(&info, 0, sizeof(info));
  640.  
  641. +
  642. +#ifdef TEST_OP_ALLOCAE_OP_DEALLOCATE
  643. +    /*
  644. +     * Test code for OP_ALLOCATE and OP_DEALLOCATE, do not use except for
  645. +     * testing!
  646. +     */
  647. +    size_t data_i;
  648. +
  649. +    /* Test whether the data block consists is a block of zero bytes */
  650. +    for (data_i = 0 ; data_i < args->len ; data_i++) {
  651. +        if (((char *)args->buffer)[data_i] != '\0')
  652. +            break;
  653. +    }
  654. +
  655. +    if (data_i == args->len) {
  656. +        DPRINTF(0, ("write_to_mds(state->path.path='%s'): "
  657. +            "Using DEALLOCATE+ALLOCATE for zero block\n",
  658. +            state->path.path));
  659. +
  660. +        status = nfs42_deallocate(session, file, stateid,
  661. +            args->offset, args->len,
  662. +            &info);
  663. +        if (status) {
  664. +            DPRINTF(0, ("write_to_mds(state->path.path='%s'): "
  665. +                "DEALLOCATE failed with status=0x%x\n",
  666. +                state->path.path,
  667. +                status));
  668. +        }
  669. +        else {
  670. +            status = nfs42_allocate(session, file, stateid,
  671. +                args->offset, args->len,
  672. +                &info);
  673. +            if (status) {
  674. +                DPRINTF(0, ("write_to_mds(state->path.path='%s'): "
  675. +                    "ALLOCATE failed with status=0x%x\n",
  676. +                    state->path.path,
  677. +                    status));
  678. +            }
  679. +        }
  680. +
  681. +        if (!status) {
  682. +            /* Update ctime on success */
  683. +            args->ctime = info.change;
  684. +        }
  685. +
  686. +        len = args->len;
  687. +        goto out;
  688. +    }
  689. +#endif /* TEST_OP_ALLOCAE_OP_DEALLOCATE */
  690. +
  691.  retry_write:
  692.      p = args->buffer;
  693.      to_send = args->len;
  694. diff --git a/daemon/recovery.c b/daemon/recovery.c
  695. index 42ca27b..66bf25e 100644
  696. --- a/daemon/recovery.c
  697. +++ b/daemon/recovery.c
  698. @@ -809,6 +809,14 @@ bool_t nfs41_recover_stateid(
  699.      } else if (argop->op == OP_READ_PLUS) {
  700.          nfs42_read_plus_args *read_plus = (nfs42_read_plus_args *)argop->arg;
  701.          stateid = read_plus->stateid;
  702. +    } else if (argop->op == OP_ALLOCATE) {
  703. +        nfs42_allocate_args *allocate =
  704. +            (nfs42_allocate_args *)argop->arg;
  705. +        stateid = allocate->stateid;
  706. +    } else if (argop->op == OP_DEALLOCATE) {
  707. +        nfs42_deallocate_args *deallocate =
  708. +            (nfs42_deallocate_args *)argop->arg;
  709. +        stateid = deallocate->stateid;
  710.      } else if (argop->op == OP_SEEK) {
  711.          nfs42_seek_args *seek = (nfs42_seek_args *)argop->arg;
  712.          stateid = seek->stateid;
  713. --
  714. 2.45.1
  715.  
  716. From 0c8b3f29d53061b6db4704b22a098d89041d35e5 Mon Sep 17 00:00:00 2001
  717. From: Roland Mainz <roland.mainz@nrubsig.org>
  718. Date: Fri, 14 Feb 2025 16:59:13 +0100
  719. Subject: [PATCH 3/3] daemon,include,sys,tests: Implement |FSCTL_SET_ZERO_DATA|
  720.  via NFSv4.2 DEALLOCATE
  721.  
  722. Implement |FSCTL_SET_ZERO_DATA| via NFSv4.2 DEALLOCATE, which enables utilities
  723. like Cygwin /usr/bin/fallocate to punch holes into files.
  724.  
  725. Reported-by: Lionel Cons <lionelcons1972@gmail.com>
  726. Reported-by: Cedric Blancher <cedric.blancher@gmail.com>
  727. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  728. ---
  729. daemon/daemon_debug.c                 |   1 +
  730.  daemon/fsctl.c                        | 117 +++++++++++++++++++++
  731.  daemon/upcall.c                       |   2 +
  732.  daemon/upcall.h                       |   5 +
  733.  include/from_kernel.h                 |  12 +++
  734.  include/nfs41_driver.h                |   1 +
  735.  sys/nfs41sys_debug.c                  |   1 +
  736.  sys/nfs41sys_driver.h                 |  11 ++
  737.  sys/nfs41sys_fsctl.c                  | 141 ++++++++++++++++++++++++++
  738.  sys/nfs41sys_updowncall.c             |   7 ++
  739.  tests/sparsefiles/testsparsefile1.ksh |  40 ++++++++
  740.  11 files changed, 338 insertions(+)
  741.  
  742. diff --git a/daemon/daemon_debug.c b/daemon/daemon_debug.c
  743. index ab7f567..4f3a9f2 100644
  744. --- a/daemon/daemon_debug.c
  745. +++ b/daemon/daemon_debug.c
  746. @@ -467,6 +467,7 @@ const char* opcode2string(nfs41_opcodes opcode)
  747.          NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_ACL_QUERY)
  748.          NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_ACL_SET)
  749.          NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES)
  750. +        NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_FSCTL_SET_ZERO_DATA)
  751.          NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_INVALID_OPCODE1)
  752.          default: break;
  753.      }
  754. diff --git a/daemon/fsctl.c b/daemon/fsctl.c
  755. index 421909d..dfff5a4 100644
  756. --- a/daemon/fsctl.c
  757. +++ b/daemon/fsctl.c
  758. @@ -28,6 +28,7 @@
  759.  #include "util.h"
  760.  
  761.  #define QARLVL 2 /* dprintf level for "query allocated ranges" logging */
  762. +#define SZDLVL 2 /* dprintf level for "set zero data" logging */
  763.  
  764.  static int parse_queryallocatedranges(unsigned char *buffer,
  765.      uint32_t length, nfs41_upcall *upcall)
  766. @@ -254,3 +255,119 @@ const nfs41_upcall_op nfs41_op_queryallocatedranges = {
  767.      .marshall = marshall_queryallocatedranges,
  768.      .arg_size = sizeof(queryallocatedranges_upcall_args)
  769.  };
  770. +
  771. +static int parse_setzerodata(unsigned char *buffer,
  772. +    uint32_t length, nfs41_upcall *upcall)
  773. +{
  774. +    int status;
  775. +    setzerodata_upcall_args *args = &upcall->args.setzerodata;
  776. +
  777. +    status = safe_read(&buffer, &length, &args->setzerodata,
  778. +        sizeof(args->setzerodata));
  779. +    if (status) goto out;
  780. +
  781. +    DPRINTF(SZDLVL, ("parse_setzerodata: "
  782. +        "parsing '%s' setzerodata=(FileOffset=%lld BeyondFinalZero=%lld)\n",
  783. +        opcode2string(upcall->opcode),
  784. +        (long long)args->setzerodata.FileOffset.QuadPart,
  785. +        (long long)args->setzerodata.BeyondFinalZero.QuadPart));
  786. +out:
  787. +    return status;
  788. +}
  789. +
  790. +
  791. +static
  792. +int handle_setzerodata(void *daemon_context,
  793. +    nfs41_upcall *upcall)
  794. +{
  795. +    int status = ERROR_INVALID_PARAMETER;
  796. +    setzerodata_upcall_args *args = &upcall->args.setzerodata;
  797. +    nfs41_open_state *state = upcall->state_ref;
  798. +    nfs41_session *session = state->session;
  799. +    nfs41_path_fh *file = &state->file;
  800. +    nfs41_file_info info;
  801. +    int64_t offset_start; /* signed! */
  802. +    int64_t offset_end; /* signed! */
  803. +    int64_t len; /* signed! */
  804. +    stateid_arg stateid;
  805. +
  806. +    (void)memset(&info, 0, sizeof(info));
  807. +
  808. +    offset_start = args->setzerodata.FileOffset.QuadPart;
  809. +    offset_end = args->setzerodata.BeyondFinalZero.QuadPart;
  810. +    len = offset_end - offset_start;
  811. +
  812. +    DPRINTF(SZDLVL,
  813. +        ("--> handle_setzerodata("
  814. +            "state->path.path='%s', "
  815. +            "offset_start=%lld, "
  816. +            "offset_end=%lld, "
  817. +            "len=%lld)\n",
  818. +            state->path.path,
  819. +            offset_start,
  820. +            offset_end,
  821. +            len));
  822. +
  823. +    /* NFS DEALLOCATE requires NFSv4.2 */
  824. +    if (state->session->client->root->nfsminorvers < 2) {
  825. +        status = ERROR_NOT_SUPPORTED;
  826. +        goto out;
  827. +    }
  828. +
  829. +    if (len < 0) {
  830. +        status = ERROR_INVALID_PARAMETER;
  831. +        DPRINTF(SZDLVL, ("handle_setzerodata: invalid len\b"));
  832. +        goto out;
  833. +    }
  834. +
  835. +    if (len == 0) {
  836. +        status = NO_ERROR;
  837. +        DPRINTF(SZDLVL, ("handle_setzerodata: len == 0, NOP\n"));
  838. +        goto out;
  839. +    }
  840. +
  841. +    nfs41_open_stateid_arg(state, &stateid);
  842. +
  843. +    status = nfs42_deallocate(session, file, &stateid,
  844. +         offset_start, len, &info);
  845. +     if (status) {
  846. +        DPRINTF(SZDLVL, ("handle_setzerodata(state->path.path='%s'): "
  847. +            "DEALLOCATE failed with status=0x%x\n",
  848. +            state->path.path,
  849. +            status));
  850. +        goto out;
  851. +     }
  852. +
  853. +    /* Update ctime on success */
  854. +    EASSERT((info.attrmask.count > 0) &&
  855. +        (info.attrmask.arr[0] & FATTR4_WORD0_CHANGE));
  856. +    args->ctime = info.change;
  857. +
  858. +    DPRINTF(SZDLVL,
  859. +        ("handle_setzerodata(state->path.path='%s'): args->ctime=%llu\n",
  860. +        state->path.path,
  861. +        args->ctime));
  862. +
  863. +out:
  864. +    DPRINTF(SZDLVL,
  865. +        ("<-- handle_setzerodata(), status=0x%lx\n",
  866. +        status));
  867. +
  868. +    return status;
  869. +}
  870. +
  871. +static int marshall_setzerodata(unsigned char *buffer,
  872. +    uint32_t *length, nfs41_upcall *upcall)
  873. +{
  874. +    setzerodata_upcall_args *args = &upcall->args.setzerodata;
  875. +    int status;
  876. +    status = safe_write(&buffer, length, &args->ctime, sizeof(args->ctime));
  877. +    return status;
  878. +}
  879. +
  880. +const nfs41_upcall_op nfs41_op_setzerodata = {
  881. +    .parse = parse_setzerodata,
  882. +    .handle = handle_setzerodata,
  883. +    .marshall = marshall_setzerodata,
  884. +    .arg_size = sizeof(setzerodata_upcall_args)
  885. +};
  886. diff --git a/daemon/upcall.c b/daemon/upcall.c
  887. index 3f741e6..8643fb5 100644
  888. --- a/daemon/upcall.c
  889. +++ b/daemon/upcall.c
  890. @@ -50,6 +50,7 @@ extern const nfs41_upcall_op nfs41_op_volume;
  891.  extern const nfs41_upcall_op nfs41_op_getacl;
  892.  extern const nfs41_upcall_op nfs41_op_setacl;
  893.  extern const nfs41_upcall_op nfs41_op_queryallocatedranges;
  894. +extern const nfs41_upcall_op nfs41_op_setzerodata;
  895.  
  896.  /* |_nfs41_opcodes| and |g_upcall_op_table| must be in sync! */
  897.  static const nfs41_upcall_op *g_upcall_op_table[] = {
  898. @@ -74,6 +75,7 @@ static const nfs41_upcall_op *g_upcall_op_table[] = {
  899.      &nfs41_op_getacl,
  900.      &nfs41_op_setacl,
  901.      &nfs41_op_queryallocatedranges,
  902. +    &nfs41_op_setzerodata,
  903.      NULL,
  904.      NULL
  905.  };
  906. diff --git a/daemon/upcall.h b/daemon/upcall.h
  907. index 290aabd..94f830c 100644
  908. --- a/daemon/upcall.h
  909. +++ b/daemon/upcall.h
  910. @@ -197,6 +197,10 @@ typedef struct __queryallocatedranges_upcall_args {
  911.      ULONG                           returned_size;
  912.  } queryallocatedranges_upcall_args;
  913.  
  914. +typedef struct __setzerodata_upcall_args {
  915. +    FILE_ZERO_DATA_INFORMATION  setzerodata;
  916. +    ULONGLONG                   ctime;
  917. +} setzerodata_upcall_args;
  918.  
  919.  typedef union __upcall_args {
  920.      mount_upcall_args       mount;
  921. @@ -215,6 +219,7 @@ typedef union __upcall_args {
  922.      getacl_upcall_args      getacl;
  923.      setacl_upcall_args      setacl;
  924.      queryallocatedranges_upcall_args queryallocatedranges;
  925. +    setzerodata_upcall_args setzerodata;
  926.  } upcall_args;
  927.  
  928.  typedef enum _nfs41_opcodes nfs41_opcodes;
  929. diff --git a/include/from_kernel.h b/include/from_kernel.h
  930. index 8b9ddc0..7a9c47a 100644
  931. --- a/include/from_kernel.h
  932. +++ b/include/from_kernel.h
  933. @@ -452,4 +452,16 @@ typedef struct _FILE_ALLOCATED_RANGE_BUFFER {
  934.  } FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER;
  935.  #endif /* _WINIOCTL_ */
  936.  
  937. +/*
  938. + * |FILE_ZERO_DATA_INFORMATION| - test for
  939. + * /usr/i686-w64-mingw32/sys-root/mingw/include/winioctl.h header
  940. + * to avoid type redefinition warnings
  941. + */
  942. +#ifndef _WINIOCTL_
  943. +typedef struct _FILE_ZERO_DATA_INFORMATION {
  944. +    LARGE_INTEGER FileOffset;       /* absolute offset */
  945. +    LARGE_INTEGER BeyondFinalZero;  /* absolute offset */
  946. +} FILE_ZERO_DATA_INFORMATION, *PFILE_ZERO_DATA_INFORMATION;
  947. +#endif /* !_WINIOCTL_ */
  948. +
  949.  #endif
  950. diff --git a/include/nfs41_driver.h b/include/nfs41_driver.h
  951. index 403bf04..37b3141 100644
  952. --- a/include/nfs41_driver.h
  953. +++ b/include/nfs41_driver.h
  954. @@ -83,6 +83,7 @@ typedef enum _nfs41_opcodes {
  955.      NFS41_SYSOP_ACL_QUERY,
  956.      NFS41_SYSOP_ACL_SET,
  957.      NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES,
  958. +    NFS41_SYSOP_FSCTL_SET_ZERO_DATA,
  959.      NFS41_SYSOP_SHUTDOWN,
  960.      NFS41_SYSOP_INVALID_OPCODE1
  961.  } nfs41_opcodes;
  962. diff --git a/sys/nfs41sys_debug.c b/sys/nfs41sys_debug.c
  963. index dccd933..dd35f90 100644
  964. --- a/sys/nfs41sys_debug.c
  965. +++ b/sys/nfs41sys_debug.c
  966. @@ -678,6 +678,7 @@ const char *opcode2string(int opcode)
  967.      case NFS41_SYSOP_ACL_QUERY: return "NFS41_SYSOP_ACL_QUERY";
  968.      case NFS41_SYSOP_ACL_SET: return "NFS41_SYSOP_ACL_SET";
  969.      case NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES: return "NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES";
  970. +    case NFS41_SYSOP_FSCTL_SET_ZERO_DATA: return "NFS41_SYSOP_FSCTL_SET_ZERO_DATA";
  971.      default: return "UNKNOWN";
  972.      }
  973.  }
  974. diff --git a/sys/nfs41sys_driver.h b/sys/nfs41sys_driver.h
  975. index 7fccf69..f14d33d 100644
  976. --- a/sys/nfs41sys_driver.h
  977. +++ b/sys/nfs41sys_driver.h
  978. @@ -276,6 +276,9 @@ typedef struct _updowncall_entry {
  979.              PVOID Buffer;
  980.              LONGLONG returned_size;
  981.          } QueryAllocatedRanges;
  982. +        struct {
  983. +            FILE_ZERO_DATA_INFORMATION setzerodata;
  984. +        } SetZeroData;
  985.      } u;
  986.  
  987.  } nfs41_updowncall_entry;
  988. @@ -650,6 +653,14 @@ NTSTATUS marshal_nfs41_queryallocatedranges(
  989.  NTSTATUS unmarshal_nfs41_queryallocatedranges(
  990.      nfs41_updowncall_entry *cur,
  991.      unsigned char **buf);
  992. +NTSTATUS marshal_nfs41_setzerodata(
  993. +    nfs41_updowncall_entry *entry,
  994. +    unsigned char *buf,
  995. +    ULONG buf_len,
  996. +    ULONG *len);
  997. +NTSTATUS unmarshal_nfs41_setzerodata(
  998. +    nfs41_updowncall_entry *cur,
  999. +    unsigned char **buf);
  1000.  
  1001.  /* nfs41sys_ioctl.c */
  1002.  NTSTATUS nfs41_IoCtl(
  1003. diff --git a/sys/nfs41sys_fsctl.c b/sys/nfs41sys_fsctl.c
  1004. index 1799fe9..acacf7e 100644
  1005. --- a/sys/nfs41sys_fsctl.c
  1006. +++ b/sys/nfs41sys_fsctl.c
  1007. @@ -368,6 +368,144 @@ out:
  1008.      return status;
  1009.  }
  1010.  
  1011. +static
  1012. +NTSTATUS nfs41_SetZeroData(
  1013. +    IN OUT PRX_CONTEXT RxContext)
  1014. +{
  1015. +    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
  1016. +    nfs41_updowncall_entry *entry = NULL;
  1017. +    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
  1018. +    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
  1019. +        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
  1020. +    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
  1021. +        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
  1022. +    __notnull XXCTL_LOWIO_COMPONENT *FsCtl =
  1023. +        &RxContext->LowIoContext.ParamsFor.FsCtl;
  1024. +    __notnull PFILE_ZERO_DATA_INFORMATION setzerodatabuffer =
  1025. +        (PFILE_ZERO_DATA_INFORMATION)FsCtl->pInputBuffer;
  1026. +    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
  1027. +
  1028. +    DbgEn();
  1029. +
  1030. +    RxContext->IoStatusBlock.Information = 0;
  1031. +
  1032. +    if (FsCtl->InputBufferLength <
  1033. +        sizeof(FILE_ZERO_DATA_INFORMATION)) {
  1034. +        DbgP("nfs41_SetZeroData: "
  1035. +            "buffer to small\n");
  1036. +        status = STATUS_BUFFER_TOO_SMALL;
  1037. +        goto out;
  1038. +    }
  1039. +
  1040. +    DbgP("nfs41_SetZeroData: "
  1041. +        "setzerodatabuffer=(FileOffset=%lld,BeyondFinalZero=%lld)\n",
  1042. +        (long long)setzerodatabuffer->FileOffset.QuadPart,
  1043. +        (long long)setzerodatabuffer->BeyondFinalZero.QuadPart);
  1044. +
  1045. +    /*
  1046. +     * Disable caching because NFSv4.2 DEALLOCATE is basically a
  1047. +     * "write" operation. AFAIK we should flush the cache and wait
  1048. +     * for the kernel lazy writer (which |RxChangeBufferingState()|
  1049. +     * AFAIK does) before doing the DEALLOCATE, to avoid that we
  1050. +     * have outstanding writes in the kernel cache at the same
  1051. +     * location where the DEALLOCATE should do it's work
  1052. +     */
  1053. +    ULONG flag = DISABLE_CACHING;
  1054. +    DbgP("nfs41_SetZeroData: disableing caching for file '%wZ'\n",
  1055. +        SrvOpen->pAlreadyPrefixedName);
  1056. +    RxChangeBufferingState((PSRV_OPEN)SrvOpen, ULongToPtr(flag), 1);
  1057. +
  1058. +    status = nfs41_UpcallCreate(NFS41_SYSOP_FSCTL_SET_ZERO_DATA,
  1059. +        &nfs41_fobx->sec_ctx,
  1060. +        pVNetRootContext->session,
  1061. +        nfs41_fobx->nfs41_open_state,
  1062. +        pNetRootContext->nfs41d_version,
  1063. +        SrvOpen->pAlreadyPrefixedName,
  1064. +        &entry);
  1065. +
  1066. +    if (status)
  1067. +        goto out;
  1068. +
  1069. +    entry->u.SetZeroData.setzerodata = *setzerodatabuffer;
  1070. +
  1071. +    status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
  1072. +    if (status) {
  1073. +        /* Timeout - |nfs41_downcall()| will free |entry|+contents */
  1074. +        goto out;
  1075. +    }
  1076. +
  1077. +    if (entry->psec_ctx == &entry->sec_ctx) {
  1078. +        SeDeleteClientSecurity(entry->psec_ctx);
  1079. +    }
  1080. +    entry->psec_ctx = NULL;
  1081. +
  1082. +    if (!entry->status) {
  1083. +        DbgP("nfs41_SetZeroData: SUCCESS\n");
  1084. +        RxContext->CurrentIrp->IoStatus.Status = STATUS_SUCCESS;
  1085. +        RxContext->IoStatusBlock.Information = 0;
  1086. +    }
  1087. +    else {
  1088. +        DbgP("nfs41_SetZeroData: "
  1089. +            "FAILURE, entry->status=0x%lx\n", entry->status);
  1090. +        status = map_setfile_error(entry->status);
  1091. +        RxContext->CurrentIrp->IoStatus.Status = status;
  1092. +        RxContext->IoStatusBlock.Information = 0;
  1093. +    }
  1094. +
  1095. +    if (entry) {
  1096. +        nfs41_UpcallDestroy(entry);
  1097. +    }
  1098. +
  1099. +out:
  1100. +    DbgEx();
  1101. +    return status;
  1102. +}
  1103. +
  1104. +NTSTATUS marshal_nfs41_setzerodata(
  1105. +    nfs41_updowncall_entry *entry,
  1106. +    unsigned char *buf,
  1107. +    ULONG buf_len,
  1108. +    ULONG *len)
  1109. +{
  1110. +    NTSTATUS status = STATUS_SUCCESS;
  1111. +    ULONG header_len = 0;
  1112. +    unsigned char *tmp = buf;
  1113. +
  1114. +    status = marshal_nfs41_header(entry, tmp, buf_len, len);
  1115. +    if (status) goto out;
  1116. +    else tmp += *len;
  1117. +
  1118. +    header_len = *len + sizeof(FILE_ZERO_DATA_INFORMATION);
  1119. +    if (header_len > buf_len) {
  1120. +        status = STATUS_INSUFFICIENT_RESOURCES;
  1121. +        goto out;
  1122. +    }
  1123. +
  1124. +    RtlCopyMemory(tmp, &entry->u.SetZeroData.setzerodata,
  1125. +        sizeof(entry->u.SetZeroData.setzerodata));
  1126. +    tmp += sizeof(entry->u.SetZeroData.setzerodata);
  1127. +
  1128. +    *len = header_len;
  1129. +
  1130. +    DbgP("marshal_nfs41_setzerodata: name='%wZ'\n",
  1131. +         entry->filename);
  1132. +out:
  1133. +    return status;
  1134. +}
  1135. +
  1136. +NTSTATUS unmarshal_nfs41_setzerodata(
  1137. +    nfs41_updowncall_entry *cur,
  1138. +    unsigned char **buf)
  1139. +{
  1140. +    NTSTATUS status = STATUS_SUCCESS;
  1141. +
  1142. +    RtlCopyMemory(&cur->ChangeTime, *buf, sizeof(ULONGLONG));
  1143. +    DbgP("unmarshal_nfs41_setzerodata: returned ChangeTime %llu\n",
  1144. +        cur->ChangeTime);
  1145. +
  1146. +    return status;
  1147. +}
  1148. +
  1149.  NTSTATUS nfs41_FsCtl(
  1150.      IN OUT PRX_CONTEXT RxContext)
  1151.  {
  1152. @@ -392,6 +530,9 @@ NTSTATUS nfs41_FsCtl(
  1153.      case FSCTL_SET_SPARSE:
  1154.          status = nfs41_SetSparse(RxContext);
  1155.          break;
  1156. +    case FSCTL_SET_ZERO_DATA:
  1157. +        status = nfs41_SetZeroData(RxContext);
  1158. +        break;
  1159.      default:
  1160.          break;
  1161.      }
  1162. diff --git a/sys/nfs41sys_updowncall.c b/sys/nfs41sys_updowncall.c
  1163. index 5d1d259..e9e6eac 100644
  1164. --- a/sys/nfs41sys_updowncall.c
  1165. +++ b/sys/nfs41sys_updowncall.c
  1166. @@ -306,6 +306,10 @@ NTSTATUS handle_upcall(
  1167.          status = marshal_nfs41_queryallocatedranges(entry,
  1168.              pbOut, cbOut, len);
  1169.          break;
  1170. +    case NFS41_SYSOP_FSCTL_SET_ZERO_DATA:
  1171. +        status = marshal_nfs41_setzerodata(entry,
  1172. +            pbOut, cbOut, len);
  1173. +        break;
  1174.      default:
  1175.          status = STATUS_INVALID_PARAMETER;
  1176.          print_error("Unknown nfs41 ops %d\n", entry->opcode);
  1177. @@ -667,6 +671,9 @@ NTSTATUS nfs41_downcall(
  1178.          case NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES:
  1179.              unmarshal_nfs41_queryallocatedranges(cur, &buf);
  1180.              break;
  1181. +        case NFS41_SYSOP_FSCTL_SET_ZERO_DATA:
  1182. +            unmarshal_nfs41_setzerodata(cur, &buf);
  1183. +            break;
  1184.          }
  1185.      }
  1186.      ExReleaseFastMutex(&cur->lock);
  1187. diff --git a/tests/sparsefiles/testsparsefile1.ksh b/tests/sparsefiles/testsparsefile1.ksh
  1188. index 2860165..fa0eef5 100644
  1189. --- a/tests/sparsefiles/testsparsefile1.ksh
  1190. +++ b/tests/sparsefiles/testsparsefile1.ksh
  1191. @@ -173,6 +173,44 @@ function test_multihole_sparsefile1
  1192.      return 0
  1193.  }
  1194.  
  1195. +function test_sparse_punchhole1
  1196. +{
  1197. +    set -o errexit
  1198. +    set -o nounset
  1199. +    #set -o xtrace
  1200. +
  1201. +    rm -f 'sparse_file_punchhole'
  1202. +    dd if='/dev/zero' of='sparse_file_punchhole' count=8 bs=$((1024*1024)) status=none
  1203. +    chattr -V +S 'sparse_file_punchhole'
  1204. +
  1205. +    printf '# expected: one data section before fallocate\n'
  1206. +    /cygdrive/c/Windows/system32/fsutil sparse queryrange 'sparse_file_punchhole'
  1207. +
  1208. +    fallocate -n -p -o $((0x16000)) -l $((0x8000)) 'sparse_file_punchhole'
  1209. +
  1210. +    printf '# expected: two data section after fallocate\n'
  1211. +    /cygdrive/c/Windows/system32/fsutil sparse queryrange 'sparse_file_punchhole'
  1212. +
  1213. +    integer fsutil_num_data_sections="$(/cygdrive/c/Windows/system32/fsutil sparse queryrange 'sparse_file_punchhole' | wc -l)"
  1214. +
  1215. +    #
  1216. +    # test whether the file is OK
  1217. +    #
  1218. +    if (( fsutil_num_data_sections != 2 )) ; then
  1219. +        printf "# TEST %q failed, found %d data sections, expceted %d\n" \
  1220. +            "$0" \
  1221. +            fsutil_num_data_sections \
  1222. +            2
  1223. +        return 1
  1224. +    fi
  1225. +
  1226. +    printf "\n#\n# TEST %q OK, found %d data sections\n#\n" \
  1227. +        "$0" \
  1228. +        fsutil_num_data_sections
  1229. +
  1230. +    return 0
  1231. +}
  1232. +
  1233.  
  1234.  #
  1235.  # main
  1236. @@ -199,6 +237,8 @@ test_multihole_sparsefile1 1024 1 4  true
  1237.  # 512 does not work, as Win10 fsutil can only handle 64 data sections
  1238.  # test_multihole_sparsefile1 1024 2 512 false
  1239.  
  1240. +test_sparse_punchhole1
  1241. +
  1242.  printf '#\n# done\n#\n\n'
  1243.  
  1244.  printf '%s: All tests OK\n' "$(basename $0)"
  1245. --
  1246. 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