summaryrefslogtreecommitdiff
path: root/fs/aufs/hnotify.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/aufs/hnotify.c')
-rw-r--r--fs/aufs/hnotify.c710
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();
-}