Skip to content

Commit 250cae7

Browse files
committed
feat(fs/mem): tarfs implementation
1 parent 1677256 commit 250cae7

4 files changed

Lines changed: 106 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: 53 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 { data, attr })),
369364
}
370365
}
371366
}
@@ -464,6 +459,44 @@ 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): {filename:?}; {e}",
475+
);
476+
Errno::Inval
477+
})?;
478+
479+
let mode = i.posix_header().mode.to_flags().map_err(|e| {
480+
error!(
481+
"[Hermit image] Tar entry {filename:?} has invalid mode: {:?}; {e}",
482+
i.posix_header().mode,
483+
);
484+
Errno::Inval
485+
})?;
486+
let mode = AccessPermission::from_bits(mode.bits() as u32).ok_or_else(|| {
487+
error!("[Hermit image] Tar entry {filename:?} has invalid mode: {mode:?}");
488+
Errno::Inval
489+
})?;
490+
491+
this.traverse_create_file(filename, i.data(), mode)
492+
.inspect_err(|e| {
493+
error!("[Hermit image] Unable to write entry {filename:?}: {e}");
494+
})?;
495+
}
496+
497+
Ok(this)
498+
}
499+
467500
async fn async_traverse_open(
468501
&self,
469502
path: &str,
@@ -693,11 +726,16 @@ impl VfsNode for MemDirectory {
693726
return directory.traverse_create_file(rest, data, mode);
694727
}
695728

696-
let file = RomFile::new(data, mode);
697-
self.inner
698-
.write()
699-
.await
700-
.insert(component.to_owned(), Box::new(file));
729+
let file: Box<dyn VfsNode> = if mode.contains(AccessPermission::S_IWUSR)
730+
|| mode.contains(AccessPermission::S_IWGRP)
731+
|| mode.contains(AccessPermission::S_IWOTH)
732+
{
733+
Box::new(RamFile::new_with_data(data.to_vec(), mode))
734+
} else {
735+
Box::new(RomFile::new(data, mode))
736+
};
737+
738+
self.inner.write().await.insert(component.to_owned(), file);
701739
Ok(())
702740
},
703741
None,

src/fs/mod.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,46 @@ 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() == size_of::<*const u8>() && len.len() == size_of::<usize>() {
325+
let addr = usize::from_be_bytes(addr.try_into().unwrap());
326+
let len = usize::from_be_bytes(len.try_into().unwrap());
327+
info!("Hermit image at {addr:x} with length {len:x}");
328+
// technically, the following is UB, because the kernel might be contained within...
329+
tar_image = Some(unsafe {
330+
slice::from_raw_parts(core::ptr::with_exposed_provenance::<u8>(addr), len)
331+
});
332+
} else {
333+
error!(
334+
"Hermit image supplied with invalid address range (#addr = {}, #len = {})",
335+
addr.len(),
336+
len.len(),
337+
);
338+
}
339+
}
340+
}
341+
342+
if let Some(tar_image) = tar_image {
343+
root_filesystem.root =
344+
MemDirectory::try_from_image(tar_image).expect("Unable to parse Hermit Image");
345+
}
346+
}
308347

309348
root_filesystem
310349
.mkdir("/tmp", AccessPermission::from_bits(0o777).unwrap())

0 commit comments

Comments
 (0)