2023-10-18 10:00:19

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 0/5] Support more filesystems with FAN_REPORT_FID

Jan,

Following up on the plan laid out in this discussion, this patch set
implements the simpler and less controversial part of the plan to
enable AT_HANDLE_FID for all filesystems.

One filesystem that I tested which gained FAN_REPORT_FID support is 9p,
but there are many other filesystem for whom fanotify will become mostly
inotify drop-in replacement after this change.

Since the main goal of this change is to progress fanotify towards being
an inotify drop-in replacement, support for FAN_REPORT_FID with sb/mount
mark is at lower priority.

Because I think that support for FAN_REPORT_FID with sb/mount mark is
controversial with non-decodeable (AT_HANDLE_FID) file handles, I have
also disabled this feature that was added in v6.6-rc1 to ovelrayfs.

If you agree to this retroactive change, the path #1 should be fast
tracked into v6.6.

The rest of the changes should probably go in via the vfs tree after
review from you and nfsd maintainers.

Thanks,
Amir.

[1] https://lore.kernel.org/r/20230920110429.f4wkfuls73pd55pv@quack3/

Amir Goldstein (5):
fanotify: limit reporting of event with non-decodeable file handles
exportfs: add helpers to check if filesystem can encode/decode file
handles
exportfs: make ->encode_fh() a mandatory method for NFS export
exportfs: define FILEID_INO64_GEN* file handle types
exportfs: support encoding non-decodeable file handles by default

Documentation/filesystems/nfs/exporting.rst | 7 +--
Documentation/filesystems/porting.rst | 9 ++++
fs/affs/namei.c | 1 +
fs/befs/linuxvfs.c | 1 +
fs/efs/super.c | 1 +
fs/erofs/super.c | 1 +
fs/exportfs/expfs.c | 50 +++++++++++++++------
fs/ext2/super.c | 1 +
fs/ext4/super.c | 1 +
fs/f2fs/super.c | 1 +
fs/fat/nfs.c | 1 +
fs/fhandle.c | 6 +--
fs/fuse/inode.c | 7 +--
fs/jffs2/super.c | 1 +
fs/jfs/super.c | 1 +
fs/nfsd/export.c | 3 +-
fs/notify/fanotify/fanotify_user.c | 25 +++++++----
fs/ntfs/namei.c | 1 +
fs/ntfs3/super.c | 1 +
fs/overlayfs/util.c | 2 +-
fs/smb/client/export.c | 9 ++--
fs/squashfs/export.c | 1 +
fs/ufs/super.c | 1 +
include/linux/exportfs.h | 46 ++++++++++++++++++-
24 files changed, 133 insertions(+), 45 deletions(-)

--
2.34.1


2023-10-18 10:00:28

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 1/5] fanotify: limit reporting of event with non-decodeable file handles

Commit a95aef69a740 ("fanotify: support reporting non-decodeable file
handles") merged in v6.5-rc1, added the ability to use an fanotify group
with FAN_REPORT_FID mode to watch filesystems that do not support nfs
export, but do know how to encode non-decodeable file handles, with the
newly introduced AT_HANDLE_FID flag.

At the time that this commit was merged, there were no filesystems
in-tree with those traits.

Commit 16aac5ad1fa9 ("ovl: support encoding non-decodable file handles"),
merged in v6.6-rc1, added this trait to overlayfs, thus allowing fanotify
watching of overlayfs with FAN_REPORT_FID mode.

In retrospect, allowing an fanotify filesystem/mount mark on such
filesystem in FAN_REPORT_FID mode will result in getting events with
file handles, without the ability to resolve the filesystem objects from
those file handles (i.e. no open_by_handle_at() support).

For v6.6, the safer option would be to allow this mode for inode marks
only, where the caller has the opportunity to use name_to_handle_at() at
the time of setting the mark. In the future we can revise this decision.

Fixes: a95aef69a740 ("fanotify: support reporting non-decodeable file handles")
Signed-off-by: Amir Goldstein <[email protected]>
---
fs/notify/fanotify/fanotify_user.c | 25 +++++++++++++++++--------
1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index f69c451018e3..537c70beaad0 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -1585,16 +1585,25 @@ static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
}

/* Check if filesystem can encode a unique fid */
-static int fanotify_test_fid(struct dentry *dentry)
+static int fanotify_test_fid(struct dentry *dentry, unsigned int flags)
{
+ unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
+ const struct export_operations *nop = dentry->d_sb->s_export_op;
+
+ /*
+ * We need to make sure that the filesystem supports encoding of
+ * file handles so user can use name_to_handle_at() to compare fids
+ * reported with events to the file handle of watched objects.
+ */
+ if (!nop)
+ return -EOPNOTSUPP;
+
/*
- * We need to make sure that the file system supports at least
- * encoding a file handle so user can use name_to_handle_at() to
- * compare fid returned with event to the file handle of watched
- * objects. However, even the relaxed AT_HANDLE_FID flag requires
- * at least empty export_operations for ecoding unique file ids.
+ * For sb/mount mark, we also need to make sure that the filesystem
+ * supports decoding file handles, so user has a way to map back the
+ * reported fids to filesystem objects.
*/
- if (!dentry->d_sb->s_export_op)
+ if (mark_type != FAN_MARK_INODE && !nop->fh_to_dentry)
return -EOPNOTSUPP;

return 0;
@@ -1812,7 +1821,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
if (ret)
goto path_put_and_out;

- ret = fanotify_test_fid(path.dentry);
+ ret = fanotify_test_fid(path.dentry, flags);
if (ret)
goto path_put_and_out;

--
2.34.1

2023-10-18 10:00:44

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 2/5] exportfs: add helpers to check if filesystem can encode/decode file handles

The logic of whether filesystem can encode/decode file handles is open
coded in many places.

In preparation to changing the logic, move the open coded logic into
inline helpers.

Signed-off-by: Amir Goldstein <[email protected]>
---
fs/exportfs/expfs.c | 8 ++------
fs/fhandle.c | 6 +-----
fs/nfsd/export.c | 3 +--
fs/notify/fanotify/fanotify_user.c | 4 ++--
fs/overlayfs/util.c | 2 +-
include/linux/exportfs.h | 27 +++++++++++++++++++++++++++
6 files changed, 34 insertions(+), 16 deletions(-)

diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
index c20704aa21b3..9ee205df8fa7 100644
--- a/fs/exportfs/expfs.c
+++ b/fs/exportfs/expfs.c
@@ -396,11 +396,7 @@ int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid,
{
const struct export_operations *nop = inode->i_sb->s_export_op;

- /*
- * If a decodeable file handle was requested, we need to make sure that
- * filesystem can decode file handles.
- */
- if (nop && !(flags & EXPORT_FH_FID) && !nop->fh_to_dentry)
+ if (!exportfs_can_encode_fh(nop, flags))
return -EOPNOTSUPP;

if (nop && nop->encode_fh)
@@ -456,7 +452,7 @@ exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len,
/*
* Try to get any dentry for the given file handle from the filesystem.
*/
- if (!nop || !nop->fh_to_dentry)
+ if (!exportfs_can_decode_fh(nop))
return ERR_PTR(-ESTALE);
result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type);
if (IS_ERR_OR_NULL(result))
diff --git a/fs/fhandle.c b/fs/fhandle.c
index 6ea8d35a9382..18b3ba8dc8ea 100644
--- a/fs/fhandle.c
+++ b/fs/fhandle.c
@@ -26,12 +26,8 @@ static long do_sys_name_to_handle(const struct path *path,
/*
* We need to make sure whether the file system support decoding of
* the file handle if decodeable file handle was requested.
- * Otherwise, even empty export_operations are sufficient to opt-in
- * to encoding FIDs.
*/
- if (!path->dentry->d_sb->s_export_op ||
- (!(fh_flags & EXPORT_FH_FID) &&
- !path->dentry->d_sb->s_export_op->fh_to_dentry))
+ if (!exportfs_can_encode_fh(path->dentry->d_sb->s_export_op, fh_flags))
return -EOPNOTSUPP;

if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle)))
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index 11a0eaa2f914..dc99dfc1d411 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -421,8 +421,7 @@ static int check_export(struct path *path, int *flags, unsigned char *uuid)
return -EINVAL;
}

