@@ -21,6 +21,7 @@ prototypes:
char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);
struct vfsmount *(*d_automount)(struct path *path);
int (*d_manage)(struct dentry *, bool);
+ void (*d_unlink)(struct dentry *, struct dentry *);
locking rules:
rename_lock ->d_lock may block rcu-walk
@@ -33,6 +34,7 @@ d_iput: no no yes no
d_dname: no no no no
d_automount: no no yes no
d_manage: no no yes (ref-walk) maybe
+d_unlink no no yes no
--------------------------- inode_operations ---------------------------
prototypes:
@@ -866,6 +866,7 @@ struct dentry_operations {
char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool, bool);
+ void (*d_unlink)(struct dentry *, struct dentry *);
};
d_revalidate: called when the VFS needs to revalidate a dentry. This
@@ -973,6 +974,14 @@ struct dentry_operations {
This function is only used if DCACHE_MANAGE_TRANSIT is set on the
dentry being transited from.
+ d_unlink: called to allow the filesystem to unlink the dentry after final
+ use. It is only called when DCACHE_NFSFS_RENAMED is set, and is
+ designed for use by 'sillyrename' schemes that are commonly
+ implemented on distributed filesystems such as NFS.
+
+ Note that the filesystem is still responsible for protecting against
+ races with other lookups.
+
Example :
static char *pipefs_dname(struct dentry *dent, char *buffer, int buflen)
@@ -301,6 +301,9 @@ static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent)
if (parent)
spin_unlock(&parent->d_lock);
dentry_iput(dentry);
+
+ if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
+ dentry->d_op->d_unlink(parent, dentry);
/*
* dentry_iput drops the locks, at which point nobody (except
* transient RCU lookups) can reach this dentry.
@@ -1161,19 +1161,22 @@ static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
if (S_ISDIR(inode->i_mode))
/* drop any readdir cache as it could easily be old */
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA;
-
- if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
+ if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
drop_nlink(inode);
- nfs_complete_unlink(dentry, inode);
- }
iput(inode);
}
+static void nfs_d_unlink(struct dentry *parent, struct dentry *dentry)
+{
+ nfs_complete_unlink(parent, dentry);
+}
+
const struct dentry_operations nfs_dentry_operations = {
.d_revalidate = nfs_lookup_revalidate,
.d_delete = nfs_dentry_delete,
.d_iput = nfs_dentry_iput,
.d_automount = nfs_d_automount,
+ .d_unlink = nfs_d_unlink,
};
static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
@@ -1248,6 +1251,7 @@ const struct dentry_operations nfs4_dentry_operations = {
.d_delete = nfs_dentry_delete,
.d_iput = nfs_dentry_iput,
.d_automount = nfs_d_automount,
+ .d_unlink = nfs_d_unlink,
};
/*
@@ -184,19 +184,17 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n
return 1;
}
-static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data)
+static int nfs_call_unlink(struct dentry *parent, struct dentry *dentry, struct nfs_unlinkdata *data)
{
- struct dentry *parent;
struct inode *dir;
int ret = 0;
- parent = dget_parent(dentry);
if (parent == NULL)
- goto out_free;
+ goto out;
dir = parent->d_inode;
if (nfs_copy_dname(dentry, data) != 0)
- goto out_dput;
+ goto out;
/* Non-exclusive lock protects against concurrent lookup() calls */
spin_lock(&dir->i_lock);
if (atomic_inc_not_zero(&NFS_I(dir)->silly_count) == 0) {
@@ -204,13 +202,11 @@ static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data)
hlist_add_head(&data->list, &NFS_I(dir)->silly_list);
spin_unlock(&dir->i_lock);
ret = 1;
- goto out_dput;
+ goto out;
}
spin_unlock(&dir->i_lock);
ret = nfs_do_call_unlink(parent, dir, data);
-out_dput:
- dput(parent);
-out_free:
+out:
return ret;
}
@@ -283,26 +279,24 @@ out:
/**
* nfs_complete_unlink - Initialize completion of the sillydelete
+ * @parent: parent directory
* @dentry: dentry to delete
- * @inode: inode
*
* Since we're most likely to be called by dentry_iput(), we
* only use the dentry to find the sillydelete. We then copy the name
* into the qstr.
*/
void
-nfs_complete_unlink(struct dentry *dentry, struct inode *inode)
+nfs_complete_unlink(struct dentry *parent, struct dentry *dentry)
{
struct nfs_unlinkdata *data = NULL;
- spin_lock(&dentry->d_lock);
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
data = dentry->d_fsdata;
}
- spin_unlock(&dentry->d_lock);
- if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data)))
+ if (data != NULL && !nfs_call_unlink(parent, dentry, data))
nfs_free_unlinkdata(data);
}
@@ -169,6 +169,7 @@ struct dentry_operations {
char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool, bool);
+ void (*d_unlink)(struct dentry *, struct dentry *);
} ____cacheline_aligned;
/*
@@ -488,7 +488,7 @@ extern void nfs_release_automount_timer(void);
/*
* linux/fs/nfs/unlink.c
*/
-extern void nfs_complete_unlink(struct dentry *dentry, struct inode *);
+extern void nfs_complete_unlink(struct dentry *dentry, struct dentry *);
extern void nfs_block_sillyrename(struct dentry *dentry);
extern void nfs_unblock_sillyrename(struct dentry *dentry);
extern int nfs_sillyrename(struct inode *dir, struct dentry *dentry);