1+ use core:: cmp;
12use std:: {
23 ffi:: { CStr , CString , OsStr } ,
34 io:: { self , Error , ErrorKind } ,
@@ -7,7 +8,10 @@ use std::{
78use uhyve_interface:: { GuestPhysAddr , Hypercall , HypercallAddress , MAX_ARGC_ENVC , parameters:: * } ;
89
910use crate :: {
10- isolation:: filemap:: UhyveFileMap ,
11+ isolation:: {
12+ fd:: { FdData , GuestFd } ,
13+ filemap:: UhyveFileMap ,
14+ } ,
1115 mem:: { MemoryError , MmapMemory } ,
1216 params:: EnvVars ,
1317 virt_to_phys,
@@ -109,16 +113,21 @@ pub fn open(mem: &MmapMemory, sysopen: &mut OpenParams, file_map: &mut UhyveFile
109113 return ;
110114 }
111115
112- if let Some ( host_path) = file_map. get_host_path ( guest_path) {
116+ sysopen . ret = if let Some ( host_path) = file_map. get_host_path ( guest_path) {
113117 debug ! ( "{guest_path:#?} found in file map." ) ;
114118 // We can safely unwrap here, as host_path.as_bytes will never contain internal \0 bytes
115119 // As host_path_c_string is a valid CString, this implementation is presumed to be safe.
116120 let host_path_c_string = CString :: new ( host_path. as_bytes ( ) ) . unwrap ( ) ;
117121
118- sysopen . ret =
122+ let host_fd =
119123 unsafe { libc:: open ( host_path_c_string. as_c_str ( ) . as_ptr ( ) , flags, sysopen. mode ) } ;
120-
121- file_map. fdmap . insert_fd ( sysopen. ret ) ;
124+ if let Some ( guest_fd) = file_map. fdmap . insert ( FdData :: Raw ( host_fd) ) {
125+ guest_fd. 0
126+ } else if host_fd < 0 {
127+ host_fd
128+ } else {
129+ -ENOENT
130+ }
122131 } else {
123132 debug ! ( "{guest_path:#?} not found in file map." ) ;
124133 if ( flags & O_CREAT ) == O_CREAT {
@@ -130,29 +139,35 @@ pub fn open(mem: &MmapMemory, sysopen: &mut OpenParams, file_map: &mut UhyveFile
130139
131140 let host_path_c_string = file_map. create_temporary_file ( guest_path) ;
132141 let new_host_path = host_path_c_string. as_c_str ( ) . as_ptr ( ) ;
133- sysopen. ret = unsafe { libc:: open ( new_host_path, flags, sysopen. mode ) } ;
134- file_map. fdmap . insert_fd ( sysopen. ret . into_raw_fd ( ) ) ;
142+ let host_fd = unsafe { libc:: open ( new_host_path, flags, sysopen. mode ) } ;
143+ if let Some ( guest_fd) = file_map. fdmap . insert ( FdData :: Raw ( host_fd) ) {
144+ guest_fd. 0
145+ } else if host_fd < 0 {
146+ host_fd
147+ } else {
148+ -ENOENT
149+ }
135150 } else {
136151 debug ! ( "Returning -ENOENT for {guest_path:#?}" ) ;
137- sysopen . ret = -ENOENT ;
152+ -ENOENT
138153 }
139154 }
140155}
141156
142157/// Handles an close syscall by closing the file on the host.
143158pub fn close ( sysclose : & mut CloseParams , file_map : & mut UhyveFileMap ) {
144- if file_map . fdmap . is_fd_present ( sysclose. fd . into_raw_fd ( ) ) {
145- if sysclose . fd > 2 {
146- unsafe { sysclose . ret = libc :: close ( sysclose. fd ) }
147- file_map . fdmap . remove_fd ( sysclose . fd )
148- } else {
149- // Ignore closes of stdin, stdout and stderr that would
150- // otherwise affect Uhyve
151- sysclose . ret = 0
159+ sysclose. ret = if file_map
160+ . fdmap
161+ . is_fd_present ( GuestFd ( sysclose. fd . into_raw_fd ( ) ) )
162+ {
163+ match file_map . fdmap . remove ( GuestFd ( sysclose . fd ) ) {
164+ Some ( FdData :: Raw ( fd ) ) => unsafe { libc :: close ( fd ) } ,
165+ // ignore other closures (fdmap's remove already handles stdio)
166+ _ => 0 ,
152167 }
153168 } else {
154- sysclose . ret = -EBADF
155- }
169+ -EBADF
170+ } ;
156171}
157172
158173/// Handles a read syscall on the host.
@@ -162,25 +177,45 @@ pub fn read(
162177 root_pt : GuestPhysAddr ,
163178 file_map : & mut UhyveFileMap ,
164179) {
165- if file_map. fdmap . is_fd_present ( sysread. fd . into_raw_fd ( ) ) {
180+ sysread . ret = if let Some ( fdata ) = file_map. fdmap . get_mut ( GuestFd ( sysread. fd . into_raw_fd ( ) ) ) {
166181 let guest_phys_addr = virt_to_phys ( sysread. buf , mem, root_pt) ;
167182 if let Ok ( guest_phys_addr) = guest_phys_addr
168183 && let Ok ( host_address) = mem. host_address ( guest_phys_addr)
169184 {
170- let bytes_read =
171- unsafe { libc:: read ( sysread. fd , host_address as * mut libc:: c_void , sysread. len ) } ;
172- if bytes_read >= 0 {
173- sysread. ret = bytes_read;
174- } else {
175- sysread. ret = -1
185+ match fdata {
186+ FdData :: Raw ( rfd) => {
187+ let bytes_read =
188+ unsafe { libc:: read ( * rfd, host_address as * mut libc:: c_void , sysread. len ) } ;
189+ if bytes_read >= 0 { bytes_read } else { -1 }
190+ }
191+ FdData :: Virtual { data, offset } => {
192+ let data: & [ u8 ] = data. get ( ) ;
193+ let remaining = {
194+ let pos = cmp:: min ( * offset, data. len ( ) as u64 ) ;
195+ & data[ pos as usize ..]
196+ } ;
197+ let amt = cmp:: min ( remaining. len ( ) as u64 , sysread. len as u64 ) as usize ;
198+ assert ! ( amt <= isize :: MAX as usize ) ;
199+
200+ // SAFETY: the input slices can't overlap, as `host_address` is owned by the guest
201+ // and `data` is owned by the host.
202+ unsafe {
203+ core:: ptr:: copy_nonoverlapping (
204+ remaining. as_ptr ( ) ,
205+ host_address as * mut u8 ,
206+ amt,
207+ )
208+ } ;
209+ amt as isize
210+ }
176211 }
177212 } else {
178213 warn ! ( "Unable to get host address for read buffer" ) ;
179- sysread . ret = -EFAULT as isize ;
214+ -EFAULT as isize
180215 }
181216 } else {
182- sysread . ret = -EBADF as isize ;
183- }
217+ -EBADF as isize
218+ } ;
184219}
185220
186221/// Handles an write syscall on the host.
@@ -191,12 +226,21 @@ pub fn write(
191226 file_map : & mut UhyveFileMap ,
192227) -> io:: Result < ( ) > {
193228 let mut bytes_written: usize = 0 ;
229+ let gfd = GuestFd ( syswrite. fd . into_raw_fd ( ) ) ;
230+ if !file_map. fdmap . is_fd_present ( gfd) {
231+ // We don't write anything if the file descriptor is not available,
232+ // but this is OK for now, as we have no means of returning an error code
233+ // and writes are not necessarily guaranteed to write anything.
234+ return Ok ( ( ) ) ;
235+ }
236+
194237 while bytes_written != syswrite. len {
195238 let guest_phys_addr = virt_to_phys (
196239 syswrite. buf + bytes_written as u64 ,
197240 & peripherals. mem ,
198241 root_pt,
199242 ) ;
243+ let guest_phys_len = syswrite. len - bytes_written;
200244
201245 if let Ok ( guest_phys_addr) = guest_phys_addr {
202246 if syswrite. fd == 1 || syswrite. fd == 2 {
@@ -212,36 +256,38 @@ pub fn write(
212256 } ) ?
213257 } ;
214258 return peripherals. serial . output ( bytes) ;
215- } else if !file_map. fdmap . is_fd_present ( syswrite. fd . into_raw_fd ( ) ) {
216- // We don't write anything if the file descriptor is not available,
217- // but this is OK for now, as we have no means of returning an error code
218- // and writes are not necessarily guaranteed to write anything.
219- return Ok ( ( ) ) ;
220259 }
221260 } else {
222261 return Ok ( ( ) ) ;
223262 }
224263
225- unsafe {
226- let step = libc:: write (
227- syswrite. fd ,
228- peripherals
229- . mem
230- . host_address ( guest_phys_addr. unwrap ( ) )
231- . map_err ( |e| match e {
232- MemoryError :: BoundsViolation => {
233- unreachable ! ( "Bounds violation after host_address function" )
234- }
235- MemoryError :: WrongMemoryError => {
236- Error :: new ( ErrorKind :: AddrNotAvailable , e. to_string ( ) )
237- }
238- } ) ? as * const libc:: c_void ,
239- syswrite. len - bytes_written,
240- ) ;
241- if step >= 0 {
242- bytes_written += step as usize ;
243- } else {
244- return Err ( io:: Error :: last_os_error ( ) ) ;
264+ let host_address = peripherals
265+ . mem
266+ . host_address ( guest_phys_addr. unwrap ( ) )
267+ . map_err ( |e| match e {
268+ MemoryError :: BoundsViolation => {
269+ unreachable ! ( "Bounds violation after host_address function" )
270+ }
271+ MemoryError :: WrongMemoryError => {
272+ Error :: new ( ErrorKind :: AddrNotAvailable , e. to_string ( ) )
273+ }
274+ } ) ?;
275+
276+ match file_map. fdmap . get_mut ( gfd) . unwrap ( ) {
277+ FdData :: Raw ( r) => unsafe {
278+ let step = libc:: write ( * r, host_address as * const libc:: c_void , guest_phys_len) ;
279+ if step >= 0 {
280+ bytes_written += step as usize ;
281+ } else {
282+ return Err ( io:: Error :: last_os_error ( ) ) ;
283+ }
284+ } ,
285+ FdData :: Virtual { .. } => {
286+ // virtual fds are read-only
287+ return Err ( io:: Error :: new (
288+ io:: ErrorKind :: ReadOnlyFilesystem ,
289+ format ! ( "Unable to write to virtual file {gfd}" ) ,
290+ ) ) ;
245291 }
246292 }
247293 }
@@ -251,16 +297,36 @@ pub fn write(
251297
252298/// Handles an lseek syscall on the host.
253299pub fn lseek ( syslseek : & mut LseekParams , file_map : & mut UhyveFileMap ) {
254- if file_map. fdmap . is_fd_present ( syslseek. fd . into_raw_fd ( ) ) {
255- unsafe {
256- syslseek. offset =
257- libc:: lseek ( syslseek. fd , syslseek. offset as i64 , syslseek. whence ) as isize ;
300+ syslseek. offset = match file_map. fdmap . get_mut ( GuestFd ( syslseek. fd . into_raw_fd ( ) ) ) {
301+ Some ( FdData :: Raw ( r) ) => unsafe {
302+ libc:: lseek ( * r, syslseek. offset as i64 , syslseek. whence ) as isize
303+ } ,
304+ Some ( FdData :: Virtual { data, offset } ) => {
305+ let tmp = match syslseek. whence {
306+ SEEK_SET => 0 ,
307+ SEEK_CUR => * offset as isize ,
308+ SEEK_END => data. get ( ) . len ( ) as isize ,
309+ _ => -1 ,
310+ } ;
311+ if tmp >= 0 {
312+ let tmp2 = ( tmp as i64 ) + ( syslseek. offset as i64 ) ;
313+ match tmp2. try_into ( ) {
314+ Ok ( tmp3) => {
315+ * offset = tmp3;
316+ tmp2 as isize
317+ }
318+ _ => -1 ,
319+ }
320+ } else {
321+ tmp
322+ }
258323 }
259- } else {
260- // TODO: Return -EBADF to the ret field, as soon as it is implemented for LseekParams
261- warn ! ( "lseek attempted to use an unknown file descriptor" ) ;
262- syslseek. offset = -1
263- }
324+ None => {
325+ // TODO: Return -EBADF to the ret field, as soon as it is implemented for LseekParams
326+ warn ! ( "lseek attempted to use an unknown file descriptor" ) ;
327+ -1
328+ }
329+ } ;
264330}
265331
266332/// Copies the arguments of the application into the VM's memory to the destinations specified in `syscmdval`.
0 commit comments