From 0fb36562dd30991c8895cedf37d6d6bb66de9d03 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Tue, 5 Sep 2023 10:29:05 +0100 Subject: [PATCH] Add lib/diskusage to measure used/free on disks --- lib/diskusage/diskusage.go | 15 ++++++++++++++ lib/diskusage/diskusage_netbsd.go | 27 ++++++++++++++++++++++++++ lib/diskusage/diskusage_openbsd.go | 27 ++++++++++++++++++++++++++ lib/diskusage/diskusage_test.go | 23 ++++++++++++++++++++++ lib/diskusage/diskusage_unix.go | 27 ++++++++++++++++++++++++++ lib/diskusage/diskusage_unsupported.go | 10 ++++++++++ lib/diskusage/diskusage_windows.go | 16 +++++++++++++++ 7 files changed, 145 insertions(+) create mode 100644 lib/diskusage/diskusage.go create mode 100644 lib/diskusage/diskusage_netbsd.go create mode 100644 lib/diskusage/diskusage_openbsd.go create mode 100644 lib/diskusage/diskusage_test.go create mode 100644 lib/diskusage/diskusage_unix.go create mode 100644 lib/diskusage/diskusage_unsupported.go create mode 100644 lib/diskusage/diskusage_windows.go diff --git a/lib/diskusage/diskusage.go b/lib/diskusage/diskusage.go new file mode 100644 index 000000000..a2caa93ae --- /dev/null +++ b/lib/diskusage/diskusage.go @@ -0,0 +1,15 @@ +// Package diskusage provides a cross platform version of the statfs +// system call to read disk space usage. +package diskusage + +import "errors" + +// Info is returned from New showing details about the disk. +type Info struct { + Free uint64 // total free bytes + Available uint64 // free bytes available to the current user + Total uint64 // total bytes on disk +} + +// ErrUnsupported is returned if this platform doesn't support disk usage. +var ErrUnsupported = errors.New("disk usage unsupported on this platform") diff --git a/lib/diskusage/diskusage_netbsd.go b/lib/diskusage/diskusage_netbsd.go new file mode 100644 index 000000000..973bd1d86 --- /dev/null +++ b/lib/diskusage/diskusage_netbsd.go @@ -0,0 +1,27 @@ +//go:build netbsd + +package diskusage + +import ( + "golang.org/x/sys/unix" +) + +// New returns the disk status for dir. +// +// May return Unsupported error if it doesn't work on this platform. +func New(dir string) (info Info, err error) { + var statfs unix.Statvfs_t + err = unix.Statvfs(dir, &statfs) + if err != nil { + return info, err + } + // Note that these can be different sizes on different OSes so + // we upcast them all to uint64 + //nolint:unconvert + info.Free = uint64(statfs.Bfree) * uint64(statfs.Bsize) + //nolint:unconvert + info.Available = uint64(statfs.Bavail) * uint64(statfs.Bsize) + //nolint:unconvert + info.Total = uint64(statfs.Blocks) * uint64(statfs.Bsize) + return info, nil +} diff --git a/lib/diskusage/diskusage_openbsd.go b/lib/diskusage/diskusage_openbsd.go new file mode 100644 index 000000000..ff0e1f7a4 --- /dev/null +++ b/lib/diskusage/diskusage_openbsd.go @@ -0,0 +1,27 @@ +//go:build openbsd + +package diskusage + +import ( + "golang.org/x/sys/unix" +) + +// New returns the disk status for dir. +// +// May return Unsupported error if it doesn't work on this platform. +func New(dir string) (info Info, err error) { + var statfs unix.Statfs_t + err = unix.Statfs(dir, &statfs) + if err != nil { + return info, err + } + // Note that these can be different sizes on different OSes so + // we upcast them all to uint64 + //nolint:unconvert + info.Free = uint64(statfs.F_bfree) * uint64(statfs.F_bsize) + //nolint:unconvert + info.Available = uint64(statfs.F_bavail) * uint64(statfs.F_bsize) + //nolint:unconvert + info.Total = uint64(statfs.F_blocks) * uint64(statfs.F_bsize) + return info, nil +} diff --git a/lib/diskusage/diskusage_test.go b/lib/diskusage/diskusage_test.go new file mode 100644 index 000000000..f0263bfb5 --- /dev/null +++ b/lib/diskusage/diskusage_test.go @@ -0,0 +1,23 @@ +package diskusage + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNew(t *testing.T) { + info, err := New(".") + if err == ErrUnsupported { + t.Skip(err) + } + require.NoError(t, err) + t.Logf("Free %16d", info.Free) + t.Logf("Available %16d", info.Available) + t.Logf("Total %16d", info.Total) + assert.True(t, info.Total != 0) + assert.True(t, info.Total > info.Free) + assert.True(t, info.Total > info.Available) + assert.True(t, info.Free >= info.Available) +} diff --git a/lib/diskusage/diskusage_unix.go b/lib/diskusage/diskusage_unix.go new file mode 100644 index 000000000..822cd96f7 --- /dev/null +++ b/lib/diskusage/diskusage_unix.go @@ -0,0 +1,27 @@ +//go:build aix || android || darwin || dragonfly || freebsd || ios || linux + +package diskusage + +import ( + "golang.org/x/sys/unix" +) + +// New returns the disk status for dir. +// +// May return Unsupported error if it doesn't work on this platform. +func New(dir string) (info Info, err error) { + var statfs unix.Statfs_t + err = unix.Statfs(dir, &statfs) + if err != nil { + return info, err + } + // Note that these can be different sizes on different OSes so + // we upcast them all to uint64 + //nolint:unconvert + info.Free = uint64(statfs.Bfree) * uint64(statfs.Bsize) + //nolint:unconvert + info.Available = uint64(statfs.Bavail) * uint64(statfs.Bsize) + //nolint:unconvert + info.Total = uint64(statfs.Blocks) * uint64(statfs.Bsize) + return info, nil +} diff --git a/lib/diskusage/diskusage_unsupported.go b/lib/diskusage/diskusage_unsupported.go new file mode 100644 index 000000000..1e4bddc40 --- /dev/null +++ b/lib/diskusage/diskusage_unsupported.go @@ -0,0 +1,10 @@ +//go:build illumos || js || plan9 || solaris + +package diskusage + +// New returns the disk status for dir. +// +// May return Unsupported error if it doesn't work on this platform. +func New(dir string) (info Info, err error) { + return info, ErrUnsupported +} diff --git a/lib/diskusage/diskusage_windows.go b/lib/diskusage/diskusage_windows.go new file mode 100644 index 000000000..e7a957c5d --- /dev/null +++ b/lib/diskusage/diskusage_windows.go @@ -0,0 +1,16 @@ +//go:build windows + +package diskusage + +import ( + "golang.org/x/sys/windows" +) + +// New returns the disk status for dir. +// +// May return Unsupported error if it doesn't work on this platform. +func New(dir string) (info Info, err error) { + dir16 := windows.StringToUTF16Ptr(dir) + err = windows.GetDiskFreeSpaceEx(dir16, &info.Available, &info.Total, &info.Free) + return info, err +}