[KERNEL32]
[reactos.git] / reactos / dll / win32 / kernel32 / client / file / volume.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: lib/kernel32/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 * UPDATE HISTORY:
11 * Created 01/11/98
12 */
13 //WINE copyright notice:
14 /*
15 * DOS drives handling functions
16 *
17 * Copyright 1993 Erik Bos
18 * Copyright 1996 Alexandre Julliard
19 */
20
21 #include <k32.h>
22 #define NDEBUG
23 #include <debug.h>
24 DEBUG_CHANNEL(kernel32file);
25
26 HANDLE
27 WINAPI
28 InternalOpenDirW(IN LPCWSTR DirName,
29 IN BOOLEAN Write)
30 {
31 UNICODE_STRING NtPathU;
32 OBJECT_ATTRIBUTES ObjectAttributes;
33 NTSTATUS errCode;
34 IO_STATUS_BLOCK IoStatusBlock;
35 HANDLE hFile;
36
37 if (!RtlDosPathNameToNtPathName_U(DirName, &NtPathU, NULL, NULL))
38 {
39 WARN("Invalid path\n");
40 SetLastError(ERROR_BAD_PATHNAME);
41 return INVALID_HANDLE_VALUE;
42 }
43
44 InitializeObjectAttributes(&ObjectAttributes,
45 &NtPathU,
46 OBJ_CASE_INSENSITIVE,
47 NULL,
48 NULL);
49
50 errCode = NtCreateFile(&hFile,
51 Write ? FILE_GENERIC_WRITE : FILE_GENERIC_READ,
52 &ObjectAttributes,
53 &IoStatusBlock,
54 NULL,
55 0,
56 FILE_SHARE_READ | FILE_SHARE_WRITE,
57 FILE_OPEN,
58 0,
59 NULL,
60 0);
61
62 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathU.Buffer);
63
64 if (!NT_SUCCESS(errCode))
65 {
66 BaseSetLastNTError(errCode);
67 return INVALID_HANDLE_VALUE;
68 }
69
70 return hFile;
71 }
72
73 /*
74 * @implemented
75 */
76 BOOL
77 WINAPI
78 GetVolumeInformationA(IN LPCSTR lpRootPathName,
79 IN LPSTR lpVolumeNameBuffer,
80 IN DWORD nVolumeNameSize,
81 OUT LPDWORD lpVolumeSerialNumber OPTIONAL,
82 OUT LPDWORD lpMaximumComponentLength OPTIONAL,
83 OUT LPDWORD lpFileSystemFlags OPTIONAL,
84 OUT LPSTR lpFileSystemNameBuffer OPTIONAL,
85 IN DWORD nFileSystemNameSize)
86 {
87 UNICODE_STRING FileSystemNameU;
88 UNICODE_STRING VolumeNameU = { 0, 0, NULL };
89 ANSI_STRING VolumeName;
90 ANSI_STRING FileSystemName;
91 PWCHAR RootPathNameW;
92 BOOL Result;
93
94 if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE)))
95 return FALSE;
96
97 if (lpVolumeNameBuffer)
98 {
99 VolumeNameU.MaximumLength = (USHORT)nVolumeNameSize * sizeof(WCHAR);
100 VolumeNameU.Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
101 0,
102 VolumeNameU.MaximumLength);
103 if (VolumeNameU.Buffer == NULL)
104 {
105 goto FailNoMem;
106 }
107 }
108
109 if (lpFileSystemNameBuffer)
110 {
111 FileSystemNameU.Length = 0;
112 FileSystemNameU.MaximumLength = (USHORT)nFileSystemNameSize * sizeof(WCHAR);
113 FileSystemNameU.Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
114 0,
115 FileSystemNameU.MaximumLength);
116 if (FileSystemNameU.Buffer == NULL)
117 {
118 if (VolumeNameU.Buffer != NULL)
119 {
120 RtlFreeHeap(RtlGetProcessHeap(),
121 0,
122 VolumeNameU.Buffer);
123 }
124
125 FailNoMem:
126 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
127 return FALSE;
128 }
129 }
130
131 Result = GetVolumeInformationW (RootPathNameW,
132 lpVolumeNameBuffer ? VolumeNameU.Buffer : NULL,
133 nVolumeNameSize,
134 lpVolumeSerialNumber,
135 lpMaximumComponentLength,
136 lpFileSystemFlags,
137 lpFileSystemNameBuffer ? FileSystemNameU.Buffer : NULL,
138 nFileSystemNameSize);
139
140 if (Result)
141 {
142 if (lpVolumeNameBuffer)
143 {
144 VolumeNameU.Length = wcslen(VolumeNameU.Buffer) * sizeof(WCHAR);
145 VolumeName.Length = 0;
146 VolumeName.MaximumLength = (USHORT)nVolumeNameSize;
147 VolumeName.Buffer = lpVolumeNameBuffer;
148 }
149
150 if (lpFileSystemNameBuffer)
151 {
152 FileSystemNameU.Length = wcslen(FileSystemNameU.Buffer) * sizeof(WCHAR);
153 FileSystemName.Length = 0;
154 FileSystemName.MaximumLength = (USHORT)nFileSystemNameSize;
155 FileSystemName.Buffer = lpFileSystemNameBuffer;
156 }
157
158 /* convert unicode strings to ansi (or oem) */
159 if (bIsFileApiAnsi)
160 {
161 if (lpVolumeNameBuffer)
162 {
163 RtlUnicodeStringToAnsiString (&VolumeName,
164 &VolumeNameU,
165 FALSE);
166 }
167 if (lpFileSystemNameBuffer)
168 {
169 RtlUnicodeStringToAnsiString (&FileSystemName,
170 &FileSystemNameU,
171 FALSE);
172 }
173 }
174 else
175 {
176 if (lpVolumeNameBuffer)
177 {
178 RtlUnicodeStringToOemString (&VolumeName,
179 &VolumeNameU,
180 FALSE);
181 }
182 if (lpFileSystemNameBuffer)
183 {
184 RtlUnicodeStringToOemString (&FileSystemName,
185 &FileSystemNameU,
186 FALSE);
187 }
188 }
189 }
190
191 if (lpVolumeNameBuffer)
192 {
193 RtlFreeHeap (RtlGetProcessHeap (),
194 0,
195 VolumeNameU.Buffer);
196 }
197 if (lpFileSystemNameBuffer)
198 {
199 RtlFreeHeap (RtlGetProcessHeap (),
200 0,
201 FileSystemNameU.Buffer);
202 }
203
204 return Result;
205 }
206
207 #define FS_VOLUME_BUFFER_SIZE (MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_VOLUME_INFORMATION))
208
209 #define FS_ATTRIBUTE_BUFFER_SIZE (MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_ATTRIBUTE_INFORMATION))
210
211 /*
212 * @implemented
213 */
214 BOOL
215 WINAPI
216 GetVolumeInformationW(IN LPCWSTR lpRootPathName,
217 IN LPWSTR lpVolumeNameBuffer,
218 IN DWORD nVolumeNameSize,
219 OUT LPDWORD lpVolumeSerialNumber OPTIONAL,
220 OUT LPDWORD lpMaximumComponentLength OPTIONAL,
221 OUT LPDWORD lpFileSystemFlags OPTIONAL,
222 OUT LPWSTR lpFileSystemNameBuffer OPTIONAL,
223 IN DWORD nFileSystemNameSize)
224 {
225 PFILE_FS_VOLUME_INFORMATION FileFsVolume;
226 PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute;
227 IO_STATUS_BLOCK IoStatusBlock;
228 WCHAR RootPathName[MAX_PATH];
229 UCHAR Buffer[max(FS_VOLUME_BUFFER_SIZE, FS_ATTRIBUTE_BUFFER_SIZE)];
230
231 HANDLE hFile;
232 NTSTATUS errCode;
233
234 FileFsVolume = (PFILE_FS_VOLUME_INFORMATION)Buffer;
235 FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer;
236
237 TRACE("FileFsVolume %p\n", FileFsVolume);
238 TRACE("FileFsAttribute %p\n", FileFsAttribute);
239
240 if (!lpRootPathName || !wcscmp(lpRootPathName, L""))
241 {
242 GetCurrentDirectoryW (MAX_PATH, RootPathName);
243 }
244 else
245 {
246 wcsncpy (RootPathName, lpRootPathName, 3);
247 }
248 RootPathName[3] = 0;
249
250 hFile = InternalOpenDirW(RootPathName, FALSE);
251 if (hFile == INVALID_HANDLE_VALUE)
252 {
253 return FALSE;
254 }
255
256 TRACE("hFile: %p\n", hFile);
257 errCode = NtQueryVolumeInformationFile(hFile,
258 &IoStatusBlock,
259 FileFsVolume,
260 FS_VOLUME_BUFFER_SIZE,
261 FileFsVolumeInformation);
262 if ( !NT_SUCCESS(errCode) )
263 {
264 WARN("Status: %x\n", errCode);
265 CloseHandle(hFile);
266 BaseSetLastNTError (errCode);
267 return FALSE;
268 }
269
270 if (lpVolumeSerialNumber)
271 *lpVolumeSerialNumber = FileFsVolume->VolumeSerialNumber;
272
273 if (lpVolumeNameBuffer)
274 {
275 if (nVolumeNameSize * sizeof(WCHAR) >= FileFsVolume->VolumeLabelLength + sizeof(WCHAR))
276 {
277 memcpy(lpVolumeNameBuffer,
278 FileFsVolume->VolumeLabel,
279 FileFsVolume->VolumeLabelLength);
280 lpVolumeNameBuffer[FileFsVolume->VolumeLabelLength / sizeof(WCHAR)] = 0;
281 }
282 else
283 {
284 CloseHandle(hFile);
285 SetLastError(ERROR_MORE_DATA);
286 return FALSE;
287 }
288 }
289
290 errCode = NtQueryVolumeInformationFile (hFile,
291 &IoStatusBlock,
292 FileFsAttribute,
293 FS_ATTRIBUTE_BUFFER_SIZE,
294 FileFsAttributeInformation);
295 CloseHandle(hFile);
296 if (!NT_SUCCESS(errCode))
297 {
298 WARN("Status: %x\n", errCode);
299 BaseSetLastNTError (errCode);
300 return FALSE;
301 }
302
303 if (lpFileSystemFlags)
304 *lpFileSystemFlags = FileFsAttribute->FileSystemAttributes;
305 if (lpMaximumComponentLength)
306 *lpMaximumComponentLength = FileFsAttribute->MaximumComponentNameLength;
307 if (lpFileSystemNameBuffer)
308 {
309 if (nFileSystemNameSize * sizeof(WCHAR) >= FileFsAttribute->FileSystemNameLength + sizeof(WCHAR))
310 {
311 memcpy(lpFileSystemNameBuffer,
312 FileFsAttribute->FileSystemName,
313 FileFsAttribute->FileSystemNameLength);
314 lpFileSystemNameBuffer[FileFsAttribute->FileSystemNameLength / sizeof(WCHAR)] = 0;
315 }
316 else
317 {
318 SetLastError(ERROR_MORE_DATA);
319 return FALSE;
320 }
321 }
322 return TRUE;
323 }
324
325 /*
326 * @implemented
327 */
328 BOOL
329 WINAPI
330 SetVolumeLabelA(IN LPCSTR lpRootPathName,
331 IN LPCSTR lpVolumeName OPTIONAL) /* NULL if deleting label */
332 {
333 PWCHAR RootPathNameW;
334 PWCHAR VolumeNameW = NULL;
335 BOOL Result;
336
337 if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE)))
338 return FALSE;
339
340 if (lpVolumeName)
341 {
342 if (!(VolumeNameW = FilenameA2W(lpVolumeName, TRUE)))
343 return FALSE;
344 }
345
346 Result = SetVolumeLabelW (RootPathNameW,
347 VolumeNameW);
348
349 if (VolumeNameW)
350 {
351 RtlFreeHeap (RtlGetProcessHeap (),
352 0,
353 VolumeNameW );
354 }
355
356 return Result;
357 }
358
359 /*
360 * @implemented
361 */
362 BOOL
363 WINAPI
364 SetVolumeLabelW(IN LPCWSTR lpRootPathName,
365 IN LPCWSTR lpVolumeName OPTIONAL) /* NULL if deleting label */
366 {
367 PFILE_FS_LABEL_INFORMATION LabelInfo;
368 IO_STATUS_BLOCK IoStatusBlock;
369 ULONG LabelLength;
370 HANDLE hFile;
371 NTSTATUS Status;
372
373 LabelLength = wcslen(lpVolumeName) * sizeof(WCHAR);
374 LabelInfo = RtlAllocateHeap(RtlGetProcessHeap(),
375 0,
376 sizeof(FILE_FS_LABEL_INFORMATION) +
377 LabelLength);
378 if (LabelInfo == NULL)
379 {
380 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
381 return FALSE;
382 }
383 LabelInfo->VolumeLabelLength = LabelLength;
384 memcpy(LabelInfo->VolumeLabel,
385 lpVolumeName,
386 LabelLength);
387
388 hFile = InternalOpenDirW(lpRootPathName, TRUE);
389 if (INVALID_HANDLE_VALUE == hFile)
390 {
391 RtlFreeHeap(RtlGetProcessHeap(),
392 0,
393 LabelInfo);
394 return FALSE;
395 }
396
397 Status = NtSetVolumeInformationFile(hFile,
398 &IoStatusBlock,
399 LabelInfo,
400 sizeof(FILE_FS_LABEL_INFORMATION) +
401 LabelLength,
402 FileFsLabelInformation);
403
404 RtlFreeHeap(RtlGetProcessHeap(),
405 0,
406 LabelInfo);
407
408 if (!NT_SUCCESS(Status))
409 {
410 WARN("Status: %x\n", Status);
411 CloseHandle(hFile);
412 BaseSetLastNTError(Status);
413 return FALSE;
414 }
415
416 CloseHandle(hFile);
417 return TRUE;
418 }
419
420 /*
421 * @implemented (Wine 13 sep 2008)
422 */
423 HANDLE
424 WINAPI
425 FindFirstVolumeW(IN LPWSTR volume,
426 IN DWORD len)
427 {
428 DWORD size = 1024;
429 DWORD br;
430 HANDLE mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
431 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE );
432 if (mgr == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
433
434 for (;;)
435 {
436 MOUNTMGR_MOUNT_POINT input;
437 MOUNTMGR_MOUNT_POINTS *output;
438
439 if (!(output = RtlAllocateHeap( RtlGetProcessHeap(), 0, size )))
440 {
441 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
442 break;
443 }
444 memset( &input, 0, sizeof(input) );
445
446 if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, &input, sizeof(input),
447 output, size, &br, NULL ))
448 {
449 if (GetLastError() != ERROR_MORE_DATA) break;
450 size = output->Size;
451 RtlFreeHeap( RtlGetProcessHeap(), 0, output );
452 continue;
453 }
454 CloseHandle( mgr );
455 /* abuse the Size field to store the current index */
456 output->Size = 0;
457 if (!FindNextVolumeW( output, volume, len ))
458 {
459 RtlFreeHeap( RtlGetProcessHeap(), 0, output );
460 return INVALID_HANDLE_VALUE;
461 }
462 return (HANDLE)output;
463 }
464 CloseHandle( mgr );
465 return INVALID_HANDLE_VALUE;
466 }
467
468 /*
469 * @implemented (Wine 13 sep 2008)
470 */
471 HANDLE
472 WINAPI
473 FindFirstVolumeA(IN LPSTR volume,
474 IN DWORD len)
475 {
476 WCHAR *buffer = NULL;
477 HANDLE handle;
478
479 buffer = RtlAllocateHeap( RtlGetProcessHeap(), 0, len * sizeof(WCHAR) );
480
481 if (!buffer)
482 {
483 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
484 return INVALID_HANDLE_VALUE;
485 }
486
487 handle = FindFirstVolumeW( buffer, len );
488
489 if (handle != INVALID_HANDLE_VALUE)
490 {
491 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL ))
492 {
493 FindVolumeClose( handle );
494 handle = INVALID_HANDLE_VALUE;
495 }
496 }
497 RtlFreeHeap( RtlGetProcessHeap(), 0, buffer );
498 return handle;
499 }
500
501 /*
502 * @implemented (Wine 13 sep 2008)
503 */
504 BOOL
505 WINAPI
506 FindVolumeClose(IN HANDLE hFindVolume)
507 {
508 return RtlFreeHeap(RtlGetProcessHeap(), 0, hFindVolume);
509 }
510
511 /*
512 * @implemented
513 */
514 BOOL
515 WINAPI
516 GetVolumePathNameA(IN LPCSTR lpszFileName,
517 IN LPSTR lpszVolumePathName,
518 IN DWORD cchBufferLength)
519 {
520 PWCHAR FileNameW = NULL;
521 WCHAR VolumePathName[MAX_PATH];
522 BOOL Result;
523
524 if (lpszFileName)
525 {
526 if (!(FileNameW = FilenameA2W(lpszFileName, FALSE)))
527 return FALSE;
528 }
529
530 Result = GetVolumePathNameW(FileNameW, VolumePathName, cchBufferLength);
531
532 if (Result)
533 FilenameW2A_N(lpszVolumePathName, MAX_PATH, VolumePathName, -1);
534
535 return Result;
536 }
537
538 /*
539 * @implemented
540 */
541 BOOL
542 WINAPI
543 GetVolumePathNameW(IN LPCWSTR lpszFileName,
544 IN LPWSTR lpszVolumePathName,
545 IN DWORD cchBufferLength)
546 {
547 DWORD PathLength;
548 UNICODE_STRING UnicodeFilePath;
549 LPWSTR FilePart;
550 PWSTR FullFilePath, FilePathName;
551 ULONG PathSize;
552 WCHAR VolumeName[MAX_PATH];
553 DWORD ErrorCode;
554 BOOL Result = FALSE;
555
556 if (!lpszFileName || !lpszVolumePathName || !cchBufferLength)
557 {
558 SetLastError(ERROR_INVALID_PARAMETER);
559 return FALSE;
560 }
561
562 if (!(PathLength = GetFullPathNameW(lpszFileName, 0, NULL, NULL)))
563 {
564 return Result;
565 }
566 else
567 {
568 PathLength = PathLength + 10;
569 PathSize = PathLength * sizeof(WCHAR);
570
571 if (!(FullFilePath = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize)))
572 {
573 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
574 return Result;
575 }
576
577 if (!GetFullPathNameW(lpszFileName, PathLength, FullFilePath, &FilePart))
578 {
579 RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath);
580 return Result;
581 }
582
583 RtlInitUnicodeString(&UnicodeFilePath, FullFilePath);
584
585 if (UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] != '\\')
586 {
587 UnicodeFilePath.Length += sizeof(WCHAR);
588 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] = '\\';
589 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
590 }
591
592 if (!(FilePathName = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize)))
593 {
594 RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath);
595 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
596 return Result;
597 }
598
599 while (!GetVolumeNameForVolumeMountPointW(UnicodeFilePath.Buffer,
600 VolumeName,
601 MAX_PATH))
602 {
603 if (((UnicodeFilePath.Length == 4) && (UnicodeFilePath.Buffer[0] == '\\') &&
604 (UnicodeFilePath.Buffer[1] == '\\')) || ((UnicodeFilePath.Length == 6) &&
605 (UnicodeFilePath.Buffer[1] == ':')))
606 {
607 break;
608 }
609
610 UnicodeFilePath.Length -= sizeof(WCHAR);
611 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
612
613 memcpy(FilePathName, UnicodeFilePath.Buffer, UnicodeFilePath.Length);
614 FilePathName[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
615
616 if (!GetFullPathNameW(FilePathName, PathLength, FullFilePath, &FilePart))
617 {
618 goto Cleanup2;
619 }
620
621 if (!FilePart)
622 {
623 RtlInitUnicodeString(&UnicodeFilePath, FullFilePath);
624 UnicodeFilePath.Length += sizeof(WCHAR);
625 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] = '\\';
626 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
627 break;
628 }
629
630 FilePart[0] = '\0';
631 RtlInitUnicodeString(&UnicodeFilePath, FullFilePath);
632 }
633 }
634
635 if (UnicodeFilePath.Length > (cchBufferLength * sizeof(WCHAR)) - sizeof(WCHAR))
636 {
637 ErrorCode = ERROR_FILENAME_EXCED_RANGE;
638 goto Cleanup1;
639 }
640
641 memcpy(lpszVolumePathName, UnicodeFilePath.Buffer, UnicodeFilePath.Length);
642 lpszVolumePathName[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
643
644 Result = TRUE;
645 goto Cleanup2;
646
647 Cleanup1:
648 SetLastError(ErrorCode);
649 Cleanup2:
650 RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath);
651 RtlFreeHeap(RtlGetProcessHeap(), 0, FilePathName);
652 return Result;
653 }
654
655 /*
656 * @implemented
657 */
658 BOOL
659 WINAPI
660 FindNextVolumeA(IN HANDLE handle,
661 IN LPSTR volume,
662 IN DWORD len)
663 {
664 WCHAR *buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, len * sizeof(WCHAR));
665 BOOL ret;
666
667 if (!buffer)
668 {
669 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
670 return FALSE;
671 }
672
673 if ((ret = FindNextVolumeW( handle, buffer, len )))
674 {
675 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL )) ret = FALSE;
676 }
677
678 RtlFreeHeap(RtlGetProcessHeap(), 0, buffer);
679 return ret;
680 }
681
682 /*
683 * @implemented
684 */
685 BOOL
686 WINAPI
687 FindNextVolumeW(IN HANDLE handle,
688 IN LPWSTR volume,
689 IN DWORD len)
690 {
691 MOUNTMGR_MOUNT_POINTS *data = handle;
692
693 while (data->Size < data->NumberOfMountPoints)
694 {
695 static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{',};
696 WCHAR *link = (WCHAR *)((char *)data + data->MountPoints[data->Size].SymbolicLinkNameOffset);
697 DWORD size = data->MountPoints[data->Size].SymbolicLinkNameLength;
698 data->Size++;
699 /* skip non-volumes */
700 if (size < sizeof(volumeW) || memcmp( link, volumeW, sizeof(volumeW) )) continue;
701 if (size + sizeof(WCHAR) >= len * sizeof(WCHAR))
702 {
703 SetLastError( ERROR_FILENAME_EXCED_RANGE );
704 return FALSE;
705 }
706 memcpy( volume, link, size );
707 volume[1] = '\\'; /* map \??\ to \\?\ */
708 volume[size / sizeof(WCHAR)] = '\\'; /* Windows appends a backslash */
709 volume[size / sizeof(WCHAR) + 1] = 0;
710 DPRINT( "returning entry %u %s\n", data->Size - 1, volume );
711 return TRUE;
712 }
713 SetLastError( ERROR_NO_MORE_FILES );
714 return FALSE;
715 }
716
717 /*
718 * @unimplemented
719 */
720 BOOL
721 WINAPI
722 GetVolumePathNamesForVolumeNameA(IN LPCSTR lpszVolumeName,
723 IN LPSTR lpszVolumePathNames,
724 IN DWORD cchBufferLength,
725 OUT PDWORD lpcchReturnLength)
726 {
727 STUB;
728 return 0;
729 }
730
731
732 /*
733 * @unimplemented
734 */
735 BOOL
736 WINAPI
737 GetVolumePathNamesForVolumeNameW(IN LPCWSTR lpszVolumeName,
738 IN LPWSTR lpszVolumePathNames,
739 IN DWORD cchBufferLength,
740 OUT PDWORD lpcchReturnLength)
741 {
742 STUB;
743 return 0;
744 }
745
746 /* EOF */