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 /* Supported adapter types */
35 typedef enum _ADAPTER_TYPE
43 } ADAPTER_TYPE
, *PADAPTER_TYPE
;
44 const PCSTR AdapterTypes_A
[] =
53 const PCWSTR AdapterTypes_U
[] =
63 /* Supported controller types */
64 typedef enum _CONTROLLER_TYPE
69 } CONTROLLER_TYPE
, *PCONTROLLER_TYPE
;
70 const PCSTR ControllerTypes_A
[] =
76 const PCWSTR ControllerTypes_U
[] =
83 /* Supported peripheral types */
84 typedef enum _PERIPHERAL_TYPE
91 } PERIPHERAL_TYPE
, *PPERIPHERAL_TYPE
;
92 const PCSTR PeripheralTypes_A
[] =
94 // "vdisk", // Enable this when we'll support boot from virtual disks!
100 const PCWSTR PeripheralTypes_U
[] =
102 // L"vdisk", // Enable this when we'll support boot from virtual disks!
110 /* FUNCTIONS ****************************************************************/
115 OUT PANSI_STRING TokenSpecifier
,
120 ULONG SpecifierLength
;
124 * We must have a valid "specifier(key)" string, where 'specifier'
125 * cannot be the empty string, and is followed by '('.
129 return NULL
; /* No '(' found */
131 return NULL
; /* Path starts with '(' and is thus invalid */
133 SpecifierLength
= (p
- ArcPath
) * sizeof(CHAR
);
136 * The strtoul function skips any leading whitespace.
138 * Note that if the token is "specifier()" then strtoul won't perform
139 * any conversion and return 0, therefore effectively making the token
140 * equivalent to "specifier(0)", as it should be.
142 // KeyValue = atoi(p);
143 KeyValue
= strtoul(p
, (PSTR
*)&p
, 10);
145 /* Skip any trailing whitespace */
146 while (isspace(*p
)) ++p
;
148 /* The token must terminate with ')' */
157 /* We should have succeeded, copy the token specifier in the buffer */
158 Status
= RtlStringCbCopyNA(TokenSpecifier
->Buffer
,
159 TokenSpecifier
->MaximumLength
,
160 ArcPath
, SpecifierLength
);
161 if (!NT_SUCCESS(Status
))
164 TokenSpecifier
->Length
= strlen(TokenSpecifier
->Buffer
) * sizeof(CHAR
);
166 /* We succeeded, return the token key value */
169 /* Next token starts just after */
176 OUT PUNICODE_STRING TokenSpecifier
,
181 ULONG SpecifierLength
;
185 * We must have a valid "specifier(key)" string, where 'specifier'
186 * cannot be the empty string, and is followed by '('.
190 return NULL
; /* No '(' found */
192 return NULL
; /* Path starts with '(' and is thus invalid */
194 SpecifierLength
= (p
- ArcPath
) * sizeof(WCHAR
);
199 * The strtoul function skips any leading whitespace.
201 * Note that if the token is "specifier()" then strtoul won't perform
202 * any conversion and return 0, therefore effectively making the token
203 * equivalent to "specifier(0)", as it should be.
205 // KeyValue = _wtoi(p);
206 KeyValue
= wcstoul(p
, (PWSTR
*)&p
, 10);
208 /* Skip any trailing whitespace */
209 while (iswspace(*p
)) ++p
;
211 /* The token must terminate with ')' */
220 /* We should have succeeded, copy the token specifier in the buffer */
221 Status
= RtlStringCbCopyNW(TokenSpecifier
->Buffer
,
222 TokenSpecifier
->MaximumLength
,
223 ArcPath
, SpecifierLength
);
224 if (!NT_SUCCESS(Status
))
227 TokenSpecifier
->Length
= wcslen(TokenSpecifier
->Buffer
) * sizeof(WCHAR
);
229 /* We succeeded, return the token key value */
232 /* Next token starts just after */
239 IN PCSTR CandidateToken
,
240 IN
const PCSTR
* TokenTable
)
244 while (TokenTable
[Index
] && _stricmp(CandidateToken
, TokenTable
[Index
]) != 0)
254 IN PCWSTR CandidateToken
,
255 IN
const PCWSTR
* TokenTable
)
259 while (TokenTable
[Index
] && _wcsicmp(CandidateToken
, TokenTable
[Index
]) != 0)
269 IN PCUNICODE_STRING CandidateToken
,
270 IN
const PCWSTR
* TokenTable
)
276 UNICODE_STRING Token
;
279 while (TokenTable
[Index
])
282 Length
= wcslen(TokenTable
[Index
])*sizeof(WCHAR
);
283 if (RtlCompareMemory(CandidateToken
->Buffer
, TokenTable
[Index
], Length
) == Length
)
286 RtlInitUnicodeString(&Token
, TokenTable
[Index
]);
287 // if (RtlCompareUnicodeString(CandidateToken, &Token, TRUE) == 0)
288 if (RtlEqualUnicodeString(CandidateToken
, &Token
, TRUE
))
301 OUT PUNICODE_STRING NormalizedArcPath
,
308 if (NormalizedArcPath
->MaximumLength
< sizeof(UNICODE_NULL
))
311 *NormalizedArcPath
->Buffer
= UNICODE_NULL
;
312 NormalizedArcPath
->Length
= 0;
314 EndOfArcName
= wcschr(ArcPath
, OBJ_NAME_PATH_SEPARATOR
);
316 EndOfArcName
= ArcPath
+ wcslen(ArcPath
);
318 while ((p
= wcsstr(ArcPath
, L
"()")) && (p
< EndOfArcName
))
321 Status
= RtlStringCbCopyNW(NormalizedArcPath
->Buffer
,
322 NormalizedArcPath
->MaximumLength
,
323 ArcPath
, (p
- ArcPath
) * sizeof(WCHAR
));
325 Status
= RtlStringCbCatNW(NormalizedArcPath
->Buffer
,
326 NormalizedArcPath
->MaximumLength
,
327 ArcPath
, (p
- ArcPath
) * sizeof(WCHAR
));
329 if (!NT_SUCCESS(Status
))
332 Status
= RtlStringCbCatW(NormalizedArcPath
->Buffer
,
333 NormalizedArcPath
->MaximumLength
,
335 if (!NT_SUCCESS(Status
))
338 NormalizedArcPath
->Buffer
+= wcslen(NormalizedArcPath
->Buffer
);
343 Status
= RtlStringCbCatW(NormalizedArcPath
->Buffer
,
344 NormalizedArcPath
->MaximumLength
,
346 if (!NT_SUCCESS(Status
))
349 NormalizedArcPath
->Length
= wcslen(NormalizedArcPath
->Buffer
) * sizeof(WCHAR
);
356 * ARC name (counted string) to be resolved into a NT device name.
357 * The caller should have already delimited it from within an ARC path
358 * (usually by finding where the first path separator appears in the path).
361 * Receives the resolved NT name. The buffer is NULL-terminated.
364 ResolveArcNameNtSymLink(
365 OUT PUNICODE_STRING NtName
,
366 IN PUNICODE_STRING ArcName
)
369 OBJECT_ATTRIBUTES ObjectAttributes
;
370 HANDLE DirectoryHandle
, LinkHandle
;
371 UNICODE_STRING ArcNameDir
;
373 if (NtName
->MaximumLength
< sizeof(UNICODE_NULL
))
374 return STATUS_BUFFER_TOO_SMALL
;
377 *NtName
->Buffer
= UNICODE_NULL
;
381 /* Open the \ArcName object directory */
382 RtlInitUnicodeString(&ArcNameDir
, L
"\\ArcName");
383 InitializeObjectAttributes(&ObjectAttributes
,
385 OBJ_CASE_INSENSITIVE
,
388 Status
= NtOpenDirectoryObject(&DirectoryHandle
,
389 DIRECTORY_ALL_ACCESS
,
391 if (!NT_SUCCESS(Status
))
393 DPRINT1("NtOpenDirectoryObject(%wZ) failed, Status 0x%08lx\n", &ArcNameDir
, Status
);
397 /* Open the ARC name link */
398 InitializeObjectAttributes(&ObjectAttributes
,
400 OBJ_CASE_INSENSITIVE
,
403 Status
= NtOpenSymbolicLinkObject(&LinkHandle
,
404 SYMBOLIC_LINK_ALL_ACCESS
,
407 /* Close the \ArcName object directory handle */
408 NtClose(DirectoryHandle
);
410 /* Check for success */
411 if (!NT_SUCCESS(Status
))
413 DPRINT1("NtOpenSymbolicLinkObject(%wZ) failed, Status 0x%08lx\n", ArcName
, Status
);
417 /* Reserve one WCHAR for the NULL-termination */
418 NtName
->MaximumLength
-= sizeof(UNICODE_NULL
);
420 /* Resolve the link */
421 Status
= NtQuerySymbolicLinkObject(LinkHandle
, NtName
, NULL
);
423 /* Restore the NULL-termination */
424 NtName
->MaximumLength
+= sizeof(UNICODE_NULL
);
426 /* Check for success */
427 if (!NT_SUCCESS(Status
))
429 /* We failed, don't touch NtName */
430 DPRINT1("NtQuerySymbolicLinkObject(%wZ) failed, Status 0x%08lx\n", ArcName
, Status
);
434 /* We succeeded, NULL-terminate NtName */
435 NtName
->Buffer
[NtName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
444 * In input, pointer to an ARC path (NULL-terminated) starting by an ARC name
445 * to be resolved into a NT device name.
446 * In opposition to ResolveArcNameNtSymLink(), the caller does not have to
447 * delimit the ARC name from within an ARC path. The real ARC name is deduced
448 * after parsing the ARC path, and, in output, ArcNamePath points to the
449 * beginning of the path after the ARC name part.
452 * Receives the resolved NT name. The buffer is NULL-terminated.
455 * (Optional) partition list that helps in resolving the paths pointing
459 ResolveArcNameManually(
460 OUT PUNICODE_STRING NtName
,
461 IN OUT PCWSTR
* ArcNamePath
,
462 IN PPARTLIST PartList OPTIONAL
)
465 WCHAR TokenBuffer
[50];
466 UNICODE_STRING Token
;
471 ULONG PartitionNumber
;
472 ADAPTER_TYPE AdapterType
;
473 CONTROLLER_TYPE ControllerType
;
474 PERIPHERAL_TYPE PeripheralType
;
475 BOOLEAN UseSignature
= FALSE
;
477 PDISKENTRY DiskEntry
;
478 PPARTENTRY PartEntry
= NULL
;
480 if (NtName
->MaximumLength
< sizeof(UNICODE_NULL
))
481 return STATUS_BUFFER_TOO_SMALL
;
484 *NtName
->Buffer
= UNICODE_NULL
;
489 * The format of ArcName is:
490 * adapter(www)[controller(xxx)peripheral(yyy)[partition(zzz)][filepath]] ,
491 * where the [filepath] part is not being parsed.
494 RtlInitEmptyUnicodeString(&Token
, TokenBuffer
, sizeof(TokenBuffer
));
498 /* Retrieve the adapter */
499 p
= ArcGetNextTokenU(p
, &Token
, &AdapterKey
);
502 DPRINT1("No adapter specified!\n");
503 return STATUS_OBJECT_PATH_SYNTAX_BAD
;
506 /* Check for the 'signature()' pseudo-adapter, introduced in Windows 2000 */
507 if (_wcsicmp(Token
.Buffer
, L
"signature") == 0)
510 * We've got a signature! Remember this for later, and set the adapter type to SCSI.
511 * We however check that the rest of the ARC path is valid by parsing the other tokens.
512 * AdapterKey stores the disk signature value (that holds in a ULONG).
515 AdapterType
= ScsiAdapter
;
519 /* Check for regular adapters */
520 AdapterType
= (ADAPTER_TYPE
)/*ArcMatchTokenU*/ArcMatchToken_UStr(/*Token.Buffer*/&Token
, AdapterTypes_U
);
521 if (AdapterType
>= AdapterTypeMax
)
523 DPRINT1("Invalid adapter type %wZ\n", &Token
);
524 return STATUS_OBJECT_NAME_INVALID
;
527 /* Check for adapters that don't take any extra controller or peripheral nodes */
528 if (AdapterType
== NetAdapter
|| AdapterType
== RamdiskAdapter
)
531 // return STATUS_OBJECT_PATH_SYNTAX_BAD;
533 if (AdapterType
== NetAdapter
)
535 DPRINT1("%S(%lu) path is not supported!\n", AdapterTypes_U
[AdapterType
], AdapterKey
);
536 return STATUS_NOT_SUPPORTED
;
539 Status
= RtlStringCbPrintfW(NtName
->Buffer
, NtName
->MaximumLength
,
540 L
"\\Device\\Ramdisk%lu", AdapterKey
);
545 /* Here, we have either an 'eisa', a 'scsi/signature', or a 'multi' adapter */
547 /* Check for a valid controller */
548 p
= ArcGetNextTokenU(p
, &Token
, &ControllerKey
);
551 DPRINT1("%S(%lu) adapter doesn't have a controller!\n", AdapterTypes_U
[AdapterType
], AdapterKey
);
552 return STATUS_OBJECT_PATH_SYNTAX_BAD
;
554 ControllerType
= (CONTROLLER_TYPE
)/*ArcMatchTokenU*/ArcMatchToken_UStr(/*Token.Buffer*/&Token
, ControllerTypes_U
);
555 if (ControllerType
>= ControllerTypeMax
)
557 DPRINT1("Invalid controller type %wZ\n", &Token
);
558 return STATUS_OBJECT_NAME_INVALID
;
561 /* Here the controller can only be either a disk or a CDROM */
564 * Ignore the controller in case we have a 'multi' adapter.
565 * I guess a similar condition holds for the 'eisa' adapter too...
567 * For SignatureAdapter, as similar for ScsiAdapter, the controller key corresponds
568 * to the disk target ID. Note that actually, the implementation just ignores the
569 * target ID, as well as the LUN, and just loops over all the available disks and
570 * searches for the one having the correct signature.
572 if ((AdapterType
== MultiAdapter
/* || AdapterType == EisaAdapter */) && ControllerKey
!= 0)
574 DPRINT1("%S(%lu) adapter with %S(%lu non-zero), ignored!\n",
575 AdapterTypes_U
[AdapterType
], AdapterKey
,
576 ControllerTypes_U
[ControllerType
], ControllerKey
);
581 * Only the 'scsi' adapter supports a direct 'cdrom' controller.
582 * For the others, we need a 'disk' controller to which a 'cdrom' peripheral can talk to.
584 if ((AdapterType
!= ScsiAdapter
) && (ControllerType
== CdRomController
))
586 DPRINT1("%S(%lu) adapter cannot have a CDROM controller!\n", AdapterTypes_U
[AdapterType
], AdapterKey
);
587 return STATUS_OBJECT_PATH_INVALID
;
590 /* Check for a valid peripheral */
591 p
= ArcGetNextTokenU(p
, &Token
, &PeripheralKey
);
594 DPRINT1("%S(%lu)%S(%lu) adapter-controller doesn't have a peripheral!\n",
595 AdapterTypes_U
[AdapterType
], AdapterKey
,
596 ControllerTypes_U
[ControllerType
], ControllerKey
);
597 return STATUS_OBJECT_PATH_SYNTAX_BAD
;
599 PeripheralType
= (PERIPHERAL_TYPE
)/*ArcMatchTokenU*/ArcMatchToken_UStr(/*Token.Buffer*/&Token
, PeripheralTypes_U
);
600 if (PeripheralType
>= PeripheralTypeMax
)
602 DPRINT1("Invalid peripheral type %wZ\n", &Token
);
603 return STATUS_OBJECT_NAME_INVALID
;
607 * If we had a 'cdrom' controller already, the corresponding peripheral can only be 'fdisk'
608 * (see for example the ARC syntax for SCSI CD-ROMs: scsi(x)cdrom(y)fdisk(z) where z == 0).
610 if ((ControllerType
== CdRomController
) && (PeripheralType
!= FDiskPeripheral
))
612 DPRINT1("%S(%lu) controller cannot have a %S(%lu) peripheral! (note that we haven't check whether the adapter was SCSI or not)\n",
613 ControllerTypes_U
[ControllerType
], ControllerKey
,
614 PeripheralTypes_U
[PeripheralType
], PeripheralKey
);
615 return STATUS_OBJECT_PATH_INVALID
;
618 /* For a 'scsi' adapter, the possible peripherals are only 'rdisk' or 'fdisk' */
619 if (AdapterType
== ScsiAdapter
&& !(PeripheralType
== RDiskPeripheral
|| PeripheralType
== FDiskPeripheral
))
621 DPRINT1("%S(%lu)%S(%lu) SCSI adapter-controller has an invalid peripheral %S(%lu) !\n",
622 AdapterTypes_U
[AdapterType
], AdapterKey
,
623 ControllerTypes_U
[ControllerType
], ControllerKey
,
624 PeripheralTypes_U
[PeripheralType
], PeripheralKey
);
625 return STATUS_OBJECT_PATH_INVALID
;
629 if (AdapterType
== SignatureAdapter
&& PeripheralKey
!= 0)
631 DPRINT1("%S(%lu) adapter with %S(%lu non-zero), ignored!\n",
632 AdapterTypes_U
[AdapterType
], AdapterKey
,
633 PeripheralTypes_U
[PeripheralType
], PeripheralKey
);
638 /* Check for the optional 'partition' specifier */
639 q
= ArcGetNextTokenU(p
, &Token
, &PartitionNumber
);
640 if (q
&& _wcsicmp(Token
.Buffer
, L
"partition") == 0)
642 /* We've got a partition! */
648 * Either no other ARC token was found, or we've got something else
649 * (possibly invalid or not)...
655 // TODO: Check the partition number in case of fdisks and cdroms??
658 if (ControllerType
== CdRomController
) // and so, AdapterType == ScsiAdapter and PeripheralType == FDiskPeripheral
660 Status
= RtlStringCbPrintfW(NtName
->Buffer
, NtName
->MaximumLength
,
661 L
"\\Device\\Scsi\\CdRom%lu", ControllerKey
);
664 /* Now, ControllerType == DiskController */
665 if (PeripheralType
== CdRomPeripheral
)
667 Status
= RtlStringCbPrintfW(NtName
->Buffer
, NtName
->MaximumLength
,
668 L
"\\Device\\CdRom%lu", PeripheralKey
);
671 if (PeripheralType
== FDiskPeripheral
)
673 Status
= RtlStringCbPrintfW(NtName
->Buffer
, NtName
->MaximumLength
,
674 L
"\\Device\\Floppy%lu", PeripheralKey
);
677 if (PeripheralType
== RDiskPeripheral
)
681 /* The disk signature is stored in AdapterKey */
682 DiskEntry
= GetDiskBySignature(PartList
, AdapterKey
);
686 DiskEntry
= GetDiskBySCSI(PartList
, AdapterKey
,
687 ControllerKey
, PeripheralKey
);
690 return STATUS_OBJECT_PATH_NOT_FOUND
; // STATUS_NOT_FOUND;
692 if (PartitionNumber
!= 0)
694 PartEntry
= GetPartition(DiskEntry
, PartitionNumber
);
696 return STATUS_OBJECT_PATH_NOT_FOUND
; // STATUS_DEVICE_NOT_PARTITIONED;
697 ASSERT(PartEntry
->DiskEntry
== DiskEntry
);
700 Status
= RtlStringCbPrintfW(NtName
->Buffer
, NtName
->MaximumLength
,
701 L
"\\Device\\Harddisk%lu\\Partition%lu",
702 DiskEntry
->DiskNumber
, PartitionNumber
);
706 if (PeripheralType
== VDiskPeripheral
)
708 // TODO: Check how Win 7+ deals with virtual disks.
709 Status
= RtlStringCbPrintfW(NtName
->Buffer
, NtName
->MaximumLength
,
710 L
"\\Device\\VirtualHarddisk%lu\\Partition%lu",
711 PeripheralKey
, PartitionNumber
);
716 if (!NT_SUCCESS(Status
))
720 return STATUS_SUCCESS
;
726 OUT PUNICODE_STRING NtPath
,
728 IN PPARTLIST PartList OPTIONAL
)
732 UNICODE_STRING ArcName
;
734 /* TODO: We should "normalize" the path, i.e. expand all the xxx() into xxx(0) */
736 if (NtPath
->MaximumLength
< sizeof(UNICODE_NULL
))
739 *NtPath
->Buffer
= UNICODE_NULL
;
743 * - First, check whether the ARC path is already inside \\ArcName
744 * and if so, map it to the corresponding NT path.
745 * - Only then, if we haven't found any ArcName, try to build a
746 * NT path by deconstructing the ARC path, using its disk and
747 * partition numbers. We may use here our disk/partition list.
749 * See also freeldr/arcname.c
751 * Note that it would be nice to maintain a cache of these mappings.
755 * Initialize the ARC name to resolve, by cutting the ARC path at the first
756 * NT path separator. The ARC name therefore ends where the NT path part starts.
758 RtlInitUnicodeString(&ArcName
, ArcPath
);
759 BeginOfPath
= wcschr(ArcName
.Buffer
, OBJ_NAME_PATH_SEPARATOR
);
761 ArcName
.Length
= (ULONG_PTR
)BeginOfPath
- (ULONG_PTR
)ArcName
.Buffer
;
763 /* Resolve the ARC name via NT SymLinks. Note that NtPath is returned NULL-terminated. */
764 Status
= ResolveArcNameNtSymLink(NtPath
, &ArcName
);
765 if (!NT_SUCCESS(Status
))
767 /* We failed, attempt a manual resolution */
768 DPRINT1("ResolveArcNameNtSymLink(ArcName = '%wZ') for ArcPath = '%S' failed, Status 0x%08lx\n", &ArcName
, ArcPath
, Status
);
771 * We failed at directly resolving the ARC path, and we cannot perform
772 * a manual resolution because we don't have any disk/partition list,
773 * we therefore fail here.
777 DPRINT1("PartList == NULL, cannot perform a manual resolution\n");
781 *NtPath
->Buffer
= UNICODE_NULL
;
784 BeginOfPath
= ArcPath
;
785 Status
= ResolveArcNameManually(NtPath
, &BeginOfPath
, PartList
);
786 if (!NT_SUCCESS(Status
))
788 /* We really failed this time, bail out */
789 DPRINT1("ResolveArcNameManually(ArcPath = '%S') failed, Status 0x%08lx\n", ArcPath
, Status
);
795 * We succeeded. Concatenate the rest of the system-specific path. We know the path is going
796 * to be inside the NT namespace, therefore we can use the path string concatenation function
797 * that uses '\\' as the path separator.
799 if (BeginOfPath
&& *BeginOfPath
)
801 Status
= ConcatPaths(NtPath
->Buffer
, NtPath
->MaximumLength
/ sizeof(WCHAR
), 1, BeginOfPath
);
802 if (!NT_SUCCESS(Status
))
804 /* Buffer not large enough, or whatever...: just bail out */
808 NtPath
->Length
= wcslen(NtPath
->Buffer
) * sizeof(WCHAR
);
818 * - First, check whether any of the ARC paths inside \\ArcName
819 * map to the corresponding NT path. If so, we are OK.
820 * - Only then, if we haven't found any ArcName, try to build an
821 * ARC path by deconstructing the NT path, using its disk and
822 * partition numbers. We may use here our disk/partition list.
824 * See also freeldr/arcname.c
826 * Note that it would be nice to maintain a cache of these mappings.