asterinas/kernel/src/fs/utils/inode_mode.rs

277 lines
6.7 KiB
Rust

// SPDX-License-Identifier: MPL-2.0
use aster_systree::SysPerms;
use crate::prelude::*;
bitflags! {
pub struct InodeMode: u16 {
/// set-user-ID
const S_ISUID = 0o4000;
/// set-group-ID
const S_ISGID = 0o2000;
/// sticky bit
const S_ISVTX = 0o1000;
/// read by owner
const S_IRUSR = 0o0400;
/// write by owner
const S_IWUSR = 0o0200;
/// execute/search by owner
const S_IXUSR = 0o0100;
/// read by group
const S_IRGRP = 0o0040;
/// write by group
const S_IWGRP = 0o0020;
/// execute/search by group
const S_IXGRP = 0o0010;
/// read by others
const S_IROTH = 0o0004;
/// write by others
const S_IWOTH = 0o0002;
/// execute/search by others
const S_IXOTH = 0o0001;
}
}
impl InodeMode {
pub fn is_owner_readable(&self) -> bool {
self.contains(Self::S_IRUSR)
}
pub fn is_owner_writable(&self) -> bool {
self.contains(Self::S_IWUSR)
}
pub fn is_owner_executable(&self) -> bool {
self.contains(Self::S_IXUSR)
}
pub fn is_group_readable(&self) -> bool {
self.contains(Self::S_IRGRP)
}
pub fn is_group_writable(&self) -> bool {
self.contains(Self::S_IWGRP)
}
pub fn is_group_executable(&self) -> bool {
self.contains(Self::S_IXGRP)
}
pub fn is_other_readable(&self) -> bool {
self.contains(Self::S_IROTH)
}
pub fn is_other_writable(&self) -> bool {
self.contains(Self::S_IWOTH)
}
pub fn is_other_executable(&self) -> bool {
self.contains(Self::S_IXOTH)
}
#[expect(dead_code)]
pub fn has_sticky_bit(&self) -> bool {
self.contains(Self::S_ISVTX)
}
pub fn has_set_uid(&self) -> bool {
self.contains(Self::S_ISUID)
}
pub fn has_set_gid(&self) -> bool {
self.contains(Self::S_ISGID)
}
}
impl From<SysPerms> for InodeMode {
fn from(value: SysPerms) -> Self {
InodeMode::from_bits_truncate(value.bits())
}
}
/// Changes an inode mode.
///
/// # Syntax
///
/// If you are familiar with the `chmod` tool, the syntax of the `chmod` macro
/// should appear natural and self-explaining.
///
/// The `chmod` macro takes two or more arguments. The first argument is an
/// initial value of the inode mode. The rest arguments are one or more
/// symbolic mode, each of which dictates how the current value of the inode
/// mode shall be changed.
///
/// The format of a symbolic mode is as follows:
///
/// ```
/// [ugoa][+-=][perms]
/// ```
///
/// where `[perms]` is either zero or more letters from `rwx`.
///
/// The user part, `[ugoa]`,
/// controls the target users whose access to the file will be changed:
/// * `u`: the user who owns it;
/// * `g`: other users in the file owner's group;
/// * `o`: other users not in the file owner's group;
/// * `a`: all users.
///
/// The operator part, `[+-=]`,
/// indicates how the file permission bits are changed:
/// * `+`: `[perms]` are added to the target users' permissions;
/// * `-`: `[perms]` are removed from the target users' permissions;
/// * `=`: `[perms]` are set to the target users' permissions.
///
/// The permission part, `[perms]`, is a combination of `rwx`.
/// * `r`: the read permission;
/// * `w`: the write permission;
/// * `x`: the execute/search permission.
///
/// # Examples
///
/// ```
/// // Add the read and write permissions to everyone
/// let mode0 = mkmod!(a+rw);
/// assert!(mode0.bits() == 0o666);
///
/// // Add the execute permissions to the owner user
/// let mode1 = chmod!(mode0, u+x);
/// assert!(mode1.bits() == 0o766);
///
/// // Combine the above two steps into one invocation
/// let mode2 = mkmod!(a+rw, u+x);
/// assert!(mode2.bits() == 0o766);
/// ```
macro_rules! chmod {
($mode:expr) => { $mode };
($mode:expr, $who:ident + $perms:ident $(, $($rest:tt)*)?) => {
$crate::fs::utils::chmod!(@apply $mode, $who, '+', $perms $(, $($rest)*)?)
};
($mode:expr, $who:ident - $perms:ident $(, $($rest:tt)*)?) => {
$crate::fs::utils::chmod!(@apply $mode, $who, '-', $perms $(, $($rest)*)?)
};
($mode:expr, $who:ident = $perms:ident $(, $($rest:tt)*)?) => {
$crate::fs::utils::chmod!(@apply $mode, $who, '=', $perms $(, $($rest)*)?)
};
($mode:expr, $who:ident = $(, $($rest:tt)*)?) => {
$crate::fs::utils::chmod!(@apply $mode, $who, '=', none $(, $($rest)*)?)
};
(@apply $mode:expr, $who:ident, $op:expr, $perms:ident $(, $($rest:tt)*)?) => {{
let mask = $crate::fs::utils::who_and_perms_to_mask!($who, $perms);
let new_mode = match $op {
'+' => $mode | mask,
'-' => $mode & !mask,
'=' => ($mode & !$crate::fs::utils::who_and_perms_to_mask!($who, rwx)) | mask,
_ => unreachable!(),
};
$crate::fs::utils::chmod!(new_mode $(, $($rest)*)?)
}};
}
/// Makes an inode mode.
///
/// `mkmod` is equivalent to `chmod` with the first argument set to
/// `InodeMode::empty()`. See [`chmod`] for details.
macro_rules! mkmod {
($($args:tt)*) => {
$crate::fs::utils::chmod!($crate::fs::utils::InodeMode::empty(), $($args)*)
};
}
macro_rules! who_and_perms_to_mask {
($who:ident, $perms:ident) => {
$crate::fs::utils::InodeMode::from_bits_truncate(
$crate::fs::utils::who_to_mask!($who) & $crate::fs::utils::perms_to_mask!($perms),
)
};
}
macro_rules! who_to_mask {
(u) => {
0o700
};
(g) => {
0o070
};
(o) => {
0o007
};
(ug) => {
0o770
};
(uo) => {
0o707
};
(go) => {
0o077
};
(a) => {
0o777
};
(ugo) => {
0o777
};
}
macro_rules! perms_to_mask {
(none) => {
0
};
(r) => {
0o444
};
(w) => {
0o222
};
(x) => {
0o111
};
(rw) => {
0o666
};
(rx) => {
0o555
};
(wx) => {
0o333
};
(rwx) => {
0o777
};
}
pub(crate) use chmod;
pub(crate) use mkmod;
pub(crate) use perms_to_mask;
pub(crate) use who_and_perms_to_mask;
pub(crate) use who_to_mask;
#[cfg(ktest)]
mod test {
use ostd::prelude::*;
#[ktest]
fn test_mkmod_and_chmod() {
let mode0 = mkmod!(a+rw);
assert!(mode0.bits() == 0o666);
let mode1 = chmod!(mode0, u+x);
assert!(mode1.bits() == 0o766);
let mode2 = mkmod!(a+rw, u+x);
assert!(mode2.bits() == 0o766);
let mode3 = chmod!(mode2, ug-wx);
assert!(mode3.bits() == 0o446);
let mode4 = chmod!(mode3, o=rx);
assert!(mode4.bits() == 0o445);
let mode5 = chmod!(mode4, u=, g=, o=);
assert!(mode5.bits() == 0o000);
}
}