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 (except gfs2 and xfs) so it can be dealt with on a per-fs
basis.
This does not change behavior for any in-tree file systems.
Acked-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Sage Weil <sage@newdream.net>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index 7f6c677..ecd7717 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -814,6 +814,7 @@
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 @@
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 @@
_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 f55ae23..87d95a8 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -583,6 +583,8 @@
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 7cd8ab0..c692dad 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3062,6 +3062,8 @@
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 1a867a3..d2e5490 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -827,6 +827,9 @@
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 @@
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 @@
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 3313dd1..9908c20 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -1355,6 +1355,8 @@
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 4d4cc6a..c88612f 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -521,6 +521,8 @@
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 @@
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 ed5c5d4..7a5ad97 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -296,6 +296,8 @@
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 32f3b86..552f94d 100644
--- a/fs/ext3/namei.c
+++ b/fs/ext3/namei.c
@@ -2074,6 +2074,8 @@
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 67fd0b0..957580d 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2123,6 +2123,8 @@
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 @@
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 @@
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 c6ba49b..40d5c2a 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -667,6 +667,8 @@
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/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 @@
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 @@
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 2638c834e..73ea3ba 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -683,6 +683,8 @@
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 b1c72a9..b9fe158 100644
--- a/fs/hpfs/namei.c
+++ b/fs/hpfs/namei.c
@@ -461,6 +461,8 @@
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);
diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
index 82faddd..727d64439 100644
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -609,6 +609,8 @@
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 eaaf2b5..0569dac 100644
--- a/fs/jfs/namei.c
+++ b/fs/jfs/namei.c
@@ -360,6 +360,8 @@
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 @@
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 9ed89d1..2b32734 100644
--- a/fs/logfs/dir.c
+++ b/fs/logfs/dir.c
@@ -273,6 +273,8 @@
{
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 @@
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 8d11187..596edb5 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2568,7 +2568,6 @@
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 @@
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 7237672..48483b5 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -1748,6 +1748,8 @@
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 546849b..78306e6 100644
--- a/fs/nilfs2/namei.c
+++ b/fs/nilfs2/namei.c
@@ -334,6 +334,8 @@
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 e5d738c..b017ebb 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -810,6 +810,9 @@
(unsigned long long)OCFS2_I(dir)->ip_blkno,
(unsigned long long)OCFS2_I(inode)->ip_blkno);
+ 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 de4ff29..95ef443 100644
--- a/fs/omfs/dir.c
+++ b/fs/omfs/dir.c
@@ -240,8 +240,12 @@
struct inode *inode = dentry->d_inode;
int ret;
- if (S_ISDIR(inode->i_mode) && !omfs_dir_is_empty(inode))
- return -ENOTEMPTY;
+
+ if (S_ISDIR(inode->i_mode)) {
+ dentry_unhash(dentry);
+ if (!omfs_dir_is_empty(inode))
+ return -ENOTEMPTY;
+ }
ret = omfs_delete_entry(dentry);
if (ret)
diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
index 1186626..43e94f0 100644
--- a/fs/reiserfs/namei.c
+++ b/fs/reiserfs/namei.c
@@ -831,6 +831,8 @@
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 @@
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 7217d67..6ca9176 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -656,6 +656,8 @@
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 f1dce84..b70f026 100644
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -783,6 +783,8 @@
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 29309e2..3a769d5 100644
--- a/fs/ufs/namei.c
+++ b/fs/ufs/namei.c
@@ -258,6 +258,8 @@
struct inode * inode = dentry->d_inode;
int err= -ENOTEMPTY;
+ dentry_unhash(dentry);
+
lock_ufs(dir->i_sb);
if (ufs_empty_dir (inode)) {
err = ufs_unlink(dir, dentry);