From 834efb94978e62da824c5f4cde878fe592c2426d Mon Sep 17 00:00:00 2001 From: Ioannis Angelakopoulos Date: Mon, 4 Apr 2022 17:37:46 -0400 Subject: [PATCH] FUSE, VFS: Add the fuse_fsnotify_update_mark inode operation Every time a local watch is placed/modified/removed on/from an inode the same operation has to take place in the FUSE server. Thus add the inode operation "fuse_fsnotify_update_mark", which is specific to FUSE inodes. This operation is called from the "inotify_add_watch" system call in the inotify subsystem. Specifically, the operation is called when a process tries to add, modify or remove a watch from a FUSE inode and the remote fsnotify support is enabled both in the guest kernel and the FUSE server (virtiofsd). Essentially, when the kernel adds/modifies a watch locally, also send a fsnotify request to the FUSE server to do the same. We keep the local watch placement since it is essential for the functionality of the fsnotify notification subsystem. However, the local events generated by the guest kernel will be suppressed if they affect FUSE inodes and the remote fsnotify support is enabled. Signed-off-by: Ioannis Angelakopoulos --- fs/fuse/dir.c | 31 +++++++++++++++++++++++++++++++ include/linux/fs.h | 1 + 2 files changed, 32 insertions(+) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 9ff27b8a9782c4..2471fcce92fd9b 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include static void fuse_advise_use_readdirplus(struct inode *dir) { @@ -1888,6 +1890,32 @@ static int fuse_getattr(struct user_namespace *mnt_userns, return fuse_update_get_attr(inode, NULL, stat, request_mask, flags); } +static int fuse_fsnotify_update_mark(struct inode *inode) +{ + uint64_t mask; + /* + * We have to remove the bits added to the mask before being attached + * or detached to the inode, since these bits are going to be + * added by the "remote" host kernel. If these bits were still enabled + * in the mask that was sent to the "remote" kernel then the watch would + * be rejected as an unsupported value. These bits are added by the + * fsnotify subsystem thus we use the corresponding fsnotify bits here. + */ + mask = inode->i_fsnotify_mask & ~(FS_IN_IGNORED | FS_UNMOUNT | + FS_IN_ONESHOT | FS_EXCL_UNLINK); + + if (!inode) + return -EINVAL; + + if (mask && !(mask & ALL_FSNOTIFY_EVENTS & ~(ALL_FSNOTIFY_DIRENT_EVENTS | + FS_UNMOUNT, FS_IN_IGNORED, + FS_ERROR))) + return -EINVAL; + + /* Send the inode and the aggregated mask for the inode*/ + return fuse_fsnotify_send_request(inode, mask); +} + static const struct inode_operations fuse_dir_inode_operations = { .lookup = fuse_lookup, .mkdir = fuse_mkdir, @@ -1907,6 +1935,7 @@ static const struct inode_operations fuse_dir_inode_operations = { .set_acl = fuse_set_acl, .fileattr_get = fuse_fileattr_get, .fileattr_set = fuse_fileattr_set, + .fsnotify_update = fuse_fsnotify_update_mark, }; static const struct file_operations fuse_dir_operations = { @@ -1929,6 +1958,7 @@ static const struct inode_operations fuse_common_inode_operations = { .set_acl = fuse_set_acl, .fileattr_get = fuse_fileattr_get, .fileattr_set = fuse_fileattr_set, + .fsnotify_update = fuse_fsnotify_update_mark, }; static const struct inode_operations fuse_symlink_inode_operations = { @@ -1936,6 +1966,7 @@ static const struct inode_operations fuse_symlink_inode_operations = { .get_link = fuse_get_link, .getattr = fuse_getattr, .listxattr = fuse_listxattr, + .fsnotify_update = fuse_fsnotify_update_mark, }; void fuse_init_common(struct inode *inode) diff --git a/include/linux/fs.h b/include/linux/fs.h index bbde95387a23af..176cdac7b7e702 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2036,6 +2036,7 @@ struct inode_operations { int (*fileattr_set)(struct user_namespace *mnt_userns, struct dentry *dentry, struct fileattr *fa); int (*fileattr_get)(struct dentry *dentry, struct fileattr *fa); + int (*fsnotify_update)(struct inode *inode); } ____cacheline_aligned; static inline ssize_t call_read_iter(struct file *file, struct kiocb *kio,