Skip to content

Commit

Permalink
FUSE, VFS: Add the fuse_fsnotify_update_mark inode operation
Browse files Browse the repository at this point in the history
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 <iangelak@redhat.com>
  • Loading branch information
iangelak committed Apr 29, 2022
1 parent c76a3b3 commit 834efb9
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 0 deletions.
31 changes: 31 additions & 0 deletions fs/fuse/dir.c
Expand Up @@ -20,6 +20,8 @@
#include <linux/security.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fsnotify_backend.h>
#include <linux/fanotify.h>

static void fuse_advise_use_readdirplus(struct inode *dir)
{
Expand Down Expand Up @@ -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,
Expand All @@ -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 = {
Expand All @@ -1929,13 +1958,15 @@ 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 = {
.setattr = fuse_setattr,
.get_link = fuse_get_link,
.getattr = fuse_getattr,
.listxattr = fuse_listxattr,
.fsnotify_update = fuse_fsnotify_update_mark,
};

void fuse_init_common(struct inode *inode)
Expand Down
1 change: 1 addition & 0 deletions include/linux/fs.h
Expand Up @@ -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,
Expand Down

0 comments on commit 834efb9

Please sign in to comment.