pastebin - collaborative debugging tool
nrubsig.kpaste.net RSS


winstreamsutil.c - Win32 named streams utility
Posted by Anonymous on Wed 21st Jan 2026 03:22
raw | new post

  1. /*
  2.  * MIT License
  3.  *
  4.  * Copyright (c) 2004-2026 Roland Mainz <roland.mainz@nrubsig.org>
  5.  *
  6.  * Permission is hereby granted, free of charge, to any person obtaining a copy
  7.  * of this software and associated documentation files (the "Software"), to deal
  8.  * in the Software without restriction, including without limitation the rights
  9.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10.  * copies of the Software, and to permit persons to whom the Software is
  11.  * furnished to do so, subject to the following conditions:
  12.  *
  13.  * The above copyright notice and this permission notice shall be included in all
  14.  * copies or substantial portions of the Software.
  15.  *
  16.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19.  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22.  * SOFTWARE.
  23.  */
  24.  
  25. /*
  26.  * winstreamsutil.c - Win32 named streams utility
  27.  *
  28.  * Written by Roland Mainz <roland.mainz@nrubsig.org>
  29.  */
  30.  
  31. /*
  32.  * Compile with
  33.  * $ clang -target x86_64-pc-windows-gnu -std=gnu17 -Wall -Wextra \
  34.  * -municode -g winstreamsutil.c \
  35.  * -lntdll -o winstreamsutil.exe
  36.  */
  37.  
  38. #define UNICODE 1
  39. #define _UNICODE 1
  40.  
  41. #include <windows.h>
  42. #include <stdlib.h>
  43. #include <stdbool.h>
  44. #include <stdio.h>
  45. #include <wchar.h>
  46.  
  47. #define EXIT_USAGE 2 /* Traditional UNIX exit for fore |usage()| */
  48.  
  49. #define NT_MAX_LONG_PATH 1024/*32767*/
  50.  
  51. static
  52. int lsstream_list_streams(const wchar_t *restrict path, bool skip_default_stream)
  53. {
  54.     WIN32_FIND_STREAM_DATA sd;
  55.     ZeroMemory(&sd, sizeof(sd));
  56.  
  57.     HANDLE h = FindFirstStreamW(path, FindStreamInfoStandard, &sd, 0);
  58.     if (h == INVALID_HANDLE_VALUE) {
  59.         DWORD e = GetLastError();
  60.         if (e == ERROR_HANDLE_EOF)
  61.             return 0;
  62.         (void)fwprintf(stderr, L"FindFirstStreamW(path='%ls'), status=%d\n",
  63.             path, (int)e);
  64.         return 3;
  65.     }
  66.  
  67.     for (;;) {
  68.         if (skip_default_stream) {
  69.             if (wcscmp(sd.cStreamName, L"::$DATA") == 0)
  70.                 goto nextstr;
  71.         }
  72.  
  73.         (void)wprintf(L"%ls%ls\n", path, sd.cStreamName);
  74.  
  75. nextstr:
  76.         if (!FindNextStreamW(h, &sd)) {
  77.             DWORD e = GetLastError();
  78.             if (e == ERROR_HANDLE_EOF)
  79.                 break;
  80.             (void)fwprintf(stderr, L"FindNextStreamW() returned lasterr=%d\n", (int)e);
  81.             FindClose(h);
  82.             return 4;
  83.         }
  84.     }
  85.  
  86.     FindClose(h);
  87.     return 0;
  88. }
  89.  
  90. static
  91. int lsstream_walk(const wchar_t *restrict path, bool find_recursive)
  92. {
  93.     wchar_t pattern[NT_MAX_LONG_PATH];
  94.     (void)swprintf(pattern, NT_MAX_LONG_PATH, L"%ls\\*", path);
  95.  
  96.     WIN32_FIND_DATAW fd;
  97.     HANDLE h = FindFirstFileW(pattern, &fd);
  98.     if (h == INVALID_HANDLE_VALUE) {
  99.         (void)fwprintf(stderr,
  100.             L"FindFirstFileW(path=%ls) returned lasterr=%d\n",
  101.             path, (int)GetLastError());
  102.         return EXIT_FAILURE;
  103.     }
  104.  
  105.     do {
  106.         if (wcscmp(fd.cFileName, L".") == 0 ||
  107.             wcscmp(fd.cFileName, L"..") == 0)
  108.             continue;
  109.  
  110.         wchar_t full[NT_MAX_LONG_PATH];
  111.         (void)swprintf(full, NT_MAX_LONG_PATH, L"%ls\\%ls", path, fd.cFileName);
  112.  
  113.         lsstream_list_streams(full, true);
  114.  
  115.         if (find_recursive && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  116.             lsstream_walk(full, find_recursive);
  117.         }
  118.     } while (FindNextFileW(h, &fd));
  119.  
  120.     DWORD e = GetLastError();
  121.     if (e != ERROR_NO_MORE_FILES) {
  122.         (void)fwprintf(stderr, L"FindNextFileW() returned lasterr=%d\n", (int)e);
  123.     }
  124.  
  125.     FindClose(h);
  126.     return EXIT_SUCCESS;
  127. }
  128.  
  129. static
  130. int cmd_find(int ac, wchar_t *av[])
  131. {
  132.     bool find_recursive = false;
  133.     bool print_usage = false;
  134.     int i;
  135.     wchar_t *find_path = NULL;
  136.  
  137.     for (i=2 ; i < ac ; i++) {
  138.         if (av[i][0] == L'/') {
  139.             if (wcscmp(av[i], L"/?") == 0)
  140.                 print_usage = true;
  141.             else if (wcscmp(av[i], L"/s") == 0)
  142.                 find_recursive = true;
  143.             else if (wcscmp(av[i], L"/-s") == 0)
  144.                 find_recursive = false;
  145.             else {
  146.                 (void)fwprintf(stderr, L"Unknown option '%ls'\n", av[i]);
  147.                 return EXIT_FAILURE;
  148.             }
  149.         }
  150.         else {
  151.             find_path = av[i];
  152.         }
  153.     }
  154.  
  155.     if (print_usage) {
  156.         (void)fwprintf(stderr,
  157.             L"Usage: winstreamutil find [/s} [path]\n"
  158.             L"\t/s\tRecurse into subdirs.\n"
  159.             L"\tpath\tPath to search.");
  160.         return EXIT_USAGE;
  161.     }
  162.  
  163.     if (find_path == NULL) {
  164.         (void)fwprintf(stderr, L"No path given.\n");
  165.             return EXIT_FAILURE;
  166.     }
  167.  
  168.     lsstream_walk(find_path, find_recursive);
  169.     return EXIT_SUCCESS;
  170. }
  171.  
  172. typedef LONG NTSTATUS;
  173. #ifndef NT_SUCCESS
  174. #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
  175. #endif
  176.  
  177. typedef struct _IO_STATUS_BLOCK {
  178.     union { NTSTATUS Status; PVOID Pointer; } DUMMYUNIONNAME;
  179.     ULONG_PTR Information;
  180. } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
  181.  
  182. typedef enum _FILE_INFORMATION_CLASS {
  183.     FileRenameInformation = 10
  184. } FILE_INFORMATION_CLASS;
  185.  
  186. typedef struct _FILE_RENAME_INFORMATION {
  187.     BOOLEAN ReplaceIfExists;
  188.     HANDLE  RootDirectory;
  189.     ULONG   FileNameLength;
  190.     WCHAR   FileName[1];
  191. } FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;
  192.  
  193. NTSTATUS NTAPI NtSetInformationFile(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS);
  194. ULONG NTAPI RtlNtStatusToDosError(NTSTATUS);
  195.  
  196. static
  197. HANDLE OpenForRenameW(const wchar_t *restrict path)
  198. {
  199.     DWORD share  = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
  200.     DWORD flags  = FILE_FLAG_BACKUP_SEMANTICS;
  201.     return CreateFileW(path, DELETE | SYNCHRONIZE, share, NULL, OPEN_EXISTING, flags, NULL);
  202. }
  203.  
  204. static
  205. int cmd_renamestream(int ac, wchar_t *av[])
  206. {
  207.     bool print_usage = false;
  208.     int i;
  209.     wchar_t *base_path = NULL;
  210.     wchar_t *src_streamname = NULL;
  211.     wchar_t *dst_streamname = NULL;
  212.  
  213.     for (i=2 ; i < ac ; i++) {
  214.         if (av[i][0] == L'/') {
  215.             if (wcscmp(av[i], L"/?") == 0)
  216.                 print_usage = true;
  217.             else {
  218.                 (void)fwprintf(stderr, L"Unknown option '%ls'\n", av[i]);
  219.                 return EXIT_FAILURE;
  220.             }
  221.         }
  222.         else {
  223.             if (base_path == NULL)
  224.                 base_path = av[i];
  225.             else if (src_streamname == NULL)
  226.                 src_streamname = av[i];
  227.             else if (dst_streamname == NULL)
  228.                 dst_streamname = av[i];
  229.             else {
  230.                 (void)fwprintf(stderr, L"Too many filenames\n");
  231.                 return EXIT_FAILURE;
  232.             }
  233.         }
  234.     }
  235.  
  236.     if ((base_path == NULL) && (src_streamname == NULL) && (dst_streamname == NULL))
  237.         print_usage = true;
  238.  
  239.     if (print_usage) {
  240.         (void)fwprintf(stderr,
  241.             L"Usage: winstreamutil renamestream path srcstreamname dststreamname\n"
  242.             L"\tpath\tPath of base file/dir (e.g. C:\\foo.txt)\n"
  243.             L"\tsrcstreamname\tsrc stream bae (e.g. \":mystr1:$DATA\")\n"
  244.             L"\tdststreamname\tdst stream bae (e.g. \":mystr2:$DATA\")\n");
  245.         return EXIT_USAGE;
  246.     }
  247.  
  248.     if ((base_path == NULL) || (src_streamname == NULL) || (dst_streamname == NULL)) {
  249.         (void)fwprintf(stderr, L"Missing paths/stream.\n");
  250.             return EXIT_FAILURE;
  251.     }
  252.  
  253.     if ((src_streamname[0] != L':') || (dst_streamname[0] != L':')) {
  254.         (void)fwprintf(stderr, L"Stream names must start with ':'\n");
  255.             return EXIT_FAILURE;
  256.     }
  257.  
  258.     PFILE_RENAME_INFORMATION fri = calloc(1,
  259.         sizeof(FILE_RENAME_INFORMATION)+256*sizeof(wchar_t));
  260.     if (fri == NULL) {
  261.         (void)fwprintf(stderr, L"Out of memory for fri.\n");
  262.             return EXIT_FAILURE;
  263.     }
  264.  
  265.     wchar_t src_stream_path[NT_MAX_LONG_PATH];
  266.     (void)swprintf(src_stream_path, NT_MAX_LONG_PATH,
  267.         L"%ls%ls", base_path, src_streamname);
  268.  
  269.     HANDLE bh = OpenForRenameW(src_stream_path);
  270.     if (bh == INVALID_HANDLE_VALUE) {
  271.         (void)fwprintf(stderr, L"Cannot open src stream '%ls', lasterr=%d\n",
  272.             src_stream_path, (int)GetLastError());
  273.         return EXIT_FAILURE;
  274.     }
  275.  
  276.     fri->ReplaceIfExists = FALSE;
  277.     fri->RootDirectory   = NULL;
  278.     fri->FileNameLength  = wcslen(dst_streamname)*sizeof(wchar_t);
  279.     (void)wcscpy(fri->FileName, dst_streamname);
  280.  
  281.     IO_STATUS_BLOCK iosb = { 0 };
  282.     NTSTATUS status = NtSetInformationFile(bh, &iosb,
  283.         fri,
  284.         (sizeof(FILE_RENAME_INFORMATION)+fri->FileNameLength),
  285.         FileRenameInformation);
  286.  
  287.     bool ok = (bool)NT_SUCCESS(status);
  288.     if (ok) {
  289.         (void)fwprintf(stderr, L"Renamed stream '%ls%ls' to '%ls%ls'.\n",
  290.             base_path, src_streamname,
  291.             base_path, dst_streamname);
  292.     }
  293.     else {
  294.         (void)fwprintf(stderr,
  295.             L"Renaming failed with lasterr=%d\n",
  296.             (int)RtlNtStatusToDosError(status));
  297.     }
  298.  
  299.     (void)CloseHandle(bh);
  300.     free(fri);
  301.  
  302.     return EXIT_SUCCESS;
  303. }
  304.  
  305. static
  306. void usage(const wchar_t *restrict progname)
  307. {
  308.     (void)fwprintf(stderr,
  309.         L"%ls: Available commands:\n"
  310.         L"find\tfind all non-default named streams in path\n"
  311.         L"renamestream\trename stream\n",
  312.         progname);
  313. }
  314.  
  315. int wmain(int argc, wchar_t *argv[])
  316. {
  317.     if (argc < 2) {
  318.         (void)usage(argv[0]);
  319.         return EXIT_USAGE;
  320.     }
  321.  
  322.     /*
  323.      * FIXME: ToDO: Add more sub commands:
  324.      * createnew, queryallocatedranges, delete
  325.      */
  326.  
  327.     if (wcscmp(argv[1], L"find") == 0) {
  328.         return cmd_find(argc, argv);
  329.     }
  330.     else if (wcscmp(argv[1], L"renamestream") == 0) {
  331.         return cmd_renamestream(argc, argv);
  332.     }
  333.     else {
  334.         (void)fwprintf(stderr,
  335.             L"%ls: Unknown subcmd '%ls':\n",
  336.             argv[0], argv[1]);
  337.     }
  338.  
  339.     return EXIT_SUCCESS;
  340. }

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