You’ve got a shared directory tree that used to “just work.” Then someone migrated it, restored from backup, re-mounted it via NFS, or “cleaned up permissions” at 2 a.m. Now half the team gets Permission denied, the other half can delete files they shouldn’t, and your ticket queue is learning to reproduce by binary fission.
The hard part isn’t running chown -R. The hard part is taking control without accidentally flattening ACLs, disabling inheritance, or replacing a carefully designed access model with a single blunt instrument. This is how you fix permissions in bulk like you plan to sleep later.
Know what “inheritance” actually means on Linux
People say “inheritance” like it’s a universal property of permissions. It’s not. On Linux, inheritance is an emergent behavior created by a few separate mechanisms:
1) POSIX mode bits (chmod) do not inherit
Directories have permissions; files have permissions. Creating a new file inside a directory does not “inherit” the directory’s mode bits. The new file gets a mode based on the creating process and its umask, not the parent directory’s rwx.
2) The setgid bit on directories is group inheritance (sort of)
If a directory has the setgid bit (chmod g+s), newly created files and directories typically inherit the directory’s group ownership. This is the old-school, surprisingly effective tool for shared team folders.
3) POSIX ACLs provide real inheritance via “default ACLs”
ACLs give you named users/groups, masks, and defaults. A directory can have default ACL entries that are applied to new objects created inside it. That’s the closest thing to Windows-style inheritance you get in native POSIX ACL land.
4) “Taking ownership” is a policy decision, not just a command
When you run chown, you’re changing who can change permissions, who can change ACLs, and who is effectively accountable. Done right, it restores order. Done wrong, it’s a quiet coup.
5) The ACL mask is the silent killer
On systems using POSIX ACLs, the mask entry can restrict effective permissions for named users and groups. So you can “grant” access and still see Permission denied because the mask clamps it down. This is how perfectly rational engineers lose an afternoon.
One paraphrased idea from a reliability luminary fits here: paraphrased idea
— “Everything fails; design so failures are predictable and recoverable.” — Richard Cook (paraphrased idea)
Interesting facts & history you can use in arguments
- Fact 1: Traditional Unix permissions (owner/group/other) date back to early Unix in the 1970s, optimized for simplicity over expressiveness.
- Fact 2: The setuid/setgid/sticky bits weren’t “security features” so much as pragmatic hacks for multi-user systems that needed shared workflows.
- Fact 3: POSIX ACLs became mainstream in Linux in the early 2000s as enterprises demanded finer-grained access control than mode bits could provide.
- Fact 4: The ACL
maskentry exists to provide a ceiling on effective permissions—great for control, terrible for intuition. - Fact 5: On many Linux filesystems, ACL support is built-in, but you still need userland tools (
getfacl,setfacl) to manage it sanely. - Fact 6: SMB/CIFS (Samba) often maps Windows ACL semantics onto POSIX ACLs or extended attributes, which can look “right” in Windows and “weird” on Linux.
- Fact 7: NFS has had multiple security models across versions; what “works” on NFSv3 with UID mapping can break spectacularly when you move identity management around.
- Fact 8:
rsynccan preserve ownership, mode bits, xattrs, and ACLs—but only if you explicitly ask and the target supports it. - Fact 9: Sticky bit on directories (like
/tmp) is a simple but powerful multi-user safety mechanism: you can create files, but not delete others’.
Fast diagnosis playbook (find the bottleneck fast)
This is the “stop guessing” sequence. Run it in order. It narrows the problem to identity, filesystem, ACL semantics, or tooling.
First: identify the access path and identity translation
- Where is the access coming from? local shell, SSH, Samba, NFS, container bind mount?
- Who is the user? numeric UID/GID matters more than names in many failure modes.
- Is there an identity mismatch? AD/LDAP/SSSD changes, idmap, NFS idmapping issues.
Second: check effective permissions at the failure point
- Directory execute bit (
x) on every parent path segment. - POSIX ACL entries and the ACL mask.
- Mount options (e.g.,
noacl,nosuid, root squashing in NFS).
Third: decide if you’re fixing ownership, ACLs, or both
- If ownership is wrong but ACLs are right, prefer targeted chown.
- If ACL defaults are missing, fix the directory default ACLs, not every file.
- If the model is inconsistent, snapshot/backup the metadata and rebuild from a known policy.
Joke 1: If your permission model requires a 40-slide deck, it’s not a model. It’s a hostage situation.
Practical tasks: commands, outputs, and decisions (12+)
These are the tasks I actually run during incidents and migrations. Each one includes what the output means and what decision you make from it.
Task 1: Confirm filesystem type and mount options (ACLs can be disabled)
cr0x@server:~$ findmnt -no SOURCE,TARGET,FSTYPE,OPTIONS /srv/share
/dev/sdb1 /srv/share ext4 rw,relatime,errors=remount-ro
Meaning: You’re on ext4 with normal options. If you saw noacl (or an NFS mount lacking expected features), you’d expect ACL behavior to differ.
Decision: If mount options conflict with your permission strategy, fix mount config before touching permissions. Otherwise you’ll “fix” metadata that the kernel ignores.
Task 2: Reproduce as the affected user and capture the exact failure
cr0x@server:~$ sudo -u alice bash -lc 'cd /srv/share/team && touch testfile'
touch: cannot touch 'testfile': Permission denied
Meaning: This is a write/create failure, not a read failure. On directories, creating requires w and x on the directory.
Decision: Focus on directory permissions/ACLs, not file permissions. You can chmod files forever; create will still fail.
Task 3: Check numeric identity and group membership (names can lie)
cr0x@server:~$ id alice
uid=10501(alice) gid=10501(alice) groups=10501(alice),12000(finance),12010(shared-team)
Meaning: Alice is in shared-team. If she wasn’t, your “permissions issue” might be an identity/group provisioning issue.
Decision: If group membership is missing, fix IAM/LDAP/AD/SSSD first. Don’t paper over identity with permissive ACLs.
Task 4: Inspect directory mode bits (do we have execute?)
cr0x@server:~$ namei -l /srv/share/team
f: /srv/share/team
drwxr-xr-x root root /
drwxr-xr-x root root srv
drwxr-x--- root shared-team share
drwxrwx--- root shared-team team
Meaning: The path looks traversable for the group on /srv/share and /srv/share/team. If any parent lacked x for the relevant class, traversal would fail.
Decision: If traversal fails, fix the parent directory first. A perfect ACL on the leaf directory won’t help if you can’t reach it.
Task 5: Check ACLs and the mask on the directory
cr0x@server:~$ getfacl -p /srv/share/team
# file: /srv/share/team
# owner: root
# group: shared-team
user::rwx
group::rwx
other::---
mask::r-x
default:user::rwx
default:group::rwx
default:other::---
Meaning: The mask::r-x caps group permissions to read/execute even though group::rwx exists. That can block writes.
Decision: Fix the mask (setfacl -m m::rwx) or re-apply ACL entries with correct mask handling. Don’t blindly add users; you’ll still be blocked.
Task 6: Show effective ACL permissions (avoid self-deception)
cr0x@server:~$ getfacl -p /srv/share/team | sed -n '1,20p'
# file: /srv/share/team
# owner: root
# group: shared-team
user::rwx
group::rwx #effective:r-x
other::---
mask::r-x
default:user::rwx
Meaning: The #effective annotation tells you the truth: group is effectively r-x, not rwx.
Decision: If effective permissions don’t match intent, treat the mask as a first-class config knob, not an afterthought.
Task 7: Fix only the directory ACL mask (surgical change)
cr0x@server:~$ setfacl -m m::rwx /srv/share/team
cr0x@server:~$ getfacl -p /srv/share/team | grep -E 'mask|group::'
group::rwx
mask::rwx
Meaning: Mask now allows writes. If users still can’t create files, the next suspect is default ACLs and umask.
Decision: Retest creation as the user. If fixed, stop. Don’t continue “improving” permissions after the incident is resolved.
Task 8: Verify default ACLs (inheritance for new files)
cr0x@server:~$ getfacl -p /srv/share/team | grep -E '^default:'
default:user::rwx
default:group::rwx
default:other::---
Meaning: Default ACL exists, but it might be missing named entries for required groups, or the default mask might be wrong.
Decision: If you need multiple groups to inherit access, add default named group entries and set an appropriate default mask.
Task 9: Add a default ACL for a collaborating group (and keep it inheritable)
cr0x@server:~$ setfacl -m d:g:finance:rwx /srv/share/team
cr0x@server:~$ setfacl -m d:m::rwx /srv/share/team
cr0x@server:~$ getfacl -p /srv/share/team | grep -E 'default:group:finance|default:mask'
default:group:finance:rwx
default:mask::rwx
Meaning: New files/dirs created under /srv/share/team will inherit a finance group ACL entry, subject to the default mask.
Decision: If inheritance is the goal, always configure defaults on directories. Don’t recursively slap ACLs onto existing files unless you have to.
Task 10: Confirm setgid on the directory (group ownership continuity)
cr0x@server:~$ stat -c '%A %U %G %n' /srv/share/team
drwxrwx--- root shared-team /srv/share/team
Meaning: No setgid bit shown (s would appear in the group execute position). Without it, new files may get the creator’s primary group, not shared-team.
Decision: For shared collaboration directories, enable setgid unless you have a strong reason not to.
Task 11: Enable setgid and validate behavior with a test file
cr0x@server:~$ chmod g+s /srv/share/team
cr0x@server:~$ stat -c '%A %U %G %n' /srv/share/team
drwxrws--- root shared-team /srv/share/team
cr0x@server:~$ sudo -u alice bash -lc 'touch /srv/share/team/owned-by-group && stat -c "%U %G %n" /srv/share/team/owned-by-group'
alice shared-team /srv/share/team/owned-by-group
Meaning: Group ownership now stays consistent. That reduces future “why is this file owned by the wrong group?” tickets.
Decision: Keep setgid on high-collaboration directories. It’s boring. Boring is good.
Task 12: Preview the blast radius before a recursive ownership change
cr0x@server:~$ find /srv/share/team -maxdepth 2 -printf '%u:%g %m %p\n' | head
root:shared-team 2770 /srv/share/team
root:shared-team 2770 /srv/share/team/projects
bob:shared-team 0660 /srv/share/team/projects/notes.txt
carol:finance 0660 /srv/share/team/projects/budget.xlsx
Meaning: You have mixed ownership and groups. A blind chown -R root:shared-team would overwrite deliberate per-project ownership patterns.
Decision: If mixed ownership is expected, avoid global chown. If it’s accidental, define rules (what should change, what should remain).
Task 13: Change ownership safely with filters (target only what’s wrong)
cr0x@server:~$ find /srv/share/team -group finance -print | head -n 3
/srv/share/team/projects/budget.xlsx
/srv/share/team/projects/q1
/srv/share/team/archive/old
Meaning: These objects have group finance. Maybe that’s wrong. Maybe it’s correct. Now you know where it is.
Decision: Use find predicates to scope changes. Your future self will appreciate the restraint.
Task 14: Perform a scoped chgrp (group only) and verify one sample
cr0x@server:~$ sudo chgrp shared-team /srv/share/team/projects/budget.xlsx
cr0x@server:~$ stat -c '%U %G %n' /srv/share/team/projects/budget.xlsx
carol shared-team /srv/share/team/projects/budget.xlsx
Meaning: Group adjusted, owner preserved. That’s usually less risky than changing both.
Decision: Prefer chgrp over chown when ownership isn’t the problem.
Task 15: Back up ACLs and xattrs before bulk edits (your rollback plan)
cr0x@server:~$ getfacl -R -p /srv/share/team > /root/team.acl.backup
cr0x@server:~$ wc -l /root/team.acl.backup
48231 /root/team.acl.backup
Meaning: You have a text backup of ACL metadata. Not perfect, but it’s a real rollback lever.
Decision: If you can’t snapshot, at least capture metadata. Bulk permission changes without rollback is gambling with production data.
Task 16: Restore ACLs from backup if you botch it
cr0x@server:~$ setfacl --restore=/root/team.acl.backup
cr0x@server:~$ echo $?
0
Meaning: Exit code 0 indicates the restore succeeded. You still need spot checks.
Decision: When restoring, validate a few critical directories and files. Trust, but verify—especially after you just proved you’re fallible.
Bulk ownership changes without collateral damage
“Take ownership” is usually shorthand for one of three goals:
- Operational control: a service account or admin group must be able to repair permissions later.
- Access correction: end-users need to read/write again.
- Policy enforcement: everything in a tree must conform to a known standard.
Those goals require different tactics. Treat them differently, or you’ll fix the incident and break your governance model. Yes, both are bad; one just shows up in audits instead of pagers.
When you should not run chown -R
A recursive ownership change is appropriate when the current owners are clearly wrong (e.g., everything is owned by a retired migration UID, or by root because a restore ran as root). It’s not appropriate when:
- The tree contains multiple projects with different owners by design.
- Applications rely on specific owners for security boundaries.
- ACLs already encode the access policy and ownership is only used for “who can chmod.”
Take ownership while preserving inheritance: the safe pattern
Here’s the pattern that works across most shared-file setups:
- Fix directory-level inheritance first: setgid + default ACLs + correct masks.
- Stop using ownership as access control in shared trees. Use group + ACL policy for access, reserve ownership for administrative control.
- Normalize only what must be normalized: directories and collaboration-critical file areas, not historical archives unless required.
- Validate with real user actions: create, rename, delete, edit. Reading is the easy part.
What “not breaking inheritance” really means
It means you don’t accidentally remove:
- default ACL entries on directories,
- setgid on collaboration directories,
- the intended ACL mask values,
- Samba/NFS mapping expectations (IDs, xattrs),
- application-specific permission requirements.
Joke 2: chmod 777 is like unplugging the fire alarm because it’s loud. Quiet, yes. Better, no.
Three corporate mini-stories from the trenches
Mini-story 1: The incident caused by a wrong assumption
They had a Linux file server exporting a share over Samba. A new engineer—smart, fast, dangerously optimistic—assumed Windows-style inheritance was “stored on the folder” and would naturally carry down.
During a reorg, they created a new top-level directory, copied data in, and adjusted permissions at the top using a Windows client. The permissions looked right in Explorer. Everyone went home.
Next morning, the design team couldn’t save files. The directory showed “Modify” access, yet every save failed. The engineer checked share-level settings and found nothing. Classic.
The actual issue: the new directory had no default POSIX ACL entries, and the ACL mask on some directories was restrictive. Files created by some applications got a umask-limited mode and no inheritable ACL entries. Windows clients displayed a friendly abstraction; the Linux side enforced a different reality.
The fix was not a massive recursive ACL rewrite. They set default ACLs on the right directories, corrected masks, enabled setgid, and then touched only the hotspots where files were being created. The “wrong assumption” wasn’t about syntax. It was about which system was the source of truth.
Mini-story 2: The optimization that backfired
A storage team wanted to speed up nightly ingestion into a shared dataset. Someone proposed: “Let’s run the ingest as root, it’ll avoid permission checks and go faster.” It worked. Ingest finished earlier. People celebrated with the kind of quiet satisfaction you only get from shaving minutes off a job nobody enjoys.
Two weeks later, users started reporting random inability to edit files. It wasn’t consistent: one directory was fine, the next wasn’t. Support tickets turned into Slack threads, then into meetings, then into more Slack threads. Productivity died by a thousand “can you try again?” messages.
The root cause was subtle: root-owned files were created with modes and ACLs that didn’t match the collaboration model. Some files got no named ACL entries, and others got restrictive masks due to the way ACLs were applied by scripts. The ingest was “faster” because it bypassed the correctness constraints.
The rollback wasn’t pretty. They had to identify files created by the ingest user over a date range, restore correct group ownership, and apply ACL policy where needed. The lesson stuck: performance hacks that touch identity and permissions are a debt instrument with compounding interest.
Mini-story 3: The boring but correct practice that saved the day
A different org had a disciplined habit: before any large permission change, they captured ACLs recursively to a timestamped file and took a filesystem snapshot if available. It was written into their change template, and everyone complied because the on-call rotation was shared suffering.
During a migration, a script intended to fix group ownership accidentally ran on the wrong mountpoint. It changed group ownership on a live share used by several teams. The monitoring didn’t scream; permissions issues rarely look like CPU spikes. Users screamed instead, which is a less machine-friendly signal.
Because they had the pre-change ACL backup and a snapshot, recovery was mechanical: remount read-only briefly, restore ACLs, roll back the snapshot for the affected subtree, then re-apply the intended change with corrected scoping. The outage wasn’t zero, but it was bounded. No archaeology, no guesswork.
What saved them wasn’t heroics. It was boredom: a checklist, a rollback artifact, and the refusal to treat “permissions work” as too simple to need safety rails.
Common mistakes: symptom → root cause → fix
1) Symptom: “User has rwx on the directory but still can’t create files”
Root cause: ACL mask clamps effective permissions (mask::r-x), or user lacks write on the directory in effective terms.
Fix: Inspect with getfacl and look for #effective. Adjust mask: setfacl -m m::rwx DIR and if needed setfacl -m d:m::rwx DIR.
2) Symptom: “New files have the wrong group”
Root cause: Missing setgid bit on directories, or application creates in a different location without setgid.
Fix: chmod g+s DIR. Confirm via stat and test file creation as a normal user.
3) Symptom: “Everything looks fine on Linux, but Windows users can’t edit”
Root cause: Samba ACL mapping mismatch, xattrs not preserved, or Windows expects inheritance semantics that POSIX defaults don’t provide.
Fix: Validate POSIX ACL defaults on directories; ensure the share is configured consistently; confirm xattr support and that copy/migration preserved ACLs.
4) Symptom: “After chmod -R, access got worse”
Root cause: Mode bits changed but ACLs remain; chmod may interact with ACL mask and remove intended access paths.
Fix: Stop recursive chmod on ACL-managed trees. Re-apply intended ACL policy on directories and correct masks. Restore from ACL backup if you have it.
5) Symptom: “Root can’t fix it over NFS”
Root cause: Root squashing maps root to anonymous UID, blocking ownership changes.
Fix: Do permission repair on the server side (not the client mount), or adjust NFS export policy deliberately and temporarily with change control.
6) Symptom: “Some directories are writable; others identical are not”
Root cause: Inconsistent default ACLs across directories, often due to partial migrations or ad-hoc fixes.
Fix: Compare with getfacl on a good and bad directory. Normalize directory defaults; avoid touching historical files unless required.
Checklists / step-by-step plan
Step-by-step plan for a safe bulk fix
- Clarify the model: decide which groups/users should have read/write, and which directories need inheritance behavior.
- Capture rollback artifacts: snapshot if available; otherwise
getfacl -R -pto a file. - Confirm the access path: local vs Samba vs NFS; confirm ID mapping and group membership.
- Pick a pilot subtree: one project folder, not the whole share.
- Fix inheritance on directories: setgid + default ACL entries + default masks.
- Fix directory ACL masks: ensure effective permissions match intent.
- Retest real actions: create, rename, delete, edit, and create-from-app (not just
touch). - Normalize group ownership where needed: prefer
chgrpwith scopedfindexpressions. - Only then consider recursive operations: when you can articulate the blast radius and have rollback.
- Roll forward systematically: apply the same fix pattern to the next subtree; don’t improvise per directory.
- Document the rules: setgid expectations, default ACL entries, and the reason masks are set as they are.
- Guardrails: a periodic audit job that flags directories missing setgid or default ACLs.
Pre-change checklist (printable in your head)
- Do I have a snapshot or ACL backup?
- Do I know which protocol users hit (Samba/NFS/local)?
- Did I reproduce as a real user with
sudo -u? - Did I check the ACL mask and effective permissions?
- Am I changing directories (inheritance) before files (symptoms)?
- Am I using scoped
findinstead of-Rby habit?
Post-change checklist (prove it’s fixed)
- Create a file, a directory, and rename both as an affected user.
- Verify group ownership of new objects matches expectation (setgid working).
- Verify a second collaborating group can access if that’s in policy (default ACL works).
- Spot-check a “known sensitive” directory to ensure you didn’t widen access.
- Keep the ACL backup/snapshot until the next business cycle completes.
FAQ
1) What does “take ownership” mean in a shared folder context?
Usually: make a consistent administrative owner (often root or a service account) while using group/ACLs to grant user access. Ownership is for control; ACLs are for collaboration.
2) Why does chmod sometimes appear to “break” ACL permissions?
Because ACLs and mode bits interact. The ACL mask can change effective permissions, and some chmod operations can alter the mask. If you manage access with ACLs, treat chmod as a surgical tool, not a recursive lifestyle choice.
3) How do I preserve inheritance for new files?
On Linux: use setgid on directories for group inheritance, and default ACLs for permission inheritance. Verify default mask entries so the inherited permissions are actually effective.
4) Should I apply ACLs recursively to every file?
Not by default. Fix directory defaults first; they influence future content. Recursively fixing existing files is sometimes necessary after migrations, but it increases risk and runtime, and it’s easy to overexpose data.
5) I ran chown -R and now apps are failing. What’s the fastest rollback?
If you took a snapshot, roll it back for the affected subtree. If not, restore ACLs from your getfacl backup and then correct ownership using scoped rules. If you have neither, you’re rebuilding policy by inference—budget time accordingly.
6) Why can a user list a directory but not access files inside?
Listing requires read on the directory; accessing a specific name requires execute on the directory and appropriate permissions on the file. Missing x on a parent directory is a common gotcha.
7) What’s the difference between an ACL entry and the ACL mask?
Entries express intended permissions for users/groups. The mask is the maximum effective permission for named users/groups and the group class. If the mask is restrictive, entries won’t behave as you expect.
8) How do Samba and Windows inheritance relate to Linux ACL defaults?
Samba can map Windows ACLs to POSIX ACLs and/or store richer metadata in xattrs. Windows “inheritance” doesn’t always translate cleanly. On Linux, default ACLs on directories are the closest analogue for new object inheritance.
9) Is it safe to fix permissions while users are active?
Sometimes. Directory ACL/default changes are usually safe but can cause transient failures if you remove access briefly. Large recursive changes increase risk and load. If this is business-critical, do a controlled window and keep rollback ready.
Conclusion: next steps you should actually do
If you’re staring at a messy permission tree, don’t start with recursion. Start with truth: confirm identity, confirm mount semantics, confirm effective ACLs (especially masks), and then fix inheritance at the directory level. That’s where stability comes from.
Practical next steps:
- Pick one problematic directory and run:
namei -l,stat,getfacl, and a real-user create test. - Back up ACLs for the subtree before you change anything meaningful.
- Set setgid and default ACLs on collaboration directories; correct masks so “granted” means “effective.”
- Scope ownership/group changes with
findpredicates; preferchgrpoverchownwhen you can. - After the incident, write down the intended policy and add an audit job to catch drift before the next migration does.
Permissions aren’t hard because Linux is hard. They’re hard because humans are inconsistent, and storage remembers everything.