Changing permissions using symbolic mode and numeric mode in Linux and Unix (macOS)

Wednesday, July 17, 2024 at 11:38 AM | 15 min read

Last modified on Tuesday, June 9, 2026 at 10:07 PM

#linux, #macOS, #unix, #file permissions, #numeric mode, #symbolic mode

Person holding a card, used as a metaphor for access permissions

Photo by Danielle Rice on unsplash.com

Table of Contents

A little while ago I took a Cybersecurity certification course in which I learned about permissions and their importance in system access control. This made me want to become proficient in permissions security. And as with everything else, as I learn, create, or experience things tech, I like to write it down. Which brought me to writing this post.

Two approaches to set or change permissions in Linux and Unix (macOS)

In Linux and Unix (macOS), there are two ways of setting or changing file and folder permissions. One is in symbolic mode, and the other is in numeric mode.

Revisiting the ls -l command to view current file or folder permissions

In order to set or change file or folder permissions in Linux or Unix (macOS), I first have to know what are the current permissions of those files or folders. I can find that out with the ls -l or ls -la command. The difference between the two is that ls -l will list all visible files or folders, but ls -la will include hidden files or folders.

So let's say I want to list the files and/or folders in a directory called "text-files" on my Linux Mint OS in VirtualBox on Windows 11. I would run the following:

maria@maria-VirtualBox:~/Desktop/text-files$ ls -l text-files

And this would return the following in Terminal:

total 72 -rw-rw-r-- 1 maria maria 47 Jul 12 21:28 duplicates.txt prw-rw-r-- 1 maria maria 0 Jul 14 07:28 first-named-pipe -rw-rw-r-- 1 maria maria 21800 Jul 14 18:22 history2.txt -rw-rw-r-- 1 maria maria 21800 Jul 14 18:22 history.txt -rw-rw-r-- 1 maria maria 1477 Jul 13 12:23 list-home-directory.txt -rw-rw-r-- 1 maria maria 258 Jul 14 10:05 names1.txt -rw-rw-r-- 1 maria maria 186 Jul 14 14:56 names2.txt -rw-rw-r-- 1 maria maria 38 Jul 14 19:52 regex.txt -rw-rw-r-- 1 maria maria 86 Jul 14 13:49 tee.txt

And if I ran:

maria@maria-VirtualBox:~/Desktop/text-files$ ls -lah text-files

the following would be returned in Terminal:

total 80K drwx------ 2 maria maria 4.0K Jul 17 08:03 . drwxr-xr-x 4 maria maria 4.0K Jul 15 10:38 .. -rw-rw-r-- 1 maria maria 47 Jul 12 21:28 duplicates.txt prw-rw-r-- 1 maria maria 0 Jul 14 07:28 first-named-pipe -rw-rw-r-- 1 maria maria 21800 Jul 14 18:22 history2.txt -rw-rw-r-- 1 maria maria 21800 Jul 14 18:22 history.txt -rw-rw-r-- 1 maria maria 1477 Jul 13 12:23 list-home-directory.txt -rw-rw-r-- 1 maria maria 258 Jul 14 10:05 names1.txt -rw-rw-r-- 1 maria maria 186 Jul 14 14:56 names2.txt -rw-rw-r-- 1 maria maria 38 Jul 14 19:52 regex.txt -rw-rw-r-- 1 maria maria 86 Jul 14 13:49 tee.txt

You will notice that there is a difference in stdout between ls -l and ls -lah. Why? Because the first iteration of ls only includes visible files and or folders, therefore not including any dots (.), represented by the -a flag.

ls -lah includes dots, which here represents either the current directory (.) or the parent directory (..), and the stdout is human readable, represented by the -h flag. This means that the actual size is indicated, instead of simply a number. Here it is total 80K instead of simply total 80.

-l means "long list", which most importantly includes file or folder permissions. -l, or "long list format", includes file (or folder) permissions, the number of links, owner name, owner group, file size, time of last modification, and the file or directory name. This option is used along with many other options, including here: ls -lah, for example.

Why does the current directory, "text-files", contain two links (2) in the second column from the left? It is 2 because the "text-files" directory can be either referred to "by name" in the parent directory (Desktop) or in itself. And column two from the left represents the number of hard links that the directory contains.

What exactly is a hard link in Linux? A hard link is a link between the filename (or directory) and the actual data stored on the filesystem. The hard link here is "." and the actual data stored on the filesystem is the contents of "." This hard link is also represented by the directory name "text-files", and "clicking" on the "text-files" file icon, for example, would take you to the actual data stored inside the "text-files" folder.

