diff --git a/lib/fs/fs.go b/lib/fs/fs.go index e8cbe48923..31c176b12c 100644 --- a/lib/fs/fs.go +++ b/lib/fs/fs.go @@ -286,31 +286,56 @@ const FlockFilename = "flock.lock" // MustGetFreeSpace returns free space for the given directory path. func MustGetFreeSpace(path string) uint64 { // Try obtaining cached value at first. - freeSpaceMapLock.Lock() - defer freeSpaceMapLock.Unlock() + diskSpaceMapLock.Lock() + defer diskSpaceMapLock.Unlock() - e, ok := freeSpaceMap[path] + e, ok := diskSpaceMap[path] if ok && fasttime.UnixTimestamp()-e.updateTime < 2 { // Fast path - the entry is fresh. - return e.freeSpace + return e.free } // Slow path. // Determine the amount of free space at path. - e.freeSpace = mustGetFreeSpace(path) + e = updateDiskSpaceLocked(path) + return e.free +} + +// MustGetTotalSpace returns the total disk space for the given directory path. +func MustGetTotalSpace(path string) uint64 { + // Try obtaining cached value at first. + defer diskSpaceMapLock.Unlock() + + e, ok := diskSpaceMap[path] + if ok && fasttime.UnixTimestamp()-e.updateTime < 2 { + // Fast path - the entry is fresh. + return e.total + } + + // Slow path. + // Determine the amount of total space at path. + e = updateDiskSpaceLocked(path) + return e.total +} + +func updateDiskSpaceLocked(path string) diskSpaceEntry { + var e diskSpaceEntry + e.total, e.free = mustGetDiskSpace(path) e.updateTime = fasttime.UnixTimestamp() - freeSpaceMap[path] = e - return e.freeSpace + diskSpaceMap[path] = e + + return e } var ( - freeSpaceMap = make(map[string]freeSpaceEntry) - freeSpaceMapLock sync.Mutex + diskSpaceMap = make(map[string]diskSpaceEntry) + diskSpaceMapLock sync.Mutex ) -type freeSpaceEntry struct { +type diskSpaceEntry struct { updateTime uint64 - freeSpace uint64 + free uint64 + total uint64 } // IsDirOrSymlink returns true if de is directory or symlink. diff --git a/lib/fs/fs_netbsd.go b/lib/fs/fs_netbsd.go index 74d173ac44..12e4720a99 100644 --- a/lib/fs/fs_netbsd.go +++ b/lib/fs/fs_netbsd.go @@ -10,6 +10,11 @@ func freeSpace(stat statfs_t) uint64 { return uint64(stat.Bavail) * uint64(stat.Bsize) } +// totalSpace returns the total capacity of the filesystem in bytes. +func totalSpace(stat statfs_t) uint64 { + return uint64(stat.Blocks) * uint64(stat.Bsize) +} + func statfs(path string, buf *statfs_t) (err error) { return unix.Statvfs(path, buf) } diff --git a/lib/fs/fs_nix.go b/lib/fs/fs_nix.go index 8264bfaa6a..dacfdb6020 100644 --- a/lib/fs/fs_nix.go +++ b/lib/fs/fs_nix.go @@ -12,6 +12,11 @@ func freeSpace(stat statfs_t) uint64 { return uint64(stat.Bavail) * uint64(stat.Bsize) } +// totalSpace returns the total capacity of the filesystem described by stat in bytes. +func totalSpace(stat statfs_t) uint64 { + return uint64(stat.Blocks) * uint64(stat.Bsize) +} + func statfs(path string, stat *statfs_t) (err error) { return unix.Statfs(path, stat) } diff --git a/lib/fs/fs_openbsd.go b/lib/fs/fs_openbsd.go index 131e55791a..1d6857111e 100644 --- a/lib/fs/fs_openbsd.go +++ b/lib/fs/fs_openbsd.go @@ -10,6 +10,11 @@ func freeSpace(stat statfs_t) uint64 { return uint64(stat.F_bavail) * uint64(stat.F_bsize) } +// totalSpace returns the total capacity of the filesystem in bytes. +func totalSpace(stat statfs_t) uint64 { + return uint64(stat.F_blocks) * uint64(stat.F_bsize) +} + func statfs(path string, stat *statfs_t) (err error) { return unix.Statfs(path, stat) } diff --git a/lib/fs/fs_solaris.go b/lib/fs/fs_solaris.go index 385903896f..b71a3bc206 100644 --- a/lib/fs/fs_solaris.go +++ b/lib/fs/fs_solaris.go @@ -48,12 +48,19 @@ func createFlockFile(flockFile string) (*os.File, error) { return flockF, nil } -func mustGetFreeSpace(path string) uint64 { +func mustGetDiskSpace(path string) (total, free uint64) { var stat unix.Statvfs_t if err := unix.Statvfs(path, &stat); err != nil { logger.Panicf("FATAL: cannot determine free disk space on %q: %s", path, err) } - return freeSpace(stat) + total = totalSpace(stat) + free = freeSpace(stat) + return +} + +// totalSpace returns the total capacity of the filesystem in bytes. +func totalSpace(stat unix.Statvfs_t) uint64 { + return uint64(stat.Blocks) * uint64(stat.Bsize) } func freeSpace(stat unix.Statvfs_t) uint64 { diff --git a/lib/fs/fs_unix.go b/lib/fs/fs_unix.go index 40beac8303..bf462d9874 100644 --- a/lib/fs/fs_unix.go +++ b/lib/fs/fs_unix.go @@ -43,10 +43,13 @@ func createFlockFile(flockFile string) (*os.File, error) { return flockF, nil } -func mustGetFreeSpace(path string) uint64 { +func mustGetDiskSpace(path string) (total, free uint64) { var stat statfs_t if err := statfs(path, &stat); err != nil { logger.Panicf("FATAL: cannot determine free disk space on %q: %s", path, err) } - return freeSpace(stat) + + total = totalSpace(stat) + free = freeSpace(stat) + return } diff --git a/lib/fs/fs_windows.go b/lib/fs/fs_windows.go index 9fbc02af75..d591013045 100644 --- a/lib/fs/fs_windows.go +++ b/lib/fs/fs_windows.go @@ -102,14 +102,13 @@ func mUnmap(data []byte) error { return os.NewSyscallError("CloseHandle", errno) } -func mustGetFreeSpace(path string) uint64 { - var freeBytes uint64 +func mustGetDiskSpace(path string) (total, free uint64) { // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getdiskfreespaceexw - err := windows.GetDiskFreeSpaceEx(windows.StringToUTF16Ptr(path), &freeBytes, nil, nil) + err := windows.GetDiskFreeSpaceEx(windows.StringToUTF16Ptr(path), &free, &total, nil) if err != nil { logger.Panicf("FATAL: cannot get free space for %q : %s", path, err) } - return freeBytes + return total, free } // stub