-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
cp: FIxed permission race when creating files #10204
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
base: main
Are you sure you want to change the base?
Conversation
|
GNU testsuite comparison: |
|
I realise this is rather untenable right now, so I have dug up some logs to show the change in action, here is gnu openat(AT_FDCWD, "/tmp/b.txt", O_RDONLY|O_PATH|O_DIRECTORY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/tmp/a.txt", O_RDONLY) = 3
openat(AT_FDCWD, "/tmp/b.txt", O_WRONLY|O_CREAT|O_EXCL, 0644) = 8
+++ exited with 0 +++and when openat(AT_FDCWD, "/tmp/b.txt", O_RDONLY|O_PATH|O_DIRECTORY) = -1 ENOTDIR (Not a directory)
openat(AT_FDCWD, "/tmp/a.txt", O_RDONLY) = 3
openat(AT_FDCWD, "/tmp/b.txt", O_WRONLY|O_TRUNC) = 8
+++ exited with 0 +++Here is the implementations strace penat(AT_FDCWD, "/tmp/a.txt", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/tmp/a.txt", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/tmp/a.txt", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/tmp/b.txt", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0600) = 8
openat(AT_FDCWD, "/tmp/a.txt", O_RDONLY|O_CLOEXEC) = 9
openat(AT_FDCWD, "/tmp/b.txt", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0100644) = 12
fchmod(12, 0100644) = 0
chmod("/tmp/b.txt", 0644) = 0
+++ exited with 0 +++Note that the first 3 reads of openat(AT_FDCWD, "/tmp/b.txt", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0600) = 8instead of openat(AT_FDCWD, "/tmp/b.txt", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 8on main. Now when we already have openat(AT_FDCWD, "/tmp/a.txt", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/tmp/a.txt", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/tmp/a.txt", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/tmp/b.txt", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 8
openat(AT_FDCWD, "/tmp/a.txt", O_RDONLY|O_CLOEXEC) = 9
openat(AT_FDCWD, "/tmp/b.txt", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0100644) = 12
fchmod(12, 0100644) = 0
chmod("/tmp/b.txt", 0100644) = 0
+++ exited with 0 +++which opens openat(AT_FDCWD, "/tmp/a.txt", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/tmp/a.txt", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/tmp/a.txt", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/tmp/b.txt", O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0600) = -1 EEXIST (File exists)
openat(AT_FDCWD, "/tmp/b.txt", O_WRONLY|O_TRUNC|O_CLOEXEC) = 8
fchmod(8, 0600) = 0
openat(AT_FDCWD, "/tmp/a.txt", O_RDONLY|O_CLOEXEC) = 9
openat(AT_FDCWD, "/tmp/b.txt", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0100644) = 12
fchmod(12, 0100644) = 0
chmod("/tmp/b.txt", 0100644) = 0
+++ exited with 0 +++By changing the files permissions we ensure that the file cannot be read while data may be being copied in. This permission is later loosened to the correct permission. @sylvestre I hope that this makes it less hand-wavy :) |
| } else { | ||
| // If a symlink exists in the position we want to write the file to, and it symlinks to | ||
| // a nonexistent file, we should just overwrite the symlink | ||
| let nonexistent_file = std::fs::read_link(&dest)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dangling symlink handling is incorrect. This creates a file at the symlink target instead of replacing the symlink itself, causing test failures. Should remove the symlink first with std::fs::remove_file(&dest)? before creating new file at &dest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree, if we look at test_copy_through_dangling_symlink_posixly_correct:
#[test]
fn test_copy_through_dangling_symlink_posixly_correct() {
let (at, mut ucmd) = at_and_ucmd!();
at.touch("file");
at.write("file", "content");
at.symlink_file("nonexistent", "target");
ucmd.arg("file")
.arg("target")
.env("POSIXLY_CORRECT", "1")
.succeeds();
assert!(at.file_exists("nonexistent"));
let contents = at.read("nonexistent");
assert_eq!(contents, "content");
}we see that the file should be put at the symlink.
|
and it needs tests :) |
Specific tests are at |
CodSpeed Performance ReportMerging this PR will degrade performance by 16.32%Comparing Summary
Performance Changes
Footnotes
|
|
GNU testsuite comparison: |
|
GNU testsuite comparison: |
Fixes #10011.