]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 26 May 2011 16:52:14 +0000 (09:52 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 26 May 2011 16:52:14 +0000 (09:52 -0700)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6: (25 commits)
  cifs: remove unnecessary dentry_unhash on rmdir/rename_dir
  ocfs2: remove unnecessary dentry_unhash on rmdir/rename_dir
  exofs: remove unnecessary dentry_unhash on rmdir/rename_dir
  nfs: remove unnecessary dentry_unhash on rmdir/rename_dir
  ext2: remove unnecessary dentry_unhash on rmdir/rename_dir
  ext3: remove unnecessary dentry_unhash on rmdir/rename_dir
  ext4: remove unnecessary dentry_unhash on rmdir/rename_dir
  btrfs: remove unnecessary dentry_unhash in rmdir/rename_dir
  ceph: remove unnecessary dentry_unhash calls
  vfs: clean up vfs_rename_other
  vfs: clean up vfs_rename_dir
  vfs: clean up vfs_rmdir
  vfs: fix vfs_rename_dir for FS_RENAME_DOES_D_MOVE filesystems
  libfs: drop unneeded dentry_unhash
  vfs: update dentry_unhash() comment
  vfs: push dentry_unhash on rename_dir into file systems
  vfs: push dentry_unhash on rmdir into file systems
  vfs: remove dget() from dentry_unhash()
  vfs: dentry_unhash immediately prior to rmdir
  vfs: Block mmapped writes while the fs is frozen
  ...

1  2 
fs/Kconfig
fs/configfs/dir.c
fs/fat/namei_msdos.c
fs/fat/namei_vfat.c
fs/fuse/dir.c
fs/hpfs/namei.c
fs/namei.c
fs/ubifs/dir.c

diff --combined fs/Kconfig
index f6edba2e069f3a4a13f3c06a4786cfa9ed2532c3,efb7d4ec6fcfa496dcd0f0b2d4988ee4f387429f..19891aab9c6ed7255388f175eabfcd7e4f5bb253
@@@ -47,7 -47,7 +47,7 @@@ config FS_POSIX_AC
        def_bool n
  
  config EXPORTFS
-       bool
+       tristate
  
  config FILE_LOCKING
        bool "Enable POSIX file locking API" if EXPERT
@@@ -124,7 -124,6 +124,7 @@@ config TMPF
  config TMPFS_POSIX_ACL
        bool "Tmpfs POSIX Access Control Lists"
        depends on TMPFS
 +      select TMPFS_XATTR
        select GENERIC_ACL
        help
          POSIX Access Control Lists (ACLs) support permissions for users and
  
          If you don't know what Access Control Lists are, say N.
  
 +config TMPFS_XATTR
 +      bool "Tmpfs extended attributes"
 +      depends on TMPFS
 +      default n
 +      help
 +        Extended attributes are name:value pairs associated with inodes by
 +        the kernel or by users (see the attr(5) manual page, or visit
 +        <http://acl.bestbits.at/> for details).
 +
 +        Currently this enables support for the trusted.* and
 +        security.* namespaces.
 +
 +        You need this for POSIX ACL support on tmpfs.
 +
 +        If unsure, say N.
 +
  config HUGETLBFS
        bool "HugeTLB file system support"
        depends on X86 || IA64 || SPARC64 || (S390 && 64BIT) || \
diff --combined fs/configfs/dir.c
index 9a37a9b6de3a23f026f6dc0d9c2906e07b1f8bf0,9908c20bb1a557d7d0a2976ea352c5c980c197f2..9d17d350abc55ec3dfffca5282ba12f2d418facd
@@@ -53,14 -53,11 +53,14 @@@ DEFINE_SPINLOCK(configfs_dirent_lock)
  static void configfs_d_iput(struct dentry * dentry,
                            struct inode * inode)
  {
 -      struct configfs_dirent * sd = dentry->d_fsdata;
 +      struct configfs_dirent *sd = dentry->d_fsdata;
  
        if (sd) {
                BUG_ON(sd->s_dentry != dentry);
 +              /* Coordinate with configfs_readdir */
 +              spin_lock(&configfs_dirent_lock);
                sd->s_dentry = NULL;
 +              spin_unlock(&configfs_dirent_lock);
                configfs_put(sd);
        }
        iput(inode);
@@@ -692,8 -689,7 +692,8 @@@ static int create_default_group(struct 
                        sd = child->d_fsdata;
                        sd->s_type |= CONFIGFS_USET_DEFAULT;
                } else {
 -                      d_delete(child);
 +                      BUG_ON(child->d_inode);
 +                      d_drop(child);
                        dput(child);
                }
        }
@@@ -1359,6 -1355,8 +1359,8 @@@ static int configfs_rmdir(struct inode 
        struct module *subsys_owner = NULL, *dead_item_owner = NULL;
        int ret;
  
+       dentry_unhash(dentry);
        if (dentry->d_parent == configfs_sb->s_root)
                return -EPERM;
  
@@@ -1549,7 -1547,7 +1551,7 @@@ static int configfs_readdir(struct fil
        struct configfs_dirent * parent_sd = dentry->d_fsdata;
        struct configfs_dirent *cursor = filp->private_data;
        struct list_head *p, *q = &cursor->s_sibling;
 -      ino_t ino;
 +      ino_t ino = 0;
        int i = filp->f_pos;
  
        switch (i) {
                                struct configfs_dirent *next;
                                const char * name;
                                int len;
 +                              struct inode *inode = NULL;
  
                                next = list_entry(p, struct configfs_dirent,
                                                   s_sibling);
  
                                name = configfs_get_name(next);
                                len = strlen(name);
 -                              if (next->s_dentry)
 -                                      ino = next->s_dentry->d_inode->i_ino;
 -                              else
 +
 +                              /*
 +                               * We'll have a dentry and an inode for
 +                               * PINNED items and for open attribute
 +                               * files.  We lock here to prevent a race
 +                               * with configfs_d_iput() clearing
 +                               * s_dentry before calling iput().
 +                               *
 +                               * Why do we go to the trouble?  If
 +                               * someone has an attribute file open,
 +                               * the inode number should match until
 +                               * they close it.  Beyond that, we don't
 +                               * care.
 +                               */
 +                              spin_lock(&configfs_dirent_lock);
 +                              dentry = next->s_dentry;
 +                              if (dentry)
 +                                      inode = dentry->d_inode;
 +                              if (inode)
 +                                      ino = inode->i_ino;
 +                              spin_unlock(&configfs_dirent_lock);
 +                              if (!inode)
                                        ino = iunique(configfs_sb, 2);
  
                                if (filldir(dirent, name, len, filp->f_pos, ino,
@@@ -1707,8 -1685,7 +1709,8 @@@ int configfs_register_subsystem(struct 
                err = configfs_attach_group(sd->s_element, &group->cg_item,
                                            dentry);
                if (err) {
 -                      d_delete(dentry);
 +                      BUG_ON(dentry->d_inode);
 +                      d_drop(dentry);
                        dput(dentry);
                } else {
                        spin_lock(&configfs_dirent_lock);
diff --combined fs/fat/namei_msdos.c
index 3b222dafd15b7eb6fa8f8b425a407f8a70dc7a19,c3eccbd0203709c0ffdf798534a56847f67bce9a..be15437c272e913b10e50278bcb28c01cf93fe21
@@@ -326,6 -326,8 +326,8 @@@ static int msdos_rmdir(struct inode *di
        struct fat_slot_info sinfo;
        int err;
  
+       dentry_unhash(dentry);
        lock_super(sb);
        /*
         * Check whether the directory is not in use, then check
@@@ -457,6 -459,9 +459,9 @@@ static int do_msdos_rename(struct inod
        old_inode = old_dentry->d_inode;
        new_inode = new_dentry->d_inode;
  
+       if (new_inode && S_ISDIR(new_inode->i_mode))
+               dentry_unhash(new_dentry);
        err = fat_scan(old_dir, old_name, &old_sinfo);
        if (err) {
                err = -EIO;
@@@ -659,14 -664,14 +664,14 @@@ static const struct inode_operations ms
  
  static void setup(struct super_block *sb)
  {
 +      MSDOS_SB(sb)->dir_ops = &msdos_dir_inode_operations;
        sb->s_d_op = &msdos_dentry_operations;
        sb->s_flags |= MS_NOATIME;
  }
  
  static int msdos_fill_super(struct super_block *sb, void *data, int silent)
  {
 -      return fat_fill_super(sb, data, silent, &msdos_dir_inode_operations,
 -                           0, setup);
 +      return fat_fill_super(sb, data, silent, 0, setup);
  }
  
  static struct dentry *msdos_mount(struct file_system_type *fs_type,
diff --combined fs/fat/namei_vfat.c
index 20b4ea53fdc4e3be434dc7601dce74a3a4658473,e2466b2f8cf26c6842dbbb7517b9319a9658eb2a..c61a6789f36cb2642b324c05ea8f7a6fd936fe28
@@@ -824,6 -824,8 +824,8 @@@ static int vfat_rmdir(struct inode *dir
        struct fat_slot_info sinfo;
        int err;
  
+       dentry_unhash(dentry);
        lock_super(sb);
  
        err = fat_dir_empty(inode);
@@@ -931,6 -933,9 +933,9 @@@ static int vfat_rename(struct inode *ol
        int err, is_dir, update_dotdot, corrupt = 0;
        struct super_block *sb = old_dir->i_sb;
  
+       if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+               dentry_unhash(new_dentry);
        old_sinfo.bh = sinfo.bh = dotdot_bh = NULL;
        old_inode = old_dentry->d_inode;
        new_inode = new_dentry->d_inode;
@@@ -1065,7 -1070,6 +1070,7 @@@ static const struct inode_operations vf
  
  static void setup(struct super_block *sb)
  {
 +      MSDOS_SB(sb)->dir_ops = &vfat_dir_inode_operations;
        if (MSDOS_SB(sb)->options.name_check != 's')
                sb->s_d_op = &vfat_ci_dentry_ops;
        else
  
  static int vfat_fill_super(struct super_block *sb, void *data, int silent)
  {
 -      return fat_fill_super(sb, data, silent, &vfat_dir_inode_operations,
 -                           1, setup);
 +      return fat_fill_super(sb, data, silent, 1, setup);
  }
  
  static struct dentry *vfat_mount(struct file_system_type *fs_type,
diff --combined fs/fuse/dir.c
index b32eb29a4e6ff4f8dd892b1af9ea74d712f148ee,e462a7a281bfb5b46565cf1dbbfdf6b65085f91c..0d0e3faddcfa0bbd9ddac898d9e7caa0fc2b57bf
@@@ -174,7 -174,7 +174,7 @@@ static int fuse_dentry_revalidate(struc
                if (!inode)
                        return 0;
  
 -              if (nd->flags & LOOKUP_RCU)
 +              if (nd && (nd->flags & LOOKUP_RCU))
                        return -ECHILD;
  
                fc = get_fuse_conn(inode);
@@@ -667,6 -667,8 +667,8 @@@ static int fuse_rmdir(struct inode *dir
        if (IS_ERR(req))
                return PTR_ERR(req);
  
+       dentry_unhash(entry);
        req->in.h.opcode = FUSE_RMDIR;
        req->in.h.nodeid = get_node_id(dir);
        req->in.numargs = 1;
@@@ -691,6 -693,10 +693,10 @@@ static int fuse_rename(struct inode *ol
        struct fuse_rename_in inarg;
        struct fuse_conn *fc = get_fuse_conn(olddir);
        struct fuse_req *req = fuse_get_req(fc);
+       if (newent->d_inode && S_ISDIR(newent->d_inode->i_mode))
+               dentry_unhash(newent);
        if (IS_ERR(req))
                return PTR_ERR(req);
  
diff --combined fs/hpfs/namei.c
index 1f05839c27a7fca1f24c99dda71adf07bb2930c5,d3db95f51a4e339a49295dc819898bc1e0532ab5..ff0ce21c0867065f2efbcb30fbfee929ee6e4e09
@@@ -29,7 -29,7 +29,7 @@@ static int hpfs_mkdir(struct inode *dir
        fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh);
        if (!fnode)
                goto bail;
 -      dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0, 1);
 +      dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0);
        if (!dnode)
                goto bail1;
        memset(&dee, 0, sizeof dee);
@@@ -37,8 -37,8 +37,8 @@@
        if (!(mode & 0222)) dee.read_only = 1;
        /*dee.archive = 0;*/
        dee.hidden = name[0] == '.';
 -      dee.fnode = fno;
 -      dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds());
 +      dee.fnode = cpu_to_le32(fno);
 +      dee.creation_date = dee.write_date = dee.read_date = cpu_to_le32(gmt_to_local(dir->i_sb, get_seconds()));
        result = new_inode(dir->i_sb);
        if (!result)
                goto bail2;
@@@ -46,7 -46,7 +46,7 @@@
        result->i_ino = fno;
        hpfs_i(result)->i_parent_dir = dir->i_ino;
        hpfs_i(result)->i_dno = dno;
 -      result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date);
 +      result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, le32_to_cpu(dee.creation_date));
        result->i_ctime.tv_nsec = 0; 
        result->i_mtime.tv_nsec = 0; 
        result->i_atime.tv_nsec = 0; 
@@@ -60,7 -60,8 +60,7 @@@
        if (dee.read_only)
                result->i_mode &= ~0222;
  
 -      mutex_lock(&hpfs_i(dir)->i_mutex);
 -      r = hpfs_add_dirent(dir, name, len, &dee, 0);
 +      r = hpfs_add_dirent(dir, name, len, &dee);
        if (r == 1)
                goto bail3;
        if (r == -1) {
        }
        fnode->len = len;
        memcpy(fnode->name, name, len > 15 ? 15 : len);
 -      fnode->up = dir->i_ino;
 +      fnode->up = cpu_to_le32(dir->i_ino);
        fnode->dirflag = 1;
        fnode->btree.n_free_nodes = 7;
        fnode->btree.n_used_nodes = 1;
 -      fnode->btree.first_free = 0x14;
 -      fnode->u.external[0].disk_secno = dno;
 -      fnode->u.external[0].file_secno = -1;
 +      fnode->btree.first_free = cpu_to_le16(0x14);
 +      fnode->u.external[0].disk_secno = cpu_to_le32(dno);
 +      fnode->u.external[0].file_secno = cpu_to_le32(-1);
        dnode->root_dnode = 1;
 -      dnode->up = fno;
 +      dnode->up = cpu_to_le32(fno);
        de = hpfs_add_de(dir->i_sb, dnode, "\001\001", 2, 0);
 -      de->creation_date = de->write_date = de->read_date = gmt_to_local(dir->i_sb, get_seconds());
 +      de->creation_date = de->write_date = de->read_date = cpu_to_le32(gmt_to_local(dir->i_sb, get_seconds()));
        if (!(mode & 0222)) de->read_only = 1;
        de->first = de->directory = 1;
        /*de->hidden = de->system = 0;*/
 -      de->fnode = fno;
 +      de->fnode = cpu_to_le32(fno);
        mark_buffer_dirty(bh);
        brelse(bh);
        hpfs_mark_4buffers_dirty(&qbh0);
                hpfs_write_inode_nolock(result);
        }
        d_instantiate(dentry, result);
 -      mutex_unlock(&hpfs_i(dir)->i_mutex);
        hpfs_unlock(dir->i_sb);
        return 0;
  bail3:
 -      mutex_unlock(&hpfs_i(dir)->i_mutex);
        iput(result);
  bail2:
        hpfs_brelse4(&qbh0);
@@@ -137,8 -140,8 +137,8 @@@ static int hpfs_create(struct inode *di
        if (!(mode & 0222)) dee.read_only = 1;
        dee.archive = 1;
        dee.hidden = name[0] == '.';
 -      dee.fnode = fno;
 -      dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds());
 +      dee.fnode = cpu_to_le32(fno);
 +      dee.creation_date = dee.write_date = dee.read_date = cpu_to_le32(gmt_to_local(dir->i_sb, get_seconds()));
  
        result = new_inode(dir->i_sb);
        if (!result)
        result->i_op = &hpfs_file_iops;
        result->i_fop = &hpfs_file_ops;
        result->i_nlink = 1;
 -      hpfs_decide_conv(result, name, len);
        hpfs_i(result)->i_parent_dir = dir->i_ino;
 -      result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date);
 +      result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, le32_to_cpu(dee.creation_date));
        result->i_ctime.tv_nsec = 0;
        result->i_mtime.tv_nsec = 0;
        result->i_atime.tv_nsec = 0;
        result->i_data.a_ops = &hpfs_aops;
        hpfs_i(result)->mmu_private = 0;
  
 -      mutex_lock(&hpfs_i(dir)->i_mutex);
 -      r = hpfs_add_dirent(dir, name, len, &dee, 0);
 +      r = hpfs_add_dirent(dir, name, len, &dee);
        if (r == 1)
                goto bail2;
        if (r == -1) {
        }
        fnode->len = len;
        memcpy(fnode->name, name, len > 15 ? 15 : len);
 -      fnode->up = dir->i_ino;
 +      fnode->up = cpu_to_le32(dir->i_ino);
        mark_buffer_dirty(bh);
        brelse(bh);
  
                hpfs_write_inode_nolock(result);
        }
        d_instantiate(dentry, result);
 -      mutex_unlock(&hpfs_i(dir)->i_mutex);
        hpfs_unlock(dir->i_sb);
        return 0;
  
  bail2:
 -      mutex_unlock(&hpfs_i(dir)->i_mutex);
        iput(result);
  bail1:
        brelse(bh);
@@@ -225,8 -232,8 +225,8 @@@ static int hpfs_mknod(struct inode *dir
        if (!(mode & 0222)) dee.read_only = 1;
        dee.archive = 1;
        dee.hidden = name[0] == '.';
 -      dee.fnode = fno;
 -      dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds());
 +      dee.fnode = cpu_to_le32(fno);
 +      dee.creation_date = dee.write_date = dee.read_date = cpu_to_le32(gmt_to_local(dir->i_sb, get_seconds()));
  
        result = new_inode(dir->i_sb);
        if (!result)
        hpfs_init_inode(result);
        result->i_ino = fno;
        hpfs_i(result)->i_parent_dir = dir->i_ino;
 -      result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date);
 +      result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, le32_to_cpu(dee.creation_date));
        result->i_ctime.tv_nsec = 0;
        result->i_mtime.tv_nsec = 0;
        result->i_atime.tv_nsec = 0;
        result->i_blocks = 1;
        init_special_inode(result, mode, rdev);
  
 -      mutex_lock(&hpfs_i(dir)->i_mutex);
 -      r = hpfs_add_dirent(dir, name, len, &dee, 0);
 +      r = hpfs_add_dirent(dir, name, len, &dee);
        if (r == 1)
                goto bail2;
        if (r == -1) {
        }
        fnode->len = len;
        memcpy(fnode->name, name, len > 15 ? 15 : len);
 -      fnode->up = dir->i_ino;
 +      fnode->up = cpu_to_le32(dir->i_ino);
        mark_buffer_dirty(bh);
  
        insert_inode_hash(result);
  
        hpfs_write_inode_nolock(result);
        d_instantiate(dentry, result);
 -      mutex_unlock(&hpfs_i(dir)->i_mutex);
        brelse(bh);
        hpfs_unlock(dir->i_sb);
        return 0;
  bail2:
 -      mutex_unlock(&hpfs_i(dir)->i_mutex);
        iput(result);
  bail1:
        brelse(bh);
@@@ -300,8 -310,8 +300,8 @@@ static int hpfs_symlink(struct inode *d
        memset(&dee, 0, sizeof dee);
        dee.archive = 1;
        dee.hidden = name[0] == '.';
 -      dee.fnode = fno;
 -      dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds());
 +      dee.fnode = cpu_to_le32(fno);
 +      dee.creation_date = dee.write_date = dee.read_date = cpu_to_le32(gmt_to_local(dir->i_sb, get_seconds()));
  
        result = new_inode(dir->i_sb);
        if (!result)
        result->i_ino = fno;
        hpfs_init_inode(result);
        hpfs_i(result)->i_parent_dir = dir->i_ino;
 -      result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date);
 +      result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, le32_to_cpu(dee.creation_date));
        result->i_ctime.tv_nsec = 0;
        result->i_mtime.tv_nsec = 0;
        result->i_atime.tv_nsec = 0;
        result->i_op = &page_symlink_inode_operations;
        result->i_data.a_ops = &hpfs_symlink_aops;
  
 -      mutex_lock(&hpfs_i(dir)->i_mutex);
 -      r = hpfs_add_dirent(dir, name, len, &dee, 0);
 +      r = hpfs_add_dirent(dir, name, len, &dee);
        if (r == 1)
                goto bail2;
        if (r == -1) {
        }
        fnode->len = len;
        memcpy(fnode->name, name, len > 15 ? 15 : len);
 -      fnode->up = dir->i_ino;
 +      fnode->up = cpu_to_le32(dir->i_ino);
        hpfs_set_ea(result, fnode, "SYMLINK", symlink, strlen(symlink));
        mark_buffer_dirty(bh);
        brelse(bh);
  
        hpfs_write_inode_nolock(result);
        d_instantiate(dentry, result);
 -      mutex_unlock(&hpfs_i(dir)->i_mutex);
        hpfs_unlock(dir->i_sb);
        return 0;
  bail2:
 -      mutex_unlock(&hpfs_i(dir)->i_mutex);
        iput(result);
  bail1:
        brelse(bh);
