@@ -5,8 +5,7 @@ use std::fs::{File, OpenOptions};
5
5
use std:: path:: { Display , Path , PathBuf } ;
6
6
use std:: { fs, io} ;
7
7
8
- use fs2;
9
- use fs2:: FileExt ;
8
+ use self :: sys:: * ;
10
9
11
10
#[ derive( PartialEq ) ]
12
11
enum State {
@@ -107,12 +106,12 @@ impl Filesystem {
107
106
108
107
match state {
109
108
State :: Exclusive => {
110
- acquire ( msg, & path, self . quiet , & || f . try_lock_exclusive ( ) , & || {
111
- f . lock_exclusive ( )
109
+ acquire ( msg, & path, self . quiet , & || try_lock_exclusive ( & f ) , & || {
110
+ lock_exclusive ( & f )
112
111
} ) ?;
113
112
}
114
113
State :: Shared => {
115
- acquire ( msg, & path, self . quiet , & || f . try_lock_shared ( ) , & || f . lock_shared ( ) ) ?;
114
+ acquire ( msg, & path, self . quiet , & || try_lock_shared ( & f ) , & || lock_shared ( & f ) ) ?;
116
115
}
117
116
}
118
117
@@ -129,16 +128,16 @@ impl Filesystem {
129
128
130
129
impl Drop for FileLock {
131
130
fn drop ( & mut self ) {
132
- self . file . unlock ( ) . ok ( ) ;
131
+ unlock ( & self . file ) . ok ( ) ;
133
132
}
134
133
}
135
134
136
135
fn acquire (
137
136
msg : & str ,
138
137
path : & Path ,
139
138
quiet : bool ,
140
- try : & dyn Fn ( ) -> io:: Result < ( ) > ,
141
- block : & dyn Fn ( ) -> io:: Result < ( ) > ,
139
+ lock_try : & dyn Fn ( ) -> io:: Result < ( ) > ,
140
+ lock_block : & dyn Fn ( ) -> io:: Result < ( ) > ,
142
141
) -> io:: Result < ( ) > {
143
142
#[ cfg( all( target_os = "linux" , not( target_env = "musl" ) ) ) ]
144
143
fn is_on_nfs_mount ( path : & Path ) -> bool {
@@ -168,12 +167,16 @@ fn acquire(
168
167
return Ok ( ( ) ) ;
169
168
}
170
169
171
- match try( ) {
172
- Ok ( _) => return Ok ( ( ) ) ,
173
- #[ cfg( target_os = "macos" ) ]
174
- Err ( ref e) if e. raw_os_error ( ) == Some ( :: libc:: ENOTSUP ) => return Ok ( ( ) ) ,
170
+ match lock_try ( ) {
171
+ Ok ( ( ) ) => return Ok ( ( ) ) ,
172
+
173
+ // In addition to ignoring NFS which is commonly not working we also
174
+ // just ignore locking on filesystems that look like they don't
175
+ // implement file locking.
176
+ Err ( e) if error_unsupported ( & e) => return Ok ( ( ) ) ,
177
+
175
178
Err ( e) => {
176
- if e . raw_os_error ( ) != fs2 :: lock_contended_error ( ) . raw_os_error ( ) {
179
+ if ! error_contended ( & e ) {
177
180
return Err ( e) ;
178
181
}
179
182
}
@@ -187,7 +190,125 @@ fn acquire(
187
190
)
188
191
}
189
192
190
- block ( )
193
+ lock_block ( )
194
+ }
195
+
196
+ #[ cfg( unix) ]
197
+ mod sys {
198
+ use std:: fs:: File ;
199
+ use std:: io:: { Error , Result } ;
200
+ use std:: os:: unix:: io:: AsRawFd ;
201
+
202
+ pub ( super ) fn lock_shared ( file : & File ) -> Result < ( ) > {
203
+ flock ( file, libc:: LOCK_SH )
204
+ }
205
+
206
+ pub ( super ) fn lock_exclusive ( file : & File ) -> Result < ( ) > {
207
+ flock ( file, libc:: LOCK_EX )
208
+ }
209
+
210
+ pub ( super ) fn try_lock_shared ( file : & File ) -> Result < ( ) > {
211
+ flock ( file, libc:: LOCK_SH | libc:: LOCK_NB )
212
+ }
213
+
214
+ pub ( super ) fn try_lock_exclusive ( file : & File ) -> Result < ( ) > {
215
+ flock ( file, libc:: LOCK_EX | libc:: LOCK_NB )
216
+ }
217
+
218
+ pub ( super ) fn unlock ( file : & File ) -> Result < ( ) > {
219
+ flock ( file, libc:: LOCK_UN )
220
+ }
221
+
222
+ pub ( super ) fn error_contended ( err : & Error ) -> bool {
223
+ err. raw_os_error ( ) . map_or ( false , |x| x == libc:: EWOULDBLOCK )
224
+ }
225
+
226
+ pub ( super ) fn error_unsupported ( err : & Error ) -> bool {
227
+ match err. raw_os_error ( ) {
228
+ Some ( libc:: ENOTSUP ) => true ,
229
+ #[ cfg( target_os = "linux" ) ]
230
+ Some ( libc:: ENOSYS ) => true ,
231
+ _ => false ,
232
+ }
233
+ }
234
+
235
+ #[ cfg( not( target_os = "solaris" ) ) ]
236
+ fn flock ( file : & File , flag : libc:: c_int ) -> Result < ( ) > {
237
+ let ret = unsafe { libc:: flock ( file. as_raw_fd ( ) , flag) } ;
238
+ if ret < 0 {
239
+ Err ( Error :: last_os_error ( ) )
240
+ } else {
241
+ Ok ( ( ) )
242
+ }
243
+ }
244
+
245
+ #[ cfg( target_os = "solaris" ) ]
246
+ fn flock ( file : & File , flag : libc:: c_int ) -> Result < ( ) > {
247
+ // Solaris lacks flock(), so simply succeed with a no-op
248
+ Ok ( ( ) )
249
+ }
250
+ }
251
+
252
+ #[ cfg( windows) ]
253
+ mod sys {
254
+ use std:: fs:: File ;
255
+ use std:: io:: { Error , Result } ;
256
+ use std:: mem;
257
+ use std:: os:: windows:: io:: AsRawHandle ;
258
+
259
+ use winapi:: shared:: minwindef:: DWORD ;
260
+ use winapi:: shared:: winerror:: { ERROR_INVALID_FUNCTION , ERROR_LOCK_VIOLATION } ;
261
+ use winapi:: um:: fileapi:: { LockFileEx , UnlockFile } ;
262
+ use winapi:: um:: minwinbase:: { LOCKFILE_EXCLUSIVE_LOCK , LOCKFILE_FAIL_IMMEDIATELY } ;
263
+
264
+ pub ( super ) fn lock_shared ( file : & File ) -> Result < ( ) > {
265
+ lock_file ( file, 0 )
266
+ }
267
+
268
+ pub ( super ) fn lock_exclusive ( file : & File ) -> Result < ( ) > {
269
+ lock_file ( file, LOCKFILE_EXCLUSIVE_LOCK )
270
+ }
271
+
272
+ pub ( super ) fn try_lock_shared ( file : & File ) -> Result < ( ) > {
273
+ lock_file ( file, LOCKFILE_FAIL_IMMEDIATELY )
274
+ }
275
+
276
+ pub ( super ) fn try_lock_exclusive ( file : & File ) -> Result < ( ) > {
277
+ lock_file ( file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY )
278
+ }
279
+
280
+ pub ( super ) fn error_contended ( err : & Error ) -> bool {
281
+ err. raw_os_error ( )
282
+ . map_or ( false , |x| x == ERROR_LOCK_VIOLATION as i32 )
283
+ }
284
+
285
+ pub ( super ) fn error_unsupported ( err : & Error ) -> bool {
286
+ err. raw_os_error ( )
287
+ . map_or ( false , |x| x == ERROR_INVALID_FUNCTION as i32 )
288
+ }
289
+
290
+ pub ( super ) fn unlock ( file : & File ) -> Result < ( ) > {
291
+ unsafe {
292
+ let ret = UnlockFile ( file. as_raw_handle ( ) , 0 , 0 , !0 , !0 ) ;
293
+ if ret == 0 {
294
+ Err ( Error :: last_os_error ( ) )
295
+ } else {
296
+ Ok ( ( ) )
297
+ }
298
+ }
299
+ }
300
+
301
+ fn lock_file ( file : & File , flags : DWORD ) -> Result < ( ) > {
302
+ unsafe {
303
+ let mut overlapped = mem:: zeroed ( ) ;
304
+ let ret = LockFileEx ( file. as_raw_handle ( ) , flags, 0 , !0 , !0 , & mut overlapped) ;
305
+ if ret == 0 {
306
+ Err ( Error :: last_os_error ( ) )
307
+ } else {
308
+ Ok ( ( ) )
309
+ }
310
+ }
311
+ }
191
312
}
192
313
193
314
fn create_dir_all ( path : & Path ) -> io:: Result < ( ) > {
0 commit comments