lkml.org 
[lkml]   [1998]   [Sep]   [5]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subjectpatch for NFS rename -- please test!
I've put together a patch to allow NFS to rename over a busy target, as
a number of applications apparently need to do this.

The approach is straightforward: for the case of a target file with
d_count > 1, make a copy of the target dentry and attempt to
silly-rename the target. If the silly-rename succeeds, hash the copied
dentry as the replacement target.

As both parent directories are locked, the success of the silly-rename
is equivalent to a negative lookup on the original target name. Thus we
have essentially the same initial conditions but now with a negative
target, so it's safe to proceed with the rename.

I've tested the changes under knfsd and unfsd with some torture scripts
and it appears to be solid, but I'd appreciate it if the people who
reported application problems could retest with the patch in place.

Regards,
Bill--- linux-2.1.120/fs/nfs/dir.c.old Sat Sep 5 10:52:50 1998
+++ linux-2.1.120/fs/nfs/dir.c Sat Sep 5 15:49:58 1998
@@ -446,14 +482,14 @@
*/
static void nfs_dentry_delete(struct dentry *dentry)
{
+ dfprintk(VFS, "NFS: dentry_delete(%s/%s, %x)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ dentry->d_flags);
+
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
int error;

dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
-#ifdef NFS_DEBUG_VERBOSE
-printk("nfs_dentry_delete: unlinking %s/%s\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
/* Unhash it first */
d_drop(dentry);
error = nfs_safe_remove(dentry);
@@ -486,6 +522,10 @@
*/
static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
{
+ dfprintk(VFS, "NFS: dentry_iput(%s/%s, cnt=%d, ino=%ld)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ dentry->d_count, inode->i_ino);
+
if (NFS_WRITEBACK(inode)) {
#ifdef NFS_PARANOIA
printk("nfs_dentry_iput: pending writes for %s/%s, i_count=%d\n",
@@ -859,6 +899,10 @@
struct dentry *sdentry;
int error = -EIO;

+ dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ dentry->d_count);
+
/*
* Note that a silly-renamed file can be deleted once it's
* no longer in use -- it's just an ordinary file now.
@@ -931,6 +975,10 @@
struct inode *inode = dentry->d_inode;
int error, rehash = 0;

+ dfprintk(VFS, "NFS: safe_remove(%s/%s, %ld)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ inode->i_ino);
+
/* N.B. not needed now that d_delete is done in advance? */
error = -EBUSY;
if (inode) {
@@ -1130,22 +1178,13 @@
{
struct inode *old_inode = old_dentry->d_inode;
struct inode *new_inode = new_dentry->d_inode;
+ struct dentry *dentry = NULL;
int error, rehash = 0, update = 1;

-#ifdef NFS_DEBUG_VERBOSE
-printk("nfs_rename: old %s/%s, count=%d, new %s/%s, count=%d\n",
-old_dentry->d_parent->d_name.name,old_dentry->d_name.name,old_dentry->d_count,
-new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
-#endif
- if (!old_dir || !S_ISDIR(old_dir->i_mode)) {
- printk("nfs_rename: old inode is NULL or not a directory\n");
- return -ENOENT;
- }
-
- if (!new_dir || !S_ISDIR(new_dir->i_mode)) {
- printk("nfs_rename: new inode is NULL or not a directory\n");
- return -ENOENT;
- }
+ dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n",
+ old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+ new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+ new_dentry->d_count);

error = -ENAMETOOLONG;
if (old_dentry->d_name.len > NFS_MAXNAMLEN ||
@@ -1155,16 +1194,43 @@
/*
* First check whether the target is busy ... we can't
* safely do _any_ rename if the target is in use.
+ *
+ * For files, make a copy of the dentry and then do a
+ * silly-rename. If the silly-rename succeeds, the
+ * copied dentry is hashed and becomes the new target.
+ *
+ * For directories, prune any unused children.
*/
- if (new_dentry->d_count > 1 && !list_empty(&new_dentry->d_subdirs))
- shrink_dcache_parent(new_dentry);
error = -EBUSY;
- if (new_dentry->d_count > 1) {
+ if (new_dentry->d_count > 1 && new_inode) {
+ if (S_ISREG(new_inode->i_mode)) {
+ int err;
+ /* copy the target dentry's name */
+ dentry = d_alloc(new_dentry->d_parent,
+ &new_dentry->d_name);
+ if (!dentry)
+ goto out;
+
+ /* silly-rename the existing target ... */
+ err = nfs_sillyrename(new_dir, new_dentry);
+ if (!err) {
+ new_dentry = dentry;
+ new_inode = NULL;
+ /* hash the replacement target */
+ d_add(new_dentry, NULL);
+ }
+ } else if (!list_empty(&new_dentry->d_subdirs)) {
+ shrink_dcache_parent(new_dentry);
+ }
+
+ /* dentry still busy? */
+ if (new_dentry->d_count > 1) {
#ifdef NFS_PARANOIA
printk("nfs_rename: target %s/%s busy, d_count=%d\n",
new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
#endif
- goto out;
+ goto out;
+ }
}

/*
@@ -1208,7 +1274,7 @@
#endif
goto out;
}
- if (new_dentry->d_count > 1) {
+ if (new_dentry->d_count > 1 && new_inode) {
#ifdef NFS_PARANOIA
printk("nfs_rename: new dentry %s/%s busy, d_count=%d\n",
new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
@@ -1251,7 +1317,11 @@
if (update)
d_move(old_dentry, new_dentry);
}
+
out:
+ /* new dentry created? */
+ if (dentry)
+ dput(dentry);
return error;
}
\
 
 \ /
  Last update: 2005-03-22 13:44    [W:0.028 / U:0.020 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site