@@@ -361,6 -374,7 +361,6 @@@ static int hpfs_unlink(struct inode *di
        struct hpfs_dirent *de;
        struct inode *inode = dentry->d_inode;
        dnode_secno dno;
 -      fnode_secno fno;
        int r;
        int rep = 0;
        int err;
        hpfs_lock(dir->i_sb);
        hpfs_adjust_length(name, &len);
  again:
 -      mutex_lock(&hpfs_i(inode)->i_parent_mutex);
 -      mutex_lock(&hpfs_i(dir)->i_mutex);
        err = -ENOENT;
        de = map_dirent(dir, hpfs_i(dir)->i_dno, name, len, &dno, &qbh);
        if (!de)
        if (de->directory)
                goto out1;
  
 -      fno = de->fnode;
        r = hpfs_remove_dirent(dir, dno, de, &qbh, 1);
        switch (r) {
        case 1:
                if (rep++)
                        break;
  
 -              mutex_unlock(&hpfs_i(dir)->i_mutex);
 -              mutex_unlock(&hpfs_i(inode)->i_parent_mutex);
                dentry_unhash(dentry);
                if (!d_unhashed(dentry)) {
-                       dput(dentry);
                        hpfs_unlock(dir->i_sb);
                        return -ENOSPC;
                }
                    !S_ISREG(inode->i_mode) ||
                    get_write_access(inode)) {
                        d_rehash(dentry);
-                       dput(dentry);
                } else {
                        struct iattr newattrs;
                        /*printk("HPFS: truncating file before delete.\n");*/
                        newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
                        err = notify_change(dentry, &newattrs);
                        put_write_access(inode);
-                       dput(dentry);
                        if (!err)
                                goto again;
                }
  out1:
        hpfs_brelse4(&qbh);
  out:
 -      mutex_unlock(&hpfs_i(dir)->i_mutex);
 -      mutex_unlock(&hpfs_i(inode)->i_parent_mutex);
        hpfs_unlock(dir->i_sb);
        return err;
  }
