From 1b2bed92424f45248aa4b2f547d05e8140825637 Mon Sep 17 00:00:00 2001 From: Shantanav Mukherjee Date: Fri, 16 Oct 2026 08:03:42 +0530 Subject: [PATCH] fs: ignore disappearing files in DiskUsage A file or directory can disappear after filepath.Walk reads its parent but before the entry is inspected. Treat ENOENT for descendants as a transient condition because the removed entry no longer contributes to disk usage. Keep returning errors for a missing root and for all other walk failures. Apply the behavior on Unix and Windows and cover the error handling with regression tests. Assisted-by: OpenAI Codex Signed-off-by: Shantanav Mukherjee --- fs/du.go | 12 +++++++++++- fs/du_test.go | 20 ++++++++++++++++++++ fs/du_unix.go | 2 +- fs/du_windows.go | 2 +- 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/fs/du.go b/fs/du.go index fccc985d..e63ce10c 100644 --- a/fs/du.go +++ b/fs/du.go @@ -16,7 +16,17 @@ package fs -import "context" +import ( + "context" + "os" +) + +func handleWalkError(root, path string, err error) error { + if err != nil && path != root && os.IsNotExist(err) { + return nil + } + return err +} // Usage of disk information type Usage struct { diff --git a/fs/du_test.go b/fs/du_test.go index eb84b66c..29e0f61e 100644 --- a/fs/du_test.go +++ b/fs/du_test.go @@ -142,6 +142,26 @@ func TestUsage(t *testing.T) { } } +func TestHandleWalkError(t *testing.T) { + t.Parallel() + + root := filepath.Join("root") + missing := filepath.Join(root, "removed") + notExistErr := &os.PathError{Op: "lstat", Path: missing, Err: os.ErrNotExist} + + if err := handleWalkError(root, missing, notExistErr); err != nil { + t.Fatalf("expected missing child to be ignored, got %v", err) + } + if err := handleWalkError(root, root, notExistErr); !errors.Is(err, os.ErrNotExist) { + t.Fatalf("expected missing root error, got %v", err) + } + + permissionErr := &os.PathError{Op: "open", Path: missing, Err: os.ErrPermission} + if err := handleWalkError(root, missing, permissionErr); !errors.Is(err, os.ErrPermission) { + t.Fatalf("expected permission error, got %v", err) + } +} + // createSparseFile creates a sparse file filled with random // bytes for data parts // The parse alternate data length, hole length, data length, .... diff --git a/fs/du_unix.go b/fs/du_unix.go index d960b236..a77206ee 100644 --- a/fs/du_unix.go +++ b/fs/du_unix.go @@ -67,7 +67,7 @@ func diskUsage(ctx context.Context, roots ...string) (Usage, error) { for _, root := range roots { if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { if err != nil { - return err + return handleWalkError(root, path, err) } select { diff --git a/fs/du_windows.go b/fs/du_windows.go index 48494e2c..3fb52bfe 100644 --- a/fs/du_windows.go +++ b/fs/du_windows.go @@ -32,7 +32,7 @@ func diskUsage(ctx context.Context, roots ...string) (Usage, error) { for _, root := range roots { if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { if err != nil { - return err + return handleWalkError(root, path, err) } select {