fanotify_user.c 14.6 KB
Newer Older
1
#include <linux/fcntl.h>
2
#include <linux/file.h>
3
#include <linux/fs.h>
4
#include <linux/anon_inodes.h>
5
#include <linux/fsnotify_backend.h>
6
#include <linux/init.h>
Eric Paris's avatar
Eric Paris committed
7
#include <linux/mount.h>
8
#include <linux/namei.h>
Eric Paris's avatar
Eric Paris committed
9
#include <linux/poll.h>
10
11
#include <linux/security.h>
#include <linux/syscalls.h>
12
#include <linux/types.h>
Eric Paris's avatar
Eric Paris committed
13
14
15
#include <linux/uaccess.h>

#include <asm/ioctls.h>
16
17
18

#include "fanotify.h"

19
20
static struct kmem_cache *fanotify_mark_cache __read_mostly;

Eric Paris's avatar
Eric Paris committed
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
 * Get an fsnotify notification event if one exists and is small
 * enough to fit in "count". Return an error pointer if the count
 * is not large enough.
 *
 * Called with the group->notification_mutex held.
 */
static struct fsnotify_event *get_one_event(struct fsnotify_group *group,
					    size_t count)
{
	BUG_ON(!mutex_is_locked(&group->notification_mutex));

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

	if (fsnotify_notify_queue_is_empty(group))
		return NULL;

	if (FAN_EVENT_METADATA_LEN > count)
		return ERR_PTR(-EINVAL);

	/* held the notification_mutex the whole time, so this is the
	 * same event we peeked above */
	return fsnotify_remove_notify_event(group);
}

46
static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event)
Eric Paris's avatar
Eric Paris committed
47
48
49
50
51
52
{
	int client_fd;
	struct dentry *dentry;
	struct vfsmount *mnt;
	struct file *new_file;

53
	pr_debug("%s: group=%p event=%p\n", __func__, group, event);
Eric Paris's avatar
Eric Paris committed
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

	client_fd = get_unused_fd();
	if (client_fd < 0)
		return client_fd;

	if (event->data_type != FSNOTIFY_EVENT_PATH) {
		WARN_ON(1);
		put_unused_fd(client_fd);
		return -EINVAL;
	}

	/*
	 * we need a new file handle for the userspace program so it can read even if it was
	 * originally opened O_WRONLY.
	 */
	dentry = dget(event->path.dentry);
	mnt = mntget(event->path.mnt);
	/* it's possible this event was an overflow event.  in that case dentry and mnt
	 * are NULL;  That's fine, just don't call dentry open */
	if (dentry && mnt)
		new_file = dentry_open(dentry, mnt,
				       O_RDONLY | O_LARGEFILE | FMODE_NONOTIFY,
				       current_cred());
	else
		new_file = ERR_PTR(-EOVERFLOW);
	if (IS_ERR(new_file)) {
		/*
		 * we still send an event even if we can't open the file.  this
		 * can happen when say tasks are gone and we try to open their
		 * /proc files or we try to open a WRONLY file like in sysfs
		 * we just send the errno to userspace since there isn't much
		 * else we can do.
		 */
		put_unused_fd(client_fd);
		client_fd = PTR_ERR(new_file);
	} else {
		fd_install(client_fd, new_file);
	}

93
	return client_fd;
Eric Paris's avatar
Eric Paris committed
94
95
96
97
98
99
100
101
102
103
104
105
}

static ssize_t fill_event_metadata(struct fsnotify_group *group,
				   struct fanotify_event_metadata *metadata,
				   struct fsnotify_event *event)
{
	pr_debug("%s: group=%p metadata=%p event=%p\n", __func__,
		 group, metadata, event);

	metadata->event_len = FAN_EVENT_METADATA_LEN;
	metadata->vers = FANOTIFY_METADATA_VERSION;
	metadata->mask = fanotify_outgoing_mask(event->mask);
106
	metadata->pid = pid_vnr(event->tgid);
107
	metadata->fd = create_fd(group, event);
Eric Paris's avatar
Eric Paris committed
108

109
	return metadata->fd;
Eric Paris's avatar
Eric Paris committed
110
111
112
113
114
115
116
117
118
119
120
121
}

