@@ -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