2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: dll/win32/kernel32/client/file/disk.c
5 * PURPOSE: Disk and Drive functions
6 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
7 * Erik Bos, Alexandre Julliard :
8 * GetLogicalDriveStringsA,
9 * GetLogicalDriveStringsW, GetLogicalDrives
13 //WINE copyright notice:
15 * DOS drives handling functions
17 * Copyright 1993 Erik Bos
18 * Copyright 1996 Alexandre Julliard
27 #define MAX_DOS_DRIVES 26
32 /* Synced to Wine-2008/12/28 */
35 GetLogicalDriveStringsA(IN DWORD nBufferLength
,
42 dwDriveMap
= GetLogicalDrives();
44 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
46 if (dwDriveMap
& (1<<drive
))
51 if ((count
* 4) + 1 > nBufferLength
) return ((count
* 4) + 1);
55 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
56 if (dwDriveMap
& (1<<drive
))
58 *p
++ = 'A' + (UCHAR
)drive
;
71 /* Synced to Wine-2008/12/28 */
74 GetLogicalDriveStringsW(IN DWORD nBufferLength
,
81 dwDriveMap
= GetLogicalDrives();
83 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
85 if (dwDriveMap
& (1<<drive
))
89 if ((count
* 4) + 1 > nBufferLength
) return ((count
* 4) + 1);
92 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
93 if (dwDriveMap
& (1<<drive
))
95 *p
++ = (WCHAR
)('A' + drive
);
108 /* Synced to Wine-? */
111 GetLogicalDrives(VOID
)
114 PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo
;
116 /* Get the Device Map for this Process */
117 Status
= NtQueryInformationProcess(NtCurrentProcess(),
119 &ProcessDeviceMapInfo
,
120 sizeof(ProcessDeviceMapInfo
),
123 /* Return the Drive Map */
124 if (!NT_SUCCESS(Status
))
126 BaseSetLastNTError(Status
);
130 return ProcessDeviceMapInfo
.Query
.DriveMap
;
138 GetDiskFreeSpaceA(IN LPCSTR lpRootPathName
,
139 OUT LPDWORD lpSectorsPerCluster
,
140 OUT LPDWORD lpBytesPerSector
,
141 OUT LPDWORD lpNumberOfFreeClusters
,
142 OUT LPDWORD lpTotalNumberOfClusters
)
145 PUNICODE_STRING RootPathU
;
147 RootPath
= lpRootPathName
;
148 if (RootPath
== NULL
)
153 RootPathU
= Basep8BitStringToStaticUnicodeString(RootPath
);
154 if (RootPathU
== NULL
)
159 return GetDiskFreeSpaceW(RootPathU
->Buffer
, lpSectorsPerCluster
,
160 lpBytesPerSector
, lpNumberOfFreeClusters
,
161 lpTotalNumberOfClusters
);
169 GetDiskFreeSpaceW(IN LPCWSTR lpRootPathName
,
170 OUT LPDWORD lpSectorsPerCluster
,
171 OUT LPDWORD lpBytesPerSector
,
172 OUT LPDWORD lpNumberOfFreeClusters
,
173 OUT LPDWORD lpTotalNumberOfClusters
)
179 UNICODE_STRING FileName
;
180 IO_STATUS_BLOCK IoStatusBlock
;
181 OBJECT_ATTRIBUTES ObjectAttributes
;
182 FILE_FS_SIZE_INFORMATION FileFsSize
;
184 /* If no path provided, get root path */
185 RootPath
= lpRootPathName
;
186 if (lpRootPathName
== NULL
)
191 /* Convert the path to NT path */
192 if (!RtlDosPathNameToNtPathName_U(RootPath
, &FileName
, NULL
, NULL
))
194 SetLastError(ERROR_PATH_NOT_FOUND
);
198 /* Open it for disk space query! */
199 InitializeObjectAttributes(&ObjectAttributes
, &FileName
,
200 OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
201 Status
= NtOpenFile(&RootHandle
, SYNCHRONIZE
, &ObjectAttributes
, &IoStatusBlock
,
202 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
203 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_FOR_FREE_SPACE_QUERY
);
204 if (!NT_SUCCESS(Status
))
206 BaseSetLastNTError(Status
);
207 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName
.Buffer
);
208 if (lpBytesPerSector
!= NULL
)
210 *lpBytesPerSector
= 0;
216 /* We don't need the name any longer */
217 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName
.Buffer
);
219 /* Query disk space! */
220 Status
= NtQueryVolumeInformationFile(RootHandle
, &IoStatusBlock
, &FileFsSize
,
221 sizeof(FILE_FS_SIZE_INFORMATION
),
222 FileFsSizeInformation
);
224 if (!NT_SUCCESS(Status
))
226 BaseSetLastNTError(Status
);
230 /* Are we in some compatibility mode where size must be below 2GB? */
231 Below2GB
= ((NtCurrentPeb()->AppCompatFlags
.LowPart
& GetDiskFreeSpace2GB
) == GetDiskFreeSpace2GB
);
233 /* If we're to overflow output, make sure we return the maximum */
234 if (FileFsSize
.TotalAllocationUnits
.HighPart
!= 0)
236 FileFsSize
.TotalAllocationUnits
.LowPart
= -1;
239 if (FileFsSize
.AvailableAllocationUnits
.HighPart
!= 0)
241 FileFsSize
.AvailableAllocationUnits
.LowPart
= -1;
244 /* Return what user asked for */
245 if (lpSectorsPerCluster
!= NULL
)
247 *lpSectorsPerCluster
= FileFsSize
.SectorsPerAllocationUnit
;
250 if (lpBytesPerSector
!= NULL
)
252 *lpBytesPerSector
= FileFsSize
.BytesPerSector
;
255 if (lpNumberOfFreeClusters
!= NULL
)
259 *lpNumberOfFreeClusters
= FileFsSize
.AvailableAllocationUnits
.LowPart
;
261 /* If we have to remain below 2GB... */
266 /* Compute how many clusters there are in less than 2GB: 2 * 1024 * 1024 * 1024- 1 */
267 FreeClusters
= 0x7FFFFFFF / (FileFsSize
.SectorsPerAllocationUnit
* FileFsSize
.BytesPerSector
);
268 /* If that's higher than what was queried, then return the queried value, it's OK! */
269 if (FreeClusters
> FileFsSize
.AvailableAllocationUnits
.LowPart
)
271 FreeClusters
= FileFsSize
.AvailableAllocationUnits
.LowPart
;
274 *lpNumberOfFreeClusters
= FreeClusters
;
278 if (lpTotalNumberOfClusters
!= NULL
)
282 *lpTotalNumberOfClusters
= FileFsSize
.TotalAllocationUnits
.LowPart
;
284 /* If we have to remain below 2GB... */
289 /* Compute how many clusters there are in less than 2GB: 2 * 1024 * 1024 * 1024- 1 */
290 TotalClusters
= 0x7FFFFFFF / (FileFsSize
.SectorsPerAllocationUnit
* FileFsSize
.BytesPerSector
);
291 /* If that's higher than what was queried, then return the queried value, it's OK! */
292 if (TotalClusters
> FileFsSize
.TotalAllocationUnits
.LowPart
)
294 TotalClusters
= FileFsSize
.TotalAllocationUnits
.LowPart
;
297 *lpTotalNumberOfClusters
= TotalClusters
;
309 GetDiskFreeSpaceExA(IN LPCSTR lpDirectoryName OPTIONAL
,
310 OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller
,
311 OUT PULARGE_INTEGER lpTotalNumberOfBytes
,
312 OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes
)
315 PUNICODE_STRING RootPathU
;
317 RootPath
= lpDirectoryName
;
318 if (RootPath
== NULL
)
323 RootPathU
= Basep8BitStringToStaticUnicodeString(RootPath
);
324 if (RootPathU
== NULL
)
329 return GetDiskFreeSpaceExW(RootPathU
->Buffer
, lpFreeBytesAvailableToCaller
,
330 lpTotalNumberOfBytes
, lpTotalNumberOfFreeBytes
);
338 GetDiskFreeSpaceExW(IN LPCWSTR lpDirectoryName OPTIONAL
,
339 OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller
,
340 OUT PULARGE_INTEGER lpTotalNumberOfBytes
,
341 OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes
)
346 UNICODE_STRING FileName
;
347 DWORD BytesPerAllocationUnit
;
348 IO_STATUS_BLOCK IoStatusBlock
;
349 OBJECT_ATTRIBUTES ObjectAttributes
;
350 FILE_FS_SIZE_INFORMATION FileFsSize
;
352 /* If no path provided, get root path */
353 RootPath
= lpDirectoryName
;
354 if (lpDirectoryName
== NULL
)
359 /* Convert the path to NT path */
360 if (!RtlDosPathNameToNtPathName_U(RootPath
, &FileName
, NULL
, NULL
))
362 SetLastError(ERROR_PATH_NOT_FOUND
);
366 /* Open it for disk space query! */
367 InitializeObjectAttributes(&ObjectAttributes
, &FileName
,
368 OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
369 Status
= NtOpenFile(&RootHandle
, SYNCHRONIZE
, &ObjectAttributes
, &IoStatusBlock
,
370 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
371 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_FOR_FREE_SPACE_QUERY
);
372 if (!NT_SUCCESS(Status
))
374 BaseSetLastNTError(Status
);
375 /* If error conversion lead to file not found, override to use path not found
376 * which is more accurate
378 if (GetLastError() == ERROR_FILE_NOT_FOUND
)
380 SetLastError(ERROR_PATH_NOT_FOUND
);
383 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName
.Buffer
);
388 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName
.Buffer
);
390 /* If user asks for lpTotalNumberOfFreeBytes, try to use full size information */
391 if (lpTotalNumberOfFreeBytes
!= NULL
)
393 FILE_FS_FULL_SIZE_INFORMATION FileFsFullSize
;
395 /* Issue the full fs size request */
396 Status
= NtQueryVolumeInformationFile(RootHandle
, &IoStatusBlock
, &FileFsFullSize
,
397 sizeof(FILE_FS_FULL_SIZE_INFORMATION
),
398 FileFsFullSizeInformation
);
399 /* If it succeed, complete out buffers */
400 if (NT_SUCCESS(Status
))
402 /* We can close here, we'll return */
405 /* Compute the size of an AU */
406 BytesPerAllocationUnit
= FileFsFullSize
.SectorsPerAllocationUnit
* FileFsFullSize
.BytesPerSector
;
408 /* And then return what was asked */
409 if (lpFreeBytesAvailableToCaller
!= NULL
)
411 lpFreeBytesAvailableToCaller
->QuadPart
= FileFsFullSize
.CallerAvailableAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
414 if (lpTotalNumberOfBytes
!= NULL
)
416 lpTotalNumberOfBytes
->QuadPart
= FileFsFullSize
.TotalAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
419 /* No need to check for nullness ;-) */
420 lpTotalNumberOfFreeBytes
->QuadPart
= FileFsFullSize
.ActualAvailableAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
426 /* Otherwise, fallback to normal size information */
427 Status
= NtQueryVolumeInformationFile(RootHandle
, &IoStatusBlock
,
428 &FileFsSize
, sizeof(FILE_FS_SIZE_INFORMATION
),
429 FileFsSizeInformation
);
431 if (!NT_SUCCESS(Status
))
433 BaseSetLastNTError(Status
);
437 /* Compute the size of an AU */
438 BytesPerAllocationUnit
= FileFsSize
.SectorsPerAllocationUnit
* FileFsSize
.BytesPerSector
;
440 /* And then return what was asked, available is free, the same! */
441 if (lpFreeBytesAvailableToCaller
!= NULL
)
443 lpFreeBytesAvailableToCaller
->QuadPart
= FileFsSize
.AvailableAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
446 if (lpTotalNumberOfBytes
!= NULL
)
448 lpTotalNumberOfBytes
->QuadPart
= FileFsSize
.TotalAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
451 if (lpTotalNumberOfFreeBytes
!= NULL
)
453 lpTotalNumberOfFreeBytes
->QuadPart
= FileFsSize
.AvailableAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
464 GetDriveTypeA(IN LPCSTR lpRootPathName
)
468 if (lpRootPathName
!= NULL
)
470 PUNICODE_STRING RootPathUStr
;
472 RootPathUStr
= Basep8BitStringToStaticUnicodeString(lpRootPathName
);
473 if (RootPathUStr
== NULL
)
475 return DRIVE_NO_ROOT_DIR
;
478 RootPathU
= RootPathUStr
->Buffer
;
485 return GetDriveTypeW(RootPathU
);
493 GetDriveTypeW(IN LPCWSTR lpRootPathName
)
500 IO_STATUS_BLOCK IoStatusBlock
;
501 OBJECT_ATTRIBUTES ObjectAttributes
;
502 UNICODE_STRING PathName
, VolumeString
;
503 FILE_FS_DEVICE_INFORMATION FileFsDevice
;
504 WCHAR Buffer
[MAX_PATH
], VolumeName
[MAX_PATH
];
506 /* If no path, get one */
507 if (lpRootPathName
== NULL
)
510 /* This will be current drive (<letter>:\ - drop the rest)*/
511 if (RtlGetCurrentDirectory_U(sizeof(Buffer
), Buffer
) > 3 * sizeof(WCHAR
))
513 Buffer
[3] = UNICODE_NULL
;
518 /* Handle broken value */
519 if (lpRootPathName
== (PVOID
)-1)
521 return DRIVE_UNKNOWN
;
524 RootPath
= lpRootPathName
;
525 /* If provided path is 2-len, it might be a drive letter... */
526 if (wcslen(lpRootPathName
) == 2)
529 DriveLetter
= RtlUpcaseUnicodeChar(lpRootPathName
[0]);
530 /* That's a drive letter! */
531 if (DriveLetter
>= L
'A' && DriveLetter
<= L
'Z' && lpRootPathName
[1] == L
':')
533 /* Make it a volume */
534 Buffer
[0] = DriveLetter
;
537 Buffer
[3] = UNICODE_NULL
;
543 /* If the provided looks like a DOS device... Like <letter>:\<0> */
544 DriveLetter
= RtlUpcaseUnicodeChar(RootPath
[0]);
545 /* We'll take the quick path!
546 * We'll find the device type looking at the device map (and types ;-))
547 * associated with the current process
549 if (DriveLetter
>= L
'A' && DriveLetter
<= L
'Z' && RootPath
[1] == L
':' &&
550 RootPath
[2] == L
'\\' && RootPath
[3] == UNICODE_NULL
)
553 PROCESS_DEVICEMAP_INFORMATION DeviceMap
;
555 /* Query the device map */
556 Status
= NtQueryInformationProcess(NtCurrentProcess(), ProcessDeviceMap
,
558 sizeof(PROCESS_DEVICEMAP_INFORMATION
),
560 /* Zero output if we failed */
561 if (!NT_SUCCESS(Status
))
563 RtlZeroMemory(&DeviceMap
, sizeof(PROCESS_DEVICEMAP_INFORMATION
));
566 /* Get our index in the device map */
567 Index
= DriveLetter
- L
'A';
568 /* Check we're in the device map (bit set) */
569 if (((1 << Index
) & DeviceMap
.Query
.DriveMap
) != 0)
571 /* Validate device type and return it */
572 if (DeviceMap
.Query
.DriveType
[Index
] >= DRIVE_REMOVABLE
&&
573 DeviceMap
.Query
.DriveType
[Index
] <= DRIVE_RAMDISK
)
575 return DeviceMap
.Query
.DriveType
[Index
];
577 /* Otherwise, return we don't know the type */
580 return DRIVE_UNKNOWN
;
584 /* We couldn't find ourselves, do it the slow way */
587 /* No path provided, use root */
588 if (lpRootPathName
== NULL
)
593 /* Convert to NT path */
594 if (!RtlDosPathNameToNtPathName_U(RootPath
, &PathName
, NULL
, NULL
))
596 return DRIVE_NO_ROOT_DIR
;
599 /* If not a directory, fail, we need a volume */
600 if (PathName
.Buffer
[(PathName
.Length
/ sizeof(WCHAR
)) - 1] != L
'\\')
602 RtlFreeHeap(RtlGetProcessHeap(), 0, PathName
.Buffer
);
603 return DRIVE_NO_ROOT_DIR
;
606 /* Let's probe for it, by forcing open failure! */
608 InitializeObjectAttributes(&ObjectAttributes
, &PathName
,
609 OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
610 Status
= NtOpenFile(&RootHandle
, SYNCHRONIZE
| FILE_READ_ATTRIBUTES
,
611 &ObjectAttributes
, &IoStatusBlock
,
612 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
613 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
);
614 /* It properly failed! */
615 if (Status
== STATUS_FILE_IS_A_DIRECTORY
)
617 /* It might be a mount point, then, query for target */
618 if (BasepGetVolumeNameFromReparsePoint(lpRootPathName
, VolumeName
, MAX_PATH
, NULL
))
620 /* We'll reopen the target */
621 RtlInitUnicodeString(&VolumeString
, VolumeName
);
622 VolumeName
[1] = L
'?';
623 VolumeString
.Length
-= sizeof(WCHAR
);
624 InitializeObjectAttributes(&ObjectAttributes
, &VolumeString
,
625 OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
630 /* heh. It worked? Or failed for whatever other reason?
631 * Check we have a directory if we get farther in path
633 PathName
.Length
+= sizeof(WCHAR
);
634 if (IsThisARootDirectory(0, &PathName
))
636 /* Yes? Heh, then it's fine, keep our current handle */
641 /* Then, retry to open without forcing non directory type */
642 PathName
.Length
-= sizeof(WCHAR
);
643 if (NT_SUCCESS(Status
))
650 /* Now, we retry without forcing file type - should work now */
653 Status
= NtOpenFile(&RootHandle
, SYNCHRONIZE
| FILE_READ_ATTRIBUTES
,
654 &ObjectAttributes
, &IoStatusBlock
,
655 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
656 FILE_SYNCHRONOUS_IO_NONALERT
);
659 /* We don't need path any longer */
660 RtlFreeHeap(RtlGetProcessHeap(), 0, PathName
.Buffer
);
661 if (!NT_SUCCESS(Status
))
663 return DRIVE_NO_ROOT_DIR
;
666 /* Query the device for its type */
667 Status
= NtQueryVolumeInformationFile(RootHandle
,
670 sizeof(FILE_FS_DEVICE_INFORMATION
),
671 FileFsDeviceInformation
);
672 /* No longer required */
674 if (!NT_SUCCESS(Status
))
676 return DRIVE_UNKNOWN
;
679 /* Do we have a remote device? Return so! */
680 if ((FileFsDevice
.Characteristics
& FILE_REMOTE_DEVICE
) == FILE_REMOTE_DEVICE
)
685 /* Check the device type */
686 switch (FileFsDevice
.DeviceType
)
689 case FILE_DEVICE_CD_ROM
:
690 case FILE_DEVICE_CD_ROM_FILE_SYSTEM
:
694 case FILE_DEVICE_DISK
:
695 case FILE_DEVICE_DISK_FILE_SYSTEM
:
696 /* Removable media? Floppy is one */
697 if ((FileFsDevice
.Characteristics
& FILE_REMOVABLE_MEDIA
) == FILE_REMOVABLE_MEDIA
||
698 (FileFsDevice
.Characteristics
& FILE_FLOPPY_DISKETTE
) == FILE_FLOPPY_DISKETTE
)
700 return DRIVE_REMOVABLE
;
708 case FILE_DEVICE_NETWORK
:
709 case FILE_DEVICE_NETWORK_FILE_SYSTEM
:
712 case FILE_DEVICE_VIRTUAL_DISK
:
713 return DRIVE_RAMDISK
;
716 /* Nothing matching, just fail */
717 return DRIVE_UNKNOWN
;