G27
Self-checkedTier 1 15 min reados.file-systems

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 0,1,…0, 1, \ldots; any record can be read or written by number, in any order. For records of rr bytes, record number nn starts at byte n⋅rn \cdot r (or (n−1)⋅r(n - 1) \cdot r 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:

StructureDescriptionProblem
Single-levelone directory for all filesnames must be unique across all users
Two-levela master directory with one directory per userusers cannot easily share or group files
Tree-structureddirectories contain files and subdirectories; one root; every file has a unique path namesharing needs links
Acyclic graphthe tree plus links, so a file or directory can appear in several places; no cyclesdeletion with several names; cycles must be prevented
General graphcycles allowedtraversal 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 dd components costs 2d+12d + 1 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 O(n)O(n)) 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 (2×3+12 \times 3 + 1); /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 table (inode, type, first data block)
inodetypeblock
2dir20
5dir31
7dir52
8dir60
9dir44
12file90
13file95
directory blocks (block, name, inode)
blocknameinode
20.2
20..2
20home5
20usr7
31.5
31..2
31ann9
44.9
44..5
44notes.txt12
44todo13
52.7
52..2
52lib8

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.

0 / 16
computed by script: the file is stored by the allocation method and read through its on-disk structure. python3 scripts/gen-os-fs-traces.py

Worked examples

EX 1Links, unlinking and permissions on a real file system

Easy

A 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 accesses

Reading 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 dd components costs 2d+12d + 1 accesses with no caching.

EX 2Access methods

Easy

A 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 (5−1)×100=400(5 - 1) \times 100 = 400 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

Medium

A 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

Medium

Mode 0754 in octal: owner 7=111=7 = 111 = rwx, group 5=101=5 = 101 = r-x, others 4=100=4 = 100 = 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 level

The 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: 2+1=32 + 1 = 3 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 (nn-th record at n⋅rn \cdot r or (n−1)⋅r(n - 1) \cdot r); indexed.
  • Directories: single-level, two-level, tree, acyclic graph (links), general graph.
  • Path of dd components: 2d+12d + 1 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

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

GATE 2021 · MCQQ8 · page 358 of the book
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 answer

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 answer

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 answer

Four components: 2×4+1=92 \times 4 + 1 = 9.

Question 4

Why does a system keep a system-wide open-file table as well as one per process?

Show answer

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 dd components: 2d+12d + 1 accesses. Hard link: same inode, count; symbolic link: path, can dangle.
  • Permissions rwx ×\times 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