rust/hg-core/src/checkexec.rs
author Pierre-Yves David <pierre-yves.david@octobus.net>
Wed, 06 Mar 2024 16:10:44 +0100
changeset 51534 767b62cb728e
parent 50377 e2c8b30ab4e7
permissions -rw-r--r--
branchcache: gather newly closed head in a dedicated set This is part of a series to more clearly split the update in two step. This will allow us to introduce a fast path during update in a future changeset.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
49894
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
     1
use std::fs;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
     2
use std::io;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
     3
use std::os::unix::fs::{MetadataExt, PermissionsExt};
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
     4
use std::path::Path;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
     5
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
     6
const EXECFLAGS: u32 = 0o111;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
     7
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
     8
fn is_executable(path: impl AsRef<Path>) -> Result<bool, io::Error> {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
     9
    let metadata = fs::metadata(path)?;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    10
    let mode = metadata.mode();
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    11
    Ok(mode & EXECFLAGS != 0)
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    12
}
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    13
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    14
fn make_executable(path: impl AsRef<Path>) -> Result<(), io::Error> {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    15
    let mode = fs::metadata(path.as_ref())?.mode();
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    16
    fs::set_permissions(
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    17
        path,
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    18
        fs::Permissions::from_mode((mode & 0o777) | EXECFLAGS),
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    19
    )?;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    20
    Ok(())
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    21
}
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    22
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    23
fn copy_mode(
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    24
    src: impl AsRef<Path>,
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    25
    dst: impl AsRef<Path>,
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    26
) -> Result<(), io::Error> {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    27
    let mode = match fs::symlink_metadata(src) {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    28
        Ok(metadata) => metadata.mode(),
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    29
        Err(e) if e.kind() == io::ErrorKind::NotFound =>
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    30
        // copymode in python has a more complicated handling of FileNotFound
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    31
        // error, which we don't need because all it does is applying
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    32
        // umask, which the OS already does when we mkdir.
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    33
        {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    34
            return Ok(())
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    35
        }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    36
        Err(e) => return Err(e),
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    37
    };
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    38
    fs::set_permissions(dst, fs::Permissions::from_mode(mode))?;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    39
    Ok(())
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    40
}
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    41
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    42
fn check_exec_impl(path: impl AsRef<Path>) -> Result<bool, io::Error> {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    43
    let basedir = path.as_ref().join(".hg");
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    44
    let cachedir = basedir.join("wcache");
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    45
    let storedir = basedir.join("store");
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    46
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    47
    if !cachedir.exists() {
49895
07792fd1837f doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49894
diff changeset
    48
        // we want to create the 'cache' directory, not the '.hg' one.
07792fd1837f doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49894
diff changeset
    49
        // Automatically creating '.hg' directory could silently spawn
07792fd1837f doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49894
diff changeset
    50
        // invalid Mercurial repositories. That seems like a bad idea.
49894
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    51
        fs::create_dir(&cachedir)
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    52
            .and_then(|()| {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    53
                if storedir.exists() {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    54
                    copy_mode(&storedir, &cachedir)
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    55
                } else {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    56
                    copy_mode(&basedir, &cachedir)
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    57
                }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    58
            })
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    59
            .ok();
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    60
    }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    61
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    62
    let leave_file: bool;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    63
    let checkdir: &Path;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    64
    let checkisexec = cachedir.join("checkisexec");
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    65
    let checknoexec = cachedir.join("checknoexec");
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    66
    if cachedir.is_dir() {
49895
07792fd1837f doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49894
diff changeset
    67
        // Check if both files already exist in cache and have correct
07792fd1837f doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49894
diff changeset
    68
        // permissions. if so, we assume that permissions work.
07792fd1837f doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49894
diff changeset
    69
        // If not, we delete the files and try again.
49894
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    70
        match is_executable(&checkisexec) {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    71
            Err(e) if e.kind() == io::ErrorKind::NotFound => (),
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    72
            Err(e) => return Err(e),
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    73
            Ok(is_exec) => {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    74
                if is_exec {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    75
                    let noexec_is_exec = match is_executable(&checknoexec) {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    76
                        Err(e) if e.kind() == io::ErrorKind::NotFound => {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    77
                            fs::write(&checknoexec, "")?;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    78
                            is_executable(&checknoexec)?
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    79
                        }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    80
                        Err(e) => return Err(e),
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    81
                        Ok(exec) => exec,
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    82
                    };
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    83
                    if !noexec_is_exec {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    84
                        // check-exec is exec and check-no-exec is not exec
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    85
                        return Ok(true);
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    86
                    }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    87
                    fs::remove_file(&checknoexec)?;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    88
                }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    89
                fs::remove_file(&checkisexec)?;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    90
            }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    91
        }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    92
        checkdir = &cachedir;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    93
        leave_file = true;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    94
    } else {
49895
07792fd1837f doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49894
diff changeset
    95
        // no cache directory (probably because .hg doesn't exist):
07792fd1837f doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49894
diff changeset
    96
        // check directly in `path` and don't leave the temp file behind
49894
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    97
        checkdir = path.as_ref();
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    98
        leave_file = false;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
    99
    };
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   100
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   101
    let tmp_file = tempfile::NamedTempFile::new_in(checkdir)?;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   102
    if !is_executable(tmp_file.path())? {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   103
        make_executable(tmp_file.path())?;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   104
        if is_executable(tmp_file.path())? {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   105
            if leave_file {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   106
                tmp_file.persist(checkisexec).ok();
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   107
            }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   108
            return Ok(true);
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   109
        }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   110
    }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   111
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   112
    Ok(false)
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   113
}
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   114
50377
e2c8b30ab4e7 rustdoc: wording for checkexec
Georges Racinet <georges.racinet@octobus.net>
parents: 50376
diff changeset
   115
/// This function is a Rust rewrite of the `checkexec` function from
e2c8b30ab4e7 rustdoc: wording for checkexec
Georges Racinet <georges.racinet@octobus.net>
parents: 50376
diff changeset
   116
/// `posix.py`.
e2c8b30ab4e7 rustdoc: wording for checkexec
Georges Racinet <georges.racinet@octobus.net>
parents: 50376
diff changeset
   117
///
e2c8b30ab4e7 rustdoc: wording for checkexec
Georges Racinet <georges.racinet@octobus.net>
parents: 50376
diff changeset
   118
/// Returns `true` if the filesystem supports execute permissions.
49894
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   119
pub fn check_exec(path: impl AsRef<Path>) -> bool {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   120
    check_exec_impl(path).unwrap_or(false)
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
   121
}