Merge branch 'nfs-for-2.6.32'
Trond Myklebust [Fri, 11 Sep 2009 18:59:37 +0000 (14:59 -0400)]
48 files changed:
Documentation/filesystems/nfs.txt [new file with mode: 0644]
Documentation/kernel-parameters.txt
fs/lockd/host.c
fs/lockd/mon.c
fs/nfs/Makefile
fs/nfs/cache_lib.c [new file with mode: 0644]
fs/nfs/cache_lib.h [new file with mode: 0644]
fs/nfs/callback.c
fs/nfs/client.c
fs/nfs/direct.c
fs/nfs/dns_resolve.c [new file with mode: 0644]
fs/nfs/dns_resolve.h [new file with mode: 0644]
fs/nfs/file.c
fs/nfs/idmap.c
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/mount_clnt.c
fs/nfs/nfs3proc.c
fs/nfs/nfs4namespace.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4xdr.c
fs/nfs/super.c
fs/nfs/write.c
fs/nfsd/export.c
fs/nfsd/nfs4idmap.c
fs/nfsd/nfsctl.c
include/linux/nfs4.h
include/linux/nfs_fs_sb.h
include/linux/sunrpc/cache.h
include/linux/sunrpc/clnt.h
include/linux/sunrpc/msg_prot.h
include/linux/sunrpc/rpc_pipe_fs.h
include/linux/sunrpc/xdr.h
include/linux/sunrpc/xprt.h
net/sunrpc/Makefile
net/sunrpc/addr.c [new file with mode: 0644]
net/sunrpc/auth_gss/auth_gss.c
net/sunrpc/auth_gss/svcauth_gss.c
net/sunrpc/cache.c
net/sunrpc/clnt.c
net/sunrpc/rpc_pipe.c
net/sunrpc/rpcb_clnt.c
net/sunrpc/sunrpc_syms.c
net/sunrpc/svcauth_unix.c
net/sunrpc/timer.c
net/sunrpc/xdr.c
net/sunrpc/xprtrdma/transport.c
net/sunrpc/xprtsock.c

diff --git a/Documentation/filesystems/nfs.txt b/Documentation/filesystems/nfs.txt
new file mode 100644 (file)
index 0000000..f50f26c
--- /dev/null
@@ -0,0 +1,98 @@
+
+The NFS client
+==============
+
+The NFS version 2 protocol was first documented in RFC1094 (March 1989).
+Since then two more major releases of NFS have been published, with NFSv3
+being documented in RFC1813 (June 1995), and NFSv4 in RFC3530 (April
+2003).
+
+The Linux NFS client currently supports all the above published versions,
+and work is in progress on adding support for minor version 1 of the NFSv4
+protocol.
+
+The purpose of this document is to provide information on some of the
+upcall interfaces that are used in order to provide the NFS client with
+some of the information that it requires in order to fully comply with
+the NFS spec.
+
+The DNS resolver
+================
+
+NFSv4 allows for one server to refer the NFS client to data that has been
+migrated onto another server by means of the special "fs_locations"
+attribute. See
+       http://tools.ietf.org/html/rfc3530#section-6
+and
+       http://tools.ietf.org/html/draft-ietf-nfsv4-referrals-00
+
+The fs_locations information can take the form of either an ip address and
+a path, or a DNS hostname and a path. The latter requires the NFS client to
+do a DNS lookup in order to mount the new volume, and hence the need for an
+upcall to allow userland to provide this service.
+
+Assuming that the user has the 'rpc_pipefs' filesystem mounted in the usual
+/var/lib/nfs/rpc_pipefs, the upcall consists of the following steps:
+
+   (1) The process checks the dns_resolve cache to see if it contains a
+       valid entry. If so, it returns that entry and exits.
+
+   (2) If no valid entry exists, the helper script '/sbin/nfs_cache_getent'
+       (may be changed using the 'nfs.cache_getent' kernel boot parameter)
+       is run, with two arguments:
+               - the cache name, "dns_resolve"
+               - the hostname to resolve
+
+   (3) After looking up the corresponding ip address, the helper script
+       writes the result into the rpc_pipefs pseudo-file
+       '/var/lib/nfs/rpc_pipefs/cache/dns_resolve/channel'
+       in the following (text) format:
+
+               "<ip address> <hostname> <ttl>\n"
+
+       Where <ip address> is in the usual IPv4 (123.456.78.90) or IPv6
+       (ffee:ddcc:bbaa:9988:7766:5544:3322:1100, ffee::1100, ...) format.
+       <hostname> is identical to the second argument of the helper
+       script, and <ttl> is the 'time to live' of this cache entry (in
+       units of seconds).
+
+       Note: If <ip address> is invalid, say the string "0", then a negative
+       entry is created, which will cause the kernel to treat the hostname
+       as having no valid DNS translation.
+
+
+
+
+A basic sample /sbin/nfs_cache_getent
+=====================================
+
+#!/bin/bash
+#
+ttl=600
+#
+cut=/usr/bin/cut
+getent=/usr/bin/getent
+rpc_pipefs=/var/lib/nfs/rpc_pipefs
+#
+die()
+{
+       echo "Usage: $0 cache_name entry_name"
+       exit 1
+}
+
+[ $# -lt 2 ] && die
+cachename="$1"
+cache_path=${rpc_pipefs}/cache/${cachename}/channel
+
+case "${cachename}" in
+       dns_resolve)
+               name="$2"
+               result="$(${getent} hosts ${name} | ${cut} -f1 -d\ )"
+               [ -z "${result}" ] && result="0"
+               ;;
+       *)
+               die
+               ;;
+esac
+echo "${result} ${name} ${ttl}" >${cache_path}
+
index 7936b80..ce88537 100644 (file)
@@ -1503,6 +1503,14 @@ and is between 256 and 4096 characters. It is defined in the file
                        [NFS] set the TCP port on which the NFSv4 callback
                        channel should listen.
 
+       nfs.cache_getent=
+                       [NFS] sets the pathname to the program which is used
+                       to update the NFS client cache entries.
+
+       nfs.cache_getent_timeout=
+                       [NFS] sets the timeout after which an attempt to
+                       update a cache entry is deemed to have failed.
+
        nfs.idmap_cache_timeout=
                        [NFS] set the maximum lifetime for idmapper cache
                        entries.
@@ -2395,6 +2403,18 @@ and is between 256 and 4096 characters. It is defined in the file
        stifb=          [HW]
                        Format: bpp:<bpp1>[:<bpp2>[:<bpp3>...]]
 
+       sunrpc.min_resvport=
+       sunrpc.max_resvport=
+                       [NFS,SUNRPC]
+                       SunRPC servers often require that client requests
+                       originate from a privileged port (i.e. a port in the
+                       range 0 < portnr < 1024).
+                       An administrator who wishes to reserve some of these
+                       ports for other uses may adjust the range that the
+                       kernel's sunrpc client considers to be privileged
+                       using these two parameters to set the minimum and
+                       maximum port values.
+
        sunrpc.pool_mode=
                        [NFS]
                        Control how the NFS server code allocates CPUs to
@@ -2411,6 +2431,15 @@ and is between 256 and 4096 characters. It is defined in the file
                        pernode     one pool for each NUMA node (equivalent
                                    to global on non-NUMA machines)
 
+       sunrpc.tcp_slot_table_entries=
+       sunrpc.udp_slot_table_entries=
+                       [NFS,SUNRPC]
+                       Sets the upper limit on the number of simultaneous
+                       RPC calls that can be sent from the client to a
+                       server. Increasing these values may allow you to
+                       improve throughput, but will also increase the
+                       amount of memory reserved for use by the client.
+
        swiotlb=        [IA-64] Number of I/O TLB slabs
 
        switches=       [HW,M68k]
index 99d737b..7cb076a 100644 (file)
@@ -87,18 +87,6 @@ static unsigned int nlm_hash_address(const struct sockaddr *sap)
        return hash & (NLM_HOST_NRHASH - 1);
 }
 
-static void nlm_clear_port(struct sockaddr *sap)
-{
-       switch (sap->sa_family) {
-       case AF_INET:
-               ((struct sockaddr_in *)sap)->sin_port = 0;
-               break;
-       case AF_INET6:
-               ((struct sockaddr_in6 *)sap)->sin6_port = 0;
-               break;
-       }
-}
-
 /*
  * Common host lookup routine for server & client
  */
@@ -177,7 +165,7 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
        host->h_addrbuf    = nsm->sm_addrbuf;
        memcpy(nlm_addr(host), ni->sap, ni->salen);
        host->h_addrlen = ni->salen;
-       nlm_clear_port(nlm_addr(host));
+       rpc_set_port(nlm_addr(host), 0);
        memcpy(nlm_srcaddr(host), ni->src_sap, ni->src_len);
        host->h_version    = ni->version;
        host->h_proto      = ni->protocol;
index 7fce1b5..30c9331 100644 (file)
@@ -61,43 +61,6 @@ static inline struct sockaddr *nsm_addr(const struct nsm_handle *nsm)
        return (struct sockaddr *)&nsm->sm_addr;
 }
 
-static void nsm_display_ipv4_address(const struct sockaddr *sap, char *buf,
-                                    const size_t len)
-{
-       const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
-       snprintf(buf, len, "%pI4", &sin->sin_addr.s_addr);
-}
-
-static void nsm_display_ipv6_address(const struct sockaddr *sap, char *buf,
-                                    const size_t len)
-{
-       const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
-
-       if (ipv6_addr_v4mapped(&sin6->sin6_addr))
-               snprintf(buf, len, "%pI4", &sin6->sin6_addr.s6_addr32[3]);
-       else if (sin6->sin6_scope_id != 0)
-               snprintf(buf, len, "%pI6%%%u", &sin6->sin6_addr,
-                               sin6->sin6_scope_id);
-       else
-               snprintf(buf, len, "%pI6", &sin6->sin6_addr);
-}
-
-static void nsm_display_address(const struct sockaddr *sap,
-                               char *buf, const size_t len)
-{
-       switch (sap->sa_family) {
-       case AF_INET:
-               nsm_display_ipv4_address(sap, buf, len);
-               break;
-       case AF_INET6:
-               nsm_display_ipv6_address(sap, buf, len);
-               break;
-       default:
-               snprintf(buf, len, "unsupported address family");
-               break;
-       }
-}
-
 static struct rpc_clnt *nsm_create(void)
 {
        struct sockaddr_in sin = {
@@ -307,8 +270,11 @@ static struct nsm_handle *nsm_create_handle(const struct sockaddr *sap,
        memcpy(nsm_addr(new), sap, salen);
        new->sm_addrlen = salen;
        nsm_init_private(new);
-       nsm_display_address((const struct sockaddr *)&new->sm_addr,
-                               new->sm_addrbuf, sizeof(new->sm_addrbuf));
+
+       if (rpc_ntop(nsm_addr(new), new->sm_addrbuf,
+                                       sizeof(new->sm_addrbuf)) == 0)
+               (void)snprintf(new->sm_addrbuf, sizeof(new->sm_addrbuf),
+                               "unsupported address family");
        memcpy(new->sm_name, hostname, hostname_len);
        new->sm_name[hostname_len] = '\0';
 
index 8451598..da7fda6 100644 (file)
@@ -6,7 +6,8 @@ obj-$(CONFIG_NFS_FS) += nfs.o
 
 nfs-y                  := client.o dir.o file.o getroot.o inode.o super.o nfs2xdr.o \
                           direct.o pagelist.o proc.o read.o symlink.o unlink.o \
-                          write.o namespace.o mount_clnt.o
+                          write.o namespace.o mount_clnt.o \
+                          dns_resolve.o cache_lib.o
 nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
 nfs-$(CONFIG_NFS_V3)   += nfs3proc.o nfs3xdr.o
 nfs-$(CONFIG_NFS_V3_ACL)       += nfs3acl.o
diff --git a/fs/nfs/cache_lib.c b/fs/nfs/cache_lib.c
new file mode 100644 (file)
index 0000000..b4ffd01
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * linux/fs/nfs/cache_lib.c
+ *
+ * Helper routines for the NFS client caches
+ *
+ * Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com>
+ */
+#include <linux/kmod.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/sunrpc/cache.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
+
+#include "cache_lib.h"
+
+#define NFS_CACHE_UPCALL_PATHLEN 256
+#define NFS_CACHE_UPCALL_TIMEOUT 15
+
+static char nfs_cache_getent_prog[NFS_CACHE_UPCALL_PATHLEN] =
+                               "/sbin/nfs_cache_getent";
+static unsigned long nfs_cache_getent_timeout = NFS_CACHE_UPCALL_TIMEOUT;
+
+module_param_string(cache_getent, nfs_cache_getent_prog,
+               sizeof(nfs_cache_getent_prog), 0600);
+MODULE_PARM_DESC(cache_getent, "Path to the client cache upcall program");
+module_param_named(cache_getent_timeout, nfs_cache_getent_timeout, ulong, 0600);
+MODULE_PARM_DESC(cache_getent_timeout, "Timeout (in seconds) after which "
+               "the cache upcall is assumed to have failed");
+
+int nfs_cache_upcall(struct cache_detail *cd, char *entry_name)
+{
+       static char *envp[] = { "HOME=/",
+               "TERM=linux",
+               "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
+               NULL
+       };
+       char *argv[] = {
+               nfs_cache_getent_prog,
+               cd->name,
+               entry_name,
+               NULL
+       };
+       int ret = -EACCES;
+
+       if (nfs_cache_getent_prog[0] == '\0')
+               goto out;
+       ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
+       /*
+        * Disable the upcall mechanism if we're getting an ENOENT or
+        * EACCES error. The admin can re-enable it on the fly by using
+        * sysfs to set the 'cache_getent' parameter once the problem
+        * has been fixed.
+        */
+       if (ret == -ENOENT || ret == -EACCES)
+               nfs_cache_getent_prog[0] = '\0';
+out:
+       return ret > 0 ? 0 : ret;
+}
+
+/*
+ * Deferred request handling
+ */
+void nfs_cache_defer_req_put(struct nfs_cache_defer_req *dreq)
+{
+       if (atomic_dec_and_test(&dreq->count))
+               kfree(dreq);
+}
+
+static void nfs_dns_cache_revisit(struct cache_deferred_req *d, int toomany)
+{
+       struct nfs_cache_defer_req *dreq;
+
+       dreq = container_of(d, struct nfs_cache_defer_req, deferred_req);
+
+       complete_all(&dreq->completion);
+       nfs_cache_defer_req_put(dreq);
+}
+
+static struct cache_deferred_req *nfs_dns_cache_defer(struct cache_req *req)
+{
+       struct nfs_cache_defer_req *dreq;
+
+       dreq = container_of(req, struct nfs_cache_defer_req, req);
+       dreq->deferred_req.revisit = nfs_dns_cache_revisit;
+       atomic_inc(&dreq->count);
+
+       return &dreq->deferred_req;
+}
+
+struct nfs_cache_defer_req *nfs_cache_defer_req_alloc(void)
+{
+       struct nfs_cache_defer_req *dreq;
+
+       dreq = kzalloc(sizeof(*dreq), GFP_KERNEL);
+       if (dreq) {
+               init_completion(&dreq->completion);
+               atomic_set(&dreq->count, 1);
+               dreq->req.defer = nfs_dns_cache_defer;
+       }
+       return dreq;
+}
+
+int nfs_cache_wait_for_upcall(struct nfs_cache_defer_req *dreq)
+{
+       if (wait_for_completion_timeout(&dreq->completion,
+                       nfs_cache_getent_timeout * HZ) == 0)
+               return -ETIMEDOUT;
+       return 0;
+}
+
+int nfs_cache_register(struct cache_detail *cd)
+{
+       struct nameidata nd;
+       struct vfsmount *mnt;
+       int ret;
+
+       mnt = rpc_get_mount();
+       if (IS_ERR(mnt))
+               return PTR_ERR(mnt);
+       ret = vfs_path_lookup(mnt->mnt_root, mnt, "/cache", 0, &nd);
+       if (ret)
+               goto err;
+       ret = sunrpc_cache_register_pipefs(nd.path.dentry,
+                       cd->name, 0600, cd);
+       path_put(&nd.path);
+       if (!ret)
+               return ret;
+err:
+       rpc_put_mount();
+       return ret;
+}
+
+void nfs_cache_unregister(struct cache_detail *cd)
+{
+       sunrpc_cache_unregister_pipefs(cd);
+       rpc_put_mount();
+}
+
diff --git a/fs/nfs/cache_lib.h b/fs/nfs/cache_lib.h
new file mode 100644 (file)
index 0000000..76f856e
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Helper routines for the NFS client caches
+ *
+ * Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com>
+ */
+
+#include <linux/completion.h>
+#include <linux/sunrpc/cache.h>
+#include <asm/atomic.h>
+
+/*
+ * Deferred request handling
+ */
+struct nfs_cache_defer_req {
+       struct cache_req req;
+       struct cache_deferred_req deferred_req;
+       struct completion completion;
+       atomic_t count;
+};
+
+extern int nfs_cache_upcall(struct cache_detail *cd, char *entry_name);
+extern struct nfs_cache_defer_req *nfs_cache_defer_req_alloc(void);
+extern void nfs_cache_defer_req_put(struct nfs_cache_defer_req *dreq);
+extern int nfs_cache_wait_for_upcall(struct nfs_cache_defer_req *dreq);
+
+extern int nfs_cache_register(struct cache_detail *cd);
+extern void nfs_cache_unregister(struct cache_detail *cd);
index 7f604c7..293fa05 100644 (file)
@@ -43,21 +43,29 @@ static struct svc_program nfs4_callback_program;
 unsigned int nfs_callback_set_tcpport;
 unsigned short nfs_callback_tcpport;
 unsigned short nfs_callback_tcpport6;
-static const int nfs_set_port_min = 0;
-static const int nfs_set_port_max = 65535;
+#define NFS_CALLBACK_MAXPORTNR (65535U)
 
-static int param_set_port(const char *val, struct kernel_param *kp)
+static int param_set_portnr(const char *val, struct kernel_param *kp)
 {
-       char *endp;
-       int num = simple_strtol(val, &endp, 0);
-       if (endp == val || *endp || num < nfs_set_port_min || num > nfs_set_port_max)
+       unsigned long num;
+       int ret;
+
+       if (!val)
+               return -EINVAL;
+       ret = strict_strtoul(val, 0, &num);
+       if (ret == -EINVAL || num > NFS_CALLBACK_MAXPORTNR)
                return -EINVAL;
-       *((int *)kp->arg) = num;
+       *((unsigned int *)kp->arg) = num;
        return 0;
 }
 
-module_param_call(callback_tcpport, param_set_port, param_get_int,
-                &nfs_callback_set_tcpport, 0644);
+static int param_get_portnr(char *buffer, struct kernel_param *kp)
+{
+       return param_get_uint(buffer, kp);
+}
+#define param_check_portnr(name, p) __param_check(name, p, unsigned int);
+
+module_param_named(callback_tcpport, nfs_callback_set_tcpport, portnr, 0644);
 
 /*
  * This is the NFSv4 callback kernel thread.
index c6be84a..e350bd6 100644 (file)
@@ -809,6 +809,9 @@ static int nfs_init_server(struct nfs_server *server,
        /* Initialise the client representation from the mount data */
        server->flags = data->flags;
        server->options = data->options;
+       server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
+               NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP|
+               NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME;
 
        if (data->rsize)
                server->rsize = nfs_block_size(data->rsize, NULL);
@@ -1075,10 +1078,6 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
                (unsigned long long) server->fsid.major,
                (unsigned long long) server->fsid.minor);
 
-       BUG_ON(!server->nfs_client);
-       BUG_ON(!server->nfs_client->rpc_ops);
-       BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
-
        spin_lock(&nfs_client_lock);
        list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
        list_add_tail(&server->master_link, &nfs_volume_list);
@@ -1275,7 +1274,7 @@ static int nfs4_init_server(struct nfs_server *server,
 
        /* Initialise the client representation from the mount data */
        server->flags = data->flags;
-       server->caps |= NFS_CAP_ATOMIC_OPEN;
+       server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR;
        server->options = data->options;
 
        /* Get a client record */
@@ -1360,10 +1359,6 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
        if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
                server->namelen = NFS4_MAXNAMLEN;
 
-       BUG_ON(!server->nfs_client);
-       BUG_ON(!server->nfs_client->rpc_ops);
-       BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
-
        spin_lock(&nfs_client_lock);
        list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
        list_add_tail(&server->master_link, &nfs_volume_list);
@@ -1401,7 +1396,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
 
        /* Initialise the client representation from the parent server */
        nfs_server_copy_userdata(server, parent_server);
-       server->caps |= NFS_CAP_ATOMIC_OPEN;
+       server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR;
 
        /* Get a client representation.
         * Note: NFSv4 always uses TCP, */
index e4e089a..6c32100 100644 (file)
@@ -934,9 +934,6 @@ out:
  * back into its cache.  We let the server do generic write
  * parameter checking and report problems.
  *
- * We also avoid an unnecessary invocation of generic_osync_inode(),
- * as it is fairly meaningless to sync the metadata of an NFS file.
- *
  * We eliminate local atime updates, see direct read above.
  *
  * We avoid unnecessary page cache invalidations for normal cached
diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c
new file mode 100644 (file)
index 0000000..f4d54ba
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * linux/fs/nfs/dns_resolve.c
+ *
+ * Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com>
+ *
+ * Resolves DNS hostnames into valid ip addresses
+ */
+
+#include <linux/hash.h>
+#include <linux/string.h>
+#include <linux/kmod.h>
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <linux/seq_file.h>
+#include <linux/inet.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/cache.h>
+#include <linux/sunrpc/svcauth.h>
+
+#include "dns_resolve.h"
+#include "cache_lib.h"
+
+#define NFS_DNS_HASHBITS 4
+#define NFS_DNS_HASHTBL_SIZE (1 << NFS_DNS_HASHBITS)
+
+static struct cache_head *nfs_dns_table[NFS_DNS_HASHTBL_SIZE];
+
+struct nfs_dns_ent {
+       struct cache_head h;
+
+       char *hostname;
+       size_t namelen;
+
+       struct sockaddr_storage addr;
+       size_t addrlen;
+};
+
+
+static void nfs_dns_ent_init(struct cache_head *cnew,
+               struct cache_head *ckey)
+{
+       struct nfs_dns_ent *new;
+       struct nfs_dns_ent *key;
+
+       new = container_of(cnew, struct nfs_dns_ent, h);
+       key = container_of(ckey, struct nfs_dns_ent, h);
+
+       kfree(new->hostname);
+       new->hostname = kstrndup(key->hostname, key->namelen, GFP_KERNEL);
+       if (new->hostname) {
+               new->namelen = key->namelen;
+               memcpy(&new->addr, &key->addr, key->addrlen);
+               new->addrlen = key->addrlen;
+       } else {
+               new->namelen = 0;
+               new->addrlen = 0;
+       }
+}
+
+static void nfs_dns_ent_put(struct kref *ref)
+{
+       struct nfs_dns_ent *item;
+
+       item = container_of(ref, struct nfs_dns_ent, h.ref);
+       kfree(item->hostname);
+       kfree(item);
+}
+
+static struct cache_head *nfs_dns_ent_alloc(void)
+{
+       struct nfs_dns_ent *item = kmalloc(sizeof(*item), GFP_KERNEL);
+
+       if (item != NULL) {
+               item->hostname = NULL;
+               item->namelen = 0;
+               item->addrlen = 0;
+               return &item->h;
+       }
+       return NULL;
+};
+
+static unsigned int nfs_dns_hash(const struct nfs_dns_ent *key)
+{
+       return hash_str(key->hostname, NFS_DNS_HASHBITS);
+}
+
+static void nfs_dns_request(struct cache_detail *cd,
+               struct cache_head *ch,
+               char **bpp, int *blen)
+{
+       struct nfs_dns_ent *key = container_of(ch, struct nfs_dns_ent, h);
+
+       qword_add(bpp, blen, key->hostname);
+       (*bpp)[-1] = '\n';
+}
+
+static int nfs_dns_upcall(struct cache_detail *cd,
+               struct cache_head *ch)
+{
+       struct nfs_dns_ent *key = container_of(ch, struct nfs_dns_ent, h);
+       int ret;
+
+       ret = nfs_cache_upcall(cd, key->hostname);
+       if (ret)
+               ret = sunrpc_cache_pipe_upcall(cd, ch, nfs_dns_request);
+       return ret;
+}
+
+static int nfs_dns_match(struct cache_head *ca,
+               struct cache_head *cb)
+{
+       struct nfs_dns_ent *a;
+       struct nfs_dns_ent *b;
+
+       a = container_of(ca, struct nfs_dns_ent, h);
+       b = container_of(cb, struct nfs_dns_ent, h);
+
+       if (a->namelen == 0 || a->namelen != b->namelen)
+               return 0;
+       return memcmp(a->hostname, b->hostname, a->namelen) == 0;
+}
+
+static int nfs_dns_show(struct seq_file *m, struct cache_detail *cd,
+               struct cache_head *h)
+{
+       struct nfs_dns_ent *item;
+       long ttl;
+
+       if (h == NULL) {
+               seq_puts(m, "# ip address      hostname        ttl\n");
+               return 0;
+       }
+       item = container_of(h, struct nfs_dns_ent, h);
+       ttl = (long)item->h.expiry_time - (long)get_seconds();
+       if (ttl < 0)
+               ttl = 0;
+
+       if (!test_bit(CACHE_NEGATIVE, &h->flags)) {
+               char buf[INET6_ADDRSTRLEN+IPV6_SCOPE_ID_LEN+1];
+
+               rpc_ntop((struct sockaddr *)&item->addr, buf, sizeof(buf));
+               seq_printf(m, "%15s ", buf);
+       } else
+               seq_puts(m, "<none>          ");
+       seq_printf(m, "%15s %ld\n", item->hostname, ttl);
+       return 0;
+}
+
+struct nfs_dns_ent *nfs_dns_lookup(struct cache_detail *cd,
+               struct nfs_dns_ent *key)
+{
+       struct cache_head *ch;
+
+       ch = sunrpc_cache_lookup(cd,
+                       &key->h,
+                       nfs_dns_hash(key));
+       if (!ch)
+               return NULL;
+       return container_of(ch, struct nfs_dns_ent, h);
+}
+
+struct nfs_dns_ent *nfs_dns_update(struct cache_detail *cd,
+               struct nfs_dns_ent *new,
+               struct nfs_dns_ent *key)
+{
+       struct cache_head *ch;
+
+       ch = sunrpc_cache_update(cd,
+                       &new->h, &key->h,
+                       nfs_dns_hash(key));
+       if (!ch)
+               return NULL;
+       return container_of(ch, struct nfs_dns_ent, h);
+}
+
+static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen)
+{
+       char buf1[NFS_DNS_HOSTNAME_MAXLEN+1];
+       struct nfs_dns_ent key, *item;
+       unsigned long ttl;
+       ssize_t len;
+       int ret = -EINVAL;
+
+       if (buf[buflen-1] != '\n')
+               goto out;
+       buf[buflen-1] = '\0';
+
+       len = qword_get(&buf, buf1, sizeof(buf1));
+       if (len <= 0)
+               goto out;
+       key.addrlen = rpc_pton(buf1, len,
+                       (struct sockaddr *)&key.addr,
+                       sizeof(key.addr));
+
+       len = qword_get(&buf, buf1, sizeof(buf1));
+       if (len <= 0)
+               goto out;
+
+       key.hostname = buf1;
+       key.namelen = len;
+       memset(&key.h, 0, sizeof(key.h));
+
+       ttl = get_expiry(&buf);
+       if (ttl == 0)
+               goto out;
+       key.h.expiry_time = ttl + get_seconds();
+
+       ret = -ENOMEM;
+       item = nfs_dns_lookup(cd, &key);
+       if (item == NULL)
+               goto out;
+
+       if (key.addrlen == 0)
+               set_bit(CACHE_NEGATIVE, &key.h.flags);
+
+       item = nfs_dns_update(cd, &key, item);
+       if (item == NULL)
+               goto out;
+
+       ret = 0;
+       cache_put(&item->h, cd);
+out:
+       return ret;
+}
+
+static struct cache_detail nfs_dns_resolve = {
+       .owner = THIS_MODULE,
+       .hash_size = NFS_DNS_HASHTBL_SIZE,
+       .hash_table = nfs_dns_table,
+       .name = "dns_resolve",
+       .cache_put = nfs_dns_ent_put,
+       .cache_upcall = nfs_dns_upcall,
+       .cache_parse = nfs_dns_parse,
+       .cache_show = nfs_dns_show,
+       .match = nfs_dns_match,
+       .init = nfs_dns_ent_init,
+       .update = nfs_dns_ent_init,
+       .alloc = nfs_dns_ent_alloc,
+};
+
+static int do_cache_lookup(struct cache_detail *cd,
+               struct nfs_dns_ent *key,
+               struct nfs_dns_ent **item,
+               struct nfs_cache_defer_req *dreq)
+{
+       int ret = -ENOMEM;
+
+       *item = nfs_dns_lookup(cd, key);
+       if (*item) {
+               ret = cache_check(cd, &(*item)->h, &dreq->req);
+               if (ret)
+                       *item = NULL;
+       }
+       return ret;
+}
+
+static int do_cache_lookup_nowait(struct cache_detail *cd,
+               struct nfs_dns_ent *key,
+               struct nfs_dns_ent **item)
+{
+       int ret = -ENOMEM;
+
+       *item = nfs_dns_lookup(cd, key);
+       if (!*item)
+               goto out_err;
+       ret = -ETIMEDOUT;
+       if (!test_bit(CACHE_VALID, &(*item)->h.flags)
+                       || (*item)->h.expiry_time < get_seconds()
+                       || cd->flush_time > (*item)->h.last_refresh)
+               goto out_put;
+       ret = -ENOENT;
+       if (test_bit(CACHE_NEGATIVE, &(*item)->h.flags))
+               goto out_put;
+       return 0;
+out_put:
+       cache_put(&(*item)->h, cd);
+out_err:
+       *item = NULL;
+       return ret;
+}
+
+static int do_cache_lookup_wait(struct cache_detail *cd,
+               struct nfs_dns_ent *key,
+               struct nfs_dns_ent **item)
+{
+       struct nfs_cache_defer_req *dreq;
+       int ret = -ENOMEM;
+
+       dreq = nfs_cache_defer_req_alloc();
+       if (!dreq)
+               goto out;
+       ret = do_cache_lookup(cd, key, item, dreq);
+       if (ret == -EAGAIN) {
+               ret = nfs_cache_wait_for_upcall(dreq);
+               if (!ret)
+                       ret = do_cache_lookup_nowait(cd, key, item);
+       }
+       nfs_cache_defer_req_put(dreq);
+out:
+       return ret;
+}
+
+ssize_t nfs_dns_resolve_name(char *name, size_t namelen,
+               struct sockaddr *sa, size_t salen)
+{
+       struct nfs_dns_ent key = {
+               .hostname = name,
+               .namelen = namelen,
+       };
+       struct nfs_dns_ent *item = NULL;
+       ssize_t ret;
+
+       ret = do_cache_lookup_wait(&nfs_dns_resolve, &key, &item);
+       if (ret == 0) {
+               if (salen >= item->addrlen) {
+                       memcpy(sa, &item->addr, item->addrlen);
+                       ret = item->addrlen;
+               } else
+                       ret = -EOVERFLOW;
+               cache_put(&item->h, &nfs_dns_resolve);
+       } else if (ret == -ENOENT)
+               ret = -ESRCH;
+       return ret;
+}
+
+int nfs_dns_resolver_init(void)
+{
+       return nfs_cache_register(&nfs_dns_resolve);
+}
+
+void nfs_dns_resolver_destroy(void)
+{
+       nfs_cache_unregister(&nfs_dns_resolve);
+}
+
diff --git a/fs/nfs/dns_resolve.h b/fs/nfs/dns_resolve.h
new file mode 100644 (file)
index 0000000..a3f0938
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Resolve DNS hostnames into valid ip addresses
+ */
+#ifndef __LINUX_FS_NFS_DNS_RESOLVE_H
+#define __LINUX_FS_NFS_DNS_RESOLVE_H
+
+#define NFS_DNS_HOSTNAME_MAXLEN        (128)
+
+extern int nfs_dns_resolver_init(void);
+extern void nfs_dns_resolver_destroy(void);
+extern ssize_t nfs_dns_resolve_name(char *name, size_t namelen,
+               struct sockaddr *sa, size_t salen);
+
+#endif
index 0506232..5021b75 100644 (file)
@@ -328,6 +328,42 @@ nfs_file_fsync(struct file *file, struct dentry *dentry, int datasync)
 }
 
 /*
+ * Decide whether a read/modify/write cycle may be more efficient
+ * then a modify/write/read cycle when writing to a page in the
+ * page cache.
+ *
+ * The modify/write/read cycle may occur if a page is read before
+ * being completely filled by the writer.  In this situation, the
+ * page must be completely written to stable storage on the server
+ * before it can be refilled by reading in the page from the server.
+ * This can lead to expensive, small, FILE_SYNC mode writes being
+ * done.
+ *
+ * It may be more efficient to read the page first if the file is
+ * open for reading in addition to writing, the page is not marked
+ * as Uptodate, it is not dirty or waiting to be committed,
+ * indicating that it was previously allocated and then modified,
+ * that there were valid bytes of data in that range of the file,
+ * and that the new data won't completely replace the old data in
+ * that range of the file.
+ */
+static int nfs_want_read_modify_write(struct file *file, struct page *page,
+                       loff_t pos, unsigned len)
+{
+       unsigned int pglen = nfs_page_length(page);
+       unsigned int offset = pos & (PAGE_CACHE_SIZE - 1);
+       unsigned int end = offset + len;
+
+       if ((file->f_mode & FMODE_READ) &&      /* open for read? */
+           !PageUptodate(page) &&              /* Uptodate? */
+           !PagePrivate(page) &&               /* i/o request already? */
+           pglen &&                            /* valid bytes of file? */
+           (end < pglen || offset))            /* replace all valid bytes? */
+               return 1;
+       return 0;
+}
+
+/*
  * This does the "real" work of the write. We must allocate and lock the
  * page to be sent back to the generic routine, which then copies the
  * data from user space.
@@ -340,15 +376,16 @@ static int nfs_write_begin(struct file *file, struct address_space *mapping,
                        struct page **pagep, void **fsdata)
 {
        int ret;
-       pgoff_t index;
+       pgoff_t index = pos >> PAGE_CACHE_SHIFT;
        struct page *page;
-       index = pos >> PAGE_CACHE_SHIFT;
+       int once_thru = 0;
 
        dfprintk(PAGECACHE, "NFS: write_begin(%s/%s(%ld), %u@%lld)\n",
                file->f_path.dentry->d_parent->d_name.name,
                file->f_path.dentry->d_name.name,
                mapping->host->i_ino, len, (long long) pos);
 
+start:
        /*
         * Prevent starvation issues if someone is doing a consistency
         * sync-to-disk
@@ -367,6 +404,13 @@ static int nfs_write_begin(struct file *file, struct address_space *mapping,
        if (ret) {
                unlock_page(page);
                page_cache_release(page);
+       } else if (!once_thru &&
+                  nfs_want_read_modify_write(file, page, pos, len)) {
+               once_thru = 1;
+               ret = nfs_readpage(file, page);
+               page_cache_release(page);
+               if (!ret)
+                       goto start;
        }
        return ret;
 }
@@ -479,6 +523,7 @@ const struct address_space_operations nfs_file_aops = {
        .invalidatepage = nfs_invalidate_page,
        .releasepage = nfs_release_page,
        .direct_IO = nfs_direct_IO,
+       .migratepage = nfs_migrate_page,
        .launder_page = nfs_launder_page,
 };
 
index 86147b0..21a84d4 100644 (file)
@@ -101,7 +101,7 @@ static void idmap_pipe_destroy_msg(struct rpc_pipe_msg *);
 
 static unsigned int fnvhash32(const void *, size_t);
 
-static struct rpc_pipe_ops idmap_upcall_ops = {
+static const struct rpc_pipe_ops idmap_upcall_ops = {
        .upcall         = idmap_pipe_upcall,
        .downcall       = idmap_pipe_downcall,
        .destroy_msg    = idmap_pipe_destroy_msg,
@@ -119,8 +119,8 @@ nfs_idmap_new(struct nfs_client *clp)
        if (idmap == NULL)
                return -ENOMEM;
 
-       idmap->idmap_dentry = rpc_mkpipe(clp->cl_rpcclient->cl_dentry, "idmap",
-                                        idmap, &idmap_upcall_ops, 0);
+       idmap->idmap_dentry = rpc_mkpipe(clp->cl_rpcclient->cl_path.dentry,
+                       "idmap", idmap, &idmap_upcall_ops, 0);
        if (IS_ERR(idmap->idmap_dentry)) {
                error = PTR_ERR(idmap->idmap_dentry);
                kfree(idmap);
index bd7938e..060022b 100644 (file)
@@ -46,6 +46,7 @@
 #include "iostat.h"
 #include "internal.h"
 #include "fscache.h"
+#include "dns_resolve.h"
 
 #define NFSDBG_FACILITY                NFSDBG_VFS
 
@@ -286,6 +287,11 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                /* We can't support update_atime(), since the server will reset it */
                inode->i_flags |= S_NOATIME|S_NOCMTIME;
                inode->i_mode = fattr->mode;
+               if ((fattr->valid & NFS_ATTR_FATTR_MODE) == 0
+                               && nfs_server_capable(inode, NFS_CAP_MODE))
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL;
                /* Why so? Because we want revalidate for devices/FIFOs, and
                 * that's precisely what we have in nfs_file_inode_operations.
                 */
@@ -330,20 +336,46 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                nfsi->attr_gencount = fattr->gencount;
                if (fattr->valid & NFS_ATTR_FATTR_ATIME)
                        inode->i_atime = fattr->atime;
+               else if (nfs_server_capable(inode, NFS_CAP_ATIME))
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (fattr->valid & NFS_ATTR_FATTR_MTIME)
                        inode->i_mtime = fattr->mtime;
+               else if (nfs_server_capable(inode, NFS_CAP_MTIME))
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_DATA;
                if (fattr->valid & NFS_ATTR_FATTR_CTIME)
                        inode->i_ctime = fattr->ctime;
+               else if (nfs_server_capable(inode, NFS_CAP_CTIME))
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL;
                if (fattr->valid & NFS_ATTR_FATTR_CHANGE)
                        nfsi->change_attr = fattr->change_attr;
+               else if (nfs_server_capable(inode, NFS_CAP_CHANGE_ATTR))
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_DATA;
                if (fattr->valid & NFS_ATTR_FATTR_SIZE)
                        inode->i_size = nfs_size_to_loff_t(fattr->size);
+               else
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_DATA
+                               | NFS_INO_REVAL_PAGECACHE;
                if (fattr->valid & NFS_ATTR_FATTR_NLINK)
                        inode->i_nlink = fattr->nlink;
+               else if (nfs_server_capable(inode, NFS_CAP_NLINK))
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (fattr->valid & NFS_ATTR_FATTR_OWNER)
                        inode->i_uid = fattr->uid;
+               else if (nfs_server_capable(inode, NFS_CAP_OWNER))
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL;
                if (fattr->valid & NFS_ATTR_FATTR_GROUP)
                        inode->i_gid = fattr->gid;
+               else if (nfs_server_capable(inode, NFS_CAP_OWNER_GROUP))
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL;
                if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED)
                        inode->i_blocks = fattr->du.nfs2.blocks;
                if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
