]>
Commit | Line | Data |
---|---|---|
e420f6f9 WB |
1 | //! pidfd helper functionality |
2 | ||
b2564ece | 3 | use std::ffi::{CStr, CString, OsString}; |
512f780a | 4 | use std::io::{self, BufRead, BufReader}; |
937921aa | 5 | use std::os::raw::c_int; |
3bbd1db0 | 6 | use std::os::unix::ffi::OsStringExt; |
caeb9675 | 7 | use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; |
e420f6f9 | 8 | |
8150a439 | 9 | use anyhow::{bail, Error}; |
512f780a WB |
10 | use libc::pid_t; |
11 | ||
3bbd1db0 | 12 | use crate::capability::Capabilities; |
a03af34b | 13 | use crate::error::io_err_other; |
e420f6f9 | 14 | use crate::nsfd::{ns_type, NsFd}; |
e420f6f9 | 15 | |
3bbd1db0 WB |
16 | use super::{CGroups, IdMap, IdMapEntry, ProcStatus, Uids, UserCaps}; |
17 | ||
8a3c5add | 18 | pub struct PidFd(OwnedFd, pid_t); |
9aa2a15a | 19 | file_descriptor_impl!(PidFd); |
512f780a | 20 | |
e420f6f9 | 21 | impl PidFd { |
42f25756 | 22 | pub fn current() -> io::Result<Self> { |
401ab6a2 | 23 | Self::open(unsafe { libc::getpid() }) |
42f25756 WB |
24 | } |
25 | ||
512f780a | 26 | pub fn open(pid: pid_t) -> io::Result<Self> { |
d463e3b4 | 27 | let path = CString::new(format!("/proc/{pid}")).unwrap(); |
b2564ece WB |
28 | |
29 | let fd = c_try!(unsafe { libc::open(path.as_ptr(), libc::O_DIRECTORY | libc::O_CLOEXEC) }); | |
8a3c5add | 30 | let fd = unsafe { OwnedFd::from_raw_fd(fd) }; |
b2564ece WB |
31 | |
32 | Ok(Self(fd, pid)) | |
512f780a WB |
33 | } |
34 | ||
92eface0 WB |
35 | /// Turn a valid pid file descriptor into a PidFd. |
36 | /// | |
37 | /// # Safety | |
38 | /// | |
39 | /// The file descriptor must already be a valid pidfd, this is not checked. This function only | |
40 | /// fails if reading the pid from the pidfd's proc entry fails. | |
caeb9675 | 41 | pub unsafe fn try_from_fd(fd: OwnedFd) -> io::Result<Self> { |
a03af34b | 42 | #[allow(clippy::unnecessary_cast)] // pid_t is a type alias |
8a3c5add | 43 | let mut this = Self(fd, -1 as pid_t); |
512f780a WB |
44 | let pid = this.read_pid()?; |
45 | this.1 = pid; | |
46 | Ok(this) | |
e420f6f9 WB |
47 | } |
48 | ||
49 | pub fn mount_namespace(&self) -> io::Result<NsFd<ns_type::Mount>> { | |
8a3c5add | 50 | NsFd::openat(self.0.as_raw_fd(), c_str!("ns/mnt")) |
e420f6f9 WB |
51 | } |
52 | ||
53 | pub fn cgroup_namespace(&self) -> io::Result<NsFd<ns_type::Cgroup>> { | |
8a3c5add | 54 | NsFd::openat(self.0.as_raw_fd(), c_str!("ns/cgroup")) |
e420f6f9 WB |
55 | } |
56 | ||
57 | pub fn user_namespace(&self) -> io::Result<NsFd<ns_type::User>> { | |
8a3c5add | 58 | NsFd::openat(self.0.as_raw_fd(), c_str!("ns/user")) |
e420f6f9 WB |
59 | } |
60 | ||
caeb9675 WB |
61 | fn fd(&self, path: &CStr, flags: c_int, mode: c_int) -> io::Result<OwnedFd> { |
62 | Ok(unsafe { | |
63 | OwnedFd::from_raw_fd(c_try!(libc::openat( | |
e420f6f9 | 64 | self.as_raw_fd(), |
937921aa WB |
65 | path.as_ptr() as *const _, |
66 | flags | libc::O_CLOEXEC, | |
3bb4df0b | 67 | mode, |
caeb9675 WB |
68 | ))) |
69 | }) | |
e420f6f9 | 70 | } |
937921aa | 71 | |
caeb9675 | 72 | pub fn fd_cwd(&self) -> io::Result<OwnedFd> { |
1e80bab0 | 73 | self.fd(c_str!("cwd"), libc::O_DIRECTORY, 0) |
937921aa WB |
74 | } |
75 | ||
caeb9675 | 76 | pub fn fd_num(&self, num: RawFd, flags: c_int) -> io::Result<OwnedFd> { |
d463e3b4 | 77 | let path = format!("fd/{num}\0"); |
512f780a WB |
78 | self.fd( |
79 | unsafe { CStr::from_bytes_with_nul_unchecked(path.as_bytes()) }, | |
80 | flags, | |
81 | 0, | |
82 | ) | |
937921aa | 83 | } |
275009ec | 84 | |
bff40ab9 | 85 | pub fn enter_cwd(&self) -> io::Result<()> { |
7ca1a14c | 86 | c_try!(unsafe { libc::fchdir(self.fd_cwd()?.as_raw_fd()) }); |
bff40ab9 WB |
87 | Ok(()) |
88 | } | |
89 | ||
90 | pub fn enter_chroot(&self) -> io::Result<()> { | |
7ca1a14c WB |
91 | c_try!(unsafe { libc::fchdir(self.as_raw_fd()) }); |
92 | c_try!(unsafe { libc::chroot(b"root\0".as_ptr() as *const _) }); | |
93 | c_try!(unsafe { libc::chdir(b"/\0".as_ptr() as *const _) }); | |
512f780a WB |
94 | Ok(()) |
95 | } | |
3bb4df0b WB |
96 | |
97 | // procfs files cannot be async, we cannot add them to epoll... | |
98 | pub fn open_file(&self, path: &CStr, flags: c_int, mode: c_int) -> io::Result<std::fs::File> { | |
99 | Ok(unsafe { std::fs::File::from_raw_fd(self.fd(path, flags, mode)?.into_raw_fd()) }) | |
100 | } | |
101 | ||
512f780a WB |
102 | #[inline] |
103 | fn open_buffered(&self, path: &CStr) -> io::Result<impl BufRead> { | |
104 | Ok(BufReader::new(self.open_file( | |
105 | path, | |
3bb4df0b WB |
106 | libc::O_RDONLY | libc::O_CLOEXEC, |
107 | 0, | |
512f780a WB |
108 | )?)) |
109 | } | |
110 | ||
111 | #[inline] | |
112 | pub fn get_pid(&self) -> pid_t { | |
113 | self.1 | |
114 | } | |
115 | ||
116 | fn read_pid(&self) -> io::Result<pid_t> { | |
1e80bab0 | 117 | let reader = self.open_buffered(c_str!("status"))?; |
512f780a WB |
118 | |
119 | for line in reader.lines() { | |
120 | let line = line?; | |
121 | let mut parts = line.split_ascii_whitespace(); | |
122 | if parts.next() == Some("Pid:") { | |
123 | let pid = parts | |
124 | .next() | |
125 | .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "bad 'Pid:' line in proc"))? | |
126 | .parse::<pid_t>() | |
127 | .map_err(|_| { | |
128 | io::Error::new(io::ErrorKind::Other, "failed to parse pid from proc") | |
129 | })?; | |
130 | return Ok(pid); | |
131 | } | |
132 | } | |
133 | ||
134 | Err(io::ErrorKind::NotFound.into()) | |
135 | } | |
136 | ||
1349eed4 WB |
137 | #[inline] |
138 | fn __check_uid_gid(value: Option<&str>) -> io::Result<libc::uid_t> { | |
139 | value | |
140 | .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "bad 'Uid/Gid:' line in proc"))? | |
141 | .parse::<libc::uid_t>() | |
142 | .map_err(|_| io::Error::new(io::ErrorKind::Other, "failed to parse uid from proc")) | |
143 | } | |
144 | ||
512f780a | 145 | pub fn get_status(&self) -> io::Result<ProcStatus> { |
1e80bab0 | 146 | let reader = self.open_buffered(c_str!("status"))?; |
512f780a | 147 | |
512f780a WB |
148 | #[inline] |
149 | fn check_u64_hex(value: Option<&str>) -> io::Result<u64> { | |
a03af34b | 150 | u64::from_str_radix( |
512f780a WB |
151 | value.ok_or_else(|| { |
152 | io::Error::new(io::ErrorKind::Other, "bad numeric property line in proc") | |
153 | })?, | |
154 | 16, | |
155 | ) | |
a03af34b | 156 | .map_err(io_err_other) |
512f780a WB |
157 | } |
158 | ||
159 | #[inline] | |
160 | fn check_u32_oct(value: Option<&str>) -> io::Result<u32> { | |
a03af34b | 161 | u32::from_str_radix( |
512f780a WB |
162 | value.ok_or_else(|| { |
163 | io::Error::new(io::ErrorKind::Other, "bad numeric property line in proc") | |
164 | })?, | |
165 | 8, | |
166 | ) | |
a03af34b | 167 | .map_err(io_err_other) |
512f780a | 168 | } |
3bb4df0b | 169 | |
512f780a WB |
170 | let mut ids = Uids::default(); |
171 | let mut caps = Capabilities::default(); | |
172 | let mut umask = 0o022; | |
3bb4df0b WB |
173 | for line in reader.lines() { |
174 | let line = line?; | |
175 | let mut parts = line.split_ascii_whitespace(); | |
176 | match parts.next() { | |
177 | Some("Uid:") => { | |
1349eed4 WB |
178 | ids.ruid = Self::__check_uid_gid(parts.next())?; |
179 | ids.euid = Self::__check_uid_gid(parts.next())?; | |
180 | ids.suid = Self::__check_uid_gid(parts.next())?; | |
181 | ids.fsuid = Self::__check_uid_gid(parts.next())?; | |
3bb4df0b WB |
182 | } |
183 | Some("Gid:") => { | |
1349eed4 WB |
184 | ids.rgid = Self::__check_uid_gid(parts.next())?; |
185 | ids.egid = Self::__check_uid_gid(parts.next())?; | |
186 | ids.sgid = Self::__check_uid_gid(parts.next())?; | |
187 | ids.fsgid = Self::__check_uid_gid(parts.next())?; | |
3bb4df0b | 188 | } |
512f780a WB |
189 | Some("CapInh:") => caps.inheritable = check_u64_hex(parts.next())?, |
190 | Some("CapPrm:") => caps.permitted = check_u64_hex(parts.next())?, | |
191 | Some("CapEff:") => caps.effective = check_u64_hex(parts.next())?, | |
192 | //Some("CapBnd:") => caps.bounding = check_u64_hex(parts.next())?, | |
193 | Some("Umask:") => umask = check_u32_oct(parts.next())?, | |
3bb4df0b WB |
194 | _ => continue, |
195 | } | |
512f780a WB |
196 | } |
197 | ||
198 | Ok(ProcStatus { | |
199 | uids: ids, | |
200 | capabilities: caps, | |
201 | umask, | |
202 | }) | |
203 | } | |
204 | ||
205 | pub fn get_cgroups(&self) -> Result<CGroups, Error> { | |
1e80bab0 | 206 | let reader = self.open_buffered(c_str!("cgroup"))?; |
512f780a WB |
207 | |
208 | let mut cgroups = CGroups::new(); | |
209 | ||
210 | for line in reader.split(b'\n') { | |
211 | let line = line?; | |
212 | let mut parts = line.splitn(3, |b| *b == b':'); | |
213 | let num = parts.next(); | |
214 | let name = parts.next(); | |
215 | let path = parts.next(); | |
f3cae2a7 | 216 | if num.is_none() || name.is_none() || path.is_none() || parts.next().is_some() { |
512f780a | 217 | bail!("failed to parse cgroup line: {:?}", line); |
3bb4df0b | 218 | } |
512f780a WB |
219 | |
220 | let name = String::from_utf8(name.unwrap().to_vec())?; | |
221 | let path = OsString::from_vec(path.unwrap().to_vec()); | |
222 | ||
9486338a | 223 | if name.is_empty() { |
512f780a WB |
224 | cgroups.v2 = Some(path); |
225 | } else { | |
226 | for entry in name.split(',') { | |
fe73c2fb WB |
227 | cgroups |
228 | .v1 | |
229 | .get_or_insert_with(Default::default) | |
230 | .insert(entry.to_string(), path.clone()); | |
512f780a WB |
231 | } |
232 | } | |
233 | } | |
234 | ||
235 | Ok(cgroups) | |
236 | } | |
237 | ||
1349eed4 WB |
238 | pub fn get_uid_gid_map(&self, file: &CStr) -> Result<IdMap, Error> { |
239 | let reader = self.open_buffered(file)?; | |
240 | ||
241 | let mut entries = Vec::new(); | |
242 | for line in reader.lines() { | |
243 | let line = line?; | |
244 | let mut parts = line.split_ascii_whitespace(); | |
9486338a WB |
245 | let ns = u64::from(Self::__check_uid_gid(parts.next())?); |
246 | let host = u64::from(Self::__check_uid_gid(parts.next())?); | |
247 | let range = u64::from(Self::__check_uid_gid(parts.next())?); | |
1349eed4 WB |
248 | entries.push(IdMapEntry { ns, host, range }); |
249 | } | |
250 | ||
3bbd1db0 | 251 | Ok(IdMap::new(entries)) |
1349eed4 WB |
252 | } |
253 | ||
254 | pub fn get_uid_map(&self) -> Result<IdMap, Error> { | |
1e80bab0 | 255 | self.get_uid_gid_map(c_str!("uid_map")) |
1349eed4 WB |
256 | } |
257 | ||
258 | pub fn get_gid_map(&self) -> Result<IdMap, Error> { | |
1e80bab0 | 259 | self.get_uid_gid_map(c_str!("gid_map")) |
1349eed4 WB |
260 | } |
261 | ||
42f25756 WB |
262 | pub fn read_file(&self, file: &CStr) -> io::Result<Vec<u8>> { |
263 | use io::Read; | |
264 | ||
265 | let mut reader = self.open_file(file, libc::O_RDONLY | libc::O_CLOEXEC, 0)?; | |
266 | let mut out = Vec::new(); | |
267 | reader.read_to_end(&mut out)?; | |
268 | Ok(out) | |
269 | } | |
270 | ||
512f780a WB |
271 | pub fn user_caps(&self) -> Result<UserCaps, Error> { |
272 | UserCaps::new(self) | |
273 | } | |
274 | } |