What do file permissions (r, w, x) actually permit us to do?

The "read permission" (r) allows me to access the file's contents. I can use commands such as cat, tee, or less to stdout the file's contents. I can also use text editors like Vim to read the file contents. The read permission (r) is required in order to make copies of a file.

The "write permission" (w) allows me to change the contents of a file. For example, I can use the redirect operator (>) or the redirect append operator (>>) to change a file's contents. Without write permissions, I cannot change a file's contents.

The "execute permission" (x) permits me to execute the contents of a file. Typically, the execute permission is required for executables such as bash or zsh shell scripts, and Python programs, for example.

Applying the principle of least privilege when setting permissions in macOS

File permissions in Linux (or Unix for that matter) determine who can access files and directories on the system and how. Permissions are central to safeguarding systems and networks. In that way, as frustrating as macOS can be with its System Integrity Protection (SIP), I know that my data and sensitive information are safe. But for those operating systems like Linux, it is a strong permissions implementation which helps shield the OS.

macOS SIP applies the principle of least privilege. For example, I have to be able to access and use both Apple pre-installed applications and applications I have downloaded from the internet. And I can execute them. I can also delete the applications that I have downloaded from the internet, but I can't modify or delete any pre-installed Apple applications even though I get back the following when running ls -ld Applications:

drwxrwxr-x 86 root admin 2752 Jun 7 18:31 Applications

What does this mean? On macOS, the /Applications folder has a special permission setup:

  • The owner is root with read/write/execute permissions.
  • Group is represented by admin with read/write/execute permissions.
  • Others have read/execute permissions only.

The key is in the group admin. Any user with administrator privileges on macOS is automatically a member of the admin group. So even though root owns the directory, my admin account has write permission to the folder itself via group membership. And write permission on a directory is what allows admin (me) to delete entries within it.

However, to delete a file inside /Applications, what matters is having write permission on the containing directory, in this case Applications, not ownership of the file itself.

An individual app bundle (e.g., Google Chrome.app) may be owned by root, but I don't need to own or write to the file. I need write access to /Applications, which I have as an admin.

So macOS is intentionally designed this way. admin users like myself can manage (install/remove) applications without needing sudo, but I can't tamper with the internals of system-owned app bundles without escalating privileges. It's a clean balance between usability and security.

So macOS admins AND standard users are given only the privileges they need to access files, folders, and applications to get the job done.

But then how does setuid, setgid, and sticky bit fit into modern macOS?

Real world setuid use cases on macOS

setuid causes an executable1 to run as its owner (typically root) rather than the user who launched it. Genuinely still used, but almost exclusively by "Apple-blessed" system binaries: Real world use cases are:

setuidUse case
sudoThe canonical/standard example. It's setuid root so a regular user can invoke it and temporarily escalate.
passwdNeeds to write to protected credential stores, so it runs as root via setuid.
mountSome mount helpers use setuid to allow non-root users to mount specific filesystems.
suSwitching users requires root-level access to PAM2 (Pluggable Authentication Module) and credential stores.

For my own tools or scripts on macOS today, setuid is impractical. Between nosuid on APFS3 (Apple File System) volumes, SIP, and Gatekeeper, Apple has made it a system-internal mechanism rather than a general-purpose one.

Real world setgid use cases on macOS

setgid on an executable causes it to run with the group privileges of the file's group instead of the user's group. On a directory, it causes new files created inside to inherit the directory's group instead of the creator's primary group. It is more practical than setuid, especially with the directory variant:

setgidUse Case
Shared development directoriessetting setgid on a directory ensures every file created inside inherits the directory's group, not the creator's. Useful for team projects where multiple users share a group (e.g., staff or a custom dev group).
/usr/localHomebrew historically relied on group-based write access here so members of the admin group could install packages without sudo.
Log directoriesShared log directories in multi-user or multi-process setups often use setgid so all processes writing logs end up with consistent group ownership.

Real world sticky bit use cases on macOS

sticky bit is probably the most practically relevant on modern macOS:

sticky bitUse case
/tmpThe classic use case, and still active. /tmp (or /private/tmp on macOS) is world-writable but has the sticky bit set, so users can create files there but cannot delete each other's files.
Any shared writable directoryWhere you want users to manage only their own files follows the same pattern.

I can verify the sticky bit on /tmp with ls -la /private/ | grep tmp command which returns:

drwxrwxrwt 331 root wheel 10592 Jun 9 14:21 tmp

