summaryrefslogtreecommitdiffstats
path: root/fs/notify
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2009-12-17 21:24:26 -0500
committerEric Paris <eparis@redhat.com>2010-07-28 09:58:55 -0400
commit52c923dd079df49f58016a9e56df184b132611d6 (patch)
tree29263174337bd3c2b99a7c9452e8e0708635432b /fs/notify
parent11637e4b7dc098e9a863f0a619d55ebc60f5949e (diff)
fanotify: fanotify_init syscall implementation
NAME fanotify_init - initialize an fanotify group SYNOPSIS int fanotify_init(unsigned int flags, unsigned int event_f_flags, int priority); DESCRIPTION fanotify_init() initializes a new fanotify instance and returns a file descriptor associated with the new fanotify event queue. The following values can be OR'd into the flags field: FAN_NONBLOCK Set the O_NONBLOCK file status flag on the new open file description. Using this flag saves extra calls to fcntl(2) to achieve the same result. FAN_CLOEXEC Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor. See the description of the O_CLOEXEC flag in open(2) for reasons why this may be useful. The event_f_flags argument is unused and must be set to 0 The priority argument is unused and must be set to 0 RETURN VALUE On success, this system call return a new file descriptor. On error, -1 is returned, and errno is set to indicate the error. ERRORS EINVAL An invalid value was specified in flags. EINVAL A non-zero valid was passed in event_f_flags or in priority ENFILE The system limit on the total number of file descriptors has been reached. ENOMEM Insufficient kernel memory is available. CONFORMING TO These system calls are Linux-specific. Signed-off-by: Eric Paris <eparis@redhat.com>
Diffstat (limited to 'fs/notify')
-rw-r--r--fs/notify/fanotify/fanotify.h2
-rw-r--r--fs/notify/fanotify/fanotify_user.c61
2 files changed, 62 insertions, 1 deletions
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index 50765eb30fe..dd656cfab1b 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -4,6 +4,8 @@
#include <linux/kernel.h>
#include <linux/types.h>
+extern const struct fsnotify_ops fanotify_fsnotify_ops;
+
static inline bool fanotify_mask_valid(__u32 mask)
{
if (mask & ~((__u32)FAN_ALL_INCOMING_EVENTS))
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index cf176fc7086..67c0b5e4a48 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -1,13 +1,72 @@
#include <linux/fcntl.h>
#include <linux/fs.h>
+#include <linux/anon_inodes.h>
#include <linux/fsnotify_backend.h>
#include <linux/security.h>
#include <linux/syscalls.h>
#include "fanotify.h"
+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;
+}
+
+static const struct file_operations fanotify_fops = {
+ .poll = NULL,
+ .read = NULL,
+ .fasync = NULL,
+ .release = fanotify_release,
+ .unlocked_ioctl = NULL,
+ .compat_ioctl = NULL,
+};
+
+/* fanotify syscalls */
SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags,
unsigned int, priority)
{
- return -ENOSYS;
+ 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;
}