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