Parsing and Generation
Your ACLs need to move between Rust code and the outside world. The most
common external format is the text output of getfacl(1), the standard
POSIX tool for displaying ACLs. posix-acls can parse this format into
PosixAcl values and generate it back, with round-trip fidelity.
The getfacl format
A getfacl text block looks like this:
# owner: alice
# group: devs
# flags: -s-
user::rwx
user:bob:rw-
group::r-x
group:ops:r--
mask::rwx
other::r--
default:user::rwx
default:group::r-x
default:other::r--
Comment lines (#) carry metadata: owner name, owning group, and optional
flags (SUID/SGID/sticky). Entry lines use the tag:qualifier:perms syntax.
Default entries are prefixed with default:.
Parsing
parse::<GetfaclParser>(text) converts a getfacl text block into a
PosixAcl:
#![allow(unused)]
fn main() {
use posix_acls::parser::{parse, GetfaclParser};
let text = "\
owner: alice
group: devs
user::rwx
user:bob:rw-
group::r-x
mask::rwx
other::r--
";
let acl = parse::<GetfaclParser>(text).unwrap();
assert_eq!(acl.owner(), "alice");
assert_eq!(acl.owning_group(), "devs");
assert!(acl.is_extended());
}
The parser validates the ACL structurally after parsing – it delegates to
PosixAcl::is_valid() so there is a single authoritative validation path.
If the text is syntactically valid but structurally invalid (e.g., named
entries without a mask), you get a ParseError rather than a silently
broken PosixAcl.
Parse errors
ParseError covers seven failure modes:
| Variant | Cause |
|---|---|
EmptyInput | The input string is empty or all whitespace |
InvalidTagSyntax | A tag name is not user, group, mask, or other |
InvalidPermString | A permission field is not a valid rwx string |
MissingRequiredEntry | A mandatory tag (user::, group::, other::) is absent |
DuplicateEntry | The same named entry appears more than once |
MissingMask | Named entries present but no mask:: |
SpuriousMask | A mask:: entry with no named entries |
#![allow(unused)]
fn main() {
use posix_acls::parser::{parse, GetfaclParser, ParseError};
// Named entry without a mask
let text = "user::rwx\nuser:bob:rw-\ngroup::r-x\nother::r--\n";
assert_eq!(parse::<GetfaclParser>(text), Err(ParseError::MissingMask));
// Empty input
assert_eq!(parse::<GetfaclParser>(" \n "), Err(ParseError::EmptyInput));
}
Generation
generate::<GetfaclGenerator>(acl) produces the getfacl text format from
a PosixAcl. Entries are emitted in canonical POSIX order: UserObj,
named users (sorted alphabetically), GroupObj, named groups (sorted),
Mask, Other:
#![allow(unused)]
fn main() {
use posix_acls::{AclBuilder, PosixPerm};
use posix_acls::generator::{generate, GetfaclGenerator};
let acl = AclBuilder::new("alice", "devs")
.owner_perms(PosixPerm::RWX)
.owning_group_perms(PosixPerm::RX)
.other_perms(PosixPerm::NONE)
.add_user("zoe", PosixPerm::R)
.add_user("bob", PosixPerm::RW)
.build();
let text = generate::<GetfaclGenerator>(&acl).unwrap();
// Named users are sorted alphabetically
let bob_pos = text.find("user:bob:").unwrap();
let zoe_pos = text.find("user:zoe:").unwrap();
assert!(bob_pos < zoe_pos);
}
The generator validates the ACL before producing output. If is_valid()
fails, you get a GenerateError::InvalidAcl.
Default entries are emitted after access entries with the default: prefix:
#![allow(unused)]
fn main() {
use posix_acls::{AclBuilder, PosixPerm};
use posix_acls::generator::{generate, GetfaclGenerator};
let acl = AclBuilder::new("alice", "devs")
.owner_perms(PosixPerm::RWX)
.owning_group_perms(PosixPerm::RX)
.other_perms(PosixPerm::NONE)
.default_owner_perms(PosixPerm::RWX)
.default_owning_group_perms(PosixPerm::RX)
.default_other_perms(PosixPerm::NONE)
.build();
let text = generate::<GetfaclGenerator>(&acl).unwrap();
assert!(text.contains("default:user::rwx"));
assert!(text.contains("default:group::r-x"));
}
Special bits are emitted as a # flags: comment line when any are set:
#![allow(unused)]
fn main() {
use posix_acls::{AclBuilder, PosixPerm};
use posix_acls::perm::SpecialBits;
use posix_acls::generator::{generate, GetfaclGenerator};
let acl = AclBuilder::new("alice", "devs")
.owner_perms(PosixPerm::RWX)
.owning_group_perms(PosixPerm::RX)
.other_perms(PosixPerm::R)
.special_bits(SpecialBits::SGID)
.build();
let text = generate::<GetfaclGenerator>(&acl).unwrap();
assert!(text.contains("# flags: -s-"));
}
Round-trip fidelity
Parse-generate-parse round-trips preserve all structural information:
#![allow(unused)]
fn main() {
use posix_acls::{AclBuilder, PosixPerm};
use posix_acls::perm::SpecialBits;
use posix_acls::parser::{parse, GetfaclParser};
use posix_acls::generator::{generate, GetfaclGenerator};
let original = AclBuilder::new("alice", "devs")
.owner_perms(PosixPerm::RWX)
.owning_group_perms(PosixPerm::RX)
.other_perms(PosixPerm::R)
.add_user("bob", PosixPerm::RW)
.add_group("ops", PosixPerm::R)
.special_bits(SpecialBits::SGID)
.default_owner_perms(PosixPerm::RWX)
.default_owning_group_perms(PosixPerm::RX)
.default_other_perms(PosixPerm::NONE)
.build();
let text = generate::<GetfaclGenerator>(&original).unwrap();
let recovered = parse::<GetfaclParser>(&text).unwrap();
assert_eq!(original.owner(), recovered.owner());
assert_eq!(original.owning_group(), recovered.owning_group());
assert_eq!(original.special_bits(), recovered.special_bits());
assert_eq!(original.entries(), recovered.entries());
assert_eq!(original.default_entries(), recovered.default_entries());
}
Custom formats
Both the parser and generator are trait-based, so you can add new text
formats without modifying the crate. Implement PosixAclFormat for parsing
or PosixAclGenerator for generation:
use posix_acls::parser::PosixAclFormat;
use posix_acls::{PosixAcl, ParseError};
struct MyFormat;
impl PosixAclFormat for MyFormat {
fn parse(input: &str) -> Result<PosixAcl, ParseError> {
// Your parsing logic here
todo!()
}
fn name() -> &'static str {
"my-format"
}
}
The generic functions parse::<F>(text) and generate::<F>(acl) dispatch
to whichever format you provide.
Next steps
You can now serialise and deserialise ACLs. The next page explains the POSIX.1e access-check algorithm – how the kernel decides whether a user can read, write, or execute a file based on its ACL.
See Access Checks.