lib/fs: Add total disk space retrieval (#9523)

Extends the disk space monitoring functionality by adding support for
retrieving total disk capacity in addition to free space.

Related: https://github.com/VictoriaMetrics/VictoriaLogs/issues/513

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Phuong Le
2025-08-04 14:58:19 +07:00
committed by GitHub
parent ddadfd6d58
commit 7da45924e2
7 changed files with 68 additions and 19 deletions

View File

@@ -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.

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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