@@ -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 unchanged.
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,31 @@ 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 {
447+ None => fd. borrow_mut ( ) . read ( communicate, & mut bytes, this) ,
448+ Some ( offset) => {
449+ let Ok ( offset) = u64:: try_from ( offset) else {
450+ let einval = this. eval_libc ( "EINVAL" ) ;
451+ this. set_last_error ( einval) ?;
452+ return Ok ( -1 ) ;
453+ } ;
454+ fd. borrow_mut ( ) . pread ( communicate, & mut bytes, offset, this)
455+ }
456+ } ;
457+ drop ( fd) ;
458+
459+ // `File::read` never returns a value larger than `count`, so this cannot fail.
460+ match result?. map ( |c| i64:: try_from ( c) . unwrap ( ) ) {
420461 Ok ( read_bytes) => {
421462 // If reading to `bytes` did not fail, we write those bytes to the buffer.
422463 // Crucially, if fewer than `bytes.len()` bytes were read, only write
@@ -434,7 +475,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
434475 }
435476 }
436477
437- fn write ( & mut self , fd : i32 , buf : Pointer , count : u64 ) -> InterpResult < ' tcx , i64 > {
478+ fn write (
479+ & mut self ,
480+ fd : i32 ,
481+ buf : Pointer ,
482+ count : u64 ,
483+ offset : Option < i128 > ,
484+ ) -> InterpResult < ' tcx , i64 > {
438485 let this = self . eval_context_mut ( ) ;
439486
440487 // Isolation check is done via `FileDescriptor` trait.
@@ -451,16 +498,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
451498
452499 let bytes = this. read_bytes_ptr_strip_provenance ( buf, Size :: from_bytes ( count) ) ?. to_owned ( ) ;
453500 // 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 {
501+ let Some ( fd ) = this. machine . fds . dup ( fd) else {
455502 return this. fd_not_found ( ) ;
456503 } ;
457504
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) ;
505+ let result = match offset {
506+ None => fd. borrow_mut ( ) . write ( communicate, & bytes, this) ,
507+ Some ( offset) => {
508+ let Ok ( offset) = u64:: try_from ( offset) else {
509+ let einval = this. eval_libc ( "EINVAL" ) ;
510+ this. set_last_error ( einval) ?;
511+ return Ok ( -1 ) ;
512+ } ;
513+ fd. borrow_mut ( ) . pwrite ( communicate, & bytes, offset, this)
514+ }
515+ } ;
516+ drop ( fd) ;
463517
518+ let result = result?. map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
464519 this. try_unwrap_io_result ( result)
465520 }
466521}
0 commit comments