@@ -36,6 +36,30 @@ pub trait FileDescription: std::fmt::Debug + Any {
36
36
throw_unsup_format ! ( "cannot write to {}" , self . name( ) ) ;
37
37
}
38
38
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
+
39
63
/// Seeks to the given offset (which can be relative to the beginning, end, or current position).
40
64
/// Returns the new position from the start of the stream.
41
65
fn seek < ' tcx > (
@@ -380,7 +404,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
380
404
Ok ( ( -1 ) . into ( ) )
381
405
}
382
406
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 > {
384
419
let this = self . eval_context_mut ( ) ;
385
420
386
421
// Isolation check is done via `FileDescriptor` trait.
@@ -398,25 +433,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
398
433
let communicate = this. machine . communicate ( ) ;
399
434
400
435
// 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 {
402
437
trace ! ( "read: FD not found" ) ;
403
438
return this. fd_not_found ( ) ;
404
439
} ;
405
440
406
- trace ! ( "read: FD mapped to {:?}" , file_descriptor ) ;
441
+ trace ! ( "read: FD mapped to {fd :?}" ) ;
407
442
// We want to read at most `count` bytes. We are sure that `count` is not negative
408
443
// because it was a target's `usize`. Also we are sure that its smaller than
409
444
// `usize::MAX` because it is bounded by the host's `isize`.
410
445
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 ( ) ) {
420
459
Ok ( read_bytes) => {
421
460
// If reading to `bytes` did not fail, we write those bytes to the buffer.
422
461
// Crucially, if fewer than `bytes.len()` bytes were read, only write
@@ -434,7 +473,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
434
473
}
435
474
}
436
475
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 > {
438
483
let this = self . eval_context_mut ( ) ;
439
484
440
485
// Isolation check is done via `FileDescriptor` trait.
@@ -451,16 +496,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
451
496
452
497
let bytes = this. read_bytes_ptr_strip_provenance ( buf, Size :: from_bytes ( count) ) ?. to_owned ( ) ;
453
498
// 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 {
455
500
return this. fd_not_found ( ) ;
456
501
} ;
457
502
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) ;
463
513
514
+ let result = result?. map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
464
515
this. try_unwrap_io_result ( result)
465
516
}
466
517
}
0 commit comments