1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: dll/win32/kernel32/client/dosdev.c 5 * PURPOSE: Dos device functions 6 * PROGRAMMER: Ariadne (ariadne@xs4all.nl) 7 * Pierre Schweitzer 8 * UPDATE HISTORY: 9 * Created 01/11/98 10 */ 11 12 /* INCLUDES ******************************************************************/ 13 14 #include <k32.h> 15 16 #define NDEBUG 17 #include <debug.h> 18 #include <dbt.h> 19 DEBUG_CHANNEL(kernel32file); 20 21 /* FUNCTIONS *****************************************************************/ 22 23 /* 24 * @implemented 25 */ 26 NTSTATUS 27 IsGlobalDeviceMap( 28 HANDLE DirectoryHandle, 29 PBOOLEAN IsGlobal) 30 { 31 NTSTATUS Status; 32 DWORD ReturnLength; 33 UNICODE_STRING GlobalString; 34 OBJECT_NAME_INFORMATION NameInfo, *PNameInfo; 35 36 /* We need both parameters */ 37 if (DirectoryHandle == 0 || IsGlobal == NULL) 38 { 39 return STATUS_INVALID_PARAMETER; 40 } 41 42 PNameInfo = NULL; 43 _SEH2_TRY 44 { 45 /* Query handle information */ 46 Status = NtQueryObject(DirectoryHandle, 47 ObjectNameInformation, 48 &NameInfo, 49 0, 50 &ReturnLength); 51 /* Only failure we tolerate is length mismatch */ 52 if (NT_SUCCESS(Status) || Status == STATUS_INFO_LENGTH_MISMATCH) 53 { 54 /* Allocate big enough buffer */ 55 PNameInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, ReturnLength); 56 if (PNameInfo == NULL) 57 { 58 Status = STATUS_NO_MEMORY; 59 _SEH2_LEAVE; 60 } 61 62 /* Query again handle information */ 63 Status = NtQueryObject(DirectoryHandle, 64 ObjectNameInformation, 65 PNameInfo, 66 ReturnLength, 67 &ReturnLength); 68 69 /* 70 * If it succeed, check we have Global?? 71 * If so, return success 72 */ 73 if (NT_SUCCESS(Status)) 74 { 75 RtlInitUnicodeString(&GlobalString, L"\\GLOBAL??"); 76 *IsGlobal = RtlEqualUnicodeString(&GlobalString, &PNameInfo->Name, FALSE); 77 Status = STATUS_SUCCESS; 78 } 79 } 80 } 81 _SEH2_FINALLY 82 { 83 if (PNameInfo != NULL) 84 { 85 RtlFreeHeap(RtlGetProcessHeap(), 0, PNameInfo); 86 } 87 } 88 _SEH2_END; 89 90 return Status; 91 } 92 93 /* 94 * @implemented 95 */ 96 DWORD 97 FindSymbolicLinkEntry( 98 PWSTR NameToFind, 99 PWSTR NamesList, 100 DWORD TotalEntries, 101 PBOOLEAN Found) 102 { 103 WCHAR Current; 104 DWORD Entries; 105 PWSTR PartialNamesList; 106 107 /* We need all parameters to be set */ 108 if (NameToFind == NULL || NamesList == NULL || Found == NULL) 109 { 110 return ERROR_INVALID_PARAMETER; 111 } 112 113 /* Assume failure */ 114 *Found = FALSE; 115 116 /* If no entries, job done, nothing found */ 117 if (TotalEntries == 0) 118 { 119 return ERROR_SUCCESS; 120 } 121 122 /* Start browsing the names list */ 123 Entries = 0; 124 PartialNamesList = NamesList; 125 /* As long as we didn't find the name... */ 126 while (wcscmp(NameToFind, PartialNamesList) != 0) 127 { 128 /* We chomped an entry! */ 129 ++Entries; 130 131 /* We're out of entries, bail out not to overrun */ 132 if (Entries > TotalEntries) 133 { 134 /* 135 * Even though we found nothing, 136 * the function ran fine 137 */ 138 return ERROR_SUCCESS; 139 } 140 141 /* Jump to the next string */ 142 do 143 { 144 Current = *PartialNamesList; 145 ++PartialNamesList; 146 } while (Current != UNICODE_NULL); 147 } 148 149 /* 150 * We're here because the loop stopped: 151 * it means we found the name in the list 152 */ 153 *Found = TRUE; 154 return ERROR_SUCCESS; 155 } 156 157 /* 158 * @implemented 159 */ 160 BOOL 161 WINAPI 162 DefineDosDeviceA( 163 DWORD dwFlags, 164 LPCSTR lpDeviceName, 165 LPCSTR lpTargetPath 166 ) 167 { 168 BOOL Result; 169 NTSTATUS Status; 170 ANSI_STRING AnsiString; 171 PWSTR TargetPathBuffer; 172 UNICODE_STRING TargetPathU; 173 PUNICODE_STRING DeviceNameU; 174 175 /* Convert DeviceName using static unicode string */ 176 RtlInitAnsiString(&AnsiString, lpDeviceName); 177 DeviceNameU = &NtCurrentTeb()->StaticUnicodeString; 178 Status = RtlAnsiStringToUnicodeString(DeviceNameU, &AnsiString, FALSE); 179 if (!NT_SUCCESS(Status)) 180 { 181 /* 182 * If the static unicode string is too small, 183 * it's because the name is too long... 184 * so, return appropriate status! 185 */ 186 if (Status == STATUS_BUFFER_OVERFLOW) 187 { 188 SetLastError(ERROR_FILENAME_EXCED_RANGE); 189 return FALSE; 190 } 191 192 BaseSetLastNTError(Status); 193 return FALSE; 194 } 195 196 /* Convert target path if existing */ 197 if (lpTargetPath != NULL) 198 { 199 RtlInitAnsiString(&AnsiString, lpTargetPath); 200 Status = RtlAnsiStringToUnicodeString(&TargetPathU, &AnsiString, TRUE); 201 if (!NT_SUCCESS(Status)) 202 { 203 BaseSetLastNTError(Status); 204 return FALSE; 205 } 206 207 TargetPathBuffer = TargetPathU.Buffer; 208 } 209 else 210 { 211 TargetPathBuffer = NULL; 212 } 213 214 /* Call W */ 215 Result = DefineDosDeviceW(dwFlags, DeviceNameU->Buffer, TargetPathBuffer); 216 217 /* Free target path if allocated */ 218 if (TargetPathBuffer != NULL) 219 { 220 RtlFreeUnicodeString(&TargetPathU); 221 } 222 223 return Result; 224 } 225 226 227 /* 228 * @implemented 229 */ 230 BOOL 231 WINAPI 232 DefineDosDeviceW( 233 DWORD dwFlags, 234 LPCWSTR lpDeviceName, 235 LPCWSTR lpTargetPath 236 ) 237 { 238 ULONG ArgumentCount; 239 ULONG BufferSize; 240 BASE_API_MESSAGE ApiMessage; 241 PBASE_DEFINE_DOS_DEVICE DefineDosDeviceRequest = &ApiMessage.Data.DefineDosDeviceRequest; 242 PCSR_CAPTURE_BUFFER CaptureBuffer; 243 UNICODE_STRING NtTargetPathU; 244 UNICODE_STRING DeviceNameU; 245 HANDLE hUser32; 246 DEV_BROADCAST_VOLUME dbcv; 247 DWORD dwRecipients; 248 typedef long (WINAPI *BSM_type)(DWORD, LPDWORD, UINT, WPARAM, LPARAM); 249 BSM_type BSM_ptr; 250 BOOLEAN LUIDDeviceMapsEnabled; 251 WCHAR Letter; 252 WPARAM wParam; 253 254 /* Get status about local device mapping */ 255 LUIDDeviceMapsEnabled = BaseStaticServerData->LUIDDeviceMapsEnabled; 256 257 /* Validate input & flags */ 258 if ((dwFlags & 0xFFFFFFE0) || 259 ((dwFlags & DDD_EXACT_MATCH_ON_REMOVE) && 260 !(dwFlags & DDD_REMOVE_DEFINITION)) || 261 (lpTargetPath == NULL && !(dwFlags & (DDD_LUID_BROADCAST_DRIVE | DDD_REMOVE_DEFINITION))) || 262 ((dwFlags & DDD_LUID_BROADCAST_DRIVE) && 263 (lpDeviceName == NULL || lpTargetPath != NULL || dwFlags & (DDD_NO_BROADCAST_SYSTEM | DDD_EXACT_MATCH_ON_REMOVE | DDD_RAW_TARGET_PATH) || !LUIDDeviceMapsEnabled))) 264 { 265 SetLastError(ERROR_INVALID_PARAMETER); 266 return FALSE; 267 } 268 269 /* Initialize device unicode string to ease its use */ 270 RtlInitUnicodeString(&DeviceNameU, lpDeviceName); 271 272 /* The buffer for CSR call will contain it */ 273 BufferSize = DeviceNameU.MaximumLength; 274 ArgumentCount = 1; 275 276 /* If we don't have target path, use empty string */ 277 if (lpTargetPath == NULL) 278 { 279 RtlInitUnicodeString(&NtTargetPathU, NULL); 280 } 281 else 282 { 283 /* Else, use it raw if asked to */ 284 if (dwFlags & DDD_RAW_TARGET_PATH) 285 { 286 RtlInitUnicodeString(&NtTargetPathU, lpTargetPath); 287 } 288 else 289 { 290 /* Otherwise, use it converted */ 291 if (!RtlDosPathNameToNtPathName_U(lpTargetPath, 292 &NtTargetPathU, 293 NULL, 294 NULL)) 295 { 296 WARN("RtlDosPathNameToNtPathName_U() failed\n"); 297 BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID); 298 return FALSE; 299 } 300 } 301 302 /* This target path will be the second arg */ 303 ArgumentCount = 2; 304 BufferSize += NtTargetPathU.MaximumLength; 305 } 306 307 /* Allocate the capture buffer for our strings */ 308 CaptureBuffer = CsrAllocateCaptureBuffer(ArgumentCount, 309 BufferSize); 310 if (CaptureBuffer == NULL) 311 { 312 if (!(dwFlags & DDD_RAW_TARGET_PATH)) 313 { 314 RtlFreeUnicodeString(&NtTargetPathU); 315 } 316 317 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 318 return FALSE; 319 } 320 321 /* Set the flags */ 322 DefineDosDeviceRequest->Flags = dwFlags; 323 324 /* Allocate a buffer for the device name */ 325 DefineDosDeviceRequest->DeviceName.MaximumLength = CsrAllocateMessagePointer(CaptureBuffer, 326 DeviceNameU.MaximumLength, 327 (PVOID*)&DefineDosDeviceRequest->DeviceName.Buffer); 328 /* And copy it while upcasing it */ 329 RtlUpcaseUnicodeString(&DefineDosDeviceRequest->DeviceName, &DeviceNameU, FALSE); 330 331 /* If we have a target path, copy it too, and free it if allocated */ 332 if (NtTargetPathU.Length != 0) 333 { 334 DefineDosDeviceRequest->TargetPath.MaximumLength = CsrAllocateMessagePointer(CaptureBuffer, 335 NtTargetPathU.MaximumLength, 336 (PVOID*)&DefineDosDeviceRequest->TargetPath.Buffer); 337 RtlCopyUnicodeString(&DefineDosDeviceRequest->TargetPath, &NtTargetPathU); 338 339 if (!(dwFlags & DDD_RAW_TARGET_PATH)) 340 { 341 RtlFreeUnicodeString(&NtTargetPathU); 342 } 343 } 344 /* Otherwise, null initialize the string */ 345 else 346 { 347 RtlInitUnicodeString(&DefineDosDeviceRequest->TargetPath, NULL); 348 } 349 350 /* Finally, call the server */ 351 CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, 352 CaptureBuffer, 353 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepDefineDosDevice), 354 sizeof(*DefineDosDeviceRequest)); 355 CsrFreeCaptureBuffer(CaptureBuffer); 356 357 /* Return failure if any */ 358 if (!NT_SUCCESS(ApiMessage.Status)) 359 { 360 WARN("CsrClientCallServer() failed (Status %lx)\n", ApiMessage.Status); 361 BaseSetLastNTError(ApiMessage.Status); 362 return FALSE; 363 } 364 365 /* Here is the success path, we will always return true */ 366 367 /* Should broadcast the event? Only do if not denied and if drive letter */ 368 if (!(dwFlags & DDD_NO_BROADCAST_SYSTEM) && 369 DeviceNameU.Length == 2 * sizeof(WCHAR) && 370 DeviceNameU.Buffer[1] == L':') 371 { 372 /* Make sure letter is valid and there are no local device mappings */ 373 Letter = RtlUpcaseUnicodeChar(DeviceNameU.Buffer[0]) - L'A'; 374 if (Letter < 26 && !LUIDDeviceMapsEnabled) 375 { 376 /* Rely on user32 for broadcasting */ 377 hUser32 = LoadLibraryW(L"user32.dll"); 378 if (hUser32 != 0) 379 { 380 /* Get the function pointer */ 381 BSM_ptr = (BSM_type)GetProcAddress(hUser32, "BroadcastSystemMessageW"); 382 if (BSM_ptr) 383 { 384 /* Set our target */ 385 dwRecipients = BSM_APPLICATIONS; 386 387 /* And initialize our structure */ 388 dbcv.dbcv_size = sizeof(DEV_BROADCAST_VOLUME); 389 dbcv.dbcv_devicetype = DBT_DEVTYP_VOLUME; 390 dbcv.dbcv_reserved = 0; 391 392 /* Set the volume which had the event */ 393 dbcv.dbcv_unitmask = 1 << Letter; 394 dbcv.dbcv_flags = DBTF_NET; 395 396 /* And properly set the event (removal or arrival?) */ 397 wParam = (dwFlags & DDD_REMOVE_DEFINITION) ? DBT_DEVICEREMOVECOMPLETE : DBT_DEVICEARRIVAL; 398 399 /* And broadcast! */ 400 BSM_ptr(BSF_SENDNOTIFYMESSAGE | BSF_FLUSHDISK, 401 &dwRecipients, 402 WM_DEVICECHANGE, 403 wParam, 404 (LPARAM)&dbcv); 405 } 406 407 /* We're done! */ 408 FreeLibrary(hUser32); 409 } 410 } 411 } 412 413 return TRUE; 414 } 415 416 417 /* 418 * @implemented 419 */ 420 DWORD 421 WINAPI 422 QueryDosDeviceA( 423 LPCSTR lpDeviceName, 424 LPSTR lpTargetPath, 425 DWORD ucchMax 426 ) 427 { 428 NTSTATUS Status; 429 USHORT CurrentPosition; 430 ANSI_STRING AnsiString; 431 UNICODE_STRING TargetPathU; 432 PUNICODE_STRING DeviceNameU; 433 DWORD RetLength, CurrentLength, Length; 434 PWSTR DeviceNameBuffer, TargetPathBuffer; 435 436 /* If we want a specific device name, convert it */ 437 if (lpDeviceName != NULL) 438 { 439 /* Convert DeviceName using static unicode string */ 440 RtlInitAnsiString(&AnsiString, lpDeviceName); 441 DeviceNameU = &NtCurrentTeb()->StaticUnicodeString; 442 Status = RtlAnsiStringToUnicodeString(DeviceNameU, &AnsiString, FALSE); 443 if (!NT_SUCCESS(Status)) 444 { 445 /* 446 * If the static unicode string is too small, 447 * it's because the name is too long... 448 * so, return appropriate status! 449 */ 450 if (Status == STATUS_BUFFER_OVERFLOW) 451 { 452 SetLastError(ERROR_FILENAME_EXCED_RANGE); 453 return FALSE; 454 } 455 456 BaseSetLastNTError(Status); 457 return FALSE; 458 } 459 460 DeviceNameBuffer = DeviceNameU->Buffer; 461 } 462 else 463 { 464 DeviceNameBuffer = NULL; 465 } 466 467 /* Allocate the output buffer for W call */ 468 TargetPathBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, ucchMax * sizeof(WCHAR)); 469 if (TargetPathBuffer == NULL) 470 { 471 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 472 return 0; 473 } 474 475 /* Call W */ 476 Length = QueryDosDeviceW(DeviceNameBuffer, TargetPathBuffer, ucchMax); 477 /* We'll return that length in case of a success */ 478 RetLength = Length; 479 480 /* Handle the case where we would fill output buffer completly */ 481 if (Length != 0 && Length == ucchMax) 482 { 483 /* This will be our work length (but not the one we return) */ 484 --Length; 485 /* Already 0 the last char */ 486 lpTargetPath[Length] = ANSI_NULL; 487 } 488 489 /* If we had an output, start the convert loop */ 490 if (Length != 0) 491 { 492 /* 493 * We'll have to loop because TargetPathBuffer may contain 494 * several strings (NULL separated) 495 * We'll start at position 0 496 */ 497 CurrentPosition = 0; 498 while (CurrentPosition < Length) 499 { 500 /* Get the maximum length */ 501 CurrentLength = min(Length - CurrentPosition, MAXUSHORT / 2); 502 503 /* Initialize our output string */ 504 AnsiString.Length = 0; 505 AnsiString.MaximumLength = CurrentLength + sizeof(ANSI_NULL); 506 AnsiString.Buffer = &lpTargetPath[CurrentPosition]; 507 508 /* Initialize input string that will be converted */ 509 TargetPathU.Length = CurrentLength * sizeof(WCHAR); 510 TargetPathU.MaximumLength = CurrentLength * sizeof(WCHAR) + sizeof(UNICODE_NULL); 511 TargetPathU.Buffer = &TargetPathBuffer[CurrentPosition]; 512 513 /* Convert to ANSI */ 514 Status = RtlUnicodeStringToAnsiString(&AnsiString, &TargetPathU, FALSE); 515 if (!NT_SUCCESS(Status)) 516 { 517 BaseSetLastNTError(Status); 518 /* In case of a failure, forget about everything */ 519 RetLength = 0; 520 521 goto Leave; 522 } 523 524 /* Move to the next string */ 525 CurrentPosition += CurrentLength; 526 } 527 } 528 529 Leave: 530 /* Free our intermediate buffer and leave */ 531 RtlFreeHeap(RtlGetProcessHeap(), 0, TargetPathBuffer); 532 533 return RetLength; 534 } 535 536 537 /* 538 * @implemented 539 */ 540 DWORD 541 WINAPI 542 QueryDosDeviceW( 543 LPCWSTR lpDeviceName, 544 LPWSTR lpTargetPath, 545 DWORD ucchMax 546 ) 547 { 548 PWSTR Ptr; 549 PVOID Buffer; 550 NTSTATUS Status; 551 USHORT i, TotalEntries; 552 UNICODE_STRING UnicodeString; 553 OBJECT_ATTRIBUTES ObjectAttributes; 554 HANDLE DirectoryHandle, DeviceHandle; 555 BOOLEAN IsGlobal, GlobalNeeded, Found; 556 POBJECT_DIRECTORY_INFORMATION DirInfo; 557 OBJECT_DIRECTORY_INFORMATION NullEntry = {{0}}; 558 ULONG ReturnLength, NameLength, Length, Context, BufferLength; 559 560 /* Open the '\??' directory */ 561 RtlInitUnicodeString(&UnicodeString, L"\\??"); 562 InitializeObjectAttributes(&ObjectAttributes, 563 &UnicodeString, 564 OBJ_CASE_INSENSITIVE, 565 NULL, 566 NULL); 567 Status = NtOpenDirectoryObject(&DirectoryHandle, 568 DIRECTORY_QUERY, 569 &ObjectAttributes); 570 if (!NT_SUCCESS(Status)) 571 { 572 WARN("NtOpenDirectoryObject() failed (Status %lx)\n", Status); 573 BaseSetLastNTError(Status); 574 return 0; 575 } 576 577 Buffer = NULL; 578 _SEH2_TRY 579 { 580 if (lpDeviceName != NULL) 581 { 582 /* Open the lpDeviceName link object */ 583 RtlInitUnicodeString(&UnicodeString, lpDeviceName); 584 InitializeObjectAttributes(&ObjectAttributes, 585 &UnicodeString, 586 OBJ_CASE_INSENSITIVE, 587 DirectoryHandle, 588 NULL); 589 Status = NtOpenSymbolicLinkObject(&DeviceHandle, 590 SYMBOLIC_LINK_QUERY, 591 &ObjectAttributes); 592 if (!NT_SUCCESS(Status)) 593 { 594 WARN("NtOpenSymbolicLinkObject() failed (Status %lx)\n", Status); 595 _SEH2_LEAVE; 596 } 597 598 /* 599 * Make sure we don't overrun the output buffer, so convert our DWORD 600 * size to USHORT size properly 601 */ 602 Length = (ucchMax <= MAXULONG / sizeof(WCHAR)) ? (ucchMax * sizeof(WCHAR)) : MAXULONG; 603 604 /* Query link target */ 605 UnicodeString.Length = 0; 606 UnicodeString.MaximumLength = Length <= MAXUSHORT ? Length : MAXUSHORT; 607 UnicodeString.Buffer = lpTargetPath; 608 609 ReturnLength = 0; 610 Status = NtQuerySymbolicLinkObject(DeviceHandle, 611 &UnicodeString, 612 &ReturnLength); 613 NtClose(DeviceHandle); 614 if (!NT_SUCCESS(Status)) 615 { 616 WARN("NtQuerySymbolicLinkObject() failed (Status %lx)\n", Status); 617 _SEH2_LEAVE; 618 } 619 620 TRACE("ReturnLength: %lu\n", ReturnLength); 621 TRACE("TargetLength: %hu\n", UnicodeString.Length); 622 TRACE("Target: '%wZ'\n", &UnicodeString); 623 624 Length = ReturnLength / sizeof(WCHAR); 625 /* Make sure we null terminate output buffer */ 626 if (Length == 0 || lpTargetPath[Length - 1] != UNICODE_NULL) 627 { 628 if (Length >= ucchMax) 629 { 630 TRACE("Buffer is too small\n"); 631 Status = STATUS_BUFFER_TOO_SMALL; 632 _SEH2_LEAVE; 633 } 634 635 /* Append null-character */ 636 lpTargetPath[Length] = UNICODE_NULL; 637 Length++; 638 } 639 640 if (Length < ucchMax) 641 { 642 /* Append null-character */ 643 lpTargetPath[Length] = UNICODE_NULL; 644 Length++; 645 } 646 647 _SEH2_LEAVE; 648 } 649 650 /* 651 * If LUID device maps are enabled, 652 * ?? may not point to BaseNamedObjects 653 * It may only be local DOS namespace. 654 * And thus, it might be required to browse 655 * Global?? for global devices 656 */ 657 GlobalNeeded = FALSE; 658 if (BaseStaticServerData->LUIDDeviceMapsEnabled) 659 { 660 /* Assume ?? == Global?? */ 661 IsGlobal = TRUE; 662 /* Check if it's the case */ 663 Status = IsGlobalDeviceMap(DirectoryHandle, &IsGlobal); 664 if (NT_SUCCESS(Status) && !IsGlobal) 665 { 666 /* It's not, we'll have to browse Global?? too! */ 667 GlobalNeeded = TRUE; 668 } 669 } 670 671 /* 672 * Make sure we don't overrun the output buffer, so convert our DWORD 673 * size to USHORT size properly 674 */ 675 BufferLength = (ucchMax <= MAXULONG / sizeof(WCHAR)) ? (ucchMax * sizeof(WCHAR)) : MAXULONG; 676 Length = 0; 677 Ptr = lpTargetPath; 678 679 Context = 0; 680 TotalEntries = 0; 681 682 /* 683 * We'll query all entries at once, with a rather big buffer 684 * If it's too small, we'll grow it by 2. 685 * Limit the number of attempts to 3. 686 */ 687 for (i = 0; i < 3; ++i) 688 { 689 /* Allocate the query buffer */ 690 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); 691 if (Buffer == NULL) 692 { 693 Status = STATUS_INSUFFICIENT_RESOURCES; 694 _SEH2_LEAVE; 695 } 696 697 /* Perform the query */ 698 Status = NtQueryDirectoryObject(DirectoryHandle, 699 Buffer, 700 BufferLength, 701 FALSE, 702 TRUE, 703 &Context, 704 &ReturnLength); 705 /* Only failure accepted is: no more entries */ 706 if (!NT_SUCCESS(Status)) 707 { 708 if (Status != STATUS_NO_MORE_ENTRIES) 709 { 710 _SEH2_LEAVE; 711 } 712 713 /* 714 * Which is a success! But break out, 715 * it means our query returned no results 716 * so, nothing to parse. 717 */ 718 Status = STATUS_SUCCESS; 719 break; 720 } 721 722 /* In case we had them all, start browsing for devices */ 723 if (Status != STATUS_MORE_ENTRIES) 724 { 725 DirInfo = Buffer; 726 727 /* Loop until we find the nul entry (terminating entry) */ 728 while (TRUE) 729 { 730 /* It's an entry full of zeroes */ 731 if (RtlCompareMemory(&NullEntry, DirInfo, sizeof(NullEntry)) == sizeof(NullEntry)) 732 { 733 break; 734 } 735 736 /* Only handle symlinks */ 737 if (!wcscmp(DirInfo->TypeName.Buffer, L"SymbolicLink")) 738 { 739 TRACE("Name: '%wZ'\n", &DirInfo->Name); 740 741 /* Get name length in chars to only comparisons */ 742 NameLength = DirInfo->Name.Length / sizeof(WCHAR); 743 744 /* Make sure we don't overrun output buffer */ 745 if (Length > ucchMax || 746 NameLength > ucchMax - Length || 747 ucchMax - NameLength - Length < sizeof(WCHAR)) 748 { 749 Status = STATUS_BUFFER_TOO_SMALL; 750 _SEH2_LEAVE; 751 } 752 753 /* Copy and NULL terminate string */ 754 memcpy(Ptr, DirInfo->Name.Buffer, DirInfo->Name.Length); 755 Ptr[NameLength] = UNICODE_NULL; 756 757 Ptr += (NameLength + 1); 758 Length += (NameLength + 1); 759 760 /* 761 * Keep the entries count, in case we would have to 762 * handle GLOBAL?? too 763 */ 764 ++TotalEntries; 765 } 766 767 /* Move to the next entry */ 768 ++DirInfo; 769 } 770 771 /* 772 * No need to loop again here, we got all the entries 773 * Note: we don't free the buffer here, because we may 774 * need it for GLOBAL??, so we save a few cycles here. 775 */ 776 break; 777 } 778 779 /* Failure path here, we'll need bigger buffer */ 780 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 781 Buffer = NULL; 782 783 /* We can't have bigger than that one, so leave */ 784 if (BufferLength == MAXULONG) 785 { 786 break; 787 } 788 789 /* Prevent any overflow while computing new size */ 790 if (MAXULONG - BufferLength < BufferLength) 791 { 792 BufferLength = MAXULONG; 793 } 794 else 795 { 796 BufferLength *= 2; 797 } 798 } 799 800 /* 801 * Out of the hot loop, but with more entries left? 802 * that's an error case, leave here! 803 */ 804 if (Status == STATUS_MORE_ENTRIES) 805 { 806 Status = STATUS_BUFFER_TOO_SMALL; 807 _SEH2_LEAVE; 808 } 809 810 /* Now, if we had to handle GLOBAL??, go for it! */ 811 if (BaseStaticServerData->LUIDDeviceMapsEnabled && NT_SUCCESS(Status) && GlobalNeeded) 812 { 813 NtClose(DirectoryHandle); 814 DirectoryHandle = 0; 815 816 RtlInitUnicodeString(&UnicodeString, L"\\GLOBAL??"); 817 InitializeObjectAttributes(&ObjectAttributes, 818 &UnicodeString, 819 OBJ_CASE_INSENSITIVE, 820 NULL, 821 NULL); 822 Status = NtOpenDirectoryObject(&DirectoryHandle, 823 DIRECTORY_QUERY, 824 &ObjectAttributes); 825 if (!NT_SUCCESS(Status)) 826 { 827 WARN("NtOpenDirectoryObject() failed (Status %lx)\n", Status); 828 _SEH2_LEAVE; 829 } 830 831 /* 832 * We'll query all entries at once, with a rather big buffer 833 * If it's too small, we'll grow it by 2. 834 * Limit the number of attempts to 3. 835 */ 836 for (i = 0; i < 3; ++i) 837 { 838 /* If we had no buffer from previous attempt, allocate one */ 839 if (Buffer == NULL) 840 { 841 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); 842 if (Buffer == NULL) 843 { 844 Status = STATUS_INSUFFICIENT_RESOURCES; 845 _SEH2_LEAVE; 846 } 847 } 848 849 /* Perform the query */ 850 Status = NtQueryDirectoryObject(DirectoryHandle, 851 Buffer, 852 BufferLength, 853 FALSE, 854 TRUE, 855 &Context, 856 &ReturnLength); 857 /* Only failure accepted is: no more entries */ 858 if (!NT_SUCCESS(Status)) 859 { 860 if (Status != STATUS_NO_MORE_ENTRIES) 861 { 862 _SEH2_LEAVE; 863 } 864 865 /* 866 * Which is a success! But break out, 867 * it means our query returned no results 868 * so, nothing to parse. 869 */ 870 Status = STATUS_SUCCESS; 871 break; 872 } 873 874 /* In case we had them all, start browsing for devices */ 875 if (Status != STATUS_MORE_ENTRIES) 876 { 877 DirInfo = Buffer; 878 879 /* Loop until we find the nul entry (terminating entry) */ 880 while (TRUE) 881 { 882 /* It's an entry full of zeroes */ 883 if (RtlCompareMemory(&NullEntry, DirInfo, sizeof(NullEntry)) == sizeof(NullEntry)) 884 { 885 break; 886 } 887 888 /* Only handle symlinks */ 889 if (!wcscmp(DirInfo->TypeName.Buffer, L"SymbolicLink")) 890 { 891 TRACE("Name: '%wZ'\n", &DirInfo->Name); 892 893 /* 894 * Now, we previously already browsed ??, and we 895 * don't want to devices twice, so we'll check 896 * the output buffer for duplicates. 897 * We'll add our entry only if we don't have already 898 * returned it. 899 */ 900 if (FindSymbolicLinkEntry(DirInfo->Name.Buffer, 901 lpTargetPath, 902 TotalEntries, 903 &Found) == ERROR_SUCCESS && 904 !Found) 905 { 906 /* Get name length in chars to only comparisons */ 907 NameLength = DirInfo->Name.Length / sizeof(WCHAR); 908 909 /* Make sure we don't overrun output buffer */ 910 if (Length > ucchMax || 911 NameLength > ucchMax - Length || 912 ucchMax - NameLength - Length < sizeof(WCHAR)) 913 { 914 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 915 NtClose(DirectoryHandle); 916 BaseSetLastNTError(STATUS_BUFFER_TOO_SMALL); 917 return 0; 918 } 919 920 /* Copy and NULL terminate string */ 921 memcpy(Ptr, DirInfo->Name.Buffer, DirInfo->Name.Length); 922 Ptr[NameLength] = UNICODE_NULL; 923 924 Ptr += (NameLength + 1); 925 Length += (NameLength + 1); 926 } 927 } 928 929 /* Move to the next entry */ 930 ++DirInfo; 931 } 932 933 /* No need to loop again here, we got all the entries */ 934 break; 935 } 936 937 /* Failure path here, we'll need bigger buffer */ 938 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 939 Buffer = NULL; 940 941 /* We can't have bigger than that one, so leave */ 942 if (BufferLength == MAXULONG) 943 { 944 break; 945 } 946 947 /* Prevent any overflow while computing new size */ 948 if (MAXULONG - BufferLength < BufferLength) 949 { 950 BufferLength = MAXULONG; 951 } 952 else 953 { 954 BufferLength *= 2; 955 } 956 } 957 958 /* 959 * Out of the hot loop, but with more entries left? 960 * that's an error case, leave here! 961 */ 962 if (Status == STATUS_MORE_ENTRIES) 963 { 964 Status = STATUS_BUFFER_TOO_SMALL; 965 _SEH2_LEAVE; 966 } 967 } 968 969 /* If we failed somewhere, just leave */ 970 if (!NT_SUCCESS(Status)) 971 { 972 _SEH2_LEAVE; 973 } 974 975 /* If we returned no entries, time to write the empty string */ 976 if (Length == 0) 977 { 978 /* Unless output buffer is too small! */ 979 if (ucchMax <= 0) 980 { 981 Status = STATUS_BUFFER_TOO_SMALL; 982 _SEH2_LEAVE; 983 } 984 985 /* Emptry string is one char (terminator!) */ 986 *Ptr = UNICODE_NULL; 987 ++Ptr; 988 Length = 1; 989 } 990 991 /* 992 * If we have enough room, we need to double terminate the buffer: 993 * that's a MULTI_SZ buffer, its end is marked by double NULL. 994 * One was already added during the "copy string" process. 995 * If we don't have enough room: that's a failure case. 996 */ 997 if (Length < ucchMax) 998 { 999 *Ptr = UNICODE_NULL; 1000 ++Ptr; 1001 } 1002 else 1003 { 1004 Status = STATUS_BUFFER_TOO_SMALL; 1005 } 1006 } 1007 _SEH2_FINALLY 1008 { 1009 if (DirectoryHandle != 0) 1010 { 1011 NtClose(DirectoryHandle); 1012 } 1013 1014 if (Buffer != NULL) 1015 { 1016 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 1017 } 1018 1019 if (!NT_SUCCESS(Status)) 1020 { 1021 Length = 0; 1022 BaseSetLastNTError(Status); 1023 } 1024 } 1025 _SEH2_END; 1026 1027 return Length; 1028 } 1029 1030 /* EOF */ 1031