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
26 DEBUG_CHANNEL(kernel32file
);
28 #define MAX_DOS_DRIVES 26
33 /* Synced to Wine-2008/12/28 */
36 GetLogicalDriveStringsA(IN DWORD nBufferLength
,
43 dwDriveMap
= GetLogicalDrives();
45 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
47 if (dwDriveMap
& (1<<drive
))
52 if ((count
* 4) + 1 > nBufferLength
) return ((count
* 4) + 1);
56 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
57 if (dwDriveMap
& (1<<drive
))
59 *p
++ = 'A' + (UCHAR
)drive
;
72 /* Synced to Wine-2008/12/28 */
75 GetLogicalDriveStringsW(IN DWORD nBufferLength
,
82 dwDriveMap
= GetLogicalDrives();
84 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
86 if (dwDriveMap
& (1<<drive
))
90 if ((count
* 4) + 1 > nBufferLength
) return ((count
* 4) + 1);
93 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
94 if (dwDriveMap
& (1<<drive
))
96 *p
++ = (WCHAR
)('A' + drive
);
109 /* Synced to Wine-? */
112 GetLogicalDrives(VOID
)
115 PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo
;
117 /* Get the Device Map for this Process */
118 Status
= NtQueryInformationProcess(NtCurrentProcess(),
120 &ProcessDeviceMapInfo
,
121 sizeof(ProcessDeviceMapInfo
),
124 /* Return the Drive Map */
125 if (!NT_SUCCESS(Status
))
127 BaseSetLastNTError(Status
);
131 return ProcessDeviceMapInfo
.Query
.DriveMap
;
139 GetDiskFreeSpaceA(IN LPCSTR lpRootPathName
,
140 OUT LPDWORD lpSectorsPerCluster
,
141 OUT LPDWORD lpBytesPerSector
,
142 OUT LPDWORD lpNumberOfFreeClusters
,
143 OUT LPDWORD lpTotalNumberOfClusters
)
146 PUNICODE_STRING RootPathU
;
148 RootPath
= lpRootPathName
;
149 if (RootPath
== NULL
)
154 RootPathU
= Basep8BitStringToStaticUnicodeString(RootPath
);
155 if (RootPathU
== NULL
)
160 return GetDiskFreeSpaceW(RootPathU
->Buffer
, lpSectorsPerCluster
,
161 lpBytesPerSector
, lpNumberOfFreeClusters
,
162 lpTotalNumberOfClusters
);
170 GetDiskFreeSpaceW(IN LPCWSTR lpRootPathName
,
171 OUT LPDWORD lpSectorsPerCluster
,
172 OUT LPDWORD lpBytesPerSector
,
173 OUT LPDWORD lpNumberOfFreeClusters
,
174 OUT LPDWORD lpTotalNumberOfClusters
)
180 UNICODE_STRING FileName
;
181 IO_STATUS_BLOCK IoStatusBlock
;
182 OBJECT_ATTRIBUTES ObjectAttributes
;
183 FILE_FS_SIZE_INFORMATION FileFsSize
;
185 /* If no path provided, get root path */
186 RootPath
= lpRootPathName
;
187 if (lpRootPathName
== NULL
)
192 /* Convert the path to NT path */
193 if (!RtlDosPathNameToNtPathName_U(RootPath
, &FileName
, NULL
, NULL
))
195 SetLastError(ERROR_PATH_NOT_FOUND
);
199 /* Open it for disk space query! */
200 InitializeObjectAttributes(&ObjectAttributes
, &FileName
,
201 OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
202 Status
= NtOpenFile(&RootHandle
, SYNCHRONIZE
, &ObjectAttributes
, &IoStatusBlock
,
203 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
204 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_FOR_FREE_SPACE_QUERY
);
205 if (!NT_SUCCESS(Status
))
207 BaseSetLastNTError(Status
);
208 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName
.Buffer
);
209 if (lpBytesPerSector
!= NULL
)
211 *lpBytesPerSector
= 0;
217 /* We don't need the name any longer */
218 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName
.Buffer
);
220 /* Query disk space! */
221 Status
= NtQueryVolumeInformationFile(RootHandle
, &IoStatusBlock
, &FileFsSize
,
222 sizeof(FILE_FS_SIZE_INFORMATION
),
223 FileFsSizeInformation
);
225 if (!NT_SUCCESS(Status
))
227 BaseSetLastNTError(Status
);
231 /* Are we in some compatibility mode where size must be below 2GB? */
232 Below2GB
= ((NtCurrentPeb()->AppCompatFlags
.LowPart
& GetDiskFreeSpace2GB
) == GetDiskFreeSpace2GB
);
234 /* If we're to overflow output, make sure we return the maximum */
235 if (FileFsSize
.TotalAllocationUnits
.HighPart
!= 0)
237 FileFsSize
.TotalAllocationUnits
.LowPart
= -1;
240 if (FileFsSize
.AvailableAllocationUnits
.HighPart
!= 0)
242 FileFsSize
.AvailableAllocationUnits
.LowPart
= -1;
245 /* Return what user asked for */
246 if (lpSectorsPerCluster
!= NULL
)
248 *lpSectorsPerCluster
= FileFsSize
.SectorsPerAllocationUnit
;
251 if (lpBytesPerSector
!= NULL
)
253 *lpBytesPerSector
= FileFsSize
.BytesPerSector
;
256 if (lpNumberOfFreeClusters
!= NULL
)
260 *lpNumberOfFreeClusters
= FileFsSize
.AvailableAllocationUnits
.LowPart
;
262 /* If we have to remain below 2GB... */
267 /* Compute how many clusters there are in less than 2GB: 2 * 1024 * 1024 * 1024- 1 */
268 FreeClusters
= 0x7FFFFFFF / (FileFsSize
.SectorsPerAllocationUnit
* FileFsSize
.BytesPerSector
);
269 /* If that's higher than what was queried, then return the queried value, it's OK! */
270 if (FreeClusters
> FileFsSize
.AvailableAllocationUnits
.LowPart
)
272 FreeClusters
= FileFsSize
.AvailableAllocationUnits
.LowPart
;
275 *lpNumberOfFreeClusters
= FreeClusters
;
279 if (lpTotalNumberOfClusters
!= NULL
)
283 *lpTotalNumberOfClusters
= FileFsSize
.TotalAllocationUnits
.LowPart
;
285 /* If we have to remain below 2GB... */
290 /* Compute how many clusters there are in less than 2GB: 2 * 1024 * 1024 * 1024- 1 */
291 TotalClusters
= 0x7FFFFFFF / (FileFsSize
.SectorsPerAllocationUnit
* FileFsSize
.BytesPerSector
);
292 /* If that's higher than what was queried, then return the queried value, it's OK! */
293 if (TotalClusters
> FileFsSize
.TotalAllocationUnits
.LowPart
)
295 TotalClusters
= FileFsSize
.TotalAllocationUnits
.LowPart
;
298 *lpTotalNumberOfClusters
= TotalClusters
;
310 GetDiskFreeSpaceExA(IN LPCSTR lpDirectoryName OPTIONAL
,
311 OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller
,
312 OUT PULARGE_INTEGER lpTotalNumberOfBytes
,
313 OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes
)
316 PUNICODE_STRING RootPathU
;
318 RootPath
= lpDirectoryName
;
319 if (RootPath
== NULL
)
324 RootPathU
= Basep8BitStringToStaticUnicodeString(RootPath
);
325 if (RootPathU
== NULL
)
330 return GetDiskFreeSpaceExW(RootPathU
->Buffer
, lpFreeBytesAvailableToCaller
,
331 lpTotalNumberOfBytes
, lpTotalNumberOfFreeBytes
);
339 GetDiskFreeSpaceExW(IN LPCWSTR lpDirectoryName OPTIONAL
,
340 OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller
,
341 OUT PULARGE_INTEGER lpTotalNumberOfBytes
,
342 OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes
)
347 UNICODE_STRING FileName
;
348 DWORD BytesPerAllocationUnit
;
349 IO_STATUS_BLOCK IoStatusBlock
;
350 OBJECT_ATTRIBUTES ObjectAttributes
;
351 FILE_FS_SIZE_INFORMATION FileFsSize
;
353 /* If no path provided, get root path */
354 RootPath
= lpDirectoryName
;
355 if (lpDirectoryName
== NULL
)
360 /* Convert the path to NT path */
361 if (!RtlDosPathNameToNtPathName_U(RootPath
, &FileName
, NULL
, NULL
))
363 SetLastError(ERROR_PATH_NOT_FOUND
);
367 /* Open it for disk space query! */
368 InitializeObjectAttributes(&ObjectAttributes
, &FileName
,
369 OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
370 Status
= NtOpenFile(&RootHandle
, SYNCHRONIZE
, &ObjectAttributes
, &IoStatusBlock
,
371 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
372 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_FOR_FREE_SPACE_QUERY
);
373 if (!NT_SUCCESS(Status
))
375 BaseSetLastNTError(Status
);
376 /* If error conversion lead to file not found, override to use path not found
377 * which is more accurate
379 if (GetLastError() == ERROR_FILE_NOT_FOUND
)
381 SetLastError(ERROR_PATH_NOT_FOUND
);
384 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName
.Buffer
);
389 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName
.Buffer
);
391 /* If user asks for lpTotalNumberOfFreeBytes, try to use full size information */
392 if (lpTotalNumberOfFreeBytes
!= NULL
)
394 FILE_FS_FULL_SIZE_INFORMATION FileFsFullSize
;
396 /* Issue the full fs size request */
397 Status
= NtQueryVolumeInformationFile(RootHandle
, &IoStatusBlock
, &FileFsFullSize
,
398 sizeof(FILE_FS_FULL_SIZE_INFORMATION
),
399 FileFsFullSizeInformation
);
400 /* If it succeed, complete out buffers */
401 if (NT_SUCCESS(Status
))
403 /* We can close here, we'll return */
406 /* Compute the size of an AU */
407 BytesPerAllocationUnit
= FileFsFullSize
.SectorsPerAllocationUnit
* FileFsFullSize
.BytesPerSector
;
409 /* And then return what was asked */
410 if (lpFreeBytesAvailableToCaller
!= NULL
)
412 lpFreeBytesAvailableToCaller
->QuadPart
= FileFsFullSize
.CallerAvailableAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
415 if (lpTotalNumberOfBytes
!= NULL
)
417 lpTotalNumberOfBytes
->QuadPart
= FileFsFullSize
.TotalAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
420 /* No need to check for nullness ;-) */
421 lpTotalNumberOfFreeBytes
->QuadPart
= FileFsFullSize
.ActualAvailableAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
427 /* Otherwise, fallback to normal size information */
428 Status
= NtQueryVolumeInformationFile(RootHandle
, &IoStatusBlock
,
429 &FileFsSize
, sizeof(FILE_FS_SIZE_INFORMATION
),
430 FileFsSizeInformation
);
432 if (!NT_SUCCESS(Status
))
434 BaseSetLastNTError(Status
);
438 /* Compute the size of an AU */
439 BytesPerAllocationUnit
= FileFsSize
.SectorsPerAllocationUnit
* FileFsSize
.BytesPerSector
;
441 /* And then return what was asked, available is free, the same! */
442 if (lpFreeBytesAvailableToCaller
!= NULL
)
444 lpFreeBytesAvailableToCaller
->QuadPart
= FileFsSize
.AvailableAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
447 if (lpTotalNumberOfBytes
!= NULL
)
449 lpTotalNumberOfBytes
->QuadPart
= FileFsSize
.TotalAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
452 if (lpTotalNumberOfFreeBytes
!= NULL
)
454 lpTotalNumberOfFreeBytes
->QuadPart
= FileFsSize
.AvailableAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
465 GetDriveTypeA(IN LPCSTR lpRootPathName
)
467 PWCHAR RootPathNameW
;
470 return GetDriveTypeW(NULL
);
472 if (!(RootPathNameW
= FilenameA2W(lpRootPathName
, FALSE
)))
473 return DRIVE_UNKNOWN
;
475 return GetDriveTypeW(RootPathNameW
);
483 GetDriveTypeW(IN LPCWSTR lpRootPathName
)
485 FILE_FS_DEVICE_INFORMATION FileFsDevice
;
486 OBJECT_ATTRIBUTES ObjectAttributes
;
487 IO_STATUS_BLOCK IoStatusBlock
;
488 UNICODE_STRING PathName
;
491 PWSTR CurrentDir
= NULL
;
496 /* If NULL is passed, use current directory path */
497 DWORD BufferSize
= GetCurrentDirectoryW(0, NULL
);
498 CurrentDir
= HeapAlloc(GetProcessHeap(), 0, BufferSize
* sizeof(WCHAR
));
500 return DRIVE_UNKNOWN
;
501 if (!GetCurrentDirectoryW(BufferSize
, CurrentDir
))
503 HeapFree(GetProcessHeap(), 0, CurrentDir
);
504 return DRIVE_UNKNOWN
;
507 if (wcslen(CurrentDir
) > 3)
510 lpRootPath
= CurrentDir
;
514 size_t Length
= wcslen(lpRootPathName
);
516 TRACE("lpRootPathName: %S\n", lpRootPathName
);
518 lpRootPath
= lpRootPathName
;
521 WCHAR DriveLetter
= RtlUpcaseUnicodeChar(lpRootPathName
[0]);
523 if (DriveLetter
>= L
'A' && DriveLetter
<= L
'Z' && lpRootPathName
[1] == L
':')
525 Length
= (Length
+ 2) * sizeof(WCHAR
);
527 CurrentDir
= HeapAlloc(GetProcessHeap(), 0, Length
);
529 return DRIVE_UNKNOWN
;
531 StringCbPrintfW(CurrentDir
, Length
, L
"%s\\", lpRootPathName
);
533 lpRootPath
= CurrentDir
;
538 TRACE("lpRootPath: %S\n", lpRootPath
);
540 if (!RtlDosPathNameToNtPathName_U(lpRootPath
, &PathName
, NULL
, NULL
))
542 if (CurrentDir
!= NULL
)
543 HeapFree(GetProcessHeap(), 0, CurrentDir
);
545 return DRIVE_NO_ROOT_DIR
;
548 TRACE("PathName: %S\n", PathName
.Buffer
);
550 if (CurrentDir
!= NULL
)
551 HeapFree(GetProcessHeap(), 0, CurrentDir
);
553 if (PathName
.Buffer
[(PathName
.Length
>> 1) - 1] != L
'\\')
555 return DRIVE_NO_ROOT_DIR
;
558 InitializeObjectAttributes(&ObjectAttributes
,
560 OBJ_CASE_INSENSITIVE
,
564 Status
= NtOpenFile(&FileHandle
,
565 FILE_READ_ATTRIBUTES
| SYNCHRONIZE
,
568 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
569 FILE_SYNCHRONOUS_IO_NONALERT
);
571 RtlFreeHeap(RtlGetProcessHeap(), 0, PathName
.Buffer
);
572 if (!NT_SUCCESS(Status
))
573 return DRIVE_NO_ROOT_DIR
; /* According to WINE regression tests */
575 Status
= NtQueryVolumeInformationFile(FileHandle
,
578 sizeof(FILE_FS_DEVICE_INFORMATION
),
579 FileFsDeviceInformation
);
581 if (!NT_SUCCESS(Status
))
586 switch (FileFsDevice
.DeviceType
)
588 case FILE_DEVICE_CD_ROM
:
589 case FILE_DEVICE_CD_ROM_FILE_SYSTEM
:
591 case FILE_DEVICE_VIRTUAL_DISK
:
592 return DRIVE_RAMDISK
;
593 case FILE_DEVICE_NETWORK_FILE_SYSTEM
:
595 case FILE_DEVICE_DISK
:
596 case FILE_DEVICE_DISK_FILE_SYSTEM
:
597 if (FileFsDevice
.Characteristics
& FILE_REMOTE_DEVICE
)
599 if (FileFsDevice
.Characteristics
& FILE_REMOVABLE_MEDIA
)
600 return DRIVE_REMOVABLE
;
604 ERR("Returning DRIVE_UNKNOWN for device type %lu\n", FileFsDevice
.DeviceType
);
606 return DRIVE_UNKNOWN
;