Chmod a directory on a Mac, and why folders need the run bit

Running chmod on a directory looks like the same operation as running it on a file. The command is identical, the number looks identical, and the result is not. Every one of the nine bits means something different once the target is a folder, and two of the three permission letters mean something so different that carrying over the file reading is the direct cause of the most common folder permission failures on a Mac.

The macOS manual page is precise about this, and the precision is easy to read past. It does not say the bit allows execution and leave it there. For directories it uses a different word, and that word is the key to everything below.

The nine bits mean different things on a folder

The absolute mode is built by adding octal values. The manual page lists 0100 as allowing execution by owner for files, and, for directories, allowing the owner to search in the directory. 0010 and 0001 say the same for group members and for others. Search is the accurate word. It is the permission to look up a name inside the directory and pass through it to whatever the name refers to.

That splits cleanly from read. Read on a directory is the permission to enumerate the names it contains. The two are independent, and all four combinations are meaningful.

r-x is the ordinary case. Names can be listed and anything inside can be reached. r-- is the one that produces a folder people describe as broken: ls prints the names, and every attempt to open one of them is refused, because opening a file means passing through the directory that holds it. --x is the opposite and is deliberately useful. Nothing can be enumerated, and anyone who already knows a filename can reach it, which is how a directory is made into a set of unguessable but shareable paths. --- refuses both.

Write is the third letter, and it is the one people misread most often. It has nothing to do with the contents of the files inside.

Write on a directory governs the children, not their contents

The manual page states that the w permission on directories permits file creation, relocation and copying into that directory. Creation, relocation, copying. Not editing.

The consequence that surprises people is deletion. Removing a file is a change to the directory that lists it, so it is the directory's write bit that decides whether the removal is allowed, not the file's own mode. A file at 444 sitting in a folder at 755 owned by the current user can be deleted without resistance. Setting a file to read-only is therefore not a defence against losing it, and anyone who has tried to protect a file that way has protected the wrong object.

The manual page adds a second clause in the same sentence that is worth reading carefully, because it differs from what Linux documentation tends to say: files created within the directory itself will inherit its group ID. On macOS that inheritance is the behaviour of the directory, not something the setgid bit has to be set to enable. A folder shared with a group therefore hands its group to new files without any special mode, which is one fewer thing to configure and one more thing to check when the group on a new file is not what was expected.

Editing an existing file, by contrast, is governed entirely by that file's own mode. A directory at 555 can still contain files being written all day, as long as their own write bits allow it. What 555 stops is adding, renaming and removing.

Traversal is checked at every level of the path

This is the single most useful fact for diagnosis, and it explains most of the cases where a mode change appears to do nothing.

Opening /Users/name/Projects/site/index.html requires search permission on /, on /Users, on /Users/name, on Projects and on site. Every one. A single directory without the relevant search bit makes everything beneath it unreachable, and the mode on the file at the end of the path is irrelevant at that point.

The system call documentation says so directly. chmod(2) lists EACCES as the error returned when search permission is denied for a component of the path prefix. The failure is attributed to a component of the path, not to the target.

The practical move is to read the whole path before changing anything. ls -ld on each ancestor in turn shows where the search bit stops, which is almost always one level higher than the folder someone has been repeatedly re-running chmod against. Running the same number on the leaf folder for a third time cannot succeed when the missing bit is two levels up.

The fourth digit is mostly a directory feature

Three-digit modes are the habit, and the fourth digit in front is where directories get behaviour that files do not have. Writing only three digits clears all of it, which is worth knowing before applying a flat number to a folder that had one of these set.

The sticky bit is 1000, and on a directory it is genuinely useful. The manual page sticky(7) describes a directory with the sticky bit set as an append-only directory, or more accurately a directory in which the deletion of files is restricted. A file in such a directory may only be removed or renamed by a user who has write permission for the directory and who is the owner of the file, the owner of the directory, or the super-user. That is the mechanism behind /tmp, which must be publicly writable while denying users the licence to delete each other's files. Any user may create a sticky directory, so this is available for a shared drop folder without administrator involvement. One caveat from the same page: neither open(2) nor mkdir(2) will create a file with the sticky bit set, so it has to be applied afterwards.

The setgid bit is 2000. On macOS its directory role is smaller than Linux habits suggest, because group inheritance on new files already happens, as the chmod manual page states under the write permission.

The setuid bit is 4000, and the manual page describes directories with it set as forcing all files and subdirectories created in them to be owned by the directory owner rather than by the user ID of the creating process. That description carries an explicit condition: if the underlying file system supports this feature. It points at a mount option, and that option does not appear in the macOS mount manual page, so this is not behaviour to design around on a Mac.

What each mode does to a folder

Mode Letters What it allows Where it fits
755 rwxr-xr-x Owner adds and removes, everyone lists and traverses The default for directories under mask 022
750 rwxr-x--- Same, with other shut out completely A project folder on a machine with several accounts
700 rwx------ Only the owner can list or traverse Private folders, key directories
775 rwxrwxr-x Group can also add, rename and remove A folder a real, narrow group works in
711 rwx--x--x Others can traverse but not list A parent folder whose children are shared individually
1777 rwxrwxrwt Anyone adds, only owners remove their own A shared drop folder
644 rw-r--r-- Listing only, nothing inside can be opened Nowhere, and it is the usual accident

