Mercurial > hg > mercurial-source
changeset 40762:44840bcc411a
rust-chg: port basic socket path handling from cHg of C
This is basically modeled after setcmdserveropts() of chg.c.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Mon, 24 Sep 2018 18:33:46 +0900 |
parents | 571d8eb39095 |
children | 74da9d999cd7 |
files | rust/chg/src/lib.rs rust/chg/src/locator.rs rust/chg/src/procutil.rs |
diffstat | 3 files changed, 80 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/rust/chg/src/lib.rs +++ b/rust/chg/src/lib.rs @@ -12,6 +12,7 @@ extern crate tokio_process; pub mod attachio; +pub mod locator; pub mod message; pub mod procutil; pub mod runcommand;
new file mode 100644 --- /dev/null +++ b/rust/chg/src/locator.rs @@ -0,0 +1,74 @@ +// Copyright 2011, 2018 Yuya Nishihara <yuya@tcha.org> +// +// This software may be used and distributed according to the terms of the +// GNU General Public License version 2 or any later version. + +//! Utility for locating command-server process. + +use std::env; +use std::fs::{self, DirBuilder}; +use std::io; +use std::os::unix::fs::{DirBuilderExt, MetadataExt}; +use std::path::{Path, PathBuf}; + +use super::procutil; + +/// Determines the server socket to connect to. +/// +/// If no `$CHGSOCKNAME` is specified, the socket directory will be created +/// as necessary. +pub fn prepare_server_socket_path() -> io::Result<PathBuf> { + if let Some(s) = env::var_os("CHGSOCKNAME") { + Ok(PathBuf::from(s)) + } else { + let mut path = default_server_socket_dir(); + create_secure_dir(&path)?; + path.push("server"); + Ok(path) + } +} + +/// Determines the default server socket path as follows. +/// +/// 1. `$XDG_RUNTIME_DIR/chg` +/// 2. `$TMPDIR/chg$UID` +/// 3. `/tmp/chg$UID` +pub fn default_server_socket_dir() -> PathBuf { + // XDG_RUNTIME_DIR should be ignored if it has an insufficient permission. + // https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + if let Some(Ok(s)) = env::var_os("XDG_RUNTIME_DIR").map(check_secure_dir) { + let mut path = PathBuf::from(s); + path.push("chg"); + path + } else { + let mut path = env::temp_dir(); + path.push(format!("chg{}", procutil::get_effective_uid())); + path + } +} + +/// Creates a directory which the other users cannot access to. +/// +/// If the directory already exists, tests its permission. +fn create_secure_dir<P>(path: P) -> io::Result<()> + where P: AsRef<Path>, +{ + DirBuilder::new().mode(0o700).create(path.as_ref()).or_else(|err| { + if err.kind() == io::ErrorKind::AlreadyExists { + check_secure_dir(path).map(|_| ()) + } else { + Err(err) + } + }) +} + +fn check_secure_dir<P>(path: P) -> io::Result<P> + where P: AsRef<Path>, +{ + let a = fs::symlink_metadata(path.as_ref())?; + if a.is_dir() && a.uid() == procutil::get_effective_uid() && (a.mode() & 0o777) == 0o700 { + Ok(path) + } else { + Err(io::Error::new(io::ErrorKind::Other, "insecure directory")) + } +}
--- a/rust/chg/src/procutil.rs +++ b/rust/chg/src/procutil.rs @@ -15,6 +15,11 @@ fn sendfds(sockfd: c_int, fds: *const c_int, fdlen: size_t) -> ssize_t; } +/// Returns the effective uid of the current process. +pub fn get_effective_uid() -> u32 { + unsafe { libc::geteuid() } +} + /// 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) };