1 /* vi: set sw=4 ts=4: */
3 * nfsmount.c -- Linux NFS mount
4 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
17 * numbers to be specified on the command line.
19 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
20 * Omit the call to connect() for Linux version 1.3.11 or later.
22 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
23 * Implemented the "bg", "fg" and "retry" mount options for NFS.
25 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
26 * - added Native Language Support
28 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
33 * nfsmount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
41 #include <sys/utsname.h>
45 #include <rpc/pmap_prot.h>
46 #include <rpc/pmap_clnt.h>
49 /* This is just a warning of a common mistake. Possibly this should be a
50 * uclibc faq entry rather than in busybox... */
51 #if ENABLE_FEATURE_MOUNT_NFS && defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
52 #error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
57 * NFS stats. The good thing with these values is that NFSv3 errors are
58 * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which
59 * no-one uses anyway), so we can happily mix code as long as we make sure
60 * no NFSv3 errors are returned to NFSv2 clients.
61 * Error codes that have a `--' in the v2 column are not part of the
62 * standard, but seem to be widely used nevertheless.
65 NFS_OK = 0, /* v2 v3 */
66 NFSERR_PERM = 1, /* v2 v3 */
67 NFSERR_NOENT = 2, /* v2 v3 */
68 NFSERR_IO = 5, /* v2 v3 */
69 NFSERR_NXIO = 6, /* v2 v3 */
70 NFSERR_EAGAIN = 11, /* v2 v3 */
71 NFSERR_ACCES = 13, /* v2 v3 */
72 NFSERR_EXIST = 17, /* v2 v3 */
73 NFSERR_XDEV = 18, /* v3 */
74 NFSERR_NODEV = 19, /* v2 v3 */
75 NFSERR_NOTDIR = 20, /* v2 v3 */
76 NFSERR_ISDIR = 21, /* v2 v3 */
77 NFSERR_INVAL = 22, /* v2 v3 that Sun forgot */
78 NFSERR_FBIG = 27, /* v2 v3 */
79 NFSERR_NOSPC = 28, /* v2 v3 */
80 NFSERR_ROFS = 30, /* v2 v3 */
81 NFSERR_MLINK = 31, /* v3 */
82 NFSERR_OPNOTSUPP = 45, /* v2 v3 */
83 NFSERR_NAMETOOLONG = 63, /* v2 v3 */
84 NFSERR_NOTEMPTY = 66, /* v2 v3 */
85 NFSERR_DQUOT = 69, /* v2 v3 */
86 NFSERR_STALE = 70, /* v2 v3 */
87 NFSERR_REMOTE = 71, /* v2 v3 */
88 NFSERR_WFLUSH = 99, /* v2 */
89 NFSERR_BADHANDLE = 10001, /* v3 */
90 NFSERR_NOT_SYNC = 10002, /* v3 */
91 NFSERR_BAD_COOKIE = 10003, /* v3 */
92 NFSERR_NOTSUPP = 10004, /* v3 */
93 NFSERR_TOOSMALL = 10005, /* v3 */
94 NFSERR_SERVERFAULT = 10006, /* v3 */
95 NFSERR_BADTYPE = 10007, /* v3 */
96 NFSERR_JUKEBOX = 10008 /* v3 */
99 #define NFS_PROGRAM 100003
111 /* Disable the nls stuff */
112 # undef bindtextdomain
113 # define bindtextdomain(Domain, Directory) /* empty */
115 # define textdomain(Domain) /* empty */
118 S_QUOTA = 128, /* Quota initialized for file/directory/symlink */
123 * We want to be able to compile mount on old kernels in such a way
124 * that the binary will work well on more recent kernels.
125 * Thus, if necessary we teach nfsmount.c the structure of new fields
126 * that will come later.
128 * Moreover, the new kernel includes conflict with glibc includes
129 * so it is easiest to ignore the kernel altogether (at compile time).
132 /* NOTE: Do not make this into a 'static const int' because the pre-processor
133 * needs to test this value in some #if statements. */
134 #define NFS_MOUNT_VERSION 4
141 unsigned char data[64];
144 struct nfs_mount_data {
147 struct nfs2_fh old_root; /* 1 */
153 int acregmin; /* 1 */
154 int acregmax; /* 1 */
155 int acdirmin; /* 1 */
156 int acdirmax; /* 1 */
157 struct sockaddr_in addr; /* 1 */
158 char hostname[256]; /* 1 */
160 unsigned int bsize; /* 3 */
161 struct nfs3_fh root; /* 4 */
164 /* bits in the flags field */
166 NFS_MOUNT_SOFT = 0x0001, /* 1 */
167 NFS_MOUNT_INTR = 0x0002, /* 1 */
168 NFS_MOUNT_SECURE = 0x0004, /* 1 */
169 NFS_MOUNT_POSIX = 0x0008, /* 1 */
170 NFS_MOUNT_NOCTO = 0x0010, /* 1 */
171 NFS_MOUNT_NOAC = 0x0020, /* 1 */
172 NFS_MOUNT_TCP = 0x0040, /* 2 */
173 NFS_MOUNT_VER3 = 0x0080, /* 3 */
174 NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
175 NFS_MOUNT_NONLM = 0x0200 /* 3 */
179 #define UTIL_LINUX_VERSION "2.10m"
180 #define util_linux_version "util-linux-2.10m"
182 #define HAVE_inet_aton
187 #define HAVE_locale_h
188 #define HAVE_libintl_h
190 #define HAVE_langinfo_h
191 #define HAVE_progname
193 #define HAVE_nanosleep
194 #define HAVE_personality
195 #define HAVE_tm_gmtoff
197 static char *nfs_strerror(int status);
199 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
202 EX_FAIL = 32, /* mount failure */
203 EX_BG = 256 /* retry in background (internal only) */
208 * nfs_mount_version according to the sources seen at compile time.
210 static int nfs_mount_version;
213 * Unfortunately, the kernel prints annoying console messages
214 * in case of an unexpected nfs mount version (instead of
215 * just returning some error). Therefore we'll have to try
216 * and figure out what version the kernel expects.
219 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
220 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
221 * nfs_mount_version: version this source and running kernel can handle
224 find_kernel_nfs_mount_version(void)
226 static int kernel_version = 0;
231 nfs_mount_version = NFS_MOUNT_VERSION; /* default */
233 kernel_version = get_linux_version_code();
234 if (kernel_version) {
235 if (kernel_version < KERNEL_VERSION(2,1,32))
236 nfs_mount_version = 1;
237 else if (kernel_version < KERNEL_VERSION(2,2,18) ||
238 (kernel_version >= KERNEL_VERSION(2,3,0) &&
239 kernel_version < KERNEL_VERSION(2,3,99)))
240 nfs_mount_version = 3;
242 nfs_mount_version = 4; /* since 2.3.99pre4 */
244 if (nfs_mount_version > NFS_MOUNT_VERSION)
245 nfs_mount_version = NFS_MOUNT_VERSION;
249 get_mountport(struct sockaddr_in *server_addr,
251 long unsigned version,
255 struct pmaplist *pmap;
256 static struct pmap p = {0, 0, 0, 0};
258 server_addr->sin_port = PMAPPORT;
259 pmap = pmap_getmaps(server_addr);
261 if (version > MAX_NFSPROT)
262 version = MAX_NFSPROT;
271 if (pmap->pml_map.pm_prog != prog)
273 if (!version && p.pm_vers > pmap->pml_map.pm_vers)
275 if (version > 2 && pmap->pml_map.pm_vers != version)
277 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
279 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
280 (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) ||
281 (port && pmap->pml_map.pm_port != port))
283 memcpy(&p, &pmap->pml_map, sizeof(p));
285 pmap = pmap->pml_next;
288 p.pm_vers = MOUNTVERS;
290 p.pm_port = MOUNTPORT;
292 p.pm_prot = IPPROTO_TCP;
296 int nfsmount(const char *spec, const char *node, int *flags,
297 char **mount_opts, int running_bg)
299 static char *prev_bg_host;
305 char *mounthost=NULL;
307 struct timeval total_timeout;
308 enum clnt_stat clnt_stat;
309 struct nfs_mount_data data;
313 struct sockaddr_in server_addr;
314 struct sockaddr_in mount_server_addr;
317 struct timeval retry_timeout;
319 struct fhstatus nfsv2;
320 struct mountres3 nfsv3;
345 find_kernel_nfs_mount_version();
350 if (strlen(spec) >= sizeof(hostdir)) {
351 bb_error_msg("excessively long host:dir argument");
354 strcpy(hostdir, spec);
355 if ((s = strchr(hostdir, ':'))) {
359 /* Ignore all but first hostname in replicated mounts
360 until they can be fully supported. (mack@sgi.com) */
361 if ((s = strchr(hostdir, ','))) {
363 bb_error_msg("warning: multiple hostnames not supported");
366 bb_error_msg("directory to mount not in host:dir format");
370 server_addr.sin_family = AF_INET;
371 #ifdef HAVE_inet_aton
372 if (!inet_aton(hostname, &server_addr.sin_addr))
375 if ((hp = gethostbyname(hostname)) == NULL) {
376 bb_herror_msg("%s", hostname);
379 if (hp->h_length > sizeof(struct in_addr)) {
380 bb_error_msg("got bad hp->h_length");
381 hp->h_length = sizeof(struct in_addr);
383 memcpy(&server_addr.sin_addr,
384 hp->h_addr, hp->h_length);
388 memcpy (&mount_server_addr, &server_addr, sizeof (mount_server_addr));
390 /* add IP address to mtab options for use when unmounting */
392 s = inet_ntoa(server_addr.sin_addr);
393 old_opts = *mount_opts;
396 if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
397 bb_error_msg("excessively long option argument");
400 sprintf(new_opts, "%s%saddr=%s",
401 old_opts, *old_opts ? "," : "", s);
402 *mount_opts = bb_xstrdup(new_opts);
404 /* Set default options.
405 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
406 * let the kernel decide.
407 * timeo is filled in after we know whether it'll be TCP or UDP. */
408 memset(&data, 0, sizeof(data));
414 #if NFS_MOUNT_VERSION >= 2
415 data.namlen = NAME_MAX;
425 retry = 10000; /* 10000 minutes ~ 1 week */
428 mountprog = MOUNTPROG;
432 nfsprog = NFS_PROGRAM;
437 for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
438 if ((opteq = strchr(opt, '='))) {
439 val = atoi(opteq + 1);
441 if (!strcmp(opt, "rsize"))
443 else if (!strcmp(opt, "wsize"))
445 else if (!strcmp(opt, "timeo"))
447 else if (!strcmp(opt, "retrans"))
449 else if (!strcmp(opt, "acregmin"))
451 else if (!strcmp(opt, "acregmax"))
453 else if (!strcmp(opt, "acdirmin"))
455 else if (!strcmp(opt, "acdirmax"))
457 else if (!strcmp(opt, "actimeo")) {
463 else if (!strcmp(opt, "retry"))
465 else if (!strcmp(opt, "port"))
467 else if (!strcmp(opt, "mountport"))
469 else if (!strcmp(opt, "mounthost"))
470 mounthost=bb_xstrndup(opteq+1,
471 strcspn(opteq+1," \t\n\r,"));
472 else if (!strcmp(opt, "mountprog"))
474 else if (!strcmp(opt, "mountvers"))
476 else if (!strcmp(opt, "nfsprog"))
478 else if (!strcmp(opt, "nfsvers") ||
479 !strcmp(opt, "vers"))
481 else if (!strcmp(opt, "proto")) {
482 if (!strncmp(opteq+1, "tcp", 3))
484 else if (!strncmp(opteq+1, "udp", 3))
487 printf("Warning: Unrecognized proto= option.\n");
488 } else if (!strcmp(opt, "namlen")) {
489 #if NFS_MOUNT_VERSION >= 2
490 if (nfs_mount_version >= 2)
494 printf("Warning: Option namlen is not supported.\n");
495 } else if (!strcmp(opt, "addr"))
498 printf("unknown nfs mount parameter: %s=%d\n", opt, val);
504 if (!strncmp(opt, "no", 2)) {
508 if (!strcmp(opt, "bg"))
510 else if (!strcmp(opt, "fg"))
512 else if (!strcmp(opt, "soft"))
514 else if (!strcmp(opt, "hard"))
516 else if (!strcmp(opt, "intr"))
518 else if (!strcmp(opt, "posix"))
520 else if (!strcmp(opt, "cto"))
522 else if (!strcmp(opt, "ac"))
524 else if (!strcmp(opt, "tcp"))
526 else if (!strcmp(opt, "udp"))
528 else if (!strcmp(opt, "lock")) {
529 if (nfs_mount_version >= 3)
532 printf("Warning: option nolock is not supported.\n");
534 printf("unknown nfs mount option: %s%s\n", val ? "" : "no", opt);
539 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
541 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
542 | (intr ? NFS_MOUNT_INTR : 0)
543 | (posix ? NFS_MOUNT_POSIX : 0)
544 | (nocto ? NFS_MOUNT_NOCTO : 0)
545 | (noac ? NFS_MOUNT_NOAC : 0);
546 #if NFS_MOUNT_VERSION >= 2
547 if (nfs_mount_version >= 2)
548 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
550 #if NFS_MOUNT_VERSION >= 3
551 if (nfs_mount_version >= 3)
552 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
554 if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
555 bb_error_msg("NFSv%d not supported!", nfsvers);
558 if (nfsvers && !mountvers)
559 mountvers = (nfsvers < 3) ? 1 : nfsvers;
560 if (nfsvers && nfsvers < mountvers) {
564 /* Adjust options if none specified */
566 data.timeo = tcp ? 70 : 7;
568 #ifdef NFS_MOUNT_DEBUG
569 printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
570 data.rsize, data.wsize, data.timeo, data.retrans);
571 printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
572 data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
573 printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
574 port, bg, retry, data.flags);
575 printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n",
576 mountprog, mountvers, nfsprog, nfsvers);
577 printf("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n",
578 (data.flags & NFS_MOUNT_SOFT) != 0,
579 (data.flags & NFS_MOUNT_INTR) != 0,
580 (data.flags & NFS_MOUNT_POSIX) != 0,
581 (data.flags & NFS_MOUNT_NOCTO) != 0,
582 (data.flags & NFS_MOUNT_NOAC) != 0);
583 #if NFS_MOUNT_VERSION >= 2
585 (data.flags & NFS_MOUNT_TCP) != 0);
589 data.version = nfs_mount_version;
591 if (*flags & MS_REMOUNT)
592 goto copy_data_and_return;
595 * If the previous mount operation on the same host was
596 * backgrounded, and the "bg" for this mount is also set,
597 * give up immediately, to avoid the initial timeout.
599 if (bg && !running_bg &&
600 prev_bg_host && strcmp(hostname, prev_bg_host) == 0) {
606 /* create mount daemon client */
607 /* See if the nfs host = mount host. */
609 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
610 mount_server_addr.sin_family = AF_INET;
611 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
613 if ((hp = gethostbyname(mounthost)) == NULL) {
614 bb_herror_msg("%s", mounthost);
617 if (hp->h_length > sizeof(struct in_addr)) {
618 bb_error_msg("got bad hp->h_length?");
619 hp->h_length = sizeof(struct in_addr);
621 mount_server_addr.sin_family = AF_INET;
622 memcpy(&mount_server_addr.sin_addr,
623 hp->h_addr, hp->h_length);
629 * The following loop implements the mount retries. On the first
630 * call, "running_bg" is 0. When the mount times out, and the
631 * "bg" option is set, the exit status EX_BG will be returned.
632 * For a backgrounded mount, there will be a second call by the
633 * child process with "running_bg" set to 1.
635 * The case where the mount point is not present and the "bg"
636 * option is set, is treated as a timeout. This is done to
637 * support nested mounts.
639 * The "retry" count specified by the user is the number of
640 * minutes to retry before giving up.
642 * Only the first error message will be displayed.
644 retry_timeout.tv_sec = 3;
645 retry_timeout.tv_usec = 0;
646 total_timeout.tv_sec = 20;
647 total_timeout.tv_usec = 0;
648 timeout = time(NULL) + 60 * retry;
653 if (bg && stat(node, &statbuf) == -1) {
655 sleep(val); /* 1, 2, 4, 8, 16, 30, ... */
661 /* be careful not to use too many CPU cycles */
665 pm_mnt = get_mountport(&mount_server_addr,
671 /* contact the mount daemon via TCP */
672 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
675 switch (pm_mnt->pm_prot) {
677 mclient = clntudp_create(&mount_server_addr,
684 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
687 mclient = clnttcp_create(&mount_server_addr,
696 /* try to mount hostname:pathname */
697 mclient->cl_auth = authunix_create_default();
699 /* make pointers in xdr_mountres3 NULL so
700 * that xdr_array allocates memory for us
702 memset(&status, 0, sizeof(status));
704 if (pm_mnt->pm_vers == 3)
705 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
706 (xdrproc_t) xdr_dirpath,
708 (xdrproc_t) xdr_mountres3,
712 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
713 (xdrproc_t) xdr_dirpath,
715 (xdrproc_t) xdr_fhstatus,
719 if (clnt_stat == RPC_SUCCESS)
720 break; /* we're done */
721 if (errno != ECONNREFUSED) {
722 clnt_perror(mclient, "mount");
723 goto fail; /* don't retry */
725 if (!running_bg && prevt == 0)
726 clnt_perror(mclient, "mount");
727 auth_destroy(mclient->cl_auth);
728 clnt_destroy(mclient);
732 if (!running_bg && prevt == 0)
733 clnt_pcreateerror("mount");
740 prev_bg_host = bb_xstrdup(hostname);
749 nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
752 if (status.nfsv2.fhs_status != 0) {
753 bb_error_msg("%s:%s failed, reason given by server: %s",
755 nfs_strerror(status.nfsv2.fhs_status));
758 memcpy(data.root.data,
759 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
761 #if NFS_MOUNT_VERSION >= 4
762 data.root.size = NFS_FHSIZE;
763 memcpy(data.old_root.data,
764 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
768 #if NFS_MOUNT_VERSION >= 4
769 fhandle3 *my_fhandle;
770 if (status.nfsv3.fhs_status != 0) {
771 bb_error_msg("%s:%s failed, reason given by server: %s",
773 nfs_strerror(status.nfsv3.fhs_status));
776 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
777 memset(data.old_root.data, 0, NFS_FHSIZE);
778 memset(&data.root, 0, sizeof(data.root));
779 data.root.size = my_fhandle->fhandle3_len;
780 memcpy(data.root.data,
781 (char *) my_fhandle->fhandle3_val,
782 my_fhandle->fhandle3_len);
784 data.flags |= NFS_MOUNT_VER3;
788 /* create nfs socket for kernel */
791 if (nfs_mount_version < 3) {
792 printf("NFS over TCP is not supported.\n");
795 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
797 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
799 perror("nfs socket");
802 if (bindresvport(fsock, 0) < 0) {
803 perror("nfs bindresvport");
807 server_addr.sin_port = PMAPPORT;
808 port = pmap_getport(&server_addr, nfsprog, nfsvers,
809 tcp ? IPPROTO_TCP : IPPROTO_UDP);
812 #ifdef NFS_MOUNT_DEBUG
814 printf("used portmapper to find NFS port\n");
817 #ifdef NFS_MOUNT_DEBUG
818 printf("using port %d for nfs daemon\n", port);
820 server_addr.sin_port = htons(port);
822 * connect() the socket for kernels 1.3.10 and below only,
823 * to avoid problems with multihomed hosts.
826 if (get_linux_version_code() <= KERNEL_VERSION(2,3,10)
827 && connect(fsock, (struct sockaddr *) &server_addr,
828 sizeof (server_addr)) < 0) {
829 perror("nfs connect");
833 /* prepare data structure for kernel */
836 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
837 strncpy(data.hostname, hostname, sizeof(data.hostname));
841 auth_destroy(mclient->cl_auth);
842 clnt_destroy(mclient);
844 copy_data_and_return:
845 *mount_opts = xrealloc(*mount_opts, sizeof(data));
846 memcpy(*mount_opts, &data, sizeof(data));
854 auth_destroy(mclient->cl_auth);
855 clnt_destroy(mclient);
865 * We need to translate between nfs status return values and
866 * the local errno values which may not be the same.
868 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
869 * "after #include <errno.h> the symbol errno is reserved for any use,
870 * it cannot even be used as a struct tag or field name".
874 #define EDQUOT ENOSPC
877 static const struct {
882 { NFSERR_PERM, EPERM },
883 { NFSERR_NOENT, ENOENT },
885 { NFSERR_NXIO, ENXIO },
886 { NFSERR_ACCES, EACCES },
887 { NFSERR_EXIST, EEXIST },
888 { NFSERR_NODEV, ENODEV },
889 { NFSERR_NOTDIR, ENOTDIR },
890 { NFSERR_ISDIR, EISDIR },
892 { NFSERR_INVAL, EINVAL }, /* that Sun forgot */
894 { NFSERR_FBIG, EFBIG },
895 { NFSERR_NOSPC, ENOSPC },
896 { NFSERR_ROFS, EROFS },
897 { NFSERR_NAMETOOLONG, ENAMETOOLONG },
898 { NFSERR_NOTEMPTY, ENOTEMPTY },
899 { NFSERR_DQUOT, EDQUOT },
900 { NFSERR_STALE, ESTALE },
902 { NFSERR_WFLUSH, EWFLUSH },
904 /* Throw in some NFSv3 values for even more fun (HP returns these) */
910 static char *nfs_strerror(int status)
913 static char buf[256];
915 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
916 if (nfs_errtbl[i].stat == status)
917 return strerror(nfs_errtbl[i].errnum);
919 sprintf(buf, "unknown nfs status return value: %d", status);
924 xdr_fhandle (XDR *xdrs, fhandle objp)
926 if (!xdr_opaque (xdrs, objp, FHSIZE))
932 xdr_fhstatus (XDR *xdrs, fhstatus *objp)
934 if (!xdr_u_int (xdrs, &objp->fhs_status))
936 switch (objp->fhs_status) {
938 if (!xdr_fhandle (xdrs, objp->fhstatus_u.fhs_fhandle))
948 xdr_dirpath (XDR *xdrs, dirpath *objp)
950 if (!xdr_string (xdrs, objp, MNTPATHLEN))
956 xdr_fhandle3 (XDR *xdrs, fhandle3 *objp)
958 if (!xdr_bytes (xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
964 xdr_mountres3_ok (XDR *xdrs, mountres3_ok *objp)
966 if (!xdr_fhandle3 (xdrs, &objp->fhandle))
968 if (!xdr_array (xdrs, (char **)&objp->auth_flavours.auth_flavours_val, (unsigned int *) &objp->auth_flavours.auth_flavours_len, ~0,
969 sizeof (int), (xdrproc_t) xdr_int))
975 xdr_mountstat3 (XDR *xdrs, mountstat3 *objp)
977 if (!xdr_enum (xdrs, (enum_t *) objp))
983 xdr_mountres3 (XDR *xdrs, mountres3 *objp)
985 if (!xdr_mountstat3 (xdrs, &objp->fhs_status))
987 switch (objp->fhs_status) {
989 if (!xdr_mountres3_ok (xdrs, &objp->mountres3_u.mountinfo))