summaryrefslogtreecommitdiffstats
path: root/fs/notify
diff options
context:
space:
mode:
authorAmir Goldstein <amir73il@gmail.com>2019-01-10 12:04:34 -0500
committerJan Kara <jack@suse.cz>2019-02-07 10:38:34 -0500
commite9e0c8903009477b630e37a8b6364b26a00720da (patch)
tree26abd52b6da51386edcea7f96237f3cdd803fc62 /fs/notify
parentbb2f7b4542c7a1d023d516af37dc70bb49db0438 (diff)
fanotify: encode file identifier for FAN_REPORT_FID
When user requests the flag FAN_REPORT_FID in fanotify_init(), a unique file identifier of the event target object will be reported with the event. The file identifier includes the filesystem's fsid (i.e. from statfs(2)) and an NFS file handle of the file (i.e. from name_to_handle_at(2)). The file identifier makes holding the path reference and passing a file descriptor to user redundant, so those are disabled in a group with FAN_REPORT_FID. Encode fid and store it in event for a group with FAN_REPORT_FID. Up to 12 bytes of file handle on 32bit arch (16 bytes on 64bit arch) are stored inline in fanotify_event struct. Larger file handles are stored in an external allocated buffer. On failure to encode fid, we print a warning and queue the event without the fid information. [JK: Fold part of later patched into this one to use exportfs_encode_inode_fh() right away] Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/notify')
-rw-r--r--fs/notify/fanotify/fanotify.c84
-rw-r--r--fs/notify/fanotify/fanotify.h78
-rw-r--r--fs/notify/fanotify/fanotify_user.c6
3 files changed, 155 insertions, 13 deletions
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index d8e3b6e50844..dd33227e518a 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -13,6 +13,7 @@
13#include <linux/wait.h> 13#include <linux/wait.h>
14#include <linux/audit.h> 14#include <linux/audit.h>
15#include <linux/sched/mm.h> 15#include <linux/sched/mm.h>
16#include <linux/statfs.h>
16 17
17#include "fanotify.h" 18#include "fanotify.h"
18 19
@@ -25,10 +26,18 @@ static bool should_merge(struct fsnotify_event *old_fsn,
25 old = FANOTIFY_E(old_fsn); 26 old = FANOTIFY_E(old_fsn);
26 new = FANOTIFY_E(new_fsn); 27 new = FANOTIFY_E(new_fsn);
27 28
28 if (old_fsn->inode == new_fsn->inode && old->pid == new->pid && 29 if (old_fsn->inode != new_fsn->inode || old->pid != new->pid ||
29 old->path.mnt == new->path.mnt && 30 old->fh_type != new->fh_type || old->fh_len != new->fh_len)
30 old->path.dentry == new->path.dentry) 31 return false;
31 return true; 32
33 if (fanotify_event_has_path(old)) {
34 return old->path.mnt == new->path.mnt &&
35 old->path.dentry == new->path.dentry;
36 } else if (fanotify_event_has_fid(old)) {
37 return fanotify_fid_equal(&old->fid, &new->fid, old->fh_len);
38 }
39
40 /* Do not merge events if we failed to encode fid */
32 return false; 41 return false;
33} 42}
34 43
@@ -143,6 +152,60 @@ static u32 fanotify_group_event_mask(struct fsnotify_iter_info *iter_info,
143 ~marks_ignored_mask; 152 ~marks_ignored_mask;
144} 153}
145 154
155static int fanotify_encode_fid(struct fanotify_event *event,
156 const struct path *path, gfp_t gfp)
157{
158 struct fanotify_fid *fid = &event->fid;
159 int dwords, bytes = 0;
160 struct kstatfs stat;
161 int err, type;
162
163 stat.f_fsid.val[0] = stat.f_fsid.val[1] = 0;
164 fid->ext_fh = NULL;
165 dwords = 0;
166 err = -ENOENT;
167 type = exportfs_encode_inode_fh(d_inode(path->dentry), NULL, &dwords,
168 NULL);
169 if (!dwords)
170 goto out_err;
171
172 err = vfs_statfs(path, &stat);
173 if (err)
174 goto out_err;
175
176 bytes = dwords << 2;
177 if (bytes > FANOTIFY_INLINE_FH_LEN) {
178 /* Treat failure to allocate fh as failure to allocate event */
179 err = -ENOMEM;
180 fid->ext_fh = kmalloc(bytes, gfp);
181 if (!fid->ext_fh)
182 goto out_err;
183 }
184
185 type = exportfs_encode_inode_fh(d_inode(path->dentry),
186 fanotify_fid_fh(fid, bytes), &dwords,
187 NULL);
188 err = -EINVAL;
189 if (!type || type == FILEID_INVALID || bytes != dwords << 2)
190 goto out_err;
191
192 fid->fsid = stat.f_fsid;
193 event->fh_len = bytes;
194
195 return type;
196
197out_err:
198 pr_warn_ratelimited("fanotify: failed to encode fid (fsid=%x.%x, "
199 "type=%d, bytes=%d, err=%i)\n",
200 stat.f_fsid.val[0], stat.f_fsid.val[1],
201 type, bytes, err);
202 kfree(fid->ext_fh);
203 fid->ext_fh = NULL;
204 event->fh_len = 0;
205
206 return FILEID_INVALID;
207}
208
146struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group, 209struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
147 struct inode *inode, u32 mask, 210 struct inode *inode, u32 mask,
148 const struct path *path) 211 const struct path *path)
@@ -181,10 +244,16 @@ init: __maybe_unused
181 event->pid = get_pid(task_pid(current)); 244 event->pid = get_pid(task_pid(current));
182 else 245 else
183 event->pid = get_pid(task_tgid(current)); 246 event->pid = get_pid(task_tgid(current));
184 if (path) { 247 event->fh_len = 0;
248 if (path && FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
249 /* Report the event without a file identifier on encode error */
250 event->fh_type = fanotify_encode_fid(event, path, gfp);
251 } else if (path) {
252 event->fh_type = FILEID_ROOT;
185 event->path = *path; 253 event->path = *path;
186 path_get(&event->path); 254 path_get(&event->path);
187 } else { 255 } else {
256 event->fh_type = FILEID_INVALID;
188 event->path.mnt = NULL; 257 event->path.mnt = NULL;
189 event->path.dentry = NULL; 258 event->path.dentry = NULL;
190 } 259 }
@@ -281,7 +350,10 @@ static void fanotify_free_event(struct fsnotify_event *fsn_event)
281 struct fanotify_event *event; 350 struct fanotify_event *event;
282 351
283 event = FANOTIFY_E(fsn_event); 352 event = FANOTIFY_E(fsn_event);
284 path_put(&event->path); 353 if (fanotify_event_has_path(event))
354 path_put(&event->path);
355 else if (fanotify_event_has_ext_fh(event))
356 kfree(event->fid.ext_fh);
285 put_pid(event->pid); 357 put_pid(event->pid);
286 if (fanotify_is_perm_event(event->mask)) { 358 if (fanotify_is_perm_event(event->mask)) {
287 kmem_cache_free(fanotify_perm_event_cachep, 359 kmem_cache_free(fanotify_perm_event_cachep,
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index 898b5b2bc1c7..271482fb9611 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -2,12 +2,50 @@
2#include <linux/fsnotify_backend.h> 2#include <linux/fsnotify_backend.h>
3#include <linux/path.h> 3#include <linux/path.h>
4#include <linux/slab.h> 4#include <linux/slab.h>
5#include <linux/exportfs.h>
5 6
6extern struct kmem_cache *fanotify_mark_cache; 7extern struct kmem_cache *fanotify_mark_cache;
7extern struct kmem_cache *fanotify_event_cachep; 8extern struct kmem_cache *fanotify_event_cachep;
8extern struct kmem_cache *fanotify_perm_event_cachep; 9extern struct kmem_cache *fanotify_perm_event_cachep;
9 10
10/* 11/*
12 * 3 dwords are sufficient for most local fs (64bit ino, 32bit generation).
13 * For 32bit arch, fid increases the size of fanotify_event by 12 bytes and
14 * fh_* fields increase the size of fanotify_event by another 4 bytes.
15 * For 64bit arch, fid increases the size of fanotify_fid by 8 bytes and
16 * fh_* fields are packed in a hole after mask.
17 */
18#if BITS_PER_LONG == 32
19#define FANOTIFY_INLINE_FH_LEN (3 << 2)
20#else
21#define FANOTIFY_INLINE_FH_LEN (4 << 2)
22#endif
23
24struct fanotify_fid {
25 __kernel_fsid_t fsid;
26 union {
27 unsigned char fh[FANOTIFY_INLINE_FH_LEN];
28 unsigned char *ext_fh;
29 };
30};
31
32static inline void *fanotify_fid_fh(struct fanotify_fid *fid,
33 unsigned int fh_len)
34{
35 return fh_len <= FANOTIFY_INLINE_FH_LEN ? fid->fh : fid->ext_fh;
36}
37
38static inline bool fanotify_fid_equal(struct fanotify_fid *fid1,
39 struct fanotify_fid *fid2,
40 unsigned int fh_len)
41{
42 return fid1->fsid.val[0] == fid2->fsid.val[0] &&
43 fid1->fsid.val[1] == fid2->fsid.val[1] &&
44 !memcmp(fanotify_fid_fh(fid1, fh_len),
45 fanotify_fid_fh(fid2, fh_len), fh_len);
46}
47
48/*
11 * Structure for normal fanotify events. It gets allocated in 49 * Structure for normal fanotify events. It gets allocated in
12 * fanotify_handle_event() and freed when the information is retrieved by 50 * fanotify_handle_event() and freed when the information is retrieved by
13 * userspace 51 * userspace
@@ -16,13 +54,47 @@ struct fanotify_event {
16 struct fsnotify_event fse; 54 struct fsnotify_event fse;
17 u32 mask; 55 u32 mask;
18 /* 56 /*
19 * We hold ref to this path so it may be dereferenced at any point 57 * Those fields are outside fanotify_fid to pack fanotify_event nicely
20 * during this object's lifetime 58 * on 64bit arch and to use fh_type as an indication of whether path
59 * or fid are used in the union:
60 * FILEID_ROOT (0) for path, > 0 for fid, FILEID_INVALID for neither.
21 */ 61 */
22 struct path path; 62 u8 fh_type;
63 u8 fh_len;
64 u16 pad;
65 union {
66 /*
67 * We hold ref to this path so it may be dereferenced at any
68 * point during this object's lifetime
69 */
70 struct path path;
71 /*
72 * With FAN_REPORT_FID, we do not hold any reference on the
73 * victim object. Instead we store its NFS file handle and its
74 * filesystem's fsid as a unique identifier.
75 */
76 struct fanotify_fid fid;
77 };
23 struct pid *pid; 78 struct pid *pid;
24}; 79};
25 80
81static inline bool fanotify_event_has_path(struct fanotify_event *event)
82{
83 return event->fh_type == FILEID_ROOT;
84}
85
86static inline bool fanotify_event_has_fid(struct fanotify_event *event)
87{
88 return event->fh_type != FILEID_ROOT &&
89 event->fh_type != FILEID_INVALID;
90}
91
92static inline bool fanotify_event_has_ext_fh(struct fanotify_event *event)
93{
94 return fanotify_event_has_fid(event) &&
95 event->fh_len > FANOTIFY_INLINE_FH_LEN;
96}
97
26/* 98/*
27 * Structure for permission fanotify events. It gets allocated and freed in 99 * Structure for permission fanotify events. It gets allocated and freed in
28 * fanotify_handle_event() since we wait there for user response. When the 100 * fanotify_handle_event() since we wait there for user response. When the
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 096503bd0edb..c965fcf4979e 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -181,7 +181,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
181 struct fanotify_event_metadata metadata; 181 struct fanotify_event_metadata metadata;
182 struct fanotify_event *event; 182 struct fanotify_event *event;
183 struct file *f = NULL; 183 struct file *f = NULL;
184 int fd, ret; 184 int ret, fd = FAN_NOFD;
185 185
186 pr_debug("%s: group=%p event=%p\n", __func__, group, fsn_event); 186 pr_debug("%s: group=%p event=%p\n", __func__, group, fsn_event);
187 187
@@ -193,9 +193,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
193 metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS; 193 metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
194 metadata.pid = pid_vnr(event->pid); 194 metadata.pid = pid_vnr(event->pid);
195 195
196 if (unlikely(event->mask & FAN_Q_OVERFLOW)) { 196 if (fanotify_event_has_path(event)) {
197 fd = FAN_NOFD;
198 } else {
199 fd = create_fd(group, event, &f); 197 fd = create_fd(group, event, &f);
200 if (fd < 0) 198 if (fd < 0)
201 return fd; 199 return fd;