1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
| | // SPDX-License-Identifier: EUPL-1.2+
// SPDX-FileCopyrightText: 2022 Alyssa Ross <hi@alyssa.is>
use std::ffi::CString;
use std::fs::{read_link, File};
use std::io::{Error, Result};
use std::os::raw::c_char;
use std::os::unix::prelude::*;
use std::path::{Path, PathBuf};
extern "C" {
fn openat_path_resolve_in_root(dirfd: BorrowedFd, pathname: *const c_char) -> Option<OwnedFd>;
}
// We can't make a const Path yet.
// https://github.com/rust-lang/rust/issues/102619
const PROC_FD_PATH: &str = "/proc/self/fd";
pub trait OwnedFdExt: AsFd + AsRawFd + Sized {
/// Returns the path in proc(5) that corresponds to this file descriptor.
fn fd_path(&self) -> PathBuf {
let mut path = PathBuf::from(PROC_FD_PATH);
path.push(self.as_raw_fd().to_string());
path
}
/// Returns the path to this file, according to the magic link in proc(5).
fn path(&self) -> Result<PathBuf> {
read_link(self.fd_path())
}
}
impl OwnedFdExt for OwnedFd {}
/// An `O_PATH` file descriptor.
#[derive(Debug)]
#[repr(transparent)]
pub struct Root(OwnedFd);
fn c_string(path: impl Into<PathBuf>) -> CString {
let bytes = path.into().into_os_string().into_vec();
CString::new(bytes).unwrap()
}
impl Root {
/// Panics if `path` contains NUL bytes.
pub fn open(path: impl AsRef<Path>) -> Result<Self> {
Ok(Self(OwnedFd::from(File::open(path)?)))
}
/// Resolves `path` as if the directory represented by `self` was the root directory
/// (including for symlink resolution). The returned file descriptor will not have
/// the `O_CLOEXEC` flag set.
///
/// Panics if `path` contains NUL bytes.
pub fn resolve_no_cloexec(&self, path: impl Into<PathBuf>) -> Result<OwnedFd> {
let c_path = c_string(path);
unsafe { openat_path_resolve_in_root(self.as_fd(), c_path.as_ptr()) }
.ok_or_else(Error::last_os_error)
}
}
impl AsFd for Root {
fn as_fd(&self) -> BorrowedFd {
self.0.as_fd()
}
}
|