146 lines
4.0 KiB
C
146 lines
4.0 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#include <linux/fsnotify_backend.h>
|
|
#include <linux/path.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/exportfs.h>
|
|
|
|
extern struct kmem_cache *fanotify_mark_cache;
|
|
extern struct kmem_cache *fanotify_event_cachep;
|
|
extern struct kmem_cache *fanotify_perm_event_cachep;
|
|
|
|
/* Possible states of the permission event */
|
|
enum {
|
|
FAN_EVENT_INIT,
|
|
FAN_EVENT_REPORTED,
|
|
FAN_EVENT_ANSWERED,
|
|
FAN_EVENT_CANCELED,
|
|
};
|
|
|
|
/*
|
|
* 3 dwords are sufficient for most local fs (64bit ino, 32bit generation).
|
|
* For 32bit arch, fid increases the size of fanotify_event by 12 bytes and
|
|
* fh_* fields increase the size of fanotify_event by another 4 bytes.
|
|
* For 64bit arch, fid increases the size of fanotify_fid by 8 bytes and
|
|
* fh_* fields are packed in a hole after mask.
|
|
*/
|
|
#if BITS_PER_LONG == 32
|
|
#define FANOTIFY_INLINE_FH_LEN (3 << 2)
|
|
#else
|
|
#define FANOTIFY_INLINE_FH_LEN (4 << 2)
|
|
#endif
|
|
|
|
struct fanotify_fid {
|
|
__kernel_fsid_t fsid;
|
|
union {
|
|
unsigned char fh[FANOTIFY_INLINE_FH_LEN];
|
|
unsigned char *ext_fh;
|
|
};
|
|
};
|
|
|
|
static inline void *fanotify_fid_fh(struct fanotify_fid *fid,
|
|
unsigned int fh_len)
|
|
{
|
|
return fh_len <= FANOTIFY_INLINE_FH_LEN ? fid->fh : fid->ext_fh;
|
|
}
|
|
|
|
static inline bool fanotify_fid_equal(struct fanotify_fid *fid1,
|
|
struct fanotify_fid *fid2,
|
|
unsigned int fh_len)
|
|
{
|
|
return fid1->fsid.val[0] == fid2->fsid.val[0] &&
|
|
fid1->fsid.val[1] == fid2->fsid.val[1] &&
|
|
!memcmp(fanotify_fid_fh(fid1, fh_len),
|
|
fanotify_fid_fh(fid2, fh_len), fh_len);
|
|
}
|
|
|
|
/*
|
|
* Structure for normal fanotify events. It gets allocated in
|
|
* fanotify_handle_event() and freed when the information is retrieved by
|
|
* userspace
|
|
*/
|
|
struct fanotify_event {
|
|
struct fsnotify_event fse;
|
|
u32 mask;
|
|
/*
|
|
* Those fields are outside fanotify_fid to pack fanotify_event nicely
|
|
* on 64bit arch and to use fh_type as an indication of whether path
|
|
* or fid are used in the union:
|
|
* FILEID_ROOT (0) for path, > 0 for fid, FILEID_INVALID for neither.
|
|
*/
|
|
u8 fh_type;
|
|
u8 fh_len;
|
|
u16 pad;
|
|
union {
|
|
/*
|
|
* We hold ref to this path so it may be dereferenced at any
|
|
* point during this object's lifetime
|
|
*/
|
|
struct path path;
|
|
/*
|
|
* With FAN_REPORT_FID, we do not hold any reference on the
|
|
* victim object. Instead we store its NFS file handle and its
|
|
* filesystem's fsid as a unique identifier.
|
|
*/
|
|
struct fanotify_fid fid;
|
|
};
|
|
struct pid *pid;
|
|
};
|
|
|
|
static inline bool fanotify_event_has_path(struct fanotify_event *event)
|
|
{
|
|
return event->fh_type == FILEID_ROOT;
|
|
}
|
|
|
|
static inline bool fanotify_event_has_fid(struct fanotify_event *event)
|
|
{
|
|
return event->fh_type != FILEID_ROOT &&
|
|
event->fh_type != FILEID_INVALID;
|
|
}
|
|
|
|
static inline bool fanotify_event_has_ext_fh(struct fanotify_event *event)
|
|
{
|
|
return fanotify_event_has_fid(event) &&
|
|
event->fh_len > FANOTIFY_INLINE_FH_LEN;
|
|
}
|
|
|
|
static inline void *fanotify_event_fh(struct fanotify_event *event)
|
|
{
|
|
return fanotify_fid_fh(&event->fid, event->fh_len);
|
|
}
|
|
|
|
/*
|
|
* Structure for permission fanotify events. It gets allocated and freed in
|
|
* fanotify_handle_event() since we wait there for user response. When the
|
|
* information is retrieved by userspace the structure is moved from
|
|
* group->notification_list to group->fanotify_data.access_list to wait for
|
|
* user response.
|
|
*/
|
|
struct fanotify_perm_event {
|
|
struct fanotify_event fae;
|
|
unsigned short response; /* userspace answer to the event */
|
|
unsigned short state; /* state of the event */
|
|
int fd; /* fd we passed to userspace for this event */
|
|
};
|
|
|
|
static inline struct fanotify_perm_event *
|
|
FANOTIFY_PE(struct fsnotify_event *fse)
|
|
{
|
|
return container_of(fse, struct fanotify_perm_event, fae.fse);
|
|
}
|
|
|
|
static inline bool fanotify_is_perm_event(u32 mask)
|
|
{
|
|
return IS_ENABLED(CONFIG_FANOTIFY_ACCESS_PERMISSIONS) &&
|
|
mask & FANOTIFY_PERM_EVENTS;
|
|
}
|
|
|
|
static inline struct fanotify_event *FANOTIFY_E(struct fsnotify_event *fse)
|
|
{
|
|
return container_of(fse, struct fanotify_event, fse);
|
|
}
|
|
|
|
struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
|
|
struct inode *inode, u32 mask,
|
|
const void *data, int data_type,
|
|
__kernel_fsid_t *fsid);
|