e59c291b67fba0d838d35008f404ad23724cad80
[reactos.git] / dll / win32 / kernel32 / client / file / volume.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: dll/win32/kernel32/client/file/volume.c
5 * PURPOSE: File volume functions
6 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
7 * Erik Bos, Alexandre Julliard :
8 * GetLogicalDriveStringsA,
9 * GetLogicalDriveStringsW, GetLogicalDrives
10 * Pierre Schweitzer (pierre@reactos.org)
11 * UPDATE HISTORY:
12 * Created 01/11/98
13 */
14 //WINE copyright notice:
15 /*
16 * DOS drives handling functions
17 *
18 * Copyright 1993 Erik Bos
19 * Copyright 1996 Alexandre Julliard
20 */
21
22 #include <k32.h>
23 #define NDEBUG
24 #include <debug.h>
25
26
27 /*
28 * @implemented
29 */
30 BOOL
31 WINAPI
32 GetVolumeInformationA(IN LPCSTR lpRootPathName,
33 IN LPSTR lpVolumeNameBuffer,
34 IN DWORD nVolumeNameSize,
35 OUT LPDWORD lpVolumeSerialNumber OPTIONAL,
36 OUT LPDWORD lpMaximumComponentLength OPTIONAL,
37 OUT LPDWORD lpFileSystemFlags OPTIONAL,
38 OUT LPSTR lpFileSystemNameBuffer OPTIONAL,
39 IN DWORD nFileSystemNameSize)
40 {
41 BOOL Ret;
42 NTSTATUS Status;
43 PUNICODE_STRING RootPathNameU;
44 ANSI_STRING VolumeName, FileSystemName;
45 UNICODE_STRING VolumeNameU, FileSystemNameU;
46
47 /* If no root path provided, default to \ */
48 if (lpRootPathName == NULL)
49 {
50 lpRootPathName = "\\";
51 }
52
53 /* Convert root path to unicode */
54 RootPathNameU = Basep8BitStringToStaticUnicodeString(lpRootPathName);
55 if (RootPathNameU == NULL)
56 {
57 return FALSE;
58 }
59
60 /* Init all our STRINGS (U/A) */
61 VolumeNameU.Buffer = NULL;
62 VolumeNameU.MaximumLength = 0;
63 FileSystemNameU.Buffer = NULL;
64 FileSystemNameU.MaximumLength = 0;
65
66 VolumeName.Buffer = lpVolumeNameBuffer;
67 VolumeName.MaximumLength = nVolumeNameSize + 1;
68 FileSystemName.Buffer = lpFileSystemNameBuffer;
69 FileSystemName.MaximumLength = nFileSystemNameSize + 1;
70
71 /* Assume failure for now */
72 Ret = FALSE;
73
74 /* If caller wants volume name, allocate a buffer to receive it */
75 if (lpVolumeNameBuffer != NULL)
76 {
77 VolumeNameU.MaximumLength = sizeof(WCHAR) * (nVolumeNameSize + 1);
78 VolumeNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0,
79 VolumeNameU.MaximumLength);
80 if (VolumeNameU.Buffer == NULL)
81 {
82 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
83 goto CleanAndQuit;
84 }
85 }
86
87 /* If caller wants file system name, allocate a buffer to receive it */
88 if (lpFileSystemNameBuffer != NULL)
89 {
90 FileSystemNameU.MaximumLength = sizeof(WCHAR) * (nVolumeNameSize + 1);
91 FileSystemNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0,
92 FileSystemNameU.MaximumLength);
93 if (FileSystemNameU.Buffer == NULL)
94 {
95 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
96 goto CleanAndQuit;
97 }
98 }
99
100 /* Call W */
101 Ret = GetVolumeInformationW(RootPathNameU->Buffer, VolumeNameU.Buffer,
102 nVolumeNameSize, lpVolumeSerialNumber,
103 lpMaximumComponentLength, lpFileSystemFlags,
104 FileSystemNameU.Buffer, nFileSystemNameSize);
105 /* If it succeed, convert back to ANSI */
106 if (Ret)
107 {
108 if (lpVolumeNameBuffer != NULL)
109 {
110 RtlInitUnicodeString(&VolumeNameU, VolumeNameU.Buffer);
111 Status = RtlUnicodeStringToAnsiString(&VolumeName, &VolumeNameU, FALSE);
112 if (!NT_SUCCESS(Status))
113 {
114 BaseSetLastNTError(Status);
115 Ret = FALSE;
116
117 goto CleanAndQuit;
118 }
119 }
120
121 if (lpFileSystemNameBuffer != NULL)
122 {
123 RtlInitUnicodeString(&FileSystemNameU, FileSystemNameU.Buffer);
124 Status = RtlUnicodeStringToAnsiString(&FileSystemName, &FileSystemNameU, FALSE);
125 if (!NT_SUCCESS(Status))
126 {
127 BaseSetLastNTError(Status);
128 Ret = FALSE;
129
130 goto CleanAndQuit;
131 }
132 }
133 }
134
135 /* Clean and quit */
136 CleanAndQuit:
137 if (VolumeNameU.Buffer != NULL)
138 {
139 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameU.Buffer);
140 }
141
142 if (FileSystemNameU.Buffer != NULL)
143 {
144 RtlFreeHeap(RtlGetProcessHeap(), 0, FileSystemNameU.Buffer);
145 }
146
147 return Ret;
148 }
149
150 /*
151 * @implemented
152 */
153 BOOL
154 IsThisARootDirectory(IN HANDLE VolumeHandle,
155 IN PUNICODE_STRING NtPathName)
156 {
157 NTSTATUS Status;
158 IO_STATUS_BLOCK IoStatusBlock;
159 struct
160 {
161 FILE_NAME_INFORMATION;
162 WCHAR Buffer[MAX_PATH];
163 } FileNameInfo;
164
165 /* If we have a handle, query the name */
166 if (VolumeHandle)
167 {
168 Status = NtQueryInformationFile(VolumeHandle, &IoStatusBlock, &FileNameInfo, sizeof(FileNameInfo), FileNameInformation);
169 if (!NT_SUCCESS(Status))
170 {
171 return FALSE;
172 }
173
174 /* Check we properly end with a \ */
175 if (FileNameInfo.FileName[FileNameInfo.FileNameLength / sizeof(WCHAR) - 1] != L'\\')
176 {
177 return FALSE;
178 }
179 }
180
181 /* If we have a path */
182 if (NtPathName != NULL)
183 {
184 HANDLE LinkHandle;
185 WCHAR Buffer[512];
186 ULONG ReturnedLength;
187 UNICODE_STRING LinkTarget;
188 OBJECT_ATTRIBUTES ObjectAttributes;
189
190 NtPathName->Length -= sizeof(WCHAR);
191
192 InitializeObjectAttributes(&ObjectAttributes, NtPathName,
193 OBJ_CASE_INSENSITIVE,
194 NULL, NULL);
195
196 /* Try to see whether that's a symbolic name */
197 Status = NtOpenSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_QUERY, &ObjectAttributes);
198 NtPathName->Length += sizeof(WCHAR);
199 if (!NT_SUCCESS(Status))
200 {
201 return FALSE;
202 }
203
204 /* If so, query the target */
205 LinkTarget.Buffer = Buffer;
206 LinkTarget.Length = 0;
207 LinkTarget.MaximumLength = sizeof(Buffer);
208
209 Status = NtQuerySymbolicLinkObject(LinkHandle, &LinkTarget, &ReturnedLength);
210 NtClose(LinkHandle);
211 /* A root directory (NtName) is a symbolic link */
212 if (!NT_SUCCESS(Status))
213 {
214 return FALSE;
215 }
216 }
217
218 return TRUE;
219 }
220
221 /*
222 * @implemented
223 */
224 BOOL
225 WINAPI
226 GetVolumeInformationW(IN LPCWSTR lpRootPathName,
227 IN LPWSTR lpVolumeNameBuffer,
228 IN DWORD nVolumeNameSize,
229 OUT LPDWORD lpVolumeSerialNumber OPTIONAL,
230 OUT LPDWORD lpMaximumComponentLength OPTIONAL,
231 OUT LPDWORD lpFileSystemFlags OPTIONAL,
232 OUT LPWSTR lpFileSystemNameBuffer OPTIONAL,
233 IN DWORD nFileSystemNameSize)
234 {
235 BOOL Ret;
236 NTSTATUS Status;
237 HANDLE VolumeHandle;
238 LPCWSTR RootPathName;
239 UNICODE_STRING NtPathName;
240 IO_STATUS_BLOCK IoStatusBlock;
241 OBJECT_ATTRIBUTES ObjectAttributes;
242 PFILE_FS_VOLUME_INFORMATION VolumeInfo;
243 PFILE_FS_ATTRIBUTE_INFORMATION VolumeAttr;
244 ULONG OldMode, VolumeInfoSize, VolumeAttrSize;
245
246 /* If no root path provided, default to \ */
247 if (lpRootPathName == NULL)
248 {
249 RootPathName = L"\\";
250 }
251 else
252 {
253 RootPathName = lpRootPathName;
254 }
255
256 /* Convert to NT name */
257 if (!RtlDosPathNameToNtPathName_U(RootPathName, &NtPathName, NULL, NULL))
258 {
259 SetLastError(ERROR_PATH_NOT_FOUND);
260 return FALSE;
261 }
262
263 /* Check we really end with a backslash */
264 if (NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] != L'\\')
265 {
266 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
267 BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
268 return FALSE;
269 }
270
271 /* Try to open the received path */
272 InitializeObjectAttributes(&ObjectAttributes, &NtPathName,
273 OBJ_CASE_INSENSITIVE,
274 NULL, NULL);
275
276 /* No errors to the user */
277 RtlSetThreadErrorMode(RTL_SEM_FAILCRITICALERRORS, &OldMode);
278 Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, 0, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT);
279 RtlSetThreadErrorMode(OldMode, NULL);
280 if (!NT_SUCCESS(Status))
281 {
282 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
283 BaseSetLastNTError(Status);
284 return FALSE;
285 }
286
287 /* Check whether that's a root directory */
288 if (!IsThisARootDirectory(VolumeHandle, &NtPathName))
289 {
290 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
291 NtClose(VolumeHandle);
292 SetLastError(ERROR_DIR_NOT_ROOT);
293 return FALSE;
294 }
295
296 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
297
298 /* Assume we don't need to query FileFsVolumeInformation */
299 VolumeInfo = NULL;
300 /* If user wants volume name, allocate a buffer to query it */
301 if (lpVolumeNameBuffer != NULL)
302 {
303 VolumeInfoSize = nVolumeNameSize + sizeof(FILE_FS_VOLUME_INFORMATION);
304 }
305 /* If user just wants the serial number, allocate a dummy buffer */
306 else if (lpVolumeSerialNumber != NULL)
307 {
308 VolumeInfoSize = MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_VOLUME_INFORMATION);
309 }
310 /* Otherwise, nothing to query */
311 else
312 {
313 VolumeInfoSize = 0;
314 }
315
316 /* If we're to query, allocate a big enough buffer */
317 if (VolumeInfoSize != 0)
318 {
319 VolumeInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumeInfoSize);
320 if (VolumeInfo == NULL)
321 {
322 NtClose(VolumeHandle);
323 BaseSetLastNTError(STATUS_NO_MEMORY);
324 return FALSE;
325 }
326 }
327
328 /* Assume we don't need to query FileFsAttributeInformation */
329 VolumeAttr = NULL;
330 /* If user wants filesystem name, allocate a buffer to query it */
331 if (lpFileSystemNameBuffer != NULL)
332 {
333 VolumeAttrSize = nFileSystemNameSize + sizeof(FILE_FS_ATTRIBUTE_INFORMATION);
334 }
335 /* If user just wants max compo len or flags, allocate a dummy buffer */
336 else if (lpMaximumComponentLength != NULL || lpFileSystemFlags != NULL)
337 {
338 VolumeAttrSize = MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_ATTRIBUTE_INFORMATION);
339 }
340 else
341 {
342 VolumeAttrSize = 0;
343 }
344
345 /* If we're to query, allocate a big enough buffer */
346 if (VolumeAttrSize != 0)
347 {
348 VolumeAttr = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumeAttrSize);
349 if (VolumeAttr == NULL)
350 {
351 if (VolumeInfo != NULL)
352 {
353 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeInfo);
354 }
355
356 NtClose(VolumeHandle);
357 BaseSetLastNTError(STATUS_NO_MEMORY);
358 return FALSE;
359 }
360 }
361
362 /* Assume we'll fail */
363 Ret = FALSE;
364
365 /* If we're to query FileFsVolumeInformation, do it now! */
366 if (VolumeInfo != NULL)
367 {
368 Status = NtQueryVolumeInformationFile(VolumeHandle, &IoStatusBlock, VolumeInfo, VolumeInfoSize, FileFsVolumeInformation);
369 if (!NT_SUCCESS(Status))
370 {
371 BaseSetLastNTError(Status);
372 goto CleanAndQuit;
373 }
374 }
375
376 /* If we're to query FileFsAttributeInformation, do it now! */
377 if (VolumeAttr != NULL)
378 {
379 Status = NtQueryVolumeInformationFile(VolumeHandle, &IoStatusBlock, VolumeAttr, VolumeAttrSize, FileFsAttributeInformation);
380 if (!NT_SUCCESS(Status))
381 {
382 BaseSetLastNTError(Status);
383 goto CleanAndQuit;
384 }
385 }
386
387 /* If user wants volume name */
388 if (lpVolumeNameBuffer != NULL)
389 {
390 /* Check its buffer can hold it (+ 0) */
391 if (VolumeInfo->VolumeLabelLength >= nVolumeNameSize)
392 {
393 SetLastError(ERROR_BAD_LENGTH);
394 goto CleanAndQuit;
395 }
396
397 /* Copy and zero */
398 RtlCopyMemory(lpVolumeNameBuffer, VolumeInfo->VolumeLabel, VolumeInfo->VolumeLabelLength);
399 lpVolumeNameBuffer[VolumeInfo->VolumeLabelLength / sizeof(WCHAR)] = UNICODE_NULL;
400 }
401
402 /* If user wants wants serial number, return it */
403 if (lpVolumeSerialNumber != NULL)
404 {
405 *lpVolumeSerialNumber = VolumeInfo->VolumeSerialNumber;
406 }
407
408 /* If user wants filesystem name */
409 if (lpFileSystemNameBuffer != NULL)
410 {
411 /* Check its buffer can hold it (+ 0) */
412 if (VolumeAttr->FileSystemNameLength >= nFileSystemNameSize)
413 {
414 SetLastError(ERROR_BAD_LENGTH);
415 goto CleanAndQuit;
416 }
417
418 /* Copy and zero */
419 RtlCopyMemory(lpFileSystemNameBuffer, VolumeAttr->FileSystemName, VolumeAttr->FileSystemNameLength);
420 lpFileSystemNameBuffer[VolumeAttr->FileSystemNameLength / sizeof(WCHAR)] = UNICODE_NULL;
421 }
422
423 /* If user wants wants max compo len, return it */
424 if (lpMaximumComponentLength != NULL)
425 {
426 *lpMaximumComponentLength = VolumeAttr->MaximumComponentNameLength;
427 }
428
429 /* If user wants wants FS flags, return them */
430 if (lpFileSystemFlags != NULL)
431 {
432 *lpFileSystemFlags = VolumeAttr->FileSystemAttributes;
433 }
434
435 /* We did it! */
436 Ret = TRUE;
437
438 CleanAndQuit:
439 NtClose(VolumeHandle);
440
441 if (VolumeInfo != NULL)
442 {
443 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeInfo);
444 }
445
446 if (VolumeAttr != NULL)
447 {
448 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeAttr);
449 }
450
451 return Ret;
452 }
453
454 /*
455 * @implemented
456 */
457 BOOL
458 WINAPI
459 SetVolumeLabelA(IN LPCSTR lpRootPathName,
460 IN LPCSTR lpVolumeName OPTIONAL) /* NULL if deleting label */
461 {
462 BOOL Ret;
463 UNICODE_STRING VolumeNameU;
464 PUNICODE_STRING RootPathNameU;
465
466 if (lpRootPathName == NULL)
467 {
468 lpRootPathName = "\\";
469 }
470
471 RootPathNameU = Basep8BitStringToStaticUnicodeString(lpRootPathName);
472 if (RootPathNameU == NULL)
473 {
474 return FALSE;
475 }
476
477 if (lpVolumeName != NULL)
478 {
479 if (!Basep8BitStringToDynamicUnicodeString(&VolumeNameU, lpVolumeName))
480 {
481 return FALSE;
482 }
483 }
484 else
485 {
486 VolumeNameU.Buffer = NULL;
487 }
488
489 Ret = SetVolumeLabelW(RootPathNameU->Buffer, VolumeNameU.Buffer);
490 RtlFreeUnicodeString(&VolumeNameU);
491 return Ret;
492 }
493
494 /*
495 * @implemented
496 */
497 BOOL
498 WINAPI
499 SetVolumeLabelW(IN LPCWSTR lpRootPathName,
500 IN LPCWSTR lpVolumeName OPTIONAL) /* NULL if deleting label */
501 {
502 BOOL Ret;
503 NTSTATUS Status;
504 PWSTR VolumeRoot;
505 HANDLE VolumeHandle;
506 WCHAR VolumeGuid[MAX_PATH];
507 IO_STATUS_BLOCK IoStatusBlock;
508 OBJECT_ATTRIBUTES ObjectAttributes;
509 PFILE_FS_LABEL_INFORMATION FsLabelInfo;
510 UNICODE_STRING VolumeName, NtVolumeName;
511
512 /* If no root path provided, default to \ */
513 VolumeRoot = L"\\";
514
515 /* If user wants to set a label, make it a string */
516 if (lpVolumeName != NULL)
517 {
518 RtlInitUnicodeString(&VolumeName, lpVolumeName);
519 }
520 else
521 {
522 VolumeName.Length = 0;
523 VolumeName.MaximumLength = 0;
524 VolumeName.Buffer = NULL;
525 }
526
527 /* If we received a volume, try to get its GUID name */
528 if (lpRootPathName != NULL)
529 {
530 Ret = GetVolumeNameForVolumeMountPointW(lpRootPathName, VolumeGuid, MAX_PATH);
531 }
532 else
533 {
534 Ret = FALSE;
535 }
536
537 /* If we got the GUID name, use it */
538 if (Ret)
539 {
540 VolumeRoot = VolumeGuid;
541 }
542 else
543 {
544 /* Otherwise, use the name provided by the caller */
545 if (lpRootPathName != NULL)
546 {
547 VolumeRoot = (PWSTR)lpRootPathName;
548 }
549 }
550
551 /* Convert to a NT path */
552 if (!RtlDosPathNameToNtPathName_U(VolumeRoot, &NtVolumeName, NULL, NULL))
553 {
554 SetLastError(ERROR_PATH_NOT_FOUND);
555 return FALSE;
556 }
557
558
559 /* Check we really end with a backslash */
560 if (NtVolumeName.Buffer[(NtVolumeName.Length / sizeof(WCHAR)) - 1] != L'\\')
561 {
562 RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer);
563 BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
564 return FALSE;
565 }
566
567 /* Try to open the root directory */
568 InitializeObjectAttributes(&ObjectAttributes, &NtVolumeName,
569 OBJ_CASE_INSENSITIVE, NULL, NULL);
570
571 Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE | FILE_WRITE_DATA,
572 &ObjectAttributes, &IoStatusBlock,
573 FILE_SHARE_READ | FILE_SHARE_WRITE,
574 FILE_SYNCHRONOUS_IO_NONALERT);
575 if (!NT_SUCCESS(Status))
576 {
577 RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer);
578 BaseSetLastNTError(Status);
579 return FALSE;
580 }
581
582 /* Validate it's really a root path */
583 if (!IsThisARootDirectory(VolumeHandle, NULL))
584 {
585 RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer);
586 NtClose(VolumeHandle);
587 SetLastError(ERROR_DIR_NOT_ROOT);
588 return FALSE;
589 }
590
591 /* Done */
592 NtClose(VolumeHandle);
593
594 /* Now, open the volume to perform the label change */
595 NtVolumeName.Length -= sizeof(WCHAR);
596 InitializeObjectAttributes(&ObjectAttributes, &NtVolumeName,
597 OBJ_CASE_INSENSITIVE, NULL, NULL);
598
599 Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE | FILE_WRITE_DATA,
600 &ObjectAttributes, &IoStatusBlock,
601 FILE_SHARE_READ | FILE_SHARE_WRITE,
602 FILE_SYNCHRONOUS_IO_NONALERT);
603
604 RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer);
605
606 if (!NT_SUCCESS(Status))
607 {
608 BaseSetLastNTError(Status);
609 return FALSE;
610 }
611
612 /* Assume success */
613 Ret = TRUE;
614
615 /* Allocate a buffer that can hold new label and its size */
616 FsLabelInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(FILE_FS_LABEL_INFORMATION) + VolumeName.Length);
617 if (FsLabelInfo != NULL)
618 {
619 /* Copy name and set its size */
620 RtlCopyMemory(FsLabelInfo->VolumeLabel, VolumeName.Buffer, VolumeName.Length);
621 FsLabelInfo->VolumeLabelLength = VolumeName.Length;
622
623 /* And finally, set new label */
624 Status = NtSetVolumeInformationFile(VolumeHandle, &IoStatusBlock, FsLabelInfo, sizeof(FILE_FS_LABEL_INFORMATION) + VolumeName.Length, FileFsLabelInformation);
625 }
626 else
627 {
628 /* Allocation failed */
629 Status = STATUS_NO_MEMORY;
630 }
631
632 /* In case of failure, set status and mark failure */
633 if (!NT_SUCCESS(Status))
634 {
635 BaseSetLastNTError(Status);
636 Ret = FALSE;
637 }
638
639 /* We're done */
640 NtClose(VolumeHandle);
641
642 /* Free buffer if required */
643 if (FsLabelInfo != NULL)
644 {
645 RtlFreeHeap(RtlGetProcessHeap(), 0, FsLabelInfo);
646 }
647
648 return Ret;
649 }
650
651 /*
652 * @implemented (Wine 13 sep 2008)
653 */
654 HANDLE
655 WINAPI
656 FindFirstVolumeW(IN LPWSTR volume,
657 IN DWORD len)
658 {
659 DWORD size = 1024;
660 DWORD br;
661 HANDLE mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
662 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE );
663 if (mgr == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
664
665 for (;;)
666 {
667 MOUNTMGR_MOUNT_POINT input;
668 MOUNTMGR_MOUNT_POINTS *output;
669
670 if (!(output = RtlAllocateHeap( RtlGetProcessHeap(), 0, size )))
671 {
672 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
673 break;
674 }
675 memset( &input, 0, sizeof(input) );
676
677 if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, &input, sizeof(input),
678 output, size, &br, NULL ))
679 {
680 if (GetLastError() != ERROR_MORE_DATA) break;
681 size = output->Size;
682 RtlFreeHeap( RtlGetProcessHeap(), 0, output );
683 continue;
684 }
685 CloseHandle( mgr );
686 /* abuse the Size field to store the current index */
687 output->Size = 0;
688 if (!FindNextVolumeW( output, volume, len ))
689 {
690 RtlFreeHeap( RtlGetProcessHeap(), 0, output );
691 return INVALID_HANDLE_VALUE;
692 }
693 return (HANDLE)output;
694 }
695 CloseHandle( mgr );
696 return INVALID_HANDLE_VALUE;
697 }
698
699 /*
700 * @implemented (Wine 13 sep 2008)
701 */
702 HANDLE
703 WINAPI
704 FindFirstVolumeA(IN LPSTR volume,
705 IN DWORD len)
706 {
707 WCHAR *buffer = NULL;
708 HANDLE handle;
709
710 buffer = RtlAllocateHeap( RtlGetProcessHeap(), 0, len * sizeof(WCHAR) );
711
712 if (!buffer)
713 {
714 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
715 return INVALID_HANDLE_VALUE;
716 }
717
718 handle = FindFirstVolumeW( buffer, len );
719
720 if (handle != INVALID_HANDLE_VALUE)
721 {
722 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL ))
723 {
724 FindVolumeClose( handle );
725 handle = INVALID_HANDLE_VALUE;
726 }
727 }
728 RtlFreeHeap( RtlGetProcessHeap(), 0, buffer );
729 return handle;
730 }
731
732 /*
733 * @implemented (Wine 13 sep 2008)
734 */
735 BOOL
736 WINAPI
737 FindVolumeClose(IN HANDLE hFindVolume)
738 {
739 return RtlFreeHeap(RtlGetProcessHeap(), 0, hFindVolume);
740 }
741
742 /*
743 * @implemented
744 */
745 BOOL
746 WINAPI
747 GetVolumePathNameA(IN LPCSTR lpszFileName,
748 IN LPSTR lpszVolumePathName,
749 IN DWORD cchBufferLength)
750 {
751 BOOL Ret;
752 PUNICODE_STRING FileNameU;
753 ANSI_STRING VolumePathName;
754 UNICODE_STRING VolumePathNameU;
755
756 /* Convert file name to unicode */
757 FileNameU = Basep8BitStringToStaticUnicodeString(lpszFileName);
758 if (FileNameU == NULL)
759 {
760 return FALSE;
761 }
762
763 /* Initialize all the strings we'll need */
764 VolumePathName.Buffer = lpszVolumePathName;
765 VolumePathName.Length = 0;
766 VolumePathName.MaximumLength = cchBufferLength - 1;
767
768 VolumePathNameU.Length = 0;
769 VolumePathNameU.MaximumLength = (cchBufferLength - 1) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
770 /* Allocate a buffer for calling the -W */
771 VolumePathNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumePathNameU.MaximumLength);
772 if (VolumePathNameU.Buffer == NULL)
773 {
774 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
775 return FALSE;
776 }
777
778 /* Call the -W implementation */
779 Ret = GetVolumePathNameW(FileNameU->Buffer, VolumePathNameU.Buffer, cchBufferLength);
780 /* If it succeed */
781 if (Ret)
782 {
783 NTSTATUS Status;
784
785 /* Convert back to ANSI */
786 RtlInitUnicodeString(&VolumePathNameU, VolumePathNameU.Buffer);
787 Status = RtlUnicodeStringToAnsiString(&VolumePathName, &VolumePathNameU, FALSE);
788 /* If conversion failed, just set error code and fail the rest */
789 if (!NT_SUCCESS(Status))
790 {
791 BaseSetLastNTError(Status);
792 Ret = FALSE;
793 }
794 /* Otherwise, null terminate the string (it's OK, we computed -1) */
795 else
796 {
797 VolumePathName.Buffer[VolumePathName.Length] = ANSI_NULL;
798 }
799 }
800
801 /* Free the buffer allocated for -W call */
802 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNameU.Buffer);
803 return Ret;
804 }
805
806 /*
807 * @implemented
808 */
809 BOOL
810 WINAPI
811 GetVolumePathNameW(IN LPCWSTR lpszFileName,
812 IN LPWSTR lpszVolumePathName,
813 IN DWORD cchBufferLength)
814 {
815 BOOL MountPoint;
816 DWORD FullPathLen;
817 WCHAR OldFilePart;
818 UNICODE_STRING FullPath;
819 PWSTR FullPathBuf, FilePart, VolumeNameBuf;
820
821 /* Probe for full path len */
822 FullPathLen = GetFullPathNameW(lpszFileName, 0, NULL, NULL);
823 if (FullPathLen == 0)
824 {
825 return FALSE;
826 }
827
828 /* Allocate a big enough buffer to receive it */
829 FullPathBuf = RtlAllocateHeap(RtlGetProcessHeap(), 0, (FullPathLen + 10) * sizeof(WCHAR));
830 if (FullPathBuf == NULL)
831 {
832 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
833 return FALSE;
834 }
835
836 /* And get full path name */
837 if (GetFullPathNameW(lpszFileName, FullPathLen + 10, FullPathBuf, &FilePart) == 0)
838 {
839 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
840 return FALSE;
841 }
842
843 /* Make a string out of it */
844 RtlInitUnicodeString(&FullPath, FullPathBuf);
845 /* We will finish our string with '\', for ease of the parsing after */
846 if (FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\')
847 {
848 FullPath.Length += sizeof(WCHAR);
849 FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] = L'\\';
850 FullPath.Buffer[FullPath.Length / sizeof(WCHAR)] = UNICODE_NULL;
851 }
852
853 /* Allocate a buffer big enough to receive our volume name */
854 VolumeNameBuf = RtlAllocateHeap(RtlGetProcessHeap(), 0, 0x2000 * sizeof(WCHAR));
855 if (VolumeNameBuf == NULL)
856 {
857 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
858 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
859 return FALSE;
860 }
861
862 /* We don't care about file part: we added an extra backslash, so there's no
863 * file, we're back at the dir level.
864 * We'll recompute file part afterwards
865 */
866 FilePart = NULL;
867 /* Keep track of the letter we could drop to shorten the string */
868 OldFilePart = UNICODE_NULL;
869 /* As long as querying volume name fails, keep looping */
870 while (!BasepGetVolumeNameForVolumeMountPoint(FullPath.Buffer, VolumeNameBuf, 0x2000u, &MountPoint))
871 {
872 USHORT LastSlash;
873
874 /* Not a mount point, but opening returning access denied? Assume it's one, just not
875 * a reparse backed one (classic mount point, a device)!
876 */
877 if (!MountPoint && GetLastError() == ERROR_ACCESS_DENIED)
878 {
879 MountPoint = TRUE;
880 }
881
882 /* BasepGetVolumeNameForVolumeMountPoint failed, but returned a volume name.
883 * This can happen when we are given a reparse point where MountMgr could find associated
884 * volume name which is not a valid DOS volume
885 * A valid DOS name always starts with \\
886 */
887 if (VolumeNameBuf[0] != UNICODE_NULL && (FullPath.Buffer[0] != L'\\' || FullPath.Buffer[1] != L'\\'))
888 {
889 CHAR RootPathName[4];
890
891 /* Construct a simple <letter>:\ string to get drive type */
892 RootPathName[0] = FullPath.Buffer[0];
893 RootPathName[1] = ':';
894 RootPathName[2] = '\\';
895 RootPathName[3] = ANSI_NULL;
896
897 /* If we weren't given a drive letter actually, or if that's not a remote drive
898 * Note: in this code path, we're recursive and stop fail loop
899 */
900 if (FullPath.Buffer[1] != L':' || GetDriveTypeA(RootPathName) != DRIVE_REMOTE)
901 {
902 BOOL Ret;
903
904 /* We won't need the full path, we'll now work with the returned volume name */
905 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
906 /* If it wasn't an NT name which was returned */
907 if ((VolumeNameBuf[0] != L'\\') || (VolumeNameBuf[1] != L'?') ||
908 (VolumeNameBuf[2] != L'?') || (VolumeNameBuf[3] != L'\\'))
909 {
910 PWSTR GlobalPath;
911 UNICODE_STRING GlobalRoot;
912
913 /* Create a new name in the NT namespace (from Win32) */
914 RtlInitUnicodeString(&FullPath, VolumeNameBuf);
915 RtlInitUnicodeString(&GlobalRoot, L"\\\\?\\GLOBALROOT");
916
917 /* We allocate a buffer than can contain both the namespace and the volume name */
918 GlobalPath = RtlAllocateHeap(RtlGetProcessHeap(), 0, FullPath.Length + GlobalRoot.Length);
919 if (GlobalPath == NULL)
920 {
921 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameBuf);
922 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
923 return FALSE;
924 }
925
926 /* Fill in the new query name */
927 RtlCopyMemory(GlobalPath, GlobalRoot.Buffer, GlobalRoot.Length);
928 RtlCopyMemory((PVOID)((ULONG_PTR)GlobalPath + GlobalRoot.Length), FullPath.Buffer, FullPath.Length);
929 GlobalPath[(FullPath.Length + GlobalRoot.Length) / sizeof(WCHAR)] = UNICODE_NULL;
930
931 /* Give it another try */
932 Ret = GetVolumePathNameW(GlobalPath, lpszVolumePathName, cchBufferLength);
933
934 RtlFreeHeap(RtlGetProcessHeap(), 0, GlobalPath);
935 }
936 else
937 {
938 /* If we don't have a drive letter in the Win32 name space \\.\<letter>: */
939 if ((VolumeNameBuf[4] != UNICODE_NULL) && (VolumeNameBuf[5] != L':'))
940 {
941 /* Shit our starting \\ */
942 RtlInitUnicodeString(&FullPath, VolumeNameBuf);
943 RtlMoveMemory(VolumeNameBuf, (PVOID)((ULONG_PTR)VolumeNameBuf + (2 * sizeof(WCHAR))), FullPath.Length - (3 * sizeof(WCHAR)));
944 }
945 /* Otherwise, just make sure we're double \ at the being to query again with the
946 * proper namespace
947 */
948 else
949 {
950 VolumeNameBuf[1] = L'\\';
951 }
952
953 /* Give it another try */
954 Ret = GetVolumePathNameW(VolumeNameBuf, lpszVolumePathName, cchBufferLength);
955 }
956
957 /* And done! */
958 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameBuf);
959 return Ret;
960 }
961 }
962
963 /* No mount point but with a file part? Restore filepart and exit */
964 if (!MountPoint && FilePart != NULL)
965 {
966 FilePart[0] = OldFilePart;
967 RtlInitUnicodeString(&FullPath, FullPathBuf);
968 break;
969 }
970
971 /* We cannot go down the path any longer, too small */
972 if (FullPath.Length <= sizeof(WCHAR))
973 {
974 break;
975 }
976
977 /* Prepare the next split */
978 LastSlash = (FullPath.Length / sizeof(WCHAR)) - 2;
979 if (FullPath.Length / sizeof(WCHAR) != 2)
980 {
981 do
982 {
983 if (FullPath.Buffer[LastSlash] == L'\\')
984 {
985 break;
986 }
987
988 --LastSlash;
989 } while (LastSlash != 0);
990 }
991
992 /* We couldn't split path, quit */
993 if (LastSlash == 0)
994 {
995 break;
996 }
997
998 /* If that's a mount point, keep track of the directory name */
999 if (MountPoint)
1000 {
1001 FilePart = &FullPath.Buffer[LastSlash + 1];
1002 OldFilePart = FilePart[0];
1003 /* And null terminate the string */
1004 FilePart[0] = UNICODE_NULL;
1005 }
1006 /* Otherwise, just null terminate the string */
1007 else
1008 {
1009 FullPath.Buffer[LastSlash + 1] = UNICODE_NULL;
1010 }
1011
1012 /* We went down a bit in the path, fix the string and retry */
1013 RtlInitUnicodeString(&FullPath, FullPathBuf);
1014 }
1015
1016 /* Once here, we'll return something from the full path buffer, so release
1017 * output buffer
1018 */
1019 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameBuf);
1020
1021 /* Not a mount point, bail out */
1022 if (!MountPoint && FilePart == NULL)
1023 {
1024 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
1025 return FALSE;
1026 }
1027
1028 /* Make sure we have enough room to copy our volume */
1029 if ((cchBufferLength * sizeof(WCHAR)) < FullPath.Length + sizeof(UNICODE_NULL))
1030 {
1031 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
1032 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1033 return FALSE;
1034 }
1035
1036 /* Copy and null terminate */
1037 RtlCopyMemory(lpszVolumePathName, FullPath.Buffer, FullPath.Length);
1038 lpszVolumePathName[FullPath.Length / sizeof(WCHAR)] = UNICODE_NULL;
1039
1040 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPathBuf);
1041
1042 /* Done! */
1043 return TRUE;
1044 }
1045
1046 /*
1047 * @implemented
1048 */
1049 BOOL
1050 WINAPI
1051 FindNextVolumeA(IN HANDLE handle,
1052 IN LPSTR volume,
1053 IN DWORD len)
1054 {
1055 WCHAR *buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, len * sizeof(WCHAR));
1056 BOOL ret;
1057
1058 if (!buffer)
1059 {
1060 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1061 return FALSE;
1062 }
1063
1064 if ((ret = FindNextVolumeW( handle, buffer, len )))
1065 {
1066 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL )) ret = FALSE;
1067 }
1068
1069 RtlFreeHeap(RtlGetProcessHeap(), 0, buffer);
1070 return ret;
1071 }
1072
1073 /*
1074 * @implemented
1075 */
1076 BOOL
1077 WINAPI
1078 FindNextVolumeW(IN HANDLE handle,
1079 IN LPWSTR volume,
1080 IN DWORD len)
1081 {
1082 MOUNTMGR_MOUNT_POINTS *data = handle;
1083
1084 while (data->Size < data->NumberOfMountPoints)
1085 {
1086 static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{',};
1087 WCHAR *link = (WCHAR *)((char *)data + data->MountPoints[data->Size].SymbolicLinkNameOffset);
1088 DWORD size = data->MountPoints[data->Size].SymbolicLinkNameLength;
1089 data->Size++;
1090 /* skip non-volumes */
1091 if (size < sizeof(volumeW) || memcmp( link, volumeW, sizeof(volumeW) )) continue;
1092 if (size + sizeof(WCHAR) >= len * sizeof(WCHAR))
1093 {
1094 SetLastError( ERROR_FILENAME_EXCED_RANGE );
1095 return FALSE;
1096 }
1097 memcpy( volume, link, size );
1098 volume[1] = '\\'; /* map \??\ to \\?\ */
1099 volume[size / sizeof(WCHAR)] = '\\'; /* Windows appends a backslash */
1100 volume[size / sizeof(WCHAR) + 1] = 0;
1101 DPRINT( "returning entry %u %s\n", data->Size - 1, volume );
1102 return TRUE;
1103 }
1104 SetLastError( ERROR_NO_MORE_FILES );
1105 return FALSE;
1106 }
1107
1108 /*
1109 * @implemented
1110 */
1111 BOOL
1112 WINAPI
1113 GetVolumePathNamesForVolumeNameA(IN LPCSTR lpszVolumeName,
1114 IN LPSTR lpszVolumePathNames,
1115 IN DWORD cchBufferLength,
1116 OUT PDWORD lpcchReturnLength)
1117 {
1118 BOOL Ret;
1119 NTSTATUS Status;
1120 DWORD cchReturnLength;
1121 ANSI_STRING VolumePathName;
1122 PUNICODE_STRING VolumeNameU;
1123 UNICODE_STRING VolumePathNamesU;
1124
1125 /* Convert volume name to unicode */
1126 VolumeNameU = Basep8BitStringToStaticUnicodeString(lpszVolumeName);
1127 if (VolumeNameU == NULL)
1128 {
1129 return FALSE;
1130 }
1131
1132 /* Initialize the strings we'll use later on */
1133 VolumePathName.Length = 0;
1134 VolumePathName.MaximumLength = cchBufferLength;
1135 VolumePathName.Buffer = lpszVolumePathNames;
1136
1137 VolumePathNamesU.Length = 0;
1138 VolumePathNamesU.MaximumLength = sizeof(WCHAR) * cchBufferLength;
1139 /* If caller provided a non 0 sized string, allocate a buffer for our unicode string */
1140 if (VolumePathNamesU.MaximumLength != 0)
1141 {
1142 VolumePathNamesU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.MaximumLength);
1143 if (VolumePathNamesU.Buffer == NULL)
1144 {
1145 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1146 return FALSE;
1147 }
1148 }
1149 else
1150 {
1151 VolumePathNamesU.Buffer = NULL;
1152 }
1153
1154 /* Call the -W implementation */
1155 Ret = GetVolumePathNamesForVolumeNameW(VolumeNameU->Buffer, VolumePathNamesU.Buffer,
1156 cchBufferLength, &cchReturnLength);
1157 /* Call succeed, we'll return the total length */
1158 if (Ret)
1159 {
1160 VolumePathNamesU.Length = sizeof(WCHAR) * cchReturnLength;
1161 }
1162 else
1163 {
1164 /* Else, if we fail for anything else than too small buffer, quit */
1165 if (GetLastError() != ERROR_MORE_DATA)
1166 {
1167 if (VolumePathNamesU.Buffer != NULL)
1168 {
1169 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.Buffer);
1170 }
1171
1172 return FALSE;
1173 }
1174
1175 /* Otherwise, we'll just copy as much as we can */
1176 VolumePathNamesU.Length = sizeof(WCHAR) * cchBufferLength;
1177 }
1178
1179 /* Convert our output string back to ANSI */
1180 Status = RtlUnicodeStringToAnsiString(&VolumePathName, &VolumePathNamesU, FALSE);
1181 if (!NT_SUCCESS(Status))
1182 {
1183 BaseSetLastNTError(Status);
1184
1185 if (VolumePathNamesU.Buffer != NULL)
1186 {
1187 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.Buffer);
1188 }
1189
1190 return FALSE;
1191 }
1192
1193 /* If caller wants return length, two cases... */
1194 if (lpcchReturnLength != NULL)
1195 {
1196 /* We succeed: return the copied length */
1197 if (Ret)
1198 {
1199 *lpcchReturnLength = VolumePathName.Length;
1200 }
1201 /* We failed, return the size we would have loved having! */
1202 else
1203 {
1204 *lpcchReturnLength = sizeof(WCHAR) * cchReturnLength;
1205 }
1206 }
1207
1208 /* Release our buffer if allocated */
1209 if (VolumePathNamesU.Buffer != NULL)
1210 {
1211 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.Buffer);
1212 }
1213
1214 return Ret;
1215 }
1216
1217
1218 /*
1219 * @implemented
1220 */
1221 BOOL
1222 WINAPI
1223 GetVolumePathNamesForVolumeNameW(IN LPCWSTR lpszVolumeName,
1224 IN LPWSTR lpszVolumePathNames,
1225 IN DWORD cchBufferLength,
1226 OUT PDWORD lpcchReturnLength)
1227 {
1228 BOOL Ret;
1229 PWSTR MultiSz;
1230 DWORD BytesReturned;
1231 HANDLE MountMgrHandle;
1232 UNICODE_STRING VolumeName;
1233 PMOUNTMGR_TARGET_NAME TargetName;
1234 PMOUNTMGR_VOLUME_PATHS VolumePaths;
1235 ULONG BufferSize, CharsInMgr, CharsInOutput, Paths;
1236
1237 /* First look that our volume name looks somehow correct */
1238 RtlInitUnicodeString(&VolumeName, lpszVolumeName);
1239 if (VolumeName.Buffer[(VolumeName.Length / sizeof(WCHAR)) - 1] != L'\\')
1240 {
1241 BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
1242 return FALSE;
1243 }
1244
1245 /* Validate it's a DOS volume name finishing with a backslash */
1246 if (!MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&VolumeName))
1247 {
1248 SetLastError(ERROR_INVALID_PARAMETER);
1249 return FALSE;
1250 }
1251
1252 /* Allocate an input MOUNTMGR_TARGET_NAME */
1253 TargetName = RtlAllocateHeap(RtlGetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT));
1254 if (TargetName == NULL)
1255 {
1256 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1257 return FALSE;
1258 }
1259
1260 /* And fill it */
1261 RtlZeroMemory(TargetName, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT));
1262 TargetName->DeviceNameLength = VolumeName.Length - sizeof(WCHAR);
1263 RtlCopyMemory(TargetName->DeviceName, VolumeName.Buffer, TargetName->DeviceNameLength);
1264 TargetName->DeviceName[1] = L'?';
1265
1266 /* Open the mount manager */
1267 MountMgrHandle = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, 0,
1268 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1269 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
1270 INVALID_HANDLE_VALUE);
1271 if (MountMgrHandle == INVALID_HANDLE_VALUE)
1272 {
1273 RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName);
1274 return FALSE;
1275 }
1276
1277 /* Allocate an initial output buffer, just to get length */
1278 VolumePaths = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(MOUNTMGR_VOLUME_PATHS));
1279 if (VolumePaths == NULL)
1280 {
1281 CloseHandle(MountMgrHandle);
1282 RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName);
1283 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1284 return FALSE;
1285 }
1286
1287 /* Query the paths */
1288 Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS,
1289 TargetName, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT),
1290 VolumePaths, sizeof(MOUNTMGR_VOLUME_PATHS), &BytesReturned,
1291 NULL);
1292 /* Loop until we can query everything */
1293 while (!Ret)
1294 {
1295 /* If failed for another reason than too small buffer, fail */
1296 if (GetLastError() != ERROR_MORE_DATA)
1297 {
1298 CloseHandle(MountMgrHandle);
1299 RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName);
1300 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths);
1301 return FALSE;
1302 }
1303
1304 /* Get the required length */
1305 BufferSize = VolumePaths->MultiSzLength + sizeof(MOUNTMGR_VOLUME_PATHS);
1306
1307 /* And reallocate our output buffer (big enough this time) */
1308 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths);
1309 VolumePaths = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferSize);
1310 if (VolumePaths == NULL)
1311 {
1312 CloseHandle(MountMgrHandle);
1313 RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName);
1314 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1315 return FALSE;
1316 }
1317
1318 /* Query again the mount mgr */
1319 Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS,
1320 TargetName, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT),
1321 VolumePaths, BufferSize, &BytesReturned, NULL);
1322 }
1323
1324 /* We're done, no need for input nor mount mgr any longer */
1325 CloseHandle(MountMgrHandle);
1326 RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName);
1327
1328 /* Initialize:
1329 - Number of paths we saw (useful to count extra \)
1330 - Progress in mount mgr output
1331 - Progress in output buffer
1332 - Direct buffer to returned MultiSz
1333 */
1334 Paths = 0;
1335 CharsInMgr = 0;
1336 CharsInOutput = 0;
1337 MultiSz = VolumePaths->MultiSz;
1338
1339 /* If we have an output buffer */
1340 if (cchBufferLength != 0)
1341 {
1342 /* Loop on the output to recopy it back to the caller
1343 * Note that we loop until -1 not to handle last 0 (will be done later on)
1344 */
1345 for (; (CharsInMgr < VolumePaths->MultiSzLength / sizeof(WCHAR) - 1) && (CharsInOutput < cchBufferLength);
1346 ++CharsInMgr, ++CharsInOutput)
1347 {
1348 /* When we reach the end of a path */
1349 if (MultiSz[CharsInMgr] == UNICODE_NULL)
1350 {
1351 /* On path done (count), add an extra \ at the end */
1352 ++Paths;
1353 lpszVolumePathNames[CharsInOutput] = L'\\';
1354 ++CharsInOutput;
1355 /* Make sure we don't overflow */
1356 if (CharsInOutput == cchBufferLength)
1357 {
1358 break;
1359 }
1360 }
1361
1362 /* Copy the char to the caller
1363 * So, in case we're in the end of a path, we wrote two chars to
1364 * the output buffer: \\ and \0
1365 */
1366 lpszVolumePathNames[CharsInOutput] = MultiSz[CharsInMgr];
1367 }
1368 }
1369
1370 /* If output buffer was too small (ie, we couldn't parse all the input buffer) */
1371 if (CharsInMgr < VolumePaths->MultiSzLength / sizeof(WCHAR) - 1)
1372 {
1373 /* Keep looping on it, to count the number of extra \ that will be required
1374 * So that on the next call, caller can allocate enough space
1375 */
1376 for (; CharsInMgr < VolumePaths->MultiSzLength / sizeof(WCHAR) - 1; ++CharsInMgr)
1377 {
1378 if (MultiSz[CharsInMgr] == UNICODE_NULL)
1379 {
1380 ++Paths;
1381 }
1382 }
1383 }
1384
1385 /* If we couldn't write as much as we wanted to the output buffer
1386 * This handles the case where we could write everything excepted the
1387 * terminating \0 for multi SZ
1388 */
1389 if (CharsInOutput >= cchBufferLength)
1390 {
1391 /* Fail and set appropriate error code */
1392 Ret = FALSE;
1393 SetLastError(ERROR_MORE_DATA);
1394 /* If caller wants to know how many chars to allocate, return it */
1395 if (lpcchReturnLength != NULL)
1396 {
1397 /* It's amount of extra \ + number of chars in MultiSz (including double \0) */
1398 *lpcchReturnLength = Paths + (VolumePaths->MultiSzLength / sizeof(WCHAR));
1399 }
1400 }
1401 else
1402 {
1403 /* It succeed so terminate the multi SZ (second \0) */
1404 lpszVolumePathNames[CharsInOutput] = UNICODE_NULL;
1405 Ret = TRUE;
1406
1407 /* If caller wants the amount of chars written, return it */
1408 if (lpcchReturnLength != NULL)
1409 {
1410 /* Including the terminating \0 we just added */
1411 *lpcchReturnLength = CharsInOutput + 1;
1412 }
1413 }
1414
1415 /* Free last bits */
1416 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths);
1417
1418 /* And return */
1419 return Ret;
1420 }
1421
1422 /* EOF */