Thread (13 messages) 13 messages, 5 authors, 2020-04-20

Re: [PATCH v2 13/16] fanotify: report name info for FAN_DIR_MODIFY event

From: Amir Goldstein <amir73il@gmail.com>
Date: 2020-02-19 11:23:02
Also in: linux-fsdevel

On Mon, Feb 17, 2020 at 3:15 PM Amir Goldstein [off-list ref] wrote:
quoted hunk ↗ jump to hunk
Report event FAN_DIR_MODIFY with name in a variable length record similar
to how fid's are reported.  With name info reporting implemented, setting
FAN_DIR_MODIFY in mark mask is now allowed.

When events are reported with name, the reported fid identifies the
directory and the name follows the fid. The info record type for this
event info is FAN_EVENT_INFO_TYPE_DFID_NAME.

For now, all reported events have at most one info record which is
either FAN_EVENT_INFO_TYPE_FID or FAN_EVENT_INFO_TYPE_DFID_NAME (for
FAN_DIR_MODIFY).  Later on, events "on child" will report both records.

There are several ways that an application can use this information:

1. When watching a single directory, the name is always relative to
the watched directory, so application need to fstatat(2) the name
relative to the watched directory.

2. When watching a set of directories, the application could keep a map
of dirfd for all watched directories and hash the map by fid obtained
with name_to_handle_at(2).  When getting a name event, the fid in the
event info could be used to lookup the base dirfd in the map and then
call fstatat(2) with that dirfd.

3. When watching a filesystem (FAN_MARK_FILESYSTEM) or a large set of
directories, the application could use open_by_handle_at(2) with the fid
in event info to obtain dirfd for the directory where event happened and
call fstatat(2) with this dirfd.

The last option scales better for a large number of watched directories.
The first two options may be available in the future also for non
privileged fanotify watchers, because open_by_handle_at(2) requires
the CAP_DAC_READ_SEARCH capability.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
 fs/notify/fanotify/fanotify.c      |   2 +-
 fs/notify/fanotify/fanotify_user.c | 120 ++++++++++++++++++++++-------
 include/linux/fanotify.h           |   3 +-
 include/uapi/linux/fanotify.h      |   1 +
 4 files changed, 98 insertions(+), 28 deletions(-)
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index fc75dc53a218..b651c18d3a93 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -478,7 +478,7 @@ static int fanotify_handle_event(struct fsnotify_group *group,
        BUILD_BUG_ON(FAN_OPEN_EXEC != FS_OPEN_EXEC);
        BUILD_BUG_ON(FAN_OPEN_EXEC_PERM != FS_OPEN_EXEC_PERM);

-       BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 19);
+       BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 20);

        mask = fanotify_group_event_mask(group, iter_info, mask, data,
                                         data_type);
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 284f3548bb79..a1bafc21ebbb 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -51,20 +51,32 @@ struct kmem_cache *fanotify_name_event_cachep __read_mostly;
 struct kmem_cache *fanotify_perm_event_cachep __read_mostly;

 #define FANOTIFY_EVENT_ALIGN 4
+#define FANOTIFY_INFO_HDR_LEN \
+       (sizeof(struct fanotify_event_info_fid) + sizeof(struct file_handle))

-static int fanotify_fid_info_len(struct fanotify_fid_hdr *fh)
+static int fanotify_fid_info_len(int fh_len, int name_len)
 {
-       return roundup(sizeof(struct fanotify_event_info_fid) +
-                      sizeof(struct file_handle) + fh->len,
-                      FANOTIFY_EVENT_ALIGN);
+       int info_len = fh_len;
+
+       if (name_len)
+               info_len += name_len + 1;
+
+       return roundup(FANOTIFY_INFO_HDR_LEN + info_len, FANOTIFY_EVENT_ALIGN);
 }

 static int fanotify_event_info_len(struct fanotify_event *event)
 {
-       if (!fanotify_event_has_fid(event))
-               return 0;
+       int info_len = 0;
+
+       if (fanotify_event_has_fid(event))
+               info_len += fanotify_fid_info_len(event->fh.len, 0);
+
+       if (fanotify_event_has_dfid_name(event)) {
+               info_len += fanotify_fid_info_len(event->dfh.len,
+                                       fanotify_event_name_len(event));
+       }

-       return fanotify_fid_info_len(&event->fh);
+       return info_len;
 }

 /*
@@ -210,23 +222,34 @@ static int process_access_response(struct fsnotify_group *group,
        return -ENOENT;
 }

-static int copy_fid_to_user(__kernel_fsid_t *fsid, struct fanotify_fid_hdr *fh,
-                           struct fanotify_fid *fid, char __user *buf)
+static int copy_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fid_hdr *fh,
+                            struct fanotify_fid *fid, const struct qstr *name,
+                            char __user *buf, size_t count)
 {
        struct fanotify_event_info_fid info = { };
        struct file_handle handle = { };
-       unsigned char bounce[FANOTIFY_INLINE_FH_LEN], *data;
+       unsigned char bounce[max(FANOTIFY_INLINE_FH_LEN, DNAME_INLINE_LEN)];
+       const unsigned char *data;
        size_t fh_len = fh->len;
-       size_t len = fanotify_fid_info_len(fh);
+       size_t name_len = name ? name->len : 0;
+       size_t info_len = fanotify_fid_info_len(fh_len, name_len);
+       size_t len = info_len;
+
+       pr_debug("%s: fh_len=%lu name_len=%lu, info_len=%lu, count=%lu\n",
+                __func__, fh_len, name_len, info_len, count);
Changed all %lu above to %zu to print size_t without a warning.

Thanks kbuild test robot,
Amir.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help