@@ -12,6 +12,7 @@ use core::mem::{MaybeUninit, offset_of};
1212use align_address:: Align ;
1313use async_lock:: { Mutex , RwLock } ;
1414use async_trait:: async_trait;
15+ use hermit_entry:: ThinTree ;
1516
1617use crate :: errno:: Errno ;
1718use crate :: executor:: block_on;
@@ -302,6 +303,10 @@ impl RomFile {
302303 pub fn new ( data : & ' static [ u8 ] , mode : AccessPermission ) -> Self {
303304 let microseconds = arch:: kernel:: systemtime:: now_micros ( ) ;
304305 let t = timespec:: from_usec ( microseconds as i64 ) ;
306+ Self :: new_with_timestamp ( data, mode, t)
307+ }
308+
309+ pub fn new_with_timestamp ( data : & ' static [ u8 ] , mode : AccessPermission , t : timespec ) -> Self {
305310 let attr = FileAttr {
306311 st_size : data. len ( ) . try_into ( ) . unwrap ( ) ,
307312 st_mode : mode | AccessPermission :: S_IFREG ,
@@ -372,19 +377,16 @@ impl RamFile {
372377 }
373378}
374379
380+ type MemDirectoryMap = BTreeMap < String , Box < dyn VfsNode + core:: marker:: Send + core:: marker:: Sync > > ;
381+
375382pub struct MemDirectoryInterface {
376383 /// Directory entries
377- inner :
378- Arc < RwLock < BTreeMap < String , Box < dyn VfsNode + core:: marker:: Send + core:: marker:: Sync > > > > ,
384+ inner : Arc < RwLock < MemDirectoryMap > > ,
379385 read_idx : Mutex < usize > ,
380386}
381387
382388impl MemDirectoryInterface {
383- pub fn new (
384- inner : Arc <
385- RwLock < BTreeMap < String , Box < dyn VfsNode + core:: marker:: Send + core:: marker:: Sync > > > ,
386- > ,
387- ) -> Self {
389+ pub fn new ( inner : Arc < RwLock < MemDirectoryMap > > ) -> Self {
388390 Self {
389391 inner,
390392 read_idx : Mutex :: new ( 0 ) ,
@@ -455,11 +457,47 @@ impl ObjectInterface for MemDirectoryInterface {
455457
456458#[ derive( Debug ) ]
457459pub ( crate ) struct MemDirectory {
458- inner :
459- Arc < RwLock < BTreeMap < String , Box < dyn VfsNode + core:: marker:: Send + core:: marker:: Sync > > > > ,
460+ inner : Arc < RwLock < MemDirectoryMap > > ,
460461 attr : FileAttr ,
461462}
462463
464+ fn thin_tree_to_vfs_node (
465+ thin_tree : ThinTree < ' static > ,
466+ t : timespec ,
467+ ) -> io:: Result < Box < dyn VfsNode + Send + Sync > > {
468+ Ok ( match thin_tree {
469+ ThinTree :: File { content, metadata } => {
470+ let mut st_mode = AccessPermission :: S_IRUSR ;
471+ if metadata. is_exec {
472+ st_mode |= AccessPermission :: S_IXUSR ;
473+ }
474+ Box :: new ( RomFile :: new_with_timestamp ( content, st_mode, t) )
475+ as Box < dyn VfsNode + Send + Sync >
476+ }
477+ ThinTree :: Directory ( d) => Box :: new ( MemDirectory {
478+ inner : Arc :: new ( RwLock :: new (
479+ d. into_iter ( )
480+ . map ( |( key, value) | {
481+ Ok ( (
482+ core:: str:: from_utf8 ( key)
483+ . expect ( "unable to interpret Hermit image entry filename as string" )
484+ . to_owned ( ) ,
485+ thin_tree_to_vfs_node ( value, t) ?,
486+ ) )
487+ } )
488+ . collect :: < io:: Result < _ > > ( ) ?,
489+ ) ) ,
490+ attr : FileAttr {
491+ st_mode : AccessPermission :: S_IRUSR | AccessPermission :: S_IFDIR ,
492+ st_atim : t,
493+ st_mtim : t,
494+ st_ctim : t,
495+ ..Default :: default ( )
496+ } ,
497+ } ) as Box < dyn VfsNode + Send + Sync > ,
498+ } )
499+ }
500+
463501impl MemDirectory {
464502 pub fn new ( mode : AccessPermission ) -> Self {
465503 let microseconds = arch:: kernel:: systemtime:: now_micros ( ) ;
@@ -477,6 +515,52 @@ impl MemDirectory {
477515 }
478516 }
479517
518+ /// Create a read-only memory tree from a tar image
519+ ///
520+ /// This ignores top-level files in the image
521+ pub fn try_from_image ( image : & ' static hermit_entry:: tar_parser:: Bytes ) -> io:: Result < Self > {
522+ let microseconds = arch:: kernel:: systemtime:: now_micros ( ) ;
523+ let t = timespec:: from_usec ( microseconds as i64 ) ;
524+
525+ // This is not perfectly memory-efficient, but we expect this
526+ // to be invoked usually once per kernel boot, so this cost should
527+ // be acceptable, given that it reduces code duplication and
528+ // makes implementation way easier.
529+ let thin_tree = ThinTree :: try_from_image ( image) . map_err ( |e| {
530+ error ! ( "unable to parse tar image: {e:?}" ) ;
531+ Errno :: Inval
532+ } ) ?;
533+
534+ let tree = match thin_tree {
535+ ThinTree :: Directory ( d) => d
536+ . into_iter ( )
537+ . map ( |( key, value) | {
538+ Ok ( (
539+ core:: str:: from_utf8 ( key)
540+ . expect ( "unable to interpret Hermit image entry filename as string" )
541+ . to_owned ( ) ,
542+ thin_tree_to_vfs_node ( value, t) ?,
543+ ) )
544+ } )
545+ . collect :: < io:: Result < _ > > ( ) ?,
546+ ThinTree :: File { .. } => {
547+ error ! ( "root of image isn't a directory" ) ;
548+ return Err ( Errno :: Inval ) ;
549+ }
550+ } ;
551+
552+ Ok ( Self {
553+ inner : Arc :: new ( RwLock :: new ( tree) ) ,
554+ attr : FileAttr {
555+ st_mode : AccessPermission :: S_IRUSR | AccessPermission :: S_IFDIR ,
556+ st_atim : t,
557+ st_mtim : t,
558+ st_ctim : t,
559+ ..Default :: default ( )
560+ } ,
561+ } )
562+ }
563+
480564 async fn async_traverse_open (
481565 & self ,
482566 components : & mut Vec < & str > ,
0 commit comments