shrink the code a bit
[oweals/busybox.git] / util-linux / nfsmount.c
index f2bd2f49c05d9a202ee4d937753a5a506ce3757d..e9d6dc6354309b6b84391cda61cd780ea8c04a99 100644 (file)
@@ -1,3 +1,4 @@
+/* vi: set sw=4 ts=4: */
 /*
  * nfsmount.c -- Linux NFS mount
  * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
@@ -23,7 +24,9 @@
  *
  * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
  * - added Native Language Support
- * 
+ *
+ * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
+ * plus NFSv3 stuff.
  */
 
 /*
 #include <string.h>
 #include <errno.h>
 #include <netdb.h>
-#include <rpc/rpc.h>
-#include <rpc/pmap_prot.h>
-#include <rpc/pmap_clnt.h>
 #include <sys/socket.h>
-#include <sys/time.h>
+#include <time.h>
 #include <sys/utsname.h>
-#include <sys/stat.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
-
+#include <stdlib.h>
+#include "busybox.h"
+#undef TRUE
+#undef FALSE
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
 #include "nfsmount.h"
 
-#include <linux/nfs.h>
-/* we suppose that libc-dev is providing NFSv3 headers (kernel >= 2.2) */
-#include <linux/nfs_mount.h>
+/* This is just a warning of a common mistake.  Possibly this should be a
+ *  * uclibc faq entry rather than in busybox... */
+#if ENABLE_FEATURE_MOUNT_NFS && defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
+#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
+#endif
 
-#define _
-#define HAVE_inet_aton
-#define MS_REMOUNT     32      /* Alter flags of a mounted FS */
-#define sloppy 0
-#define EX_FAIL 1
-#define EX_BG 1
-#define xstrdup strdup
-#define xstrndup strndup
+
+/*
+ * NFS stats. The good thing with these values is that NFSv3 errors are
+ * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which
+ * no-one uses anyway), so we can happily mix code as long as we make sure
+ * no NFSv3 errors are returned to NFSv2 clients.
+ * Error codes that have a `--' in the v2 column are not part of the
+ * standard, but seem to be widely used nevertheless.
+ */
+enum nfs_stat {
+       NFS_OK = 0,                     /* v2 v3 */
+       NFSERR_PERM = 1,                /* v2 v3 */
+       NFSERR_NOENT = 2,               /* v2 v3 */
+       NFSERR_IO = 5,                  /* v2 v3 */
+       NFSERR_NXIO = 6,                /* v2 v3 */
+       NFSERR_EAGAIN = 11,             /* v2 v3 */
+       NFSERR_ACCES = 13,              /* v2 v3 */
+       NFSERR_EXIST = 17,              /* v2 v3 */
+       NFSERR_XDEV = 18,               /*    v3 */
+       NFSERR_NODEV = 19,              /* v2 v3 */
+       NFSERR_NOTDIR = 20,             /* v2 v3 */
+       NFSERR_ISDIR = 21,              /* v2 v3 */
+       NFSERR_INVAL = 22,              /* v2 v3 that Sun forgot */
+       NFSERR_FBIG = 27,               /* v2 v3 */
+       NFSERR_NOSPC = 28,              /* v2 v3 */
+       NFSERR_ROFS = 30,               /* v2 v3 */
+       NFSERR_MLINK = 31,              /*    v3 */
+       NFSERR_OPNOTSUPP = 45,          /* v2 v3 */
+       NFSERR_NAMETOOLONG = 63,        /* v2 v3 */
+       NFSERR_NOTEMPTY = 66,           /* v2 v3 */
+       NFSERR_DQUOT = 69,              /* v2 v3 */
+       NFSERR_STALE = 70,              /* v2 v3 */
+       NFSERR_REMOTE = 71,             /* v2 v3 */
+       NFSERR_WFLUSH = 99,             /* v2    */
+       NFSERR_BADHANDLE = 10001,       /*    v3 */
+       NFSERR_NOT_SYNC = 10002,        /*    v3 */
+       NFSERR_BAD_COOKIE = 10003,      /*    v3 */
+       NFSERR_NOTSUPP = 10004,         /*    v3 */
+       NFSERR_TOOSMALL = 10005,        /*    v3 */
+       NFSERR_SERVERFAULT = 10006,     /*    v3 */
+       NFSERR_BADTYPE = 10007,         /*    v3 */
+       NFSERR_JUKEBOX = 10008          /*    v3 */
+};
+
+#define NFS_PROGRAM    100003
 
 
-static char *nfs_strerror(int stat);
+
+#ifndef NFS_FHSIZE
+static const int NFS_FHSIZE = 32;
+#endif
+#ifndef NFS_PORT
+static const int NFS_PORT = 2049;
+#endif
+
+/* Disable the nls stuff */
+# undef bindtextdomain
+# define bindtextdomain(Domain, Directory) /* empty */
+# undef textdomain
+# define textdomain(Domain) /* empty */
+# define _(Text) (Text)
+# define N_(Text) (Text)
+
+static const int MS_MGC_VAL = 0xc0ed0000; /* Magic number indicatng "new" flags */
+static const int MS_RDONLY = 1;      /* Mount read-only */
+static const int MS_NOSUID = 2;      /* Ignore suid and sgid bits */
+static const int MS_NODEV = 4;      /* Disallow access to device special files */
+static const int MS_NOEXEC = 8;      /* Disallow program execution */
+static const int MS_SYNCHRONOUS = 16;      /* Writes are synced at once */
+static const int MS_REMOUNT = 32;      /* Alter flags of a mounted FS */
+static const int MS_MANDLOCK = 64;      /* Allow mandatory locks on an FS */
+static const int S_QUOTA = 128;     /* Quota initialized for file/directory/symlink */
+static const int S_APPEND = 256;     /* Append-only file */
+static const int S_IMMUTABLE = 512;     /* Immutable file */
+static const int MS_NOATIME = 1024;    /* Do not update access times. */
+static const int MS_NODIRATIME = 2048;    /* Do not update directory access times */
+
+
+/*
+ * We want to be able to compile mount on old kernels in such a way
+ * that the binary will work well on more recent kernels.
+ * Thus, if necessary we teach nfsmount.c the structure of new fields
+ * that will come later.
+ *
+ * Moreover, the new kernel includes conflict with glibc includes
+ * so it is easiest to ignore the kernel altogether (at compile time).
+ */
+
+/* NOTE: Do not make this into a 'static const int' because the pre-processor
+ * needs to test this value in some #if statements. */
+#define NFS_MOUNT_VERSION 4
+
+struct nfs2_fh {
+       char                    data[32];
+};
+struct nfs3_fh {
+       unsigned short          size;
+       unsigned char           data[64];
+};
+
+struct nfs_mount_data {
+       int             version;                /* 1 */
+       int             fd;                     /* 1 */
+       struct nfs2_fh  old_root;               /* 1 */
+       int             flags;                  /* 1 */
+       int             rsize;                  /* 1 */
+       int             wsize;                  /* 1 */
+       int             timeo;                  /* 1 */
+       int             retrans;                /* 1 */
+       int             acregmin;               /* 1 */
+       int             acregmax;               /* 1 */
+       int             acdirmin;               /* 1 */
+       int             acdirmax;               /* 1 */
+       struct sockaddr_in addr;                /* 1 */
+       char            hostname[256];          /* 1 */
+       int             namlen;                 /* 2 */
+       unsigned int    bsize;                  /* 3 */
+       struct nfs3_fh  root;                   /* 4 */
+};
+
+/* bits in the flags field */
+
+static const int NFS_MOUNT_SOFT = 0x0001;      /* 1 */
+static const int NFS_MOUNT_INTR = 0x0002;      /* 1 */
+static const int NFS_MOUNT_SECURE = 0x0004;    /* 1 */
+static const int NFS_MOUNT_POSIX = 0x0008;     /* 1 */
+static const int NFS_MOUNT_NOCTO = 0x0010;     /* 1 */
+static const int NFS_MOUNT_NOAC = 0x0020;      /* 1 */
+static const int NFS_MOUNT_TCP = 0x0040;       /* 2 */
+static const int NFS_MOUNT_VER3 = 0x0080;      /* 3 */
+static const int NFS_MOUNT_KERBEROS = 0x0100;  /* 3 */
+static const int NFS_MOUNT_NONLM = 0x0200;     /* 3 */
+
+
+#define UTIL_LINUX_VERSION "2.10m"
+#define util_linux_version "util-linux-2.10m"
+
+#define HAVE_inet_aton
+#define HAVE_scsi_h
+#define HAVE_blkpg_h
+#define HAVE_kd_h
+#define HAVE_termcap
+#define HAVE_locale_h
+#define HAVE_libintl_h
+#define ENABLE_NLS
+#define HAVE_langinfo_h
+#define HAVE_progname
+#define HAVE_openpty
+#define HAVE_nanosleep
+#define HAVE_personality
+#define HAVE_tm_gmtoff
+
+static char *nfs_strerror(int status);
 
 #define MAKE_VERSION(p,q,r)    (65536*(p) + 256*(q) + (r))
