Skip to content

File Systems

Galfurian edited this page Aug 19, 2026 · 10 revisions

This page teaches you how MentOS manages files and directories on disk.

Important: All code references point to the actual MentOS source code. Explore these files in kernel/src/fs/ and kernel/inc/fs/ to understand how MentOS implements file systems.

The Problem: How Do We Store Files?

Scenario: You write a file myfile.txt to disk. Then you turn off the computer. When you turn it back on, the file is still there.

Question: How does the computer remember where myfile.txt is? What's the structure on disk?

Answer: A filesystem - a way to organize data on disk into files and folders.

The Three Layers of File I/O

MentOS uses three layers (like a sandwich):

┌─────────────────────────────────────┐
│  User Programs (ring 3)             │
│  printf("Hello");                   │
│  read(fd, buf, 10);                 │
│  write(fd, "data", 4);              │
├─────────────────────────────────────┤
│  VFS Layer (kernel)                 │
│  "I don't care HOW you store files" │
│  Just provide: open, read, write    │
│  Works with ANY filesystem          │
├─────────────────────────────────────┤
│  Filesystem Drivers                 │
│  EXT2: "Here's how WE do it"        │
│  ProcFS: "And here's how WE do it"  │
└─────────────────────────────────────┘
        ↓
    Hard Disk

Key Idea: The VFS (Virtual File System) is a plug-and-play interface. Add a new filesystem? Just implement the VFS interface!

What is mounted, and where

MentOS mounts exactly two filesystems at boot, both from kmain():

Mount point Filesystem Backing store Set up by
/ EXT2 the ATA disk /dev/hda vfs_mount("ext2", "/", "/dev/hda")
/proc procfs nothing — generated on demand procfs_module_init() + mount

The root image is built by the filesystem CMake target from the contents of the filesystem/ directory: mke2fs ... -b 4096 ... rootfs.img 32M, i.e. a 32 MiB EXT2 image with 4 KiB blocks, handed to QEMU as an IDE drive. After mounting, fhs_initialize() creates the standard Filesystem Hierarchy Standard directories.

procfs is a separate filesystem driver (kernel/src/fs/procfs.c), not a corner of EXT2. Nothing under /proc is stored on disk.

1. The VFS Layer - "The Adapter"

What is the VFS?

The VFS is a layer of abstraction that says: "I don't care if you're using EXT2, FAT32, or a USB drive. You all must support the same interface."

Implementation: kernel/src/fs/vfs.c and kernel/inc/fs/vfs.h

Key VFS Data Structures

Think of these as "handles" to files and directories. The three concepts below — file descriptor, inode, directory entry — are the standard Unix vocabulary, and they are the right way to reason about file I/O on any Unix-like system.

How MentOS maps onto that vocabulary. Linux gives each concept its own kernel structure: struct inode (the on-disk object), struct dentry (a name in the tree) and struct file (one open instance, with its own position). MentOS folds all three into a single vfs_file_t (kernel/inc/fs/vfs_types.h), which carries the name, the inode number ino, the mode/uid/gid, and the read offset f_pos at once. Only the per-process descriptor is a separate structure (vfs_file_descriptor_t).

So: learn the concepts as described below, but do not expect to find struct inode or struct dentry in the MentOS tree — and be aware that because f_pos lives inside the shared vfs_file_t, MentOS does not cleanly separate "the file" from "this open of the file".

File Descriptor (fd)

When you do: fd = open("/home/user/file.txt", O_RDONLY)

The kernel creates a vfs_file_descriptor_t:
┌─────────────────────────────────┐
│ File Descriptor (fd = 3)        │
├─────────────────────────────────┤
│ • Flags mask: O_RDONLY          │
│ • Points to: vfs_file_t         │
└─────────────────────────────────┘
       ↓
    vfs_file_t (name, flags, f_pos, operations)
       ↓
    Filesystem driver (EXT2 / procfs)

Real analogy: Like a bookmark in a book. The fd tells you WHERE YOU ARE in the file (position = page 5).

Two properties that matter in MentOS:

  • A file descriptor is a per-process index, not a global identifier. Each task_struct carries its own fd_list array, so fd 3 in one process and fd 3 in another are unrelated. This is why fork() can duplicate descriptors (vfs_dup_task) without the two processes fighting over numbers.
  • The table is small and fixed: MAX_OPEN_FD is 16 (kernel/inc/fs/vfs.h). There is no growable descriptor table, so a program that leaks descriptors will hit -EMFILE quickly. Remember that 0, 1 and 2 are already taken.

