From 7a8f00cd7fc81024e26bf41cea4132ac9e080739 Mon Sep 17 00:00:00 2001 From: Greg Magolan Date: Tue, 28 May 2024 11:04:25 -0700 Subject: [PATCH] fix: exclude files in copy_to_directory before checking their realpath (#857) --- tools/copy_to_directory/main.go | 145 +++++++++++++++----------------- 1 file changed, 69 insertions(+), 76 deletions(-) diff --git a/tools/copy_to_directory/main.go b/tools/copy_to_directory/main.go index aadf748..dba6bda 100644 --- a/tools/copy_to_directory/main.go +++ b/tools/copy_to_directory/main.go @@ -26,7 +26,6 @@ type fileInfo struct { WorkspacePath string `json:"workspace_path"` Hardlink bool `json:"hardlink"` - Realpath string FileInfo fs.FileInfo } @@ -131,9 +130,6 @@ func (w *walker) copyDir(cfg *config, srcPaths pathSet, file fileInfo) error { // filepath.WalkDir walks the file tree rooted at root, calling fn for each file or directory in // the tree, including root. See https://pkg.go.dev/path/filepath#WalkDir for more info. walkPath := file.Path - if file.Realpath != "" { - walkPath = file.Realpath - } return filepath.WalkDir(walkPath, func(p string, dirEntry fs.DirEntry, err error) error { if err != nil { return err @@ -157,53 +153,6 @@ func (w *walker) copyDir(cfg *config, srcPaths pathSet, file fileInfo) error { return err } - if info.Mode()&os.ModeSymlink == os.ModeSymlink { - // symlink to directories are intentionally never followed by filepath.Walk to avoid infinite recursion - linkPath, err := common.Realpath(p) - if err != nil { - if os.IsNotExist(err) { - return fmt.Errorf("failed to get realpath of dangling symlink %s: %w", p, err) - } - return fmt.Errorf("failed to get realpath of %s: %w", p, err) - } - if srcPaths[linkPath] { - // recursive symlink; silently ignore - return nil - } - stat, err := os.Stat(linkPath) - if err != nil { - return fmt.Errorf("failed to stat file %s pointed to by symlink %s: %w", linkPath, p, err) - } - if stat.IsDir() { - // symlink points to a directory - f := fileInfo{ - Package: file.Package, - Path: linkPath, - RootPath: file.RootPath, - ShortPath: file.ShortPath, - Workspace: file.Workspace, - WorkspacePath: file.WorkspacePath, - Hardlink: file.Hardlink, - FileInfo: stat, - } - return w.copyDir(cfg, srcPaths, f) - } else { - // symlink points to a regular file - f := fileInfo{ - Package: file.Package, - Path: linkPath, - RootPath: file.RootPath, - ShortPath: path.Join(file.ShortPath, r), - Workspace: file.Workspace, - WorkspacePath: path.Join(file.WorkspacePath, r), - Hardlink: file.Hardlink, - FileInfo: stat, - } - return w.copyPath(cfg, f) - } - } - - // a regular file f := fileInfo{ Package: file.Package, Path: p, @@ -214,7 +163,43 @@ func (w *walker) copyDir(cfg *config, srcPaths pathSet, file fileInfo) error { Hardlink: file.Hardlink, FileInfo: info, } - return w.copyPath(cfg, f) + + outputPath, err := w.calculateOutputPath(cfg, f) + if err != nil { + return fmt.Errorf("failed to calculate output path for %s: %w", file.WorkspacePath, err) + } + if outputPath == "" { + // this path is excluded + return nil + } + + // if file is a symlink, resolve its realpath + if info.Mode()&os.ModeSymlink == os.ModeSymlink { + realpath, err := common.Realpath(p) + if err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("failed to get realpath of dangling symlink %s: %w", p, err) + } + return fmt.Errorf("failed to get realpath of %s: %w", p, err) + } + if srcPaths[realpath] { + // recursive symlink; silently ignore + return nil + } + stat, err := os.Stat(realpath) + if err != nil { + return fmt.Errorf("failed to stat file %s pointed to by symlink %s: %w", realpath, p, err) + } + f.Path = realpath + f.FileInfo = stat + + if stat.IsDir() { + // symlink points to a directory + return w.copyDir(cfg, srcPaths, f) + } + } + + return w.copyPath(cfg, f, outputPath) }) } @@ -305,16 +290,7 @@ func (w *walker) calculateOutputPath(cfg *config, file fileInfo) (string, error) return path.Join(cfg.Dst, outputPath), nil } -func (w *walker) copyPath(cfg *config, file fileInfo) error { - outputPath, err := w.calculateOutputPath(cfg, file) - if err != nil { - return fmt.Errorf("failed to calculate output path %s: %w", file.WorkspacePath, err) - } - if outputPath == "" { - // this path is excluded - return nil - } - +func (w *walker) copyPath(cfg *config, file fileInfo, outputPath string) error { // add this file to the copy Paths dup, exists := copySet[outputPath] if exists { @@ -329,7 +305,7 @@ func (w *walker) copyPath(cfg *config, file fileInfo) error { outputDir := path.Dir(outputPath) if !mkdirSet[outputDir] { - if err = os.MkdirAll(outputDir, os.ModePerm); err != nil { + if err := os.MkdirAll(outputDir, os.ModePerm); err != nil { return err } // https://pkg.go.dev/path#Dir @@ -353,10 +329,27 @@ func (w *walker) copyPaths(cfg *config) error { if err != nil { return fmt.Errorf("failed to lstat file %s: %w", file.Path, err) } + file.FileInfo = info + // if file is a directory, then short-circuit without calculating the output path + if file.FileInfo.IsDir() { + if err := w.copyDir(cfg, nil, file); err != nil { + return err + } + continue + } + + outputPath, err := w.calculateOutputPath(cfg, file) + if err != nil { + return fmt.Errorf("failed to calculate output path for %s: %w", file.WorkspacePath, err) + } + if outputPath == "" { + // this path is excluded + continue + } + + // if file is a symlink, resolve its realpath if info.Mode()&os.ModeSymlink == os.ModeSymlink { - // On Windows, filepath.WalkDir doesn't like directory symlinks so we must - // call filepath.WalkDir on the realpath realpath, err := common.Realpath(file.Path) if err != nil { if os.IsNotExist(err) { @@ -368,20 +361,20 @@ func (w *walker) copyPaths(cfg *config) error { if err != nil { return fmt.Errorf("failed to stat file %s pointed to by symlink %s: %w", realpath, file.Path, err) } - file.Realpath = realpath + file.Path = realpath file.FileInfo = stat - } else { - file.FileInfo = info + + if file.FileInfo.IsDir() { + // symlink points to a directory + if err := w.copyDir(cfg, nil, file); err != nil { + return err + } + continue + } } - if file.FileInfo.IsDir() { - if err := w.copyDir(cfg, nil, file); err != nil { - return err - } - } else { - if err := w.copyPath(cfg, file); err != nil { - return err - } + if err := w.copyPath(cfg, file, outputPath); err != nil { + return err } } return nil