static ssize_t copy_event_to_user(struct fsnotify_group *group,
				  struct fsnotify_event *event,
				  char __user *buf)
{
	struct fanotify_event_metadata fanotify_event_metadata;
	int ret;

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

	ret = fill_event_metadata(group, &fanotify_event_metadata, event);
122
	if (ret < 0)
Eric Paris's avatar
Eric Paris committed
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
		return ret;

	if (copy_to_user(buf, &fanotify_event_metadata, FAN_EVENT_METADATA_LEN))
		return -EFAULT;

	return FAN_EVENT_METADATA_LEN;
}

/* intofiy userspace file descriptor functions */
static unsigned int fanotify_poll(struct file *file, poll_table *wait)
{
	struct fsnotify_group *group = file->private_data;
	int ret = 0;

	poll_wait(file, &group->notification_waitq, wait);
	mutex_lock(&group->notification_mutex);
	if (!fsnotify_notify_queue_is_empty(group))
		ret = POLLIN | POLLRDNORM;
	mutex_unlock(&group->notification_mutex);

	return ret;
}

static ssize_t fanotify_read(struct file *file, char __user *buf,
			     size_t count, loff_t *pos)
{
	struct fsnotify_group *group;
	struct fsnotify_event *kevent;
	char __user *start;
	int ret;
	DEFINE_WAIT(wait);

	start = buf;
	group = file->private_data;

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

	while (1) {
		prepare_to_wait(&group->notification_waitq, &wait, TASK_INTERRUPTIBLE);

		mutex_lock(&group->notification_mutex);
		kevent = get_one_event(group, count);
		mutex_unlock(&group->notification_mutex);

		if (kevent) {
			ret = PTR_ERR(kevent);
			if (IS_ERR(kevent))
				break;
			ret = copy_event_to_user(group, kevent, buf);
			fsnotify_put_event(kevent);
			if (ret < 0)
				break;
			buf += ret;
			count -= ret;
			continue;
		}

		ret = -EAGAIN;
		if (file->f_flags & O_NONBLOCK)
			break;
		ret = -EINTR;
		if (signal_pending(current))
			break;

		if (start != buf)
			break;

		schedule();
	}

	finish_wait(&group->notification_waitq, &wait);
	if (start != buf && ret != -EFAULT)
		ret = buf - start;
	return ret;
}

199
200
201
202
203
204
205
206
207
208
209
210
static int fanotify_release(struct inode *ignored, struct file *file)
{
	struct fsnotify_group *group = file->private_data;

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

	/* matches the fanotify_init->fsnotify_alloc_group */
	fsnotify_put_group(group);

	return 0;
}

Eric Paris's avatar
Eric Paris committed
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct fsnotify_group *group;
	struct fsnotify_event_holder *holder;
	void __user *p;
	int ret = -ENOTTY;
	size_t send_len = 0;

	group = file->private_data;

	p = (void __user *) arg;

	switch (cmd) {
	case FIONREAD:
		mutex_lock(&group->notification_mutex);
		list_for_each_entry(holder, &group->notification_list, event_list)
			send_len += FAN_EVENT_METADATA_LEN;
		mutex_unlock(&group->notification_mutex);
		ret = put_user(send_len, (int __user *) p);
		break;
	}

	return ret;
}

236
static const struct file_operations fanotify_fops = {
Eric Paris's avatar
Eric Paris committed
237
238
	.poll		= fanotify_poll,
	.read		= fanotify_read,
239
240
	.fasync		= NULL,
	.release	= fanotify_release,
Eric Paris's avatar
Eric Paris committed
241
242
	.unlocked_ioctl	= fanotify_ioctl,
	.compat_ioctl	= fanotify_ioctl,
243
244
};