@@ -1145,6 +1177,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
        loff_t cur_isize, new_isize;
        unsigned long invalid = 0;
        unsigned long now = jiffies;
+       unsigned long save_cache_validity;
 
        dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n",
                        __func__, inode->i_sb->s_id, inode->i_ino,
@@ -1171,10 +1204,11 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
         */
        nfsi->read_cache_jiffies = fattr->time_start;
 
-       if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) || (fattr->valid & (NFS_ATTR_FATTR_MTIME|NFS_ATTR_FATTR_CTIME)))
-           nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR
-                   | NFS_INO_INVALID_ATIME
-                   | NFS_INO_REVAL_PAGECACHE);
+       save_cache_validity = nfsi->cache_validity;
+       nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR
+                       | NFS_INO_INVALID_ATIME
+                       | NFS_INO_REVAL_FORCED
+                       | NFS_INO_REVAL_PAGECACHE);
 
        /* Do atomic weak cache consistency updates */
        nfs_wcc_update_inode(inode, fattr);
@@ -1189,7 +1223,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                                nfs_force_lookup_revalidate(inode);
                        nfsi->change_attr = fattr->change_attr;
                }
-       }
+       } else if (server->caps & NFS_CAP_CHANGE_ATTR)
+               invalid |= save_cache_validity;
 
        if (fattr->valid & NFS_ATTR_FATTR_MTIME) {
                /* NFSv2/v3: Check if the mtime agrees */
@@ -1201,7 +1236,12 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                                nfs_force_lookup_revalidate(inode);
                        memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
                }
-       }
+       } else if (server->caps & NFS_CAP_MTIME)
+               invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_DATA
+                               | NFS_INO_REVAL_PAGECACHE
+                               | NFS_INO_REVAL_FORCED);
+
        if (fattr->valid & NFS_ATTR_FATTR_CTIME) {
                /* If ctime has changed we should definitely clear access+acl caches */
                if (!timespec_equal(&inode->i_ctime, &fattr->ctime)) {
@@ -1215,7 +1255,11 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                        }
                        memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
                }
-       }
+       } else if (server->caps & NFS_CAP_CTIME)
+               invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL
+                               | NFS_INO_REVAL_FORCED);
 
        /* Check if our cached file size is stale */
        if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
@@ -1231,30 +1275,50 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                        dprintk("NFS: isize change on server for file %s/%ld\n",
                                        inode->i_sb->s_id, inode->i_ino);
                }
-       }
+       } else
+               invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+                               | NFS_INO_REVAL_PAGECACHE
+                               | NFS_INO_REVAL_FORCED);
 
 
        if (fattr->valid & NFS_ATTR_FATTR_ATIME)
                memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime));
+       else if (server->caps & NFS_CAP_ATIME)
+               invalid |= save_cache_validity & (NFS_INO_INVALID_ATIME
+                               | NFS_INO_REVAL_FORCED);
 
        if (fattr->valid & NFS_ATTR_FATTR_MODE) {
                if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)) {
                        invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
                        inode->i_mode = fattr->mode;
                }
-       }
+       } else if (server->caps & NFS_CAP_MODE)
+               invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL
+                               | NFS_INO_REVAL_FORCED);
+
        if (fattr->valid & NFS_ATTR_FATTR_OWNER) {
                if (inode->i_uid != fattr->uid) {
                        invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
                        inode->i_uid = fattr->uid;
                }
-       }
+       } else if (server->caps & NFS_CAP_OWNER)
+               invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL
+                               | NFS_INO_REVAL_FORCED);
+
        if (fattr->valid & NFS_ATTR_FATTR_GROUP) {
                if (inode->i_gid != fattr->gid) {
                        invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
                        inode->i_gid = fattr->gid;
                }
-       }
+       } else if (server->caps & NFS_CAP_OWNER_GROUP)
+               invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL
+                               | NFS_INO_REVAL_FORCED);
 
        if (fattr->valid & NFS_ATTR_FATTR_NLINK) {
                if (inode->i_nlink != fattr->nlink) {
@@ -1263,7 +1327,9 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                                invalid |= NFS_INO_INVALID_DATA;
                        inode->i_nlink = fattr->nlink;
                }
-       }
+       } else if (server->caps & NFS_CAP_NLINK)
+               invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+                               | NFS_INO_REVAL_FORCED);
 
        if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
                /*
@@ -1293,9 +1359,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                                || S_ISLNK(inode->i_mode)))
                invalid &= ~NFS_INO_INVALID_DATA;
        if (!nfs_have_delegation(inode, FMODE_READ) ||
-                       (nfsi->cache_validity & NFS_INO_REVAL_FORCED))
+                       (save_cache_validity & NFS_INO_REVAL_FORCED))
                nfsi->cache_validity |= invalid;
-       nfsi->cache_validity &= ~NFS_INO_REVAL_FORCED;
 
        return 0;
  out_changed:
@@ -1442,6 +1507,10 @@ static int __init init_nfs_fs(void)
 {
        int err;
 
+       err = nfs_dns_resolver_init();
+       if (err < 0)
+               goto out8;
+
        err = nfs_fscache_register();
        if (err < 0)
                goto out7;
@@ -1500,6 +1569,8 @@ out5:
 out6:
        nfs_fscache_unregister();
 out7:
+       nfs_dns_resolver_destroy();
+out8:
        return err;
 }
 
@@ -1511,6 +1582,7 @@ static void __exit exit_nfs_fs(void)
        nfs_destroy_inodecache();
        nfs_destroy_nfspagecache();
        nfs_fscache_unregister();
+       nfs_dns_resolver_destroy();
 #ifdef CONFIG_PROC_FS
        rpc_proc_unregister("nfs");
 #endif
index 7dd90a6..e21b1bb 100644 (file)
@@ -49,6 +49,11 @@ struct nfs_clone_mount {
 #define NFS_MAX_SECFLAVORS     (12)
 
 /*
+ * Value used if the user did not specify a port value.
+ */
+#define NFS_UNSPEC_PORT                (-1)
+
+/*
  * In-kernel mount arguments
  */
 struct nfs_parsed_mount_data {
@@ -63,6 +68,7 @@ struct nfs_parsed_mount_data {
        unsigned int            auth_flavor_len;
        rpc_authflavor_t        auth_flavors[1];
        char                    *client_address;
+       unsigned int            version;
        unsigned int            minorversion;
        char                    *fscache_uniq;
 
@@ -71,7 +77,7 @@ struct nfs_parsed_mount_data {
                size_t                  addrlen;
                char                    *hostname;
                u32                     version;
-               unsigned short          port;
+               int                     port;
                unsigned short          protocol;
        } mount_server;
 
@@ -80,7 +86,7 @@ struct nfs_parsed_mount_data {
                size_t                  addrlen;
                char                    *hostname;
                char                    *export_path;
-               unsigned short          port;
+               int                     port;
                unsigned short          protocol;
        } nfs_server;
 
@@ -102,6 +108,7 @@ struct nfs_mount_request {
 };
 
 extern int nfs_mount(struct nfs_mount_request *info);
+extern void nfs_umount(const struct nfs_mount_request *info);
 
 /* client.c */
 extern struct rpc_program nfs_program;
@@ -213,7 +220,6 @@ void nfs_zap_acl_cache(struct inode *inode);
 extern int nfs_wait_bit_killable(void *word);
 
 /* super.c */
-void nfs_parse_ip_address(char *, size_t, struct sockaddr *, size_t *);
 extern struct file_system_type nfs_xdev_fs_type;
 #ifdef CONFIG_NFS_V4
 extern struct file_system_type nfs4_xdev_fs_type;
@@ -248,6 +254,12 @@ extern void nfs_read_prepare(struct rpc_task *task, void *calldata);
 
 /* write.c */
 extern void nfs_write_prepare(struct rpc_task *task, void *calldata);
+#ifdef CONFIG_MIGRATION
+extern int nfs_migrate_page(struct address_space *,
+               struct page *, struct page *);
+#else
+#define nfs_migrate_page NULL
+#endif
 
 /* nfs4proc.c */
 extern int _nfs4_call_sync(struct nfs_server *server,
@@ -368,24 +380,3 @@ unsigned int nfs_page_array_len(unsigned int base, size_t len)
        return ((unsigned long)len + (unsigned long)base +
                PAGE_SIZE - 1) >> PAGE_SHIFT;
 }
-
-#define IPV6_SCOPE_DELIMITER   '%'
-
-/*
- * Set the port number in an address.  Be agnostic about the address
- * family.
- */
-static inline void nfs_set_port(struct sockaddr *sap, unsigned short port)
-{
-       struct sockaddr_in *ap = (struct sockaddr_in *)sap;
-       struct sockaddr_in6 *ap6 = (struct sockaddr_in6 *)sap;
-
-       switch (sap->sa_family) {
-       case AF_INET:
-               ap->sin_port = htons(port);
-               break;
-       case AF_INET6:
-               ap6->sin6_port = htons(port);
-               break;
-       }
-}
index 38ef9ea..0adefc4 100644 (file)
@@ -209,6 +209,71 @@ out_mnt_err:
        goto out;
 }
 
+/**
+ * nfs_umount - Notify a server that we have unmounted this export
+ * @info: pointer to umount request arguments
+ *
+ * MOUNTPROC_UMNT is advisory, so we set a short timeout, and always
+ * use UDP.
+ */
+void nfs_umount(const struct nfs_mount_request *info)
+{
+       static const struct rpc_timeout nfs_umnt_timeout = {
+               .to_initval = 1 * HZ,
+               .to_maxval = 3 * HZ,
+               .to_retries = 2,
+       };
+       struct rpc_create_args args = {
+               .protocol       = IPPROTO_UDP,
+               .address        = info->sap,
+               .addrsize       = info->salen,
+               .timeout        = &nfs_umnt_timeout,
+               .servername     = info->hostname,
+               .program        = &mnt_program,
+               .version        = info->version,
+               .authflavor     = RPC_AUTH_UNIX,
+               .flags          = RPC_CLNT_CREATE_NOPING,
+       };
+       struct mountres result;
+       struct rpc_message msg  = {
+               .rpc_argp       = info->dirpath,
+               .rpc_resp       = &result,
+       };
+       struct rpc_clnt *clnt;
+       int status;
+
+       if (info->noresvport)
+               args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
+
+       clnt = rpc_create(&args);
+       if (unlikely(IS_ERR(clnt)))
+               goto out_clnt_err;
+
+       dprintk("NFS: sending UMNT request for %s:%s\n",
+               (info->hostname ? info->hostname : "server"), info->dirpath);
+
+       if (info->version == NFS_MNT3_VERSION)
+               msg.rpc_proc = &clnt->cl_procinfo[MOUNTPROC3_UMNT];
+       else
+               msg.rpc_proc = &clnt->cl_procinfo[MOUNTPROC_UMNT];
+
+       status = rpc_call_sync(clnt, &msg, 0);
+       rpc_shutdown_client(clnt);
+
+       if (unlikely(status < 0))
+               goto out_call_err;
+
+       return;
+
+out_clnt_err:
+       dprintk("NFS: failed to create UMNT RPC client, status=%ld\n",
+                       PTR_ERR(clnt));
+       return;
+
+out_call_err:
+       dprintk("NFS: UMNT request failed, status=%d\n", status);
+}
+
 /*
  * XDR encode/decode functions for MOUNT
  */
@@ -258,7 +323,7 @@ static int decode_status(struct xdr_stream *xdr, struct mountres *res)
                return -EIO;
        status = ntohl(*p);
 
-       for (i = 0; i <= ARRAY_SIZE(mnt_errtbl); i++) {
+       for (i = 0; i < ARRAY_SIZE(mnt_errtbl); i++) {
                if (mnt_errtbl[i].status == status) {
                        res->errno = mnt_errtbl[i].errno;
                        return 0;
@@ -309,7 +374,7 @@ static int decode_fhs_status(struct xdr_stream *xdr, struct mountres *res)
                return -EIO;
        status = ntohl(*p);
 
-       for (i = 0; i <= ARRAY_SIZE(mnt3_errtbl); i++) {
+       for (i = 0; i < ARRAY_SIZE(mnt3_errtbl); i++) {
                if (mnt3_errtbl[i].status == status) {
                        res->errno = mnt3_errtbl[i].errno;
                        return 0;
@@ -407,6 +472,13 @@ static struct rpc_procinfo mnt_procedures[] = {
                .p_statidx      = MOUNTPROC_MNT,
                .p_name         = "MOUNT",
        },
+       [MOUNTPROC_UMNT] = {
+               .p_proc         = MOUNTPROC_UMNT,
+               .p_encode       = (kxdrproc_t)mnt_enc_dirpath,
+               .p_arglen       = MNT_enc_dirpath_sz,
+               .p_statidx      = MOUNTPROC_UMNT,
+               .p_name         = "UMOUNT",
+       },
 };
 
 static struct rpc_procinfo mnt3_procedures[] = {
@@ -419,6 +491,13 @@ static struct rpc_procinfo mnt3_procedures[] = {
                .p_statidx      = MOUNTPROC3_MNT,
                .p_name         = "MOUNT",
        },
+       [MOUNTPROC3_UMNT] = {
+               .p_proc         = MOUNTPROC3_UMNT,
+               .p_encode       = (kxdrproc_t)mnt_enc_dirpath,
+               .p_arglen       = MNT_enc_dirpath_sz,
+               .p_statidx      = MOUNTPROC3_UMNT,
+               .p_name         = "UMOUNT",
+       },
 };
 
 
index d0cc5ce..ee6a13f 100644 (file)
@@ -299,7 +299,6 @@ static void nfs3_free_createdata(struct nfs3_createdata *data)
 
 /*
  * Create a regular file.
- * For now, we don't implement O_EXCL.
  */
 static int
 nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
index 2a2a0a7..2636c26 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/inet.h>
 #include "internal.h"
 #include "nfs4_fs.h"
+#include "dns_resolve.h"
 
 #define NFSDBG_FACILITY                NFSDBG_VFS
 
@@ -95,6 +96,20 @@ static int nfs4_validate_fspath(const struct vfsmount *mnt_parent,
        return 0;
 }
 
+static size_t nfs_parse_server_name(char *string, size_t len,
+               struct sockaddr *sa, size_t salen)
+{
+       ssize_t ret;
+
+       ret = rpc_pton(string, len, sa, salen);
+       if (ret == 0) {
+               ret = nfs_dns_resolve_name(string, len, sa, salen);
+               if (ret < 0)
+                       ret = 0;
+       }
+       return ret;
+}
+
 static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
                                     char *page, char *page2,
                                     const struct nfs4_fs_location *location)
@@ -121,11 +136,12 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
 
                if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len))
                        continue;
-               nfs_parse_ip_address(buf->data, buf->len,
-                               mountdata->addr, &mountdata->addrlen);
-               if (mountdata->addr->sa_family == AF_UNSPEC)
+               mountdata->addrlen = nfs_parse_server_name(buf->data,
+                               buf->len,
+                               mountdata->addr, mountdata->addrlen);
+               if (mountdata->addrlen == 0)
                        continue;
-               nfs_set_port(mountdata->addr, NFS_PORT);
+               rpc_set_port(mountdata->addr, NFS_PORT);
 
                memcpy(page2, buf->data, buf->len);
                page2[buf->len] = '\0';
index 6917311..be6544a 100644 (file)
@@ -61,6 +61,8 @@
 #define NFS4_POLL_RETRY_MIN    (HZ/10)
 #define NFS4_POLL_RETRY_MAX    (15*HZ)
 
+#define NFS4_MAX_LOOP_ON_RECOVER (10)
+
 struct nfs4_opendata;
 static int _nfs4_proc_open(struct nfs4_opendata *data);
 static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
@@ -426,17 +428,19 @@ out:
 static int nfs4_recover_session(struct nfs4_session *session)
 {
        struct nfs_client *clp = session->clp;
+       unsigned int loop;
        int ret;
 
-       for (;;) {
+       for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) {
                ret = nfs4_wait_clnt_recover(clp);
                if (ret != 0)
-                               return ret;
+                       break;
                if (!test_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state))
                        break;
                nfs4_schedule_state_manager(clp);
+               ret = -EIO;
        }
-       return 0;
+       return ret;
 }
 
 static int nfs41_setup_sequence(struct nfs4_session *session,
@@ -1444,18 +1448,20 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
 static int nfs4_recover_expired_lease(struct nfs_server *server)
 {
        struct nfs_client *clp = server->nfs_client;
+       unsigned int loop;
        int ret;
 
-       for (;;) {
+       for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) {
                ret = nfs4_wait_clnt_recover(clp);
                if (ret != 0)
-                       return ret;
+                       break;
                if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) &&
                    !test_bit(NFS4CLNT_CHECK_LEASE,&clp->cl_state))
                        break;
                nfs4_schedule_state_recovery(clp);
+               ret = -EIO;
        }
-       return 0;
+       return ret;
 }
 
 /*
@@ -1997,12 +2003,34 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
        status = nfs4_call_sync(server, &msg, &args, &res, 0);
        if (status == 0) {
                memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask));
+               server->caps &= ~(NFS_CAP_ACLS|NFS_CAP_HARDLINKS|
+                               NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
+                               NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|
+                               NFS_CAP_OWNER_GROUP|NFS_CAP_ATIME|
+                               NFS_CAP_CTIME|NFS_CAP_MTIME);
                if (res.attr_bitmask[0] & FATTR4_WORD0_ACL)
                        server->caps |= NFS_CAP_ACLS;
                if (res.has_links != 0)
                        server->caps |= NFS_CAP_HARDLINKS;
                if (res.has_symlinks != 0)
                        server->caps |= NFS_CAP_SYMLINKS;
+               if (res.attr_bitmask[0] & FATTR4_WORD0_FILEID)
+                       server->caps |= NFS_CAP_FILEID;
+               if (res.attr_bitmask[1] & FATTR4_WORD1_MODE)
+                       server->caps |= NFS_CAP_MODE;
+               if (res.attr_bitmask[1] & FATTR4_WORD1_NUMLINKS)
+                       server->caps |= NFS_CAP_NLINK;
+               if (res.attr_bitmask[1] & FATTR4_WORD1_OWNER)
+                       server->caps |= NFS_CAP_OWNER;
+               if (res.attr_bitmask[1] & FATTR4_WORD1_OWNER_GROUP)
+                       server->caps |= NFS_CAP_OWNER_GROUP;
+               if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_ACCESS)
+                       server->caps |= NFS_CAP_ATIME;
+               if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_METADATA)
+                       server->caps |= NFS_CAP_CTIME;
+               if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_MODIFY)
+                       server->caps |= NFS_CAP_MTIME;
+
                memcpy(server->cache_consistency_bitmask, res.attr_bitmask, sizeof(server->cache_consistency_bitmask));
                server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE;
                server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY;
index 617273e..cfc30d3 100644 (file)
@@ -702,29 +702,12 @@ struct compound_hdr {
        u32             minorversion;
 };
 
-/*
- * START OF "GENERIC" ENCODE ROUTINES.
- *   These may look a little ugly since they are imported from a "generic"
- * set of XDR encode/decode routines which are intended to be shared by
- * all of our NFSv4 implementations (OpenBSD, MacOS X...).
- *
- * If the pain of reading these is too great, it should be a straightforward
- * task to translate them into Linux-specific versions which are more
- * consistent with the style used in NFSv2/v3...
- */
-#define WRITE32(n)               *p++ = htonl(n)
-#define WRITE64(n)               do {                          \
-       *p++ = htonl((uint32_t)((n) >> 32));                            \
-       *p++ = htonl((uint32_t)(n));                                    \
-} while (0)
-#define WRITEMEM(ptr,nbytes)     do {                          \
-       p = xdr_encode_opaque_fixed(p, ptr, nbytes);            \
-} while (0)
-
-#define RESERVE_SPACE(nbytes)  do {                            \
-       p = xdr_reserve_space(xdr, nbytes);                     \
-       BUG_ON(!p);                                             \
-} while (0)
+static __be32 *reserve_space(struct xdr_stream *xdr, size_t nbytes)
+{
+       __be32 *p = xdr_reserve_space(xdr, nbytes);
+       BUG_ON(!p);
+       return p;
+}
 
 static void encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
 {
@@ -749,12 +732,11 @@ static void encode_compound_hdr(struct xdr_stream *xdr,
 
        dprintk("encode_compound: tag=%.*s\n", (int)hdr->taglen, hdr->tag);
        BUG_ON(hdr->taglen > NFS4_MAXTAGLEN);
-       RESERVE_SPACE(12+(XDR_QUADLEN(hdr->taglen)<<2));
-       WRITE32(hdr->taglen);
-       WRITEMEM(hdr->tag, hdr->taglen);
-       WRITE32(hdr->minorversion);
+       p = reserve_space(xdr, 4 + hdr->taglen + 8);
+       p = xdr_encode_opaque(p, hdr->tag, hdr->taglen);
+       *p++ = cpu_to_be32(hdr->minorversion);
        hdr->nops_p = p;
-       WRITE32(hdr->nops);
+       *p = cpu_to_be32(hdr->nops);
 }
 
 static void encode_nops(struct compound_hdr *hdr)
@@ -829,55 +811,53 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
                len += 16;
        else if (iap->ia_valid & ATTR_MTIME)
                len += 4;
-       RESERVE_SPACE(len);
+       p = reserve_space(xdr, len);
 
        /*
         * We write the bitmap length now, but leave the bitmap and the attribute
         * buffer length to be backfilled at the end of this routine.
         */
-       WRITE32(2);
+       *p++ = cpu_to_be32(2);
        q = p;
        p += 3;
 
        if (iap->ia_valid & ATTR_SIZE) {
                bmval0 |= FATTR4_WORD0_SIZE;
-               WRITE64(iap->ia_size);
+               p = xdr_encode_hyper(p, iap->ia_size);
        }
        if (iap->ia_valid & ATTR_MODE) {
                bmval1 |= FATTR4_WORD1_MODE;
-               WRITE32(iap->ia_mode & S_IALLUGO);
+               *p++ = cpu_to_be32(iap->ia_mode & S_IALLUGO);
        }
        if (iap->ia_valid & ATTR_UID) {
                bmval1 |= FATTR4_WORD1_OWNER;
-               WRITE32(owner_namelen);
-               WRITEMEM(owner_name, owner_namelen);
+               p = xdr_encode_opaque(p, owner_name, owner_namelen);
        }
        if (iap->ia_valid & ATTR_GID) {
                bmval1 |= FATTR4_WORD1_OWNER_GROUP;
-               WRITE32(owner_grouplen);
-               WRITEMEM(owner_group, owner_grouplen);
+               p = xdr_encode_opaque(p, owner_group, owner_grouplen);
        }
        if (iap->ia_valid & ATTR_ATIME_SET) {
                bmval1 |= FATTR4_WORD1_TIME_ACCESS_SET;
-               WRITE32(NFS4_SET_TO_CLIENT_TIME);
-               WRITE32(0);
-               WRITE32(iap->ia_mtime.tv_sec);
-               WRITE32(iap->ia_mtime.tv_nsec);
+               *p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
+               *p++ = cpu_to_be32(0);
+               *p++ = cpu_to_be32(iap->ia_mtime.tv_sec);
+               *p++ = cpu_to_be32(iap->ia_mtime.tv_nsec);
        }
        else if (iap->ia_valid & ATTR_ATIME) {
                bmval1 |= FATTR4_WORD1_TIME_ACCESS_SET;
-               WRITE32(NFS4_SET_TO_SERVER_TIME);
+               *p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
        }
        if (iap->ia_valid & ATTR_MTIME_SET) {
                bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET;
-               WRITE32(NFS4_SET_TO_CLIENT_TIME);
-               WRITE32(0);
-               WRITE32(iap->ia_mtime.tv_sec);
-               WRITE32(iap->ia_mtime.tv_nsec);
+               *p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
+               *p++ = cpu_to_be32(0);
+               *p++ = cpu_to_be32(iap->ia_mtime.tv_sec);
+               *p++ = cpu_to_be32(iap->ia_mtime.tv_nsec);
        }
        else if (iap->ia_valid & ATTR_MTIME) {
                bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET;
-               WRITE32(NFS4_SET_TO_SERVER_TIME);
+               *p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
        }
 
        /*
@@ -891,7 +871,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
        len = (char *)p - (char *)q - 12;
        *q++ = htonl(bmval0);
        *q++ = htonl(bmval1);
-       *q++ = htonl(len);
+       *q = htonl(len);
 
 /* out: */
 }
@@ -900,9 +880,9 @@ static void encode_access(struct xdr_stream *xdr, u32 access, struct compound_hd
 {
        __be32 *p;
 
-       RESERVE_SPACE(8);
-       WRITE32(OP_ACCESS);
-       WRITE32(access);
+       p = reserve_space(xdr, 8);
+       *p++ = cpu_to_be32(OP_ACCESS);
+       *p = cpu_to_be32(access);
        hdr->nops++;
        hdr->replen += decode_access_maxsz;
 }
@@ -911,10 +891,10 @@ static void encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg
 {
        __be32 *p;
 
-       RESERVE_SPACE(8+NFS4_STATEID_SIZE);
-       WRITE32(OP_CLOSE);
-       WRITE32(arg->seqid->sequence->counter);
-       WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE);
+       p = reserve_space(xdr, 8+NFS4_STATEID_SIZE);
+       *p++ = cpu_to_be32(OP_CLOSE);
+       *p++ = cpu_to_be32(arg->seqid->sequence->counter);
+       xdr_encode_opaque_fixed(p, arg->stateid->data, NFS4_STATEID_SIZE);
        hdr->nops++;
        hdr->replen += decode_close_maxsz;
 }
