cswork / libraryOpen practice workspace

Articles

Linux Filesystem Interview Questions: Inodes, Links, and Space That Does Not Return

Practice linux filesystem interview questions with worked failure traces, recovery choices, and verification tests grounded in official documentation.

The key to Linux filesystem interview questions is separating a pathname, an inode, a hard-link count, an open file reference, and allocated space on a particular filesystem. Removing a name does not always free the underlying storage. And “No space left on device” does not always mean all data bytes are consumed.

This guide uses Linux manual pages for documented behavior. The link-count exercise and incident evidence are original practice fixtures, not output from a production Linux host. Commands are scoped diagnostic examples; never delete or truncate an unknown application file to make a graph improve. Start with Linux Host and Filesystem Fundamentals and explain which resource each command measures.

What is an inode, and where does the filename live?

An inode carries or associates file metadata such as type, ownership, size, and link count. Its number is unique within a filesystem, not globally across every mounted device. The inode manual describes those fields. To compare file identity, reason about filesystem/device identity together with inode number.

A directory entry associates a name with an inode. This is why two different names can identify the same regular file. Renaming a file within a filesystem is not conceptually equivalent to copying its data into a new inode. A pathname is a lookup route, not a durable object identifier independent of future namespace changes.

An open descriptor gives a process a way to access an opened file. Deleting the pathname later does not retroactively invalidate that open access. The descriptor number itself is local to the process; descriptor 7 in two processes does not imply a shared file. Duplication and inheritance can also create additional references that matter during reclamation.

In an interview, draw the names separately from the open reference. That small separation prevents several wrong claims: that deleting the first name necessarily deletes data, that a symbolic link keeps the target inode alive, or that recreating the same pathname makes an old descriptor point to the new file.

How do hard links differ from symbolic links?

A hard link creates another directory entry for the same underlying inode. The link manual documents this behavior and relevant restrictions, including crossing filesystem boundaries. Editing file contents through one hard-link name changes the same file observed through the other.

A symbolic link is a separate filesystem object containing a path reference. It can refer across filesystems and can become dangling when its target path no longer resolves. It does not add a hard link to the target inode. If the target pathname is later recreated as a different file, following the symlink can resolve to that new file.

Compare these with a copy. A normal copied file has its own identity and contents, although storage techniques such as copy-on-write can make physical allocation more complicated. Do not infer physical bytes from “two names” or assume every apparent duplicate is two independently allocated data sets.

Be precise with inspection: GNU stat and ls -i can help compare inode identity, while link-following options change whether you inspect the symlink or its target. Tool options vary across platforms. State that your command syntax is for the intended Linux/GNU environment rather than silently mixing it with macOS output.

Exercise: predict link count and data accessibility

This original fixture uses only a fresh temporary directory. It writes a tiny scratch file, adds a hard link and symlink, then unlinks both hard-link names while an open Python file remains active:

import os
import tempfile
from pathlib import Path

with tempfile.TemporaryDirectory(prefix="inode-practice-") as directory:
    root = Path(directory)
    first = root / "first"
    second = root / "second"
    shortcut = root / "shortcut"
    first.write_bytes(b"receipt\n")
    os.link(first, second)
    shortcut.symlink_to(first.name)
    with first.open("rb") as handle:
        print(os.fstat(handle.fileno()).st_nlink)  # 2
        first.unlink()
        print(os.fstat(handle.fileno()).st_nlink)  # 1
        print(shortcut.exists())                  # False: dangling
        second.unlink()
        print(os.fstat(handle.fileno()).st_nlink)  # 0
        print(handle.read())                      # b'receipt\n'

The symlink never raised the target's hard-link count. Once first disappears, shortcut still exists as a symlink object, but following it fails; exists() follows its target. The second hard link still permits access until it is removed. After both names are gone, the open handle can still read the file.

The unlink manual explains why storage is retained when the final link is removed but an open reference remains. Closing the last open reference removes that particular reason for retention. Do not claim this small exercise proves exact byte reclamation for every filesystem, snapshot system, or storage backend.

Add a follow-up: create a new file at first while the original handle remains open. The old descriptor continues accessing the old file; a new pathname lookup accesses the replacement. This is the mechanism behind a common log-rotation confusion. Renaming or recreating a path alone does not force an application to reopen its writer.

Why can df stay high after deleting a large log?

Assume an original incident fixture: a service writes a 12 GB log, an operator unlinks its last name, directory-based usage falls, but filesystem used space barely changes. A plausible hypothesis is that the service still has the old file open. The hypothesis is not confirmed until you inspect its references.

The df manual describes filesystem space reporting. The du manual describes estimating usage through directory traversal. Their accounting scopes differ. A file that is no longer reachable through the scanned tree can still occupy filesystem blocks.

In an authorized Linux environment, use a scoped process check, such as lsof -p PID +L1, after substituting the investigated service's exact PID and confirming your lsof version. You can also inspect /proc/PID/fd references. The proc descriptor manual documents this interface. Permissions and process namespaces affect what you can see.

