pastebin - collaborative debugging tool
rovema.kpaste.net RSS


msnfs41client: Patches for case-insenstive support, fattr4_offline, softwarecompat-notes, tests+misc, 2025-09-17
Posted by Anonymous on Wed 17th Sep 2025 15:51
raw | new post

  1. From 816e47a15aa13c2c5b53048cbd974e17fd0f97c5 Mon Sep 17 00:00:00 2001
  2. From: Roland Mainz <roland.mainz@nrubsig.org>
  3. Date: Mon, 15 Sep 2025 18:30:12 +0200
  4. Subject: [PATCH 01/10] daemon,include,nfs41_build_features.h,sys: Add hack to
  5.  override case_preserving+case_insensitive options for testing
  6.  
  7. Add hack to override case_preserving+case_insensitive options for
  8. testing, which provides two mount options "forcecasepreserving=0/1"
  9. and "forcecaseinsensitive=0/1" to override the
  10. |FATTR4_WORD0_CASE_INSENSITIVE| and/or |FATTR4_WORD0_CASE_PRESERVING|
  11. attributes obtained from the NFS server.
  12.  
  13. This is only a HACK to circumvent a Linux nfsd bug which always returns
  14. |FATTR4_WORD0_CASE_INSENSITIVE==0|&&|FATTR4_WORD0_CASE_PRESERVING==1|,
  15. even for FAT.
  16.  
  17. Since Windows file accesses via UNC path make mount options basically
  18. per-server mounts from a single server can only have one set of
  19. "forcecasepreserving=0/1" and "forcecaseinsensitive=0/1" options.
  20.  
  21. As workaround you can use use the same hostname but a different port
  22. number (as for ms-nfs41-client the port number of part of the UNC path),
  23. e.g. ssh on the NFS server itself to forward port 2050 to 2049 to
  24. pretent it is a different server:
  25. $ ssh -L '*:2050:localhost:2049' root@localhost 'printf "# forwarding...\n" ; sleep $((60*60*24*366*99))' #
  26. and then connect the NFS client to port 2050 on the NFS server.
  27.  
  28. This build option should be removed as soon as the Linux nfsd bug has
  29. been fixed.
  30.  
  31. THIS OPTION MUST NOT BE USED ON PRODUCTION SYSTEMS!!
  32.  
  33. Reported-by: Cedric Blancher <cedric.blancher@gmail.com>
  34. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  35. ---
  36. daemon/mount.c            | 24 +++++++++++
  37.  daemon/namespace.c        | 32 +++++++++++++--
  38.  daemon/nfs41.h            | 15 +++++++
  39.  daemon/nfs41_client.c     | 22 +++++++++++
  40.  daemon/nfs41_superblock.c | 38 ++++++++++++------
  41.  daemon/upcall.h           |  4 ++
  42.  include/nfs41_driver.h    |  6 +++
  43.  nfs41_build_features.h    | 26 ++++++++++++
  44.  sys/nfs41sys_driver.h     | 12 ++++++
  45.  sys/nfs41sys_mount.c      | 83 ++++++++++++++++++++++++++++++++++-----
  46.  10 files changed, 238 insertions(+), 24 deletions(-)
  47.  
  48. diff --git a/daemon/mount.c b/daemon/mount.c
  49. index 89f555c..db3327d 100644
  50. --- a/daemon/mount.c
  51. +++ b/daemon/mount.c
  52. @@ -55,13 +55,33 @@ static int parse_mount(unsigned char *buffer, uint32_t length, nfs41_upcall *upc
  53.      if (status) goto out;
  54.      status = safe_read(&buffer, &length, &args->nfsvers, sizeof(DWORD));
  55.      if (status) goto out;
  56. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  57. +    status = safe_read(&buffer, &length, &args->force_case_preserving,
  58. +        sizeof(tristate_bool));
  59. +    if (status) goto out;
  60. +    status = safe_read(&buffer, &length, &args->force_case_insensitive,
  61. +        sizeof(tristate_bool));
  62. +    if (status) goto out;
  63. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  64.  
  65. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  66. +    DPRINTF(1, ("parsing NFS41_SYSOP_MOUNT: hostport='%s' root='%s' "
  67. +        "sec_flavor='%s' rsize=%d wsize=%d use_nfspubfh=%d "
  68. +        "nfsvers=%d force_case_preserving=%d force_case_insensitive=%d\n",
  69. +        args->hostport, args->path, secflavorop2name(args->sec_flavor),
  70. +        args->rsize, args->wsize, args->use_nfspubfh,
  71. +        args->nfsvers,
  72. +        (int)args->force_case_preserving,
  73. +        (int)args->force_case_insensitive));
  74. +#else
  75.      DPRINTF(1, ("parsing NFS41_SYSOP_MOUNT: hostport='%s' root='%s' "
  76.          "sec_flavor='%s' rsize=%d wsize=%d use_nfspubfh=%d "
  77.          "nfsvers=%d\n",
  78.          args->hostport, args->path, secflavorop2name(args->sec_flavor),
  79.          args->rsize, args->wsize, args->use_nfspubfh,
  80.          args->nfsvers));
  81. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  82. +
  83.      return status;
  84.  out:
  85.      DPRINTF(1, ("parsing NFS41_SYSOP_MOUNT: failed %d\n", status));
  86. @@ -183,6 +203,10 @@ static int handle_mount(void *daemon_context, nfs41_upcall *upcall)
  87.          // create root
  88.          status = nfs41_root_create(hostname, port,
  89.              args->use_nfspubfh?true:false,
  90. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  91. +            args->force_case_preserving,
  92. +            args->force_case_insensitive,
  93. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  94.              args->nfsvers,
  95.              args->sec_flavor,
  96.              args->wsize + WRITE_OVERHEAD, args->rsize + READ_OVERHEAD, &root);
  97. diff --git a/daemon/namespace.c b/daemon/namespace.c
  98. index 89a0e0b..7ff6ee9 100644
  99. --- a/daemon/namespace.c
  100. +++ b/daemon/namespace.c
  101. @@ -43,6 +43,10 @@ int nfs41_root_create(
  102.      IN const char *name,
  103.      IN uint32_t port,
  104.      IN bool use_nfspubfh,
  105. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  106. +    IN tristate_bool force_case_preserving,
  107. +    IN tristate_bool force_case_insensitive,
  108. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  109.      IN DWORD nfsvers,
  110.      IN uint32_t sec_flavor,
  111.      IN uint32_t wsize,
  112. @@ -52,10 +56,23 @@ int nfs41_root_create(
  113.      int status = NO_ERROR;
  114.      nfs41_root *root;
  115.  
  116. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  117.      DPRINTF(NSLVL,
  118.          ("--> nfs41_root_create(name='%s', port=%d, "
  119. -            "use_nfspubfh=%d, nfsvers=%d)\n",
  120. -            name, port, (int)use_nfspubfh, (int)nfsvers));
  121. +            "use_nfspubfh=%d, "
  122. +            "force_case_preserving=%d force_case_insensitive=%d"
  123. +            "nfsvers=%d)\n",
  124. +            name, port, (int)use_nfspubfh,
  125. +            (int)force_case_preserving, (int)force_case_insensitive,
  126. +            (int)nfsvers));
  127. +#else
  128. +    DPRINTF(NSLVL,
  129. +        ("--> nfs41_root_create(name='%s', port=%d, "
  130. +            "use_nfspubfh=%d, "
  131. +            "nfsvers=%d)\n",
  132. +            name, port, (int)use_nfspubfh,
  133. +            (int)nfsvers));
  134. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  135.  
  136.      root = calloc(1, sizeof(nfs41_root));
  137.      if (root == NULL) {
  138. @@ -65,6 +82,11 @@ int nfs41_root_create(
  139.  
  140.      list_init(&root->clients);
  141.      root->use_nfspubfh = use_nfspubfh;
  142. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  143. +    root->force_case_preserving = force_case_preserving;
  144. +    root->force_case_insensitive = force_case_insensitive;
  145. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  146. +
  147.      /*
  148.       * nfs41_root_mount_addrs() will enable NFSv4.2 features (like
  149.       * |OP_READ_PLUS|) after NFSv4.x minor version autonegitiation
  150. @@ -98,7 +120,11 @@ int nfs41_root_create(
  151.  
  152.      /* generate a unique client_owner */
  153.      status = nfs41_client_owner(name, port, root->nfsminorvers,
  154. -        use_nfspubfh, sec_flavor, &root->client_owner);
  155. +        use_nfspubfh,
  156. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  157. +        root->force_case_preserving, root->force_case_insensitive,
  158. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  159. +        sec_flavor, &root->client_owner);
  160.      if (status) {
  161.          eprintf("nfs41_client_owner() failed with %d\n", status);
  162.          free(root);
  163. diff --git a/daemon/nfs41.h b/daemon/nfs41.h
  164. index 670ce9c..bdebbd9 100644
  165. --- a/daemon/nfs41.h
  166. +++ b/daemon/nfs41.h
  167. @@ -25,8 +25,10 @@
  168.  #define __NFS41__ 1
  169.  
  170.  #include <stdbool.h>
  171. +#include "nfs41_build_features.h"
  172.  #include "util.h"
  173.  #include "list.h"
  174. +#include "nfs41_driver.h" /* needed for |tristate_bool| */
  175.  
  176.  
  177.  struct __nfs41_session;
  178. @@ -320,6 +322,10 @@ typedef struct __nfs41_root {
  179.      uint32_t uid;
  180.      uint32_t gid;
  181.      DWORD sec_flavor;
  182. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  183. +    tristate_bool force_case_preserving;
  184. +    tristate_bool force_case_insensitive;
  185. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  186.  } nfs41_root;
  187.  
  188.  
  189. @@ -328,12 +334,17 @@ int nfs41_root_create(
  190.      IN const char *name,
  191.      IN uint32_t port,
  192.      IN bool use_nfspubfh,
  193. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  194. +    IN tristate_bool force_case_preserving,
  195. +    IN tristate_bool force_case_insensitive,
  196. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  197.      IN DWORD nfsvers,
  198.      IN uint32_t sec_flavor,
  199.      IN uint32_t wsize,
  200.      IN uint32_t rsize,
  201.      OUT nfs41_root **root_out);
  202.  
  203. +
  204.  void nfs41_root_ref(
  205.      IN nfs41_root *root);
  206.  
  207. @@ -449,6 +460,10 @@ int nfs41_client_owner(
  208.      IN uint32_t port,
  209.      IN int nfsminorvers,
  210.      IN bool use_nfspubfh,
  211. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  212. +    IN tristate_bool force_case_preserving,
  213. +    IN tristate_bool force_case_insensitive,
  214. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  215.      IN uint32_t sec_flavor,
  216.      OUT client_owner4 *owner);
  217.  
  218. diff --git a/daemon/nfs41_client.c b/daemon/nfs41_client.c
  219. index 7510f05..f585ee9 100644
  220. --- a/daemon/nfs41_client.c
  221. +++ b/daemon/nfs41_client.c
  222. @@ -371,6 +371,10 @@ int nfs41_client_owner(
  223.      IN uint32_t port,
  224.      IN int nfsminorvers,
  225.      IN bool use_nfspubfh,
  226. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  227. +    IN tristate_bool force_case_preserving,
  228. +    IN tristate_bool force_case_insensitive,
  229. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  230.      IN uint32_t sec_flavor,
  231.      OUT client_owner4 *owner)
  232.  {
  233. @@ -443,6 +447,24 @@ int nfs41_client_owner(
  234.          goto out_hash;
  235.      }
  236.  
  237. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  238. +    if (!CryptHashData(hash,
  239. +        (const BYTE*)&force_case_preserving,
  240. +        (DWORD)sizeof(force_case_preserving), 0)) {
  241. +        status = GetLastError();
  242. +        eprintf("CryptHashData() failed with %d\n", status);
  243. +        goto out_hash;
  244. +    }
  245. +
  246. +    if (!CryptHashData(hash,
  247. +        (const BYTE*)&force_case_insensitive,
  248. +        (DWORD)sizeof(force_case_insensitive), 0)) {
  249. +        status = GetLastError();
  250. +        eprintf("CryptHashData() failed with %d\n", status);
  251. +        goto out_hash;
  252. +    }
  253. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  254. +
  255.      if (!CryptHashData(hash, (const BYTE*)&sec_flavor, (DWORD)sizeof(sec_flavor), 0)) {
  256.          status = GetLastError();
  257.          eprintf("CryptHashData() failed with %d\n", status);
  258. diff --git a/daemon/nfs41_superblock.c b/daemon/nfs41_superblock.c
  259. index 23b4024..626ef04 100644
  260. --- a/daemon/nfs41_superblock.c
  261. +++ b/daemon/nfs41_superblock.c
  262. @@ -81,6 +81,7 @@ static int get_superblock_attrs(
  263.      IN nfs41_superblock *superblock,
  264.      IN nfs41_path_fh *file)
  265.  {
  266. +    nfs41_root *root = session->client->root;
  267.      bool_t supports_named_attrs;
  268.      int status;
  269.      bitmap4 attr_request;
  270. @@ -125,22 +126,35 @@ static int get_superblock_attrs(
  271.      superblock->link_support = info.link_support;
  272.      superblock->symlink_support = info.symlink_support;
  273.      superblock->ea_support = supports_named_attrs;
  274. -//#define TEST_LINUX_FORCE_FAT32 1
  275. -#ifdef TEST_LINUX_FORCE_FAT32
  276. -    /*
  277. -     * Testing-ONLY: Force FAT32 behaviour, because Linux nfsd returns
  278. -     * |info.case_insensitive==0| even on FAT32
  279. -     * Windows Server 2019 nfsd and OpenText nfsd do this correctly
  280. -     */
  281. -    DPRINTF(0, ("get_superblock_attrs: TEST_LINUX_FORCE_FAT32 enabled!\n"));
  282. -    superblock->case_preserving = 0/*info.case_preserving*/;
  283. -    superblock->case_insensitive = 1/*info.case_insensitive*/;
  284. +
  285. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  286. +    if (root->force_case_preserving == TRISTATE_BOOL_NOT_SET) {
  287. +        superblock->case_preserving = info.case_preserving;
  288. +    }
  289. +    else {
  290. +        superblock->case_preserving =
  291. +            root->force_case_preserving?1:0;
  292. +        DPRINTF(0,
  293. +            ("get_superblock_attrs: OVERRIDING case_preserving to %d\n",
  294. +            (int)superblock->case_preserving));
  295. +    }
  296. +    if (root->force_case_insensitive == TRISTATE_BOOL_NOT_SET) {
  297. +        superblock->case_insensitive = info.case_insensitive;
  298. +    }
  299. +    else {
  300. +        superblock->case_insensitive =
  301. +            root->force_case_insensitive?1:0;
  302. +        DPRINTF(0,
  303. +            ("get_superblock_attrs: OVERRIDING case_insensitive to %d\n",
  304. +            (int)superblock->case_insensitive));
  305. +    }
  306.  #else
  307.      superblock->case_preserving = info.case_preserving;
  308.      superblock->case_insensitive = info.case_insensitive;
  309. -#endif /* TEST_FS_FORCE_FAT32 */
  310. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  311. +
  312.      superblock->sparse_file_support = 1; /* always ON for now */
  313. -    if (session->client->root->nfsminorvers >= 2) {
  314. +    if (root->nfsminorvers >= 2) {
  315.          superblock->block_clone_support = 1;
  316.      }
  317.      else {
  318. diff --git a/daemon/upcall.h b/daemon/upcall.h
  319. index 2d829ae..8e099bc 100644
  320. --- a/daemon/upcall.h
  321. +++ b/daemon/upcall.h
  322. @@ -40,6 +40,10 @@ typedef struct __mount_upcall_args {
  323.      DWORD       wsize;
  324.      DWORD       use_nfspubfh;
  325.      DWORD       nfsvers;
  326. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  327. +    tristate_bool force_case_preserving;
  328. +    tristate_bool force_case_insensitive;
  329. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  330.      DWORD       lease_time;
  331.      NFS41_FILE_FS_ATTRIBUTE_INFORMATION FsAttrs;
  332.  } mount_upcall_args;
  333. diff --git a/include/nfs41_driver.h b/include/nfs41_driver.h
  334. index 7a4ddfc..310eeee 100644
  335. --- a/include/nfs41_driver.h
  336. +++ b/include/nfs41_driver.h
  337. @@ -156,4 +156,10 @@ typedef enum _nfs41_start_driver_state {
  338.  #define ERROR_NFS_VERSION_MISMATCH ERROR_REMOTE_FILE_VERSION_MISMATCH
  339.  #define STATUS_NFS_VERSION_MISMATCH STATUS_REMOTE_FILE_VERSION_MISMATCH
  340.  
  341. +/* Boolean with three states to cover "not set" */
  342. +typedef signed char tristate_bool;
  343. +#define TRISTATE_BOOL_NOT_SET   (-1)
  344. +#define TRISTATE_BOOL_FALSE     (0)
  345. +#define TRISTATE_BOOL_TRUE      (1)
  346. +
  347.  #endif /* !_NFS41_DRIVER_ */
  348. diff --git a/nfs41_build_features.h b/nfs41_build_features.h
  349. index 38f6372..c2963f4 100644
  350. --- a/nfs41_build_features.h
  351. +++ b/nfs41_build_features.h
  352. @@ -236,4 +236,30 @@
  353.   */
  354.  #define NFS41_DRIVER_HACK_LOCKING_STORAGE32_RANGELOCK_PROBING 1
  355.  
  356. +/*
  357. + * |NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS| - provide mount
  358. + * options "forcecasepreserving=0/1" and "forcecaseinsensitive=0/1"
  359. + * to override |FATTR4_WORD0_CASE_INSENSITIVE|/|FATTR4_WORD0_CASE_PRESERVING|
  360. + * obtained by the NFS server.
  361. + *
  362. + * This is only a HACK to circumvent a Linux nfsd bug which always returns
  363. + * |FATTR4_WORD0_CASE_INSENSITIVE==0|&&|FATTR4_WORD0_CASE_PRESERVING==1|,
  364. + * even for FAT.
  365. + * Since Windows file accesses via UNC path make mount options basically
  366. + * per-server mounts from a single server can only have one set of
  367. + * "forcecasepreserving=0/1" and "forcecaseinsensitive=0/1" options.
  368. + *
  369. + * As workaround you can use use the same hostname but a different port
  370. + * number (as for ms-nfs41-client the port number of part of the UNC path),
  371. + * e.g. ssh on the NFS server itself to forward port 2050 to 2049 to
  372. + * pretent it is a different server:
  373. + * $ ssh -L '*:2050:localhost:2049' root@localhost 'printf "# forwarding...\n" ; sleep $((60*60*24*366*99))' #
  374. + * and then connect the NFS client to port 2050 on the NFS server.
  375. + *
  376. + * This build option should be removed as soon as the Linux nfsd has been
  377. + * fixed.
  378. + * THIS OPTION MUST NOT BE USED ON PRODUCTION SYSTEMS!!
  379. + */
  380. +#define NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS 1
  381. +
  382.  #endif /* !_NFS41_DRIVER_BUILDFEATURES_ */
  383. diff --git a/sys/nfs41sys_driver.h b/sys/nfs41sys_driver.h
  384. index c96244c..df26e64 100644
  385. --- a/sys/nfs41sys_driver.h
  386. +++ b/sys/nfs41sys_driver.h
  387. @@ -201,6 +201,10 @@ typedef struct _updowncall_entry {
  388.              DWORD lease_time;
  389.              DWORD use_nfspubfh;
  390.              DWORD nfsvers;
  391. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  392. +            tristate_bool force_case_preserving;
  393. +            tristate_bool force_case_insensitive;
  394. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  395.          } Mount;
  396.          struct {
  397.              PMDL MdlAddress;
  398. @@ -339,6 +343,10 @@ typedef struct _NFS41_MOUNT_CONFIG {
  399.      DWORD timeout;
  400.      NFS41_MOUNT_CREATEMODE dir_createmode;
  401.      NFS41_MOUNT_CREATEMODE file_createmode;
  402. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  403. +    tristate_bool force_case_preserving;
  404. +    tristate_bool force_case_insensitive;
  405. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  406.  } NFS41_MOUNT_CONFIG, *PNFS41_MOUNT_CONFIG;
  407.  
  408.  typedef struct _nfs41_mount_entry {
  409. @@ -434,6 +442,10 @@ typedef struct _NFS41_V_NET_ROOT_EXTENSION {
  410.      BOOLEAN                 write_thru;
  411.      BOOLEAN                 nocache;
  412.      BOOLEAN                 timebasedcoherency;
  413. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  414. +    tristate_bool           force_case_preserving;
  415. +    tristate_bool           force_case_insensitive;
  416. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  417.  } NFS41_V_NET_ROOT_EXTENSION, *PNFS41_V_NET_ROOT_EXTENSION;
  418.  #define NFS41GetVNetRootExtension(pVNetRoot)      \
  419.          (((pVNetRoot) == NULL) ? NULL :           \
  420. diff --git a/sys/nfs41sys_mount.c b/sys/nfs41sys_mount.c
  421. index 6d652b6..6903b62 100644
  422. --- a/sys/nfs41sys_mount.c
  423. +++ b/sys/nfs41sys_mount.c
  424. @@ -108,7 +108,11 @@ NTSTATUS marshal_nfs41_mount(
  425.      else tmp += *len;
  426.  
  427.      header_len = *len + length_as_utf8(entry->u.Mount.srv_name) +
  428. -        length_as_utf8(entry->u.Mount.root) + 5 * sizeof(DWORD);
  429. +        length_as_utf8(entry->u.Mount.root) + 5 * sizeof(DWORD)
  430. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  431. +        + 2 * sizeof(tristate_bool)
  432. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  433. +        ;
  434.      if (header_len > buf_len) {
  435.          status = STATUS_INSUFFICIENT_RESOURCES;
  436.          goto out;
  437. @@ -126,18 +130,36 @@ NTSTATUS marshal_nfs41_mount(
  438.      RtlCopyMemory(tmp, &entry->u.Mount.use_nfspubfh, sizeof(DWORD));
  439.      tmp += sizeof(DWORD);
  440.      RtlCopyMemory(tmp, &entry->u.Mount.nfsvers, sizeof(DWORD));
  441. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  442. +    tmp += sizeof(DWORD);
  443. +    RtlCopyMemory(tmp, &entry->u.Mount.force_case_preserving,
  444. +        sizeof(tristate_bool));
  445. +    tmp += sizeof(tristate_bool);
  446. +    RtlCopyMemory(tmp, &entry->u.Mount.force_case_insensitive,
  447. +        sizeof(tristate_bool));
  448. +    /* tmp += sizeof(tristate_bool); */
  449. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  450.  
  451.      *len = header_len;
  452.  
  453.  #ifdef DEBUG_MARSHAL_DETAIL
  454.      DbgP("marshal_nfs41_mount: server name='%wZ' mount point='%wZ' "
  455. -         "sec_flavor='%s' rsize=%d wsize=%d use_nfspubfh=%d "
  456. -         "nfsvers=%d\n",
  457. -        entry->u.Mount.srv_name, entry->u.Mount.root,
  458. -         secflavorop2name(entry->u.Mount.sec_flavor),
  459. -         (int)entry->u.Mount.rsize, (int)entry->u.Mount.wsize,
  460. -         (int)entry->u.Mount.use_nfspubfh,
  461. -         (int)entry->u.Mount.nfsvers);
  462. +        "sec_flavor='%s' rsize=%d wsize=%d use_nfspubfh=%d "
  463. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  464. +        "nfsvers=%d force_case_preserving=%d force_case_insensitive=%d\n"
  465. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  466. +        ,
  467. +        entry->u.Mount.srv_name, entry->u.Mount.root,
  468. +        secflavorop2name(entry->u.Mount.sec_flavor),
  469. +        (int)entry->u.Mount.rsize, (int)entry->u.Mount.wsize,
  470. +        (int)entry->u.Mount.use_nfspubfh,
  471. +        (int)entry->u.Mount.nfsvers
  472. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  473. +        ,
  474. +        (int)entry->u.Mount.force_case_preserving,
  475. +        (int)entry->u.Mount.force_case_insensitive
  476. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  477. +         );
  478.  #endif
  479.  out:
  480.      return status;
  481. @@ -262,6 +284,10 @@ NTSTATUS nfs41_mount(
  482.      entry->u.Mount.wsize = config->WriteSize;
  483.      entry->u.Mount.use_nfspubfh = config->use_nfspubfh;
  484.      entry->u.Mount.nfsvers = config->nfsvers;
  485. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  486. +    entry->u.Mount.force_case_preserving = config->force_case_preserving;
  487. +    entry->u.Mount.force_case_insensitive = config->force_case_insensitive;
  488. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  489.      entry->u.Mount.sec_flavor = sec_flavor;
  490.      entry->u.Mount.FsAttrs = FsAttrs;
  491.  
  492. @@ -320,6 +346,10 @@ void nfs41_MountConfig_InitDefaults(
  493.      Config->file_createmode.use_nfsv3attrsea_mode = TRUE;
  494.      Config->file_createmode.mode =
  495.          NFS41_DRIVER_DEFAULT_FILE_CREATE_MODE;
  496. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  497. +    Config->force_case_preserving = TRISTATE_BOOL_NOT_SET;
  498. +    Config->force_case_insensitive = TRISTATE_BOOL_NOT_SET;
  499. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  500.  }
  501.  
  502.  static
  503. @@ -647,6 +677,22 @@ NTSTATUS nfs41_MountConfig_ParseOptions(
  504.                  (int)Config->file_createmode.use_nfsv3attrsea_mode,
  505.                  (int)Config->file_createmode.mode);
  506.          }
  507. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  508. +        else if (wcsncmp(L"forcecasepreserving", Name, NameLen) == 0) {
  509. +            BOOLEAN val;
  510. +            status = nfs41_MountConfig_ParseBoolean(Option, &usValue,
  511. +                FALSE, &val);
  512. +            Config->force_case_preserving =
  513. +                val?TRISTATE_BOOL_TRUE:TRISTATE_BOOL_FALSE;
  514. +        }
  515. +        else if (wcsncmp(L"forcecaseinsensitive", Name, NameLen) == 0) {
  516. +            BOOLEAN val;
  517. +            status = nfs41_MountConfig_ParseBoolean(Option, &usValue,
  518. +                FALSE, &val);
  519. +            Config->force_case_insensitive =
  520. +                val?TRISTATE_BOOL_TRUE:TRISTATE_BOOL_FALSE;
  521. +        }
  522. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  523.          else {
  524.              status = STATUS_INVALID_PARAMETER;
  525.              print_error("Unrecognized option '%ls' -> '%wZ'\n",
  526. @@ -1068,6 +1114,10 @@ NTSTATUS nfs41_CreateVNetRoot(
  527.          "timeout=%d "
  528.          "dir_cmode=(usenfsv3attrs=%d mode=0%o) "
  529.          "file_cmode=(usenfsv3attrs=%d mode=0%o) "
  530. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  531. +        "force_case_preserving=%d "
  532. +        "force_case_insensitive=%d "
  533. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  534.          "}\n",
  535.          &Config->MntPt,
  536.          &Config->SrvName,
  537. @@ -1081,7 +1131,13 @@ NTSTATUS nfs41_CreateVNetRoot(
  538.          Config->dir_createmode.use_nfsv3attrsea_mode?1:0,
  539.          Config->dir_createmode.mode,
  540.          Config->file_createmode.use_nfsv3attrsea_mode?1:0,
  541. -        Config->file_createmode.mode);
  542. +        Config->file_createmode.mode
  543. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  544. +        ,
  545. +        (int)Config->force_case_preserving,
  546. +        (int)Config->force_case_insensitive
  547. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  548. +        );
  549.  
  550.      pVNetRootContext->MntPt.Buffer = pVNetRootContext->mntpt_buffer;
  551.      pVNetRootContext->MntPt.Length = Config->MntPt.Length;
  552. @@ -1096,6 +1152,15 @@ NTSTATUS nfs41_CreateVNetRoot(
  553.          Config->file_createmode.use_nfsv3attrsea_mode;
  554.      pVNetRootContext->file_createmode.mode =
  555.          Config->file_createmode.mode;
  556. +#ifdef NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS
  557. +    /*
  558. +     * FIXME: NO-OP for now, as no one reads
  559. +     * |pVNetRootContext->force_case_preserving| and
  560. +     * |pVNetRootContext->force_case_insensitive| (yet)
  561. +     */
  562. +    pVNetRootContext->force_case_preserving = Config->force_case_preserving;
  563. +    pVNetRootContext->force_case_insensitive = Config->force_case_insensitive;
  564. +#endif /* NFS41_DRIVER_HACK_FORCE_FILENAME_CASE_MOUNTOPTIONS */
  565.  
  566.      status = map_sec_flavor(&Config->SecFlavor, &pVNetRootContext->sec_flavor);
  567.      if (status != STATUS_SUCCESS) {
  568. --
  569. 2.51.0
  570.  
  571. From d26f9e97e72e32e55b74e8a6c849d7a24297014f Mon Sep 17 00:00:00 2001
  572. From: Roland Mainz <roland.mainz@nrubsig.org>
  573. Date: Mon, 15 Sep 2025 18:48:20 +0200
  574. Subject: [PATCH 02/10] daemon: Remove |GetACP()| debug code
  575.  
  576. Remove |GetACP()| debug code
  577.  
  578. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  579. ---
  580. daemon/nfs41_daemon.c | 4 ----
  581.  1 file changed, 4 deletions(-)
  582.  
  583. diff --git a/daemon/nfs41_daemon.c b/daemon/nfs41_daemon.c
  584. index fb919c5..074c291 100644
  585. --- a/daemon/nfs41_daemon.c
  586. +++ b/daemon/nfs41_daemon.c
  587. @@ -805,10 +805,6 @@ VOID ServiceStart(DWORD argc, LPTSTR *argv)
  588.      DPRINTF(0, ("SID cache disabled\n"));
  589.  #endif /* NFS41_DRIVER_SID_CACHE */
  590.  
  591. -#if 1
  592. -    DPRINTF(0, ("wmain: GetACP()=%d\n", (int)GetACP()));
  593. -#endif
  594. -
  595.  #ifdef _DEBUG
  596.      logprintf("NFS client daemon (DEBUG build) %s starting...\n",
  597.          GIT_COMMIT_ID);
  598. --
  599. 2.51.0
  600.  
  601. From e66af0732c0c7cf6994258475859e3234746c6cf Mon Sep 17 00:00:00 2001
  602. From: Roland Mainz <roland.mainz@nrubsig.org>
  603. Date: Mon, 15 Sep 2025 18:52:44 +0200
  604. Subject: [PATCH 03/10] tests: Document how to (easily) test custom NFS TCP
  605.  ports via local ssh port forwaring on the NFS server
  606.  
  607. Document how to (easily) test custom NFS TCP ports via local ssh port
  608. forwaring on the NFS server.
  609.  
  610. Reported-by: Lionel Cons <Lionelcons1972@gmail.com>
  611. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  612. ---
  613. tests/manual_testing.txt | 9 +++++++--
  614.  1 file changed, 7 insertions(+), 2 deletions(-)
  615.  
  616. diff --git a/tests/manual_testing.txt b/tests/manual_testing.txt
  617. index d9e00ca..5659ed5 100644
  618. --- a/tests/manual_testing.txt
  619. +++ b/tests/manual_testing.txt
  620. @@ -1,5 +1,5 @@
  621.  #
  622. -# ms-nfs41-client manual testing sequence, 2025-09-04
  623. +# ms-nfs41-client manual testing sequence, 2025-09-15
  624.  #
  625.  # Draft version, needs to be turned into automated tests
  626.  # if possible
  627. @@ -158,7 +158,12 @@
  628.  #   4. Disable verifier:
  629.  #       $ verifier /reset
  630.  #
  631. -
  632. +# - Testing NFSv4.* with non-standard TCP ports:
  633. +#   1. Run this on the NFS server itself to forward accesses to TCP/2050
  634. +#   to TCP/2049 on the same server machine:
  635. +#   $ ssh -L '*:2050:localhost:2049' root@localhost 'printf "# forwarding...\n" ; sleep $((60*60*24*366))'
  636. +#   2. Connect to the NFS server via nfs://servername1:2050//path/to/nfsexport
  637. +#
  638.  
  639.  #
  640.  # "cthon04" test suite:
  641. --
  642. 2.51.0
  643.  
  644. From 8ff8c9e1b1d226e9e05f31ebf7c9687c338c852b Mon Sep 17 00:00:00 2001
  645. From: Cedric Blancher <cedric.blancher@gmail.com>
  646. Date: Sat, 13 Sep 2025 13:17:38 +0200
  647. Subject: [PATCH 04/10] daemon: Update+comment |FATTR_|+|OP_| for new RFCs
  648.  
  649. Update+comment |FATTR_|+|OP_| for new RFCs.
  650.  
  651. Signed-off-by: Roland Mainz <roland.mainz@nrubsig.org>
  652. ---
  653. daemon/nfs41_const.h | 10 +++++++++-
  654.  daemon/nfs41_ops.h   | 10 +++++++++-
  655.  2 files changed, 18 insertions(+), 2 deletions(-)
  656.  
  657. diff --git a/daemon/nfs41_const.h b/daemon/nfs41_const.h
  658. index 74ad31d..04adcc4 100644
  659. --- a/daemon/nfs41_const.h
  660. +++ b/daemon/nfs41_const.h
  661. @@ -369,7 +369,15 @@ enum nfsstat4 {
  662.  #define FATTR4_WORD2_MODE_SET_MASKED    MAKE_WORD2(74)
  663.  #define FATTR4_WORD2_FS_CHARSET_CAP     MAKE_WORD2(76)
  664.  #define FATTR4_WORD2_CLONE_BLKSIZE      MAKE_WORD2(77)
  665. -
  666. +#define FATTR4_WORD2_SPACE_FREED        MAKE_WORD2(78)
  667. +#define FATTR4_WORD2_CHANGE_ATTR_TYPE   MAKE_WORD2(79)
  668. +#define FATTR4_WORD2_SECURITY_LABEL     MAKE_WORD2(80)
  669. +#define FATTR4_WORD2_MODE_UMASK         MAKE_WORD2(81) /* RFC 8275 */
  670. +#define FATTR4_WORD2_XATTR_SUPPORT      MAKE_WORD2(82) /* RFC 8726+Linux XATTR is incompatible with Win32 EA */
  671. +#define FATTR4_WORD2_OFFLINE            MAKE_WORD2(83) /* RFC 9754 */
  672. +#define FATTR4_WORD2_TIME_DELEG_ACCESS  MAKE_WORD2(84)
  673. +#define FATTR4_WORD2_TIME_DELEG_MODIFY  MAKE_WORD2(85)
  674. +#define FATTR4_WORD2_OPEN_ARGUMENTS     MAKE_WORD2(86)
  675.  
  676.  /*
  677.   * File types
  678. diff --git a/daemon/nfs41_ops.h b/daemon/nfs41_ops.h
  679. index bd93fbf..8f2b022 100644
  680. --- a/daemon/nfs41_ops.h
  681. +++ b/daemon/nfs41_ops.h
  682. @@ -104,7 +104,15 @@ enum nfs_opnum4 {
  683.      OP_WRITE_SAME           = 70,
  684.      OP_CLONE                = 71,
  685.  
  686. -    /* xattr support (RFC8726) */
  687. +    /*
  688. +     * xattr support (RFC8726)
  689. +     * Note that RFC8726 XATTRS represent Linux XATTRs and are
  690. +     * semantically incompatible to Windows EA (extended attributes).
  691. +     * Windows EAs can only properly (i.e. pass the Windows HCL
  692. +     * filesystem tests) be implemented via NFSv4 "named attributes"
  693. +     * (OP_OPENATTR)&co, so these will remain no-ops for Windows NFS
  694. +     * clients conformaing to the Win32 specs.
  695. +     */
  696.      OP_GETXATTR             = 72,
  697.      OP_SETXATTR             = 73,
  698.      OP_LISTXATTRS           = 74,
  699. --
  700. 2.51.0
  701.  
  702. From 088f99b5d8e8745e62a805e8f77f9b701ed80738 Mon Sep 17 00:00:00 2001
  703. From: Roland Mainz <roland.mainz@nrubsig.org>
  704. Date: Mon, 15 Sep 2025 18:59:43 +0200
  705. Subject: [PATCH 05/10] daemon: Implement Win32 |FILE_ATTRIBUTE_OFFLINE| via
  706.  |FATTR4_OFFLINE|
  707.  
  708. Implement Win32 |FILE_ATTRIBUTE_OFFLINE| via RFC9754 |FATTR4_OFFLINE|
  709. (see https://datatracker.ietf.org/doc/rfc9754/).
  710.  
  711. Reported-by: Lionel Cons <Lionelcons1972@gmail.com>
  712. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  713. ---
  714. daemon/daemon_debug.c     |  3 +++
  715.  daemon/fileinfoutil.c     |  5 ++++
  716.  daemon/lookup.c           |  3 ++-
  717.  daemon/name_cache.c       | 48 +++++++++++++++++++++++----------------
  718.  daemon/nfs41_superblock.c |  2 +-
  719.  daemon/nfs41_types.h      |  2 ++
  720.  daemon/nfs41_xdr.c        |  4 ++++
  721.  daemon/open.c             |  1 +
  722.  8 files changed, 47 insertions(+), 21 deletions(-)
  723.  
  724. diff --git a/daemon/daemon_debug.c b/daemon/daemon_debug.c
  725. index 02a0aec..19d9e6e 100644
  726. --- a/daemon/daemon_debug.c
  727. +++ b/daemon/daemon_debug.c
  728. @@ -1147,6 +1147,9 @@ void print_nfs41_file_info(
  729.                  (long)info->clone_blksize);
  730.          }
  731.  
  732. +        if (info->attrmask.arr[2] & FATTR4_WORD2_OFFLINE)
  733. +            PRNFS41FI_FMT("offline=%d, ", (int)info->offline);
  734. +
  735.          p += snprintf(p, (sizeof(buf)-(p-buf)), "} ");
  736.      }
  737.  
  738. diff --git a/daemon/fileinfoutil.c b/daemon/fileinfoutil.c
  739. index 2556c87..3466cfd 100644
  740. --- a/daemon/fileinfoutil.c
  741. +++ b/daemon/fileinfoutil.c
  742. @@ -84,6 +84,8 @@ ULONG nfs_file_info_to_attributes(
  743.          attrs |= FILE_ATTRIBUTE_SYSTEM;
  744.      if (info->archive)
  745.          attrs |= FILE_ATTRIBUTE_ARCHIVE;
  746. +    if (info->offline)
  747. +        attrs |= FILE_ATTRIBUTE_OFFLINE;
  748.  
  749.      /*
  750.       * |FILE_ATTRIBUTE_NORMAL| attribute is only set if no other
  751. @@ -637,6 +639,9 @@ void nfs41_file_info_cpy(
  752.          if (attrmask->arr[2] & FATTR4_WORD2_CLONE_BLKSIZE) {
  753.              dest->clone_blksize = src->clone_blksize;
  754.          }
  755. +        if (attrmask->arr[2] & FATTR4_WORD2_OFFLINE) {
  756. +            dest->offline = src->offline;
  757. +        }
  758.      }
  759.  
  760.      if (flags & NFS41FILEINFOCPY_COPY_SYMLINK_DIR) {
  761. diff --git a/daemon/lookup.c b/daemon/lookup.c
  762. index 4eb6808..d56307d 100644
  763. --- a/daemon/lookup.c
  764. +++ b/daemon/lookup.c
  765. @@ -96,7 +96,7 @@ static void init_component_args(
  766.  {
  767.      uint32_t i;
  768.  
  769. -    args->attr_request.count = 2;
  770. +    args->attr_request.count = 3;
  771.      args->attr_request.arr[0] = FATTR4_WORD0_TYPE
  772.          | FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE
  773.          | FATTR4_WORD0_FSID | FATTR4_WORD0_FILEID
  774. @@ -107,6 +107,7 @@ static void init_component_args(
  775.          | FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_CREATE
  776.          | FATTR4_WORD1_TIME_MODIFY
  777.          | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP;
  778. +    args->attr_request.arr[2] = FATTR4_WORD2_OFFLINE;
  779.  
  780.      args->getrootattr.attr_request = &args->attr_request;
  781.      res->root.path = path;
  782. diff --git a/daemon/name_cache.c b/daemon/name_cache.c
  783. index f025983..1ef1a47 100644
  784. --- a/daemon/name_cache.c
  785. +++ b/daemon/name_cache.c
  786. @@ -77,22 +77,23 @@ static __inline bool_t is_delegation(
  787.      return type == OPEN_DELEGATE_READ || type == OPEN_DELEGATE_WRITE;
  788.  }
  789.  
  790. -#define NC_ATTR_TYPE        (1 <<  0)
  791. -#define NC_ATTR_CHANGE      (1 <<  1)
  792. -#define NC_ATTR_FSID        (1 <<  2)
  793. -#define NC_ATTR_SIZE        (1 <<  3)
  794. -#define NC_ATTR_SPACE_USED  (1 <<  4)
  795. -#define NC_ATTR_HIDDEN      (1 <<  5)
  796. -#define NC_ATTR_ARCHIVE     (1 <<  6)
  797. -#define NC_ATTR_MODE        (1 <<  7)
  798. -#define NC_ATTR_NUMLINKS    (1 <<  8)
  799. -#define NC_ATTR_OWNER       (1 <<  9)
  800. -#define NC_ATTR_OWNER_GROUP (1 << 10)
  801. -#define NC_ATTR_TIME_ACCESS (1 << 11)
  802. -#define NC_ATTR_TIME_CREATE (1 << 12)
  803. -#define NC_ATTR_TIME_MODIFY (1 << 13)
  804. -#define NC_ATTR_SYSTEM      (1 << 14)
  805. -#define NC_ATTR_CLONE_BLKSIZE (1 << 15)
  806. +#define NC_ATTR_TYPE            (1 <<  0)
  807. +#define NC_ATTR_CHANGE          (1 <<  1)
  808. +#define NC_ATTR_FSID            (1 <<  2)
  809. +#define NC_ATTR_SIZE            (1 <<  3)
  810. +#define NC_ATTR_SPACE_USED      (1 <<  4)
  811. +#define NC_ATTR_HIDDEN          (1 <<  5)
  812. +#define NC_ATTR_ARCHIVE         (1 <<  6)
  813. +#define NC_ATTR_SYSTEM          (1 <<  7)
  814. +#define NC_ATTR_OFFLINE         (1 <<  8)
  815. +#define NC_ATTR_MODE            (1 <<  9)
  816. +#define NC_ATTR_NUMLINKS        (1 << 10)
  817. +#define NC_ATTR_OWNER           (1 << 11)
  818. +#define NC_ATTR_OWNER_GROUP     (1 << 12)
  819. +#define NC_ATTR_TIME_ACCESS     (1 << 13)
  820. +#define NC_ATTR_TIME_CREATE     (1 << 14)
  821. +#define NC_ATTR_TIME_MODIFY     (1 << 15)
  822. +#define NC_ATTR_CLONE_BLKSIZE   (1 << 16)
  823.  
  824.  /* attribute cache */
  825.  struct attr_cache_entry {
  826. @@ -114,14 +115,15 @@ struct attr_cache_entry {
  827.      uint32_t                numlinks;
  828.      unsigned                mode : 30;
  829.      unsigned                hidden : 1;
  830. -    unsigned                system : 1;
  831.      unsigned                archive : 1;
  832. -    uint32_t                clone_blksize;
  833. -    util_reltimestamp       expiration;
  834. +    unsigned                system : 1;
  835. +    unsigned                offline : 1;
  836.      unsigned                ref_count : 26;
  837.      unsigned                type : 4;
  838.      unsigned                invalidated : 1;
  839.      unsigned                delegated : 1;
  840. +    uint32_t                clone_blksize;
  841. +    util_reltimestamp       expiration;
  842.      char                    owner[NFS4_FATTR4_OWNER_LIMIT+1];
  843.      char                    owner_group[NFS4_FATTR4_OWNER_LIMIT+1];
  844.  };
  845. @@ -394,6 +396,10 @@ static void attr_cache_update(
  846.              entry->nc_attrs |= NC_ATTR_CLONE_BLKSIZE;
  847.              entry->clone_blksize = info->clone_blksize;
  848.          }
  849. +        if (info->attrmask.arr[2] & FATTR4_WORD2_OFFLINE) {
  850. +            entry->nc_attrs |= NC_ATTR_OFFLINE;
  851. +            entry->offline = info->offline;
  852. +        }
  853.      }
  854.  
  855.      if (is_delegation(delegation))
  856. @@ -489,6 +495,10 @@ static void copy_attrs(
  857.          dst->attrmask.arr[2] |= FATTR4_WORD2_CLONE_BLKSIZE;
  858.          dst->clone_blksize = src->clone_blksize;
  859.      }
  860. +    if (src->nc_attrs & NC_ATTR_OFFLINE) {
  861. +        dst->attrmask.arr[2] |= FATTR4_WORD2_OFFLINE;
  862. +        dst->offline = src->offline;
  863. +    }
  864.  
  865.      if (dst->attrmask.arr[2] != 0) {
  866.          dst->attrmask.count = 3;
  867. diff --git a/daemon/nfs41_superblock.c b/daemon/nfs41_superblock.c
  868. index 626ef04..84d06db 100644
  869. --- a/daemon/nfs41_superblock.c
  870. +++ b/daemon/nfs41_superblock.c
  871. @@ -185,7 +185,7 @@ static int get_superblock_attrs(
  872.          | FATTR4_WORD1_SYSTEM
  873.          | FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_CREATE
  874.          | FATTR4_WORD1_TIME_MODIFY;
  875. -    superblock->default_getattr.arr[2] = 0;
  876. +    superblock->default_getattr.arr[2] = FATTR4_WORD2_OFFLINE;
  877.  
  878.      nfs41_superblock_supported_attrs(superblock, &superblock->default_getattr);
  879.  
  880. diff --git a/daemon/nfs41_types.h b/daemon/nfs41_types.h
  881. index b0dca52..bded8db 100644
  882. --- a/daemon/nfs41_types.h
  883. +++ b/daemon/nfs41_types.h
  884. @@ -4,6 +4,7 @@
  885.   *
  886.   * Olga Kornievskaia <aglo@umich.edu>
  887.   * Casey Bodley <cbodley@umich.edu>
  888. + * Roland Mainz <roland.mainz@nrubsig.org>
  889.   *
  890.   * This library is free software; you can redistribute it and/or modify it
  891.   * under the terms of the GNU Lesser General Public License as published by
  892. @@ -221,6 +222,7 @@ typedef struct __nfs41_file_info {
  893.      bool_t                  hidden;
  894.      bool_t                  system;
  895.      bool_t                  archive;
  896. +    bool_t                  offline;
  897.      uint32_t                clone_blksize;
  898.      bool_t                  case_insensitive;
  899.      bool_t                  case_preserving;
  900. diff --git a/daemon/nfs41_xdr.c b/daemon/nfs41_xdr.c
  901. index c1e22e5..1736ade 100644
  902. --- a/daemon/nfs41_xdr.c
  903. +++ b/daemon/nfs41_xdr.c
  904. @@ -1927,6 +1927,10 @@ static bool_t decode_file_attrs(
  905.              if (!xdr_uint32_t(xdr, &info->clone_blksize))
  906.                  return FALSE;
  907.          }
  908. +        if (attrs->attrmask.arr[2] & FATTR4_WORD2_OFFLINE) {
  909. +            if (!xdr_bool(xdr, &info->offline))
  910. +                return FALSE;
  911. +        }
  912.      }
  913.      return TRUE;
  914.  }
  915. diff --git a/daemon/open.c b/daemon/open.c
  916. index dd10c38..43f753c 100644
  917. --- a/daemon/open.c
  918. +++ b/daemon/open.c
  919. @@ -1021,6 +1021,7 @@ static int handle_open(void *daemon_context, nfs41_upcall *upcall)
  920.          createattrs.hidden = args->file_attrs & FILE_ATTRIBUTE_HIDDEN ? 1 : 0;
  921.          createattrs.system = args->file_attrs & FILE_ATTRIBUTE_SYSTEM ? 1 : 0;
  922.          createattrs.archive = args->file_attrs & FILE_ATTRIBUTE_ARCHIVE ? 1 : 0;
  923. +        /* FIXME: What about |FILE_ATTRIBUTE_OFFLINE| ? */
  924.  
  925.          map_access_2_allowdeny(args->access_mask, args->access_mode,
  926.              args->disposition, &state->share_access, &state->share_deny);
  927. --
  928. 2.51.0
  929.  
  930. From b6584909d927abd24154dd090c006ddd5e1593ae Mon Sep 17 00:00:00 2001
  931. From: Roland Mainz <roland.mainz@nrubsig.org>
  932. Date: Wed, 17 Sep 2025 15:19:17 +0200
  933. Subject: [PATCH 06/10] daemon,sys: Make case-insensitive name matching
  934.  per-superblock
  935.  
  936. Make case-insensitive name matching per-superblock instead of per-server,
  937. to allow that a single server can export both case-sensitive and
  938. case-insensitive filesystems.
  939.  
  940. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  941. ---
  942. daemon/lookup.c           |  15 ++--
  943.  daemon/mount.c            |   7 +-
  944.  daemon/name_cache.c       | 139 ++++++++++++++++++++++++--------------
  945.  daemon/name_cache.h       |  10 +--
  946.  daemon/nfs41_compound.c   |   5 +-
  947.  daemon/nfs41_ops.c        |   9 ++-
  948.  daemon/nfs41_ops.h        |   1 +
  949.  daemon/nfs41_superblock.c |  19 ++----
  950.  daemon/open.c             |  29 +++++++-
  951.  daemon/readdir.c          |   9 ++-
  952.  daemon/setattr.c          |   4 ++
  953.  daemon/symlink.c          |   5 +-
  954.  daemon/upcall.h           |   1 +
  955.  daemon/util.h             |   6 ++
  956.  sys/nfs41sys_driver.h     |   1 +
  957.  sys/nfs41sys_openclose.c  |  17 +++++
  958.  16 files changed, 198 insertions(+), 79 deletions(-)
  959.  
  960. diff --git a/daemon/lookup.c b/daemon/lookup.c
  961. index d56307d..dfff849 100644
  962. --- a/daemon/lookup.c
  963. +++ b/daemon/lookup.c
  964. @@ -231,7 +231,9 @@ static int server_lookup(
  965.          /* add the file handle and attributes to the name cache */
  966.          bitmap4_cpy(&res->getrootattr.info->attrmask,
  967.              &res->getrootattr.obj_attributes.attrmask);
  968. -        nfs41_name_cache_insert(session_name_cache(session), path, &name,
  969. +        nfs41_name_cache_insert(session_name_cache(session),
  970. +            BIT2BOOL(dir->fh.superblock->case_insensitive),
  971. +            path, &name,
  972.              &dir->fh, res->getrootattr.info, NULL, OPEN_DELEGATE_NONE);
  973.      }
  974.      file = dir;
  975. @@ -254,7 +256,9 @@ static int server_lookup(
  976.              if (parent_out) *parent_out = file;
  977.          } else if (res->lookup[i].status == NFS4ERR_NOENT) {
  978.              /* insert a negative lookup entry */
  979. -            nfs41_name_cache_insert(session_name_cache(session), path,
  980. +            nfs41_name_cache_insert(session_name_cache(session),
  981. +                BIT2BOOL(dir->fh.superblock->case_insensitive),
  982. +                path,
  983.                  args->lookup[i].name, NULL, NULL, NULL, OPEN_DELEGATE_NONE);
  984.          }
  985.          status = res->lookup[i].status;     if (status) break;
  986. @@ -282,6 +286,7 @@ static int server_lookup(
  987.          bitmap4_cpy(&res->getattr[i].info->attrmask,
  988.              &res->getattr[i].obj_attributes.attrmask);
  989.          nfs41_name_cache_insert(session_name_cache(session),
  990. +            BIT2BOOL(parent->fh.superblock->case_insensitive),
  991.              path, args->lookup[i].name, &res->file[i].fh,
  992.              res->getattr[i].info, NULL, OPEN_DELEGATE_NONE);
  993.  
  994. @@ -463,6 +468,7 @@ out:
  995.  int nfs41_lookup(
  996.      IN nfs41_root *root,
  997.      IN nfs41_session *session,
  998. +    IN bool casesensitive,
  999.      IN OUT nfs41_abs_path *path_inout,
  1000.      OUT OPTIONAL nfs41_path_fh *parent_out,
  1001.      OUT OPTIONAL nfs41_path_fh *target_out,
  1002. @@ -496,7 +502,7 @@ int nfs41_lookup(
  1003.      if (target_out == NULL) target_out = &target;
  1004.      parent_out->fh.len = target_out->fh.len = 0;
  1005.  
  1006. -    status = nfs41_name_cache_lookup(cache, path_pos, path_end, &path_pos,
  1007. +    status = nfs41_name_cache_lookup(cache, casesensitive, path_pos, path_end, &path_pos,
  1008.          &parent_out->fh, &target_out->fh, info_out, &negative);
  1009.      if (status == NO_ERROR || negative)
  1010.          goto out;
  1011. @@ -538,7 +544,8 @@ int nfs41_lookup(
  1012.          if (session_out) *session_out = new_session;
  1013.  
  1014.          /* look up the new path */
  1015. -        status = nfs41_lookup(root, new_session, path_inout,
  1016. +        status = nfs41_lookup(root, new_session,
  1017. +            casesensitive, path_inout,
  1018.              parent_out, target_out, info_out, session_out);
  1019.      }
  1020.  out:
  1021. diff --git a/daemon/mount.c b/daemon/mount.c
  1022. index db3327d..d1f2e9e 100644
  1023. --- a/daemon/mount.c
  1024. +++ b/daemon/mount.c
  1025. @@ -234,8 +234,13 @@ static int handle_mount(void *daemon_context, nfs41_upcall *upcall)
  1026.      }
  1027.      path.len = (unsigned short)strlen(path.path);
  1028.  
  1029. -    // look up the mount path, and fail if it doesn't exist
  1030. +    /*
  1031. +     * look up the mount path, and fail if it doesn't exist
  1032. +     * (The lookup is done case-sensitive, but will work correctly
  1033. +     * with case mixing if the exported filesystem is case-insensitive)
  1034. +     */
  1035.      status = nfs41_lookup(root, client->session,
  1036. +        false,
  1037.          &path, NULL, &file, NULL, NULL);
  1038.      if (status) {
  1039.          eprintf("nfs41_lookup('%s') failed with %d\n", path.path, status);
  1040. diff --git a/daemon/name_cache.c b/daemon/name_cache.c
  1041. index 1ef1a47..1351a58 100644
  1042. --- a/daemon/name_cache.c
  1043. +++ b/daemon/name_cache.c
  1044. @@ -530,7 +530,13 @@ struct name_cache_entry {
  1045.  };
  1046.  #define NAME_ENTRY_SIZE sizeof(struct name_cache_entry)
  1047.  
  1048. -static int name_cmp(struct name_cache_entry *lhs, struct name_cache_entry *rhs);
  1049. +/*
  1050. + * FIXME: We use a thread-local variable |name_cmp| here because we do not know
  1051. + * an (easy) way to do a |RB_GENERATE()| with two diffrent name comparisation
  1052. + * functions (|name_cmp_case_sensitive()| and |name_cmp_case_insensitive()|)
  1053. + */
  1054. +__declspec(thread) static
  1055. +int (*name_cmp)(struct name_cache_entry *, struct name_cache_entry *) = NULL;
  1056.  
  1057.  RB_GENERATE(name_tree, name_cache_entry, rbnode, name_cmp)
  1058.  
  1059. @@ -545,7 +551,6 @@ struct nfs41_name_cache {
  1060.      uint32_t                delegations;
  1061.      uint32_t                max_delegations;
  1062.      SRWLOCK                 lock;
  1063. -    bool                    casesensitivesearch;
  1064.      UCollator               *icu_coll;
  1065.  };
  1066.  
  1067. @@ -579,7 +584,8 @@ int icu_strcmpcoll(UCollator *coll, const char *str1, const char *str2, int32_t
  1068.  }
  1069.  
  1070.  static
  1071. -int name_cmp(struct name_cache_entry *lhs, struct name_cache_entry *rhs)
  1072. +int name_cmp_case_sensitive(
  1073. +    struct name_cache_entry *lhs, struct name_cache_entry *rhs)
  1074.  {
  1075.      const int diff = rhs->component_len - lhs->component_len;
  1076.      int res;
  1077. @@ -589,43 +595,65 @@ int name_cmp(struct name_cache_entry *lhs, struct name_cache_entry *rhs)
  1078.  
  1079.      unsigned short clen = lhs->component_len;
  1080.  
  1081. -    if (lhs->name_cache->casesensitivesearch) {
  1082. -        /* FIXME: Can we use |memcmp()| here ? */
  1083. -        res = strncmp(lhs->component, rhs->component, clen);
  1084. -    }
  1085. -    else {
  1086. -        res = icu_strcmpcoll(lhs->name_cache->icu_coll,
  1087. -            lhs->component, rhs->component, (int32_t)clen);
  1088. +    /* FIXME: Can we use |memcmp()| here ? */
  1089. +    res = strncmp(lhs->component, rhs->component, clen);
  1090. +
  1091. +    return res;
  1092. +}
  1093. +
  1094. +static
  1095. +int name_cmp_case_insensitive(
  1096. +    struct name_cache_entry *lhs, struct name_cache_entry *rhs)
  1097. +{
  1098. +    const int diff = rhs->component_len - lhs->component_len;
  1099. +    int res;
  1100. +
  1101. +    if (diff != 0)
  1102. +        return diff;
  1103. +
  1104. +    unsigned short clen = lhs->component_len;
  1105. +
  1106. +    res = icu_strcmpcoll(lhs->name_cache->icu_coll,
  1107. +        lhs->component, rhs->component, (int32_t)clen);
  1108.  
  1109.  //#define DEBUG_CASEINSENSITIVE_NAMECMP 1
  1110.  #ifdef DEBUG_CASEINSENSITIVE_NAMECMP
  1111. -        int casesensitive_res;
  1112. -
  1113. -        /* FIXME: Can we use |memcmp()| here ? */
  1114. -        casesensitive_res = strncmp(lhs->component, rhs->component,
  1115. -            clen);
  1116. -
  1117. -        if (res != casesensitive_res) {
  1118. -            /*
  1119. -             * Explicitly call |clen| "byte_clen" in the output to make sure
  1120. -             * people do not get confused, e.g. if |<Ae><ae>| has the same
  1121. -             * terminal character width as "dir1
  1122. -             */
  1123. -            DPRINTF(0,
  1124. -                ("name_cmp: "
  1125. -                    "(lhs='%.*s',rhs='%.*s',byte_clen=%d), "
  1126. -                    "res(=%d) != casesensitive_res(=%d)\n",
  1127. -                    (int)clen, lhs->component,
  1128. -                    (int)clen, rhs->component,
  1129. -                    (int)clen,
  1130. -                    res, casesensitive_res));
  1131. -        }
  1132. -#endif /* DEBUG_CASEINSENSITIVE_NAMECMP */
  1133. +    int casesensitive_res;
  1134. +
  1135. +    /* FIXME: Can we use |memcmp()| here ? */
  1136. +    casesensitive_res = strncmp(lhs->component, rhs->component,
  1137. +        clen);
  1138. +
  1139. +    if (res != casesensitive_res) {
  1140. +        /*
  1141. +         * Explicitly call |clen| "byte_clen" in the output to make sure
  1142. +         * people do not get confused, e.g. if |<Ae><ae>| has the same
  1143. +         * terminal character width as "dir1
  1144. +         */
  1145. +        DPRINTF(0,
  1146. +            ("name_cmp: "
  1147. +                "(lhs='%.*s',rhs='%.*s',byte_clen=%d), "
  1148. +                "res(=%d) != casesensitive_res(=%d)\n",
  1149. +                (int)clen, lhs->component,
  1150. +                (int)clen, rhs->component,
  1151. +                (int)clen,
  1152. +                res, casesensitive_res));
  1153.      }
  1154. +#endif /* DEBUG_CASEINSENSITIVE_NAMECMP */
  1155.  
  1156.      return res;
  1157.  }
  1158.  
  1159. +#define NC_SET_NAMECMP(ciss) \
  1160. +    { name_cmp = (ciss)?name_cmp_case_insensitive:name_cmp_case_sensitive; }
  1161. +#define NC_CLEAR_NAMECMP() \
  1162. +    { \
  1163. +        name_cmp = \
  1164. +            (int (*)(struct name_cache_entry *, struct name_cache_entry *))NULL; \
  1165. +    }
  1166. +
  1167. +
  1168. +
  1169.  /* internal name cache functions used by the public name cache interface;
  1170.   * these functions expect the caller to hold a lock on the cache */
  1171.  
  1172. @@ -1004,13 +1032,6 @@ int nfs41_name_cache_create(
  1173.          goto out;
  1174.      }
  1175.  
  1176. -    /*
  1177. -     * We need to set this to the real value later, once we have the value
  1178. -     * of the |FATTR4_WORD0_CASE_INSENSITIVE| attribute...
  1179. -     * FIXME: This is suboptimal...
  1180. -     */
  1181. -    cache->casesensitivesearch = false;
  1182. -
  1183.      UErrorCode xstatus = U_ZERO_ERROR;
  1184.      cache->icu_coll = ucol_open("en_US", &xstatus);
  1185.      if (U_FAILURE(xstatus)) {
  1186. @@ -1086,16 +1107,6 @@ int nfs41_name_cache_free(
  1187.      return status;
  1188.  }
  1189.  
  1190. -void nfs41_name_cache_set_casesensitivesearch(
  1191. -    IN struct nfs41_name_cache *cache,
  1192. -    IN bool casesensitivesearch)
  1193. -{
  1194. -    cache->casesensitivesearch = casesensitivesearch;
  1195. -    DPRINTF(1,
  1196. -        ("nfs41_name_cache_set_casesensitivesearch: casesensitivesearch=%d\n",
  1197. -        (int)casesensitivesearch));
  1198. -}
  1199. -
  1200.  static __inline void copy_fh(
  1201.      OUT nfs41_fh *dst,
  1202.      IN OPTIONAL const struct name_cache_entry *src)
  1203. @@ -1108,6 +1119,7 @@ static __inline void copy_fh(
  1204.  
  1205.  int nfs41_name_cache_lookup(
  1206.      IN struct nfs41_name_cache *cache,
  1207. +    IN bool caseinsensitivesearch,
  1208.      IN const char *path,
  1209.      IN const char *path_end,
  1210.      OUT OPTIONAL const char **remaining_path_out,
  1211. @@ -1120,6 +1132,8 @@ int nfs41_name_cache_lookup(
  1212.      const char *path_pos = path;
  1213.      int status;
  1214.  
  1215. +    NC_SET_NAMECMP(caseinsensitivesearch);
  1216. +
  1217.      AcquireSRWLockShared(&cache->lock);
  1218.  
  1219.      if (!name_cache_enabled(cache)) {
  1220. @@ -1137,6 +1151,7 @@ int nfs41_name_cache_lookup(
  1221.  
  1222.  out_unlock:
  1223.      ReleaseSRWLockShared(&cache->lock);
  1224. +    NC_CLEAR_NAMECMP();
  1225.      if (remaining_path_out) *remaining_path_out = path_pos;
  1226.      return status;
  1227.  }
  1228. @@ -1151,6 +1166,9 @@ int nfs41_attr_cache_lookup(
  1229.  
  1230.      DPRINTF(NCLVL1, ("--> nfs41_attr_cache_lookup(%llu)\n", fileid));
  1231.  
  1232. +    /* No name argument, so no lookups by name */
  1233. +    NC_CLEAR_NAMECMP();
  1234. +
  1235.      AcquireSRWLockShared(&cache->lock);
  1236.  
  1237.      if (!name_cache_enabled(cache)) {
  1238. @@ -1183,6 +1201,9 @@ int nfs41_attr_cache_update(
  1239.  
  1240.      DPRINTF(NCLVL1, ("--> nfs41_attr_cache_update(%llu)\n", fileid));
  1241.  
  1242. +    /* No name argument, so no lookups by name */
  1243. +    NC_CLEAR_NAMECMP();
  1244. +
  1245.      AcquireSRWLockExclusive(&cache->lock);
  1246.  
  1247.      if (!name_cache_enabled(cache)) {
  1248. @@ -1207,6 +1228,7 @@ out_unlock:
  1249.  
  1250.  int nfs41_name_cache_insert(
  1251.      IN struct nfs41_name_cache *cache,
  1252. +    IN bool caseinsensitivesearch,
  1253.      IN const char *path,
  1254.      IN const nfs41_component *name,
  1255.      IN OPTIONAL const nfs41_fh *fh,
  1256. @@ -1220,6 +1242,8 @@ int nfs41_name_cache_insert(
  1257.      DPRINTF(NCLVL1, ("--> nfs41_name_cache_insert('%.*s')\n",
  1258.          name->name + name->len - path, path));
  1259.  
  1260. +    NC_SET_NAMECMP(caseinsensitivesearch);
  1261. +
  1262.      AcquireSRWLockExclusive(&cache->lock);
  1263.  
  1264.      if (!name_cache_enabled(cache)) {
  1265. @@ -1268,6 +1292,7 @@ int nfs41_name_cache_insert(
  1266.  
  1267.  out_unlock:
  1268.      ReleaseSRWLockExclusive(&cache->lock);
  1269. +    NC_CLEAR_NAMECMP();
  1270.  
  1271.      DPRINTF(NCLVL1, ("<-- nfs41_name_cache_insert() returning %d\n",
  1272.          status));
  1273. @@ -1296,6 +1321,7 @@ out_err_deleg:
  1274.  
  1275.  int nfs41_name_cache_delegreturn(
  1276.      IN struct nfs41_name_cache *cache,
  1277. +    IN bool caseinsensitivesearch,
  1278.      IN uint64_t fileid,
  1279.      IN const char *path,
  1280.      IN const nfs41_component *name)
  1281. @@ -1307,6 +1333,8 @@ int nfs41_name_cache_delegreturn(
  1282.      DPRINTF(NCLVL1, ("--> nfs41_name_cache_delegreturn(%llu, '%s')\n",
  1283.          fileid, path));
  1284.  
  1285. +    NC_SET_NAMECMP(caseinsensitivesearch);
  1286. +
  1287.      AcquireSRWLockExclusive(&cache->lock);
  1288.  
  1289.      if (!name_cache_enabled(cache)) {
  1290. @@ -1343,6 +1371,7 @@ int nfs41_name_cache_delegreturn(
  1291.  
  1292.  out_unlock:
  1293.      ReleaseSRWLockExclusive(&cache->lock);
  1294. +    NC_CLEAR_NAMECMP();
  1295.  
  1296.      DPRINTF(NCLVL1, ("<-- nfs41_name_cache_delegreturn() returning %d\n", status));
  1297.      return status;
  1298. @@ -1350,6 +1379,7 @@ out_unlock:
  1299.  
  1300.  int nfs41_name_cache_remove(
  1301.      IN struct nfs41_name_cache *cache,
  1302. +    IN bool caseinsensitivesearch,
  1303.      IN const char *path,
  1304.      IN const nfs41_component *name,
  1305.      IN uint64_t fileid,
  1306. @@ -1361,6 +1391,8 @@ int nfs41_name_cache_remove(
  1307.  
  1308.      DPRINTF(NCLVL1, ("--> nfs41_name_cache_remove('%s')\n", path));
  1309.  
  1310. +    NC_SET_NAMECMP(caseinsensitivesearch);
  1311. +
  1312.      AcquireSRWLockExclusive(&cache->lock);
  1313.  
  1314.      if (!name_cache_enabled(cache)) {
  1315. @@ -1390,6 +1422,7 @@ int nfs41_name_cache_remove(
  1316.  
  1317.  out_unlock:
  1318.      ReleaseSRWLockExclusive(&cache->lock);
  1319. +    NC_CLEAR_NAMECMP();
  1320.  
  1321.      DPRINTF(NCLVL1, ("<-- nfs41_name_cache_remove() returning %d\n", status));
  1322.      return status;
  1323. @@ -1406,6 +1439,7 @@ out_attributes:
  1324.  
  1325.  int nfs41_name_cache_rename(
  1326.      IN struct nfs41_name_cache *cache,
  1327. +    IN bool caseinsensitivesearch,
  1328.      IN const char *src_path,
  1329.      IN const nfs41_component *src_name,
  1330.      IN const change_info4 *src_cinfo,
  1331. @@ -1420,6 +1454,8 @@ int nfs41_name_cache_rename(
  1332.      DPRINTF(NCLVL1, ("--> nfs41_name_cache_rename('%s' to '%s')\n",
  1333.          src_path, dst_path));
  1334.  
  1335. +    NC_SET_NAMECMP(caseinsensitivesearch);
  1336. +
  1337.      AcquireSRWLockExclusive(&cache->lock);
  1338.  
  1339.      if (!name_cache_enabled(cache)) {
  1340. @@ -1506,6 +1542,7 @@ int nfs41_name_cache_rename(
  1341.  
  1342.  out_unlock:
  1343.      ReleaseSRWLockExclusive(&cache->lock);
  1344. +    NC_CLEAR_NAMECMP();
  1345.  
  1346.      DPRINTF(NCLVL1, ("<-- nfs41_name_cache_rename() returning %d\n", status));
  1347.      return status;
  1348. @@ -1659,6 +1696,7 @@ static __inline uint32_t max_putfh_components(
  1349.  
  1350.  int nfs41_name_cache_remove_stale(
  1351.      IN struct nfs41_name_cache *cache,
  1352. +    IN bool caseinsensitivesearch,
  1353.      IN nfs41_session *session,
  1354.      IN nfs41_abs_path *path)
  1355.  {
  1356. @@ -1669,6 +1707,8 @@ int nfs41_name_cache_remove_stale(
  1357.      uint32_t count, index;
  1358.      int status = NO_ERROR;
  1359.  
  1360. +    NC_SET_NAMECMP(caseinsensitivesearch);
  1361. +
  1362.      AcquireSRWLockShared(&cache->lock);
  1363.  
  1364.      /* if there's no cache, don't check any components */
  1365. @@ -1695,6 +1735,7 @@ int nfs41_name_cache_remove_stale(
  1366.      }
  1367.  
  1368.      ReleaseSRWLockShared(&path->lock);
  1369. +    NC_CLEAR_NAMECMP();
  1370.  
  1371.      return status;
  1372.  }
  1373. \ No newline at end of file
  1374. diff --git a/daemon/name_cache.h b/daemon/name_cache.h
  1375. index 8bc5b78..3950983 100644
  1376. --- a/daemon/name_cache.h
  1377. +++ b/daemon/name_cache.h
  1378. @@ -56,15 +56,12 @@ int nfs41_attr_cache_update(
  1379.  int nfs41_name_cache_create(
  1380.      OUT struct nfs41_name_cache **cache_out);
  1381.  
  1382. -void nfs41_name_cache_set_casesensitivesearch(
  1383. -    IN struct nfs41_name_cache *cache,
  1384. -    IN bool casesensitivesearch);
  1385. -
  1386.  int nfs41_name_cache_free(
  1387.      IN OUT struct nfs41_name_cache **cache_out);
  1388.  
  1389.  int nfs41_name_cache_lookup(
  1390.      IN struct nfs41_name_cache *cache,
  1391. +    IN bool caseinsensitivesearch,
  1392.      IN const char *path,
  1393.      IN const char *path_end,
  1394.      OUT OPTIONAL const char **remaining_path_out,
  1395. @@ -75,6 +72,7 @@ int nfs41_name_cache_lookup(
  1396.  
  1397.  int nfs41_name_cache_insert(
  1398.      IN struct nfs41_name_cache *cache,
  1399. +    IN bool caseinsensitivesearch,
  1400.      IN const char *path,
  1401.      IN const nfs41_component *name,
  1402.      IN OPTIONAL const nfs41_fh *fh,
  1403. @@ -84,12 +82,14 @@ int nfs41_name_cache_insert(
  1404.  
  1405.  int nfs41_name_cache_delegreturn(
  1406.      IN struct nfs41_name_cache *cache,
  1407. +    IN bool caseinsensitivesearch,
  1408.      IN uint64_t fileid,
  1409.      IN const char *path,
  1410.      IN const nfs41_component *name);
  1411.  
  1412.  int nfs41_name_cache_remove(
  1413.      IN struct nfs41_name_cache *cache,
  1414. +    IN bool caseinsensitivesearch,
  1415.      IN const char *path,
  1416.      IN const nfs41_component *name,
  1417.      IN uint64_t fileid,
  1418. @@ -97,6 +97,7 @@ int nfs41_name_cache_remove(
  1419.  
  1420.  int nfs41_name_cache_rename(
  1421.      IN struct nfs41_name_cache *cache,
  1422. +    IN bool caseinsensitivesearch,
  1423.      IN const char *src_path,
  1424.      IN const nfs41_component *src_name,
  1425.      IN const change_info4 *src_cinfo,
  1426. @@ -106,6 +107,7 @@ int nfs41_name_cache_rename(
  1427.  
  1428.  int nfs41_name_cache_remove_stale(
  1429.      IN struct nfs41_name_cache *cache,
  1430. +    IN bool caseinsensitivesearch,
  1431.      IN nfs41_session *session,
  1432.      IN nfs41_abs_path *path);
  1433.  
  1434. diff --git a/daemon/nfs41_compound.c b/daemon/nfs41_compound.c
  1435. index 25d2449..4d3b49e 100644
  1436. --- a/daemon/nfs41_compound.c
  1437. +++ b/daemon/nfs41_compound.c
  1438. @@ -371,9 +371,12 @@ retry:
  1439.                  if (argarray[i].op == OP_PUTFH) {
  1440.                      putfh = (nfs41_putfh_args*)argarray[i].arg;
  1441.  
  1442. -                    if (!putfh->in_recovery && putfh->file->path)
  1443. +                    if (!putfh->in_recovery && putfh->file->path) {
  1444.                          nfs41_name_cache_remove_stale(name_cache,
  1445. +                            BIT2BOOL(
  1446. +                                putfh->file->fh.superblock->case_insensitive),
  1447.                              session, putfh->file->path);
  1448. +                    }
  1449.                  }
  1450.              }
  1451.          }
  1452. diff --git a/daemon/nfs41_ops.c b/daemon/nfs41_ops.c
  1453. index 7c017b2..9012db4 100644
  1454. --- a/daemon/nfs41_ops.c
  1455. +++ b/daemon/nfs41_ops.c
  1456. @@ -412,7 +412,9 @@ static void open_update_cache(
  1457.      bitmap4_cpy(&file_attrs->info->attrmask, &file_attrs->obj_attributes.attrmask);
  1458.  retry_cache_insert:
  1459.      AcquireSRWLockShared(&file->path->lock);
  1460. -    status = nfs41_name_cache_insert(cache, file->path->path, &file->name,
  1461. +    status = nfs41_name_cache_insert(cache,
  1462. +        BIT2BOOL(parent->fh.superblock->case_insensitive),
  1463. +        file->path->path, &file->name,
  1464.          &file->fh, file_attrs->info, changeinfo,
  1465.          already_delegated ? OPEN_DELEGATE_NONE : delegation->type);
  1466.      ReleaseSRWLockShared(&file->path->lock);
  1467. @@ -688,6 +690,7 @@ int nfs41_create(
  1468.      bitmap4_cpy(&info->attrmask, &getattr_res.obj_attributes.attrmask);
  1469.      AcquireSRWLockShared(&file->path->lock);
  1470.      nfs41_name_cache_insert(session_name_cache(session),
  1471. +        BIT2BOOL(parent->fh.superblock->case_insensitive),
  1472.          file->path->path, &file->name, &file->fh,
  1473.          info, &create_res.cinfo, OPEN_DELEGATE_NONE);
  1474.      ReleaseSRWLockShared(&file->path->lock);
  1475. @@ -1331,6 +1334,7 @@ int nfs41_remove(
  1476.      /* remove the target file from the cache */
  1477.      AcquireSRWLockShared(&parent->path->lock);
  1478.      nfs41_name_cache_remove(session_name_cache(session),
  1479. +        BIT2BOOL(parent->fh.superblock->case_insensitive),
  1480.          parent->path->path, target, fileid, &remove_res.cinfo);
  1481.      ReleaseSRWLockShared(&parent->path->lock);
  1482.  
  1483. @@ -1429,6 +1433,7 @@ int nfs41_rename(
  1484.  
  1485.      /* move/rename the target file's name cache entry */
  1486.      nfs41_name_cache_rename(session_name_cache(session),
  1487. +        BIT2BOOL(src_dir->fh.superblock->case_insensitive),
  1488.          src_dir->path->path, src_name, &rename_res.source_cinfo,
  1489.          dst_dir->path->path, dst_name, &rename_res.target_cinfo);
  1490.  
  1491. @@ -1623,6 +1628,7 @@ int nfs41_link(
  1492.      bitmap4_cpy(&cinfo->attrmask, &getattr_res[1].obj_attributes.attrmask);
  1493.      AcquireSRWLockShared(&dst_dir->path->lock);
  1494.      nfs41_name_cache_insert(session_name_cache(session),
  1495. +        BIT2BOOL(dst_dir->fh.superblock->case_insensitive),
  1496.          dst_dir->path->path, target, &file.fh,
  1497.          cinfo, &link_res.cinfo, OPEN_DELEGATE_NONE);
  1498.      ReleaseSRWLockShared(&dst_dir->path->lock);
  1499. @@ -1858,6 +1864,7 @@ int nfs41_delegreturn(
  1500.  
  1501.      AcquireSRWLockShared(&file->path->lock);
  1502.      nfs41_name_cache_delegreturn(session_name_cache(session),
  1503. +        BIT2BOOL(file->fh.superblock->case_insensitive),
  1504.          file->fh.fileid, file->path->path, &file->name);
  1505.      ReleaseSRWLockShared(&file->path->lock);
  1506.  out:
  1507. diff --git a/daemon/nfs41_ops.h b/daemon/nfs41_ops.h
  1508. index 8f2b022..b3b70fb 100644
  1509. --- a/daemon/nfs41_ops.h
  1510. +++ b/daemon/nfs41_ops.h
  1511. @@ -1222,6 +1222,7 @@ enum nfsstat4 nfs41_reclaim_complete(
  1512.  int nfs41_lookup(
  1513.      IN nfs41_root *root,
  1514.      IN nfs41_session *session,
  1515. +    IN bool casesensitive,
  1516.      IN OUT nfs41_abs_path *path,
  1517.      OUT OPTIONAL nfs41_path_fh *parent_out,
  1518.      OUT OPTIONAL nfs41_path_fh *target_out,
  1519. diff --git a/daemon/nfs41_superblock.c b/daemon/nfs41_superblock.c
  1520. index 84d06db..c0141f0 100644
  1521. --- a/daemon/nfs41_superblock.c
  1522. +++ b/daemon/nfs41_superblock.c
  1523. @@ -132,8 +132,7 @@ static int get_superblock_attrs(
  1524.          superblock->case_preserving = info.case_preserving;
  1525.      }
  1526.      else {
  1527. -        superblock->case_preserving =
  1528. -            root->force_case_preserving?1:0;
  1529. +        superblock->case_preserving = BOOL2BIT(root->force_case_preserving);
  1530.          DPRINTF(0,
  1531.              ("get_superblock_attrs: OVERRIDING case_preserving to %d\n",
  1532.              (int)superblock->case_preserving));
  1533. @@ -142,8 +141,7 @@ static int get_superblock_attrs(
  1534.          superblock->case_insensitive = info.case_insensitive;
  1535.      }
  1536.      else {
  1537. -        superblock->case_insensitive =
  1538. -            root->force_case_insensitive?1:0;
  1539. +        superblock->case_insensitive = BOOL2BIT(root->force_case_insensitive);
  1540.          DPRINTF(0,
  1541.              ("get_superblock_attrs: OVERRIDING case_insensitive to %d\n",
  1542.              (int)superblock->case_insensitive));
  1543. @@ -161,10 +159,6 @@ static int get_superblock_attrs(
  1544.          superblock->block_clone_support = 0;
  1545.      }
  1546.  
  1547. -    nfs41_name_cache_set_casesensitivesearch(
  1548. -        session->client->server->name_cache,
  1549. -        superblock->case_insensitive?false:true);
  1550. -
  1551.      if (bitmap_isset(&info.attrmask, 0, FATTR4_WORD0_CANSETTIME))
  1552.          superblock->cansettime = info.cansettime;
  1553.      else /* cansettime is not supported, try setting them anyway */
  1554. @@ -257,8 +251,9 @@ static int get_superblock_attrs(
  1555.          superblock->layout_types, superblock->cansettime,
  1556.          superblock->time_delta.seconds, superblock->time_delta.nseconds,
  1557.          superblock->aclsupport, superblock->link_support,
  1558. -        superblock->symlink_support, superblock->case_preserving,
  1559. -        superblock->case_insensitive,
  1560. +        superblock->symlink_support,
  1561. +        (int)superblock->case_preserving,
  1562. +        (int)superblock->case_insensitive,
  1563.          superblock->sparse_file_support,
  1564.          superblock->block_clone_support));
  1565.  out:
  1566. @@ -327,8 +322,8 @@ void nfs41_superblock_fs_attributes(
  1567.          superblock->link_support,
  1568.          superblock->symlink_support,
  1569.          superblock->ea_support,
  1570. -        superblock->case_preserving,
  1571. -        superblock->case_insensitive,
  1572. +        (int)superblock->case_preserving,
  1573. +        (int)superblock->case_insensitive,
  1574.          superblock->aclsupport,
  1575.          (unsigned int)FsAttrs->MaximumComponentNameLength,
  1576.          (unsigned long)FsAttrs->FileSystemAttributes));
  1577. diff --git a/daemon/open.c b/daemon/open.c
  1578. index 43f753c..593452c 100644
  1579. --- a/daemon/open.c
  1580. +++ b/daemon/open.c
  1581. @@ -317,6 +317,10 @@ static int parse_open(unsigned char *buffer, uint32_t length, nfs41_upcall *upca
  1582.  
  1583.      status = get_name(&buffer, &length, &args->path);
  1584.      if (status) goto out;
  1585. +
  1586. +    status = safe_read(&buffer, &length, &args->is_caseinsensitive_volume,
  1587. +        sizeof(tristate_bool));
  1588. +    if (status) goto out;
  1589.      status = safe_read(&buffer, &length, &args->isvolumemntpt, sizeof(BOOLEAN));
  1590.      if (status) goto out;
  1591.      status = safe_read(&buffer, &length, &args->access_mask, sizeof(ULONG));
  1592. @@ -737,6 +741,25 @@ static int handle_open(void *daemon_context, nfs41_upcall *upcall)
  1593.      open_upcall_args *args = &upcall->args.open;
  1594.      nfs41_open_state *state;
  1595.      nfs41_file_info info = { 0 };
  1596. +    bool is_caseinsensitive_volume;
  1597. +
  1598. +    switch (args->is_caseinsensitive_volume) {
  1599. +        case TRISTATE_BOOL_FALSE:
  1600. +            is_caseinsensitive_volume = false;
  1601. +            break;
  1602. +        case TRISTATE_BOOL_TRUE:
  1603. +            is_caseinsensitive_volume = true;
  1604. +            break;
  1605. +        default:
  1606. +            eprintf("handle_open(args->path='%s'): "
  1607. +                "Invalid args->is_caseinsensitive_volume=%d value\n",
  1608. +                args->path, (int)args->is_caseinsensitive_volume);
  1609. +            /* fall-through */
  1610. +        case TRISTATE_BOOL_NOT_SET:
  1611. +            /* We default to case-sensitive mode */
  1612. +            is_caseinsensitive_volume = false;
  1613. +            break;
  1614. +    }
  1615.  
  1616.      EASSERT_MSG(!(args->create_opts & FILE_COMPLETE_IF_OPLOCKED),
  1617.          ("handle_open: file='%s': "
  1618. @@ -776,7 +799,8 @@ static int handle_open(void *daemon_context, nfs41_upcall *upcall)
  1619.  
  1620.      // always do a lookup
  1621.      status = nfs41_lookup(upcall->root_ref, nfs41_root_session(upcall->root_ref),
  1622. -        &state->path, &state->parent, &state->file, &info, &state->session);
  1623. +        is_caseinsensitive_volume, &state->path,
  1624. +        &state->parent, &state->file, &info, &state->session);
  1625.  
  1626.      if (status == ERROR_REPARSE) {
  1627.          uint32_t depth = 0;
  1628. @@ -802,7 +826,8 @@ static int handle_open(void *daemon_context, nfs41_upcall *upcall)
  1629.  
  1630.              /* redo the lookup until it doesn't return REPARSE */
  1631.              status = nfs41_lookup(upcall->root_ref, state->session,
  1632. -                &state->path, &state->parent, NULL, NULL, &state->session);
  1633. +                is_caseinsensitive_volume, &state->path,
  1634. +                &state->parent, NULL, NULL, &state->session);
  1635.          } while (status == ERROR_REPARSE);
  1636.  
  1637.          if (status == NO_ERROR || status == ERROR_FILE_NOT_FOUND) {
  1638. diff --git a/daemon/readdir.c b/daemon/readdir.c
  1639. index f809c9d..0e35421 100644
  1640. --- a/daemon/readdir.c
  1641. +++ b/daemon/readdir.c
  1642. @@ -499,8 +499,9 @@ static int lookup_entry(
  1643.      status = format_abs_path(parent->path, &name, &path);
  1644.      if (status) goto out;
  1645.  
  1646. -    status = nfs41_lookup(root, session, &path,
  1647. -        NULL, NULL, &entry->attr_info, NULL);
  1648. +    status = nfs41_lookup(root, session,
  1649. +        BIT2BOOL(parent->fh.superblock->case_insensitive),
  1650. +        &path, NULL, NULL, &entry->attr_info, NULL);
  1651.      if (status) goto out;
  1652.  out:
  1653.      return status;
  1654. @@ -522,7 +523,9 @@ static int lookup_symlink(
  1655.      if (status) goto out;
  1656.  
  1657.      file.path = &path;
  1658. -    status = nfs41_lookup(root, session, &path, NULL, &file, &info, &session);
  1659. +    status = nfs41_lookup(root, session,
  1660. +        BIT2BOOL(parent->fh.superblock->case_insensitive),
  1661. +        &path, NULL, &file, &info, &session);
  1662.      if (status) goto out;
  1663.  
  1664.      last_component(path.path, path.path + path.len, &file.name);
  1665. diff --git a/daemon/setattr.c b/daemon/setattr.c
  1666. index a47918e..db45478 100644
  1667. --- a/daemon/setattr.c
  1668. +++ b/daemon/setattr.c
  1669. @@ -413,6 +413,7 @@ static int handle_nfs41_rename(void *daemon_context, setattr_upcall_args *args)
  1670.  
  1671.      /* the destination path is absolute, so start from the root session */
  1672.      status = nfs41_lookup(args->root, nfs41_root_session(args->root),
  1673. +        BIT2BOOL(state->file.fh.superblock->case_insensitive),
  1674.          &dst_path, &dst_dir, &dst, NULL, &dst_session);
  1675.  
  1676.      while (status == ERROR_REPARSE) {
  1677. @@ -431,6 +432,7 @@ static int handle_nfs41_rename(void *daemon_context, setattr_upcall_args *args)
  1678.  
  1679.          /* redo the lookup until it doesn't return REPARSE */
  1680.          status = nfs41_lookup(args->root, dst_session,
  1681. +            BIT2BOOL(state->file.fh.superblock->case_insensitive),
  1682.              &dst_path, &dst_dir, NULL, NULL, &dst_session);
  1683.      }
  1684.  
  1685. @@ -591,6 +593,7 @@ static int handle_nfs41_link(void *daemon_context, setattr_upcall_args *args)
  1686.  
  1687.      /* the destination path is absolute, so start from the root session */
  1688.      status = nfs41_lookup(args->root, nfs41_root_session(args->root),
  1689. +        BIT2BOOL(state->file.fh.superblock->case_insensitive),
  1690.          &dst_path, &dst_dir, &dst, NULL, &dst_session);
  1691.  
  1692.      while (status == ERROR_REPARSE) {
  1693. @@ -609,6 +612,7 @@ static int handle_nfs41_link(void *daemon_context, setattr_upcall_args *args)
  1694.  
  1695.          /* redo the lookup until it doesn't return REPARSE */
  1696.          status = nfs41_lookup(args->root, dst_session,
  1697. +            BIT2BOOL(state->file.fh.superblock->case_insensitive),
  1698.              &dst_path, &dst_dir, &dst, NULL, &dst_session);
  1699.      }
  1700.  
  1701. diff --git a/daemon/symlink.c b/daemon/symlink.c
  1702. index 09cb07d..0537380 100644
  1703. --- a/daemon/symlink.c
  1704. +++ b/daemon/symlink.c
  1705. @@ -222,8 +222,9 @@ int nfs41_symlink_follow(
  1706.          last_component(path.path, path.path + path.len, &file.name);
  1707.  
  1708.          /* get attributes for the target */
  1709. -        status = nfs41_lookup(root, session, &path,
  1710. -            NULL, &file, info, &session);
  1711. +        status = nfs41_lookup(root, session,
  1712. +            BIT2BOOL(symlink->fh.superblock->case_insensitive),
  1713. +            &path, NULL, &file, info, &session);
  1714.          if (status) goto out;
  1715.  
  1716.          symlink = &file;
  1717. diff --git a/daemon/upcall.h b/daemon/upcall.h
  1718. index 8e099bc..b16719b 100644
  1719. --- a/daemon/upcall.h
  1720. +++ b/daemon/upcall.h
  1721. @@ -55,6 +55,7 @@ typedef struct __open_upcall_args {
  1722.      ULONGLONG fileid;
  1723.      ULONGLONG fsid_major, fsid_minor;
  1724.      const char *path;
  1725. +    tristate_bool is_caseinsensitive_volume;
  1726.      BOOLEAN isvolumemntpt;
  1727.      ULONG access_mask;
  1728.      ULONG access_mode;
  1729. diff --git a/daemon/util.h b/daemon/util.h
  1730. index b936ac4..6a7f458 100644
  1731. --- a/daemon/util.h
  1732. +++ b/daemon/util.h
  1733. @@ -39,6 +39,12 @@ typedef struct __nfs41_file_info nfs41_file_info;
  1734.  typedef struct __nfs41_superblock nfs41_superblock;
  1735.  enum stable_how4;
  1736.  
  1737. +/*
  1738. + * Turn |unsigned int foo:1| from/to |bool|
  1739. + */
  1740. +#define BIT2BOOL(bit) ((bit)?true:false)
  1741. +#define BOOL2BIT(b) ((b)?1U:0U)
  1742. +
  1743.  /*
  1744.   * UTIL_GETRELTIME - Get a relative time stamp
  1745.   * |GetTickCount64()| is almost twice as fast as |time()|, and
  1746. diff --git a/sys/nfs41sys_driver.h b/sys/nfs41sys_driver.h
  1747. index df26e64..95f4c8c 100644
  1748. --- a/sys/nfs41sys_driver.h
  1749. +++ b/sys/nfs41sys_driver.h
  1750. @@ -227,6 +227,7 @@ typedef struct _updowncall_entry {
  1751.              ULONGLONG fileid;
  1752.              ULONGLONG fsid_major, fsid_minor;
  1753.              UNICODE_STRING symlink;
  1754. +            tristate_bool is_caseinsensitive_volume;
  1755.              BOOLEAN isvolumemntpt;
  1756.              ULONG access_mask;
  1757.              ULONG access_mode;
  1758. diff --git a/sys/nfs41sys_openclose.c b/sys/nfs41sys_openclose.c
  1759. index 2833d1f..86bc604 100644
  1760. --- a/sys/nfs41sys_openclose.c
  1761. +++ b/sys/nfs41sys_openclose.c
  1762. @@ -119,6 +119,7 @@ NTSTATUS marshal_nfs41_open(
  1763.      else tmp += *len;
  1764.  
  1765.      header_len = *len + length_as_utf8(entry->filename) +
  1766. +        1 * sizeof(tristate_bool) +
  1767.          7 * sizeof(ULONG) +
  1768.          1 * sizeof(BOOLEAN) +
  1769.          2 * sizeof(HANDLE) +
  1770. @@ -129,6 +130,9 @@ NTSTATUS marshal_nfs41_open(
  1771.      }
  1772.      status = marshall_unicode_as_utf8(&tmp, entry->filename);
  1773.      if (status) goto out;
  1774. +    RtlCopyMemory(tmp, &entry->u.Open.is_caseinsensitive_volume,
  1775. +        sizeof(entry->u.Open.is_caseinsensitive_volume));
  1776. +    tmp += sizeof(entry->u.Open.is_caseinsensitive_volume);
  1777.      RtlCopyMemory(tmp, &entry->u.Open.isvolumemntpt,
  1778.          sizeof(entry->u.Open.isvolumemntpt));
  1779.      tmp += sizeof(entry->u.Open.isvolumemntpt);
  1780. @@ -632,6 +636,19 @@ NTSTATUS nfs41_Create(
  1781.          SrvOpen->pAlreadyPrefixedName, &entry);
  1782.      if (status) goto out;
  1783.  
  1784. +    entry->u.Open.is_caseinsensitive_volume = TRISTATE_BOOL_NOT_SET;
  1785. +    ULONG fsattrs = pVNetRootContext->FsAttrs.FileSystemAttributes;
  1786. +    /*
  1787. +     * Only set |entry->u.Open.is_caseinsensitive_volume| to |TRUE|/|FALSE|
  1788. +     * if we got any attributes from the superblock yet...
  1789. +     */
  1790. +    if (fsattrs) {
  1791. +        if (fsattrs & FILE_CASE_SENSITIVE_SEARCH)
  1792. +            entry->u.Open.is_caseinsensitive_volume = TRISTATE_BOOL_FALSE;
  1793. +        else
  1794. +            entry->u.Open.is_caseinsensitive_volume = TRISTATE_BOOL_TRUE;
  1795. +    }
  1796. +
  1797.      /* Check whether this is the mount point for this volume */
  1798.      entry->u.Open.isvolumemntpt =
  1799.          isFileNameTheVolumeMountPoint(SrvOpen->pAlreadyPrefixedName,
  1800. --
  1801. 2.51.0
  1802.  
  1803. From a783b3d654b8b1c8ca9dec40da458953166786cc Mon Sep 17 00:00:00 2001
  1804. From: Roland Mainz <roland.mainz@nrubsig.org>
  1805. Date: Wed, 17 Sep 2025 15:32:09 +0200
  1806. Subject: [PATCH 07/10] daemon: Name cache should use C99 |bool|, not |bool_t|
  1807.  
  1808. Name cache should use C99 |bool|, not |bool_t|, so that the
  1809. compilers+linters can recognise this as boolean.
  1810.  
  1811. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  1812. ---
  1813. daemon/lookup.c     |  6 +++---
  1814.  daemon/name_cache.c | 16 ++++++++--------
  1815.  daemon/name_cache.h |  2 +-
  1816.  3 files changed, 12 insertions(+), 12 deletions(-)
  1817.  
  1818. diff --git a/daemon/lookup.c b/daemon/lookup.c
  1819. index dfff849..27f8383 100644
  1820. --- a/daemon/lookup.c
  1821. +++ b/daemon/lookup.c
  1822. @@ -177,7 +177,7 @@ out:
  1823.      return status;
  1824.  }
  1825.  
  1826. -static int map_lookup_error(int status, bool_t last_component)
  1827. +static int map_lookup_error(int status, bool last_component)
  1828.  {
  1829.      switch (status) {
  1830.      case NFS4ERR_NOENT:
  1831. @@ -302,7 +302,7 @@ static int server_lookup(
  1832.          }
  1833.      }
  1834.  out:
  1835. -    return map_lookup_error(status, i == count-1);
  1836. +    return map_lookup_error(status, ((i == count-1)?true:false));
  1837.  }
  1838.  
  1839.  static uint32_t max_lookup_components(
  1840. @@ -480,7 +480,7 @@ int nfs41_lookup(
  1841.      nfs41_path_fh parent = { 0 }, target = { 0 }, *server_start;
  1842.      const char *path_pos, *path_end;
  1843.      struct lookup_referral referral = { 0 };
  1844. -    bool_t negative = 0;
  1845. +    bool negative = false;
  1846.      int status;
  1847.  
  1848.      if (session_out) *session_out = session;
  1849. diff --git a/daemon/name_cache.c b/daemon/name_cache.c
  1850. index 1351a58..32851a9 100644
  1851. --- a/daemon/name_cache.c
  1852. +++ b/daemon/name_cache.c
  1853. @@ -71,7 +71,7 @@ enum {
  1854.   * ERROR_TOO_MANY_OPEN_FILES, chosen arbitrarily for this case, instructs the
  1855.   * caller to return an outstanding delegation before caching a new one.
  1856.   */
  1857. -static __inline bool_t is_delegation(
  1858. +static __inline bool is_delegation(
  1859.      IN enum open_delegation_type4 type)
  1860.  {
  1861.      return type == OPEN_DELEGATE_READ || type == OPEN_DELEGATE_WRITE;
  1862. @@ -659,7 +659,7 @@ int name_cmp_case_insensitive(
  1863.  
  1864.  #define name_entry(pos) list_container(pos, struct name_cache_entry, exp_entry)
  1865.  
  1866. -static __inline bool_t name_cache_enabled(
  1867. +static __inline bool name_cache_enabled(
  1868.      IN struct nfs41_name_cache *cache)
  1869.  {
  1870.      return cache->expiration > 0;
  1871. @@ -890,7 +890,7 @@ static struct name_cache_entry* name_cache_search(
  1872.  
  1873.  static int entry_invis(
  1874.      IN struct name_cache_entry *entry,
  1875. -    OUT OPTIONAL bool_t *is_negative)
  1876. +    OUT OPTIONAL bool *is_negative)
  1877.  {
  1878.      /* name entry timer expired? */
  1879.      if (!list_empty(&entry->exp_entry) && (UTIL_GETRELTIME() > entry->expiration)) {
  1880. @@ -899,7 +899,7 @@ static int entry_invis(
  1881.      }
  1882.      /* negative lookup entry? */
  1883.      if (entry->attributes == NULL) {
  1884. -        if (is_negative) *is_negative = 1;
  1885. +        if (is_negative) *is_negative = true;
  1886.          DPRINTF(NCLVL2, ("name_entry_negative('%s')\n", entry->component));
  1887.          return 1;
  1888.      }
  1889. @@ -914,13 +914,13 @@ static int entry_invis(
  1890.  
  1891.  static int name_cache_lookup(
  1892.      IN struct nfs41_name_cache *cache,
  1893. -    IN bool_t skip_invis,
  1894. +    IN bool skip_invis,
  1895.      IN const char *path,
  1896.      IN const char *path_end,
  1897.      OUT OPTIONAL const char **remaining_path_out,
  1898.      OUT OPTIONAL struct name_cache_entry **parent_out,
  1899.      OUT OPTIONAL struct name_cache_entry **target_out,
  1900. -    OUT OPTIONAL bool_t *is_negative)
  1901. +    OUT OPTIONAL bool *is_negative)
  1902.  {
  1903.      struct name_cache_entry *parent, *target;
  1904.      nfs41_component component;
  1905. @@ -1126,7 +1126,7 @@ int nfs41_name_cache_lookup(
  1906.      OUT OPTIONAL nfs41_fh *parent_out,
  1907.      OUT OPTIONAL nfs41_fh *target_out,
  1908.      OUT OPTIONAL nfs41_file_info *info_out,
  1909. -    OUT OPTIONAL bool_t *is_negative)
  1910. +    OUT OPTIONAL bool *is_negative)
  1911.  {
  1912.      struct name_cache_entry *parent, *target;
  1913.      const char *path_pos = path;
  1914. @@ -1567,7 +1567,7 @@ out_unlock:
  1915.   */
  1916.  #define MAX_PUTFH_PER_COMPOUND 64
  1917.  
  1918. -static bool_t get_path_fhs(
  1919. +static bool get_path_fhs(
  1920.      IN struct nfs41_name_cache *cache,
  1921.      IN nfs41_abs_path *path,
  1922.      IN OUT const char **path_pos,
  1923. diff --git a/daemon/name_cache.h b/daemon/name_cache.h
  1924. index 3950983..3d79137 100644
  1925. --- a/daemon/name_cache.h
  1926. +++ b/daemon/name_cache.h
  1927. @@ -68,7 +68,7 @@ int nfs41_name_cache_lookup(
  1928.      OUT OPTIONAL nfs41_fh *parent_out,
  1929.      OUT OPTIONAL nfs41_fh *target_out,
  1930.      OUT OPTIONAL nfs41_file_info *info_out,
  1931. -    OUT OPTIONAL bool_t *is_negative);
  1932. +    OUT OPTIONAL bool *is_negative);
  1933.  
  1934.  int nfs41_name_cache_insert(
  1935.      IN struct nfs41_name_cache *cache,
  1936. --
  1937. 2.51.0
  1938.  
  1939. From 97675e5c3d869f9f9133aafa4210a6fdd28bae7f Mon Sep 17 00:00:00 2001
  1940. From: Roland Mainz <roland.mainz@nrubsig.org>
  1941. Date: Wed, 17 Sep 2025 16:07:07 +0200
  1942. Subject: [PATCH 08/10] daemon: superblock+mount admin info logging should
  1943.  include the NFS fsid
  1944.  
  1945. superblock+mount admin info logging should include the NFS fsid.
  1946.  
  1947. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  1948. ---
  1949. daemon/mount.c            | 14 +++++++++-----
  1950.  daemon/nfs41_superblock.c | 14 ++++++++++----
  1951.  2 files changed, 19 insertions(+), 9 deletions(-)
  1952.  
  1953. diff --git a/daemon/mount.c b/daemon/mount.c
  1954. index d1f2e9e..3864b4a 100644
  1955. --- a/daemon/mount.c
  1956. +++ b/daemon/mount.c
  1957. @@ -99,7 +99,7 @@ static int handle_mount(void *daemon_context, nfs41_upcall *upcall)
  1958.      multi_addr4 addrs = { 0 };
  1959.      nfs41_root *root = NULL;
  1960.      nfs41_client *client;
  1961. -    nfs41_path_fh file;
  1962. +    nfs41_path_fh file = { 0 };
  1963.  #ifdef NFS41_DRIVER_USE_AUTHENTICATIONID_FOR_MOUNT_NAMESPACE
  1964.      LUID authenticationid = { .LowPart = 0, .HighPart = 0L };
  1965.  #endif /* NFS41_DRIVER_USE_AUTHENTICATIONID_FOR_MOUNT_NAMESPACE */
  1966. @@ -258,7 +258,8 @@ out:
  1967.      if (status == 0) {
  1968.  #ifdef NFS41_DRIVER_USE_AUTHENTICATIONID_FOR_MOUNT_NAMESPACE
  1969.          logprintf("mount(hostport='%s', use_nfspubfh=%d, %s='%s', "
  1970. -            "authid=(0x%lx.0x%lx)) success, root=0x%p, NFS version=4.%d\n",
  1971. +            "authid=(0x%lx.0x%lx)) success, root=0x%p, "
  1972. +            "NFS version=4.%d, NFS fsid=(%llu,%llu)\n",
  1973.              args->hostport?args->hostport:"<NULL>",
  1974.              (int)args->use_nfspubfh,
  1975.              (args->use_nfspubfh?"relative_path":"path"),
  1976. @@ -266,16 +267,19 @@ out:
  1977.              (long)authenticationid.HighPart,
  1978.              (long)authenticationid.LowPart,
  1979.              root,
  1980. -            (int)root->nfsminorvers);
  1981. +            (int)root->nfsminorvers,
  1982. +            file.fh.superblock->fsid.major, file.fh.superblock->fsid.minor);
  1983.  #else
  1984.          logprintf("mount(hostport='%s', use_nfspubfh=%d, %s='%s') success, "
  1985. -            "root=0x%p, NFS version=4.%d\n",
  1986. +            "root=0x%p, "
  1987. +            "NFS version=4.%d, NFS fsid=(%llu,%llu)\n",
  1988.              args->hostport?args->hostport:"<NULL>",
  1989.              (int)args->use_nfspubfh,
  1990.              (args->use_nfspubfh?"relative_path":"path"),
  1991.              args->path?args->path:"<NULL>",
  1992.              root,
  1993. -            (int)root->nfsminorvers);
  1994. +            (int)root->nfsminorvers,
  1995. +            file.fh.superblock->fsid.major, file.fh.superblock->fsid.minor);
  1996.  #endif /* NFS41_DRIVER_USE_AUTHENTICATIONID_FOR_MOUNT_NAMESPACE */
  1997.      }
  1998.      else {
  1999. diff --git a/daemon/nfs41_superblock.c b/daemon/nfs41_superblock.c
  2000. index c0141f0..8777feb 100644
  2001. --- a/daemon/nfs41_superblock.c
  2002. +++ b/daemon/nfs41_superblock.c
  2003. @@ -207,11 +207,15 @@ static int get_superblock_attrs(
  2004.              is = stpcpy(is, "SACL");
  2005.          }
  2006.  
  2007. -        logprintf("get_superblock_attrs: Supported ACL types: { %s }\n",
  2008. +        logprintf("get_superblock_attrs(fsid=(%llu,%llu)): "
  2009. +            "Supported ACL types: { %s }\n",
  2010. +            superblock->fsid.major, superblock->fsid.minor,
  2011.              infobuff);
  2012.      }
  2013.      else {
  2014. -        logprintf("get_superblock_attrs: No ACL support\n");
  2015. +        logprintf("get_superblock_attrs(fsid=(%llu,%llu)): "
  2016. +            "No ACL support\n",
  2017. +            superblock->fsid.major, superblock->fsid.minor);
  2018.      }
  2019.  
  2020.      /* Windows/DOS attributes */
  2021. @@ -230,13 +234,15 @@ static int get_superblock_attrs(
  2022.              *is++ = ',';
  2023.          is = stpcpy(is, "SYSTEM");
  2024.      }
  2025. -    logprintf("get_superblock_attrs: "
  2026. +    logprintf("get_superblock_attrs(fsid=(%llu,%llu)): "
  2027.          "Supported Windows/DOS attributes: { %s }\n",
  2028. +        superblock->fsid.major, superblock->fsid.minor,
  2029.          infobuff);
  2030.  
  2031.      /* Filename case handling */
  2032. -    logprintf("get_superblock_attrs: "
  2033. +    logprintf("get_superblock_attrs(fsid=(%llu,%llu)): "
  2034.          "Case handling: case_insensitive=%d, case_preserving=%d\n",
  2035. +        superblock->fsid.major, superblock->fsid.minor,
  2036.          (int)superblock->case_insensitive,
  2037.          (int)superblock->case_preserving);
  2038.  
  2039. --
  2040. 2.51.0
  2041.  
  2042. From ca4a15ba1a4f6bb2d41d9492e5828e2c0eab1ea5 Mon Sep 17 00:00:00 2001
  2043. From: Roland Mainz <roland.mainz@nrubsig.org>
  2044. Date: Wed, 17 Sep 2025 16:23:01 +0200
  2045. Subject: [PATCH 09/10] README.md,docs: Document case-insensitive filesystem
  2046.  support
  2047.  
  2048. Document case-insensitive filesystem support.
  2049.  
  2050. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  2051. ---
  2052. README.md       | 6 ++++++
  2053.  docs/README.xml | 9 +++++++++
  2054.  2 files changed, 15 insertions(+)
  2055.  
  2056. diff --git a/README.md b/README.md
  2057. index 519f1f0..13098fe 100644
  2058. --- a/README.md
  2059. +++ b/README.md
  2060. @@ -139,6 +139,12 @@ NFSv4.2/NFSv4.1 filesystem driver for Windows 10/11 & Windows Server
  2061.      sparse files. Requires on Win11 \>= 22H2 because it relies on
  2062.      `|CopyFile2()|` flag `|COPY_FILE_ENABLE_SPARSE_COPY|`.
  2063.  
  2064. +- Case-insensitive filesystem support
  2065. +
  2066. +  - Requires NFSv4.1 server which supports the
  2067. +    `|FATTR4_WORD0_CASE_INSENSITIVE|` attribute set to `TRUE` (currently
  2068. +    Windows Server NFSv4.1 server exporting NTFS).
  2069. +
  2070.  - Data copy offload (server-side copy)
  2071.  
  2072.    - Implemented via Win32 `|FSCTL_OFFLOAD_READ|`+`|FSCTL_OFFLOAD_WRITE|`
  2073. diff --git a/docs/README.xml b/docs/README.xml
  2074. index 4f290a3..f64fcee 100644
  2075. --- a/docs/README.xml
  2076. +++ b/docs/README.xml
  2077. @@ -141,6 +141,15 @@
  2078.            </itemizedlist>
  2079.          </para>
  2080.        </listitem>
  2081. +      <listitem>
  2082. +        <para>Case-insensitive filesystem support
  2083. +          <itemizedlist>
  2084. +            <listitem>
  2085. +              <para>Requires NFSv4.1 server which supports the <literal>|FATTR4_WORD0_CASE_INSENSITIVE|</literal> attribute set to <literal>TRUE</literal> (currently Windows Server NFSv4.1 server exporting NTFS).</para>
  2086. +            </listitem>
  2087. +          </itemizedlist>
  2088. +        </para>
  2089. +      </listitem>
  2090.        <listitem>
  2091.          <para>Data copy offload (server-side copy)
  2092.            <itemizedlist>
  2093. --
  2094. 2.51.0
  2095.  
  2096. From 990065fe93b86f56cf9067a2bb0995b67c53b507 Mon Sep 17 00:00:00 2001
  2097. From: Roland Mainz <roland.mainz@nrubsig.org>
  2098. Date: Wed, 17 Sep 2025 16:42:17 +0200
  2099. Subject: [PATCH 10/10] tests: Add notes about software compatibility
  2100.  
  2101. Add notes about software compatibility.
  2102.  
  2103. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  2104. ---
  2105. tests/tests_software_compat.txt | 65 +++++++++++++++++++++++++++++++++
  2106.  1 file changed, 65 insertions(+)
  2107.  create mode 100644 tests/tests_software_compat.txt
  2108.  
  2109. diff --git a/tests/tests_software_compat.txt b/tests/tests_software_compat.txt
  2110. new file mode 100644
  2111. index 0000000..c9aa29a
  2112. --- /dev/null
  2113. +++ b/tests/tests_software_compat.txt
  2114. @@ -0,0 +1,65 @@
  2115. +#
  2116. +# ms-nfs41-client software compatibility notes
  2117. +#
  2118. +
  2119. +# Name: FireFox
  2120. +# Version: FireFox 143.0b9
  2121. +# Download URL: https://download-installer.cdn.mozilla.net/pub/firefox/releases/143.0b9/win64/de/Firefox%20Setup%20143.0b9.exe
  2122. +# Can use NFS for data storage: yes
  2123. +# Software can be installed on NFS: Yes, requires global-mount, requires case-insensitive filesystem
  2124. +# Notes:
  2125. +# - Requires case-insensitive filesystem for installation on NFS because DLL
  2126. +# are stored with a different case than they are loaded
  2127. +# - Requires global-mount, because installer uses different, evelated logon than the caller
  2128. +# - The *.msi installer does not provide an option to install to a non-standard location, so the *.exe installer is needed
  2129. +#
  2130. +
  2131. +# Name: Seamonkey
  2132. +# Version:
  2133. +# Download URL: https://archive.seamonkey-project.org/releases/2.53.21/win64/de/seamonkey-2.53.21.de.win64.installer.exe
  2134. +# Can use NFS for data storage: yes
  2135. +# Software can be installed on NFS: Yes, requires global-mount, requires case-insensitive filesystem
  2136. +# Notes:
  2137. +# - Requires case-insensitive filesystem for installation on NFS because DLL
  2138. +# are stored with a different case than they are loaded
  2139. +# - Requires global-mount, because installer uses different, evelated logon than the caller
  2140. +#
  2141. +
  2142. +# Name: VMware Workstation
  2143. +# Version: VMware-workstation-full-17.5.0-22583795
  2144. +# Installer: VMware-workstation-full-17.5.0-22583795.exe
  2145. +# Download URL: XXX
  2146. +# Can use NFS for data storage: yes
  2147. +# Software can be installed on NFS: Yes, requires global-mount, requires case-insensitive filesystem
  2148. +# Notes:
  2149. +# - Requires case-insensitive filesystem for installation on NFS because DLL
  2150. +# are stored with a different case than they are loaded
  2151. +# - Requires global-mount, because installer uses different, evelated logon than the caller
  2152. +#
  2153. +
  2154. +# Name: Wireshark
  2155. +# Version: Wireshark-4.4.3-x64
  2156. +# Installer: Wireshark-4.4.3-x64.exe
  2157. +# Download URL:
  2158. +# Can use NFS for data storage: yes
  2159. +# Software can be installed on NFS: Yes, requires global-mount, requires case-insensitive filesystem
  2160. +# Notes:
  2161. +# - Requires case-insensitive filesystem for installation on NFS because DLL
  2162. +# are stored with a different case than they are loaded
  2163. +# - Requires global-mount, because installer uses different, evelated logon than the caller
  2164. +#
  2165. +
  2166. +# Name: JAVA SDK
  2167. +# Version: jdk-23_windows-x64
  2168. +# Installer: jdk-23_windows-x64_bin.msi
  2169. +# Download URL:
  2170. +# Can use NFS for data storage: yes
  2171. +# Software can be installed on NFS: No, MSI installer fails to unpack the files
  2172. +# Notes:
  2173. +# - MSI installer fails to unpack the files
  2174. +# - running java software via *.class or *.jar works fine, even on case-sensive filesystems
  2175. +#
  2176. +
  2177. +
  2178. +
  2179. +# EOF.
  2180. --
  2181. 2.51.0

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