Linux-Kernel-Notes

Thinking in linux kernel

View on GitHub

2.1 Source Map, Mount, and Initialization

中文 English Contents

Chapter 2: FUSE Implementation Analysis · Article 1 of 6

This article uses the current kernel tree’s fs/fuse/ and include/uapi/linux/fuse.h as its baseline. It follows registration through mount construction and FUSE_INIT, and explains why “the VFS mount exists” and “the protocol connection is negotiated” are separate milestones.

1. Source map

File Main responsibility
fs/fuse/inode.c registration, mount, connection initialization, inode construction and attributes
fs/fuse/dir.c lookup, namespace mutation, rename, attributes, and permissions
fs/fuse/file.c open/release, cached I/O, direct I/O, writeback, locks, and fsync
fs/fuse/dev.c /dev/fuse queues, daemon reads/writes, interruption, and completion
fs/fuse/req.c channel-independent synchronous, background, and notification-reply sends
fs/fuse/dax.c virtio-fs DAX window, iomap, faults, and mapping reclaim
fs/fuse/control.c fusectl waiting/abort/background controls
fs/fuse/readdir.c readdir and readdirplus
fs/fuse/ioctl.c forwarded ioctl and retry protocol
fs/fuse/xattr.c extended attributes
fs/fuse/passthrough.c backing-file passthrough
fs/fuse/virtio_fs.c virtio-fs transport and DAX-device attachment
fs/fuse/fuse_i.h internal core objects and cross-file APIs
fs/fuse/fuse_dev_i.h channels, requests, input queues, and processing queues
include/uapi/linux/fuse.h userspace ABI: opcodes, flags, and binary structures

The productive reading direction is VFS operation table → operation implementation → fuse_args → request/channel. Starting in the byte-copy loops in dev.c obscures the VFS contract that created the request.

2. Filesystem registration and mount chain

The FUSE module registers a file_system_type whose .init_fs_context points to fuse_init_fs_context. Depending on configuration, related filesystem types such as fuseblk are also registered.

module initialization
  -> register_filesystem(&fuse_fs_type)
  -> mount request
  -> fuse_init_fs_context()
  -> parse mount parameters
  -> fuse_get_tree()
  -> get_tree_nodev()/get_tree_bdev()
  -> fuse_fill_super()
  -> fuse_fill_super_common()
  -> fuse_send_init()

The modern mount API stores parsed values in a private fs_context until tree construction consumes them. Parameter lifetime is therefore distinct from superblock and connection lifetime.

3. Objects created during mount

Keep these objects separate:

A classic mount normally begins with an already-open FUSE device descriptor. virtio-fs builds the transport differently while reusing much of the common superblock and connection logic. /dev/fuse is therefore a transport mechanism, not a universal architectural requirement.

4. fuse_fill_super_common()

Common superblock construction broadly:

  1. installs superblock operations, limits, and time granularity;
  2. attaches the fuse_conn and fuse_mount;
  3. creates the root inode and dentry;
  4. configures BDI, cache, and writeback behavior;
  5. installs permission/export policy from mount mode;
  6. establishes references needed for later teardown.

At this point the VFS skeleton exists, but optional protocol capabilities are not yet authoritative.

5. FUSE_INIT capability negotiation

fuse_send_init() builds the initialization request. A completion handler validates the daemon’s version and fields, then publishes negotiated state into fuse_conn.

Negotiated values include, depending on protocol version:

Negotiation is not a union of flags. A feature is usable only when both sides support it and all associated fields pass validation.

6. Why INIT is special

Normal requests depend on negotiated size and feature limits, while INIT itself occurs before those limits exist. The code must accept older response layouts, establish defaults, and downgrade cleanly.

Typical protocol mistakes include:

7. Connection state sequence

allocate connection
  -> establish transport
  -> create VFS root objects
  -> send FUSE_INIT
  -> success: publish initialized state
  -> ordinary requests
  -> disconnect/abort/unmount
  -> release after all references drain

Every waiter for initialization must wake on success, failure, and daemon disappearance. Feature fields used by later request paths should remain stable after publication.

8. Root inode

The protocol reserves a root node ID. The root inode is not initially discovered through ordinary pathname lookup, but it must still exist as a VFS inode/dentry. A later lookup under the mount root sends that root node ID as the parent.

Root mode, ownership, and initial attributes need safe values during the interval before the first attribute refresh.

9. Debugging mount failures

Use the symptom to choose an entry point:

10. Summary

FUSE mount code first builds VFS and connection objects, then FUSE_INIT establishes the protocol contract. super_block, fuse_mount, fuse_conn, and transport have different lifetimes. Every later source path must be read in light of capabilities actually negotiated during INIT.

Next: Namespace and Metadata Operations