Reactos

[SETUPLIB] SetupCreateDirectory(): Don't assume the form of the directory prefix (#7257)

Addendum to commit 32e6eed760 (r63715)
CORE-5982

The function assumed that the directory path name to be created
always starts with a harddisk-partition root device name of the form:

\Device\HarddiskX\PartitionY\

Indeed, it can be (when using the volume manager) of the form:

\Device\HarddiskVolumeN\

and could even have a different format if trying to install ReactOS
on an external removable drive or other weird device.

Since the format of this prefix is not 100% always the same,
a different way to create the sub-directories is needed.
The nested-directory creation algorithm is changed as follows:

Suppose that the directory to be created is:

\Device\HarddiskVolume1\ReactOS\system32\drivers

The function first loops backwards each path component in order
to find the deepest existing sub-directory: it will try to verify
whether each of the following sub-directories exist, successively:

\Device\HarddiskVolume1\ReactOS\system32\drivers
\Device\HarddiskVolume1\ReactOS\system32\
\Device\HarddiskVolume1\ReactOS\
\Device\HarddiskVolume1\

(Notice the trailing path separators kept in this step.)
In principle, this root device FS directory must exist (since the
volume has been formatted previously). Once found, the function will
then create each of the sub-directories in turn:

\Device\HarddiskVolume1\ReactOS
\Device\HarddiskVolume1\ReactOS\system32
\Device\HarddiskVolume1\ReactOS\system32\drivers

----

An alternative to the fix could be to always specify the root device
name in a separate parameter, but this hasn't been pursued here so as
to not modify all the callers of this function.

