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 <rmacklem@FreeBSD.org>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Reviewed-by: Tino Reichardt <milky-zfs@mcmilk.de>
Reviewed-by: Tony Hutter <hutter2@llnl.gov>
This commit is contained in:
rmacklem 2024-07-23 16:38:19 -07:00 committed by GitHub
parent fb6d8cf229
commit dbe07928ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 63 additions and 20 deletions

View File

@ -44,8 +44,6 @@
#include "nfs.h" #include "nfs.h"
#define _PATH_MOUNTDPID "/var/run/mountd.pid" #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_FILE "/etc/zfs/exports"
#define ZFS_EXPORTS_LOCK ZFS_EXPORTS_FILE".lock" #define ZFS_EXPORTS_LOCK ZFS_EXPORTS_FILE".lock"
@ -69,17 +67,30 @@
* index, quiet * index, quiet
*/ */
static int static int
translate_opts(const char *shareopts, FILE *out) translate_opts(char *oldopts, FILE *out)
{ {
static const char *const known_opts[] = { "ro", "maproot", "mapall", static const char *const known_opts[] = { "ro", "maproot", "mapall",
"mask", "network", "sec", "alldirs", "public", "webnfs", "index", "mask", "network", "sec", "alldirs", "public", "webnfs", "index",
"quiet" }; "quiet" };
char oldopts[OPTSSIZE], newopts[OPTSSIZE]; char *newopts, *o, *s = NULL;
char *o, *s = NULL;
unsigned int i; 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'; newopts[0] = '\0';
s = oldopts; s = oldopts;
while ((o = strsep(&s, "-, ")) != NULL) { while ((o = strsep(&s, "-, ")) != NULL) {
@ -89,14 +100,16 @@ translate_opts(const char *shareopts, FILE *out)
len = strlen(known_opts[i]); len = strlen(known_opts[i]);
if (strncmp(known_opts[i], o, len) == 0 && if (strncmp(known_opts[i], o, len) == 0 &&
(o[len] == '\0' || o[len] == '=')) { (o[len] == '\0' || o[len] == '=')) {
strlcat(newopts, "-", sizeof (newopts)); strlcat(newopts, "-", newopts_len);
break; break;
} }
} }
strlcat(newopts, o, sizeof (newopts)); strlcat(newopts, o, newopts_len);
strlcat(newopts, " ", sizeof (newopts)); strlcat(newopts, " ", newopts_len);
} }
return (fputs(newopts, out)); ret = fputs(newopts, out);
free(newopts);
return (ret);
} }
static int static int
@ -106,20 +119,38 @@ nfs_enable_share_impl(sa_share_impl_t impl_share, FILE *tmpfile)
if (strcmp(shareopts, "on") == 0) if (strcmp(shareopts, "on") == 0)
shareopts = ""; shareopts = "";
boolean_t need_free; boolean_t need_free, fnd_semi;
char *mp; char *mp, *lineopts, *exportopts, *s;
size_t whitelen;
int rc = nfs_escape_mountpoint(impl_share->sa_mountpoint, &mp, int rc = nfs_escape_mountpoint(impl_share->sa_mountpoint, &mp,
&need_free); &need_free);
if (rc != SA_OK) if (rc != SA_OK)
return (rc); return (rc);
if (fputs(mp, tmpfile) == EOF || lineopts = strdup(shareopts);
fputc('\t', tmpfile) == EOF || if (lineopts == NULL)
translate_opts(shareopts, tmpfile) == EOF || return (SA_SYSTEM_ERR);
fputc('\n', tmpfile) == EOF) { s = lineopts;
fprintf(stderr, "failed to write to temporary file\n"); fnd_semi = B_FALSE;
rc = SA_SYSTEM_ERR; 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) if (need_free)
free(mp); free(mp);

View File

@ -38,7 +38,7 @@
.\" Copyright (c) 2019, Kjeld Schouten-Lebbing .\" Copyright (c) 2019, Kjeld Schouten-Lebbing
.\" Copyright (c) 2022 Hewlett Packard Enterprise Development LP. .\" Copyright (c) 2022 Hewlett Packard Enterprise Development LP.
.\" .\"
.Dd August 8, 2023 .Dd June 29, 2024
.Dt ZFSPROPS 7 .Dt ZFSPROPS 7
.Os .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 This is done to negate the need for quoting, as well as to make parsing
with scripts easier. with scripts easier.
.Pp .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 See
.Xr exports 5 .Xr exports 5
for the meaning of the default options. for the meaning of the default options.