Skip to content

Commit

Permalink
fanotify: Introduce the FAN_ERROR mark
Browse files Browse the repository at this point in the history
The FAN_ERROR mark is used by filesystem wide monitoring tools to
receive notifications of type FS_ERROR_EVENT, emited by filesystems when
a problem is detected.  The error notification includes a generic error
descriptor, an optional location record and a filesystem specific blob.

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
  • Loading branch information
krisman-at-collabora authored and intel-lab-lkp committed Apr 26, 2021
1 parent 38aa5f5 commit 6179a61
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 10 deletions.
48 changes: 40 additions & 8 deletions fs/notify/fanotify/fanotify.c
Expand Up @@ -240,12 +240,14 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
__func__, iter_info->report_mask, event_mask, data, data_type);

if (!fid_mode) {
/* Do we have path to open a file descriptor? */
if (!path)
return 0;
/* Path type events are only relevant for files and dirs */
if (!d_is_reg(path->dentry) && !d_can_lookup(path->dentry))
return 0;
if (!fanotify_is_error_event(event_mask)) {
/* Do we have path to open a file descriptor? */
if (!path)
return 0;
/* Path type events are only relevant for files and dirs */
if (!d_is_reg(path->dentry) && !d_can_lookup(path->dentry))
return 0;
}
} else if (!(fid_mode & FAN_REPORT_FID)) {
/* Do we have a directory inode to report? */
if (!dir && !(event_mask & FS_ISDIR))
Expand Down Expand Up @@ -458,6 +460,25 @@ static struct fanotify_event *fanotify_alloc_perm_event(const struct path *path,
return &pevent->fae;
}

static void fanotify_init_error_event(struct fanotify_event *fae,
const struct fs_error_report *report,
__kernel_fsid_t *fsid)
{
struct fanotify_error_event *fee;

fae->type = FANOTIFY_EVENT_TYPE_ERROR;
fee = FANOTIFY_EE(fae);
fee->error = report->error;
fee->fsid = *fsid;

fee->loc.line = report->line;
fee->loc.function = report->function;

fee->fs_data_size = report->fs_data_size;

memcpy(&fee->fs_data, report->fs_data, report->fs_data_size);
}

static struct fanotify_event *fanotify_alloc_fid_event(struct inode *id,
__kernel_fsid_t *fsid,
gfp_t gfp)
Expand Down Expand Up @@ -618,6 +639,13 @@ static struct fanotify_event *fanotify_ring_get_slot(struct fsnotify_group *grou
{
size_t size = 0;

if (fanotify_is_error_event(mask)) {
const struct fs_error_report *report = data;
size = sizeof(struct fanotify_error_event) + report->fs_data_size;
} else {
return ERR_PTR(-EINVAL);
}

pr_debug("%s: group=%p mask=%x size=%lu\n", __func__, group, mask, size);

return FANOTIFY_E(fsnotify_ring_alloc_event_slot(group, size));
Expand All @@ -629,6 +657,9 @@ static void fanotify_ring_write_event(struct fsnotify_group *group,
{
fanotify_init_event(group, event, 0, mask);

if (fanotify_is_error_event(mask))
fanotify_init_error_event(event, data, fsid);

event->pid = get_pid(task_tgid(current));
}

Expand Down Expand Up @@ -695,8 +726,9 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
BUILD_BUG_ON(FAN_ONDIR != FS_ISDIR);
BUILD_BUG_ON(FAN_OPEN_EXEC != FS_OPEN_EXEC);
BUILD_BUG_ON(FAN_OPEN_EXEC_PERM != FS_OPEN_EXEC_PERM);
BUILD_BUG_ON(FAN_ERROR != FS_ERROR);

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, dir);
Expand All @@ -714,7 +746,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
return 0;
}

if (FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS)) {
if (FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS) || mask == FAN_ERROR) {
fsid = fanotify_get_fsid(iter_info);
/* Racing with mark destruction or creation? */
if (!fsid.val[0] && !fsid.val[1])
Expand Down
8 changes: 8 additions & 0 deletions fs/notify/fanotify/fanotify.h
Expand Up @@ -135,6 +135,7 @@ enum fanotify_event_type {
FANOTIFY_EVENT_TYPE_PATH,
FANOTIFY_EVENT_TYPE_PATH_PERM,
FANOTIFY_EVENT_TYPE_OVERFLOW, /* struct fanotify_event */
FANOTIFY_EVENT_TYPE_ERROR,
};

struct fanotify_event {
Expand Down Expand Up @@ -207,6 +208,8 @@ static inline __kernel_fsid_t *fanotify_event_fsid(struct fanotify_event *event)
return &FANOTIFY_FE(event)->fsid;
else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME)
return &FANOTIFY_NE(event)->fsid;
else if (event->type == FANOTIFY_EVENT_TYPE_ERROR)
return &FANOTIFY_EE(event)->fsid;
else
return NULL;
}
Expand Down Expand Up @@ -292,6 +295,11 @@ static inline struct fanotify_event *FANOTIFY_E(struct fsnotify_event *fse)
return container_of(fse, struct fanotify_event, fse);
}

static inline bool fanotify_is_error_event(u32 mask)
{
return mask & FANOTIFY_ERROR_EVENTS;
}

