diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp --- a/libcxx/src/filesystem/operations.cpp +++ b/libcxx/src/filesystem/operations.cpp @@ -1301,8 +1301,8 @@ space_info __space(const path& p, error_code* ec) { ErrorHandler err("space", ec, &p); space_info si; - struct statvfs m_svfs = {}; - if (::statvfs(p.c_str(), &m_svfs) == -1) { + detail::StatVFS m_svfs = {}; + if (detail::statvfs(p.c_str(), &m_svfs) == -1) { err.report(capture_errno()); si.capacity = si.free = si.available = static_cast(-1); return si; diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h --- a/libcxx/src/filesystem/posix_compat.h +++ b/libcxx/src/filesystem/posix_compat.h @@ -277,6 +277,40 @@ } int close(int fd) { return _close(fd); } int chdir(const wchar_t *path) { return _wchdir(path); } + +struct StatVFS { + uint64_t f_frsize; + uint64_t f_blocks; + uint64_t f_bfree; + uint64_t f_bavail; +}; + +int statvfs(const wchar_t *p, StatVFS *buf) { + path dir = p; + while (true) { + error_code local_ec; + const file_status st = status(dir, local_ec); + if (!exists(st) || is_directory(st)) + break; + path parent = dir.parent_path(); + if (parent == dir) { + errno = ENOENT; + return -1; + } + dir = parent; + } + ULARGE_INTEGER free_bytes_available_to_caller, total_number_of_bytes, + total_number_of_free_bytes; + if (!GetDiskFreeSpaceExW(dir.c_str(), &free_bytes_available_to_caller, + &total_number_of_bytes, &total_number_of_free_bytes)) + return set_errno(); + buf->f_frsize = 1; + buf->f_blocks = total_number_of_bytes.QuadPart; + buf->f_bfree = total_number_of_free_bytes.QuadPart; + buf->f_bavail = free_bytes_available_to_caller.QuadPart; + return 0; +} + #else int symlink_file(const char *oldname, const char *newname) { return ::symlink(oldname, newname); @@ -295,10 +329,13 @@ using ::remove; using ::rename; using ::stat; +using ::statvfs; using ::truncate; #define O_BINARY 0 +using StatVFS = struct statvfs; + #endif } // namespace