/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS system libraries * FILE: dll/win32/kernel32/client/file/disk.c * PURPOSE: Disk and Drive functions * PROGRAMMER: Ariadne ( ariadne@xs4all.nl) * Erik Bos, Alexandre Julliard : * GetLogicalDriveStringsA, * GetLogicalDriveStringsW, GetLogicalDrives * UPDATE HISTORY: * Created 01/11/98 */ //WINE copyright notice: /* * DOS drives handling functions * * Copyright 1993 Erik Bos * Copyright 1996 Alexandre Julliard */ #include #include #define NDEBUG #include DEBUG_CHANNEL(kernel32file); #define MAX_DOS_DRIVES 26 HANDLE WINAPI InternalOpenDirW(IN LPCWSTR DirName, IN BOOLEAN Write) { UNICODE_STRING NtPathU; OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS errCode; IO_STATUS_BLOCK IoStatusBlock; HANDLE hFile; if (!RtlDosPathNameToNtPathName_U(DirName, &NtPathU, NULL, NULL)) { WARN("Invalid path\n"); SetLastError(ERROR_BAD_PATHNAME); return INVALID_HANDLE_VALUE; } InitializeObjectAttributes(&ObjectAttributes, &NtPathU, OBJ_CASE_INSENSITIVE, NULL, NULL); errCode = NtCreateFile(&hFile, Write ? FILE_GENERIC_WRITE : FILE_GENERIC_READ, &ObjectAttributes, &IoStatusBlock, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0); RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathU.Buffer); if (!NT_SUCCESS(errCode)) { BaseSetLastNTError(errCode); return INVALID_HANDLE_VALUE; } return hFile; } /* * @implemented */ /* Synced to Wine-2008/12/28 */ DWORD WINAPI GetLogicalDriveStringsA(IN DWORD nBufferLength, IN LPSTR lpBuffer) { DWORD drive, count; DWORD dwDriveMap; LPSTR p; dwDriveMap = GetLogicalDrives(); for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++) { if (dwDriveMap & (1< nBufferLength) return ((count * 4) + 1); p = lpBuffer; for (drive = 0; drive < MAX_DOS_DRIVES; drive++) if (dwDriveMap & (1< nBufferLength) return ((count * 4) + 1); p = lpBuffer; for (drive = 0; drive < MAX_DOS_DRIVES; drive++) if (dwDriveMap & (1<Buffer, lpSectorsPerCluster, lpBytesPerSector, lpNumberOfFreeClusters, lpTotalNumberOfClusters); } /* * @implemented */ BOOL WINAPI GetDiskFreeSpaceW(IN LPCWSTR lpRootPathName, OUT LPDWORD lpSectorsPerCluster, OUT LPDWORD lpBytesPerSector, OUT LPDWORD lpNumberOfFreeClusters, OUT LPDWORD lpTotalNumberOfClusters) { BOOL Below2GB; PCWSTR RootPath; NTSTATUS Status; HANDLE RootHandle; UNICODE_STRING FileName; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; FILE_FS_SIZE_INFORMATION FileFsSize; /* If no path provided, get root path */ RootPath = lpRootPathName; if (lpRootPathName == NULL) { RootPath = L"\\"; } /* Convert the path to NT path */ if (!RtlDosPathNameToNtPathName_U(RootPath, &FileName, NULL, NULL)) { SetLastError(ERROR_PATH_NOT_FOUND); return FALSE; } /* Open it for disk space query! */ InitializeObjectAttributes(&ObjectAttributes, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenFile(&RootHandle, SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_FREE_SPACE_QUERY); if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer); if (lpBytesPerSector != NULL) { *lpBytesPerSector = 0; } return FALSE; } /* We don't need the name any longer */ RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer); /* Query disk space! */ Status = NtQueryVolumeInformationFile(RootHandle, &IoStatusBlock, &FileFsSize, sizeof(FILE_FS_SIZE_INFORMATION), FileFsSizeInformation); NtClose(RootHandle); if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); return FALSE; } /* Are we in some compatibility mode where size must be below 2GB? */ Below2GB = ((NtCurrentPeb()->AppCompatFlags.LowPart & GetDiskFreeSpace2GB) == GetDiskFreeSpace2GB); /* If we're to overflow output, make sure we return the maximum */ if (FileFsSize.TotalAllocationUnits.HighPart != 0) { FileFsSize.TotalAllocationUnits.LowPart = -1; } if (FileFsSize.AvailableAllocationUnits.HighPart != 0) { FileFsSize.AvailableAllocationUnits.LowPart = -1; } /* Return what user asked for */ if (lpSectorsPerCluster != NULL) { *lpSectorsPerCluster = FileFsSize.SectorsPerAllocationUnit; } if (lpBytesPerSector != NULL) { *lpBytesPerSector = FileFsSize.BytesPerSector; } if (lpNumberOfFreeClusters != NULL) { if (!Below2GB) { *lpNumberOfFreeClusters = FileFsSize.AvailableAllocationUnits.LowPart; } /* If we have to remain below 2GB... */ else { DWORD FreeClusters; /* Compute how many clusters there are in less than 2GB: 2 * 1024 * 1024 * 1024- 1 */ FreeClusters = 0x7FFFFFFF / (FileFsSize.SectorsPerAllocationUnit * FileFsSize.BytesPerSector); /* If that's higher than what was queried, then return the queried value, it's OK! */ if (FreeClusters > FileFsSize.AvailableAllocationUnits.LowPart) { FreeClusters = FileFsSize.AvailableAllocationUnits.LowPart; } *lpNumberOfFreeClusters = FreeClusters; } } if (lpTotalNumberOfClusters != NULL) { if (!Below2GB) { *lpTotalNumberOfClusters = FileFsSize.TotalAllocationUnits.LowPart; } /* If we have to remain below 2GB... */ else { DWORD TotalClusters; /* Compute how many clusters there are in less than 2GB: 2 * 1024 * 1024 * 1024- 1 */ TotalClusters = 0x7FFFFFFF / (FileFsSize.SectorsPerAllocationUnit * FileFsSize.BytesPerSector); /* If that's higher than what was queried, then return the queried value, it's OK! */ if (TotalClusters > FileFsSize.TotalAllocationUnits.LowPart) { TotalClusters = FileFsSize.TotalAllocationUnits.LowPart; } *lpTotalNumberOfClusters = TotalClusters; } } return TRUE; } /* * @implemented */ BOOL WINAPI GetDiskFreeSpaceExA(IN LPCSTR lpDirectoryName OPTIONAL, OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller, OUT PULARGE_INTEGER lpTotalNumberOfBytes, OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes) { PWCHAR DirectoryNameW=NULL; if (lpDirectoryName) { if (!(DirectoryNameW = FilenameA2W(lpDirectoryName, FALSE))) return FALSE; } return GetDiskFreeSpaceExW (DirectoryNameW , lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes); } /* * @implemented */ BOOL WINAPI GetDiskFreeSpaceExW(IN LPCWSTR lpDirectoryName OPTIONAL, OUT PULARGE_INTEGER lpFreeBytesAvailableToCaller, OUT PULARGE_INTEGER lpTotalNumberOfBytes, OUT PULARGE_INTEGER lpTotalNumberOfFreeBytes) { union { FILE_FS_SIZE_INFORMATION FsSize; FILE_FS_FULL_SIZE_INFORMATION FsFullSize; } FsInfo; IO_STATUS_BLOCK IoStatusBlock; ULARGE_INTEGER BytesPerCluster; HANDLE hFile; NTSTATUS Status; if (lpDirectoryName == NULL) lpDirectoryName = L"\\"; hFile = InternalOpenDirW(lpDirectoryName, FALSE); if (INVALID_HANDLE_VALUE == hFile) { return FALSE; } if (lpFreeBytesAvailableToCaller != NULL || lpTotalNumberOfBytes != NULL) { /* To get the free space available to the user associated with the current thread, try FileFsFullSizeInformation. If this is not supported by the file system, fall back to FileFsSize */ Status = NtQueryVolumeInformationFile(hFile, &IoStatusBlock, &FsInfo.FsFullSize, sizeof(FsInfo.FsFullSize), FileFsFullSizeInformation); if (NT_SUCCESS(Status)) { /* Close the handle before returning data to avoid a handle leak in case of a fault! */ CloseHandle(hFile); BytesPerCluster.QuadPart = FsInfo.FsFullSize.BytesPerSector * FsInfo.FsFullSize.SectorsPerAllocationUnit; if (lpFreeBytesAvailableToCaller != NULL) { lpFreeBytesAvailableToCaller->QuadPart = BytesPerCluster.QuadPart * FsInfo.FsFullSize.CallerAvailableAllocationUnits.QuadPart; } if (lpTotalNumberOfBytes != NULL) { lpTotalNumberOfBytes->QuadPart = BytesPerCluster.QuadPart * FsInfo.FsFullSize.TotalAllocationUnits.QuadPart; } if (lpTotalNumberOfFreeBytes != NULL) { lpTotalNumberOfFreeBytes->QuadPart = BytesPerCluster.QuadPart * FsInfo.FsFullSize.ActualAvailableAllocationUnits.QuadPart; } return TRUE; } } Status = NtQueryVolumeInformationFile(hFile, &IoStatusBlock, &FsInfo.FsSize, sizeof(FsInfo.FsSize), FileFsSizeInformation); /* Close the handle before returning data to avoid a handle leak in case of a fault! */ CloseHandle(hFile); if (!NT_SUCCESS(Status)) { BaseSetLastNTError (Status); return FALSE; } BytesPerCluster.QuadPart = FsInfo.FsSize.BytesPerSector * FsInfo.FsSize.SectorsPerAllocationUnit; if (lpFreeBytesAvailableToCaller) { lpFreeBytesAvailableToCaller->QuadPart = BytesPerCluster.QuadPart * FsInfo.FsSize.AvailableAllocationUnits.QuadPart; } if (lpTotalNumberOfBytes) { lpTotalNumberOfBytes->QuadPart = BytesPerCluster.QuadPart * FsInfo.FsSize.TotalAllocationUnits.QuadPart; } if (lpTotalNumberOfFreeBytes) { lpTotalNumberOfFreeBytes->QuadPart = BytesPerCluster.QuadPart * FsInfo.FsSize.AvailableAllocationUnits.QuadPart; } return TRUE; } /* * @implemented */ UINT WINAPI GetDriveTypeA(IN LPCSTR lpRootPathName) { PWCHAR RootPathNameW; if (!lpRootPathName) return GetDriveTypeW(NULL); if (!(RootPathNameW = FilenameA2W(lpRootPathName, FALSE))) return DRIVE_UNKNOWN; return GetDriveTypeW(RootPathNameW); } /* * @implemented */ UINT WINAPI GetDriveTypeW(IN LPCWSTR lpRootPathName) { FILE_FS_DEVICE_INFORMATION FileFsDevice; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING PathName; HANDLE FileHandle; NTSTATUS Status; PWSTR CurrentDir = NULL; PCWSTR lpRootPath; if (!lpRootPathName) { /* If NULL is passed, use current directory path */ DWORD BufferSize = GetCurrentDirectoryW(0, NULL); CurrentDir = HeapAlloc(GetProcessHeap(), 0, BufferSize * sizeof(WCHAR)); if (!CurrentDir) return DRIVE_UNKNOWN; if (!GetCurrentDirectoryW(BufferSize, CurrentDir)) { HeapFree(GetProcessHeap(), 0, CurrentDir); return DRIVE_UNKNOWN; } if (wcslen(CurrentDir) > 3) CurrentDir[3] = 0; lpRootPath = CurrentDir; } else { size_t Length = wcslen(lpRootPathName); TRACE("lpRootPathName: %S\n", lpRootPathName); lpRootPath = lpRootPathName; if (Length == 2) { WCHAR DriveLetter = RtlUpcaseUnicodeChar(lpRootPathName[0]); if (DriveLetter >= L'A' && DriveLetter <= L'Z' && lpRootPathName[1] == L':') { Length = (Length + 2) * sizeof(WCHAR); CurrentDir = HeapAlloc(GetProcessHeap(), 0, Length); if (!CurrentDir) return DRIVE_UNKNOWN; StringCbPrintfW(CurrentDir, Length, L"%s\\", lpRootPathName); lpRootPath = CurrentDir; } } } TRACE("lpRootPath: %S\n", lpRootPath); if (!RtlDosPathNameToNtPathName_U(lpRootPath, &PathName, NULL, NULL)) { if (CurrentDir != NULL) HeapFree(GetProcessHeap(), 0, CurrentDir); return DRIVE_NO_ROOT_DIR; } TRACE("PathName: %S\n", PathName.Buffer); if (CurrentDir != NULL) HeapFree(GetProcessHeap(), 0, CurrentDir); if (PathName.Buffer[(PathName.Length >> 1) - 1] != L'\\') { return DRIVE_NO_ROOT_DIR; } InitializeObjectAttributes(&ObjectAttributes, &PathName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenFile(&FileHandle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT); RtlFreeHeap(RtlGetProcessHeap(), 0, PathName.Buffer); if (!NT_SUCCESS(Status)) return DRIVE_NO_ROOT_DIR; /* According to WINE regression tests */ Status = NtQueryVolumeInformationFile(FileHandle, &IoStatusBlock, &FileFsDevice, sizeof(FILE_FS_DEVICE_INFORMATION), FileFsDeviceInformation); NtClose(FileHandle); if (!NT_SUCCESS(Status)) { return 0; } switch (FileFsDevice.DeviceType) { case FILE_DEVICE_CD_ROM: case FILE_DEVICE_CD_ROM_FILE_SYSTEM: return DRIVE_CDROM; case FILE_DEVICE_VIRTUAL_DISK: return DRIVE_RAMDISK; case FILE_DEVICE_NETWORK_FILE_SYSTEM: return DRIVE_REMOTE; case FILE_DEVICE_DISK: case FILE_DEVICE_DISK_FILE_SYSTEM: if (FileFsDevice.Characteristics & FILE_REMOTE_DEVICE) return DRIVE_REMOTE; if (FileFsDevice.Characteristics & FILE_REMOVABLE_MEDIA) return DRIVE_REMOVABLE; return DRIVE_FIXED; } ERR("Returning DRIVE_UNKNOWN for device type %lu\n", FileFsDevice.DeviceType); return DRIVE_UNKNOWN; } /* EOF */