// Package symwalk provides an implementation of symbolic link aware filepath walk. // // filepath.Walk does not follow symbolic links. // symwalk.Walk calls filepath.Walk by providing it with a special WalkFn called symWalkFunc. package symwalk import ( "os" "path/filepath" ) // symwalkFunc calls the provided WalkFn for regular files. // However, when it encounters a symbolic link, it resolves the link fully using the // filepath.EvalSymlinks function and recursively calls symwalk.Walk on the resolved path. // This ensures that unlink filepath.Walk, traversal does not stop at symbolic links. // // Note that symwalk.Walk does not terminate if there are any non-terminating loops in // the file structure. func walk(filename string, linkDirname string, walkFn filepath.WalkFunc) error { symWalkFunc := func(path string, info os.FileInfo, err error) error { if fname, err := filepath.Rel(filename, path); err == nil { path = filepath.Join(linkDirname, fname) } else { return err } if err == nil && info.Mode()&os.ModeSymlink == os.ModeSymlink { finalPath, err := filepath.EvalSymlinks(path) if err != nil { return err } info, err := os.Lstat(finalPath) if err != nil { return walkFn(path, info, err) } if info.IsDir() { return walk(finalPath, path, walkFn) } } return walkFn(path, info, err) } return filepath.Walk(filename, symWalkFunc) } // Walk extends filepath.Walk to also follow symlinks func Walk(path string, walkFn filepath.WalkFunc) error { return walk(path, path, walkFn) }