diff --git a/Cargo.lock b/Cargo.lock index 39bb70f3d5..0774bc3e1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -863,6 +863,7 @@ dependencies = [ "hermit-entry", "hermit-macro", "hermit-sync", + "hex", "llvm-tools", "log", "mem-barrier", @@ -882,6 +883,7 @@ dependencies = [ "smoltcp", "take-static", "talc", + "tar-no-std", "thiserror", "time", "tock-registers 0.10.1", @@ -917,6 +919,12 @@ dependencies = [ "spinning_top", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "home" version = "0.5.12" @@ -1954,6 +1962,17 @@ dependencies = [ "xattr", ] +[[package]] +name = "tar-no-std" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715f9a4586706a61c571cb5ee1c3ac2bbb2cf63e15bce772307b95befef5f5ee" +dependencies = [ + "bitflags 2.11.1", + "log", + "num-traits", +] + [[package]] name = "thiserror" version = "2.0.18" diff --git a/Cargo.toml b/Cargo.toml index a9904c8533..854e2b07b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -326,10 +326,12 @@ simple-shell = { version = "0.0.1", optional = true } smallvec = { version = "1", features = ["const_new"] } take-static = "0.1" talc = { version = "5" } +tar-no-std = { version = "0.4", features = ["alloc"] } thiserror = { version = "2", default-features = false } time = { version = "0.3", default-features = false } volatile = "0.6" uhyve-interface = "0.1.4" +hex = { version = "0.4", default-features = false, features = ["alloc"] } [dependencies.smoltcp] version = "0.13" diff --git a/src/fs/mem.rs b/src/fs/mem.rs index 61002da066..6de57a9c39 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -122,15 +122,6 @@ pub(crate) struct RamFileInner { pub attr: FileAttr, } -impl RamFileInner { - pub fn new(attr: FileAttr) -> Self { - Self { - data: Vec::new(), - attr, - } - } -} - pub struct RamFileInterface { /// Position within the file pos: Mutex, @@ -354,6 +345,10 @@ impl VfsNode for RamFile { impl RamFile { pub fn new(mode: AccessPermission) -> Self { + Self::new_with_data(Vec::new(), mode) + } + + fn new_with_data(data: Vec, mode: AccessPermission) -> Self { let microseconds = arch::kernel::systemtime::now_micros(); let t = timespec::from_usec(microseconds as i64); let attr = FileAttr { @@ -365,7 +360,7 @@ impl RamFile { }; Self { - data: Arc::new(RwLock::new(RamFileInner::new(attr))), + data: Arc::new(RwLock::new(RamFileInner { data, attr })), } } } @@ -464,6 +459,68 @@ impl MemDirectory { } } + #[allow(unused)] + pub fn try_from_image(image: &'static [u8]) -> io::Result { + let this = Self::new(AccessPermission::S_IRUSR); + + for i in image.chunks(1024) { + debug!("[DUMP] {}", hex::encode(i)); + } + + let taref = tar_no_std::TarArchiveRef::new(image).map_err(|e| { + error!("[Hermit image] Tar file has invalid format: {e:?}"); + Errno::Inval + })?; + + for i in taref.entries() { + let filename = i.filename(); + let filename = filename.as_str().map_err(|e| { + error!( + "[Hermit image] Tar entry has not supported filename (non UTF-8): {filename:?}; {e}", + ); + Errno::Inval + })?; + if filename.is_empty() { + continue; + } + debug!("[Hermit image] Processing tar entry: {filename}"); + + let mode = i.posix_header().mode.to_flags().map_err(|e| { + error!( + "[Hermit image] Tar entry {filename:?} has invalid mode: {:?}; {e}", + i.posix_header().mode, + ); + Errno::Inval + })?; + let mode = AccessPermission::from_bits(mode.bits() as u32).ok_or_else(|| { + error!("[Hermit image] Tar entry {filename:?} has invalid mode: {mode:?}"); + Errno::Inval + })?; + + for (i, _) in filename.match_indices("/") { + let part = &filename[..i]; + if this.traverse_lstat(part).is_err() { + this.traverse_mkdir( + part, + AccessPermission::S_IRUSR + | AccessPermission::S_IWUSR + | AccessPermission::S_IRGRP, + ) + .inspect_err(|e| { + error!("[Hermit image] Unable to mkdir {part:?}: {e}"); + })?; + } + } + + this.traverse_create_file(filename, i.data(), mode) + .inspect_err(|e| { + error!("[Hermit image] Unable to write entry {filename:?}: {e}"); + })?; + } + + Ok(this) + } + async fn async_traverse_open( &self, path: &str, @@ -693,11 +750,16 @@ impl VfsNode for MemDirectory { return directory.traverse_create_file(rest, data, mode); } - let file = RomFile::new(data, mode); - self.inner - .write() - .await - .insert(component.to_owned(), Box::new(file)); + let file: Box = if mode.contains(AccessPermission::S_IWUSR) + || mode.contains(AccessPermission::S_IWGRP) + || mode.contains(AccessPermission::S_IWOTH) + { + Box::new(RamFile::new_with_data(data.to_vec(), mode)) + } else { + Box::new(RomFile::new(data, mode)) + }; + + self.inner.write().await.insert(component.to_owned(), file); Ok(()) }, None,