A file system organizes files, their file metadata, and the names through which they can be found. In the Unix model used here, a file-system directory contains directory entries associating names with inodes; an inode describes the underlying file and how to locate its contents. The file-system namespace answers "what does this name reach?", while the inode answers "which file is it?". These are distinct responsibilities, even though they cooperate on every lookup.
For a computed toy example, let one file-system directory contain names
a and b, both referring to inode 42 in the same file system. That inode has size 6 bytes and two hard links. Its direct references lead to four-byte file data blocks 7 and 9: block 7 holds hell, while block 9 holds o followed by a newline, with two unused slots. Reading either name therefore obtains the same six-byte contents hello followed by a newline. There is one file with two names, not two copies. This direct-reference layout and tiny block size are teaching choices, not requirements on every file system.The separation is useful because a rename within this file system, from
a to a previously unused name c, can change a directory entry without copying the six bytes of contents. The remaining name b still reaches inode 42. A successful write changing the first byte through either name changes what both names subsequently read, to jello followed by a newline. This sharing is the attraction of hard links, and also why a hard link is not an independent backup. A program that replaces a named file with a different file can instead make the names diverge. [ref]https://pubs.opengroup.org/onlinepubs/9799919799/functions/rename.html. The Open Group, POSIX.1-2024, rename: changing names and replacing directory entries
A file system organization in which each file data block identifies its successor. It supports sequential traversal and growth without a fixed per-file index tree, but arbitrary file offsets require following the chain. Adding predecessor links permits reverse traversal.
Allocated space unused inside an allocation unit. For a computer file stored in fixed-size file data blocks, the final partially occupied block can waste almost one block.
A representation of currently unallocated disk block numbers, maintained as a list or linked groups of available numbers. Allocation removes a number; deallocation returns it after ownership is relinquished. Reserved structures and blocks still owned by an unnamed but open file are unavailable. A block allocation bitmap is an alternative representation of the same allocation state.
A disk block number identifies a fixed-size storage region on a volume. It locates storage rather than naming a file. A file can occupy several numbered file data blocks, while a disk block containing several inodes can describe several files.
Move file contents to consolidate their allocated regions. A per-file read-then-write cost model charges two transfers and, if specified, two separate seek and rotation overheads for each moved file. That model differs from an ideal sequential transfer of the same total number of bytes.
An object whose contents and attributes can be accessed through operating system interfaces. Here an ordinary file means a byte sequence with file metadata, distinct from a file-system directory or a symbolic link. Its contents need not disappear when the creating operating-system process ends.
A computer file whose logically zero-filled regions need not occupy physical file data blocks. The file offset and byte length describe the logical content independently of the blocks actually allocated.
A byte position measured from the start of a file, beginning at zero. A memory-mapped file combines a starting file offset with a displacement inside its mapping; this is distinct from an open file description's current read/write position.
Information describing a file, such as type, owner, access permissions, size, timestamps and hard link count. In the toy layout, the inode carries these attributes; the directory entries carry the names. The distinction matters because renaming a file need not copy its contents.
A storage unit holding part of a file's contents. The toy inode references two four-byte file data blocks for six bytes of contents: allocated capacity is bytes, while logical size is bytes. This counts only the two file data blocks, excluding storage for the inode and directory entries.
A file containing directory entries. Each entry supplies a name in that file-system directory; two entries in the same file-system directory cannot have the same name. In an inode-based file system, a file-system directory has its own inode, and its contents describe names of other objects. [ref]
https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap03.html#tag_03_103. The Open Group, POSIX.1-2024, sections 3.103–3.104: directory and directory entry
An association between a name in a file-system directory and a file. In the toy file system, the pairs are
(a, 42) and (b, 42): each name resolves to the same inode. The name belongs to the directory entry, rather than being the single authoritative name stored in that inode.The organization of names and the rules for resolving them to files. A path such as
/notes/a selects successive directory entries starting from the root file-system directory. Its spelling describes a route, not an immutable identity: replacing the final directory entry can make the same path reach a different inode.A directory entry associating a name directly with an existing file. In the inode-based example, successful
link("a", "b") adds the second name for inode 42 and increases its hard link count from 1 to 2. Both directory entries have equal status; removing a leaves b usable. This example assumes both names are in the same file system, the destination name is unused, and the operation is permitted. [ref]https://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html. The Open Group, POSIX.1-2017, link: adding a directory entry and incrementing the link count
For an ordinary file under the POSIX rules used here, successful
unlink("a") removes the named directory entry and decreases the hard link count. An existing open file description still refers to the same file; removing a name does not detach an already open file descriptor. When the final hard link disappears, the contents remain available through open references until those references close. This distinction is between the lifetime of a name and the lifetime of the underlying file. [ref]https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html. The Open Group, POSIX.1-2017, unlink: removal of names and deferred removal of open-file contents
Start with names
a and b for inode 42 and one file descriptor 3 referring to one open file description. Let count hard links and count live open file descriptions for this file. Assume successful operations, no additional references, and no memory-mapped file. The stages areAfter the first removal, b still works. After the second, neither name works, but a read through the existing file descriptor can still obtain all six bytes if its open file description's current position is at the beginning. The final close releases the last open reference, allowing the inode and its file data blocks to be reclaimed. Freed storage is reusable; this does not promise that old bytes are securely erased. [ref]https://pubs.opengroup.org/onlinepubs/009604499/functions/close.html. The Open Group, POSIX, close: releasing descriptors, open file descriptions and unlinked-file storage
The reasoning is bookkeeping rather than a long proof: each successful removal subtracts one from and leaves unchanged; closing the only file descriptor in this example releases the sole open file description. Storage is reclaimable only at the last stage. is a teaching count, not a count of file descriptors or an asserted internal inode field: several file descriptors can share one open file description, and other references such as a memory-mapped file require additional lifetime accounting. The diagrams are abstract models, not operations on actual user files.
A separate file containing a path string to be interpreted during lookup. A symbolic link to
a stores that spelling rather than adding another hard link to inode 42; it can remain after a disappears and then fail to reach a target. It does not increase the target's hard link count. The diagrams below track only hard links and open references. [ref]https://pubs.opengroup.org/onlinepubs/009695299/functions/symlink.html. The Open Group, POSIX, symlink: storing the supplied path string
// Target: computer-science.bigb
A memory cache of device blocks indexed by device identity and block number. A hit avoids slow device access; a miss obtains a buffer and reads the block. Dirty buffers carry modified data that must be written before reuse, and pinned buffers cannot be evicted while an operation uses them. Deferred writes improve throughput but need explicit durability handling.
A buffer cache can hold both file data blocks and storage containing file metadata, such as inodes and block allocation bitmaps. Reusing a buffered device block saves repeated input-output; retaining several modifications before buffer cache writeback can combine them into one transfer. Section 40.7 of Operating Systems: Three Easy Pieces by Remzi H. Arpaci-Dusseau and others explains these benefits for file systems.[ref]
File System Implementation. pp. 14–16: caching and buffering
The central distinction is that a dirty buffer needs a write, whereas a pinned buffer must retain its identity. A clean buffer can still be pinned, and a dirty buffer can have no client references. Neither releasing a reference nor obtaining a cache hit establishes file-system transaction durability.
A data structure holding the contents of one fixed-size region of a storage device, together with its buffer cache key, buffer cache validity, modification state and lifetime information. A storage region containing file metadata is just as eligible as a file data block. The structure can survive many callers: releasing it usually makes it available for later reuse, rather than immediately deleting the cached contents.
The identity used to find a buffered device block. In the fixed-block-size model, identifies the device and its block number; and are different keys if . A file's pathname or its logical block offset alone is insufficient for this device-indexed model. Interfaces supporting different transfer sizes need to specify the size and overlap rules too.
Maintain at most one logical buffered device block for each buffer cache key within this buffer cache. Otherwise two callers could modify different copies and later overwrite one another's changes. Lookup and reservation must therefore be coordinated by synchronization: even before contents are ready, a reserved key directs a second caller to the same structure. This invariant concerns the participating cache, not every copy of those bytes anywhere in the machine.
Whether a buffered device block's contents are ready for the requested use. Reserving a buffer cache key does not make its previous occupant's contents valid. A read fills the reserved structure before exposing its contents. A second caller finding an unfinished fill must wait; finding the identity is not yet a data cache hit under the convention used here. A failed fill must not expose the stale contents as valid.
A buffered partial write first needs the untouched bytes: read the old contents on a cache miss, change the requested range, and retain the result as a dirty buffer. A complete overwrite can initialize every byte without reading the old contents if the interface permits that path. The buffer cache access trace deliberately uses partial writes, so every cache miss reads the old block.
A valid buffered device block with modifications still owed to its backing location. Until successful buffer cache writeback, simply reassigning its buffer cache key would lose those modifications. “Dirty” records a write obligation; a write of identical contents can still mark the structure dirty. Repeated buffered writes can leave only the latest contents to be transferred.
A valid buffered device block with no outstanding write obligation to the backing location tracked by this cache. It can serve a cache hit. It becomes eligible for buffer cache eviction only when its retention and any pending input-output permit reuse. “Clean” does not assert that a device's volatile memory cache has been flushed to persistent storage.
A buffered device block whose identity is retained for unfinished work. Reference counting can represent both client references and additional pins. Dropping a client reference does not release an outstanding journal pin; dropping a pin does not remove somebody else's reference. In the buffer cache state model, counts all such retained references, so prevents buffer cache eviction. This count keeps the structure alive; a mutex separately controls who can inspect or change its contents. Waiting callers also need lifetime protection.
Transferring a dirty buffer's current contents to its backing location. The buffer cache state model permits no concurrent modification during the transfer and marks the result clean only after successful completion. A failed write preserves the obligation and prevents the proposed buffer cache eviction. A production implementation allowing new writes during input-output must retain their obligation rather than incorrectly clearing it when an older transfer finishes.
A successful transfer can leave data in a device's volatile memory cache. A storage flush or the documented equivalent supplies a persistence boundary; file-system journaling supplies recovery rules for related updates. These are additional obligations, not consequences of becoming a clean buffer.[ref]
Removing a buffer cache key's resident contents so the storage in computer memory can serve another key. An unreferenced clean buffer can be reused directly in the buffer cache state model; an unreferenced dirty buffer requires successful buffer cache writeback first. A pinned buffer cannot be chosen regardless of cleanliness. LRU replacement ranks eligible candidates; it does not override lifetime protection. If every candidate is retained, a cache miss cannot obtain space until something releases a reference or capacity changes. This is a resource shortage, not permission to discard live contents.
Use one fixed-size buffered device block per buffer cache key, serialized operations, successful reads and writes, and no outside changes to the backing contents. Record an absent state or a reserved key with validity , dirty flag , and retained-reference count . The bits describe contents, while the nonnegative integer describes lifetime. Every dirty buffer is valid, so implies . Reservation begins at ; successful fill changes only to 1. Modifying a valid retained structure sets . Releasing a caller changes , not or .
Buffer cache validity, dirty buffers and retained references impose different state-transition rules
. Details. The first diagram separates contents transitions from reference transitions. Buffer cache writeback temporarily retains the structure while transferring its contents; that internal reference is acquired and released inside the single serialized operation shown. Buffer cache eviction requires a valid clean buffer with . The diagram omits read errors, explicit cancellation of invalid reservations, concurrent writes, invalidation and journal ordering. In particular, a journal can forbid otherwise possible buffer cache writeback until its write-ahead logging dependencies are satisfied.
For a small independent check, bound to . Starting absent, allow reservation, acquisition, successful fill, modification, successful buffer cache writeback, release of a valid retained structure, and eligible buffer cache eviction. Exactly nine abstract states are reachable: absent, two invalid reservations with , and six valid states combining with . Every transition preserves ; the only eviction source has . This bounded finite-state machine check supports these safety rules, not a claim that work always completes.
Let be three distinct buffer cache keys on one device. Use two initially empty slots, LRU replacement refreshed at each completed request, and synchronous successful buffer cache writeback of a dirty victim. Each request acquires its target, finishes any fill, reads or partially modifies it, and releases the client reference. An explicit pin on remains after request 2 through request 7; pin and unpin do not refresh recency. No other references, invalidations, background writes or durability operations occur. Initial backing contents are represented by the integers ; partial writes change the tracked value of to 11 and then 12 while preserving the unshown remainder.
Exact cache hits, cache misses, retained references and buffer cache evictions in a two-slot buffer cache
. Details. The eight data requests are read , write , read , read , read , write , read , and read after unpinning . They yield four cache hits, four cache misses, four device reads and one device write. The pin and unpin are lifetime operations, excluded from the data-request count. At the read of , is older than but pinned, so the clean buffer for is evicted instead. Reading then returns 11 without device access even though its backing value is still 10. The second write supersedes 11; reading makes the older entry again. After unpinning, the final read of writes back 12 before evicting . After that request, backing contains 12, while the resident keys are .
This example exposes the value of retaining modified contents: a cache hit can return a newer value than a device read would. It also exposes the cost of reference counting constraints: a pinned older entry forces a different victim. An independent capacity sweep gives two cache hits, six cache misses and two device writes with one slot and no pin, but three cache hits, five cache misses and two device writes with two slots and no pin. With one slot and the stipulated pin, the read of cannot proceed. With three slots, both pin choices give five cache hits, three cache misses and no eviction-triggered write; remains dirty with value 12, so the absence of a device write does not establish file-system transaction durability.
The teaching implementation's
bget checks device and block number under a shared spinlock, increments refcnt before releasing that lock, and then takes the buffer's content mutex. On a cache miss it reserves an unused structure and clears valid; bread fills it only if necessary. This makes lookup and reservation share one synchronization boundary. brelse releases the content mutex and decrements the reference count. Unlike the stipulated trace policy, it moves the structure to the recent end only when that decrement reaches zero. [ref]This implementation has
valid and refcnt but no generic dirty flag. Its file-system journaling code records modified blocks with log_write, pins each newly recorded block, copies cached contents to the journal during commit, and unpins after installing the committed contents at their final locations. Thus journal pins keep modifications resident after callers release them. Its eviction loop does not perform the generic dirty-victim buffer cache writeback of our model. [ref][ref]Caching file contents in units based on memory pages, commonly by a file mapping and an offset, rather than necessarily by the device-and-block buffer cache key used above. A file's offset must be mapped to storage before device access; the two naming systems describe different layers. How page cache and buffer cache storage relate depends on the operating system and file system. A separate duplicate copy at each layer is not a universal requirement.
For the Linux kernel interfaces documented in Buffer Heads by The kernel development community, per-block state can be attached to larger cached memory units called Linux folios;
mark_buffer_dirty also marks the backing page dirty in the page cache. The documented reclamation checks include dirty state, pending buffer cache writeback and elevated reference counts. That documentation deprecates Linux buffer heads for new file systems in favour of iomap. These are platform-specific interfaces, not a definition that every page cache has Linux buffer heads or that every buffer cache has its own disjoint allocation. [ref]The Linux kernel's per-block state structure, which can be attached to a Linux folio. Its reference counting is distinct from its dirty and valid state.
// Destination: computer-science.bigb
File-system journaling makes a group of related file system updates recoverable after a crash by recording them in a journal before changing their final storage locations. The useful promise is file-system crash consistency: after journal replay, a protected file-system transaction has taken effect completely or has not taken effect. File-system transaction durability adds a different promise: an acknowledged update survives the crash. The protected group can contain only file metadata, or both file metadata and file data blocks, depending on the file-system journaling mode.
Consider a tiny file containing
ABCD in four-byte file data block 7. Its inode records size 4 and a reference to block 7. Appending EFGH in previously free block 9 requires three new stored records: , the inode with size 8 and references to blocks 7 and 9; , the block allocation bitmap marking block 9 allocated; and , file data block 9 containing EFGH. An inode reference to a block still marked free lets a later allocation overwrite live contents; allocating a block without recording its owner can leak space. Even mutually consistent file metadata can expose stale contents if has not reached storage. Chapter 42 of Crash Consistency: FSCK and Journaling (section 42.1, pp. 2–4) develops this three-record append problem; the names, block numbers and tiny contents here are original teaching choices.The first diagram protects all three records with write-ahead logging. Each journal payload record identifies a final destination and its complete new contents. A storage flush makes the complete journal payload durable before a separate journal commit record is written and made durable. Only then may a journal checkpoint copy the records to their final locations. The journal remains available until those copies have themselves passed a storage flush; it can then be retired and its space reused. Journal replay repairs an interrupted journal checkpoint using the retained new contents.[ref]
Write-ahead logging separates durable journal commit records from journal checkpoints and safe journal reuse
. Details. These are persistence dependencies, not a claim that issuing writes in this order is sufficient. A device can report completion while its volatile memory cache still holds the only copy. The Linux kernel documents both flushing earlier completed requests and making completion of a particular request depend on persistence. The timeline uses separate storage flushes to make the dependencies explicit; an implementation can use equivalent device-supported operations. A commit write can persist before its storage flush returns: recovery uses the actual durable marker, while acknowledgment waits for successful completion.[ref]
The appeal is that recovery knows exactly which updates to finish. It does not have to infer a user's intended contents from contradictory inodes and block allocation bitmaps. The cost includes writing the journal payload as well as its final copies and waiting for persistence boundaries. The journal records a recent transition, not a historical collection of recoverable files; file-system journaling is not a file-system backup.
The guarantee that recovery produces a valid file system state under a specified crash model. For the protected file-system transaction in our full-data example, the observable file is either
ABCD or ABCDEFGH; it never names a free block or exposes a half-applied protected append after recovery. This concerns the recovered state. The intermediate final-location records during a journal checkpoint may disagree until journal replay finishes, and live concurrent access requires separate synchronization.A program that compares independently represented facts in a file system, such as the hard link counts in inodes versus references found in directory entries, or the block allocation bitmap versus block ownership recovered from inodes and reserved structures. Disagreement detects corruption; repair policies must decide which evidence to trust and preserve recoverable data. A scan of names alone is insufficient to infer live ownership while the file system is changing or has unnamed open files, so checking requires a suitable stable view.
A group of updates treated as one unit for file-system crash consistency. The three-record append above is one explicitly chosen group. Real file-system journaling can batch several system calls or split large work across several groups; it does not automatically make an application's several writes into one file-system transaction. An atomic operation for competing software threads likewise does not by itself provide file-system transaction durability.
Creation shows why the group must include every related change. In a toy file-system directory with space for one more name, creating
notes with contents EFGH needs a directory entry mapping notes to newly allocated inode 42, an inode allocation bitmap marking 42 in use, that inode with size 4 and a reference to block 9, a block allocation bitmap allocating 9, and file data block 9 with the contents. An exposed directory entry must reach an allocated inode, and its content references must reach allocated file data blocks. Protecting just the inode leaves these relationships outside the file-system transaction. This simplified five-record creation excludes parent-directory growth, timestamps and allocation counters; a real file system must include its own additional dependencies.An acknowledged file-system transaction remains recoverable after a crash under the stated storage assumptions. In the full-data timeline, durable journal payload and a durable journal commit record suffice even before the journal checkpoint. A crash before durable commit can lose the append while still preserving file-system crash consistency. A successful ordinary write may only have reached a buffer cache; an application requiring persistence must use its documented interface and check errors. On Linux kernel-based systems, fsync on a file descriptor persists the associated file contents and file metadata, but persisting a newly created name also requires fsync on the containing file-system directory.[ref]
A system call for requesting persistence of a file's changed contents and associated file metadata. Its scope, successful completion and separate file-system directory synchronization matter to file-system transaction durability.
Making a recoverable journal entry durable before installing its protected updates at their final locations. In this article the journal contains new record images, so recovery redoes committed changes rather than undoing uncommitted changes. The diagrams assume one isolated file-system transaction, fresh journal slots with unambiguous record identities, no other writer, and atomic storage writes for each record and marker. A crash loses volatile state; previously durable contents remain intact. Storage flushes are honored, and recovery finishes before the file system is exposed. These are model assumptions, not a claim that every physical file data block write is indivisible on arbitrary hardware.
The protected new contents and destination information for a file-system transaction. In the diagrams, the three complete journal payload records correspond to . Records can reach storage in any order within this phase. The journal commit record cannot become durable until all three are durable.
A marker identifying a complete committed file-system transaction. A marker alone is insufficient if its journal payload is missing or belongs to another group. The simple model prevents this through ordering and fresh record identities. Real journal formats also validate record identities and integrity: the ext4 documentation says replay discards a group lacking its commit or having mismatched integrity checks.[ref]
Installing committed journal payload at final locations and making those copies durable. Safe retirement follows this completion, not merely submission of the copies. Discarding the journal while some copies remain only in a volatile memory cache can leave an unrecoverable mixture after a crash. Commit establishes a recoverable decision; a journal checkpoint permits reclamation of its journal space.
Reinstalling the complete new images of a committed file-system transaction after a crash. An uncommitted journal payload is ignored. Rewriting the same image has idempotence: repeating it gives the same contents, unlike performing "append four more bytes" again. If recovery itself crashes, retaining the journal until the journal checkpoint is durable allows the same recovery to restart.
The second diagram exhausts the small full-data model. A triple lists in that order; a bit is 1 exactly when the corresponding new record has persisted. Before commit, any of the subsets of journal payload records can be durable, while every final-location triple is
000. After commit, the journal triple is 111, while any of the eight final-location triples can be durable. Retirement adds one state: the live journal is gone and the final-location triple is 111. Thus there are distinct abstract crash states. Waiting for a storage flush without another record reaching persistence does not create an additional state. Retirement is modeled as an atomic change of live-log status: old payload bytes may remain physically present but are no longer part of the live journal.All 17 abstract crash states recover to the old or new three-record append under the stated storage flush and atomic storage write assumptions
. Details. The short correctness argument has three cases. Before commit, final locations are unchanged. With commit, the complete journal payload restores
111 regardless of the current final-location subset. After retirement, 111 is already durable. Each of the six partial final-location triples is repairable because its journal is retained. The enumeration is exhaustive for these records and phase rules; it excludes torn records, dishonest flush completion, media corruption, journal reuse across several file-system transactions and failures of the recovery code. It is a model check, not a measured crash test of ext4.Which contents pass through the journal and which persistence dependencies accompany them. The ext4 documentation distinguishes these modes: file metadata and contents both pass through the journal in
data=journal; only file metadata is journaled in data=ordered, with associated contents forced to final locations before metadata commit; only file metadata is journaled in data=writeback, without that data-before-commit dependency.[ref]For our append, full data journaling uses the three-record model. Ordered metadata journaling protects in the journal and first persists directly in free block 9. Before commit, changing an unreferenced free block does not change the visible old file; after commit, journal replay supplies the new file metadata and the new contents are already durable. Without that dependency, recovery can produce correct allocation and size but stale contents in block 9. Ordered metadata journaling does not give general all-or-nothing contents for overwrites of already allocated file data blocks, nor does it combine arbitrary application writes into a file-system transaction. The choice of protected records determines the scope of the guarantee.
A data structure using a bit per allocation unit to mark whether it is available. Its agreement with ownership references is a file-system crash consistency requirement; changing just this structure does not install the owner's update.
A file-system allocation bitmap for storage blocks. In the append model, the bit for file data block 9 changes from 0, free, to 1, allocated.
A file-system allocation bitmap for inodes. Appending to an existing file does not allocate an inode; creating the toy file does.
A request whose successful completion, under the device's stated contract, makes preceding covered writes survive loss of volatile memory cache contents. The diagrams wait for completion at each persistence boundary. Storage flush and atomic storage write are different properties: persistence of completed writes does not make a multi-record update indivisible.
A write whose durable result after a crash is either the complete old record or the complete new record. Its unit and failure conditions must be stated. The diagrams assume this property for each payload record, final-location record, commit marker and retirement marker; they do not derive it from the size of a file data block.
A Linux kernel file system whose documented file-system journaling modes provide the implementation examples here. The diagrams specify a simpler abstract protocol, not its full on-storage format.
A separately retained copy intended to recover files after loss or unwanted changes. A journal can be overwritten after a journal checkpoint and can faithfully commit an unwanted deletion; it does not supply that historical copy or protect against loss of the device holding the only copy.
// Target: computer-science.bigb
// Target: computer-science.bigb
// Target: computer-science.bigb
// Target: computer-science.bigb
// Target: computer-science.bigb
An inode is a data structure describing an object in an inode-based file system. For an ordinary file, it records file metadata such as type, owner, access permissions, size and hard link count, together with information locating the contents. In the direct-reference toy layout, inode 42 points to file data blocks 7 and 9; its two names
a and b live in directory entries. Other file systems can locate contents differently, so the diagram is a model of the separation rather than a universal storage layout. [ref]https://pages.cs.wisc.edu/~remzi/OSTEP/file-implementation.pdf. Arpaci-Dusseau and Arpaci-Dusseau, OSTEP chapter 40, sections 40.3–40.4: inodes and directory organization
An inode number is unique only within its file system among allocated objects. Two different file systems can each have inode 42, referring to unrelated files. POSIX exposes the file system's device identity as
st_dev and the file's serial number as st_ino; the pair identifies a file within a running system. Neither the bare inode number nor that pair is a permanent historical identifier: after reclamation, a later file can reuse the number. In particular, comparing an old saved number with a new one does not establish continuity of the file. [ref]https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_stat.h.html. The Open Group, POSIX.1-2017, sys/stat.h: joint identity from st_dev and st_ino
The design's appeal is that one inode unifies the file metadata and contents reached through several directory entries. Ritchie and Thompson's original 1974 account explicitly describes a directory entry as a name and an i-number, with the corresponding inode holding ownership, protection, size, content addresses and the hard link count. It gives a concrete early explanation of how names and file identity can evolve separately. [ref]
https://www.cs.cmu.edu/~15712/papers/ritchie74.pdf. Ritchie and Thompson, The UNIX Time-Sharing System, 1974, section 4: implementation of the file system
// Target: computer-science.bigb
An address in a traditional inode-based file system that points to a block of further disk block numbers, rather than directly to user contents. If a block can store addresses, one singly indirect block can locate up to file data blocks, and a doubly indirect arrangement can locate up to . The address blocks themselves consume allocated storage and must be reclaimed when their file no longer needs them. This lets small files use a short direct layout while supporting larger files.
An inode-based file system uses direct pointers for small files and successive levels of indirect block pointers for larger ones. If an address block contains pointers, a depth- region addresses file data blocks. With the inode resident, an uncached byte access in that region requires address-block reads and one file data block read.
A persistent collection of inodes indexed by inode numbers in an inode-based file system. The same term can also denote an operating system's in-memory collection of active inodes; these must be distinguished. A persistent table belongs to a particular volume, while the system-wide active collection keys entries by both volume identity and inode number.
An inode number identifies an allocated inode within one file system. A directory entry stores a name and this number; it does not normally store the file data block addresses. Different volumes can reuse the same number for unrelated files, and a reclaimed number can later be reused on the same volume. This is therefore a local, current identity, rather than a permanent identifier.
Articles by others on the same topic
There are currently no matching articles.





