Building a Customized Virtual File System in C++


Managing files efficiently is at the heart of any operating system. A Virtual File System (VFS) provides an abstraction layer for file operations, making interactions simple and intuitive for developers. This blog walks through building a Customized Virtual File System in C++, with features like file creation, reading, writing, and more.
Whether you're a C++ enthusiast or keen on learning systems programming, this project will give you hands-on experience with data structures, memory management, and file operations.
🔎 1. Introduction: What are we building and why?
In this project, we build a Customized Virtual File System (VFS) to simulate file management. Here’s why this project is exciting:
It mimics how real-world operating systems handle file operations (e.g., Linux’s ext4 or NTFS).
It deepens your understanding of data structures like linked lists, inodes, and file tables.
It’s a hands-on way to explore systems programming with C++.
The VFS supports:
File operations like create, read, write, delete, and truncate.
File permissions (read, write, or both).
In-memory simulation of files with metadata management.
By the end, you’ll have a functioning VFS with a command-line interface (CLI) to interact with the file system.
📚 2. Concept Recap: Key File System Components
Before diving into the implementation, let’s recap some key concepts:
Superblock:
Tracks the total and free inodes in the file system. It acts as a manager for metadata about the file system.
Inode:
Represents an individual file. It stores metadata like file name, size, type, permissions, and content buffer.
File Table:
Maintains information about open files during runtime, including read/write offsets and access modes.
UFDT (User File Descriptor Table):
Maps file descriptors to file tables, enabling user processes to interact with files.
Diagrammatically, here’s how the VFS is structured:
Superblock --> Inodes (Linked List) --> File Table --> UFDT --> User Commands
💻 3. Implementation Walkthrough
Step 1: Setting Up the Superblock and Inodes
The superblock and inodes are initialized during system startup. Each inode represents a file and is stored in a dynamically allocated linked list.
void InitialiseSuperBlock() {
for (int i = 0; i < MAXINODE; i++) {
UFDTArr[i].ptrfiletable = NULL;
}
SUPERBLOCKobj.TotalInodes = MAXINODE;
SUPERBLOCKobj.FreeInode = MAXINODE;
}
void CreateDILB() {
PINODE temp = NULL;
for (int i = 1; i <= MAXINODE; i++) {
PINODE newn = (PINODE)malloc(sizeof(INODE));
newn->LinkCount = newn->ReferenceCount = 0;
newn->FileType = newn->FileSize = 0;
newn->Buffer = NULL;
newn->next = NULL;
newn->InodeNumber = i;
if (temp == NULL) {
head = newn;
temp = head;
} else {
temp->next = newn;
temp = temp->next;
}
}
}
Step 2: Creating and Deleting Files
These core functions handle file creation and deletion, ensuring proper metadata management.
int CreateFile(char *name, int permission) {
if (name == NULL || permission == 0 || permission > 3) return -1;
if (SUPERBLOCKobj.FreeInode == 0) return -2;
(SUPERBLOCKobj.FreeInode)--;
PINODE temp = head;
while (temp != NULL) {
if (temp->FileType == 0) break;
temp = temp->next;
}
int i = 0;
while (i < MAXINODE) {
if (UFDTArr[i].ptrfiletable == NULL) break;
i++;
}
UFDTArr[i].ptrfiletable = (PFILETABLE)malloc(sizeof(FILETABLE));
UFDTArr[i].ptrfiletable->count = 1;
UFDTArr[i].ptrfiletable->mode = permission;
UFDTArr[i].ptrfiletable->readoffset = 0;
UFDTArr[i].ptrfiletable->writeoffset = 0;
UFDTArr[i].ptrfiletable->ptrinode = temp;
strcpy(temp->FileName, name);
temp->FileType = REGULAR;
temp->ReferenceCount = 1;
temp->LinkCount = 1;
temp->FileSize = MAXFILESIZE;
temp->FileActualSize = 0;
temp->permission = permission;
temp->Buffer = (char *)malloc(MAXFILESIZE);
memset(temp->Buffer, 0, MAXFILESIZE);
return i;
}
int rm_File(char *name) {
int fd = GetFDFromName(name);
if (fd == -1) return -1;
(UFDTArr[fd].ptrfiletable->ptrinode->LinkCount)--;
if (UFDTArr[fd].ptrfiletable->ptrinode->LinkCount == 0) {
UFDTArr[fd].ptrfiletable->ptrinode->FileType = 0;
free(UFDTArr[fd].ptrfiletable->ptrinode->Buffer);
free(UFDTArr[fd].ptrfiletable);
}
UFDTArr[fd].ptrfiletable = NULL;
(SUPERBLOCKobj.FreeInode)++;
}
Step 3: Reading and Writing Files
File operations like reading and writing are controlled through offsets and buffers.
int ReadFile(int fd, char *arr, int isize) {
if (UFDTArr[fd].ptrfiletable == NULL) return -1;
int read_size = UFDTArr[fd].ptrfiletable->ptrinode->FileActualSize -
UFDTArr[fd].ptrfiletable->readoffset;
if (read_size < isize) {
strncpy(arr, UFDTArr[fd].ptrfiletable->ptrinode->Buffer +
UFDTArr[fd].ptrfiletable->readoffset,
read_size);
UFDTArr[fd].ptrfiletable->readoffset += read_size;
} else {
strncpy(arr, UFDTArr[fd].ptrfiletable->ptrinode->Buffer +
UFDTArr[fd].ptrfiletable->readoffset,
isize);
UFDTArr[fd].ptrfiletable->readoffset += isize;
}
return isize;
}
Step 4: Command-Line Interface
The CLI lets users interact with the VFS using commands like create
, read
, write
, and delete
.
int main() {
char command[4][80], str[80];
int count = 0;
InitialiseSuperBlock();
CreateDILB();
while (1) {
printf("\nCustomised VFS : > ");
fgets(str, 80, stdin);
count = sscanf(str, "%s %s %s %s", command[0], command[1], command[2],
command[3]);
if (strcmp(command[0], "create") == 0) {
CreateFile(command[1], atoi(command[2]));
} else if (strcmp(command[0], "read") == 0) {
// Handle read command
} else if (strcmp(command[0], "write") == 0) {
// Handle write command
} else if (strcmp(command[0], "exit") == 0) {
printf("Terminating the Customised Virtual File System\n");
break;
}
}
return 0;
}
🧠 4. Challenges & Learnings
Challenges:
Memory Management: Allocating and freeing memory for buffers and inodes required careful handling to avoid leaks.
Command Parsing: Implementing a robust parser for CLI commands was tricky but a great learning experience.
Learnings:
The importance of efficient data structures for real-world systems.
How operating systems manage file metadata through inodes and file tables.
🏃♂️ 5. How to Run It
Prerequisites:
- A C++ compiler (e.g., GCC or Clang).
Steps:
Clone the GitHub repository:
git clone https://github.com/Mayurdpatil67/virtual-file-system.git cd virtual-file-system
Compile the code:
g++ CVFS.cpp -o CVFS
Run the VFS:
./CVFS
Example Commands:
create file1 3
write file1
read file1 10
ls
rm file1
💻 6. GitHub Repository
The complete source code for this project is available on GitHub. Feel free to clone, fork, or contribute to the repository:
- GitHub Link: Customized Virtual File System
🌟 7. Conclusion
This project demonstrates the power and flexibility of a Virtual File System. You’ve learned how to:
Simulate file management with inodes, superblocks, and file tables.
Implement core file operations in C++.
Build a CLI for user interaction.
What’s Next?
Add directory support to organize files hierarchically.
Implement file compression to optimize storage.
Extend the CLI with advanced commands.
Happy coding! 🚀
Subscribe to my newsletter
Read articles from Mayur Patil directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Mayur Patil
Mayur Patil
Skilled in Java & Spring Boot , Backedn Enthusiast