@@ -923,10 +903,10 @@ static void encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *ar
 {
        __be32 *p;
 
-       RESERVE_SPACE(16);
-       WRITE32(OP_COMMIT);
-       WRITE64(args->offset);
-       WRITE32(args->count);
+       p = reserve_space(xdr, 16);
+       *p++ = cpu_to_be32(OP_COMMIT);
+       p = xdr_encode_hyper(p, args->offset);
+       *p = cpu_to_be32(args->count);
        hdr->nops++;
        hdr->replen += decode_commit_maxsz;
 }
@@ -935,30 +915,28 @@ static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *
 {
        __be32 *p;
 
-       RESERVE_SPACE(8);
-       WRITE32(OP_CREATE);
-       WRITE32(create->ftype);
+       p = reserve_space(xdr, 8);
+       *p++ = cpu_to_be32(OP_CREATE);
+       *p = cpu_to_be32(create->ftype);
 
        switch (create->ftype) {
        case NF4LNK:
-               RESERVE_SPACE(4);
-               WRITE32(create->u.symlink.len);
+               p = reserve_space(xdr, 4);
+               *p = cpu_to_be32(create->u.symlink.len);
                xdr_write_pages(xdr, create->u.symlink.pages, 0, create->u.symlink.len);
                break;
 
        case NF4BLK: case NF4CHR:
-               RESERVE_SPACE(8);
-               WRITE32(create->u.device.specdata1);
-               WRITE32(create->u.device.specdata2);
+               p = reserve_space(xdr, 8);
+               *p++ = cpu_to_be32(create->u.device.specdata1);
+               *p = cpu_to_be32(create->u.device.specdata2);
                break;
 
        default:
                break;
        }
 
-       RESERVE_SPACE(4 + create->name->len);
-       WRITE32(create->name->len);
-       WRITEMEM(create->name->name, create->name->len);
+       encode_string(xdr, create->name->len, create->name->name);
        hdr->nops++;
        hdr->replen += decode_create_maxsz;
 
@@ -969,10 +947,10 @@ static void encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap, struct c
 {
        __be32 *p;
 
-       RESERVE_SPACE(12);
-       WRITE32(OP_GETATTR);
-       WRITE32(1);
-       WRITE32(bitmap);
+       p = reserve_space(xdr, 12);
+       *p++ = cpu_to_be32(OP_GETATTR);
+       *p++ = cpu_to_be32(1);
+       *p = cpu_to_be32(bitmap);
        hdr->nops++;
        hdr->replen += decode_getattr_maxsz;
 }
@@ -981,11 +959,11 @@ static void encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm
 {
        __be32 *p;
 
-       RESERVE_SPACE(16);
-       WRITE32(OP_GETATTR);
-       WRITE32(2);
-       WRITE32(bm0);
-       WRITE32(bm1);
+       p = reserve_space(xdr, 16);
+       *p++ = cpu_to_be32(OP_GETATTR);
+       *p++ = cpu_to_be32(2);
+       *p++ = cpu_to_be32(bm0);
+       *p = cpu_to_be32(bm1);
        hdr->nops++;
        hdr->replen += decode_getattr_maxsz;
 }
@@ -1012,8 +990,8 @@ static void encode_getfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
 {
        __be32 *p;
 
-       RESERVE_SPACE(4);
-       WRITE32(OP_GETFH);
+       p = reserve_space(xdr, 4);
+       *p = cpu_to_be32(OP_GETFH);
        hdr->nops++;
        hdr->replen += decode_getfh_maxsz;
 }
@@ -1022,10 +1000,9 @@ static void encode_link(struct xdr_stream *xdr, const struct qstr *name, struct
 {
        __be32 *p;
 
-       RESERVE_SPACE(8 + name->len);
-       WRITE32(OP_LINK);
-       WRITE32(name->len);
-       WRITEMEM(name->name, name->len);
+       p = reserve_space(xdr, 8 + name->len);
+       *p++ = cpu_to_be32(OP_LINK);
+       xdr_encode_opaque(p, name->name, name->len);
        hdr->nops++;
        hdr->replen += decode_link_maxsz;
 }
@@ -1052,27 +1029,27 @@ static void encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args
 {
        __be32 *p;
 
-       RESERVE_SPACE(32);
-       WRITE32(OP_LOCK);
-       WRITE32(nfs4_lock_type(args->fl, args->block));
-       WRITE32(args->reclaim);
-       WRITE64(args->fl->fl_start);
-       WRITE64(nfs4_lock_length(args->fl));
-       WRITE32(args->new_lock_owner);
+       p = reserve_space(xdr, 32);
+       *p++ = cpu_to_be32(OP_LOCK);
+       *p++ = cpu_to_be32(nfs4_lock_type(args->fl, args->block));
+       *p++ = cpu_to_be32(args->reclaim);
+       p = xdr_encode_hyper(p, args->fl->fl_start);
+       p = xdr_encode_hyper(p, nfs4_lock_length(args->fl));
+       *p = cpu_to_be32(args->new_lock_owner);
        if (args->new_lock_owner){
-               RESERVE_SPACE(4+NFS4_STATEID_SIZE+32);
-               WRITE32(args->open_seqid->sequence->counter);
-               WRITEMEM(args->open_stateid->data, NFS4_STATEID_SIZE);
-               WRITE32(args->lock_seqid->sequence->counter);
-               WRITE64(args->lock_owner.clientid);
-               WRITE32(16);
-               WRITEMEM("lock id:", 8);
-               WRITE64(args->lock_owner.id);
+               p = reserve_space(xdr, 4+NFS4_STATEID_SIZE+32);
+               *p++ = cpu_to_be32(args->open_seqid->sequence->counter);
+               p = xdr_encode_opaque_fixed(p, args->open_stateid->data, NFS4_STATEID_SIZE);
+               *p++ = cpu_to_be32(args->lock_seqid->sequence->counter);
+               p = xdr_encode_hyper(p, args->lock_owner.clientid);
+               *p++ = cpu_to_be32(16);
+               p = xdr_encode_opaque_fixed(p, "lock id:", 8);
+               xdr_encode_hyper(p, args->lock_owner.id);
        }
        else {
-               RESERVE_SPACE(NFS4_STATEID_SIZE+4);
-               WRITEMEM(args->lock_stateid->data, NFS4_STATEID_SIZE);
-               WRITE32(args->lock_seqid->sequence->counter);
+               p = reserve_space(xdr, NFS4_STATEID_SIZE+4);
+               p = xdr_encode_opaque_fixed(p, args->lock_stateid->data, NFS4_STATEID_SIZE);
+               *p = cpu_to_be32(args->lock_seqid->sequence->counter);
        }
        hdr->nops++;
        hdr->replen += decode_lock_maxsz;
@@ -1082,15 +1059,15 @@ static void encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *ar
 {
        __be32 *p;
 
-       RESERVE_SPACE(52);
-       WRITE32(OP_LOCKT);
-       WRITE32(nfs4_lock_type(args->fl, 0));
-       WRITE64(args->fl->fl_start);
-       WRITE64(nfs4_lock_length(args->fl));
-       WRITE64(args->lock_owner.clientid);
-       WRITE32(16);
-       WRITEMEM("lock id:", 8);
-       WRITE64(args->lock_owner.id);
+       p = reserve_space(xdr, 52);
+       *p++ = cpu_to_be32(OP_LOCKT);
+       *p++ = cpu_to_be32(nfs4_lock_type(args->fl, 0));
+       p = xdr_encode_hyper(p, args->fl->fl_start);
+       p = xdr_encode_hyper(p, nfs4_lock_length(args->fl));
+       p = xdr_encode_hyper(p, args->lock_owner.clientid);
+       *p++ = cpu_to_be32(16);
+       p = xdr_encode_opaque_fixed(p, "lock id:", 8);
+       xdr_encode_hyper(p, args->lock_owner.id);
        hdr->nops++;
        hdr->replen += decode_lockt_maxsz;
 }
@@ -1099,13 +1076,13 @@ static void encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *ar
 {
        __be32 *p;
 
-       RESERVE_SPACE(12+NFS4_STATEID_SIZE+16);
-       WRITE32(OP_LOCKU);
-       WRITE32(nfs4_lock_type(args->fl, 0));
-       WRITE32(args->seqid->sequence->counter);
-       WRITEMEM(args->stateid->data, NFS4_STATEID_SIZE);
-       WRITE64(args->fl->fl_start);
-       WRITE64(nfs4_lock_length(args->fl));
+       p = reserve_space(xdr, 12+NFS4_STATEID_SIZE+16);
+       *p++ = cpu_to_be32(OP_LOCKU);
+       *p++ = cpu_to_be32(nfs4_lock_type(args->fl, 0));
+       *p++ = cpu_to_be32(args->seqid->sequence->counter);
+       p = xdr_encode_opaque_fixed(p, args->stateid->data, NFS4_STATEID_SIZE);
+       p = xdr_encode_hyper(p, args->fl->fl_start);
+       xdr_encode_hyper(p, nfs4_lock_length(args->fl));
        hdr->nops++;
        hdr->replen += decode_locku_maxsz;
 }
@@ -1115,10 +1092,9 @@ static void encode_lookup(struct xdr_stream *xdr, const struct qstr *name, struc
        int len = name->len;
        __be32 *p;
 
-       RESERVE_SPACE(8 + len);
-       WRITE32(OP_LOOKUP);
-       WRITE32(len);
-       WRITEMEM(name->name, len);
+       p = reserve_space(xdr, 8 + len);
+       *p++ = cpu_to_be32(OP_LOOKUP);
+       xdr_encode_opaque(p, name->name, len);
        hdr->nops++;
        hdr->replen += decode_lookup_maxsz;
 }
@@ -1127,21 +1103,21 @@ static void encode_share_access(struct xdr_stream *xdr, fmode_t fmode)
 {
        __be32 *p;
 
-       RESERVE_SPACE(8);
+       p = reserve_space(xdr, 8);
        switch (fmode & (FMODE_READ|FMODE_WRITE)) {
        case FMODE_READ:
-               WRITE32(NFS4_SHARE_ACCESS_READ);
+               *p++ = cpu_to_be32(NFS4_SHARE_ACCESS_READ);
                break;
        case FMODE_WRITE:
-               WRITE32(NFS4_SHARE_ACCESS_WRITE);
+               *p++ = cpu_to_be32(NFS4_SHARE_ACCESS_WRITE);
                break;
        case FMODE_READ|FMODE_WRITE:
-               WRITE32(NFS4_SHARE_ACCESS_BOTH);
+               *p++ = cpu_to_be32(NFS4_SHARE_ACCESS_BOTH);
                break;
        default:
-               WRITE32(0);
+               *p++ = cpu_to_be32(0);
        }
-       WRITE32(0);             /* for linux, share_deny = 0 always */
+       *p = cpu_to_be32(0);            /* for linux, share_deny = 0 always */
 }
 
 static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_openargs *arg)
@@ -1151,29 +1127,29 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena
  * opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4,
  * owner 4 = 32
  */
-       RESERVE_SPACE(8);
-       WRITE32(OP_OPEN);
-       WRITE32(arg->seqid->sequence->counter);
+       p = reserve_space(xdr, 8);
+       *p++ = cpu_to_be32(OP_OPEN);
+       *p = cpu_to_be32(arg->seqid->sequence->counter);
        encode_share_access(xdr, arg->fmode);
-       RESERVE_SPACE(28);
-       WRITE64(arg->clientid);
-       WRITE32(16);
-       WRITEMEM("open id:", 8);
-       WRITE64(arg->id);
+       p = reserve_space(xdr, 28);
+       p = xdr_encode_hyper(p, arg->clientid);
+       *p++ = cpu_to_be32(16);
+       p = xdr_encode_opaque_fixed(p, "open id:", 8);
+       xdr_encode_hyper(p, arg->id);
 }
 
 static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_openargs *arg)
 {
        __be32 *p;
 
-       RESERVE_SPACE(4);
+       p = reserve_space(xdr, 4);
        switch(arg->open_flags & O_EXCL) {
        case 0:
-               WRITE32(NFS4_CREATE_UNCHECKED);
+               *p = cpu_to_be32(NFS4_CREATE_UNCHECKED);
                encode_attrs(xdr, arg->u.attrs, arg->server);
                break;
        default:
-               WRITE32(NFS4_CREATE_EXCLUSIVE);
+               *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE);
                encode_nfs4_verifier(xdr, &arg->u.verifier);
        }
 }
@@ -1182,14 +1158,14 @@ static void encode_opentype(struct xdr_stream *xdr, const struct nfs_openargs *a
 {
        __be32 *p;
 
-       RESERVE_SPACE(4);
+       p = reserve_space(xdr, 4);
        switch (arg->open_flags & O_CREAT) {
        case 0:
-               WRITE32(NFS4_OPEN_NOCREATE);
+               *p = cpu_to_be32(NFS4_OPEN_NOCREATE);
                break;
        default:
                BUG_ON(arg->claim != NFS4_OPEN_CLAIM_NULL);
-               WRITE32(NFS4_OPEN_CREATE);
+               *p = cpu_to_be32(NFS4_OPEN_CREATE);
                encode_createmode(xdr, arg);
        }
 }
@@ -1198,16 +1174,16 @@ static inline void encode_delegation_type(struct xdr_stream *xdr, fmode_t delega
 {
        __be32 *p;
 
-       RESERVE_SPACE(4);
+       p = reserve_space(xdr, 4);
        switch (delegation_type) {
        case 0:
-               WRITE32(NFS4_OPEN_DELEGATE_NONE);
+               *p = cpu_to_be32(NFS4_OPEN_DELEGATE_NONE);
                break;
        case FMODE_READ:
-               WRITE32(NFS4_OPEN_DELEGATE_READ);
+               *p = cpu_to_be32(NFS4_OPEN_DELEGATE_READ);
                break;
        case FMODE_WRITE|FMODE_READ:
-               WRITE32(NFS4_OPEN_DELEGATE_WRITE);
+               *p = cpu_to_be32(NFS4_OPEN_DELEGATE_WRITE);
                break;
        default:
                BUG();
@@ -1218,8 +1194,8 @@ static inline void encode_claim_null(struct xdr_stream *xdr, const struct qstr *
 {
        __be32 *p;
 
-       RESERVE_SPACE(4);
-       WRITE32(NFS4_OPEN_CLAIM_NULL);
+       p = reserve_space(xdr, 4);
+       *p = cpu_to_be32(NFS4_OPEN_CLAIM_NULL);
        encode_string(xdr, name->len, name->name);
 }
 
@@ -1227,8 +1203,8 @@ static inline void encode_claim_previous(struct xdr_stream *xdr, fmode_t type)
 {
        __be32 *p;
 
-       RESERVE_SPACE(4);
-       WRITE32(NFS4_OPEN_CLAIM_PREVIOUS);
+       p = reserve_space(xdr, 4);
+       *p = cpu_to_be32(NFS4_OPEN_CLAIM_PREVIOUS);
        encode_delegation_type(xdr, type);
 }
 
@@ -1236,9 +1212,9 @@ static inline void encode_claim_delegate_cur(struct xdr_stream *xdr, const struc
 {
        __be32 *p;
 
-       RESERVE_SPACE(4+NFS4_STATEID_SIZE);
-       WRITE32(NFS4_OPEN_CLAIM_DELEGATE_CUR);
-       WRITEMEM(stateid->data, NFS4_STATEID_SIZE);
+       p = reserve_space(xdr, 4+NFS4_STATEID_SIZE);
+       *p++ = cpu_to_be32(NFS4_OPEN_CLAIM_DELEGATE_CUR);
+       xdr_encode_opaque_fixed(p, stateid->data, NFS4_STATEID_SIZE);
        encode_string(xdr, name->len, name->name);
 }
 
@@ -1267,10 +1243,10 @@ static void encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_co
 {
        __be32 *p;
 
-       RESERVE_SPACE(4+NFS4_STATEID_SIZE+4);
-       WRITE32(OP_OPEN_CONFIRM);
-       WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE);
-       WRITE32(arg->seqid->sequence->counter);
+       p = reserve_space(xdr, 4+NFS4_STATEID_SIZE+4);
+       *p++ = cpu_to_be32(OP_OPEN_CONFIRM);
+       p = xdr_encode_opaque_fixed(p, arg->stateid->data, NFS4_STATEID_SIZE);
+       *p = cpu_to_be32(arg->seqid->sequence->counter);
        hdr->nops++;
        hdr->replen += decode_open_confirm_maxsz;
 }
@@ -1279,10 +1255,10 @@ static void encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_close
 {
        __be32 *p;
 
-       RESERVE_SPACE(4+NFS4_STATEID_SIZE+4);
-       WRITE32(OP_OPEN_DOWNGRADE);
-       WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE);
-       WRITE32(arg->seqid->sequence->counter);
+       p = reserve_space(xdr, 4+NFS4_STATEID_SIZE+4);
+       *p++ = cpu_to_be32(OP_OPEN_DOWNGRADE);
+       p = xdr_encode_opaque_fixed(p, arg->stateid->data, NFS4_STATEID_SIZE);
+       *p = cpu_to_be32(arg->seqid->sequence->counter);
        encode_share_access(xdr, arg->fmode);
        hdr->nops++;
        hdr->replen += decode_open_downgrade_maxsz;
@@ -1294,10 +1270,9 @@ encode_putfh(struct xdr_stream *xdr, const struct nfs_fh *fh, struct compound_hd
        int len = fh->size;
        __be32 *p;
 
-       RESERVE_SPACE(8 + len);
-       WRITE32(OP_PUTFH);
-       WRITE32(len);
-       WRITEMEM(fh->data, len);
+       p = reserve_space(xdr, 8 + len);
+       *p++ = cpu_to_be32(OP_PUTFH);
+       xdr_encode_opaque(p, fh->data, len);
        hdr->nops++;
        hdr->replen += decode_putfh_maxsz;
 }
@@ -1306,8 +1281,8 @@ static void encode_putrootfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
 {
        __be32 *p;
 
-       RESERVE_SPACE(4);
-       WRITE32(OP_PUTROOTFH);
+       p = reserve_space(xdr, 4);
+       *p = cpu_to_be32(OP_PUTROOTFH);
        hdr->nops++;
        hdr->replen += decode_putrootfh_maxsz;
 }
@@ -1317,26 +1292,26 @@ static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context
        nfs4_stateid stateid;
        __be32 *p;
 
-       RESERVE_SPACE(NFS4_STATEID_SIZE);
+       p = reserve_space(xdr, NFS4_STATEID_SIZE);
        if (ctx->state != NULL) {
                nfs4_copy_stateid(&stateid, ctx->state, ctx->lockowner);
-               WRITEMEM(stateid.data, NFS4_STATEID_SIZE);
+               xdr_encode_opaque_fixed(p, stateid.data, NFS4_STATEID_SIZE);
        } else
-               WRITEMEM(zero_stateid.data, NFS4_STATEID_SIZE);
+               xdr_encode_opaque_fixed(p, zero_stateid.data, NFS4_STATEID_SIZE);
 }
 
 static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args, struct compound_hdr *hdr)
 {
        __be32 *p;
 
-       RESERVE_SPACE(4);
-       WRITE32(OP_READ);
+       p = reserve_space(xdr, 4);
+       *p = cpu_to_be32(OP_READ);
 
        encode_stateid(xdr, args->context);
 
-       RESERVE_SPACE(12);
-       WRITE64(args->offset);
-       WRITE32(args->count);
+       p = reserve_space(xdr, 12);
+       p = xdr_encode_hyper(p, args->offset);
+       *p = cpu_to_be32(args->count);
        hdr->nops++;
        hdr->replen += decode_read_maxsz;
 }
@@ -1349,20 +1324,20 @@ static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
        };
        __be32 *p;
 
-       RESERVE_SPACE(12+NFS4_VERIFIER_SIZE+20);
-       WRITE32(OP_READDIR);
-       WRITE64(readdir->cookie);
-       WRITEMEM(readdir->verifier.data, NFS4_VERIFIER_SIZE);
-       WRITE32(readdir->count >> 1);  /* We're not doing readdirplus */
-       WRITE32(readdir->count);
-       WRITE32(2);
+       p = reserve_space(xdr, 12+NFS4_VERIFIER_SIZE+20);
+       *p++ = cpu_to_be32(OP_READDIR);
+       p = xdr_encode_hyper(p, readdir->cookie);
+       p = xdr_encode_opaque_fixed(p, readdir->verifier.data, NFS4_VERIFIER_SIZE);
+       *p++ = cpu_to_be32(readdir->count >> 1);  /* We're not doing readdirplus */
+       *p++ = cpu_to_be32(readdir->count);
+       *p++ = cpu_to_be32(2);
        /* Switch to mounted_on_fileid if the server supports it */
        if (readdir->bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)
                attrs[0] &= ~FATTR4_WORD0_FILEID;
        else
                attrs[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;
-       WRITE32(attrs[0] & readdir->bitmask[0]);
-       WRITE32(attrs[1] & readdir->bitmask[1]);
+       *p++ = cpu_to_be32(attrs[0] & readdir->bitmask[0]);
+       *p = cpu_to_be32(attrs[1] & readdir->bitmask[1]);
        hdr->nops++;
        hdr->replen += decode_readdir_maxsz;
        dprintk("%s: cookie = %Lu, verifier = %08x:%08x, bitmap = %08x:%08x\n",
@@ -1378,8 +1353,8 @@ static void encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *
 {
        __be32 *p;
 
-       RESERVE_SPACE(4);
-       WRITE32(OP_READLINK);
+       p = reserve_space(xdr, 4);
+       *p = cpu_to_be32(OP_READLINK);
        hdr->nops++;
        hdr->replen += decode_readlink_maxsz;
 }
@@ -1388,10 +1363,9 @@ static void encode_remove(struct xdr_stream *xdr, const struct qstr *name, struc
 {
        __be32 *p;
 
-       RESERVE_SPACE(8 + name->len);
-       WRITE32(OP_REMOVE);
-       WRITE32(name->len);
-       WRITEMEM(name->name, name->len);
+       p = reserve_space(xdr, 8 + name->len);
+       *p++ = cpu_to_be32(OP_REMOVE);
+       xdr_encode_opaque(p, name->name, name->len);
        hdr->nops++;
        hdr->replen += decode_remove_maxsz;
 }
@@ -1400,14 +1374,10 @@ static void encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, co
 {
        __be32 *p;
 
-       RESERVE_SPACE(8 + oldname->len);
-       WRITE32(OP_RENAME);
-       WRITE32(oldname->len);
-       WRITEMEM(oldname->name, oldname->len);
-
-       RESERVE_SPACE(4 + newname->len);
-       WRITE32(newname->len);
-       WRITEMEM(newname->name, newname->len);
+       p = reserve_space(xdr, 4);
+       *p = cpu_to_be32(OP_RENAME);
+       encode_string(xdr, oldname->len, oldname->name);
+       encode_string(xdr, newname->len, newname->name);
        hdr->nops++;
        hdr->replen += decode_rename_maxsz;
 }
@@ -1416,9 +1386,9 @@ static void encode_renew(struct xdr_stream *xdr, const struct nfs_client *client
 {
        __be32 *p;
 
-       RESERVE_SPACE(12);
-       WRITE32(OP_RENEW);
-       WRITE64(client_stateid->cl_clientid);
+       p = reserve_space(xdr, 12);
+       *p++ = cpu_to_be32(OP_RENEW);
+       xdr_encode_hyper(p, client_stateid->cl_clientid);
        hdr->nops++;
        hdr->replen += decode_renew_maxsz;
 }
@@ -1428,8 +1398,8 @@ encode_restorefh(struct xdr_stream *xdr, struct compound_hdr *hdr)
 {
        __be32 *p;
 
-       RESERVE_SPACE(4);
-       WRITE32(OP_RESTOREFH);
+       p = reserve_space(xdr, 4);
+       *p = cpu_to_be32(OP_RESTOREFH);
        hdr->nops++;
        hdr->replen += decode_restorefh_maxsz;
 }
@@ -1439,16 +1409,16 @@ encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compoun
 {
        __be32 *p;
 
-       RESERVE_SPACE(4+NFS4_STATEID_SIZE);
-       WRITE32(OP_SETATTR);
-       WRITEMEM(zero_stateid.data, NFS4_STATEID_SIZE);
-       RESERVE_SPACE(2*4);
-       WRITE32(1);
-       WRITE32(FATTR4_WORD0_ACL);
+       p = reserve_space(xdr, 4+NFS4_STATEID_SIZE);
+       *p++ = cpu_to_be32(OP_SETATTR);
+       xdr_encode_opaque_fixed(p, zero_stateid.data, NFS4_STATEID_SIZE);
+       p = reserve_space(xdr, 2*4);
+       *p++ = cpu_to_be32(1);
+       *p = cpu_to_be32(FATTR4_WORD0_ACL);
        if (arg->acl_len % 4)
                return -EINVAL;
-       RESERVE_SPACE(4);
-       WRITE32(arg->acl_len);
+       p = reserve_space(xdr, 4);
+       *p = cpu_to_be32(arg->acl_len);
        xdr_write_pages(xdr, arg->acl_pages, arg->acl_pgbase, arg->acl_len);
        hdr->nops++;
        hdr->replen += decode_setacl_maxsz;
@@ -1460,8 +1430,8 @@ encode_savefh(struct xdr_stream *xdr, struct compound_hdr *hdr)
 {
        __be32 *p;
 
-       RESERVE_SPACE(4);
-       WRITE32(OP_SAVEFH);
+       p = reserve_space(xdr, 4);
+       *p = cpu_to_be32(OP_SAVEFH);
        hdr->nops++;
        hdr->replen += decode_savefh_maxsz;
 }
@@ -1470,9 +1440,9 @@ static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs
 {
        __be32 *p;
 
-       RESERVE_SPACE(4+NFS4_STATEID_SIZE);
-       WRITE32(OP_SETATTR);
-       WRITEMEM(arg->stateid.data, NFS4_STATEID_SIZE);
+       p = reserve_space(xdr, 4+NFS4_STATEID_SIZE);
+       *p++ = cpu_to_be32(OP_SETATTR);
+       xdr_encode_opaque_fixed(p, arg->stateid.data, NFS4_STATEID_SIZE);
        hdr->nops++;
        hdr->replen += decode_setattr_maxsz;
        encode_attrs(xdr, arg->iap, server);
@@ -1482,17 +1452,17 @@ static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclie
 {
        __be32 *p;
 
-       RESERVE_SPACE(4 + NFS4_VERIFIER_SIZE);
-       WRITE32(OP_SETCLIENTID);
-       WRITEMEM(setclientid->sc_verifier->data, NFS4_VERIFIER_SIZE);
+       p = reserve_space(xdr, 4 + NFS4_VERIFIER_SIZE);
+       *p++ = cpu_to_be32(OP_SETCLIENTID);
+       xdr_encode_opaque_fixed(p, setclientid->sc_verifier->data, NFS4_VERIFIER_SIZE);
 
        encode_string(xdr, setclientid->sc_name_len, setclientid->sc_name);
-       RESERVE_SPACE(4);
-       WRITE32(setclientid->sc_prog);
+       p = reserve_space(xdr, 4);
+       *p = cpu_to_be32(setclientid->sc_prog);
        encode_string(xdr, setclientid->sc_netid_len, setclientid->sc_netid);
        encode_string(xdr, setclientid->sc_uaddr_len, setclientid->sc_uaddr);
-       RESERVE_SPACE(4);
-       WRITE32(setclientid->sc_cb_ident);
+       p = reserve_space(xdr, 4);
+       *p = cpu_to_be32(setclientid->sc_cb_ident);
        hdr->nops++;
        hdr->replen += decode_setclientid_maxsz;
 }
@@ -1501,10 +1471,10 @@ static void encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs_
 {
        __be32 *p;
 
-       RESERVE_SPACE(12 + NFS4_VERIFIER_SIZE);
-       WRITE32(OP_SETCLIENTID_CONFIRM);
-       WRITE64(client_state->cl_clientid);
-       WRITEMEM(client_state->cl_confirm.data, NFS4_VERIFIER_SIZE);
+       p = reserve_space(xdr, 12 + NFS4_VERIFIER_SIZE);
+       *p++ = cpu_to_be32(OP_SETCLIENTID_CONFIRM);
+       p = xdr_encode_hyper(p, client_state->cl_clientid);
+       xdr_encode_opaque_fixed(p, client_state->cl_confirm.data, NFS4_VERIFIER_SIZE);
        hdr->nops++;
        hdr->replen += decode_setclientid_confirm_maxsz;
 }
@@ -1513,15 +1483,15 @@ static void encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *arg
 {
        __be32 *p;
 
-       RESERVE_SPACE(4);
-       WRITE32(OP_WRITE);
+       p = reserve_space(xdr, 4);
+       *p = cpu_to_be32(OP_WRITE);
 
        encode_stateid(xdr, args->context);
 
-       RESERVE_SPACE(16);
-       WRITE64(args->offset);
-       WRITE32(args->stable);
-       WRITE32(args->count);
+       p = reserve_space(xdr, 16);
+       p = xdr_encode_hyper(p, args->offset);
+       *p++ = cpu_to_be32(args->stable);
+       *p = cpu_to_be32(args->count);
 
        xdr_write_pages(xdr, args->pages, args->pgbase, args->count);
        hdr->nops++;
@@ -1532,10 +1502,10 @@ static void encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *state
 {
        __be32 *p;
 
-       RESERVE_SPACE(4+NFS4_STATEID_SIZE);
+       p = reserve_space(xdr, 4+NFS4_STATEID_SIZE);
 
-       WRITE32(OP_DELEGRETURN);
-       WRITEMEM(stateid->data, NFS4_STATEID_SIZE);
+       *p++ = cpu_to_be32(OP_DELEGRETURN);
+       xdr_encode_opaque_fixed(p, stateid->data, NFS4_STATEID_SIZE);
        hdr->nops++;
        hdr->replen += decode_delegreturn_maxsz;
 }
@@ -1548,16 +1518,16 @@ static void encode_exchange_id(struct xdr_stream *xdr,
 {
        __be32 *p;
 
-       RESERVE_SPACE(4 + sizeof(args->verifier->data));
-       WRITE32(OP_EXCHANGE_ID);
-       WRITEMEM(args->verifier->data, sizeof(args->verifier->data));
+       p = reserve_space(xdr, 4 + sizeof(args->verifier->data));
+       *p++ = cpu_to_be32(OP_EXCHANGE_ID);
+       xdr_encode_opaque_fixed(p, args->verifier->data, sizeof(args->verifier->data));
 
        encode_string(xdr, args->id_len, args->id);
 
-       RESERVE_SPACE(12);
-       WRITE32(args->flags);
-       WRITE32(0);     /* zero length state_protect4_a */
-       WRITE32(0);     /* zero length implementation id array */
+       p = reserve_space(xdr, 12);
+       *p++ = cpu_to_be32(args->flags);
+       *p++ = cpu_to_be32(0);  /* zero length state_protect4_a */
+       *p = cpu_to_be32(0);    /* zero length implementation id array */
        hdr->nops++;
        hdr->replen += decode_exchange_id_maxsz;
 }
@@ -1571,55 +1541,43 @@ static void encode_create_session(struct xdr_stream *xdr,
        uint32_t len;
        struct nfs_client *clp = args->client;
 
-       RESERVE_SPACE(4);
-       WRITE32(OP_CREATE_SESSION);
-
-       RESERVE_SPACE(8);
-       WRITE64(clp->cl_ex_clid);
+       len = scnprintf(machine_name, sizeof(machine_name), "%s",
+                       clp->cl_ipaddr);
 
-       RESERVE_SPACE(8);
-       WRITE32(clp->cl_seqid);                 /*Sequence id */
-       WRITE32(args->flags);                   /*flags */
+       p = reserve_space(xdr, 20 + 2*28 + 20 + len + 12);
+       *p++ = cpu_to_be32(OP_CREATE_SESSION);
+       p = xdr_encode_hyper(p, clp->cl_ex_clid);
+       *p++ = cpu_to_be32(clp->cl_seqid);                      /*Sequence id */
+       *p++ = cpu_to_be32(args->flags);                        /*flags */
 
-       RESERVE_SPACE(2*28);                    /* 2 channel_attrs */
        /* Fore Channel */
-       WRITE32(args->fc_attrs.headerpadsz);    /* header padding size */
-       WRITE32(args->fc_attrs.max_rqst_sz);    /* max req size */
-       WRITE32(args->fc_attrs.max_resp_sz);    /* max resp size */
-       WRITE32(args->fc_attrs.max_resp_sz_cached);     /* Max resp sz cached */
-       WRITE32(args->fc_attrs.max_ops);        /* max operations */
-       WRITE32(args->fc_attrs.max_reqs);       /* max requests */
-       WRITE32(0);                             /* rdmachannel_attrs */
+       *p++ = cpu_to_be32(args->fc_attrs.headerpadsz); /* header padding size */
+       *p++ = cpu_to_be32(args->fc_attrs.max_rqst_sz); /* max req size */
+       *p++ = cpu_to_be32(args->fc_attrs.max_resp_sz); /* max resp size */
+       *p++ = cpu_to_be32(args->fc_attrs.max_resp_sz_cached);  /* Max resp sz cached */
+       *p++ = cpu_to_be32(args->fc_attrs.max_ops);     /* max operations */
+       *p++ = cpu_to_be32(args->fc_attrs.max_reqs);    /* max requests */
+       *p++ = cpu_to_be32(0);                          /* rdmachannel_attrs */
 
        /* Back Channel */
-       WRITE32(args->fc_attrs.headerpadsz);    /* header padding size */
-       WRITE32(args->bc_attrs.max_rqst_sz);    /* max req size */
-       WRITE32(args->bc_attrs.max_resp_sz);    /* max resp size */
-       WRITE32(args->bc_attrs.max_resp_sz_cached);     /* Max resp sz cached */
-       WRITE32(args->bc_attrs.max_ops);        /* max operations */
-       WRITE32(args->bc_attrs.max_reqs);       /* max requests */
-       WRITE32(0);                             /* rdmachannel_attrs */
-
-       RESERVE_SPACE(4);
-       WRITE32(args->cb_program);              /* cb_program */
-
-       RESERVE_SPACE(4);                       /* # of security flavors */
-       WRITE32(1);
-
-       RESERVE_SPACE(4);
-       WRITE32(RPC_AUTH_UNIX);                 /* auth_sys */
+       *p++ = cpu_to_be32(args->fc_attrs.headerpadsz); /* header padding size */
+       *p++ = cpu_to_be32(args->bc_attrs.max_rqst_sz); /* max req size */
+       *p++ = cpu_to_be32(args->bc_attrs.max_resp_sz); /* max resp size */
+       *p++ = cpu_to_be32(args->bc_attrs.max_resp_sz_cached);  /* Max resp sz cached */
+       *p++ = cpu_to_be32(args->bc_attrs.max_ops);     /* max operations */
+       *p++ = cpu_to_be32(args->bc_attrs.max_reqs);    /* max requests */
+       *p++ = cpu_to_be32(0);                          /* rdmachannel_attrs */
+
+       *p++ = cpu_to_be32(args->cb_program);           /* cb_program */
+       *p++ = cpu_to_be32(1);
+       *p++ = cpu_to_be32(RPC_AUTH_UNIX);                      /* auth_sys */
 
        /* authsys_parms rfc1831 */
-       RESERVE_SPACE(4);
-       WRITE32((u32)clp->cl_boot_time.tv_nsec);        /* stamp */
-       len = scnprintf(machine_name, sizeof(machine_name), "%s",
-                       clp->cl_ipaddr);
-       RESERVE_SPACE(16 + len);
-       WRITE32(len);
-       WRITEMEM(machine_name, len);
-       WRITE32(0);                             /* UID */
-       WRITE32(0);                             /* GID */
-       WRITE32(0);                             /* No more gids */
+       *p++ = cpu_to_be32((u32)clp->cl_boot_time.tv_nsec);     /* stamp */
+       p = xdr_encode_opaque(p, machine_name, len);
+       *p++ = cpu_to_be32(0);                          /* UID */
+       *p++ = cpu_to_be32(0);                          /* GID */
+       *p = cpu_to_be32(0);                            /* No more gids */
        hdr->nops++;
        hdr->replen += decode_create_session_maxsz;
 }
@@ -1629,9 +1587,9 @@ static void encode_destroy_session(struct xdr_stream *xdr,
                                   struct compound_hdr *hdr)
 {
        __be32 *p;
-       RESERVE_SPACE(4 + NFS4_MAX_SESSIONID_LEN);
-       WRITE32(OP_DESTROY_SESSION);
-       WRITEMEM(session->sess_id.data, NFS4_MAX_SESSIONID_LEN);
+       p = reserve_space(xdr, 4 + NFS4_MAX_SESSIONID_LEN);
+       *p++ = cpu_to_be32(OP_DESTROY_SESSION);
+       xdr_encode_opaque_fixed(p, session->sess_id.data, NFS4_MAX_SESSIONID_LEN);
        hdr->nops++;
        hdr->replen += decode_destroy_session_maxsz;
 }
@@ -1655,8 +1613,8 @@ static void encode_sequence(struct xdr_stream *xdr,
        WARN_ON(args->sa_slotid == NFS4_MAX_SLOT_TABLE);
        slot = tp->slots + args->sa_slotid;
 
-       RESERVE_SPACE(4);
-       WRITE32(OP_SEQUENCE);
+       p = reserve_space(xdr, 4 + NFS4_MAX_SESSIONID_LEN + 16);
+       *p++ = cpu_to_be32(OP_SEQUENCE);
 
        /*
         * Sessionid + seqid + slotid + max slotid + cache_this
@@ -1670,12 +1628,11 @@ static void encode_sequence(struct xdr_stream *xdr,
                ((u32 *)session->sess_id.data)[3],
                slot->seq_nr, args->sa_slotid,
                tp->highest_used_slotid, args->sa_cache_this);
-       RESERVE_SPACE(NFS4_MAX_SESSIONID_LEN + 16);
-       WRITEMEM(session->sess_id.data, NFS4_MAX_SESSIONID_LEN);
-       WRITE32(slot->seq_nr);
-       WRITE32(args->sa_slotid);
-       WRITE32(tp->highest_used_slotid);
-       WRITE32(args->sa_cache_this);
+       p = xdr_encode_opaque_fixed(p, session->sess_id.data, NFS4_MAX_SESSIONID_LEN);
+       *p++ = cpu_to_be32(slot->seq_nr);
+       *p++ = cpu_to_be32(args->sa_slotid);
+       *p++ = cpu_to_be32(tp->highest_used_slotid);
+       *p = cpu_to_be32(args->sa_cache_this);
        hdr->nops++;
        hdr->replen += decode_sequence_maxsz;
 #endif /* CONFIG_NFS_V4_1 */
@@ -2466,68 +2423,53 @@ static int nfs4_xdr_enc_get_lease_time(struct rpc_rqst *req, uint32_t *p,
 }
 #endif /* CONFIG_NFS_V4_1 */
 
-/*
- * START OF "GENERIC" DECODE ROUTINES.
- *   These may look a little ugly since they are imported from a "generic"
- * set of XDR encode/decode routines which are intended to be shared by
- * all of our NFSv4 implementations (OpenBSD, MacOS X...).
- *
- * If the pain of reading these is too great, it should be a straightforward
- * task to translate them into Linux-specific versions which are more
- * consistent with the style used in NFSv2/v3...
- */
-#define READ32(x)         (x) = ntohl(*p++)
-#define READ64(x)         do {                 \
-       (x) = (u64)ntohl(*p++) << 32;           \
-       (x) |= ntohl(*p++);                     \
-} while (0)
-#define READTIME(x)       do {                 \
-       p++;                                    \
-       (x.tv_sec) = ntohl(*p++);               \
-       (x.tv_nsec) = ntohl(*p++);              \
-} while (0)
-#define COPYMEM(x,nbytes) do {                 \
-       memcpy((x), p, nbytes);                 \
-       p += XDR_QUADLEN(nbytes);               \
-} while (0)
-
-#define READ_BUF(nbytes)  do { \
-       p = xdr_inline_decode(xdr, nbytes); \
-       if (unlikely(!p)) { \
-               dprintk("nfs: %s: prematurely hit end of receive" \
-                               " buffer\n", __func__); \
-               dprintk("nfs: %s: xdr->p=%p, bytes=%u, xdr->end=%p\n", \
-                               __func__, xdr->p, nbytes, xdr->end); \
-               return -EIO; \
-       } \
-} while (0)
+static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
+{
+       dprintk("nfs: %s: prematurely hit end of receive buffer. "
+               "Remaining buffer length is %tu words.\n",
+               func, xdr->end - xdr->p);
+}
 
 static int decode_opaque_inline(struct xdr_stream *xdr, unsigned int *len, char **string)
 {
        __be32 *p;
 
-       READ_BUF(4);
-       READ32(*len);
-       READ_BUF(*len);
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p))
+               goto out_overflow;
+       *len = be32_to_cpup(p);
+       p = xdr_inline_decode(xdr, *len);
+       if (unlikely(!p))
+               goto out_overflow;
        *string = (char *)p;
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
 {
        __be32 *p;
 
-       READ_BUF(8);
-       READ32(hdr->status);
-       READ32(hdr->taglen);
+       p = xdr_inline_decode(xdr, 8);
+       if (unlikely(!p))
+               goto out_overflow;
+       hdr->status = be32_to_cpup(p++);
+       hdr->taglen = be32_to_cpup(p);
 
-       READ_BUF(hdr->taglen + 4);
+       p = xdr_inline_decode(xdr, hdr->taglen + 4);
+       if (unlikely(!p))
+               goto out_overflow;
        hdr->tag = (char *)p;
        p += XDR_QUADLEN(hdr->taglen);
-       READ32(hdr->nops);
+       hdr->nops = be32_to_cpup(p);
        if (unlikely(hdr->nops < 1))
                return nfs4_stat_to_errno(hdr->status);
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
@@ -2536,18 +2478,23 @@ static int decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
        uint32_t opnum;
        int32_t nfserr;
 
-       READ_BUF(8);
-       READ32(opnum);
+       p = xdr_inline_decode(xdr, 8);
+       if (unlikely(!p))
+               goto out_overflow;
+       opnum = be32_to_cpup(p++);
        if (opnum != expected) {
                dprintk("nfs: Server returned operation"
                        " %d but we issued a request for %d\n",
                                opnum, expected);
                return -EIO;
        }
-       READ32(nfserr);
+       nfserr = be32_to_cpup(p);
        if (nfserr != NFS_OK)
                return nfs4_stat_to_errno(nfserr);
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 /* Dummy routine */
@@ -2557,8 +2504,11 @@ static int decode_ace(struct xdr_stream *xdr, void *ace, struct nfs_client *clp)
        unsigned int strlen;
        char *str;
 
-       READ_BUF(12);
-       return decode_opaque_inline(xdr, &strlen, &str);
+       p = xdr_inline_decode(xdr, 12);
+       if (likely(p))
+               return decode_opaque_inline(xdr, &strlen, &str);
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
@@ -2566,27 +2516,39 @@ static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
        uint32_t bmlen;
        __be32 *p;
 
-       READ_BUF(4);
-       READ32(bmlen);
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p))
+               goto out_overflow;
+       bmlen = be32_to_cpup(p);
 
        bitmap[0] = bitmap[1] = 0;
-       READ_BUF((bmlen << 2));
+       p = xdr_inline_decode(xdr, (bmlen << 2));
+       if (unlikely(!p))
+               goto out_overflow;
        if (bmlen > 0) {
-               READ32(bitmap[0]);
+               bitmap[0] = be32_to_cpup(p++);
                if (bmlen > 1)
-                       READ32(bitmap[1]);
+                       bitmap[1] = be32_to_cpup(p);
        }
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static inline int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen, __be32 **savep)
 {
        __be32 *p;
 
-       READ_BUF(4);
-       READ32(*attrlen);
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p))
+               goto out_overflow;
+       *attrlen = be32_to_cpup(p);
        *savep = xdr->p;
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_supported(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *bitmask)
@@ -2609,8 +2571,10 @@ static int decode_attr_type(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *
        if (unlikely(bitmap[0] & (FATTR4_WORD0_TYPE - 1U)))
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_TYPE)) {
-               READ_BUF(4);
-               READ32(*type);
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               *type = be32_to_cpup(p);
                if (*type < NF4REG || *type > NF4NAMEDATTR) {
                        dprintk("%s: bad type %d\n", __func__, *type);
                        return -EIO;
@@ -2620,6 +2584,9 @@ static int decode_attr_type(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *
        }
        dprintk("%s: type=0%o\n", __func__, nfs_type2fmt[*type]);
        return ret;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_change(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *change)
@@ -2631,14 +2598,19 @@ static int decode_attr_change(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t
        if (unlikely(bitmap[0] & (FATTR4_WORD0_CHANGE - 1U)))
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_CHANGE)) {
-               READ_BUF(8);
-               READ64(*change);
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+               xdr_decode_hyper(p, change);
                bitmap[0] &= ~FATTR4_WORD0_CHANGE;
                ret = NFS_ATTR_FATTR_CHANGE;
        }
        dprintk("%s: change attribute=%Lu\n", __func__,
                        (unsigned long long)*change);
        return ret;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_size(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *size)
@@ -2650,13 +2622,18 @@ static int decode_attr_size(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *
        if (unlikely(bitmap[0] & (FATTR4_WORD0_SIZE - 1U)))
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_SIZE)) {
-               READ_BUF(8);
-               READ64(*size);
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+               xdr_decode_hyper(p, size);
                bitmap[0] &= ~FATTR4_WORD0_SIZE;
                ret = NFS_ATTR_FATTR_SIZE;
        }
        dprintk("%s: file size=%Lu\n", __func__, (unsigned long long)*size);
        return ret;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_link_support(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
@@ -2667,12 +2644,17 @@ static int decode_attr_link_support(struct xdr_stream *xdr, uint32_t *bitmap, ui
        if (unlikely(bitmap[0] & (FATTR4_WORD0_LINK_SUPPORT - 1U)))
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_LINK_SUPPORT)) {
-               READ_BUF(4);
-               READ32(*res);
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               *res = be32_to_cpup(p);
                bitmap[0] &= ~FATTR4_WORD0_LINK_SUPPORT;
        }
        dprintk("%s: link support=%s\n", __func__, *res == 0 ? "false" : "true");
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_symlink_support(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
@@ -2683,12 +2665,17 @@ static int decode_attr_symlink_support(struct xdr_stream *xdr, uint32_t *bitmap,
        if (unlikely(bitmap[0] & (FATTR4_WORD0_SYMLINK_SUPPORT - 1U)))
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_SYMLINK_SUPPORT)) {
-               READ_BUF(4);
-               READ32(*res);
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               *res = be32_to_cpup(p);
                bitmap[0] &= ~FATTR4_WORD0_SYMLINK_SUPPORT;
        }
        dprintk("%s: symlink support=%s\n", __func__, *res == 0 ? "false" : "true");
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_fsid *fsid)
@@ -2701,9 +2688,11 @@ static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs
        if (unlikely(bitmap[0] & (FATTR4_WORD0_FSID - 1U)))
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_FSID)) {
-               READ_BUF(16);
-               READ64(fsid->major);
-               READ64(fsid->minor);
+               p = xdr_inline_decode(xdr, 16);
+               if (unlikely(!p))
+                       goto out_overflow;
+               p = xdr_decode_hyper(p, &fsid->major);
+               xdr_decode_hyper(p, &fsid->minor);
                bitmap[0] &= ~FATTR4_WORD0_FSID;
                ret = NFS_ATTR_FATTR_FSID;
        }
