int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags) { int err; unsigned int sigen; struct super_block *sb; sb = d1->d_sb; err = si_read_lock(sb, flags); if (unlikely(err)) goto out; di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR)); if (au_ftest_lock(flags, GEN)) { sigen = au_sigen(sb); err = au_digen_test(d1, sigen); AuDebugOn(!err && au_dbrange_test(d1)); if (!err) { err = au_digen_test(d2, sigen); AuDebugOn(!err && au_dbrange_test(d2)); } if (unlikely(err)) aufs_read_and_write_unlock2(d1, d2); } out: return err; }
int au_alloc_dinfo(struct dentry *dentry) { struct au_dinfo *dinfo; struct super_block *sb; int nbr; dinfo = au_cache_alloc_dinfo(); if (unlikely(!dinfo)) goto out; sb = dentry->d_sb; nbr = au_sbend(sb) + 1; if (nbr <= 0) nbr = 1; dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); if (unlikely(!dinfo->di_hdentry)) goto out_dinfo; atomic_set(&dinfo->di_generation, au_sigen(sb)); /* smp_mb(); */ /* atomic_set */ au_rw_init_wlock_nested(&dinfo->di_rwsem, AuLsc_DI_CHILD); dinfo->di_bstart = -1; dinfo->di_bend = -1; dinfo->di_bwh = -1; dinfo->di_bdiropq = -1; dentry->d_fsdata = dinfo; dentry->d_op = &aufs_dop; return 0; /* success */ out_dinfo: au_cache_free_dinfo(dinfo); out: return -ENOMEM; }
/* dentry and super_block lock. call at entry point */ int aufs_read_lock(struct dentry *dentry, int flags) { int err; struct super_block *sb; sb = dentry->d_sb; err = si_read_lock(sb, flags); if (unlikely(err)) goto out; if (au_ftest_lock(flags, DW)) di_write_lock_child(dentry); else di_read_lock_child(dentry, flags); if (au_ftest_lock(flags, GEN)) { err = au_digen_test(dentry, au_sigen(sb)); AuDebugOn(!err && au_dbrange_test(dentry)); if (unlikely(err)) aufs_read_unlock(dentry, flags); } out: return err; }
/* * if valid returns 1, otherwise 0. */ static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { int valid, err; unsigned int sigen; unsigned char do_udba; struct super_block *sb; struct inode *inode; err = -EINVAL; sb = dentry->d_sb; inode = dentry->d_inode; aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW); sigen = au_sigen(sb); if (au_digen(dentry) != sigen) { AuDebugOn(IS_ROOT(dentry)); if (inode) err = au_reval_dpath(dentry, sigen); if (unlikely(err)) goto out_dgrade; AuDebugOn(au_digen(dentry) != sigen); } if (inode && au_iigen(inode) != sigen) { AuDebugOn(IS_ROOT(dentry)); err = au_refresh_hinode(inode, dentry); if (unlikely(err)) goto out_dgrade; AuDebugOn(au_iigen(inode) != sigen); } di_downgrade_lock(dentry, AuLock_IR); AuDebugOn(au_digen(dentry) != sigen); AuDebugOn(inode && au_iigen(inode) != sigen); err = -EINVAL; do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); if (do_udba && inode) { aufs_bindex_t bstart = au_ibstart(inode); if (bstart >= 0 && au_test_higen(inode, au_h_iptr(inode, bstart))) goto out; } err = h_d_revalidate(dentry, inode, nd, do_udba); if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) /* both of real entry and whiteout found */ err = -EIO; goto out; out_dgrade: di_downgrade_lock(dentry, AuLock_IR); out: aufs_read_unlock(dentry, AuLock_IR); AuTraceErr(err); valid = !err; if (!valid) AuDbg("%.*s invalid\n", AuDLNPair(dentry)); return valid; }
/* common function to regular file and dir */ int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), int wlock) { int err; unsigned int sigen, figen; aufs_bindex_t bstart; unsigned char pseudo_link; struct dentry *dentry; struct inode *inode; err = 0; dentry = file->f_dentry; inode = dentry->d_inode; AuDebugOn(au_special_file(inode->i_mode)); sigen = au_sigen(dentry->d_sb); fi_write_lock(file); figen = au_figen(file); di_write_lock_child(dentry); bstart = au_dbstart(dentry); pseudo_link = (bstart != au_ibstart(inode)); if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) { if (!wlock) { di_downgrade_lock(dentry, AuLock_IR); fi_downgrade_lock(file); } goto out; /* success */ } AuDbg("sigen %d, figen %d\n", sigen, figen); if (sigen != au_digen(dentry) || sigen != au_iigen(inode)) { err = au_reval_dpath(dentry, sigen); if (unlikely(err < 0)) goto out; AuDebugOn(au_digen(dentry) != sigen || au_iigen(inode) != sigen); } err = refresh_file(file, reopen); if (!err) { if (!wlock) { di_downgrade_lock(dentry, AuLock_IR); fi_downgrade_lock(file); } } else { di_write_unlock(dentry); fi_write_unlock(file); } out: return err; }
static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, ino_t dir_ino) { struct dentry *dentry, *d; struct inode *inode; au_gen_t sigen; LKTRTrace("i%lu, diri%lu\n", (unsigned long)ino, (unsigned long)dir_ino); dentry = NULL; inode = ilookup(sb, ino); if (!inode) goto out; dentry = ERR_PTR(-ESTALE); sigen = au_sigen(sb); if (unlikely(is_bad_inode(inode) || IS_DEADDIR(inode) || sigen != au_iigen(inode))) goto out_iput; dentry = NULL; if (!dir_ino || S_ISDIR(inode->i_mode)) dentry = d_find_alias(inode); else { spin_lock(&dcache_lock); list_for_each_entry(d, &inode->i_dentry, d_alias) if (!au_test_anon(d) && d->d_parent->d_inode->i_ino == dir_ino) { dentry = dget_locked(d); break; } spin_unlock(&dcache_lock); } if (unlikely(dentry && sigen != au_digen(dentry))) { dput(dentry); dentry = ERR_PTR(-ESTALE); } out_iput: iput(inode); out: AuTraceErrPtr(dentry); return dentry; }
static int test_children_busy(struct dentry *root, aufs_bindex_t bindex, const unsigned int verbose) { int err; unsigned int sigen; sigen = au_sigen(root->d_sb); DiMustNoWaiters(root); IiMustNoWaiters(root->d_inode); di_write_unlock(root); err = test_dentry_busy(root, bindex, sigen, verbose); if (!err) err = test_inode_busy(root->d_sb, bindex, sigen, verbose); di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */ return err; }
int au_di_init(struct dentry *dentry) { int err; struct super_block *sb; struct au_dinfo *dinfo; err = 0; sb = dentry->d_sb; dinfo = au_di_alloc(sb, AuLsc_DI_CHILD); if (dinfo) { atomic_set(&dinfo->di_generation, au_sigen(sb)); /* smp_mb(); */ /* atomic_set */ dentry->d_fsdata = dinfo; } else err = -ENOMEM; return err; }
/* * returns the number of found lower positive dentries, * otherwise an error. */ int au_refresh_hdentry(struct dentry *dentry, mode_t type) { int npositive, err; unsigned int sigen; aufs_bindex_t bstart; struct au_dinfo *dinfo; struct super_block *sb; struct dentry *parent; DiMustWriteLock(dentry); sb = dentry->d_sb; AuDebugOn(IS_ROOT(dentry)); sigen = au_sigen(sb); parent = dget_parent(dentry); AuDebugOn(au_digen(parent) != sigen || au_iigen(parent->d_inode) != sigen); dinfo = au_di(dentry); err = au_di_realloc(dinfo, au_sbend(sb) + 1); npositive = err; if (unlikely(err)) goto out; au_do_refresh_hdentry(dinfo->di_hdentry + dinfo->di_bstart, dinfo, parent); npositive = 0; bstart = au_dbstart(parent); if (type != S_IFDIR && dinfo->di_bstart == bstart) goto out_dgen; /* success */ npositive = au_lkup_dentry(dentry, bstart, type, /*nd*/NULL); if (npositive < 0) goto out; if (dinfo->di_bwh >= 0 && dinfo->di_bwh <= dinfo->di_bstart) d_drop(dentry); out_dgen: au_update_digen(dentry); out: dput(parent); AuTraceErr(npositive); return npositive; }
static aufs_bindex_t si_nfsd_read_lock(struct super_block *sb, struct au_nfsd_si_lock *nsi_lock) { aufs_bindex_t bindex; si_read_lock(sb, AuLock_FLUSH); /* branch id may be wrapped around */ bindex = au_br_index(sb, nsi_lock->br_id); if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb)) goto out; /* success */ if (!nsi_lock->force_lock) si_read_unlock(sb); bindex = -1; out: return bindex; }
int au_alloc_dinfo(struct dentry *dentry) { struct au_dinfo *dinfo; struct super_block *sb; int nbr; LKTRTrace("%.*s\n", AuDLNPair(dentry)); AuDebugOn(dentry->d_fsdata); dinfo = au_cache_alloc_dinfo(); if (dinfo) { sb = dentry->d_sb; nbr = au_sbend(sb) + 1; if (nbr <= 0) nbr = 1; dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); if (dinfo->di_hdentry) { au_h_dentry_init_all(dinfo->di_hdentry, nbr); atomic_set(&dinfo->di_generation, au_sigen(sb)); /* smp_mb(); */ /* atomic_set */ au_rw_init_wlock_nested(&dinfo->di_rwsem, AuLsc_DI_CHILD); au_dbg_locked_di_reg(dentry, AuLock_DW, AuLsc_DI_CHILD); dinfo->di_bstart = -1; dinfo->di_bend = -1; dinfo->di_bwh = -1; dinfo->di_bdiropq = -1; dentry->d_fsdata = dinfo; dentry->d_op = &aufs_dop; return 0; /* success */ } au_cache_free_dinfo(dinfo); } AuTraceErr(-ENOMEM); return -ENOMEM; }
/* * 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 struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct dentry *ret, *parent; struct inode *inode; struct super_block *sb; int err, npositive; IMustLock(dir); /* todo: support rcu-walk? */ ret = ERR_PTR(-ECHILD); if (flags & LOOKUP_RCU) goto out; ret = ERR_PTR(-ENAMETOOLONG); if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) goto out; sb = dir->i_sb; err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ret = ERR_PTR(err); if (unlikely(err)) goto out; err = au_di_init(dentry); ret = ERR_PTR(err); if (unlikely(err)) goto out_si; inode = NULL; npositive = 0; /* suppress a warning */ parent = dentry->d_parent; /* dir inode is locked */ di_read_lock_parent(parent, AuLock_IR); err = au_alive_dir(parent); if (!err) err = au_digen_test(parent, au_sigen(sb)); if (!err) { /* regardless LOOKUP_CREATE, always ALLOW_NEG */ npositive = au_lkup_dentry(dentry, au_dbtop(parent), AuLkup_ALLOW_NEG); err = npositive; } di_read_unlock(parent, AuLock_IR); ret = ERR_PTR(err); if (unlikely(err < 0)) goto out_unlock; if (npositive) { inode = au_new_inode(dentry, /*must_new*/0); if (IS_ERR(inode)) { ret = (void *)inode; inode = NULL; goto out_unlock; } } if (inode) atomic_inc(&inode->i_count); ret = d_splice_alias(inode, dentry); #if 0 if (unlikely(d_need_lookup(dentry))) { spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_NEED_LOOKUP; spin_unlock(&dentry->d_lock); } else #endif if (inode) { if (!IS_ERR(ret)) { iput(inode); if (ret && ret != dentry) ii_write_unlock(inode); } else { ii_write_unlock(inode); iput(inode); inode = NULL; } } out_unlock: di_write_unlock(dentry); out_si: si_read_unlock(sb); out: return ret; }
static int aufs_permission(struct inode *inode, int mask) { int err; aufs_bindex_t bindex, bend; const unsigned char isdir = !!S_ISDIR(inode->i_mode), write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); struct inode *h_inode; struct super_block *sb; struct au_branch *br; /* todo: support rcu-walk? */ if (mask & MAY_NOT_BLOCK) return -ECHILD; sb = inode->i_sb; si_read_lock(sb, AuLock_FLUSH); ii_read_lock_child(inode); #if 0 err = au_iigen_test(inode, au_sigen(sb)); if (unlikely(err)) goto out; #endif if (!isdir || write_mask) { err = au_busy_or_stale(); h_inode = au_h_iptr(inode, au_ibstart(inode)); if (unlikely(!h_inode || (h_inode->i_mode & S_IFMT) != (inode->i_mode & S_IFMT))) goto out; err = 0; bindex = au_ibstart(inode); br = au_sbr(sb, bindex); err = h_permission(h_inode, mask, br->br_mnt, br->br_perm); if (write_mask && !err && !special_file(h_inode->i_mode)) { /* test whether the upper writable branch exists */ err = -EROFS; for (; bindex >= 0; bindex--) if (!au_br_rdonly(au_sbr(sb, bindex))) { err = 0; break; } } goto out; } /* non-write to dir */ err = 0; bend = au_ibend(inode); for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) { h_inode = au_h_iptr(inode, bindex); if (h_inode) { err = au_busy_or_stale(); if (unlikely(!S_ISDIR(h_inode->i_mode))) break; br = au_sbr(sb, bindex); err = h_permission(h_inode, mask, br->br_mnt, br->br_perm); } } out: ii_read_unlock(inode); si_read_unlock(sb); return err; }
static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct dentry *ret, *parent; struct inode *inode; struct super_block *sb; int err, npositive, lc_idx; IMustLock(dir); sb = dir->i_sb; err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ret = ERR_PTR(err); if (unlikely(err)) goto out; ret = ERR_PTR(-ENAMETOOLONG); if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) goto out_si; err = au_di_init(dentry); ret = ERR_PTR(err); if (unlikely(err)) goto out_si; inode = NULL; npositive = 0; /* suppress a warning */ parent = dentry->d_parent; /* dir inode is locked */ di_read_lock_parent(parent, AuLock_IR); err = au_alive_dir(parent); if (!err) err = au_digen_test(parent, au_sigen(sb)); if (!err) { npositive = au_lkup_dentry(dentry, au_dbstart(parent), /*type*/0, nd); err = npositive; } di_read_unlock(parent, AuLock_IR); ret = ERR_PTR(err); if (unlikely(err < 0)) goto out_unlock; if (npositive) { inode = au_new_inode(dentry, /*must_new*/0); ret = (void *)inode; } if (IS_ERR(inode)) { inode = NULL; goto out_unlock; } ret = d_splice_alias(inode, dentry); if (unlikely(IS_ERR(ret) && inode)) { ii_write_unlock(inode); lc_idx = AuLcNonDir_IIINFO; if (S_ISLNK(inode->i_mode)) lc_idx = AuLcSymlink_IIINFO; else if (S_ISDIR(inode->i_mode)) lc_idx = AuLcDir_IIINFO; au_rw_class(&au_ii(inode)->ii_rwsem, au_lc_key + lc_idx); iput(inode); } out_unlock: di_write_unlock(dentry); if (unlikely(IS_ERR(ret) && inode)) { lc_idx = AuLcNonDir_DIINFO; if (S_ISLNK(inode->i_mode)) lc_idx = AuLcSymlink_DIINFO; else if (S_ISDIR(inode->i_mode)) lc_idx = AuLcDir_DIINFO; au_rw_class(&au_di(dentry)->di_rwsem, au_lc_key + lc_idx); } out_si: si_read_unlock(sb); out: return ret; }
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(!parent->d_inode); sb = dentry->d_sb; inode = dentry->d_inode; 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) { AuDebugOn(au_dbstart(dentry) < 0 && au_dbend(dentry) >= 0); if (inode) 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, /*nd*/NULL); 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 %.*s, %d\n", AuDLNPair(dentry), err); AuDbgDentry(dentry); } AuTraceErr(err); return err; }
/* * if valid returns 1, otherwise 0. */ static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { int valid, err; unsigned int sigen; unsigned char do_udba; struct super_block *sb; struct inode *inode; valid = 0; if (unlikely(!au_di(dentry))) goto out; valid = 1; sb = dentry->d_sb; inode = dentry->d_inode; /* * 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; } 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 (inode && (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, nd, do_udba); if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { err = -EIO; AuDbg("both of real entry and whiteout found, %.*s, err %d\n", AuDLNPair(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("%.*s invalid, %d\n", AuDLNPair(dentry), valid); d_drop(dentry); } return valid; }
static struct dentry * aufs_decode_fh(struct super_block *sb, __u32 *fh, int fh_len, int fh_type, int (*acceptable)(void *context, struct dentry *de), void *context) { struct dentry *dentry; ino_t ino, dir_ino; aufs_bindex_t bindex; struct au_nfsd_si_lock nsi_lock = { .sigen = fh[Fh_sigen], .br_id = fh[Fh_br_id], .force_lock = 0 }; LKTRTrace("%d, fh{br_id %u, sigen %u, i%u, diri%u, g%u}\n", fh_type, fh[Fh_br_id], fh[Fh_sigen], fh[Fh_ino], fh[Fh_dir_ino], fh[Fh_igen]); AuDebugOn(fh_len < Fh_tail); dentry = ERR_PTR(-ESTALE); /* branch id may be wrapped around */ bindex = si_nfsd_read_lock(sb, &nsi_lock); if (unlikely(bindex < 0)) goto out; nsi_lock.force_lock = 1; /* is this inode still cached? */ ino = decode_ino(fh + Fh_ino); AuDebugOn(ino == AUFS_ROOT_INO); dir_ino = decode_ino(fh + Fh_dir_ino); dentry = decode_by_ino(sb, ino, dir_ino); if (IS_ERR(dentry)) goto out_unlock; if (dentry) goto accept; /* is the parent dir cached? */ dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); if (IS_ERR(dentry)) goto out_unlock; if (dentry) goto accept; /* lookup path */ dentry = decode_by_path(sb, bindex, ino, fh, fh_len, &nsi_lock); if (IS_ERR(dentry)) goto out_unlock; if (unlikely(!dentry)) goto out_unlock; accept: LKTRLabel(accept); if (dentry->d_inode->i_generation == fh[Fh_igen] && acceptable(context, dentry)) goto out_unlock; /* success */ LKTRLabel(stale); dput(dentry); dentry = ERR_PTR(-ESTALE); out_unlock: LKTRLabel(out_unlock); si_read_unlock(sb); out: LKTRLabel(out); if (0 && IS_ERR(dentry)) dentry = ERR_PTR(-ESTALE); AuTraceErrPtr(dentry); return dentry; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) static struct dentry * aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { return aufs_decode_fh(sb, fid->raw, fh_len, fh_type, h_acceptable, /*context*/NULL); } #endif /* KERNEL_VERSION */ /* ---------------------------------------------------------------------- */ static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, int connectable) { int err; aufs_bindex_t bindex, bend; struct super_block *sb, *h_sb; struct inode *inode; struct dentry *parent, *h_parent; struct au_branch *br; LKTRTrace("%.*s, max %d, conn %d\n", AuDLNPair(dentry), *max_len, connectable); AuDebugOn(au_test_anon(dentry)); parent = NULL; err = -ENOSPC; if (unlikely(*max_len <= Fh_tail)) { AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); goto out; } err = 0; //FILEID_ROOT; if (IS_ROOT(dentry)) { AuDebugOn(dentry->d_inode->i_ino != AUFS_ROOT_INO); goto out; } err = -EIO; h_parent = NULL; sb = dentry->d_sb; aufs_read_lock(dentry, AuLock_FLUSH | AuLock_IR); parent = dget_parent(dentry); di_read_lock_parent(parent, !AuLock_IR); inode = dentry->d_inode; AuDebugOn(!inode); #ifdef CONFIG_AUFS_DEBUG { unsigned int mnt_flags = au_mntflags(sb); if (unlikely(!au_opt_test_xino(mnt_flags))) AuWarn1("NFS-exporting requires xino\n"); if (unlikely(0 && !au_opt_test(mnt_flags, UDBA_INOTIFY))) AuWarn1("udba=inotify is recommended " "for NFS-exporting\n"); } #endif bend = au_dbtaildir(parent); for (bindex = au_dbstart(parent); bindex <= bend; bindex++) { h_parent = au_h_dptr(parent, bindex); if (h_parent) { dget(h_parent); break; } } if (unlikely(!h_parent)) goto out_unlock; LKTRTrace("b%d\n", bindex); err = -EPERM; br = au_sbr(sb, bindex); h_sb = br->br_mnt->mnt_sb; if (unlikely(!h_sb->s_export_op)) { AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); goto out_dput; } fh[Fh_br_id] = br->br_id; fh[Fh_sigen] = au_sigen(sb); encode_ino(fh + Fh_ino, inode->i_ino); encode_ino(fh + Fh_dir_ino, parent->d_inode->i_ino); fh[Fh_igen] = inode->i_generation; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) /* it should be set at exporting time */ if (unlikely(!h_sb->s_export_op->find_exported_dentry)) { AuWarn("set default find_exported_dentry for %s\n", au_sbtype(h_sb)); h_sb->s_export_op->find_exported_dentry = find_exported_dentry; } #endif *max_len -= Fh_tail; fh[Fh_h_type] = au_call_encode_fh(h_parent, fh + Fh_tail, max_len, /*connectable or subtreecheck*/0); err = fh[Fh_h_type]; *max_len += Fh_tail; /* todo: macros? */ if (err != 255) err = 99; else AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); out_dput: dput(h_parent); out_unlock: di_read_unlock(parent, !AuLock_IR); dput(parent); aufs_read_unlock(dentry, AuLock_IR); out: AuTraceErr(err); if (unlikely(err < 0)) err = 255; return err; }
static struct dentry * aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { struct dentry *dentry; __u32 *fh = fid->raw; ino_t ino, dir_ino; aufs_bindex_t bindex; struct au_nfsd_si_lock nsi_lock = { .force_lock = 0 }; dentry = ERR_PTR(-ESTALE); /* it should never happen, but the file handle is unreliable */ if (unlikely(fh_len < Fh_tail)) goto out; nsi_lock.sigen = fh[Fh_sigen]; nsi_lock.br_id = fh[Fh_br_id]; /* branch id may be wrapped around */ bindex = si_nfsd_read_lock(sb, &nsi_lock); if (unlikely(bindex < 0)) goto out; nsi_lock.force_lock = 1; /* is this inode still cached? */ ino = decode_ino(fh + Fh_ino); /* it should never happen */ if (unlikely(ino == AUFS_ROOT_INO)) goto out; dir_ino = decode_ino(fh + Fh_dir_ino); dentry = decode_by_ino(sb, ino, dir_ino); if (IS_ERR(dentry)) goto out_unlock; if (dentry) goto accept; /* is the parent dir cached? */ dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); if (IS_ERR(dentry)) goto out_unlock; if (dentry) goto accept; /* lookup path */ dentry = decode_by_path(sb, bindex, ino, fh, fh_len, &nsi_lock); if (IS_ERR(dentry)) goto out_unlock; if (unlikely(!dentry)) /* todo?: make it ESTALE */ goto out_unlock; accept: if (dentry->d_inode->i_generation == fh[Fh_igen]) goto out_unlock; /* success */ dput(dentry); dentry = ERR_PTR(-ESTALE); out_unlock: si_read_unlock(sb); out: AuTraceErrPtr(dentry); return dentry; } #if 0 /* reserved for future use */ /* support subtreecheck option */ static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { struct dentry *parent; __u32 *fh = fid->raw; ino_t dir_ino; dir_ino = decode_ino(fh + Fh_dir_ino); parent = decode_by_ino(sb, dir_ino, 0); if (IS_ERR(parent)) goto out; if (!parent) parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]), dir_ino, fh, fh_len); out: AuTraceErrPtr(parent); return parent; } #endif /* ---------------------------------------------------------------------- */ static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, int connectable) { int err; aufs_bindex_t bindex, bend; struct super_block *sb, *h_sb; struct inode *inode; struct dentry *parent, *h_parent; struct au_branch *br; AuDebugOn(au_test_anon(dentry)); parent = NULL; err = -ENOSPC; if (unlikely(*max_len <= Fh_tail)) { AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); goto out; } err = FILEID_ROOT; if (IS_ROOT(dentry)) { AuDebugOn(dentry->d_inode->i_ino != AUFS_ROOT_INO); goto out; } err = -EIO; h_parent = NULL; sb = dentry->d_sb; aufs_read_lock(dentry, AuLock_FLUSH | AuLock_IR); parent = dget_parent(dentry); di_read_lock_parent(parent, !AuLock_IR); inode = dentry->d_inode; AuDebugOn(!inode); #ifdef CONFIG_AUFS_DEBUG if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) AuWarn1("NFS-exporting requires xino\n"); #endif bend = au_dbtaildir(parent); for (bindex = au_dbstart(parent); bindex <= bend; bindex++) { h_parent = au_h_dptr(parent, bindex); if (h_parent) { dget(h_parent); break; } } if (unlikely(!h_parent)) goto out_unlock; err = -EPERM; br = au_sbr(sb, bindex); h_sb = br->br_mnt->mnt_sb; if (unlikely(!h_sb->s_export_op)) { AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); goto out_dput; } fh[Fh_br_id] = br->br_id; fh[Fh_sigen] = au_sigen(sb); encode_ino(fh + Fh_ino, inode->i_ino); encode_ino(fh + Fh_dir_ino, parent->d_inode->i_ino); fh[Fh_igen] = inode->i_generation; *max_len -= Fh_tail; fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail), max_len, /*connectable or subtreecheck*/0); err = fh[Fh_h_type]; *max_len += Fh_tail; /* todo: macros? */ if (err != 255) err = 99; else AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); out_dput: dput(h_parent); out_unlock: di_read_unlock(parent, !AuLock_IR); dput(parent); aufs_read_unlock(dentry, AuLock_IR); out: if (unlikely(err < 0)) err = 255; return err; }
void au_update_digen(struct dentry *dentry) { atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb)); /* smp_mb(); */ /* atomic_set */ }
static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct dentry *ret, *parent; struct inode *inode; struct super_block *sb; int err, npositive; IMustLock(dir); /* todo: support rcu-walk? */ ret = ERR_PTR(-ECHILD); if (flags & LOOKUP_RCU) goto out; ret = ERR_PTR(-ENAMETOOLONG); if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) goto out; sb = dir->i_sb; err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ret = ERR_PTR(err); if (unlikely(err)) goto out; err = au_di_init(dentry); ret = ERR_PTR(err); if (unlikely(err)) goto out_si; inode = NULL; npositive = 0; /* suppress a warning */ parent = dentry->d_parent; /* dir inode is locked */ di_read_lock_parent(parent, AuLock_IR); err = au_alive_dir(parent); if (!err) err = au_digen_test(parent, au_sigen(sb)); if (!err) { npositive = au_lkup_dentry(dentry, au_dbstart(parent), /*type*/0); err = npositive; } di_read_unlock(parent, AuLock_IR); ret = ERR_PTR(err); if (unlikely(err < 0)) goto out_unlock; if (npositive) { inode = au_new_inode(dentry, /*must_new*/0); if (IS_ERR(inode)) { ret = (void *)inode; inode = NULL; goto out_unlock; } } if (inode) atomic_inc(&inode->i_count); ret = d_splice_alias(inode, dentry); #if 0 if (unlikely(d_need_lookup(dentry))) { spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_NEED_LOOKUP; spin_unlock(&dentry->d_lock); } else #endif if (inode) { if (!IS_ERR(ret)) { iput(inode); if (ret && ret != dentry) ii_write_unlock(inode); } else { ii_write_unlock(inode); iput(inode); inode = NULL; } } out_unlock: di_write_unlock(dentry); if (inode) { /* verbose coding for lock class name */ if (unlikely(S_ISLNK(inode->i_mode))) au_rw_class(&au_di(dentry)->di_rwsem, au_lc_key + AuLcSymlink_DIINFO); else if (unlikely(S_ISDIR(inode->i_mode))) au_rw_class(&au_di(dentry)->di_rwsem, au_lc_key + AuLcDir_DIINFO); else /* likely */ au_rw_class(&au_di(dentry)->di_rwsem, au_lc_key + AuLcNonDir_DIINFO); } out_si: si_read_unlock(sb); out: return ret; }