Check file permissions on a Mac: reading the letters in the row

Most searches for how to check file permissions on a Mac start with an error message rather than curiosity. Something refused to open, a script could not write where it was told to, or a folder copied from another machine now behaves as though it belongs to nobody. The permission row is the first place to look, and it answers roughly half of those cases. The other half fail for reasons that never appear in that row at all, and knowing which half is which saves a great deal of time spent running chmod at problems it cannot fix.

This covers how to read the row, the octal form of the same information, why directories follow different rules from files, and the separate layers macOS applies on top.

Reading the row that ls -l prints

The long listing form puts everything on one line:

ls -l report.pdf
-rw-r--r--  1 admin  staff  20480 Sep 17 18:00 report.pdf

The first ten characters are the whole permission story for the POSIX layer.

The first character is the type. A hyphen means a regular file, d means a directory, l means a symbolic link. Getting this wrong is common with links, because a link and its target have independent permissions and the link's own row is almost never the interesting one.

The remaining nine characters are three groups of three, and each group reads rwx in that fixed order with a hyphen where the right is absent.

Group Applies to
Characters 2 to 4 The owner, named in the third column
Characters 5 to 7 The group, named in the fourth column
Characters 8 to 10 Everyone else

So -rw-r--r-- means the owner may read and write, and the group and everyone else may only read. The owner in the example is admin and the group is staff. Those two columns matter as much as the letters, because the letters only say what each category may do, not which category the reader falls into. Running id -Gn lists the groups the current account belongs to, which is how to tell whether the middle triad or the last one applies.

One habit is worth forming early. Check the folder as well as the file, because the folder's row governs operations that feel like they belong to the file.

The same thing as a number

The three letter groups are three digits, with read worth 4, write worth 2, and execute worth 1. rw- is 6, r-x is 5, --- is 0. That is the entire translation, and it is why 644 and -rw-r--r-- are the same sentence written twice.

Both forms can be printed at once:

stat -f "%Sp %Su %Sg %Lp" report.pdf
-rw-r--r-- admin wheel 644

The octal form matters because chmod accepts it, and because default permissions are expressed that way. New files are created with the permissions allowed by the shell's umask, which subtracts from the maximum. Running umask with no arguments prints the current value, and on a standard macOS account it is 022. That is why a freshly created file arrives as 644 rather than 666: the mask removes the write bit from group and from everyone else.

Knowing the default is what makes an odd value visible. A file at 600 came from something that deliberately tightened it. A file at 777 came from somebody trying to make a problem go away, and it is worth treating as a finding rather than a solution, since it grants write access to every account on the machine.

Directories do not behave like files

The same three letters mean different things on a directory, and this is where most confusion lives.

Read on a directory means the names can be listed. Execute means the directory can be traversed, which is what any operation on a file inside it requires. A directory with read but not execute produces a listing that half works:

ls broken-dir
ls: fts_read: Permission denied
f

The name f is visible because the read bit is set. Nothing further about it can be discovered, because reaching the file itself needs the execute bit. A directory set to 444 behaves exactly this way, and the symptom looks like a corrupted folder rather than a permission setting.

Write on a directory controls the list of names, not the contents of the files. This is the rule that catches people out. Renaming, moving, and deleting a file are all changes to the directory's list of names, so they need write permission on the directory and none at all on the file:

mv read-only-dir/f read-only-dir/g
mv: rename read-only-dir/f to read-only-dir/g: Permission denied

The file in that example was perfectly writable. The error names the file, which sends the reader to check the file's row, where nothing is wrong. When a rename or a delete is refused, check the enclosing folder first. The reverse also holds: a read only file inside a writable folder can be deleted without complaint, which surprises people who expected the file's own permissions to protect it.

Two different refusals

macOS produces two error messages here and they point at different layers.

Message Layer What to check
Permission denied POSIX permissions The row from ls -l, plus the enclosing folder
Operation not permitted Something above POSIX ACLs, file flags, system protection, privacy grants

The second message is the one that wastes time, because chmod does not fix any of its causes. Three examples show the range.

A path inside the system volume refuses writes even for the root account:

touch /System/test
touch: /System/test: Operation not permitted

The directory's own row shows drwxr-xr-x owned by root, which reads as though root may write. System Integrity Protection refuses anyway, and its state can be confirmed with csrutil status.

A locked file behaves the same way. The Locked checkbox in Get Info sets a file flag, and flags are visible with a different option:

ls -lO locked.txt
-rw-r--r--@ 1 admin  wheel  uchg 0 Sep 26 11:31 locked.txt

The uchg flag is what makes rm report "Operation not permitted" on a file whose permissions allow everything. Clearing it takes chflags nouchg rather than any change to the letters.

The third case is the privacy layer. Access to folders such as Desktop and Documents, and to the disk as a whole, is granted per application in the privacy controls in System Settings, and a terminal that has not been granted it gets refused regardless of who owns the files. A script that works when run one way and fails when run from a scheduled job is usually this, not permissions.

