//! Scrape-time filesystem capacity for the archive data directory. const std = @import("std"); const builtin = @import("builtin"); const FsCount = if (builtin.os.tag.isDarwin()) c_uint else if (builtin.abi.isMusl()) u64 else c_ulong; /// `struct statvfs` through `f_namemax`; `reserved` covers the trailing /// fields Linux libcs append, which are never read. const Statvfs = extern struct { f_bsize: c_ulong, f_frsize: c_ulong, f_blocks: FsCount, f_bfree: FsCount, f_bavail: FsCount, f_files: FsCount, f_ffree: FsCount, f_favail: FsCount, f_fsid: c_ulong, f_flag: c_ulong, f_namemax: c_ulong, reserved: [8]c_int, }; extern "c" fn fstatvfs(fd: std.c.fd_t, buf: *Statvfs) c_int; /// Bytes available to this unprivileged process on the filesystem containing /// dir. Failure is represented as unavailable so /metrics never lies with a /// zero that looks like a full disk. pub fn freeBytes(dir: std.Io.Dir) ?u64 { var stat: Statvfs = undefined; if (fstatvfs(dir.handle, &stat) != 0) return null; return std.math.mul(u64, @intCast(stat.f_bavail), @intCast(stat.f_frsize)) catch null; } test "free bytes comes from the real temporary filesystem" { var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); const free = freeBytes(tmp.dir); try std.testing.expect(free != null and free.? > 0); }