Module inode

Module inode 

Source
Expand description

§Inode abstraction.

In a Unix-like file system, every object in a file system is represented by a data structure known as an inode. An inode (index node) stores metadata about a file system object, including its size, permissions, timestamps, and pointers to the underlying data blocks.

§Inode Internals

An Inode is the authoritative metadata structure for a file or directory. It stores essential information such as the object’s type, size, and pointers to data blocks, serving as the central handle for locating and managing file data.

At its core, an inode functions as an indexing structure: it maps a FileBlockNumber (a block’s position relative to the file) to a LogicalBlockAddress (the actual block location on disk). This mapping enables the file system to translate file-relative accesses into physical disk operations, bridging the logical view of a file with the underlying storage layout.

While the on-disk inode structure provides persistence, the kernel maintains in-memory inodes to speed up access and coordinate concurrent operations. To ensure this, KeOS provides the FastFileSystemInner::get_inode API to maintains single, global view of each inode inside the kernel.

The function looks up an inode number (InodeNumber) and returns a TrackedInode, which is a consistent view for a reference-counted, thread-safe wrapper around the in-memory inode. All kernel subsystems interact with inodes through this wrapper, ensuring proper synchronization.

A TrackedInode provides two key capabilities:

Together, get_inode and TrackedInode enforce a disciplined access model: there is exactly one in-memory representation of each inode, guarded by lock. This design keeps inode state consistent across memory and disk, even under concurrent file system activity.

§Inode Indexing in FFS

An inode does not store file data directly. Instead, it contains pointers to data blocks that hold the file’s contents. To balance efficiency for small files with scalability for large files, FFS adopts a tiered indexing scheme as follow:

             ┌───────────────────────────┐
             │         Inode             │
             ├───────────────────────────┤
             │ dblocks[0] → Data blk 0   │
             │ dblocks[1] → Data blk 1   │
             │ ...                       │
             │ dblocks[11] → Data blk11  │
             │                           │
             │ iblock 0 ───────────────┐ │
             │                         │ │
             │ diblock ─────────────┐  │ │
             └──────────────────────┬──┬─┘
                                    │  │
         ┌──────────────────────────┘  │
  ┌──────▼───────┐                 ┌───▼──────────┐
  │ Double ind.  │                 │ Indirect     │
  ├──────────────┤                 ├──────────────┤
  │ → iblock 1   │                 │ → Data blk12 │
  │ → iblock 2   │─┐               │ → Data blk13 │
  │ ..           │ │               │ ...          │
  │ → iblock 512 │ │               │ → Data blk523│
  └──────────────┘ │               └──────────────┘
                   │
    ┌──────────────┘
┌───▼──────────┐
│ Indirect blk │
├──────────────┤
│ → Data blk X │
│ → Data blk Y │
│ ...          │
└──────────────┘
  • Direct blocks (dblocks) The first 12 pointers directly reference data blocks. This makes access to small files very fast, as no additional lookup is required. Small files can therefore be served entirely from these direct entries.
  • Indirect block (iblock) When the file grows beyond the direct blocks, the inode refers to a single indirect block. This block is itself an array of data block pointers, extending the maximum file size significantly.
  • Double indirect block (diblock) For even larger files, the inode uses a double indirection. The inode points to a block that contains pointers to indirect blocks, each of which then contains pointers to data blocks. This extra level of indirection allows extremely large files to be addressed.

Together, these three levels form a hierarchical mapping from a FileBlockNumber (position within a file) to a LogicalBlockAddress (actual block on disk).

Your task is to implement the two core file access functions based on the indexing structure: Inode::get and Inode::grow.

  • Inode::get retrieves the disk location of a specific file block. It traverses the inode’s indexing structure and returns the corresponding disk block address, or None if the block has not been allocated.

  • Inode::grow ensures that the inode can cover a target file block number. If needed, it allocates new blocks and updates the inode’s indexing structure. All modifications are performed transactionally to guarantee consistency.

These functions use MetaData::load to access or create IndirectBlocks, and they update these blocks via the transaction API (using BlockPointsTo::write and BlockPointsToWriteGuard::submit). For reference on using these APIs, you may consult the documentation or implementation of the followings:

While implementing the requirements, you may encounter RunningTransaction struct. You do not need to understand it until the implementation of the journaling; for now, simply pass a reference to the required methods.

§Implementation Requirements

You need to implement the followings:

After implement the functionalities, move on to the next section.

Structs§

Inode
Represents an inode in memory, the metadata structure for a file or directory.