static inline bool fanotify_event_has_path(struct fanotify_event *event)
{
return event->type == FANOTIFY_EVENT_TYPE_PATH ||
Expand Down
63 changes: 63 additions & 0 deletions fs/notify/fanotify/fanotify_user.c
Expand Up @@ -96,6 +96,24 @@ static size_t fanotify_event_len(struct fanotify_event *event,
int fh_len;
int dot_len = 0;

if (fanotify_is_error_event(event->mask)) {
struct fanotify_error_event *fee = FANOTIFY_EE(event);
/*
* Error events (FAN_ERROR) have a different format
* as follows:
* [ event_metadata ]
* [ fs-generic error header ]
* [ error location (optional) ]
* [ fs-specific blob ]
*/
event_len = fanotify_error_info_len(fee);
if (fee->loc.function)
event_len += fanotify_location_info_len(&fee->loc);
if (fee->fs_data)
event_len += fanotify_error_fsdata_len(fee);
return event_len;
}

if (!fid_mode)
return event_len;

Expand Down Expand Up @@ -322,6 +340,38 @@ static ssize_t copy_error_fsdata_info_to_user(struct fanotify_error_event *fee,
return info.hdr.len;
}

static int copy_error_event_to_user(struct fanotify_event *event,
char __user *buf, int count)
{
struct fanotify_error_event *fee = FANOTIFY_EE(event);
ssize_t len;
int original_count = count;

len = copy_error_info_to_user(fee, buf, count);
if (len < 0)
return -EFAULT;
buf += len;
count -= len;

if (fee->loc.function) {
len = copy_location_info_to_user(&fee->loc, buf, count);
if (len < 0)
return len;
buf += len;
count -= len;
}

if (fee->fs_data_size) {
len = copy_error_fsdata_info_to_user(fee, buf, count);
if (len < 0)
return len;
buf += len;
count -= len;
}

return original_count - count;
}

static int copy_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
int info_type, const char *name, size_t name_len,
char __user *buf, size_t count)
Expand Down Expand Up @@ -528,6 +578,9 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
count -= ret;
}

if (fanotify_is_error_event(event->mask))
ret = copy_error_event_to_user(event, buf, count);

return metadata.event_len;

out_close_fd:
Expand Down Expand Up @@ -1328,6 +1381,10 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
(mask & ~FANOTIFY_SUBMISSION_BUFFER_EVENTS))
goto fput_and_out;

if (fanotify_is_error_event(mask) &&
!(group->flags & FSN_SUBMISSION_RING_BUFFER))
goto fput_and_out;

ret = fanotify_find_path(dfd, pathname, &path, flags,
(mask & ALL_FSNOTIFY_EVENTS), obj_type);
if (ret)
Expand All @@ -1350,6 +1407,12 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,

fsid = &__fsid;
}
if (mask & FAN_ERROR) {
ret = fanotify_check_path_fsid(&path, &__fsid);
if (ret)
goto path_put_and_out;
fsid = &__fsid;
}

/* inode held in place by reference to path; group by fget on fd */
if (mark_type == FAN_MARK_INODE)
Expand Down
9 changes: 7 additions & 2 deletions include/linux/fanotify.h
Expand Up @@ -56,9 +56,13 @@
#define FANOTIFY_INODE_EVENTS (FANOTIFY_DIRENT_EVENTS | \
FAN_ATTRIB | FAN_MOVE_SELF | FAN_DELETE_SELF)

#define FANOTIFY_ERROR_EVENTS (FAN_ERROR)

/* Events that user can request to be notified on */
#define FANOTIFY_EVENTS (FANOTIFY_PATH_EVENTS | \
FANOTIFY_INODE_EVENTS)
FANOTIFY_INODE_EVENTS | \
FANOTIFY_ERROR_EVENTS)


/* Events that require a permission response from user */
#define FANOTIFY_PERM_EVENTS (FAN_OPEN_PERM | FAN_ACCESS_PERM | \
Expand All @@ -70,9 +74,10 @@
/* Events that may be reported to user */
#define FANOTIFY_OUTGOING_EVENTS (FANOTIFY_EVENTS | \
FANOTIFY_PERM_EVENTS | \
FANOTIFY_ERROR_EVENTS | \
FAN_Q_OVERFLOW | FAN_ONDIR)

#define FANOTIFY_SUBMISSION_BUFFER_EVENTS 0
#define FANOTIFY_SUBMISSION_BUFFER_EVENTS FANOTIFY_ERROR_EVENTS

#define ALL_FANOTIFY_EVENT_BITS (FANOTIFY_OUTGOING_EVENTS | \
FANOTIFY_EVENT_FLAGS)
Expand Down
2 changes: 2 additions & 0 deletions include/uapi/linux/fanotify.h
Expand Up @@ -25,6 +25,8 @@
#define FAN_ACCESS_PERM 0x00020000 /* File accessed in perm check */
#define FAN_OPEN_EXEC_PERM 0x00040000 /* File open/exec in perm check */

#define FAN_ERROR 0x00100000 /* Filesystem error */

#define FAN_EVENT_ON_CHILD 0x08000000 /* Interested in child events */

#define FAN_ONDIR 0x40000000 /* Event occurred against dir */
Expand Down

0 comments on commit 6179a61

Please sign in to comment.