linux/fs/dnotify.c
<<
>>
Prefs
   1/*
   2 * Directory notifications for Linux.
   3 *
   4 * Copyright (C) 2000,2001,2002 Stephen Rothwell
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms of the GNU General Public License as published by the
   8 * Free Software Foundation; either version 2, or (at your option) any
   9 * later version.
  10 *
  11 * This program is distributed in the hope that it will be useful, but
  12 * WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * General Public License for more details.
  15 */
  16#include <linux/fs.h>
  17#include <linux/module.h>
  18#include <linux/sched.h>
  19#include <linux/dnotify.h>
  20#include <linux/init.h>
  21#include <linux/spinlock.h>
  22#include <linux/slab.h>
  23
  24int dir_notify_enable __read_mostly = 1;
  25
  26static struct kmem_cache *dn_cache __read_mostly;
  27
  28static void redo_inode_mask(struct inode *inode)
  29{
  30        unsigned long new_mask;
  31        struct dnotify_struct *dn;
  32
  33        new_mask = 0;
  34        for (dn = inode->i_dnotify; dn != NULL; dn = dn->dn_next)
  35                new_mask |= dn->dn_mask & ~DN_MULTISHOT;
  36        inode->i_dnotify_mask = new_mask;
  37}
  38
  39void dnotify_flush(struct file *filp, fl_owner_t id)
  40{
  41        struct dnotify_struct *dn;
  42        struct dnotify_struct **prev;
  43        struct inode *inode;
  44
  45        inode = filp->f_path.dentry->d_inode;
  46        if (!S_ISDIR(inode->i_mode))
  47                return;
  48        spin_lock(&inode->i_lock);
  49        prev = &inode->i_dnotify;
  50        while ((dn = *prev) != NULL) {
  51                if ((dn->dn_owner == id) && (dn->dn_filp == filp)) {
  52                        *prev = dn->dn_next;
  53                        redo_inode_mask(inode);
  54                        kmem_cache_free(dn_cache, dn);
  55                        break;
  56                }
  57                prev = &dn->dn_next;
  58        }
  59        spin_unlock(&inode->i_lock);
  60}
  61
  62int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
  63{
  64        struct dnotify_struct *dn;
  65        struct dnotify_struct *odn;
  66        struct dnotify_struct **prev;
  67        struct inode *inode;
  68        fl_owner_t id = current->files;
  69        int error = 0;
  70
  71        if ((arg & ~DN_MULTISHOT) == 0) {
  72                dnotify_flush(filp, id);
  73                return 0;
  74        }
  75        if (!dir_notify_enable)
  76                return -EINVAL;
  77        inode = filp->f_path.dentry->d_inode;
  78        if (!S_ISDIR(inode->i_mode))
  79                return -ENOTDIR;
  80        dn = kmem_cache_alloc(dn_cache, GFP_KERNEL);
  81        if (dn == NULL)
  82                return -ENOMEM;
  83        spin_lock(&inode->i_lock);
  84        prev = &inode->i_dnotify;
  85        while ((odn = *prev) != NULL) {
  86                if ((odn->dn_owner == id) && (odn->dn_filp == filp)) {
  87                        odn->dn_fd = fd;
  88                        odn->dn_mask |= arg;
  89                        inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
  90                        goto out_free;
  91                }
  92                prev = &odn->dn_next;
  93        }
  94
  95        error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
  96        if (error)
  97                goto out_free;
  98
  99        dn->dn_mask = arg;
 100        dn->dn_fd = fd;
 101        dn->dn_filp = filp;
 102        dn->dn_owner = id;
 103        inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
 104        dn->dn_next = inode->i_dnotify;
 105        inode->i_dnotify = dn;
 106        spin_unlock(&inode->i_lock);
 107
 108        if (filp->f_op && filp->f_op->dir_notify)
 109                return filp->f_op->dir_notify(filp, arg);
 110        return 0;
 111
 112out_free:
 113        spin_unlock(&inode->i_lock);
 114        kmem_cache_free(dn_cache, dn);
 115        return error;
 116}
 117
 118void __inode_dir_notify(struct inode *inode, unsigned long event)
 119{
 120        struct dnotify_struct * dn;
 121        struct dnotify_struct **prev;
 122        struct fown_struct *    fown;
 123        int                     changed = 0;
 124
 125        spin_lock(&inode->i_lock);
 126        prev = &inode->i_dnotify;
 127        while ((dn = *prev) != NULL) {
 128                if ((dn->dn_mask & event) == 0) {
 129                        prev = &dn->dn_next;
 130                        continue;
 131                }
 132                fown = &dn->dn_filp->f_owner;
 133                send_sigio(fown, dn->dn_fd, POLL_MSG);
 134                if (dn->dn_mask & DN_MULTISHOT)
 135                        prev = &dn->dn_next;
 136                else {
 137                        *prev = dn->dn_next;
 138                        changed = 1;
 139                        kmem_cache_free(dn_cache, dn);
 140                }
 141        }
 142        if (changed)
 143                redo_inode_mask(inode);
 144        spin_unlock(&inode->i_lock);
 145}
 146
 147EXPORT_SYMBOL(__inode_dir_notify);
 148
 149/*
 150 * This is hopelessly wrong, but unfixable without API changes.  At
 151 * least it doesn't oops the kernel...
 152 *
 153 * To safely access ->d_parent we need to keep d_move away from it.  Use the
 154 * dentry's d_lock for this.
 155 */
 156void dnotify_parent(struct dentry *dentry, unsigned long event)
 157{
 158        struct dentry *parent;
 159
 160        if (!dir_notify_enable)
 161                return;
 162
 163        spin_lock(&dentry->d_lock);
 164        parent = dentry->d_parent;
 165        if (parent->d_inode->i_dnotify_mask & event) {
 166                dget(parent);
 167                spin_unlock(&dentry->d_lock);
 168                __inode_dir_notify(parent->d_inode, event);
 169                dput(parent);
 170        } else {
 171                spin_unlock(&dentry->d_lock);
 172        }
 173}
 174EXPORT_SYMBOL_GPL(dnotify_parent);
 175
 176static int __init dnotify_init(void)
 177{
 178        dn_cache = kmem_cache_create("dnotify_cache",
 179                sizeof(struct dnotify_struct), 0, SLAB_PANIC, NULL);
 180        return 0;
 181}
 182
 183module_init(dnotify_init)
 184