Skip to content

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

Merged
merged 2 commits into from
Jun 2, 2025

Conversation

RaghavenderSingh
Copy link
Contributor

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 and cargo 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:

  1. Creates a symlinked Cargo.toml file
  2. Runs cargo add to add a dependency
  3. Verifies the symlink is preserved and the dependency is added to the target file

I've manually tested this fix and confirmed it works correctly.

@rustbot
Copy link
Collaborator

rustbot commented Mar 8, 2025

r? @weihanglo

rustbot has assigned @weihanglo.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added A-manifest Area: Cargo.toml issues S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 8, 2025
@RaghavenderSingh RaghavenderSingh force-pushed the fix-cargo-add-symlinks branch from a45d668 to 53fdb96 Compare March 8, 2025 19:25
@RaghavenderSingh RaghavenderSingh force-pushed the fix-cargo-add-symlinks branch 3 times, most recently from 465de3c to 1708e12 Compare March 9, 2025 20:04
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor

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(). preview

The test now shows how cargo add handles symlinks correctly. Let me know what you think.

Copy link
Contributor

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.

Copy link
Member

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?

@RaghavenderSingh RaghavenderSingh force-pushed the fix-cargo-add-symlinks branch 2 times, most recently from b0850ce to 26bd6b5 Compare March 19, 2025 13:12
@RaghavenderSingh RaghavenderSingh requested a review from epage March 24, 2025 09:24
@epage
Copy link
Contributor

epage commented Mar 24, 2025

@RaghavenderSingh you requested a review but it doesn't look like my review comments have been addressed.

Copy link
Member

@weihanglo weihanglo left a 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.

Comment on lines 198 to 205
let actual_path = if path.is_symlink() {
std::fs::read_link(path)?
} else {
path.to_path_buf()
};
Copy link
Member

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)

Suggested change
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
};

Copy link
Member

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?

@rustbot rustbot added S-waiting-on-author Status: The marked PR is awaiting some action (such as code changes) from the PR author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 13, 2025
Copy link
Member

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

let mode = meta.permissions().mode() & mask;

std::fs::Permissions::from_mode(mode)
const PERMS_MASK: u32 = 0o777;
Copy link
Member

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?

@@ -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
Copy link
Member

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.
@RaghavenderSingh RaghavenderSingh force-pushed the fix-cargo-add-symlinks branch from 88e7f30 to 5d07fe5 Compare May 30, 2025 10:00
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: The marked PR is awaiting some action (such as code changes) from the PR author. labels May 30, 2025
Copy link
Member

@weihanglo weihanglo left a 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.

@rustbot rustbot added S-waiting-on-author Status: The marked PR is awaiting some action (such as code changes) from the PR author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 2, 2025
- 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
@RaghavenderSingh RaghavenderSingh force-pushed the fix-cargo-add-symlinks branch from e62156f to ecfe3a9 Compare June 2, 2025 05:00
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: The marked PR is awaiting some action (such as code changes) from the PR author. labels Jun 2, 2025
Copy link
Member

@weihanglo weihanglo left a 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!

@weihanglo weihanglo added this pull request to the merge queue Jun 2, 2025
Merged via the queue into rust-lang:master with commit d794004 Jun 2, 2025
23 checks passed
bors added a commit to rust-lang/rust that referenced this pull request Jun 4, 2025
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-manifest Area: Cargo.toml issues S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

cargo add overwrites a symlink Cargo.toml
4 participants