Files, Permissions, and Ownership

LESSON

Linux Workstations: Ownership, Reproducibility, and Repair

002 25 min beginner

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:

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:

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

Key Takeaways

PREVIOUS The Shell as a Control Surface NEXT Packages and Dependency Trust