[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.
This commit is contained in:
Hermès Bélusca-Maïto 2024-08-07 22:31:01 +02:00
parent 00ddae49d5
commit ea5728b5f3
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
2 changed files with 87 additions and 74 deletions

View file

@ -27,27 +27,24 @@
static
NTSTATUS
SetupCreateSingleDirectory(
IN PCWSTR DirectoryName)
_In_ PCUNICODE_STRING DirectoryName)
{
NTSTATUS Status;
UNICODE_STRING PathName = *DirectoryName;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING PathName;
HANDLE DirectoryHandle;
NTSTATUS Status;
if (!RtlCreateUnicodeString(&PathName, DirectoryName))
return STATUS_NO_MEMORY;
if (PathName.Length > sizeof(WCHAR) &&
PathName.Buffer[PathName.Length / sizeof(WCHAR) - 1] == L'\\')
/* Remove the trailing separator if needed */
if (PathName.Length >= 2 * sizeof(WCHAR) &&
PathName.Buffer[PathName.Length / sizeof(WCHAR) - 1] == OBJ_NAME_PATH_SEPARATOR)
{
PathName.Length -= sizeof(WCHAR);
PathName.Buffer[PathName.Length / sizeof(WCHAR)] = UNICODE_NULL;
}
InitializeObjectAttributes(&ObjectAttributes,
&PathName,
OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
@ -63,79 +60,80 @@ SetupCreateSingleDirectory(
NULL,
0);
if (NT_SUCCESS(Status))
{
NtClose(DirectoryHandle);
}
RtlFreeUnicodeString(&PathName);
return Status;
}
/**
* @brief
* Create a new directory, specified by the given path.
* Any intermediate non-existing directory is created as well.
*
* @param[in] PathName
* The path of the directory to be created.
*
* @return An NTSTATUS code indicating success or failure.
**/
NTSTATUS
SetupCreateDirectory(
IN PCWSTR PathName)
_In_ PCWSTR PathName)
{
NTSTATUS Status = STATUS_SUCCESS;
PWCHAR PathBuffer = NULL;
PWCHAR Ptr, EndPtr;
ULONG BackslashCount;
ULONG Size;
UNICODE_STRING PathNameU;
PCWSTR Buffer;
PCWCH Ptr, End;
Size = (wcslen(PathName) + 1) * sizeof(WCHAR);
PathBuffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Size);
if (PathBuffer == NULL)
return STATUS_INSUFFICIENT_RESOURCES;
RtlInitUnicodeString(&PathNameU, PathName);
Buffer = PathNameU.Buffer;
End = Buffer + (PathNameU.Length / sizeof(WCHAR));
wcscpy(PathBuffer, PathName);
EndPtr = PathBuffer + wcslen(PathName);
Ptr = PathBuffer;
/* Skip the '\Device\HarddiskX\PartitionY\ part */
BackslashCount = 0;
while (Ptr < EndPtr && BackslashCount < 4)
/* Find the deepest existing sub-directory: start from the
* end and go back, verifying each sub-directory in turn */
for (Ptr = End; Ptr > Buffer;)
{
if (*Ptr == L'\\')
BackslashCount++;
BOOLEAN bExists;
Ptr++;
/* If we are on a separator, truncate at the next character.
* The trailing separator is kept for the existence check. */
if ((Ptr < End) && (*Ptr == OBJ_NAME_PATH_SEPARATOR))
PathNameU.Length = (ULONG_PTR)(Ptr+1) - (ULONG_PTR)Buffer;
/* Check if the sub-directory exists and stop
* if so: this is the deepest existing one */
DPRINT("PathName: %wZ\n", &PathNameU);
bExists = DoesPathExist_UStr(NULL, &PathNameU, TRUE);
if (bExists)
break;
/* Skip back any consecutive path separators */
while ((Ptr > Buffer) && (*Ptr == OBJ_NAME_PATH_SEPARATOR))
--Ptr;
/* Go to the beginning of the path component, stop at the separator */
while ((Ptr > Buffer) && (*Ptr != OBJ_NAME_PATH_SEPARATOR))
--Ptr;
}
while (Ptr < EndPtr)
/* Skip any consecutive path separators */
while ((Ptr < End) && (*Ptr == OBJ_NAME_PATH_SEPARATOR))
++Ptr;
/* Create all the remaining sub-directories */
for (; Ptr < End; ++Ptr)
{
if (*Ptr == L'\\')
{
*Ptr = 0;
/* Go to the end of the current path component, stop at
* the separator or terminating NUL and truncate it */
while ((Ptr < End) && (*Ptr != OBJ_NAME_PATH_SEPARATOR))
++Ptr;
PathNameU.Length = (ULONG_PTR)Ptr - (ULONG_PTR)Buffer;
DPRINT("PathBuffer: %S\n", PathBuffer);
if (!DoesDirExist(NULL, PathBuffer))
{
DPRINT("Create: %S\n", PathBuffer);
Status = SetupCreateSingleDirectory(PathBuffer);
if (!NT_SUCCESS(Status))
goto done;
}
*Ptr = L'\\';
}
Ptr++;
}
if (!DoesDirExist(NULL, PathBuffer))
{
DPRINT("Create: %S\n", PathBuffer);
Status = SetupCreateSingleDirectory(PathBuffer);
DPRINT("Create: %wZ\n", &PathNameU);
Status = SetupCreateSingleDirectory(&PathNameU);
if (!NT_SUCCESS(Status))
goto done;
break;
}
done:
DPRINT("Done.\n");
if (PathBuffer != NULL)
RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
return Status;
}
@ -694,20 +692,18 @@ CombinePaths(
}
BOOLEAN
DoesPathExist(
IN HANDLE RootDirectory OPTIONAL,
IN PCWSTR PathName,
IN BOOLEAN IsDirectory)
DoesPathExist_UStr(
_In_opt_ HANDLE RootDirectory,
_In_ PCUNICODE_STRING PathName,
_In_ BOOLEAN IsDirectory)
{
NTSTATUS Status;
UNICODE_STRING Name;
HANDLE FileHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
RtlInitUnicodeString(&Name, PathName);
InitializeObjectAttributes(&ObjectAttributes,
&Name,
(PUNICODE_STRING)PathName,
OBJ_CASE_INSENSITIVE,
RootDirectory,
NULL);
@ -729,12 +725,23 @@ DoesPathExist(
{
DPRINT("Failed to open %s '%wZ', Status 0x%08lx\n",
IsDirectory ? "directory" : "file",
&Name, Status);
PathName, Status);
}
return NT_SUCCESS(Status);
}
BOOLEAN
DoesPathExist(
_In_opt_ HANDLE RootDirectory,
_In_ PCWSTR PathName,
_In_ BOOLEAN IsDirectory)
{
UNICODE_STRING PathNameU;
RtlInitUnicodeString(&PathNameU, PathName);
return DoesPathExist_UStr(RootDirectory, &PathNameU, IsDirectory);
}
// FIXME: DEPRECATED! HACKish function that needs to be deprecated!
BOOLEAN
DoesFileExist_2(

View file

@ -10,7 +10,7 @@
NTSTATUS
SetupCreateDirectory(
IN PCWSTR DirectoryName);
_In_ PCWSTR PathName);
NTSTATUS
SetupDeleteFile(
@ -65,11 +65,17 @@ CombinePaths(
IN ULONG NumberOfPathComponents,
IN /* PCWSTR */ ...);
BOOLEAN
DoesPathExist_UStr(
_In_opt_ HANDLE RootDirectory,
_In_ PCUNICODE_STRING PathName,
_In_ BOOLEAN IsDirectory);
BOOLEAN
DoesPathExist(
IN HANDLE RootDirectory OPTIONAL,
IN PCWSTR PathName,
IN BOOLEAN IsDirectory);
_In_opt_ HANDLE RootDirectory,
_In_ PCWSTR PathName,
_In_ BOOLEAN IsDirectory);
#define DoesDirExist(RootDirectory, DirName) \
DoesPathExist((RootDirectory), (DirName), TRUE)