]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - fs/notify/fanotify/fanotify.c
fanotify: do not clone on merge unless needed
[mirror_ubuntu-artful-kernel.git] / fs / notify / fanotify / fanotify.c
1 #include <linux/fdtable.h>
2 #include <linux/fsnotify_backend.h>
3 #include <linux/init.h>
4 #include <linux/kernel.h> /* UINT_MAX */
5 #include <linux/types.h>
6
7 #include "fanotify.h"
8
9 static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new)
10 {
11 pr_debug("%s: old=%p new=%p\n", __func__, old, new);
12
13 if ((old->to_tell == new->to_tell) &&
14 (old->data_type == new->data_type)) {
15 switch (old->data_type) {
16 case (FSNOTIFY_EVENT_PATH):
17 if ((old->path.mnt == new->path.mnt) &&
18 (old->path.dentry == new->path.dentry))
19 return true;
20 case (FSNOTIFY_EVENT_NONE):
21 return true;
22 default:
23 BUG();
24 };
25 }
26 return false;
27 }
28
29 static int fanotify_merge(struct list_head *list, struct fsnotify_event *event)
30 {
31 struct fsnotify_event_holder *test_holder;
32 struct fsnotify_event *test_event;
33 struct fsnotify_event *new_event;
34 int ret = 0;
35
36 pr_debug("%s: list=%p event=%p\n", __func__, list, event);
37
38 /* and the list better be locked by something too! */
39
40 list_for_each_entry_reverse(test_holder, list, event_list) {
41 test_event = test_holder->event;
42 if (should_merge(test_event, event)) {
43 ret = -EEXIST;
44
45 /* if they are exactly the same we are done */
46 if (test_event->mask == event->mask)
47 goto out;
48
49 /*
50 * if the refcnt == 1 this is the only queue
51 * for this event and so we can update the mask
52 * in place.
53 */
54 if (atomic_read(&test_event->refcnt) == 1) {
55 test_event->mask |= event->mask;
56 goto out;
57 }
58
59 /* can't allocate memory, merge was no possible */
60 new_event = fsnotify_clone_event(test_event);
61 if (unlikely(!new_event)) {
62 ret = 0;
63 goto out;
64 }
65
66 /* build new event and replace it on the list */
67 new_event->mask = (test_event->mask | event->mask);
68 fsnotify_replace_event(test_holder, new_event);
69 /* match ref from fsnotify_clone_event() */
70 fsnotify_put_event(new_event);
71
72 break;
73 }
74 }
75 out:
76 return ret;
77 }
78
79 static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event)
80 {
81 int ret;
82
83
84 BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS);
85 BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY);
86 BUILD_BUG_ON(FAN_CLOSE_NOWRITE != FS_CLOSE_NOWRITE);
87 BUILD_BUG_ON(FAN_CLOSE_WRITE != FS_CLOSE_WRITE);
88 BUILD_BUG_ON(FAN_OPEN != FS_OPEN);
89 BUILD_BUG_ON(FAN_EVENT_ON_CHILD != FS_EVENT_ON_CHILD);
90 BUILD_BUG_ON(FAN_Q_OVERFLOW != FS_Q_OVERFLOW);
91
92 pr_debug("%s: group=%p event=%p\n", __func__, group, event);
93
94 ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge);
95 /* -EEXIST means this event was merged with another, not that it was an error */
96 if (ret == -EEXIST)
97 ret = 0;
98 return ret;
99 }
100
101 static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *inode,
102 struct vfsmount *mnt, __u32 mask, void *data,
103 int data_type)
104 {
105 struct fsnotify_mark *fsn_mark;
106 bool send;
107
108 pr_debug("%s: group=%p inode=%p mask=%x data=%p data_type=%d\n",
109 __func__, group, inode, mask, data, data_type);
110
111 /* sorry, fanotify only gives a damn about files and dirs */
112 if (!S_ISREG(inode->i_mode) &&
113 !S_ISDIR(inode->i_mode))
114 return false;
115
116 /* if we don't have enough info to send an event to userspace say no */
117 if (data_type != FSNOTIFY_EVENT_PATH)
118 return false;
119
120 fsn_mark = fsnotify_find_mark(group, inode);
121 if (!fsn_mark)
122 return false;
123
124 /* if the event is for a child and this inode doesn't care about
125 * events on the child, don't send it! */
126 if ((mask & FS_EVENT_ON_CHILD) &&
127 !(fsn_mark->mask & FS_EVENT_ON_CHILD)) {
128 send = false;
129 } else {
130 /*
131 * We care about children, but do we care about this particular
132 * type of event?
133 */
134 mask = (mask & ~FS_EVENT_ON_CHILD);
135 send = (fsn_mark->mask & mask);
136 }
137
138 /* find took a reference */
139 fsnotify_put_mark(fsn_mark);
140
141 return send;
142 }
143
144 const struct fsnotify_ops fanotify_fsnotify_ops = {
145 .handle_event = fanotify_handle_event,
146 .should_send_event = fanotify_should_send_event,
147 .free_group_priv = NULL,
148 .free_event_priv = NULL,
149 .freeing_mark = NULL,
150 };