@@ -2711,6 +2700,9 @@ static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs
                        (unsigned long long)fsid->major,
                        (unsigned long long)fsid->minor);
        return ret;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_lease_time(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
@@ -2721,12 +2713,17 @@ static int decode_attr_lease_time(struct xdr_stream *xdr, uint32_t *bitmap, uint
        if (unlikely(bitmap[0] & (FATTR4_WORD0_LEASE_TIME - 1U)))
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_LEASE_TIME)) {
-               READ_BUF(4);
-               READ32(*res);
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               *res = be32_to_cpup(p);
                bitmap[0] &= ~FATTR4_WORD0_LEASE_TIME;
        }
        dprintk("%s: file size=%u\n", __func__, (unsigned int)*res);
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
@@ -2737,12 +2734,17 @@ static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint
        if (unlikely(bitmap[0] & (FATTR4_WORD0_ACLSUPPORT - 1U)))
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_ACLSUPPORT)) {
-               READ_BUF(4);
-               READ32(*res);
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               *res = be32_to_cpup(p);
                bitmap[0] &= ~FATTR4_WORD0_ACLSUPPORT;
        }
        dprintk("%s: ACLs supported=%u\n", __func__, (unsigned int)*res);
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *fileid)
@@ -2754,13 +2756,18 @@ static int decode_attr_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t
        if (unlikely(bitmap[0] & (FATTR4_WORD0_FILEID - 1U)))
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_FILEID)) {
-               READ_BUF(8);
-               READ64(*fileid);
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+               xdr_decode_hyper(p, fileid);
                bitmap[0] &= ~FATTR4_WORD0_FILEID;
                ret = NFS_ATTR_FATTR_FILEID;
        }
        dprintk("%s: fileid=%Lu\n", __func__, (unsigned long long)*fileid);
        return ret;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_mounted_on_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *fileid)
@@ -2772,13 +2779,18 @@ static int decode_attr_mounted_on_fileid(struct xdr_stream *xdr, uint32_t *bitma
        if (unlikely(bitmap[1] & (FATTR4_WORD1_MOUNTED_ON_FILEID - 1U)))
                return -EIO;
        if (likely(bitmap[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)) {
-               READ_BUF(8);
-               READ64(*fileid);
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+               xdr_decode_hyper(p, fileid);
                bitmap[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;
                ret = NFS_ATTR_FATTR_FILEID;
        }
        dprintk("%s: fileid=%Lu\n", __func__, (unsigned long long)*fileid);
        return ret;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_files_avail(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
@@ -2790,12 +2802,17 @@ static int decode_attr_files_avail(struct xdr_stream *xdr, uint32_t *bitmap, uin
        if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_AVAIL - 1U)))
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_FILES_AVAIL)) {
-               READ_BUF(8);
-               READ64(*res);
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+               xdr_decode_hyper(p, res);
                bitmap[0] &= ~FATTR4_WORD0_FILES_AVAIL;
        }
        dprintk("%s: files avail=%Lu\n", __func__, (unsigned long long)*res);
        return status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_files_free(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
@@ -2807,12 +2824,17 @@ static int decode_attr_files_free(struct xdr_stream *xdr, uint32_t *bitmap, uint
        if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_FREE - 1U)))
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_FILES_FREE)) {
-               READ_BUF(8);
-               READ64(*res);
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+               xdr_decode_hyper(p, res);
                bitmap[0] &= ~FATTR4_WORD0_FILES_FREE;
        }
        dprintk("%s: files free=%Lu\n", __func__, (unsigned long long)*res);
        return status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_files_total(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
@@ -2824,12 +2846,17 @@ static int decode_attr_files_total(struct xdr_stream *xdr, uint32_t *bitmap, uin
        if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_TOTAL - 1U)))
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_FILES_TOTAL)) {
-               READ_BUF(8);
-               READ64(*res);
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+               xdr_decode_hyper(p, res);
                bitmap[0] &= ~FATTR4_WORD0_FILES_TOTAL;
        }
        dprintk("%s: files total=%Lu\n", __func__, (unsigned long long)*res);
        return status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_pathname(struct xdr_stream *xdr, struct nfs4_pathname *path)
@@ -2838,8 +2865,10 @@ static int decode_pathname(struct xdr_stream *xdr, struct nfs4_pathname *path)
        __be32 *p;
        int status = 0;
 
-       READ_BUF(4);
-       READ32(n);
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p))
+               goto out_overflow;
+       n = be32_to_cpup(p);
        if (n == 0)
                goto root_path;
        dprintk("path ");
@@ -2873,6 +2902,9 @@ out_eio:
        dprintk(" status %d", status);
        status = -EIO;
        goto out;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_fs_locations *res)
@@ -2890,8 +2922,10 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st
        status = decode_pathname(xdr, &res->fs_path);
        if (unlikely(status != 0))
                goto out;
-       READ_BUF(4);
-       READ32(n);
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p))
+               goto out_overflow;
+       n = be32_to_cpup(p);
        if (n <= 0)
                goto out_eio;
        res->nlocations = 0;
@@ -2899,8 +2933,10 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st
                u32 m;
                struct nfs4_fs_location *loc = &res->locations[res->nlocations];
 
-               READ_BUF(4);
-               READ32(m);
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               m = be32_to_cpup(p);
 
                loc->nservers = 0;
                dprintk("%s: servers ", __func__);
@@ -2939,6 +2975,8 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st
 out:
        dprintk("%s: fs_locations done, error = %d\n", __func__, status);
        return status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
 out_eio:
        status = -EIO;
        goto out;
@@ -2953,12 +2991,17 @@ static int decode_attr_maxfilesize(struct xdr_stream *xdr, uint32_t *bitmap, uin
        if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXFILESIZE - 1U)))
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_MAXFILESIZE)) {
-               READ_BUF(8);
-               READ64(*res);
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+               xdr_decode_hyper(p, res);
                bitmap[0] &= ~FATTR4_WORD0_MAXFILESIZE;
        }
        dprintk("%s: maxfilesize=%Lu\n", __func__, (unsigned long long)*res);
        return status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_maxlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *maxlink)
@@ -2970,12 +3013,17 @@ static int decode_attr_maxlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_
        if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXLINK - 1U)))
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_MAXLINK)) {
-               READ_BUF(4);
-               READ32(*maxlink);
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               *maxlink = be32_to_cpup(p);
                bitmap[0] &= ~FATTR4_WORD0_MAXLINK;
        }
        dprintk("%s: maxlink=%u\n", __func__, *maxlink);
        return status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_maxname(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *maxname)
@@ -2987,12 +3035,17 @@ static int decode_attr_maxname(struct xdr_stream *xdr, uint32_t *bitmap, uint32_
        if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXNAME - 1U)))
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_MAXNAME)) {
-               READ_BUF(4);
-               READ32(*maxname);
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               *maxname = be32_to_cpup(p);
                bitmap[0] &= ~FATTR4_WORD0_MAXNAME;
        }
        dprintk("%s: maxname=%u\n", __func__, *maxname);
        return status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_maxread(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
@@ -3005,8 +3058,10 @@ static int decode_attr_maxread(struct xdr_stream *xdr, uint32_t *bitmap, uint32_
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_MAXREAD)) {
                uint64_t maxread;
-               READ_BUF(8);
-               READ64(maxread);
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+               xdr_decode_hyper(p, &maxread);
                if (maxread > 0x7FFFFFFF)
                        maxread = 0x7FFFFFFF;
                *res = (uint32_t)maxread;
@@ -3014,6 +3069,9 @@ static int decode_attr_maxread(struct xdr_stream *xdr, uint32_t *bitmap, uint32_
        }
        dprintk("%s: maxread=%lu\n", __func__, (unsigned long)*res);
        return status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_maxwrite(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
@@ -3026,8 +3084,10 @@ static int decode_attr_maxwrite(struct xdr_stream *xdr, uint32_t *bitmap, uint32
                return -EIO;
        if (likely(bitmap[0] & FATTR4_WORD0_MAXWRITE)) {
                uint64_t maxwrite;
-               READ_BUF(8);
-               READ64(maxwrite);
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+               xdr_decode_hyper(p, &maxwrite);
                if (maxwrite > 0x7FFFFFFF)
                        maxwrite = 0x7FFFFFFF;
                *res = (uint32_t)maxwrite;
@@ -3035,6 +3095,9 @@ static int decode_attr_maxwrite(struct xdr_stream *xdr, uint32_t *bitmap, uint32
        }
        dprintk("%s: maxwrite=%lu\n", __func__, (unsigned long)*res);
        return status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_mode(struct xdr_stream *xdr, uint32_t *bitmap, umode_t *mode)
@@ -3047,14 +3110,19 @@ static int decode_attr_mode(struct xdr_stream *xdr, uint32_t *bitmap, umode_t *m
        if (unlikely(bitmap[1] & (FATTR4_WORD1_MODE - 1U)))
                return -EIO;
        if (likely(bitmap[1] & FATTR4_WORD1_MODE)) {
-               READ_BUF(4);
-               READ32(tmp);
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               tmp = be32_to_cpup(p);
                *mode = tmp & ~S_IFMT;
                bitmap[1] &= ~FATTR4_WORD1_MODE;
                ret = NFS_ATTR_FATTR_MODE;
        }
        dprintk("%s: file mode=0%o\n", __func__, (unsigned int)*mode);
        return ret;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_nlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *nlink)
@@ -3066,16 +3134,22 @@ static int decode_attr_nlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t
        if (unlikely(bitmap[1] & (FATTR4_WORD1_NUMLINKS - 1U)))
                return -EIO;
        if (likely(bitmap[1] & FATTR4_WORD1_NUMLINKS)) {
-               READ_BUF(4);
-               READ32(*nlink);
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               *nlink = be32_to_cpup(p);
                bitmap[1] &= ~FATTR4_WORD1_NUMLINKS;
                ret = NFS_ATTR_FATTR_NLINK;
        }
        dprintk("%s: nlink=%u\n", __func__, (unsigned int)*nlink);
        return ret;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
-static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_client *clp, uint32_t *uid)
+static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap,
+               struct nfs_client *clp, uint32_t *uid, int may_sleep)
 {
        uint32_t len;
        __be32 *p;
@@ -3085,10 +3159,16 @@ static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, struct nf
        if (unlikely(bitmap[1] & (FATTR4_WORD1_OWNER - 1U)))
                return -EIO;
        if (likely(bitmap[1] & FATTR4_WORD1_OWNER)) {
-               READ_BUF(4);
-               READ32(len);
-               READ_BUF(len);
-               if (len < XDR_MAX_NETOBJ) {
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               len = be32_to_cpup(p);
+               p = xdr_inline_decode(xdr, len);
+               if (unlikely(!p))
+                       goto out_overflow;
+               if (!may_sleep) {
+                       /* do nothing */
+               } else if (len < XDR_MAX_NETOBJ) {
                        if (nfs_map_name_to_uid(clp, (char *)p, len, uid) == 0)
                                ret = NFS_ATTR_FATTR_OWNER;
                        else
@@ -3101,9 +3181,13 @@ static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, struct nf
        }
        dprintk("%s: uid=%d\n", __func__, (int)*uid);
        return ret;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
-static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_client *clp, uint32_t *gid)
+static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap,
+               struct nfs_client *clp, uint32_t *gid, int may_sleep)
 {
        uint32_t len;
        __be32 *p;
@@ -3113,10 +3197,16 @@ static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, struct nf
        if (unlikely(bitmap[1] & (FATTR4_WORD1_OWNER_GROUP - 1U)))
                return -EIO;
        if (likely(bitmap[1] & FATTR4_WORD1_OWNER_GROUP)) {
-               READ_BUF(4);
-               READ32(len);
-               READ_BUF(len);
-               if (len < XDR_MAX_NETOBJ) {
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               len = be32_to_cpup(p);
+               p = xdr_inline_decode(xdr, len);
+               if (unlikely(!p))
+                       goto out_overflow;
+               if (!may_sleep) {
+                       /* do nothing */
+               } else if (len < XDR_MAX_NETOBJ) {
                        if (nfs_map_group_to_gid(clp, (char *)p, len, gid) == 0)
                                ret = NFS_ATTR_FATTR_GROUP;
                        else
@@ -3129,6 +3219,9 @@ static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, struct nf
        }
        dprintk("%s: gid=%d\n", __func__, (int)*gid);
        return ret;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_rdev(struct xdr_stream *xdr, uint32_t *bitmap, dev_t *rdev)
@@ -3143,9 +3236,11 @@ static int decode_attr_rdev(struct xdr_stream *xdr, uint32_t *bitmap, dev_t *rde
        if (likely(bitmap[1] & FATTR4_WORD1_RAWDEV)) {
                dev_t tmp;
 
-               READ_BUF(8);
-               READ32(major);
-               READ32(minor);
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+               major = be32_to_cpup(p++);
+               minor = be32_to_cpup(p);
                tmp = MKDEV(major, minor);
                if (MAJOR(tmp) == major && MINOR(tmp) == minor)
                        *rdev = tmp;
@@ -3154,6 +3249,9 @@ static int decode_attr_rdev(struct xdr_stream *xdr, uint32_t *bitmap, dev_t *rde
        }
        dprintk("%s: rdev=(0x%x:0x%x)\n", __func__, major, minor);
        return ret;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_space_avail(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
@@ -3165,12 +3263,17 @@ static int decode_attr_space_avail(struct xdr_stream *xdr, uint32_t *bitmap, uin
        if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_AVAIL - 1U)))
                return -EIO;
        if (likely(bitmap[1] & FATTR4_WORD1_SPACE_AVAIL)) {
-               READ_BUF(8);
-               READ64(*res);
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+               xdr_decode_hyper(p, res);
                bitmap[1] &= ~FATTR4_WORD1_SPACE_AVAIL;
        }
        dprintk("%s: space avail=%Lu\n", __func__, (unsigned long long)*res);
        return status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_space_free(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
@@ -3182,12 +3285,17 @@ static int decode_attr_space_free(struct xdr_stream *xdr, uint32_t *bitmap, uint
        if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_FREE - 1U)))
                return -EIO;
        if (likely(bitmap[1] & FATTR4_WORD1_SPACE_FREE)) {
-               READ_BUF(8);
-               READ64(*res);
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+               xdr_decode_hyper(p, res);
                bitmap[1] &= ~FATTR4_WORD1_SPACE_FREE;
        }
        dprintk("%s: space free=%Lu\n", __func__, (unsigned long long)*res);
        return status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_space_total(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
@@ -3199,12 +3307,17 @@ static int decode_attr_space_total(struct xdr_stream *xdr, uint32_t *bitmap, uin
        if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_TOTAL - 1U)))
                return -EIO;
        if (likely(bitmap[1] & FATTR4_WORD1_SPACE_TOTAL)) {
-               READ_BUF(8);
-               READ64(*res);
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+               xdr_decode_hyper(p, res);
                bitmap[1] &= ~FATTR4_WORD1_SPACE_TOTAL;
        }
        dprintk("%s: space total=%Lu\n", __func__, (unsigned long long)*res);
        return status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_space_used(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *used)
@@ -3216,14 +3329,19 @@ static int decode_attr_space_used(struct xdr_stream *xdr, uint32_t *bitmap, uint
        if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_USED - 1U)))
                return -EIO;
        if (likely(bitmap[1] & FATTR4_WORD1_SPACE_USED)) {
-               READ_BUF(8);
-               READ64(*used);
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+               xdr_decode_hyper(p, used);
                bitmap[1] &= ~FATTR4_WORD1_SPACE_USED;
                ret = NFS_ATTR_FATTR_SPACE_USED;
        }
        dprintk("%s: space used=%Lu\n", __func__,
                        (unsigned long long)*used);
        return ret;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_time(struct xdr_stream *xdr, struct timespec *time)
@@ -3232,12 +3350,17 @@ static int decode_attr_time(struct xdr_stream *xdr, struct timespec *time)
        uint64_t sec;
        uint32_t nsec;
 
-       READ_BUF(12);
-       READ64(sec);
-       READ32(nsec);
+       p = xdr_inline_decode(xdr, 12);
+       if (unlikely(!p))
+               goto out_overflow;
+       p = xdr_decode_hyper(p, &sec);
+       nsec = be32_to_cpup(p);
        time->tv_sec = (time_t)sec;
        time->tv_nsec = (long)nsec;
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_attr_time_access(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time)
@@ -3315,11 +3438,16 @@ static int decode_change_info(struct xdr_stream *xdr, struct nfs4_change_info *c
 {
        __be32 *p;
 
-       READ_BUF(20);
-       READ32(cinfo->atomic);
-       READ64(cinfo->before);
-       READ64(cinfo->after);
+       p = xdr_inline_decode(xdr, 20);
+       if (unlikely(!p))
+               goto out_overflow;
+       cinfo->atomic = be32_to_cpup(p++);
+       p = xdr_decode_hyper(p, &cinfo->before);
+       xdr_decode_hyper(p, &cinfo->after);
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_access(struct xdr_stream *xdr, struct nfs4_accessres *access)
@@ -3331,40 +3459,62 @@ static int decode_access(struct xdr_stream *xdr, struct nfs4_accessres *access)
        status = decode_op_hdr(xdr, OP_ACCESS);
        if (status)
                return status;
-       READ_BUF(8);
-       READ32(supp);
-       READ32(acc);
+       p = xdr_inline_decode(xdr, 8);
+       if (unlikely(!p))
+               goto out_overflow;
+       supp = be32_to_cpup(p++);
+       acc = be32_to_cpup(p);
        access->supported = supp;
        access->access = acc;
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
-static int decode_close(struct xdr_stream *xdr, struct nfs_closeres *res)
+static int decode_opaque_fixed(struct xdr_stream *xdr, void *buf, size_t len)
 {
        __be32 *p;
+
+       p = xdr_inline_decode(xdr, len);
+       if (likely(p)) {
+               memcpy(buf, p, len);
+               return 0;
+       }
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+static int decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
+{
+       return decode_opaque_fixed(xdr, stateid->data, NFS4_STATEID_SIZE);
+}
+
+static int decode_close(struct xdr_stream *xdr, struct nfs_closeres *res)
+{
        int status;
 
        status = decode_op_hdr(xdr, OP_CLOSE);
        if (status != -EIO)
                nfs_increment_open_seqid(status, res->seqid);
-       if (status)
-               return status;
-       READ_BUF(NFS4_STATEID_SIZE);
-       COPYMEM(res->stateid.data, NFS4_STATEID_SIZE);
-       return 0;
+       if (!status)
+               status = decode_stateid(xdr, &res->stateid);
+       return status;
+}
+
+static int decode_verifier(struct xdr_stream *xdr, void *verifier)
+{
+       return decode_opaque_fixed(xdr, verifier, 8);
 }
 
 static int decode_commit(struct xdr_stream *xdr, struct nfs_writeres *res)
 {
-       __be32 *p;
        int status;
 
        status = decode_op_hdr(xdr, OP_COMMIT);
-       if (status)
-               return status;
-       READ_BUF(8);
-       COPYMEM(res->verf->verifier, 8);
-       return 0;
+       if (!status)
+               status = decode_verifier(xdr, res->verf->verifier);
+       return status;
 }
 
 static int decode_create(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
@@ -3378,10 +3528,16 @@ static int decode_create(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
                return status;
        if ((status = decode_change_info(xdr, cinfo)))
                return status;
-       READ_BUF(4);
-       READ32(bmlen);
-       READ_BUF(bmlen << 2);
-       return 0;
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p))
+               goto out_overflow;
+       bmlen = be32_to_cpup(p);
+       p = xdr_inline_decode(xdr, bmlen << 2);
+       if (likely(p))
+               return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_res *res)
@@ -3466,7 +3622,8 @@ xdr_error:
        return status;
 }
 
-static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, const struct nfs_server *server)
+static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
+               const struct nfs_server *server, int may_sleep)
 {
        __be32 *savep;
        uint32_t attrlen,
@@ -3538,12 +3695,14 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, cons
                goto xdr_error;
        fattr->valid |= status;
 
-       status = decode_attr_owner(xdr, bitmap, server->nfs_client, &fattr->uid);
+       status = decode_attr_owner(xdr, bitmap, server->nfs_client,
+                       &fattr->uid, may_sleep);
        if (status < 0)
                goto xdr_error;
        fattr->valid |= status;
 
-       status = decode_attr_group(xdr, bitmap, server->nfs_client, &fattr->gid);
+       status = decode_attr_group(xdr, bitmap, server->nfs_client,
+                       &fattr->gid, may_sleep);
        if (status < 0)
                goto xdr_error;
        fattr->valid |= status;
@@ -3633,14 +3792,21 @@ static int decode_getfh(struct xdr_stream *xdr, struct nfs_fh *fh)
        if (status)
                return status;
 
-       READ_BUF(4);
-       READ32(len);
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p))
+               goto out_overflow;
+       len = be32_to_cpup(p);
        if (len > NFS4_FHSIZE)
                return -EIO;
        fh->size = len;
-       READ_BUF(len);
-       COPYMEM(fh->data, len);
+       p = xdr_inline_decode(xdr, len);
+       if (unlikely(!p))
+               goto out_overflow;
+       memcpy(fh->data, p, len);
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_link(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
@@ -3662,10 +3828,12 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl)
        __be32 *p;
        uint32_t namelen, type;
 
-       READ_BUF(32);
-       READ64(offset);
-       READ64(length);
-       READ32(type);
+       p = xdr_inline_decode(xdr, 32);
+       if (unlikely(!p))
+               goto out_overflow;
+       p = xdr_decode_hyper(p, &offset);
+       p = xdr_decode_hyper(p, &length);
+       type = be32_to_cpup(p++);
        if (fl != NULL) {
                fl->fl_start = (loff_t)offset;
                fl->fl_end = fl->fl_start + (loff_t)length - 1;
@@ -3676,23 +3844,27 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl)
                        fl->fl_type = F_RDLCK;
                fl->fl_pid = 0;
        }
