Skip to content

cp: error when trying to preserve metadata on dangling symbolic link #3531

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
jfinkels opened this issue May 15, 2022 · 2 comments · Fixed by #3692
Closed

cp: error when trying to preserve metadata on dangling symbolic link #3531

jfinkels opened this issue May 15, 2022 · 2 comments · Fixed by #3692
Labels

Comments

@jfinkels
Copy link
Collaborator

Combining the -P (--no-dereference) option and the -p (--preserve=mode,ownership,timestamps) option in uutils cp does not match the behavior of GNU cp when attempting to copy a symbolic link that points to a file that does not exist. (Contrast this with issue #3364, which was just about the -P option. That issue was resolved.)

GNU cp:

$ ln -s no-such-file dangle && cp -Pp dangle d2
# no output, d2 is created

uutils cp:

$ ln -s no-such-file dangle && ./target/release/cp -Pp dangle d2
./target/release/cp: 'dangle' -> '/home/jeffrey/src/coreutils/d2': No such file or directory (os error 2)
@jfinkels
Copy link
Collaborator Author

Here's a failing test case:

#[test]
fn test_copy_through_dangling_symlink_no_dereference_permissions() {
    let (at, mut ucmd) = at_and_ucmd!();
    at.symlink_file("no-such-file", "dangle");
    ucmd.args(&["-P", "-p", "dangle", "d2"]).succeeds().no_stderr().no_stdout();
    at.file_exists("d2");

    // `-p` means `--preserve=mode,ownership,timestamps`                                                                                                                                                   
    #[cfg(unix)]
    {
        let metadata1 = at.symlink_metadata("dangle");
        let metadata2 = at.symlink_metadata("d2");
        assert_eq!(metadata1.mode(), metadata2.mode());
        assert_eq!(metadata1.uid(), metadata2.uid());
        assert_eq!(metadata1.atime(), metadata2.atime());
        assert_eq!(metadata1.mtime(), metadata2.mtime());
        assert_eq!(metadata1.ctime(), metadata2.ctime());
    }
}

The error seems to be occurring at this line:

fs::set_permissions(dest, source_metadata.permissions()).context(context)?;
(the set_permissions() call returns an Err).

@jfinkels
Copy link
Collaborator Author

jfinkels commented Jul 3, 2022

I think this may have to do with this: rust-lang/rust#75942:

The documentation for std::fs::set_permissions doesn't say what it does if the given path names a symlink.

Experimentally, it seems that on Unix-family platforms it follows symlinks, while on Windows it does not.

Maybe the call to set_permissions() is attempting to follow the link (named "dangle" in this case) to a file that does not exist (named "no-such-file"), resulting in the "No such file" error. If that is the case, then the solution should be to use a version of set_permissions() that does not follow links.

jfinkels added a commit to jfinkels/coreutils that referenced this issue Jul 3, 2022
Fix a bug in which `cp` incorrectly exited with an error when
attempting to copy the attributes of a dangling symbolic link (that
is, when running `cp -P -p`).

Fixes uutils#3531.
jfinkels added a commit to jfinkels/coreutils that referenced this issue Jul 3, 2022
Fix a bug in which `cp` incorrectly exited with an error when
attempting to copy the attributes of a dangling symbolic link (that
is, when running `cp -P -p`).

Fixes uutils#3531.
@sylvestre sylvestre moved this to Done in GNU Compatibility Aug 18, 2022
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

1 participant