+86 -73
+76 -69
base/setup/lib/utils/filesup.c
··· 27 27 static 28 28 NTSTATUS 29 29 SetupCreateSingleDirectory( 30 - IN PCWSTR DirectoryName) 30 + _In_ PCUNICODE_STRING DirectoryName) 31 31 { 32 + NTSTATUS Status; 33 + UNICODE_STRING PathName = *DirectoryName; 32 34 OBJECT_ATTRIBUTES ObjectAttributes; 33 35 IO_STATUS_BLOCK IoStatusBlock; 34 - UNICODE_STRING PathName; 35 36 HANDLE DirectoryHandle; 36 - NTSTATUS Status; 37 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) - 1] == L'\\') 38 + /* Remove the trailing separator if needed */ 39 + if (PathName.Length >= 2 * sizeof(WCHAR) && 40 + PathName.Buffer[PathName.Length / sizeof(WCHAR) - 1] == OBJ_NAME_PATH_SEPARATOR) 43 41 { 44 42 PathName.Length -= sizeof(WCHAR); 45 - PathName.Buffer[PathName.Length / sizeof(WCHAR)] = UNICODE_NULL; 46 43 } 47 44 48 45 InitializeObjectAttributes(&ObjectAttributes, 49 46 &PathName, 50 - OBJ_CASE_INSENSITIVE | OBJ_INHERIT, 47 + OBJ_CASE_INSENSITIVE, 51 48 NULL, 52 49 NULL); 53 50 ··· 63 60 NULL, 64 61 0); 65 62 if (NT_SUCCESS(Status)) 66 - { 67 63 NtClose(DirectoryHandle); 68 - } 69 - 70 - RtlFreeUnicodeString(&PathName); 71 64 72 65 return Status; 73 66 } 74 67 68 + /** 69 + * @brief 70 + * Create a new directory, specified by the given path. 71 + * Any intermediate non-existing directory is created as well. 72 + * 73 + * @param[in] PathName 74 + * The path of the directory to be created. 75 + * 76 + * @return An NTSTATUS code indicating success or failure. 77 + **/ 75 78 NTSTATUS 76 79 SetupCreateDirectory( 77 - IN PCWSTR PathName) 80 + _In_ PCWSTR PathName) 78 81 { 79 82 NTSTATUS Status = STATUS_SUCCESS; 80 - PWCHAR PathBuffer = NULL; 81 - PWCHAR Ptr, EndPtr; 82 - ULONG BackslashCount; 83 - ULONG Size; 84 - 85 - Size = (wcslen(PathName) + 1) * sizeof(WCHAR); 86 - PathBuffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Size); 87 - if (PathBuffer == NULL) 88 - return STATUS_INSUFFICIENT_RESOURCES; 89 - 90 - wcscpy(PathBuffer, PathName); 91 - EndPtr = PathBuffer + wcslen(PathName); 92 - 93 - Ptr = PathBuffer; 83 + UNICODE_STRING PathNameU; 84 + PCWSTR Buffer; 85 + PCWCH Ptr, End; 94 86 95 - /* Skip the '\Device\HarddiskX\PartitionY\ part */ 96 - BackslashCount = 0; 97 - while (Ptr < EndPtr && BackslashCount < 4) 98 - { 99 - if (*Ptr == L'\\') 100 - BackslashCount++; 101 - 102 - Ptr++; 103 - } 87 + RtlInitUnicodeString(&PathNameU, PathName); 88 + Buffer = PathNameU.Buffer; 89 + End = Buffer + (PathNameU.Length / sizeof(WCHAR)); 104 90 105 - while (Ptr < EndPtr) 91 + /* Find the deepest existing sub-directory: start from the 92 + * end and go back, verifying each sub-directory in turn */ 93 + for (Ptr = End; Ptr > Buffer;) 106 94 { 107 - if (*Ptr == L'\\') 108 - { 109 - *Ptr = 0; 95 + BOOLEAN bExists; 110 96 111 - DPRINT("PathBuffer: %S\n", PathBuffer); 112 - if (!DoesDirExist(NULL, PathBuffer)) 113 - { 114 - DPRINT("Create: %S\n", PathBuffer); 115 - Status = SetupCreateSingleDirectory(PathBuffer); 116 - if (!NT_SUCCESS(Status)) 117 - goto done; 118 - } 97 + /* If we are on a separator, truncate at the next character. 98 + * The trailing separator is kept for the existence check. */ 99 + if ((Ptr < End) && (*Ptr == OBJ_NAME_PATH_SEPARATOR)) 100 + PathNameU.Length = (ULONG_PTR)(Ptr+1) - (ULONG_PTR)Buffer; 119 101 120 - *Ptr = L'\\'; 121 - } 102 + /* Check if the sub-directory exists and stop 103 + * if so: this is the deepest existing one */ 104 + DPRINT("PathName: %wZ\n", &PathNameU); 105 + bExists = DoesPathExist_UStr(NULL, &PathNameU, TRUE); 106 + if (bExists) 107 + break; 122 108 123 - Ptr++; 109 + /* Skip back any consecutive path separators */ 110 + while ((Ptr > Buffer) && (*Ptr == OBJ_NAME_PATH_SEPARATOR)) 111 + --Ptr; 112 + /* Go to the beginning of the path component, stop at the separator */ 113 + while ((Ptr > Buffer) && (*Ptr != OBJ_NAME_PATH_SEPARATOR)) 114 + --Ptr; 124 115 } 125 116 126 - if (!DoesDirExist(NULL, PathBuffer)) 117 + /* Skip any consecutive path separators */ 118 + while ((Ptr < End) && (*Ptr == OBJ_NAME_PATH_SEPARATOR)) 119 + ++Ptr; 120 + 121 + /* Create all the remaining sub-directories */ 122 + for (; Ptr < End; ++Ptr) 127 123 { 128 - DPRINT("Create: %S\n", PathBuffer); 129 - Status = SetupCreateSingleDirectory(PathBuffer); 124 + /* Go to the end of the current path component, stop at 125 + * the separator or terminating NUL and truncate it */ 126 + while ((Ptr < End) && (*Ptr != OBJ_NAME_PATH_SEPARATOR)) 127 + ++Ptr; 128 + PathNameU.Length = (ULONG_PTR)Ptr - (ULONG_PTR)Buffer; 129 + 130 + DPRINT("Create: %wZ\n", &PathNameU); 131 + Status = SetupCreateSingleDirectory(&PathNameU); 130 132 if (!NT_SUCCESS(Status)) 131 - goto done; 133 + break; 132 134 } 133 135 134 - done: 135 136 DPRINT("Done.\n"); 136 - if (PathBuffer != NULL) 137 - RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer); 138 - 139 137 return Status; 140 138 } 141 139 ··· 694 692 } 695 693 696 694 BOOLEAN 697 - DoesPathExist( 698 - IN HANDLE RootDirectory OPTIONAL, 699 - IN PCWSTR PathName, 700 - IN BOOLEAN IsDirectory) 695 + DoesPathExist_UStr( 696 + _In_opt_ HANDLE RootDirectory, 697 + _In_ PCUNICODE_STRING PathName, 698 + _In_ BOOLEAN IsDirectory) 701 699 { 702 700 NTSTATUS Status; 703 - UNICODE_STRING Name; 704 701 HANDLE FileHandle; 705 702 OBJECT_ATTRIBUTES ObjectAttributes; 706 703 IO_STATUS_BLOCK IoStatusBlock; 707 704 708 - RtlInitUnicodeString(&Name, PathName); 709 705 InitializeObjectAttributes(&ObjectAttributes, 710 - &Name, 706 + (PUNICODE_STRING)PathName, 711 707 OBJ_CASE_INSENSITIVE, 712 708 RootDirectory, 713 709 NULL); ··· 729 725 { 730 726 DPRINT("Failed to open %s '%wZ', Status 0x%08lx\n", 731 727 IsDirectory ? "directory" : "file", 732 - &Name, Status); 728 + PathName, Status); 733 729 } 734 730 735 731 return NT_SUCCESS(Status); 732 + } 733 + 734 + BOOLEAN 735 + DoesPathExist( 736 + _In_opt_ HANDLE RootDirectory, 737 + _In_ PCWSTR PathName, 738 + _In_ BOOLEAN IsDirectory) 739 + { 740 + UNICODE_STRING PathNameU; 741 + RtlInitUnicodeString(&PathNameU, PathName); 742 + return DoesPathExist_UStr(RootDirectory, &PathNameU, IsDirectory); 736 743 } 737 744 738 745 // FIXME: DEPRECATED! HACKish function that needs to be deprecated!
+10 -4
base/setup/lib/utils/filesup.h
··· 10 10 11 11 NTSTATUS 12 12 SetupCreateDirectory( 13 - IN PCWSTR DirectoryName); 13 + _In_ PCWSTR PathName); 14 14 15 15 NTSTATUS 16 16 SetupDeleteFile( ··· 66 66 IN /* PCWSTR */ ...); 67 67 68 68 BOOLEAN 69 + DoesPathExist_UStr( 70 + _In_opt_ HANDLE RootDirectory, 71 + _In_ PCUNICODE_STRING PathName, 72 + _In_ BOOLEAN IsDirectory); 73 + 74 + BOOLEAN 69 75 DoesPathExist( 70 - IN HANDLE RootDirectory OPTIONAL, 71 - IN PCWSTR PathName, 72 - IN BOOLEAN IsDirectory); 76 + _In_opt_ HANDLE RootDirectory, 77 + _In_ PCWSTR PathName, 78 + _In_ BOOLEAN IsDirectory); 73 79 74 80 #define DoesDirExist(RootDirectory, DirName) \ 75 81 DoesPathExist((RootDirectory), (DirName), TRUE)