245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
static void fanotify_free_mark(struct fsnotify_mark *fsn_mark)
{
	kmem_cache_free(fanotify_mark_cache, fsn_mark);
}

static int fanotify_find_path(int dfd, const char __user *filename,
			      struct path *path, unsigned int flags)
{
	int ret;

	pr_debug("%s: dfd=%d filename=%p flags=%x\n", __func__,
		 dfd, filename, flags);

	if (filename == NULL) {
		struct file *file;
		int fput_needed;

		ret = -EBADF;
		file = fget_light(dfd, &fput_needed);
		if (!file)
			goto out;

		ret = -ENOTDIR;
		if ((flags & FAN_MARK_ONLYDIR) &&
		    !(S_ISDIR(file->f_path.dentry->d_inode->i_mode))) {
			fput_light(file, fput_needed);
			goto out;
		}

		*path = file->f_path;
		path_get(path);
		fput_light(file, fput_needed);
	} else {
		unsigned int lookup_flags = 0;

		if (!(flags & FAN_MARK_DONT_FOLLOW))
			lookup_flags |= LOOKUP_FOLLOW;
		if (flags & FAN_MARK_ONLYDIR)
			lookup_flags |= LOOKUP_DIRECTORY;

		ret = user_path_at(dfd, filename, lookup_flags, path);
		if (ret)
			goto out;
	}

	/* you can only watch an inode if you have read permissions on it */
	ret = inode_permission(path->dentry->d_inode, MAY_READ);
	if (ret)
		path_put(path);
out:
	return ret;
}

298
299
300
301
302
303
static void fanotify_update_object_mask(struct fsnotify_group *group,
					struct inode *inode,
					struct vfsmount *mnt,
					struct fsnotify_mark *fsn_mark,
					unsigned int flags,
					__u32 mask)
304
{
305
	__u32 old_mask, new_mask;
306

307
308
	pr_debug("%s: group=%p inode=%p mnt=%p fsn_mark=%p flags=%x mask=%x\n",
		 __func__, group, inode, mnt, fsn_mark, flags, mask);
309
310

	spin_lock(&fsn_mark->lock);
311
312
313
314
315
316
317
	old_mask = fsn_mark->mask;
	if (flags & FAN_MARK_ADD)
		fsn_mark->mask |= mask;
	else if (flags & FAN_MARK_REMOVE)
		fsn_mark->mask &= ~mask;
	else
		BUG();
318
319
320
321
322
	new_mask = fsn_mark->mask;
	spin_unlock(&fsn_mark->lock);

	if (!new_mask)
		fsnotify_destroy_mark(fsn_mark);
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367

	/* we made changes to a mask, update the group mask and the object mask
	 * so things happen quickly. */
	if (old_mask != new_mask) {
		__u32 dropped, do_object, do_group;

		/* more bits in old than in new? */
		dropped = (old_mask & ~new_mask);
		/* more bits in this fsn_mark than the group? */
		do_group = (new_mask & ~group->mask);

		if (inode) {
			/* more bits in this fsn_mark than the object's mask? */
			do_object = (new_mask & ~inode->i_fsnotify_mask);
			/* update the object with this new fsn_mark */
			if (dropped || do_object)
				fsnotify_recalc_inode_mask(inode);
		} else if (mnt) {
			/* more bits in this fsn_mark than the object's mask? */
			do_object = (new_mask & ~mnt->mnt_fsnotify_mask);
			/* update the object with this new fsn_mark */
			if (dropped || do_object)
				fsnotify_recalc_vfsmount_mask(mnt);
		} else {
			BUG();
		}

		/* update the group mask with the new mask */
		if (dropped || do_group)
			fsnotify_recalc_group_mask(group);
	}
}

static int fanotify_remove_mark(struct fsnotify_group *group, struct inode *inode,
				struct vfsmount *mnt, unsigned int flags, __u32 mask)
{
	struct fsnotify_mark *fsn_mark = NULL;

