recursiveSymlinks returns all of the paths in the chain of symlinks starting at `path`. If `path` isn't a symlink, only `path` is returned. If at any point the symlink chain goes outside of `mountpoint` then nil is returned. Despite its name it's interestingly enough not implemented recursively.
(afs FS, mountpoint string, path string)
| 159 | // point the symlink chain goes outside of `mountpoint` then nil is returned. |
| 160 | // Despite its name it's interestingly enough not implemented recursively. |
| 161 | func recursiveSymlinks(afs FS, mountpoint string, path string) ([]string, error) { |
| 162 | if !strings.HasSuffix(mountpoint, "/") { |
| 163 | mountpoint += "/" |
| 164 | } |
| 165 | |
| 166 | paths := []string{} |
| 167 | for { |
| 168 | if !strings.HasPrefix(path, mountpoint) { |
| 169 | return nil, nil |
| 170 | } |
| 171 | |
| 172 | stat, err := afs.LStat(path) |
| 173 | if err != nil { |
| 174 | return nil, xerrors.Errorf("lstat %q: %w", path, err) |
| 175 | } |
| 176 | |
| 177 | paths = append(paths, path) |
| 178 | if stat.Mode()&os.ModeSymlink == 0 { |
| 179 | break |
| 180 | } |
| 181 | |
| 182 | newPath, err := afs.Readlink(path) |
| 183 | if err != nil { |
| 184 | return nil, xerrors.Errorf("readlink %q: %w", path, err) |
| 185 | } |
| 186 | if newPath == "" { |
| 187 | break |
| 188 | } |
| 189 | |
| 190 | if filepath.IsAbs(newPath) { |
| 191 | path = newPath |
| 192 | } else { |
| 193 | dir := filepath.Dir(path) |
| 194 | path = filepath.Join(dir, newPath) |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | return paths, nil |
| 199 | } |
| 200 | |
| 201 | // SameDirSymlinks returns all links in the same directory as `target` that |
| 202 | // point to target, either indirectly or directly. Only symlinks in the same |
no test coverage detected