fanotify.c 7.2 KB
Newer Older
1
#include <linux/fanotify.h>
2 3 4
#include <linux/fdtable.h>
#include <linux/fsnotify_backend.h>
#include <linux/init.h>
E
Eric Paris 已提交
5
#include <linux/jiffies.h>
6
#include <linux/kernel.h> /* UINT_MAX */
7
#include <linux/mount.h>
E
Eric Paris 已提交
8
#include <linux/sched.h>
9
#include <linux/sched/user.h>
10
#include <linux/types.h>
E
Eric Paris 已提交
11
#include <linux/wait.h>
12

13 14 15 16
#include "fanotify.h"

static bool should_merge(struct fsnotify_event *old_fsn,
			 struct fsnotify_event *new_fsn)
17
{
18
	struct fanotify_event_info *old, *new;
19

20 21 22 23 24 25 26 27
	pr_debug("%s: old=%p new=%p\n", __func__, old_fsn, new_fsn);
	old = FANOTIFY_E(old_fsn);
	new = FANOTIFY_E(new_fsn);

	if (old_fsn->inode == new_fsn->inode && old->tgid == new->tgid &&
	    old->path.mnt == new->path.mnt &&
	    old->path.dentry == new->path.dentry)
		return true;
28 29 30
	return false;
}

31
/* and the list better be locked by something too! */
32
static int fanotify_merge(struct list_head *list, struct fsnotify_event *event)
33
{
34
	struct fsnotify_event *test_event;
35 36 37

	pr_debug("%s: list=%p event=%p\n", __func__, list, event);

38 39 40 41 42 43 44
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
	/*
	 * Don't merge a permission event with any other event so that we know
	 * the event structure we have created in fanotify_handle_event() is the
	 * one we should check for permission response.
	 */
	if (event->mask & FAN_ALL_PERM_EVENTS)
45
		return 0;
46 47
#endif

48 49
	list_for_each_entry_reverse(test_event, list, list) {
		if (should_merge(test_event, event)) {
50 51
			test_event->mask |= event->mask;
			return 1;
52 53
		}
	}
54

55
	return 0;
56 57
}

E
Eric Paris 已提交
58
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
59
static int fanotify_get_response(struct fsnotify_group *group,
60 61
				 struct fanotify_perm_event_info *event,
				 struct fsnotify_iter_info *iter_info)
E
Eric Paris 已提交
62 63 64 65 66
{
	int ret;

	pr_debug("%s: group=%p event=%p\n", __func__, group, event);

67 68 69 70 71 72 73 74 75
	/*
	 * fsnotify_prepare_user_wait() fails if we race with mark deletion.
	 * Just let the operation pass in that case.
	 */
	if (!fsnotify_prepare_user_wait(iter_info)) {
		event->response = FAN_ALLOW;
		goto out;
	}

76
	wait_event(group->fanotify_data.access_waitq, event->response);
E
Eric Paris 已提交
77

78 79
	fsnotify_finish_user_wait(iter_info);
out:
E
Eric Paris 已提交
80 81 82 83 84 85 86 87 88 89 90
	/* userspace responded, convert to something usable */
	switch (event->response) {
	case FAN_ALLOW:
		ret = 0;
		break;
	case FAN_DENY:
	default:
		ret = -EPERM;
	}
	event->response = 0;

91 92 93
	pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__,
		 group, event, ret);
	
E
Eric Paris 已提交
94 95 96 97
	return ret;
}
#endif

98
static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark,
99
				       struct fsnotify_mark *vfsmnt_mark,
100
				       u32 event_mask,
101
				       const void *data, int data_type)
102
{
103
	__u32 marks_mask, marks_ignored_mask;
104
	const struct path *path = data;
105

106 107 108
	pr_debug("%s: inode_mark=%p vfsmnt_mark=%p mask=%x data=%p"
		 " data_type=%d\n", __func__, inode_mark, vfsmnt_mark,
		 event_mask, data, data_type);
109

110
	/* if we don't have enough info to send an event to userspace say no */
111
	if (data_type != FSNOTIFY_EVENT_PATH)
112 113
		return false;

114
	/* sorry, fanotify only gives a damn about files and dirs */
115
	if (!d_is_reg(path->dentry) &&
116
	    !d_can_lookup(path->dentry))
117 118
		return false;

119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
	if (inode_mark && vfsmnt_mark) {
		marks_mask = (vfsmnt_mark->mask | inode_mark->mask);
		marks_ignored_mask = (vfsmnt_mark->ignored_mask | inode_mark->ignored_mask);
	} else if (inode_mark) {
		/*
		 * if the event is for a child and this inode doesn't care about
		 * events on the child, don't send it!
		 */
		if ((event_mask & FS_EVENT_ON_CHILD) &&
		    !(inode_mark->mask & FS_EVENT_ON_CHILD))
			return false;
		marks_mask = inode_mark->mask;
		marks_ignored_mask = inode_mark->ignored_mask;
	} else if (vfsmnt_mark) {
		marks_mask = vfsmnt_mark->mask;
		marks_ignored_mask = vfsmnt_mark->ignored_mask;
	} else {
		BUG();
	}

139
	if (d_is_dir(path->dentry) &&
140
	    !(marks_mask & FS_ISDIR & ~marks_ignored_mask))
141 142
		return false;

143 144
	if (event_mask & FAN_ALL_OUTGOING_EVENTS & marks_mask &
				 ~marks_ignored_mask)
145 146 147
		return true;

	return false;
148 149
}

