diff options
Diffstat (limited to 'fs/aufs/hnotify.c')
-rw-r--r-- | fs/aufs/hnotify.c | 710 |
1 files changed, 0 insertions, 710 deletions
diff --git a/fs/aufs/hnotify.c b/fs/aufs/hnotify.c deleted file mode 100644 index 643bc57c5..000000000 --- a/fs/aufs/hnotify.c +++ /dev/null @@ -1,710 +0,0 @@ -/* - * Copyright (C) 2005-2015 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * abstraction to notify the direct changes on lower directories - */ - -#include "aufs.h" - -int au_hn_alloc(struct au_hinode *hinode, struct inode *inode) -{ - int err; - struct au_hnotify *hn; - - err = -ENOMEM; - hn = au_cache_alloc_hnotify(); - if (hn) { - hn->hn_aufs_inode = inode; - hinode->hi_notify = hn; - err = au_hnotify_op.alloc(hinode); - AuTraceErr(err); - if (unlikely(err)) { - hinode->hi_notify = NULL; - au_cache_free_hnotify(hn); - /* - * The upper dir was removed by udba, but the same named - * dir left. In this case, aufs assignes a new inode - * number and set the monitor again. - * For the lower dir, the old monitnor is still left. - */ - if (err == -EEXIST) - err = 0; - } - } - - AuTraceErr(err); - return err; -} - -void au_hn_free(struct au_hinode *hinode) -{ - struct au_hnotify *hn; - - hn = hinode->hi_notify; - if (hn) { - hinode->hi_notify = NULL; - if (au_hnotify_op.free(hinode, hn)) - au_cache_free_hnotify(hn); - } -} - -/* ---------------------------------------------------------------------- */ - -void au_hn_ctl(struct au_hinode *hinode, int do_set) -{ - if (hinode->hi_notify) - au_hnotify_op.ctl(hinode, do_set); -} - -void au_hn_reset(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 hn_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)) { - pr_warn("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_write(inode->i_sb, bindex, h_i->i_ino, /*ino*/0); - /* ignore this error */ - /* bad action? */ - } - - /* children inode number will be broken */ - -out: - AuTraceErr(err); - return err; -} - -static int hn_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; - - au_digen_dec(d); - if (d_really_is_positive(d)) - /* todo: reset children xino? - cached children only? */ - au_iigen_dec(d_inode(d)); - } - } - -out_dpages: - au_dpages_free(&dpages); - -#if 0 - /* discard children */ - dentry_unhash(dentry); - dput(dentry); -#endif -out: - return err; -} - -/* - * return 0 if processed. - */ -static int hn_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)) { - pr_warn("branch root dir was changed\n"); - err = 0; - goto out; - } - - if (!isdir) { - AuDebugOn(!name); - au_iigen_dec(inode); - spin_lock(&inode->i_lock); - hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { - spin_lock(&d->d_lock); - dname = &d->d_name; - if (dname->len != nlen - && memcmp(dname->name, name, nlen)) { - spin_unlock(&d->d_lock); - continue; - } - err = 0; - au_digen_dec(d); - spin_unlock(&d->d_lock); - break; - } - spin_unlock(&inode->i_lock); - } else { - au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIR); - d = d_find_any_alias(inode); - if (!d) { - au_iigen_dec(inode); - goto out; - } - - spin_lock(&d->d_lock); - dname = &d->d_name; - if (dname->len == nlen && !memcmp(dname->name, name, nlen)) { - spin_unlock(&d->d_lock); - err = hn_gen_tree(d); - spin_lock(&d->d_lock); - } - spin_unlock(&d->d_lock); - dput(d); - } - -out: - AuTraceErr(err); - return err; -} - -static int hn_gen_by_name(struct dentry *dentry, const unsigned int isdir) -{ - int err; - - if (IS_ROOT(dentry)) { - pr_warn("branch root dir was changed\n"); - return 0; - } - - err = 0; - if (!isdir) { - au_digen_dec(dentry); - if (d_really_is_positive(dentry)) - au_iigen_dec(d_inode(dentry)); - } else { - au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR); - if (d_really_is_positive(dentry)) - err = hn_gen_tree(dentry); - } - - AuTraceErr(err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* hnotify job flags */ -#define AuHnJob_XINO0 1 -#define AuHnJob_GEN (1 << 1) -#define AuHnJob_DIRENT (1 << 2) -#define AuHnJob_ISDIR (1 << 3) -#define AuHnJob_TRYXINO0 (1 << 4) -#define AuHnJob_MNTPNT (1 << 5) -#define au_ftest_hnjob(flags, name) ((flags) & AuHnJob_##name) -#define au_fset_hnjob(flags, name) \ - do { (flags) |= AuHnJob_##name; } while (0) -#define au_fclr_hnjob(flags, name) \ - do { (flags) &= ~AuHnJob_##name; } while (0) - -enum { - AuHn_CHILD, - AuHn_PARENT, - AuHnLast -}; - -struct au_hnotify_args { - struct inode *h_dir, *dir, *h_child_inode; - u32 mask; - unsigned int flags[AuHnLast]; - unsigned int h_child_nlen; - char h_child_name[]; -}; - -struct hn_job_args { - unsigned int flags; - struct inode *inode, *h_inode, *dir, *h_dir; - struct dentry *dentry; - char *h_name; - int h_nlen; -}; - -static int hn_job(struct hn_job_args *a) -{ - const unsigned int isdir = au_ftest_hnjob(a->flags, ISDIR); - int e; - - /* reset xino */ - if (au_ftest_hnjob(a->flags, XINO0) && a->inode) - hn_xino(a->inode, a->h_inode); /* ignore this error */ - - if (au_ftest_hnjob(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 - && !(a->h_inode->i_state & I_LINKABLE)) - hn_xino(a->inode, a->h_inode); /* ignore this error */ - mutex_unlock(&a->h_inode->i_mutex); - } - - /* make the generation obsolete */ - if (au_ftest_hnjob(a->flags, GEN)) { - e = -1; - if (a->inode) - e = hn_gen_by_inode(a->h_name, a->h_nlen, a->inode, - isdir); - if (e && a->dentry) - hn_gen_by_name(a->dentry, isdir); - /* ignore this error */ - } - - /* make dir entries obsolete */ - if (au_ftest_hnjob(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_hnjob(a->flags, MNTPNT) - && a->dentry - && d_mountpoint(a->dentry)) - pr_warn("mount-point %pd is removed or renamed\n", a->dentry); - - return 0; -} - -/* ---------------------------------------------------------------------- */ - -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_any_alias(dir); - if (!parent) - return NULL; - - dentry = NULL; - spin_lock(&parent->d_lock); - list_for_each_entry(d, &parent->d_subdirs, d_child) { - /* AuDbg("%pd\n", d); */ - spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED); - dname = &d->d_name; - if (dname->len != nlen || memcmp(dname->name, name, nlen)) - goto cont_unlock; - if (au_di(d)) - au_digen_dec(d); - else - goto cont_unlock; - if (au_dcount(d) > 0) { - dentry = dget_dlock(d); - spin_unlock(&d->d_lock); - break; - } - -cont_unlock: - spin_unlock(&d->d_lock); - } - spin_unlock(&parent->d_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)) { - pr_warn("wrong root branch\n"); - iput(inode); - inode = NULL; - goto out; - } - - ii_write_lock_child(inode); - -out: - return inode; -} - -static void au_hn_bh(void *_args) -{ - struct au_hnotify_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 hn_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, i%lu, hi%lu, hci%lu\n", - 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); - si_write_lock(sb, AuLock_NOPLMW); - - 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_hnjob(a->flags[AuHn_CHILD], GEN) - || au_ftest_hnjob(a->flags[AuHn_CHILD], MNTPNT))) - dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen, - a->dir); - try_iput = 0; - if (dentry && d_really_is_positive(dentry)) - inode = d_inode(dentry); - if (xino && !inode && h_ino - && (au_ftest_hnjob(a->flags[AuHn_CHILD], XINO0) - || au_ftest_hnjob(a->flags[AuHn_CHILD], TRYXINO0) - || au_ftest_hnjob(a->flags[AuHn_CHILD], GEN))) { - inode = lookup_wlock_by_ino(sb, bfound, h_ino); - try_iput = 1; - } - - args.flags = a->flags[AuHn_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 = hn_job(&args); - if (dentry) { - if (au_di(dentry)) - 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[AuHn_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 = hn_job(&args); - ii_write_unlock(a->dir); - -out: - iput(a->h_child_inode); - iput(a->h_dir); - iput(a->dir); - si_write_unlock(sb); - au_nwt_done(&sbinfo->si_nowait); - kfree(a); -} - -/* ---------------------------------------------------------------------- */ - -int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, - struct qstr *h_child_qstr, struct inode *h_child_inode) -{ - int err, len; - unsigned int flags[AuHnLast], f; - unsigned char isdir, isroot, wh; - struct inode *dir; - struct au_hnotify_args *args; - char *p, *h_child_name; - - err = 0; - AuDebugOn(!hnotify || !hnotify->hn_aufs_inode); - dir = igrab(hnotify->hn_aufs_inode); - if (!dir) - goto out; - - isroot = (dir->i_ino == AUFS_ROOT_INO); - wh = 0; - h_child_name = (void *)h_child_qstr->name; - len = h_child_qstr->len; - if (h_child_name) { - if (len > AUFS_WH_PFX_LEN - && !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[AuHn_PARENT] = AuHnJob_ISDIR; - flags[AuHn_CHILD] = 0; - if (isdir) - flags[AuHn_CHILD] = AuHnJob_ISDIR; - au_fset_hnjob(flags[AuHn_PARENT], DIRENT); - au_fset_hnjob(flags[AuHn_CHILD], GEN); - switch (mask & FS_EVENTS_POSS_ON_CHILD) { - case FS_MOVED_FROM: - case FS_MOVED_TO: - au_fset_hnjob(flags[AuHn_CHILD], XINO0); - au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); - /*FALLTHROUGH*/ - case FS_CREATE: - AuDebugOn(!h_child_name); - break; - - case FS_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_hnjob(flags[AuHn_CHILD], TRYXINO0); - au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); - break; - - default: - AuDebugOn(1); - } - - if (wh) - h_child_inode = NULL; - - err = -ENOMEM; - /* iput() and kfree() will be called in au_hnotify() */ - args = kmalloc(sizeof(*args) + len + 1, GFP_NOFS); - if (unlikely(!args)) { - AuErr1("no memory\n"); - iput(dir); - goto out; - } - args->flags[AuHn_PARENT] = flags[AuHn_PARENT]; - args->flags[AuHn_CHILD] = flags[AuHn_CHILD]; - args->mask = mask; - args->dir = dir; - args->h_dir = igrab(h_dir); - 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); - p[len] = 0; - } - - /* NFS fires the event for silly-renamed one from kworker */ - f = 0; - if (!dir->i_nlink - || (au_test_nfs(h_dir->i_sb) && (mask & FS_DELETE))) - f = AuWkq_NEST; - err = au_wkq_nowait(au_hn_bh, args, dir->i_sb, f); - if (unlikely(err)) { - pr_err("wkq %d\n", err); - iput(args->h_child_inode); - iput(args->h_dir); - iput(args->dir); - kfree(args); - } - -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm) -{ - int err; - - AuDebugOn(!(udba & AuOptMask_UDBA)); - - err = 0; - if (au_hnotify_op.reset_br) - err = au_hnotify_op.reset_br(udba, br, perm); - - return err; -} - -int au_hnotify_init_br(struct au_branch *br, int perm) -{ - int err; - - err = 0; - if (au_hnotify_op.init_br) - err = au_hnotify_op.init_br(br, perm); - - return err; -} - -void au_hnotify_fin_br(struct au_branch *br) -{ - if (au_hnotify_op.fin_br) - au_hnotify_op.fin_br(br); -} - -static void au_hn_destroy_cache(void) -{ - kmem_cache_destroy(au_cachep[AuCache_HNOTIFY]); - au_cachep[AuCache_HNOTIFY] = NULL; -} - -int __init au_hnotify_init(void) -{ - int err; - - err = -ENOMEM; - au_cachep[AuCache_HNOTIFY] = AuCache(au_hnotify); - if (au_cachep[AuCache_HNOTIFY]) { - err = 0; - if (au_hnotify_op.init) - err = au_hnotify_op.init(); - if (unlikely(err)) - au_hn_destroy_cache(); - } - AuTraceErr(err); - return err; -} - -void au_hnotify_fin(void) -{ - if (au_hnotify_op.fin) - au_hnotify_op.fin(); - /* cf. au_cache_fin() */ - if (au_cachep[AuCache_HNOTIFY]) - au_hn_destroy_cache(); -} |