2 * PROJECT: ReactOS Setup Library
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: ARC path to-and-from NT path resolver.
5 * COPYRIGHT: Copyright 2017-2018 Hermes Belusca-Maito
10 * - ARC Specification v1.2: http://netbsd.org./docs/Hardware/Machines/ARC/riscspec.pdf
11 * - "Setup and Startup", MSDN article: https://technet.microsoft.com/en-us/library/cc977184.aspx
12 * - Answer for "How do I determine the ARC path for a particular drive letter in Windows?": https://serverfault.com/a/5929
13 * - ARC - LinuxMIPS: https://www.linux-mips.org/wiki/ARC
14 * - ARCLoad - LinuxMIPS: https://www.linux-mips.org/wiki/ARCLoad
15 * - Inside Windows 2000 Server: https://books.google.fr/books?id=kYT7gKnwUQ8C&pg=PA71&lpg=PA71&dq=nt+arc+path&source=bl&ots=K8I1F_KQ_u&sig=EJq5t-v2qQk-QB7gNSREFj7pTVo&hl=en&sa=X&redir_esc=y#v=onepage&q=nt%20arc%20path&f=false
16 * - Inside Windows Server 2003: https://books.google.fr/books?id=zayrcM9ZYdAC&pg=PA61&lpg=PA61&dq=arc+path+to+nt+path&source=bl&ots=x2JSWfp2MA&sig=g9mufN6TCOrPejDov6Rjp0Jrldo&hl=en&sa=X&redir_esc=y#v=onepage&q=arc%20path%20to%20nt%20path&f=false
18 * 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-Naming-Conventions-1432.htm
19 * concerning which values of disk() or rdisk() are valid when either scsi() or multi() adapters are specified.
22 /* INCLUDES *****************************************************************/
34 /* TYPEDEFS *****************************************************************/
36 /* Supported adapter types */
37 typedef enum _ADAPTER_TYPE
45 } ADAPTER_TYPE
, *PADAPTER_TYPE
;
46 const PCSTR AdapterTypes_A
[] =
55 const PCWSTR AdapterTypes_U
[] =
65 /* Supported controller types */
66 typedef enum _CONTROLLER_TYPE
71 } CONTROLLER_TYPE
, *PCONTROLLER_TYPE
;
72 const PCSTR ControllerTypes_A
[] =
78 const PCWSTR ControllerTypes_U
[] =
85 /* Supported peripheral types */
86 typedef enum _PERIPHERAL_TYPE
93 } PERIPHERAL_TYPE
, *PPERIPHERAL_TYPE
;
94 const PCSTR PeripheralTypes_A
[] =
96 // "vdisk", // Enable this when we'll support boot from virtual disks!
102 const PCWSTR PeripheralTypes_U
[] =
104 // L"vdisk", // Enable this when we'll support boot from virtual disks!
112 /* FUNCTIONS ****************************************************************/
117 OUT PANSI_STRING TokenSpecifier
,
122 ULONG SpecifierLength
;
126 * We must have a valid "specifier(key)" string, where 'specifier'
127 * cannot be the empty string, and is followed by '('.
131 return NULL
; /* No '(' found */
133 return NULL
; /* Path starts with '(' and is thus invalid */
135 SpecifierLength
= (p
- ArcPath
) * sizeof(CHAR
);
138 * The strtoul function skips any leading whitespace.
140 * Note that if the token is "specifier()" then strtoul won't perform
141 * any conversion and return 0, therefore effectively making the token
142 * equivalent to "specifier(0)", as it should be.
144 // KeyValue = atoi(p);
145 KeyValue
= strtoul(p
, (PSTR
*)&p
, 10);
147 /* Skip any trailing whitespace */
148 while (isspace(*p
)) ++p
;
150 /* The token must terminate with ')' */
159 /* We should have succeeded, copy the token specifier in the buffer */
160 Status
= RtlStringCbCopyNA(TokenSpecifier
->Buffer
,
161 TokenSpecifier
->MaximumLength
,
162 ArcPath
, SpecifierLength
);
163 if (!NT_SUCCESS(Status
))
166 TokenSpecifier
->Length
= strlen(TokenSpecifier
->Buffer
) * sizeof(CHAR
);
168 /* We succeeded, return the token key value */
171 /* Next token starts just after */
178 OUT PUNICODE_STRING TokenSpecifier
,
183 ULONG SpecifierLength
;
187 * We must have a valid "specifier(key)" string, where 'specifier'
188 * cannot be the empty string, and is followed by '('.
192 return NULL
; /* No '(' found */
194 return NULL
; /* Path starts with '(' and is thus invalid */
196 SpecifierLength
= (p
- ArcPath
) * sizeof(WCHAR
);
201 * The strtoul function skips any leading whitespace.
203 * Note that if the token is "specifier()" then strtoul won't perform
204 * any conversion and return 0, therefore effectively making the token
205 * equivalent to "specifier(0)", as it should be.
207 // KeyValue = _wtoi(p);
208 KeyValue
= wcstoul(p
, (PWSTR
*)&p
, 10);
210 /* Skip any trailing whitespace */
211 while (iswspace(*p
)) ++p
;
213 /* The token must terminate with ')' */
222 /* We should have succeeded, copy the token specifier in the buffer */
223 Status
= RtlStringCbCopyNW(TokenSpecifier
->Buffer
,
224 TokenSpecifier
->MaximumLength
,
225 ArcPath
, SpecifierLength
);
226 if (!NT_SUCCESS(Status
))
229 TokenSpecifier
->Length
= wcslen(TokenSpecifier
->Buffer
) * sizeof(WCHAR
);
231 /* We succeeded, return the token key value */
234 /* Next token starts just after */
241 IN PCSTR CandidateToken
,
242 IN
const PCSTR
* TokenTable
)
246 while (TokenTable
[Index
] && _stricmp(CandidateToken
, TokenTable
[Index
]) != 0)
256 IN PCWSTR CandidateToken
,
257 IN
const PCWSTR
* TokenTable
)
261 while (TokenTable
[Index
] && _wcsicmp(CandidateToken
, TokenTable
[Index
]) != 0)
271 IN PCUNICODE_STRING CandidateToken
,
272 IN
const PCWSTR
* TokenTable
)
278 UNICODE_STRING Token
;
281 while (TokenTable
[Index
])
284 Length
= wcslen(TokenTable
[Index
])*sizeof(WCHAR
);
285 if (RtlCompareMemory(CandidateToken
->Buffer
, TokenTable
[Index
], Length
) == Length
)
288 RtlInitUnicodeString(&Token
, TokenTable
[Index
]);
289 // if (RtlCompareUnicodeString(CandidateToken, &Token, TRUE) == 0)
290 if (RtlEqualUnicodeString(CandidateToken
, &Token
, TRUE
))
303 OUT PUNICODE_STRING NormalizedArcPath
,
310 if (NormalizedArcPath
->MaximumLength
< sizeof(UNICODE_NULL
))
313 *NormalizedArcPath
->Buffer
= UNICODE_NULL
;
314 NormalizedArcPath
->Length
= 0;
316 EndOfArcName
= wcschr(ArcPath
, OBJ_NAME_PATH_SEPARATOR
);
318 EndOfArcName
= ArcPath
+ wcslen(ArcPath
);
320 while ((p
= wcsstr(ArcPath
, L
"()")) && (p
< EndOfArcName
))
323 Status
= RtlStringCbCopyNW(NormalizedArcPath
->Buffer
,
324 NormalizedArcPath
->MaximumLength
,
325 ArcPath
, (p
- ArcPath
) * sizeof(WCHAR
));
327 Status
= RtlStringCbCatNW(NormalizedArcPath
->Buffer
,
328 NormalizedArcPath
->MaximumLength
,
329 ArcPath
, (p
- ArcPath
) * sizeof(WCHAR
));
331 if (!NT_SUCCESS(Status
))
334 Status
= RtlStringCbCatW(NormalizedArcPath
->Buffer
,
335 NormalizedArcPath
->MaximumLength
,
337 if (!NT_SUCCESS(Status
))
340 NormalizedArcPath
->Buffer
+= wcslen(NormalizedArcPath
->Buffer
);
345 Status
= RtlStringCbCatW(NormalizedArcPath
->Buffer
,
346 NormalizedArcPath
->MaximumLength
,
348 if (!NT_SUCCESS(Status
))
351 NormalizedArcPath
->Length
= wcslen(NormalizedArcPath
->Buffer
) * sizeof(WCHAR
);
358 * In input, pointer to an ARC path (NULL-terminated) starting by an
359 * ARC name to be parsed into its different components.
360 * In output, ArcNamePath points to the beginning of the path after
365 IN OUT PCWSTR
* ArcNamePath
,
366 OUT PULONG pAdapterKey
,
367 OUT PULONG pControllerKey
,
368 OUT PULONG pPeripheralKey
,
369 OUT PULONG pPartitionNumber
,
370 OUT PADAPTER_TYPE pAdapterType
,
371 OUT PCONTROLLER_TYPE pControllerType
,
372 OUT PPERIPHERAL_TYPE pPeripheralType
,
373 OUT PBOOLEAN pUseSignature
)
376 WCHAR TokenBuffer
[50];
377 UNICODE_STRING Token
;
382 ULONG PartitionNumber
;
383 ADAPTER_TYPE AdapterType
;
384 CONTROLLER_TYPE ControllerType
;
385 PERIPHERAL_TYPE PeripheralType
;
386 BOOLEAN UseSignature
= FALSE
;
389 * The format of ArcName is:
390 * adapter(www)[controller(xxx)peripheral(yyy)[partition(zzz)][filepath]] ,
391 * where the [filepath] part is not being parsed.
394 RtlInitEmptyUnicodeString(&Token
, TokenBuffer
, sizeof(TokenBuffer
));
398 /* Retrieve the adapter */
399 p
= ArcGetNextTokenU(p
, &Token
, &AdapterKey
);
402 DPRINT1("No adapter specified!\n");
403 return STATUS_OBJECT_PATH_SYNTAX_BAD
;
406 /* Check for the 'signature()' pseudo-adapter, introduced in Windows 2000 */
407 if (_wcsicmp(Token
.Buffer
, L
"signature") == 0)
410 * We've got a signature! Remember this for later, and set the adapter type to SCSI.
411 * We however check that the rest of the ARC path is valid by parsing the other tokens.
412 * AdapterKey stores the disk signature value (that holds in a ULONG).
415 AdapterType
= ScsiAdapter
;
419 /* Check for regular adapters */
420 AdapterType
= (ADAPTER_TYPE
)/*ArcMatchTokenU*/ArcMatchToken_UStr(/*Token.Buffer*/&Token
, AdapterTypes_U
);
421 if (AdapterType
>= AdapterTypeMax
)
423 DPRINT1("Invalid adapter type %wZ\n", &Token
);
424 return STATUS_OBJECT_NAME_INVALID
;
427 /* Check for adapters that don't take any extra controller or peripheral nodes */
428 if (AdapterType
== NetAdapter
|| AdapterType
== RamdiskAdapter
)
431 // return STATUS_OBJECT_PATH_SYNTAX_BAD;
433 if (AdapterType
== NetAdapter
)
435 DPRINT1("%S(%lu) path is not supported!\n", AdapterTypes_U
[AdapterType
], AdapterKey
);
436 return STATUS_NOT_SUPPORTED
;
443 /* Here, we have either an 'eisa', a 'scsi/signature', or a 'multi' adapter */
445 /* Check for a valid controller */
446 p
= ArcGetNextTokenU(p
, &Token
, &ControllerKey
);
449 DPRINT1("%S(%lu) adapter doesn't have a controller!\n", AdapterTypes_U
[AdapterType
], AdapterKey
);
450 return STATUS_OBJECT_PATH_SYNTAX_BAD
;
452 ControllerType
= (CONTROLLER_TYPE
)/*ArcMatchTokenU*/ArcMatchToken_UStr(/*Token.Buffer*/&Token
, ControllerTypes_U
);
453 if (ControllerType
>= ControllerTypeMax
)
455 DPRINT1("Invalid controller type %wZ\n", &Token
);
456 return STATUS_OBJECT_NAME_INVALID
;
459 /* Here the controller can only be either a disk or a CDROM */
462 * Ignore the controller in case we have a 'multi' adapter.
463 * I guess a similar condition holds for the 'eisa' adapter too...
465 * For SignatureAdapter, as similar for ScsiAdapter, the controller key corresponds
466 * to the disk target ID. Note that actually, the implementation just ignores the
467 * target ID, as well as the LUN, and just loops over all the available disks and
468 * searches for the one having the correct signature.
470 if ((AdapterType
== MultiAdapter
/* || AdapterType == EisaAdapter */) && ControllerKey
!= 0)
472 DPRINT1("%S(%lu) adapter with %S(%lu non-zero), ignored!\n",
473 AdapterTypes_U
[AdapterType
], AdapterKey
,
474 ControllerTypes_U
[ControllerType
], ControllerKey
);
479 * Only the 'scsi' adapter supports a direct 'cdrom' controller.
480 * For the others, we need a 'disk' controller to which a 'cdrom' peripheral can talk to.
482 if ((AdapterType
!= ScsiAdapter
) && (ControllerType
== CdRomController
))
484 DPRINT1("%S(%lu) adapter cannot have a CDROM controller!\n", AdapterTypes_U
[AdapterType
], AdapterKey
);
485 return STATUS_OBJECT_PATH_INVALID
;
488 /* Check for a valid peripheral */
489 p
= ArcGetNextTokenU(p
, &Token
, &PeripheralKey
);
492 DPRINT1("%S(%lu)%S(%lu) adapter-controller doesn't have a peripheral!\n",
493 AdapterTypes_U
[AdapterType
], AdapterKey
,
494 ControllerTypes_U
[ControllerType
], ControllerKey
);
495 return STATUS_OBJECT_PATH_SYNTAX_BAD
;
497 PeripheralType
= (PERIPHERAL_TYPE
)/*ArcMatchTokenU*/ArcMatchToken_UStr(/*Token.Buffer*/&Token
, PeripheralTypes_U
);
498 if (PeripheralType
>= PeripheralTypeMax
)
500 DPRINT1("Invalid peripheral type %wZ\n", &Token
);
501 return STATUS_OBJECT_NAME_INVALID
;
505 * If we had a 'cdrom' controller already, the corresponding peripheral can only be 'fdisk'
506 * (see for example the ARC syntax for SCSI CD-ROMs: scsi(x)cdrom(y)fdisk(z) where z == 0).
508 if ((ControllerType
== CdRomController
) && (PeripheralType
!= FDiskPeripheral
))
510 DPRINT1("%S(%lu) controller cannot have a %S(%lu) peripheral! (note that we haven't check whether the adapter was SCSI or not)\n",
511 ControllerTypes_U
[ControllerType
], ControllerKey
,
512 PeripheralTypes_U
[PeripheralType
], PeripheralKey
);
513 return STATUS_OBJECT_PATH_INVALID
;
516 /* For a 'scsi' adapter, the possible peripherals are only 'rdisk' or 'fdisk' */
517 if (AdapterType
== ScsiAdapter
&& !(PeripheralType
== RDiskPeripheral
|| PeripheralType
== FDiskPeripheral
))
519 DPRINT1("%S(%lu)%S(%lu) SCSI adapter-controller has an invalid peripheral %S(%lu) !\n",
520 AdapterTypes_U
[AdapterType
], AdapterKey
,
521 ControllerTypes_U
[ControllerType
], ControllerKey
,
522 PeripheralTypes_U
[PeripheralType
], PeripheralKey
);
523 return STATUS_OBJECT_PATH_INVALID
;
527 if (AdapterType
== SignatureAdapter
&& PeripheralKey
!= 0)
529 DPRINT1("%S(%lu) adapter with %S(%lu non-zero), ignored!\n",
530 AdapterTypes_U
[AdapterType
], AdapterKey
,
531 PeripheralTypes_U
[PeripheralType
], PeripheralKey
);
536 /* Check for the optional 'partition' specifier */
537 q
= ArcGetNextTokenU(p
, &Token
, &PartitionNumber
);
538 if (q
&& _wcsicmp(Token
.Buffer
, L
"partition") == 0)
540 /* We've got a partition! */
546 * Either no other ARC token was found, or we've got something else
547 * (possibly invalid or not)...
552 // TODO: Check the partition number in case of fdisks and cdroms??
555 /* Return the results */
557 *pAdapterKey
= AdapterKey
;
558 *pControllerKey
= ControllerKey
;
559 *pPeripheralKey
= PeripheralKey
;
560 *pPartitionNumber
= PartitionNumber
;
561 *pAdapterType
= AdapterType
;
562 *pControllerType
= ControllerType
;
563 *pPeripheralType
= PeripheralType
;
564 *pUseSignature
= UseSignature
;
566 return STATUS_SUCCESS
;
571 * ARC name (counted string) to be resolved into a NT device name.
572 * The caller should have already delimited it from within an ARC path
573 * (usually by finding where the first path separator appears in the path).
576 * Receives the resolved NT name. The buffer is NULL-terminated.
579 ResolveArcNameNtSymLink(
580 OUT PUNICODE_STRING NtName
,
581 IN PUNICODE_STRING ArcName
)
584 OBJECT_ATTRIBUTES ObjectAttributes
;
585 HANDLE DirectoryHandle
, LinkHandle
;
586 UNICODE_STRING ArcNameDir
;
588 if (NtName
->MaximumLength
< sizeof(UNICODE_NULL
))
589 return STATUS_BUFFER_TOO_SMALL
;
592 *NtName
->Buffer
= UNICODE_NULL
;
596 /* Open the \ArcName object directory */
597 RtlInitUnicodeString(&ArcNameDir
, L
"\\ArcName");
598 InitializeObjectAttributes(&ObjectAttributes
,
600 OBJ_CASE_INSENSITIVE
,
603 Status
= NtOpenDirectoryObject(&DirectoryHandle
,
604 DIRECTORY_ALL_ACCESS
,
606 if (!NT_SUCCESS(Status
))
608 DPRINT1("NtOpenDirectoryObject(%wZ) failed, Status 0x%08lx\n", &ArcNameDir
, Status
);
612 /* Open the ARC name link */
613 InitializeObjectAttributes(&ObjectAttributes
,
615 OBJ_CASE_INSENSITIVE
,
618 Status
= NtOpenSymbolicLinkObject(&LinkHandle
,
619 SYMBOLIC_LINK_ALL_ACCESS
,
622 /* Close the \ArcName object directory handle */
623 NtClose(DirectoryHandle
);
625 /* Check for success */
626 if (!NT_SUCCESS(Status
))
628 DPRINT1("NtOpenSymbolicLinkObject(%wZ) failed, Status 0x%08lx\n", ArcName
, Status
);
632 /* Reserve one WCHAR for the NULL-termination */
633 NtName
->MaximumLength
-= sizeof(UNICODE_NULL
);
635 /* Resolve the link */
636 Status
= NtQuerySymbolicLinkObject(LinkHandle
, NtName
, NULL
);
638 /* Restore the NULL-termination */
639 NtName
->MaximumLength
+= sizeof(UNICODE_NULL
);
641 /* Check for success */
642 if (!NT_SUCCESS(Status
))
644 /* We failed, don't touch NtName */
645 DPRINT1("NtQuerySymbolicLinkObject(%wZ) failed, Status 0x%08lx\n", ArcName
, Status
);
649 /* We succeeded, NULL-terminate NtName */
650 NtName
->Buffer
[NtName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
659 * In input, pointer to an ARC path (NULL-terminated) starting by an
660 * ARC name to be resolved into a NT device name.
661 * In opposition to ResolveArcNameNtSymLink(), the caller does not have
662 * to delimit the ARC name from within an ARC path. The real ARC name is
663 * deduced after parsing the ARC path, and, in output, ArcNamePath points
664 * to the beginning of the path after the ARC name part.
667 * Receives the resolved NT name. The buffer is NULL-terminated.
670 * (Optional) partition list that helps in resolving the paths pointing
674 ResolveArcNameManually(
675 OUT PUNICODE_STRING NtName
,
676 IN OUT PCWSTR
* ArcNamePath
,
677 IN PPARTLIST PartList
)
683 ULONG PartitionNumber
;
684 ADAPTER_TYPE AdapterType
;
685 CONTROLLER_TYPE ControllerType
;
686 PERIPHERAL_TYPE PeripheralType
;
687 BOOLEAN UseSignature
;
689 PDISKENTRY DiskEntry
;
690 PPARTENTRY PartEntry
= NULL
;
692 if (NtName
->MaximumLength
< sizeof(UNICODE_NULL
))
693 return STATUS_BUFFER_TOO_SMALL
;
696 *NtName
->Buffer
= UNICODE_NULL
;
700 /* Parse the ARC path */
701 Status
= ParseArcName(ArcNamePath
,
710 if (!NT_SUCCESS(Status
))
713 // TODO: Check the partition number in case of fdisks and cdroms??
715 /* Check for adapters that don't take any extra controller or peripheral nodes */
716 if (AdapterType
== NetAdapter
|| AdapterType
== RamdiskAdapter
)
718 if (AdapterType
== NetAdapter
)
720 DPRINT1("%S(%lu) path is not supported!\n", AdapterTypes_U
[AdapterType
], AdapterKey
);
721 return STATUS_NOT_SUPPORTED
;
724 Status
= RtlStringCbPrintfW(NtName
->Buffer
, NtName
->MaximumLength
,
725 L
"\\Device\\Ramdisk%lu", AdapterKey
);
728 if (ControllerType
== CdRomController
) // and so, AdapterType == ScsiAdapter and PeripheralType == FDiskPeripheral
730 Status
= RtlStringCbPrintfW(NtName
->Buffer
, NtName
->MaximumLength
,
731 L
"\\Device\\Scsi\\CdRom%lu", ControllerKey
);
734 /* Now, ControllerType == DiskController */
735 if (PeripheralType
== CdRomPeripheral
)
737 Status
= RtlStringCbPrintfW(NtName
->Buffer
, NtName
->MaximumLength
,
738 L
"\\Device\\CdRom%lu", PeripheralKey
);
741 if (PeripheralType
== FDiskPeripheral
)
743 Status
= RtlStringCbPrintfW(NtName
->Buffer
, NtName
->MaximumLength
,
744 L
"\\Device\\Floppy%lu", PeripheralKey
);
747 if (PeripheralType
== RDiskPeripheral
)
751 /* The disk signature is stored in AdapterKey */
752 DiskEntry
= GetDiskBySignature(PartList
, AdapterKey
);
756 DiskEntry
= GetDiskBySCSI(PartList
, AdapterKey
,
757 ControllerKey
, PeripheralKey
);
760 return STATUS_OBJECT_PATH_NOT_FOUND
; // STATUS_NOT_FOUND;
762 if (PartitionNumber
!= 0)
764 PartEntry
= GetPartition(DiskEntry
, PartitionNumber
);
766 return STATUS_OBJECT_PATH_NOT_FOUND
; // STATUS_DEVICE_NOT_PARTITIONED;
767 ASSERT(PartEntry
->DiskEntry
== DiskEntry
);
770 Status
= RtlStringCbPrintfW(NtName
->Buffer
, NtName
->MaximumLength
,
771 L
"\\Device\\Harddisk%lu\\Partition%lu",
772 DiskEntry
->DiskNumber
, PartitionNumber
);
776 if (PeripheralType
== VDiskPeripheral
)
778 // TODO: Check how Win 7+ deals with virtual disks.
779 Status
= RtlStringCbPrintfW(NtName
->Buffer
, NtName
->MaximumLength
,
780 L
"\\Device\\VirtualHarddisk%lu\\Partition%lu",
781 PeripheralKey
, PartitionNumber
);
785 if (!NT_SUCCESS(Status
))
788 return STATUS_SUCCESS
;
794 OUT PUNICODE_STRING NtPath
,
796 IN PPARTLIST PartList OPTIONAL
)
800 UNICODE_STRING ArcName
;
802 /* TODO: We should "normalize" the path, i.e. expand all the xxx() into xxx(0) */
804 if (NtPath
->MaximumLength
< sizeof(UNICODE_NULL
))
807 *NtPath
->Buffer
= UNICODE_NULL
;
811 * - First, check whether the ARC path is already inside \\ArcName
812 * and if so, map it to the corresponding NT path.
813 * - Only then, if we haven't found any ArcName, try to build a
814 * NT path by deconstructing the ARC path, using its disk and
815 * partition numbers. We may use here our disk/partition list.
817 * See also freeldr/arcname.c
819 * Note that it would be nice to maintain a cache of these mappings.
823 * Initialize the ARC name to resolve, by cutting the ARC path at the first
824 * NT path separator. The ARC name therefore ends where the NT path part starts.
826 RtlInitUnicodeString(&ArcName
, ArcPath
);
827 BeginOfPath
= wcschr(ArcName
.Buffer
, OBJ_NAME_PATH_SEPARATOR
);
829 ArcName
.Length
= (ULONG_PTR
)BeginOfPath
- (ULONG_PTR
)ArcName
.Buffer
;
831 /* Resolve the ARC name via NT SymLinks. Note that NtPath is returned NULL-terminated. */
832 Status
= ResolveArcNameNtSymLink(NtPath
, &ArcName
);
833 if (!NT_SUCCESS(Status
))
835 /* We failed, attempt a manual resolution */
836 DPRINT1("ResolveArcNameNtSymLink(ArcName = '%wZ') for ArcPath = '%S' failed, Status 0x%08lx\n", &ArcName
, ArcPath
, Status
);
839 * We failed at directly resolving the ARC path, and we cannot perform
840 * a manual resolution because we don't have any disk/partition list,
841 * we therefore fail here.
845 DPRINT1("PartList == NULL, cannot perform a manual resolution\n");
849 *NtPath
->Buffer
= UNICODE_NULL
;
852 BeginOfPath
= ArcPath
;
853 Status
= ResolveArcNameManually(NtPath
, &BeginOfPath
, PartList
);
854 if (!NT_SUCCESS(Status
))
856 /* We really failed this time, bail out */
857 DPRINT1("ResolveArcNameManually(ArcPath = '%S') failed, Status 0x%08lx\n", ArcPath
, Status
);
863 * We succeeded. Concatenate the rest of the system-specific path. We know the path is going
864 * to be inside the NT namespace, therefore we can use the path string concatenation function
865 * that uses '\\' as the path separator.
867 if (BeginOfPath
&& *BeginOfPath
)
869 Status
= ConcatPaths(NtPath
->Buffer
, NtPath
->MaximumLength
/ sizeof(WCHAR
), 1, BeginOfPath
);
870 if (!NT_SUCCESS(Status
))
872 /* Buffer not large enough, or whatever...: just bail out */
876 NtPath
->Length
= wcslen(NtPath
->Buffer
) * sizeof(WCHAR
);
886 * - First, check whether any of the ARC paths inside \\ArcName
887 * map to the corresponding NT path. If so, we are OK.
888 * - Only then, if we haven't found any ArcName, try to build an
889 * ARC path by deconstructing the NT path, using its disk and
890 * partition numbers. We may use here our disk/partition list.
892 * See also freeldr/arcname.c
894 * Note that it would be nice to maintain a cache of these mappings.