diff --git a/rust/chg/src/clientext.rs b/rust/chg/src/clientext.rs --- a/rust/chg/src/clientext.rs +++ b/rust/chg/src/clientext.rs @@ -5,9 +5,10 @@ //! cHg extensions to command server client. -use bytes::Bytes; +use bytes::{BufMut, Bytes, BytesMut}; use std::ffi::OsStr; use std::io; +use std::mem; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::AsRawFd; use std::path::Path; @@ -41,6 +42,9 @@ I: IntoIterator, P: AsRef; + /// Changes the umask of the server process. + fn set_umask(self, mask: u32) -> OneShotRequest; + /// Runs the specified Mercurial command with cHg extension. fn run_command_chg(self, handler: H, args: I) -> ChgRunCommand where @@ -90,6 +94,12 @@ OneShotRequest::start_with_args(self, b"setenv", message::pack_env_vars_os(vars)) } + fn set_umask(self, mask: u32) -> OneShotRequest { + let mut args = BytesMut::with_capacity(mem::size_of_val(&mask)); + args.put_u32_be(mask); + OneShotRequest::start_with_args(self, b"setumask2", args) + } + fn run_command_chg(self, handler: H, args: I) -> ChgRunCommand where I: IntoIterator, diff --git a/rust/chg/src/locator.rs b/rust/chg/src/locator.rs --- a/rust/chg/src/locator.rs +++ b/rust/chg/src/locator.rs @@ -24,8 +24,14 @@ use super::message::{Instruction, ServerSpec}; use super::procutil; -const REQUIRED_SERVER_CAPABILITIES: &[&str] = - &["attachio", "chdir", "runcommand", "setenv", "validate"]; +const REQUIRED_SERVER_CAPABILITIES: &[&str] = &[ + "attachio", + "chdir", + "runcommand", + "setenv", + "setumask2", + "validate", +]; /// Helper to connect to and spawn a server process. #[derive(Clone, Debug)] diff --git a/rust/chg/src/main.rs b/rust/chg/src/main.rs --- a/rust/chg/src/main.rs +++ b/rust/chg/src/main.rs @@ -73,6 +73,7 @@ } fn run() -> io::Result { + let umask = unsafe { procutil::get_umask() }; // not thread safe let mut loc = Locator::prepare_from_env()?; loc.set_early_args(locator::collect_early_args(env::args_os().skip(1))); let handler = ChgUiHandler::new(); @@ -80,6 +81,7 @@ let fut = loc .connect() .and_then(|(_, client)| client.attach_io(io::stdin(), io::stdout(), io::stderr())) + .and_then(move |client| client.set_umask(umask)) .and_then(|client| { let pid = client.server_spec().process_id.unwrap(); let pgid = client.server_spec().process_group_id; diff --git a/rust/chg/src/procutil.rs b/rust/chg/src/procutil.rs --- a/rust/chg/src/procutil.rs +++ b/rust/chg/src/procutil.rs @@ -25,6 +25,19 @@ unsafe { libc::geteuid() } } +/// Returns the umask of the current process. +/// +/// # Safety +/// +/// This is unsafe because the umask value is temporarily changed, and +/// the change can be observed from the other threads. Don't call this in +/// multi-threaded context. +pub unsafe fn get_umask() -> u32 { + let mask = libc::umask(0); + libc::umask(mask); + mask +} + /// Changes the given fd to blocking mode. pub fn set_blocking_fd(fd: RawFd) -> io::Result<()> { let flags = unsafe { libc::fcntl(fd, libc::F_GETFL) };