@@@ -438,12 -456,17 +435,14 @@@ static int hpfs_rmdir(struct inode *dir
        struct hpfs_dirent *de;
        struct inode *inode = dentry->d_inode;
        dnode_secno dno;
 -      fnode_secno fno;
        int n_items = 0;
        int err;
        int r;
  
+       dentry_unhash(dentry);
        hpfs_adjust_length(name, &len);
        hpfs_lock(dir->i_sb);
 -      mutex_lock(&hpfs_i(inode)->i_parent_mutex);
 -      mutex_lock(&hpfs_i(dir)->i_mutex);
        err = -ENOENT;
        de = map_dirent(dir, hpfs_i(dir)->i_dno, name, len, &dno, &qbh);
        if (!de)
        if (n_items)
                goto out1;
  
 -      fno = de->fnode;
        r = hpfs_remove_dirent(dir, dno, de, &qbh, 1);
        switch (r) {
        case 1:
  out1:
        hpfs_brelse4(&qbh);
  out:
 -      mutex_unlock(&hpfs_i(dir)->i_mutex);
 -      mutex_unlock(&hpfs_i(inode)->i_parent_mutex);
        hpfs_unlock(dir->i_sb);
        return err;
  }
@@@ -535,12 -561,22 +534,16 @@@ static int hpfs_rename(struct inode *ol
        struct buffer_head *bh;
        struct fnode *fnode;
        int err;
+       if (new_inode && S_ISDIR(new_inode->i_mode))
+               dentry_unhash(new_dentry);
        if ((err = hpfs_chk_name(new_name, &new_len))) return err;
        err = 0;
        hpfs_adjust_length(old_name, &old_len);
  
        hpfs_lock(i->i_sb);
        /* order doesn't matter, due to VFS exclusion */
 -      mutex_lock(&hpfs_i(i)->i_parent_mutex);
 -      if (new_inode)
 -              mutex_lock(&hpfs_i(new_inode)->i_parent_mutex);
 -      mutex_lock(&hpfs_i(old_dir)->i_mutex);
 -      if (new_dir != old_dir)
 -              mutex_lock(&hpfs_i(new_dir)->i_mutex);
        
        /* Erm? Moving over the empty non-busy directory is perfectly legal */
        if (new_inode && S_ISDIR(new_inode->i_mode)) {
  
        if (new_dir == old_dir) hpfs_brelse4(&qbh);
  
 -      hpfs_lock_creation(i->i_sb);
 -      if ((r = hpfs_add_dirent(new_dir, new_name, new_len, &de, 1))) {
 -              hpfs_unlock_creation(i->i_sb);
 +      if ((r = hpfs_add_dirent(new_dir, new_name, new_len, &de))) {
                if (r == -1) hpfs_error(new_dir->i_sb, "hpfs_rename: dirent already exists!");
                err = r == 1 ? -ENOSPC : -EFSERROR;
                if (new_dir != old_dir) hpfs_brelse4(&qbh);
        
        if (new_dir == old_dir)
                if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, old_name, old_len, &dno, &qbh))) {
 -                      hpfs_unlock_creation(i->i_sb);
                        hpfs_error(i->i_sb, "lookup succeeded but map dirent failed at #2");
                        err = -ENOENT;
                        goto end1;
                }
  
        if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 0))) {
 -              hpfs_unlock_creation(i->i_sb);
                hpfs_error(i->i_sb, "hpfs_rename: could not remove dirent");
                err = r == 2 ? -ENOSPC : -EFSERROR;
                goto end1;
        }
 -      hpfs_unlock_creation(i->i_sb);
 -      
 +
        end:
        hpfs_i(i)->i_parent_dir = new_dir->i_ino;
        if (S_ISDIR(i->i_mode)) {
                drop_nlink(old_dir);
        }
        if ((fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) {
 -              fnode->up = new_dir->i_ino;
 +              fnode->up = cpu_to_le32(new_dir->i_ino);
                fnode->len = new_len;
                memcpy(fnode->name, new_name, new_len>15?15:new_len);
                if (new_len < 15) memset(&fnode->name[new_len], 0, 15 - new_len);
                mark_buffer_dirty(bh);
                brelse(bh);
        }
 -      hpfs_i(i)->i_conv = hpfs_sb(i->i_sb)->sb_conv;
 -      hpfs_decide_conv(i, new_name, new_len);
  end1:
 -      if (old_dir != new_dir)
 -              mutex_unlock(&hpfs_i(new_dir)->i_mutex);
 -      mutex_unlock(&hpfs_i(old_dir)->i_mutex);
 -      mutex_unlock(&hpfs_i(i)->i_parent_mutex);
 -      if (new_inode)
 -              mutex_unlock(&hpfs_i(new_inode)->i_parent_mutex);
        hpfs_unlock(i->i_sb);
        return err;
  }
