fix: exclude files in copy_to_directory before checking their realpath (#857)

This commit is contained in:
Greg Magolan 2024-05-28 11:04:25 -07:00 committed by GitHub
parent f30debb505
commit 7a8f00cd7f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 69 additions and 76 deletions

View File

@ -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,21 +361,21 @@ 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
}
} else {
if err := w.copyPath(cfg, file); err != nil {
return err
continue
}
}
if err := w.copyPath(cfg, file, outputPath); err != nil {
return err
}
}
return nil
}