@@ -36,6 +36,30 @@ pub trait FileDescription: std::fmt::Debug + Any {
3636 throw_unsup_format ! ( "cannot write to {}" , self . name( ) ) ;
3737 }
3838
39+ /// Reads as much as possible into the given buffer from a given offset,
40+ /// and returns the number of bytes read.
41+ fn pread < ' tcx > (
42+ & mut self ,
43+ _communicate_allowed : bool ,
44+ _bytes : & mut [ u8 ] ,
45+ _offset : u64 ,
46+ _ecx : & mut MiriInterpCx < ' tcx > ,
47+ ) -> InterpResult < ' tcx , io:: Result < usize > > {
48+ throw_unsup_format ! ( "cannot pread from {}" , self . name( ) ) ;
49+ }
50+
51+ /// Writes as much as possible from the given buffer starting at a given offset,
52+ /// and returns the number of bytes written.
53+ fn pwrite < ' tcx > (
54+ & mut self ,
55+ _communicate_allowed : bool ,
56+ _bytes : & [ u8 ] ,
57+ _offset : u64 ,
58+ _ecx : & mut MiriInterpCx < ' tcx > ,
59+ ) -> InterpResult < ' tcx , io:: Result < usize > > {
60+ throw_unsup_format ! ( "cannot pwrite to {}" , self . name( ) ) ;
61+ }
62+
3963 /// Seeks to the given offset (which can be relative to the beginning, end, or current position).
4064 /// Returns the new position from the start of the stream.
4165 fn seek < ' tcx > (
@@ -380,7 +404,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
380404 Ok ( ( -1 ) . into ( ) )
381405 }
382406
383- fn read ( & mut self , fd : i32 , buf : Pointer , count : u64 ) -> InterpResult < ' tcx , i64 > {
407+ /// Read data from `fd` into buffer specified by `buf` and `count`.
408+ ///
409+ /// If `offset` is `None`, reads data from current cursor position associated with `fd`
410+ /// and updates cursor position on completion. Otherwise, reads from the specified offset
411+ /// and keeps the cursor intact.
412+ fn read (
413+ & mut self ,
414+ fd : i32 ,
415+ buf : Pointer ,
416+ count : u64 ,
417+ offset : Option < i128 > ,
418+ ) -> InterpResult < ' tcx , i64 > {
384419 let this = self . eval_context_mut ( ) ;
385420
386421 // Isolation check is done via `FileDescriptor` trait.
@@ -398,25 +433,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
398433 let communicate = this. machine . communicate ( ) ;
399434
400435 // We temporarily dup the FD to be able to retain mutable access to `this`.
401- let Some ( file_descriptor ) = this. machine . fds . dup ( fd) else {
436+ let Some ( fd ) = this. machine . fds . dup ( fd) else {
402437 trace ! ( "read: FD not found" ) ;
403438 return this. fd_not_found ( ) ;
404439 } ;
405440
406- trace ! ( "read: FD mapped to {:?}" , file_descriptor ) ;
441+ trace ! ( "read: FD mapped to {fd :?}" ) ;
407442 // We want to read at most `count` bytes. We are sure that `count` is not negative
408443 // because it was a target's `usize`. Also we are sure that its smaller than
409444 // `usize::MAX` because it is bounded by the host's `isize`.
410445 let mut bytes = vec ! [ 0 ; usize :: try_from( count) . unwrap( ) ] ;
411- // `File::read` never returns a value larger than `count`,
412- // so this cannot fail.
413- let result = file_descriptor
414- . borrow_mut ( )
415- . read ( communicate, & mut bytes, this) ?
416- . map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
417- drop ( file_descriptor) ;
418-
419- match result {
446+ let result = match offset. map ( u64:: try_from) {
447+ None => fd. borrow_mut ( ) . read ( communicate, & mut bytes, this) ,
448+ Some ( Ok ( offset) ) => fd. borrow_mut ( ) . pread ( communicate, & mut bytes, offset, this) ,
449+ Some ( Err ( _) ) => {
450+ let einval = this. eval_libc ( "EINVAL" ) ;
451+ this. set_last_error ( einval) ?;
452+ return Ok ( -1 ) ;
453+ }
454+ } ;
455+ drop ( fd) ;
456+
457+ // `File::read` never returns a value larger than `count`, so this cannot fail.
458+ match result?. map ( |c| i64:: try_from ( c) . unwrap ( ) ) {
420459 Ok ( read_bytes) => {
421460 // If reading to `bytes` did not fail, we write those bytes to the buffer.
422461 // Crucially, if fewer than `bytes.len()` bytes were read, only write
@@ -434,7 +473,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
434473 }
435474 }
436475
437- fn write ( & mut self , fd : i32 , buf : Pointer , count : u64 ) -> InterpResult < ' tcx , i64 > {
476+ fn write (
477+ & mut self ,
478+ fd : i32 ,
479+ buf : Pointer ,
480+ count : u64 ,
481+ offset : Option < i128 > ,
482+ ) -> InterpResult < ' tcx , i64 > {
438483 let this = self . eval_context_mut ( ) ;
439484
440485 // Isolation check is done via `FileDescriptor` trait.
@@ -451,16 +496,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
451496
452497 let bytes = this. read_bytes_ptr_strip_provenance ( buf, Size :: from_bytes ( count) ) ?. to_owned ( ) ;
453498 // We temporarily dup the FD to be able to retain mutable access to `this`.
454- let Some ( file_descriptor ) = this. machine . fds . dup ( fd) else {
499+ let Some ( fd ) = this. machine . fds . dup ( fd) else {
455500 return this. fd_not_found ( ) ;
456501 } ;
457502
458- let result = file_descriptor
459- . borrow_mut ( )
460- . write ( communicate, & bytes, this) ?
461- . map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
462- drop ( file_descriptor) ;
503+ let result = match offset. map ( u64:: try_from) {
504+ None => fd. borrow_mut ( ) . write ( communicate, & bytes, this) ,
505+ Some ( Ok ( offset) ) => fd. borrow_mut ( ) . pwrite ( communicate, & bytes, offset, this) ,
506+ Some ( Err ( _) ) => {
507+ let einval = this. eval_libc ( "EINVAL" ) ;
508+ this. set_last_error ( einval) ?;
509+ return Ok ( -1 ) ;
510+ }
511+ } ;
512+ drop ( fd) ;
463513
514+ let result = result?. map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
464515 this. try_unwrap_io_result ( result)
465516 }
466517}
0 commit comments