-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Fix cargo add overwriting symlinked Cargo.toml files #15281
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
Fix cargo add overwriting symlinked Cargo.toml files #15281
Conversation
r? @weihanglo rustbot has assigned @weihanglo. Use |
a45d668
to
53fdb96
Compare
465de3c
to
1708e12
Compare
1708e12
to
4f2006f
Compare
src/cargo/util/toml_mut/manifest.rs
Outdated
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.
This commit does not change the test. Does the previous commit fail cargo test
or is it testing the wrong thing? Each commit should be atomic which includes passing tests. When we ask for a test commit to be split out first, it is to show the current behavior. The commit with the fix will also change the test and that diff shows how behavior changes.
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.
Note that this is still not addressed.
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.
From #15281 (comment)
Hi @epage, I implemented symlink preservation in write_atomic().
The test now shows how cargo add handles symlinks correctly. Let me know what you think.
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.
So the test fails for the first commit and then passes for the second commit? In that case, these commits are not atomic, as in they do not stand on their own with every commit passing tests.
We ask for the splitting of test/fix commits so that the second commit shows how the behavior changes by how the test body changes to ensure it still passes. This is a clear way of communicating the way the behavior changed.
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.
@RaghavenderSingh would you mind doing what epage suggested?
b0850ce
to
26bd6b5
Compare
@RaghavenderSingh you requested a review but it doesn't look like my review comments have been addressed. |
1d94106
to
bef4aa7
Compare
bef4aa7
to
976b828
Compare
976b828
to
4896a35
Compare
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.
This may need a rebase onto master to pass CI.
crates/cargo-util/src/paths.rs
Outdated
let actual_path = if path.is_symlink() { | ||
std::fs::read_link(path)? | ||
} else { | ||
path.to_path_buf() | ||
}; |
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.
To avoid unnecessary allocations. And we can continue call it path
so we don't need to change other lines.
(It is good that shadowing is allowed in Rust)
let actual_path = if path.is_symlink() { | |
std::fs::read_link(path)? | |
} else { | |
path.to_path_buf() | |
}; | |
let path = if path.is_symlink() { | |
&std::fs::read_link(path)? | |
} else { | |
path | |
}; |
src/cargo/util/toml_mut/manifest.rs
Outdated
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.
@RaghavenderSingh would you mind doing what epage suggested?
4896a35
to
bffece8
Compare
tests/testsuite/cargo_add/mod.rs
Outdated
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.
This test demonstrates that cargo add replaces symlinked Cargo.toml
files with regular files, breaking the symlink. The test currently
fails as expected.
FWIW, as previously noted, we prefer to the test pass in every commit.
- This commit adding the test can be seen as a minimal reproducible example of the bugt.
- When the test passes in the first commit, it captures the current problematic behavior. So, when the next real "fix" commit changes the behavior, the behavior change can show up in git diff.
- This also helps if we want to bisect other unrelated things, so that the test suite won't be in a half-broken state
crates/cargo-util/src/paths.rs
Outdated
let mode = meta.permissions().mode() & mask; | ||
|
||
std::fs::Permissions::from_mode(mode) | ||
const PERMS_MASK: u32 = 0o777; |
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.
This is unrelated, I guess? Could we keep it the old way?
crates/cargo-util/src/paths.rs
Outdated
@@ -213,7 +222,7 @@ pub fn write_atomic<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Res | |||
tmp.write_all(contents.as_ref())?; | |||
|
|||
// On unix platforms, set the permissions on the newly created file. We can use fchmod (called | |||
// by the std lib; subject to change) which ignores the umask so that the new file has the same | |||
// by the std lib method below) which ignores the umask so that the new file has the same |
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.
What do we change this? It is indeed subject to change
I know the old wording here might make less sense, but we should keep the focus on the issue, not other fly-by stuffs.
This test shows the current behavior where cargo add replaces symlinked Cargo.toml files with regular files. The test passes, documenting this problematic behavior.
88e7f30
to
5d07fe5
Compare
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.
Could you squah the last two commits into the second commit? They are unnecessary.
- Preserve symlinks when writing files atomically in write_atomic() - Update test to verify correct symlink preservation behavior - Apply rustfmt formatting This fixes the issue where cargo add would replace symlinked Cargo.toml files with regular files, breaking the symlink to the original target. Fixes rust-lang#15241
e62156f
to
ecfe3a9
Compare
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.
Thanks for your contribution!
Update cargo 5 commits in 64a12460708cf146e16cc61f28aba5dc2463bbb4..f6bebc3abbd9b273a457f77c31f8e93bc77f93fc 2025-05-30 18:25:08 +0000 to 2025-06-02 19:04:43 +0000 - fix(trim-paths): remap all paths to `build.build-dir` (rust-lang/cargo#15614) - test(trim-paths): enable more tests for windows-msvc (rust-lang/cargo#15621) - fix(fingerprint): explicit reason rather than "stale; unknown reason" (rust-lang/cargo#15617) - Fix cargo add overwriting symlinked Cargo.toml files (rust-lang/cargo#15281) - chore(deps): update alpine docker tag to v3.22 (rust-lang/cargo#15616) r? ghost
What does this PR try to resolve?
This PR fixes a bug where
cargo add
breaks symlinks to Cargo.toml files. Currently, when Cargo.toml is a symlink andcargo add
is used to add a dependency, the symlink is replaced with a regular file, breaking the link to the original target file.This issue was reported in #15241 where a user who relies on symlinked Cargo.toml files found that
cargo add
breaks their workflow.Fixes #15241
How should we test and review this PR?
I've modified
LocalManifest::write()
to check if the path is a symlink, and if so, follow it to get the actual target path. This ensures we write to the actual file rather than replacing the symlink.I've also added a test in
tests/testsuite/cargo_add/symlink.rs
that:cargo add
to add a dependencyI've manually tested this fix and confirmed it works correctly.