AlgoMaster Logo

The Virtual File System Layer

27 min readUpdated August 7, 2026
Listen to this chapter
Unlock Audio

A process reads two paths:

The first is commonly backed by a persistent local file system. The second is generated by the kernel when it is read. Yet the application can use the same interface for both:

The application does not call ext4_read(), procfs_read(), or a device-specific function. Linux routes the generic operation through its Virtual File System, commonly abbreviated VFS.

VFS is the kernel layer that presents common file and pathname operations while dispatching them to different file-system implementations.

VFS is not a file system stored on disk. It is an abstraction and object model inside the kernel. It lets local, memory-backed, remote, and synthetic file systems participate in one namespace and serve the same system-call interface.

The Problem VFS Solves

A general-purpose operating system supports file systems with very different implementations.

An ext4 or XFS file system maps file data and metadata onto a local block device. A tmpfs file system keeps its ordinary contents in memory. NFS reaches a remote server over a network. procfs generates views of process and kernel state. Each understands “file” differently below the interface.

Without a common layer, application code would need to select an implementation:

That would expose file-system selection to every program, duplicate pathname logic, and make descriptors difficult to use uniformly.

VFS reverses the relationship:

The application calls open(), read(), write(), stat(), or rename(). The VFS handles the common kernel-facing contract, then dispatches to the implementation for the resolved object.

The file system supplies operations that satisfy VFS interfaces. Applications remain written against stable system calls.

This arrangement is sometimes called a filesystem switch because the common layer chooses the correct implementation after path or descriptor lookup.

VFS Inside the Kernel

VFS sits below the system-call interface and above individual file-system implementations.

The VFS sits in the middle of the kernel stack. Everything above it is identical no matter which file system is involved, and everything below it varies.

The C library can provide convenience wrappers such as open() and fopen(), but it is not VFS. The wrapper enters the kernel; VFS participates after that transition.

VFS is also not FUSE. FUSE is a mechanism that lets a user-space process implement file-system behavior with help from a kernel component. Requests still enter the kernel's VFS layer before being forwarded through FUSE to the user-space implementation.

The word virtual means the interface is independent of one physical representation. It does not mean the files are imaginary or necessarily held in virtual memory.

The Core VFS Objects

Linux VFS represents the file-system view with a small set of related object types. The most important are:

  • The superblock object, representing one file-system instance.
  • The inode object, representing one file-system object and its metadata.
  • The dentry object, representing a name-to-object association for one path component.
  • The file object, representing one open file description.
  • The mount object, representing where a file-system tree is attached in a namespace.

These are kernel concepts. Their exact C structures evolve, and file-system implementations can store additional private state alongside them.

The arrows show relationships, not one fixed memory layout. A file system can create or recover these VFS objects from disk metadata, remote responses, or computed kernel state.

VFS Superblocks as File-System Instances

A VFS superblock represents a particular file-system instance known to the kernel.

It carries information such as:

  • The file-system type
  • File-system-wide properties and limits
  • The root object for that instance
  • Operations for managing file-system-level state
  • A link to implementation-specific information

For a local disk file system, the VFS superblock is associated with persistent file-system metadata read from storage. The on-disk format may also contain a structure called a superblock. The VFS object is the kernel's runtime representation, not simply a raw copy of those bytes.

For procfs, there is no disk superblock containing process entries. The kernel still creates a VFS superblock so that procfs can participate in mounting, path resolution, and common metadata operations.

One file-system type can have several instances:

Each file-system instance has its own object identities and state. A separate mount object can expose an instance in the namespace, including exposing the same tree at more than one location.

The VFS Inode as a Common Object View

A VFS inode represents a file-system object within one superblock.

It exposes common metadata such as:

  • File type and mode
  • Owner and group
  • Logical size
  • Timestamps
  • Link count
  • Inode number within the file-system instance

For an inode-based disk file system, the implementation populates the VFS inode from persistent metadata and associates private mapping information with it.

Not every file system has a traditional on-disk inode. NFS obtains attributes and file handles from a server. procfs constructs objects from kernel state. Both still provide VFS inode objects because generic code needs a common representation for operations such as stat() and permission checks.

This distinction is important:

They correspond closely for some file systems but are not universally the same structure.

An inode number remains meaningful only together with its file-system instance. In user space, st_dev identifies the containing instance and st_ino identifies the object within it.

Dentries as Name Components

A dentry, short for directory entry object, represents a relationship between one component name and a VFS inode within a directory context.

For:

VFS works with component relationships resembling:

The final dentry can refer to the VFS inode for config.json.

A VFS dentry is not necessarily identical to a directory record stored on disk. It is a kernel object used during pathname operations. The underlying file system decides how directory contents are represented and supplies lookup behavior that lets VFS establish the relationship.

