@@ -202,57 +202,64 @@ func recursiveSymlinks(afs FS, mountpoint string, path string) ([]string, error)
202
202
// point to target, either indirectly or directly. Only symlinks in the same
203
203
// directory as `target` are considered.
204
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.
205
232
var (
206
- found = make ([]string , 0 )
207
- maxIterations = 10 // arbitrary upper limit to prevent infinite loops
233
+ found = make ([]string , 0 )
234
+ // Set an arbitrary upper limit to prevent infinite loops.
235
+ maxIterations = 10
208
236
)
209
237
for range maxIterations {
210
- foundThisTime := false
211
- fis , err := afero .ReadDir (afs , filepath .Dir (target ))
212
- if err != nil {
213
- return nil , xerrors .Errorf ("read dir %q: %w" , filepath .Dir (target ), err )
214
- }
215
- for _ , fi := range fis {
216
- // Ignore the target itself.
217
- if fi .Name () == filepath .Base (target ) {
218
- continue
219
- }
220
- // Ignore non-symlinks.
221
- if fi .Mode ()& os .ModeSymlink == 0 {
222
- continue
223
- }
224
- // Get the target of the symlink.
225
- link , err := afs .Readlink (filepath .Join (filepath .Dir (target ), fi .Name ()))
226
- if err != nil {
227
- return nil , xerrors .Errorf ("readlink %q: %w" , fi .Name (), err )
228
- }
229
- // Make the link absolute.
230
- if ! filepath .IsAbs (link ) {
231
- link = filepath .Join (filepath .Dir (target ), link )
232
- }
238
+ var foundThisTime bool
239
+ for linkName , linkDest := range allLinks {
233
240
// Ignore symlinks that point outside of target's directory.
234
- if filepath .Dir (link ) != filepath .Dir (target ) {
241
+ if filepath .Dir (linkName ) != filepath .Dir (target ) {
235
242
continue
236
243
}
237
244
238
- // Check if the symlink points to to the target, or if it points
239
- // to one of the symlinks we've already found.
240
- if link != target {
241
- if ! slices . Contains (found , link ) {
242
- continue
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
243
250
}
244
251
}
245
252
246
- // Have we already seen this target?
247
- fullPath := filepath .Join (filepath .Dir (target ), fi .Name ())
248
- if slices .Contains (found , fullPath ) {
249
- continue
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
+ }
250
260
}
251
-
252
- found = append (found , filepath .Join (filepath .Dir (target ), fi .Name ()))
253
- foundThisTime = true
254
261
}
255
- // If we didn't find any symlinks this time , we're done.
262
+ // If we didn't find any new symlinks , we're done.
256
263
if ! foundThisTime {
257
264
break
258
265
}
0 commit comments