Right, let’s talk about one of the most brilliantly confusing and misunderstood parts of Unix file permissions: the directory execute bit. You’ve probably wrapped your head around read and write for files, but directories? They play by a different set of rules, and the execute permission is the star of this particular show. It’s not about running a directory like a script (thankfully, that’s not a thing), it’s about granting the key to the kingdom: access.

Think of a directory not as a folder containing stuff, but as a map to the stuff inside. The read (r) bit on a directory lets you see that map. You can list the filenames with ls. But the execute (x) bit is what lets you use the map. It grants you permission to access the inodes of the files and subdirectories within. No x bit, and that directory is a locked door, no matter what permissions the files inside have.

The Golden Rule of Access

Here’s the absolute, non-negotiable rule you must burn into your memory: To access any file or subdirectory within a directory, you must have execute permission on every single directory in the absolute path leading to it.

Let’s say you want to read /home/zaphod/important_plans/secret.txt. Your user needs x on /, x on home, x on zaphod, and x on important_plans. Fail any one of these checks, and you’re denied. It doesn’t matter if secret.txt is world-readable (chmod 644). If you can’t traverse the path to get to it, the file might as well not exist.

Let’s make a lab to prove this. We’ll create a deeply nested file and then start slamming doors.

mkdir -p project/secret_stuff/docs
echo "The answer is 42" > project/secret_stuff/docs/the_answer.txt
sudo chmod 644 project/secret_stuff/docs/the_answer.txt  # Make the file itself readable
ls -l project/secret_stuff/docs/the_answer.txt # You can see it, good.
cat project/secret_stuff/docs/the_answer.txt # You can read it, good.

# Now, let's remove the execute bit on the 'secret_stuff' directory.
sudo chmod 664 project/secret_stuff/  # rw-rw-r-- (no execute for anyone)
ls project/secret_stuff/  # You'll probably get 'permission denied'
cat project/secret_stuff/docs/the_answer.txt  # This will also fail with 'permission denied'

See? You were locked out. The system didn’t even get to check the permissions on the_answer.txt because it couldn’t get past the secret_stuff door.

The Curious Case of ls Without Execute

This is where it gets a bit weird. What if you have read but not execute on a directory? You can run ls -l on the directory itself, and it will bravely try to list the contents. But because it can’t access the inodes of the files inside to get their metadata (permissions, size, etc.), it will fail spectacularly. You’ll see the filenames (that’s the read bit working) but all the other info will be replaced by question marks, and you’ll get a load of error messages. It’s a sad and useless state of affairs.

sudo chmod 604 project/secret_stuff/  # Give read, but no execute, to the world
ls -l project/secret_stuff/
# You'll see something like:
# ls: cannot access 'project/secret_stuff/docs': Permission denied
# total 0
# d????????? ? ? ? ?            ? docs

Told you it was weird. The designers basically gave you just enough rope to hang yourself with this one.

How Inheritance Actually Works (Spoiler: It Doesn’t)

Here’s the part everyone gets wrong: POSIX permissions do not have inheritance. I know, I know, you’ve heard the term. But it’s a lie. The system does not proactively set permissions on new items based on their parent directory. It just doesn’t.

What actually happens is governed by your umask. This is a process-level setting that tells the system which permissions to subtract from the default permissions when you create a new file or directory. The default for a file is usually 666 (rw-rw-rw-) and for a directory is 777 (rwxrwxrwx). A common umask is 022, which subtracts the write bit for group and others (—-w–w-). So: 666 & ~022 = 644 (rw-r–r–) 777 & ~022 = 755 (rwxr-xr-x)

The parent directory’s permissions are irrelevant for this calculation. Its only job is to hold the items. The inheritance is a myth. The new item’s permissions are determined solely by the mode you asked for (e.g., mkdir -m 755) minus the active umask.

Best Practices and Pitfalls

  1. Directories should almost always have the execute bit set. A directory without x is usually a mistake. The standard permission for a directory is 755 (rwxr-xr-x) if you want others to list its contents, or 711 (rwx–x–x) if you just want them to be able to access known paths (e.g., for a web root where index.html is known, but you don’t want users listing all your /cgi-bin/ files).

  2. The rwx Triple is a Package Deal. For directories, r and x are symbiotic. Having one without the other is either useless (r without x) or very restrictive (x without r). Set both.

  3. Your umask is Your Friend. Set a sane umask in your shell’s startup file (like ~/.bashrc). umask 022 or the more restrictive umask 077 (which gives no permissions to group and others) are the standard choices. This is your first line of defense against accidentally creating a world-writable file.

  4. The Sticky Bit (t) on /tmp. This is a brilliant exception to the rule. On a world-writable directory like /tmp (mode 1777), the sticky bit means a user can only delete or rename files they themselves own. It’s what keeps me from deleting your temporary files and you from deleting mine, even though we both have full rwx access to the directory. It’s a fantastic design for shared spaces.