1168404Spjd/*- 2168404Spjd * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3168404Spjd * All rights reserved. 4168404Spjd * 5168404Spjd * Redistribution and use in source and binary forms, with or without 6168404Spjd * modification, are permitted provided that the following conditions 7168404Spjd * are met: 8168404Spjd * 1. Redistributions of source code must retain the above copyright 9168404Spjd * notice, this list of conditions and the following disclaimer. 10168404Spjd * 2. Redistributions in binary form must reproduce the above copyright 11168404Spjd * notice, this list of conditions and the following disclaimer in the 12168404Spjd * documentation and/or other materials provided with the distribution. 13168404Spjd * 14168404Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15168404Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16168404Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17168404Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18168404Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19168404Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20168404Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21168404Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22168404Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23168404Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24168404Spjd * SUCH DAMAGE. 25168404Spjd */ 26168404Spjd 27168404Spjd#include <sys/cdefs.h> 28168404Spjd__FBSDID("$FreeBSD$"); 29168404Spjd 30168404Spjd#include <sys/param.h> 31219089Spjd 32219089Spjd#include <assert.h> 33219089Spjd#include <errno.h> 34168404Spjd#include <fcntl.h> 35219089Spjd#include <fsshare.h> 36168404Spjd#include <libutil.h> 37168404Spjd#include <pathnames.h> /* _PATH_MOUNTDPID */ 38219089Spjd#include <signal.h> 39219089Spjd#include <stdio.h> 40219089Spjd#include <string.h> 41219089Spjd#include <unistd.h> 42168404Spjd 43168404Spjd#define FILE_HEADER "# !!! DO NOT EDIT THIS FILE MANUALLY !!!\n\n" 44168404Spjd#define OPTSSIZE 1024 45168404Spjd#define MAXLINESIZE (PATH_MAX + OPTSSIZE) 46168404Spjd 47168404Spjdstatic void 48168404Spjdrestart_mountd(void) 49168404Spjd{ 50168404Spjd struct pidfh *pfh; 51168404Spjd pid_t mountdpid; 52168404Spjd 53168404Spjd pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &mountdpid); 54168404Spjd if (pfh != NULL) { 55168404Spjd /* Mountd is not running. */ 56168404Spjd pidfile_remove(pfh); 57168404Spjd return; 58168404Spjd } 59168404Spjd if (errno != EEXIST) { 60168404Spjd /* Cannot open pidfile for some reason. */ 61168404Spjd return; 62168404Spjd } 63168404Spjd /* We have mountd(8) PID in mountdpid varible. */ 64168404Spjd kill(mountdpid, SIGHUP); 65168404Spjd} 66168404Spjd 67168404Spjd/* 68168404Spjd * Read one line from a file. Skip comments, empty lines and a line with a 69168404Spjd * mountpoint specified in the 'skip' argument. 70168404Spjd */ 71168404Spjdstatic char * 72168404Spjdgetline(FILE *fd, const char *skip) 73168404Spjd{ 74168404Spjd static char line[MAXLINESIZE]; 75168404Spjd size_t len, skiplen; 76168404Spjd char *s, last; 77168404Spjd 78168404Spjd if (skip != NULL) 79168404Spjd skiplen = strlen(skip); 80168404Spjd for (;;) { 81168404Spjd s = fgets(line, sizeof(line), fd); 82168404Spjd if (s == NULL) 83168404Spjd return (NULL); 84168404Spjd /* Skip empty lines and comments. */ 85168404Spjd if (line[0] == '\n' || line[0] == '#') 86168404Spjd continue; 87168404Spjd len = strlen(line); 88168404Spjd if (line[len - 1] == '\n') 89168404Spjd line[len - 1] = '\0'; 90168404Spjd last = line[skiplen]; 91168404Spjd /* Skip the given mountpoint. */ 92168404Spjd if (skip != NULL && strncmp(skip, line, skiplen) == 0 && 93168404Spjd (last == '\t' || last == ' ' || last == '\0')) { 94168404Spjd continue; 95168404Spjd } 96168404Spjd break; 97168404Spjd } 98168404Spjd return (line); 99168404Spjd} 100168404Spjd 101168404Spjd/* 102168404Spjd * Function translate options to a format acceptable by exports(5), eg. 103168404Spjd * 104168929Spjd * -ro -network=192.168.0.0 -mask=255.255.255.0 -maproot=0 freefall.freebsd.org 69.147.83.54 105168404Spjd * 106168404Spjd * Accepted input formats: 107168404Spjd * 108168929Spjd * ro,network=192.168.0.0,mask=255.255.255.0,maproot=0,freefall.freebsd.org 109168929Spjd * ro network=192.168.0.0 mask=255.255.255.0 maproot=0 freefall.freebsd.org 110168929Spjd * -ro,-network=192.168.0.0,-mask=255.255.255.0,-maproot=0,freefall.freebsd.org 111168929Spjd * -ro -network=192.168.0.0 -mask=255.255.255.0 -maproot=0 freefall.freebsd.org 112168929Spjd * 113168929Spjd * Recognized keywords: 114168929Spjd * 115209757Smm * ro, maproot, mapall, mask, network, sec, alldirs, public, webnfs, index, quiet 116168929Spjd * 117168404Spjd */ 118168929Spjdstatic const char *known_opts[] = { "ro", "maproot", "mapall", "mask", 119209757Smm "network", "sec", "alldirs", "public", "webnfs", "index", "quiet", NULL }; 120168404Spjdstatic char * 121168404Spjdtranslate_opts(const char *shareopts) 122168404Spjd{ 123168404Spjd static char newopts[OPTSSIZE]; 124168929Spjd char oldopts[OPTSSIZE]; 125168404Spjd char *o, *s = NULL; 126168929Spjd unsigned int i; 127168929Spjd size_t len; 128168404Spjd 129168404Spjd strlcpy(oldopts, shareopts, sizeof(oldopts)); 130168404Spjd newopts[0] = '\0'; 131168404Spjd s = oldopts; 132168404Spjd while ((o = strsep(&s, "-, ")) != NULL) { 133168404Spjd if (o[0] == '\0') 134168404Spjd continue; 135168929Spjd for (i = 0; known_opts[i] != NULL; i++) { 136168929Spjd len = strlen(known_opts[i]); 137168929Spjd if (strncmp(known_opts[i], o, len) == 0 && 138168929Spjd (o[len] == '\0' || o[len] == '=')) { 139168929Spjd strlcat(newopts, "-", sizeof(newopts)); 140168929Spjd break; 141168929Spjd } 142168929Spjd } 143168929Spjd strlcat(newopts, o, sizeof(newopts)); 144168929Spjd strlcat(newopts, " ", sizeof(newopts)); 145168404Spjd } 146168404Spjd return (newopts); 147168404Spjd} 148168404Spjd 149168404Spjdstatic int 150168404Spjdfsshare_main(const char *file, const char *mountpoint, const char *shareopts, 151168404Spjd int share) 152168404Spjd{ 153168404Spjd char tmpfile[PATH_MAX]; 154168404Spjd char *line; 155168404Spjd FILE *newfd, *oldfd; 156168404Spjd int fd, error; 157168404Spjd 158168404Spjd newfd = oldfd = NULL; 159168404Spjd error = 0; 160168404Spjd 161168404Spjd /* 162168404Spjd * Create temporary file in the same directory, so we can atomically 163168404Spjd * rename it. 164168404Spjd */ 165168404Spjd if (strlcpy(tmpfile, file, sizeof(tmpfile)) >= sizeof(tmpfile)) 166168404Spjd return (ENAMETOOLONG); 167168404Spjd if (strlcat(tmpfile, ".XXXXXXXX", sizeof(tmpfile)) >= sizeof(tmpfile)) 168168404Spjd return (ENAMETOOLONG); 169168404Spjd fd = mkstemp(tmpfile); 170168404Spjd if (fd == -1) 171168404Spjd return (errno); 172168404Spjd /* 173168404Spjd * File name is random, so we don't really need file lock now, but it 174168404Spjd * will be needed after rename(2). 175168404Spjd */ 176168404Spjd error = flock(fd, LOCK_EX); 177168404Spjd assert(error == 0 || (error == -1 && errno == EOPNOTSUPP)); 178168404Spjd newfd = fdopen(fd, "r+"); 179168404Spjd assert(newfd != NULL); 180168404Spjd /* Open old exports file. */ 181168404Spjd oldfd = fopen(file, "r"); 182168404Spjd if (oldfd == NULL) { 183168404Spjd if (share) { 184168404Spjd if (errno != ENOENT) { 185168404Spjd error = errno; 186168404Spjd goto out; 187168404Spjd } 188168404Spjd } else { 189168404Spjd /* If there is no exports file, ignore the error. */ 190168404Spjd if (errno == ENOENT) 191168404Spjd errno = 0; 192168404Spjd error = errno; 193168404Spjd goto out; 194168404Spjd } 195168404Spjd } else { 196168404Spjd error = flock(fileno(oldfd), LOCK_EX); 197168404Spjd assert(error == 0 || (error == -1 && errno == EOPNOTSUPP)); 198168404Spjd error = 0; 199168404Spjd } 200168404Spjd 201168404Spjd /* Place big, fat warning at the begining of the file. */ 202168404Spjd fprintf(newfd, "%s", FILE_HEADER); 203168404Spjd while (oldfd != NULL && (line = getline(oldfd, mountpoint)) != NULL) 204168404Spjd fprintf(newfd, "%s\n", line); 205168404Spjd if (oldfd != NULL && ferror(oldfd) != 0) { 206168404Spjd error = ferror(oldfd); 207168404Spjd goto out; 208168404Spjd } 209168404Spjd if (ferror(newfd) != 0) { 210168404Spjd error = ferror(newfd); 211168404Spjd goto out; 212168404Spjd } 213168404Spjd if (share) { 214168404Spjd fprintf(newfd, "%s\t%s\n", mountpoint, 215168404Spjd translate_opts(shareopts)); 216168404Spjd } 217168404Spjd 218168404Spjdout: 219168404Spjd if (error != 0) 220168404Spjd unlink(tmpfile); 221168404Spjd else { 222168404Spjd if (rename(tmpfile, file) == -1) { 223168404Spjd error = errno; 224168404Spjd unlink(tmpfile); 225168404Spjd } else { 226222313Swill fflush(newfd); 227168404Spjd /* 228168404Spjd * Send SIGHUP to mountd, but unlock exports file later. 229168404Spjd */ 230168404Spjd restart_mountd(); 231168404Spjd } 232168404Spjd } 233168404Spjd if (oldfd != NULL) { 234168404Spjd flock(fileno(oldfd), LOCK_UN); 235168404Spjd fclose(oldfd); 236168404Spjd } 237168404Spjd if (newfd != NULL) { 238168404Spjd flock(fileno(newfd), LOCK_UN); 239168404Spjd fclose(newfd); 240168404Spjd } 241168404Spjd return (error); 242168404Spjd} 243168404Spjd 244168404Spjd/* 245168404Spjd * Add the given mountpoint to the given exports file. 246168404Spjd */ 247168404Spjdint 248168404Spjdfsshare(const char *file, const char *mountpoint, const char *shareopts) 249168404Spjd{ 250168404Spjd 251168404Spjd return (fsshare_main(file, mountpoint, shareopts, 1)); 252168404Spjd} 253168404Spjd 254168404Spjd/* 255168404Spjd * Remove the given mountpoint from the given exports file. 256168404Spjd */ 257168404Spjdint 258168404Spjdfsunshare(const char *file, const char *mountpoint) 259168404Spjd{ 260168404Spjd 261168404Spjd return (fsshare_main(file, mountpoint, NULL, 0)); 262168404Spjd} 263