https://git.reactos.org/?p=reactos.git;a=commitdiff;h=aa44ab1fbceecc0ab9c69…
commit aa44ab1fbceecc0ab9c69512ca5f04eba0a6bfcd
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Mon May 22 01:09:35 2017 +0000
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Thu May 31 18:00:59 2018 +0200
[SETUPLIB] Add an ARC path to (and from) NT path resolver.
The NT path resolver allows mapping between an ARC path as specified in freeldr.ini /
boot.ini , to its corresponding NT path, if possible.
Currently, only the mapping direction "ARC to NT" is implemented. It will be
used wherever such mappings are needed, for example when identifying
the ReactOS / Windows installations from the available freeldr.ini / boot.ini entries
(for upgrading / repair purposes).
The resolver supports the usual ARC paths: multi()disk()[r|f]disk()[partition()] ;
eisa()disk()[r|f]disk()[partition()] ; multi()disk()cdrom() ;
scsi()disk()[r|f]disk()[partition()] ; scsi()cdrom()fdisk() ; ramdisk(x) ; net(x)
(actually reported as "unsupported" since it would map to some
path on some network), and the newly-introduced Win2k
signature()disk()rdisk()[partition()].
The code is in work-in-progress status.
Some validation tests, that were used during the implementation of the resolver, have
been added.
svn path=/branches/setup_improvements/; revision=74621
svn path=/branches/setup_improvements/; revision=74631
---
base/setup/lib/CMakeLists.txt | 1 +
base/setup/lib/arcname.c | 860 +++++++++++++++++++++++++++++++++++++++++
base/setup/lib/arcname.h | 21 +
base/setup/lib/arcname_tests.c | 134 +++++++
base/setup/lib/setuplib.h | 2 +
5 files changed, 1018 insertions(+)
diff --git a/base/setup/lib/CMakeLists.txt b/base/setup/lib/CMakeLists.txt
index 258a14b03c..a37be9d1fb 100644
--- a/base/setup/lib/CMakeLists.txt
+++ b/base/setup/lib/CMakeLists.txt
@@ -1,5 +1,6 @@
list(APPEND SOURCE
+ arcname.c
fsutil.c
genlist.c
ntverrsrc.c
diff --git a/base/setup/lib/arcname.c b/base/setup/lib/arcname.c
new file mode 100644
index 0000000000..d2ea9d927a
--- /dev/null
+++ b/base/setup/lib/arcname.c
@@ -0,0 +1,860 @@
+/*
+ * PROJECT: ReactOS Setup Library
+ * LICENSE: GPL-2.0+ (
https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: ARC path to-and-from NT path resolver.
+ * COPYRIGHT: Copyright 2017-2018 Hermes Belusca-Maito
+ */
+/*
+ * References:
+ *
+ * - ARC Specification v1.2:
http://netbsd.org./docs/Hardware/Machines/ARC/riscspec.pdf
+ * - "Setup and Startup", MSDN article:
https://technet.microsoft.com/en-us/library/cc977184.aspx
+ * - Answer for "How do I determine the ARC path for a particular drive letter in
Windows?":
https://serverfault.com/a/5929
+ * - ARC - LinuxMIPS:
https://www.linux-mips.org/wiki/ARC
+ * - ARCLoad - LinuxMIPS:
https://www.linux-mips.org/wiki/ARCLoad
+ * - Inside Windows 2000 Server:
https://books.google.fr/books?id=kYT7gKnwUQ8C&pg=PA71&lpg=PA71&…
+ * - Inside Windows Server 2003:
https://books.google.fr/books?id=zayrcM9ZYdAC&pg=PA61&lpg=PA61&…
+ *
+ * Stuff to read:
http://www.adminxp.com/windows2000/index.php?aid=46 and
http://www.trcb.com/Computers-and-Technology/Windows-XP/Windows-XP-ARC-Nami…
+ * concerning which values of disk() or rdisk() are valid when either scsi() or multi()
adapters are specified.
+ */
+
+/* INCLUDES *****************************************************************/
+
+#include "precomp.h"
+
+#include "partlist.h"
+#include "arcname.h"
+
+#define NDEBUG
+#include <debug.h>
+
+
+/* Supported adapter types */
+typedef enum _ADAPTER_TYPE
+{
+ EisaAdapter,
+ ScsiAdapter,
+ MultiAdapter,
+ NetAdapter,
+ RamdiskAdapter,
+ AdapterTypeMax
+} ADAPTER_TYPE, *PADAPTER_TYPE;
+const PCSTR AdapterTypes_A[] =
+{
+ "eisa",
+ "scsi",
+ "multi",
+ "net",
+ "ramdisk",
+ NULL
+};
+const PCWSTR AdapterTypes_U[] =
+{
+ L"eisa",
+ L"scsi",
+ L"multi",
+ L"net",
+ L"ramdisk",
+ NULL
+};
+
+/* Supported controller types */
+typedef enum _CONTROLLER_TYPE
+{
+ DiskController,
+ CdRomController,
+ ControllerTypeMax
+} CONTROLLER_TYPE, *PCONTROLLER_TYPE;
+const PCSTR ControllerTypes_A[] =
+{
+ "disk",
+ "cdrom",
+ NULL
+};
+const PCWSTR ControllerTypes_U[] =
+{
+ L"disk",
+ L"cdrom",
+ NULL
+};
+
+/* Supported peripheral types */
+typedef enum _PERIPHERAL_TYPE
+{
+// VDiskPeripheral,
+ RDiskPeripheral,
+ FDiskPeripheral,
+ CdRomPeripheral,
+ PeripheralTypeMax
+} PERIPHERAL_TYPE, *PPERIPHERAL_TYPE;
+const PCSTR PeripheralTypes_A[] =
+{
+// "vdisk", // Enable this when we'll support boot from virtual disks!
+ "rdisk",
+ "fdisk",
+ "cdrom",
+ NULL
+};
+const PCWSTR PeripheralTypes_U[] =
+{
+// L"vdisk", // Enable this when we'll support boot from virtual disks!
+ L"rdisk",
+ L"fdisk",
+ L"cdrom",
+ NULL
+};
+
+
+/* FUNCTIONS ****************************************************************/
+
+PCSTR
+ArcGetNextTokenA(
+ IN PCSTR ArcPath,
+ OUT PANSI_STRING TokenSpecifier,
+ OUT PULONG Key)
+{
+ HRESULT hr;
+ PCSTR p = ArcPath;
+ ULONG SpecifierLength;
+ ULONG KeyValue;
+
+ /*
+ * We must have a valid "specifier(key)" string, where 'specifier'
+ * cannot be the empty string, and is followed by '('.
+ */
+ p = strchr(p, '(');
+ if (p == NULL)
+ return NULL; /* No '(' found */
+ if (p == ArcPath)
+ return NULL; /* Path starts with '(' and is thus invalid */
+
+ SpecifierLength = (p - ArcPath) * sizeof(CHAR);
+
+ /*
+ * The strtoul function skips any leading whitespace.
+ *
+ * Note that if the token is "specifier()" then strtoul won't perform
+ * any conversion and return 0, therefore effectively making the token
+ * equivalent to "specifier(0)", as it should be.
+ */
+ // KeyValue = atoi(p);
+ KeyValue = strtoul(p, (PSTR*)&p, 10);
+
+ /* Skip any trailing whitespace */
+ while (*p && isspace(*p)) ++p;
+
+ /* The token must terminate with ')' */
+ if (*p != ')')
+ return NULL;
+#if 0
+ p = strchr(p, ')');
+ if (p == NULL)
+ return NULL;
+#endif
+
+ /* We should have succeeded, copy the token specifier in the buffer */
+ hr = StringCbCopyNA(TokenSpecifier->Buffer, TokenSpecifier->MaximumLength,
+ ArcPath, SpecifierLength);
+ if (FAILED(hr))
+ return NULL;
+
+ TokenSpecifier->Length = strlen(TokenSpecifier->Buffer) * sizeof(CHAR);
+
+ /* We succeeded, return the token key value */
+ *Key = KeyValue;
+
+ /* Next token starts just after */
+ return ++p;
+}
+
+PCWSTR
+ArcGetNextTokenU(
+ IN PCWSTR ArcPath,
+ OUT PUNICODE_STRING TokenSpecifier,
+ OUT PULONG Key)
+{
+ HRESULT hr;
+ PCWSTR p = ArcPath;
+ ULONG SpecifierLength;
+ ULONG KeyValue;
+
+ /*
+ * We must have a valid "specifier(key)" string, where 'specifier'
+ * cannot be the empty string, and is followed by '('.
+ */
+ p = wcschr(p, L'(');
+ if (p == NULL)
+ return NULL; /* No '(' found */
+ if (p == ArcPath)
+ return NULL; /* Path starts with '(' and is thus invalid */
+
+ SpecifierLength = (p - ArcPath) * sizeof(WCHAR);
+
+ ++p;
+
+ /*
+ * The strtoul function skips any leading whitespace.
+ *
+ * Note that if the token is "specifier()" then strtoul won't perform
+ * any conversion and return 0, therefore effectively making the token
+ * equivalent to "specifier(0)", as it should be.
+ */
+ // KeyValue = _wtoi(p);
+ KeyValue = wcstoul(p, (PWSTR*)&p, 10);
+ ASSERT(p);
+
+ /* Skip any trailing whitespace */
+ while (*p && iswspace(*p)) ++p;
+
+ /* The token must terminate with ')' */
+ if (*p != L')')
+ return NULL;
+#if 0
+ p = wcschr(p, L')');
+ if (p == NULL)
+ return NULL;
+#endif
+
+ /* We should have succeeded, copy the token specifier in the buffer */
+ hr = StringCbCopyNW(TokenSpecifier->Buffer, TokenSpecifier->MaximumLength,
+ ArcPath, SpecifierLength);
+ if (FAILED(hr))
+ return NULL;
+
+ TokenSpecifier->Length = wcslen(TokenSpecifier->Buffer) * sizeof(WCHAR);
+
+ /* We succeeded, return the token key value */
+ *Key = KeyValue;
+
+ /* Next token starts just after */
+ return ++p;
+}
+
+
+ULONG
+ArcMatchTokenA(
+ IN PCSTR CandidateToken,
+ IN const PCSTR* TokenTable)
+{
+ ULONG Index = 0;
+
+ while (TokenTable[Index] && _stricmp(CandidateToken, TokenTable[Index]) !=
0)
+ {
+ ++Index;
+ }
+
+ return Index;
+}
+
+ULONG
+ArcMatchTokenU(
+ IN PCWSTR CandidateToken,
+ IN const PCWSTR* TokenTable)
+{
+ ULONG Index = 0;
+
+ while (TokenTable[Index] && _wcsicmp(CandidateToken, TokenTable[Index]) !=
0)
+ {
+ ++Index;
+ }
+
+ return Index;
+}
+
+ULONG
+ArcMatchToken_UStr(
+ IN PCUNICODE_STRING CandidateToken,
+ IN const PCWSTR* TokenTable)
+{
+ ULONG Index = 0;
+#if 0
+ SIZE_T Length;
+#else
+ UNICODE_STRING Token;
+#endif
+
+ while (TokenTable[Index])
+ {
+#if 0
+ Length = wcslen(TokenTable[Index])*sizeof(WCHAR);
+ if (RtlCompareMemory(CandidateToken->Buffer, TokenTable[Index], Length) ==
Length)
+ break;
+#else
+ RtlInitUnicodeString(&Token, TokenTable[Index]);
+ // if (RtlCompareUnicodeString(CandidateToken, &Token, TRUE) == 0)
+ if (RtlEqualUnicodeString(CandidateToken, &Token, TRUE))
+ break;
+#endif
+
+ ++Index;
+ }
+
+ return Index;
+}
+
+
+BOOLEAN
+ArcPathNormalize(
+ OUT PUNICODE_STRING NormalizedArcPath,
+ IN PCWSTR ArcPath)
+{
+ HRESULT hr;
+ PCWSTR EndOfArcName;
+ PCWSTR p;
+
+ if (NormalizedArcPath->MaximumLength < sizeof(UNICODE_NULL))
+ return FALSE;
+
+ *NormalizedArcPath->Buffer = UNICODE_NULL;
+ NormalizedArcPath->Length = 0;
+
+ EndOfArcName = wcschr(ArcPath, OBJ_NAME_PATH_SEPARATOR);
+ if (!EndOfArcName)
+ EndOfArcName = ArcPath + wcslen(ArcPath);
+
+ while ((p = wcsstr(ArcPath, L"()")) && (p < EndOfArcName))
+ {
+#if 0
+ hr = StringCbCopyNW(NormalizedArcPath->Buffer,
NormalizedArcPath->MaximumLength,
+ ArcPath, (p - ArcPath) * sizeof(WCHAR));
+#else
+ hr = StringCbCatNW(NormalizedArcPath->Buffer,
NormalizedArcPath->MaximumLength,
+ ArcPath, (p - ArcPath) * sizeof(WCHAR));
+#endif
+ if (FAILED(hr))
+ return FALSE;
+ hr = StringCbCatW(NormalizedArcPath->Buffer,
NormalizedArcPath->MaximumLength, L"(0)");
+ if (FAILED(hr))
+ return FALSE;
+#if 0
+ NormalizedArcPath->Buffer += wcslen(NormalizedArcPath->Buffer);
+#endif
+ ArcPath = p + 2;
+ }
+ hr = StringCbCatW(NormalizedArcPath->Buffer, NormalizedArcPath->MaximumLength,
ArcPath);
+ if (FAILED(hr))
+ return FALSE;
+
+ NormalizedArcPath->Length = wcslen(NormalizedArcPath->Buffer) * sizeof(WCHAR);
+ return TRUE;
+}
+
+
+/*
+ * ArcName:
+ * ARC name (counted string) to be resolved into a NT device name.
+ * The caller should have already delimited it from within an ARC path
+ * (usually by finding where the first path separator appears in the path).
+ *
+ * NtName:
+ * Receives the resolved NT name. The buffer is NULL-terminated.
+ */
+static NTSTATUS
+ResolveArcNameNtSymLink(
+ OUT PUNICODE_STRING NtName,
+ IN PUNICODE_STRING ArcName)
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE DirectoryHandle, LinkHandle;
+ UNICODE_STRING ArcNameDir;
+
+ if (NtName->MaximumLength < sizeof(UNICODE_NULL))
+ return STATUS_BUFFER_TOO_SMALL;
+
+#if 0
+ *NtName->Buffer = UNICODE_NULL;
+ NtName->Length = 0;
+#endif
+
+ /* Open the \ArcName object directory */
+ RtlInitUnicodeString(&ArcNameDir, L"\\ArcName");
+ InitializeObjectAttributes(&ObjectAttributes,
+ &ArcNameDir,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+ Status = NtOpenDirectoryObject(&DirectoryHandle,
+ DIRECTORY_ALL_ACCESS,
+ &ObjectAttributes);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("NtOpenDirectoryObject(%wZ) failed, Status 0x%08lx\n",
&ArcNameDir, Status);
+ return Status;
+ }
+
+ /* Open the ARC name link */
+ InitializeObjectAttributes(&ObjectAttributes,
+ ArcName,
+ OBJ_CASE_INSENSITIVE,
+ DirectoryHandle,
+ NULL);
+ Status = NtOpenSymbolicLinkObject(&LinkHandle,
+ SYMBOLIC_LINK_ALL_ACCESS,
+ &ObjectAttributes);
+
+ /* Close the \ArcName object directory handle */
+ NtClose(DirectoryHandle);
+
+ /* Check for success */
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("NtOpenSymbolicLinkObject(%wZ) failed, Status 0x%08lx\n",
ArcName, Status);
+ return Status;
+ }
+
+ /* Reserve one WCHAR for the NULL-termination */
+ NtName->MaximumLength -= sizeof(UNICODE_NULL);
+
+ /* Resolve the link */
+ Status = NtQuerySymbolicLinkObject(LinkHandle, NtName, NULL);
+
+ /* Restore the NULL-termination */
+ NtName->MaximumLength += sizeof(UNICODE_NULL);
+
+ /* Check for success */
+ if (!NT_SUCCESS(Status))
+ {
+ /* We failed, don't touch NtName */
+ DPRINT1("NtQuerySymbolicLinkObject(%wZ) failed, Status 0x%08lx\n",
ArcName, Status);
+ }
+ else
+ {
+ /* We succeeded, NULL-terminate NtName */
+ NtName->Buffer[NtName->Length / sizeof(WCHAR)] = UNICODE_NULL;
+ }
+
+ NtClose(LinkHandle);
+ return Status;
+}
+
+/*
+ * ArcNamePath:
+ * In input, pointer to an ARC path (NULL-terminated) starting by an ARC name
+ * to be resolved into a NT device name.
+ * In opposition to ResolveArcNameNtSymLink(), the caller does not have to
+ * delimit the ARC name from within an ARC path. The real ARC name is deduced
+ * after parsing the ARC path, and, in output, ArcNamePath points to the
+ * beginning of the path after the ARC name part.
+ *
+ * NtName:
+ * Receives the resolved NT name. The buffer is NULL-terminated.
+ *
+ * PartList:
+ * (Optional) partition list that helps in resolving the paths pointing
+ * to hard disks.
+ */
+static NTSTATUS
+ResolveArcNameManually(
+ OUT PUNICODE_STRING NtName,
+ IN OUT PCWSTR* ArcNamePath,
+ IN PPARTLIST PartList OPTIONAL)
+{
+ HRESULT hr;
+ WCHAR TokenBuffer[50];
+ UNICODE_STRING Token;
+ PCWSTR p, q;
+ ULONG AdapterKey;
+ ULONG ControllerKey;
+ ULONG PeripheralKey;
+ ULONG PartitionNumber;
+ ADAPTER_TYPE AdapterType;
+ CONTROLLER_TYPE ControllerType;
+ PERIPHERAL_TYPE PeripheralType;
+ BOOLEAN UseSignature = FALSE;
+
+ PDISKENTRY DiskEntry;
+ PPARTENTRY PartEntry = NULL;
+
+ if (NtName->MaximumLength < sizeof(UNICODE_NULL))
+ return STATUS_BUFFER_TOO_SMALL;
+
+#if 0
+ *NtName->Buffer = UNICODE_NULL;
+ NtName->Length = 0;
+#endif
+
+ /*
+ * The format of ArcName is:
+ * adapter(www)[controller(xxx)peripheral(yyy)[partition(zzz)][filepath]] ,
+ * where the [filepath] part is not being parsed.
+ */
+
+ RtlInitEmptyUnicodeString(&Token, TokenBuffer, sizeof(TokenBuffer));
+
+ p = *ArcNamePath;
+
+ /* Retrieve the adapter */
+ p = ArcGetNextTokenU(p, &Token, &AdapterKey);
+ if (!p)
+ {
+ DPRINT1("No adapter specified!\n");
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ /* Check for the 'signature()' pseudo-adapter, introduced in Windows 2000 */
+ if (_wcsicmp(Token.Buffer, L"signature") == 0)
+ {
+ /*
+ * We've got a signature! Remember this for later, and set the adapter type
to SCSI.
+ * We however check that the rest of the ARC path is valid by parsing the other
tokens.
+ * AdapterKey stores the disk signature value (that holds in a ULONG).
+ */
+ UseSignature = TRUE;
+ AdapterType = ScsiAdapter;
+ }
+ else
+ {
+ /* Check for regular adapters */
+ AdapterType =
(ADAPTER_TYPE)/*ArcMatchTokenU*/ArcMatchToken_UStr(/*Token.Buffer*/&Token,
AdapterTypes_U);
+ if (AdapterType >= AdapterTypeMax)
+ {
+ DPRINT1("Invalid adapter type %wZ\n", &Token);
+ return STATUS_OBJECT_NAME_INVALID;
+ }
+
+ /* Check for adapters that don't take any extra controller or peripheral
nodes */
+ if (AdapterType == NetAdapter || AdapterType == RamdiskAdapter)
+ {
+ // if (*p)
+ // return STATUS_OBJECT_PATH_SYNTAX_BAD;
+
+ if (AdapterType == NetAdapter)
+ {
+ DPRINT1("%S(%lu) path is not supported!\n",
AdapterTypes_U[AdapterType], AdapterKey);
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ hr = StringCbPrintfW(NtName->Buffer, NtName->MaximumLength,
L"\\Device\\Ramdisk%lu", AdapterKey);
+ goto Quit;
+ }
+ }
+
+ /* Here, we have either an 'eisa', a 'scsi/signature', or a
'multi' adapter */
+
+ /* Check for a valid controller */
+ p = ArcGetNextTokenU(p, &Token, &ControllerKey);
+ if (!p)
+ {
+ DPRINT1("%S(%lu) adapter doesn't have a controller!\n",
AdapterTypes_U[AdapterType], AdapterKey);
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+ ControllerType =
(CONTROLLER_TYPE)/*ArcMatchTokenU*/ArcMatchToken_UStr(/*Token.Buffer*/&Token,
ControllerTypes_U);
+ if (ControllerType >= ControllerTypeMax)
+ {
+ DPRINT1("Invalid controller type %wZ\n", &Token);
+ return STATUS_OBJECT_NAME_INVALID;
+ }
+
+ /* Here the controller can only be either a disk or a CDROM */
+
+ /*
+ * Ignore the controller in case we have a 'multi' adapter.
+ * I guess a similar condition holds for the 'eisa' adapter too...
+ *
+ * For SignatureAdapter, as similar for ScsiAdapter, the controller key corresponds
+ * to the disk target ID. Note that actually, the implementation just ignores the
+ * target ID, as well as the LUN, and just loops over all the available disks and
+ * searches for the one having the correct signature.
+ */
+ if ((AdapterType == MultiAdapter /* || AdapterType == EisaAdapter */) &&
ControllerKey != 0)
+ {
+ DPRINT1("%S(%lu) adapter with %S(%lu non-zero), ignored!\n",
+ AdapterTypes_U[AdapterType], AdapterKey,
+ ControllerTypes_U[ControllerType], ControllerKey);
+ ControllerKey = 0;
+ }
+
+ /*
+ * Only the 'scsi' adapter supports a direct 'cdrom' controller.
+ * For the others, we need a 'disk' controller to which a 'cdrom'
peripheral can talk to.
+ */
+ if ((AdapterType != ScsiAdapter) && (ControllerType == CdRomController))
+ {
+ DPRINT1("%S(%lu) adapter cannot have a CDROM controller!\n",
AdapterTypes_U[AdapterType], AdapterKey);
+ return STATUS_OBJECT_PATH_INVALID;
+ }
+
+ /* Check for a valid peripheral */
+ p = ArcGetNextTokenU(p, &Token, &PeripheralKey);
+ if (!p)
+ {
+ DPRINT1("%S(%lu)%S(%lu) adapter-controller doesn't have a
peripheral!\n",
+ AdapterTypes_U[AdapterType], AdapterKey,
+ ControllerTypes_U[ControllerType], ControllerKey);
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+ PeripheralType =
(PERIPHERAL_TYPE)/*ArcMatchTokenU*/ArcMatchToken_UStr(/*Token.Buffer*/&Token,
PeripheralTypes_U);
+ if (PeripheralType >= PeripheralTypeMax)
+ {
+ DPRINT1("Invalid peripheral type %wZ\n", &Token);
+ return STATUS_OBJECT_NAME_INVALID;
+ }
+
+ /*
+ * If we had a 'cdrom' controller already, the corresponding peripheral can
only be 'fdisk'
+ * (see for example the ARC syntax for SCSI CD-ROMs: scsi(x)cdrom(y)fdisk(z) where z
== 0).
+ */
+ if ((ControllerType == CdRomController) && (PeripheralType !=
FDiskPeripheral))
+ {
+ DPRINT1("%S(%lu) controller cannot have a %S(%lu) peripheral! (note that we
haven't check whether the adapter was SCSI or not)\n",
+ ControllerTypes_U[ControllerType], ControllerKey,
+ PeripheralTypes_U[PeripheralType], PeripheralKey);
+ return STATUS_OBJECT_PATH_INVALID;
+ }
+
+ /* For a 'scsi' adapter, the possible peripherals are only 'rdisk' or
'fdisk' */
+ if (AdapterType == ScsiAdapter && !(PeripheralType == RDiskPeripheral ||
PeripheralType == FDiskPeripheral))
+ {
+ DPRINT1("%S(%lu)%S(%lu) SCSI adapter-controller has an invalid peripheral
%S(%lu) !\n",
+ AdapterTypes_U[AdapterType], AdapterKey,
+ ControllerTypes_U[ControllerType], ControllerKey,
+ PeripheralTypes_U[PeripheralType], PeripheralKey);
+ return STATUS_OBJECT_PATH_INVALID;
+ }
+
+#if 0
+ if (AdapterType == SignatureAdapter && PeripheralKey != 0)
+ {
+ DPRINT1("%S(%lu) adapter with %S(%lu non-zero), ignored!\n",
+ AdapterTypes_U[AdapterType], AdapterKey,
+ PeripheralTypes_U[PeripheralType], PeripheralKey);
+ PeripheralKey = 0;
+ }
+#endif
+
+ /* Check for the optional 'partition' specifier */
+ q = ArcGetNextTokenU(p, &Token, &PartitionNumber);
+ if (q && _wcsicmp(Token.Buffer, L"partition") == 0)
+ {
+ /* We've got a partition! */
+ p = q;
+ }
+ else
+ {
+ /*
+ * Either no other ARC token was found, or we've got something else
+ * (possibly invalid or not)...
+ */
+ PartitionNumber = 0;
+ }
+
+
+ // TODO: Check the partition number in case of fdisks and cdroms??
+
+
+ if (ControllerType == CdRomController) // and so, AdapterType == ScsiAdapter and
PeripheralType == FDiskPeripheral
+ hr = StringCbPrintfW(NtName->Buffer, NtName->MaximumLength,
L"\\Device\\Scsi\\CdRom%lu", ControllerKey);
+ else
+ /* Now, ControllerType == DiskController */
+ if (PeripheralType == CdRomPeripheral)
+ hr = StringCbPrintfW(NtName->Buffer, NtName->MaximumLength,
L"\\Device\\CdRom%lu", PeripheralKey);
+ else
+ if (PeripheralType == FDiskPeripheral)
+ hr = StringCbPrintfW(NtName->Buffer, NtName->MaximumLength,
L"\\Device\\Floppy%lu", PeripheralKey);
+ else
+ if (PeripheralType == RDiskPeripheral)
+ {
+ if (UseSignature)
+ {
+ /* The disk signature is stored in AdapterKey */
+ DiskEntry = GetDiskBySignature(PartList, AdapterKey);
+ }
+ else
+ {
+ DiskEntry = GetDiskBySCSI(PartList, AdapterKey,
+ ControllerKey, PeripheralKey);
+ }
+ if (!DiskEntry)
+ return STATUS_OBJECT_PATH_NOT_FOUND; // STATUS_NOT_FOUND;
+
+ if (PartitionNumber != 0)
+ {
+ PartEntry = GetPartition(DiskEntry, PartitionNumber);
+ if (!PartEntry)
+ return STATUS_OBJECT_PATH_NOT_FOUND; // STATUS_DEVICE_NOT_PARTITIONED;
+ ASSERT(PartEntry->DiskEntry == DiskEntry);
+ }
+
+ hr = StringCbPrintfW(NtName->Buffer, NtName->MaximumLength,
L"\\Device\\Harddisk%lu\\Partition%lu",
+ DiskEntry->DiskNumber, PartitionNumber);
+ }
+#if 0
+ else
+ if (PeripheralType == VDiskPeripheral)
+ {
+ // TODO: Check how Win 7+ deals with virtual disks.
+ hr = StringCbPrintfW(NtName->Buffer, NtName->MaximumLength,
L"\\Device\\VirtualHarddisk%lu\\Partition%lu",
+ PeripheralKey, PartitionNumber);
+ }
+#endif
+
+Quit:
+ if (FAILED(hr))
+ {
+ /*
+ * We can directly cast the HRESULTs into NTSTATUS since the error codes
+ * returned by StringCbPrintfW:
+ * STRSAFE_E_INVALID_PARAMETER == 0x80070057,
+ * STRSAFE_E_INSUFFICIENT_BUFFER == 0x8007007a,
+ * do not have assigned values in the NTSTATUS space.
+ */
+ return (NTSTATUS)hr;
+ }
+
+ *ArcNamePath = p;
+ return STATUS_SUCCESS;
+}
+
+
+/**** FIXME: Redundant with filesup.c ! ****\
+|** (but filesup.c is not yet included in **|
+\** setuplib, hence this code copy) **/
+
+static
+HRESULT
+ConcatPaths(
+ IN OUT PWSTR PathElem1,
+ IN SIZE_T cchPathSize,
+ IN PCWSTR PathElem2 OPTIONAL)
+{
+ HRESULT hr;
+ SIZE_T cchPathLen;
+
+ if (!PathElem2)
+ return S_OK;
+ if (cchPathSize <= 1)
+ return S_OK;
+
+ cchPathLen = min(cchPathSize, wcslen(PathElem1));
+
+ if (PathElem2[0] != L'\\' && cchPathLen > 0 &&
PathElem1[cchPathLen-1] != L'\\')
+ {
+ /* PathElem2 does not start with '\' and PathElem1 does not end with
'\' */
+ hr = StringCchCatW(PathElem1, cchPathSize, L"\\");
+ if (FAILED(hr))
+ return hr;
+ }
+ else if (PathElem2[0] == L'\\' && cchPathLen > 0 &&
PathElem1[cchPathLen-1] == L'\\')
+ {
+ /* PathElem2 starts with '\' and PathElem1 ends with '\' */
+ while (*PathElem2 == L'\\')
+ ++PathElem2; // Skip any backslash
+ }
+ hr = StringCchCatW(PathElem1, cchPathSize, PathElem2);
+ return hr;
+}
+
+/*******************************************/
+
+
+BOOLEAN
+ArcPathToNtPath(
+ OUT PUNICODE_STRING NtPath,
+ IN PCWSTR ArcPath,
+ IN PPARTLIST PartList OPTIONAL)
+{
+ NTSTATUS Status;
+ PCWSTR BeginOfPath;
+ UNICODE_STRING ArcName;
+
+ /* TODO: We should "normalize" the path, i.e. expand all the xxx() into
xxx(0) */
+
+ if (NtPath->MaximumLength < sizeof(UNICODE_NULL))
+ return FALSE;
+
+ *NtPath->Buffer = UNICODE_NULL;
+ NtPath->Length = 0;
+
+ /*
+ * - First, check whether the ARC path is already inside \\ArcName
+ * and if so, map it to the corresponding NT path.
+ * - Only then, if we haven't found any ArcName, try to build a
+ * NT path by deconstructing the ARC path, using its disk and
+ * partition numbers. We may use here our disk/partition list.
+ *
+ * See also freeldr/arcname.c
+ *
+ * Note that it would be nice to maintain a cache of these mappings.
+ */
+
+ /*
+ * Initialize the ARC name to resolve, by cutting the ARC path at the first
+ * NT path separator. The ARC name therefore ends where the NT path part starts.
+ */
+ RtlInitUnicodeString(&ArcName, ArcPath);
+ BeginOfPath = wcschr(ArcName.Buffer, OBJ_NAME_PATH_SEPARATOR);
+ if (BeginOfPath)
+ ArcName.Length = (ULONG_PTR)BeginOfPath - (ULONG_PTR)ArcName.Buffer;
+
+ /* Resolve the ARC name via NT SymLinks. Note that NtPath is returned
NULL-terminated. */
+ Status = ResolveArcNameNtSymLink(NtPath, &ArcName);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We failed, attempt a manual resolution */
+ DPRINT1("ResolveArcNameNtSymLink(ArcName = '%wZ') for ArcPath =
'%S' failed, Status 0x%08lx\n", &ArcName, ArcPath, Status);
+
+ /*
+ * We failed at directly resolving the ARC path, and we cannot perform
+ * a manual resolution because we don't have any disk/partition list,
+ * we therefore fail here.
+ */
+ if (!PartList)
+ {
+ DPRINT1("PartList == NULL, cannot perform a manual resolution\n");
+ return FALSE;
+ }
+
+ *NtPath->Buffer = UNICODE_NULL;
+ NtPath->Length = 0;
+
+ BeginOfPath = ArcPath;
+ Status = ResolveArcNameManually(NtPath, &BeginOfPath, PartList);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We really failed this time, bail out */
+ DPRINT1("ResolveArcNameManually(ArcPath = '%S') failed, Status
0x%08lx\n", ArcPath, Status);
+ return FALSE;
+ }
+ }
+
+ /*
+ * We succeeded. Concatenate the rest of the system-specific path. We know the path
is going
+ * to be inside the NT namespace, therefore we can use the path string concatenation
function
+ * that uses '\\' as the path separator.
+ */
+ if (BeginOfPath && *BeginOfPath)
+ {
+ HRESULT hr;
+ hr = ConcatPaths(NtPath->Buffer, NtPath->MaximumLength / sizeof(WCHAR),
BeginOfPath);
+ if (FAILED(hr))
+ {
+ /* Buffer not large enough, or whatever...: just bail out */
+ return FALSE;
+ }
+ }
+ NtPath->Length = wcslen(NtPath->Buffer) * sizeof(WCHAR);
+ return TRUE;
+}
+
+#if 0
+PWSTR
+NtPathToArcPath(
+ IN PWSTR NtPath)
+{
+ /*
+ * - First, check whether any of the ARC paths inside \\ArcName
+ * map to the corresponding NT path. If so, we are OK.
+ * - Only then, if we haven't found any ArcName, try to build an
+ * ARC path by deconstructing the NT path, using its disk and
+ * partition numbers. We may use here our disk/partition list.
+ *
+ * See also freeldr/arcname.c
+ *
+ * Note that it would be nice to maintain a cache of these mappings.
+ */
+}
+#endif
+
+/* EOF */
diff --git a/base/setup/lib/arcname.h b/base/setup/lib/arcname.h
new file mode 100644
index 0000000000..45d5b6b5c7
--- /dev/null
+++ b/base/setup/lib/arcname.h
@@ -0,0 +1,21 @@
+/*
+ * PROJECT: ReactOS Setup Library
+ * LICENSE: GPL-2.0+ (
https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: ARC path to-and-from NT path resolver.
+ * COPYRIGHT: Copyright 2017-2018 Hermes Belusca-Maito
+ */
+
+#pragma once
+
+BOOLEAN
+ArcPathNormalize(
+ OUT PUNICODE_STRING NormalizedArcPath,
+ IN PCWSTR ArcPath);
+
+BOOLEAN
+ArcPathToNtPath(
+ OUT PUNICODE_STRING NtPath,
+ IN PCWSTR ArcPath,
+ IN PPARTLIST PartList OPTIONAL);
+
+/* EOF */
diff --git a/base/setup/lib/arcname_tests.c b/base/setup/lib/arcname_tests.c
new file mode 100644
index 0000000000..deba131552
--- /dev/null
+++ b/base/setup/lib/arcname_tests.c
@@ -0,0 +1,134 @@
+/*
+ * PROJECT: ReactOS Setup Library
+ * LICENSE: GPL-2.0+ (
https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: Tests for the arcname.c functions:
+ * ArcPathNormalize(), ArcPathToNtPath().
+ * COPYRIGHT: Copyright 2017-2018 Hermes Belusca-Maito
+ *
+ * You may need to fix the included headers before being able to
+ * compile this file (this file has only been compiled under VS).
+ */
+
+#include <stdio.h>
+#include <tchar.h>
+#include <conio.h>
+
+#define WIN32_NO_STATUS
+#include <windows.h>
+#include <winternl.h>
+#undef WIN32_NO_STATUS
+
+#include <ntstatus.h>
+
+#include <strsafe.h>
+
+#include "arcname.h"
+
+#define OBJ_NAME_PATH_SEPARATOR ((WCHAR)L'\\')
+
+int _tmain(int argc, _TCHAR* argv[])
+{
+ WCHAR ArcPath[MAX_PATH] = L"multi(5)disk()rdisk(1)partition()\\ReactOS";
+ WCHAR NormalizedArcPathBuffer[MAX_PATH];
+ UNICODE_STRING NormalizedArcPath;
+ WCHAR NtPathBuffer[MAX_PATH];
+ UNICODE_STRING NtPath;
+
+ NormalizedArcPath.Buffer = NormalizedArcPathBuffer;
+ NormalizedArcPath.Length = 0;
+ NormalizedArcPath.MaximumLength = sizeof(NormalizedArcPathBuffer);
+
+ ArcPathNormalize(&NormalizedArcPath, ArcPath);
+ wprintf(L"ArcPath = '%s' ; Normalized = '%wZ'\n", ArcPath,
&NormalizedArcPath);
+
+ NtPath.Buffer = NtPathBuffer;
+ NtPath.Length = 0;
+ NtPath.MaximumLength = sizeof(NtPathBuffer);
+
+ ArcPathToNtPath(&NtPath, NormalizedArcPath.Buffer);
+ // wprintf(L"ArcPath = '%s' ; NtPath = '%wZ'\n", ArcPath,
&NtPath);
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"ramdisk(0)"); // OK
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath,
L"ramdisk(0)\\ReactOS\\system32\\ntoskrnl.exe"); // OK
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"net(0)\\Foobar"); // OK but not
supported
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"net(0)disk(1)\\Foobar"); // Bad
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"scsi(2)disk(1)rdisk(3)"); // OK
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"scsi(2)disk(1)fdisk(3)"); // OK
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"scsi(2)cdrom(1)"); // Bad: missing
fdisk
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"scsi(2)cdrom(1)cdrom(0)"); // Bad: twice
cdrom
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"scsi(2)cdrom(1)fdisk(0)"); // OK
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"scsi(2)cdrom(1)rdisk(0)"); // Bad; cdrom
controller and rdisk peripheral
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"multi(2)cdrom(1)fdisk(0)"); // Bad: multi
adapter cannot have cdrom controller
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"multi(2)rdisk(1)cdrom(1)fdisk(0)"); // Bad:
rdisk is not a controller
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"multi(2)disk(1)cdrom(1)fdisk(0)"); // OK
(disk(1) ignored)
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"multi(2)disk(1)rdisk(1)fdisk(0)"); // Same
(and also fdisk is not considered as part of ARC path)
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"multi(2)disk(1)rdisk(1)partition(3)"); //
OK (disk(1) ignored)
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+
+ _getch();
+
+ /* All these are OK */
+ ArcPathToNtPath(&NtPath,
L"scsi(0)disk(3)rdisk(0)partition(1)\\OS.DIR");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath,
L"scsi(1)disk(3)rdisk(3)partition(2)\\OS\\ARCOS\\LOADER");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+
+ _getch();
+
+ /* All these are OK */
+ ArcPathToNtPath(&NtPath, L"multi(0)disk(0)rdisk(0)partition(1)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"multi(0)disk(0)rdisk(0)partition(0)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"multi(0)disk(0)cdrom(3)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"ramdisk(0)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"net(0)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"multi(0)disk(0)fdisk(0)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"multi(0)disk(0)rdisk(1)partition(0)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"multi(0)disk(0)rdisk(1)partition(3)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"multi(0)disk(0)rdisk(1)partition(1)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"multi(0)disk(0)rdisk(0)partition(3)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"multi(0)disk(0)fdisk(1)partition(0)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"multi(0)disk(0)fdisk(0)partition(0)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"multi(0)disk(0)fdisk(1)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"eisa(0)disk(0)fdisk(0)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"eisa(0)disk(0)fdisk(1)partition(0)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"eisa(0)disk(0)fdisk(0)partition(0)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+
+ /* These are invalid storage ARC paths (but otherwise are valid ARC names) */
+ ArcPathToNtPath(&NtPath, L"multi(0)video(0)monitor(0)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+ ArcPathToNtPath(&NtPath, L"multi(0)key(0)keyboard(0)");
+ wprintf(L"NtPath = '%wZ'\n", &NtPath);
+
+ _getch();
+ return 0;
+}
diff --git a/base/setup/lib/setuplib.h b/base/setup/lib/setuplib.h
index 6a4452429b..055ed5eef7 100644
--- a/base/setup/lib/setuplib.h
+++ b/base/setup/lib/setuplib.h
@@ -28,8 +28,10 @@ extern HANDLE ProcessHeap;
#include "errorcode.h"
#include "linklist.h"
#include "ntverrsrc.h"
+// #include "arcname.h"
#include "fsutil.h"
#include "genlist.h"
#include "partlist.h"
+#include "arcname.h"
/* EOF */