diff --git a/internal/pkg/util/util.go b/internal/pkg/util/util.go index a47037e3..245c2adf 100644 --- a/internal/pkg/util/util.go +++ b/internal/pkg/util/util.go @@ -183,82 +183,100 @@ func FindFilterFiles( ignorePattern []string, ignore_xdev bool) (ofiles []string, err error) { wwlog.Debug("Finding files: %s include: %s ignore: %s", path, includePattern, ignorePattern) - // preprocess patterns to remove leading (and trailing) /, as we are handling relative paths + + // Preprocess patterns to remove leading (and trailing) /, as we are handling relative paths for i, pattern := range ignorePattern { ignorePattern[i] = strings.Trim(pattern, "/") } - cwd, err := os.Getwd() + + // Convert the base path to an absolute path + absPath, err := filepath.Abs(path) if err != nil { - return ofiles, err + return ofiles, fmt.Errorf("failed to resolve absolute path: %s: %w", path, err) } - defer func() { - err = FirstError(err, os.Chdir(cwd)) - }() - err = os.Chdir(path) - if err != nil { - return ofiles, fmt.Errorf("failed to change path: %s: %w", path, err) - } - // expand our include list as fspath.Match with /foo/* would catch /foo/baar but - // not /foo/baar/sibling + + // Expand the include list var globedInclude []string for _, include := range includePattern { - globed, err := filepath.Glob(include) + globed, err := filepath.Glob(filepath.Join(absPath, include)) if err != nil { - return ofiles, err + return ofiles, fmt.Errorf("failed to glob pattern %s: %w", include, err) } globedInclude = append(globedInclude, globed...) } + + var dev uint64 if ignore_xdev { wwlog.Debug("Ignoring cross-device (xdev) files") + pathStat, err := os.Stat(absPath) + if err != nil { + return ofiles, fmt.Errorf("failed to stat base path: %s: %w", absPath, err) + } + dev = pathStat.Sys().(*syscall.Stat_t).Dev } - path_stat, err := os.Stat(".") - if err != nil { - return ofiles, err - } - - dev := path_stat.Sys().(*syscall.Stat_t).Dev for _, inc := range globedInclude { - wwlog.Debug("inc %s", inc) + wwlog.Debug("Processing include pattern: %s", inc) stat, err := os.Lstat(inc) if err != nil { - return ofiles, err + return ofiles, fmt.Errorf("failed to stat include: %s: %w", inc, err) } + if stat.IsDir() { - // get the rest of dir + // Walk the directory err = filepath.WalkDir(inc, func(location string, info fs.DirEntry, err error) error { if err != nil { return err } - if location == "." { + + relPath, relErr := filepath.Rel(absPath, location) + if relErr != nil { + wwlog.Warn("Error computing relative path for %s: %v", location, relErr) + return relErr + } + + if relPath == "." { return nil } + fsInfo, err := info.Info() if err != nil { return err } + if ignore_xdev && fsInfo.Sys().(*syscall.Stat_t).Dev != dev { wwlog.Debug("Ignored (cross-device): %s", location) return nil } - for _, ignored_pat := range ignorePattern { - if ignored, _ := filepath.Match(ignored_pat, location); ignored { - wwlog.Debug("Ignored %s due to pattern %s", location, ignored_pat) - return filepath.SkipDir + + for _, ignoredPattern := range ignorePattern { + if ignored, _ := filepath.Match(ignoredPattern, relPath); ignored { + wwlog.Debug("Ignored %s due to pattern %s", relPath, ignoredPattern) + if info.IsDir() { + return filepath.SkipDir + } + return nil } } - ofiles = append(ofiles, location) + + ofiles = append(ofiles, relPath) return nil }) if err != nil { - return ofiles, err + return ofiles, fmt.Errorf("error walking directory %s: %w", inc, err) } } else { - ofiles = append(ofiles, inc) + // Add the file directly + relPath, relErr := filepath.Rel(absPath, inc) + if relErr != nil { + wwlog.Warn("Error computing relative path for %s: %v", inc, relErr) + return ofiles, relErr + } + ofiles = append(ofiles, relPath) } } - return ofiles, err + return ofiles, nil } // ****************************************************************************** diff --git a/internal/pkg/util/util_test.go b/internal/pkg/util/util_test.go index a8a4f4c1..b81710cc 100644 --- a/internal/pkg/util/util_test.go +++ b/internal/pkg/util/util_test.go @@ -1,25 +1,13 @@ package util import ( - "os" "path/filepath" - "reflect" - "sort" "testing" "github.com/stretchr/testify/assert" "github.com/warewulf/warewulf/internal/pkg/testenv" - "github.com/warewulf/warewulf/internal/pkg/wwlog" ) -func TryCreatePath(t *testing.T, elem ...string) { - err := os.MkdirAll(filepath.Join(elem...), os.ModePerm) - if err != nil { - t.Errorf("Failed creating dir: %v", err) - t.FailNow() - } -} - func Test_FindFiles(t *testing.T) { var tests = map[string]struct { createFiles []string @@ -55,34 +43,52 @@ func Test_FindFiles(t *testing.T) { } func Test_FindFilterFiles(t *testing.T) { - wwlog.SetLogLevel(wwlog.DEBUG) - dir, err := os.MkdirTemp(os.TempDir(), "warewulf-test") - if err != nil { - t.Errorf("Failed creating tmpdir: %v", err) - t.FailNow() - } - defer os.RemoveAll(dir) - TryCreatePath(t, dir, "boot") - TryCreatePath(t, dir, "usr", "local") - TryCreatePath(t, dir, "usr", "bin") - TryCreatePath(t, dir, "usr", "usr", "local") - TryCreatePath(t, dir, "bin") - TryCreatePath(t, dir, "lib") - - assert.NoError(t, os.Symlink("/path/to/target", filepath.Join(dir, "symlink"))) - - files, err := FindFilterFiles(dir, []string{"boot", "usr", "bin", "symlink"}, []string{"/b*/", "/usr/local"}, true) - - if err != nil { - t.Errorf("FindFilerFiles failed: %v", err) - t.FailNow() + var tests = map[string]struct { + createFiles []string + include []string + exclude []string + findFiles []string + }{ + "no files": { + createFiles: []string{}, + include: []string{"*"}, + findFiles: nil, + }, + "single file": { + createFiles: []string{"testfile"}, + include: []string{"*"}, + findFiles: []string{"testfile"}, + }, + "nested file": { + createFiles: []string{"testdir/testfile"}, + include: []string{"*"}, + findFiles: []string{"testdir", "testdir/testfile"}, + }, + "multiple files": { + createFiles: []string{"test1/testfile", "test2/testfile"}, + include: []string{"*"}, + findFiles: []string{"test1", "test1/testfile", "test2", "test2/testfile"}, + }, + "excluded files": { + createFiles: []string{"test1/test1", "test1/test2", "test2/test1", "test2/test2"}, + include: []string{"*"}, + exclude: []string{"test1/*2", "test2"}, + findFiles: []string{"test1", "test1/test1"}, + }, } - expected := []string{"usr", "usr/bin", "usr/usr", "usr/usr/local", "symlink"} - sort.Strings(expected) - sort.Strings(files) - if !reflect.DeepEqual(files, expected) { - t.Errorf("expected %v, got %v", expected, files) - t.FailNow() + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + env := testenv.New(t) + defer env.RemoveAll(t) + env.MkdirAll(t, "/test") + for _, file_ := range tt.createFiles { + env.CreateFile(t, filepath.Join("/test", file_)) + } + + files, err := FindFilterFiles(env.GetPath("/test"), tt.include, tt.exclude, true) + assert.NoError(t, err) + assert.Equal(t, tt.findFiles, files) + }) } }