Repository navigation
local: stop --copy-links following symlink loops - fixes #4402 - #9947
Merged
Merged
Conversation
With -L/--copy-links a symlink pointing to one of its parent directories was followed until the OS gave up with "too many levels of symbolic links" 40 levels deep. As every looping symlink multiplies the listing, a small tree with three such symlinks listed millions of entries. When a followed symlink points to a directory, compare it with the directory being listed and each of its parents using os.SameFile and if it matches report an error and skip it, the same way as a circular symlink. A symlink to a sibling directory is still followed. Loops excluded by the directory filters are skipped quietly as before.
ncw
approved these changes
Sep 21, 2026
ncw
left a comment
Member
There was a problem hiding this comment.
Thank you for fixing this @hsdfat
Thank you also for flagging the behaviour change for non-recursive listings. It also means rclone mount -L will no longer show a link -> .. entry that could previously be browsed lazily. I think that is the right trade-off, since find -L behaves the same way.
I'll merge now,
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this change do?
With
-L/--copy-links, a symlink pointing to one of its parent directories was followed until the OS gave up with "too many levels of symbolic links" about 40 levels deep. Every looping symlink multiplies the listing, so the tree from the issue (two directories, two files, three symlinks) listed about 6 million entries in a minute and was still going.This implements the algorithm suggested in the issue: when a followed symlink points to a directory, compare it with the directory being listed and each of its parents using
os.SameFile(device and inode). A match is a loop, and it is reported and skipped through the existing circular-symlink path (a no-retry error, counted, entry skipped). A symlink to a sibling directory is still followed as before.Listcall, so ordinary listings do no extra work.--excludea looping link see no new error.One behaviour change to be aware of: a non-recursive listing (
rclone lsf -L dir,--max-depth 1) of a directory containing a symlink to its parent used to show that entry and exit 0; it now reports the loop and exits with an error, asfind -Ldoes. Recursive commands already ended with ELOOP errors on these trees.Testing
TestCopyLinksLoopbuilds the tree from the issue and lists it withwalk.ListRfor three cases: from the root (sibling links followed once, 4 loops reported), with the remote rooted below a looping parent, and with the loop excluded by a filter (skipped without an error). It stops after 100 entries so it fails fast rather than hanging on master, where all three cases fail. A fourth case callsListon the directory directly and checks the non-recursive behaviour described above: the link to the parent is skipped with one error and the sibling link is still returned.rclone lsf -R --copy-linksfinishes in 0.06s with 8 entries and 4 loop errors, the same loopsfind -Lreports.go build ./...,make quicktest,go test -race ./backend/local/,golangci-lint run ./backend/local/(also withGOOS=windows), andgo vetfor windows/darwin pass.go run ./fstest/test_all -backends local:PASS: All tests finished OK.The check uses
os.SameFile, which works on Windows too; the test skips where symlinks can't be created, and I have not run it on Windows.Linked issue
Fixes #4402
Checklist
local.md.)test_allpasses for this backend and if submitting a new backend can provide a test account for the integration tester - see CONTRIBUTING.md.