Skip to content

Rohan13253/CustomisedVirtualFileSystem

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 

Repository files navigation

🖥️ Customised Virtual File System (CVFS)

C++ Linux

A from-scratch implementation of a Unix-style Virtual File System in C/C++, running entirely in RAM with a custom interactive shell — built to understand exactly how the Linux kernel manages files at the data structure level.

Language Platform Status


📋 Table of Contents


What Is This?

When you call open() or write() in a Linux program, the kernel runs through a chain of data structures — inodes, file tables, file descriptor tables — before a single byte touches the disk. This project reimplements that entire chain from scratch in user space, with RAM acting as the virtual disk.

Why is this interesting?

  • There is no OS support. No fopen, no fwrite. Everything — allocation, permission checks, offset tracking — is implemented manually.
  • The data structure design mirrors the actual Unix File Subsystem (inode → file table → UFDT).
  • It ships with a working interactive shell that accepts real commands (creat, read, write, stat, unlink, ls).

Memory Layout & Disk Structure

A real disk is divided into zones. This project mirrors that layout in RAM:

┌─────────────────────────────────────────────────────────────────────┐
│                        VIRTUAL DISK (RAM)                           │
├──────────────┬─────────────────┬──────────────────┬────────────────┤
│  Boot Block  │   Super Block   │   Inode Table    │  Data Blocks   │
│  (1 KB)      │                 │   (DILB)         │                │
│              │  TotalInodes: 5 │  Linked List of  │  char* Buffer  │
│  "Boot OS    │  FreeInodes:  5 │  Inode structs   │  per file      │
│   process    │                 │  (heap-allocated)│  (100 bytes)   │
│   done"      │                 │                  │                │
└──────────────┴─────────────────┴──────────────────┴────────────────┘

This maps directly to the hand-drawn architecture diagram: Boot Block → Super Block → DILB (Data Inode List Block) → Data Block.

Each data block is a heap-allocated char* buffer of MAXFILESIZE bytes, assigned to an inode when a file is created via malloc() and reclaimed when deleted via free().


System Architecture

Layer Overview

┌─────────────────────────────────────────────────────────────────────┐
│                          USER INPUT                                 │
│                   CVFS > creat Notes.txt 3                          │
└──────────────────────────┬──────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────────────┐
│                    CUSTOM SHELL  (main)                             │
│  • Reads input via fgets()                                          │
│  • Tokenizes via sscanf()                                           │
│  • Routes to system call by arg count (iCount = 1 / 2 / 3)         │
└───────┬─────────────┬────────────────┬──────────────┬──────────────┘
        │             │                │              │
        ▼             ▼                ▼              ▼
  CreateFile     UnlinkFile       write_file      read_file
  stat_file      ls_file          ManPage         DisplayHelp
        │             │                │              │
        └─────────────┴────────────────┴──────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────────────┐
│                  UAREA  (per-process context)                       │
│                                                                     │
│   UFDT[0]  UFDT[1]  UFDT[2]  ...  UFDT[MAXOPENEDFILES - 1]        │
│     │                                                               │
│     └──► FILETABLE ──► INODE ──► char* Buffer (Data Block)         │
└─────────────────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────────────┐
│              DILB — Inode Linked List                               │
│                                                                     │
│  [Inode 1]──►[Inode 2]──►[Inode 3]──►[Inode 4]──►[Inode 5]──►NULL │
│  (free)       (in use)    (free)       (in use)    (free)           │
└─────────────────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────────────┐
│                       SUPER BLOCK                                   │
│               TotalInodes = 5   FreeInodes = 2                      │
└─────────────────────────────────────────────────────────────────────┘

The FD → Data Chain

This is the core pointer chain that every file operation traverses. This mirrors the hand-drawn diagram (UAREA → File Table → Inode Table → Data Block):

Integer FD (e.g., 0)
        │
        ▼
uareaobj.UFDT[0]            ← pointer to FileTable
        │
        ├─ ReadOffset   = 0
        ├─ WriteOffset  = 11
        ├─ Count        = 1
        ├─ Mode         = READ+WRITE (3)
        │
        └─ ptrinode ─────────────────────────► INODE struct
                                                   │
                                                   ├─ FileName:       "Notes.txt"
                                                   ├─ InodeNumber:    1
                                                   ├─ FileSize:       100
                                                   ├─ ActualFileSize: 11
                                                   ├─ FileType:       REGULARFILE (1)
                                                   ├─ Permission:     3  (R+W)
                                                   ├─ LinkCount:      1
                                                   ├─ ReferenceCount: 1
                                                   │
                                                   └─ Buffer ──────► [H][e][l][l][o][ ][W][o][r][l][d]
                                                                       Data Block on heap (char*)

