Skip to content

Commit e085146

Browse files
committed
feat(fs/mem): tarfs implementation
1 parent 8140a12 commit e085146

4 files changed

Lines changed: 111 additions & 16 deletions

File tree

Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ simple-shell = { version = "0.0.1", optional = true }
326326
smallvec = { version = "1", features = ["const_new"] }
327327
take-static = "0.1"
328328
talc = { version = "5" }
329+
tar-no-std = { version = "0.4", features = ["alloc"] }
329330
thiserror = { version = "2", default-features = false }
330331
time = { version = "0.3", default-features = false }
331332
volatile = "0.6"

src/fs/mem.rs

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,6 @@ pub(crate) struct RamFileInner {
122122
pub attr: FileAttr,
123123
}
124124

125-
impl RamFileInner {
126-
pub fn new(attr: FileAttr) -> Self {
127-
Self {
128-
data: Vec::new(),
129-
attr,
130-
}
131-
}
132-
}
133-
134125
pub struct RamFileInterface {
135126
/// Position within the file
136127
pos: Mutex<usize>,
@@ -354,6 +345,10 @@ impl VfsNode for RamFile {
354345

355346
impl RamFile {
356347
pub fn new(mode: AccessPermission) -> Self {
348+
Self::new_with_data(Vec::new(), mode)
349+
}
350+
351+
fn new_with_data(data: Vec<u8>, mode: AccessPermission) -> Self {
357352
let microseconds = arch::kernel::systemtime::now_micros();
358353
let t = timespec::from_usec(microseconds as i64);
359354
let attr = FileAttr {
@@ -365,7 +360,7 @@ impl RamFile {
365360
};
366361

367362
Self {
368-
data: Arc::new(RwLock::new(RamFileInner::new(attr))),
363+
data: Arc::new(RwLock::new(RamFileInner { attr, data })),
369364
}
370365
}
371366
}
@@ -464,6 +459,50 @@ impl MemDirectory {
464459
}
465460
}
466461

462+
pub fn try_from_image(image: &'static [u8]) -> io::Result<Self> {
463+
let this = Self::new(AccessPermission::S_IRUSR);
464+
465+
let taref = tar_no_std::TarArchiveRef::new(image).map_err(|e| {
466+
error!("[Hermit image] Tar file has invalid format: {e:?}");
467+
Errno::Inval
468+
})?;
469+
470+
for i in taref.entries() {
471+
let filename = i.filename();
472+
let filename = filename.as_str().map_err(|e| {
473+
error!(
474+
"[Hermit image] Tar entry has not supported filename (non UTF-8): {:?}; {}",
475+
filename, e,
476+
);
477+
Errno::Inval
478+
})?;
479+
480+
let mode = i.posix_header().mode.to_flags().map_err(|e| {
481+
error!(
482+
"[Hermit image] Tar entry {:?} has invalid mode: {:?}; {}",
483+
filename,
484+
i.posix_header().mode,
485+
e,
486+
);
487+
Errno::Inval
488+
})?;
489+
let mode = AccessPermission::from_bits(mode.bits() as u32).ok_or_else(|| {
490+
error!(
491+
"[Hermit image] Tar entry {:?} has invalid mode: {:?}",
492+
filename, mode,
493+
);
494+
Errno::Inval
495+
})?;
496+
497+
this.traverse_create_file(filename, i.data(), mode)
498+
.inspect_err(|e| {
499+
error!("[Hermit image] Unable to write entry {:?}: {}", filename, e,);
500+
})?;
501+
}
502+
503+
Ok(this)
504+
}
505+
467506
async fn async_traverse_open(
468507
&self,
469508
path: &str,
@@ -693,11 +732,16 @@ impl VfsNode for MemDirectory {
693732
return directory.traverse_create_file(rest, data, mode);
694733
}
695734

696-
let file = RomFile::new(data, mode);
697-
self.inner
698-
.write()
699-
.await
700-
.insert(component.to_owned(), Box::new(file));
735+
let file: Box<dyn VfsNode> = if mode.contains(AccessPermission::S_IWUSR)
736+
|| mode.contains(AccessPermission::S_IWGRP)
737+
|| mode.contains(AccessPermission::S_IWOTH)
738+
{
739+
Box::new(RamFile::new_with_data(data.to_vec(), mode))
740+
} else {
741+
Box::new(RomFile::new(data, mode))
742+
};
743+
744+
self.inner.write().await.insert(component.to_owned(), file);
701745
Ok(())
702746
},
703747
None,

src/fs/mod.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,45 @@ pub(crate) fn init() {
304304
const VERSION: &str = env!("CARGO_PKG_VERSION");
305305
const UTC_BUILT_TIME: &str = build_time::build_time_utc!();
306306

307-
let root_filesystem = Filesystem::new();
307+
let mut root_filesystem = Filesystem::new();
308+
309+
// Handle optional Hermit Image specified in FDT.
310+
if let Some(fdt) = crate::env::fdt() {
311+
let mut tar_image: Option<&'static [u8]> = None;
312+
313+
// per FDT specification, /chosen always exists.
314+
let chosen = fdt.find_node("/chosen").unwrap();
315+
if let Some(fdt::node::NodeProperty {
316+
value: image_reg, ..
317+
}) = chosen.property("image_reg")
318+
{
319+
let cell_sizes = fdt.root().cell_sizes();
320+
let split_point = cell_sizes.address_cells * 4;
321+
let end_point = split_point + cell_sizes.size_cells * 4;
322+
if image_reg.len() == end_point {
323+
let (addr, len) = image_reg.split_at(split_point);
324+
if addr.len() == core::mem::size_of::<*const u8>()
325+
&& len.len() == core::mem::size_of::<usize>()
326+
{
327+
// TODO: endian-ness?
328+
let addr = usize::from_ne_bytes(addr.try_into().unwrap());
329+
let len = usize::from_ne_bytes(len.try_into().unwrap());
330+
// technically, the following is UB, because the kernel might be contained within...
331+
tar_image = Some(unsafe {
332+
core::slice::from_raw_parts(
333+
core::ptr::with_exposed_provenance::<u8>(addr),
334+
len,
335+
)
336+
});
337+
}
338+
}
339+
}
340+
341+
if let Some(tar_image) = tar_image {
342+
root_filesystem.root =
343+
MemDirectory::try_from_image(tar_image).expect("Unable to parse Hermit Image");
344+
}
345+
}
308346

309347
root_filesystem
310348
.mkdir("/tmp", AccessPermission::from_bits(0o777).unwrap())

0 commit comments

Comments
 (0)