]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
fsnotify: add mount notification infrastructure
authorMiklos Szeredi <mszeredi@redhat.com>
Wed, 29 Jan 2025 16:57:59 +0000 (17:57 +0100)
committerChristian Brauner <brauner@kernel.org>
Tue, 4 Feb 2025 10:14:47 +0000 (11:14 +0100)
This is just the plumbing between the event source (fs/namespace.c) and the
event consumer (fanotify).  In itself it does nothing.

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Link: https://lore.kernel.org/r/20250129165803.72138-2-mszeredi@redhat.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/mount.h
fs/notify/fsnotify.c
fs/notify/fsnotify.h
fs/notify/mark.c
include/linux/fsnotify.h
include/linux/fsnotify_backend.h

index ffb613cdfeee97b99fe9419e8152c166e0a9ac83..82aa3bad7cf5ae9e32ab0660433fc61793b784cb 100644 (file)
@@ -21,6 +21,10 @@ struct mnt_namespace {
                struct rcu_head         mnt_ns_rcu;
        };
        u64 event;
+#ifdef CONFIG_FSNOTIFY
+       __u32                   n_fsnotify_mask;
+       struct fsnotify_mark_connector __rcu *n_fsnotify_marks;
+#endif
        unsigned int            nr_mounts; /* # of mounts in the namespace */
        unsigned int            pending_mounts;
        struct rb_node          mnt_ns_tree_node; /* node in the mnt_ns_tree */
index 8ee495a58d0adcc0e1381f885938820bdfecc9a8..c64b95cf50c71b99dd5d3feddaff2b389545a50b 100644 (file)
@@ -28,6 +28,11 @@ void __fsnotify_vfsmount_delete(struct vfsmount *mnt)
        fsnotify_clear_marks_by_mount(mnt);
 }
 