- if (!inode->i_sb->s_export_op ||
- !inode->i_sb->s_export_op->fh_to_dentry) {
+ if (!exportfs_can_decode_fh(inode->i_sb->s_export_op)) {
dprintk("exp_export: export of invalid fs type.\n");
return -EINVAL;
}
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 537c70beaad0..ce926eb9feea 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -1595,7 +1595,7 @@ static int fanotify_test_fid(struct dentry *dentry, unsigned int flags)
* file handles so user can use name_to_handle_at() to compare fids
* reported with events to the file handle of watched objects.
*/
- if (!nop)
+ if (!exportfs_can_encode_fid(nop))
return -EOPNOTSUPP;

/*
@@ -1603,7 +1603,7 @@ static int fanotify_test_fid(struct dentry *dentry, unsigned int flags)
* supports decoding file handles, so user has a way to map back the
* reported fids to filesystem objects.
*/
- if (mark_type != FAN_MARK_INODE && !nop->fh_to_dentry)
+ if (mark_type != FAN_MARK_INODE && !exportfs_can_decode_fh(nop))
return -EOPNOTSUPP;

return 0;
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index 89e0d60d35b6..f0a712214ec2 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -55,7 +55,7 @@ int ovl_can_decode_fh(struct super_block *sb)
if (!capable(CAP_DAC_READ_SEARCH))
return 0;

- if (!sb->s_export_op || !sb->s_export_op->fh_to_dentry)
+ if (!exportfs_can_decode_fh(sb->s_export_op))
return 0;

return sb->s_export_op->encode_fh ? -1 : FILEID_INO32_GEN;
diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h
index 11fbd0ee1370..5b3c9f30b422 100644
--- a/include/linux/exportfs.h
+++ b/include/linux/exportfs.h
@@ -233,6 +233,33 @@ extern int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid,
extern int exportfs_encode_fh(struct dentry *dentry, struct fid *fid,
int *max_len, int flags);

+static inline bool exportfs_can_encode_fid(const struct export_operations *nop)
+{
+ return nop;
+}
+
+static inline bool exportfs_can_decode_fh(const struct export_operations *nop)
+{
+ return nop && nop->fh_to_dentry;
+}
+
+static inline bool exportfs_can_encode_fh(const struct export_operations *nop,
+ int fh_flags)
+{
+ /*
+ * If a non-decodeable file handle was requested, we only need to make
+ * sure that filesystem can encode file handles.
+ */
+ if (fh_flags & EXPORT_FH_FID)
+ return exportfs_can_encode_fid(nop);
+
+ /*
+ * If a decodeable file handle was requested, we need to make sure that
+ * filesystem can also decode file handles.
+ */
+ return exportfs_can_decode_fh(nop);
+}
+
static inline int exportfs_encode_fid(struct inode *inode, struct fid *fid,
int *max_len)
{
--
2.34.1

2023-10-18 10:01:02

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 4/5] exportfs: define FILEID_INO64_GEN* file handle types

Similar to the common FILEID_INO32* file handle types, define common
FILEID_INO64* file handle types.

The type values of FILEID_INO64_GEN and FILEID_INO64_GEN_PARENT are the
values returned by fuse and xfs for 64bit ino encoded file handle types.

Note that these type value are filesystem specific and they do not define
a universal file handle format, for example:
fuse encodes FILEID_INO64_GEN as [ino-hi32,ino-lo32,gen] and xfs encodes
FILEID_INO64_GEN as [hostr-order-ino64,gen] (a.k.a xfs_fid64).

The FILEID_INO64_GEN fhandle type is going to be used for file ids for
fanotify from filesystems that do not support NFS export.

Signed-off-by: Amir Goldstein <[email protected]>
---
fs/fuse/inode.c | 7 ++++---
include/linux/exportfs.h | 11 +++++++++++
2 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 2e4eb7cf26fb..e63f966698a5 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -1002,7 +1002,7 @@ static int fuse_encode_fh(struct inode *inode, u32 *fh, int *max_len,
}

*max_len = len;
- return parent ? 0x82 : 0x81;
+ return parent ? FILEID_INO64_GEN_PARENT : FILEID_INO64_GEN;
}

