Linux-Kernel-Notes

Thinking in linux kernel

View on GitHub

2.2 Namespace and Metadata Operations

中文 English Contents

Chapter 2: FUSE Implementation Analysis · Article 2 of 6

This article follows fs/fuse/dir.c through pathname lookup, inode instantiation, attribute refresh, create, unlink, and rename. The central issue is keeping VFS state and daemon state consistent across a message boundary.

1. Begin at the inode operation table

A normal directory maps VFS methods to FUSE entry points such as:

.lookup   -> fuse_lookup
.create   -> fuse_create
.unlink   -> fuse_unlink
.rename   -> fuse_rename2
.getattr  -> fuse_getattr
.setattr  -> fuse_setattr

Different inode types use different tables, while much of the attribute machinery is shared. The table tells you the VFS contract before you inspect protocol details.

2. LOOKUP call chain

VFS path walk
  -> fuse_lookup(dir, dentry, flags)
  -> fuse_lookup_name(sb, parent_nodeid, name, ...)
  -> fuse_lookup_init(args, parent_nodeid, name, out)
  -> fuse_simple_request()
  -> daemon: FUSE_LOOKUP
  -> validate fuse_entry_out
  -> fuse_iget() / update attributes
  -> d_splice_alias() or negative dentry

The reply establishes identity, a lookup reference, an entry lease, and an attribute lease in one transaction.

3. Reply validation

The kernel treats daemon output as untrusted. It checks, among other conditions:

A malformed reply is a protocol error, not a cacheable negative lookup.

4. Positive and negative dentries

A successful lookup instantiates or reuses an inode and adds a lookup reference that the daemon must account for. ENOENT can produce a negative dentry with its own validity duration.

Two leases remain independent:

An expired attribute lease can cause GETATTR without repeating name lookup. An expired entry lease may require confirming that the name still identifies the same object.

5. Inode instantiation and attribute updates

FUSE finds or creates a VFS inode from node identity, converts fuse_attr, and updates cached state. File size is especially delicate:

The implementation is consequently much more than assigning reply fields to struct inode.

6. GETATTR

fuse_getattr() first decides whether the attribute cache is still valid. If not, it sends FUSE_GETATTR, possibly including an open fh, then applies the returned attributes before filling the caller’s kstat.

vfs_getattr
  -> fuse_getattr
  -> valid cache: generic_fillattr
  -> otherwise FUSE_GETATTR
  -> fuse_change_attributes()
  -> generic_fillattr

7. SETATTR and truncate

chmod, chown, timestamp changes, and truncate can converge on fuse_setattr(). It translates VFS iattr valid bits into fuse_setattr_in, sends FUSE_SETATTR, and reconciles the reply.

A truncate sequence must coordinate:

  1. permission and writer-state checks;
  2. page cache, writeback, and direct-I/O exclusion;
  3. backend size modification;
  4. response attribute adoption and cache truncation;
  5. a coherent state if the remote operation fails.

8. Combined create and open

fuse_create() can use fuse_create_open() and FUSE_CREATE to receive both a new fuse_entry_out and fuse_open_out. This avoids a second OPEN round trip.

Combining operations complicates rollback. If the backend created the entry and handle but local file initialization later fails, the kernel must release the handle and balance the new lookup reference. Compatibility paths must also preserve O_EXCL and atomic-create semantics.

fuse_unlink() sends the parent node ID and name. On success, the VFS namespace entry disappears and related caches are updated, but the underlying object may remain alive because:

10. Rename and invalidation

fuse_rename2() uses FUSE_RENAME2 for supported flags and can fall back to the older opcode. One request names old parent/name and new parent/name.

After success, code must reconcile both parent directories, source and target dentries, an overwritten target inode, and cross-directory parent relationships. EXCHANGE, NOREPLACE, and WHITEOUT add distinct cases.

The daemon’s backend operation must itself be atomic. Kernel dentry locks cannot turn a two-step remote implementation into an atomic rename.

11. FORGET accounting

When VFS cache references disappear, FUSE queues FORGET or BATCH_FORGET. These messages normally receive no reply. They reduce daemon lookup counts, but do not close live file handles.

When diagnosing leaks, count separately:

12. Debugging method

Record parent node ID, name, opcode, unique, response node ID/generation, entry and attribute lease, and final dentry/inode. A pathname string alone loses identity after rename, aliasing, and unlink.

13. Summary

dir.c converts VFS name operations into protocol transactions and replies into dentry/inode state. LOOKUP establishes identity, lookup accounting, and two leases. Create, unlink, and rename must preserve atomicity and invalidation ordering across kernel and daemon state.

Previous: Source Map, Mount, and Initialization Next: Open, Read, Write, and Writeback