diff --combined fs/namei.c
index 6ff858c049c030fd5ea1e142a02c39bb4b51a2dc,f90f0593092acdc959ef54522f18f0c4f0082131..2358b326b2211ef93b912e7a43062763de8999a9
@@@ -179,7 -179,7 +179,7 @@@ EXPORT_SYMBOL(putname)
  static int acl_permission_check(struct inode *inode, int mask, unsigned int flags,
                int (*check_acl)(struct inode *inode, int mask, unsigned int flags))
  {
 -      umode_t                 mode = inode->i_mode;
 +      unsigned int mode = inode->i_mode;
  
        mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
  
@@@ -391,79 -391,28 +391,28 @@@ void path_put(struct path *path
  }
  EXPORT_SYMBOL(path_put);
  
- /**
-  * nameidata_drop_rcu - drop this nameidata out of rcu-walk
-  * @nd: nameidata pathwalk data to drop
-  * Returns: 0 on success, -ECHILD on failure
-  *
+ /*
   * Path walking has 2 modes, rcu-walk and ref-walk (see
-  * Documentation/filesystems/path-lookup.txt). __drop_rcu* functions attempt
-  * to drop out of rcu-walk mode and take normal reference counts on dentries
-  * and vfsmounts to transition to rcu-walk mode. __drop_rcu* functions take
-  * refcounts at the last known good point before rcu-walk got stuck, so
-  * ref-walk may continue from there. If this is not successful (eg. a seqcount
-  * has changed), then failure is returned and path walk restarts from the
-  * beginning in ref-walk mode.
-  *
-  * nameidata_drop_rcu attempts to drop the current nd->path and nd->root into
-  * ref-walk. Must be called from rcu-walk context.
+  * Documentation/filesystems/path-lookup.txt).  In situations when we can't
+  * continue in RCU mode, we attempt to drop out of rcu-walk mode and grab
+  * normal reference counts on dentries and vfsmounts to transition to rcu-walk
+  * mode.  Refcounts are grabbed at the last known good point before rcu-walk
+  * got stuck, so ref-walk may continue from there. If this is not successful
+  * (eg. a seqcount has changed), then failure is returned and it's up to caller
+  * to restart the path walk from the beginning in ref-walk mode.
   */
- static int nameidata_drop_rcu(struct nameidata *nd)
- {
-       struct fs_struct *fs = current->fs;
-       struct dentry *dentry = nd->path.dentry;
-       int want_root = 0;
-       BUG_ON(!(nd->flags & LOOKUP_RCU));
-       if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) {
-               want_root = 1;
-               spin_lock(&fs->lock);
-               if (nd->root.mnt != fs->root.mnt ||
-                               nd->root.dentry != fs->root.dentry)
-                       goto err_root;
-       }
-       spin_lock(&dentry->d_lock);
-       if (!__d_rcu_to_refcount(dentry, nd->seq))
-               goto err;
-       BUG_ON(nd->inode != dentry->d_inode);
-       spin_unlock(&dentry->d_lock);
-       if (want_root) {
-               path_get(&nd->root);
-               spin_unlock(&fs->lock);
-       }
-       mntget(nd->path.mnt);
-       rcu_read_unlock();
-       br_read_unlock(vfsmount_lock);
-       nd->flags &= ~LOOKUP_RCU;
-       return 0;
- err:
-       spin_unlock(&dentry->d_lock);
- err_root:
-       if (want_root)
-               spin_unlock(&fs->lock);
-       return -ECHILD;
- }
- /* Try to drop out of rcu-walk mode if we were in it, otherwise do nothing.  */
- static inline int nameidata_drop_rcu_maybe(struct nameidata *nd)
- {
-       if (nd->flags & LOOKUP_RCU)
-               return nameidata_drop_rcu(nd);
-       return 0;
- }
  
  /**
-  * nameidata_dentry_drop_rcu - drop nameidata and dentry out of rcu-walk
-  * @nd: nameidata pathwalk data to drop
-  * @dentry: dentry to drop
+  * unlazy_walk - try to switch to ref-walk mode.
+  * @nd: nameidata pathwalk data
+  * @dentry: child of nd->path.dentry or NULL
   * Returns: 0 on success, -ECHILD on failure
   *
-  * nameidata_dentry_drop_rcu attempts to drop the current nd->path and nd->root,
-  * and dentry into ref-walk. @dentry must be a path found by a do_lookup call on
-  * @nd. Must be called from rcu-walk context.
+  * unlazy_walk attempts to legitimize the current nd->path, nd->root and dentry
+  * for ref-walk mode.  @dentry must be a path found by a do_lookup call on
+  * @nd or NULL.  Must be called from rcu-walk context.
   */
