@@ -16,6 +16,8 @@ use crate::shims::unix::*;
16
16
use crate :: * ;
17
17
use shims:: time:: system_time_to_duration;
18
18
19
+ use self :: fd:: FlockOp ;
20
+
19
21
#[ derive( Debug ) ]
20
22
struct FileHandle {
21
23
file : File ,
@@ -127,6 +129,97 @@ impl FileDescription for FileHandle {
127
129
}
128
130
}
129
131
132
+ fn flock < ' tcx > (
133
+ & self ,
134
+ communicate_allowed : bool ,
135
+ op : FlockOp ,
136
+ ) -> InterpResult < ' tcx , io:: Result < ( ) > > {
137
+ assert ! ( communicate_allowed, "isolation should have prevented even opening a file" ) ;
138
+ #[ cfg( target_family = "unix" ) ]
139
+ {
140
+ use std:: os:: fd:: AsRawFd ;
141
+
142
+ use FlockOp :: * ;
143
+ // We always use non-blocking call to prevent interpreter from being blocked
144
+ let ( host_op, lock_nb) = match op {
145
+ SharedLock { nonblocking } => ( libc:: LOCK_SH | libc:: LOCK_NB , nonblocking) ,
146
+ ExclusiveLock { nonblocking } => ( libc:: LOCK_EX | libc:: LOCK_NB , nonblocking) ,
147
+ Unlock => ( libc:: LOCK_UN , false ) ,
148
+ } ;
149
+
150
+ let fd = self . file . as_raw_fd ( ) ;
151
+ let ret = unsafe { libc:: flock ( fd, host_op) } ;
152
+ let res = match ret {
153
+ 0 => Ok ( ( ) ) ,
154
+ -1 => {
155
+ let err = io:: Error :: last_os_error ( ) ;
156
+ if !lock_nb && err. kind ( ) == io:: ErrorKind :: WouldBlock {
157
+ throw_unsup_format ! ( "blocking `flock` is not currently supported" ) ;
158
+ }
159
+ Err ( err)
160
+ }
161
+ ret => panic ! ( "Unexpected return value from flock: {ret}" ) ,
162
+ } ;
163
+ Ok ( res)
164
+ }
165
+
166
+ #[ cfg( target_family = "windows" ) ]
167
+ {
168
+ use std:: os:: windows:: io:: AsRawHandle ;
169
+ use windows_sys:: Win32 :: {
170
+ Foundation :: { ERROR_IO_PENDING , ERROR_LOCK_VIOLATION , FALSE , HANDLE , TRUE } ,
171
+ Storage :: FileSystem :: {
172
+ LockFileEx , UnlockFile , LOCKFILE_EXCLUSIVE_LOCK , LOCKFILE_FAIL_IMMEDIATELY ,
173
+ } ,
174
+ } ;
175
+ let fh = self . file . as_raw_handle ( ) as HANDLE ;
176
+
177
+ use FlockOp :: * ;
178
+ let ( ret, lock_nb) = match op {
179
+ SharedLock { nonblocking } | ExclusiveLock { nonblocking } => {
180
+ // We always use non-blocking call to prevent interpreter from being blocked
181
+ let mut flags = LOCKFILE_FAIL_IMMEDIATELY ;
182
+ if matches ! ( op, ExclusiveLock { .. } ) {
183
+ flags |= LOCKFILE_EXCLUSIVE_LOCK ;
184
+ }
185
+ let ret = unsafe { LockFileEx ( fh, flags, 0 , !0 , !0 , & mut std:: mem:: zeroed ( ) ) } ;
186
+ ( ret, nonblocking)
187
+ }
188
+ Unlock => {
189
+ let ret = unsafe { UnlockFile ( fh, 0 , 0 , !0 , !0 ) } ;
190
+ ( ret, false )
191
+ }
192
+ } ;
193
+
194
+ let res = match ret {
195
+ TRUE => Ok ( ( ) ) ,
196
+ FALSE => {
197
+ let mut err = io:: Error :: last_os_error ( ) ;
198
+ let code: u32 = err. raw_os_error ( ) . unwrap ( ) . try_into ( ) . unwrap ( ) ;
199
+ if matches ! ( code, ERROR_IO_PENDING | ERROR_LOCK_VIOLATION ) {
200
+ if lock_nb {
201
+ // Replace error with a custom WouldBlock error, which later will be
202
+ // mapped in the `helpers` module
203
+ let desc = format ! ( "LockFileEx wouldblock error: {err}" ) ;
204
+ err = io:: Error :: new ( io:: ErrorKind :: WouldBlock , desc) ;
205
+ } else {
206
+ throw_unsup_format ! ( "blocking `flock` is not currently supported" ) ;
207
+ }
208
+ }
209
+ Err ( err)
210
+ }
211
+ _ => panic ! ( "Unexpected return value: {ret}" ) ,
212
+ } ;
213
+ Ok ( res)
214
+ }
215
+
216
+ #[ cfg( not( any( target_family = "unix" , target_family = "windows" ) ) ) ]
217
+ {
218
+ let _ = op;
219
+ compile_error ! ( "flock is supported only on UNIX and Windows hosts" ) ;
220
+ }
221
+ }
222
+
130
223
fn is_tty ( & self , communicate_allowed : bool ) -> bool {
131
224
communicate_allowed && self . file . is_terminal ( )
132
225
}
0 commit comments