Skip to content
Draft
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
16 changes: 8 additions & 8 deletions crates/vfs/src/tree/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::collections::BTreeMap;
use astr::{AStr, CowAStr};

use crate::path;
use crate::tree::{Kind, Tree};
use crate::tree::Tree;

use super::{BlitFile, Error, File};

Expand Down Expand Up @@ -94,7 +94,7 @@ impl<T: BlitFile> TreeBuilder<T> {

// Resolve symlinks-to-dirs
for link in self.explicit.iter() {
if let Kind::Symlink(target) = &link.kind {
if let Some(target) = link.kind.as_symlink() {
// Resolve the link.
let target = if target.starts_with('/') {
CowAStr::Borrowed(target)
Expand Down Expand Up @@ -156,7 +156,7 @@ mod tests {
fn from(value: AStr) -> Self {
Self {
path: value,
kind: Kind::Directory,
kind: Kind::DIRECTORY,
id: "Virtual".into(),
}
}
Expand Down Expand Up @@ -191,27 +191,27 @@ mod tests {
let paths = vec![
CustomFile {
path: "/usr/bin/nano".into(),
kind: Kind::Regular,
kind: Kind::REGULAR,
id: "nano".into(),
},
CustomFile {
path: "/usr/bin/rnano".into(),
kind: Kind::Symlink("nano".into()),
kind: Kind::symlink("nano".into()),
id: "nano".into(),
},
CustomFile {
path: "/usr/share/nano".into(),
kind: Kind::Directory,
kind: Kind::DIRECTORY,
id: "nano".into(),
},
CustomFile {
path: "/var/run/lock".into(),
kind: Kind::Symlink("/run/lock".into()),
kind: Kind::symlink("/run/lock".into()),
id: "baselayout".into(),
},
CustomFile {
path: "/var/run/lock/subsys/1".into(),
kind: Kind::Regular,
kind: Kind::REGULAR,
id: "baselayout".into(),
},
];
Expand Down
135 changes: 135 additions & 0 deletions crates/vfs/src/tree/kind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
use std::mem::ManuallyDrop;

use astr::AStr;

const _: () = {
// AStr must contain as its first field a pointer to an Arc allocation
// (one with alignment 4 or larger) for the REGULAR and DIRECTORY values
// to definitely not overlap with any valid AStr value.
//
// Since we can't really assert on this, assert that it's pointer-sized
// for now.
assert!(size_of::<usize>() == size_of::<ManuallyDrop<AStr>>());
};

pub union Kind {
addr_or_tag: usize,
symlink_target: ManuallyDrop<AStr>,
}

impl Kind {
/// Regular path
pub const REGULAR: Self = Self { addr_or_tag: 0x1 };
/// Directory (parenting node)
pub const DIRECTORY: Self = Self { addr_or_tag: 0x2 };

pub fn symlink(target: AStr) -> Self {
Self {
symlink_target: ManuallyDrop::new(target),
}
}

fn addr_or_tag(&self) -> usize {
// Safety: We asserted that the two fields in the union are of the same
// size at the start of the file. We know AStr is a pointer, so there's
// no padding bytes involved. Reading either usize or a pointer as a
// usize is always valid.
unsafe { self.addr_or_tag }
}

pub fn is_regular(&self) -> bool {
self.addr_or_tag() == 0x1
}

pub fn is_directory(&self) -> bool {
self.addr_or_tag() == 0x2
}

pub fn is_symlink(&self) -> bool {
self.addr_or_tag() >= 0x8
}

pub fn as_symlink(&self) -> Option<&AStr> {
self.is_symlink().then(|| unsafe { &*self.symlink_target })
}
}

impl Clone for Kind {
fn clone(&self) -> Self {
if let Some(target) = self.as_symlink() {
let symlink_target = ManuallyDrop::new(AStr::clone(target));
Self { symlink_target }
} else {
let addr_or_tag = self.addr_or_tag();
Self { addr_or_tag }
}
}
}

impl Default for Kind {
fn default() -> Self {
Self::DIRECTORY
}
}

impl Drop for Kind {
fn drop(&mut self) {
if self.is_symlink() {
unsafe {
ManuallyDrop::drop(&mut self.symlink_target);
}
}
}
}

mod debug_impl {
use std::fmt;

#[derive(Debug)]
#[allow(dead_code)]
enum Kind<'a> {
Regular,
Directory,
Symlink(&'a str),
}

impl super::Kind {
fn to_enum(&self) -> Kind<'_> {
if self.is_regular() {
Kind::Regular
} else if let Some(target) = self.as_symlink() {
Kind::Symlink(target)
} else {
Kind::Directory
}
}
}

impl fmt::Debug for super::Kind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.to_enum().fmt(f)
}
}
}

// Run through miri to validate unsafe code.
#[cfg(test)]
mod tests {
use crate::tree::Kind;

#[test]
fn clone_drop_symlink() {
let kind = Kind::symlink("/test/thing".into());
let kind2 = kind.clone();
drop(kind);
drop(kind2);
}

#[test]
fn clone_drop_regular() {
let kind = Kind::REGULAR;
let kind2 = kind.clone();
drop(kind);
drop(kind2);
}
}
21 changes: 2 additions & 19 deletions crates/vfs/src/tree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,9 @@ use snafu::Snafu;
use crate::path::{self, VfsPath};

pub mod builder;
mod kind;

#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Kind {
// Regular path
Regular,

// Directory (parenting node)
#[default]
Directory,

// Symlink to somewhere else.
Symlink(AStr),
}

impl Kind {
#[must_use]
pub fn is_directory(&self) -> bool {
matches!(self, Self::Directory)
}
}
pub use self::kind::Kind;

/// Simple generic interface for blittable files while retaining details.
///
Expand Down
6 changes: 3 additions & 3 deletions moss/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1133,9 +1133,9 @@ impl BlitFile for PendingFile {
/// Match internal kind to minimalist vfs kind
fn kind(&self) -> vfs::tree::Kind {
match &self.layout.entry {
layout::Entry::Symlink(source, _) => vfs::tree::Kind::Symlink(source.clone()),
layout::Entry::Directory(_) => vfs::tree::Kind::Directory,
_ => vfs::tree::Kind::Regular,
layout::Entry::Symlink(source, _) => vfs::tree::Kind::symlink(source.clone()),
layout::Entry::Directory(_) => vfs::tree::Kind::DIRECTORY,
_ => vfs::tree::Kind::REGULAR,
}
}

Expand Down
Loading