- static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry)
+ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry)
  {
        struct fs_struct *fs = current->fs;
        struct dentry *parent = nd->path.dentry;
                        goto err_root;
        }
        spin_lock(&parent->d_lock);
-       spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
-       if (!__d_rcu_to_refcount(dentry, nd->seq))
-               goto err;
-       /*
-        * If the sequence check on the child dentry passed, then the child has
-        * not been removed from its parent. This means the parent dentry must
-        * be valid and able to take a reference at this point.
-        */
-       BUG_ON(!IS_ROOT(dentry) && dentry->d_parent != parent);
-       BUG_ON(!parent->d_count);
-       parent->d_count++;
-       spin_unlock(&dentry->d_lock);
+       if (!dentry) {
+               if (!__d_rcu_to_refcount(parent, nd->seq))
+                       goto err_parent;
+               BUG_ON(nd->inode != parent->d_inode);
+       } else {
+               spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
+               if (!__d_rcu_to_refcount(dentry, nd->seq))
+                       goto err_child;
+               /*
+                * If the sequence check on the child dentry passed, then
+                * the child has not been removed from its parent. This
+                * means the parent dentry must be valid and able to take
+                * a reference at this point.
+                */
+               BUG_ON(!IS_ROOT(dentry) && dentry->d_parent != parent);
+               BUG_ON(!parent->d_count);
+               parent->d_count++;
+               spin_unlock(&dentry->d_lock);
+       }
        spin_unlock(&parent->d_lock);
        if (want_root) {
                path_get(&nd->root);
        br_read_unlock(vfsmount_lock);
        nd->flags &= ~LOOKUP_RCU;
        return 0;
- err:
+ err_child:
        spin_unlock(&dentry->d_lock);
+ err_parent:
        spin_unlock(&parent->d_lock);
  err_root:
        if (want_root)
        return -ECHILD;
  }
  
