skip to content

A shared directory carries an ACL granting a named group write access, yet files newly created inside it do not get that access. How do POSIX default ACLs work, and what determines the permissions of a file created in such a directory?

level: middleimportance: should knowfreq 42%

answer

  1. directories can hold two lists
  2. a template, not a rule
  3. stamped once at creation time
  4. umask steps aside
  5. existing files were never touched

basics

~20 s

An ordinary ACL controls access to the directory itself and is not inherited. Inheritance requires a separate default ACL, set with setfacl -d, which acts as a template copied onto newly created files and subdirectories. When one exists, the process umask is not applied.

solid answer

~50 s

A directory can carry two ACLs. The **access ACL** decides who may act on the directory itself, and it never propagates. The **default ACL**, set with `setfacl -d -m g:devs:rwx dir` and shown by `getfacl` on `default:` lines, is pure inheritance metadata: the kernel copies it onto every file and subdirectory created inside. A new subdirectory receives it as both its own access ACL and its own default ACL, so inheritance continues downward; a new file receives it as an access ACL only. Crucially, when a default ACL is present the process umask is ignored — instead the inherited owner, mask and other entries are reduced by the mode the creating program requested (typically 0666 for files, 0777 for directories). And a default ACL changes nothing about files that already exist, so retrofitting a directory takes two operations: `setfacl -R -m ...` for the existing tree and `setfacl -d -m ...` for what comes next.

code

bash · 7 lines
bash
mkdir -p /tmp/shared
setfacl -m g:adm:rwx /tmp/shared        # access ACL: use the directory
setfacl -d -m g:adm:rwx /tmp/shared    # default ACL: template for new files
getfacl /tmp/shared                    # note the default: lines
touch /tmp/shared/new.txt
getfacl /tmp/shared/new.txt            # group:adm:rwx inherited, x dropped
setfacl -k /tmp/shared                 # stop inheriting, keep current access

go deeper

for a junior

Know that inheritance needs a separate default ACL created with setfacl -d, that getfacl shows it on default: lines, and that it affects only files created after you set it.

for a middle

Explain that the default ACL is a template copied at creation time — as an access ACL for files, as both access and default for subdirectories — and that the umask is bypassed while the creating program's requested mode still reduces the result.

for a senior

Anticipate the gaps: files moved rather than created, atomic write-and-rename patterns landing files elsewhere, and later recursive chmods clamping the inherited entries through the mask. Retrofit with both a recursive -m and a recursive -d -m.

for a principal

Decide whether shared-directory behaviour is guaranteed by default ACLs, by group ownership conventions, or by the applications themselves, and make that choice explicit — a scheme depending on every writer's personal umask is the one that fails silently at scale.

