lkml.org 
[lkml]   [2009]   [Mar]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[Aufs 23/25] aufs internal inotify
    Date
    initial commit
    detect bypassing aufs by inotify-watch

    Signed-off-by: J. R. Okajima <hooanon05@yahoo.co.jp>
    ---
    fs/aufs/hinotify.c | 746 ++++++++++++++++++++++++++++++++++++++++++++++++++++
    1 files changed, 746 insertions(+), 0 deletions(-)
    create mode 100644 fs/aufs/hinotify.c

    diff --git a/fs/aufs/hinotify.c b/fs/aufs/hinotify.c
    new file mode 100644
    index 0000000..8a8cd5c
    --- /dev/null
    +++ b/fs/aufs/hinotify.c
    @@ -0,0 +1,746 @@
    +/*
    + * Copyright (C) 2005-2009 Junjiro R. Okajima
    + *
    + * This program, aufs is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + */
    +
    +/*
    + * inotify for the lower directories
    + */
    +
    +#include "aufs.h"
    +
    +static const __u32 AuHinMask = (IN_MOVE | IN_DELETE | IN_CREATE);
    +static struct inotify_handle *au_hin_handle;
    +
    +AuCacheFuncs(hinotify, HINOTIFY);
    +
    +int au_hin_alloc(struct au_hinode *hinode, struct inode *inode,
    + struct inode *h_inode)
    +{
    + int err;
    + struct au_hinotify *hin;
    + s32 wd;
    +
    + err = -ENOMEM;
    + hin = au_cache_alloc_hinotify();
    + if (hin) {
    + AuDebugOn(hinode->hi_notify);
    + hinode->hi_notify = hin;
    + hin->hin_aufs_inode = inode;
    +
    + inotify_init_watch(&hin->hin_watch);
    + wd = inotify_add_watch(au_hin_handle, &hin->hin_watch, h_inode,
    + AuHinMask);
    + if (wd >= 0)
    + return 0; /* success */
    +
    + err = wd;
    + put_inotify_watch(&hin->hin_watch);
    + au_cache_free_hinotify(hin);
    + hinode->hi_notify = NULL;
    + }
    +
    + return err;
    +}
    +
    +void au_hin_free(struct au_hinode *hinode)
    +{
    + int err;
    + struct au_hinotify *hin;
    +
    + hin = hinode->hi_notify;
    + if (hin) {
    + err = 0;
    + if (atomic_read(&hin->hin_watch.count))
    + err = inotify_rm_watch(au_hin_handle, &hin->hin_watch);
    + if (unlikely(err))
    + /* it means the watch is already removed */
    + AuWarn("failed inotify_rm_watch() %d\n", err);
    + au_cache_free_hinotify(hin);
    + hinode->hi_notify = NULL;
    + }
    +}
    +
    +/* ---------------------------------------------------------------------- */
    +
    +void au_hin_ctl(struct au_hinode *hinode, int do_set)
    +{
    + struct inode *h_inode;
    + struct inotify_watch *watch;
    +
    + if (!hinode->hi_notify)
    + return;
    +
    + h_inode = hinode->hi_inode;
    + IMustLock(h_inode);
    +
    + /* todo: try inotify_find_update_watch()? */
    + watch = &hinode->hi_notify->hin_watch;
    + mutex_lock(&h_inode->inotify_mutex);
    + /* mutex_lock(&watch->ih->mutex); */
    + if (do_set) {
    + AuDebugOn(watch->mask & AuHinMask);
    + watch->mask |= AuHinMask;
    + } else {
    + AuDebugOn(!(watch->mask & AuHinMask));
    + watch->mask &= ~AuHinMask;
    + }
    + /* mutex_unlock(&watch->ih->mutex); */
    + mutex_unlock(&h_inode->inotify_mutex);
    +}
    +
    +void au_reset_hinotify(struct inode *inode, unsigned int flags)
    +{
    + aufs_bindex_t bindex, bend;
    + struct inode *hi;
    + struct dentry *iwhdentry;
    +
    + bend = au_ibend(inode);
    + for (bindex = au_ibstart(inode); bindex <= bend; bindex++) {
    + hi = au_h_iptr(inode, bindex);
    + if (!hi)
    + continue;
    +
    + /* mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD); */
    + iwhdentry = au_hi_wh(inode, bindex);
    + if (iwhdentry)
    + dget(iwhdentry);
    + au_igrab(hi);
    + au_set_h_iptr(inode, bindex, NULL, 0);
    + au_set_h_iptr(inode, bindex, au_igrab(hi),
    + flags & ~AuHi_XINO);
    + iput(hi);
    + dput(iwhdentry);
    + /* mutex_unlock(&hi->i_mutex); */
    + }
    +}
    +
    +/* ---------------------------------------------------------------------- */
    +
    +static int hin_xino(struct inode *inode, struct inode *h_inode)
    +{
    + int err;
    + aufs_bindex_t bindex, bend, bfound, bstart;
    + struct inode *h_i;
    +
    + err = 0;
    + if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
    + AuWarn("branch root dir was changed\n");
    + goto out;
    + }
    +
    + bfound = -1;
    + bend = au_ibend(inode);
    + bstart = au_ibstart(inode);
    +#if 0 /* reserved for future use */
    + if (bindex == bend) {
    + /* keep this ino in rename case */
    + goto out;
    + }
    +#endif
    + for (bindex = bstart; bindex <= bend; bindex++) {
    + if (au_h_iptr(inode, bindex) == h_inode) {
    + bfound = bindex;
    + break;
    + }
    + }
    + if (bfound < 0)
    + goto out;
    +
    + for (bindex = bstart; bindex <= bend; bindex++) {
    + h_i = au_h_iptr(inode, bindex);
    + if (!h_i)
    + continue;
    +
    + err = au_xino_write0(inode->i_sb, bindex, h_i->i_ino, 0);
    + /* ignore this error */
    + /* bad action? */
    + }
    +
    + /* children inode number will be broken */
    +
    + out:
    + AuTraceErr(err);
    + return err;
    +}
    +
    +static int hin_gen_tree(struct dentry *dentry)
    +{
    + int err, i, j, ndentry;
    + struct au_dcsub_pages dpages;
    + struct au_dpage *dpage;
    + struct dentry **dentries;
    +
    + err = au_dpages_init(&dpages, GFP_NOFS);
    + if (unlikely(err))
    + goto out;
    + err = au_dcsub_pages(&dpages, dentry, NULL, NULL);
    + if (unlikely(err))
    + goto out_dpages;
    +
    + for (i = 0; i < dpages.ndpage; i++) {
    + dpage = dpages.dpages + i;
    + dentries = dpage->dentries;
    + ndentry = dpage->ndentry;
    + for (j = 0; j < ndentry; j++) {
    + struct dentry *d;
    +
    + d = dentries[j];
    + if (IS_ROOT(d))
    + continue;
    +
    + d_drop(d);
    + au_digen_dec(d);
    + if (d->d_inode)
    + /* todo: reset children xino?
    + cached children only? */
    + au_iigen_dec(d->d_inode);
    + }
    + }
    +
    + out_dpages:
    + au_dpages_free(&dpages);
    +
    + /* discard children */
    + dentry_unhash(dentry);
    + dput(dentry);
    + out:
    + return err;
    +}
    +
    +/*
    + * return 0 if processed.
    + */
    +static int hin_gen_by_inode(char *name, unsigned int nlen, struct inode *inode,
    + const unsigned int isdir)
    +{
    + int err;
    + struct dentry *d;
    + struct qstr *dname;
    +
    + err = 1;
    + if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
    + AuWarn("branch root dir was changed\n");
    + err = 0;
    + goto out;
    + }
    +
    + if (!isdir) {
    + AuDebugOn(!name);
    + au_iigen_dec(inode);
    + spin_lock(&dcache_lock);
    + list_for_each_entry(d, &inode->i_dentry, d_alias) {
    + dname = &d->d_name;
    + if (dname->len != nlen
    + && memcmp(dname->name, name, nlen))
    + continue;
    + err = 0;
    + spin_lock(&d->d_lock);
    + __d_drop(d);
    + au_digen_dec(d);
    + spin_unlock(&d->d_lock);
    + break;
    + }
    + spin_unlock(&dcache_lock);
    + } else {
    + au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIRS);
    + d = d_find_alias(inode);
    + if (!d) {
    + au_iigen_dec(inode);
    + goto out;
    + }
    +
    + dname = &d->d_name;
    + if (dname->len == nlen && !memcmp(dname->name, name, nlen))
    + err = hin_gen_tree(d);
    + dput(d);
    + }
    +
    + out:
    + AuTraceErr(err);
    + return err;
    +}
    +
    +static int hin_gen_by_name(struct dentry *dentry, const unsigned int isdir)
    +{
    + int err;
    + struct inode *inode;
    +
    + inode = dentry->d_inode;
    + if (IS_ROOT(dentry)
    + /* || (inode && inode->i_ino == AUFS_ROOT_INO) */
    + ) {
    + AuWarn("branch root dir was changed\n");
    + return 0;
    + }
    +
    + err = 0;
    + if (!isdir) {
    + d_drop(dentry);
    + au_digen_dec(dentry);
    + if (inode)
    + au_iigen_dec(inode);
    + } else {
    + au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIRS);
    + if (inode)
    + err = hin_gen_tree(dentry);
    + }
    +
    + AuTraceErr(err);
    + return err;
    +}
    +
    +/* ---------------------------------------------------------------------- */
    +
    +/* hinotify job flags */
    +#define AuHinJob_XINO0 1
    +#define AuHinJob_GEN (1 << 1)
    +#define AuHinJob_DIRENT (1 << 2)
    +#define AuHinJob_ISDIR (1 << 3)
    +#define AuHinJob_TRYXINO0 (1 << 4)
    +#define AuHinJob_MNTPNT (1 << 5)
    +#define au_ftest_hinjob(flags, name) ((flags) & AuHinJob_##name)
    +#define au_fset_hinjob(flags, name) { (flags) |= AuHinJob_##name; }
    +#define au_fclr_hinjob(flags, name) { (flags) &= ~AuHinJob_##name; }
    +
    +struct hin_job_args {
    + unsigned int flags;
    + struct inode *inode, *h_inode, *dir, *h_dir;
    + struct dentry *dentry;
    + char *h_name;
    + int h_nlen;
    +};
    +
    +static int hin_job(struct hin_job_args *a)
    +{
    + const unsigned int isdir = au_ftest_hinjob(a->flags, ISDIR);
    +
    + /* reset xino */
    + if (au_ftest_hinjob(a->flags, XINO0) && a->inode)
    + hin_xino(a->inode, a->h_inode); /* ignore this error */
    +
    + if (au_ftest_hinjob(a->flags, TRYXINO0)
    + && a->inode
    + && a->h_inode) {
    + mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);
    + if (!a->h_inode->i_nlink)
    + hin_xino(a->inode, a->h_inode); /* ignore this error */
    + mutex_unlock(&a->h_inode->i_mutex);
    + }
    +
    + /* make the generation obsolete */
    + if (au_ftest_hinjob(a->flags, GEN)) {
    + int err = -1;
    + if (a->inode)
    + err = hin_gen_by_inode(a->h_name, a->h_nlen, a->inode,
    + isdir);
    + if (err && a->dentry)
    + hin_gen_by_name(a->dentry, isdir);
    + /* ignore this error */
    + }
    +
    + /* make dir entries obsolete */
    + if (au_ftest_hinjob(a->flags, DIRENT) && a->inode) {
    + struct au_vdir *vdir;
    +
    + vdir = au_ivdir(a->inode);
    + if (vdir)
    + vdir->vd_jiffy = 0;
    + /* IMustLock(a->inode); */
    + /* a->inode->i_version++; */
    + }
    +
    + /* can do nothing but warn */
    + if (au_ftest_hinjob(a->flags, MNTPNT)
    + && a->dentry
    + && d_mountpoint(a->dentry))
    + AuWarn("mount-point %.*s is removed or renamed\n",
    + AuDLNPair(a->dentry));
    +
    + return 0;
    +}
    +
    +/* ---------------------------------------------------------------------- */
    +
    +static char *in_name(u32 mask)
    +{
    +#ifdef CONFIG_AUFS_DEBUG
    +#define test_ret(flag) if (mask & flag) \
    + return #flag;
    + test_ret(IN_ACCESS);
    + test_ret(IN_MODIFY);
    + test_ret(IN_ATTRIB);
    + test_ret(IN_CLOSE_WRITE);
    + test_ret(IN_CLOSE_NOWRITE);
    + test_ret(IN_OPEN);
    + test_ret(IN_MOVED_FROM);
    + test_ret(IN_MOVED_TO);
    + test_ret(IN_CREATE);
    + test_ret(IN_DELETE);
    + test_ret(IN_DELETE_SELF);
    + test_ret(IN_MOVE_SELF);
    + test_ret(IN_UNMOUNT);
    + test_ret(IN_Q_OVERFLOW);
    + test_ret(IN_IGNORED);
    + return "";
    +#undef test_ret
    +#else
    + return "??";
    +#endif
    +}
    +
    +static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen,
    + struct inode *dir)
    +{
    + struct dentry *dentry, *d, *parent;
    + struct qstr *dname;
    +
    + parent = d_find_alias(dir);
    + if (!parent)
    + return NULL;
    +
    + dentry = NULL;
    + spin_lock(&dcache_lock);
    + list_for_each_entry(d, &parent->d_subdirs, d_u.d_child) {
    + /* AuDbg("%.*s\n", AuDLNPair(d)); */
    + dname = &d->d_name;
    + if (dname->len != nlen || memcmp(dname->name, name, nlen))
    + continue;
    + if (!atomic_read(&d->d_count) || !d->d_fsdata) {
    + spin_lock(&d->d_lock);
    + __d_drop(d);
    + spin_unlock(&d->d_lock);
    + continue;
    + }
    +
    + dentry = dget(d);
    + break;
    + }
    + spin_unlock(&dcache_lock);
    + dput(parent);
    +
    + if (dentry)
    + di_write_lock_child(dentry);
    +
    + return dentry;
    +}
    +
    +static struct inode *lookup_wlock_by_ino(struct super_block *sb,
    + aufs_bindex_t bindex, ino_t h_ino)
    +{
    + struct inode *inode;
    + ino_t ino;
    + int err;
    +
    + inode = NULL;
    + err = au_xino_read(sb, bindex, h_ino, &ino);
    + if (!err && ino)
    + inode = ilookup(sb, ino);
    + if (!inode)
    + goto out;
    +
    + if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
    + AuWarn("wrong root branch\n");
    + iput(inode);
    + inode = NULL;
    + goto out;
    + }
    +
    + ii_write_lock_child(inode);
    +
    + out:
    + return inode;
    +}
    +
    +enum { CHILD, PARENT };
    +struct postproc_args {
    + struct inode *h_dir, *dir, *h_child_inode;
    + u32 mask;
    + unsigned int flags[2];
    + unsigned int h_child_nlen;
    + char h_child_name[];
    +};
    +
    +static void postproc(void *_args)
    +{
    + struct postproc_args *a = _args;
    + struct super_block *sb;
    + aufs_bindex_t bindex, bend, bfound;
    + unsigned char xino, try_iput;
    + int err;
    + struct inode *inode;
    + ino_t h_ino;
    + struct hin_job_args args;
    + struct dentry *dentry;
    + struct au_sbinfo *sbinfo;
    +
    + AuDebugOn(!_args);
    + AuDebugOn(!a->h_dir);
    + AuDebugOn(!a->dir);
    + AuDebugOn(!a->mask);
    + AuDbg("mask 0x%x %s, i%lu, hi%lu, hci%lu\n",
    + a->mask, in_name(a->mask), a->dir->i_ino, a->h_dir->i_ino,
    + a->h_child_inode ? a->h_child_inode->i_ino : 0);
    +
    + inode = NULL;
    + dentry = NULL;
    + /*
    + * do not lock a->dir->i_mutex here
    + * because of d_revalidate() may cause a deadlock.
    + */
    + sb = a->dir->i_sb;
    + AuDebugOn(!sb);
    + sbinfo = au_sbi(sb);
    + AuDebugOn(!sbinfo);
    + /* big aufs lock */
    + si_noflush_write_lock(sb);
    +
    + ii_read_lock_parent(a->dir);
    + bfound = -1;
    + bend = au_ibend(a->dir);
    + for (bindex = au_ibstart(a->dir); bindex <= bend; bindex++)
    + if (au_h_iptr(a->dir, bindex) == a->h_dir) {
    + bfound = bindex;
    + break;
    + }
    + ii_read_unlock(a->dir);
    + if (unlikely(bfound < 0))
    + goto out;
    +
    + xino = !!au_opt_test(au_mntflags(sb), XINO);
    + h_ino = 0;
    + if (a->h_child_inode)
    + h_ino = a->h_child_inode->i_ino;
    +
    + if (a->h_child_nlen
    + && (au_ftest_hinjob(a->flags[CHILD], GEN)
    + || au_ftest_hinjob(a->flags[CHILD], MNTPNT)))
    + dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen,
    + a->dir);
    + try_iput = 0;
    + if (dentry)
    + inode = dentry->d_inode;
    + if (xino && !inode && h_ino
    + && (au_ftest_hinjob(a->flags[CHILD], XINO0)
    + || au_ftest_hinjob(a->flags[CHILD], TRYXINO0)
    + || au_ftest_hinjob(a->flags[CHILD], GEN))) {
    + inode = lookup_wlock_by_ino(sb, bfound, h_ino);
    + try_iput = 1;
    + }
    +
    + args.flags = a->flags[CHILD];
    + args.dentry = dentry;
    + args.inode = inode;
    + args.h_inode = a->h_child_inode;
    + args.dir = a->dir;
    + args.h_dir = a->h_dir;
    + args.h_name = a->h_child_name;
    + args.h_nlen = a->h_child_nlen;
    + err = hin_job(&args);
    + if (dentry) {
    + if (dentry->d_fsdata)
    + di_write_unlock(dentry);
    + dput(dentry);
    + }
    + if (inode && try_iput) {
    + ii_write_unlock(inode);
    + iput(inode);
    + }
    +
    + ii_write_lock_parent(a->dir);
    + args.flags = a->flags[PARENT];
    + args.dentry = NULL;
    + args.inode = a->dir;
    + args.h_inode = a->h_dir;
    + args.dir = NULL;
    + args.h_dir = NULL;
    + args.h_name = NULL;
    + args.h_nlen = 0;
    + err = hin_job(&args);
    + ii_write_unlock(a->dir);
    +
    + out:
    + au_nwt_done(&sbinfo->si_nowait);
    + si_write_unlock(sb);
    +
    + iput(a->h_child_inode);
    + iput(a->h_dir);
    + iput(a->dir);
    + kfree(a);
    +}
    +
    +/* ---------------------------------------------------------------------- */
    +
    +static void aufs_inotify(struct inotify_watch *watch, u32 wd, u32 mask,
    + u32 cookie, const char *h_child_name,
    + struct inode *h_child_inode)
    +{
    + struct au_hinotify *hinotify;
    + struct postproc_args *args;
    + int len, wkq_err;
    + unsigned char isdir, isroot, wh;
    + char *p;
    + struct inode *dir;
    + unsigned int flags[2];
    +
    + /* if IN_UNMOUNT happens, there must be another bug */
    + AuDebugOn(mask & IN_UNMOUNT);
    + if (mask & (IN_IGNORED | IN_UNMOUNT)) {
    + put_inotify_watch(watch);
    + return;
    + }
    +#ifdef AuDbgHinotify
    + au_debug(1);
    + if (1 || !h_child_name || strcmp(h_child_name, AUFS_XINO_FNAME)) {
    + AuDbg("i%lu, wd %d, mask 0x%x %s, cookie 0x%x, hcname %s,"
    + " hi%lu\n",
    + watch->inode->i_ino, wd, mask, in_name(mask), cookie,
    + h_child_name ? h_child_name : "",
    + h_child_inode ? h_child_inode->i_ino : 0);
    + WARN_ON(1);
    + }
    + au_debug(0);
    +#endif
    +
    + hinotify = container_of(watch, struct au_hinotify, hin_watch);
    + AuDebugOn(!hinotify || !hinotify->hin_aufs_inode);
    + dir = igrab(hinotify->hin_aufs_inode);
    + if (!dir)
    + return;
    +
    + isroot = (dir->i_ino == AUFS_ROOT_INO);
    + len = 0;
    + wh = 0;
    + if (h_child_name) {
    + len = strlen(h_child_name);
    + if (!memcmp(h_child_name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {
    + h_child_name += AUFS_WH_PFX_LEN;
    + len -= AUFS_WH_PFX_LEN;
    + wh = 1;
    + }
    + }
    +
    + isdir = 0;
    + if (h_child_inode)
    + isdir = !!S_ISDIR(h_child_inode->i_mode);
    + flags[PARENT] = AuHinJob_ISDIR;
    + flags[CHILD] = 0;
    + if (isdir)
    + flags[CHILD] = AuHinJob_ISDIR;
    + switch (mask & IN_ALL_EVENTS) {
    + case IN_MOVED_FROM:
    + case IN_MOVED_TO:
    + AuDebugOn(!h_child_name || !h_child_inode);
    + au_fset_hinjob(flags[CHILD], GEN);
    + au_fset_hinjob(flags[CHILD], XINO0);
    + au_fset_hinjob(flags[CHILD], MNTPNT);
    + au_fset_hinjob(flags[PARENT], DIRENT);
    + break;
    +
    + case IN_CREATE:
    + AuDebugOn(!h_child_name || !h_child_inode);
    + au_fset_hinjob(flags[PARENT], DIRENT);
    + au_fset_hinjob(flags[CHILD], GEN);
    + break;
    +
    + case IN_DELETE:
    + /*
    + * aufs never be able to get this child inode.
    + * revalidation should be in d_revalidate()
    + * by checking i_nlink, i_generation or d_unhashed().
    + */
    + AuDebugOn(!h_child_name);
    + au_fset_hinjob(flags[PARENT], DIRENT);
    + au_fset_hinjob(flags[CHILD], GEN);
    + au_fset_hinjob(flags[CHILD], TRYXINO0);
    + au_fset_hinjob(flags[CHILD], MNTPNT);
    + break;
    +
    + default:
    + AuDebugOn(1);
    + }
    +
    + if (wh)
    + h_child_inode = NULL;
    +
    + /* iput() and kfree() will be called in postproc() */
    + /*
    + * inotify_mutex is already acquired and kmalloc/prune_icache may lock
    + * iprune_mutex. strange.
    + */
    + lockdep_off();
    + args = kmalloc(sizeof(*args) + len + 1, GFP_NOFS);
    + lockdep_on();
    + if (unlikely(!args)) {
    + AuErr1("no memory\n");
    + iput(dir);
    + return;
    + }
    + args->flags[PARENT] = flags[PARENT];
    + args->flags[CHILD] = flags[CHILD];
    + args->mask = mask;
    + args->dir = dir;
    + args->h_dir = igrab(watch->inode);
    + if (h_child_inode)
    + h_child_inode = igrab(h_child_inode); /* can be NULL */
    + args->h_child_inode = h_child_inode;
    + args->h_child_nlen = len;
    + if (len) {
    + p = (void *)args;
    + p += sizeof(*args);
    + memcpy(p, h_child_name, len + 1);
    + }
    +
    + lockdep_off();
    + wkq_err = au_wkq_nowait(postproc, args, dir->i_sb);
    + lockdep_on();
    + if (unlikely(wkq_err))
    + AuErr("wkq %d\n", wkq_err);
    +}
    +
    +static void aufs_inotify_destroy(struct inotify_watch *watch)
    +{
    + return;
    +}
    +
    +static struct inotify_operations aufs_inotify_ops = {
    + .handle_event = aufs_inotify,
    + .destroy_watch = aufs_inotify_destroy
    +};
    +
    +/* ---------------------------------------------------------------------- */
    +
    +static void au_hin_destroy_cache(void)
    +{
    + kmem_cache_destroy(au_cachep[AuCache_HINOTIFY]);
    + au_cachep[AuCache_HINOTIFY] = NULL;
    +}
    +
    +int __init au_hinotify_init(void)
    +{
    + int err;
    +
    + err = -ENOMEM;
    + au_cachep[AuCache_HINOTIFY] = AuCache(au_hinotify);
    + if (au_cachep[AuCache_HINOTIFY]) {
    + err = 0;
    + au_hin_handle = inotify_init(&aufs_inotify_ops);
    + if (IS_ERR(au_hin_handle)) {
    + err = PTR_ERR(au_hin_handle);
    + au_hin_destroy_cache();
    + }
    + }
    + AuTraceErr(err);
    + return err;
    +}
    +
    +void au_hinotify_fin(void)
    +{
    + inotify_destroy(au_hin_handle);
    + if (au_cachep[AuCache_HINOTIFY])
    + au_hin_destroy_cache();
    +}
    --
    1.6.1.284.g5dc13


    \
     
     \ /
      Last update: 2009-03-09 04:41    [W:0.082 / U:116.316 seconds]
    ©2003-2017 Jasper Spaans. hosted at Digital OceanAdvertise on this site