- /* Try to drop out of rcu-walk mode if we were in it, otherwise do nothing.  */
- static inline int nameidata_dentry_drop_rcu_maybe(struct nameidata *nd, struct dentry *dentry)
- {
-       if (nd->flags & LOOKUP_RCU) {
-               if (unlikely(nameidata_dentry_drop_rcu(nd, dentry))) {
-                       nd->flags &= ~LOOKUP_RCU;
-                       if (!(nd->flags & LOOKUP_ROOT))
-                               nd->root.mnt = NULL;
-                       rcu_read_unlock();
-                       br_read_unlock(vfsmount_lock);
-                       return -ECHILD;
-               }
-       }
-       return 0;
- }
- /**
-  * nameidata_drop_rcu_last - drop nameidata ending path walk out of rcu-walk
-  * @nd: nameidata pathwalk data to drop
-  * Returns: 0 on success, -ECHILD on failure
-  *
-  * nameidata_drop_rcu_last attempts to drop the current nd->path into ref-walk.
-  * nd->path should be the final element of the lookup, so nd->root is discarded.
-  * Must be called from rcu-walk context.
-  */
- static int nameidata_drop_rcu_last(struct nameidata *nd)
- {
-       struct dentry *dentry = nd->path.dentry;
-       BUG_ON(!(nd->flags & LOOKUP_RCU));
-       nd->flags &= ~LOOKUP_RCU;
-       if (!(nd->flags & LOOKUP_ROOT))
-               nd->root.mnt = NULL;
-       spin_lock(&dentry->d_lock);
-       if (!__d_rcu_to_refcount(dentry, nd->seq))
-               goto err_unlock;
-       BUG_ON(nd->inode != dentry->d_inode);
-       spin_unlock(&dentry->d_lock);
-       mntget(nd->path.mnt);
-       rcu_read_unlock();
-       br_read_unlock(vfsmount_lock);
-       return 0;
- err_unlock:
-       spin_unlock(&dentry->d_lock);
-       rcu_read_unlock();
-       br_read_unlock(vfsmount_lock);
-       return -ECHILD;
- }
  /**
   * release_open_intent - free up open intent resources
   * @nd: pointer to nameidata
@@@ -606,26 -511,39 +511,39 @@@ do_revalidate(struct dentry *dentry, st
        return dentry;
  }
  
- /*
-  * handle_reval_path - force revalidation of a dentry
-  *
-  * In some situations the path walking code will trust dentries without
-  * revalidating them. This causes problems for filesystems that depend on
-  * d_revalidate to handle file opens (e.g. NFSv4). When FS_REVAL_DOT is set
-  * (which indicates that it's possible for the dentry to go stale), force
-  * a d_revalidate call before proceeding.
+ /**
+  * complete_walk - successful completion of path walk
+  * @nd:  pointer nameidata
   *
-  * Returns 0 if the revalidation was successful. If the revalidation fails,
-  * either return the error returned by d_revalidate or -ESTALE if the
-  * revalidation it just returned 0. If d_revalidate returns 0, we attempt to
-  * invalidate the dentry. It's up to the caller to handle putting references
-  * to the path if necessary.
+  * If we had been in RCU mode, drop out of it and legitimize nd->path.
+  * Revalidate the final result, unless we'd already done that during
+  * the path walk or the filesystem doesn't ask for it.  Return 0 on
+  * success, -error on failure.  In case of failure caller does not
+  * need to drop nd->path.
   */
