Mercurial > hg
comparison rust/chg/src/locator.rs @ 39976: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 | |
children | 7d3285f799cc |
comparison
equal
deleted
inserted
replaced
39975:571d8eb39095 | 39976:44840bcc411a |
---|---|
1 // Copyright 2011, 2018 Yuya Nishihara <yuya@tcha.org> | |
2 // | |
3 // This software may be used and distributed according to the terms of the | |
4 // GNU General Public License version 2 or any later version. | |
5 | |
6 //! Utility for locating command-server process. | |
7 | |
8 use std::env; | |
9 use std::fs::{self, DirBuilder}; | |
10 use std::io; | |
11 use std::os::unix::fs::{DirBuilderExt, MetadataExt}; | |
12 use std::path::{Path, PathBuf}; | |
13 | |
14 use super::procutil; | |
15 | |
16 /// Determines the server socket to connect to. | |
17 /// | |
18 /// If no `$CHGSOCKNAME` is specified, the socket directory will be created | |
19 /// as necessary. | |
20 pub fn prepare_server_socket_path() -> io::Result<PathBuf> { | |
21 if let Some(s) = env::var_os("CHGSOCKNAME") { | |
22 Ok(PathBuf::from(s)) | |
23 } else { | |
24 let mut path = default_server_socket_dir(); | |
25 create_secure_dir(&path)?; | |
26 path.push("server"); | |
27 Ok(path) | |
28 } | |
29 } | |
30 | |
31 /// Determines the default server socket path as follows. | |
32 /// | |
33 /// 1. `$XDG_RUNTIME_DIR/chg` | |
34 /// 2. `$TMPDIR/chg$UID` | |
35 /// 3. `/tmp/chg$UID` | |
36 pub fn default_server_socket_dir() -> PathBuf { | |
37 // XDG_RUNTIME_DIR should be ignored if it has an insufficient permission. | |
38 // https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html | |
39 if let Some(Ok(s)) = env::var_os("XDG_RUNTIME_DIR").map(check_secure_dir) { | |
40 let mut path = PathBuf::from(s); | |
41 path.push("chg"); | |
42 path | |
43 } else { | |
44 let mut path = env::temp_dir(); | |
45 path.push(format!("chg{}", procutil::get_effective_uid())); | |
46 path | |
47 } | |
48 } | |
49 | |
50 /// Creates a directory which the other users cannot access to. | |
51 /// | |
52 /// If the directory already exists, tests its permission. | |
53 fn create_secure_dir<P>(path: P) -> io::Result<()> | |
54 where P: AsRef<Path>, | |
55 { | |
56 DirBuilder::new().mode(0o700).create(path.as_ref()).or_else(|err| { | |
57 if err.kind() == io::ErrorKind::AlreadyExists { | |
58 check_secure_dir(path).map(|_| ()) | |
59 } else { | |
60 Err(err) | |
61 } | |
62 }) | |
63 } | |
64 | |
65 fn check_secure_dir<P>(path: P) -> io::Result<P> | |
66 where P: AsRef<Path>, | |
67 { | |
68 let a = fs::symlink_metadata(path.as_ref())?; | |
69 if a.is_dir() && a.uid() == procutil::get_effective_uid() && (a.mode() & 0o777) == 0o700 { | |
70 Ok(path) | |
71 } else { | |
72 Err(io::Error::new(io::ErrorKind::Other, "insecure directory")) | |
73 } | |
74 } |