The t at the end is the sticky bit.

How does setuid, setgid, and sticky bit fit into Linux?

Real world setuid use cases on Linux

setuid use cases on Linux are more practical than on macOS:

setuidUse case
passwd, sudo, suSame as on macOS, these are the classics
pingOn many Linux distros ping is still setuid root (or uses capabilities as a modern alternative) to access raw sockets
crontabNeeds setuid to write to protected spool directories on behalf of users
atThe job scheduler, same reasoning as crontab
newgrpAllows users to switch their active group, needs setuid root to do so
pkexecPolicyKit's4 command executor, setuid root (famously exploited in the PwnKit vulnerability in 2022)
Custom toolsUnlike macOS, you can actually create your own setuid binaries on Linux filesystems, as long as the filesystem isn't mounted nosuid

I can find all the above on my Mint VM with:

find / -perm -4000 -type f 2>/dev/null

Real world setgid use cases on Linux

Both the executable and directory variants are widely used with setgid:

setgidUse case
write / wallTerminal messaging tools use setgid tty5 to write to other users' terminals
ssh-agentOn some distros ssh-agent uses setgid to access specific resources
/var/mailMail spool directories typically use setgid mail so mail delivery agents can write to them
Shared project directoriesExactly as on macOS but without the APFS/SIP restrictions getting in the way
/usr/local/share type directoriesShared resource directories commonly use setgid so the owning group is inherited automatically

I can find setgid use cases on Linux by running:

find / -perm -2000 -type f 2>/dev/null

Real world sticky bit use cases on Linux

Same core sticky bit use case as on macOS but it appears in more places:

sticky bitUse case
/tmp and /var/tmpworld-writable, sticky bit set, same as macOS
/run/lockShared lock file directory, and sticky bit prevents processes from deleting each other's locks
Any shared writable directory in multi-user environmentsLinux servers with many users rely on sticky bit heavily

I can find sticky directories with:

find / -perm -1000 -type d 2>/dev/null

Setting or changing file permissions in symbolic mode

As I discuss in my post entitled Shell script for turning your macOS laptop's WiFi off and on or my post entitled How to show hidden files or folders on macOS, you can set or change file or folder permissions using the chmod command.

Here, I will first talk about using the chmod command to set or change permissions in symbolic mode. But what does symbolic mode mean? And why permissions to begin with?

Symbolic mode refers to setting permissions using letters or symbols as opposed to numeric mode which sets permissions using numbers.

The chmod command, or "change mode", is used to change permissions for a file or directory on Linux or Unix.

The chmod command and syntax

chmod syntax is the following:

chmod [options] [mode] [File_name]

"options" refers to the flags that change the behavior of chmod.

"mode" refers to the permissions to be set, represented by either a three digit octal number or symbolic notation. ComputerNetworkingNotes describes the chmod command symbolic notation beautifully:

# symbolic notation syntax chmod [permission level] [+/-] [permission type] object

chmod is the command.

"permission level" refers to user, group, or other, which we want to update.

+/-. + is used to add permissions and - is used to remove them.

"permission type" refers to read (r), write (w), and execute (x) which we want to set.

Let's refer back to the stdout of ls -lah text-files example for reference:

total 80K drwx------ 2 maria maria 4.0K Jul 17 08:03 . drwxr-xr-x 4 maria maria 4.0K Jul 15 10:38 .. -rw-rw-r-- 1 maria maria 47 Jul 12 21:28 duplicates.txt prw-rw-r-- 1 maria maria 0 Jul 14 07:28 first-named-pipe -rw-rw-r-- 1 maria maria 21800 Jul 14 18:22 history2.txt -rw-rw-r-- 1 maria maria 21800 Jul 14 18:22 history.txt -rw-rw-r-- 1 maria maria 1477 Jul 13 12:23 list-home-directory.txt -rw-rw-r-- 1 maria maria 258 Jul 14 10:05 names1.txt -rw-rw-r-- 1 maria maria 186 Jul 14 14:56 names2.txt -rw-rw-r-- 1 maria maria 38 Jul 14 19:52 regex.txt -rw-rw-r-- 1 maria maria 86 Jul 14 13:49 tee.txt

Now let's change the permissions for the duplicates.txt file. The permissions for duplicates.txt are its default permissions that were assigned to it when I created the file. Whenever you create a file in Linux, -rw-rw-r-- are the permissions assigned to the file upon creation.

