//! Access test functions.
use crate::fs::{access_impl, FollowSymlinks};
#[cfg(racy_asserts)]
use crate::fs::{access_unchecked, file_path};
use std::path::Path;
use std::{fs, io};
/// Access modes for use with [`DirExt::access`].
#[derive(Clone, Copy, Debug)]
pub struct AccessModes {
/// Is the object readable?
pub readable: bool,
/// Is the object writable?
pub writable: bool,
/// Is the object executable?
pub executable: bool,
}
/// Access modes for use with [`DirExt::access`].
#[derive(Clone, Copy, Debug)]
pub enum AccessType {
/// Test whether the named object is accessible in the given modes.
Access(AccessModes),
/// Test whether the named object exists.
Exists,
}
/// Canonicalize the given path, ensuring that the resolution of the path never
/// escapes the directory tree rooted at `start`.
#[cfg_attr(not(racy_asserts), allow(clippy::let_and_return))]
pub fn access(
start: &fs::File,
path: &Path,
type_: AccessType,
follow: FollowSymlinks,
) -> io::Result<()> {
// Call the underlying implementation.
let result = access_impl(start, path, type_, follow);
#[cfg(racy_asserts)]
let unchecked = access_unchecked(start, path, type_, follow);
#[cfg(racy_asserts)]
check_access(start, path, type_, follow, &result, &unchecked);
result
}
#[cfg(racy_asserts)]
#[allow(clippy::enum_glob_use)]
fn check_access(
start: &fs::File,
path: &Path,
_type_: AccessType,
_follow: FollowSymlinks,
result: &io::Result<()>,
unchecked: &io::Result<()>,
) {
use io::ErrorKind::*;
match (map_result(result), map_result(stat)) {
(Ok(()), Ok(())) => {}
(Err((PermissionDenied, message)), _) => {
// TODO: Check that access in the no-follow case got the right
// error.
}
(Err((kind, message)), Err((unchecked_kind, unchecked_message))) => {
assert_eq!(kind, unchecked_kind);
assert_eq!(
message,
unchecked_message,
"start='{:?}', path='{:?}'",
start,
path.display()
);
}
other => panic!(
"unexpected result from access start='{:?}', path='{}':\n{:#?}",
start,
path.display(),
other,
),
}
}