fanotify: introduce a generic info record copying helper
The copy_info_records_to_user() helper allows for the separation of info record copying routines/conditionals from copy_event_to_user(), which reduces the overall clutter within this function. This becomes especially true as we start introducing additional info records in the future i.e. struct fanotify_event_info_pidfd. On success, this helper returns the total amount of bytes that have been copied into the user supplied buffer and on error, a negative value is returned to the caller. The newly defined macro FANOTIFY_INFO_MODES can be used to obtain info record types that have been enabled for a specific notification group. This macro becomes useful in the subsequent patch when the FAN_REPORT_PIDFD initialization flag is introduced. Link: https://lore.kernel.org/r/8872947dfe12ce8ae6e9a7f2d49ea29bc8006af0.1628398044.git.repnop@google.com Signed-off-by: Matthew Bobrowski <repnop@google.com> Reviewed-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
Родитель
d3424c9bac
Коммит
0aca67bb7f
|
@ -173,7 +173,7 @@ static struct fanotify_event *get_one_event(struct fsnotify_group *group,
|
|||
size_t event_size = FAN_EVENT_METADATA_LEN;
|
||||
struct fanotify_event *event = NULL;
|
||||
struct fsnotify_event *fsn_event;
|
||||
unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
|
||||
unsigned int info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES);
|
||||
|
||||
pr_debug("%s: group=%p count=%zd\n", __func__, group, count);
|
||||
|
||||
|
@ -183,8 +183,8 @@ static struct fanotify_event *get_one_event(struct fsnotify_group *group,
|
|||
goto out;
|
||||
|
||||
event = FANOTIFY_E(fsn_event);
|
||||
if (fid_mode)
|
||||
event_size += fanotify_event_info_len(fid_mode, event);
|
||||
if (info_mode)
|
||||
event_size += fanotify_event_info_len(info_mode, event);
|
||||
|
||||
if (event_size > count) {
|
||||
event = ERR_PTR(-EINVAL);
|
||||
|
@ -401,6 +401,86 @@ static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
|
|||
return info_len;
|
||||
}
|
||||
|
||||
static int copy_info_records_to_user(struct fanotify_event *event,
|
||||
struct fanotify_info *info,
|
||||
unsigned int info_mode,
|
||||
char __user *buf, size_t count)
|
||||
{
|
||||
int ret, total_bytes = 0, info_type = 0;
|
||||
unsigned int fid_mode = info_mode & FANOTIFY_FID_BITS;
|
||||
|
||||
/*
|
||||
* Event info records order is as follows: dir fid + name, child fid.
|
||||
*/
|
||||
if (fanotify_event_dir_fh_len(event)) {
|
||||
info_type = info->name_len ? FAN_EVENT_INFO_TYPE_DFID_NAME :
|
||||
FAN_EVENT_INFO_TYPE_DFID;
|
||||
ret = copy_fid_info_to_user(fanotify_event_fsid(event),
|
||||
fanotify_info_dir_fh(info),
|
||||
info_type,
|
||||
fanotify_info_name(info),
|
||||
info->name_len, buf, count);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
buf += ret;
|
||||
count -= ret;
|
||||
total_bytes += ret;
|
||||
}
|
||||
|
||||
if (fanotify_event_object_fh_len(event)) {
|
||||
const char *dot = NULL;
|
||||
int dot_len = 0;
|
||||
|
||||
if (fid_mode == FAN_REPORT_FID || info_type) {
|
||||
/*
|
||||
* With only group flag FAN_REPORT_FID only type FID is
|
||||
* reported. Second info record type is always FID.
|
||||
*/
|
||||
info_type = FAN_EVENT_INFO_TYPE_FID;
|
||||
} else if ((fid_mode & FAN_REPORT_NAME) &&
|
||||
(event->mask & FAN_ONDIR)) {
|
||||
/*
|
||||
* With group flag FAN_REPORT_NAME, if name was not
|
||||
* recorded in an event on a directory, report the name
|
||||
* "." with info type DFID_NAME.
|
||||
*/
|
||||
info_type = FAN_EVENT_INFO_TYPE_DFID_NAME;
|
||||
dot = ".";
|
||||
dot_len = 1;
|
||||
} else if ((event->mask & ALL_FSNOTIFY_DIRENT_EVENTS) ||
|
||||
(event->mask & FAN_ONDIR)) {
|
||||
/*
|
||||
* With group flag FAN_REPORT_DIR_FID, a single info
|
||||
* record has type DFID for directory entry modification
|
||||
* event and for event on a directory.
|
||||
*/
|
||||
info_type = FAN_EVENT_INFO_TYPE_DFID;
|
||||
} else {
|
||||
/*
|
||||
* With group flags FAN_REPORT_DIR_FID|FAN_REPORT_FID,
|
||||
* a single info record has type FID for event on a
|
||||
* non-directory, when there is no directory to report.
|
||||
* For example, on FAN_DELETE_SELF event.
|
||||
*/
|
||||
info_type = FAN_EVENT_INFO_TYPE_FID;
|
||||
}
|
||||
|
||||
ret = copy_fid_info_to_user(fanotify_event_fsid(event),
|
||||
fanotify_event_object_fh(event),
|
||||
info_type, dot, dot_len,
|
||||
buf, count);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
buf += ret;
|
||||
count -= ret;
|
||||
total_bytes += ret;
|
||||
}
|
||||
|
||||
return total_bytes;
|
||||
}
|
||||
|
||||
static ssize_t copy_event_to_user(struct fsnotify_group *group,
|
||||
struct fanotify_event *event,
|
||||
char __user *buf, size_t count)
|
||||
|
@ -408,15 +488,14 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
|
|||
struct fanotify_event_metadata metadata;
|
||||
struct path *path = fanotify_event_path(event);
|
||||
struct fanotify_info *info = fanotify_event_info(event);
|
||||
unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
|
||||
unsigned int info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES);
|
||||
struct file *f = NULL;
|
||||
int ret, fd = FAN_NOFD;
|
||||
int info_type = 0;
|
||||
|
||||
pr_debug("%s: group=%p event=%p\n", __func__, group, event);
|
||||
|
||||
metadata.event_len = FAN_EVENT_METADATA_LEN +
|
||||
fanotify_event_info_len(fid_mode, event);
|
||||
fanotify_event_info_len(info_mode, event);
|
||||
metadata.metadata_len = FAN_EVENT_METADATA_LEN;
|
||||
metadata.vers = FANOTIFY_METADATA_VERSION;
|
||||
metadata.reserved = 0;
|
||||
|
@ -465,69 +544,11 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
|
|||
if (f)
|
||||
fd_install(fd, f);
|
||||
|
||||
/* Event info records order is: dir fid + name, child fid */
|
||||
if (fanotify_event_dir_fh_len(event)) {
|
||||
info_type = info->name_len ? FAN_EVENT_INFO_TYPE_DFID_NAME :
|
||||
FAN_EVENT_INFO_TYPE_DFID;
|
||||
ret = copy_fid_info_to_user(fanotify_event_fsid(event),
|
||||
fanotify_info_dir_fh(info),
|
||||
info_type,
|
||||
fanotify_info_name(info),
|
||||
info->name_len, buf, count);
|
||||
if (ret < 0)
|
||||
goto out_close_fd;
|
||||
|
||||
buf += ret;
|
||||
count -= ret;
|
||||
}
|
||||
|
||||
if (fanotify_event_object_fh_len(event)) {
|
||||
const char *dot = NULL;
|
||||
int dot_len = 0;
|
||||
|
||||
if (fid_mode == FAN_REPORT_FID || info_type) {
|
||||
/*
|
||||
* With only group flag FAN_REPORT_FID only type FID is
|
||||
* reported. Second info record type is always FID.
|
||||
*/
|
||||
info_type = FAN_EVENT_INFO_TYPE_FID;
|
||||
} else if ((fid_mode & FAN_REPORT_NAME) &&
|
||||
(event->mask & FAN_ONDIR)) {
|
||||
/*
|
||||
* With group flag FAN_REPORT_NAME, if name was not
|
||||
* recorded in an event on a directory, report the
|
||||
* name "." with info type DFID_NAME.
|
||||
*/
|
||||
info_type = FAN_EVENT_INFO_TYPE_DFID_NAME;
|
||||
dot = ".";
|
||||
dot_len = 1;
|
||||
} else if ((event->mask & ALL_FSNOTIFY_DIRENT_EVENTS) ||
|
||||
(event->mask & FAN_ONDIR)) {
|
||||
/*
|
||||
* With group flag FAN_REPORT_DIR_FID, a single info
|
||||
* record has type DFID for directory entry modification
|
||||
* event and for event on a directory.
|
||||
*/
|
||||
info_type = FAN_EVENT_INFO_TYPE_DFID;
|
||||
} else {
|
||||
/*
|
||||
* With group flags FAN_REPORT_DIR_FID|FAN_REPORT_FID,
|
||||
* a single info record has type FID for event on a
|
||||
* non-directory, when there is no directory to report.
|
||||
* For example, on FAN_DELETE_SELF event.
|
||||
*/
|
||||
info_type = FAN_EVENT_INFO_TYPE_FID;
|
||||
}
|
||||
|
||||
ret = copy_fid_info_to_user(fanotify_event_fsid(event),
|
||||
fanotify_event_object_fh(event),
|
||||
info_type, dot, dot_len,
|
||||
if (info_mode) {
|
||||
ret = copy_info_records_to_user(event, info, info_mode,
|
||||
buf, count);
|
||||
if (ret < 0)
|
||||
goto out_close_fd;
|
||||
|
||||
buf += ret;
|
||||
count -= ret;
|
||||
}
|
||||
|
||||
return metadata.event_len;
|
||||
|
|
|
@ -27,6 +27,8 @@ extern struct ctl_table fanotify_table[]; /* for sysctl */
|
|||
|
||||
#define FANOTIFY_FID_BITS (FAN_REPORT_FID | FAN_REPORT_DFID_NAME)
|
||||
|
||||
#define FANOTIFY_INFO_MODES (FANOTIFY_FID_BITS)
|
||||
|
||||
/*
|
||||
* fanotify_init() flags that require CAP_SYS_ADMIN.
|
||||
* We do not allow unprivileged groups to request permission events.
|
||||
|
|
Загрузка…
Ссылка в новой задаче