xref: /reactos/base/setup/lib/utils/filesup.c (revision 40462c92)
1 /*
2  * PROJECT:     ReactOS Setup Library
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     File support functions.
5  * COPYRIGHT:   Casper S. Hornstrup (chorns@users.sourceforge.net)
6  *              Copyright 2017-2018 Hermes Belusca-Maito
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include "precomp.h"
12 #include "filesup.h"
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 
18 // ACHTUNG! HAXX FIXME!!
19 #define _SEH2_TRY
20 #define _SEH2_LEAVE     goto __SEH2_FINALLY__label;
21 #define _SEH2_FINALLY   __SEH2_FINALLY__label:
22 #define _SEH2_END
23 
24 
25 /* FUNCTIONS ****************************************************************/
26 
27 static
28 NTSTATUS
29 SetupCreateSingleDirectory(
30     IN PCWSTR DirectoryName)
31 {
32     OBJECT_ATTRIBUTES ObjectAttributes;
33     IO_STATUS_BLOCK IoStatusBlock;
34     UNICODE_STRING PathName;
35     HANDLE DirectoryHandle;
36     NTSTATUS Status;
37 
38     if (!RtlCreateUnicodeString(&PathName, DirectoryName))
39         return STATUS_NO_MEMORY;
40 
41     if (PathName.Length > sizeof(WCHAR) &&
42         PathName.Buffer[PathName.Length / sizeof(WCHAR) - 2] == L'\\' &&
43         PathName.Buffer[PathName.Length / sizeof(WCHAR) - 1] == L'.')
44     {
45         PathName.Length -= sizeof(WCHAR);
46         PathName.Buffer[PathName.Length / sizeof(WCHAR)] = UNICODE_NULL;
47     }
48 
49     if (PathName.Length > sizeof(WCHAR) &&
50         PathName.Buffer[PathName.Length / sizeof(WCHAR) - 1] == L'\\')
51     {
52         PathName.Length -= sizeof(WCHAR);
53         PathName.Buffer[PathName.Length / sizeof(WCHAR)] = UNICODE_NULL;
54     }
55 
56     InitializeObjectAttributes(&ObjectAttributes,
57                                &PathName,
58                                OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
59                                NULL,
60                                NULL);
61 
62     Status = NtCreateFile(&DirectoryHandle,
63                           FILE_LIST_DIRECTORY | SYNCHRONIZE,
64                           &ObjectAttributes,
65                           &IoStatusBlock,
66                           NULL,
67                           FILE_ATTRIBUTE_DIRECTORY,
68                           FILE_SHARE_READ | FILE_SHARE_WRITE,
69                           FILE_OPEN_IF,
70                           FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE,
71                           NULL,
72                           0);
73     if (NT_SUCCESS(Status))
74     {
75         NtClose(DirectoryHandle);
76     }
77 
78     RtlFreeUnicodeString(&PathName);
79 
80     return Status;
81 }
82 
83 NTSTATUS
84 SetupCreateDirectory(
85     IN PCWSTR PathName)
86 {
87     NTSTATUS Status = STATUS_SUCCESS;
88     PWCHAR PathBuffer = NULL;
89     PWCHAR Ptr, EndPtr;
90     ULONG BackslashCount;
91     ULONG Size;
92 
93     Size = (wcslen(PathName) + 1) * sizeof(WCHAR);
94     PathBuffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Size);
95     if (PathBuffer == NULL)
96         return STATUS_INSUFFICIENT_RESOURCES;
97 
98     wcscpy(PathBuffer, PathName);
99     EndPtr = PathBuffer + wcslen(PathName);
100 
101     Ptr = PathBuffer;
102 
103     /* Skip the '\Device\HarddiskX\PartitionY\ part */
104     BackslashCount = 0;
105     while (Ptr < EndPtr && BackslashCount < 4)
106     {
107         if (*Ptr == L'\\')
108             BackslashCount++;
109 
110         Ptr++;
111     }
112 
113     while (Ptr < EndPtr)
114     {
115         if (*Ptr == L'\\')
116         {
117             *Ptr = 0;
118 
119             DPRINT("PathBuffer: %S\n", PathBuffer);
120             if (!DoesDirExist(NULL, PathBuffer))
121             {
122                 DPRINT("Create: %S\n", PathBuffer);
123                 Status = SetupCreateSingleDirectory(PathBuffer);
124                 if (!NT_SUCCESS(Status))
125                     goto done;
126             }
127 
128             *Ptr = L'\\';
129         }
130 
131         Ptr++;
132     }
133 
134     if (!DoesDirExist(NULL, PathBuffer))
135     {
136         DPRINT("Create: %S\n", PathBuffer);
137         Status = SetupCreateSingleDirectory(PathBuffer);
138         if (!NT_SUCCESS(Status))
139             goto done;
140     }
141 
142 done:
143     DPRINT("Done.\n");
144     if (PathBuffer != NULL)
145         RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
146 
147     return Status;
148 }
149 
150 NTSTATUS
151 SetupDeleteFile(
152     IN PCWSTR FileName,
153     IN BOOLEAN ForceDelete) // ForceDelete can be used to delete read-only files
154 {
155     NTSTATUS Status;
156     UNICODE_STRING NtPathU;
157     OBJECT_ATTRIBUTES ObjectAttributes;
158     IO_STATUS_BLOCK IoStatusBlock;
159     HANDLE FileHandle;
160     FILE_DISPOSITION_INFORMATION FileDispInfo;
161     BOOLEAN RetryOnce = FALSE;
162 
163     /* Open the directory name that was passed in */
164     RtlInitUnicodeString(&NtPathU, FileName);
165     InitializeObjectAttributes(&ObjectAttributes,
166                                &NtPathU,
167                                OBJ_CASE_INSENSITIVE,
168                                NULL,
169                                NULL);
170 
171 Retry: // We go back there once if RetryOnce == TRUE
172     Status = NtOpenFile(&FileHandle,
173                         DELETE | FILE_READ_ATTRIBUTES |
174                         (RetryOnce ? FILE_WRITE_ATTRIBUTES : 0),
175                         &ObjectAttributes,
176                         &IoStatusBlock,
177                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
178                         FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT);
179     if (!NT_SUCCESS(Status))
180     {
181         DPRINT1("NtOpenFile failed with Status 0x%08lx\n", Status);
182         return Status;
183     }
184 
185     if (RetryOnce)
186     {
187         FILE_BASIC_INFORMATION FileInformation;
188 
189         Status = NtQueryInformationFile(FileHandle,
190                                         &IoStatusBlock,
191                                         &FileInformation,
192                                         sizeof(FILE_BASIC_INFORMATION),
193                                         FileBasicInformation);
194         if (!NT_SUCCESS(Status))
195         {
196             DPRINT1("NtQueryInformationFile failed with Status 0x%08lx\n", Status);
197             NtClose(FileHandle);
198             return Status;
199         }
200 
201         FileInformation.FileAttributes = FILE_ATTRIBUTE_NORMAL;
202         Status = NtSetInformationFile(FileHandle,
203                                       &IoStatusBlock,
204                                       &FileInformation,
205                                       sizeof(FILE_BASIC_INFORMATION),
206                                       FileBasicInformation);
207         NtClose(FileHandle);
208         if (!NT_SUCCESS(Status))
209         {
210             DPRINT1("NtSetInformationFile failed with Status 0x%08lx\n", Status);
211             return Status;
212         }
213     }
214 
215     /* Ask for the file to be deleted */
216     FileDispInfo.DeleteFile = TRUE;
217     Status = NtSetInformationFile(FileHandle,
218                                   &IoStatusBlock,
219                                   &FileDispInfo,
220                                   sizeof(FILE_DISPOSITION_INFORMATION),
221                                   FileDispositionInformation);
222     NtClose(FileHandle);
223 
224     if (!NT_SUCCESS(Status))
225         DPRINT1("Deletion of file '%S' failed, Status 0x%08lx\n", FileName, Status);
226 
227     // FIXME: Check the precise value of Status!
228     if (!NT_SUCCESS(Status) && ForceDelete && !RetryOnce)
229     {
230         /* Retry once */
231         RetryOnce = TRUE;
232         goto Retry;
233     }
234 
235     /* Return result to the caller */
236     return Status;
237 }
238 
239 NTSTATUS
240 SetupCopyFile(
241     IN PCWSTR SourceFileName,
242     IN PCWSTR DestinationFileName,
243     IN BOOLEAN FailIfExists)
244 {
245     NTSTATUS Status;
246     UNICODE_STRING FileName;
247     OBJECT_ATTRIBUTES ObjectAttributes;
248     HANDLE FileHandleSource;
249     HANDLE FileHandleDest;
250     IO_STATUS_BLOCK IoStatusBlock;
251     FILE_STANDARD_INFORMATION FileStandard;
252     FILE_BASIC_INFORMATION FileBasic;
253     ULONG RegionSize;
254     HANDLE SourceFileSection;
255     PVOID SourceFileMap = NULL;
256     SIZE_T SourceSectionSize = 0;
257     LARGE_INTEGER ByteOffset;
258 
259     RtlInitUnicodeString(&FileName, SourceFileName);
260     InitializeObjectAttributes(&ObjectAttributes,
261                                &FileName,
262                                OBJ_CASE_INSENSITIVE,
263                                NULL,
264                                NULL);
265 
266     Status = NtOpenFile(&FileHandleSource,
267                         GENERIC_READ,
268                         &ObjectAttributes,
269                         &IoStatusBlock,
270                         FILE_SHARE_READ,
271                         FILE_SEQUENTIAL_ONLY);
272     if (!NT_SUCCESS(Status))
273     {
274         DPRINT1("NtOpenFile failed: %x, %wZ\n", Status, &FileName);
275         goto done;
276     }
277 
278     Status = NtQueryInformationFile(FileHandleSource,
279                                     &IoStatusBlock,
280                                     &FileStandard,
281                                     sizeof(FILE_STANDARD_INFORMATION),
282                                     FileStandardInformation);
283     if (!NT_SUCCESS(Status))
284     {
285         DPRINT1("NtQueryInformationFile failed: %x\n", Status);
286         goto closesrc;
287     }
288 
289     Status = NtQueryInformationFile(FileHandleSource,
290                                     &IoStatusBlock,
291                                     &FileBasic,
292                                     sizeof(FILE_BASIC_INFORMATION),
293                                     FileBasicInformation);
294     if (!NT_SUCCESS(Status))
295     {
296         DPRINT1("NtQueryInformationFile failed: %x\n", Status);
297         goto closesrc;
298     }
299 
300     Status = NtCreateSection(&SourceFileSection,
301                              SECTION_MAP_READ,
302                              NULL,
303                              NULL,
304                              PAGE_READONLY,
305                              SEC_COMMIT,
306                              FileHandleSource);
307     if (!NT_SUCCESS(Status))
308     {
309         DPRINT1("NtCreateSection failed: %x, %S\n", Status, SourceFileName);
310         goto closesrc;
311     }
312 
313     Status = NtMapViewOfSection(SourceFileSection,
314                                 NtCurrentProcess(),
315                                 &SourceFileMap,
316                                 0,
317                                 0,
318                                 NULL,
319                                 &SourceSectionSize,
320                                 ViewUnmap,
321                                 0,
322                                 PAGE_READONLY);
323     if (!NT_SUCCESS(Status))
324     {
325         DPRINT1("NtMapViewOfSection failed: %x, %S\n", Status, SourceFileName);
326         goto closesrcsec;
327     }
328 
329     RtlInitUnicodeString(&FileName, DestinationFileName);
330     InitializeObjectAttributes(&ObjectAttributes,
331                                &FileName,
332                                OBJ_CASE_INSENSITIVE,
333                                NULL,
334                                NULL);
335 
336     Status = NtCreateFile(&FileHandleDest,
337                           GENERIC_WRITE | SYNCHRONIZE,
338                           &ObjectAttributes,
339                           &IoStatusBlock,
340                           NULL,
341                           FileBasic.FileAttributes, // FILE_ATTRIBUTE_NORMAL,
342                           0,
343                           FailIfExists ? FILE_CREATE : FILE_OVERWRITE_IF,
344                           FILE_NO_INTERMEDIATE_BUFFERING |
345                           FILE_SEQUENTIAL_ONLY |
346                           FILE_SYNCHRONOUS_IO_NONALERT,
347                           NULL,
348                           0);
349     if (!NT_SUCCESS(Status))
350     {
351         /*
352          * Open may have failed because the file to overwrite
353          * is in readonly mode.
354          */
355         if (Status == STATUS_ACCESS_DENIED)
356         {
357             FILE_BASIC_INFORMATION FileBasicInfo;
358 
359             /* Reattempt to open it with limited access */
360             Status = NtCreateFile(&FileHandleDest,
361                                   FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
362                                   &ObjectAttributes,
363                                   &IoStatusBlock,
364                                   NULL,
365                                   FILE_ATTRIBUTE_NORMAL,
366                                   0,
367                                   FILE_OPEN,
368                                   FILE_NO_INTERMEDIATE_BUFFERING |
369                                   FILE_SEQUENTIAL_ONLY |
370                                   FILE_SYNCHRONOUS_IO_NONALERT,
371                                   NULL,
372                                   0);
373             /* Fail for real if we cannot open it that way */
374             if (!NT_SUCCESS(Status))
375             {
376                 DPRINT1("NtCreateFile failed: %x, %wZ\n", Status, &FileName);
377                 goto unmapsrcsec;
378             }
379 
380             /* Zero our basic info, just to set attributes */
381             RtlZeroMemory(&FileBasicInfo, sizeof(FileBasicInfo));
382             /* Reset attributes to normal, no read-only */
383             FileBasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
384             /*
385              * We basically don't care about whether it succeed:
386              * if it didn't, later open will fail.
387              */
388             NtSetInformationFile(FileHandleDest, &IoStatusBlock, &FileBasicInfo,
389                                  sizeof(FileBasicInfo), FileBasicInformation);
390 
391             /* Close file */
392             NtClose(FileHandleDest);
393 
394             /* And re-attempt overwrite */
395             Status = NtCreateFile(&FileHandleDest,
396                                   GENERIC_WRITE | SYNCHRONIZE,
397                                   &ObjectAttributes,
398                                   &IoStatusBlock,
399                                   NULL,
400                                   FILE_ATTRIBUTE_NORMAL,
401                                   0,
402                                   FILE_OVERWRITE_IF,
403                                   FILE_NO_INTERMEDIATE_BUFFERING |
404                                   FILE_SEQUENTIAL_ONLY |
405                                   FILE_SYNCHRONOUS_IO_NONALERT,
406                                   NULL,
407                                   0);
408         }
409 
410         /* We failed */
411         if (!NT_SUCCESS(Status))
412         {
413             DPRINT1("NtCreateFile failed: %x, %wZ\n", Status, &FileName);
414             goto unmapsrcsec;
415         }
416     }
417 
418     RegionSize = (ULONG)PAGE_ROUND_UP(FileStandard.EndOfFile.u.LowPart);
419     IoStatusBlock.Status = 0;
420     ByteOffset.QuadPart = 0ULL;
421     Status = NtWriteFile(FileHandleDest,
422                          NULL,
423                          NULL,
424                          NULL,
425                          &IoStatusBlock,
426                          SourceFileMap,
427                          RegionSize,
428                          &ByteOffset,
429                          NULL);
430     if (!NT_SUCCESS(Status))
431     {
432         DPRINT1("NtWriteFile failed: %x:%x, iosb: %p src: %p, size: %x\n",
433                 Status, IoStatusBlock.Status, &IoStatusBlock, SourceFileMap, RegionSize);
434         goto closedest;
435     }
436 
437     /* Copy file date/time from source file */
438     Status = NtSetInformationFile(FileHandleDest,
439                                   &IoStatusBlock,
440                                   &FileBasic,
441                                   sizeof(FILE_BASIC_INFORMATION),
442                                   FileBasicInformation);
443     if (!NT_SUCCESS(Status))
444     {
445         DPRINT1("NtSetInformationFile failed: %x\n", Status);
446         goto closedest;
447     }
448 
449     /* Shorten the file back to its real size after completing the write */
450     Status = NtSetInformationFile(FileHandleDest,
451                                   &IoStatusBlock,
452                                   &FileStandard.EndOfFile,
453                                   sizeof(FILE_END_OF_FILE_INFORMATION),
454                                   FileEndOfFileInformation);
455     if (!NT_SUCCESS(Status))
456     {
457         DPRINT1("NtSetInformationFile failed: %x\n", Status);
458     }
459 
460 closedest:
461     NtClose(FileHandleDest);
462 
463 unmapsrcsec:
464     NtUnmapViewOfSection(NtCurrentProcess(), SourceFileMap);
465 
466 closesrcsec:
467     NtClose(SourceFileSection);
468 
469 closesrc:
470     NtClose(FileHandleSource);
471 
472 done:
473     return Status;
474 }
475 
476 /*
477  * Synchronized with its kernel32 counterpart, but we don't manage reparse points here.
478  */
479 NTSTATUS
480 SetupMoveFile(
481     IN PCWSTR ExistingFileName,
482     IN PCWSTR NewFileName,
483     IN ULONG Flags)
484 {
485     NTSTATUS Status;
486     IO_STATUS_BLOCK IoStatusBlock;
487     OBJECT_ATTRIBUTES ObjectAttributes;
488     PFILE_RENAME_INFORMATION RenameInfo;
489     UNICODE_STRING NewPathU, ExistingPathU;
490     HANDLE SourceHandle = NULL;
491     BOOLEAN ReplaceIfExists;
492 
493     RtlInitUnicodeString(&ExistingPathU, ExistingFileName);
494     RtlInitUnicodeString(&NewPathU, NewFileName);
495 
496     _SEH2_TRY
497     {
498         ReplaceIfExists = !!(Flags & MOVEFILE_REPLACE_EXISTING);
499 
500         /* Unless we manage a proper opening, we'll attempt to reopen without reparse support */
501         InitializeObjectAttributes(&ObjectAttributes,
502                                    &ExistingPathU,
503                                    OBJ_CASE_INSENSITIVE,
504                                    NULL,
505                                    NULL);
506         /* Attempt to open source file */
507         Status = NtOpenFile(&SourceHandle,
508                             FILE_READ_ATTRIBUTES | DELETE | SYNCHRONIZE,
509                             &ObjectAttributes,
510                             &IoStatusBlock,
511                             FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
512                             FILE_OPEN_FOR_BACKUP_INTENT | ((Flags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0));
513         if (!NT_SUCCESS(Status))
514         {
515             if (Status != STATUS_INVALID_PARAMETER)
516             {
517                 _SEH2_LEAVE;
518             }
519         }
520 
521         /* At that point, we MUST have a source handle */
522         ASSERT(SourceHandle);
523 
524         /* Allocate renaming buffer and fill it */
525         RenameInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(FILE_RENAME_INFORMATION));
526         if (RenameInfo == NULL)
527         {
528             Status = STATUS_NO_MEMORY;
529             _SEH2_LEAVE;
530         }
531 
532         RtlCopyMemory(&RenameInfo->FileName, NewPathU.Buffer, NewPathU.Length);
533         RenameInfo->ReplaceIfExists = ReplaceIfExists;
534         RenameInfo->RootDirectory = NULL;
535         RenameInfo->FileNameLength = NewPathU.Length;
536 
537         /* Attempt to rename the file */
538         Status = NtSetInformationFile(SourceHandle,
539                                       &IoStatusBlock,
540                                       RenameInfo,
541                                       NewPathU.Length + sizeof(FILE_RENAME_INFORMATION),
542                                       FileRenameInformation);
543         RtlFreeHeap(RtlGetProcessHeap(), 0, RenameInfo);
544         if (NT_SUCCESS(Status))
545         {
546             /* If it succeeded, all fine, quit */
547             _SEH2_LEAVE;
548         }
549         /*
550          * If we failed for any other reason than not the same device, fail.
551          * If we failed because of different devices, only allow renaming
552          * if user allowed copy.
553          */
554         if (Status != STATUS_NOT_SAME_DEVICE || !(Flags & MOVEFILE_COPY_ALLOWED))
555         {
556             /* ReactOS hack! To be removed once all FSD have proper renaming support
557              * Just leave status to error and leave
558              */
559             if (Status == STATUS_NOT_IMPLEMENTED)
560             {
561                 DPRINT1("Forcing copy, renaming not supported by FSD\n");
562             }
563             else
564             {
565                 _SEH2_LEAVE;
566             }
567         }
568 
569         /* Close the source file */
570         NtClose(SourceHandle);
571         SourceHandle = NULL;
572 
573         /* Perform the file copy */
574         Status = SetupCopyFile(ExistingFileName,
575                                NewFileName,
576                                !ReplaceIfExists);
577 
578         /* If it succeeded, delete the source file */
579         if (NT_SUCCESS(Status))
580         {
581             /* Force-delete files even if read-only */
582             SetupDeleteFile(ExistingFileName, TRUE);
583         }
584     }
585     _SEH2_FINALLY
586     {
587         if (SourceHandle)
588             NtClose(SourceHandle);
589     }
590     _SEH2_END;
591 
592     return Status;
593 }
594 
595 NTSTATUS
596 ConcatPathsV(
597     IN OUT PWSTR PathBuffer,
598     IN SIZE_T cchPathSize,
599     IN ULONG NumberOfPathComponents,
600     IN va_list PathComponentsList)
601 {
602     NTSTATUS Status = STATUS_SUCCESS;
603     SIZE_T cchPathLen;
604     PCWSTR PathComponent;
605 
606     if (cchPathSize < 1)
607         return STATUS_SUCCESS;
608 
609     while (NumberOfPathComponents--)
610     {
611         PathComponent = va_arg(PathComponentsList, PCWSTR);
612         if (!PathComponent)
613             continue;
614 
615         cchPathLen = min(cchPathSize, wcslen(PathBuffer));
616         if (cchPathLen >= cchPathSize)
617             return STATUS_BUFFER_OVERFLOW;
618 
619         if (PathComponent[0] != OBJ_NAME_PATH_SEPARATOR &&
620             cchPathLen > 0 && PathBuffer[cchPathLen-1] != OBJ_NAME_PATH_SEPARATOR)
621         {
622             /* PathComponent does not start with '\' and PathBuffer does not end with '\' */
623             Status = RtlStringCchCatW(PathBuffer, cchPathSize, L"\\");
624             if (!NT_SUCCESS(Status))
625                 return Status;
626         }
627         else if (PathComponent[0] == OBJ_NAME_PATH_SEPARATOR &&
628                  cchPathLen > 0 && PathBuffer[cchPathLen-1] == OBJ_NAME_PATH_SEPARATOR)
629         {
630             /* PathComponent starts with '\' and PathBuffer ends with '\' */
631             while (*PathComponent == OBJ_NAME_PATH_SEPARATOR)
632                 ++PathComponent; // Skip any backslash
633         }
634         Status = RtlStringCchCatW(PathBuffer, cchPathSize, PathComponent);
635         if (!NT_SUCCESS(Status))
636             return Status;
637     }
638 
639     return Status;
640 }
641 
642 NTSTATUS
643 CombinePathsV(
644     OUT PWSTR PathBuffer,
645     IN SIZE_T cchPathSize,
646     IN ULONG NumberOfPathComponents,
647     IN va_list PathComponentsList)
648 {
649     if (cchPathSize < 1)
650         return STATUS_SUCCESS;
651 
652     *PathBuffer = UNICODE_NULL;
653     return ConcatPathsV(PathBuffer, cchPathSize,
654                         NumberOfPathComponents,
655                         PathComponentsList);
656 }
657 
658 NTSTATUS
659 ConcatPaths(
660     IN OUT PWSTR PathBuffer,
661     IN SIZE_T cchPathSize,
662     IN ULONG NumberOfPathComponents,
663     IN /* PCWSTR */ ...)
664 {
665     NTSTATUS Status;
666     va_list PathComponentsList;
667 
668     if (cchPathSize < 1)
669         return STATUS_SUCCESS;
670 
671     va_start(PathComponentsList, NumberOfPathComponents);
672     Status = ConcatPathsV(PathBuffer, cchPathSize,
673                           NumberOfPathComponents,
674                           PathComponentsList);
675     va_end(PathComponentsList);
676 
677     return Status;
678 }
679 
680 NTSTATUS
681 CombinePaths(
682     OUT PWSTR PathBuffer,
683     IN SIZE_T cchPathSize,
684     IN ULONG NumberOfPathComponents,
685     IN /* PCWSTR */ ...)
686 {
687     NTSTATUS Status;
688     va_list PathComponentsList;
689 
690     if (cchPathSize < 1)
691         return STATUS_SUCCESS;
692 
693     *PathBuffer = UNICODE_NULL;
694 
695     va_start(PathComponentsList, NumberOfPathComponents);
696     Status = CombinePathsV(PathBuffer, cchPathSize,
697                            NumberOfPathComponents,
698                            PathComponentsList);
699     va_end(PathComponentsList);
700 
701     return Status;
702 }
703 
704 BOOLEAN
705 DoesPathExist(
706     IN HANDLE RootDirectory OPTIONAL,
707     IN PCWSTR PathName,
708     IN BOOLEAN IsDirectory)
709 {
710     NTSTATUS Status;
711     UNICODE_STRING Name;
712     HANDLE FileHandle;
713     OBJECT_ATTRIBUTES ObjectAttributes;
714     IO_STATUS_BLOCK IoStatusBlock;
715 
716     RtlInitUnicodeString(&Name, PathName);
717     InitializeObjectAttributes(&ObjectAttributes,
718                                &Name,
719                                OBJ_CASE_INSENSITIVE,
720                                RootDirectory,
721                                NULL);
722 
723     Status = NtOpenFile(&FileHandle,
724                         IsDirectory ? (FILE_LIST_DIRECTORY | SYNCHRONIZE)
725                                     :  FILE_GENERIC_READ, // Contains SYNCHRONIZE
726                         &ObjectAttributes,
727                         &IoStatusBlock,
728                         FILE_SHARE_READ | FILE_SHARE_WRITE,
729                         FILE_SYNCHRONOUS_IO_NONALERT |
730                             (IsDirectory ? FILE_DIRECTORY_FILE
731                                          : FILE_NON_DIRECTORY_FILE));
732     if (NT_SUCCESS(Status))
733     {
734         NtClose(FileHandle);
735     }
736     else
737     {
738         DPRINT("Failed to open %s '%wZ', Status 0x%08lx\n",
739                IsDirectory ? "directory" : "file",
740                &Name, Status);
741     }
742 
743     return NT_SUCCESS(Status);
744 }
745 
746 // FIXME: DEPRECATED! HACKish function that needs to be deprecated!
747 BOOLEAN
748 DoesFileExist_2(
749     IN PCWSTR PathName OPTIONAL,
750     IN PCWSTR FileName)
751 {
752     WCHAR FullName[MAX_PATH];
753     CombinePaths(FullName, ARRAYSIZE(FullName), 2, PathName, FileName);
754     return DoesFileExist(NULL, FullName);
755 }
756 
757 /*
758  * The format of NtPath should be:
759  *    \Device\HarddiskXXX\PartitionYYY[\path] ,
760  * where XXX and YYY respectively represent the hard disk and partition numbers,
761  * and [\path] represent an optional path (separated by '\\').
762  *
763  * If a NT path of such a form is correctly parsed, the function returns respectively:
764  * - in pDiskNumber: the hard disk number XXX,
765  * - in pPartNumber: the partition number YYY,
766  * - in PathComponent: pointer value (inside NtPath) to the beginning of \path.
767  *
768  * NOTE: The function does not accept leading whitespace.
769  */
770 BOOLEAN
771 NtPathToDiskPartComponents(
772     IN PCWSTR NtPath,
773     OUT PULONG pDiskNumber,
774     OUT PULONG pPartNumber,
775     OUT PCWSTR* PathComponent OPTIONAL)
776 {
777     ULONG DiskNumber, PartNumber;
778     PCWSTR Path;
779 
780     *pDiskNumber = 0;
781     *pPartNumber = 0;
782     if (PathComponent) *PathComponent = NULL;
783 
784     Path = NtPath;
785 
786     if (_wcsnicmp(Path, L"\\Device\\Harddisk", 16) != 0)
787     {
788         /* The NT path doesn't start with the prefix string, thus it cannot be a hard disk device path */
789         DPRINT1("'%S' : Not a possible hard disk device.\n", NtPath);
790         return FALSE;
791     }
792 
793     Path += 16;
794 
795     /* A number must be present now */
796     if (!iswdigit(*Path))
797     {
798         DPRINT1("'%S' : expected a number! Not a regular hard disk device.\n", Path);
799         return FALSE;
800     }
801     DiskNumber = wcstoul(Path, (PWSTR*)&Path, 10);
802 
803     /* Either NULL termination, or a path separator must be present now */
804     if (*Path && *Path != OBJ_NAME_PATH_SEPARATOR)
805     {
806         DPRINT1("'%S' : expected a path separator!\n", Path);
807         return FALSE;
808     }
809 
810     if (!*Path)
811     {
812         DPRINT1("The path only specified a hard disk (and nothing else, like a partition...), so we stop there.\n");
813         goto Quit;
814     }
815 
816     /* Here, *Path == L'\\' */
817 
818     if (_wcsnicmp(Path, L"\\Partition", 10) != 0)
819     {
820         /* Actually, \Partition is optional so, if we don't have it, we still return success. Or should we? */
821         DPRINT1("'%S' : unexpected format!\n", NtPath);
822         goto Quit;
823     }
824 
825     Path += 10;
826 
827     /* A number must be present now */
828     if (!iswdigit(*Path))
829     {
830         /* If we don't have a number it means this part of path is actually not a partition specifier, so we shouldn't fail either. Or should we? */
831         DPRINT1("'%S' : expected a number!\n", Path);
832         goto Quit;
833     }
834     PartNumber = wcstoul(Path, (PWSTR*)&Path, 10);
835 
836     /* Either NULL termination, or a path separator must be present now */
837     if (*Path && *Path != OBJ_NAME_PATH_SEPARATOR)
838     {
839         /* We shouldn't fail here because it just means this part of path is actually not a partition specifier. Or should we? */
840         DPRINT1("'%S' : expected a path separator!\n", Path);
841         goto Quit;
842     }
843 
844     /* OK, here we really have a partition specifier: return its number */
845     *pPartNumber = PartNumber;
846 
847 Quit:
848     /* Return the disk number */
849     *pDiskNumber = DiskNumber;
850 
851     /* Return the path component also, if the user wants it */
852     if (PathComponent) *PathComponent = Path;
853 
854     return TRUE;
855 }
856 
857 NTSTATUS
858 OpenAndMapFile(
859     IN  HANDLE RootDirectory OPTIONAL,
860     IN  PCWSTR PathNameToFile,
861     OUT PHANDLE FileHandle,         // IN OUT PHANDLE OPTIONAL
862     OUT PHANDLE SectionHandle,
863     OUT PVOID* BaseAddress,
864     OUT PULONG FileSize OPTIONAL,
865     IN  BOOLEAN ReadWriteAccess)
866 {
867     NTSTATUS Status;
868     UNICODE_STRING FileName;
869     OBJECT_ATTRIBUTES ObjectAttributes;
870     IO_STATUS_BLOCK IoStatusBlock;
871     ULONG SectionPageProtection;
872     SIZE_T ViewSize;
873     PVOID ViewBase;
874 
875     /* Open the file */
876 
877     RtlInitUnicodeString(&FileName, PathNameToFile);
878     InitializeObjectAttributes(&ObjectAttributes,
879                                &FileName,
880                                OBJ_CASE_INSENSITIVE,
881                                RootDirectory,
882                                NULL);
883 
884     *FileHandle = NULL;
885     *SectionHandle = NULL;
886 
887     Status = NtOpenFile(FileHandle,
888                         FILE_GENERIC_READ | // Contains SYNCHRONIZE
889                             (ReadWriteAccess ? FILE_GENERIC_WRITE : 0),
890                         &ObjectAttributes,
891                         &IoStatusBlock,
892                         FILE_SHARE_READ,
893                         FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
894     if (!NT_SUCCESS(Status))
895     {
896         DPRINT1("Failed to open file '%wZ', Status 0x%08lx\n", &FileName, Status);
897         return Status;
898     }
899 
900     if (FileSize)
901     {
902         /* Query the file size */
903         FILE_STANDARD_INFORMATION FileInfo;
904         Status = NtQueryInformationFile(*FileHandle,
905                                         &IoStatusBlock,
906                                         &FileInfo,
907                                         sizeof(FileInfo),
908                                         FileStandardInformation);
909         if (!NT_SUCCESS(Status))
910         {
911             DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
912             NtClose(*FileHandle);
913             *FileHandle = NULL;
914             return Status;
915         }
916 
917         if (FileInfo.EndOfFile.HighPart != 0)
918             DPRINT1("WARNING!! The file '%wZ' is too large!\n", &FileName);
919 
920         *FileSize = FileInfo.EndOfFile.LowPart;
921 
922         DPRINT("File size: %lu\n", *FileSize);
923     }
924 
925     /* Map the file in memory */
926 
927     SectionPageProtection = (ReadWriteAccess ? PAGE_READWRITE : PAGE_READONLY);
928 
929     /* Create the section */
930     Status = NtCreateSection(SectionHandle,
931                              STANDARD_RIGHTS_REQUIRED | SECTION_QUERY |
932                              SECTION_MAP_READ |
933                                 (ReadWriteAccess ? SECTION_MAP_WRITE : 0),
934                              NULL,
935                              NULL,
936                              SectionPageProtection,
937                              SEC_COMMIT /* | SEC_IMAGE (_NO_EXECUTE) */,
938                              *FileHandle);
939     if (!NT_SUCCESS(Status))
940     {
941         DPRINT1("Failed to create a memory section for file '%wZ', Status 0x%08lx\n", &FileName, Status);
942         NtClose(*FileHandle);
943         *FileHandle = NULL;
944         return Status;
945     }
946 
947     /* Map the section */
948     ViewSize = 0;
949     ViewBase = NULL;
950     Status = NtMapViewOfSection(*SectionHandle,
951                                 NtCurrentProcess(),
952                                 &ViewBase,
953                                 0, 0,
954                                 NULL,
955                                 &ViewSize,
956                                 ViewShare,
957                                 0,
958                                 SectionPageProtection);
959     if (!NT_SUCCESS(Status))
960     {
961         DPRINT1("Failed to map a view for file '%wZ', Status 0x%08lx\n", &FileName, Status);
962         NtClose(*SectionHandle);
963         *SectionHandle = NULL;
964         NtClose(*FileHandle);
965         *FileHandle = NULL;
966         return Status;
967     }
968 
969     *BaseAddress = ViewBase;
970     return STATUS_SUCCESS;
971 }
972 
973 BOOLEAN
974 UnMapFile(
975     IN HANDLE SectionHandle,
976     IN PVOID BaseAddress)
977 {
978     NTSTATUS Status;
979     BOOLEAN Success = TRUE;
980 
981     Status = NtUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
982     if (!NT_SUCCESS(Status))
983     {
984         DPRINT1("UnMapFile: NtUnmapViewOfSection(0x%p) failed with Status 0x%08lx\n",
985                 BaseAddress, Status);
986         Success = FALSE;
987     }
988     Status = NtClose(SectionHandle);
989     if (!NT_SUCCESS(Status))
990     {
991         DPRINT1("UnMapFile: NtClose(0x%p) failed with Status 0x%08lx\n",
992                 SectionHandle, Status);
993         Success = FALSE;
994     }
995 
996     return Success;
997 }
998 
999 /* EOF */
1000