Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

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

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

1 participant