72dc6f384fe970effbf1898f411a52c393c02537
[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 HANDLE mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
430 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE );
431 if (mgr == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
432
433 for (;;)
434 {
435 MOUNTMGR_MOUNT_POINT input;
436 MOUNTMGR_MOUNT_POINTS *output;
437
438 if (!(output = RtlAllocateHeap( RtlGetProcessHeap(), 0, size )))
439 {
440 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
441 break;
442 }
443 memset( &input, 0, sizeof(input) );
444
445 if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, &input, sizeof(input),
446 output, size, NULL, NULL ))
447 {
448 if (GetLastError() != ERROR_MORE_DATA) break;
449 size = output->Size;
450 RtlFreeHeap( RtlGetProcessHeap(), 0, output );
451 continue;
452 }
453 CloseHandle( mgr );
454 /* abuse the Size field to store the current index */
455 output->Size = 0;
456 if (!FindNextVolumeW( output, volume, len ))
457 {
458 RtlFreeHeap( RtlGetProcessHeap(), 0, output );
459 return INVALID_HANDLE_VALUE;
460 }
461 return (HANDLE)output;
462 }
463 CloseHandle( mgr );
464 return INVALID_HANDLE_VALUE;
465 }
466
467 /*
468 * @implemented (Wine 13 sep 2008)
469 */
470 HANDLE
471 WINAPI
472 FindFirstVolumeA(IN LPSTR volume,
473 IN DWORD len)
474 {
475 WCHAR *buffer = NULL;
476 HANDLE handle;
477
478 buffer = RtlAllocateHeap( RtlGetProcessHeap(), 0, len * sizeof(WCHAR) );
479
480 if (!buffer)
481 {
482 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
483 return INVALID_HANDLE_VALUE;
484 }
485
486 handle = FindFirstVolumeW( buffer, len );
487
488 if (handle != INVALID_HANDLE_VALUE)
489 {
490 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL ))
491 {
492 FindVolumeClose( handle );
493 handle = INVALID_HANDLE_VALUE;
494 }
495 }
496 RtlFreeHeap( RtlGetProcessHeap(), 0, buffer );
497 return handle;
498 }
499
500 /*
501 * @implemented (Wine 13 sep 2008)
502 */
503 BOOL
504 WINAPI
505 FindVolumeClose(IN HANDLE hFindVolume)
506 {
507 return RtlFreeHeap(RtlGetProcessHeap(), 0, hFindVolume);
508 }
509
510 /*
511 * @implemented
512 */
513 BOOL
514 WINAPI
515 GetVolumePathNameA(IN LPCSTR lpszFileName,
516 IN LPSTR lpszVolumePathName,
517 IN DWORD cchBufferLength)
518 {
519 PWCHAR FileNameW = NULL;
520 WCHAR VolumePathName[MAX_PATH];
521 BOOL Result;
522
523 if (lpszFileName)
524 {
525 if (!(FileNameW = FilenameA2W(lpszFileName, FALSE)))
526 return FALSE;
527 }
528
529 Result = GetVolumePathNameW(FileNameW, VolumePathName, cchBufferLength);
530
531 if (Result)
532 FilenameW2A_N(lpszVolumePathName, MAX_PATH, VolumePathName, -1);
533
534 return Result;
535 }
536
537 /*
538 * @implemented
539 */
540 BOOL
541 WINAPI
542 GetVolumePathNameW(IN LPCWSTR lpszFileName,
543 IN LPWSTR lpszVolumePathName,
544 IN DWORD cchBufferLength)
545 {
546 DWORD PathLength;
547 UNICODE_STRING UnicodeFilePath;
548 LPWSTR FilePart;
549 PWSTR FullFilePath, FilePathName;
550 ULONG PathSize;
551 WCHAR VolumeName[MAX_PATH];
552 DWORD ErrorCode;
553 BOOL Result = FALSE;
554
555 if (!(PathLength = GetFullPathNameW(lpszFileName, 0, NULL, NULL)))
556 {
557 return Result;
558 }
559 else
560 {
561 PathLength = PathLength + 10;
562 PathSize = PathLength * sizeof(WCHAR);
563
564 if (!(FullFilePath = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize)))
565 {
566 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
567 return Result;
568 }
569
570 if (!GetFullPathNameW(lpszFileName, PathLength, FullFilePath, &FilePart))
571 {
572 RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath);
573 return Result;
574 }
575
576 RtlInitUnicodeString(&UnicodeFilePath, FullFilePath);
577
578 if (UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] != '\\')
579 {
580 UnicodeFilePath.Length += sizeof(WCHAR);
581 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] = '\\';
582 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
583 }
584
585 if (!(FilePathName = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize)))
586 {
587 RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath);
588 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
589 return Result;
590 }
591
592 while (!GetVolumeNameForVolumeMountPointW(UnicodeFilePath.Buffer,
593 VolumeName,
594 MAX_PATH))
595 {
596 if (((UnicodeFilePath.Length == 4) && (UnicodeFilePath.Buffer[0] == '\\') &&
597 (UnicodeFilePath.Buffer[1] == '\\')) || ((UnicodeFilePath.Length == 6) &&
598 (UnicodeFilePath.Buffer[1] == ':')))
599 {
600 break;
601 }
602
603 UnicodeFilePath.Length -= sizeof(WCHAR);
604 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
605
606 memcpy(FilePathName, UnicodeFilePath.Buffer, UnicodeFilePath.Length);
607 FilePathName[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
608
609 if (!GetFullPathNameW(FilePathName, PathLength, FullFilePath, &FilePart))
610 {
611 goto Cleanup2;
612 }
613
614 if (!FilePart)
615 {
616 RtlInitUnicodeString(&UnicodeFilePath, FullFilePath);
617 UnicodeFilePath.Length += sizeof(WCHAR);
618 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] = '\\';
619 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
620 break;
621 }
622
623 FilePart[0] = '\0';
624 RtlInitUnicodeString(&UnicodeFilePath, FullFilePath);
625 }
626 }
627
628 if (UnicodeFilePath.Length > (cchBufferLength * sizeof(WCHAR)) - sizeof(WCHAR))
629 {
630 ErrorCode = ERROR_FILENAME_EXCED_RANGE;
631 goto Cleanup1;
632 }
633
634 memcpy(lpszVolumePathName, UnicodeFilePath.Buffer, UnicodeFilePath.Length);
635 lpszVolumePathName[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
636
637 Result = TRUE;
638 goto Cleanup2;
639
640 Cleanup1:
641 SetLastError(ErrorCode);
642 Cleanup2:
643 RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath);
644 RtlFreeHeap(RtlGetProcessHeap(), 0, FilePathName);
645 return Result;
646 }
647
648 /*
649 * @implemented
650 */
651 BOOL
652 WINAPI
653 FindNextVolumeA(IN HANDLE handle,
654 IN LPSTR volume,
655 IN DWORD len)
656 {
657 WCHAR *buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, len * sizeof(WCHAR));
658 BOOL ret;
659
660 if (!buffer)
661 {
662 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
663 return FALSE;
664 }
665
666 if ((ret = FindNextVolumeW( handle, buffer, len )))
667 {
668 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL )) ret = FALSE;
669 }
670
671 RtlFreeHeap(RtlGetProcessHeap(), 0, buffer);
672 return ret;
673 }
674
675 /*
676 * @implemented
677 */
678 BOOL
679 WINAPI
680 FindNextVolumeW(IN HANDLE handle,
681 IN LPWSTR volume,
682 IN DWORD len)
683 {
684 MOUNTMGR_MOUNT_POINTS *data = handle;
685
686 while (data->Size < data->NumberOfMountPoints)
687 {
688 static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{',};
689 WCHAR *link = (WCHAR *)((char *)data + data->MountPoints[data->Size].SymbolicLinkNameOffset);
690 DWORD size = data->MountPoints[data->Size].SymbolicLinkNameLength;
691 data->Size++;
692 /* skip non-volumes */
693 if (size < sizeof(volumeW) || memcmp( link, volumeW, sizeof(volumeW) )) continue;
694 if (size + sizeof(WCHAR) >= len * sizeof(WCHAR))
695 {
696 SetLastError( ERROR_FILENAME_EXCED_RANGE );
697 return FALSE;
698 }
699 memcpy( volume, link, size );
700 volume[1] = '\\'; /* map \??\ to \\?\ */
701 volume[size / sizeof(WCHAR)] = '\\'; /* Windows appends a backslash */
702 volume[size / sizeof(WCHAR) + 1] = 0;
703 DPRINT( "returning entry %u %s\n", data->Size - 1, volume );
704 return TRUE;
705 }
706 SetLastError( ERROR_NO_MORE_FILES );
707 return FALSE;
708 }
709
710 /*
711 * @unimplemented
712 */
713 BOOL
714 WINAPI
715 GetVolumePathNamesForVolumeNameA(IN LPCSTR lpszVolumeName,
716 IN LPSTR lpszVolumePathNames,
717 IN DWORD cchBufferLength,
718 OUT PDWORD lpcchReturnLength)
719 {
720 STUB;
721 return 0;
722 }
723
724
725 /*
726 * @unimplemented
727 */
728 BOOL
729 WINAPI
730 GetVolumePathNamesForVolumeNameW(IN LPCWSTR lpszVolumeName,
731 IN LPWSTR lpszVolumePathNames,
732 IN DWORD cchBufferLength,
733 OUT PDWORD lpcchReturnLength)
734 {
735 STUB;
736 return 0;
737 }
738
739 /* EOF */