	BUG_ON(inode && mnt);
	BUG_ON(!inode && !mnt);

	if (inode)
		fsn_mark = fsnotify_find_inode_mark(group, inode);
	else if (mnt)
		fsn_mark = fsnotify_find_vfsmount_mark(group, mnt);
368
	else
369
		BUG();
370

371
372
373
374
	if (!fsn_mark)
		return -ENOENT;

	fanotify_update_object_mask(group, inode, mnt, fsn_mark, flags, mask);
375

376
	/* matches the fsnotify_find_inode_mark() */
377
378
379
380
381
	fsnotify_put_mark(fsn_mark);

	return 0;
}

382
383
static struct fsnotify_mark *fanotify_add_vfsmount_mark(struct fsnotify_group *group,
							struct vfsmount *mnt)
384
385
386
{
	struct fsnotify_mark *fsn_mark;

387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
	fsn_mark = fsnotify_find_vfsmount_mark(group, mnt);
	if (!fsn_mark) {
		struct fsnotify_mark *new_fsn_mark;
		int ret;

		fsn_mark = ERR_PTR(-ENOMEM);
		new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
		if (!new_fsn_mark)
			goto out;

		fsnotify_init_mark(new_fsn_mark, fanotify_free_mark);
		ret = fsnotify_add_mark(new_fsn_mark, group, NULL, mnt, 0);
		if (ret) {
			fsn_mark = ERR_PTR(ret);
			fanotify_free_mark(new_fsn_mark);
			goto out;
		}

		fsn_mark = new_fsn_mark;
	}
out:
	return fsn_mark;
}

static struct fsnotify_mark *fanotify_add_inode_mark(struct fsnotify_group *group,
						     struct inode *inode)
{
	struct fsnotify_mark *fsn_mark;

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

418
	fsn_mark = fsnotify_find_inode_mark(group, inode);
419
420
	if (!fsn_mark) {
		struct fsnotify_mark *new_fsn_mark;
421
		int ret;
422

423
		fsn_mark = ERR_PTR(-ENOMEM);
424
425
426
427
428
		new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
		if (!new_fsn_mark)
			goto out;

		fsnotify_init_mark(new_fsn_mark, fanotify_free_mark);
429
		ret = fsnotify_add_mark(new_fsn_mark, group, inode, NULL, 0);
430
		if (ret) {
431
			fsn_mark = ERR_PTR(ret);
432
433
434
435
436
437
			fanotify_free_mark(new_fsn_mark);
			goto out;
		}

		fsn_mark = new_fsn_mark;
	}
438
439
440
out:
	return fsn_mark;
}
441

442
443
444
445
static int fanotify_add_mark(struct fsnotify_group *group, struct inode *inode,
			     struct vfsmount *mnt, unsigned int flags, __u32 mask)
{
	struct fsnotify_mark *fsn_mark;
446

447
448
	pr_debug("%s: group=%p inode=%p mnt=%p flags=%x mask=%x\n",
		 __func__, group, inode, mnt, flags, mask);
449

450
451
	BUG_ON(inode && mnt);
	BUG_ON(!inode && !mnt);
452

453
454
455
456
457
458
	if (inode)
		fsn_mark = fanotify_add_inode_mark(group, inode);
	else if (mnt)
		fsn_mark = fanotify_add_vfsmount_mark(group, mnt);
	else
		BUG();
459

460
461
462
463
	if (IS_ERR(fsn_mark))
		goto out;

	fanotify_update_object_mask(group, inode, mnt, fsn_mark, flags, mask);
464
465
466
467

	/* match the init or the find.... */
	fsnotify_put_mark(fsn_mark);
out:
468
	return PTR_ERR(fsn_mark);
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
}

static bool fanotify_mark_validate_input(int flags,
					 __u32 mask)
{
	pr_debug("%s: flags=%x mask=%x\n", __func__, flags, mask);

	/* are flags valid of this operation? */
	if (!fanotify_mark_flags_valid(flags))
		return false;
	/* is the mask valid? */
	if (!fanotify_mask_valid(mask))
		return false;
	return true;
}

