Files, Permissions, and Ownership
LESSON
Files, Permissions, and Ownership
By the end of this lesson, you will be able to...
trace a failed file read through every directory in the path, the final file, and the process identity;
explain why a path, an inode, and a directory entry play different roles in filesystem access;
choose a narrow permission boundary instead of widening an entire tree to make one failure disappear.
Idea in one sentence: A file path is a route through directories to an inode, and access succeeds only when the requesting process has the right identity and permissions at every relevant step.
Core Insight
A note-preview service should display one generated report. The report exists:
/srv/notes/private/today.md
When Maya runs cat from her own shell, it works. When the preview service asks for the same path, it receives Permission denied. The tempting conclusion is: “The file needs a more open mode.”
That model works only when the final file mode is the missing permission. It breaks when a parent directory blocks traversal, the service runs as a different user or group, a symbolic link changes the route, or the operation is about changing a directory entry rather than reading file contents.
The stronger model is:
Access is a path walk plus an identity-based decision at each boundary. Read the path components, the directory modes, the final inode mode, and the process credentials together.
This makes a “mysterious” permission error traceable.
A Path Is Not the File
A pathname such as /srv/notes/private/today.md is a sequence of directory lookups. Each directory contains entries that map a name to an inode. An inode stores metadata about an object, including its owner, group, mode bits, timestamps, size, and references to its data.
This distinction matters because one object can have more than one directory entry. A hard link is another name for the same inode. Renaming a file usually changes directory entries, while changing file contents changes the object those entries refer to. A path is therefore a route, not a complete identity.
For ordinary Unix mode bits, the kernel chooses one of three permission classes for the requesting process:
- owner, when the process's effective filesystem identity matches the inode owner;
- group, when one of the process's groups matches the inode group;
- other, when neither applies.
The selected class provides r, w, and x. On a regular file, they mean read contents, change contents, and execute as a program. On a directory, they mean list names, create or remove names, and search or traverse the directory.
The words are deliberately different. Directory x does not execute a directory. It lets a process use a known name inside it while walking a path.
The Access Trace
Here is a synthetic state for the preview service. The output is illustrative; real owners, groups, and paths will differ.
Process: previewd
uid=1107(preview) gid=1107(preview) groups=1107(preview),2400(readers)
drwxr-xr-x root root /srv
drwx--x--- maya readers /srv/notes
drwx------ maya maya /srv/notes/private
-rw-r----- maya readers /srv/notes/private/today.md
At first glance, the final file appears readable: its group has r, and previewd belongs to readers. But the process cannot reach that check yet.
| Path component or object | Needed for previewd to read today.md |
Result | Why it matters |
|---|---|---|---|
/srv |
search (x) |
allowed through other |
The path can begin to resolve. |
/srv/notes |
search (x) |
allowed through readers |
The process may use the known name private, but cannot list the directory because group r is absent. |
/srv/notes/private |
search (x) |
denied | Only Maya has x; resolution stops here with EACCES. |
today.md |
read (r) |
not reached | Its group-readable mode cannot repair a blocked parent directory. |
The useful observation is not merely the error text. It is the first component where the route stops. Linux path resolution requires search permission on each directory as it walks a nonfinal component. A readable final file is irrelevant if the process cannot traverse a parent directory.
Inspect the Same Evidence on a Real Machine
Start by identifying the requested path and the identity that makes the request. A service account, a user session, and a sudo invocation can have different effective users and groups.
ps -o user,group,egroup,comm -p "$service_pid"
id preview
Then inspect every path component, not only the last file:
stat -c '%A %U %G %n' \
/srv \
/srv/notes \
/srv/notes/private \
/srv/notes/private/today.md
stat reports the mode, owner, group, and the name inspected. If you need to confirm whether two names identify the same object, include the inode number:
stat -c 'inode=%i links=%h %n' \
/srv/notes/private/today.md \
/srv/notes/archive/today-copy.md
Matching inode numbers show hard links to one inode. Different inode numbers show different objects, even if the contents look alike.
These commands are observation tools, not permission proofs. Access-control lists, filesystem-specific rules, mount options, security modules, containers, and capabilities can add rules beyond the nine ordinary mode bits. When the mode trace looks sufficient but access still fails, inspect the actual service context and the additional policy rather than immediately widening modes.
Worked Example: Give the Service Only the Boundary It Needs
Suppose the product requirement is narrower than “let the preview service read everything private.” The service needs only a generated report. It does not need to list notes, edit note sources, or delete user files.
Three responses are possible:
| Response | What it changes | Why it is tempting | Boundary problem |
|---|---|---|---|
chmod -R a+rX /srv/notes |
Broadly adds read and search access | It makes the error disappear quickly. | It exposes far more names and contents than the service needs. |
Give readers search on private |
Lets the group traverse the directory | It repairs the immediate blocked component. | It may expose every group-readable file in that directory. |
Write the report into a dedicated preview directory |
Creates an explicit handoff object | It takes one more design step. | The producer must maintain the handoff, but the service receives only its intended input. |
For this requirement, the third response is usually the better boundary. A producer can write a derived report to /srv/notes/preview/today.md; the preview service gets search permission on the directory and read permission on that one report. The source notes remain private.
The worked access state becomes smaller:
drwx--x--- maya readers /srv/notes/preview
-rw-r----- maya readers /srv/notes/preview/today.md
Now the service can traverse preview because its group has directory x, and read today.md because its group has file r. It still cannot list directory entries without directory r, create or remove names without directory w, or read the original private notes.
This is a teaching model, not a universal directory layout. A dedicated handoff is useful when a service needs a small, stable output. If a process truly needs many source files, a carefully scoped group or ACL may be appropriate. The decision should name the required objects rather than begin from “which chmod makes the error vanish?”
Deletion and Renaming Are Directory Operations
Another common surprise: a read-only file can sometimes be deleted by a process that cannot write its contents.
Assume no sticky-bit or ACL restriction. To remove or rename a name, the relevant authority is generally write plus search permission on the parent directory. The operation changes the directory entry. It does not require write permission on the file's contents.
Parent directory: drwxrwx--- maya editors
File: -r--r----- maya readers today.md
A member of editors may be able to remove today.md from that parent directory even though they cannot change its contents. The sticky bit is a separate directory rule commonly used on shared locations such as /tmp; it restricts who may remove or rename entries there.
This is the central trade-off: permissions create useful safety boundaries, but each operation has a different object of control. File modes govern file-content access; directory modes govern names and traversal. Treating them as one switch produces either unexplained failures or overly broad fixes.
Common Confusions
Confusion: “The file exists and is group-readable, so the service can read it.”
Why it is tempting: The final line from ls -l is visible and easy to inspect.
Better model: The process must search every parent directory before the kernel reaches the final inode.
Confusion: “The user who started the service is its identity.”
Why it is tempting: Interactive commands use the current shell's credentials.
Better model: Check the credentials of the process making the system call. Service managers, containers, and privilege changes can alter them.
Confusion: “Make the file read-only to prevent deletion.”
Why it is tempting: Deletion feels like changing the file.
Better model: Deletion normally changes the parent directory entry. Inspect the parent directory and any sticky-bit rule.
Confusion: “chmod 777 is a diagnostic tool.”
Why it is tempting: It removes ordinary mode-bit barriers quickly.
Better model: It also removes the boundary you are trying to understand. Trace the first denied component and grant only the capability the real operation needs.
Check Your Understanding
Check 1: A process belongs to readers. The file /srv/a/b/report.txt is group-readable, but directory /srv/a/b is drwx------ and owned by another user. Can the process read the file?
Think first, then reveal.
Answer: No. It lacks search permission on /srv/a/b, so path resolution stops before the final file's mode is considered.
Check 2: A directory is drwxr-x--- for the process's group. What can group members do there?
Think first, then reveal.
Answer: They can list names because of r and traverse known names because of x. They cannot create, remove, or rename entries because group w is absent.
Check 3: Why can a user with parent-directory write and search permission sometimes delete a file they cannot modify?
Think first, then reveal.
Answer: The deletion removes a directory entry. Subject to sticky-bit, ACL, and other policy rules, the relevant permission is on the parent directory rather than the final file's content mode.
Practice: Trace a Safer Handoff
A service account render needs to display one HTML report created by user nora. The report currently lives at /home/nora/work/private/report.html. Do not solve this by making Nora's whole home directory readable.
Design a small access trace and handoff plan. A good answer should include:
- every directory that
renderwould need to search to reach the original path; - the effective user and group evidence to inspect for the service;
- a dedicated output directory or similarly narrow interface;
- the required directory and final-file capabilities for
render; - what
rendermust still be unable to list, alter, or delete; - a verification step using the service identity and the final path.
A strong answer can state: “The service should read a derived copy from a dedicated handoff directory, not traverse Nora's private working tree.” That changes the boundary instead of only changing a mode bit.
Resources
- [REFERENCE] Linux manual page: path_resolution(7) — Focus: Trace how each pathname component is resolved and why directory search permission matters.
- [REFERENCE] Linux manual page: inode(7) — Focus: Separate directory names, inode metadata, hard links, mode bits, and sticky-directory behavior.
- [REFERENCE] GNU Coreutils Manual: Mode Structure — Focus: Compare read, write, and search meanings for files and directories.
- [REFERENCE] GNU Coreutils Manual: Setting Permissions — Focus: See why a file's mode does not by itself prevent deletion from a writable directory.
- [COURSE] The Missing Semester: The Shell — Focus: Practice reading modes, paths, permissions, and the distinction between a command and the shell that runs it.
Key Takeaways
- A pathname is a route through directory entries; an inode is the object and metadata reached by that route.
- To read a file, a process needs the right identity, search permission on the path's directories, and read permission on the final object.
- Directory
r,w, andxcontrol listing names, changing names, and traversing known names; they are not the same as file-content permissions. - The narrowest useful repair names the required handoff object instead of opening an entire private tree.
- Permission errors become debuggable when you find the first denied component and the credentials that reached it.
← Back to Linux Workstations: Ownership, Reproducibility, and Repair