diff --git a/pkg/util/fsutil/fsutil.go b/pkg/util/fsutil/fsutil.go index 3a2487ba..07242b80 100644 --- a/pkg/util/fsutil/fsutil.go +++ b/pkg/util/fsutil/fsutil.go @@ -805,6 +805,7 @@ func copyFileObjectHandler( excludePatterns []string, ignoreDirNames, ignoreFileNames map[string]struct{}, errs *[]error) filepath.WalkFunc { var foCount uint64 + const recursivePatternSuffix = "/**" return func(path string, info os.FileInfo, err error) error { foCount++ @@ -823,13 +824,19 @@ func copyFileObjectHandler( var isIgnored bool for _, xpattern := range excludePatterns { - found, err := doublestar.Match(xpattern, path) + matched, err := doublestar.Match(xpattern, path) if err != nil { log.Warnf("copyFileObjectHandler - [%v] excludePatterns Match error - %v\n", path, err) //should only happen when the pattern is malformed continue } - if found { + + if !matched && strings.HasSuffix(xpattern, recursivePatternSuffix) { + trimmedPattern := filepath.Clean(strings.TrimSuffix(xpattern, recursivePatternSuffix)) + matched = trimmedPattern == filepath.Clean(path) + } + + if matched { isIgnored = true break } diff --git a/pkg/util/fsutil/fsutil_test.go b/pkg/util/fsutil/fsutil_test.go new file mode 100644 index 00000000..95d7d86b --- /dev/null +++ b/pkg/util/fsutil/fsutil_test.go @@ -0,0 +1,50 @@ +package fsutil + +import ( + "errors" + "os" + "path/filepath" + "testing" +) + +func TestCopyDirExcludesWholeDirectoryWhenPatternHasDoubleStar(t *testing.T) { + src := t.TempDir() + dst := t.TempDir() + + excludedDir := filepath.Join(src, "var", "lib", "postgresql", "data") + if err := os.MkdirAll(excludedDir, 0o755); err != nil { + t.Fatalf("failed to create excluded dir: %v", err) + } + + excludedFile := filepath.Join(excludedDir, "file.txt") + if err := os.WriteFile(excludedFile, []byte("x"), 0o644); err != nil { + t.Fatalf("failed to create excluded file: %v", err) + } + + keepFile := filepath.Join(src, "keep.txt") + if err := os.WriteFile(keepFile, []byte("y"), 0o644); err != nil { + t.Fatalf("failed to create keep file: %v", err) + } + + pattern := excludedDir + "/**" + clone := false + copyRelPath := true + skipErrors := true + + if err, errs := CopyDir(clone, src, dst, copyRelPath, skipErrors, []string{pattern}, nil, nil); err != nil { + t.Fatalf("CopyDir returned error: %v", err) + } else if len(errs) > 0 { + t.Fatalf("CopyDir returned copy errors: %v", errs) + } + + excludedTarget := filepath.Join(dst, "var", "lib", "postgresql", "data") + if _, err := os.Stat(excludedTarget); err == nil { + t.Fatalf("expected excluded directory %q to be skipped", excludedTarget) + } else if !errors.Is(err, os.ErrNotExist) { + t.Fatalf("unexpected error checking excluded directory: %v", err) + } + + if _, err := os.Stat(filepath.Join(dst, "keep.txt")); err != nil { + t.Fatalf("expected kept file to be copied, got error: %v", err) + } +}