| .. | .. |
|---|
| 2 | 2 | #include <linux/fsnotify_backend.h> |
|---|
| 3 | 3 | #include <linux/path.h> |
|---|
| 4 | 4 | #include <linux/slab.h> |
|---|
| 5 | +#include <linux/exportfs.h> |
|---|
| 5 | 6 | |
|---|
| 6 | 7 | extern struct kmem_cache *fanotify_mark_cache; |
|---|
| 7 | | -extern struct kmem_cache *fanotify_event_cachep; |
|---|
| 8 | +extern struct kmem_cache *fanotify_fid_event_cachep; |
|---|
| 9 | +extern struct kmem_cache *fanotify_path_event_cachep; |
|---|
| 8 | 10 | extern struct kmem_cache *fanotify_perm_event_cachep; |
|---|
| 9 | 11 | |
|---|
| 10 | | -/* |
|---|
| 11 | | - * Structure for normal fanotify events. It gets allocated in |
|---|
| 12 | | - * fanotify_handle_event() and freed when the information is retrieved by |
|---|
| 13 | | - * userspace |
|---|
| 14 | | - */ |
|---|
| 15 | | -struct fanotify_event_info { |
|---|
| 16 | | - struct fsnotify_event fse; |
|---|
| 17 | | - /* |
|---|
| 18 | | - * We hold ref to this path so it may be dereferenced at any point |
|---|
| 19 | | - * during this object's lifetime |
|---|
| 20 | | - */ |
|---|
| 21 | | - struct path path; |
|---|
| 22 | | - struct pid *tgid; |
|---|
| 12 | +/* Possible states of the permission event */ |
|---|
| 13 | +enum { |
|---|
| 14 | + FAN_EVENT_INIT, |
|---|
| 15 | + FAN_EVENT_REPORTED, |
|---|
| 16 | + FAN_EVENT_ANSWERED, |
|---|
| 17 | + FAN_EVENT_CANCELED, |
|---|
| 23 | 18 | }; |
|---|
| 19 | + |
|---|
| 20 | +/* |
|---|
| 21 | + * 3 dwords are sufficient for most local fs (64bit ino, 32bit generation). |
|---|
| 22 | + * fh buf should be dword aligned. On 64bit arch, the ext_buf pointer is |
|---|
| 23 | + * stored in either the first or last 2 dwords. |
|---|
| 24 | + */ |
|---|
| 25 | +#define FANOTIFY_INLINE_FH_LEN (3 << 2) |
|---|
| 26 | +#define FANOTIFY_FH_HDR_LEN offsetof(struct fanotify_fh, buf) |
|---|
| 27 | + |
|---|
| 28 | +/* Fixed size struct for file handle */ |
|---|
| 29 | +struct fanotify_fh { |
|---|
| 30 | + u8 type; |
|---|
| 31 | + u8 len; |
|---|
| 32 | +#define FANOTIFY_FH_FLAG_EXT_BUF 1 |
|---|
| 33 | + u8 flags; |
|---|
| 34 | + u8 pad; |
|---|
| 35 | + unsigned char buf[]; |
|---|
| 36 | +} __aligned(4); |
|---|
| 37 | + |
|---|
| 38 | +/* Variable size struct for dir file handle + child file handle + name */ |
|---|
| 39 | +struct fanotify_info { |
|---|
| 40 | + /* size of dir_fh/file_fh including fanotify_fh hdr size */ |
|---|
| 41 | + u8 dir_fh_totlen; |
|---|
| 42 | + u8 file_fh_totlen; |
|---|
| 43 | + u8 name_len; |
|---|
| 44 | + u8 pad; |
|---|
| 45 | + unsigned char buf[]; |
|---|
| 46 | + /* |
|---|
| 47 | + * (struct fanotify_fh) dir_fh starts at buf[0] |
|---|
| 48 | + * (optional) file_fh starts at buf[dir_fh_totlen] |
|---|
| 49 | + * name starts at buf[dir_fh_totlen + file_fh_totlen] |
|---|
| 50 | + */ |
|---|
| 51 | +} __aligned(4); |
|---|
| 52 | + |
|---|
| 53 | +static inline bool fanotify_fh_has_ext_buf(struct fanotify_fh *fh) |
|---|
| 54 | +{ |
|---|
| 55 | + return (fh->flags & FANOTIFY_FH_FLAG_EXT_BUF); |
|---|
| 56 | +} |
|---|
| 57 | + |
|---|
| 58 | +static inline char **fanotify_fh_ext_buf_ptr(struct fanotify_fh *fh) |
|---|
| 59 | +{ |
|---|
| 60 | + BUILD_BUG_ON(FANOTIFY_FH_HDR_LEN % 4); |
|---|
| 61 | + BUILD_BUG_ON(__alignof__(char *) - 4 + sizeof(char *) > |
|---|
| 62 | + FANOTIFY_INLINE_FH_LEN); |
|---|
| 63 | + return (char **)ALIGN((unsigned long)(fh->buf), __alignof__(char *)); |
|---|
| 64 | +} |
|---|
| 65 | + |
|---|
| 66 | +static inline void *fanotify_fh_ext_buf(struct fanotify_fh *fh) |
|---|
| 67 | +{ |
|---|
| 68 | + return *fanotify_fh_ext_buf_ptr(fh); |
|---|
| 69 | +} |
|---|
| 70 | + |
|---|
| 71 | +static inline void *fanotify_fh_buf(struct fanotify_fh *fh) |
|---|
| 72 | +{ |
|---|
| 73 | + return fanotify_fh_has_ext_buf(fh) ? fanotify_fh_ext_buf(fh) : fh->buf; |
|---|
| 74 | +} |
|---|
| 75 | + |
|---|
| 76 | +static inline int fanotify_info_dir_fh_len(struct fanotify_info *info) |
|---|
| 77 | +{ |
|---|
| 78 | + if (!info->dir_fh_totlen || |
|---|
| 79 | + WARN_ON_ONCE(info->dir_fh_totlen < FANOTIFY_FH_HDR_LEN)) |
|---|
| 80 | + return 0; |
|---|
| 81 | + |
|---|
| 82 | + return info->dir_fh_totlen - FANOTIFY_FH_HDR_LEN; |
|---|
| 83 | +} |
|---|
| 84 | + |
|---|
| 85 | +static inline struct fanotify_fh *fanotify_info_dir_fh(struct fanotify_info *info) |
|---|
| 86 | +{ |
|---|
| 87 | + BUILD_BUG_ON(offsetof(struct fanotify_info, buf) % 4); |
|---|
| 88 | + |
|---|
| 89 | + return (struct fanotify_fh *)info->buf; |
|---|
| 90 | +} |
|---|
| 91 | + |
|---|
| 92 | +static inline int fanotify_info_file_fh_len(struct fanotify_info *info) |
|---|
| 93 | +{ |
|---|
| 94 | + if (!info->file_fh_totlen || |
|---|
| 95 | + WARN_ON_ONCE(info->file_fh_totlen < FANOTIFY_FH_HDR_LEN)) |
|---|
| 96 | + return 0; |
|---|
| 97 | + |
|---|
| 98 | + return info->file_fh_totlen - FANOTIFY_FH_HDR_LEN; |
|---|
| 99 | +} |
|---|
| 100 | + |
|---|
| 101 | +static inline struct fanotify_fh *fanotify_info_file_fh(struct fanotify_info *info) |
|---|
| 102 | +{ |
|---|
| 103 | + return (struct fanotify_fh *)(info->buf + info->dir_fh_totlen); |
|---|
| 104 | +} |
|---|
| 105 | + |
|---|
| 106 | +static inline const char *fanotify_info_name(struct fanotify_info *info) |
|---|
| 107 | +{ |
|---|
| 108 | + return info->buf + info->dir_fh_totlen + info->file_fh_totlen; |
|---|
| 109 | +} |
|---|
| 110 | + |
|---|
| 111 | +static inline void fanotify_info_init(struct fanotify_info *info) |
|---|
| 112 | +{ |
|---|
| 113 | + info->dir_fh_totlen = 0; |
|---|
| 114 | + info->file_fh_totlen = 0; |
|---|
| 115 | + info->name_len = 0; |
|---|
| 116 | +} |
|---|
| 117 | + |
|---|
| 118 | +static inline void fanotify_info_copy_name(struct fanotify_info *info, |
|---|
| 119 | + const struct qstr *name) |
|---|
| 120 | +{ |
|---|
| 121 | + info->name_len = name->len; |
|---|
| 122 | + strcpy(info->buf + info->dir_fh_totlen + info->file_fh_totlen, |
|---|
| 123 | + name->name); |
|---|
| 124 | +} |
|---|
| 125 | + |
|---|
| 126 | +/* |
|---|
| 127 | + * Common structure for fanotify events. Concrete structs are allocated in |
|---|
| 128 | + * fanotify_handle_event() and freed when the information is retrieved by |
|---|
| 129 | + * userspace. The type of event determines how it was allocated, how it will |
|---|
| 130 | + * be freed and which concrete struct it may be cast to. |
|---|
| 131 | + */ |
|---|
| 132 | +enum fanotify_event_type { |
|---|
| 133 | + FANOTIFY_EVENT_TYPE_FID, /* fixed length */ |
|---|
| 134 | + FANOTIFY_EVENT_TYPE_FID_NAME, /* variable length */ |
|---|
| 135 | + FANOTIFY_EVENT_TYPE_PATH, |
|---|
| 136 | + FANOTIFY_EVENT_TYPE_PATH_PERM, |
|---|
| 137 | + FANOTIFY_EVENT_TYPE_OVERFLOW, /* struct fanotify_event */ |
|---|
| 138 | +}; |
|---|
| 139 | + |
|---|
| 140 | +struct fanotify_event { |
|---|
| 141 | + struct fsnotify_event fse; |
|---|
| 142 | + u32 mask; |
|---|
| 143 | + enum fanotify_event_type type; |
|---|
| 144 | + struct pid *pid; |
|---|
| 145 | +}; |
|---|
| 146 | + |
|---|
| 147 | +static inline void fanotify_init_event(struct fanotify_event *event, |
|---|
| 148 | + unsigned long id, u32 mask) |
|---|
| 149 | +{ |
|---|
| 150 | + fsnotify_init_event(&event->fse, id); |
|---|
| 151 | + event->mask = mask; |
|---|
| 152 | + event->pid = NULL; |
|---|
| 153 | +} |
|---|
| 154 | + |
|---|
| 155 | +struct fanotify_fid_event { |
|---|
| 156 | + struct fanotify_event fae; |
|---|
| 157 | + __kernel_fsid_t fsid; |
|---|
| 158 | + struct fanotify_fh object_fh; |
|---|
| 159 | + /* Reserve space in object_fh.buf[] - access with fanotify_fh_buf() */ |
|---|
| 160 | + unsigned char _inline_fh_buf[FANOTIFY_INLINE_FH_LEN]; |
|---|
| 161 | +}; |
|---|
| 162 | + |
|---|
| 163 | +static inline struct fanotify_fid_event * |
|---|
| 164 | +FANOTIFY_FE(struct fanotify_event *event) |
|---|
| 165 | +{ |
|---|
| 166 | + return container_of(event, struct fanotify_fid_event, fae); |
|---|
| 167 | +} |
|---|
| 168 | + |
|---|
| 169 | +struct fanotify_name_event { |
|---|
| 170 | + struct fanotify_event fae; |
|---|
| 171 | + __kernel_fsid_t fsid; |
|---|
| 172 | + struct fanotify_info info; |
|---|
| 173 | +}; |
|---|
| 174 | + |
|---|
| 175 | +static inline struct fanotify_name_event * |
|---|
| 176 | +FANOTIFY_NE(struct fanotify_event *event) |
|---|
| 177 | +{ |
|---|
| 178 | + return container_of(event, struct fanotify_name_event, fae); |
|---|
| 179 | +} |
|---|
| 180 | + |
|---|
| 181 | +static inline __kernel_fsid_t *fanotify_event_fsid(struct fanotify_event *event) |
|---|
| 182 | +{ |
|---|
| 183 | + if (event->type == FANOTIFY_EVENT_TYPE_FID) |
|---|
| 184 | + return &FANOTIFY_FE(event)->fsid; |
|---|
| 185 | + else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME) |
|---|
| 186 | + return &FANOTIFY_NE(event)->fsid; |
|---|
| 187 | + else |
|---|
| 188 | + return NULL; |
|---|
| 189 | +} |
|---|
| 190 | + |
|---|
| 191 | +static inline struct fanotify_fh *fanotify_event_object_fh( |
|---|
| 192 | + struct fanotify_event *event) |
|---|
| 193 | +{ |
|---|
| 194 | + if (event->type == FANOTIFY_EVENT_TYPE_FID) |
|---|
| 195 | + return &FANOTIFY_FE(event)->object_fh; |
|---|
| 196 | + else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME) |
|---|
| 197 | + return fanotify_info_file_fh(&FANOTIFY_NE(event)->info); |
|---|
| 198 | + else |
|---|
| 199 | + return NULL; |
|---|
| 200 | +} |
|---|
| 201 | + |
|---|
| 202 | +static inline struct fanotify_info *fanotify_event_info( |
|---|
| 203 | + struct fanotify_event *event) |
|---|
| 204 | +{ |
|---|
| 205 | + if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME) |
|---|
| 206 | + return &FANOTIFY_NE(event)->info; |
|---|
| 207 | + else |
|---|
| 208 | + return NULL; |
|---|
| 209 | +} |
|---|
| 210 | + |
|---|
| 211 | +static inline int fanotify_event_object_fh_len(struct fanotify_event *event) |
|---|
| 212 | +{ |
|---|
| 213 | + struct fanotify_info *info = fanotify_event_info(event); |
|---|
| 214 | + struct fanotify_fh *fh = fanotify_event_object_fh(event); |
|---|
| 215 | + |
|---|
| 216 | + if (info) |
|---|
| 217 | + return info->file_fh_totlen ? fh->len : 0; |
|---|
| 218 | + else |
|---|
| 219 | + return fh ? fh->len : 0; |
|---|
| 220 | +} |
|---|
| 221 | + |
|---|
| 222 | +static inline int fanotify_event_dir_fh_len(struct fanotify_event *event) |
|---|
| 223 | +{ |
|---|
| 224 | + struct fanotify_info *info = fanotify_event_info(event); |
|---|
| 225 | + |
|---|
| 226 | + return info ? fanotify_info_dir_fh_len(info) : 0; |
|---|
| 227 | +} |
|---|
| 228 | + |
|---|
| 229 | +struct fanotify_path_event { |
|---|
| 230 | + struct fanotify_event fae; |
|---|
| 231 | + struct path path; |
|---|
| 232 | +}; |
|---|
| 233 | + |
|---|
| 234 | +static inline struct fanotify_path_event * |
|---|
| 235 | +FANOTIFY_PE(struct fanotify_event *event) |
|---|
| 236 | +{ |
|---|
| 237 | + return container_of(event, struct fanotify_path_event, fae); |
|---|
| 238 | +} |
|---|
| 24 | 239 | |
|---|
| 25 | 240 | /* |
|---|
| 26 | 241 | * Structure for permission fanotify events. It gets allocated and freed in |
|---|
| .. | .. |
|---|
| 29 | 244 | * group->notification_list to group->fanotify_data.access_list to wait for |
|---|
| 30 | 245 | * user response. |
|---|
| 31 | 246 | */ |
|---|
| 32 | | -struct fanotify_perm_event_info { |
|---|
| 33 | | - struct fanotify_event_info fae; |
|---|
| 34 | | - int response; /* userspace answer to question */ |
|---|
| 247 | +struct fanotify_perm_event { |
|---|
| 248 | + struct fanotify_event fae; |
|---|
| 249 | + struct path path; |
|---|
| 250 | + unsigned short response; /* userspace answer to the event */ |
|---|
| 251 | + unsigned short state; /* state of the event */ |
|---|
| 35 | 252 | int fd; /* fd we passed to userspace for this event */ |
|---|
| 36 | 253 | }; |
|---|
| 37 | 254 | |
|---|
| 38 | | -static inline struct fanotify_perm_event_info * |
|---|
| 39 | | -FANOTIFY_PE(struct fsnotify_event *fse) |
|---|
| 255 | +static inline struct fanotify_perm_event * |
|---|
| 256 | +FANOTIFY_PERM(struct fanotify_event *event) |
|---|
| 40 | 257 | { |
|---|
| 41 | | - return container_of(fse, struct fanotify_perm_event_info, fae.fse); |
|---|
| 258 | + return container_of(event, struct fanotify_perm_event, fae); |
|---|
| 42 | 259 | } |
|---|
| 43 | 260 | |
|---|
| 44 | 261 | static inline bool fanotify_is_perm_event(u32 mask) |
|---|
| 45 | 262 | { |
|---|
| 46 | 263 | return IS_ENABLED(CONFIG_FANOTIFY_ACCESS_PERMISSIONS) && |
|---|
| 47 | | - mask & FAN_ALL_PERM_EVENTS; |
|---|
| 264 | + mask & FANOTIFY_PERM_EVENTS; |
|---|
| 48 | 265 | } |
|---|
| 49 | 266 | |
|---|
| 50 | | -static inline struct fanotify_event_info *FANOTIFY_E(struct fsnotify_event *fse) |
|---|
| 267 | +static inline struct fanotify_event *FANOTIFY_E(struct fsnotify_event *fse) |
|---|
| 51 | 268 | { |
|---|
| 52 | | - return container_of(fse, struct fanotify_event_info, fse); |
|---|
| 269 | + return container_of(fse, struct fanotify_event, fse); |
|---|
| 53 | 270 | } |
|---|
| 54 | 271 | |
|---|
| 55 | | -struct fanotify_event_info *fanotify_alloc_event(struct fsnotify_group *group, |
|---|
| 56 | | - struct inode *inode, u32 mask, |
|---|
| 57 | | - const struct path *path); |
|---|
| 272 | +static inline bool fanotify_event_has_path(struct fanotify_event *event) |
|---|
| 273 | +{ |
|---|
| 274 | + return event->type == FANOTIFY_EVENT_TYPE_PATH || |
|---|
| 275 | + event->type == FANOTIFY_EVENT_TYPE_PATH_PERM; |
|---|
| 276 | +} |
|---|
| 277 | + |
|---|
| 278 | +static inline struct path *fanotify_event_path(struct fanotify_event *event) |
|---|
| 279 | +{ |
|---|
| 280 | + if (event->type == FANOTIFY_EVENT_TYPE_PATH) |
|---|
| 281 | + return &FANOTIFY_PE(event)->path; |
|---|
| 282 | + else if (event->type == FANOTIFY_EVENT_TYPE_PATH_PERM) |
|---|
| 283 | + return &FANOTIFY_PERM(event)->path; |
|---|
| 284 | + else |
|---|
| 285 | + return NULL; |
|---|
| 286 | +} |
|---|