blob: 68bf8baa14176104a93abf10631b70c1cf2b3637 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#[cfg(feature = "serde")]
use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize};
use {
crate::{error::RightsRoutingError, walk_state::WalkStateUnit},
fidl_fuchsia_io as fio,
std::fmt,
};
/// All the fio rights required to represent fio::OpenFlags::RIGHT_READABLE.
const LEGACY_READABLE_RIGHTS: fio::Operations = fio::Operations::empty()
.union(fio::Operations::READ_BYTES)
.union(fio::Operations::GET_ATTRIBUTES)
.union(fio::Operations::TRAVERSE)
.union(fio::Operations::ENUMERATE);
/// All the fio rights required to represent fio::OpenFlags::RIGHT_WRITABLE.
const LEGACY_WRITABLE_RIGHTS: fio::Operations = fio::Operations::empty()
.union(fio::Operations::WRITE_BYTES)
.union(fio::Operations::UPDATE_ATTRIBUTES)
.union(fio::Operations::MODIFY_DIRECTORY);
/// Opaque rights type to define new traits like PartialOrd on.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Rights(fio::Operations);
impl Rights {
/// Converts new fuchsia.io directory rights to legacy fuchsia.io compatible rights. This will
/// be remove once new rights are supported by component manager.
pub fn into_legacy(&self) -> fio::OpenFlags {
let mut flags = fio::OpenFlags::empty();
let Self(rights) = self;
// The `intersects` below is intentional. The translation from io2 to io rights is lossy
// in the sense that a single io2 right may require an io right with coarser permissions.
if rights.intersects(LEGACY_READABLE_RIGHTS) {
flags |= fio::OpenFlags::RIGHT_READABLE;
}
if rights.intersects(LEGACY_WRITABLE_RIGHTS) {
flags |= fio::OpenFlags::RIGHT_WRITABLE;
}
if rights.contains(fio::Operations::EXECUTE) {
flags |= fio::OpenFlags::RIGHT_EXECUTABLE;
}
flags
}
}
/// Allows creating rights from fio::Operations.
impl From<fio::Operations> for Rights {
fn from(operations: fio::Operations) -> Self {
Rights(operations)
}
}
impl fmt::Display for Rights {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self(rights) = self;
match *rights {
fio::R_STAR_DIR => write!(f, "r*"),
fio::W_STAR_DIR => write!(f, "w*"),
fio::X_STAR_DIR => write!(f, "x*"),
fio::RW_STAR_DIR => write!(f, "rw*"),
fio::RX_STAR_DIR => write!(f, "rx*"),
ops => write!(f, "{:?}", ops),
}
}
}
#[cfg(feature = "serde")]
impl Serialize for Rights {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let Self(rights) = self;
rights.bits().serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Rights {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let bits: u64 = Deserialize::deserialize(deserializer)?;
let rights = fio::Operations::from_bits(bits)
.ok_or(serde::de::Error::custom("invalid value for fuchsia.io/Operations"))?;
Ok(Self(rights))
}
}
impl WalkStateUnit for Rights {
type Error = RightsRoutingError;
/// Ensures the next walk state of rights satisfies a monotonic increasing sequence. Used to
/// verify the expectation that no right requested from a use, offer, or expose is missing as
/// capability routing walks from the capability's consumer to its provider.
fn validate_next(&self, next_rights: &Rights) -> Result<(), Self::Error> {
if next_rights.0.contains(self.0) {
Ok(())
} else {
Err(RightsRoutingError::Invalid { requested: *self, provided: *next_rights })
}
}
fn finalize_error() -> Self::Error {
RightsRoutingError::MissingRightsSource
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
/// All the fio rights required to represent fio::OpenFlags::RIGHT_EXECUTABLE.
const LEGACY_EXECUTABLE_RIGHTS: fio::Operations = fio::Operations::EXECUTE;
#[test]
fn validate_next() {
assert_matches!(
Rights::from(fio::Operations::empty())
.validate_next(&Rights::from(LEGACY_READABLE_RIGHTS)),
Ok(())
);
assert_matches!(
Rights::from(fio::Operations::READ_BYTES | fio::Operations::GET_ATTRIBUTES)
.validate_next(&Rights::from(LEGACY_READABLE_RIGHTS)),
Ok(())
);
let provided = fio::Operations::READ_BYTES | fio::Operations::GET_ATTRIBUTES;
assert_eq!(
Rights::from(LEGACY_READABLE_RIGHTS).validate_next(&Rights::from(provided)),
Err(RightsRoutingError::Invalid {
requested: Rights::from(LEGACY_READABLE_RIGHTS),
provided: Rights::from(provided),
})
);
let provided = fio::Operations::READ_BYTES | fio::Operations::GET_ATTRIBUTES;
assert_eq!(
Rights::from(fio::Operations::WRITE_BYTES).validate_next(&Rights::from(provided)),
Err(RightsRoutingError::Invalid {
requested: Rights::from(fio::Operations::WRITE_BYTES),
provided: Rights::from(provided),
})
);
}
#[test]
fn into_legacy() {
assert_eq!(
Rights::from(LEGACY_READABLE_RIGHTS).into_legacy(),
fio::OpenFlags::RIGHT_READABLE
);
assert_eq!(
Rights::from(LEGACY_WRITABLE_RIGHTS).into_legacy(),
fio::OpenFlags::RIGHT_WRITABLE
);
assert_eq!(
Rights::from(LEGACY_EXECUTABLE_RIGHTS).into_legacy(),
fio::OpenFlags::RIGHT_EXECUTABLE
);
assert_eq!(
Rights::from(LEGACY_READABLE_RIGHTS | LEGACY_WRITABLE_RIGHTS).into_legacy(),
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE
);
assert_eq!(
Rights::from(
LEGACY_READABLE_RIGHTS | LEGACY_WRITABLE_RIGHTS | LEGACY_EXECUTABLE_RIGHTS
)
.into_legacy(),
fio::OpenFlags::RIGHT_READABLE
| fio::OpenFlags::RIGHT_WRITABLE
| fio::OpenFlags::RIGHT_EXECUTABLE
);
assert_eq!(
Rights::from(fio::Operations::READ_BYTES).into_legacy(),
fio::OpenFlags::RIGHT_READABLE
);
assert_eq!(
Rights::from(fio::Operations::GET_ATTRIBUTES).into_legacy(),
fio::OpenFlags::RIGHT_READABLE
);
assert_eq!(
Rights::from(fio::Operations::TRAVERSE).into_legacy(),
fio::OpenFlags::RIGHT_READABLE
);
assert_eq!(
Rights::from(fio::Operations::ENUMERATE).into_legacy(),
fio::OpenFlags::RIGHT_READABLE
);
assert_eq!(
Rights::from(fio::Operations::WRITE_BYTES).into_legacy(),
fio::OpenFlags::RIGHT_WRITABLE
);
assert_eq!(
Rights::from(fio::Operations::UPDATE_ATTRIBUTES).into_legacy(),
fio::OpenFlags::RIGHT_WRITABLE
);
assert_eq!(
Rights::from(fio::Operations::MODIFY_DIRECTORY).into_legacy(),
fio::OpenFlags::RIGHT_WRITABLE
);
}
}