use std::os::fd::RawFd;
use btoi::btoi;
use libc::pid_t;
use memchr::{
arch::all::{is_equal, is_prefix},
memchr,
};
use nix::{errno::Errno, unistd::Pid};
use crate::path::{XPath, XPathBuf};
const FD: &[u8] = b"/fd/";
const PROC: &[u8] = b"/proc/";
const TASK: &[u8] = b"/task/";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProcMagic {
Fd {
pid: Pid,
fd: RawFd,
},
Cwd {
pid: Pid,
},
Root {
pid: Pid,
},
Exe {
pid: Pid,
},
Ns {
pid: Pid,
kind: NsKind,
},
}
impl ProcMagic {
pub fn check_link(
expected_pid: Pid,
path: &XPath,
restrict_magiclinks: bool,
) -> Result<Option<ProcMagic>, Errno> {
if !path.is_proc() {
return Ok(None); }
let after_proc = &path.as_bytes()[PROC.len()..];
let pid_sep_index = memchr(b'/', after_proc).unwrap_or(after_proc.len());
let pid_component = &after_proc[..pid_sep_index];
if pid_component.is_empty() || pid_component.iter().any(|c| !c.is_ascii_digit()) {
return Ok(None); }
let mut current_pid = if restrict_magiclinks {
let parsed = bytes_to_pid(pid_component)?;
if parsed != expected_pid {
return Err(Errno::EACCES);
}
parsed
} else {
bytes_to_pid(pid_component)?
};
let mut remainder = &after_proc[pid_sep_index..];
if is_prefix(remainder, TASK) {
let after_task = &remainder[TASK.len()..]; let tid_end = memchr(b'/', after_task).unwrap_or(after_task.len());
if tid_end == 0 {
return Ok(None); }
current_pid = bytes_to_pid(&after_task[..tid_end])?;
remainder = &after_task[tid_end..]; }
if is_prefix(remainder, FD) {
let fd_digits = &remainder[FD.len()..];
if fd_digits.is_empty() || fd_digits.iter().any(|c| !c.is_ascii_digit()) {
return Ok(None);
}
let fd_value = bytes_to_fd(fd_digits)?;
return Ok(Some(Self::Fd {
pid: current_pid,
fd: fd_value,
}));
}
if is_equal(remainder, b"/cwd") {
return Ok(Some(Self::Cwd { pid: current_pid }));
}
if is_equal(remainder, b"/root") {
return Ok(Some(Self::Root { pid: current_pid }));
}
if is_equal(remainder, b"/exe") {
return Ok(Some(Self::Exe { pid: current_pid }));
}
if remainder.starts_with(b"/ns/") {
let name = &remainder[b"/ns/".len()..];
if !name.is_empty() && memchr::memchr(b'/', name).is_none() {
let kind = NsKind::try_from(name)?;
return Ok(Some(ProcMagic::Ns {
pid: current_pid,
kind,
}));
}
}
Ok(None)
}
pub fn link_path(self) -> XPathBuf {
match self {
Self::Fd { pid, fd } => {
let mut sym = XPathBuf::from_pid(pid);
sym.push(b"fd");
sym.push_fd(fd);
sym
}
Self::Cwd { pid } => {
let mut sym = XPathBuf::from_pid(pid);
sym.push(b"cwd");
sym
}
Self::Root { pid } => {
let mut sym = XPathBuf::from_pid(pid);
sym.push(b"root");
sym
}
Self::Exe { pid } => {
let mut sym = XPathBuf::from_pid(pid);
sym.push(b"exe");
sym
}
Self::Ns { pid, kind } => {
let mut sym = XPathBuf::from_pid(pid);
sym.push(b"ns");
sym.push(match kind {
NsKind::Cgroup => b"cgroup",
NsKind::Ipc => b"ipc",
NsKind::Mnt => b"mnt",
NsKind::Net => b"net",
NsKind::Pid => b"pid",
NsKind::PidForChildren => b"pid_for_children",
NsKind::Time => b"time",
NsKind::TimeForChildren => b"time_for_children",
NsKind::User => b"user",
NsKind::Uts => b"uts",
});
sym
}
}
}
#[inline]
pub fn link_fd(self) -> Result<RawFd, Errno> {
match self {
Self::Fd { fd, .. } => Ok(fd),
Self::Cwd { .. } => Ok(libc::AT_FDCWD),
Self::Root { .. } => Ok(-1),
Self::Exe { .. } => Ok(-2),
Self::Ns { .. } => Err(Errno::EINVAL),
}
}
#[inline]
pub fn want_dir(self) -> bool {
matches!(self, Self::Cwd { .. } | Self::Root { .. })
}
pub fn base(self) -> Option<XPathBuf> {
match self {
Self::Fd { fd, .. } => Some(XPathBuf::from_fd(fd)),
Self::Ns { kind, .. } => Some(XPathBuf::from(kind)),
Self::Cwd { .. } | Self::Root { .. } | Self::Exe { .. } => None,
}
}
pub fn pid(self) -> Pid {
match self {
Self::Fd { pid, .. } => pid,
Self::Cwd { pid } => pid,
Self::Root { pid } => pid,
Self::Exe { pid } => pid,
Self::Ns { pid, .. } => pid,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NsKind {
Cgroup,
Ipc,
Mnt,
Net,
Pid,
PidForChildren,
Time,
TimeForChildren,
User,
Uts,
}
impl From<NsKind> for XPathBuf {
fn from(kind: NsKind) -> Self {
match kind {
NsKind::Cgroup => "cgroup",
NsKind::Ipc => "ipc",
NsKind::Mnt => "mnt",
NsKind::Net => "net",
NsKind::Pid => "pid",
NsKind::PidForChildren => "pid_for_children",
NsKind::Time => "time",
NsKind::TimeForChildren => "time_for_children",
NsKind::User => "user",
NsKind::Uts => "uts",
}
.into()
}
}
impl TryFrom<&[u8]> for NsKind {
type Error = Errno;
fn try_from(name: &[u8]) -> Result<Self, Self::Error> {
match name {
b"net" => Ok(Self::Net),
b"mnt" => Ok(Self::Mnt),
b"pid" => Ok(Self::Pid),
b"user" => Ok(Self::User),
b"uts" => Ok(Self::Uts),
b"ipc" => Ok(Self::Ipc),
b"cgroup" => Ok(Self::Cgroup),
b"time" => Ok(Self::Time),
b"pid_for_children" => Ok(Self::PidForChildren),
b"time_for_children" => Ok(Self::TimeForChildren),
_ => Err(Errno::EOPNOTSUPP),
}
}
}
fn bytes_to_pid(bytes: &[u8]) -> Result<Pid, Errno> {
btoi::<pid_t>(bytes)
.map(Pid::from_raw)
.or(Err(Errno::EINVAL))
}
fn bytes_to_fd(bytes: &[u8]) -> Result<RawFd, Errno> {
btoi::<RawFd>(bytes).or(Err(Errno::EINVAL))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
path::{XPath, XPathBuf},
xpath,
};
fn assert_ok_some(result: Result<Option<ProcMagic>, Errno>, expected: ProcMagic) {
assert_eq!(result, Ok(Some(expected)));
}
fn assert_ok_none(result: Result<Option<ProcMagic>, Errno>) {
assert_eq!(result, Ok(None));
}
fn assert_err(result: Result<Option<ProcMagic>, Errno>, e: Errno) {
assert_eq!(result, Err(e));
}
#[test]
fn test_check_link_port_cwd_restricted_this() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/cwd"), true),
ProcMagic::Cwd { pid: this },
);
}
#[test]
fn test_check_link_port_exe_restricted_this() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/exe"), true),
ProcMagic::Exe { pid: this },
);
}
#[test]
fn test_check_link_port_root_restricted_this() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/root"), true),
ProcMagic::Root { pid: this },
);
}
#[test]
fn test_check_link_port_fd_dir_restricted_that_eacces() {
let this = Pid::this();
let that = Pid::from_raw(1);
assert_err(
ProcMagic::check_link(this, &xpath!("/proc/{that}/fd"), true),
Errno::EACCES,
);
}
#[test]
fn test_check_link_port_fd_dir_unrestricted_that_none() {
let this = Pid::this();
let that = Pid::from_raw(1);
assert_ok_none(ProcMagic::check_link(
this,
&xpath!("/proc/{that}/fd"),
false,
));
}
#[test]
fn test_check_link_port_fd_dir_restricted_this_none() {
let this = Pid::this();
assert_ok_none(ProcMagic::check_link(
this,
&xpath!("/proc/{this}/fd"),
true,
));
}
#[test]
fn test_check_link_port_fd0_restricted_this() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/fd/0"), true),
ProcMagic::Fd { pid: this, fd: 0 },
);
}
#[test]
fn test_check_link_port_fd42_restricted_this() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/fd/42"), true),
ProcMagic::Fd { pid: this, fd: 42 },
);
}
#[test]
fn test_check_link_port_fd1984_restricted_this() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/fd/1984"), true),
ProcMagic::Fd {
pid: this,
fd: 1984,
},
);
}
#[test]
fn test_check_link_port_task_tid_fd7_restricted_that_tid() {
let this = Pid::this();
let that = Pid::from_raw(1);
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/task/{that}/fd/7"), true),
ProcMagic::Fd { pid: that, fd: 7 },
);
}
#[test]
fn test_check_link_port_cwd_restricted_that_eacces() {
let this = Pid::this();
let that = Pid::from_raw(1);
assert_err(
ProcMagic::check_link(this, &xpath!("/proc/{that}/cwd"), true),
Errno::EACCES,
);
}
#[test]
fn test_check_link_port_exe_restricted_that_eacces() {
let this = Pid::this();
let that = Pid::from_raw(1);
assert_err(
ProcMagic::check_link(this, &xpath!("/proc/{that}/exe"), true),
Errno::EACCES,
);
}
#[test]
fn test_check_link_port_root_restricted_that_eacces() {
let this = Pid::this();
let that = Pid::from_raw(1);
assert_err(
ProcMagic::check_link(this, &xpath!("/proc/{that}/root"), true),
Errno::EACCES,
);
}
#[test]
fn test_check_link_port_fd0_restricted_that_eacces() {
let this = Pid::this();
let that = Pid::from_raw(1);
assert_err(
ProcMagic::check_link(this, &xpath!("/proc/{that}/fd/0"), true),
Errno::EACCES,
);
}
#[test]
fn test_check_link_port_task_mismatch_restricted_eacces() {
let this = Pid::this();
let that = Pid::from_raw(this.as_raw() + 123);
assert_err(
ProcMagic::check_link(this, &xpath!("/proc/{that}/task/{this}/fd/7"), true),
Errno::EACCES,
);
}
#[test]
fn test_check_link_port_cwd_unrestricted_that_ok() {
let this = Pid::this();
let that = Pid::from_raw(1);
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{that}/cwd"), false),
ProcMagic::Cwd { pid: that },
);
}
#[test]
fn test_check_link_port_exe_unrestricted_that_ok() {
let this = Pid::this();
let that = Pid::from_raw(1);
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{that}/exe"), false),
ProcMagic::Exe { pid: that },
);
}
#[test]
fn test_check_link_port_root_unrestricted_that_ok() {
let this = Pid::this();
let that = Pid::from_raw(1);
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{that}/root"), false),
ProcMagic::Root { pid: that },
);
}
#[test]
fn test_check_link_port_fd0_unrestricted_that_ok() {
let this = Pid::this();
let that = Pid::from_raw(1);
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{that}/fd/0"), false),
ProcMagic::Fd { pid: that, fd: 0 },
);
}
#[test]
fn test_check_link_port_task_unrestricted_ok_tid_becomes_owner() {
let this = Pid::this();
let that = Pid::from_raw(this.as_raw() + 77);
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{that}/task/{this}/fd/7"), false),
ProcMagic::Fd { pid: this, fd: 7 },
);
}
#[test]
fn test_check_link_not_proc_prefix_dev_null() {
let this = Pid::this();
assert_ok_none(ProcMagic::check_link(this, &xpath!("/dev/null"), true));
}
#[test]
fn test_check_link_not_proc_prefix_root() {
let this = Pid::this();
assert_ok_none(ProcMagic::check_link(this, &xpath!("/"), true));
}
#[test]
fn test_check_link_pid_component_non_digits() {
let this = Pid::this();
assert_ok_none(ProcMagic::check_link(this, &xpath!("/proc/abc/cwd"), true));
}
#[test]
fn test_check_link_pid_component_empty() {
let this = Pid::this();
assert_ok_none(ProcMagic::check_link(this, &xpath!("/proc//cwd"), true));
}
#[test]
fn test_check_link_pid_component_mixed_digits() {
let this = Pid::this();
assert_ok_none(ProcMagic::check_link(this, &xpath!("/proc/12a/fd/1"), true));
}
#[test]
fn test_check_link_task_without_tid_trailing_slash() {
let this = Pid::this();
assert_ok_none(ProcMagic::check_link(
this,
&xpath!("/proc/{this}/task/"),
true,
));
}
#[test]
fn test_check_link_task_double_slash_tid_missing() {
let this = Pid::this();
assert_ok_none(ProcMagic::check_link(
this,
&xpath!("/proc/{this}/task//fd/3"),
true,
));
}
#[test]
fn test_check_link_fd_empty_component() {
let this = Pid::this();
assert_ok_none(ProcMagic::check_link(
this,
&xpath!("/proc/{this}/fd/"),
true,
));
}
#[test]
fn test_check_link_fd_non_digit_component() {
let this = Pid::this();
assert_ok_none(ProcMagic::check_link(
this,
&xpath!("/proc/{this}/fd/+1"),
true,
));
}
#[test]
fn test_check_link_fd_letter_component() {
let this = Pid::this();
assert_ok_none(ProcMagic::check_link(
this,
&xpath!("/proc/{this}/fd/a7"),
true,
));
}
#[test]
fn test_check_link_fd_overflow_triggers_einval() {
let this = Pid::this();
let huge = format!("/proc/{}/fd/{}", this.as_raw(), "999999999999999999999999");
let buf = XPathBuf::from(huge.as_str());
let x: &XPath = buf.as_ref();
assert_err(ProcMagic::check_link(this, x, true), Errno::EINVAL);
}
#[test]
fn test_check_link_exact_match_required_for_cwd_extra_segment_none() {
let this = Pid::this();
assert_ok_none(ProcMagic::check_link(
this,
&xpath!("/proc/{this}/cwd/extra"),
true,
));
}
#[test]
fn test_check_link_exact_match_required_for_exe_extra_segment_none() {
let this = Pid::this();
assert_ok_none(ProcMagic::check_link(
this,
&xpath!("/proc/{this}/exe/also"),
true,
));
}
#[test]
fn test_check_link_exact_match_required_for_root_extra_segment_none() {
let this = Pid::this();
assert_ok_none(ProcMagic::check_link(
this,
&xpath!("/proc/{this}/root/too"),
true,
));
}
#[test]
fn test_check_link_fd_value_0() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/fd/0"), true),
ProcMagic::Fd { pid: this, fd: 0 },
);
}
#[test]
fn test_check_link_fd_value_5() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/fd/5"), true),
ProcMagic::Fd { pid: this, fd: 5 },
);
}
#[test]
fn test_check_link_fd_value_9() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/fd/9"), true),
ProcMagic::Fd { pid: this, fd: 9 },
);
}
#[test]
fn test_check_link_fd_value_63() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/fd/63"), true),
ProcMagic::Fd { pid: this, fd: 63 },
);
}
#[test]
fn test_check_link_fd_value_100() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/fd/100"), true),
ProcMagic::Fd { pid: this, fd: 100 },
);
}
#[test]
fn test_check_link_fd_value_255() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/fd/255"), true),
ProcMagic::Fd { pid: this, fd: 255 },
);
}
#[test]
fn test_check_link_fd_task_tid_value_3() {
let this = Pid::this();
let tid = Pid::from_raw(this.as_raw() + 2000);
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/task/{tid}/fd/3"), true),
ProcMagic::Fd { pid: tid, fd: 3 },
);
}
#[test]
fn test_check_link_fd_task_tid_value_11() {
let this = Pid::this();
let tid = Pid::from_raw(this.as_raw() + 2001);
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/task/{tid}/fd/11"), true),
ProcMagic::Fd { pid: tid, fd: 11 },
);
}
#[test]
fn test_check_link_fd_task_tid_value_90() {
let this = Pid::this();
let tid = Pid::from_raw(this.as_raw() + 2002);
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/task/{tid}/fd/90"), true),
ProcMagic::Fd { pid: tid, fd: 90 },
);
}
#[test]
fn test_check_link_ns_net_ok() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/ns/net"), true),
ProcMagic::Ns {
pid: this,
kind: NsKind::Net,
},
);
}
#[test]
fn test_check_link_ns_mnt_ok() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/ns/mnt"), true),
ProcMagic::Ns {
pid: this,
kind: NsKind::Mnt,
},
);
}
#[test]
fn test_check_link_ns_pid_ok() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/ns/pid"), true),
ProcMagic::Ns {
pid: this,
kind: NsKind::Pid,
},
);
}
#[test]
fn test_check_link_ns_user_ok() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/ns/user"), true),
ProcMagic::Ns {
pid: this,
kind: NsKind::User,
},
);
}
#[test]
fn test_check_link_ns_time_for_children_ok() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/ns/time_for_children"), true),
ProcMagic::Ns {
pid: this,
kind: NsKind::TimeForChildren,
},
);
}
#[test]
fn test_check_link_ns_pid_for_children_ok() {
let this = Pid::this();
assert_ok_some(
ProcMagic::check_link(this, &xpath!("/proc/{this}/ns/pid_for_children"), true),
ProcMagic::Ns {
pid: this,
kind: NsKind::PidForChildren,
},
);
}
#[test]
fn test_check_link_ns_unknown_eopnotsupp() {
let this = Pid::this();
assert_err(
ProcMagic::check_link(this, &xpath!("/proc/{this}/ns/foobar"), true),
Errno::EOPNOTSUPP,
);
}
#[test]
fn test_check_link_ns_extra_slash_none() {
let this = Pid::this();
assert_ok_none(ProcMagic::check_link(
this,
&xpath!("/proc/{this}/ns/net/extra"),
true,
));
}
#[test]
fn test_link_path_fd_roundtrip() {
let this = Pid::this();
let m = ProcMagic::Fd { pid: this, fd: 7 };
assert_eq!(m.link_path(), xpath!("{this}/fd/7"));
}
#[test]
fn test_link_path_cwd_roundtrip() {
let this = Pid::this();
assert_eq!(
ProcMagic::Cwd { pid: this }.link_path(),
xpath!("{this}/cwd")
);
}
#[test]
fn test_link_path_root_roundtrip() {
let this = Pid::this();
assert_eq!(
ProcMagic::Root { pid: this }.link_path(),
xpath!("{this}/root")
);
}
#[test]
fn test_link_path_exe_roundtrip() {
let this = Pid::this();
assert_eq!(
ProcMagic::Exe { pid: this }.link_path(),
xpath!("{this}/exe")
);
}
#[test]
fn test_link_path_ns_net_roundtrip() {
let this = Pid::this();
let m = ProcMagic::Ns {
pid: this,
kind: NsKind::Net,
};
assert_eq!(m.link_path(), xpath!("{this}/ns/net"));
}
#[test]
fn test_link_path_ns_uts_roundtrip() {
let this = Pid::this();
let m = ProcMagic::Ns {
pid: this,
kind: NsKind::Uts,
};
assert_eq!(m.link_path(), xpath!("{this}/ns/uts"));
}
#[test]
fn test_link_fd_for_fd_returns_value() {
let this = Pid::this();
assert_eq!(ProcMagic::Fd { pid: this, fd: 3 }.link_fd(), Ok(3));
}
#[test]
fn test_link_fd_for_cwd_returns_at_fdcwd() {
let this = Pid::this();
assert_eq!(ProcMagic::Cwd { pid: this }.link_fd(), Ok(libc::AT_FDCWD));
}
#[test]
fn test_link_fd_for_root_returns_minus_one() {
let this = Pid::this();
assert_eq!(ProcMagic::Root { pid: this }.link_fd(), Ok(-1));
}
#[test]
fn test_link_fd_for_ns_is_einval() {
let this = Pid::this();
assert_eq!(
ProcMagic::Ns {
pid: this,
kind: NsKind::Net
}
.link_fd(),
Err(Errno::EINVAL)
);
}
#[test]
fn test_want_dir_true_for_cwd_and_root() {
let this = Pid::this();
assert!(ProcMagic::Cwd { pid: this }.want_dir());
assert!(ProcMagic::Root { pid: this }.want_dir());
}
#[test]
fn test_want_dir_false_for_fd_exe_ns() {
let this = Pid::this();
assert!(!ProcMagic::Fd { pid: this, fd: 5 }.want_dir());
assert!(!ProcMagic::Exe { pid: this }.want_dir());
assert!(!ProcMagic::Ns {
pid: this,
kind: NsKind::Net
}
.want_dir());
}
#[test]
fn test_base_for_fd_returns_number() {
let this = Pid::this();
assert_eq!(
ProcMagic::Fd { pid: this, fd: 42 }.base(),
Some(XPathBuf::from("42"))
);
}
#[test]
fn test_base_for_ns_returns_kind() {
let this = Pid::this();
assert_eq!(
ProcMagic::Ns {
pid: this,
kind: NsKind::Mnt
}
.base(),
Some(XPathBuf::from("mnt"))
);
}
#[test]
fn test_base_for_cwd_root_exe_is_none() {
let this = Pid::this();
assert_eq!(ProcMagic::Cwd { pid: this }.base(), None);
assert_eq!(ProcMagic::Root { pid: this }.base(), None);
assert_eq!(ProcMagic::Exe { pid: this }.base(), None);
}
#[test]
fn test_pid_for_fd() {
let this = Pid::from_raw(4242);
assert_eq!(ProcMagic::Fd { pid: this, fd: 1 }.pid(), this);
}
#[test]
fn test_pid_for_cwd_root_exe() {
let p = Pid::from_raw(2025);
assert_eq!(ProcMagic::Cwd { pid: p }.pid(), p);
assert_eq!(ProcMagic::Root { pid: p }.pid(), p);
assert_eq!(ProcMagic::Exe { pid: p }.pid(), p);
}
#[test]
fn test_pid_for_ns() {
let p = Pid::from_raw(77);
assert_eq!(
ProcMagic::Ns {
pid: p,
kind: NsKind::User
}
.pid(),
p
);
}
#[test]
fn test_try_from_bytes_known_values_subset() {
assert_eq!(NsKind::try_from(b"net".as_slice()), Ok(NsKind::Net));
assert_eq!(NsKind::try_from(b"mnt".as_slice()), Ok(NsKind::Mnt));
assert_eq!(NsKind::try_from(b"pid".as_slice()), Ok(NsKind::Pid));
assert_eq!(NsKind::try_from(b"user".as_slice()), Ok(NsKind::User));
assert_eq!(NsKind::try_from(b"uts".as_slice()), Ok(NsKind::Uts));
assert_eq!(
NsKind::try_from(b"time_for_children".as_slice()),
Ok(NsKind::TimeForChildren)
);
}
#[test]
fn test_try_from_bytes_unknown_is_eopnotsupp() {
assert_eq!(
NsKind::try_from(b"other".as_slice()),
Err(Errno::EOPNOTSUPP)
);
}
#[test]
fn test_from_ns_kind_into_xpathbuf_subset() {
let xb_net: XPathBuf = XPathBuf::from(NsKind::Net);
let xb_ipc: XPathBuf = XPathBuf::from(NsKind::Ipc);
let xb_cg: XPathBuf = XPathBuf::from(NsKind::Cgroup);
assert_eq!(xb_net, XPathBuf::from("net"));
assert_eq!(xb_ipc, XPathBuf::from("ipc"));
assert_eq!(xb_cg, XPathBuf::from("cgroup"));
}
#[test]
fn test_bytes_to_pid_zero_and_positive() {
assert_eq!(bytes_to_pid(b"0"), Ok(Pid::from_raw(0)));
assert_eq!(bytes_to_pid(b"12345"), Ok(Pid::from_raw(12345)));
}
#[test]
fn test_bytes_to_pid_negative_is_parsed() {
assert_eq!(bytes_to_pid(b"-7"), Ok(Pid::from_raw(-7)));
}
#[test]
fn test_bytes_to_pid_empty_is_einval() {
assert_eq!(bytes_to_pid(b""), Err(Errno::EINVAL));
}
#[test]
fn test_bytes_to_pid_overflow_is_einval() {
assert_eq!(
bytes_to_pid(b"9999999999999999999999999"),
Err(Errno::EINVAL)
);
}
#[test]
fn test_bytes_to_fd_basic_values() {
assert_eq!(bytes_to_fd(b"0"), Ok(0));
assert_eq!(bytes_to_fd(b"42"), Ok(42));
assert_eq!(bytes_to_fd(b"1984"), Ok(1984));
}
#[test]
fn test_bytes_to_fd_invalid_inputs() {
assert_eq!(bytes_to_fd(b""), Err(Errno::EINVAL));
assert_eq!(bytes_to_fd(b"1a"), Err(Errno::EINVAL));
assert_eq!(bytes_to_fd(b"999999999999999999999999"), Err(Errno::EINVAL));
}
}