Linux: Permission denied even after chmod
Diagnose Linux Permission denied that persists after chmod by checking ownership, parent directories, mount options, and SELinux or AppArmor.
Symptoms
- bash: ./script.sh: Permission denied
- chmod +x was applied but execution still fails
- Writes fail in a directory that shows rwx permissions
Possible causes
- The user does not own the file and lacks the needed permission class
- A parent directory lacks execute permission, blocking traversal
- The filesystem is mounted noexec or read-only
- SELinux or AppArmor denies access despite Unix permissions
Diagnosis steps
Check permissions on the file and every parent directory
namei -l /path/to/script.shInterpretation
namei lists permissions along the whole path, revealing a missing execute on a parent.
Test whether the filesystem forbids execution
mount | grep " /mnt "Interpretation
A noexec or ro mount option blocks execution regardless of mode bits.
Check for mandatory access control denials
dmesg | grep -i deniedInterpretation
SELinux and AppArmor log denials that chmod cannot fix.
Root cause
Unix permission checks consider ownership class (user, group, other) and require execute on every parent directory to traverse a path. chmod changes mode bits but not ownership, mount options, or MAC policy, so a denial can persist when the real obstacle is a different layer.
Fix
Fix ownership with chown so the user is in the right permission class, grant execute on parent directories as needed, and remove noexec or remount rw where appropriate. For SELinux or AppArmor, adjust labels or policy rather than disabling enforcement. Never use blanket chmod 777 as a fix.
Verify the fix
Re-run the command; it executes or the write succeeds, and namei shows execute permission on the file and all parent directories, with no new MAC denial in dmesg.
Symptoms
A command fails with Permission denied even after chmod +x. The mode bits look correct, which means another layer is refusing access.
Diagnosis
Inspect the whole path and mount options:
namei -l /data/script.sh
mount | grep /data
dmesg | tail | grep -i denied
Fix
Correct ownership and traversal, not just file mode:
chown app:app /data/script.sh
chmod 750 /data/script.sh
chmod +x /data # parent must be traversable
Remove noexec if the mount is the blocker, and fix MAC labels rather than disabling SELinux.
Verification
Run the script; it executes. namei confirms execute permission on the file and every parent directory, and dmesg shows no new denial, proving access now succeeds.
Four Layers That Can Deny Access
Permission denied in Linux is not a single check but four that run in order: the user-versus-group-versus-other class on the file, the same check on every parent directory for traversal, mount flags such as noexec or read-only, and mandatory access control from SELinux or AppArmor. chmod only touches the first, so it cannot fix a missing parent execute bit, a noexec mount, or an MAC denial. namei -l shows the whole path at once, mount output shows filesystem flags, and dmesg shows MAC denials, which together pinpoint the layer that is actually refusing access before you change anything.