Thanks to visit codestin.com
Credit goes to github.com

Skip to content

vfs: give stable inode numbers from the backend ID - #9837

Draft
biagiop1986 wants to merge 1 commit into
rclone:masterfrom
biagiop1986:stable-inode-numbers
Draft

vfs: give stable inode numbers from the backend ID#9837
biagiop1986 wants to merge 1 commit into
rclone:masterfrom
biagiop1986:stable-inode-numbers

Conversation

@biagiop1986

@biagiop1986 biagiop1986 commented Aug 28, 2026

Copy link
Copy Markdown

newInode allocates inode numbers from a process-wide atomic counter, and newFile/newDir call it every time they construct a node. When the directory cache expires, ForgetAll empties d.items, so the next listing rebuilds those nodes and the same file comes back with a different inode number.

This derives the inode number from the backend's own object ID when the entry implements fs.IDer and has one, so it stays the same for as long as the object does, however many times the node is dropped and rebuilt.

Many backends can supply an object ID - Drive, Onedrive, Box, Dropbox, B2 and Mega among them - plus the wrapping backends (crypt, union, chunker, compress, hasher, cache, combine) which forward it. Anything without an ID keeps the counter exactly as before: that includes local, which has no IDs at all, and most directories, since far fewer backends give a directory an ID than give one to an object. The two are kept in separate halves of the uint64 range so they cannot collide.

This complements #9548, which made cmd/mount2 report the VFS inode to the kernel at all: that made the number visible, this makes it stable.

I have opened this as a draft because the following are open to your preference:

  • Hashing. I used md5 of the ID truncated to 8 bytes, to match the existing md5.Sum([]byte(...)) idiom in cmd/serve/nfs/cache.go, fs/newfs.go and fs/filter/filter.go. Happy to change it.
  • The high-bit split between ID-derived and counter-derived inodes. It makes collisions between the two impossible, but halves each space.
  • Whether this should be opt-in behind a flag. I did not add one because inode stability was never guaranteed and mount2: allow the mount to be exported by the kernel NFS server #9548 shipped without one, but I can add one if you would rather not change existing behaviour by default.

Linked issue

Fixes #9830

For new or changed backends

Not a backend change.

Testing

vfs/vfs_test.go adds TestDeriveInode (same ID gives the same inode number, different IDs give different inode numbers, missing or empty ID falls back to the counter), plus TestFileInodeAfterForget and TestDirInodeAfterForget, which check a file/dir keeps its inode across a ForgetAll and that it does not without an ID. I verified the tests fail without the change and pass with it.

The ESTALE path itself needs a kernel NFS server, so as with #9548 it is not covered by the local harness. make quicktest passes apart from cmd/mountlib and cmd/serve/s3, which fail on my machine for environmental reasons (macFUSE too old, and flock not present on macOS); both fail identically on a clean master.

Checklist

  • This change is trivial OR it has been discussed and agreed in the linked issue.
  • I have read the contribution guidelines.
  • (If I used AI tools to help write this code) I have read and understood the AI-assisted contributions guidance, and I have tested and take ownership of this change myself.
  • I have added tests for all changes in this PR if appropriate.
  • I have added documentation for the changes if appropriate.
  • All commit messages are in house style.
  • (Backend changes only) test_all passes for this backend and if submitting a new backend can provide a test account for the integration tester - see CONTRIBUTING.md.
  • This Pull Request is ready for review.

Before this change, `newInode` handed out inode numbers from a process-wide
atomic counter, and `newFile`/`newDir` called it every time they built a node.
The directory cache drops its nodes when it expires -- `ForgetAll` empties
`d.items` -- so the next listing rebuilt them and the same file came back with
a different inode number.

That is invisible to a local FUSE mount, but not to one re-exported over NFS:
the kernel validates its file handles against the inode number, so once the
directory cache had expired, clients saw ESTALE for files they still had open.

This change derives the inode number from the backend's own ID when the object
or directory implements `fs.IDer` and has one, so it stays the same for as long
as the object does, no matter how often the node is dropped and rebuilt. Many
backends can supply an ID for their objects, e.g., Drive, Onedrive, Box,
Dropbox, B2, and Mega.

Anything without an ID keeps the counter as before. That includes the local
backend, which has no IDs at all, and most directories, since far fewer
backends give a directory an ID than give one to an object. The two are kept
in separate halves of the uint64 range -- ID-derived numbers have the top bit
set, counter ones do not -- so they cannot collide with each other.

This complements the earlier fix to make cmd/mount2 report the VFS inode to the
kernel at all: that made the number visible, this makes it stable.

The ESTALE path itself needs a kernel NFS server so it isn't covered here; the
tests check the property it depends on, that a given backend ID always maps to
the same inode and that a file keeps its inode across a `ForgetAll`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

vfs: give stable inode numbers from the backend ID

1 participant