-       READ64(clientid);
-       READ32(namelen);
-       READ_BUF(namelen);
-       return -NFS4ERR_DENIED;
+       p = xdr_decode_hyper(p, &clientid);
+       namelen = be32_to_cpup(p);
+       p = xdr_inline_decode(xdr, namelen);
+       if (likely(p))
+               return -NFS4ERR_DENIED;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_lock(struct xdr_stream *xdr, struct nfs_lock_res *res)
 {
-       __be32 *p;
        int status;
 
        status = decode_op_hdr(xdr, OP_LOCK);
        if (status == -EIO)
                goto out;
        if (status == 0) {
-               READ_BUF(NFS4_STATEID_SIZE);
-               COPYMEM(res->stateid.data, NFS4_STATEID_SIZE);
+               status = decode_stateid(xdr, &res->stateid);
+               if (unlikely(status))
+                       goto out;
        } else if (status == -NFS4ERR_DENIED)
                status = decode_lock_denied(xdr, NULL);
        if (res->open_seqid != NULL)
@@ -3713,16 +3885,13 @@ static int decode_lockt(struct xdr_stream *xdr, struct nfs_lockt_res *res)
 
 static int decode_locku(struct xdr_stream *xdr, struct nfs_locku_res *res)
 {
-       __be32 *p;
        int status;
 
        status = decode_op_hdr(xdr, OP_LOCKU);
        if (status != -EIO)
                nfs_increment_lock_seqid(status, res->seqid);
-       if (status == 0) {
-               READ_BUF(NFS4_STATEID_SIZE);
-               COPYMEM(res->stateid.data, NFS4_STATEID_SIZE);
-       }
+       if (status == 0)
+               status = decode_stateid(xdr, &res->stateid);
        return status;
 }
 
@@ -3737,34 +3906,46 @@ static int decode_space_limit(struct xdr_stream *xdr, u64 *maxsize)
        __be32 *p;
        uint32_t limit_type, nblocks, blocksize;
 
-       READ_BUF(12);
-       READ32(limit_type);
+       p = xdr_inline_decode(xdr, 12);
+       if (unlikely(!p))
+               goto out_overflow;
+       limit_type = be32_to_cpup(p++);
        switch (limit_type) {
        case 1:
-               READ64(*maxsize);
+               xdr_decode_hyper(p, maxsize);
                break;
        case 2:
-               READ32(nblocks);
-               READ32(blocksize);
+               nblocks = be32_to_cpup(p++);
+               blocksize = be32_to_cpup(p);
                *maxsize = (uint64_t)nblocks * (uint64_t)blocksize;
        }
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
 {
        __be32 *p;
        uint32_t delegation_type;
+       int status;
 
-       READ_BUF(4);
-       READ32(delegation_type);
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p))
+               goto out_overflow;
+       delegation_type = be32_to_cpup(p);
        if (delegation_type == NFS4_OPEN_DELEGATE_NONE) {
                res->delegation_type = 0;
                return 0;
        }
-       READ_BUF(NFS4_STATEID_SIZE+4);
-       COPYMEM(res->delegation.data, NFS4_STATEID_SIZE);
-       READ32(res->do_recall);
+       status = decode_stateid(xdr, &res->delegation);
+       if (unlikely(status))
+               return status;
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p))
+               goto out_overflow;
+       res->do_recall = be32_to_cpup(p);
 
        switch (delegation_type) {
        case NFS4_OPEN_DELEGATE_READ:
@@ -3776,6 +3957,9 @@ static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
                                return -EIO;
        }
        return decode_ace(xdr, NULL, res->server->nfs_client);
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
@@ -3787,23 +3971,27 @@ static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
        status = decode_op_hdr(xdr, OP_OPEN);
        if (status != -EIO)
                nfs_increment_open_seqid(status, res->seqid);
-       if (status)
+       if (!status)
+               status = decode_stateid(xdr, &res->stateid);
+       if (unlikely(status))
                return status;
-       READ_BUF(NFS4_STATEID_SIZE);
-       COPYMEM(res->stateid.data, NFS4_STATEID_SIZE);
 
        decode_change_info(xdr, &res->cinfo);
 
-       READ_BUF(8);
-       READ32(res->rflags);
-       READ32(bmlen);
+       p = xdr_inline_decode(xdr, 8);
+       if (unlikely(!p))
+               goto out_overflow;
+       res->rflags = be32_to_cpup(p++);
+       bmlen = be32_to_cpup(p);
        if (bmlen > 10)
                goto xdr_error;
 
-       READ_BUF(bmlen << 2);
+       p = xdr_inline_decode(xdr, bmlen << 2);
+       if (unlikely(!p))
+               goto out_overflow;
        savewords = min_t(uint32_t, bmlen, NFS4_BITMAP_SIZE);
        for (i = 0; i < savewords; ++i)
-               READ32(res->attrset[i]);
+               res->attrset[i] = be32_to_cpup(p++);
        for (; i < NFS4_BITMAP_SIZE; i++)
                res->attrset[i] = 0;
 
@@ -3811,36 +3999,33 @@ static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
 xdr_error:
        dprintk("%s: Bitmap too large! Length = %u\n", __func__, bmlen);
        return -EIO;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmres *res)
 {
-       __be32 *p;
        int status;
 
        status = decode_op_hdr(xdr, OP_OPEN_CONFIRM);
        if (status != -EIO)
                nfs_increment_open_seqid(status, res->seqid);
-       if (status)
-               return status;
-       READ_BUF(NFS4_STATEID_SIZE);
-       COPYMEM(res->stateid.data, NFS4_STATEID_SIZE);
-       return 0;
+       if (!status)
+               status = decode_stateid(xdr, &res->stateid);
+       return status;
 }
 
 static int decode_open_downgrade(struct xdr_stream *xdr, struct nfs_closeres *res)
 {
-       __be32 *p;
        int status;
 
        status = decode_op_hdr(xdr, OP_OPEN_DOWNGRADE);
        if (status != -EIO)
                nfs_increment_open_seqid(status, res->seqid);
-       if (status)
-               return status;
-       READ_BUF(NFS4_STATEID_SIZE);
-       COPYMEM(res->stateid.data, NFS4_STATEID_SIZE);
-       return 0;
+       if (!status)
+               status = decode_stateid(xdr, &res->stateid);
+       return status;
 }
 
 static int decode_putfh(struct xdr_stream *xdr)
@@ -3863,9 +4048,11 @@ static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_
        status = decode_op_hdr(xdr, OP_READ);
        if (status)
                return status;
-       READ_BUF(8);
-       READ32(eof);
-       READ32(count);
+       p = xdr_inline_decode(xdr, 8);
+       if (unlikely(!p))
+               goto out_overflow;
+       eof = be32_to_cpup(p++);
+       count = be32_to_cpup(p);
        hdrlen = (u8 *) p - (u8 *) iov->iov_base;
        recvd = req->rq_rcv_buf.len - hdrlen;
        if (count > recvd) {
@@ -3878,6 +4065,9 @@ static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_
        res->eof = eof;
        res->count = count;
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir_res *readdir)
@@ -3892,17 +4082,17 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
        int             status;
 
        status = decode_op_hdr(xdr, OP_READDIR);
-       if (status)
+       if (!status)
+               status = decode_verifier(xdr, readdir->verifier.data);
+       if (unlikely(status))
                return status;
-       READ_BUF(8);
-       COPYMEM(readdir->verifier.data, 8);
        dprintk("%s: verifier = %08x:%08x\n",
                        __func__,
                        ((u32 *)readdir->verifier.data)[0],
                        ((u32 *)readdir->verifier.data)[1]);
 
 
-       hdrlen = (char *) p - (char *) iov->iov_base;
+       hdrlen = (char *) xdr->p - (char *) iov->iov_base;
        recvd = rcvbuf->len - hdrlen;
        if (pglen > recvd)
                pglen = recvd;
@@ -3990,8 +4180,10 @@ static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req)
                return status;
 
        /* Convert length of symlink */
-       READ_BUF(4);
-       READ32(len);
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p))
+               goto out_overflow;
+       len = be32_to_cpup(p);
        if (len >= rcvbuf->page_len || len <= 0) {
                dprintk("nfs: server returned giant symlink!\n");
                return -ENAMETOOLONG;
@@ -4015,6 +4207,9 @@ static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req)
        kaddr[len+rcvbuf->page_base] = '\0';
        kunmap_atomic(kaddr, KM_USER0);
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_remove(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
@@ -4112,10 +4307,16 @@ static int decode_setattr(struct xdr_stream *xdr)
        status = decode_op_hdr(xdr, OP_SETATTR);
        if (status)
                return status;
-       READ_BUF(4);
-       READ32(bmlen);
-       READ_BUF(bmlen << 2);
-       return 0;
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p))
+               goto out_overflow;
+       bmlen = be32_to_cpup(p);
+       p = xdr_inline_decode(xdr, bmlen << 2);
+       if (likely(p))
+               return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_setclientid(struct xdr_stream *xdr, struct nfs_client *clp)
@@ -4124,35 +4325,50 @@ static int decode_setclientid(struct xdr_stream *xdr, struct nfs_client *clp)
        uint32_t opnum;
        int32_t nfserr;
 
-       READ_BUF(8);
-       READ32(opnum);
+       p = xdr_inline_decode(xdr, 8);
+       if (unlikely(!p))
+               goto out_overflow;
+       opnum = be32_to_cpup(p++);
        if (opnum != OP_SETCLIENTID) {
                dprintk("nfs: decode_setclientid: Server returned operation"
                        " %d\n", opnum);
                return -EIO;
        }
-       READ32(nfserr);
+       nfserr = be32_to_cpup(p);
        if (nfserr == NFS_OK) {
-               READ_BUF(8 + NFS4_VERIFIER_SIZE);
-               READ64(clp->cl_clientid);
-               COPYMEM(clp->cl_confirm.data, NFS4_VERIFIER_SIZE);
+               p = xdr_inline_decode(xdr, 8 + NFS4_VERIFIER_SIZE);
+               if (unlikely(!p))
+                       goto out_overflow;
+               p = xdr_decode_hyper(p, &clp->cl_clientid);
+               memcpy(clp->cl_confirm.data, p, NFS4_VERIFIER_SIZE);
        } else if (nfserr == NFSERR_CLID_INUSE) {
                uint32_t len;
 
                /* skip netid string */
-               READ_BUF(4);
-               READ32(len);
-               READ_BUF(len);
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               len = be32_to_cpup(p);
+               p = xdr_inline_decode(xdr, len);
+               if (unlikely(!p))
+                       goto out_overflow;
 
                /* skip uaddr string */
-               READ_BUF(4);
-               READ32(len);
-               READ_BUF(len);
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               len = be32_to_cpup(p);
+               p = xdr_inline_decode(xdr, len);
+               if (unlikely(!p))
+                       goto out_overflow;
                return -NFSERR_CLID_INUSE;
        } else
                return nfs4_stat_to_errno(nfserr);
 
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_setclientid_confirm(struct xdr_stream *xdr)
@@ -4169,11 +4385,16 @@ static int decode_write(struct xdr_stream *xdr, struct nfs_writeres *res)
        if (status)
                return status;
 
