@@ -21,6 +21,7 @@ use core::mem::{MaybeUninit, offset_of};
2121use align_address:: Align ;
2222use async_lock:: { Mutex , RwLock } ;
2323use async_trait:: async_trait;
24+ use hermit_entry:: ThinTree ;
2425
2526use crate :: errno:: Errno ;
2627use crate :: executor:: block_on;
@@ -319,6 +320,10 @@ impl RomFile {
319320 pub fn new ( data : & ' static [ u8 ] , mode : AccessPermission ) -> Self {
320321 let microseconds = arch:: kernel:: systemtime:: now_micros ( ) ;
321322 let t = timespec:: from_usec ( microseconds as i64 ) ;
323+ Self :: new_with_timestamp ( data, mode, t)
324+ }
325+
326+ pub fn new_with_timestamp ( data : & ' static [ u8 ] , mode : AccessPermission , t : timespec ) -> Self {
322327 let attr = FileAttr {
323328 st_size : data. len ( ) . try_into ( ) . unwrap ( ) ,
324329 st_mode : mode | AccessPermission :: S_IFREG ,
@@ -477,6 +482,43 @@ pub(crate) struct MemDirectory {
477482 attr : FileAttr ,
478483}
479484
485+ fn thin_tree_to_vfs_node (
486+ thin_tree : ThinTree < ' static > ,
487+ t : timespec ,
488+ ) -> io:: Result < Box < dyn VfsNode + Send + Sync > > {
489+ Ok ( match thin_tree {
490+ ThinTree :: File { content, metadata } => {
491+ let mut st_mode = AccessPermission :: S_IRUSR ;
492+ if metadata. is_exec {
493+ st_mode |= AccessPermission :: S_IXUSR ;
494+ }
495+ Box :: new ( RomFile :: new_with_timestamp ( content, st_mode, t) )
496+ as Box < dyn VfsNode + Send + Sync >
497+ }
498+ ThinTree :: Directory ( d) => Box :: new ( MemDirectory {
499+ inner : Arc :: new ( RwLock :: new (
500+ d. into_iter ( )
501+ . map ( |( key, value) | {
502+ Ok ( (
503+ core:: str:: from_utf8 ( key)
504+ . expect ( "unable to interpret Hermit image entry filename as string" )
505+ . to_owned ( ) ,
506+ thin_tree_to_vfs_node ( value, t) ?,
507+ ) )
508+ } )
509+ . collect :: < io:: Result < _ > > ( ) ?,
510+ ) ) ,
511+ attr : FileAttr {
512+ st_mode : AccessPermission :: S_IRUSR | AccessPermission :: S_IFDIR ,
513+ st_atim : t,
514+ st_mtim : t,
515+ st_ctim : t,
516+ ..Default :: default ( )
517+ } ,
518+ } ) as Box < dyn VfsNode + Send + Sync > ,
519+ } )
520+ }
521+
480522impl MemDirectory {
481523 pub fn new ( mode : AccessPermission ) -> Self {
482524 let microseconds = arch:: kernel:: systemtime:: now_micros ( ) ;
@@ -494,6 +536,52 @@ impl MemDirectory {
494536 }
495537 }
496538
539+ /// Create a read-only memory tree from a tar image
540+ ///
541+ /// This ignores top-level files in the image
542+ pub fn try_from_image ( image : & ' static hermit_entry:: tar_parser:: Bytes ) -> io:: Result < Self > {
543+ let microseconds = arch:: kernel:: systemtime:: now_micros ( ) ;
544+ let t = timespec:: from_usec ( microseconds as i64 ) ;
545+
546+ // This is not perfectly memory-efficient, but we expect this
547+ // to be invoked usually once per kernel boot, so this cost should
548+ // be acceptable, given that it reduces code duplication and
549+ // makes implementation way easier.
550+ let thin_tree = ThinTree :: try_from_image ( image) . map_err ( |e| {
551+ error ! ( "unable to parse tar image: {e:?}" ) ;
552+ Errno :: Inval
553+ } ) ?;
554+
555+ let tree = match thin_tree {
556+ ThinTree :: Directory ( d) => d
557+ . into_iter ( )
558+ . map ( |( key, value) | {
559+ Ok ( (
560+ core:: str:: from_utf8 ( key)
561+ . expect ( "unable to interpret Hermit image entry filename as string" )
562+ . to_owned ( ) ,
563+ thin_tree_to_vfs_node ( value, t) ?,
564+ ) )
565+ } )
566+ . collect :: < io:: Result < _ > > ( ) ?,
567+ ThinTree :: File { .. } => {
568+ error ! ( "root of image isn't a directory" ) ;
569+ return Err ( Errno :: Inval ) ;
570+ }
571+ } ;
572+
573+ Ok ( Self {
574+ inner : Arc :: new ( RwLock :: new ( tree) ) ,
575+ attr : FileAttr {
576+ st_mode : AccessPermission :: S_IRUSR | AccessPermission :: S_IFDIR ,
577+ st_atim : t,
578+ st_mtim : t,
579+ st_ctim : t,
580+ ..Default :: default ( )
581+ } ,
582+ } )
583+ }
584+
497585 async fn async_traverse_open (
498586 & self ,
499587 components : & mut Vec < & str > ,
0 commit comments