Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions nvx-rs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -937,13 +937,13 @@ action that needs more than 256 rules.
### `nvx::host::filesystem`

The planner of every NVX caller: `plan` turns an `ExportRequest`, the mapped
host paths at explicit guest targets or at those of the host-path convention,
their denied, allowed, and write paths, and the owner, into the `ExportPlan` of
host paths through OpenVMM's aggregate virtio-fs device, for a root's
`ReservedTargets`, the blockless root's or the container root's: its children,
its ordered binds, which give the host-mapping table, and the identities that
each start checks again; and `guest_path` and `resolve_guest_path` (also at
`nvx::`).
host paths at their guest targets, their denied, allowed, and write paths, and
the owner, into the `ExportPlan` of host paths through OpenVMM's aggregate
virtio-fs device, for a root's `ReservedTargets`, the blockless root's or the
container root's: its children, its ordered binds, which give the host-mapping
table, and the identities that each start checks again; and `guest_path` (also
at `nvx::`), the guest path at which the host-path convention places a resolved
host path.

### `nvx::host::nvxs`

Expand Down
6 changes: 0 additions & 6 deletions nvx-rs/public-api/linux.txt
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,6 @@ struct nvx::host::filesystem::ExportRequest
field nvx::host::filesystem::ExportRequest::allowed
field nvx::host::filesystem::ExportRequest::denied
field nvx::host::filesystem::ExportRequest::mappings
method nvx::host::filesystem::ExportRequest::new
field nvx::host::filesystem::ExportRequest::owner
field nvx::host::filesystem::ExportRequest::reserved
field nvx::host::filesystem::ExportRequest::write
Expand All @@ -978,7 +977,6 @@ field nvx::host::filesystem::FileIdentity::index
const nvx::host::filesystem::GUEST_EXPORT
const nvx::host::filesystem::MAX_CHILDREN
struct nvx::host::filesystem::Mapping
method nvx::host::filesystem::Mapping::convention
field nvx::host::filesystem::Mapping::name
field nvx::host::filesystem::Mapping::path
field nvx::host::filesystem::Mapping::read_only
Expand All @@ -987,7 +985,6 @@ enum nvx::host::filesystem::Owner
variant nvx::host::filesystem::Owner::Caller
variant nvx::host::filesystem::Owner::Vmm
struct nvx::host::filesystem::PathList
const nvx::host::filesystem::PathList::EMPTY
field nvx::host::filesystem::PathList::name
field nvx::host::filesystem::PathList::paths
const nvx::host::filesystem::RESERVED_TARGETS
Expand All @@ -998,12 +995,10 @@ field nvx::host::filesystem::ReservedTargets::exact
method nvx::host::filesystem::ReservedTargets::reservation
field nvx::host::filesystem::ReservedTargets::trees
enum nvx::host::filesystem::Target
variant nvx::host::filesystem::Target::Convention
variant nvx::host::filesystem::Target::Guest
field nvx::host::filesystem::Target::Guest::0
fn nvx::host::filesystem::guest_path
fn nvx::host::filesystem::plan
fn nvx::host::filesystem::resolve_guest_path
mod nvx::host::network
enum nvx::host::network::Access
variant nvx::host::network::Access::Allow
Expand Down Expand Up @@ -1799,7 +1794,6 @@ const nvx::protocol::view::HOSTNAME_TOKEN
const nvx::protocol::view::MACHINE_ID_TOKEN
fn nvx::protocol::view::parse
fn nvx::protocol::view::tokens
fn nvx::resolve_guest_path
mod nvx::testing (hidden)
mod nvx::testing::fake_openvmm (hidden)
fn nvx::testing::fake_openvmm::main (hidden)
Expand Down
6 changes: 0 additions & 6 deletions nvx-rs/public-api/windows.txt
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,6 @@ struct nvx::host::filesystem::ExportRequest
field nvx::host::filesystem::ExportRequest::allowed
field nvx::host::filesystem::ExportRequest::denied
field nvx::host::filesystem::ExportRequest::mappings
method nvx::host::filesystem::ExportRequest::new
field nvx::host::filesystem::ExportRequest::owner
field nvx::host::filesystem::ExportRequest::reserved
field nvx::host::filesystem::ExportRequest::write
Expand All @@ -436,7 +435,6 @@ field nvx::host::filesystem::FileIdentity::index
const nvx::host::filesystem::GUEST_EXPORT
const nvx::host::filesystem::MAX_CHILDREN
struct nvx::host::filesystem::Mapping
method nvx::host::filesystem::Mapping::convention
field nvx::host::filesystem::Mapping::name
field nvx::host::filesystem::Mapping::path
field nvx::host::filesystem::Mapping::read_only
Expand All @@ -445,7 +443,6 @@ enum nvx::host::filesystem::Owner
variant nvx::host::filesystem::Owner::Caller
variant nvx::host::filesystem::Owner::Vmm
struct nvx::host::filesystem::PathList
const nvx::host::filesystem::PathList::EMPTY
field nvx::host::filesystem::PathList::name
field nvx::host::filesystem::PathList::paths
const nvx::host::filesystem::RESERVED_TARGETS
Expand All @@ -456,12 +453,10 @@ field nvx::host::filesystem::ReservedTargets::exact
method nvx::host::filesystem::ReservedTargets::reservation
field nvx::host::filesystem::ReservedTargets::trees
enum nvx::host::filesystem::Target
variant nvx::host::filesystem::Target::Convention
variant nvx::host::filesystem::Target::Guest
field nvx::host::filesystem::Target::Guest::0
fn nvx::host::filesystem::guest_path
fn nvx::host::filesystem::plan
fn nvx::host::filesystem::resolve_guest_path
mod nvx::host::network
enum nvx::host::network::Access
variant nvx::host::network::Access::Allow
Expand Down Expand Up @@ -1257,7 +1252,6 @@ const nvx::protocol::view::HOSTNAME_TOKEN
const nvx::protocol::view::MACHINE_ID_TOKEN
fn nvx::protocol::view::parse
fn nvx::protocol::view::tokens
fn nvx::resolve_guest_path
mod nvx::testing (hidden)
mod nvx::testing::fake_openvmm (hidden)
fn nvx::testing::fake_openvmm::main (hidden)
Expand Down
15 changes: 10 additions & 5 deletions nvx-rs/src/host/cli/run/guest_tokens/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::*;
use crate::host::filesystem::{ExportRequest, Mapping, ReservedTargets, Target};
use crate::host::filesystem::{ExportRequest, Mapping, Owner, PathList, ReservedTargets, Target};

