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