- From 3e6d626c9d8d96b046191f31bf3478ec8f20a8df Mon Sep 17 00:00:00 2001
- From: Roland Mainz <roland.mainz@nrubsig.org>
- Date: Fri, 23 Jan 2026 18:33:14 +0100
- Subject: [PATCH 1/5] daemon: Fix DrMemory UNINITIALIZED READ hit in
- nfs41_|unlock()|
- Fix DrMemory UNINITIALIZED READ hit in nfs41_|unlock()|:
- ---- snip ----
- Error #1: UNINITIALIZED READ: reading 4 byte(s)
- libtirpc.dll!xdr_u_int32_t [ms-nfs41-client\libtirpc\src\xdr.c:242]
- libtirpc.dll!xdr_uint32_t [ms-nfs41-client\libtirpc\src\xdr.c:268]
- nfs_encode_compound [ms-nfs41-client\daemon\nfs41_xdr.c:3739]
- libtirpc.dll!authunix_wrap [ms-nfs41-client\libtirpc\src\auth_unix.c:386]
- libtirpc.dll!clnt_vc_call [ms-nfs41-client\libtirpc\src\clnt_vc.c:634]
- nfs41_send_compound [ms-nfs41-client\daemon\nfs41_rpc.c:391]
- compound_encode_send_decode [ms-nfs41-client\daemon\nfs41_compound.c:195]
- nfs41_unlock [ms-nfs41-client\daemon\nfs41_ops.c:1212]
- map_current_user_to_ids [ms-nfs41-client\daemon\nfs41_daemon.c:111]
- handle_unlock [ms-nfs41-client\daemon\lock.c:412]
- upcall_handle [ms-nfs41-client\daemon\upcall.c:216]
- nfsd_worker_thread_main [ms-nfs41-client\daemon\nfs41_daemon.c:230]
- nfsd_thread_main [ms-nfs41-client\daemon\nfs41_daemon.c:279]
- KERNEL32.dll!BaseThreadInitThunk +0x13 (0x00007ff82a207374 <KERNEL32.dll+0x17374>)
- ---- snip ----
- Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
- ---
- daemon/nfs41_ops.c | 1 +
- 1 file changed, 1 insertion(+)
- diff --git a/daemon/nfs41_ops.c b/daemon/nfs41_ops.c
- index 9d3d720..6bc62b0 100644
- --- a/daemon/nfs41_ops.c
- +++ b/daemon/nfs41_ops.c
- @@ -1204,6 +1204,7 @@ int nfs41_unlock(
- compound_add_op(&compound, OP_LOCKU, &locku_args, &locku_res);
- /* 18.12.3: the server MUST accept any legal value for locktype */
- locku_args.locktype = READ_LT;
- + locku_args.seqid = 0; /* ignored */
- locku_args.offset = offset;
- locku_args.length = length;
- locku_args.lock_stateid = stateid;
- --
- 2.51.0
- From 0959ef20a6c9dcab408132d3f2782808f3a4065a Mon Sep 17 00:00:00 2001
- From: Roland Mainz <roland.mainz@nrubsig.org>
- Date: Fri, 23 Jan 2026 19:40:25 +0100
- Subject: [PATCH 2/5] daemon: Add more detailed debug output in lock+unlock
- error codepaths
- Add more detailed debug output in lock+unlock error codepaths.
- Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
- ---
- daemon/lock.c | 33 ++++++++++++++++++++++++++++++++-
- 1 file changed, 32 insertions(+), 1 deletion(-)
- diff --git a/daemon/lock.c b/daemon/lock.c
- index 7265fd4..96d1f03 100644
- --- a/daemon/lock.c
- +++ b/daemon/lock.c
- @@ -250,7 +250,14 @@ static int handle_lock_retry(void *deamon_context, nfs41_upcall *upcall)
- status = nfs41_lock(state->session, &state->file, &state->owner,
- type, lock->offset, lock->length, FALSE, TRUE, &stateid);
- if (status) {
- - DPRINTF(LKLVL, ("nfs41_lock failed with '%s'\n",
- + DPRINTF(0/*LKLVL*/,
- + ("handle_lock_retry(state->path.path='%s' "
- + "args->(offset=%llu,length=%llu,exclusive=%d)): "
- + "nfs41_lock() failed with '%s'\n",
- + state->path.path,
- + (unsigned long long)args->offset,
- + (unsigned long long)args->length,
- + (int)args->exclusive,
- nfs_error_string(status)));
- status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
- LeaveCriticalSection(&state->locks.lock);
- @@ -343,6 +350,16 @@ static void cancel_lock(IN nfs41_upcall *upcall)
- status = nfs41_unlock(state->session, &state->file,
- args->offset, args->length, &stateid);
- + if (status) {
- + DPRINTF(0/*LKLVL*/,
- + ("cancel_lock(state->path.path='%s' "
- + "args->(offset=%llu,length=%llu)): "
- + "nfs41_unlock() failed with '%s'\n",
- + state->path.path,
- + (unsigned long long)args->offset,
- + (unsigned long long)args->length,
- + nfs_error_string(status)));
- + }
- open_unlock_remove(state, &stateid, &input);
- LeaveCriticalSection(&state->locks.lock);
- @@ -406,6 +423,20 @@ static int handle_unlock(void *daemon_context, nfs41_upcall *upcall)
- status = nfs41_unlock(state->session, &state->file,
- input.offset, input.length, &stateid);
- + if (status) {
- + DPRINTF(0/*LKLVL*/,
- + ("handle_unlock(state->path.path='%s' "
- + "args->count=%d i=%d\n"
- + "input.(offset=%llu,length=%llu)): "
- + "nfs41_unlock() failed with '%s'\n",
- + state->path.path,
- + (int)args->count,
- + (int)i,
- + (unsigned long long)input.offset,
- + (unsigned long long)input.length,
- + nfs_error_string(status)));
- + }
- +
- open_unlock_remove(state, &stateid, &input);
- LeaveCriticalSection(&state->locks.lock);
- --
- 2.51.0
- From 946f059ff6abc212d5003a9824c4bbad2d645350 Mon Sep 17 00:00:00 2001
- From: Roland Mainz <roland.mainz@nrubsig.org>
- Date: Sat, 24 Jan 2026 14:26:07 +0100
- Subject: [PATCH 3/5] daemon,sys: Add infrastructure so |nfs41_unlock()| can
- use the kernel |LOWIO_LOCK_LIST.ExclusiveLock|
- Add infrastructure so |nfs41_unlock()| can use the kernel
- |LOWIO_LOCK_LIST.ExclusiveLock|.
- This technically works, except that Windows 10 has a bug which does not
- assign a value to |LOWIO_LOCK_LIST.ExclusiveLock| (yet). For
- now we always assume |LOWIO_LOCK_LIST.ExclusiveLock==1|, but keep the
- code around so it can be used when the Windows 10 bug has been fixed.
- Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
- ---
- daemon/lock.c | 39 +++++++++++++++++++++++-------
- daemon/nfs41_ops.c | 9 +++++--
- daemon/nfs41_ops.h | 3 ++-
- sys/nfs41sys_driver.h | 1 +
- sys/nfs41sys_lock.c | 56 +++++++++++++++++++++++++++++++++++++++----
- 5 files changed, 91 insertions(+), 17 deletions(-)
- diff --git a/daemon/lock.c b/daemon/lock.c
- index 96d1f03..3dcec3e 100644
- --- a/daemon/lock.c
- +++ b/daemon/lock.c
- @@ -1,6 +1,6 @@
- /* NFSv4.1 client for Windows
- * Copyright (C) 2012 The Regents of the University of Michigan
- - * Copyright (C) 2023-2025 Roland Mainz <roland.mainz@nrubsig.org>
- + * Copyright (C) 2023-2026 Roland Mainz <roland.mainz@nrubsig.org>
- *
- * Olga Kornievskaia <aglo@umich.edu>
- * Casey Bodley <cbodley@umich.edu>
- @@ -187,9 +187,14 @@ static int parse_lock(
- EASSERT(length == 0);
- - DPRINTF(1, ("parsing NFS41_SYSOP_LOCK: offset=0x%llx length=0x%llx exclusive=%u "
- - "blocking=%u\n", args->offset, args->length, args->exclusive,
- - args->blocking));
- + /* Catch non-boolean values */
- + EASSERT((args->exclusive == TRUE) || (args->exclusive == FALSE));
- + EASSERT((args->blocking == TRUE) || (args->blocking == FALSE));
- +
- + DPRINTF(1, ("parsing NFS41_SYSOP_LOCK: offset=0x%llx length=0x%llx exclusive=%d "
- + "blocking=%d\n", args->offset, args->length,
- + (int)args->exclusive,
- + (int)args->blocking));
- out:
- return status;
- }
- @@ -329,6 +334,7 @@ static void cancel_lock(IN nfs41_upcall *upcall)
- lock_upcall_args *args = &upcall->args.lock;
- nfs41_open_state *state = upcall->state_ref;
- int status = NO_ERROR;
- + bool exclusivelock;
- DPRINTF(1, ("--> cancel_lock()\n"));
- @@ -339,6 +345,11 @@ static void cancel_lock(IN nfs41_upcall *upcall)
- input.offset = args->offset;
- input.length = args->length;
- + exclusivelock = (bool)args->exclusive;
- +
- + /* Catch non-boolean values */
- + EASSERT((exclusivelock == TRUE) || (exclusivelock == FALSE));
- +
- /* search for the range to unlock, and remove if delegated */
- status = open_unlock_delegate(state, &input);
- @@ -349,15 +360,17 @@ static void cancel_lock(IN nfs41_upcall *upcall)
- lock_stateid_arg(state, &stateid);
- status = nfs41_unlock(state->session, &state->file,
- - args->offset, args->length, &stateid);
- + (exclusivelock?WRITE_LT:READ_LT), args->offset, args->length,
- + &stateid);
- if (status) {
- DPRINTF(0/*LKLVL*/,
- ("cancel_lock(state->path.path='%s' "
- - "args->(offset=%llu,length=%llu)): "
- + "args->(offset=%llu,length=%llu,exclusive=%d)): "
- "nfs41_unlock() failed with '%s'\n",
- state->path.path,
- (unsigned long long)args->offset,
- (unsigned long long)args->length,
- + (int)args->exclusive,
- nfs_error_string(status)));
- }
- @@ -381,7 +394,7 @@ static int parse_unlock(
- status = safe_read(&buffer, &length, &args->count, sizeof(ULONG));
- if (status) goto out;
- - args->buf_len = args->count*2L*sizeof(LONGLONG);
- + args->buf_len = args->count*(2L*sizeof(LONGLONG)+sizeof(BOOLEAN));
- status = get_safe_read_bufferpos(&buffer, &length,
- args->buf_len, (const void **)&args->buf);
- if (status) goto out;
- @@ -405,8 +418,14 @@ static int handle_unlock(void *daemon_context, nfs41_upcall *upcall)
- int status = NO_ERROR;
- for (i = 0; i < args->count; i++) {
- + BOOLEAN exclusivelock;
- +
- if (safe_read(&buf, &buf_len, &input.offset, sizeof(LONGLONG))) break;
- if (safe_read(&buf, &buf_len, &input.length, sizeof(LONGLONG))) break;
- + if (safe_read(&buf, &buf_len, &exclusivelock, sizeof(BOOLEAN))) break;
- +
- + /* Catch non-boolean values */
- + EASSERT((exclusivelock == TRUE) || (exclusivelock == FALSE));
- /* do the same translation as LOCK, or the ranges won't match */
- if (input.length >= NFS4_UINT64_MAX - input.offset)
- @@ -421,19 +440,21 @@ static int handle_unlock(void *daemon_context, nfs41_upcall *upcall)
- lock_stateid_arg(state, &stateid);
- status = nfs41_unlock(state->session, &state->file,
- - input.offset, input.length, &stateid);
- + (exclusivelock?WRITE_LT:READ_LT), input.offset, input.length,
- + &stateid);
- if (status) {
- DPRINTF(0/*LKLVL*/,
- ("handle_unlock(state->path.path='%s' "
- "args->count=%d i=%d\n"
- - "input.(offset=%llu,length=%llu)): "
- + "input.(offset=%llu,length=%llu) exclusivelock=%d): "
- "nfs41_unlock() failed with '%s'\n",
- state->path.path,
- (int)args->count,
- (int)i,
- (unsigned long long)input.offset,
- (unsigned long long)input.length,
- + (int)exclusivelock,
- nfs_error_string(status)));
- }
- diff --git a/daemon/nfs41_ops.c b/daemon/nfs41_ops.c
- index 6bc62b0..2dfd1e6 100644
- --- a/daemon/nfs41_ops.c
- +++ b/daemon/nfs41_ops.c
- @@ -1176,6 +1176,7 @@ out:
- int nfs41_unlock(
- IN nfs41_session *session,
- IN nfs41_path_fh *file,
- + IN uint32_t type,
- IN uint64_t offset,
- IN uint64_t length,
- IN OUT stateid_arg *stateid)
- @@ -1202,8 +1203,12 @@ int nfs41_unlock(
- putfh_args.in_recovery = 0;
- compound_add_op(&compound, OP_LOCKU, &locku_args, &locku_res);
- - /* 18.12.3: the server MUST accept any legal value for locktype */
- - locku_args.locktype = READ_LT;
- + /*
- + * https://datatracker.ietf.org/doc/html/rfc5661 Section 18.12.3 says:
- + * "... the server MUST accept any legal value for locktype ..."
- + * but we try to be nice and pass the correct value anyway.
- + */
- + locku_args.locktype = type;
- locku_args.seqid = 0; /* ignored */
- locku_args.offset = offset;
- locku_args.length = length;
- diff --git a/daemon/nfs41_ops.h b/daemon/nfs41_ops.h
- index c7d45a8..c186359 100644
- --- a/daemon/nfs41_ops.h
- +++ b/daemon/nfs41_ops.h
- @@ -1,6 +1,6 @@
- /* NFSv4.1 client for Windows
- * Copyright (C) 2012 The Regents of the University of Michigan
- - * Copyright (C) 2024-2025 Roland Mainz <roland.mainz@nrubsig.org>
- + * Copyright (C) 2024-2026 Roland Mainz <roland.mainz@nrubsig.org>
- *
- * Olga Kornievskaia <aglo@umich.edu>
- * Casey Bodley <cbodley@umich.edu>
- @@ -1365,6 +1365,7 @@ int nfs41_lock(
- int nfs41_unlock(
- IN nfs41_session *session,
- IN nfs41_path_fh *file,
- + IN uint32_t type,
- IN uint64_t offset,
- IN uint64_t length,
- IN OUT stateid_arg *stateid);
- diff --git a/sys/nfs41sys_driver.h b/sys/nfs41sys_driver.h
- index 670285c..3835bca 100644
- --- a/sys/nfs41sys_driver.h
- +++ b/sys/nfs41sys_driver.h
- @@ -222,6 +222,7 @@ typedef struct _updowncall_entry {
- BOOLEAN blocking;
- } Lock;
- struct {
- + USHORT lowio_operation;
- ULONG count;
- LOWIO_LOCK_LIST locks;
- } Unlock;
- diff --git a/sys/nfs41sys_lock.c b/sys/nfs41sys_lock.c
- index be7bcb5..d31b0e4 100644
- --- a/sys/nfs41sys_lock.c
- +++ b/sys/nfs41sys_lock.c
- @@ -139,7 +139,7 @@ NTSTATUS marshal_nfs41_unlock(
- tmp += *len;
- header_len = *len + sizeof(ULONG) +
- - ((size_t)entry->u.Unlock.count * 2) * sizeof(LONGLONG);
- + ((size_t)entry->u.Unlock.count) * (2L*sizeof(LONGLONG)+sizeof(BOOLEAN));
- if (header_len > buf_len) {
- DbgP("marshal_nfs41_unlock: "
- "upcall buffer too small: header_len(=%ld) > buf_len(=%ld)\n",
- @@ -152,10 +152,42 @@ NTSTATUS marshal_nfs41_unlock(
- lock = &entry->u.Unlock.locks;
- while (lock) {
- + BOOLEAN exclusivelock;
- +
- RtlCopyMemory(tmp, &lock->ByteOffset, sizeof(LONGLONG));
- tmp += sizeof(LONGLONG);
- RtlCopyMemory(tmp, &lock->Length, sizeof(LONGLONG));
- tmp += sizeof(LONGLONG);
- +#define WINDOWS_LOWIO_OP_UNLOCK_HAS_RANDOM_VALUE_IN_LOWIO_LOCK_LIST_BUG 1
- +
- +#ifdef WINDOWS_LOWIO_OP_UNLOCK_HAS_RANDOM_VALUE_IN_LOWIO_LOCK_LIST_BUG
- + if (entry->u.Unlock.lowio_operation == LOWIO_OP_UNLOCK_MULTIPLE) {
- + /*
- + * Windows 10 bugs:
- + * 1. For |LOWIO_OP_UNLOCK_MULTIPLE| |lock->ExclusiveLock| has
- + * random(uninitialised var ?)
- + * 2. For |LOWIO_OP_UNLOCK| |lock->ExclusiveLock| is always |0|
- + *
- + * As workaround for both [1] and [2] we always set the value
- + * to |TRUE| for now.
- + * This only works because |nfs41_unlock()| states:
- + * ---- snip ----
- + * https://datatracker.ietf.org/doc/html/rfc5661 Section 18.12.3 says:
- + * "... the server MUST accept any legal value for locktype ..."
- + * ---- snip ----
- + */
- + exclusivelock = TRUE/*lock->ExclusiveLock?TRUE:FALSE*/;
- + }
- + else {
- + exclusivelock = TRUE/*lock->ExclusiveLock?TRUE:FALSE*/;
- + }
- +#else
- + exclusivelock = lock->ExclusiveLock?TRUE:FALSE;
- +#endif /* WINDOWS_LOWIO_OP_UNLOCK_HAS_RANDOM_VALUE_IN_LOWIO_LOCK_LIST_BUG */
- +
- + RtlCopyMemory(tmp, &exclusivelock, sizeof(BOOLEAN));
- + tmp += sizeof(BOOLEAN);
- +
- lock = lock->Next;
- }
- @@ -355,14 +387,21 @@ static void print_unlock_args(
- PLOWIO_LOCK_LIST lock = LowIoContext->ParamsFor.Locks.LockList;
- DbgP("LOWIO_OP_UNLOCK_MULTIPLE:");
- while (lock) {
- - DbgP(" (offset=%llu, length=%llu)", lock->ByteOffset, lock->Length);
- + DbgP(" (offset=%llu, length=%llu, exclusivelock=%d)",
- + lock->ByteOffset,
- + lock->Length,
- + (int)lock->ExclusiveLock);
- lock = lock->Next;
- }
- DbgP("\n");
- } else {
- - DbgP("LOWIO_OP_UNLOCK: offset=%llu, length=%llu\n",
- + ULONG flags = LowIoContext->ParamsFor.Locks.Flags;
- + DbgP("LOWIO_OP_UNLOCK: "
- + "offset=%llu, length=%llu, exclusivelock=%d, flags=0x%lx\n",
- LowIoContext->ParamsFor.Locks.ByteOffset,
- - LowIoContext->ParamsFor.Locks.Length);
- + LowIoContext->ParamsFor.Locks.Length,
- + (int)BooleanFlagOn(flags, SL_EXCLUSIVE_LOCK),
- + (long)flags);
- }
- }
- @@ -406,6 +445,8 @@ NTSTATUS nfs41_Unlock(
- /* RxReleaseFcbResourceForThreadInMRx(RxContext, RxContext->pFcb,
- LowIoContext->ResourceThreadId); */
- + print_unlock_args(RxContext);
- +
- #ifdef NFS41_DRIVER_HACK_LOCKING_STORAGE32_RANGELOCK_PROBING
- /*
- * FIXME: This does NOT handle |LOWIO_OP_UNLOCK_MULTIPLE|
- @@ -425,7 +466,9 @@ NTSTATUS nfs41_Unlock(
- pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
- if (status) goto out;
- - if (LowIoContext->Operation == LOWIO_OP_UNLOCK_MULTIPLE) {
- + entry->u.Unlock.lowio_operation = LowIoContext->Operation;
- +
- + if (entry->u.Unlock.lowio_operation == LOWIO_OP_UNLOCK_MULTIPLE) {
- entry->u.Unlock.count = unlock_list_count(
- LowIoContext->ParamsFor.Locks.LockList);
- RtlCopyMemory(&entry->u.Unlock.locks,
- @@ -437,6 +480,9 @@ NTSTATUS nfs41_Unlock(
- LowIoContext->ParamsFor.Locks.ByteOffset;
- entry->u.Unlock.locks.Length =
- LowIoContext->ParamsFor.Locks.Length;
- + entry->u.Unlock.locks.ExclusiveLock =
- + BooleanFlagOn(LowIoContext->ParamsFor.Locks.Flags, SL_EXCLUSIVE_LOCK);
- + entry->u.Unlock.locks.Next = NULL;
- }
- status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
- --
- 2.51.0
- From 0a2bd708c6b17a9f08c7c5599c861bfec08b3c89 Mon Sep 17 00:00:00 2001
- From: Roland Mainz <roland.mainz@nrubsig.org>
- Date: Sat, 24 Jan 2026 14:53:26 +0100
- Subject: [PATCH 4/5] sys: Fix |printf()|-style format "0x%ld" to "0x%lx"
- Fix |printf()|-style format "0x%ld" to "0x%lx".
- Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
- ---
- sys/nfs41sys_fsctl.c | 2 +-
- sys/nfs41sys_lock.c | 4 ++--
- 2 files changed, 3 insertions(+), 3 deletions(-)
- diff --git a/sys/nfs41sys_fsctl.c b/sys/nfs41sys_fsctl.c
- index 27c0946..43925eb 100644
- --- a/sys/nfs41sys_fsctl.c
- +++ b/sys/nfs41sys_fsctl.c
- @@ -315,7 +315,7 @@ NTSTATUS marshal_nfs41_queryallocatedranges(
- }
- DbgP("marshal_nfs41_queryallocatedranges: name='%wZ' "
- - "buffersize=0x%ld, buffer=0x%p\n",
- + "buffersize=0x%lx, buffer=0x%p\n",
- entry->filename,
- (long)entry->u.QueryAllocatedRanges.BufferSize,
- (void *)entry->u.QueryAllocatedRanges.Buffer);
- diff --git a/sys/nfs41sys_lock.c b/sys/nfs41sys_lock.c
- index d31b0e4..c4dad13 100644
- --- a/sys/nfs41sys_lock.c
- +++ b/sys/nfs41sys_lock.c
- @@ -331,7 +331,7 @@ NTSTATUS nfs41_Lock(
- #ifdef NFS41_DRIVER_HACK_LOCKING_STORAGE32_RANGELOCK_PROBING
- if (rangelock_hack_skiplock(LowIoContext, nfs41_fcb)) {
- - DbgP("nfs41_Lock: RANGELOCK hack 0x%ld\n",
- + DbgP("nfs41_Lock: RANGELOCK hack 0x%lx\n",
- (long)LowIoContext->ParamsFor.Locks.ByteOffset);
- status = STATUS_SUCCESS;
- goto out;
- @@ -454,7 +454,7 @@ NTSTATUS nfs41_Unlock(
- * use it
- */
- if (rangelock_hack_skiplock(LowIoContext, nfs41_fcb)) {
- - DbgP("nfs41_Unlock: RANGELOCK hack 0x%ld\n",
- + DbgP("nfs41_Unlock: RANGELOCK hack 0x%lx\n",
- (long)LowIoContext->ParamsFor.Locks.ByteOffset);
- status = STATUS_SUCCESS;
- goto out;
- --
- 2.51.0
- From c5542c985e0c85195a4a70223213fe5d66230b2f Mon Sep 17 00:00:00 2001
- From: Roland Mainz <roland.mainz@nrubsig.org>
- Date: Sat, 24 Jan 2026 19:23:02 +0100
- Subject: [PATCH 5/5] tests: winstreamutil: Implement cat(1)-like catstream
- subcmd
- winstreamutil: Implement cat(1)-like catstream subcmd to
- print out the contents of a stream (without depending in cmd.exe
- or powershell).
- Signed-off-by: Cedric Blancher <cedric.blancher@gmail.com>
- ---
- tests/winstreamsutil/winstreamsutil.c | 116 ++++++++++++++++++++++++++
- 1 file changed, 116 insertions(+)
- diff --git a/tests/winstreamsutil/winstreamsutil.c b/tests/winstreamsutil/winstreamsutil.c
- index 54e2429..bb63249 100644
- --- a/tests/winstreamsutil/winstreamsutil.c
- +++ b/tests/winstreamsutil/winstreamsutil.c
- @@ -197,6 +197,118 @@ int cmd_find(int ac, wchar_t *av[])
- return EXIT_SUCCESS;
- }
- +static
- +bool CopyHANDLEToHANDLE(HANDLE hSrc, HANDLE hDest)
- +{
- + BYTE buffer[4096];
- + DWORD dwBytesRead = 0;
- + DWORD dwBytesWritten = 0;
- + BOOL bSuccess = FALSE;
- + bool retval = false;
- +
- + while (ReadFile(hSrc, buffer, sizeof(buffer), &dwBytesRead, NULL) &&
- + (dwBytesRead > 0)) {
- + bSuccess = WriteFile(hDest,
- + buffer,
- + dwBytesRead,
- + &dwBytesWritten,
- + NULL);
- +
- + if (!bSuccess || (dwBytesRead != dwBytesWritten)) {
- + (void)fwprintf(stderr,
- + L"Failed to write to the destination file, lasterr=%d\n",
- + (int)GetLastError());
- + retval = false;
- + goto done;
- + }
- + }
- +
- + retval = true;
- +
- +done:
- + return retval;
- +}
- +
- +static
- +int cmd_catstream(int ac, wchar_t *av[])
- +{
- + int res;
- + HANDLE h;
- + bool ok;
- + bool print_usage = false;
- + int i;
- + const wchar_t *progname = av[0];
- + wchar_t *base_path = NULL;
- + wchar_t *src_streamname = NULL;
- +
- + for (i=2 ; i < ac ; i++) {
- + if (av[i][0] == L'/') {
- + if (wcscmp(av[i], L"/?") == 0)
- + print_usage = true;
- + else {
- + (void)fwprintf(stderr, L"%ls: Unknown option '%ls'\n",
- + progname, av[i]);
- + return EXIT_FAILURE;
- + }
- + }
- + else {
- + if (base_path == NULL)
- + base_path = av[i];
- + else if (src_streamname == NULL)
- + src_streamname = av[i];
- + else {
- + (void)fwprintf(stderr,
- + L"%ls: Too many filenames\n", progname);
- + return EXIT_FAILURE;
- + }
- + }
- + }
- +
- + if ((base_path == NULL) && (src_streamname == NULL))
- + print_usage = true;
- +
- + if (print_usage) {
- + (void)fwprintf(stderr,
- + L"Usage: winstreamutil catstream path srcstreamname\n"
- + L"\tpath\tPath of base file/dir (e.g. C:\\foo.txt)\n"
- + L"\tsrcstreamname\tsrc stream name (e.g. \":mystr1:$DATA\")\n");
- + return EXIT_USAGE;
- + }
- +
- + if ((base_path == NULL) || (src_streamname == NULL)) {
- + (void)fwprintf(stderr,
- + L"%ls: Missing paths/stream.\n", progname);
- + return EXIT_FAILURE;
- + }
- +
- + if (src_streamname[0] != L':') {
- + (void)fwprintf(stderr,
- + L"%ls: Stream name must start with ':'\n", progname);
- + return EXIT_FAILURE;
- + }
- +
- + wchar_t src_stream_path[NT_MAX_LONG_PATH];
- + (void)swprintf(src_stream_path, NT_MAX_LONG_PATH,
- + L"%ls%ls", base_path, src_streamname);
- +
- + h = CreateFileW(src_stream_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
- + if (h == INVALID_HANDLE_VALUE) {
- + (void)fwprintf(stderr,
- + L"%ls: Cannot open src stream '%ls', lasterr=%d\n",
- + progname,
- + src_stream_path, (int)GetLastError());
- + return EXIT_FAILURE;
- + }
- +
- + ok = CopyHANDLEToHANDLE(h, GetStdHandle(STD_OUTPUT_HANDLE));
- + res = ok?EXIT_SUCCESS:EXIT_FAILURE;
- +
- + (void)CloseHandle(h);
- +
- + return res;
- +}
- +
- +
- typedef LONG NTSTATUS;
- #ifndef NT_SUCCESS
- #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
- @@ -551,6 +663,7 @@ void usage(const wchar_t *restrict progname)
- L"Available commands:\n"
- L"info\tprint info about a stream as ksh93 compound variable\n"
- L"find\tfind all non-default named streams in path\n"
- + L"catstream\tCopy data of stream to stdout\n"
- L"renamestream\trename stream\n"
- L"deletestream\tdelete stream\n",
- progname);
- @@ -574,6 +687,9 @@ int wmain(int ac, wchar_t *av[])
- else if (wcscmp(av[1], L"find") == 0) {
- return cmd_find(ac, av);
- }
- + else if (wcscmp(av[1], L"catstream") == 0) {
- + return cmd_catstream(ac, av);
- + }
- else if (wcscmp(av[1], L"renamestream") == 0) {
- return cmd_renamestream(ac, av);
- }
- --
- 2.51.0
msnfs41client: Patches for winstreamutil catstream subcmd, tests, unlocking fixes+misc, 2026-01-24
Posted by Anonymous on Sat 24th Jan 2026 18:38
raw | new post
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.
rovema.kpaste.net RSS