Skip to content

Commit 36a63b4

Browse files
committed
fix: preserve file permissions on unix during write_atomic
Preseves u/g/o r/w/x permissions on unix platforms.
1 parent 038ccd2 commit 36a63b4

File tree

1 file changed

+26
-2
lines changed

1 file changed

+26
-2
lines changed

crates/cargo-util/src/paths.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use anyhow::{Context, Result};
44
use filetime::FileTime;
55
use std::env;
66
use std::ffi::{OsStr, OsString};
7-
use std::fs::{self, File, Metadata, OpenOptions};
7+
use std::fs::{self, File, Metadata, OpenOptions, Permissions};
88
use std::io;
99
use std::io::prelude::*;
1010
use std::iter;
@@ -185,10 +185,34 @@ pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()>
185185
/// write_atomic uses tempfile::persist to accomplish atomic writes.
186186
pub fn write_atomic<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()> {
187187
let path = path.as_ref();
188+
189+
// On unix platforms, get the permissions of the original file. Copy only the user/group/other
190+
// read/write/execute permission bits. The tempfile lib defaults to an initial mode of 0o600,
191+
// and we'll set the proper permissions after creating the file.
192+
#[cfg(unix)]
193+
let perms = path.metadata().ok().map(|meta| {
194+
use std::os::unix::fs::PermissionsExt;
195+
196+
// these constants are u16 on macOS
197+
let mask = u32::from(libc::S_IRWXU | libc::S_IRWXG | libc::S_IRWXO);
198+
let mode = meta.permissions().mode() & mask;
199+
200+
Permissions::from_mode(mode)
201+
});
202+
188203
let mut tmp = TempFileBuilder::new()
189204
.prefix(path.file_name().unwrap())
190205
.tempfile_in(path.parent().unwrap())?;
191206
tmp.write_all(contents.as_ref())?;
207+
208+
// On unix platforms, set the permissions on the newly created file. We can use fchmod (called
209+
// by the std lib; subject to change) which ignores the umask so that the new file has the same
210+
// permissions as the old file.
211+
#[cfg(unix)]
212+
if let Some(perms) = perms {
213+
tmp.as_file().set_permissions(perms)?;
214+
}
215+
192216
tmp.persist(path)?;
193217
Ok(())
194218
}
@@ -846,7 +870,7 @@ mod tests {
846870
let new_perms = std::fs::metadata(tmp.path()).unwrap().permissions();
847871

848872
let mask = u32::from(libc::S_IRWXU | libc::S_IRWXG | libc::S_IRWXO);
849-
assert_eq!(0o600, new_perms.mode() & mask);
873+
assert_eq!(original_perms.mode(), new_perms.mode() & mask);
850874
}
851875

852876
#[test]

0 commit comments

Comments
 (0)