diff --git a/dlls/kernel/volume.c b/dlls/kernel/volume.c index 2a4eab3d4b4..63dc5ed94c5 100644 --- a/dlls/kernel/volume.c +++ b/dlls/kernel/volume.c @@ -148,6 +148,40 @@ static char *get_dos_device_path( LPCWSTR name ) } +/* open a handle to a device root */ +static BOOL open_device_root( LPCWSTR root, HANDLE *handle ) +{ + static const WCHAR default_rootW[] = {'\\',0}; + UNICODE_STRING nt_name; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + + if (!root) root = default_rootW; + if (!RtlDosPathNameToNtPathName_U( root, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nt_name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = NtOpenFile( handle, 0, &attr, &io, 0, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ); + RtlFreeUnicodeString( &nt_name ); + if (status != STATUS_SUCCESS) + { + SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } + return TRUE; +} + + /* create symlinks for the DOS drives; helper for VOLUME_CreateDevices */ static int create_drives(void) { @@ -1206,3 +1240,134 @@ DWORD WINAPI QueryDosDeviceA( LPCSTR devname, LPSTR target, DWORD bufsize ) HeapFree(GetProcessHeap(), 0, targetW); return ret; } + + +/*********************************************************************** + * GetDiskFreeSpaceExW (KERNEL32.@) + * + * This function is used to acquire the size of the available and + * total space on a logical volume. + * + * RETURNS + * + * Zero on failure, nonzero upon success. Use GetLastError to obtain + * detailed error information. + * + */ +BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail, + PULARGE_INTEGER total, PULARGE_INTEGER totalfree ) +{ + FILE_FS_SIZE_INFORMATION info; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE handle; + UINT units; + + TRACE( "%s,%p,%p,%p\n", debugstr_w(root), avail, total, totalfree ); + + if (!open_device_root( root, &handle )) return FALSE; + + status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation ); + NtClose( handle ); + if (status != STATUS_SUCCESS) + { + SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } + + units = info.SectorsPerAllocationUnit * info.BytesPerSector; + if (total) total->QuadPart = info.TotalAllocationUnits.QuadPart * units; + if (totalfree) totalfree->QuadPart = info.AvailableAllocationUnits.QuadPart * units; + /* FIXME: this one should take quotas into account */ + if (avail) avail->QuadPart = info.AvailableAllocationUnits.QuadPart * units; + return TRUE; +} + + +/*********************************************************************** + * GetDiskFreeSpaceExA (KERNEL32.@) + */ +BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail, + PULARGE_INTEGER total, PULARGE_INTEGER totalfree ) +{ + UNICODE_STRING rootW; + BOOL ret; + + if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root); + else rootW.Buffer = NULL; + + ret = GetDiskFreeSpaceExW( rootW.Buffer, avail, total, totalfree); + + RtlFreeUnicodeString(&rootW); + return ret; +} + + +/*********************************************************************** + * GetDiskFreeSpaceW (KERNEL32.@) + */ +BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors, + LPDWORD sector_bytes, LPDWORD free_clusters, + LPDWORD total_clusters ) +{ + FILE_FS_SIZE_INFORMATION info; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE handle; + UINT units; + + TRACE( "%s,%p,%p,%p,%p\n", debugstr_w(root), + cluster_sectors, sector_bytes, free_clusters, total_clusters ); + + if (!open_device_root( root, &handle )) return FALSE; + + status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation ); + NtClose( handle ); + if (status != STATUS_SUCCESS) + { + SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } + + units = info.SectorsPerAllocationUnit * info.BytesPerSector; + + /* cap the size and available at 2GB as per specs */ + if (info.AvailableAllocationUnits.QuadPart * units > 0x7fffffff) + info.AvailableAllocationUnits.QuadPart = 0x7fffffff / units; + if (info.TotalAllocationUnits.QuadPart * units > 0x7fffffff) + info.TotalAllocationUnits.QuadPart = 0x7fffffff / units; + + if (cluster_sectors) *cluster_sectors = info.SectorsPerAllocationUnit; + if (sector_bytes) *sector_bytes = info.BytesPerSector; + if (free_clusters) *free_clusters = info.AvailableAllocationUnits.u.LowPart; + if (total_clusters) *total_clusters = info.TotalAllocationUnits.u.LowPart; + return TRUE; +} + + +/*********************************************************************** + * GetDiskFreeSpaceA (KERNEL32.@) + */ +BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors, + LPDWORD sector_bytes, LPDWORD free_clusters, + LPDWORD total_clusters ) +{ + UNICODE_STRING rootW; + BOOL ret = FALSE; + + if (root) + { + if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root)) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + } + else rootW.Buffer = NULL; + + ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes, + free_clusters, total_clusters ); + RtlFreeUnicodeString(&rootW); + + return ret; +} diff --git a/files/drive.c b/files/drive.c index 7fc1378bc8c..f532dd71cfb 100644 --- a/files/drive.c +++ b/files/drive.c @@ -39,9 +39,6 @@ #ifdef HAVE_UNISTD_H # include #endif -#ifdef HAVE_SYS_STATVFS_H -# include -#endif #define NONAMELESSUNION #define NONAMELESSSTRUCT @@ -643,35 +640,6 @@ int DRIVE_Chdir( int drive, LPCWSTR path ) } -/*********************************************************************** - * DRIVE_GetFreeSpace - */ -static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size, - PULARGE_INTEGER available ) -{ - struct statvfs info; - - if (!DRIVE_IsValid(drive)) - { - SetLastError( ERROR_PATH_NOT_FOUND ); - return 0; - } - - if (statvfs( DOSDrives[drive].root, &info ) < 0) - { - FILE_SetDosError(); - WARN("cannot do statvfs(%s)\n", DOSDrives[drive].root); - return 0; - } - size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_frsize, info.f_blocks ); - if (DOSDrives[drive].type == DRIVE_CDROM) - available->QuadPart = 0; /* ALWAYS 0, even if no real CD-ROM mounted there !! */ - else - available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_frsize, info.f_bavail ); - - return 1; -} - /*********************************************************************** * DRIVE_GetCurrentDirectory * Returns "X:\\path\\etc\\". @@ -728,236 +696,6 @@ WCHAR *DRIVE_BuildEnv(void) } -/*********************************************************************** - * GetDiskFreeSpaceW (KERNEL32.@) - * - * Fails if expression resulting from current drive's dir and "root" - * is not a root dir of the target drive. - * - * UNDOC: setting some LPDWORDs to NULL is perfectly possible - * if the corresponding info is unneeded. - * - * FIXME: needs to support UNC names from Win95 OSR2 on. - * - * Behaviour under Win95a: - * CurrDir root result - * "E:\\TEST" "E:" FALSE - * "E:\\" "E:" TRUE - * "E:\\" "E" FALSE - * "E:\\" "\\" TRUE - * "E:\\TEST" "\\" TRUE - * "E:\\TEST" ":\\" FALSE - * "E:\\TEST" "E:\\" TRUE - * "E:\\TEST" "" FALSE - * "E:\\" "" FALSE (!) - * "E:\\" 0x0 TRUE - * "E:\\TEST" 0x0 TRUE (!) - * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\") - * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST") - */ -BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors, - LPDWORD sector_bytes, LPDWORD free_clusters, - LPDWORD total_clusters ) -{ - int drive, sec_size; - ULARGE_INTEGER size,available; - LPCWSTR path; - DWORD cluster_sec; - - TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root), cluster_sectors, sector_bytes, - free_clusters, total_clusters); - - if (!root || root[0] == '\\' || root[0] == '/') - drive = DRIVE_GetCurrentDrive(); - else - if (root[0] && root[1] == ':') /* root contains drive tag */ - { - drive = toupperW(root[0]) - 'A'; - path = &root[2]; - if (path[0] == '\0') - { - path = DRIVE_GetDosCwd(drive); - if (!path) - { - SetLastError(ERROR_PATH_NOT_FOUND); - return FALSE; - } - } - else - if (path[0] == '\\') - path++; - - if (path[0]) /* oops, we are in a subdir */ - { - SetLastError(ERROR_INVALID_NAME); - return FALSE; - } - } - else - { - if (!root[0]) - SetLastError(ERROR_PATH_NOT_FOUND); - else - SetLastError(ERROR_INVALID_NAME); - return FALSE; - } - - if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE; - - /* Cap the size and available at 2GB as per specs. */ - if ((size.u.HighPart) ||(size.u.LowPart > 0x7fffffff)) - { - size.u.HighPart = 0; - size.u.LowPart = 0x7fffffff; - } - if ((available.u.HighPart) ||(available.u.LowPart > 0x7fffffff)) - { - available.u.HighPart =0; - available.u.LowPart = 0x7fffffff; - } - sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512; - size.u.LowPart /= sec_size; - available.u.LowPart /= sec_size; - /* FIXME: probably have to adjust those variables too for CDFS */ - cluster_sec = 1; - while (cluster_sec * 65536 < size.u.LowPart) cluster_sec *= 2; - - if (cluster_sectors) - *cluster_sectors = cluster_sec; - if (sector_bytes) - *sector_bytes = sec_size; - if (free_clusters) - *free_clusters = available.u.LowPart / cluster_sec; - if (total_clusters) - *total_clusters = size.u.LowPart / cluster_sec; - return TRUE; -} - - -/*********************************************************************** - * GetDiskFreeSpaceA (KERNEL32.@) - */ -BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors, - LPDWORD sector_bytes, LPDWORD free_clusters, - LPDWORD total_clusters ) -{ - UNICODE_STRING rootW; - BOOL ret = FALSE; - - if (root) - { - if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root)) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return FALSE; - } - } - else - rootW.Buffer = NULL; - - ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes, - free_clusters, total_clusters ); - RtlFreeUnicodeString(&rootW); - - return ret; -} - - -/*********************************************************************** - * GetDiskFreeSpaceExW (KERNEL32.@) - * - * This function is used to acquire the size of the available and - * total space on a logical volume. - * - * RETURNS - * - * Zero on failure, nonzero upon success. Use GetLastError to obtain - * detailed error information. - * - */ -BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, - PULARGE_INTEGER avail, - PULARGE_INTEGER total, - PULARGE_INTEGER totalfree) -{ - int drive; - ULARGE_INTEGER size,available; - - if (!root) drive = DRIVE_GetCurrentDrive(); - else - { /* C: always works for GetDiskFreeSpaceEx */ - if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\'))) - { - FIXME("there are valid root names which are not supported yet\n"); - /* ..like UNC names, for instance. */ - - WARN("invalid root '%s'\n", debugstr_w(root)); - return FALSE; - } - drive = toupperW(root[0]) - 'A'; - } - - if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE; - - if (total) - { - total->u.HighPart = size.u.HighPart; - total->u.LowPart = size.u.LowPart; - } - - if (totalfree) - { - totalfree->u.HighPart = available.u.HighPart; - totalfree->u.LowPart = available.u.LowPart; - } - - if (avail) - { - if (FIXME_ON(dosfs)) - { - /* On Windows2000, we need to check the disk quota - allocated for the user owning the calling process. We - don't want to be more obtrusive than necessary with the - FIXME messages, so don't print the FIXME unless Wine is - actually masquerading as Windows2000. */ - - RTL_OSVERSIONINFOEXW ovi; - ovi.dwOSVersionInfoSize = sizeof(ovi); - if (RtlGetVersion(&ovi)) - { - if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4) - FIXME("no per-user quota support yet\n"); - } - } - - /* Quick hack, should eventually be fixed to work 100% with - Windows2000 (see comment above). */ - avail->u.HighPart = available.u.HighPart; - avail->u.LowPart = available.u.LowPart; - } - - return TRUE; -} - -/*********************************************************************** - * GetDiskFreeSpaceExA (KERNEL32.@) - */ -BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail, - PULARGE_INTEGER total, - PULARGE_INTEGER totalfree) -{ - UNICODE_STRING rootW; - BOOL ret; - - if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root); - else rootW.Buffer = NULL; - - ret = GetDiskFreeSpaceExW( rootW.Buffer, avail, total, totalfree); - - RtlFreeUnicodeString(&rootW); - return ret; -} - /*********************************************************************** * GetDriveTypeW (KERNEL32.@) *