summaryrefslogtreecommitdiffstats
path: root/fs/nfs/dir.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-09-12 01:29:55 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-09-12 01:29:55 +0000
commit545f435ebcfd94a1e7c20b46efe81b4d6ac4e698 (patch)
treee9ce4bc598d06374bda906f18365984bf22a526a /fs/nfs/dir.c
parent4291a610eef89d0d5c69d9a10ee6560e1aa36c74 (diff)
Merge with Linux 2.1.55. More bugfixes and goodies from my private
CVS archive.
Diffstat (limited to 'fs/nfs/dir.c')
-rw-r--r--fs/nfs/dir.c251
1 files changed, 222 insertions, 29 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 83546d460..acee50754 100644
--- a/fs/nfs/dir.c
+++ b/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>
@@ -41,7 +48,7 @@ struct nfs_dirent {
static int nfs_dir_open(struct inode * inode, struct file * file);
static long nfs_dir_read(struct inode *, struct file *, char *, unsigned long);
-static int nfs_readdir(struct inode *, struct file *, void *, filldir_t);
+static int nfs_readdir(struct file *, void *, filldir_t);
static int nfs_lookup(struct inode *, struct dentry *);
static int nfs_create(struct inode *, struct dentry *, int);
static int nfs_mkdir(struct inode *, struct dentry *, int);
@@ -114,9 +121,7 @@ static struct nfs_dirent dircache[NFS_MAX_DIRCACHE];
* page cache (may require some fiddling for rsize < PAGE_SIZE).
*/
-static int
-nfs_readdir(struct inode *inode, struct file *filp, void *dirent,
- filldir_t filldir)
+static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
static struct wait_queue *readdir_wait = NULL;
struct wait_queue **waitp = NULL;
@@ -126,6 +131,7 @@ nfs_readdir(struct inode *inode, struct file *filp, void *dirent,
u32 cookie;
int ismydir, result;
int i, j, index = 0;
+ struct inode *inode = filp->f_dentry->d_inode;
dfprintk(VFS, "NFS: nfs_readdir(%x/%ld)\n", inode->i_dev, inode->i_ino);
if (!inode || !S_ISDIR(inode->i_mode)) {
@@ -349,6 +355,15 @@ static int nfs_lookup_revalidate(struct dentry * dentry)
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)
{
struct inode *inode;
@@ -372,7 +387,6 @@ static int nfs_lookup(struct inode *dir, struct dentry * dentry)
inode = NULL;
if (!error) {
- error = -ENOENT;
inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
if (!inode)
return -EACCES;
@@ -380,7 +394,7 @@ static int nfs_lookup(struct inode *dir, struct dentry * dentry)
return error;
dentry->d_time = jiffies;
- dentry->d_revalidate = nfs_lookup_revalidate;
+ dentry->d_op = &nfs_dentry_operations;
d_add(dentry, inode);
return 0;
}
@@ -524,9 +538,150 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
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)
{
@@ -543,21 +698,27 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
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;
}
static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
struct nfs_sattr sattr;
- struct nfs_fattr fattr;
- struct nfs_fh fhandle;
- struct inode * inode;
int error;
dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n",
@@ -584,12 +745,16 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
if (error)
return error;
- inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
- if (!inode)
- return -EACCES;
-
nfs_invalidate_dircache(dir);
- d_instantiate(dentry, inode);
+ /* 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()
+ * HERE. But why? If somebody wants to reference this
+ * symlink, the cached_lookup() will fail, and
+ * nfs_proc_symlink() will be called anyway.
+ */
+ d_drop(dentry);
return 0;
}
@@ -616,7 +781,8 @@ static int nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentr
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;
}
@@ -627,8 +793,17 @@ static int nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentr
* 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)
@@ -652,10 +827,22 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
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;
@@ -663,8 +850,7 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
nfs_invalidate_dircache(new_dir);
/* Update the dcache */
- d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
- d_delete(new_dentry);
+ d_move(old_dentry, new_dentry);
return 0;
}
@@ -731,3 +917,10 @@ void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
} else
inode->i_op = NULL;
}
+
+/*
+ * Local variables:
+ * version-control: t
+ * kept-new-versions: 5
+ * End:
+ */