|
1 | 1 | use std::{ |
2 | 2 | fmt::{Debug, Display}, |
3 | | - io, |
| 3 | + fs, io, |
4 | 4 | }; |
5 | 5 |
|
6 | | -use rustix::{fs::Mode, process::umask}; |
| 6 | +use rustix::{ |
| 7 | + fs::{Mode, RawMode}, |
| 8 | + process::umask, |
| 9 | +}; |
7 | 10 |
|
8 | 11 | pub type Result<T = (), E = Exit> = std::result::Result<T, E>; |
9 | 12 |
|
@@ -76,26 +79,31 @@ macro_rules! help_text { |
76 | 79 | /// Gets the umask of the calling process |
77 | 80 | pub fn get_umask() -> Mode { |
78 | 81 | /* |
79 | | - FIXME: Behold the umask abomination, a perfect example of UNIX's "let's make everything |
80 | | - as backwards and painful as possible" design philosophy. This cursed bitmask lives in |
81 | | - kernel space and can ONLY be read by WRITING TO IT because someone in 1973 |
82 | | - decided that having a simple getter function was too logical. So here we are, doing |
| 82 | + Historical note: The traditional umask() syscall is a perfect example of UNIX's "let's make |
| 83 | + everything as backwards and painful as possible" design philosophy. This cursed bitmask |
| 84 | + lives in kernel space and could ONLY be read by WRITING TO IT because someone in 1973 |
| 85 | + decided that having a simple getter function was too logical. The classic approach required |
83 | 86 | the umask shuffle: set it to 0, capture the old value, then set it back like some |
84 | | - demented atomic operation that's not actually atomic. I mean who needs threads anyway, |
85 | | - it's not like computers will ever be powerful enough to do more things at once, |
86 | | - not for hundreds of years at the very least. |
87 | | -
|
88 | | - *sigh* |
| 87 | + demented atomic operation that's not actually atomic. |
89 | 88 |
|
90 | | - We SHOULD read from /proc/self/status like any sane program except nobody does that? |
91 | | - but no - we're stuck with this fossilized turd of an API that makes you modify |
92 | | - global state just to observe it. At least on anything older than 2016 which is more common |
93 | | - than you'd think, sob |
| 89 | + Fortunately, modern Linux (kernel 4.7+, 2016) added umask to /proc/self/status, so we |
| 90 | + can read it cleanly without the race condition. We fall back to the traditional method |
| 91 | + for older systems or non-Linux platforms. |
94 | 92 |
|
95 | | - I HATE UNIX |
96 | | - I HATE UNIX |
97 | | - I HATE UNIX |
| 93 | + The old way was truly cursed - modifying global state just to observe it. At least |
| 94 | + we've mostly escaped that particular circle of UNIX hell on modern systems. |
98 | 95 | */ |
| 96 | + |
| 97 | + if let Ok(status) = fs::read_to_string("/proc/self/status") { |
| 98 | + if let Some(umask) = status |
| 99 | + .lines() |
| 100 | + .find_map(|line| RawMode::from_str_radix(line.strip_prefix("Umask:")?.trim(), 8).ok()) |
| 101 | + { |
| 102 | + return Mode::from_raw_mode(umask); |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + // Fallback method with a possible race condition |
99 | 107 | let current_umask = umask(Mode::empty()); |
100 | 108 | umask(current_umask); |
101 | 109 |
|
|
0 commit comments