ls -l Permission Explainer

Paste a block of ls -l straight from the terminal and every line comes back decoded, with the octal mode alongside and the dangerous combinations called out. Counting rwx characters by eye works fine until the listing is forty lines long and one of them is wrong.

Read ls -l Output — Decode rwx Strings, Octal Modes and Risky PermissionsBuildFigure

Ten characters, read in four pieces

-rwxr-xr-- is one type character followed by three groups of three. The first character says what the entry is: - regular file, d directory, l symlink, c and b character and block devices, p a named pipe, s a socket. Then owner, group and other, each as read, write, execute in that fixed order, with - for absent. The example is 754: owner 7, group 5, other 4.

An eleventh character sometimes appears. On Linux a trailing + means a POSIX ACL is attached and the nine bits are not the whole story — getfacl shows the rest. On macOS an @ means extended attributes, which are metadata rather than access rules, and a + there means an ACL as well. Either way the effective access can differ from what the rwx string implies, in both directions.

The link line is a lie, deliberately

Symlinks almost always show lrwxrwxrwx, and that has nothing to do with whether you can read what they point at. The kernel does not consult a symlink's own mode; it resolves the link and applies the target's permissions. Chasing a permission problem on the link itself is wasted effort — follow it with ls -lL or readlink -f and check the far end.

Where the octal comes from and why it is worth converting

Each group of three is a binary number: r is the 4 bit, w the 2 bit, x the 1 bit. rwx is 4+2+1 = 7, r-x is 5, rw- is 6, r-- is 4. Converting in your head takes about a week of practice and is worth the week, because octal is the form you type into chmod and the form that appears in Ansible, Dockerfiles, Git's index and every deployment tool. The reverse direction matters as much: knowing that 640 means the group can read and nobody else can see it at all lets you review a playbook without running it.

What a listing will not tell you

This decodes the nine bits and the three special bits, and there are several layers it cannot see. SELinux and AppArmor can deny an operation that the mode permits, and their denials appear in the audit log rather than as anything visible in ls. Mount options override everything below them — a filesystem mounted ro or noexec makes the write and execute bits decorative. Capabilities set on a binary with setcap grant privilege without setuid ever appearing. Immutable and append-only attributes, set with chattr and visible only through lsattr, will refuse a write from root itself. And on a network filesystem, the server's idea of who you are may not match the client's at all.

So a clean listing is evidence, not proof. When the mode looks correct and the operation still fails, the next places to look are dmesg and the audit log, then mount, then lsattr — usually in that order, because that is roughly the order of how often each one turns out to be the culprit.

Questions people ask

There is a + or an @ at the end of the permission string.

On Linux, + means a POSIX ACL grants access beyond the three standard groups; read it with getfacl and set it with setfacl. On macOS, @ means extended attributes are present — the quarantine flag on a downloaded file is the one people meet first — and is listed by ls -l@. Neither is reflected in the octal mode, so a file with an ACL can be reachable by accounts the rwx bits appear to exclude.

Why does the same file show a different owner over NFS?

Because ownership travels as numeric UID and GID, and the mapping from number to name is local to each machine. If uid 1000 is sam on the client and deploy on the server, the same file shows a different name on each side while being the same file with the same access. NFSv4 with idmapping, or simply keeping the numbers consistent across the fleet, is the fix. Squashing options such as root_squash add another layer, remapping root to nobody on the wire.

Everything is executable after copying from a USB stick.

FAT and exFAT store no Unix permissions, so the driver invents them from the mount options — commonly 777 for everything, sometimes 755. The bits you see are a mount-time default, not anything that was on the disk. Copy the files, then run chmod -R u=rwX,go=rX over the destination to put sane modes back; the capital X keeps text files from staying executable.

Is my pasted output sent anywhere?

No. The parsing is JavaScript running in this page, with no network call in it. Disconnect and it produces the same output. That matters here because ls -l output routinely contains internal hostnames, account names and full paths that you would not want in a third party log.

Related