linux/fs/notify/fsnotify.c
<<
>>
Prefs
   1/*
   2 *  Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com>
   3 *
   4 *  This program is free software; you can redistribute it and/or modify
   5 *  it under the terms of the GNU General Public License as published by
   6 *  the Free Software Foundation; either version 2, or (at your option)
   7 *  any later version.
   8 *
   9 *  This program is distributed in the hope that it will be useful,
  10 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 *  GNU General Public License for more details.
  13 *
  14 *  You should have received a copy of the GNU General Public License
  15 *  along with this program; see the file COPYING.  If not, write to
  16 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  17 */
  18
  19#include <linux/dcache.h>
  20#include <linux/fs.h>
  21#include <linux/gfp.h>
  22#include <linux/init.h>
  23#include <linux/module.h>
  24#include <linux/mount.h>
  25#include <linux/srcu.h>
  26
  27#include <linux/fsnotify_backend.h>
  28#include "fsnotify.h"
  29
  30/*
  31 * Clear all of the marks on an inode when it is being evicted from core
  32 */
  33void __fsnotify_inode_delete(struct inode *inode)
  34{
  35        fsnotify_clear_marks_by_inode(inode);
  36}
  37EXPORT_SYMBOL_GPL(__fsnotify_inode_delete);
  38
  39void __fsnotify_vfsmount_delete(struct vfsmount *mnt)
  40{
  41        fsnotify_clear_marks_by_mount(mnt);
  42}
  43
  44/*
  45 * Given an inode, first check if we care what happens to our children.  Inotify
  46 * and dnotify both tell their parents about events.  If we care about any event
  47 * on a child we run all of our children and set a dentry flag saying that the
  48 * parent cares.  Thus when an event happens on a child it can quickly tell if
  49 * if there is a need to find a parent and send the event to the parent.
  50 */
  51void __fsnotify_update_child_dentry_flags(struct inode *inode)
  52{
  53        struct dentry *alias;
  54        int watched;
  55
  56        if (!S_ISDIR(inode->i_mode))
  57                return;
  58
  59        /* determine if the children should tell inode about their events */
  60        watched = fsnotify_inode_watches_children(inode);
  61
  62        spin_lock(&inode->i_lock);
  63        /* run all of the dentries associated with this inode.  Since this is a
  64         * directory, there damn well better only be one item on this list */
  65        list_for_each_entry(alias, &inode->i_dentry, d_alias) {
  66                struct dentry *child;
  67
  68                /* run all of the children of the original inode and fix their
  69                 * d_flags to indicate parental interest (their parent is the
  70                 * original inode) */
  71                spin_lock(&alias->d_lock);
  72                list_for_each_entry(child, &alias->d_subdirs, d_u.d_child) {
  73                        if (!child->d_inode)
  74                                continue;
  75
  76                        spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
  77                        if (watched)
  78                                child->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED;
  79                        else
  80                                child->d_flags &= ~DCACHE_FSNOTIFY_PARENT_WATCHED;
  81                        spin_unlock(&child->d_lock);
  82                }
  83                spin_unlock(&alias->d_lock);
  84        }
  85        spin_unlock(&inode->i_lock);
  86}
  87
  88/* Notify this dentry's parent about a child's events. */
  89int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
  90{
  91        struct dentry *parent;
  92        struct inode *p_inode;
  93        int ret = 0;
  94
  95        if (!dentry)
  96                dentry = path->dentry;
  97
  98        if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED))
  99                return 0;
 100
 101        parent = dget_parent(dentry);
 102        p_inode = parent->d_inode;
 103
 104        if (unlikely(!fsnotify_inode_watches_children(p_inode)))
 105                __fsnotify_update_child_dentry_flags(p_inode);
 106        else if (p_inode->i_fsnotify_mask & mask) {
 107                /* we are notifying a parent so come up with the new mask which
 108                 * specifies these are events which came from a child. */
 109                mask |= FS_EVENT_ON_CHILD;
 110
 111                if (path)
 112                        ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
 113                                       dentry->d_name.name, 0);
 114                else
 115                        ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
 116                                       dentry->d_name.name, 0);
 117        }
 118
 119        dput(parent);
 120
 121        return ret;
 122}
 123EXPORT_SYMBOL_GPL(__fsnotify_parent);
 124
 125static int send_to_group(struct inode *to_tell, struct vfsmount *mnt,
 126                         struct fsnotify_mark *inode_mark,
 127                         struct fsnotify_mark *vfsmount_mark,
 128                         __u32 mask, void *data,
 129                         int data_is, u32 cookie,
 130                         const unsigned char *file_name,
 131                         struct fsnotify_event **event)
 132{
 133        struct fsnotify_group *group = NULL;
 134        __u32 inode_test_mask = 0;
 135        __u32 vfsmount_test_mask = 0;
 136
 137        if (unlikely(!inode_mark && !vfsmount_mark)) {
 138                BUG();
 139                return 0;
 140        }
 141
 142        /* clear ignored on inode modification */
 143        if (mask & FS_MODIFY) {
 144                if (inode_mark &&
 145                    !(inode_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
 146                        inode_mark->ignored_mask = 0;
 147                if (vfsmount_mark &&
 148                    !(vfsmount_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
 149                        vfsmount_mark->ignored_mask = 0;
 150        }
 151
 152        /* does the inode mark tell us to do something? */
 153        if (inode_mark) {
 154                group = inode_mark->group;
 155                inode_test_mask = (mask & ~FS_EVENT_ON_CHILD);
 156                inode_test_mask &= inode_mark->mask;
 157                inode_test_mask &= ~inode_mark->ignored_mask;
 158        }
 159
 160        /* does the vfsmount_mark tell us to do something? */
 161        if (vfsmount_mark) {
 162                vfsmount_test_mask = (mask & ~FS_EVENT_ON_CHILD);
 163                group = vfsmount_mark->group;
 164                vfsmount_test_mask &= vfsmount_mark->mask;
 165                vfsmount_test_mask &= ~vfsmount_mark->ignored_mask;
 166                if (inode_mark)
 167                        vfsmount_test_mask &= ~inode_mark->ignored_mask;
 168        }
 169
 170        pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x inode_mark=%p"
 171                 " inode_test_mask=%x vfsmount_mark=%p vfsmount_test_mask=%x"
 172                 " data=%p data_is=%d cookie=%d event=%p\n",
 173                 __func__, group, to_tell, mnt, mask, inode_mark,
 174                 inode_test_mask, vfsmount_mark, vfsmount_test_mask, data,
 175                 data_is, cookie, *event);
 176
 177        if (!inode_test_mask && !vfsmount_test_mask)
 178                return 0;
 179
 180        if (group->ops->should_send_event(group, to_tell, inode_mark,
 181                                          vfsmount_mark, mask, data,
 182                                          data_is) == false)
 183                return 0;
 184
 185        if (!*event) {
 186                *event = fsnotify_create_event(to_tell, mask, data,
 187                                                data_is, file_name,
 188                                                cookie, GFP_KERNEL);
 189                if (!*event)
 190                        return -ENOMEM;
 191        }
 192        return group->ops->handle_event(group, inode_mark, vfsmount_mark, *event);
 193}
 194
 195/*
 196 * This is the main call to fsnotify.  The VFS calls into hook specific functions
 197 * in linux/fsnotify.h.  Those functions then in turn call here.  Here will call
 198 * out to all of the registered fsnotify_group.  Those groups can then use the
 199 * notification event in whatever means they feel necessary.
 200 */
 201int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
 202             const unsigned char *file_name, u32 cookie)
 203{
 204        struct hlist_node *inode_node = NULL, *vfsmount_node = NULL;
 205        struct fsnotify_mark *inode_mark = NULL, *vfsmount_mark = NULL;
 206        struct fsnotify_group *inode_group, *vfsmount_group;
 207        struct fsnotify_event *event = NULL;
 208        struct vfsmount *mnt;
 209        int idx, ret = 0;
 210        /* global tests shouldn't care about events on child only the specific event */
 211        __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD);
 212
 213        if (data_is == FSNOTIFY_EVENT_PATH)
 214                mnt = ((struct path *)data)->mnt;
 215        else
 216                mnt = NULL;
 217
 218        /*
 219         * if this is a modify event we may need to clear the ignored masks
 220         * otherwise return if neither the inode nor the vfsmount care about
 221         * this type of event.
 222         */
 223        if (!(mask & FS_MODIFY) &&
 224            !(test_mask & to_tell->i_fsnotify_mask) &&
 225            !(mnt && test_mask & mnt->mnt_fsnotify_mask))
 226                return 0;
 227
 228        idx = srcu_read_lock(&fsnotify_mark_srcu);
 229
 230        if ((mask & FS_MODIFY) ||
 231            (test_mask & to_tell->i_fsnotify_mask))
 232                inode_node = srcu_dereference(to_tell->i_fsnotify_marks.first,
 233                                              &fsnotify_mark_srcu);
 234
 235        if (mnt && ((mask & FS_MODIFY) ||
 236                    (test_mask & mnt->mnt_fsnotify_mask))) {
 237                vfsmount_node = srcu_dereference(mnt->mnt_fsnotify_marks.first,
 238                                                 &fsnotify_mark_srcu);
 239                inode_node = srcu_dereference(to_tell->i_fsnotify_marks.first,
 240                                              &fsnotify_mark_srcu);
 241        }
 242
 243        while (inode_node || vfsmount_node) {
 244                inode_group = vfsmount_group = NULL;
 245
 246                if (inode_node) {
 247                        inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu),
 248                                                 struct fsnotify_mark, i.i_list);
 249                        inode_group = inode_mark->group;
 250                }
 251
 252                if (vfsmount_node) {
 253                        vfsmount_mark = hlist_entry(srcu_dereference(vfsmount_node, &fsnotify_mark_srcu),
 254                                                        struct fsnotify_mark, m.m_list);
 255                        vfsmount_group = vfsmount_mark->group;
 256                }
 257
 258                if (inode_group > vfsmount_group) {
 259                        /* handle inode */
 260                        ret = send_to_group(to_tell, NULL, inode_mark, NULL, mask, data,
 261                                            data_is, cookie, file_name, &event);
 262                        /* we didn't use the vfsmount_mark */
 263                        vfsmount_group = NULL;
 264                } else if (vfsmount_group > inode_group) {
 265                        ret = send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data,
 266                                            data_is, cookie, file_name, &event);
 267                        inode_group = NULL;
 268                } else {
 269                        ret = send_to_group(to_tell, mnt, inode_mark, vfsmount_mark,
 270                                            mask, data, data_is, cookie, file_name,
 271                                            &event);
 272                }
 273
 274                if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS))
 275                        goto out;
 276
 277                if (inode_group)
 278                        inode_node = srcu_dereference(inode_node->next,
 279                                                      &fsnotify_mark_srcu);
 280                if (vfsmount_group)
 281                        vfsmount_node = srcu_dereference(vfsmount_node->next,
 282                                                         &fsnotify_mark_srcu);
 283        }
 284        ret = 0;
 285out:
 286        srcu_read_unlock(&fsnotify_mark_srcu, idx);
 287        /*
 288         * fsnotify_create_event() took a reference so the event can't be cleaned
 289         * up while we are still trying to add it to lists, drop that one.
 290         */
 291        if (event)
 292                fsnotify_put_event(event);
 293
 294        return ret;
 295}
 296EXPORT_SYMBOL_GPL(fsnotify);
 297
 298static __init int fsnotify_init(void)
 299{
 300        int ret;
 301
 302        BUG_ON(hweight32(ALL_FSNOTIFY_EVENTS) != 23);
 303
 304        ret = init_srcu_struct(&fsnotify_mark_srcu);
 305        if (ret)
 306                panic("initializing fsnotify_mark_srcu");
 307
 308        return 0;
 309}
 310core_initcall(fsnotify_init);
 311