@@ -57,17 +57,32 @@ impl Capabilities {
5757
5858 #[ cfg( unix) ]
5959 fn probe_file_mode ( root : & Path ) -> std:: io:: Result < bool > {
60- use std:: os:: unix:: fs:: { MetadataExt , OpenOptionsExt } ;
60+ use std:: os:: unix:: fs:: { MetadataExt , OpenOptionsExt , PermissionsExt } ;
6161
62- // test it exactly as we typically create executable files, not using chmod.
62+ // First check that we can create an executable file, then check that we
63+ // can change the executable bit.
64+ // The equivalent test by git itself is here:
65+ // https://github.com/git/git/blob/f0ef5b6d9bcc258e4cbef93839d1b7465d5212b9/setup.c#L2367-L2379
6366 let rand = fastrand:: usize ( ..) ;
6467 let test_path = root. join ( format ! ( "_test_executable_bit{rand}" ) ) ;
6568 let res = std:: fs:: OpenOptions :: new ( )
6669 . create_new ( true )
6770 . write ( true )
6871 . mode ( 0o777 )
6972 . open ( & test_path)
70- . and_then ( |f| f. metadata ( ) . map ( |m| m. mode ( ) & 0o100 == 0o100 ) ) ;
73+ . and_then ( |file| {
74+ let old_mode = file. metadata ( ) ?. mode ( ) ;
75+ let is_executable = old_mode & 0o100 == 0o100 ;
76+ let exe_bit_flip_works_in_filesystem = {
77+ let toggled_exe_bit = old_mode ^ 0o100 ;
78+ match file. set_permissions ( PermissionsExt :: from_mode ( toggled_exe_bit) ) {
79+ Ok ( ( ) ) => toggled_exe_bit == file. metadata ( ) ?. mode ( ) ,
80+ Err ( err) if err. kind ( ) == std:: io:: ErrorKind :: PermissionDenied => false ,
81+ Err ( err) => return Err ( err) ,
82+ }
83+ } ;
84+ Ok ( is_executable && exe_bit_flip_works_in_filesystem)
85+ } ) ;
7186 std:: fs:: remove_file ( test_path) ?;
7287 res
7388 }
0 commit comments