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