##// END OF EJS Templates
rhg: implement checkexec to support weird filesystems...
Arseniy Alekseyev -
r50789:678588b0 default
parent child Browse files
Show More
@@ -0,0 +1,111 b''
1 use std::fs;
2 use std::io;
3 use std::os::unix::fs::{MetadataExt, PermissionsExt};
4 use std::path::Path;
5
6 // This is a rust rewrite of [checkexec] function from [posix.py]
7
8 const EXECFLAGS: u32 = 0o111;
9
10 fn is_executable(path: impl AsRef<Path>) -> Result<bool, io::Error> {
11 let metadata = fs::metadata(path)?;
12 let mode = metadata.mode();
13 Ok(mode & EXECFLAGS != 0)
14 }
15
16 fn make_executable(path: impl AsRef<Path>) -> Result<(), io::Error> {
17 let mode = fs::metadata(path.as_ref())?.mode();
18 fs::set_permissions(
19 path,
20 fs::Permissions::from_mode((mode & 0o777) | EXECFLAGS),
21 )?;
22 Ok(())
23 }
24
25 fn copy_mode(
26 src: impl AsRef<Path>,
27 dst: impl AsRef<Path>,
28 ) -> Result<(), io::Error> {
29 let mode = match fs::symlink_metadata(src) {
30 Ok(metadata) => metadata.mode(),
31 Err(e) if e.kind() == io::ErrorKind::NotFound =>
32 // copymode in python has a more complicated handling of FileNotFound
33 // error, which we don't need because all it does is applying
34 // umask, which the OS already does when we mkdir.
35 {
36 return Ok(())
37 }
38 Err(e) => return Err(e),
39 };
40 fs::set_permissions(dst, fs::Permissions::from_mode(mode))?;
41 Ok(())
42 }
43
44 fn check_exec_impl(path: impl AsRef<Path>) -> Result<bool, io::Error> {
45 let basedir = path.as_ref().join(".hg");
46 let cachedir = basedir.join("wcache");
47 let storedir = basedir.join("store");
48
49 if !cachedir.exists() {
50 fs::create_dir(&cachedir)
51 .and_then(|()| {
52 if storedir.exists() {
53 copy_mode(&storedir, &cachedir)
54 } else {
55 copy_mode(&basedir, &cachedir)
56 }
57 })
58 .ok();
59 }
60
61 let leave_file: bool;
62 let checkdir: &Path;
63 let checkisexec = cachedir.join("checkisexec");
64 let checknoexec = cachedir.join("checknoexec");
65 if cachedir.is_dir() {
66 match is_executable(&checkisexec) {
67 Err(e) if e.kind() == io::ErrorKind::NotFound => (),
68 Err(e) => return Err(e),
69 Ok(is_exec) => {
70 if is_exec {
71 let noexec_is_exec = match is_executable(&checknoexec) {
72 Err(e) if e.kind() == io::ErrorKind::NotFound => {
73 fs::write(&checknoexec, "")?;
74 is_executable(&checknoexec)?
75 }
76 Err(e) => return Err(e),
77 Ok(exec) => exec,
78 };
79 if !noexec_is_exec {
80 // check-exec is exec and check-no-exec is not exec
81 return Ok(true);
82 }
83 fs::remove_file(&checknoexec)?;
84 }
85 fs::remove_file(&checkisexec)?;
86 }
87 }
88 checkdir = &cachedir;
89 leave_file = true;
90 } else {
91 checkdir = path.as_ref();
92 leave_file = false;
93 };
94
95 let tmp_file = tempfile::NamedTempFile::new_in(checkdir)?;
96 if !is_executable(tmp_file.path())? {
97 make_executable(tmp_file.path())?;
98 if is_executable(tmp_file.path())? {
99 if leave_file {
100 tmp_file.persist(checkisexec).ok();
101 }
102 return Ok(true);
103 }
104 }
105
106 Ok(false)
107 }
108
109 pub fn check_exec(path: impl AsRef<Path>) -> bool {
110 check_exec_impl(path).unwrap_or(false)
111 }
@@ -30,6 +30,7 b' pub mod matchers;'
30 pub mod repo;
30 pub mod repo;
31 pub mod revlog;
31 pub mod revlog;
32 pub use revlog::*;
32 pub use revlog::*;
33 pub mod checkexec;
33 pub mod config;
34 pub mod config;
34 pub mod lock;
35 pub mod lock;
35 pub mod logging;
36 pub mod logging;
@@ -254,10 +254,10 b' pub fn run(invocation: &crate::CliInvoca'
254
254
255 let mut dmap = repo.dirstate_map_mut()?;
255 let mut dmap = repo.dirstate_map_mut()?;
256
256
257 let check_exec = hg::checkexec::check_exec(repo.working_directory_path());
258
257 let options = StatusOptions {
259 let options = StatusOptions {
258 // we're currently supporting file systems with exec flags only
260 check_exec,
259 // anyway
260 check_exec: true,
261 list_clean: display_states.clean,
261 list_clean: display_states.clean,
262 list_unknown: display_states.unknown,
262 list_unknown: display_states.unknown,
263 list_ignored: display_states.ignored,
263 list_ignored: display_states.ignored,
@@ -312,6 +312,7 b' pub fn run(invocation: &crate::CliInvoca'
312 unsure_is_modified(
312 unsure_is_modified(
313 working_directory_vfs,
313 working_directory_vfs,
314 store_vfs,
314 store_vfs,
315 check_exec,
315 &manifest,
316 &manifest,
316 &to_check.path,
317 &to_check.path,
317 )
318 )
@@ -554,6 +555,7 b" impl DisplayStatusPaths<'_> {"
554 fn unsure_is_modified(
555 fn unsure_is_modified(
555 working_directory_vfs: hg::vfs::Vfs,
556 working_directory_vfs: hg::vfs::Vfs,
556 store_vfs: hg::vfs::Vfs,
557 store_vfs: hg::vfs::Vfs,
558 check_exec: bool,
557 manifest: &Manifest,
559 manifest: &Manifest,
558 hg_path: &HgPath,
560 hg_path: &HgPath,
559 ) -> Result<bool, HgError> {
561 ) -> Result<bool, HgError> {
@@ -561,20 +563,32 b' fn unsure_is_modified('
561 let fs_path = hg_path_to_path_buf(hg_path).expect("HgPath conversion");
563 let fs_path = hg_path_to_path_buf(hg_path).expect("HgPath conversion");
562 let fs_metadata = vfs.symlink_metadata(&fs_path)?;
564 let fs_metadata = vfs.symlink_metadata(&fs_path)?;
563 let is_symlink = fs_metadata.file_type().is_symlink();
565 let is_symlink = fs_metadata.file_type().is_symlink();
566
567 let entry = manifest
568 .find_by_path(hg_path)?
569 .expect("ambgious file not in p1");
570
564 // TODO: Also account for `FALLBACK_SYMLINK` and `FALLBACK_EXEC` from the
571 // TODO: Also account for `FALLBACK_SYMLINK` and `FALLBACK_EXEC` from the
565 // dirstate
572 // dirstate
566 let fs_flags = if is_symlink {
573 let fs_flags = if is_symlink {
567 Some(b'l')
574 Some(b'l')
568 } else if has_exec_bit(&fs_metadata) {
575 } else if check_exec && has_exec_bit(&fs_metadata) {
569 Some(b'x')
576 Some(b'x')
570 } else {
577 } else {
571 None
578 None
572 };
579 };
573
580
574 let entry = manifest
581 let entry_flags = if check_exec {
575 .find_by_path(hg_path)?
582 entry.flags
576 .expect("ambgious file not in p1");
583 } else {
577 if entry.flags != fs_flags {
584 if entry.flags == Some(b'x') {
585 None
586 } else {
587 entry.flags
588 }
589 };
590
591 if entry_flags != fs_flags {
578 return Ok(true);
592 return Ok(true);
579 }
593 }
580 let filelog = hg::filelog::Filelog::open_vfs(&store_vfs, hg_path)?;
594 let filelog = hg::filelog::Filelog::open_vfs(&store_vfs, hg_path)?;
General Comments 0
You need to be logged in to leave comments. Login now