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