author | Raphaël Gomès <rgomes@octobus.net> |
Mon, 04 Nov 2024 11:00:58 +0100 | |
changeset 52302 | db065b33fa56 |
parent 50416 | e2c8b30ab4e7 |
permissions | -rw-r--r-- |
49894
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
1 |
use std::fs; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
2 |
use std::io; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
3 |
use std::os::unix::fs::{MetadataExt, PermissionsExt}; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
4 |
use std::path::Path; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
5 |
|
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
6 |
const EXECFLAGS: u32 = 0o111; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
7 |
|
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
8 |
fn is_executable(path: impl AsRef<Path>) -> Result<bool, io::Error> { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
9 |
let metadata = fs::metadata(path)?; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
10 |
let mode = metadata.mode(); |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
11 |
Ok(mode & EXECFLAGS != 0) |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
12 |
} |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
13 |
|
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
14 |
fn make_executable(path: impl AsRef<Path>) -> Result<(), io::Error> { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
15 |
let mode = fs::metadata(path.as_ref())?.mode(); |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
16 |
fs::set_permissions( |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
17 |
path, |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
18 |
fs::Permissions::from_mode((mode & 0o777) | EXECFLAGS), |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
19 |
)?; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
20 |
Ok(()) |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
21 |
} |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
22 |
|
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
23 |
fn copy_mode( |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
24 |
src: impl AsRef<Path>, |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
25 |
dst: impl AsRef<Path>, |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
26 |
) -> Result<(), io::Error> { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
27 |
let mode = match fs::symlink_metadata(src) { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
28 |
Ok(metadata) => metadata.mode(), |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
29 |
Err(e) if e.kind() == io::ErrorKind::NotFound => |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
30 |
// copymode in python has a more complicated handling of FileNotFound |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
31 |
// error, which we don't need because all it does is applying |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
32 |
// umask, which the OS already does when we mkdir. |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
33 |
{ |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
34 |
return Ok(()) |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
35 |
} |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
36 |
Err(e) => return Err(e), |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
37 |
}; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
38 |
fs::set_permissions(dst, fs::Permissions::from_mode(mode))?; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
39 |
Ok(()) |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
40 |
} |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
41 |
|
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
42 |
fn check_exec_impl(path: impl AsRef<Path>) -> Result<bool, io::Error> { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
43 |
let basedir = path.as_ref().join(".hg"); |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
44 |
let cachedir = basedir.join("wcache"); |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
45 |
let storedir = basedir.join("store"); |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
46 |
|
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
47 |
if !cachedir.exists() { |
49895
07792fd1837f
doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
49894
diff
changeset
|
48 |
// we want to create the 'cache' directory, not the '.hg' one. |
07792fd1837f
doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
49894
diff
changeset
|
49 |
// Automatically creating '.hg' directory could silently spawn |
07792fd1837f
doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
49894
diff
changeset
|
50 |
// invalid Mercurial repositories. That seems like a bad idea. |
49894
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
51 |
fs::create_dir(&cachedir) |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
52 |
.and_then(|()| { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
53 |
if storedir.exists() { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
54 |
copy_mode(&storedir, &cachedir) |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
55 |
} else { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
56 |
copy_mode(&basedir, &cachedir) |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
57 |
} |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
58 |
}) |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
59 |
.ok(); |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
60 |
} |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
61 |
|
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
62 |
let leave_file: bool; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
63 |
let checkdir: &Path; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
64 |
let checkisexec = cachedir.join("checkisexec"); |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
65 |
let checknoexec = cachedir.join("checknoexec"); |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
66 |
if cachedir.is_dir() { |
49895
07792fd1837f
doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
49894
diff
changeset
|
67 |
// Check if both files already exist in cache and have correct |
07792fd1837f
doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
49894
diff
changeset
|
68 |
// permissions. if so, we assume that permissions work. |
07792fd1837f
doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
49894
diff
changeset
|
69 |
// If not, we delete the files and try again. |
49894
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
70 |
match is_executable(&checkisexec) { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
71 |
Err(e) if e.kind() == io::ErrorKind::NotFound => (), |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
72 |
Err(e) => return Err(e), |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
73 |
Ok(is_exec) => { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
74 |
if is_exec { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
75 |
let noexec_is_exec = match is_executable(&checknoexec) { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
76 |
Err(e) if e.kind() == io::ErrorKind::NotFound => { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
77 |
fs::write(&checknoexec, "")?; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
78 |
is_executable(&checknoexec)? |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
79 |
} |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
80 |
Err(e) => return Err(e), |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
81 |
Ok(exec) => exec, |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
82 |
}; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
83 |
if !noexec_is_exec { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
84 |
// check-exec is exec and check-no-exec is not exec |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
85 |
return Ok(true); |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
86 |
} |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
87 |
fs::remove_file(&checknoexec)?; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
88 |
} |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
89 |
fs::remove_file(&checkisexec)?; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
90 |
} |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
91 |
} |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
92 |
checkdir = &cachedir; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
93 |
leave_file = true; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
94 |
} else { |
49895
07792fd1837f
doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
49894
diff
changeset
|
95 |
// no cache directory (probably because .hg doesn't exist): |
07792fd1837f
doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
49894
diff
changeset
|
96 |
// check directly in `path` and don't leave the temp file behind |
49894
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
97 |
checkdir = path.as_ref(); |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
98 |
leave_file = false; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
99 |
}; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
100 |
|
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
101 |
let tmp_file = tempfile::NamedTempFile::new_in(checkdir)?; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
102 |
if !is_executable(tmp_file.path())? { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
103 |
make_executable(tmp_file.path())?; |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
104 |
if is_executable(tmp_file.path())? { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
105 |
if leave_file { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
106 |
tmp_file.persist(checkisexec).ok(); |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
107 |
} |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
108 |
return Ok(true); |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
109 |
} |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
110 |
} |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
111 |
|
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
112 |
Ok(false) |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
113 |
} |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
114 |
|
50416
e2c8b30ab4e7
rustdoc: wording for checkexec
Georges Racinet <georges.racinet@octobus.net>
parents:
50415
diff
changeset
|
115 |
/// This function is a Rust rewrite of the `checkexec` function from |
e2c8b30ab4e7
rustdoc: wording for checkexec
Georges Racinet <georges.racinet@octobus.net>
parents:
50415
diff
changeset
|
116 |
/// `posix.py`. |
e2c8b30ab4e7
rustdoc: wording for checkexec
Georges Racinet <georges.racinet@octobus.net>
parents:
50415
diff
changeset
|
117 |
/// |
e2c8b30ab4e7
rustdoc: wording for checkexec
Georges Racinet <georges.racinet@octobus.net>
parents:
50415
diff
changeset
|
118 |
/// Returns `true` if the filesystem supports execute permissions. |
49894
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
119 |
pub fn check_exec(path: impl AsRef<Path>) -> bool { |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
120 |
check_exec_impl(path).unwrap_or(false) |
678588b01af1
rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff
changeset
|
121 |
} |