Grant and Revoke File Access with setfacl

Regular chmod cannot give one extra person read access to a file without also opening it to the whole group, which is exactly the gap setfacl fills. You will finish with a repeatable way to grant a named user access to one file, limit that access with the ACL mask, arrange defaults for new files in a directory, and remove the changes again. Examples use setfacl 2.3.2 from the installed acl package, version 2.3.2-1build1.1.

Allow about fifteen minutes. You need a Linux shell, the acl package, a file or directory you own, and a second user name for the examples. Commands that inspect your own files are normally unprivileged. Changing a file you do not own needs the file owner or a process with CAP_FOWNER, usually root, so use sudo only when your system's ownership model requires it.

1. Check the installed command

Confirm the binary and version before relying on option details. This is read-only:

$ command -v setfacl
/usr/bin/setfacl
$ setfacl --version
setfacl 2.3.2
$ dpkg-query -W -f='${Package} ${Version}\n' acl
acl 2.3.2-1build1.1

Keep the companion command getfacl nearby to display the ACL: permissions shown by ls -l only cover the ordinary mode bits and hide every named ACL entry.

2. Prepare a disposable test file

Use a scratch directory so the first change cannot touch a real document. None of this needs elevated privileges:

$ workdir=$(mktemp -d)
$ printf '%s\n' 'private test data' > "$workdir/report.txt"
$ getfacl --omit-header "$workdir/report.txt"
user::rw-
group::r--
other::r--

Those three entries are the base ACL: owner, owning group and everyone else. Replace ALICE below with a real local user name, checking it first if you are unsure:

$ id ALICE
uid=...

Do not copy the literal word ALICE into a production command. If the user does not exist, setfacl rejects the name rather than creating an account.

3. Grant one named user access

Modify the access ACL with -m. This adds or replaces the entry for the named user and leaves the existing base entries in place:

$ setfacl -m u:ALICE:rw "$workdir/report.txt"
$ getfacl --omit-header "$workdir/report.txt"
user::rw-
user:ALICE:rw-
group::r--
mask::rw-
other::r--

The automatically created mask::rw- is a ceiling on the effective permissions for the owning group and named users or groups. The named entry says what ALICE is granted; the mask says what ALICE can actually use. setfacl recalculates the mask by default to cover the permissions affected by it.

Checkpoint: the ACL must show both user:ALICE:rw- and an effective mask that includes rw. If the output instead shows user:ALICE:rw- #effective:r--, the mask is limiting the grant.

4. Use the mask deliberately

To let ALICE read the file but not write it, change the mask to r--. This is an ACL change, so check the result immediately:

$ setfacl -m m::r-- "$workdir/report.txt"
$ getfacl --omit-header "$workdir/report.txt"
user::rw-
user:ALICE:rw- #effective:r--
group::r--
mask::r--
other::r--

The stored named entry stays rw-, but its effective rights are read-only. To let ALICE write again, restore the mask with setfacl -m m::rw- "$workdir/report.txt". The mask never limits the file owner or the other:: entry.

Tip: be careful with -n. It tells setfacl not to recalculate the mask, so it is only useful when you are managing the mask explicitly yourself. Leaving it out is safer for an ordinary grant, since the default recalculation stops a stale mask silently narrowing the request.

5. Add a default ACL to a directory

A regular ACL affects the directory itself; a default ACL is inherited by new children. This changes future permissions, so do not apply it to a shared or service directory until you have checked the intended policy:

$ mkdir "$workdir/incoming"
$ setfacl -m u:ALICE:rwx "$workdir/incoming"
$ setfacl -d -m u:ALICE:rwx "$workdir/incoming"
$ getfacl --omit-header "$workdir/incoming"
user::rwx
user:ALICE:rwx
group::r-x
mask::rwx
other::r-x
default:user::rwx
default:user:ALICE:rwx
default:group::r-x
default:mask::rwx
default:other::r-x

The -d option makes the operation apply to the directory's Default ACL. setfacl creates the required default owner, group, others and mask entries from the directory ACL when needed. Test inheritance with a new file:

$ : > "$workdir/incoming/new.txt"
$ getfacl --omit-header "$workdir/incoming/new.txt"
user::rw-
user:ALICE:rwx #effective:rw-
group::r-x #effective:r--
mask::rw-
other::r--

The exact inherited base permissions depend on the creating process's mode and umask. What matters is that a named entry for ALICE exists and the mask gives it the effective rights you intended. A default ACL never retroactively rewrites files already in the directory.

6. Remove or restore the changes

Remove one named entry with -x, naming it without a permissions field:

$ setfacl -x u:ALICE "$workdir/report.txt"
$ getfacl --omit-header "$workdir/report.txt"
user::rw-
group::r--
other::r--

Recovery: for a broader rollback, back up with getfacl -R before changing a tree. Restore that output later with setfacl --restore=BACKUP_FILE. Restore is for a complete permission backup, cannot be mixed with ordinary setfacl options, and may also restore owners, groups and special mode bits recorded in the backup. Treat that file as sensitive, since it describes access to the whole tree.

7. Diagnose the common traps

If setfacl reports an operation not supported, the filesystem may not support POSIX ACLs. On such a filesystem the utility can fall back to modifying mode bits, but an ACL that cannot fit entirely into those bits causes an error and a non-zero exit status. Check the exit status and inspect the resulting ACL rather than assuming the request applied.

If a named user has less access than expected, check the mask and the file's parent directories. The ACL entry is only one part of the access check: directory search permission, ownership, filesystem policy and other security controls still matter. ls -l may show a plus sign or a group-mode value that reflects the mask, not the full named-user entry.

Warning: recursive operation is available with -R, but it can change a large tree quickly. Symlink handling matters too: the default recursive walk follows symlink arguments but skips symlinks encountered below the starting point; -L follows directory symlinks and -P avoids them. Use --test first where possible; it lists the resulting ACLs without changing files, a useful checkpoint before any command containing -R.

Done means