posix_acl/
error.rs

1use crate::error::ACLError::{IoError, ValidationError};
2use acl_sys::{ACL_TYPE_ACCESS, ACL_TYPE_DEFAULT};
3use std::error::Error;
4use std::io::ErrorKind;
5use std::{fmt, io};
6
7/// Use a bit flag to track whether error was caused by read or write
8pub(crate) const FLAG_WRITE: u32 = 0x4000_0000;
9
10/// Error type from ACL operations. To distinguish different causes, use the [`kind()`](Self::kind)
11/// method.
12//
13// Perhaps an overkill, I could have used io::Error instead.
14// But now that I wrote this, might as well keep it. :)
15#[derive(Debug)]
16#[allow(clippy::upper_case_acronyms)]
17#[allow(clippy::module_name_repetitions)]
18pub enum ACLError {
19    /// Filesystem error while reading or writing ACL (file not found, permission denied, etc).
20    IoError(IoErrorDetail),
21    /// ACL is not valid and cannot be written.
22    ///
23    /// Unfortunately it is not possible to provide detailed reasons, but mainly it can mean:
24    /// * Required entries are missing (`UserObj`, `GroupObj`, `Mask` and `Other`).
25    /// * ACL contains entries that are not unique.
26    ValidationError(ValidationErrorDetail),
27}
28
29// Stores private fields for ACLError::IoError
30#[derive(Debug)]
31pub struct IoErrorDetail {
32    err: io::Error,
33    flags: u32,
34}
35
36// Currently an empty struct, created for future extensibility
37#[derive(Debug)]
38pub struct ValidationErrorDetail {
39    _private: (),
40}
41
42impl Error for ACLError {
43    /// Get underlying [`std::io::Error`] value.
44    fn source(&self) -> Option<&(dyn Error + 'static)> {
45        match self {
46            ValidationError(..) => None,
47            IoError(IoErrorDetail { ref err, .. }) => Some(err),
48        }
49    }
50}
51
52impl fmt::Display for ACLError {
53    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54        match self {
55            IoError(IoErrorDetail { flags, err }) => write!(
56                f,
57                "Error {} {}: {}",
58                op_display(*flags),
59                type_display(*flags),
60                err
61            ),
62            ValidationError(_) => write!(f, "ACL failed validation"),
63        }
64    }
65}
66
67impl ACLError {
68    /// Get a general category of error, as [`std::io::ErrorKind`].
69    /// Validation errors get returned as `InvalidData`.
70    ///
71    /// ```
72    /// use posix_acl::PosixACL;
73    /// use std::io::ErrorKind;
74    /// let err = PosixACL::read_acl("/tmp/this-file-does-not-exist").unwrap_err();
75    /// assert_eq!(err.kind(), ErrorKind::NotFound);
76    /// ```
77    #[must_use]
78    pub fn kind(&self) -> ErrorKind {
79        match self {
80            ValidationError(_) => ErrorKind::InvalidData,
81            IoError(IoErrorDetail { ref err, .. }) => err.kind(),
82        }
83    }
84
85    /// Get reference to underlying `std::io::Error` that occurred, if any.
86    ///
87    /// ```
88    /// use posix_acl::PosixACL;
89    /// use std::io::ErrorKind;
90    /// let err = PosixACL::read_acl("/tmp/this-file-does-not-exist").unwrap_err();
91    /// assert_eq!(err.as_io_error().unwrap().raw_os_error().unwrap(), libc::ENOENT);
92    /// ```
93    #[must_use]
94    pub fn as_io_error(&self) -> Option<&io::Error> {
95        match self {
96            ValidationError(_) => None,
97            IoError(IoErrorDetail { ref err, .. }) => Some(err),
98        }
99    }
100
101    pub(crate) fn last_os_error(flags: u32) -> ACLError {
102        IoError(IoErrorDetail {
103            err: io::Error::last_os_error(),
104            flags,
105        })
106    }
107
108    pub(crate) fn validation_error() -> ACLError {
109        ValidationError(ValidationErrorDetail { _private: () })
110    }
111}
112
113/// `acl_type_t` string representation for error messages
114pub(crate) fn op_display(flags: u32) -> &'static str {
115    if flags & FLAG_WRITE == FLAG_WRITE {
116        "writing"
117    } else {
118        "reading"
119    }
120}
121
122/// `acl_type_t` string representation for error messages
123pub(crate) fn type_display(flags: u32) -> &'static str {
124    let flags = flags & !FLAG_WRITE;
125    match flags {
126        ACL_TYPE_ACCESS => "ACL",
127        ACL_TYPE_DEFAULT => "default ACL",
128        _ => panic!("Invalid flags"),
129    }
130}