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
32 InternalOpenDirW(IN LPCWSTR DirName
,
35 UNICODE_STRING NtPathU
;
36 OBJECT_ATTRIBUTES ObjectAttributes
;
38 IO_STATUS_BLOCK IoStatusBlock
;
41 if (!RtlDosPathNameToNtPathName_U(DirName
, &NtPathU
, NULL
, NULL
))
43 WARN("Invalid path\n");
44 SetLastError(ERROR_BAD_PATHNAME
);
45 return INVALID_HANDLE_VALUE
;
48 InitializeObjectAttributes(&ObjectAttributes
,
54 errCode
= NtCreateFile(&hFile
,
55 Write
? FILE_GENERIC_WRITE
: FILE_GENERIC_READ
,
60 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
66 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathU
.Buffer
);
68 if (!NT_SUCCESS(errCode
))
70 BaseSetLastNTError(errCode
);
71 return INVALID_HANDLE_VALUE
;
80 /* Synced to Wine-2008/12/28 */
83 GetLogicalDriveStringsA(IN DWORD nBufferLength
,
90 dwDriveMap
= GetLogicalDrives();
92 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
94 if (dwDriveMap
& (1<<drive
))
99 if ((count
* 4) + 1 > nBufferLength
) return ((count
* 4) + 1);
103 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
104 if (dwDriveMap
& (1<<drive
))
106 *p
++ = 'A' + (UCHAR
)drive
;
119 /* Synced to Wine-2008/12/28 */
122 GetLogicalDriveStringsW(IN DWORD nBufferLength
,
129 dwDriveMap
= GetLogicalDrives();
131 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
133 if (dwDriveMap
& (1<<drive
))
137 if ((count
* 4) + 1 > nBufferLength
) return ((count
* 4) + 1);
140 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
141 if (dwDriveMap
& (1<<drive
))
143 *p
++ = (WCHAR
)('A' + drive
);
156 /* Synced to Wine-? */
159 GetLogicalDrives(VOID
)
162 PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo
;
164 /* Get the Device Map for this Process */
165 Status
= NtQueryInformationProcess(NtCurrentProcess(),
167 &ProcessDeviceMapInfo
,
168 sizeof(ProcessDeviceMapInfo
),
171 /* Return the Drive Map */
172 if (!NT_SUCCESS(Status
))
174 BaseSetLastNTError(Status
);
178 return ProcessDeviceMapInfo
.Query
.DriveMap
;
186 GetDiskFreeSpaceA(IN LPCSTR lpRootPathName
,
187 OUT LPDWORD lpSectorsPerCluster
,
188 OUT LPDWORD lpBytesPerSector
,
189 OUT LPDWORD lpNumberOfFreeClusters
,
190 OUT LPDWORD lpTotalNumberOfClusters
)
193 PUNICODE_STRING RootPathU
;
195 RootPath
= lpRootPathName
;
196 if (RootPath
== NULL
)
201 RootPathU
= Basep8BitStringToStaticUnicodeString(RootPath
);
202 if (RootPathU
== NULL
)
207 return GetDiskFreeSpaceW(RootPathU
->Buffer
, lpSectorsPerCluster
,
208 lpBytesPerSector
, lpNumberOfFreeClusters
,
209 lpTotalNumberOfClusters
);
217 GetDiskFreeSpaceW(IN LPCWSTR lpRootPathName
,
218 OUT LPDWORD lpSectorsPerCluster
,
219 OUT LPDWORD lpBytesPerSector
,
220 OUT LPDWORD lpNumberOfFreeClusters
,
221 OUT LPDWORD lpTotalNumberOfClusters
)
227 UNICODE_STRING FileName
;
228 IO_STATUS_BLOCK IoStatusBlock
;
229 OBJECT_ATTRIBUTES ObjectAttributes
;
230 FILE_FS_SIZE_INFORMATION FileFsSize
;
232 /* If no path provided, get root path */
233 RootPath
= lpRootPathName
;
234 if (lpRootPathName
== NULL
)
239 /* Convert the path to NT path */
240 if (!RtlDosPathNameToNtPathName_U(RootPath
, &FileName
, NULL
, NULL
))
242 SetLastError(ERROR_PATH_NOT_FOUND
);
246 /* Open it for disk space query! */
247 InitializeObjectAttributes(&ObjectAttributes
, &FileName
,
248 OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
249 Status
= NtOpenFile(&RootHandle
, SYNCHRONIZE
, &ObjectAttributes
, &IoStatusBlock
,
250 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
251 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_FOR_FREE_SPACE_QUERY
);
252 if (!NT_SUCCESS(Status
))
254 BaseSetLastNTError(Status
);
255 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName
.Buffer
);
256 if (lpBytesPerSector
!= NULL
)
258 *lpBytesPerSector
= 0;
264 /* We don't need the name any longer */
265 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName
.Buffer
);
267 /* Query disk space! */
268 Status
= NtQueryVolumeInformationFile(RootHandle
, &IoStatusBlock
, &FileFsSize
,
269 sizeof(FILE_FS_SIZE_INFORMATION
),
270 FileFsSizeInformation
);
272 if (!NT_SUCCESS(Status
))
274 BaseSetLastNTError(Status
);
278 /* Are we in some compatibility mode where size must be below 2GB? */
279 Below2GB
= ((NtCurrentPeb()->AppCompatFlags
.LowPart
& GetDiskFreeSpace2GB
) == GetDiskFreeSpace2GB
);
281 /* If we're to overflow output, make sure we return the maximum */
282 if (FileFsSize
.TotalAllocationUnits
.HighPart
!= 0)
284 FileFsSize
.TotalAllocationUnits
.LowPart
= -1;
287 if (FileFsSize
.AvailableAllocationUnits
.HighPart
!= 0)
289 FileFsSize
.AvailableAllocationUnits
.LowPart
= -1;
292 /* Return what user asked for */
293 if (lpSectorsPerCluster
!= NULL
)
295 *lpSectorsPerCluster
= FileFsSize
.SectorsPerAllocationUnit
;
298 if (lpBytesPerSector
!= NULL
)
300 *lpBytesPerSector
= FileFsSize
.BytesPerSector
;
303 if (lpNumberOfFreeClusters
!= NULL
)
307 *lpNumberOfFreeClusters
= FileFsSize
.AvailableAllocationUnits
.LowPart
;
309 /* If we have to remain below 2GB... */
314 /* Compute how many clusters there are in less than 2GB: 2 * 1024 * 1024 * 1024- 1 */
315 FreeClusters
= 0x7FFFFFFF / (FileFsSize
.SectorsPerAllocationUnit
* FileFsSize
.BytesPerSector
);
316 /* If that's higher than what was queried, then return the queried value, it's OK! */
317 if (FreeClusters
> FileFsSize
.AvailableAllocationUnits
.LowPart
)
319 FreeClusters
= FileFsSize
.AvailableAllocationUnits
.LowPart
;
322 *lpNumberOfFreeClusters
= FreeClusters
;
326 if (lpTotalNumberOfClusters
!= NULL
)
330 *lpTotalNumberOfClusters
= FileFsSize
.TotalAllocationUnits
.LowPart
;
332 /* If we have to remain below 2GB... */
337 /* Compute how many clusters there are in less than 2GB: 2 * 1024 * 1024 * 1024- 1 */
338 TotalClusters
= 0x7FFFFFFF / (FileFsSize
.SectorsPerAllocationUnit
* FileFsSize
.BytesPerSector
);
339 /* If that's higher than what was queried, then return the queried value, it's OK! */
340 if (TotalClusters
> FileFsSize
.TotalAllocationUnits
.LowPart
)
342 TotalClusters
= FileFsSize
.TotalAllocationUnits
.LowPart
;
345 *lpTotalNumberOfClusters
= TotalClusters
;
357 GetDiskFreeSpaceExA(IN LPCSTR lpDirectoryName OPTIONAL
,
358 OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller
,
359 OUT PULARGE_INTEGER lpTotalNumberOfBytes
,
360 OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes
)
363 PUNICODE_STRING RootPathU
;
365 RootPath
= lpDirectoryName
;
366 if (RootPath
== NULL
)
371 RootPathU
= Basep8BitStringToStaticUnicodeString(RootPath
);
372 if (RootPathU
== NULL
)
377 return GetDiskFreeSpaceExW(RootPathU
->Buffer
, lpFreeBytesAvailableToCaller
,
378 lpTotalNumberOfBytes
, lpTotalNumberOfFreeBytes
);
386 GetDiskFreeSpaceExW(IN LPCWSTR lpDirectoryName OPTIONAL
,
387 OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller
,
388 OUT PULARGE_INTEGER lpTotalNumberOfBytes
,
389 OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes
)
394 UNICODE_STRING FileName
;
395 DWORD BytesPerAllocationUnit
;
396 IO_STATUS_BLOCK IoStatusBlock
;
397 OBJECT_ATTRIBUTES ObjectAttributes
;
398 FILE_FS_SIZE_INFORMATION FileFsSize
;
400 /* If no path provided, get root path */
401 RootPath
= lpDirectoryName
;
402 if (lpDirectoryName
== NULL
)
407 /* Convert the path to NT path */
408 if (!RtlDosPathNameToNtPathName_U(RootPath
, &FileName
, NULL
, NULL
))
410 SetLastError(ERROR_PATH_NOT_FOUND
);
414 /* Open it for disk space query! */
415 InitializeObjectAttributes(&ObjectAttributes
, &FileName
,
416 OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
417 Status
= NtOpenFile(&RootHandle
, SYNCHRONIZE
, &ObjectAttributes
, &IoStatusBlock
,
418 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
419 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_FOR_FREE_SPACE_QUERY
);
420 if (!NT_SUCCESS(Status
))
422 BaseSetLastNTError(Status
);
423 /* If error conversion lead to file not found, override to use path not found
424 * which is more accurate
426 if (GetLastError() == ERROR_FILE_NOT_FOUND
)
428 SetLastError(ERROR_PATH_NOT_FOUND
);
431 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName
.Buffer
);
436 RtlFreeHeap(RtlGetProcessHeap(), 0, FileName
.Buffer
);
438 /* If user asks for lpTotalNumberOfFreeBytes, try to use full size information */
439 if (lpTotalNumberOfFreeBytes
!= NULL
)
441 FILE_FS_FULL_SIZE_INFORMATION FileFsFullSize
;
443 /* Issue the full fs size request */
444 Status
= NtQueryVolumeInformationFile(RootHandle
, &IoStatusBlock
, &FileFsFullSize
,
445 sizeof(FILE_FS_FULL_SIZE_INFORMATION
),
446 FileFsFullSizeInformation
);
447 /* If it succeed, complete out buffers */
448 if (NT_SUCCESS(Status
))
450 /* We can close here, we'll return */
453 /* Compute the size of an AU */
454 BytesPerAllocationUnit
= FileFsFullSize
.SectorsPerAllocationUnit
* FileFsFullSize
.BytesPerSector
;
456 /* And then return what was asked */
457 if (lpFreeBytesAvailableToCaller
!= NULL
)
459 lpFreeBytesAvailableToCaller
->QuadPart
= FileFsFullSize
.CallerAvailableAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
462 if (lpTotalNumberOfBytes
!= NULL
)
464 lpTotalNumberOfBytes
->QuadPart
= FileFsFullSize
.TotalAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
467 /* No need to check for nullness ;-) */
468 lpTotalNumberOfFreeBytes
->QuadPart
= FileFsFullSize
.ActualAvailableAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
474 /* Otherwise, fallback to normal size information */
475 Status
= NtQueryVolumeInformationFile(RootHandle
, &IoStatusBlock
,
476 &FileFsSize
, sizeof(FILE_FS_SIZE_INFORMATION
),
477 FileFsSizeInformation
);
479 if (!NT_SUCCESS(Status
))
481 BaseSetLastNTError(Status
);
485 /* Compute the size of an AU */
486 BytesPerAllocationUnit
= FileFsSize
.SectorsPerAllocationUnit
* FileFsSize
.BytesPerSector
;
488 /* And then return what was asked, available is free, the same! */
489 if (lpFreeBytesAvailableToCaller
!= NULL
)
491 lpFreeBytesAvailableToCaller
->QuadPart
= FileFsSize
.AvailableAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
494 if (lpTotalNumberOfBytes
!= NULL
)
496 lpTotalNumberOfBytes
->QuadPart
= FileFsSize
.TotalAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
499 if (lpTotalNumberOfFreeBytes
!= NULL
)
501 lpTotalNumberOfFreeBytes
->QuadPart
= FileFsSize
.AvailableAllocationUnits
.QuadPart
* BytesPerAllocationUnit
;
512 GetDriveTypeA(IN LPCSTR lpRootPathName
)
514 PWCHAR RootPathNameW
;
517 return GetDriveTypeW(NULL
);
519 if (!(RootPathNameW
= FilenameA2W(lpRootPathName
, FALSE
)))
520 return DRIVE_UNKNOWN
;
522 return GetDriveTypeW(RootPathNameW
);
530 GetDriveTypeW(IN LPCWSTR lpRootPathName
)
532 FILE_FS_DEVICE_INFORMATION FileFsDevice
;
533 OBJECT_ATTRIBUTES ObjectAttributes
;
534 IO_STATUS_BLOCK IoStatusBlock
;
535 UNICODE_STRING PathName
;
538 PWSTR CurrentDir
= NULL
;
543 /* If NULL is passed, use current directory path */
544 DWORD BufferSize
= GetCurrentDirectoryW(0, NULL
);
545 CurrentDir
= HeapAlloc(GetProcessHeap(), 0, BufferSize
* sizeof(WCHAR
));
547 return DRIVE_UNKNOWN
;
548 if (!GetCurrentDirectoryW(BufferSize
, CurrentDir
))
550 HeapFree(GetProcessHeap(), 0, CurrentDir
);
551 return DRIVE_UNKNOWN
;
554 if (wcslen(CurrentDir
) > 3)
557 lpRootPath
= CurrentDir
;
561 size_t Length
= wcslen(lpRootPathName
);
563 TRACE("lpRootPathName: %S\n", lpRootPathName
);
565 lpRootPath
= lpRootPathName
;
568 WCHAR DriveLetter
= RtlUpcaseUnicodeChar(lpRootPathName
[0]);
570 if (DriveLetter
>= L
'A' && DriveLetter
<= L
'Z' && lpRootPathName
[1] == L
':')
572 Length
= (Length
+ 2) * sizeof(WCHAR
);
574 CurrentDir
= HeapAlloc(GetProcessHeap(), 0, Length
);
576 return DRIVE_UNKNOWN
;
578 StringCbPrintfW(CurrentDir
, Length
, L
"%s\\", lpRootPathName
);
580 lpRootPath
= CurrentDir
;
585 TRACE("lpRootPath: %S\n", lpRootPath
);
587 if (!RtlDosPathNameToNtPathName_U(lpRootPath
, &PathName
, NULL
, NULL
))
589 if (CurrentDir
!= NULL
)
590 HeapFree(GetProcessHeap(), 0, CurrentDir
);
592 return DRIVE_NO_ROOT_DIR
;
595 TRACE("PathName: %S\n", PathName
.Buffer
);
597 if (CurrentDir
!= NULL
)
598 HeapFree(GetProcessHeap(), 0, CurrentDir
);
600 if (PathName
.Buffer
[(PathName
.Length
>> 1) - 1] != L
'\\')
602 return DRIVE_NO_ROOT_DIR
;
605 InitializeObjectAttributes(&ObjectAttributes
,
607 OBJ_CASE_INSENSITIVE
,
611 Status
= NtOpenFile(&FileHandle
,
612 FILE_READ_ATTRIBUTES
| SYNCHRONIZE
,
615 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
616 FILE_SYNCHRONOUS_IO_NONALERT
);
618 RtlFreeHeap(RtlGetProcessHeap(), 0, PathName
.Buffer
);
619 if (!NT_SUCCESS(Status
))
620 return DRIVE_NO_ROOT_DIR
; /* According to WINE regression tests */
622 Status
= NtQueryVolumeInformationFile(FileHandle
,
625 sizeof(FILE_FS_DEVICE_INFORMATION
),
626 FileFsDeviceInformation
);
628 if (!NT_SUCCESS(Status
))
633 switch (FileFsDevice
.DeviceType
)
635 case FILE_DEVICE_CD_ROM
:
636 case FILE_DEVICE_CD_ROM_FILE_SYSTEM
:
638 case FILE_DEVICE_VIRTUAL_DISK
:
639 return DRIVE_RAMDISK
;
640 case FILE_DEVICE_NETWORK_FILE_SYSTEM
:
642 case FILE_DEVICE_DISK
:
643 case FILE_DEVICE_DISK_FILE_SYSTEM
:
644 if (FileFsDevice
.Characteristics
& FILE_REMOTE_DEVICE
)
646 if (FileFsDevice
.Characteristics
& FILE_REMOVABLE_MEDIA
)
647 return DRIVE_REMOVABLE
;
651 ERR("Returning DRIVE_UNKNOWN for device type %lu\n", FileFsDevice
.DeviceType
);
653 return DRIVE_UNKNOWN
;