Note: the default permissions in macOS are slightly different. A bit more strict. If I create a file called test.txt and then run the ls -l test.txt command to check its default permissions, the following is returned as stdout in Terminal:

-rw-r--r-- 1 mariacam staff 0 Jul 17 17:42 test.txt

This is also the case when you create a shell script with the .sh extension, for example, so you cannot execute the script. The user who creates the file does not have x (execute) permissions by default. In order to give a user execute permissions to duplicates.txt for demonstration purposes only (I would not usually need to give a .txt file execute permissions) in symbolic mode, I would do the following:

chmod u+x duplicates.txt

Then I could run ls -l duplicates.txt, and the following would be returned in Terminal as stdout:

-rwxrw-r-- 1 maria maria 47 Jul 12 21:28 duplicates.txt

The first "-" identifies the file as a file. rwx represents user permissions, and now includes execute (x) permissions. groups still only has read and write (rw) permissions, and other only contains read (r--) permissions.

If I want to remove execute (x) permissions from duplicates.txt, I would do the following:

chmod u-x duplicates.txt

and then ran ls -l duplicates.txt, the following would be returned in Terminal as stdout:

-rw-rw-r-- 1 maria maria 47 Jul 12 21:28 duplicates.txt

Now the user only has read and write (rw) permissions again.

Let's say I wanted to change the permissions of duplicates.txt for other. Let's say I wanted to add write (w) and execute (x) permissions. I would do the following:

chmod o+wx duplicates.txt

And when I run ls -l duplicates.txt, the following would be returned in Terminal:

-rw-rw-rwx 1 maria maria 47 Jul 12 21:28 duplicates.txt

I can also add or remove permissions for two or three types of users (u, g, o) at once. For instance:

chmod ug+x duplicates.txt

And when I run ls -l duplicates.txt, the following is returned in Terminal:

-rwxrwxrwx 1 maria maria 47 Jul 12 21:28 duplicates.txt

Now let's say I want to remove execute (x) permissions from user (u), group (g), and other (o) all at once:

chmod ugo-x duplicates.txt

It returns the following in Terminal when I run ls -l duplicates.txt:

-rw-rw-rw- 1 maria maria 47 Jul 12 21:28 duplicates.txt

I can also use a, which stands for "all", instead of ugo:

chmod a-x duplicates.txt

And if I want to change back permissions to its default for duplicates.txt, I would do the following:

chmod o-w duplicates.txt

The following would be returned in Terminal as the stdout of ls -l duplicates.txt:

-rw-rw-r-- 1 maria maria 47 Jul 12 21:28 duplicates.txt

Setting or changing file permissions in numeric mode

I find symbolic mode faster for quick changes, but numeric mode makes the full permission set clearer at a glance.

Let's refer back to the stdout of ls -lah text-files example again.

total 80K drwx------ 2 maria maria 4.0K Jul 17 08:03 . drwxr-xr-x 4 maria maria 4.0K Jul 15 10:38 .. -rw-rw-r-- 1 maria maria 47 Jul 12 21:28 duplicates.txt prw-rw-r-- 1 maria maria 0 Jul 14 07:28 first-named-pipe -rw-rw-r-- 1 maria maria 21800 Jul 14 18:22 history2.txt -rw-rw-r-- 1 maria maria 21800 Jul 14 18:22 history.txt -rw-rw-r-- 1 maria maria 1477 Jul 13 12:23 list-home-directory.txt -rw-rw-r-- 1 maria maria 258 Jul 14 10:05 names1.txt -rw-rw-r-- 1 maria maria 186 Jul 14 14:56 names2.txt -rw-rw-r-- 1 maria maria 38 Jul 14 19:52 regex.txt -rw-rw-r-- 1 maria maria 86 Jul 14 13:49 tee.txt

This time, I am going to change permissions in numeric mode. I am going to change the permissions for duplicates.txt in numeric mode:

chmod 764 duplicates.txt

This results in the following stdout in Terminal when I run ls -l duplicates.txt:

-rwxrw-r-- 1 maria maria 47 Jul 12 21:28 duplicates.txt

So how did I come up with 764 to represent users, groups, and others permissions respectively? Numeric permissions works in the following way:

Octal notation Symbolic notation No permissions: 0 --- Execute permissions: 1 --x Write permissions: 2 -w- Write and execute permissions: 3 -wx Read permissions: 4 r-- Read and execute permissions: 5 r-x Read and write permissions: 6 rw- Read write and execute permissions: 7 rwx

Setting or changing directory permissions

