#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 <netinet/in.h>
#include <net/if.h>
#include <xpc/xpc.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>
#include <util.h>
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfs/nfs.h>
#define _NFS_XDR_SUBS_FUNCS_
#include <nfs/xdr_subs.h>
#define _PATH_NFS_CONF "/etc/nfs.conf"
struct nfs_conf_client {
int access_cache_timeout;
int access_for_getattr;
int allow_async;
int callback_port;
int initialdowndelay;
int iosize;
int nextdowndelay;
int nfsiod_thread_max;
int statfs_rate_limit;
int is_mobile;
int squishy_flags;
};
struct nfs_conf_client config =
{
-1,
-1,
-1,
-1,
-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
#define ALTF_CALLBACK 0x00010000
#define ALTF_NAMEDATTR 0x00020000
#define ALTF_ACL 0x00040000
#define ALTF_ACLONLY 0x00080000
#define ALTF_NFC 0x00100000
#define ALTF_INET 0x00200000
#define ALTF_INET6 0x00400000
#define ALTF_QUOTA 0x00800000
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 },
{ "deadtimeout", 0, ALTF_DEADTIMEOUT, 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 },
{ "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 },
{ "acl", 0, ALTF_ACL, 1 },
{ "aclonly", 0, ALTF_ACLONLY, 1 },
{ "bg", 0, ALTF_BG, 1 },
{ "callback", 0, ALTF_CALLBACK, 1 },
{ "conn", 0, ALTF_CONN, 1 },
{ "dumbtimer", 0, ALTF_DUMBTIMR, 1 },
{ "hard", 0, ALTF_HARD, 1 },
{ "inet", 0, ALTF_INET, 1 },
{ "inet6", 0, ALTF_INET6, 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 },
{ "namedattr", 0, ALTF_NAMEDATTR, 1 },
{ "negnamecache", 0, ALTF_NEGNAMECACHE, 1 },
{ "nfc", 0, ALTF_NFC, 1 },
{ "nlm", 0, ALTF_LOCKS, 1 },
{ "quota", 0, ALTF_QUOTA, 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;
char *mntfromarg = NULL;
int nfsproto = 0;
#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;
struct {
int mntflags;
uint32_t mattrs[NFS_MATTR_BITMAP_LEN];
uint32_t mflags_mask[NFS_MFLAG_BITMAP_LEN];
uint32_t mflags[NFS_MFLAG_BITMAP_LEN];
uint32_t nfs_version, nfs_minor_version;
uint32_t rsize, wsize, readdirsize, readahead;
struct timespec acregmin, acregmax, acdirmin, acdirmax;
uint32_t lockmode;
struct nfs_sec sec;
uint32_t maxgrouplist;
int socket_type, socket_family;
uint32_t nfs_port, mount_port;
struct timespec request_timeout;
uint32_t soft_retry_count;
struct timespec dead_timeout;
fhandle_t fh;
} options;
#define SETFLAG(F, V) \
do { \
NFS_BITMAP_SET(options.mflags_mask, F); \
if (V) \
NFS_BITMAP_SET(options.mflags, F); \
else \
NFS_BITMAP_CLR(options.mflags, F); \
} while (0)
struct nfs_fs_server {
struct nfs_fs_server * ns_next;
char * ns_name;
struct addrinfo * ns_ailist;
};
struct nfs_fs_location {
struct nfs_fs_location *nl_next;
struct nfs_fs_server * nl_servers;
char * nl_path;
uint32_t nl_servcnt;
};
void setNFSVersion(uint32_t);
void dump_mount_options(struct nfs_fs_location *, char *);
void handle_mntopts(char *);
void warn_badoptions(char *);
int config_read(struct nfs_conf_client *);
void config_sysctl(void);
int parse_fs_locations(const char *, struct nfs_fs_location **);
int getaddresslist(struct nfs_fs_server *);
void getaddresslists(struct nfs_fs_location *, int *);
void freeaddresslists(struct nfs_fs_location *);
int assemble_mount_args(struct nfs_fs_location *, char **);
void usage(void);
int
main(int argc, char *argv[])
{
uint i;
int c, num, error, try, delay, servcnt;
char mntonname[MAXPATHLEN];
char *p, *xdrbuf = NULL;
struct nfs_fs_location *nfsl = NULL;
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);
bzero(&options, sizeof(options));
config_read(&config);
while ((c = getopt(argc, argv, "234a:bcdF:g:I:iLlo:PR:r:sTt:Uvw:x:")) != EOF)
switch (c) {
case '4':
setNFSVersion(4);
break;
case '3':
setNFSVersion(3);
break;
case '2':
setNFSVersion(2);
break;
case 'a':
num = strtol(optarg, &p, 10);
if (*p || num < 0) {
warnx("illegal -a value -- %s", optarg);
} else {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_READAHEAD);
options.readahead = num;
}
break;
case 'b':
opflags |= BGRND;
break;
case 'c':
SETFLAG(NFS_MFLAG_NOCONNECT, 1);
break;
case 'd':
SETFLAG(NFS_MFLAG_DUMBTIMER, 1);
break;
case 'F': { int fd, len;
if ((fd = open(optarg, O_RDONLY)) < 0)
err(errno, "could not open file containing file system specification: %s", optarg);
if ((mntfromarg = malloc(MAXPATHLEN)) == NULL)
errx(ENOMEM, "memory allocation failed");
if ((len = read(fd, mntfromarg, MAXPATHLEN-1)) < 0)
err(errno, "could not read file containing file system specification: %s", optarg);
mntfromarg[len--] = '\0';
while (isspace(mntfromarg[len]))
mntfromarg[len--] = '\0';
close(fd);
}
break;
case 'g':
num = strtol(optarg, &p, 10);
if (*p || num <= 0) {
warnx("illegal maxgroups value -- %s", optarg);
} else {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_MAX_GROUP_LIST);
options.maxgrouplist = num;
}
break;
case 'I':
num = strtol(optarg, &p, 10);
if (*p || num <= 0) {
warnx("illegal -I value -- %s", optarg);
} else {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_READDIR_SIZE);
options.readdirsize = num;
}
break;
case 'i':
SETFLAG(NFS_MFLAG_INTR, 1);
break;
case 'L':
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_LOCK_MODE);
options.lockmode = NFS_LOCK_MODE_DISABLED;
break;
case 'l':
SETFLAG(NFS_MFLAG_RDIRPLUS, 1);
break;
case 'o':
handle_mntopts(optarg);
break;
case 'P':
SETFLAG(NFS_MFLAG_RESVPORT, 1);
break;
case 'R':
num = strtol(optarg, &p, 10);
if (*p)
warnx("illegal -R value -- %s", optarg);
else
retrycnt = num;
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 {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_READ_SIZE);
options.rsize = num;
}
break;
case 's':
SETFLAG(NFS_MFLAG_SOFT, 1);
break;
case 'T':
options.socket_type = SOCK_STREAM;
break;
case 't':
num = strtol(optarg, &p, 10);
if (*p || num <= 0) {
warnx("illegal -t value -- %s", optarg);
} else {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_REQUEST_TIMEOUT);
options.request_timeout.tv_sec = num/10;
options.request_timeout.tv_nsec = (num%10) * 100000000;
}
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 {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_WRITE_SIZE);
options.wsize = num;
}
break;
case 'x':
num = strtol(optarg, &p, 10);
if (*p || num <= 0) {
warnx("illegal -x value -- %s", optarg);
} else {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_SOFT_RETRY_COUNT);
options.soft_retry_count = num;
}
break;
case 'U':
SETFLAG(NFS_MFLAG_MNTUDP, 1);
break;
case 'v':
verbose++;
break;
default:
usage();
break;
}
argc -= optind;
argv += optind;
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_SOFT) &&
NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_SOFT) &&
(options.mntflags & MNT_RDONLY)) {
if (!NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_LOCK_MODE)) {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_LOCK_MODE);
options.lockmode = NFS_LOCK_MODE_LOCAL;
}
if (!NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_DEAD_TIMEOUT)) {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_DEAD_TIMEOUT);
options.dead_timeout.tv_sec = 60;
options.dead_timeout.tv_nsec = 0;
}
}
if (options.socket_type || options.socket_family)
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_SOCKET_TYPE);
if ((argc == 1) && !strcmp(argv[0], "configupdate")) {
if (geteuid() != 0)
errx(1, "Must be superuser to configupdate");
config_sysctl();
exit(0);
}
if (!mntfromarg && (argc > 1)) {
mntfromarg = *argv++;
argc--;
}
if (argc != 1)
usage();
if (realpath(*argv, mntonname) == NULL) {
if (errno) {
err(errno, "realpath %s", mntonname);
} else {
errx(1, "realpath %s", mntonname);
}
}
error = parse_fs_locations(mntfromarg, &nfsl);
if (error || !nfsl) {
if (!error)
error = EINVAL;
warnx("could not parse file system specification");
exit(error);
}
if (retrycnt < 0)
retrycnt = (opflags & BGRND) ? DEF_RETRY_BG : DEF_RETRY_FG;
if (retrycnt <= 0)
SETFLAG(NFS_MFLAG_MNTQUICK, 1);
for (try = 1; try <= retrycnt+1; try++) {
if (try > 1) {
if (opflags & BGRND) {
printf("Retrying NFS mount in background...\n");
opflags &= ~BGRND;
if (daemon(0, 0))
errc(errno, errno, "mount nfs background: fork failed");
opflags |= ISBGRND;
}
delay = MIN(5*(try-1),30);
if (verbose)
printf("Retrying NFS mount in %d seconds...\n", delay);
sleep(delay);
}
getaddresslists(nfsl, &servcnt);
if (!servcnt) {
error = ENOENT;
warnx("no usable addresses for host: %s", nfsl->nl_servers->ns_name);
continue;
}
if (verbose)
dump_mount_options(nfsl, mntonname);
if ((error = assemble_mount_args(nfsl, &xdrbuf)))
errc(error, error, "error %d assembling mount args\n", error);
if (mount("nfs", mntonname, options.mntflags, xdrbuf)) {
error = errno;
switch (error) {
case ETIMEDOUT:
case EAGAIN:
case EPIPE:
case EADDRNOTAVAIL:
case ENETDOWN:
case ENETUNREACH:
case ENETRESET:
case ECONNABORTED:
case ECONNRESET:
case ENOTCONN:
case ESHUTDOWN:
case ECONNREFUSED:
case EHOSTDOWN:
case EHOSTUNREACH:
break;
default:
errc(error, error, "can't mount %s from %s onto %s", nfsl->nl_path, nfsl->nl_servers->ns_name, mntonname);
break;
}
freeaddresslists(nfsl);
if (xdrbuf) {
xb_free(xdrbuf);
xdrbuf = NULL;
}
continue;
}
error = 0;
break;
}
if (error)
errc(error, error, "can't mount %s from %s onto %s", nfsl->nl_path, nfsl->nl_servers->ns_name, mntonname);
exit(error);
}
static char *
get_socket_type_mount_arg(void)
{
const char *proto = "inet", *family = "";
static char type[8];
if (options.socket_family == AF_INET)
family = "4";
if (options.socket_family == AF_INET6)
family = "6";
if (options.socket_type) {
if (options.socket_type == SOCK_DGRAM)
proto = "udp";
if (options.socket_type == SOCK_STREAM)
proto = "tcp";
} else {
if (nfsproto == IPPROTO_UDP)
proto = "udp";
if (nfsproto == IPPROTO_TCP)
proto = "tcp";
}
sprintf(type, "%s%s", proto, family);
return (type);
}
int
assemble_mount_args(struct nfs_fs_location *nfslhead, char **xdrbufp)
{
struct xdrbuf xb;
int error, i, numlocs, numcomps, numaddrs;
char uaddr[128], *p, *cp;
uint32_t argslength_offset, attrslength_offset, end_offset;
uint32_t mattrs[NFS_MATTR_BITMAP_LEN];
struct addrinfo *ai;
void *sinaddr;
struct nfs_fs_location *nfsl;
struct nfs_fs_server *nfss;
*xdrbufp = NULL;
for (i=0; i < NFS_MFLAG_BITMAP_LEN; i++)
mattrs[i] = options.mattrs[i];
NFS_BITMAP_SET(mattrs, NFS_MATTR_FS_LOCATIONS);
NFS_BITMAP_SET(mattrs, NFS_MATTR_MNTFLAGS);
for (i=0; i < NFS_MFLAG_BITMAP_LEN; i++)
if (options.mflags_mask[i]) {
NFS_BITMAP_SET(mattrs, NFS_MATTR_FLAGS);
break;
}
error = 0;
xb_init_buffer(&xb, NULL, 0);
xb_add_32(error, &xb, NFS_ARGSVERSION_XDR);
argslength_offset = xb_offset(&xb);
xb_add_32(error, &xb, 0); xb_add_32(error, &xb, NFS_XDRARGS_VERSION_0);
xb_add_bitmap(error, &xb, mattrs, NFS_MATTR_BITMAP_LEN);
attrslength_offset = xb_offset(&xb);
xb_add_32(error, &xb, 0); if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_FLAGS)) {
xb_add_bitmap(error, &xb, options.mflags_mask, NFS_MFLAG_BITMAP_LEN);
xb_add_bitmap(error, &xb, options.mflags, NFS_MFLAG_BITMAP_LEN);
}
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_NFS_VERSION))
xb_add_32(error, &xb, options.nfs_version);
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_NFS_MINOR_VERSION))
xb_add_32(error, &xb, options.nfs_minor_version);
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_READ_SIZE))
xb_add_32(error, &xb, options.rsize);
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_WRITE_SIZE))
xb_add_32(error, &xb, options.wsize);
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_READDIR_SIZE))
xb_add_32(error, &xb, options.readdirsize);
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_READAHEAD))
xb_add_32(error, &xb, options.readahead);
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_ATTRCACHE_REG_MIN)) {
xb_add_32(error, &xb, options.acregmin.tv_sec);
xb_add_32(error, &xb, options.acregmin.tv_nsec);
}
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_ATTRCACHE_REG_MAX)) {
xb_add_32(error, &xb, options.acregmax.tv_sec);
xb_add_32(error, &xb, options.acregmax.tv_nsec);
}
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_ATTRCACHE_DIR_MIN)) {
xb_add_32(error, &xb, options.acdirmin.tv_sec);
xb_add_32(error, &xb, options.acdirmin.tv_nsec);
}
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_ATTRCACHE_DIR_MAX)) {
xb_add_32(error, &xb, options.acdirmax.tv_sec);
xb_add_32(error, &xb, options.acdirmax.tv_nsec);
}
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_LOCK_MODE))
xb_add_32(error, &xb, options.lockmode);
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SECURITY))
xb_add_word_array(error, &xb, options.sec.flavors, options.sec.count);
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_MAX_GROUP_LIST))
xb_add_32(error, &xb, options.maxgrouplist);
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SOCKET_TYPE)) {
char *type = get_socket_type_mount_arg();
xb_add_string(error, &xb, type, strlen(type));
}
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_NFS_PORT))
xb_add_32(error, &xb, options.nfs_port);
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_MOUNT_PORT))
xb_add_32(error, &xb, options.mount_port);
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_REQUEST_TIMEOUT)) {
xb_add_32(error, &xb, options.request_timeout.tv_sec);
xb_add_32(error, &xb, options.request_timeout.tv_nsec);
}
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SOFT_RETRY_COUNT))
xb_add_32(error, &xb, options.soft_retry_count);
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_DEAD_TIMEOUT)) {
xb_add_32(error, &xb, options.dead_timeout.tv_sec);
xb_add_32(error, &xb, options.dead_timeout.tv_nsec);
}
if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_FH))
xb_add_fh(error, &xb, &options.fh.fh_data[0], options.fh.fh_len);
for (numlocs=0, nfsl=nfslhead; nfsl; nfsl=nfsl->nl_next)
numlocs++;
xb_add_32(error, &xb, numlocs);
for (nfsl=nfslhead; nfsl; nfsl=nfsl->nl_next) {
xb_add_32(error, &xb, nfsl->nl_servcnt);
for (nfss=nfsl->nl_servers; nfss; nfss=nfss->ns_next) {
xb_add_string(error, &xb, nfss->ns_name, strlen(nfss->ns_name));
for (numaddrs = 0, ai = nfss->ns_ailist; ai; ai = ai->ai_next)
numaddrs++;
xb_add_32(error, &xb, numaddrs);
for (ai = nfss->ns_ailist; ai; ai = ai->ai_next) {
if (ai->ai_family == AF_INET)
sinaddr = &((struct sockaddr_in*)ai->ai_addr)->sin_addr;
else
sinaddr = &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr;
if (inet_ntop(ai->ai_family, sinaddr, uaddr, sizeof(uaddr)) != uaddr) {
warn("unable to convert server address to string");
error = errno;
}
xb_add_string(error, &xb, uaddr, strlen(uaddr));
}
xb_add_32(error, &xb, 0);
}
p = nfsl->nl_path;
while (*p && (*p == '/'))
p++;
numcomps = 0;
while (*p) {
while (*p && (*p != '/'))
p++;
numcomps++;
while (*p && (*p == '/'))
p++;
}
xb_add_32(error, &xb, numcomps);
p = nfsl->nl_path;
while (*p && (*p == '/'))
p++;
numcomps = 0;
while (*p) {
cp = p;
while (*p && (*p != '/'))
p++;
xb_add_string(error, &xb, cp, (p - cp));
if (error)
break;
while (*p && (*p == '/'))
p++;
}
xb_add_32(error, &xb, 0);
}
xb_add_32(error, &xb, options.mntflags);
xb_build_done(error, &xb);
end_offset = xb_offset(&xb);
if (!error) {
error = xb_seek(&xb, argslength_offset);
xb_add_32(error, &xb, end_offset - argslength_offset + XDRWORD);
}
if (!error) {
error = xb_seek(&xb, attrslength_offset);
xb_add_32(error, &xb, end_offset - attrslength_offset - XDRWORD);
}
if (!error) {
*xdrbufp = xb_buffer_base(&xb);
xb.xb_flags &= ~XB_CLEANUP;
}
xb_cleanup(&xb);
return (error);
}
void
setNFSVersion(uint32_t vers)
{
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_NFS_VERSION) &&
(options.nfs_version != vers))
errx(EINVAL,"conflicting NFS version options: %d %d",
options.nfs_version, vers);
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_NFS_VERSION);
options.nfs_version = vers;
}
static int
get_sec_flavors(const char *flavorlist_orig, struct nfs_sec *sec_flavs)
{
char *flavorlist;
char *flavor;
u_int32_t flav_bits;
#define SYS_BIT 0x00000001
#define KRB5_BIT 0x00000002
#define KRB5I_BIT 0x00000004
#define KRB5P_BIT 0x00000008
#define NONE_BIT 0x00000010
flavorlist = strdup(flavorlist_orig);
if (!flavorlist)
return (ENOMEM);
bzero(sec_flavs, sizeof(struct nfs_sec));
flav_bits = 0;
while ( ((flavor = strsep(&flavorlist, ":")) != NULL) && (sec_flavs->count < NX_MAX_SEC_FLAVORS)) {
if (flavor[0] == '\0')
continue;
if (!strcmp("krb5p", flavor)) {
if (flav_bits & KRB5P_BIT) {
warnx("sec krb5p appears more than once: %s", flavorlist);
continue;
}
flav_bits |= KRB5P_BIT;
sec_flavs->flavors[sec_flavs->count++] = RPCAUTH_KRB5P;
} else if (!strcmp("krb5i", flavor)) {
if (flav_bits & KRB5I_BIT) {
warnx("sec krb5i appears more than once: %s", flavorlist);
continue;
}
flav_bits |= KRB5I_BIT;
sec_flavs->flavors[sec_flavs->count++] = RPCAUTH_KRB5I;
} else if (!strcmp("krb5", flavor)) {
if (flav_bits & KRB5_BIT) {
warnx("sec krb5 appears more than once: %s", flavorlist);
continue;
}
flav_bits |= KRB5_BIT;
sec_flavs->flavors[sec_flavs->count++] = RPCAUTH_KRB5;
} else if (!strcmp("sys", flavor)) {
if (flav_bits & SYS_BIT) {
warnx("sec sys appears more than once: %s", flavorlist);
continue;
}
flav_bits |= SYS_BIT;
sec_flavs->flavors[sec_flavs->count++] = RPCAUTH_SYS;
} else if (!strcmp("none", flavor)) {
if (flav_bits & NONE_BIT) {
warnx("sec none appears more than once: %s", flavorlist);
continue;
}
flav_bits |= NONE_BIT;
sec_flavs->flavors[sec_flavs->count++] = RPCAUTH_NONE;
} else {
warnx("unknown sec flavor '%s' ignored", flavor);
}
}
free(flavorlist);
if (sec_flavs->count)
return 0;
else
return 1;
}
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, &options.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 {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_ATTRCACHE_REG_MIN);
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_ATTRCACHE_REG_MAX);
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_ATTRCACHE_DIR_MIN);
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_ATTRCACHE_DIR_MAX);
options.acregmin.tv_sec = num;
options.acregmax.tv_sec = num;
options.acdirmin.tv_sec = num;
options.acdirmax.tv_sec = num;
options.acregmin.tv_nsec = 0;
options.acregmax.tv_nsec = 0;
options.acdirmin.tv_nsec = 0;
options.acdirmax.tv_nsec = 0;
}
}
if ((p2 = getmntoptstr(mop, "acregmin"))) {
num = getmntoptnum(mop, "acregmin");
if (num < 0) {
warnx("illegal acregmin value -- %s", p2);
} else {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_ATTRCACHE_REG_MIN);
options.acregmin.tv_sec = num;
options.acregmin.tv_nsec = 0;
}
}
if ((p2 = getmntoptstr(mop, "acregmax"))) {
num = getmntoptnum(mop, "acregmax");
if (num < 0) {
warnx("illegal acregmax value -- %s", p2);
} else {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_ATTRCACHE_REG_MAX);
options.acregmax.tv_sec = num;
options.acregmax.tv_nsec = 0;
}
}
if ((p2 = getmntoptstr(mop, "acdirmin"))) {
num = getmntoptnum(mop, "acdirmin");
if (num < 0) {
warnx("illegal acdirmin value -- %s", p2);
} else {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_ATTRCACHE_DIR_MIN);
options.acdirmin.tv_sec = num;
options.acdirmin.tv_nsec = 0;
}
}
if ((p2 = getmntoptstr(mop, "acdirmax"))) {
num = getmntoptnum(mop, "acdirmax");
if (num < 0) {
warnx("illegal acdirmax value -- %s", p2);
} else {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_ATTRCACHE_DIR_MAX);
options.acdirmax.tv_sec = num;
options.acdirmax.tv_nsec = 0;
}
}
}
if (altflags & ALTF_DEADTIMEOUT) {
if ((p2 = getmntoptstr(mop, "deadtimeout"))) {
num = strtol(p2, &p, 10);
if (num < 0) {
warnx("illegal deadtimeout value -- %s", p2);
} else {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_DEAD_TIMEOUT);
options.dead_timeout.tv_sec = num;
options.dead_timeout.tv_nsec = 0;
}
}
}
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 {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_READDIR_SIZE);
options.readdirsize = num;
}
}
}
if (altflags & ALTF_MAXGROUPS) {
num = getmntoptnum(mop, "maxgroups");
if (num <= 0) {
warnx("illegal maxgroups value -- %s", getmntoptstr(mop, "maxgroups"));
} else {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_MAX_GROUP_LIST);
options.maxgrouplist = num;
}
}
if (altflags & ALTF_MOUNTPORT) {
num = getmntoptnum(mop, "mountport");
if (num < 0) {
warnx("illegal mountport number -- %s", getmntoptstr(mop, "mountport"));
} else {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_MOUNT_PORT);
options.mount_port = num;
}
}
if (altflags & ALTF_PORT) {
num = getmntoptnum(mop, "port");
if (num < 0) {
warnx("illegal port number -- %s", getmntoptstr(mop, "port"));
} else {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_NFS_PORT);
options.nfs_port = num;
}
}
if (altflags & ALTF_PROTO) {
p2 = getmntoptstr(mop, "proto");
if (p2) {
if (!strcasecmp(p2, "tcp")) {
options.socket_type = SOCK_STREAM;
options.socket_family = AF_INET;
} else if (!strcasecmp(p2, "udp")) {
options.socket_type = SOCK_DGRAM;
options.socket_family = AF_INET;
} else if (!strcasecmp(p2, "tcp6")) {
options.socket_type = SOCK_STREAM;
options.socket_family = AF_INET6;
} else if (!strcasecmp(p2, "udp6")) {
options.socket_type = SOCK_DGRAM;
options.socket_family = AF_INET6;
} 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 {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_READAHEAD);
options.readahead = num;
}
}
if (altflags & ALTF_RETRANS) {
num = getmntoptnum(mop, "retrans");
if (num <= 0) {
warnx("illegal retrans value -- %s", getmntoptstr(mop, "retrans"));
} else {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_SOFT_RETRY_COUNT);
options.soft_retry_count = num;
}
}
if (altflags & ALTF_RETRYCNT)
retrycnt = getmntoptnum(mop, "retrycnt");
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 {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_READ_SIZE);
options.rsize = num;
}
}
}
if (altflags & ALTF_SEC) {
p2 = getmntoptstr(mop, "sec");
if (!p2)
warnx("missing security value for sec= option");
else if (get_sec_flavors(p2, &options.sec))
warnx("couldn't parse security value -- %s", p2);
else
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_SECURITY);
}
if (altflags & ALTF_TIMEO) {
num = getmntoptnum(mop, "timeo");
if (num <= 0) {
warnx("illegal timeout value -- %s", getmntoptstr(mop, "timeo"));
} else {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_REQUEST_TIMEOUT);
options.request_timeout.tv_sec = num/10;
options.request_timeout.tv_nsec = (num%10) * 100000000;
}
}
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) {
switch (num) {
case 2:
setNFSVersion(2);
break;
case 3:
setNFSVersion(3);
break;
case 4:
setNFSVersion(4);
break;
default:
warnx("illegal NFS version value -- %s", p2);
break;
}
}
}
if (altflags & ALTF_VERS2) {
warnx("option nfsv2 deprecated, use vers=#");
setNFSVersion(2);
}
if (altflags & ALTF_VERS3) {
warnx("option nfsv3 deprecated, use vers=#");
setNFSVersion(3);
}
if (altflags & ALTF_VERS4) {
warnx("option nfsv4 deprecated, use vers=#");
setNFSVersion(4);
}
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 {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_WRITE_SIZE);
options.wsize = num;
}
}
}
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_ACL)
SETFLAG(NFS_MFLAG_NOACL, 0);
if (altflags & ALTF_ACLONLY)
SETFLAG(NFS_MFLAG_ACLONLY, 1);
if (altflags & ALTF_ATTRCACHE) {
NFS_BITMAP_CLR(options.mattrs, NFS_MATTR_ATTRCACHE_REG_MIN);
NFS_BITMAP_CLR(options.mattrs, NFS_MATTR_ATTRCACHE_REG_MAX);
NFS_BITMAP_CLR(options.mattrs, NFS_MATTR_ATTRCACHE_DIR_MIN);
NFS_BITMAP_CLR(options.mattrs, NFS_MATTR_ATTRCACHE_DIR_MAX);
}
if (altflags & ALTF_BG)
opflags |= BGRND;
if (altflags & ALTF_CALLBACK)
SETFLAG(NFS_MFLAG_NOCALLBACK, 0);
if (altflags & ALTF_CONN)
SETFLAG(NFS_MFLAG_NOCONNECT, 0);
if (altflags & ALTF_DUMBTIMR)
SETFLAG(NFS_MFLAG_DUMBTIMER, 1);
if (altflags & ALTF_HARD)
SETFLAG(NFS_MFLAG_SOFT, 0);
if (altflags & ALTF_INET)
options.socket_family = AF_INET;
if (altflags & ALTF_INET6)
options.socket_family = AF_INET6;
if (altflags & ALTF_INTR)
SETFLAG(NFS_MFLAG_INTR, 1);
if (altflags & ALTF_LOCALLOCKS) {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_LOCK_MODE);
options.lockmode = NFS_LOCK_MODE_LOCAL;
}
if (altflags & ALTF_LOCKS) {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_LOCK_MODE);
options.lockmode = NFS_LOCK_MODE_ENABLED;
}
if (altflags & ALTF_MNTUDP)
SETFLAG(NFS_MFLAG_MNTUDP, 1);
if (altflags & ALTF_MUTEJUKEBOX)
SETFLAG(NFS_MFLAG_MUTEJUKEBOX, 1);
if (altflags & ALTF_NAMEDATTR)
SETFLAG(NFS_MFLAG_NONAMEDATTR, 0);
if (altflags & ALTF_NEGNAMECACHE)
SETFLAG(NFS_MFLAG_NONEGNAMECACHE, 0);
if (altflags & ALTF_NFC)
SETFLAG(NFS_MFLAG_NFC, 1);
if (altflags & ALTF_QUOTA)
SETFLAG(NFS_MFLAG_NOQUOTA, 0);
if (altflags & ALTF_RDIRPLUS)
SETFLAG(NFS_MFLAG_RDIRPLUS, 1);
if (altflags & ALTF_RESVPORT)
SETFLAG(NFS_MFLAG_RESVPORT, 1);
if (altflags & ALTF_SOFT)
SETFLAG(NFS_MFLAG_SOFT, 1);
if (altflags & ALTF_TCP)
options.socket_type = SOCK_STREAM;
if (altflags & ALTF_UDP)
options.socket_type = SOCK_DGRAM;
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("negative altflags=0x%x\n", altflags);
if (altflags & ALTF_ACL)
SETFLAG(NFS_MFLAG_NOACL, 1);
if (altflags & ALTF_ACLONLY)
SETFLAG(NFS_MFLAG_ACLONLY, 0);
if (altflags & ALTF_ATTRCACHE) {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_ATTRCACHE_REG_MIN);
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_ATTRCACHE_REG_MAX);
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_ATTRCACHE_DIR_MIN);
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_ATTRCACHE_DIR_MAX);
options.acregmin.tv_sec = 0;
options.acregmax.tv_sec = 0;
options.acdirmin.tv_sec = 0;
options.acdirmax.tv_sec = 0;
options.acregmin.tv_nsec = 0;
options.acregmax.tv_nsec = 0;
options.acdirmin.tv_nsec = 0;
options.acdirmax.tv_nsec = 0;
}
if (altflags & ALTF_BG)
opflags &= ~BGRND;
if (altflags & ALTF_CALLBACK)
SETFLAG(NFS_MFLAG_NOCALLBACK, 1);
if (altflags & ALTF_CONN)
SETFLAG(NFS_MFLAG_NOCONNECT, 1);
if (altflags & ALTF_DUMBTIMR)
SETFLAG(NFS_MFLAG_DUMBTIMER, 0);
if (altflags & ALTF_HARD)
SETFLAG(NFS_MFLAG_SOFT, 1);
if (altflags & ALTF_INET)
options.socket_family = AF_INET6;
if (altflags & ALTF_INET6)
options.socket_family = AF_INET;
if (altflags & ALTF_INTR)
SETFLAG(NFS_MFLAG_INTR, 0);
if (altflags & ALTF_LOCALLOCKS) {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_LOCK_MODE);
options.lockmode = NFS_LOCK_MODE_ENABLED;
}
if (altflags & ALTF_LOCKS) {
NFS_BITMAP_SET(options.mattrs, NFS_MATTR_LOCK_MODE);
options.lockmode = NFS_LOCK_MODE_DISABLED;
}
if (altflags & ALTF_MNTUDP)
SETFLAG(NFS_MFLAG_MNTUDP, 0);
if (altflags & ALTF_MUTEJUKEBOX)
SETFLAG(NFS_MFLAG_MUTEJUKEBOX, 0);
if (altflags & ALTF_NAMEDATTR)
SETFLAG(NFS_MFLAG_NONAMEDATTR, 1);
if (altflags & ALTF_NEGNAMECACHE)
SETFLAG(NFS_MFLAG_NONEGNAMECACHE, 1);
if (altflags & ALTF_NFC)
SETFLAG(NFS_MFLAG_NFC, 0);
if (altflags & ALTF_QUOTA)
SETFLAG(NFS_MFLAG_NOQUOTA, 1);
if (altflags & ALTF_RDIRPLUS)
SETFLAG(NFS_MFLAG_RDIRPLUS, 0);
if (altflags & ALTF_RESVPORT)
SETFLAG(NFS_MFLAG_RESVPORT, 0);
if (altflags & ALTF_SOFT)
SETFLAG(NFS_MFLAG_SOFT, 0);
if (altflags & ALTF_TCP)
options.socket_type = SOCK_DGRAM;
if (altflags & ALTF_UDP)
options.socket_type = SOCK_STREAM;
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 > 1)
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")) {
if (value && val)
conf->access_cache_timeout = val;
} else if (!strcmp(key, "nfs.client.access_for_getattr")) {
conf->access_for_getattr = val;
} else if (!strcmp(key, "nfs.client.allow_async")) {
conf->allow_async = val;
} else if (!strcmp(key, "nfs.client.callback_port")) {
if (value && val)
conf->callback_port = val;
} else if (!strcmp(key, "nfs.client.initialdowndelay")) {
conf->initialdowndelay = val;
} else if (!strcmp(key, "nfs.client.iosize")) {
if (value && val)
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")) {
if (value)
conf->nfsiod_thread_max = val;
} else if (!strcmp(key, "nfs.client.statfs_rate_limit")) {
if (value && val)
conf->statfs_rate_limit = val;
} else if (!strcmp(key, "nfs.client.is_mobile")) {
if (value && val)
conf->is_mobile = val;
} else if (!strcmp(key, "nfs.client.squishy_flags")) {
if (value && val)
conf->squishy_flags = 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)));
}
static int
mobile_client()
{
char model[128];
size_t len = sizeof(model);
if (sysctlbyname("hw.model", &model, &len, NULL, 0) < 0 || len <= 0)
return 0;
return strnstr(model, "Book", len) != NULL;
}
void
config_sysctl(void)
{
if (config.access_cache_timeout != -1)
sysctl_set("vfs.generic.nfs.client.access_cache_timeout", config.access_cache_timeout);
if (config.access_for_getattr != -1)
sysctl_set("vfs.generic.nfs.client.access_for_getattr", config.access_for_getattr);
if (config.allow_async != -1)
sysctl_set("vfs.generic.nfs.client.allow_async", config.allow_async);
if (config.callback_port != -1)
sysctl_set("vfs.generic.nfs.client.callback_port", config.callback_port);
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);
if (config.is_mobile == -1)
config.is_mobile = mobile_client();
sysctl_set("vfs.generic.nfs.client.is_mobile", config.is_mobile);
if (config.squishy_flags != -1)
sysctl_set("vfs.generic.nfs.client.squishy_flags", config.squishy_flags);
}
int
parse_fs_locations(const char *mntfromarg, struct nfs_fs_location **nfslp)
{
struct nfs_fs_location *nfslhead = NULL;
struct nfs_fs_location *nfslprev = NULL;
struct nfs_fs_location *nfsl = NULL;
struct nfs_fs_server *nfss, *nfssprev;
struct sockaddr_in6 sin6;
char *argcopy, *p, *q, *host, *path, *colon, *colonslash, ch;
*nfslp = NULL;
argcopy = strdup(mntfromarg);
if (!argcopy)
return (ENOMEM);
p = argcopy;
while (*p) {
if (!((nfsl = malloc(sizeof(*nfsl)))))
return (ENOMEM);
bzero(nfsl, sizeof(*nfsl));
colon = colonslash = NULL;
nfssprev = NULL;
while (*p && (!colon || !colonslash)) {
if (!((nfss = malloc(sizeof(*nfss)))))
return (ENOMEM);
bzero(nfss, sizeof(*nfss));
host = p;
if (*p == '[') {
p++;
while (isxdigit(*p) || (*p == ':')) {
if (*p == ':') {
if (!colon)
colon = p;
if (!colonslash && (*(p+1) == '/'))
colonslash = p;
}
p++;
}
if ((*p == ']') && ((*(p+1) == ',') || (*(p+1) == ':'))) {
ch = *p;
*p = '\0';
if (inet_pton(AF_INET6, host+1, &sin6)) {
*p = ch;
p++;
goto addhost;
}
*p = ch;
p++;
}
}
while (*p && (!colon || !colonslash)) {
if (*p == ':') {
if (!colon)
colon = p;
if (!colonslash && (*(p+1) == '/')) {
colonslash = p;
break;
}
}
if (*p == ',')
break;
p++;
}
if (!*p) {
if (!colon)
return (EINVAL);
p = colon;
}
addhost:
ch = *p;
*p = '\0';
nfss->ns_name = strdup(host);
*p = ch;
if (!nfss->ns_name)
return (ENOMEM);
if (nfssprev)
nfssprev->ns_next = nfss;
else
nfsl->nl_servers = nfss;
nfsl->nl_servcnt++;
nfssprev = nfss;
nfss = NULL;
if (*p++ != ',')
break;
colon = colonslash = NULL;
}
if (!*p) {
if (!colon)
return (EINVAL);
p = colon;
p++;
}
path = p;
while (*p) {
if (*p == ',') {
q = p+1;
while (*q && (*q != ':'))
q++;
if (!*q)
p = q;
break;
}
p++;
}
ch = *p;
*p = '\0';
nfsl->nl_path = strdup(path);
*p = ch;
if (!nfsl->nl_path)
return (ENOMEM);
if (nfslprev)
nfslprev->nl_next = nfsl;
else
nfslhead = nfsl;
nfslprev = nfsl;
nfsl = NULL;
if (*p == ',')
p++;
}
if (!nfslhead)
return (EINVAL);
free(argcopy);
*nfslp = nfslhead;
return (0);
}
static int
addrinfo_cmp(struct addrinfo *a1, struct addrinfo *a2)
{
if (a1->ai_family != a2->ai_family)
return (a1->ai_family - a2->ai_family);
if (a1->ai_addrlen != a2->ai_addrlen)
return (a1->ai_addrlen - a2->ai_addrlen);
return bcmp(a1->ai_addr, a2->ai_addr, a1->ai_addrlen);
}
int
getaddresslist(struct nfs_fs_server *nfss)
{
struct addrinfo aihints, *ailist = NULL, *ai, *aiprev, *ainext, *aidiscard;
char *hostname, namebuf[NI_MAXHOST];
void *sinaddr;
char uaddr[128];
const char *uap;
nfss->ns_ailist = NULL;
hostname = nfss->ns_name;
if ((hostname[0] == '[') && (hostname[strlen(hostname)-1] == ']')) {
strlcpy(namebuf, hostname+1, sizeof(namebuf));
namebuf[strlen(namebuf)-1] = '\0';
hostname = namebuf;
}
bzero(&aihints, sizeof(aihints));
aihints.ai_flags = AI_ADDRCONFIG;
aihints.ai_socktype = options.socket_type;
if (getaddrinfo(hostname, NULL, &aihints, &ailist)) {
warnx("can't resolve host: %s", hostname);
return (ENOENT);
}
aidiscard = NULL;
aiprev = NULL;
for (ai = ailist; ai; ai = ainext) {
ainext = ai->ai_next;
if (options.socket_family && (ai->ai_family != options.socket_family))
goto discard;
if (options.socket_type && (ai->ai_socktype != options.socket_type))
goto discard;
if ((options.nfs_version >= 4) && (ai->ai_socktype == SOCK_DGRAM))
goto discard;
if ((ai->ai_family != AF_INET) && (ai->ai_family != AF_INET6))
goto discard;
if (!options.socket_type && aiprev &&
(ai->ai_socktype != aiprev->ai_socktype) &&
!addrinfo_cmp(aiprev, ai))
goto discard;
aiprev = ai;
if (verbose > 2) {
if (ai->ai_family == AF_INET)
sinaddr = &((struct sockaddr_in*)ai->ai_addr)->sin_addr;
else
sinaddr = &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr;
uap = inet_ntop(ai->ai_family, sinaddr, uaddr, sizeof(uaddr));
printf("usable address: %s %s %s\n",
(ai->ai_socktype == SOCK_DGRAM) ? "udp" :
(ai->ai_socktype == SOCK_STREAM) ? "tcp" : "???",
(ai->ai_family == AF_INET) ? "inet" :
(ai->ai_family == AF_INET6) ? "inet6" : "???",
uap ? uap : "???");
}
continue;
discard:
if (aiprev)
aiprev->ai_next = ai->ai_next;
else
ailist = ai->ai_next;
ai->ai_next = aidiscard;
aidiscard = ai;
if (verbose > 2) {
if (ai->ai_family == AF_INET)
sinaddr = &((struct sockaddr_in*)ai->ai_addr)->sin_addr;
else
sinaddr = &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr;
uap = inet_ntop(ai->ai_family, sinaddr, uaddr, sizeof(uaddr));
printf("discard address: %s %s %s\n",
(ai->ai_socktype == SOCK_DGRAM) ? "udp" :
(ai->ai_socktype == SOCK_STREAM) ? "tcp" : "???",
(ai->ai_family == AF_INET) ? "inet" :
(ai->ai_family == AF_INET6) ? "inet6" : "???",
uap ? uap : "???");
}
}
if (aidiscard)
freeaddrinfo(aidiscard);
nfss->ns_ailist = ailist;
return (0);
}
void
getaddresslists(struct nfs_fs_location *nfsl, int *servcnt)
{
struct nfs_fs_server *nfss;
int error;
*servcnt = 0;
while (nfsl) {
nfss = nfsl->nl_servers;
while (nfss) {
error = getaddresslist(nfss);
if (!error && nfss->ns_ailist)
*servcnt += 1;
nfss = nfss->ns_next;
}
nfsl = nfsl->nl_next;
}
}
void
freeaddresslists(struct nfs_fs_location *nfsl)
{
struct nfs_fs_server *nfss;
while (nfsl) {
nfss = nfsl->nl_servers;
while (nfss) {
if (nfss->ns_ailist) {
freeaddrinfo(nfss->ns_ailist);
nfss->ns_ailist = NULL;
}
nfss = nfss->ns_next;
}
nfsl = nfsl->nl_next;
}
}
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" },
{ MNT_DONTBROWSE, "nobrowse" },
{ MNT_CPROTECT, "protect"},
{ MNT_ROOTFS, "rootfs"},
{ MNT_DOVOLFS, "dovolfs"},
{ MNT_NOUSERXATTR, "nouserxattr"},
{ MNT_MULTILABEL, "multilabel"},
{ 0, NULL }
};
static const char *
sec_flavor_name(uint32_t flavor)
{
switch(flavor) {
case RPCAUTH_NONE: return ("none");
case RPCAUTH_SYS: return ("sys");
case RPCAUTH_KRB5: return ("krb5");
case RPCAUTH_KRB5I: return ("krb5i");
case RPCAUTH_KRB5P: return ("krb5p");
default: return ("?");
}
}
void
dump_mount_options(struct nfs_fs_location *nfslhead, char *mntonname)
{
struct nfs_fs_location *nfsl;
struct nfs_fs_server *nfss;
struct addrinfo *ai;
void *sinaddr;
char uaddr[128];
const char *uap;
struct opt *o;
int flags, i;
printf("mount %s on %s\n", mntfromarg, mntonname);
flags = options.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("socket: type:%s", ((options.socket_type == SOCK_STREAM) ? "tcp" :
(options.socket_type == SOCK_DGRAM) ? "udp" : "any"));
if (options.socket_family)
printf("%s%s", (options.socket_type ? "" : ",inet"),
((options.socket_family == AF_INET) ? "4" :
(options.socket_family == AF_INET6) ? "6" : ""));
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_NFS_PORT))
printf(",port=%d", options.nfs_port);
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_MOUNT_PORT))
printf(",mountport=%d", options.mount_port);
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_MNTUDP) || (verbose > 1))
printf(",%smntudp", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_MNTUDP) ? "" : "no");
printf("\n");
printf("file system locations:\n");
for (nfsl=nfslhead; nfsl; nfsl=nfsl->nl_next) {
printf("%s\n", nfsl->nl_path);
for (nfss=nfsl->nl_servers; nfss; nfss=nfss->ns_next) {
printf(" %s\n", nfss->ns_name);
for (ai=nfss->ns_ailist; ai; ai=ai->ai_next) {
if (ai->ai_family == AF_INET)
sinaddr = &((struct sockaddr_in*)ai->ai_addr)->sin_addr;
else
sinaddr = &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr;
uap = inet_ntop(ai->ai_family, sinaddr, uaddr, sizeof(uaddr));
printf(" %s %s\n",
(ai->ai_family == AF_INET) ? "inet" :
(ai->ai_family == AF_INET6) ? "inet6" : "???",
uap ? uap : "???");
}
}
}
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_FH)) {
printf("fh %d ", options.fh.fh_len);
for (i=0; i < options.fh.fh_len; i++)
printf("%02x", options.fh.fh_data[i] & 0xff);
printf("\n");
}
printf("NFS options:");
printf(" %s,retrycnt=%d", ((opflags & BGRND) ? "bg" : "fg"), retrycnt);
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_NFS_VERSION)) {
printf(",vers=%d", options.nfs_version);
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_NFS_MINOR_VERSION))
printf(".%d", options.nfs_minor_version);
}
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_SOFT) || (verbose > 1))
printf(",%s", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_SOFT) ? "soft" : "hard");
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_INTR) || (verbose > 1))
printf(",%sintr", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_INTR) ? "" : "no");
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_RESVPORT) || (verbose > 1))
printf(",%sresvport", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_RESVPORT) ? "" : "no");
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_NOCONNECT) || (verbose > 1))
printf(",%sconn", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_NOCONNECT) ? "no" : "");
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_NOCALLBACK) || (verbose > 1))
printf(",%scallback", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_NOCALLBACK) ? "no" : "");
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_NONEGNAMECACHE) || (verbose > 1))
printf(",%snegnamecache", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_NONEGNAMECACHE) ? "no" : "");
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_NONAMEDATTR) || (verbose > 1))
printf(",%snamedattr", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_NONAMEDATTR) ? "no" : "");
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_NOACL) || (verbose > 1))
printf(",%sacl", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_NOACL) ? "no" : "");
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_ACLONLY) || (verbose > 1))
printf(",%saclonly", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_ACLONLY) ? "" : "no");
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_CALLUMNT) || (verbose > 1))
printf(",%scallumnt", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_CALLUMNT) ? "" : "no");
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_LOCK_MODE) || (verbose > 1))
switch(options.lockmode) {
case NFS_LOCK_MODE_ENABLED:
printf(",locks");
break;
case NFS_LOCK_MODE_DISABLED:
printf(",nolocks");
break;
case NFS_LOCK_MODE_LOCAL:
printf(",locallocks");
break;
}
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_NOQUOTA) || (verbose > 1))
printf(",%squota", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_NOQUOTA) ? "no" : "");
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_READ_SIZE) || (verbose > 1))
printf(",rsize=%d", NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_READ_SIZE) ? options.rsize : NFS_RSIZE);
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_WRITE_SIZE) || (verbose > 1))
printf(",wsize=%d", NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_WRITE_SIZE) ? options.wsize : NFS_WSIZE);
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_READAHEAD) || (verbose > 1))
printf(",readahead=%d", NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_READAHEAD) ? options.readahead : NFS_DEFRAHEAD);
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_READDIR_SIZE) || (verbose > 1))
printf(",dsize=%d", NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_READDIR_SIZE) ? options.readdirsize : NFS_READDIRSIZE);
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_RDIRPLUS) || (verbose > 1))
printf(",%srdirplus", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_RDIRPLUS) ? "" : "no");
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_DUMBTIMER) || (verbose > 1))
printf(",%sdumbtimr", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_DUMBTIMER) ? "" : "no");
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_REQUEST_TIMEOUT) || (verbose > 1))
printf(",timeo=%ld", NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_REQUEST_TIMEOUT) ?
((options.request_timeout.tv_sec * 10) + (options.request_timeout.tv_nsec % 100000000)) : 10);
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_SOFT_RETRY_COUNT) || (verbose > 1))
printf(",retrans=%d", NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_SOFT_RETRY_COUNT) ? options.soft_retry_count : NFS_RETRANS);
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_MAX_GROUP_LIST) || (verbose > 1))
printf(",maxgroups=%d", NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_MAX_GROUP_LIST) ? options.maxgrouplist : NFS_MAXGRPS);
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_ATTRCACHE_REG_MIN) || (verbose > 1))
printf(",acregmin=%ld", NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_ATTRCACHE_REG_MIN) ? options.acregmin.tv_sec : NFS_MINATTRTIMO);
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_ATTRCACHE_REG_MAX) || (verbose > 1))
printf(",acregmax=%ld", NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_ATTRCACHE_REG_MAX) ? options.acregmax.tv_sec : NFS_MAXATTRTIMO);
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_ATTRCACHE_DIR_MIN) || (verbose > 1))
printf(",acdirmin=%ld", NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_ATTRCACHE_DIR_MIN) ? options.acdirmin.tv_sec : NFS_MINATTRTIMO);
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_ATTRCACHE_DIR_MAX) || (verbose > 1))
printf(",acdirmax=%ld", NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_ATTRCACHE_DIR_MAX) ? options.acdirmax.tv_sec : NFS_MAXATTRTIMO);
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_DEAD_TIMEOUT) || (verbose > 1))
printf(",deadtimeout=%ld", NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_DEAD_TIMEOUT) ? options.dead_timeout.tv_sec : 0);
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_MUTEJUKEBOX) || (verbose > 1))
printf(",%smutejukebox", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_MUTEJUKEBOX) ? "" : "no");
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_EPHEMERAL) || (verbose > 1))
printf(",%sephemeral", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_EPHEMERAL) ? "" : "no");
if (NFS_BITMAP_ISSET(options.mflags_mask, NFS_MFLAG_NFC) || (verbose > 1))
printf(",%snfc", NFS_BITMAP_ISSET(options.mflags, NFS_MFLAG_NFC) ? "" : "no");
if (NFS_BITMAP_ISSET(options.mattrs, NFS_MATTR_SECURITY)) {
printf(",sec=%s", sec_flavor_name(options.sec.flavors[0]));
for (i=1; i < options.sec.count; i++)
printf(":%s", sec_flavor_name(options.sec.flavors[i]));
}
printf("\n");
}