- static inline int handle_reval_path(struct nameidata *nd)
+ static int complete_walk(struct nameidata *nd)
  {
        struct dentry *dentry = nd->path.dentry;
        int status;
  
+       if (nd->flags & LOOKUP_RCU) {
+               nd->flags &= ~LOOKUP_RCU;
+               if (!(nd->flags & LOOKUP_ROOT))
+                       nd->root.mnt = NULL;
+               spin_lock(&dentry->d_lock);
+               if (unlikely(!__d_rcu_to_refcount(dentry, nd->seq))) {
+                       spin_unlock(&dentry->d_lock);
+                       rcu_read_unlock();
+                       br_read_unlock(vfsmount_lock);
+                       return -ECHILD;
+               }
+               BUG_ON(nd->inode != dentry->d_inode);
+               spin_unlock(&dentry->d_lock);
+               mntget(nd->path.mnt);
+               rcu_read_unlock();
+               br_read_unlock(vfsmount_lock);
+       }
        if (likely(!(nd->flags & LOOKUP_JUMPED)))
                return 0;
  
        if (!status)
                status = -ESTALE;
  
+       path_put(&nd->path);
        return status;
  }
  
@@@ -1241,13 -1160,8 +1160,8 @@@ static int do_lookup(struct nameidata *
                if (likely(__follow_mount_rcu(nd, path, inode, false)))
                        return 0;
  unlazy:
-               if (dentry) {
-                       if (nameidata_dentry_drop_rcu(nd, dentry))
-                               return -ECHILD;
-               } else {
-                       if (nameidata_drop_rcu(nd))
-                               return -ECHILD;
-               }
+               if (unlazy_walk(nd, dentry))
+                       return -ECHILD;
        } else {
                dentry = __d_lookup(parent, name);
        }
@@@ -1303,7 -1217,7 +1217,7 @@@ static inline int may_lookup(struct nam
                int err = exec_permission(nd->inode, IPERM_FLAG_RCU);
                if (err != -ECHILD)
                        return err;
-               if (nameidata_drop_rcu(nd))
+               if (unlazy_walk(nd, NULL))
                        return -ECHILD;
        }
        return exec_permission(nd->inode, 0);
@@@ -1357,8 -1271,12 +1271,12 @@@ static inline int walk_component(struc
                return -ENOENT;
        }
        if (unlikely(inode->i_op->follow_link) && follow) {
-               if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry))
-                       return -ECHILD;
+               if (nd->flags & LOOKUP_RCU) {
+                       if (unlikely(unlazy_walk(nd, path->dentry))) {
+                               terminate_walk(nd);
+                               return -ECHILD;
+                       }
+               }
                BUG_ON(inode != path->dentry->d_inode);
                return 1;
        }
