// walk recursively descends path, calling walkFn. Copied, with some
// modifications from path/filepath.walk.
-func walk(path string, info fs.FileInfo, walkFn filepath.WalkFunc) error {
+// Walk follows the root if it's a symlink, but reports the original paths,
+// so it calls walk with both the resolvedPath (which is the path with the root resolved)
+// and path (which is the path reported to the walkFn).
+func walk(path, resolvedPath string, info fs.FileInfo, walkFn filepath.WalkFunc) error {
if err := walkFn(path, info, nil); err != nil || !info.IsDir() {
return err
}
- fis, err := ReadDir(path)
+ fis, err := ReadDir(resolvedPath)
if err != nil {
return walkFn(path, info, err)
}
for _, fi := range fis {
filename := filepath.Join(path, fi.Name())
- if err := walk(filename, fi, walkFn); err != nil {
+ resolvedFilename := filepath.Join(resolvedPath, fi.Name())
+ if err := walk(filename, resolvedFilename, fi, walkFn); err != nil {
if !fi.IsDir() || err != filepath.SkipDir {
return err
}
if err != nil {
err = walkFn(root, nil, err)
} else {
- err = walk(root, info, walkFn)
+ resolved := root
+ if info.Mode()&os.ModeSymlink != 0 {
+ // Walk follows root if it's a symlink (but does not follow other symlinks).
+ if op, ok := OverlayPath(root); ok {
+ resolved = op
+ }
+ resolved, err = os.Readlink(resolved)
+ if err != nil {
+ return err
+ }
+ // Re-stat to get the info for the resolved file.
+ info, err = Lstat(resolved)
+ if err != nil {
+ return err
+ }
+ }
+ err = walk(root, resolved, info, walkFn)
}
if err == filepath.SkipDir {
return nil
{"control", "dir", []string{"dir", "dir" + string(filepath.Separator) + "file"}},
// ensure Walk doesn't walk into the directory pointed to by the symlink
// (because it's supposed to use Lstat instead of Stat).
- {"symlink_to_dir", "symlink", []string{"symlink"}},
- {"overlay_to_symlink_to_dir", "overlay_symlink", []string{"overlay_symlink"}},
+ {"symlink_to_dir", "symlink", []string{"symlink", "symlink" + string(filepath.Separator) + "file"}},
+ {"overlay_to_symlink_to_dir", "overlay_symlink", []string{"overlay_symlink", "overlay_symlink" + string(filepath.Separator) + "file"}},
}
for _, tc := range testCases {