#[test]
fn blockless_modes_and_workloads_use_cli_owned_names() {
Expand Down Expand Up @@ -64,10 +64,15 @@ fn planned_boot_shares_keep_table_sources_targets_access_and_file_kind() {
read_only: true,
},
];
let plan = crate::host::filesystem::plan(&ExportRequest::new(
&mappings,
ReservedTargets::BLOCKLESS_ROOT,
))
let none = |name| PathList { name, paths: &[] };
let plan = crate::host::filesystem::plan(&ExportRequest {
mappings: &mappings,
denied: none("--mount-deny"),
allowed: none("--mount-allow"),
write: none("--mount-write"),
owner: Owner::Vmm,
reserved: ReservedTargets::BLOCKLESS_ROOT,
})
.unwrap()
.unwrap();
let table = plan.guest_table();
Expand Down
14 changes: 0 additions & 14 deletions nvx-rs/src/host/filesystem/guest_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

use std::path::{Component, Path, Prefix};

use super::host_path::canonicalize;

/// Joins an aggregate child index and child-relative source into a guest export path.
pub(super) fn guest_source(child: usize, source: &str) -> String {
if source.is_empty() {
Expand Down Expand Up @@ -54,18 +52,6 @@ pub fn guest_path(path: &Path) -> Option<String> {
Some(guest)
}

/// Returns the guest path of an existing host path the way mappings derive it: from the
/// resolved path, so links, letter case, and short names do not matter. A path that does not
/// exist is translated as written.
///
/// Use it to turn a host working directory into a `process.cwd` inside a mapped path.
pub fn resolve_guest_path(path: &Path) -> Option<String> {
match canonicalize(path) {
Ok(canonical) => guest_path(&canonical),
Err(_) => guest_path(path),
}
}

/// Returns whether the guest path `path` is `directory` or one of its descendants, comparing
/// whole components: `/run/nvx` is within `/run`, but `/runner` is not, and every absolute path is
/// within the root, `/`.
Expand Down
103 changes: 22 additions & 81 deletions nvx-rs/src/host/filesystem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
//! visible; the paths to which the writes of a read-write directory are narrowed; the host
//! identity of the file operations; and the guest targets that the guest's root keeps for itself.
//!
//! - A caller gives each target, or lets MXC's convention for Linux guests derive it from the
//! resolved host path: a Windows path `C:\work\src` appears at `/mnt/c/work/src`, and a Linux
//! path at the same path ([`fn@guest_path`]).
//! - A caller gives each target, such as the guest path that MXC's convention for Linux guests
//! derives from a resolved host path ([`fn@guest_path`]): a Windows path `C:\work\src` appears
//! at `/mnt/c/work/src`, and a Linux path at the same path.
//! - The reserved targets depend on the root, and the caller supplies them ([`ReservedTargets`]):
//! the blockless root keeps the guest's system trees, and the container root of a layered
//! sandbox keeps the trees of its runtime.
Expand Down Expand Up @@ -51,9 +51,9 @@ use crate::protocol::MAX_HOST_MAPPINGS;
use crate::protocol::export::EXPORT_DIRECTORY;
use crate::protocol::share::{RESERVED_EXACT_MOUNT_TARGETS, RESERVED_MOUNT_TARGETS};

pub use self::guest_path::guest_path;
pub(crate) use self::guest_path::within;
use self::guest_path::{depth, guest_source};
pub use self::guest_path::{guest_path, resolve_guest_path};
pub(crate) use self::host_path::{canonicalize, is_reparse_point};
use self::host_path::{identity, relative_text};
use self::limits::{check_policy_paths, check_root};
Expand Down Expand Up @@ -240,8 +240,6 @@ impl Bind {
/// Where the guest sees a mapped host path.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Target<'a> {
/// At the guest path that [`fn@guest_path`] derives from the resolved host path.
Convention,
/// At this absolute guest path: a path other than the root, of at most 4,095 bytes, without
/// NUL characters or empty, `.`, or `..` components.
Guest(&'a str),
Expand All @@ -261,19 +259,6 @@ pub struct Mapping<'a> {
pub read_only: bool,
}

impl<'a> Mapping<'a> {
/// Returns the mappings of the paths of `list`, under its name, at the guest paths of the
/// convention, read-only or read-write.
pub fn convention(list: PathList<'a>, read_only: bool) -> impl Iterator<Item = Self> + 'a {
list.paths.iter().map(move |path| Self {
name: list.name,
path,
target: Target::Convention,
read_only,
})
}
}

/// A list of host paths, and the name by which messages refer to its entries, such as
/// `readOnly`.
#[derive(Debug, Clone, Copy)]
Expand All @@ -284,14 +269,6 @@ pub struct PathList<'a> {
pub paths: &'a [PathBuf],
}

impl PathList<'static> {
/// A list without paths.
pub const EMPTY: Self = Self {
name: "",
paths: &[],
};
}

/// The host paths that a sandbox maps, those that it hides inside them and shows again inside
/// those, the paths to which it narrows writes, the host identity of the file operations, and the
/// guest targets that the guest's root keeps for itself.
Expand All @@ -315,27 +292,11 @@ pub struct ExportRequest<'a> {
pub reserved: ReservedTargets<'a>,
}

impl<'a> ExportRequest<'a> {
/// Returns the request that maps `mappings` into a root that reserves `reserved`, without
/// denied, allowed, or write paths, as the VMM.
pub fn new(mappings: &'a [Mapping<'a>], reserved: ReservedTargets<'a>) -> Self {
Self {
mappings,
denied: PathList::EMPTY,
allowed: PathList::EMPTY,
write: PathList::EMPTY,
owner: Owner::Vmm,
reserved,
}
}
}

