diff options
Diffstat (limited to 'fs/aufs/dentry.c')
-rw-r--r-- | fs/aufs/dentry.c | 1105 |
1 files changed, 0 insertions, 1105 deletions
diff --git a/fs/aufs/dentry.c b/fs/aufs/dentry.c deleted file mode 100644 index 1191a6c93..000000000 --- a/fs/aufs/dentry.c +++ /dev/null @@ -1,1105 +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/>. - */ - -/* - * lookup and dentry operations - */ - -#include <linux/namei.h> -#include "aufs.h" - -#define AuLkup_ALLOW_NEG 1 -#define AuLkup_IGNORE_PERM (1 << 1) -#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name) -#define au_fset_lkup(flags, name) \ - do { (flags) |= AuLkup_##name; } while (0) -#define au_fclr_lkup(flags, name) \ - do { (flags) &= ~AuLkup_##name; } while (0) - -struct au_do_lookup_args { - unsigned int flags; - mode_t type; -}; - -/* - * returns positive/negative dentry, NULL or an error. - * NULL means whiteout-ed or not-found. - */ -static struct dentry* -au_do_lookup(struct dentry *h_parent, struct dentry *dentry, - aufs_bindex_t bindex, struct qstr *wh_name, - struct au_do_lookup_args *args) -{ - struct dentry *h_dentry; - struct inode *h_inode; - struct au_branch *br; - int wh_found, opq; - unsigned char wh_able; - const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG); - const unsigned char ignore_perm = !!au_ftest_lkup(args->flags, - IGNORE_PERM); - - wh_found = 0; - br = au_sbr(dentry->d_sb, bindex); - wh_able = !!au_br_whable(br->br_perm); - if (wh_able) - wh_found = au_wh_test(h_parent, wh_name, /*try_sio*/0); - h_dentry = ERR_PTR(wh_found); - if (!wh_found) - goto real_lookup; - if (unlikely(wh_found < 0)) - goto out; - - /* We found a whiteout */ - /* au_set_dbend(dentry, bindex); */ - au_set_dbwh(dentry, bindex); - if (!allow_neg) - return NULL; /* success */ - -real_lookup: - if (!ignore_perm) - h_dentry = vfsub_lkup_one(&dentry->d_name, h_parent); - else - h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); - if (IS_ERR(h_dentry)) { - if (PTR_ERR(h_dentry) == -ENAMETOOLONG - && !allow_neg) - h_dentry = NULL; - goto out; - } - - h_inode = d_inode(h_dentry); - if (d_is_negative(h_dentry)) { - if (!allow_neg) - goto out_neg; - } else if (wh_found - || (args->type && args->type != (h_inode->i_mode & S_IFMT))) - goto out_neg; - - if (au_dbend(dentry) <= bindex) - au_set_dbend(dentry, bindex); - if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) - au_set_dbstart(dentry, bindex); - au_set_h_dptr(dentry, bindex, h_dentry); - - if (!d_is_dir(h_dentry) - || !wh_able - || (d_really_is_positive(dentry) && !d_is_dir(dentry))) - goto out; /* success */ - - mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); - opq = au_diropq_test(h_dentry); - mutex_unlock(&h_inode->i_mutex); - if (opq > 0) - au_set_dbdiropq(dentry, bindex); - else if (unlikely(opq < 0)) { - au_set_h_dptr(dentry, bindex, NULL); - h_dentry = ERR_PTR(opq); - } - goto out; - -out_neg: - dput(h_dentry); - h_dentry = NULL; -out: - return h_dentry; -} - -static int au_test_shwh(struct super_block *sb, const struct qstr *name) -{ - if (unlikely(!au_opt_test(au_mntflags(sb), SHWH) - && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))) - return -EPERM; - return 0; -} - -/* - * returns the number of lower positive dentries, - * otherwise an error. - * can be called at unlinking with @type is zero. - */ -int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type) -{ - int npositive, err; - aufs_bindex_t bindex, btail, bdiropq; - unsigned char isdir, dirperm1; - struct qstr whname; - struct au_do_lookup_args args = { - .flags = 0, - .type = type - }; - const struct qstr *name = &dentry->d_name; - struct dentry *parent; - struct super_block *sb; - - sb = dentry->d_sb; - err = au_test_shwh(sb, name); - if (unlikely(err)) - goto out; - - err = au_wh_name_alloc(&whname, name); - if (unlikely(err)) - goto out; - - isdir = !!d_is_dir(dentry); - if (!type) - au_fset_lkup(args.flags, ALLOW_NEG); - dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1); - - npositive = 0; - parent = dget_parent(dentry); - btail = au_dbtaildir(parent); - for (bindex = bstart; bindex <= btail; bindex++) { - struct dentry *h_parent, *h_dentry; - struct inode *h_inode, *h_dir; - - h_dentry = au_h_dptr(dentry, bindex); - if (h_dentry) { - if (d_is_positive(h_dentry)) - npositive++; - if (type != S_IFDIR) - break; - continue; - } - h_parent = au_h_dptr(parent, bindex); - if (!h_parent || !d_is_dir(h_parent)) - continue; - - h_dir = d_inode(h_parent); - mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); - h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname, - &args); - mutex_unlock(&h_dir->i_mutex); - err = PTR_ERR(h_dentry); - if (IS_ERR(h_dentry)) - goto out_parent; - if (h_dentry) - au_fclr_lkup(args.flags, ALLOW_NEG); - if (dirperm1) - au_fset_lkup(args.flags, IGNORE_PERM); - - if (au_dbwh(dentry) >= 0) - break; - if (!h_dentry) - continue; - if (d_is_negative(h_dentry)) - continue; - h_inode = d_inode(h_dentry); - npositive++; - if (!args.type) - args.type = h_inode->i_mode & S_IFMT; - if (args.type != S_IFDIR) - break; - else if (isdir) { - /* the type of lower may be different */ - bdiropq = au_dbdiropq(dentry); - if (bdiropq >= 0 && bdiropq <= bindex) - break; - } - } - - if (npositive) { - AuLabel(positive); - au_update_dbstart(dentry); - } - err = npositive; - if (unlikely(!au_opt_test(au_mntflags(sb), UDBA_NONE) - && au_dbstart(dentry) < 0)) { - err = -EIO; - AuIOErr("both of real entry and whiteout found, %pd, err %d\n", - dentry, err); - } - -out_parent: - dput(parent); - kfree(whname.name); -out: - return err; -} - -struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent) -{ - struct dentry *dentry; - int wkq_err; - - if (!au_test_h_perm_sio(d_inode(parent), MAY_EXEC)) - dentry = vfsub_lkup_one(name, parent); - else { - struct vfsub_lkup_one_args args = { - .errp = &dentry, - .name = name, - .parent = parent - }; - - wkq_err = au_wkq_wait(vfsub_call_lkup_one, &args); - if (unlikely(wkq_err)) - dentry = ERR_PTR(wkq_err); - } - - return dentry; -} - -/* - * lookup @dentry on @bindex which should be negative. - */ -int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh) -{ - int err; - struct dentry *parent, *h_parent, *h_dentry; - struct au_branch *br; - - parent = dget_parent(dentry); - h_parent = au_h_dptr(parent, bindex); - br = au_sbr(dentry->d_sb, bindex); - if (wh) - h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); - else - h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); - err = PTR_ERR(h_dentry); - if (IS_ERR(h_dentry)) - goto out; - if (unlikely(d_is_positive(h_dentry))) { - err = -EIO; - AuIOErr("%pd should be negative on b%d.\n", h_dentry, bindex); - dput(h_dentry); - goto out; - } - - err = 0; - if (bindex < au_dbstart(dentry)) - au_set_dbstart(dentry, bindex); - if (au_dbend(dentry) < bindex) - au_set_dbend(dentry, bindex); - au_set_h_dptr(dentry, bindex, h_dentry); - -out: - dput(parent); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* subset of struct inode */ -struct au_iattr { - unsigned long i_ino; - /* unsigned int i_nlink; */ - kuid_t i_uid; - kgid_t i_gid; - u64 i_version; -/* - loff_t i_size; - blkcnt_t i_blocks; -*/ - umode_t i_mode; -}; - -static void au_iattr_save(struct au_iattr *ia, struct inode *h_inode) -{ - ia->i_ino = h_inode->i_ino; - /* ia->i_nlink = h_inode->i_nlink; */ - ia->i_uid = h_inode->i_uid; - ia->i_gid = h_inode->i_gid; - ia->i_version = h_inode->i_version; -/* - ia->i_size = h_inode->i_size; - ia->i_blocks = h_inode->i_blocks; -*/ - ia->i_mode = (h_inode->i_mode & S_IFMT); -} - -static int au_iattr_test(struct au_iattr *ia, struct inode *h_inode) -{ - return ia->i_ino != h_inode->i_ino - /* || ia->i_nlink != h_inode->i_nlink */ - || !uid_eq(ia->i_uid, h_inode->i_uid) - || !gid_eq(ia->i_gid, h_inode->i_gid) - || ia->i_version != h_inode->i_version -/* - || ia->i_size != h_inode->i_size - || ia->i_blocks != h_inode->i_blocks -*/ - || ia->i_mode != (h_inode->i_mode & S_IFMT); -} - -static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent, - struct au_branch *br) -{ - int err; - struct au_iattr ia; - struct inode *h_inode; - struct dentry *h_d; - struct super_block *h_sb; - - err = 0; - memset(&ia, -1, sizeof(ia)); - h_sb = h_dentry->d_sb; - h_inode = NULL; - if (d_is_positive(h_dentry)) { - h_inode = d_inode(h_dentry); - au_iattr_save(&ia, h_inode); - } else if (au_test_nfs(h_sb) || au_test_fuse(h_sb)) - /* nfs d_revalidate may return 0 for negative dentry */ - /* fuse d_revalidate always return 0 for negative dentry */ - goto out; - - /* main purpose is namei.c:cached_lookup() and d_revalidate */ - h_d = vfsub_lkup_one(&h_dentry->d_name, h_parent); - err = PTR_ERR(h_d); - if (IS_ERR(h_d)) - goto out; - - err = 0; - if (unlikely(h_d != h_dentry - || d_inode(h_d) != h_inode - || (h_inode && au_iattr_test(&ia, h_inode)))) - err = au_busy_or_stale(); - dput(h_d); - -out: - AuTraceErr(err); - return err; -} - -int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, - struct dentry *h_parent, struct au_branch *br) -{ - int err; - - err = 0; - if (udba == AuOpt_UDBA_REVAL - && !au_test_fs_remote(h_dentry->d_sb)) { - IMustLock(h_dir); - err = (d_inode(h_dentry->d_parent) != h_dir); - } else if (udba != AuOpt_UDBA_NONE) - err = au_h_verify_dentry(h_dentry, h_parent, br); - - return err; -} - -/* ---------------------------------------------------------------------- */ - -static int au_do_refresh_hdentry(struct dentry *dentry, struct dentry *parent) -{ - int err; - aufs_bindex_t new_bindex, bindex, bend, bwh, bdiropq; - struct au_hdentry tmp, *p, *q; - struct au_dinfo *dinfo; - struct super_block *sb; - - DiMustWriteLock(dentry); - - sb = dentry->d_sb; - dinfo = au_di(dentry); - bend = dinfo->di_bend; - bwh = dinfo->di_bwh; - bdiropq = dinfo->di_bdiropq; - p = dinfo->di_hdentry + dinfo->di_bstart; - for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) { - if (!p->hd_dentry) - continue; - - new_bindex = au_br_index(sb, p->hd_id); - if (new_bindex == bindex) - continue; - - if (dinfo->di_bwh == bindex) - bwh = new_bindex; - if (dinfo->di_bdiropq == bindex) - bdiropq = new_bindex; - if (new_bindex < 0) { - au_hdput(p); - p->hd_dentry = NULL; - continue; - } - - /* swap two lower dentries, and loop again */ - q = dinfo->di_hdentry + new_bindex; - tmp = *q; - *q = *p; - *p = tmp; - if (tmp.hd_dentry) { - bindex--; - p--; - } - } - - dinfo->di_bwh = -1; - if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh)) - dinfo->di_bwh = bwh; - - dinfo->di_bdiropq = -1; - if (bdiropq >= 0 - && bdiropq <= au_sbend(sb) - && au_sbr_whable(sb, bdiropq)) - dinfo->di_bdiropq = bdiropq; - - err = -EIO; - dinfo->di_bstart = -1; - dinfo->di_bend = -1; - bend = au_dbend(parent); - p = dinfo->di_hdentry; - for (bindex = 0; bindex <= bend; bindex++, p++) - if (p->hd_dentry) { - dinfo->di_bstart = bindex; - break; - } - - if (dinfo->di_bstart >= 0) { - p = dinfo->di_hdentry + bend; - for (bindex = bend; bindex >= 0; bindex--, p--) - if (p->hd_dentry) { - dinfo->di_bend = bindex; - err = 0; - break; - } - } - - return err; -} - -static void au_do_hide(struct dentry *dentry) -{ - struct inode *inode; - - if (d_really_is_positive(dentry)) { - inode = d_inode(dentry); - if (!d_is_dir(dentry)) { - if (inode->i_nlink && !d_unhashed(dentry)) - drop_nlink(inode); - } else { - clear_nlink(inode); - /* stop next lookup */ - inode->i_flags |= S_DEAD; - } - smp_mb(); /* necessary? */ - } - d_drop(dentry); -} - -static int au_hide_children(struct dentry *parent) -{ - int err, i, j, ndentry; - struct au_dcsub_pages dpages; - struct au_dpage *dpage; - struct dentry *dentry; - - err = au_dpages_init(&dpages, GFP_NOFS); - if (unlikely(err)) - goto out; - err = au_dcsub_pages(&dpages, parent, NULL, NULL); - if (unlikely(err)) - goto out_dpages; - - /* in reverse order */ - for (i = dpages.ndpage - 1; i >= 0; i--) { - dpage = dpages.dpages + i; - ndentry = dpage->ndentry; - for (j = ndentry - 1; j >= 0; j--) { - dentry = dpage->dentries[j]; - if (dentry != parent) - au_do_hide(dentry); - } - } - -out_dpages: - au_dpages_free(&dpages); -out: - return err; -} - -static void au_hide(struct dentry *dentry) -{ - int err; - - AuDbgDentry(dentry); - if (d_is_dir(dentry)) { - /* shrink_dcache_parent(dentry); */ - err = au_hide_children(dentry); - if (unlikely(err)) - AuIOErr("%pd, failed hiding children, ignored %d\n", - dentry, err); - } - au_do_hide(dentry); -} - -/* - * By adding a dirty branch, a cached dentry may be affected in various ways. - * - * a dirty branch is added - * - on the top of layers - * - in the middle of layers - * - to the bottom of layers - * - * on the added branch there exists - * - a whiteout - * - a diropq - * - a same named entry - * + exist - * * negative --> positive - * * positive --> positive - * - type is unchanged - * - type is changed - * + doesn't exist - * * negative --> negative - * * positive --> negative (rejected by au_br_del() for non-dir case) - * - none - */ -static int au_refresh_by_dinfo(struct dentry *dentry, struct au_dinfo *dinfo, - struct au_dinfo *tmp) -{ - int err; - aufs_bindex_t bindex, bend; - struct { - struct dentry *dentry; - struct inode *inode; - mode_t mode; - } orig_h, tmp_h; - struct au_hdentry *hd; - struct inode *inode, *h_inode; - struct dentry *h_dentry; - - err = 0; - AuDebugOn(dinfo->di_bstart < 0); - orig_h.mode = 0; - orig_h.dentry = dinfo->di_hdentry[dinfo->di_bstart].hd_dentry; - orig_h.inode = NULL; - if (d_is_positive(orig_h.dentry)) { - orig_h.inode = d_inode(orig_h.dentry); - orig_h.mode = orig_h.inode->i_mode & S_IFMT; - } - memset(&tmp_h, 0, sizeof(tmp_h)); - if (tmp->di_bstart >= 0) { - tmp_h.dentry = tmp->di_hdentry[tmp->di_bstart].hd_dentry; - tmp_h.inode = NULL; - if (d_is_positive(tmp_h.dentry)) { - tmp_h.inode = d_inode(tmp_h.dentry); - tmp_h.mode = tmp_h.inode->i_mode & S_IFMT; - } - } - - inode = NULL; - if (d_really_is_positive(dentry)) - inode = d_inode(dentry); - if (!orig_h.inode) { - AuDbg("nagative originally\n"); - if (inode) { - au_hide(dentry); - goto out; - } - AuDebugOn(inode); - AuDebugOn(dinfo->di_bstart != dinfo->di_bend); - AuDebugOn(dinfo->di_bdiropq != -1); - - if (!tmp_h.inode) { - AuDbg("negative --> negative\n"); - /* should have only one negative lower */ - if (tmp->di_bstart >= 0 - && tmp->di_bstart < dinfo->di_bstart) { - AuDebugOn(tmp->di_bstart != tmp->di_bend); - AuDebugOn(dinfo->di_bstart != dinfo->di_bend); - au_set_h_dptr(dentry, dinfo->di_bstart, NULL); - au_di_cp(dinfo, tmp); - hd = tmp->di_hdentry + tmp->di_bstart; - au_set_h_dptr(dentry, tmp->di_bstart, - dget(hd->hd_dentry)); - } - au_dbg_verify_dinode(dentry); - } else { - AuDbg("negative --> positive\n"); - /* - * similar to the behaviour of creating with bypassing - * aufs. - * unhash it in order to force an error in the - * succeeding create operation. - * we should not set S_DEAD here. - */ - d_drop(dentry); - /* au_di_swap(tmp, dinfo); */ - au_dbg_verify_dinode(dentry); - } - } else { - AuDbg("positive originally\n"); - /* inode may be NULL */ - AuDebugOn(inode && (inode->i_mode & S_IFMT) != orig_h.mode); - if (!tmp_h.inode) { - AuDbg("positive --> negative\n"); - /* or bypassing aufs */ - au_hide(dentry); - if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_bstart) - dinfo->di_bwh = tmp->di_bwh; - if (inode) - err = au_refresh_hinode_self(inode); - au_dbg_verify_dinode(dentry); - } else if (orig_h.mode == tmp_h.mode) { - AuDbg("positive --> positive, same type\n"); - if (!S_ISDIR(orig_h.mode) - && dinfo->di_bstart > tmp->di_bstart) { - /* - * similar to the behaviour of removing and - * creating. - */ - au_hide(dentry); - if (inode) - err = au_refresh_hinode_self(inode); - au_dbg_verify_dinode(dentry); - } else { - /* fill empty slots */ - if (dinfo->di_bstart > tmp->di_bstart) - dinfo->di_bstart = tmp->di_bstart; - if (dinfo->di_bend < tmp->di_bend) - dinfo->di_bend = tmp->di_bend; - dinfo->di_bwh = tmp->di_bwh; - dinfo->di_bdiropq = tmp->di_bdiropq; - hd = tmp->di_hdentry; - bend = dinfo->di_bend; - for (bindex = tmp->di_bstart; bindex <= bend; - bindex++) { - if (au_h_dptr(dentry, bindex)) - continue; - h_dentry = hd[bindex].hd_dentry; - if (!h_dentry) - continue; - AuDebugOn(d_is_negative(h_dentry)); - h_inode = d_inode(h_dentry); - AuDebugOn(orig_h.mode - != (h_inode->i_mode - & S_IFMT)); - au_set_h_dptr(dentry, bindex, - dget(h_dentry)); - } - err = au_refresh_hinode(inode, dentry); - au_dbg_verify_dinode(dentry); - } - } else { - AuDbg("positive --> positive, different type\n"); - /* similar to the behaviour of removing and creating */ - au_hide(dentry); - if (inode) - err = au_refresh_hinode_self(inode); - au_dbg_verify_dinode(dentry); - } - } - -out: - return err; -} - -int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) -{ - int err, ebrange; - unsigned int sigen; - struct au_dinfo *dinfo, *tmp; - struct super_block *sb; - struct inode *inode; - - DiMustWriteLock(dentry); - AuDebugOn(IS_ROOT(dentry)); - AuDebugOn(d_really_is_negative(parent)); - - sb = dentry->d_sb; - sigen = au_sigen(sb); - err = au_digen_test(parent, sigen); - if (unlikely(err)) - goto out; - - dinfo = au_di(dentry); - err = au_di_realloc(dinfo, au_sbend(sb) + 1); - if (unlikely(err)) - goto out; - ebrange = au_dbrange_test(dentry); - if (!ebrange) - ebrange = au_do_refresh_hdentry(dentry, parent); - - if (d_unhashed(dentry) || ebrange /* || dinfo->di_tmpfile */) { - AuDebugOn(au_dbstart(dentry) < 0 && au_dbend(dentry) >= 0); - if (d_really_is_positive(dentry)) { - inode = d_inode(dentry); - err = au_refresh_hinode_self(inode); - } - au_dbg_verify_dinode(dentry); - if (!err) - goto out_dgen; /* success */ - goto out; - } - - /* temporary dinfo */ - AuDbgDentry(dentry); - err = -ENOMEM; - tmp = au_di_alloc(sb, AuLsc_DI_TMP); - if (unlikely(!tmp)) - goto out; - au_di_swap(tmp, dinfo); - /* returns the number of positive dentries */ - /* - * if current working dir is removed, it returns an error. - * but the dentry is legal. - */ - err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); - AuDbgDentry(dentry); - au_di_swap(tmp, dinfo); - if (err == -ENOENT) - err = 0; - if (err >= 0) { - /* compare/refresh by dinfo */ - AuDbgDentry(dentry); - err = au_refresh_by_dinfo(dentry, dinfo, tmp); - au_dbg_verify_dinode(dentry); - AuTraceErr(err); - } - au_rw_write_unlock(&tmp->di_rwsem); - au_di_free(tmp); - if (unlikely(err)) - goto out; - -out_dgen: - au_update_digen(dentry); -out: - if (unlikely(err && !(dentry->d_flags & DCACHE_NFSFS_RENAMED))) { - AuIOErr("failed refreshing %pd, %d\n", dentry, err); - AuDbgDentry(dentry); - } - AuTraceErr(err); - return err; -} - -static int au_do_h_d_reval(struct dentry *h_dentry, unsigned int flags, - struct dentry *dentry, aufs_bindex_t bindex) -{ - int err, valid; - - err = 0; - if (!(h_dentry->d_flags & DCACHE_OP_REVALIDATE)) - goto out; - - AuDbg("b%d\n", bindex); - /* - * gave up supporting LOOKUP_CREATE/OPEN for lower fs, - * due to whiteout and branch permission. - */ - flags &= ~(/*LOOKUP_PARENT |*/ LOOKUP_OPEN | LOOKUP_CREATE - | LOOKUP_FOLLOW | LOOKUP_EXCL); - /* it may return tri-state */ - valid = h_dentry->d_op->d_revalidate(h_dentry, flags); - - if (unlikely(valid < 0)) - err = valid; - else if (!valid) - err = -EINVAL; - -out: - AuTraceErr(err); - return err; -} - -/* todo: remove this */ -static int h_d_revalidate(struct dentry *dentry, struct inode *inode, - unsigned int flags, int do_udba) -{ - int err; - umode_t mode, h_mode; - aufs_bindex_t bindex, btail, bstart, ibs, ibe; - unsigned char plus, unhashed, is_root, h_plus, h_nfs, tmpfile; - struct inode *h_inode, *h_cached_inode; - struct dentry *h_dentry; - struct qstr *name, *h_name; - - err = 0; - plus = 0; - mode = 0; - ibs = -1; - ibe = -1; - unhashed = !!d_unhashed(dentry); - is_root = !!IS_ROOT(dentry); - name = &dentry->d_name; - tmpfile = au_di(dentry)->di_tmpfile; - - /* - * Theoretically, REVAL test should be unnecessary in case of - * {FS,I}NOTIFY. - * But {fs,i}notify doesn't fire some necessary events, - * IN_ATTRIB for atime/nlink/pageio - * Let's do REVAL test too. - */ - if (do_udba && inode) { - mode = (inode->i_mode & S_IFMT); - plus = (inode->i_nlink > 0); - ibs = au_ibstart(inode); - ibe = au_ibend(inode); - } - - bstart = au_dbstart(dentry); - btail = bstart; - if (inode && S_ISDIR(inode->i_mode)) - btail = au_dbtaildir(dentry); - for (bindex = bstart; bindex <= btail; bindex++) { - h_dentry = au_h_dptr(dentry, bindex); - if (!h_dentry) - continue; - - AuDbg("b%d, %pd\n", bindex, h_dentry); - h_nfs = !!au_test_nfs(h_dentry->d_sb); - spin_lock(&h_dentry->d_lock); - h_name = &h_dentry->d_name; - if (unlikely(do_udba - && !is_root - && ((!h_nfs - && (unhashed != !!d_unhashed(h_dentry) - || (!tmpfile - && !au_qstreq(name, h_name)) - )) - || (h_nfs - && !(flags & LOOKUP_OPEN) - && (h_dentry->d_flags - & DCACHE_NFSFS_RENAMED))) - )) { - int h_unhashed; - - h_unhashed = d_unhashed(h_dentry); - spin_unlock(&h_dentry->d_lock); - AuDbg("unhash 0x%x 0x%x, %pd %pd\n", - unhashed, h_unhashed, dentry, h_dentry); - goto err; - } - spin_unlock(&h_dentry->d_lock); - - err = au_do_h_d_reval(h_dentry, flags, dentry, bindex); - if (unlikely(err)) - /* do not goto err, to keep the errno */ - break; - - /* todo: plink too? */ - if (!do_udba) - continue; - - /* UDBA tests */ - if (unlikely(!!inode != d_is_positive(h_dentry))) - goto err; - - h_inode = NULL; - if (d_is_positive(h_dentry)) - h_inode = d_inode(h_dentry); - h_plus = plus; - h_mode = mode; - h_cached_inode = h_inode; - if (h_inode) { - h_mode = (h_inode->i_mode & S_IFMT); - h_plus = (h_inode->i_nlink > 0); - } - if (inode && ibs <= bindex && bindex <= ibe) - h_cached_inode = au_h_iptr(inode, bindex); - - if (!h_nfs) { - if (unlikely(plus != h_plus && !tmpfile)) - goto err; - } else { - if (unlikely(!(h_dentry->d_flags & DCACHE_NFSFS_RENAMED) - && !is_root - && !IS_ROOT(h_dentry) - && unhashed != d_unhashed(h_dentry))) - goto err; - } - if (unlikely(mode != h_mode - || h_cached_inode != h_inode)) - goto err; - continue; - -err: - err = -EINVAL; - break; - } - - AuTraceErr(err); - return err; -} - -/* todo: consolidate with do_refresh() and au_reval_for_attr() */ -static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen) -{ - int err; - struct dentry *parent; - - if (!au_digen_test(dentry, sigen)) - return 0; - - parent = dget_parent(dentry); - di_read_lock_parent(parent, AuLock_IR); - AuDebugOn(au_digen_test(parent, sigen)); - au_dbg_verify_gen(parent, sigen); - err = au_refresh_dentry(dentry, parent); - di_read_unlock(parent, AuLock_IR); - dput(parent); - AuTraceErr(err); - return err; -} - -int au_reval_dpath(struct dentry *dentry, unsigned int sigen) -{ - int err; - struct dentry *d, *parent; - - if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR)) - return simple_reval_dpath(dentry, sigen); - - /* slow loop, keep it simple and stupid */ - /* cf: au_cpup_dirs() */ - err = 0; - parent = NULL; - while (au_digen_test(dentry, sigen)) { - d = dentry; - while (1) { - dput(parent); - parent = dget_parent(d); - if (!au_digen_test(parent, sigen)) - break; - d = parent; - } - - if (d != dentry) - di_write_lock_child2(d); - - /* someone might update our dentry while we were sleeping */ - if (au_digen_test(d, sigen)) { - /* - * todo: consolidate with simple_reval_dpath(), - * do_refresh() and au_reval_for_attr(). - */ - di_read_lock_parent(parent, AuLock_IR); - err = au_refresh_dentry(d, parent); - di_read_unlock(parent, AuLock_IR); - } - - if (d != dentry) - di_write_unlock(d); - dput(parent); - if (unlikely(err)) - break; - } - - return err; -} - -/* - * if valid returns 1, otherwise 0. - */ -static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags) -{ - int valid, err; - unsigned int sigen; - unsigned char do_udba; - struct super_block *sb; - struct inode *inode; - - /* todo: support rcu-walk? */ - if (flags & LOOKUP_RCU) - return -ECHILD; - - valid = 0; - if (unlikely(!au_di(dentry))) - goto out; - - valid = 1; - sb = dentry->d_sb; - /* - * todo: very ugly - * i_mutex of parent dir may be held, - * but we should not return 'invalid' due to busy. - */ - err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); - if (unlikely(err)) { - valid = err; - AuTraceErr(err); - goto out; - } - inode = NULL; - if (d_really_is_positive(dentry)) - inode = d_inode(dentry); - if (unlikely(inode && is_bad_inode(inode))) { - err = -EINVAL; - AuTraceErr(err); - goto out_dgrade; - } - if (unlikely(au_dbrange_test(dentry))) { - err = -EINVAL; - AuTraceErr(err); - goto out_dgrade; - } - - sigen = au_sigen(sb); - if (au_digen_test(dentry, sigen)) { - AuDebugOn(IS_ROOT(dentry)); - err = au_reval_dpath(dentry, sigen); - if (unlikely(err)) { - AuTraceErr(err); - goto out_dgrade; - } - } - di_downgrade_lock(dentry, AuLock_IR); - - err = -EINVAL; - if (!(flags & (LOOKUP_OPEN | LOOKUP_EMPTY)) - && inode - && !(inode->i_state && I_LINKABLE) - && (IS_DEADDIR(inode) || !inode->i_nlink)) - goto out_inval; - - do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); - if (do_udba && inode) { - aufs_bindex_t bstart = au_ibstart(inode); - struct inode *h_inode; - - if (bstart >= 0) { - h_inode = au_h_iptr(inode, bstart); - if (h_inode && au_test_higen(inode, h_inode)) - goto out_inval; - } - } - - err = h_d_revalidate(dentry, inode, flags, do_udba); - if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { - err = -EIO; - AuDbg("both of real entry and whiteout found, %p, err %d\n", - dentry, err); - } - goto out_inval; - -out_dgrade: - di_downgrade_lock(dentry, AuLock_IR); -out_inval: - aufs_read_unlock(dentry, AuLock_IR); - AuTraceErr(err); - valid = !err; -out: - if (!valid) { - AuDbg("%pd invalid, %d\n", dentry, valid); - d_drop(dentry); - } - return valid; -} - -static void aufs_d_release(struct dentry *dentry) -{ - if (au_di(dentry)) { - au_di_fin(dentry); - au_hn_di_reinit(dentry); - } -} - -const struct dentry_operations aufs_dop = { - .d_revalidate = aufs_d_revalidate, - .d_weak_revalidate = aufs_d_revalidate, - .d_release = aufs_d_release -}; |