@@@ -1378,12 -1296,12 +1296,12 @@@ static inline int nested_symlink(struc
  {
        int res;
  
 -      BUG_ON(nd->depth >= MAX_NESTED_LINKS);
        if (unlikely(current->link_count >= MAX_NESTED_LINKS)) {
                path_put_conditional(path, nd);
                path_put(&nd->path);
                return -ELOOP;
        }
 +      BUG_ON(nd->depth >= MAX_NESTED_LINKS);
  
        nd->depth++;
        current->link_count++;
@@@ -1657,18 -1575,8 +1575,8 @@@ static int path_lookupat(int dfd, cons
                }
        }
  
-       if (nd->flags & LOOKUP_RCU) {
-               /* went all way through without dropping RCU */
-               BUG_ON(err);
-               if (nameidata_drop_rcu_last(nd))
-                       err = -ECHILD;
-       }
-       if (!err) {
-               err = handle_reval_path(nd);
-               if (err)
-                       path_put(&nd->path);
-       }
+       if (!err)
+               err = complete_walk(nd);
  
        if (!err && nd->flags & LOOKUP_DIRECTORY) {
                if (!nd->inode->i_op->lookup) {
@@@ -2134,13 -2042,9 +2042,9 @@@ static struct file *do_last(struct name
                        return ERR_PTR(error);
                /* fallthrough */
        case LAST_ROOT:
-               if (nd->flags & LOOKUP_RCU) {
-                       if (nameidata_drop_rcu_last(nd))
-                               return ERR_PTR(-ECHILD);
-               }
-               error = handle_reval_path(nd);
+               error = complete_walk(nd);
                if (error)
-                       goto exit;
+                       return ERR_PTR(error);
                audit_inode(pathname, nd->path.dentry);
                if (open_flag & O_CREAT) {
                        error = -EISDIR;
                }
                goto ok;
        case LAST_BIND:
-               /* can't be RCU mode here */
-               error = handle_reval_path(nd);
+               error = complete_walk(nd);
                if (error)
-                       goto exit;
+                       return ERR_PTR(error);
                audit_inode(pathname, dir);
                goto ok;
        }
                if (error) /* symlink */
                        return NULL;
                /* sayonara */
-               if (nd->flags & LOOKUP_RCU) {
-                       if (nameidata_drop_rcu_last(nd))
-                               return ERR_PTR(-ECHILD);
-               }
+               error = complete_walk(nd);
+               if (error)
+                       return ERR_PTR(-ECHILD);
  
                error = -ENOTDIR;
                if (nd->flags & LOOKUP_DIRECTORY) {
        }
  
        /* create side of things */
-       if (nd->flags & LOOKUP_RCU) {
-               if (nameidata_drop_rcu_last(nd))
-                       return ERR_PTR(-ECHILD);
-       }
+       error = complete_walk(nd);
+       if (error)
+               return ERR_PTR(error);
  
        audit_inode(pathname, dir);
        error = -EISDIR;
@@@ -2629,10 -2529,10 +2529,10 @@@ SYSCALL_DEFINE2(mkdir, const char __use
  }
  
  /*
-  * We try to drop the dentry early: we should have
-  * a usage count of 2 if we're the only user of this
-  * dentry, and if that is true (possibly after pruning
-  * the dcache), then we drop the dentry now.
+  * The dentry_unhash() helper will try to drop the dentry early: we
+  * should have a usage count of 2 if we're the only user of this
+  * dentry, and if that is true (possibly after pruning the dcache),
+  * then we drop the dentry now.
   *
   * A low-level filesystem can, if it choses, legally
   * do a
   */
  void dentry_unhash(struct dentry *dentry)
  {
-       dget(dentry);
        shrink_dcache_parent(dentry);
        spin_lock(&dentry->d_lock);
-       if (dentry->d_count == 2)
+       if (dentry->d_count == 1)
                __d_drop(dentry);
        spin_unlock(&dentry->d_lock);
  }
@@@ -2664,25 -2563,26 +2563,26 @@@ int vfs_rmdir(struct inode *dir, struc
                return -EPERM;
  
        mutex_lock(&dentry->d_inode->i_mutex);
-       dentry_unhash(dentry);
+       error = -EBUSY;
        if (d_mountpoint(dentry))
-               error = -EBUSY;
-       else {
-               error = security_inode_rmdir(dir, dentry);
-               if (!error) {
-                       error = dir->i_op->rmdir(dir, dentry);
-                       if (!error) {
-                               dentry->d_inode->i_flags |= S_DEAD;
-                               dont_mount(dentry);
-                       }
-               }
-       }
+               goto out;
+       error = security_inode_rmdir(dir, dentry);
+       if (error)
+               goto out;
+       error = dir->i_op->rmdir(dir, dentry);
+       if (error)
+               goto out;
+       dentry->d_inode->i_flags |= S_DEAD;
+       dont_mount(dentry);
+ out:
        mutex_unlock(&dentry->d_inode->i_mutex);
-       if (!error) {
+       if (!error)
                d_delete(dentry);
-       }
-       dput(dentry);
        return error;
  }
  
@@@ -3053,12 -2953,7 +2953,7 @@@ SYSCALL_DEFINE2(link, const char __use
   *       HOWEVER, it relies on the assumption that any object with ->lookup()
   *       has no more than 1 dentry.  If "hybrid" objects will ever appear,
   *       we'd better make sure that there's no link(2) for them.
-  *    d) some filesystems don't support opened-but-unlinked directories,
-  *       either because of layout or because they are not ready to deal with
-  *       all cases correctly. The latter will be fixed (taking this sort of
-  *       stuff into VFS), but the former is not going away. Solution: the same
-  *       trick as in rmdir().
-  *    e) conversion from fhandle to dentry may come in the wrong moment - when
+  *    d) conversion from fhandle to dentry may come in the wrong moment - when
   *       we are removing the target. Solution: we will have to grab ->i_mutex
   *       in the fhandle_to_dentry code. [FIXME - current nfsfh.c relies on
   *       ->i_mutex on parents, which works but leads to some truly excessive
@@@ -3068,7 -2963,7 +2963,7 @@@ static int vfs_rename_dir(struct inode 
                          struct inode *new_dir, struct dentry *new_dentry)
  {
        int error = 0;
-       struct inode *target;
+       struct inode *target = new_dentry->d_inode;
  
        /*
         * If we are going to change the parent - check write permissions,
        if (error)
                return error;
  
-       target = new_dentry->d_inode;
        if (target)
                mutex_lock(&target->i_mutex);
-       if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
-               error = -EBUSY;
-       else {
-               if (target)
-                       dentry_unhash(new_dentry);
-               error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
-       }
+       error = -EBUSY;
+       if (d_mountpoint(old_dentry) || d_mountpoint(new_dentry))
+               goto out;
+       error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+       if (error)
+               goto out;
        if (target) {
-               if (!error) {
-                       target->i_flags |= S_DEAD;
-                       dont_mount(new_dentry);
-               }
-               mutex_unlock(&target->i_mutex);
-               if (d_unhashed(new_dentry))
-                       d_rehash(new_dentry);
-               dput(new_dentry);
+               target->i_flags |= S_DEAD;
+               dont_mount(new_dentry);
        }
+ out:
+       if (target)
+               mutex_unlock(&target->i_mutex);
        if (!error)
                if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
                        d_move(old_dentry,new_dentry);
  static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
                            struct inode *new_dir, struct dentry *new_dentry)
  {
-       struct inode *target;
+       struct inode *target = new_dentry->d_inode;
        int error;
  
        error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry);
                return error;
  
        dget(new_dentry);
-       target = new_dentry->d_inode;
        if (target)
                mutex_lock(&target->i_mutex);
+       error = -EBUSY;
        if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
-               error = -EBUSY;
-       else
-               error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
-       if (!error) {
-               if (target)
-                       dont_mount(new_dentry);
-               if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
-                       d_move(old_dentry, new_dentry);
-       }
+               goto out;
+       error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+       if (error)
+               goto out;
+       if (target)
+               dont_mount(new_dentry);
+       if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
+               d_move(old_dentry, new_dentry);
+ out:
        if (target)
                mutex_unlock(&target->i_mutex);
        dput(new_dentry);
diff --combined fs/ubifs/dir.c
index ef5abd38f0bf32a90f30ef6994c81b6d864f93e8,d80810bb4c37d1fbff81c53998775186ccfa88ee..c2b80943560d6f9516468903a9847ae5089e28f8
@@@ -603,7 -603,7 +603,7 @@@ static int ubifs_unlink(struct inode *d
                ubifs_release_budget(c, &req);
        else {
                /* We've deleted something - clean the "no space" flags */
 -              c->nospace = c->nospace_rp = 0;
 +              c->bi.nospace = c->bi.nospace_rp = 0;
                smp_wmb();
        }
        return 0;
@@@ -656,6 -656,8 +656,8 @@@ static int ubifs_rmdir(struct inode *di
        struct ubifs_inode *dir_ui = ubifs_inode(dir);
        struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 2 };
  
+       dentry_unhash(dentry);
        /*
         * Budget request settings: deletion direntry, deletion inode and
         * changing the parent inode. If budgeting fails, go ahead anyway
                ubifs_release_budget(c, &req);
        else {
                /* We've deleted something - clean the "no space" flags */
 -              c->nospace = c->nospace_rp = 0;
 +              c->bi.nospace = c->bi.nospace_rp = 0;
                smp_wmb();
        }
        return 0;
@@@ -976,6 -978,9 +978,9 @@@ static int ubifs_rename(struct inode *o
                        .dirtied_ino_d = ALIGN(old_inode_ui->data_len, 8) };
        struct timespec time;
  
+       if (new_inode && S_ISDIR(new_inode->i_mode))
+               dentry_unhash(new_dentry);
        /*
         * Budget request settings: deletion direntry, new direntry, removing
         * the old inode, and changing old and new parent directory inodes.