2011-03-30 22:02:01

by Sage Weil

[permalink] [raw]
Subject: [PATCH 0/7] Do not dentry_unhash() in VFS

Hi Al, everyone,

For the most part the VFS does not hash or unhash dentries outside of
dcache pruning. This is left to the file system namespace methods. The
only exception is on rmdir and directory rename, where we call
dentry_unhash() for the benefit of a few file systems that can't handle
lingering references to the empty and unlinked directory.

This patchset aims to clean this up by pushing the dentry_unhash calls
into each fs where they can be safely removed on a per-fs basis. We
also take this opportunity to finally fix a longstanding bug in
vfs_rename_dir() which rehashes new_dentry (Miklos' patch, see
http://marc.info/?l=linux-fsdevel&m=121666695328987&w=2
http://marc.info/?t=121580579500004&r=1&w=2
).

On a per-fs basis, the dentry_unhash calls can be replaced with
shrink_dcache_parent (or dropped entirely) if it is safe to do so. I
expect that this is safe for most of them.

My hope is that this will move us closer to being able to introduce a
->d_prune d_op. Independent of that, however, I think this is a
worthwhile dcache cleanup.

Thanks!
sage



Miklos Szeredi (1):
vfs: fix vfs_rename_dir for FS_RENAME_DOES_D_MOVE filesystems

Sage Weil (6):
vfs: dentry_unhash immediately prior to rmdir
vfs: remove dget() from dentry_unhash()
vfs: push dentry_unhash on rmdir into file systems
vfs: push dentry_unhash on rename_dir into file systems
vfs: update dentry_unhash() comment
libfs: drop unneeded dentry_unhash

fs/9p/vfs_inode.c | 4 ++++
fs/affs/namei.c | 5 +++++
fs/afs/dir.c | 5 +++++
fs/autofs4/root.c | 2 ++
fs/bfs/dir.c | 3 +++
fs/btrfs/inode.c | 5 +++++
fs/ceph/dir.c | 6 ++++++
fs/cifs/inode.c | 5 +++++
fs/coda/dir.c | 5 +++++
fs/configfs/dir.c | 2 ++
fs/ecryptfs/inode.c | 5 +++++
fs/exofs/namei.c | 5 +++++
fs/ext2/namei.c | 5 +++++
fs/ext3/namei.c | 5 +++++
fs/ext4/namei.c | 5 +++++
fs/fat/namei_msdos.c | 5 +++++
fs/fat/namei_vfat.c | 5 +++++
fs/fuse/dir.c | 6 ++++++
fs/gfs2/ops_inode.c | 5 +++++
fs/hfs/dir.c | 6 ++++++
fs/hfsplus/dir.c | 2 ++
fs/hostfs/hostfs_kern.c | 5 +++++
fs/hpfs/namei.c | 9 ++++++---
fs/jffs2/dir.c | 5 +++++
fs/jfs/namei.c | 5 +++++
fs/libfs.c | 4 ++++
fs/logfs/dir.c | 5 +++++
fs/minix/namei.c | 5 +++++
fs/namei.c | 21 ++++++---------------
fs/ncpfs/dir.c | 5 +++++
fs/nfs/dir.c | 5 +++++
fs/nilfs2/namei.c | 5 +++++
fs/ocfs2/namei.c | 6 ++++++
fs/omfs/dir.c | 5 +++++
fs/reiserfs/namei.c | 5 +++++
fs/reiserfs/xattr.c | 1 -
fs/sysv/namei.c | 5 +++++
fs/ubifs/dir.c | 5 +++++
fs/udf/namei.c | 5 +++++
fs/ufs/namei.c | 5 +++++
40 files changed, 188 insertions(+), 19 deletions(-)


2011-03-30 22:02:05

by Sage Weil

[permalink] [raw]
Subject: [PATCH 1/7] vfs: dentry_unhash immediately prior to rmdir

This presumes that there is no reason to unhash a dentry if we fail because
it is a mountpoint or the LSM check fails, and that the LSM checks do not
depend on the dentry being unhashed.

Signed-off-by: Sage Weil <[email protected]>
---
fs/namei.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index a4689eb..f47a5c0 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2838,24 +2838,24 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
return -EPERM;

mutex_lock(&dentry->d_inode->i_mutex);
- dentry_unhash(dentry);
if (d_mountpoint(dentry))
error = -EBUSY;
else {
error = security_inode_rmdir(dir, dentry);
if (!error) {
+ dentry_unhash(dentry);
error = dir->i_op->rmdir(dir, dentry);
if (!error) {
dentry->d_inode->i_flags |= S_DEAD;
dont_mount(dentry);
}
+ dput(dentry);
}
}
mutex_unlock(&dentry->d_inode->i_mutex);
if (!error) {
d_delete(dentry);
}
- dput(dentry);

return error;
}
--
1.7.0

2011-03-30 22:02:07

by Sage Weil

[permalink] [raw]
Subject: [PATCH 2/7] vfs: remove dget() from dentry_unhash()

This serves no useful purpose that I can discern. All callers (rename,
rmdir) hold their own reference to the dentry.

A quick audit of all file systems showed no relevant checks on the value
of d_count in vfs_rmdir/vfs_rename_dir paths.

Signed-off-by: Sage Weil <[email protected]>
---
fs/hpfs/namei.c | 3 ---
fs/namei.c | 5 +----
fs/reiserfs/xattr.c | 1 -
3 files changed, 1 insertions(+), 8 deletions(-)

diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
index f4ad9e3..00c8218 100644
--- a/fs/hpfs/namei.c
+++ b/fs/hpfs/namei.c
@@ -415,7 +415,6 @@ again:
mutex_unlock(&hpfs_i(inode)->i_parent_mutex);
dentry_unhash(dentry);
if (!d_unhashed(dentry)) {
- dput(dentry);
unlock_kernel();
return -ENOSPC;
}
@@ -423,7 +422,6 @@ again:
!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");*/
@@ -431,7 +429,6 @@ again:
newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
err = notify_change(dentry, &newattrs);
put_write_access(inode);
- dput(dentry);
if (!err)
goto again;
}
diff --git a/fs/namei.c b/fs/namei.c
index f47a5c0..316e89b 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2819,10 +2819,9 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, int, mode)
*/
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);
}
@@ -2849,7 +2848,6 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
dentry->d_inode->i_flags |= S_DEAD;
dont_mount(dentry);
}
- dput(dentry);
}
}
mutex_unlock(&dentry->d_inode->i_mutex);
@@ -3260,7 +3258,6 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
mutex_unlock(&target->i_mutex);
if (d_unhashed(new_dentry))
d_rehash(new_dentry);
- dput(new_dentry);
}
if (!error)
if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c
index 5c11ca8..7451ec4 100644
--- a/fs/reiserfs/xattr.c
+++ b/fs/reiserfs/xattr.c
@@ -105,7 +105,6 @@ static int xattr_rmdir(struct inode *dir, struct dentry *dentry)
mutex_unlock(&dentry->d_inode->i_mutex);
if (!error)
d_delete(dentry);
- dput(dentry);

return error;
}
--
1.7.0

2011-03-30 22:02:52

by Sage Weil

[permalink] [raw]
Subject: [PATCH 5/7] vfs: update dentry_unhash() comment

The helper is now only called by file systems, not the VFS.

Signed-off-by: Sage Weil <[email protected]>
---
fs/namei.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 17de6ab..d376688 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2803,10 +2803,10 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, int, mode)
}

/*
- * 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 1 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
--
1.7.0

2011-03-30 22:02:49

by Sage Weil

[permalink] [raw]
Subject: [PATCH 6/7] libfs: drop unneeded dentry_unhash

There are no libfs issues with dangling references to empty directories.
Replace dentry_unhash() calls with shrink_dcache_parent().

Signed-off-by: Sage Weil <[email protected]>
---
fs/libfs.c | 7 +++----
1 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/fs/libfs.c b/fs/libfs.c
index 91a3710..65aa858 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -311,8 +311,7 @@ int simple_rmdir(struct inode *dir, struct dentry *dentry)
if (!simple_empty(dentry))
return -ENOTEMPTY;

- dentry_unhash(dentry);
-
+ shrink_dcache_parent(dentry);
drop_nlink(dentry->d_inode);
simple_unlink(dir, dentry);
drop_nlink(dir);
@@ -325,8 +324,8 @@ int simple_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *inode = old_dentry->d_inode;
int they_are_dirs = S_ISDIR(old_dentry->d_inode->i_mode);

- if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
- dentry_unhash(new_dentry);
+ if (new_dentry->d_inode && they_are_dirs)
+ shrink_dcache_parent(new_dentry);

if (!simple_empty(new_dentry))
return -ENOTEMPTY;
--
1.7.0

2011-03-30 22:02:47

by Sage Weil

[permalink] [raw]
Subject: [PATCH 7/7] vfs: fix vfs_rename_dir for FS_RENAME_DOES_D_MOVE filesystems

From: Miklos Szeredi <[email protected]>

vfs_rename_dir() doesn't properly account for filesystems with
FS_RENAME_DOES_D_MOVE. If new_dentry has a target inode attached, it
unhashes the new_dentry prior to the rename() iop and rehashes it after,
but doesn't account for the possibility that rename() may have swapped
{old,new}_dentry. For FS_RENAME_DOES_D_MOVE filesystems, it rehashes
new_dentry (now the old renamed-from name, which d_move() expected to go
away), such that a subsequent lookup will find it. Currently all
FS_RENAME_DOES_D_MOVE filesystems compensate for this by failing in
d_revalidate.

The bug was introduced by: commit 349457ccf2592c14bdf13b6706170ae2e94931b1
"[PATCH] Allow file systems to manually d_move() inside of ->rename()"

Fix by not rehashing the new dentry. Rehashing used to be needed by
d_move() but isn't anymore.

Reported-by: Sage Weil <[email protected]>
Signed-off-by: Miklos Szeredi <[email protected]>
---
fs/namei.c | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index d376688..98119a5 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3252,8 +3252,6 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
dont_mount(new_dentry);
}
mutex_unlock(&target->i_mutex);
- if (d_unhashed(new_dentry))
- d_rehash(new_dentry);
}
if (!error)
if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
--
1.7.0

2011-03-30 22:03:35

by Sage Weil

[permalink] [raw]
Subject: [PATCH 3/7] vfs: push dentry_unhash on rmdir into file systems

Only a few file systems need this. Start by pushing it down into each
fs rmdir method so it can be dealt with on a per-fs basis.

This does not change behavior for any in-tree file systems.

Signed-off-by: Sage Weil <[email protected]>
---
fs/9p/vfs_inode.c | 1 +
fs/affs/namei.c | 2 ++
fs/afs/dir.c | 2 ++
fs/autofs4/root.c | 2 ++
fs/btrfs/inode.c | 2 ++
fs/ceph/dir.c | 3 +++
fs/cifs/inode.c | 2 ++
fs/coda/dir.c | 2 ++
fs/configfs/dir.c | 2 ++
fs/ecryptfs/inode.c | 2 ++
fs/exofs/namei.c | 2 ++
fs/ext2/namei.c | 2 ++
fs/ext3/namei.c | 2 ++
fs/ext4/namei.c | 2 ++
fs/fat/namei_msdos.c | 2 ++
fs/fat/namei_vfat.c | 2 ++
fs/fuse/dir.c | 2 ++
fs/gfs2/ops_inode.c | 2 ++
fs/hfs/dir.c | 3 +++
fs/hfsplus/dir.c | 2 ++
fs/hostfs/hostfs_kern.c | 2 ++
fs/hpfs/namei.c | 2 ++
fs/jffs2/dir.c | 2 ++
fs/jfs/namei.c | 2 ++
fs/libfs.c | 2 ++
fs/logfs/dir.c | 2 ++
fs/minix/namei.c | 2 ++
fs/namei.c | 1 -
fs/ncpfs/dir.c | 2 ++
fs/nfs/dir.c | 2 ++
fs/nilfs2/namei.c | 2 ++
fs/ocfs2/namei.c | 3 +++
fs/omfs/dir.c | 2 ++
fs/reiserfs/namei.c | 2 ++
fs/sysv/namei.c | 2 ++
fs/ubifs/dir.c | 2 ++
fs/udf/namei.c | 2 ++
fs/ufs/namei.c | 2 ++
38 files changed, 76 insertions(+), 1 deletions(-)

diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index b76a40b..eb83ad1 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -731,6 +731,7 @@ int v9fs_vfs_unlink(struct inode *i, struct dentry *d)

int v9fs_vfs_rmdir(struct inode *i, struct dentry *d)
{
+ dentry_unhash(d);
return v9fs_remove(i, d, 1);
}

diff --git a/fs/affs/namei.c b/fs/affs/namei.c
index e3e9efc..d087153 100644
--- a/fs/affs/namei.c
+++ b/fs/affs/namei.c
@@ -320,6 +320,8 @@ affs_rmdir(struct inode *dir, struct dentry *dentry)
dentry->d_inode->i_ino,
(int)dentry->d_name.len, dentry->d_name.name);

+ dentry_unhash(dentry);
+
return affs_remove_header(dentry);
}

diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index 20c106f..9a7f414 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -845,6 +845,8 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
_enter("{%x:%u},{%s}",
dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name);

+ dentry_unhash(dentry);
+
ret = -ENAMETOOLONG;
if (dentry->d_name.len >= AFSNAMEMAX)
goto error;
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index 014e7ab..64415f4 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -600,6 +600,8 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry)
if (!autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN))
return -EACCES;

+ dentry_unhash(dentry);
+
if (atomic_dec_and_test(&ino->count)) {
p_ino = autofs4_dentry_ino(dentry->d_parent);
if (p_ino && dentry->d_parent != dentry)
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 9007bbd..bd3527b 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3005,6 +3005,8 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)
return -ENOTEMPTY;

+ dentry_unhash(dentry);
+
trans = __unlink_start_trans(dir, dentry);
if (IS_ERR(trans))
return PTR_ERR(trans);
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index ebafa65..c1d9d6f 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -824,6 +824,9 @@ static int ceph_unlink(struct inode *dir, struct dentry *dentry)
int err = -EROFS;
int op;

+ if ((dentry->d_inode->i_mode & S_IFMT) == S_IFDIR)
+ dentry_unhash(dentry);
+
if (ceph_snap(dir) == CEPH_SNAPDIR) {
/* rmdir .snap/foo is RMSNAP */
dout("rmsnap dir %p '%.*s' dn %p\n", dir, dentry->d_name.len,
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 8852470..cee5896 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -1461,6 +1461,8 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)

