- Update to r53061
[reactos.git] / dll / win32 / kernel32 / client / file / volume.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/kernel32/file/volume.c
6 * PURPOSE: File volume functions
7 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
8 * Erik Bos, Alexandre Julliard :
9 * GetLogicalDriveStringsA,
10 * GetLogicalDriveStringsW, GetLogicalDrives
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 DEBUG_CHANNEL(kernel32file);
26
27 #define MAX_DOS_DRIVES 26
28
29
30 static HANDLE
31 InternalOpenDirW(LPCWSTR DirName,
32 BOOLEAN Write)
33 {
34 UNICODE_STRING NtPathU;
35 OBJECT_ATTRIBUTES ObjectAttributes;
36 NTSTATUS errCode;
37 IO_STATUS_BLOCK IoStatusBlock;
38 HANDLE hFile;
39
40 if (!RtlDosPathNameToNtPathName_U(DirName,
41 &NtPathU,
42 NULL,
43 NULL))
44 {
45 WARN("Invalid path\n");
46 SetLastError(ERROR_BAD_PATHNAME);
47 return INVALID_HANDLE_VALUE;
48 }
49
50 InitializeObjectAttributes(&ObjectAttributes,
51 &NtPathU,
52 OBJ_CASE_INSENSITIVE,
53 NULL,
54 NULL);
55
56 errCode = NtCreateFile (&hFile,
57 Write ? FILE_GENERIC_WRITE : FILE_GENERIC_READ,
58 &ObjectAttributes,
59 &IoStatusBlock,
60 NULL,
61 0,
62 FILE_SHARE_READ|FILE_SHARE_WRITE,
63 FILE_OPEN,
64 0,
65 NULL,
66 0);
67
68 RtlFreeHeap(RtlGetProcessHeap(),
69 0,
70 NtPathU.Buffer);
71
72 if (!NT_SUCCESS(errCode))
73 {
74 BaseSetLastNTError (errCode);
75 return INVALID_HANDLE_VALUE;
76 }
77 return hFile;
78 }
79
80
81 /*
82 * @implemented
83 */
84 /* Synced to Wine-2008/12/28 */
85 DWORD WINAPI
86 GetLogicalDriveStringsA(DWORD nBufferLength,
87 LPSTR lpBuffer)
88 {
89 DWORD drive, count;
90 DWORD dwDriveMap;
91 LPSTR p;
92
93 dwDriveMap = GetLogicalDrives();
94
95 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
96 {
97 if (dwDriveMap & (1<<drive))
98 count++;
99 }
100
101
102 if ((count * 4) + 1 > nBufferLength) return ((count * 4) + 1);
103
104 p = lpBuffer;
105
106 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
107 if (dwDriveMap & (1<<drive))
108 {
109 *p++ = 'A' + (UCHAR)drive;
110 *p++ = ':';
111 *p++ = '\\';
112 *p++ = '\0';
113 }
114 *p = '\0';
115
116 return (count * 4);
117 }
118
119
120 /*
121 * @implemented
122 */
123 /* Synced to Wine-2008/12/28 */
124 DWORD WINAPI
125 GetLogicalDriveStringsW(DWORD nBufferLength,
126 LPWSTR lpBuffer)
127 {
128 DWORD drive, count;
129 DWORD dwDriveMap;
130 LPWSTR p;
131
132 dwDriveMap = GetLogicalDrives();
133
134 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
135 {
136 if (dwDriveMap & (1<<drive))
137 count++;
138 }
139
140 if ((count * 4) + 1 > nBufferLength) return ((count * 4) + 1);
141
142 p = lpBuffer;
143 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
144 if (dwDriveMap & (1<<drive))
145 {
146 *p++ = (WCHAR)('A' + drive);
147 *p++ = (WCHAR)':';
148 *p++ = (WCHAR)'\\';
149 *p++ = (WCHAR)'\0';
150 }
151 *p = (WCHAR)'\0';
152
153 return (count * 4);
154 }
155
156
157 /*
158 * @implemented
159 */
160 /* Synced to Wine-? */
161 DWORD WINAPI
162 GetLogicalDrives(VOID)
163 {
164 NTSTATUS Status;
165 PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo;
166
167 /* Get the Device Map for this Process */
168 Status = NtQueryInformationProcess(NtCurrentProcess(),
169 ProcessDeviceMap,
170 &ProcessDeviceMapInfo,
171 sizeof(ProcessDeviceMapInfo),
172 NULL);
173
174 /* Return the Drive Map */
175 if (!NT_SUCCESS(Status))
176 {
177 BaseSetLastNTError(Status);
178 return 0;
179 }
180
181 return ProcessDeviceMapInfo.Query.DriveMap;
182 }
183
184
185 /*
186 * @implemented
187 */
188 BOOL WINAPI
189 GetDiskFreeSpaceA (
190 LPCSTR lpRootPathName,
191 LPDWORD lpSectorsPerCluster,
192 LPDWORD lpBytesPerSector,
193 LPDWORD lpNumberOfFreeClusters,
194 LPDWORD lpTotalNumberOfClusters
195 )
196 {
197 PWCHAR RootPathNameW=NULL;
198
199 if (lpRootPathName)
200 {
201 if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE)))
202 return FALSE;
203 }
204
205 return GetDiskFreeSpaceW (RootPathNameW,
206 lpSectorsPerCluster,
207 lpBytesPerSector,
208 lpNumberOfFreeClusters,
209 lpTotalNumberOfClusters);
210 }
211
212
213 /*
214 * @implemented
215 */
216 BOOL WINAPI
217 GetDiskFreeSpaceW(
218 LPCWSTR lpRootPathName,
219 LPDWORD lpSectorsPerCluster,
220 LPDWORD lpBytesPerSector,
221 LPDWORD lpNumberOfFreeClusters,
222 LPDWORD lpTotalNumberOfClusters
223 )
224 {
225 FILE_FS_SIZE_INFORMATION FileFsSize;
226 IO_STATUS_BLOCK IoStatusBlock;
227 WCHAR RootPathName[MAX_PATH];
228 HANDLE hFile;
229 NTSTATUS errCode;
230
231 if (lpRootPathName)
232 {
233 wcsncpy (RootPathName, lpRootPathName, 3);
234 }
235 else
236 {
237 GetCurrentDirectoryW (MAX_PATH, RootPathName);
238 }
239 RootPathName[3] = 0;
240
241 hFile = InternalOpenDirW(RootPathName, FALSE);
242 if (INVALID_HANDLE_VALUE == hFile)
243 {
244 SetLastError(ERROR_PATH_NOT_FOUND);
245 return FALSE;
246 }
247
248 errCode = NtQueryVolumeInformationFile(hFile,
249 &IoStatusBlock,
250 &FileFsSize,
251 sizeof(FILE_FS_SIZE_INFORMATION),
252 FileFsSizeInformation);
253 if (!NT_SUCCESS(errCode))
254 {
255 CloseHandle(hFile);
256 BaseSetLastNTError (errCode);
257 return FALSE;
258 }
259
260 if (lpSectorsPerCluster)
261 *lpSectorsPerCluster = FileFsSize.SectorsPerAllocationUnit;
262 if (lpBytesPerSector)
263 *lpBytesPerSector = FileFsSize.BytesPerSector;
264 if (lpNumberOfFreeClusters)
265 *lpNumberOfFreeClusters = FileFsSize.AvailableAllocationUnits.u.LowPart;
266 if (lpTotalNumberOfClusters)
267 *lpTotalNumberOfClusters = FileFsSize.TotalAllocationUnits.u.LowPart;
268 CloseHandle(hFile);
269
270 return TRUE;
271 }
272
273
274 /*
275 * @implemented
276 */
277 BOOL WINAPI
278 GetDiskFreeSpaceExA (
279 LPCSTR lpDirectoryName OPTIONAL,
280 PULARGE_INTEGER lpFreeBytesAvailableToCaller,
281 PULARGE_INTEGER lpTotalNumberOfBytes,
282 PULARGE_INTEGER lpTotalNumberOfFreeBytes
283 )
284 {
285 PWCHAR DirectoryNameW=NULL;
286
287 if (lpDirectoryName)
288 {
289 if (!(DirectoryNameW = FilenameA2W(lpDirectoryName, FALSE)))
290 return FALSE;
291 }
292
293 return GetDiskFreeSpaceExW (DirectoryNameW ,
294 lpFreeBytesAvailableToCaller,
295 lpTotalNumberOfBytes,
296 lpTotalNumberOfFreeBytes);
297 }
298
299
300 /*
301 * @implemented
302 */
303 BOOL WINAPI
304 GetDiskFreeSpaceExW(
305 LPCWSTR lpDirectoryName OPTIONAL,
306 PULARGE_INTEGER lpFreeBytesAvailableToCaller,
307 PULARGE_INTEGER lpTotalNumberOfBytes,
308 PULARGE_INTEGER lpTotalNumberOfFreeBytes
309 )
310 {
311 union
312 {
313 FILE_FS_SIZE_INFORMATION FsSize;
314 FILE_FS_FULL_SIZE_INFORMATION FsFullSize;
315 } FsInfo;
316 IO_STATUS_BLOCK IoStatusBlock;
317 ULARGE_INTEGER BytesPerCluster;
318 HANDLE hFile;
319 NTSTATUS Status;
320
321 if (lpDirectoryName == NULL)
322 lpDirectoryName = L"\\";
323
324 hFile = InternalOpenDirW(lpDirectoryName, FALSE);
325 if (INVALID_HANDLE_VALUE == hFile)
326 {
327 return FALSE;
328 }
329
330 if (lpFreeBytesAvailableToCaller != NULL || lpTotalNumberOfBytes != NULL)
331 {
332 /* To get the free space available to the user associated with the
333 current thread, try FileFsFullSizeInformation. If this is not
334 supported by the file system, fall back to FileFsSize */
335
336 Status = NtQueryVolumeInformationFile(hFile,
337 &IoStatusBlock,
338 &FsInfo.FsFullSize,
339 sizeof(FsInfo.FsFullSize),
340 FileFsFullSizeInformation);
341
342 if (NT_SUCCESS(Status))
343 {
344 /* Close the handle before returning data
345 to avoid a handle leak in case of a fault! */
346 CloseHandle(hFile);
347
348 BytesPerCluster.QuadPart =
349 FsInfo.FsFullSize.BytesPerSector * FsInfo.FsFullSize.SectorsPerAllocationUnit;
350
351 if (lpFreeBytesAvailableToCaller != NULL)
352 {
353 lpFreeBytesAvailableToCaller->QuadPart =
354 BytesPerCluster.QuadPart * FsInfo.FsFullSize.CallerAvailableAllocationUnits.QuadPart;
355 }
356
357 if (lpTotalNumberOfBytes != NULL)
358 {
359 lpTotalNumberOfBytes->QuadPart =
360 BytesPerCluster.QuadPart * FsInfo.FsFullSize.TotalAllocationUnits.QuadPart;
361 }
362
363 if (lpTotalNumberOfFreeBytes != NULL)
364 {
365 lpTotalNumberOfFreeBytes->QuadPart =
366 BytesPerCluster.QuadPart * FsInfo.FsFullSize.ActualAvailableAllocationUnits.QuadPart;
367 }
368
369 return TRUE;
370 }
371 }
372
373 Status = NtQueryVolumeInformationFile(hFile,
374 &IoStatusBlock,
375 &FsInfo.FsSize,
376 sizeof(FsInfo.FsSize),
377 FileFsSizeInformation);
378
379 /* Close the handle before returning data
380 to avoid a handle leak in case of a fault! */
381 CloseHandle(hFile);
382
383 if (!NT_SUCCESS(Status))
384 {
385 BaseSetLastNTError (Status);
386 return FALSE;
387 }
388
389 BytesPerCluster.QuadPart =
390 FsInfo.FsSize.BytesPerSector * FsInfo.FsSize.SectorsPerAllocationUnit;
391
392 if (lpFreeBytesAvailableToCaller)
393 {
394 lpFreeBytesAvailableToCaller->QuadPart =
395 BytesPerCluster.QuadPart * FsInfo.FsSize.AvailableAllocationUnits.QuadPart;
396 }
397
398 if (lpTotalNumberOfBytes)
399 {
400 lpTotalNumberOfBytes->QuadPart =
401 BytesPerCluster.QuadPart * FsInfo.FsSize.TotalAllocationUnits.QuadPart;
402 }
403
404 if (lpTotalNumberOfFreeBytes)
405 {
406 lpTotalNumberOfFreeBytes->QuadPart =
407 BytesPerCluster.QuadPart * FsInfo.FsSize.AvailableAllocationUnits.QuadPart;
408 }
409
410 return TRUE;
411 }
412
413
414 /*
415 * @implemented
416 */
417 UINT WINAPI
418 GetDriveTypeA(LPCSTR lpRootPathName)
419 {
420 PWCHAR RootPathNameW;
421
422 if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE)))
423 return DRIVE_UNKNOWN;
424
425 return GetDriveTypeW(RootPathNameW);
426 }
427
428
429 /*
430 * @implemented
431 */
432 UINT WINAPI
433 GetDriveTypeW(LPCWSTR lpRootPathName)
434 {
435 FILE_FS_DEVICE_INFORMATION FileFsDevice;
436 IO_STATUS_BLOCK IoStatusBlock;
437
438 HANDLE hFile;
439 NTSTATUS errCode;
440
441 hFile = InternalOpenDirW(lpRootPathName, FALSE);
442 if (hFile == INVALID_HANDLE_VALUE)
443 {
444 return DRIVE_NO_ROOT_DIR; /* According to WINE regression tests */
445 }
446
447 errCode = NtQueryVolumeInformationFile (hFile,
448 &IoStatusBlock,
449 &FileFsDevice,
450 sizeof(FILE_FS_DEVICE_INFORMATION),
451 FileFsDeviceInformation);
452 if (!NT_SUCCESS(errCode))
453 {
454 CloseHandle(hFile);
455 BaseSetLastNTError (errCode);
456 return 0;
457 }
458 CloseHandle(hFile);
459
460 switch (FileFsDevice.DeviceType)
461 {
462 case FILE_DEVICE_CD_ROM:
463 case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
464 return DRIVE_CDROM;
465 case FILE_DEVICE_VIRTUAL_DISK:
466 return DRIVE_RAMDISK;
467 case FILE_DEVICE_NETWORK_FILE_SYSTEM:
468 return DRIVE_REMOTE;
469 case FILE_DEVICE_DISK:
470 case FILE_DEVICE_DISK_FILE_SYSTEM:
471 if (FileFsDevice.Characteristics & FILE_REMOTE_DEVICE)
472 return DRIVE_REMOTE;
473 if (FileFsDevice.Characteristics & FILE_REMOVABLE_MEDIA)
474 return DRIVE_REMOVABLE;
475 return DRIVE_FIXED;
476 }
477
478 ERR("Returning DRIVE_UNKNOWN for device type %d\n", FileFsDevice.DeviceType);
479
480 return DRIVE_UNKNOWN;
481 }
482
483
484 /*
485 * @implemented
486 */
487 BOOL WINAPI
488 GetVolumeInformationA(
489 LPCSTR lpRootPathName,
490 LPSTR lpVolumeNameBuffer,
491 DWORD nVolumeNameSize,
492 LPDWORD lpVolumeSerialNumber,
493 LPDWORD lpMaximumComponentLength,
494 LPDWORD lpFileSystemFlags,
495 LPSTR lpFileSystemNameBuffer,
496 DWORD nFileSystemNameSize
497 )
498 {
499 UNICODE_STRING FileSystemNameU;
500 UNICODE_STRING VolumeNameU = { 0, 0, NULL };
501 ANSI_STRING VolumeName;
502 ANSI_STRING FileSystemName;
503 PWCHAR RootPathNameW;
504 BOOL Result;
505
506 if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE)))
507 return FALSE;
508
509 if (lpVolumeNameBuffer)
510 {
511 VolumeNameU.MaximumLength = (USHORT)nVolumeNameSize * sizeof(WCHAR);
512 VolumeNameU.Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
513 0,
514 VolumeNameU.MaximumLength);
515 if (VolumeNameU.Buffer == NULL)
516 {
517 goto FailNoMem;
518 }
519 }
520
521 if (lpFileSystemNameBuffer)
522 {
523 FileSystemNameU.Length = 0;
524 FileSystemNameU.MaximumLength = (USHORT)nFileSystemNameSize * sizeof(WCHAR);
525 FileSystemNameU.Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
526 0,
527 FileSystemNameU.MaximumLength);
528 if (FileSystemNameU.Buffer == NULL)
529 {
530 if (VolumeNameU.Buffer != NULL)
531 {
532 RtlFreeHeap(RtlGetProcessHeap(),
533 0,
534 VolumeNameU.Buffer);
535 }
536
537 FailNoMem:
538 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
539 return FALSE;
540 }
541 }
542
543 Result = GetVolumeInformationW (RootPathNameW,
544 lpVolumeNameBuffer ? VolumeNameU.Buffer : NULL,
545 nVolumeNameSize,
546 lpVolumeSerialNumber,
547 lpMaximumComponentLength,
548 lpFileSystemFlags,
549 lpFileSystemNameBuffer ? FileSystemNameU.Buffer : NULL,
550 nFileSystemNameSize);
551
552 if (Result)
553 {
554 if (lpVolumeNameBuffer)
555 {
556 VolumeNameU.Length = wcslen(VolumeNameU.Buffer) * sizeof(WCHAR);
557 VolumeName.Length = 0;
558 VolumeName.MaximumLength = (USHORT)nVolumeNameSize;
559 VolumeName.Buffer = lpVolumeNameBuffer;
560 }
561
562 if (lpFileSystemNameBuffer)
563 {
564 FileSystemNameU.Length = wcslen(FileSystemNameU.Buffer) * sizeof(WCHAR);
565 FileSystemName.Length = 0;
566 FileSystemName.MaximumLength = (USHORT)nFileSystemNameSize;
567 FileSystemName.Buffer = lpFileSystemNameBuffer;
568 }
569
570 /* convert unicode strings to ansi (or oem) */
571 if (bIsFileApiAnsi)
572 {
573 if (lpVolumeNameBuffer)
574 {
575 RtlUnicodeStringToAnsiString (&VolumeName,
576 &VolumeNameU,
577 FALSE);
578 }
579 if (lpFileSystemNameBuffer)
580 {
581 RtlUnicodeStringToAnsiString (&FileSystemName,
582 &FileSystemNameU,
583 FALSE);
584 }
585 }
586 else
587 {
588 if (lpVolumeNameBuffer)
589 {
590 RtlUnicodeStringToOemString (&VolumeName,
591 &VolumeNameU,
592 FALSE);
593 }
594 if (lpFileSystemNameBuffer)
595 {
596 RtlUnicodeStringToOemString (&FileSystemName,
597 &FileSystemNameU,
598 FALSE);
599 }
600 }
601 }
602
603 if (lpVolumeNameBuffer)
604 {
605 RtlFreeHeap (RtlGetProcessHeap (),
606 0,
607 VolumeNameU.Buffer);
608 }
609 if (lpFileSystemNameBuffer)
610 {
611 RtlFreeHeap (RtlGetProcessHeap (),
612 0,
613 FileSystemNameU.Buffer);
614 }
615
616 return Result;
617 }
618
619 #define FS_VOLUME_BUFFER_SIZE (MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_VOLUME_INFORMATION))
620
621 #define FS_ATTRIBUTE_BUFFER_SIZE (MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_ATTRIBUTE_INFORMATION))
622
623 /*
624 * @implemented
625 */
626 BOOL WINAPI
627 GetVolumeInformationW(
628 LPCWSTR lpRootPathName,
629 LPWSTR lpVolumeNameBuffer,
630 DWORD nVolumeNameSize,
631 LPDWORD lpVolumeSerialNumber,
632 LPDWORD lpMaximumComponentLength,
633 LPDWORD lpFileSystemFlags,
634 LPWSTR lpFileSystemNameBuffer,
635 DWORD nFileSystemNameSize
636 )
637 {
638 PFILE_FS_VOLUME_INFORMATION FileFsVolume;
639 PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute;
640 IO_STATUS_BLOCK IoStatusBlock;
641 WCHAR RootPathName[MAX_PATH];
642 UCHAR Buffer[max(FS_VOLUME_BUFFER_SIZE, FS_ATTRIBUTE_BUFFER_SIZE)];
643
644 HANDLE hFile;
645 NTSTATUS errCode;
646
647 FileFsVolume = (PFILE_FS_VOLUME_INFORMATION)Buffer;
648 FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer;
649
650 TRACE("FileFsVolume %p\n", FileFsVolume);
651 TRACE("FileFsAttribute %p\n", FileFsAttribute);
652
653 if (!lpRootPathName || !wcscmp(lpRootPathName, L""))
654 {
655 GetCurrentDirectoryW (MAX_PATH, RootPathName);
656 }
657 else
658 {
659 wcsncpy (RootPathName, lpRootPathName, 3);
660 }
661 RootPathName[3] = 0;
662
663 hFile = InternalOpenDirW(RootPathName, FALSE);
664 if (hFile == INVALID_HANDLE_VALUE)
665 {
666 return FALSE;
667 }
668
669 TRACE("hFile: %x\n", hFile);
670 errCode = NtQueryVolumeInformationFile(hFile,
671 &IoStatusBlock,
672 FileFsVolume,
673 FS_VOLUME_BUFFER_SIZE,
674 FileFsVolumeInformation);
675 if ( !NT_SUCCESS(errCode) )
676 {
677 WARN("Status: %x\n", errCode);
678 CloseHandle(hFile);
679 BaseSetLastNTError (errCode);
680 return FALSE;
681 }
682
683 if (lpVolumeSerialNumber)
684 *lpVolumeSerialNumber = FileFsVolume->VolumeSerialNumber;
685
686 if (lpVolumeNameBuffer)
687 {
688 if (nVolumeNameSize * sizeof(WCHAR) >= FileFsVolume->VolumeLabelLength + sizeof(WCHAR))
689 {
690 memcpy(lpVolumeNameBuffer,
691 FileFsVolume->VolumeLabel,
692 FileFsVolume->VolumeLabelLength);
693 lpVolumeNameBuffer[FileFsVolume->VolumeLabelLength / sizeof(WCHAR)] = 0;
694 }
695 else
696 {
697 CloseHandle(hFile);
698 SetLastError(ERROR_MORE_DATA);
699 return FALSE;
700 }
701 }
702
703 errCode = NtQueryVolumeInformationFile (hFile,
704 &IoStatusBlock,
705 FileFsAttribute,
706 FS_ATTRIBUTE_BUFFER_SIZE,
707 FileFsAttributeInformation);
708 CloseHandle(hFile);
709 if (!NT_SUCCESS(errCode))
710 {
711 WARN("Status: %x\n", errCode);
712 BaseSetLastNTError (errCode);
713 return FALSE;
714 }
715
716 if (lpFileSystemFlags)
717 *lpFileSystemFlags = FileFsAttribute->FileSystemAttributes;
718 if (lpMaximumComponentLength)
719 *lpMaximumComponentLength = FileFsAttribute->MaximumComponentNameLength;
720 if (lpFileSystemNameBuffer)
721 {
722 if (nFileSystemNameSize * sizeof(WCHAR) >= FileFsAttribute->FileSystemNameLength + sizeof(WCHAR))
723 {
724 memcpy(lpFileSystemNameBuffer,
725 FileFsAttribute->FileSystemName,
726 FileFsAttribute->FileSystemNameLength);
727 lpFileSystemNameBuffer[FileFsAttribute->FileSystemNameLength / sizeof(WCHAR)] = 0;
728 }
729 else
730 {
731 SetLastError(ERROR_MORE_DATA);
732 return FALSE;
733 }
734 }
735 return TRUE;
736 }
737
738
739 /*
740 * @implemented
741 */
742 BOOL
743 WINAPI
744 SetVolumeLabelA (
745 LPCSTR lpRootPathName,
746 LPCSTR lpVolumeName /* NULL if deleting label */
747 )
748 {
749 PWCHAR RootPathNameW;
750 PWCHAR VolumeNameW = NULL;
751 BOOL Result;
752
753 if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE)))
754 return FALSE;
755
756 if (lpVolumeName)
757 {
758 if (!(VolumeNameW = FilenameA2W(lpVolumeName, TRUE)))
759 return FALSE;
760 }
761
762 Result = SetVolumeLabelW (RootPathNameW,
763 VolumeNameW);
764
765 if (VolumeNameW)
766 {
767 RtlFreeHeap (RtlGetProcessHeap (),
768 0,
769 VolumeNameW );
770 }
771
772 return Result;
773 }
774
775
776 /*
777 * @implemented
778 */
779 BOOL WINAPI
780 SetVolumeLabelW(
781 LPCWSTR lpRootPathName,
782 LPCWSTR lpVolumeName /* NULL if deleting label */
783 )
784 {
785 PFILE_FS_LABEL_INFORMATION LabelInfo;
786 IO_STATUS_BLOCK IoStatusBlock;
787 ULONG LabelLength;
788 HANDLE hFile;
789 NTSTATUS Status;
790
791 LabelLength = wcslen(lpVolumeName) * sizeof(WCHAR);
792 LabelInfo = RtlAllocateHeap(RtlGetProcessHeap(),
793 0,
794 sizeof(FILE_FS_LABEL_INFORMATION) +
795 LabelLength);
796 if (LabelInfo == NULL)
797 {
798 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
799 return FALSE;
800 }
801 LabelInfo->VolumeLabelLength = LabelLength;
802 memcpy(LabelInfo->VolumeLabel,
803 lpVolumeName,
804 LabelLength);
805
806 hFile = InternalOpenDirW(lpRootPathName, TRUE);
807 if (INVALID_HANDLE_VALUE == hFile)
808 {
809 RtlFreeHeap(RtlGetProcessHeap(),
810 0,
811 LabelInfo);
812 return FALSE;
813 }
814
815 Status = NtSetVolumeInformationFile(hFile,
816 &IoStatusBlock,
817 LabelInfo,
818 sizeof(FILE_FS_LABEL_INFORMATION) +
819 LabelLength,
820 FileFsLabelInformation);
821
822 RtlFreeHeap(RtlGetProcessHeap(),
823 0,
824 LabelInfo);
825
826 if (!NT_SUCCESS(Status))
827 {
828 WARN("Status: %x\n", Status);
829 CloseHandle(hFile);
830 BaseSetLastNTError(Status);
831 return FALSE;
832 }
833
834 CloseHandle(hFile);
835 return TRUE;
836 }
837
838 /**
839 * @name GetVolumeNameForVolumeMountPointW
840 *
841 * Return an unique volume name for a drive root or mount point.
842 *
843 * @param VolumeMountPoint
844 * Pointer to string that contains either root drive name or
845 * mount point name.
846 * @param VolumeName
847 * Pointer to buffer that is filled with resulting unique
848 * volume name on success.
849 * @param VolumeNameLength
850 * Size of VolumeName buffer in TCHARs.
851 *
852 * @return
853 * TRUE when the function succeeds and the VolumeName buffer is filled,
854 * FALSE otherwise.
855 */
856
857 BOOL WINAPI
858 GetVolumeNameForVolumeMountPointW(
859 IN LPCWSTR VolumeMountPoint,
860 OUT LPWSTR VolumeName,
861 IN DWORD VolumeNameLength)
862 {
863 UNICODE_STRING NtFileName;
864 OBJECT_ATTRIBUTES ObjectAttributes;
865 HANDLE FileHandle;
866 IO_STATUS_BLOCK Iosb;
867 ULONG BufferLength;
868 PMOUNTDEV_NAME MountDevName;
869 PMOUNTMGR_MOUNT_POINT MountPoint;
870 ULONG MountPointSize;
871 PMOUNTMGR_MOUNT_POINTS MountPoints;
872 ULONG Index;
873 PUCHAR SymbolicLinkName;
874 BOOL Result;
875 NTSTATUS Status;
876
877 if (!VolumeMountPoint || !VolumeMountPoint[0])
878 {
879 SetLastError(ERROR_PATH_NOT_FOUND);
880 return FALSE;
881 }
882
883 /*
884 * First step is to convert the passed volume mount point name to
885 * an NT acceptable name.
886 */
887
888 if (!RtlDosPathNameToNtPathName_U(VolumeMountPoint, &NtFileName, NULL, NULL))
889 {
890 SetLastError(ERROR_PATH_NOT_FOUND);
891 return FALSE;
892 }
893
894 if (NtFileName.Length > sizeof(WCHAR) &&
895 NtFileName.Buffer[(NtFileName.Length / sizeof(WCHAR)) - 1] == '\\')
896 {
897 NtFileName.Length -= sizeof(WCHAR);
898 }
899
900 /*
901 * Query mount point device name which we will later use for determining
902 * the volume name.
903 */
904
905 InitializeObjectAttributes(&ObjectAttributes, &NtFileName, 0, NULL, NULL);
906 Status = NtOpenFile(&FileHandle, FILE_READ_ATTRIBUTES | SYNCHRONIZE,
907 &ObjectAttributes, &Iosb,
908 FILE_SHARE_READ | FILE_SHARE_WRITE,
909 FILE_SYNCHRONOUS_IO_NONALERT);
910 RtlFreeUnicodeString(&NtFileName);
911 if (!NT_SUCCESS(Status))
912 {
913 BaseSetLastNTError(Status);
914 return FALSE;
915 }
916
917 BufferLength = sizeof(MOUNTDEV_NAME) + 50 * sizeof(WCHAR);
918 do
919 {
920 MountDevName = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
921 if (MountDevName == NULL)
922 {
923 NtClose(FileHandle);
924 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
925 return FALSE;
926 }
927
928 Status = NtDeviceIoControlFile(FileHandle, NULL, NULL, NULL, &Iosb,
929 IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
930 NULL, 0, MountDevName, BufferLength);
931 if (!NT_SUCCESS(Status))
932 {
933 RtlFreeHeap(GetProcessHeap(), 0, MountDevName);
934 if (Status == STATUS_BUFFER_OVERFLOW)
935 {
936 BufferLength = sizeof(MOUNTDEV_NAME) + MountDevName->NameLength;
937 continue;
938 }
939 else
940 {
941 NtClose(FileHandle);
942 BaseSetLastNTError(Status);
943 return FALSE;
944 }
945 }
946 }
947 while (!NT_SUCCESS(Status));
948
949 NtClose(FileHandle);
950
951 /*
952 * Get the mount point information from mount manager.
953 */
954
955 MountPointSize = MountDevName->NameLength + sizeof(MOUNTMGR_MOUNT_POINT);
956 MountPoint = RtlAllocateHeap(GetProcessHeap(), 0, MountPointSize);
957 if (MountPoint == NULL)
958 {
959 RtlFreeHeap(GetProcessHeap(), 0, MountDevName);
960 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
961 return FALSE;
962 }
963 RtlZeroMemory(MountPoint, sizeof(MOUNTMGR_MOUNT_POINT));
964 MountPoint->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
965 MountPoint->DeviceNameLength = MountDevName->NameLength;
966 RtlCopyMemory(MountPoint + 1, MountDevName->Name, MountDevName->NameLength);
967 RtlFreeHeap(RtlGetProcessHeap(), 0, MountDevName);
968
969 RtlInitUnicodeString(&NtFileName, L"\\??\\MountPointManager");
970 InitializeObjectAttributes(&ObjectAttributes, &NtFileName, 0, NULL, NULL);
971 Status = NtOpenFile(&FileHandle, FILE_GENERIC_READ | SYNCHRONIZE, &ObjectAttributes,
972 &Iosb, FILE_SHARE_READ | FILE_SHARE_WRITE,
973 FILE_SYNCHRONOUS_IO_NONALERT);
974 if (!NT_SUCCESS(Status))
975 {
976 BaseSetLastNTError(Status);
977 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
978 return FALSE;
979 }
980
981 BufferLength = sizeof(MOUNTMGR_MOUNT_POINTS);
982 do
983 {
984 MountPoints = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
985 if (MountPoints == NULL)
986 {
987 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
988 NtClose(FileHandle);
989 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
990 return FALSE;
991 }
992
993 Status = NtDeviceIoControlFile(FileHandle, NULL, NULL, NULL, &Iosb,
994 IOCTL_MOUNTMGR_QUERY_POINTS,
995 MountPoint, MountPointSize,
996 MountPoints, BufferLength);
997 if (!NT_SUCCESS(Status))
998 {
999 if (Status == STATUS_BUFFER_OVERFLOW)
1000 {
1001 BufferLength = MountPoints->Size;
1002 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
1003 continue;
1004 }
1005 else if (!NT_SUCCESS(Status))
1006 {
1007 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
1008 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
1009 NtClose(FileHandle);
1010 BaseSetLastNTError(Status);
1011 return FALSE;
1012 }
1013 }
1014 }
1015 while (!NT_SUCCESS(Status));
1016
1017 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
1018 NtClose(FileHandle);
1019
1020 /*
1021 * Now we've gathered info about all mount points mapped to our device, so
1022 * select the correct one and copy it into the output buffer.
1023 */
1024
1025 for (Index = 0; Index < MountPoints->NumberOfMountPoints; Index++)
1026 {
1027 MountPoint = MountPoints->MountPoints + Index;
1028 SymbolicLinkName = (PUCHAR)MountPoints + MountPoint->SymbolicLinkNameOffset;
1029
1030 /*
1031 * Check for "\\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\"
1032 * (with the last slash being optional) style symbolic links.
1033 */
1034
1035 if (MountPoint->SymbolicLinkNameLength == 48 * sizeof(WCHAR) ||
1036 (MountPoint->SymbolicLinkNameLength == 49 * sizeof(WCHAR) &&
1037 SymbolicLinkName[48] == L'\\'))
1038 {
1039 if (RtlCompareMemory(SymbolicLinkName, L"\\??\\Volume{",
1040 11 * sizeof(WCHAR)) == 11 * sizeof(WCHAR) &&
1041 SymbolicLinkName[19] == L'-' && SymbolicLinkName[24] == L'-' &&
1042 SymbolicLinkName[29] == L'-' && SymbolicLinkName[34] == L'-' &&
1043 SymbolicLinkName[47] == L'}')
1044 {
1045 if (VolumeNameLength >= MountPoint->SymbolicLinkNameLength / sizeof(WCHAR))
1046 {
1047 RtlCopyMemory(VolumeName,
1048 (PUCHAR)MountPoints + MountPoint->SymbolicLinkNameOffset,
1049 MountPoint->SymbolicLinkNameLength);
1050 VolumeName[1] = L'\\';
1051 Result = TRUE;
1052 }
1053 else
1054 {
1055 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1056 Result = FALSE;
1057 }
1058
1059 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
1060
1061 return Result;
1062 }
1063 }
1064 }
1065
1066 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
1067 SetLastError(ERROR_INVALID_PARAMETER);
1068
1069 return FALSE;
1070 }
1071
1072 /*
1073 * @implemented (Wine 13 sep 2008)
1074 */
1075 BOOL
1076 WINAPI
1077 GetVolumeNameForVolumeMountPointA(
1078 LPCSTR lpszVolumeMountPoint,
1079 LPSTR lpszVolumeName,
1080 DWORD cchBufferLength
1081 )
1082 {
1083 BOOL ret;
1084 WCHAR volumeW[50], *pathW = NULL;
1085 DWORD len = min( sizeof(volumeW) / sizeof(WCHAR), cchBufferLength );
1086
1087 TRACE("(%s, %p, %x)\n", debugstr_a(lpszVolumeMountPoint), lpszVolumeName, cchBufferLength);
1088
1089 if (!lpszVolumeMountPoint || !(pathW = FilenameA2W( lpszVolumeMountPoint, TRUE )))
1090 return FALSE;
1091
1092 if ((ret = GetVolumeNameForVolumeMountPointW( pathW, volumeW, len )))
1093 FilenameW2A_N( lpszVolumeName, len, volumeW, -1 );
1094
1095 RtlFreeHeap( RtlGetProcessHeap(), 0, pathW );
1096 return ret;
1097 }
1098
1099 /*
1100 * @implemented (Wine 13 sep 2008)
1101 */
1102 HANDLE
1103 WINAPI
1104 FindFirstVolumeW(
1105 LPWSTR volume,
1106 DWORD len
1107 )
1108 {
1109 DWORD size = 1024;
1110 HANDLE mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
1111 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE );
1112 if (mgr == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
1113
1114 for (;;)
1115 {
1116 MOUNTMGR_MOUNT_POINT input;
1117 MOUNTMGR_MOUNT_POINTS *output;
1118
1119 if (!(output = RtlAllocateHeap( RtlGetProcessHeap(), 0, size )))
1120 {
1121 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1122 break;
1123 }
1124 memset( &input, 0, sizeof(input) );
1125
1126 if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, &input, sizeof(input),
1127 output, size, NULL, NULL ))
1128 {
1129 if (GetLastError() != ERROR_MORE_DATA) break;
1130 size = output->Size;
1131 RtlFreeHeap( RtlGetProcessHeap(), 0, output );
1132 continue;
1133 }
1134 CloseHandle( mgr );
1135 /* abuse the Size field to store the current index */
1136 output->Size = 0;
1137 if (!FindNextVolumeW( output, volume, len ))
1138 {
1139 RtlFreeHeap( RtlGetProcessHeap(), 0, output );
1140 return INVALID_HANDLE_VALUE;
1141 }
1142 return (HANDLE)output;
1143 }
1144 CloseHandle( mgr );
1145 return INVALID_HANDLE_VALUE;
1146 }
1147
1148 /*
1149 * @implemented (Wine 13 sep 2008)
1150 */
1151 HANDLE
1152 WINAPI
1153 FindFirstVolumeA(
1154 LPSTR volume,
1155 DWORD len
1156 )
1157 {
1158 WCHAR *buffer = NULL;
1159 HANDLE handle;
1160
1161 buffer = RtlAllocateHeap( RtlGetProcessHeap(), 0, len * sizeof(WCHAR) );
1162
1163 if (!buffer)
1164 {
1165 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1166 return INVALID_HANDLE_VALUE;
1167 }
1168
1169 handle = FindFirstVolumeW( buffer, len );
1170
1171 if (handle != INVALID_HANDLE_VALUE)
1172 {
1173 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL ))
1174 {
1175 FindVolumeClose( handle );
1176 handle = INVALID_HANDLE_VALUE;
1177 }
1178 }
1179 RtlFreeHeap( RtlGetProcessHeap(), 0, buffer );
1180 return handle;
1181 }
1182
1183 /*
1184 * @implemented (Wine 13 sep 2008)
1185 */
1186 BOOL
1187 WINAPI
1188 FindVolumeClose(
1189 HANDLE hFindVolume
1190 )
1191 {
1192 return RtlFreeHeap(RtlGetProcessHeap(), 0, hFindVolume);
1193 }
1194
1195 /*
1196 * @implemented
1197 */
1198 BOOL
1199 WINAPI
1200 GetVolumePathNameA(LPCSTR lpszFileName,
1201 LPSTR lpszVolumePathName,
1202 DWORD cchBufferLength)
1203 {
1204 PWCHAR FileNameW = NULL;
1205 WCHAR VolumePathName[MAX_PATH];
1206 BOOL Result;
1207
1208 if (lpszFileName)
1209 {
1210 if (!(FileNameW = FilenameA2W(lpszFileName, FALSE)))
1211 return FALSE;
1212 }
1213
1214 Result = GetVolumePathNameW(FileNameW, VolumePathName, cchBufferLength);
1215
1216 if (Result)
1217 FilenameW2A_N(lpszVolumePathName, MAX_PATH, VolumePathName, -1);
1218
1219 return Result;
1220 }
1221
1222 /*
1223 * @implemented
1224 */
1225 BOOL
1226 WINAPI
1227 GetVolumePathNameW(LPCWSTR lpszFileName,
1228 LPWSTR lpszVolumePathName,
1229 DWORD cchBufferLength)
1230 {
1231 DWORD PathLength;
1232 UNICODE_STRING UnicodeFilePath;
1233 LPWSTR FilePart;
1234 PWSTR FullFilePath, FilePathName;
1235 ULONG PathSize;
1236 WCHAR VolumeName[MAX_PATH];
1237 DWORD ErrorCode;
1238 BOOL Result = FALSE;
1239
1240 if (!(PathLength = GetFullPathNameW(lpszFileName, 0, NULL, NULL)))
1241 {
1242 return Result;
1243 }
1244 else
1245 {
1246 PathLength = PathLength + 10;
1247 PathSize = PathLength * sizeof(WCHAR);
1248
1249 if (!(FullFilePath = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize)))
1250 {
1251 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1252 return Result;
1253 }
1254
1255 if (!GetFullPathNameW(lpszFileName, PathLength, FullFilePath, &FilePart))
1256 {
1257 RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath);
1258 return Result;
1259 }
1260
1261 RtlInitUnicodeString(&UnicodeFilePath, FullFilePath);
1262
1263 if (UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] != '\\')
1264 {
1265 UnicodeFilePath.Length += sizeof(WCHAR);
1266 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] = '\\';
1267 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
1268 }
1269
1270 if (!(FilePathName = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize)))
1271 {
1272 RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath);
1273 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1274 return Result;
1275 }
1276
1277 while (!GetVolumeNameForVolumeMountPointW(UnicodeFilePath.Buffer,
1278 VolumeName,
1279 MAX_PATH))
1280 {
1281 if (((UnicodeFilePath.Length == 4) && (UnicodeFilePath.Buffer[0] == '\\') &&
1282 (UnicodeFilePath.Buffer[1] == '\\')) || ((UnicodeFilePath.Length == 6) &&
1283 (UnicodeFilePath.Buffer[1] == ':')))
1284 {
1285 break;
1286 }
1287
1288 UnicodeFilePath.Length -= sizeof(WCHAR);
1289 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
1290
1291 memcpy(FilePathName, UnicodeFilePath.Buffer, UnicodeFilePath.Length);
1292 FilePathName[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
1293
1294 if (!GetFullPathNameW(FilePathName, PathLength, FullFilePath, &FilePart))
1295 {
1296 goto Cleanup2;
1297 }
1298
1299 if (!FilePart)
1300 {
1301 RtlInitUnicodeString(&UnicodeFilePath, FullFilePath);
1302 UnicodeFilePath.Length += sizeof(WCHAR);
1303 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] = '\\';
1304 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
1305 break;
1306 }
1307
1308 FilePart[0] = '\0';
1309 RtlInitUnicodeString(&UnicodeFilePath, FullFilePath);
1310 }
1311 }
1312
1313 if (UnicodeFilePath.Length > (cchBufferLength * sizeof(WCHAR)) - sizeof(WCHAR))
1314 {
1315 ErrorCode = ERROR_FILENAME_EXCED_RANGE;
1316 goto Cleanup1;
1317 }
1318
1319 memcpy(lpszVolumePathName, UnicodeFilePath.Buffer, UnicodeFilePath.Length);
1320 lpszVolumePathName[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
1321
1322 Result = TRUE;
1323 goto Cleanup2;
1324
1325 Cleanup1:
1326 SetLastError(ErrorCode);
1327 Cleanup2:
1328 RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath);
1329 RtlFreeHeap(RtlGetProcessHeap(), 0, FilePathName);
1330 return Result;
1331 }
1332
1333
1334 /*
1335 * @unimplemented
1336 */
1337 BOOL
1338 WINAPI
1339 SetVolumeMountPointW(
1340 LPCWSTR lpszVolumeMountPoint,
1341 LPCWSTR lpszVolumeName
1342 )
1343 {
1344 STUB;
1345 return 0;
1346 }
1347
1348 /*
1349 * @unimplemented
1350 */
1351 BOOL
1352 WINAPI
1353 DeleteVolumeMountPointA(
1354 LPCSTR lpszVolumeMountPoint
1355 )
1356 {
1357 STUB;
1358 return 0;
1359 }
1360
1361 /*
1362 * @unimplemented
1363 */
1364 HANDLE
1365 WINAPI
1366 FindFirstVolumeMountPointA(
1367 LPCSTR lpszRootPathName,
1368 LPSTR lpszVolumeMountPoint,
1369 DWORD cchBufferLength
1370 )
1371 {
1372 STUB;
1373 return 0;
1374 }
1375
1376 /*
1377 * @implemented
1378 */
1379 BOOL
1380 WINAPI
1381 FindNextVolumeA(HANDLE handle,
1382 LPSTR volume,
1383 DWORD len)
1384 {
1385 WCHAR *buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, len * sizeof(WCHAR));
1386 BOOL ret;
1387
1388 if (!buffer)
1389 {
1390 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1391 return FALSE;
1392 }
1393
1394 if ((ret = FindNextVolumeW( handle, buffer, len )))
1395 {
1396 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL )) ret = FALSE;
1397 }
1398
1399 HeapFree( GetProcessHeap(), 0, buffer );
1400 return ret;
1401 }
1402
1403 /*
1404 * @unimplemented
1405 */
1406 BOOL
1407 WINAPI
1408 FindNextVolumeMountPointA(
1409 HANDLE hFindVolumeMountPoint,
1410 LPSTR lpszVolumeMountPoint,
1411 DWORD cchBufferLength
1412 )
1413 {
1414 STUB;
1415 return 0;
1416 }
1417
1418 /*
1419 * @unimplemented
1420 */
1421 BOOL
1422 WINAPI
1423 GetVolumePathNamesForVolumeNameA(
1424 LPCSTR lpszVolumeName,
1425 LPSTR lpszVolumePathNames,
1426 DWORD cchBufferLength,
1427 PDWORD lpcchReturnLength
1428 )
1429 {
1430 STUB;
1431 return 0;
1432 }
1433
1434 /*
1435 * @unimplemented
1436 */
1437 BOOL
1438 WINAPI
1439 SetVolumeMountPointA(
1440 LPCSTR lpszVolumeMountPoint,
1441 LPCSTR lpszVolumeName
1442 )
1443 {
1444 STUB;
1445 return 0;
1446 }
1447
1448 /*
1449 * @unimplemented
1450 */
1451 BOOL
1452 WINAPI
1453 FindVolumeMountPointClose(
1454 HANDLE hFindVolumeMountPoint
1455 )
1456 {
1457 STUB;
1458 return 0;
1459 }
1460
1461 /*
1462 * @unimplemented
1463 */
1464 BOOL
1465 WINAPI
1466 DeleteVolumeMountPointW(
1467 LPCWSTR lpszVolumeMountPoint
1468 )
1469 {
1470 STUB;
1471 return 0;
1472 }
1473
1474 /*
1475 * @unimplemented
1476 */
1477 HANDLE
1478 WINAPI
1479 FindFirstVolumeMountPointW(
1480 LPCWSTR lpszRootPathName,
1481 LPWSTR lpszVolumeMountPoint,
1482 DWORD cchBufferLength
1483 )
1484 {
1485 STUB;
1486 return 0;
1487 }
1488
1489 /*
1490 * @implemented
1491 */
1492 BOOL
1493 WINAPI
1494 FindNextVolumeW(
1495 HANDLE handle,
1496 LPWSTR volume,
1497 DWORD len
1498 )
1499 {
1500 MOUNTMGR_MOUNT_POINTS *data = handle;
1501
1502 while (data->Size < data->NumberOfMountPoints)
1503 {
1504 static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{',};
1505 WCHAR *link = (WCHAR *)((char *)data + data->MountPoints[data->Size].SymbolicLinkNameOffset);
1506 DWORD size = data->MountPoints[data->Size].SymbolicLinkNameLength;
1507 data->Size++;
1508 /* skip non-volumes */
1509 if (size < sizeof(volumeW) || memcmp( link, volumeW, sizeof(volumeW) )) continue;
1510 if (size + sizeof(WCHAR) >= len * sizeof(WCHAR))
1511 {
1512 SetLastError( ERROR_FILENAME_EXCED_RANGE );
1513 return FALSE;
1514 }
1515 memcpy( volume, link, size );
1516 volume[1] = '\\'; /* map \??\ to \\?\ */
1517 volume[size / sizeof(WCHAR)] = '\\'; /* Windows appends a backslash */
1518 volume[size / sizeof(WCHAR) + 1] = 0;
1519 DPRINT( "returning entry %u %s\n", data->Size - 1, volume );
1520 return TRUE;
1521 }
1522 SetLastError( ERROR_NO_MORE_FILES );
1523 return FALSE;
1524 }
1525
1526 /*
1527 * @unimplemented
1528 */
1529 BOOL
1530 WINAPI
1531 FindNextVolumeMountPointW(
1532 HANDLE hFindVolumeMountPoint,
1533 LPWSTR lpszVolumeMountPoint,
1534 DWORD cchBufferLength
1535 )
1536 {
1537 STUB;
1538 return 0;
1539 }
1540
1541 /*
1542 * @unimplemented
1543 */
1544 BOOL
1545 WINAPI
1546 GetVolumePathNamesForVolumeNameW(
1547 LPCWSTR lpszVolumeName,
1548 LPWSTR lpszVolumePathNames,
1549 DWORD cchBufferLength,
1550 PDWORD lpcchReturnLength
1551 )
1552 {
1553 STUB;
1554 return 0;
1555 }
1556
1557
1558 /* EOF */