diff options
Diffstat (limited to 'fs/notify/fanotify/fanotify.h')
-rw-r--r-- | fs/notify/fanotify/fanotify.h | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h new file mode 100644 index 000000000..896c819a1 --- /dev/null +++ b/fs/notify/fanotify/fanotify.h @@ -0,0 +1,286 @@ +/* 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_fid_event_cachep; +extern struct kmem_cache *fanotify_path_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). + * fh buf should be dword aligned. On 64bit arch, the ext_buf pointer is + * stored in either the first or last 2 dwords. + */ +#define FANOTIFY_INLINE_FH_LEN (3 << 2) +#define FANOTIFY_FH_HDR_LEN offsetof(struct fanotify_fh, buf) + +/* Fixed size struct for file handle */ +struct fanotify_fh { + u8 type; + u8 len; +#define FANOTIFY_FH_FLAG_EXT_BUF 1 + u8 flags; + u8 pad; + unsigned char buf[]; +} __aligned(4); + +/* Variable size struct for dir file handle + child file handle + name */ +struct fanotify_info { + /* size of dir_fh/file_fh including fanotify_fh hdr size */ + u8 dir_fh_totlen; + u8 file_fh_totlen; + u8 name_len; + u8 pad; + unsigned char buf[]; + /* + * (struct fanotify_fh) dir_fh starts at buf[0] + * (optional) file_fh starts at buf[dir_fh_totlen] + * name starts at buf[dir_fh_totlen + file_fh_totlen] + */ +} __aligned(4); + +static inline bool fanotify_fh_has_ext_buf(struct fanotify_fh *fh) +{ + return (fh->flags & FANOTIFY_FH_FLAG_EXT_BUF); +} + +static inline char **fanotify_fh_ext_buf_ptr(struct fanotify_fh *fh) +{ + BUILD_BUG_ON(FANOTIFY_FH_HDR_LEN % 4); + BUILD_BUG_ON(__alignof__(char *) - 4 + sizeof(char *) > + FANOTIFY_INLINE_FH_LEN); + return (char **)ALIGN((unsigned long)(fh->buf), __alignof__(char *)); +} + +static inline void *fanotify_fh_ext_buf(struct fanotify_fh *fh) +{ + return *fanotify_fh_ext_buf_ptr(fh); +} + +static inline void *fanotify_fh_buf(struct fanotify_fh *fh) +{ + return fanotify_fh_has_ext_buf(fh) ? fanotify_fh_ext_buf(fh) : fh->buf; +} + +static inline int fanotify_info_dir_fh_len(struct fanotify_info *info) +{ + if (!info->dir_fh_totlen || + WARN_ON_ONCE(info->dir_fh_totlen < FANOTIFY_FH_HDR_LEN)) + return 0; + + return info->dir_fh_totlen - FANOTIFY_FH_HDR_LEN; +} + +static inline struct fanotify_fh *fanotify_info_dir_fh(struct fanotify_info *info) +{ + BUILD_BUG_ON(offsetof(struct fanotify_info, buf) % 4); + + return (struct fanotify_fh *)info->buf; +} + +static inline int fanotify_info_file_fh_len(struct fanotify_info *info) +{ + if (!info->file_fh_totlen || + WARN_ON_ONCE(info->file_fh_totlen < FANOTIFY_FH_HDR_LEN)) + return 0; + + return info->file_fh_totlen - FANOTIFY_FH_HDR_LEN; +} + +static inline struct fanotify_fh *fanotify_info_file_fh(struct fanotify_info *info) +{ + return (struct fanotify_fh *)(info->buf + info->dir_fh_totlen); +} + +static inline const char *fanotify_info_name(struct fanotify_info *info) +{ + return info->buf + info->dir_fh_totlen + info->file_fh_totlen; +} + +static inline void fanotify_info_init(struct fanotify_info *info) +{ + info->dir_fh_totlen = 0; + info->file_fh_totlen = 0; + info->name_len = 0; +} + +static inline void fanotify_info_copy_name(struct fanotify_info *info, + const struct qstr *name) +{ + info->name_len = name->len; + strcpy(info->buf + info->dir_fh_totlen + info->file_fh_totlen, + name->name); +} + +/* + * Common structure for fanotify events. Concrete structs are allocated in + * fanotify_handle_event() and freed when the information is retrieved by + * userspace. The type of event determines how it was allocated, how it will + * be freed and which concrete struct it may be cast to. + */ +enum fanotify_event_type { + FANOTIFY_EVENT_TYPE_FID, /* fixed length */ + FANOTIFY_EVENT_TYPE_FID_NAME, /* variable length */ + FANOTIFY_EVENT_TYPE_PATH, + FANOTIFY_EVENT_TYPE_PATH_PERM, + FANOTIFY_EVENT_TYPE_OVERFLOW, /* struct fanotify_event */ +}; + +struct fanotify_event { + struct fsnotify_event fse; + u32 mask; + enum fanotify_event_type type; + struct pid *pid; +}; + +static inline void fanotify_init_event(struct fanotify_event *event, + unsigned long id, u32 mask) +{ + fsnotify_init_event(&event->fse, id); + event->mask = mask; + event->pid = NULL; +} + +struct fanotify_fid_event { + struct fanotify_event fae; + __kernel_fsid_t fsid; + struct fanotify_fh object_fh; + /* Reserve space in object_fh.buf[] - access with fanotify_fh_buf() */ + unsigned char _inline_fh_buf[FANOTIFY_INLINE_FH_LEN]; +}; + +static inline struct fanotify_fid_event * +FANOTIFY_FE(struct fanotify_event *event) +{ + return container_of(event, struct fanotify_fid_event, fae); +} + +struct fanotify_name_event { + struct fanotify_event fae; + __kernel_fsid_t fsid; + struct fanotify_info info; +}; + +static inline struct fanotify_name_event * +FANOTIFY_NE(struct fanotify_event *event) +{ + return container_of(event, struct fanotify_name_event, fae); +} + +static inline __kernel_fsid_t *fanotify_event_fsid(struct fanotify_event *event) +{ + if (event->type == FANOTIFY_EVENT_TYPE_FID) + return &FANOTIFY_FE(event)->fsid; + else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME) + return &FANOTIFY_NE(event)->fsid; + else + return NULL; +} + +static inline struct fanotify_fh *fanotify_event_object_fh( + struct fanotify_event *event) +{ + if (event->type == FANOTIFY_EVENT_TYPE_FID) + return &FANOTIFY_FE(event)->object_fh; + else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME) + return fanotify_info_file_fh(&FANOTIFY_NE(event)->info); + else + return NULL; +} + +static inline struct fanotify_info *fanotify_event_info( + struct fanotify_event *event) +{ + if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME) + return &FANOTIFY_NE(event)->info; + else + return NULL; +} + +static inline int fanotify_event_object_fh_len(struct fanotify_event *event) +{ + struct fanotify_info *info = fanotify_event_info(event); + struct fanotify_fh *fh = fanotify_event_object_fh(event); + + if (info) + return info->file_fh_totlen ? fh->len : 0; + else + return fh ? fh->len : 0; +} + +static inline int fanotify_event_dir_fh_len(struct fanotify_event *event) +{ + struct fanotify_info *info = fanotify_event_info(event); + + return info ? fanotify_info_dir_fh_len(info) : 0; +} + +struct fanotify_path_event { + struct fanotify_event fae; + struct path path; +}; + +static inline struct fanotify_path_event * +FANOTIFY_PE(struct fanotify_event *event) +{ + return container_of(event, struct fanotify_path_event, fae); +} + +/* + * 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; + struct path path; + 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_PERM(struct fanotify_event *event) +{ + return container_of(event, struct fanotify_perm_event, fae); +} + +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); +} + +static inline bool fanotify_event_has_path(struct fanotify_event *event) +{ + return event->type == FANOTIFY_EVENT_TYPE_PATH || + event->type == FANOTIFY_EVENT_TYPE_PATH_PERM; +} + +static inline struct path *fanotify_event_path(struct fanotify_event *event) +{ + if (event->type == FANOTIFY_EVENT_TYPE_PATH) + return &FANOTIFY_PE(event)->path; + else if (event->type == FANOTIFY_EVENT_TYPE_PATH_PERM) + return &FANOTIFY_PERM(event)->path; + else + return NULL; +} |