miniOS
x86_64 hobby kernel with SMP, VFS, and POSIX process model
Loading...
Searching...
No Matches
ext2.c File Reference
#include <miniOS/fs/ext2.h>
#include <miniOS/fs/vfs.h>
#include <miniOS/drivers/block_layer.h>
#include <miniOS/drivers/block_device.h>
#include <string.h>
#include <miniOS/io.h>
#include <miniOS/types.h>
Include dependency graph for ext2.c:

Classes

struct  find_ctx
struct  dir_empty_ctx

Functions

static int read_block (uint32_t block_no, void *buf)
static int write_block (uint32_t block_no, const void *buf)
static int read_inode (uint32_t ino, ext2_inode_t *out)
static int write_inode (uint32_t ino, const ext2_inode_t *in)
static int free_block (uint32_t block_no)
int ext2_truncate_inode (uint32_t ino, uint64_t new_size)
static int allocate_block (uint32_t *block_no)
static int allocate_inode (uint32_t *ino)
static int free_inode (uint32_t ino)
static uint8_t inode_vfs_type (const ext2_inode_t *inode)
static int inode_data_block (const ext2_inode_t *inode, uint32_t block_idx, uint32_t *block_no)
int ext2_mount (uint64_t part_lba_start, uint64_t part_lba_end)
int ext2_readdir (uint32_t dir_ino, uint64_t *offset, vfs_dirent_cb_t cb, void *ud)
static int find_entry_cb (const char *name, uint8_t nlen, uint32_t ino, uint8_t ftype, void *ud)
static int names_match (const char *name, uint8_t nlen, const char *target)
static int dir_empty_cb (const char *name, uint8_t nlen, uint32_t ino, uint8_t ftype, void *ud)
static int ext2_dir_is_empty (uint32_t dir_ino)
static int ext2_remove_dirent (uint32_t parent_ino, const char *name, uint32_t *removed_ino_out, uint8_t *removed_ftype_out)
int ext2_lookup (const char *path, vfs_inode_info_t *out)
int ext2_read_file (uint32_t ino, uint64_t off, void *buf, uint32_t len)
: Null-terminated filename (no '/' allowed).

ext2_create() - Allocate a new inode and add a directory entry to @parent_ino. @parent_ino: Inode number of the parent directory.

@out_ino: Set to the new inode number on success.

Algorithm:

  1. Allocate a new inode number via allocate_inode().
  2. Initialise the inode struct: i_mode=EXT2_S_IFREG|0644, i_links_count=1, i_size=0, i_blocks=0, all i_block[]=0. Zero all other fields.
  3. Write the new inode to disk via write_inode().
  4. Insert a new ext2_dirent_t in the parent directory: a. Iterate parent directory blocks; find the last block with space. "Space" = last entry's actual size (8 + name_len rounded up to 4) is less than its rec_len (meaning the last entry was padded). b. Shrink the last entry's rec_len to its minimal size (8 + name_len aligned up to 4 bytes), then append the new entry using the freed space. c. The new entry's rec_len fills to the end of the block. d. If no space in existing blocks, allocate a new block, update i_block[k] and i_blocks in the parent inode, write parent inode back, then place the new entry at the start of the new block with rec_len=g_block_size.
  5. Write the modified directory block back to disk.
Returns
: 0 on success, -1 on I/O error, -2 on ENOSPC (no free inodes/blocks).
int ext2_create (uint32_t parent_ino, const char *name, uint16_t mode, uint32_t *out_ino)
int ext2_readlink (uint32_t ino, char *buf, uint32_t len)
: Null-terminated filename (no '/' allowed; max 255 chars).

ext2_symlink() - Create a new symlink inode and directory entry. @parent_ino: Inode number of the parent directory.

@target: Symlink target string (null-terminated; stored as-is).

Fast-link used when strlen(target) <= 60; target stored inline in i_block[]. Block-link used when strlen(target) > 60; allocates one data block. Directory entry file_type set to 7 (EXT2_FT_SYMLINK).

Returns
: 0 on success, -1 on I/O error, -2 on ENOSPC.
int ext2_symlink (uint32_t parent_ino, const char *name, const char *target)
int ext2_write_file (uint32_t ino, uint64_t off, const void *buf, uint32_t len)
int ext2_flush_inode (uint32_t ino)