static struct dentry *fuse_fh_to_dentry(struct super_block *sb,
@@ -1010,7 +1010,8 @@ static struct dentry *fuse_fh_to_dentry(struct super_block *sb,
{
struct fuse_inode_handle handle;

- if ((fh_type != 0x81 && fh_type != 0x82) || fh_len < 3)
+ if ((fh_type != FILEID_INO64_GEN &&
+ fh_type != FILEID_INO64_GEN_PARENT) || fh_len < 3)
return NULL;

handle.nodeid = (u64) fid->raw[0] << 32;
@@ -1024,7 +1025,7 @@ static struct dentry *fuse_fh_to_parent(struct super_block *sb,
{
struct fuse_inode_handle parent;

- if (fh_type != 0x82 || fh_len < 6)
+ if (fh_type != FILEID_INO64_GEN_PARENT || fh_len < 6)
return NULL;

parent.nodeid = (u64) fid->raw[3] << 32;
diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h
index 6b6e01321405..21eeb9f6bdbd 100644
--- a/include/linux/exportfs.h
+++ b/include/linux/exportfs.h
@@ -98,6 +98,17 @@ enum fid_type {
*/
FILEID_FAT_WITH_PARENT = 0x72,

+ /*
+ * 64 bit inode number, 32 bit generation number.
+ */
+ FILEID_INO64_GEN = 0x81,
+
+ /*
+ * 64 bit inode number, 32 bit generation number,
+ * 64 bit parent inode number, 32 bit parent generation.
+ */
+ FILEID_INO64_GEN_PARENT = 0x82,
+
/*
* 128 bit child FID (struct lu_fid)
* 128 bit parent FID (struct lu_fid)
--
2.34.1

2023-10-18 10:01:21

by Amir Goldstein

[permalink] [raw]
Subject: [PATCH 5/5] exportfs: support encoding non-decodeable file handles by default

AT_HANDLE_FID was added as an API for name_to_handle_at() that request
the encoding of a file id, which is not intended to be decoded.

This file id is used by fanotify to describe objects in events.

So far, overlayfs is the only filesystem that supports encoding
non-decodeable file ids, by providing export_operations with an
->encode_fh() method and without a ->decode_fh() method.

Add support for encoding non-decodeable file ids to all the filesystems
that do not provide export_operations, by encoding a file id of type
FILEID_INO64_GEN from { i_ino, i_generation }.

A filesystem may that does not support NFS export, can opt-out of
encoding non-decodeable file ids for fanotify by defining an empty
export_operations struct (i.e. with a NULL ->encode_fh() method).

This allows the use of fanotify events with file ids on filesystems
like 9p which do not support NFS export to bring fanotify in feature
parity with inotify on those filesystems.

Note that fanotify also requires that the filesystems report a non-null
fsid. Currently, many simple filesystems that have support for inotify
(e.g. debugfs, tracefs, sysfs) report a null fsid, so can still not be
used with fanotify in file id reporting mode.

Signed-off-by: Amir Goldstein <[email protected]>
---
fs/exportfs/expfs.c | 30 +++++++++++++++++++++++++++---
include/linux/exportfs.h | 10 +++++++---
2 files changed, 34 insertions(+), 6 deletions(-)

diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
index 30da4539e257..34e7d835d4ef 100644
--- a/fs/exportfs/expfs.c
+++ b/fs/exportfs/expfs.c
@@ -383,6 +383,30 @@ int generic_encode_ino32_fh(struct inode *inode, __u32 *fh, int *max_len,
}
EXPORT_SYMBOL_GPL(generic_encode_ino32_fh);

+/**
+ * exportfs_encode_ino64_fid - encode non-decodeable 64bit ino file id
+ * @inode: the object to encode
+ * @fid: where to store the file handle fragment
+ * @max_len: maximum length to store there
+ *
+ * This generic function is used to encode a non-decodeable file id for
+ * fanotify for filesystems that do not support NFS export.
+ */
+static int exportfs_encode_ino64_fid(struct inode *inode, struct fid *fid,
+ int *max_len)
+{
+ if (*max_len < 3) {
+ *max_len = 3;
+ return FILEID_INVALID;
+ }
+
+ fid->i64.ino = inode->i_ino;
+ fid->i64.gen = inode->i_generation;
+ *max_len = 3;
+
+ return FILEID_INO64_GEN;
+}
+
/**
* exportfs_encode_inode_fh - encode a file handle from inode
* @inode: the object to encode
@@ -401,10 +425,10 @@ int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid,
if (!exportfs_can_encode_fh(nop, flags))
return -EOPNOTSUPP;

- if (nop && nop->encode_fh)
- return nop->encode_fh(inode, fid->raw, max_len, parent);
+ if (!nop && (flags & EXPORT_FH_FID))
+ return exportfs_encode_ino64_fid(inode, fid, max_len);

- return -EOPNOTSUPP;
+ return nop->encode_fh(inode, fid->raw, max_len, parent);
}
EXPORT_SYMBOL_GPL(exportfs_encode_inode_fh);

diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h
index 21eeb9f6bdbd..6688e457da64 100644
--- a/include/linux/exportfs.h
+++ b/include/linux/exportfs.h
@@ -134,7 +134,11 @@ struct fid {
u32 parent_ino;
u32 parent_gen;
} i32;
- struct {
+ struct {
+ u64 ino;
+ u32 gen;
+ } __packed i64;
+ struct {
u32 block;
u16 partref;
u16 parent_partref;
@@ -246,7 +250,7 @@ extern int exportfs_encode_fh(struct dentry *dentry, struct fid *fid,

static inline bool exportfs_can_encode_fid(const struct export_operations *nop)
{
- return nop && nop->encode_fh;
+ return !nop || nop->encode_fh;
}

static inline bool exportfs_can_decode_fh(const struct export_operations *nop)
@@ -259,7 +263,7 @@ static inline bool exportfs_can_encode_fh(const struct export_operations *nop,
{
/*
* If a non-decodeable file handle was requested, we only need to make
- * sure that filesystem can encode file handles.
+ * sure that filesystem did not opt-out of encoding fid.
*/
if (fh_flags & EXPORT_FH_FID)
return exportfs_can_encode_fid(nop);
--
2.34.1

2023-10-18 14:28:52

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH 5/5] exportfs: support encoding non-decodeable file handles by default

On Wed, 2023-10-18 at 13:00 +0300, Amir Goldstein wrote:
> AT_HANDLE_FID was added as an API for name_to_handle_at() that request
> the encoding of a file id, which is not intended to be decoded.
>
> This file id is used by fanotify to describe objects in events.
>
> So far, overlayfs is the only filesystem that supports encoding
> non-decodeable file ids, by providing export_operations with an
> ->encode_fh() method and without a ->decode_fh() method.
>
> Add support for encoding non-decodeable file ids to all the filesystems
> that do not provide export_operations, by encoding a file id of type
> FILEID_INO64_GEN from { i_ino, i_generation }.
>
> A filesystem may that does not support NFS export, can opt-out of
> encoding non-decodeable file ids for fanotify by defining an empty
> export_operations struct (i.e. with a NULL ->encode_fh() method).
>
> This allows the use of fanotify events with file ids on filesystems
> like 9p which do not support NFS export to bring fanotify in feature
> parity with inotify on those filesystems.
>
> Note that fanotify also requires that the filesystems report a non-null
> fsid. Currently, many simple filesystems that have support for inotify
> (e.g. debugfs, tracefs, sysfs) report a null fsid, so can still not be
> used with fanotify in file id reporting mode.
>
> Signed-off-by: Amir Goldstein <[email protected]>
> ---
> fs/exportfs/expfs.c | 30 +++++++++++++++++++++++++++---
> include/linux/exportfs.h | 10 +++++++---
> 2 files changed, 34 insertions(+), 6 deletions(-)
>
> diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
> index 30da4539e257..34e7d835d4ef 100644
> --- a/fs/exportfs/expfs.c
> +++ b/fs/exportfs/expfs.c
> @@ -383,6 +383,30 @@ int generic_encode_ino32_fh(struct inode *inode, __u32 *fh, int *max_len,
> }
> EXPORT_SYMBOL_GPL(generic_encode_ino32_fh);
>
> +/**
> + * exportfs_encode_ino64_fid - encode non-decodeable 64bit ino file id
> + * @inode: the object to encode
> + * @fid: where to store the file handle fragment
> + * @max_len: maximum length to store there
> + *
> + * This generic function is used to encode a non-decodeable file id for
> + * fanotify for filesystems that do not support NFS export.
> + */
> +static int exportfs_encode_ino64_fid(struct inode *inode, struct fid *fid,
> + int *max_len)
> +{
> + if (*max_len < 3) {
> + *max_len = 3;
> + return FILEID_INVALID;
> + }
> +
> + fid->i64.ino = inode->i_ino;
> + fid->i64.gen = inode->i_generation;

Note that i_ino is unsigned long and so is a 32-bit value on 32-bit
arches. If the backend storage uses 64-bit inodes, then we usually end
up hashing them down to 32-bits first. e.g. see nfs_fattr_to_ino_t().
ceph has some similar code.

The upshot is that if you're relying on i_ino, the value can change
between different arches, even when they are dealing with the same
backend filesystem.

Since this is expected to be used by filesystems that don't set up
export operations, then that may just be something they have to deal
with. I'm not sure what else you can use in lieu of i_ino in this case.

> + *max_len = 3;
> +
> + return FILEID_INO64_GEN;
> +}
> +
> /**
> * exportfs_encode_inode_fh - encode a file handle from inode
> * @inode: the object to encode
> @@ -401,10 +425,10 @@ int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid,
> if (!exportfs_can_encode_fh(nop, flags))
> return -EOPNOTSUPP;
>
> - if (nop && nop->encode_fh)
> - return nop->encode_fh(inode, fid->raw, max_len, parent);
> + if (!nop && (flags & EXPORT_FH_FID))
> + return exportfs_encode_ino64_fid(inode, fid, max_len);
>
> - return -EOPNOTSUPP;
> + return nop->encode_fh(inode, fid->raw, max_len, parent);
> }
> EXPORT_SYMBOL_GPL(exportfs_encode_inode_fh);
>
> diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h
> index 21eeb9f6bdbd..6688e457da64 100644
> --- a/include/linux/exportfs.h
> +++ b/include/linux/exportfs.h
> @@ -134,7 +134,11 @@ struct fid {
> u32 parent_ino;
> u32 parent_gen;
> } i32;
> - struct {
> + struct {
> + u64 ino;
> + u32 gen;
> + } __packed i64;
> + struct {
> u32 block;
> u16 partref;
> u16 parent_partref;
> @@ -246,7 +250,7 @@ extern int exportfs_encode_fh(struct dentry *dentry, struct fid *fid,
>
> static inline bool exportfs_can_encode_fid(const struct export_operations *nop)
> {
> - return nop && nop->encode_fh;
> + return !nop || nop->encode_fh;
> }
>
> static inline bool exportfs_can_decode_fh(const struct export_operations *nop)
> @@ -259,7 +263,7 @@ static inline bool exportfs_can_encode_fh(const struct export_operations *nop,
> {
> /*
> * If a non-decodeable file handle was requested, we only need to make
> - * sure that filesystem can encode file handles.
> + * sure that filesystem did not opt-out of encoding fid.
> */
> if (fh_flags & EXPORT_FH_FID)
> return exportfs_can_encode_fid(nop);

--
Jeff Layton <[email protected]>

2023-10-18 14:29:46

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH 2/5] exportfs: add helpers to check if filesystem can encode/decode file handles

On Wed, 2023-10-18 at 12:59 +0300, Amir Goldstein wrote:
> The logic of whether filesystem can encode/decode file handles is open
> coded in many places.
>
> In preparation to changing the logic, move the open coded logic into
> inline helpers.
>
> Signed-off-by: Amir Goldstein <[email protected]>
> ---
> fs/exportfs/expfs.c | 8 ++------
> fs/fhandle.c | 6 +-----
> fs/nfsd/export.c | 3 +--
> fs/notify/fanotify/fanotify_user.c | 4 ++--
> fs/overlayfs/util.c | 2 +-
> include/linux/exportfs.h | 27 +++++++++++++++++++++++++++
> 6 files changed, 34 insertions(+), 16 deletions(-)
>
> diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
> index c20704aa21b3..9ee205df8fa7 100644
> --- a/fs/exportfs/expfs.c
> +++ b/fs/exportfs/expfs.c
> @@ -396,11 +396,7 @@ int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid,
> {
> const struct export_operations *nop = inode->i_sb->s_export_op;
>
> - /*
> - * If a decodeable file handle was requested, we need to make sure that
> - * filesystem can decode file handles.
> - */
> - if (nop && !(flags & EXPORT_FH_FID) && !nop->fh_to_dentry)
> + if (!exportfs_can_encode_fh(nop, flags))
> return -EOPNOTSUPP;
>
> if (nop && nop->encode_fh)
> @@ -456,7 +452,7 @@ exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len,
> /*
> * Try to get any dentry for the given file handle from the filesystem.
> */
> - if (!nop || !nop->fh_to_dentry)
> + if (!exportfs_can_decode_fh(nop))
> return ERR_PTR(-ESTALE);
> result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type);
> if (IS_ERR_OR_NULL(result))
> diff --git a/fs/fhandle.c b/fs/fhandle.c
> index 6ea8d35a9382..18b3ba8dc8ea 100644
> --- a/fs/fhandle.c
> +++ b/fs/fhandle.c
> @@ -26,12 +26,8 @@ static long do_sys_name_to_handle(const struct path *path,
> /*
> * We need to make sure whether the file system support decoding of
> * the file handle if decodeable file handle was requested.
> - * Otherwise, even empty export_operations are sufficient to opt-in
> - * to encoding FIDs.
> */
> - if (!path->dentry->d_sb->s_export_op ||
> - (!(fh_flags & EXPORT_FH_FID) &&
> - !path->dentry->d_sb->s_export_op->fh_to_dentry))
> + if (!exportfs_can_encode_fh(path->dentry->d_sb->s_export_op, fh_flags))
> return -EOPNOTSUPP;
>
> if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle)))
> diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
> index 11a0eaa2f914..dc99dfc1d411 100644
> --- a/fs/nfsd/export.c
> +++ b/fs/nfsd/export.c
> @@ -421,8 +421,7 @@ static int check_export(struct path *path, int *flags, unsigned char *uuid)
> return -EINVAL;
> }
>
> - if (!inode->i_sb->s_export_op ||
> - !inode->i_sb->s_export_op->fh_to_dentry) {
> + if (!exportfs_can_decode_fh(inode->i_sb->s_export_op)) {
> dprintk("exp_export: export of invalid fs type.\n");
> return -EINVAL;
> }
> diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
> index 537c70beaad0..ce926eb9feea 100644
> --- a/fs/notify/fanotify/fanotify_user.c
> +++ b/fs/notify/fanotify/fanotify_user.c
> @@ -1595,7 +1595,7 @@ static int fanotify_test_fid(struct dentry *dentry, unsigned int flags)
> * file handles so user can use name_to_handle_at() to compare fids
> * reported with events to the file handle of watched objects.
> */
> - if (!nop)
> + if (!exportfs_can_encode_fid(nop))
> return -EOPNOTSUPP;
>
> /*
> @@ -1603,7 +1603,7 @@ static int fanotify_test_fid(struct dentry *dentry, unsigned int flags)
> * supports decoding file handles, so user has a way to map back the
> * reported fids to filesystem objects.
> */
> - if (mark_type != FAN_MARK_INODE && !nop->fh_to_dentry)
> + if (mark_type != FAN_MARK_INODE && !exportfs_can_decode_fh(nop))
> return -EOPNOTSUPP;
>
> return 0;
> diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
> index 89e0d60d35b6..f0a712214ec2 100644
> --- a/fs/overlayfs/util.c
> +++ b/fs/overlayfs/util.c
> @@ -55,7 +55,7 @@ int ovl_can_decode_fh(struct super_block *sb)
> if (!capable(CAP_DAC_READ_SEARCH))
> return 0;
>
> - if (!sb->s_export_op || !sb->s_export_op->fh_to_dentry)
> + if (!exportfs_can_decode_fh(sb->s_export_op))
> return 0;
>
> return sb->s_export_op->encode_fh ? -1 : FILEID_INO32_GEN;
> diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h
> index 11fbd0ee1370..5b3c9f30b422 100644
> --- a/include/linux/exportfs.h
> +++ b/include/linux/exportfs.h
> @@ -233,6 +233,33 @@ extern int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid,
> extern int exportfs_encode_fh(struct dentry *dentry, struct fid *fid,
> int *max_len, int flags);
>
> +static inline bool exportfs_can_encode_fid(const struct export_operations *nop)
> +{
> + return nop;
> +}
> +
> +static inline bool exportfs_can_decode_fh(const struct export_operations *nop)
> +{
> + return nop && nop->fh_to_dentry;
> +}
> +
> +static inline bool exportfs_can_encode_fh(const struct export_operations *nop,
> + int fh_flags)
> +{
> + /*
> + * If a non-decodeable file handle was requested, we only need to make
> + * sure that filesystem can encode file handles.
> + */
> + if (fh_flags & EXPORT_FH_FID)
> + return exportfs_can_encode_fid(nop);
> +
> + /*
> + * If a decodeable file handle was requested, we need to make sure that
> + * filesystem can also decode file handles.
> + */
> + return exportfs_can_decode_fh(nop);
> +}
> +
> static inline int exportfs_encode_fid(struct inode *inode, struct fid *fid,
> int *max_len)
> {

Nice cleanup.

Reviewed-by: Jeff Layton <[email protected]>

2023-10-18 14:33:02

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH 4/5] exportfs: define FILEID_INO64_GEN* file handle types

On Wed, 2023-10-18 at 12:59 +0300, Amir Goldstein wrote:
> Similar to the common FILEID_INO32* file handle types, define common
> FILEID_INO64* file handle types.
>
> The type values of FILEID_INO64_GEN and FILEID_INO64_GEN_PARENT are the
> values returned by fuse and xfs for 64bit ino encoded file handle types.
>
> Note that these type value are filesystem specific and they do not define
> a universal file handle format, for example:
> fuse encodes FILEID_INO64_GEN as [ino-hi32,ino-lo32,gen] and xfs encodes
> FILEID_INO64_GEN as [hostr-order-ino64,gen] (a.k.a xfs_fid64).
>
> The FILEID_INO64_GEN fhandle type is going to be used for file ids for
> fanotify from filesystems that do not support NFS export.
>
> Signed-off-by: Amir Goldstein <[email protected]>
> ---
> fs/fuse/inode.c | 7 ++++---
> include/linux/exportfs.h | 11 +++++++++++
> 2 files changed, 15 insertions(+), 3 deletions(-)
>
> diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
> index 2e4eb7cf26fb..e63f966698a5 100644
> --- a/fs/fuse/inode.c
> +++ b/fs/fuse/inode.c
> @@ -1002,7 +1002,7 @@ static int fuse_encode_fh(struct inode *inode, u32 *fh, int *max_len,
> }
>
> *max_len = len;
> - return parent ? 0x82 : 0x81;
> + return parent ? FILEID_INO64_GEN_PARENT : FILEID_INO64_GEN;
> }
>
> static struct dentry *fuse_fh_to_dentry(struct super_block *sb,
> @@ -1010,7 +1010,8 @@ static struct dentry *fuse_fh_to_dentry(struct super_block *sb,
> {
> struct fuse_inode_handle handle;
>
> - if ((fh_type != 0x81 && fh_type != 0x82) || fh_len < 3)
> + if ((fh_type != FILEID_INO64_GEN &&
> + fh_type != FILEID_INO64_GEN_PARENT) || fh_len < 3)
> return NULL;
>
> handle.nodeid = (u64) fid->raw[0] << 32;
> @@ -1024,7 +1025,7 @@ static struct dentry *fuse_fh_to_parent(struct super_block *sb,
> {
> struct fuse_inode_handle parent;
>
> - if (fh_type != 0x82 || fh_len < 6)
> + if (fh_type != FILEID_INO64_GEN_PARENT || fh_len < 6)
> return NULL;
>
> parent.nodeid = (u64) fid->raw[3] << 32;
> diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h
> index 6b6e01321405..21eeb9f6bdbd 100644
> --- a/include/linux/exportfs.h
> +++ b/include/linux/exportfs.h
> @@ -98,6 +98,17 @@ enum fid_type {
> */
> FILEID_FAT_WITH_PARENT = 0x72,
>
> + /*
> + * 64 bit inode number, 32 bit generation number.
> + */
> + FILEID_INO64_GEN = 0x81,
> +
> + /*
> + * 64 bit inode number, 32 bit generation number,
> + * 64 bit parent inode number, 32 bit parent generation.
> + */
> + FILEID_INO64_GEN_PARENT = 0x82,
> +
> /*
> * 128 bit child FID (struct lu_fid)
> * 128 bit parent FID (struct lu_fid)

Reviewed-by: Jeff Layton <[email protected]>

2023-10-18 15:11:34

by Amir Goldstein

[permalink] [raw]
Subject: Re: [PATCH 5/5] exportfs: support encoding non-decodeable file handles by default

On Wed, Oct 18, 2023 at 5:28 PM Jeff Layton <[email protected]> wrote:
>
> On Wed, 2023-10-18 at 13:00 +0300, Amir Goldstein wrote:
> > AT_HANDLE_FID was added as an API for name_to_handle_at() that request
> > the encoding of a file id, which is not intended to be decoded.
> >
> > This file id is used by fanotify to describe objects in events.
> >
> > So far, overlayfs is the only filesystem that supports encoding
> > non-decodeable file ids, by providing export_operations with an
> > ->encode_fh() method and without a ->decode_fh() method.
> >
> > Add support for encoding non-decodeable file ids to all the filesystems
> > that do not provide export_operations, by encoding a file id of type
> > FILEID_INO64_GEN from { i_ino, i_generation }.
> >
> > A filesystem may that does not support NFS export, can opt-out of
> > encoding non-decodeable file ids for fanotify by defining an empty
> > export_operations struct (i.e. with a NULL ->encode_fh() method).
> >
> > This allows the use of fanotify events with file ids on filesystems
> > like 9p which do not support NFS export to bring fanotify in feature
> > parity with inotify on those filesystems.
> >
> > Note that fanotify also requires that the filesystems report a non-null
> > fsid. Currently, many simple filesystems that have support for inotify
> > (e.g. debugfs, tracefs, sysfs) report a null fsid, so can still not be
> > used with fanotify in file id reporting mode.
> >
> > Signed-off-by: Amir Goldstein <[email protected]>
> > ---
> > fs/exportfs/expfs.c | 30 +++++++++++++++++++++++++++---
> > include/linux/exportfs.h | 10 +++++++---
> > 2 files changed, 34 insertions(+), 6 deletions(-)
> >
> > diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
> > index 30da4539e257..34e7d835d4ef 100644
> > --- a/fs/exportfs/expfs.c
> > +++ b/fs/exportfs/expfs.c
> > @@ -383,6 +383,30 @@ int generic_encode_ino32_fh(struct inode *inode, __u32 *fh, int *max_len,
> > }
> > EXPORT_SYMBOL_GPL(generic_encode_ino32_fh);
> >
> > +/**
> > + * exportfs_encode_ino64_fid - encode non-decodeable 64bit ino file id
> > + * @inode: the object to encode
> > + * @fid: where to store the file handle fragment
> > + * @max_len: maximum length to store there
> > + *
> > + * This generic function is used to encode a non-decodeable file id for
> > + * fanotify for filesystems that do not support NFS export.
> > + */
> > +static int exportfs_encode_ino64_fid(struct inode *inode, struct fid *fid,
> > + int *max_len)
> > +{
> > + if (*max_len < 3) {
> > + *max_len = 3;
> > + return FILEID_INVALID;
> > + }
> > +
> > + fid->i64.ino = inode->i_ino;
> > + fid->i64.gen = inode->i_generation;
>
> Note that i_ino is unsigned long and so is a 32-bit value on 32-bit
> arches. If the backend storage uses 64-bit inodes, then we usually end
> up hashing them down to 32-bits first. e.g. see nfs_fattr_to_ino_t().
> ceph has some similar code.
>
> The upshot is that if you're relying on i_ino, the value can change
> between different arches, even when they are dealing with the same
> backend filesystem.
>
> Since this is expected to be used by filesystems that don't set up
> export operations, then that may just be something they have to deal
> with. I'm not sure what else you can use in lieu of i_ino in this case.
>

True. That is one more justification for patch [1/5].

If we only support watching inodes when the reported fid is
{i_ino, i_generation}, then the likelihood of collision drops
considerably compared to watching sb/mount.

Because watcher cannot decode fid, watcher must use
name_to_handle_at() when setting up the inode watch and
store it in some index, so it knows how to map from event->fid
to inode later.

This means that even if there is a fid collision between two inodes,
then the watcher can detect the collision when setting up the
inode watch.

Thanks,
Amir.

2023-10-18 15:28:39

by Chuck Lever III

[permalink] [raw]
Subject: Re: [PATCH 5/5] exportfs: support encoding non-decodeable file handles by default

On Wed, Oct 18, 2023 at 01:00:00PM +0300, Amir Goldstein wrote:
> AT_HANDLE_FID was added as an API for name_to_handle_at() that request
> the encoding of a file id, which is not intended to be decoded.
>
> This file id is used by fanotify to describe objects in events.
>
> So far, overlayfs is the only filesystem that supports encoding
> non-decodeable file ids, by providing export_operations with an
> ->encode_fh() method and without a ->decode_fh() method.
>
> Add support for encoding non-decodeable file ids to all the filesystems
> that do not provide export_operations, by encoding a file id of type
> FILEID_INO64_GEN from { i_ino, i_generation }.
>
> A filesystem may that does not support NFS export, can opt-out of
> encoding non-decodeable file ids for fanotify by defining an empty
> export_operations struct (i.e. with a NULL ->encode_fh() method).
>
> This allows the use of fanotify events with file ids on filesystems
> like 9p which do not support NFS export to bring fanotify in feature
> parity with inotify on those filesystems.
>
> Note that fanotify also requires that the filesystems report a non-null
> fsid. Currently, many simple filesystems that have support for inotify
> (e.g. debugfs, tracefs, sysfs) report a null fsid, so can still not be
> used with fanotify in file id reporting mode.
>
> Signed-off-by: Amir Goldstein <[email protected]>
> ---
> fs/exportfs/expfs.c | 30 +++++++++++++++++++++++++++---
> include/linux/exportfs.h | 10 +++++++---
> 2 files changed, 34 insertions(+), 6 deletions(-)
>
> diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
> index 30da4539e257..34e7d835d4ef 100644
> --- a/fs/exportfs/expfs.c
> +++ b/fs/exportfs/expfs.c
> @@ -383,6 +383,30 @@ int generic_encode_ino32_fh(struct inode *inode, __u32 *fh, int *max_len,
> }
> EXPORT_SYMBOL_GPL(generic_encode_ino32_fh);
>
> +/**
> + * exportfs_encode_ino64_fid - encode non-decodeable 64bit ino file id
> + * @inode: the object to encode
> + * @fid: where to store the file handle fragment
> + * @max_len: maximum length to store there

Length in what units? Is the 3 below in units of bytes or
sizeof(__be32) ? I'm guessing it's the latter; if so, it should
be mentioned here. (We have XDR_UNIT for this purpose, btw).

export_encode_fh() isn't exactly clear about that either, sadly.


> + *
> + * This generic function is used to encode a non-decodeable file id for
> + * fanotify for filesystems that do not support NFS export.
> + */
> +static int exportfs_encode_ino64_fid(struct inode *inode, struct fid *fid,
> + int *max_len)
> +{
> + if (*max_len < 3) {
> + *max_len = 3;

Let's make this a symbolic constant rather than a naked integer.


> + return FILEID_INVALID;
> + }
> +
> + fid->i64.ino = inode->i_ino;
> + fid->i64.gen = inode->i_generation;
> + *max_len = 3;
> +
> + return FILEID_INO64_GEN;
> +}
> +
> /**
> * exportfs_encode_inode_fh - encode a file handle from inode
> * @inode: the object to encode
> @@ -401,10 +425,10 @@ int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid,
> if (!exportfs_can_encode_fh(nop, flags))
> return -EOPNOTSUPP;
>
> - if (nop && nop->encode_fh)
> - return nop->encode_fh(inode, fid->raw, max_len, parent);
> + if (!nop && (flags & EXPORT_FH_FID))
> + return exportfs_encode_ino64_fid(inode, fid, max_len);
>
> - return -EOPNOTSUPP;
> + return nop->encode_fh(inode, fid->raw, max_len, parent);
> }
> EXPORT_SYMBOL_GPL(exportfs_encode_inode_fh);
>
> diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h
> index 21eeb9f6bdbd..6688e457da64 100644
> --- a/include/linux/exportfs.h
> +++ b/include/linux/exportfs.h
> @@ -134,7 +134,11 @@ struct fid {
> u32 parent_ino;
> u32 parent_gen;
> } i32;
> - struct {
> + struct {
> + u64 ino;
> + u32 gen;
> + } __packed i64;
> + struct {
> u32 block;
> u16 partref;
> u16 parent_partref;
> @@ -246,7 +250,7 @@ extern int exportfs_encode_fh(struct dentry *dentry, struct fid *fid,
>
> static inline bool exportfs_can_encode_fid(const struct export_operations *nop)
> {
> - return nop && nop->encode_fh;
> + return !nop || nop->encode_fh;
> }
>
> static inline bool exportfs_can_decode_fh(const struct export_operations *nop)
> @@ -259,7 +263,7 @@ static inline bool exportfs_can_encode_fh(const struct export_operations *nop,
> {
> /*
> * If a non-decodeable file handle was requested, we only need to make
> - * sure that filesystem can encode file handles.
> + * sure that filesystem did not opt-out of encoding fid.
> */
> if (fh_flags & EXPORT_FH_FID)
> return exportfs_can_encode_fid(nop);
> --
> 2.34.1
>
>

--
Chuck Lever

2023-10-18 17:31:12

by Amir Goldstein

[permalink] [raw]
Subject: Re: [PATCH 5/5] exportfs: support encoding non-decodeable file handles by default

On Wed, Oct 18, 2023 at 6:28 PM Chuck Lever <[email protected]> wrote:
>
> On Wed, Oct 18, 2023 at 01:00:00PM +0300, Amir Goldstein wrote:
> > AT_HANDLE_FID was added as an API for name_to_handle_at() that request
> > the encoding of a file id, which is not intended to be decoded.
> >
> > This file id is used by fanotify to describe objects in events.
> >
> > So far, overlayfs is the only filesystem that supports encoding
> > non-decodeable file ids, by providing export_operations with an
> > ->encode_fh() method and without a ->decode_fh() method.
> >
> > Add support for encoding non-decodeable file ids to all the filesystems
> > that do not provide export_operations, by encoding a file id of type
> > FILEID_INO64_GEN from { i_ino, i_generation }.
> >
> > A filesystem may that does not support NFS export, can opt-out of
> > encoding non-decodeable file ids for fanotify by defining an empty
> > export_operations struct (i.e. with a NULL ->encode_fh() method).
> >
> > This allows the use of fanotify events with file ids on filesystems
> > like 9p which do not support NFS export to bring fanotify in feature
> > parity with inotify on those filesystems.
> >
> > Note that fanotify also requires that the filesystems report a non-null
> > fsid. Currently, many simple filesystems that have support for inotify
> > (e.g. debugfs, tracefs, sysfs) report a null fsid, so can still not be
> > used with fanotify in file id reporting mode.
> >
> > Signed-off-by: Amir Goldstein <[email protected]>
> > ---
> > fs/exportfs/expfs.c | 30 +++++++++++++++++++++++++++---
> > include/linux/exportfs.h | 10 +++++++---
> > 2 files changed, 34 insertions(+), 6 deletions(-)
> >
> > diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
> > index 30da4539e257..34e7d835d4ef 100644
> > --- a/fs/exportfs/expfs.c
> > +++ b/fs/exportfs/expfs.c
> > @@ -383,6 +383,30 @@ int generic_encode_ino32_fh(struct inode *inode, __u32 *fh, int *max_len,
> > }
> > EXPORT_SYMBOL_GPL(generic_encode_ino32_fh);
> >
> > +/**
> > + * exportfs_encode_ino64_fid - encode non-decodeable 64bit ino file id
> > + * @inode: the object to encode
> > + * @fid: where to store the file handle fragment
> > + * @max_len: maximum length to store there
>
> Length in what units? Is the 3 below in units of bytes or
> sizeof(__be32) ? I'm guessing it's the latter; if so, it should
> be mentioned here. (We have XDR_UNIT for this purpose, btw).
>
> export_encode_fh() isn't exactly clear about that either, sadly.
>
>

Yeh, it's the same all over the place including in filesystem
implementations.

> > + *
> > + * This generic function is used to encode a non-decodeable file id for
> > + * fanotify for filesystems that do not support NFS export.
> > + */
> > +static int exportfs_encode_ino64_fid(struct inode *inode, struct fid *fid,
> > + int *max_len)
> > +{
> > + if (*max_len < 3) {
> > + *max_len = 3;
>
> Let's make this a symbolic constant rather than a naked integer.
>

Sure, no problem.

Thanks for the review.
Amir.

2023-10-19 14:23:08

by Jan Kara

[permalink] [raw]
Subject: Re: [PATCH 1/5] fanotify: limit reporting of event with non-decodeable file handles

On Wed 18-10-23 12:59:56, Amir Goldstein wrote:
> Commit a95aef69a740 ("fanotify: support reporting non-decodeable file
> handles") merged in v6.5-rc1, added the ability to use an fanotify group
> with FAN_REPORT_FID mode to watch filesystems that do not support nfs
> export, but do know how to encode non-decodeable file handles, with the
> newly introduced AT_HANDLE_FID flag.
>
> At the time that this commit was merged, there were no filesystems
> in-tree with those traits.
>
> Commit 16aac5ad1fa9 ("ovl: support encoding non-decodable file handles"),
> merged in v6.6-rc1, added this trait to overlayfs, thus allowing fanotify
> watching of overlayfs with FAN_REPORT_FID mode.
>
> In retrospect, allowing an fanotify filesystem/mount mark on such
> filesystem in FAN_REPORT_FID mode will result in getting events with
> file handles, without the ability to resolve the filesystem objects from
> those file handles (i.e. no open_by_handle_at() support).
>
> For v6.6, the safer option would be to allow this mode for inode marks
> only, where the caller has the opportunity to use name_to_handle_at() at
> the time of setting the mark. In the future we can revise this decision.
>
> Fixes: a95aef69a740 ("fanotify: support reporting non-decodeable file handles")
> Signed-off-by: Amir Goldstein <[email protected]>

OK, I agree sb/mount marks reporting FIDs are hardly usable without
name_to_handle_at() so better forbid them before someone comes up with some
creative abuse. I've queued the patch into my tree.

Honza

> ---
> fs/notify/fanotify/fanotify_user.c | 25 +++++++++++++++++--------
> 1 file changed, 17 insertions(+), 8 deletions(-)
>
> diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
> index f69c451018e3..537c70beaad0 100644
> --- a/fs/notify/fanotify/fanotify_user.c
> +++ b/fs/notify/fanotify/fanotify_user.c
> @@ -1585,16 +1585,25 @@ static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
> }
>
> /* Check if filesystem can encode a unique fid */
> -static int fanotify_test_fid(struct dentry *dentry)
> +static int fanotify_test_fid(struct dentry *dentry, unsigned int flags)
> {
> + unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
> + const struct export_operations *nop = dentry->d_sb->s_export_op;
> +
> + /*
> + * We need to make sure that the filesystem supports encoding of
> + * file handles so user can use name_to_handle_at() to compare fids
> + * reported with events to the file handle of watched objects.
> + */
> + if (!nop)
> + return -EOPNOTSUPP;
> +
> /*
> - * We need to make sure that the file system supports at least
> - * encoding a file handle so user can use name_to_handle_at() to
> - * compare fid returned with event to the file handle of watched
> - * objects. However, even the relaxed AT_HANDLE_FID flag requires
> - * at least empty export_operations for ecoding unique file ids.
> + * For sb/mount mark, we also need to make sure that the filesystem
> + * supports decoding file handles, so user has a way to map back the
> + * reported fids to filesystem objects.
> */
> - if (!dentry->d_sb->s_export_op)
> + if (mark_type != FAN_MARK_INODE && !nop->fh_to_dentry)
> return -EOPNOTSUPP;
>
> return 0;
> @@ -1812,7 +1821,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
> if (ret)
> goto path_put_and_out;
>
> - ret = fanotify_test_fid(path.dentry);
> + ret = fanotify_test_fid(path.dentry, flags);
> if (ret)
> goto path_put_and_out;
>
> --
> 2.34.1
>
--
Jan Kara <[email protected]>
SUSE Labs, CR

2023-10-19 14:23:45

by Jan Kara

[permalink] [raw]
Subject: Re: [PATCH 2/5] exportfs: add helpers to check if filesystem can encode/decode file handles

On Wed 18-10-23 12:59:57, Amir Goldstein wrote:
> The logic of whether filesystem can encode/decode file handles is open
> coded in many places.
>
> In preparation to changing the logic, move the open coded logic into
> inline helpers.
>
> Signed-off-by: Amir Goldstein <[email protected]>

Yeah, good cleanup. Feel free to add:

Reviewed-by: Jan Kara <[email protected]>

Honza

> ---
> fs/exportfs/expfs.c | 8 ++------
> fs/fhandle.c | 6 +-----
> fs/nfsd/export.c | 3 +--
> fs/notify/fanotify/fanotify_user.c | 4 ++--
> fs/overlayfs/util.c | 2 +-
> include/linux/exportfs.h | 27 +++++++++++++++++++++++++++
> 6 files changed, 34 insertions(+), 16 deletions(-)
>
> diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
> index c20704aa21b3..9ee205df8fa7 100644
> --- a/fs/exportfs/expfs.c
> +++ b/fs/exportfs/expfs.c
> @@ -396,11 +396,7 @@ int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid,
> {
> const struct export_operations *nop = inode->i_sb->s_export_op;
>
> - /*
> - * If a decodeable file handle was requested, we need to make sure that
> - * filesystem can decode file handles.
> - */
> - if (nop && !(flags & EXPORT_FH_FID) && !nop->fh_to_dentry)
> + if (!exportfs_can_encode_fh(nop, flags))
> return -EOPNOTSUPP;
>
> if (nop && nop->encode_fh)
> @@ -456,7 +452,7 @@ exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len,
> /*
> * Try to get any dentry for the given file handle from the filesystem.
> */
> - if (!nop || !nop->fh_to_dentry)
> + if (!exportfs_can_decode_fh(nop))
> return ERR_PTR(-ESTALE);
> result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type);
> if (IS_ERR_OR_NULL(result))
> diff --git a/fs/fhandle.c b/fs/fhandle.c
> index 6ea8d35a9382..18b3ba8dc8ea 100644
> --- a/fs/fhandle.c
> +++ b/fs/fhandle.c
> @@ -26,12 +26,8 @@ static long do_sys_name_to_handle(const struct path *path,
> /*
> * We need to make sure whether the file system support decoding of
> * the file handle if decodeable file handle was requested.
> - * Otherwise, even empty export_operations are sufficient to opt-in
> - * to encoding FIDs.
> */
> - if (!path->dentry->d_sb->s_export_op ||
> - (!(fh_flags & EXPORT_FH_FID) &&
> - !path->dentry->d_sb->s_export_op->fh_to_dentry))
> + if (!exportfs_can_encode_fh(path->dentry->d_sb->s_export_op, fh_flags))
> return -EOPNOTSUPP;
>
> if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle)))
> diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
> index 11a0eaa2f914..dc99dfc1d411 100644
> --- a/fs/nfsd/export.c
> +++ b/fs/nfsd/export.c
> @@ -421,8 +421,7 @@ static int check_export(struct path *path, int *flags, unsigned char *uuid)
> return -EINVAL;
> }
>
> - if (!inode->i_sb->s_export_op ||
> - !inode->i_sb->s_export_op->fh_to_dentry) {
> + if (!exportfs_can_decode_fh(inode->i_sb->s_export_op)) {
> dprintk("exp_export: export of invalid fs type.\n");
> return -EINVAL;
> }
> diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
> index 537c70beaad0..ce926eb9feea 100644
> --- a/fs/notify/fanotify/fanotify_user.c
> +++ b/fs/notify/fanotify/fanotify_user.c
> @@ -1595,7 +1595,7 @@ static int fanotify_test_fid(struct dentry *dentry, unsigned int flags)
> * file handles so user can use name_to_handle_at() to compare fids
> * reported with events to the file handle of watched objects.
> */
> - if (!nop)
> + if (!exportfs_can_encode_fid(nop))
> return -EOPNOTSUPP;
>
> /*
> @@ -1603,7 +1603,7 @@ static int fanotify_test_fid(struct dentry *dentry, unsigned int flags)
> * supports decoding file handles, so user has a way to map back the
> * reported fids to filesystem objects.
> */
> - if (mark_type != FAN_MARK_INODE && !nop->fh_to_dentry)
> + if (mark_type != FAN_MARK_INODE && !exportfs_can_decode_fh(nop))
> return -EOPNOTSUPP;
>
> return 0;
> diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
> index 89e0d60d35b6..f0a712214ec2 100644
> --- a/fs/overlayfs/util.c
> +++ b/fs/overlayfs/util.c
> @@ -55,7 +55,7 @@ int ovl_can_decode_fh(struct super_block *sb)
> if (!capable(CAP_DAC_READ_SEARCH))
> return 0;
>
> - if (!sb->s_export_op || !sb->s_export_op->fh_to_dentry)
> + if (!exportfs_can_decode_fh(sb->s_export_op))
> return 0;
>
> return sb->s_export_op->encode_fh ? -1 : FILEID_INO32_GEN;
> diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h
> index 11fbd0ee1370..5b3c9f30b422 100644
> --- a/include/linux/exportfs.h
> +++ b/include/linux/exportfs.h
> @@ -233,6 +233,33 @@ extern int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid,
> extern int exportfs_encode_fh(struct dentry *dentry, struct fid *fid,
> int *max_len, int flags);
>
> +static inline bool exportfs_can_encode_fid(const struct export_operations *nop)
> +{
> + return nop;
> +}
> +
> +static inline bool exportfs_can_decode_fh(const struct export_operations *nop)
> +{
> + return nop && nop->fh_to_dentry;
> +}
> +
> +static inline bool exportfs_can_encode_fh(const struct export_operations *nop,
> + int fh_flags)
> +{
> + /*
> + * If a non-decodeable file handle was requested, we only need to make
> + * sure that filesystem can encode file handles.
> + */
> + if (fh_flags & EXPORT_FH_FID)
> + return exportfs_can_encode_fid(nop);
> +
> + /*
> + * If a decodeable file handle was requested, we need to make sure that
> + * filesystem can also decode file handles.
> + */
> + return exportfs_can_decode_fh(nop);
> +}
> +
> static inline int exportfs_encode_fid(struct inode *inode, struct fid *fid,
> int *max_len)
> {
> --
> 2.34.1
>
--
Jan Kara <[email protected]>
SUSE Labs, CR

2023-10-19 14:42:14

by Jan Kara

[permalink] [raw]
Subject: Re: [PATCH 4/5] exportfs: define FILEID_INO64_GEN* file handle types

On Wed 18-10-23 12:59:59, Amir Goldstein wrote:
> Similar to the common FILEID_INO32* file handle types, define common
> FILEID_INO64* file handle types.
>
> The type values of FILEID_INO64_GEN and FILEID_INO64_GEN_PARENT are the
> values returned by fuse and xfs for 64bit ino encoded file handle types.
>
> Note that these type value are filesystem specific and they do not define
> a universal file handle format, for example:
> fuse encodes FILEID_INO64_GEN as [ino-hi32,ino-lo32,gen] and xfs encodes
> FILEID_INO64_GEN as [hostr-order-ino64,gen] (a.k.a xfs_fid64).
>
> The FILEID_INO64_GEN fhandle type is going to be used for file ids for
> fanotify from filesystems that do not support NFS export.
>
> Signed-off-by: Amir Goldstein <[email protected]>

Yeah, better than the plain numbers. Feel free to add:

Reviewed-by: Jan Kara <[email protected]>

Honza

> ---
> fs/fuse/inode.c | 7 ++++---
> include/linux/exportfs.h | 11 +++++++++++
> 2 files changed, 15 insertions(+), 3 deletions(-)
>
> diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
> index 2e4eb7cf26fb..e63f966698a5 100644
> --- a/fs/fuse/inode.c
> +++ b/fs/fuse/inode.c
> @@ -1002,7 +1002,7 @@ static int fuse_encode_fh(struct inode *inode, u32 *fh, int *max_len,
> }
>
> *max_len = len;
> - return parent ? 0x82 : 0x81;
> + return parent ? FILEID_INO64_GEN_PARENT : FILEID_INO64_GEN;
> }
>
> static struct dentry *fuse_fh_to_dentry(struct super_block *sb,
> @@ -1010,7 +1010,8 @@ static struct dentry *fuse_fh_to_dentry(struct super_block *sb,
> {
> struct fuse_inode_handle handle;
>
> - if ((fh_type != 0x81 && fh_type != 0x82) || fh_len < 3)
> + if ((fh_type != FILEID_INO64_GEN &&
> + fh_type != FILEID_INO64_GEN_PARENT) || fh_len < 3)
> return NULL;
>
> handle.nodeid = (u64) fid->raw[0] << 32;
> @@ -1024,7 +1025,7 @@ static struct dentry *fuse_fh_to_parent(struct super_block *sb,
> {
> struct fuse_inode_handle parent;
>
> - if (fh_type != 0x82 || fh_len < 6)
> + if (fh_type != FILEID_INO64_GEN_PARENT || fh_len < 6)
> return NULL;
>
> parent.nodeid = (u64) fid->raw[3] << 32;
> diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h
> index 6b6e01321405..21eeb9f6bdbd 100644
> --- a/include/linux/exportfs.h
> +++ b/include/linux/exportfs.h
> @@ -98,6 +98,17 @@ enum fid_type {
> */
> FILEID_FAT_WITH_PARENT = 0x72,
>
> + /*
> + * 64 bit inode number, 32 bit generation number.
> + */
> + FILEID_INO64_GEN = 0x81,
> +
> + /*
> + * 64 bit inode number, 32 bit generation number,
> + * 64 bit parent inode number, 32 bit parent generation.
> + */
> + FILEID_INO64_GEN_PARENT = 0x82,
> +
> /*
> * 128 bit child FID (struct lu_fid)
> * 128 bit parent FID (struct lu_fid)
> --
> 2.34.1
>
--
Jan Kara <[email protected]>
SUSE Labs, CR

2023-10-23 13:56:01

by Amir Goldstein

[permalink] [raw]
Subject: Re: [PATCH 5/5] exportfs: support encoding non-decodeable file handles by default

On Wed, Oct 18, 2023 at 1:00 PM Amir Goldstein <[email protected]> wrote:
>
> AT_HANDLE_FID was added as an API for name_to_handle_at() that request
> the encoding of a file id, which is not intended to be decoded.
>
> This file id is used by fanotify to describe objects in events.
>
> So far, overlayfs is the only filesystem that supports encoding
> non-decodeable file ids, by providing export_operations with an
> ->encode_fh() method and without a ->decode_fh() method.
>
> Add support for encoding non-decodeable file ids to all the filesystems
> that do not provide export_operations, by encoding a file id of type
> FILEID_INO64_GEN from { i_ino, i_generation }.
>
> A filesystem may that does not support NFS export, can opt-out of
> encoding non-decodeable file ids for fanotify by defining an empty
> export_operations struct (i.e. with a NULL ->encode_fh() method).
>
> This allows the use of fanotify events with file ids on filesystems
> like 9p which do not support NFS export to bring fanotify in feature
> parity with inotify on those filesystems.
>
> Note that fanotify also requires that the filesystems report a non-null
> fsid. Currently, many simple filesystems that have support for inotify
> (e.g. debugfs, tracefs, sysfs) report a null fsid, so can still not be
> used with fanotify in file id reporting mode.
>
> Signed-off-by: Amir Goldstein <[email protected]>
> ---

Hi Jan,

Did you get a chance to look at this patch?
I saw your review comments on the rest of the series, so was waiting
for feedback on this last one before posting v2.

BTW, I am going to post a complementary patch to add fsid support for
the simple filesystems.

Thanks,
Amir.

> fs/exportfs/expfs.c | 30 +++++++++++++++++++++++++++---
> include/linux/exportfs.h | 10 +++++++---
> 2 files changed, 34 insertions(+), 6 deletions(-)
>
> diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
> index 30da4539e257..34e7d835d4ef 100644
> --- a/fs/exportfs/expfs.c
> +++ b/fs/exportfs/expfs.c
> @@ -383,6 +383,30 @@ int generic_encode_ino32_fh(struct inode *inode, __u32 *fh, int *max_len,
> }
> EXPORT_SYMBOL_GPL(generic_encode_ino32_fh);
>
> +/**
> + * exportfs_encode_ino64_fid - encode non-decodeable 64bit ino file id
> + * @inode: the object to encode
> + * @fid: where to store the file handle fragment
> + * @max_len: maximum length to store there
> + *
> + * This generic function is used to encode a non-decodeable file id for
> + * fanotify for filesystems that do not support NFS export.
> + */
> +static int exportfs_encode_ino64_fid(struct inode *inode, struct fid *fid,
> + int *max_len)
> +{
> + if (*max_len < 3) {
> + *max_len = 3;
> + return FILEID_INVALID;
> + }
> +
> + fid->i64.ino = inode->i_ino;
> + fid->i64.gen = inode->i_generation;
> + *max_len = 3;
> +
> + return FILEID_INO64_GEN;
> +}
> +
> /**
> * exportfs_encode_inode_fh - encode a file handle from inode
> * @inode: the object to encode
> @@ -401,10 +425,10 @@ int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid,
> if (!exportfs_can_encode_fh(nop, flags))
> return -EOPNOTSUPP;
>
> - if (nop && nop->encode_fh)
> - return nop->encode_fh(inode, fid->raw, max_len, parent);
> + if (!nop && (flags & EXPORT_FH_FID))
> + return exportfs_encode_ino64_fid(inode, fid, max_len);
>
> - return -EOPNOTSUPP;
> + return nop->encode_fh(inode, fid->raw, max_len, parent);
> }
> EXPORT_SYMBOL_GPL(exportfs_encode_inode_fh);
>
> diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h
> index 21eeb9f6bdbd..6688e457da64 100644
> --- a/include/linux/exportfs.h
> +++ b/include/linux/exportfs.h
> @@ -134,7 +134,11 @@ struct fid {
> u32 parent_ino;
> u32 parent_gen;
> } i32;
> - struct {
> + struct {
> + u64 ino;
> + u32 gen;
> + } __packed i64;
> + struct {
> u32 block;
> u16 partref;
> u16 parent_partref;
> @@ -246,7 +250,7 @@ extern int exportfs_encode_fh(struct dentry *dentry, struct fid *fid,
>
> static inline bool exportfs_can_encode_fid(const struct export_operations *nop)
> {
> - return nop && nop->encode_fh;
> + return !nop || nop->encode_fh;
> }
>
> static inline bool exportfs_can_decode_fh(const struct export_operations *nop)
> @@ -259,7 +263,7 @@ static inline bool exportfs_can_encode_fh(const struct export_operations *nop,
> {
> /*
> * If a non-decodeable file handle was requested, we only need to make
> - * sure that filesystem can encode file handles.
> + * sure that filesystem did not opt-out of encoding fid.
> */
> if (fh_flags & EXPORT_FH_FID)
> return exportfs_can_encode_fid(nop);
> --
> 2.34.1
>

2023-10-23 16:33:16

by Jan Kara

[permalink] [raw]
Subject: Re: [PATCH 5/5] exportfs: support encoding non-decodeable file handles by default

On Mon 23-10-23 16:55:40, Amir Goldstein wrote:
> On Wed, Oct 18, 2023 at 1:00 PM Amir Goldstein <[email protected]> wrote:
> >
> > AT_HANDLE_FID was added as an API for name_to_handle_at() that request
> > the encoding of a file id, which is not intended to be decoded.
> >
> > This file id is used by fanotify to describe objects in events.
> >
> > So far, overlayfs is the only filesystem that supports encoding
> > non-decodeable file ids, by providing export_operations with an
> > ->encode_fh() method and without a ->decode_fh() method.
> >
> > Add support for encoding non-decodeable file ids to all the filesystems
> > that do not provide export_operations, by encoding a file id of type
> > FILEID_INO64_GEN from { i_ino, i_generation }.
> >
> > A filesystem may that does not support NFS export, can opt-out of
> > encoding non-decodeable file ids for fanotify by defining an empty
> > export_operations struct (i.e. with a NULL ->encode_fh() method).
> >
> > This allows the use of fanotify events with file ids on filesystems
> > like 9p which do not support NFS export to bring fanotify in feature
> > parity with inotify on those filesystems.
> >
> > Note that fanotify also requires that the filesystems report a non-null
> > fsid. Currently, many simple filesystems that have support for inotify
> > (e.g. debugfs, tracefs, sysfs) report a null fsid, so can still not be
> > used with fanotify in file id reporting mode.
> >
> > Signed-off-by: Amir Goldstein <[email protected]>
> > ---
>
> Hi Jan,
>
> Did you get a chance to look at this patch?
> I saw your review comments on the rest of the series, so was waiting
> for feedback on this last one before posting v2.

Ah, sorry. I don't have any further comments regarding this patch besides
what Chuck already wrote.

Honza
--
Jan Kara <[email protected]>
SUSE Labs, CR

2023-10-23 16:44:31

by Amir Goldstein

[permalink] [raw]
Subject: Re: [PATCH 5/5] exportfs: support encoding non-decodeable file handles by default

On Mon, Oct 23, 2023 at 7:33 PM Jan Kara <[email protected]> wrote:
>
> On Mon 23-10-23 16:55:40, Amir Goldstein wrote:
> > On Wed, Oct 18, 2023 at 1:00 PM Amir Goldstein <[email protected]> wrote:
> > >
> > > AT_HANDLE_FID was added as an API for name_to_handle_at() that request
> > > the encoding of a file id, which is not intended to be decoded.
> > >
> > > This file id is used by fanotify to describe objects in events.
> > >
> > > So far, overlayfs is the only filesystem that supports encoding
> > > non-decodeable file ids, by providing export_operations with an
> > > ->encode_fh() method and without a ->decode_fh() method.
> > >
> > > Add support for encoding non-decodeable file ids to all the filesystems
> > > that do not provide export_operations, by encoding a file id of type
> > > FILEID_INO64_GEN from { i_ino, i_generation }.
> > >
> > > A filesystem may that does not support NFS export, can opt-out of
> > > encoding non-decodeable file ids for fanotify by defining an empty
> > > export_operations struct (i.e. with a NULL ->encode_fh() method).
> > >
> > > This allows the use of fanotify events with file ids on filesystems
> > > like 9p which do not support NFS export to bring fanotify in feature
> > > parity with inotify on those filesystems.
> > >
> > > Note that fanotify also requires that the filesystems report a non-null
> > > fsid. Currently, many simple filesystems that have support for inotify
> > > (e.g. debugfs, tracefs, sysfs) report a null fsid, so can still not be
> > > used with fanotify in file id reporting mode.
> > >
> > > Signed-off-by: Amir Goldstein <[email protected]>
> > > ---
> >
> > Hi Jan,
> >
> > Did you get a chance to look at this patch?
> > I saw your review comments on the rest of the series, so was waiting
> > for feedback on this last one before posting v2.
>
> Ah, sorry. I don't have any further comments regarding this patch besides
> what Chuck already wrote.

No worries.
I will post v2 with minor fixes and add your RVB to all patches.

Thanks,
Amir.