/* * Copyright (c) 1999-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Rick Macklem at The University of Guelph. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SIMON_SAYS_USE_NFSV4 "4.0alpha" #define _PATH_NFS_CONF "/etc/nfs.conf" struct nfs_conf_client { int access_cache_timeout; int allow_async; int initialdowndelay; int iosize; int nextdowndelay; int nfsiod_thread_max; int statfs_rate_limit; }; /* init to invalid values so we will only set values specified in nfs.conf */ struct nfs_conf_client config = { -1, -1, -1, -1, -1, -1, -1, }; /* mount options */ /* values */ #define ALTF_ATTRCACHE_VAL 0x00000001 #define ALTF_DSIZE 0x00000002 #define ALTF_MAXGROUPS 0x00000004 #define ALTF_MOUNTPORT 0x00000008 #define ALTF_PORT 0x00000010 #define ALTF_PROTO 0x00000020 #define ALTF_READAHEAD 0x00000040 #define ALTF_RETRANS 0x00000080 #define ALTF_RETRYCNT 0x00000100 #define ALTF_RSIZE 0x00000200 #define ALTF_SEC 0x00000400 #define ALTF_TIMEO 0x00000800 #define ALTF_VERS 0x00001000 #define ALTF_VERS2 0x00002000 /* deprecated */ #define ALTF_VERS3 0x00004000 /* deprecated */ #define ALTF_VERS4 0x00008000 /* deprecated */ #define ALTF_WSIZE 0x00010000 #define ALTF_DEADTIMEOUT 0x00020000 /* switches */ #define ALTF_ATTRCACHE 0x00000001 #define ALTF_BG 0x00000002 #define ALTF_CONN 0x00000004 #define ALTF_DUMBTIMR 0x00000008 #define ALTF_HARD 0x00000010 #define ALTF_INTR 0x00000020 #define ALTF_LOCALLOCKS 0x00000040 #define ALTF_LOCKS 0x00000080 #define ALTF_MNTUDP 0x00000100 #define ALTF_NEGNAMECACHE 0x00000200 #define ALTF_RDIRPLUS 0x00000400 #define ALTF_RESVPORT 0x00000800 #define ALTF_SOFT 0x00001000 #define ALTF_TCP 0x00002000 #define ALTF_UDP 0x00004000 #define ALTF_MUTEJUKEBOX 0x00008000 /* standard and value-setting options */ struct mntopt mopts[] = { MOPT_STDOPTS, MOPT_FORCE, MOPT_UPDATE, MOPT_ASYNC, MOPT_SYNC, { "acdirmax", 0, ALTF_ATTRCACHE_VAL, 1 }, { "acdirmin", 0, ALTF_ATTRCACHE_VAL, 1 }, { "acregmax", 0, ALTF_ATTRCACHE_VAL, 1 }, { "acregmin", 0, ALTF_ATTRCACHE_VAL, 1 }, { "actimeo", 0, ALTF_ATTRCACHE_VAL, 1 }, { "dsize", 0, ALTF_DSIZE, 1 }, { "maxgroups", 0, ALTF_MAXGROUPS, 1 }, { "mountport", 0, ALTF_MOUNTPORT, 1 }, { "port", 0, ALTF_PORT, 1 }, { "proto", 0, ALTF_PROTO, 1 }, { "readahead", 0, ALTF_READAHEAD, 1 }, { "retrans", 0, ALTF_RETRANS, 1 }, { "retrycnt", 0, ALTF_RETRYCNT, 1 }, { "rsize", 0, ALTF_RSIZE, 1 }, { "rwsize", 0, ALTF_RSIZE|ALTF_WSIZE, 1 }, { "sec", 0, ALTF_SEC, 1 }, { "timeo", 0, ALTF_TIMEO, 1 }, { "vers", 0, ALTF_VERS, 1 }, { "wsize", 0, ALTF_WSIZE, 1 }, { "deadtimeout", 0, ALTF_DEADTIMEOUT, 1 }, /**/ { "nfsv2", 0, ALTF_VERS2, 1 }, /* deprecated, use vers=# */ { "nfsv3", 0, ALTF_VERS3, 1 }, /* deprecated, use vers=# */ { "nfsv4", 0, ALTF_VERS4, 1 }, /* deprecated, use vers=# */ { "nfsvers", 0, ALTF_VERS, 1 }, /* deprecated, use vers=# */ { NULL } }; /* on/off switching options */ struct mntopt mopts_switches[] = { { "ac", 0, ALTF_ATTRCACHE, 1 }, { "bg", 0, ALTF_BG, 1 }, { "conn", 0, ALTF_CONN, 1 }, { "dumbtimer", 0, ALTF_DUMBTIMR, 1 }, { "hard", 0, ALTF_HARD, 1 }, { "intr", 0, ALTF_INTR, 1 }, { "locallocks", 0, ALTF_LOCALLOCKS, 1 }, { "lock", 0, ALTF_LOCKS, 1 }, { "lockd", 0, ALTF_LOCKS, 1 }, { "locks", 0, ALTF_LOCKS, 1 }, { "mntudp", 0, ALTF_MNTUDP, 1 }, { "mutejukebox", 0, ALTF_MUTEJUKEBOX, 1 }, { "negnamecache", 0, ALTF_NEGNAMECACHE, 1 }, { "nlm", 0, ALTF_LOCKS, 1 }, { "rdirplus", 0, ALTF_RDIRPLUS, 1 }, { "resvport", 0, ALTF_RESVPORT, 1 }, { "soft", 0, ALTF_SOFT, 1 }, { "tcp", 0, ALTF_TCP, 1 }, { "udp", 0, ALTF_UDP, 1 }, { NULL } }; /* inverse of mopts_switches (set up at runtime) */ struct mntopt *mopts_switches_no = NULL; /* default NFS mount args */ struct nfs_args nfsdefargs = { NFS_ARGSVERSION, /* struct version */ NULL, /* server address */ sizeof (struct sockaddr_in), /* server address size */ SOCK_STREAM, /* socket type */ 0, /* socket protocol */ NULL, /* file handle */ 0, /* file handle size */ NFSMNT_NFSV3, /* NFS mount flags */ NFS_WSIZE, /* write size */ NFS_RSIZE, /* read size */ NFS_READDIRSIZE, /* directory read size */ 10, /* initial timeout */ NFS_RETRANS, /* times to retry send */ NFS_MAXGRPS, /* max size of group list */ NFS_DEFRAHEAD, /* #blocks of readahead to do */ 0, /* obsolete */ 0, /* obsolete */ NULL, /* server's name (mntfromname) */ NFS_MINATTRTIMO, /* reg file min attr cache timeout */ NFS_MAXATTRTIMO, /* reg file max attr cache timeout */ NFS_MINDIRATTRTIMO, /* dir min attr cache timeout */ NFS_MAXDIRATTRTIMO, /* dir max attr cache timeout */ 0, /* security mechanism flavor */ 0 /* dead timeout */ }; int nfsproto = IPPROTO_TCP; int forceproto = 0; int forceport = 0; int mountport = 0; int mnttcp_ok = 1; int force2 = 0; int force3 = 0; int force4 = 0; int nolocallocks = 0; #define PMAP_TIMEOUT_SHORT 10 // set when retrycnt=0 (automounter) #define PMAP_TIMEOUT_LONG 60 int pmap_timeout = PMAP_TIMEOUT_LONG; // seconds /* flags controlling mount_nfs behavior */ #define BGRND 0x1 #define ISBGRND 0x2 int opflags = 0; int verbose = 0; #define DEF_RETRY_BG 10000 #define DEF_RETRY_FG 1 int retrycnt = -1; uid_t real_uid, eff_uid; /* mount options */ int mntflags = 0; struct nfs_args nfsargs, *nfsargsp = &nfsargs; #ifdef __LP64__ typedef unsigned int xdr_ulong_t; typedef int xdr_long_t; #else typedef unsigned long xdr_ulong_t; typedef long xdr_long_t; #endif struct nfhret { xdr_ulong_t stat; xdr_long_t vers; xdr_long_t auth; bool_t sysok; xdr_long_t fhsize; uint8_t nfh[NFSX_V3FHMAX]; }; /* function prototypes */ void checkNFSVersionOptions(void); void dump_mount_options(char *); void handle_mntopts(char *); void warn_badoptions(char *); int config_read(struct nfs_conf_client *); void config_sysctl(void); int pmap_getport_timely(struct sockaddr_in *, int, int, int, int); int getnfsargs(char *, struct nfs_args *); void set_rpc_maxgrouplist(int); __dead void usage(void); int xdr_dir(XDR *, char *); int xdr_fh(XDR *, struct nfhret *); int main(int argc, char *argv[]) { uint i; int c, num, rv; char name[MAXPATHLEN], *p, *spec; /* drop setuid root privs asap */ eff_uid = geteuid(); real_uid = getuid(); seteuid(real_uid); /* set up mopts_switches_no from mopts_switches */ mopts_switches_no = malloc(sizeof(mopts_switches)); if (!mopts_switches_no) errx(ENOMEM, "memory allocation failed"); bcopy(mopts_switches, mopts_switches_no, sizeof(mopts_switches)); for (i=0; i < sizeof(mopts_switches)/sizeof(struct mntopt); i++) mopts_switches_no[i].m_inverse = (mopts_switches[i].m_inverse == 0); /* set up defaults and process options */ nfsargs = nfsdefargs; config_read(&config); while ((c = getopt(argc, argv, "234a:bcdg:I:iLlo:PR:r:sTt:Uvw:x:")) != EOF) switch (c) { case '4': errx(EPERM, "sorry, you must specify vers=%s to use the alpha-quality NFSv4 support", SIMON_SAYS_USE_NFSV4); force4 = 1; checkNFSVersionOptions(); break; case '3': force3 = 1; checkNFSVersionOptions(); break; case '2': force2 = 1; checkNFSVersionOptions(); nfsargsp->flags &= ~NFSMNT_NFSV3; break; case 'a': num = strtol(optarg, &p, 10); if (*p || num < 0) { warnx("illegal -a value -- %s", optarg); } else { nfsargsp->readahead = num; nfsargsp->flags |= NFSMNT_READAHEAD; } break; case 'b': opflags |= BGRND; break; case 'c': nfsargsp->flags |= NFSMNT_NOCONN; break; case 'd': nfsargsp->flags |= NFSMNT_DUMBTIMR; break; case 'g': num = strtol(optarg, &p, 10); if (*p || num <= 0) { warnx("illegal maxgroups value -- %s", optarg); } else { set_rpc_maxgrouplist(num); nfsargsp->maxgrouplist = num; nfsargsp->flags |= NFSMNT_MAXGRPS; } break; case 'I': num = strtol(optarg, &p, 10); if (*p || num <= 0) { warnx("illegal -I value -- %s", optarg); } else { nfsargsp->readdirsize = num; nfsargsp->flags |= NFSMNT_READDIRSIZE; } break; case 'i': nfsargsp->flags |= NFSMNT_INT; break; case 'L': nfsargsp->flags |= NFSMNT_NOLOCKS; nfsargsp->flags &= ~NFSMNT_LOCALLOCKS; break; case 'l': nfsargsp->flags |= NFSMNT_RDIRPLUS; break; case 'o': handle_mntopts(optarg); break; case 'P': nfsargsp->flags |= NFSMNT_RESVPORT; break; case 'R': num = strtol(optarg, &p, 10); if (*p) warnx("illegal -R value -- %s", optarg); else { retrycnt = num; if (retrycnt == 0) pmap_timeout = PMAP_TIMEOUT_SHORT; } break; case 'r': num = strtol(optarg, &p, 10); if ((*p == 'k') || (*p == 'K')) { num *= 1024; p++; } if (*p || num <= 0) { warnx("illegal -r value -- %s", optarg); } else { nfsargsp->rsize = num; nfsargsp->flags |= NFSMNT_RSIZE; } break; case 's': nfsargsp->flags |= NFSMNT_SOFT; break; case 'T': nfsargsp->sotype = SOCK_STREAM; nfsproto = IPPROTO_TCP; forceproto++; break; case 't': num = strtol(optarg, &p, 10); if (*p || num <= 0) { warnx("illegal -t value -- %s", optarg); } else { nfsargsp->timeo = num; nfsargsp->flags |= NFSMNT_TIMEO; } break; case 'w': num = strtol(optarg, &p, 10); if ((*p == 'k') || (*p == 'K')) { num *= 1024; p++; } if (*p || num <= 0) { warnx("illegal -w value -- %s", optarg); } else { nfsargsp->wsize = num; nfsargsp->flags |= NFSMNT_WSIZE; } break; case 'x': num = strtol(optarg, &p, 10); if (*p || num <= 0) { warnx("illegal -x value -- %s", optarg); } else { nfsargsp->retrans = num; nfsargsp->flags |= NFSMNT_RETRANS; } break; case 'U': mnttcp_ok = 0; break; case 'v': verbose++; break; default: usage(); break; } argc -= optind; argv += optind; /* soft, read-only mount implies ... */ if ((nfsargsp->flags & NFSMNT_SOFT) && (mntflags & MNT_RDONLY)) { /* ...use of local locks */ if (!(nfsargsp->flags & NFSMNT_NOLOCKS) && !nolocallocks) nfsargsp->flags |= NFSMNT_LOCALLOCKS; /* ... dead timeout */ if (!(nfsargsp->flags & NFSMNT_DEADTIMEOUT)) { nfsargsp->flags |= NFSMNT_DEADTIMEOUT; nfsargsp->deadtimeout = 60; } } if ((argc == 1) && !strcmp(argv[0], "configupdate")) { config_sysctl(); exit(0); } if (argc != 2) usage(); spec = *argv++; if (realpath(*argv, name) == NULL) err(errno ? errno : 1, "realpath %s", name); if ((rv = getnfsargs(spec, nfsargsp))) exit(rv); config_sysctl(); if (verbose) dump_mount_options(name); if (mount("nfs", name, mntflags, nfsargsp)) err(errno, "%s", name); exit(0); } void checkNFSVersionOptions(void) { if ((force2 + force3 + force4) > 1) errx(EINVAL,"conflicting NFS version options:%s%s%s", force2 ? " v2" : "", force3 ? " v3" : "", force4 ? " v4" : ""); } void handle_mntopts(char *opts) { mntoptparse_t mop; char *p; const char *p2; int num, altflags = 0, dummyflags; getmnt_silent = 1; /* do standard and value-setting options first */ altflags = 0; mop = getmntopts(opts, mopts, &mntflags, &altflags); if (mop == NULL) errx(EINVAL, "getmntops failed: %s", opts); if (altflags & ALTF_ATTRCACHE_VAL) { if ((p2 = getmntoptstr(mop, "actimeo"))) { num = getmntoptnum(mop, "actimeo"); if (num < 0) { warnx("illegal actimeo value -- %s", p2); } else { nfsargsp->acregmin = num; nfsargsp->acregmax = nfsargsp->acregmin; nfsargsp->acdirmin = nfsargsp->acregmin; nfsargsp->acdirmax = nfsargsp->acregmin; nfsargsp->flags |= NFSMNT_ACREGMIN; nfsargsp->flags |= NFSMNT_ACREGMAX; nfsargsp->flags |= NFSMNT_ACDIRMIN; nfsargsp->flags |= NFSMNT_ACDIRMAX; } } if ((p2 = getmntoptstr(mop, "acregmin"))) { num = getmntoptnum(mop, "acregmin"); if (num < 0) { warnx("illegal acregmin value -- %s", p2); } else { nfsargsp->acregmin = num; nfsargsp->flags |= NFSMNT_ACREGMIN; } } if ((p2 = getmntoptstr(mop, "acregmax"))) { num = getmntoptnum(mop, "acregmax"); if (num < 0) { warnx("illegal acregmax value -- %s", p2); } else { nfsargsp->acregmax = num; nfsargsp->flags |= NFSMNT_ACREGMAX; } } if ((p2 = getmntoptstr(mop, "acdirmin"))) { num = getmntoptnum(mop, "acdirmin"); if (num < 0) { warnx("illegal acdirmin value -- %s", p2); } else { nfsargsp->acdirmin = num; nfsargsp->flags |= NFSMNT_ACDIRMIN; } } if ((p2 = getmntoptstr(mop, "acdirmax"))) { num = getmntoptnum(mop, "acdirmax"); if (num < 0) { warnx("illegal acdirmax value -- %s", p2); } else { nfsargsp->acdirmax = num; nfsargsp->flags |= NFSMNT_ACDIRMAX; } } } if (altflags & ALTF_DEADTIMEOUT) { if ((p2 = getmntoptstr(mop, "deadtimeout"))) { num = strtol(p2, &p, 10); if (num < 0) { warnx("illegal deadtimeout value -- %s", p2); } else { nfsargsp->deadtimeout = num; nfsargsp->flags |= NFSMNT_DEADTIMEOUT; } } } if (altflags & ALTF_DSIZE) { if ((p2 = getmntoptstr(mop, "dsize"))) { num = strtol(p2, &p, 10); if ((*p == 'k') || (*p == 'K')) { num *= 1024; p++; } if ((num <= 0) || *p) { warnx("illegal dsize value -- %s", p2); } else { nfsargsp->readdirsize = num; nfsargsp->flags |= NFSMNT_READDIRSIZE; } } } if (altflags & ALTF_MAXGROUPS) { num = getmntoptnum(mop, "maxgroups"); if (num <= 0) { warnx("illegal maxgroups value -- %s", getmntoptstr(mop, "maxgroups")); } else { set_rpc_maxgrouplist(num); nfsargsp->maxgrouplist = num; nfsargsp->flags |= NFSMNT_MAXGRPS; } } if (altflags & ALTF_MOUNTPORT) { num = getmntoptnum(mop, "mountport"); if (num < 0) warnx("illegal mountport number -- %s", getmntoptstr(mop, "mountport")); else mountport = num; } if (altflags & ALTF_PORT) { num = getmntoptnum(mop, "port"); if (num < 0) warnx("illegal port number -- %s", getmntoptstr(mop, "port")); else forceport = num; } if (altflags & ALTF_PROTO) { p2 = getmntoptstr(mop, "proto"); if (p2) { if (!strcasecmp(p2, "tcp")) { nfsargsp->sotype = SOCK_STREAM; nfsproto = IPPROTO_TCP; forceproto++; } else if (!strcasecmp(p2, "udp")) { nfsargsp->sotype = SOCK_DGRAM; nfsproto = IPPROTO_UDP; forceproto++; } else { warnx("unknown protocol -- %s", p2); } } } if (altflags & ALTF_READAHEAD) { num = getmntoptnum(mop, "readahead"); if (num < 0) { warnx("illegal readahead value -- %s", getmntoptstr(mop, "readahead")); } else { nfsargsp->readahead = num; nfsargsp->flags |= NFSMNT_READAHEAD; } } if (altflags & ALTF_RETRANS) { num = getmntoptnum(mop, "retrans"); if (num <= 0) { warnx("illegal retrans value -- %s", getmntoptstr(mop, "retrans")); } else { nfsargsp->retrans = num; nfsargsp->flags |= NFSMNT_RETRANS; } } if (altflags & ALTF_RETRYCNT) { retrycnt = getmntoptnum(mop, "retrycnt"); if (retrycnt == 0) pmap_timeout = PMAP_TIMEOUT_SHORT; } if (altflags & ALTF_RSIZE) { p2 = getmntoptstr(mop, "rwsize"); if (!p2) p2 = getmntoptstr(mop, "rsize"); if (p2) { num = strtol(p2, &p, 10); if ((*p == 'k') || (*p == 'K')) { num *= 1024; p++; } if ((num <= 0) || *p) { warnx("illegal rsize value -- %s", p2); } else { nfsargsp->rsize = num; nfsargsp->flags |= NFSMNT_RSIZE; } } } if (altflags & ALTF_SEC) { p2 = getmntoptstr(mop, "sec"); if (!p2) warnx("missing security value for sec= option"); else if (!strcmp("krb5p", p2)) nfsargsp->auth = RPCAUTH_KRB5P; else if (!strcmp("krb5i", p2)) nfsargsp->auth = RPCAUTH_KRB5I; else if (!strcmp("krb5", p2)) nfsargsp->auth = RPCAUTH_KRB5; else if (!strcmp("sys", p2)) nfsargsp->auth = RPCAUTH_SYS; else warnx("illegal security value -- %s", p2); if (nfsargsp->auth == RPCAUTH_SYS) nfsargsp->flags |= NFSMNT_SECSYSOK; } if (altflags & ALTF_TIMEO) { num = getmntoptnum(mop, "timeo"); if (num <= 0) { warnx("illegal timeout value -- %s", getmntoptstr(mop, "timeo")); } else { nfsargsp->timeo = num; nfsargsp->flags |= NFSMNT_TIMEO; } } if (altflags & ALTF_VERS) { if ((p2 = getmntoptstr(mop, "vers"))) num = getmntoptnum(mop, "vers"); else if ((p2 = getmntoptstr(mop, "nfsvers"))) num = getmntoptnum(mop, "nfsvers"); if (p2) { if (!strcmp(p2, SIMON_SAYS_USE_NFSV4)) num = 4; switch (num) { case 2: force2 = 1; nfsargsp->flags &= ~NFSMNT_NFSV3; nfsargsp->flags &= ~NFSMNT_NFSV4; break; case 3: force3 = 1; nfsargsp->flags |= NFSMNT_NFSV3; nfsargsp->flags &= ~NFSMNT_NFSV4; break; case 4: if (strcmp(p2, SIMON_SAYS_USE_NFSV4)) errx(EPERM, "sorry, you must specify vers=%s to use the alpha-quality NFSv4 support", SIMON_SAYS_USE_NFSV4); force4 = 1; nfsargsp->flags &= ~NFSMNT_NFSV3; nfsargsp->flags |= NFSMNT_NFSV4; break; default: warnx("illegal NFS version value -- %s", p2); break; } } } if (altflags & ALTF_VERS2) { warnx("option nfsv2 deprecated, use vers=#"); force2 = 1; nfsargsp->flags &= ~NFSMNT_NFSV3; nfsargsp->flags &= ~NFSMNT_NFSV4; checkNFSVersionOptions(); } if (altflags & ALTF_VERS3) { warnx("option nfsv3 deprecated, use vers=#"); force3 = 1; nfsargsp->flags |= NFSMNT_NFSV3; nfsargsp->flags &= ~NFSMNT_NFSV4; checkNFSVersionOptions(); } if (altflags & ALTF_VERS4) { warnx("option nfsv4 deprecated, use vers=#"); errx(EPERM, "sorry, you must specify vers=%s to use the alpha-quality NFSv4 support", SIMON_SAYS_USE_NFSV4); force4 = 1; nfsargsp->flags &= ~NFSMNT_NFSV3; nfsargsp->flags |= NFSMNT_NFSV4; checkNFSVersionOptions(); } if (altflags & ALTF_WSIZE) { p2 = getmntoptstr(mop, "rwsize"); if (!p2) p2 = getmntoptstr(mop, "wsize"); if (p2) { num = strtol(p2, &p, 10); if ((*p == 'k') || (*p == 'K')) { num *= 1024; p++; } if ((num <= 0) || *p) { warnx("illegal wsize value -- %s", p2); } else { nfsargsp->wsize = num; nfsargsp->flags |= NFSMNT_WSIZE; } } } freemntopts(mop); /* next do positive form of switch options */ altflags = 0; mop = getmntopts(opts, mopts_switches, &dummyflags, &altflags); if (mop == NULL) errx(EINVAL, "getmntops failed: %s", opts); if (verbose > 2) printf("altflags=0x%x\n", altflags); if (altflags & ALTF_ATTRCACHE) { nfsargsp->acregmin = nfsdefargs.acregmin; nfsargsp->acregmax = nfsdefargs.acregmax; nfsargsp->acdirmin = nfsdefargs.acdirmin; nfsargsp->acdirmax = nfsdefargs.acdirmax; nfsargsp->flags &= ~NFSMNT_ACREGMIN; nfsargsp->flags &= ~NFSMNT_ACREGMAX; nfsargsp->flags &= ~NFSMNT_ACDIRMIN; nfsargsp->flags &= ~NFSMNT_ACDIRMAX; } if (altflags & ALTF_BG) opflags |= BGRND; if (altflags & ALTF_CONN) nfsargsp->flags &= ~NFSMNT_NOCONN; if (altflags & ALTF_DUMBTIMR) nfsargsp->flags |= NFSMNT_DUMBTIMR; if (altflags & ALTF_HARD) nfsargsp->flags &= ~NFSMNT_SOFT; if (altflags & ALTF_INTR) nfsargsp->flags |= NFSMNT_INT; if (altflags & ALTF_LOCALLOCKS) { nfsargsp->flags |= NFSMNT_LOCALLOCKS; nfsargsp->flags &= ~NFSMNT_NOLOCKS; } if (altflags & ALTF_LOCKS) nfsargsp->flags &= ~NFSMNT_NOLOCKS; if (altflags & ALTF_MNTUDP) mnttcp_ok = 0; if (altflags & ALTF_MUTEJUKEBOX) nfsargsp->flags |= NFSMNT_MUTEJUKEBOX; if (altflags & ALTF_NEGNAMECACHE) nfsargsp->flags &= ~NFSMNT_NONEGNAMECACHE; if (altflags & ALTF_RDIRPLUS) nfsargsp->flags |= NFSMNT_RDIRPLUS; if (altflags & ALTF_RESVPORT) nfsargsp->flags |= NFSMNT_RESVPORT; if (altflags & ALTF_SOFT) nfsargsp->flags |= NFSMNT_SOFT; if (altflags & ALTF_TCP) { nfsargsp->sotype = SOCK_STREAM; nfsproto = IPPROTO_TCP; forceproto++; } if (altflags & ALTF_UDP) { nfsargsp->sotype = SOCK_DGRAM; nfsproto = IPPROTO_UDP; forceproto++; } freemntopts(mop); /* finally do negative form of switch options */ altflags = 0; mop = getmntopts(opts, mopts_switches_no, &dummyflags, &altflags); if (mop == NULL) errx(EINVAL, "getmntops failed: %s", opts); if (verbose > 2) printf("altflags=0x%x\n", altflags); if (altflags & ALTF_ATTRCACHE) { nfsargsp->acregmin = 0; nfsargsp->acregmax = 0; nfsargsp->acdirmin = 0; nfsargsp->acdirmax = 0; nfsargsp->flags |= NFSMNT_ACREGMIN; nfsargsp->flags |= NFSMNT_ACREGMAX; nfsargsp->flags |= NFSMNT_ACDIRMIN; nfsargsp->flags |= NFSMNT_ACDIRMAX; } if (altflags & ALTF_BG) opflags &= ~BGRND; if (altflags & ALTF_CONN) nfsargsp->flags |= NFSMNT_NOCONN; if (altflags & ALTF_DUMBTIMR) nfsargsp->flags &= ~NFSMNT_DUMBTIMR; if (altflags & ALTF_HARD) nfsargsp->flags |= NFSMNT_SOFT; if (altflags & ALTF_INTR) nfsargsp->flags &= ~NFSMNT_INT; if (altflags & ALTF_LOCALLOCKS) { nfsargsp->flags &= ~NFSMNT_LOCALLOCKS; nolocallocks = 1; } if (altflags & ALTF_LOCKS) { nfsargsp->flags &= ~NFSMNT_LOCALLOCKS; nfsargsp->flags |= NFSMNT_NOLOCKS; } if (altflags & ALTF_MNTUDP) mnttcp_ok = 1; if (altflags & ALTF_MUTEJUKEBOX) nfsargsp->flags &= ~NFSMNT_MUTEJUKEBOX; if (altflags & ALTF_NEGNAMECACHE) nfsargsp->flags |= NFSMNT_NONEGNAMECACHE; if (altflags & ALTF_RDIRPLUS) nfsargsp->flags &= ~NFSMNT_RDIRPLUS; if (altflags & ALTF_RESVPORT) nfsargsp->flags &= ~NFSMNT_RESVPORT; if (altflags & ALTF_SOFT) nfsargsp->flags &= ~NFSMNT_SOFT; if (altflags & ALTF_TCP) { /* notcp = udp */ nfsargsp->sotype = SOCK_DGRAM; nfsproto = IPPROTO_UDP; forceproto++; } if (altflags & ALTF_UDP) { /* noudp = tcp */ nfsargsp->sotype = SOCK_STREAM; nfsproto = IPPROTO_TCP; forceproto++; } freemntopts(mop); warn_badoptions(opts); } /* * Compare the supplied option string with the lists * of known mount options and issue a warning for any * unknown options. */ void warn_badoptions(char *opts) { char *p, *opt, *saveopt, *myopts; struct mntopt *k, *known; myopts = strdup(opts); if (myopts == NULL) return; for (opt = myopts; (opt = strtok(opt, ",")) != NULL; opt = NULL) { saveopt = opt; if (strncmp(opt, "no", 2) == 0) // negative option opt += 2; p = strchr(opt, '='); // value option if (p) *p = '\0'; known = &mopts[0]; for (k = known; k->m_option != NULL; ++k) if (strcasecmp(opt, k->m_option) == 0) break; if (k->m_option != NULL) // known option continue; known = &mopts_switches[0]; for (k = known; k->m_option != NULL; ++k) if (strcasecmp(opt, k->m_option) == 0) break; if (k->m_option != NULL) // known option continue; warnx("warning: option \"%s\" not known", saveopt); } free(myopts); } /* * read the NFS client values from nfs.conf */ int config_read(struct nfs_conf_client *conf) { FILE *f; size_t len, linenum = 0; char *line, *p, *key, *value; long val; if (!(f = fopen(_PATH_NFS_CONF, "r"))) { if (errno != ENOENT) warn("%s", _PATH_NFS_CONF); return (1); } for (; (line = fparseln(f, &len, &linenum, NULL, 0)); free(line)) { if (len <= 0) continue; /* trim trailing whitespace */ p = line + len - 1; while ((p > line) && isspace(*p)) *p-- = '\0'; /* find key start */ key = line; while (isspace(*key)) key++; /* find equals/value */ value = p = strchr(line, '='); if (p) /* trim trailing whitespace on key */ do { *p-- = '\0'; } while ((p > line) && isspace(*p)); /* find value start */ if (value) do { value++; } while (isspace(*value)); /* all client keys start with "nfs.client." */ if (strncmp(key, "nfs.client.", 11)) { if (verbose) printf("%4ld %s=%s\n", linenum, key, value ? value : ""); continue; } val = !value ? 1 : strtol(value, NULL, 0); if (verbose) printf("%4ld %s=%s (%ld)\n", linenum, key, value ? value : "", val); if (!strcmp(key, "nfs.client.access_cache_timeout")) { conf->access_cache_timeout = val; } else if (!strcmp(key, "nfs.client.allow_async")) { conf->allow_async = val; } else if (!strcmp(key, "nfs.client.initialdowndelay")) { conf->initialdowndelay = val; } else if (!strcmp(key, "nfs.client.iosize")) { conf->iosize = val; } else if (!strcmp(key, "nfs.client.mount.options")) { handle_mntopts(value); } else if (!strcmp(key, "nfs.client.nextdowndelay")) { conf->nextdowndelay = val; } else if (!strcmp(key, "nfs.client.nfsiod_thread_max")) { conf->nfsiod_thread_max = val; } else if (!strcmp(key, "nfs.client.statfs_rate_limit")) { conf->statfs_rate_limit = val; } else { if (verbose) printf("ignoring unknown config value: %4ld %s=%s\n", linenum, key, value ? value : ""); } } fclose(f); return (0); } /* set a sysctl config value */ int sysctl_set(const char *name, int val) { return (sysctlbyname(name, NULL, 0, &val, sizeof(val))); } void config_sysctl(void) { seteuid(eff_uid); /* must be root to do sysctls */ if (config.access_cache_timeout != -1) sysctl_set("vfs.generic.nfs.client.access_cache_timeout", config.access_cache_timeout); if (config.allow_async != -1) sysctl_set("vfs.generic.nfs.client.allow_async", config.allow_async); if (config.initialdowndelay != -1) sysctl_set("vfs.generic.nfs.client.initialdowndelay", config.initialdowndelay); if (config.iosize != -1) sysctl_set("vfs.generic.nfs.client.iosize", config.iosize); if (config.nextdowndelay != -1) sysctl_set("vfs.generic.nfs.client.nextdowndelay", config.nextdowndelay); if (config.nfsiod_thread_max != -1) sysctl_set("vfs.generic.nfs.client.nfsiod_thread_max", config.nfsiod_thread_max); if (config.statfs_rate_limit != -1) sysctl_set("vfs.generic.nfs.client.statfs_rate_limit", config.statfs_rate_limit); seteuid(real_uid); } /* * A substitute for the pmap_getport function in the RPC library. * It makes an RPC call to the portmapper at addr to retrieve the * port number for the service corresponding to prog, vers and prot. * This function has an additional timeout arg that allows the caller * to specify a timeout if the portmapper doesn't respond. */ int pmap_getport_timely(struct sockaddr_in *addr, int prog, int vers, int prot, int timeout) { CLIENT *clnt; struct pmap args = { prog, vers, prot, 0 }; u_short port = 0; struct timeval tv; int sock = RPC_ANYSOCK; tv.tv_sec = timeout; tv.tv_usec = 0; addr->sin_port = htons(PMAPPORT); clnt = clntudp_bufcreate(addr, PMAPPROG, PMAPVERS, tv, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); if (clnt != NULL) { if (clnt_call(clnt, PMAPPROC_GETPORT, (xdrproc_t) xdr_pmap, &args, (xdrproc_t) xdr_u_short, &port, tv) != RPC_SUCCESS) { rpc_createerr.cf_stat = RPC_PMAPFAILURE; clnt_geterr(clnt, &rpc_createerr.cf_error); } else if (port == 0) { rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; } clnt_destroy(clnt); } (void) close(sock); return (port); } int getnfsargs(char *spec, struct nfs_args *nfsargsp) { CLIENT *clp; struct hostent *hp; static struct sockaddr_in saddr; struct timeval pertry, try; enum clnt_stat clnt_stat; int so = RPC_ANYSOCK, nfsvers, mntvers, retry; char *hostp, *delimp; u_short tport = 0; static struct nfhret nfhret; static char nam[MAXPATHLEN]; strlcpy(nam, spec, MAXPATHLEN); if ((delimp = strchr(spec, '@')) != NULL) { hostp = delimp + 1; } else if ((delimp = strchr(spec, ':')) != NULL) { hostp = spec; spec = delimp + 1; } else { warnx("no : or @ spec"); return (EINVAL); } *delimp = '\0'; /* * Handle an internet host address */ if ((hp = gethostbyname(hostp)) != NULL) { memmove(&saddr.sin_addr, hp->h_addr, hp->h_length); } else if (isdigit(*hostp) && ((saddr.sin_addr.s_addr = inet_addr(hostp)) != INADDR_NONE)) { ; /* it was an address */ } else { warnx("can't get net id for host"); return (ENOENT); } if (force4) { /* we don't need most of this version/port searching for force NFSv4 */ nfsvers = NFS_VER4; saddr.sin_family = AF_INET; tport = 2049; nfhret.fhsize = 0; nfhret.auth = nfsargsp->auth ? nfsargsp->auth : RPCAUTH_SYS; goto got_everything; } else if (force2) { nfsvers = NFS_VER2; mntvers = RPCMNT_VER1; } else { nfsvers = NFS_VER3; mntvers = RPCMNT_VER3; } if (retrycnt < 0) retrycnt = (opflags & BGRND) ? DEF_RETRY_BG : DEF_RETRY_FG; retry = retrycnt; tryagain: nfhret.stat = EACCES; /* Mark not yet successful */ for (;;) { saddr.sin_family = AF_INET; if (forceport) { /* * We're given which port to use, but need to * establish the protocol: TCP or UDP. * If TCP is chosen by default or forced * then attempt a TCP connection to the NFS * server to check whether TCP is available. * Fall back to UDP if necessary. */ saddr.sin_port = htons(forceport); if (nfsproto == IPPROTO_TCP) { seteuid(eff_uid); // for reserved port clp = clnttcp_create(&saddr, RPCPROG_NFS, nfsvers, &so, 0, 0); seteuid(real_uid); if (clp) { /* TCP is OK */ clnt_destroy(clp); (void) close(so); tport = forceport; } else if (!forceproto) { /* Fall back to UDP */ nfsproto = IPPROTO_UDP; nfsargsp->sotype = SOCK_DGRAM; tport = forceport; } } else tport = forceport; } else { /* * Query the portmapper to get the NFS server's * port and proto - TCP or UDP. */ saddr.sin_port = htons(PMAPPORT); tport = pmap_getport_timely(&saddr, RPCPROG_NFS, nfsvers, nfsproto, pmap_timeout); if (tport == 0 && rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED) { /* * If the portmapper reply indicates that * the default transport (TCP) isn't supported * and the user hasn't explicitly selected a * protocol then try UDP. */ if (!forceproto) { nfsproto = IPPROTO_UDP; nfsargsp->sotype = SOCK_DGRAM; tport = pmap_getport_timely(&saddr, RPCPROG_NFS, nfsvers, nfsproto, pmap_timeout); } if (tport == 0 && (opflags & ISBGRND) == 0) { char s[] = "NFS Portmap"; clnt_pcreateerror(s); } } } if (tport > 0) { saddr.sin_port = htons(mountport); pertry.tv_sec = 10; pertry.tv_usec = 0; so = RPC_ANYSOCK; /* * temporarily revert to root, to avoid reserved port * number restriction (port# less than 1024) */ seteuid(eff_uid); if (mnttcp_ok && nfsargsp->sotype == SOCK_STREAM) clp = clnttcp_create(&saddr, RPCPROG_MNT, mntvers, &so, 0, 0); else clp = clntudp_create(&saddr, RPCPROG_MNT, mntvers, pertry, &so); seteuid(real_uid); if (clp == NULL) { if ((opflags & ISBGRND) == 0) { char s[] = "Cannot MNT RPC"; clnt_pcreateerror(s); } } else { clp->cl_auth = authunix_create_default(); try.tv_sec = 10; try.tv_usec = 0; nfhret.auth = nfsargsp->auth; nfhret.vers = mntvers; clnt_stat = clnt_call(clp, RPCMNT_MOUNT, (xdrproc_t) xdr_dir, spec, (xdrproc_t) xdr_fh, &nfhret, try); switch (clnt_stat) { case RPC_PROGVERSMISMATCH: if (nfsvers == NFS_VER3 && !force3) { retry = retrycnt; nfsvers = NFS_VER2; mntvers = RPCMNT_VER1; nfsargsp->flags &= ~NFSMNT_NFSV3; goto tryagain; } else { char s[] = "MNT RPC"; errx(EPROGMISMATCH, "%s", clnt_sperror(clp, s)); } case RPC_SUCCESS: nfsargsp->flags |= NFSMNT_CALLUMNT; auth_destroy(clp->cl_auth); clnt_destroy(clp); retry = 0; break; default: /* XXX should give up on some errors */ if ((opflags & ISBGRND) == 0) { char s[] = "bad MNT RPC"; warnx("%s", clnt_sperror(clp, s)); } break; } } } if (retry <= 0) break; --retry; if (opflags & BGRND) { opflags &= ~BGRND; if (daemon(0, 0)) err(errno, "nfs bgrnd: fork failed"); opflags |= ISBGRND; } sleep(60); } if (nfhret.stat) { if (opflags & ISBGRND) exit(nfhret.stat); warnx("can't access %s: %s", spec, strerror(nfhret.stat)); return (nfhret.stat); } got_everything: saddr.sin_port = forceport ? htons(forceport) : htons(tport); nfsargsp->addr = (struct sockaddr *) &saddr; nfsargsp->addrlen = sizeof (saddr); nfsargsp->fh = nfhret.nfh; nfsargsp->fhsize = nfhret.fhsize; nfsargsp->hostname = nam; nfsargsp->flags |= NFSMNT_SECFLAVOR; nfsargsp->auth = nfhret.auth; if (nfhret.sysok || nfsargsp->auth == RPCAUTH_SYS) nfsargsp->flags |= NFSMNT_SECSYSOK; return (0); } /* * xdr routines for mount rpc's */ int xdr_dir(XDR *xdrsp, char *dirp) { return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); } int xdr_fh(XDR *xdrsp, struct nfhret *np) { int i, sec_opt; xdr_long_t auth, authcnt, authfnd = 0; sec_opt = (np->auth) ? 1 : 0; np->sysok = FALSE; if (!xdr_u_long(xdrsp, &np->stat)) return (0); if (np->stat) return (1); switch (np->vers) { case 1: if (!sec_opt) { /* No "sec=" option was given, so default to sys */ np->auth = RPCAUTH_SYS; } np->fhsize = NFSX_V2FH; return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH)); case 3: if (!xdr_long(xdrsp, &np->fhsize)) return (0); if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX) return (0); if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize)) return (0); if (!xdr_long(xdrsp, &authcnt)) return (0); for (i = 0; i < authcnt; i++) { if (!xdr_long(xdrsp, &auth)) return (0); if (sec_opt) { /* A "sec=" option was given, check if server supports it */ if (auth == np->auth) { authfnd = 1; break; } continue; } switch (auth) { case RPCAUTH_SYS: if (!authfnd) np->auth = RPCAUTH_SYS; np->sysok = TRUE; authfnd = 1; break; case RPCAUTH_KRB5: if (!authfnd) np->auth = RPCAUTH_KRB5; authfnd = 1; break; case RPCAUTH_KRB5I: if (!authfnd) np->auth = RPCAUTH_KRB5I; authfnd = 1; break; case RPCAUTH_KRB5P: if (!authfnd) np->auth = RPCAUTH_KRB5P; authfnd = 1; break; } } if (!authfnd) { if (!(authcnt) && (!(sec_opt) || (np->auth == RPCAUTH_SYS))) { /* * Some servers, such as DEC's OSF/1 return a nil authenticator * list to indicate RPCAUTH_SYS. */ np->auth = RPCAUTH_SYS; } else np->stat = EAUTH; } return (1); }; return (0); } __dead void usage(void) { fprintf(stderr, "usage: mount_nfs [-o options] server:/path directory\n"); exit(EINVAL); } /* Map from mount otions to printable formats. */ static struct opt { int o_opt; const char *o_name; } optnames[] = { { MNT_ASYNC, "asynchronous" }, { MNT_EXPORTED, "NFS exported" }, { MNT_LOCAL, "local" }, { MNT_NODEV, "nodev" }, { MNT_NOEXEC, "noexec" }, { MNT_NOSUID, "nosuid" }, { MNT_QUOTA, "with quotas" }, { MNT_RDONLY, "read-only" }, { MNT_SYNCHRONOUS, "synchronous" }, { MNT_UNION, "union" }, { MNT_AUTOMOUNTED, "automounted" }, { MNT_JOURNALED, "journaled" }, { MNT_DEFWRITE, "defwrite" }, { MNT_IGNORE_OWNERSHIP, "noowners" }, { MNT_NOATIME, "noatime" }, { MNT_QUARANTINE, "quarantine" }, { 0, NULL } }; void dump_mount_options(char *name) { struct opt *o; int flags, i; printf("mount %s on %s\n", nfsargsp->hostname, name); flags = mntflags; printf("mount flags: 0x%x", flags); for (o = optnames; flags && o->o_opt; o++) if (flags & o->o_opt) { printf(", %s", o->o_name); flags &= ~o->o_opt; } printf("\n"); printf("%s %s", inet_ntoa(((struct sockaddr_in *)nfsargsp->addr)->sin_addr), ((nfsproto == IPPROTO_TCP) ? "tcp" : (nfsproto == IPPROTO_UDP) ? "udp" : "???")); if (forceport) printf(",port=%d", forceport); if (mountport) printf(",mountport=%d", mountport); printf(", fh %d ", nfsargsp->fhsize); for (i=0; i < nfsargsp->fhsize; i++) printf("%02x", nfsargsp->fh[i] & 0xff); printf("\n"); flags = nfsargsp->flags; printf("NFS options: 0x%x", flags); printf(" %s,retrycnt=%d,vers=%d", ((opflags & BGRND) ? "bg" : "fg"), retrycnt, (flags & NFSMNT_NFSV4) ? 4 : (flags & NFSMNT_NFSV3) ? 3 : 2); if ((flags & NFSMNT_SOFT) || (verbose > 1)) printf(",%s", (flags & NFSMNT_SOFT) ? "soft" : "hard"); if ((flags & NFSMNT_INT) || (verbose > 1)) printf(",%sintr", (flags & NFSMNT_INT) ? "" : "no"); if ((flags & NFSMNT_RESVPORT) || (verbose > 1)) printf(",%sresvport", (flags & NFSMNT_RESVPORT) ? "" : "no"); if ((flags & NFSMNT_NOCONN) || (verbose > 1)) printf(",%sconn", (flags & NFSMNT_NOCONN) ? "no" : ""); if ((flags & NFSMNT_NOLOCKS) || (verbose > 1)) printf(",%slocks", (flags & NFSMNT_NOLOCKS) ? "no" : ""); if ((flags & NFSMNT_LOCALLOCKS) || (verbose > 1)) printf(",%slocallocks", (flags & NFSMNT_LOCALLOCKS) ? "" : "no"); if ((flags & NFSMNT_NONEGNAMECACHE) || (verbose > 1)) printf(",%snegnamecache", (flags & NFSMNT_NONEGNAMECACHE) ? "no" : ""); if ((flags & NFSMNT_RSIZE) || (verbose > 1)) printf(",rsize=%d", nfsargsp->rsize); if ((flags & NFSMNT_WSIZE) || (verbose > 1)) printf(",wsize=%d", nfsargsp->wsize); if ((flags & NFSMNT_READDIRSIZE) || (verbose > 1)) printf(",dsize=%d", nfsargsp->readdirsize); if ((flags & NFSMNT_READAHEAD) || (verbose > 1)) printf(",readahead=%d", nfsargsp->readahead); if ((flags & NFSMNT_RDIRPLUS) || (verbose > 1)) printf(",%srdirplus", (flags & NFSMNT_RDIRPLUS) ? "" : "no"); if ((flags & NFSMNT_DUMBTIMR) || (verbose > 1)) printf(",%sdumbtimer", (flags & NFSMNT_DUMBTIMR) ? "" : "no"); if ((flags & NFSMNT_TIMEO) || (verbose > 1)) printf(",timeout=%d", nfsargsp->timeo); if ((flags & NFSMNT_RETRANS) || (verbose > 1)) printf(",retrans=%d", nfsargsp->retrans); if ((flags & NFSMNT_MAXGRPS) || (verbose > 1)) printf(",maxgroups=%d", nfsargsp->maxgrouplist); if ((flags & NFSMNT_ACREGMIN) || (verbose > 1)) printf(",acregmin=%d", nfsargsp->acregmin); if ((flags & NFSMNT_ACREGMAX) || (verbose > 1)) printf(",acregmax=%d", nfsargsp->acregmax); if ((flags & NFSMNT_ACDIRMIN) || (verbose > 1)) printf(",acdirmin=%d", nfsargsp->acdirmin); if ((flags & NFSMNT_ACDIRMAX) || (verbose > 1)) printf(",acdirmax=%d", nfsargsp->acdirmax); printf(",sec=%s (%d)\n", (nfsargsp->auth == RPCAUTH_KRB5P) ? "krb5p" : (nfsargsp->auth == RPCAUTH_KRB5I) ? "krb5i" : (nfsargsp->auth == RPCAUTH_KRB5) ? "krb5" : (nfsargsp->auth == RPCAUTH_SYS) ? "sys" : (nfsargsp->auth == RPCAUTH_NULL) ? "null" : "???", nfsargsp->auth); if ((flags & NFSMNT_DEADTIMEOUT) || (verbose > 1)) printf(",deadtimeout=%d", nfsargsp->deadtimeout); }