Identify the relevant device, inode, size, descriptor, and service owner. A deleted-file entry is evidence of retention, not automatic proof that this one entry explains the entire discrepancy. Multiple processes can retain the same file, and another large deleted file may exist. Avoid double-counting the same inode through several descriptors.

Also check timing. The writer may continue appending to the unnamed file, making the incident worse while no visible pathname grows. A cleanup that removes only directory names can therefore hide the active consumer from a naive disk-usage scan rather than stop the space growth.

What is the safest way to release the old file?

Choose the application's documented reopen or rotation mechanism when available. A verified log-reopen operation can close the old descriptor and open the new path without terminating the whole service. A controlled restart may be necessary if the application has no supported reopen path, but it carries availability and state risks.

Do not assume SIGHUP means “reopen logs” for every program. Signal handling is application-specific. Confirm what the service implements, whether the supervisor changes behavior, and whether all worker processes reopen. A successful signal command merely shows signal delivery or attempted delivery, not that every old reference disappeared.

Avoid recommending truncation through /proc/PID/fd/N as a generic production fix. That can destroy data or violate the application's expectations, particularly for database, index, or checkpoint files. Truncating an active file may also interact with its current write offset in unexpected ways. An interview answer should explain why the operation is unsafe without a documented contract.

After the chosen action, verify the old device/inode is no longer retained by the relevant processes, the new log receives entries, filesystem availability improves as expected, and service health remains acceptable. Preserve required incident evidence before destructive cleanup. If the metric does not change, revise the hypothesis rather than repeating increasingly broad deletion.

Can free bytes coexist with inode exhaustion?

Yes. A filesystem can have capacity for more data but lack the metadata resources needed to create another file. Huge numbers of small files can make that scenario plausible. GNU df -i /target/path reports inode usage where the filesystem provides it; df -h /target/path addresses byte capacity instead. These are different diagnostics.

An original exercise gives you 50 GB free, an inode-use figure at its limit, and millions of abandoned zero-length job markers. Creating a new file fails. Removing one verified disposable marker may restore one inode opportunity without freeing meaningful data bytes. Adding a larger data volume does not necessarily fix that existing filesystem's inode policy.

Do not extrapolate one fixed inode model to all filesystem types. Allocation mechanisms differ, and reported inode availability can have filesystem-specific meaning. Ask for the filesystem type and inspect its actual metadata limits. Quotas and reserved capacity can also constrain a particular user even when an overall capacity report looks comfortable.

Keep descriptor exhaustion separate. A process failing to open another file because it reached a descriptor limit is not the same as failing to allocate an inode. Preserve the exact error and operation, then inspect the relevant process limit. “Increase every limit” is not a defensible first step.

Why else might df and du disagree?

First ensure both inspect the same mount and namespace. A container's path can refer to a different filesystem view from the host's similarly named path. A mounted filesystem can hide files underneath its mount point from traversal while those underlying files still occupy their original filesystem. Do not unmount a live service volume just to test this casually.

Next compare permissions, scan completeness, traversal options, time, and units. A scan that cannot read directories undercounts them; a long scan and a later filesystem reading are not simultaneous measurements. Hard-link counting behavior also affects summaries. A per-directory total is not always additive across independently scanned overlapping trees.

Distinguish apparent file length from allocated blocks. Sparse files can have large logical sizes with fewer allocated blocks; other storage techniques and metadata accounting introduce different relationships. Snapshots, copy-on-write retention, reserved blocks, and filesystem metadata may account for space outside your simple pathname sum.

The interview objective is not listing every possible explanation. Choose a discriminating next check. If deleted-open file evidence is absent, verify the mount and traversal scope. If the file is sparse, compare apparent size with block allocation. If storage snapshots are involved, consult that filesystem's accounting and retention policy rather than assuming ordinary unlink semantics settle physical reclamation.

Turn evidence into an incident answer

Give a concise sequence: exact failing path and error; its mount; byte and inode availability; directory-usage scope; open references if deletion did not help; application-specific remediation; and post-action verification. Explain what each observation rules in without pretending it rules out every alternative.

The second column adds this article's filesystem-specific reasoning to broader interview questions.

Practice questionTopic-specific follow-up
Linux Host and Filesystem FundamentalsTrace pathname, inode, link count, and mount.
Explain Linux Command Execution, Filesystems, and IsolationCheck the workload namespace before trusting host output.
Explain What Happens When a Linux Shell Runs lsExplain directory entries rather than treating names as file data.
Diagnose Storage I/O Performance and Identify the WorkloadFind the writer before choosing cleanup.
Diagnose I/O, Memory, and CPU ConstraintsDistinguish byte capacity, inode capacity, and other constraints.

Return to Linux Host and Filesystem Fundamentals and predict the scratch exercise before running it. Then explain why a symbolic link, a surviving hard link, and an open descriptor have three different consequences. That explanation transfers directly to the “deleted log, still full disk” follow-up.

Sources and further reading