Kernel Support
Fine-grained control
Based on draft standard
Stored as EAs
Key Terms
Traditional UNIX file permissions give you exactly three buckets: owner (user), group, and everyone else (other). You set read/write/execute for each bucket — and that’s it.
This model works for many situations, but it breaks down when you need more precise control. Consider this scenario:
Scenario: You have a file project_notes.txt owned by user alice. You want:
alice— full read/writebob— read onlycarol— read + write- Everyone else — no access
With traditional permissions, you cannot express “bob = read-only, carol = read-write” at the same time. You can only set one permission for the group, and bob and carol would need to be in different groups or you’d have to use workarounds.
| Feature | Traditional UNIX Permissions | ACLs |
|---|---|---|
| Specify permission for individual users? | ✗ Only owner | ✓ Any number of users |
| Specify permission for individual groups? | ✗ Only one group | ✓ Any number of groups |
| Backward compatible with stat() and ls? | ✓ Native | ✓ Maintained via mask |
| Visible with ls -l? | ✓ Always | ⚠ Partial (+ symbol in ls -l) |
| Requires special filesystem mount? | ✗ Always available | ⚠ Needs -o acl on ext*/Reiserfs |
| Standardized by POSIX? | ✓ Yes (POSIX.1) | ⚠ Draft only (POSIX.1e withdrawn) |
| Stored as | Fixed i-node fields | Extended Attributes (system.*) |
An ACL is a list of ACL entries. Each entry specifies a principal (a specific user or group) and a set of permissions (read, write, execute).
| ACL Entry Type | Meaning | Example | Mandatory? |
|---|---|---|---|
user:: |
File owner | user::rw- |
✓ Always |
user:alice: |
Specific named user | user:alice:r-- |
Optional |
group:: |
File’s owning group | group::r-- |
✓ Always |
group:devs: |
Specific named group | group:devs:rw- |
Optional |
mask:: |
Max permissions for named entries | mask::rw- |
✓ When named entries exist |
other:: |
Everyone else | other::--- |
✓ Always |
ACLs require both kernel support (present by default in modern kernels) and the file system must be mounted with the acl option on ext2/ext3/ext4/Reiserfs:
# Mount with ACL support
mount -o acl /dev/sdb1 /mnt/data
# Or add to /etc/fstab
# /dev/sdb1 /mnt/data ext4 defaults,acl 0 2
# XFS and Btrfs have ACL support enabled by default (no mount option needed)
# Check if a mounted filesystem has ACL support
tune2fs -l /dev/sdb1 | grep "Default mount options"
# Look for: acl
# Install ACL tools
sudo apt install acl # Debian/Ubuntu
sudo dnf install acl # Fedora/RHEL
# Create a test file
touch project.txt
# View current ACL (same as traditional perms initially)
getfacl project.txt
# Output:
# file: project.txt
# owner: ravi
# group: ravi
# user::rw-
# group::r--
# other::r--
# Grant specific user 'alice' read-only access
setfacl -m u:alice:r-- project.txt
# Grant specific user 'carol' read+write access
setfacl -m u:carol:rw- project.txt
# Grant a group 'devteam' read access
setfacl -m g:devteam:r-- project.txt
# View the updated ACL
getfacl project.txt
# Output:
# file: project.txt
# owner: ravi
# group: ravi
# user::rw-
# user:alice:r--
# user:carol:rw-
# group::r--
# group:devteam:r--
# mask::rw- <-- auto-created when named entries added
# other::r--
# Notice the '+' in ls -l output indicating ACL is present
ls -l project.txt
# -rw-rw-r--+ 1 ravi ravi 0 Jan 10 project.txt
# Remove a specific ACL entry
setfacl -x u:alice project.txt
# Remove ALL ACL entries (revert to traditional permissions)
setfacl -b project.txt
A default ACL can be set on a directory. When new files or subdirectories are created inside, they inherit the default ACL as their initial ACL. This is very useful for shared project directories.
# Create a shared project directory
mkdir /home/projects/webapp
# Set default ACL: new files will give devteam read+write
setfacl -d -m g:devteam:rw- /home/projects/webapp
# View both access ACL and default ACL
getfacl /home/projects/webapp
# Output:
# file: projects/webapp
# owner: ravi
# group: ravi
# user::rwx
# group::r-x
# other::r-x
# default:user::rwx
# default:group::r-x
# default:group:devteam:rw- <-- inherited by new files
# default:mask::rwx
# default:other::r-x
# Now create a file inside — it inherits the default ACL
touch /home/projects/webapp/index.html
getfacl /home/projects/webapp/index.html
# The devteam group will have rw- on this new file automatically
ACLs are implemented on top of the extended attribute system. The kernel stores ACL data in the system namespace EAs:
| EA Name | What it Stores |
|---|---|
system.posix_acl_access |
The access ACL for the file (controls who can access it) |
system.posix_acl_default |
The default ACL on a directory (inherited by new children) |
# You can see the raw EA (binary) that stores the ACL
getfattr -m system -d /home/projects/webapp/index.html
# Output (binary, shown as hex):
# system.posix_acl_access=0s...
# Or view as hex explicitly
getfattr -m system --absolute-names -d project.txt
system namespace EAs under the hood. If the file system doesn’t support EAs, it cannot support ACLs.The ACL C API is defined in <sys/acl.h>. Link with -lacl.
/* Compile: gcc -o acl_demo acl_demo.c -lacl */
#include <sys/types.h>
#include <sys/acl.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <file>\n", argv[0]);
return 1;
}
/* Get the access ACL for the file */
acl_t acl = acl_get_file(argv[1], ACL_TYPE_ACCESS);
if (acl == NULL) {
perror("acl_get_file");
return 1;
}
/* Convert ACL to a human-readable string */
char *text = acl_to_text(acl, NULL);
if (text) {
printf("ACL for %s:\n%s\n", argv[1], text);
acl_free(text);
}
acl_free(acl);
return 0;
}
/* Check if a file has an extended ACL (beyond basic user/group/other) */
#include <sys/types.h>
#include <sys/acl.h>
#include <stdio.h>
#include <acl/libacl.h> /* Linux-specific extension */
int has_extended_acl(const char *path) {
acl_t acl = acl_get_file(path, ACL_TYPE_ACCESS);
if (!acl) return -1;
/* acl_equiv_mode() returns 0 if ACL is equivalent to basic permissions */
mode_t mode;
int is_extended = (acl_equiv_mode(acl, &mode) == 1);
acl_free(acl);
return is_extended;
}
int main() {
const char *files[] = { "/etc/passwd", "/tmp/project.txt", NULL };
for (int i = 0; files[i]; i++) {
int ext = has_extended_acl(files[i]);
printf("%s: %s\n", files[i],
ext == 1 ? "Has extended ACL" :
ext == 0 ? "Basic permissions only" : "Error");
}
return 0;
}
An important historical note: ACLs on Linux (and most UNIX systems) are based on a draft standard — POSIX.1e and POSIX.2c — which was ultimately withdrawn and never finalized.
| 1990s IEEE begins POSIX.1e/2c work to standardize ACLs, capabilities, and audit |
→ | Draft 17 Final published draft. Many UNIX vendors implement based on this draft |
→ | Withdrawn IEEE withdraws the draft — standardization abandoned |
→ | Today Linux, BSDs, Solaris all implement Draft 17 APIs — de facto standard |
# Syntax: setfacl [options] acl_spec file...
# -m = modify (add or update entries)
# -x = remove specific entry
# -b = remove all ACL entries (back to basic perms)
# -d = operate on the default ACL
# -R = recursive (apply to all files in directory)
# -k = remove default ACL
# Grant user permissions
setfacl -m u:username:rwx file # read+write+execute
setfacl -m u:username:r-- file # read only
setfacl -m u:username:--- file # no permissions
# Grant group permissions
setfacl -m g:groupname:rw- file
# Remove a specific user entry
setfacl -x u:username file
# Set default ACL on a directory
setfacl -d -m u:username:rw- /some/dir
# Copy ACL from one file to another
getfacl file1 | setfacl --set-file=- file2
# Recursive ACL on a directory tree
setfacl -R -m g:devteam:rw- /home/projects/webapp/
system namespace. Specifically, system.posix_acl_access stores the access ACL and system.posix_acl_default stores the default ACL for directories. This is why EA support must be available on the file system before ACLs can be used.rwx, if the mask is r--, the effective permission is only r--. The mask is automatically created and updated when you add named entries with setfacl.+ at the end of the permission string (e.g., -rw-rw-r--+) indicates that the file has an extended ACL beyond the basic user/group/other permissions. To see the full ACL, use getfacl filename.acl_get_file() to retrieve the ACL, then acl_equiv_mode() (Linux-specific, from <acl/libacl.h>) which returns 1 if the ACL contains entries beyond the minimal user/group/other set. Alternatively, call getxattr() directly and check if system.posix_acl_access exists and has more than the minimum 3 entries.Series Complete
You have covered all topics from the PDF chapter