Data Structures (Deep Dive)

struct Inode — Core metadata node

typedef struct Inode {
    char  FileName[50];     // Name of the file
    int   InodeNumber;      // Unique ID (1 to MAXINODE)
    int   FileSize;         // Allocated size = MAXFILESIZE (100 bytes)
    int   ActualFileSize;   // Bytes actually written so far
    int   FileType;         // REGULARFILE(1) or SPECIALFILE(2); 0 = free slot
    int   ReferenceCount;   // Number of processes referencing this inode
    int   LinkCount;        // Hard link count (currently always 1)
    int   Permission;       // READ(1), WRITE(2), READ+WRITE(3)
    char *Buffer;           // Pointer to heap-allocated data block
    struct Inode *next;     // Next inode in DILB linked list
} INODE, *PINODE, **PPINODE;

FileType == 0 is the free-slot marker. CreateFile scans the DILB for FileType == 0 to find an available inode. UnlinkFile resets FileType back to 0 to return it to the pool.


struct FileTable — Open file context

typedef struct FileTable {
    int     ReadOffset;   // Current read cursor (bytes from start of buffer)
    int     WriteOffset;  // Current write cursor (bytes from start of buffer)
    int     Count;        // Reference count for this file table entry
    int     Mode;         // Permission mode file was opened/created with
    PINODE  ptrinode;     // Pointer to the associated Inode
} FILETABLE, *PFILETABLE;

Each open file gets its own FileTable. Two processes opening the same file would get two separate FileTable entries pointing to the same Inode — exactly how Linux handles shared file access.


struct SuperBlock — File system state tracker

struct SuperBlock {
    int TotalInodes;   // = MAXINODE — constant, never changes after init
    int FreeInodes;    // Decrements on creat, increments on unlink
};

struct UAREA — Process context

struct UAREA {
    char       ProcessName[50];       // = "Myexe"
    PFILETABLE UFDT[MAXOPENEDFILES];  // Array of FileTable pointers (size 20)
};

UFDT is the User File Descriptor Table. The array index is the file descriptor. UFDT[0] = FD 0, UFDT[3] = FD 3. A NULL entry means that FD slot is unused and available.


struct BootBlock — Simulated boot sector

struct BootBlock {
    char Information[100];  // "Boot process of Operating System done"
};

How Each System Call Works

CreateFile(name, permission) → returns FD

START
  │
  ├─► name != NULL ?                  else → ERR_INVALID_PARAMETER
  ├─► permission in [1, 3] ?          else → ERR_INVALID_PARAMETER
  ├─► SuperBlock.FreeInodes > 0 ?     else → ERR_NO_INODES
  ├─► IsFileExists(name) == false ?   else → ERR_FILE_ALREADY_EXIST
  │
  ├─► Scan DILB linked list for first Inode with FileType == 0
  ├─► Scan UFDT array for first NULL slot  →  that index becomes the FD
  │
  ├─► malloc(sizeof(FILETABLE))   → allocate FileTable on heap
  ├─► malloc(MAXFILESIZE)         → allocate data buffer on heap
  │
  ├─► Populate FileTable  (ReadOffset=0, WriteOffset=0, Mode=permission)
  ├─► Populate Inode      (name, FileSize=MAXFILESIZE, FileType=REGULAR)
  ├─► Link:  UFDT[fd]->ptrinode = &inode
  │
  ├─► SuperBlock.FreeInodes--
  │
  └─► return fd

write_file(fd, data, size) → returns bytes written

START
  │
  ├─► fd in [0, MAXOPENEDFILES] ?          else → ERR_INVALID_PARAMETER
  ├─► UFDT[fd] != NULL ?                   else → ERR_FILE_NOT_EXIST
  ├─► Permission >= WRITE (2) ?            else → ERR_PERMISSION_DENIED
  ├─► (MAXFILESIZE - WriteOffset) >= size? else → ERR_INSUFFICIENT_SPACE
  │
  ├─► strncpy(Buffer + WriteOffset, data, size)
  ├─► WriteOffset     += size
  ├─► ActualFileSize  += size
  │
  └─► return size