## Two ACLs on one directory Regular files have one ACL. Directories may have two, and conflating them is the usual source of "my ACL isn't working": - The **access ACL** answers "who may read, write or traverse *this directory*". It behaves exactly like a file's ACL and has no effect on anything created inside. - The **default ACL** answers "what ACL should new entries in this directory start with". It grants no access to anyone by itself — it is a template. `getfacl` prints them together, prefixing the template with `default:`: ``` $ getfacl /srv/shared # file: srv/shared # owner: root # group: staff user::rwx group::r-x other::r-x default:user::rwx default:group::r-x default:group:devs:rwx default:mask::rwx default:other::r-x ``` You set default entries with `setfacl -d -m ...`, which is exactly equivalent to prefixing the entry with `d:`: ``` $ setfacl -d -m g:devs:rwx /srv/shared $ setfacl -m d:g:devs:rwx /srv/shared # identical ``` A default ACL must be complete: if you add one named entry, `setfacl` fills in `default:user::`, `default:group::`, `default:other::` and `default:mask::` for you, seeding them from the directory's own access ACL. ## What happens at file creation When a process calls `open(..., O_CREAT, mode)` or `mkdir(path, mode)` in a directory that has a default ACL, the kernel does this: 1. Copies the parent's default ACL onto the new object as its **access ACL**. 2. If the new object is a **directory**, also copies the default ACL onto it as its own **default ACL**, so inheritance keeps flowing down the tree. 3. Reduces the three mode-bearing entries — `user::`, the group class (i.e. the `mask` when the ACL is extended, otherwise `group::`), and `other::` — by the corresponding triads of the `mode` argument the program passed. And the point candidates most often miss: **the umask is not consulted at all**. The umask is the fallback rule for directories with no default ACL. Once a default ACL exists, the administrator's template plus the creating program's requested mode fully determine the outcome. That is what makes default ACLs usable for shared directories — you are no longer at the mercy of each user's personal `umask` setting. Because most programs request `0666` for files and `0777` for directories, the practical effect is that inherited named entries survive intact while the executable bit is dropped from ordinary files, which is what you want. ## Retrofitting an existing directory This is the second half of the interview answer. A default ACL is applied at creation time only — it is not a live rule evaluated on access, and it does not reach backwards. A directory that already contains a thousand files needs two commands: ``` $ setfacl -R -m g:devs:rwX /srv/shared # existing files and directories $ setfacl -R -d -m g:devs:rwx /srv/shared # the template for future ones ``` (`-R` recurses; `-d` selects the default ACL. Note that `-R` on its own does *not* imply `-d`, and `-d` on its own does *not* touch existing content — that asymmetry is the whole reason people believe inheritance is broken.) To remove inheritance without disturbing current access, use `setfacl -k dir`, which deletes the default ACL only. `setfacl -b dir` removes the extended entries of both. ## Where it goes wrong in production - **A file moved in, not created in.** `mv` across a rename within the same filesystem preserves the file's existing ACL; the default ACL is applied only on *creation*. Copying with a tool that creates a new file (`cp` without ACL preservation) does inherit the default. Two paths that look identical to a user produce different ACLs. - **A process that writes atomically.** Editors and daemons that write to a temporary file and `rename()` it into place produce a file that inherited from whichever directory the temporary was created in. - **A later chmod.** Because the group-class mode bits address the mask on an extended ACL, a `chmod` on inherited files clamps the inherited named entries just as it does anywhere else. - **Confusing the two ACLs.** Granting `g:devs:rwx` on the directory itself lets the group create and delete entries in it, but every file they create still lands with only their personal default rights. Both halves are usually needed. ## The mental model An access ACL is a rule the kernel evaluates on every access. A default ACL is a stamp applied exactly once, at creation. If you keep that distinction, the behaviour of a shared directory becomes predictable: `getfacl` on the parent tells you what new files will look like, and `getfacl` on the file tells you what it actually got.

  • What does a new subdirectory inherit that a new regular file does not?
    A new subdirectory receives the parent's default ACL twice: once as its own access ACL, deciding who may use it, and once as its own default ACL, so the template continues to propagate to anything created deeper. A regular file gets the template only as an access ACL, because a file has nothing to pass on.
  • How do you remove inheritance from a directory without changing who can access the directory today?
    `setfacl -k dir` deletes only the default ACL, leaving the access ACL untouched, so current permissions on the directory are unchanged while newly created files stop inheriting. By contrast `setfacl -b dir` strips the extended entries from both, which does change who can reach the directory.
  • A file was moved into the shared directory instead of created there and did not pick up the ACL. Why?
    The default ACL is applied by the kernel at creation time only — during `open(O_CREAT)` or `mkdir`. A `rename()` within the same filesystem moves the existing inode, which keeps the ACL it was created with. Copying it with a tool that creates a fresh file, or re-applying with `setfacl -R -m`, is what fixes such a file.

saying these in an interview costs you the question

  • Expecting a plain ACL on a directory to be inherited by its files
  • Thinking the default ACL retroactively fixes existing files
  • Believing the umask still applies inside a default-ACL directory
  • Assuming -R also sets the default ACL
  • Treating the default ACL as a live access rule on the directory

context