use crate::entry::EntryKind;
#[must_use]
pub fn format_perms(mode: u32) -> String {
let bits = mode & 0o7777;
if bits & 0o7000 == 0 {
format!("{bits:03o}")
} else {
format!("{bits:04o}")
}
}
#[must_use]
pub const fn is_default(kind: EntryKind, mode: u32, umask: u32) -> bool {
let bits = mode & 0o7777;
if bits & 0o7000 != 0 {
return false;
}
let perm = bits & 0o0777;
let umask = umask & 0o0777;
match kind {
EntryKind::RegularFile => perm == 0o666 & !umask & 0o777,
EntryKind::Directory => perm == 0o777 & !umask & 0o777,
EntryKind::Symlink => perm == 0o755 || perm == 0o777,
EntryKind::CharDevice
| EntryKind::BlockDevice
| EntryKind::Fifo
| EntryKind::Socket
| EntryKind::Other => false,
}
}
#[cfg(test)]
mod tests {
use super::{EntryKind, format_perms, is_default};
#[test]
fn standard_permissions_render_three_digits() {
assert_eq!(format_perms(0o755), "755");
assert_eq!(format_perms(0o644), "644");
assert_eq!(format_perms(0o600), "600");
assert_eq!(format_perms(0o000), "000");
assert_eq!(format_perms(0o022), "022");
}
#[test]
fn sticky_setuid_setgid_render_four_digits() {
assert_eq!(format_perms(0o4755), "4755");
assert_eq!(format_perms(0o2755), "2755");
assert_eq!(format_perms(0o1777), "1777");
assert_eq!(format_perms(0o7777), "7777");
}
#[test]
fn ignores_file_type_bits() {
assert_eq!(format_perms(0o100_644), "644");
assert_eq!(format_perms(0o040_755), "755");
}
#[test]
fn is_default_matches_umask_022_defaults() {
assert!(is_default(EntryKind::RegularFile, 0o644, 0o022));
assert!(is_default(EntryKind::Directory, 0o755, 0o022));
assert!(is_default(EntryKind::Symlink, 0o755, 0o022));
assert!(is_default(EntryKind::Symlink, 0o777, 0o022));
}
#[test]
fn is_default_tracks_other_umasks() {
assert!(is_default(EntryKind::RegularFile, 0o600, 0o077));
assert!(is_default(EntryKind::Directory, 0o700, 0o077));
assert!(!is_default(EntryKind::RegularFile, 0o644, 0o077));
assert!(!is_default(EntryKind::Directory, 0o755, 0o077));
assert!(is_default(EntryKind::RegularFile, 0o664, 0o002));
assert!(is_default(EntryKind::Directory, 0o775, 0o002));
assert!(!is_default(EntryKind::RegularFile, 0o644, 0o002));
assert!(!is_default(EntryKind::Directory, 0o755, 0o002));
assert!(is_default(EntryKind::RegularFile, 0o666, 0));
assert!(is_default(EntryKind::Directory, 0o777, 0));
}
#[test]
fn is_default_strips_file_type_bits_from_mode() {
assert!(is_default(EntryKind::RegularFile, 0o100_644, 0o022));
assert!(is_default(EntryKind::Directory, 0o040_755, 0o022));
}
#[test]
fn is_default_rejects_anything_else() {
assert!(!is_default(EntryKind::RegularFile, 0o755, 0o022));
assert!(!is_default(EntryKind::RegularFile, 0o777, 0o022));
assert!(!is_default(EntryKind::Directory, 0o644, 0o022));
assert!(!is_default(EntryKind::Directory, 0o777, 0o022));
assert!(!is_default(EntryKind::Symlink, 0o644, 0o022));
assert!(!is_default(EntryKind::RegularFile, 0o4644, 0o022));
assert!(!is_default(EntryKind::Directory, 0o2755, 0o022));
assert!(!is_default(EntryKind::Directory, 0o1755, 0o022));
for k in [
EntryKind::CharDevice,
EntryKind::BlockDevice,
EntryKind::Fifo,
EntryKind::Socket,
EntryKind::Other,
] {
assert!(!is_default(k, 0o644, 0o022));
assert!(!is_default(k, 0o755, 0o022));
assert!(!is_default(k, 0o777, 0o022));
}
}
#[test]
fn is_default_ignores_high_bits_in_umask() {
assert!(is_default(EntryKind::RegularFile, 0o644, 0o7_777_022));
}
}