Shell-Level ACL Management in Linux: Complete Guide
Part 5 of 8 —Shell-Level ACL Management in Linux: Complete Guide
The two main shell commands for working with ACLs are getfacl (read/display ACL) and setfacl (create/modify/delete ACL entries). These are the tools you’ll use every day when managing file access on a Linux system. Understanding all their options gives you complete control over ACLs from the command line.
Command Options Reference
getfacl — View ACL
| Option | What It Does | Example |
|---|---|---|
| (no options) | Show access ACL with file/owner/group header | getfacl file.txt |
| -d | Show default ACL instead of access ACL (directories only) | getfacl -d dir/ |
| –omit-header | Hide the # file/owner/group comment lines | getfacl --omit-header file.txt |
| -R | Recursive — show ACL for all files in directory tree | getfacl -R /project/ |
| -n | Show UID/GID numbers instead of names | getfacl -n file.txt |
| -p | Don’t strip leading “/” — for absolute paths in backup | getfacl -Rp /data/ > backup.txt |
| -e | Show all effective rights comments | getfacl -e file.txt |
# Basic: view access ACL with header
getfacl myfile.txt
# file: myfile.txt
# owner: ravi
# group: users
user::rw-
user:alice:r-x
group::r--
mask::r-x
other::---
# Clean output without header (useful in scripts)
getfacl --omit-header myfile.txt
user::rw-
user:alice:r-x
group::r--
mask::r-x
other::---
# Show with numeric UID/GID (useful when users may not exist on target)
getfacl -n myfile.txt
# user::rw-
# user:1001:r-x
# group::r--
# mask::r-x
# other::---
# View default ACL of a directory
getfacl -d mydir/
setfacl — Modify ACL
| Option | What It Does | Example |
|---|---|---|
| -m entries | Add or modify ACL entries (short form) | setfacl -m u:bob:r-- file |
| -M file | Read ACL entries to add/modify from a file | setfacl -M acl.txt file |
| -x entries | Remove specific ACL entries (no permissions needed) | setfacl -x u:bob file |
| -X file | Read entries to remove from a file | setfacl -X remove.txt file |
| -b | Remove ALL extended entries (leaves minimal ACL: user/group/other) | setfacl -b file |
| -k | Remove default ACL from a directory | setfacl -k mydir/ |
| -d | Apply to default ACL instead of access ACL | setfacl -d -m u:bob:r-- dir/ |
| -R | Apply recursively to all files in directory tree | setfacl -R -m u:bob:r-- dir/ |
| -n | Don’t auto-recalculate ACL_MASK after changes | setfacl -n -m u:bob:r-- file |
| –set spec | Completely replace ACL (all old entries removed first) | setfacl --set u::rw,g::r,o::- file |
| –restore file | Restore ACLs from a getfacl backup file | setfacl --restore=backup.txt |
Step-by-Step Shell Examples
# Step 1: Set umask and create file
umask 022
touch project.c
getfacl project.c
# file: project.c (minimal ACL — rw-r--r--)
# Step 2: Add a named user entry
setfacl -m u:alice:rwx project.c
getfacl --omit-header project.c
# user::rw-
# user:alice:rwx <-- new entry
# group::r--
# mask::rwx <-- auto-created by setfacl
# other::r--
# Step 3: Add a named group entry
setfacl -m g:devteam:rw- project.c
getfacl --omit-header project.c
# user::rw-
# user:alice:rwx
# group::r--
# group:devteam:rw- <-- new entry
# mask::rwx <-- auto-updated to union of alice+devteam+group
# other::r--
# Step 4: Tighten the mask
setfacl -m m::rw- project.c
getfacl --omit-header project.c
# user:alice:rwx #effective:rw- <-- execute removed by mask!
# Step 5: ls -l shows extended ACL with '+'
ls -l project.c
# -rw-rw-r--+ 1 ravi users 0 Jan 1 10:00 project.c
# ^^^
# This is ACL_MASK (rw-), NOT ACL_GROUP_OBJ (r--)
# Remove a specific named user entry
setfacl -x u:alice project.c
# Note: no permissions needed when removing — just tag:qualifier
getfacl --omit-header project.c
# mask auto-adjusts to union of remaining entries
# Remove a named group entry
setfacl -x g:devteam project.c
# Remove ALL extended entries at once (-b = blank/clean)
setfacl -b project.c
getfacl --omit-header project.c
# user::rw-
# group::r--
# other::r--
# (Back to minimal ACL! No mask, no named user/group entries)
# ls -l no longer shows '+'
ls -l project.c
# -rw-r--r-- 1 ravi users 0 Jan 1 10:00 project.c
# Set ACL on all files and dirs in /project/ recursively
setfacl -R -m u:contractor:r-x /project/
# Check a few files
getfacl --omit-header /project/main.c
getfacl --omit-header /project/src/utils.c
# You can combine: set default ACL for new files AND access ACL for existing
# First, set default ACL on the directory (for future files)
setfacl -d -m u:contractor:r-x /project/
# Then, apply access ACL to all existing files
setfacl -R -m u:contractor:r-x /project/
# Remove default ACL from directory
setfacl -k /project/
<code">#!/bin/bash # acl_backup_restore.sh PROJECT_DIR="/home/ravi/project" BACKUP_FILE="/backup/project_acls_$(date +%Y%m%d).txt" echo "=== Backing up ACLs ===" # -R recursive, output goes to backup file getfacl -R "$PROJECT_DIR" > "$BACKUP_FILE" echo "Saved to: $BACKUP_FILE" echo "" echo "=== Restoring ACLs ===" # --restore reads the backup file # Must be run from / for correct path resolution cd / setfacl --restore="$BACKUP_FILE" echo "Restore complete." # Verify a sample file echo "" echo "=== Verification ===" getfacl "$PROJECT_DIR/main.c"
<code"># --set REPLACES the entire ACL (unlike -m which adds/modifies) # --set must include owner, group, other entries (the three base entries) # Completely set a new ACL setfacl --set "u::rw-,u:bob:r--,g::r--,g:devs:rw-,m::rw-,o::---" project.c # Verify the complete replacement getfacl --omit-header project.c # Difference: -m vs --set # -m: adds/updates specified entries, leaves others untouched # --set: wipes everything, then sets exactly what you specify # Example: file currently has user:alice entry # After setfacl -m u:bob:r-- file → alice entry still there, bob added # After setfacl --set "u::rw-,u:bob:r--,g::r--,m::r--,o::---" file # → alice entry is GONE, only bob remains
<code">#!/bin/bash
# setup_project_acl.sh — Configure ACLs for a team project directory
# Usage: ./setup_project_acl.sh /path/to/project
PROJECT="$1"
OWNER="ravi"
TEAM_GROUP="devteam"
REVIEWER="alice"
AUDITOR="charlie"
if [ -z "$PROJECT" ]; then
echo "Usage: $0 /path/to/project"
exit 1
fi
if [ ! -d "$PROJECT" ]; then
echo "Error: $PROJECT is not a directory"
exit 1
fi
echo "Setting up ACLs for: $PROJECT"
# 1. Set access ACL on the directory itself
setfacl -m "u:$REVIEWER:r-x,u:$AUDITOR:r--,g:$TEAM_GROUP:rwx,m::rwx,o::---" "$PROJECT"
# 2. Set default ACL so new files inherit these permissions
setfacl -d -m "u::rwx,u:$REVIEWER:r-x,u:$AUDITOR:r--,g:$TEAM_GROUP:rwx,m::rwx,o::---" "$PROJECT"
# 3. Apply to existing files recursively
setfacl -R -m "u:$REVIEWER:r-x,u:$AUDITOR:r--,g:$TEAM_GROUP:rwx,m::rwx,o::---" "$PROJECT"
# 4. Show results
echo ""
echo "=== Directory ACL ==="
getfacl "$PROJECT"
echo ""
echo "=== Default ACL ==="
getfacl -d "$PROJECT"
echo ""
echo "Done! New files created in $PROJECT will inherit these ACLs."
Common setfacl Patterns
<code"># Pattern 1: Give one user read access setfacl -m u:username:r-- filename # Pattern 2: Give one group read+write access setfacl -m g:groupname:rw- filename # Pattern 3: Block all access for a user (even if they're in a group) # (Tricky: you can't really "deny" with POSIX ACLs — user match wins) # Best approach: don't add the user to any permissive group # Pattern 4: Give user read-only on entire directory tree setfacl -R -m u:auditor:r-x /sensitive/ # Pattern 5: Remove one user's special access setfacl -x u:username filename # Pattern 6: Remove all ACL extensions (reset to chmod-only) setfacl -b filename # Pattern 7: Copy ACL from one file to another getfacl source.txt | setfacl --set-file=- dest.txt # Pattern 8: Set a very restrictive mask (limits all group class to read) setfacl -n -m m::r-- filename # Pattern 9: Set default ACL on directory for future files setfacl -d -m u:alice:rwx,g:team:rw-,m::rwx,o::--- /shared/ # Pattern 10: Remove default ACL from directory setfacl -k /shared/
Interview Questions
Q1. What is the difference between setfacl -m and setfacl –set?
-m (modify) adds or updates specific entries, leaving all other existing entries untouched. --set completely replaces the entire ACL — first removes all entries, then applies exactly what you specify. With --set, you must include the three mandatory entries (user, group, other) otherwise the ACL will be invalid.Q2. What does setfacl -b do and when would you use it?
setfacl -b removes all “extended” ACL entries (ACL_USER, ACL_GROUP, and ACL_MASK), leaving only the three base entries (ACL_USER_OBJ, ACL_GROUP_OBJ, ACL_OTHER) — a minimal ACL equivalent to traditional permissions. Use it when you want to completely remove per-user/per-group access control and revert to standard chmod permissions.Q3. How do you apply the same ACL to all files in a directory including new files created later?
setfacl -d -m ... to set a default ACL on the directory — this is inherited by all future files. (2) Use setfacl -R -m ... to apply the ACL to all existing files recursively. Default ACL handles future files; -R handles existing ones.Q4. When removing an ACL entry with setfacl -x, do you need to specify the permissions?
setfacl -x u:bob filename — no permissions needed. This makes sense because you’re identifying which entry to remove, not what to change it to.Q5. How do you back up and restore all ACLs in a directory tree?
getfacl -R /directory/ > acls_backup.txtRestore:
cd / && setfacl --restore=acls_backup.txtThe restore must be run from
/ because getfacl records relative paths and the restore uses them to find the correct files.Q6. What does the + sign at the end of ls -l permissions indicate?
ls -l reflect the ACL_MASK value, not ACL_GROUP_OBJ. Use getfacl filename to see the full ACL.Understand how directories pass ACLs to newly created files and subdirectories
