116038816SMartin Matuska /* 216038816SMartin Matuska * CDDL HEADER START 316038816SMartin Matuska * 416038816SMartin Matuska * The contents of this file are subject to the terms of the 516038816SMartin Matuska * Common Development and Distribution License (the "License"). 616038816SMartin Matuska * You may not use this file except in compliance with the License. 716038816SMartin Matuska * 816038816SMartin Matuska * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 916038816SMartin Matuska * or http://www.opensolaris.org/os/licensing. 1016038816SMartin Matuska * See the License for the specific language governing permissions 1116038816SMartin Matuska * and limitations under the License. 1216038816SMartin Matuska * 1316038816SMartin Matuska * When distributing Covered Code, include this CDDL HEADER in each 1416038816SMartin Matuska * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1516038816SMartin Matuska * If applicable, add the following below this CDDL HEADER, with the 1616038816SMartin Matuska * fields enclosed by brackets "[]" replaced with your own identifying 1716038816SMartin Matuska * information: Portions Copyright [yyyy] [name of copyright owner] 1816038816SMartin Matuska * 1916038816SMartin Matuska * CDDL HEADER END 2016038816SMartin Matuska */ 2116038816SMartin Matuska 2216038816SMartin Matuska 2316038816SMartin Matuska #include <sys/types.h> 2416038816SMartin Matuska #include <sys/stat.h> 2516038816SMartin Matuska #include <sys/file.h> 2616038816SMartin Matuska #include <fcntl.h> 2716038816SMartin Matuska #include <stdio.h> 2816038816SMartin Matuska #include <errno.h> 2916038816SMartin Matuska #include <libshare.h> 3016038816SMartin Matuska #include "nfs.h" 3116038816SMartin Matuska 3216038816SMartin Matuska 3316038816SMartin Matuska static int nfs_lock_fd = -1; 3416038816SMartin Matuska 3516038816SMartin Matuska 3616038816SMartin Matuska /* 3716038816SMartin Matuska * nfs_exports_[lock|unlock] are used to guard against conconcurrent 3816038816SMartin Matuska * updates to the exports file. Each protocol is responsible for 3916038816SMartin Matuska * providing the necessary locking to ensure consistency. 4016038816SMartin Matuska */ 4116038816SMartin Matuska static int 4216038816SMartin Matuska nfs_exports_lock(const char *name) 4316038816SMartin Matuska { 4416038816SMartin Matuska int err; 4516038816SMartin Matuska 4616038816SMartin Matuska nfs_lock_fd = open(name, O_RDWR | O_CREAT | O_CLOEXEC, 0600); 4716038816SMartin Matuska if (nfs_lock_fd == -1) { 4816038816SMartin Matuska err = errno; 4916038816SMartin Matuska fprintf(stderr, "failed to lock %s: %s\n", name, strerror(err)); 5016038816SMartin Matuska return (err); 5116038816SMartin Matuska } 5216038816SMartin Matuska 53*e92ffd9bSMartin Matuska while ((err = flock(nfs_lock_fd, LOCK_EX)) != 0 && errno == EINTR) 54*e92ffd9bSMartin Matuska ; 55*e92ffd9bSMartin Matuska if (err != 0) { 5616038816SMartin Matuska err = errno; 5716038816SMartin Matuska fprintf(stderr, "failed to lock %s: %s\n", name, strerror(err)); 5816038816SMartin Matuska (void) close(nfs_lock_fd); 5916038816SMartin Matuska nfs_lock_fd = -1; 6016038816SMartin Matuska return (err); 6116038816SMartin Matuska } 6216038816SMartin Matuska 6316038816SMartin Matuska return (0); 6416038816SMartin Matuska } 6516038816SMartin Matuska 6616038816SMartin Matuska static void 6716038816SMartin Matuska nfs_exports_unlock(const char *name) 6816038816SMartin Matuska { 6916038816SMartin Matuska verify(nfs_lock_fd > 0); 7016038816SMartin Matuska 7116038816SMartin Matuska if (flock(nfs_lock_fd, LOCK_UN) != 0) { 7216038816SMartin Matuska fprintf(stderr, "failed to unlock %s: %s\n", 7316038816SMartin Matuska name, strerror(errno)); 7416038816SMartin Matuska } 7516038816SMartin Matuska 7616038816SMartin Matuska (void) close(nfs_lock_fd); 7716038816SMartin Matuska nfs_lock_fd = -1; 7816038816SMartin Matuska } 7916038816SMartin Matuska 80*e92ffd9bSMartin Matuska struct tmpfile { 81*e92ffd9bSMartin Matuska /* 82*e92ffd9bSMartin Matuska * This only needs to be as wide as ZFS_EXPORTS_FILE and mktemp suffix, 83*e92ffd9bSMartin Matuska * 64 is more than enough. 84*e92ffd9bSMartin Matuska */ 85*e92ffd9bSMartin Matuska char name[64]; 86*e92ffd9bSMartin Matuska FILE *fp; 87*e92ffd9bSMartin Matuska }; 8816038816SMartin Matuska 89*e92ffd9bSMartin Matuska static boolean_t 90*e92ffd9bSMartin Matuska nfs_init_tmpfile(const char *prefix, const char *mdir, struct tmpfile *tmpf) 91*e92ffd9bSMartin Matuska { 9216038816SMartin Matuska if (mdir != NULL && 93*e92ffd9bSMartin Matuska mkdir(mdir, 0755) < 0 && 94*e92ffd9bSMartin Matuska errno != EEXIST) { 9516038816SMartin Matuska fprintf(stderr, "failed to create %s: %s\n", 9616038816SMartin Matuska mdir, strerror(errno)); 97*e92ffd9bSMartin Matuska return (B_FALSE); 9816038816SMartin Matuska } 9916038816SMartin Matuska 100*e92ffd9bSMartin Matuska strcpy(tmpf->name, prefix); 101*e92ffd9bSMartin Matuska strcat(tmpf->name, ".XXXXXXXX"); 10216038816SMartin Matuska 103*e92ffd9bSMartin Matuska int fd = mkostemp(tmpf->name, O_CLOEXEC); 10416038816SMartin Matuska if (fd == -1) { 10516038816SMartin Matuska fprintf(stderr, "Unable to create temporary file: %s", 10616038816SMartin Matuska strerror(errno)); 107*e92ffd9bSMartin Matuska return (B_FALSE); 10816038816SMartin Matuska } 109*e92ffd9bSMartin Matuska 110*e92ffd9bSMartin Matuska tmpf->fp = fdopen(fd, "w+"); 111*e92ffd9bSMartin Matuska if (tmpf->fp == NULL) { 112*e92ffd9bSMartin Matuska fprintf(stderr, "Unable to reopen temporary file: %s", 113*e92ffd9bSMartin Matuska strerror(errno)); 11416038816SMartin Matuska close(fd); 115*e92ffd9bSMartin Matuska return (B_FALSE); 116*e92ffd9bSMartin Matuska } 117*e92ffd9bSMartin Matuska 118*e92ffd9bSMartin Matuska return (B_TRUE); 119*e92ffd9bSMartin Matuska } 120*e92ffd9bSMartin Matuska 121*e92ffd9bSMartin Matuska static void 122*e92ffd9bSMartin Matuska nfs_abort_tmpfile(struct tmpfile *tmpf) 123*e92ffd9bSMartin Matuska { 124*e92ffd9bSMartin Matuska unlink(tmpf->name); 125*e92ffd9bSMartin Matuska fclose(tmpf->fp); 12616038816SMartin Matuska } 12716038816SMartin Matuska 12816038816SMartin Matuska static int 129*e92ffd9bSMartin Matuska nfs_fini_tmpfile(const char *exports, struct tmpfile *tmpf) 13016038816SMartin Matuska { 131*e92ffd9bSMartin Matuska if (fflush(tmpf->fp) != 0) { 132*e92ffd9bSMartin Matuska fprintf(stderr, "Failed to write to temporary file: %s\n", 13316038816SMartin Matuska strerror(errno)); 134*e92ffd9bSMartin Matuska nfs_abort_tmpfile(tmpf); 13516038816SMartin Matuska return (SA_SYSTEM_ERR); 13616038816SMartin Matuska } 137*e92ffd9bSMartin Matuska 138*e92ffd9bSMartin Matuska if (rename(tmpf->name, exports) == -1) { 139*e92ffd9bSMartin Matuska fprintf(stderr, "Unable to rename %s -> %s: %s\n", 140*e92ffd9bSMartin Matuska tmpf->name, exports, strerror(errno)); 141*e92ffd9bSMartin Matuska nfs_abort_tmpfile(tmpf); 142*e92ffd9bSMartin Matuska return (SA_SYSTEM_ERR); 143*e92ffd9bSMartin Matuska } 144*e92ffd9bSMartin Matuska 145*e92ffd9bSMartin Matuska (void) fchmod(fileno(tmpf->fp), 0644); 146*e92ffd9bSMartin Matuska fclose(tmpf->fp); 14716038816SMartin Matuska return (SA_OK); 14816038816SMartin Matuska } 14916038816SMartin Matuska 150*e92ffd9bSMartin Matuska static int 151*e92ffd9bSMartin Matuska nfs_process_exports(const char *exports, const char *mountpoint, 152*e92ffd9bSMartin Matuska boolean_t (*cbk)(void *userdata, char *line, boolean_t found_mountpoint), 153*e92ffd9bSMartin Matuska void *userdata) 154*e92ffd9bSMartin Matuska { 155*e92ffd9bSMartin Matuska int error = SA_OK; 156*e92ffd9bSMartin Matuska boolean_t cont = B_TRUE; 157*e92ffd9bSMartin Matuska 158*e92ffd9bSMartin Matuska FILE *oldfp = fopen(exports, "re"); 159*e92ffd9bSMartin Matuska if (oldfp != NULL) { 160*e92ffd9bSMartin Matuska char *buf = NULL, *sep; 161*e92ffd9bSMartin Matuska size_t buflen = 0, mplen = strlen(mountpoint); 162*e92ffd9bSMartin Matuska 163*e92ffd9bSMartin Matuska while (cont && getline(&buf, &buflen, oldfp) != -1) { 164*e92ffd9bSMartin Matuska if (buf[0] == '\n' || buf[0] == '#') 165*e92ffd9bSMartin Matuska continue; 166*e92ffd9bSMartin Matuska 167*e92ffd9bSMartin Matuska cont = cbk(userdata, buf, 168*e92ffd9bSMartin Matuska (sep = strpbrk(buf, "\t \n")) != NULL && 169*e92ffd9bSMartin Matuska sep - buf == mplen && 170*e92ffd9bSMartin Matuska strncmp(buf, mountpoint, mplen) == 0); 171*e92ffd9bSMartin Matuska } 172*e92ffd9bSMartin Matuska free(buf); 173*e92ffd9bSMartin Matuska 174*e92ffd9bSMartin Matuska if (ferror(oldfp) != 0) 175*e92ffd9bSMartin Matuska error = ferror(oldfp); 176*e92ffd9bSMartin Matuska 177*e92ffd9bSMartin Matuska if (fclose(oldfp) != 0) { 178*e92ffd9bSMartin Matuska fprintf(stderr, "Unable to close file %s: %s\n", 179*e92ffd9bSMartin Matuska exports, strerror(errno)); 180*e92ffd9bSMartin Matuska error = error != SA_OK ? error : SA_SYSTEM_ERR; 181*e92ffd9bSMartin Matuska } 182*e92ffd9bSMartin Matuska } 183*e92ffd9bSMartin Matuska 184*e92ffd9bSMartin Matuska return (error); 185*e92ffd9bSMartin Matuska } 186*e92ffd9bSMartin Matuska 187*e92ffd9bSMartin Matuska static boolean_t 188*e92ffd9bSMartin Matuska nfs_copy_entries_cb(void *userdata, char *line, boolean_t found_mountpoint) 189*e92ffd9bSMartin Matuska { 190*e92ffd9bSMartin Matuska FILE *newfp = userdata; 191*e92ffd9bSMartin Matuska if (!found_mountpoint) 192*e92ffd9bSMartin Matuska fputs(line, newfp); 193*e92ffd9bSMartin Matuska return (B_TRUE); 194*e92ffd9bSMartin Matuska } 195*e92ffd9bSMartin Matuska 196*e92ffd9bSMartin Matuska /* 197*e92ffd9bSMartin Matuska * Copy all entries from the exports file (if it exists) to newfp, 198*e92ffd9bSMartin Matuska * omitting any entries for the specified mountpoint. 199*e92ffd9bSMartin Matuska */ 200*e92ffd9bSMartin Matuska static int 201*e92ffd9bSMartin Matuska nfs_copy_entries(FILE *newfp, const char *exports, const char *mountpoint) 202*e92ffd9bSMartin Matuska { 203*e92ffd9bSMartin Matuska fputs(FILE_HEADER, newfp); 204*e92ffd9bSMartin Matuska 205*e92ffd9bSMartin Matuska int error = nfs_process_exports( 206*e92ffd9bSMartin Matuska exports, mountpoint, nfs_copy_entries_cb, newfp); 207*e92ffd9bSMartin Matuska 208*e92ffd9bSMartin Matuska if (error == SA_OK && ferror(newfp) != 0) 209*e92ffd9bSMartin Matuska error = ferror(newfp); 210*e92ffd9bSMartin Matuska 211*e92ffd9bSMartin Matuska return (error); 212*e92ffd9bSMartin Matuska } 213*e92ffd9bSMartin Matuska 2143ff01b23SMartin Matuska int 21516038816SMartin Matuska nfs_toggle_share(const char *lockfile, const char *exports, 21616038816SMartin Matuska const char *expdir, sa_share_impl_t impl_share, 217*e92ffd9bSMartin Matuska int(*cbk)(sa_share_impl_t impl_share, FILE *tmpfile)) 21816038816SMartin Matuska { 21916038816SMartin Matuska int error; 220*e92ffd9bSMartin Matuska struct tmpfile tmpf; 22116038816SMartin Matuska 222*e92ffd9bSMartin Matuska if (!nfs_init_tmpfile(exports, expdir, &tmpf)) 22316038816SMartin Matuska return (SA_SYSTEM_ERR); 22416038816SMartin Matuska 22516038816SMartin Matuska error = nfs_exports_lock(lockfile); 22616038816SMartin Matuska if (error != 0) { 227*e92ffd9bSMartin Matuska nfs_abort_tmpfile(&tmpf); 22816038816SMartin Matuska return (error); 22916038816SMartin Matuska } 23016038816SMartin Matuska 231*e92ffd9bSMartin Matuska error = nfs_copy_entries(tmpf.fp, exports, impl_share->sa_mountpoint); 23216038816SMartin Matuska if (error != SA_OK) 23316038816SMartin Matuska goto fullerr; 23416038816SMartin Matuska 235*e92ffd9bSMartin Matuska error = cbk(impl_share, tmpf.fp); 23616038816SMartin Matuska if (error != SA_OK) 23716038816SMartin Matuska goto fullerr; 23816038816SMartin Matuska 239*e92ffd9bSMartin Matuska error = nfs_fini_tmpfile(exports, &tmpf); 24016038816SMartin Matuska nfs_exports_unlock(lockfile); 24116038816SMartin Matuska return (error); 24216038816SMartin Matuska 24316038816SMartin Matuska fullerr: 244*e92ffd9bSMartin Matuska nfs_abort_tmpfile(&tmpf); 24516038816SMartin Matuska nfs_exports_unlock(lockfile); 24616038816SMartin Matuska return (error); 24716038816SMartin Matuska } 248*e92ffd9bSMartin Matuska 249*e92ffd9bSMartin Matuska static boolean_t 250*e92ffd9bSMartin Matuska nfs_is_shared_cb(void *userdata, char *line, boolean_t found_mountpoint) 251*e92ffd9bSMartin Matuska { 252*e92ffd9bSMartin Matuska (void) line; 253*e92ffd9bSMartin Matuska 254*e92ffd9bSMartin Matuska boolean_t *found = userdata; 255*e92ffd9bSMartin Matuska *found = found_mountpoint; 256*e92ffd9bSMartin Matuska return (!found_mountpoint); 257*e92ffd9bSMartin Matuska } 258*e92ffd9bSMartin Matuska 259*e92ffd9bSMartin Matuska boolean_t 260*e92ffd9bSMartin Matuska nfs_is_shared_impl(const char *exports, sa_share_impl_t impl_share) 261*e92ffd9bSMartin Matuska { 262*e92ffd9bSMartin Matuska boolean_t found = B_FALSE; 263*e92ffd9bSMartin Matuska nfs_process_exports(exports, impl_share->sa_mountpoint, 264*e92ffd9bSMartin Matuska nfs_is_shared_cb, &found); 265*e92ffd9bSMartin Matuska return (found); 266*e92ffd9bSMartin Matuska } 267