/rust/registry/src/index.crates.io-6f17d22bba15001f/rustix-linux-procfs-0.1.1/src/lib.rs
Line | Count | Source (jump to first uncovered line) |
1 | | #![doc = include_str!("../README.md")] |
2 | | #![cfg(any(target_os = "linux", target_os = "android"))] |
3 | | #![no_std] |
4 | | |
5 | | use core::mem::MaybeUninit; |
6 | | use once_cell::sync::OnceCell; |
7 | | use rustix::cstr; |
8 | | use rustix::fd::{AsFd, BorrowedFd, OwnedFd}; |
9 | | use rustix::ffi::CStr; |
10 | | use rustix::fs::{ |
11 | | fstat, fstatfs, major, openat, readlinkat_raw, renameat, seek, FileType, FsWord, Mode, OFlags, |
12 | | RawDir, SeekFrom, Stat, CWD, PROC_SUPER_MAGIC, |
13 | | }; |
14 | | use rustix::io; |
15 | | use rustix::path::DecInt; |
16 | | |
17 | | /// Linux's procfs always uses inode 1 for its root directory. |
18 | | const PROC_ROOT_INO: u64 = 1; |
19 | | |
20 | | // Identify an entry within "/proc", to determine which anomalies to check for. |
21 | | #[derive(Copy, Clone, Debug)] |
22 | | enum Kind { |
23 | | Proc, |
24 | | Pid, |
25 | | Fd, |
26 | | File, |
27 | | Symlink, |
28 | | } |
29 | | |
30 | | /// Check a subdirectory of "/proc" for anomalies. |
31 | 0 | fn check_proc_entry( |
32 | 0 | kind: Kind, |
33 | 0 | entry: BorrowedFd<'_>, |
34 | 0 | proc_stat: Option<&Stat>, |
35 | 0 | ) -> io::Result<Stat> { |
36 | 0 | let entry_stat = fstat(entry)?; |
37 | 0 | check_proc_entry_with_stat(kind, entry, entry_stat, proc_stat) |
38 | 0 | } |
39 | | |
40 | | /// Check a subdirectory of "/proc" for anomalies, using the provided `Stat`. |
41 | 0 | fn check_proc_entry_with_stat( |
42 | 0 | kind: Kind, |
43 | 0 | entry: BorrowedFd<'_>, |
44 | 0 | entry_stat: Stat, |
45 | 0 | proc_stat: Option<&Stat>, |
46 | 0 | ) -> io::Result<Stat> { |
47 | 0 | // Check the filesystem magic. |
48 | 0 | check_procfs(entry)?; |
49 | | |
50 | 0 | match kind { |
51 | 0 | Kind::Proc => check_proc_root(entry, &entry_stat)?, |
52 | 0 | Kind::Pid | Kind::Fd => check_proc_subdir(entry, &entry_stat, proc_stat)?, |
53 | 0 | Kind::File => check_proc_file(&entry_stat, proc_stat)?, |
54 | 0 | Kind::Symlink => check_proc_symlink(&entry_stat, proc_stat)?, |
55 | | } |
56 | | |
57 | | // "/proc" directories are typically mounted r-xr-xr-x. |
58 | | // "/proc/self/fd" is r-x------. Allow them to have fewer permissions, but |
59 | | // not more. |
60 | 0 | match kind { |
61 | 0 | Kind::Symlink => { |
62 | 0 | // On Linux, symlinks don't have their own permissions. |
63 | 0 | } |
64 | | _ => { |
65 | 0 | let expected_mode = if let Kind::Fd = kind { 0o500 } else { 0o555 }; |
66 | 0 | if entry_stat.st_mode & 0o777 & !expected_mode != 0 { |
67 | 0 | return Err(io::Errno::NOTSUP); |
68 | 0 | } |
69 | | } |
70 | | } |
71 | | |
72 | 0 | match kind { |
73 | | Kind::Fd => { |
74 | | // Check that the "/proc/self/fd" directory doesn't have any |
75 | | // extraneous links into it (which might include unexpected |
76 | | // subdirectories). |
77 | 0 | if entry_stat.st_nlink != 2 { |
78 | 0 | return Err(io::Errno::NOTSUP); |
79 | 0 | } |
80 | | } |
81 | | Kind::Pid | Kind::Proc => { |
82 | | // Check that the "/proc" and "/proc/self" directories aren't |
83 | | // empty. |
84 | 0 | if entry_stat.st_nlink <= 2 { |
85 | 0 | return Err(io::Errno::NOTSUP); |
86 | 0 | } |
87 | | } |
88 | | Kind::File => { |
89 | | // Check that files in procfs don't have extraneous hard links to |
90 | | // them (which might indicate hard links to other things). |
91 | 0 | if entry_stat.st_nlink != 1 { |
92 | 0 | return Err(io::Errno::NOTSUP); |
93 | 0 | } |
94 | | } |
95 | | Kind::Symlink => { |
96 | | // Check that symlinks in procfs don't have extraneous hard links |
97 | | // to them (which might indicate hard links to other things). |
98 | 0 | if entry_stat.st_nlink != 1 { |
99 | 0 | return Err(io::Errno::NOTSUP); |
100 | 0 | } |
101 | | } |
102 | | } |
103 | | |
104 | 0 | Ok(entry_stat) |
105 | 0 | } |
106 | | |
107 | 0 | fn check_proc_root(entry: BorrowedFd<'_>, stat: &Stat) -> io::Result<()> { |
108 | 0 | // We use `O_DIRECTORY` for proc directories, so open should fail if we |
109 | 0 | // don't get a directory when we expect one. |
110 | 0 | assert_eq!(FileType::from_raw_mode(stat.st_mode), FileType::Directory); |
111 | | |
112 | | // Check the root inode number. |
113 | 0 | if stat.st_ino != PROC_ROOT_INO { |
114 | 0 | return Err(io::Errno::NOTSUP); |
115 | 0 | } |
116 | 0 |
|
117 | 0 | // Proc is a non-device filesystem, so check for major number 0. |
118 | 0 | // <https://www.kernel.org/doc/Documentation/admin-guide/devices.txt> |
119 | 0 | if major(stat.st_dev) != 0 { |
120 | 0 | return Err(io::Errno::NOTSUP); |
121 | 0 | } |
122 | 0 |
|
123 | 0 | // Check that "/proc" is a mountpoint. |
124 | 0 | if !is_mountpoint(entry) { |
125 | 0 | return Err(io::Errno::NOTSUP); |
126 | 0 | } |
127 | 0 |
|
128 | 0 | Ok(()) |
129 | 0 | } |
130 | | |
131 | 0 | fn check_proc_subdir( |
132 | 0 | entry: BorrowedFd<'_>, |
133 | 0 | stat: &Stat, |
134 | 0 | proc_stat: Option<&Stat>, |
135 | 0 | ) -> io::Result<()> { |
136 | 0 | // We use `O_DIRECTORY` for proc directories, so open should fail if we |
137 | 0 | // don't get a directory when we expect one. |
138 | 0 | assert_eq!(FileType::from_raw_mode(stat.st_mode), FileType::Directory); |
139 | | |
140 | 0 | check_proc_nonroot(stat, proc_stat)?; |
141 | | |
142 | | // Check that subdirectories of "/proc" are not mount points. |
143 | 0 | if is_mountpoint(entry) { |
144 | 0 | return Err(io::Errno::NOTSUP); |
145 | 0 | } |
146 | 0 |
|
147 | 0 | Ok(()) |
148 | 0 | } |
149 | | |
150 | 0 | fn check_proc_file(stat: &Stat, proc_stat: Option<&Stat>) -> io::Result<()> { |
151 | 0 | // Check that we have a regular file. |
152 | 0 | if FileType::from_raw_mode(stat.st_mode) != FileType::RegularFile { |
153 | 0 | return Err(io::Errno::NOTSUP); |
154 | 0 | } |
155 | 0 |
|
156 | 0 | check_proc_nonroot(stat, proc_stat)?; |
157 | | |
158 | 0 | Ok(()) |
159 | 0 | } |
160 | | |
161 | 0 | fn check_proc_symlink(stat: &Stat, proc_stat: Option<&Stat>) -> io::Result<()> { |
162 | 0 | // Check that we have a symbolic link. |
163 | 0 | if FileType::from_raw_mode(stat.st_mode) != FileType::Symlink { |
164 | 0 | return Err(io::Errno::NOTSUP); |
165 | 0 | } |
166 | 0 |
|
167 | 0 | check_proc_nonroot(stat, proc_stat)?; |
168 | | |
169 | 0 | Ok(()) |
170 | 0 | } |
171 | | |
172 | 0 | fn check_proc_nonroot(stat: &Stat, proc_stat: Option<&Stat>) -> io::Result<()> { |
173 | 0 | // Check that we haven't been linked back to the root of "/proc". |
174 | 0 | if stat.st_ino == PROC_ROOT_INO { |
175 | 0 | return Err(io::Errno::NOTSUP); |
176 | 0 | } |
177 | 0 |
|
178 | 0 | // Check that we're still in procfs. |
179 | 0 | if stat.st_dev != proc_stat.unwrap().st_dev { |
180 | 0 | return Err(io::Errno::NOTSUP); |
181 | 0 | } |
182 | 0 |
|
183 | 0 | Ok(()) |
184 | 0 | } |
185 | | |
186 | | /// Check that `file` is opened on a `procfs` filesystem. |
187 | 0 | fn check_procfs(file: BorrowedFd<'_>) -> io::Result<()> { |
188 | 0 | let statfs = fstatfs(file)?; |
189 | 0 | let f_type = statfs.f_type; |
190 | 0 | if f_type != FsWord::from(PROC_SUPER_MAGIC) { |
191 | 0 | return Err(io::Errno::NOTSUP); |
192 | 0 | } |
193 | 0 |
|
194 | 0 | Ok(()) |
195 | 0 | } |
196 | | |
197 | | /// Check whether the given directory handle is a mount point. |
198 | 0 | fn is_mountpoint(file: BorrowedFd<'_>) -> bool { |
199 | | // We use a `renameat` call that would otherwise fail, but which fails with |
200 | | // `XDEV` first if it would cross a mount point. |
201 | 0 | let err = renameat(file, cstr!("../."), file, cstr!(".")).unwrap_err(); |
202 | 0 | match err { |
203 | 0 | io::Errno::XDEV => true, // the rename failed due to crossing a mount point |
204 | 0 | io::Errno::BUSY => false, // the rename failed normally |
205 | 0 | _ => panic!("Unexpected error from `renameat`: {:?}", err), |
206 | | } |
207 | 0 | } |
208 | | |
209 | | /// Open a directory in `/proc`, mapping all errors to `io::Errno::NOTSUP`. |
210 | 0 | fn proc_opendirat<P: rustix::path::Arg, Fd: AsFd>(dirfd: Fd, path: P) -> io::Result<OwnedFd> { |
211 | 0 | // We don't add `PATH` here because that disables `DIRECTORY`. And we don't |
212 | 0 | // add `NOATIME` for the same reason as the comment in `open_and_check_file`. |
213 | 0 | let oflags = |
214 | 0 | OFlags::RDONLY | OFlags::NOFOLLOW | OFlags::DIRECTORY | OFlags::CLOEXEC | OFlags::NOCTTY; |
215 | 0 | openat(dirfd, path, oflags, Mode::empty()).map_err(|_err| io::Errno::NOTSUP) Unexecuted instantiation: rustix_linux_procfs::proc_opendirat::<&core::ffi::c_str::CStr, std::os::fd::owned::BorrowedFd>::{closure#0} Unexecuted instantiation: rustix_linux_procfs::proc_opendirat::<&[u8], std::os::fd::owned::BorrowedFd>::{closure#0} |
216 | 0 | } Unexecuted instantiation: rustix_linux_procfs::proc_opendirat::<&core::ffi::c_str::CStr, std::os::fd::owned::BorrowedFd> Unexecuted instantiation: rustix_linux_procfs::proc_opendirat::<&[u8], std::os::fd::owned::BorrowedFd> |
217 | | |
218 | | /// Returns a handle to Linux's `/proc` directory. |
219 | | /// |
220 | | /// This ensures that `/proc` is procfs, that nothing is mounted on top of it, |
221 | | /// and that it looks normal. It also returns the `Stat` of `/proc`. |
222 | | /// |
223 | | /// # References |
224 | | /// - [Linux] |
225 | | /// |
226 | | /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html |
227 | 0 | fn proc() -> io::Result<(BorrowedFd<'static>, &'static Stat)> { |
228 | | static PROC: StaticFd = StaticFd::new(); |
229 | | |
230 | | // `OnceBox` is “racy” in that the initialization function may run |
231 | | // multiple times. We're ok with that, since the initialization function |
232 | | // has no side effects. |
233 | 0 | PROC.get_or_try_init(|| { |
234 | | // Open "/proc". |
235 | 0 | let proc = proc_opendirat(CWD, cstr!("/proc"))?; |
236 | 0 | let proc_stat = |
237 | 0 | check_proc_entry(Kind::Proc, proc.as_fd(), None).map_err(|_err| io::Errno::NOTSUP)?; |
238 | | |
239 | 0 | Ok(new_static_fd(proc, proc_stat)) |
240 | 0 | }) |
241 | 0 | .map(|(fd, stat)| (fd.as_fd(), stat)) |
242 | 0 | } |
243 | | |
244 | | /// Returns a handle to Linux's `/proc/self` directory. |
245 | | /// |
246 | | /// This ensures that `/proc/self` is procfs, that nothing is mounted on top of |
247 | | /// it, and that it looks normal. It also returns the `Stat` of `/proc/self`. |
248 | | /// |
249 | | /// # References |
250 | | /// - [Linux] |
251 | | /// |
252 | | /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html |
253 | | #[allow(unsafe_code)] |
254 | 0 | fn proc_self() -> io::Result<(BorrowedFd<'static>, &'static Stat)> { |
255 | | static PROC_SELF: StaticFd = StaticFd::new(); |
256 | | |
257 | | // The init function here may run multiple times; see above. |
258 | 0 | PROC_SELF |
259 | 0 | .get_or_try_init(|| { |
260 | 0 | let (proc, proc_stat) = proc()?; |
261 | | |
262 | | // `getpid` would return our pid in our own pid namespace, so |
263 | | // instead use `readlink` on the `self` symlink to learn our pid in |
264 | | // the procfs namespace. |
265 | 0 | let self_symlink = open_and_check_file(proc, proc_stat, cstr!("self"), Kind::Symlink)?; |
266 | 0 | let mut buf = [MaybeUninit::<u8>::uninit(); 20]; |
267 | 0 | let (init, _uninit) = readlinkat_raw(self_symlink, cstr!(""), &mut buf)?; |
268 | 0 | let pid: &[u8] = unsafe { core::mem::transmute(init) }; |
269 | | |
270 | | // Open "/proc/self". Use our pid to compute the name rather than |
271 | | // literally using "self", as "self" is a symlink. |
272 | 0 | let proc_self = proc_opendirat(proc, pid)?; |
273 | 0 | let proc_self_stat = check_proc_entry(Kind::Pid, proc_self.as_fd(), Some(proc_stat)) |
274 | 0 | .map_err(|_err| io::Errno::NOTSUP)?; |
275 | | |
276 | 0 | Ok(new_static_fd(proc_self, proc_self_stat)) |
277 | 0 | }) |
278 | 0 | .map(|(owned, stat)| (owned.as_fd(), stat)) |
279 | 0 | } |
280 | | |
281 | | /// Returns a handle to Linux's `/proc/self/fd` directory. |
282 | | /// |
283 | | /// This ensures that `/proc/self/fd` is `procfs`, that nothing is mounted on |
284 | | /// top of it, and that it looks normal. |
285 | | /// |
286 | | /// # References |
287 | | /// - [Linux] |
288 | | /// |
289 | | /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html |
290 | | #[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] |
291 | 0 | pub fn proc_self_fd() -> io::Result<BorrowedFd<'static>> { |
292 | | static PROC_SELF_FD: StaticFd = StaticFd::new(); |
293 | | |
294 | | // The init function here may run multiple times; see above. |
295 | 0 | PROC_SELF_FD |
296 | 0 | .get_or_try_init(|| { |
297 | 0 | let (_, proc_stat) = proc()?; |
298 | | |
299 | 0 | let (proc_self, _proc_self_stat) = proc_self()?; |
300 | | |
301 | | // Open "/proc/self/fd". |
302 | 0 | let proc_self_fd = proc_opendirat(proc_self, cstr!("fd"))?; |
303 | 0 | let proc_self_fd_stat = |
304 | 0 | check_proc_entry(Kind::Fd, proc_self_fd.as_fd(), Some(proc_stat)) |
305 | 0 | .map_err(|_err| io::Errno::NOTSUP)?; |
306 | | |
307 | 0 | Ok(new_static_fd(proc_self_fd, proc_self_fd_stat)) |
308 | 0 | }) |
309 | 0 | .map(|(owned, _stat)| owned.as_fd()) |
310 | 0 | } |
311 | | |
312 | | type StaticFd = OnceCell<(OwnedFd, Stat)>; |
313 | | |
314 | | #[inline] |
315 | 0 | fn new_static_fd(fd: OwnedFd, stat: Stat) -> (OwnedFd, Stat) { |
316 | 0 | (fd, stat) |
317 | 0 | } |
318 | | |
319 | | /// Returns a handle to Linux's `/proc/self/fdinfo` directory. |
320 | | /// |
321 | | /// This ensures that `/proc/self/fdinfo` is `procfs`, that nothing is mounted |
322 | | /// on top of it, and that it looks normal. It also returns the `Stat` of |
323 | | /// `/proc/self/fd`. |
324 | | /// |
325 | | /// # References |
326 | | /// - [Linux] |
327 | | /// |
328 | | /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html |
329 | 0 | fn proc_self_fdinfo() -> io::Result<(BorrowedFd<'static>, &'static Stat)> { |
330 | | static PROC_SELF_FDINFO: StaticFd = StaticFd::new(); |
331 | | |
332 | 0 | PROC_SELF_FDINFO |
333 | 0 | .get_or_try_init(|| { |
334 | 0 | let (_, proc_stat) = proc()?; |
335 | | |
336 | 0 | let (proc_self, _proc_self_stat) = proc_self()?; |
337 | | |
338 | | // Open "/proc/self/fdinfo". |
339 | 0 | let proc_self_fdinfo = proc_opendirat(proc_self, cstr!("fdinfo"))?; |
340 | 0 | let proc_self_fdinfo_stat = |
341 | 0 | check_proc_entry(Kind::Fd, proc_self_fdinfo.as_fd(), Some(proc_stat)) |
342 | 0 | .map_err(|_err| io::Errno::NOTSUP)?; |
343 | | |
344 | 0 | Ok((proc_self_fdinfo, proc_self_fdinfo_stat)) |
345 | 0 | }) |
346 | 0 | .map(|(owned, stat)| (owned.as_fd(), stat)) |
347 | 0 | } |
348 | | |
349 | | /// Returns a handle to a Linux `/proc/self/fdinfo/<fd>` file. |
350 | | /// |
351 | | /// This ensures that `/proc/self/fdinfo/<fd>` is `procfs`, that nothing is |
352 | | /// mounted on top of it, and that it looks normal. |
353 | | /// |
354 | | /// # References |
355 | | /// - [Linux] |
356 | | /// |
357 | | /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html |
358 | | #[inline] |
359 | | #[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] |
360 | 0 | pub fn proc_self_fdinfo_fd<Fd: AsFd>(fd: Fd) -> io::Result<OwnedFd> { |
361 | 0 | _proc_self_fdinfo(fd.as_fd()) |
362 | 0 | } |
363 | | |
364 | 0 | fn _proc_self_fdinfo(fd: BorrowedFd<'_>) -> io::Result<OwnedFd> { |
365 | 0 | let (proc_self_fdinfo, proc_self_fdinfo_stat) = proc_self_fdinfo()?; |
366 | 0 | let fd_str = DecInt::from_fd(fd); |
367 | 0 | open_and_check_file( |
368 | 0 | proc_self_fdinfo, |
369 | 0 | proc_self_fdinfo_stat, |
370 | 0 | fd_str.as_c_str(), |
371 | 0 | Kind::File, |
372 | 0 | ) |
373 | 0 | } |
374 | | |
375 | | /// Returns a handle to a Linux `/proc/self/pagemap` file. |
376 | | /// |
377 | | /// This ensures that `/proc/self/pagemap` is `procfs`, that nothing is |
378 | | /// mounted on top of it, and that it looks normal. |
379 | | /// |
380 | | /// # References |
381 | | /// - [Linux] |
382 | | /// - [Linux pagemap] |
383 | | /// |
384 | | /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html |
385 | | /// [Linux pagemap]: https://www.kernel.org/doc/Documentation/vm/pagemap.txt |
386 | | #[inline] |
387 | | #[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] |
388 | 0 | pub fn proc_self_pagemap() -> io::Result<OwnedFd> { |
389 | 0 | proc_self_file(cstr!("pagemap")) |
390 | 0 | } |
391 | | |
392 | | /// Returns a handle to a Linux `/proc/self/maps` file. |
393 | | /// |
394 | | /// This ensures that `/proc/self/maps` is `procfs`, that nothing is |
395 | | /// mounted on top of it, and that it looks normal. |
396 | | /// |
397 | | /// # References |
398 | | /// - [Linux] |
399 | | /// |
400 | | /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html |
401 | | #[inline] |
402 | | #[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] |
403 | 0 | pub fn proc_self_maps() -> io::Result<OwnedFd> { |
404 | 0 | proc_self_file(cstr!("maps")) |
405 | 0 | } |
406 | | |
407 | | /// Returns a handle to a Linux `/proc/self/status` file. |
408 | | /// |
409 | | /// This ensures that `/proc/self/status` is `procfs`, that nothing is |
410 | | /// mounted on top of it, and that it looks normal. |
411 | | /// |
412 | | /// # References |
413 | | /// - [Linux] |
414 | | /// |
415 | | /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html |
416 | | #[inline] |
417 | | #[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] |
418 | 0 | pub fn proc_self_status() -> io::Result<OwnedFd> { |
419 | 0 | proc_self_file(cstr!("status")) |
420 | 0 | } |
421 | | |
422 | | /// Open a file under `/proc/self`. |
423 | 0 | fn proc_self_file(name: &CStr) -> io::Result<OwnedFd> { |
424 | 0 | let (proc_self, proc_self_stat) = proc_self()?; |
425 | 0 | open_and_check_file(proc_self, proc_self_stat, name, Kind::File) |
426 | 0 | } |
427 | | |
428 | | /// Open a procfs file within in `dir` and check it for bind mounts. |
429 | 0 | fn open_and_check_file( |
430 | 0 | dir: BorrowedFd<'_>, |
431 | 0 | dir_stat: &Stat, |
432 | 0 | name: &CStr, |
433 | 0 | kind: Kind, |
434 | 0 | ) -> io::Result<OwnedFd> { |
435 | 0 | let (_, proc_stat) = proc()?; |
436 | | |
437 | | // Don't use `NOATIME`, because it [requires us to own the file], and when |
438 | | // a process sets itself non-dumpable Linux changes the user:group of its |
439 | | // `/proc/<pid>` files [to root:root]. |
440 | | // |
441 | | // [requires us to own the file]: https://man7.org/linux/man-pages/man2/openat.2.html |
442 | | // [to root:root]: https://man7.org/linux/man-pages/man5/proc.5.html |
443 | 0 | let mut oflags = OFlags::RDONLY | OFlags::CLOEXEC | OFlags::NOFOLLOW | OFlags::NOCTTY; |
444 | 0 | if let Kind::Symlink = kind { |
445 | 0 | // Open symlinks with `O_PATH`. |
446 | 0 | oflags |= OFlags::PATH; |
447 | 0 | } |
448 | 0 | let file = openat(dir, name, oflags, Mode::empty()).map_err(|_err| io::Errno::NOTSUP)?; |
449 | 0 | let file_stat = fstat(&file)?; |
450 | | |
451 | | // `is_mountpoint` only works on directory mount points, not file mount |
452 | | // points. To detect file mount points, scan the parent directory to see if |
453 | | // we can find a regular file with an inode and name that matches the file |
454 | | // we just opened. If we can't find it, there could be a file bind mount on |
455 | | // top of the file we want. |
456 | | // |
457 | | // TODO: With Linux 5.8 we might be able to use `statx` and |
458 | | // `STATX_ATTR_MOUNT_ROOT` to detect mountpoints directly instead of doing |
459 | | // this scanning. |
460 | | |
461 | 0 | let expected_type = match kind { |
462 | 0 | Kind::File => FileType::RegularFile, |
463 | 0 | Kind::Symlink => FileType::Symlink, |
464 | 0 | _ => unreachable!(), |
465 | | }; |
466 | | |
467 | 0 | let mut found_file = false; |
468 | 0 | let mut found_dot = false; |
469 | 0 |
|
470 | 0 | // Open a new fd, so that if we're called on multiple threads, they don't |
471 | 0 | // share a seek position. |
472 | 0 | let oflags = |
473 | 0 | OFlags::RDONLY | OFlags::CLOEXEC | OFlags::NOFOLLOW | OFlags::NOCTTY | OFlags::DIRECTORY; |
474 | 0 | let dir = openat(dir, cstr!("."), oflags, Mode::empty()).map_err(|_err| io::Errno::NOTSUP)?; |
475 | 0 | let check_dir_stat = fstat(&dir)?; |
476 | 0 | if check_dir_stat.st_dev != dir_stat.st_dev || check_dir_stat.st_ino != dir_stat.st_ino { |
477 | 0 | return Err(io::Errno::NOTSUP); |
478 | 0 | } |
479 | 0 |
|
480 | 0 | // Position the directory iteration at the start. |
481 | 0 | seek(&dir, SeekFrom::Start(0))?; |
482 | | |
483 | 0 | let mut buf = [MaybeUninit::uninit(); 2048]; |
484 | 0 | let mut iter = RawDir::new(dir, &mut buf); |
485 | 0 | while let Some(entry) = iter.next() { |
486 | 0 | let entry = entry.map_err(|_err| io::Errno::NOTSUP)?; |
487 | 0 | if entry.ino() == file_stat.st_ino |
488 | 0 | && entry.file_type() == expected_type |
489 | 0 | && entry.file_name() == name |
490 | | { |
491 | | // We found the file. Proceed to check the file handle. |
492 | 0 | let _ = check_proc_entry_with_stat(kind, file.as_fd(), file_stat, Some(proc_stat))?; |
493 | | |
494 | 0 | found_file = true; |
495 | 0 | } else if entry.ino() == dir_stat.st_ino |
496 | 0 | && entry.file_type() == FileType::Directory |
497 | 0 | && entry.file_name() == cstr!(".") |
498 | 0 | { |
499 | 0 | // We found ".", and it's the right ".". |
500 | 0 | found_dot = true; |
501 | 0 | } |
502 | | } |
503 | | |
504 | 0 | if found_file && found_dot { |
505 | 0 | Ok(file) |
506 | | } else { |
507 | 0 | Err(io::Errno::NOTSUP) |
508 | | } |
509 | 0 | } |