How Linux’s Virtual File System Turns Everything Into a File
The article explains Linux’s Virtual File System (VFS) as an interface layer that treats all objects as files, illustrates the concept with Java‑style interface examples, then details the underlying C structures—file, file_operations, dentry, inode—and shows how the kernel links them to enable diverse file systems.
Introduction
In Unix philosophy, "everything is a file" means that any object can be accessed through the standard file‑handling interfaces. Linux implements this idea with a middle layer called the Virtual File System (VFS), which defines a uniform set of operations that higher‑level code can use regardless of the underlying file‑system implementation.
Java‑style Interface Example
To make the VFS concept easier to grasp, a Java‑style interface is used to model the VFS API.
public interface VFSFile {
int open(String file, int mode);
int read(int fd, byte[] buffer, int size);
int write(int fd, byte[] buffer, int size);
// ... other operations
}
public class Ext3File implements VFSFile {
@Override
public int open(String file, int mode) {
// implementation
}
@Override
public int read(int fd, byte[] buffer, int size) {
// implementation
}
@Override
public int write(int fd, byte[] buffer, int size) {
// implementation
}
// ...
}
public class Main {
public static void main(String[] args) {
VFSFile file = new Ext3File();
int fd = file.open("/tmp/file.txt", 0);
// use fd with read/write
}
}Any object that implements VFSFile can be manipulated through open(), read(), and write() without the caller needing to know the concrete type.
Kernel Implementation in C
Linux is written in C, which lacks a native interface construct. Instead, the kernel simulates interfaces using structures of function pointers.
1. struct file
struct file {
// ... other members ...
const struct file_operations *f_op; // pointer to operations
// ...
};2. struct file_operations
struct file_operations {
loff_t (*llseek)(struct file *, loff_t, int);
ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
// ... other callbacks ...
int (*open)(struct inode *, struct file *);
// ...
};The kernel fills this structure with pointers to the concrete functions provided by a specific file‑system implementation.
3. Opening a file – __dentry_open()
static struct file *
__dentry_open(struct dentry *dentry,
struct vfsmount *mnt,
struct file *f,
int (*open)(struct inode *, struct file *),
const struct cred *cred)
{
// ...
inode = dentry->d_inode;
// assign the file_operations from the inode to the file
f->f_op = fops_get(inode->i_fop);
// ...
return f;
}When a file is opened, the kernel retrieves the inode, copies its i_fop pointer into the newly created file object's f_op, and thus equips the file with the appropriate operations.
4. Example: Minix file‑system operations
const struct file_operations minix_file_operations = {
.llseek = generic_file_llseek,
.read = do_sync_read,
.aio_read = generic_file_aio_read,
.write = do_sync_write,
.aio_write = generic_file_aio_write,
.mmap = generic_file_mmap,
.fsync = generic_file_fsync,
.splice_read = generic_file_splice_read,
};If the current file‑system is Minix, calls to read() ultimately invoke do_sync_read(), and so on.
5. Directory entry – struct dentry
struct dentry {
struct dentry *d_parent; // parent directory
struct qstr d_name; // name of the entry
struct inode *d_inode; // pointer to inode
const struct dentry_operations *d_op; // operations for dentry
// ...
};Each component of a pathname creates a dentry object, linked via d_parent to form a tree that the kernel can traverse.
6. Path – struct path
struct path {
struct dentry *dentry; // points to the dentry of the file
// ... other members ...
};7. Inode – struct inode
struct inode {
uid_t i_uid; // owner user ID
gid_t i_gid; // owner group ID
struct timespec i_atime; // last access time
struct timespec i_mtime; // last modification time
struct timespec i_ctime; // creation time
unsigned short i_bytes; // file size
const struct file_operations *i_fop; // operations for files of this inode
// ... other attributes ...
};The inode represents the actual file on disk and stores metadata such as ownership, timestamps, and a pointer to the file‑operations table that will be used when a file is opened.
Conclusion
The Virtual File System provides a uniform interface that abstracts away the details of individual file‑system implementations. By defining a set of operation callbacks (the file_operations structure) and linking them through inode and file objects, Linux can support a wide variety of file systems while allowing user‑space programs to interact with them using the familiar open(), read(), and write() system calls.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
