Skip to content

Commit 0cff0d2

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

4 files changed

Lines changed: 125 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: 72 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,63 @@ 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+
if filename.is_empty() {
479+
continue;
480+
}
481+
debug!("[Hermit image] Processing tar entry: {}", filename);
482+
483+
let mode = i.posix_header().mode.to_flags().map_err(|e| {
484+
error!(
485+
"[Hermit image] Tar entry {filename:?} has invalid mode: {:?}; {e}",
486+
i.posix_header().mode,
487+
);
488+
Errno::Inval
489+
})?;
490+
let mode = AccessPermission::from_bits(mode.bits() as u32).ok_or_else(|| {
491+
error!("[Hermit image] Tar entry {filename:?} has invalid mode: {mode:?}");
492+
Errno::Inval
493+
})?;
494+
495+
for (i, _) in filename.match_indices("/") {
496+
let part = &filename[..i];
497+
if this.traverse_lstat(part).is_err() {
498+
this.traverse_mkdir(
499+
part,
500+
AccessPermission::S_IRUSR
501+
| AccessPermission::S_IWUSR
502+
| AccessPermission::S_IRGRP,
503+
)
504+
.inspect_err(|e| {
505+
error!("[Hermit image] Unable to mkdir {part:?}: {e}");
506+
})?;
507+
}
508+
}
509+
510+
this.traverse_create_file(filename, i.data(), mode)
511+
.inspect_err(|e| {
512+
error!("[Hermit image] Unable to write entry {filename:?}: {e}");
513+
})?;
514+
}
515+
516+
Ok(this)
517+
}
518+
467519
async fn async_traverse_open(
468520
&self,
469521
path: &str,
@@ -693,11 +745,16 @@ impl VfsNode for MemDirectory {
693745
return directory.traverse_create_file(rest, data, mode);
694746
}
695747

696-
let file = RomFile::new(data, mode);
697-
self.inner
698-
.write()
699-
.await
700-
.insert(component.to_owned(), Box::new(file));
748+
let file: Box<dyn VfsNode> = if mode.contains(AccessPermission::S_IWUSR)
749+
|| mode.contains(AccessPermission::S_IWGRP)
750+
|| mode.contains(AccessPermission::S_IWOTH)
751+
{
752+
Box::new(RamFile::new_with_data(data.to_vec(), mode))
753+
} else {
754+
Box::new(RomFile::new(data, mode))
755+
};
756+
757+
self.inner.write().await.insert(component.to_owned(), file);
701758
Ok(())
702759
},
703760
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)