/// Plans the export of `request`, or returns `None` if it maps nothing.
///
/// Mapped paths must exist, and resolve to paths that are valid UTF-8. A mapping's target is the
/// one that the request gives, or the guest path that the convention derives from the resolved
/// host path, so two spellings of one object (links, letter case, or short names) map to one guest
/// path; either must be free in the request's [`ReservedTargets`]. An object mapped twice at one
/// Mapped paths must exist, and resolve to paths that are valid UTF-8, so two spellings of one
/// object (links, letter case, or short names) name one object. A mapping's target, which the
/// request gives, must be free in the request's [`ReservedTargets`]. An object mapped twice at one
/// target is mapped once, read-only if either mapping is. Two objects never share a target, and a
/// target inside another mapping's must show the very object that the other mapping shows there.
/// A whole volume cannot be mapped, and a policy that would need more children than OpenVMM
Expand Down Expand Up @@ -556,7 +517,7 @@ fn resolve_mapping<'a>(mapping: &Mapping<'a>, reserved: ReservedTargets<'_>) ->
let Mapping {
name,
path,
target,
target: Target::Guest(target),
read_only,
} = *mapping;
let canonical = canonicalize(path).map_err(|error| {
Expand Down Expand Up @@ -584,44 +545,24 @@ fn resolve_mapping<'a>(mapping: &Mapping<'a>, reserved: ReservedTargets<'_>) ->
{
return unsupported(format!("{name} entry {} {reason}", path.display()));
}
let target = match target {
Target::Convention => {
let Some(target) = guest_path(&canonical) else {
return unsupported(format!(
"{name} entry {} has no guest path; use an absolute local path",
path.display()
));
};
if let Some(reserved) = reserved.reservation(&target) {
return unsupported(format!(
"{name} entry {} would cover the guest's {reserved}",
path.display()
));
}
target
}
Target::Guest(target) => {
if let Some(problem) = target_problem(target) {
return unsupported(format!(
"{name} entry {} has the guest target {target:?}, which {problem}",
path.display()
));
}
if let Some(reserved) = reserved.reservation(target) {
return unsupported(format!(
"{name} entry {} has the guest target {target}, which would cover the \
guest's {reserved}",
path.display()
));
}
target.to_owned()
}
};
if let Some(problem) = target_problem(target) {
return unsupported(format!(
"{name} entry {} has the guest target {target:?}, which {problem}",
path.display()
));
}
if let Some(reserved) = reserved.reservation(target) {
return unsupported(format!(
"{name} entry {} has the guest target {target}, which would cover the guest's \
{reserved}",
path.display()
));
}
Ok(Mapped {
name,
given: path.to_path_buf(),
canonical,
target,
target: target.to_owned(),
read_only,
directory: metadata.is_dir(),
})
Expand Down
Loading
Loading