fanotify: record name info for FAN_DIR_MODIFY event
[platform/kernel/linux-rpi.git] / fs / notify / fanotify / fanotify.h
index 68b3050..35bfbf4 100644 (file)
@@ -5,7 +5,8 @@
 #include <linux/exportfs.h>
 
 extern struct kmem_cache *fanotify_mark_cache;
-extern struct kmem_cache *fanotify_event_cachep;
+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 */
@@ -18,94 +19,140 @@ enum {
 
 /*
  * 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.
+ * fh buf should be dword aligned. On 64bit arch, the ext_buf pointer is
+ * stored in either the first or last 2 dwords.
  */
-#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;
-       };
-};
+struct fanotify_fh {
+       unsigned char buf[FANOTIFY_INLINE_FH_LEN];
+       u8 type;
+       u8 len;
+} __aligned(4);
+
+static inline bool fanotify_fh_has_ext_buf(struct fanotify_fh *fh)
+{
+       return fh->len > FANOTIFY_INLINE_FH_LEN;
+}
+
+static inline char **fanotify_fh_ext_buf_ptr(struct fanotify_fh *fh)
+{
+       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_fid_fh(struct fanotify_fid *fid,
-                                   unsigned int fh_len)
+static inline void *fanotify_fh_ext_buf(struct fanotify_fh *fh)
 {
-       return fh_len <= FANOTIFY_INLINE_FH_LEN ? fid->fh : fid->ext_fh;
+       return *fanotify_fh_ext_buf_ptr(fh);
 }
 
-static inline bool fanotify_fid_equal(struct fanotify_fid *fid1,
-                                     struct fanotify_fid *fid2,
-                                     unsigned int fh_len)
+static inline void *fanotify_fh_buf(struct fanotify_fh *fh)
 {
-       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);
+       return fanotify_fh_has_ext_buf(fh) ? fanotify_fh_ext_buf(fh) : fh->buf;
 }
 
 /*
- * Structure for normal fanotify events. It gets allocated in
+ * Common structure for fanotify events. Concrete structs are allocated in
  * fanotify_handle_event() and freed when the information is retrieved by
- * userspace
+ * 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,
+};
+
 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;
-       };
+       enum fanotify_event_type type;
        struct pid *pid;
 };
 
-static inline bool fanotify_event_has_path(struct fanotify_event *event)
+struct fanotify_fid_event {
+       struct fanotify_event fae;
+       __kernel_fsid_t fsid;
+       struct fanotify_fh object_fh;
+};
+
+static inline struct fanotify_fid_event *
+FANOTIFY_FE(struct fanotify_event *event)
 {
-       return event->fh_type == FILEID_ROOT;
+       return container_of(event, struct fanotify_fid_event, fae);
 }
 
-static inline bool fanotify_event_has_fid(struct fanotify_event *event)
+struct fanotify_name_event {
+       struct fanotify_event fae;
+       __kernel_fsid_t fsid;
+       struct fanotify_fh dir_fh;
+       u8 name_len;
+       char name[0];
+};
+
+static inline struct fanotify_name_event *
+FANOTIFY_NE(struct fanotify_event *event)
 {
-       return event->fh_type != FILEID_ROOT &&
-               event->fh_type != FILEID_INVALID;
+       return container_of(event, struct fanotify_name_event, fae);
 }
 
-static inline bool fanotify_event_has_ext_fh(struct fanotify_event *event)
+static inline __kernel_fsid_t *fanotify_event_fsid(struct fanotify_event *event)
 {
-       return fanotify_event_has_fid(event) &&
-               event->fh_len > FANOTIFY_INLINE_FH_LEN;
+       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 void *fanotify_event_fh(struct fanotify_event *event)
+static inline struct fanotify_fh *fanotify_event_object_fh(
+                                               struct fanotify_event *event)
 {
-       return fanotify_fid_fh(&event->fid, event->fh_len);
+       if (event->type == FANOTIFY_EVENT_TYPE_FID)
+               return &FANOTIFY_FE(event)->object_fh;
+       else
+               return NULL;
+}
+
+static inline struct fanotify_fh *fanotify_event_dir_fh(
+                                               struct fanotify_event *event)
+{
+       if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME)
+               return &FANOTIFY_NE(event)->dir_fh;
+       else
+               return NULL;
+}
+
+static inline int fanotify_event_object_fh_len(struct fanotify_event *event)
+{
+       struct fanotify_fh *fh = fanotify_event_object_fh(event);
+
+       return fh ? fh->len : 0;
+}
+
+static inline bool fanotify_event_has_name(struct fanotify_event *event)
+{
+       return event->type == FANOTIFY_EVENT_TYPE_FID_NAME;
+}
+
+static inline int fanotify_event_name_len(struct fanotify_event *event)
+{
+       return fanotify_event_has_name(event) ?
+               FANOTIFY_NE(event)->name_len : 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);
 }
 
 /*
@@ -117,15 +164,16 @@ static inline void *fanotify_event_fh(struct fanotify_event *event)
  */
 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_PE(struct fsnotify_event *fse)
+FANOTIFY_PERM(struct fanotify_event *event)
 {
-       return container_of(fse, struct fanotify_perm_event, fae.fse);
+       return container_of(event, struct fanotify_perm_event, fae);
 }
 
 static inline bool fanotify_is_perm_event(u32 mask)
@@ -139,7 +187,24 @@ 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;
+}
+
 struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
                                            struct inode *inode, u32 mask,
                                            const void *data, int data_type,
+                                           const struct qstr *file_name,
                                            __kernel_fsid_t *fsid);