Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Permissions

Your shared project directory needs three kinds of access: reading source files, writing build artifacts, and executing scripts. Every permission decision in POSIX ACLs ultimately reduces to a combination of these three bits. This page covers the types that model them and the algebraic structure that makes combining permissions predictable.

PosixPerm

PosixPerm represents the classic rwx permission triple as a 3-bit bitmask. Eight named constants cover every combination:

#![allow(unused)]
fn main() {
use posix_acls::PosixPerm;

assert_eq!(PosixPerm::NONE.to_string(), "---");
assert_eq!(PosixPerm::R.to_string(),    "r--");
assert_eq!(PosixPerm::RW.to_string(),   "rw-");
assert_eq!(PosixPerm::RWX.to_string(),  "rwx");

// Individual bit checks
assert!(PosixPerm::RW.read());
assert!(PosixPerm::RW.write());
assert!(!PosixPerm::RW.execute());
}

All eight constants: NONE, X, W, WX, R, RX, RW, RWX.

You can also construct from raw bits – only the low 3 bits are kept:

#![allow(unused)]
fn main() {
use posix_acls::PosixPerm;

let p = PosixPerm::new(0b101);
assert_eq!(p, PosixPerm::RX);
assert_eq!(p.bits(), 5);
}

Containment

contains checks whether one permission set is a superset of another – the fundamental operation behind access checks:

#![allow(unused)]
fn main() {
use posix_acls::PosixPerm;

assert!(PosixPerm::RWX.contains(PosixPerm::R));
assert!(PosixPerm::RW.contains(PosixPerm::RW));
assert!(!PosixPerm::R.contains(PosixPerm::W));
}

Masking

POSIX.1e defines an effective-rights mask that limits the permissions of named user and group entries. The mask_with method applies it:

#![allow(unused)]
fn main() {
use posix_acls::PosixPerm;

// Bob has rwx, but the mask is r-- -- effective permissions are r--
assert_eq!(PosixPerm::RWX.mask_with(PosixPerm::R), PosixPerm::R);

// The mask does not restrict the owner or other entries --
// those are applied directly in the access-check algorithm
assert_eq!(PosixPerm::RW.mask_with(PosixPerm::RX), PosixPerm::R);
}

Lattice algebra

PosixPerm implements the algebraic traits from acls_rs::algebra. The partial order is the subset relation on bits: R and W are incomparable, but both are less than RW.

#![allow(unused)]
fn main() {
use posix_acls::PosixPerm;
use acls_rs::algebra::{JoinSemilattice, MeetSemilattice, Monoid};

// Join (OR) is the least upper bound -- the least restrictive merge
assert_eq!(PosixPerm::R.join(PosixPerm::W), PosixPerm::RW);

// Meet (AND) is the greatest lower bound -- the most restrictive merge
assert_eq!(PosixPerm::RW.meet(PosixPerm::RX), PosixPerm::R);

// Identity element is NONE
assert_eq!(PosixPerm::RWX.join(PosixPerm::identity()), PosixPerm::RWX);
}

The partial order:

#![allow(unused)]
fn main() {
use posix_acls::PosixPerm;

assert!(PosixPerm::R < PosixPerm::RW);
assert!(PosixPerm::RWX > PosixPerm::R);

// R and W are incomparable -- neither is a subset of the other
assert_eq!(PosixPerm::R.partial_cmp(&PosixPerm::W), None);
}

These properties – commutativity, idempotence, associativity, and the absorption law – are verified by property-based tests using proptest. They guarantee that merging permissions from multiple sources (roles, group memberships, policy rules) always produces a well-defined result regardless of evaluation order.

TraitOperationMeaning
Semigroupcombine (OR)Merge two permission sets
Monoididentity (NONE)Empty permission set
JoinSemilatticejoin (OR)Least upper bound
MeetSemilatticemeet (AND)Greatest lower bound
BoundedJoinSemilatticebottom (NONE)Minimal element
BoundedMeetSemilatticetop (RWX)Maximal element

Parsing and display

PosixPerm implements Display and FromStr for the standard rwx string format:

#![allow(unused)]
fn main() {
use posix_acls::PosixPerm;

let p: PosixPerm = "rw-".parse().unwrap();
assert_eq!(p, PosixPerm::RW);

// Display produces the same format
assert_eq!(format!("{p}"), "rw-");
}

The parser also handles s, t, S, and T in the execute position (as produced by some getfacl implementations when SUID/SGID/sticky bits interact with the execute bit):

#![allow(unused)]
fn main() {
use posix_acls::PosixPerm;

// Lowercase 's'/'t' means execute IS set (plus the special bit)
let p: PosixPerm = "rws".parse().unwrap();
assert_eq!(p, PosixPerm::RWX);

// Uppercase 'S'/'T' means execute is NOT set (only the special bit)
let p: PosixPerm = "rwS".parse().unwrap();
assert_eq!(p, PosixPerm::RW);
}

SpecialBits

SpecialBits represents the three special Unix permission bits – SUID, SGID, and sticky – which occupy bits 11-9 of the 12-bit mode word. These are not part of the ACL entry system but are carried alongside the ACL for mode word conversion.

#![allow(unused)]
fn main() {
use posix_acls::perm::SpecialBits;

let bits = SpecialBits::SGID;
assert!(bits.sgid());
assert!(!bits.suid());
assert!(!bits.sticky());

// Display uses the getfacl # flags: format
assert_eq!(bits.to_string(), "-s-");
}

SGID on a directory is particularly relevant: it causes new files to inherit the directory’s owning group, which is often combined with default ACLs to enforce consistent group ownership in shared project directories.

Constants: NONE, STICKY, SGID, SUID. Construct from raw bits with SpecialBits::new(bits).

Next steps

You now know how individual permissions work. The next page introduces ACL tags and entries – the structures that attach permissions to specific users and groups – and the AclBuilder for constructing complete ACLs.

See Building ACLs.