]> git.lizzy.rs Git - rust.git/blob - src/tools/tidy/src/bins.rs
Merge commit 'd0cf3481a84e3aa68c2f185c460e282af36ebc42' into clippyup
[rust.git] / src / tools / tidy / src / bins.rs
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
5 //! huge amount of bloat to the Git history, so it's good to just ensure we
6 //! don't do that again.
7
8 pub use os_impl::*;
9
10 // All files are executable on Windows, so just check on Unix.
11 #[cfg(windows)]
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 }
21
22 #[cfg(unix)]
23 mod os_impl {
24     use std::fs;
25     use std::os::unix::prelude::*;
26     use std::path::Path;
27     use std::process::{Command, Stdio};
28
29     enum FilesystemSupport {
30         Supported,
31         Unsupported,
32         ReadOnlyFs,
33     }
34
35     use FilesystemSupport::*;
36
37     fn is_executable(path: &Path) -> std::io::Result<bool> {
38         Ok(path.metadata()?.mode() & 0o111 != 0)
39     }
40
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             };
79         }
80
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             }
92         }
93
94         return true;
95     }
96
97     #[cfg(unix)]
98     pub fn check(path: &Path, bad: &mut bool) {
99         crate::walk_no_read(
100             path,
101             &mut |path| crate::filter_dirs(path) || path.ends_with("src/etc"),
102             &mut |entry| {
103                 let file = entry.path();
104                 let filename = file.file_name().unwrap().to_string_lossy();
105                 let extensions = [".py", ".sh"];
106                 if extensions.iter().any(|e| filename.ends_with(e)) {
107                     return;
108                 }
109
110                 if t!(is_executable(&file), file) {
111                     let rel_path = file.strip_prefix(path).unwrap();
112                     let git_friendly_path = rel_path.to_str().unwrap().replace("\\", "/");
113                     let output = Command::new("git")
114                         .arg("ls-files")
115                         .arg(&git_friendly_path)
116                         .current_dir(path)
117                         .stderr(Stdio::null())
118                         .output()
119                         .unwrap_or_else(|e| {
120                             panic!("could not run git ls-files: {e}");
121                         });
122                     let path_bytes = rel_path.as_os_str().as_bytes();
123                     if output.status.success() && output.stdout.starts_with(path_bytes) {
124                         tidy_error!(bad, "binary checked into source: {}", file.display());
125                     }
126                 }
127             },
128         )
129     }
130 }