7
7
//! The [`FileLock`] type represents a locked file, and provides access to the
8
8
//! file.
9
9
10
+ use std:: fs:: TryLockError ;
10
11
use std:: fs:: { File , OpenOptions } ;
11
12
use std:: io;
12
13
use std:: io:: { Read , Seek , SeekFrom , Write } ;
@@ -17,7 +18,6 @@ use crate::util::errors::CargoResult;
17
18
use crate :: util:: style;
18
19
use anyhow:: Context as _;
19
20
use cargo_util:: paths;
20
- use sys:: * ;
21
21
22
22
/// A locked file.
23
23
///
@@ -103,7 +103,7 @@ impl Write for FileLock {
103
103
impl Drop for FileLock {
104
104
fn drop ( & mut self ) {
105
105
if let Some ( f) = self . f . take ( ) {
106
- if let Err ( e) = unlock ( & f ) {
106
+ if let Err ( e) = f . unlock ( ) {
107
107
tracing:: warn!( "failed to release lock: {e:?}" ) ;
108
108
}
109
109
}
@@ -216,9 +216,7 @@ impl Filesystem {
216
216
let mut opts = OpenOptions :: new ( ) ;
217
217
opts. read ( true ) . write ( true ) . create ( true ) ;
218
218
let ( path, f) = self . open ( path. as_ref ( ) , & opts, true ) ?;
219
- acquire ( gctx, msg, & path, & || try_lock_exclusive ( & f) , & || {
220
- lock_exclusive ( & f)
221
- } ) ?;
219
+ acquire ( gctx, msg, & path, & || f. try_lock ( ) , & || f. lock ( ) ) ?;
222
220
Ok ( FileLock { f : Some ( f) , path } )
223
221
}
224
222
@@ -233,7 +231,7 @@ impl Filesystem {
233
231
let mut opts = OpenOptions :: new ( ) ;
234
232
opts. read ( true ) . write ( true ) . create ( true ) ;
235
233
let ( path, f) = self . open ( path. as_ref ( ) , & opts, true ) ?;
236
- if try_acquire ( & path, & || try_lock_exclusive ( & f ) ) ? {
234
+ if try_acquire ( & path, & || f . try_lock ( ) ) ? {
237
235
Ok ( Some ( FileLock { f : Some ( f) , path } ) )
238
236
} else {
239
237
Ok ( None )
@@ -259,8 +257,8 @@ impl Filesystem {
259
257
P : AsRef < Path > ,
260
258
{
261
259
let ( path, f) = self . open ( path. as_ref ( ) , & OpenOptions :: new ( ) . read ( true ) , false ) ?;
262
- acquire ( gctx, msg, & path, & || try_lock_shared ( & f ) , & || {
263
- lock_shared ( & f )
260
+ acquire ( gctx, msg, & path, & || f . try_lock_shared ( ) , & || {
261
+ f . lock_shared ( )
264
262
} ) ?;
265
263
Ok ( FileLock { f : Some ( f) , path } )
266
264
}
@@ -279,8 +277,8 @@ impl Filesystem {
279
277
let mut opts = OpenOptions :: new ( ) ;
280
278
opts. read ( true ) . write ( true ) . create ( true ) ;
281
279
let ( path, f) = self . open ( path. as_ref ( ) , & opts, true ) ?;
282
- acquire ( gctx, msg, & path, & || try_lock_shared ( & f ) , & || {
283
- lock_shared ( & f )
280
+ acquire ( gctx, msg, & path, & || f . try_lock_shared ( ) , & || {
281
+ f . lock_shared ( )
284
282
} ) ?;
285
283
Ok ( FileLock { f : Some ( f) , path } )
286
284
}
@@ -296,7 +294,7 @@ impl Filesystem {
296
294
let mut opts = OpenOptions :: new ( ) ;
297
295
opts. read ( true ) . write ( true ) . create ( true ) ;
298
296
let ( path, f) = self . open ( path. as_ref ( ) , & opts, true ) ?;
299
- if try_acquire ( & path, & || try_lock_shared ( & f ) ) ? {
297
+ if try_acquire ( & path, & || f . try_lock_shared ( ) ) ? {
300
298
Ok ( Some ( FileLock { f : Some ( f) , path } ) )
301
299
} else {
302
300
Ok ( None )
@@ -335,7 +333,7 @@ impl PartialEq<Filesystem> for Path {
335
333
}
336
334
}
337
335
338
- fn try_acquire ( path : & Path , lock_try : & dyn Fn ( ) -> io :: Result < ( ) > ) -> CargoResult < bool > {
336
+ fn try_acquire ( path : & Path , lock_try : & dyn Fn ( ) -> Result < ( ) , TryLockError > ) -> CargoResult < bool > {
339
337
// File locking on Unix is currently implemented via `flock`, which is known
340
338
// to be broken on NFS. We could in theory just ignore errors that happen on
341
339
// NFS, but apparently the failure mode [1] for `flock` on NFS is **blocking
@@ -352,22 +350,21 @@ fn try_acquire(path: &Path, lock_try: &dyn Fn() -> io::Result<()>) -> CargoResul
352
350
}
353
351
354
352
match lock_try ( ) {
355
- Ok ( ( ) ) => return Ok ( true ) ,
353
+ Ok ( ( ) ) => Ok ( true ) ,
356
354
357
355
// In addition to ignoring NFS which is commonly not working we also
358
356
// just ignore locking on filesystems that look like they don't
359
357
// implement file locking.
360
- Err ( e ) if error_unsupported ( & e) => return Ok ( true ) ,
358
+ Err ( TryLockError :: Error ( e ) ) if error_unsupported ( & e) => Ok ( true ) ,
361
359
362
- Err ( e) => {
363
- if !error_contended ( & e) {
364
- let e = anyhow:: Error :: from ( e) ;
365
- let cx = format ! ( "failed to lock file: {}" , path. display( ) ) ;
366
- return Err ( e. context ( cx) ) ;
367
- }
360
+ Err ( TryLockError :: Error ( e) ) => {
361
+ let e = anyhow:: Error :: from ( e) ;
362
+ let cx = format ! ( "failed to lock file: {}" , path. display( ) ) ;
363
+ Err ( e. context ( cx) )
368
364
}
365
+
366
+ Err ( TryLockError :: WouldBlock ) => Ok ( false ) ,
369
367
}
370
- Ok ( false )
371
368
}
372
369
373
370
/// Acquires a lock on a file in a "nice" manner.
@@ -389,7 +386,7 @@ fn acquire(
389
386
gctx : & GlobalContext ,
390
387
msg : & str ,
391
388
path : & Path ,
392
- lock_try : & dyn Fn ( ) -> io :: Result < ( ) > ,
389
+ lock_try : & dyn Fn ( ) -> Result < ( ) , TryLockError > ,
393
390
lock_block : & dyn Fn ( ) -> io:: Result < ( ) > ,
394
391
) -> CargoResult < ( ) > {
395
392
if cfg ! ( debug_assertions) {
@@ -431,176 +428,20 @@ fn is_on_nfs_mount(_path: &Path) -> bool {
431
428
}
432
429
433
430
#[ cfg( unix) ]
434
- mod sys {
435
- use std:: fs:: File ;
436
- use std:: io:: { Error , Result } ;
437
- use std:: os:: unix:: io:: AsRawFd ;
438
-
439
- #[ cfg( not( target_os = "solaris" ) ) ]
440
- const LOCK_SH : i32 = libc:: LOCK_SH ;
441
- #[ cfg( target_os = "solaris" ) ]
442
- const LOCK_SH : i32 = 1 ;
443
- #[ cfg( not( target_os = "solaris" ) ) ]
444
- const LOCK_EX : i32 = libc:: LOCK_EX ;
445
- #[ cfg( target_os = "solaris" ) ]
446
- const LOCK_EX : i32 = 2 ;
447
- #[ cfg( not( target_os = "solaris" ) ) ]
448
- const LOCK_NB : i32 = libc:: LOCK_NB ;
449
- #[ cfg( target_os = "solaris" ) ]
450
- const LOCK_NB : i32 = 4 ;
451
- #[ cfg( not( target_os = "solaris" ) ) ]
452
- const LOCK_UN : i32 = libc:: LOCK_UN ;
453
- #[ cfg( target_os = "solaris" ) ]
454
- const LOCK_UN : i32 = 8 ;
455
-
456
- pub ( super ) fn lock_shared ( file : & File ) -> Result < ( ) > {
457
- flock ( file, LOCK_SH )
458
- }
459
-
460
- pub ( super ) fn lock_exclusive ( file : & File ) -> Result < ( ) > {
461
- flock ( file, LOCK_EX )
462
- }
463
-
464
- pub ( super ) fn try_lock_shared ( file : & File ) -> Result < ( ) > {
465
- flock ( file, LOCK_SH | LOCK_NB )
466
- }
467
-
468
- pub ( super ) fn try_lock_exclusive ( file : & File ) -> Result < ( ) > {
469
- flock ( file, LOCK_EX | LOCK_NB )
470
- }
471
-
472
- pub ( super ) fn unlock ( file : & File ) -> Result < ( ) > {
473
- flock ( file, LOCK_UN )
474
- }
475
-
476
- pub ( super ) fn error_contended ( err : & Error ) -> bool {
477
- err. raw_os_error ( ) . map_or ( false , |x| x == libc:: EWOULDBLOCK )
478
- }
479
-
480
- pub ( super ) fn error_unsupported ( err : & Error ) -> bool {
481
- match err. raw_os_error ( ) {
482
- // Unfortunately, depending on the target, these may or may not be the same.
483
- // For targets in which they are the same, the duplicate pattern causes a warning.
484
- #[ allow( unreachable_patterns) ]
485
- Some ( libc:: ENOTSUP | libc:: EOPNOTSUPP ) => true ,
486
- Some ( libc:: ENOSYS ) => true ,
487
- _ => false ,
488
- }
489
- }
490
-
491
- #[ cfg( not( target_os = "solaris" ) ) ]
492
- fn flock ( file : & File , flag : libc:: c_int ) -> Result < ( ) > {
493
- let ret = unsafe { libc:: flock ( file. as_raw_fd ( ) , flag) } ;
494
- if ret < 0 {
495
- Err ( Error :: last_os_error ( ) )
496
- } else {
497
- Ok ( ( ) )
498
- }
499
- }
500
-
501
- #[ cfg( target_os = "solaris" ) ]
502
- fn flock ( file : & File , flag : libc:: c_int ) -> Result < ( ) > {
503
- // Solaris lacks flock(), so try to emulate using fcntl()
504
- let mut flock = libc:: flock {
505
- l_type : 0 ,
506
- l_whence : 0 ,
507
- l_start : 0 ,
508
- l_len : 0 ,
509
- l_sysid : 0 ,
510
- l_pid : 0 ,
511
- l_pad : [ 0 , 0 , 0 , 0 ] ,
512
- } ;
513
- flock. l_type = if flag & LOCK_UN != 0 {
514
- libc:: F_UNLCK
515
- } else if flag & LOCK_EX != 0 {
516
- libc:: F_WRLCK
517
- } else if flag & LOCK_SH != 0 {
518
- libc:: F_RDLCK
519
- } else {
520
- panic ! ( "unexpected flock() operation" )
521
- } ;
522
-
523
- let mut cmd = libc:: F_SETLKW ;
524
- if ( flag & LOCK_NB ) != 0 {
525
- cmd = libc:: F_SETLK ;
526
- }
527
-
528
- let ret = unsafe { libc:: fcntl ( file. as_raw_fd ( ) , cmd, & flock) } ;
529
-
530
- if ret < 0 {
531
- Err ( Error :: last_os_error ( ) )
532
- } else {
533
- Ok ( ( ) )
534
- }
431
+ fn error_unsupported ( err : & std:: io:: Error ) -> bool {
432
+ match err. raw_os_error ( ) {
433
+ // Unfortunately, depending on the target, these may or may not be the same.
434
+ // For targets in which they are the same, the duplicate pattern causes a warning.
435
+ #[ allow( unreachable_patterns) ]
436
+ Some ( libc:: ENOTSUP | libc:: EOPNOTSUPP ) => true ,
437
+ Some ( libc:: ENOSYS ) => true ,
438
+ _ => false ,
535
439
}
536
440
}
537
441
538
442
#[ cfg( windows) ]
539
- mod sys {
540
- use std:: fs:: File ;
541
- use std:: io:: { Error , Result } ;
542
- use std:: mem;
543
- use std:: os:: windows:: io:: AsRawHandle ;
544
-
545
- use windows_sys:: Win32 :: Foundation :: HANDLE ;
546
- use windows_sys:: Win32 :: Foundation :: { ERROR_INVALID_FUNCTION , ERROR_LOCK_VIOLATION } ;
547
- use windows_sys:: Win32 :: Storage :: FileSystem :: {
548
- LOCKFILE_EXCLUSIVE_LOCK , LOCKFILE_FAIL_IMMEDIATELY , LockFileEx , UnlockFile ,
549
- } ;
550
-
551
- pub ( super ) fn lock_shared ( file : & File ) -> Result < ( ) > {
552
- lock_file ( file, 0 )
553
- }
554
-
555
- pub ( super ) fn lock_exclusive ( file : & File ) -> Result < ( ) > {
556
- lock_file ( file, LOCKFILE_EXCLUSIVE_LOCK )
557
- }
558
-
559
- pub ( super ) fn try_lock_shared ( file : & File ) -> Result < ( ) > {
560
- lock_file ( file, LOCKFILE_FAIL_IMMEDIATELY )
561
- }
562
-
563
- pub ( super ) fn try_lock_exclusive ( file : & File ) -> Result < ( ) > {
564
- lock_file ( file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY )
565
- }
566
-
567
- pub ( super ) fn error_contended ( err : & Error ) -> bool {
568
- err. raw_os_error ( )
569
- . map_or ( false , |x| x == ERROR_LOCK_VIOLATION as i32 )
570
- }
571
-
572
- pub ( super ) fn error_unsupported ( err : & Error ) -> bool {
573
- err. raw_os_error ( )
574
- . map_or ( false , |x| x == ERROR_INVALID_FUNCTION as i32 )
575
- }
576
-
577
- pub ( super ) fn unlock ( file : & File ) -> Result < ( ) > {
578
- unsafe {
579
- let ret = UnlockFile ( file. as_raw_handle ( ) as HANDLE , 0 , 0 , !0 , !0 ) ;
580
- if ret == 0 {
581
- Err ( Error :: last_os_error ( ) )
582
- } else {
583
- Ok ( ( ) )
584
- }
585
- }
586
- }
587
-
588
- fn lock_file ( file : & File , flags : u32 ) -> Result < ( ) > {
589
- unsafe {
590
- let mut overlapped = mem:: zeroed ( ) ;
591
- let ret = LockFileEx (
592
- file. as_raw_handle ( ) as HANDLE ,
593
- flags,
594
- 0 ,
595
- !0 ,
596
- !0 ,
597
- & mut overlapped,
598
- ) ;
599
- if ret == 0 {
600
- Err ( Error :: last_os_error ( ) )
601
- } else {
602
- Ok ( ( ) )
603
- }
604
- }
605
- }
443
+ fn error_unsupported ( err : & std:: io:: Error ) -> bool {
444
+ use windows_sys:: Win32 :: Foundation :: ERROR_INVALID_FUNCTION ;
445
+ err. raw_os_error ( )
446
+ . map_or ( false , |x| x == ERROR_INVALID_FUNCTION as i32 )
606
447
}
0 commit comments