pastebin - collaborative debugging tool
rovema.kpaste.net RSS


msnfs41client: Patches for Win32 data copy offload, fix rm in read-only dirs, cleanup+misc, 2025-08-20
Posted by Anonymous on Wed 20th Aug 2025 17:19
raw | new post

  1. From f69e5071a0e1a2e3d8b33fa4ac3d14e1ca017a13 Mon Sep 17 00:00:00 2001
  2. From: Roland Mainz <roland.mainz@nrubsig.org>
  3. Date: Wed, 20 Aug 2025 10:10:21 +0200
  4. Subject: [PATCH 1/6] daemon: Handle NFSv4.2 COPY requests which copied less
  5.  bytes than requested
  6.  
  7. Handle NFSv4.2 COPY requests which copied less bytes than requested.
  8.  
  9. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  10. ---
  11. daemon/fsctl.c     | 40 ++++++++++++++++++++++++++++------------
  12.  daemon/nfs41_ops.h |  1 +
  13.  daemon/nfs42_ops.c |  9 ++-------
  14.  3 files changed, 31 insertions(+), 19 deletions(-)
  15.  
  16. diff --git a/daemon/fsctl.c b/daemon/fsctl.c
  17. index e1c4f19..f90950b 100644
  18. --- a/daemon/fsctl.c
  19. +++ b/daemon/fsctl.c
  20. @@ -595,18 +595,34 @@ int duplicate_sparsefile(nfs41_open_state *src_state,
  21.              (int)hole_seek_sr_eof));
  22.  
  23.  #ifdef DUP_DATA_USE_NFSCOPY
  24. -        nfs41_write_verf verf;
  25. -        status = nfs42_copy(session,
  26. -            src_file,
  27. -            dst_file,
  28. -            &src_stateid,
  29. -            &dst_stateid,
  30. -            data_seek_sr_offset,
  31. -            destfileoffset + (data_seek_sr_offset-srcfileoffset),
  32. -            data_size,
  33. -            &verf,
  34. -            info);
  35. -        /* FIXME: What should we do with |verf| ? Should we COMMIT this ? */
  36. +        uint64_t bytes_written;
  37. +        uint64_t bytestowrite = data_size;
  38. +        uint64_t writeoffset = 0ULL;
  39. +
  40. +        do
  41. +        {
  42. +            nfs41_write_verf verf;
  43. +            bytes_written = 0ULL;
  44. +
  45. +            status = nfs42_copy(session,
  46. +                src_file,
  47. +                dst_file,
  48. +                &src_stateid,
  49. +                &dst_stateid,
  50. +                (data_seek_sr_offset + writeoffset),
  51. +                (destfileoffset + (data_seek_sr_offset-srcfileoffset) +
  52. +                    writeoffset),
  53. +                bytestowrite,
  54. +                &bytes_written,
  55. +                &verf,
  56. +                info);
  57. +            if (status)
  58. +                break;
  59. +
  60. +            bytestowrite -= bytes_written;
  61. +            writeoffset += bytes_written;
  62. +            /* FIXME: What should we do with |verf| ? Should we COMMIT this ? */
  63. +        } while (bytestowrite > 0ULL);
  64.  #else
  65.          status = nfs42_clone(session,
  66.              src_file,
  67. diff --git a/daemon/nfs41_ops.h b/daemon/nfs41_ops.h
  68. index dd9dcb4..bd93fbf 100644
  69. --- a/daemon/nfs41_ops.h
  70. +++ b/daemon/nfs41_ops.h
  71. @@ -1299,6 +1299,7 @@ int nfs42_copy(
  72.      IN uint64_t src_offset,
  73.      IN uint64_t dst_offset,
  74.      IN uint64_t length,
  75. +    OUT uint64_t *bytes_written,
  76.      OUT nfs41_write_verf *writeverf,
  77.      OUT nfs41_file_info *cinfo);
  78.  
  79. diff --git a/daemon/nfs42_ops.c b/daemon/nfs42_ops.c
  80. index 582a968..3494a3b 100644
  81. --- a/daemon/nfs42_ops.c
  82. +++ b/daemon/nfs42_ops.c
  83. @@ -122,6 +122,7 @@ int nfs42_copy(
  84.      IN uint64_t src_offset,
  85.      IN uint64_t dst_offset,
  86.      IN uint64_t length,
  87. +    OUT uint64_t *bytes_written,
  88.      OUT nfs41_write_verf *writeverf,
  89.      OUT nfs41_file_info *cinfo)
  90.  {
  91. @@ -205,13 +206,7 @@ int nfs42_copy(
  92.  
  93.      nfs41_superblock_space_changed(dst_file->fh.superblock);
  94.  
  95. -    if (copy_res.u.resok4.response.count != length) {
  96. -        DPRINTF(0,
  97. -            ("nfs42_copy: "
  98. -            "copy_res.u.resok4.response.count(=%lld) < length(=%lld)\n",
  99. -            (long long)copy_res.u.resok4.response.count, (long long)length));
  100. -        status = ERROR_NET_WRITE_FAULT;
  101. -    }
  102. +    *bytes_written = copy_res.u.resok4.response.count;
  103.  
  104.  out:
  105.      return status;
  106. --
  107. 2.45.1
  108.  
  109. From 5570d231f70931307e16ee386b0d9d5f704682ff Mon Sep 17 00:00:00 2001
  110. From: Roland Mainz <roland.mainz@nrubsig.org>
  111. Date: Wed, 20 Aug 2025 12:21:53 +0200
  112. Subject: [PATCH 2/6] README.md,daemon,docs,include,sys: Implement data copy
  113.  offload via |FSCTL_OFFLOAD_READ|+|FSCTL_OFFLOAD_WRITE|
  114.  
  115. Implement data copy (server-side data copying) offload via
  116. |FSCTL_OFFLOAD_READ|+|FSCTL_OFFLOAD_WRITE| and NFSv4.2 COPY.
  117.  
  118. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  119. ---
  120. README.md                 |  17 ++
  121.  daemon/daemon_debug.c     |   1 +
  122.  daemon/fsctl.c            | 151 +++++++------
  123.  daemon/upcall.c           |   2 +
  124.  docs/README.xml           |  21 ++
  125.  include/nfs41_driver.h    |   1 +
  126.  sys/nfs41sys_debug.c      |   2 +
  127.  sys/nfs41sys_driver.c     |   5 +
  128.  sys/nfs41sys_driver.h     |   8 +
  129.  sys/nfs41sys_fsctl.c      | 439 +++++++++++++++++++++++++++++++++++++-
  130.  sys/nfs41sys_updowncall.c |   2 +
  131.  11 files changed, 581 insertions(+), 68 deletions(-)
  132.  
  133. diff --git a/README.md b/README.md
  134. index 1c737a1..efe279c 100644
  135. --- a/README.md
  136. +++ b/README.md
  137. @@ -139,6 +139,23 @@ NFSv4.2/NFSv4.1 filesystem driver for Windows 10/11 & Windows Server
  138.      sparse files. Requires on Win11 \>= 22H2 because it relies on
  139.      `|CopyFile2()|` flag `|COPY_FILE_ENABLE_SPARSE_COPY|`.
  140.  
  141. +- Data copy offload (server-side copy)
  142. +
  143. +  - Implemented via Win32 `|FSCTL_OFFLOAD_READ|`+`|FSCTL_OFFLOAD_WRITE|`
  144. +    to copy data blocks directly on the server.
  145. +
  146. +  - Requires NFSv4.2 server which supports the NFSv4.2 operations
  147. +    "COPY", "DEALLOCATE", "SEEK"
  148. +
  149. +  - Sparse files are correctly copied including all hole and data ranges
  150. +
  151. +  - Windows 10 `|CopyFile2()|` API uses
  152. +    `|FSCTL_OFFLOAD_READ|`+`|FSCTL_OFFLOAD_WRITE|` by default
  153. +
  154. +  - Windows 10 tools like xcopy.exe (on Windows 11 requires `/NOCLONE`,
  155. +    otherwise block cloning is the default), robocopy etc. all use
  156. +    `|CopyFile2()|`, and therefore server-side copies by default
  157. +
  158.  - Block cloning support
  159.  
  160.    - Implemented via Win32 `|FSCTL_DUPLICATE_EXTENTS_TO_FILE|` to clone
  161. diff --git a/daemon/daemon_debug.c b/daemon/daemon_debug.c
  162. index 20fd420..02a0aec 100644
  163. --- a/daemon/daemon_debug.c
  164. +++ b/daemon/daemon_debug.c
  165. @@ -485,6 +485,7 @@ const char* opcode2string(nfs41_opcodes opcode)
  166.          NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES)
  167.          NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_FSCTL_SET_ZERO_DATA)
  168.          NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_FSCTL_DUPLICATE_DATA)
  169. +        NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY)
  170.          NFSOPCODE_TO_STRLITERAL(NFS41_SYSOP_INVALID_OPCODE1)
  171.          default: break;
  172.      }
  173. diff --git a/daemon/fsctl.c b/daemon/fsctl.c
  174. index f90950b..fbbd8aa 100644
  175. --- a/daemon/fsctl.c
  176. +++ b/daemon/fsctl.c
  177. @@ -27,9 +27,6 @@
  178.  #include "daemon_debug.h"
  179.  #include "util.h"
  180.  
  181. -/* Testing only: Use NFS COPY instead of NFS CLONE */
  182. -// #define DUP_DATA_USE_NFSCOPY 1
  183. -
  184.  #define QARLVL 2 /* dprintf level for "query allocated ranges" logging */
  185.  #define SZDLVL 2 /* dprintf level for "set zero data" logging */
  186.  #define DDLVL  2 /* dprintf level for "duplicate data" logging */
  187. @@ -470,7 +467,8 @@ out:
  188.  }
  189.  
  190.  static
  191. -int duplicate_sparsefile(nfs41_open_state *src_state,
  192. +int duplicate_sparsefile(nfs41_opcodes opcode,
  193. +    nfs41_open_state *src_state,
  194.      nfs41_open_state *dst_state,
  195.      uint64_t srcfileoffset,
  196.      uint64_t destfileoffset,
  197. @@ -498,7 +496,8 @@ int duplicate_sparsefile(nfs41_open_state *src_state,
  198.      (void)memset(info, 0, sizeof(*info));
  199.  
  200.      DPRINTF(DDLVL,
  201. -        ("--> duplicate_sparsefile(src_state->path.path='%s')\n",
  202. +        ("--> duplicate_sparsefile(opcode='%s',src_state->path.path='%s')\n",
  203. +        opcode2string(opcode),
  204.          src_state->path.path));
  205.  
  206.      nfs41_open_stateid_arg(src_state, &src_stateid);
  207. @@ -518,9 +517,11 @@ int duplicate_sparsefile(nfs41_open_state *src_state,
  208.      if (status) {
  209.          DPRINTF(0/*DDLVL*/,
  210.              ("duplicate_sparsefile("
  211. +            "opcode='%s',"
  212.              "src_state->path.path='%s' "
  213.              "dst_state->path.path='%s'): "
  214.              "DEALLOCATE failed with '%s'\n",
  215. +            opcode2string(opcode),
  216.              src_state->path.path,
  217.              dst_state->path.path,
  218.              nfs_error_string(status)));
  219. @@ -594,62 +595,60 @@ int duplicate_sparsefile(nfs41_open_state *src_state,
  220.              (int)data_seek_sr_eof,
  221.              (int)hole_seek_sr_eof));
  222.  
  223. -#ifdef DUP_DATA_USE_NFSCOPY
  224. -        uint64_t bytes_written;
  225. -        uint64_t bytestowrite = data_size;
  226. -        uint64_t writeoffset = 0ULL;
  227. -
  228. -        do
  229. -        {
  230. -            nfs41_write_verf verf;
  231. -            bytes_written = 0ULL;
  232. -
  233. -            status = nfs42_copy(session,
  234. +        if (opcode == NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY) {
  235. +            uint64_t bytes_written;
  236. +            uint64_t bytestowrite = data_size;
  237. +            uint64_t writeoffset = 0ULL;
  238. +
  239. +            do
  240. +            {
  241. +                nfs41_write_verf verf;
  242. +                bytes_written = 0ULL;
  243. +
  244. +                status = nfs42_copy(session,
  245. +                    src_file,
  246. +                    dst_file,
  247. +                    &src_stateid,
  248. +                    &dst_stateid,
  249. +                    (data_seek_sr_offset + writeoffset),
  250. +                    (destfileoffset + (data_seek_sr_offset-srcfileoffset) + writeoffset),
  251. +                    bytestowrite,
  252. +                    &bytes_written,
  253. +                    &verf,
  254. +                    info);
  255. +                if (status)
  256. +                    break;
  257. +
  258. +                bytestowrite -= bytes_written;
  259. +                writeoffset += bytes_written;
  260. +                /* FIXME: What should we do with |verf| ? Should we COMMIT this ? */
  261. +            } while (bytestowrite > 0ULL);
  262. +        }
  263. +        else if (opcode == NFS41_SYSOP_FSCTL_DUPLICATE_DATA) {
  264. +            status = nfs42_clone(session,
  265.                  src_file,
  266.                  dst_file,
  267.                  &src_stateid,
  268.                  &dst_stateid,
  269. -                (data_seek_sr_offset + writeoffset),
  270. -                (destfileoffset + (data_seek_sr_offset-srcfileoffset) +
  271. -                    writeoffset),
  272. -                bytestowrite,
  273. -                &bytes_written,
  274. -                &verf,
  275. +                data_seek_sr_offset,
  276. +                destfileoffset + (data_seek_sr_offset-srcfileoffset),
  277. +                data_size,
  278.                  info);
  279. -            if (status)
  280. -                break;
  281. -
  282. -            bytestowrite -= bytes_written;
  283. -            writeoffset += bytes_written;
  284. -            /* FIXME: What should we do with |verf| ? Should we COMMIT this ? */
  285. -        } while (bytestowrite > 0ULL);
  286. -#else
  287. -        status = nfs42_clone(session,
  288. -            src_file,
  289. -            dst_file,
  290. -            &src_stateid,
  291. -            &dst_stateid,
  292. -            data_seek_sr_offset,
  293. -            destfileoffset + (data_seek_sr_offset-srcfileoffset),
  294. -            data_size,
  295. -            info);
  296. -#endif /* DUP_DATA_USE_NFSCOPY */
  297. -        if (status) {
  298. -            const char dup_op_name[] =
  299. -#ifdef DUP_DATA_USE_NFSCOPY
  300. -                "COPY";
  301. -#else
  302. -                "CLONE";
  303. -#endif /* DUP_DATA_USE_NFSCOPY */
  304. +        }
  305. +        else {
  306. +            EASSERT(0);
  307. +        }
  308.  
  309. +        if (status) {
  310.              DPRINTF(0/*DDLVL*/,
  311.                  ("duplicate_sparsefile("
  312. +                "opcode='%s',"
  313.                  "src_state->path.path='%s' "
  314.                  "dst_state->path.path='%s'): "
  315. -                "'%s' failed with '%s'\n",
  316. +                "failed with '%s'\n",
  317. +                opcode2string(opcode),
  318.                  src_state->path.path,
  319.                  dst_state->path.path,
  320. -                dup_op_name,
  321.                  nfs_error_string(status)));
  322.              status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
  323.              goto out;
  324. @@ -704,25 +703,35 @@ int handle_duplicatedata(void *daemon_context,
  325.              dst_state->path.path,
  326.              src_state->path.path));
  327.  
  328. -    /* NFS SEEK supported ? */
  329. -    if (src_session->client->root->supports_nfs42_seek == false) {
  330. -        status = ERROR_NOT_SUPPORTED;
  331. -        goto out;
  332. +    /*
  333. +     * Check whether we support the required NFS operations...
  334. +     */
  335. +    if (upcall->opcode == NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY) {
  336. +        if ((src_session->client->root->supports_nfs42_seek == false) ||
  337. +            (src_session->client->root->supports_nfs42_copy == false) ||
  338. +            (src_session->client->root->supports_nfs42_deallocate == false)) {
  339. +            status = ERROR_NOT_SUPPORTED;
  340. +            goto out;
  341. +        }
  342.      }
  343. -    /* NFS CLONE supported ? */
  344. -    if (
  345. -#ifdef DUP_DATA_USE_NFSCOPY
  346. -        src_session->client->root->supports_nfs42_copy == false
  347. -#else
  348. -        src_session->client->root->supports_nfs42_clone == false
  349. -#endif /* DUP_DATA_USE_NFSCOPY */
  350. -        ) {
  351. -        status = ERROR_NOT_SUPPORTED;
  352. -        goto out;
  353. +    else if (upcall->opcode == NFS41_SYSOP_FSCTL_DUPLICATE_DATA) {
  354. +        if ((src_session->client->root->supports_nfs42_seek == false) ||
  355. +            (src_session->client->root->supports_nfs42_clone == false) ||
  356. +            (src_session->client->root->supports_nfs42_deallocate == false)) {
  357. +            status = ERROR_NOT_SUPPORTED;
  358. +            goto out;
  359. +        }
  360.      }
  361. -    /* NFS DEALLOCATE supported ? */
  362. -    if (src_session->client->root->supports_nfs42_deallocate == false) {
  363. -        status = ERROR_NOT_SUPPORTED;
  364. +    else {
  365. +        status = ERROR_INVALID_PARAMETER;
  366. +        eprintf("duplicate_sparsefile("
  367. +            "opcode='%s',"
  368. +            "src_state->path.path='%s' "
  369. +            "dst_state->path.path='%s'): "
  370. +            "Unknown opcode.\n",
  371. +            opcode2string(upcall->opcode),
  372. +            src_state->path.path,
  373. +            dst_state->path.path);
  374.          goto out;
  375.      }
  376.  
  377. @@ -853,7 +862,8 @@ int handle_duplicatedata(void *daemon_context,
  378.  
  379.      (void)memset(&info, 0, sizeof(info));
  380.  
  381. -    status = duplicate_sparsefile(src_state,
  382. +    status = duplicate_sparsefile(upcall->opcode,
  383. +        src_state,
  384.          dst_state,
  385.          args->srcfileoffset,
  386.          args->destfileoffset,
  387. @@ -895,3 +905,10 @@ const nfs41_upcall_op nfs41_op_duplicatedata = {
  388.      .marshall = marshall_duplicatedata,
  389.      .arg_size = sizeof(duplicatedata_upcall_args)
  390.  };
  391. +
  392. +const nfs41_upcall_op nfs41_op_offload_datacopy = {
  393. +    .parse = parse_duplicatedata,
  394. +    .handle = handle_duplicatedata,
  395. +    .marshall = marshall_duplicatedata,
  396. +    .arg_size = sizeof(duplicatedata_upcall_args)
  397. +};
  398. diff --git a/daemon/upcall.c b/daemon/upcall.c
  399. index 45e0729..53433c4 100644
  400. --- a/daemon/upcall.c
  401. +++ b/daemon/upcall.c
  402. @@ -52,6 +52,7 @@ extern const nfs41_upcall_op nfs41_op_setacl;
  403.  extern const nfs41_upcall_op nfs41_op_queryallocatedranges;
  404.  extern const nfs41_upcall_op nfs41_op_setzerodata;
  405.  extern const nfs41_upcall_op nfs41_op_duplicatedata;
  406. +extern const nfs41_upcall_op nfs41_op_offload_datacopy;
  407.  
  408.  /* |_nfs41_opcodes| and |g_upcall_op_table| must be in sync! */
  409.  static const nfs41_upcall_op *g_upcall_op_table[] = {
  410. @@ -78,6 +79,7 @@ static const nfs41_upcall_op *g_upcall_op_table[] = {
  411.      &nfs41_op_queryallocatedranges,
  412.      &nfs41_op_setzerodata,
  413.      &nfs41_op_duplicatedata,
  414. +    &nfs41_op_offload_datacopy,
  415.      NULL,
  416.      NULL
  417.  };
  418. diff --git a/docs/README.xml b/docs/README.xml
  419. index 1252843..a69372a 100644
  420. --- a/docs/README.xml
  421. +++ b/docs/README.xml
  422. @@ -141,6 +141,27 @@
  423.            </itemizedlist>
  424.          </para>
  425.        </listitem>
  426. +      <listitem>
  427. +        <para>Data copy offload (server-side copy)
  428. +          <itemizedlist>
  429. +            <listitem>
  430. +              <para>Implemented via Win32 <literal>|FSCTL_OFFLOAD_READ|</literal>+<literal>|FSCTL_OFFLOAD_WRITE|</literal> to copy data blocks directly on the server.</para>
  431. +            </listitem>
  432. +            <listitem>
  433. +              <para>Requires NFSv4.2 server which supports the NFSv4.2 operations "COPY", "DEALLOCATE", "SEEK"</para>
  434. +            </listitem>
  435. +            <listitem>
  436. +              <para>Sparse files are correctly copied including all hole and data ranges</para>
  437. +            </listitem>
  438. +            <listitem>
  439. +              <para>Windows 10 <literal>|CopyFile2()|</literal> API uses <literal>|FSCTL_OFFLOAD_READ|</literal>+<literal>|FSCTL_OFFLOAD_WRITE|</literal> by default</para>
  440. +            </listitem>
  441. +            <listitem>
  442. +              <para>Windows 10 tools like xcopy.exe (on Windows 11 requires <literal>/NOCLONE</literal>, otherwise block cloning is the default), robocopy etc. all use <literal>|CopyFile2()|</literal>, and therefore server-side copies by default</para>
  443. +            </listitem>
  444. +          </itemizedlist>
  445. +        </para>
  446. +      </listitem>
  447.        <listitem>
  448.          <para>Block cloning support
  449.            <itemizedlist>
  450. diff --git a/include/nfs41_driver.h b/include/nfs41_driver.h
  451. index bf956f4..7a4ddfc 100644
  452. --- a/include/nfs41_driver.h
  453. +++ b/include/nfs41_driver.h
  454. @@ -88,6 +88,7 @@ typedef enum _nfs41_opcodes {
  455.      NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES,
  456.      NFS41_SYSOP_FSCTL_SET_ZERO_DATA,
  457.      NFS41_SYSOP_FSCTL_DUPLICATE_DATA,
  458. +    NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY,
  459.      NFS41_SYSOP_SHUTDOWN,
  460.      NFS41_SYSOP_INVALID_OPCODE1
  461.  } nfs41_opcodes;
  462. diff --git a/sys/nfs41sys_debug.c b/sys/nfs41sys_debug.c
  463. index ee95c9a..ffebee3 100644
  464. --- a/sys/nfs41sys_debug.c
  465. +++ b/sys/nfs41sys_debug.c
  466. @@ -683,6 +683,8 @@ const char *opcode2string(int opcode)
  467.      case NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES: return "NFS41_SYSOP_FSCTL_QUERYALLOCATEDRANGES";
  468.      case NFS41_SYSOP_FSCTL_SET_ZERO_DATA: return "NFS41_SYSOP_FSCTL_SET_ZERO_DATA";
  469.      case NFS41_SYSOP_FSCTL_DUPLICATE_DATA: return "NFS41_SYSOP_FSCTL_DUPLICATE_DATA";
  470. +    case NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY:
  471. +        return "NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY";
  472.      default: return "UNKNOWN";
  473.      }
  474.  }
  475. diff --git a/sys/nfs41sys_driver.c b/sys/nfs41sys_driver.c
  476. index b079180..fd72305 100644
  477. --- a/sys/nfs41sys_driver.c
  478. +++ b/sys/nfs41sys_driver.c
  479. @@ -131,6 +131,8 @@ PRDBSS_DEVICE_OBJECT nfs41_dev;
  480.  KEVENT upcallEvent;
  481.  FAST_MUTEX upcallLock, downcallLock, fcblistLock;
  482.  FAST_MUTEX openOwnerLock;
  483. +FAST_MUTEX offloadcontextLock;
  484. +nfs41_offloadcontext_list offloadcontext_list;
  485.  
  486.  LONGLONG xid = 0;
  487.  LONG open_owner_id = 1;
  488. @@ -772,6 +774,7 @@ NTSTATUS nfs41_DeallocateForFobx(
  489.      __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(pFobx);
  490.  
  491.      nfs41_invalidate_fobx_entry(pFobx);
  492. +    nfs41_remove_offloadcontext_for_fobx(pFobx);
  493.  
  494.      if (nfs41_fobx->acl) {
  495.          RxFreePool(nfs41_fobx->acl);
  496. @@ -1396,9 +1399,11 @@ NTSTATUS DriverEntry(
  497.      ExInitializeFastMutex(&downcallLock);
  498.      ExInitializeFastMutex(&openOwnerLock);
  499.      ExInitializeFastMutex(&fcblistLock);
  500. +    ExInitializeFastMutex(&offloadcontextLock);
  501.      InitializeListHead(&upcall.head);
  502.      InitializeListHead(&downcall.head);
  503.      InitializeListHead(&openlist.head);
  504. +    InitializeListHead(&offloadcontext_list.head);
  505.  #ifdef USE_LOOKASIDELISTS_FOR_UPDOWNCALLENTRY_MEM
  506.      /*
  507.       * The |Depth| parameter is unfortunately ignored in Win10,
  508. diff --git a/sys/nfs41sys_driver.h b/sys/nfs41sys_driver.h
  509. index 597d207..3f8cc68 100644
  510. --- a/sys/nfs41sys_driver.h
  511. +++ b/sys/nfs41sys_driver.h
  512. @@ -507,6 +507,12 @@ typedef struct _nfs41_fcb_list {
  513.  } nfs41_fcb_list;
  514.  nfs41_fcb_list openlist;
  515.  
  516. +typedef struct _nfs41_offloadcontext_list {
  517. +    LIST_ENTRY head;
  518. +} nfs41_offloadcontext_list;
  519. +extern nfs41_offloadcontext_list offloadcontext_list;
  520. +extern FAST_MUTEX offloadcontextLock;
  521. +
  522.  typedef enum _NULMRX_STORAGE_TYPE_CODES {
  523.      NTC_NFS41_DEVICE_EXTENSION      =   (NODE_TYPE_CODE)0xFC00,
  524.  } NFS41_STORAGE_TYPE_CODES;
  525. @@ -685,6 +691,8 @@ NTSTATUS marshal_nfs41_duplicatedata(
  526.  NTSTATUS unmarshal_nfs41_duplicatedata(
  527.      nfs41_updowncall_entry *cur,
  528.      unsigned char **buf);
  529. +void nfs41_remove_offloadcontext_for_fobx(
  530. +    IN PMRX_FOBX pFobx);
  531.  
  532.  /* nfs41sys_ioctl.c */
  533.  NTSTATUS nfs41_IoCtl(
  534. diff --git a/sys/nfs41sys_fsctl.c b/sys/nfs41sys_fsctl.c
  535. index c1a3f2b..546a95b 100644
  536. --- a/sys/nfs41sys_fsctl.c
  537. +++ b/sys/nfs41sys_fsctl.c
  538. @@ -56,7 +56,7 @@
  539.  #include <rx.h>
  540.  #include <windef.h>
  541.  #include <winerror.h>
  542. -
  543. +#include <ntddstor.h>
  544.  #include <Ntstrsafe.h>
  545.  
  546.  #include "nfs41sys_buildconfig.h"
  547. @@ -863,6 +863,437 @@ NTSTATUS unmarshal_nfs41_duplicatedata(
  548.      return status;
  549.  }
  550.  
  551. +/*
  552. + * |offloadcontext_entry| - context to store |FSCTL_OFFLOAD_READ| token
  553. + * information
  554. + *
  555. + * * Notes:
  556. + * - These are stored in a global list, as |FSCTL_OFFLOAD_READ|+
  557. + * |FSCTL_OFFLOAD_WRITE| is intended to work for intra-server and
  558. + * inter-server copies, so |FSCTL_OFFLOAD_READ| might be done on one
  559. + * filesystem but |FSCTL_OFFLOAD_WRITE| might be done on a different
  560. + * one
  561. + *
  562. + * * FIXME:
  563. + * - Is it legal if one user passes a token to another user, or
  564. + * should this be prevented ?
  565. + * - |offloadcontext_entry| lifetime is unkown. Right now we create
  566. + * it via |FSCTL_OFFLOAD_READ| and remove it when the matching file gets
  567. + * closed, but we ignore |FSCTL_OFFLOAD_READ_INPUT.TokenTimeToLive|
  568. + */
  569. +typedef struct _offloadcontext_entry
  570. +{
  571. +    LIST_ENTRY              next;
  572. +    /*
  573. +     * r/w lock - shared access for |FSCTL_OFFLOAD_WRITE|, so one token can
  574. +     * be used for multiple parallel writes, exclusive access for file delete
  575. +     * (i.e. wait until all shared access before deleting the context)
  576. +     */
  577. +    ERESOURCE               resource;
  578. +    STORAGE_OFFLOAD_TOKEN   token;
  579. +    PNFS41_FOBX             src_fobx;
  580. +    ULONGLONG               src_fileoffset;
  581. +    ULONGLONG               src_length;
  582. +} offloadcontext_entry;
  583. +
  584. +
  585. +void nfs41_remove_offloadcontext_for_fobx(
  586. +    IN PMRX_FOBX pFobx)
  587. +{
  588. +    PLIST_ENTRY pEntry;
  589. +    offloadcontext_entry *cur, *found = NULL;
  590. +    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(pFobx);
  591. +
  592. +    ExAcquireFastMutexUnsafe(&offloadcontextLock);
  593. +
  594. +    pEntry = offloadcontext_list.head.Flink;
  595. +    while (!IsListEmpty(&offloadcontext_list.head)) {
  596. +        cur = (offloadcontext_entry *)CONTAINING_RECORD(pEntry,
  597. +            offloadcontext_entry, next);
  598. +        if (cur->src_fobx == nfs41_fobx) {
  599. +            found = cur;
  600. +            break;
  601. +        }
  602. +        if (pEntry->Flink == &offloadcontext_list.head) {
  603. +            break;
  604. +        }
  605. +        pEntry = pEntry->Flink;
  606. +    }
  607. +
  608. +    if (found) {
  609. +        DbgP("nfs41_remove_offloadcontext(pFobx=0x%p): "
  610. +            "removing found=0x%p\n",
  611. +            pFobx,
  612. +            found);
  613. +
  614. +        /* Wait for any shared access in |nfs41_OffloadWrite()| to finish */
  615. +        (void)ExAcquireResourceExclusiveLite(&found->resource, TRUE);
  616. +        ExReleaseResourceLite(&found->resource);
  617. +
  618. +        RemoveEntryList(&found->next);
  619. +
  620. +        (void)ExDeleteResourceLite(&found->resource);
  621. +        RxFreePool(found);
  622. +    }
  623. +    else {
  624. +        DbgP("nfs41_remove_offloadcontext(pFobx=0x%p): Nothing found.\n",
  625. +            pFobx);
  626. +    }
  627. +
  628. +    ExReleaseFastMutexUnsafe(&offloadcontextLock);
  629. +}
  630. +
  631. +static
  632. +offloadcontext_entry *nfs41_find_offloadcontext_acquireshared(
  633. +    IN offloadcontext_entry *unvalidated_oce)
  634. +{
  635. +    PLIST_ENTRY pEntry;
  636. +    offloadcontext_entry *cur, *found = NULL;
  637. +
  638. +    ExAcquireFastMutexUnsafe(&offloadcontextLock);
  639. +
  640. +    pEntry = offloadcontext_list.head.Flink;
  641. +    while (!IsListEmpty(&offloadcontext_list.head)) {
  642. +        cur = (offloadcontext_entry *)CONTAINING_RECORD(pEntry,
  643. +            offloadcontext_entry, next);
  644. +        if (cur == unvalidated_oce) {
  645. +            found = cur;
  646. +            break;
  647. +        }
  648. +        if (pEntry->Flink == &offloadcontext_list.head) {
  649. +            break;
  650. +        }
  651. +        pEntry = pEntry->Flink;
  652. +    }
  653. +
  654. +    if (found) {
  655. +        DbgP("nfs41_find_offloadcontext_acquireshared(unvalidated_oce=0x%p): "
  656. +            "found=0x%p\n",
  657. +            unvalidated_oce);
  658. +
  659. +        (void)ExAcquireSharedStarveExclusive(&found->resource, TRUE);
  660. +        ExReleaseFastMutexUnsafe(&offloadcontextLock);
  661. +        return found;
  662. +    }
  663. +    else {
  664. +        DbgP("nfs41_find_offloadcontext_acquireshared(unvalidated_oce=0x%p): "
  665. +            "Nothing found.\n",
  666. +            unvalidated_oce);
  667. +        ExReleaseFastMutexUnsafe(&offloadcontextLock);
  668. +        return NULL;
  669. +    }
  670. +}
  671. +
  672. +static
  673. +NTSTATUS nfs41_OffloadRead(
  674. +    IN OUT PRX_CONTEXT RxContext)
  675. +{
  676. +    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
  677. +    __notnull XXCTL_LOWIO_COMPONENT *FsCtl =
  678. +        &RxContext->LowIoContext.ParamsFor.FsCtl;
  679. +    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
  680. +
  681. +    DbgEn();
  682. +
  683. +    RxContext->IoStatusBlock.Information = 0;
  684. +
  685. +    if (FsCtl->pInputBuffer == NULL) {
  686. +        status = STATUS_INVALID_USER_BUFFER;
  687. +        goto out;
  688. +    }
  689. +
  690. +    if (FsCtl->pOutputBuffer == NULL) {
  691. +        status = STATUS_INVALID_USER_BUFFER;
  692. +        goto out;
  693. +    }
  694. +
  695. +    if (FsCtl->InputBufferLength < sizeof(FSCTL_OFFLOAD_READ_INPUT)) {
  696. +        DbgP("nfs41_OffloadRead: "
  697. +            "buffer too small for FSCTL_OFFLOAD_READ_INPUT\n");
  698. +        status = STATUS_BUFFER_TOO_SMALL;
  699. +        goto out;
  700. +    }
  701. +    if (FsCtl->OutputBufferLength < sizeof(FSCTL_OFFLOAD_READ_OUTPUT)) {
  702. +        DbgP("nfs41_OffloadRead: "
  703. +            "buffer too small for FSCTL_OFFLOAD_READ_OUTPUT\n");
  704. +        status = STATUS_BUFFER_TOO_SMALL;
  705. +        goto out;
  706. +    }
  707. +
  708. +    PFSCTL_OFFLOAD_READ_INPUT ori =
  709. +        (PFSCTL_OFFLOAD_READ_INPUT)FsCtl->pInputBuffer;
  710. +    PFSCTL_OFFLOAD_READ_OUTPUT oro =
  711. +        (PFSCTL_OFFLOAD_READ_OUTPUT)FsCtl->pOutputBuffer;
  712. +
  713. +    DbgP("nfs41_OffloadRead: "
  714. +        "ori->(Size=%lu, Flags=0x%lx, TokenTimeToLive=%lu, Reserved=%lu, "
  715. +        "FileOffset=%llu, CopyLength=%llu)\n",
  716. +        (unsigned long)ori->Size,
  717. +        (unsigned long)ori->Flags,
  718. +        (unsigned long)ori->TokenTimeToLive,
  719. +        (unsigned long)ori->Reserved,
  720. +        (unsigned long long)ori->FileOffset,
  721. +        (unsigned long long)ori->CopyLength);
  722. +
  723. +    offloadcontext_entry *oce = RxAllocatePoolWithTag(NonPagedPoolNx,
  724. +        sizeof(offloadcontext_entry), NFS41_MM_POOLTAG);
  725. +    if (oce == NULL) {
  726. +        status = STATUS_INSUFFICIENT_RESOURCES;
  727. +        goto out;
  728. +    }
  729. +
  730. +    DbgP("nfs41_OffloadRead: oce=0x%p\n", oce);
  731. +
  732. +    (void)ExInitializeResourceLite(&oce->resource);
  733. +
  734. +    (void)memset(&oce->token, 0, sizeof(oce->token));
  735. +    /* Add safeguard to |TokenType| */
  736. +    oce->token.TokenType[0] = 'N';
  737. +    oce->token.TokenType[1] = 'F';
  738. +    oce->token.TokenType[2] = 'S';
  739. +    oce->token.TokenType[3] = '4';
  740. +    /* FIXME: What about the endianness of |TokenIdLength| ? */
  741. +    *((USHORT *)(&oce->token.TokenIdLength[0])) =
  742. +        STORAGE_OFFLOAD_TOKEN_ID_LENGTH;
  743. +    *((void **)(&oce->token.Token[0])) = oce;
  744. +    oce->src_fobx = nfs41_fobx;
  745. +    oce->src_fileoffset = ori->FileOffset;
  746. +    oce->src_length = ori->CopyLength;
  747. +
  748. +    oro->Size = sizeof(FSCTL_OFFLOAD_READ_OUTPUT);
  749. +    oro->Flags = 0;
  750. +    oro->TransferLength = ori->CopyLength;
  751. +    (void)memcpy(&oro->Token[0], &oce->token, sizeof(oce->token));
  752. +
  753. +    nfs41_AddEntry(offloadcontextLock, offloadcontext_list, oce);
  754. +
  755. +    RxContext->CurrentIrp->IoStatus.Status = status = STATUS_SUCCESS;
  756. +    RxContext->InformationToReturn = sizeof(FSCTL_OFFLOAD_READ_OUTPUT);
  757. +
  758. +out:
  759. +    DbgEx();
  760. +    return status;
  761. +}
  762. +
  763. +static
  764. +NTSTATUS check_nfs41_offload_write_args(
  765. +    PRX_CONTEXT RxContext)
  766. +{
  767. +    NTSTATUS status = STATUS_SUCCESS;
  768. +    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
  769. +    __notnull PNFS41_V_NET_ROOT_EXTENSION VNetRootContext =
  770. +        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
  771. +
  772. +    /* access checks */
  773. +    if (VNetRootContext->read_only) {
  774. +        status = STATUS_MEDIA_WRITE_PROTECTED;
  775. +        goto out;
  776. +    }
  777. +    if (!(SrvOpen->DesiredAccess &
  778. +        (FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES))) {
  779. +        status = STATUS_ACCESS_DENIED;
  780. +        goto out;
  781. +    }
  782. +
  783. +out:
  784. +    return status;
  785. +}
  786. +
  787. +static
  788. +NTSTATUS nfs41_OffloadWrite(
  789. +    IN OUT PRX_CONTEXT RxContext)
  790. +{
  791. +    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
  792. +    nfs41_updowncall_entry *entry = NULL;
  793. +    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
  794. +    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
  795. +        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
  796. +    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
  797. +        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
  798. +    __notnull XXCTL_LOWIO_COMPONENT *FsCtl =
  799. +        &RxContext->LowIoContext.ParamsFor.FsCtl;
  800. +    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
  801. +    offloadcontext_entry *src_oce = NULL;
  802. +
  803. +    struct {
  804. +        LONGLONG    srcfileoffset;
  805. +        LONGLONG    destfileoffset;
  806. +        LONGLONG    bytecount;
  807. +    } dd;
  808. +
  809. +    DbgEn();
  810. +
  811. +    LONGLONG io_delay;
  812. +    RxContext->IoStatusBlock.Information = 0;
  813. +
  814. +    status = check_nfs41_offload_write_args(RxContext);
  815. +    if (status)
  816. +        goto out;
  817. +
  818. +    if (FsCtl->pInputBuffer == NULL) {
  819. +        status = STATUS_INVALID_USER_BUFFER;
  820. +        goto out;
  821. +    }
  822. +    if (FsCtl->pOutputBuffer == NULL) {
  823. +        status = STATUS_INVALID_USER_BUFFER;
  824. +        goto out;
  825. +    }
  826. +    if (FsCtl->InputBufferLength < sizeof(FSCTL_OFFLOAD_WRITE_INPUT)) {
  827. +        DbgP("nfs41_OffloadWrite: "
  828. +            "buffer too small for FSCTL_OFFLOAD_WRITE_INPUT\n");
  829. +        status = STATUS_BUFFER_TOO_SMALL;
  830. +        goto out;
  831. +    }
  832. +    if (FsCtl->OutputBufferLength < sizeof(FSCTL_OFFLOAD_WRITE_OUTPUT)) {
  833. +        DbgP("nfs41_OffloadWrite: "
  834. +            "buffer too small for FSCTL_OFFLOAD_WRITE_OUTPUT\n");
  835. +        status = STATUS_BUFFER_TOO_SMALL;
  836. +        goto out;
  837. +    }
  838. +
  839. +    PFSCTL_OFFLOAD_WRITE_INPUT owi =
  840. +        (PFSCTL_OFFLOAD_WRITE_INPUT)FsCtl->pInputBuffer;
  841. +    PFSCTL_OFFLOAD_WRITE_OUTPUT owo =
  842. +        (PFSCTL_OFFLOAD_WRITE_OUTPUT)FsCtl->pOutputBuffer;
  843. +
  844. +    offloadcontext_entry *unvalidated_src_oce;
  845. +
  846. +    /*
  847. +     * Peel |offloadcontext_entry| pointer from token...
  848. +     */
  849. +    unvalidated_src_oce =
  850. +        *((void **)(&(((STORAGE_OFFLOAD_TOKEN *)(&owi->Token[0]))->Token[0])));
  851. +    DbgP("nfs41_OffloadWrite: "
  852. +        "unvalidated_src_oce=0x%p\n", unvalidated_src_oce);
  853. +
  854. +    /*
  855. +     * ... and validate it (and take a shared lock if validation was
  856. +     * successful, so nobody can delete the context while we use it)!
  857. +     */
  858. +    src_oce = nfs41_find_offloadcontext_acquireshared(unvalidated_src_oce);
  859. +    if (src_oce == NULL) {
  860. +        DbgP("nfs41_OffloadWrite: "
  861. +            "nfs41_find_offloadcontext_acquireshared() failed\n");
  862. +        status = STATUS_INVALID_PARAMETER;
  863. +        goto out;
  864. +    }
  865. +
  866. +    DbgP("nfs41_OffloadWrite: src_oce=0x%p\n", src_oce);
  867. +
  868. +    /* Check safeguard... */
  869. +    if ((src_oce->token.TokenType[0] != 'N') ||
  870. +        (src_oce->token.TokenType[1] != 'F') ||
  871. +        (src_oce->token.TokenType[2] != 'S') ||
  872. +        (src_oce->token.TokenType[3] != '4')) {
  873. +        DbgP("nfs41_OffloadWrite: "
  874. +            "token in src_oce=0x%p not a 'NFS4' token\n",
  875. +            src_oce);
  876. +        status = STATUS_INVALID_PARAMETER;
  877. +        goto out;
  878. +    }
  879. +
  880. +    /*
  881. +     * FIXME: We should validate the length passed as
  882. +     * |FSCTL_OFFLOAD_READ_INPUT.CopyLength| here better, because it is
  883. +     * also used as some kind of access control to different parts of a
  884. +     * file
  885. +     */
  886. +    dd.srcfileoffset    = src_oce->src_fileoffset + owi->TransferOffset;
  887. +    dd.destfileoffset   = owi->FileOffset;
  888. +    dd.bytecount        = owi->CopyLength;
  889. +
  890. +    DbgP("nfs41_OffloadWrite: "
  891. +        "dd=(srcfileoffset=%lld,"
  892. +        "destfileoffset=%lld,"
  893. +        "bytecount=%lld)\n",
  894. +        (long long)dd.srcfileoffset,
  895. +        (long long)dd.destfileoffset,
  896. +        (long long)dd.bytecount);
  897. +
  898. +    if (dd.bytecount == 0LL) {
  899. +        status = STATUS_SUCCESS;
  900. +        goto out;
  901. +    }
  902. +
  903. +    PNFS41_FOBX nfs41_src_fobx = src_oce->src_fobx;
  904. +    if (!nfs41_src_fobx) {
  905. +        DbgP("nfs41_OffloadWrite: No nfs41_src_fobx\n");
  906. +        status = STATUS_INVALID_PARAMETER;
  907. +        goto out;
  908. +    }
  909. +
  910. +    /*
  911. +     * Disable caching because NFSv4.2 COPY is basically a
  912. +     * "write" operation. AFAIK we should flush the cache and wait
  913. +     * for the kernel lazy writer (which |RxChangeBufferingState()|
  914. +     * AFAIK does) before doing the COPY, to avoid that we
  915. +     * have outstanding writes in the kernel cache at the same
  916. +     * location where the COPY should do it's work
  917. +     */
  918. +    ULONG flag = DISABLE_CACHING;
  919. +    DbgP("nfs41_OffloadWrite: disableing caching for file '%wZ'\n",
  920. +        SrvOpen->pAlreadyPrefixedName);
  921. +    RxChangeBufferingState((PSRV_OPEN)SrvOpen, ULongToPtr(flag), 1);
  922. +
  923. +    status = nfs41_UpcallCreate(NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY,
  924. +        &nfs41_fobx->sec_ctx,
  925. +        pVNetRootContext->session,
  926. +        nfs41_fobx->nfs41_open_state,
  927. +        pNetRootContext->nfs41d_version,
  928. +        SrvOpen->pAlreadyPrefixedName,
  929. +        &entry);
  930. +
  931. +    if (status)
  932. +        goto out;
  933. +
  934. +    entry->u.DuplicateData.src_state = nfs41_src_fobx->nfs41_open_state;
  935. +    entry->u.DuplicateData.srcfileoffset = dd.srcfileoffset;
  936. +    entry->u.DuplicateData.destfileoffset = dd.destfileoffset;
  937. +    entry->u.DuplicateData.bytecount = dd.bytecount;
  938. +
  939. +    /* Add extra timeout depending on file size */
  940. +    io_delay = pVNetRootContext->timeout +
  941. +        EXTRA_TIMEOUT_PER_BYTE(entry->u.DuplicateData.bytecount);
  942. +
  943. +    status = nfs41_UpcallWaitForReply(entry, io_delay);
  944. +    if (status) {
  945. +        /* Timeout - |nfs41_downcall()| will free |entry|+contents */
  946. +        entry = NULL;
  947. +        goto out;
  948. +    }
  949. +
  950. +    if (!entry->status) {
  951. +        DbgP("nfs41_OffloadWrite: SUCCESS\n");
  952. +
  953. +        owo->Size = sizeof(FSCTL_OFFLOAD_READ_OUTPUT);
  954. +        owo->Flags = 0;
  955. +        owo->LengthWritten = dd.bytecount;
  956. +
  957. +        RxContext->CurrentIrp->IoStatus.Status = status = STATUS_SUCCESS;
  958. +        RxContext->InformationToReturn = sizeof(FSCTL_OFFLOAD_READ_OUTPUT);
  959. +    }
  960. +    else {
  961. +        DbgP("nfs41_OffloadWrite: "
  962. +            "FAILURE, entry->status=0x%lx\n", entry->status);
  963. +        status = map_setfile_error(entry->status);
  964. +        RxContext->CurrentIrp->IoStatus.Status = status;
  965. +        RxContext->IoStatusBlock.Information = 0;
  966. +    }
  967. +
  968. +out:
  969. +    if (src_oce) {
  970. +        /* Release resource we obtained in shared mode */
  971. +        ExReleaseResourceLite(&src_oce->resource);
  972. +    }
  973. +
  974. +    if (entry) {
  975. +        nfs41_UpcallDestroy(entry);
  976. +    }
  977. +
  978. +    DbgEx();
  979. +    return status;
  980. +}
  981. +
  982.  NTSTATUS nfs41_FsCtl(
  983.      IN OUT PRX_CONTEXT RxContext)
  984.  {
  985. @@ -895,6 +1326,12 @@ NTSTATUS nfs41_FsCtl(
  986.      case FSCTL_DUPLICATE_EXTENTS_TO_FILE:
  987.          status = nfs41_DuplicateData(RxContext);
  988.          break;
  989. +    case FSCTL_OFFLOAD_READ:
  990. +        status = nfs41_OffloadRead(RxContext);
  991. +        break;
  992. +    case FSCTL_OFFLOAD_WRITE:
  993. +        status = nfs41_OffloadWrite(RxContext);
  994. +        break;
  995.      default:
  996.          break;
  997.      }
  998. diff --git a/sys/nfs41sys_updowncall.c b/sys/nfs41sys_updowncall.c
  999. index cf5be35..fc523a4 100644
  1000. --- a/sys/nfs41sys_updowncall.c
  1001. +++ b/sys/nfs41sys_updowncall.c
  1002. @@ -321,6 +321,7 @@ NTSTATUS handle_upcall(
  1003.              pbOut, cbOut, len);
  1004.          break;
  1005.      case NFS41_SYSOP_FSCTL_DUPLICATE_DATA:
  1006. +    case NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY:
  1007.          status = marshal_nfs41_duplicatedata(entry,
  1008.              pbOut, cbOut, len);
  1009.          break;
  1010. @@ -785,6 +786,7 @@ NTSTATUS nfs41_downcall(
  1011.              unmarshal_nfs41_setzerodata(cur, &buf);
  1012.              break;
  1013.          case NFS41_SYSOP_FSCTL_DUPLICATE_DATA:
  1014. +        case NFS41_SYSOP_FSCTL_OFFLOAD_DATACOPY:
  1015.              unmarshal_nfs41_duplicatedata(cur, &buf);
  1016.              break;
  1017.          }
  1018. --
  1019. 2.45.1
  1020.  
  1021. From 53d029884cf831c74cf9cb5b3e6808583d94f215 Mon Sep 17 00:00:00 2001
  1022. From: Roland Mainz <roland.mainz@nrubsig.org>
  1023. Date: Wed, 20 Aug 2025 14:04:41 +0200
  1024. Subject: [PATCH 3/6] sys: Move |FAST_MUTEX| into the list structs they protect
  1025.  
  1026. Move |FAST_MUTEX| into the list structs they protect.
  1027.  
  1028. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  1029. ---
  1030. sys/nfs41sys_driver.c     | 44 +++++++++++++++++++--------------------
  1031.  sys/nfs41sys_driver.h     | 21 +++++++++----------
  1032.  sys/nfs41sys_fsctl.c      | 25 +++++++++++-----------
  1033.  sys/nfs41sys_mount.c      | 26 +++++++++++------------
  1034.  sys/nfs41sys_openclose.c  |  2 +-
  1035.  sys/nfs41sys_updowncall.c | 20 +++++++++---------
  1036.  6 files changed, 69 insertions(+), 69 deletions(-)
  1037.  
  1038. diff --git a/sys/nfs41sys_driver.c b/sys/nfs41sys_driver.c
  1039. index fd72305..a0328e9 100644
  1040. --- a/sys/nfs41sys_driver.c
  1041. +++ b/sys/nfs41sys_driver.c
  1042. @@ -129,10 +129,11 @@ PRDBSS_DEVICE_OBJECT nfs41_dev;
  1043.  
  1044.  
  1045.  KEVENT upcallEvent;
  1046. -FAST_MUTEX upcallLock, downcallLock, fcblistLock;
  1047. -FAST_MUTEX openOwnerLock;
  1048. -FAST_MUTEX offloadcontextLock;
  1049. -nfs41_offloadcontext_list offloadcontext_list;
  1050. +nfs41_updowncall_list upcalllist;
  1051. +nfs41_updowncall_list downcalllist;
  1052. +nfs41_fcb_list openlist;
  1053. +
  1054. +nfs41_offloadcontext_list offloadcontextlist;
  1055.  
  1056.  LONGLONG xid = 0;
  1057.  LONG open_owner_id = 1;
  1058. @@ -692,7 +693,7 @@ VOID nfs41_remove_fcb_entry(
  1059.  {
  1060.      PLIST_ENTRY pEntry;
  1061.      nfs41_fcb_list_entry *cur;
  1062. -    ExAcquireFastMutexUnsafe(&fcblistLock);
  1063. +    ExAcquireFastMutexUnsafe(&openlist.lock);
  1064.  
  1065.      pEntry = openlist.head.Flink;
  1066.      while (!IsListEmpty(&openlist.head)) {
  1067. @@ -715,7 +716,7 @@ VOID nfs41_remove_fcb_entry(
  1068.          }
  1069.          pEntry = pEntry->Flink;
  1070.      }
  1071. -    ExReleaseFastMutexUnsafe(&fcblistLock);
  1072. +    ExReleaseFastMutexUnsafe(&openlist.lock);
  1073.  }
  1074.  
  1075.  static
  1076. @@ -726,7 +727,7 @@ VOID nfs41_invalidate_fobx_entry(
  1077.      nfs41_fcb_list_entry *cur;
  1078.      __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(pFobx);
  1079.  
  1080. -    ExAcquireFastMutexUnsafe(&fcblistLock);
  1081. +    ExAcquireFastMutexUnsafe(&openlist.lock);
  1082.  
  1083.      pEntry = openlist.head.Flink;
  1084.      while (!IsListEmpty(&openlist.head)) {
  1085. @@ -749,7 +750,7 @@ VOID nfs41_invalidate_fobx_entry(
  1086.          }
  1087.          pEntry = pEntry->Flink;
  1088.      }
  1089. -    ExReleaseFastMutexUnsafe(&fcblistLock);
  1090. +    ExReleaseFastMutexUnsafe(&openlist.lock);
  1091.  }
  1092.  
  1093.  NTSTATUS nfs41_Flush(
  1094. @@ -795,7 +796,7 @@ VOID nfs41_update_fcb_list(
  1095.  {
  1096.      PLIST_ENTRY pEntry;
  1097.      nfs41_fcb_list_entry *cur;
  1098. -    ExAcquireFastMutexUnsafe(&fcblistLock);
  1099. +    ExAcquireFastMutexUnsafe(&openlist.lock);
  1100.      pEntry = openlist.head.Flink;
  1101.      while (!IsListEmpty(&openlist.head)) {
  1102.          cur = (nfs41_fcb_list_entry *)CONTAINING_RECORD(pEntry,
  1103. @@ -822,7 +823,7 @@ VOID nfs41_update_fcb_list(
  1104.          }
  1105.          pEntry = pEntry->Flink;
  1106.      }
  1107. -    ExReleaseFastMutexUnsafe(&fcblistLock);
  1108. +    ExReleaseFastMutexUnsafe(&openlist.lock);
  1109.  }
  1110.  
  1111.  NTSTATUS nfs41_IsValidDirectory (
  1112. @@ -910,7 +911,7 @@ void enable_caching(
  1113.  
  1114.      RxChangeBufferingState((PSRV_OPEN)SrvOpen, ULongToPtr(flag), 1);
  1115.  
  1116. -    ExAcquireFastMutexUnsafe(&fcblistLock);
  1117. +    ExAcquireFastMutexUnsafe(&openlist.lock);
  1118.      pEntry = openlist.head.Flink;
  1119.      while (!IsListEmpty(&openlist.head)) {
  1120.          cur = (nfs41_fcb_list_entry *)CONTAINING_RECORD(pEntry,
  1121. @@ -950,7 +951,7 @@ void enable_caching(
  1122.          nfs41_fobx->deleg_type = 0;
  1123.      }
  1124.  out_release_fcblistlock:
  1125. -    ExReleaseFastMutexUnsafe(&fcblistLock);
  1126. +    ExReleaseFastMutexUnsafe(&openlist.lock);
  1127.  }
  1128.  
  1129.  NTSTATUS nfs41_CompleteBufferingStateChangeRequest(
  1130. @@ -1238,7 +1239,7 @@ VOID fcbopen_main(PVOID ctx)
  1131.          PLIST_ENTRY pEntry;
  1132.          nfs41_fcb_list_entry *cur;
  1133.          status = KeDelayExecutionThread(KernelMode, TRUE, &timeout);
  1134. -        ExAcquireFastMutexUnsafe(&fcblistLock);
  1135. +        ExAcquireFastMutexUnsafe(&openlist.lock);
  1136.          pEntry = openlist.head.Flink;
  1137.          while (!IsListEmpty(&openlist.head)) {
  1138.              PNFS41_NETROOT_EXTENSION pNetRootContext;
  1139. @@ -1335,7 +1336,7 @@ out:
  1140.              }
  1141.              pEntry = pEntry->Flink;
  1142.          }
  1143. -        ExReleaseFastMutexUnsafe(&fcblistLock);
  1144. +        ExReleaseFastMutexUnsafe(&openlist.lock);
  1145.      }
  1146.  //    DbgEx();
  1147.  }
  1148. @@ -1395,15 +1396,14 @@ NTSTATUS DriverEntry(
  1149.      }
  1150.  
  1151.      KeInitializeEvent(&upcallEvent, SynchronizationEvent, FALSE );
  1152. -    ExInitializeFastMutex(&upcallLock);
  1153. -    ExInitializeFastMutex(&downcallLock);
  1154. -    ExInitializeFastMutex(&openOwnerLock);
  1155. -    ExInitializeFastMutex(&fcblistLock);
  1156. -    ExInitializeFastMutex(&offloadcontextLock);
  1157. -    InitializeListHead(&upcall.head);
  1158. -    InitializeListHead(&downcall.head);
  1159. +    ExInitializeFastMutex(&upcalllist.lock);
  1160. +    ExInitializeFastMutex(&downcalllist.lock);
  1161. +    ExInitializeFastMutex(&openlist.lock);
  1162. +    ExInitializeFastMutex(&offloadcontextlist.lock);
  1163. +    InitializeListHead(&upcalllist.head);
  1164. +    InitializeListHead(&downcalllist.head);
  1165.      InitializeListHead(&openlist.head);
  1166. -    InitializeListHead(&offloadcontext_list.head);
  1167. +    InitializeListHead(&offloadcontextlist.head);
  1168.  #ifdef USE_LOOKASIDELISTS_FOR_UPDOWNCALLENTRY_MEM
  1169.      /*
  1170.       * The |Depth| parameter is unfortunately ignored in Win10,
  1171. diff --git a/sys/nfs41sys_driver.h b/sys/nfs41sys_driver.h
  1172. index 3f8cc68..c96244c 100644
  1173. --- a/sys/nfs41sys_driver.h
  1174. +++ b/sys/nfs41sys_driver.h
  1175. @@ -302,9 +302,11 @@ typedef struct _updowncall_entry {
  1176.  } nfs41_updowncall_entry;
  1177.  
  1178.  typedef struct _updowncall_list {
  1179. +    FAST_MUTEX lock;
  1180.      LIST_ENTRY head;
  1181.  } nfs41_updowncall_list;
  1182. -nfs41_updowncall_list upcall, downcall;
  1183. +extern nfs41_updowncall_list upcalllist;
  1184. +extern nfs41_updowncall_list downcalllist;
  1185.  
  1186.  
  1187.  #define SERVER_NAME_BUFFER_SIZE         1024
  1188. @@ -351,7 +353,8 @@ typedef struct _nfs41_mount_entry {
  1189.  } nfs41_mount_entry;
  1190.  
  1191.  typedef struct _nfs41_mount_list {
  1192. -    LIST_ENTRY head;
  1193. +    FAST_MUTEX  lock;
  1194. +    LIST_ENTRY  head;
  1195.  } nfs41_mount_list;
  1196.  
  1197.  #define nfs41_AddEntry(lock,list,pEntry)                    \
  1198. @@ -409,7 +412,6 @@ typedef struct _NFS41_NETROOT_EXTENSION {
  1199.      NODE_BYTE_SIZE          NodeByteSize;
  1200.      DWORD                   nfs41d_version;
  1201.      BOOLEAN                 mounts_init;
  1202. -    FAST_MUTEX              mountLock;
  1203.      nfs41_mount_list        mounts;
  1204.  } NFS41_NETROOT_EXTENSION, *PNFS41_NETROOT_EXTENSION;
  1205.  #define NFS41GetNetRootExtension(pNetRoot)      \
  1206. @@ -503,15 +505,16 @@ typedef struct _nfs41_fcb_list_entry {
  1207.  } nfs41_fcb_list_entry;
  1208.  
  1209.  typedef struct _nfs41_fcb_list {
  1210. -    LIST_ENTRY head;
  1211. +    FAST_MUTEX  lock;
  1212. +    LIST_ENTRY  head;
  1213.  } nfs41_fcb_list;
  1214. -nfs41_fcb_list openlist;
  1215. +extern nfs41_fcb_list openlist;
  1216.  
  1217.  typedef struct _nfs41_offloadcontext_list {
  1218. +    FAST_MUTEX lock;
  1219.      LIST_ENTRY head;
  1220.  } nfs41_offloadcontext_list;
  1221. -extern nfs41_offloadcontext_list offloadcontext_list;
  1222. -extern FAST_MUTEX offloadcontextLock;
  1223. +extern nfs41_offloadcontext_list offloadcontextlist;
  1224.  
  1225.  typedef enum _NULMRX_STORAGE_TYPE_CODES {
  1226.      NTC_NFS41_DEVICE_EXTENSION      =   (NODE_TYPE_CODE)0xFC00,
  1227. @@ -542,10 +545,6 @@ typedef enum _NULMRX_STORAGE_TYPE_CODES {
  1228.  
  1229.  /* Globals */
  1230.  extern KEVENT upcallEvent;
  1231. -extern FAST_MUTEX upcallLock;
  1232. -extern FAST_MUTEX downcallLock;
  1233. -extern FAST_MUTEX fcblistLock;
  1234. -extern FAST_MUTEX openOwnerLock;
  1235.  
  1236.  extern LONGLONG xid;
  1237.  extern LONG open_owner_id;
  1238. diff --git a/sys/nfs41sys_fsctl.c b/sys/nfs41sys_fsctl.c
  1239. index 546a95b..0d7da28 100644
  1240. --- a/sys/nfs41sys_fsctl.c
  1241. +++ b/sys/nfs41sys_fsctl.c
  1242. @@ -904,17 +904,17 @@ void nfs41_remove_offloadcontext_for_fobx(
  1243.      offloadcontext_entry *cur, *found = NULL;
  1244.      __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(pFobx);
  1245.  
  1246. -    ExAcquireFastMutexUnsafe(&offloadcontextLock);
  1247. +    ExAcquireFastMutexUnsafe(&offloadcontextlist.lock);
  1248.  
  1249. -    pEntry = offloadcontext_list.head.Flink;
  1250. -    while (!IsListEmpty(&offloadcontext_list.head)) {
  1251. +    pEntry = offloadcontextlist.head.Flink;
  1252. +    while (!IsListEmpty(&offloadcontextlist.head)) {
  1253.          cur = (offloadcontext_entry *)CONTAINING_RECORD(pEntry,
  1254.              offloadcontext_entry, next);
  1255.          if (cur->src_fobx == nfs41_fobx) {
  1256.              found = cur;
  1257.              break;
  1258.          }
  1259. -        if (pEntry->Flink == &offloadcontext_list.head) {
  1260. +        if (pEntry->Flink == &offloadcontextlist.head) {
  1261.              break;
  1262.          }
  1263.          pEntry = pEntry->Flink;
  1264. @@ -940,7 +940,7 @@ void nfs41_remove_offloadcontext_for_fobx(
  1265.              pFobx);
  1266.      }
  1267.  
  1268. -    ExReleaseFastMutexUnsafe(&offloadcontextLock);
  1269. +    ExReleaseFastMutexUnsafe(&offloadcontextlist.lock);
  1270.  }
  1271.  
  1272.  static
  1273. @@ -950,17 +950,17 @@ offloadcontext_entry *nfs41_find_offloadcontext_acquireshared(
  1274.      PLIST_ENTRY pEntry;
  1275.      offloadcontext_entry *cur, *found = NULL;
  1276.  
  1277. -    ExAcquireFastMutexUnsafe(&offloadcontextLock);
  1278. +    ExAcquireFastMutexUnsafe(&offloadcontextlist.lock);
  1279.  
  1280. -    pEntry = offloadcontext_list.head.Flink;
  1281. -    while (!IsListEmpty(&offloadcontext_list.head)) {
  1282. +    pEntry = offloadcontextlist.head.Flink;
  1283. +    while (!IsListEmpty(&offloadcontextlist.head)) {
  1284.          cur = (offloadcontext_entry *)CONTAINING_RECORD(pEntry,
  1285.              offloadcontext_entry, next);
  1286.          if (cur == unvalidated_oce) {
  1287.              found = cur;
  1288.              break;
  1289.          }
  1290. -        if (pEntry->Flink == &offloadcontext_list.head) {
  1291. +        if (pEntry->Flink == &offloadcontextlist.head) {
  1292.              break;
  1293.          }
  1294.          pEntry = pEntry->Flink;
  1295. @@ -972,14 +972,14 @@ offloadcontext_entry *nfs41_find_offloadcontext_acquireshared(
  1296.              unvalidated_oce);
  1297.  
  1298.          (void)ExAcquireSharedStarveExclusive(&found->resource, TRUE);
  1299. -        ExReleaseFastMutexUnsafe(&offloadcontextLock);
  1300. +        ExReleaseFastMutexUnsafe(&offloadcontextlist.lock);
  1301.          return found;
  1302.      }
  1303.      else {
  1304.          DbgP("nfs41_find_offloadcontext_acquireshared(unvalidated_oce=0x%p): "
  1305.              "Nothing found.\n",
  1306.              unvalidated_oce);
  1307. -        ExReleaseFastMutexUnsafe(&offloadcontextLock);
  1308. +        ExReleaseFastMutexUnsafe(&offloadcontextlist.lock);
  1309.          return NULL;
  1310.      }
  1311.  }
  1312. @@ -1065,7 +1065,8 @@ NTSTATUS nfs41_OffloadRead(
  1313.      oro->TransferLength = ori->CopyLength;
  1314.      (void)memcpy(&oro->Token[0], &oce->token, sizeof(oce->token));
  1315.  
  1316. -    nfs41_AddEntry(offloadcontextLock, offloadcontext_list, oce);
  1317. +    nfs41_AddEntry(offloadcontextlist.lock,
  1318. +        offloadcontextlist, oce);
  1319.  
  1320.      RxContext->CurrentIrp->IoStatus.Status = status = STATUS_SUCCESS;
  1321.      RxContext->InformationToReturn = sizeof(FSCTL_OFFLOAD_READ_OUTPUT);
  1322. diff --git a/sys/nfs41sys_mount.c b/sys/nfs41sys_mount.c
  1323. index 069c682..6d652b6 100644
  1324. --- a/sys/nfs41sys_mount.c
  1325. +++ b/sys/nfs41sys_mount.c
  1326. @@ -986,7 +986,7 @@ NTSTATUS nfs41_CreateVNetRoot(
  1327.  
  1328.          status = STATUS_NFS_SHARE_NOT_MOUNTED;
  1329.  
  1330. -        ExAcquireFastMutexUnsafe(&pNetRootContext->mountLock);
  1331. +        ExAcquireFastMutexUnsafe(&pNetRootContext->mounts.lock);
  1332.          pEntry = &pNetRootContext->mounts.head;
  1333.          pEntry = pEntry->Flink;
  1334.          while (pEntry != NULL) {
  1335. @@ -1039,7 +1039,7 @@ NTSTATUS nfs41_CreateVNetRoot(
  1336.              status = STATUS_SUCCESS;
  1337.          }
  1338.  #endif /* NFS41_DRIVER_SYSTEM_LUID_MOUNTS_ARE_GLOBAL */
  1339. -        ExReleaseFastMutexUnsafe(&pNetRootContext->mountLock);
  1340. +        ExReleaseFastMutexUnsafe(&pNetRootContext->mounts.lock);
  1341.  
  1342.          if (status != STATUS_SUCCESS) {
  1343.              DbgP("No existing mount found, "
  1344. @@ -1111,13 +1111,13 @@ NTSTATUS nfs41_CreateVNetRoot(
  1345.  #ifdef DEBUG_MOUNT
  1346.          DbgP("Initializing mount array\n");
  1347.  #endif
  1348. -        ExInitializeFastMutex(&pNetRootContext->mountLock);
  1349. +        ExInitializeFastMutex(&pNetRootContext->mounts.lock);
  1350.          InitializeListHead(&pNetRootContext->mounts.head);
  1351.          pNetRootContext->mounts_init = TRUE;
  1352.      } else {
  1353.          PLIST_ENTRY pEntry;
  1354.  
  1355. -        ExAcquireFastMutexUnsafe(&pNetRootContext->mountLock);
  1356. +        ExAcquireFastMutexUnsafe(&pNetRootContext->mounts.lock);
  1357.          pEntry = &pNetRootContext->mounts.head;
  1358.          pEntry = pEntry->Flink;
  1359.          while (pEntry != NULL) {
  1360. @@ -1167,7 +1167,7 @@ NTSTATUS nfs41_CreateVNetRoot(
  1361.                  break;
  1362.              pEntry = pEntry->Flink;
  1363.          }
  1364. -        ExReleaseFastMutexUnsafe(&pNetRootContext->mountLock);
  1365. +        ExReleaseFastMutexUnsafe(&pNetRootContext->mounts.lock);
  1366.  #ifdef DEBUG_MOUNT
  1367.          if (!found_matching_flavor)
  1368.              DbgP("Didn't find matching security flavor\n");
  1369. @@ -1180,7 +1180,7 @@ NTSTATUS nfs41_CreateVNetRoot(
  1370.          &pVNetRootContext->FsAttrs);
  1371.      if (status != STATUS_SUCCESS) {
  1372.          BOOLEAN MountsEmpty;
  1373. -        nfs41_IsListEmpty(pNetRootContext->mountLock,
  1374. +        nfs41_IsListEmpty(pNetRootContext->mounts.lock,
  1375.              pNetRootContext->mounts, MountsEmpty);
  1376.          if (!found_existing_mount && MountsEmpty)
  1377.              pNetRootContext->mounts_init = FALSE;
  1378. @@ -1219,7 +1219,7 @@ NTSTATUS nfs41_CreateVNetRoot(
  1379.           * \\server@port\@(pubnfs4|nfs4)\path mounts later
  1380.           */
  1381.          copy_nfs41_mount_config(&entry->Config, Config);
  1382. -        nfs41_AddEntry(pNetRootContext->mountLock,
  1383. +        nfs41_AddEntry(pNetRootContext->mounts.lock,
  1384.              pNetRootContext->mounts, entry);
  1385.      } else if (!found_matching_flavor) {
  1386.          ASSERT(existing_mount != NULL);
  1387. @@ -1390,7 +1390,7 @@ NTSTATUS nfs41_FinalizeNetRoot(
  1388.      }
  1389.  
  1390.      do {
  1391. -        nfs41_GetFirstMountEntry(pNetRootContext->mountLock,
  1392. +        nfs41_GetFirstMountEntry(pNetRootContext->mounts.lock,
  1393.              pNetRootContext->mounts, mount_tmp);
  1394.          if (mount_tmp == NULL)
  1395.              break;
  1396. @@ -1432,7 +1432,7 @@ NTSTATUS nfs41_FinalizeNetRoot(
  1397.                  print_error("nfs41_unmount RPCSEC_GSS_KRB5P failed with %d\n",
  1398.                              status);
  1399.          }
  1400. -        nfs41_RemoveEntry(pNetRootContext->mountLock, mount_tmp);
  1401. +        nfs41_RemoveEntry(pNetRootContext->mounts.lock, mount_tmp);
  1402.          RxFreePool(mount_tmp);
  1403.          mount_tmp = NULL;
  1404.      } while (1);
  1405. @@ -1441,10 +1441,10 @@ NTSTATUS nfs41_FinalizeNetRoot(
  1406.  
  1407.      // check if there is anything waiting in the upcall or downcall queue
  1408.      do {
  1409. -        nfs41_GetFirstEntry(upcallLock, upcall, tmp);
  1410. +        nfs41_GetFirstEntry(upcalllist.lock, upcalllist, tmp);
  1411.          if (tmp != NULL) {
  1412.              DbgP("Removing entry from upcall list\n");
  1413. -            nfs41_RemoveEntry(upcallLock, tmp);
  1414. +            nfs41_RemoveEntry(upcalllist.lock, tmp);
  1415.              tmp->status = STATUS_INSUFFICIENT_RESOURCES;
  1416.              (void)KeSetEvent(&tmp->cond, IO_NFS41FS_INCREMENT, FALSE);
  1417.          } else
  1418. @@ -1452,10 +1452,10 @@ NTSTATUS nfs41_FinalizeNetRoot(
  1419.      } while (1);
  1420.  
  1421.      do {
  1422. -        nfs41_GetFirstEntry(downcallLock, downcall, tmp);
  1423. +        nfs41_GetFirstEntry(downcalllist.lock, downcalllist, tmp);
  1424.          if (tmp != NULL) {
  1425.              DbgP("Removing entry from downcall list\n");
  1426. -            nfs41_RemoveEntry(downcallLock, tmp);
  1427. +            nfs41_RemoveEntry(downcalllist.lock, tmp);
  1428.              tmp->status = STATUS_INSUFFICIENT_RESOURCES;
  1429.              (void)KeSetEvent(&tmp->cond, IO_NFS41FS_INCREMENT, FALSE);
  1430.          } else
  1431. diff --git a/sys/nfs41sys_openclose.c b/sys/nfs41sys_openclose.c
  1432. index f7bd38d..2833d1f 100644
  1433. --- a/sys/nfs41sys_openclose.c
  1434. +++ b/sys/nfs41sys_openclose.c
  1435. @@ -1051,7 +1051,7 @@ retry_on_link:
  1436.              oentry->session = pVNetRootContext->session;
  1437.              oentry->ChangeTime = entry->ChangeTime;
  1438.              oentry->skip = FALSE;
  1439. -            nfs41_AddEntry(fcblistLock, openlist, oentry);
  1440. +            nfs41_AddEntry(openlist.lock, openlist, oentry);
  1441.          }
  1442.      }
  1443.  
  1444. diff --git a/sys/nfs41sys_updowncall.c b/sys/nfs41sys_updowncall.c
  1445. index fc523a4..cf9e4cf 100644
  1446. --- a/sys/nfs41sys_updowncall.c
  1447. +++ b/sys/nfs41sys_updowncall.c
  1448. @@ -518,7 +518,7 @@ NTSTATUS nfs41_UpcallWaitForReply(
  1449.  
  1450.      FsRtlEnterFileSystem();
  1451.  
  1452. -    nfs41_AddEntry(upcallLock, upcall, entry);
  1453. +    nfs41_AddEntry(upcalllist.lock, upcalllist, entry);
  1454.      (void)KeSetEvent(&upcallEvent, IO_NFS41FS_INCREMENT, FALSE);
  1455.  
  1456.      if (entry->async_op)
  1457. @@ -567,7 +567,7 @@ retry_wait:
  1458.          ExReleaseFastMutexUnsafe(&entry->lock);
  1459.          goto out;
  1460.      }
  1461. -    nfs41_RemoveEntry(downcallLock, entry);
  1462. +    nfs41_RemoveEntry(downcalllist.lock, entry);
  1463.  
  1464.  out:
  1465.      FsRtlExitFileSystem();
  1466. @@ -585,14 +585,14 @@ NTSTATUS nfs41_upcall(
  1467.      FsRtlEnterFileSystem();
  1468.  
  1469.  process_upcall:
  1470. -    nfs41_RemoveFirst(upcallLock, upcall, pEntry);
  1471. +    nfs41_RemoveFirst(upcalllist.lock, upcalllist, pEntry);
  1472.      if (pEntry) {
  1473.          nfs41_updowncall_entry *entry;
  1474.  
  1475.          entry = (nfs41_updowncall_entry *)CONTAINING_RECORD(pEntry,
  1476.                      nfs41_updowncall_entry, next);
  1477.          ExAcquireFastMutexUnsafe(&entry->lock);
  1478. -        nfs41_AddEntry(downcallLock, downcall, entry);
  1479. +        nfs41_AddEntry(downcalllist.lock, downcalllist, entry);
  1480.          status = handle_upcall(RxContext, entry, &len);
  1481.          if (status == STATUS_SUCCESS &&
  1482.                  entry->state == NFS41_WAITING_FOR_UPCALL)
  1483. @@ -665,8 +665,8 @@ NTSTATUS nfs41_downcall(
  1484.  
  1485.      unmarshal_nfs41_header(tmp, &buf);
  1486.  
  1487. -    ExAcquireFastMutexUnsafe(&downcallLock);
  1488. -    pEntry = &downcall.head;
  1489. +    ExAcquireFastMutexUnsafe(&downcalllist.lock);
  1490. +    pEntry = &downcalllist.head;
  1491.      pEntry = pEntry->Flink;
  1492.      while (pEntry != NULL) {
  1493.          cur = (nfs41_updowncall_entry *)CONTAINING_RECORD(pEntry,
  1494. @@ -675,11 +675,11 @@ NTSTATUS nfs41_downcall(
  1495.              found = 1;
  1496.              break;
  1497.          }
  1498. -        if (pEntry->Flink == &downcall.head)
  1499. +        if (pEntry->Flink == &downcalllist.head)
  1500.              break;
  1501.          pEntry = pEntry->Flink;
  1502.      }
  1503. -    ExReleaseFastMutexUnsafe(&downcallLock);
  1504. +    ExReleaseFastMutexUnsafe(&downcalllist.lock);
  1505.      SeStopImpersonatingClient();
  1506.      if (!found) {
  1507.          print_error("Didn't find xid=%lld entry\n", tmp->xid);
  1508. @@ -728,7 +728,7 @@ NTSTATUS nfs41_downcall(
  1509.              break;
  1510.          }
  1511.          ExReleaseFastMutexUnsafe(&cur->lock);
  1512. -        nfs41_RemoveEntry(downcallLock, cur);
  1513. +        nfs41_RemoveEntry(downcalllist.lock, cur);
  1514.          nfs41_UpcallDestroy(cur);
  1515.          status = STATUS_UNSUCCESSFUL;
  1516.          goto out_free;
  1517. @@ -806,7 +806,7 @@ NTSTATUS nfs41_downcall(
  1518.                          map_readwrite_errors(cur->status);
  1519.                      cur->u.ReadWrite.rxcontext->InformationToReturn = 0;
  1520.                  }
  1521. -                nfs41_RemoveEntry(downcallLock, cur);
  1522. +                nfs41_RemoveEntry(downcalllist.lock, cur);
  1523.                  RxLowIoCompletion(cur->u.ReadWrite.rxcontext);
  1524.                  nfs41_UpcallDestroy(cur);
  1525.                  break;
  1526. --
  1527. 2.45.1
  1528.  
  1529. From 3b1789d27c56e109fd49dce920a953770019de23 Mon Sep 17 00:00:00 2001
  1530. From: Roland Mainz <roland.mainz@nrubsig.org>
  1531. Date: Wed, 20 Aug 2025 16:21:21 +0200
  1532. Subject: [PATCH 4/6] sys,tests: Reenable |FORCE_POSIX_SEMANTICS_DELETE| so rm
  1533.  -Rf on read-only dir can report an error
  1534.  
  1535. Reenable |FORCE_POSIX_SEMANTICS_DELETE| so rm -Rf on read-only dir
  1536. can report an error.
  1537.  
  1538. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  1539. ---
  1540. sys/nfs41sys_buildconfig.h | 16 +++++++++++++++-
  1541.  sys/nfs41sys_fileinfo.c    | 14 ++++++++++++++
  1542.  tests/manual_testing.txt   | 11 ++++++++++-
  1543.  3 files changed, 39 insertions(+), 2 deletions(-)
  1544.  
  1545. diff --git a/sys/nfs41sys_buildconfig.h b/sys/nfs41sys_buildconfig.h
  1546. index a8c10ad..21eb201 100644
  1547. --- a/sys/nfs41sys_buildconfig.h
  1548. +++ b/sys/nfs41sys_buildconfig.h
  1549. @@ -26,8 +26,22 @@
  1550.  /*
  1551.   * |FORCE_POSIX_SEMANTICS_DELETE| is for bug-by-bug compatibility with the
  1552.   * original Windows NFSv3 filesystem driver
  1553. + *
  1554. + * If we ever disable this e must make sure that this works and still returns
  1555. + * errors to the caller, e.g. rm -Rf on a readonly dir must return an
  1556. + * error.
  1557. + *
  1558. + * Example:
  1559. + * ---- snip ----
  1560. + * $ ksh93 -c 'mkdir d1 && touch d1/f1 && chmod -R a-w d1 &&
  1561. + *      if rm -Rf d1 ; then echo "# Test failed" ; else
  1562. + *      echo "# Test OK" ; fi'
  1563. + * rm: cannot remove 'd1': Permission denied
  1564. + * # Test OK
  1565. + * ---- snip ----
  1566.   */
  1567. -// #define FORCE_POSIX_SEMANTICS_DELETE 1
  1568. +#define FORCE_POSIX_SEMANTICS_DELETE 1
  1569. +
  1570.  #define USE_STACK_FOR_DOWNCALL_UPDOWNCALLENTRY_MEM 1
  1571.  
  1572.  #if (NTDDI_VERSION >= NTDDI_WIN10_VB)
  1573. diff --git a/sys/nfs41sys_fileinfo.c b/sys/nfs41sys_fileinfo.c
  1574. index 82b8054..cea57ad 100644
  1575. --- a/sys/nfs41sys_fileinfo.c
  1576. +++ b/sys/nfs41sys_fileinfo.c
  1577. @@ -696,6 +696,20 @@ NTSTATUS nfs41_SetFileInformation(
  1578.                  }
  1579.  #else
  1580.                  /* Do Win32 delete-on-close */
  1581. +                /*
  1582. +                 * We must make sure that this works and still returns errors
  1583. +                 * to the caller, e.g. rm -Rf on a readonly dir must return
  1584. +                 * an error.
  1585. +                 *
  1586. +                 * Example:
  1587. +                 * ---- snip ----
  1588. +                 * $ ksh93 -c 'mkdir d1 && touch d1/f1 && chmod -R a-w d1 &&
  1589. +                 *      if rm -Rf d1 ; then echo "# Test failed" ; else
  1590. +                 *      echo "# Test OK" ; fi'
  1591. +                 * rm: cannot remove 'd1': Permission denied
  1592. +                 * # Test OK
  1593. +                s * ---- snip ----
  1594. +                 */
  1595.                  nfs41_fcb->DeletePending = TRUE;
  1596.                  nfs41_fcb->StandardInfo.DeletePending = TRUE;
  1597.  #endif /* FORCE_POSIX_SEMANTICS_DELETE */
  1598. diff --git a/tests/manual_testing.txt b/tests/manual_testing.txt
  1599. index c9a5fd6..38bd49b 100644
  1600. --- a/tests/manual_testing.txt
  1601. +++ b/tests/manual_testing.txt
  1602. @@ -1,5 +1,5 @@
  1603.  #
  1604. -# ms-nfs41-client manual testing sequence, 2025-08-16
  1605. +# ms-nfs41-client manual testing sequence, 2025-08-20
  1606.  #
  1607.  # Draft version, needs to be turned into automated tests
  1608.  # if possible
  1609. @@ -771,6 +771,15 @@ bash /usr/share/msnfs41client/tests/misc/wintartest_comparewinvsgnu001.bash myta
  1610.  bash -c 'set -o errexit ; rm -Rf sillytestdir ; mkdir sillytestdir ; cd sillytestdir ; touch sillytest ; ( command exec {n}<"sillytest" ; printf "fd=%d\n" $n ; sleep 10) & sleep 1 ; ls -la ; cmd /C "del sillytest" ; ls -la ; if [[ "$(ls -1 .nfs*)" != "" ]] ; then echo "# test OK" ; else echo "# test FAILED" ; fi ; wait'
  1611.  bash -c 'set -o errexit ; rm -Rf sillytestdir ; mkdir sillytestdir ; cd sillytestdir ; touch sillytest ; ( command exec {n}<"sillytest" ; printf "fd=%d\n" $n ; sleep 10) & sleep 1 ; ls -la ; rm -f sillytest ; ls -la ; if [[ "$(ls -1 .nfs*)" != "" ]] ; then echo "# test OK" ; else echo "# test FAILED" ; fi ; wait'
  1612.  
  1613. +#
  1614. +# Test rm on read-only dir
  1615. +#
  1616. +---- snip ----
  1617. +$ ksh93 -c 'mkdir d1 && touch d1/f1 && chmod -R a-w d1 && if rm -Rf d1 ; then echo "# Test failed, rm on readonly dir should return error" ; else echo "# Test OK" ; fi'
  1618. +rm: cannot remove 'd1': Permission denied
  1619. +# Test OK
  1620. +---- snip ----
  1621. +
  1622.  #
  1623.  # WSL install for testing
  1624.  #
  1625. --
  1626. 2.45.1
  1627.  
  1628. From 180c8865f1f810b9dd5e4f7398b8142843f87542 Mon Sep 17 00:00:00 2001
  1629. From: Roland Mainz <roland.mainz@nrubsig.org>
  1630. Date: Wed, 20 Aug 2025 17:43:10 +0200
  1631. Subject: [PATCH 5/6] daemon: Align upcall/downcall buffers to cache line size
  1632.  
  1633. Align upcall/downcall buffers to cache line size for slightly
  1634. better performance.
  1635.  
  1636. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  1637. ---
  1638. daemon/nfs41_daemon.c | 5 ++++-
  1639.  1 file changed, 4 insertions(+), 1 deletion(-)
  1640.  
  1641. diff --git a/daemon/nfs41_daemon.c b/daemon/nfs41_daemon.c
  1642. index c798c28..fb919c5 100644
  1643. --- a/daemon/nfs41_daemon.c
  1644. +++ b/daemon/nfs41_daemon.c
  1645. @@ -137,7 +137,10 @@ static unsigned int nfsd_worker_thread_main(void *args)
  1646.      HANDLE pipe;
  1647.      // buffer used to process upcall, assumed to be fixed size.
  1648.      // if we ever need to handle non-cached IO, need to make it dynamic
  1649. -    unsigned char outbuf[UPCALL_BUF_SIZE], inbuf[UPCALL_BUF_SIZE];
  1650. +    // we use 128byte alignment to make sure we start at the beginning
  1651. +    // of a cache line
  1652. +    _declspec(align(128)) unsigned char outbuf[UPCALL_BUF_SIZE];
  1653. +    _declspec(align(128)) unsigned char inbuf[UPCALL_BUF_SIZE];
  1654.      DWORD inbuf_len, outbuf_len;
  1655.      nfs41_upcall upcall;
  1656.  
  1657. --
  1658. 2.45.1
  1659.  
  1660. From c5e498c2f270b931dc51c4467fa1078631ef2b5a Mon Sep 17 00:00:00 2001
  1661. From: Roland Mainz <roland.mainz@nrubsig.org>
  1662. Date: Wed, 20 Aug 2025 17:53:30 +0200
  1663. Subject: [PATCH 6/6] daemon: Fix |DPRINTF()| debug level constant in
  1664.  |duplicate_sparsefile()|
  1665.  
  1666. Fix |DPRINTF()| debug level constant in |duplicate_sparsefile()|.
  1667.  
  1668. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  1669. ---
  1670. daemon/fsctl.c | 2 +-
  1671.  1 file changed, 1 insertion(+), 1 deletion(-)
  1672.  
  1673. diff --git a/daemon/fsctl.c b/daemon/fsctl.c
  1674. index fbbd8aa..c730ab2 100644
  1675. --- a/daemon/fsctl.c
  1676. +++ b/daemon/fsctl.c
  1677. @@ -543,7 +543,7 @@ int duplicate_sparsefile(nfs41_opcodes opcode,
  1678.  
  1679.  #ifdef LINUX_NFSD_SEEK_NXIO_BUG_WORKAROUND
  1680.          if (data_seek_status == NFS4ERR_NXIO) {
  1681. -            DPRINTF(QARLVL, ("SEEK_DATA failed with NFS4ERR_NXIO\n"));
  1682. +            DPRINTF(DDLVL, ("SEEK_DATA failed with NFS4ERR_NXIO\n"));
  1683.              goto out;
  1684.          }
  1685.  #endif /* LINUX_NFSD_SEEK_NXIO_BUG_WORKAROUND */
  1686. --
  1687. 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