More than one dentry can refer to the same inode:

This is the VFS representation of peer hard-link names.

A dentry can also represent a lookup that did not find an inode. Such negative lookup state can avoid repeating work, but its caching and invalidation behavior are separate concerns. The essential role here is to give VFS a component-level namespace object.

VFS File Objects as Open Descriptions

Linux uses struct file as the VFS representation of an open file description. Despite its name, it is not the persistent regular file itself.

The VFS file object contains per-opening state such as:

  • Current file offset, when meaningful
  • Access mode and file status flags
  • A reference to the opened path and underlying inode
  • The operations used for this open object
  • Runtime ownership and reference information

A process file descriptor points through its descriptor table to this file object:

Two separate open() calls normally create two VFS file objects and independent offsets, even when both reach the same inode. dup() creates another descriptor reference to the same file object, so the offset remains shared.

Separate opens reach the same inode through separate file objects, so the offsets are independent. dup stops at the file object, so the offset is shared.

The VFS object model therefore implements the descriptor and open-description relationships visible to applications.

Mount Objects in the Namespace Tree

A VFS superblock describes a file-system instance. A mount object describes how a root from that instance is attached at a location in a process's mount namespace.

Suppose an ext4 instance is attached at:

Path resolution walks through the parent file system to the catalog mount point, crosses to the mounted instance's root dentry, and continues there.

The parent namespace path /var/lib/catalog crosses a mount point, and resolution continues at the mounted file system's root with orders/42.json.

The same file-system tree can be exposed at more than one namespace location through bind-style mounting. Consequently, a VFS path location needs both:

The dentry identifies a component inside a file-system tree. The mount object identifies which namespace attachment is being traversed.

Mount options such as read-only, noexec, or nosuid can affect operations independently of inode mode bits. VFS has the context needed to apply those attachment-level rules during the walk and operation.

Late Dispatch Through Operation Tables

VFS objects carry or reach tables of function pointers supplied by the file-system implementation.

Conceptually, a file object supports operations resembling:

An inode supports namespace and metadata operations resembling:

These are simplified teaching structures, not exact kernel declarations. Real Linux interfaces use version-specific function signatures and additional operation tables.

The design pattern is late dispatch:

A generic operation combined with the resolved VFS object selects an entry in that object's operation table, which names the specific implementation function.

For a read:

The system call stays the same. The resolved object's operations determine what happens next.

If an object does not support a requested operation, VFS returns an appropriate error. A directory and a regular file can share a VFS inode interface without both supporting ordinary byte writes.

Loading simulation...

Opening a Path Through VFS

Consider:

The logical path is:

  1. The system-call handler receives the pathname and flags.
  2. VFS selects the starting root because the path is absolute.
  3. It walks srv, catalog, and config.json as component dentries.
  4. At mount boundaries, it switches to the attached tree.
  5. For component lookup, it uses the relevant file system's directory operations.
  6. It obtains the final VFS inode and applies common plus file-system-specific checks.
  7. It asks the implementation to open the object.
  8. It creates a VFS file object and installs a descriptor-table entry.
  9. The descriptor number returns to the application.

VFS owns the common walk and object relationships. The concrete file system owns how a directory lookup reaches its stored, remote, or generated representation.

Reading Through VFS

Once the file is open, read() no longer resolves the pathname:

The logical read path is:

  1. Look up fd in the calling process's descriptor table.
  2. Follow it to the VFS file object.
  3. Confirm that the open description permits reading.
  4. Invoke the read operation associated with that file object.
  5. Advance the shared file offset when the operation uses it.
  6. Return the reported byte count or error.

The call is identical in all three cases. Only the final stage knows whether the bytes come from a disk, a network peer, or code that generates them on demand.

For /etc/hostname, the implementation commonly obtains bytes belonging to a persistent regular file.

For /proc/uptime, the implementation formats current kernel timing information. The result can change between reads even though no storage device contains a normal file with those bytes.

VFS unifies dispatch and return conventions. It does not force the two implementations to obtain data the same way.

VFS Dispatch for Namespace Changes

Operations such as create, unlink, and rename begin with path resolution and then modify directory relationships.

For:

VFS resolves the parent directory and final dentry, checks common constraints, and calls the file system's unlink implementation.

A local disk file system updates its directory and inode metadata. NFS sends a request to the remote server. A read-only synthetic file system can reject the operation.

VFS also enforces cross-object rules that require namespace context. A rename between two different mounted file-system instances cannot be completed by one implementation as a single internal rename, so it normally fails with EXDEV.

The common system-call signature does not mean every target permits the operation:

