From b52878275ce54b5d3a654ed24dfb169c1c501998 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 2 May 2023 15:48:14 +0300 Subject: exportfs: change connectable argument to bit flags Convert the bool connectable arguemnt into a bit flags argument and define the EXPORT_FS_CONNECTABLE flag as a requested property of the file handle. We are going to add a flag for requesting non-decodeable file handles. Acked-by: Jeff Layton Acked-by: Chuck Lever Signed-off-by: Amir Goldstein Signed-off-by: Jan Kara Message-Id: <20230502124817.3070545-2-amir73il@gmail.com> --- fs/exportfs/expfs.c | 13 +++++++++++-- fs/nfsd/nfsfh.c | 5 +++-- 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index ab88d33d106c..ab7feffe2d19 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -393,14 +393,23 @@ int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid, } EXPORT_SYMBOL_GPL(exportfs_encode_inode_fh); +/** + * exportfs_encode_fh - encode a file handle from dentry + * @dentry: the object to encode + * @fid: where to store the file handle fragment + * @max_len: maximum length to store there + * @flags: properties of the requested file handle + * + * Returns an enum fid_type or a negative errno. + */ int exportfs_encode_fh(struct dentry *dentry, struct fid *fid, int *max_len, - int connectable) + int flags) { int error; struct dentry *p = NULL; struct inode *inode = dentry->d_inode, *parent = NULL; - if (connectable && !S_ISDIR(inode->i_mode)) { + if ((flags & EXPORT_FH_CONNECTABLE) && !S_ISDIR(inode->i_mode)) { p = dget_parent(dentry); /* * note that while p might've ceased to be our parent already, diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index ccd8485fee04..31e4505c0df3 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -414,10 +414,11 @@ static void _fh_update(struct svc_fh *fhp, struct svc_export *exp, struct fid *fid = (struct fid *) (fhp->fh_handle.fh_fsid + fhp->fh_handle.fh_size/4 - 1); int maxsize = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4; - int subtreecheck = !(exp->ex_flags & NFSEXP_NOSUBTREECHECK); + int fh_flags = (exp->ex_flags & NFSEXP_NOSUBTREECHECK) ? 0 : + EXPORT_FH_CONNECTABLE; fhp->fh_handle.fh_fileid_type = - exportfs_encode_fh(dentry, fid, &maxsize, subtreecheck); + exportfs_encode_fh(dentry, fid, &maxsize, fh_flags); fhp->fh_handle.fh_size += maxsize * 4; } else { fhp->fh_handle.fh_fileid_type = FILEID_ROOT; -- cgit From 304e9c83e80d5cbe20ab64ffa1fac9fc51d30bc9 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 2 May 2023 15:48:15 +0300 Subject: exportfs: add explicit flag to request non-decodeable file handles So far, all callers of exportfs_encode_inode_fh(), except for fsnotify's show_mark_fhandle(), check that filesystem can decode file handles, but we would like to add more callers that do not require a file handle that can be decoded. Introduce a flag to explicitly request a file handle that may not to be decoded later and a wrapper exportfs_encode_fid() that sets this flag and convert show_mark_fhandle() to use the new wrapper. This will be used to allow adding fanotify support to filesystems that do not support NFS export. Acked-by: Jeff Layton Acked-by: Chuck Lever Signed-off-by: Amir Goldstein Signed-off-by: Jan Kara Message-Id: <20230502124817.3070545-3-amir73il@gmail.com> --- fs/exportfs/expfs.c | 20 ++++++++++++++++++-- fs/notify/fanotify/fanotify.c | 4 ++-- fs/notify/fdinfo.c | 2 +- 3 files changed, 21 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index ab7feffe2d19..40e624cf7e92 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -381,11 +381,27 @@ static int export_encode_fh(struct inode *inode, struct fid *fid, return type; } +/** + * exportfs_encode_inode_fh - encode a file handle from inode + * @inode: the object to encode + * @fid: where to store the file handle fragment + * @max_len: maximum length to store there + * @flags: properties of the requested file handle + * + * Returns an enum fid_type or a negative errno. + */ int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid, - int *max_len, struct inode *parent) + int *max_len, struct inode *parent, int flags) { 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) + return -EOPNOTSUPP; + if (nop && nop->encode_fh) return nop->encode_fh(inode, fid->raw, max_len, parent); @@ -418,7 +434,7 @@ int exportfs_encode_fh(struct dentry *dentry, struct fid *fid, int *max_len, parent = p->d_inode; } - error = exportfs_encode_inode_fh(inode, fid, max_len, parent); + error = exportfs_encode_inode_fh(inode, fid, max_len, parent, flags); dput(p); return error; diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 29bdd99b29fa..d1a49f5b6e6d 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -380,7 +380,7 @@ static int fanotify_encode_fh_len(struct inode *inode) if (!inode) return 0; - exportfs_encode_inode_fh(inode, NULL, &dwords, NULL); + exportfs_encode_inode_fh(inode, NULL, &dwords, NULL, 0); fh_len = dwords << 2; /* @@ -443,7 +443,7 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode, } dwords = fh_len >> 2; - type = exportfs_encode_inode_fh(inode, buf, &dwords, NULL); + type = exportfs_encode_inode_fh(inode, buf, &dwords, NULL, 0); err = -EINVAL; if (!type || type == FILEID_INVALID || fh_len != dwords << 2) goto out_err; diff --git a/fs/notify/fdinfo.c b/fs/notify/fdinfo.c index 55081ae3a6ec..5c430736ec12 100644 --- a/fs/notify/fdinfo.c +++ b/fs/notify/fdinfo.c @@ -50,7 +50,7 @@ static void show_mark_fhandle(struct seq_file *m, struct inode *inode) f.handle.handle_bytes = sizeof(f.pad); size = f.handle.handle_bytes >> 2; - ret = exportfs_encode_inode_fh(inode, (struct fid *)f.handle.f_handle, &size, NULL); + ret = exportfs_encode_fid(inode, (struct fid *)f.handle.f_handle, &size); if ((ret == FILEID_INVALID) || (ret < 0)) { WARN_ONCE(1, "Can't encode file handler for inotify: %d\n", ret); return; -- cgit From 96b2b072ee62be8ae68c8ecf14854c4d0505a8f8 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 2 May 2023 15:48:16 +0300 Subject: exportfs: allow exporting non-decodeable file handles to userspace Some userspace programs use st_ino as a unique object identifier, even though inode numbers may be recycable. This issue has been addressed for NFS export long ago using the exportfs file handle API and the unique file handle identifiers are also exported to userspace via name_to_handle_at(2). fanotify also uses file handles to identify objects in events, but only for filesystems that support NFS export. Relax the requirement for NFS export support and allow more filesystems to export a unique object identifier via name_to_handle_at(2) with the flag AT_HANDLE_FID. A file handle requested with the AT_HANDLE_FID flag, may or may not be usable as an argument to open_by_handle_at(2). To allow filesystems to opt-in to supporting AT_HANDLE_FID, a struct export_operations is required, but even an empty struct is sufficient for encoding FIDs. Acked-by: Jeff Layton Acked-by: Chuck Lever Signed-off-by: Amir Goldstein Acked-by: Christian Brauner Signed-off-by: Jan Kara Message-Id: <20230502124817.3070545-4-amir73il@gmail.com> --- fs/fhandle.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/fhandle.c b/fs/fhandle.c index f2bc27d1975e..4a635cf787fc 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c @@ -16,7 +16,7 @@ static long do_sys_name_to_handle(const struct path *path, struct file_handle __user *ufh, - int __user *mnt_id) + int __user *mnt_id, int fh_flags) { long retval; struct file_handle f_handle; @@ -24,11 +24,14 @@ static long do_sys_name_to_handle(const struct path *path, struct file_handle *handle = NULL; /* - * We need to make sure whether the file system - * support decoding of the file handle + * 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 || - !path->dentry->d_sb->s_export_op->fh_to_dentry) + (!(fh_flags & EXPORT_FH_FID) && + !path->dentry->d_sb->s_export_op->fh_to_dentry)) return -EOPNOTSUPP; if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) @@ -45,10 +48,10 @@ static long do_sys_name_to_handle(const struct path *path, /* convert handle size to multiple of sizeof(u32) */ handle_dwords = f_handle.handle_bytes >> 2; - /* we ask for a non connected handle */ + /* we ask for a non connectable maybe decodeable file handle */ retval = exportfs_encode_fh(path->dentry, (struct fid *)handle->f_handle, - &handle_dwords, 0); + &handle_dwords, fh_flags); handle->handle_type = retval; /* convert handle size to bytes */ handle_bytes = handle_dwords * sizeof(u32); @@ -84,6 +87,7 @@ static long do_sys_name_to_handle(const struct path *path, * @handle: resulting file handle * @mnt_id: mount id of the file system containing the file * @flag: flag value to indicate whether to follow symlink or not + * and whether a decodable file handle is required. * * @handle->handle_size indicate the space available to store the * variable part of the file handle in bytes. If there is not @@ -96,17 +100,19 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name, { struct path path; int lookup_flags; + int fh_flags; int err; - if ((flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0) + if (flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH | AT_HANDLE_FID)) return -EINVAL; lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0; + fh_flags = (flag & AT_HANDLE_FID) ? EXPORT_FH_FID : 0; if (flag & AT_EMPTY_PATH) lookup_flags |= LOOKUP_EMPTY; err = user_path_at(dfd, name, lookup_flags, &path); if (!err) { - err = do_sys_name_to_handle(&path, handle, mnt_id); + err = do_sys_name_to_handle(&path, handle, mnt_id, fh_flags); path_put(&path); } return err; -- cgit From a95aef69a740f5a1c7d70f2b58552207edaef99a Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 2 May 2023 15:48:17 +0300 Subject: fanotify: support reporting non-decodeable file handles fanotify users do not always need to decode the file handles reported with FAN_REPORT_FID. Relax the restriction that filesystem needs to support NFS export and allow reporting file handles from filesystems that only support ecoding unique file handles. Even filesystems that do not have export_operations at all can fallback to use the default FILEID_INO32_GEN encoding, but we use the existence of export_operations as an indication that the encoded file handles will be sufficiently unique and that user will be able to compare them to filesystem objects using AT_HANDLE_FID flag to name_to_handle_at(2). For filesystems that do not support NFS export, users will have to use the AT_HANDLE_FID of name_to_handle_at(2) if they want to compare the object in path to the object fid reported in an event. Signed-off-by: Amir Goldstein Signed-off-by: Jan Kara Message-Id: <20230502124817.3070545-5-amir73il@gmail.com> --- fs/notify/fanotify/fanotify.c | 4 ++-- fs/notify/fanotify/fanotify_user.c | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index d1a49f5b6e6d..d2bbf1445a9e 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -380,7 +380,7 @@ static int fanotify_encode_fh_len(struct inode *inode) if (!inode) return 0; - exportfs_encode_inode_fh(inode, NULL, &dwords, NULL, 0); + exportfs_encode_fid(inode, NULL, &dwords); fh_len = dwords << 2; /* @@ -443,7 +443,7 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode, } dwords = fh_len >> 2; - type = exportfs_encode_inode_fh(inode, buf, &dwords, NULL, 0); + type = exportfs_encode_fid(inode, buf, &dwords); err = -EINVAL; if (!type || type == FILEID_INVALID || fh_len != dwords << 2) goto out_err; diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 22fb1cf7e1fc..95d7d8790bc3 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -1591,11 +1591,10 @@ static int fanotify_test_fid(struct dentry *dentry) * 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, name_to_handle_at() requires that the - * filesystem also supports decoding file handles. + * objects. However, even the relaxed AT_HANDLE_FID flag requires + * at least empty export_operations for ecoding unique file ids. */ - if (!dentry->d_sb->s_export_op || - !dentry->d_sb->s_export_op->fh_to_dentry) + if (!dentry->d_sb->s_export_op) return -EOPNOTSUPP; return 0; -- cgit From 7cdafe6cc4a6ee94c56a5c96d6edd80d066d5a3b Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Wed, 24 May 2023 18:48:25 +0300 Subject: exportfs: check for error return value from exportfs_encode_*() The exportfs_encode_*() helpers call the filesystem ->encode_fh() method which returns a signed int. All the in-tree implementations of ->encode_fh() return a positive integer and FILEID_INVALID (255) for error. Fortify the callers for possible future ->encode_fh() implementation that will return a negative error value. name_to_handle_at() would propagate the returned error to the users if filesystem ->encode_fh() method returns an error. Reported-by: Dan Carpenter Link: https://lore.kernel.org/linux-fsdevel/ca02955f-1877-4fde-b453-3c1d22794740@kili.mountain/ Signed-off-by: Amir Goldstein Reviewed-by: Jeff Layton Signed-off-by: Jan Kara Message-Id: <20230524154825.881414-1-amir73il@gmail.com> --- fs/fhandle.c | 5 +++-- fs/nfsd/nfsfh.c | 4 +++- fs/notify/fanotify/fanotify.c | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/fhandle.c b/fs/fhandle.c index 4a635cf787fc..fd0d6a3b3699 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c @@ -57,18 +57,19 @@ static long do_sys_name_to_handle(const struct path *path, handle_bytes = handle_dwords * sizeof(u32); handle->handle_bytes = handle_bytes; if ((handle->handle_bytes > f_handle.handle_bytes) || - (retval == FILEID_INVALID) || (retval == -ENOSPC)) { + (retval == FILEID_INVALID) || (retval < 0)) { /* As per old exportfs_encode_fh documentation * we could return ENOSPC to indicate overflow * But file system returned 255 always. So handle * both the values */ + if (retval == FILEID_INVALID || retval == -ENOSPC) + retval = -EOVERFLOW; /* * set the handle size to zero so we copy only * non variable part of the file_handle */ handle_bytes = 0; - retval = -EOVERFLOW; } else retval = 0; /* copy the mount id */ diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 31e4505c0df3..0f5eacae5f43 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -416,9 +416,11 @@ static void _fh_update(struct svc_fh *fhp, struct svc_export *exp, int maxsize = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4; int fh_flags = (exp->ex_flags & NFSEXP_NOSUBTREECHECK) ? 0 : EXPORT_FH_CONNECTABLE; + int fileid_type = + exportfs_encode_fh(dentry, fid, &maxsize, fh_flags); fhp->fh_handle.fh_fileid_type = - exportfs_encode_fh(dentry, fid, &maxsize, fh_flags); + fileid_type > 0 ? fileid_type : FILEID_INVALID; fhp->fh_handle.fh_size += maxsize * 4; } else { fhp->fh_handle.fh_fileid_type = FILEID_ROOT; diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index d2bbf1445a9e..9dac7f6e72d2 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -445,7 +445,7 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode, dwords = fh_len >> 2; type = exportfs_encode_fid(inode, buf, &dwords); err = -EINVAL; - if (!type || type == FILEID_INVALID || fh_len != dwords << 2) + if (type <= 0 || type == FILEID_INVALID || fh_len != dwords << 2) goto out_err; fh->type = type; -- cgit From 7b8c9d7bb4570ee4800642009c8f2d9756004552 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sun, 11 Jun 2023 15:24:29 +0300 Subject: fsnotify: move fsnotify_open() hook into do_dentry_open() fsnotify_open() hook is called only from high level system calls context and not called for the very many helpers to open files. This may makes sense for many of the special file open cases, but it is inconsistent with fsnotify_close() hook that is called for every last fput() of on a file object with FMODE_OPENED. As a result, it is possible to observe ACCESS, MODIFY and CLOSE events without ever observing an OPEN event. Fix this inconsistency by replacing all the fsnotify_open() hooks with a single hook inside do_dentry_open(). If there are special cases that would like to opt-out of the possible overhead of fsnotify() call in fsnotify_open(), they would probably also want to avoid the overhead of fsnotify() call in the rest of the fsnotify hooks, so they should be opening that file with the __FMODE_NONOTIFY flag. However, in the majority of those cases, the s_fsnotify_connectors optimization in fsnotify_parent() would be sufficient to avoid the overhead of fsnotify() call anyway. Signed-off-by: Amir Goldstein Signed-off-by: Jan Kara Message-Id: <20230611122429.1499617-1-amir73il@gmail.com> --- fs/exec.c | 5 ----- fs/fhandle.c | 1 - fs/open.c | 6 +++++- 3 files changed, 5 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/exec.c b/fs/exec.c index a466e797c8e2..238473de1ec5 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -152,8 +152,6 @@ SYSCALL_DEFINE1(uselib, const char __user *, library) path_noexec(&file->f_path))) goto exit; - fsnotify_open(file); - error = -ENOEXEC; read_lock(&binfmt_lock); @@ -934,9 +932,6 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags) if (err) goto exit; - if (name->name[0] != '\0') - fsnotify_open(file); - out: return file; diff --git a/fs/fhandle.c b/fs/fhandle.c index fd0d6a3b3699..6ea8d35a9382 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c @@ -242,7 +242,6 @@ static long do_handle_open(int mountdirfd, struct file_handle __user *ufh, retval = PTR_ERR(file); } else { retval = fd; - fsnotify_open(file); fd_install(fd, file); } path_put(&path); diff --git a/fs/open.c b/fs/open.c index 4478adcc4f3a..005ca91a173b 100644 --- a/fs/open.c +++ b/fs/open.c @@ -969,6 +969,11 @@ static int do_dentry_open(struct file *f, } } + /* + * Once we return a file with FMODE_OPENED, __fput() will call + * fsnotify_close(), so we need fsnotify_open() here for symmetry. + */ + fsnotify_open(f); return 0; cleanup_all: @@ -1358,7 +1363,6 @@ static long do_sys_openat2(int dfd, const char __user *filename, put_unused_fd(fd); fd = PTR_ERR(f); } else { - fsnotify_open(f); fd_install(fd, f); } } -- cgit