patch-2.1.52 linux/fs/nfs/dir.c
Next file: linux/fs/nfs/inode.c
Previous file: linux/fs/namei.c
Back to the patch index
Back to the overall index
- Lines: 292
- Date:
Sun Aug 24 11:10:29 1997
- Orig file:
v2.1.51/linux/fs/nfs/dir.c
- Orig date:
Mon Aug 18 18:19:46 1997
diff -u --recursive --new-file v2.1.51/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
@@ -7,6 +7,13 @@
*
* 10 Apr 1996 Added silly rename for unlink --okir
* 28 Sep 1996 Improved directory cache --okir
+ * 23 Aug 1997 Claus Heine claus@momo.math.rwth-aachen.de
+ * Re-implemented silly rename for unlink, newly implemented
+ * silly rename for nfs_rename() following the suggestions
+ * of Olaf Kirch (okir) found in this file.
+ * Following Linus comments on my original hack, this version
+ * depends only on the dcache stuff and doesn't touch the inode
+ * layer (iput() and friends).
*/
#include <linux/sched.h>
@@ -349,10 +356,13 @@
return time < max;
}
+static void nfs_silly_delete(struct dentry *);
+
static struct dentry_operations nfs_dentry_operations = {
nfs_lookup_revalidate,
0, /* d_hash */
0, /* d_compare */
+ nfs_silly_delete,
};
static int nfs_lookup(struct inode *dir, struct dentry * dentry)
@@ -378,7 +388,6 @@
inode = NULL;
if (!error) {
- error = -ENOENT;
inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
if (!inode)
return -EACCES;
@@ -530,9 +539,150 @@
return 0;
}
-/*
- * We should do silly-rename here, but I'm too lazy to fix
- * up the directory entry implications of it..
+
+/* Note: we copy the code from lookup_dentry() here, only: we have to
+ * omit the directory lock. We are already the owner of the lock when
+ * we reach here. And "down(&dir->i_sem)" would make us sleep forever
+ * ('cause WE have the lock)
+ *
+ * VERY IMPORTANT: calculate the hash for this dentry!!!!!!!!
+ * Otherwise the cached lookup DEFINITELY WILL fail. And a new dentry
+ * is created. Without the DCACHE_NFSFS_RENAMED flag. And with d_count
+ * == 1. And trouble.
+ *
+ * Concerning my choice of the temp name: it is just nice to have
+ * i_ino part of the temp name, as this offers another check whether
+ * somebody attempts to remove the "silly renamed" dentry
+ * itself. Which is something that I consider evil. Your opinion may
+ * vary.
+ * BUT:
+ * Now that I compute the hash value right, it should be possible to simply
+ * check for the DCACHE_NFSFS_RENAMED flag in dentry->d_flag instead of
+ * doing the string compare.
+ * WHICH MEANS:
+ * This offers the opportunity to shorten the temp name. Currently, I use
+ * the hex representation of i_ino + the hex value of jiffies. This
+ * sums up to as much as 36 characters for a 64 bit machine, and needs
+ * 20 chars on a 32 bit machine. Have a look at jiffiesize etc.
+ * QUINTESSENCE
+ * The use of i_ino is simply cosmetic. All we need is a unique temp
+ * file name for the .nfs files. The hex representation of "jiffies"
+ * seemed to be adequate. And as we retry in case such a file already
+ * exists we are guaranteed to succed (after some jiffies have passed
+ * by :)
+ */
+
+static
+struct dentry *nfs_silly_lookup(struct dentry *parent, char *silly, int slen)
+{
+ struct qstr sqstr;
+ struct dentry *sdentry;
+ int i, error;
+
+ sqstr.name = silly;
+ sqstr.len = slen;
+ sqstr.hash = init_name_hash();
+ for (i= 0; i < slen; i++)
+ sqstr.hash = partial_name_hash(silly[i], sqstr.hash);
+ sqstr.hash = end_name_hash(sqstr.hash);
+ sdentry = d_lookup(parent, &sqstr);
+ if (!sdentry) {
+ sdentry = d_alloc(parent, &sqstr);
+ if (sdentry == NULL)
+ return ERR_PTR(-ENOMEM);
+ error = nfs_lookup(parent->d_inode, sdentry);
+ if (error) {
+ dput(sdentry);
+ return ERR_PTR(error);
+ }
+ }
+ return sdentry;
+}
+
+static int nfs_sillyrename(struct inode *dir, struct dentry *dentry)
+{
+ static unsigned int sillycounter = 0;
+ const int i_inosize = sizeof(dir->i_ino)*2;
+ const int countersize = sizeof(sillycounter)*2;
+ const int slen = strlen(".nfs") + i_inosize + countersize;
+ char silly[slen+1];
+ int error;
+ struct dentry *sdentry;
+
+ if (dentry->d_count == 1) {
+ return -EIO; /* No need to silly rename. */
+ }
+
+ if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
+ return -EBUSY; /* don't allow to unlink silly inode -- nope,
+ * think a bit: silly DENTRY, NOT inode --
+ * itself
+ */
+ }
+
+ sprintf(silly, ".nfs%*.*lx",
+ i_inosize, i_inosize, dentry->d_inode->i_ino);
+
+ sdentry = NULL;
+ do {
+ char *suffix = silly + slen - countersize;
+
+ dput(sdentry);
+ sillycounter++;
+ sprintf(suffix, "%*.*x", countersize, countersize, sillycounter);
+
+ dfprintk(VFS, "trying to rename %s to %s\n",
+ dentry->d_name.name, silly);
+
+ sdentry = nfs_silly_lookup(dentry->d_parent, silly, slen);
+ if (IS_ERR(sdentry)) {
+ return -EIO; /* FIXME ? */
+ }
+ } while(sdentry->d_inode != NULL); /* need negative lookup */
+
+ error = nfs_proc_rename(NFS_SERVER(dir),
+ NFS_FH(dir), dentry->d_name.name,
+ NFS_FH(dir), silly);
+ if (error) {
+ dput(sdentry);
+ return error;
+ }
+ nfs_invalidate_dircache(dir);
+ d_move(dentry, sdentry);
+ dput(sdentry);
+ dentry->d_flags |= DCACHE_NFSFS_RENAMED;
+
+ return 0; /* don't unlink */
+}
+
+static void nfs_silly_delete(struct dentry *dentry)
+{
+ if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
+ struct inode *dir = dentry->d_parent->d_inode;
+ int error;
+
+ dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
+
+ /* Unhash it first */
+ d_drop(dentry);
+ dfprintk(VFS, "trying to unlink %s\n", dentry->d_name.name);
+ error = nfs_proc_remove(NFS_SERVER(dir),
+ NFS_FH(dir), dentry->d_name.name);
+ if (error < 0)
+ printk("NFS " __FUNCTION__ " failed (err = %d)\n",
+ -error);
+ dentry->d_inode->i_nlink --;
+ nfs_invalidate_dircache(dir);
+ }
+}
+
+/* We do silly rename. In case sillyrename() returns -EBUSY, the inode
+ * belongs to an active ".nfs..." file and we return -EBUSY.
+ *
+ * If sillyrename() returns 0, we do nothing, otherwise we unlink.
+ *
+ * inode->i_nlink is updated here rather than waiting for the next
+ * nfs_refresh_inode() for cosmetic reasons only.
*/
static int nfs_unlink(struct inode *dir, struct dentry *dentry)
{
@@ -549,12 +699,21 @@
if (dentry->d_name.len > NFS_MAXNAMLEN)
return -ENAMETOOLONG;
- error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name);
- if (error)
- return error;
+ error = nfs_sillyrename(dir, dentry);
+
+ if (error == -EBUSY) {
+ return -EBUSY;
+ } else if (error < 0) {
+ error = nfs_proc_remove(NFS_SERVER(dir),
+ NFS_FH(dir), dentry->d_name.name);
+ if (error < 0)
+ return error;
+
+ dentry->d_inode->i_nlink --;
+ nfs_invalidate_dircache(dir);
+ d_delete(dentry);
+ }
- nfs_invalidate_dircache(dir);
- d_delete(dentry);
return 0;
}
@@ -588,7 +747,7 @@
return error;
nfs_invalidate_dircache(dir);
- /* this looks _funny_ doesn't it? But: nfs_proc_symlynk()
+ /* this looks _funny_ doesn't it? But: nfs_proc_symlink()
* only fills in sattr, not fattr. Thus nfs_fhget() cannot be
* called, it would be pointless, without a valid fattr
* argument. Other possibility: call nfs_proc_lookup()
@@ -623,7 +782,8 @@
return error;
nfs_invalidate_dircache(dir);
- inode->i_count++;
+ inode->i_count ++;
+ inode->i_nlink ++; /* no need to wait for nfs_refresh_inode() */
d_instantiate(dentry, inode);
return 0;
}
@@ -634,8 +794,17 @@
* different file handle for the same inode after a rename (e.g. when
* moving to a different directory). A fail-safe method to do so would
* be to look up old_dir/old_name, create a link to new_dir/new_name and
- * rename the old file using the silly_rename stuff. This way, the original
+ * rename the old file using the sillyrename stuff. This way, the original
* file in old_dir will go away when the last process iput()s the inode.
+ *
+ * FIXED.
+ *
+ * It actually works quite well. One needs to have the possibility for
+ * at least one ".nfs..." file in each directory the file ever gets
+ * moved or linked to which happens automagically with the new
+ * implementation that only depends on the dcache stuff instead of
+ * using the inode layer
+ *
*/
static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
@@ -659,10 +828,22 @@
if (old_dentry->d_name.len > NFS_MAXNAMLEN || new_dentry->d_name.len > NFS_MAXNAMLEN)
return -ENAMETOOLONG;
- error = nfs_proc_rename(NFS_SERVER(old_dir),
- NFS_FH(old_dir), old_dentry->d_name.name,
- NFS_FH(new_dir), new_dentry->d_name.name);
+ if (new_dir != old_dir) {
+ error = nfs_sillyrename(old_dir, old_dentry);
+ if (error == -EBUSY) {
+ return -EBUSY;
+ } else if (error == 0) { /* did silly rename stuff */
+ error = nfs_link(old_dentry->d_inode,
+ new_dir, new_dentry);
+
+ return error;
+ }
+ /* no need for silly rename, proceed as usual */
+ }
+ error = nfs_proc_rename(NFS_SERVER(old_dir),
+ NFS_FH(old_dir), old_dentry->d_name.name,
+ NFS_FH(new_dir), new_dentry->d_name.name);
if (error)
return error;
@@ -737,3 +918,10 @@
} else
inode->i_op = NULL;
}
+
+/*
+ * Local variables:
+ * version-control: t
+ * kept-new-versions: 5
+ * End:
+ */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov