pastebin - collaborative debugging tool
rovema.kpaste.net RSS


msnfs41client: Patches for |FSCTL_QUERY_ALLOCATED_RANGES| buffer size calculation, UNC path vs mkinstalldirs+misc, 2025-03-03
Posted by Anonymous on Mon 3rd Mar 2025 18:55
raw | new post

  1. From d7139291dd000f326aa5b40813f435ee7988a51d Mon Sep 17 00:00:00 2001
  2. From: Roland Mainz <roland.mainz@nrubsig.org>
  3. Date: Mon, 3 Mar 2025 18:12:23 +0100
  4. Subject: [PATCH 1/2] tests: Need workaround for GNU mkinstalldirs not able to
  5.  handle UNC paths
  6.  
  7. Need workaround for GNU mkinstalldirs not able to handle UNC paths.
  8.  
  9. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  10. ---
  11. tests/nfsbuildtest/nfsbuildtest.ksh93 | 8 ++++++++
  12.  1 file changed, 8 insertions(+)
  13.  
  14. diff --git a/tests/nfsbuildtest/nfsbuildtest.ksh93 b/tests/nfsbuildtest/nfsbuildtest.ksh93
  15. index 92d0cf7..17eaef3 100644
  16. --- a/tests/nfsbuildtest/nfsbuildtest.ksh93
  17. +++ b/tests/nfsbuildtest/nfsbuildtest.ksh93
  18. @@ -117,6 +117,10 @@ function gcc_build
  19.         # patch sources and configure build
  20.         #
  21.  
  22. +       # original mkinstalldirs cannot handle UNC paths
  23. +       printf '#!/bin/sh\n# original mkinstalldirs cannot handle Cygwin UNC paths\nmkdir -p "$@"\nexit $?' >'mkinstalldirs'
  24. +       chmod a+x 'mkinstalldirs'
  25. +
  26.         # Cygwin/MSYS2: workaround for configure using cp -p where ln -s should be used
  27.         # (this is an automake/autoconf issue, they should trust Cygwin and not use
  28.         # ancient workarounds for issues which no longer exists)
  29. @@ -282,6 +286,10 @@ function bash_build
  30.         # patch sources and configure build
  31.         #
  32.  
  33. +       # original mkinstalldirs cannot handle UNC paths
  34. +       printf '#!/bin/sh\n# original mkinstalldirs cannot handle Cygwin UNC paths\nmkdir -p "$@"\nexit $?' >'support/mkinstalldirs'
  35. +       chmod a+x 'support/mkinstalldirs'
  36. +
  37.         # disable loadable plugins
  38.         sed -i -E 's/-\( cd \$\(LOADABLES_DIR\) && \$\(MAKE\) \$\(MFLAGS\) DESTDIR=\$\(DESTDIR\) \$@ \)//' Makefile.in
  39.  
  40. --
  41. 2.45.1
  42.  
  43. From 0fd8ca32556fb2fe23a43cc831d31f21d48b5ab0 Mon Sep 17 00:00:00 2001
  44. From: Roland Mainz <roland.mainz@nrubsig.org>
  45. Date: Mon, 3 Mar 2025 18:16:11 +0100
  46. Subject: [PATCH 2/2] daemon,sys,tests: RFE:
  47.  |DeviceIoControl(...,FSCTL_QUERY_ALLOCATED_RANGES,...)| should support
  48.  |ERROR_MORE_DATA|
  49.  
  50. RFE: |DeviceIoControl(..., FSCTL_QUERY_ALLOCATED_RANGES, ...)| should
  51. support |ERROR_MORE_DATA| to query the buffer size required to
  52. list all data ranges in |FILE_ALLOCATED_RANGE_BUFFER|s in a
  53. given offset+length.
  54.  
  55. This is partially ruined by a bug in Windows 10's $ fsutil sparse
  56. queryrange ... # which causes random data to be returned if
  57. |ERROR_MORE_DATA| returns a buffer size different than the buffer
  58. size passed in.
  59.  
  60. fsutil passes 64 |FILE_ALLOCATED_RANGE_BUFFER| in, and expects
  61. that the size for 64 |FILE_ALLOCATED_RANGE_BUFFER| will be returned
  62. if the error code is |ERROR_MORE_DATA|.
  63. For now we define the cpp symbol
  64. |WINDOWS_FSUTILSPARSEQUERYRANGE_MOREDATA_BUG| to enable our workaround,
  65. until we get a fix for the issue from Microsoft.
  66.  
  67. Reported-by: Lionel Cons <lionelcons1972@gmail.com>
  68. Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
  69. ---
  70. daemon/fsctl.c                        | 69 +++++++++++++++++++++++----
  71.  daemon/upcall.h                       |  1 +
  72.  sys/nfs41sys_driver.h                 |  3 +-
  73.  sys/nfs41sys_fileinfo.c               |  2 +
  74.  sys/nfs41sys_fsctl.c                  | 29 +++++++++--
  75.  tests/sparsefiles/testsparsefile1.ksh |  4 +-
  76.  tests/winfsinfo1/winfsinfo.c          |  7 ++-
  77.  7 files changed, 96 insertions(+), 19 deletions(-)
  78.  
  79. diff --git a/daemon/fsctl.c b/daemon/fsctl.c
  80. index 29092b2..bc74363 100644
  81. --- a/daemon/fsctl.c
  82. +++ b/daemon/fsctl.c
  83. @@ -73,6 +73,7 @@ int query_sparsefile_datasections(nfs41_open_state *state,
  84.      bool_t hole_seek_sr_eof;
  85.      uint64_t hole_seek_sr_offset;
  86.      size_t i;
  87. +    bool error_more_data = false;
  88.  
  89.      stateid_arg stateid;
  90.  
  91. @@ -91,7 +92,7 @@ int query_sparsefile_datasections(nfs41_open_state *state,
  92.      next_offset = start_offset;
  93.      *res_num_records = 0;
  94.  
  95. -    for (i=0 ; i < out_maxrecords ; i++) {
  96. +    for (i=0 ; ; i++) {
  97.          data_seek_status = nfs42_seek(state->session,
  98.              &state->file,
  99.              &stateid,
  100. @@ -172,16 +173,44 @@ int query_sparsefile_datasections(nfs41_open_state *state,
  101.              (int)data_seek_sr_eof,
  102.              (int)hole_seek_sr_eof));
  103.  
  104. -        outbuffer[i].FileOffset.QuadPart = data_seek_sr_offset;
  105. -        outbuffer[i].Length.QuadPart = data_size;
  106. +        if (i < out_maxrecords) {
  107. +            outbuffer[i].FileOffset.QuadPart = data_seek_sr_offset;
  108. +            outbuffer[i].Length.QuadPart = data_size;
  109. +        }
  110. +        else {
  111. +            error_more_data = true;
  112. +
  113. +            /* Windows bug:
  114. +             * Technically we should continue until we reach
  115. +             * |end_offset| to return the correct size of the
  116. +             * buffer with |DeviceIoControl(...,
  117. +             * FSCTL_QUERY_ALLOCATED_RANGES,...)| ==
  118. +             * |ERROR_MORE_DATA|.
  119. +             *
  120. +             * Unfortunaely Win10 fsutil will then return
  121. +             * random values after the first 64 entries, so the
  122. +             * best we can do for bug-by-bug compatibility is
  123. +             * to do the same: Stop counting here, and
  124. +             * therefore return
  125. +             * |out_maxrecords*sizeof(FILE_ALLOCATED_RANGE_BUFFER)|,
  126. +             * which is exactly the same number of bytes of the
  127. +             * original buffer passed as argument
  128. +             */
  129. +#define WINDOWS_FSUTILSPARSEQUERYRANGE_MOREDATA_BUG 1
  130. +
  131. +#ifdef WINDOWS_FSUTILSPARSEQUERYRANGE_MOREDATA_BUG
  132. +            break;
  133. +#endif /* WINDOWS_FSUTILSPARSEQUERYRANGE_MOREDATA_BUG */
  134. +        }
  135. +
  136.          (*res_num_records)++;
  137.  
  138. -        if ((uint64_t)outbuffer[i].FileOffset.QuadPart > end_offset) {
  139. +        if (data_seek_sr_offset > end_offset) {
  140.              DPRINTF(QARLVL,
  141.                  ("end offset reached, "
  142. -                "outbuffer[%d].FileOffset.QuadPart(=%lld) > end_offset(=%lld)\n",
  143. +                "i=%d, data_seek_sr_offset(=%lld) > end_offset(=%lld)\n",
  144.                  (int)i,
  145. -                (long long)outbuffer[i].FileOffset.QuadPart,
  146. +                (long long)data_seek_sr_offset,
  147.                  (long long)end_offset));
  148.              break;
  149.          }
  150. @@ -196,6 +225,12 @@ int query_sparsefile_datasections(nfs41_open_state *state,
  151.      }
  152.  
  153.  out:
  154. +    if (error_more_data) {
  155. +        DPRINTF(QARLVL, ("returning ERROR_MORE_DATA, *res_num_records=%ld\n",
  156. +            (long)*res_num_records));
  157. +        status = ERROR_MORE_DATA;
  158. +    }
  159. +
  160.      DPRINTF(QARLVL, ("<-- query_sparsefile_datasections(), status=0x%x\n",
  161.          status));
  162.      return status;
  163. @@ -232,6 +267,7 @@ int handle_queryallocatedranges(void *daemon_context,
  164.              "got space for %ld records\n",
  165.              (int)num_records));
  166.  
  167. +    args->buffer_overflow = FALSE;
  168.      args->returned_size = 0;
  169.  
  170.      size_t res_num_records = 0;
  171. @@ -243,13 +279,23 @@ int handle_queryallocatedranges(void *daemon_context,
  172.          num_records,
  173.          &res_num_records);
  174.  
  175. -    if (!status) {
  176. -        args->returned_size =
  177. -            (ULONG)res_num_records*sizeof(FILE_ALLOCATED_RANGE_BUFFER);
  178. +    /*
  179. +     * Return buffer size, either on success, or to return the size
  180. +     * of the buffer which would be needed.
  181. +     */
  182. +    args->returned_size =
  183. +        (ULONG)res_num_records*sizeof(FILE_ALLOCATED_RANGE_BUFFER);
  184. +
  185. +    if (status == ERROR_MORE_DATA) {
  186. +        status = NO_ERROR;
  187. +        args->buffer_overflow = TRUE;
  188.      }
  189.  
  190.      DPRINTF(QARLVL,
  191. -        ("<-- handle_queryallocatedranges(), status=0x%lx\n",
  192. +        ("<-- handle_queryallocatedranges(args->buffer_overflow=%d, args->returned_size=%ld), "
  193. +        "status=0x%lx\n",
  194. +        (int)args->buffer_overflow,
  195. +        (long)args->returned_size,
  196.          status));
  197.      return status;
  198.  }
  199. @@ -260,8 +306,11 @@ static int marshall_queryallocatedranges(unsigned char *buffer,
  200.      int status;
  201.      queryallocatedranges_upcall_args *args = &upcall->args.queryallocatedranges;
  202.  
  203. +    status = safe_write(&buffer, length, &args->buffer_overflow, sizeof(args->buffer_overflow));
  204. +    if (status) goto out;
  205.      status = safe_write(&buffer, length, &args->returned_size, sizeof(args->returned_size));
  206.  
  207. +out:
  208.      return status;
  209.  }
  210.  
  211. diff --git a/daemon/upcall.h b/daemon/upcall.h
  212. index 19e36f8..fbed276 100644
  213. --- a/daemon/upcall.h
  214. +++ b/daemon/upcall.h
  215. @@ -195,6 +195,7 @@ typedef struct __queryallocatedranges_upcall_args {
  216.      FILE_ALLOCATED_RANGE_BUFFER     inrange;
  217.      HANDLE                          outbuffer;
  218.      ULONG                           outbuffersize;
  219. +    BOOLEAN                         buffer_overflow;
  220.      ULONG                           returned_size;
  221.  } queryallocatedranges_upcall_args;
  222.  
  223. diff --git a/sys/nfs41sys_driver.h b/sys/nfs41sys_driver.h
  224. index 65884ca..9b2c08a 100644
  225. --- a/sys/nfs41sys_driver.h
  226. +++ b/sys/nfs41sys_driver.h
  227. @@ -275,7 +275,8 @@ typedef struct _updowncall_entry {
  228.              PMDL BufferMdl;
  229.              ULONG BufferSize;
  230.              PVOID Buffer;
  231. -            LONGLONG returned_size;
  232. +            BOOLEAN buffer_overflow;
  233. +            ULONG returned_size;
  234.          } QueryAllocatedRanges;
  235.          struct {
  236.              FILE_ZERO_DATA_INFORMATION setzerodata;
  237. diff --git a/sys/nfs41sys_fileinfo.c b/sys/nfs41sys_fileinfo.c
  238. index 07bb770..6efa067 100644
  239. --- a/sys/nfs41sys_fileinfo.c
  240. +++ b/sys/nfs41sys_fileinfo.c
  241. @@ -487,6 +487,8 @@ NTSTATUS map_setfile_error(
  242.      case ERROR_DISK_FULL:               return STATUS_DISK_FULL;
  243.      case ERROR_DISK_QUOTA_EXCEEDED:     return STATUS_DISK_QUOTA_EXCEEDED;
  244.      case ERROR_FILE_TOO_LARGE:          return STATUS_FILE_TOO_LARGE;
  245. +    case ERROR_INSUFFICIENT_BUFFER:     return STATUS_BUFFER_TOO_SMALL;
  246. +    case ERROR_MORE_DATA:               return STATUS_BUFFER_OVERFLOW;
  247.      case ERROR_INTERNAL_ERROR:          return STATUS_INTERNAL_ERROR;
  248.      default:
  249.          print_error("map_setfile_error: "
  250. diff --git a/sys/nfs41sys_fsctl.c b/sys/nfs41sys_fsctl.c
  251. index 3157b54..274bf29 100644
  252. --- a/sys/nfs41sys_fsctl.c
  253. +++ b/sys/nfs41sys_fsctl.c
  254. @@ -181,16 +181,31 @@ NTSTATUS nfs41_QueryAllocatedRanges(
  255.      }
  256.      entry->psec_ctx = NULL;
  257.  
  258. -    if (!entry->status) {
  259. +    if (entry->status == NO_ERROR) {
  260.          DbgP("nfs41_QueryAllocatedRanges: SUCCESS\n");
  261. -        RxContext->CurrentIrp->IoStatus.Status = STATUS_SUCCESS;
  262. -        RxContext->IoStatusBlock.Information =
  263. -            (ULONG_PTR)entry->u.QueryAllocatedRanges.returned_size;
  264. +
  265. +        if (entry->u.QueryAllocatedRanges.buffer_overflow) {
  266. +            DbgP("nfs41_QueryAllocatedRanges: buffer_overflow: "
  267. +                "need at least a buffer with %ld bytes\n",
  268. +                (long)entry->u.QueryAllocatedRanges.returned_size);
  269. +            status = RxContext->CurrentIrp->IoStatus.Status =
  270. +                STATUS_BUFFER_OVERFLOW;
  271. +            RxContext->IoStatusBlock.Information =
  272. +                (ULONG_PTR)entry->u.QueryAllocatedRanges.returned_size;
  273. +        }
  274. +        else {
  275. +            status = RxContext->CurrentIrp->IoStatus.Status =
  276. +                STATUS_SUCCESS;
  277. +            RxContext->IoStatusBlock.Information =
  278. +                (ULONG_PTR)entry->u.QueryAllocatedRanges.returned_size;
  279. +        }
  280.      }
  281.      else {
  282.          DbgP("nfs41_QueryAllocatedRanges: "
  283.              "FAILURE, entry->status=0x%lx\n", entry->status);
  284. +
  285.          status = map_setfile_error(entry->status);
  286. +
  287.          RxContext->CurrentIrp->IoStatus.Status = status;
  288.          RxContext->IoStatusBlock.Information = 0;
  289.      }
  290. @@ -294,12 +309,16 @@ NTSTATUS unmarshal_nfs41_queryallocatedranges(
  291.          goto out;
  292.      }
  293.  
  294. +    RtlCopyMemory(&cur->u.QueryAllocatedRanges.buffer_overflow,
  295. +        *buf, sizeof(BOOLEAN));
  296. +    *buf += sizeof(BOOLEAN);
  297.      RtlCopyMemory(&cur->u.QueryAllocatedRanges.returned_size,
  298.          *buf, sizeof(ULONG));
  299.      *buf += sizeof(ULONG);
  300.  
  301.      DbgP("unmarshal_nfs41_queryallocatedranges: "
  302. -        "queryallocatedranges.returned_size=%llu\n",
  303. +        "buffer_overflow=%d returned_size=%llu\n",
  304. +        (int)cur->u.QueryAllocatedRanges.buffer_overflow,
  305.          cur->u.QueryAllocatedRanges.returned_size);
  306.  
  307.  out:
  308. diff --git a/tests/sparsefiles/testsparsefile1.ksh b/tests/sparsefiles/testsparsefile1.ksh
  309. index b541849..2427af2 100644
  310. --- a/tests/sparsefiles/testsparsefile1.ksh
  311. +++ b/tests/sparsefiles/testsparsefile1.ksh
  312. @@ -288,8 +288,8 @@ test_multihole_sparsefile1 1024 2 32 false
  313.  test_multihole_sparsefile1 1024 0 4  true
  314.  test_multihole_sparsefile1 1024 1 4  true
  315.  
  316. -# 512 does not work, as Win10 fsutil can only handle 64 data sections
  317. -# test_multihole_sparsefile1 1024 2 512 false
  318. +# fsutil uses 64 entries per queryrange, so we test this here
  319. +test_multihole_sparsefile1 1024 2 256 false
  320.  
  321.  test_sparse_punchhole1
  322.  
  323. diff --git a/tests/winfsinfo1/winfsinfo.c b/tests/winfsinfo1/winfsinfo.c
  324. index 77cc036..de1d95e 100644
  325. --- a/tests/winfsinfo1/winfsinfo.c
  326. +++ b/tests/winfsinfo1/winfsinfo.c
  327. @@ -1127,7 +1127,7 @@ retry_fsctl:
  328.                   * bytes we passed in, not the number of bytes which
  329.                   * we should allocate
  330.                   */
  331. -                max_ranges_per_query+=2;
  332. +                max_ranges_per_query += 16;
  333.                  ranges = realloc(ranges,
  334.                      (sizeof(FILE_ALLOCATED_RANGE_BUFFER) * max_ranges_per_query));
  335.                  if (ranges == NULL) {
  336. @@ -1142,6 +1142,11 @@ retry_fsctl:
  337.                  goto retry_fsctl;
  338.              }
  339.  
  340. +            (void)fprintf(stderr,
  341. +                "%s: DeviceIoControl() failed, lasterr=%d\n",
  342. +                progname,
  343. +                (int)lasterr);
  344. +
  345.              retval = 1;
  346.              goto out;
  347.          }
  348. --
  349. 2.45.1

Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:

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




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