The 775 row is the one to think twice about on a Mac. Files here typically belong to the group staff, which is much broader than the word group implies, so granting the group the ability to create, rename and delete inside a folder is closer to granting it to everyone than the digits suggest. A narrow group created for the purpose is the version of 775 that means what it appears to mean.

Applying one number to a whole tree

Recursion is where file modes and directory modes collide, and the collision goes in both directions.

A flat chmod -R 755 on a project gives the directories what they need and marks every document, image and source file inside as executable, which produces no error and surfaces much later as tools treating data as programs. A flat chmod -R 644 does the opposite and worse, stripping the search bit from every directory so the tree becomes unopenable, and stripping the execute bit from every program in it.

The manual page supplies the intended answer in its own examples. The symbolic permission X sets the execute or search bit only if the target is a directory, or if any execute bit is already set in the original mode, and the listed example =rw,+X is described as setting read and write to the usual defaults while retaining any execute permissions currently set. Applied recursively that gives every directory its search bit, keeps already-executable files executable, and promotes nothing else. The manual page also notes that X is only meaningful with + and is ignored otherwise, which is why it appears in that combination and not alone.

Two details matter on any recursive run. -P is the default for symbolic links, meaning none are followed; -L follows all of them and -H follows only those named on the command line. And the manual page warns about wildcards such as .* unintentionally matching the .. hard link to the parent directory, which is how a mode change escapes the folder it was pointed at. There is no undo, so reading the current modes before overwriting them is cheaper than reconstructing them from memory. The comparison page covers which tools in this category show a mode change before committing it.

The layer a directory mode cannot express

Nine bits cannot say what a shared folder usually needs to say, and on macOS there is a second mechanism that can.

Access control lists carry rights that apply only to directories, and the chmod manual page names them: list for listing entries, search for looking up files by name, add_file, add_subdirectory, and delete_child for deleting a contained object. That list contains the distinction a mode cannot make. In mode terms, the ability to add a file and the ability to delete someone else's file are the same write bit. In ACL terms they are add_file and delete_child, and they can be granted separately. The sticky bit is the mode-level approximation of the same idea, and it is coarser.

Directories are also where ACL inheritance lives. The manual page lists four inheritance words that may only be applied to directories: file_inherit and directory_inherit, which pass an entry down to files and to subdirectories, limit_inherit, which clears directory_inherit in the entry that is inherited so nesting does not continue indefinitely, and only_inherit, where the entry is handed to created items but is not considered when the directory's own ACL is processed. A mode has no equivalent. That is why a folder configured once with an inherited ACL keeps behaving correctly for files created in it months later, and why a folder configured with a mode alone does not.

Entries are added with +a, which inserts into the canonical location, and the canonical order places local deny entries above local allow entries, with inherited entries below both. A trailing + on the permission string in a listing is the sign an ACL exists, and ls -le prints it. Removing one takes chmod -N for the whole list, chmod -a for a single entry, chmod -I to remove all inherited entries, or chmod -i to clear the inherited flag from every entry while keeping them. Setting a numeric mode does none of this, which is the explanation for a folder that keeps behaving in a way its digits contradict. Keeping the mode, the owner and the ACL marker visible next to the folders while working is what the views described on the Features page are for.

What to change first

Read the path before changing the folder: ls -ld on each ancestor shows where the search bit actually stops, and that is usually a level above the folder being blamed. If a tree needs correcting, use =rw,+X rather than a flat recursive number so directories get their search bit and data files are not left executable. And if the goal is a folder several people add to without deleting each other's work, that is the sticky bit or an inherited ACL, not a larger number. Keeping the modes, the owners and the ACL markers in the same window as the folders is what Atriens is built for.

Frequently asked questions

Why does a folder need the execute bit at all?

Because on a directory that bit is not about running anything. The macOS manual page calls it search, and it is the permission to look up a name inside the directory and pass through it. Without it a folder can be listed and nothing inside it can be opened, since opening any file requires passing through every directory on its path.

How can a folder be made visible to others without letting them see the file names?

Give it --x for the users in question, which is the third digit 1 with no read bit. Names cannot be enumerated and anyone who already knows a filename can reach it. Mode 711 is the usual spelling of that for a parent folder whose contents are shared individually.

Why can someone delete a read-only file from a folder?

Because deletion changes the directory that lists the file, not the file itself, so it is the directory's write bit that decides. The manual page describes w on a directory as permitting file creation, relocation and copying into it. Restricting deletion inside a writable shared folder is what the sticky bit is for.

What is the right way to chmod a directory and everything inside it?

Use =rw,+X recursively rather than a flat number. The manual page describes X as setting the execute or search bit only if the target is a directory or an execute bit is already set, so directories get their search bit, existing programs stay runnable, and documents are not marked executable. A flat recursive 755 marks every data file executable and a flat recursive 644 makes the tree unopenable.

What does the 1 in 1777 do?

It is the sticky bit. sticky(7) describes a directory with it set as one in which deletion is restricted: a file may only be removed or renamed by a user who has write permission for the directory and who owns the file, owns the directory, or is the super-user. It is how /tmp stays publicly writable without users deleting each other's files.

Why does a folder keep behaving differently from what its mode says?

Usually an access control list. A trailing + in a listing marks one, and ls -le prints the entries, whose canonical order puts deny entries above allow entries. Directories can also carry inherited entries that apply to items created inside them later, which no numeric mode can express. chmod -N removes the list, chmod -I removes only the inherited entries.

Back to all posts