@@ -16,6 +16,8 @@ use crate::shims::unix::*;
1616use crate :: * ;
1717use shims:: time:: system_time_to_duration;
1818
19+ use self :: fd:: FlockOp ;
20+
1921#[ derive( Debug ) ]
2022struct FileHandle {
2123 file : File ,
@@ -127,6 +129,97 @@ impl FileDescription for FileHandle {
127129 }
128130 }
129131
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+
130223 fn is_tty ( & self , communicate_allowed : bool ) -> bool {
131224 communicate_allowed && self . file . is_terminal ( )
132225 }
0 commit comments