read_file(fd, buffer, size) → returns bytes read

START
  │
  ├─► fd in [0, MAXOPENEDFILES] ?          else → ERR_INVALID_PARAMETER
  ├─► UFDT[fd] != NULL ?                   else → ERR_FILE_NOT_EXIST
  ├─► Permission >= READ (1) ?             else → ERR_PERMISSION_DENIED
  ├─► (MAXFILESIZE - ReadOffset) >= size ? else → ERR_INSUFFICIENT_DATA
  │
  ├─► strncpy(buffer, Buffer + ReadOffset, size)
  ├─► ReadOffset += size
  │
  └─► return size

UnlinkFile(name) → returns 0 on success

START
  │
  ├─► name != NULL ?              else → ERR_INVALID_PARAMETER
  ├─► IsFileExists(name) == true? else → ERR_FILE_NOT_EXIST
  │
  ├─► Scan UFDT for entry matching FileName
  │
  ├─► free(ptrinode->Buffer)      ← release data block
  ├─► Reset all Inode fields = 0  ← FileType=0 marks it free again
  ├─► free(UFDT[i])               ← release FileTable
  ├─► UFDT[i] = NULL              ← FD slot is now available
  ├─► SuperBlock.FreeInodes++
  │
  └─► return EXECUTE_SUCCESS (0)

Initialization Flow

Everything that happens before the shell prompt appears:

main()
  │
  └─► StartAuxilaryDataInitialisation()
            │
            ├─► BootBlock initialized
            │   └─► prints "Boot process of Operating System done"
            │
            ├─► InitialiseSuperblock()
            │   ├─► TotalInodes = MAXINODE (5)
            │   └─► FreeInodes  = MAXINODE (5)
            │
            ├─► CreateDILB()
            │   └─► Loop i = 1 to MAXINODE:
            │         Allocate new INODE on heap
            │         Set all fields to 0 / NULL
            │         Link into list:
            │         head → [1] → [2] → [3] → [4] → [5] → NULL
            │
            └─► InitialiseUAREA()
                ├─► ProcessName = "Myexe"
                └─► UFDT[0 .. 19] = NULL

After this, the while(1) shell loop begins and CVFS is ready.


Command Reference

Command Syntax Description
help help Display all available commands
man man <command> Show manual page for a specific command
creat creat <filename> <perm> Create a new regular file. Returns an integer FD.
write write <fd> Prompt for data and write it to the file
read read <fd> <size> Read size bytes from the file at current offset
stat stat <filename> Display metadata (size, permissions, type, link count)
ls ls List all files currently in the virtual file system
unlink unlink <filename> Delete a file and free its inode and data buffer
clear clear Clear the terminal screen
exit exit Shut down CVFS

Permission Values for creat

Value Meaning
1 Read only
2 Write only
3 Read + Write

Error Code Reference

Code Macro When It Fires
-1 ERR_INVALID_PARAMETER NULL filename, out-of-range FD, invalid permission value
-2 ERR_NO_INODES FreeInodes == 0 when creat is called
-3 ERR_FILE_ALREADY_EXIST creat with a name already present in the DILB
-4 ERR_FILE_NOT_EXIST stat, unlink, or read/write on unknown file/FD
-5 ERR_PERMISSION_DENIED Writing to read-only, or reading from write-only file
-6 ERR_INSUFFICIENT_SPACE WriteOffset + size > MAXFILESIZE
-7 ERR_INSUFFICIENT_DATA ReadOffset + size > MAXFILESIZE

Configuration Reference

All system limits are macros at the top of CVFS.cpp. Change them before compiling to resize the system:

#define MAXFILESIZE      100   // Max bytes per file (data block size)
#define MAXOPENEDFILES   20    // UFDT size = max simultaneously open files
#define MAXINODE         5     // Max total files in the system

#define READ             1     // Permission bit: read
#define WRITE            2     // Permission bit: write
#define EXECUTE          4     // Permission bit: execute (defined, not yet enforced)

#define REGULARFILE      1     // FileType value for a regular file
#define SPECIALFILE      2     // FileType value for a special/device file

#define START            0     // lseek whence: from start  (defined, not yet used)
#define CURRENT          1     // lseek whence: from current position
#define END              2     // lseek whence: from end

