Skip to content

Commit 88e7f30

Browse files
fix: Preserve symlinks when running cargo add
When cargo add is used on a project where Cargo.toml is a symlink, it now follows the symlink and writes to the target file instead of replacing the symlink with a regular file. This is achieved by updating write_atomic() to detect symlinks and resolve them before writing. Fixes #15241
1 parent 3498cfe commit 88e7f30

File tree

1 file changed

+14
-5
lines changed

1 file changed

+14
-5
lines changed

crates/cargo-util/src/paths.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,19 @@ pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()>
190190
/// Writes a file to disk atomically.
191191
///
192192
/// This uses `tempfile::persist` to accomplish atomic writes.
193+
/// If the path is a symlink, it will follow the symlink and write to the actual target.
193194
pub fn write_atomic<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()> {
194195
let path = path.as_ref();
195196

197+
// Check if the path is a symlink and follow it if it is
198+
let resolved_path;
199+
let path = if path.is_symlink() {
200+
resolved_path = std::fs::read_link(path)?;
201+
&resolved_path
202+
} else {
203+
path
204+
};
205+
196206
// On unix platforms, get the permissions of the original file. Copy only the user/group/other
197207
// read/write/execute permission bits. The tempfile lib defaults to an initial mode of 0o600,
198208
// and we'll set the proper permissions after creating the file.
@@ -201,10 +211,9 @@ pub fn write_atomic<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Res
201211
use std::os::unix::fs::PermissionsExt;
202212

203213
// these constants are u16 on macOS
204-
let mask = u32::from(libc::S_IRWXU | libc::S_IRWXG | libc::S_IRWXO);
205-
let mode = meta.permissions().mode() & mask;
206-
207-
std::fs::Permissions::from_mode(mode)
214+
const PERMS_MASK: u32 = 0o777;
215+
let mode = meta.permissions().mode() as u32;
216+
std::fs::Permissions::from_mode(mode & PERMS_MASK)
208217
});
209218

210219
let mut tmp = TempFileBuilder::new()
@@ -213,7 +222,7 @@ pub fn write_atomic<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Res
213222
tmp.write_all(contents.as_ref())?;
214223

215224
// On unix platforms, set the permissions on the newly created file. We can use fchmod (called
216-
// by the std lib; subject to change) which ignores the umask so that the new file has the same
225+
// by the std lib method below) which ignores the umask so that the new file has the same
217226
// permissions as the old file.
218227
#[cfg(unix)]
219228
if let Some(perms) = perms {

0 commit comments

Comments
 (0)