-
Notifications
You must be signed in to change notification settings - Fork 69
File Systems
This page covers the Virtual File System (VFS) layer and supported filesystem implementations.
MentOS implements a Virtual File System abstraction that supports multiple filesystem types:
- EXT2 - Primary persistent filesystem on disk
- ProcFS - Virtual filesystem at /proc with process information
- Pipes - Named and unnamed pipes for IPC
Key Files:
-
kernel/inc/fs/vfs.h- VFS abstraction layer -
kernel/src/fs/vfs.c- VFS implementation -
kernel/src/fs/ext2.c- EXT2 filesystem driver -
kernel/src/fs/procfs.c- Process filesystem -
kernel/src/fs/pipe.c- Pipe implementation -
kernel/src/fs/namei.c- Path name resolution -
kernel/src/fs/fcntl.c- File control -
kernel/src/fs/ioctl.c- Device control -
kernel/src/fs/vfs.c- VFS core
The VFS abstracts filesystem operations:
User Programs
↓
POSIX API (open, read, write, etc.)
↓
VFS Layer (vfs.h/vfs.c)
├─ File descriptors
├─ Superblocks (mount points)
├─ Inodes (files/directories)
├─ Directory entries
└─ Generic file operations
↓
Filesystem Implementations
├─ EXT2 Driver
├─ ProcFS Driver
└─ Pipe Driver
↓
Hardware/Block Device (ATA disk, RAM, etc.)
Represents an open file instance:
typedef struct vfs_file {
vfs_inode_t *ino; // Associated inode
vfs_dentry_t *dentry; // Directory entry
unsigned long f_pos; // Current read/write position
int f_flags; // Open flags (O_RDONLY, O_WRONLY, etc.)
mode_t f_mode; // File mode/permissions
struct file_operations *f_ops; // Filesystem-specific operations
void *private_data; // Filesystem-specific data
} vfs_file_t;
// File operations
struct file_operations {
ssize_t (*read)(vfs_file_t *file, void *buf, size_t count);
ssize_t (*write)(vfs_file_t *file, const void *buf, size_t count);
off_t (*lseek)(vfs_file_t *file, off_t offset, int whence);
int (*ioctl)(vfs_file_t *file, unsigned int cmd, unsigned long arg);
};Represents a file or directory metadata:
typedef struct vfs_inode {
ino_t i_ino; // Inode number (unique per filesystem)
mode_t i_mode; // Type (regular, directory, symlink) + permissions
nlink_t i_nlink; // Hard link count
uid_t i_uid; // Owner user ID
gid_t i_gid; // Owner group ID
off_t i_size; // File size in bytes
time_t i_atime; // Last access time
time_t i_mtime; // Last modification time
time_t i_ctime; // Last inode change time
unsigned long i_blocks; // Number of disk blocks
unsigned long i_blksize; // Preferred I/O block size
struct inode_operations *i_ops;
void *i_sb; // Pointer to superblock
void *i_private; // Filesystem-specific data
} vfs_inode_t;
// Inode operations
struct inode_operations {
int (*mkdir)(vfs_inode_t *dir, const char *name, mode_t mode);
int (*rmdir)(vfs_inode_t *dir, const char *name);
int (*unlink)(vfs_inode_t *dir, const char *name);
vfs_inode_t *(*lookup)(vfs_inode_t *dir, const char *name);
};Represents a mounted filesystem:
typedef struct super_block {
dev_t s_dev; // Device identifier
unsigned long s_blocksize; // Filesystem block size
vfs_inode_t *root; // Root inode of filesystem
list_head_t s_list; // List of superblocks
file_system_type_t *s_type; // Filesystem type operations
void *s_fs_info; // Filesystem-specific data
} super_block_t;Represents a filename and link to inode:
typedef struct vfs_dentry {
char *d_name; // Filename
vfs_inode_t *d_inode; // Inode pointer
vfs_dentry_t *d_parent; // Parent directory
list_head_t d_child; // Child entries
list_head_t d_sibling; // Siblings in parent
} vfs_dentry_t;sys_open() - Open or create file
int fd = open("/path/to/file", O_RDONLY | O_CREAT, 0644);Open Flags:
-
O_RDONLY- Read-only -
O_WRONLY- Write-only -
O_RDWR- Read and write -
O_CREAT- Create if doesn't exist -
O_EXCL- Fail if exists (with O_CREAT) -
O_TRUNC- Truncate to zero length -
O_APPEND- Append to end
Mode Bits (for O_CREAT):
-
0644- rw-r--r-- (user read/write, others read) -
0755- rwxr-xr-x (user all, others read/execute)
VFS Internals:
open("/home/user/file.txt", O_RDONLY, 0)
↓
1. Parse path: /home/user/file.txt
- Root inode: get_root_inode()
- Look up "home" in root
- Look up "user" in /home
- Look up "file.txt" in /home/user
2. Get inode for file.txt
3. Create file descriptor table entry
- Allocate struct vfs_file
- Set position to 0
- Link to inode
- Add to process's fd table
4. Return file descriptor (fd ≥ 3)
read() - Read data
ssize_t n = read(fd, buf, 100);write() - Write data
ssize_t n = write(fd, "Hello", 5);File Position:
- Maintains position in each open file instance
- Automatically advances after read/write
- Can be changed with lseek()
Directory Listing (getdents)
struct dirent {
ino_t d_ino; // Inode number
unsigned short d_reclen; // Record length
unsigned short d_namlen; // Name length
char d_name[256]; // Filename
};
ssize_t n = getdents(fd, dirp, bufsize);EXT2 (Second Extended Filesystem) is the primary persistent storage filesystem.
Key Files:
-
kernel/src/fs/ext2.c- EXT2 implementation
Boot Block | Superblock | Group 0 | Group 1 | ... | Data Blocks
1 block | 1 block | ... | ... | |
Superblock (1024 bytes):
struct ext2_superblock {
uint32_t s_inodes_count; // Total inodes
uint32_t s_blocks_count; // Total blocks
uint32_t s_r_blocks_count; // Reserved blocks
uint32_t s_free_blocks_count; // Free blocks
uint32_t s_free_inodes_count; // Free inodes
uint32_t s_first_data_block; // First block number
uint32_t s_log_block_size; // Log2(block size) - 10
uint32_t s_log_frag_size; // Log2(fragment size)
uint32_t s_blocks_per_group; // Blocks per group
uint32_t s_frags_per_group; // Fragments per group
uint32_t s_inodes_per_group; // Inodes per group
uint32_t s_mtime; // Mount time
uint32_t s_wtime; // Write time
uint16_t s_mnt_count; // Mount count
uint16_t s_max_mnt_count; // Max mounts before fsck
// ... more fields
};Block Group Descriptor:
struct ext2_group_desc {
uint32_t bg_block_bitmap; // Block bitmap block
uint32_t bg_inode_bitmap; // Inode bitmap block
uint32_t bg_inode_table; // Inode table start block
uint16_t bg_free_blocks_count; // Free blocks in group
uint16_t bg_free_inodes_count; // Free inodes in group
uint16_t bg_used_dirs_count; // Directories in group
};Inode (128 bytes):
struct ext2_inode {
uint16_t i_mode; // Type and permissions
uint16_t i_uid; // User ID
uint32_t i_size; // File size
uint32_t i_atime; // Access time
uint32_t i_ctime; // Creation/change time
uint32_t i_mtime; // Modification time
uint32_t i_dtime; // Deletion time
uint16_t i_gid; // Group ID
uint16_t i_links_count; // Hard link count
uint32_t i_blocks; // Blocks used
uint32_t i_flags; // Flags
uint32_t i_osd1; // OS dependent
uint32_t i_block[15]; // 12 direct + 3 indirect pointers
uint32_t i_generation; // File version
uint32_t i_file_acl; // Extended attributes
uint32_t i_dir_acl; // Directory ACL
uint32_t i_faddr; // Fragment address
uint32_t i_osd2[3]; // OS dependent
};Block Addressing:
- First 12 entries (i_block[0-11]): Direct block pointers
- Entry 12 (i_block[12]): Indirect block (points to block of pointers)
- Entry 13 (i_block[13]): Double indirect
- Entry 14 (i_block[14]): Triple indirect
struct ext2_dir_entry {
uint32_t inode; // Inode number
uint16_t rec_len; // Record length (multiple of 4)
uint8_t name_len; // Name length
uint8_t file_type; // File type (regular, dir, symlink, etc.)
char name[EXT2_NAME_LEN]; // Filename
};File Types:
- 0 - Unknown
- 1 - Regular file
- 2 - Directory
- 3 - Character device
- 4 - Block device
- 5 - Named pipe (FIFO)
- 6 - Socket
- 7 - Symbolic link
Stored in inode i_mode:
Type bits (top 4 bits):
- 0x8000 - Regular file
- 0x4000 - Directory
- 0xA000 - Symbolic link
Permission bits (bottom 9 bits):
- 0700 - User permissions (rwx)
- 0070 - Group permissions (rwx)
- 0007 - Other permissions (rwx)
ProcFS provides a window into kernel data structures through the filesystem.
Key Files:
-
kernel/src/fs/procfs.c- ProcFS implementation
/proc/
├── uptime - System uptime
├── version - Kernel version
├── cpuinfo - CPU information
├── loadavg - Load average
├── meminfo - Memory information
├── modules - Loaded modules
├── [PID]/ - Process directories
│ ├── cmdline - Command line
│ ├── cwd - Current working directory (symlink)
│ ├── exe - Executable (symlink)
│ ├── environ - Environment variables
│ ├── fd/ - File descriptors (symlinks)
│ ├── maps - Memory mappings
│ ├── stat - Process statistics
│ └── status - Process status
├── [PID]/task/ - Thread directories
└── self - Link to current process
int fd = open("/proc/uptime", O_RDONLY);
char buf[256];
read(fd, buf, 256); // Read uptime in secondsMost /proc files are generated on-demand:
// When user reads /proc/[PID]/stat:
1. Kernel reads request
2. Looks up process by PID
3. Gathers statistics (CPU time, memory, etc.)
4. Formats as text
5. Returns to userPipes provide unidirectional inter-process communication.
Creating a pipe:
int fds[2];
pipe(fds);
// fds[0] = read end
// fds[1] = write endUsage Pattern:
int fds[2];
pipe(fds);
if (fork() == 0) {
// Child process - read from pipe
close(fds[1]); // Close write end
char buf[256];
read(fds[0], buf, 256);
printf("Received: %s\n", buf);
} else {
// Parent process - write to pipe
close(fds[0]); // Close read end
write(fds[1], "Hello", 5);
}sync() - Flush all filesystems
sync(); // Writes all dirty data to diskfsync() - Sync specific file
fsync(fd); // Syncs file to diskO_SYNC flag - Synchronous writes
fd = open("/file", O_WRONLY | O_SYNC);
write(fd, data, size); // Blocks until written to diskThe VFS maintains mount points:
Root Filesystem (/)
├── /home - EXT2 partition
├── /proc - ProcFS (virtual)
└── /dev - Device files
Registering a filesystem:
// In kernel startup
vfs_register_filesystem(&ext2_fs_type);
vfs_register_superblock("root", "/", &ext2_fs_type, root_inode);The kernel resolves paths through successive lookups:
/home/user/file.txt
↓
1. Get root inode
2. Look up "home" in root → inode_home
3. Check inode_home is directory
4. Look up "user" in inode_home → inode_user
5. Check inode_user is directory
6. Look up "file.txt" in inode_user → inode_file
7. Return inode_file
Symlink Following:
/home/user/link → /home/data/file.txt
↓
1. Look up /home/user/link → symlink inode
2. Read link target: /home/data/file.txt
3. Continue lookup from /home/data/file.txt
File access check:
bool can_access(inode_t *inode, int flags) {
uid_t uid = current->uid;
gid_t gid = current->gid;
mode_t mode = inode->i_mode;
// Owner check
if (uid == inode->i_uid) {
return (mode & 0700) & flags;
}
// Group check
else if (gid == inode->i_gid) {
return (mode & 0070) & flags;
}
// Others check
else {
return (mode & 0007) & flags;
}
}EXT2 supports extended attributes for storing metadata:
// Get extended attribute
ssize_t len = getxattr("/path", "user.name", buf, bufsize);
// Set extended attribute
setxattr("/path", "user.name", value, len, 0);Using mtools (for building)
In the build process:
# Create EXT2 filesystem image
dd if=/dev/zero of=rootfs.img bs=1024 count=16384
mkfs.ext2 -F rootfs.img
# Mount and populate
mount -t ext2 rootfs.img /tmp/mnt
cp programs /tmp/mnt/bin
umount /tmp/mnt- System Calls - File operation syscalls
- Kernel - VFS implementation details
- Development Guide - Using filesystem in programs
- EXT2 Specification - Complete filesystem format
- Linux kernel documentation - VFS architecture