@@ -9,19 +9,56 @@ use std::path::Path;
9
9
10
10
// All files are executable on Windows, so just check on Unix.
11
11
#[ cfg( windows) ]
12
- pub fn check ( _path : & Path , _bad : & mut bool ) { }
12
+ pub fn check ( _path : & Path , _output : & Path , _bad : & mut bool ) { }
13
13
14
14
#[ cfg( unix) ]
15
- pub fn check ( path : & Path , bad : & mut bool ) {
15
+ pub fn check ( path : & Path , output : & Path , bad : & mut bool ) {
16
16
use std:: fs;
17
17
use std:: os:: unix:: prelude:: * ;
18
18
use std:: process:: { Command , Stdio } ;
19
19
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) ;
25
62
}
26
63
}
27
64
@@ -36,8 +73,7 @@ pub fn check(path: &Path, bad: &mut bool) {
36
73
return ;
37
74
}
38
75
39
- let metadata = t ! ( entry. metadata( ) , file) ;
40
- if metadata. mode ( ) & 0o111 != 0 {
76
+ if t ! ( is_executable( & file) , file) {
41
77
let rel_path = file. strip_prefix ( path) . unwrap ( ) ;
42
78
let git_friendly_path = rel_path. to_str ( ) . unwrap ( ) . replace ( "\\ " , "/" ) ;
43
79
let output = Command :: new ( "git" )
0 commit comments