@@ -9,19 +9,56 @@ use std::path::Path;
99
1010// All files are executable on Windows, so just check on Unix.
1111#[ cfg( windows) ]
12- pub fn check ( _path : & Path , _bad : & mut bool ) { }
12+ pub fn check ( _path : & Path , _output : & Path , _bad : & mut bool ) { }
1313
1414#[ cfg( unix) ]
15- pub fn check ( path : & Path , bad : & mut bool ) {
15+ pub fn check ( path : & Path , output : & Path , bad : & mut bool ) {
1616 use std:: fs;
1717 use std:: os:: unix:: prelude:: * ;
1818 use std:: process:: { Command , Stdio } ;
1919
20- if let Ok ( contents) = fs:: read_to_string ( "/proc/version" ) {
21- // Probably on Windows Linux Subsystem or Docker via VirtualBox,
22- // all files will be marked as executable, so skip checking.
23- if contents. contains ( "Microsoft" ) || contents. contains ( "boot2docker" ) {
24- return ;
20+ fn is_executable ( path : & Path ) -> std:: io:: Result < bool > {
21+ Ok ( path. metadata ( ) ?. mode ( ) & 0o111 != 0 )
22+ }
23+
24+ // We want to avoid false positives on filesystems that do not support the
25+ // executable bit. This occurs on some versions of Window's linux subsystem,
26+ // for example.
27+ //
28+ // We try to create the temporary file first in the src directory, which is
29+ // the preferred location as it's most likely to be on the same filesystem,
30+ // and then in the output (`build`) directory if that fails. Sometimes we
31+ // see the source directory mounted as read-only which means we can't
32+ // readily create a file there to test.
33+ //
34+ // See #36706 and #74753 for context.
35+ let mut temp_path = path. join ( "tidy-test-file" ) ;
36+ match fs:: File :: create ( & temp_path) . or_else ( |_| {
37+ temp_path = output. join ( "tidy-test-file" ) ;
38+ fs:: File :: create ( & temp_path)
39+ } ) {
40+ Ok ( file) => {
41+ let exec = is_executable ( & temp_path) . unwrap_or ( false ) ;
42+ std:: mem:: drop ( file) ;
43+ std:: fs:: remove_file ( & temp_path) . expect ( "Deleted temp file" ) ;
44+ if exec {
45+ // If the file is executable, then we assume that this
46+ // filesystem does not track executability, so skip this check.
47+ return ;
48+ }
49+ }
50+ Err ( e) => {
51+ // If the directory is read-only or we otherwise don't have rights,
52+ // just don't run this check.
53+ //
54+ // 30 is the "Read-only filesystem" code at least in one CI
55+ // environment.
56+ if e. raw_os_error ( ) == Some ( 30 ) {
57+ eprintln ! ( "tidy: Skipping binary file check, read-only filesystem" ) ;
58+ return ;
59+ }
60+
61+ panic ! ( "unable to create temporary file `{:?}`: {:?}" , temp_path, e) ;
2562 }
2663 }
2764
@@ -36,8 +73,7 @@ pub fn check(path: &Path, bad: &mut bool) {
3673 return ;
3774 }
3875
39- let metadata = t ! ( entry. metadata( ) , file) ;
40- if metadata. mode ( ) & 0o111 != 0 {
76+ if t ! ( is_executable( & file) , file) {
4177 let rel_path = file. strip_prefix ( path) . unwrap ( ) ;
4278 let git_friendly_path = rel_path. to_str ( ) . unwrap ( ) . replace ( "\\ " , "/" ) ;
4379 let output = Command :: new ( "git" )
0 commit comments