diff options
Diffstat (limited to 'ubuntu/aufs/i_op_add.c')
-rw-r--r-- | ubuntu/aufs/i_op_add.c | 88 |
1 files changed, 57 insertions, 31 deletions
diff --git a/ubuntu/aufs/i_op_add.c b/ubuntu/aufs/i_op_add.c index 18635718325..cd11360361d 100644 --- a/ubuntu/aufs/i_op_add.c +++ b/ubuntu/aufs/i_op_add.c @@ -81,6 +81,18 @@ out: return err; } +static int au_d_may_add(struct dentry *dentry) +{ + int err; + + err = 0; + if (unlikely(d_unhashed(dentry))) + err = -ENOENT; + if (unlikely(dentry->d_inode)) + err = -EEXIST; + return err; +} + /* * simple tests for the adding inode operations. * following the checks in vfs, plus the parent-child relationship. @@ -236,13 +248,18 @@ static int add_simple(struct inode *dir, struct dentry *dentry, IMustLock(dir); parent = dentry->d_parent; /* dir inode is locked */ - aufs_read_lock(dentry, AuLock_DW); + err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); + if (unlikely(err)) + goto out; + err = au_d_may_add(dentry); + if (unlikely(err)) + goto out_unlock; di_write_lock_parent(parent); wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL, &pin, &wr_dir_args); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) - goto out; + goto out_parent; bstart = au_dbstart(dentry); h_path.dentry = au_h_dptr(dentry, bstart); @@ -275,19 +292,20 @@ static int add_simple(struct inode *dir, struct dentry *dentry, err = -EIO; } au_dtime_revert(&dt); - d_drop(dentry); } au_unpin(&pin); dput(wh_dentry); -out: +out_parent: + di_write_unlock(parent); +out_unlock: if (unlikely(err)) { au_update_dbstart(dentry); d_drop(dentry); } - di_write_unlock(parent); aufs_read_unlock(dentry, AuLock_DW); +out: return err; } @@ -456,19 +474,22 @@ int aufs_link(struct dentry *src_dentry, struct inode *dir, inode = src_dentry->d_inode; IMustLock(inode); - err = -ENOENT; - if (unlikely(!inode->i_nlink)) - goto out; - err = -ENOMEM; a = kzalloc(sizeof(*a), GFP_NOFS); if (unlikely(!a)) goto out; a->parent = dentry->d_parent; /* dir inode is locked */ - err = aufs_read_and_write_lock2(dentry, src_dentry, AuLock_NOPLM); + err = aufs_read_and_write_lock2(dentry, src_dentry, + AuLock_NOPLM | AuLock_GEN); if (unlikely(err)) goto out_kfree; + err = au_d_hashed_positive(src_dentry); + if (unlikely(err)) + goto out_unlock; + err = au_d_may_add(dentry); + if (unlikely(err)) + goto out_unlock; a->src_parent = dget_parent(src_dentry); wr_dir_args.force_btgt = au_dbstart(src_dentry); @@ -479,7 +500,7 @@ int aufs_link(struct dentry *src_dentry, struct inode *dir, &wr_dir_args); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) - goto out_unlock; + goto out_parent; err = 0; sb = dentry->d_sb; @@ -539,34 +560,32 @@ int aufs_link(struct dentry *src_dentry, struct inode *dir, au_cpup_attr_timesizes(dir); inc_nlink(inode); inode->i_ctime = dir->i_ctime; - if (!d_unhashed(a->h_path.dentry)) - d_instantiate(dentry, au_igrab(inode)); - else + d_instantiate(dentry, au_igrab(inode)); + if (d_unhashed(a->h_path.dentry)) /* some filesystem calls d_drop() */ d_drop(dentry); goto out_unpin; /* success */ out_revert: rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, /*force*/0); - if (!rerr) - goto out_dt; - AuIOErr("%.*s reverting failed(%d, %d)\n", - AuDLNPair(dentry), err, rerr); - err = -EIO; -out_dt: - d_drop(dentry); + if (unlikely(rerr)) { + AuIOErr("%.*s reverting failed(%d, %d)\n", + AuDLNPair(dentry), err, rerr); + err = -EIO; + } au_dtime_revert(&dt); out_unpin: au_unpin(&a->pin); out_wh: dput(wh_dentry); +out_parent: + di_write_unlock(a->parent); + dput(a->src_parent); out_unlock: if (unlikely(err)) { au_update_dbstart(dentry); d_drop(dentry); } - di_write_unlock(a->parent); - dput(a->src_parent); aufs_read_and_write_unlock2(dentry, src_dentry); out_kfree: kfree(a); @@ -599,14 +618,20 @@ int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) if (unlikely(!a)) goto out; - aufs_read_lock(dentry, AuLock_DW); + err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); + if (unlikely(err)) + goto out_free; + err = au_d_may_add(dentry); + if (unlikely(err)) + goto out_unlock; + parent = dentry->d_parent; /* dir inode is locked */ di_write_lock_parent(parent); wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, &a->pin, &wr_dir_args); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) - goto out_free; + goto out_parent; sb = dentry->d_sb; bindex = au_dbstart(dentry); @@ -614,7 +639,7 @@ int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) h_path.mnt = au_sbr_mnt(sb, bindex); err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode); if (unlikely(err)) - goto out_unlock; + goto out_unpin; /* make the dir opaque */ diropq = 0; @@ -634,7 +659,7 @@ int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) err = epilog(dir, bindex, wh_dentry, dentry); if (!err) { inc_nlink(dir); - goto out_unlock; /* success */ + goto out_unpin; /* success */ } /* revert */ @@ -658,18 +683,19 @@ out_dir: AuDLNPair(dentry), err, rerr); err = -EIO; } - d_drop(dentry); au_dtime_revert(&a->dt); -out_unlock: +out_unpin: au_unpin(&a->pin); dput(wh_dentry); -out_free: +out_parent: + di_write_unlock(parent); +out_unlock: if (unlikely(err)) { au_update_dbstart(dentry); d_drop(dentry); } - di_write_unlock(parent); aufs_read_unlock(dentry, AuLock_DW); +out_free: kfree(a); out: return err; |