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
IsGlobalDeviceMap(HANDLE DirectoryHandle,PBOOLEAN IsGlobal)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
FindSymbolicLinkEntry(PWSTR NameToFind,PWSTR NamesList,DWORD TotalEntries,PBOOLEAN Found)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
DefineDosDeviceA(DWORD dwFlags,LPCSTR lpDeviceName,LPCSTR lpTargetPath)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
DefineDosDeviceW(DWORD dwFlags,LPCWSTR lpDeviceName,LPCWSTR lpTargetPath)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
QueryDosDeviceA(LPCSTR lpDeviceName,LPSTR lpTargetPath,DWORD ucchMax)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
QueryDosDeviceW(LPCWSTR lpDeviceName,LPWSTR lpTargetPath,DWORD ucchMax)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