diff --git a/NEWS.md b/NEWS.md index 9fca99a785f3a3..9c1485888b74ca 100644 --- a/NEWS.md +++ b/NEWS.md @@ -50,6 +50,7 @@ New library functions --------------------- * `hardlink(src, dst)` can be used to create hard links. ([#41639]) +* `diskstat(path=pwd())` can be used to return statistics about the disk. ([#42248]) New library features -------------------- diff --git a/base/exports.jl b/base/exports.jl index 3c7baa40cb5fcf..84c53ca405e7d0 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -893,6 +893,7 @@ export chown, cp, ctime, + diskstat, download, filemode, filesize, diff --git a/base/file.jl b/base/file.jl index 66e49174a4656e..4cca1f51d01240 100644 --- a/base/file.jl +++ b/base/file.jl @@ -8,6 +8,7 @@ export chown, cp, cptree, + diskstat, hardlink, mkdir, mkpath, @@ -1168,3 +1169,61 @@ function chown(path::AbstractString, owner::Integer, group::Integer=-1) err < 0 && uv_error("chown($(repr(path)), $owner, $group)", err) path end + + +# - http://docs.libuv.org/en/v1.x/fs.html#c.uv_fs_statfs (libuv function docs) +# - http://docs.libuv.org/en/v1.x/fs.html#c.uv_statfs_t (libuv docs of the returned struct) +""" + DiskStat + +Stores information about the disk in bytes. Populate by calling `diskstat`. +""" +struct DiskStat + ftype::UInt64 + bsize::UInt64 + blocks::UInt64 + bfree::UInt64 + bavail::UInt64 + files::UInt64 + ffree::UInt64 + fspare::NTuple{4, UInt64} # reserved +end + +function Base.getproperty(stats::DiskStat, field::Symbol) + total = getfield(stats, :bsize) * getfield(stats, :blocks) + available = getfield(stats, :bsize) * getfield(stats, :bavail) + field === :available && return available + field === :total && return total + field === :used && return total - available + return getfield(stats, field) +end + +@eval Base.propertynames(stats::DiskStat) = $((fieldnames(DiskStat)[1:end-1]..., :available, :total, :used)) + +function Base.show(io::IO, x::DiskStat) + print(io, "DiskStat(") + for field in 1:(nfields(x) - 1) + print(io, "$(getfield(x, field)), ") + end + print(io, "available: $(x.available), total: $(x.total), used: $(x.used))") +end + +""" + diskstat(path=pwd()) + +Returns statistics in bytes about the disk that contains the file or directory pointed at by +`path`. If no argument is passed, statistics about the disk that contains the current +working directory are returned. + +!!! compat "Julia 1.8" + This method was added in Julia 1.8. +""" +function diskstat(path::AbstractString=pwd()) + req = zeros(UInt8, _sizeof_uv_fs) + err = ccall(:uv_fs_statfs, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), + C_NULL, req, path, C_NULL) + err < 0 && uv_error("diskstat($(repr(path)))", err) + statfs_ptr = ccall(:jl_uv_fs_t_ptr, Ptr{Nothing}, (Ptr{Cvoid},), req) + + return unsafe_load(reinterpret(Ptr{DiskStat}, statfs_ptr)) +end diff --git a/doc/src/base/file.md b/doc/src/base/file.md index 40d1cc2ca7ef0a..86a1f2bab5dcd8 100644 --- a/doc/src/base/file.md +++ b/doc/src/base/file.md @@ -15,6 +15,7 @@ Base.Filesystem.chmod Base.Filesystem.chown Base.RawFD Base.stat +Base.Filesystem.diskstat Base.Filesystem.lstat Base.Filesystem.ctime Base.Filesystem.mtime diff --git a/test/file.jl b/test/file.jl index 39c73eed390def..3515b7a0a3de9d 100644 --- a/test/file.jl +++ b/test/file.jl @@ -1695,3 +1695,17 @@ end @test !isnothing(Base.Filesystem.getgroupname(s.gid)) end end + +@testset "diskstat() works" begin + # Sanity check assuming disk is smaller than 32PB + PB = Int64(2)^44 + + dstat = diskstat() + @test dstat.total < 32PB + @test dstat.used + dstat.available == dstat.total + @test occursin(r"^DiskStat\(.*, available: \d+, total: \d+, used: \d+\)$", sprint(show, dstat)) + # Test diskstat(::AbstractString) + dstat = diskstat(pwd()) + @test dstat.total < 32PB + @test dstat.used + dstat.available == dstat.total +end