File systems: concepts and directories
What a file is and what the system keeps about it: attributes, operations, the open-file tables, access methods, directory structures from single-level to graphs, path names and their cost, hard and symbolic links and link counts, protection bits, and the layers and on-disk layout of a file system.
Why it exists
Programs and users need to keep data after the program ends and to share it. The operating system provides the file, a named collection of related bytes, and the directory, a structure that names files. It hides the disk: nobody asks for "block 4172 of the third cylinder", they ask for /home/ann/notes.txt. This page covers what a file is and what operations exist, how directories are organised and how path names are resolved, how links and protection work, and how a file system is layered. The next page, file allocation, covers how a file's blocks are placed on disk.
Prerequisites
No other concept page is needed. File operations are reached through system calls (open, read, write, close), and the files live on a disk whose requests are scheduled as in disk scheduling.
Precise definitions
File. A named collection of related information on secondary storage; to the user the smallest unit of logical storage. The OS sees a file as a sequence of bytes (or records) and does not interpret it, except for a few types (directories, executables).
File attributes (metadata, kept in the directory entry or in an inode): name (the human-readable form), identifier (a unique number, such as the inode number), type, location (where its blocks are), size, protection (who may read, write, execute), and times (creation, last modification, last access), and the owner.
File operations. The OS provides: create (find space, make a directory entry), write and read (at the current file position, which the system keeps as a file pointer per open file), reposition (seek), delete (release the space and remove the entry), truncate (erase the contents, keep the attributes).
Open-file tables. To avoid searching the directory on every access, a file is opened first. The system keeps:
- a system-wide open-file table: one entry per open file, with a copy of its inode and an open count (the number of processes that have it open), so that the entry is removed when the count falls to zero;
- a per-process open-file table: an entry for each file the process has open, with the file pointer, the access mode, and a pointer to the system-wide entry. The index in this table is the file descriptor.
Access methods.
- Sequential access: information is processed in order, one record after another; the file pointer advances automatically. Read next, write next, rewind.
- Direct (relative) access: a file is a set of fixed-size records numbered ; any record can be read or written by number, in any order. For records of bytes, record number starts at byte (or if numbering starts at 1). This is the model for databases.
- Indexed access: an index (a small file) maps keys to records, and the data are found through it.
Directory. A file that holds the entries (name, attributes or inode number) of the files it contains. Operations: search, create, delete, list, rename, traverse. Structures:
| Structure | Description | Problem |
|---|---|---|
| Single-level | one directory for all files | names must be unique across all users |
| Two-level | a master directory with one directory per user | users cannot easily share or group files |
| Tree-structured | directories contain files and subdirectories; one root; every file has a unique path name | sharing needs links |
| Acyclic graph | the tree plus links, so a file or directory can appear in several places; no cycles | deletion with several names; cycles must be prevented |
| General graph | cycles allowed | traversal can loop; cycles need garbage collection or a bound on search |
Path names. An absolute path starts at the root (/home/ann/notes.txt); a relative path starts at the current directory of the process (notes.txt, ../ann/todo). The special names . (this directory) and .. (the parent) are entries in every directory.
Resolving a path name. To open /a/b/c the system starts at the root directory and, for each component, reads the data of the current directory to find the component's inode number and then reads that inode. If directories fit in one block, each component costs two disk accesses (the directory block, then the inode) and, with the root inode already in memory, reading the first data block of the file adds one: a path of components costs disk accesses (without caching). A relative path starts from the current directory and saves the components above it.
Links. A hard link is an additional directory entry that points to the same inode; all names are equal, the inode keeps a link count, and the file's data are freed only when the count falls to 0. A hard link cannot cross file systems and normally cannot name a directory. A symbolic (soft) link is a small file that contains a path name; opening it follows the path. If the target is removed, the link dangles (it points to nothing); removing the link does not affect the target.
Protection. Controlling who may do what to a file. Access-control list (ACL): for each file, the list of users and the access they have. Unix permission bits: three groups of three bits (read, write, execute) for the owner, the group and others, written in octal: the mode 0755 means rwxr-xr-x (owner all, others read and execute); 0640 is rw-r-----. For a directory, read lets you list, write lets you create and delete entries, execute lets you pass through it.
Mounting. Attaching a file system to a point (the mount point, an existing directory) of the tree; the root of the mounted file system then appears there.
Layers of a file system. From the top: the application programs; the logical file system (directories, names, protection, file control blocks); the file-organisation module (maps logical blocks to physical blocks and manages free space); the basic file system (issues generic read and write commands to the device driver for a block address); I/O control (device drivers and interrupt handlers); the devices.
Directory implementation. A linear list of entries (simple, searching is ) or a hash table (fast lookup, collisions to handle, fixed size problems).
On-disk layout (a Unix-style file system). A boot block (code to start the operating system), the superblock (the size of the file system, the numbers of inodes and blocks, free-block information), the inode list (an array of inodes), and the data blocks (file data and directory data).
How it works
The animation resolves path names against a small file system: the inode table (inode number, type, first data block) and the contents of four directory blocks. The root is inode 2 and is already in memory. Each component costs one directory-block read (the highlighted directory row) and one inode read (the highlighted inode row). Three paths are resolved: /home/ann/notes.txt takes 7 disk accesses (); /home/bob/todo fails after 3 accesses because the directory block of /home has no entry bob; and the relative path notes.txt from the current directory (/home/ann) takes just 3.
Resolving path names through directories and inodes: start
Translation computed, not typed| inode | type | block |
|---|---|---|
| 2 | dir | 20 |
| 5 | dir | 31 |
| 7 | dir | 52 |
| 8 | dir | 60 |
| 9 | dir | 44 |
| 12 | file | 90 |
| 13 | file | 95 |
| block | name | inode |
|---|---|---|
| 20 | . | 2 |
| 20 | .. | 2 |
| 20 | home | 5 |
| 20 | usr | 7 |
| 31 | . | 5 |
| 31 | .. | 2 |
| 31 | ann | 9 |
| 44 | . | 9 |
| 44 | .. | 5 |
| 44 | notes.txt | 12 |
| 44 | todo | 13 |
| 52 | . | 7 |
| 52 | .. | 2 |
| 52 | lib | 8 |
No address yet.
Step 0
A directory is a file whose data blocks hold (name, inode number) pairs; an inode says what a file is and where its blocks are. To open a path name the system walks down the tree: for each component it reads the current directory's data block to find the component's inode number, then reads that inode. The root's inode (number 2) is known to the system and is already in memory. Count the disk accesses: one for each directory block and one for each inode.
Worked examples
EX 1Links, unlinking and permissions on a real file system
EasyA program creates a file, adds a hard link and a symbolic link, removes the original name and looks at what survives; it then sets the permission bits and counts the disk accesses for three path names.
#define _POSIX_C_SOURCE 200809L
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
/* real file system calls in a fresh temporary directory: names are directory entries that point to an inode */
static void show(const char *what, const char *path) {
struct stat st;
if (stat(path, &st) != 0) { printf("%-32s -> does not exist (or points to nothing)\n", what); return; }
printf("%-32s -> link count %d, size %ld\n", what, (int)st.st_nlink, (long)st.st_size);
}
int main(void) {
char dir[64];
snprintf(dir, sizeof dir, "/tmp/fsdemo%ld", (long)getpid());
if (mkdir(dir, 0700) != 0) return 1;
char file[256], hard[256], soft[256];
snprintf(file, sizeof file, "%s/notes.txt", dir);
snprintf(hard, sizeof hard, "%s/todo", dir);
snprintf(soft, sizeof soft, "%s/soft", dir);
int fd = open(file, O_CREAT | O_WRONLY, 0600);
if (fd < 0 || write(fd, "hello", 5) != 5) return 1;
close(fd);
show("after creating notes.txt", file);
if (link(file, hard) != 0 || symlink(file, soft) != 0) return 1;
struct stat a, b, c;
stat(file, &a); stat(hard, &b); lstat(soft, &c);
printf("hard link shares the inode: %d; symbolic link has its own inode: %d\n", a.st_ino == b.st_ino, c.st_ino != a.st_ino);
show("after link(notes.txt, todo)", file);
unlink(file);
show("after unlink(notes.txt): todo", hard);
show("after unlink(notes.txt): soft", soft);
chmod(hard, 0640);
stat(hard, &a);
printf("permission bits after chmod 0640: %o (owner rw-, group r--, others ---)\n", (unsigned)(a.st_mode & 0777));
unlink(hard); unlink(soft); rmdir(dir);
/* disk accesses to open a file by path name: a directory block and an inode for each component, plus the first data block */
const char *paths[3] = {"/home/ann/notes.txt", "/usr/lib/libc.so", "/a/b/c/d/e"};
for (int i = 0; i < 3; i++) {
int comps = 0;
for (const char *p = paths[i]; *p; p++) if (*p == '/') comps++;
printf("%s: %d components, %d disk accesses\n", paths[i], comps, 2 * comps + 1);
}
return 0;
}after creating notes.txt -> link count 1, size 5
hard link shares the inode: 1; symbolic link has its own inode: 1
after link(notes.txt, todo) -> link count 2, size 5
after unlink(notes.txt): todo -> link count 1, size 5
after unlink(notes.txt): soft -> does not exist (or points to nothing)
permission bits after chmod 0640: 640 (owner rw-, group r--, others ---)
/home/ann/notes.txt: 3 components, 7 disk accesses
/usr/lib/libc.so: 3 components, 7 disk accesses
/a/b/c/d/e: 5 components, 11 disk accessesReading it. After link, the file has two names and link count 2; unlink of the first name removes one directory entry and the count drops to 1, the data survive under the other name. The symbolic link was only a path to the removed name, so it now dangles. The hard link shares the inode (same identity); the symbolic link is a different file. After chmod 0640 the bits are rw-r-----. A path of components costs accesses with no caching.
EX 2Access methods
EasyA file of 1000 bytes holds 100-byte records. Sequential access: reading 7 records moves the file pointer to byte 700; the next read gets record 8. Direct access: to read record 5 (numbering from 1) the system positions the pointer at and reads 100 bytes, no matter where the pointer was. A sequential file can be accessed directly by seeking (it is only a byte stream); an indexed file keeps, say, the key of each record with its number, so a record is found by looking up the key in the index first.
EX 3Link counts and deletion
MediumA file has one name (link count 1). ln a b creates a hard link: both names refer to the same inode, count 2; writing through b changes the data seen through a. Removing a makes the count 1; the file is still there under b. Removing b makes the count 0 and the blocks and inode are released (if no process still has it open: the open-file count must also be 0). A symbolic link s to a does not change the link count of the inode: it is a separate small file holding the text of the path. Remove a and s dangles. The common exam question: "how many names, what is the link count, which links survive after these commands".
EX 4Permission bits
MediumMode 0754 in octal: owner rwx, group r-x, others r--: rwxr-xr--. Mode 0644 gives rw-r--r--; 0600 gives rw-------. A user who is neither the owner nor in the group is "others": for 0754 such a user may read but not write or execute. To make a directory searchable but not listable for others, use execute without read (--x). The check made by the system: if the user is the owner, use the owner bits; otherwise if in the group use the group bits; otherwise the others bits; only one of the three triplets applies.
EX 5Counting disk accesses to open a file
GATE levelThe path is /usr/lib/x.so (3 components). Root inode in memory; each directory fits in one block; no caching. Read the root directory block (1) and the inode of usr (2); the block of usr (3) and the inode of lib (4); the block of lib (5) and the inode of x.so (6): six accesses to open the file; reading its first data block adds one (7). If the question counts the root inode as a disk read (the root's inode must also be fetched), add one: 8. Read the question for the convention (is the root inode cached? do directories span several blocks? is the first data block included?). If the current directory is /usr/lib and the path is x.so, only the last component is resolved: accesses.
Cheat box
Cheat box
- File attributes: name, identifier (inode), type, location, size, protection, times. Operations: create, read, write, seek, delete, truncate; open first.
- Open-file tables: system-wide (with open count) and per-process (file pointer, descriptor).
- Access: sequential; direct (-th record at or ); indexed.
- Directories: single-level, two-level, tree, acyclic graph (links), general graph.
- Path of components: accesses (root inode in memory, one-block directories, no cache). Relative paths save the upper components.
- Hard link: same inode, link count; data freed at count 0. Symbolic link: a file holding a path; dangles if the target goes.
- Permissions: rwx for owner, group, others; octal 0755 =
rwxr-xr-x; one triplet applies. - Layout: boot block, superblock, inode list, data blocks.
Traps and GATE patterns
Connections
Builds on
No prerequisites.
Across subjects
No cross-subject links yet.
Within this subject
Journeys that pass through here
Reading and writing a file are system calls; the open-file tables belong to the process and the system, like the PCB of a process. How the blocks of a file are found on the disk is file allocation, and the order in which disk requests are served is disk scheduling.
PYQs
1 previous year question from your book. I re-solved each one myself (0 by running or simulating, 1 by reasoning only); the book's key is shown next to my answer. All PYQs
Consider a linear list based directory implementation in a file system. Each directory is a list of nodes, where each node contains the file name along with the file metadata, such as the list of pointers to the data blocks. Consider a given directory foo. Which of the following operations will necessarily require a full scan of foo for successful completion?
- (a)Creation of a new file in foo
- (b)Deletion of an existing file from foo
- (c)Renaming of an existing file in foo
- (d)Opening of an existing file in foo
Self-check
Question 1
A file has link count 3 and one symbolic link points to it. All three hard-linked names are removed. What happens to the data and to the symbolic link?
Show answerHide
The link count reaches 0 (and no process has it open), so the data and the inode are freed. The symbolic link still exists as a file holding the path, but it dangles.
Question 2
Mode 0640 on a file owned by ann, group staff. What can a member of staff who is not ann do? What can an unrelated user do?
Show answerHide
A member of the group may read (r--); an unrelated user has no access (---).
Question 3
How many disk accesses to read the first block of /a/b/c/d with the root inode in memory and one-block directories?
Show answerHide
Four components: .
Question 4
Why does a system keep a system-wide open-file table as well as one per process?
Show answerHide
The system-wide table has one entry (with the inode copy and an open count) per open file, so processes that open the same file share it and the file is closed for good only when the count falls to zero; the per-process table gives each process its own file pointer and descriptor.
Revision card
One-screen revision
- File = named bytes plus attributes (inode); open-file tables: system-wide and per-process.
- Access: sequential, direct, indexed. Directories: tree, acyclic graph with links.
- Path of components: accesses. Hard link: same inode, count; symbolic link: path, can dangle.
- Permissions rwx owner, group, others (octal).
- Layers: application, logical FS, file organisation, basic FS, I/O control, device.
References
- ·Silberschatz, Galvin, Gagne, Operating System Concepts, Chapter 13 File-System Interface and Chapter 14 File-System Implementation (directory implementation)
- ·Tanenbaum and Bos, Modern Operating Systems, Chapter 4 File Systems (files, directories, file-system implementation)
- ·Stallings, Operating Systems: Internals and Design Principles, Chapter 12 File Management
Verification
The path-resolution animation is performed by a script (scripts/gen-os-fs-traces.py) against the inode and directory tables shown, and its disk-access counts (7 for /home/ann/notes.txt, 3 for the failing path, 3 for the relative path) are rechecked by tests/os-fs-traces.test.ts with an independent resolver and by the formula 2 x components + 1 in scripts/enum-checks/os_fs.py. The behaviour of hard links, symbolic links, unlinking and permission bits is confirmed by running the same operations on the real file system, both in os_fs.py and in the C program on this page (npm run verify:c). The cost model for path resolution (root inode in memory; one directory block and one inode read per component; directories fit in one block; no caching) is the usual textbook assumption and is stated; real systems cache directory entries and inodes heavily. The layering and the on-disk layout (boot block, superblock, inode list, data blocks) are the classic Unix description. Textbook chapter titles are from memory. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand. No defect found. Status raised to self-checked; verified needs a textbook or human check.
last verified: never
My notes (saved in this browser)