VFS provides a common route to the responsible implementation and a common style of result.

File Semantics in Synthetic File Systems

procfs and sysfs expose kernel state through pathnames and file operations.

Examples include:

These are not ordinary persistent files placed on a disk. Their VFS inodes and operations represent live kernel objects or generated attributes.

This can produce unfamiliar metadata:

stat can report a logical size of zero while cat still reads nonempty generated output. The producer does not know the complete future contents as one stored regular-file byte sequence.

The file interface remains valuable. Shell redirection, descriptors, permission checks, polling support where provided, and ordinary tools can interact with kernel state without a unique system call for every statistic.

The interface does not turn every pseudo-file into a fully ordinary regular file. Seeking, writing, renaming, and metadata behavior depend on the object and its implementation.

Remote File Systems: Interface Preservation Without Locality

An NFS-mounted file can be opened with the same open() call as a local file. VFS dispatches operations to the NFS client implementation, which communicates with a remote server.

An application read() reaches the VFS file object, which dispatches to the NFS client implementation, which issues a network request to the server.

The common interface does not erase the network:

  • The operation can have much higher and more variable latency.
  • Server or network failures can become file-operation errors.
  • Attribute and consistency behavior follows the remote protocol and mount configuration.
  • A pathname can become unavailable without a local storage-device failure.

Applications should not infer performance or failure behavior solely from the fact that an object uses a file descriptor.

VFS normalizes the programming model. It cannot make a remote file system physically equivalent to a local one.

Semantic Variation Across VFS File Systems

File systems differ in capabilities and guarantees.

Examples include:

  • Case-sensitive versus case-insensitive name matching
  • Maximum filename and file sizes
  • Supported timestamp precision
  • Extended attributes and access-control support
  • Sparse-file, reflink, or snapshot capabilities
  • Rename, locking, and consistency details
  • Behavior under remote disconnection

VFS defines common operations and objects, but each operation still has an implementation contract.

An application can ask to create a hard link through a common syscall. One file system may support it, while another returns an error. An application can ask to seek, but a particular pseudo-file can reject the request.

Portable code handles unsupported operations and does not assume that successful behavior observed on one file-system type is universal.

File-System Registration with VFS

Linux file-system implementations register a file-system type with the kernel. The registration supplies enough information for the kernel to create or obtain a file-system instance when it is mounted.

Implementations can be built into the kernel or supplied by loadable modules. Once registered and mounted, their roots participate in the VFS namespace.

Linux exposes known file-system types in:

A simplified result can contain:

In this file, the nodev prefix means the file-system type does not require a block-device source. It should not be confused with the separate nodev mount option that controls interpretation of device special files.

The list shows types currently known to the running kernel, not necessarily every file system currently mounted.

Observing VFS Dispatch on Linux

Use findmnt to identify the mounted file system serving a pathname:

Typical file-system types are:

Exact results depend on the host, container, and mount namespace.

GNU stat can report the file-system type:

Now trace the common user-facing operations:

Both traces contain the same families of system calls. The path selected during openat() determines which VFS objects and implementation operations handle the later read().

Dynamic-loader activity can add unrelated calls to the trace. The important observation is not that every line matches, but that applications use the same syscall interface for different mounted file systems.

Inspect mount relationships in detail with:

or:

mountinfo exposes the calling process's mount-namespace view, including mount identifiers, parent relationships, mount points, options, and file-system types.

Reading Stored and Generated Files with One Program

The following program applies the same open(), fstat(), read(), and close() sequence to any supplied path. It prints the metadata view and escapes a short prefix of the bytes.

Compile and compare:

Both paths produce descriptors and byte counts through the same calls. On Linux, /proc/uptime commonly reports st_size as zero while the read returns generated text. The output makes the common VFS interface visible without implying a common storage mechanism.

Summary

Linux VFS is the in-kernel layer that provides common pathname, metadata, and open-file operations while dispatching to individual file-system implementations. It lets local, memory-backed, remote, and synthetic file systems participate in one mounted namespace.

Its core object model includes superblocks for file-system instances, inodes for objects, dentries for component names, file objects for open descriptions, and mount objects for namespace attachments. These common objects can be backed by disk metadata, remote state, memory, or generated kernel information.

Operation tables provide late dispatch. After VFS resolves a pathname or descriptor, it invokes the implementation associated with that object. The syscall remains read() or rename(), while ext4, NFS, procfs, or another file system performs the specific work.

VFS unifies the programming interface, not every guarantee. File-system capabilities, semantics, latency, and failure modes can still differ.

The central mental model is:

System calls enter one VFS object model; resolved objects dispatch each operation to the file system that owns them.

Quiz

The Virtual File System Layer Quiz

5 quizzes