Path: blob/main/sys/contrib/openzfs/lib/libzfs/libzfs_share_nfs.c
178696 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 <unistd.h>31#include <libzutil.h>32#include "libzfs_impl.h"333435/*36* nfs_exports_[lock|unlock] are used to guard against conconcurrent37* updates to the exports file. Each protocol is responsible for38* providing the necessary locking to ensure consistency.39*/40static int41nfs_exports_lock(const char *name, int *nfs_lock_fd)42{43int err;4445*nfs_lock_fd = open(name, O_RDWR | O_CREAT | O_CLOEXEC, 0600);46if (*nfs_lock_fd == -1) {47err = errno;48fprintf(stderr, "failed to lock %s: %s\n", name,49zfs_strerror(err));50return (err);51}5253while ((err = flock(*nfs_lock_fd, LOCK_EX)) != 0 && errno == EINTR)54;55if (err != 0) {56err = errno;57fprintf(stderr, "failed to lock %s: %s\n", name,58zfs_strerror(err));59(void) close(*nfs_lock_fd);60*nfs_lock_fd = -1;61return (err);62}6364return (0);65}6667static void68nfs_exports_unlock(const char *name, int *nfs_lock_fd)69{70verify(*nfs_lock_fd > 0);7172if (flock(*nfs_lock_fd, LOCK_UN) != 0)73fprintf(stderr, "failed to unlock %s: %s\n",74name, zfs_strerror(errno));7576(void) close(*nfs_lock_fd);77*nfs_lock_fd = -1;78}7980struct tmpfile {81/*82* This only needs to be as wide as ZFS_EXPORTS_FILE and mktemp suffix,83* 64 is more than enough.84*/85char name[64];86FILE *fp;87};8889static boolean_t90nfs_init_tmpfile(const char *prefix, const char *mdir, struct tmpfile *tmpf)91{92if (mdir != NULL &&93mkdir(mdir, 0755) < 0 &&94errno != EEXIST) {95fprintf(stderr, "failed to create %s: %s\n",96// cppcheck-suppress uninitvar97mdir, zfs_strerror(errno));98return (B_FALSE);99}100101strlcpy(tmpf->name, prefix, sizeof (tmpf->name));102strlcat(tmpf->name, ".XXXXXXXX", sizeof (tmpf->name));103104int fd = mkostemp(tmpf->name, O_CLOEXEC);105if (fd == -1) {106fprintf(stderr, "Unable to create temporary file: %s",107zfs_strerror(errno));108return (B_FALSE);109}110111tmpf->fp = fdopen(fd, "w+");112if (tmpf->fp == NULL) {113fprintf(stderr, "Unable to reopen temporary file: %s",114zfs_strerror(errno));115close(fd);116return (B_FALSE);117}118119return (B_TRUE);120}121122static void123nfs_abort_tmpfile(struct tmpfile *tmpf)124{125unlink(tmpf->name);126fclose(tmpf->fp);127}128129static int130nfs_fini_tmpfile(const char *exports, struct tmpfile *tmpf)131{132if (fflush(tmpf->fp) != 0) {133fprintf(stderr, "Failed to write to temporary file: %s\n",134zfs_strerror(errno));135nfs_abort_tmpfile(tmpf);136return (SA_SYSTEM_ERR);137}138139if (rename(tmpf->name, exports) == -1) {140fprintf(stderr, "Unable to rename %s -> %s: %s\n",141tmpf->name, exports, zfs_strerror(errno));142nfs_abort_tmpfile(tmpf);143return (SA_SYSTEM_ERR);144}145146(void) fchmod(fileno(tmpf->fp), 0644);147fclose(tmpf->fp);148return (SA_OK);149}150151int152nfs_escape_mountpoint(const char *mp, char **out, boolean_t *need_free)153{154if (strpbrk(mp, "\t\n\v\f\r \\") == NULL) {155*out = (char *)mp;156*need_free = B_FALSE;157return (SA_OK);158} else {159size_t len = strlen(mp);160*out = malloc(len * 4 + 1);161if (!*out)162return (SA_NO_MEMORY);163*need_free = B_TRUE;164165char *oc = *out;166for (const char *c = mp; c < mp + len; ++c)167if (memchr("\t\n\v\f\r \\", *c,168strlen("\t\n\v\f\r \\"))) {169sprintf(oc, "\\%03hho", *c);170oc += 4;171} else172*oc++ = *c;173*oc = '\0';174}175176return (SA_OK);177}178179static int180nfs_process_exports(const char *exports, const char *mountpoint,181boolean_t (*cbk)(void *userdata, char *line, boolean_t found_mountpoint),182void *userdata)183{184int error = SA_OK;185boolean_t cont = B_TRUE;186187FILE *oldfp = fopen(exports, "re");188if (oldfp != NULL) {189boolean_t need_mp_free;190char *mp;191if ((error = nfs_escape_mountpoint(mountpoint,192&mp, &need_mp_free)) != SA_OK) {193(void) fclose(oldfp);194return (error);195}196197char *buf = NULL, *sep;198size_t buflen = 0, mplen = strlen(mp);199200while (cont && getline(&buf, &buflen, oldfp) != -1) {201if (buf[0] == '\n' || buf[0] == '#')202continue;203204cont = cbk(userdata, buf,205(sep = strpbrk(buf, "\t \n")) != NULL &&206sep - buf == mplen &&207strncmp(buf, mp, mplen) == 0);208}209free(buf);210if (need_mp_free)211free(mp);212213if (ferror(oldfp) != 0)214error = ferror(oldfp);215216if (fclose(oldfp) != 0) {217fprintf(stderr, "Unable to close file %s: %s\n",218exports, zfs_strerror(errno));219error = error != SA_OK ? error : SA_SYSTEM_ERR;220}221}222223return (error);224}225226static boolean_t227nfs_copy_entries_cb(void *userdata, char *line, boolean_t found_mountpoint)228{229FILE *newfp = userdata;230if (!found_mountpoint)231fputs(line, newfp);232return (B_TRUE);233}234235/*236* Copy all entries from the exports file (if it exists) to newfp,237* omitting any entries for the specified mountpoint.238*/239static int240nfs_copy_entries(FILE *newfp, const char *exports, const char *mountpoint)241{242fputs(NFS_FILE_HEADER, newfp);243244int error = nfs_process_exports(245exports, mountpoint, nfs_copy_entries_cb, newfp);246247if (error == SA_OK && ferror(newfp) != 0)248error = ferror(newfp);249250return (error);251}252253int254nfs_toggle_share(const char *lockfile, const char *exports,255const char *expdir, sa_share_impl_t impl_share,256int(*cbk)(sa_share_impl_t impl_share, FILE *tmpfile))257{258int error, nfs_lock_fd = -1;259struct tmpfile tmpf;260261if (!nfs_init_tmpfile(exports, expdir, &tmpf))262return (SA_SYSTEM_ERR);263264error = nfs_exports_lock(lockfile, &nfs_lock_fd);265if (error != 0) {266nfs_abort_tmpfile(&tmpf);267return (error);268}269270error = nfs_copy_entries(tmpf.fp, exports, impl_share->sa_mountpoint);271if (error != SA_OK)272goto fullerr;273274error = cbk(impl_share, tmpf.fp);275if (error != SA_OK)276goto fullerr;277278error = nfs_fini_tmpfile(exports, &tmpf);279nfs_exports_unlock(lockfile, &nfs_lock_fd);280return (error);281282fullerr:283nfs_abort_tmpfile(&tmpf);284nfs_exports_unlock(lockfile, &nfs_lock_fd);285return (error);286}287288void289nfs_reset_shares(const char *lockfile, const char *exports)290{291int nfs_lock_fd = -1;292293if (nfs_exports_lock(lockfile, &nfs_lock_fd) == 0) {294(void) ! truncate(exports, 0);295nfs_exports_unlock(lockfile, &nfs_lock_fd);296}297}298299static boolean_t300nfs_is_shared_cb(void *userdata, char *line, boolean_t found_mountpoint)301{302(void) line;303304boolean_t *found = userdata;305*found = found_mountpoint;306return (!found_mountpoint);307}308309boolean_t310nfs_is_shared_impl(const char *exports, sa_share_impl_t impl_share)311{312boolean_t found = B_FALSE;313nfs_process_exports(exports, impl_share->sa_mountpoint,314nfs_is_shared_cb, &found);315return (found);316}317318319