Since everything is considered a file in Linux and Unix (macOS), changing directory permissions are basically the same in symbolic mode. However, directory permissions do affect the accessibility of its contents and therefore should be addressed here.

What do directory permissions (r, w, x) actually permit us to do?

The directory read permission (r) permits me to read the contents of a directory. This means that I can view the list of files within that directory. The directory read permission is required to be able to use the ls command, for example. This does not mean that I can view the actual contents of a file inside the directory. That depends on the permissions for the particular file.

The directory write permission (w) permits me to change the contents of a directory. And since I can change the contents of a directory, this means that I can add or remove files from that directory. Directory write permissions are needed to be able to use the mv (move or rename) or rm (remove) commands within the directory. I also need directory write permissions in order to use the touch command or redirect operators to create new files, or the cp command to copy files within the directory.

The directory execute permission is different from the file execute permission. The directory execute permission provides us access to the directory. directory execute permission lets us view extended file information in the directory (ls -l), lets us cd into the directory, or cd into the directory and then cd into a subdirectory.

If I don't have directory execute permission for a directory, it can result in limits on other permissions. For example, I can't add a new file to a directory (via the write permission) if I can't access the directory's metadata to store information for a new file. I cannot. That is why "directory-type files" are provided execute permissions by default. For example, if I were to create a subdirectory within the Desktop directory in Linux called script-files, its default permissions would be drwxrwxr-x. By default, the user and group would have rwx permissions, and other would have r-x permissions. In macOS, however, when I create a new directory, the default permissions are drwxr-xr-x. Again, the default permissions are a bit more strict. But all user types, users, groups, and others, all have directory execute permissions.

Conclusion

In this post, I provide a practical walkthrough of the chmod command in both symbolic and numeric mode, with real terminal output from Linux Mint and macOS. I compare the similarities and differences between the two, concluding that setuid permissions on macOS have become much more restrictive due to the nosuid6 mount flag, SIP, and entitlements7. Apple has made setuid unreachable for everyday use. Entitlements and sandboxing8 have taken over the role that setuid/setgid played in classic Unix security. But sticky bit is still alive and well, and setgid directories are still useful. Linux, on the other hand, typically uses ext4 as its default filesystem, which is not mounted nosuid by default. So setuid/setgid bits are actually honored by the kernel. There's no SIP equivalent, no Gatekeeper, and no cryptographically sealed volumes. Instead, Linux uses capabilities9 which let you grant a binary with just the specific (least) privilege it needs instead of full root via setuid.

Footnotes

  1. An executable in macOS is a file that can be run as a program, typically containing code that the operating system can execute directly. An example is the files which reside in the /bin directory which are all executables/commands:

    chmod dd hostname ln ps sh test .. cp df kill ls pwd sleep unlink [ csh echo ksh mkdir realpath stty wait4path bash dash ed launchctl mv rm sync zsh cat date expr link pax rmdir tcsh
  2. PAM (Pluggable Authentication Module) is a framework used in macOS (and other Unix-like systems) to manage authentication methods for applications. It allows different authentication schemes to be integrated into a single application programming interface (API), enabling flexibility in how users are authenticated.

  3. The Apple File System (APFS) is the default file system for Mac computers using macOS 10.13 (High Sierra) or later, features strong encryption, space sharing, snapshots, fast directory sizing, and improved file system fundamentals. — from the Apple Disk Utility User Guide

  4. PolicyKit, now known as polkit, is a framework for managing system-wide privileges in Unix-like operating systems, allowing unprivileged processes to communicate with privileged ones.

  5. TTY (teletypewriter) group consists of terminal devices, specifically representing interactive communication channels between the user and the system. setgid tty on Linux refers to the setgid permission applied to the tty group, allowing a program to run with the group permissions of the tty group. This means that when a user executes a command with setgid set, it will inherit the group privileges of the tty group, enabling controlled access to certain functionalities without granting full root privileges.

  6. nosuid is a mount option that prevents the execution of set-user-ID (SETUID) and set-group-ID (SETGID) files on a filesystem.

  7. Entitlements in macOS are special permissions embedded in an app's code signature that allow it to access resources restricted by macOS's security framework. They enable apps to expand their capabilities beyond a locked sandbox environment.

  8. Sandboxing in macOS is a security mechanism that restricts applications' access to system resources to minimize the impact of potential security breaches.

  9. Linux capabilities are a feature that allows the division of root privileges into smaller, distinct units, enabling processes to have only the specific permissions they need to perform their tasks. This is an excellent example of the principle of least privilege in cybersecurity.