-       READ_BUF(16);
-       READ32(res->count);
-       READ32(res->verf->committed);
-       COPYMEM(res->verf->verifier, 8);
+       p = xdr_inline_decode(xdr, 16);
+       if (unlikely(!p))
+               goto out_overflow;
+       res->count = be32_to_cpup(p++);
+       res->verf->committed = be32_to_cpup(p++);
+       memcpy(res->verf->verifier, p, 8);
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_delegreturn(struct xdr_stream *xdr)
@@ -4187,6 +4408,7 @@ static int decode_exchange_id(struct xdr_stream *xdr,
 {
        __be32 *p;
        uint32_t dummy;
+       char *dummy_str;
        int status;
        struct nfs_client *clp = res->client;
 
@@ -4194,36 +4416,45 @@ static int decode_exchange_id(struct xdr_stream *xdr,
        if (status)
                return status;
 
-       READ_BUF(8);
-       READ64(clp->cl_ex_clid);
-       READ_BUF(12);
-       READ32(clp->cl_seqid);
-       READ32(clp->cl_exchange_flags);
+       p = xdr_inline_decode(xdr, 8);
+       if (unlikely(!p))
+               goto out_overflow;
+       xdr_decode_hyper(p, &clp->cl_ex_clid);
+       p = xdr_inline_decode(xdr, 12);
+       if (unlikely(!p))
+               goto out_overflow;
+       clp->cl_seqid = be32_to_cpup(p++);
+       clp->cl_exchange_flags = be32_to_cpup(p++);
 
        /* We ask for SP4_NONE */
-       READ32(dummy);
+       dummy = be32_to_cpup(p);
        if (dummy != SP4_NONE)
                return -EIO;
 
        /* Throw away minor_id */
-       READ_BUF(8);
+       p = xdr_inline_decode(xdr, 8);
+       if (unlikely(!p))
+               goto out_overflow;
 
        /* Throw away Major id */
-       READ_BUF(4);
-       READ32(dummy);
-       READ_BUF(dummy);
+       status = decode_opaque_inline(xdr, &dummy, &dummy_str);
+       if (unlikely(status))
+               return status;
 
        /* Throw away server_scope */
-       READ_BUF(4);
-       READ32(dummy);
-       READ_BUF(dummy);
+       status = decode_opaque_inline(xdr, &dummy, &dummy_str);
+       if (unlikely(status))
+               return status;
 
        /* Throw away Implementation id array */
-       READ_BUF(4);
-       READ32(dummy);
-       READ_BUF(dummy);
+       status = decode_opaque_inline(xdr, &dummy, &dummy_str);
+       if (unlikely(status))
+               return status;
 
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_chan_attrs(struct xdr_stream *xdr,
@@ -4232,22 +4463,35 @@ static int decode_chan_attrs(struct xdr_stream *xdr,
        __be32 *p;
        u32 nr_attrs;
 
-       READ_BUF(28);
-       READ32(attrs->headerpadsz);
-       READ32(attrs->max_rqst_sz);
-       READ32(attrs->max_resp_sz);
-       READ32(attrs->max_resp_sz_cached);
-       READ32(attrs->max_ops);
-       READ32(attrs->max_reqs);
-       READ32(nr_attrs);
+       p = xdr_inline_decode(xdr, 28);
+       if (unlikely(!p))
+               goto out_overflow;
+       attrs->headerpadsz = be32_to_cpup(p++);
+       attrs->max_rqst_sz = be32_to_cpup(p++);
+       attrs->max_resp_sz = be32_to_cpup(p++);
+       attrs->max_resp_sz_cached = be32_to_cpup(p++);
+       attrs->max_ops = be32_to_cpup(p++);
+       attrs->max_reqs = be32_to_cpup(p++);
+       nr_attrs = be32_to_cpup(p);
        if (unlikely(nr_attrs > 1)) {
                printk(KERN_WARNING "%s: Invalid rdma channel attrs count %u\n",
                        __func__, nr_attrs);
                return -EINVAL;
        }
-       if (nr_attrs == 1)
-               READ_BUF(4); /* skip rdma_attrs */
+       if (nr_attrs == 1) {
+               p = xdr_inline_decode(xdr, 4); /* skip rdma_attrs */
+               if (unlikely(!p))
+                       goto out_overflow;
+       }
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+static int decode_sessionid(struct xdr_stream *xdr, struct nfs4_sessionid *sid)
+{
+       return decode_opaque_fixed(xdr, sid->data, NFS4_MAX_SESSIONID_LEN);
 }
 
 static int decode_create_session(struct xdr_stream *xdr,
@@ -4259,24 +4503,26 @@ static int decode_create_session(struct xdr_stream *xdr,
        struct nfs4_session *session = clp->cl_session;
 
        status = decode_op_hdr(xdr, OP_CREATE_SESSION);
-
-       if (status)
+       if (!status)
+               status = decode_sessionid(xdr, &session->sess_id);
+       if (unlikely(status))
                return status;
 
-       /* sessionid */
-       READ_BUF(NFS4_MAX_SESSIONID_LEN);
-       COPYMEM(&session->sess_id, NFS4_MAX_SESSIONID_LEN);
-
        /* seqid, flags */
-       READ_BUF(8);
-       READ32(clp->cl_seqid);
-       READ32(session->flags);
+       p = xdr_inline_decode(xdr, 8);
+       if (unlikely(!p))
+               goto out_overflow;
+       clp->cl_seqid = be32_to_cpup(p++);
+       session->flags = be32_to_cpup(p);
 
        /* Channel attributes */
        status = decode_chan_attrs(xdr, &session->fc_attrs);
        if (!status)
                status = decode_chan_attrs(xdr, &session->bc_attrs);
        return status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 static int decode_destroy_session(struct xdr_stream *xdr, void *dummy)
@@ -4300,7 +4546,9 @@ static int decode_sequence(struct xdr_stream *xdr,
                return 0;
 
        status = decode_op_hdr(xdr, OP_SEQUENCE);
-       if (status)
+       if (!status)
+               status = decode_sessionid(xdr, &id);
+       if (unlikely(status))
                goto out_err;
 
        /*
@@ -4309,36 +4557,43 @@ static int decode_sequence(struct xdr_stream *xdr,
         */
        status = -ESERVERFAULT;
 
-       slot = &res->sr_session->fc_slot_table.slots[res->sr_slotid];
-       READ_BUF(NFS4_MAX_SESSIONID_LEN + 20);
-       COPYMEM(id.data, NFS4_MAX_SESSIONID_LEN);
        if (memcmp(id.data, res->sr_session->sess_id.data,
                   NFS4_MAX_SESSIONID_LEN)) {
                dprintk("%s Invalid session id\n", __func__);
                goto out_err;
        }
+
+       p = xdr_inline_decode(xdr, 20);
+       if (unlikely(!p))
+               goto out_overflow;
+
        /* seqid */
-       READ32(dummy);
+       slot = &res->sr_session->fc_slot_table.slots[res->sr_slotid];
+       dummy = be32_to_cpup(p++);
        if (dummy != slot->seq_nr) {
                dprintk("%s Invalid sequence number\n", __func__);
                goto out_err;
        }
        /* slot id */
-       READ32(dummy);
+       dummy = be32_to_cpup(p++);
        if (dummy != res->sr_slotid) {
                dprintk("%s Invalid slot id\n", __func__);
                goto out_err;
        }
        /* highest slot id - currently not processed */
-       READ32(dummy);
+       dummy = be32_to_cpup(p++);
        /* target highest slot id - currently not processed */
-       READ32(dummy);
+       dummy = be32_to_cpup(p++);
        /* result flags - currently not processed */
-       READ32(dummy);
+       dummy = be32_to_cpup(p);
        status = 0;
 out_err:
        res->sr_status = status;
        return status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       status = -EIO;
+       goto out_err;
 #else  /* CONFIG_NFS_V4_1 */
        return 0;
 #endif /* CONFIG_NFS_V4_1 */
@@ -4370,7 +4625,8 @@ static int nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp, __be32 *p, struct
        status = decode_open_downgrade(&xdr, res);
        if (status != 0)
                goto out;
-       decode_getfattr(&xdr, res->fattr, res->server);
+       decode_getfattr(&xdr, res->fattr, res->server,
+                       !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
 }
@@ -4397,7 +4653,8 @@ static int nfs4_xdr_dec_access(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_ac
        status = decode_access(&xdr, res);
        if (status != 0)
                goto out;
-       decode_getfattr(&xdr, res->fattr, res->server);
+       decode_getfattr(&xdr, res->fattr, res->server,
+                       !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
 }
@@ -4424,7 +4681,8 @@ static int nfs4_xdr_dec_lookup(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_lo
                goto out;
        if ((status = decode_getfh(&xdr, res->fh)) != 0)
                goto out;
-       status = decode_getfattr(&xdr, res->fattr, res->server);
+       status = decode_getfattr(&xdr, res->fattr, res->server
+                       ,!RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
 }
@@ -4448,7 +4706,8 @@ static int nfs4_xdr_dec_lookup_root(struct rpc_rqst *rqstp, __be32 *p, struct nf
        if ((status = decode_putrootfh(&xdr)) != 0)
                goto out;
        if ((status = decode_getfh(&xdr, res->fh)) == 0)
-               status = decode_getfattr(&xdr, res->fattr, res->server);
+               status = decode_getfattr(&xdr, res->fattr, res->server,
+                               !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
 }
@@ -4473,7 +4732,8 @@ static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, __be32 *p, struct nfs_rem
                goto out;
        if ((status = decode_remove(&xdr, &res->cinfo)) != 0)
                goto out;
-       decode_getfattr(&xdr, &res->dir_attr, res->server);
+       decode_getfattr(&xdr, &res->dir_attr, res->server,
+                       !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
 }
@@ -4503,11 +4763,13 @@ static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_re
        if ((status = decode_rename(&xdr, &res->old_cinfo, &res->new_cinfo)) != 0)
                goto out;
        /* Current FH is target directory */
-       if (decode_getfattr(&xdr, res->new_fattr, res->server) != 0)
+       if (decode_getfattr(&xdr, res->new_fattr, res->server,
+                               !RPC_IS_ASYNC(rqstp->rq_task)) != 0)
                goto out;
        if ((status = decode_restorefh(&xdr)) != 0)
                goto out;
-       decode_getfattr(&xdr, res->old_fattr, res->server);
+       decode_getfattr(&xdr, res->old_fattr, res->server,
+                       !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
 }
@@ -4540,11 +4802,13 @@ static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_link
         * Note order: OP_LINK leaves the directory as the current
         *             filehandle.
         */
-       if (decode_getfattr(&xdr, res->dir_attr, res->server) != 0)
+       if (decode_getfattr(&xdr, res->dir_attr, res->server,
+                               !RPC_IS_ASYNC(rqstp->rq_task)) != 0)
                goto out;
        if ((status = decode_restorefh(&xdr)) != 0)
                goto out;
-       decode_getfattr(&xdr, res->fattr, res->server);
+       decode_getfattr(&xdr, res->fattr, res->server,
+                       !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
 }
@@ -4573,11 +4837,13 @@ static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_cr
                goto out;
        if ((status = decode_getfh(&xdr, res->fh)) != 0)
                goto out;
-       if (decode_getfattr(&xdr, res->fattr, res->server) != 0)
+       if (decode_getfattr(&xdr, res->fattr, res->server,
+                               !RPC_IS_ASYNC(rqstp->rq_task)) != 0)
                goto out;
        if ((status = decode_restorefh(&xdr)) != 0)
                goto out;
-       decode_getfattr(&xdr, res->dir_fattr, res->server);
+       decode_getfattr(&xdr, res->dir_fattr, res->server,
+                       !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
 }
@@ -4609,7 +4875,8 @@ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_g
        status = decode_putfh(&xdr);
        if (status)
                goto out;
-       status = decode_getfattr(&xdr, res->fattr, res->server);
+       status = decode_getfattr(&xdr, res->fattr, res->server,
+                       !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
 }
@@ -4716,7 +4983,8 @@ static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, __be32 *p, struct nfs_clos
         *      an ESTALE error. Shouldn't be a problem,
         *      though, since fattr->valid will remain unset.
         */
-       decode_getfattr(&xdr, res->fattr, res->server);
+       decode_getfattr(&xdr, res->fattr, res->server,
+                       !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
 }
@@ -4748,11 +5016,13 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, __be32 *p, struct nfs_openr
                goto out;
        if (decode_getfh(&xdr, &res->fh) != 0)
                goto out;
-       if (decode_getfattr(&xdr, res->f_attr, res->server) != 0)
+       if (decode_getfattr(&xdr, res->f_attr, res->server,
+                               !RPC_IS_ASYNC(rqstp->rq_task)) != 0)
                goto out;
        if (decode_restorefh(&xdr) != 0)
                goto out;
-       decode_getfattr(&xdr, res->dir_attr, res->server);
+       decode_getfattr(&xdr, res->dir_attr, res->server,
+                       !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
 }
@@ -4800,7 +5070,8 @@ static int nfs4_xdr_dec_open_noattr(struct rpc_rqst *rqstp, __be32 *p, struct nf
        status = decode_open(&xdr, res);
        if (status)
                goto out;
-       decode_getfattr(&xdr, res->f_attr, res->server);
+       decode_getfattr(&xdr, res->f_attr, res->server,
+                       !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
 }
@@ -4827,7 +5098,8 @@ static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs_se
        status = decode_setattr(&xdr);
        if (status)
                goto out;
-       decode_getfattr(&xdr, res->fattr, res->server);
+       decode_getfattr(&xdr, res->fattr, res->server,
+                       !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
 }
@@ -5001,7 +5273,8 @@ static int nfs4_xdr_dec_write(struct rpc_rqst *rqstp, __be32 *p, struct nfs_writ
        status = decode_write(&xdr, res);
        if (status)
                goto out;
-       decode_getfattr(&xdr, res->fattr, res->server);
+       decode_getfattr(&xdr, res->fattr, res->server,
+                       !RPC_IS_ASYNC(rqstp->rq_task));
        if (!status)
                status = res->count;
 out:
@@ -5030,7 +5303,8 @@ static int nfs4_xdr_dec_commit(struct rpc_rqst *rqstp, __be32 *p, struct nfs_wri
        status = decode_commit(&xdr, res);
        if (status)
                goto out;
-       decode_getfattr(&xdr, res->fattr, res->server);
+       decode_getfattr(&xdr, res->fattr, res->server,
+                       !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
 }
@@ -5194,7 +5468,8 @@ static int nfs4_xdr_dec_delegreturn(struct rpc_rqst *rqstp, __be32 *p, struct nf
        if (status != 0)
                goto out;
        status = decode_delegreturn(&xdr);
-       decode_getfattr(&xdr, res->fattr, res->server);
+       decode_getfattr(&xdr, res->fattr, res->server,
+                       !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
 }
@@ -5222,7 +5497,8 @@ static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req, __be32 *p,
                goto out;
        xdr_enter_page(&xdr, PAGE_SIZE);
        status = decode_getfattr(&xdr, &res->fs_locations->fattr,
-                                res->fs_locations->server);
+                                res->fs_locations->server,
+                                !RPC_IS_ASYNC(req->rq_task));
 out:
        return status;
 }
index 0b4cbdc..867f705 100644 (file)
@@ -73,7 +73,7 @@ enum {
        Opt_cto, Opt_nocto,
        Opt_ac, Opt_noac,
        Opt_lock, Opt_nolock,
-       Opt_v2, Opt_v3,
+       Opt_v2, Opt_v3, Opt_v4,
        Opt_udp, Opt_tcp, Opt_rdma,
        Opt_acl, Opt_noacl,
        Opt_rdirplus, Opt_nordirplus,
@@ -127,6 +127,7 @@ static const match_table_t nfs_mount_option_tokens = {
        { Opt_nolock, "nolock" },
        { Opt_v2, "v2" },
        { Opt_v3, "v3" },
+       { Opt_v4, "v4" },
        { Opt_udp, "udp" },
        { Opt_tcp, "tcp" },
        { Opt_rdma, "rdma" },
@@ -158,7 +159,7 @@ static const match_table_t nfs_mount_option_tokens = {
        { Opt_mountvers, "mountvers=%s" },
        { Opt_nfsvers, "nfsvers=%s" },
        { Opt_nfsvers, "vers=%s" },
-       { Opt_minorversion, "minorversion=%u" },
+       { Opt_minorversion, "minorversion=%s" },
 
        { Opt_sec, "sec=%s" },
        { Opt_proto, "proto=%s" },
@@ -272,6 +273,10 @@ static const struct super_operations nfs_sops = {
 };
 
 #ifdef CONFIG_NFS_V4
+static int nfs4_validate_text_mount_data(void *options,
+       struct nfs_parsed_mount_data *args, const char *dev_name);
+static int nfs4_try_mount(int flags, const char *dev_name,
+       struct nfs_parsed_mount_data *data, struct vfsmount *mnt);
 static int nfs4_get_sb(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
 static int nfs4_remote_get_sb(struct file_system_type *fs_type,
@@ -742,127 +747,23 @@ static int nfs_verify_server_address(struct sockaddr *addr)
        }
        }
 
+       dfprintk(MOUNT, "NFS: Invalid IP address specified\n");
        return 0;
 }
 
-static void nfs_parse_ipv4_address(char *string, size_t str_len,
-                                  struct sockaddr *sap, size_t *addr_len)
-{
-       struct sockaddr_in *sin = (struct sockaddr_in *)sap;
-       u8 *addr = (u8 *)&sin->sin_addr.s_addr;
-
-       if (str_len <= INET_ADDRSTRLEN) {
-               dfprintk(MOUNT, "NFS: parsing IPv4 address %*s\n",
-                               (int)str_len, string);
-
-               sin->sin_family = AF_INET;
-               *addr_len = sizeof(*sin);
-               if (in4_pton(string, str_len, addr, '\0', NULL))
-                       return;
-       }
-
-       sap->sa_family = AF_UNSPEC;
-       *addr_len = 0;
-}
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static int nfs_parse_ipv6_scope_id(const char *string, const size_t str_len,
-                                  const char *delim,
-                                  struct sockaddr_in6 *sin6)
-{
-       char *p;
-       size_t len;
-
-       if ((string + str_len) == delim)
-               return 1;
-
-       if (*delim != IPV6_SCOPE_DELIMITER)
-               return 0;
-
-       if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL))
-               return 0;
-
-       len = (string + str_len) - delim - 1;
-       p = kstrndup(delim + 1, len, GFP_KERNEL);
-       if (p) {
-               unsigned long scope_id = 0;
-               struct net_device *dev;
-
-               dev = dev_get_by_name(&init_net, p);
-               if (dev != NULL) {
-                       scope_id = dev->ifindex;
-                       dev_put(dev);
-               } else {
-                       if (strict_strtoul(p, 10, &scope_id) == 0) {
-                               kfree(p);
-                               return 0;
-                       }
-               }
-
-               kfree(p);
-
-               sin6->sin6_scope_id = scope_id;
-               dfprintk(MOUNT, "NFS: IPv6 scope ID = %lu\n", scope_id);
-               return 1;
-       }
-
-       return 0;
-}
-
-static void nfs_parse_ipv6_address(char *string, size_t str_len,
-                                  struct sockaddr *sap, size_t *addr_len)
-{
-       struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
-       u8 *addr = (u8 *)&sin6->sin6_addr.in6_u;
-       const char *delim;
-
-       if (str_len <= INET6_ADDRSTRLEN) {
-               dfprintk(MOUNT, "NFS: parsing IPv6 address %*s\n",
-                               (int)str_len, string);
-
-               sin6->sin6_family = AF_INET6;
-               *addr_len = sizeof(*sin6);
-               if (in6_pton(string, str_len, addr,
-                                       IPV6_SCOPE_DELIMITER, &delim) != 0) {
-                       if (nfs_parse_ipv6_scope_id(string, str_len,
-                                                       delim, sin6) != 0)
-                               return;
-               }
-       }
-
-       sap->sa_family = AF_UNSPEC;
-       *addr_len = 0;
-}
-#else
-static void nfs_parse_ipv6_address(char *string, size_t str_len,
-                                  struct sockaddr *sap, size_t *addr_len)
-{
-       sap->sa_family = AF_UNSPEC;
-       *addr_len = 0;
-}
-#endif
-
 /*
- * Construct a sockaddr based on the contents of a string that contains
- * an IP address in presentation format.
- *
- * If there is a problem constructing the new sockaddr, set the address
- * family to AF_UNSPEC.
+ * Select between a default port value and a user-specified port value.
+ * If a zero value is set, then autobind will be used.
  */
-void nfs_parse_ip_address(char *string, size_t str_len,
-                                struct sockaddr *sap, size_t *addr_len)
+static void nfs_set_default_port(struct sockaddr *sap, const int parsed_port,
+                                const unsigned short default_port)
 {
-       unsigned int i, colons;
+       unsigned short port = default_port;
 
-       colons = 0;
-       for (i = 0; i < str_len; i++)
-               if (string[i] == ':')
-                       colons++;
+       if (parsed_port != NFS_UNSPEC_PORT)
+               port = parsed_port;
 
-       if (colons >= 2)
-               nfs_parse_ipv6_address(string, str_len, sap, addr_len);
-       else
-               nfs_parse_ipv4_address(string, str_len, sap, addr_len);
+       rpc_set_port(sap, port);
 }
 
 /*
@@ -904,8 +805,6 @@ static void nfs_set_mount_transport_protocol(struct nfs_parsed_mount_data *mnt)
 
 /*
  * Parse the value of the 'sec=' option.
- *
- * The flavor_len setting is for v4 mounts.
  */
 static int nfs_parse_security_flavors(char *value,
                                      struct nfs_parsed_mount_data *mnt)
@@ -916,53 +815,43 @@ static int nfs_parse_security_flavors(char *value,
 
        switch (match_token(value, nfs_secflavor_tokens, args)) {
        case Opt_sec_none:
-               mnt->auth_flavor_len = 0;
                mnt->auth_flavors[0] = RPC_AUTH_NULL;
                break;
        case Opt_sec_sys:
-               mnt->auth_flavor_len = 0;
                mnt->auth_flavors[0] = RPC_AUTH_UNIX;
                break;
        case Opt_sec_krb5:
-               mnt->auth_flavor_len = 1;
                mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5;
                break;
        case Opt_sec_krb5i:
-               mnt->auth_flavor_len = 1;
                mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5I;
                break;
        case Opt_sec_krb5p:
-               mnt->auth_flavor_len = 1;
                mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5P;
                break;
        case Opt_sec_lkey:
-               mnt->auth_flavor_len = 1;
                mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEY;
                break;
        case Opt_sec_lkeyi:
-               mnt->auth_flavor_len = 1;
                mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYI;
                break;
        case Opt_sec_lkeyp:
-               mnt->auth_flavor_len = 1;
                mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYP;
                break;
        case Opt_sec_spkm:
-               mnt->auth_flavor_len = 1;
                mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKM;
                break;
        case Opt_sec_spkmi:
-               mnt->auth_flavor_len = 1;
                mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMI;
                break;
        case Opt_sec_spkmp:
-               mnt->auth_flavor_len = 1;
                mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMP;
                break;
        default:
                return 0;
        }
 
+       mnt->auth_flavor_len = 1;
        return 1;
 }
 
@@ -1001,7 +890,6 @@ static int nfs_parse_mount_options(char *raw,
        while ((p = strsep(&raw, ",")) != NULL) {
                substring_t args[MAX_OPT_ARGS];
                unsigned long option;
-               int int_option;
                int token;
 
                if (!*p)
@@ -1047,10 +935,18 @@ static int nfs_parse_mount_options(char *raw,
                        break;
                case Opt_v2:
                        mnt->flags &= ~NFS_MOUNT_VER3;
+                       mnt->version = 2;
                        break;
                case Opt_v3:
                        mnt->flags |= NFS_MOUNT_VER3;
+                       mnt->version = 3;
                        break;
+#ifdef CONFIG_NFS_V4
+               case Opt_v4:
+                       mnt->flags &= ~NFS_MOUNT_VER3;
+                       mnt->version = 4;
+                       break;
+#endif
                case Opt_udp:
                        mnt->flags &= ~NFS_MOUNT_TCP;
                        mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP;
@@ -1264,20 +1160,33 @@ static int nfs_parse_mount_options(char *raw,
                        switch (option) {
                        case NFS2_VERSION:
                                mnt->flags &= ~NFS_MOUNT_VER3;
+                               mnt->version = 2;
                                break;
                        case NFS3_VERSION:
                                mnt->flags |= NFS_MOUNT_VER3;
+                               mnt->version = 3;
                                break;
+#ifdef CONFIG_NFS_V4
+                       case NFS4_VERSION:
+                               mnt->flags &= ~NFS_MOUNT_VER3;
+                               mnt->version = 4;
+                               break;
+#endif
                        default:
                                goto out_invalid_value;
                        }
                        break;
                case Opt_minorversion:
-                       if (match_int(args, &int_option))
-                               return 0;
-                       if (int_option < 0 || int_option > NFS4_MAX_MINOR_VERSION)
-                               return 0;
-                       mnt->minorversion = int_option;
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+                       rc = strict_strtoul(string, 10, &option);
+                       kfree(string);
+                       if (rc != 0)
+                               goto out_invalid_value;
+                       if (option > NFS4_MAX_MINOR_VERSION)
+                               goto out_invalid_value;
+                       mnt->minorversion = option;
                        break;
 
                /*
@@ -1352,11 +1261,14 @@ static int nfs_parse_mount_options(char *raw,
                        string = match_strdup(args);
                        if (string == NULL)
                                goto out_nomem;
-                       nfs_parse_ip_address(string, strlen(string),
-                                            (struct sockaddr *)
-                                               &mnt->nfs_server.address,
-                                            &mnt->nfs_server.addrlen);
+                       mnt->nfs_server.addrlen =
+                               rpc_pton(string, strlen(string),
+                                       (struct sockaddr *)
+                                       &mnt->nfs_server.address,
+                                       sizeof(mnt->nfs_server.address));
                        kfree(string);
+                       if (mnt->nfs_server.addrlen == 0)
+                               goto out_invalid_address;
                        break;
                case Opt_clientaddr:
                        string = match_strdup(args);
@@ -1376,11 +1288,14 @@ static int nfs_parse_mount_options(char *raw,
                        string = match_strdup(args);
                        if (string == NULL)
                                goto out_nomem;
-                       nfs_parse_ip_address(string, strlen(string),
-                                            (struct sockaddr *)
-                                               &mnt->mount_server.address,
-                                            &mnt->mount_server.addrlen);
+                       mnt->mount_server.addrlen =
+                               rpc_pton(string, strlen(string),
+                                       (struct sockaddr *)
+                                       &mnt->mount_server.address,
+                                       sizeof(mnt->mount_server.address));
                        kfree(string);
+                       if (mnt->mount_server.addrlen == 0)
+                               goto out_invalid_address;
                        break;
                case Opt_lookupcache:
                        string = match_strdup(args);
@@ -1432,8 +1347,11 @@ static int nfs_parse_mount_options(char *raw,
 
        return 1;
 
+out_invalid_address:
+       printk(KERN_INFO "NFS: bad IP address specified: %s\n", p);
+       return 0;
 out_invalid_value:
-       printk(KERN_INFO "NFS: bad mount option value specified: %s \n", p);
+       printk(KERN_INFO "NFS: bad mount option value specified: %s\n", p);
        return 0;
 out_nomem:
        printk(KERN_INFO "NFS: not enough memory to parse option\n");
@@ -1445,13 +1363,60 @@ out_security_failure:
 }
 
 /*
+ * Match the requested auth flavors with the list returned by
+ * the server.  Returns zero and sets the mount's authentication
+ * flavor on success; returns -EACCES if server does not support
+ * the requested flavor.
+ */
+static int nfs_walk_authlist(struct nfs_parsed_mount_data *args,
+                            struct nfs_mount_request *request)
+{
+       unsigned int i, j, server_authlist_len = *(request->auth_flav_len);
+
+       /*
+        * Certain releases of Linux's mountd return an empty
+        * flavor list.  To prevent behavioral regression with
+        * these servers (ie. rejecting mounts that used to
+        * succeed), revert to pre-2.6.32 behavior (no checking)
+        * if the returned flavor list is empty.
+        */
+       if (server_authlist_len == 0)
+               return 0;
+
+       /*
+        * We avoid sophisticated negotiating here, as there are
+        * plenty of cases where we can get it wrong, providing
+        * either too little or too much security.
+        *
+        * RFC 2623, section 2.7 suggests we SHOULD prefer the
+        * flavor listed first.  However, some servers list
+        * AUTH_NULL first.  Our caller plants AUTH_SYS, the
+        * preferred default, in args->auth_flavors[0] if user
+        * didn't specify sec= mount option.
+        */
+       for (i = 0; i < args->auth_flavor_len; i++)
+               for (j = 0; j < server_authlist_len; j++)
+                       if (args->auth_flavors[i] == request->auth_flavs[j]) {
+                               dfprintk(MOUNT, "NFS: using auth flavor %d\n",
+                                       request->auth_flavs[j]);
+                               args->auth_flavors[0] = request->auth_flavs[j];
+                               return 0;
+                       }
+
+       dfprintk(MOUNT, "NFS: server does not support requested auth flavor\n");
+       nfs_umount(request);
+       return -EACCES;
+}
+
+/*
  * Use the remote server's MOUNT service to request the NFS file handle
  * corresponding to the provided path.
  */
 static int nfs_try_mount(struct nfs_parsed_mount_data *args,
                         struct nfs_fh *root_fh)
 {
-       unsigned int auth_flavor_len = 0;
+       rpc_authflavor_t server_authlist[NFS_MAX_SECFLAVORS];
+       unsigned int server_authlist_len = ARRAY_SIZE(server_authlist);
        struct nfs_mount_request request = {
                .sap            = (struct sockaddr *)
                                                &args->mount_server.address,
@@ -1459,7 +1424,8 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
                .protocol       = args->mount_server.protocol,
                .fh             = root_fh,
                .noresvport     = args->flags & NFS_MOUNT_NORESVPORT,
-               .auth_flav_len  = &auth_flavor_len,
+               .auth_flav_len  = &server_authlist_len,
+               .auth_flavs     = server_authlist,
        };
        int status;
 
@@ -1485,23 +1451,25 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
                args->mount_server.addrlen = args->nfs_server.addrlen;
        }
        request.salen = args->mount_server.addrlen;
-
-       /*
-        * autobind will be used if mount_server.port == 0
-        */
-       nfs_set_port(request.sap, args->mount_server.port);
+       nfs_set_default_port(request.sap, args->mount_server.port, 0);
 
        /*
         * Now ask the mount server to map our export path
         * to a file handle.
         */
        status = nfs_mount(&request);
-       if (status == 0)
-               return 0;
+       if (status != 0) {
+               dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n",
+                               request.hostname, status);
+               return status;
+       }
 
-       dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n",
-                       request.hostname, status);
-       return status;
+       /*
+        * MNTv1 (NFSv2) does not support auth flavor negotiation.
+        */
+       if (args->mount_server.version != NFS_MNT3_VERSION)
+               return 0;
+       return nfs_walk_authlist(args, &request);
 }
 
 static int nfs_parse_simple_hostname(const char *dev_name,
@@ -1661,6 +1629,7 @@ static int nfs_validate_mount_data(void *options,
                                   const char *dev_name)
 {
        struct nfs_mount_data *data = (struct nfs_mount_data *)options;
+       struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address;
 
        if (data == NULL)
                goto out_no_data;
@@ -1672,10 +1641,12 @@ static int nfs_validate_mount_data(void *options,
        args->acregmax          = NFS_DEF_ACREGMAX;
        args->acdirmin          = NFS_DEF_ACDIRMIN;
        args->acdirmax          = NFS_DEF_ACDIRMAX;
-       args->mount_server.port = 0;    /* autobind unless user sets port */
-       args->nfs_server.port   = 0;    /* autobind unless user sets port */
+       args->mount_server.port = NFS_UNSPEC_PORT;
+       args->nfs_server.port   = NFS_UNSPEC_PORT;
        args->nfs_server.protocol = XPRT_TRANSPORT_TCP;
        args->auth_flavors[0]   = RPC_AUTH_UNIX;
+       args->auth_flavor_len   = 1;
+       args->minorversion      = 0;
 
        switch (data->version) {
        case 1:
@@ -1697,8 +1668,11 @@ static int nfs_validate_mount_data(void *options,
                        if (data->root.size > NFS3_FHSIZE || data->root.size == 0)
                                goto out_invalid_fh;
                        mntfh->size = data->root.size;
-               } else
+                       args->version = 3;
+               } else {
                        mntfh->size = NFS2_FHSIZE;
+                       args->version = 2;
+               }
 
 
                memcpy(mntfh->data, data->root.data, mntfh->size);
@@ -1720,11 +1694,9 @@ static int nfs_validate_mount_data(void *options,
                args->acdirmin          = data->acdirmin;
                args->acdirmax          = data->acdirmax;
 
-               memcpy(&args->nfs_server.address, &data->addr,
-                      sizeof(data->addr));
+               memcpy(sap, &data->addr, sizeof(data->addr));
                args->nfs_server.addrlen = sizeof(data->addr);
-               if (!nfs_verify_server_address((struct sockaddr *)
-                                               &args->nfs_server.address))
+               if (!nfs_verify_server_address(sap))
                        goto out_no_address;
 
                if (!(data->flags & NFS_MOUNT_TCP))
@@ -1772,12 +1744,18 @@ static int nfs_validate_mount_data(void *options,
                if (nfs_parse_mount_options((char *)options, args) == 0)
                        return -EINVAL;
 
-               if (!nfs_verify_server_address((struct sockaddr *)
-                                               &args->nfs_server.address))
+               if (!nfs_verify_server_address(sap))
                        goto out_no_address;
 
-               nfs_set_port((struct sockaddr *)&args->nfs_server.address,
-                               args->nfs_server.port);
+               if (args->version == 4)
+#ifdef CONFIG_NFS_V4
+                       return nfs4_validate_text_mount_data(options,
+                                                            args, dev_name);
+#else
+                       goto out_v4_not_compiled;
+#endif
+
+               nfs_set_default_port(sap, args->nfs_server.port, 0);
 
                nfs_set_mount_transport_protocol(args);
 
@@ -1825,6 +1803,12 @@ out_v3_not_compiled:
        return -EPROTONOSUPPORT;
 #endif /* !CONFIG_NFS_V3 */
 
+#ifndef CONFIG_NFS_V4
+out_v4_not_compiled:
+       dfprintk(MOUNT, "NFS: NFSv4 is not compiled into kernel\n");
+       return -EPROTONOSUPPORT;
+#endif /* !CONFIG_NFS_V4 */
+
 out_nomem:
        dfprintk(MOUNT, "NFS: not enough memory to handle mount options\n");
        return -ENOMEM;
@@ -2120,6 +2104,14 @@ static int nfs_get_sb(struct file_system_type *fs_type,
        if (error < 0)
                goto out;
 
+#ifdef CONFIG_NFS_V4
+       if (data->version == 4) {
+               error = nfs4_try_mount(flags, dev_name, data, mnt);
+               kfree(data->client_address);
+               goto out;
+       }
+#endif /* CONFIG_NFS_V4 */
+
        /* Get a volume representation */
        server = nfs_create_server(data, mntfh);
        if (IS_ERR(server)) {
@@ -2317,6 +2309,43 @@ static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args)
        args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3);
 }
 
+static int nfs4_validate_text_mount_data(void *options,
+                                        struct nfs_parsed_mount_data *args,
+                                        const char *dev_name)
+{
+       struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address;
+
+       nfs_set_default_port(sap, args->nfs_server.port, NFS_PORT);
+
+       nfs_validate_transport_protocol(args);
+
+       nfs4_validate_mount_flags(args);
+
+       if (args->version != 4) {
+               dfprintk(MOUNT,
+                        "NFS4: Illegal mount version\n");
+               return -EINVAL;
+       }
+
+       if (args->auth_flavor_len > 1) {
+               dfprintk(MOUNT,
+                        "NFS4: Too many RPC auth flavours specified\n");
+               return -EINVAL;
+       }
+
+       if (args->client_address == NULL) {
+               dfprintk(MOUNT,
+                        "NFS4: mount program didn't pass callback address\n");
+               return -EINVAL;
+       }
+
+       return nfs_parse_devname(dev_name,
+                                  &args->nfs_server.hostname,
+                                  NFS4_MAXNAMLEN,
+                                  &args->nfs_server.export_path,
+                                  NFS4_MAXPATHLEN);
+}
+
 /*
  * Validate NFSv4 mount options
  */
@@ -2324,7 +2353,7 @@ static int nfs4_validate_mount_data(void *options,
                                    struct nfs_parsed_mount_data *args,
                                    const char *dev_name)
 {
-       struct sockaddr_in *ap;
+       struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address;
        struct nfs4_mount_data *data = (struct nfs4_mount_data *)options;
        char *c;
 
@@ -2337,23 +2366,22 @@ static int nfs4_validate_mount_data(void *options,
        args->acregmax          = NFS_DEF_ACREGMAX;
        args->acdirmin          = NFS_DEF_ACDIRMIN;
        args->acdirmax          = NFS_DEF_ACDIRMAX;
-       args->nfs_server.port   = NFS_PORT; /* 2049 unless user set port= */
+       args->nfs_server.port   = NFS_UNSPEC_PORT;
        args->auth_flavors[0]   = RPC_AUTH_UNIX;
-       args->auth_flavor_len   = 0;
+       args->auth_flavor_len   = 1;
+       args->version           = 4;
        args->minorversion      = 0;
 
        switch (data->version) {
        case 1:
-               ap = (struct sockaddr_in *)&args->nfs_server.address;
                if (data->host_addrlen > sizeof(args->nfs_server.address))
                        goto out_no_address;
                if (data->host_addrlen == 0)
                        goto out_no_address;
                args->nfs_server.addrlen = data->host_addrlen;
-               if (copy_from_user(ap, data->host_addr, data->host_addrlen))
+               if (copy_from_user(sap, data->host_addr, data->host_addrlen))
                        return -EFAULT;
-               if (!nfs_verify_server_address((struct sockaddr *)
-                                               &args->nfs_server.address))
+               if (!nfs_verify_server_address(sap))
                        goto out_no_address;
 
                if (data->auth_flavourlen) {
@@ -2399,39 +2427,14 @@ static int nfs4_validate_mount_data(void *options,
                nfs_validate_transport_protocol(args);
 
                break;
-       default: {
-               int status;
-
+       default:
                if (nfs_parse_mount_options((char *)options, args) == 0)
                        return -EINVAL;
 
-               if (!nfs_verify_server_address((struct sockaddr *)
-                                               &args->nfs_server.address))
+               if (!nfs_verify_server_address(sap))
                        return -EINVAL;
 
-               nfs_set_port((struct sockaddr *)&args->nfs_server.address,
-                               args->nfs_server.port);
-
-               nfs_validate_transport_protocol(args);
-
-               nfs4_validate_mount_flags(args);
-
-               if (args->auth_flavor_len > 1)
-                       goto out_inval_auth;
-
-               if (args->client_address == NULL)
-                       goto out_no_client_address;
-
-               status = nfs_parse_devname(dev_name,
-                                          &args->nfs_server.hostname,
-                                          NFS4_MAXNAMLEN,
-                                          &args->nfs_server.export_path,
-                                          NFS4_MAXPATHLEN);
-               if (status < 0)
-                       return status;
-
-               break;
-               }
+               return nfs4_validate_text_mount_data(options, args, dev_name);
        }
 
        return 0;
@@ -2448,10 +2451,6 @@ out_inval_auth:
 out_no_address:
        dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n");
        return -EINVAL;
-
-out_no_client_address:
-       dfprintk(MOUNT, "NFS4: mount program didn't pass callback address\n");
-       return -EINVAL;
 }
 
 /*
@@ -2618,6 +2617,34 @@ out_err:
        return ret;
 }
 
+static int nfs4_try_mount(int flags, const char *dev_name,
+                        struct nfs_parsed_mount_data *data,
+                        struct vfsmount *mnt)
+{
+       char *export_path;
+       struct vfsmount *root_mnt;
+       int error;
+
+       dfprintk(MOUNT, "--> nfs4_try_mount()\n");
+
+       export_path = data->nfs_server.export_path;
+       data->nfs_server.export_path = "/";
+       root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, data,
+                       data->nfs_server.hostname);
+       data->nfs_server.export_path = export_path;
+
+       error = PTR_ERR(root_mnt);
+       if (IS_ERR(root_mnt))
+               goto out;
+
+       error = nfs_follow_remote_path(root_mnt, export_path, mnt);
+
+out:
+       dfprintk(MOUNT, "<-- nfs4_try_mount() = %d%s\n", error,
+                       error != 0 ? " [error]" : "");
+       return error;
+}
+
 /*
  * Get the superblock for an NFS4 mountpoint
  */
@@ -2625,8 +2652,6 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
 {
        struct nfs_parsed_mount_data *data;
-       char *export_path;
-       struct vfsmount *root_mnt;
        int error = -ENOMEM;
 
        data = kzalloc(sizeof(*data), GFP_KERNEL);
@@ -2638,17 +2663,7 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
        if (error < 0)
                goto out;
 
-       export_path = data->nfs_server.export_path;
-       data->nfs_server.export_path = "/";
-       root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, data,
-                       data->nfs_server.hostname);
-       data->nfs_server.export_path = export_path;
-
-       error = PTR_ERR(root_mnt);
-       if (IS_ERR(root_mnt))
-               goto out;
-
-       error = nfs_follow_remote_path(root_mnt, export_path, mnt);
+       error = nfs4_try_mount(flags, dev_name, data, mnt);
 
 out:
        kfree(data->client_address);
index a34fae2..120acad 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/file.h>
 #include <linux/writeback.h>
 #include <linux/swap.h>
+#include <linux/migrate.h>
 
 #include <linux/sunrpc/clnt.h>
 #include <linux/nfs_fs.h>
@@ -26,6 +27,7 @@
 #include "internal.h"
 #include "iostat.h"
 #include "nfs4_fs.h"
+#include "fscache.h"
 
 #define NFSDBG_FACILITY                NFSDBG_PAGECACHE
 
@@ -218,24 +220,17 @@ static void nfs_end_page_writeback(struct page *page)
                clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC);
 }
 
-/*
- * Find an associated nfs write request, and prepare to flush it out
- * May return an error if the user signalled nfs_wait_on_request().
- */
-static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
-                               struct page *page)
+static struct nfs_page *nfs_find_and_lock_request(struct page *page)
 {
        struct inode *inode = page->mapping->host;
        struct nfs_page *req;
        int ret;
 
        spin_lock(&inode->i_lock);
-       for(;;) {
+       for (;;) {
                req = nfs_page_find_request_locked(page);
-               if (req == NULL) {
-                       spin_unlock(&inode->i_lock);
-                       return 0;
-               }
+               if (req == NULL)
+                       break;
                if (nfs_set_page_tag_locked(req))
                        break;
                /* Note: If we hold the page lock, as is the case in nfs_writepage,
@@ -247,23 +242,40 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
                ret = nfs_wait_on_request(req);
                nfs_release_request(req);
                if (ret != 0)
-                       return ret;
+                       return ERR_PTR(ret);
                spin_lock(&inode->i_lock);
        }
-       if (test_bit(PG_CLEAN, &req->wb_flags)) {
-               spin_unlock(&inode->i_lock);
-               BUG();
-       }
-       if (nfs_set_page_writeback(page) != 0) {
-               spin_unlock(&inode->i_lock);
-               BUG();
-       }
        spin_unlock(&inode->i_lock);
+       return req;
+}
+
+/*
+ * Find an associated nfs write request, and prepare to flush it out
+ * May return an error if the user signalled nfs_wait_on_request().
+ */
+static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
+                               struct page *page)
+{
+       struct nfs_page *req;
+       int ret = 0;
+
+       req = nfs_find_and_lock_request(page);
+       if (!req)
+               goto out;
+       ret = PTR_ERR(req);
+       if (IS_ERR(req))
+               goto out;
+
+       ret = nfs_set_page_writeback(page);
+       BUG_ON(ret != 0);
+       BUG_ON(test_bit(PG_CLEAN, &req->wb_flags));
+
        if (!nfs_pageio_add_request(pgio, req)) {
                nfs_redirty_request(req);
-               return pgio->pg_error;
+               ret = pgio->pg_error;
        }
-       return 0;
+out:
+       return ret;
 }
 
 static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, struct nfs_pageio_descriptor *pgio)
@@ -1580,6 +1592,41 @@ int nfs_wb_page(struct inode *inode, struct page* page)
        return nfs_wb_page_priority(inode, page, FLUSH_STABLE);
 }
 
+#ifdef CONFIG_MIGRATION
+int nfs_migrate_page(struct address_space *mapping, struct page *newpage,
+               struct page *page)
+{
+       struct nfs_page *req;
+       int ret;
+
+       if (PageFsCache(page))
+               nfs_fscache_release_page(page, GFP_KERNEL);
+
+       req = nfs_find_and_lock_request(page);
+       ret = PTR_ERR(req);
+       if (IS_ERR(req))
+               goto out;
+
+       ret = migrate_page(mapping, newpage, page);
+       if (!req)
+               goto out;
+       if (ret)
+               goto out_unlock;
+       page_cache_get(newpage);
+       req->wb_page = newpage;
+       SetPagePrivate(newpage);
+       set_page_private(newpage, page_private(page));
+       ClearPagePrivate(page);
+       set_page_private(page, 0);
+       page_cache_release(page);
+out_unlock:
+       nfs_clear_page_tag_locked(req);
+       nfs_release_request(req);
+out:
+       return ret;
+}
+#endif
+
 int __init nfs_init_writepagecache(void)
 {
        nfs_wdata_cachep = kmem_cache_create("nfs_write_data",
index b92a276..d946264 100644 (file)
@@ -85,6 +85,11 @@ static void expkey_request(struct cache_detail *cd,
        (*bpp)[-1] = '\n';
 }
 
+static int expkey_upcall(struct cache_detail *cd, struct cache_head *h)
+{
+       return sunrpc_cache_pipe_upcall(cd, h, expkey_request);
+}
+
 static struct svc_expkey *svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old);
 static struct svc_expkey *svc_expkey_lookup(struct svc_expkey *);
 static struct cache_detail svc_expkey_cache;
@@ -259,7 +264,7 @@ static struct cache_detail svc_expkey_cache = {
        .hash_table     = expkey_table,
        .name           = "nfsd.fh",
        .cache_put      = expkey_put,
-       .cache_request  = expkey_request,
+       .cache_upcall   = expkey_upcall,
        .cache_parse    = expkey_parse,
        .cache_show     = expkey_show,
        .match          = expkey_match,
@@ -355,6 +360,11 @@ static void svc_export_request(struct cache_detail *cd,
        (*bpp)[-1] = '\n';
 }
 
+static int svc_export_upcall(struct cache_detail *cd, struct cache_head *h)
+{
+       return sunrpc_cache_pipe_upcall(cd, h, svc_export_request);
+}
+
 static struct svc_export *svc_export_update(struct svc_export *new,
                                            struct svc_export *old);
 static struct svc_export *svc_export_lookup(struct svc_export *);
@@ -724,7 +734,7 @@ struct cache_detail svc_export_cache = {
        .hash_table     = export_table,
        .name           = "nfsd.export",
        .cache_put      = svc_export_put,
-       .cache_request  = svc_export_request,
+       .cache_upcall   = svc_export_upcall,
        .cache_parse    = svc_export_parse,
        .cache_show     = svc_export_show,
        .match          = svc_export_match,
index 5b39842..cdfa86f 100644 (file)
@@ -146,6 +146,12 @@ idtoname_request(struct cache_detail *cd, struct cache_head *ch, char **bpp,
 }
 
 static int
+idtoname_upcall(struct cache_detail *cd, struct cache_head *ch)
+{
+       return sunrpc_cache_pipe_upcall(cd, ch, idtoname_request);
+}
+
+static int
 idtoname_match(struct cache_head *ca, struct cache_head *cb)
 {
        struct ent *a = container_of(ca, struct ent, h);
@@ -175,10 +181,10 @@ idtoname_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h)
 }
 
 static void
-warn_no_idmapd(struct cache_detail *detail)
+warn_no_idmapd(struct cache_detail *detail, int has_died)
 {
        printk("nfsd: nfsv4 idmapping failing: has idmapd %s?\n",
-                       detail->last_close? "died" : "not been started");
+                       has_died ? "died" : "not been started");
 }
 
 
@@ -192,7 +198,7 @@ static struct cache_detail idtoname_cache = {
        .hash_table     = idtoname_table,
        .name           = "nfs4.idtoname",
        .cache_put      = ent_put,
-       .cache_request  = idtoname_request,
+       .cache_upcall   = idtoname_upcall,
        .cache_parse    = idtoname_parse,
        .cache_show     = idtoname_show,
        .warn_no_listener = warn_no_idmapd,
@@ -325,6 +331,12 @@ nametoid_request(struct cache_detail *cd, struct cache_head *ch, char **bpp,
 }
 
 static int
+nametoid_upcall(struct cache_detail *cd, struct cache_head *ch)
+{
+       return sunrpc_cache_pipe_upcall(cd, ch, nametoid_request);
+}
+
+static int
 nametoid_match(struct cache_head *ca, struct cache_head *cb)
 {
        struct ent *a = container_of(ca, struct ent, h);
@@ -363,7 +375,7 @@ static struct cache_detail nametoid_cache = {
        .hash_table     = nametoid_table,
        .name           = "nfs4.nametoid",
        .cache_put      = ent_put,
-       .cache_request  = nametoid_request,
+       .cache_upcall   = nametoid_upcall,
        .cache_parse    = nametoid_parse,
        .cache_show     = nametoid_show,
        .warn_no_listener = warn_no_idmapd,
index 6d08475..7e906c5 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/nfsd/xdr.h>
 #include <linux/nfsd/syscall.h>
 #include <linux/lockd/lockd.h>
+#include <linux/sunrpc/clnt.h>
 
 #include <asm/uaccess.h>
 #include <net/ipv6.h>
@@ -490,22 +491,18 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size)
  *
  * Input:
  *                     buf:    '\n'-terminated C string containing a
- *                             presentation format IPv4 address
+ *                             presentation format IP address
  *                     size:   length of C string in @buf
  * Output:
  *     On success:     returns zero if all specified locks were released;
  *                     returns one if one or more locks were not released
  *     On error:       return code is negative errno value
- *
- * Note: Only AF_INET client addresses are passed in
  */
 static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
 {
-       struct sockaddr_in sin = {
-               .sin_family     = AF_INET,
-       };
-       int b1, b2, b3, b4;
-       char c;
+       struct sockaddr_storage address;
+       struct sockaddr *sap = (struct sockaddr *)&address;
+       size_t salen = sizeof(address);
        char *fo_path;
 
        /* sanity check */
@@ -519,14 +516,10 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
        if (qword_get(&buf, fo_path, size) < 0)
                return -EINVAL;
 
-       /* get ipv4 address */
-       if (sscanf(fo_path, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) != 4)
-               return -EINVAL;
-       if (b1 > 255 || b2 > 255 || b3 > 255 || b4 > 255)
+       if (rpc_pton(fo_path, size, sap, salen) == 0)
                return -EINVAL;
-       sin.sin_addr.s_addr = htonl((b1 << 24) | (b2 << 16) | (b3 << 8) | b4);
 
-       return nlmsvc_unlock_all_by_ip((struct sockaddr *)&sin);
+       return nlmsvc_unlock_all_by_ip(sap);
 }
 
 /**
index bd2eba5..33b2836 100644 (file)
@@ -472,6 +472,7 @@ enum lock_type4 {
 
 #define NFSPROC4_NULL 0
 #define NFSPROC4_COMPOUND 1
+#define NFS4_VERSION 4
 #define NFS4_MINOR_VERSION 0
 
 #if defined(CONFIG_NFS_V4_1)
index 19fe15d..320569e 100644 (file)
@@ -167,6 +167,15 @@ struct nfs_server {
 #define NFS_CAP_SYMLINKS       (1U << 2)
 #define NFS_CAP_ACLS           (1U << 3)
 #define NFS_CAP_ATOMIC_OPEN    (1U << 4)
+#define NFS_CAP_CHANGE_ATTR    (1U << 5)
+#define NFS_CAP_FILEID         (1U << 6)
+#define NFS_CAP_MODE           (1U << 7)
+#define NFS_CAP_NLINK          (1U << 8)
+#define NFS_CAP_OWNER          (1U << 9)
+#define NFS_CAP_OWNER_GROUP    (1U << 10)
+#define NFS_CAP_ATIME          (1U << 11)
+#define NFS_CAP_CTIME          (1U << 12)
+#define NFS_CAP_MTIME          (1U << 13)
 
 
 /* maximum number of slots to use */
index 2d8b211..6f52b4d 100644 (file)
@@ -59,6 +59,15 @@ struct cache_head {
 
 #define        CACHE_NEW_EXPIRY 120    /* keep new things pending confirmation for 120 seconds */
 
+struct cache_detail_procfs {
+       struct proc_dir_entry   *proc_ent;
+       struct proc_dir_entry   *flush_ent, *channel_ent, *content_ent;
+};
+
+struct cache_detail_pipefs {
+       struct dentry *dir;
+};
+
 struct cache_detail {
        struct module *         owner;
        int                     hash_size;
@@ -70,15 +79,17 @@ struct cache_detail {
        char                    *name;
        void                    (*cache_put)(struct kref *);
 
-       void                    (*cache_request)(struct cache_detail *cd,
-                                                struct cache_head *h,
-                                                char **bpp, int *blen);
+       int                     (*cache_upcall)(struct cache_detail *,
+                                               struct cache_head *);
+
        int                     (*cache_parse)(struct cache_detail *,
                                               char *buf, int len);
 
        int                     (*cache_show)(struct seq_file *m,
                                              struct cache_detail *cd,
                                              struct cache_head *h);
+       void                    (*warn_no_listener)(struct cache_detail *cd,
+                                             int has_died);
 
        struct cache_head *     (*alloc)(void);
        int                     (*match)(struct cache_head *orig, struct cache_head *new);
@@ -96,13 +107,15 @@ struct cache_detail {
 
        /* fields for communication over channel */
        struct list_head        queue;
-       struct proc_dir_entry   *proc_ent;
-       struct proc_dir_entry   *flush_ent, *channel_ent, *content_ent;
 
        atomic_t                readers;                /* how many time is /chennel open */
        time_t                  last_close;             /* if no readers, when did last close */
        time_t                  last_warn;              /* when we last warned about no readers */
-       void                    (*warn_no_listener)(struct cache_detail *cd);
+
+       union {
+               struct cache_detail_procfs procfs;
+               struct cache_detail_pipefs pipefs;
+       } u;
 };
 
 
@@ -127,6 +140,10 @@ struct cache_deferred_req {
 };
 
 
+extern const struct file_operations cache_file_operations_pipefs;
+extern const struct file_operations content_file_operations_pipefs;
+extern const struct file_operations cache_flush_operations_pipefs;
+
 extern struct cache_head *
 sunrpc_cache_lookup(struct cache_detail *detail,
                    struct cache_head *key, int hash);
@@ -134,6 +151,13 @@ extern struct cache_head *
 sunrpc_cache_update(struct cache_detail *detail,
                    struct cache_head *new, struct cache_head *old, int hash);
 
+extern int
+sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h,
+               void (*cache_request)(struct cache_detail *,
+                                     struct cache_head *,
+                                     char **,
+                                     int *));
+
 
 extern void cache_clean_deferred(void *owner);
 
@@ -171,6 +195,10 @@ extern void cache_purge(struct cache_detail *detail);
 extern int cache_register(struct cache_detail *cd);
 extern void cache_unregister(struct cache_detail *cd);
 
+extern int sunrpc_cache_register_pipefs(struct dentry *parent, const char *,
+                                       mode_t, struct cache_detail *);
+extern void sunrpc_cache_unregister_pipefs(struct cache_detail *);
+
 extern void qword_add(char **bpp, int *lp, char *str);
 extern void qword_addhex(char **bpp, int *lp, char *buf, int blen);
 extern int qword_get(char **bpp, char *dest, int bufsize);
index 37881f1..ab3f6e9 100644 (file)
@@ -9,6 +9,10 @@
 #ifndef _LINUX_SUNRPC_CLNT_H
 #define _LINUX_SUNRPC_CLNT_H
 
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+
 #include <linux/sunrpc/msg_prot.h>
 #include <linux/sunrpc/sched.h>
 #include <linux/sunrpc/xprt.h>
@@ -17,6 +21,7 @@
 #include <linux/sunrpc/xdr.h>
 #include <linux/sunrpc/timer.h>
 #include <asm/signal.h>
+#include <linux/path.h>
 
 struct rpc_inode;
 
@@ -50,9 +55,7 @@ struct rpc_clnt {
 
        int                     cl_nodelen;     /* nodename length */
        char                    cl_nodename[UNX_MAXNODENAME];
-       char                    cl_pathname[30];/* Path in rpc_pipe_fs */
-       struct vfsmount *       cl_vfsmnt;
-       struct dentry *         cl_dentry;      /* inode */
+       struct path             cl_path;
        struct rpc_clnt *       cl_parent;      /* Points to parent of clones */
        struct rpc_rtt          cl_rtt_default;
        struct rpc_timeout      cl_timeout_default;
@@ -151,5 +154,39 @@ void               rpc_force_rebind(struct rpc_clnt *);
 size_t         rpc_peeraddr(struct rpc_clnt *, struct sockaddr *, size_t);
 const char     *rpc_peeraddr2str(struct rpc_clnt *, enum rpc_display_format_t);
 
+size_t         rpc_ntop(const struct sockaddr *, char *, const size_t);
+size_t         rpc_pton(const char *, const size_t,
+                        struct sockaddr *, const size_t);
+char *         rpc_sockaddr2uaddr(const struct sockaddr *);
+size_t         rpc_uaddr2sockaddr(const char *, const size_t,
+                                  struct sockaddr *, const size_t);
+
+static inline unsigned short rpc_get_port(const struct sockaddr *sap)
+{
+       switch (sap->sa_family) {
+       case AF_INET:
+               return ntohs(((struct sockaddr_in *)sap)->sin_port);
+       case AF_INET6:
+               return ntohs(((struct sockaddr_in6 *)sap)->sin6_port);
+       }
+       return 0;
+}
+
+static inline void rpc_set_port(struct sockaddr *sap,
+                               const unsigned short port)
+{
+       switch (sap->sa_family) {
+       case AF_INET:
+               ((struct sockaddr_in *)sap)->sin_port = htons(port);
+               break;
+       case AF_INET6:
+               ((struct sockaddr_in6 *)sap)->sin6_port = htons(port);
+               break;
+       }
+}
+
+#define IPV6_SCOPE_DELIMITER           '%'
+#define IPV6_SCOPE_ID_LEN              sizeof("%nnnnnnnnnn")
+
 #endif /* __KERNEL__ */
 #endif /* _LINUX_SUNRPC_CLNT_H */
index 70df4f1..77e6248 100644 (file)
@@ -189,7 +189,22 @@ typedef __be32     rpc_fraghdr;
  * Additionally, the two alternative forms specified in Section 2.2 of
  * [RFC2373] are also acceptable.
  */
-#define RPCBIND_MAXUADDRLEN    (56u)
+
+#include <linux/inet.h>
+
+/* Maximum size of the port number part of a universal address */
+#define RPCBIND_MAXUADDRPLEN   sizeof(".255.255")
+
+/* Maximum size of an IPv4 universal address */
+#define RPCBIND_MAXUADDR4LEN   \
+               (INET_ADDRSTRLEN + RPCBIND_MAXUADDRPLEN)
+
+/* Maximum size of an IPv6 universal address */
+#define RPCBIND_MAXUADDR6LEN   \
+               (INET6_ADDRSTRLEN + RPCBIND_MAXUADDRPLEN)
+
+/* Assume INET6_ADDRSTRLEN will always be larger than INET_ADDRSTRLEN... */
+#define RPCBIND_MAXUADDRLEN    RPCBIND_MAXUADDR6LEN
 
 #endif /* __KERNEL__ */
 #endif /* _LINUX_SUNRPC_MSGPROT_H_ */
index cea764c..cf14db9 100644 (file)
@@ -3,6 +3,8 @@
 
 #ifdef __KERNEL__
 
+#include <linux/workqueue.h>
+
 struct rpc_pipe_msg {
        struct list_head list;
        void *data;
@@ -32,8 +34,8 @@ struct rpc_inode {
        wait_queue_head_t waitq;
 #define RPC_PIPE_WAIT_FOR_OPEN 1
        int flags;
-       struct rpc_pipe_ops *ops;
        struct delayed_work queue_timeout;
+       const struct rpc_pipe_ops *ops;
 };
 
 static inline struct rpc_inode *
@@ -44,9 +46,19 @@ RPC_I(struct inode *inode)
 
 extern int rpc_queue_upcall(struct inode *, struct rpc_pipe_msg *);
 
-extern struct dentry *rpc_mkdir(char *, struct rpc_clnt *);
-extern int rpc_rmdir(struct dentry *);
-extern struct dentry *rpc_mkpipe(struct dentry *, const char *, void *, struct rpc_pipe_ops *, int flags);
+struct rpc_clnt;
+extern struct dentry *rpc_create_client_dir(struct dentry *, struct qstr *, struct rpc_clnt *);
+extern int rpc_remove_client_dir(struct dentry *);
+
+struct cache_detail;
+extern struct dentry *rpc_create_cache_dir(struct dentry *,
+                                          struct qstr *,
+                                          mode_t umode,
+                                          struct cache_detail *);
+extern void rpc_remove_cache_dir(struct dentry *);
+
+extern struct dentry *rpc_mkpipe(struct dentry *, const char *, void *,
+                                const struct rpc_pipe_ops *, int flags);
 extern int rpc_unlink(struct dentry *);
 extern struct vfsmount *rpc_get_mount(void);
 extern void rpc_put_mount(void);
index b99c625..7da466b 100644 (file)
@@ -117,17 +117,15 @@ static inline __be32 *xdr_encode_array(__be32 *p, const void *s, unsigned int le
 static inline __be32 *
 xdr_encode_hyper(__be32 *p, __u64 val)
 {
-       *p++ = htonl(val >> 32);
-       *p++ = htonl(val & 0xFFFFFFFF);
-       return p;
+       *(__be64 *)p = cpu_to_be64(val);
+       return p + 2;
 }
 
 static inline __be32 *
 xdr_decode_hyper(__be32 *p, __u64 *valp)
 {
-       *valp  = ((__u64) ntohl(*p++)) << 32;
-       *valp |= ntohl(*p++);
-       return p;
+       *valp = be64_to_cpup((__be64 *)p);
+       return p + 2;
 }
 
 /*
index 1175d58..c090df4 100644 (file)
@@ -38,10 +38,8 @@ enum rpc_display_format_t {
        RPC_DISPLAY_ADDR = 0,
        RPC_DISPLAY_PORT,
        RPC_DISPLAY_PROTO,
-       RPC_DISPLAY_ALL,
        RPC_DISPLAY_HEX_ADDR,
        RPC_DISPLAY_HEX_PORT,
-       RPC_DISPLAY_UNIVERSAL_ADDR,
        RPC_DISPLAY_NETID,
        RPC_DISPLAY_MAX,
 };
index db73fd2..9d2fca5 100644 (file)
@@ -10,7 +10,7 @@ obj-$(CONFIG_SUNRPC_XPRT_RDMA) += xprtrdma/
 sunrpc-y := clnt.o xprt.o socklib.o xprtsock.o sched.o \
            auth.o auth_null.o auth_unix.o auth_generic.o \
            svc.o svcsock.o svcauth.o svcauth_unix.o \
-           rpcb_clnt.o timer.o xdr.o \
+           addr.o rpcb_clnt.o timer.o xdr.o \
            sunrpc_syms.o cache.o rpc_pipe.o \
            svc_xprt.o
 sunrpc-$(CONFIG_NFS_V4_1) += backchannel_rqst.o bc_svc.o
diff --git a/net/sunrpc/addr.c b/net/sunrpc/addr.c
new file mode 100644 (file)
index 0000000..22e8fd8
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2009, Oracle.  All rights reserved.
+ *
+ * Convert socket addresses to presentation addresses and universal
+ * addresses, and vice versa.
+ *
+ * Universal addresses are introduced by RFC 1833 and further refined by
+ * recent RFCs describing NFSv4.  The universal address format is part
+ * of the external (network) interface provided by rpcbind version 3
+ * and 4, and by NFSv4.  Such an address is a string containing a
+ * presentation format IP address followed by a port number in
+ * "hibyte.lobyte" format.
+ *
+ * IPv6 addresses can also include a scope ID, typically denoted by
+ * a '%' followed by a device name or a non-negative integer.  Refer to
+ * RFC 4291, Section 2.2 for details on IPv6 presentation formats.
+ */
+
+#include <net/ipv6.h>
+#include <linux/sunrpc/clnt.h>
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+
+static size_t rpc_ntop6_noscopeid(const struct sockaddr *sap,
+                                 char *buf, const int buflen)
+{
+       const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
+       const struct in6_addr *addr = &sin6->sin6_addr;
+
+       /*
+        * RFC 4291, Section 2.2.2
+        *
+        * Shorthanded ANY address
+        */
+       if (ipv6_addr_any(addr))
+               return snprintf(buf, buflen, "::");
+
+       /*
+        * RFC 4291, Section 2.2.2
+        *
+        * Shorthanded loopback address
+        */
+       if (ipv6_addr_loopback(addr))
+               return snprintf(buf, buflen, "::1");
+
+       /*
+        * RFC 4291, Section 2.2.3
+        *
+        * Special presentation address format for mapped v4
+        * addresses.
+        */
+       if (ipv6_addr_v4mapped(addr))
+               return snprintf(buf, buflen, "::ffff:%pI4",
+                                       &addr->s6_addr32[3]);
+
+       /*
+        * RFC 4291, Section 2.2.1
+        *
+        * To keep the result as short as possible, especially
+        * since we don't shorthand, we don't want leading zeros
+        * in each halfword, so avoid %pI6.
+        */
+       return snprintf(buf, buflen, "%x:%x:%x:%x:%x:%x:%x:%x",
+               ntohs(addr->s6_addr16[0]), ntohs(addr->s6_addr16[1]),
+               ntohs(addr->s6_addr16[2]), ntohs(addr->s6_addr16[3]),
+               ntohs(addr->s6_addr16[4]), ntohs(addr->s6_addr16[5]),
+               ntohs(addr->s6_addr16[6]), ntohs(addr->s6_addr16[7]));
+}
+
+static size_t rpc_ntop6(const struct sockaddr *sap,
+                       char *buf, const size_t buflen)
+{
+       const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
+       char scopebuf[IPV6_SCOPE_ID_LEN];
+       size_t len;
+       int rc;
+
+       len = rpc_ntop6_noscopeid(sap, buf, buflen);
+       if (unlikely(len == 0))
+               return len;
+
+       if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) &&
+           !(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_SITELOCAL))
+               return len;
+
+       rc = snprintf(scopebuf, sizeof(scopebuf), "%c%u",
+                       IPV6_SCOPE_DELIMITER, sin6->sin6_scope_id);
+       if (unlikely((size_t)rc > sizeof(scopebuf)))
+               return 0;
+
+       len += rc;
+       if (unlikely(len > buflen))
+               return 0;
+
+       strcat(buf, scopebuf);
+       return len;
+}
+
+#else  /* !(defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)) */
+
+static size_t rpc_ntop6_noscopeid(const struct sockaddr *sap,
+                                 char *buf, const int buflen)
+{
+       return 0;
+}
+
+static size_t rpc_ntop6(const struct sockaddr *sap,
+                       char *buf, const size_t buflen)
+{
+       return 0;
+}
+
+#endif /* !(defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)) */
+
+static int rpc_ntop4(const struct sockaddr *sap,
+                    char *buf, const size_t buflen)
+{
+       const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
+
+       return snprintf(buf, buflen, "%pI4", &sin->sin_addr);
+}
+
+/**
+ * rpc_ntop - construct a presentation address in @buf
+ * @sap: socket address
+ * @buf: construction area
+ * @buflen: size of @buf, in bytes
+ *
+ * Plants a %NUL-terminated string in @buf and returns the length
+ * of the string, excluding the %NUL.  Otherwise zero is returned.
+ */
+size_t rpc_ntop(const struct sockaddr *sap, char *buf, const size_t buflen)
+{
+       switch (sap->sa_family) {
+       case AF_INET:
+               return rpc_ntop4(sap, buf, buflen);
+       case AF_INET6:
+               return rpc_ntop6(sap, buf, buflen);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rpc_ntop);
+
+static size_t rpc_pton4(const char *buf, const size_t buflen,
+                       struct sockaddr *sap, const size_t salen)
+{
+       struct sockaddr_in *sin = (struct sockaddr_in *)sap;
+       u8 *addr = (u8 *)&sin->sin_addr.s_addr;
+
+       if (buflen > INET_ADDRSTRLEN || salen < sizeof(struct sockaddr_in))
+               return 0;
+
+       memset(sap, 0, sizeof(struct sockaddr_in));
+
+       if (in4_pton(buf, buflen, addr, '\0', NULL) == 0)
+               return 0;
+
+       sin->sin_family = AF_INET;
+       return sizeof(struct sockaddr_in);;
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static int rpc_parse_scope_id(const char *buf, const size_t buflen,
+                             const char *delim, struct sockaddr_in6 *sin6)
+{
+       char *p;
+       size_t len;
+
+       if ((buf + buflen) == delim)
+               return 1;
+
+       if (*delim != IPV6_SCOPE_DELIMITER)
+               return 0;
+
+       if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) &&
+           !(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_SITELOCAL))
+               return 0;
+
+       len = (buf + buflen) - delim - 1;
+       p = kstrndup(delim + 1, len, GFP_KERNEL);
+       if (p) {
+               unsigned long scope_id = 0;
+               struct net_device *dev;
+
+               dev = dev_get_by_name(&init_net, p);
+               if (dev != NULL) {
+                       scope_id = dev->ifindex;
+                       dev_put(dev);
+               } else {
+                       if (strict_strtoul(p, 10, &scope_id) == 0) {
+                               kfree(p);
+                               return 0;
+                       }
+               }
+
+               kfree(p);
+
+               sin6->sin6_scope_id = scope_id;
+               return 1;
+       }
+
+       return 0;
+}
+
+static size_t rpc_pton6(const char *buf, const size_t buflen,
+                       struct sockaddr *sap, const size_t salen)
+{
+       struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
+       u8 *addr = (u8 *)&sin6->sin6_addr.in6_u;
+       const char *delim;
+
+       if (buflen > (INET6_ADDRSTRLEN + IPV6_SCOPE_ID_LEN) ||
+           salen < sizeof(struct sockaddr_in6))
+               return 0;
+
+       memset(sap, 0, sizeof(struct sockaddr_in6));
+
+       if (in6_pton(buf, buflen, addr, IPV6_SCOPE_DELIMITER, &delim) == 0)
+               return 0;
+
+       if (!rpc_parse_scope_id(buf, buflen, delim, sin6))
+               return 0;
+
+       sin6->sin6_family = AF_INET6;
+       return sizeof(struct sockaddr_in6);
+}
+#else
+static size_t rpc_pton6(const char *buf, const size_t buflen,
+                       struct sockaddr *sap, const size_t salen)
+{
+       return 0;
+}
+#endif
+
+/**
+ * rpc_pton - Construct a sockaddr in @sap
+ * @buf: C string containing presentation format IP address
+ * @buflen: length of presentation address in bytes
+ * @sap: buffer into which to plant socket address
+ * @salen: size of buffer in bytes
+ *
+ * Returns the size of the socket address if successful; otherwise
+ * zero is returned.
+ *
+ * Plants a socket address in @sap and returns the size of the
+ * socket address, if successful.  Returns zero if an error
+ * occurred.
+ */
+size_t rpc_pton(const char *buf, const size_t buflen,
+               struct sockaddr *sap, const size_t salen)
+{
+       unsigned int i;
+
+       for (i = 0; i < buflen; i++)
+               if (buf[i] == ':')
+                       return rpc_pton6(buf, buflen, sap, salen);
+       return rpc_pton4(buf, buflen, sap, salen);
+}
+EXPORT_SYMBOL_GPL(rpc_pton);
+
+/**
+ * rpc_sockaddr2uaddr - Construct a universal address string from @sap.
+ * @sap: socket address
+ *
+ * Returns a %NUL-terminated string in dynamically allocated memory;
+ * otherwise NULL is returned if an error occurred.  Caller must
+ * free the returned string.
+ */
+char *rpc_sockaddr2uaddr(const struct sockaddr *sap)
+{
+       char portbuf[RPCBIND_MAXUADDRPLEN];
+       char addrbuf[RPCBIND_MAXUADDRLEN];
+       unsigned short port;
+
+       switch (sap->sa_family) {
+       case AF_INET:
+               if (rpc_ntop4(sap, addrbuf, sizeof(addrbuf)) == 0)
+                       return NULL;
+               port = ntohs(((struct sockaddr_in *)sap)->sin_port);
+               break;
+       case AF_INET6:
+               if (rpc_ntop6_noscopeid(sap, addrbuf, sizeof(addrbuf)) == 0)
+                       return NULL;
+               port = ntohs(((struct sockaddr_in6 *)sap)->sin6_port);
+               break;
+       default:
+               return NULL;
+       }
+
+       if (snprintf(portbuf, sizeof(portbuf),
+                    ".%u.%u", port >> 8, port & 0xff) > (int)sizeof(portbuf))
+               return NULL;
+
+       if (strlcat(addrbuf, portbuf, sizeof(addrbuf)) > sizeof(addrbuf))
+               return NULL;
+
+       return kstrdup(addrbuf, GFP_KERNEL);
+}
+EXPORT_SYMBOL_GPL(rpc_sockaddr2uaddr);
+
+/**
+ * rpc_uaddr2sockaddr - convert a universal address to a socket address.
+ * @uaddr: C string containing universal address to convert
+ * @uaddr_len: length of universal address string
+ * @sap: buffer into which to plant socket address
+ * @salen: size of buffer
+ *
+ * Returns the size of the socket address if successful; otherwise
+ * zero is returned.
+ */
+size_t rpc_uaddr2sockaddr(const char *uaddr, const size_t uaddr_len,
+                         struct sockaddr *sap, const size_t salen)
+{
+       char *c, buf[RPCBIND_MAXUADDRLEN];
+       unsigned long portlo, porthi;
+       unsigned short port;
+
+       if (uaddr_len > sizeof(buf))
+               return 0;
+
+       memcpy(buf, uaddr, uaddr_len);
+
+       buf[uaddr_len] = '\n';
+       buf[uaddr_len + 1] = '\0';
+
+       c = strrchr(buf, '.');
+       if (unlikely(c == NULL))
+               return 0;
+       if (unlikely(strict_strtoul(c + 1, 10, &portlo) != 0))
+               return 0;
+       if (unlikely(portlo > 255))
+               return 0;
+
+       c[0] = '\n';
+       c[1] = '\0';
+
+       c = strrchr(buf, '.');
+       if (unlikely(c == NULL))
+               return 0;
+       if (unlikely(strict_strtoul(c + 1, 10, &porthi) != 0))
+               return 0;
+       if (unlikely(porthi > 255))
+               return 0;
+
+       port = (unsigned short)((porthi << 8) | portlo);
+
+       c[0] = '\0';
+
+       if (rpc_pton(buf, strlen(buf), sap, salen) == 0)
+               return 0;
+
+       switch (sap->sa_family) {
+       case AF_INET:
+               ((struct sockaddr_in *)sap)->sin_port = htons(port);
+               return sizeof(struct sockaddr_in);
+       case AF_INET6:
+               ((struct sockaddr_in6 *)sap)->sin6_port = htons(port);
+               return sizeof(struct sockaddr_in6);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rpc_uaddr2sockaddr);
index 66d458f..fc6a43c 100644 (file)
@@ -89,8 +89,8 @@ static struct rpc_wait_queue pipe_version_rpc_waitqueue;
 static DECLARE_WAIT_QUEUE_HEAD(pipe_version_waitqueue);
 
 static void gss_free_ctx(struct gss_cl_ctx *);
-static struct rpc_pipe_ops gss_upcall_ops_v0;
-static struct rpc_pipe_ops gss_upcall_ops_v1;
+static const struct rpc_pipe_ops gss_upcall_ops_v0;
+static const struct rpc_pipe_ops gss_upcall_ops_v1;
 
 static inline struct gss_cl_ctx *
 gss_get_ctx(struct gss_cl_ctx *ctx)
@@ -777,7 +777,7 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
         * that we supported only the old pipe.  So we instead create
         * the new pipe first.
         */
-       gss_auth->dentry[1] = rpc_mkpipe(clnt->cl_dentry,
+       gss_auth->dentry[1] = rpc_mkpipe(clnt->cl_path.dentry,
                                         "gssd",
                                         clnt, &gss_upcall_ops_v1,
                                         RPC_PIPE_WAIT_FOR_OPEN);
@@ -786,7 +786,7 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
                goto err_put_mech;
        }
 
-       gss_auth->dentry[0] = rpc_mkpipe(clnt->cl_dentry,
+       gss_auth->dentry[0] = rpc_mkpipe(clnt->cl_path.dentry,
                                         gss_auth->mech->gm_name,
                                         clnt, &gss_upcall_ops_v0,
                                         RPC_PIPE_WAIT_FOR_OPEN);
@@ -1507,7 +1507,7 @@ static const struct rpc_credops gss_nullops = {
        .crunwrap_resp  = gss_unwrap_resp,
 };
 
-static struct rpc_pipe_ops gss_upcall_ops_v0 = {
+static const struct rpc_pipe_ops gss_upcall_ops_v0 = {
        .upcall         = gss_pipe_upcall,
        .downcall       = gss_pipe_downcall,
        .destroy_msg    = gss_pipe_destroy_msg,
@@ -1515,7 +1515,7 @@ static struct rpc_pipe_ops gss_upcall_ops_v0 = {
        .release_pipe   = gss_pipe_release,
 };
 
-static struct rpc_pipe_ops gss_upcall_ops_v1 = {
+static const struct rpc_pipe_ops gss_upcall_ops_v1 = {
        .upcall         = gss_pipe_upcall,
        .downcall       = gss_pipe_downcall,
        .destroy_msg    = gss_pipe_destroy_msg,
index 2278a50..2e6a148 100644 (file)
@@ -181,6 +181,11 @@ static void rsi_request(struct cache_detail *cd,
        (*bpp)[-1] = '\n';
 }
 
+static int rsi_upcall(struct cache_detail *cd, struct cache_head *h)
+{
+       return sunrpc_cache_pipe_upcall(cd, h, rsi_request);
+}
+
 
 static int rsi_parse(struct cache_detail *cd,
                    char *mesg, int mlen)
@@ -270,7 +275,7 @@ static struct cache_detail rsi_cache = {
        .hash_table     = rsi_table,
        .name           = "auth.rpcsec.init",
        .cache_put      = rsi_put,
-       .cache_request  = rsi_request,
+       .cache_upcall   = rsi_upcall,
        .cache_parse    = rsi_parse,
        .match          = rsi_match,
        .init           = rsi_init,
index ff0c230..45cdaff 100644 (file)
 #include <linux/net.h>
 #include <linux/workqueue.h>
 #include <linux/mutex.h>
+#include <linux/pagemap.h>
 #include <asm/ioctls.h>
 #include <linux/sunrpc/types.h>
 #include <linux/sunrpc/cache.h>
 #include <linux/sunrpc/stats.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
 
 #define         RPCDBG_FACILITY RPCDBG_CACHE
 
@@ -175,7 +177,13 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail,
 }
 EXPORT_SYMBOL_GPL(sunrpc_cache_update);
 
-static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h);
+static int cache_make_upcall(struct cache_detail *cd, struct cache_head *h)
+{
+       if (!cd->cache_upcall)
+               return -EINVAL;
+       return cd->cache_upcall(cd, h);
+}
+
 /*
  * This is the generic cache management routine for all
  * the authentication caches.
@@ -284,76 +292,11 @@ static DEFINE_SPINLOCK(cache_list_lock);
 static struct cache_detail *current_detail;
 static int current_index;
 
-static const struct file_operations cache_file_operations;
-static const struct file_operations content_file_operations;
-static const struct file_operations cache_flush_operations;
-
 static void do_cache_clean(struct work_struct *work);
 static DECLARE_DELAYED_WORK(cache_cleaner, do_cache_clean);
 
-static void remove_cache_proc_entries(struct cache_detail *cd)
-{
-       if (cd->proc_ent == NULL)
-               return;
-       if (cd->flush_ent)
-               remove_proc_entry("flush", cd->proc_ent);
-       if (cd->channel_ent)
-               remove_proc_entry("channel", cd->proc_ent);
-       if (cd->content_ent)
-               remove_proc_entry("content", cd->proc_ent);
-       cd->proc_ent = NULL;
-       remove_proc_entry(cd->name, proc_net_rpc);
-}
-
-#ifdef CONFIG_PROC_FS
-static int create_cache_proc_entries(struct cache_detail *cd)
-{
-       struct proc_dir_entry *p;
-
-       cd->proc_ent = proc_mkdir(cd->name, proc_net_rpc);
-       if (cd->proc_ent == NULL)
-               goto out_nomem;
-       cd->channel_ent = cd->content_ent = NULL;
-
-       p = proc_create_data("flush", S_IFREG|S_IRUSR|S_IWUSR,
-                            cd->proc_ent, &cache_flush_operations, cd);
-       cd->flush_ent = p;
-       if (p == NULL)
-               goto out_nomem;
-
-       if (cd->cache_request || cd->cache_parse) {
-               p = proc_create_data("channel", S_IFREG|S_IRUSR|S_IWUSR,
-                                    cd->proc_ent, &cache_file_operations, cd);
-               cd->channel_ent = p;
-               if (p == NULL)
-                       goto out_nomem;
-       }
-       if (cd->cache_show) {
-               p = proc_create_data("content", S_IFREG|S_IRUSR|S_IWUSR,
-                               cd->proc_ent, &content_file_operations, cd);
-               cd->content_ent = p;
-               if (p == NULL)
-                       goto out_nomem;
-       }
-       return 0;
-out_nomem:
-       remove_cache_proc_entries(cd);
-       return -ENOMEM;
-}
-#else /* CONFIG_PROC_FS */
-static int create_cache_proc_entries(struct cache_detail *cd)
-{
-       return 0;
-}
-#endif
-
-int cache_register(struct cache_detail *cd)
+static void sunrpc_init_cache_detail(struct cache_detail *cd)
 {
-       int ret;
-
-       ret = create_cache_proc_entries(cd);
-       if (ret)
-               return ret;
        rwlock_init(&cd->hash_lock);
        INIT_LIST_HEAD(&cd->queue);
        spin_lock(&cache_list_lock);
@@ -367,11 +310,9 @@ int cache_register(struct cache_detail *cd)
 
        /* start the cleaning process */
        schedule_delayed_work(&cache_cleaner, 0);
-       return 0;
 }
-EXPORT_SYMBOL_GPL(cache_register);
 
-void cache_unregister(struct cache_detail *cd)
+static void sunrpc_destroy_cache_detail(struct cache_detail *cd)
 {
        cache_purge(cd);
        spin_lock(&cache_list_lock);
@@ -386,7 +327,6 @@ void cache_unregister(struct cache_detail *cd)
        list_del_init(&cd->others);
        write_unlock(&cd->hash_lock);
        spin_unlock(&cache_list_lock);
-       remove_cache_proc_entries(cd);
        if (list_empty(&cache_list)) {
                /* module must be being unloaded so its safe to kill the worker */
                cancel_delayed_work_sync(&cache_cleaner);
@@ -395,7 +335,6 @@ void cache_unregister(struct cache_detail *cd)
 out:
        printk(KERN_ERR "nfsd: failed to unregister %s cache\n", cd->name);
 }
-EXPORT_SYMBOL_GPL(cache_unregister);
 
 /* clean cache tries to find something to clean
  * and cleans it.
@@ -687,18 +626,18 @@ struct cache_reader {
        int                     offset; /* if non-0, we have a refcnt on next request */
 };
 
-static ssize_t
-cache_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
+static ssize_t cache_read(struct file *filp, char __user *buf, size_t count,
+                         loff_t *ppos, struct cache_detail *cd)
 {
        struct cache_reader *rp = filp->private_data;
        struct cache_request *rq;
-       struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
+       struct inode *inode = filp->f_path.dentry->d_inode;
        int err;
 
        if (count == 0)
                return 0;
 
-       mutex_lock(&queue_io_mutex); /* protect against multiple concurrent
+       mutex_lock(&inode->i_mutex); /* protect against multiple concurrent
                              * readers on this file */
  again:
        spin_lock(&queue_lock);
@@ -711,7 +650,7 @@ cache_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
        }
        if (rp->q.list.next == &cd->queue) {
                spin_unlock(&queue_lock);
-               mutex_unlock(&queue_io_mutex);
+               mutex_unlock(&inode->i_mutex);
                BUG_ON(rp->offset);
                return 0;
        }
@@ -758,49 +697,90 @@ cache_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
        }
        if (err == -EAGAIN)
                goto again;
-       mutex_unlock(&queue_io_mutex);
+       mutex_unlock(&inode->i_mutex);
        return err ? err :  count;
 }
 
-static char write_buf[8192]; /* protected by queue_io_mutex */
+static ssize_t cache_do_downcall(char *kaddr, const char __user *buf,
+                                size_t count, struct cache_detail *cd)
+{
+       ssize_t ret;
 
-static ssize_t
-cache_write(struct file *filp, const char __user *buf, size_t count,
-           loff_t *ppos)
+       if (copy_from_user(kaddr, buf, count))
+               return -EFAULT;
+       kaddr[count] = '\0';
+       ret = cd->cache_parse(cd, kaddr, count);
+       if (!ret)
+               ret = count;
+       return ret;
+}
+
+static ssize_t cache_slow_downcall(const char __user *buf,
+                                  size_t count, struct cache_detail *cd)
 {
-       int err;
-       struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
+       static char write_buf[8192]; /* protected by queue_io_mutex */
+       ssize_t ret = -EINVAL;
 
-       if (count == 0)
-               return 0;
        if (count >= sizeof(write_buf))
-               return -EINVAL;
-
+               goto out;
        mutex_lock(&queue_io_mutex);
+       ret = cache_do_downcall(write_buf, buf, count, cd);
+       mutex_unlock(&queue_io_mutex);
+out:
+       return ret;
+}
 
-       if (copy_from_user(write_buf, buf, count)) {
-               mutex_unlock(&queue_io_mutex);
-               return -EFAULT;
-       }
-       write_buf[count] = '\0';
-       if (cd->cache_parse)
-               err = cd->cache_parse(cd, write_buf, count);
-       else
-               err = -EINVAL;
+static ssize_t cache_downcall(struct address_space *mapping,
+                             const char __user *buf,
+                             size_t count, struct cache_detail *cd)
+{
+       struct page *page;
+       char *kaddr;
+       ssize_t ret = -ENOMEM;
+
+       if (count >= PAGE_CACHE_SIZE)
+               goto out_slow;
+
+       page = find_or_create_page(mapping, 0, GFP_KERNEL);
+       if (!page)
+               goto out_slow;
+
+       kaddr = kmap(page);
+       ret = cache_do_downcall(kaddr, buf, count, cd);
+       kunmap(page);
+       unlock_page(page);
+       page_cache_release(page);
+       return ret;
+out_slow:
+       return cache_slow_downcall(buf, count, cd);
+}
 
-       mutex_unlock(&queue_io_mutex);
-       return err ? err : count;
+static ssize_t cache_write(struct file *filp, const char __user *buf,
+                          size_t count, loff_t *ppos,
+                          struct cache_detail *cd)
+{
+       struct address_space *mapping = filp->f_mapping;
+       struct inode *inode = filp->f_path.dentry->d_inode;
+       ssize_t ret = -EINVAL;
+
+       if (!cd->cache_parse)
+               goto out;
+
+       mutex_lock(&inode->i_mutex);
+       ret = cache_downcall(mapping, buf, count, cd);
+       mutex_unlock(&inode->i_mutex);
+out:
+       return ret;
 }
 
 static DECLARE_WAIT_QUEUE_HEAD(queue_wait);
 
-static unsigned int
-cache_poll(struct file *filp, poll_table *wait)
+static unsigned int cache_poll(struct file *filp, poll_table *wait,
+                              struct cache_detail *cd)
 {
        unsigned int mask;
        struct cache_reader *rp = filp->private_data;
        struct cache_queue *cq;
-       struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
 
        poll_wait(filp, &queue_wait, wait);
 
@@ -822,14 +802,13 @@ cache_poll(struct file *filp, poll_table *wait)
        return mask;
 }
 
-static int
-cache_ioctl(struct inode *ino, struct file *filp,
-           unsigned int cmd, unsigned long arg)
+static int cache_ioctl(struct inode *ino, struct file *filp,
+                      unsigned int cmd, unsigned long arg,
+                      struct cache_detail *cd)
 {
        int len = 0;
        struct cache_reader *rp = filp->private_data;
        struct cache_queue *cq;
-       struct cache_detail *cd = PDE(ino)->data;
 
        if (cmd != FIONREAD || !rp)
                return -EINVAL;
@@ -852,15 +831,15 @@ cache_ioctl(struct inode *ino, struct file *filp,
        return put_user(len, (int __user *)arg);
 }
 
-static int
-cache_open(struct inode *inode, struct file *filp)
+static int cache_open(struct inode *inode, struct file *filp,
+                     struct cache_detail *cd)
 {
        struct cache_reader *rp = NULL;
 
+       if (!cd || !try_module_get(cd->owner))
+               return -EACCES;
        nonseekable_open(inode, filp);
        if (filp->f_mode & FMODE_READ) {
-               struct cache_detail *cd = PDE(inode)->data;
-
                rp = kmalloc(sizeof(*rp), GFP_KERNEL);
                if (!rp)
                        return -ENOMEM;
@@ -875,11 +854,10 @@ cache_open(struct inode *inode, struct file *filp)
        return 0;
 }
 
-static int
-cache_release(struct inode *inode, struct file *filp)
+static int cache_release(struct inode *inode, struct file *filp,
+                        struct cache_detail *cd)
 {
        struct cache_reader *rp = filp->private_data;
-       struct cache_detail *cd = PDE(inode)->data;
 
        if (rp) {
                spin_lock(&queue_lock);
@@ -903,23 +881,12 @@ cache_release(struct inode *inode, struct file *filp)
                cd->last_close = get_seconds();
                atomic_dec(&cd->readers);
        }
+       module_put(cd->owner);
        return 0;
 }
 
 
 
-static const struct file_operations cache_file_operations = {
-       .owner          = THIS_MODULE,
-       .llseek         = no_llseek,
-       .read           = cache_read,
-       .write          = cache_write,
-       .poll           = cache_poll,
-       .ioctl          = cache_ioctl, /* for FIONREAD */
-       .open           = cache_open,
-       .release        = cache_release,
-};
-
-
 static void queue_loose(struct cache_detail *detail, struct cache_head *ch)
 {
        struct cache_queue *cq;
@@ -1020,15 +987,21 @@ static void warn_no_listener(struct cache_detail *detail)
        if (detail->last_warn != detail->last_close) {
                detail->last_warn = detail->last_close;
                if (detail->warn_no_listener)
-                       detail->warn_no_listener(detail);
+                       detail->warn_no_listener(detail, detail->last_close != 0);
        }
 }
 
 /*
- * register an upcall request to user-space.
+ * register an upcall request to user-space and queue it up for read() by the
+ * upcall daemon.
+ *
  * Each request is at most one page long.
  */
-static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h)
+int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h,
+               void (*cache_request)(struct cache_detail *,
+                                     struct cache_head *,
+                                     char **,
+                                     int *))
 {
 
        char *buf;
@@ -1036,9 +1009,6 @@ static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h)
        char *bp;
        int len;
 
-       if (detail->cache_request == NULL)
-               return -EINVAL;
-
        if (atomic_read(&detail->readers) == 0 &&
            detail->last_close < get_seconds() - 30) {
                        warn_no_listener(detail);
@@ -1057,7 +1027,7 @@ static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h)
 
        bp = buf; len = PAGE_SIZE;
 
-       detail->cache_request(detail, h, &bp, &len);
+       cache_request(detail, h, &bp, &len);
 
        if (len < 0) {
                kfree(buf);
@@ -1075,6 +1045,7 @@ static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h)
        wake_up(&queue_wait);
        return 0;
 }
+EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall);
 
 /*
  * parse a message from user-space and pass it
@@ -1242,11 +1213,13 @@ static const struct seq_operations cache_content_op = {
        .show   = c_show,
 };
 
-static int content_open(struct inode *inode, struct file *file)
+static int content_open(struct inode *inode, struct file *file,
+                       struct cache_detail *cd)
 {
        struct handle *han;
-       struct cache_detail *cd = PDE(inode)->data;
 
+       if (!cd || !try_module_get(cd->owner))
+               return -EACCES;
        han = __seq_open_private(file, &cache_content_op, sizeof(*han));
        if (han == NULL)
                return -ENOMEM;
@@ -1255,17 +1228,33 @@ static int content_open(struct inode *inode, struct file *file)
        return 0;
 }
 
-static const struct file_operations content_file_operations = {
-       .open           = content_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = seq_release_private,
-};
+static int content_release(struct inode *inode, struct file *file,
+               struct cache_detail *cd)
+{
+       int ret = seq_release_private(inode, file);
+       module_put(cd->owner);
+       return ret;
+}
+
+static int open_flush(struct inode *inode, struct file *file,
+                       struct cache_detail *cd)
+{
+       if (!cd || !try_module_get(cd->owner))
+               return -EACCES;
+       return nonseekable_open(inode, file);
+}
+
+static int release_flush(struct inode *inode, struct file *file,
+                       struct cache_detail *cd)
+{
+       module_put(cd->owner);
+       return 0;
+}
 
 static ssize_t read_flush(struct file *file, char __user *buf,
-                           size_t count, loff_t *ppos)
+                         size_t count, loff_t *ppos,
+                         struct cache_detail *cd)
 {
-       struct cache_detail *cd = PDE(file->f_path.dentry->d_inode)->data;
        char tbuf[20];
        unsigned long p = *ppos;
        size_t len;
@@ -1283,10 +1272,10 @@ static ssize_t read_flush(struct file *file, char __user *buf,
        return len;
 }
 
-static ssize_t write_flush(struct file * file, const char __user * buf,
-                            size_t count, loff_t *ppos)
+static ssize_t write_flush(struct file *file, const char __user *buf,
+                          size_t count, loff_t *ppos,
+                          struct cache_detail *cd)
 {
-       struct cache_detail *cd = PDE(file->f_path.dentry->d_inode)->data;
        char tbuf[20];
        char *ep;
        long flushtime;
@@ -1307,8 +1296,343 @@ static ssize_t write_flush(struct file * file, const char __user * buf,
        return count;
 }
 
-static const struct file_operations cache_flush_operations = {
-       .open           = nonseekable_open,
-       .read           = read_flush,
-       .write          = write_flush,
+static ssize_t cache_read_procfs(struct file *filp, char __user *buf,
+                                size_t count, loff_t *ppos)
+{
+       struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
+
+       return cache_read(filp, buf, count, ppos, cd);
+}
+
+static ssize_t cache_write_procfs(struct file *filp, const char __user *buf,
+                                 size_t count, loff_t *ppos)
+{
+       struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
+
+       return cache_write(filp, buf, count, ppos, cd);
+}
+
+static unsigned int cache_poll_procfs(struct file *filp, poll_table *wait)
+{
+       struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
+
+       return cache_poll(filp, wait, cd);
+}
+
+static int cache_ioctl_procfs(struct inode *inode, struct file *filp,
+                             unsigned int cmd, unsigned long arg)
+{
+       struct cache_detail *cd = PDE(inode)->data;
+
+       return cache_ioctl(inode, filp, cmd, arg, cd);
+}
+
+static int cache_open_procfs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = PDE(inode)->data;
+
+       return cache_open(inode, filp, cd);
+}
+
+static int cache_release_procfs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = PDE(inode)->data;
+
+       return cache_release(inode, filp, cd);
+}
+
+static const struct file_operations cache_file_operations_procfs = {
+       .owner          = THIS_MODULE,
+       .llseek         = no_llseek,
+       .read           = cache_read_procfs,
+       .write          = cache_write_procfs,
+       .poll           = cache_poll_procfs,
+       .ioctl          = cache_ioctl_procfs, /* for FIONREAD */
+       .open           = cache_open_procfs,
+       .release        = cache_release_procfs,
 };
+
+static int content_open_procfs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = PDE(inode)->data;
+
+       return content_open(inode, filp, cd);
+}
+
+static int content_release_procfs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = PDE(inode)->data;
+
+       return content_release(inode, filp, cd);
+}
+
+static const struct file_operations content_file_operations_procfs = {
+       .open           = content_open_procfs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = content_release_procfs,
+};
+
+static int open_flush_procfs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = PDE(inode)->data;
+
+       return open_flush(inode, filp, cd);
+}
+
+static int release_flush_procfs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = PDE(inode)->data;
+
+       return release_flush(inode, filp, cd);
+}
+
+static ssize_t read_flush_procfs(struct file *filp, char __user *buf,
+                           size_t count, loff_t *ppos)
+{
+       struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
+
+       return read_flush(filp, buf, count, ppos, cd);
+}
+
+static ssize_t write_flush_procfs(struct file *filp,
+                                 const char __user *buf,
+                                 size_t count, loff_t *ppos)
+{
+       struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
+
+       return write_flush(filp, buf, count, ppos, cd);
+}
+
+static const struct file_operations cache_flush_operations_procfs = {
+       .open           = open_flush_procfs,
+       .read           = read_flush_procfs,
+       .write          = write_flush_procfs,
+       .release        = release_flush_procfs,
+};
+
+static void remove_cache_proc_entries(struct cache_detail *cd)
+{
+       if (cd->u.procfs.proc_ent == NULL)
+               return;
+       if (cd->u.procfs.flush_ent)
+               remove_proc_entry("flush", cd->u.procfs.proc_ent);
+       if (cd->u.procfs.channel_ent)
+               remove_proc_entry("channel", cd->u.procfs.proc_ent);
+       if (cd->u.procfs.content_ent)
+               remove_proc_entry("content", cd->u.procfs.proc_ent);
+       cd->u.procfs.proc_ent = NULL;
+       remove_proc_entry(cd->name, proc_net_rpc);
+}
+
+#ifdef CONFIG_PROC_FS
+static int create_cache_proc_entries(struct cache_detail *cd)
+{
+       struct proc_dir_entry *p;
+
+       cd->u.procfs.proc_ent = proc_mkdir(cd->name, proc_net_rpc);
+       if (cd->u.procfs.proc_ent == NULL)
+               goto out_nomem;
+       cd->u.procfs.channel_ent = NULL;
+       cd->u.procfs.content_ent = NULL;
+
+       p = proc_create_data("flush", S_IFREG|S_IRUSR|S_IWUSR,
+                            cd->u.procfs.proc_ent,
+                            &cache_flush_operations_procfs, cd);
+       cd->u.procfs.flush_ent = p;
+       if (p == NULL)
+               goto out_nomem;
+
+       if (cd->cache_upcall || cd->cache_parse) {
+               p = proc_create_data("channel", S_IFREG|S_IRUSR|S_IWUSR,
+                                    cd->u.procfs.proc_ent,
+                                    &cache_file_operations_procfs, cd);
+               cd->u.procfs.channel_ent = p;
+               if (p == NULL)
+                       goto out_nomem;
+       }
+       if (cd->cache_show) {
+               p = proc_create_data("content", S_IFREG|S_IRUSR|S_IWUSR,
+                               cd->u.procfs.proc_ent,
+                               &content_file_operations_procfs, cd);
+               cd->u.procfs.content_ent = p;
+               if (p == NULL)
+                       goto out_nomem;
+       }
+       return 0;
+out_nomem:
+       remove_cache_proc_entries(cd);
+       return -ENOMEM;
+}
+#else /* CONFIG_PROC_FS */
+static int create_cache_proc_entries(struct cache_detail *cd)
+{
+       return 0;
+}
+#endif
+
+int cache_register(struct cache_detail *cd)
+{
+       int ret;
+
+       sunrpc_init_cache_detail(cd);
+       ret = create_cache_proc_entries(cd);
+       if (ret)
+               sunrpc_destroy_cache_detail(cd);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(cache_register);
+
+void cache_unregister(struct cache_detail *cd)
+{
+       remove_cache_proc_entries(cd);
+       sunrpc_destroy_cache_detail(cd);
+}
+EXPORT_SYMBOL_GPL(cache_unregister);
+
+static ssize_t cache_read_pipefs(struct file *filp, char __user *buf,
+                                size_t count, loff_t *ppos)
+{
+       struct cache_detail *cd = RPC_I(filp->f_path.dentry->d_inode)->private;
+
+       return cache_read(filp, buf, count, ppos, cd);
+}
+
+static ssize_t cache_write_pipefs(struct file *filp, const char __user *buf,
+                                 size_t count, loff_t *ppos)
+{
+       struct cache_detail *cd = RPC_I(filp->f_path.dentry->d_inode)->private;
+
+       return cache_write(filp, buf, count, ppos, cd);
+}
+
+static unsigned int cache_poll_pipefs(struct file *filp, poll_table *wait)
+{
+       struct cache_detail *cd = RPC_I(filp->f_path.dentry->d_inode)->private;
+
+       return cache_poll(filp, wait, cd);
+}
+
+static int cache_ioctl_pipefs(struct inode *inode, struct file *filp,
+                             unsigned int cmd, unsigned long arg)
+{
+       struct cache_detail *cd = RPC_I(inode)->private;
+
+       return cache_ioctl(inode, filp, cmd, arg, cd);
+}
+
+static int cache_open_pipefs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = RPC_I(inode)->private;
+
+       return cache_open(inode, filp, cd);
+}
+
+static int cache_release_pipefs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = RPC_I(inode)->private;
+
+       return cache_release(inode, filp, cd);
+}
+
+const struct file_operations cache_file_operations_pipefs = {
+       .owner          = THIS_MODULE,
+       .llseek         = no_llseek,
+       .read           = cache_read_pipefs,
+       .write          = cache_write_pipefs,
+       .poll           = cache_poll_pipefs,
+       .ioctl          = cache_ioctl_pipefs, /* for FIONREAD */
+       .open           = cache_open_pipefs,
+       .release        = cache_release_pipefs,
+};
+
+static int content_open_pipefs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = RPC_I(inode)->private;
+
+       return content_open(inode, filp, cd);
+}
+
+static int content_release_pipefs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = RPC_I(inode)->private;
+
+       return content_release(inode, filp, cd);
+}
+
+const struct file_operations content_file_operations_pipefs = {
+       .open           = content_open_pipefs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = content_release_pipefs,
+};
+
+static int open_flush_pipefs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = RPC_I(inode)->private;
+
+       return open_flush(inode, filp, cd);
+}
+
+static int release_flush_pipefs(struct inode *inode, struct file *filp)
+{
+       struct cache_detail *cd = RPC_I(inode)->private;
+
+       return release_flush(inode, filp, cd);
+}
+
+static ssize_t read_flush_pipefs(struct file *filp, char __user *buf,
+                           size_t count, loff_t *ppos)
+{
+       struct cache_detail *cd = RPC_I(filp->f_path.dentry->d_inode)->private;
+
+       return read_flush(filp, buf, count, ppos, cd);
+}
+
+static ssize_t write_flush_pipefs(struct file *filp,
+                                 const char __user *buf,
+                                 size_t count, loff_t *ppos)
+{
+       struct cache_detail *cd = RPC_I(filp->f_path.dentry->d_inode)->private;
+
+       return write_flush(filp, buf, count, ppos, cd);
+}
+
+const struct file_operations cache_flush_operations_pipefs = {
+       .open           = open_flush_pipefs,
+       .read           = read_flush_pipefs,
+       .write          = write_flush_pipefs,
+       .release        = release_flush_pipefs,
+};
+
+int sunrpc_cache_register_pipefs(struct dentry *parent,
+                                const char *name, mode_t umode,
+                                struct cache_detail *cd)
+{
+       struct qstr q;
+       struct dentry *dir;
+       int ret = 0;
+
+       sunrpc_init_cache_detail(cd);
+       q.name = name;
+       q.len = strlen(name);
+       q.hash = full_name_hash(q.name, q.len);
+       dir = rpc_create_cache_dir(parent, &q, umode, cd);
+       if (!IS_ERR(dir))
+               cd->u.pipefs.dir = dir;
+       else {
+               sunrpc_destroy_cache_detail(cd);
+               ret = PTR_ERR(dir);
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sunrpc_cache_register_pipefs);
+
+void sunrpc_cache_unregister_pipefs(struct cache_detail *cd)
+{
+       rpc_remove_cache_dir(cd->u.pipefs.dir);
+       cd->u.pipefs.dir = NULL;
+       sunrpc_destroy_cache_detail(cd);
+}
+EXPORT_SYMBOL_GPL(sunrpc_cache_unregister_pipefs);
+
index df1039f..fac0ca9 100644 (file)
@@ -27,6 +27,8 @@
 #include <linux/types.h>
 #include <linux/kallsyms.h>
 #include <linux/mm.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
 #include <linux/slab.h>
 #include <linux/utsname.h>
 #include <linux/workqueue.h>
@@ -97,33 +99,49 @@ static int
 rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name)
 {
        static uint32_t clntid;
+       struct nameidata nd;
+       struct path path;
+       char name[15];
+       struct qstr q = {
+               .name = name,
+       };
        int error;
 
-       clnt->cl_vfsmnt = ERR_PTR(-ENOENT);
-       clnt->cl_dentry = ERR_PTR(-ENOENT);
+       clnt->cl_path.mnt = ERR_PTR(-ENOENT);
+       clnt->cl_path.dentry = ERR_PTR(-ENOENT);
        if (dir_name == NULL)
                return 0;
 
-       clnt->cl_vfsmnt = rpc_get_mount();
-       if (IS_ERR(clnt->cl_vfsmnt))
-               return PTR_ERR(clnt->cl_vfsmnt);
+       path.mnt = rpc_get_mount();
+       if (IS_ERR(path.mnt))
+               return PTR_ERR(path.mnt);
+       error = vfs_path_lookup(path.mnt->mnt_root, path.mnt, dir_name, 0, &nd);
+       if (error)
+               goto err;
 
        for (;;) {
-               snprintf(clnt->cl_pathname, sizeof(clnt->cl_pathname),
-                               "%s/clnt%x", dir_name,
-                               (unsigned int)clntid++);
-               clnt->cl_pathname[sizeof(clnt->cl_pathname) - 1] = '\0';
-               clnt->cl_dentry = rpc_mkdir(clnt->cl_pathname, clnt);
-               if (!IS_ERR(clnt->cl_dentry))
-                       return 0;
-               error = PTR_ERR(clnt->cl_dentry);
+               q.len = snprintf(name, sizeof(name), "clnt%x", (unsigned int)clntid++);
+               name[sizeof(name) - 1] = '\0';
+               q.hash = full_name_hash(q.name, q.len);
+               path.dentry = rpc_create_client_dir(nd.path.dentry, &q, clnt);
+               if (!IS_ERR(path.dentry))
+                       break;
+               error = PTR_ERR(path.dentry);
                if (error != -EEXIST) {
-                       printk(KERN_INFO "RPC: Couldn't create pipefs entry %s, error %d\n",
-                                       clnt->cl_pathname, error);
-                       rpc_put_mount();
-                       return error;
+                       printk(KERN_INFO "RPC: Couldn't create pipefs entry"
+                                       " %s/%s, error %d\n",
+                                       dir_name, name, error);
+                       goto err_path_put;
                }
        }
+       path_put(&nd.path);
+       clnt->cl_path = path;
+       return 0;
+err_path_put:
+       path_put(&nd.path);
+err:
+       rpc_put_mount();
+       return error;
 }
 
 static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, struct rpc_xprt *xprt)
@@ -231,8 +249,8 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
        return clnt;
 
 out_no_auth:
-       if (!IS_ERR(clnt->cl_dentry)) {
-               rpc_rmdir(clnt->cl_dentry);
+       if (!IS_ERR(clnt->cl_path.dentry)) {
+               rpc_remove_client_dir(clnt->cl_path.dentry);
                rpc_put_mount();
        }
 out_no_path:
@@ -423,8 +441,8 @@ rpc_free_client(struct kref *kref)
 
        dprintk("RPC:       destroying %s client for %s\n",
                        clnt->cl_protname, clnt->cl_server);
-       if (!IS_ERR(clnt->cl_dentry)) {
-               rpc_rmdir(clnt->cl_dentry);
+       if (!IS_ERR(clnt->cl_path.dentry)) {
+               rpc_remove_client_dir(clnt->cl_path.dentry);
                rpc_put_mount();
        }
        if (clnt->cl_parent != clnt) {
index 9ced062..7f676bd 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/workqueue.h>
 #include <linux/sunrpc/rpc_pipe_fs.h>
+#include <linux/sunrpc/cache.h>
 
 static struct vfsmount *rpc_mount __read_mostly;
 static int rpc_mount_count;
@@ -125,7 +126,7 @@ static void
 rpc_close_pipes(struct inode *inode)
 {
        struct rpc_inode *rpci = RPC_I(inode);
-       struct rpc_pipe_ops *ops;
+       const struct rpc_pipe_ops *ops;
        int need_release;
 
        mutex_lock(&inode->i_mutex);
@@ -398,66 +399,12 @@ static const struct file_operations rpc_info_operations = {
 
 
 /*
- * We have a single directory with 1 node in it.
- */
-enum {
-       RPCAUTH_Root = 1,
-       RPCAUTH_lockd,
-       RPCAUTH_mount,
-       RPCAUTH_nfs,
-       RPCAUTH_portmap,
-       RPCAUTH_statd,
-       RPCAUTH_nfsd4_cb,
-       RPCAUTH_RootEOF
-};
-
-/*
  * Description of fs contents.
  */
 struct rpc_filelist {
-       char *name;
+       const char *name;
        const struct file_operations *i_fop;
-       int mode;
-};
-
-static struct rpc_filelist files[] = {
-       [RPCAUTH_lockd] = {
-               .name = "lockd",
-               .mode = S_IFDIR | S_IRUGO | S_IXUGO,
-       },
-       [RPCAUTH_mount] = {
-               .name = "mount",
-               .mode = S_IFDIR | S_IRUGO | S_IXUGO,
-       },
-       [RPCAUTH_nfs] = {
-               .name = "nfs",
-               .mode = S_IFDIR | S_IRUGO | S_IXUGO,
-       },
-       [RPCAUTH_portmap] = {
-               .name = "portmap",
-               .mode = S_IFDIR | S_IRUGO | S_IXUGO,
-       },
-       [RPCAUTH_statd] = {
-               .name = "statd",
-               .mode = S_IFDIR | S_IRUGO | S_IXUGO,
-       },
-       [RPCAUTH_nfsd4_cb] = {
-               .name = "nfsd4_cb",
-               .mode = S_IFDIR | S_IRUGO | S_IXUGO,
-       },
-};
-
-enum {
-       RPCAUTH_info = 2,
-       RPCAUTH_EOF
-};
-
-static struct rpc_filelist authfiles[] = {
-       [RPCAUTH_info] = {
-               .name = "info",
-               .i_fop = &rpc_info_operations,
-               .mode = S_IFREG | S_IRUSR,
-       },
+       umode_t mode;
 };
 
 struct vfsmount *rpc_get_mount(void)
@@ -469,11 +416,13 @@ struct vfsmount *rpc_get_mount(void)
                return ERR_PTR(err);
        return rpc_mount;
 }
+EXPORT_SYMBOL_GPL(rpc_get_mount);
 
 void rpc_put_mount(void)
 {
        simple_release_fs(&rpc_mount, &rpc_mount_count);
 }
+EXPORT_SYMBOL_GPL(rpc_put_mount);
 
 static int rpc_delete_dentry(struct dentry *dentry)
 {
@@ -484,39 +433,8 @@ static const struct dentry_operations rpc_dentry_operations = {
        .d_delete = rpc_delete_dentry,
 };
 
-static int
-rpc_lookup_parent(char *path, struct nameidata *nd)
-{
-       struct vfsmount *mnt;
-
-       if (path[0] == '\0')
-               return -ENOENT;
-
-       mnt = rpc_get_mount();
-       if (IS_ERR(mnt)) {
-               printk(KERN_WARNING "%s: %s failed to mount "
-                              "pseudofilesystem \n", __FILE__, __func__);
-               return PTR_ERR(mnt);
-       }
-
-       if (vfs_path_lookup(mnt->mnt_root, mnt, path, LOOKUP_PARENT, nd)) {
-               printk(KERN_WARNING "%s: %s failed to find path %s\n",
-                               __FILE__, __func__, path);
-               rpc_put_mount();
-               return -ENOENT;
-       }
-       return 0;
-}
-
-static void
-rpc_release_path(struct nameidata *nd)
-{
-       path_put(&nd->path);
-       rpc_put_mount();
-}
-
 static struct inode *
-rpc_get_inode(struct super_block *sb, int mode)
+rpc_get_inode(struct super_block *sb, umode_t mode)
 {
        struct inode *inode = new_inode(sb);
        if (!inode)
@@ -534,212 +452,274 @@ rpc_get_inode(struct super_block *sb, int mode)
        return inode;
 }
 
-/*
- * FIXME: This probably has races.
- */
-static void rpc_depopulate(struct dentry *parent,
-                          unsigned long start, unsigned long eof)
+static int __rpc_create_common(struct inode *dir, struct dentry *dentry,
+                              umode_t mode,
+                              const struct file_operations *i_fop,
+                              void *private)
 {
-       struct inode *dir = parent->d_inode;
-       struct list_head *pos, *next;
-       struct dentry *dentry, *dvec[10];
-       int n = 0;
+       struct inode *inode;
 
-       mutex_lock_nested(&dir->i_mutex, I_MUTEX_CHILD);
-repeat:
-       spin_lock(&dcache_lock);
-       list_for_each_safe(pos, next, &parent->d_subdirs) {
-               dentry = list_entry(pos, struct dentry, d_u.d_child);
-               if (!dentry->d_inode ||
-                               dentry->d_inode->i_ino < start ||
-                               dentry->d_inode->i_ino >= eof)
-                       continue;
-               spin_lock(&dentry->d_lock);
-               if (!d_unhashed(dentry)) {
-                       dget_locked(dentry);
-&