Inode (index node)

Every file/directory has an INODE that stores:
  • File type (regular file, directory, symlink, device)
  • Permissions (rwxrwxrwx)
  • Owner (user ID, group ID)
  • Size in bytes
  • Timestamps (created, modified, accessed)
  • WHERE ON DISK is the file data?

Inode does NOT store the filename!

Real analogy: Like a library card catalog entry. It describes WHAT a file is, but not its name or location.

Directory Entry (dentry)

Maps FILENAME to INODE:
  "file.txt" → inode #12345
  "folder/" → inode #12346
  ".." → inode #12340 (parent)

Real analogy: Like page 5 in the phone book: "Smith, John" → phone number "555-1234"

How open() Works Under the Hood

fd = open("/home/user/file.txt", O_RDONLY)

1. PARSE PATH: /home/user/file.txt
   Start at root inode (inode #2)
   │
   ├─ Directory lookup: "home"
   │  └─ Read root inode → find "home" entry → get inode #1000
   │
   ├─ Directory lookup: "user"  
   │  └─ Read inode #1000 → find "user" entry → get inode #1001
   │
   └─ Directory lookup: "file.txt"
      └─ Read inode #1001 → find "file.txt" entry → get inode #1002

2. GET INODE METADATA from disk (inode #1002)
   • Size: 150 bytes
   • Permissions: rw-r--r--
   • Owner: uid 1000
   • Data blocks on disk: 256, 257, 258

3. CREATE FILE DESCRIPTOR
   allocate fd = 3 (first free slot)
   fd[3].position = 0
   fd[3].inode = inode #1002
   fd[3].flags = O_RDONLY

4. RETURN 3 to user

Result: When user calls read(3, buf, 10), kernel knows exactly which file to read!

How read() Works

ssize_t n = read(3, buf, 10);  // Read 10 bytes from fd #3
1. Kernel looks up fd #3
   → Gets inode #1002
   → Knows position = 0

2. Kernel calculates which DISK BLOCK to read
   • File position = 0
   • Block size = 4096 bytes
   • Need byte 0-9
   → Those are in block #256 on disk

3. Kernel reads block #256 from disk
   → Gets 4096 bytes

4. Kernel copies bytes 0-9 to user buffer
   buf = "Hello file"  ← user gets this

5. Kernel updates position
   fd[3].position = 10

6. RETURN 10 (bytes read)

MentOS Implementation:

  • Path parsing: kernel/src/fs/namei.c (name to inode lookup)
  • File descriptor management: kernel/src/fs/vfs.c
  • Read/write operations: kernel/src/fs/read_write.c and kernel/src/fs/vfs.c

2. EXT2 Filesystem - "How Data Lives on Disk"

What is EXT2?

EXT2 is a specific filesystem implementation. It says: "Here's HOW I organize data on this disk."

Implementation: kernel/src/fs/ext2.c

In MentOS the root image is created with a 4 KiB block size, so where the diagrams below say "block", read 4096 bytes.

Disk Layout

Think of a disk like a filing cabinet:

┌─────────────────────────────────────────┐
│ Boot Sector (1 block) [How to boot OS]  │
├─────────────────────────────────────────┤
│ Superblock (1 block) [Disk metadata]    │
├─────────────────────────────────────────┤
│ Inode Bitmap (several blocks)           │
│ [Which inodes are used? (bitmap)]       │
├─────────────────────────────────────────┤
│ Block Bitmap (several blocks)           │
│ [Which disk blocks are used?]           │
├─────────────────────────────────────────┤
│ Inode Table (many blocks)               │
│ [Inode #1, #2, #3, ... metadata]        │
├─────────────────────────────────────────┤
│ Data Blocks (bulk of disk)              │
│ [Actual file contents]                  │
└─────────────────────────────────────────┘

Key Concept: The Inode

On disk, each inode is 128 bytes and contains:

struct ext2_inode {
    uint16_t i_mode;           // File type + permissions
    uint16_t i_uid;            // Owner user ID
    uint32_t i_size;           // File size in bytes
    uint32_t i_atime;          // Last access time
    uint32_t i_ctime;          // Creation time
    uint32_t i_mtime;          // Modification time
    uint16_t i_gid;            // Owner group ID
    uint16_t i_links_count;    // Hard link count
    uint32_t i_blocks;         // Disk blocks used
    uint32_t i_flags;          // Special flags
    
    // MOST IMPORTANT: Where is the file data on disk?
    uint32_t i_block[15];      // "Block pointers" (see next section)
};

Block Pointers: How Does EXT2 Find File Data?

This is the clever part!

Each inode has 15 block pointers:

  • i_block[0-11] = Direct pointers (point directly to data blocks)
  • i_block[12] = Indirect pointer (points to block of pointers)
  • i_block[13] = Double indirect
  • i_block[14] = Triple indirect

Example: Reading a small file (5KB)

File: myfile.txt (5KB = 1.25 blocks, rounded to 2 blocks)

Inode #42:
  i_size = 5120
  i_block[0] = 1000  ← Block 1000 on disk
  i_block[1] = 1001  ← Block 1001 on disk
  i_block[2-14] = 0  ← Unused

When reading bytes 0-5119:
1. Check inode #42
2. See that data is in blocks 1000-1001
3. Read block 1000 (4096 bytes) → get bytes 0-4095
4. Read block 1001 (1024 bytes) → get bytes 4096-5119
5. Done!

Example: Reading a huge file (5MB)

File: bigfile.bin (5MB)

Inode #50:
  i_size = 5242880
  i_block[0-11] = 2000, 2001, 2002, ...  (12 direct blocks = 48KB)
  i_block[12] = 5000  ← Indirect block!

When we need block #1000 in the file:
1. Calculate: block 1000 is beyond direct pointers
2. Read "indirect block" #5000 from disk
3. It contains: [3000, 3001, 3002, 3003, ...]
   Each entry is a block pointer!
4. From that, get pointer to actual block
5. Read that block
6. Done!

Why this design?

  • Small files: Fast! Use direct pointers
  • Large files: Works! Use indirect pointers
  • Huge files: Still works! Use double/triple indirect

Directory Entries on Disk

Directories are just files that store directory entries:

Directory /home/user/ (stored as a file on disk)

Bytes:  Data
────────────────────────────────────────────
0-3:    12345 (inode of "desktop")
4-255:  "desktop\0" + padding

256-259: 12346 (inode of "documents")
260-383: "documents\0" + padding

384-387: 12347 (inode of "file.txt")
388-415: "file.txt\0" + padding

When kernel does ls /home/user/:

  1. Open inode of /home/user/ (it's a directory)
  2. Read its data blocks (which contain directory entries)
  3. Parse entries: filename → inode → lookup inode metadata
  4. Print: file.txt (12347, 5KB, rw-r--r--, Jan 15 14:30)

3. Other Filesystems

ProcFS - "The Virtual Filesystem"

Location: kernel/src/fs/procfs.c

ProcFS is not on disk - it's generated on-the-fly by the kernel. It is mounted at /proc by kmain() during boot (not by /bin/init), and it is a distinct filesystem type registered with the VFS — it shares no code and no storage with the EXT2 root.

Examples of procfs entries in MentOS:

- `/proc/uptime`, `/proc/version`, `/proc/meminfo`, `/proc/stat` (see `kernel/src/io/proc_system.c`)
- `/proc/ipc/msg`, `/proc/ipc/sem`, `/proc/ipc/shm` (see `kernel/src/io/proc_ipc.c`)
- `/proc/feedback` (scheduler feedback, see `kernel/src/io/proc_feedback.c`)
- `/proc/video` (video info, see `kernel/src/io/proc_video.c`)
- `/proc/<pid>/cmdline`, `/proc/<pid>/stat` (see `kernel/src/io/proc_running.c`)

Example: When user reads /proc/1/stat:

  1. Kernel sees: "This is a procfs file"
  2. procfs routes the read to the proc-running callbacks
  3. __procr_do_stat() formats the process data
  4. User reads the formatted stat line

Why? Provides Unix-style interface to kernel data without storing anything on disk!

The standard descriptors 0, 1, 2

On a conventional Unix system, the first process gets descriptors 0/1/2 backed by a terminal device, and the shell later manages the controlling terminal and job control through it.

MentOS does something simpler. process_create_init() in kernel/src/process/process.c calls vfs_open("/proc/video", ...) three times — once O_RDONLY, twice O_WRONLY — to produce descriptors 0, 1 and 2:

vfs_file_t *vfs_stdin  = vfs_open("/proc/video", O_RDONLY, 0);
vfs_file_t *vfs_stdout = vfs_open("/proc/video", O_WRONLY, 0);
vfs_file_t *vfs_stderr = vfs_open("/proc/video", O_WRONLY, 0);

Every later process inherits these through fork(). Reading and writing fds 0/1/2 behaves the way you expect, but there is no tty layer, no /dev/console, and no controlling-terminal concept behind them — so do not go looking for one.

Pipes - "Files That Connect Processes"

Location: kernel/src/fs/pipe.c

A pipe is a special "file" that two processes use to communicate:

cat bigfile.txt | grep "error"
cat process              grep process
    │                        │
    ├─ Write to pipe    ─────┤
    │  (data flows)     ─────┤
    ├─ "line1"              │ Read from pipe
    ├─ "error line"         │ ← grep gets this
    ├─ "line3"              │
    └─ EOF                  │ Done reading

Implementation: kernel/src/fs/pipe.c - Creates a ring buffer and exposes it through the ordinary VFS file operations.

Why a pipe lives in the filesystem layer. A pipe is two things at once, and it is worth seeing both views:

  • As a communication abstraction, it is IPC: a unidirectional byte stream between processes, with blocking semantics and EOF when the write end closes. That view belongs on the IPC page.
  • As an implementation, it is a file. pipe() returns two file descriptors, and read() and write() on them go through exactly the same vfs_read/vfs_write dispatch as a file on disk — which is why the code lives in kernel/src/fs/ rather than kernel/src/ipc/.

That duality is not an accident of MentOS; it is the Unix "everything is a file" design.

Named pipes (FIFOs) are not complete. kernel/src/fs/pipe.c contains FIFO-related scaffolding — it sets S_IFIFO in file flags and reports S_IFIFO from stat — but no creation path was found: mkfifo() is not provided, and sys_mknod is not among the handlers registered by syscall_init(). Treat anonymous pipe() as the supported mechanism, and named pipes as unfinished.

Filesystem Operations Summary

Creating a File

fd = open("/home/user/newfile.txt", O_CREAT | O_WRONLY, 0644);

Behind the scenes:

  1. Parse path → navigate to /home/user/ directory inode
  2. Call EXT2 driver: "Create new inode"
  3. Inode allocated → added to inode table on disk
  4. Add directory entry: "newfile.txt" → inode #12345
  5. Write inode and directory entry changes back to disk
  6. Return fd pointing to new inode

Deleting a File

unlink("/home/user/oldfile.txt");

Behind the scenes:

  1. Parse path → find inode #54321
  2. Remove directory entry "oldfile.txt"
  3. Mark inode as free in inode bitmap
  4. If no other hard links exist, free its data blocks
  5. Mark data blocks as free in block bitmap
  6. Write changes back to disk

Directory Operations

mkdir("/home/user/newfolder", 0755);

Behind the scenes:

  1. Create new inode (type=directory)
  2. Create directory entries within it:
    • "." → self inode
    • ".." → parent inode
  3. Add entry in parent directory
  4. Write to disk

Hands-On Exercises

Exercise 1: Explore the File System

Goal: Understand the MentOS filesystem structure.

Steps:

  1. Boot MentOS:

    make qemu
  2. Explore directories:

    /bin/ls /              # Root directory
    /bin/ls /bin           # Binaries
    /bin/ls /tmp           # Temp files
    /bin/ls -l /           # Long listing with metadata
  3. Check file properties:

    /bin/stat /bin/ls      # File metadata
    /bin/stat /            # Directory info
  4. What do you notice?

    • File sizes
    • Timestamps
    • Permissions (755, 644, etc.)
    • Hard link counts

Exercise 2: Create and Manipulate Files

Goal: Write a program that creates, reads, and modifies files.

Program - userspace/bin/file_demo.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main() {
    printf("=== File Operations Demo ===\n\n");
    
    // 1. CREATE and WRITE
    printf("1. Creating file...\n");
    int fd = open("/tmp/test.txt", O_CREAT | O_WRONLY, 0644);
    if (fd < 0) {
        perror("open");
        return 1;
    }
    
    const char *text = "Hello from MentOS!\n";
    write(fd, text, strlen(text));
    close(fd);
    printf("   File created: /tmp/test.txt\n");
    
    // 2. READ
    printf("\n2. Reading file...\n");
    fd = open("/tmp/test.txt", O_RDONLY);
    char buffer[100];
    int n = read(fd, buffer, 100);
    printf("   Read %d bytes: %s", n, buffer);
    close(fd);
    
    // 3. APPEND
    printf("\n3. Appending to file...\n");
    fd = open("/tmp/test.txt", O_APPEND | O_WRONLY);
    const char *more = "This line was appended!\n";
    write(fd, more, strlen(more));
    close(fd);
    
    // 4. READ AGAIN
    printf("\n4. Reading file again...\n");
    fd = open("/tmp/test.txt", O_RDONLY);
    n = read(fd, buffer, 100);
    printf("   Full file content:\n%s", buffer);
    close(fd);
    
    // 5. COPY
    printf("\n5. Copying file...\n");
    fd = open("/tmp/test.txt", O_RDONLY);
    int fd2 = open("/tmp/test_copy.txt", O_CREAT | O_WRONLY, 0644);
    
    while ((n = read(fd, buffer, 50)) > 0) {
        write(fd2, buffer, n);
    }
    close(fd);
    close(fd2);
    printf("   File copied to /tmp/test_copy.txt\n");
    
    return 0;
}

Build and Run:

# Add to userspace/bin/CMakeLists.txt
# In MentOS:
/bin/file_demo
/bin/ls -l /tmp/test*   # Verify files created
/bin/cat /tmp/test_copy.txt

Exercise 3: Work with Directories

Goal: Create directories and list their contents.

Program - userspace/bin/dir_ops.c:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>

int main() {
    printf("=== Directory Operations ===\n\n");
    
    // 1. CREATE DIRECTORY
    printf("1. Creating directory /tmp/mydir...\n");
    mkdir("/tmp/mydir", 0755);
    
    // 2. CREATE FILES IN IT
    printf("2. Creating files in directory...\n");
    char path[100];
    for (int i = 1; i <= 5; i++) {
        snprintf(path, 100, "/tmp/mydir/file%d.txt", i);
        int fd = open(path, O_CREAT | O_WRONLY, 0644);
        write(fd, "content", 7);
        close(fd);
        printf("   Created %s\n", path);
    }
    
    // 3. LIST FILES
    printf("\n3. Directory contents:\n");
    system("/bin/ls -l /tmp/mydir");
    
    // 4. CREATE SUBDIRECTORY
    printf("\n4. Creating subdirectory...\n");
    mkdir("/tmp/mydir/subdir", 0755);
    printf("   Created /tmp/mydir/subdir\n");
    
    // 5. FULL TREE
    printf("\n5. Full tree structure:\n");
    system("/bin/ls -lR /tmp/mydir");
    
    return 0;
}

Test:

/bin/dir_ops
/bin/ls -lR /tmp/mydir

Exercise 4: Understand Inodes and Hard Links

Goal: Learn about inodes and how hard links work.

Program - userspace/bin/hardlink_demo.c:

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

int main() {
    printf("=== Hard Links and Inodes ===\n\n");
    
    // 1. CREATE FILE
    printf("1. Creating original file...\n");
    int fd = open("/tmp/original.txt", O_CREAT | O_WRONLY, 0644);
    write(fd, "Original data", 13);
    close(fd);
    
    // Check inode
    struct stat sb;
    stat("/tmp/original.txt", &sb);
    printf("   Inode: %lu\n", (unsigned long)sb.st_ino);
    printf("   Link count: %lu\n", (unsigned long)sb.st_nlink);
    printf("   Size: %lu bytes\n", (unsigned long)sb.st_size);
    
    // 2. CREATE HARD LINK
    printf("\n2. Creating hard link...\n");
    link("/tmp/original.txt", "/tmp/hardlink.txt");
    
    stat("/tmp/original.txt", &sb);
    printf("   Original link count: %lu\n", (unsigned long)sb.st_nlink);
    
    stat("/tmp/hardlink.txt", &sb);
    printf("   Hard link inode: %lu\n", (unsigned long)sb.st_ino);
    printf("   Hard link count: %lu\n", (unsigned long)sb.st_nlink);
    
    // 3. MODIFY THROUGH LINK
    printf("\n3. Modifying through hard link...\n");
    fd = open("/tmp/hardlink.txt", O_APPEND | O_WRONLY);
    write(fd, " - Modified!", 11);
    close(fd);
    
    // 4. READ ORIGINAL
    printf("\n4. Reading original file:\n");
    char buf[100];
    fd = open("/tmp/original.txt", O_RDONLY);
    int n = read(fd, buf, 100);
    printf("   %.*s\n", n, buf);
    close(fd);
    
    // 5. DELETE LINK
    printf("\n5. Deleting hard link...\n");
    unlink("/tmp/hardlink.txt");
    
    stat("/tmp/original.txt", &sb);
    printf("   Final link count: %lu\n", (unsigned long)sb.st_nlink);
    printf("   (Note: inode still exists until all links deleted)\n");
    
    return 0;
}

Test and Observe:

/bin/hardlink_demo
# Note: Both files share same inode!
# Modifying one affects the other
# File only deleted when ALL hard links removed

Exercise 5: Low-Level File Access

Goal: Use system calls to interact with the filesystem directly.

Program - userspace/bin/rawio.c:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main() {
    printf("=== Low-Level File I/O ===\n\n");
    
    // 1. OPEN
    printf("1. Opening file with O_RDWR...\n");
    int fd = open("/tmp/rawtest.txt", O_CREAT | O_RDWR, 0644);
    printf("   File descriptor: %d\n", fd);
    
    // 2. WRITE at specific positions
    printf("\n2. Writing at different positions...\n");
    
    lseek(fd, 0, SEEK_SET);
    write(fd, "START", 5);
    printf("   Wrote 'START' at offset 0\n");
    
    lseek(fd, 10, SEEK_SET);
    write(fd, "MIDDLE", 6);
    printf("   Wrote 'MIDDLE' at offset 10\n");
    
    lseek(fd, 20, SEEK_SET);
    write(fd, "END", 3);
    printf("   Wrote 'END' at offset 20\n");
    
    // 3. SEEK and READ
    printf("\n3. Seeking and reading...\n");
    lseek(fd, 0, SEEK_SET);
    
    char buf[100];
    int n = read(fd, buf, 100);
    
    printf("   File content (%d bytes):\n", n);
    for (int i = 0; i < n; i++) {
        if (buf[i] == '\0') {
            printf("[NULL]");
        } else {
            printf("%c", buf[i]);
        }
    }
    printf("\n");
    
    // 4. TRUNCATE
    printf("\n4. Truncating file to 15 bytes...\n");
    ftruncate(fd, 15);
    
    lseek(fd, 0, SEEK_SET);
    n = read(fd, buf, 100);
    printf("   After truncate: %d bytes\n", n);
    
    close(fd);
    return 0;
}

Explore:

  • lseek() for seeking to positions
  • read()/write() at different offsets
  • ftruncate() to change file size
  • File descriptors and offsets

Exercise 6: Traverse EXT2 Filesystem

Advanced Goal: Understand EXT2 structure on disk.

Steps:

  1. Examine EXT2 superblock and group descriptors
  2. Look at kernel/src/fs/ext2.c implementation
  3. Write program to:
    • Read superblock
    • Enumerate block groups
    • Count free inodes and blocks
    • Navigate inode bitmap

Hints:

  • The primary superblock always lives at byte offset 1024 from the start of the volume (see the comment on ext2_superblock_t in kernel/inc/fs/ext2.h). With the 1 KiB blocks of classic EXT2 that happens to be "block 1"; with MentOS's 4 KiB blocks it is inside block 0. Use the byte offset, not the block index.
  • Group descriptors follow the superblock
  • Each group has inode bitmap and block bitmap
  • Use direct disk I/O through /dev/hda (careful!)

Key Files to Explore

  • kernel/src/fs/vfs.c - VFS core operations
  • kernel/src/fs/ext2.c - EXT2 filesystem implementation
  • kernel/src/fs/namei.c - Path name resolution
  • kernel/src/fs/procfs.c - Virtual procfs
  • kernel/src/fs/pipe.c - Pipes for IPC
  • kernel/inc/fs/vfs.h - VFS data structures
  • kernel/inc/fs/ext2.h - EXT2 on-disk format
  • lib/inc/fcntl.h - File control flags
  • lib/inc/sys/stat.h - File metadata structures

Further Reading


Key takeaway: Files are just organized data on disk. The VFS abstracts the details, letting kernels support many filesystems. EXT2 shows how one filesystem organizes inodes and blocks to store everything efficiently!

Clone this wiki locally