The two characters after the letters

A row can carry a marker in the eleventh position, and each one points at a layer the nine letters do not describe.

An @ means extended attributes are present. These hold Finder tags, download provenance, quarantine state and similar metadata. List them with xattr -l. They rarely block anything, but the quarantine attribute is what produces the dialog about an app downloaded from the internet.

A + means an access control list is attached. ACLs are evaluated before the POSIX letters and can both grant and deny, which is how a file that looks writable refuses to be deleted. They are shown with a different flag:

ls -le protected.txt
-rw-r--r--@ 1 admin  wheel  1 Sep 26 11:24 protected.txt
 0: group:everyone deny delete

That single ACL entry denies deletion to everyone, and no reading of -rw-r--r-- would predict it. Finder applies entries like this in several places, including items restored from a backup and folders inside shared volumes, which is why a file that reports itself as writable while refusing to be deleted is a normal thing to encounter rather than a sign of damage.

Between them, ls -le and ls -lO and xattr -l cover everything the plain listing leaves out, and running all three on a file that is behaving strangely takes a few seconds. The reason to keep a shell next to the folder rather than in a separate window is that these checks are worth running the moment something looks wrong, and a workflow that is described in the feature notes rather than assembled by hand.

Checking a whole folder instead of one file

Diagnosing a single file is the easy case. A folder copied from another machine, restored from a backup, or handed over by a colleague usually has a mixture, and reading rows one at a time does not find the outliers.

Printing the octal value next to every path turns the folder into something that can be scanned:

find . -type f -exec stat -f "%Lp %N" {} \;
644 ./notes.md
777 ./import.csv
700 ./deploy.sh

Anything that is not the expected default stands out immediately, and 777 in that list is worth investigating rather than leaving alone.

Two narrower searches answer the two questions that come up most often. Files writable by every account on the machine are found by matching that bit directly:

find . -type f -perm -o+w

Files that belong to somebody else, which is the usual aftermath of copying a folder as one account and using it as another, come from an ownership test:

find . ! -user "$(whoami)"

An empty result from either is a useful answer, not a failed command. Running both before changing anything turns a vague suspicion about a folder into a list of specific paths, and a list of paths can be fixed precisely rather than with a recursive change that touches everything.

Checking the same thing from Finder

Get Info shows a simplified view of the same data, and it is the right tool when the goal is to change something rather than to diagnose it. Select the item, choose File followed by Get Info, and expand Sharing and Permissions. The settings available per user or group are four:

  • Read and Write, allowing the item to be opened and changed
  • Read only, allowing it to be opened but not changed
  • Write only, which turns a folder into a drop box that others can copy into but not open
  • No Access, which blocks the item entirely

Two controls at the bottom of that window do work that is tedious from the command line. "Apply to enclosed items" pushes the current settings down through an entire folder, and "Make [user name] the owner" changes ownership after adding the account to the list. Both are reachable from the action menu at the bottom of the Info window, and both require unlocking the section first.

The safety net is worth knowing about before using either. While the Info window is still open, the action menu also offers "Revert changes", which undoes every permission and ownership change made since the window was opened. Once the window closes, that option is gone, so the moment to reconsider a recursive change is before dismissing the dialog. Questions about which of these is the right lever come up regularly, and the common ones are collected in the questions readers send in.

What to check first

Run ls -le on the item and on the folder that contains it, and read the error message rather than the permissions: "Permission denied" points at the letters, and "Operation not permitted" points at an ACL, a locked flag, system protection, or a privacy grant. Resist widening permissions to make a refusal stop, because the refusals that respond to that treatment were rarely the problem. Keeping the folder and a shell in the same window, the way Atriens is arranged, is what makes running those three checks the reflex instead of the last resort.

Frequently asked questions

What does the `@` symbol after the permissions mean?

It means the file carries extended attributes, which hold metadata such as Finder tags, download provenance and quarantine state. List them with xattr -l on the file. Extended attributes rarely block access on their own, though the quarantine attribute is what triggers the warning about software downloaded from the internet.

Why can a file be deleted when its permissions say read only?

Because deleting a file is a change to the folder's list of names, not to the file. Write permission on the enclosing folder is what allows a delete or a rename, and the file's own permissions are not consulted. The same rule explains why a writable file inside a read only folder cannot be renamed.

What is the difference between "Permission denied" and "Operation not permitted"?

"Permission denied" comes from the POSIX permission bits shown by ls -l, so checking the file and its folder will explain it. "Operation not permitted" comes from a layer above that: an access control list, a locked file flag, System Integrity Protection, or a privacy grant that the app running the command does not hold.

Should permissions be set to 777 to fix an access problem?

It is best treated as a diagnostic step rather than a fix, because 777 grants read, write and execute to every account on the machine. If it makes the problem go away, the real answer is usually a narrower change to ownership or to the enclosing folder. If it does not, the refusal was never a POSIX permission issue.

Back to all posts