backup: skip source paths that cannot be accessed (#21852)

This commit is contained in:
Eyüp Can Akman
2026-06-21 18:16:16 +03:00
committed by GitHub
parent d9dd1b7534
commit 8d7679c831
3 changed files with 40 additions and 4 deletions
+8 -4
View File
@@ -175,13 +175,17 @@ var ErrInvalidSourceData = errors.New("at least one source file could not be rea
// ErrNoSourceData is used to report that no source data was found
var ErrNoSourceData = errors.Fatal("all source directories/files do not exist")
// filterExisting returns a slice of all existing items, or an error if no
// items exist at all.
// filterExisting returns the items that exist and can be accessed. It returns
// ErrNoSourceData if none remain, or ErrInvalidSourceData if some were skipped.
func filterExisting(items []string, warnf func(msg string, args ...interface{})) (result []string, err error) {
for _, item := range items {
_, err := fs.Lstat(item)
if errors.Is(err, os.ErrNotExist) {
warnf("%v does not exist, skipping\n", item)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
warnf("%v does not exist, skipping\n", item)
} else {
warnf("%v cannot be accessed, skipping\n", item)
}
continue
}
+23
View File
@@ -10,6 +10,7 @@ import (
"strings"
"testing"
"github.com/restic/restic/internal/errors"
rtest "github.com/restic/restic/internal/test"
)
@@ -76,6 +77,28 @@ func TestCollectTargets(t *testing.T) {
rtest.Assert(t, err == ErrInvalidSourceData, "expected error when not all targets exist")
}
func TestFilterExistingUnreadable(t *testing.T) {
dir := rtest.TempDir(t)
existing := filepath.Join(dir, "existing")
rtest.OK(t, os.Mkdir(existing, 0755))
file := filepath.Join(dir, "file")
rtest.OK(t, os.WriteFile(file, []byte("x"), 0600))
// Regression test for #5667. A target whose Lstat fails with an error other
// than ErrNotExist must be skipped (ENOTDIR on unix, NUL byte everywhere).
for _, unreadable := range []string{filepath.Join(file, "child"), "invalid\x00path"} {
result, err := filterExisting([]string{unreadable}, t.Logf)
rtest.Assert(t, errors.Is(err, ErrNoSourceData), "input %q: expected ErrNoSourceData; got %v", unreadable, err)
rtest.Assert(t, len(result) == 0, "input %q: expected no targets; got %v", unreadable, result)
result, err = filterExisting([]string{existing, unreadable}, t.Logf)
rtest.Assert(t, errors.Is(err, ErrInvalidSourceData), "input %q: expected ErrInvalidSourceData; got %v", unreadable, err)
rtest.Equals(t, []string{existing}, result)
}
}
func TestReadFilenamesRaw(t *testing.T) {
// These should all be returned exactly as-is.
expected := []string{