summaryrefslogtreecommitdiffstats
path: root/fs/nfs/inode.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs/inode.c')
-rw-r--r--fs/nfs/inode.c301
1 files changed, 215 insertions, 86 deletions
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index eb56950eb..8300fee67 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -32,13 +32,16 @@
#include <asm/system.h>
#include <asm/uaccess.h>
+#define CONFIG_NFS_SNAPSHOT 1
#define NFSDBG_FACILITY NFSDBG_VFS
#define NFS_PARANOIA 1
+static struct inode * __nfs_fhget(struct super_block *, struct nfs_fattr *);
+
static void nfs_read_inode(struct inode *);
static void nfs_put_inode(struct inode *);
static void nfs_delete_inode(struct inode *);
-static int nfs_notify_change(struct inode *, struct iattr *);
+static int nfs_notify_change(struct dentry *, struct iattr *);
static void nfs_put_super(struct super_block *);
static int nfs_statfs(struct super_block *, struct statfs *, int);
@@ -138,6 +141,7 @@ nfs_put_super(struct super_block *sb)
*/
nfs_invalidate_dircache_sb(sb);
+ kfree(server->hostname);
sb->s_dev = 0;
unlock_super(sb);
MOD_DEC_USE_COUNT;
@@ -180,15 +184,16 @@ struct super_block *
nfs_read_super(struct super_block *sb, void *raw_data, int silent)
{
struct nfs_mount_data *data = (struct nfs_mount_data *) raw_data;
- struct sockaddr_in srvaddr;
struct nfs_server *server;
- struct rpc_timeout timeparms;
struct rpc_xprt *xprt;
struct rpc_clnt *clnt;
+ struct nfs_fh *root_fh;
+ struct inode *root_inode;
unsigned int authflavor;
int tcp;
- kdev_t dev = sb->s_dev;
- struct inode *root_inode;
+ struct sockaddr_in srvaddr;
+ struct rpc_timeout timeparms;
+ struct nfs_fattr fattr;
MOD_INC_USE_COUNT;
if (!data)
@@ -211,7 +216,6 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
lock_super(sb);
sb->s_magic = NFS_SUPER_MAGIC;
- sb->s_dev = dev;
sb->s_op = &nfs_sops;
sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
sb->u.nfs_sb.s_root = data->root;
@@ -223,6 +227,10 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
server->acregmax = data->acregmax*HZ;
server->acdirmin = data->acdirmin*HZ;
server->acdirmax = data->acdirmax*HZ;
+
+ server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL);
+ if (!server->hostname)
+ goto out_unlock;
strcpy(server->hostname, data->hostname);
/* Which protocol do we use? */
@@ -234,21 +242,19 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
timeparms.to_maxval = tcp? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT;
timeparms.to_exponential = 1;
- /* Choose authentication flavor */
- if (data->flags & NFS_MOUNT_SECURE) {
- authflavor = RPC_AUTH_DES;
- } else if (data->flags & NFS_MOUNT_KERBEROS) {
- authflavor = RPC_AUTH_KRB;
- } else {
- authflavor = RPC_AUTH_UNIX;
- }
-
/* Now create transport and client */
xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP,
&srvaddr, &timeparms);
if (xprt == NULL)
goto out_no_xprt;
+ /* Choose authentication flavor */
+ authflavor = RPC_AUTH_UNIX;
+ if (data->flags & NFS_MOUNT_SECURE)
+ authflavor = RPC_AUTH_DES;
+ else if (data->flags & NFS_MOUNT_KERBEROS)
+ authflavor = RPC_AUTH_KRB;
+
clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
NFS_VERSION, authflavor);
if (clnt == NULL)
@@ -260,23 +266,30 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
server->client = clnt;
/* Fire up rpciod if not yet running */
-#ifdef RPCIOD_RESULT
- if (rpciod_up())
+ if (rpciod_up() != 0)
goto out_no_iod;
-#else
- rpciod_up();
-#endif
/*
* Keep the super block locked while we try to get
* the root fh attributes.
*/
- root_inode = nfs_fhget(sb, &data->root, NULL);
+ root_fh = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
+ if (!root_fh)
+ goto out_no_fh;
+ *root_fh = data->root;
+
+ if (nfs_proc_getattr(server, root_fh, &fattr) != 0)
+ goto out_no_fattr;
+
+ root_inode = __nfs_fhget(sb, &fattr);
if (!root_inode)
goto out_no_root;
sb->s_root = d_alloc_root(root_inode, NULL);
if (!sb->s_root)
goto out_no_root;
+ sb->s_root->d_op = &nfs_dentry_operations;
+ sb->s_root->d_fsdata = root_fh;
+
/* We're airborne */
unlock_super(sb);
@@ -289,14 +302,19 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
out_no_root:
printk("nfs_read_super: get root inode failed\n");
iput(root_inode);
+ goto out_free_fh;
+
+out_no_fattr:
+ printk("nfs_read_super: get root fattr failed\n");
+out_free_fh:
+ kfree(root_fh);
+out_no_fh:
rpciod_down();
-#ifdef RPCIOD_RESULT
goto out_shutdown;
out_no_iod:
- printk("nfs_read_super: couldn't start rpciod!\n");
+ printk("NFS: couldn't start rpciod!\n");
out_shutdown:
-#endif
rpc_shutdown_client(server->client);
goto out_unlock;
@@ -307,6 +325,7 @@ out_no_client:
out_no_xprt:
printk("NFS: cannot create RPC transport.\n");
+ kfree(server->hostname);
out_unlock:
unlock_super(sb);
goto out_fail;
@@ -349,46 +368,39 @@ nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
}
/*
- * This is our own version of iget that looks up inodes by file handle
- * instead of inode number. We use this technique instead of using
- * the vfs read_inode function because there is no way to pass the
- * file handle or current attributes into the read_inode function.
+ * Free all unused dentries in an inode's alias list.
+ *
+ * Subtle note: we have to be very careful not to cause
+ * any IO operations with the stale dentries, as this
+ * could cause file corruption. But since the dentry
+ * count is 0 and all pending IO for a dentry has been
+ * flushed when the count went to 0, we're safe here.
*/
-struct inode *
-nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle,
- struct nfs_fattr *fattr)
+void nfs_free_dentries(struct inode *inode)
{
- struct nfs_fattr newfattr;
- int error;
- struct inode *inode;
-
- if (!sb) {
- printk("nfs_fhget: super block is NULL\n");
- return NULL;
- }
- if (!fattr) {
- error = nfs_proc_getattr(&sb->u.nfs_sb.s_server, fhandle,
- &newfattr);
- if (error) {
- printk("nfs_fhget: getattr error = %d\n", -error);
- return NULL;
+ struct list_head *tmp, *head = &inode->i_dentry;
+
+restart:
+ tmp = head;
+ while ((tmp = tmp->next) != head) {
+ struct dentry *dentry = list_entry(tmp, struct dentry, d_alias);
+ if (!dentry->d_count) {
+printk("nfs_free_dentries: freeing %s/%s, i_count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count);
+ dget(dentry);
+ d_drop(dentry);
+ dput(dentry);
+ goto restart;
}
- fattr = &newfattr;
- }
- if (!(inode = iget(sb, fattr->fileid))) {
- printk("nfs_fhget: iget failed\n");
- return NULL;
- }
-#ifdef NFS_PARANOIA
-if (inode->i_dev != sb->s_dev)
-printk("nfs_fhget: impossible\n");
-#endif
-
- if (inode->i_ino != fattr->fileid) {
- printk("nfs_fhget: unexpected inode from iget\n");
- return inode;
}
+}
+/*
+ * Fill in inode information from the fattr.
+ */
+static void
+nfs_fill_inode(struct inode *inode, struct nfs_fattr *fattr)
+{
/*
* Check whether the mode has been set, as we only want to
* do this once. (We don't allow inodes to change types.)
@@ -418,29 +430,140 @@ printk("nfs_fhget: impossible\n");
inode->i_size = fattr->size;
inode->i_mtime = fattr->mtime.seconds;
NFS_OLDMTIME(inode) = fattr->mtime.seconds;
- *NFS_FH(inode) = *fhandle;
}
- if (memcmp(NFS_FH(inode), fhandle, sizeof(struct nfs_fh)))
- printk("nfs_fhget: fhandle changed!\n");
nfs_refresh_inode(inode, fattr);
- dprintk("NFS: fhget(%x/%ld ct=%d)\n",
- inode->i_dev, inode->i_ino,
- inode->i_count);
+}
+/*
+ * This is our own version of iget that looks up inodes by file handle
+ * instead of inode number. We use this technique instead of using
+ * the vfs read_inode function because there is no way to pass the
+ * file handle or current attributes into the read_inode function.
+ *
+ * We provide a special check for NetApp .snapshot directories to avoid
+ * inode aliasing problems. All snapshot inodes are anonymous (unhashed).
+ */
+struct inode *
+nfs_fhget(struct dentry *dentry, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
+{
+ struct super_block *sb = dentry->d_sb;
+
+ dprintk("NFS: nfs_fhget(%s/%s fileid=%d)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ fattr->fileid);
+
+ /* Install the filehandle in the dentry */
+ *((struct nfs_fh *) dentry->d_fsdata) = *fhandle;
+
+#ifdef CONFIG_NFS_SNAPSHOT
+ /*
+ * Check for NetApp snapshot dentries, and get an
+ * unhashed inode to avoid aliasing problems.
+ */
+ if ((dentry->d_parent->d_inode->u.nfs_i.flags & NFS_IS_SNAPSHOT) ||
+ (IS_ROOT(dentry->d_parent) && dentry->d_name.len == 9 &&
+ memcmp(dentry->d_name.name, ".snapshot", 9) == 0)) {
+ struct inode *inode = get_empty_inode();
+ if (!inode)
+ goto out;
+ inode->i_sb = sb;
+ inode->i_dev = sb->s_dev;
+ inode->i_ino = fattr->fileid;
+ nfs_read_inode(inode);
+ nfs_fill_inode(inode, fattr);
+ inode->u.nfs_i.flags |= NFS_IS_SNAPSHOT;
+ dprintk("NFS: nfs_fhget(snapshot ino=%ld)\n", inode->i_ino);
+ out:
+ return inode;
+ }
+#endif
+ return __nfs_fhget(sb, fattr);
+}
+
+/*
+ * Look up the inode by super block and fattr->fileid.
+ *
+ * Note carefully the special handling of busy inodes (i_count > 1).
+ * With the kernel 2.1.xx dcache all inodes except hard links must
+ * have i_count == 1 after iget(). Otherwise, it indicates that the
+ * server has reused a fileid (i_ino) and we have a stale inode.
+ */
+static struct inode *
+__nfs_fhget(struct super_block *sb, struct nfs_fattr *fattr)
+{
+ struct inode *inode;
+ int max_count;
+
+retry:
+ inode = iget(sb, fattr->fileid);
+ if (!inode)
+ goto out_no_inode;
+ /* N.B. This should be impossible ... */
+ if (inode->i_ino != fattr->fileid)
+ goto out_bad_id;
+
+ /*
+ * Check for busy inodes, and attempt to get rid of any
+ * unused local references. If successful, we release the
+ * inode and try again.
+ *
+ * Note that the busy test uses the values in the fattr,
+ * as the inode may have become a different object.
+ * (We can probably handle modes changes here, too.)
+ */
+ max_count = S_ISDIR(fattr->mode) ? 1 : fattr->nlink;
+ if (inode->i_count > max_count) {
+printk("__nfs_fhget: inode %ld busy, i_count=%d, i_nlink=%d\n",
+inode->i_ino, inode->i_count, inode->i_nlink);
+ nfs_free_dentries(inode);
+ if (inode->i_count > max_count) {
+printk("__nfs_fhget: inode %ld still busy, i_count=%d\n",
+inode->i_ino, inode->i_count);
+ if (!list_empty(&inode->i_dentry)) {
+ struct dentry *dentry;
+ dentry = list_entry(inode->i_dentry.next,
+ struct dentry, d_alias);
+printk("__nfs_fhget: killing %s/%s filehandle\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+ memset(dentry->d_fsdata, 0,
+ sizeof(struct nfs_fh));
+ } else
+ printk("NFS: inode %ld busy, no aliases?\n",
+ inode->i_ino);
+ make_bad_inode(inode);
+ remove_inode_hash(inode);
+ }
+ iput(inode);
+ goto retry;
+ }
+ nfs_fill_inode(inode, fattr);
+ dprintk("NFS: __nfs_fhget(%x/%ld ct=%d)\n",
+ inode->i_dev, inode->i_ino, inode->i_count);
+
+out:
return inode;
+
+out_no_inode:
+ printk("__nfs_fhget: iget failed\n");
+ goto out;
+out_bad_id:
+ printk("__nfs_fhget: unexpected inode from iget\n");
+ goto out;
}
int
-nfs_notify_change(struct inode *inode, struct iattr *attr)
+nfs_notify_change(struct dentry *dentry, struct iattr *attr)
{
+ struct inode *inode = dentry->d_inode;
+ int error;
struct nfs_sattr sattr;
struct nfs_fattr fattr;
- int error;
/*
* Make sure the inode is up-to-date.
*/
- error = nfs_revalidate(inode);
+ error = nfs_revalidate(dentry);
if (error) {
#ifdef NFS_PARANOIA
printk("nfs_notify_change: revalidate failed, error=%d\n", error);
@@ -476,7 +599,7 @@ printk("nfs_notify_change: revalidate failed, error=%d\n", error);
sattr.atime.useconds = 0;
}
- error = nfs_proc_setattr(NFS_SERVER(inode), NFS_FH(inode),
+ error = nfs_proc_setattr(NFS_DSERVER(dentry), NFS_FH(dentry),
&sattr, &fattr);
if (error)
goto out;
@@ -503,9 +626,9 @@ out:
* Externally visible revalidation function
*/
int
-nfs_revalidate(struct inode *inode)
+nfs_revalidate(struct dentry *dentry)
{
- return nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ return nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
}
/*
@@ -513,38 +636,43 @@ nfs_revalidate(struct inode *inode)
* the cached attributes have to be refreshed.
*/
int
-_nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
+_nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry)
{
- struct nfs_fattr fattr;
+ struct inode *inode = dentry->d_inode;
int status = 0;
+ struct nfs_fattr fattr;
if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
goto out;
- dfprintk(PAGECACHE, "NFS: revalidating %x/%ld inode\n",
- inode->i_dev, inode->i_ino);
- status = nfs_proc_getattr(server, NFS_FH(inode), &fattr);
+ dfprintk(PAGECACHE, "NFS: revalidating %s/%s, ino=%ld\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ inode->i_ino);
+ status = nfs_proc_getattr(server, NFS_FH(dentry), &fattr);
if (status) {
#ifdef NFS_PARANOIA
-printk("nfs_revalidate_inode: getattr failed, error=%d\n", status);
+printk("nfs_revalidate_inode: %s/%s getattr failed, ino=%ld, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, status);
#endif
- goto done;
+ goto out;
}
status = nfs_refresh_inode(inode, &fattr);
- if (status)
- goto done;
+ if (status) {
+#ifdef NFS_PARANOIA
+printk("nfs_revalidate_inode: %s/%s refresh failed, ino=%ld, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, status);
+#endif
+ goto out;
+ }
if (fattr.mtime.seconds == NFS_OLDMTIME(inode)) {
/* Update attrtimeo value */
if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode))
NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode);
}
NFS_OLDMTIME(inode) = fattr.mtime.seconds;
-
-done:
- dfprintk(PAGECACHE,
- "NFS: inode %x/%ld revalidation complete (status %d).\n",
- inode->i_dev, inode->i_ino, status);
+ dfprintk(PAGECACHE, "NFS: %s/%s revalidation complete\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
out:
return status;
}
@@ -575,7 +703,8 @@ nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
goto out;
}
if (inode->i_ino != fattr->fileid) {
- printk("nfs_refresh_inode: inode number mismatch\n");
+ printk("nfs_refresh_inode: mismatch, ino=%ld, fattr=%d\n",
+ inode->i_ino, fattr->fileid);
goto out;
}