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
GetVolumeInformationA(IN LPCSTR lpRootPathName,IN LPSTR lpVolumeNameBuffer,IN DWORD nVolumeNameSize,OUT LPDWORD lpVolumeSerialNumber OPTIONAL,OUT LPDWORD lpMaximumComponentLength OPTIONAL,OUT LPDWORD lpFileSystemFlags OPTIONAL,OUT LPSTR lpFileSystemNameBuffer OPTIONAL,IN DWORD nFileSystemNameSize)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
IsThisARootDirectory(IN HANDLE VolumeHandle,IN PUNICODE_STRING NtPathName)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
GetVolumeInformationW(IN LPCWSTR lpRootPathName,IN LPWSTR lpVolumeNameBuffer,IN DWORD nVolumeNameSize,OUT LPDWORD lpVolumeSerialNumber OPTIONAL,OUT LPDWORD lpMaximumComponentLength OPTIONAL,OUT LPDWORD lpFileSystemFlags OPTIONAL,OUT LPWSTR lpFileSystemNameBuffer OPTIONAL,IN DWORD nFileSystemNameSize)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
SetVolumeLabelA(IN LPCSTR lpRootPathName,IN LPCSTR lpVolumeName OPTIONAL)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
SetVolumeLabelW(IN LPCWSTR lpRootPathName,IN LPCWSTR lpVolumeName OPTIONAL)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
FindFirstVolumeW(IN LPWSTR volume,IN DWORD len)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
FindFirstVolumeA(IN LPSTR volume,IN DWORD len)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
FindVolumeClose(IN HANDLE hFindVolume)741 FindVolumeClose(IN HANDLE hFindVolume)
742 {
743 return RtlFreeHeap(RtlGetProcessHeap(), 0, hFindVolume);
744 }
745
746 /*
747 * @implemented
748 */
749 BOOL
750 WINAPI
GetVolumePathNameA(IN LPCSTR lpszFileName,IN LPSTR lpszVolumePathName,IN DWORD cchBufferLength)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
GetVolumePathNameW(IN LPCWSTR lpszFileName,IN LPWSTR lpszVolumePathName,IN DWORD cchBufferLength)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
FindNextVolumeA(IN HANDLE handle,IN LPSTR volume,IN DWORD len)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
FindNextVolumeW(IN HANDLE handle,IN LPWSTR volume,IN DWORD len)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
GetVolumePathNamesForVolumeNameA(IN LPCSTR lpszVolumeName,IN LPSTR lpszVolumePathNames,IN DWORD cchBufferLength,OUT PDWORD lpcchReturnLength)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
GetVolumePathNamesForVolumeNameW(IN LPCWSTR lpszVolumeName,IN LPWSTR lpszVolumePathNames,IN DWORD cchBufferLength,OUT PDWORD lpcchReturnLength)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