#include <sys/param.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/stat.h>
#include <sys/syslog.h>
#include <sys/sysctl.h>
#include <rpc/rpc.h>
#include <rpc/pmap_clnt.h>
#include <rpc/pmap_prot.h>
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfs/nfs.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <libutil.h>
#include <mntopts.h>
#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;
};
struct nfs_conf_client config =
{
-1,
-1,
-1,
-1,
-1,
-1,
-1,
};
#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
#define ALTF_VERS3 0x00004000
#define ALTF_VERS4 0x00008000
#define ALTF_WSIZE 0x00010000
#define ALTF_DEADTIMEOUT 0x00020000
#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
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 },
{ "nfsv3", 0, ALTF_VERS3, 1 },
{ "nfsv4", 0, ALTF_VERS4, 1 },
{ "nfsvers", 0, ALTF_VERS, 1 },
{ NULL }
};
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 }
};
struct mntopt *mopts_switches_no = NULL;
struct nfs_args nfsdefargs = {
NFS_ARGSVERSION,
NULL,
sizeof (struct sockaddr_in),
SOCK_STREAM,
0,
NULL,
0,
NFSMNT_NFSV3,
NFS_WSIZE,
NFS_RSIZE,
NFS_READDIRSIZE,
10,
NFS_RETRANS,
NFS_MAXGRPS,
NFS_DEFRAHEAD,
0,
0,
NULL,
NFS_MINATTRTIMO,
NFS_MAXATTRTIMO,
NFS_MINDIRATTRTIMO,
NFS_MAXDIRATTRTIMO,
0,
0
};
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;
#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;
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];
};
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;
eff_uid = geteuid();
real_uid = getuid();
seteuid(real_uid);
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);
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;
if ((nfsargsp->flags & NFSMNT_SOFT) && (mntflags & MNT_RDONLY)) {
if (!(nfsargsp->flags & NFSMNT_NOLOCKS) && !nolocallocks)
nfsargsp->flags |= NFSMNT_LOCALLOCKS;
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;
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);
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);
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) {
nfsargsp->sotype = SOCK_DGRAM;
nfsproto = IPPROTO_UDP;
forceproto++;
}
if (altflags & ALTF_UDP) {
nfsargsp->sotype = SOCK_STREAM;
nfsproto = IPPROTO_TCP;
forceproto++;
}
freemntopts(mop);
warn_badoptions(opts);
}
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) opt += 2;
p = strchr(opt, '='); 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) 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) continue;
warnx("warning: option \"%s\" not known", saveopt);
}
free(myopts);
}
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;
p = line + len - 1;
while ((p > line) && isspace(*p))
*p-- = '\0';
key = line;
while (isspace(*key))
key++;
value = p = strchr(line, '=');
if (p)
do { *p-- = '\0'; } while ((p > line) && isspace(*p));
if (value)
do { value++; } while (isspace(*value));
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);
}
int
sysctl_set(const char *name, int val)
{
return (sysctlbyname(name, NULL, 0, &val, sizeof(val)));
}
void
config_sysctl(void)
{
seteuid(eff_uid);
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);
}
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 <host>:<dirpath> or <dirpath>@<host> spec");
return (EINVAL);
}
*delimp = '\0';
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)) {
;
} else {
warnx("can't get net id for host");
return (ENOENT);
}
if (force4) {
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;
for (;;) {
saddr.sin_family = AF_INET;
if (forceport) {
saddr.sin_port = htons(forceport);
if (nfsproto == IPPROTO_TCP) {
seteuid(eff_uid); clp = clnttcp_create(&saddr, RPCPROG_NFS,
nfsvers, &so, 0, 0);
seteuid(real_uid);
if (clp) {
clnt_destroy(clp);
(void) close(so);
tport = forceport;
} else if (!forceproto) {
nfsproto = IPPROTO_UDP;
nfsargsp->sotype = SOCK_DGRAM;
tport = forceport;
}
} else
tport = forceport;
} else {
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 (!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;
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:
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);
}
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) {
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) {
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))) {
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);
}
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);
}