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:
Santiago Gimeno 2026-02-02 10:04:36 +01:00 committed by GitHub
parent 91dc2389cc
commit c68ca444e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 55 additions and 66 deletions

View File

@ -3109,67 +3109,35 @@ static void fs__lchown(uv_fs_t* req) {
static void fs__statfs(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; uv_statfs_t* stat_fs;
DWORD sectors_per_cluster; NTSTATUS nt_status;
DWORD bytes_per_sector; HANDLE handle;
DWORD free_clusters;
DWORD total_clusters;
WCHAR* pathw;
pathw = req->file.pathw; handle = CreateFileW(req->file.pathw,
retry_get_disk_free_space: FILE_READ_ATTRIBUTES,
if (0 == GetDiskFreeSpaceW(pathw, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
&sectors_per_cluster, NULL,
&bytes_per_sector, OPEN_EXISTING,
&free_clusters, FILE_FLAG_BACKUP_SEMANTICS,
&total_clusters)) { NULL);
DWORD err;
WCHAR* fpart;
size_t len;
DWORD ret;
BOOL is_second;
err = GetLastError(); if (handle == INVALID_HANDLE_VALUE) {
is_second = pathw != req->file.pathw; SET_REQ_WIN32_ERROR(req, GetLastError());
if (err != ERROR_DIRECTORY || is_second) { return;
if (is_second)
uv__free(pathw);
SET_REQ_WIN32_ERROR(req, err);
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';
goto retry_get_disk_free_space;
} }
if (pathw != req->file.pathw) {
uv__free(pathw); nt_status = pNtQueryVolumeInformationFile(handle,
&io_status,
&info,
sizeof info,
FileFsFullSizeInformation);
CloseHandle(handle);
if (NT_ERROR(nt_status)) {
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
return;
} }
stat_fs = uv__malloc(sizeof(*stat_fs)); stat_fs = uv__malloc(sizeof(*stat_fs));
@ -3179,11 +3147,12 @@ retry_get_full_path_name:
} }
stat_fs->f_type = 0; stat_fs->f_type = 0;
stat_fs->f_bsize = bytes_per_sector * sectors_per_cluster; stat_fs->f_bsize = (uint64_t)info.SectorsPerAllocationUnit *
stat_fs->f_frsize = bytes_per_sector * sectors_per_cluster; info.BytesPerSector;
stat_fs->f_blocks = total_clusters; stat_fs->f_frsize = stat_fs->f_bsize;
stat_fs->f_bfree = free_clusters; stat_fs->f_blocks = info.TotalAllocationUnits.QuadPart;
stat_fs->f_bavail = free_clusters; stat_fs->f_bfree = info.ActualAvailableAllocationUnits.QuadPart;
stat_fs->f_bavail = info.CallerAvailableAllocationUnits.QuadPart;
stat_fs->f_files = 0; stat_fs->f_files = 0;
stat_fs->f_ffree = 0; stat_fs->f_ffree = 0;
req->ptr = stat_fs; req->ptr = stat_fs;

View File

@ -4915,21 +4915,41 @@ TEST_FS_IMPL(fs_invalid_mkdir_name) {
TEST_FS_IMPL(fs_statfs) { TEST_FS_IMPL(fs_statfs) {
uv_fs_t req; uv_fs_t req;
uv_fs_t req1;
int r; 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(); 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); r = uv_fs_statfs(NULL, &req, ".", NULL);
ASSERT_OK(r); ASSERT_OK(r);
statfs_cb(&req); 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); r = uv_fs_statfs(loop, &req, ".", statfs_cb);
ASSERT_OK(r); ASSERT_OK(r);
r = uv_fs_statfs(loop, &req1, "test_file", statfs_cb);
ASSERT_OK(r);
uv_run(loop, UV_RUN_DEFAULT); uv_run(loop, UV_RUN_DEFAULT);
ASSERT_EQ(2, statfs_cb_count); ASSERT_EQ(4, statfs_cb_count);
MAKE_VALGRIND_HAPPY(loop); MAKE_VALGRIND_HAPPY(loop);
return 0; return 0;