+#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
 
-static int
-linux_version_code(void) {
-       struct utsname my_utsname;
-       int p, q, r;
+static const int EX_FAIL = 32;       /* mount failure */
+static const int EX_BG = 256;       /* retry in background (internal only) */
 
-       if (uname(&my_utsname) == 0) {
-               p = atoi(strtok(my_utsname.release, "."));
-               q = atoi(strtok(NULL, "."));
-               r = atoi(strtok(NULL, "."));
-               return MAKE_VERSION(p,q,r);
-       }
-       return 0;
-}
 
 /*
- * nfs_mount_version according to the kernel sources seen at compile time.
+ * nfs_mount_version according to the sources seen at compile time.
  */
-static int nfs_mount_version = NFS_MOUNT_VERSION;
+static int nfs_mount_version;
 
 /*
  * Unfortunately, the kernel prints annoying console messages
@@ -96,46 +236,109 @@ static int nfs_mount_version = NFS_MOUNT_VERSION;
  *     nfs_mount_version: version this source and running kernel can handle
  */
 static void
-find_kernel_nfs_mount_version(void) {
-       int kernel_version = linux_version_code();
+find_kernel_nfs_mount_version(void)
+{
+       static int kernel_version = 0;
+
+       if (kernel_version)
+               return;
+
+       nfs_mount_version = NFS_MOUNT_VERSION; /* default */
 
+       kernel_version = get_kernel_revision();
        if (kernel_version) {
-            if (kernel_version < MAKE_VERSION(2,1,32))
-                 nfs_mount_version = 1;
-            else
-                 nfs_mount_version = 3;
+               if (kernel_version < MAKE_VERSION(2,1,32))
+                       nfs_mount_version = 1;
+               else if (kernel_version < MAKE_VERSION(2,2,18) ||
+                               (kernel_version >=   MAKE_VERSION(2,3,0) &&
+                                kernel_version < MAKE_VERSION(2,3,99)))
+                       nfs_mount_version = 3;
+               else
+                       nfs_mount_version = 4; /* since 2.3.99pre4 */
        }
        if (nfs_mount_version > NFS_MOUNT_VERSION)
-            nfs_mount_version = NFS_MOUNT_VERSION;
+               nfs_mount_version = NFS_MOUNT_VERSION;
 }
 
-int nfsmount(const char *spec, const char *node, unsigned long *flags,
-            char **extra_opts, char **mount_opts, int running_bg)
+static struct pmap *
+get_mountport(struct sockaddr_in *server_addr,
+      long unsigned prog,
+      long unsigned version,
+      long unsigned proto,
+      long unsigned port)
+{
+struct pmaplist *pmap;
+static struct pmap p = {0, 0, 0, 0};
+
+server_addr->sin_port = PMAPPORT;
+pmap = pmap_getmaps(server_addr);
+
+if (version > MAX_NFSPROT)
+       version = MAX_NFSPROT;
+if (!prog)
+       prog = MOUNTPROG;
+p.pm_prog = prog;
+p.pm_vers = version;
+p.pm_prot = proto;
+p.pm_port = port;
+
+while (pmap) {
+       if (pmap->pml_map.pm_prog != prog)
+               goto next;
+       if (!version && p.pm_vers > pmap->pml_map.pm_vers)
+               goto next;
+       if (version > 2 && pmap->pml_map.pm_vers != version)
+               goto next;
+       if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
+               goto next;
+       if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
+           (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) ||
+           (port && pmap->pml_map.pm_port != port))
+               goto next;
+       memcpy(&p, &pmap->pml_map, sizeof(p));
+next:
+       pmap = pmap->pml_next;
+}
+if (!p.pm_vers)
+       p.pm_vers = MOUNTVERS;
+if (!p.pm_port)
+       p.pm_port = MOUNTPORT;
+if (!p.pm_prot)
+       p.pm_prot = IPPROTO_TCP;
+return &p;
+}
+
+int nfsmount(const char *spec, const char *node, int *flags,
+            char **mount_opts, int running_bg)
 {
        static char *prev_bg_host;
        char hostdir[1024];
        CLIENT *mclient;
        char *hostname;
-       char *dirname;
+       char *pathname;
        char *old_opts;
        char *mounthost=NULL;
        char new_opts[1024];
-       fhandle root_fhandle;
        struct timeval total_timeout;
        enum clnt_stat clnt_stat;
-       static struct nfs_mount_data data;
+       struct nfs_mount_data data;
        char *opt, *opteq;
        int val;
        struct hostent *hp;
        struct sockaddr_in server_addr;
        struct sockaddr_in mount_server_addr;
+       struct pmap* pm_mnt;
        int msock, fsock;
        struct timeval retry_timeout;
-       struct fhstatus status;
+       union {
+               struct fhstatus nfsv2;
+               struct mountres3 nfsv3;
+       } status;
        struct stat statbuf;
        char *s;
        int port;
        int mountport;
+       int proto;
        int bg;
        int soft;
        int intr;
@@ -160,25 +363,22 @@ int nfsmount(const char *spec, const char *node, unsigned long *flags,
        msock = fsock = -1;
        mclient = NULL;
        if (strlen(spec) >= sizeof(hostdir)) {
-               fprintf(stderr, _("mount: "
-                       "excessively long host:dir argument\n"));
+               bb_error_msg("excessively long host:dir argument");
                goto fail;
        }
        strcpy(hostdir, spec);
        if ((s = strchr(hostdir, ':'))) {
                hostname = hostdir;
-               dirname = s + 1;
+               pathname = s + 1;
                *s = '\0';
                /* Ignore all but first hostname in replicated mounts
                   until they can be fully supported. (mack@sgi.com) */
                if ((s = strchr(hostdir, ','))) {
                        *s = '\0';
-                       fprintf(stderr, _("mount: warning: "
-                               "multiple hostnames not supported\n"));
+                       bb_error_msg("warning: multiple hostnames not supported");
                }
        } else {
-               fprintf(stderr, _("mount: "
-                       "directory to mount not in host:dir format\n"));
+               bb_error_msg("directory to mount not in host:dir format");
                goto fail;
        }
 
@@ -188,13 +388,11 @@ int nfsmount(const char *spec, const char *node, unsigned long *flags,
 #endif
        {
                if ((hp = gethostbyname(hostname)) == NULL) {
-                       fprintf(stderr, _("mount: can't get address for %s\n"),
-                               hostname);
+                       bb_herror_msg("%s", hostname);
                        goto fail;
                } else {
                        if (hp->h_length > sizeof(struct in_addr)) {
-                               fprintf(stderr,
-                                       _("mount: got bad hp->h_length\n"));
+                               bb_error_msg("got bad hp->h_length");
                                hp->h_length = sizeof(struct in_addr);
                        }
                        memcpy(&server_addr.sin_addr,
@@ -207,17 +405,16 @@ int nfsmount(const char *spec, const char *node, unsigned long *flags,
        /* add IP address to mtab options for use when unmounting */
 
        s = inet_ntoa(server_addr.sin_addr);
-       old_opts = *extra_opts;
+       old_opts = *mount_opts;
        if (!old_opts)
                old_opts = "";
        if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
-               fprintf(stderr, _("mount: "
-                       "excessively long option argument\n"));
+               bb_error_msg("excessively long option argument");
                goto fail;
        }
        sprintf(new_opts, "%s%saddr=%s",
                old_opts, *old_opts ? "," : "", s);
-       *extra_opts = xstrdup(new_opts);
+       *mount_opts = bb_xstrdup(new_opts);
 
        /* Set default options.
         * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
@@ -244,17 +441,17 @@ int nfsmount(const char *spec, const char *node, unsigned long *flags,
        tcp = 0;
 
        mountprog = MOUNTPROG;
-       mountvers = MOUNTVERS;
+       mountvers = 0;
        port = 0;
        mountport = 0;
        nfsprog = NFS_PROGRAM;
-       nfsvers = NFS_VERSION;
+       nfsvers = 0;
 
        /* parse options */
 
        for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
                if ((opteq = strchr(opt, '='))) {
-                       val = atoi(opteq + 1);  
+                       val = atoi(opteq + 1);
                        *opteq = '\0';
                        if (!strcmp(opt, "rsize"))
                                data.rsize = val;
@@ -285,7 +482,7 @@ int nfsmount(const char *spec, const char *node, unsigned long *flags,
                        else if (!strcmp(opt, "mountport"))
                                mountport = val;
                        else if (!strcmp(opt, "mounthost"))
-                               mounthost=xstrndup(opteq+1,
+                               mounthost=bb_xstrndup(opteq+1,
                                                  strcspn(opteq+1," \t\n\r,"));
                        else if (!strcmp(opt, "mountprog"))
                                mountprog = val;
@@ -324,9 +521,9 @@ int nfsmount(const char *spec, const char *node, unsigned long *flags,
                                val = 0;
                                opt += 2;
                        }
-                       if (!strcmp(opt, "bg")) 
+                       if (!strcmp(opt, "bg"))
                                bg = val;
-                       else if (!strcmp(opt, "fg")) 
+                       else if (!strcmp(opt, "fg"))
                                bg = !val;
                        else if (!strcmp(opt, "soft"))
                                soft = val;
@@ -350,14 +547,14 @@ int nfsmount(const char *spec, const char *node, unsigned long *flags,
                                else
                                        printf(_("Warning: option nolock is not supported.\n"));
                        } else {
-                               if (!sloppy) {
-                                       printf(_("unknown nfs mount option: "
-                                              "%s%s\n"), val ? "" : "no", opt);
-                                       goto fail;
-                               }
+                               printf(_("unknown nfs mount option: "
+                                          "%s%s\n"), val ? "" : "no", opt);
+                               goto fail;
                        }
                }
        }
+       proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
+
        data.flags = (soft ? NFS_MOUNT_SOFT : 0)
                | (intr ? NFS_MOUNT_INTR : 0)
                | (posix ? NFS_MOUNT_POSIX : 0)
@@ -371,6 +568,19 @@ int nfsmount(const char *spec, const char *node, unsigned long *flags,
        if (nfs_mount_version >= 3)
                data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
 #endif
+       if (nfsvers > MAX_NFSPROT) {
+               bb_error_msg("NFSv%d not supported!", nfsvers);
+               return 0;
+       }
+       if (mountvers > MAX_NFSPROT) {
+               bb_error_msg("NFSv%d not supported!", nfsvers);
+               return 0;
+       }
+       if (nfsvers && !mountvers)
+               mountvers = (nfsvers < 3) ? 1 : nfsvers;
+       if (nfsvers && nfsvers < mountvers) {
+               mountvers = nfsvers;
+       }
 
        /* Adjust options if none specified */
        if (!data.timeo)
@@ -398,10 +608,9 @@ int nfsmount(const char *spec, const char *node, unsigned long *flags,
 #endif
 
        data.version = nfs_mount_version;
-       *mount_opts = (char *) &data;
 
        if (*flags & MS_REMOUNT)
-               return 0;
+               goto copy_data_and_return;
 
        /*
         * If the previous mount operation on the same host was
@@ -423,13 +632,11 @@ int nfsmount(const char *spec, const char *node, unsigned long *flags,
            mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
          } else {
                  if ((hp = gethostbyname(mounthost)) == NULL) {
-                         fprintf(stderr, _("mount: can't get address for %s\n"),
-                                 hostname);
+                         bb_herror_msg("%s", mounthost);
                          goto fail;
                  } else {
                          if (hp->h_length > sizeof(struct in_addr)) {
-                                 fprintf(stderr,
-                                         _("mount: got bad hp->h_length?\n"));
+                                 bb_error_msg("got bad hp->h_length?");
                                  hp->h_length = sizeof(struct in_addr);
                          }
                          mount_server_addr.sin_family = AF_INET;
@@ -476,28 +683,60 @@ int nfsmount(const char *spec, const char *node, unsigned long *flags,
                        if (t - prevt < 30)
                                sleep(30);
 
+                       pm_mnt = get_mountport(&mount_server_addr,
+                                      mountprog,
+                                      mountvers,
+                                      proto,
+                                      mountport);
+
                        /* contact the mount daemon via TCP */
-                       mount_server_addr.sin_port = htons(mountport);
+                       mount_server_addr.sin_port = htons(pm_mnt->pm_port);
                        msock = RPC_ANYSOCK;
-                       mclient = clnttcp_create(&mount_server_addr,
-                                                mountprog, mountvers,
-                                                &msock, 0, 0);
 
-                       /* if this fails, contact the mount daemon via UDP */
-                       if (!mclient) {
-                               mount_server_addr.sin_port = htons(mountport);
-                               msock = RPC_ANYSOCK;
+                       switch (pm_mnt->pm_prot) {
+                       case IPPROTO_UDP:
                                mclient = clntudp_create(&mount_server_addr,
-                                                        mountprog, mountvers,
-                                                        retry_timeout, &msock);
+                                                pm_mnt->pm_prog,
+                                                pm_mnt->pm_vers,
+                                                retry_timeout,
+                                                &msock);
+                 if (mclient)
+                         break;
+                 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
+                 msock = RPC_ANYSOCK;
+               case IPPROTO_TCP:
+                       mclient = clnttcp_create(&mount_server_addr,
+                                                pm_mnt->pm_prog,
+                                                pm_mnt->pm_vers,
+                                                &msock, 0, 0);
+                       break;
+               default:
+                       mclient = 0;
                        }
                        if (mclient) {
-                               /* try to mount hostname:dirname */
+                               /* try to mount hostname:pathname */
                                mclient->cl_auth = authunix_create_default();
-                               clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
-                                       (xdrproc_t) xdr_dirpath, (caddr_t) &dirname,
-                                       (xdrproc_t) xdr_fhstatus, (caddr_t) &status,
+
+                       /* make pointers in xdr_mountres3 NULL so
+                        * that xdr_array allocates memory for us
+                        */
+                       memset(&status, 0, sizeof(status));
+
+                       if (pm_mnt->pm_vers == 3)
+                               clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
+                                                     (xdrproc_t) xdr_dirpath,
+                                                     (caddr_t) &pathname,
+                                                     (xdrproc_t) xdr_mountres3,
+                                                     (caddr_t) &status,
                                        total_timeout);
+                       else
+                               clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
+                                                     (xdrproc_t) xdr_dirpath,
+                                                     (caddr_t) &pathname,
+                                                     (xdrproc_t) xdr_fhstatus,
+                                                     (caddr_t) &status,
+                                                     total_timeout);
+
                                if (clnt_stat == RPC_SUCCESS)
                                        break;          /* we're done */
                                if (errno != ECONNREFUSED) {
@@ -519,7 +758,7 @@ int nfsmount(const char *spec, const char *node, unsigned long *flags,
                if (!bg)
                        goto fail;
                if (!running_bg) {
-                       prev_bg_host = xstrdup(hostname);
+                       prev_bg_host = bb_xstrdup(hostname);
                        if (retry > 0)
                                retval = EX_BG;
                        goto fail;
@@ -528,21 +767,50 @@ int nfsmount(const char *spec, const char *node, unsigned long *flags,
                if (t >= timeout)
                        goto fail;
        }
+       nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
 
-       if (status.fhs_status != 0) {
-               fprintf(stderr,
-                       _("mount: %s:%s failed, reason given by server: %s\n"),
-                       hostname, dirname, nfs_strerror(status.fhs_status));
-               goto fail;
+       if (nfsvers == 2) {
+               if (status.nfsv2.fhs_status != 0) {
+                       bb_error_msg("%s:%s failed, reason given by server: %s",
+                               hostname, pathname,
+                               nfs_strerror(status.nfsv2.fhs_status));
+                       goto fail;
+               }
+               memcpy(data.root.data,
+                      (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
+                      NFS_FHSIZE);
+#if NFS_MOUNT_VERSION >= 4
+               data.root.size = NFS_FHSIZE;
+               memcpy(data.old_root.data,
+                      (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
+                      NFS_FHSIZE);
+#endif
+       } else {
+#if NFS_MOUNT_VERSION >= 4
+               fhandle3 *my_fhandle;
+               if (status.nfsv3.fhs_status != 0) {
+                       bb_error_msg("%s:%s failed, reason given by server: %s",
+                               hostname, pathname,
+                               nfs_strerror(status.nfsv3.fhs_status));
+                       goto fail;
+               }
+               my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
+               memset(data.old_root.data, 0, NFS_FHSIZE);
+               memset(&data.root, 0, sizeof(data.root));
+               data.root.size = my_fhandle->fhandle3_len;
+               memcpy(data.root.data,
+                      (char *) my_fhandle->fhandle3_val,
+                      my_fhandle->fhandle3_len);
+
+               data.flags |= NFS_MOUNT_VER3;
+#endif
        }
-       memcpy((char *) &root_fhandle, (char *) status.fhstatus_u.fhs_fhandle,
-               sizeof (root_fhandle));
 
        /* create nfs socket for kernel */
 
        if (tcp) {
                if (nfs_mount_version < 3) {
-                       printf(_("NFS over TCP is not supported.\n"));
+                       printf(_("NFS over TCP is not supported.\n"));
                        goto fail;
                }
                fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
@@ -576,7 +844,7 @@ int nfsmount(const char *spec, const char *node, unsigned long *flags,
          * to avoid problems with multihomed hosts.
          * --Swen
          */
-       if (linux_version_code() <= 66314
+       if (get_kernel_revision() <= 66314
            && connect(fsock, (struct sockaddr *) &server_addr,
                       sizeof (server_addr)) < 0) {
                perror(_("nfs connect"));
@@ -586,8 +854,6 @@ int nfsmount(const char *spec, const char *node, unsigned long *flags,
        /* prepare data structure for kernel */
 
        data.fd = fsock;
-       memcpy((char *) &data.root, (char *) &root_fhandle,
-               sizeof (root_fhandle));
        memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
        strncpy(data.hostname, hostname, sizeof(data.hostname));
 
@@ -596,6 +862,9 @@ int nfsmount(const char *spec, const char *node, unsigned long *flags,
        auth_destroy(mclient->cl_auth);
        clnt_destroy(mclient);
        close(msock);
+copy_data_and_return:
+       *mount_opts = xrealloc(*mount_opts, sizeof(data));
+       memcpy(*mount_opts, &data, sizeof(data));
        return 0;
 
        /* abort */
@@ -611,7 +880,7 @@ fail:
        if (fsock != -1)
                close(fsock);
        return retval;
-}      
+}
 
 /*
  * We need to translate between nfs status return values and
@@ -626,7 +895,7 @@ fail:
 #define EDQUOT ENOSPC
 #endif
 
-static struct {
+static const struct {
        enum nfs_stat stat;
        int errnum;
 } nfs_errtbl[] = {
@@ -659,651 +928,105 @@ static struct {
        { -1,                   EIO             }
 };
 
-static char *nfs_strerror(int stat)
+static char *nfs_strerror(int status)
 {
        int i;
        static char buf[256];
 
        for (i = 0; nfs_errtbl[i].stat != -1; i++) {
-               if (nfs_errtbl[i].stat == stat)
+               if (nfs_errtbl[i].stat == status)
                        return strerror(nfs_errtbl[i].errnum);
        }
-       sprintf(buf, _("unknown nfs status return value: %d"), stat);
+       sprintf(buf, _("unknown nfs status return value: %d"), status);
        return buf;
 }
 
-#if 0
-int
-my_getport(struct in_addr server, struct timeval *timeo, ...)
+static bool_t
+xdr_fhandle (XDR *xdrs, fhandle objp)
 {
-        struct sockaddr_in sin;
-        struct pmap     pmap;
-        CLIENT          *clnt;
-        int             sock = RPC_ANYSOCK, port;
-
-        pmap.pm_prog = prog;
-        pmap.pm_vers = vers;
-        pmap.pm_prot = prot;
-        pmap.pm_port = 0;
-        sin.sin_family = AF_INET;
-        sin.sin_addr = server;
-        sin.sin_port = htons(111);
-        clnt = clntudp_create(&sin, 100000, 2, *timeo, &sock);
-        status = clnt_call(clnt, PMAP_GETPORT,
-                                &pmap, (xdrproc_t) xdr_pmap,
-                                &port, (xdrproc_t) xdr_uint);
-        if (status != SUCCESS) {
-            /* natter */
-                port = 0;
-        }
-
-        clnt_destroy(clnt);
-        close(sock);
-        return port;
-}
-#endif
-
-
-
-
-
-
-
-
-
-
-
-/*
- * Please do not edit this file.
- * It was generated using rpcgen.
- */
+       //register int32_t *buf;
 
-#include <rpc/types.h>
-#include <rpc/xdr.h>
-
-/*
- * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
- * unrestricted use provided that this legend is included on all tape
- * media and as a part of the software program in whole or part.  Users
- * may copy or modify Sun RPC without charge, but are not authorized
- * to license or distribute it to anyone else except as part of a product or
- * program developed by the user or with the express written consent of
- * Sun Microsystems, Inc.
- *
- * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
- * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
- *
- * Sun RPC is provided with no support and without any obligation on the
- * part of Sun Microsystems, Inc. to assist in its use, correction,
- * modification or enhancement.
- *
- * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
- * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
- * OR ANY PART THEREOF.
- *
- * In no event will Sun Microsystems, Inc. be liable for any lost revenue
- * or profits or other special, indirect and consequential damages, even if
- * Sun has been advised of the possibility of such damages.
- *
- * Sun Microsystems, Inc.
- * 2550 Garcia Avenue
- * Mountain View, California  94043
- */
-/*
- * Copyright (c) 1985, 1990 by Sun Microsystems, Inc.
- */
-
-/* from @(#)mount.x    1.3 91/03/11 TIRPC 1.0 */
-
-bool_t
-xdr_fhandle(XDR *xdrs, fhandle objp)
-{
-
-        if (!xdr_opaque(xdrs, objp, FHSIZE)) {
-                return (FALSE);
-        }
-       return (TRUE);
+        if (!xdr_opaque (xdrs, objp, FHSIZE))
+                return FALSE;
+       return TRUE;
 }
 
 bool_t
-xdr_fhstatus(XDR *xdrs, fhstatus *objp)
+xdr_fhstatus (XDR *xdrs, fhstatus *objp)
 {
+       //register int32_t *buf;
 
-        if (!xdr_u_int(xdrs, &objp->fhs_status)) {
-                return (FALSE);
-        }
+        if (!xdr_u_int (xdrs, &objp->fhs_status))
+                return FALSE;
        switch (objp->fhs_status) {
        case 0:
-                if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle)) {
-                        return (FALSE);
-                }
+                if (!xdr_fhandle (xdrs, objp->fhstatus_u.fhs_fhandle))
+                        return FALSE;
                break;
        default:
                break;
        }
-       return (TRUE);
+       return TRUE;
 }
 
 bool_t
-xdr_dirpath(XDR *xdrs, dirpath *objp)
+xdr_dirpath (XDR *xdrs, dirpath *objp)
 {
+       //register int32_t *buf;
 
-        if (!xdr_string(xdrs, objp, MNTPATHLEN)) {
-                return (FALSE);
-        }
-       return (TRUE);
+        if (!xdr_string (xdrs, objp, MNTPATHLEN))
+                return FALSE;
+       return TRUE;
 }
 
 bool_t
-xdr_name(XDR *xdrs, name *objp)
+xdr_fhandle3 (XDR *xdrs, fhandle3 *objp)
 {
+       //register int32_t *buf;
 
-        if (!xdr_string(xdrs, objp, MNTNAMLEN)) {
-                return (FALSE);
-        }
-       return (TRUE);
+        if (!xdr_bytes (xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
+                return FALSE;
+       return TRUE;
 }
 
 bool_t
-xdr_mountlist(XDR *xdrs, mountlist *objp)
+xdr_mountres3_ok (XDR *xdrs, mountres3_ok *objp)
 {
-
-        if (!xdr_pointer(xdrs, (char **)objp, sizeof(struct mountbody), (xdrproc_t)xdr_mountbody)) {
-                return (FALSE);
-        }
-       return (TRUE);
-}
-
-bool_t
-xdr_mountbody(XDR *xdrs, mountbody *objp)
-{
-
-        if (!xdr_name(xdrs, &objp->ml_hostname)) {
-                return (FALSE);
-        }
-        if (!xdr_dirpath(xdrs, &objp->ml_directory)) {
-                return (FALSE);
-        }
-        if (!xdr_mountlist(xdrs, &objp->ml_next)) {
-                return (FALSE);
-        }
-       return (TRUE);
-}
-
-bool_t
-xdr_groups(XDR *xdrs, groups *objp)
-{
-
-        if (!xdr_pointer(xdrs, (char **)objp, sizeof(struct groupnode), (xdrproc_t)xdr_groupnode)) {
-                return (FALSE);
-        }
-       return (TRUE);
+       //register int32_t *buf;
+
+        if (!xdr_fhandle3 (xdrs, &objp->fhandle))
+                return FALSE;
+        if (!xdr_array (xdrs, (char **)&objp->auth_flavours.auth_flavours_val, (unsigned int *) &objp->auth_flavours.auth_flavours_len, ~0,
+               sizeof (int), (xdrproc_t) xdr_int))
+                return FALSE;
+       return TRUE;
 }
 
 bool_t
-xdr_groupnode(XDR *xdrs, groupnode *objp)
+xdr_mountstat3 (XDR *xdrs, mountstat3 *objp)
 {
+       //register int32_t *buf;
 
-        if (!xdr_name(xdrs, &objp->gr_name)) {
-                return (FALSE);
-        }
-        if (!xdr_groups(xdrs, &objp->gr_next)) {
-                return (FALSE);
-        }
-       return (TRUE);
+        if (!xdr_enum (xdrs, (enum_t *) objp))
+                return FALSE;
+       return TRUE;
 }
 
 bool_t
-xdr_exports(XDR *xdrs, exports *objp)
+xdr_mountres3 (XDR *xdrs, mountres3 *objp)
 {
+       //register int32_t *buf;
 
-        if (!xdr_pointer(xdrs, (char **)objp, sizeof(struct exportnode), (xdrproc_t)xdr_exportnode)) {
-                return (FALSE);
-        }
-       return (TRUE);
-}
-
-bool_t
-xdr_exportnode(XDR *xdrs, exportnode *objp)
-{
-
-        if (!xdr_dirpath(xdrs, &objp->ex_dir)) {
-                return (FALSE);
-        }
-        if (!xdr_groups(xdrs, &objp->ex_groups)) {
-                return (FALSE);
-        }
-        if (!xdr_exports(xdrs, &objp->ex_next)) {
-                return (FALSE);
-        }
-       return (TRUE);
-}
-
-bool_t
-xdr_ppathcnf(XDR *xdrs, ppathcnf *objp)
-{
-
-        register long *buf;
-
-        int i;
-
-        if (xdrs->x_op == XDR_ENCODE) {
-        buf = (long*)XDR_INLINE(xdrs,6 * BYTES_PER_XDR_UNIT);
-          if (buf == NULL) {
-                if (!xdr_int(xdrs, &objp->pc_link_max)) {
-                        return (FALSE);
-                }
-                if (!xdr_short(xdrs, &objp->pc_max_canon)) {
-                        return (FALSE);
-                }
-                if (!xdr_short(xdrs, &objp->pc_max_input)) {
-                        return (FALSE);
-                }
-                if (!xdr_short(xdrs, &objp->pc_name_max)) {
-                        return (FALSE);
-                }
-                if (!xdr_short(xdrs, &objp->pc_path_max)) {
-                        return (FALSE);
-                }
-                if (!xdr_short(xdrs, &objp->pc_pipe_buf)) {
-                        return (FALSE);
-                }
-
-         }
-         else {
-                IXDR_PUT_LONG(buf,objp->pc_link_max);
-                IXDR_PUT_SHORT(buf,objp->pc_max_canon);
-                IXDR_PUT_SHORT(buf,objp->pc_max_input);
-                IXDR_PUT_SHORT(buf,objp->pc_name_max);
-                IXDR_PUT_SHORT(buf,objp->pc_path_max);
-                IXDR_PUT_SHORT(buf,objp->pc_pipe_buf);
-         }
-        if (!xdr_u_char(xdrs, &objp->pc_vdisable)) {
-                return (FALSE);
-        }
-        if (!xdr_char(xdrs, &objp->pc_xxx)) {
-                return (FALSE);
-        }
-               buf = (long*)XDR_INLINE(xdrs,   2  * BYTES_PER_XDR_UNIT);
-               if (buf == NULL) {
-                if (!xdr_vector(xdrs, (char *)objp->pc_mask, 2, sizeof(short), (xdrproc_t)xdr_short)) {
-                        return (FALSE);
-                }
-
-         }
-         else {
-               { register short *genp; 
-                 for ( i = 0,genp=objp->pc_mask;
-                       i < 2; i++){
-                                IXDR_PUT_SHORT(buf,*genp++);
-                  }
-                };
-         }
-
-        return (TRUE);
-       } else if (xdrs->x_op == XDR_DECODE) {
-        buf = (long*)XDR_INLINE(xdrs,6 * BYTES_PER_XDR_UNIT);
-          if (buf == NULL) {
-                if (!xdr_int(xdrs, &objp->pc_link_max)) {
-                        return (FALSE);
-                }
-                if (!xdr_short(xdrs, &objp->pc_max_canon)) {
-                        return (FALSE);
-                }
-                if (!xdr_short(xdrs, &objp->pc_max_input)) {
-                        return (FALSE);
-                }
-                if (!xdr_short(xdrs, &objp->pc_name_max)) {
-                        return (FALSE);
-                }
-                if (!xdr_short(xdrs, &objp->pc_path_max)) {
-                        return (FALSE);
-                }
-                if (!xdr_short(xdrs, &objp->pc_pipe_buf)) {
-                        return (FALSE);
-                }
-
-         }
-         else {
-                objp->pc_link_max = IXDR_GET_LONG(buf);
-                objp->pc_max_canon = IXDR_GET_SHORT(buf);
-                objp->pc_max_input = IXDR_GET_SHORT(buf);
-                objp->pc_name_max = IXDR_GET_SHORT(buf);
-                objp->pc_path_max = IXDR_GET_SHORT(buf);
-                objp->pc_pipe_buf = IXDR_GET_SHORT(buf);
-         }
-        if (!xdr_u_char(xdrs, &objp->pc_vdisable)) {
-                return (FALSE);
-        }
-        if (!xdr_char(xdrs, &objp->pc_xxx)) {
-                return (FALSE);
-        }
-               buf = (long*)XDR_INLINE(xdrs,   2  * BYTES_PER_XDR_UNIT);
-               if (buf == NULL) {
-                if (!xdr_vector(xdrs, (char *)objp->pc_mask, 2, sizeof(short), (xdrproc_t)xdr_short)) {
-                        return (FALSE);
-                }
-
-         }
-         else {
-               { register short *genp; 
-                 for ( i = 0,genp=objp->pc_mask;
-                       i < 2; i++){
-                                *genp++ = IXDR_GET_SHORT(buf);
-                  }
-                };
-         }
-        return(TRUE);
-       }
-
-        if (!xdr_int(xdrs, &objp->pc_link_max)) {
-                return (FALSE);
-        }
-        if (!xdr_short(xdrs, &objp->pc_max_canon)) {
-                return (FALSE);
-        }
-        if (!xdr_short(xdrs, &objp->pc_max_input)) {
-                return (FALSE);
-        }
-        if (!xdr_short(xdrs, &objp->pc_name_max)) {
-                return (FALSE);
-        }
-        if (!xdr_short(xdrs, &objp->pc_path_max)) {
-                return (FALSE);
-        }
-        if (!xdr_short(xdrs, &objp->pc_pipe_buf)) {
-                return (FALSE);
-        }
-        if (!xdr_u_char(xdrs, &objp->pc_vdisable)) {
-                return (FALSE);
-        }
-        if (!xdr_char(xdrs, &objp->pc_xxx)) {
-                return (FALSE);
-        }
-        if (!xdr_vector(xdrs, (char *)objp->pc_mask, 2, sizeof(short), (xdrproc_t)xdr_short)) {
-                return (FALSE);
-        }
-       return (TRUE);
-}
-
-
-/*
- * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
- * unrestricted use provided that this legend is included on all tape
- * media and as a part of the software program in whole or part.  Users
- * may copy or modify Sun RPC without charge, but are not authorized
- * to license or distribute it to anyone else except as part of a product or
- * program developed by the user or with the express written consent of
- * Sun Microsystems, Inc.
- *
- * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
- * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
- *
- * Sun RPC is provided with no support and without any obligation on the
- * part of Sun Microsystems, Inc. to assist in its use, correction,
- * modification or enhancement.
- *
- * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
- * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
- * OR ANY PART THEREOF.
- *
- * In no event will Sun Microsystems, Inc. be liable for any lost revenue
- * or profits or other special, indirect and consequential damages, even if
- * Sun has been advised of the possibility of such damages.
- *
- * Sun Microsystems, Inc.
- * 2550 Garcia Avenue
- * Mountain View, California  94043
- */
-/*
- * Copyright (c) 1985, 1990 by Sun Microsystems, Inc.
- */
-
-/* from @(#)mount.x    1.3 91/03/11 TIRPC 1.0 */
-
-#include <string.h>            /* for memset() */
-
-/* Default timeout can be changed using clnt_control() */
-static struct timeval TIMEOUT = { 25, 0 };
-
-void *
-mountproc_null_1(argp, clnt)
-       void *argp;
-       CLIENT *clnt;
-{
-       static char clnt_res;
-
-       memset((char *)&clnt_res, 0, sizeof(clnt_res));
-       if (clnt_call(clnt, MOUNTPROC_NULL, (xdrproc_t) xdr_void, argp, (xdrproc_t) xdr_void, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
-               return (NULL);
-       }
-       return ((void *)&clnt_res);
-}
-
-fhstatus *
-mountproc_mnt_1(argp, clnt)
-       dirpath *argp;
-       CLIENT *clnt;
-{
-       static fhstatus clnt_res;
-
-       memset((char *)&clnt_res, 0, sizeof(clnt_res));
-       if (clnt_call(clnt, MOUNTPROC_MNT, (xdrproc_t) xdr_dirpath,
-                     (caddr_t) argp, (xdrproc_t) xdr_fhstatus,
-                     (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) {
-               return (NULL);
-       }
-       return (&clnt_res);
-}
-
-mountlist *
-mountproc_dump_1(argp, clnt)
-       void *argp;
-       CLIENT *clnt;
-{
-       static mountlist clnt_res;
-
-       memset((char *)&clnt_res, 0, sizeof(clnt_res));
-       if (clnt_call(clnt, MOUNTPROC_DUMP, (xdrproc_t) xdr_void,
-                     (caddr_t) argp, (xdrproc_t) xdr_mountlist,
-                     (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) {
-               return (NULL);
-       }
-       return (&clnt_res);
-}
-
-void *
-mountproc_umnt_1(argp, clnt)
-       dirpath *argp;
-       CLIENT *clnt;
-{
-       static char clnt_res;
-
-       memset((char *)&clnt_res, 0, sizeof(clnt_res));
-       if (clnt_call(clnt, MOUNTPROC_UMNT, (xdrproc_t) xdr_dirpath,
-                     (caddr_t) argp, (xdrproc_t) xdr_void,
-                     (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) {
-               return (NULL);
-       }
-       return ((void *)&clnt_res);
-}
-
-void *
-mountproc_umntall_1(argp, clnt)
-       void *argp;
-       CLIENT *clnt;
-{
-       static char clnt_res;
-
-       memset((char *)&clnt_res, 0, sizeof(clnt_res));
-       if (clnt_call(clnt, MOUNTPROC_UMNTALL, (xdrproc_t) xdr_void,
-                     (caddr_t) argp, (xdrproc_t) xdr_void,
-                     (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) {
-               return (NULL);
-       }
-       return ((void *)&clnt_res);
-}
-
-exports *
-mountproc_export_1(argp, clnt)
-       void *argp;
-       CLIENT *clnt;
-{
-       static exports clnt_res;
-
-       memset((char *)&clnt_res, 0, sizeof(clnt_res));
-       if (clnt_call(clnt, MOUNTPROC_EXPORT, (xdrproc_t) xdr_void,
-                     (caddr_t) argp, (xdrproc_t) xdr_exports,
-                     (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) {
-               return (NULL);
-       }
-       return (&clnt_res);
-}
-
-exports *
-mountproc_exportall_1(argp, clnt)
-       void *argp;
-       CLIENT *clnt;
-{
-       static exports clnt_res;
-
-       memset((char *)&clnt_res, 0, sizeof(clnt_res));
-       if (clnt_call(clnt, MOUNTPROC_EXPORTALL, (xdrproc_t) xdr_void,
-                     (caddr_t) argp, (xdrproc_t) xdr_exports,
-                     (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) {
-         return (NULL);
-       }
-       return (&clnt_res);
-}
-
-void *
-mountproc_null_2(argp, clnt)
-       void *argp;
-       CLIENT *clnt;
-{
-       static char clnt_res;
-
-       memset((char *)&clnt_res, 0, sizeof(clnt_res));
-       if (clnt_call(clnt, MOUNTPROC_NULL, (xdrproc_t) xdr_void, argp, (xdrproc_t) xdr_void, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
-               return (NULL);
-       }
-       return ((void *)&clnt_res);
-}
-
-fhstatus *
-mountproc_mnt_2(argp, clnt)
-       dirpath *argp;
-       CLIENT *clnt;
-{
-       static fhstatus clnt_res;
-
-       memset((char *)&clnt_res, 0, sizeof(clnt_res));
-       if (clnt_call(clnt, MOUNTPROC_MNT, (xdrproc_t) xdr_dirpath,
-                     (caddr_t) argp, (xdrproc_t) xdr_fhstatus,
-                     (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) {
-               return (NULL);
-       }
-       return (&clnt_res);
-}
-
-mountlist *
-mountproc_dump_2(argp, clnt)
-       void *argp;
-       CLIENT *clnt;
-{
-       static mountlist clnt_res;
-
-       memset((char *)&clnt_res, 0, sizeof(clnt_res));
-       if (clnt_call(clnt, MOUNTPROC_DUMP, (xdrproc_t) xdr_void, argp,
-                     (xdrproc_t) xdr_mountlist, (caddr_t) &clnt_res,
-                     TIMEOUT) != RPC_SUCCESS) {
-               return (NULL);
-       }
-       return (&clnt_res);
-}
-
-void *
-mountproc_umnt_2(argp, clnt)
-       dirpath *argp;
-       CLIENT *clnt;
-{
-       static char clnt_res;
-
-       memset((char *)&clnt_res, 0, sizeof(clnt_res));
-       if (clnt_call(clnt, MOUNTPROC_UMNT, (xdrproc_t) xdr_dirpath,
-                     (caddr_t) argp, (xdrproc_t) xdr_void,
-                     (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) {
-               return (NULL);
-       }
-       return ((void *)&clnt_res);
-}
-
-void *
-mountproc_umntall_2(argp, clnt)
-       void *argp;
-       CLIENT *clnt;
-{
-       static char clnt_res;
-
-       memset((char *)&clnt_res, 0, sizeof(clnt_res));
-       if (clnt_call(clnt, MOUNTPROC_UMNTALL, (xdrproc_t) xdr_void,
-                     (caddr_t) argp, (xdrproc_t) xdr_void,
-                     (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) {
-               return (NULL);
-       }
-       return ((void *)&clnt_res);
-}
-
-exports *
-mountproc_export_2(argp, clnt)
-       void *argp;
-       CLIENT *clnt;
-{
-       static exports clnt_res;
-
-       memset((char *)&clnt_res, 0, sizeof(clnt_res));
-       if (clnt_call(clnt, MOUNTPROC_EXPORT, (xdrproc_t) xdr_void,
-                     argp, (xdrproc_t) xdr_exports, (caddr_t) &clnt_res,
-                     TIMEOUT) != RPC_SUCCESS) {
-               return (NULL);
-       }
-       return (&clnt_res);
-}
-
-exports *
-mountproc_exportall_2(argp, clnt)
-       void *argp;
-       CLIENT *clnt;
-{
-       static exports clnt_res;
-
-       memset((char *)&clnt_res, 0, sizeof(clnt_res));
-       if (clnt_call(clnt, MOUNTPROC_EXPORTALL, (xdrproc_t) xdr_void, argp,
-                     (xdrproc_t) xdr_exports, (caddr_t) &clnt_res,
-                     TIMEOUT) != RPC_SUCCESS) {
-               return (NULL);
-       }
-       return (&clnt_res);
-}
-
-ppathcnf *
-mountproc_pathconf_2(argp, clnt)
-       dirpath *argp;
-       CLIENT *clnt;
-{
-       static ppathcnf clnt_res;
-
-       memset((char *)&clnt_res, 0, sizeof(clnt_res));
-       if (clnt_call(clnt, MOUNTPROC_PATHCONF, (xdrproc_t) xdr_dirpath,
-                     (caddr_t) argp, (xdrproc_t) xdr_ppathcnf,
-                     (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) {
-               return (NULL);
+        if (!xdr_mountstat3 (xdrs, &objp->fhs_status))
+                return FALSE;
+       switch (objp->fhs_status) {
+       case MNT_OK:
+                if (!xdr_mountres3_ok (xdrs, &objp->mountres3_u.mountinfo))
+                        return FALSE;
+               break;
+       default:
+               break;
        }
-       return (&clnt_res);
+       return TRUE;
 }
 
-
-
-