xref: /reactos/base/setup/lib/utils/arcname.c (revision 4561998a)
1 /*
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
6  */
7 /*
8  * References:
9  *
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
17  *
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.
20  */
21 
22 /* INCLUDES *****************************************************************/
23 
24 #include "precomp.h"
25 
26 #include "filesup.h"
27 #include "partlist.h"
28 #include "arcname.h"
29 
30 #define NDEBUG
31 #include <debug.h>
32 
33 
34 /* TYPEDEFS *****************************************************************/
35 
36 /* Supported adapter types */
37 typedef enum _ADAPTER_TYPE
38 {
39     EisaAdapter,
40     ScsiAdapter,
41     MultiAdapter,
42     NetAdapter,
43     RamdiskAdapter,
44     AdapterTypeMax
45 } ADAPTER_TYPE, *PADAPTER_TYPE;
46 const PCSTR AdapterTypes_A[] =
47 {
48     "eisa",
49     "scsi",
50     "multi",
51     "net",
52     "ramdisk",
53     NULL
54 };
55 const PCWSTR AdapterTypes_U[] =
56 {
57     L"eisa",
58     L"scsi",
59     L"multi",
60     L"net",
61     L"ramdisk",
62     NULL
63 };
64 
65 /* Supported controller types */
66 typedef enum _CONTROLLER_TYPE
67 {
68     DiskController,
69     CdRomController,
70     ControllerTypeMax
71 } CONTROLLER_TYPE, *PCONTROLLER_TYPE;
72 const PCSTR ControllerTypes_A[] =
73 {
74     "disk",
75     "cdrom",
76     NULL
77 };
78 const PCWSTR ControllerTypes_U[] =
79 {
80     L"disk",
81     L"cdrom",
82     NULL
83 };
84 
85 /* Supported peripheral types */
86 typedef enum _PERIPHERAL_TYPE
87 {
88 //  VDiskPeripheral,
89     RDiskPeripheral,
90     FDiskPeripheral,
91     CdRomPeripheral,
92     PeripheralTypeMax
93 } PERIPHERAL_TYPE, *PPERIPHERAL_TYPE;
94 const PCSTR PeripheralTypes_A[] =
95 {
96 //  "vdisk", // Enable this when we'll support boot from virtual disks!
97     "rdisk",
98     "fdisk",
99     "cdrom",
100     NULL
101 };
102 const PCWSTR PeripheralTypes_U[] =
103 {
104 //  L"vdisk", // Enable this when we'll support boot from virtual disks!
105     L"rdisk",
106     L"fdisk",
107     L"cdrom",
108     NULL
109 };
110 
111 
112 /* FUNCTIONS ****************************************************************/
113 
114 /* static */ PCSTR
115 ArcGetNextTokenA(
116     IN  PCSTR ArcPath,
117     OUT PANSI_STRING TokenSpecifier,
118     OUT PULONG Key)
119 {
120     NTSTATUS Status;
121     PCSTR p = ArcPath;
122     ULONG SpecifierLength;
123     ULONG KeyValue;
124 
125     /*
126      * We must have a valid "specifier(key)" string, where 'specifier'
127      * cannot be the empty string, and is followed by '('.
128      */
129     p = strchr(p, '(');
130     if (p == NULL)
131         return NULL; /* No '(' found */
132     if (p == ArcPath)
133         return NULL; /* Path starts with '(' and is thus invalid */
134 
135     SpecifierLength = (p - ArcPath) * sizeof(CHAR);
136 
137     /*
138      * The strtoul function skips any leading whitespace.
139      *
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.
143      */
144     // KeyValue = atoi(p);
145     KeyValue = strtoul(p, (PSTR*)&p, 10);
146 
147     /* Skip any trailing whitespace */
148     while (isspace(*p)) ++p;
149 
150     /* The token must terminate with ')' */
151     if (*p != ')')
152         return NULL;
153 #if 0
154     p = strchr(p, ')');
155     if (p == NULL)
156         return NULL;
157 #endif
158 
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))
164         return NULL;
165 
166     TokenSpecifier->Length = strlen(TokenSpecifier->Buffer) * sizeof(CHAR);
167 
168     /* We succeeded, return the token key value */
169     *Key = KeyValue;
170 
171     /* Next token starts just after */
172     return ++p;
173 }
174 
175 static PCWSTR
176 ArcGetNextTokenU(
177     IN  PCWSTR ArcPath,
178     OUT PUNICODE_STRING TokenSpecifier,
179     OUT PULONG Key)
180 {
181     NTSTATUS Status;
182     PCWSTR p = ArcPath;
183     ULONG SpecifierLength;
184     ULONG KeyValue;
185 
186     /*
187      * We must have a valid "specifier(key)" string, where 'specifier'
188      * cannot be the empty string, and is followed by '('.
189      */
190     p = wcschr(p, L'(');
191     if (p == NULL)
192         return NULL; /* No '(' found */
193     if (p == ArcPath)
194         return NULL; /* Path starts with '(' and is thus invalid */
195 
196     SpecifierLength = (p - ArcPath) * sizeof(WCHAR);
197 
198     ++p;
199 
200     /*
201      * The strtoul function skips any leading whitespace.
202      *
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.
206      */
207     // KeyValue = _wtoi(p);
208     KeyValue = wcstoul(p, (PWSTR*)&p, 10);
209 
210     /* Skip any trailing whitespace */
211     while (iswspace(*p)) ++p;
212 
213     /* The token must terminate with ')' */
214     if (*p != L')')
215         return NULL;
216 #if 0
217     p = wcschr(p, L')');
218     if (p == NULL)
219         return NULL;
220 #endif
221 
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))
227         return NULL;
228 
229     TokenSpecifier->Length = wcslen(TokenSpecifier->Buffer) * sizeof(WCHAR);
230 
231     /* We succeeded, return the token key value */
232     *Key = KeyValue;
233 
234     /* Next token starts just after */
235     return ++p;
236 }
237 
238 
239 /* static */ ULONG
240 ArcMatchTokenA(
241     IN PCSTR CandidateToken,
242     IN const PCSTR* TokenTable)
243 {
244     ULONG Index = 0;
245 
246     while (TokenTable[Index] && _stricmp(CandidateToken, TokenTable[Index]) != 0)
247     {
248         ++Index;
249     }
250 
251     return Index;
252 }
253 
254 /* static */ ULONG
255 ArcMatchTokenU(
256     IN PCWSTR CandidateToken,
257     IN const PCWSTR* TokenTable)
258 {
259     ULONG Index = 0;
260 
261     while (TokenTable[Index] && _wcsicmp(CandidateToken, TokenTable[Index]) != 0)
262     {
263         ++Index;
264     }
265 
266     return Index;
267 }
268 
269 static ULONG
270 ArcMatchToken_UStr(
271     IN PCUNICODE_STRING CandidateToken,
272     IN const PCWSTR* TokenTable)
273 {
274     ULONG Index = 0;
275 #if 0
276     SIZE_T Length;
277 #else
278     UNICODE_STRING Token;
279 #endif
280 
281     while (TokenTable[Index])
282     {
283 #if 0
284         Length = wcslen(TokenTable[Index]);
285         if ((Length == CandidateToken->Length / sizeof(WCHAR)) &&
286             (_wcsnicmp(CandidateToken->Buffer, TokenTable[Index], Length) == 0))
287         {
288             break;
289         }
290 #else
291         RtlInitUnicodeString(&Token, TokenTable[Index]);
292         if (RtlEqualUnicodeString(CandidateToken, &Token, TRUE))
293             break;
294 #endif
295 
296         ++Index;
297     }
298 
299     return Index;
300 }
301 
302 
303 BOOLEAN
304 ArcPathNormalize(
305     OUT PUNICODE_STRING NormalizedArcPath,
306     IN  PCWSTR ArcPath)
307 {
308     NTSTATUS Status;
309     PCWSTR EndOfArcName;
310     PCWSTR p;
311 
312     if (NormalizedArcPath->MaximumLength < sizeof(UNICODE_NULL))
313         return FALSE;
314 
315     *NormalizedArcPath->Buffer = UNICODE_NULL;
316     NormalizedArcPath->Length = 0;
317 
318     EndOfArcName = wcschr(ArcPath, OBJ_NAME_PATH_SEPARATOR);
319     if (!EndOfArcName)
320         EndOfArcName = ArcPath + wcslen(ArcPath);
321 
322     while ((p = wcsstr(ArcPath, L"()")) && (p < EndOfArcName))
323     {
324 #if 0
325         Status = RtlStringCbCopyNW(NormalizedArcPath->Buffer,
326                                    NormalizedArcPath->MaximumLength,
327                                    ArcPath, (p - ArcPath) * sizeof(WCHAR));
328 #else
329         Status = RtlStringCbCatNW(NormalizedArcPath->Buffer,
330                                   NormalizedArcPath->MaximumLength,
331                                   ArcPath, (p - ArcPath) * sizeof(WCHAR));
332 #endif
333         if (!NT_SUCCESS(Status))
334             return FALSE;
335 
336         Status = RtlStringCbCatW(NormalizedArcPath->Buffer,
337                                  NormalizedArcPath->MaximumLength,
338                                  L"(0)");
339         if (!NT_SUCCESS(Status))
340             return FALSE;
341 #if 0
342         NormalizedArcPath->Buffer += wcslen(NormalizedArcPath->Buffer);
343 #endif
344         ArcPath = p + 2;
345     }
346 
347     Status = RtlStringCbCatW(NormalizedArcPath->Buffer,
348                              NormalizedArcPath->MaximumLength,
349                              ArcPath);
350     if (!NT_SUCCESS(Status))
351         return FALSE;
352 
353     NormalizedArcPath->Length = wcslen(NormalizedArcPath->Buffer) * sizeof(WCHAR);
354     return TRUE;
355 }
356 
357 
358 /*
359  * ArcNamePath:
360  *      In input, pointer to an ARC path (NULL-terminated) starting by an
361  *      ARC name to be parsed into its different components.
362  *      In output, ArcNamePath points to the beginning of the path after
363  *      the ARC name part.
364  */
365 static NTSTATUS
366 ParseArcName(
367     IN OUT PCWSTR* ArcNamePath,
368     OUT PULONG pAdapterKey,
369     OUT PULONG pControllerKey,
370     OUT PULONG pPeripheralKey,
371     OUT PULONG pPartitionNumber,
372     OUT PADAPTER_TYPE pAdapterType,
373     OUT PCONTROLLER_TYPE pControllerType,
374     OUT PPERIPHERAL_TYPE pPeripheralType,
375     OUT PBOOLEAN pUseSignature)
376 {
377     // NTSTATUS Status;
378     WCHAR TokenBuffer[50];
379     UNICODE_STRING Token;
380     PCWSTR p, q;
381     ULONG AdapterKey = 0;
382     ULONG ControllerKey = 0;
383     ULONG PeripheralKey = 0;
384     ULONG PartitionNumber = 0;
385     ADAPTER_TYPE AdapterType = AdapterTypeMax;
386     CONTROLLER_TYPE ControllerType = ControllerTypeMax;
387     PERIPHERAL_TYPE PeripheralType = PeripheralTypeMax;
388     BOOLEAN UseSignature = FALSE;
389 
390     /*
391      * The format of ArcName is:
392      *    adapter(www)[controller(xxx)peripheral(yyy)[partition(zzz)][filepath]] ,
393      * where the [filepath] part is not being parsed.
394      */
395 
396     RtlInitEmptyUnicodeString(&Token, TokenBuffer, sizeof(TokenBuffer));
397 
398     p = *ArcNamePath;
399 
400     /* Retrieve the adapter */
401     p = ArcGetNextTokenU(p, &Token, &AdapterKey);
402     if (!p)
403     {
404         DPRINT1("No adapter specified!\n");
405         return STATUS_OBJECT_PATH_SYNTAX_BAD;
406     }
407 
408     /* Check for the 'signature()' pseudo-adapter, introduced in Windows 2000 */
409     if (_wcsicmp(Token.Buffer, L"signature") == 0)
410     {
411         /*
412          * We've got a signature! Remember this for later, and set the adapter type to SCSI.
413          * We however check that the rest of the ARC path is valid by parsing the other tokens.
414          * AdapterKey stores the disk signature value (that holds in a ULONG).
415          */
416         UseSignature = TRUE;
417         AdapterType = ScsiAdapter;
418     }
419     else
420     {
421         /* Check for regular adapters */
422         // ArcMatchTokenU(Token.Buffer, AdapterTypes_U);
423         AdapterType = (ADAPTER_TYPE)ArcMatchToken_UStr(&Token, AdapterTypes_U);
424         if (AdapterType >= AdapterTypeMax)
425         {
426             DPRINT1("Invalid adapter type %wZ\n", &Token);
427             return STATUS_OBJECT_NAME_INVALID;
428         }
429 
430         /* Check for adapters that don't take any extra controller or peripheral nodes */
431         if (AdapterType == NetAdapter || AdapterType == RamdiskAdapter)
432         {
433             // if (*p)
434             //     return STATUS_OBJECT_PATH_SYNTAX_BAD;
435 
436             if (AdapterType == NetAdapter)
437             {
438                 DPRINT1("%S(%lu) path is not supported!\n", AdapterTypes_U[AdapterType], AdapterKey);
439                 return STATUS_NOT_SUPPORTED;
440             }
441 
442             goto Quit;
443         }
444     }
445 
446     /* Here, we have either an 'eisa', a 'scsi/signature', or a 'multi' adapter */
447 
448     /* Check for a valid controller */
449     p = ArcGetNextTokenU(p, &Token, &ControllerKey);
450     if (!p)
451     {
452         DPRINT1("%S(%lu) adapter doesn't have a controller!\n", AdapterTypes_U[AdapterType], AdapterKey);
453         return STATUS_OBJECT_PATH_SYNTAX_BAD;
454     }
455     // ArcMatchTokenU(Token.Buffer, ControllerTypes_U);
456     ControllerType = (CONTROLLER_TYPE)ArcMatchToken_UStr(&Token, ControllerTypes_U);
457     if (ControllerType >= ControllerTypeMax)
458     {
459         DPRINT1("Invalid controller type %wZ\n", &Token);
460         return STATUS_OBJECT_NAME_INVALID;
461     }
462 
463     /* Here the controller can only be either a disk or a CDROM */
464 
465     /*
466      * Ignore the controller in case we have a 'multi' adapter.
467      * I guess a similar condition holds for the 'eisa' adapter too...
468      *
469      * For SignatureAdapter, as similar for ScsiAdapter, the controller key corresponds
470      * to the disk target ID. Note that actually, the implementation just ignores the
471      * target ID, as well as the LUN, and just loops over all the available disks and
472      * searches for the one having the correct signature.
473      */
474     if ((AdapterType == MultiAdapter /* || AdapterType == EisaAdapter */) && ControllerKey != 0)
475     {
476         DPRINT1("%S(%lu) adapter with %S(%lu non-zero), ignored!\n",
477                AdapterTypes_U[AdapterType], AdapterKey,
478                ControllerTypes_U[ControllerType], ControllerKey);
479         ControllerKey = 0;
480     }
481 
482     /*
483      * Only the 'scsi' adapter supports a direct 'cdrom' controller.
484      * For the others, we need a 'disk' controller to which a 'cdrom' peripheral can talk to.
485      */
486     if ((AdapterType != ScsiAdapter) && (ControllerType == CdRomController))
487     {
488         DPRINT1("%S(%lu) adapter cannot have a CDROM controller!\n", AdapterTypes_U[AdapterType], AdapterKey);
489         return STATUS_OBJECT_PATH_INVALID;
490     }
491 
492     /* Check for a valid peripheral */
493     p = ArcGetNextTokenU(p, &Token, &PeripheralKey);
494     if (!p)
495     {
496         DPRINT1("%S(%lu)%S(%lu) adapter-controller doesn't have a peripheral!\n",
497                AdapterTypes_U[AdapterType], AdapterKey,
498                ControllerTypes_U[ControllerType], ControllerKey);
499         return STATUS_OBJECT_PATH_SYNTAX_BAD;
500     }
501     // ArcMatchTokenU(Token.Buffer, PeripheralTypes_U);
502     PeripheralType = (PERIPHERAL_TYPE)ArcMatchToken_UStr(&Token, PeripheralTypes_U);
503     if (PeripheralType >= PeripheralTypeMax)
504     {
505         DPRINT1("Invalid peripheral type %wZ\n", &Token);
506         return STATUS_OBJECT_NAME_INVALID;
507     }
508 
509     /*
510      * If we had a 'cdrom' controller already, the corresponding peripheral can only be 'fdisk'
511      * (see for example the ARC syntax for SCSI CD-ROMs: scsi(x)cdrom(y)fdisk(z) where z == 0).
512      */
513     if ((ControllerType == CdRomController) && (PeripheralType != FDiskPeripheral))
514     {
515         DPRINT1("%S(%lu) controller cannot have a %S(%lu) peripheral! (note that we haven't check whether the adapter was SCSI or not)\n",
516                ControllerTypes_U[ControllerType], ControllerKey,
517                PeripheralTypes_U[PeripheralType], PeripheralKey);
518         return STATUS_OBJECT_PATH_INVALID;
519     }
520 
521     /* For a 'scsi' adapter, the possible peripherals are only 'rdisk' or 'fdisk' */
522     if (AdapterType == ScsiAdapter && !(PeripheralType == RDiskPeripheral || PeripheralType == FDiskPeripheral))
523     {
524         DPRINT1("%S(%lu)%S(%lu) SCSI adapter-controller has an invalid peripheral %S(%lu) !\n",
525                AdapterTypes_U[AdapterType], AdapterKey,
526                ControllerTypes_U[ControllerType], ControllerKey,
527                PeripheralTypes_U[PeripheralType], PeripheralKey);
528         return STATUS_OBJECT_PATH_INVALID;
529     }
530 
531 #if 0
532     if (AdapterType == SignatureAdapter && PeripheralKey != 0)
533     {
534         DPRINT1("%S(%lu) adapter with %S(%lu non-zero), ignored!\n",
535                AdapterTypes_U[AdapterType], AdapterKey,
536                PeripheralTypes_U[PeripheralType], PeripheralKey);
537         PeripheralKey = 0;
538     }
539 #endif
540 
541     /* Check for the optional 'partition' specifier */
542     q = ArcGetNextTokenU(p, &Token, &PartitionNumber);
543     if (q && _wcsicmp(Token.Buffer, L"partition") == 0)
544     {
545         /* We've got a partition! */
546         p = q;
547     }
548     else
549     {
550         /*
551          * Either no other ARC token was found, or we've got something else
552          * (possibly invalid or not)...
553          */
554         PartitionNumber = 0;
555     }
556 
557     // TODO: Check the partition number in case of fdisks and cdroms??
558 
559 Quit:
560     /* Return the results */
561     *ArcNamePath      = p;
562     *pAdapterKey      = AdapterKey;
563     *pControllerKey   = ControllerKey;
564     *pPeripheralKey   = PeripheralKey;
565     *pPartitionNumber = PartitionNumber;
566     *pAdapterType     = AdapterType;
567     *pControllerType  = ControllerType;
568     *pPeripheralType  = PeripheralType;
569     *pUseSignature    = UseSignature;
570 
571     return STATUS_SUCCESS;
572 }
573 
574 /*
575  * ArcName:
576  *      ARC name (counted string) to be resolved into a NT device name.
577  *      The caller should have already delimited it from within an ARC path
578  *      (usually by finding where the first path separator appears in the path).
579  *
580  * NtName:
581  *      Receives the resolved NT name. The buffer is NULL-terminated.
582  */
583 static NTSTATUS
584 ResolveArcNameNtSymLink(
585     OUT PUNICODE_STRING NtName,
586     IN  PUNICODE_STRING ArcName)
587 {
588     NTSTATUS Status;
589     OBJECT_ATTRIBUTES ObjectAttributes;
590     HANDLE DirectoryHandle, LinkHandle;
591     UNICODE_STRING ArcNameDir;
592 
593     if (NtName->MaximumLength < sizeof(UNICODE_NULL))
594         return STATUS_BUFFER_TOO_SMALL;
595 
596     /* Open the \ArcName object directory */
597     RtlInitUnicodeString(&ArcNameDir, L"\\ArcName");
598     InitializeObjectAttributes(&ObjectAttributes,
599                                &ArcNameDir,
600                                OBJ_CASE_INSENSITIVE,
601                                NULL,
602                                NULL);
603     Status = NtOpenDirectoryObject(&DirectoryHandle,
604                                    DIRECTORY_ALL_ACCESS,
605                                    &ObjectAttributes);
606     if (!NT_SUCCESS(Status))
607     {
608         DPRINT1("NtOpenDirectoryObject(%wZ) failed, Status 0x%08lx\n", &ArcNameDir, Status);
609         return Status;
610     }
611 
612     /* Open the ARC name link */
613     InitializeObjectAttributes(&ObjectAttributes,
614                                ArcName,
615                                OBJ_CASE_INSENSITIVE,
616                                DirectoryHandle,
617                                NULL);
618     Status = NtOpenSymbolicLinkObject(&LinkHandle,
619                                       SYMBOLIC_LINK_QUERY,
620                                       &ObjectAttributes);
621 
622     /* Close the \ArcName object directory handle */
623     NtClose(DirectoryHandle);
624 
625     /* Check for success */
626     if (!NT_SUCCESS(Status))
627     {
628         DPRINT1("NtOpenSymbolicLinkObject(%wZ) failed, Status 0x%08lx\n", ArcName, Status);
629         return Status;
630     }
631 
632     /* Reserve one WCHAR for the NULL-termination */
633     NtName->MaximumLength -= sizeof(UNICODE_NULL);
634 
635     /* Resolve the link and close its handle */
636     Status = NtQuerySymbolicLinkObject(LinkHandle, NtName, NULL);
637     NtClose(LinkHandle);
638 
639     /* Restore the NULL-termination */
640     NtName->MaximumLength += sizeof(UNICODE_NULL);
641 
642     /* Check for success */
643     if (!NT_SUCCESS(Status))
644     {
645         /* We failed, don't touch NtName */
646         DPRINT1("NtQuerySymbolicLinkObject(%wZ) failed, Status 0x%08lx\n", ArcName, Status);
647     }
648     else
649     {
650         /* We succeeded, NULL-terminate NtName */
651         NtName->Buffer[NtName->Length / sizeof(WCHAR)] = UNICODE_NULL;
652     }
653 
654     return Status;
655 }
656 
657 /*
658  * ArcNamePath:
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.
665  *
666  * NtName:
667  *      Receives the resolved NT name. The buffer is NULL-terminated.
668  *
669  * PartList:
670  *      (Optional) partition list that helps in resolving the paths pointing
671  *      to hard disks.
672  */
673 static NTSTATUS
674 ResolveArcNameManually(
675     OUT PUNICODE_STRING NtName,
676     IN OUT PCWSTR* ArcNamePath,
677     IN  PPARTLIST PartList)
678 {
679     NTSTATUS Status;
680     ULONG AdapterKey;
681     ULONG ControllerKey;
682     ULONG PeripheralKey;
683     ULONG PartitionNumber;
684     ADAPTER_TYPE AdapterType;
685     CONTROLLER_TYPE ControllerType;
686     PERIPHERAL_TYPE PeripheralType;
687     BOOLEAN UseSignature;
688 
689     PDISKENTRY DiskEntry;
690     PPARTENTRY PartEntry = NULL;
691 
692     if (NtName->MaximumLength < sizeof(UNICODE_NULL))
693         return STATUS_BUFFER_TOO_SMALL;
694 
695     /* Parse the ARC path */
696     Status = ParseArcName(ArcNamePath,
697                           &AdapterKey,
698                           &ControllerKey,
699                           &PeripheralKey,
700                           &PartitionNumber,
701                           &AdapterType,
702                           &ControllerType,
703                           &PeripheralType,
704                           &UseSignature);
705     if (!NT_SUCCESS(Status))
706         return Status;
707 
708     // TODO: Check the partition number in case of fdisks and cdroms??
709 
710     /* Check for adapters that don't take any extra controller or peripheral node */
711     if (AdapterType == NetAdapter || AdapterType == RamdiskAdapter)
712     {
713         if (AdapterType == NetAdapter)
714         {
715             DPRINT1("%S(%lu) path is not supported!\n", AdapterTypes_U[AdapterType], AdapterKey);
716             return STATUS_NOT_SUPPORTED;
717         }
718 
719         Status = RtlStringCbPrintfW(NtName->Buffer, NtName->MaximumLength,
720                                     L"\\Device\\Ramdisk%lu", AdapterKey);
721     }
722     else
723     if (ControllerType == CdRomController) // and so, AdapterType == ScsiAdapter and PeripheralType == FDiskPeripheral
724     {
725         Status = RtlStringCbPrintfW(NtName->Buffer, NtName->MaximumLength,
726                                     L"\\Device\\Scsi\\CdRom%lu", ControllerKey);
727     }
728     else
729     /* Now, ControllerType == DiskController */
730     if (PeripheralType == CdRomPeripheral)
731     {
732         Status = RtlStringCbPrintfW(NtName->Buffer, NtName->MaximumLength,
733                                     L"\\Device\\CdRom%lu", PeripheralKey);
734     }
735     else
736     if (PeripheralType == FDiskPeripheral)
737     {
738         Status = RtlStringCbPrintfW(NtName->Buffer, NtName->MaximumLength,
739                                     L"\\Device\\Floppy%lu", PeripheralKey);
740     }
741     else
742     if (PeripheralType == RDiskPeripheral)
743     {
744         if (UseSignature)
745         {
746             /* The disk signature is stored in AdapterKey */
747             DiskEntry = GetDiskBySignature(PartList, AdapterKey);
748         }
749         else
750         {
751             DiskEntry = GetDiskBySCSI(PartList, AdapterKey,
752                                       ControllerKey, PeripheralKey);
753         }
754         if (!DiskEntry)
755             return STATUS_OBJECT_PATH_NOT_FOUND; // STATUS_NOT_FOUND;
756 
757         if (PartitionNumber != 0)
758         {
759             PartEntry = GetPartition(DiskEntry, PartitionNumber);
760             if (!PartEntry)
761                 return STATUS_OBJECT_PATH_NOT_FOUND; // STATUS_DEVICE_NOT_PARTITIONED;
762             ASSERT(PartEntry->DiskEntry == DiskEntry);
763         }
764 
765         Status = RtlStringCbPrintfW(NtName->Buffer, NtName->MaximumLength,
766                                     L"\\Device\\Harddisk%lu\\Partition%lu",
767                                     DiskEntry->DiskNumber, PartitionNumber);
768     }
769 #if 0 // FIXME: Not implemented yet!
770     else
771     if (PeripheralType == VDiskPeripheral)
772     {
773         // TODO: Check how Win 7+ deals with virtual disks.
774         Status = RtlStringCbPrintfW(NtName->Buffer, NtName->MaximumLength,
775                                     L"\\Device\\VirtualHarddisk%lu\\Partition%lu",
776                                     PeripheralKey, PartitionNumber);
777     }
778 #endif
779 
780     if (!NT_SUCCESS(Status))
781     {
782         /* Returned NtName is invalid, so zero it out */
783         *NtName->Buffer = UNICODE_NULL;
784         NtName->Length = 0;
785 
786         return Status;
787     }
788 
789     /* Update NtName length */
790     NtName->Length = wcslen(NtName->Buffer) * sizeof(WCHAR);
791 
792     return STATUS_SUCCESS;
793 }
794 
795 
796 BOOLEAN
797 ArcPathToNtPath(
798     OUT PUNICODE_STRING NtPath,
799     IN  PCWSTR ArcPath,
800     IN  PPARTLIST PartList OPTIONAL)
801 {
802     NTSTATUS Status;
803     PCWSTR BeginOfPath;
804     UNICODE_STRING ArcName;
805 
806     /* TODO: We should "normalize" the path, i.e. expand all the xxx() into xxx(0) */
807 
808     if (NtPath->MaximumLength < sizeof(UNICODE_NULL))
809         return FALSE;
810 
811     *NtPath->Buffer = UNICODE_NULL;
812     NtPath->Length = 0;
813 
814     /*
815      * - First, check whether the ARC path is already inside \\ArcName
816      *   and if so, map it to the corresponding NT path.
817      * - Only then, if we haven't found any ArcName, try to build a
818      *   NT path by deconstructing the ARC path, using its disk and
819      *   partition numbers. We may use here our disk/partition list.
820      *
821      * See also freeldr/arcname.c
822      *
823      * Note that it would be nice to maintain a cache of these mappings.
824      */
825 
826     /*
827      * Initialize the ARC name to resolve, by cutting the ARC path at the first
828      * NT path separator. The ARC name therefore ends where the NT path part starts.
829      */
830     RtlInitUnicodeString(&ArcName, ArcPath);
831     BeginOfPath = wcschr(ArcName.Buffer, OBJ_NAME_PATH_SEPARATOR);
832     if (BeginOfPath)
833         ArcName.Length = (ULONG_PTR)BeginOfPath - (ULONG_PTR)ArcName.Buffer;
834 
835     /* Resolve the ARC name via NT SymLinks. Note that NtPath is returned NULL-terminated. */
836     Status = ResolveArcNameNtSymLink(NtPath, &ArcName);
837     if (!NT_SUCCESS(Status))
838     {
839         /* We failed, attempt a manual resolution */
840         DPRINT1("ResolveArcNameNtSymLink(ArcName = '%wZ') for ArcPath = '%S' failed, Status 0x%08lx\n", &ArcName, ArcPath, Status);
841 
842         /*
843          * We failed at directly resolving the ARC path, and we cannot perform
844          * a manual resolution because we don't have any disk/partition list,
845          * we therefore fail here.
846          */
847         if (!PartList)
848         {
849             DPRINT1("PartList == NULL, cannot perform a manual resolution\n");
850             return FALSE;
851         }
852 
853         *NtPath->Buffer = UNICODE_NULL;
854         NtPath->Length = 0;
855 
856         BeginOfPath = ArcPath;
857         Status = ResolveArcNameManually(NtPath, &BeginOfPath, PartList);
858         if (!NT_SUCCESS(Status))
859         {
860             /* We really failed this time, bail out */
861             DPRINT1("ResolveArcNameManually(ArcPath = '%S') failed, Status 0x%08lx\n", ArcPath, Status);
862             return FALSE;
863         }
864     }
865 
866     /*
867      * We succeeded. Concatenate the rest of the system-specific path. We know the path is going
868      * to be inside the NT namespace, therefore we can use the path string concatenation function
869      * that uses '\\' as the path separator.
870      */
871     if (BeginOfPath && *BeginOfPath)
872     {
873         Status = ConcatPaths(NtPath->Buffer, NtPath->MaximumLength / sizeof(WCHAR), 1, BeginOfPath);
874         if (!NT_SUCCESS(Status))
875         {
876             /* Buffer not large enough, or whatever...: just bail out */
877             return FALSE;
878         }
879     }
880     NtPath->Length = wcslen(NtPath->Buffer) * sizeof(WCHAR);
881 
882     return TRUE;
883 }
884 
885 #if 0 // FIXME: Not implemented yet!
886 PWSTR
887 NtPathToArcPath(
888     IN PWSTR NtPath)
889 {
890     /*
891      * - First, check whether any of the ARC paths inside \\ArcName
892      *   map to the corresponding NT path. If so, we are OK.
893      * - Only then, if we haven't found any ArcName, try to build an
894      *   ARC path by deconstructing the NT path, using its disk and
895      *   partition numbers. We may use here our disk/partition list.
896      *
897      * See also freeldr/arcname.c
898      *
899      * Note that it would be nice to maintain a cache of these mappings.
900      */
901 }
902 #endif
903 
904 /* EOF */
905