+void __fsnotify_mntns_delete(struct mnt_namespace *mntns)
+{
+       fsnotify_clear_marks_by_mntns(mntns);
+}
+
 /**
  * fsnotify_unmount_inodes - an sb is unmounting.  handle any watched inodes.
  * @sb: superblock being unmounted.
@@ -420,7 +425,7 @@ static int send_to_group(__u32 mask, const void *data, int data_type,
                                     file_name, cookie, iter_info);
 }
 
-static struct fsnotify_mark *fsnotify_first_mark(struct fsnotify_mark_connector **connp)
+static struct fsnotify_mark *fsnotify_first_mark(struct fsnotify_mark_connector *const *connp)
 {
        struct fsnotify_mark_connector *conn;
        struct hlist_node *node = NULL;
@@ -538,14 +543,15 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
 {
        const struct path *path = fsnotify_data_path(data, data_type);
        struct super_block *sb = fsnotify_data_sb(data, data_type);
-       struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(sb);
+       const struct fsnotify_mnt *mnt_data = fsnotify_data_mnt(data, data_type);
+       struct fsnotify_sb_info *sbinfo = sb ? fsnotify_sb_info(sb) : NULL;
        struct fsnotify_iter_info iter_info = {};
        struct mount *mnt = NULL;
        struct inode *inode2 = NULL;
        struct dentry *moved;
        int inode2_type;
        int ret = 0;
-       __u32 test_mask, marks_mask;
+       __u32 test_mask, marks_mask = 0;
 
        if (path)
                mnt = real_mount(path->mnt);
@@ -578,17 +584,20 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
        if ((!sbinfo || !sbinfo->sb_marks) &&
            (!mnt || !mnt->mnt_fsnotify_marks) &&
            (!inode || !inode->i_fsnotify_marks) &&
-           (!inode2 || !inode2->i_fsnotify_marks))
+           (!inode2 || !inode2->i_fsnotify_marks) &&
+           (!mnt_data || !mnt_data->ns->n_fsnotify_marks))
                return 0;
 
-       marks_mask = READ_ONCE(sb->s_fsnotify_mask);
+       if (sb)
+               marks_mask |= READ_ONCE(sb->s_fsnotify_mask);
        if (mnt)
                marks_mask |= READ_ONCE(mnt->mnt_fsnotify_mask);
        if (inode)
                marks_mask |= READ_ONCE(inode->i_fsnotify_mask);
        if (inode2)
                marks_mask |= READ_ONCE(inode2->i_fsnotify_mask);
-
+       if (mnt_data)
+               marks_mask |= READ_ONCE(mnt_data->ns->n_fsnotify_mask);
 
        /*
         * If this is a modify event we may need to clear some ignore masks.
@@ -618,6 +627,10 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
                iter_info.marks[inode2_type] =
                        fsnotify_first_mark(&inode2->i_fsnotify_marks);
        }
+       if (mnt_data) {
+               iter_info.marks[FSNOTIFY_ITER_TYPE_MNTNS] =
+                       fsnotify_first_mark(&mnt_data->ns->n_fsnotify_marks);
+       }
 
        /*
         * We need to merge inode/vfsmount/sb mark lists so that e.g. inode mark
@@ -702,11 +715,31 @@ void file_set_fsnotify_mode(struct file *file)
 }
 #endif
 
+void fsnotify_mnt(__u32 mask, struct mnt_namespace *ns, struct vfsmount *mnt)
+{
+       struct fsnotify_mnt data = {
+               .ns = ns,
+               .mnt_id = real_mount(mnt)->mnt_id_unique,
+       };
+
+       if (WARN_ON_ONCE(!ns))
+               return;
+
+       /*
+        * This is an optimization as well as making sure fsnotify_init() has
+        * been called.
+        */
+       if (!ns->n_fsnotify_marks)
+               return;
+
+       fsnotify(mask, &data, FSNOTIFY_EVENT_MNT, NULL, NULL, NULL, 0);
+}
+
 static __init int fsnotify_init(void)
 {
        int ret;
 
-       BUILD_BUG_ON(HWEIGHT32(ALL_FSNOTIFY_BITS) != 24);
+       BUILD_BUG_ON(HWEIGHT32(ALL_FSNOTIFY_BITS) != 26);
 
        ret = init_srcu_struct(&fsnotify_mark_srcu);
        if (ret)
index 663759ed6fbc1b87144471f53faef6ea55ad6f7a..5950c7a67f4147af0f2d758c096a60f9fa083971 100644 (file)
@@ -33,6 +33,12 @@ static inline struct super_block *fsnotify_conn_sb(
        return conn->obj;
 }
 
+static inline struct mnt_namespace *fsnotify_conn_mntns(
+                               struct fsnotify_mark_connector *conn)
+{
+       return conn->obj;
+}
+
 static inline struct super_block *fsnotify_object_sb(void *obj,
                        enum fsnotify_obj_type obj_type)
 {
@@ -89,6 +95,11 @@ static inline void fsnotify_clear_marks_by_sb(struct super_block *sb)
        fsnotify_destroy_marks(fsnotify_sb_marks(sb));
 }
 
+static inline void fsnotify_clear_marks_by_mntns(struct mnt_namespace *mntns)
+{
+       fsnotify_destroy_marks(&mntns->n_fsnotify_marks);
+}
+
 /*
  * update the dentry->d_flags of all of inode's children to indicate if inode cares
  * about events that happen to its children.
index 4981439e62092a0acb5d92461b7f4a905a611c5f..798340db69d761dd05c1b361c251818dee89b9cf 100644 (file)
@@ -107,6 +107,8 @@ static fsnotify_connp_t *fsnotify_object_connp(void *obj,
                return &real_mount(obj)->mnt_fsnotify_marks;
        case FSNOTIFY_OBJ_TYPE_SB:
                return fsnotify_sb_marks(obj);
+       case FSNOTIFY_OBJ_TYPE_MNTNS:
+               return &((struct mnt_namespace *)obj)->n_fsnotify_marks;
        default:
                return NULL;
        }
@@ -120,6 +122,8 @@ static __u32 *fsnotify_conn_mask_p(struct fsnotify_mark_connector *conn)
                return &fsnotify_conn_mount(conn)->mnt_fsnotify_mask;
        else if (conn->type == FSNOTIFY_OBJ_TYPE_SB)
                return &fsnotify_conn_sb(conn)->s_fsnotify_mask;
+       else if (conn->type == FSNOTIFY_OBJ_TYPE_MNTNS)
+               return &fsnotify_conn_mntns(conn)->n_fsnotify_mask;
        return NULL;
 }
 
@@ -346,12 +350,15 @@ static void *fsnotify_detach_connector_from_object(
                fsnotify_conn_mount(conn)->mnt_fsnotify_mask = 0;
        } else if (conn->type == FSNOTIFY_OBJ_TYPE_SB) {
                fsnotify_conn_sb(conn)->s_fsnotify_mask = 0;
+       } else if (conn->type == FSNOTIFY_OBJ_TYPE_MNTNS) {
+               fsnotify_conn_mntns(conn)->n_fsnotify_mask = 0;
        }
 
        rcu_assign_pointer(*connp, NULL);
        conn->obj = NULL;
        conn->type = FSNOTIFY_OBJ_TYPE_DETACHED;
-       fsnotify_update_sb_watchers(sb, conn);
+       if (sb)
+               fsnotify_update_sb_watchers(sb, conn);
 
        return inode;
 }
@@ -724,7 +731,7 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark, void *obj,
         * Attach the sb info before attaching a connector to any object on sb.
         * The sb info will remain attached as long as sb lives.
         */
-       if (!fsnotify_sb_info(sb)) {
+       if (sb && !fsnotify_sb_info(sb)) {
                err = fsnotify_attach_info_to_sb(sb);
                if (err)
                        return err;
@@ -770,7 +777,8 @@ restart:
        /* mark should be the last entry.  last is the current last entry */
        hlist_add_behind_rcu(&mark->obj_list, &last->obj_list);
 added:
-       fsnotify_update_sb_watchers(sb, conn);
+       if (sb)
+               fsnotify_update_sb_watchers(sb, conn);
        /*
         * Since connector is attached to object using cmpxchg() we are
         * guaranteed that connector initialization is fully visible by anyone
index 1a9ef8f6784ddfe61283b6925cfea90f25496561..589e274adc7d9c2404b641042081c35a0d3eaafd 100644 (file)
@@ -299,6 +299,11 @@ static inline void fsnotify_vfsmount_delete(struct vfsmount *mnt)
        __fsnotify_vfsmount_delete(mnt);
 }
 
+static inline void fsnotify_mntns_delete(struct mnt_namespace *mntns)
+{
+       __fsnotify_mntns_delete(mntns);
+}
+
 /*
  * fsnotify_inoderemove - an inode is going away
  */
@@ -507,4 +512,19 @@ static inline int fsnotify_sb_error(struct super_block *sb, struct inode *inode,
                        NULL, NULL, NULL, 0);
 }
 
+static inline void fsnotify_mnt_attach(struct mnt_namespace *ns, struct vfsmount *mnt)
+{
+       fsnotify_mnt(FS_MNT_ATTACH, ns, mnt);
+}
+
+static inline void fsnotify_mnt_detach(struct mnt_namespace *ns, struct vfsmount *mnt)
+{
+       fsnotify_mnt(FS_MNT_DETACH, ns, mnt);
+}
+
+static inline void fsnotify_mnt_move(struct mnt_namespace *ns, struct vfsmount *mnt)
+{
+       fsnotify_mnt(FS_MNT_MOVE, ns, mnt);
+}
+
 #endif /* _LINUX_FS_NOTIFY_H */
index 0d24a21a8e60a80a887ebe6baa9b5acfc546afcd..6cd8d1d28b8be4406356a2ad3db02bbb9d674680 100644 (file)
 
 #define FS_PRE_ACCESS          0x00100000      /* Pre-content access hook */
 
+#define FS_MNT_ATTACH          0x01000000      /* Mount was attached */
+#define FS_MNT_DETACH          0x02000000      /* Mount was detached */
+#define FS_MNT_MOVE            (FS_MNT_ATTACH | FS_MNT_DETACH)
+
 /*
  * Set on inode mark that cares about things that happen to its children.
  * Always set for dnotify and inotify.
@@ -80,6 +84,9 @@
  */
 #define ALL_FSNOTIFY_DIRENT_EVENTS (FS_CREATE | FS_DELETE | FS_MOVE | FS_RENAME)
 
+/* Mount namespace events */
+#define FSNOTIFY_MNT_EVENTS (FS_MNT_ATTACH | FS_MNT_DETACH)
+
 /* Content events can be used to inspect file content */
 #define FSNOTIFY_CONTENT_PERM_EVENTS (FS_OPEN_PERM | FS_OPEN_EXEC_PERM | \
                                      FS_ACCESS_PERM)
 
 /* Events that can be reported to backends */
 #define ALL_FSNOTIFY_EVENTS (ALL_FSNOTIFY_DIRENT_EVENTS | \
+                            FSNOTIFY_MNT_EVENTS | \
                             FS_EVENTS_POSS_ON_CHILD | \
                             FS_DELETE_SELF | FS_MOVE_SELF | \
                             FS_UNMOUNT | FS_Q_OVERFLOW | FS_IN_IGNORED | \
@@ -298,6 +306,7 @@ enum fsnotify_data_type {
        FSNOTIFY_EVENT_PATH,
        FSNOTIFY_EVENT_INODE,
        FSNOTIFY_EVENT_DENTRY,
+       FSNOTIFY_EVENT_MNT,
        FSNOTIFY_EVENT_ERROR,
 };
 
@@ -318,6 +327,11 @@ static inline const struct path *file_range_path(const struct file_range *range)
        return range->path;
 }
 
