Process Hacker
main.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * FiIn
4  *
5  * Copyright (C) 2010 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <ph.h>
24 #include <md5.h>
25 #include <sha.h>
26 
27 #define FI_ARG_HELP 1
28 #define FI_ARG_ACTION 2
29 #define FI_ARG_NATIVE 3
30 #define FI_ARG_PATTERN 4
31 #define FI_ARG_CASESENSITIVE 5
32 #define FI_ARG_OUTPUT 6
33 #define FI_ARG_FORCE 7
34 #define FI_ARG_LENGTH 8
35 
36 #define HASH_MD5 1
37 #define HASH_SHA1 2
38 #define HASH_CRC32 3
39 
41 BOOLEAN FiArgHelp;
43 BOOLEAN FiArgNative;
47 BOOLEAN FiArgForce;
48 ULONG64 FiArgLength = MAXULONG64;
49 
54 
55 static BOOLEAN NTAPI FiCommandLineCallback(
56  _In_opt_ PPH_COMMAND_LINE_OPTION Option,
57  _In_opt_ PPH_STRING Value,
58  _In_opt_ PVOID Context
59  )
60 {
61  if (Option)
62  {
63  switch (Option->Id)
64  {
65  case FI_ARG_HELP:
66  FiArgHelp = TRUE;
67  break;
68  case FI_ARG_ACTION:
69  PhSwapReference(&FiArgAction, Value);
70  break;
71  case FI_ARG_NATIVE:
72  FiArgNative = TRUE;
73  break;
74  case FI_ARG_PATTERN:
75  PhSwapReference(&FiArgPattern, Value);
76  break;
79  break;
80  case FI_ARG_OUTPUT:
81  PhSwapReference(&FiArgOutput, Value);
82  break;
83  case FI_ARG_FORCE:
84  FiArgForce = TRUE;
85  break;
86  case FI_ARG_LENGTH:
87  PhStringToInteger64(&Value->sr, 0, (PLONG64)&FiArgLength);
88  break;
89  }
90  }
91  else
92  {
93  if (!FiArgAction)
94  PhSwapReference(&FiArgAction, Value);
95  else if (!FiArgFileName)
96  PhSwapReference(&FiArgFileName, Value);
97  }
98 
99  return TRUE;
100 }
101 
103  VOID
104  )
105 {
106  wprintf(
107  L"FiIn, by wj32.\n"
108  L"fiin action [-C] [-f] [-L length] [-N] [-o filename] [-p pattern] [filename]\n"
109  L"\taction The action to be performed.\n"
110  L"\t-C Specifies that file names are case-sensitive.\n"
111  L"\t-f Forces the action to succeed by overwriting files.\n"
112  L"\t-L length Specifies the length for an operation.\n"
113  L"\t-N Specifies that file names are in native format.\n"
114  L"\t-o filename Specifies the output file name, or the command line.\n"
115  L"\t-p pattern A search pattern for listings.\n"
116  L"\n"
117  L"Actions:\n"
118  L"copy\n"
119  L"del\n"
120  L"dir\n"
121  L"execute\n"
122  L"hash\n"
123  L"map\n"
124  L"mkdir\n"
125  L"rename\n"
126  L"streams\n"
127  L"touch\n"
128  );
129 }
130 
132  _In_ PPH_STRING FileName
133  )
134 {
135  if (!FiArgNative)
136  {
137  PPH_STRING fileName;
138  PPH_STRING fullPath;
139  UNICODE_STRING fileNameUs;
140 
141  // Get the full path first so we can detect long file names.
142  fullPath = PhGetFullPath(FileName->Buffer, NULL);
143 
144  if (fullPath)
145  {
146  if (fullPath->Length / 2 > MAX_PATH)
147  {
148  wprintf(L"Warning: Detected long file name \"%s\".\n", fullPath->Buffer);
149 
150  fileName = PhConcatStrings2(L"\\??\\", fullPath->Buffer);
151  PhDereferenceObject(fullPath);
152 
153  return fileName;
154  }
155 
156  PhDereferenceObject(fullPath);
157  }
158 
160  FileName->Buffer,
161  &fileNameUs,
162  NULL,
163  NULL
164  ))
165  {
166  fileName = PhCreateStringFromUnicodeString(&fileNameUs);
167  RtlFreeHeap(RtlProcessHeap(), 0, fileNameUs.Buffer);
168 
169  return fileName;
170  }
171  else
172  {
173  wprintf(L"Warning: Unable to convert the file name \"%s\" to a NT file name.\n", FileName->Buffer);
174  // Fallback method.
175  return PhConcatStrings2(L"\\??\\", FileName->Buffer);
176  }
177  }
178  else
179  {
180  // Some limited conversions are applied for convenience.
181 
182  if (FileName->Length >= 2 * 2 && iswalpha(FileName->Buffer[0]) && FileName->Buffer[1] == ':')
183  {
184  return PhConcatStrings2(L"\\??\\", FileName->Buffer);
185  }
186  else if (FileName->Buffer[0] != '\\')
187  {
188  return PhFormatString(
189  L"\\??\\%s%s",
190  NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath.Buffer,
191  FileName->Buffer
192  );
193  }
194  else
195  {
196  return PhReferenceObject(FileName);
197  }
198  }
199 }
200 
201 BOOLEAN FiCreateFile(
202  _Out_ PHANDLE FileHandle,
203  _In_ ACCESS_MASK DesiredAccess,
204  _In_ PPH_STRING FileName,
205  _In_opt_ ULONG FileAttributes,
206  _In_ ULONG ShareAccess,
207  _In_ ULONG CreateDisposition,
208  _In_opt_ ULONG Options
209  )
210 {
211  NTSTATUS status;
212  HANDLE fileHandle;
214  IO_STATUS_BLOCK isb;
215  PPH_STRING fileName;
216  UNICODE_STRING fileNameUs;
217 
218  if (!FileAttributes)
219  FileAttributes = FILE_ATTRIBUTE_NORMAL;
220  if (!Options)
222 
223  // Not needed, because we handle Win32 paths anyway.
224  //if (!(FiArgNative))
225  //{
226  // status = PhCreateFileWin32(
227  // FileHandle,
228  // FileName->Buffer,
229  // DesiredAccess,
230  // FileAttributes,
231  // FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
232  // CreateDisposition,
233  // Options
234  // );
235 
236  // if (!NT_SUCCESS(status))
237  // {
238  // wprintf(L"Error creating/opening file: %s\n", PhGetNtMessage(status)->Buffer);
239  // return FALSE;
240  // }
241 
242  // return TRUE;
243  //}
244 
245  fileName = FiFormatFileName(FileName);
246 
247  if (!PhStringRefToUnicodeString(&fileName->sr, &fileNameUs))
248  {
249  PhDereferenceObject(fileName);
250  return FALSE;
251  }
252 
254  &oa,
255  &fileNameUs,
257  NULL,
258  NULL
259  );
260 
261  status = NtCreateFile(
262  &fileHandle,
263  DesiredAccess,
264  &oa,
265  &isb,
266  NULL,
267  FileAttributes,
268  ShareAccess,
269  CreateDisposition,
270  Options,
271  NULL,
272  0
273  );
274 
275  if (!NT_SUCCESS(status))
276  {
277  wprintf(L"Error creating/opening file: %s\n", PhGetNtMessage(status)->Buffer);
278  return FALSE;
279  }
280 
281  *FileHandle = fileHandle;
282 
283  return TRUE;
284 }
285 
287  _In_ PFILE_DIRECTORY_INFORMATION Information,
288  _In_opt_ PVOID Context
289  )
290 {
291  PPH_STRING date, time, size;
292  SYSTEMTIME systemTime;
293 
294  PhLargeIntegerToLocalSystemTime(&systemTime, &Information->LastWriteTime);
295  date = PhFormatDate(&systemTime, NULL);
296  time = PhFormatTime(&systemTime, NULL);
297  size = PhFormatUInt64(Information->EndOfFile.QuadPart, TRUE);
298 
299  wprintf(
300  L"%-10s %12s %c%c%c%c%c%c%c%c %11s %.*s\n",
301  date->Buffer,
302  time->Buffer,
303  (Information->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? '+' : ' ',
304  (Information->FileAttributes & FILE_ATTRIBUTE_HIDDEN) ? 'h' : ' ',
305  (Information->FileAttributes & FILE_ATTRIBUTE_SYSTEM) ? 's' : ' ',
306  (Information->FileAttributes & FILE_ATTRIBUTE_READONLY) ? 'r' : ' ',
307  (Information->FileAttributes & FILE_ATTRIBUTE_COMPRESSED) ? 'z' : ' ',
308  (Information->FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) ? 'e' : ' ',
309  (Information->FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) ? '%' : ' ',
310  (Information->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? '*' : ' ',
311  size->Buffer,
312  Information->FileNameLength / 2,
313  Information->FileName
314  );
315 
316  PhDereferenceObject(date);
317  PhDereferenceObject(time);
318  PhDereferenceObject(size);
319 
320  if (Information->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
321  FipDirDirCount++;
322  else
323  FipDirFileCount++;
324 
325  FipDirTotalSize += Information->EndOfFile.QuadPart;
326  FipDirTotalAllocSize += Information->AllocationSize.QuadPart;
327 
328  return TRUE;
329 }
330 
331 int __cdecl main(int argc, char *argv[])
332 {
333  static PH_COMMAND_LINE_OPTION options[] =
334  {
335  { FI_ARG_HELP, L"h", NoArgumentType },
337  { FI_ARG_NATIVE, L"N", NoArgumentType },
341  { FI_ARG_FORCE, L"f", NoArgumentType },
343  };
344  PH_STRINGREF commandLine;
345  NTSTATUS status = STATUS_SUCCESS;
346 
347  if (!NT_SUCCESS(PhInitializePhLibEx(0, 0, 0)))
348  return 1;
349 
350  PhUnicodeStringToStringRef(&NtCurrentPeb()->ProcessParameters->CommandLine, &commandLine);
351 
352  if (!PhParseCommandLine(
353  &commandLine,
354  options,
355  sizeof(options) / sizeof(PH_COMMAND_LINE_OPTION),
357  FiCommandLineCallback,
358  NULL
359  ) || FiArgHelp)
360  {
361  FiPrintHelp();
362  return 0;
363  }
364 
365  if (!FiArgFileName && (
366  FiArgAction &&
367  PhEqualString2(FiArgAction, L"dir", TRUE)
368  ))
369  {
370  FiArgFileName = PhCreateStringFromUnicodeString(&NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath);
371  }
372 
373  if (!FiArgAction)
374  {
375  FiPrintHelp();
376  return 1;
377  }
378  else if (PhEqualString2(FiArgAction, L"map", TRUE))
379  {
380  WCHAR deviceNameBuffer[7] = L"\\??\\ :";
381  ULONG i;
382  WCHAR targetNameBuffer[0x100];
383  UNICODE_STRING targetName;
384 
385  targetName.Buffer = targetNameBuffer;
386  targetName.MaximumLength = sizeof(targetNameBuffer);
387 
388  for (i = 0; i < 26; i++)
389  {
390  HANDLE linkHandle;
392  UNICODE_STRING deviceName;
393 
394  deviceNameBuffer[4] = (WCHAR)('A' + i);
395  deviceName.Buffer = deviceNameBuffer;
396  deviceName.Length = 6 * sizeof(WCHAR);
397 
399  &oa,
400  &deviceName,
402  NULL,
403  NULL
404  );
405 
406  if (NT_SUCCESS(NtOpenSymbolicLinkObject(
407  &linkHandle,
408  SYMBOLIC_LINK_QUERY,
409  &oa
410  )))
411  {
412  if (NT_SUCCESS(NtQuerySymbolicLinkObject(
413  linkHandle,
414  &targetName,
415  NULL
416  )))
417  {
418  wprintf(L"%c: %.*s\n", 'A' + i, targetName.Length / 2, targetName.Buffer);
419  }
420 
421  NtClose(linkHandle);
422  }
423  }
424  }
425  else if (!FiArgFileName)
426  {
427  wprintf(L"Error: file name missing.\n");
428  FiPrintHelp();
429  return 1;
430  }
431  else if (PhEqualString2(FiArgAction, L"hash", TRUE))
432  {
433  HANDLE fileHandle;
434  LARGE_INTEGER fileSize;
435  IO_STATUS_BLOCK isb;
436  ULONG mode;
437 
438  if (!FiArgOutput)
439  mode = HASH_MD5;
440  else if (PhEqualString2(FiArgOutput, L"md5", TRUE))
441  mode = HASH_MD5;
442  else if (PhEqualString2(FiArgOutput, L"sha1", TRUE))
443  mode = HASH_SHA1;
444  else if (PhEqualString2(FiArgOutput, L"crc32", TRUE))
445  mode = HASH_CRC32;
446  else
447  {
448  wprintf(L"Invalid hash algorithm. Possibilities: md5, sha1, crc32\n");
449  return 1;
450  }
451 
452  if (FiCreateFile(
453  &fileHandle,
454  FILE_GENERIC_READ,
455  FiArgFileName,
456  0,
457  FILE_SHARE_READ | FILE_SHARE_DELETE,
458  FILE_OPEN,
460  ))
461  {
462  if (NT_SUCCESS(status = PhGetFileSize(fileHandle, &fileSize)))
463  {
464  MD5_CTX md5Context;
465  A_SHA_CTX shaContext;
466  ULONG crc;
467  UCHAR buffer[PAGE_SIZE * 4];
468  ULONG64 bytesRemaining;
469 
470  bytesRemaining = fileSize.QuadPart;
471 
472  switch (mode)
473  {
474  case HASH_MD5:
475  MD5Init(&md5Context);
476  break;
477  case HASH_SHA1:
478  A_SHAInit(&shaContext);
479  break;
480  case HASH_CRC32:
481  crc = 0;
482  break;
483  }
484 
485  while (bytesRemaining)
486  {
487  status = NtReadFile(
488  fileHandle,
489  NULL,
490  NULL,
491  NULL,
492  &isb,
493  buffer,
494  sizeof(buffer),
495  NULL,
496  NULL
497  );
498 
499  if (!NT_SUCCESS(status))
500  break;
501 
502  switch (mode)
503  {
504  case HASH_MD5:
505  MD5Update(&md5Context, buffer, (ULONG)isb.Information);
506  break;
507  case HASH_SHA1:
508  A_SHAUpdate(&shaContext, buffer, (ULONG)isb.Information);
509  break;
510  case HASH_CRC32:
511  crc = PhCrc32(crc, buffer, isb.Information);
512  break;
513  }
514 
515  bytesRemaining -= (ULONG)isb.Information;
516  }
517 
518  if (status == STATUS_END_OF_FILE)
519  status = STATUS_SUCCESS;
520 
521  switch (mode)
522  {
523  case HASH_MD5:
524  {
525  MD5Final(&md5Context);
526  wprintf(L"%s", PhBufferToHexString(md5Context.digest, 16)->Buffer);
527  }
528  break;
529  case HASH_SHA1:
530  {
531  UCHAR hash[20];
532 
533  A_SHAFinal(&shaContext, hash);
534  wprintf(L"%s", PhBufferToHexString(hash, 20)->Buffer);
535  }
536  break;
537  case HASH_CRC32:
538  {
539  wprintf(L"%08x", crc);
540  }
541  break;
542  }
543 
544  if (!NT_SUCCESS(status))
545  wprintf(L"Warning: I/O error encountered: %s\n", PhGetNtMessage(status)->Buffer);
546  }
547 
548  NtClose(fileHandle);
549  }
550 
551  if (!NT_SUCCESS(status))
552  {
553  wprintf(L"Error: %s\n", PhGetNtMessage(status)->Buffer);
554  return 1;
555  }
556  }
557  else if (PhEqualString2(FiArgAction, L"execute", TRUE))
558  {
559  if (FiArgNative)
560  {
561  if (!NT_SUCCESS(status = PhCreateProcess(
562  FiFormatFileName(FiArgFileName)->Buffer,
563  FiArgOutput ? &FiArgOutput->sr : NULL,
564  NULL,
565  NULL,
566  NULL,
567  0,
568  NULL,
569  NULL,
570  NULL,
571  NULL
572  )))
573  {
574  wprintf(L"Error: %s\n", PhGetNtMessage(status)->Buffer);
575  return 1;
576  }
577  }
578  else
579  {
580  if (!NT_SUCCESS(status = PhCreateProcessWin32(
581  FiArgFileName->Buffer,
582  PhGetString(FiArgOutput),
583  NULL,
584  NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath.Buffer,
586  NULL,
587  NULL,
588  NULL
589  )))
590  {
591  wprintf(L"Error: %s\n", PhGetNtMessage(status)->Buffer);
592  return 1;
593  }
594  }
595  }
596  else if (PhEqualString2(FiArgAction, L"del", TRUE))
597  {
598  HANDLE fileHandle;
599 
600  if (FiCreateFile(
601  &fileHandle,
602  DELETE | SYNCHRONIZE,
603  FiArgFileName,
604  0,
605  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
606  FILE_OPEN,
608  ))
609  {
610  FILE_DISPOSITION_INFORMATION dispositionInfo;
611  IO_STATUS_BLOCK isb;
612 
613  dispositionInfo.DeleteFile = TRUE;
614  if (!NT_SUCCESS(status = NtSetInformationFile(fileHandle, &isb, &dispositionInfo,
616  {
617  wprintf(L"Error deleting file: %s\n", PhGetNtMessage(status)->Buffer);
618  }
619 
620  NtClose(fileHandle);
621  }
622  }
623  else if (PhEqualString2(FiArgAction, L"touch", TRUE))
624  {
625  HANDLE fileHandle;
626 
627  if (FiCreateFile(
628  &fileHandle,
629  FILE_READ_ATTRIBUTES | SYNCHRONIZE,
630  FiArgFileName,
631  0,
632  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
633  FILE_OPEN_IF,
635  ))
636  {
637  NtClose(fileHandle);
638  }
639  }
640  else if (PhEqualString2(FiArgAction, L"mkdir", TRUE))
641  {
642  HANDLE fileHandle;
643 
644  if (FiCreateFile(
645  &fileHandle,
646  FILE_READ_ATTRIBUTES | SYNCHRONIZE,
647  FiArgFileName,
648  FILE_ATTRIBUTE_DIRECTORY,
649  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
650  FILE_CREATE,
652  ))
653  {
654  NtClose(fileHandle);
655  }
656  }
657  else if (PhEqualString2(FiArgAction, L"rename", TRUE))
658  {
659  HANDLE fileHandle;
660  PPH_STRING newFileName;
661 
662  if (!FiArgOutput)
663  {
664  wprintf(L"Error: new file name missing.\n");
665  FiPrintHelp();
666  return 1;
667  }
668 
669  newFileName = FiFormatFileName(FiArgOutput);
670 
671  if (FiCreateFile(
672  &fileHandle,
673  DELETE | SYNCHRONIZE,
674  FiArgFileName,
675  0,
676  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
677  FILE_OPEN,
679  ))
680  {
681  PFILE_RENAME_INFORMATION renameInfo;
682  ULONG renameInfoSize;
683  IO_STATUS_BLOCK isb;
684 
685  renameInfoSize = FIELD_OFFSET(FILE_RENAME_INFORMATION, FileName) + (ULONG)newFileName->Length;
686  renameInfo = PhAllocate(renameInfoSize);
687  renameInfo->ReplaceIfExists = FiArgForce;
688  renameInfo->RootDirectory = NULL;
689  renameInfo->FileNameLength = (ULONG)newFileName->Length;
690  memcpy(renameInfo->FileName, newFileName->Buffer, newFileName->Length);
691 
692  status = NtSetInformationFile(fileHandle, &isb, renameInfo, renameInfoSize, FileRenameInformation);
693  PhFree(renameInfo);
694 
695  if (!NT_SUCCESS(status))
696  {
697  wprintf(L"Error renaming file: %s\n", PhGetNtMessage(status)->Buffer);
698  }
699 
700  NtClose(fileHandle);
701  }
702  }
703  else if (PhEqualString2(FiArgAction, L"copy", TRUE))
704  {
705  HANDLE fileHandle;
706  HANDLE outFileHandle;
707  LARGE_INTEGER fileSize;
708  FILE_BASIC_INFORMATION basicInfo;
709 
710  if (!FiArgOutput)
711  {
712  wprintf(L"Error: output file name missing.\n");
713  FiPrintHelp();
714  return 1;
715  }
716 
717  if (FiCreateFile(
718  &fileHandle,
719  FILE_READ_ATTRIBUTES | FILE_READ_DATA | SYNCHRONIZE,
720  FiArgFileName,
721  0,
722  FILE_SHARE_READ | FILE_SHARE_DELETE,
723  FILE_OPEN,
725  ) && FiCreateFile(
726  &outFileHandle,
727  FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | SYNCHRONIZE,
728  FiArgOutput,
729  0,
730  FILE_SHARE_READ | FILE_SHARE_DELETE,
733  ))
734  {
735 #define COPY_BUFFER_SIZE 0x10000
736  IO_STATUS_BLOCK isb;
737  PVOID buffer;
738  ULONG64 bytesToCopy = FiArgLength;
739 
740  if (NT_SUCCESS(PhGetFileSize(fileHandle, &fileSize)))
741  {
742  PhSetFileSize(outFileHandle, &fileSize);
743  }
744 
745  buffer = PhAllocatePage(COPY_BUFFER_SIZE, NULL);
746 
747  if (!buffer)
748  {
749  wprintf(L"Error allocating buffer.\n");
750  return 1;
751  }
752 
753  while (bytesToCopy)
754  {
755  status = NtReadFile(
756  fileHandle,
757  NULL,
758  NULL,
759  NULL,
760  &isb,
761  buffer,
762  bytesToCopy >= COPY_BUFFER_SIZE ? COPY_BUFFER_SIZE : (ULONG)bytesToCopy,
763  NULL,
764  NULL
765  );
766 
767  if (status == STATUS_END_OF_FILE)
768  {
769  break;
770  }
771  else if (!NT_SUCCESS(status))
772  {
773  wprintf(L"Error reading from file: %s\n", PhGetNtMessage(status)->Buffer);
774  break;
775  }
776 
777  status = NtWriteFile(
778  outFileHandle,
779  NULL,
780  NULL,
781  NULL,
782  &isb,
783  buffer,
784  (ULONG)isb.Information, // number of bytes read
785  NULL,
786  NULL
787  );
788 
789  if (!NT_SUCCESS(status))
790  {
791  wprintf(L"Error writing to output file: %s\n", PhGetNtMessage(status)->Buffer);
792  break;
793  }
794 
795  bytesToCopy -= (ULONG)isb.Information;
796  }
797 
798  PhFreePage(buffer);
799 
800  // Copy basic attributes over.
802  fileHandle,
803  &isb,
804  &basicInfo,
805  sizeof(FILE_BASIC_INFORMATION),
807  )))
808  {
810  outFileHandle,
811  &isb,
812  &basicInfo,
813  sizeof(FILE_BASIC_INFORMATION),
815  );
816  }
817 
818  NtClose(fileHandle);
819  NtClose(outFileHandle);
820  }
821  }
822  else if (PhEqualString2(FiArgAction, L"dir", TRUE))
823  {
824  HANDLE fileHandle;
825  UNICODE_STRING pattern;
826  PPH_STRING totalSize, totalAllocSize;
827 
828  if (FiCreateFile(
829  &fileHandle,
830  FILE_LIST_DIRECTORY | SYNCHRONIZE,
831  FiArgFileName,
832  0,
833  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
834  FILE_OPEN,
836  ))
837  {
838  FipDirFileCount = 0;
839  FipDirDirCount = 0;
840  FipDirTotalSize = 0;
842 
843  if (FiArgPattern)
844  PhStringRefToUnicodeString(&FiArgPattern->sr, &pattern);
845 
847  fileHandle,
848  FiArgPattern ? &pattern : NULL,
850  NULL
851  );
852  NtClose(fileHandle);
853 
854  totalSize = PhFormatUInt64(FipDirTotalSize, TRUE);
855  totalAllocSize = PhFormatUInt64(FipDirTotalAllocSize, TRUE);
856 
857  wprintf(
858  L"%12I64u file(s) %11s bytes\n"
859  L"%12I64u dir(s) %11s bytes allocated\n",
861  totalSize->Buffer,
863  totalAllocSize->Buffer
864  );
865 
866  PhDereferenceObject(totalSize);
867  PhDereferenceObject(totalAllocSize);
868  }
869  }
870  else if (PhEqualString2(FiArgAction, L"streams", TRUE))
871  {
872  HANDLE fileHandle;
873  PVOID streams;
875 
876  if (FiCreateFile(
877  &fileHandle,
878  FILE_READ_ATTRIBUTES | SYNCHRONIZE,
879  FiArgFileName,
880  0,
881  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
882  FILE_OPEN,
884  ))
885  {
886  if (NT_SUCCESS(PhEnumFileStreams(fileHandle, &streams)))
887  {
888  stream = PH_FIRST_STREAM(streams);
889 
890  while (stream)
891  {
892  PPH_STRING size, allocationSize;
893 
894  size = PhFormatUInt64(stream->StreamSize.QuadPart, TRUE);
895  allocationSize = PhFormatUInt64(stream->StreamAllocationSize.QuadPart, TRUE);
896 
897  wprintf(
898  L"%11s %11s %.*s\n",
899  size->Buffer,
900  allocationSize->Buffer,
901  stream->StreamNameLength / 2,
902  stream->StreamName
903  );
904 
905  PhDereferenceObject(size);
906  PhDereferenceObject(allocationSize);
907 
908  stream = PH_NEXT_STREAM(stream);
909  }
910  }
911 
912  NtClose(fileHandle);
913  }
914  }
915  else
916  {
917  wprintf(L"Error: invalid action \"%s\".\n", FiArgAction->Buffer);
918  FiPrintHelp();
919  return 1;
920  }
921 }