Skip to content

Commit

Permalink
fix: exclude files in copy_to_directory before checking their realpath (
Browse files Browse the repository at this point in the history
  • Loading branch information
gregmagolan authored May 28, 2024
1 parent f30debb commit 7a8f00c
Showing 1 changed file with 61 additions and 68 deletions.
129 changes: 61 additions & 68 deletions tools/copy_to_directory/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ type fileInfo struct {
WorkspacePath string `json:"workspace_path"`
Hardlink bool `json:"hardlink"`

Realpath string
FileInfo fs.FileInfo
}

Expand Down Expand Up @@ -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
Expand All @@ -157,64 +153,53 @@ func (w *walker) copyDir(cfg *config, srcPaths pathSet, file fileInfo) error {
return err
}

f := fileInfo{
Package: file.Package,
Path: p,
RootPath: file.RootPath,
ShortPath: path.Join(file.ShortPath, r),
Workspace: file.Workspace,
WorkspacePath: path.Join(file.WorkspacePath, r),
Hardlink: file.Hardlink,
FileInfo: info,
}

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 {
// symlink to directories are intentionally never followed by filepath.Walk to avoid infinite recursion
linkPath, err := common.Realpath(p)
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[linkPath] {
if srcPaths[realpath] {
// recursive symlink; silently ignore
return nil
}
stat, err := os.Stat(linkPath)
stat, err := os.Stat(realpath)
if err != nil {
return fmt.Errorf("failed to stat file %s pointed to by symlink %s: %w", linkPath, p, err)
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
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,
RootPath: file.RootPath,
ShortPath: path.Join(file.ShortPath, r),
Workspace: file.Workspace,
WorkspacePath: path.Join(file.WorkspacePath, r),
Hardlink: file.Hardlink,
FileInfo: info,
}
return w.copyPath(cfg, f)
return w.copyPath(cfg, f, outputPath)
})
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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) {
Expand All @@ -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() {
if err := w.copyDir(cfg, nil, file); err != nil {
return err
}
} else {
if err := w.copyPath(cfg, file); err != nil {
return err
if file.FileInfo.IsDir() {
// symlink points to a directory
if err := w.copyDir(cfg, nil, file); err != nil {
return err
}
continue
}
}

if err := w.copyPath(cfg, file, outputPath); err != nil {
return err
}
}
return nil
}
Expand Down

0 comments on commit 7a8f00c

Please sign in to comment.