150
struct fanotify_event_info *fanotify_alloc_event(struct inode *inode, u32 mask,
151
						 const struct path *path)
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
{
	struct fanotify_event_info *event;

#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
	if (mask & FAN_ALL_PERM_EVENTS) {
		struct fanotify_perm_event_info *pevent;

		pevent = kmem_cache_alloc(fanotify_perm_event_cachep,
					  GFP_KERNEL);
		if (!pevent)
			return NULL;
		event = &pevent->fae;
		pevent->response = 0;
		goto init;
	}
#endif
	event = kmem_cache_alloc(fanotify_event_cachep, GFP_KERNEL);
	if (!event)
		return NULL;
init: __maybe_unused
	fsnotify_init_event(&event->fse, inode, mask);
	event->tgid = get_pid(task_tgid(current));
	if (path) {
		event->path = *path;
		path_get(&event->path);
	} else {
		event->path.mnt = NULL;
		event->path.dentry = NULL;
	}
	return event;
}

184 185 186 187
static int fanotify_handle_event(struct fsnotify_group *group,
				 struct inode *inode,
				 struct fsnotify_mark *inode_mark,
				 struct fsnotify_mark *fanotify_mark,
188
				 u32 mask, const void *data, int data_type,
189 190
				 const unsigned char *file_name, u32 cookie,
				 struct fsnotify_iter_info *iter_info)
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
{
	int ret = 0;
	struct fanotify_event_info *event;
	struct fsnotify_event *fsn_event;

	BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS);
	BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY);
	BUILD_BUG_ON(FAN_CLOSE_NOWRITE != FS_CLOSE_NOWRITE);
	BUILD_BUG_ON(FAN_CLOSE_WRITE != FS_CLOSE_WRITE);
	BUILD_BUG_ON(FAN_OPEN != FS_OPEN);
	BUILD_BUG_ON(FAN_EVENT_ON_CHILD != FS_EVENT_ON_CHILD);
	BUILD_BUG_ON(FAN_Q_OVERFLOW != FS_Q_OVERFLOW);
	BUILD_BUG_ON(FAN_OPEN_PERM != FS_OPEN_PERM);
	BUILD_BUG_ON(FAN_ACCESS_PERM != FS_ACCESS_PERM);
	BUILD_BUG_ON(FAN_ONDIR != FS_ISDIR);

207 208 209 210
	if (!fanotify_should_send_event(inode_mark, fanotify_mark, mask, data,
					data_type))
		return 0;

211 212 213
	pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode,
		 mask);

214
	event = fanotify_alloc_event(inode, mask, data);
215 216 217 218
	if (unlikely(!event))
		return -ENOMEM;

	fsn_event = &event->fse;
219
	ret = fsnotify_add_event(group, fsn_event, fanotify_merge);
220
	if (ret) {
221 222
		/* Permission events shouldn't be merged */
		BUG_ON(ret == 1 && mask & FAN_ALL_PERM_EVENTS);
223 224
		/* Our event wasn't used in the end. Free it. */
		fsnotify_destroy_event(group, fsn_event);
225 226

		return 0;
227 228 229
	}

#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
230
	if (mask & FAN_ALL_PERM_EVENTS) {
231 232
		ret = fanotify_get_response(group, FANOTIFY_PE(fsn_event),
					    iter_info);
233 234
		fsnotify_destroy_event(group, fsn_event);
	}
235 236 237 238
#endif
	return ret;
}

239 240 241 242 243 244 245 246 247
static void fanotify_free_group_priv(struct fsnotify_group *group)
{
	struct user_struct *user;

	user = group->fanotify_data.user;
	atomic_dec(&user->fanotify_listeners);
	free_uid(user);
}

248 249 250 251 252 253 254
static void fanotify_free_event(struct fsnotify_event *fsn_event)
{
	struct fanotify_event_info *event;

	event = FANOTIFY_E(fsn_event);
	path_put(&event->path);
	put_pid(event->tgid);
255 256 257 258 259 260 261
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
	if (fsn_event->mask & FAN_ALL_PERM_EVENTS) {
		kmem_cache_free(fanotify_perm_event_cachep,
				FANOTIFY_PE(fsn_event));
		return;
	}
#endif
262 263 264
	kmem_cache_free(fanotify_event_cachep, event);
}

265 266 267 268 269
static void fanotify_free_mark(struct fsnotify_mark *fsn_mark)
{
	kmem_cache_free(fanotify_mark_cache, fsn_mark);
}

270 271
const struct fsnotify_ops fanotify_fsnotify_ops = {
	.handle_event = fanotify_handle_event,
272
	.free_group_priv = fanotify_free_group_priv,
273
	.free_event = fanotify_free_event,
274
	.free_mark = fanotify_free_mark,
275
};