From: Erez Zadok Date: Thu, 10 Jan 2008 17:13:32 +0000 (-0500) Subject: Unionfs: branch-management related locking fixes X-Git-Url: https://git.fsl.cs.stonybrook.edu/?a=commitdiff_plain;h=f5bfec383667fe90ef7551af393d3f57f53e851a;p=unionfs-odf.git Unionfs: branch-management related locking fixes Add necessary locking to dentry/inode branch-configuration, so we get consistent values during branch-management actions. In d_revalidate_chain, ->permission, and ->create, also lock parent dentry. --- diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c index 00500f6b14..8eb4e84340 100644 --- a/fs/unionfs/commonfops.c +++ b/fs/unionfs/commonfops.c @@ -385,6 +385,7 @@ int unionfs_file_revalidate(struct file *file, bool willwrite) * First revalidate the dentry inside struct file, * but not unhashed dentries. */ +reval_dentry: if (unlikely(!d_deleted(dentry) && !__unionfs_d_revalidate_chain(dentry, NULL, willwrite))) { err = -ESTALE; @@ -395,6 +396,11 @@ int unionfs_file_revalidate(struct file *file, bool willwrite) dgen = atomic_read(&UNIONFS_D(dentry)->generation); fgen = atomic_read(&UNIONFS_F(file)->generation); + if (unlikely(sbgen > dgen)) { + pr_debug("unionfs: retry dentry revalidation\n"); + schedule(); + goto reval_dentry; + } BUG_ON(sbgen > dgen); /* diff --git a/fs/unionfs/dentry.c b/fs/unionfs/dentry.c index f045b04ed7..3fce8427f7 100644 --- a/fs/unionfs/dentry.c +++ b/fs/unionfs/dentry.c @@ -221,7 +221,7 @@ bool is_newer_lower(const struct dentry *dentry) if (!dentry || !UNIONFS_D(dentry)) return false; inode = dentry->d_inode; - if (!inode || !UNIONFS_I(inode) || + if (!inode || !UNIONFS_I(inode)->lower_inodes || ibstart(inode) < 0 || ibend(inode) < 0) return false; @@ -313,6 +313,8 @@ bool __unionfs_d_revalidate_chain(struct dentry *dentry, struct nameidata *nd, chain_len = 0; sbgen = atomic_read(&UNIONFS_SB(dentry->d_sb)->generation); dtmp = dentry->d_parent; + if (dentry != dtmp) + unionfs_lock_dentry(dtmp, UNIONFS_DMUTEX_REVAL_PARENT); dgen = atomic_read(&UNIONFS_D(dtmp)->generation); /* XXX: should we check if is_newer_lower all the way up? */ if (unlikely(is_newer_lower(dtmp))) { @@ -333,6 +335,8 @@ bool __unionfs_d_revalidate_chain(struct dentry *dentry, struct nameidata *nd, } purge_inode_data(dtmp->d_inode); } + if (dentry != dtmp) + unionfs_unlock_dentry(dtmp); while (sbgen != dgen) { /* The root entry should always be valid */ BUG_ON(IS_ROOT(dtmp)); diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c index 087c5f2b20..b292f88b3f 100644 --- a/fs/unionfs/inode.c +++ b/fs/unionfs/inode.c @@ -29,6 +29,13 @@ static int unionfs_create(struct inode *parent, struct dentry *dentry, struct nameidata lower_nd; unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_PARENT); + valid = __unionfs_d_revalidate_chain(dentry->d_parent, nd, false); + unionfs_unlock_dentry(dentry->d_parent); + if (unlikely(!valid)) { + err = -ESTALE; /* same as what real_lookup does */ + goto out; + } unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); valid = __unionfs_d_revalidate_chain(dentry, nd, false); @@ -757,6 +764,14 @@ static int unionfs_permission(struct inode *inode, int mask, const int is_file = !S_ISDIR(inode->i_mode); const int write_mask = (mask & MAY_WRITE) && !(mask & MAY_READ); + if (nd) + unionfs_lock_dentry(nd->dentry, UNIONFS_DMUTEX_CHILD); + + if (!UNIONFS_I(inode)->lower_inodes) { + if (is_file) /* dirs can be unlinked but chdir'ed to */ + err = -ESTALE; /* force revalidate */ + goto out; + } bstart = ibstart(inode); bend = ibend(inode); if (unlikely(bstart < 0 || bend < 0)) { @@ -824,6 +839,8 @@ static int unionfs_permission(struct inode *inode, int mask, out: unionfs_check_inode(inode); unionfs_check_nd(nd); + if (nd) + unionfs_unlock_dentry(nd->dentry); return err; }