Getting Started

Prerequisites

A C++ compiler (g++). Verify with:

g++ --version

Linux / macOS: GCC or Clang are typically pre-installed or available via your package manager.

Windows: Install WSL and follow the Linux steps, or use MinGW-w64.


Clone

git clone https://github.com/Rohan13253/CustomisedVirtualFileSystem.git
cd CustomisedVirtualFileSystem

Compile

g++ CVFS.cpp -o Myexe

No external libraries. No build system. No dependencies.

Run

./Myexe

Full Session Walkthrough

Boot process of Operating System done
CVFS : Superblock initialised succesfully
CVFS : DILB created succesfully
CVFS : UAREA initialised succesfully
---------------------------------------------------------
----------------CVFS Started Succesfully ----------------
---------------------------------------------------------

 CVFS > help
 (displays command list)

 CVFS > creat Notes.txt 3
Current Inodes remaining : 5
File is succesfully created with FD : 0

 CVFS > write 0
Please enter the data that you want to write into the file :
Hello World
11 bytes gets succesfully written into the file

 CVFS > read 0 5
Read operation is successfull
Data from file is : Hello

 CVFS > stat Notes.txt
------------ Statistical Information of file -----------
File name : Notes.txt
File size on Disk : 100
Actual File size : 11
Link count : 1
File permission : Read + Write
File type : Regular file
--------------------------------------------------------

 CVFS > creat Config.txt 1
Current Inodes remaining : 4
File is succesfully created with FD : 1

 CVFS > write 1
Please enter the data that you want to write into the file :
test data
Error : Unable to write as there is no write permission

 CVFS > ls
Notes.txt
Config.txt

 CVFS > unlink Notes.txt
Unlink Opertaion is succesfully performed

 CVFS > ls
Config.txt

 CVFS > exit
Thank you for using CVFS
Deallocating all resources...

Known Limitations

Documented honestly for contributors and interviewers:

# Issue Severity Location
1 No persistence. All data lives in RAM and is lost when the process exits. High Entire system
2 Read permission check is incorrect. read_file checks Permission < READ (i.e., < 1), but a write-only file has Permission = 2, which passes the check. Write-only files can be read. High read_file()
3 malloc size bug in read command. malloc(sizeof(atoi(Command[2]))) allocates sizeof(int) = 4 bytes instead of the requested read size. Reading more than 4 bytes causes a heap buffer overflow. High main()
4 write success message always prints from FD 0. The buffer display after a successful write hardcodes UFDT[0] instead of using the actual FD. Medium main() write handler
5 No open system call. creat simultaneously allocates the inode and returns an FD. There is no way to re-open an existing file once its FD is forgotten. Medium Design
6 No close system call. Files cannot be closed without deleting them. All FD slots stay occupied until unlink. Medium Design
7 lseek macros defined but function not implemented. START, CURRENT, END are defined but never used. Low CVFS.cpp
8 fflush(stdin) is undefined behavior on input streams in the C standard. Low main()

Future Enhancements

Priority Enhancement Notes
🔴 High Fix malloc size bug in read Change to malloc(atoi(Command[2]) + 1)
🔴 High Fix read permission check Use !(Permission & READ) bitwise check instead of < READ
🔴 High Sync README with actual macro values MAXINODE and MAXFILESIZE are wrong in current README
🔴 High Persistence Serialize DILB + data buffers to a .cvfs file on exit, reload on startup
🟠 Medium Implement open and close Files should be creatable once and openable many times independently
🟠 Medium Implement lseek Macros already defined — add the function and shell command
🟡 Low Directory support Add DirectoryInode type; implement mkdir, cd, pwd
🟡 Low Multi-user simulation Multiple UAREA instances with simulated user switching
🟡 Low Replace fflush(stdin) Use while(getchar() != '\n') for portable input flushing

CVFS

Author

Rohan Murlidhar Pawar


This project was built to understand the Unix File Subsystem at the kernel data structure level. The design intentionally mirrors the real Linux implementation — UAREA, UFDT, File Table, and Inode chain — rather than using simplified abstractions.

About

A from-scratch implementation of a Unix-style Virtual File System in C/C++, running entirely in RAM. Built to understand Linux kernel data structures (Inodes, File Tables, UFDT) without relying on OS abstractions.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages