11use std:: {
22 ffi:: { CStr , CString , OsStr } ,
3+ fs:: File ,
34 io:: { self , Error , ErrorKind } ,
4- os:: { fd:: IntoRawFd , unix:: ffi:: OsStrExt } ,
5+ os:: { fd:: IntoRawFd , unix:: ffi:: OsStrExt , unix:: io:: FromRawFd } ,
6+ sync:: Arc ,
57} ;
68
9+ use clean_path:: clean;
710use uhyve_interface:: { GuestPhysAddr , Hypercall , HypercallAddress , MAX_ARGC_ENVC , parameters:: * } ;
811
912use crate :: {
10- isolation:: filemap:: UhyveFileMap ,
13+ isolation:: filemap:: { HermitImageThinFile , MappedFileMutRef , UhyveFileMap } ,
1114 mem:: { MemoryError , MmapMemory } ,
1215 params:: EnvVars ,
1316 virt_to_phys,
@@ -88,8 +91,44 @@ pub fn unlink(mem: &MmapMemory, sysunlink: &mut UnlinkParams, file_map: &mut Uhy
8891 sysunlink. ret = if let Some ( host_path) = file_map. get_host_path ( guest_path) {
8992 // We can safely unwrap here, as host_path.as_bytes will never contain internal \0 bytes
9093 // As host_path_c_string is a valid CString, this implementation is presumed to be safe.
91- let host_path_c_string = CString :: new ( host_path. as_bytes ( ) ) . unwrap ( ) ;
92- unsafe { libc:: unlink ( host_path_c_string. as_c_str ( ) . as_ptr ( ) ) }
94+ match host_path {
95+ MappedFileMutRef :: OnHost ( oh) => {
96+ let host_path_c_string = CString :: new ( oh. as_os_str ( ) . as_bytes ( ) ) . unwrap ( ) ;
97+ unsafe { libc:: unlink ( host_path_c_string. as_c_str ( ) . as_ptr ( ) ) }
98+ }
99+ MappedFileMutRef :: InImage { .. } => {
100+ // we need to find the parent entry
101+ // TODO: reject if the parent entry points to a different entry / image
102+ let mut guest_pathbuf = clean ( OsStr :: from_bytes ( guest_path. to_bytes ( ) ) ) ;
103+ ( || {
104+ let Some ( file_name) = guest_pathbuf. file_name ( ) else {
105+ return -ENOENT ;
106+ } ;
107+ let Ok ( file_name) = str:: from_utf8 ( file_name. as_bytes ( ) ) else {
108+ return -ENOENT ;
109+ } ;
110+ let file_name = file_name. to_string ( ) ;
111+ if !guest_pathbuf. pop ( ) {
112+ return -ENOENT ;
113+ }
114+ let guest_path_c_string =
115+ CString :: new ( guest_pathbuf. as_os_str ( ) . as_bytes ( ) ) . unwrap ( ) ;
116+ if let Some ( MappedFileMutRef :: InImage { image : _, thin } ) =
117+ file_map. get_host_path ( & guest_path_c_string)
118+ {
119+ // TODO: reject unlinking of directories
120+ thin. unlink ( & [ & * file_name] ) ;
121+ 0
122+ } else {
123+ // TODO: unmount entire image if we unlink the root?
124+ error ! (
125+ "The kernel requested to unlink() an unknown path ({guest_path:?}): Rejecting..."
126+ ) ;
127+ -ENOENT
128+ }
129+ } ) ( )
130+ }
131+ }
93132 } else {
94133 error ! ( "The kernel requested to unlink() an unknown path ({guest_path:?}): Rejecting..." ) ;
95134 -ENOENT
@@ -109,16 +148,63 @@ pub fn open(mem: &MmapMemory, sysopen: &mut OpenParams, file_map: &mut UhyveFile
109148 return ;
110149 }
111150
112- if let Some ( host_path ) = file_map. get_host_path ( guest_path) {
151+ if let Some ( guest_entry ) = file_map. get_host_path ( guest_path) {
113152 debug ! ( "{guest_path:#?} found in file map." ) ;
114- // We can safely unwrap here, as host_path.as_bytes will never contain internal \0 bytes
115- // As host_path_c_string is a valid CString, this implementation is presumed to be safe.
116- let host_path_c_string = CString :: new ( host_path. as_bytes ( ) ) . unwrap ( ) ;
153+ match guest_entry {
154+ MappedFileMutRef :: OnHost ( host_path) => {
155+ // We can safely unwrap here, as host_path.as_bytes will never contain internal \0 bytes
156+ // As host_path_c_string is a valid CString, this implementation is presumed to be safe.
157+ let host_path_c_string = CString :: new ( host_path. as_os_str ( ) . as_bytes ( ) ) . unwrap ( ) ;
158+
159+ sysopen. ret = unsafe {
160+ libc:: open ( host_path_c_string. as_c_str ( ) . as_ptr ( ) , flags, sysopen. mode )
161+ } ;
117162
118- sysopen. ret =
119- unsafe { libc:: open ( host_path_c_string. as_c_str ( ) . as_ptr ( ) , flags, sysopen. mode ) } ;
163+ file_map. fdmap . insert_fd ( sysopen. ret ) ;
164+ }
165+ MappedFileMutRef :: InImage {
166+ image,
167+ thin : HermitImageThinFile :: File ( r) ,
168+ } => {
169+ // For simplicity, create a temporary files with these contents.
170+ debug ! ( "Attempting to open a temp file for unpacked {guest_path:#?}..." ) ;
171+ // Existing files that already exist should be in the file map, not here.
172+ // If a supposed attacker can predict where we open a file and its filename,
173+ // this contigency, together with O_CREAT, will cause the write to fail.
174+ flags |= O_EXCL ;
175+ let image = Arc :: clone ( image) ;
176+ let r = r. clone ( ) ;
120177
121- file_map. fdmap . insert_fd ( sysopen. ret ) ;
178+ let host_path_c_string = file_map. create_temporary_file ( guest_path) ;
179+ let new_host_path = host_path_c_string. as_c_str ( ) . as_ptr ( ) ;
180+ sysopen. ret = unsafe { libc:: open ( new_host_path, flags, sysopen. mode ) } ;
181+ if sysopen. ret >= 0 {
182+ use std:: io:: Write ;
183+ let raw_fd = sysopen. ret . into_raw_fd ( ) ;
184+ let mut fh = unsafe { File :: from_raw_fd ( raw_fd) } ;
185+ // SAFETY (slice access): we assume that the image parsing was correct
186+ // and that the temporary file wasn't modified after creation.
187+ match fh. write_all ( & image[ r. clone ( ) ] ) {
188+ Ok ( _) => {
189+ // don't destroy the file descriptor
190+ core:: mem:: forget ( fh) ;
191+ file_map. fdmap . insert_fd ( sysopen. ret . into_raw_fd ( ) ) ;
192+ }
193+
194+ Err ( _) => {
195+ // TODO: what's the correct error code for this?
196+ sysopen. ret = -ENOENT ;
197+
198+ unsafe { libc:: unlink ( new_host_path) } ;
199+ }
200+ }
201+ }
202+ }
203+ _ => {
204+ debug ! ( "Returning -ENOENT for {guest_path:#?}" ) ;
205+ sysopen. ret = -ENOENT ;
206+ }
207+ } ;
122208 } else {
123209 debug ! ( "{guest_path:#?} not found in file map." ) ;
124210 if ( flags & O_CREAT ) == O_CREAT {
0 commit comments