Variables

static uint64_t g_part_start
static uint64_t g_part_end
static uint32_t g_block_size
static uint32_t g_sectors_per_block
static uint32_t g_inodes_per_group
static uint32_t g_blocks_per_group
static uint32_t g_inode_size
static uint32_t g_first_data_block
static uint32_t g_sb_blocks_count
static uint32_t g_sb_inodes_count
static uint8_t g_block_buf [4096]
static uint8_t g_indirect_buf [4096]
static uint8_t g_dindirect_buf [4096]

: Null-terminated directory name (no '/' allowed).

ext2_mkdir() - Create a new directory inode and add a directory entry in @parent_ino. @parent_ino: Inode number of the parent directory.

@out_ino: Set to the new directory inode number on success.

Algorithm:

  1. Allocate a new inode and a data block.
  2. Initialise the new inode: i_mode=EXT2_S_IFDIR|0755, i_links_count=2 (one for the parent's dirent, one for the '.' entry inside the new dir), i_size=g_block_size, i_blocks=g_block_size/512, i_block[0]=new_block.
  3. Write the new inode's data block with '.' (points to new_ino) and '..' (points to parent_ino) directory entries filling the full block.
  4. Insert a new ext2_dirent_t (file_type=EXT2_FT_DIR) in the parent directory, using the same split-and-append or new-block logic as ext2_create().
  5. Increment the parent inode's i_links_count (the '..' hard link).
  6. Increment bg_used_dirs_count in the block group descriptor.
Returns
: 0 on success, -1 on I/O error, -2 on ENOSPC (no free inodes/blocks).
static vfs_ops_t g_ext2_vfs_ops
int ext2_mkdir (uint32_t parent_ino, const char *name, uint32_t *out_ino)
int ext2_rmdir (uint32_t parent_ino, const char *name)
static int ext2_add_dirent (uint32_t parent_ino, const char *name, uint32_t ino, uint8_t ftype)
static uint8_t ext2_mode_to_ft (uint16_t mode)
static int ext2_rename (uint32_t old_parent_ino, const char *old_name, uint32_t new_parent_ino, const char *new_name)
static int ext2_unlink (uint32_t parent_ino, const char *name)
int ext2_chmod (uint32_t ino, uint16_t mode)
int ext2_mknod (uint32_t parent_ino, const char *name, uint16_t mode, uint32_t dev, uint32_t *out_ino)
static int ext2_statfs (vfs_statfs_t *out)
static int ext2_fs_mount (const char *source, const char *target, const void *data)
void ext2_init (void)

Function Documentation

◆ allocate_block()

int allocate_block ( uint32_t * block_no)
static
Here is the call graph for this function:
Here is the caller graph for this function:

◆ allocate_inode()

int allocate_inode ( uint32_t * ino)
static
Here is the call graph for this function:
Here is the caller graph for this function:

◆ dir_empty_cb()

int dir_empty_cb ( const char * name,
uint8_t nlen,
uint32_t ino,
uint8_t ftype,
void * ud )
static
Here is the caller graph for this function:

◆ ext2_add_dirent()

int ext2_add_dirent ( uint32_t parent_ino,
const char * name,
uint32_t ino,
uint8_t ftype )
static
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ext2_chmod()

int ext2_chmod ( uint32_t ino,
uint16_t mode )

ext2_chmod() - Change permission bits of an inode on disk. @ino: Inode number. @mode: New permission bits (low 12 bits); file type bits are preserved.

Reads the inode, replaces the low 12 bits of i_mode, writes it back.

Returns
: 0 on success, -1 on I/O error.
Here is the call graph for this function:

◆ ext2_create()

int ext2_create ( uint32_t parent_ino,
const char * name,
uint16_t mode,
uint32_t * out_ino )
Here is the call graph for this function:

◆ ext2_dir_is_empty()

int ext2_dir_is_empty ( uint32_t dir_ino)
static
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ext2_flush_inode()

int ext2_flush_inode ( uint32_t ino)

ext2_flush_inode() - Write the current inode state back to disk. @ino: Inode number to flush.

Reads the inode from disk and immediately writes it back. Used by vfs_close() (via ops->flush) to ensure i_size and i_block[] are durable after writes. The ATA flush command is issued separately by the caller.

Returns
: 0 on success, -1 on I/O error.
Here is the call graph for this function:

◆ ext2_fs_mount()

int ext2_fs_mount ( const char * source,
const char * target,
const void * data )
static
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ext2_init()

void ext2_init ( void )

ext2_init() - Register the ext2 filesystem type with the VFS registry.

After this call, vfs_mount_fstype("ext2", source, target, data) can be used to mount any ext2 partition by passing a vfs_mount_data_t as @data. ext2_init() does NOT mount anything itself; it only registers the type. Call once during kernel startup, before the first ext2 mount.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ext2_lookup()

int ext2_lookup ( const char * path,
vfs_inode_info_t * out )

ext2_lookup() - Resolve path components starting from root inode 2. @path: Absolute path; must start with '/'.

Tokenises @path on '/' using strtok-like manual scanning. For each component, calls ext2_readdir with find_entry_cb to search the current directory inode for a matching entry. Updates current_ino to the matched child inode and continues to the next component. Returns current_ino after all components.

Returns
: Final inode number (>= 2), or 0 if any component is not found.
Here is the call graph for this function:

◆ ext2_mkdir()

int ext2_mkdir ( uint32_t parent_ino,
const char * name,
uint32_t * out_ino )
Here is the call graph for this function:

◆ ext2_mknod()

int ext2_mknod ( uint32_t parent_ino,
const char * name,
uint16_t mode,
uint32_t dev,
uint32_t * out_ino )

ext2_mknod() - Create a char or block device inode in @parent_ino.

Allocates an inode with i_mode = @mode (EXT2_S_IFCHR|perms or EXT2_S_IFBLK|perms), i_links_count = 1, no data blocks, and stores @dev in i_block[0] (traditional Linux ext2 device number encoding). Inserts a directory entry with EXT2_FT_CHRDEV or EXT2_FT_BLKDEV into the parent directory using the same split-and-append logic as ext2_create.

Here is the call graph for this function:

◆ ext2_mode_to_ft()

uint8_t ext2_mode_to_ft ( uint16_t mode)
static
Here is the caller graph for this function:

◆ ext2_mount()

int ext2_mount ( uint64_t part_lba_start,
uint64_t part_lba_end )

ext2_mount() - Read superblock and initialise filesystem global state. @part_lba_start: LBA of partition start; stored in g_part_start.

Reads 2 sectors at part_lba_start + 2 (superblock at 1024-byte offset). Checks s_magic == 0xEF53. Sets g_block_size = 1024 << s_log_block_size, g_sectors_per_block, g_inodes_per_group, g_inode_size (128 for rev 0, s_inode_size for rev 1), g_first_data_block. Reads block group descriptor table at block g_first_data_block+1 to obtain inode table base. Single block group assumed (8 MiB image has exactly one group).

Returns
: 0 on success, -1 on ATA I/O error or invalid magic.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ext2_read_file()

int ext2_read_file ( uint32_t ino,
uint64_t off,
void * buf,
uint32_t len )

ext2_read_file() - Copy file data from inode blocks into caller's buffer. @ino: Inode number of the regular file. @off: Starting byte offset within the file. @buf: Destination buffer. @len: Requested byte count.

Reads the inode to get i_size and i_block[]. Clamps read to i_size. Computes starting block index as off / g_block_size, byte offset within block as off % g_block_size. Resolves each logical block through direct pointers and the first single-indirect block, then copies the relevant slice. Iterates until @len bytes are copied or EOF.

Returns
: Bytes copied (0..@len), or -1 on I/O error.
Here is the call graph for this function:

◆ ext2_readdir()

int ext2_readdir ( uint32_t dir_ino,
uint64_t * offset,
vfs_dirent_cb_t cb,
void * ud )

ext2_readdir() - Iterate directory entries in all direct blocks of a directory inode. @dir_ino: Directory inode number. @cb: Called for each valid entry (inode != 0) with name (not null-terminated), name_len, child inode, file_type, and @ud. @ud: Forwarded to @cb unchanged.

Reads each direct block (i_block[0..11]) of the directory inode. Within each block, walks ext2_dirent_t records by incrementing a byte pointer by rec_len. Skips entries with inode==0 (deleted or padding entries). Stops block iteration when the block pointer address reaches block_end. Returns early if @cb returns non-zero.

Returns
: 0 when all entries visited, or first non-zero return value from @cb.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ext2_readlink()

int ext2_readlink ( uint32_t ino,
char * buf,
uint32_t len )

ext2_readlink() - Read the target string of a symlink inode. @ino: Inode number of the symlink (i_mode must have EXT2_S_IFLNK set). @buf: Output buffer. NOT null-terminated (POSIX readlink semantics). @len: Maximum bytes to copy into buf.

Fast-link (i_blocks == 0): target stored inline in i_block[0..14] bytes. Block-link (i_blocks > 0): target stored in data block i_block[0].

Returns
: Number of bytes copied (>= 0), or -1 on error.
Here is the call graph for this function:

◆ ext2_remove_dirent()

int ext2_remove_dirent ( uint32_t parent_ino,
const char * name,
uint32_t * removed_ino_out,
uint8_t * removed_ftype_out )
static
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ext2_rename()

int ext2_rename ( uint32_t old_parent_ino,
const char * old_name,
uint32_t new_parent_ino,
const char * new_name )
static
Here is the call graph for this function:

◆ ext2_rmdir()

int ext2_rmdir ( uint32_t parent_ino,
const char * name )
Here is the call graph for this function:

◆ ext2_statfs()

int ext2_statfs ( vfs_statfs_t * out)
static
Here is the call graph for this function:

◆ ext2_symlink()

int ext2_symlink ( uint32_t parent_ino,
const char * name,
const char * target )
Here is the call graph for this function:

◆ ext2_truncate_inode()

int ext2_truncate_inode ( uint32_t ino,
uint64_t new_size )

ext2_truncate_inode() - Truncate a regular file to zero bytes. @ino: Inode number of the file to truncate.

Walks i_block[0..11] (direct blocks) and i_block[12..14] (indirect pointers, treated as direct block references — single-group, small files only use direct blocks). Frees each allocated block via free_block(). Clears all block pointer entries in the inode (i_block[0..14] = 0). Resets i_size = 0 and i_blocks = 0. Writes the updated inode back to disk.

Only truncates regular files (i_mode & EXT2_S_IFREG). Returns -1 for directories or other non-regular types.

Returns
: 0 on success, -1 on type mismatch or I/O error.
Here is the call graph for this function:

◆ ext2_unlink()

int ext2_unlink ( uint32_t parent_ino,
const char * name )
static
Here is the call graph for this function:

◆ ext2_write_file()

int ext2_write_file ( uint32_t ino,
uint64_t off,
const void * buf,
uint32_t len )

ext2_write_file() - Write bytes to an inode's data blocks, allocating as needed. @ino: Inode number of the target regular file. @off: Byte offset within the file. @buf: Source data. @len: Number of bytes to write.

For each block touched by the range [off, off+len):

  • If the block is not yet allocated (i_block[k]==0), allocate it and zero it.
  • If the write is a partial block, read-modify-write (read existing, patch bytes).
  • If the write covers a full block, write directly without reading first. Updates i_size = max(i_size, off + bytes_written). Does NOT write back the inode (caller calls ext2_flush_inode then ata_flush via vfs_close).
Returns
: Number of bytes written, or -1 on I/O or allocation error.
Here is the call graph for this function:

◆ find_entry_cb()

int find_entry_cb ( const char * name,
uint8_t nlen,
uint32_t ino,
uint8_t ftype,
void * ud )
static
Here is the caller graph for this function:

◆ free_block()

int free_block ( uint32_t block_no)
static
Here is the call graph for this function:
Here is the caller graph for this function:

◆ free_inode()

int free_inode ( uint32_t ino)
static
Here is the call graph for this function:
Here is the caller graph for this function:

◆ inode_data_block()

int inode_data_block ( const ext2_inode_t * inode,
uint32_t block_idx,
uint32_t * block_no )
static
Here is the call graph for this function:
Here is the caller graph for this function:

◆ inode_vfs_type()

uint8_t inode_vfs_type ( const ext2_inode_t * inode)
static
Here is the caller graph for this function:

◆ names_match()

int names_match ( const char * name,
uint8_t nlen,
const char * target )
static
Here is the caller graph for this function:

◆ read_block()

int read_block ( uint32_t block_no,
void * buf )
static
Here is the call graph for this function:
Here is the caller graph for this function:

◆ read_inode()

int read_inode ( uint32_t ino,
ext2_inode_t * out )
static
Here is the call graph for this function:
Here is the caller graph for this function:

◆ write_block()

int write_block ( uint32_t block_no,
const void * buf )
static
Here is the call graph for this function:
Here is the caller graph for this function:

◆ write_inode()

int write_inode ( uint32_t ino,
const ext2_inode_t * in )
static
Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ g_block_buf

uint8_t g_block_buf[4096]
static

◆ g_block_size

uint32_t g_block_size
static

◆ g_blocks_per_group

uint32_t g_blocks_per_group
static

◆ g_dindirect_buf

uint8_t g_dindirect_buf[4096]
static

◆ g_ext2_vfs_ops

vfs_ops_t g_ext2_vfs_ops
static
Initial value:
= {
.lookup = ext2_lookup,
.read = ext2_read_file,
.readdir = ext2_readdir,
.write = ext2_write_file,
.flush = ext2_flush_inode,
.create = ext2_create,
.unlink = ext2_unlink,
.rename = ext2_rename,
.mkdir = ext2_mkdir,
.rmdir = ext2_rmdir,
.truncate = ext2_truncate_inode,
.readlink = ext2_readlink,
.symlink = ext2_symlink,
.mknod = ext2_mknod,
.chmod = ext2_chmod,
.statfs = ext2_statfs,
}
int ext2_chmod(uint32_t ino, uint16_t mode)
Definition ext2.c:1802
static int ext2_rename(uint32_t old_parent_ino, const char *old_name, uint32_t new_parent_ino, const char *new_name)
Definition ext2.c:1671
static int ext2_statfs(vfs_statfs_t *out)
Definition ext2.c:1949
static int ext2_unlink(uint32_t parent_ino, const char *name)
Definition ext2.c:1748
int ext2_mkdir(uint32_t parent_ino, const char *name, uint32_t *out_ino)
Definition ext2.c:1369
int ext2_write_file(uint32_t ino, uint64_t off, const void *buf, uint32_t len)
Definition ext2.c:1184
int ext2_lookup(const char *path, vfs_inode_info_t *out)
Definition ext2.c:733
int ext2_mknod(uint32_t parent_ino, const char *name, uint16_t mode, uint32_t dev, uint32_t *out_ino)
Definition ext2.c:1820
int ext2_create(uint32_t parent_ino, const char *name, uint16_t mode, uint32_t *out_ino)
Definition ext2.c:882
int ext2_readdir(uint32_t dir_ino, uint64_t *offset, vfs_dirent_cb_t cb, void *ud)
Definition ext2.c:556
int ext2_flush_inode(uint32_t ino)
Definition ext2.c:1341
int ext2_readlink(uint32_t ino, char *buf, uint32_t len)
Definition ext2.c:1019
int ext2_rmdir(uint32_t parent_ino, const char *name)
Definition ext2.c:1532
int ext2_symlink(uint32_t parent_ino, const char *name, const char *target)
Definition ext2.c:1060
int ext2_truncate_inode(uint32_t ino, uint64_t new_size)
Definition ext2.c:225
int ext2_read_file(uint32_t ino, uint64_t off, void *buf, uint32_t len)
Definition ext2.c:809

◆ g_first_data_block

uint32_t g_first_data_block
static

◆ g_indirect_buf

uint8_t g_indirect_buf[4096]
static

◆ g_inode_size

uint32_t g_inode_size
static

◆ g_inodes_per_group

uint32_t g_inodes_per_group
static

◆ g_part_end

uint64_t g_part_end
static

◆ g_part_start

uint64_t g_part_start
static

◆ g_sb_blocks_count

uint32_t g_sb_blocks_count
static

◆ g_sb_inodes_count

uint32_t g_sb_inodes_count
static

◆ g_sectors_per_block

uint32_t g_sectors_per_block
static