Path: blob/main/sys/contrib/openzfs/lib/libshare/nfs.c
48378 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/212223#include <sys/types.h>24#include <sys/stat.h>25#include <sys/file.h>26#include <fcntl.h>27#include <ctype.h>28#include <stdio.h>29#include <errno.h>30#include <libshare.h>31#include <unistd.h>32#include <libzutil.h>33#include "nfs.h"343536/*37* nfs_exports_[lock|unlock] are used to guard against conconcurrent38* updates to the exports file. Each protocol is responsible for39* providing the necessary locking to ensure consistency.40*/41static int42nfs_exports_lock(const char *name, int *nfs_lock_fd)43{44int err;4546*nfs_lock_fd = open(name, O_RDWR | O_CREAT | O_CLOEXEC, 0600);47if (*nfs_lock_fd == -1) {48err = errno;49fprintf(stderr, "failed to lock %s: %s\n", name,50zfs_strerror(err));51return (err);52}5354while ((err = flock(*nfs_lock_fd, LOCK_EX)) != 0 && errno == EINTR)55;56if (err != 0) {57err = errno;58fprintf(stderr, "failed to lock %s: %s\n", name,59zfs_strerror(err));60(void) close(*nfs_lock_fd);61*nfs_lock_fd = -1;62return (err);63}6465return (0);66}6768static void69nfs_exports_unlock(const char *name, int *nfs_lock_fd)70{71verify(*nfs_lock_fd > 0);7273if (flock(*nfs_lock_fd, LOCK_UN) != 0)74fprintf(stderr, "failed to unlock %s: %s\n",75name, zfs_strerror(errno));7677(void) close(*nfs_lock_fd);78*nfs_lock_fd = -1;79}8081struct tmpfile {82/*83* This only needs to be as wide as ZFS_EXPORTS_FILE and mktemp suffix,84* 64 is more than enough.85*/86char name[64];87FILE *fp;88};8990static boolean_t91nfs_init_tmpfile(const char *prefix, const char *mdir, struct tmpfile *tmpf)92{93if (mdir != NULL &&94mkdir(mdir, 0755) < 0 &&95errno != EEXIST) {96fprintf(stderr, "failed to create %s: %s\n",97// cppcheck-suppress uninitvar98mdir, zfs_strerror(errno));99return (B_FALSE);100}101102strlcpy(tmpf->name, prefix, sizeof (tmpf->name));103strlcat(tmpf->name, ".XXXXXXXX", sizeof (tmpf->name));104105int fd = mkostemp(tmpf->name, O_CLOEXEC);106if (fd == -1) {107fprintf(stderr, "Unable to create temporary file: %s",108zfs_strerror(errno));109return (B_FALSE);110}111112tmpf->fp = fdopen(fd, "w+");113if (tmpf->fp == NULL) {114fprintf(stderr, "Unable to reopen temporary file: %s",115zfs_strerror(errno));116close(fd);117return (B_FALSE);118}119120return (B_TRUE);121}122123static void124nfs_abort_tmpfile(struct tmpfile *tmpf)125{126unlink(tmpf->name);127fclose(tmpf->fp);128}129130static int131nfs_fini_tmpfile(const char *exports, struct tmpfile *tmpf)132{133if (fflush(tmpf->fp) != 0) {134fprintf(stderr, "Failed to write to temporary file: %s\n",135zfs_strerror(errno));136nfs_abort_tmpfile(tmpf);137return (SA_SYSTEM_ERR);138}139140if (rename(tmpf->name, exports) == -1) {141fprintf(stderr, "Unable to rename %s -> %s: %s\n",142tmpf->name, exports, zfs_strerror(errno));143nfs_abort_tmpfile(tmpf);144return (SA_SYSTEM_ERR);145}146147(void) fchmod(fileno(tmpf->fp), 0644);148fclose(tmpf->fp);149return (SA_OK);150}151152int153nfs_escape_mountpoint(const char *mp, char **out, boolean_t *need_free)154{155if (strpbrk(mp, "\t\n\v\f\r \\") == NULL) {156*out = (char *)mp;157*need_free = B_FALSE;158return (SA_OK);159} else {160size_t len = strlen(mp);161*out = malloc(len * 4 + 1);162if (!*out)163return (SA_NO_MEMORY);164*need_free = B_TRUE;165166char *oc = *out;167for (const char *c = mp; c < mp + len; ++c)168if (memchr("\t\n\v\f\r \\", *c,169strlen("\t\n\v\f\r \\"))) {170sprintf(oc, "\\%03hho", *c);171oc += 4;172} else173*oc++ = *c;174*oc = '\0';175}176177return (SA_OK);178}179180static int181nfs_process_exports(const char *exports, const char *mountpoint,182boolean_t (*cbk)(void *userdata, char *line, boolean_t found_mountpoint),183void *userdata)184{185int error = SA_OK;186boolean_t cont = B_TRUE;187188FILE *oldfp = fopen(exports, "re");189if (oldfp != NULL) {190boolean_t need_mp_free;191char *mp;192if ((error = nfs_escape_mountpoint(mountpoint,193&mp, &need_mp_free)) != SA_OK) {194(void) fclose(oldfp);195return (error);196}197198char *buf = NULL, *sep;199size_t buflen = 0, mplen = strlen(mp);200201while (cont && getline(&buf, &buflen, oldfp) != -1) {202if (buf[0] == '\n' || buf[0] == '#')203continue;204205cont = cbk(userdata, buf,206(sep = strpbrk(buf, "\t \n")) != NULL &&207sep - buf == mplen &&208strncmp(buf, mp, mplen) == 0);209}210free(buf);211if (need_mp_free)212free(mp);213214if (ferror(oldfp) != 0)215error = ferror(oldfp);216217if (fclose(oldfp) != 0) {218fprintf(stderr, "Unable to close file %s: %s\n",219exports, zfs_strerror(errno));220error = error != SA_OK ? error : SA_SYSTEM_ERR;221}222}223224return (error);225}226227static boolean_t228nfs_copy_entries_cb(void *userdata, char *line, boolean_t found_mountpoint)229{230FILE *newfp = userdata;231if (!found_mountpoint)232fputs(line, newfp);233return (B_TRUE);234}235236/*237* Copy all entries from the exports file (if it exists) to newfp,238* omitting any entries for the specified mountpoint.239*/240static int241nfs_copy_entries(FILE *newfp, const char *exports, const char *mountpoint)242{243fputs(FILE_HEADER, newfp);244245int error = nfs_process_exports(246exports, mountpoint, nfs_copy_entries_cb, newfp);247248if (error == SA_OK && ferror(newfp) != 0)249error = ferror(newfp);250251return (error);252}253254int255nfs_toggle_share(const char *lockfile, const char *exports,256const char *expdir, sa_share_impl_t impl_share,257int(*cbk)(sa_share_impl_t impl_share, FILE *tmpfile))258{259int error, nfs_lock_fd = -1;260struct tmpfile tmpf;261262if (!nfs_init_tmpfile(exports, expdir, &tmpf))263return (SA_SYSTEM_ERR);264265error = nfs_exports_lock(lockfile, &nfs_lock_fd);266if (error != 0) {267nfs_abort_tmpfile(&tmpf);268return (error);269}270271error = nfs_copy_entries(tmpf.fp, exports, impl_share->sa_mountpoint);272if (error != SA_OK)273goto fullerr;274275error = cbk(impl_share, tmpf.fp);276if (error != SA_OK)277goto fullerr;278279error = nfs_fini_tmpfile(exports, &tmpf);280nfs_exports_unlock(lockfile, &nfs_lock_fd);281return (error);282283fullerr:284nfs_abort_tmpfile(&tmpf);285nfs_exports_unlock(lockfile, &nfs_lock_fd);286return (error);287}288289void290nfs_reset_shares(const char *lockfile, const char *exports)291{292int nfs_lock_fd = -1;293294if (nfs_exports_lock(lockfile, &nfs_lock_fd) == 0) {295(void) ! truncate(exports, 0);296nfs_exports_unlock(lockfile, &nfs_lock_fd);297}298}299300static boolean_t301nfs_is_shared_cb(void *userdata, char *line, boolean_t found_mountpoint)302{303(void) line;304305boolean_t *found = userdata;306*found = found_mountpoint;307return (!found_mountpoint);308}309310boolean_t311nfs_is_shared_impl(const char *exports, sa_share_impl_t impl_share)312{313boolean_t found = B_FALSE;314nfs_process_exports(exports, impl_share->sa_mountpoint,315nfs_is_shared_cb, &found);316return (found);317}318319320