blob: 2a0a60c9048af688651e58e7e51b4e656ab7ed85 [file] [log] [blame]
// Copyright 2019 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.
use {
crate::model::walk_state::WalkStateUnit,
fidl_fuchsia_io::{self as fio},
fidl_fuchsia_io2::{self as fio2},
fuchsia_zircon as zx,
lazy_static::lazy_static,
std::convert::From,
thiserror::Error,
};
lazy_static! {
// TODO: const initialization of bitflag types with bitwise-or is not supported in FIDL. Change
// when supported.
/// All rights corresponding to r*.
pub static ref READ_RIGHTS: fio2::Operations =
fio2::Operations::Connect
| fio2::Operations::Enumerate
| fio2::Operations::Traverse
| fio2::Operations::ReadBytes
| fio2::Operations::GetAttributes;
/// All rights corresponding to w*.
pub static ref WRITE_RIGHTS: fio2::Operations =
fio2::Operations::Connect
| fio2::Operations::Enumerate
| fio2::Operations::Traverse
| fio2::Operations::WriteBytes
| fio2::Operations::ModifyDirectory
| fio2::Operations::UpdateAttributes;
/// All the fio2 rights required to represent fio::OPEN_RIGHT_READABLE.
static ref LEGACY_READABLE_RIGHTS: fio2::Operations =
fio2::Operations::ReadBytes
| fio2::Operations::GetAttributes
| fio2::Operations::Traverse
| fio2::Operations::Enumerate;
/// All the fio2 rights required to represent fio::OPEN_RIGHT_WRITABLE.
static ref LEGACY_WRITABLE_RIGHTS: fio2::Operations =
fio2::Operations::WriteBytes
| fio2::Operations::UpdateAttributes
| fio2::Operations::ModifyDirectory;
/// All the fio2 rights required to represent fio::OPEN_RIGHT_EXECUTABLE.
static ref LEGACY_EXECUTABLE_RIGHTS: fio2::Operations = fio2::Operations::Execute;
}
/// Opaque rights type to define new traits like PartialOrd on.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Rights(fio2::Operations);
impl Rights {
/// Converts fuchsia.io2 directory rights to fuchsia.io compatible rights. This will be removed
/// once fuchsia.io2 is supported by component manager.
pub fn into_legacy(&self) -> u32 {
let mut flags: u32 = 0;
let rights = self.0;
// 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::OPEN_RIGHT_READABLE;
}
if rights.intersects(*LEGACY_WRITABLE_RIGHTS) {
flags |= fio::OPEN_RIGHT_WRITABLE;
}
if rights.contains(fio2::Operations::Execute) {
flags |= fio::OPEN_RIGHT_EXECUTABLE;
}
if rights.contains(fio2::Operations::Admin) {
flags |= fio::OPEN_RIGHT_ADMIN;
}
// Since there is no direct translation for connect in CV1 we must explicitly define it
// here as both flags.
//
// TODO(fxbug.dev/60673): Is this correct? ReadBytes | Connect seems like it should translate to
// READABLE | WRITABLE, not empty rights.
if flags == 0 && rights.contains(fio2::Operations::Connect) {
flags |= fio::OPEN_RIGHT_READABLE | fio::OPEN_RIGHT_WRITABLE;
}
flags
}
}
/// Allows creating rights from fio2::Operations.
impl From<fio2::Operations> for Rights {
fn from(operations: fio2::Operations) -> Self {
Rights(operations)
}
}
#[derive(Debug, Error, Clone)]
pub enum RightsError {
#[error("Requested rights greater than provided rights")]
Invalid,
#[error("Directory routes must end at source with a rights declaration")]
InvalidFinalize,
}
impl RightsError {
/// Convert this error into its approximate `zx::Status` equivalent.
pub fn as_zx_status(&self) -> zx::Status {
zx::Status::UNAVAILABLE
}
}
impl WalkStateUnit for Rights {
type Error = RightsError;
/// 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(RightsError::Invalid)
}
}
fn finalize_error() -> Self::Error {
RightsError::InvalidFinalize
}
}
#[cfg(test)]
mod tests {
use super::*;
use matches::assert_matches;
#[test]
fn validate_next() {
assert_matches!(
Rights::from(fio2::Operations::empty())
.validate_next(&Rights::from(*LEGACY_READABLE_RIGHTS)),
Ok(())
);
assert_matches!(
Rights::from(fio2::Operations::ReadBytes | fio2::Operations::GetAttributes)
.validate_next(&Rights::from(*LEGACY_READABLE_RIGHTS)),
Ok(())
);
assert_matches!(
Rights::from(Rights::from(*LEGACY_READABLE_RIGHTS)).validate_next(&Rights::from(
fio2::Operations::ReadBytes | fio2::Operations::GetAttributes
)),
Err(RightsError::Invalid)
);
assert_matches!(
Rights::from(fio2::Operations::WriteBytes).validate_next(&Rights::from(
fio2::Operations::ReadBytes | fio2::Operations::GetAttributes
)),
Err(RightsError::Invalid)
);
}
#[test]
fn into_legacy() {
assert_eq!(Rights::from(*LEGACY_READABLE_RIGHTS).into_legacy(), fio::OPEN_RIGHT_READABLE);
assert_eq!(Rights::from(*LEGACY_WRITABLE_RIGHTS).into_legacy(), fio::OPEN_RIGHT_WRITABLE);
assert_eq!(
Rights::from(*LEGACY_EXECUTABLE_RIGHTS).into_legacy(),
fio::OPEN_RIGHT_EXECUTABLE
);
assert_eq!(
Rights::from(*LEGACY_READABLE_RIGHTS | *LEGACY_WRITABLE_RIGHTS).into_legacy(),
fio::OPEN_RIGHT_READABLE | fio::OPEN_RIGHT_WRITABLE
);
assert_eq!(
Rights::from(
*LEGACY_READABLE_RIGHTS | *LEGACY_WRITABLE_RIGHTS | *LEGACY_EXECUTABLE_RIGHTS
)
.into_legacy(),
fio::OPEN_RIGHT_READABLE | fio::OPEN_RIGHT_WRITABLE | fio::OPEN_RIGHT_EXECUTABLE
);
assert_eq!(
Rights::from(fio2::Operations::ReadBytes).into_legacy(),
fio::OPEN_RIGHT_READABLE
);
assert_eq!(
Rights::from(fio2::Operations::GetAttributes).into_legacy(),
fio::OPEN_RIGHT_READABLE
);
assert_eq!(
Rights::from(fio2::Operations::Traverse).into_legacy(),
fio::OPEN_RIGHT_READABLE
);
assert_eq!(
Rights::from(fio2::Operations::Enumerate).into_legacy(),
fio::OPEN_RIGHT_READABLE
);
assert_eq!(
Rights::from(fio2::Operations::WriteBytes).into_legacy(),
fio::OPEN_RIGHT_WRITABLE
);
assert_eq!(
Rights::from(fio2::Operations::UpdateAttributes).into_legacy(),
fio::OPEN_RIGHT_WRITABLE
);
assert_eq!(
Rights::from(fio2::Operations::ModifyDirectory).into_legacy(),
fio::OPEN_RIGHT_WRITABLE
);
}
}