diff --git a/MAINTAINERS b/MAINTAINERS index 4523d6cb4c4e89e55bcee99177498528205b2939..c36f5d76e1a2e659db6c4f2e6f243df640c088d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2296,6 +2296,12 @@ S: Maintained F: Documentation/hwmon/f71805f F: drivers/hwmon/f71805f.c +FANOTIFY +M: Eric Paris +S: Maintained +F: fs/notify/fanotify/ +F: include/linux/fanotify.h + FARSYNC SYNCHRONOUS DRIVER M: Kevin Curtis W: http://www.farsite.co.uk/ diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 756566fe844909c2499f3e5a53903fd2f62f2419..85366c78cc3759b1a0027e1a3d5d0598e2f2fdec 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -165,9 +165,6 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, "mask=%x data=%p data_type=%d\n", __func__, group, to_tell, inode_mark, vfsmnt_mark, event_mask, data, data_type); - pr_debug("%s: group=%p vfsmount_mark=%p inode_mark=%p mask=%x\n", - __func__, group, vfsmnt_mark, inode_mark, event_mask); - /* sorry, fanotify only gives a damn about files and dirs */ if (!S_ISREG(to_tell->i_mode) && !S_ISDIR(to_tell->i_mode)) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 032b837fcd11912e54a01920ddf40b9506f1c8f9..5ed8e58d7bfc316f44c056e6208c3787dbc5f445 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -195,6 +195,14 @@ static int prepare_for_access_response(struct fsnotify_group *group, re->fd = fd; mutex_lock(&group->fanotify_data.access_mutex); + + if (group->fanotify_data.bypass_perm) { + mutex_unlock(&group->fanotify_data.access_mutex); + kmem_cache_free(fanotify_response_event_cache, re); + event->response = FAN_ALLOW; + return 0; + } + list_add_tail(&re->list, &group->fanotify_data.access_list); mutex_unlock(&group->fanotify_data.access_mutex); @@ -364,9 +372,28 @@ static ssize_t fanotify_write(struct file *file, const char __user *buf, size_t static int fanotify_release(struct inode *ignored, struct file *file) { struct fsnotify_group *group = file->private_data; + struct fanotify_response_event *re, *lre; pr_debug("%s: file=%p group=%p\n", __func__, file, group); +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + mutex_lock(&group->fanotify_data.access_mutex); + + group->fanotify_data.bypass_perm = true; + + list_for_each_entry_safe(re, lre, &group->fanotify_data.access_list, list) { + pr_debug("%s: found group=%p re=%p event=%p\n", __func__, group, + re, re->event); + + list_del_init(&re->list); + re->event->response = FAN_ALLOW; + + kmem_cache_free(fanotify_response_event_cache, re); + } + mutex_unlock(&group->fanotify_data.access_mutex); + + wake_up(&group->fanotify_data.access_waitq); +#endif /* matches the fanotify_init->fsnotify_alloc_group */ fsnotify_put_group(group); @@ -614,7 +641,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) __func__, flags, event_f_flags); if (!capable(CAP_SYS_ADMIN)) - return -EACCES; + return -EPERM; if (flags & ~FAN_ALL_INIT_FLAGS) return -EINVAL; diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 3970392b272264810574ca6536eed74bd3fc013d..36802420d69a94024eefde05f1eab9f6a9932241 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -148,13 +148,14 @@ static int send_to_group(struct inode *to_tell, struct vfsmount *mnt, const unsigned char *file_name, struct fsnotify_event **event) { - struct fsnotify_group *group = inode_mark->group; - __u32 inode_test_mask = (mask & ~FS_EVENT_ON_CHILD); - __u32 vfsmount_test_mask = (mask & ~FS_EVENT_ON_CHILD); + struct fsnotify_group *group = NULL; + __u32 inode_test_mask = 0; + __u32 vfsmount_test_mask = 0; - pr_debug("%s: group=%p to_tell=%p mnt=%p mark=%p mask=%x data=%p" - " data_is=%d cookie=%d event=%p\n", __func__, group, to_tell, - mnt, inode_mark, mask, data, data_is, cookie, *event); + if (unlikely(!inode_mark && !vfsmount_mark)) { + BUG(); + return 0; + } /* clear ignored on inode modification */ if (mask & FS_MODIFY) { @@ -168,18 +169,29 @@ static int send_to_group(struct inode *to_tell, struct vfsmount *mnt, /* does the inode mark tell us to do something? */ if (inode_mark) { + group = inode_mark->group; + inode_test_mask = (mask & ~FS_EVENT_ON_CHILD); inode_test_mask &= inode_mark->mask; inode_test_mask &= ~inode_mark->ignored_mask; } /* does the vfsmount_mark tell us to do something? */ if (vfsmount_mark) { + vfsmount_test_mask = (mask & ~FS_EVENT_ON_CHILD); + group = vfsmount_mark->group; vfsmount_test_mask &= vfsmount_mark->mask; vfsmount_test_mask &= ~vfsmount_mark->ignored_mask; if (inode_mark) vfsmount_test_mask &= ~inode_mark->ignored_mask; } + pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x inode_mark=%p" + " inode_test_mask=%x vfsmount_mark=%p vfsmount_test_mask=%x" + " data=%p data_is=%d cookie=%d event=%p\n", + __func__, group, to_tell, mnt, mask, inode_mark, + inode_test_mask, vfsmount_mark, vfsmount_test_mask, data, + data_is, cookie, *event); + if (!inode_test_mask && !vfsmount_test_mask) return 0; @@ -207,13 +219,12 @@ static int send_to_group(struct inode *to_tell, struct vfsmount *mnt, int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const unsigned char *file_name, u32 cookie) { - struct hlist_node *inode_node, *vfsmount_node; + struct hlist_node *inode_node = NULL, *vfsmount_node = NULL; struct fsnotify_mark *inode_mark = NULL, *vfsmount_mark = NULL; struct fsnotify_group *inode_group, *vfsmount_group; struct fsnotify_event *event = NULL; struct vfsmount *mnt; int idx, ret = 0; - bool used_inode = false, used_vfsmount = false; /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); @@ -238,57 +249,50 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, (test_mask & to_tell->i_fsnotify_mask)) inode_node = srcu_dereference(to_tell->i_fsnotify_marks.first, &fsnotify_mark_srcu); - else - inode_node = NULL; - if (mnt) { - if ((mask & FS_MODIFY) || - (test_mask & mnt->mnt_fsnotify_mask)) - vfsmount_node = srcu_dereference(mnt->mnt_fsnotify_marks.first, - &fsnotify_mark_srcu); - else - vfsmount_node = NULL; - } else { - mnt = NULL; - vfsmount_node = NULL; + if (mnt && ((mask & FS_MODIFY) || + (test_mask & mnt->mnt_fsnotify_mask))) { + vfsmount_node = srcu_dereference(mnt->mnt_fsnotify_marks.first, + &fsnotify_mark_srcu); + inode_node = srcu_dereference(to_tell->i_fsnotify_marks.first, + &fsnotify_mark_srcu); } while (inode_node || vfsmount_node) { + inode_group = vfsmount_group = NULL; + if (inode_node) { inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu), struct fsnotify_mark, i.i_list); inode_group = inode_mark->group; - } else - inode_group = (void *)-1; + } if (vfsmount_node) { vfsmount_mark = hlist_entry(srcu_dereference(vfsmount_node, &fsnotify_mark_srcu), struct fsnotify_mark, m.m_list); vfsmount_group = vfsmount_mark->group; - } else - vfsmount_group = (void *)-1; + } - if (inode_group < vfsmount_group) { + if (inode_group > vfsmount_group) { /* handle inode */ send_to_group(to_tell, NULL, inode_mark, NULL, mask, data, data_is, cookie, file_name, &event); - used_inode = true; - } else if (vfsmount_group < inode_group) { + /* we didn't use the vfsmount_mark */ + vfsmount_group = NULL; + } else if (vfsmount_group > inode_group) { send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data, data_is, cookie, file_name, &event); - used_vfsmount = true; + inode_group = NULL; } else { send_to_group(to_tell, mnt, inode_mark, vfsmount_mark, mask, data, data_is, cookie, file_name, &event); - used_vfsmount = true; - used_inode = true; } - if (used_inode) + if (inode_group) inode_node = srcu_dereference(inode_node->next, &fsnotify_mark_srcu); - if (used_vfsmount) + if (vfsmount_group) vfsmount_node = srcu_dereference(vfsmount_node->next, &fsnotify_mark_srcu); } diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index f0949a57ca9d72cc7cb47df5717ab6fef250427b..63531a6b4d2a0e42759dbe1a224a103afec1327a 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -65,14 +65,14 @@ FAN_ALL_PERM_EVENTS |\ FAN_Q_OVERFLOW) -#define FANOTIFY_METADATA_VERSION 1 +#define FANOTIFY_METADATA_VERSION 2 struct fanotify_event_metadata { __u32 event_len; __u32 vers; - __s32 fd; __u64 mask; - __s64 pid; + __s32 fd; + __s32 pid; } __attribute__ ((packed)); struct fanotify_response { @@ -95,11 +95,4 @@ struct fanotify_response { (long)(meta)->event_len >= (long)FAN_EVENT_METADATA_LEN && \ (long)(meta)->event_len <= (long)(len)) -#ifdef __KERNEL__ - -struct fanotify_wait { - struct fsnotify_event *event; - __s32 fd; -}; -#endif /* __KERNEL__ */ #endif /* _LINUX_FANOTIFY_H */ diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index ed36fb57c426b13ce2b9ec3df0574779607949f5..e40190d1687813d5c86e2a1b9e414e48fcf22595 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -156,6 +156,7 @@ struct fsnotify_group { struct mutex access_mutex; struct list_head access_list; wait_queue_head_t access_waitq; + bool bypass_perm; /* protected by access_mutex */ #endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */ int f_flags; } fanotify_data;