cFYI(1, "cifs_rmdir, inode = 0x%p", inode);

+ dentry_unhash(direntry);
+
xid = GetXid();

full_path = build_path_from_dentry(direntry);
diff --git a/fs/coda/dir.c b/fs/coda/dir.c
index 2b8dae4..9f72b75 100644
--- a/fs/coda/dir.c
+++ b/fs/coda/dir.c
@@ -336,6 +336,8 @@ static int coda_rmdir(struct inode *dir, struct dentry *de)
int len = de->d_name.len;
int error;

+ dentry_unhash(de);
+
error = venus_rmdir(dir->i_sb, coda_i2f(dir), name, len);
if (!error) {
/* VFS may delete the child */
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index 90ff3cb..468c77c 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -1355,6 +1355,8 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
struct module *subsys_owner = NULL, *dead_item_owner = NULL;
int ret;

+ dentry_unhash(dentry);
+
if (dentry->d_parent == configfs_sb->s_root)
return -EPERM;

diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index b592938..6f8a00b 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -556,6 +556,8 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry)
struct dentry *lower_dir_dentry;
int rc;

+ dentry_unhash(dentry);
+
lower_dentry = ecryptfs_dentry_to_lower(dentry);
dget(dentry);
lower_dir_dentry = lock_parent(lower_dentry);
diff --git a/fs/exofs/namei.c b/fs/exofs/namei.c
index 4d70db1..0697175 100644
--- a/fs/exofs/namei.c
+++ b/fs/exofs/namei.c
@@ -227,6 +227,8 @@ static int exofs_rmdir(struct inode *dir, struct dentry *dentry)
struct inode *inode = dentry->d_inode;
int err = -ENOTEMPTY;

