linux-bk/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 = 1;
  25
  26static rwlock_t dn_lock = RW_LOCK_UNLOCKED;
  27static kmem_cache_t *dn_cache;
  28
  29static void redo_inode_mask(struct inode *inode)
  30{
  31        unsigned long new_mask;
  32        struct dnotify_struct *dn;
  33
  34        new_mask = 0;
  35        for (dn = inode->i_dnotify; dn != NULL; dn = dn->dn_next)
  36                new_mask |= dn->dn_mask & ~DN_MULTISHOT;
  37        inode->i_dnotify_mask = new_mask;
  38}
  39
  40void dnotify_flush(struct file *filp, fl_owner_t id)
  41{
  42        struct dnotify_struct *dn;
  43        struct dnotify_struct **prev;
  44        struct inode *inode;
  45
  46        inode = filp->f_dentry->d_inode;
  47        if (!S_ISDIR(inode->i_mode))
  48                return;
  49        write_lock(&dn_lock);
  50        prev = &inode->i_dnotify;
  51        while ((dn = *prev) != NULL) {
  52                if ((dn->dn_owner == id) && (dn->dn_filp == filp)) {
  53                        *prev = dn->dn_next;
  54                        redo_inode_mask(inode);
  55                        kmem_cache_free(dn_cache, dn);
  56                        break;
  57                }
  58                prev = &dn->dn_next;
  59        }
  60        write_unlock(&dn_lock);
  61}
  62
  63int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
  64{
  65        struct dnotify_struct *dn;
  66        struct dnotify_struct *odn;
  67        struct dnotify_struct **prev;
  68        struct inode *inode;
  69        fl_owner_t id = current->files;
  70        int error = 0;
  71
  72        if ((arg & ~DN_MULTISHOT) == 0) {
  73                dnotify_flush(filp, id);
  74                return 0;
  75        }
  76        if (!dir_notify_enable)
  77                return -EINVAL;
  78        inode = filp->f_dentry->d_inode;
  79        if (!S_ISDIR(inode->i_mode))
  80                return -ENOTDIR;
  81        dn = kmem_cache_alloc(dn_cache, SLAB_KERNEL);
  82        if (dn == NULL)
  83                return -ENOMEM;
  84        write_lock(&dn_lock);
  85        prev = &inode->i_dnotify;
  86        while ((odn = *prev) != NULL) {
  87                if ((odn->dn_owner == id) && (odn->dn_filp == filp)) {
  88                        odn->dn_fd = fd;
  89                        odn->dn_mask |= arg;
  90                        inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
  91                        goto out_free;
  92                }
  93                prev = &odn->dn_next;
  94        }
  95
  96        error = f_setown(filp, current->pid, 0);
  97        if (error)
  98                goto out_free;
  99
 100        dn->dn_mask = arg;
 101        dn->dn_fd = fd;
 102        dn->dn_filp = filp;
 103        dn->dn_owner = id;
 104        inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
 105        dn->dn_next = inode->i_dnotify;
 106        inode->i_dnotify = dn;
 107out:
 108        write_unlock(&dn_lock);
 109        return error;
 110out_free:
 111        kmem_cache_free(dn_cache, dn);
 112        goto out;
 113}
 114
 115void __inode_dir_notify(struct inode *inode, unsigned long event)
 116{
 117        struct dnotify_struct * dn;
 118        struct dnotify_struct **prev;
 119        struct fown_struct *    fown;
 120        int                     changed = 0;
 121
 122        write_lock(&dn_lock);
 123        prev = &inode->i_dnotify;
 124        while ((dn = *prev) != NULL) {
 125                if ((dn->dn_mask & event) == 0) {
 126                        prev = &dn->dn_next;
 127                        continue;
 128                }
 129                fown = &dn->dn_filp->f_owner;
 130                send_sigio(fown, dn->dn_fd, POLL_MSG);
 131                if (dn->dn_mask & DN_MULTISHOT)
 132                        prev = &dn->dn_next;
 133                else {
 134                        *prev = dn->dn_next;
 135                        changed = 1;
 136                        kmem_cache_free(dn_cache, dn);
 137                }
 138        }
 139        if (changed)
 140                redo_inode_mask(inode);
 141        write_unlock(&dn_lock);
 142}
 143
 144EXPORT_SYMBOL(__inode_dir_notify);
 145
 146/*
 147 * This is hopelessly wrong, but unfixable without API changes.  At
 148 * least it doesn't oops the kernel...
 149 *
 150 * To safely access ->d_parent we need to keep d_move away from it.  Use the
 151 * dentry's d_lock for this.
 152 */
 153void dnotify_parent(struct dentry *dentry, unsigned long event)
 154{
 155        struct dentry *parent;
 156
 157        spin_lock(&dentry->d_lock);
 158        parent = dentry->d_parent;
 159        if (parent->d_inode->i_dnotify_mask & event) {
 160                dget(parent);
 161                spin_unlock(&dentry->d_lock);
 162                __inode_dir_notify(parent->d_inode, event);
 163                dput(parent);
 164        } else {
 165                spin_unlock(&dentry->d_lock);
 166        }
 167}
 168
 169static int __init dnotify_init(void)
 170{
 171        dn_cache = kmem_cache_create("dnotify_cache",
 172                sizeof(struct dnotify_struct), 0, 0, NULL, NULL);
 173        if (!dn_cache)
 174                panic("cannot create dnotify slab cache");
 175        return 0;
 176}
 177
 178module_init(dnotify_init)
 179
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.