ParvaFS is the official file system of ParvaOS. This guide explains how ParvaFS organizes and manages data on disk, covering its design decisions, data structures, and core operations.
A file system organizes storage into structured blocks. Key concepts:
- Superblock: Stores file system metadata, such as a magic signature, version, block size, and pointers to other regions.
- Bitmap Blocks: Maintain a bit-level map of which data blocks are free or allocated, enabling quick allocation and deallocation.
- Data Blocks: Fixed-size units (512 bytes each) that hold file contents or directory entries.
- Directory Entries: Metadata records for files/directories, including type (file or directory), size, starting block address, and name.
- Directory Structure: A linked-chain of blocks representing a directory’s entries, enabling variable-sized directories.
ParvaFS divides the disk into several logical regions:
-
Reserved Region: First block (before superblock), reserved for boot code or other uses.
-
Superblock (512 bytes)
- Contains the magic signature
"PARVA FS"followed by padding zeros. - Written at a fixed address:
SUPERBLOCK_ADDR = (1 << 20) / 512.
- Contains the magic signature
-
Bitmap Region
- Starts at
BITMAP_ADDR_OFFSET = SUPERBLOCK_ADDR + 2. - Consists of
MAX_BLOCKS/8blocks, each with a 4-byte header and 508 bytes of bitmap data. - Each bit represents one data block: 0 = free, 1 = allocated.
- Starts at
-
Data Region
- Begins at
DATA_ADDR_OFFSET = BITMAP_ADDR_OFFSET + (MAX_BLOCKS/8). - Used to store directory and file data in 512‑byte blocks.
- Begins at
| Reserved | Superblock | Bitmap Blocks | Data Blocks |
-
Fixed 512‑byte buffer: first 4 bytes store a
nextblock pointer (big‑endian u32), remaining 508 bytes are data. -
Methods:
read(addr): load block from disk.write(): flush buffer to disk.alloc(): find a free data block viaBlockBitmapand initialize it.next(): return next chained block if pointer ≠ 0.set_next(addr): updatenextpointer.
-
Scans bitmap blocks to find free blocks.
-
Methods:
is_free(addr): check bit in bitmap for data block ataddr.alloc(addr): mark bit as 1 (used).free(addr): mark bit as 0 (free).next_free_addr(): linear scan to return the next free block address.
-
Represents a file or directory within a
Dir. -
Fields:
kind: FileType(Dir or File)addr: u32starting block of file contents or subdirectory.size: u32number of bytes (for files).name: String
-
Methods:
to_file(),to_dir(): convert intoFileorDirobject.
-
Represents a directory: stores its starting block address.
-
A directory’s blocks form a linked list, each block containing back‑to‑back entries of variable length:
- 1 byte type, 4 bytes address, 4 bytes size, 1 byte name length, N bytes name.
-
Methods:
root(): return root directory atDATA_ADDR_OFFSET.open(path): resolve each component, walking chained blocks.create_dir(name),create_file(name): append new entry and allocate block for its data.delete_entry(name): zero out entry pointer and free all data blocks.update_entry_size(name, size): update size bytes in entry header.read(): returnReadDiriterator.
-
Iterates entries in a
Dirby scanning the data region:- Parse the entry header.
- Read the name.
- Skip entries with addr = 0 (deleted).
- Advance to next block when needed.
-
In‑memory handle for reading/writing a file’s data blocks and metadata.
-
Fields:
name,addr(start),size,dir(parent).
-
Read/Write:
read(buf): sequentially read through chained blocks.write(buf): overwrite existing blocks, chain new ones, updateDirentry size.delete(path): wrapper overDir::delete_entry.
- Thin wrapper around ATA driver
ata::read/write(bus, dsk, block, buf). - Used via a global
Mutex<Option<BlockDevice>>guard.
init(): probe ATA buses/disks for the ParvaFS magic in superblock; if found, callmount(bus,dsk).mount(bus, dsk): set the globalBLOCK_DEVICEto enable all FS calls.
format(bus, dsk): write the magic to superblock, mount the device, and allocate the root directory block.
dirname(path),filename(path): split full paths at the last/.realpath(path): convert relative paths to absolute using process’s current directory.
-
Create
- Walk to parent
DirviaDir::open. - Call
create_fileorcreate_dirto append aDirEntry, allocate a data block.
- Walk to parent
-
Open
- Resolve full path to
Dir; find entry in last component; convert toFileorDir.
- Resolve full path to
-
Read
- For files: sequentially load each block’s data.
- For directories: iterate entries via
ReadDir.
-
Write
- Overwrite existing data blocks, allocate new ones if buffer larger than current chain, update
sizein directory header.
- Overwrite existing data blocks, allocate new ones if buffer larger than current chain, update
-
Delete
- Zero-out entry pointer, then free all data blocks in chain.
- Fixed block size (512 bytes) simplifies on‑disk layout but can lead to internal fragmentation.
- Linked blocks allow files/directories to grow arbitrarily but incur pointer overhead and slower seeks.
- Linear bitmap scan in
next_free_addr()can be slow for large disks; could be improved with hierarchical bitmaps.
Some aspects of ParvaFS were inspired by the moros project.