diff --git a/src/HiddenFiles.jl b/src/HiddenFiles.jl index e6ecfc9..0fe6c88 100644 --- a/src/HiddenFiles.jl +++ b/src/HiddenFiles.jl @@ -28,16 +28,15 @@ include("docs.jl") @static if Sys.isunix() include("utils/zfs.jl") - if iszfs() # @static breaks here # ZFS + + function _ishidden_zfs(f::AbstractString, rp::AbstractString) error("not yet implemented") - _ishidden_zfs(f::AbstractString, rp::AbstractString) = error("not yet implemented") - _ishidden = _ishidden_zfs end - + # Trivial Unix check _isdotfile(f::AbstractString) = startswith(basename(f), '.') # Check dotfiles, but also account for ZFS - _ishidden_unix(f::AbstractString, rp::AbstractString) = _isdotfile(rp) || (iszfs() && _ishidden_zfs("", "")) + _ishidden_unix(f::AbstractString, rp::AbstractString) = _isdotfile(rp) || (iszfs() && _ishidden_zfs(f, rp)) @static if Sys.isbsd() # BDS-related; this is true for macOS as well # https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/chflags.2.html diff --git a/src/docs.jl b/src/docs.jl index af41ab6..4ded083 100644 --- a/src/docs.jl +++ b/src/docs.jl @@ -14,8 +14,17 @@ See also: [`_ishidden_unix`](@ref), [`_ishidden_windows`](@ref), [`_ishidden_mac _ishidden ### ZFS ### -"This function is not yet implemented" -iszfs +""" +```julia +_iszfs(f::AbstractString) -> Bool +``` + +Determines if the file system that `f` exists on is ZFS. + +!!! note + This is only implemented for Unix-like systems, as I have not seen ZFS for Windows. +""" +_iszfs "This function is not yet implemented" _ishidden_zfs diff --git a/src/utils/zfs.jl b/src/utils/zfs.jl index 5341127..78fe8af 100644 --- a/src/utils/zfs.jl +++ b/src/utils/zfs.jl @@ -1,5 +1,95 @@ -function iszfs() - # TODO - return false +# TODO: Correct < 1.6 error codes + +# https://lists.freebsd.org/pipermail/freebsd-hackers/2018-February/052295.html +const SUN_ZFS_SUPER_MAGIC = 0x1b +const BSD_ZFS_SUPER_MAGIC = 0xde # Obtained from testing +# https://reviews.freebsd.org/rS320069 +const LINUX_ZFS_SUPER_MAGIC = 0x2fc12fc1 + +const ZFS_SUPER_MAGICS = (SUN_ZFS_SUPER_MAGIC, BSD_ZFS_SUPER_MAGIC, LINUX_ZFS_SUPER_MAGIC) + +@static if VERSION ≥ v"1.6" + @static if VERSION < v"1.8" + # Adapted from Julia 1.8's diskstat: https://github.com/JuliaLang/julia/pull/42248 + # C calls for UV statfs requires Julia 1.6 + # http://docs.libuv.org/en/v1.x/fs.html#c.uv_fs_statfs + struct DiskStat + ftype::UInt64 + bsize::UInt64 + blocks::UInt64 + bfree::UInt64 + bavail::UInt64 + files::UInt64 + ffree::UInt64 + fspare::NTuple{4, UInt64} # reserved + end + function statfs(f::AbstractString) + buf = Vector{UInt8}(undef, ccall(:jl_sizeof_uv_fs_t, Int32, ())) + i = ccall(:uv_fs_statfs, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), + C_NULL, buf, f, C_NULL) + i < 0 && Base.uv_error("statfs($(repr(f)))", i) + p = ccall(:jl_uv_fs_t_ptr, Ptr{UInt32}, (Ptr{Cvoid},), buf) + + return unsafe_load(reinterpret(Ptr{DiskStat}, p)) + end + else + statfs = diskstat + end + + function _iszfs(f::AbstractString) + s = statfs(f) + @info s.ftype + return s.ftype ∈ ZFS_SUPER_MAGICS + end +else + # If Julia version < 1.6, we have to write our own, not very nice solution + + # It occurred to me that UInt8 wasn't always enough to store the f_type (eda1c22) + const STATFS_BUF_ELTYPE = UInt32 + const SIZEOF_STATFS_BUF_ELTYPE = Int8(sizeof(STATFS_BUF_ELTYPE)) + + @static if Sys.isapple() + # macOS: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/statfs.2.html + const SIZEOF_STATFS = cld(2168, SIZEOF_STATFS_BUF_ELTYPE) + const F_TYPE_OFFSET = cld(0x3d, SIZEOF_STATFS_BUF_ELTYPE) + const F_FSSUBTYPE_OFFSET = cld(0x45, SIZEOF_STATFS_BUF_ELTYPE) + + function _iszfs(f::AbstractString) + buf = Vector{STATFS_BUF_ELTYPE}(undef, SIZEOF_STATFS) + # statfs(const char *path, struct statfs *buf); + i = ccall(:statfs, Int, (Cstring, Ptr{Cvoid}), f, buf) + i < 0 && Base.uv_error("statfs($(repr(f)))", i) + @info buf[F_TYPE_OFFSET], buf[F_FSSUBTYPE_OFFSET], buf, f + return buf[F_TYPE_OFFSET] ∈ ZFS_SUPER_MAGICS || buf[F_FSSUBTYPE_OFFSET] ∈ ZFS_SUPER_MAGICS + end + elseif Sys.isbsd() + # https://www.freebsd.org/cgi/man.cgi?query=statfs&sektion=2 + const SIZEOF_STATFS = cld(2344, SIZEOF_STATFS_BUF_ELTYPE) + const F_TYPE_OFFSET = cld(0x05, SIZEOF_STATFS_BUF_ELTYPE) + + function _iszfs(f::AbstractString) + buf = Vector{STATFS_BUF_ELTYPE}(undef, SIZEOF_STATFS) + # statfs(const char *path, struct statfs *buf); + i = ccall(:statfs, Int, (Cstring, Ptr{Cvoid}), f, buf) + i < 0 && Base.uv_error("statfs($(repr(f)))", i) + @info buf[F_TYPE_OFFSET], buf, f + return buf[F_TYPE_OFFSET] ∈ ZFS_SUPER_MAGICS + end + elseif Sys.isunix() + # Linux: https://man7.org/linux/man-pages/man2/statfs.2.html + const SIZEOF_STATFS = cld(120, SIZEOF_STATFS_BUF_ELTYPE) + const F_TYPE_OFFSET = cld(0x01, SIZEOF_STATFS_BUF_ELTYPE) + + function _iszfs(f::AbstractString) + buf = Vector{STATFS_BUF_ELTYPE}(undef, SIZEOF_STATFS) + # statfs(const char *path, struct statfs *buf); + i = ccall(:statfs, Int, (Cstring, Ptr{Cvoid}), f, buf) + i < 0 && Base.uv_error("statfs($(repr(f)))", i) + @info buf[F_TYPE_OFFSET], buf, f + return buf[F_TYPE_OFFSET] ∈ ZFS_SUPER_MAGICS + end + else + _iszfs(_f::AbstractString) = error("Cannot call statfs for non-Unix operating systems") + end end