@@ -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 unchanged.
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,31 @@ 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 {
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 ( ) ) {
420
461
Ok ( read_bytes) => {
421
462
// If reading to `bytes` did not fail, we write those bytes to the buffer.
422
463
// Crucially, if fewer than `bytes.len()` bytes were read, only write
@@ -434,7 +475,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
434
475
}
435
476
}
436
477
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 > {
438
485
let this = self . eval_context_mut ( ) ;
439
486
440
487
// Isolation check is done via `FileDescriptor` trait.
@@ -451,16 +498,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
451
498
452
499
let bytes = this. read_bytes_ptr_strip_provenance ( buf, Size :: from_bytes ( count) ) ?. to_owned ( ) ;
453
500
// 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 {
455
502
return this. fd_not_found ( ) ;
456
503
} ;
457
504
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) ;
463
517
518
+ let result = result?. map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
464
519
this. try_unwrap_io_result ( result)
465
520
}
466
521
}
0 commit comments