+struct fsnotify_mnt {
+       const struct mnt_namespace *ns;
+       u64 mnt_id;
+};
+
 static inline struct inode *fsnotify_data_inode(const void *data, int data_type)
 {
        switch (data_type) {
@@ -383,6 +397,24 @@ static inline struct super_block *fsnotify_data_sb(const void *data,
        }
 }
 
+static inline const struct fsnotify_mnt *fsnotify_data_mnt(const void *data,
+                                                          int data_type)
+{
+       switch (data_type) {
+       case FSNOTIFY_EVENT_MNT:
+               return data;
+       default:
+               return NULL;
+       }
+}
+
+static inline u64 fsnotify_data_mnt_id(const void *data, int data_type)
+{
+       const struct fsnotify_mnt *mnt_data = fsnotify_data_mnt(data, data_type);
+
+       return mnt_data ? mnt_data->mnt_id : 0;
+}
+
 static inline struct fs_error_report *fsnotify_data_error_report(
                                                        const void *data,
                                                        int data_type)
@@ -420,6 +452,7 @@ enum fsnotify_iter_type {
        FSNOTIFY_ITER_TYPE_SB,
        FSNOTIFY_ITER_TYPE_PARENT,
        FSNOTIFY_ITER_TYPE_INODE2,
+       FSNOTIFY_ITER_TYPE_MNTNS,
        FSNOTIFY_ITER_TYPE_COUNT
 };
 
@@ -429,6 +462,7 @@ enum fsnotify_obj_type {
        FSNOTIFY_OBJ_TYPE_INODE,
        FSNOTIFY_OBJ_TYPE_VFSMOUNT,
        FSNOTIFY_OBJ_TYPE_SB,
+       FSNOTIFY_OBJ_TYPE_MNTNS,
        FSNOTIFY_OBJ_TYPE_COUNT,
        FSNOTIFY_OBJ_TYPE_DETACHED = FSNOTIFY_OBJ_TYPE_COUNT
 };
@@ -613,8 +647,10 @@ extern int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data
 extern void __fsnotify_inode_delete(struct inode *inode);
 extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt);
 extern void fsnotify_sb_delete(struct super_block *sb);
+extern void __fsnotify_mntns_delete(struct mnt_namespace *mntns);
 extern void fsnotify_sb_free(struct super_block *sb);
 extern u32 fsnotify_get_cookie(void);
+extern void fsnotify_mnt(__u32 mask, struct mnt_namespace *ns, struct vfsmount *mnt);
 
 static inline __u32 fsnotify_parent_needed_mask(__u32 mask)
 {
@@ -928,6 +964,9 @@ static inline void __fsnotify_vfsmount_delete(struct vfsmount *mnt)
 static inline void fsnotify_sb_delete(struct super_block *sb)
 {}
 
+static inline void __fsnotify_mntns_delete(struct mnt_namespace *mntns)
+{}
+
 static inline void fsnotify_sb_free(struct super_block *sb)
 {}
 
@@ -942,6 +981,9 @@ static inline u32 fsnotify_get_cookie(void)
 static inline void fsnotify_unmount_inodes(struct super_block *sb)
 {}
 
+static inline void fsnotify_mnt(__u32 mask, struct mnt_namespace *ns, struct vfsmount *mnt)
+{}
+
 #endif /* CONFIG_FSNOTIFY */
 
 #endif /* __KERNEL __ */