]>
Commit | Line | Data |
---|---|---|
a7813a04 XL |
1 | //! Tidy check to ensure that there are no binaries checked into the source tree |
2 | //! by accident. | |
3 | //! | |
4 | //! In the past we've accidentally checked in test binaries and such which add a | |
9fa01778 XL |
5 | //! huge amount of bloat to the Git history, so it's good to just ensure we |
6 | //! don't do that again. | |
a7813a04 | 7 | |
cdc7bbd5 | 8 | pub use os_impl::*; |
a7813a04 | 9 | |
9fa01778 | 10 | // All files are executable on Windows, so just check on Unix. |
a7813a04 | 11 | #[cfg(windows)] |
cdc7bbd5 XL |
12 | mod os_impl { |
13 | use std::path::Path; | |
14 | ||
15 | pub fn check_filesystem_support(_sources: &[&Path], _output: &Path) -> bool { | |
16 | return false; | |
17 | } | |
18 | ||
19 | pub fn check(_path: &Path, _bad: &mut bool) {} | |
20 | } | |
a7813a04 XL |
21 | |
22 | #[cfg(unix)] | |
cdc7bbd5 | 23 | mod os_impl { |
a7813a04 XL |
24 | use std::fs; |
25 | use std::os::unix::prelude::*; | |
cdc7bbd5 | 26 | use std::path::Path; |
dfeec247 | 27 | use std::process::{Command, Stdio}; |
a7813a04 | 28 | |
cdc7bbd5 XL |
29 | enum FilesystemSupport { |
30 | Supported, | |
31 | Unsupported, | |
32 | ReadOnlyFs, | |
33 | } | |
34 | ||
35 | use FilesystemSupport::*; | |
36 | ||
29967ef6 XL |
37 | fn is_executable(path: &Path) -> std::io::Result<bool> { |
38 | Ok(path.metadata()?.mode() & 0o111 != 0) | |
39 | } | |
40 | ||
cdc7bbd5 XL |
41 | pub fn check_filesystem_support(sources: &[&Path], output: &Path) -> bool { |
42 | // We want to avoid false positives on filesystems that do not support the | |
43 | // executable bit. This occurs on some versions of Window's linux subsystem, | |
44 | // for example. | |
45 | // | |
46 | // We try to create the temporary file first in the src directory, which is | |
47 | // the preferred location as it's most likely to be on the same filesystem, | |
48 | // and then in the output (`build`) directory if that fails. Sometimes we | |
49 | // see the source directory mounted as read-only which means we can't | |
50 | // readily create a file there to test. | |
51 | // | |
52 | // See #36706 and #74753 for context. | |
53 | ||
54 | fn check_dir(dir: &Path) -> FilesystemSupport { | |
55 | let path = dir.join("tidy-test-file"); | |
56 | match fs::File::create(&path) { | |
57 | Ok(file) => { | |
58 | let exec = is_executable(&path).unwrap_or(false); | |
59 | std::mem::drop(file); | |
60 | std::fs::remove_file(&path).expect("Deleted temp file"); | |
61 | // If the file is executable, then we assume that this | |
62 | // filesystem does not track executability, so skip this check. | |
63 | return if exec { Unsupported } else { Supported }; | |
64 | } | |
65 | Err(e) => { | |
66 | // If the directory is read-only or we otherwise don't have rights, | |
67 | // just don't run this check. | |
68 | // | |
69 | // 30 is the "Read-only filesystem" code at least in one CI | |
70 | // environment. | |
71 | if e.raw_os_error() == Some(30) { | |
72 | eprintln!("tidy: Skipping binary file check, read-only filesystem"); | |
73 | return ReadOnlyFs; | |
74 | } | |
75 | ||
76 | panic!("unable to create temporary file `{:?}`: {:?}", path, e); | |
77 | } | |
78 | }; | |
29967ef6 | 79 | } |
29967ef6 | 80 | |
cdc7bbd5 XL |
81 | for &source_dir in sources { |
82 | match check_dir(source_dir) { | |
83 | Unsupported => return false, | |
84 | ReadOnlyFs => { | |
85 | return match check_dir(output) { | |
86 | Supported => true, | |
87 | _ => false, | |
88 | }; | |
89 | } | |
90 | _ => {} | |
91 | } | |
9e0c209e | 92 | } |
cdc7bbd5 XL |
93 | |
94 | return true; | |
9e0c209e SL |
95 | } |
96 | ||
cdc7bbd5 XL |
97 | #[cfg(unix)] |
98 | pub fn check(path: &Path, bad: &mut bool) { | |
f2b60f7d FG |
99 | use std::ffi::OsStr; |
100 | ||
101 | const ALLOWED: &[&str] = &["configure", "x"]; | |
064997fb | 102 | |
cdc7bbd5 XL |
103 | crate::walk_no_read( |
104 | path, | |
064997fb FG |
105 | &mut |path| { |
106 | crate::filter_dirs(path) | |
107 | || path.ends_with("src/etc") | |
108 | // This is a list of directories that we almost certainly | |
109 | // don't need to walk. A future PR will likely want to | |
110 | // remove these in favor of crate::walk_no_read using git | |
111 | // ls-files to discover the paths we should check, which | |
112 | // would naturally ignore all of these directories. It's | |
113 | // also likely faster than walking the directory tree | |
114 | // directly (since git is just reading from a couple files | |
115 | // to produce the results). | |
116 | || path.ends_with("target") | |
117 | || path.ends_with("build") | |
118 | || path.ends_with(".git") | |
119 | }, | |
cdc7bbd5 XL |
120 | &mut |entry| { |
121 | let file = entry.path(); | |
f2b60f7d FG |
122 | let extension = file.extension(); |
123 | let scripts = ["py", "sh", "ps1"]; | |
124 | if scripts.into_iter().any(|e| extension == Some(OsStr::new(e))) { | |
cdc7bbd5 XL |
125 | return; |
126 | } | |
a7813a04 | 127 | |
cdc7bbd5 XL |
128 | if t!(is_executable(&file), file) { |
129 | let rel_path = file.strip_prefix(path).unwrap(); | |
130 | let git_friendly_path = rel_path.to_str().unwrap().replace("\\", "/"); | |
064997fb FG |
131 | |
132 | if ALLOWED.contains(&git_friendly_path.as_str()) { | |
133 | return; | |
134 | } | |
135 | ||
cdc7bbd5 XL |
136 | let output = Command::new("git") |
137 | .arg("ls-files") | |
138 | .arg(&git_friendly_path) | |
139 | .current_dir(path) | |
140 | .stderr(Stdio::null()) | |
141 | .output() | |
142 | .unwrap_or_else(|e| { | |
5e7ed085 | 143 | panic!("could not run git ls-files: {e}"); |
cdc7bbd5 XL |
144 | }); |
145 | let path_bytes = rel_path.as_os_str().as_bytes(); | |
146 | if output.status.success() && output.stdout.starts_with(path_bytes) { | |
147 | tidy_error!(bad, "binary checked into source: {}", file.display()); | |
148 | } | |
dfeec247 | 149 | } |
cdc7bbd5 XL |
150 | }, |
151 | ) | |
152 | } | |
a7813a04 | 153 | } |