uutils / coreutils

Cross-platform Rust rewrite of the GNU coreutils
https://uutils.github.io/
MIT License
17.61k stars 1.27k forks source link

cp: when doing hardlink copy of symlink, should replace a technically-different symlink #6531

Open BenWiederhake opened 3 months ago

BenWiederhake commented 3 months ago

Let's say we have a regular file regularfile, and two symlinks to it, symlink and hop. Then we have an additional symlink indirect_symlink which points to hop. For most intents and purposes, indirect_symlink points to regularfile, but technically it doesn't, and this difference is visible in ls and would also change completely if hop ever points somewhere else or becomes a (different) regular file.

Now cp should replace indirect_symlink by a hardlink-copy of symlink, but our current implementation says "Eh, everything is fine!" and does nothing, which is just wrong. The -v in the example below is just for illustration.

$ ln -s README.md symlink
$ ln -s README.md hop
$ rm -f indirect_symlink && ln -s hop indirect_symlink && ls -1i README.md symlink hop indirect_symlink && cp -vlP symlink indirect_symlink && ls -1i README.md symlink hop indirect_symlink
20092600 README.md
20074249 hop
20074347 indirect_symlink
20074037 symlink
removed 'indirect_symlink'
'symlink' -> 'indirect_symlink'
20092600 README.md
20074249 hop
20074037 indirect_symlink
20074037 symlink
$ readlink indirect_symlink  # Points directly to README.md
README.md
$ rm -f indirect_symlink && ln -s hop indirect_symlink && ls -1i README.md symlink hop indirect_symlink && cargo run -q cp -vlP symlink indirect_symlink && ls -1i README.md symlink hop indirect_symlink
20092600 README.md
20074249 hop
20074347 indirect_symlink
20074037 symlink
20092600 README.md
20074249 hop
20074347 indirect_symlink
20074037 symlink
$ readlink indirect_symlink  # It didn't even get rewritten!
hop

Found while reading https://github.com/uutils/coreutils/pull/6496, but only remotely related.