@@ -3,14 +3,12 @@ use std::io;
3
3
use std:: io:: { Read , Seek , SeekFrom , Write } ;
4
4
use std:: path:: { Display , Path , PathBuf } ;
5
5
6
- use fs2:: { lock_contended_error, FileExt } ;
7
6
use termcolor:: Color :: Cyan ;
8
- #[ cfg( windows) ]
9
- use winapi:: shared:: winerror:: ERROR_INVALID_FUNCTION ;
10
7
11
8
use crate :: util:: errors:: { CargoResult , CargoResultExt } ;
12
9
use crate :: util:: paths;
13
10
use crate :: util:: Config ;
11
+ use sys:: * ;
14
12
15
13
#[ derive( Debug ) ]
16
14
pub struct FileLock {
@@ -95,7 +93,7 @@ impl Drop for FileLock {
95
93
fn drop ( & mut self ) {
96
94
if self . state != State :: Unlocked {
97
95
if let Some ( f) = self . f . take ( ) {
98
- let _ = f . unlock ( ) ;
96
+ let _ = unlock ( & f ) ;
99
97
}
100
98
}
101
99
}
@@ -231,13 +229,13 @@ impl Filesystem {
231
229
. chain_err ( || format ! ( "failed to open: {}" , path. display( ) ) ) ?;
232
230
match state {
233
231
State :: Exclusive => {
234
- acquire ( config, msg, & path, & || f . try_lock_exclusive ( ) , & || {
235
- f . lock_exclusive ( )
232
+ acquire ( config, msg, & path, & || try_lock_exclusive ( & f ) , & || {
233
+ lock_exclusive ( & f )
236
234
} ) ?;
237
235
}
238
236
State :: Shared => {
239
- acquire ( config, msg, & path, & || f . try_lock_shared ( ) , & || {
240
- f . lock_shared ( )
237
+ acquire ( config, msg, & path, & || try_lock_shared ( & f ) , & || {
238
+ lock_shared ( & f )
241
239
} ) ?;
242
240
}
243
241
State :: Unlocked => { }
@@ -281,8 +279,8 @@ fn acquire(
281
279
config : & Config ,
282
280
msg : & str ,
283
281
path : & Path ,
284
- r#try : & dyn Fn ( ) -> io:: Result < ( ) > ,
285
- block : & dyn Fn ( ) -> io:: Result < ( ) > ,
282
+ lock_try : & dyn Fn ( ) -> io:: Result < ( ) > ,
283
+ lock_block : & dyn Fn ( ) -> io:: Result < ( ) > ,
286
284
) -> CargoResult < ( ) > {
287
285
// File locking on Unix is currently implemented via `flock`, which is known
288
286
// to be broken on NFS. We could in theory just ignore errors that happen on
@@ -298,24 +296,16 @@ fn acquire(
298
296
return Ok ( ( ) ) ;
299
297
}
300
298
301
- match r#try ( ) {
299
+ match lock_try ( ) {
302
300
Ok ( ( ) ) => return Ok ( ( ) ) ,
303
301
304
302
// In addition to ignoring NFS which is commonly not working we also
305
303
// just ignore locking on filesystems that look like they don't
306
- // implement file locking. We detect that here via the return value of
307
- // locking (e.g., inspecting errno).
308
- #[ cfg( unix) ]
309
- Err ( ref e) if e. raw_os_error ( ) == Some ( libc:: ENOTSUP ) => return Ok ( ( ) ) ,
310
-
311
- #[ cfg( target_os = "linux" ) ]
312
- Err ( ref e) if e. raw_os_error ( ) == Some ( libc:: ENOSYS ) => return Ok ( ( ) ) ,
313
-
314
- #[ cfg( windows) ]
315
- Err ( ref e) if e. raw_os_error ( ) == Some ( ERROR_INVALID_FUNCTION as i32 ) => return Ok ( ( ) ) ,
304
+ // implement file locking.
305
+ Err ( e) if error_unsupported ( & e) => return Ok ( ( ) ) ,
316
306
317
307
Err ( e) => {
318
- if e . raw_os_error ( ) != lock_contended_error ( ) . raw_os_error ( ) {
308
+ if ! error_contended ( & e ) {
319
309
let e = anyhow:: Error :: from ( e) ;
320
310
let cx = format ! ( "failed to lock file: {}" , path. display( ) ) ;
321
311
return Err ( e. context ( cx) . into ( ) ) ;
@@ -325,7 +315,7 @@ fn acquire(
325
315
let msg = format ! ( "waiting for file lock on {}" , msg) ;
326
316
config. shell ( ) . status_with_color ( "Blocking" , & msg, Cyan ) ?;
327
317
328
- block ( ) . chain_err ( || format ! ( "failed to lock file: {}" , path. display( ) ) ) ?;
318
+ lock_block ( ) . chain_err ( || format ! ( "failed to lock file: {}" , path. display( ) ) ) ?;
329
319
return Ok ( ( ) ) ;
330
320
331
321
#[ cfg( all( target_os = "linux" , not( target_env = "musl" ) ) ) ]
@@ -352,3 +342,121 @@ fn acquire(
352
342
false
353
343
}
354
344
}
345
+
346
+ #[ cfg( unix) ]
347
+ mod sys {
348
+ use std:: fs:: File ;
349
+ use std:: io:: { Error , Result } ;
350
+ use std:: os:: unix:: io:: AsRawFd ;
351
+
352
+ pub ( super ) fn lock_shared ( file : & File ) -> Result < ( ) > {
353
+ flock ( file, libc:: LOCK_SH )
354
+ }
355
+
356
+ pub ( super ) fn lock_exclusive ( file : & File ) -> Result < ( ) > {
357
+ flock ( file, libc:: LOCK_EX )
358
+ }
359
+
360
+ pub ( super ) fn try_lock_shared ( file : & File ) -> Result < ( ) > {
361
+ flock ( file, libc:: LOCK_SH | libc:: LOCK_NB )
362
+ }
363
+
364
+ pub ( super ) fn try_lock_exclusive ( file : & File ) -> Result < ( ) > {
365
+ flock ( file, libc:: LOCK_EX | libc:: LOCK_NB )
366
+ }
367
+
368
+ pub ( super ) fn unlock ( file : & File ) -> Result < ( ) > {
369
+ flock ( file, libc:: LOCK_UN )
370
+ }
371
+
372
+ pub ( super ) fn error_contended ( err : & Error ) -> bool {
373
+ err. raw_os_error ( ) . map_or ( false , |x| x == libc:: EWOULDBLOCK )
374
+ }
375
+
376
+ pub ( super ) fn error_unsupported ( err : & Error ) -> bool {
377
+ match err. raw_os_error ( ) {
378
+ Some ( libc:: ENOTSUP ) => true ,
379
+ #[ cfg( target_os = "linux" ) ]
380
+ Some ( libc:: ENOSYS ) => true ,
381
+ _ => false ,
382
+ }
383
+ }
384
+
385
+ #[ cfg( not( target_os = "solaris" ) ) ]
386
+ fn flock ( file : & File , flag : libc:: c_int ) -> Result < ( ) > {
387
+ let ret = unsafe { libc:: flock ( file. as_raw_fd ( ) , flag) } ;
388
+ if ret < 0 {
389
+ Err ( Error :: last_os_error ( ) )
390
+ } else {
391
+ Ok ( ( ) )
392
+ }
393
+ }
394
+
395
+ #[ cfg( target_os = "solaris" ) ]
396
+ fn flock ( file : & File , flag : libc:: c_int ) -> Result < ( ) > {
397
+ // Solaris lacks flock(), so simply succeed with a no-op
398
+ Ok ( ( ) )
399
+ }
400
+ }
401
+
402
+ #[ cfg( windows) ]
403
+ mod sys {
404
+ use std:: fs:: File ;
405
+ use std:: io:: { Error , Result } ;
406
+ use std:: mem;
407
+ use std:: os:: windows:: io:: AsRawHandle ;
408
+
409
+ use winapi:: shared:: minwindef:: DWORD ;
410
+ use winapi:: shared:: winerror:: { ERROR_INVALID_FUNCTION , ERROR_LOCK_VIOLATION } ;
411
+ use winapi:: um:: fileapi:: { LockFileEx , UnlockFile } ;
412
+ use winapi:: um:: minwinbase:: { LOCKFILE_EXCLUSIVE_LOCK , LOCKFILE_FAIL_IMMEDIATELY } ;
413
+
414
+ pub ( super ) fn lock_shared ( file : & File ) -> Result < ( ) > {
415
+ lock_file ( file, 0 )
416
+ }
417
+
418
+ pub ( super ) fn lock_exclusive ( file : & File ) -> Result < ( ) > {
419
+ lock_file ( file, LOCKFILE_EXCLUSIVE_LOCK )
420
+ }
421
+
422
+ pub ( super ) fn try_lock_shared ( file : & File ) -> Result < ( ) > {
423
+ lock_file ( file, LOCKFILE_FAIL_IMMEDIATELY )
424
+ }
425
+
426
+ pub ( super ) fn try_lock_exclusive ( file : & File ) -> Result < ( ) > {
427
+ lock_file ( file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY )
428
+ }
429
+
430
+ pub ( super ) fn error_contended ( err : & Error ) -> bool {
431
+ err. raw_os_error ( )
432
+ . map_or ( false , |x| x == ERROR_LOCK_VIOLATION as i32 )
433
+ }
434
+
435
+ pub ( super ) fn error_unsupported ( err : & Error ) -> bool {
436
+ err. raw_os_error ( )
437
+ . map_or ( false , |x| x == ERROR_INVALID_FUNCTION as i32 )
438
+ }
439
+
440
+ pub ( super ) fn unlock ( file : & File ) -> Result < ( ) > {
441
+ unsafe {
442
+ let ret = UnlockFile ( file. as_raw_handle ( ) , 0 , 0 , !0 , !0 ) ;
443
+ if ret == 0 {
444
+ Err ( Error :: last_os_error ( ) )
445
+ } else {
446
+ Ok ( ( ) )
447
+ }
448
+ }
449
+ }
450
+
451
+ fn lock_file ( file : & File , flags : DWORD ) -> Result < ( ) > {
452
+ unsafe {
453
+ let mut overlapped = mem:: zeroed ( ) ;
454
+ let ret = LockFileEx ( file. as_raw_handle ( ) , flags, 0 , !0 , !0 , & mut overlapped) ;
455
+ if ret == 0 {
456
+ Err ( Error :: last_os_error ( ) )
457
+ } else {
458
+ Ok ( ( ) )
459
+ }
460
+ }
461
+ }
462
+ }
0 commit comments