[CMAKE]
[reactos.git] / dll / win32 / kernel32 / 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 SetLastErrorByStatus (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 SetLastErrorByStatus(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 SetLastErrorByStatus (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 SetLastErrorByStatus (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 HANDLE hFile;
438 NTSTATUS Status;
439 UNICODE_STRING NtPath;
440 OBJECT_ATTRIBUTES ObjectAttributes;
441 WCHAR Buffer[MAX_PATH+1];
442
443 if (!lpRootPathName)
444 {
445 DWORD cBytes;
446
447 /* Get current directory */
448 cBytes = RtlGetCurrentDirectory_U(sizeof(Buffer), Buffer);
449 if(cBytes < sizeof(Buffer))
450 {
451 ASSERT(cBytes < MAX_PATH*sizeof(WCHAR));
452 Buffer[cBytes/sizeof(WCHAR)] = L'\\';
453 Buffer[cBytes/sizeof(WCHAR)+1] = L'\0';
454 lpRootPathName = Buffer;
455 } /* else fail... should we allow longer current dirs? */
456 }
457
458 if (!RtlDosPathNameToNtPathName_U(lpRootPathName, &NtPath, NULL, NULL))
459 {
460 WARN("Invalid path: %ls\n", lpRootPathName);
461 return DRIVE_NO_ROOT_DIR;
462 }
463
464 /* Path from RtlDosPathNameToNtPathName_U does not contain '/' and multiple '\\' in a row */
465 if(!NtPath.Length || NtPath.Buffer[NtPath.Length/sizeof(WCHAR)-1] != L'\\')
466 {
467 /* Path must be ended by slash */
468 WARN("Invalid path: %ls\n", NtPath.Buffer);
469 return DRIVE_NO_ROOT_DIR;
470 }
471
472 /* Remove ending slash */
473 NtPath.Length -= sizeof(WCHAR);
474
475 InitializeObjectAttributes(&ObjectAttributes,
476 &NtPath,
477 OBJ_CASE_INSENSITIVE,
478 NULL,
479 NULL);
480
481 Status = NtCreateFile(&hFile,
482 FILE_GENERIC_READ,
483 &ObjectAttributes,
484 &IoStatusBlock,
485 NULL,
486 0,
487 FILE_SHARE_READ|FILE_SHARE_WRITE,
488 FILE_OPEN,
489 0,
490 NULL,
491 0);
492
493 RtlFreeUnicodeString(&NtPath);
494
495 if (!NT_SUCCESS(Status))
496 {
497 WARN("Invalid path: %ls\n", lpRootPathName);
498 return DRIVE_NO_ROOT_DIR; /* According to WINE regression tests */
499 }
500
501 Status = NtQueryVolumeInformationFile (hFile,
502 &IoStatusBlock,
503 &FileFsDevice,
504 sizeof(FILE_FS_DEVICE_INFORMATION),
505 FileFsDeviceInformation);
506 CloseHandle(hFile);
507 if (!NT_SUCCESS(Status))
508 {
509 ERR("NtQueryVolumeInformationFile failed for %ls\n", lpRootPathName);
510 return DRIVE_UNKNOWN;
511 }
512
513 switch (FileFsDevice.DeviceType)
514 {
515 case FILE_DEVICE_CD_ROM:
516 case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
517 return DRIVE_CDROM;
518 case FILE_DEVICE_VIRTUAL_DISK:
519 return DRIVE_RAMDISK;
520 case FILE_DEVICE_NETWORK_FILE_SYSTEM:
521 return DRIVE_REMOTE;
522 case FILE_DEVICE_DISK:
523 case FILE_DEVICE_DISK_FILE_SYSTEM:
524 if (FileFsDevice.Characteristics & FILE_REMOTE_DEVICE)
525 return DRIVE_REMOTE;
526 if (FileFsDevice.Characteristics & FILE_REMOVABLE_MEDIA)
527 return DRIVE_REMOVABLE;
528 return DRIVE_FIXED;
529 }
530
531 ERR("Returning DRIVE_UNKNOWN for device type %d\n", FileFsDevice.DeviceType);
532
533 return DRIVE_UNKNOWN;
534 }
535
536
537 /*
538 * @implemented
539 */
540 BOOL WINAPI
541 GetVolumeInformationA(
542 LPCSTR lpRootPathName,
543 LPSTR lpVolumeNameBuffer,
544 DWORD nVolumeNameSize,
545 LPDWORD lpVolumeSerialNumber,
546 LPDWORD lpMaximumComponentLength,
547 LPDWORD lpFileSystemFlags,
548 LPSTR lpFileSystemNameBuffer,
549 DWORD nFileSystemNameSize
550 )
551 {
552 UNICODE_STRING FileSystemNameU;
553 UNICODE_STRING VolumeNameU = { 0, 0, NULL };
554 ANSI_STRING VolumeName;
555 ANSI_STRING FileSystemName;
556 PWCHAR RootPathNameW;
557 BOOL Result;
558
559 if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE)))
560 return FALSE;
561
562 if (lpVolumeNameBuffer)
563 {
564 VolumeNameU.MaximumLength = (USHORT)nVolumeNameSize * sizeof(WCHAR);
565 VolumeNameU.Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
566 0,
567 VolumeNameU.MaximumLength);
568 if (VolumeNameU.Buffer == NULL)
569 {
570 goto FailNoMem;
571 }
572 }
573
574 if (lpFileSystemNameBuffer)
575 {
576 FileSystemNameU.Length = 0;
577 FileSystemNameU.MaximumLength = (USHORT)nFileSystemNameSize * sizeof(WCHAR);
578 FileSystemNameU.Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
579 0,
580 FileSystemNameU.MaximumLength);
581 if (FileSystemNameU.Buffer == NULL)
582 {
583 if (VolumeNameU.Buffer != NULL)
584 {
585 RtlFreeHeap(RtlGetProcessHeap(),
586 0,
587 VolumeNameU.Buffer);
588 }
589
590 FailNoMem:
591 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
592 return FALSE;
593 }
594 }
595
596 Result = GetVolumeInformationW (RootPathNameW,
597 lpVolumeNameBuffer ? VolumeNameU.Buffer : NULL,
598 nVolumeNameSize,
599 lpVolumeSerialNumber,
600 lpMaximumComponentLength,
601 lpFileSystemFlags,
602 lpFileSystemNameBuffer ? FileSystemNameU.Buffer : NULL,
603 nFileSystemNameSize);
604
605 if (Result)
606 {
607 if (lpVolumeNameBuffer)
608 {
609 VolumeNameU.Length = wcslen(VolumeNameU.Buffer) * sizeof(WCHAR);
610 VolumeName.Length = 0;
611 VolumeName.MaximumLength = (USHORT)nVolumeNameSize;
612 VolumeName.Buffer = lpVolumeNameBuffer;
613 }
614
615 if (lpFileSystemNameBuffer)
616 {
617 FileSystemNameU.Length = wcslen(FileSystemNameU.Buffer) * sizeof(WCHAR);
618 FileSystemName.Length = 0;
619 FileSystemName.MaximumLength = (USHORT)nFileSystemNameSize;
620 FileSystemName.Buffer = lpFileSystemNameBuffer;
621 }
622
623 /* convert unicode strings to ansi (or oem) */
624 if (bIsFileApiAnsi)
625 {
626 if (lpVolumeNameBuffer)
627 {
628 RtlUnicodeStringToAnsiString (&VolumeName,
629 &VolumeNameU,
630 FALSE);
631 }
632 if (lpFileSystemNameBuffer)
633 {
634 RtlUnicodeStringToAnsiString (&FileSystemName,
635 &FileSystemNameU,
636 FALSE);
637 }
638 }
639 else
640 {
641 if (lpVolumeNameBuffer)
642 {
643 RtlUnicodeStringToOemString (&VolumeName,
644 &VolumeNameU,
645 FALSE);
646 }
647 if (lpFileSystemNameBuffer)
648 {
649 RtlUnicodeStringToOemString (&FileSystemName,
650 &FileSystemNameU,
651 FALSE);
652 }
653 }
654 }
655
656 if (lpVolumeNameBuffer)
657 {
658 RtlFreeHeap (RtlGetProcessHeap (),
659 0,
660 VolumeNameU.Buffer);
661 }
662 if (lpFileSystemNameBuffer)
663 {
664 RtlFreeHeap (RtlGetProcessHeap (),
665 0,
666 FileSystemNameU.Buffer);
667 }
668
669 return Result;
670 }
671
672 #define FS_VOLUME_BUFFER_SIZE (MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_VOLUME_INFORMATION))
673
674 #define FS_ATTRIBUTE_BUFFER_SIZE (MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_ATTRIBUTE_INFORMATION))
675
676 /*
677 * @implemented
678 */
679 BOOL WINAPI
680 GetVolumeInformationW(
681 LPCWSTR lpRootPathName,
682 LPWSTR lpVolumeNameBuffer,
683 DWORD nVolumeNameSize,
684 LPDWORD lpVolumeSerialNumber,
685 LPDWORD lpMaximumComponentLength,
686 LPDWORD lpFileSystemFlags,
687 LPWSTR lpFileSystemNameBuffer,
688 DWORD nFileSystemNameSize
689 )
690 {
691 PFILE_FS_VOLUME_INFORMATION FileFsVolume;
692 PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute;
693 IO_STATUS_BLOCK IoStatusBlock;
694 WCHAR RootPathName[MAX_PATH];
695 UCHAR Buffer[max(FS_VOLUME_BUFFER_SIZE, FS_ATTRIBUTE_BUFFER_SIZE)];
696
697 HANDLE hFile;
698 NTSTATUS errCode;
699
700 FileFsVolume = (PFILE_FS_VOLUME_INFORMATION)Buffer;
701 FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer;
702
703 TRACE("FileFsVolume %p\n", FileFsVolume);
704 TRACE("FileFsAttribute %p\n", FileFsAttribute);
705
706 if (!lpRootPathName || !wcscmp(lpRootPathName, L""))
707 {
708 GetCurrentDirectoryW (MAX_PATH, RootPathName);
709 }
710 else
711 {
712 wcsncpy (RootPathName, lpRootPathName, 3);
713 }
714 RootPathName[3] = 0;
715
716 hFile = InternalOpenDirW(RootPathName, FALSE);
717 if (hFile == INVALID_HANDLE_VALUE)
718 {
719 return FALSE;
720 }
721
722 TRACE("hFile: %x\n", hFile);
723 errCode = NtQueryVolumeInformationFile(hFile,
724 &IoStatusBlock,
725 FileFsVolume,
726 FS_VOLUME_BUFFER_SIZE,
727 FileFsVolumeInformation);
728 if ( !NT_SUCCESS(errCode) )
729 {
730 WARN("Status: %x\n", errCode);
731 CloseHandle(hFile);
732 SetLastErrorByStatus (errCode);
733 return FALSE;
734 }
735
736 if (lpVolumeSerialNumber)
737 *lpVolumeSerialNumber = FileFsVolume->VolumeSerialNumber;
738
739 if (lpVolumeNameBuffer)
740 {
741 if (nVolumeNameSize * sizeof(WCHAR) >= FileFsVolume->VolumeLabelLength + sizeof(WCHAR))
742 {
743 memcpy(lpVolumeNameBuffer,
744 FileFsVolume->VolumeLabel,
745 FileFsVolume->VolumeLabelLength);
746 lpVolumeNameBuffer[FileFsVolume->VolumeLabelLength / sizeof(WCHAR)] = 0;
747 }
748 else
749 {
750 CloseHandle(hFile);
751 SetLastError(ERROR_MORE_DATA);
752 return FALSE;
753 }
754 }
755
756 errCode = NtQueryVolumeInformationFile (hFile,
757 &IoStatusBlock,
758 FileFsAttribute,
759 FS_ATTRIBUTE_BUFFER_SIZE,
760 FileFsAttributeInformation);
761 CloseHandle(hFile);
762 if (!NT_SUCCESS(errCode))
763 {
764 WARN("Status: %x\n", errCode);
765 SetLastErrorByStatus (errCode);
766 return FALSE;
767 }
768
769 if (lpFileSystemFlags)
770 *lpFileSystemFlags = FileFsAttribute->FileSystemAttributes;
771 if (lpMaximumComponentLength)
772 *lpMaximumComponentLength = FileFsAttribute->MaximumComponentNameLength;
773 if (lpFileSystemNameBuffer)
774 {
775 if (nFileSystemNameSize * sizeof(WCHAR) >= FileFsAttribute->FileSystemNameLength + sizeof(WCHAR))
776 {
777 memcpy(lpFileSystemNameBuffer,
778 FileFsAttribute->FileSystemName,
779 FileFsAttribute->FileSystemNameLength);
780 lpFileSystemNameBuffer[FileFsAttribute->FileSystemNameLength / sizeof(WCHAR)] = 0;
781 }
782 else
783 {
784 SetLastError(ERROR_MORE_DATA);
785 return FALSE;
786 }
787 }
788 return TRUE;
789 }
790
791
792 /*
793 * @implemented
794 */
795 BOOL
796 WINAPI
797 SetVolumeLabelA (
798 LPCSTR lpRootPathName,
799 LPCSTR lpVolumeName /* NULL if deleting label */
800 )
801 {
802 PWCHAR RootPathNameW;
803 PWCHAR VolumeNameW = NULL;
804 BOOL Result;
805
806 if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE)))
807 return FALSE;
808
809 if (lpVolumeName)
810 {
811 if (!(VolumeNameW = FilenameA2W(lpVolumeName, TRUE)))
812 return FALSE;
813 }
814
815 Result = SetVolumeLabelW (RootPathNameW,
816 VolumeNameW);
817
818 if (VolumeNameW)
819 {
820 RtlFreeHeap (RtlGetProcessHeap (),
821 0,
822 VolumeNameW );
823 }
824
825 return Result;
826 }
827
828
829 /*
830 * @implemented
831 */
832 BOOL WINAPI
833 SetVolumeLabelW(
834 LPCWSTR lpRootPathName,
835 LPCWSTR lpVolumeName /* NULL if deleting label */
836 )
837 {
838 PFILE_FS_LABEL_INFORMATION LabelInfo;
839 IO_STATUS_BLOCK IoStatusBlock;
840 ULONG LabelLength;
841 HANDLE hFile;
842 NTSTATUS Status;
843
844 LabelLength = wcslen(lpVolumeName) * sizeof(WCHAR);
845 LabelInfo = RtlAllocateHeap(RtlGetProcessHeap(),
846 0,
847 sizeof(FILE_FS_LABEL_INFORMATION) +
848 LabelLength);
849 if (LabelInfo == NULL)
850 {
851 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
852 return FALSE;
853 }
854 LabelInfo->VolumeLabelLength = LabelLength;
855 memcpy(LabelInfo->VolumeLabel,
856 lpVolumeName,
857 LabelLength);
858
859 hFile = InternalOpenDirW(lpRootPathName, TRUE);
860 if (INVALID_HANDLE_VALUE == hFile)
861 {
862 RtlFreeHeap(RtlGetProcessHeap(),
863 0,
864 LabelInfo);
865 return FALSE;
866 }
867
868 Status = NtSetVolumeInformationFile(hFile,
869 &IoStatusBlock,
870 LabelInfo,
871 sizeof(FILE_FS_LABEL_INFORMATION) +
872 LabelLength,
873 FileFsLabelInformation);
874
875 RtlFreeHeap(RtlGetProcessHeap(),
876 0,
877 LabelInfo);
878
879 if (!NT_SUCCESS(Status))
880 {
881 WARN("Status: %x\n", Status);
882 CloseHandle(hFile);
883 SetLastErrorByStatus(Status);
884 return FALSE;
885 }
886
887 CloseHandle(hFile);
888 return TRUE;
889 }
890
891 /**
892 * @name GetVolumeNameForVolumeMountPointW
893 *
894 * Return an unique volume name for a drive root or mount point.
895 *
896 * @param VolumeMountPoint
897 * Pointer to string that contains either root drive name or
898 * mount point name.
899 * @param VolumeName
900 * Pointer to buffer that is filled with resulting unique
901 * volume name on success.
902 * @param VolumeNameLength
903 * Size of VolumeName buffer in TCHARs.
904 *
905 * @return
906 * TRUE when the function succeeds and the VolumeName buffer is filled,
907 * FALSE otherwise.
908 */
909
910 BOOL WINAPI
911 GetVolumeNameForVolumeMountPointW(
912 IN LPCWSTR VolumeMountPoint,
913 OUT LPWSTR VolumeName,
914 IN DWORD VolumeNameLength)
915 {
916 UNICODE_STRING NtFileName;
917 OBJECT_ATTRIBUTES ObjectAttributes;
918 HANDLE FileHandle;
919 IO_STATUS_BLOCK Iosb;
920 ULONG BufferLength;
921 PMOUNTDEV_NAME MountDevName;
922 PMOUNTMGR_MOUNT_POINT MountPoint;
923 ULONG MountPointSize;
924 PMOUNTMGR_MOUNT_POINTS MountPoints;
925 ULONG Index;
926 PUCHAR SymbolicLinkName;
927 BOOL Result;
928 NTSTATUS Status;
929
930 if (!VolumeMountPoint || !VolumeMountPoint[0])
931 {
932 SetLastError(ERROR_PATH_NOT_FOUND);
933 return FALSE;
934 }
935
936 /*
937 * First step is to convert the passed volume mount point name to
938 * an NT acceptable name.
939 */
940
941 if (!RtlDosPathNameToNtPathName_U(VolumeMountPoint, &NtFileName, NULL, NULL))
942 {
943 SetLastError(ERROR_PATH_NOT_FOUND);
944 return FALSE;
945 }
946
947 if (NtFileName.Length > sizeof(WCHAR) &&
948 NtFileName.Buffer[(NtFileName.Length / sizeof(WCHAR)) - 1] == '\\')
949 {
950 NtFileName.Length -= sizeof(WCHAR);
951 }
952
953 /*
954 * Query mount point device name which we will later use for determining
955 * the volume name.
956 */
957
958 InitializeObjectAttributes(&ObjectAttributes, &NtFileName, 0, NULL, NULL);
959 Status = NtOpenFile(&FileHandle, FILE_READ_ATTRIBUTES | SYNCHRONIZE,
960 &ObjectAttributes, &Iosb,
961 FILE_SHARE_READ | FILE_SHARE_WRITE,
962 FILE_SYNCHRONOUS_IO_NONALERT);
963 RtlFreeUnicodeString(&NtFileName);
964 if (!NT_SUCCESS(Status))
965 {
966 SetLastErrorByStatus(Status);
967 return FALSE;
968 }
969
970 BufferLength = sizeof(MOUNTDEV_NAME) + 50 * sizeof(WCHAR);
971 do
972 {
973 MountDevName = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
974 if (MountDevName == NULL)
975 {
976 NtClose(FileHandle);
977 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
978 return FALSE;
979 }
980
981 Status = NtDeviceIoControlFile(FileHandle, NULL, NULL, NULL, &Iosb,
982 IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
983 NULL, 0, MountDevName, BufferLength);
984 if (!NT_SUCCESS(Status))
985 {
986 RtlFreeHeap(GetProcessHeap(), 0, MountDevName);
987 if (Status == STATUS_BUFFER_OVERFLOW)
988 {
989 BufferLength = sizeof(MOUNTDEV_NAME) + MountDevName->NameLength;
990 continue;
991 }
992 else
993 {
994 NtClose(FileHandle);
995 SetLastErrorByStatus(Status);
996 return FALSE;
997 }
998 }
999 }
1000 while (!NT_SUCCESS(Status));
1001
1002 NtClose(FileHandle);
1003
1004 /*
1005 * Get the mount point information from mount manager.
1006 */
1007
1008 MountPointSize = MountDevName->NameLength + sizeof(MOUNTMGR_MOUNT_POINT);
1009 MountPoint = RtlAllocateHeap(GetProcessHeap(), 0, MountPointSize);
1010 if (MountPoint == NULL)
1011 {
1012 RtlFreeHeap(GetProcessHeap(), 0, MountDevName);
1013 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1014 return FALSE;
1015 }
1016 RtlZeroMemory(MountPoint, sizeof(MOUNTMGR_MOUNT_POINT));
1017 MountPoint->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
1018 MountPoint->DeviceNameLength = MountDevName->NameLength;
1019 RtlCopyMemory(MountPoint + 1, MountDevName->Name, MountDevName->NameLength);
1020 RtlFreeHeap(RtlGetProcessHeap(), 0, MountDevName);
1021
1022 RtlInitUnicodeString(&NtFileName, L"\\??\\MountPointManager");
1023 InitializeObjectAttributes(&ObjectAttributes, &NtFileName, 0, NULL, NULL);
1024 Status = NtOpenFile(&FileHandle, FILE_GENERIC_READ | SYNCHRONIZE, &ObjectAttributes,
1025 &Iosb, FILE_SHARE_READ | FILE_SHARE_WRITE,
1026 FILE_SYNCHRONOUS_IO_NONALERT);
1027 if (!NT_SUCCESS(Status))
1028 {
1029 SetLastErrorByStatus(Status);
1030 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
1031 return FALSE;
1032 }
1033
1034 BufferLength = sizeof(MOUNTMGR_MOUNT_POINTS);
1035 do
1036 {
1037 MountPoints = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
1038 if (MountPoints == NULL)
1039 {
1040 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
1041 NtClose(FileHandle);
1042 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1043 return FALSE;
1044 }
1045
1046 Status = NtDeviceIoControlFile(FileHandle, NULL, NULL, NULL, &Iosb,
1047 IOCTL_MOUNTMGR_QUERY_POINTS,
1048 MountPoint, MountPointSize,
1049 MountPoints, BufferLength);
1050 if (!NT_SUCCESS(Status))
1051 {
1052 if (Status == STATUS_BUFFER_OVERFLOW)
1053 {
1054 BufferLength = MountPoints->Size;
1055 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
1056 continue;
1057 }
1058 else if (!NT_SUCCESS(Status))
1059 {
1060 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
1061 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
1062 NtClose(FileHandle);
1063 SetLastErrorByStatus(Status);
1064 return FALSE;
1065 }
1066 }
1067 }
1068 while (!NT_SUCCESS(Status));
1069
1070 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
1071 NtClose(FileHandle);
1072
1073 /*
1074 * Now we've gathered info about all mount points mapped to our device, so
1075 * select the correct one and copy it into the output buffer.
1076 */
1077
1078 for (Index = 0; Index < MountPoints->NumberOfMountPoints; Index++)
1079 {
1080 MountPoint = MountPoints->MountPoints + Index;
1081 SymbolicLinkName = (PUCHAR)MountPoints + MountPoint->SymbolicLinkNameOffset;
1082
1083 /*
1084 * Check for "\\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\"
1085 * (with the last slash being optional) style symbolic links.
1086 */
1087
1088 if (MountPoint->SymbolicLinkNameLength == 48 * sizeof(WCHAR) ||
1089 (MountPoint->SymbolicLinkNameLength == 49 * sizeof(WCHAR) &&
1090 SymbolicLinkName[48] == L'\\'))
1091 {
1092 if (RtlCompareMemory(SymbolicLinkName, L"\\??\\Volume{",
1093 11 * sizeof(WCHAR)) == 11 * sizeof(WCHAR) &&
1094 SymbolicLinkName[19] == L'-' && SymbolicLinkName[24] == L'-' &&
1095 SymbolicLinkName[29] == L'-' && SymbolicLinkName[34] == L'-' &&
1096 SymbolicLinkName[47] == L'}')
1097 {
1098 if (VolumeNameLength >= MountPoint->SymbolicLinkNameLength / sizeof(WCHAR))
1099 {
1100 RtlCopyMemory(VolumeName,
1101 (PUCHAR)MountPoints + MountPoint->SymbolicLinkNameOffset,
1102 MountPoint->SymbolicLinkNameLength);
1103 VolumeName[1] = L'\\';
1104 Result = TRUE;
1105 }
1106 else
1107 {
1108 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1109 Result = FALSE;
1110 }
1111
1112 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
1113
1114 return Result;
1115 }
1116 }
1117 }
1118
1119 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
1120 SetLastError(ERROR_INVALID_PARAMETER);
1121
1122 return FALSE;
1123 }
1124
1125 /*
1126 * @implemented (Wine 13 sep 2008)
1127 */
1128 BOOL
1129 WINAPI
1130 GetVolumeNameForVolumeMountPointA(
1131 LPCSTR lpszVolumeMountPoint,
1132 LPSTR lpszVolumeName,
1133 DWORD cchBufferLength
1134 )
1135 {
1136 BOOL ret;
1137 WCHAR volumeW[50], *pathW = NULL;
1138 DWORD len = min( sizeof(volumeW) / sizeof(WCHAR), cchBufferLength );
1139
1140 TRACE("(%s, %p, %x)\n", debugstr_a(lpszVolumeMountPoint), lpszVolumeName, cchBufferLength);
1141
1142 if (!lpszVolumeMountPoint || !(pathW = FilenameA2W( lpszVolumeMountPoint, TRUE )))
1143 return FALSE;
1144
1145 if ((ret = GetVolumeNameForVolumeMountPointW( pathW, volumeW, len )))
1146 FilenameW2A_N( lpszVolumeName, len, volumeW, -1 );
1147
1148 RtlFreeHeap( RtlGetProcessHeap(), 0, pathW );
1149 return ret;
1150 }
1151
1152 /*
1153 * @implemented (Wine 13 sep 2008)
1154 */
1155 HANDLE
1156 WINAPI
1157 FindFirstVolumeW(
1158 LPWSTR volume,
1159 DWORD len
1160 )
1161 {
1162 DWORD size = 1024;
1163 HANDLE mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
1164 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE );
1165 if (mgr == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
1166
1167 for (;;)
1168 {
1169 MOUNTMGR_MOUNT_POINT input;
1170 MOUNTMGR_MOUNT_POINTS *output;
1171
1172 if (!(output = RtlAllocateHeap( RtlGetProcessHeap(), 0, size )))
1173 {
1174 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1175 break;
1176 }
1177 memset( &input, 0, sizeof(input) );
1178
1179 if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, &input, sizeof(input),
1180 output, size, NULL, NULL ))
1181 {
1182 if (GetLastError() != ERROR_MORE_DATA) break;
1183 size = output->Size;
1184 RtlFreeHeap( RtlGetProcessHeap(), 0, output );
1185 continue;
1186 }
1187 CloseHandle( mgr );
1188 /* abuse the Size field to store the current index */
1189 output->Size = 0;
1190 if (!FindNextVolumeW( output, volume, len ))
1191 {
1192 RtlFreeHeap( RtlGetProcessHeap(), 0, output );
1193 return INVALID_HANDLE_VALUE;
1194 }
1195 return (HANDLE)output;
1196 }
1197 CloseHandle( mgr );
1198 return INVALID_HANDLE_VALUE;
1199 }
1200
1201 /*
1202 * @implemented (Wine 13 sep 2008)
1203 */
1204 HANDLE
1205 WINAPI
1206 FindFirstVolumeA(
1207 LPSTR volume,
1208 DWORD len
1209 )
1210 {
1211 WCHAR *buffer = NULL;
1212 HANDLE handle;
1213
1214 buffer = RtlAllocateHeap( RtlGetProcessHeap(), 0, len * sizeof(WCHAR) );
1215
1216 if (!buffer)
1217 {
1218 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1219 return INVALID_HANDLE_VALUE;
1220 }
1221
1222 handle = FindFirstVolumeW( buffer, len );
1223
1224 if (handle != INVALID_HANDLE_VALUE)
1225 {
1226 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL ))
1227 {
1228 FindVolumeClose( handle );
1229 handle = INVALID_HANDLE_VALUE;
1230 }
1231 }
1232 RtlFreeHeap( RtlGetProcessHeap(), 0, buffer );
1233 return handle;
1234 }
1235
1236 /*
1237 * @implemented (Wine 13 sep 2008)
1238 */
1239 BOOL
1240 WINAPI
1241 FindVolumeClose(
1242 HANDLE hFindVolume
1243 )
1244 {
1245 return RtlFreeHeap(RtlGetProcessHeap(), 0, hFindVolume);
1246 }
1247
1248 /*
1249 * @implemented
1250 */
1251 BOOL
1252 WINAPI
1253 GetVolumePathNameA(LPCSTR lpszFileName,
1254 LPSTR lpszVolumePathName,
1255 DWORD cchBufferLength)
1256 {
1257 PWCHAR FileNameW = NULL;
1258 WCHAR VolumePathName[MAX_PATH];
1259 BOOL Result;
1260
1261 if (lpszFileName)
1262 {
1263 if (!(FileNameW = FilenameA2W(lpszFileName, FALSE)))
1264 return FALSE;
1265 }
1266
1267 Result = GetVolumePathNameW(FileNameW, VolumePathName, cchBufferLength);
1268
1269 if (Result)
1270 FilenameW2A_N(lpszVolumePathName, MAX_PATH, VolumePathName, -1);
1271
1272 return Result;
1273 }
1274
1275 /*
1276 * @implemented
1277 */
1278 BOOL
1279 WINAPI
1280 GetVolumePathNameW(LPCWSTR lpszFileName,
1281 LPWSTR lpszVolumePathName,
1282 DWORD cchBufferLength)
1283 {
1284 DWORD PathLength;
1285 UNICODE_STRING UnicodeFilePath;
1286 LPWSTR FilePart;
1287 PWSTR FullFilePath, FilePathName;
1288 ULONG PathSize;
1289 WCHAR VolumeName[MAX_PATH];
1290 DWORD ErrorCode;
1291 BOOL Result = FALSE;
1292
1293 if (!(PathLength = GetFullPathNameW(lpszFileName, 0, NULL, NULL)))
1294 {
1295 return Result;
1296 }
1297 else
1298 {
1299 PathLength = PathLength + 10;
1300 PathSize = PathLength * sizeof(WCHAR);
1301
1302 if (!(FullFilePath = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize)))
1303 {
1304 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1305 return Result;
1306 }
1307
1308 if (!GetFullPathNameW(lpszFileName, PathLength, FullFilePath, &FilePart))
1309 {
1310 RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath);
1311 return Result;
1312 }
1313
1314 RtlInitUnicodeString(&UnicodeFilePath, FullFilePath);
1315
1316 if (UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] != '\\')
1317 {
1318 UnicodeFilePath.Length += sizeof(WCHAR);
1319 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] = '\\';
1320 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
1321 }
1322
1323 if (!(FilePathName = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize)))
1324 {
1325 RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath);
1326 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1327 return Result;
1328 }
1329
1330 while (!GetVolumeNameForVolumeMountPointW(UnicodeFilePath.Buffer,
1331 VolumeName,
1332 MAX_PATH))
1333 {
1334 if (((UnicodeFilePath.Length == 4) && (UnicodeFilePath.Buffer[0] == '\\') &&
1335 (UnicodeFilePath.Buffer[1] == '\\')) || ((UnicodeFilePath.Length == 6) &&
1336 (UnicodeFilePath.Buffer[1] == ':')))
1337 {
1338 break;
1339 }
1340
1341 UnicodeFilePath.Length -= sizeof(WCHAR);
1342 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
1343
1344 memcpy(FilePathName, UnicodeFilePath.Buffer, UnicodeFilePath.Length);
1345 FilePathName[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
1346
1347 if (!GetFullPathNameW(FilePathName, PathLength, FullFilePath, &FilePart))
1348 {
1349 goto Cleanup2;
1350 }
1351
1352 if (!FilePart)
1353 {
1354 RtlInitUnicodeString(&UnicodeFilePath, FullFilePath);
1355 UnicodeFilePath.Length += sizeof(WCHAR);
1356 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR) - 1] = '\\';
1357 UnicodeFilePath.Buffer[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
1358 break;
1359 }
1360
1361 FilePart[0] = '\0';
1362 RtlInitUnicodeString(&UnicodeFilePath, FullFilePath);
1363 }
1364 }
1365
1366 if (UnicodeFilePath.Length > (cchBufferLength * sizeof(WCHAR)) - sizeof(WCHAR))
1367 {
1368 ErrorCode = ERROR_FILENAME_EXCED_RANGE;
1369 goto Cleanup1;
1370 }
1371
1372 memcpy(lpszVolumePathName, UnicodeFilePath.Buffer, UnicodeFilePath.Length);
1373 lpszVolumePathName[UnicodeFilePath.Length / sizeof(WCHAR)] = '\0';
1374
1375 Result = TRUE;
1376 goto Cleanup2;
1377
1378 Cleanup1:
1379 SetLastError(ErrorCode);
1380 Cleanup2:
1381 RtlFreeHeap(RtlGetProcessHeap(), 0, FullFilePath);
1382 RtlFreeHeap(RtlGetProcessHeap(), 0, FilePathName);
1383 return Result;
1384 }
1385
1386 /* EOF */