485
/* fanotify syscalls */
486
487
488
SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags,
		unsigned int, priority)
{
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
	struct fsnotify_group *group;
	int f_flags, fd;

	pr_debug("%s: flags=%d event_f_flags=%d priority=%d\n",
		__func__, flags, event_f_flags, priority);

	if (event_f_flags)
		return -EINVAL;
	if (priority)
		return -EINVAL;

	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;

	if (flags & ~FAN_ALL_INIT_FLAGS)
		return -EINVAL;

	f_flags = (O_RDONLY | FMODE_NONOTIFY);
	if (flags & FAN_CLOEXEC)
		f_flags |= O_CLOEXEC;
	if (flags & FAN_NONBLOCK)
		f_flags |= O_NONBLOCK;

	/* fsnotify_alloc_group takes a ref.  Dropped in fanotify_release */
	group = fsnotify_alloc_group(&fanotify_fsnotify_ops);
	if (IS_ERR(group))
		return PTR_ERR(group);

	fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags);
	if (fd < 0)
		goto out_put_group;

	return fd;

out_put_group:
	fsnotify_put_group(group);
	return fd;
526
}
527

528
529
530
SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags,
			      __u64 mask, int dfd,
			      const char  __user * pathname)
531
{
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
	struct inode *inode;
	struct fsnotify_group *group;
	struct file *filp;
	struct path path;
	int ret, fput_needed;

	pr_debug("%s: fanotify_fd=%d flags=%x dfd=%d pathname=%p mask=%llx\n",
		 __func__, fanotify_fd, flags, dfd, pathname, mask);

	/* we only use the lower 32 bits as of right now. */
	if (mask & ((__u64)0xffffffff << 32))
		return -EINVAL;

	if (!fanotify_mark_validate_input(flags, mask))
		return -EINVAL;

	filp = fget_light(fanotify_fd, &fput_needed);
	if (unlikely(!filp))
		return -EBADF;

	/* verify that this is indeed an fanotify instance */
	ret = -EINVAL;
	if (unlikely(filp->f_op != &fanotify_fops))
		goto fput_and_out;

	ret = fanotify_find_path(dfd, pathname, &path, flags);
	if (ret)
		goto fput_and_out;

	/* inode held in place by reference to path; group by fget on fd */
	inode = path.dentry->d_inode;
	group = filp->private_data;

	/* create/update an inode mark */
566
567
568
569
570
571
572
573
574
575
	switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) {
	case FAN_MARK_ADD:
		ret = fanotify_add_mark(group, inode, NULL, flags, mask);
		break;
	case FAN_MARK_REMOVE:
		ret = fanotify_remove_mark(group, inode, NULL, flags, mask);
		break;
	default:
		ret = -EINVAL;
	}
576
577
578
579
580
581
582

	path_put(&path);
fput_and_out:
	fput_light(filp, fput_needed);
	return ret;
}

583
584
585
586
587
588
589
590
591
592
593
#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
asmlinkage long SyS_fanotify_mark(long fanotify_fd, long flags, __u64 mask,
				  long dfd, long pathname)
{
	return SYSC_fanotify_mark((int) fanotify_fd, (unsigned int) flags,
				  mask, (int) dfd,
				  (const char  __user *) pathname);
}
SYSCALL_ALIAS(sys_fanotify_mark, SyS_fanotify_mark);
#endif

594
595
596
597
598
599
600
601
602
603
/*
 * fanotify_user_setup - Our initialization function.  Note that we cannnot return
 * error because we have compiled-in VFS hooks.  So an (unlikely) failure here
 * must result in panic().
 */
static int __init fanotify_user_setup(void)
{
	fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, SLAB_PANIC);

	return 0;
604
}
605
device_initcall(fanotify_user_setup);