From ce25ef79a77633ecbd0042eb35c9432dd582d448 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Mon, 5 Jan 2026 12:25:12 +0100 Subject: pkg/osutil: add DiskUsage function DiskUsage returns total recursive disk usage of the dir (similar to du -s). --- pkg/osutil/osutil.go | 18 +++++++++++++++++ pkg/osutil/osutil_bsd.go | 41 -------------------------------------- pkg/osutil/osutil_darwin.go | 39 ------------------------------------ pkg/osutil/osutil_fuchsia.go | 31 ----------------------------- pkg/osutil/osutil_linux.go | 6 ++++++ pkg/osutil/osutil_nonlinux.go | 46 +++++++++++++++++++++++++++++++++++++++++++ pkg/osutil/osutil_test.go | 42 +++++++++++++++++++++++++++++++++++++++ pkg/osutil/osutil_windows.go | 31 ----------------------------- 8 files changed, 112 insertions(+), 142 deletions(-) delete mode 100644 pkg/osutil/osutil_bsd.go delete mode 100644 pkg/osutil/osutil_darwin.go create mode 100644 pkg/osutil/osutil_nonlinux.go (limited to 'pkg') diff --git a/pkg/osutil/osutil.go b/pkg/osutil/osutil.go index 936465ee7..fa963a3fb 100644 --- a/pkg/osutil/osutil.go +++ b/pkg/osutil/osutil.go @@ -11,6 +11,7 @@ import ( "errors" "fmt" "io" + "io/fs" "os" "os/exec" "path/filepath" @@ -366,3 +367,20 @@ func CreationTime(fi os.FileInfo) time.Time { // //go:linkname MonotonicNano runtime.nanotime func MonotonicNano() time.Duration + +// DiskUsage returns total recursive disk usage of the dir (similar to du -s). +func DiskUsage(dir string) (uint64, error) { + var total uint64 + err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + info, err := d.Info() + if err != nil { + return err + } + total += sysDiskUsage(info) + return nil + }) + return total, err +} diff --git a/pkg/osutil/osutil_bsd.go b/pkg/osutil/osutil_bsd.go deleted file mode 100644 index b26c91ff7..000000000 --- a/pkg/osutil/osutil_bsd.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2017 syzkaller project authors. All rights reserved. -// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. - -//go:build freebsd || netbsd || openbsd - -package osutil - -import ( - "os" - "os/exec" - "time" -) - -func creationTime(fi os.FileInfo) time.Time { - return time.Time{} -} - -func RemoveAll(dir string) error { - return os.RemoveAll(dir) -} - -func SystemMemorySize() uint64 { - return 0 -} - -func prolongPipe(r, w *os.File) { -} - -func Sandbox(cmd *exec.Cmd, user, net bool) error { - return nil -} - -func SandboxChown(file string) error { - return nil -} - -func setPdeathsig(cmd *exec.Cmd, hardKill bool) { -} - -func killPgroup(cmd *exec.Cmd) { -} diff --git a/pkg/osutil/osutil_darwin.go b/pkg/osutil/osutil_darwin.go deleted file mode 100644 index 0b1f30711..000000000 --- a/pkg/osutil/osutil_darwin.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2017 syzkaller project authors. All rights reserved. -// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. - -package osutil - -import ( - "os" - "os/exec" - "time" -) - -func creationTime(fi os.FileInfo) time.Time { - return time.Time{} -} - -func RemoveAll(dir string) error { - return os.RemoveAll(dir) -} - -func SystemMemorySize() uint64 { - return 0 -} - -func prolongPipe(r, w *os.File) { -} - -func Sandbox(cmd *exec.Cmd, user, net bool) error { - return nil -} - -func SandboxChown(file string) error { - return nil -} - -func setPdeathsig(cmd *exec.Cmd, hardKill bool) { -} - -func killPgroup(cmd *exec.Cmd) { -} diff --git a/pkg/osutil/osutil_fuchsia.go b/pkg/osutil/osutil_fuchsia.go index fce4e38ba..a4a8c22b2 100644 --- a/pkg/osutil/osutil_fuchsia.go +++ b/pkg/osutil/osutil_fuchsia.go @@ -7,43 +7,12 @@ package osutil import ( "os" - "os/exec" - "time" ) -func creationTime(fi os.FileInfo) time.Time { - return time.Time{} -} - func HandleInterrupts(shutdown chan struct{}) { } -func RemoveAll(dir string) error { - return os.RemoveAll(dir) -} - -func SystemMemorySize() uint64 { - return 0 -} - func ProcessExitStatus(ps *os.ProcessState) int { // TODO: can be extracted from ExitStatus string. return 0 } - -func prolongPipe(r, w *os.File) { -} - -func Sandbox(cmd *exec.Cmd, user, net bool) error { - return nil -} - -func SandboxChown(file string) error { - return nil -} - -func setPdeathsig(cmd *exec.Cmd, hardKill bool) { -} - -func killPgroup(cmd *exec.Cmd) { -} diff --git a/pkg/osutil/osutil_linux.go b/pkg/osutil/osutil_linux.go index 63743a5f2..50be9b047 100644 --- a/pkg/osutil/osutil_linux.go +++ b/pkg/osutil/osutil_linux.go @@ -5,6 +5,7 @@ package osutil import ( "fmt" + "io/fs" "os" "os/exec" "path/filepath" @@ -150,3 +151,8 @@ func prolongPipe(r, w *os.File) { syscall.Syscall(syscall.SYS_FCNTL, w.Fd(), syscall.F_SETPIPE_SZ, uintptr(sz)) } } + +func sysDiskUsage(info fs.FileInfo) uint64 { + stat := info.Sys().(*syscall.Stat_t) + return uint64(max(0, stat.Size, stat.Blocks*512)) +} diff --git a/pkg/osutil/osutil_nonlinux.go b/pkg/osutil/osutil_nonlinux.go new file mode 100644 index 000000000..1d3ee8b82 --- /dev/null +++ b/pkg/osutil/osutil_nonlinux.go @@ -0,0 +1,46 @@ +// Copyright 2026 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +//go:build !linux + +package osutil + +import ( + "io/fs" + "os" + "os/exec" + "time" +) + +func creationTime(fi os.FileInfo) time.Time { + return time.Time{} +} + +func RemoveAll(dir string) error { + return os.RemoveAll(dir) +} + +func SystemMemorySize() uint64 { + return 0 +} + +func prolongPipe(r, w *os.File) { +} + +func Sandbox(cmd *exec.Cmd, user, net bool) error { + return nil +} + +func SandboxChown(file string) error { + return nil +} + +func setPdeathsig(cmd *exec.Cmd, hardKill bool) { +} + +func killPgroup(cmd *exec.Cmd) { +} + +func sysDiskUsage(info fs.FileInfo) uint64 { + return uint64(max(0, info.Size())) +} diff --git a/pkg/osutil/osutil_test.go b/pkg/osutil/osutil_test.go index 4d9bf4cf8..460cc85ca 100644 --- a/pkg/osutil/osutil_test.go +++ b/pkg/osutil/osutil_test.go @@ -4,6 +4,7 @@ package osutil import ( + "bytes" "fmt" "os" "path/filepath" @@ -150,3 +151,44 @@ func TestReadWriteJSON(t *testing.T) { t.Fatal(diff) } } + +func TestDiskUsage(t *testing.T) { + dir := t.TempDir() + var currentUsage uint64 + expectUsage := func(minIncrease, maxIncrease uint64) { + usage, err := DiskUsage(dir) + if err != nil { + t.Fatal(err) + } + expectMin := currentUsage + minIncrease + expectMax := currentUsage + maxIncrease + t.Logf("got usage %v when expected (%v, %v)", usage, expectMin, expectMax) + if usage <= expectMin || usage >= expectMax { + t.Fatalf("bad usage %v, expect (%v, %v)", usage, expectMin, expectMax) + } + currentUsage = usage + } + expectUsage(1, 5<<10) + if err := MkdirAll(filepath.Join(dir, "nested")); err != nil { + t.Fatal(err) + } + expectUsage(1, 5<<10) + if err := WriteFile(filepath.Join(dir, "nested", "foo"), bytes.Repeat([]byte{'a'}, 1<<10)); err != nil { + t.Fatal(err) + } + expectUsage(1<<10, 5<<10) + if err := WriteFile(filepath.Join(dir, "nested", "bar"), bytes.Repeat([]byte{'a'}, 10<<10)); err != nil { + t.Fatal(err) + } + expectUsage(10<<10, 14<<10) + // Symlinks must not be counted twice. + if err := os.Symlink(filepath.Join(dir, "nested"), filepath.Join(dir, "dirlink")); err != nil { + t.Fatal(err) + } + expectUsage(1, 1<<10) + + if err := os.Symlink(filepath.Join(dir, "nested", "bar"), filepath.Join(dir, "filelink")); err != nil { + t.Fatal(err) + } + expectUsage(1, 1<<10) +} diff --git a/pkg/osutil/osutil_windows.go b/pkg/osutil/osutil_windows.go index 03e729e83..db9f56efa 100644 --- a/pkg/osutil/osutil_windows.go +++ b/pkg/osutil/osutil_windows.go @@ -5,43 +5,12 @@ package osutil import ( "os" - "os/exec" "syscall" - "time" ) -func creationTime(fi os.FileInfo) time.Time { - return time.Time{} -} - func HandleInterrupts(shutdown chan struct{}) { } -func RemoveAll(dir string) error { - return os.RemoveAll(dir) -} - -func SystemMemorySize() uint64 { - return 0 -} - -func prolongPipe(r, w *os.File) { -} - func ProcessExitStatus(ps *os.ProcessState) int { return ps.Sys().(syscall.WaitStatus).ExitStatus() } - -func Sandbox(cmd *exec.Cmd, user, net bool) error { - return nil -} - -func SandboxChown(file string) error { - return nil -} - -func setPdeathsig(cmd *exec.Cmd, hardKill bool) { -} - -func killPgroup(cmd *exec.Cmd) { -} -- cgit mrf-deployment