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 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 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 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 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 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 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 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 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 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 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