@@ -3,14 +3,12 @@ use std::io;
33use std:: io:: { Read , Seek , SeekFrom , Write } ;
44use std:: path:: { Display , Path , PathBuf } ;
55
6- use fs2:: { lock_contended_error, FileExt } ;
76use termcolor:: Color :: Cyan ;
8- #[ cfg( windows) ]
9- use winapi:: shared:: winerror:: ERROR_INVALID_FUNCTION ;
107
118use crate :: util:: errors:: { CargoResult , CargoResultExt } ;
129use crate :: util:: paths;
1310use crate :: util:: Config ;
11+ use sys:: * ;
1412
1513#[ derive( Debug ) ]
1614pub struct FileLock {
@@ -95,7 +93,7 @@ impl Drop for FileLock {
9593 fn drop ( & mut self ) {
9694 if self . state != State :: Unlocked {
9795 if let Some ( f) = self . f . take ( ) {
98- let _ = f . unlock ( ) ;
96+ let _ = unlock ( & f ) ;
9997 }
10098 }
10199 }
@@ -231,13 +229,13 @@ impl Filesystem {
231229 . chain_err ( || format ! ( "failed to open: {}" , path. display( ) ) ) ?;
232230 match state {
233231 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 )
236234 } ) ?;
237235 }
238236 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 )
241239 } ) ?;
242240 }
243241 State :: Unlocked => { }
@@ -281,8 +279,8 @@ fn acquire(
281279 config : & Config ,
282280 msg : & str ,
283281 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 < ( ) > ,
286284) -> CargoResult < ( ) > {
287285 // File locking on Unix is currently implemented via `flock`, which is known
288286 // to be broken on NFS. We could in theory just ignore errors that happen on
@@ -298,24 +296,16 @@ fn acquire(
298296 return Ok ( ( ) ) ;
299297 }
300298
301- match r#try ( ) {
299+ match lock_try ( ) {
302300 Ok ( ( ) ) => return Ok ( ( ) ) ,
303301
304302 // In addition to ignoring NFS which is commonly not working we also
305303 // 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 ( ( ) ) ,
316306
317307 Err ( e) => {
318- if e . raw_os_error ( ) != lock_contended_error ( ) . raw_os_error ( ) {
308+ if ! error_contended ( & e ) {
319309 let e = anyhow:: Error :: from ( e) ;
320310 let cx = format ! ( "failed to lock file: {}" , path. display( ) ) ;
321311 return Err ( e. context ( cx) . into ( ) ) ;
@@ -325,7 +315,7 @@ fn acquire(
325315 let msg = format ! ( "waiting for file lock on {}" , msg) ;
326316 config. shell ( ) . status_with_color ( "Blocking" , & msg, Cyan ) ?;
327317
328- block ( ) . chain_err ( || format ! ( "failed to lock file: {}" , path. display( ) ) ) ?;
318+ lock_block ( ) . chain_err ( || format ! ( "failed to lock file: {}" , path. display( ) ) ) ?;
329319 return Ok ( ( ) ) ;
330320
331321 #[ cfg( all( target_os = "linux" , not( target_env = "musl" ) ) ) ]
@@ -352,3 +342,121 @@ fn acquire(
352342 false
353343 }
354344}
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