SameDirSymlinks returns all links in the same directory as `target` that point to target, either indirectly or directly. Only symlinks in the same directory as `target` are considered.
(afs FS, target string)
| 202 | // point to target, either indirectly or directly. Only symlinks in the same |
| 203 | // directory as `target` are considered. |
| 204 | func SameDirSymlinks(afs FS, target string) ([]string, error) { |
| 205 | // Get the list of files in the directory of the target. |
| 206 | fis, err := afero.ReadDir(afs, filepath.Dir(target)) |
| 207 | if err != nil { |
| 208 | return nil, xerrors.Errorf("read dir %q: %w", filepath.Dir(target), err) |
| 209 | } |
| 210 | |
| 211 | // Do an initial pass to map all symlinks to their destinations. |
| 212 | allLinks := make(map[string]string) |
| 213 | for _, fi := range fis { |
| 214 | // Ignore non-symlinks. |
| 215 | if fi.Mode()&os.ModeSymlink == 0 { |
| 216 | continue |
| 217 | } |
| 218 | |
| 219 | absPath := filepath.Join(filepath.Dir(target), fi.Name()) |
| 220 | link, err := afs.Readlink(filepath.Join(filepath.Dir(target), fi.Name())) |
| 221 | if err != nil { |
| 222 | return nil, xerrors.Errorf("readlink %q: %w", fi.Name(), err) |
| 223 | } |
| 224 | |
| 225 | if !filepath.IsAbs(link) { |
| 226 | link = filepath.Join(filepath.Dir(target), link) |
| 227 | } |
| 228 | allLinks[absPath] = link |
| 229 | } |
| 230 | |
| 231 | // Now we can start checking for symlinks that point to the target. |
| 232 | var ( |
| 233 | found = make([]string, 0) |
| 234 | // Set an arbitrary upper limit to prevent infinite loops. |
| 235 | maxIterations = 10 |
| 236 | ) |
| 237 | for range maxIterations { |
| 238 | var foundThisTime bool |
| 239 | for linkName, linkDest := range allLinks { |
| 240 | // Ignore symlinks that point outside of target's directory. |
| 241 | if filepath.Dir(linkName) != filepath.Dir(target) { |
| 242 | continue |
| 243 | } |
| 244 | |
| 245 | // If the symlink points to the target, add it to the list. |
| 246 | if linkDest == target { |
| 247 | if !slices.Contains(found, linkName) { |
| 248 | found = append(found, linkName) |
| 249 | foundThisTime = true |
| 250 | } |
| 251 | } |
| 252 | |
| 253 | // If the symlink points to another symlink that we already determined |
| 254 | // points to the target, add it to the list. |
| 255 | if slices.Contains(found, linkDest) { |
| 256 | if !slices.Contains(found, linkName) { |
| 257 | found = append(found, linkName) |
| 258 | foundThisTime = true |
| 259 | } |
| 260 | } |
| 261 | } |