77//! The [`FileLock`] type represents a locked file, and provides access to the
88//! file.
99
10+ use std:: fs:: TryLockError ;
1011use std:: fs:: { File , OpenOptions } ;
1112use std:: io;
1213use std:: io:: { Read , Seek , SeekFrom , Write } ;
@@ -17,7 +18,6 @@ use crate::util::errors::CargoResult;
1718use crate :: util:: style;
1819use anyhow:: Context as _;
1920use cargo_util:: paths;
20- use sys:: * ;
2121
2222/// A locked file.
2323///
@@ -103,7 +103,7 @@ impl Write for FileLock {
103103impl Drop for FileLock {
104104 fn drop ( & mut self ) {
105105 if let Some ( f) = self . f . take ( ) {
106- if let Err ( e) = unlock ( & f ) {
106+ if let Err ( e) = f . unlock ( ) {
107107 tracing:: warn!( "failed to release lock: {e:?}" ) ;
108108 }
109109 }
@@ -216,9 +216,7 @@ impl Filesystem {
216216 let mut opts = OpenOptions :: new ( ) ;
217217 opts. read ( true ) . write ( true ) . create ( true ) ;
218218 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 ( ) ) ?;
222220 Ok ( FileLock { f : Some ( f) , path } )
223221 }
224222
@@ -233,7 +231,7 @@ impl Filesystem {
233231 let mut opts = OpenOptions :: new ( ) ;
234232 opts. read ( true ) . write ( true ) . create ( true ) ;
235233 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 ( ) ) ? {
237235 Ok ( Some ( FileLock { f : Some ( f) , path } ) )
238236 } else {
239237 Ok ( None )
@@ -259,8 +257,8 @@ impl Filesystem {
259257 P : AsRef < Path > ,
260258 {
261259 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 ( )
264262 } ) ?;
265263 Ok ( FileLock { f : Some ( f) , path } )
266264 }
@@ -279,8 +277,8 @@ impl Filesystem {
279277 let mut opts = OpenOptions :: new ( ) ;
280278 opts. read ( true ) . write ( true ) . create ( true ) ;
281279 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 ( )
284282 } ) ?;
285283 Ok ( FileLock { f : Some ( f) , path } )
286284 }
@@ -296,7 +294,7 @@ impl Filesystem {
296294 let mut opts = OpenOptions :: new ( ) ;
297295 opts. read ( true ) . write ( true ) . create ( true ) ;
298296 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 ( ) ) ? {
300298 Ok ( Some ( FileLock { f : Some ( f) , path } ) )
301299 } else {
302300 Ok ( None )
@@ -335,7 +333,7 @@ impl PartialEq<Filesystem> for Path {
335333 }
336334}
337335
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 > {
339337 // File locking on Unix is currently implemented via `flock`, which is known
340338 // to be broken on NFS. We could in theory just ignore errors that happen on
341339 // 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
352350 }
353351
354352 match lock_try ( ) {
355- Ok ( ( ) ) => return Ok ( true ) ,
353+ Ok ( ( ) ) => Ok ( true ) ,
356354
357355 // In addition to ignoring NFS which is commonly not working we also
358356 // just ignore locking on filesystems that look like they don't
359357 // implement file locking.
360- Err ( e ) if error_unsupported ( & e) => return Ok ( true ) ,
358+ Err ( TryLockError :: Error ( e ) ) if error_unsupported ( & e) => Ok ( true ) ,
361359
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) )
368364 }
365+
366+ Err ( TryLockError :: WouldBlock ) => Ok ( false ) ,
369367 }
370- Ok ( false )
371368}
372369
373370/// Acquires a lock on a file in a "nice" manner.
@@ -389,7 +386,7 @@ fn acquire(
389386 gctx : & GlobalContext ,
390387 msg : & str ,
391388 path : & Path ,
392- lock_try : & dyn Fn ( ) -> io :: Result < ( ) > ,
389+ lock_try : & dyn Fn ( ) -> Result < ( ) , TryLockError > ,
393390 lock_block : & dyn Fn ( ) -> io:: Result < ( ) > ,
394391) -> CargoResult < ( ) > {
395392 if cfg ! ( debug_assertions) {
@@ -431,176 +428,20 @@ fn is_on_nfs_mount(_path: &Path) -> bool {
431428}
432429
433430#[ 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 ,
535439 }
536440}
537441
538442#[ 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 )
606447}
0 commit comments