diff options
Diffstat (limited to 'fs/notify')
| -rw-r--r-- | fs/notify/fanotify/fanotify.h | 5 | ||||
| -rw-r--r-- | fs/notify/fanotify/fanotify_user.c | 220 |
2 files changed, 221 insertions, 4 deletions
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index 59c3331a0e81..5608783c6bca 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h | |||
| @@ -30,3 +30,8 @@ static inline bool fanotify_mask_valid(__u32 mask) | |||
| 30 | return false; | 30 | return false; |
| 31 | return true; | 31 | return true; |
| 32 | } | 32 | } |
| 33 | |||
| 34 | static inline __u32 fanotify_outgoing_mask(__u32 mask) | ||
| 35 | { | ||
| 36 | return mask & FAN_ALL_OUTGOING_EVENTS; | ||
| 37 | } | ||
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index bc4fa48157f1..a99550f83f8a 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c | |||
| @@ -4,15 +4,202 @@ | |||
| 4 | #include <linux/anon_inodes.h> | 4 | #include <linux/anon_inodes.h> |
| 5 | #include <linux/fsnotify_backend.h> | 5 | #include <linux/fsnotify_backend.h> |
| 6 | #include <linux/init.h> | 6 | #include <linux/init.h> |
| 7 | #include <linux/mount.h> | ||
| 7 | #include <linux/namei.h> | 8 | #include <linux/namei.h> |
| 9 | #include <linux/poll.h> | ||
| 8 | #include <linux/security.h> | 10 | #include <linux/security.h> |
| 9 | #include <linux/syscalls.h> | 11 | #include <linux/syscalls.h> |
| 10 | #include <linux/types.h> | 12 | #include <linux/types.h> |
| 13 | #include <linux/uaccess.h> | ||
| 14 | |||
| 15 | #include <asm/ioctls.h> | ||
| 11 | 16 | ||
| 12 | #include "fanotify.h" | 17 | #include "fanotify.h" |
| 13 | 18 | ||
| 14 | static struct kmem_cache *fanotify_mark_cache __read_mostly; | 19 | static struct kmem_cache *fanotify_mark_cache __read_mostly; |
| 15 | 20 | ||
| 21 | /* | ||
| 22 | * Get an fsnotify notification event if one exists and is small | ||
| 23 | * enough to fit in "count". Return an error pointer if the count | ||
| 24 | * is not large enough. | ||
| 25 | * | ||
| 26 | * Called with the group->notification_mutex held. | ||
| 27 | */ | ||
| 28 | static struct fsnotify_event *get_one_event(struct fsnotify_group *group, | ||
| 29 | size_t count) | ||
| 30 | { | ||
| 31 | BUG_ON(!mutex_is_locked(&group->notification_mutex)); | ||
| 32 | |||
| 33 | pr_debug("%s: group=%p count=%zd\n", __func__, group, count); | ||
| 34 | |||
| 35 | if (fsnotify_notify_queue_is_empty(group)) | ||
| 36 | return NULL; | ||
| 37 | |||
| 38 | if (FAN_EVENT_METADATA_LEN > count) | ||
| 39 | return ERR_PTR(-EINVAL); | ||
| 40 | |||
| 41 | /* held the notification_mutex the whole time, so this is the | ||
| 42 | * same event we peeked above */ | ||
| 43 | return fsnotify_remove_notify_event(group); | ||
| 44 | } | ||
| 45 | |||
| 46 | static int create_and_fill_fd(struct fsnotify_group *group, | ||
| 47 | struct fanotify_event_metadata *metadata, | ||
| 48 | struct fsnotify_event *event) | ||
| 49 | { | ||
| 50 | int client_fd; | ||
| 51 | struct dentry *dentry; | ||
| 52 | struct vfsmount *mnt; | ||
| 53 | struct file *new_file; | ||
| 54 | |||
| 55 | pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, group, | ||
| 56 | metadata, event); | ||
| 57 | |||
| 58 | client_fd = get_unused_fd(); | ||
| 59 | if (client_fd < 0) | ||
| 60 | return client_fd; | ||
| 61 | |||
| 62 | if (event->data_type != FSNOTIFY_EVENT_PATH) { | ||
| 63 | WARN_ON(1); | ||
| 64 | put_unused_fd(client_fd); | ||
| 65 | return -EINVAL; | ||
| 66 | } | ||
| 67 | |||
| 68 | /* | ||
| 69 | * we need a new file handle for the userspace program so it can read even if it was | ||
| 70 | * originally opened O_WRONLY. | ||
| 71 | */ | ||
| 72 | dentry = dget(event->path.dentry); | ||
| 73 | mnt = mntget(event->path.mnt); | ||
| 74 | /* it's possible this event was an overflow event. in that case dentry and mnt | ||
| 75 | * are NULL; That's fine, just don't call dentry open */ | ||
| 76 | if (dentry && mnt) | ||
| 77 | new_file = dentry_open(dentry, mnt, | ||
| 78 | O_RDONLY | O_LARGEFILE | FMODE_NONOTIFY, | ||
| 79 | current_cred()); | ||
| 80 | else | ||
| 81 | new_file = ERR_PTR(-EOVERFLOW); | ||
| 82 | if (IS_ERR(new_file)) { | ||
| 83 | /* | ||
| 84 | * we still send an event even if we can't open the file. this | ||
| 85 | * can happen when say tasks are gone and we try to open their | ||
| 86 | * /proc files or we try to open a WRONLY file like in sysfs | ||
| 87 | * we just send the errno to userspace since there isn't much | ||
| 88 | * else we can do. | ||
| 89 | */ | ||
| 90 | put_unused_fd(client_fd); | ||
| 91 | client_fd = PTR_ERR(new_file); | ||
| 92 | } else { | ||
| 93 | fd_install(client_fd, new_file); | ||
| 94 | } | ||
| 95 | |||
| 96 | metadata->fd = client_fd; | ||
| 97 | |||
| 98 | return 0; | ||
| 99 | } | ||
| 100 | |||
| 101 | static ssize_t fill_event_metadata(struct fsnotify_group *group, | ||
| 102 | struct fanotify_event_metadata *metadata, | ||
| 103 | struct fsnotify_event *event) | ||
| 104 | { | ||
| 105 | pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, | ||
| 106 | group, metadata, event); | ||
| 107 | |||
| 108 | metadata->event_len = FAN_EVENT_METADATA_LEN; | ||
| 109 | metadata->vers = FANOTIFY_METADATA_VERSION; | ||
| 110 | metadata->mask = fanotify_outgoing_mask(event->mask); | ||
| 111 | |||
| 112 | return create_and_fill_fd(group, metadata, event); | ||
| 113 | |||
| 114 | } | ||
| 115 | |||
| 116 | static ssize_t copy_event_to_user(struct fsnotify_group *group, | ||
| 117 | struct fsnotify_event *event, | ||
| 118 | char __user *buf) | ||
| 119 | { | ||
| 120 | struct fanotify_event_metadata fanotify_event_metadata; | ||
| 121 | int ret; | ||
| 122 | |||
| 123 | pr_debug("%s: group=%p event=%p\n", __func__, group, event); | ||
| 124 | |||
| 125 | ret = fill_event_metadata(group, &fanotify_event_metadata, event); | ||
| 126 | if (ret) | ||
| 127 | return ret; | ||
| 128 | |||
| 129 | if (copy_to_user(buf, &fanotify_event_metadata, FAN_EVENT_METADATA_LEN)) | ||
| 130 | return -EFAULT; | ||
| 131 | |||
| 132 | return FAN_EVENT_METADATA_LEN; | ||
| 133 | } | ||
| 134 | |||
| 135 | /* intofiy userspace file descriptor functions */ | ||
| 136 | static unsigned int fanotify_poll(struct file *file, poll_table *wait) | ||
| 137 | { | ||
| 138 | struct fsnotify_group *group = file->private_data; | ||
| 139 | int ret = 0; | ||
| 140 | |||
| 141 | poll_wait(file, &group->notification_waitq, wait); | ||
| 142 | mutex_lock(&group->notification_mutex); | ||
| 143 | if (!fsnotify_notify_queue_is_empty(group)) | ||
| 144 | ret = POLLIN | POLLRDNORM; | ||
| 145 | mutex_unlock(&group->notification_mutex); | ||
| 146 | |||
| 147 | return ret; | ||
| 148 | } | ||
| 149 | |||
| 150 | static ssize_t fanotify_read(struct file *file, char __user *buf, | ||
| 151 | size_t count, loff_t *pos) | ||
| 152 | { | ||
| 153 | struct fsnotify_group *group; | ||
| 154 | struct fsnotify_event *kevent; | ||
| 155 | char __user *start; | ||
| 156 | int ret; | ||
| 157 | DEFINE_WAIT(wait); | ||
| 158 | |||
| 159 | start = buf; | ||
| 160 | group = file->private_data; | ||
| 161 | |||
| 162 | pr_debug("%s: group=%p\n", __func__, group); | ||
| 163 | |||
| 164 | while (1) { | ||
| 165 | prepare_to_wait(&group->notification_waitq, &wait, TASK_INTERRUPTIBLE); | ||
| 166 | |||
| 167 | mutex_lock(&group->notification_mutex); | ||
| 168 | kevent = get_one_event(group, count); | ||
| 169 | mutex_unlock(&group->notification_mutex); | ||
| 170 | |||
| 171 | if (kevent) { | ||
| 172 | ret = PTR_ERR(kevent); | ||
| 173 | if (IS_ERR(kevent)) | ||
| 174 | break; | ||
| 175 | ret = copy_event_to_user(group, kevent, buf); | ||
| 176 | fsnotify_put_event(kevent); | ||
| 177 | if (ret < 0) | ||
| 178 | break; | ||
| 179 | buf += ret; | ||
| 180 | count -= ret; | ||
| 181 | continue; | ||
| 182 | } | ||
| 183 | |||
| 184 | ret = -EAGAIN; | ||
| 185 | if (file->f_flags & O_NONBLOCK) | ||
| 186 | break; | ||
| 187 | ret = -EINTR; | ||
| 188 | if (signal_pending(current)) | ||
| 189 | break; | ||
| 190 | |||
| 191 | if (start != buf) | ||
| 192 | break; | ||
| 193 | |||
| 194 | schedule(); | ||
| 195 | } | ||
| 196 | |||
| 197 | finish_wait(&group->notification_waitq, &wait); | ||
| 198 | if (start != buf && ret != -EFAULT) | ||
| 199 | ret = buf - start; | ||
| 200 | return ret; | ||
| 201 | } | ||
| 202 | |||
| 16 | static int fanotify_release(struct inode *ignored, struct file *file) | 203 | static int fanotify_release(struct inode *ignored, struct file *file) |
| 17 | { | 204 | { |
| 18 | struct fsnotify_group *group = file->private_data; | 205 | struct fsnotify_group *group = file->private_data; |
| @@ -25,13 +212,38 @@ static int fanotify_release(struct inode *ignored, struct file *file) | |||
| 25 | return 0; | 212 | return 0; |
| 26 | } | 213 | } |
| 27 | 214 | ||
| 215 | static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
| 216 | { | ||
| 217 | struct fsnotify_group *group; | ||
| 218 | struct fsnotify_event_holder *holder; | ||
| 219 | void __user *p; | ||
| 220 | int ret = -ENOTTY; | ||
| 221 | size_t send_len = 0; | ||
| 222 | |||
| 223 | group = file->private_data; | ||
| 224 | |||
| 225 | p = (void __user *) arg; | ||
| 226 | |||
| 227 | switch (cmd) { | ||
| 228 | case FIONREAD: | ||
| 229 | mutex_lock(&group->notification_mutex); | ||
| 230 | list_for_each_entry(holder, &group->notification_list, event_list) | ||
| 231 | send_len += FAN_EVENT_METADATA_LEN; | ||
| 232 | mutex_unlock(&group->notification_mutex); | ||
| 233 | ret = put_user(send_len, (int __user *) p); | ||
| 234 | break; | ||
| 235 | } | ||
| 236 | |||
| 237 | return ret; | ||
| 238 | } | ||
| 239 | |||
| 28 | static const struct file_operations fanotify_fops = { | 240 | static const struct file_operations fanotify_fops = { |
| 29 | .poll = NULL, | 241 | .poll = fanotify_poll, |
| 30 | .read = NULL, | 242 | .read = fanotify_read, |
| 31 | .fasync = NULL, | 243 | .fasync = NULL, |
| 32 | .release = fanotify_release, | 244 | .release = fanotify_release, |
| 33 | .unlocked_ioctl = NULL, | 245 | .unlocked_ioctl = fanotify_ioctl, |
| 34 | .compat_ioctl = NULL, | 246 | .compat_ioctl = fanotify_ioctl, |
| 35 | }; | 247 | }; |
| 36 | 248 | ||
| 37 | static void fanotify_free_mark(struct fsnotify_mark *fsn_mark) | 249 | static void fanotify_free_mark(struct fsnotify_mark *fsn_mark) |
