@@ -14,7 +14,10 @@ use rustc_data_structures::fx::FxHashMap;
14
14
15
15
use self :: shims:: time:: system_time_to_duration;
16
16
use crate :: helpers:: check_min_arg_count;
17
- use crate :: shims:: files:: { EvalContextExt as _, FileDescription , FileDescriptionRef } ;
17
+ use crate :: shims:: files:: {
18
+ DynFileIOCallback , EvalContextExt as _, FileDescription , FileDescriptionRef ,
19
+ WeakFileDescriptionRef ,
20
+ } ;
18
21
use crate :: shims:: os_str:: bytes_to_os_str;
19
22
use crate :: shims:: unix:: fd:: { FlockOp , UnixFileDescription } ;
20
23
use crate :: * ;
@@ -23,27 +26,141 @@ use crate::*;
23
26
struct FileHandle {
24
27
file : File ,
25
28
writable : bool ,
29
+ /// Mutex for synchronizing file access across threads.
30
+ file_lock : MutexRef ,
31
+ }
32
+
33
+ impl VisitProvenance for FileHandle {
34
+ fn visit_provenance ( & self , _visit : & mut VisitWith < ' _ > ) {
35
+ // No provenance tracking needed for FileHandle as it contains no references.
36
+ // This implementation satisfies the trait requirement but performs no operations.
37
+ }
38
+ }
39
+
40
+ impl FileHandle {
41
+ /// Creates a new FileHandle with specified permissions and synchronization primitive.
42
+ fn new ( file : File , writable : bool , file_lock : MutexRef ) -> Self {
43
+ Self { file, writable, file_lock }
44
+ }
45
+
46
+ /// Attempts to create a clone of the file handle while preserving all attributes.
47
+ ///
48
+ /// # Errors
49
+ /// Returns an `InterpResult` error if file handle cloning fails.
50
+ fn try_clone < ' tcx > ( & self ) -> InterpResult < ' tcx , FileHandle > {
51
+ let cloned_file = self
52
+ . file
53
+ . try_clone ( )
54
+ . map_err ( |e| err_unsup_format ! ( "Failed to clone file handle: {}" , e) ) ?;
55
+
56
+ interp_ok ( FileHandle {
57
+ file : cloned_file,
58
+ writable : self . writable ,
59
+ file_lock : self . file_lock . clone ( ) ,
60
+ } )
61
+ }
62
+
63
+ /// Performs a synchronized file read operation with callback completion.
64
+ /// Acquires a mutex lock, validates the file descriptor, performs the read,
65
+ /// and invokes the callback with the result.
66
+ fn perform_read < ' tcx > (
67
+ this : & mut MiriInterpCx < ' tcx > ,
68
+ completion_callback : DynFileIOCallback < ' tcx > ,
69
+ mut file_handle : FileHandle ,
70
+ weak_fd : WeakFileDescriptionRef < FileHandle > ,
71
+ buffer_ptr : Pointer ,
72
+ length : usize ,
73
+ ) -> InterpResult < ' tcx > {
74
+ this. mutex_lock ( & file_handle. file_lock ) ;
75
+
76
+ let result = {
77
+ // Verify file descriptor is still valid
78
+ if weak_fd. upgrade ( ) . is_none ( ) {
79
+ throw_unsup_format ! ( "file got closed while blocking" )
80
+ }
81
+
82
+ let mut bytes = vec ! [ 0 ; length] ;
83
+ let read_result = file_handle. file . read ( & mut bytes) ;
84
+
85
+ // Handle the read result
86
+ match read_result {
87
+ Ok ( read_size) => {
88
+ // Write the bytes to memory
89
+ if let Err ( err_code) = this
90
+ . write_bytes_ptr ( buffer_ptr, bytes[ ..read_size] . iter ( ) . copied ( ) )
91
+ . report_err ( )
92
+ {
93
+ throw_unsup_format ! (
94
+ "Memory write failed during file read operation: {:#?}" ,
95
+ err_code
96
+ )
97
+ }
98
+ completion_callback. call ( this, Ok ( read_size) )
99
+ }
100
+ Err ( err_code) => completion_callback. call ( this, Err ( err_code) ) ,
101
+ }
102
+ } ;
103
+
104
+ // Always unlock the mutex, even if the read operation failed
105
+ this. mutex_unlock ( & file_handle. file_lock ) ?;
106
+
107
+ result
108
+ }
26
109
}
27
110
28
111
impl FileDescription for FileHandle {
29
112
fn name ( & self ) -> & ' static str {
30
113
"file"
31
114
}
32
115
33
- fn read < ' tcx > (
116
+ fn read_with_callback < ' tcx > (
34
117
self : FileDescriptionRef < Self > ,
35
118
communicate_allowed : bool ,
36
119
ptr : Pointer ,
37
120
len : usize ,
38
- dest : & MPlaceTy < ' tcx > ,
121
+ completion_callback : DynFileIOCallback < ' tcx > ,
39
122
ecx : & mut MiriInterpCx < ' tcx > ,
40
123
) -> InterpResult < ' tcx > {
124
+ let this = ecx;
41
125
assert ! ( communicate_allowed, "isolation should have prevented even opening a file" ) ;
42
- let mut bytes = vec ! [ 0 ; len] ;
43
- let result = ( & mut & self . file ) . read ( & mut bytes) ;
44
- match result {
45
- Ok ( read_size) => ecx. return_read_success ( ptr, & bytes, read_size, dest) ,
46
- Err ( e) => ecx. set_last_error_and_return ( e, dest) ,
126
+
127
+ // Clone the underlying File
128
+ let clone_file_handle = match self . try_clone ( ) . report_err ( ) {
129
+ Ok ( handle) => handle,
130
+ Err ( ec) => throw_unsup_format ! ( "unable to clone file discp {:#?}" , ec) ,
131
+ } ;
132
+
133
+ let weak_fd = FileDescriptionRef :: downgrade ( & self ) ;
134
+
135
+ if this. mutex_is_locked ( & self . file_lock ) {
136
+ this. block_thread (
137
+ BlockReason :: Mutex ,
138
+ None ,
139
+ callback ! (
140
+ @capture<' tcx> {
141
+ completion_callback: DynFileIOCallback <' tcx>,
142
+ clone_file_handle: FileHandle ,
143
+ weak_fd: WeakFileDescriptionRef <FileHandle >,
144
+ ptr: Pointer ,
145
+ len: usize ,
146
+ }
147
+ |this, unblock: UnblockKind | {
148
+ assert_eq!( unblock, UnblockKind :: Ready ) ;
149
+ FileHandle :: perform_read( this, completion_callback, clone_file_handle, weak_fd, ptr, len)
150
+ }
151
+ ) ,
152
+ ) ;
153
+
154
+ unreachable ! ( )
155
+ } else {
156
+ FileHandle :: perform_read (
157
+ this,
158
+ completion_callback,
159
+ clone_file_handle,
160
+ weak_fd,
161
+ ptr,
162
+ len,
163
+ )
47
164
}
48
165
}
49
166
@@ -584,9 +701,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
584
701
return this. set_last_error_and_return_i32 ( ErrorKind :: PermissionDenied ) ;
585
702
}
586
703
587
- let fd = options
588
- . open ( path)
589
- . map ( |file| this. machine . fds . insert_new ( FileHandle { file, writable } ) ) ;
704
+ let fd = options. open ( path) . map ( |file| {
705
+ this. machine . fds . insert_new ( FileHandle :: new (
706
+ file,
707
+ writable,
708
+ this. machine . sync . mutex_create ( ) ,
709
+ ) )
710
+ } ) ;
590
711
591
712
interp_ok ( Scalar :: from_i32 ( this. try_unwrap_io_result ( fd) ?) )
592
713
}
@@ -1645,7 +1766,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
1645
1766
1646
1767
match file {
1647
1768
Ok ( f) => {
1648
- let fd = this. machine . fds . insert_new ( FileHandle { file : f, writable : true } ) ;
1769
+ let fd = this. machine . fds . insert_new ( FileHandle :: new (
1770
+ f,
1771
+ true ,
1772
+ this. machine . sync . mutex_create ( ) ,
1773
+ ) ) ;
1649
1774
return interp_ok ( Scalar :: from_i32 ( fd) ) ;
1650
1775
}
1651
1776
Err ( e) =>
0 commit comments