commit c698e8b5daba0e89d866d81f94e8889738b43c81
parent e26a313a744dff177da6b17fb99f2d8d5001a480
Author: Zack Newman <zack@philomathiclife.com>
Date: Wed, 21 Aug 2024 16:02:34 -0600
rewrite promises
Diffstat:
5 files changed, 482 insertions(+), 560 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -9,30 +9,21 @@ license = "MIT OR Apache-2.0"
name = "priv_sep"
readme = "README.md"
repository = "https://git.philomathiclife.com/repos/priv_sep/"
-version = "1.0.1"
+rust-version = "1.77.0"
+version = "2.0.0"
-[lib]
-name = "priv_sep"
-path = "src/lib.rs"
+[badges]
+maintenance = { status = "actively-developed" }
+
+[package.metadata.docs.rs]
+all-features = true
[target.'cfg(target_os = "openbsd")'.dependencies]
-libc = { version = "0.2.153", default-features = false, features = ["std"], optional = true }
+libc = { version = "0.2.158", default-features = false, features = ["std"], optional = true }
[build-dependencies]
-rustc_version = "0.4.0"
+rustc_version = { version = "0.4.0", default-features = false }
[features]
openbsd = ["dep:libc"]
default = ["openbsd"]
-
-[package.metadata.docs.rs]
-all-features = true
-rustdoc-args = ["--cfg", "docsrs"]
-
-[badges]
-maintenance = { status = "actively-developed" }
-
-[profile.release]
-lto = true
-panic = 'abort'
-strip = true
diff --git a/LICENSE-MIT b/LICENSE-MIT
@@ -1,4 +1,4 @@
-Copyright © 2023 Zack Newman
+Copyright © 2024 Zack Newman
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
diff --git a/README.md b/README.md
@@ -2,7 +2,7 @@
`priv_sep` is a library for privilege separation.
It is currently designed around [`pledge(2)`](https://man.openbsd.org/amd64/pledge.2) and
-[`unveil(2)`](https://man.openbsd.org/amd64/unveil.2) for OpenBSD-stable—that is correct, -stable not -current—but
+[`unveil(2)`](https://man.openbsd.org/amd64/unveil.2) for OpenBSD, but
in the future may contain functionality for Linux's
[`seccomp(2)`](https://man7.org/linux/man-pages/man2/seccomp.2.html).
@@ -24,8 +24,8 @@ Any error returned from the underlying system call is propagated via [`Error`](h
### Status
-This package will be actively maintained to stay in-sync with the latest version of OpenBSD-stable; as a result,
+This package will be actively maintained to stay in-sync with the latest version of OpenBSD; as a result,
the crate is only tested on the `x86_64-unknown-openbsd` target. While OpenBSD supports both the most recent
-release/-stable release as well as the previous version, only the most recent version will be supported by this
-library. For that reason any removal of `promises` in subsequent releases of `pledge(2)` will lead to breaking
-changes in this library as the corresponding `Promise` variant will be removed.
+library. If using -stable, it may be necessary to build the [`rust` port](https://github.com/openbsd/ports/tree/master/lang/rust)
+from -current.
diff --git a/build.rs b/build.rs
@@ -1,12 +1,13 @@
use rustc_version::{version_meta, Channel};
-
fn main() {
- // Set cfg flags depending on release channel
- let channel = match version_meta().unwrap().channel {
- Channel::Stable => "CHANNEL_STABLE",
- Channel::Beta => "CHANNEL_BETA",
- Channel::Nightly => "CHANNEL_NIGHTLY",
- Channel::Dev => "CHANNEL_DEV",
- };
- println!("cargo:rustc-cfg={}", channel)
+ println!("cargo::rustc-check-cfg=cfg(channel_dev,channel_nightly,channel_beta,channel_stable)");
+ println!(
+ "cargo::rustc-cfg=channel_{}",
+ match version_meta().map_or(Channel::Stable, |meta| meta.channel) {
+ Channel::Dev => "dev",
+ Channel::Nightly => "nightly",
+ Channel::Beta => "beta",
+ Channel::Stable => "stable",
+ }
+ );
}
diff --git a/src/lib.rs b/src/lib.rs
@@ -2,7 +2,7 @@
//!
//! `priv_sep` is a library for privilege separation.
//! It is currently designed around [`pledge(2)`](https://man.openbsd.org/amd64/pledge.2) and
-//! [`unveil(2)`](https://man.openbsd.org/amd64/unveil.2) for OpenBSD-stable—that is correct, -stable not -current—but
+//! [`unveil(2)`](https://man.openbsd.org/amd64/unveil.2) for OpenBSD, but
//! in the future may contain functionality for Linux's
//! [`seccomp(2)`](https://man7.org/linux/man-pages/man2/seccomp.2.html).
//!
@@ -19,12 +19,14 @@
//! ## Errors
//!
//! Any error returned from the underlying system call is propagated via [`io::Error`].
-#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
+#![cfg_attr(all(doc, channel_nightly), feature(doc_auto_cfg))]
#![deny(
+ unknown_lints,
future_incompatible,
let_underscore,
missing_docs,
nonstandard_style,
+ refining_impl_trait,
rust_2018_compatibility,
rust_2018_idioms,
rust_2021_compatibility,
@@ -45,9 +47,12 @@
)]
#![allow(
clippy::blanket_clippy_restriction_lints,
+ clippy::doc_markdown,
+ clippy::exhaustive_structs,
clippy::implicit_return,
clippy::min_ident_chars,
clippy::missing_trait_methods,
+ clippy::single_call_fn,
clippy::single_char_lifetime_names,
clippy::unseparated_literal_suffix
)]
@@ -58,9 +63,8 @@ use core::{
convert::AsRef,
ffi::{c_char, c_int},
fmt::{self, Display, Formatter},
- ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Deref, Not},
+ ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not},
ptr,
- slice::Iter,
};
use std::{error, io, os::unix::ffi::OsStrExt, path::Path};
use Promise::{
@@ -145,6 +149,49 @@ pub enum Promise {
/// Consult `pledge(2)`.
Wroute,
}
+impl Promise {
+ /// Returns `self` as a `u64`.
+ const fn to_u64(self) -> u64 {
+ match self {
+ Audio => 0x1,
+ Bpf => 0x2,
+ Chown => 0x4,
+ Cpath => 0x8,
+ Disklabel => 0x10,
+ Dns => 0x20,
+ Dpath => 0x40,
+ Drm => 0x80,
+ Error => 0x100,
+ Exec => 0x200,
+ Fattr => 0x400,
+ Flock => 0x800,
+ Getpw => 0x1000,
+ Id => 0x2000,
+ Inet => 0x4000,
+ Mcast => 0x8000,
+ Pf => 0x10000,
+ Proc => 0x20000,
+ ProtExec => 0x40000,
+ Ps => 0x80000,
+ Recvfd => 0x0010_0000,
+ Route => 0x0020_0000,
+ Rpath => 0x0040_0000,
+ Sendfd => 0x0080_0000,
+ Settime => 0x0100_0000,
+ Stdio => 0x0200_0000,
+ Tape => 0x0400_0000,
+ Tmppath => 0x0800_0000,
+ Tty => 0x1000_0000,
+ Unix => 0x2000_0000,
+ Unveil => 0x4000_0000,
+ Video => 0x8000_0000,
+ Vminfo => 0x0001_0000_0000,
+ Vmm => 0x0002_0000_0000,
+ Wpath => 0x0004_0000_0000,
+ Wroute => 0x0008_0000_0000,
+ }
+ }
+}
impl Display for Promise {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
@@ -205,21 +252,14 @@ impl PartialEq<Promise> for &Promise {
/// Once a set of `promises` has been [`pledge(2)`](https://man.openbsd.org/amd64/pledge.2)d,
/// only a subset of those `promises` can be `pledge(2)`d again, so this type can be used
/// to ensure that `Promise`s are never added but only removed from an initial set.
-#[derive(Clone, Copy, Debug)]
-pub struct Promises<const CAP: usize> {
- /// The initial set of `Promise`s.
- /// `Promise`s starting from `len` are ones that have been "removed".
- proms: [Promise; CAP],
- /// The length of `vals`.
- len: usize,
-}
+#[derive(Debug, Eq, PartialEq)]
+pub struct Promises(u64);
/// Invokes `pledge(2)` always passing `NULL` for `execpromises` and
/// `promises` for `promises`.
///
/// This function MUST only be called by `Promises::pledge` and
/// `pledge_none`.
#[allow(unsafe_code)]
-#[inline]
fn pledge(promises: *const c_char) -> Result<(), io::Error> {
extern "C" {
fn pledge(promises: *const c_char, execpromises: *const c_char) -> c_int;
@@ -235,158 +275,131 @@ fn pledge(promises: *const c_char) -> Result<(), io::Error> {
Err(io::Error::last_os_error())
}
}
-impl<const CAP: usize> Promises<CAP> {
+impl Promises {
+ /// Empty `Promises`.
+ pub const NONE: Self = Self(0);
+ /// `Promises` containing all [`Promise`]s.
+ pub const ALL: Self = Self::NONE
+ .add(Promise::Audio)
+ .add(Promise::Bpf)
+ .add(Promise::Chown)
+ .add(Promise::Cpath)
+ .add(Promise::Disklabel)
+ .add(Promise::Dns)
+ .add(Promise::Dpath)
+ .add(Promise::Drm)
+ .add(Promise::Error)
+ .add(Promise::Exec)
+ .add(Promise::Fattr)
+ .add(Promise::Flock)
+ .add(Promise::Getpw)
+ .add(Promise::Id)
+ .add(Promise::Inet)
+ .add(Promise::Mcast)
+ .add(Promise::Pf)
+ .add(Promise::Proc)
+ .add(Promise::ProtExec)
+ .add(Promise::Ps)
+ .add(Promise::Recvfd)
+ .add(Promise::Route)
+ .add(Promise::Rpath)
+ .add(Promise::Sendfd)
+ .add(Promise::Settime)
+ .add(Promise::Stdio)
+ .add(Promise::Tape)
+ .add(Promise::Tmppath)
+ .add(Promise::Tty)
+ .add(Promise::Unix)
+ .add(Promise::Unveil)
+ .add(Promise::Video)
+ .add(Promise::Vminfo)
+ .add(Promise::Vmm)
+ .add(Promise::Wpath)
+ .add(Promise::Wroute);
+ /// Returns a `Promises` containing all `Promise`s in `self` and `promise`.
+ const fn add(self, promise: Promise) -> Self {
+ Self(self.0 | promise.to_u64())
+ }
/// Returns a `Promises` containing the unique `Promise`s in `initial`.
///
- /// # Example
+ /// # Examples
///
/// ```
- /// use priv_sep::{Promise, Promises};
- /// assert!(Promises::new([Promise::Stdio]).len() == 1);
+ /// # use priv_sep::{Promise, Promises};
+ /// assert_eq!(Promises::new([Promise::Stdio]).len(), 1);
/// ```
- #[allow(clippy::arithmetic_side_effects, clippy::indexing_slicing)]
#[inline]
#[must_use]
- pub fn new(mut initial: [Promise; CAP]) -> Self {
- // This is the first index where "removed" items begin.
- let mut len = CAP;
- // We "remove" duplicates by putting them at the end of `initial`.
- for i in 0..CAP {
- // We don't want to "remove" items that have been "removed" already.
- if len > i {
- // We start at one past the current index.
- // No fear of overflow since the max value of `i` is one less than `CAP`.
- let mut start = i + 1;
- // We must check all items after `i`. When we find an item that is the same,
- // we "remove" it by replacing it with the last active item. This is achieved by
- // decrementing `len` (since we know we are removing one). By decrementing `len`,
- // `len` becomes the last index of an active item which then allows us to swap it
- // with the duplicate item. We keep the index the same in this situation since we need
- // to check this item that has been swapped.
- loop {
- // We don't want to "remove" items that have been "removed" already.
- if len > start {
- // Check if the item at `start` is a duplicate.
- // `i` is less than `CAP`, so indexing `initial` with it won't `panic`.
- // `len` is always less than or equal to `CAP`, and `start` is less than it;
- // so indexing `initial` with `start` won't `panic`.
- if initial[i] == initial[start] {
- // We "remove" the item at `start` by swapping it with the last item.
- // No fear of underflow since `start` is positive and `len` is greater than it.
- len -= 1;
- initial.swap(start, len);
- } else {
- // The item at `start` is not the same as the item at `i`, so we check the next
- // item.
- // No fear of overflow since `start` is less than `len` which is less than or
- // equal to `CAP`.
- start += 1;
- }
- } else {
- // We have checked all items after `i`, so we move on to the next.
- break;
- }
- }
- } else {
- // We have "removed" all duplicate items.
- break;
- }
- }
- Self {
- proms: initial,
- len,
- }
+ pub fn new<P: AsRef<[Promise]>>(initial: P) -> Self {
+ initial
+ .as_ref()
+ .iter()
+ .fold(Self::NONE, |val, promise| val.add(*promise))
}
/// Returns the number of [`Promise`]s.
///
- /// # Example
+ /// # Examples
///
/// ```
- /// use priv_sep::{Promise, Promises};
- /// assert!(Promises::new([Promise::Stdio]).len() == 1);
+ /// # use priv_sep::{Promise, Promises};
+ /// assert_eq!(Promises::new([Promise::Stdio]).len(), 1);
/// ```
#[inline]
#[must_use]
- pub const fn len(&self) -> usize {
- self.len
+ pub const fn len(&self) -> u32 {
+ self.0.count_ones()
}
/// Returns `true` iff there are no [`Promise`]s.
- /// Returns the number of [`Promise`]s.
///
- /// # Example
+ /// # Examples
///
/// ```
- /// use priv_sep::Promises;
- /// assert!(Promises::new([]).is_empty());
+ /// # use priv_sep::Promises;
+ /// assert!(Promises::NONE.is_empty());
/// ```
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
- self.len == 0
- }
- /// Returns the set of `Promise`s.
- ///
- /// # Example
- ///
- /// ```
- /// use priv_sep::{Promise, Promises};
- /// assert!(Promises::new([Promise::Stdio]).vals().first().map_or(false, |promise| promise == Promise::Stdio));
- /// ```
- #[inline]
- #[must_use]
- pub fn vals(&self) -> &[Promise] {
- &self.proms[..self.len]
+ self.len() == 0
}
/// Returns `true` iff `self` contains `promise`.
///
/// # Examples
///
/// ```
- /// use priv_sep::{Promise, Promises};
+ /// # use priv_sep::{Promise, Promises};
/// assert!(Promises::new([Promise::Stdio]).contains(Promise::Stdio));
/// assert!(!Promises::new([Promise::Stdio]).contains(Promise::Rpath));
/// ```
#[inline]
#[must_use]
- pub fn contains(&self, promise: Promise) -> bool {
- self.vals().contains(&promise)
+ pub const fn contains(&self, promise: Promise) -> bool {
+ let val = promise.to_u64();
+ self.0 & val == val
}
- /// Removes all [`Promise`]s except `promises`.
+ /// Removes all `Promise`s _not_ in `promises` from `self`.
///
- /// Note that the internal order of `Promise`s may no longer be the same.
- ///
- /// # Example
+ /// # Examples
///
/// ```
- /// use priv_sep::{Promise, Promises};
+ /// # use priv_sep::{Promise, Promises};
/// let mut proms = Promises::new([Promise::Rpath, Promise::Stdio]);
- /// proms.retain(Promises::new([Promise::Stdio]));
- /// assert!(proms.len() == 1 && proms[0] == Promise::Stdio);
+ /// proms.retain([Promise::Stdio]);
+ /// assert!(proms.len() == 1 && proms.contains(Promise::Stdio));
/// ```
- #[allow(clippy::arithmetic_side_effects)]
#[inline]
pub fn retain<P: AsRef<[Promise]>>(&mut self, promises: P) {
- let other = promises.as_ref();
- // We "check" if an item is in `promises`. If it is not, we "remove" it by swapping it with the
- // the last item.
- for i in 0..self.len {
- // Since we swap items, we must re-check `i` with the item it was swapped with.
- loop {
- let Some(prom) = self.vals().get(i) else {
- // We have no more items to check, so we are done.
- return;
- };
- if other.contains(prom) {
- // `prom` needs to be retained, so we move to the next item.
- break;
+ self.0 = promises
+ .as_ref()
+ .iter()
+ .fold(Self::NONE, |val, promise| {
+ if self.contains(*promise) {
+ val.add(*promise)
+ } else {
+ val
}
- // `prom` needs to be "removed", so we swap it with the last item and do the process again.
- // `len` is at least 1; otherwise `self.vals().get(i)` would have returned `None` so this
- // won't underflow.
- self.len -= 1;
- self.proms.swap(i, self.len);
- }
- }
+ })
+ .0;
}
/// Same as [`Self::retain`] then [`Self::pledge`]; however this is "atomic" in that if an error occurs from `pledge`,
/// `self` is left unchanged. This is useful when one wants to "recover" from an error since there is no
@@ -401,7 +414,7 @@ impl<const CAP: usize> Promises<CAP> {
/// # Example
///
/// ```no_run
- /// use priv_sep::{Promise, Promises};
+ /// # use priv_sep::{Promise, Promises};
/// assert!(Promises::new([Promise::Rpath, Promise::Stdio]).retain_then_pledge([Promise::Rpath]).is_ok());
/// ```
#[inline]
@@ -411,43 +424,33 @@ impl<const CAP: usize> Promises<CAP> {
) -> Result<(), io::Error> {
// We opt for the easy way by copying `self`. This should be the fastest for the
// typical case since `self` likely has very few `Promise`s, and it makes for less code.
- let cur = *self;
+ let cur = Self(self.0);
self.retain(promises);
- if self.len < cur.len {
+ if *self == cur {
+ Ok(())
+ } else {
self.pledge().map_err(|err| {
*self = cur;
err
})
- } else {
- Ok(())
}
}
- /// Removes `promises` from `self`.
- ///
- /// Note that the internal order of `Promise`s may no longer be the same.
+ /// Removes all `Promise`s in `promises` from `self`.
///
- /// # Example
+ /// # Examples
///
/// ```
- /// use priv_sep::{Promise, Promises};
+ /// # use priv_sep::{Promise, Promises};
/// let mut proms = Promises::new([Promise::Rpath, Promise::Stdio]);
- /// proms.remove_promises(Promises::new([Promise::Stdio]));
- /// assert!(proms.len() == 1 && proms[0] == Promise::Rpath);
+ /// proms.remove_promises([Promise::Stdio]);
+ /// assert!(proms.len() == 1 && proms.contains(Promise::Rpath));
/// ```
- #[allow(clippy::arithmetic_side_effects, clippy::into_iter_on_ref)]
#[inline]
pub fn remove_promises<P: AsRef<[Promise]>>(&mut self, promises: P) {
- promises.as_ref().into_iter().fold((), |(), prom| {
- for (idx, prom2) in self.vals().into_iter().enumerate() {
- // We must "remove" the item.
- if prom == prom2 {
- // `len` is at least one; otherwise `self.vals().into_iter()` would have iterated `None`.
- self.len -= 1;
- self.proms.swap(idx, self.len);
- break;
- }
- }
- });
+ promises
+ .as_ref()
+ .iter()
+ .fold((), |(), promise| self.remove(*promise));
}
/// Same as [`Self::remove_promises`] then [`Self::pledge`]; however this is "atomic" in that if an error occurs from
/// `pledge`, `self` is left unchanged. This is useful when one wants to "recover" from an error since there
@@ -462,7 +465,7 @@ impl<const CAP: usize> Promises<CAP> {
/// # Example
///
/// ```no_run
- /// use priv_sep::{Promise, Promises};
+ /// # use priv_sep::{Promise, Promises};
/// assert!(Promises::new([Promise::Rpath, Promise::Stdio]).remove_promises_then_pledge([Promise::Rpath]).is_ok());
/// ```
#[inline]
@@ -472,51 +475,35 @@ impl<const CAP: usize> Promises<CAP> {
) -> Result<(), io::Error> {
// We opt for the easy way by copying `self`. This should be the fastest for the
// typical case since `self` likely has very few `Promise`s, and it makes for less code.
- let cur = *self;
+ let cur = Self(self.0);
self.remove_promises(promises);
- if self.len() < cur.len {
+ if *self == cur {
+ Ok(())
+ } else {
self.pledge().map_err(|err| {
*self = cur;
err
})
- } else {
- Ok(())
}
}
- /// Removes `promise` from `self` returning `true` iff `promise` existed.
- ///
- /// Note that the internal order of `Promise`s may no longer be the same.
+ /// Removes `promise` from `self`.
///
- /// # Example
+ /// # Examples
///
/// ```
- /// use priv_sep::{Promise, Promises};
+ /// # use priv_sep::{Promise, Promises};
/// let mut proms = Promises::new([Promise::Rpath, Promise::Stdio]);
- /// assert!(proms.remove(Promise::Stdio));
- /// assert!(proms.len() == 1 && proms[0] == Promise::Rpath);
+ /// proms.remove(Promise::Stdio);
+ /// assert!(proms.len() == 1 && proms.contains(Promise::Rpath));
/// ```
- #[allow(clippy::arithmetic_side_effects, clippy::into_iter_on_ref)]
#[inline]
- pub fn remove(&mut self, promise: Promise) -> bool {
- for (idx, prom) in self.vals().into_iter().enumerate() {
- // We must "remove" `prom`.
- if *prom == promise {
- // `len` is at least one; otherwise `self.vals().into_iter()` would have iterated `None`.
- self.len -= 1;
- self.proms.swap(idx, self.len);
- // Since `self` contains unique `Promise`s, we don't have to check any more.
- return true;
- }
- }
- false
+ pub fn remove(&mut self, promise: Promise) {
+ self.0 &= !promise.to_u64();
}
/// Same as [`Self::remove`] then [`Self::pledge`]; however this is "atomic" in that if an error occurs from `pledge`,
/// `self` is left unchanged. This is useful when one wants to "recover" from an error since there is no
/// way to add `Promise`s back forcing one to have to create a second `Promises`.
///
- /// The contained `bool` in `Ok` is `true` iff `remove` returned `true`. Note that when `remove` returns
- /// `false`, `pledge` is not called.
- ///
/// # Errors
///
/// Returns [`io::Error`] iff `pledge` does.
@@ -524,38 +511,24 @@ impl<const CAP: usize> Promises<CAP> {
/// # Example
///
/// ```no_run
- /// use priv_sep::{Promise, Promises};
+ /// # use priv_sep::{Promise, Promises};
/// assert!(Promises::new([Promise::Rpath, Promise::Stdio]).remove_then_pledge(Promise::Rpath).is_ok());
/// ```
#[inline]
- pub fn remove_then_pledge(&mut self, promise: Promise) -> Result<bool, io::Error> {
+ pub fn remove_then_pledge(&mut self, promise: Promise) -> Result<(), io::Error> {
// We opt for the easy way by copying `self`. This should be the fastest for the
// typical case since `self` likely has very few `Promise`s, and it makes for less code.
- let cur = *self;
- if self.remove(promise) {
- self.pledge()
- .map_err(|err| {
- *self = cur;
- err
- })
- .map(|()| true)
+ let cur = Self(self.0);
+ self.remove(promise);
+ if *self == cur {
+ Ok(())
} else {
- Ok(false)
+ self.pledge().map_err(|err| {
+ *self = cur;
+ err
+ })
}
}
- /// Returns an [`Iterator`] without consuming `self`.
- ///
- /// # Example
- ///
- /// ```
- /// use priv_sep::{Promise, Promises};
- /// assert!(Promises::new([Promise::Rpath, Promise::Stdio]).iter().next().map_or(false, |prom| prom == Promise::Rpath));
- /// ```
- #[allow(clippy::into_iter_on_ref)]
- #[inline]
- pub fn iter(&self) -> Iter<'_, Promise> {
- self.vals().into_iter()
- }
/// Invokes [`pledge(2)`](https://man.openbsd.org/amd64/pledge.2) always passing in
/// `NULL` for `execpromises` and its contained [`Promise`]s for `promises`.
///
@@ -563,61 +536,130 @@ impl<const CAP: usize> Promises<CAP> {
///
/// Returns [`io::Error`] iff `pledge(2)` errors.
///
- /// # Example
+ /// # Examples
///
/// ```no_run
- /// use priv_sep::{Promise, Promises};
+ /// # use priv_sep::{Promise, Promises};
/// assert!(Promises::new([Promise::Stdio]).pledge().is_ok());
/// ```
- #[inline]
#[allow(
unsafe_code,
clippy::arithmetic_side_effects,
+ clippy::cognitive_complexity,
clippy::indexing_slicing,
- clippy::into_iter_on_ref
+ clippy::too_many_lines
)]
+ #[inline]
pub fn pledge(&self) -> Result<(), io::Error> {
let mut p = Vec::new();
- self.vals().into_iter().fold((), |(), promise| {
- p.extend_from_slice(match *promise {
- Audio => b"audio ",
- Bpf => b"bpf ",
- Chown => b"chown ",
- Cpath => b"cpath ",
- Disklabel => b"disklabel ",
- Dns => b"dns ",
- Dpath => b"dpath ",
- Drm => b"drm ",
- Error => b"error ",
- Exec => b"exec ",
- Fattr => b"fattr ",
- Flock => b"flock ",
- Getpw => b"getpw ",
- Id => b"id ",
- Inet => b"inet ",
- Mcast => b"mcast ",
- Pf => b"pf ",
- Proc => b"proc ",
- ProtExec => b"prot_exec ",
- Ps => b"ps ",
- Recvfd => b"recvfd ",
- Route => b"route ",
- Rpath => b"rpath ",
- Sendfd => b"sendfd ",
- Settime => b"settime ",
- Stdio => b"stdio ",
- Tape => b"tape ",
- Tmppath => b"tmppath ",
- Tty => b"tty ",
- Unix => b"unix ",
- Unveil => b"unveil ",
- Video => b"video ",
- Vminfo => b"vminfo ",
- Vmm => b"vmm ",
- Wpath => b"wpath ",
- Wroute => b"wroute ",
- });
- });
+ if self.contains(Audio) {
+ p.extend_from_slice(b"audio ");
+ }
+ if self.contains(Bpf) {
+ p.extend_from_slice(b"bpf ");
+ }
+ if self.contains(Chown) {
+ p.extend_from_slice(b"chown ");
+ }
+ if self.contains(Cpath) {
+ p.extend_from_slice(b"cpath ");
+ }
+ if self.contains(Disklabel) {
+ p.extend_from_slice(b"disklabel ");
+ }
+ if self.contains(Dns) {
+ p.extend_from_slice(b"dns ");
+ }
+ if self.contains(Dpath) {
+ p.extend_from_slice(b"dpath ");
+ }
+ if self.contains(Drm) {
+ p.extend_from_slice(b"drm ");
+ }
+ if self.contains(Error) {
+ p.extend_from_slice(b"error ");
+ }
+ if self.contains(Exec) {
+ p.extend_from_slice(b"exec ");
+ }
+ if self.contains(Fattr) {
+ p.extend_from_slice(b"fattr ");
+ }
+ if self.contains(Flock) {
+ p.extend_from_slice(b"flock ");
+ }
+ if self.contains(Getpw) {
+ p.extend_from_slice(b"getpw ");
+ }
+ if self.contains(Id) {
+ p.extend_from_slice(b"id ");
+ }
+ if self.contains(Inet) {
+ p.extend_from_slice(b"inet ");
+ }
+ if self.contains(Mcast) {
+ p.extend_from_slice(b"mcast ");
+ }
+ if self.contains(Pf) {
+ p.extend_from_slice(b"pf ");
+ }
+ if self.contains(Proc) {
+ p.extend_from_slice(b"proc ");
+ }
+ if self.contains(ProtExec) {
+ p.extend_from_slice(b"prot_exec ");
+ }
+ if self.contains(Ps) {
+ p.extend_from_slice(b"ps ");
+ }
+ if self.contains(Recvfd) {
+ p.extend_from_slice(b"recvfd ");
+ }
+ if self.contains(Route) {
+ p.extend_from_slice(b"route ");
+ }
+ if self.contains(Rpath) {
+ p.extend_from_slice(b"rpath ");
+ }
+ if self.contains(Sendfd) {
+ p.extend_from_slice(b"sendfd ");
+ }
+ if self.contains(Settime) {
+ p.extend_from_slice(b"settime ");
+ }
+ if self.contains(Stdio) {
+ p.extend_from_slice(b"stdio ");
+ }
+ if self.contains(Tape) {
+ p.extend_from_slice(b"tape ");
+ }
+ if self.contains(Tmppath) {
+ p.extend_from_slice(b"tmppath ");
+ }
+ if self.contains(Tty) {
+ p.extend_from_slice(b"tty ");
+ }
+ if self.contains(Unix) {
+ p.extend_from_slice(b"unix ");
+ }
+ if self.contains(Unveil) {
+ p.extend_from_slice(b"unveil ");
+ }
+ if self.contains(Video) {
+ p.extend_from_slice(b"video ");
+ }
+ if self.contains(Vminfo) {
+ p.extend_from_slice(b"vminfo ");
+ }
+ if self.contains(Vmm) {
+ p.extend_from_slice(b"vmm ");
+ }
+ if self.contains(Wpath) {
+ p.extend_from_slice(b"wpath ");
+ }
+ if self.contains(Wroute) {
+ p.extend_from_slice(b"wroute ");
+ }
let idx = p.len();
if idx == 0 {
p.push(0);
@@ -636,95 +678,16 @@ impl<const CAP: usize> Promises<CAP> {
pledge(arg.as_ptr())
}
}
-impl<const CAP: usize> AsRef<[Promise]> for Promises<CAP> {
+impl PartialEq<&Self> for Promises {
#[inline]
- fn as_ref(&self) -> &[Promise] {
- self.vals()
- }
-}
-impl<const CAP: usize> Deref for Promises<CAP> {
- type Target = [Promise];
- #[inline]
- fn deref(&self) -> &[Promise] {
- self.vals()
- }
-}
-impl<const CAP: usize, const CAP2: usize> PartialEq<Promises<CAP2>> for Promises<CAP> {
- #[inline]
- fn eq(&self, other: &Promises<CAP2>) -> bool {
- self.len == other.len
- && self
- .iter()
- .try_fold((), |(), prom| {
- if other.contains(*prom) {
- Ok(())
- } else {
- Err(())
- }
- })
- .map_or(false, |()| true)
- }
-}
-impl<const CAP: usize> Eq for Promises<CAP> {}
-/// An [`Iterator`] of [`Promise`]s
-pub struct PromIter<const CAP: usize> {
- /// Source of `Promise`s.
- prom: Promises<CAP>,
- /// Current index.
- idx: usize,
-}
-impl<const CAP: usize> Iterator for PromIter<CAP> {
- type Item = Promise;
- #[allow(clippy::arithmetic_side_effects)]
- #[inline]
- fn next(&mut self) -> Option<Self::Item> {
- self.prom.vals().get(self.idx).map(|prom| {
- // `idx` must be less than `CAP`, since `get` returned `Some`.
- self.idx += 1;
- *prom
- })
- }
-}
-impl<const CAP: usize> ExactSizeIterator for PromIter<CAP> {
- #[allow(clippy::arithmetic_side_effects)]
- #[inline]
- fn len(&self) -> usize {
- // `idx` is <= `len`.
- self.prom.len - self.idx
- }
-}
-impl<const CAP: usize> DoubleEndedIterator for PromIter<CAP> {
- #[allow(clippy::arithmetic_side_effects)]
- #[inline]
- fn next_back(&mut self) -> Option<Self::Item> {
- self.prom.proms[self.idx..self.prom.len].last().map(|prom| {
- // `len` is at least 1; otherwise the above slice would be empty which means `last` would
- // have returned `None`.
- self.prom.len -= 1;
- *prom
- })
- }
-}
-impl<const CAP: usize> IntoIterator for Promises<CAP> {
- type Item = Promise;
- type IntoIter = PromIter<CAP>;
- #[inline]
- fn into_iter(self) -> Self::IntoIter {
- PromIter { prom: self, idx: 0 }
- }
-}
-impl<'a, const CAP: usize> IntoIterator for &'a Promises<CAP> {
- type Item = &'a Promise;
- type IntoIter = Iter<'a, Promise>;
- #[inline]
- fn into_iter(self) -> Self::IntoIter {
- self.iter()
+ fn eq(&self, other: &&Self) -> bool {
+ *self == **other
}
}
-impl<const CAP: usize> From<[Promise; CAP]> for Promises<CAP> {
+impl PartialEq<Promises> for &Promises {
#[inline]
- fn from(value: [Promise; CAP]) -> Self {
- Self::new(value)
+ fn eq(&self, other: &Promises) -> bool {
+ **self == *other
}
}
/// Invokes [`pledge(2)`](https://man.openbsd.org/amd64/pledge.2) with `NULL` for
@@ -750,7 +713,6 @@ pub fn pledge_none() -> Result<(), io::Error> {
/// This function MUST only be called by the functions `Permissions::unveil` and
/// `unveil_no_more`.
#[allow(unsafe_code)]
-#[inline]
fn unveil(path: *const c_char, permissions: *const c_char) -> Result<(), io::Error> {
extern "C" {
fn unveil(path: *const c_char, permissions: *const c_char) -> c_int;
@@ -766,62 +728,116 @@ fn unveil(path: *const c_char, permissions: *const c_char) -> Result<(), io::Err
Err(io::Error::last_os_error())
}
}
-/// `permissions` to [`unveil(2)`](https://man.openbsd.org/amd64/unveil.2).
-#[allow(clippy::exhaustive_structs, clippy::struct_excessive_bools)]
-#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
-pub struct Permissions {
- /// Consult `unveil(2)`.
- pub create: bool,
- /// Consult `unveil(2)`.
- pub execute: bool,
- /// Consult `unveil(2)`.
- pub read: bool,
- /// Consult `unveil(2)`.
- pub write: bool,
+/// A permission in [`Permissions`].
+#[allow(clippy::exhaustive_enums)]
+#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+pub enum Permission {
+ /// [c](https://man.openbsd.org/amd64/unveil.2#c).
+ Create,
+ /// [x](https://man.openbsd.org/amd64/unveil.2#x).
+ Execute,
+ /// [r](https://man.openbsd.org/amd64/unveil.2#r).
+ Read,
+ /// [w](https://man.openbsd.org/amd64/unveil.2#w).
+ Write,
+}
+impl Permission {
+ /// Transforms `self` into a `u8`.
+ const fn to_u8(self) -> u8 {
+ match self {
+ Self::Create => 1,
+ Self::Execute => 2,
+ Self::Read => 4,
+ Self::Write => 8,
+ }
+ }
+}
+impl PartialEq<&Self> for Permission {
+ #[inline]
+ fn eq(&self, other: &&Self) -> bool {
+ *self == **other
+ }
+}
+impl PartialEq<Permission> for &Permission {
+ #[inline]
+ fn eq(&self, other: &Permission) -> bool {
+ **self == *other
+ }
}
+/// `permissions` to [`unveil(2)`](https://man.openbsd.org/amd64/unveil.2).
+#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+pub struct Permissions(u8);
impl Permissions {
- /// Contains all fields as `true`.
- pub const ALL: Self = Self {
- create: true,
- execute: true,
- read: true,
- write: true,
- };
- /// Contains all fields as `false`.
- pub const NONE: Self = Self {
- create: false,
- execute: false,
- read: false,
- write: false,
- };
- /// A `Permissions` with only [`Permissions::create`] set to `true`.
- pub const CREATE: Self = Self {
- create: true,
- execute: false,
- read: false,
- write: false,
- };
- /// A `Permissions` with only [`Permissions::execute`] set to `true`.
- pub const EXECUTE: Self = Self {
- create: false,
- execute: true,
- read: false,
- write: false,
- };
- /// A `Permissions` with only [`Permissions::read`] set to `true`.
- pub const READ: Self = Self {
- create: false,
- execute: false,
- read: true,
- write: false,
- };
- /// A `Permissions` with only [`Permissions::write`] set to `true`.
- pub const WRITE: Self = Self {
- create: false,
- execute: false,
- read: false,
- write: true,
- };
+ /// A `Permissions` with no [`Permission`]s enabled.
+ pub const NONE: Self = Self(0);
+ /// A `Permissions` with all [`Permission`]s enabled.
+ pub const ALL: Self = Self::NONE
+ .enable_inner(Permission::Create)
+ .enable_inner(Permission::Execute)
+ .enable_inner(Permission::Read)
+ .enable_inner(Permission::Write);
+ /// A `Permissions` with only [`Permission::Create`] enabled.
+ pub const CREATE: Self = Self::NONE.enable_inner(Permission::Create);
+ /// A `Permissions` with only [`Permission::Execute`] enabled.
+ pub const EXECUTE: Self = Self::NONE.enable_inner(Permission::Execute);
+ /// A `Permissions` with only [`Permission::Read`] enabled.
+ pub const READ: Self = Self::NONE.enable_inner(Permission::Read);
+ /// A `Permissions` with only [`Permission::Write`] enabled.
+ pub const WRITE: Self = Self::NONE.enable_inner(Permission::Write);
+ /// Same as [`enable`] but returns a new instance instead of mutating `self`.
+ const fn enable_inner(self, permission: Permission) -> Self {
+ Self(self.0 | permission.to_u8())
+ }
+ /// Enables `permission` in `self`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use priv_sep::{Permission, Permissions};
+ /// let mut perms = Permissions::NONE;
+ /// perms.enable(Permission::Read);
+ /// assert_eq!(perms, Permissions::READ);
+ /// ```
+ #[inline]
+ pub fn enable(&mut self, permission: Permission) {
+ self.0 |= permission.to_u8();
+ }
+ /// Disables `permission` in `self`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use priv_sep::{Permission, Permissions};
+ /// let mut perms = Permissions::ALL;
+ /// perms.disable(Permission::Execute);
+ /// assert!(
+ /// perms.is_enabled(Permission::Create)
+ /// && perms.is_enabled(Permission::Read)
+ /// && perms.is_enabled(Permission::Write)
+ /// && !perms.is_enabled(Permission::Execute)
+ /// );
+ /// assert!(perms.is_enabled(Permission::Create) && perms.is_enabled(Permission::Read) && perms.is_enabled(Permission::Write) && !perms.is_enabled(Permission::Execute));
+ /// ```
+ #[inline]
+ pub fn disable(&mut self, permission: Permission) {
+ self.0 &= !permission.to_u8();
+ }
+ /// Returns `true` iff `self` has `permission` enabled.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use priv_sep::{Permission, Permissions};
+ /// let perms = Permissions::CREATE;
+ /// assert!(perms.is_enabled(Permission::Create));
+ /// assert!(!perms.is_enabled(Permission::Write));
+ /// ```
+ #[inline]
+ #[must_use]
+ pub const fn is_enabled(self, permission: Permission) -> bool {
+ let val = permission.to_u8();
+ self.0 & val == val
+ }
/// Invokes [`unveil(2)`](https://man.openbsd.org/amd64/unveil.2)
/// passing `path` for `path` and the contained permissions for `permissions`.
///
@@ -833,47 +849,41 @@ impl Permissions {
/// # Examples
///
/// ```no_run
- /// use std::io::ErrorKind;
- /// use priv_sep::{Permissions, UnveilErr};
+ /// # use std::io::ErrorKind;
+ /// # use priv_sep::{Permissions, UnveilErr};
/// assert!(Permissions::READ.unveil("/path/to/read").is_ok());
/// assert!(Permissions::READ.unveil("/path/does/not/exist").map_or_else(
/// |err| match err {
/// UnveilErr::Io(e) => e.kind() == ErrorKind::NotFound,
/// UnveilErr::Nul(_) => false,
/// },
- /// |_| false
+ /// |()| false
/// ));
/// ```
+ #[allow(unsafe_code, clippy::arithmetic_side_effects)]
#[inline]
- #[allow(
- unsafe_code,
- clippy::arithmetic_side_effects,
- clippy::as_conversions,
- clippy::cast_lossless
- )]
pub fn unveil<P: AsRef<Path>>(self, path: P) -> Result<(), UnveilErr> {
CString::new(path.as_ref().as_os_str().as_bytes()).map_or_else(
|e| Err(UnveilErr::Nul(e)),
|path_c| {
- // `true as usize` is guaranteed to return 1, and `false as usize` is guaranteed to return
- // 0. This means the max sum is 5.
+ // The max sum is 5, so overflow is not possible.
let mut vec = Vec::with_capacity(
- self.create as usize
- + self.execute as usize
- + self.read as usize
- + self.write as usize
+ usize::from(self.is_enabled(Permission::Create))
+ + usize::from(self.is_enabled(Permission::Execute))
+ + usize::from(self.is_enabled(Permission::Read))
+ + usize::from(self.is_enabled(Permission::Write))
+ 1,
);
- if self.create {
+ if self.is_enabled(Permission::Create) {
vec.push(b'c');
}
- if self.execute {
+ if self.is_enabled(Permission::Execute) {
vec.push(b'x');
}
- if self.read {
+ if self.is_enabled(Permission::Read) {
vec.push(b'r');
}
- if self.write {
+ if self.is_enabled(Permission::Write) {
vec.push(b'w');
}
vec.push(0);
@@ -895,23 +905,34 @@ impl Display for Permissions {
write!(
f,
"unveil(2) '{}{}{}{}' permissions",
- if self.create { "c" } else { "" },
- if self.execute { "x" } else { "" },
- if self.read { "r" } else { "" },
- if self.write { "w" } else { "" },
+ if self.is_enabled(Permission::Create) {
+ "c"
+ } else {
+ ""
+ },
+ if self.is_enabled(Permission::Execute) {
+ "x"
+ } else {
+ ""
+ },
+ if self.is_enabled(Permission::Read) {
+ "r"
+ } else {
+ ""
+ },
+ if self.is_enabled(Permission::Write) {
+ "w"
+ } else {
+ ""
+ },
)
}
}
-impl BitAnd<Self> for Permissions {
+impl BitAnd for Permissions {
type Output = Self;
#[inline]
fn bitand(self, rhs: Self) -> Self::Output {
- Self {
- create: self.create & rhs.create,
- execute: self.execute & rhs.execute,
- read: self.read & rhs.read,
- write: self.write & rhs.write,
- }
+ Self(self.0 & rhs.0)
}
}
impl BitAnd<&Self> for Permissions {
@@ -935,13 +956,10 @@ impl<'a> BitAnd<Permissions> for &'a Permissions {
*self & rhs
}
}
-impl BitAndAssign<Self> for Permissions {
+impl BitAndAssign for Permissions {
#[inline]
fn bitand_assign(&mut self, rhs: Self) {
- self.create &= rhs.create;
- self.execute &= rhs.execute;
- self.read &= rhs.read;
- self.write &= rhs.write;
+ self.0 &= rhs.0;
}
}
impl BitAndAssign<&Self> for Permissions {
@@ -950,16 +968,11 @@ impl BitAndAssign<&Self> for Permissions {
*self &= *rhs;
}
}
-impl BitOr<Self> for Permissions {
+impl BitOr for Permissions {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self::Output {
- Self {
- create: self.create | rhs.create,
- execute: self.execute | rhs.execute,
- read: self.read | rhs.read,
- write: self.write | rhs.write,
- }
+ Self(self.0 | rhs.0)
}
}
impl BitOr<&Self> for Permissions {
@@ -983,13 +996,10 @@ impl<'a> BitOr<Permissions> for &'a Permissions {
*self | rhs
}
}
-impl BitOrAssign<Self> for Permissions {
+impl BitOrAssign for Permissions {
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
- self.create |= rhs.create;
- self.execute |= rhs.execute;
- self.read |= rhs.read;
- self.write |= rhs.write;
+ self.0 |= rhs.0;
}
}
impl BitOrAssign<&Self> for Permissions {
@@ -998,16 +1008,11 @@ impl BitOrAssign<&Self> for Permissions {
*self |= *rhs;
}
}
-impl BitXor<Self> for Permissions {
+impl BitXor for Permissions {
type Output = Self;
#[inline]
fn bitxor(self, rhs: Self) -> Self::Output {
- Self {
- create: self.create ^ rhs.create,
- execute: self.execute ^ rhs.execute,
- read: self.read ^ rhs.read,
- write: self.write ^ rhs.write,
- }
+ Self(self.0 ^ rhs.0)
}
}
impl BitXor<&Self> for Permissions {
@@ -1031,13 +1036,10 @@ impl<'a> BitXor<Permissions> for &'a Permissions {
*self ^ rhs
}
}
-impl BitXorAssign<Self> for Permissions {
+impl BitXorAssign for Permissions {
#[inline]
fn bitxor_assign(&mut self, rhs: Self) {
- self.create ^= rhs.create;
- self.execute ^= rhs.execute;
- self.read ^= rhs.read;
- self.write ^= rhs.write;
+ self.0 ^= rhs.0;
}
}
impl BitXorAssign<&Self> for Permissions {
@@ -1050,12 +1052,7 @@ impl Not for Permissions {
type Output = Self;
#[inline]
fn not(self) -> Self::Output {
- Self {
- create: !self.create,
- execute: !self.execute,
- read: !self.read,
- write: !self.write,
- }
+ Self(Self::ALL.0 & !self.0)
}
}
impl Not for &Permissions {
@@ -1130,11 +1127,15 @@ mod tests {
_ = fs::metadata(FILE_EXISTS)
.expect(format!("{FILE_EXISTS} does not exist, so unit testing cannot occur").as_str());
const FILE_NOT_EXISTS: &str = "/home/zack/aadkjfasj3s23";
- _ = fs::metadata(FILE_NOT_EXISTS)
- .expect_err(format!("{FILE_NOT_EXISTS} exists, so unit testing cannot occur").as_str());
+ drop(fs::metadata(FILE_NOT_EXISTS).expect_err(
+ format!("{FILE_NOT_EXISTS} exists, so unit testing cannot occur").as_str(),
+ ));
const DIR_NOT_EXISTS: &str = "/home/zack/aadkjfasj3s23/";
- _ = fs::metadata(DIR_NOT_EXISTS)
- .expect_err(format!("{DIR_NOT_EXISTS} exists, so unit testing cannot occur").as_str());
+ drop(
+ fs::metadata(DIR_NOT_EXISTS).expect_err(
+ format!("{DIR_NOT_EXISTS} exists, so unit testing cannot occur").as_str(),
+ ),
+ );
// This tests that a NULL `promise` does nothing.
assert!(crate::pledge_none().is_ok());
print!("");
@@ -1160,82 +1161,8 @@ mod tests {
Promise::Exec,
]);
vals.retain([Promise::Error, Promise::Chown]);
- vals.len() == 2
- && vals.contains(Promise::Chown)
- && vals.vals().contains(&Promise::Error)
+ vals.len() == 2 && vals.contains(Promise::Chown) && vals.contains(Promise::Error)
});
- // This tests iteration.
- // After "removal" of duplicates,
- // this should be [Stdio, Unveil, Error, Rpath] in
- // some order.
- let mut proms = Promises::new([
- Promise::Unveil,
- Promise::Unveil,
- Promise::Stdio,
- Promise::Unveil,
- Promise::Unveil,
- Promise::Rpath,
- Promise::Error,
- Promise::Stdio,
- Promise::Rpath,
- Promise::Error,
- ])
- .into_iter();
- assert!(proms.len() == 4);
- let mut unveil = false;
- let mut stdio = false;
- let mut rpath = false;
- let mut error = false;
- let mut closure = |prom: Promise| -> bool {
- match prom {
- Promise::Unveil => {
- if unveil {
- return false;
- } else {
- unveil = true;
- true
- }
- }
- Promise::Rpath => {
- if rpath {
- return false;
- } else {
- rpath = true;
- true
- }
- }
- Promise::Error => {
- if error {
- return false;
- } else {
- error = true;
- true
- }
- }
- Promise::Stdio => {
- if stdio {
- return false;
- } else {
- stdio = true;
- true
- }
- }
- _ => false,
- }
- };
- assert!(proms.next().map_or(false, &mut closure));
- assert!(proms.len() == 3);
- assert!(proms.next_back().map_or(false, &mut closure));
- assert!(proms.len() == 2);
- assert!(proms.next_back().map_or(false, &mut closure));
- assert!(proms.len() == 1);
- assert!(proms.next().map_or(false, &mut closure));
- assert!(proms.len() == 0);
- assert!(unveil && stdio && error && rpath);
- assert!(proms.next().is_none());
- assert!(proms.next_back().is_none());
- assert!(proms.next().is_none());
- assert!(proms.next_back().is_none());
assert!(initial_promises.pledge().is_ok());
// This tests unveil with no permissions.
assert!(Permissions::NONE.unveil(FILE_EXISTS).is_ok());
@@ -1254,13 +1181,16 @@ mod tests {
assert!(fs::metadata(FILE_EXISTS).is_ok());
// The below tests that Promises can only be removed and not added.
initial_promises.remove_promises([Promise::Unveil]);
- assert!(initial_promises.remove(Promise::Rpath));
- assert!(!initial_promises.remove(Promise::Rpath));
+ assert_eq!(initial_promises.len(), 2);
+ initial_promises.remove(Promise::Rpath);
+ assert_eq!(initial_promises.len(), 1);
+ initial_promises.remove(Promise::Rpath);
+ assert_eq!(initial_promises.len(), 1);
assert!(initial_promises.pledge().is_ok());
print!("");
assert!(Promises::new([Promise::Rpath]).pledge().is_err());
// If the below is uncommented, the program should crash since the above
// call to pledge no longer allows access to the file system.
- //_ = fs::metadata(FILE_EXISTS);
+ // drop(fs::metadata(FILE_EXISTS));
}
}