7157f0fcf69cde2af899d4150a98418aedb30aa1
[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: dll/win32/kernel32/client/file/volume.c
5 * PURPOSE: File volume functions
6 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
7 * Erik Bos, Alexandre Julliard :
8 * GetLogicalDriveStringsA,
9 * GetLogicalDriveStringsW, GetLogicalDrives
10 * Pierre Schweitzer (pierre@reactos.org)
11 * UPDATE HISTORY:
12 * Created 01/11/98
13 */
14 //WINE copyright notice:
15 /*
16 * DOS drives handling functions
17 *
18 * Copyright 1993 Erik Bos
19 * Copyright 1996 Alexandre Julliard
20 */
21
22 #include <k32.h>
23 #define NDEBUG
24 #include <debug.h>
25
26
27 /*
28 * @implemented
29 */
30 BOOL
31 WINAPI
32 GetVolumeInformationA(IN LPCSTR lpRootPathName,
33 IN LPSTR lpVolumeNameBuffer,
34 IN DWORD nVolumeNameSize,
35 OUT LPDWORD lpVolumeSerialNumber OPTIONAL,
36 OUT LPDWORD lpMaximumComponentLength OPTIONAL,
37 OUT LPDWORD lpFileSystemFlags OPTIONAL,
38 OUT LPSTR lpFileSystemNameBuffer OPTIONAL,
39 IN DWORD nFileSystemNameSize)
40 {
41 BOOL Ret;
42 NTSTATUS Status;
43 PUNICODE_STRING RootPathNameU;
44 ANSI_STRING VolumeName, FileSystemName;
45 UNICODE_STRING VolumeNameU, FileSystemNameU;
46
47 /* If no root path provided, default to \ */
48 if (lpRootPathName == NULL)
49 {
50 lpRootPathName = "\\";
51 }
52
53 /* Convert root path to unicode */
54 RootPathNameU = Basep8BitStringToStaticUnicodeString(lpRootPathName);
55 if (RootPathNameU == NULL)
56 {
57 return FALSE;
58 }
59
60 /* Init all our STRINGS (U/A) */
61 VolumeNameU.Buffer = NULL;
62 VolumeNameU.MaximumLength = 0;
63 FileSystemNameU.Buffer = NULL;
64 FileSystemNameU.MaximumLength = 0;
65
66 VolumeName.Buffer = lpVolumeNameBuffer;
67 VolumeName.MaximumLength = nVolumeNameSize + 1;
68 FileSystemName.Buffer = lpFileSystemNameBuffer;
69 FileSystemName.MaximumLength = nFileSystemNameSize + 1;
70
71 /* Assume failure for now */
72 Ret = FALSE;
73
74 /* If caller wants volume name, allocate a buffer to receive it */
75 if (lpVolumeNameBuffer != NULL)
76 {
77 VolumeNameU.MaximumLength = sizeof(WCHAR) * (nVolumeNameSize + 1);
78 VolumeNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0,
79 VolumeNameU.MaximumLength);
80 if (VolumeNameU.Buffer == NULL)
81 {
82 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
83 goto CleanAndQuit;
84 }
85 }
86
87 /* If caller wants file system name, allocate a buffer to receive it */
88 if (lpFileSystemNameBuffer != NULL)
89 {
90 FileSystemNameU.MaximumLength = sizeof(WCHAR) * (nVolumeNameSize + 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 static BOOL
154 IsThisARootDirectory(IN HANDLE VolumeHandle,
155 IN PUNICODE_STRING NtPathName)
156 {
157 NTSTATUS Status;
158 IO_STATUS_BLOCK IoStatusBlock;
159 struct
160 {
161 FILE_NAME_INFORMATION;
162 WCHAR Buffer[MAX_PATH];
163 } FileNameInfo;
164
165 /* If we have a handle, query the name */
166 if (VolumeHandle)
167 {
168 Status = NtQueryInformationFile(VolumeHandle, &IoStatusBlock, &FileNameInfo, sizeof(FileNameInfo), FileNameInformation);
169 if (!NT_SUCCESS(Status))
170 {
171 return FALSE;
172 }
173
174 /* Check we properly end with a \ */
175 if (FileNameInfo.FileName[FileNameInfo.FileNameLength / sizeof(WCHAR) - 1] != L'\\')
176 {
177 return FALSE;
178 }
179 }
180
181 /* If we have a path */
182 if (NtPathName != NULL)
183 {
184 HANDLE LinkHandle;
185 WCHAR Buffer[512];
186 ULONG ReturnedLength;
187 UNICODE_STRING LinkTarget;
188 OBJECT_ATTRIBUTES ObjectAttributes;
189
190 NtPathName->Length -= sizeof(WCHAR);
191
192 InitializeObjectAttributes(&ObjectAttributes, NtPathName,
193 OBJ_CASE_INSENSITIVE,
194 NULL, NULL);
195
196 /* Try to see whether that's a symbolic name */
197 Status = NtOpenSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_QUERY, &ObjectAttributes);
198 NtPathName->Length += sizeof(WCHAR);
199 if (!NT_SUCCESS(Status))
200 {
201 return FALSE;
202 }
203
204 /* If so, query the target */
205 LinkTarget.Buffer = Buffer;
206 LinkTarget.Length = 0;
207 LinkTarget.MaximumLength = sizeof(Buffer);
208
209 Status = NtQuerySymbolicLinkObject(LinkHandle, &LinkTarget, &ReturnedLength);
210 NtClose(LinkHandle);
211 /* A root directory (NtName) is a symbolic link */
212 if (!NT_SUCCESS(Status))
213 {
214 return FALSE;
215 }
216 }
217
218 return TRUE;
219 }
220
221 /*
222 * @implemented
223 */
224 BOOL
225 WINAPI
226 GetVolumeInformationW(IN LPCWSTR lpRootPathName,
227 IN LPWSTR lpVolumeNameBuffer,
228 IN DWORD nVolumeNameSize,
229 OUT LPDWORD lpVolumeSerialNumber OPTIONAL,
230 OUT LPDWORD lpMaximumComponentLength OPTIONAL,
231 OUT LPDWORD lpFileSystemFlags OPTIONAL,
232 OUT LPWSTR lpFileSystemNameBuffer OPTIONAL,
233 IN DWORD nFileSystemNameSize)
234 {
235 BOOL Ret;
236 NTSTATUS Status;
237 HANDLE VolumeHandle;
238 LPCWSTR RootPathName;
239 UNICODE_STRING NtPathName;
240 IO_STATUS_BLOCK IoStatusBlock;
241 OBJECT_ATTRIBUTES ObjectAttributes;
242 PFILE_FS_VOLUME_INFORMATION VolumeInfo;
243 PFILE_FS_ATTRIBUTE_INFORMATION VolumeAttr;
244 ULONG OldMode, VolumeInfoSize, VolumeAttrSize;
245
246 /* If no root path provided, default to \ */
247 if (lpRootPathName == NULL)
248 {
249 RootPathName = L"\\";
250 }
251 else
252 {
253 RootPathName = lpRootPathName;
254 }
255
256 /* Convert to NT name */
257 if (!RtlDosPathNameToNtPathName_U(RootPathName, &NtPathName, NULL, NULL))
258 {
259 SetLastError(ERROR_PATH_NOT_FOUND);
260 return FALSE;
261 }
262
263 /* Check we really end with a backslash */
264 if (NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] != L'\\')
265 {
266 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
267 BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
268 return FALSE;
269 }
270
271 /* Try to open the received path */
272 InitializeObjectAttributes(&ObjectAttributes, &NtPathName,
273 OBJ_CASE_INSENSITIVE,
274 NULL, NULL);
275
276 /* No errors to the user */
277 RtlSetThreadErrorMode(RTL_SEM_FAILCRITICALERRORS, &OldMode);
278 Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, 0, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT);
279 RtlSetThreadErrorMode(OldMode, NULL);
280 if (!NT_SUCCESS(Status))
281 {
282 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
283 BaseSetLastNTError(Status);
284 return FALSE;
285 }
286
287 /* Check whether that's a root directory */
288 if (!IsThisARootDirectory(VolumeHandle, &NtPathName))
289 {
290 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
291 NtClose(VolumeHandle);
292 SetLastError(ERROR_DIR_NOT_ROOT);
293 return FALSE;
294 }
295
296 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
297
298 /* Assume we don't need to query FileFsVolumeInformation */
299 VolumeInfo = NULL;
300 /* If user wants volume name, allocate a buffer to query it */
301 if (lpVolumeNameBuffer != NULL)
302 {
303 VolumeInfoSize = nVolumeNameSize + sizeof(FILE_FS_VOLUME_INFORMATION);
304 }
305 /* If user just wants the serial number, allocate a dummy buffer */
306 else if (lpVolumeSerialNumber != NULL)
307 {
308 VolumeInfoSize = MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_VOLUME_INFORMATION);
309 }
310 /* Otherwise, nothing to query */
311 else
312 {
313 VolumeInfoSize = 0;
314 }
315
316 /* If we're to query, allocate a big enough buffer */
317 if (VolumeInfoSize != 0)
318 {
319 VolumeInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumeInfoSize);
320 if (VolumeInfo == NULL)
321 {
322 NtClose(VolumeHandle);
323 BaseSetLastNTError(STATUS_NO_MEMORY);
324 return FALSE;
325 }
326 }
327
328 /* Assume we don't need to query FileFsAttributeInformation */
329 VolumeAttr = NULL;
330 /* If user wants filesystem name, allocate a buffer to query it */
331 if (lpFileSystemNameBuffer != NULL)
332 {
333 VolumeAttrSize = nFileSystemNameSize + sizeof(FILE_FS_ATTRIBUTE_INFORMATION);
334 }
335 /* If user just wants max compo len or flags, allocate a dummy buffer */
336 else if (lpMaximumComponentLength != NULL || lpFileSystemFlags != NULL)
337 {
338 VolumeAttrSize = MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_ATTRIBUTE_INFORMATION);
339 }
340 else
341 {
342 VolumeAttrSize = 0;
343 }
344
345 /* If we're to query, allocate a big enough buffer */
346 if (VolumeAttrSize != 0)
347 {
348 VolumeAttr = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumeAttrSize);
349 if (VolumeAttr == NULL)
350 {
351 if (VolumeInfo != NULL)
352 {
353 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeInfo);
354 }
355
356 NtClose(VolumeHandle);
357 BaseSetLastNTError(STATUS_NO_MEMORY);
358 return FALSE;
359 }
360 }
361
362 /* Assume we'll fail */
363 Ret = FALSE;
364
365 /* If we're to query FileFsVolumeInformation, do it now! */
366 if (VolumeInfo != NULL)
367 {
368 Status = NtQueryVolumeInformationFile(VolumeHandle, &IoStatusBlock, VolumeInfo, VolumeInfoSize, FileFsVolumeInformation);
369 if (!NT_SUCCESS(Status))
370 {
371 BaseSetLastNTError(Status);
372 goto CleanAndQuit;
373 }
374 }
375
376 /* If we're to query FileFsAttributeInformation, do it now! */
377 if (VolumeAttr != NULL)
378 {
379 Status = NtQueryVolumeInformationFile(VolumeHandle, &IoStatusBlock, VolumeAttr, VolumeAttrSize, FileFsAttributeInformation);
380 if (!NT_SUCCESS(Status))
381 {
382 BaseSetLastNTError(Status);
383 goto CleanAndQuit;
384 }
385 }
386
387 /* If user wants volume name */
388 if (lpVolumeNameBuffer != NULL)
389 {
390 /* Check its buffer can hold it (+ 0) */
391 if (VolumeInfo->VolumeLabelLength >= nVolumeNameSize)
392 {
393 SetLastError(ERROR_BAD_LENGTH);
394 goto CleanAndQuit;
395 }
396
397 /* Copy and zero */
398 RtlCopyMemory(lpVolumeNameBuffer, VolumeInfo->VolumeLabel, VolumeInfo->VolumeLabelLength);
399 lpVolumeNameBuffer[VolumeInfo->VolumeLabelLength / sizeof(WCHAR)] = UNICODE_NULL;
400 }
401
402 /* If user wants wants serial number, return it */
403 if (lpVolumeSerialNumber != NULL)
404 {
405 *lpVolumeSerialNumber = VolumeInfo->VolumeSerialNumber;
406 }
407
408 /* If user wants filesystem name */
409 if (lpFileSystemNameBuffer != NULL)
410 {
411 /* Check its buffer can hold it (+ 0) */
412 if (VolumeAttr->FileSystemNameLength >= nFileSystemNameSize)
413 {
414 SetLastError(ERROR_BAD_LENGTH);
415 goto CleanAndQuit;
416 }
417
418 /* Copy and zero */
419 RtlCopyMemory(lpFileSystemNameBuffer, VolumeAttr->FileSystemName, VolumeAttr->FileSystemNameLength);
420 lpFileSystemNameBuffer[VolumeAttr->FileSystemNameLength / sizeof(WCHAR)] = UNICODE_NULL;
421 }
422
423 /* If user wants wants max compo len, return it */
424 if (lpMaximumComponentLength != NULL)
425 {
426 *lpMaximumComponentLength = VolumeAttr->MaximumComponentNameLength;
427 }
428
429 /* If user wants wants FS flags, return them */
430 if (lpFileSystemFlags != NULL)
431 {
432 *lpFileSystemFlags = VolumeAttr->FileSystemAttributes;
433 }
434
435 /* We did it! */
436 Ret = TRUE;
437
438 CleanAndQuit:
439 NtClose(VolumeHandle);
440
441 if (VolumeInfo != NULL)
442 {
443 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeInfo);
444 }
445
446 if (VolumeAttr != NULL)
447 {
448 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeAttr);
449 }
450
451 return Ret;
452 }
453
454 /*
455 * @implemented
456 */
457 BOOL
458 WINAPI
459 SetVolumeLabelA(IN LPCSTR lpRootPathName,
460 IN LPCSTR lpVolumeName OPTIONAL) /* NULL if deleting label */
461 {
462 BOOL Ret;
463 UNICODE_STRING VolumeNameU;
464 PUNICODE_STRING RootPathNameU;
465
466 if (lpRootPathName == NULL)
467 {
468 lpRootPathName = "\\";
469 }
470
471 RootPathNameU = Basep8BitStringToStaticUnicodeString(lpRootPathName);
472 if (RootPathNameU == NULL)
473 {
474 return FALSE;
475 }
476
477 if (lpVolumeName != NULL)
478 {
479 if (!Basep8BitStringToDynamicUnicodeString(&VolumeNameU, lpVolumeName))
480 {
481 return FALSE;
482 }
483 }
484 else
485 {
486 VolumeNameU.Buffer = NULL;
487 }
488
489 Ret = SetVolumeLabelW(RootPathNameU->Buffer, VolumeNameU.Buffer);
490 RtlFreeUnicodeString(&VolumeNameU);
491 return Ret;
492 }
493
494 /*
495 * @implemented
496 */
497 BOOL
498 WINAPI
499 SetVolumeLabelW(IN LPCWSTR lpRootPathName,
500 IN LPCWSTR lpVolumeName OPTIONAL) /* NULL if deleting label */
501 {
502 BOOL Ret;
503 NTSTATUS Status;
504 PWSTR VolumeRoot;
505 HANDLE VolumeHandle;
506 WCHAR VolumeGuid[MAX_PATH];
507 IO_STATUS_BLOCK IoStatusBlock;
508 OBJECT_ATTRIBUTES ObjectAttributes;
509 PFILE_FS_LABEL_INFORMATION FsLabelInfo;
510 UNICODE_STRING VolumeName, NtVolumeName;
511
512 /* If no root path provided, default to \ */
513 VolumeRoot = L"\\";
514
515 /* If user wants to set a label, make it a string */
516 if (lpVolumeName != NULL)
517 {
518 RtlInitUnicodeString(&VolumeName, lpVolumeName);
519 }
520 else
521 {
522 VolumeName.Length = 0;
523 VolumeName.MaximumLength = 0;
524 VolumeName.Buffer = NULL;
525 }
526
527 /* If we received a volume, try to get its GUID name */
528 if (lpRootPathName != NULL)
529 {
530 Ret = GetVolumeNameForVolumeMountPointW(lpRootPathName, VolumeGuid, MAX_PATH);
531 }
532 else
533 {
534 Ret = FALSE;
535 }
536
537 /* If we got the GUID name, use it */
538 if (Ret)
539 {
540 VolumeRoot = VolumeGuid;
541 }
542 else
543 {
544 /* Otherwise, use the name provided by the caller */
545 if (lpRootPathName != NULL)
546 {
547 VolumeRoot = (PWSTR)lpRootPathName;
548 }
549 }
550
551 /* Convert to a NT path */
552 if (!RtlDosPathNameToNtPathName_U(VolumeRoot, &NtVolumeName, NULL, NULL))
553 {
554 SetLastError(ERROR_PATH_NOT_FOUND);
555 return FALSE;
556 }
557
558
559 /* Check we really end with a backslash */
560 if (NtVolumeName.Buffer[(NtVolumeName.Length / sizeof(WCHAR)) - 1] != L'\\')
561 {
562 RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer);
563 BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
564 return FALSE;
565 }
566
567 /* Try to open the root directory */
568 InitializeObjectAttributes(&ObjectAttributes, &NtVolumeName,
569 OBJ_CASE_INSENSITIVE, NULL, NULL);
570
571 Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE | FILE_WRITE_DATA,
572 &ObjectAttributes, &IoStatusBlock,
573 FILE_SHARE_READ | FILE_SHARE_WRITE,
574 FILE_SYNCHRONOUS_IO_NONALERT);
575 if (!NT_SUCCESS(Status))
576 {
577 RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer);
578 BaseSetLastNTError(Status);
579 return FALSE;
580 }
581
582 /* Validate it's really a root path */
583 if (!IsThisARootDirectory(VolumeHandle, NULL))
584 {
585 RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer);
586 NtClose(VolumeHandle);
587 SetLastError(ERROR_DIR_NOT_ROOT);
588 return FALSE;
589 }
590
591 /* Done */
592 NtClose(VolumeHandle);
593
594 /* Now, open the volume to perform the label change */
595 NtVolumeName.Length -= sizeof(WCHAR);
596 InitializeObjectAttributes(&ObjectAttributes, &NtVolumeName,
597 OBJ_CASE_INSENSITIVE, NULL, NULL);
598
599 Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE | FILE_WRITE_DATA,
600 &ObjectAttributes, &IoStatusBlock,
601 FILE_SHARE_READ | FILE_SHARE_WRITE,
602 FILE_SYNCHRONOUS_IO_NONALERT);
603
604 RtlFreeHeap(RtlGetProcessHeap(), 0, NtVolumeName.Buffer);
605
606 if (!NT_SUCCESS(Status))
607 {
608 BaseSetLastNTError(Status);
609 return FALSE;
610 }
611
612 /* Assume success */
613 Ret = TRUE;
614
615 /* Allocate a buffer that can hold new label and its size */
616 FsLabelInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(FILE_FS_LABEL_INFORMATION) + VolumeName.Length);
617 if (FsLabelInfo != NULL)
618 {
619 /* Copy name and set its size */
620 RtlCopyMemory(FsLabelInfo->VolumeLabel, VolumeName.Buffer, VolumeName.Length);
621 FsLabelInfo->VolumeLabelLength = VolumeName.Length;
622
623 /* And finally, set new label */
624 Status = NtSetVolumeInformationFile(VolumeHandle, &IoStatusBlock, FsLabelInfo, sizeof(FILE_FS_LABEL_INFORMATION) + VolumeName.Length, FileFsLabelInformation);
625 }
626 else
627 {
628 /* Allocation failed */
629 Status = STATUS_NO_MEMORY;
630 }
631
632 /* In case of failure, set status and mark failure */
633 if (!NT_SUCCESS(Status))
634 {
635 BaseSetLastNTError(Status);
636 Ret = FALSE;
637 }
638
639 /* We're done */
640 NtClose(VolumeHandle);
641
642 /* Free buffer if required */
643 if (FsLabelInfo != NULL)
644 {
645 RtlFreeHeap(RtlGetProcessHeap(), 0, FsLabelInfo);
646 }
647
648 return Ret;
649 }
650
651 /*
652 * @implemented (Wine 13 sep 2008)
653 */
654 HANDLE
655 WINAPI
656 FindFirstVolumeW(IN LPWSTR volume,
657 IN DWORD len)
658 {
659 DWORD size = 1024;
660 DWORD br;
661 HANDLE mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
662 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE );
663 if (mgr == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
664
665 for (;;)
666 {
667 MOUNTMGR_MOUNT_POINT input;
668 MOUNTMGR_MOUNT_POINTS *output;
669
670 if (!(output = RtlAllocateHeap( RtlGetProcessHeap(), 0, size )))
671 {
672 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
673 break;
674 }
675 memset( &input, 0, sizeof(input) );
676
677 if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, &input, sizeof(input),
678 output, size, &br, NULL ))
679 {
680 if (GetLastError() != ERROR_MORE_DATA) break;
681 size = output->Size;
682 RtlFreeHeap( RtlGetProcessHeap(), 0, output );
683 continue;
684 }
685 CloseHandle( mgr );
686 /* abuse the Size field to store the current index */
687 output->Size = 0;
688 if (!FindNextVolumeW( output, volume, len ))
689 {
690 RtlFreeHeap( RtlGetProcessHeap(), 0, output );
691 return INVALID_HANDLE_VALUE;
692 }
693 return (HANDLE)output;
694 }
695 CloseHandle( mgr );
696 return INVALID_HANDLE_VALUE;
697 }
698
699 /*
700 * @implemented (Wine 13 sep 2008)
701 */
702 HANDLE
703 WINAPI
704 FindFirstVolumeA(IN LPSTR volume,
705 IN DWORD len)
706 {
707 WCHAR *buffer = NULL;
708 HANDLE handle;
709
710 buffer = RtlAllocateHeap( RtlGetProcessHeap(), 0, len * sizeof(WCHAR) );
711
712 if (!buffer)
713 {
714 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
715 return INVALID_HANDLE_VALUE;
716 }
717
718 handle = FindFirstVolumeW( buffer, len );
719
720 if (handle != INVALID_HANDLE_VALUE)
721 {
722 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL ))
723 {
724 FindVolumeClose( handle );
725 handle = INVALID_HANDLE_VALUE;
726 }
727 }
728 RtlFreeHeap( RtlGetProcessHeap(), 0, buffer );
729 return handle;
730 }
731
732 /*
733 * @implemented (Wine 13 sep 2008)
734 */
735 BOOL
736 WINAPI
737 FindVolumeClose(IN HANDLE hFindVolume)
738 {
739 return RtlFreeHeap(RtlGetProcessHeap(), 0, hFindVolume);
740 }
741
742 /*
743 * @implemented
744 */
745 BOOL
746 WINAPI
747 GetVolumePathNameA(IN LPCSTR lpszFileName,
748 IN LPSTR lpszVolumePathName,
749 IN DWORD cchBufferLength)
750 {
751 PWCHAR FileNameW = NULL;
752 WCHAR VolumePathName[MAX_PATH];
753 BOOL Result;
754
755 if (lpszFileName)
756 {
757 if (!(FileNameW = FilenameA2W(lpszFileName, FALSE)))
758 return FALSE;
759 }
760
761 Result = GetVolumePathNameW(FileNameW, VolumePathName, cchBufferLength);
762
763 if (Result)
764 FilenameW2A_N(lpszVolumePathName, MAX_PATH, VolumePathName, -1);
765
766 return Result;
767 }
768
769 /*
770 * @implemented
771 */
772 BOOL
773 WINAPI
774 GetVolumePathNameW(IN LPCWSTR lpszFileName,
775 IN LPWSTR lpszVolumePathName,
776 IN DWORD cchBufferLength)
777 {
778 DWORD PathLength;
779 UNICODE_STRING UnicodeFilePath;
780 LPWSTR FilePart;
781 PWSTR FullFilePath, FilePathName;
782 ULONG PathSize;
783 WCHAR VolumeName[MAX_PATH];
784 DWORD ErrorCode;
785 BOOL Result = FALSE;
786
787 if (!lpszFileName || !lpszVolumePathName || !cchBufferLength)
788 {
789 SetLastError(ERROR_INVALID_PARAMETER);
790 return FALSE;
791 }
792
793 if (!(PathLength = GetFullPathNameW(lpszFileName, 0, NULL, NULL)))
794 {
795 return Result;
796 }
797 else
798 {
799 PathLength = PathLength + 10;
800 PathSize = PathLength * sizeof(WCHAR);
801
802 if (!(FullFilePath = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize)))
803 {
804 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
805 return Result;
806 }
807
808 if (!GetFullPathNameW(lpszFileName, PathLength, FullFilePath, &FilePart))
809 {
810 RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath);
811 return Result;
812 }
813
814 RtlInitUnicodeString(&UnicodeFilePath, FullFilePath);
815
816 if (UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] != '\\')
817 {
818 UnicodeFilePath.Length += sizeof(WCHAR);
819 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] = '\\';
820 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
821 }
822
823 if (!(FilePathName = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize)))
824 {
825 RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath);
826 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
827 return Result;
828 }
829
830 while (!GetVolumeNameForVolumeMountPointW(UnicodeFilePath.Buffer,
831 VolumeName,
832 MAX_PATH))
833 {
834 if (((UnicodeFilePath.Length == 4) && (UnicodeFilePath.Buffer[0] == '\\') &&
835 (UnicodeFilePath.Buffer[1] == '\\')) || ((UnicodeFilePath.Length == 6) &&
836 (UnicodeFilePath.Buffer[1] == ':')))
837 {
838 break;
839 }
840
841 UnicodeFilePath.Length -= sizeof(WCHAR);
842 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
843
844 memcpy(FilePathName, UnicodeFilePath.Buffer, UnicodeFilePath.Length);
845 FilePathName[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
846
847 if (!GetFullPathNameW(FilePathName, PathLength, FullFilePath, &FilePart))
848 {
849 goto Cleanup2;
850 }
851
852 if (!FilePart)
853 {
854 RtlInitUnicodeString(&UnicodeFilePath, FullFilePath);
855 UnicodeFilePath.Length += sizeof(WCHAR);
856 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] = '\\';
857 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
858 break;
859 }
860
861 FilePart[0] = '\0';
862 RtlInitUnicodeString(&UnicodeFilePath, FullFilePath);
863 }
864 }
865
866 if (UnicodeFilePath.Length > (cchBufferLength * sizeof(WCHAR)) - sizeof(WCHAR))
867 {
868 ErrorCode = ERROR_FILENAME_EXCED_RANGE;
869 goto Cleanup1;
870 }
871
872 memcpy(lpszVolumePathName, UnicodeFilePath.Buffer, UnicodeFilePath.Length);
873 lpszVolumePathName[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
874
875 Result = TRUE;
876 goto Cleanup2;
877
878 Cleanup1:
879 SetLastError(ErrorCode);
880 Cleanup2:
881 RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath);
882 RtlFreeHeap(RtlGetProcessHeap(), 0, FilePathName);
883 return Result;
884 }
885
886 /*
887 * @implemented
888 */
889 BOOL
890 WINAPI
891 FindNextVolumeA(IN HANDLE handle,
892 IN LPSTR volume,
893 IN DWORD len)
894 {
895 WCHAR *buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, len * sizeof(WCHAR));
896 BOOL ret;
897
898 if (!buffer)
899 {
900 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
901 return FALSE;
902 }
903
904 if ((ret = FindNextVolumeW( handle, buffer, len )))
905 {
906 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL )) ret = FALSE;
907 }
908
909 RtlFreeHeap(RtlGetProcessHeap(), 0, buffer);
910 return ret;
911 }
912
913 /*
914 * @implemented
915 */
916 BOOL
917 WINAPI
918 FindNextVolumeW(IN HANDLE handle,
919 IN LPWSTR volume,
920 IN DWORD len)
921 {
922 MOUNTMGR_MOUNT_POINTS *data = handle;
923
924 while (data->Size < data->NumberOfMountPoints)
925 {
926 static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{',};
927 WCHAR *link = (WCHAR *)((char *)data + data->MountPoints[data->Size].SymbolicLinkNameOffset);
928 DWORD size = data->MountPoints[data->Size].SymbolicLinkNameLength;
929 data->Size++;
930 /* skip non-volumes */
931 if (size < sizeof(volumeW) || memcmp( link, volumeW, sizeof(volumeW) )) continue;
932 if (size + sizeof(WCHAR) >= len * sizeof(WCHAR))
933 {
934 SetLastError( ERROR_FILENAME_EXCED_RANGE );
935 return FALSE;
936 }
937 memcpy( volume, link, size );
938 volume[1] = '\\'; /* map \??\ to \\?\ */
939 volume[size / sizeof(WCHAR)] = '\\'; /* Windows appends a backslash */
940 volume[size / sizeof(WCHAR) + 1] = 0;
941 DPRINT( "returning entry %u %s\n", data->Size - 1, volume );
942 return TRUE;
943 }
944 SetLastError( ERROR_NO_MORE_FILES );
945 return FALSE;
946 }
947
948 /*
949 * @implemented
950 */
951 BOOL
952 WINAPI
953 GetVolumePathNamesForVolumeNameA(IN LPCSTR lpszVolumeName,
954 IN LPSTR lpszVolumePathNames,
955 IN DWORD cchBufferLength,
956 OUT PDWORD lpcchReturnLength)
957 {
958 BOOL Ret;
959 NTSTATUS Status;
960 DWORD cchReturnLength;
961 ANSI_STRING VolumePathName;
962 PUNICODE_STRING VolumeNameU;
963 UNICODE_STRING VolumePathNamesU;
964
965 /* Convert volume name to unicode */
966 VolumeNameU = Basep8BitStringToStaticUnicodeString(lpszVolumeName);
967 if (VolumeNameU == NULL)
968 {
969 return FALSE;
970 }
971
972 /* Initialize the strings we'll use later on */
973 VolumePathName.Length = 0;
974 VolumePathName.MaximumLength = cchBufferLength;
975 VolumePathName.Buffer = lpszVolumePathNames;
976
977 VolumePathNamesU.Length = 0;
978 VolumePathNamesU.MaximumLength = sizeof(WCHAR) * cchBufferLength;
979 /* If caller provided a non 0 sized string, allocate a buffer for our unicode string */
980 if (VolumePathNamesU.MaximumLength != 0)
981 {
982 VolumePathNamesU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.MaximumLength);
983 if (VolumePathNamesU.Buffer == NULL)
984 {
985 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
986 return FALSE;
987 }
988 }
989 else
990 {
991 VolumePathNamesU.Buffer = NULL;
992 }
993
994 /* Call the -W implementation */
995 Ret = GetVolumePathNamesForVolumeNameW(VolumeNameU->Buffer, VolumePathNamesU.Buffer,
996 cchBufferLength, &cchReturnLength);
997 /* Call succeed, we'll return the total length */
998 if (Ret)
999 {
1000 VolumePathNamesU.Length = sizeof(WCHAR) * cchReturnLength;
1001 }
1002 else
1003 {
1004 /* Else, if we fail for anything else than too small buffer, quit */
1005 if (GetLastError() != ERROR_MORE_DATA)
1006 {
1007 if (VolumePathNamesU.Buffer != NULL)
1008 {
1009 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.Buffer);
1010 }
1011
1012 return FALSE;
1013 }
1014
1015 /* Otherwise, we'll just copy as much as we can */
1016 VolumePathNamesU.Length = sizeof(WCHAR) * cchBufferLength;
1017 }
1018
1019 /* Convert our output string back to ANSI */
1020 Status = RtlUnicodeStringToAnsiString(&VolumePathName, &VolumePathNamesU, FALSE);
1021 if (!NT_SUCCESS(Status))
1022 {
1023 BaseSetLastNTError(Status);
1024
1025 if (VolumePathNamesU.Buffer != NULL)
1026 {
1027 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.Buffer);
1028 }
1029
1030 return FALSE;
1031 }
1032
1033 /* If caller wants return length, two cases... */
1034 if (lpcchReturnLength != NULL)
1035 {
1036 /* We succeed: return the copied length */
1037 if (Ret)
1038 {
1039 *lpcchReturnLength = VolumePathName.Length;
1040 }
1041 /* We failed, return the size we would have loved having! */
1042 else
1043 {
1044 *lpcchReturnLength = sizeof(WCHAR) * cchReturnLength;
1045 }
1046 }
1047
1048 /* Release our buffer if allocated */
1049 if (VolumePathNamesU.Buffer != NULL)
1050 {
1051 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePathNamesU.Buffer);
1052 }
1053
1054 return Ret;
1055 }
1056
1057
1058 /*
1059 * @unimplemented
1060 */
1061 BOOL
1062 WINAPI
1063 GetVolumePathNamesForVolumeNameW(IN LPCWSTR lpszVolumeName,
1064 IN LPWSTR lpszVolumePathNames,
1065 IN DWORD cchBufferLength,
1066 OUT PDWORD lpcchReturnLength)
1067 {
1068 STUB;
1069 return 0;
1070 }
1071
1072 /* EOF */