1/* 2 * Copyright (c) 1999-2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* 24 * Copyright (c) 1989, 1993 25 * The Regents of the University of California. All rights reserved. 26 * 27 * Redistribution and use in source and binary forms, with or without 28 * modification, are permitted provided that the following conditions 29 * are met: 30 * 1. Redistributions of source code must retain the above copyright 31 * notice, this list of conditions and the following disclaimer. 32 * 2. Redistributions in binary form must reproduce the above copyright 33 * notice, this list of conditions and the following disclaimer in the 34 * documentation and/or other materials provided with the distribution. 35 * 3. All advertising materials mentioning features or use of this software 36 * must display the following acknowledgement: 37 * This product includes software developed by the University of 38 * California, Berkeley and its contributors. 39 * 4. Neither the name of the University nor the names of its contributors 40 * may be used to endorse or promote products derived from this software 41 * without specific prior written permission. 42 * 43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 53 * SUCH DAMAGE. 54 */ 55 56#include <sys/param.h> 57#include <sys/file.h> 58#include <sys/ioctl.h> 59#include <sys/malloc.h> 60#include <sys/mount.h> 61#include <sys/socket.h> 62#include <sys/sockio.h> 63#include <sys/stat.h> 64#include <sys/syslog.h> 65#include <sys/sysctl.h> 66#include <sys/ucred.h> 67#include <sys/wait.h> 68#include <sys/queue.h> 69#include <sys/types.h> 70 71#include <oncrpc/rpc.h> 72#include <oncrpc/pmap_clnt.h> 73#include <oncrpc/pmap_prot.h> 74#include <nfs/rpcv2.h> 75#include <nfs/nfsproto.h> 76#include <nfs/nfs.h> 77 78#include <net/if.h> 79#include <net/if_types.h> 80#include <net/if_dl.h> 81#include <net/route.h> 82#include <netinet/in.h> 83#include <arpa/inet.h> 84 85int bindresvport_sa(int sd, struct sockaddr *sa); 86 87#include <ctype.h> 88#include <err.h> 89#include <errno.h> 90#include <grp.h> 91#include <netdb.h> 92#include <pwd.h> 93#include <signal.h> 94#include <stdio.h> 95#include <stdlib.h> 96#include <string.h> 97#include <unistd.h> 98#include <pthread.h> 99 100#include <CoreFoundation/CoreFoundation.h> 101#include <DiskArbitration/DiskArbitration.h> 102 103#ifdef __LP64__ 104typedef int xdr_long_t; 105#else 106typedef long xdr_long_t; 107#endif 108 109#include "pathnames.h" 110#include "common.h" 111 112/* 113 * Structure for maintaining list of export IDs for exported volumes 114 */ 115struct expidlist { 116 LIST_ENTRY(expidlist) xid_list; 117 char xid_path[MAXPATHLEN]; /* exported sub-directory */ 118 u_int32_t xid_id; /* export ID */ 119}; 120 121/* 122 * Structure for maintaining list of UUIDs for exported volumes 123 */ 124struct uuidlist { 125 TAILQ_ENTRY(uuidlist) ul_list; 126 char ul_mntfromname[MAXPATHLEN]; 127 char ul_mntonname[MAXPATHLEN]; 128 u_char ul_uuid[16]; /* UUID used */ 129 u_char ul_dauuid[16]; /* DiskArb UUID */ 130 char ul_davalid; /* DiskArb UUID valid */ 131 char ul_exported; /* currently exported? */ 132 u_int32_t ul_fsid; /* exported FS ID */ 133 LIST_HEAD(expidhead,expidlist) ul_exportids; /* export ID list */ 134}; 135TAILQ_HEAD(,uuidlist) ulhead; 136#define UL_CHECK_MNTFROM 0x1 137#define UL_CHECK_MNTON 0x2 138#define UL_CHECK_ALL 0x3 139 140#define AOK (void *) // assert alignment is OK 141 142/* 143 * Default FSID is just a "hash" of UUID 144 */ 145#define UUID2FSID(U) \ 146 (*((u_int32_t*) AOK (U)) ^ *(((u_int32_t*) AOK (U))+1) ^ \ 147 *(((u_int32_t*) AOK (U))+2) ^ *(((u_int32_t*) AOK (U))+3)) 148 149/* 150 * Structure for keeping the (outstanding) mount list 151 */ 152struct mountlist { 153 struct mountlist *ml_next; 154 char *ml_host; /* NFS client name or address */ 155 char *ml_dir; /* mounted directory */ 156}; 157 158/* 159 * Structure used to hold a list of names 160 */ 161struct namelist { 162 TAILQ_ENTRY(namelist) nl_next; 163 char *nl_name; 164}; 165TAILQ_HEAD(namelisttqh, namelist); 166 167/* 168 * Structure used to hold a list of directories 169 */ 170struct dirlist { 171 struct dirlist *dl_next; 172 char *dl_dir; 173}; 174 175/* 176 * Structure used to hold an export error message. 177 */ 178struct errlist { 179 LIST_ENTRY(errlist) el_next; 180 uint32_t el_linenum; 181 char *el_msg; 182}; 183 184/* 185 * Structures for keeping the export lists 186 */ 187 188TAILQ_HEAD(expfstqh, expfs); 189TAILQ_HEAD(expdirtqh, expdir); 190TAILQ_HEAD(hosttqh, host); 191 192/* 193 * Structure to hold the exports for each exported file system. 194 */ 195struct expfs { 196 TAILQ_ENTRY(expfs) xf_next; 197 struct expdirtqh xf_dirl; /* list of exported directories */ 198 int xf_flag; /* internal flags for this struct */ 199 u_char xf_uuid[16]; /* file system's UUID */ 200 u_int32_t xf_fsid; /* exported FS ID */ 201 char *xf_fsdir; /* mount point of this file system */ 202}; 203/* xf_flag bits */ 204#define XF_LINKED 0x1 205 206/* 207 * Structure to hold info about an exported directory 208 */ 209struct expdir { 210 TAILQ_ENTRY(expdir) xd_next; 211 struct hosttqh xd_hosts; /* List of hosts this dir exported to */ 212 struct expdirtqh xd_mountdirs; /* list of mountable sub-directories */ 213 int xd_iflags; /* internal flags for this structure */ 214 int xd_flags; /* default export flags */ 215 struct xucred xd_cred; /* default export mapped credential */ 216 struct nfs_sec xd_sec; /* default security flavors */ 217 int xd_oflags; /* old default export flags */ 218 struct xucred xd_ocred; /* old default export mapped credential */ 219 struct nfs_sec xd_osec; /* old default security flavors */ 220 struct nfs_sec xd_ssec; /* security flavors for showmount */ 221 char *xd_dir; /* pathname of exported directory */ 222 struct expidlist *xd_xid; /* corresponding export ID */ 223}; 224 225/* 226 * Structures for holding sets of exported-to hosts/nets/addresses 227 */ 228 229/* holds a host address list and name */ 230struct hostinfo { 231 char *h_name; /* host name */ 232 struct addrinfo *h_ailist; /* address list */ 233}; 234 235/* holds a network/mask and name */ 236struct netmsk { 237 char *nt_name; /* network name */ 238 sa_family_t nt_family; /* network family */ 239 union { 240 struct { 241 in_addr_t net; /* IPv4 network address */ 242 in_addr_t mask; /* IPv4 network mask */ 243 } ipv4; 244 struct { 245 struct in6_addr net; /* IPv6 network address */ 246 struct in6_addr mask; /* IPv6 network mask */ 247 } ipv6; 248 } nt_u; 249}; 250 251#define nt_net nt_u.ipv4.net 252#define nt_mask nt_u.ipv4.mask 253#define nt_net6 nt_u.ipv6.net 254#define nt_mask6 nt_u.ipv6.mask 255 256/* holds either a host or network */ 257union grouptypes { 258 struct hostinfo gt_hostinfo; 259 struct netmsk gt_net; 260 char * gt_netgroup; 261}; 262 263/* host/network list entry */ 264struct grouplist { 265 struct grouplist *gr_cache; /* linked list in cache */ 266 struct grouplist *gr_next; /* linked list in get_exportlist() */ 267 int gr_refcnt; /* #references on this group */ 268 int16_t gr_type; /* type of group */ 269 int16_t gr_flags; /* group flags */ 270 union grouptypes gr_u; /* the host/network */ 271}; 272/* Group types */ 273#define GT_NULL 0x0 /* not fully-initialized yet */ 274#define GT_NETGROUP 0x1 /* this is a netgroup */ 275#define GT_NET 0x2 /* this is a network */ 276#define GT_HOST 0x3 /* this is a single host address */ 277/* Group flags */ 278#define GF_SHOW 0x1 /* show this entry in export list */ 279 280/* 281 * host/network flags list entry 282 */ 283struct host { 284 TAILQ_ENTRY(host) ht_next; 285 int ht_flags; /* export options for these hosts */ 286 struct xucred ht_cred; /* mapped credential for these hosts */ 287 struct grouplist *ht_grp; /* host/network flags applies to */ 288 struct nfs_sec ht_sec; /* security flavors for these hosts */ 289}; 290 291struct fhreturn { 292 int fhr_flags; 293 int fhr_vers; 294 struct nfs_sec fhr_sec; 295 fhandle_t fhr_fh; 296}; 297 298/* Global defs */ 299int add_name(struct namelisttqh *, char *); 300void free_namelist(struct namelisttqh *); 301int add_dir(struct dirlist **, char *); 302char * add_expdir(struct expdir **, char *, int); 303int add_grp(struct grouplist **, struct grouplist *); 304int add_host(struct hosttqh *, struct host *); 305void add_mlist(char *, char *); 306int addrinfo_cmp(struct addrinfo *, struct addrinfo *); 307int check_dirpath(char *); 308int check_options(int); 309void clear_export_error(uint32_t); 310int cmp_secflavs(struct nfs_sec *, struct nfs_sec *); 311void merge_secflavs(struct nfs_sec *, struct nfs_sec *); 312void del_mlist(char *, char *); 313int expdir_search(struct expfs *, char *, struct sockaddr *, int *, struct nfs_sec *); 314int do_export(int, struct expfs *, struct expdir *, struct grouplist *, int, 315 struct xucred *, struct nfs_sec *); 316int do_opt(char **, char **, struct grouplist *, int *, 317 int *, int *, struct xucred *, struct nfs_sec *, char *, u_char *); 318struct expfs *ex_search(u_char *); 319void export_error(int, const char *, ...); 320void export_error_cleanup(struct expfs *); 321struct host *find_group_address_match_in_host_list(struct hosttqh *, struct grouplist *); 322struct host *find_host(struct hosttqh *, struct sockaddr *); 323void free_dirlist(struct dirlist *dl); 324void free_expdir(struct expdir *); 325void free_expfs(struct expfs *); 326void free_grp(struct grouplist *); 327void free_hosts(struct hosttqh *); 328void free_host(struct host *); 329struct expdir *get_expdir(void); 330struct expfs *get_expfs(void); 331int get_host_addresses(char *, struct grouplist *); 332struct host *get_host(void); 333int get_export_entry(void); 334void get_mountlist(void); 335int get_net(char *, struct netmsk *, int); 336int get_sec_flavors(char *flavorlist, struct nfs_sec *); 337struct grouplist *get_grp(struct grouplist *); 338const char *grp_addr(struct grouplist *); 339char * grp_name(struct grouplist *); 340int hang_options_setup(struct expdir *, int, struct xucred *, struct grouplist *, struct nfs_sec *, int *); 341void hang_options_finalize(struct expdir *); 342void hang_options_cleanup(struct expdir *); 343int hang_options_mountdir(struct expdir *, char *, int, struct grouplist *, struct nfs_sec *); 344void mntsrv(struct svc_req *, SVCXPRT *); 345void nextfield(char **, char **); 346int parsecred(char *, struct xucred *); 347int put_exlist(struct expdir *, XDR *); 348int subdir_check(char *, char *); 349int xdr_dir(XDR *, char *); 350int xdr_explist(XDR *, caddr_t); 351int xdr_fhs(XDR *, caddr_t); 352int xdr_mlist(XDR *, caddr_t); 353 354int get_uuid_from_diskarb(const char *, u_char *); 355struct uuidlist * get_uuid_from_list(const struct statfs *, u_char *, const int); 356struct uuidlist * add_uuid_to_list(const struct statfs *, u_char *, u_char *); 357struct uuidlist * get_uuid(const struct statfs *, u_char *); 358struct uuidlist * find_uuid(u_char *); 359struct uuidlist * find_uuid_by_fsid(u_int32_t); 360void uuidlist_clearexport(void); 361char * uuidstring(u_char *, char *); 362void uuidlist_save(void); 363void uuidlist_restore(void); 364 365struct expidlist * find_export_id(struct uuidlist *, u_int32_t); 366struct expidlist * get_export_id(struct uuidlist *, char *); 367 368void dump_exports(void); 369void snprintf_cred(char *buf, int, struct xucred *cr); 370 371pthread_mutex_t export_mutex; /* lock for mountd/export globals */ 372struct expfstqh xfshead; /* list of exported file systems */ 373struct dirlist *xpaths; /* list of exported paths */ 374int xpaths_complete = 1; 375struct mountlist *mlhead; /* remote mount list */ 376struct grouplist *grpcache; /* host/net group cache */ 377struct xucred def_anon = { /* default map credential: "nobody" */ 378 XUCRED_VERSION, 379 (uid_t) -2, 380 1, 381 { (gid_t) -2 }, 382}; 383 384LIST_HEAD(,errlist) xerrs; /* list of export errors */ 385int export_errors, hostnamecount, hostnamegoodcount, missingexportcount; 386SVCXPRT *udptransp, *tcptransp; 387SVCXPRT *udp6transp, *tcp6transp; 388int mounttcpsock, mountudpsock; 389int mounttcp6sock, mountudp6sock; 390 391/* export options */ 392#define OP_MAPROOT 0x00000001 /* map root credentials */ 393#define OP_MAPALL 0x00000002 /* map all credentials */ 394#define OP_SECFLAV 0x00000004 /* security flavor(s) specified */ 395#define OP_MASK 0x00000008 /* network mask specified */ 396#define OP_NET 0x00000010 /* network address specified */ 397#define OP_MANGLEDNAMES 0x00000020 /* tell the vfs to mangle names that are > 255 bytes */ 398#define OP_ALLDIRS 0x00000040 /* allow mounting subdirs */ 399#define OP_READONLY 0x00000080 /* export read-only */ 400#define OP_32BITCLIENTS 0x00000100 /* use 32-bit directory cookies */ 401#define OP_FSPATH 0x00000200 /* file system path specified */ 402#define OP_FSUUID 0x00000400 /* file system UUID specified */ 403#define OP_OFFLINE 0x00000800 /* export is offline */ 404#define OP_ONLINE 0x04000000 /* export is online */ 405#define OP_SHOW 0x08000000 /* show this entry in export list */ 406#define OP_MISSING 0x10000000 /* export is missing */ 407#define OP_DEFEXP 0x20000000 /* default export for everyone (else) */ 408#define OP_ADD 0x40000000 /* tag export for potential addition */ 409#define OP_DEL 0x80000000 /* tag export for potential deletion */ 410#define OP_EXOPTMASK 0x100009E3 /* export options mask */ 411#define OP_EXOPTS(X) ((X) & OP_EXOPTMASK) 412 413#define RECHECKEXPORTS_TIMEOUT 600 414#define RECHECKEXPORTS_DELAYED_STARTUP_TIMEOUT 120 415#define RECHECKEXPORTS_DELAYED_STARTUP_INTERVAL 5 416 417/* 418 * Mountd server for NFS mount protocol as described in: 419 * NFS: Network File System Protocol Specification, RFC1094, Appendix A 420 * The optional arguments are the exports file name 421 * default: _PATH_EXPORTS 422 * and "-n" to allow nonroot mount. 423 */ 424 425/* 426 * The incredibly complex mountd thread function 427 */ 428void * 429mountd_thread(__unused void *arg) 430{ 431 set_thread_sigmask(); 432 svc_run(); 433 log(LOG_ERR, "mountd died"); 434 exit(1); 435} 436 437void 438mountd_init(void) 439{ 440 int error; 441 442 TAILQ_INIT(&xfshead); 443 xpaths = NULL; 444 mlhead = NULL; 445 grpcache = NULL; 446 TAILQ_INIT(&ulhead); 447 LIST_INIT(&xerrs); 448 449 error = pthread_mutex_init(&export_mutex, NULL); 450 if (error) { 451 log(LOG_ERR, "export mutex init failed: %s (%d)", strerror(error), error); 452 exit(1); 453 } 454 455 uuidlist_restore(); 456} 457 458void 459mountd(void) 460{ 461 struct sockaddr_storage saddr; 462 struct sockaddr_in *sin = (struct sockaddr_in*)&saddr; 463 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&saddr; 464 socklen_t socklen; 465 struct nfs_export_args nxa; 466 int error, on = 1, init_retry, svcregcnt; 467 pthread_t thd; 468 time_t init_start; 469 470 /* global initialization */ 471 mountd_init(); 472 check_for_mount_changes(); 473 474 /* 475 * mountd needs to start from a clean slate. 476 */ 477 478 /* clear the table of mounts at startup. */ 479 unlink(_PATH_RMOUNTLIST); 480 481 /* Delete all exports that are in the kernel. */ 482 bzero(&nxa, sizeof(nxa)); 483 nxa.nxa_flags = NXA_DELETE_ALL; 484 error = nfssvc(NFSSVC_EXPORT, &nxa); 485 if (error && (errno != ENOENT)) 486 log(LOG_ERR, "Can't delete all exports: %s (%d)", strerror(errno), errno); 487 488 /* set up the export and mount lists */ 489 490 /* 491 * Note that the recheckexports_until functionality will allow us to retry exports 492 * for a while if we have problems resolving any host names. However, these problems 493 * may not affect all exports (e.g. default exports) and that could result in some 494 * hosts getting the wrong access until their export options are set up properly. 495 * Since this could result in some hosts receiving errors or erroneous processing, 496 * we check if the first get_exportlist() check resulted in any successful host 497 * name lookups. If there were names and none of them were looked up successfully, 498 * then we'll delay startup for a *short* while in an attempt to avoid any problems 499 * if the problem clears up shortly. 500 */ 501 init_start = time(NULL); 502 init_retry = 0; 503 while (1) { 504 DEBUG(1, "Getting export list."); 505 get_exportlist(); 506 if (!hostnamecount || hostnamegoodcount) { 507 if (init_retry) 508 log(LOG_WARNING, "host name resolution seems to be working now... continuing initialization"); 509 break; 510 } 511 if (!init_retry) { 512 log(LOG_WARNING, "host name resolution seems to be having problems... delaying initialization"); 513 init_retry = 1; 514 } else if (time(NULL) > (init_start + RECHECKEXPORTS_DELAYED_STARTUP_TIMEOUT)) { 515 log(LOG_WARNING, "giving up on host name resolution... continuing initialization"); 516 break; 517 } 518 sleep(RECHECKEXPORTS_DELAYED_STARTUP_INTERVAL); 519 } 520 521 DEBUG(1, "Getting mount list."); 522 get_mountlist(); 523 DEBUG(1, "Here we go."); 524 525 /* create mountd service sockets */ 526 if (!config.udp && !config.tcp) { 527 log(LOG_WARNING, "No network transport(s) configured. mountd thread not starting."); 528 return; 529 } 530 531 mountudpsock = mounttcpsock = -1; 532 mountudp6sock = mounttcp6sock = -1; 533 534 /* If we are serving UDP, set up the MOUNT/UDP socket. */ 535 if (config.udp) { 536 537 /* IPv4 */ 538 if ((mountudpsock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 539 log(LOG_ERR, "can't create MOUNT/UDP IPv4 socket: %s (%d)", strerror(errno), errno); 540 if (mountudpsock >= 0) { 541 sin->sin_family = AF_INET; 542 sin->sin_addr.s_addr = INADDR_ANY; 543 sin->sin_port = htons(config.mount_port); 544 sin->sin_len = sizeof(*sin); 545 if (bindresvport_sa(mountudpsock, (struct sockaddr*)sin) < 0) { 546 /* socket may still be lingering from previous incarnation */ 547 /* wait a few seconds and try again */ 548 sleep(6); 549 if (bindresvport_sa(mountudpsock, (struct sockaddr*)sin) < 0) { 550 log(LOG_ERR, "can't bind MOUNT/UDP IPv4 addr: %s (%d)", strerror(errno), errno); 551 close(mountudpsock); 552 mountudpsock = -1; 553 } 554 } 555 } 556 if (mountudpsock >= 0) { 557 socklen = sizeof(*sin); 558 if (getsockname(mountudpsock, (struct sockaddr*)sin, &socklen)) { 559 log(LOG_ERR, "can't getsockname on MOUNT/UDP IPv4 socket: %s (%d)", strerror(errno), errno); 560 close(mountudpsock); 561 mountudpsock = -1; 562 } else { 563 mountudpport = ntohs(sin->sin_port); 564 } 565 } 566 if ((mountudpsock >= 0) && ((udptransp = svcudp_create(mountudpsock)) == NULL)) { 567 log(LOG_ERR, "Can't create MOUNT/UDP IPv4 service"); 568 close(mountudpsock); 569 mountudpsock = -1; 570 mountudpport = 0; 571 } 572 if (mountudpsock >= 0) { 573 svcregcnt = 0; 574 if (!svc_register(udptransp, RPCPROG_MNT, 1, mntsrv, 0)) 575 log(LOG_ERR, "Can't register IPv4 MOUNT/UDP v1 service"); 576 else 577 svcregcnt++; 578 if (!svc_register(udptransp, RPCPROG_MNT, 3, mntsrv, 0)) 579 log(LOG_ERR, "Can't register IPv4 MOUNT/UDP v3 service"); 580 else 581 svcregcnt++; 582 if (!svcregcnt) { 583 svc_destroy(udptransp); 584 close(mountudpsock); 585 mountudpsock = -1; 586 mountudpport = 0; 587 } 588 } 589 590 /* IPv6 */ 591 if ((mountudp6sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) 592 log(LOG_ERR, "can't create MOUNT/UDP IPv6 socket: %s (%d)", strerror(errno), errno); 593 if (mountudp6sock >= 0) { 594 setsockopt(mountudp6sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); 595 sin6->sin6_family = AF_INET6; 596 sin6->sin6_addr = in6addr_any; 597 sin6->sin6_port = htons(config.mount_port); 598 sin6->sin6_len = sizeof(*sin6); 599 if (bindresvport_sa(mountudp6sock, (struct sockaddr*)sin6) < 0) { 600 /* socket may still be lingering from previous incarnation */ 601 /* wait a few seconds and try again */ 602 sleep(6); 603 if (bindresvport_sa(mountudp6sock, (struct sockaddr*)sin6) < 0) { 604 log(LOG_ERR, "can't bind MOUNT/UDP IPv6 addr: %s (%d)", strerror(errno), errno); 605 close(mountudp6sock); 606 mountudp6sock = -1; 607 } 608 } 609 } 610 if (mountudp6sock >= 0) { 611 socklen = sizeof(*sin6); 612 if (getsockname(mountudp6sock, (struct sockaddr*)sin6, &socklen)) { 613 log(LOG_ERR, "can't getsockname on MOUNT/UDP IPv6 socket: %s (%d)", strerror(errno), errno); 614 close(mountudp6sock); 615 mountudp6sock = -1; 616 } else { 617 mountudp6port = ntohs(sin6->sin6_port); 618 } 619 } 620 if ((mountudp6sock >= 0) && ((udp6transp = svcudp_create(mountudp6sock)) == NULL)) { 621 log(LOG_ERR, "Can't create MOUNT/UDP IPv6 service"); 622 close(mountudp6sock); 623 mountudp6sock = -1; 624 mountudp6port = 0; 625 } 626 if (mountudp6sock >= 0) { 627 svcregcnt = 0; 628 if (!svc_register(udp6transp, RPCPROG_MNT, 1, mntsrv, 0)) 629 log(LOG_ERR, "Can't register IPv6 MOUNT/UDP v1 service"); 630 else 631 svcregcnt++; 632 if (!svc_register(udp6transp, RPCPROG_MNT, 3, mntsrv, 0)) 633 log(LOG_ERR, "Can't register IPv6 MOUNT/UDP v3 service"); 634 else 635 svcregcnt++; 636 if (!svcregcnt) { 637 svc_destroy(udp6transp); 638 close(mountudp6sock); 639 mountudp6sock = -1; 640 mountudp6port = 0; 641 } 642 } 643 644 } 645 646 /* If we are serving TCP, set up the MOUNT/TCP socket. */ 647 if (config.tcp) { 648 649 /* IPv4 */ 650 if ((mounttcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) 651 log(LOG_ERR, "can't create MOUNT/TCP IPv4 socket: %s (%d)", strerror(errno), errno); 652 if (mounttcpsock >= 0) { 653 if (setsockopt(mounttcpsock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) 654 log(LOG_WARNING, "setsockopt MOUNT/TCP IPv4 SO_REUSEADDR: %s (%d)", strerror(errno), errno); 655 sin->sin_family = AF_INET; 656 sin->sin_addr.s_addr = INADDR_ANY; 657 sin->sin_port = htons(config.mount_port); 658 sin->sin_len = sizeof(*sin); 659 if (bindresvport_sa(mounttcpsock, (struct sockaddr*)sin) < 0) { 660 log(LOG_ERR, "can't bind MOUNT/TCP IPv4 addr: %s (%d)", strerror(errno), errno); 661 close(mounttcpsock); 662 mounttcpsock = -1; 663 } 664 } 665 if ((mounttcpsock >= 0) && (listen(mounttcpsock, 128) < 0)) { 666 log(LOG_ERR, "MOUNT IPv4 listen failed: %s (%d)", strerror(errno), errno); 667 close(mounttcpsock); 668 mounttcpsock = -1; 669 } 670 if (mounttcpsock >= 0) { 671 socklen = sizeof(*sin); 672 if (getsockname(mounttcpsock, (struct sockaddr*)sin, &socklen)) { 673 log(LOG_ERR, "can't getsockname on MOUNT/TCP IPv4 socket: %s (%d)", strerror(errno), errno); 674 close(mounttcpsock); 675 mounttcpsock = -1; 676 } else { 677 mounttcpport = ntohs(sin->sin_port); 678 } 679 } 680 if ((mounttcpsock >= 0) && ((tcptransp = svctcp_create(mounttcpsock, 0, 0)) == NULL)) { 681 log(LOG_ERR, "Can't create MOUNT/TCP IPv4 service"); 682 close(mounttcpsock); 683 mounttcpsock = -1; 684 mounttcpport = 0; 685 } 686 if (mounttcpsock >= 0) { 687 svcregcnt = 0; 688 if (!svc_register(tcptransp, RPCPROG_MNT, 1, mntsrv, 0)) 689 log(LOG_ERR, "Can't register IPv4 MOUNT/TCP v1 service"); 690 else 691 svcregcnt++; 692 if (!svc_register(tcptransp, RPCPROG_MNT, 3, mntsrv, 0)) 693 log(LOG_ERR, "Can't register IPv4 MOUNT/TCP v3 service"); 694 else 695 svcregcnt++; 696 if (!svcregcnt) { 697 svc_destroy(tcptransp); 698 close(mounttcpsock); 699 mounttcpsock = -1; 700 mounttcpport = 0; 701 } 702 } 703 704 /* IPv6 */ 705 if ((mounttcp6sock = socket(AF_INET6, SOCK_STREAM, 0)) < 0) 706 log(LOG_ERR, "can't create MOUNT/TCP IPv6 socket: %s (%d)", strerror(errno), errno); 707 if (mounttcp6sock >= 0) { 708 if (setsockopt(mounttcp6sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) 709 log(LOG_WARNING, "setsockopt MOUNT/TCP IPv6 SO_REUSEADDR: %s (%d)", strerror(errno), errno); 710 setsockopt(mounttcp6sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); 711 sin6->sin6_family = AF_INET6; 712 sin6->sin6_addr = in6addr_any; 713 sin6->sin6_port = htons(config.mount_port); 714 sin6->sin6_len = sizeof(*sin6); 715 if (bindresvport_sa(mounttcp6sock, (struct sockaddr*)sin6) < 0) { 716 log(LOG_ERR, "can't bind MOUNT/TCP IPv6 addr: %s (%d)", strerror(errno), errno); 717 close(mounttcp6sock); 718 mounttcp6sock = -1; 719 } 720 } 721 if ((mounttcp6sock >= 0) && (listen(mounttcp6sock, 128) < 0)) { 722 log(LOG_ERR, "MOUNT IPv6 listen failed: %s (%d)", strerror(errno), errno); 723 close(mounttcp6sock); 724 mounttcp6sock = -1; 725 } 726 if (mounttcp6sock >= 0) { 727 socklen = sizeof(*sin6); 728 if (getsockname(mounttcp6sock, (struct sockaddr*)sin6, &socklen)) { 729 log(LOG_ERR, "can't getsockname on MOUNT/TCP IPv6 socket: %s (%d)", strerror(errno), errno); 730 close(mounttcp6sock); 731 mounttcp6sock = -1; 732 } else { 733 mounttcp6port = ntohs(sin6->sin6_port); 734 } 735 } 736 if ((mounttcp6sock >= 0) && ((tcp6transp = svctcp_create(mounttcp6sock, 0, 0)) == NULL)) { 737 log(LOG_ERR, "Can't create MOUNT/TCP IPv6 service"); 738 close(mounttcp6sock); 739 mounttcp6sock = -1; 740 mounttcp6port = 0; 741 } 742 if (mounttcp6sock >= 0) { 743 svcregcnt = 0; 744 if (!svc_register(tcp6transp, RPCPROG_MNT, 1, mntsrv, 0)) 745 log(LOG_ERR, "Can't register IPv6 MOUNT/TCP v1 service"); 746 else 747 svcregcnt++; 748 if (!svc_register(tcp6transp, RPCPROG_MNT, 3, mntsrv, 0)) 749 log(LOG_ERR, "Can't register IPv6 MOUNT/TCP v3 service"); 750 else 751 svcregcnt++; 752 if (!svcregcnt) { 753 svc_destroy(tcp6transp); 754 close(mounttcp6sock); 755 mounttcp6sock = -1; 756 mounttcp6port = 0; 757 } 758 } 759 760 } 761 762 if ((mountudp6sock < 0) && (mounttcp6sock < 0)) 763 log(LOG_WARNING, "Can't create MOUNT IPv6 sockets"); 764 if ((mountudpsock < 0) && (mounttcpsock < 0)) 765 log(LOG_WARNING, "Can't create MOUNT IPv4 sockets"); 766 if ((mountudp6sock < 0) && (mounttcp6sock < 0) && 767 (mountudpsock < 0) && (mounttcpsock < 0)) { 768 log(LOG_ERR, "Can't create any MOUNT sockets!"); 769 exit(1); 770 } 771 772 /* launch mountd pthread */ 773 error = pthread_create(&thd, &pattr, mountd_thread, NULL); 774 if (error) { 775 log(LOG_ERR, "mountd pthread_create: %s (%d)", strerror(error), error); 776 exit(1); 777 } 778} 779 780/* 781 * functions for locking/unlocking the exports list (and other export-related globals) 782 */ 783void 784lock_exports(void) 785{ 786 int error; 787 788 if (checkexports) 789 return; 790 791 error = pthread_mutex_lock(&export_mutex); 792 if (error) 793 log(LOG_ERR, "export mutex lock failed: %s (%d)", strerror(error), error); 794} 795void 796unlock_exports(void) 797{ 798 int error; 799 800 if (checkexports) 801 return; 802 803 error = pthread_mutex_unlock(&export_mutex); 804 if (error) 805 log(LOG_ERR, "export mutex unlock failed: %s (%d)", strerror(error), error); 806} 807 808/* 809 * The mount rpc service 810 */ 811void 812mntsrv(struct svc_req *rqstp, SVCXPRT *transp) 813{ 814 struct expfs *xf; 815 struct fhreturn fhr; 816 struct stat stb; 817 struct statfs fsb; 818 struct nfs_sec secflavs; 819 struct sockaddr *sa; 820 u_short sport; 821 char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN]; 822 char addrbuf[2*INET6_ADDRSTRLEN], hostbuf[NI_MAXHOST]; 823 int bad = ENOENT, options; 824 u_char uuid[16]; 825 826 sa = svc_getcaller_sa(transp); 827 if (sa->sa_family == AF_INET) 828 sport = ntohs(((struct sockaddr_in*) AOK sa)->sin_port); 829 else if (sa->sa_family == AF_INET6) 830 sport = ntohs(((struct sockaddr_in6*) AOK sa)->sin6_port); 831 else 832 sport = 0; 833 834 strlcpy(hostbuf, "unknown_host", sizeof(hostbuf)); 835 strlcpy(addrbuf, "unknown_host", sizeof(addrbuf)); 836 837 switch (rqstp->rq_proc) { 838 case NULLPROC: 839 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) 840 log(LOG_ERR, "Can't send NULL MOUNT reply"); 841 return; 842 case RPCMNT_MOUNT: 843 if ((sport >= IPPORT_RESERVED) && config.mount_require_resv_port) { 844 svcerr_weakauth(transp); 845 return; 846 } 847 if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) { 848 svcerr_decode(transp); 849 return; 850 } 851 852 lock_exports(); 853 854 /* 855 * Get the real pathname and make sure it is a directory 856 * or a regular file if the -r option was specified 857 * and it exists. 858 */ 859 if (realpath(rpcpath, dirpath) == 0 || 860 stat(dirpath, &stb) < 0 || 861 (!S_ISDIR(stb.st_mode) && 862 (!config.mount_regular_files || !S_ISREG(stb.st_mode))) || 863 statfs(dirpath, &fsb) < 0) { 864 unlock_exports(); 865 chdir("/"); /* Just in case realpath doesn't */ 866 DEBUG(1, "stat failed on %s", dirpath); 867 if (!svc_sendreply(transp, (xdrproc_t)xdr_long, (caddr_t)&bad)) 868 log(LOG_ERR, "Can't send reply for failed mount"); 869 if (config.verbose) { 870 getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); 871 log(LOG_NOTICE, "Mount failed: %s %s", addrbuf, dirpath); 872 } 873 return; 874 } 875 876 /* get UUID for volume */ 877 if (!get_uuid_from_list(&fsb, uuid, UL_CHECK_ALL)) { 878 unlock_exports(); 879 DEBUG(1, "no exported volume uuid for %s", dirpath); 880 if (!svc_sendreply(transp, (xdrproc_t)xdr_long, (caddr_t)&bad)) 881 log(LOG_ERR, "Can't send reply for failed mount"); 882 if (config.verbose) { 883 getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); 884 log(LOG_NOTICE, "Mount failed: %s %s", addrbuf, dirpath); 885 } 886 return; 887 } 888 889 /* Check in the exports list */ 890 xf = ex_search(uuid); 891 if (xf && expdir_search(xf, dirpath, sa, &options, &secflavs)) { 892 fhr.fhr_flags = options; 893 fhr.fhr_vers = rqstp->rq_vers; 894 bcopy(&secflavs, &fhr.fhr_sec, sizeof(struct nfs_sec)); 895 /* Get the file handle (specifying max fh size based on protocol version */ 896 memset(&fhr.fhr_fh, 0, sizeof(fhandle_t)); 897 fhr.fhr_fh.fh_len = (fhr.fhr_vers < 3) ? NFSV2_MAX_FH_SIZE : NFSV3_MAX_FH_SIZE; 898 if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) { 899 DEBUG(1, "Can't get fh for %s: %s (%d)", dirpath, 900 strerror(errno), errno); 901 bad = EACCES; /* path must not be exported */ 902 if (!svc_sendreply(transp, (xdrproc_t)xdr_long, (caddr_t)&bad)) 903 log(LOG_ERR, "Can't send reply for failed mount"); 904 unlock_exports(); 905 return; 906 } 907 if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs, (caddr_t)&fhr)) 908 log(LOG_ERR, "Can't send mount reply"); 909 if (!getnameinfo(sa, sa->sa_len, hostbuf, sizeof(hostbuf), NULL, 0, 0)) 910 add_mlist(hostbuf, dirpath); 911 log(LOG_INFO, "Mount successful: %s %s", hostbuf, dirpath); 912 } else { 913 bad = EACCES; 914 if (config.verbose) { 915 getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); 916 log(LOG_NOTICE, "Mount failed: %s %s", addrbuf, dirpath); 917 } 918 if (!svc_sendreply(transp, (xdrproc_t)xdr_long, (caddr_t)&bad)) 919 log(LOG_ERR, "Can't send reply for failed mount"); 920 } 921 unlock_exports(); 922 return; 923 case RPCMNT_DUMP: 924 if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL)) 925 log(LOG_ERR, "Can't send MOUNT dump reply"); 926 if (config.verbose >= 3) { 927 getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); 928 DEBUG(1, "dump: %s", addrbuf); 929 } 930 return; 931 case RPCMNT_UMOUNT: 932 if ((sport >= IPPORT_RESERVED) && config.mount_require_resv_port) { 933 svcerr_weakauth(transp); 934 return; 935 } 936 if (!svc_getargs(transp, (xdrproc_t)xdr_dir, dirpath)) { 937 svcerr_decode(transp); 938 return; 939 } 940 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) 941 log(LOG_ERR, "Can't send UMOUNT reply"); 942 if (!getnameinfo(sa, sa->sa_len, hostbuf, sizeof(hostbuf), NULL, 0, NI_NAMEREQD)) 943 del_mlist(hostbuf, dirpath); 944 else 945 hostbuf[0] = '\0'; 946 if (!getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST)) 947 del_mlist(addrbuf, dirpath); 948 log(LOG_INFO, "umount: %s %s", hostbuf[0] ? hostbuf : addrbuf, dirpath); 949 return; 950 case RPCMNT_UMNTALL: 951 if ((sport >= IPPORT_RESERVED) && config.mount_require_resv_port) { 952 svcerr_weakauth(transp); 953 return; 954 } 955 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) 956 log(LOG_ERR, "Can't send UMNTALL reply"); 957 if (!getnameinfo(sa, sa->sa_len, hostbuf, sizeof(hostbuf), NULL, 0, NI_NAMEREQD)) 958 del_mlist(hostbuf, NULL); 959 else 960 hostbuf[0] = '\0'; 961 if (!getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST)) 962 del_mlist(addrbuf, (char *)NULL); 963 log(LOG_INFO, "umount all: %s", hostbuf[0] ? hostbuf : addrbuf); 964 return; 965 case RPCMNT_EXPORT: 966 lock_exports(); 967 if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL)) 968 log(LOG_ERR, "Can't send EXPORT reply"); 969 unlock_exports(); 970 if (config.verbose >= 3) { 971 getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); 972 DEBUG(1, "export: %s", addrbuf); 973 } 974 return; 975 default: 976 svcerr_noproc(transp); 977 return; 978 } 979} 980 981/* 982 * Xdr conversion for a dirpath string 983 */ 984int 985xdr_dir(XDR *xdrsp, char *dirp) 986{ 987 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 988} 989 990/* 991 * Xdr routine to generate file handle reply 992 */ 993int 994xdr_fhs(XDR *xdrsp, caddr_t cp) 995{ 996 struct fhreturn *fhrp = (struct fhreturn *) AOK cp; 997 xdr_long_t ok = 0, len, auth; 998 int32_t i; 999 1000 if (!xdr_long(xdrsp, &ok)) 1001 return (0); 1002 switch (fhrp->fhr_vers) { 1003 case 1: 1004 return (xdr_opaque(xdrsp, fhrp->fhr_fh.fh_data, NFSX_V2FH)); 1005 case 3: 1006 len = fhrp->fhr_fh.fh_len; 1007 if (!xdr_long(xdrsp, &len)) 1008 return (0); 1009 if (!xdr_opaque(xdrsp, fhrp->fhr_fh.fh_data, fhrp->fhr_fh.fh_len)) 1010 return (0); 1011 /* security flavors */ 1012 if (fhrp->fhr_sec.count == 0) { 1013 auth = RPCAUTH_SYS; 1014 len = 1; 1015 if (!xdr_long(xdrsp, &len)) 1016 return (0); 1017 return (xdr_long(xdrsp, &auth)); 1018 } 1019 1020 len = fhrp->fhr_sec.count; 1021 if (!xdr_long(xdrsp, &len)) 1022 return (0); 1023 for (i = 0; i < fhrp->fhr_sec.count; i++) { 1024 auth = (xdr_long_t)fhrp->fhr_sec.flavors[i]; 1025 if (!xdr_long(xdrsp, &auth)) 1026 return (0); 1027 } 1028 return (TRUE); 1029 }; 1030 return (0); 1031} 1032 1033int 1034xdr_mlist(XDR *xdrsp, __unused caddr_t cp) 1035{ 1036 struct mountlist *mlp; 1037 int trueval = 1; 1038 int falseval = 0; 1039 1040 mlp = mlhead; 1041 while (mlp) { 1042 if (!xdr_bool(xdrsp, &trueval)) 1043 return (0); 1044 if (!xdr_string(xdrsp, &mlp->ml_host, RPCMNT_NAMELEN)) 1045 return (0); 1046 if (!xdr_string(xdrsp, &mlp->ml_dir, RPCMNT_PATHLEN)) 1047 return (0); 1048 mlp = mlp->ml_next; 1049 } 1050 if (!xdr_bool(xdrsp, &falseval)) 1051 return (0); 1052 return (1); 1053} 1054 1055/* 1056 * Xdr conversion for export list 1057 */ 1058int 1059xdr_explist(XDR *xdrsp, __unused caddr_t cp) 1060{ 1061 struct expfs *xf; 1062 struct expdir *xd; 1063 int falseval = 0; 1064 1065 TAILQ_FOREACH(xf, &xfshead, xf_next) { 1066 TAILQ_FOREACH(xd, &xf->xf_dirl, xd_next) { 1067 if (put_exlist(xd, xdrsp)) 1068 goto errout; 1069 } 1070 } 1071 if (!xdr_bool(xdrsp, &falseval)) 1072 return (0); 1073 return (1); 1074errout: 1075 return (0); 1076} 1077 1078/* 1079 * Called from xdr_explist() to output the mountable exported 1080 * directory paths. 1081 */ 1082int 1083put_exlist(struct expdir *xd, XDR *xdrsp) 1084{ 1085 struct expdir *mxd; 1086 struct grouplist *grp; 1087 struct host *hp; 1088 int trueval = 1; 1089 int falseval = 0; 1090 char *strp; 1091 char offline_all[] = "<offline>"; 1092 char offline_some[] = "<offline*>"; 1093 char everyone[] = "(Everyone)"; 1094 char abuf[RPCMNT_NAMELEN+1]; 1095 int offline = 0, auth = 0, i; 1096 1097 if (!xd) 1098 return (0); 1099 1100 if (!xdr_bool(xdrsp, &trueval)) 1101 return (1); 1102 strp = xd->xd_dir; 1103 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) 1104 return (1); 1105 if (xd->xd_iflags & OP_OFFLINE) { 1106 /* report if export is offline for all or some* hosts */ 1107 if (!xdr_bool(xdrsp, &trueval)) 1108 return (1); 1109 strp = (xd->xd_iflags & OP_ONLINE) ? offline_some : offline_all; 1110 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) 1111 return (1); 1112 offline = 1; 1113 } 1114 if ((xd->xd_ssec.count > 1) || (xd->xd_ssec.flavors[0] != RPCAUTH_SYS)) { 1115 /* report non-default auth flavors */ 1116 if (!xdr_bool(xdrsp, &trueval)) 1117 return (1); 1118 abuf[0] = '\0'; 1119 strlcpy(abuf, "<", sizeof(abuf)); 1120 for (i=0; i < xd->xd_ssec.count; i++) { 1121 if (xd->xd_ssec.flavors[i] == RPCAUTH_SYS) 1122 strlcat(abuf, "sys", sizeof(abuf)); 1123 else if (xd->xd_ssec.flavors[i] == RPCAUTH_KRB5) 1124 strlcat(abuf, "krb5", sizeof(abuf)); 1125 else if (xd->xd_ssec.flavors[i] == RPCAUTH_KRB5I) 1126 strlcat(abuf, "krb5i", sizeof(abuf)); 1127 else if (xd->xd_ssec.flavors[i] == RPCAUTH_KRB5P) 1128 strlcat(abuf, "krb5p", sizeof(abuf)); 1129 else 1130 continue; 1131 if (i < xd->xd_ssec.count-1) 1132 strlcat(abuf, ":", sizeof(abuf)); 1133 } 1134 strlcat(abuf, ">", sizeof(abuf)); 1135 strp = abuf; 1136 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) 1137 return (1); 1138 auth = 1; 1139 } 1140 if (!(xd->xd_flags & OP_DEFEXP)) { 1141 TAILQ_FOREACH(hp, &xd->xd_hosts, ht_next) { 1142 if (!(hp->ht_flags & OP_SHOW)) 1143 continue; 1144 grp = hp->ht_grp; 1145 switch (grp->gr_type) { 1146 case GT_HOST: 1147 case GT_NET: 1148 case GT_NETGROUP: 1149 if (!xdr_bool(xdrsp, &trueval)) 1150 return (1); 1151 strp = grp_name(grp); 1152 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) 1153 return (1); 1154 } 1155 } 1156 } else if (offline || auth) { 1157 if (!xdr_bool(xdrsp, &trueval)) 1158 return (1); 1159 strp = everyone; 1160 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) 1161 return (1); 1162 } 1163 if (!xdr_bool(xdrsp, &falseval)) 1164 return (1); 1165 1166 TAILQ_FOREACH(mxd, &xd->xd_mountdirs, xd_next) { 1167 if (put_exlist(mxd, xdrsp)) 1168 return (1); 1169 } 1170 1171 return (0); 1172} 1173 1174 1175/* 1176 * Clean up a pathname. Removes quotes around quoted strings, 1177 * strips escaped characters, removes trailing slashes. 1178 */ 1179char * 1180clean_pathname(char *line) 1181{ 1182 int len, esc; 1183 char c, *p, *s; 1184 1185 if (line == NULL) 1186 return NULL; 1187 len = strlen(line); 1188 s = malloc(len + 1); 1189 if (s == NULL) 1190 return NULL; 1191 1192 len = 0; 1193 esc = 0; 1194 c = '\0'; 1195 1196 p = line; 1197 1198 if (*p == '\'' || *p == '"') { 1199 c = *p; 1200 p++; 1201 } 1202 1203 for (;*p != '\0'; p++) { 1204 if (esc == 1) { 1205 s[len++] = *p; 1206 esc = 0; 1207 } else if (*p == c) 1208 break; 1209 else if (*p == '\\') 1210 esc = 1; 1211 else if (c == '\0' && (*p == ' ' || *p == '\t')) 1212 break; 1213 else s[len++] = *p; 1214 } 1215 1216 /* strip trailing slashes */ 1217 for (; len > 1 && s[len-1] == '/'; len--) 1218 ; 1219 1220 s[len] = '\0'; 1221 1222 return (s); 1223} 1224 1225 1226/* 1227 * Query DiskArb for a volume's UUID 1228 */ 1229int 1230get_uuid_from_diskarb(const char *path, u_char *uuid) 1231{ 1232 DASessionRef session; 1233 DADiskRef disk; 1234 CFDictionaryRef dd; 1235 CFTypeRef val; 1236 CFUUIDBytes uuidbytes; 1237 int rv = 1; 1238 1239 session = NULL; 1240 disk = NULL; 1241 dd = NULL; 1242 1243 session = DASessionCreate(NULL); 1244 if (!session) { 1245 log(LOG_ERR, "can't create DiskArb session"); 1246 rv = 0; 1247 goto out; 1248 } 1249 disk = DADiskCreateFromBSDName(NULL, session, path); 1250 if (!disk) { 1251 DEBUG(1, "DADiskCreateFromBSDName(%s) failed", path); 1252 rv = 0; 1253 goto out; 1254 } 1255 dd = DADiskCopyDescription(disk); 1256 if (!dd) { 1257 DEBUG(1, "DADiskCopyDescription(%s) failed", path); 1258 rv = 0; 1259 goto out; 1260 } 1261 1262 if (!CFDictionaryGetValueIfPresent(dd, (kDADiskDescriptionVolumeUUIDKey), &val)) { 1263 DEBUG(1, "unable to get UUID for volume %s", path); 1264 rv = 0; 1265 goto out; 1266 } 1267 uuidbytes = CFUUIDGetUUIDBytes(val); 1268 bcopy(&uuidbytes, uuid, sizeof(uuidbytes)); 1269 1270out: 1271 if (session) CFRelease(session); 1272 if (disk) CFRelease(disk); 1273 if (dd) CFRelease(dd); 1274 return (rv); 1275} 1276 1277/* 1278 * find the UUID for this volume in the UUID list 1279 */ 1280struct uuidlist * 1281get_uuid_from_list(const struct statfs *fsb, u_char *uuid, const int flags) 1282{ 1283 struct uuidlist *ulp; 1284 1285 if (!(flags & UL_CHECK_ALL)) 1286 return (NULL); 1287 1288 TAILQ_FOREACH(ulp, &ulhead, ul_list) { 1289 if ((flags & UL_CHECK_MNTFROM) && 1290 strcmp(fsb->f_mntfromname, ulp->ul_mntfromname)) 1291 continue; 1292 if ((flags & UL_CHECK_MNTON) && 1293 strcmp(fsb->f_mntonname, ulp->ul_mntonname)) 1294 continue; 1295 if (uuid) 1296 bcopy(&ulp->ul_uuid, uuid, sizeof(ulp->ul_uuid)); 1297 break; 1298 } 1299 return (ulp); 1300} 1301 1302/* 1303 * find UUID list entry with the given UUID 1304 */ 1305struct uuidlist * 1306find_uuid(u_char *uuid) 1307{ 1308 struct uuidlist *ulp; 1309 1310 TAILQ_FOREACH(ulp, &ulhead, ul_list) { 1311 if (!bcmp(ulp->ul_uuid, uuid, sizeof(ulp->ul_uuid))) 1312 break; 1313 } 1314 return (ulp); 1315} 1316 1317/* 1318 * find UUID list entry with the given FSID 1319 */ 1320struct uuidlist * 1321find_uuid_by_fsid(u_int32_t fsid) 1322{ 1323 struct uuidlist *ulp; 1324 1325 TAILQ_FOREACH(ulp, &ulhead, ul_list) { 1326 if (ulp->ul_fsid == fsid) 1327 break; 1328 } 1329 return (ulp); 1330} 1331 1332/* 1333 * add a UUID to the UUID list 1334 */ 1335struct uuidlist * 1336add_uuid_to_list(const struct statfs *fsb, u_char *dauuid, u_char *uuid) 1337{ 1338 struct uuidlist *ulpnew; 1339 u_int32_t xfsid; 1340 1341 ulpnew = malloc(sizeof(struct uuidlist)); 1342 if (!ulpnew) { 1343 log(LOG_ERR, "add_uuid_to_list: out of memory"); 1344 return (NULL); 1345 } 1346 bzero(ulpnew, sizeof(*ulpnew)); 1347 LIST_INIT(&ulpnew->ul_exportids); 1348 if (dauuid) { 1349 bcopy(dauuid, ulpnew->ul_dauuid, sizeof(ulpnew->ul_dauuid)); 1350 ulpnew->ul_davalid = 1; 1351 } 1352 bcopy(uuid, ulpnew->ul_uuid, sizeof(ulpnew->ul_uuid)); 1353 strlcpy(ulpnew->ul_mntfromname, fsb->f_mntfromname, sizeof(ulpnew->ul_mntfromname)); 1354 strlcpy(ulpnew->ul_mntonname, fsb->f_mntonname, sizeof(ulpnew->ul_mntonname)); 1355 1356 /* make sure exported FS ID is unique */ 1357 xfsid = UUID2FSID(uuid); 1358 ulpnew->ul_fsid = xfsid; 1359 while (find_uuid_by_fsid(ulpnew->ul_fsid)) 1360 if (++ulpnew->ul_fsid == xfsid) { 1361 /* exhausted exported FS ID values! */ 1362 log(LOG_ERR, "exported FS ID values exhausted, can't add %s", 1363 ulpnew->ul_mntonname); 1364 free(ulpnew); 1365 return (NULL); 1366 } 1367 1368 TAILQ_INSERT_TAIL(&ulhead, ulpnew, ul_list); 1369 return (ulpnew); 1370} 1371 1372/* 1373 * get the UUID to use for this volume's file handles 1374 * and add it to the UUID list if it isn't there yet. 1375 */ 1376struct uuidlist * 1377get_uuid(const struct statfs *fsb, u_char *uuid) 1378{ 1379 CFUUIDRef cfuuid; 1380 CFUUIDBytes uuidbytes; 1381 struct uuidlist *ulp; 1382 u_char dauuid[16]; 1383 int davalid, uuidchanged, reportuuid = 0; 1384 char buf[64], buf2[64]; 1385 1386 /* get DiskArb's idea of the UUID (if any) */ 1387 davalid = get_uuid_from_diskarb(fsb->f_mntfromname, dauuid); 1388 1389 if (davalid) { 1390 DEBUG(2, "get_uuid: %s %s DiskArb says: %s", 1391 fsb->f_mntfromname, fsb->f_mntonname, 1392 uuidstring(dauuid, buf)); 1393 } 1394 1395 /* try to get UUID out of UUID list */ 1396 if ((ulp = get_uuid_from_list(fsb, uuid, UL_CHECK_MNTON))) { 1397 DEBUG(2, "get_uuid: %s %s found: %s", 1398 fsb->f_mntfromname, fsb->f_mntonname, 1399 uuidstring(uuid, buf)); 1400 /* 1401 * Check against any DiskArb UUID. 1402 * If diskarb UUID is different then drop the uuid entry. 1403 */ 1404 if (davalid) { 1405 if (!ulp->ul_davalid) 1406 uuidchanged = 1; 1407 else if (bcmp(ulp->ul_dauuid, dauuid, sizeof(dauuid))) 1408 uuidchanged = 1; 1409 else 1410 uuidchanged = 0; 1411 } else { 1412 if (ulp->ul_davalid) { 1413 /* 1414 * We had a UUID before, but now we don't? 1415 * Assume this is just a transient error, 1416 * issue a warning, and stick with the old UUID. 1417 */ 1418 uuidstring(ulp->ul_dauuid, buf); 1419 log(LOG_WARNING, "lost UUID for %s, was %s, keeping old UUID", 1420 fsb->f_mntonname, buf); 1421 uuidchanged = 0; 1422 } else 1423 uuidchanged = 0; 1424 } 1425 if (uuidchanged) { 1426 uuidstring(ulp->ul_dauuid, buf); 1427 if (davalid) 1428 uuidstring(dauuid, buf2); 1429 else 1430 strlcpy(buf2, "------------------------------------", sizeof(buf2)); 1431 if (ulp->ul_exported) { 1432 /* 1433 * Woah! We already have this file system exported with 1434 * a different UUID (UUID changed while processing the 1435 * exports list). Ignore the UUID change for now so that 1436 * all the exports for this file system will be registered 1437 * using the same UUID/FSID. 1438 * 1439 * XXX Should we do something like set gothup=1 so that 1440 * we will reregister all the exports (with the new UUID)? 1441 * If so, what's to prevent an infinite loop if we always 1442 * seem to be hitting this problem? 1443 */ 1444 log(LOG_WARNING, "ignoring UUID change for already exported file system %s, was %s now %s", 1445 fsb->f_mntonname, buf, buf2); 1446 uuidchanged = 0; 1447 } 1448 } 1449 if (uuidchanged) { 1450 log(LOG_WARNING, "UUID changed for %s, was %s now %s", 1451 fsb->f_mntonname, buf, buf2); 1452 bcopy(dauuid, uuid, sizeof(dauuid)); 1453 /* remove old UUID from list */ 1454 TAILQ_REMOVE(&ulhead, ulp, ul_list); 1455 free(ulp); 1456 ulp = NULL; 1457 } else { 1458 ulp->ul_exported = 1; 1459 } 1460 } else if (davalid) { 1461 /* 1462 * The UUID wasn't in the list, but DiskArb has a UUID for it. 1463 * (If the DiskArb UUID conflicts with something already in the 1464 * list, we'll need to create a new UUID for it below.) 1465 */ 1466 bcopy(dauuid, uuid, sizeof(dauuid)); 1467 } else { 1468 /* 1469 * We need to create a UUID to use for this volume. 1470 * This is because it wasn't already in the list, and 1471 * either DiskArb didn't have a UUID for the volume or 1472 * the UUID DiskArb has for the volume conflicts with 1473 * a UUID for a volume already in the list. 1474 */ 1475 reportuuid = 1; 1476 cfuuid = CFUUIDCreate(NULL); 1477 uuidbytes = CFUUIDGetUUIDBytes(cfuuid); 1478 bcopy(&uuidbytes, uuid, sizeof(uuidbytes)); 1479 CFRelease(cfuuid); 1480 } 1481 1482 if (!ulp) { 1483 /* 1484 * Add the UUID to the list, but make sure it is unique first. 1485 */ 1486 while ((ulp = find_uuid(uuid))) { 1487 reportuuid = 1; 1488 uuidstring(uuid, buf); 1489 log(LOG_WARNING, "%s UUID conflict with %s, %s", 1490 fsb->f_mntonname, ulp->ul_mntonname, buf); 1491 cfuuid = CFUUIDCreate(NULL); 1492 uuidbytes = CFUUIDGetUUIDBytes(cfuuid); 1493 bcopy(&uuidbytes, uuid, sizeof(uuidbytes)); 1494 CFRelease(cfuuid); 1495 /* double check that the UUID is unique */ 1496 } 1497 ulp = add_uuid_to_list(fsb, (davalid ? dauuid : NULL), uuid); 1498 if (!ulp) { 1499 log(LOG_ERR, "error adding %s", fsb->f_mntonname); 1500 } else { 1501 ulp->ul_exported = 1; 1502 } 1503 } else if (!ulp->ul_mntfromname[0]) { 1504 /* 1505 * If the volume didn't exist when mountd read the 1506 * mountdexptab, it's possible this ulp doesn't 1507 * have a copy of it's mntfromname. So, we make 1508 * sure to grab a copy here before the volume gets 1509 * exported. 1510 */ 1511 strlcpy(ulp->ul_mntfromname, fsb->f_mntfromname, sizeof(ulp->ul_mntfromname)); 1512 } 1513 1514 if (reportuuid) 1515 log(LOG_WARNING, "%s using UUID %s", fsb->f_mntonname, uuidstring(uuid, buf)); 1516 else 1517 DEBUG(1, "%s using UUID %s", fsb->f_mntonname, uuidstring(uuid, buf)); 1518 1519 return (ulp); 1520} 1521 1522/* 1523 * clear export flags on all UUID entries 1524 */ 1525void 1526uuidlist_clearexport(void) 1527{ 1528 struct uuidlist *ulp; 1529 1530 TAILQ_FOREACH(ulp, &ulhead, ul_list) { 1531 ulp->ul_exported = 0; 1532 } 1533} 1534 1535/* convert UUID bytes to UUID string */ 1536#define HEXTOC(c) \ 1537 ((c) >= 'a' ? ((c) - ('a' - 10)) : \ 1538 ((c) >= 'A' ? ((c) - ('A' - 10)) : ((c) - '0'))) 1539#define HEXSTRTOI(p) \ 1540 ((HEXTOC(p[0]) << 4) + HEXTOC(p[1])) 1541char * 1542uuidstring(u_char *uuid, char *string) 1543{ 1544 snprintf(string, (16*2)+4+1, /* XXX silly, yes. But at least we're not using sprintf. [sigh] */ 1545 "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", 1546 uuid[0] & 0xff, uuid[1] & 0xff, uuid[2] & 0xff, uuid[3] & 0xff, 1547 uuid[4] & 0xff, uuid[5] & 0xff, 1548 uuid[6] & 0xff, uuid[7] & 0xff, 1549 uuid[8] & 0xff, uuid[9] & 0xff, 1550 uuid[10] & 0xff, uuid[11] & 0xff, uuid[12] & 0xff, 1551 uuid[13] & 0xff, uuid[14] & 0xff, uuid[15] & 0xff); 1552 return (string); 1553} 1554 1555/* 1556 * save the exported volume UUID list to the mountdexptab file 1557 * 1558 * We have the option of saving all UUIDs in the list, or just 1559 * saving the ones that are currently exported. However, if we 1560 * have a volume exported, then removed from the export list, and 1561 * then added back to the export list, it may be expected that the 1562 * file handles/UUIDs will be the same. But if we don't save what 1563 * the UUIDs were before, we risk the chance of using a different 1564 * UUID for the second export. This can happen if the volume's 1565 * DiskArb UUID is not used for export (because DiskArb doesn't have 1566 * a UUID for it, or because there was a UUID conflict and we needed 1567 * to use a different UUID). 1568 */ 1569void 1570uuidlist_save(void) 1571{ 1572 FILE *ulfile; 1573 struct uuidlist *ulp; 1574 struct expidlist *xid; 1575 char buf[64], buf2[64]; 1576 1577 if ((ulfile = fopen(_PATH_MOUNTEXPLIST, "w")) == NULL) { 1578 log(LOG_ERR, "Can't write %s: %s (%d)", _PATH_MOUNTEXPLIST, 1579 strerror(errno), errno); 1580 return; 1581 } 1582 TAILQ_FOREACH(ulp, &ulhead, ul_list) { 1583#ifdef SAVE_ONLY_EXPORTED_UUIDS 1584 if (!ulp->ul_exported) 1585 continue; 1586#endif 1587 if (ulp->ul_davalid) 1588 uuidstring(ulp->ul_dauuid, buf); 1589 else 1590 strlcpy(buf, "------------------------------------", sizeof(buf)); 1591 uuidstring(ulp->ul_uuid, buf2); 1592 fprintf(ulfile, "%s %s 0x%08X %s\n", buf, buf2, ulp->ul_fsid, ulp->ul_mntonname); 1593 LIST_FOREACH(xid, &ulp->ul_exportids, xid_list) { 1594 fprintf(ulfile, "XID 0x%08X %s\n", xid->xid_id, 1595 ((xid->xid_path[0] == '\0') ? "." : xid->xid_path)); 1596 } 1597 } 1598 fclose(ulfile); 1599} 1600 1601/* 1602 * read in the exported volume UUID list from the mountdexptab file 1603 */ 1604void 1605uuidlist_restore(void) 1606{ 1607 struct uuidlist *ulp; 1608 struct expidlist *xid; 1609 char *cp, str[2*MAXPATHLEN]; 1610 FILE *ulfile; 1611 int i, slen, davalid, uuidchanged; 1612 uint32_t linenum; 1613 struct statfs fsb; 1614 u_char dauuid[16]; 1615 char buf[64], buf2[64]; 1616 1617 if ((ulfile = fopen(_PATH_MOUNTEXPLIST, "r")) == NULL) { 1618 if (errno != ENOENT) 1619 log(LOG_WARNING, "Can't open %s: %s (%d)", _PATH_MOUNTEXPLIST, 1620 strerror(errno), errno); 1621 else 1622 DEBUG(1, "Can't open %s, %s (%d)", _PATH_MOUNTEXPLIST, 1623 strerror(errno), errno); 1624 return; 1625 } 1626 ulp = NULL; 1627 linenum = 0; 1628 while (fgets(str, 2*MAXPATHLEN, ulfile) != NULL) { 1629 linenum++; 1630 slen = strlen(str); 1631 if (str[slen-1] == '\n') 1632 str[slen-1] = '\0'; 1633 if (!strncmp(str, "XID ", 4)) { 1634 /* we have an export ID line for the current UUID */ 1635 if (!ulp) { 1636 log(LOG_ERR, "ignoring orphaned export ID at line %d of %s", 1637 linenum, _PATH_MOUNTEXPLIST); 1638 continue; 1639 } 1640 /* parse XID and add to current UUID */ 1641 xid = malloc(sizeof(*xid)); 1642 if (xid == NULL) { 1643 log(LOG_ERR, "uuidlist_restore: Out of memory"); 1644 exit(2); 1645 } 1646 cp = str + 4; 1647 slen -= 4; 1648 if (sscanf(cp, "%i", &xid->xid_id) != 1) { 1649 log(LOG_ERR, "invalid export ID at line %d of %s", 1650 linenum, _PATH_MOUNTEXPLIST); 1651 free(xid); 1652 continue; 1653 } 1654 while (*cp && (*cp != ' ')) { 1655 cp++; 1656 slen--; 1657 } 1658 cp++; 1659 slen--; 1660 if (slen >= (int)sizeof(xid->xid_path)) { 1661 log(LOG_ERR, "export ID path too long at line %d of %s", 1662 linenum, _PATH_MOUNTEXPLIST); 1663 free(xid); 1664 continue; 1665 } 1666 if ((cp[0] == '.') && (cp[1] == '\0')) 1667 xid->xid_path[0] = '\0'; 1668 else 1669 strlcpy(xid->xid_path, cp, sizeof(xid->xid_path)); 1670 LIST_INSERT_HEAD(&ulp->ul_exportids, xid, xid_list); 1671 continue; 1672 } 1673 ulp = malloc(sizeof(*ulp)); 1674 if (ulp == NULL) { 1675 log(LOG_ERR, "uuidlist_restore: Out of memory"); 1676 exit(2); 1677 } 1678 bzero(ulp, sizeof(*ulp)); 1679 LIST_INIT(&ulp->ul_exportids); 1680 cp = str; 1681 if (*cp == '-') { 1682 /* DiskArb UUID not present */ 1683 ulp->ul_davalid = 0; 1684 bzero(ulp->ul_dauuid, sizeof(ulp->ul_dauuid)); 1685 while (*cp && (*cp != ' ')) 1686 cp++; 1687 } else { 1688 ulp->ul_davalid = 1; 1689 for (i=0; i < (int)sizeof(ulp->ul_dauuid); i++, cp+=2) { 1690 if (*cp == '-') 1691 cp++; 1692 if (!isxdigit(*cp) || !isxdigit(*(cp+1))) { 1693 log(LOG_ERR, "invalid UUID at line %d of %s", 1694 linenum, _PATH_MOUNTEXPLIST); 1695 free(ulp); 1696 ulp = NULL; 1697 break; 1698 } 1699 ulp->ul_dauuid[i] = HEXSTRTOI(cp); 1700 } 1701 } 1702 if (ulp == NULL) 1703 continue; 1704 cp++; 1705 for (i=0; i < (int)sizeof(ulp->ul_uuid); i++, cp+=2) { 1706 if (*cp == '-') 1707 cp++; 1708 if (!isxdigit(*cp) || !isxdigit(*(cp+1))) { 1709 log(LOG_ERR, "invalid UUID at line %d of %s", 1710 linenum, _PATH_MOUNTEXPLIST); 1711 free(ulp); 1712 ulp = NULL; 1713 break; 1714 } 1715 ulp->ul_uuid[i] = HEXSTRTOI(cp); 1716 } 1717 if (ulp == NULL) 1718 continue; 1719 if (*cp != ' ') { 1720 log(LOG_ERR, "invalid entry at line %d of %s", 1721 linenum, _PATH_MOUNTEXPLIST); 1722 free(ulp); 1723 continue; 1724 } 1725 cp++; 1726 if (sscanf(cp, "%i", &ulp->ul_fsid) != 1) { 1727 log(LOG_ERR, "invalid entry at line %d of %s", 1728 linenum, _PATH_MOUNTEXPLIST); 1729 free(ulp); 1730 continue; 1731 } 1732 while (*cp && (*cp != ' ')) 1733 cp++; 1734 if (*cp != ' ') { 1735 log(LOG_ERR, "invalid entry at line %d of %s", 1736 linenum, _PATH_MOUNTEXPLIST); 1737 free(ulp); 1738 continue; 1739 } 1740 cp++; 1741 strncpy(ulp->ul_mntonname, cp, MAXPATHLEN); 1742 ulp->ul_mntonname[MAXPATHLEN-1] = '\0'; 1743 1744 /* verify the path exists and that it is a mount point */ 1745 if (!check_dirpath(ulp->ul_mntonname) || 1746 (statfs(ulp->ul_mntonname, &fsb) < 0) || 1747 strcmp(ulp->ul_mntonname, fsb.f_mntonname)) { 1748 /* don't drop the UUID record if the volume isn't currently mounted! */ 1749 /* If it's mounted/exported later, we want to use the same record. */ 1750 DEBUG(1, "export entry for non-existent file system %s at line %d of %s", 1751 ulp->ul_mntonname, linenum, _PATH_MOUNTEXPLIST); 1752 ulp->ul_mntfromname[0] = '\0'; 1753 TAILQ_INSERT_TAIL(&ulhead, ulp, ul_list); 1754 continue; 1755 } 1756 1757 /* grab the path's mntfromname */ 1758 strncpy(ulp->ul_mntfromname, fsb.f_mntfromname, MAXPATHLEN); 1759 ulp->ul_mntfromname[MAXPATHLEN-1] = '\0'; 1760 1761 /* 1762 * Grab DiskArb's UUID for this volume (if any) and 1763 * see if it has changed. 1764 */ 1765 davalid = get_uuid_from_diskarb(ulp->ul_mntfromname, dauuid); 1766 if (davalid) { 1767 if (!ulp->ul_davalid) 1768 uuidchanged = 1; 1769 else if (bcmp(ulp->ul_dauuid, dauuid, sizeof(dauuid))) 1770 uuidchanged = 1; 1771 else 1772 uuidchanged = 0; 1773 } else { 1774 if (ulp->ul_davalid) { 1775 /* 1776 * We had a UUID before, but now we don't? 1777 * Assume this is just a transient error, 1778 * issue a warning, and stick with the old UUID. 1779 */ 1780 uuidstring(ulp->ul_dauuid, buf); 1781 log(LOG_WARNING, "lost UUID for %s, was %s, keeping old UUID", 1782 fsb.f_mntonname, buf); 1783 uuidchanged = 0; 1784 } else 1785 uuidchanged = 0; 1786 } 1787 if (uuidchanged) { 1788 /* The UUID changed, so we'll drop any entry */ 1789 uuidstring(ulp->ul_dauuid, buf); 1790 if (davalid) 1791 uuidstring(dauuid, buf2); 1792 else 1793 strlcpy(buf2, "------------------------------------", sizeof(buf2)); 1794 log(LOG_WARNING, "UUID changed for %s, was %s now %s", 1795 ulp->ul_mntonname, buf, buf2); 1796 free(ulp); 1797 continue; 1798 } 1799 1800 TAILQ_INSERT_TAIL(&ulhead, ulp, ul_list); 1801 } 1802 fclose(ulfile); 1803} 1804 1805struct expidlist * 1806find_export_id(struct uuidlist *ulp, u_int32_t id) 1807{ 1808 struct expidlist *xid; 1809 1810 LIST_FOREACH(xid, &ulp->ul_exportids, xid_list) { 1811 if (xid->xid_id == id) 1812 break; 1813 } 1814 1815 return (xid); 1816} 1817 1818struct expidlist * 1819get_export_id(struct uuidlist *ulp, char *path) 1820{ 1821 struct expidlist *xid; 1822 u_int32_t maxid = 0; 1823 1824 LIST_FOREACH(xid, &ulp->ul_exportids, xid_list) { 1825 if (!strcmp(xid->xid_path, path)) 1826 break; 1827 if (maxid < xid->xid_id) 1828 maxid = xid->xid_id; 1829 } 1830 if (xid) 1831 return (xid); 1832 /* add it */ 1833 xid = malloc(sizeof(*xid)); 1834 if (!xid) { 1835 log(LOG_ERR, "get_export_id: out of memory"); 1836 return (NULL); 1837 } 1838 bzero(xid, sizeof(*xid)); 1839 strlcpy(xid->xid_path, path, sizeof(xid->xid_path)); 1840 xid->xid_id = maxid + 1; 1841 while (find_export_id(ulp, xid->xid_id)) { 1842 xid->xid_id++; 1843 if (xid->xid_id == maxid) { 1844 /* exhausted export id values! */ 1845 log(LOG_ERR, "export ID values exhausted for %s", 1846 ulp->ul_mntonname); 1847 free(xid); 1848 return (NULL); 1849 } 1850 } 1851 LIST_INSERT_HEAD(&ulp->ul_exportids, xid, xid_list); 1852 return (xid); 1853} 1854 1855/* 1856 * find_exported_fs_by_path_and_uuid() 1857 * 1858 * Given a path and a uuid, find the exported file system that 1859 * best matches both. 1860 */ 1861struct uuidlist * 1862find_exported_fs_by_path_and_uuid(char *fspath, u_char *fsuuid) 1863{ 1864 struct uuidlist *ulp; 1865 1866 ulp = TAILQ_FIRST(&ulhead); 1867 while (ulp) { 1868 /* find next matching uuid */ 1869 while (ulp && fsuuid) { 1870 if (ulp->ul_davalid && !bcmp(ulp->ul_dauuid, fsuuid, sizeof(ulp->ul_dauuid))) 1871 break; 1872 if (!bcmp(ulp->ul_uuid, fsuuid, sizeof(ulp->ul_uuid))) 1873 break; 1874 ulp = TAILQ_NEXT(ulp, ul_list); 1875 } 1876 if (!ulp) 1877 break; 1878 /* we're done if fspath ommitted or matches */ 1879 if (!fspath || !strcmp(ulp->ul_mntonname, fspath)) 1880 break; 1881 ulp = TAILQ_NEXT(ulp, ul_list); 1882 } 1883 1884 return (ulp); 1885} 1886 1887/* 1888 * find_exported_fs_by_dirlist() 1889 * 1890 * Given a list of directories, find the common parent directory and 1891 * the exported file system that directory should be located on. 1892 */ 1893struct uuidlist * 1894find_exported_fs_by_dirlist(struct dirlist *dirhead) 1895{ 1896 struct dirlist *dirl; 1897 char *path, *p; 1898 int cmp, bestlen, len; 1899 struct uuidlist *ulp, *bestulp; 1900 1901 if (!dirhead) 1902 return (NULL); 1903 1904 path = strdup(dirhead->dl_dir); 1905 1906 dirl = dirhead->dl_next; 1907 while (dirl) { 1908 cmp = subdir_check(path, dirl->dl_dir); 1909 if (cmp >= 0) { 1910 /* same or subdir, so skip */ 1911 dirl = dirl->dl_next; 1912 continue; 1913 } 1914 p = strrchr(path, '/'); 1915 if (p == path) { 1916 /* hit root */ 1917 p[1] = '\0'; 1918 break; 1919 } 1920 p[0] = '\0'; 1921 } 1922 DEBUG(4, "find_exported_fs: %s", path); 1923 1924 /* 1925 * Now search uuid list for best match. 1926 * We're looking for the longest mntonname that this path matches. 1927 */ 1928 bestulp = NULL; 1929 bestlen = -1; 1930 TAILQ_FOREACH(ulp, &ulhead, ul_list) { 1931 if (subdir_check(ulp->ul_mntonname, path) < 0) 1932 continue; 1933 len = strlen(ulp->ul_mntonname); 1934 if (len > bestlen) { 1935 bestulp = ulp; 1936 bestlen = strlen(ulp->ul_mntonname); 1937 } 1938 } 1939 1940 free(path); 1941 DEBUG(4, "find_exported_fs: best exported fs: %s", bestulp ? bestulp->ul_mntonname : "<none>"); 1942 return (bestulp); 1943} 1944 1945/* 1946 * subdir_check() 1947 * 1948 * Compares two pathname strings to see if one is a subdir of the other. 1949 * Returns: 1950 * 1 if second path is a subdir of the first path 1951 * 0 if the paths are the same 1952 * -1 if the paths have just a substring match 1953 * -2 if the paths do not match at all 1954 * -3 if second path could not be a subdir of the first path (due to length) 1955 */ 1956int 1957subdir_check(char *s1, char *s2) 1958{ 1959 int len1, len2, rv; 1960 len1 = strlen(s1); 1961 len2 = strlen(s2); 1962 if (len1 > len2) 1963 rv = -3; 1964 else if (strncmp(s1, s2, len1)) 1965 rv = -2; 1966 else if (len1 == len2) 1967 rv = 0; 1968 else if ((s2[len1] == '/') || (len1 == 1)) 1969 rv = 1; 1970 else 1971 rv = -1; 1972 DEBUG(4, "subdir_check: %s %s %d", s1, s2, rv); 1973 return rv; 1974} 1975 1976char *line = NULL; 1977uint32_t linesize = 0; 1978FILE *exp_file; 1979uint32_t linenum, entrylines; 1980 1981/* 1982 * Get the export list 1983 */ 1984int 1985get_exportlist(void) 1986{ 1987 struct expfs *xf, *xf2, *xf3; 1988 struct grouplist ngrp, *grp, *tgrp, tmpgrp; 1989 struct expdir *xd, *xd2, *xd3; 1990 struct expidlist *xid; 1991 struct dirlist *dirhead, *dirl, *dirl2; 1992 struct namelisttqh names, netgroups; 1993 struct namelist *nl; 1994 struct host *ht, *ht2, *ht3; 1995 struct hosttqh htfree; 1996 struct statfs fsb; 1997 struct xucred anon; 1998 struct nfs_sec secflavs; 1999 char *cp, *endcp, *name, *word, *hst, *usr, *dom, savedc, *savedcp, *subdir, *mntonname, *word2; 2000 int len, dlen, hostcount, badhostcount, exflags, got_nondir, netgrp, cmp, show; 2001 char fspath[MAXPATHLEN]; 2002 u_char uuid[16], fsuuid[16]; 2003 struct uuidlist *ulp, *bestulp; 2004 char buf[64], buf2[64]; 2005 int error, opt_flags, need_export, saved_errors; 2006 2007 lock_exports(); 2008 2009 /* 2010 * First, tag all existing export/group structures for deletion 2011 */ 2012 TAILQ_FOREACH(xf, &xfshead, xf_next) { 2013 TAILQ_FOREACH(xd, &xf->xf_dirl, xd_next) { 2014 xd->xd_iflags &= ~(OP_ADD|OP_OFFLINE|OP_ONLINE); 2015 xd->xd_ssec.count = 0; 2016 xd->xd_oflags = xd->xd_flags; 2017 xd->xd_ocred = xd->xd_cred; 2018 xd->xd_osec = xd->xd_sec; 2019 xd->xd_flags |= OP_DEL; 2020 TAILQ_FOREACH(ht, &xd->xd_hosts, ht_next) 2021 ht->ht_flags |= OP_DEL; 2022 TAILQ_FOREACH(xd2, &xd->xd_mountdirs, xd_next) { 2023 xd2->xd_flags |= OP_DEL; 2024 TAILQ_FOREACH(ht, &xd2->xd_hosts, ht_next) 2025 ht->ht_flags |= OP_DEL; 2026 } 2027 } 2028 } 2029 uuidlist_clearexport(); 2030 2031 if (xpaths) { 2032 free_dirlist(xpaths); 2033 xpaths = NULL; 2034 } 2035 xpaths_complete = 1; 2036 2037 TAILQ_INIT(&names); 2038 TAILQ_INIT(&netgroups); 2039 hostnamecount = hostnamegoodcount = 0; 2040 missingexportcount = 0; 2041 export_errors = 0; 2042 linenum = 0; 2043 2044 if ((exp_file = fopen(exportsfilepath, "r")) == NULL) { 2045 log(LOG_WARNING, "Can't open %s", exportsfilepath); 2046 export_errors = 1; 2047 goto exports_read; 2048 } 2049 2050 /* 2051 * Read in the exports and build the list, calling nfssvc(NFSSVC_EXPORT) 2052 * as we go along to push the NEW export rules into the kernel. 2053 */ 2054 2055 dirhead = NULL; 2056 while (get_export_entry()) { 2057 /* 2058 * Create new exports list entry 2059 */ 2060 DEBUG(1, "---> Got line: %s", line); 2061 2062 /* 2063 * Set defaults. 2064 */ 2065 saved_errors = export_errors; 2066 hostcount = badhostcount = 0; 2067 anon = def_anon; 2068 exflags = 0; 2069 opt_flags = 0; 2070 got_nondir = 0; 2071 xf = NULL; 2072 ulp = NULL; 2073 fspath[0] = '\0'; 2074 bzero(fsuuid, sizeof(fsuuid)); 2075 2076 /* init group list and net group */ 2077 tgrp = NULL; 2078 bzero(&ngrp, sizeof(ngrp)); 2079 2080 /* init default security flavor */ 2081 bzero(&secflavs, sizeof(secflavs)); 2082 secflavs.flavors[0] = RPCAUTH_SYS; 2083 secflavs.count = 1; 2084 2085 /* 2086 * process all the fields in this line 2087 * (we loop until nextfield finds nothing) 2088 */ 2089 cp = line; 2090 nextfield(&cp, &endcp); 2091 if (*cp == '#') 2092 goto nextline; 2093 2094 while (endcp > cp) { 2095 DEBUG(2, "got field: %.*s", endcp-cp, cp); 2096 if (*cp == '-') { 2097 /* 2098 * looks like we have some options 2099 */ 2100 if (dirhead == NULL) { 2101 export_error(LOG_ERR, "got options with no exported directory: %s", cp); 2102 export_error_cleanup(xf); 2103 goto nextline; 2104 } 2105 DEBUG(3, "processing option: %.*s", endcp-cp, cp); 2106 got_nondir = 1; 2107 savedcp = cp; 2108 if (do_opt(&cp, &endcp, &ngrp, &hostcount, &opt_flags, 2109 &exflags, &anon, &secflavs, fspath, fsuuid)) { 2110 export_error(LOG_ERR, "error processing options: %s", savedcp); 2111 export_error_cleanup(xf); 2112 goto nextline; 2113 } 2114 } else if ((*cp == '/') || ((*cp == '\'' || *cp == '"') && (*(cp+1) == '/'))) { 2115 /* 2116 * looks like we have a pathname 2117 */ 2118 DEBUG(2, "processing pathname: %.*s", endcp-cp, cp); 2119 word = clean_pathname(cp); 2120 DEBUG(3, " cleaned pathname: %s", word); 2121 if (word == NULL) { 2122 export_error(LOG_ERR, "error processing pathname (out of memory)"); 2123 export_error_cleanup(xf); 2124 goto nextline; 2125 } 2126 if (got_nondir) { 2127 export_error(LOG_ERR, "Directories must be first: %s", word); 2128 export_error_cleanup(xf); 2129 free(word); 2130 goto nextline; 2131 } 2132 if (strlen(word) > RPCMNT_NAMELEN) { 2133 export_error(LOG_ERR, "pathname too long (%d > %d): %s", 2134 strlen(word), RPCMNT_NAMELEN, word); 2135 export_error_cleanup(xf); 2136 free(word); 2137 goto nextline; 2138 } 2139 /* Add path to this global exported path list */ 2140 word2 = strdup(word); 2141 if ((error = add_dir(&xpaths, word2))) 2142 free(word2); 2143 if (error == ENOMEM) { 2144 log(LOG_WARNING, "Can't allocate memory to add export path: %s", word); 2145 xpaths_complete = 0; 2146 } 2147 /* Add path to this line's directory list */ 2148 error = add_dir(&dirhead, word); 2149 if (error == EEXIST) { 2150 export_error(LOG_WARNING, "duplicate directory: %s", word); 2151 free(word); 2152 } else if (error == ENOMEM) { 2153 export_error(LOG_ERR, "Can't allocate memory to add directory: %s", word); 2154 export_error_cleanup(xf); 2155 free(word); 2156 goto nextline; 2157 } 2158 } else { 2159 /* 2160 * looks like we have a host/netgroup 2161 */ 2162 savedc = *endcp; 2163 *endcp = '\0'; 2164 DEBUG(2, "got host/netgroup: %s", cp); 2165 got_nondir = 1; 2166 if (dirhead == NULL) { 2167 export_error(LOG_ERR, "got host/group with no directory?: %s", cp); 2168 export_error_cleanup(xf); 2169 goto nextline; 2170 } 2171 2172 /* add it to the name list */ 2173 error = add_name(&names, cp); 2174 if (error == ENOMEM) { 2175 export_error(LOG_ERR, "Can't allocate memory to add host/group: %s", cp); 2176 export_error_cleanup(xf); 2177 goto nextline; 2178 } 2179 /* iterate name list until it's empty (first name gets GF_SHOW) */ 2180 show = 1; 2181 while ((nl = TAILQ_FIRST(&names))) { 2182 TAILQ_REMOVE(&names, nl, nl_next); 2183 name = nl->nl_name; 2184 free(nl); 2185 /* check if name is a netgroup */ 2186 setnetgrent(name); 2187 netgrp = getnetgrent(&hst, &usr, &dom); 2188 if (netgrp) { 2189 DEBUG(3, "got netgroup: %s", name); 2190 error = add_name(&netgroups, name); 2191 if (error == ENOMEM) { 2192 export_error(LOG_ERR, "Can't allocate memory to add host/group: %s", cp); 2193 export_error_cleanup(xf); 2194 endnetgrent(); 2195 free(name); 2196 goto nextline; 2197 } 2198 if (show) { 2199 /* add an entry for the netgroup (w/GF_SHOW) */ 2200 DEBUG(3, "add netgroup w/show: %s", name); 2201 show = 0; 2202 bzero(&tmpgrp, sizeof(tmpgrp)); 2203 tmpgrp.gr_type = GT_NETGROUP; 2204 tmpgrp.gr_flags = GF_SHOW; 2205 tmpgrp.gr_u.gt_netgroup = strdup(name); 2206 if (!tmpgrp.gr_u.gt_netgroup || !(grp = get_grp(&tmpgrp))) { 2207 export_error(LOG_ERR, "Can't allocate memory to add netgroup - %s", name); 2208 export_error_cleanup(xf); 2209 endnetgrent(); 2210 free(name); 2211 goto nextline; 2212 } else if (!add_grp(&tgrp, grp)) { 2213 DEBUG(3, "duplicate netgroup: %s", name); 2214 free_grp(grp); 2215 } 2216 } 2217 if (error == -1) { 2218 /* already in the netgroup list, skip it */ 2219 DEBUG(3, "netgroup already in netgroup list: %s", name); 2220 endnetgrent(); 2221 free(name); 2222 continue; 2223 } 2224 /* enumerate the netgroup, adding each name to the name list */ 2225 do { 2226 DEBUG(3, "add netgroup member: %s", hst); 2227 error = add_name(&names, hst); 2228 if (error == ENOMEM) { 2229 export_error(LOG_ERR, "Can't allocate memory to add netgroup:host - %s", name, hst); 2230 export_error_cleanup(xf); 2231 endnetgrent(); 2232 free(name); 2233 goto nextline; 2234 } 2235 } while (getnetgrent(&hst, &usr, &dom)); 2236 endnetgrent(); 2237 free(name); 2238 continue; 2239 } 2240 endnetgrent(); 2241 /* not a netgroup, add a host/net entry */ 2242 bzero(&tmpgrp, sizeof(tmpgrp)); 2243 if (get_host_addresses(name, &tmpgrp)) { 2244 export_error(LOG_WARNING, "couldn't get address for host: %s", name); 2245 badhostcount++; 2246 } else { 2247 DEBUG(3, "got host: %s", name); 2248 if (show) { 2249 show = 0; 2250 tmpgrp.gr_flags |= GF_SHOW; 2251 } 2252 grp = get_grp(&tmpgrp); 2253 if (!grp) { 2254 export_error(LOG_ERR, "Can't allocate memory to add host - %s", name); 2255 } else if (!add_grp(&tgrp, grp)) { 2256 DEBUG(3, "duplicate host: %s", name); 2257 free_grp(grp); 2258 } else { 2259 hostcount++; 2260 } 2261 } 2262 free(name); 2263 } 2264 2265 *endcp = savedc; 2266 } 2267 cp = endcp; 2268 nextfield(&cp, &endcp); 2269 } 2270 2271 /* 2272 * Done parsing through export entry fields. 2273 */ 2274 2275 /* check options and hosts */ 2276 if (!(opt_flags & (OP_MAPROOT|OP_MAPALL))) { 2277 /* If no mapping option specified, map root by default */ 2278 exflags |= NX_MAPROOT; 2279 opt_flags |= OP_MAPROOT; 2280 } 2281 if (check_options(opt_flags)) { 2282 export_error(LOG_ERR, "bad export options"); 2283 export_error_cleanup(xf); 2284 goto nextline; 2285 } 2286 if (!hostcount) { 2287 if (badhostcount) { 2288 export_error(LOG_ERR, "no valid hosts found for export"); 2289 export_error_cleanup(xf); 2290 goto nextline; 2291 } 2292 DEBUG(1, "default export"); 2293 } else if (opt_flags & OP_NET) { 2294 if (tgrp) { 2295 /* 2296 * Don't allow a network export coincide with a list of 2297 * host(s) on the same line. 2298 */ 2299 export_error(LOG_ERR, "can't specify both network and hosts on same line"); 2300 export_error_cleanup(xf); 2301 goto nextline; 2302 } 2303 ngrp.gr_flags |= GF_SHOW; 2304 tgrp = get_grp(&ngrp); 2305 if (!tgrp) { 2306 export_error(LOG_ERR, "Can't allocate memory to add network - %s", 2307 grp_name(&ngrp)); 2308 export_error_cleanup(xf); 2309 goto nextline; 2310 } 2311 ngrp.gr_type = GT_NULL; 2312 } 2313 2314 /* check directory list */ 2315 if (dirhead == NULL) { /* sanity check */ 2316 export_error(LOG_ERR, "no directories for export entry?"); 2317 export_error_cleanup(xf); 2318 goto nextline; 2319 } 2320 2321 mntonname = NULL; 2322 if (opt_flags & (OP_FSPATH|OP_FSUUID)) 2323 bestulp = find_exported_fs_by_path_and_uuid( 2324 (opt_flags & OP_FSPATH) ? fspath : NULL, 2325 (opt_flags & OP_FSUUID) ? fsuuid : NULL); 2326 else 2327 bestulp = find_exported_fs_by_dirlist(dirhead); 2328 2329 dirl = dirhead; 2330 /* Look for an exported directory that passes check_dirpath() */ 2331 while (dirl && !check_dirpath(dirl->dl_dir)) { 2332 export_error(LOG_WARNING, "path contains non-directory or non-existent components: %s", dirl->dl_dir); 2333 /* skip subdirectories */ 2334 dirl2 = dirl->dl_next; 2335 while (dirl2 && (subdir_check(dirl->dl_dir, dirl2->dl_dir) == 1)) 2336 dirl2 = dirl2->dl_next; 2337 dirl = dirl2; 2338 } 2339 if (!dirl) { 2340 if (!bestulp) { 2341 export_error(LOG_ERR, "no usable directories in export entry and no fallback"); 2342 export_error_cleanup(xf); 2343 goto nextline; 2344 } 2345 export_error(LOG_WARNING, "no usable directories in export entry"); 2346 goto prepare_offline_export; 2347 } 2348 if (statfs(dirl->dl_dir, &fsb) < 0) { 2349 export_error(LOG_ERR, "statfs failed (%s (%d)) for path: %s", 2350 strerror(errno), errno, dirl->dl_dir); 2351 export_error_cleanup(xf); 2352 goto nextline; 2353 } 2354 if ((opt_flags & OP_FSPATH) && strcmp(fsb.f_mntonname, fspath)) { 2355 /* fspath doesn't match export fs path? */ 2356 if (!bestulp) { 2357 export_error(LOG_ERR, "file system path (%s) does not match fspath (%s) and no fallback", 2358 fsb.f_mntonname, fspath); 2359 export_error_cleanup(xf); 2360 goto nextline; 2361 } 2362 export_error(LOG_WARNING, "file system path (%s) does not match fspath (%s)", 2363 fsb.f_mntonname, fspath); 2364 goto prepare_offline_export; 2365 } 2366 if (bestulp && (subdir_check(fsb.f_mntonname, bestulp->ul_mntonname) > 0)) { 2367 export_error(LOG_WARNING, "Exported file system (%s) doesn't match best guess (%s).", 2368 fsb.f_mntonname, bestulp->ul_mntonname); 2369 if (!(opt_flags & (OP_FSUUID|OP_FSPATH))) 2370 export_error(LOG_WARNING, "Suggest using fspath=/path and/or fsuuid=uuid to disambiguate."); 2371 goto prepare_offline_export; 2372 } 2373 if (!(ulp = get_uuid(&fsb, uuid))) { 2374 export_error(LOG_ERR, "couldn't get UUID for volume: %s", fsb.f_mntonname); 2375 export_error_cleanup(xf); 2376 goto nextline; 2377 } 2378 if ((opt_flags & OP_FSUUID) && bcmp(uuid, fsuuid, sizeof(fsuuid))) { 2379 /* fsuuid doesn't match export fs uuid? */ 2380 if (!bestulp) { 2381 export_error(LOG_ERR, "file system UUID (%s) does not match fsuuid (%s) and no fallback", 2382 uuidstring(uuid, buf), uuidstring(fsuuid, buf2)); 2383 export_error_cleanup(xf); 2384 goto nextline; 2385 } 2386 export_error(LOG_WARNING, "file system UUID (%s) does not match fsuuid (%s)", 2387 uuidstring(uuid, buf), uuidstring(fsuuid, buf2)); 2388 goto prepare_offline_export; 2389 } 2390 2391 if (bestulp && (opt_flags & OP_MISSING)) { 2392prepare_offline_export: 2393 missingexportcount++; 2394 export_error(LOG_WARNING, "using fallback (marked offline): %s", bestulp->ul_mntonname); 2395 exflags |= NX_OFFLINE; 2396 opt_flags |= OP_MISSING; 2397 ulp = bestulp; 2398 mntonname = ulp->ul_mntonname; 2399 bcopy(ulp->ul_uuid, uuid, sizeof(uuid)); 2400 } else { 2401 mntonname = fsb.f_mntonname; 2402 } 2403 2404 /* See if this directory is already in the export list. */ 2405 xf = ex_search(uuid); 2406 if (xf == NULL) { 2407 xf = get_expfs(); 2408 if (xf) 2409 xf->xf_fsdir = strdup(mntonname); 2410 if (!xf || !xf->xf_fsdir) { 2411 export_error(LOG_ERR, "Can't allocate memory to export volume: %s", 2412 mntonname); 2413 export_error_cleanup(xf); 2414 goto nextline; 2415 } 2416 bcopy(uuid, xf->xf_uuid, sizeof(uuid)); 2417 xf->xf_fsid = ulp->ul_fsid; 2418 DEBUG(2, "New expfs uuid=%s", uuidstring(uuid, buf)); 2419 } else { 2420 DEBUG(2, "Found expfs uuid=%s", uuidstring(uuid, buf)); 2421 } 2422 2423 /* verify the rest of the directories in the list are kosher */ 2424 if (dirl) 2425 dirl = dirl->dl_next; 2426 for (; dirl; dirl = dirl->dl_next) { 2427 DEBUG(2, "dir: %s", dirl->dl_dir); 2428 if (!check_dirpath(dirl->dl_dir)) { 2429 export_error(LOG_WARNING, "path contains non-directory or non-existent components: %s", dirl->dl_dir); 2430 continue; 2431 } 2432 if (statfs(dirl->dl_dir, &fsb) < 0) { 2433 export_error(LOG_WARNING, "statfs failed (%s (%d)) for path: %s", 2434 strerror(errno), errno, dirl->dl_dir); 2435 continue; 2436 } 2437 if (strcmp(xf->xf_fsdir, fsb.f_mntonname)) { 2438 export_error(LOG_WARNING, "Volume mismatch for: %s\ndirectories must be on same volume", dirl->dl_dir); 2439 continue; 2440 } 2441 } 2442 2443 /* 2444 * Done processing exports line fields. 2445 */ 2446 2447 /* 2448 * Walk the dirlist. 2449 * Verify the next dir is (or can be) an exported directory. 2450 * Check subsequent dirs to see if they are mount subdirs of that dir. 2451 */ 2452 dirl = dirhead; 2453 while (dirl) { 2454 DEBUG(2, "dir: %s", dirl->dl_dir); 2455 /* 2456 * Check for nesting conflicts with any existing entries. 2457 * Note we skip any entries that are NOT marked OP_ADD because 2458 * we don't care about directories that are tagged for deletion. 2459 */ 2460 TAILQ_FOREACH(xd2, &xf->xf_dirl, xd_next) { 2461 if ((xd2->xd_iflags & OP_ADD) && 2462 ((subdir_check(xd2->xd_dir, dirl->dl_dir) == 1) || 2463 (subdir_check(dirl->dl_dir, xd2->xd_dir) == 1))) { 2464 export_error(LOG_ERR, "%s conflicts with existing export %s", 2465 dirl->dl_dir, xd2->xd_dir); 2466 export_error_cleanup(xf); 2467 goto nextline; 2468 } 2469 } 2470 /* 2471 * Scan exported file system for a matching exported directory 2472 * or at least the insertion point of a new one. 2473 */ 2474 len = strlen(dirl->dl_dir); 2475 xd3 = NULL; 2476 cmp = 1; 2477 TAILQ_FOREACH(xd2, &xf->xf_dirl, xd_next) { 2478 dlen = strlen(xd2->xd_dir); 2479 cmp = strncmp(dirl->dl_dir, xd2->xd_dir, dlen); 2480 DEBUG(3, " %s compare %s %d", dirl->dl_dir, 2481 xd2->xd_dir, cmp); 2482 if (!cmp) { 2483 if (len == dlen) /* found an exact match */ 2484 break; 2485 /* dirl was actually longer than xd2 */ 2486 cmp = 1; 2487 } 2488 if (cmp > 0) 2489 break; 2490 xd3 = xd2; 2491 } 2492 2493 if (!cmp) { 2494 xd = xd2; 2495 DEBUG(2, " %s xd is %s", dirl->dl_dir, xd->xd_dir); 2496 } else { 2497 /* go ahead and create a new expdir structure */ 2498 if (strncmp(dirl->dl_dir, mntonname, strlen(mntonname))) { 2499 export_error(LOG_ERR, "exported dir/fs mismatch: %s %s", 2500 dirl->dl_dir, mntonname); 2501 export_error_cleanup(xf); 2502 goto nextline; 2503 } 2504 /* first, get export path and ID */ 2505 /* point subdir beyond mount path string */ 2506 subdir = dirl->dl_dir + strlen(mntonname); 2507 /* skip "/" between mount and subdir */ 2508 while (*subdir && (*subdir == '/')) 2509 subdir++; 2510 xid = get_export_id(ulp, subdir); 2511 if (!xid) { 2512 export_error(LOG_ERR, "unable to get export ID for %s", dirl->dl_dir); 2513 export_error_cleanup(xf); 2514 goto nextline; 2515 } 2516 xd = get_expdir(); 2517 if (xd) 2518 xd->xd_dir = strdup(dirl->dl_dir); 2519 if (!xd || !xd->xd_dir) { 2520 if (xd) 2521 free_expdir(xd); 2522 export_error(LOG_ERR, "can't allocate memory for export %s", dirl->dl_dir); 2523 export_error_cleanup(xf); 2524 goto nextline; 2525 } 2526 xd->xd_xid = xid; 2527 DEBUG(2, " %s new xd", xd->xd_dir); 2528 } 2529 2530 /* preflight the addition of these new export options */ 2531 if (hang_options_setup(xd, opt_flags, &anon, tgrp, &secflavs, &need_export)) { 2532 export_error(LOG_ERR, "export option conflict for %s", xd->xd_dir); 2533 /* XXX what to do about already successful exports? */ 2534 hang_options_cleanup(xd); 2535 if (cmp) 2536 free_expdir(xd); 2537 export_error_cleanup(xf); 2538 goto nextline; 2539 } 2540 2541 /* 2542 * Send list of hosts to do_export for pushing the exports into 2543 * the kernel (unless checkexports and the export is missing). 2544 */ 2545 if (need_export && !(checkexports && (opt_flags & OP_MISSING))) { 2546 int expcmd = checkexports ? NXA_CHECK : NXA_REPLACE; 2547 if (do_export(expcmd, xf, xd, tgrp, exflags, &anon, &secflavs)) { 2548 if ((errno == ENOTSUP) || (errno == EISDIR)) { 2549 /* if ENOTSUP report lack of NFS export support */ 2550 /* if EISDIR report lack of extended readdir support */ 2551 export_error(LOG_ERR, "kernel export registration failed: " 2552 "NFS exporting not supported by fstype \"%s\" (%s)", 2553 statfs(xf->xf_fsdir, &fsb) ? "?" : fsb.f_fstypename, 2554 (errno == EISDIR) ? "readdir" : "fh"); 2555 } else { 2556 export_error(LOG_ERR, "kernel export registration failed"); 2557 } 2558 hang_options_cleanup(xd); 2559 if (cmp) 2560 free_expdir(xd); 2561 export_error_cleanup(xf); 2562 goto nextline; 2563 } 2564 /* Success. Update the data structures. */ 2565 DEBUG(1, "kernel export registered for %s/%s", xf->xf_fsdir, xd->xd_xid->xid_path); 2566 } else { 2567 DEBUG(2, "kernel export already registered for %s/%s", xf->xf_fsdir, xd->xd_xid->xid_path); 2568 } 2569 2570 /* add mount subdirectories of this directory */ 2571 dirl2 = dirl->dl_next; 2572 while (dirl2) { 2573 if (subdir_check(dirl->dl_dir, dirl2->dl_dir) != 1) 2574 break; 2575 error = hang_options_mountdir(xd, dirl2->dl_dir, opt_flags, tgrp, &secflavs); 2576 if (error == EEXIST) { 2577 export_error(LOG_WARNING, "mount subdirectory export option conflict for %s", 2578 dirl2->dl_dir); 2579 } else if (error == ENOMEM) { 2580 export_error(LOG_WARNING, "unable to add mount subdirectory for %s, %s", 2581 xd->xd_dir, dirl2->dl_dir); 2582 } 2583 dirl2 = dirl2->dl_next; 2584 } 2585 dirl = dirl2; 2586 2587 /* finalize export option additions */ 2588 hang_options_finalize(xd); 2589 2590 /* mark that we've added exports to this xd */ 2591 xd->xd_iflags |= OP_ADD; 2592 2593 if (cmp) { 2594 /* add new expdir to xf */ 2595 if (xd3) { 2596 TAILQ_INSERT_AFTER(&xf->xf_dirl, xd3, xd, xd_next); 2597 } else { 2598 TAILQ_INSERT_HEAD(&xf->xf_dirl, xd, xd_next); 2599 } 2600 } 2601 2602 } 2603 2604 if ((xf->xf_flag & XF_LINKED) == 0) { 2605 /* Insert in the list in alphabetical order. */ 2606 xf3 = NULL; 2607 TAILQ_FOREACH(xf2, &xfshead, xf_next) { 2608 if (strcmp(xf->xf_fsdir, xf2->xf_fsdir) < 0) 2609 break; 2610 xf3 = xf2; 2611 } 2612 if (xf3) { 2613 TAILQ_INSERT_AFTER(&xfshead, xf3, xf, xf_next); 2614 } else { 2615 TAILQ_INSERT_HEAD(&xfshead, xf, xf_next); 2616 } 2617 xf->xf_flag |= XF_LINKED; 2618 } 2619 2620 if (export_errors == saved_errors) { 2621 /* no errors, clear any previous errors for this entry */ 2622 if (clear_export_errors(linenum)) 2623 log(LOG_WARNING, "exports:%d: export entry OK (previous errors cleared)", linenum); 2624 } 2625nextline: 2626 if (!TAILQ_EMPTY(&netgroups)) 2627 free_namelist(&netgroups); 2628 if (!TAILQ_EMPTY(&names)) 2629 free_namelist(&names); 2630 if (dirhead) { 2631 free_dirlist(dirhead); 2632 dirhead = NULL; 2633 } 2634 /* release groups */ 2635 switch (ngrp.gr_type) { 2636 case GT_NET: 2637 if (ngrp.gr_u.gt_net.nt_name) 2638 free(ngrp.gr_u.gt_net.nt_name); 2639 break; 2640 } 2641 while (tgrp) { 2642 grp = tgrp; 2643 tgrp = tgrp->gr_next; 2644 grp->gr_flags &= ~GF_SHOW; 2645 free_grp(grp); 2646 } 2647 } 2648 2649 fclose(exp_file); 2650 2651 if (config.verbose >= 5) { 2652 DEBUG(3, "========> get_exportlist() CURRENT EXPORTS UPDATED:"); 2653 dump_exports(); 2654 } 2655 2656exports_read: 2657 /* 2658 * Now, find all existing structures still tagged for deletion. 2659 * For each tagged group found, call nfssvc(NXA_DELETE) to delete 2660 * the exports for the addresses that haven't had new/replacement 2661 * exports registered. We simply scan the current exports for 2662 * an untagged match for each address. If an exported directory 2663 * loses all of its exports, we delete that expdir. 2664 */ 2665 xf = TAILQ_FIRST(&xfshead); 2666 while (xf && !checkexports) { 2667 xd = TAILQ_FIRST(&xf->xf_dirl); 2668 while (xd) { 2669 /* 2670 * First check the list of mountdirs. Since the kernel 2671 * is not aware of these and they are not registered, 2672 * we merely need to delete the data structures. 2673 */ 2674 TAILQ_FOREACH_SAFE(xd2, &xd->xd_mountdirs, xd_next, xd3) { 2675 TAILQ_FOREACH_SAFE(ht, &xd2->xd_hosts, ht_next, ht2) 2676 if (ht->ht_flags & OP_DEL) { 2677 TAILQ_REMOVE(&xd2->xd_hosts, ht, ht_next); 2678 free_host(ht); 2679 } 2680 if (xd2->xd_flags & OP_DEL) 2681 xd2->xd_flags = xd2->xd_oflags = 0; 2682 if (!(xd2->xd_flags & OP_DEFEXP) && TAILQ_EMPTY(&xd2->xd_hosts)) { 2683 /* No more exports here, delete */ 2684 TAILQ_REMOVE(&xd->xd_mountdirs, xd2, xd_next); 2685 free_expdir(xd2); 2686 } 2687 } 2688 /* 2689 * Now scan the xd_hosts list for hosts that are still 2690 * tagged for deletion and move those to the htfree list. 2691 */ 2692 TAILQ_INIT(&htfree); 2693 TAILQ_FOREACH_SAFE(ht, &xd->xd_hosts, ht_next, ht2) 2694 if (ht->ht_flags & OP_DEL) { 2695 TAILQ_REMOVE(&xd->xd_hosts, ht, ht_next); 2696 TAILQ_INSERT_TAIL(&htfree, ht, ht_next); 2697 } 2698 /* 2699 * Go through htfree and find the groups/addresses 2700 * that are no longer being exported to and place 2701 * those groups/addresses in the list tgrp. 2702 * The hosts and groups/addresses that have been 2703 * replaced with newer exports (and thus don't require 2704 * deletion from the kernel) will be freed as we go. 2705 */ 2706 tgrp = NULL; 2707 TAILQ_FOREACH_SAFE(ht, &htfree, ht_next, ht2) { 2708 grp = ht->ht_grp; 2709 if (grp->gr_type == GT_NETGROUP) { 2710 /* 2711 * netgroup entries should just be freed up now with the host. 2712 * Don't bother trying to match up because they won't. 2713 */ 2714 } else if (!find_group_address_match_in_host_list(&xd->xd_hosts, grp)) { 2715 /* no conflicts, so we can safely delete these */ 2716 /* steal the grp from the host */ 2717 ht->ht_grp = NULL; 2718 if (!add_grp(&tgrp, grp)) { 2719 /* shouldn't happen... */ 2720 log(LOG_ERR, "failure to queue group for export deletion"); 2721 /* ... but try to recover anyway */ 2722 grp->gr_next = tgrp; 2723 tgrp = grp; 2724 } 2725 } else if (grp->gr_type == GT_HOST) { 2726 struct addrinfo *a1, *a2, *prea3, *a3, atmp, *anexttmp; 2727 /* 2728 * Some or all of the addresses are still exported. 2729 * Find any addresses whose exports haven't been replaced. 2730 * a3 points to the location of the next address we will 2731 * want to delete the export for. a1 walks the array 2732 * and a3 follows along/behind essentially compacting the 2733 * array of addresses into only those that we want to 2734 * delete exports for. The effect is that addresses which 2735 * are still exported will be "squeezed" out of the array. 2736 */ 2737 a1 = grp->gr_u.gt_hostinfo.h_ailist; 2738 prea3 = NULL; 2739 a3 = a1; 2740 while (a1) { 2741 /* scan exports host list for GT_HOSTs w/matching address */ 2742 TAILQ_FOREACH(ht3, &xd->xd_hosts, ht_next) { 2743 if (ht3->ht_grp->gr_type != GT_HOST) 2744 continue; 2745 a2 = ht3->ht_grp->gr_u.gt_hostinfo.h_ailist; 2746 while (a2 && addrinfo_cmp(a1, a2)) 2747 a2 = a2->ai_next; 2748 if (a2) /* we found a match */ 2749 break; 2750 } 2751 if (!ht3) { 2752 /* didn't find address, so "add" it to the array */ 2753 if (a3 != a1) { 2754 /* switch all elements except ai_next */ 2755 atmp = *a3; 2756 anexttmp = a3->ai_next; 2757 *a3 = *a1; 2758 a3->ai_next = anexttmp; 2759 atmp.ai_next = a1->ai_next; 2760 *a1 = atmp; 2761 } 2762 prea3 = a3; 2763 a3 = a3->ai_next; 2764 } 2765 a1 = a1->ai_next; 2766 } 2767 if (a3 == grp->gr_u.gt_hostinfo.h_ailist) { 2768 /* a3 hasn't moved, so we know that all of */ 2769 /* the addresses are being exported again */ 2770 /* so we shouldn't delete any of the exports */ 2771 } else { 2772 /* some of the addresses are being exported again */ 2773 /* we've compacted the list of addresses that aren't */ 2774 /* and here we will free up the rest of them */ 2775 if (prea3) 2776 prea3->ai_next = NULL; 2777 freeaddrinfo(a3); 2778 /* steal the grp from the host */ 2779 ht->ht_grp = NULL; 2780 if (!add_grp(&tgrp, grp)) { 2781 /* shouldn't happen... */ 2782 log(LOG_ERR, "failure to queue group for export deletion"); 2783 /* ... but try to recover anyway */ 2784 grp->gr_next = tgrp; 2785 tgrp = grp; 2786 } 2787 } 2788 } 2789 TAILQ_REMOVE(&htfree, ht, ht_next); 2790 free_host(ht); 2791 } 2792 if ((config.verbose >= 3) && tgrp) { 2793 struct grouplist *g; 2794 DEBUG(1, "deleting export for %s/%s", xf->xf_fsdir, xd->xd_xid->xid_path); 2795 g = tgrp; 2796 while (g) { 2797 DEBUG(3, " %p %d %s %s", g, g->gr_refcnt, grp_name(g), grp_addr(g)); 2798 g = g->gr_next; 2799 } 2800 } 2801 if (tgrp && do_export(NXA_DELETE, xf, xd, tgrp, 0, NULL, NULL)) { 2802 log(LOG_ERR, "kernel export unregistration failed for %s, %s%s", 2803 xd->xd_dir, grp_name(tgrp), tgrp->gr_next ? ", ..." : ""); 2804 } 2805 while (tgrp) { 2806 grp = tgrp; 2807 tgrp = tgrp->gr_next; 2808 free_grp(grp); 2809 } 2810 if ((xd->xd_flags & (OP_DEL|OP_DEFEXP)) == (OP_DEL|OP_DEFEXP)) { 2811 DEBUG(1, "deleting default export for %s/%s", xf->xf_fsdir, xd->xd_xid->xid_path); 2812 /* we need to delete this default export */ 2813 xd->xd_flags = xd->xd_oflags = 0; 2814 if (do_export(NXA_DELETE, xf, xd, NULL, 0, NULL, NULL)) { 2815 log(LOG_ERR, "kernel export unregistration failed for %s," 2816 " default export", xd->xd_dir); 2817 } 2818 } 2819 xd3 = TAILQ_NEXT(xd, xd_next); 2820 if (!(xd->xd_flags & OP_DEFEXP) && TAILQ_EMPTY(&xd->xd_hosts)) { 2821 TAILQ_REMOVE(&xf->xf_dirl, xd, xd_next); 2822 free_expdir(xd); 2823 } else { 2824 xd->xd_iflags &= ~OP_ADD; 2825 xd->xd_flags &= ~OP_DEL; 2826 } 2827 xd = xd3; 2828 } 2829 2830 xf2 = TAILQ_NEXT(xf, xf_next); 2831 if (TAILQ_EMPTY(&xf->xf_dirl)) { 2832 /* No more exports here, delete */ 2833 TAILQ_REMOVE(&xfshead, xf, xf_next); 2834 free_expfs(xf); 2835 } 2836 xf = xf2; 2837 } 2838 2839 if (config.verbose >= 4) { 2840 DEBUG(2, "========> get_exportlist() NEW EXPORTS LIST:"); 2841 dump_exports(); 2842 } 2843 2844 if (!checkexports) 2845 uuidlist_save(); 2846 2847 /* 2848 * If we appear to be having problems resolving host names on startup, 2849 * then we'll want to automatically recheck exports for a while. 2850 * 2851 * First time through, we make sure to set recheckexports_until. 2852 * If we have problems then set the recheck timer - otherwise disable it. 2853 * On subsequent export checks, turn off the recheck timer once we no 2854 * longer need it or the timer expires. 2855 */ 2856 if (!checkexports && (recheckexports_until == 0)) { /* first time through... */ 2857 /* did we have any host names and were any of them problematic? */ 2858 if (hostnamegoodcount != hostnamecount) { 2859 log(LOG_WARNING, "There seem to be problems resolving host names..."); 2860 log(LOG_WARNING, "...will periodically recheck exports for a while."); 2861 recheckexports_until = time(NULL) + RECHECKEXPORTS_TIMEOUT; /* set the recheck timer */ 2862 } else { 2863 recheckexports_until = -1; /* turn it off */ 2864 } 2865 } else if (recheckexports_until > 0) { 2866 /* if we don't need to recheck any more, turn it off */ 2867 if (hostnamegoodcount == hostnamecount) { 2868 recheckexports_until = -1; 2869 } else if (recheckexports_until < time(NULL)) { 2870 log(LOG_WARNING, "Giving up on automatic rechecking of exports."); 2871 recheckexports_until = -1; 2872 } 2873 } 2874 /* should we be rechecking exports? */ 2875 if (!checkexports && ((recheckexports_until > 0) || missingexportcount)) 2876 recheckexports = 1; 2877 else 2878 recheckexports = 0; 2879 2880 unlock_exports(); 2881 2882 return (export_errors); 2883} 2884 2885/* 2886 * Allocate an exported file system structure 2887 */ 2888struct expfs * 2889get_expfs(void) 2890{ 2891 struct expfs *xf; 2892 2893 xf = malloc(sizeof(*xf)); 2894 if (xf == NULL) 2895 return (NULL); 2896 memset(xf, 0, sizeof(*xf)); 2897 TAILQ_INIT(&xf->xf_dirl); 2898 return (xf); 2899} 2900 2901/* 2902 * Allocate an exported directory structure 2903 */ 2904struct expdir * 2905get_expdir(void) 2906{ 2907 struct expdir *xd; 2908 2909 xd = malloc(sizeof(*xd)); 2910 if (xd == NULL) 2911 return (NULL); 2912 memset(xd, 0, sizeof(*xd)); 2913 TAILQ_INIT(&xd->xd_hosts); 2914 TAILQ_INIT(&xd->xd_mountdirs); 2915 return (xd); 2916} 2917 2918/* 2919 * Return the "name" of the given group 2920 */ 2921static char unknown_group[] = "unknown group"; 2922char * 2923grp_name(struct grouplist *grp) 2924{ 2925 if (grp->gr_type == GT_NETGROUP) 2926 return (grp->gr_u.gt_netgroup); 2927 if (grp->gr_type == GT_NET) 2928 return (grp->gr_u.gt_net.nt_name); 2929 if (grp->gr_type == GT_HOST) 2930 return (grp->gr_u.gt_hostinfo.h_name); 2931 return (unknown_group); 2932} 2933 2934/* 2935 * Return a string (in a static buffer) for the (first) address of the given group. 2936 */ 2937static char grpaddrbuf[MAXPATHLEN]; 2938const char * 2939grp_addr(struct grouplist *grp) 2940{ 2941 struct addrinfo *ai; 2942 void *sinaddr; 2943 const char *s = NULL; 2944 2945 if (grp->gr_type == GT_HOST) { 2946 if ((ai = grp->gr_u.gt_hostinfo.h_ailist)) { 2947 sinaddr = (ai->ai_family == AF_INET) ? 2948 (void*)&((struct sockaddr_in*) AOK ai->ai_addr)->sin_addr : 2949 (void*)&((struct sockaddr_in6*) AOK ai->ai_addr)->sin6_addr; 2950 if (inet_ntop(ai->ai_family, sinaddr, grpaddrbuf, sizeof(grpaddrbuf))) 2951 s = grpaddrbuf; 2952 } 2953 } else if (grp->gr_type == GT_NET) { 2954 sinaddr = (grp->gr_u.gt_net.nt_family == AF_INET) ? 2955 (void*)&grp->gr_u.gt_net.nt_net : (void*)&grp->gr_u.gt_net.nt_net6; 2956 if (inet_ntop(grp->gr_u.gt_net.nt_family, sinaddr, grpaddrbuf, sizeof(grpaddrbuf))) 2957 s = grpaddrbuf; 2958 } else if (grp->gr_type == GT_NETGROUP) { 2959 s = ""; 2960 } 2961 2962 if (!s) 2963 s = "???"; 2964 return (s); 2965} 2966 2967int 2968addrinfo_cmp(struct addrinfo *a1, struct addrinfo *a2) 2969{ 2970 if (a1->ai_family != a2->ai_family) 2971 return (a1->ai_family - a2->ai_family); 2972 if (a1->ai_addrlen != a2->ai_addrlen) 2973 return (a1->ai_addrlen - a2->ai_addrlen); 2974 return bcmp(a1->ai_addr, a2->ai_addr, a1->ai_addrlen); 2975} 2976 2977/* 2978 * compare two group list elements 2979 */ 2980int 2981grpcmp(struct grouplist *g1, struct grouplist *g2) 2982{ 2983 struct netmsk *n1, *n2; 2984 struct addrinfo *a1, *a2; 2985 int rv; 2986 2987 rv = g1->gr_type - g2->gr_type; 2988 if (rv) 2989 return (rv); 2990 switch (g1->gr_type) { 2991 case GT_NETGROUP: 2992 rv = strcmp(g1->gr_u.gt_netgroup, g2->gr_u.gt_netgroup); 2993 break; 2994 case GT_NET: 2995 n1 = &g1->gr_u.gt_net; 2996 n2 = &g2->gr_u.gt_net; 2997 rv = strcmp(n1->nt_name, n2->nt_name); 2998 if (rv) 2999 break; 3000 rv = n1->nt_family - n2->nt_family; 3001 if (rv) 3002 break; 3003 if (n1->nt_family == AF_INET) { 3004 rv = bcmp(&n1->nt_net, &n2->nt_net, sizeof(n1->nt_net)); 3005 if (rv) 3006 break; 3007 rv = bcmp(&n1->nt_mask, &n2->nt_mask, sizeof(n1->nt_mask)); 3008 } else if (n1->nt_family == AF_INET6) { 3009 rv = bcmp(&n1->nt_net6, &n2->nt_net6, sizeof(n1->nt_net6)); 3010 if (rv) 3011 break; 3012 rv = bcmp(&n1->nt_mask6, &n2->nt_mask6, sizeof(n1->nt_mask6)); 3013 } 3014 break; 3015 case GT_HOST: 3016 rv = strcmp(g1->gr_u.gt_hostinfo.h_name, g2->gr_u.gt_hostinfo.h_name); 3017 if (rv) 3018 break; 3019 a1 = g1->gr_u.gt_hostinfo.h_ailist; 3020 a2 = g2->gr_u.gt_hostinfo.h_ailist; 3021 while (a1 && a2) { 3022 rv = addrinfo_cmp(a1, a2); 3023 if (rv) 3024 break; 3025 a1 = a1->ai_next; 3026 a2 = a2->ai_next; 3027 } 3028 if (!rv && !(!a1 && !a2)) { 3029 if (a1) 3030 rv = 1; 3031 else 3032 rv = -1; 3033 } 3034 break; 3035 } 3036 return (rv); 3037} 3038 3039/* 3040 * Return a group in the group cache that matches the given group. 3041 * If the group isn't yet in the cache, a new group will be allocated, 3042 * populated with the info from the given group, and added to the cache. 3043 * In any event, any memory referred to within grptmp->gr_u has been 3044 * either used in a new group cache entry or freed. 3045 */ 3046struct grouplist * 3047get_grp(struct grouplist *grptmp) 3048{ 3049 struct grouplist *g, *g2; 3050 int cmp = 1, clean_up_gr_u = 1; 3051 3052 if (config.verbose >= 7) { 3053 DEBUG(5, "get_grp: %s %s", grp_name(grptmp), grp_addr(grptmp)); 3054 g = grpcache; 3055 while (g) { 3056 DEBUG(6, "grpcache: %p %d %s %s", g, g->gr_refcnt, grp_name(g), grp_addr(g)); 3057 g = g->gr_cache; 3058 } 3059 } 3060 3061 g2 = NULL; 3062 g = grpcache; 3063 while (g && ((cmp = grpcmp(grptmp, g)) > 0)) { 3064 g2 = g; 3065 g = g->gr_cache; 3066 } 3067 3068 if (!cmp) { 3069 g->gr_refcnt++; 3070 if (config.verbose >= 7) 3071 DEBUG(5, "get_grp: found %p %d %s %s", g, g->gr_refcnt, grp_name(g), grp_addr(g)); 3072 g->gr_flags |= grptmp->gr_flags; 3073 goto out; 3074 } 3075 3076 g = malloc(sizeof(*g)); 3077 if (g == NULL) 3078 goto out; 3079 memset(g, 0, sizeof(*g)); 3080 g->gr_refcnt = 1; 3081 g->gr_type = grptmp->gr_type; 3082 g->gr_flags = grptmp->gr_flags; 3083 g->gr_u = grptmp->gr_u; /* memory allocations in *grptmp->gr_u are now owned by g->gr_u */ 3084 clean_up_gr_u = 0; 3085 3086 if (g2) { 3087 g->gr_cache = g2->gr_cache; 3088 g2->gr_cache = g; 3089 } else { 3090 g->gr_cache = grpcache; 3091 grpcache = g; 3092 } 3093 3094 if (config.verbose >= 7) { 3095 DEBUG(5, "get_grp: ----- NEW %p %d %s %s", g, g->gr_refcnt, grp_name(g), grp_addr(g)); 3096 g2 = grpcache; 3097 while (g2) { 3098 DEBUG(6, "grpcache: %p %d %s %s", g2, g2->gr_refcnt, grp_name(g2), grp_addr(g2)); 3099 g2 = g2->gr_cache; 3100 } 3101 } 3102 3103out: 3104 if (clean_up_gr_u) { 3105 /* free up the contents of grptmp->gr_u */ 3106 switch (grptmp->gr_type) { 3107 case GT_HOST: 3108 if (grptmp->gr_u.gt_hostinfo.h_ailist) 3109 freeaddrinfo(grptmp->gr_u.gt_hostinfo.h_ailist); 3110 if (grptmp->gr_u.gt_hostinfo.h_name) 3111 free(grptmp->gr_u.gt_hostinfo.h_name); 3112 break; 3113 case GT_NET: 3114 if (grptmp->gr_u.gt_net.nt_name) 3115 free(grptmp->gr_u.gt_net.nt_name); 3116 break; 3117 case GT_NETGROUP: 3118 if (grptmp->gr_u.gt_netgroup) 3119 free(grptmp->gr_u.gt_netgroup); 3120 break; 3121 } 3122 } 3123 3124 return (g); 3125} 3126 3127/* 3128 * Free up a group list. 3129 */ 3130void 3131free_grp(struct grouplist *grp) 3132{ 3133 struct grouplist **g; 3134 3135 /* decrement reference count */ 3136 grp->gr_refcnt--; 3137 3138 if (config.verbose >= 7) 3139 DEBUG(5, "free_grp: %p %d %s %s", grp, grp->gr_refcnt, grp_name(grp), grp_addr(grp)); 3140 3141 if (grp->gr_refcnt > 0) 3142 return; 3143 3144 /* remove group from grpcache list */ 3145 g = &grpcache; 3146 while (*g && (*g != grp)) 3147 g = &(*g)->gr_cache; 3148 if (*g) 3149 *g = (*g)->gr_cache; 3150 3151 /* free up the memory */ 3152 if (grp->gr_type == GT_HOST) { 3153 if (grp->gr_u.gt_hostinfo.h_ailist) 3154 freeaddrinfo(grp->gr_u.gt_hostinfo.h_ailist); 3155 if (grp->gr_u.gt_hostinfo.h_name) 3156 free(grp->gr_u.gt_hostinfo.h_name); 3157 } else if (grp->gr_type == GT_NET) { 3158 if (grp->gr_u.gt_net.nt_name) 3159 free(grp->gr_u.gt_net.nt_name); 3160 } else if (grp->gr_type == GT_NETGROUP) { 3161 if (grp->gr_u.gt_netgroup) 3162 free(grp->gr_u.gt_netgroup); 3163 } 3164 free((caddr_t)grp); 3165} 3166 3167/* 3168 * insert a group list element into a group list 3169 */ 3170int 3171add_grp(struct grouplist **glp, struct grouplist *newg) 3172{ 3173 struct grouplist *g1, *g2; 3174 int cmp = 1; 3175 3176 g2 = NULL; 3177 g1 = *glp; 3178 while (g1 && ((cmp = grpcmp(newg, g1)) > 0)) { 3179 g2 = g1; 3180 g1 = g1->gr_next; 3181 } 3182 if (!cmp) { 3183 /* already in list, make sure SHOW bit is set */ 3184 if (newg->gr_flags & GF_SHOW) 3185 g1->gr_flags |= GF_SHOW; 3186 return (0); 3187 } 3188 if (g2) { 3189 newg->gr_next = g2->gr_next; 3190 g2->gr_next = newg; 3191 } else { 3192 newg->gr_next = *glp; 3193 *glp = newg; 3194 } 3195 return (1); 3196} 3197 3198/* 3199 * insert a host list element into a host list 3200 * (identical to add_grp(), but takes a host list and 3201 * allows "duplicates" if one is tagged for deletion) 3202 */ 3203int 3204add_host(struct hosttqh *head, struct host *newht) 3205{ 3206 struct host *ht; 3207 int cmp = 1; 3208 3209 TAILQ_FOREACH(ht, head, ht_next) 3210 if ((cmp = grpcmp(newht->ht_grp, ht->ht_grp)) <= 0) 3211 break; 3212 if (!cmp && !(ht->ht_flags & OP_DEL)) { 3213 /* already in list, make sure SHOW bit is set */ 3214 if (newht->ht_flags & OP_SHOW) 3215 ht->ht_flags |= OP_SHOW; 3216 return (0); 3217 } 3218 if (ht) 3219 TAILQ_INSERT_BEFORE(ht, newht, ht_next); 3220 else 3221 TAILQ_INSERT_TAIL(head, newht, ht_next); 3222 return (1); 3223} 3224 3225/* 3226 * report/record an export error message 3227 */ 3228void 3229export_error(int level, const char *fmt, ...) 3230{ 3231 struct errlist *elnew, *el, *elp; 3232 char *s = NULL; 3233 va_list ap; 3234 3235 export_errors++; 3236 3237#pragma clang diagnostic push 3238#pragma clang diagnostic ignored "-Wformat-nonliteral" 3239 va_start(ap, fmt); 3240 vasprintf(&s, fmt, ap); 3241 va_end(ap); 3242#pragma clang diagnostic pop 3243 3244 /* check if we've already logged this error */ 3245 LIST_FOREACH(el, &xerrs, el_next) { 3246 if (linenum < el->el_linenum) 3247 continue; 3248 if (linenum > el->el_linenum) { 3249 el = NULL; 3250 break; 3251 } 3252 if (!strcmp(el->el_msg, s)) 3253 break; 3254 } 3255 3256 /* log the message if we haven't already */ 3257 if (!el) 3258 log(level, "exports:%d: %s", linenum, s); 3259 3260 /* add this error to the list */ 3261 elnew = malloc(sizeof(*elnew)); 3262 if (!elnew) { 3263 free(s); 3264 return; 3265 } 3266 elnew->el_linenum = linenum; 3267 elnew->el_msg = s; 3268 elp = NULL; 3269 LIST_FOREACH(el, &xerrs, el_next) { 3270 if (linenum < el->el_linenum) 3271 break; 3272 elp = el; 3273 } 3274 if (elp) 3275 LIST_INSERT_AFTER(elp, elnew, el_next); 3276 else 3277 LIST_INSERT_HEAD(&xerrs, elnew, el_next); 3278} 3279 3280/* 3281 * clear export errors on the give line# (or all if line#=0) 3282 */ 3283int 3284clear_export_errors(uint32_t linenum) 3285{ 3286 struct errlist *el, *elnext; 3287 int cleared = 0; 3288 3289 LIST_FOREACH_SAFE(el, &xerrs, el_next, elnext) { 3290 if (linenum) { 3291 if (linenum < el->el_linenum) 3292 break; 3293 if (linenum > el->el_linenum) 3294 continue; 3295 } 3296 cleared = 1; 3297 LIST_REMOVE(el, el_next); 3298 if (el->el_msg) 3299 free(el->el_msg); 3300 free(el); 3301 } 3302 3303 return (cleared); 3304} 3305 3306/* 3307 * Clean up upon an error in get_exportlist(). 3308 */ 3309void 3310export_error_cleanup(struct expfs *xf) 3311{ 3312 if (xf && (xf->xf_flag & XF_LINKED) == 0) 3313 free_expfs(xf); 3314} 3315 3316/* 3317 * Search the export list for a matching fs. 3318 */ 3319struct expfs * 3320ex_search(u_char *uuid) 3321{ 3322 struct expfs *xf; 3323 3324 TAILQ_FOREACH(xf, &xfshead, xf_next) { 3325 if (!bcmp(xf->xf_uuid, uuid, 16)) 3326 return (xf); 3327 } 3328 return (xf); 3329} 3330 3331/* 3332 * add a directory to a dirlist (sorted) 3333 * 3334 * Note that the list is sorted to place subdirectories 3335 * after the entry for their matching parent directory. 3336 * This isn't strictly sorted because other directories may 3337 * have similar names with characters that sort lower than '/'. 3338 * For example: /export /export.test /export/subdir 3339 */ 3340int 3341add_dir(struct dirlist **dlpp, char *cp) 3342{ 3343 struct dirlist *newdl, *dl, *dl2, *dl3, *dlstop; 3344 int cplen, dlen, cmp; 3345 3346 dlstop = NULL; 3347 dl2 = NULL; 3348 dl = *dlpp; 3349 cplen = strlen(cp); 3350 3351 while (dl && (dl != dlstop)) { 3352 dlen = strlen(dl->dl_dir); 3353 cmp = strncmp(cp, dl->dl_dir, dlen); 3354 DEBUG(3, "add_dir: %s compare %s %d", cp, dl->dl_dir, cmp); 3355 if (cmp < 0) 3356 break; 3357 if (cmp == 0) { 3358 if (cplen == dlen) /* duplicate */ 3359 return (EEXIST); 3360 if (cp[dlen] == '/') { 3361 /* 3362 * Find the next entry that isn't a 3363 * subdirectory of this directory so 3364 * we know when to stop looking for 3365 * the insertion point. 3366 */ 3367 DEBUG(3, "add_dir: %s compare %s %d subdir match", 3368 cp, dl->dl_dir, cmp); 3369 dlstop = dl->dl_next; 3370 while (dlstop && (subdir_check(dl->dl_dir, dlstop->dl_dir) == 1)) 3371 dlstop = dlstop->dl_next; 3372 } else { 3373 /* 3374 * The new dir should go after this directory and 3375 * its subdirectories. So, skip subdirs of this dir. 3376 */ 3377 DEBUG(3, "add_dir: %s compare %s %d partial match", 3378 cp, dl->dl_dir, cmp); 3379 dl3 = dl; 3380 dl2 = dl; 3381 dl = dl->dl_next; 3382 while (dl && (subdir_check(dl3->dl_dir, dl->dl_dir) == 1)) { 3383 dl2 = dl; 3384 dl = dl->dl_next; 3385 } 3386 continue; 3387 } 3388 } 3389 dl2 = dl; 3390 dl = dl->dl_next; 3391 } 3392 if (dl && (dl == dlstop)) 3393 DEBUG(3, "add_dir: %s stopped before %s", cp, dlstop->dl_dir); 3394 newdl = malloc(sizeof(*dl)); 3395 if (newdl == NULL) { 3396 log(LOG_ERR, "can't allocate memory to add dir %s", cp); 3397 return (ENOMEM); 3398 } 3399 newdl->dl_dir = cp; 3400 if (dl2 == NULL) { 3401 newdl->dl_next = *dlpp; 3402 *dlpp = newdl; 3403 } else { 3404 newdl->dl_next = dl; 3405 dl2->dl_next = newdl; 3406 } 3407 if (config.verbose >= 6) { 3408 dl = *dlpp; 3409 while (dl) { 3410 DEBUG(4, "DIRLIST: %s", dl->dl_dir); 3411 dl = dl->dl_next; 3412 } 3413 } 3414 return (0); 3415} 3416 3417/* 3418 * free up all the elements in a dirlist 3419 */ 3420void 3421free_dirlist(struct dirlist *dl) 3422{ 3423 struct dirlist *dl2; 3424 3425 while (dl) { 3426 dl2 = dl->dl_next; 3427 if (dl->dl_dir) 3428 free(dl->dl_dir); 3429 free(dl); 3430 dl = dl2; 3431 } 3432} 3433 3434/* 3435 * add a name to a namelist 3436 * 3437 * returns 0 on success, -1 on duplicate, or error 3438 */ 3439int 3440add_name(struct namelisttqh *names, char *name) 3441{ 3442 struct namelist *nl; 3443 3444 TAILQ_FOREACH(nl, names, nl_next) 3445 if (!strcmp(nl->nl_name, name)) 3446 return (-1); 3447 nl = malloc(sizeof(*nl)); 3448 if (!nl) 3449 return (ENOMEM); 3450 nl->nl_name = strdup(name); 3451 if (!nl->nl_name) { 3452 free(nl); 3453 return (ENOMEM); 3454 } 3455 TAILQ_INSERT_TAIL(names, nl, nl_next); 3456 return (0); 3457} 3458 3459/* 3460 * free up all the elements in a namelist 3461 */ 3462void 3463free_namelist(struct namelisttqh *names) 3464{ 3465 struct namelist *nl, *nlnext; 3466 3467 TAILQ_FOREACH_SAFE(nl, names, nl_next, nlnext) { 3468 TAILQ_REMOVE(names, nl, nl_next); 3469 if (nl->nl_name) 3470 free(nl->nl_name); 3471 free(nl); 3472 } 3473} 3474 3475/* 3476 * find a host list entry that has the same group 3477 */ 3478struct host * 3479find_group_match_in_host_list(struct hosttqh *head, struct grouplist *grp) 3480{ 3481 struct host *ht; 3482 3483 TAILQ_FOREACH(ht, head, ht_next) 3484 if (!grpcmp(grp, ht->ht_grp)) 3485 break; 3486 return (ht); 3487} 3488 3489/* 3490 * find a host list entry that has the same address 3491 */ 3492struct host * 3493find_group_address_match_in_host_list(struct hosttqh *head, struct grouplist *grp) 3494{ 3495 struct host *ht; 3496 struct netmsk *n1, *n2; 3497 struct addrinfo *a1, *a2; 3498 int i; 3499 3500 switch (grp->gr_type) { 3501 case GT_HOST: 3502 a1 = grp->gr_u.gt_hostinfo.h_ailist; 3503 while (a1) { 3504 TAILQ_FOREACH(ht, head, ht_next) { 3505 if (ht->ht_grp->gr_type != GT_HOST) 3506 continue; 3507 if (ht->ht_flags & OP_DEL) 3508 continue; 3509 a2 = ht->ht_grp->gr_u.gt_hostinfo.h_ailist; 3510 while (a2) { 3511 if (!addrinfo_cmp(a1, a2)) 3512 return (ht); 3513 a2 = a2->ai_next; 3514 } 3515 } 3516 a1 = a1->ai_next; 3517 } 3518 break; 3519 case GT_NET: 3520 n1 = &grp->gr_u.gt_net; 3521 TAILQ_FOREACH(ht, head, ht_next) { 3522 if (ht->ht_grp->gr_type != GT_NET) 3523 continue; 3524 if (ht->ht_flags & OP_DEL) 3525 continue; 3526 n2 = &ht->ht_grp->gr_u.gt_net; 3527 if (n1->nt_family != n2->nt_family) 3528 continue; 3529 if (n1->nt_family == AF_INET) { 3530 in_addr_t ina1, ina2; 3531 ina1 = n1->nt_net & n1->nt_mask; 3532 ina2 = n2->nt_net & n2->nt_mask; 3533 if (ina1 == ina2) 3534 return (ht); 3535 } else if (n1->nt_family == AF_INET6) { 3536 struct in6_addr ina1, ina2; 3537 for (i=0; i < (int)sizeof(ina1.s6_addr); i++) { 3538 ina1.s6_addr[i] = n1->nt_net6.s6_addr[i] & n1->nt_mask6.s6_addr[i]; 3539 ina2.s6_addr[i] = n2->nt_net6.s6_addr[i] & n2->nt_mask6.s6_addr[i]; 3540 } 3541 if (!bcmp(&ina1, &ina2, sizeof(ina1))) 3542 return (ht); 3543 } 3544 } 3545 break; 3546 } 3547 3548 return (NULL); 3549} 3550 3551/* 3552 * compare two credentials 3553 */ 3554int 3555crcmp(struct xucred *cr1, struct xucred *cr2) 3556{ 3557 int i; 3558 3559 if (cr1 == cr2) 3560 return 0; 3561 if (cr1 == NULL || cr2 == NULL) 3562 return 1; 3563 if (cr1->cr_uid != cr2->cr_uid) 3564 return 1; 3565 if (cr1->cr_ngroups != cr2->cr_ngroups) 3566 return 1; 3567 /* XXX assumes groups will always be listed in some order */ 3568 for (i=0; i < cr1->cr_ngroups; i++) 3569 if (cr1->cr_groups[i] != cr2->cr_groups[i]) 3570 return 1; 3571 return (0); 3572} 3573 3574/* 3575 * tentatively hang export options for a list of groups off of an exported directory 3576 */ 3577int 3578hang_options_setup(struct expdir *xd, int opt_flags, struct xucred *cr, struct grouplist *grp, 3579 struct nfs_sec *secflavs, int *need_export) 3580{ 3581 struct host *ht; 3582 3583 *need_export = 0; 3584 3585 if (!grp) { 3586 /* default export */ 3587 if (xd->xd_flags & OP_DEFEXP) { 3588 /* exported directory already has default export! */ 3589 if ((OP_EXOPTS(xd->xd_flags) == OP_EXOPTS(opt_flags)) && 3590 (!(opt_flags & (OP_MAPALL|OP_MAPROOT)) || !crcmp(&xd->xd_cred, cr)) && 3591 (!cmp_secflavs(&xd->xd_sec, secflavs))) { 3592 if (!(xd->xd_flags & OP_DEL)) 3593 log(LOG_WARNING, "duplicate default export for %s", xd->xd_dir); 3594 xd->xd_flags &= ~OP_EXOPTMASK; 3595 xd->xd_flags |= opt_flags | OP_DEFEXP | OP_ADD; 3596 return (0); 3597 } else if (!(xd->xd_flags & OP_DEL)) { 3598 log(LOG_ERR, "multiple/conflicting default exports for %s", xd->xd_dir); 3599 return (EEXIST); 3600 } 3601 } 3602 xd->xd_flags &= ~OP_EXOPTMASK; 3603 xd->xd_flags |= opt_flags | OP_DEFEXP | OP_ADD; 3604 if (cr) 3605 xd->xd_cred = *cr; 3606 bcopy(secflavs, &xd->xd_sec, sizeof(struct nfs_sec)); 3607 DEBUG(3, "hang_options_setup: %s default 0x%x", xd->xd_dir, xd->xd_flags); 3608 *need_export = 1; 3609 return (0); 3610 } 3611 3612 while (grp) { 3613 /* first check for an existing entry for this group */ 3614 ht = find_group_match_in_host_list(&xd->xd_hosts, grp); 3615 if (ht) { 3616 /* found a match... */ 3617 if ((OP_EXOPTS(ht->ht_flags) == OP_EXOPTS(opt_flags)) && 3618 (!(opt_flags & (OP_MAPALL|OP_MAPROOT)) || !crcmp(&ht->ht_cred, cr)) && 3619 (!cmp_secflavs(&ht->ht_sec, secflavs))) { 3620 /* options match, OK, it's the same export */ 3621 if (!(ht->ht_flags & OP_ADD) && !(grp->gr_flags & GF_SHOW)) 3622 ht->ht_flags &= ~OP_SHOW; 3623 ht->ht_flags |= OP_ADD; 3624 if (grp->gr_flags & GF_SHOW) 3625 ht->ht_flags |= OP_SHOW; 3626 grp = grp->gr_next; 3627 continue; 3628 } 3629 /* options don't match... */ 3630 if (!(ht->ht_flags & OP_DEL)) { 3631 /* this is a new entry, so this is a conflict */ 3632 log(LOG_ERR, "conflicting exports for %s, %s", xd->xd_dir, grp_name(grp)); 3633 return (EEXIST); 3634 } 3635 /* this entry is marked for deletion, so there is no conflict */ 3636 /* go ahead and add a new entry with the new options */ 3637 } 3638 /* also check for an existing entry for any addresses in this group */ 3639 ht = find_group_address_match_in_host_list(&xd->xd_hosts, grp); 3640 if (ht) { 3641 /* found a match... */ 3642 if ((OP_EXOPTS(ht->ht_flags) != OP_EXOPTS(opt_flags)) || 3643 ((opt_flags & (OP_MAPALL|OP_MAPROOT)) && crcmp(&ht->ht_cred, cr)) || 3644 (cmp_secflavs(&ht->ht_sec, secflavs))) { 3645 /* ...with different options */ 3646 log(LOG_ERR, "conflicting exports for %s, %s", xd->xd_dir, grp_name(grp)); 3647 return (EEXIST); 3648 } 3649 /* ... with same options */ 3650 log(LOG_WARNING, "duplicate export for %s, %s vs. %s", xd->xd_dir, 3651 grp_name(grp), grp_name(ht->ht_grp)); 3652 grp = grp->gr_next; 3653 continue; 3654 } 3655 /* OK to add a new host */ 3656 ht = get_host(); 3657 if (!ht) { 3658 log(LOG_ERR, "Can't allocate memory for host: %s", grp_name(grp)); 3659 return(ENOMEM); 3660 } 3661 ht->ht_flags = opt_flags | OP_ADD; 3662 ht->ht_cred = *cr; 3663 ht->ht_grp = grp; 3664 grp->gr_refcnt++; 3665 bcopy(secflavs, &ht->ht_sec, sizeof(struct nfs_sec)); 3666 if (grp->gr_flags & GF_SHOW) 3667 ht->ht_flags |= OP_SHOW; 3668 if (config.verbose >= 6) 3669 DEBUG(4, "grp2host: %p %d %s %s", grp, grp->gr_refcnt, grp_name(grp), grp_addr(grp)); 3670 if (!add_host(&xd->xd_hosts, ht)) { 3671 /* This shouldn't happen given the above checks */ 3672 log(LOG_ERR, "duplicate host in export list: %s", grp_name(grp)); 3673 free_host(ht); 3674 return (EEXIST); 3675 } 3676 *need_export = 1; 3677 DEBUG(3, "hang_options_setup: %s %s 0x%x", xd->xd_dir, grp_name(grp), opt_flags); 3678 grp = grp->gr_next; 3679 } 3680 3681 return (0); 3682} 3683 3684/* 3685 * make permanent the export options added via hang_options_setup() 3686 */ 3687void 3688hang_options_finalize(struct expdir *xd) 3689{ 3690 struct host *ht; 3691 struct expdir *mxd; 3692 3693 if (xd->xd_flags & OP_ADD) { 3694 xd->xd_iflags |= (xd->xd_flags & (OP_OFFLINE|OP_MISSING)) ? OP_OFFLINE : OP_ONLINE; 3695 merge_secflavs(&xd->xd_ssec, &xd->xd_sec); 3696 xd->xd_flags &= ~(OP_ADD|OP_DEL); 3697 /* update old options in case subsequent export fails */ 3698 xd->xd_oflags = xd->xd_flags; 3699 xd->xd_ocred = xd->xd_cred; 3700 bcopy(&xd->xd_sec, &xd->xd_osec, sizeof(struct nfs_sec)); 3701 } 3702 3703 TAILQ_FOREACH(ht, &xd->xd_hosts, ht_next) { 3704 if (!(ht->ht_flags & OP_ADD)) 3705 continue; 3706 ht->ht_flags &= ~(OP_ADD|OP_DEL); 3707 xd->xd_iflags |= (ht->ht_flags & (OP_OFFLINE|OP_MISSING)) ? OP_OFFLINE : OP_ONLINE; 3708 merge_secflavs(&xd->xd_ssec, &ht->ht_sec); 3709 } 3710 3711 TAILQ_FOREACH(mxd, &xd->xd_mountdirs, xd_next) { 3712 hang_options_finalize(mxd); 3713 } 3714} 3715 3716/* 3717 * cleanup/undo the export options added via hang_options_setup() 3718 */ 3719void 3720hang_options_cleanup(struct expdir *xd) 3721{ 3722 struct host *ht, *htnext; 3723 3724 if (xd->xd_flags & OP_ADD) { 3725 xd->xd_flags = xd->xd_oflags; 3726 xd->xd_cred = xd->xd_ocred; 3727 bcopy(&xd->xd_osec, &xd->xd_sec, sizeof(struct nfs_sec)); 3728 } 3729 3730 TAILQ_FOREACH_SAFE(ht, &xd->xd_hosts, ht_next, htnext) { 3731 if (!(ht->ht_flags & OP_ADD)) 3732 continue; 3733 if (ht->ht_flags & OP_DEL) { 3734 ht->ht_flags &= ~OP_ADD; 3735 continue; 3736 } 3737 TAILQ_REMOVE(&xd->xd_hosts, ht, ht_next); 3738 free_host(ht); 3739 } 3740 3741 /* 3742 * Note: currently cleanup isn't called after handling mountdirs, 3743 * so we don't have to bother cleaning up any of the mountdirs. 3744 */ 3745} 3746 3747/* 3748 * hang export options for mountable subdirectories of an exported directory 3749 */ 3750int 3751hang_options_mountdir(struct expdir *xd, char *dir, int opt_flags, struct grouplist *grp, struct nfs_sec *secflavs) 3752{ 3753 struct host *ht; 3754 struct expdir *mxd, *mxd2, *mxd3; 3755 int cmp; 3756 3757 /* check for existing mountdir */ 3758 mxd = mxd3 = NULL; 3759 TAILQ_FOREACH(mxd2, &xd->xd_mountdirs, xd_next) { 3760 cmp = strcmp(dir, mxd2->xd_dir); 3761 if (!cmp) { 3762 /* found it */ 3763 mxd = mxd2; 3764 break; 3765 } else if (cmp < 0) { 3766 /* found where it needs to be inserted */ 3767 break; 3768 } 3769 mxd3 = mxd2; 3770 } 3771 if (!mxd) { 3772 mxd = get_expdir(); 3773 if (mxd) 3774 mxd->xd_dir = strdup(dir); 3775 if (!mxd || !mxd->xd_dir) { 3776 if (mxd) 3777 free_expdir(mxd); 3778 log(LOG_ERR, "can't allocate memory for mountable sub-directory; %s", dir); 3779 return (ENOMEM); 3780 } 3781 if (mxd3) { 3782 TAILQ_INSERT_AFTER(&xd->xd_mountdirs, mxd3, mxd, xd_next); 3783 } else { 3784 TAILQ_INSERT_HEAD(&xd->xd_mountdirs, mxd, xd_next); 3785 } 3786 } 3787 3788 if (!grp) { 3789 /* default export */ 3790 if (mxd->xd_flags & OP_DEFEXP) { 3791 /* exported directory already has default export! */ 3792 if ((OP_EXOPTS(mxd->xd_flags) == OP_EXOPTS(opt_flags)) && 3793 (!cmp_secflavs(&mxd->xd_sec, secflavs))) { 3794 if (!(mxd->xd_flags & OP_DEL)) 3795 log(LOG_WARNING, "duplicate default export for %s", mxd->xd_dir); 3796 mxd->xd_flags &= ~OP_EXOPTMASK; 3797 mxd->xd_flags |= opt_flags | OP_DEFEXP | OP_ADD; 3798 return (0); 3799 } else if (!(mxd->xd_flags & OP_DEL)) { 3800 log(LOG_ERR, "multiple/conflicting default exports for %s", mxd->xd_dir); 3801 return (EEXIST); 3802 } 3803 } 3804 mxd->xd_flags &= ~OP_EXOPTMASK; 3805 mxd->xd_flags |= opt_flags | OP_DEFEXP | OP_ADD; 3806 bcopy(secflavs, &mxd->xd_sec, sizeof(struct nfs_sec)); 3807 DEBUG(3, "hang_options_mountdir: %s default 0x%x", 3808 mxd->xd_dir, mxd->xd_flags); 3809 return (0); 3810 } 3811 3812 while (grp) { 3813 /* first check for an existing entry for this group */ 3814 ht = find_group_match_in_host_list(&mxd->xd_hosts, grp); 3815 if (ht) { 3816 /* found a match... */ 3817 if ((OP_EXOPTS(ht->ht_flags) == OP_EXOPTS(opt_flags)) && 3818 (!cmp_secflavs(&ht->ht_sec, secflavs))) { 3819 /* options match, OK, it's the same export */ 3820 ht->ht_flags |= OP_ADD; 3821 grp = grp->gr_next; 3822 continue; 3823 } 3824 /* options don't match... */ 3825 if (!(ht->ht_flags & OP_DEL)) { 3826 /* this is a new entry, so this is a conflict */ 3827 log(LOG_ERR, "conflicting mountdir exports for %s, %s", 3828 mxd->xd_dir, grp_name(grp)); 3829 return (EEXIST); 3830 } 3831 /* this entry is marked for deletion, so there is no conflict */ 3832 /* go ahead and add a new entry with the new options */ 3833 } 3834 /* also check for an existing entry for any addresses in this group */ 3835 ht = find_group_address_match_in_host_list(&mxd->xd_hosts, grp); 3836 if (ht) { 3837 /* found a match... */ 3838 if ((OP_EXOPTS(ht->ht_flags) != OP_EXOPTS(opt_flags)) || 3839 (cmp_secflavs(&ht->ht_sec, secflavs))) { 3840 /* ...with different options */ 3841 log(LOG_ERR, "conflicting mountdir exports for %s, %s", 3842 mxd->xd_dir, grp_name(grp)); 3843 return (EEXIST); 3844 } 3845 /* ... with same options */ 3846 log(LOG_WARNING, "duplicate mountdir export for %s, %s vs. %s", 3847 mxd->xd_dir, grp_name(grp), grp_name(ht->ht_grp)); 3848 grp = grp->gr_next; 3849 continue; 3850 } 3851 /* OK to add a new host */ 3852 ht = get_host(); 3853 if (!ht) { 3854 log(LOG_ERR, "Can't allocate memory for host: %s", grp_name(grp)); 3855 return(ENOMEM); 3856 } 3857 ht->ht_flags = opt_flags | OP_ADD; 3858 ht->ht_grp = grp; 3859 grp->gr_refcnt++; 3860 bcopy(secflavs, &ht->ht_sec, sizeof(struct nfs_sec)); 3861 if (grp->gr_flags & GF_SHOW) 3862 ht->ht_flags |= OP_SHOW; 3863 if (config.verbose >= 6) 3864 DEBUG(4, "grp2host: %p %d %s %s", grp, grp->gr_refcnt, grp_name(grp), grp_addr(grp)); 3865 if (!add_host(&mxd->xd_hosts, ht)) { 3866 /* This shouldn't happen given the above checks */ 3867 log(LOG_ERR, "Can't add host to mountdir export list: %s", grp_name(grp)); 3868 free_host(ht); 3869 return (EEXIST); 3870 } 3871 DEBUG(3, "hang_options_mountdir: %s %s 0x%x", 3872 mxd->xd_dir, grp_name(grp), opt_flags); 3873 grp = grp->gr_next; 3874 } 3875 3876 return (0); 3877} 3878 3879/* 3880 * Search for an exported directory on an exported file system that 3881 * a given host can mount and return the export options. 3882 * 3883 * Search order: 3884 * an exact match on exported directory path 3885 * an exact match on exported directory mountdir path 3886 * a subdir match on exported directory mountdir path with ALLDIRS 3887 * a subdir match on exported directory path with ALLDIRS 3888 */ 3889int 3890expdir_search(struct expfs *xf, char *dirpath, struct sockaddr *sa, int *options, struct nfs_sec *secflavs) 3891{ 3892 struct expdir *xd, *mxd; 3893 struct host *hp; 3894 int cmp = 1, chkalldirs = 0; 3895 3896 TAILQ_FOREACH(xd, &xf->xf_dirl, xd_next) { 3897 if ((cmp = subdir_check(xd->xd_dir, dirpath)) >= 0) 3898 break; 3899 } 3900 if (!xd) { 3901 DEBUG(1, "expdir_search: no matching export: %s", dirpath); 3902 return (0); 3903 } 3904 3905 DEBUG(1, "expdir_search: %s -> %s", dirpath, xd->xd_dir); 3906 3907 if (cmp == 0) { 3908 /* exact match on exported directory path */ 3909check_xd_hosts: 3910 /* find options for this host */ 3911 hp = find_host(&xd->xd_hosts, sa); 3912 if (hp && (!chkalldirs || (hp->ht_flags & OP_ALLDIRS))) { 3913 DEBUG(2, "expdir_search: %s host %s", dirpath, 3914 (chkalldirs ? "alldirs" : "match")); 3915 *options = hp->ht_flags; 3916 bcopy(&hp->ht_sec, secflavs, sizeof(struct nfs_sec)); 3917 } else if ((xd->xd_flags & OP_DEFEXP) && 3918 (!chkalldirs || (xd->xd_flags & OP_ALLDIRS))) { 3919 DEBUG(2, "expdir_search: %s defexp %s", 3920 dirpath, (chkalldirs ? "alldirs" : "match")); 3921 *options = xd->xd_flags; 3922 bcopy(&xd->xd_sec, secflavs, sizeof(struct nfs_sec)); 3923 } else { 3924 /* not exported to this host */ 3925 *options = 0; 3926 DEBUG(2, "expdir_search: %s NO match", dirpath); 3927 return (0); 3928 } 3929 return (1); 3930 } 3931 3932 /* search for a matching mountdir */ 3933 TAILQ_FOREACH(mxd, &xd->xd_mountdirs, xd_next) { 3934 cmp = subdir_check(mxd->xd_dir, dirpath); 3935 if (cmp < 0) 3936 continue; 3937 DEBUG(1, "expdir_search: %s subdir path match %s", 3938 dirpath, mxd->xd_dir); 3939 chkalldirs = (cmp != 0); 3940 /* found a match on a mountdir */ 3941 hp = find_host(&mxd->xd_hosts, sa); 3942 if (hp && (!chkalldirs || (hp->ht_flags & OP_ALLDIRS))) { 3943 DEBUG(2, "expdir_search: %s -> %s subdir host %s", 3944 dirpath, mxd->xd_dir, (chkalldirs ? "alldirs" : "match")); 3945 *options = hp->ht_flags; 3946 bcopy(&hp->ht_sec, secflavs, sizeof(struct nfs_sec)); 3947 return (1); 3948 } else if ((mxd->xd_flags & OP_DEFEXP) && 3949 (!chkalldirs || (mxd->xd_flags & OP_ALLDIRS))) { 3950 DEBUG(2, "expdir_search: %s -> %s subdir defexp %s", 3951 dirpath, mxd->xd_dir, (chkalldirs ? "alldirs" : "match")); 3952 *options = mxd->xd_flags; 3953 bcopy(&mxd->xd_sec, secflavs, sizeof(struct nfs_sec)); 3954 return (1); 3955 } 3956 /* not exported to this host */ 3957 } 3958 3959 DEBUG(1, "expdir_search: %s NO match, check alldirs", dirpath); 3960 chkalldirs = 1; 3961 goto check_xd_hosts; 3962} 3963 3964/* 3965 * search a host list for a match for the given address 3966 */ 3967struct host * 3968find_host(struct hosttqh *head, struct sockaddr *sa) 3969{ 3970 struct host *hp; 3971 struct grouplist *grp; 3972 struct addrinfo *ai; 3973 int i; 3974 3975 TAILQ_FOREACH(hp, head, ht_next) { 3976 grp = hp->ht_grp; 3977 switch (grp->gr_type) { 3978 case GT_HOST: 3979 for (ai = grp->gr_u.gt_hostinfo.h_ailist; ai; ai = ai->ai_next) { 3980 if (ai->ai_family != sa->sa_family) 3981 continue; 3982 if (ai->ai_addrlen != sa->sa_len) 3983 continue; 3984 if (ai->ai_family == AF_INET) { 3985 struct sockaddr_in *sin1, *sin2; 3986 sin1 = (struct sockaddr_in*) AOK ai->ai_addr; 3987 sin2 = (struct sockaddr_in*) AOK sa; 3988 if (!bcmp(&sin1->sin_addr, &sin2->sin_addr, sizeof(sin1->sin_addr))) 3989 return (hp); 3990 } else if (ai->ai_family == AF_INET6) { 3991 struct sockaddr_in6 *sin1, *sin2; 3992 sin1 = (struct sockaddr_in6*) AOK ai->ai_addr; 3993 sin2 = (struct sockaddr_in6*) AOK sa; 3994 if (!bcmp(&sin1->sin6_addr, &sin2->sin6_addr, sizeof(sin1->sin6_addr))) 3995 return (hp); 3996 } 3997 } 3998 break; 3999 case GT_NET: 4000 if (grp->gr_u.gt_net.nt_family != sa->sa_family) 4001 break; 4002 if (grp->gr_u.gt_net.nt_family == AF_INET) { 4003 in_addr_t ina = ((struct sockaddr_in*) AOK sa)->sin_addr.s_addr; 4004 if ((ina & grp->gr_u.gt_net.nt_mask) == grp->gr_u.gt_net.nt_net) 4005 return (hp); 4006 } else if (grp->gr_u.gt_net.nt_family == AF_INET6) { 4007 struct sockaddr_in6 *sa6 = (struct sockaddr_in6*) AOK sa; 4008 struct in6_addr ina; 4009 for (i=0; i < (int)sizeof(ina.s6_addr); i++) 4010 ina.s6_addr[i] = sa6->sin6_addr.s6_addr[i] & grp->gr_u.gt_net.nt_mask6.s6_addr[i]; 4011 if (!bcmp(&ina, &grp->gr_u.gt_net.nt_net6, sizeof(ina))) 4012 return (hp); 4013 } 4014 break; 4015 } 4016 } 4017 4018 return (NULL); 4019} 4020 4021/* 4022 * Parse the option string and update fields. 4023 * Option arguments may either be -<option>=<value> or 4024 * -<option> <value> 4025 */ 4026int 4027do_opt( char **cpp, 4028 char **endcpp, 4029 struct grouplist *ngrp, 4030 int *hostcountp, 4031 int *opt_flagsp, 4032 int *exflagsp, 4033 struct xucred *cr, 4034 struct nfs_sec *sec_flavs, 4035 char *fspath, 4036 u_char *fsuuid) 4037{ 4038 char *cpoptarg = NULL, *cpoptend = NULL; 4039 char *cp, *endcp, *cpopt, *cpu, savedc, savedc2 = '\0', savedc3 = '\0'; 4040 int mapallflag, usedarg; 4041 int i, rv = 0; 4042 size_t len; 4043 4044 cpopt = *cpp; 4045 cpopt++; 4046 cp = *endcpp; 4047 savedc = *cp; 4048 *cp = '\0'; 4049 while (cpopt && *cpopt) { 4050 mapallflag = 1; 4051 usedarg = -2; 4052 if (NULL != (cpoptend = strchr(cpopt, ','))) { 4053 *cpoptend++ = '\0'; 4054 if (NULL != (cpoptarg = strchr(cpopt, '='))) { 4055 savedc3 = *cpoptarg; 4056 *cpoptarg++ = '\0'; 4057 } 4058 } else { 4059 if (NULL != (cpoptarg = strchr(cpopt, '='))) { 4060 savedc3 = *cpoptarg; 4061 *cpoptarg++ = '\0'; 4062 } else { 4063 *cp = savedc; 4064 nextfield(&cp, &endcp); 4065 **endcpp = '\0'; 4066 if (endcp > cp && *cp != '-') { 4067 cpoptarg = cp; 4068 savedc2 = *endcp; 4069 *endcp = '\0'; 4070 usedarg = 0; 4071 } 4072 } 4073 } 4074 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) { 4075 *exflagsp |= NX_READONLY; 4076 *opt_flagsp |= OP_READONLY; 4077 } else if (cpoptarg && (!strcmp(cpopt, "maproot") || 4078 !(mapallflag = strcmp(cpopt, "mapall")) || 4079 !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) { 4080 usedarg++; 4081 rv = parsecred(cpoptarg, cr); 4082 if (rv) { 4083 log(LOG_ERR, "map credential error"); 4084 goto out; 4085 } else if (mapallflag == 0) { 4086 *exflagsp |= NX_MAPALL; 4087 *opt_flagsp |= OP_MAPALL; 4088 } else { 4089 *exflagsp |= NX_MAPROOT; 4090 *opt_flagsp |= OP_MAPROOT; 4091 } 4092 } else if (cpoptarg && (!strcmp(cpopt, "mask") || 4093 !strcmp(cpopt, "m"))) { 4094 if (*opt_flagsp & OP_MASK) { 4095 log(LOG_ERR, "Network option conflict"); 4096 rv = 1; 4097 goto out; 4098 } 4099 if (get_net(cpoptarg, &ngrp->gr_u.gt_net, 1)) { 4100 log(LOG_ERR, "Bad mask: %s", cpoptarg); 4101 rv = 1; 4102 goto out; 4103 } 4104 usedarg++; 4105 *opt_flagsp |= OP_MASK; 4106 } else if (cpoptarg && (!strcmp(cpopt, "network") || 4107 !strcmp(cpopt, "n"))) { 4108 if (*opt_flagsp & OP_NET) { 4109 log(LOG_ERR, "Network option conflict"); 4110 rv = 1; 4111 goto out; 4112 } 4113 if (get_net(cpoptarg, &ngrp->gr_u.gt_net, 0)) { 4114 log(LOG_ERR, "Bad net: %s", cpoptarg); 4115 rv = 1; 4116 goto out; 4117 } 4118 ngrp->gr_type = GT_NET; 4119 *hostcountp = *hostcountp + 1; 4120 usedarg++; 4121 *opt_flagsp |= OP_NET; 4122 } else if (cpoptarg && (!strcmp(cpopt, "sec"))) { 4123 if (*opt_flagsp & OP_SECFLAV) 4124 log(LOG_WARNING, "A security option was already specified and will be replaced."); 4125 if (get_sec_flavors(cpoptarg, sec_flavs)) { 4126 log(LOG_ERR, "Bad security option: %s", cpoptarg); 4127 rv = 1; 4128 goto out; 4129 } 4130 usedarg++; 4131 *opt_flagsp |= OP_SECFLAV; 4132 } else if (!strcmp(cpopt, "alldirs")) { 4133 *opt_flagsp |= OP_ALLDIRS; 4134 } else if (!strcmp(cpopt, "32bitclients")) { 4135 *exflagsp |= NX_32BITCLIENTS; 4136 *opt_flagsp |= OP_32BITCLIENTS; 4137 } else if (!strcmp(cpopt, "manglednames")) { 4138 *exflagsp |= NX_MANGLEDNAMES; 4139 *opt_flagsp |= OP_MANGLEDNAMES; 4140 } else if (!strcmp(cpopt, "fspath")) { 4141 if (!cpoptarg) { 4142 log(LOG_WARNING, "export option '%s' missing a value.", cpopt); 4143 } else if (cpoptarg[0] != '/') { 4144 log(LOG_ERR, "invalid fspath: %s", cpoptarg); 4145 rv = 1; 4146 goto out; 4147 } else { 4148 len = strlcpy(fspath, cpoptarg, MAXPATHLEN); 4149 if (len >= MAXPATHLEN) { 4150 log(LOG_ERR, "%s option path too long: %s", cpopt, cpoptarg); 4151 rv = 1; 4152 goto out; 4153 } 4154 *opt_flagsp |= OP_FSPATH; 4155 } 4156 } else if (!strcmp(cpopt, "fsuuid")) { 4157 if (!cpoptarg) { 4158 log(LOG_WARNING, "export option '%s' missing a value.", cpopt); 4159 } else { 4160 cpu = cpoptarg; 4161 for (i=0; i < 16; i++, cpu+=2) { 4162 if (*cpu == '-') 4163 cpu++; 4164 if (!isxdigit(*cpu) || !isxdigit(*(cpu+1))) { 4165 log(LOG_ERR, "invalid fsuuid: %s", cpoptarg); 4166 rv = 1; 4167 goto out; 4168 } 4169 fsuuid[i] = HEXSTRTOI(cpu); 4170 } 4171 *opt_flagsp |= OP_FSUUID; 4172 } 4173 } else if (!strcmp(cpopt, "offline")) { 4174 *exflagsp |= NX_OFFLINE; 4175 *opt_flagsp |= OP_OFFLINE; 4176 } else { 4177 log(LOG_WARNING, "unrecognized export option: %s", cpopt); 4178 goto out; 4179 } 4180 if (usedarg >= 0) { 4181 *endcp = savedc2; 4182 **endcpp = savedc; 4183 if (usedarg > 0) { 4184 *cpp = cp; 4185 *endcpp = endcp; 4186 } 4187 return (0); 4188 } 4189 if (cpoptend) 4190 *(cpoptend-1) = ','; 4191 if (savedc3) { 4192 *(cpoptarg-1) = savedc3; 4193 savedc3 = '\0'; 4194 } 4195 cpopt = cpoptend; 4196 } 4197out: 4198 if (savedc2) 4199 *endcp = savedc2; 4200 if (cpoptend) 4201 *(cpoptend-1) = ','; 4202 if (savedc3) 4203 *(cpoptarg-1) = savedc3; 4204 **endcpp = savedc; 4205 return (rv); 4206} 4207 4208/* 4209 * Translate a character string to the corresponding list of network 4210 * addresses for a hostname. 4211 */ 4212int 4213get_host_addresses(char *cp, struct grouplist *grp) 4214{ 4215 struct addrinfo *ailist, aihints; 4216 4217 if (grp->gr_type != GT_NULL) 4218 return (1); 4219 4220 bzero(&aihints, sizeof(aihints)); 4221 aihints.ai_socktype = SOCK_STREAM; 4222 if (getaddrinfo(cp, NULL, &aihints, &ailist)) { 4223 log(LOG_ERR, "getaddrinfo() failed for %s", cp); 4224 hostnamecount++; 4225 return (1); 4226 } 4227 hostnamecount++; 4228 hostnamegoodcount++; 4229 grp->gr_type = GT_HOST; 4230 grp->gr_u.gt_hostinfo.h_ailist = ailist; 4231 grp->gr_u.gt_hostinfo.h_name = strdup(cp); 4232 return (0); 4233} 4234 4235 4236/* 4237 * Free up an exported directory structure 4238 */ 4239void 4240free_expdir(struct expdir *xd) 4241{ 4242 struct expdir *xd2; 4243 4244 free_hosts(&xd->xd_hosts); 4245 while ((xd2 = TAILQ_FIRST(&xd->xd_mountdirs))) { 4246 TAILQ_REMOVE(&xd->xd_mountdirs, xd2, xd_next); 4247 free_expdir(xd2); 4248 } 4249 if (xd->xd_dir) 4250 free(xd->xd_dir); 4251 free(xd); 4252} 4253 4254/* 4255 * Free up an exportfs structure 4256 */ 4257void 4258free_expfs(struct expfs *xf) 4259{ 4260 struct expdir *xd; 4261 4262 while ((xd = TAILQ_FIRST(&xf->xf_dirl))) { 4263 TAILQ_REMOVE(&xf->xf_dirl, xd, xd_next); 4264 free_expdir(xd); 4265 } 4266 if (xf->xf_fsdir) 4267 free(xf->xf_fsdir); 4268 free(xf); 4269} 4270 4271/* 4272 * Free up a host. 4273 */ 4274void 4275free_host(struct host *hp) 4276{ 4277 if (hp->ht_grp) 4278 free_grp(hp->ht_grp); 4279 free(hp); 4280} 4281 4282/* 4283 * Free up a list of hosts. 4284 */ 4285void 4286free_hosts(struct hosttqh *head) 4287{ 4288 struct host *hp, *hp2; 4289 4290 TAILQ_FOREACH_SAFE(hp, head, ht_next, hp2) { 4291 TAILQ_REMOVE(head, hp, ht_next); 4292 free_host(hp); 4293 } 4294} 4295 4296/* 4297 * Allocate a host structure 4298 */ 4299struct host * 4300get_host(void) 4301{ 4302 struct host *hp; 4303 4304 hp = malloc(sizeof(struct host)); 4305 if (hp == NULL) 4306 return (NULL); 4307 hp->ht_flags = 0; 4308 return (hp); 4309} 4310 4311/* 4312 * Do the NFSSVC_EXPORT syscall to push the export info into the kernel. 4313 */ 4314int 4315do_export( 4316 int expcmd, 4317 struct expfs *xf, 4318 struct expdir *xd, 4319 struct grouplist *grplist, 4320 int exflags, 4321 struct xucred *cr, 4322 struct nfs_sec *secflavs) 4323{ 4324 struct nfs_export_args nxa; 4325 struct nfs_export_net_args *netargs, *na; 4326 struct grouplist *grp; 4327 struct addrinfo *ai; 4328 struct sockaddr_in *sin, *imask; 4329 struct sockaddr_in6 *sin6, *imask6; 4330 uint32_t net; 4331 4332 nxa.nxa_flags = expcmd; 4333 if ((exflags & NX_OFFLINE) && (expcmd != NXA_CHECK)) 4334 nxa.nxa_flags |= NXA_OFFLINE; 4335 nxa.nxa_fsid = xf->xf_fsid; 4336 nxa.nxa_fspath = xf->xf_fsdir; 4337 nxa.nxa_exppath = xd->xd_xid->xid_path; 4338 nxa.nxa_expid = xd->xd_xid->xid_id; 4339 4340 /* first, count the number of hosts/nets we're pushing in for this export */ 4341 /* !grplist => default export */ 4342 nxa.nxa_netcount = (!grplist ? 1 : 0); 4343 grp = grplist; 4344 while (grp) { 4345 if (grp->gr_type == GT_HOST) { 4346 /* count # addresses given for this host */ 4347 for (ai = grp->gr_u.gt_hostinfo.h_ailist; ai; ai = ai->ai_next) 4348 if ((ai->ai_family == AF_INET) || (ai->ai_family == AF_INET6)) 4349 nxa.nxa_netcount++; 4350 } else if (grp->gr_type == GT_NET) { 4351 nxa.nxa_netcount++; 4352 } 4353 grp = grp->gr_next; 4354 } 4355 4356 netargs = malloc(nxa.nxa_netcount * sizeof(struct nfs_export_net_args)); 4357 if (!netargs) { 4358 /* XXX we could possibly fall back to pushing them in, one-by-one */ 4359 log(LOG_ERR, "do_export(): malloc failed for %d net args", nxa.nxa_netcount); 4360 return (1); 4361 } 4362 nxa.nxa_nets = netargs; 4363 4364#define INIT_NETARG(N, F) \ 4365 do { \ 4366 (N)->nxna_flags = exflags; \ 4367 (N)->nxna_cred = cr ? *cr : def_anon; \ 4368 memset(&(N)->nxna_sec, 0, sizeof(struct nfs_sec)); \ 4369 if (secflavs != NULL) \ 4370 bcopy(secflavs, &(N)->nxna_sec, sizeof(struct nfs_sec)); \ 4371 if ((F) == AF_INET) { \ 4372 sin = (struct sockaddr_in*)&(N)->nxna_addr; \ 4373 imask = (struct sockaddr_in*)&(N)->nxna_mask; \ 4374 memset(sin, 0, sizeof(*sin)); \ 4375 memset(imask, 0, sizeof(*imask)); \ 4376 sin->sin_family = AF_INET; \ 4377 sin->sin_len = sizeof(*sin); \ 4378 imask->sin_family = AF_INET; \ 4379 imask->sin_len = sizeof(*imask); \ 4380 } else if ((F) == AF_INET6) { \ 4381 sin6 = (struct sockaddr_in6*)&(N)->nxna_addr; \ 4382 imask6 = (struct sockaddr_in6*)&(N)->nxna_mask; \ 4383 memset(sin6, 0, sizeof(*sin6)); \ 4384 memset(imask6, 0, sizeof(*imask6)); \ 4385 sin6->sin6_family = AF_INET6; \ 4386 sin6->sin6_len = sizeof(*sin6); \ 4387 imask6->sin6_family = AF_INET6; \ 4388 imask6->sin6_len = sizeof(*imask6); \ 4389 } \ 4390 } while (0) 4391 4392 na = netargs; 4393 if (!grplist) { 4394 /* default export, no address */ 4395 INIT_NETARG(na, AF_INET); 4396 sin->sin_len = 0; 4397 imask->sin_len = 0; 4398 na++; 4399 } 4400 grp = grplist; 4401 while (grp) { 4402 switch (grp->gr_type) { 4403 case GT_HOST: 4404 /* handle each host address in the list */ 4405 for (ai = grp->gr_u.gt_hostinfo.h_ailist; ai; ai = ai->ai_next) { 4406 if ((ai->ai_family != AF_INET) && (ai->ai_family != AF_INET6)) 4407 continue; 4408 INIT_NETARG(na, ai->ai_family); 4409 if (ai->ai_family == AF_INET) { 4410 sin->sin_addr.s_addr = ((struct sockaddr_in*) AOK ai->ai_addr)->sin_addr.s_addr; 4411 imask->sin_len = 0; 4412 } else if (ai->ai_family == AF_INET6) { 4413 bcopy(&((struct sockaddr_in6*) AOK ai->ai_addr)->sin6_addr, &sin6->sin6_addr, 4414 sizeof(sin6->sin6_addr)); 4415 imask6->sin6_len = 0; 4416 } 4417 na++; 4418 } 4419 break; 4420 case GT_NET: 4421 INIT_NETARG(na, grp->gr_u.gt_net.nt_family); 4422 if (grp->gr_u.gt_net.nt_family == AF_INET) { 4423 if (grp->gr_u.gt_net.nt_mask) 4424 imask->sin_addr.s_addr = grp->gr_u.gt_net.nt_mask; 4425 else { 4426 net = ntohl(grp->gr_u.gt_net.nt_net); 4427 if (IN_CLASSA(net)) 4428 imask->sin_addr.s_addr = inet_addr("255.0.0.0"); 4429 else if (IN_CLASSB(net)) 4430 imask->sin_addr.s_addr = 4431 inet_addr("255.255.0.0"); 4432 else 4433 imask->sin_addr.s_addr = 4434 inet_addr("255.255.255.0"); 4435 grp->gr_u.gt_net.nt_mask = imask->sin_addr.s_addr; 4436 } 4437 sin->sin_addr.s_addr = grp->gr_u.gt_net.nt_net; 4438 na++; 4439 } else if (grp->gr_u.gt_net.nt_family == AF_INET6) { 4440 bcopy(&grp->gr_u.gt_net.nt_net6, &sin6->sin6_addr, sizeof(sin6->sin6_addr)); 4441 bcopy(&grp->gr_u.gt_net.nt_mask6, &imask6->sin6_addr, sizeof(imask6->sin6_addr)); 4442 na++; 4443 } 4444 break; 4445 case GT_NETGROUP: 4446 break; 4447 default: 4448 log(LOG_ERR, "Bad grouptype"); 4449 free(netargs); 4450 return (1); 4451 } 4452 4453 grp = grp->gr_next; 4454 } 4455 4456 if (nfssvc(NFSSVC_EXPORT, &nxa)) { 4457 if ((expcmd != NXA_CHECK) && (expcmd != NXA_DELETE) && (errno == EPERM)) { 4458 log(LOG_ERR, "Can't change attributes for %s. See 'exports' man page.", 4459 xd->xd_dir); 4460 free(netargs); 4461 return (1); 4462 } 4463 log(LOG_ERR, "Can't %sexport %s: %s (%d)", 4464 (expcmd == NXA_DELETE) ? "un" : "", 4465 xd->xd_dir, strerror(errno), errno); 4466 free(netargs); 4467 return (1); 4468 } 4469 4470 free(netargs); 4471 return (0); 4472} 4473 4474/* 4475 * Translate a net address. 4476 */ 4477int 4478get_net(char *cp, struct netmsk *net, int maskflg) 4479{ 4480 struct netent *np; 4481 uint32_t netaddr; 4482 struct in_addr inetaddr, inetaddr2; 4483 struct in6_addr inet6addr; 4484 const char *name; 4485 sa_family_t family; 4486 char addrbuf[2*INET6_ADDRSTRLEN]; 4487 4488 if (NULL != (np = getnetbyname(cp))) { 4489 inetaddr = inet_makeaddr(np->n_net, 0); 4490 family = AF_INET; 4491 } else if (inet_pton(AF_INET6, cp, &inet6addr) == 1) { 4492 family = AF_INET6; 4493 } else if (isdigit(*cp)) { 4494 if ((netaddr = inet_network(cp)) == INADDR_NONE) 4495 return (1); 4496 inetaddr = inet_makeaddr(netaddr, 0); 4497 family = AF_INET; 4498 /* 4499 * Due to arbritrary subnet masks, you don't know how many 4500 * bits to shift the address to make it into a network, 4501 * however you do know how to make a network address into 4502 * a host with host == 0 and then compare them. 4503 * (What a pest) 4504 */ 4505 if (!maskflg) { 4506 setnetent(0); 4507 while (NULL != (np = getnetent())) { 4508 inetaddr2 = inet_makeaddr(np->n_net, 0); 4509 if (inetaddr2.s_addr == inetaddr.s_addr) 4510 break; 4511 } 4512 endnetent(); 4513 } 4514 } else { 4515 return (1); 4516 } 4517 if (maskflg) { 4518 if (net->nt_family == AF_UNSPEC) { 4519 net->nt_family = family; 4520 } else if (net->nt_family != family) { 4521 log(LOG_ERR, "net mask family (%d) does not match net address family (%d): %s", family, net->nt_family, cp); 4522 return (1); 4523 } 4524 if (family == AF_INET6) 4525 net->nt_mask6 = inet6addr; 4526 else 4527 net->nt_mask = inetaddr.s_addr; 4528 } else { 4529 if (net->nt_family == AF_UNSPEC) { 4530 net->nt_family = family; 4531 } else if (net->nt_family != family) { 4532 log(LOG_ERR, "net mask family (%d) does not match net address family (%d): %s", net->nt_family, family, cp); 4533 return (1); 4534 } 4535 if (np) { 4536 name = np->n_name; 4537 } else if (family == AF_INET6) { 4538 name = inet_ntop(AF_INET6, &inet6addr, addrbuf, sizeof(addrbuf)); 4539 if (!name) { 4540 log(LOG_ERR, "can't convert IPv6 addr to name: %s", cp); 4541 return (1); 4542 } 4543 } else { 4544 name = inet_ntoa(inetaddr); 4545 } 4546 net->nt_name = strdup(name); 4547 if (net->nt_name == NULL) { 4548 log(LOG_ERR, "can't allocate memory for net: %s", cp); 4549 return (1); 4550 } 4551 if (family == AF_INET6) 4552 net->nt_net6 = inet6addr; 4553 else 4554 net->nt_net = inetaddr.s_addr; 4555 DEBUG(3, "got net: %s", net->nt_name); 4556 } 4557 return (0); 4558} 4559 4560/* 4561 * Parse security flavors 4562 */ 4563int 4564get_sec_flavors(char *flavorlist, struct nfs_sec *sec_flavs) 4565{ 4566 char *flavorlistcopy; 4567 char *flavor; 4568 u_int32_t flav_bits; 4569 4570#define SYS_BIT 0x00000001 4571#define KRB5_BIT 0x00000002 4572#define KRB5I_BIT 0x00000004 4573#define KRB5P_BIT 0x00000008 4574 4575 /* try to make a copy of the string so we don't butcher the original */ 4576 flavorlistcopy = strdup(flavorlist); 4577 if (flavorlistcopy) 4578 flavorlist = flavorlistcopy; 4579 4580 bzero(sec_flavs, sizeof(struct nfs_sec)); 4581 flav_bits = 0; 4582 while ( ((flavor = strsep(&flavorlist, ":")) != NULL) && (sec_flavs->count < NX_MAX_SEC_FLAVORS)) { 4583 if (flavor[0] == '\0') 4584 continue; 4585 if (!strcmp("krb5p", flavor)) { 4586 if (flav_bits & KRB5P_BIT) { 4587 log(LOG_WARNING, "krb5p appears more than once: %s", flavorlist); 4588 continue; 4589 } 4590 flav_bits |= KRB5P_BIT; 4591 sec_flavs->flavors[sec_flavs->count++] = RPCAUTH_KRB5P; 4592 } else if (!strcmp("krb5i", flavor)) { 4593 if (flav_bits & KRB5I_BIT) { 4594 log(LOG_WARNING, "krb5i appears more than once: %s", flavorlist); 4595 continue; 4596 } 4597 flav_bits |= KRB5I_BIT; 4598 sec_flavs->flavors[sec_flavs->count++] = RPCAUTH_KRB5I; 4599 } else if (!strcmp("krb5", flavor)) { 4600 if (flav_bits & KRB5_BIT) { 4601 log(LOG_WARNING, "krb5 appears more than once: %s", flavorlist); 4602 continue; 4603 } 4604 flav_bits |= KRB5_BIT; 4605 sec_flavs->flavors[sec_flavs->count++] = RPCAUTH_KRB5; 4606 } else if (!strcmp("sys", flavor)) { 4607 if (flav_bits & SYS_BIT) { 4608 log(LOG_WARNING, "Security mechanism 'sys' appears more than once: %s", flavorlist); 4609 continue; 4610 } 4611 flav_bits |= SYS_BIT; 4612 sec_flavs->flavors[sec_flavs->count++] = RPCAUTH_SYS; 4613 } else { 4614 log(LOG_ERR, "Unknown security mechanism '%s'. See the exports(5) man page.", flavor); 4615 bzero(sec_flavs, sizeof(struct nfs_sec)); 4616 break; 4617 } 4618 } 4619 4620 if (flavorlistcopy) 4621 free(flavorlistcopy); 4622 4623 if (sec_flavs->count) 4624 return 0; 4625 else 4626 return 1; 4627} 4628 4629/* 4630 * Compare two security flavor structs 4631 */ 4632int 4633cmp_secflavs(struct nfs_sec *sf1, struct nfs_sec *sf2) 4634{ 4635 int32_t i; 4636 4637 if (sf1->count != sf2->count) 4638 return 1; 4639 for (i = 0; i < sf1->count; i++) 4640 if (sf1->flavors[i] != sf2->flavors[i]) 4641 return 1; 4642 return 0; 4643} 4644 4645/* 4646 * merge new security flavors into a current set 4647 */ 4648void 4649merge_secflavs(struct nfs_sec *cur, struct nfs_sec *new) 4650{ 4651 int32_t i, j; 4652 4653 for (i = 0; i < new->count; i++) { 4654 for (j = 0; j < cur->count; j++) 4655 if (new->flavors[i] == cur->flavors[j]) 4656 break; 4657 if (j < cur->count) 4658 continue; 4659 if (cur->count < NX_MAX_SEC_FLAVORS) { 4660 cur->flavors[j] = new->flavors[i]; 4661 cur->count++; 4662 } 4663 } 4664} 4665 4666/* 4667 * Find the next field in a line. 4668 * Fields are separated by white space. 4669 * Space, tab, and quote characters may be escaped. 4670 * Quoted strings are not broken at white space. 4671 */ 4672void 4673nextfield(char **line_start, char **line_end) 4674{ 4675 char *a, q; 4676 u_int32_t esc; 4677 4678 if (line_start == NULL) 4679 return; 4680 a = *line_start; 4681 4682 /* Skip white space */ 4683 while (*a == ' ' || *a == '\t') 4684 a++; 4685 *line_start = a; 4686 4687 /* Stop at end of line */ 4688 if (*a == '\n' || *a == '\0') { 4689 *line_end = a; 4690 return; 4691 } 4692 4693 /* Check for single or double quote */ 4694 if (*a == '\'' || *a == '"') { 4695 q = *a; 4696 a++; 4697 for (esc = 0; *a != '\0'; a++) { 4698 if (esc) 4699 esc = 0; 4700 else if (*a == '\\') 4701 esc = 1; 4702 else if (*a == q || *a == '\n') 4703 break; 4704 } 4705 if (*a == q) 4706 a++; 4707 *line_end = a; 4708 return; 4709 } 4710 4711 /* Skip to next non-escaped white space or end of line */ 4712 for (;; a++) { 4713 if (*a == '\0' || *a == '\n') 4714 break; 4715 else if (*a == '\\') { 4716 a++; 4717 if (*a == '\n' || *a == '\0') 4718 break; 4719 } else if (*a == ' ' || *a == '\t') 4720 break; 4721 } 4722 4723 *line_end = a; 4724} 4725 4726/* 4727 * Get an exports file line. Skip over blank lines and handle line continuations. 4728 * (char *line is a global) 4729 */ 4730int 4731get_export_entry(void) 4732{ 4733 char *p, *cp, *newline; 4734 size_t len, totlen; 4735 int cont_line; 4736 4737 if (linenum == 0) 4738 linenum = 1; 4739 else 4740 linenum += entrylines; 4741 entrylines = 1; 4742 4743 /* 4744 * Loop around ignoring blank lines and getting all continuation lines. 4745 */ 4746 totlen = 0; 4747 do { 4748 if ((p = fgetln(exp_file, &len)) == NULL) 4749 return (0); 4750 cp = p + len - 1; 4751 cont_line = 0; 4752 while (cp >= p && 4753 (*cp == ' ' || *cp == '\t' || *cp == '\n' || 4754 *cp == '\\')) { 4755 if (*cp == '\\') 4756 cont_line = 1; 4757 cp--; 4758 len--; 4759 } 4760 if (linesize < (totlen + len + 1)) { 4761 newline = realloc(line, (totlen + len + 1)); 4762 if (!newline) { 4763 log(LOG_ERR, "Exports line too long, can't allocate memory"); 4764 return (0); 4765 } 4766 line = newline; 4767 linesize = (totlen + len + 1); 4768 } 4769 memcpy(line + totlen, p, len); 4770 totlen += len; 4771 line[totlen] = '\0'; 4772 if (cont_line) { 4773 entrylines++; 4774 } else if (totlen == 0) { 4775 linenum += entrylines; 4776 entrylines = 1; 4777 } 4778 } while (totlen == 0 || cont_line); 4779 return (1); 4780} 4781 4782/* 4783 * Parse a description of a credential. 4784 */ 4785int 4786parsecred(char *namelist, struct xucred *cr) 4787{ 4788 char *namelistcopy; 4789 char *name; 4790 int cnt; 4791 char *names; 4792 struct passwd *pw; 4793 struct group *gr; 4794 int ngroups, groups[NGROUPS]; 4795 4796 /* try to make a copy of the string so we don't butcher the original */ 4797 namelistcopy = strdup(namelist); 4798 if (namelistcopy) 4799 namelist = namelistcopy; 4800 4801 /* 4802 * Set up the unpriviledged user. 4803 */ 4804 cr->cr_version = XUCRED_VERSION; 4805 cr->cr_uid = -2; 4806 cr->cr_groups[0] = -2; 4807 cr->cr_ngroups = 1; 4808 /* 4809 * Get the user's password table entry. 4810 */ 4811 names = strsep(&namelist, " \t\n"); 4812 name = strsep(&names, ":"); 4813 if (isdigit(*name) || *name == '-') 4814 pw = getpwuid(atoi(name)); 4815 else 4816 pw = getpwnam(name); 4817 /* 4818 * Credentials specified as those of a user. 4819 */ 4820 if (names == NULL) { 4821 if (pw == NULL) { 4822 log(LOG_ERR, "Unknown user: %s", name); 4823 if (namelistcopy) 4824 free(namelistcopy); 4825 return (ENOENT); 4826 } 4827 cr->cr_uid = pw->pw_uid; 4828 ngroups = NGROUPS; 4829 if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) 4830 log(LOG_NOTICE, "Too many groups for %s", pw->pw_name); 4831 /* Convert from int's to gid_t's */ 4832 cr->cr_ngroups = (ngroups <= NGROUPS) ? ngroups : NGROUPS; 4833 for (cnt = 0; cnt < cr->cr_ngroups; cnt++) 4834 cr->cr_groups[cnt] = groups[cnt]; 4835 if (namelistcopy) 4836 free(namelistcopy); 4837 goto out; 4838 } 4839 /* 4840 * Explicit credential specified as a colon separated list: 4841 * uid:gid:gid:... 4842 */ 4843 if (pw != NULL) 4844 cr->cr_uid = pw->pw_uid; 4845 else if (isdigit(*name) || *name == '-') 4846 cr->cr_uid = atoi(name); 4847 else { 4848 log(LOG_ERR, "Unknown user: %s", name); 4849 if (namelistcopy) 4850 free(namelistcopy); 4851 return (ENOENT); 4852 } 4853 cr->cr_ngroups = 0; 4854 while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) { 4855 name = strsep(&names, ":"); 4856 if (isdigit(*name) || *name == '-') { 4857 cr->cr_groups[cr->cr_ngroups++] = atoi(name); 4858 } else { 4859 if ((gr = getgrnam(name)) == NULL) { 4860 log(LOG_ERR, "Unknown group: %s", name); 4861 continue; 4862 } 4863 cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid; 4864 } 4865 } 4866 if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS) 4867 log(LOG_ERR, "Too many groups in %s", namelist); 4868 if (namelistcopy) 4869 free(namelistcopy); 4870out: 4871 if (config.verbose >= 5) { 4872 char buf[256]; 4873 snprintf_cred(buf, sizeof(buf), cr); 4874 DEBUG(3, "got cred: %s", buf); 4875 } 4876 if (cr->cr_ngroups < 1) { 4877 log(LOG_ERR, "no groups found: %s", namelist); 4878 return (EINVAL); 4879 } 4880 return (0); 4881} 4882 4883#define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50) 4884/* 4885 * Routines that maintain the remote mounttab 4886 */ 4887void 4888get_mountlist(void) 4889{ 4890 struct mountlist *mlp, *lastmlp; 4891 char *host, *dir, *cp; 4892 char str[STRSIZ]; 4893 FILE *mlfile; 4894 int hlen, dlen; 4895 4896 if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) { 4897 if (errno != ENOENT) 4898 log(LOG_ERR, "Can't open %s: %s (%d)", 4899 _PATH_RMOUNTLIST, strerror(errno), errno); 4900 else 4901 DEBUG(1, "Can't open %s: %s (%d)", 4902 _PATH_RMOUNTLIST, strerror(errno), errno); 4903 return; 4904 } 4905 lastmlp = NULL; 4906 while (fgets(str, STRSIZ, mlfile) != NULL) { 4907 cp = str; 4908 host = strsep(&cp, " \t\n"); 4909 dir = strsep(&cp, " \t\n"); 4910 if ((host == NULL) || (dir == NULL)) 4911 continue; 4912 hlen = strlen(host); 4913 if (hlen > RPCMNT_NAMELEN) 4914 hlen = RPCMNT_NAMELEN; 4915 dlen = strlen(dir); 4916 if (dlen > RPCMNT_PATHLEN) 4917 dlen = RPCMNT_PATHLEN; 4918 mlp = malloc(sizeof(*mlp)); 4919 if (mlp) { 4920 mlp->ml_host = malloc(hlen+1); 4921 mlp->ml_dir = malloc(dlen+1); 4922 } 4923 if (!mlp || !mlp->ml_host || !mlp->ml_dir) { 4924 log(LOG_ERR, "can't allocate memory while reading in mount list: %s %s", 4925 host, dir); 4926 if (mlp) { 4927 if (mlp->ml_host) 4928 free(mlp->ml_host); 4929 if (mlp->ml_dir) 4930 free(mlp->ml_dir); 4931 free(mlp); 4932 } 4933 break; 4934 } 4935 strncpy(mlp->ml_host, host, hlen); 4936 mlp->ml_host[hlen] = '\0'; 4937 strncpy(mlp->ml_dir, dir, dlen); 4938 mlp->ml_dir[dlen] = '\0'; 4939 mlp->ml_next = NULL; 4940 if (lastmlp) 4941 lastmlp->ml_next = mlp; 4942 else 4943 mlhead = mlp; 4944 lastmlp = mlp; 4945 } 4946 fclose(mlfile); 4947} 4948 4949void 4950del_mlist(char *host, char *dir) 4951{ 4952 struct mountlist *mlp, **mlpp; 4953 struct mountlist *mlp2; 4954 FILE *mlfile; 4955 int fnd = 0; 4956 4957 mlpp = &mlhead; 4958 mlp = mlhead; 4959 while (mlp) { 4960 if (!strcmp(mlp->ml_host, host) && 4961 (!dir || !strcmp(mlp->ml_dir, dir))) { 4962 fnd = 1; 4963 mlp2 = mlp; 4964 *mlpp = mlp = mlp->ml_next; 4965 free(mlp2->ml_host); 4966 free(mlp2->ml_dir); 4967 free(mlp2); 4968 } else { 4969 mlpp = &mlp->ml_next; 4970 mlp = mlp->ml_next; 4971 } 4972 } 4973 if (fnd) { 4974 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) { 4975 log(LOG_ERR, "Can't write %s: %s (%d)", 4976 _PATH_RMOUNTLIST, strerror(errno), errno); 4977 return; 4978 } 4979 mlp = mlhead; 4980 while (mlp) { 4981 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dir); 4982 mlp = mlp->ml_next; 4983 } 4984 fclose(mlfile); 4985 } 4986} 4987 4988void 4989add_mlist(char *host, char *dir) 4990{ 4991 struct mountlist *mlp, **mlpp; 4992 FILE *mlfile; 4993 int hlen, dlen; 4994 4995 mlpp = &mlhead; 4996 mlp = mlhead; 4997 while (mlp) { 4998 if (!strcmp(mlp->ml_host, host) && !strcmp(mlp->ml_dir, dir)) 4999 return; 5000 mlpp = &mlp->ml_next; 5001 mlp = mlp->ml_next; 5002 } 5003 5004 hlen = strlen(host); 5005 if (hlen > RPCMNT_NAMELEN) 5006 hlen = RPCMNT_NAMELEN; 5007 dlen = strlen(dir); 5008 if (dlen > RPCMNT_PATHLEN) 5009 dlen = RPCMNT_PATHLEN; 5010 5011 mlp = malloc(sizeof(*mlp)); 5012 if (mlp) { 5013 mlp->ml_host = malloc(hlen+1); 5014 mlp->ml_dir = malloc(dlen+1); 5015 } 5016 if (!mlp || !mlp->ml_host || !mlp->ml_dir) { 5017 if (mlp) { 5018 if (mlp->ml_host) 5019 free(mlp->ml_host); 5020 if (mlp->ml_dir) 5021 free(mlp->ml_dir); 5022 free(mlp); 5023 } 5024 log(LOG_ERR, "can't allocate memory to add to mount list: %s %s", host, dir); 5025 return; 5026 } 5027 strncpy(mlp->ml_host, host, hlen); 5028 mlp->ml_host[hlen] = '\0'; 5029 strncpy(mlp->ml_dir, dir, dlen); 5030 mlp->ml_dir[dlen] = '\0'; 5031 mlp->ml_next = NULL; 5032 *mlpp = mlp; 5033 if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) { 5034 log(LOG_ERR, "Can't append %s: %s (%d)", 5035 _PATH_RMOUNTLIST, strerror(errno), errno); 5036 return; 5037 } 5038 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dir); 5039 fclose(mlfile); 5040} 5041 5042/* 5043 * Check options for consistency. 5044 */ 5045int 5046check_options(int opt_flags) 5047{ 5048 if ((opt_flags & (OP_MAPROOT|OP_MAPALL)) == (OP_MAPROOT|OP_MAPALL)) { 5049 log(LOG_ERR, "-mapall and -maproot mutually exclusive"); 5050 return (1); 5051 } 5052 if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) { 5053 log(LOG_ERR, "-mask requires -net"); 5054 return (1); 5055 } 5056 return (0); 5057} 5058 5059/* 5060 * Check an absolute directory path for any symbolic links. Return true 5061 * if no symbolic links are found. 5062 */ 5063int 5064check_dirpath(char *dir) 5065{ 5066 char *cp; 5067 int ret = 1; 5068 struct stat sb; 5069 5070 for (cp = dir + 1; *cp && ret; cp++) 5071 if (*cp == '/') { 5072 *cp = '\0'; 5073 if ((lstat(dir, &sb) < 0) || !S_ISDIR(sb.st_mode)) 5074 ret = 0; 5075 *cp = '/'; 5076 } 5077 if (ret && ((lstat(dir, &sb) < 0) || !S_ISDIR(sb.st_mode))) 5078 ret = 0; 5079 return (ret); 5080} 5081 5082void 5083snprintf_cred(char *buf, int buflen, struct xucred *cr) 5084{ 5085 char crbuf2[32]; 5086 int i; 5087 5088 buf[0] = '\0'; 5089 if (!cr) 5090 return; 5091 snprintf(crbuf2, sizeof(crbuf2), "%d", cr->cr_uid); 5092 strlcat(buf, crbuf2, buflen); 5093 for (i=0; i < cr->cr_ngroups; i++) { 5094 snprintf(crbuf2, sizeof(crbuf2), ":%d", cr->cr_groups[i]); 5095 strlcat(buf, crbuf2, buflen); 5096 } 5097} 5098 5099void 5100snprintf_flags(char *buf, int buflen, int flags, struct xucred *cr) 5101{ 5102 char crbuf[256]; 5103 5104 if (flags & (OP_MAPALL|OP_MAPROOT)) 5105 snprintf_cred(crbuf, sizeof(crbuf), cr); 5106 else 5107 crbuf[0] = '\0'; 5108 5109 snprintf(buf, buflen, "FLAGS: 0x%08x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s %s", 5110 flags, 5111 (flags & OP_DEL) ? " DEL" : "", 5112 (flags & OP_ADD) ? " ADD" : "", 5113 (flags & OP_DEFEXP) ? " DEFEXP" : "", 5114 (flags & OP_MISSING) ? " MISSING" : "", 5115 (flags & OP_SHOW) ? " SHOW" : "", 5116 (flags & OP_ONLINE) ? " ONLINE" : "", 5117 (flags & OP_OFFLINE) ? " OFFLINE" : "", 5118 (flags & OP_FSUUID) ? " FSUUID" : "", 5119 (flags & OP_FSPATH) ? " FSPATH" : "", 5120 (flags & OP_32BITCLIENTS) ? " 32" : "", 5121 (flags & OP_READONLY) ? " READONLY" : "", 5122 (flags & OP_ALLDIRS) ? " ALLDIRS" : "", 5123 (flags & OP_NET) ? " NET" : "", 5124 (flags & OP_MASK) ? " MASK" : "", 5125 (flags & OP_SECFLAV) ? " SEC" : "", 5126 (flags & OP_MAPALL) ? " MAPALL" : "", 5127 (flags & OP_MAPROOT) ? " MAPROOT" : "", 5128 crbuf); 5129} 5130 5131void 5132dump_expdir(struct expfs *xf, struct expdir *xd, int mdir) 5133{ 5134 struct expdir *mxd; 5135 struct host *ht; 5136 struct grouplist *gr; 5137 char buf[2048]; 5138 struct addrinfo *ai; 5139 void *sinaddr; 5140 char addrbuf[2*INET6_ADDRSTRLEN]; 5141 const char *s; 5142 5143 snprintf_flags(buf, sizeof(buf), xd->xd_iflags, NULL); 5144 DEBUG(1, " %s %s", xd->xd_dir, buf); 5145 if (!mdir) { 5146 DEBUG(1, " %s/%s", xf->xf_fsdir, xd->xd_xid->xid_path); 5147 DEBUG(1, " XID: 0x%08x", xd->xd_xid->xid_id); 5148 } 5149 snprintf_flags(buf, sizeof(buf), xd->xd_flags, mdir ? NULL : &xd->xd_cred); 5150 DEBUG(1, " %s", buf); 5151 DEBUG(1, " HOSTS:"); 5152 5153 TAILQ_FOREACH(ht, &xd->xd_hosts, ht_next) { 5154 snprintf_flags(buf, sizeof(buf), ht->ht_flags, mdir ? NULL : &ht->ht_cred); 5155 DEBUG(1, " * %s", buf); 5156 gr = ht->ht_grp; 5157 if (gr) { 5158 switch(gr->gr_type) { 5159 case GT_NET: 5160 sinaddr = (gr->gr_u.gt_net.nt_family == AF_INET) ? 5161 (void*)&gr->gr_u.gt_net.nt_net : (void*)&gr->gr_u.gt_net.nt_net6; 5162 if (inet_ntop(gr->gr_u.gt_net.nt_family, sinaddr, addrbuf, sizeof(addrbuf))) 5163 s = addrbuf; 5164 else 5165 s = "???"; 5166 snprintf(buf, sizeof(buf), "%s %s/", grp_name(gr), s); 5167 sinaddr = (gr->gr_u.gt_net.nt_family == AF_INET) ? 5168 (void*)&gr->gr_u.gt_net.nt_mask : (void*)&gr->gr_u.gt_net.nt_mask6; 5169 if (inet_ntop(gr->gr_u.gt_net.nt_family, sinaddr, addrbuf, sizeof(addrbuf))) 5170 s = addrbuf; 5171 else 5172 s = "???"; 5173 strlcat(buf, s, sizeof(buf)); 5174 break; 5175 case GT_HOST: 5176 ai = gr->gr_u.gt_hostinfo.h_ailist; 5177 sinaddr = (ai->ai_family == AF_INET) ? 5178 (void*)&((struct sockaddr_in*) AOK ai->ai_addr)->sin_addr : 5179 (void*)&((struct sockaddr_in6*) AOK ai->ai_addr)->sin6_addr; 5180 if (inet_ntop(ai->ai_family, sinaddr, addrbuf, sizeof(addrbuf))) 5181 s = addrbuf; 5182 else 5183 s = "???"; 5184 snprintf(buf, sizeof(buf), "%s %s", grp_name(gr), s); 5185 ai = ai->ai_next; 5186 while (ai) { 5187 strlcat(buf, " ", sizeof(buf)); 5188 sinaddr = (ai->ai_family == AF_INET) ? 5189 (void*)&((struct sockaddr_in*) AOK ai->ai_addr)->sin_addr : 5190 (void*)&((struct sockaddr_in6*) AOK ai->ai_addr)->sin6_addr; 5191 if (inet_ntop(ai->ai_family, sinaddr, addrbuf, sizeof(addrbuf))) 5192 s = addrbuf; 5193 else 5194 s = "???"; 5195 if (strlcat(buf, s, sizeof(buf)) > 2000) { 5196 strlcat(buf, " ...", sizeof(buf)); 5197 break; 5198 } 5199 ai = ai->ai_next; 5200 } 5201 break; 5202 default: 5203 snprintf(buf, sizeof(buf), "%s", grp_name(gr)); 5204 break; 5205 } 5206 DEBUG(1, " %s", buf); 5207 } /* ht_grp */ 5208 } /* for xd_hosts list */ 5209 5210 if (mdir) 5211 return; 5212 5213 DEBUG(1, " MOUNTDIRS:"); 5214 TAILQ_FOREACH(mxd, &xd->xd_mountdirs, xd_next) { 5215 dump_expdir(xf, mxd, 1); 5216 } 5217} 5218 5219void 5220dump_exports(void) 5221{ 5222 struct expfs *xf; 5223 struct expdir *xd; 5224 char buf[64]; 5225 5226 if (!config.verbose) 5227 return; 5228 5229 TAILQ_FOREACH(xf, &xfshead, xf_next) { 5230 DEBUG(1, "** %s %s (0x%08x)", xf->xf_fsdir, 5231 uuidstring(xf->xf_uuid, buf), UUID2FSID(xf->xf_uuid)); 5232 TAILQ_FOREACH(xd, &xf->xf_dirl, xd_next) { 5233 dump_expdir(xf, xd, 0); 5234 } 5235 } 5236} 5237 5238/* 5239 * code to monitor list of mounts 5240 */ 5241static struct statfs *sfs[2]; 5242static int size[2], cnt[2], cur, lastfscnt; 5243#define PREV ((cur + 1) & 1) 5244 5245static int 5246sfscmp(const void *arg1, const void *arg2) 5247{ 5248 const struct statfs *sfs1 = arg1; 5249 const struct statfs *sfs2 = arg2; 5250 return strcmp(sfs1->f_mntonname, sfs2->f_mntonname); 5251} 5252 5253static void 5254get_mounts(void) 5255{ 5256 cur = (cur + 1) % 2; 5257 while (size[cur] < (lastfscnt = getfsstat(sfs[cur], size[cur] * sizeof(struct statfs), MNT_NOWAIT))) { 5258 free(sfs[cur]); 5259 size[cur] = lastfscnt + 32; 5260 sfs[cur] = malloc(size[cur] * sizeof(struct statfs)); 5261 if (!sfs[cur]) 5262 err(1, "no memory"); 5263 } 5264 cnt[cur] = lastfscnt; 5265 qsort(sfs[cur], cnt[cur], sizeof(struct statfs), sfscmp); 5266} 5267 5268static int 5269check_xpaths(char *path) 5270{ 5271 struct dirlist *dirl = xpaths; 5272 5273 while (dirl) { 5274 if (subdir_check(path, dirl->dl_dir) >= 0) { 5275 DEBUG(1, "check_for_mount_changes: %s %s\n", path, dirl->dl_dir); 5276 return (1); 5277 } 5278 dirl = dirl->dl_next; 5279 } 5280 5281 return (0); 5282} 5283 5284int 5285check_for_mount_changes(void) 5286{ 5287 int i, j, cmp, gotmount = 0; 5288 5289#define RETURN_IF_DONE do { if (gotmount && (config.verbose < 3)) return (gotmount); } while (0) 5290 5291 get_mounts(); 5292 5293 if (!xpaths_complete) { 5294 DEBUG(1, "check_for_mount_changes: xpaths not complete\n"); 5295 return (1); 5296 } 5297 5298 for (i=j=0; (i < cnt[PREV]) && (j < cnt[cur]); ) { 5299 cmp = sfscmp(&sfs[PREV][i], &sfs[cur][j]); 5300 if (!cmp) { 5301 i++; 5302 j++; 5303 continue; 5304 } 5305 if (cmp < 0) { 5306 DEBUG(1, "- %s\n", sfs[PREV][i].f_mntonname); 5307 gotmount |= check_xpaths(sfs[PREV][i].f_mntonname); 5308 RETURN_IF_DONE; 5309 i++; 5310 } 5311 if (cmp > 0) { 5312 DEBUG(1, "+ %s\n", sfs[cur][j].f_mntonname); 5313 gotmount |= check_xpaths(sfs[cur][j].f_mntonname); 5314 RETURN_IF_DONE; 5315 j++; 5316 } 5317 } 5318 while (i < cnt[PREV]) { 5319 DEBUG(1, "- %s\n", sfs[PREV][i].f_mntonname); 5320 gotmount |= check_xpaths(sfs[PREV][i].f_mntonname); 5321 RETURN_IF_DONE; 5322 i++; 5323 } 5324 while (j < cnt[cur]) { 5325 DEBUG(1, "+ %s\n", sfs[cur][j].f_mntonname); 5326 gotmount |= check_xpaths(sfs[cur][j].f_mntonname); 5327 RETURN_IF_DONE; 5328 j++; 5329 } 5330 5331 return (gotmount); 5332} 5333 5334