From dbe07928badfddce2879ab0e5cdb7a119ba69c0b Mon Sep 17 00:00:00 2001 From: rmacklem <64620010+rmacklem@users.noreply.github.com> Date: Tue, 23 Jul 2024 16:38:19 -0700 Subject: [PATCH] Add support for multiple lines to the sharenfs property for FreeBSD (#16338) There has been a bugzilla PR#147881 requesting this for a long time (14 years!). It extends the syntax of the ZFS shanenfs property (for FreeBSD only) to allow multiple sets of options for different hosts/nets, separated by ';'s. Signed-off-by: Rick Macklem Reviewed-by: Alexander Motin Reviewed-by: Tino Reichardt Reviewed-by: Tony Hutter --- lib/libshare/os/freebsd/nfs.c | 69 +++++++++++++++++++++++++---------- man/man7/zfsprops.7 | 14 ++++++- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/lib/libshare/os/freebsd/nfs.c b/lib/libshare/os/freebsd/nfs.c index d4cdb07a4..b5ce221bb 100644 --- a/lib/libshare/os/freebsd/nfs.c +++ b/lib/libshare/os/freebsd/nfs.c @@ -44,8 +44,6 @@ #include "nfs.h" #define _PATH_MOUNTDPID "/var/run/mountd.pid" -#define OPTSSIZE 1024 -#define MAXLINESIZE (PATH_MAX + OPTSSIZE) #define ZFS_EXPORTS_FILE "/etc/zfs/exports" #define ZFS_EXPORTS_LOCK ZFS_EXPORTS_FILE".lock" @@ -69,17 +67,30 @@ * index, quiet */ static int -translate_opts(const char *shareopts, FILE *out) +translate_opts(char *oldopts, FILE *out) { static const char *const known_opts[] = { "ro", "maproot", "mapall", "mask", "network", "sec", "alldirs", "public", "webnfs", "index", "quiet" }; - char oldopts[OPTSSIZE], newopts[OPTSSIZE]; - char *o, *s = NULL; + char *newopts, *o, *s = NULL; unsigned int i; - size_t len; + size_t len, newopts_len; + int ret; - strlcpy(oldopts, shareopts, sizeof (oldopts)); + /* + * Calculate the length needed for the worst case of a single + * character option: + * - Add one to strlen(oldopts) so that the trailing nul is counted + * as a separator. + * - Multiply by 3/2 since the single character option plus separator + * is expanded to 3 characters. + * - Add one for the trailing nul. Needed for a single repetition of + * the single character option and certain other cases. + */ + newopts_len = (strlen(oldopts) + 1) * 3 / 2 + 1; + newopts = malloc(newopts_len); + if (newopts == NULL) + return (EOF); newopts[0] = '\0'; s = oldopts; while ((o = strsep(&s, "-, ")) != NULL) { @@ -89,14 +100,16 @@ translate_opts(const char *shareopts, FILE *out) len = strlen(known_opts[i]); if (strncmp(known_opts[i], o, len) == 0 && (o[len] == '\0' || o[len] == '=')) { - strlcat(newopts, "-", sizeof (newopts)); + strlcat(newopts, "-", newopts_len); break; } } - strlcat(newopts, o, sizeof (newopts)); - strlcat(newopts, " ", sizeof (newopts)); + strlcat(newopts, o, newopts_len); + strlcat(newopts, " ", newopts_len); } - return (fputs(newopts, out)); + ret = fputs(newopts, out); + free(newopts); + return (ret); } static int @@ -106,20 +119,38 @@ nfs_enable_share_impl(sa_share_impl_t impl_share, FILE *tmpfile) if (strcmp(shareopts, "on") == 0) shareopts = ""; - boolean_t need_free; - char *mp; + boolean_t need_free, fnd_semi; + char *mp, *lineopts, *exportopts, *s; + size_t whitelen; int rc = nfs_escape_mountpoint(impl_share->sa_mountpoint, &mp, &need_free); if (rc != SA_OK) return (rc); - if (fputs(mp, tmpfile) == EOF || - fputc('\t', tmpfile) == EOF || - translate_opts(shareopts, tmpfile) == EOF || - fputc('\n', tmpfile) == EOF) { - fprintf(stderr, "failed to write to temporary file\n"); - rc = SA_SYSTEM_ERR; + lineopts = strdup(shareopts); + if (lineopts == NULL) + return (SA_SYSTEM_ERR); + s = lineopts; + fnd_semi = B_FALSE; + while ((exportopts = strsep(&s, ";")) != NULL) { + if (s != NULL) + fnd_semi = B_TRUE; + /* Ignore only whitespace between ';' separated option sets. */ + if (fnd_semi) { + whitelen = strspn(exportopts, "\t "); + if (exportopts[whitelen] == '\0') + continue; + } + if (fputs(mp, tmpfile) == EOF || + fputc('\t', tmpfile) == EOF || + translate_opts(exportopts, tmpfile) == EOF || + fputc('\n', tmpfile) == EOF) { + fprintf(stderr, "failed to write to temporary file\n"); + rc = SA_SYSTEM_ERR; + break; + } } + free(lineopts); if (need_free) free(mp); diff --git a/man/man7/zfsprops.7 b/man/man7/zfsprops.7 index 429369bd2..f7026119b 100644 --- a/man/man7/zfsprops.7 +++ b/man/man7/zfsprops.7 @@ -38,7 +38,7 @@ .\" Copyright (c) 2019, Kjeld Schouten-Lebbing .\" Copyright (c) 2022 Hewlett Packard Enterprise Development LP. .\" -.Dd August 8, 2023 +.Dd June 29, 2024 .Dt ZFSPROPS 7 .Os . @@ -1727,6 +1727,18 @@ Please note that the options are comma-separated, unlike those found in This is done to negate the need for quoting, as well as to make parsing with scripts easier. .Pp +For +.Fx , +there may be multiple sets of options separated by semicolon(s). +Each set of options must apply to different hosts or networks and each +set of options will create a separate line for +.Xr exports 5 . +Any semicolon separated option set that consists entirely of whitespace +will be ignored. +This use of semicolons is only for +.Fx +at this time. +.Pp See .Xr exports 5 for the meaning of the default options.