win: refactor to support large statfs blocks (#5016)
Accomplish this by replacing `GetDiskFreeSpaceW()` with `NtQueryVolumeInformationFile()` which allows us to represent blocks larger than 2^32 - 1 via `FILE_FS_FULL_SIZE_INFORMATION.TotalAllocationUnits`. Expanded `fs_statfs` test to check that `uv_fs_statfs()` also works with files, meaning https://github.com/libuv/libuv/issues/2683 remains fixed without the need of https://github.com/libuv/libuv/pull/2695.
This commit is contained in:
parent
91dc2389cc
commit
c68ca444e7
87
src/win/fs.c
87
src/win/fs.c
@ -3109,67 +3109,35 @@ static void fs__lchown(uv_fs_t* req) {
|
||||
|
||||
|
||||
static void fs__statfs(uv_fs_t* req) {
|
||||
FILE_FS_FULL_SIZE_INFORMATION info;
|
||||
IO_STATUS_BLOCK io_status;
|
||||
uv_statfs_t* stat_fs;
|
||||
DWORD sectors_per_cluster;
|
||||
DWORD bytes_per_sector;
|
||||
DWORD free_clusters;
|
||||
DWORD total_clusters;
|
||||
WCHAR* pathw;
|
||||
NTSTATUS nt_status;
|
||||
HANDLE handle;
|
||||
|
||||
pathw = req->file.pathw;
|
||||
retry_get_disk_free_space:
|
||||
if (0 == GetDiskFreeSpaceW(pathw,
|
||||
§ors_per_cluster,
|
||||
&bytes_per_sector,
|
||||
&free_clusters,
|
||||
&total_clusters)) {
|
||||
DWORD err;
|
||||
WCHAR* fpart;
|
||||
size_t len;
|
||||
DWORD ret;
|
||||
BOOL is_second;
|
||||
handle = CreateFileW(req->file.pathw,
|
||||
FILE_READ_ATTRIBUTES,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
|
||||
err = GetLastError();
|
||||
is_second = pathw != req->file.pathw;
|
||||
if (err != ERROR_DIRECTORY || is_second) {
|
||||
if (is_second)
|
||||
uv__free(pathw);
|
||||
|
||||
SET_REQ_WIN32_ERROR(req, err);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
SET_REQ_WIN32_ERROR(req, GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
len = MAX_PATH + 1;
|
||||
pathw = uv__malloc(len * sizeof(*pathw));
|
||||
if (pathw == NULL) {
|
||||
SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
|
||||
return;
|
||||
}
|
||||
retry_get_full_path_name:
|
||||
ret = GetFullPathNameW(req->file.pathw,
|
||||
len,
|
||||
pathw,
|
||||
&fpart);
|
||||
if (ret == 0) {
|
||||
uv__free(pathw);
|
||||
SET_REQ_WIN32_ERROR(req, err);
|
||||
return;
|
||||
} else if (ret > len) {
|
||||
len = ret;
|
||||
pathw = uv__reallocf(pathw, len * sizeof(*pathw));
|
||||
if (pathw == NULL) {
|
||||
SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
|
||||
return;
|
||||
}
|
||||
goto retry_get_full_path_name;
|
||||
}
|
||||
if (fpart != 0)
|
||||
*fpart = L'\0';
|
||||
nt_status = pNtQueryVolumeInformationFile(handle,
|
||||
&io_status,
|
||||
&info,
|
||||
sizeof info,
|
||||
FileFsFullSizeInformation);
|
||||
CloseHandle(handle);
|
||||
|
||||
goto retry_get_disk_free_space;
|
||||
}
|
||||
if (pathw != req->file.pathw) {
|
||||
uv__free(pathw);
|
||||
if (NT_ERROR(nt_status)) {
|
||||
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
|
||||
return;
|
||||
}
|
||||
|
||||
stat_fs = uv__malloc(sizeof(*stat_fs));
|
||||
@ -3179,11 +3147,12 @@ retry_get_full_path_name:
|
||||
}
|
||||
|
||||
stat_fs->f_type = 0;
|
||||
stat_fs->f_bsize = bytes_per_sector * sectors_per_cluster;
|
||||
stat_fs->f_frsize = bytes_per_sector * sectors_per_cluster;
|
||||
stat_fs->f_blocks = total_clusters;
|
||||
stat_fs->f_bfree = free_clusters;
|
||||
stat_fs->f_bavail = free_clusters;
|
||||
stat_fs->f_bsize = (uint64_t)info.SectorsPerAllocationUnit *
|
||||
info.BytesPerSector;
|
||||
stat_fs->f_frsize = stat_fs->f_bsize;
|
||||
stat_fs->f_blocks = info.TotalAllocationUnits.QuadPart;
|
||||
stat_fs->f_bfree = info.ActualAvailableAllocationUnits.QuadPart;
|
||||
stat_fs->f_bavail = info.CallerAvailableAllocationUnits.QuadPart;
|
||||
stat_fs->f_files = 0;
|
||||
stat_fs->f_ffree = 0;
|
||||
req->ptr = stat_fs;
|
||||
|
||||
@ -4915,21 +4915,41 @@ TEST_FS_IMPL(fs_invalid_mkdir_name) {
|
||||
|
||||
TEST_FS_IMPL(fs_statfs) {
|
||||
uv_fs_t req;
|
||||
uv_fs_t req1;
|
||||
int r;
|
||||
|
||||
/* Setup. */
|
||||
unlink("test_file");
|
||||
|
||||
r = uv_fs_open(NULL, &req, "test_file", UV_FS_O_WRONLY | UV_FS_O_CREAT,
|
||||
S_IRUSR | S_IWUSR, NULL);
|
||||
ASSERT_GT(r, 0);
|
||||
|
||||
uv_fs_req_cleanup(&req);
|
||||
|
||||
r = uv_fs_close(NULL, &req, req.result, NULL);
|
||||
ASSERT_OK(r);
|
||||
|
||||
uv_fs_req_cleanup(&req);
|
||||
|
||||
loop = uv_default_loop();
|
||||
|
||||
/* Test the synchronous version. */
|
||||
/* Test the synchronous version for both a directory and a file. */
|
||||
r = uv_fs_statfs(NULL, &req, ".", NULL);
|
||||
ASSERT_OK(r);
|
||||
statfs_cb(&req);
|
||||
ASSERT_EQ(1, statfs_cb_count);
|
||||
r = uv_fs_statfs(NULL, &req, "test_file", NULL);
|
||||
ASSERT_OK(r);
|
||||
statfs_cb(&req);
|
||||
ASSERT_EQ(2, statfs_cb_count);
|
||||
|
||||
/* Test the asynchronous version. */
|
||||
/* Test the asynchronous version too. */
|
||||
r = uv_fs_statfs(loop, &req, ".", statfs_cb);
|
||||
ASSERT_OK(r);
|
||||
r = uv_fs_statfs(loop, &req1, "test_file", statfs_cb);
|
||||
ASSERT_OK(r);
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
ASSERT_EQ(2, statfs_cb_count);
|
||||
ASSERT_EQ(4, statfs_cb_count);
|
||||
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
return 0;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user