#include <sys/param.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/stat.h>
#include <sys/syslog.h>
#include <sys/sysctl.h>
#include <sys/ucred.h>
#include <sys/wait.h>
#include <sys/queue.h>
#include <rpc/rpc.h>
#include <rpc/pmap_clnt.h>
#include <rpc/pmap_prot.h>
#ifdef ISO
#include <netiso/iso.h>
#endif
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfs/nfs.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <grp.h>
#include <netdb.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "pathnames.h"
#include <stdarg.h>
#include <CoreFoundation/CoreFoundation.h>
#include <DiskArbitration/DiskArbitration.h>
#define EXPORT_FROM_NETINFO 0
#define EXPORT_FROM_FILE 1
#define EXPORT_FROM_FILEFIRST 2
static int source = EXPORT_FROM_FILEFIRST;
#include <netinfo/ni.h>
struct expidlist {
LIST_ENTRY(expidlist) xid_list;
char xid_path[MAXPATHLEN];
u_int32_t xid_id;
};
struct uuidlist {
TAILQ_ENTRY(uuidlist) ul_list;
char ul_mntfromname[MAXPATHLEN];
char ul_mntonname[MAXPATHLEN];
u_char ul_uuid[16];
u_char ul_dauuid[16];
char ul_davalid;
char ul_exported;
u_int32_t ul_fsid;
LIST_HEAD(expidhead,expidlist) ul_exportids;
};
TAILQ_HEAD(,uuidlist) ulhead;
#define UL_CHECK_MNTFROM 0x1
#define UL_CHECK_MNTON 0x2
#define UL_CHECK_ALL 0x3
#define UUID2FSID(U) \
(*((u_int32_t*)(U)) ^ *(((u_int32_t*)(U))+1) ^ \
*(((u_int32_t*)(U))+2) ^ *(((u_int32_t*)(U))+3))
struct mountlist {
struct mountlist *ml_next;
char *ml_host;
char *ml_dir;
};
struct dirlist {
struct dirlist *dl_next;
char *dl_dir;
};
TAILQ_HEAD(expfstqh, expfs);
TAILQ_HEAD(expdirtqh, expdir);
struct expfs {
TAILQ_ENTRY(expfs) xf_next;
struct expdirtqh xf_dirl;
int xf_flag;
u_char xf_uuid[16];
char *xf_fsdir;
};
#define XF_LINKED 0x1
struct expdir {
TAILQ_ENTRY(expdir) xd_next;
struct host *xd_hosts;
struct expdirtqh xd_mountdirs;
int xd_flags;
char *xd_dir;
};
struct netmsk {
u_long nt_net;
u_long nt_mask;
char *nt_name;
};
union grouptypes {
struct hostent *gt_hostent;
struct netmsk gt_net;
#ifdef ISO
struct sockaddr_iso *gt_isoaddr;
#endif
};
struct grouplist {
int gr_type;
union grouptypes gr_u;
struct grouplist *gr_next;
};
#define GT_NULL 0x0
#define GT_HOST 0x1
#define GT_NET 0x2
#define GT_ISO 0x4
struct host {
int ht_flag;
struct grouplist *ht_grp;
struct host *ht_next;
};
#define DEFAULTHOSTNAME "localhost"
struct fhreturn {
int fhr_flag;
int fhr_vers;
fhandle_t fhr_fh;
};
int add_dir(struct dirlist **, char *);
char * add_expdir(struct expdir **, char *, int);
void add_mlist(char *, char *);
int check_dirpath(char *);
int check_options(int);
void del_mlist(char *, char *);
int expdir_search(struct expfs *, char *, u_long, int *);
int do_export(struct expfs *, struct grouplist *, int,
struct xucred *, char *, struct statfs *, struct uuidlist *);
int do_opt(char **, char **, struct grouplist *, int *,
int *, int *, struct xucred *);
struct expfs *ex_search(u_char *);
struct host *find_host(struct host *, u_long);
void free_dirlist(struct dirlist *dl);
void free_expdir(struct expdir *);
void free_expfs(struct expfs *);
void free_grp(struct grouplist *);
void free_host(struct host *);
struct expdir *get_expdir(void);
struct expfs *get_expfs(void);
void get_hostnames(char **hostnamearray);
char * get_ifinfo(int, int, int *);
void get_exportlist(void);
int get_host_addresses(char *, struct grouplist *);
struct host *get_host(void);
int get_line(int);
int ni_get_line(void);
int file_get_line(void);
void get_mountlist(void);
int get_net(char *, struct netmsk *, int);
void getexp_err(struct expfs *, struct grouplist *);
struct grouplist *get_grp(void);
int hang_options(struct expdir *, int, struct grouplist *);
int hang_options_mountdir(struct expdir *, char *, int, struct grouplist *);
void mntsrv(struct svc_req *, SVCXPRT *);
void my_svc_run(void);
void nextfield(char **, char **);
void out_of_mem(char *);
void parsecred(char *, struct xucred *);
int put_exlist(struct expdir *, XDR *);
int register_export(const char *, char * const *, int);
void sigmux(int);
int xdr_dir(XDR *, char *);
int xdr_explist(XDR *, caddr_t);
int xdr_fhs(XDR *, caddr_t);
int xdr_mlist(XDR *, caddr_t);
int get_uuid_from_diskarb(const char *, u_char *);
struct uuidlist * get_uuid_from_list(const struct statfs *, u_char *, const int);
struct uuidlist * add_uuid_to_list(const struct statfs *, u_char *, u_char *);
struct uuidlist * get_uuid(const struct statfs *, u_char *);
struct uuidlist * find_uuid(u_char *);
struct uuidlist * find_uuid_by_fsid(u_int32_t);
void uuidlist_clearexport(void);
char * uuidstring(u_char *, char *);
void uuidlist_save(void);
void uuidlist_restore(void);
struct expidlist * find_export_id(struct uuidlist *, u_int32_t);
struct expidlist * get_export_id(struct uuidlist *, char *);
int getnetgrent(char **host, char **user, char **domain);
void endnetgrent(void);
void setnetgrent(const char *netgroup);
#ifdef ISO
struct iso_addr *iso_addr(void);
#endif
void ni_exports_open(void);
void ni_exports_close(void);
struct expfstqh xfshead;
struct mountlist *mlhead;
struct grouplist *grphead;
char exportsfilepath[MAXPATHLEN];
struct xucred def_anon = {
XUCRED_VERSION,
(uid_t) -2,
1,
{ (gid_t) -2 },
};
int resvport_only = 1;
int dir_only = 1;
#define OP_MAPROOT 0x00000001
#define OP_MAPALL 0x00000002
#define OP_KERB 0x00000004
#define OP_MASK 0x00000008
#define OP_NET 0x00000010
#define OP_ISO 0x00000020
#define OP_ALLDIRS 0x00000040
#define OP_READONLY 0x00000080
#define OP_32BITCLIENTS 0x00000100
#define OP_DEFEXP 0x40000000
#define OP_EXOPTMASK 0x000000C7
#define MAXHOSTNAMES 10
char *our_hostnames[MAXHOSTNAMES];
static char urlprefix[] = "nfs://";
static char slpprefix[] = "service:x-file-service:nfs://";
static int maxprefix = (sizeof(slpprefix) > sizeof(urlprefix) ?
sizeof(slpprefix) : sizeof(urlprefix));
static char URLRegistrar[] = _PATH_SLP_REG;
#define ADD_URL 1
#define DELETE_URL 0
int debug = 0;
void SYSLOG(int, const char *, ...);
#define log SYSLOG
int
main(int argc, char *argv[])
{
SVCXPRT *udptransp, *tcptransp;
int c, i;
while ((c = getopt(argc, argv, "Ddfnr")) != EOF) {
switch (c) {
case 'D':
debug = 1;
break;
case 'd':
debug = 2;
fprintf(stderr, "Debug Enabled. Logging to stderr.\n");
break;
case 'f':
source = EXPORT_FROM_FILE;
break;
case 'n':
resvport_only = 0;
break;
case 'r':
dir_only = 0;
break;
default:
fprintf(stderr, "Usage: mountd [-d] [-r] [-n] [-f] [export_file]\n");
exit(1);
}
}
argc -= optind;
argv += optind;
TAILQ_INIT(&xfshead);
mlhead = NULL;
grphead = NULL;
TAILQ_INIT(&ulhead);
for (i = 0; i < MAXHOSTNAMES; ++i)
our_hostnames[i] = NULL;
if (argc == 1) {
source = EXPORT_FROM_FILE;
strncpy(exportsfilepath, *argv, MAXPATHLEN-1);
exportsfilepath[MAXPATHLEN-1] = '\0';
} else
strcpy(exportsfilepath, _PATH_EXPORTS);
openlog("mountd", LOG_PID, LOG_DAEMON);
if (debug < 2)
log(LOG_DEBUG, "Debug Logging Enabled.");
uuidlist_restore();
log(LOG_DEBUG, "Getting export list.");
get_exportlist();
log(LOG_DEBUG, "Getting mount list.");
get_mountlist();
log(LOG_DEBUG, "Here we go.");
if (debug < 2) {
daemon(0, 0);
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
}
signal(SIGHUP, sigmux);
signal(SIGTERM, sigmux);
{ FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
if (pidfile != NULL) {
fprintf(pidfile, "%d\n", getpid());
fclose(pidfile);
}
}
if ((udptransp = svcudp_create(RPC_ANYSOCK)) == NULL ||
(tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) {
log(LOG_ERR, "Can't create socket");
exit(1);
}
pmap_unset(RPCPROG_MNT, 1);
pmap_unset(RPCPROG_MNT, 3);
if (!svc_register(udptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_UDP) ||
!svc_register(udptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_UDP) ||
!svc_register(tcptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_TCP) ||
!svc_register(tcptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_TCP)) {
log(LOG_ERR, "Can't register mount");
exit(1);
}
my_svc_run();
log(LOG_ERR, "Mountd died");
exit(1);
}
volatile static int gothup;
volatile static int gotterm;
void
sigmux(int sig)
{
switch (sig) {
case SIGHUP:
gothup = 1;
break;
case SIGTERM:
gotterm = 1;
break;
}
}
void
my_svc_run(void)
{
fd_set readfdset;
static int tsize = 0;
int x;
if (tsize == 0)
tsize = getdtablesize();
for ( ; ; ) {
bcopy(&svc_fdset, &readfdset, sizeof(svc_fdset));
x = select(tsize, &readfdset, NULL, NULL, NULL);
if (x > 0) {
svc_getreqset(&readfdset);
}
while (gothup && !gotterm) {
gothup = 0;
get_exportlist();
}
if (gotterm) {
gotterm = 0;
exit(0);
}
}
}
void
mntsrv( struct svc_req *rqstp,
SVCXPRT *transp)
{
struct expfs *xf;
struct fhreturn fhr;
struct stat stb;
struct statfs fsb;
struct hostent *hp;
u_long saddr;
u_short sport;
char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN], *mhost;
int bad = ENOENT, options;
sigset_t sighup_mask;
u_char uuid[16];
sigemptyset(&sighup_mask);
sigaddset(&sighup_mask, SIGHUP);
saddr = transp->xp_raddr.sin_addr.s_addr;
sport = ntohs(transp->xp_raddr.sin_port);
hp = NULL;
switch (rqstp->rq_proc) {
case NULLPROC:
if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
log(LOG_ERR, "Can't send NULL reply");
return;
case RPCMNT_MOUNT:
if (sport >= IPPORT_RESERVED && resvport_only) {
svcerr_weakauth(transp);
return;
}
if (!svc_getargs(transp, xdr_dir, rpcpath)) {
svcerr_decode(transp);
return;
}
if (realpath(rpcpath, dirpath) == 0 ||
stat(dirpath, &stb) < 0 ||
(!S_ISDIR(stb.st_mode) &&
(dir_only || !S_ISREG(stb.st_mode))) ||
statfs(dirpath, &fsb) < 0) {
chdir("/");
log(LOG_DEBUG, "stat failed on %s", dirpath);
if (!svc_sendreply(transp, (xdrproc_t)xdr_long, (caddr_t)&bad))
log(LOG_ERR, "Can't send reply for failed mount");
return;
}
if (!get_uuid_from_list(&fsb, uuid, UL_CHECK_ALL)) {
log(LOG_DEBUG, "no exported volume uuid for %s", dirpath);
if (!svc_sendreply(transp, (xdrproc_t)xdr_long, (caddr_t)&bad))
log(LOG_ERR, "Can't send reply for failed mount");
return;
}
sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
xf = ex_search(uuid);
if (xf && expdir_search(xf, dirpath, saddr, &options)) {
fhr.fhr_flag = options;
fhr.fhr_vers = rqstp->rq_vers;
memset(&fhr.fhr_fh, 0, sizeof(fhandle_t));
if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
log(LOG_DEBUG, "Can't get fh for %s: %s (%d)", dirpath,
strerror(errno), errno);
bad = EACCES;
if (!svc_sendreply(transp, (xdrproc_t)xdr_long, (caddr_t)&bad))
log(LOG_ERR, "Can't send reply for failed mount");
sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
return;
}
if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs, (caddr_t)&fhr))
log(LOG_ERR, "Can't send mount reply");
hp = gethostbyaddr((caddr_t)&transp->xp_raddr.sin_addr,
sizeof(transp->xp_raddr.sin_addr), AF_INET);
mhost = hp ? hp->h_name : inet_ntoa(transp->xp_raddr.sin_addr);
add_mlist(mhost, dirpath);
log(LOG_DEBUG, "Mount successful: %s %s", mhost, dirpath);
} else {
bad = EACCES;
if (debug) {
mhost = inet_ntoa(transp->xp_raddr.sin_addr);
log(LOG_DEBUG, "Mount failed: %s %s", mhost, dirpath);
}
if (!svc_sendreply(transp, (xdrproc_t)xdr_long, (caddr_t)&bad))
log(LOG_ERR, "Can't send reply for failed mount");
}
sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
return;
case RPCMNT_DUMP:
if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
log(LOG_ERR, "Can't send dump reply");
return;
case RPCMNT_UMOUNT:
if (sport >= IPPORT_RESERVED && resvport_only) {
svcerr_weakauth(transp);
return;
}
if (!svc_getargs(transp, xdr_dir, dirpath)) {
svcerr_decode(transp);
return;
}
if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
log(LOG_ERR, "Can't send umount reply");
hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
if (hp)
del_mlist(hp->h_name, dirpath);
del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), dirpath);
return;
case RPCMNT_UMNTALL:
if (sport >= IPPORT_RESERVED && resvport_only) {
svcerr_weakauth(transp);
return;
}
if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
log(LOG_ERR, "Can't send umntall reply");
hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
if (hp)
del_mlist(hp->h_name, (char *)NULL);
del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), (char *)NULL);
return;
case RPCMNT_EXPORT:
if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
log(LOG_ERR, "Can't send export reply");
return;
default:
svcerr_noproc(transp);
return;
}
}
int
xdr_dir(xdrsp, dirp)
XDR *xdrsp;
char *dirp;
{
return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
}
int
xdr_fhs(xdrsp, cp)
XDR *xdrsp;
caddr_t cp;
{
register struct fhreturn *fhrp = (struct fhreturn *)cp;
long ok = 0, len, auth;
if (!xdr_long(xdrsp, &ok))
return (0);
switch (fhrp->fhr_vers) {
case 1:
return (xdr_opaque(xdrsp, (caddr_t)fhrp->fhr_fh.fh_data, NFSX_V2FH));
case 3:
len = fhrp->fhr_fh.fh_len;
if (!xdr_long(xdrsp, &len))
return (0);
if (!xdr_opaque(xdrsp, (caddr_t)fhrp->fhr_fh.fh_data, fhrp->fhr_fh.fh_len))
return (0);
if (fhrp->fhr_flag & OP_KERB)
auth = RPCAUTH_KERB4;
else
auth = RPCAUTH_UNIX;
len = 1;
if (!xdr_long(xdrsp, &len))
return (0);
return (xdr_long(xdrsp, &auth));
};
return (0);
}
int
xdr_mlist(xdrsp, cp)
XDR *xdrsp;
caddr_t cp;
{
struct mountlist *mlp;
int trueval = 1;
int falseval = 0;
mlp = mlhead;
while (mlp) {
if (!xdr_bool(xdrsp, &trueval))
return (0);
if (!xdr_string(xdrsp, &mlp->ml_host, RPCMNT_NAMELEN))
return (0);
if (!xdr_string(xdrsp, &mlp->ml_dir, RPCMNT_PATHLEN))
return (0);
mlp = mlp->ml_next;
}
if (!xdr_bool(xdrsp, &falseval))
return (0);
return (1);
}
int
xdr_explist(xdrsp, cp)
XDR *xdrsp;
caddr_t cp;
{
struct expfs *xf;
struct expdir *xd;
int falseval = 0;
sigset_t sighup_mask;
sigemptyset(&sighup_mask);
sigaddset(&sighup_mask, SIGHUP);
sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
TAILQ_FOREACH_REVERSE(xf, &xfshead, xf_next, expfstqh) {
TAILQ_FOREACH_REVERSE(xd, &xf->xf_dirl, xd_next, expdirtqh) {
if (put_exlist(xd, xdrsp))
goto errout;
}
}
sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
if (!xdr_bool(xdrsp, &falseval))
return (0);
return (1);
errout:
sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
return (0);
}
int
put_exlist(struct expdir *xd, XDR *xdrsp)
{
struct expdir *mxd;
struct grouplist *grp;
struct host *hp;
int trueval = 1;
int falseval = 0;
char *strp;
if (!xd)
return (0);
if (!xdr_bool(xdrsp, &trueval))
return (1);
strp = xd->xd_dir;
if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
return (1);
if (!(xd->xd_flags & OP_DEFEXP)) {
hp = xd->xd_hosts;
while (hp) {
grp = hp->ht_grp;
if (grp->gr_type == GT_HOST) {
if (!xdr_bool(xdrsp, &trueval))
return (1);
strp = grp->gr_u.gt_hostent->h_name;
if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
return (1);
} else if (grp->gr_type == GT_NET) {
if (!xdr_bool(xdrsp, &trueval))
return (1);
strp = grp->gr_u.gt_net.nt_name;
if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
return (1);
}
hp = hp->ht_next;
}
}
if (!xdr_bool(xdrsp, &falseval))
return (1);
TAILQ_FOREACH(mxd, &xd->xd_mountdirs, xd_next) {
if (put_exlist(mxd, xdrsp))
return (1);
}
return (0);
}
void
get_rtinfo(int addrs,
struct sockaddr *sa,
struct sockaddr **rti_info)
{
register int i;
#define ROUNDUP(a, size) (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
#define NEXT_SA(ap) ap = (struct sockaddr *) \
((caddr_t) ap + (ap->sa_len ? \
ROUNDUP(ap->sa_len, sizeof (u_long)) : \
sizeof(u_long)))
for (i = 0; i < RTAX_MAX; i++)
if (addrs & (1 << i)) {
rti_info[i] = sa;
NEXT_SA(sa);
} else
rti_info[i] = NULL;
}
char *
get_ifinfo(int family, int index, int *buflen)
{
int mib[6];
char *buf;
mib[0] = CTL_NET;
mib[1] = AF_ROUTE;
mib[2] = 0;
mib[3] = family;
mib[4] = NET_RT_IFLIST;
mib[5] = index;
if (sysctl(mib, 6, NULL, (size_t *)buflen, NULL, 0) == -1) {
log(LOG_ERR,
"Error requesting network interface info size: errno = %d",
errno);
return (NULL);
}
buf = (char *)malloc(*buflen);
if (buf == NULL) {
log(LOG_ERR,
"Error allocating %d bytes for network interface into",
buflen);
return (NULL);
}
if (sysctl(mib, 6, buf, (size_t *)buflen, NULL, 0) == -1) {
free(buf);
log(LOG_ERR,
"Error requesting network interface info: errno = %d",
errno);
return (NULL);
}
return (buf);
}
void
record_hostname(const char *hostname, char **hostnamearray)
{
int i, hostnamelen;
hostnamelen = strlen(hostname);
if (hostnamelen > MAXHOSTNAMELEN)
return;
for (i = 0; i < MAXHOSTNAMES && hostnamearray[i]; i++)
if (!strcmp(hostnamearray[i], hostname))
return;
if (i >= MAXHOSTNAMES)
return;
hostnamearray[i] = malloc(hostnamelen + 1);
if (hostnamearray[i] == NULL) {
log(LOG_ERR, "record_hostname: couldn't allocate memory for hostname '%s'.",
hostname);
return;
}
strcpy(hostnamearray[i], hostname);
log(LOG_DEBUG, "record_hostname: hostnamearray[%d] = '%s'.",
i, hostnamearray[i]);
}
void
get_hostnames(char **hostnamearray)
{
char *ifinfo;
int ifinfo_len;
int i;
struct if_msghdr *ifm;
struct ifa_msghdr *ifam;
struct sockaddr *sa, *rti_info[RTAX_MAX];
int if_flags = 0;
struct hostent *hostinfo;
struct sockaddr_in *sain;
ifinfo = get_ifinfo(AF_INET, 0, &ifinfo_len);
if (ifinfo == NULL) {
log(LOG_ERR, "get_hostnames: no ifinfo");
return;
}
for (i = 0; (i < MAXHOSTNAMES) && (hostnamearray[i] != NULL); ++i) {
free(hostnamearray[i]);
hostnamearray[i] = NULL;
}
for (ifm = (struct if_msghdr *)ifinfo; (char *)ifm < ifinfo+ifinfo_len;
ifm = (struct if_msghdr *)((char *)ifm + ifm->ifm_msglen)) {
if (ifm->ifm_type == RTM_IFINFO) {
if_flags = ifm->ifm_flags;
continue;
}
if (ifm->ifm_type != RTM_NEWADDR || (if_flags & IFF_LOOPBACK))
continue;
ifam = (struct ifa_msghdr *)ifm;
sa = (struct sockaddr *)(ifam+1);
get_rtinfo(ifam->ifam_addrs, sa, rti_info);
sa = rti_info[RTAX_IFA];
if (!sa)
continue;
if (sa->sa_family != AF_INET) {
log(LOG_DEBUG, "unknown socket address family %d.",
sa->sa_family);
continue;
}
sain = (struct sockaddr_in *)sa;
hostinfo = gethostbyaddr((caddr_t)&sain->sin_addr,
sizeof(sain->sin_addr), AF_INET);
if (hostinfo) {
record_hostname(hostinfo->h_name, hostnamearray);
} else {
record_hostname(inet_ntoa(sain->sin_addr), hostnamearray);
};
}
free(ifinfo);
}
char *
clean_white_space(char *line)
{
int len, esc;
char c, *p, *s;
if (line == NULL)
return NULL;
len = strlen(line);
s = malloc(len + 1);
if (s == NULL)
return NULL;
len = 0;
esc = 0;
c = '\0';
p = line;
if (*p == '\'' || *p == '"') {
c = *p;
p++;
}
for (;*p != '\0'; p++) {
if (esc == 1) {
s[len++] = *p;
esc = 0;
} else if (*p == c)
break;
else if (*p == '\\')
esc = 1;
else if (c == '\0' && (*p == ' ' || *p == '\t'))
break;
else s[len++] = *p;
}
s[len] = '\0';
return (s);
}
int
safe_exec(char *pgm, char *arg1, char *arg2)
{
int pid;
union wait status;
pid = fork();
if (pid == 0) {
(void) execl(pgm, pgm, arg1, arg2, NULL);
log(LOG_ERR, "Exec of %s failed: %s (%d)", pgm,
strerror(errno), errno);
exit(2);
}
if (pid == -1) {
log(LOG_ERR, "Fork for %s failed: %s (%d)", pgm,
strerror(errno), errno);
exit(2);
}
if (wait4(pid, (int *)&status, 0, NULL) != pid) {
log(LOG_ERR, "BUG executing %s", pgm);
exit(2);
}
if (!WIFEXITED(status)) {
log(LOG_ERR, "%s aborted by signal %d", pgm, WTERMSIG(status));
exit(2);
}
return (WEXITSTATUS(status));
}
#define URLNOTFOUND 253
int
register_export(const char *path, char * const * hostnamearray, int addurl)
{
int i;
int urlstringlength = maxprefix + MAXHOSTNAMELEN + strlen(path);
char *urlstring;
int rv, result = 0;
urlstring = malloc(urlstringlength);
if (urlstring == NULL) {
log(LOG_ERR, "can't allocate url string to register export: %s",
path);
return (ENOMEM);
}
for (i = 0; (i < MAXHOSTNAMES) && (hostnamearray[i] != NULL); ++i) {
if (urlstringlength < maxprefix + strlen(hostnamearray[i]) +
strlen(path)) {
log(LOG_ERR, "huge hostname (ignored): %s",
hostnamearray[i]);
result = ENAMETOOLONG;
continue;
}
strcpy(urlstring, urlprefix);
strcat(urlstring, hostnamearray[i]);
strcat(urlstring, path);
log(LOG_DEBUG, "%sregistering URL %s",
(addurl ? "" : "un"), urlstring);
rv = safe_exec(URLRegistrar, (addurl ? "-r" : "-d"), urlstring);
if (rv && (addurl || rv != URLNOTFOUND)) {
log(LOG_ERR, "%s exit status %d", URLRegistrar, rv);
result = rv;
}
strcpy(urlstring, slpprefix);
strcat(urlstring, hostnamearray[i]);
strcat(urlstring, path);
log(LOG_DEBUG, "%sregistering URL %s",
(addurl ? "" : "un"), urlstring);
rv = safe_exec(URLRegistrar, (addurl ? "-r" : "-d"), urlstring);
if (rv && (addurl || rv != URLNOTFOUND)) {
log(LOG_ERR, "%s exit status %d", URLRegistrar, rv);
result = rv;
}
}
free(urlstring);
return (result);
}
int
get_uuid_from_diskarb(const char *path, u_char *uuid)
{
DASessionRef session;
DADiskRef disk;
CFDictionaryRef dd;
CFTypeRef val;
CFUUIDBytes uuidbytes;
int rv = 1;
session = NULL;
disk = NULL;
dd = NULL;
session = DASessionCreate(NULL);
if (!session) {
log(LOG_ERR, "can't create DiskArb session");
rv = 0;
goto out;
}
disk = DADiskCreateFromBSDName(NULL, session, path);
if (!disk) {
log(LOG_DEBUG, "DADiskCreateFromBSDName(%s) failed", path);
rv = 0;
goto out;
}
dd = DADiskCopyDescription(disk);
if (!dd) {
log(LOG_DEBUG, "DADiskCopyDescription(%s) failed", path);
rv = 0;
goto out;
}
if (!CFDictionaryGetValueIfPresent(dd, (kDADiskDescriptionVolumeUUIDKey), &val)) {
log(LOG_DEBUG, "unable to get UUID for volume %s", path);
rv = 0;
goto out;
}
uuidbytes = CFUUIDGetUUIDBytes(val);
bcopy(&uuidbytes, uuid, sizeof(uuidbytes));
out:
if (session) CFRelease(session);
if (disk) CFRelease(disk);
if (dd) CFRelease(dd);
return (rv);
}
struct uuidlist *
get_uuid_from_list(const struct statfs *fsb, u_char *uuid, const int flags)
{
struct uuidlist *ulp;
if (!(flags & UL_CHECK_ALL))
return (NULL);
TAILQ_FOREACH(ulp, &ulhead, ul_list) {
if ((flags & UL_CHECK_MNTFROM) &&
strcmp(fsb->f_mntfromname, ulp->ul_mntfromname))
continue;
if ((flags & UL_CHECK_MNTON) &&
strcmp(fsb->f_mntonname, ulp->ul_mntonname))
continue;
if (uuid)
bcopy(&ulp->ul_uuid, uuid, sizeof(ulp->ul_uuid));
break;
}
return (ulp);
}
struct uuidlist *
find_uuid(u_char *uuid)
{
struct uuidlist *ulp;
TAILQ_FOREACH(ulp, &ulhead, ul_list) {
if (!bcmp(ulp->ul_uuid, uuid, sizeof(ulp->ul_uuid)))
break;
}
return (ulp);
}
struct uuidlist *
find_uuid_by_fsid(u_int32_t fsid)
{
struct uuidlist *ulp;
TAILQ_FOREACH(ulp, &ulhead, ul_list) {
if (ulp->ul_fsid == fsid)
break;
}
return (ulp);
}
struct uuidlist *
add_uuid_to_list(const struct statfs *fsb, u_char *dauuid, u_char *uuid)
{
struct uuidlist *ulpnew;
u_int32_t xfsid;
ulpnew = malloc(sizeof(struct uuidlist));
if (!ulpnew) {
log(LOG_ERR, "add_uuid_to_list: out of memory");
return (NULL);
}
bzero(ulpnew, sizeof(*ulpnew));
LIST_INIT(&ulpnew->ul_exportids);
if (dauuid) {
bcopy(dauuid, ulpnew->ul_dauuid, sizeof(ulpnew->ul_dauuid));
ulpnew->ul_davalid = 1;
}
bcopy(uuid, ulpnew->ul_uuid, sizeof(ulpnew->ul_uuid));
strcpy(ulpnew->ul_mntfromname, fsb->f_mntfromname);
strcpy(ulpnew->ul_mntonname, fsb->f_mntonname);
xfsid = UUID2FSID(uuid);
ulpnew->ul_fsid = xfsid;
while (find_uuid_by_fsid(ulpnew->ul_fsid))
if (++ulpnew->ul_fsid == xfsid) {
log(LOG_ERR, "exported FS ID values exhausted, can't add %s",
ulpnew->ul_mntonname);
free(ulpnew);
return (NULL);
}
TAILQ_INSERT_TAIL(&ulhead, ulpnew, ul_list);
return (ulpnew);
}
struct uuidlist *
get_uuid(const struct statfs *fsb, u_char *uuid)
{
CFUUIDRef cfuuid;
CFUUIDBytes uuidbytes;
struct uuidlist *ulp;
u_char dauuid[16];
int davalid, uuidchanged, reportuuid = 0;
char buf[64], buf2[64];
davalid = get_uuid_from_diskarb(fsb->f_mntfromname, dauuid);
if (davalid) {
log(LOG_DEBUG, "get_uuid: %s %s DiskArb says: %s",
fsb->f_mntfromname, fsb->f_mntonname,
uuidstring(dauuid, buf));
}
if ((ulp = get_uuid_from_list(fsb, uuid, UL_CHECK_MNTON))) {
log(LOG_DEBUG, "get_uuid: %s %s found: %s",
fsb->f_mntfromname, fsb->f_mntonname,
uuidstring(uuid, buf));
if (davalid) {
if (!ulp->ul_davalid)
uuidchanged = 1;
else if (bcmp(ulp->ul_dauuid, dauuid, sizeof(dauuid)))
uuidchanged = 1;
else
uuidchanged = 0;
} else {
if (ulp->ul_davalid) {
uuidstring(ulp->ul_dauuid, buf);
log(LOG_WARNING, "lost UUID for %s, was %s, keeping old UUID",
fsb->f_mntonname, buf);
uuidchanged = 0;
} else
uuidchanged = 0;
}
if (uuidchanged) {
uuidstring(ulp->ul_dauuid, buf);
if (davalid)
uuidstring(dauuid, buf2);
else
strcpy(buf2, "------------------------------------");
if (ulp->ul_exported) {
log(LOG_WARNING, "ignoring UUID change for already exported file system %s, was %s now %s",
fsb->f_mntonname, buf, buf2);
uuidchanged = 0;
}
}
if (uuidchanged) {
log(LOG_WARNING, "UUID changed for %s, was %s now %s",
fsb->f_mntonname, buf, buf2);
bcopy(dauuid, uuid, sizeof(dauuid));
TAILQ_REMOVE(&ulhead, ulp, ul_list);
free(ulp);
ulp = NULL;
} else {
ulp->ul_exported = 1;
}
} else if (davalid) {
bcopy(dauuid, uuid, sizeof(dauuid));
} else {
reportuuid = 1;
cfuuid = CFUUIDCreate(NULL);
uuidbytes = CFUUIDGetUUIDBytes(cfuuid);
bcopy(&uuidbytes, uuid, sizeof(uuidbytes));
CFRelease(cfuuid);
}
if (!ulp) {
while ((ulp = find_uuid(uuid))) {
reportuuid = 1;
uuidstring(uuid, buf);
log(LOG_WARNING, "%s UUID conflict with %s, %s",
fsb->f_mntonname, ulp->ul_mntonname, buf);
cfuuid = CFUUIDCreate(NULL);
uuidbytes = CFUUIDGetUUIDBytes(cfuuid);
bcopy(&uuidbytes, uuid, sizeof(uuidbytes));
CFRelease(cfuuid);
}
ulp = add_uuid_to_list(fsb, (davalid ? dauuid : NULL), uuid);
if (!ulp) {
log(LOG_ERR, "error adding %s", fsb->f_mntonname);
} else {
ulp->ul_exported = 1;
}
} else if (!ulp->ul_mntfromname[0]) {
strcpy(ulp->ul_mntfromname, fsb->f_mntfromname);
}
if (reportuuid)
log(LOG_WARNING, "%s using UUID %s", fsb->f_mntonname, uuidstring(uuid, buf));
else
log(LOG_DEBUG, "%s using UUID %s", fsb->f_mntonname, uuidstring(uuid, buf));
return (ulp);
}
void
uuidlist_clearexport(void)
{
struct uuidlist *ulp;
TAILQ_FOREACH(ulp, &ulhead, ul_list) {
ulp->ul_exported = 0;
}
}
#define HEXTOC(c) \
((c) >= 'a' ? ((c) - ('a' - 10)) : \
((c) >= 'A' ? ((c) - ('A' - 10)) : ((c) - '0')))
#define HEXSTRTOI(p) \
((HEXTOC(p[0]) << 4) + HEXTOC(p[1]))
char *
uuidstring(u_char *uuid, char *string)
{
sprintf(string, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
uuid[0] & 0xff, uuid[1] & 0xff, uuid[2] & 0xff, uuid[3] & 0xff,
uuid[4] & 0xff, uuid[5] & 0xff,
uuid[6] & 0xff, uuid[7] & 0xff,
uuid[8] & 0xff, uuid[9] & 0xff,
uuid[10] & 0xff, uuid[11] & 0xff, uuid[12] & 0xff,
uuid[13] & 0xff, uuid[14] & 0xff, uuid[15] & 0xff);
return (string);
}
void
uuidlist_save(void)
{
FILE *ulfile;
struct uuidlist *ulp;
struct expidlist *xid;
char buf[64], buf2[64];
if ((ulfile = fopen(_PATH_MOUNTEXPLIST, "w")) == NULL) {
log(LOG_ERR, "Can't write %s: %s (%d)", _PATH_MOUNTEXPLIST,
strerror(errno), errno);
return;
}
TAILQ_FOREACH(ulp, &ulhead, ul_list) {
#ifdef SAVE_ONLY_EXPORTED_UUIDS
if (!ulp->ul_exported)
continue;
#endif
if (ulp->ul_davalid)
uuidstring(ulp->ul_dauuid, buf);
else
strcpy(buf, "------------------------------------");
uuidstring(ulp->ul_uuid, buf2);
fprintf(ulfile, "%s %s 0x%08X %s\n", buf, buf2, ulp->ul_fsid, ulp->ul_mntonname);
LIST_FOREACH(xid, &ulp->ul_exportids, xid_list) {
fprintf(ulfile, "XID 0x%08X %s\n", xid->xid_id,
((xid->xid_path[0] == '\0') ? "." : xid->xid_path));
}
}
fclose(ulfile);
}
void
uuidlist_restore(void)
{
struct uuidlist *ulp;
struct expidlist *xid;
char *cp, str[2*MAXPATHLEN];
FILE *ulfile;
int i, slen, linenum, davalid, uuidchanged;
struct statfs fsb;
u_char dauuid[16];
char buf[64], buf2[64];
if ((ulfile = fopen(_PATH_MOUNTEXPLIST, "r")) == NULL) {
if (errno != ENOENT)
log(LOG_NOTICE, "Can't open %s: %s (%d)", _PATH_MOUNTEXPLIST,
strerror(errno), errno);
else
log(LOG_DEBUG, "Can't open %s, %s (%d)", _PATH_MOUNTEXPLIST,
strerror(errno), errno);
return;
}
ulp = NULL;
linenum = 0;
while (fgets(str, 2*MAXPATHLEN, ulfile) != NULL) {
linenum++;
slen = strlen(str);
if (str[slen-1] == '\n')
str[slen-1] = '\0';
if (!strncmp(str, "XID ", 4)) {
if (!ulp) {
log(LOG_ERR, "ignoring orphaned export ID at line %d of %s",
linenum, _PATH_MOUNTEXPLIST);
continue;
}
xid = malloc(sizeof(*xid));
if (xid == NULL)
out_of_mem("uuidlist_restore");
cp = str + 4;
slen -= 4;
if (sscanf(cp, "%i", &xid->xid_id) != 1) {
log(LOG_ERR, "invalid export ID at line %d of %s",
linenum, _PATH_MOUNTEXPLIST);
free(xid);
continue;
}
while (*cp && (*cp != ' ')) {
cp++;
slen--;
}
cp++;
slen--;
if (slen >= sizeof(xid->xid_path)) {
log(LOG_ERR, "export ID path too long at line %d of %s",
linenum, _PATH_MOUNTEXPLIST);
free(xid);
continue;
}
if ((cp[0] == '.') && (cp[1] == '\0'))
xid->xid_path[0] = '\0';
else
strcpy(xid->xid_path, cp);
LIST_INSERT_HEAD(&ulp->ul_exportids, xid, xid_list);
continue;
}
ulp = malloc(sizeof(*ulp));
if (ulp == NULL)
out_of_mem("uuidlist_restore");
bzero(ulp, sizeof(*ulp));
LIST_INIT(&ulp->ul_exportids);
cp = str;
if (*cp == '-') {
ulp->ul_davalid = 0;
bzero(ulp->ul_dauuid, sizeof(ulp->ul_dauuid));
while (*cp && (*cp != ' '))
cp++;
} else {
ulp->ul_davalid = 1;
for (i=0; i < sizeof(ulp->ul_dauuid); i++, cp+=2) {
if (*cp == '-')
cp++;
if (!isxdigit(*cp) || !isxdigit(*(cp+1))) {
log(LOG_ERR, "invalid UUID at line %d of %s",
linenum, _PATH_MOUNTEXPLIST);
free(ulp);
ulp = NULL;
break;
}
ulp->ul_dauuid[i] = HEXSTRTOI(cp);
}
}
if (ulp == NULL)
continue;
cp++;
for (i=0; i < sizeof(ulp->ul_uuid); i++, cp+=2) {
if (*cp == '-')
cp++;
if (!isxdigit(*cp) || !isxdigit(*(cp+1))) {
log(LOG_ERR, "invalid UUID at line %d of %s",
linenum, _PATH_MOUNTEXPLIST);
free(ulp);
ulp = NULL;
break;
}
ulp->ul_uuid[i] = HEXSTRTOI(cp);
}
if (ulp == NULL)
continue;
if (*cp != ' ') {
log(LOG_ERR, "invalid entry at line %d of %s",
linenum, _PATH_MOUNTEXPLIST);
free(ulp);
continue;
}
cp++;
if (sscanf(cp, "%i", &ulp->ul_fsid) != 1) {
log(LOG_ERR, "invalid entry at line %d of %s",
linenum, _PATH_MOUNTEXPLIST);
free(ulp);
continue;
}
while (*cp && (*cp != ' '))
cp++;
if (*cp != ' ') {
log(LOG_ERR, "invalid entry at line %d of %s",
linenum, _PATH_MOUNTEXPLIST);
free(ulp);
continue;
}
cp++;
strncpy(ulp->ul_mntonname, cp, MAXPATHLEN);
ulp->ul_mntonname[MAXPATHLEN-1] = '\0';
if (!check_dirpath(ulp->ul_mntonname) ||
(statfs(ulp->ul_mntonname, &fsb) < 0) ||
strcmp(ulp->ul_mntonname, fsb.f_mntonname)) {
log(LOG_DEBUG, "export entry for non-existent file system %s at line %d of %s",
ulp->ul_mntonname, linenum, _PATH_MOUNTEXPLIST);
ulp->ul_mntfromname[0] = '\0';
TAILQ_INSERT_TAIL(&ulhead, ulp, ul_list);
continue;
}
strncpy(ulp->ul_mntfromname, fsb.f_mntfromname, MAXPATHLEN);
ulp->ul_mntfromname[MAXPATHLEN-1] = '\0';
davalid = get_uuid_from_diskarb(ulp->ul_mntfromname, dauuid);
if (davalid) {
if (!ulp->ul_davalid)
uuidchanged = 1;
else if (bcmp(ulp->ul_dauuid, dauuid, sizeof(dauuid)))
uuidchanged = 1;
else
uuidchanged = 0;
} else {
if (ulp->ul_davalid) {
uuidstring(ulp->ul_dauuid, buf);
log(LOG_WARNING, "lost UUID for %s, was %s, keeping old UUID",
fsb.f_mntonname, buf);
uuidchanged = 0;
} else
uuidchanged = 0;
}
if (uuidchanged) {
uuidstring(ulp->ul_dauuid, buf);
if (davalid)
uuidstring(dauuid, buf2);
else
strcpy(buf2, "------------------------------------");
log(LOG_WARNING, "UUID changed for %s, was %s now %s",
ulp->ul_mntonname, buf, buf2);
free(ulp);
continue;
}
TAILQ_INSERT_TAIL(&ulhead, ulp, ul_list);
}
fclose(ulfile);
}
struct expidlist *
find_export_id(struct uuidlist *ulp, u_int32_t id)
{
struct expidlist *xid;
LIST_FOREACH(xid, &ulp->ul_exportids, xid_list) {
if (xid->xid_id == id)
break;
}
return (xid);
}
struct expidlist *
get_export_id(struct uuidlist *ulp, char *path)
{
struct expidlist *xid;
u_int32_t maxid = 0;
LIST_FOREACH(xid, &ulp->ul_exportids, xid_list) {
if (!strcmp(xid->xid_path, path))
break;
if (maxid < xid->xid_id)
maxid = xid->xid_id;
}
if (xid)
return (xid);
xid = malloc(sizeof(*xid));
if (!xid) {
log(LOG_ERR, "get_export_id: out of memory");
return (NULL);
}
bzero(xid, sizeof(*xid));
strcpy(xid->xid_path, path);
xid->xid_id = maxid + 1;
while (find_export_id(ulp, xid->xid_id)) {
xid->xid_id++;
if (xid->xid_id == maxid) {
log(LOG_ERR, "export ID values exhausted for %s",
ulp->ul_mntonname);
free(xid);
return (NULL);
}
}
LIST_INSERT_HEAD(&ulp->ul_exportids, xid, xid_list);
return (xid);
}
int
subdir_check(char *s1, char *s2)
{
int len1, len2, rv;
len1 = strlen(s1);
len2 = strlen(s2);
if (len1 > len2)
rv = -3;
else if (strncmp(s1, s2, len1))
rv = -2;
else if (len1 == len2)
rv = 0;
else if ((s2[len1] == '/') || (len1 == 1))
rv = 1;
else
rv = -1;
log(LOG_DEBUG, "subdir_check: %s %s %d", s1, s2, rv);
return rv;
}
#define LINESIZ 10240
char line[LINESIZ];
FILE *exp_file;
void
get_exportlist(void)
{
struct expfs *xf, *xf2, *xf3;
struct grouplist *grp, *tgrp, *pgrp;
struct expdir *xd, *xd2, *xd3;
struct dirlist *dirhead, *dirl, *dirl2;
struct dirlist *reghead, *reg;
struct statfs fsb, *fsp, *firstfsp;
struct hostent *hpe;
struct xucred anon;
char *cp, *endcp, *word, *hst, *usr, *dom, savedc, *savedcp, *s;
int len, dlen, hostcount, badhostcount, exflags, got_nondir, num, i, netgrp, cmp;
struct nfs_export_args nxa;
u_char uuid[16];
struct uuidlist *ulp;
char buf[64];
int error;
int opt_flags;
log(LOG_DEBUG, "get_exportlist: freeing old exports...");
while ((xf = TAILQ_FIRST(&xfshead))) {
TAILQ_REMOVE(&xfshead, xf, xf_next);
free_expfs(xf);
}
TAILQ_INIT(&xfshead);
log(LOG_DEBUG, "get_exportlist: freeing old groups...");
grp = grphead;
while (grp) {
tgrp = grp;
grp = grp->gr_next;
free_grp(tgrp);
}
grphead = NULL;
get_hostnames(our_hostnames);
num = getmntinfo(&firstfsp, MNT_NOWAIT);
for (i = 0, fsp = firstfsp; i < num; i++, fsp++)
register_export(fsp->f_mntonname, our_hostnames, DELETE_URL);
nxa.nxa_flags = NXA_DELETE_ALL;
nxa.nxa_expid = 0;
nxa.nxa_exppath = NULL;
nxa.nxa_netcount = 0;
nxa.nxa_nets = NULL;
nxa.nxa_fsid = 0;
nxa.nxa_fspath = NULL;
error = nfssvc(NFSSVC_EXPORT, &nxa);
if (error && (errno != ENOENT))
log(LOG_ERR, "Can't delete all exports, %d", errno);
uuidlist_clearexport();
if ((exp_file = fopen(exportsfilepath, "r")) != NULL) {
source = EXPORT_FROM_FILE;
} else {
if (source == EXPORT_FROM_FILE) {
log(LOG_ERR, "Can't open %s", exportsfilepath);
exit(2);
}
ni_exports_open();
source = EXPORT_FROM_NETINFO;
}
dirhead = NULL;
reghead = NULL;
while (get_line(source)) {
log(LOG_DEBUG, "---> Got line: %s", line);
hostcount = badhostcount = 0;
anon = def_anon;
exflags = 0;
opt_flags = 0;
got_nondir = 0;
xf = NULL;
ulp = NULL;
pgrp = NULL;
tgrp = grp = get_grp();
if (!tgrp) {
log(LOG_ERR, "can't allocate initial group for export");
getexp_err(xf, tgrp);
goto nextline;
}
cp = line;
nextfield(&cp, &endcp);
if (*cp == '#')
goto nextline;
while (endcp > cp) {
log(LOG_DEBUG, "got field: %.*s", endcp-cp, cp);
if (*cp == '-') {
if (xf == NULL) {
log(LOG_ERR, "got options with no exported directory: %s", cp);
getexp_err(xf, tgrp);
goto nextline;
}
log(LOG_DEBUG, "processing option: %.*s", endcp-cp, cp);
got_nondir = 1;
savedcp = cp;
if (do_opt(&cp, &endcp, grp, &hostcount, &opt_flags,
&exflags, &anon)) {
log(LOG_ERR, "error processing options: %s", savedcp);
getexp_err(xf, tgrp);
goto nextline;
}
} else if ((*cp == '/') ||
((*cp == '\'' || *cp == '"') && (*(cp+1) == '/'))) {
log(LOG_DEBUG, "processing pathname: %.*s", endcp-cp, cp);
word = clean_white_space(cp);
log(LOG_DEBUG, " cleaned pathname: %s", word);
if (word == NULL) {
log(LOG_ERR, "error processing pathname (out of memory)");
getexp_err(xf, tgrp);
goto nextline;
}
if (strlen(word) > RPCMNT_NAMELEN) {
log(LOG_ERR, "pathname too long (%d > %d): %s",
strlen(word), RPCMNT_NAMELEN, word);
getexp_err(xf, tgrp);
free(word);
goto nextline;
}
if (!check_dirpath(word)) {
log(LOG_ERR, "path contains symlinks: %s", word);
getexp_err(xf, tgrp);
free(word);
goto nextline;
}
if (statfs(word, &fsb) < 0) {
log(LOG_ERR, "statfs failed (%s) for path: %s",
strerror(errno), word);
getexp_err(xf, tgrp);
free(word);
goto nextline;
}
if (got_nondir) {
log(LOG_ERR, "Directories must be first: %s", word);
getexp_err(xf, tgrp);
free(word);
goto nextline;
}
ulp = get_uuid(&fsb, uuid);
if (!ulp) {
log(LOG_ERR, "couldn't get UUID for volume: %s", fsb.f_mntonname);
getexp_err(xf, tgrp);
free(word);
goto nextline;
}
if (xf) {
if (bcmp(xf->xf_uuid, uuid, sizeof(uuid))) {
log(LOG_ERR, "UUID mismatch for: %s\ndirectories must be on same volume", word);
getexp_err(xf, tgrp);
free(word);
goto nextline;
}
} else {
xf = ex_search(uuid);
if (xf == NULL) {
xf = get_expfs();
if (xf)
xf->xf_fsdir = malloc(strlen(fsb.f_mntonname) + 1);
if (!xf || !xf->xf_fsdir) {
log(LOG_ERR, "Can't allocate memory to export volume: %s",
fsb.f_mntonname);
getexp_err(xf, tgrp);
free(word);
goto nextline;
}
bcopy(uuid, xf->xf_uuid, sizeof(uuid));
strcpy(xf->xf_fsdir, fsb.f_mntonname);
log(LOG_DEBUG, "New expfs uuid=%s", uuidstring(uuid, buf));
} else {
log(LOG_DEBUG, "Found expfs uuid=%s", uuidstring(uuid, buf));
}
}
error = add_dir(&dirhead, word);
if (error == EEXIST) {
log(LOG_WARNING, "duplicate directory: %s", word);
free(word);
} else if (error == ENOMEM) {
log(LOG_ERR, "Can't allocate memory to add directory: %s", word);
getexp_err(xf, tgrp);
free(word);
goto nextline;
}
} else {
savedc = *endcp;
*endcp = '\0';
log(LOG_DEBUG, "got host/netgroup: %s", cp);
got_nondir = 1;
if (xf == NULL) {
log(LOG_ERR, "got host/group with no directory?: %s", cp);
getexp_err(xf, tgrp);
goto nextline;
}
setnetgrent(cp);
netgrp = getnetgrent(&hst, &usr, &dom);
do {
if (hostcount && (grp->gr_type != GT_NULL)) {
grp->gr_next = get_grp();
if (!grp->gr_next) {
log(LOG_ERR, "can't allocate next group for export");
getexp_err(xf, tgrp);
goto nextline;
}
pgrp = grp;
grp = grp->gr_next;
}
if (netgrp) {
if (get_host_addresses(hst, grp)) {
log(LOG_ERR, "couldn't get address for netgroup:host - %s:%s", cp, hst);
badhostcount++;
} else {
log(LOG_DEBUG, "got netgroup:host: %s:%s", cp, hst);
hostcount++;
}
} else if (get_host_addresses(cp, grp)) {
log(LOG_ERR, "couldn't get address for host: %s", cp);
badhostcount++;
} else {
log(LOG_DEBUG, "got host: %s", cp);
hostcount++;
}
} while (netgrp && getnetgrent(&hst, &usr, &dom));
endnetgrent();
*endcp = savedc;
}
cp = endcp;
nextfield(&cp, &endcp);
}
if (!(opt_flags & (OP_MAPROOT|OP_MAPALL|OP_KERB))) {
exflags |= NX_MAPROOT;
opt_flags |= OP_MAPROOT;
}
if (check_options(opt_flags)) {
log(LOG_ERR, "bad export options");
getexp_err(xf, tgrp);
goto nextline;
}
if (!hostcount) {
if (badhostcount) {
log(LOG_ERR, "no valid hosts found for export");
getexp_err(xf, tgrp);
goto nextline;
}
grp->gr_type = GT_HOST;
log(LOG_DEBUG, "Adding a default entry");
hpe = malloc(sizeof(struct hostent));
if (hpe)
hpe->h_name = malloc(sizeof(DEFAULTHOSTNAME));
if (!hpe || !hpe->h_name) {
if (hpe)
free(hpe);
log(LOG_ERR, "Can't allocate memory for default export entry %s",
xf->xf_fsdir);
getexp_err(xf, tgrp);
goto nextline;
}
strcpy(hpe->h_name, DEFAULTHOSTNAME);
hpe->h_addrtype = AF_INET;
hpe->h_length = sizeof (u_long);
hpe->h_addr_list = (char **)NULL;
grp->gr_u.gt_hostent = hpe;
} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
log(LOG_ERR, "can't specify both network and hosts on same line");
getexp_err(xf, tgrp);
goto nextline;
} else if (badhostcount && pgrp && (grp->gr_type == GT_NULL)) {
pgrp->gr_next = NULL;
free_grp(grp);
grp = pgrp;
}
dirl = dirhead;
while (dirl) {
log(LOG_DEBUG, "dir: %s", dirl->dl_dir);
TAILQ_FOREACH(xd2, &xf->xf_dirl, xd_next) {
if ((subdir_check(xd2->xd_dir, dirl->dl_dir) == 1) ||
(subdir_check(dirl->dl_dir, xd2->xd_dir) == 1)) {
log(LOG_ERR, "%s conflicts with existing export %s",
dirl->dl_dir, xd2->xd_dir);
getexp_err(xf, tgrp);
goto nextline;
}
}
s = dirl->dl_dir;
len = strlen(s);
xd3 = NULL;
cmp = 1;
TAILQ_FOREACH(xd2, &xf->xf_dirl, xd_next) {
dlen = strlen(xd2->xd_dir);
cmp = strncmp(dirl->dl_dir, xd2->xd_dir, dlen);
log(LOG_DEBUG, " %s compare %s %d", dirl->dl_dir, xd2->xd_dir, cmp);
if (!cmp) {
if (len == dlen)
break;
cmp = 1;
}
if (cmp > 0)
break;
xd3 = xd2;
}
if (cmp != 0) {
xd = get_expdir();
if (xd)
xd->xd_dir = strdup(dirl->dl_dir);
if (!xd || !xd->xd_dir) {
if (xd)
free_expdir(xd);
log(LOG_ERR, "can't allocate memory for export %s", dirl->dl_dir);
getexp_err(xf, tgrp);
goto nextline;
}
log(LOG_DEBUG, " %s new xd", xd->xd_dir);
} else {
xd = xd2;
log(LOG_DEBUG, " %s xd is %s", dirl->dl_dir, xd->xd_dir);
if (!hostcount && (xd->xd_flags & OP_DEFEXP)) {
log(LOG_ERR, "%s already has a default export", s);
getexp_err(xf, tgrp);
goto nextline;
}
}
if (do_export(xf, tgrp, exflags, &anon, dirl->dl_dir, &fsb, ulp)) {
log(LOG_ERR, "kernel export registration failed");
getexp_err(xf, tgrp);
goto nextline;
}
log(LOG_DEBUG, "kernel export registered for %s", dirl->dl_dir);
if (hang_options(xd, opt_flags, (hostcount ? tgrp : NULL))) {
log(LOG_ERR, "export option conflict for %s", xd->xd_dir);
getexp_err(xf, tgrp);
goto nextline;
}
dirl2 = dirl->dl_next;
while (dirl2) {
if (subdir_check(dirl->dl_dir, dirl2->dl_dir) != 1)
break;
error = hang_options_mountdir(xd, dirl2->dl_dir, opt_flags,
(hostcount ? tgrp : NULL));
if (error == EEXIST) {
log(LOG_WARNING, "mount subdirectory export option conflict for %s",
dirl2->dl_dir);
} else if (error == ENOMEM) {
log(LOG_WARNING, "unable to add mount subdirectory for %s, %s",
xd->xd_dir, dirl2->dl_dir);
}
dirl2 = dirl2->dl_next;
}
dirl = dirl2;
if (cmp) {
if (xd3) {
TAILQ_INSERT_AFTER(&xf->xf_dirl, xd3, xd, xd_next);
} else {
TAILQ_INSERT_HEAD(&xf->xf_dirl, xd, xd_next);
}
s = strdup(xd->xd_dir);
if (add_dir(®head, s))
free(s);
}
}
if (hostcount) {
grp->gr_next = grphead;
grphead = tgrp;
} else {
free_grp(grp);
}
if ((xf->xf_flag & XF_LINKED) == 0) {
xf3 = NULL;
TAILQ_FOREACH(xf2, &xfshead, xf_next) {
if (strcmp(xf->xf_fsdir, xf2->xf_fsdir) < 0)
break;
xf3 = xf2;
}
if (xf3) {
TAILQ_INSERT_AFTER(&xfshead, xf3, xf, xf_next);
} else {
TAILQ_INSERT_HEAD(&xfshead, xf, xf_next);
}
xf->xf_flag |= XF_LINKED;
}
nextline:
if (dirhead) {
free_dirlist(dirhead);
dirhead = NULL;
}
}
reg = reghead;
while (reg) {
register_export(reg->dl_dir, our_hostnames, ADD_URL);
reg = reg->dl_next;
}
free_dirlist(reghead);
reghead = NULL;
if (source == EXPORT_FROM_NETINFO) ni_exports_close();
else fclose(exp_file);
uuidlist_save();
}
struct expfs *
get_expfs(void)
{
struct expfs *xf;
xf = malloc(sizeof(*xf));
if (xf == NULL)
return (NULL);
memset(xf, 0, sizeof(*xf));
TAILQ_INIT(&xf->xf_dirl);
return (xf);
}
struct expdir *
get_expdir(void)
{
struct expdir *xd;
xd = malloc(sizeof(*xd));
if (xd == NULL)
return (NULL);
memset(xd, 0, sizeof(*xd));
TAILQ_INIT(&xd->xd_mountdirs);
return (xd);
}
struct grouplist *
get_grp(void)
{
struct grouplist *grp;
grp = malloc(sizeof(*grp));
if (grp == NULL)
return (NULL);
memset(grp, 0, sizeof(*grp));
return (grp);
}
void
getexp_err(struct expfs *xf, struct grouplist *grp)
{
struct grouplist *tgrp;
log(LOG_ERR, "Error processing exports list line: %s", line);
if (xf && (xf->xf_flag & XF_LINKED) == 0)
free_expfs(xf);
while (grp) {
tgrp = grp;
grp = grp->gr_next;
free_grp(tgrp);
}
}
struct expfs *
ex_search(u_char *uuid)
{
struct expfs *xf;
TAILQ_FOREACH(xf, &xfshead, xf_next) {
if (!bcmp(xf->xf_uuid, uuid, 16))
return (xf);
}
return (xf);
}
int
add_dir(struct dirlist **dlpp, char *cp)
{
struct dirlist *newdl, *dl, *dl2, *dl3, *dlstop;
int cplen, dlen, cmp;
dlstop = NULL;
dl2 = NULL;
dl = *dlpp;
cplen = strlen(cp);
while (dl && (dl != dlstop)) {
dlen = strlen(dl->dl_dir);
cmp = strncmp(cp, dl->dl_dir, dlen);
log(LOG_DEBUG, "add_dir: %s compare %s %d", cp, dl->dl_dir, cmp);
if (cmp < 0)
break;
if (cmp == 0) {
if (cplen == dlen) return (EEXIST);
if (cp[dlen] == '/') {
log(LOG_DEBUG, "add_dir: %s compare %s %d subdir match",
cp, dl->dl_dir, cmp);
dlstop = dl->dl_next;
while (dlstop && (subdir_check(dl->dl_dir, dlstop->dl_dir) == 1))
dlstop = dlstop->dl_next;
} else {
log(LOG_DEBUG, "add_dir: %s compare %s %d partial match", cp, dl->dl_dir, cmp);
dl3 = dl;
dl2 = dl;
dl = dl->dl_next;
while (dl && (subdir_check(dl3->dl_dir, dl->dl_dir) == 1)) {
dl2 = dl;
dl = dl->dl_next;
}
continue;
}
}
dl2 = dl;
dl = dl->dl_next;
}
if (dl && (dl == dlstop))
log(LOG_DEBUG, "add_dir: %s stopped before %s", cp, dlstop->dl_dir);
newdl = malloc(sizeof(*dl));
if (newdl == NULL) {
log(LOG_ERR, "can't allocate memory to add dir %s", cp);
return (ENOMEM);
}
newdl->dl_dir = cp;
if (dl2 == NULL) {
newdl->dl_next = *dlpp;
*dlpp = newdl;
} else {
newdl->dl_next = dl;
dl2->dl_next = newdl;
}
if (debug > 1) {
dl = *dlpp;
while (dl) {
log(LOG_DEBUG, "DIRLIST: %s", dl->dl_dir);
dl = dl->dl_next;
}
}
return (0);
}
void
free_dirlist(struct dirlist *dl)
{
struct dirlist *dl2;
while (dl) {
dl2 = dl->dl_next;
if (dl->dl_dir)
free(dl->dl_dir);
free(dl);
dl = dl2;
}
}
int
hang_options(struct expdir *xd, int opt_flags, struct grouplist *grp)
{
struct host *ht;
if (!grp) {
if (xd->xd_flags & OP_DEFEXP) {
if ((xd->xd_flags & OP_EXOPTMASK) == opt_flags) {
log(LOG_WARNING, "duplicate default export for %s", xd->xd_dir);
return (0);
} else {
log(LOG_ERR, "multiple/conflicting default exports for %s", xd->xd_dir);
return (EEXIST);
}
}
xd->xd_flags = opt_flags | OP_DEFEXP;
log(LOG_DEBUG, "hang_options: %s default 0x%x", xd->xd_dir, xd->xd_flags);
return (0);
}
while (grp) {
ht = get_host();
if (!ht) {
log(LOG_ERR, "Can't allocate memory for host: %s",
((grp->gr_type == GT_NET) ? grp->gr_u.gt_net.nt_name :
((grp->gr_type == GT_HOST) ? grp->gr_u.gt_hostent->h_name : "???")));
return(ENOMEM);
}
ht->ht_flag = opt_flags;
ht->ht_grp = grp;
ht->ht_next = xd->xd_hosts;
xd->xd_hosts = ht;
log(LOG_DEBUG, "hang_options: %s %s 0x%x", xd->xd_dir,
((grp->gr_type == GT_NET) ? grp->gr_u.gt_net.nt_name :
((grp->gr_type == GT_HOST) ? grp->gr_u.gt_hostent->h_name : "???")),
opt_flags);
grp = grp->gr_next;
}
return (0);
}
int
hang_options_mountdir(struct expdir *xd, char *dir, int opt_flags, struct grouplist *grp)
{
struct host *ht;
struct expdir *mxd, *mxd2, *mxd3;
int cmp;
mxd = mxd3 = NULL;
TAILQ_FOREACH(mxd2, &xd->xd_mountdirs, xd_next) {
cmp = strcmp(dir, mxd2->xd_dir);
if (!cmp) {
mxd = mxd2;
break;
} else if (cmp < 0) {
break;
}
mxd3 = mxd2;
}
if (!mxd) {
mxd = get_expdir();
if (mxd)
mxd->xd_dir = strdup(dir);
if (!mxd || !mxd->xd_dir) {
if (mxd)
free_expdir(mxd);
log(LOG_ERR, "can't allocate memory for mountable sub-directory; %s", dir);
return (ENOMEM);
}
if (mxd3) {
TAILQ_INSERT_AFTER(&xd->xd_mountdirs, mxd3, mxd, xd_next);
} else {
TAILQ_INSERT_HEAD(&xd->xd_mountdirs, mxd, xd_next);
}
}
if (!grp) {
if (mxd->xd_flags & OP_DEFEXP) {
if ((mxd->xd_flags & OP_EXOPTMASK) == opt_flags) {
log(LOG_WARNING, "duplicate default export for %s", mxd->xd_dir);
return (0);
} else {
log(LOG_ERR, "multiple/conflicting default exports for %s", mxd->xd_dir);
return (EEXIST);
}
}
mxd->xd_flags = opt_flags | OP_DEFEXP;
log(LOG_DEBUG, "hang_options_mountdir: %s default 0x%x", mxd->xd_dir, mxd->xd_flags);
return (0);
}
while (grp) {
ht = get_host();
if (!ht) {
log(LOG_ERR, "Can't allocate memory for host: %s",
((grp->gr_type == GT_NET) ? grp->gr_u.gt_net.nt_name :
((grp->gr_type == GT_HOST) ? grp->gr_u.gt_hostent->h_name : "???")));
return(ENOMEM);
}
ht->ht_flag = opt_flags;
ht->ht_grp = grp;
ht->ht_next = mxd->xd_hosts;
mxd->xd_hosts = ht;
log(LOG_DEBUG, "hang_options_mountdir: %s %s 0x%x", mxd->xd_dir,
((grp->gr_type == GT_NET) ? grp->gr_u.gt_net.nt_name :
((grp->gr_type == GT_HOST) ? grp->gr_u.gt_hostent->h_name : "???")),
opt_flags);
grp = grp->gr_next;
}
return (0);
}
int
expdir_search(struct expfs *xf, char *dirpath, u_long saddr, int *options)
{
struct expdir *xd, *mxd;
struct host *hp;
int cmp = -1, chkalldirs = 0;
TAILQ_FOREACH(xd, &xf->xf_dirl, xd_next) {
if ((cmp = subdir_check(xd->xd_dir, dirpath)) >= 0)
break;
}
if (!xd) {
log(LOG_DEBUG, "expdir_search: no matching export: %s", dirpath);
return (0);
}
log(LOG_DEBUG, "expdir_search: %s -> %s", dirpath, xd->xd_dir);
if (cmp == 0) {
check_xd_hosts:
hp = find_host(xd->xd_hosts, saddr);
if (hp && (!chkalldirs || (hp->ht_flag & OP_ALLDIRS))) {
log(LOG_DEBUG, "expdir_search: %s host %s", dirpath, (chkalldirs ? "alldirs" : "match"));
*options = hp->ht_flag;
} else if ((xd->xd_flags & OP_DEFEXP) && (!chkalldirs || (xd->xd_flags & OP_ALLDIRS))) {
log(LOG_DEBUG, "expdir_search: %s defexp %s", dirpath, (chkalldirs ? "alldirs" : "match"));
*options = xd->xd_flags;
} else {
*options = 0;
log(LOG_DEBUG, "expdir_search: %s NO match", dirpath);
return (0);
}
return (1);
}
TAILQ_FOREACH(mxd, &xd->xd_mountdirs, xd_next) {
cmp = subdir_check(mxd->xd_dir, dirpath);
if (cmp < 0)
continue;
log(LOG_DEBUG, "expdir_search: %s subdir path match %s", dirpath, mxd->xd_dir);
chkalldirs = (cmp != 0);
hp = find_host(mxd->xd_hosts, saddr);
if (hp && (!chkalldirs || (hp->ht_flag & OP_ALLDIRS))) {
log(LOG_DEBUG, "expdir_search: %s -> %s subdir host %s", dirpath, mxd->xd_dir, (chkalldirs ? "alldirs" : "match"));
*options = hp->ht_flag;
return (1);
} else if ((mxd->xd_flags & OP_DEFEXP) && (!chkalldirs || (mxd->xd_flags & OP_ALLDIRS))) {
log(LOG_DEBUG, "expdir_search: %s -> %s subdir defexp %s", dirpath, mxd->xd_dir, (chkalldirs ? "alldirs" : "match"));
*options = mxd->xd_flags;
return (1);
}
}
log(LOG_DEBUG, "expdir_search: %s NO match, check alldirs", dirpath);
chkalldirs = 1;
goto check_xd_hosts;
}
struct host *
find_host(struct host *hp, u_long saddr)
{
struct grouplist *grp;
u_long **addrp;
while (hp) {
grp = hp->ht_grp;
switch (grp->gr_type) {
case GT_HOST:
addrp = (u_long **) grp->gr_u.gt_hostent->h_addr_list;
while (*addrp) {
if (**addrp == saddr)
return (hp);
addrp++;
}
break;
case GT_NET:
if ((saddr & grp->gr_u.gt_net.nt_mask) ==
grp->gr_u.gt_net.nt_net)
return (hp);
break;
}
hp = hp->ht_next;
}
return (NULL);
}
int
do_opt( char **cpp,
char **endcpp,
struct grouplist *grp,
int *hostcountp,
int *opt_flagsp,
int *exflagsp,
struct xucred *cr)
{
char *cpoptarg, *cpoptend;
char *cp, *endcp, *cpopt, savedc, savedc2 = '\0';
int allflag, usedarg;
cpopt = *cpp;
cpopt++;
cp = *endcpp;
savedc = *cp;
*cp = '\0';
while (cpopt && *cpopt) {
allflag = 1;
usedarg = -2;
if (NULL != (cpoptend = strchr(cpopt, ','))) {
*cpoptend++ = '\0';
if (NULL != (cpoptarg = strchr(cpopt, '=')))
*cpoptarg++ = '\0';
} else {
if (NULL != (cpoptarg = strchr(cpopt, '=')))
*cpoptarg++ = '\0';
else {
*cp = savedc;
nextfield(&cp, &endcp);
**endcpp = '\0';
if (endcp > cp && *cp != '-') {
cpoptarg = cp;
savedc2 = *endcp;
*endcp = '\0';
usedarg = 0;
}
}
}
if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
*exflagsp |= NX_READONLY;
*opt_flagsp |= OP_READONLY;
} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
!(allflag = strcmp(cpopt, "mapall")) ||
!strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
usedarg++;
parsecred(cpoptarg, cr);
if (allflag == 0) {
*exflagsp |= NX_MAPALL;
*opt_flagsp |= OP_MAPALL;
} else {
*exflagsp |= NX_MAPROOT;
*opt_flagsp |= OP_MAPROOT;
}
} else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) {
*exflagsp |= NX_KERB;
*opt_flagsp |= OP_KERB;
} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
!strcmp(cpopt, "m"))) {
if (get_net(cpoptarg, &grp->gr_u.gt_net, 1)) {
log(LOG_ERR, "Bad mask: %s", cpoptarg);
return (1);
}
usedarg++;
*opt_flagsp |= OP_MASK;
} else if (cpoptarg && (!strcmp(cpopt, "network") ||
!strcmp(cpopt, "n"))) {
if (grp->gr_type != GT_NULL) {
log(LOG_ERR, "Network/host conflict");
return (1);
} else if (get_net(cpoptarg, &grp->gr_u.gt_net, 0)) {
log(LOG_ERR, "Bad net: %s", cpoptarg);
return (1);
}
grp->gr_type = GT_NET;
*hostcountp = *hostcountp + 1;
usedarg++;
*opt_flagsp |= OP_NET;
} else if (!strcmp(cpopt, "alldirs")) {
*opt_flagsp |= OP_ALLDIRS;
#ifdef ISO
} else if (cpoptarg && !strcmp(cpopt, "iso")) {
if (get_isoaddr(cpoptarg, grp)) {
log(LOG_ERR, "Bad iso addr: %s", cpoptarg);
return (1);
}
*hostcountp = *hostcountp + 1;
usedarg++;
*opt_flagsp |= OP_ISO;
#endif
} else if (!strcmp(cpopt, "32bitclients")) {
*exflagsp |= NX_32BITCLIENTS;
*opt_flagsp |= OP_32BITCLIENTS;
} else {
log(LOG_ERR, "Bad opt %s", cpopt);
return (1);
}
if (usedarg >= 0) {
*endcp = savedc2;
**endcpp = savedc;
if (usedarg > 0) {
*cpp = cp;
*endcpp = endcp;
}
return (0);
}
cpopt = cpoptend;
}
**endcpp = savedc;
return (0);
}
int
get_host_addresses(char *cp, struct grouplist *grp)
{
struct hostent *hp, *nhp;
char **addrp, **naddrp;
struct hostent t_host;
int i;
u_long saddr;
char *aptr[2];
if (grp->gr_type != GT_NULL)
return (1);
if ((hp = gethostbyname(cp)) == NULL) {
if (!isdigit(*cp)) {
log(LOG_ERR, "Gethostbyname failed for %s", cp);
return (1);
}
saddr = inet_addr(cp);
if (saddr == -1) {
log(LOG_ERR, "Inet_addr failed for %s", cp);
return (1);
}
if ((hp = gethostbyaddr((caddr_t)&saddr, sizeof (saddr),
AF_INET)) == NULL) {
hp = &t_host;
hp->h_name = cp;
hp->h_addrtype = AF_INET;
hp->h_length = sizeof (u_long);
hp->h_addr_list = aptr;
aptr[0] = (char *)&saddr;
aptr[1] = (char *)NULL;
}
}
nhp = malloc(sizeof(struct hostent));
if (nhp == NULL)
goto nomem;
memmove(nhp, hp, sizeof(struct hostent));
i = strlen(hp->h_name)+1;
nhp->h_name = malloc(i);
if (nhp->h_name == NULL)
goto nomem;
memmove(nhp->h_name, hp->h_name, i);
addrp = hp->h_addr_list;
i = 1;
while (*addrp++)
i++;
naddrp = nhp->h_addr_list = malloc(i*sizeof(char *));
if (naddrp == NULL)
goto nomem;
bzero(naddrp, i*sizeof(char *));
addrp = hp->h_addr_list;
while (*addrp) {
*naddrp = malloc(hp->h_length);
if (*naddrp == NULL)
goto nomem;
memmove(*naddrp, *addrp, hp->h_length);
addrp++;
naddrp++;
}
*naddrp = NULL;
grp->gr_type = GT_HOST;
grp->gr_u.gt_hostent = nhp;
return (0);
nomem:
if (nhp) {
if (nhp->h_name) {
naddrp = nhp->h_addr_list;
while (naddrp && *naddrp)
free(*naddrp++);
if (nhp->h_addr_list)
free(nhp->h_addr_list);
free(nhp->h_name);
}
free(nhp);
}
log(LOG_ERR, "can't allocate memory for host address(es) for %s", cp);
return (1);
}
void
free_expdir(struct expdir *xd)
{
struct expdir *xd2;
free_host(xd->xd_hosts);
while ((xd2 = TAILQ_FIRST(&xd->xd_mountdirs))) {
TAILQ_REMOVE(&xd->xd_mountdirs, xd2, xd_next);
free_expdir(xd2);
}
if (xd->xd_dir)
free(xd->xd_dir);
free(xd);
}
void
free_expfs(struct expfs *xf)
{
struct expdir *xd;
while ((xd = TAILQ_FIRST(&xf->xf_dirl))) {
TAILQ_REMOVE(&xf->xf_dirl, xd, xd_next);
free_expdir(xd);
}
if (xf->xf_fsdir)
free(xf->xf_fsdir);
free(xf);
}
void
free_host(struct host *hp)
{
struct host *hp2;
while (hp) {
hp2 = hp;
hp = hp->ht_next;
free((caddr_t)hp2);
}
}
struct host *
get_host(void)
{
struct host *hp;
hp = malloc(sizeof(struct host));
if (hp == NULL)
return (NULL);
hp->ht_next = NULL;
hp->ht_flag = 0;
return (hp);
}
#ifdef ISO
int
get_isoaddr(char *cp, struct grouplist *grp)
{
struct iso_addr *isop;
struct sockaddr_iso *isoaddr;
if (grp->gr_type != GT_NULL)
return (1);
if ((isop = iso_addr(cp)) == NULL) {
log(LOG_ERR, "iso_addr failed, ignored");
return (1);
}
isoaddr = malloc(sizeof (struct sockaddr_iso));
if (isoaddr == NULL) {
log(LOG_ERR, "can't allocate memory for ISO address: %s", cp);
return (1);
}
memset(isoaddr, 0, sizeof(struct sockaddr_iso));
memmove(&isoaddr->siso_addr, isop, sizeof(struct iso_addr));
isoaddr->siso_len = sizeof(struct sockaddr_iso);
isoaddr->siso_family = AF_ISO;
grp->gr_type = GT_ISO;
grp->gr_u.gt_isoaddr = isoaddr;
return (0);
}
#endif
void
out_of_mem(char *msg)
{
log(LOG_ERR, "%s: Out of memory", msg);
exit(2);
}
int
do_export(
struct expfs *xf,
struct grouplist *grplist,
int exflags,
struct xucred *anoncrp,
char *dir,
struct statfs *fsb,
struct uuidlist *ulp)
{
struct nfs_export_args nxa;
struct nfs_export_net_args *netargs, *na;
struct expidlist *xid;
struct grouplist *grp;
u_long **addrp;
struct sockaddr_in *sin, *imask;
u_long net;
char *subdir;
nxa.nxa_flags = NXA_ADD;
nxa.nxa_fsid = ulp->ul_fsid;
nxa.nxa_fspath = fsb->f_mntonname;
if (strncmp(dir, fsb->f_mntonname, strlen(fsb->f_mntonname)))
log(LOG_WARNING, "do_export(): exported dir/fs mismatch: %s %s",
dir, fsb->f_mntonname);
subdir = dir + strlen(fsb->f_mntonname);
while (*subdir && (*subdir == '/'))
subdir++;
xid = get_export_id(ulp, (u_char *)subdir);
if (!xid) {
log(LOG_ERR, "do_export(): unable to get export ID for %s", dir);
return (1);
}
nxa.nxa_exppath = xid->xid_path;
nxa.nxa_expid = xid->xid_id;
nxa.nxa_netcount = 0;
grp = grplist;
while (grp) {
if (grp->gr_type == GT_HOST) {
addrp = (u_long **)grp->gr_u.gt_hostent->h_addr_list;
if (!addrp)
nxa.nxa_netcount++;
while (addrp && *addrp) {
nxa.nxa_netcount++;
addrp++;
}
} else {
nxa.nxa_netcount++;
}
grp = grp->gr_next;
}
netargs = malloc(nxa.nxa_netcount * sizeof(struct nfs_export_net_args));
if (!netargs) {
log(LOG_ERR, "do_export(): malloc failed for %d net args", nxa.nxa_netcount);
return (1);
}
nxa.nxa_nets = netargs;
#define INIT_NETARG(N) \
do { \
(N)->nxna_flags = exflags; \
(N)->nxna_cred = *anoncrp; \
sin = (struct sockaddr_in*)&(N)->nxna_addr; \
imask = (struct sockaddr_in*)&(N)->nxna_mask; \
memset(sin, 0, sizeof(*sin)); \
memset(imask, 0, sizeof(*imask)); \
sin->sin_family = AF_INET; \
sin->sin_len = sizeof(*sin); \
imask->sin_family = AF_INET; \
imask->sin_len = sizeof(*imask); \
} while (0)
na = netargs;
grp = grplist;
while (grp) {
switch (grp->gr_type) {
case GT_HOST:
addrp = (u_long **)grp->gr_u.gt_hostent->h_addr_list;
if (!addrp) {
INIT_NETARG(na);
sin->sin_len = 0;
imask->sin_len = 0;
na++;
break;
}
while (*addrp) {
INIT_NETARG(na);
sin->sin_addr.s_addr = **addrp;
imask->sin_len = 0;
addrp++;
na++;
}
break;
case GT_NET:
INIT_NETARG(na);
if (grp->gr_u.gt_net.nt_mask)
imask->sin_addr.s_addr = grp->gr_u.gt_net.nt_mask;
else {
net = ntohl(grp->gr_u.gt_net.nt_net);
if (IN_CLASSA(net))
imask->sin_addr.s_addr = inet_addr("255.0.0.0");
else if (IN_CLASSB(net))
imask->sin_addr.s_addr =
inet_addr("255.255.0.0");
else
imask->sin_addr.s_addr =
inet_addr("255.255.255.0");
grp->gr_u.gt_net.nt_mask = imask->sin_addr.s_addr;
}
sin->sin_addr.s_addr = grp->gr_u.gt_net.nt_net;
na++;
break;
#ifdef ISO
case GT_ISO:
INIT_NETARG(na);
*((struct sockaddr_iso *)sin) = *grp->gr_u.gt_isoaddr;
imask->sin_len = 0;
na++;
break;
#endif
default:
log(LOG_ERR, "Bad grouptype");
free(netargs);
return (1);
}
grp = grp->gr_next;
}
if (nfssvc(NFSSVC_EXPORT, &nxa)) {
if (errno == EPERM) {
log(LOG_ERR, "Can't change attributes for %s. See 'exports' man page.", dir);
free(netargs);
return (1);
}
log(LOG_ERR, "Can't export %s: %s (%d)", dir, strerror(errno), errno);
free(netargs);
return (1);
}
free(netargs);
return (0);
}
int
get_net(char *cp, struct netmsk *net, int maskflg)
{
struct netent *np;
long netaddr;
struct in_addr inetaddr, inetaddr2;
char *name;
if (NULL != (np = getnetbyname(cp)))
inetaddr = inet_makeaddr(np->n_net, 0);
else if (isdigit(*cp)) {
if ((netaddr = inet_network(cp)) == -1)
return (1);
inetaddr = inet_makeaddr(netaddr, 0);
if (!maskflg) {
setnetent(0);
while (NULL != (np = getnetent())) {
inetaddr2 = inet_makeaddr(np->n_net, 0);
if (inetaddr2.s_addr == inetaddr.s_addr)
break;
}
endnetent();
}
} else
return (1);
if (maskflg)
net->nt_mask = inetaddr.s_addr;
else {
if (np)
name = np->n_name;
else
name = inet_ntoa(inetaddr);
net->nt_name = malloc(strlen(name) + 1);
if (net->nt_name == NULL) {
log(LOG_ERR, "can't allocate memory for net: %s", cp);
return (1);
}
strcpy(net->nt_name, name);
net->nt_net = inetaddr.s_addr;
}
return (0);
}
void
nextfield(char **line_start, char **line_end)
{
char *a, q;
if (line_start == NULL)
return;
a = *line_start;
while (*a == ' ' || *a == '\t')
a++;
*line_start = a;
if (*a == '\n' || *a == '\0') {
*line_end = a;
return;
}
if (*a == '\'' || *a == '"') {
q = *a;
a++;
while (*a != q && *a != '\0' && *a != '\n')
a++;
if (*a == q)
a++;
*line_end = a;
return;
}
for (;; a++) {
if (*a == '\0' || *a == '\n')
break;
else if (*a == '\\') {
a++;
if (*a == '\n' || *a == '\0')
break;
} else if (*a == ' ' || *a == '\t')
break;
}
*line_end = a;
}
int
get_line(int source)
{
if (source == EXPORT_FROM_NETINFO)
return ni_get_line();
return file_get_line();
}
static ni_idlist ni_exports_list;
static int ni_exports_index;
static void *ni;
void
ni_exports_open(void)
{
int status;
ni_id dir;
status = ni_open(NULL, ".", &ni);
if (status != NI_OK)
log(LOG_ERR, "NetInfo open failed: %s", ni_error(status));
NI_INIT(&ni_exports_list);
ni_exports_index = 0;
status = ni_pathsearch(ni, &dir, "/exports");
if (status == NI_NODIR)
return;
if (status != NI_OK) {
log(LOG_ERR, "NetInfo error searching for /exports: %s",
ni_error(status));
return;
}
status = ni_children(ni, &dir, &ni_exports_list);
if (status != NI_OK) {
log(LOG_ERR, "NetInfo error reading /exports: %s",
ni_error(status));
return;
}
}
void
ni_exports_close(void)
{
ni_free(ni);
ni_idlist_free(&ni_exports_list);
ni_exports_index = 0;
}
int
ni_get_line(void)
{
ni_id dir;
ni_proplist pl;
ni_namelist *nl;
ni_index where;
int status, i;
while (1) {
line[0] = '\0';
if (ni_exports_index >= ni_exports_list.ni_idlist_len)
return (0);
dir.nii_object = ni_exports_list.ni_idlist_val[ni_exports_index++];
status = ni_read(ni, &dir, &pl);
if (status != NI_OK) {
log(LOG_ERR,
"NetInfo error reading /exports/dir:%lu: %s",
dir.nii_object, ni_error(status));
continue;
}
where = ni_proplist_match(pl, "name", NULL);
if (where == NI_INDEX_NULL) {
log(LOG_ERR,
"NetInfo directory /exports/dir:%lu has no name",
dir.nii_object);
continue;
}
if (pl.ni_proplist_val[where].nip_val.ni_namelist_len == 0) {
log(LOG_ERR,
"NetInfo directory /exports/dir:%lu has no name",
dir.nii_object);
continue;
}
nl = &pl.ni_proplist_val[where].nip_val;
for (i = 0; i < nl->ni_namelist_len; i++) {
strcat(line, nl->ni_namelist_val[i]);
if (i + 1 < nl->ni_namelist_len)
strcat(line, " ");
}
where = ni_proplist_match(pl, "opts", NULL);
if (where != NI_INDEX_NULL &&
pl.ni_proplist_val[where].nip_val.ni_namelist_len > 0) {
nl = &pl.ni_proplist_val[where].nip_val;
for (i = 0; i < nl->ni_namelist_len; i++) {
strcat(line, " -");
strcat(line, nl->ni_namelist_val[i]);
}
}
where = ni_proplist_match(pl, "clients", NULL);
if (where != NI_INDEX_NULL &&
pl.ni_proplist_val[where].nip_val.ni_namelist_len > 0) {
nl = &pl.ni_proplist_val[where].nip_val;
for (i = 0; i < nl->ni_namelist_len; i++) {
strcat(line, " ");
strcat(line, nl->ni_namelist_val[i]);
}
}
break;
}
return (1);
}
int
file_get_line(void)
{
char *p, *cp;
int len;
int totlen, cont_line;
p = line;
totlen = 0;
do {
if (fgets(p, LINESIZ - totlen, exp_file) == NULL)
return (0);
len = strlen(p);
cp = p + len - 1;
cont_line = 0;
while (cp >= p &&
(*cp == ' ' || *cp == '\t' || *cp == '\n' ||
*cp == '\\')) {
if (*cp == '\\')
cont_line = 1;
cp--;
len--;
}
*++cp = '\0';
if (len > 0) {
totlen += len;
if (totlen >= LINESIZ) {
log(LOG_ERR, "Exports line too long");
exit(2);
}
p = cp;
}
} while (totlen == 0 || cont_line);
return (1);
}
void
parsecred(char *namelist, struct xucred *cr)
{
char *name;
int cnt;
char *names;
struct passwd *pw;
struct group *gr;
int ngroups, groups[NGROUPS + 1];
cr->cr_version = XUCRED_VERSION;
cr->cr_uid = -2;
cr->cr_groups[0] = -2;
cr->cr_ngroups = 1;
names = strsep(&namelist, " \t\n");
name = strsep(&names, ":");
if (isdigit(*name) || *name == '-')
pw = getpwuid(atoi(name));
else
pw = getpwnam(name);
if (names == NULL) {
if (pw == NULL) {
log(LOG_ERR, "Unknown user: %s", name);
return;
}
cr->cr_uid = pw->pw_uid;
ngroups = NGROUPS + 1;
if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
log(LOG_ERR, "Too many groups");
cr->cr_ngroups = ngroups - 1;
cr->cr_groups[0] = groups[0];
for (cnt = 2; cnt < ngroups; cnt++)
cr->cr_groups[cnt - 1] = groups[cnt];
return;
}
if (pw != NULL)
cr->cr_uid = pw->pw_uid;
else if (isdigit(*name) || *name == '-')
cr->cr_uid = atoi(name);
else {
log(LOG_ERR, "Unknown user: %s", name);
return;
}
cr->cr_ngroups = 0;
while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
name = strsep(&names, ":");
if (isdigit(*name) || *name == '-') {
cr->cr_groups[cr->cr_ngroups++] = atoi(name);
} else {
if ((gr = getgrnam(name)) == NULL) {
log(LOG_ERR, "Unknown group: %s", name);
continue;
}
cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
}
}
if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
log(LOG_ERR, "Too many groups");
}
#define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
void
get_mountlist(void)
{
struct mountlist *mlp, *lastmlp;
char *host, *dir, *cp;
char str[STRSIZ];
FILE *mlfile;
int hlen, dlen;
if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
if (errno != ENOENT)
log(LOG_ERR, "Can't open %s: %s (%d)",
_PATH_RMOUNTLIST, strerror(errno), errno);
else
log(LOG_DEBUG, "Can't open %s: %s (%d)",
_PATH_RMOUNTLIST, strerror(errno), errno);
return;
}
lastmlp = NULL;
while (fgets(str, STRSIZ, mlfile) != NULL) {
cp = str;
host = strsep(&cp, " \t\n");
dir = strsep(&cp, " \t\n");
if ((host == NULL) || (dir == NULL))
continue;
hlen = strlen(host);
if (hlen > RPCMNT_NAMELEN)
hlen = RPCMNT_NAMELEN;
dlen = strlen(dir);
if (dlen > RPCMNT_PATHLEN)
dlen = RPCMNT_PATHLEN;
mlp = malloc(sizeof(*mlp));
if (mlp) {
mlp->ml_host = malloc(hlen+1);
mlp->ml_dir = malloc(dlen+1);
}
if (!mlp || !mlp->ml_host || !mlp->ml_dir) {
log(LOG_ERR, "can't allocate memory while reading in mount list: %s %s",
host, dir);
if (mlp) {
if (mlp->ml_host)
free(mlp->ml_host);
if (mlp->ml_dir)
free(mlp->ml_dir);
free(mlp);
}
break;
}
strncpy(mlp->ml_host, host, hlen);
mlp->ml_host[hlen] = '\0';
strncpy(mlp->ml_dir, dir, dlen);
mlp->ml_dir[dlen] = '\0';
mlp->ml_next = NULL;
if (lastmlp)
lastmlp->ml_next = mlp;
else
mlhead = mlp;
lastmlp = mlp;
}
fclose(mlfile);
}
void
del_mlist(char *host, char *dir)
{
struct mountlist *mlp, **mlpp;
struct mountlist *mlp2;
FILE *mlfile;
int fnd = 0;
mlpp = &mlhead;
mlp = mlhead;
while (mlp) {
if (!strcmp(mlp->ml_host, host) &&
(!dir || !strcmp(mlp->ml_dir, dir))) {
fnd = 1;
mlp2 = mlp;
*mlpp = mlp = mlp->ml_next;
free(mlp2->ml_host);
free(mlp2->ml_dir);
free(mlp2);
} else {
mlpp = &mlp->ml_next;
mlp = mlp->ml_next;
}
}
if (fnd) {
if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
log(LOG_ERR, "Can't write %s: %s (%d)",
_PATH_RMOUNTLIST, strerror(errno), errno);
return;
}
mlp = mlhead;
while (mlp) {
fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dir);
mlp = mlp->ml_next;
}
fclose(mlfile);
}
}
void
add_mlist(char *host, char *dir)
{
struct mountlist *mlp, **mlpp;
FILE *mlfile;
int hlen, dlen;
mlpp = &mlhead;
mlp = mlhead;
while (mlp) {
if (!strcmp(mlp->ml_host, host) && !strcmp(mlp->ml_dir, dir))
return;
mlpp = &mlp->ml_next;
mlp = mlp->ml_next;
}
hlen = strlen(host);
if (hlen > RPCMNT_NAMELEN)
hlen = RPCMNT_NAMELEN;
dlen = strlen(dir);
if (dlen > RPCMNT_PATHLEN)
dlen = RPCMNT_PATHLEN;
mlp = malloc(sizeof(*mlp));
if (mlp) {
mlp->ml_host = malloc(hlen+1);
mlp->ml_dir = malloc(dlen+1);
}
if (!mlp || !mlp->ml_host || !mlp->ml_dir) {
if (mlp) {
if (mlp->ml_host)
free(mlp->ml_host);
if (mlp->ml_dir)
free(mlp->ml_dir);
free(mlp);
}
log(LOG_ERR, "can't allocate memory to add to mount list: %s %s", host, dir);
return;
}
strncpy(mlp->ml_host, host, hlen);
mlp->ml_host[hlen] = '\0';
strncpy(mlp->ml_dir, dir, dlen);
mlp->ml_dir[dlen] = '\0';
mlp->ml_next = NULL;
*mlpp = mlp;
if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
log(LOG_ERR, "Can't append %s: %s (%d)",
_PATH_RMOUNTLIST, strerror(errno), errno);
return;
}
fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dir);
fclose(mlfile);
}
#if 0
void
send_umntall(void)
{
clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
xdr_void, (caddr_t)0, xdr_void, (caddr_t)0,
NULL);
exit(0);
}
#endif
void
free_grp(struct grouplist *grp)
{
char **addrp;
if (grp->gr_type == GT_HOST) {
if (grp->gr_u.gt_hostent->h_name) {
addrp = grp->gr_u.gt_hostent->h_addr_list;
while (addrp && *addrp)
free(*addrp++);
if (grp->gr_u.gt_hostent->h_addr_list)
free((caddr_t)grp->gr_u.gt_hostent->h_addr_list);
free(grp->gr_u.gt_hostent->h_name);
}
free((caddr_t)grp->gr_u.gt_hostent);
} else if (grp->gr_type == GT_NET) {
if (grp->gr_u.gt_net.nt_name)
free(grp->gr_u.gt_net.nt_name);
}
#ifdef ISO
else if (grp->gr_type == GT_ISO)
free((caddr_t)grp->gr_u.gt_isoaddr);
#endif
free((caddr_t)grp);
}
void
SYSLOG(int pri, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (debug > 1) {
vfprintf(stderr, fmt, ap);
fputc('\n', stderr);
fflush(stderr);
} else if (debug || (pri != LOG_DEBUG)) {
vsyslog(pri, fmt, ap);
}
va_end(ap);
}
int
check_options(int opt_flags)
{
if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT|OP_MAPALL) ||
(opt_flags & (OP_MAPROOT | OP_KERB)) == (OP_MAPROOT | OP_KERB) ||
(opt_flags & (OP_MAPALL | OP_KERB)) == (OP_MAPALL | OP_KERB)) {
log(LOG_ERR, "-mapall, -maproot and -kerb mutually exclusive");
return (1);
}
if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
log(LOG_ERR, "-mask requires -net");
return (1);
}
if ((opt_flags & (OP_NET | OP_ISO)) == (OP_NET | OP_ISO)) {
log(LOG_ERR, "-net and -iso mutually exclusive");
return (1);
}
return (0);
}
int
check_dirpath(char *dir)
{
char *cp;
int ret = 1;
struct stat sb;
for (cp = dir + 1; *cp && ret; cp++)
if (*cp == '/') {
*cp = '\0';
if ((lstat(dir, &sb) < 0) || !S_ISDIR(sb.st_mode))
ret = 0;
*cp = '/';
}
if (ret && ((lstat(dir, &sb) < 0) || !S_ISDIR(sb.st_mode)))
ret = 0;
return (ret);
}