2023-10-25 06:11:33

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH v2] nfs: derive f_fsid from s_dev and server's fsid

Use s_dev number and the server's fsid to report f_fsid in statfs(2).

The server's fsid could be zero for NFSv4 root export and is not unique
across different servers, so we use the s_dev number to avoid local
f_fsid collisions.

The s_dev number could be easily recycled, so we use a 32bit hash of the
server's fsid to try to avoid the recycling of same local f_fsid for
different remote fs. The anon bdev number is only 20 bits (major is 0),
so we could use more bits for the server's fsid hash, but avoiding f_fsid
recycling is not critical, so 32bit hash is enough.

This allows nfs client to be monitored by fanotify filesystem watch
for local client access if nfs supports re-export.

For example, with inotify-tools 4.23.8.0, the following command can be
used to watch local client access over entire nfs filesystem:

fsnotifywatch --filesystem /mnt/nfs

Note that fanotify filesystem watch does not report remote changes on
server. It provides the same notifications as inotify, but it watches
over the entire filesystem and reports file handle of objects and fsid
with events.

Signed-off-by: Amir Goldstein <[email protected]>
---

Anna, Trond,

I have changed v2 according to feedback from Ben.

I would like to refer you to the documentation of f_fsid in statfs(2):

"Nobody knows what f_fsid is supposed to contain...
The general idea is that f_fsid contains some random stuff such that the
pair (f_fsid,ino) uniquely determines a file. Some operating systems use
(a variation on) the device number, or the device number combined with the
filesystem type..."

This definition leaves a lot of room for interpretations.
I chose f_fsid format {dev_num, fsid_hash}, because I think that it nicely
extends f_fsid format {dev_num, 0}, used by many fs without persistent fsid.

Thanks,
Amir.

Changes since v1:
- Use d_sev number to avoid collisions (bcodding)

fs/nfs/super.c | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 0d6473cb00cb..30bcd53da3bc 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -295,6 +295,15 @@ int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_ffree = res.afiles;

buf->f_namelen = server->namelen;
+ /*
+ * Using the anon bdev number to avoid local f_fsid collisions.
+ * Server's fsid could be zero for NFSv4 root export and is not unique
+ * across different servers, but we use it as best effort to try to
+ * avoid the recycling of same local f_fsid for different remote fs.
+ */
+ buf->f_fsid.val[0] = new_encode_dev(server->s_dev);
+ buf->f_fsid.val[1] = hash_64(server->fsid.major, 32) ^
+ hash_64(server->fsid.minor, 32);

return 0;

--
2.34.1