cap_primitives/fs/
access.rs

1//! Access test functions.
2
3use crate::fs::{access_impl, FollowSymlinks};
4#[cfg(racy_asserts)]
5use crate::fs::{access_unchecked, file_path};
6use std::path::Path;
7use std::{fs, io};
8
9/// Access modes for use with [`DirExt::access`].
10#[derive(Clone, Copy, Debug)]
11pub struct AccessModes {
12    /// Is the object readable?
13    pub readable: bool,
14    /// Is the object writable?
15    pub writable: bool,
16    /// Is the object executable?
17    pub executable: bool,
18}
19
20/// Access modes for use with [`DirExt::access`].
21#[derive(Clone, Copy, Debug)]
22pub enum AccessType {
23    /// Test whether the named object is accessible in the given modes.
24    Access(AccessModes),
25
26    /// Test whether the named object exists.
27    Exists,
28}
29
30/// Canonicalize the given path, ensuring that the resolution of the path never
31/// escapes the directory tree rooted at `start`.
32#[cfg_attr(not(racy_asserts), allow(clippy::let_and_return))]
33pub fn access(
34    start: &fs::File,
35    path: &Path,
36    type_: AccessType,
37    follow: FollowSymlinks,
38) -> io::Result<()> {
39    // Call the underlying implementation.
40    let result = access_impl(start, path, type_, follow);
41
42    #[cfg(racy_asserts)]
43    let unchecked = access_unchecked(start, path, type_, follow);
44
45    #[cfg(racy_asserts)]
46    check_access(start, path, type_, follow, &result, &unchecked);
47
48    result
49}
50
51#[cfg(racy_asserts)]
52#[allow(clippy::enum_glob_use)]
53fn check_access(
54    start: &fs::File,
55    path: &Path,
56    _type_: AccessType,
57    _follow: FollowSymlinks,
58    result: &io::Result<()>,
59    unchecked: &io::Result<()>,
60) {
61    use io::ErrorKind::*;
62
63    match (map_result(result), map_result(stat)) {
64        (Ok(()), Ok(())) => {}
65
66        (Err((PermissionDenied, message)), _) => {
67            // TODO: Check that access in the no-follow case got the right
68            // error.
69        }
70
71        (Err((kind, message)), Err((unchecked_kind, unchecked_message))) => {
72            assert_eq!(kind, unchecked_kind);
73            assert_eq!(
74                message,
75                unchecked_message,
76                "start='{:?}', path='{:?}'",
77                start,
78                path.display()
79            );
80        }
81
82        other => panic!(
83            "unexpected result from access start='{:?}', path='{}':\n{:#?}",
84            start,
85            path.display(),
86            other,
87        ),
88    }
89}