11use std:: any:: Any ;
22use std:: collections:: BTreeMap ;
3- use std:: io:: { IsTerminal , SeekFrom , Write } ;
3+ use std:: fs:: { File , Metadata } ;
4+ use std:: io:: { IsTerminal , Seek , SeekFrom , Write } ;
45use std:: marker:: CoercePointee ;
56use std:: ops:: Deref ;
67use std:: rc:: { Rc , Weak } ;
@@ -192,7 +193,7 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt {
192193 false
193194 }
194195
195- fn as_unix ( & self ) -> & dyn UnixFileDescription {
196+ fn as_unix < ' tcx > ( & self , _ecx : & MiriInterpCx < ' tcx > ) -> & dyn UnixFileDescription {
196197 panic ! ( "Not a unix file descriptor: {}" , self . name( ) ) ;
197198 }
198199}
@@ -278,6 +279,97 @@ impl FileDescription for io::Stderr {
278279 }
279280}
280281
282+ #[ derive( Debug ) ]
283+ pub struct FileHandle {
284+ pub ( crate ) file : File ,
285+ pub ( crate ) writable : bool ,
286+ }
287+
288+ impl FileDescription for FileHandle {
289+ fn name ( & self ) -> & ' static str {
290+ "file"
291+ }
292+
293+ fn read < ' tcx > (
294+ self : FileDescriptionRef < Self > ,
295+ communicate_allowed : bool ,
296+ ptr : Pointer ,
297+ len : usize ,
298+ ecx : & mut MiriInterpCx < ' tcx > ,
299+ finish : DynMachineCallback < ' tcx , Result < usize , IoError > > ,
300+ ) -> InterpResult < ' tcx > {
301+ assert ! ( communicate_allowed, "isolation should have prevented even opening a file" ) ;
302+
303+ let result = ecx. read_from_host ( & self . file , len, ptr) ?;
304+ finish. call ( ecx, result)
305+ }
306+
307+ fn write < ' tcx > (
308+ self : FileDescriptionRef < Self > ,
309+ communicate_allowed : bool ,
310+ ptr : Pointer ,
311+ len : usize ,
312+ ecx : & mut MiriInterpCx < ' tcx > ,
313+ finish : DynMachineCallback < ' tcx , Result < usize , IoError > > ,
314+ ) -> InterpResult < ' tcx > {
315+ assert ! ( communicate_allowed, "isolation should have prevented even opening a file" ) ;
316+
317+ let result = ecx. write_to_host ( & self . file , len, ptr) ?;
318+ finish. call ( ecx, result)
319+ }
320+
321+ fn seek < ' tcx > (
322+ & self ,
323+ communicate_allowed : bool ,
324+ offset : SeekFrom ,
325+ ) -> InterpResult < ' tcx , io:: Result < u64 > > {
326+ assert ! ( communicate_allowed, "isolation should have prevented even opening a file" ) ;
327+ interp_ok ( ( & mut & self . file ) . seek ( offset) )
328+ }
329+
330+ fn close < ' tcx > (
331+ self ,
332+ communicate_allowed : bool ,
333+ _ecx : & mut MiriInterpCx < ' tcx > ,
334+ ) -> InterpResult < ' tcx , io:: Result < ( ) > > {
335+ assert ! ( communicate_allowed, "isolation should have prevented even opening a file" ) ;
336+ // We sync the file if it was opened in a mode different than read-only.
337+ if self . writable {
338+ // `File::sync_all` does the checks that are done when closing a file. We do this to
339+ // to handle possible errors correctly.
340+ let result = self . file . sync_all ( ) ;
341+ // Now we actually close the file and return the result.
342+ drop ( self . file ) ;
343+ interp_ok ( result)
344+ } else {
345+ // We drop the file, this closes it but ignores any errors
346+ // produced when closing it. This is done because
347+ // `File::sync_all` cannot be done over files like
348+ // `/dev/urandom` which are read-only. Check
349+ // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439
350+ // for a deeper discussion.
351+ drop ( self . file ) ;
352+ interp_ok ( Ok ( ( ) ) )
353+ }
354+ }
355+
356+ fn metadata < ' tcx > ( & self ) -> InterpResult < ' tcx , io:: Result < Metadata > > {
357+ interp_ok ( self . file . metadata ( ) )
358+ }
359+
360+ fn is_tty ( & self , communicate_allowed : bool ) -> bool {
361+ communicate_allowed && self . file . is_terminal ( )
362+ }
363+
364+ fn as_unix < ' tcx > ( & self , ecx : & MiriInterpCx < ' tcx > ) -> & dyn UnixFileDescription {
365+ assert ! (
366+ ecx. target_os_is_unix( ) ,
367+ "unix file operations are only available for unix targets"
368+ ) ;
369+ self
370+ }
371+ }
372+
281373/// Like /dev/null
282374#[ derive( Debug ) ]
283375pub struct NullOutput ;
@@ -300,10 +392,13 @@ impl FileDescription for NullOutput {
300392 }
301393}
302394
395+ /// Internal type of a file-descriptor - this is what [`FdTable`] expects
396+ pub type FdNum = i32 ;
397+
303398/// The file descriptor table
304399#[ derive( Debug ) ]
305400pub struct FdTable {
306- pub fds : BTreeMap < i32 , DynFileDescriptionRef > ,
401+ pub fds : BTreeMap < FdNum , DynFileDescriptionRef > ,
307402 /// Unique identifier for file description, used to differentiate between various file description.
308403 next_file_description_id : FdId ,
309404}
@@ -339,21 +434,21 @@ impl FdTable {
339434 }
340435
341436 /// Insert a new file description to the FdTable.
342- pub fn insert_new ( & mut self , fd : impl FileDescription ) -> i32 {
437+ pub fn insert_new ( & mut self , fd : impl FileDescription ) -> FdNum {
343438 let fd_ref = self . new_ref ( fd) ;
344439 self . insert ( fd_ref)
345440 }
346441
347- pub fn insert ( & mut self , fd_ref : DynFileDescriptionRef ) -> i32 {
442+ pub fn insert ( & mut self , fd_ref : DynFileDescriptionRef ) -> FdNum {
348443 self . insert_with_min_num ( fd_ref, 0 )
349444 }
350445
351446 /// Insert a file description, giving it a file descriptor that is at least `min_fd_num`.
352447 pub fn insert_with_min_num (
353448 & mut self ,
354449 file_handle : DynFileDescriptionRef ,
355- min_fd_num : i32 ,
356- ) -> i32 {
450+ min_fd_num : FdNum ,
451+ ) -> FdNum {
357452 // Find the lowest unused FD, starting from min_fd. If the first such unused FD is in
358453 // between used FDs, the find_map combinator will return it. If the first such unused FD
359454 // is after all other used FDs, the find_map combinator will return None, and we will use
@@ -379,16 +474,16 @@ impl FdTable {
379474 new_fd_num
380475 }
381476
382- pub fn get ( & self , fd_num : i32 ) -> Option < DynFileDescriptionRef > {
477+ pub fn get ( & self , fd_num : FdNum ) -> Option < DynFileDescriptionRef > {
383478 let fd = self . fds . get ( & fd_num) ?;
384479 Some ( fd. clone ( ) )
385480 }
386481
387- pub fn remove ( & mut self , fd_num : i32 ) -> Option < DynFileDescriptionRef > {
482+ pub fn remove ( & mut self , fd_num : FdNum ) -> Option < DynFileDescriptionRef > {
388483 self . fds . remove ( & fd_num)
389484 }
390485
391- pub fn is_fd_num ( & self , fd_num : i32 ) -> bool {
486+ pub fn is_fd_num ( & self , fd_num : FdNum ) -> bool {
392487 self . fds . contains_key ( & fd_num)
393488 }
394489}
0 commit comments