Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
May 28, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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