+ dentry_unhash(dentry);
+
if (exofs_empty_dir(inode)) {
err = exofs_unlink(dir, dentry);
if (!err) {
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index adb9185..23aa4b3 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -296,6 +296,8 @@ static int ext2_rmdir (struct inode * dir, struct dentry *dentry)
struct inode * inode = dentry->d_inode;
int err = -ENOTEMPTY;

+ dentry_unhash(dentry);
+
if (ext2_empty_dir(inode)) {
err = ext2_unlink(dir, dentry);
if (!err) {
diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c
index b27ba71..3256f48 100644
--- a/fs/ext3/namei.c
+++ b/fs/ext3/namei.c
@@ -2074,6 +2074,8 @@ static int ext3_rmdir (struct inode * dir, struct dentry *dentry)
struct ext3_dir_entry_2 * de;
handle_t *handle;

+ dentry_unhash(dentry);
+
/* Initialize quotas before so that eventual writes go in
* separate transaction */
dquot_initialize(dir);
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 5485390..39a93c2 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2122,6 +2122,8 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
struct ext4_dir_entry_2 *de;
handle_t *handle;

+ dentry_unhash(dentry);
+
/* Initialize quotas before so that eventual writes go in
* separate transaction */
dquot_initialize(dir);
diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
index 7114990..0c25cea 100644
--- a/fs/fat/namei_msdos.c
+++ b/fs/fat/namei_msdos.c
@@ -326,6 +326,8 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
struct fat_slot_info sinfo;
int err;

+ dentry_unhash(dentry);
+
lock_super(sb);
/*
* Check whether the directory is not in use, then check
diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
index adae3fb..d7b9383 100644
--- a/fs/fat/namei_vfat.c
+++ b/fs/fat/namei_vfat.c
@@ -824,6 +824,8 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
struct fat_slot_info sinfo;
int err;

+ dentry_unhash(dentry);
+
lock_super(sb);

err = fat_dir_empty(inode);
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 8bd0ef9..4459fda 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -667,6 +667,8 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
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;
diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c
index d8b26ac..6baa30f 100644
--- a/fs/gfs2/ops_inode.c
+++ b/fs/gfs2/ops_inode.c
@@ -572,6 +572,8 @@ static int gfs2_rmdir(struct inode *dir, struct dentry *dentry)
struct gfs2_holder ri_gh;
int error;

+ dentry_unhash(dentry);
+
error = gfs2_rindex_hold(sdp, &ri_gh);
if (error)
return error;
diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c
index b4d70b1..616cfe0 100644
--- a/fs/hfs/dir.c
+++ b/fs/hfs/dir.c
@@ -253,6 +253,9 @@ static int hfs_remove(struct inode *dir, struct dentry *dentry)
struct inode *inode = dentry->d_inode;
int res;

+ if (S_ISDIR(inode->i_mode))
+ dentry_unhash(dentry);
+
if (S_ISDIR(inode->i_mode) && inode->i_size != 2)
return -ENOTEMPTY;
res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name);
diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c
index 4df5059..23451a9 100644
--- a/fs/hfsplus/dir.c
+++ b/fs/hfsplus/dir.c
@@ -370,6 +370,8 @@ static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
struct inode *inode = dentry->d_inode;
int res;

+ dentry_unhash(dentry);
+
if (inode->i_size != 2)
return -ENOTEMPTY;

diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index 2638c83..73ea3ba 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -683,6 +683,8 @@ int hostfs_rmdir(struct inode *ino, struct dentry *dentry)
char *file;
int err;

+ dentry_unhash(dentry);
+
if ((file = dentry_name(dentry)) == NULL)
return -ENOMEM;
err = do_rmdir(file);
diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
index 00c8218..c6674ae 100644
--- a/fs/hpfs/namei.c
+++ b/fs/hpfs/namei.c
@@ -462,6 +462,8 @@ static int hpfs_rmdir(struct inode *dir, struct dentry *dentry)
int err;
int r;

+ dentry_unhash(dentry);
+
hpfs_adjust_length(name, &len);
lock_kernel();
mutex_lock(&hpfs_i(inode)->i_parent_mutex);
diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
index 9297865..4619ee4 100644
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -610,6 +610,8 @@ static int jffs2_rmdir (struct inode *dir_i, struct dentry *dentry)
int ret;
uint32_t now = get_seconds();

+ dentry_unhash(dentry);
+
for (fd = f->dents ; fd; fd = fd->next) {
if (fd->ino)
return -ENOTEMPTY;
diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c
index 5a2b269..32eebe6 100644
--- a/fs/jfs/namei.c
+++ b/fs/jfs/namei.c
@@ -360,6 +360,8 @@ static int jfs_rmdir(struct inode *dip, struct dentry *dentry)

jfs_info("jfs_rmdir: dip:0x%p name:%s", dip, dentry->d_name.name);

+ dentry_unhash(dentry);
+
/* Init inode for quota operations. */
dquot_initialize(dip);
dquot_initialize(ip);
diff --git a/fs/libfs.c b/fs/libfs.c
index c88eab5..1e2ba5a 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -311,6 +311,8 @@ int simple_rmdir(struct inode *dir, struct dentry *dentry)
if (!simple_empty(dentry))
return -ENOTEMPTY;

+ dentry_unhash(dentry);
+
drop_nlink(dentry->d_inode);
simple_unlink(dir, dentry);
drop_nlink(dir);
diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c
index f9ddf0c..9a77518 100644
--- a/fs/logfs/dir.c
+++ b/fs/logfs/dir.c
@@ -273,6 +273,8 @@ static int logfs_rmdir(struct inode *dir, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;

+ dentry_unhash(dentry);
+
if (!logfs_empty_dir(inode))
return -ENOTEMPTY;

diff --git a/fs/minix/namei.c b/fs/minix/namei.c
index 6e6777f..091626f 100644
--- a/fs/minix/namei.c
+++ b/fs/minix/namei.c
@@ -168,6 +168,8 @@ static int minix_rmdir(struct inode * dir, struct dentry *dentry)
struct inode * inode = dentry->d_inode;
int err = -ENOTEMPTY;

+ dentry_unhash(dentry);
+
if (minix_empty_dir(inode)) {
err = minix_unlink(dir, dentry);
if (!err) {
diff --git a/fs/namei.c b/fs/namei.c
index 316e89b..fbbc6d7 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2842,7 +2842,6 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
else {
error = security_inode_rmdir(dir, dentry);
if (!error) {
- dentry_unhash(dentry);
error = dir->i_op->rmdir(dir, dentry);
if (!error) {
dentry->d_inode->i_flags |= S_DEAD;
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c
index f6946bb..57336b7 100644
--- a/fs/ncpfs/dir.c
+++ b/fs/ncpfs/dir.c
@@ -1033,6 +1033,8 @@ static int ncp_rmdir(struct inode *dir, struct dentry *dentry)
DPRINTK("ncp_rmdir: removing %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);

+ dentry_unhash(dentry);
+
error = -EBUSY;
if (!d_unhashed(dentry))
goto out;
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 2c3eb33..78cdbdd 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -1668,6 +1668,8 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
dfprintk(VFS, "NFS: rmdir(%s/%ld), %s\n",
dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);

+ dentry_unhash(dentry);
+
error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
/* Ensure the VFS deletes this inode */
if (error == 0 && dentry->d_inode != NULL)
diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c
index 161791d..87dddb1 100644
--- a/fs/nilfs2/namei.c
+++ b/fs/nilfs2/namei.c
@@ -334,6 +334,8 @@ static int nilfs_rmdir(struct inode *dir, struct dentry *dentry)
struct nilfs_transaction_info ti;
int err;

+ dentry_unhash(dentry);
+
err = nilfs_transaction_begin(dir->i_sb, &ti, 0);
if (err)
return err;
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index 849fb4a..65e4dfd 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -812,6 +812,9 @@ static int ocfs2_unlink(struct inode *dir,
mlog_entry("(0x%p, 0x%p, '%.*s')\n", dir, dentry,
dentry->d_name.len, dentry->d_name.name);

+ if (S_ISDIR(inode->i_mode))
+ dentry_unhash(dentry);
+
dquot_initialize(dir);

BUG_ON(dentry->d_parent->d_inode != dir);
diff --git a/fs/omfs/dir.c b/fs/omfs/dir.c
index 393f3f6..3cc7e89 100644
--- a/fs/omfs/dir.c
+++ b/fs/omfs/dir.c
@@ -256,6 +256,8 @@ static int omfs_rmdir(struct inode *dir, struct dentry *dentry)
int err = -ENOTEMPTY;
struct inode *inode = dentry->d_inode;

+ dentry_unhash(dentry);
+
if (omfs_dir_is_empty(inode)) {
err = omfs_unlink(dir, dentry);
if (!err)
diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
index 68fdf45..c2d2412 100644
--- a/fs/reiserfs/namei.c
+++ b/fs/reiserfs/namei.c
@@ -831,6 +831,8 @@ static int reiserfs_rmdir(struct inode *dir, struct dentry *dentry)
INITIALIZE_PATH(path);
struct reiserfs_dir_entry de;

+ dentry_unhash(dentry);
+
/* we will be doing 2 balancings and update 2 stat data, we change quotas
* of the owner of the directory and of the owner of the parent directory.
* The quota structure is possibly deleted only on last iput => outside
diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c
index e474fbc..fac64ac 100644
--- a/fs/sysv/namei.c
+++ b/fs/sysv/namei.c
@@ -196,6 +196,8 @@ static int sysv_rmdir(struct inode * dir, struct dentry * dentry)
struct inode *inode = dentry->d_inode;
int err = -ENOTEMPTY;

+ dentry_unhash(dentry);
+
if (sysv_empty_dir(inode)) {
err = sysv_unlink(dir, dentry);
if (!err) {
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index 14f64b6..c2d0756 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -674,6 +674,8 @@ static int ubifs_rmdir(struct inode *dir, struct dentry *dentry)
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
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
index b7c338d..45d1c99 100644
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -783,6 +783,8 @@ static int udf_rmdir(struct inode *dir, struct dentry *dentry)
struct fileIdentDesc *fi, cfi;
struct kernel_lb_addr tloc;

+ dentry_unhash(dentry);
+
retval = -ENOENT;
fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi);
if (!fi)
diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c
index d6f6815..a430d7b 100644
--- a/fs/ufs/namei.c
+++ b/fs/ufs/namei.c
@@ -259,6 +259,8 @@ static int ufs_rmdir (struct inode * dir, struct dentry *dentry)
struct inode * inode = dentry->d_inode;
int err= -ENOTEMPTY;

+ dentry_unhash(dentry);
+
lock_kernel();
if (ufs_empty_dir (inode)) {
err = ufs_unlink(dir, dentry);
--
1.7.0

2011-03-30 22:03:34

by Sage Weil

[permalink] [raw]
Subject: [PATCH 4/7] vfs: push dentry_unhash on rename_dir into file systems

Only a few file systems need this. Start by pushing it down into each
rename method so that it can be dealt with on a per-fs basis.

Signed-off-by: Sage Weil <[email protected]>
---
fs/9p/vfs_inode.c | 3 +++
fs/affs/namei.c | 3 +++
fs/afs/dir.c | 3 +++
fs/bfs/dir.c | 3 +++
fs/btrfs/inode.c | 3 +++
fs/ceph/dir.c | 3 +++
fs/cifs/inode.c | 3 +++
fs/coda/dir.c | 3 +++
fs/ecryptfs/inode.c | 3 +++
fs/exofs/namei.c | 3 +++
fs/ext2/namei.c | 3 +++
fs/ext3/namei.c | 3 +++
fs/ext4/namei.c | 3 +++
fs/fat/namei_msdos.c | 3 +++
fs/fat/namei_vfat.c | 3 +++
fs/fuse/dir.c | 4 ++++
fs/gfs2/ops_inode.c | 3 +++
fs/hfs/dir.c | 3 +++
fs/hostfs/hostfs_kern.c | 3 +++
fs/hpfs/namei.c | 4 ++++
fs/jffs2/dir.c | 3 +++
fs/jfs/namei.c | 3 +++
fs/libfs.c | 3 +++
fs/logfs/dir.c | 3 +++
fs/minix/namei.c | 3 +++
fs/namei.c | 5 +----
fs/ncpfs/dir.c | 3 +++
fs/nfs/dir.c | 3 +++
fs/nilfs2/namei.c | 3 +++
fs/ocfs2/namei.c | 3 +++
fs/omfs/dir.c | 3 +++
fs/reiserfs/namei.c | 3 +++
fs/sysv/namei.c | 3 +++
fs/ubifs/dir.c | 3 +++
fs/udf/namei.c | 3 +++
fs/ufs/namei.c | 3 +++
36 files changed, 108 insertions(+), 4 deletions(-)

diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index eb83ad1..7ce3619 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -756,6 +756,9 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct p9_wstat wstat;
int retval;

+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
P9_DPRINTK(P9_DEBUG_VFS, "\n");
retval = 0;
old_inode = old_dentry->d_inode;
diff --git a/fs/affs/namei.c b/fs/affs/namei.c
index d087153..03330e2 100644
--- a/fs/affs/namei.c
+++ b/fs/affs/namei.c
@@ -419,6 +419,9 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct buffer_head *bh = NULL;
int retval;

+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n",
(u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name,
(u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name);
diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index 9a7f414..2c4e051 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -1148,6 +1148,9 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct key *key;
int ret;

+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
vnode = AFS_FS_I(old_dentry->d_inode);
orig_dvnode = AFS_FS_I(old_dir);
new_dvnode = AFS_FS_I(new_dir);
diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c
index 685ecff..fb71de1 100644
--- a/fs/bfs/dir.c
+++ b/fs/bfs/dir.c
@@ -224,6 +224,9 @@ static int bfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct bfs_sb_info *info;
int error = -ENOENT;

+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
old_bh = new_bh = NULL;
old_inode = old_dentry->d_inode;
if (S_ISDIR(old_inode->i_mode))
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index bd3527b..9b48003 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -6823,6 +6823,9 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
u64 root_objectid;
int ret;

+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
if (new_dir->i_ino == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)
return -EPERM;

diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index c1d9d6f..06524c7 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -866,6 +866,9 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry,
struct ceph_mds_request *req;
int err;

+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
if (ceph_snap(old_dir) != ceph_snap(new_dir))
return -EXDEV;
if (ceph_snap(old_dir) != CEPH_NOSNAP ||
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index cee5896..18546b7 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -1571,6 +1571,9 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
FILE_UNIX_BASIC_INFO *info_buf_target;
int xid, rc, tmprc;

+ if (target_dentry->d_inode && S_ISDIR(target_dentry->d_inode->i_mode))
+ dentry_unhash(target_dentry);
+
cifs_sb = CIFS_SB(source_dir->i_sb);
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
diff --git a/fs/coda/dir.c b/fs/coda/dir.c
index 9f72b75..a46126f 100644
--- a/fs/coda/dir.c
+++ b/fs/coda/dir.c
@@ -361,6 +361,9 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry,
int new_length = new_dentry->d_name.len;
int error;

+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
error = venus_rename(old_dir->i_sb, coda_i2f(old_dir),
coda_i2f(new_dir), old_length, new_length,
(const char *) old_name, (const char *)new_name);
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 6f8a00b..ea221bf 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -610,6 +610,9 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct dentry *lower_new_dir_dentry;
struct dentry *trap = NULL;

+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry);
lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry);
dget(lower_old_dentry);
diff --git a/fs/exofs/namei.c b/fs/exofs/namei.c
index 0697175..de252e5 100644
--- a/fs/exofs/namei.c
+++ b/fs/exofs/namei.c
@@ -251,6 +251,9 @@ static int exofs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct exofs_dir_entry *old_de;
int err = -ENOENT;

+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
old_de = exofs_find_entry(old_dir, old_dentry, &old_page);
if (!old_de)
goto out;
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index 23aa4b3..c2b209b 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -320,6 +320,9 @@ static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry,
struct ext2_dir_entry_2 * old_de;
int err = -ENOENT;

+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
dquot_initialize(old_dir);
dquot_initialize(new_dir);

diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c
index 3256f48..1b3f814 100644
--- a/fs/ext3/namei.c
+++ b/fs/ext3/namei.c
@@ -2305,6 +2305,9 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,
struct ext3_dir_entry_2 * old_de, * new_de;
int retval, flush_file = 0;

+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
dquot_initialize(old_dir);
dquot_initialize(new_dir);

diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 39a93c2..90cf091 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2356,6 +2356,9 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
struct ext4_dir_entry_2 *old_de, *new_de;
int retval, force_da_alloc = 0;

+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
dquot_initialize(old_dir);
dquot_initialize(new_dir);

diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
index 0c25cea..c3eccbd 100644
--- a/fs/fat/namei_msdos.c
+++ b/fs/fat/namei_msdos.c
@@ -459,6 +459,9 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
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;
diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
index d7b9383..e2466b2 100644
--- a/fs/fat/namei_vfat.c
+++ b/fs/fat/namei_vfat.c
@@ -933,6 +933,9 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
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;
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 4459fda..8e20db1 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -693,6 +693,10 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
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 --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c
index 6baa30f..3fd5a31 100644
--- a/fs/gfs2/ops_inode.c
+++ b/fs/gfs2/ops_inode.c
@@ -743,6 +743,9 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
int error;

if (ndentry->d_inode) {
+ if (S_ISDIR(ndentry->d_inode->i_mode))
+ dentry_unhash(ndentry);
+
nip = GFS2_I(ndentry->d_inode);
if (ip == nip)
return 0;
diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c
index 616cfe0..1cb70cd 100644
--- a/fs/hfs/dir.c
+++ b/fs/hfs/dir.c
@@ -286,6 +286,9 @@ static int hfs_rename(struct inode *old_dir, struct dentry *old_dentry,

/* Unlink destination if it already exists */
if (new_dentry->d_inode) {
+ if (S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
res = hfs_remove(new_dir, new_dentry);
if (res)
return res;
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index 73ea3ba..e6816b9 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -738,6 +738,9 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from,
char *from_name, *to_name;
int err;

+ if (to->d_inode && S_ISDIR(to->d_inode->i_mode))
+ dentry_unhash(to);
+
if ((from_name = dentry_name(from)) == NULL)
return -ENOMEM;
if ((to_name = dentry_name(to)) == NULL) {
diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
index c6674ae..04713f7 100644
--- a/fs/hpfs/namei.c
+++ b/fs/hpfs/namei.c
@@ -562,6 +562,10 @@ static int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
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);
diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
index 4619ee4..80879d1 100644
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -787,6 +787,9 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
uint8_t type;
uint32_t now;

+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
/* The VFS will check for us and prevent trying to rename a
* file over a directory and vice versa, but if it's a directory,
* the VFS can't check whether the victim is empty. The filesystem
diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c
index 32eebe6..d213b62 100644
--- a/fs/jfs/namei.c
+++ b/fs/jfs/namei.c
@@ -1100,6 +1100,9 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
jfs_info("jfs_rename: %s %s", old_dentry->d_name.name,
new_dentry->d_name.name);

+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
dquot_initialize(old_dir);
dquot_initialize(new_dir);

diff --git a/fs/libfs.c b/fs/libfs.c
index 1e2ba5a..91a3710 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -325,6 +325,9 @@ int simple_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *inode = old_dentry->d_inode;
int they_are_dirs = S_ISDIR(old_dentry->d_inode->i_mode);

+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
if (!simple_empty(new_dentry))
return -ENOTEMPTY;

diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c
index 9a77518..7409059 100644
--- a/fs/logfs/dir.c
+++ b/fs/logfs/dir.c
@@ -624,6 +624,9 @@ static int logfs_rename_cross(struct inode *old_dir, struct dentry *old_dentry,
loff_t pos;
int err;

+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
/* 1. locate source dd */
err = logfs_get_dd(old_dir, old_dentry, &dd, &pos);
if (err)
diff --git a/fs/minix/namei.c b/fs/minix/namei.c
index 091626f..f60aed8 100644
--- a/fs/minix/namei.c
+++ b/fs/minix/namei.c
@@ -192,6 +192,9 @@ static int minix_rename(struct inode * old_dir, struct dentry *old_dentry,
struct minix_dir_entry * old_de;
int err = -ENOENT;

+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
old_de = minix_find_entry(old_dentry, &old_page);
if (!old_de)
goto out;
diff --git a/fs/namei.c b/fs/namei.c
index fbbc6d7..17de6ab 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3244,11 +3244,8 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
mutex_lock(&target->i_mutex);
if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
error = -EBUSY;
- else {
- if (target)
- dentry_unhash(new_dentry);
+ else
error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
- }
if (target) {
if (!error) {
target->i_flags |= S_DEAD;
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c
index 57336b7..e3e646b 100644
--- a/fs/ncpfs/dir.c
+++ b/fs/ncpfs/dir.c
@@ -1141,6 +1141,9 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
new_dentry->d_parent->d_name.name, new_dentry->d_name.name);

+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
ncp_age_dentry(server, old_dentry);
ncp_age_dentry(server, new_dentry);

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 78cdbdd..2520def 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -1879,6 +1879,9 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
new_dentry->d_count);

+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
/*
* For non-directories, check whether the target is busy and if so,
* make a copy of the dentry and then do a silly-rename. If the
diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c
index 87dddb1..b0ec307 100644
--- a/fs/nilfs2/namei.c
+++ b/fs/nilfs2/namei.c
@@ -371,6 +371,9 @@ static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct nilfs_transaction_info ti;
int err;

+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
err = nilfs_transaction_begin(old_dir->i_sb, &ti, 1);
if (unlikely(err))
return err;
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index 65e4dfd..68dccef 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -1067,6 +1067,9 @@ static int ocfs2_rename(struct inode *old_dir,
struct ocfs2_dir_lookup_result orphan_insert = { NULL, };
struct ocfs2_dir_lookup_result target_insert = { NULL, };

+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
/* At some point it might be nice to break this function up a
* bit. */

diff --git a/fs/omfs/dir.c b/fs/omfs/dir.c
index 3cc7e89..08e7c60 100644
--- a/fs/omfs/dir.c
+++ b/fs/omfs/dir.c
@@ -394,6 +394,9 @@ static int omfs_rename(struct inode *old_dir, struct dentry *old_dentry,
is_dir = S_ISDIR(old_inode->i_mode);

if (new_inode) {
+ if (S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
/* overwriting existing file/dir */
err = -ENOTEMPTY;
if (is_dir && !omfs_dir_is_empty(new_inode))
diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
index c2d2412..fee5284 100644
--- a/fs/reiserfs/namei.c
+++ b/fs/reiserfs/namei.c
@@ -1230,6 +1230,9 @@ static int reiserfs_rename(struct inode *old_dir, struct dentry *old_dentry,
unsigned long savelink = 1;
struct timespec ctime;

+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
/* three balancings: (1) old name removal, (2) new name insertion
and (3) maybe "save" link insertion
stat data updates: (1) old directory,
diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c
index fac64ac..e2cc675 100644
--- a/fs/sysv/namei.c
+++ b/fs/sysv/namei.c
@@ -224,6 +224,9 @@ static int sysv_rename(struct inode * old_dir, struct dentry * old_dentry,
struct sysv_dir_entry * old_de;
int err = -ENOENT;

+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
old_de = sysv_find_entry(old_dentry, &old_page);
if (!old_de)
goto out;
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index c2d0756..d5dfd6b 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -996,6 +996,9 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
.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.
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
index 45d1c99..f9729a2 100644
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -1083,6 +1083,9 @@ static int udf_rename(struct inode *old_dir, struct dentry *old_dentry,
struct kernel_lb_addr tloc;
struct udf_inode_info *old_iinfo = UDF_I(old_inode);

+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi);
if (ofi) {
if (ofibh.sbh != ofibh.ebh)
diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c
index a430d7b..85a2204 100644
--- a/fs/ufs/namei.c
+++ b/fs/ufs/namei.c
@@ -285,6 +285,9 @@ static int ufs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct ufs_dir_entry *old_de;
int err = -ENOENT;

+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
old_de = ufs_find_entry(old_dir, &old_dentry->d_name, &old_page);
if (!old_de)
goto out;
--
1.7.0

2011-03-31 03:09:32

by Dave Chinner

[permalink] [raw]
Subject: Re: [PATCH 4/7] vfs: push dentry_unhash on rename_dir into file systems

On Wed, Mar 30, 2011 at 03:09:25PM -0700, Sage Weil wrote:
> Only a few file systems need this. Start by pushing it down into each
> rename method so that it can be dealt with on a per-fs basis.

What is the criteria for a filesystem needing this?

> Signed-off-by: Sage Weil <[email protected]>
> ---
> fs/9p/vfs_inode.c | 3 +++
> fs/affs/namei.c | 3 +++
> fs/afs/dir.c | 3 +++
> fs/bfs/dir.c | 3 +++
> fs/btrfs/inode.c | 3 +++
> fs/ceph/dir.c | 3 +++
> fs/cifs/inode.c | 3 +++
> fs/coda/dir.c | 3 +++
> fs/ecryptfs/inode.c | 3 +++
> fs/exofs/namei.c | 3 +++
> fs/ext2/namei.c | 3 +++
> fs/ext3/namei.c | 3 +++
> fs/ext4/namei.c | 3 +++
> fs/fat/namei_msdos.c | 3 +++
> fs/fat/namei_vfat.c | 3 +++
> fs/fuse/dir.c | 4 ++++
> fs/gfs2/ops_inode.c | 3 +++
> fs/hfs/dir.c | 3 +++
> fs/hostfs/hostfs_kern.c | 3 +++
> fs/hpfs/namei.c | 4 ++++
> fs/jffs2/dir.c | 3 +++
> fs/jfs/namei.c | 3 +++
> fs/libfs.c | 3 +++
> fs/logfs/dir.c | 3 +++
> fs/minix/namei.c | 3 +++
> fs/namei.c | 5 +----
> fs/ncpfs/dir.c | 3 +++
> fs/nfs/dir.c | 3 +++
> fs/nilfs2/namei.c | 3 +++
> fs/ocfs2/namei.c | 3 +++
> fs/omfs/dir.c | 3 +++
> fs/reiserfs/namei.c | 3 +++
> fs/sysv/namei.c | 3 +++
> fs/ubifs/dir.c | 3 +++
> fs/udf/namei.c | 3 +++
> fs/ufs/namei.c | 3 +++
> 36 files changed, 108 insertions(+), 4 deletions(-)

You didn't push it down into XFS, hence my question....

Cheers,

Dave.
--
Dave Chinner
[email protected]

2011-03-31 04:37:17

by Sage Weil

[permalink] [raw]
Subject: Re: [PATCH 4/7] vfs: push dentry_unhash on rename_dir into file systems

On Thu, 31 Mar 2011, Dave Chinner wrote:
> On Wed, Mar 30, 2011 at 03:09:25PM -0700, Sage Weil wrote:
> > Only a few file systems need this. Start by pushing it down into each
> > rename method so that it can be dealt with on a per-fs basis.
>
> What is the criteria for a filesystem needing this?

As I understand it, it's normally just a question of whether the FS can
handle references to the directory's struct inode and dentry after they've
been unlinked. A few file systems bail out of rmdir/rename if
!d_unhashed(dentry) (dentry_unhash() doesn't unhash if there are any other
dentry references).

All but a handful of file systems should be able to drop the dentry_unhash
entirely.

> > Signed-off-by: Sage Weil <[email protected]>
> > ---
> > fs/9p/vfs_inode.c | 3 +++
> > fs/affs/namei.c | 3 +++
> > fs/afs/dir.c | 3 +++
> > fs/bfs/dir.c | 3 +++
> > fs/btrfs/inode.c | 3 +++
> > fs/ceph/dir.c | 3 +++
> > fs/cifs/inode.c | 3 +++
> > fs/coda/dir.c | 3 +++
> > fs/ecryptfs/inode.c | 3 +++
> > fs/exofs/namei.c | 3 +++
> > fs/ext2/namei.c | 3 +++
> > fs/ext3/namei.c | 3 +++
> > fs/ext4/namei.c | 3 +++
> > fs/fat/namei_msdos.c | 3 +++
> > fs/fat/namei_vfat.c | 3 +++
> > fs/fuse/dir.c | 4 ++++
> > fs/gfs2/ops_inode.c | 3 +++
> > fs/hfs/dir.c | 3 +++
> > fs/hostfs/hostfs_kern.c | 3 +++
> > fs/hpfs/namei.c | 4 ++++
> > fs/jffs2/dir.c | 3 +++
> > fs/jfs/namei.c | 3 +++
> > fs/libfs.c | 3 +++
> > fs/logfs/dir.c | 3 +++
> > fs/minix/namei.c | 3 +++
> > fs/namei.c | 5 +----
> > fs/ncpfs/dir.c | 3 +++
> > fs/nfs/dir.c | 3 +++
> > fs/nilfs2/namei.c | 3 +++
> > fs/ocfs2/namei.c | 3 +++
> > fs/omfs/dir.c | 3 +++
> > fs/reiserfs/namei.c | 3 +++
> > fs/sysv/namei.c | 3 +++
> > fs/ubifs/dir.c | 3 +++
> > fs/udf/namei.c | 3 +++
> > fs/ufs/namei.c | 3 +++
> > 36 files changed, 108 insertions(+), 4 deletions(-)
>
> You didn't push it down into XFS, hence my question....

Whoops... I was grepping for .rmdir and .rename in fs/*.c. I'll fix it
up.

sage