diff --git a/nfs-utils-2.3.1-systemd-gssproxy-restart.patch b/nfs-utils-2.3.1-systemd-gssproxy-restart.patch index 60762e8..08b8dd1 100644 --- a/nfs-utils-2.3.1-systemd-gssproxy-restart.patch +++ b/nfs-utils-2.3.1-systemd-gssproxy-restart.patch @@ -1,9 +1,9 @@ -diff -up nfs-utils-2.3.1/systemd/nfs-server.service.orig nfs-utils-2.3.1/systemd/nfs-server.service ---- nfs-utils-2.3.1/systemd/nfs-server.service.orig 2018-01-19 10:25:38.153513857 -0500 -+++ nfs-utils-2.3.1/systemd/nfs-server.service 2018-01-19 10:30:52.977245126 -0500 -@@ -26,6 +26,7 @@ Type=oneshot +diff -up nfs-utils-2.5.2/systemd/nfs-server.service.orig nfs-utils-2.5.2/systemd/nfs-server.service +--- nfs-utils-2.5.2/systemd/nfs-server.service.orig 2020-12-16 12:31:27.677558163 -0500 ++++ nfs-utils-2.5.2/systemd/nfs-server.service 2020-12-16 12:33:56.751806659 -0500 +@@ -23,6 +23,7 @@ Type=oneshot RemainAfterExit=yes - ExecStartPre=/usr/sbin/exportfs -r + ExecStartPre=-/usr/sbin/exportfs -r ExecStart=/usr/sbin/rpc.nfsd +ExecStart=-/bin/sh -c 'if systemctl -q is-active gssproxy; then systemctl reload gssproxy ; fi' ExecStop=/usr/sbin/rpc.nfsd 0 diff --git a/nfs-utils-2.5.3-rc3.patch b/nfs-utils-2.5.3-rc3.patch new file mode 100644 index 0000000..6413e07 --- /dev/null +++ b/nfs-utils-2.5.3-rc3.patch @@ -0,0 +1,1054 @@ +diff --git a/support/misc/nfsd_path.c b/support/misc/nfsd_path.c +index 8efbfcd..65e53c1 100644 +--- a/support/misc/nfsd_path.c ++++ b/support/misc/nfsd_path.c +@@ -110,7 +110,7 @@ nfsd_setup_workqueue(void) + + if (!rootdir) + return; +-printf("rootdir %s\n", rootdir); ++ + nfsd_wq = xthread_workqueue_alloc(); + if (!nfsd_wq) + return; +diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c +index 3d13610..a4ea067 100644 +--- a/support/nfs/conffile.c ++++ b/support/nfs/conffile.c +@@ -52,10 +52,14 @@ + #include + #include + #include ++#include + + #include "conffile.h" + #include "xlog.h" + ++#define CONF_FILE_EXT ".conf" ++#define CONF_FILE_EXT_LEN ((int) (sizeof(CONF_FILE_EXT) - 1)) ++ + #pragma GCC visibility push(hidden) + + static void conf_load_defaults(void); +@@ -456,7 +460,7 @@ conf_parse_line(int trans, char *line, const char *filename, int lineno, char ** + free(subconf); + } else { + /* XXX Perhaps should we not ignore errors? */ +- conf_set(trans, *section, *subsection, line, val, 0, 0); ++ conf_set(trans, *section, *subsection, line, val, 1, 0); + } + } + +@@ -577,6 +581,30 @@ static void conf_free_bindings(void) + } + } + ++static int ++conf_load_files(int trans, const char *conf_file) ++{ ++ char *conf_data; ++ char *section = NULL; ++ char *subsection = NULL; ++ ++ conf_data = conf_readfile(conf_file); ++ if (conf_data == NULL) ++ return 1; ++ ++ /* Load default configuration values. */ ++ conf_load_defaults(); ++ ++ /* Parse config contents into the transaction queue */ ++ conf_parse(trans, conf_data, §ion, &subsection, conf_file); ++ if (section) ++ free(section); ++ if (subsection) ++ free(subsection); ++ free(conf_data); ++ ++ return 0; ++} + /* Open the config file and map it into our address space, then parse it. */ + static int + conf_load_file(const char *conf_file) +@@ -609,18 +637,129 @@ conf_load_file(const char *conf_file) + return 0; + } + ++static void ++conf_init_dir(const char *conf_file) ++{ ++ struct dirent **namelist = NULL; ++ char *dname, fname[PATH_MAX], *cname; ++ int n = 0, nfiles = 0, i, fname_len, dname_len; ++ int trans, rv, path_len; ++ ++ dname = malloc(strlen(conf_file) + 3); ++ if (dname == NULL) { ++ xlog(L_WARNING, "conf_init_dir: malloc: %s", strerror(errno)); ++ return; ++ } ++ sprintf(dname, "%s.d", conf_file); ++ ++ n = scandir(dname, &namelist, NULL, versionsort); ++ if (n < 0) { ++ if (errno != ENOENT) { ++ xlog(L_WARNING, "conf_init_dir: scandir %s: %s", ++ dname, strerror(errno)); ++ } ++ free(dname); ++ return; ++ } else if (n == 0) { ++ free(dname); ++ return; ++ } ++ ++ trans = conf_begin(); ++ dname_len = strlen(dname); ++ for (i = 0; i < n; i++ ) { ++ struct dirent *d = namelist[i]; ++ ++ switch (d->d_type) { ++ case DT_UNKNOWN: ++ case DT_REG: ++ case DT_LNK: ++ break; ++ default: ++ continue; ++ } ++ if (*d->d_name == '.') ++ continue; ++ ++ fname_len = strlen(d->d_name); ++ path_len = (fname_len + dname_len); ++ if (!fname_len || path_len > PATH_MAX) { ++ xlog(L_WARNING, "conf_init_dir: Too long file name: %s in %s", ++ d->d_name, dname); ++ continue; ++ } ++ ++ /* ++ * Check the naming of the file. Only process files ++ * that end with CONF_FILE_EXT ++ */ ++ if (fname_len <= CONF_FILE_EXT_LEN) { ++ xlog(D_GENERAL, "conf_init_dir: %s: name too short", ++ d->d_name); ++ continue; ++ } ++ cname = (d->d_name + (fname_len - CONF_FILE_EXT_LEN)); ++ if (strcmp(cname, CONF_FILE_EXT) != 0) { ++ xlog(D_GENERAL, "conf_init_dir: %s: invalid file extension", ++ d->d_name); ++ continue; ++ } ++ ++ rv = snprintf(fname, PATH_MAX, "%s/%s", dname, d->d_name); ++ if (rv < path_len) { ++ xlog(L_WARNING, "conf_init_dir: file name: %s/%s too short", ++ d->d_name, dname); ++ continue; ++ } ++ ++ if (conf_load_files(trans, fname)) ++ continue; ++ nfiles++; ++ } ++ ++ if (nfiles) { ++ /* Apply the configuration values */ ++ conf_end(trans, 1); ++ } ++ for (i = 0; i < n; i++) ++ free(namelist[i]); ++ free(namelist); ++ free(dname); ++ ++ return; ++} ++ + int + conf_init_file(const char *conf_file) + { + unsigned int i; ++ int ret; + + for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) + LIST_INIT (&conf_bindings[i]); + + TAILQ_INIT (&conf_trans_queue); + +- if (conf_file == NULL) conf_file=NFS_CONFFILE; +- return conf_load_file(conf_file); ++ if (conf_file == NULL) ++ conf_file=NFS_CONFFILE; ++ ++ /* ++ * First parse the give config file ++ * then parse the config.conf.d directory ++ * (if it exists) ++ * ++ */ ++ ret = conf_load_file(conf_file); ++ ++ /* ++ * When the same variable is set in both files ++ * the conf.d file will override the config file. ++ * This allows automated admin systems to ++ * have the final say. ++ */ ++ conf_init_dir(conf_file); ++ ++ return ret; + } + + /* +diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service +index 06c1adb..b432f91 100644 +--- a/systemd/nfs-server.service ++++ b/systemd/nfs-server.service +@@ -21,13 +21,13 @@ After=rpc-gssd.service gssproxy.service rpc-svcgssd.service + [Service] + Type=oneshot + RemainAfterExit=yes +-ExecStartPre=/usr/sbin/exportfs -r ++ExecStartPre=-/usr/sbin/exportfs -r + ExecStart=/usr/sbin/rpc.nfsd + ExecStop=/usr/sbin/rpc.nfsd 0 + ExecStopPost=/usr/sbin/exportfs -au + ExecStopPost=/usr/sbin/exportfs -f + +-ExecReload=/usr/sbin/exportfs -r ++ExecReload=-/usr/sbin/exportfs -r + + [Install] + WantedBy=multi-user.target +diff --git a/systemd/nfs-v4client.target b/systemd/nfs-v4client.target +new file mode 100644 +index 0000000..3d1064e +--- /dev/null ++++ b/systemd/nfs-v4client.target +@@ -0,0 +1,12 @@ ++[Unit] ++Description=NFS client services ++Before=remote-fs-pre.target ++Wants=remote-fs-pre.target ++ ++# GSS services dependencies and ordering ++Wants=auth-rpcgss-module.service ++After=rpc-gssd.service rpc-svcgssd.service gssproxy.service ++ ++[Install] ++WantedBy=multi-user.target ++WantedBy=remote-fs.target +diff --git a/systemd/nfs.conf.man b/systemd/nfs.conf.man +index 3f1c726..16e0ec4 100644 +--- a/systemd/nfs.conf.man ++++ b/systemd/nfs.conf.man +@@ -265,7 +265,15 @@ Only + is recognized. + + .SH FILES ++.TP 10n + .I /etc/nfs.conf ++Default NFS client configuration file ++.TP 10n ++.I /etc/nfs.conf.d ++When this directory exists and files ending ++with ".conf" exist, those files will be ++used to set configuration variables. These ++files will override variables set in /etc/nfs.conf + .SH SEE ALSO + .BR nfsdcltrack (8), + .BR rpc.nfsd (8), +diff --git a/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py +index 25e92a1..23876fc 100755 +--- a/tools/mountstats/mountstats.py ++++ b/tools/mountstats/mountstats.py +@@ -378,7 +378,10 @@ class DeviceData: + print('\t%12s: %s' % (op, " ".join(str(x) for x in self.__rpc_data[op]))) + elif vers == '4': + for op in Nfsv4ops: +- print('\t%12s: %s' % (op, " ".join(str(x) for x in self.__rpc_data[op]))) ++ try: ++ print('\t%12s: %s' % (op, " ".join(str(x) for x in self.__rpc_data[op]))) ++ except KeyError: ++ continue + else: + print('\tnot implemented for version %d' % vers) + print() +diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c +index 9d5e575..9fcae0b 100644 +--- a/utils/exportfs/exportfs.c ++++ b/utils/exportfs/exportfs.c +@@ -176,10 +176,10 @@ main(int argc, char **argv) + xlog(L_ERROR, "-r and -u are incompatible"); + return 1; + } +-printf("point 1\n"); ++ + if (!setup_state_path_names(progname, ETAB, ETABTMP, ETABLCK, &etab)) + return 1; +-printf("point 2\n"); ++ + if (optind == argc && ! f_all) { + if (force_flush) { + cache_flush(1); +@@ -193,7 +193,6 @@ printf("point 2\n"); + return 0; + } + } +-printf("point 3\n"); + + /* + * Serialize things as best we can +diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man +index 1d17184..54b3f87 100644 +--- a/utils/exportfs/exports.man ++++ b/utils/exportfs/exports.man +@@ -169,13 +169,6 @@ default. In all releases after 1.0.0, + is the default, and + .I async + must be explicitly requested if needed. +-To help make system administrators aware of this change, +-.B exportfs +-will issue a warning if neither +-.I sync +-nor +-.I async +-is specified. + .TP + .IR no_wdelay + This option has no effect if +diff --git a/utils/mount/configfile.c b/utils/mount/configfile.c +index 93fe500..7934f4f 100644 +--- a/utils/mount/configfile.c ++++ b/utils/mount/configfile.c +@@ -1,5 +1,5 @@ + /* +- * configfile.c -- mount configuration file manipulation ++ * configfile.c -- mount configuration file manipulation + * Copyright (C) 2008 Red Hat, Inc + * + * - Routines use to create mount options from the mount +@@ -34,10 +34,7 @@ + #include "parse_opt.h" + #include "network.h" + #include "conffile.h" +- +-char *mountopts_convert(char *value); +-char *is_alias(char *opt); +-char *conf_get_mntopts(char *spec, char *mount_point, char *mount_opts); ++#include "mount_config.h" + + #define KBYTES(x) ((x) * (1024)) + #define MEGABYTES(x) ((x) * (1048576)) +@@ -70,17 +67,31 @@ struct mnt_alias { + {"background", "bg", MNT_NOARG}, + {"foreground", "fg", MNT_NOARG}, + {"sloppy", "sloppy", MNT_NOARG}, +- {"nfsvers", "vers", MNT_UNSET}, + }; + int mnt_alias_sz = (sizeof(mnt_alias_tab)/sizeof(mnt_alias_tab[0])); + ++static const char *version_keys[] = { ++ "v2", "v3", "v4", "vers", "nfsvers", "minorversion", NULL ++}; ++ + static int strict; + ++static int is_version(const char *field) ++{ ++ int i; ++ for (i = 0; version_keys[i] ; i++) ++ if (strcmp(version_keys[i], field) == 0) ++ return 1; ++ if (strncmp(field, "v4.", 3) == 0) ++ return 1; ++ return 0; ++} ++ + /* +- * See if the option is an alias, if so return the ++ * See if the option is an alias, if so return the + * real mount option along with the argument type. + */ +-inline static ++inline static + char *mountopts_alias(char *opt, int *argtype) + { + int i; +@@ -99,10 +110,10 @@ char *mountopts_alias(char *opt, int *argtype) + } + /* + * Convert numeric strings that end with 'k', 'm' or 'g' +- * into numeric strings with the real value. ++ * into numeric strings with the real value. + * Meaning '8k' becomes '8094'. + */ +-char *mountopts_convert(char *value) ++static char *mountopts_convert(char *value) + { + unsigned long long factor, num; + static char buf[64]; +@@ -136,110 +147,16 @@ char *mountopts_convert(char *value) + return buf; + } + +-struct entry { +- SLIST_ENTRY(entry) entries; +- char *opt; +-}; +-static SLIST_HEAD(shead, entry) head = SLIST_HEAD_INITIALIZER(head); +-static int list_size; +- +-/* +- * Add option to the link list +- */ +-inline static void +-add_entry(char *opt) +-{ +- struct entry *entry; +- +- entry = calloc(1, sizeof(struct entry)); +- if (entry == NULL) { +- xlog_warn("Unable calloc memory for mount configs"); +- return; +- } +- entry->opt = strdup(opt); +- if (entry->opt == NULL) { +- xlog_warn("Unable calloc memory for mount opts"); +- free(entry); +- return; +- } +- SLIST_INSERT_HEAD(&head, entry, entries); +-} +-/* +- * Check the alias list to see if the given +- * opt is a alias +- */ +-char *is_alias(char *opt) +-{ +- int i; +- +- for (i=0; i < mnt_alias_sz; i++) { +- if (strcasecmp(opt, mnt_alias_tab[i].alias) == 0) +- return mnt_alias_tab[i].opt; +- } +- return NULL; +-} +-/* +- * See if the given entry exists if the link list, +- * if so return that entry +- */ +-inline static +-char *lookup_entry(char *opt) +-{ +- struct entry *entry; +- char *alias = is_alias(opt); +- char *ptr; +- +- SLIST_FOREACH(entry, &head, entries) { +- /* +- * Only check the left side or options that use '=' +- */ +- if ((ptr = strchr(entry->opt, '=')) != 0) { +- int len = (int) (ptr - entry->opt); +- +- if (strncasecmp(entry->opt, opt, len) == 0) +- return opt; +- } +- if (strcasecmp(entry->opt, opt) == 0) +- return opt; +- if (alias && strcasecmp(entry->opt, alias) == 0) +- return opt; +- if (alias && strcasecmp(alias, "fg") == 0) { +- if (strcasecmp(entry->opt, "bg") == 0) +- return opt; +- } +- if (alias && strcasecmp(alias, "bg") == 0) { +- if (strcasecmp(entry->opt, "fg") == 0) +- return opt; +- } +- } +- return NULL; +-} +-/* +- * Free all entries on the link list +- */ +-inline static +-void free_all(void) +-{ +- struct entry *entry; +- +- while (!SLIST_EMPTY(&head)) { +- entry = SLIST_FIRST(&head); +- SLIST_REMOVE_HEAD(&head, entries); +- free(entry->opt); +- free(entry); +- } +-} +- + struct nfs_version config_default_vers; + unsigned long config_default_proto; + extern sa_family_t config_default_family; + + /* + * Check to see if a default value is being set. +- * If so, set the appropriate global value which will ++ * If so, set the appropriate global value which will + * be used as the initial value in the server negation. + */ +-static int ++static int + default_value(char *mopt) + { + struct mount_options *options = NULL; +@@ -253,11 +170,11 @@ default_value(char *mopt) + if (strncasecmp(field, "proto", strlen("proto")) == 0) { + if ((options = po_split(field)) != NULL) { + if (!nfs_nfs_protocol(options, &config_default_proto)) { +- xlog_warn("Unable to set default protocol : %s", ++ xlog_warn("Unable to set default protocol : %s", + strerror(errno)); + } + if (!nfs_nfs_proto_family(options, &config_default_family)) { +- xlog_warn("Unable to set default family : %s", ++ xlog_warn("Unable to set default family : %s", + strerror(errno)); + } + } else { +@@ -266,14 +183,13 @@ default_value(char *mopt) + } else if (strncasecmp(field, "vers", strlen("vers")) == 0) { + if ((options = po_split(field)) != NULL) { + if (!nfs_nfs_version("nfs", options, &config_default_vers)) { +- xlog_warn("Unable to set default version: %s", ++ xlog_warn("Unable to set default version: %s", + strerror(errno)); +- + } + } else { + xlog_warn("Unable to alloc memory for default version"); + } +- } else ++ } else + xlog_warn("Invalid default setting: '%s'", mopt); + + if (options) +@@ -282,32 +198,60 @@ default_value(char *mopt) + return 1; + } + /* +- * Parse the given section of the configuration ++ * Parse the given section of the configuration + * file to if there are any mount options set. + * If so, added them to link list. + */ +-static void +-conf_parse_mntopts(char *section, char *arg, char *opts) ++static void ++conf_parse_mntopts(char *section, char *arg, struct mount_options *options) + { + struct conf_list *list; + struct conf_list_node *node; + char buf[BUFSIZ], *value, *field; + char *nvalue, *ptr; + int argtype; ++ int have_version = 0; ++ ++ if (po_rightmost(options, version_keys) >= 0 || ++ po_contains_prefix(options, "v4.", NULL, 0) == PO_FOUND) ++ have_version = 1; + + list = conf_get_tag_list(section, arg); + TAILQ_FOREACH(node, &list->fields, link) { +- /* check first if this is an alias for another option */ +- field = mountopts_alias(node->field, &argtype); + /* +- * Do not overwrite options if already exists ++ * Do not overwrite options if already exists + */ +- snprintf(buf, BUFSIZ, "%s=", field); +- if (opts && strcasestr(opts, buf) != NULL) ++ field = mountopts_alias(node->field, &argtype); ++ if (po_contains(options, field) == PO_FOUND) + continue; ++ /* Some options can be inverted by a "no" prefix. ++ * Check for these. ++ * "no" prefixes are unlikely in the config file as ++ * "option=false" is preferred, but still possible. ++ */ ++ if (strncmp(field, "no", 2) == 0 && ++ po_contains(options, field+2) == PO_FOUND) ++ continue; ++ if (strlen(field) < BUFSIZ-3) { ++ strcat(strcpy(buf, "no"), field); ++ if (po_contains(options, buf) == PO_FOUND) ++ continue; ++ } + +- if (lookup_entry(field) != NULL) ++ /* If fg or bg already present, ignore bg or fg */ ++ if (strcmp(field, "fg") == 0 && ++ po_contains(options, "bg") == PO_FOUND) ++ continue; ++ if (strcmp(field, "bg") == 0 && ++ po_contains(options, "fg") == PO_FOUND) + continue; ++ ++ if (is_version(field)) { ++ if (have_version) ++ continue; ++ have_version = 1; ++ } ++ + buf[0] = '\0'; + value = conf_get_section(section, arg, node->field); + if (value == NULL) +@@ -333,99 +277,66 @@ conf_parse_mntopts(char *section, char *arg, char *opts) + } + if (buf[0] == '\0') + continue; +- /* +- * Keep a running tally of the list size adding +- * one for the ',' that will be appened later +- */ +- list_size += strlen(buf) + 1; +- add_entry(buf); ++ po_append(options, buf); ++ default_value(buf); + } + conf_free_list(list); + } + + /* +- * Concatenate options from the configuration file with the ++ * Concatenate options from the configuration file with the + * given options by building a link list of options from the +- * different sections in the conf file. Options that exists +- * in the either the given options or link list are not ++ * different sections in the conf file. Options that exists ++ * in the either the given options or link list are not + * overwritten so it matter which when each section is +- * parsed. ++ * parsed. + */ +-char *conf_get_mntopts(char *spec, char *mount_point, +- char *mount_opts) ++char *conf_get_mntopts(char *spec, char *mount_point, ++ char *mount_opts) + { +- struct entry *entry; +- char *ptr, *server, *config_opts; +- int optlen = 0; ++ struct mount_options *options; ++ char *ptr, *server; + + strict = 0; +- SLIST_INIT(&head); +- list_size = 0; ++ options = po_split(mount_opts); ++ if (!options) { ++ xlog_warn("conf_get_mountops: Unable calloc memory for options"); ++ return mount_opts; ++ } + /* +- * First see if there are any mount options relative ++ * First see if there are any mount options relative + * to the mount point. + */ +- conf_parse_mntopts(NFSMOUNT_MOUNTPOINT, mount_point, mount_opts); ++ conf_parse_mntopts(NFSMOUNT_MOUNTPOINT, mount_point, options); + +- /* ++ /* + * Next, see if there are any mount options relative + * to the server + */ + server = strdup(spec); + if (server == NULL) { +- xlog_warn("conf_get_mountops: Unable calloc memory for server"); +- free_all(); ++ xlog_warn("conf_get_mountops: Unable calloc memory for server"); ++ po_destroy(options); + return mount_opts; + } + if ((ptr = strchr(server, ':')) != NULL) + *ptr='\0'; +- conf_parse_mntopts(NFSMOUNT_SERVER, server, mount_opts); ++ conf_parse_mntopts(NFSMOUNT_SERVER, server, options); + free(server); + + /* +- * Finally process all the global mount options. ++ * Finally process all the global mount options. + */ +- conf_parse_mntopts(NFSMOUNT_GLOBAL_OPTS, NULL, mount_opts); ++ conf_parse_mntopts(NFSMOUNT_GLOBAL_OPTS, NULL, options); + + /* +- * If no mount options were found in the configuration file +- * just return what was passed in . ++ * Strip out defaults, which have already been handled, ++ * then join the rest and return. + */ +- if (SLIST_EMPTY(&head)) +- return mount_opts; +- +- /* +- * Found options in the configuration file. So +- * concatenate the configuration options with the +- * options that were passed in +- */ +- if (mount_opts) +- optlen = strlen(mount_opts); +- +- /* list_size + optlen + ',' + '\0' */ +- config_opts = calloc(1, (list_size+optlen+2)); +- if (config_opts == NULL) { +- xlog_warn("conf_get_mountops: Unable calloc memory for config_opts"); +- free_all(); +- return mount_opts; +- } +- +- if (mount_opts) { +- strcpy(config_opts, mount_opts); +- strcat(config_opts, ","); +- } +- SLIST_FOREACH(entry, &head, entries) { +- if (default_value(entry->opt)) +- continue; +- strcat(config_opts, entry->opt); +- strcat(config_opts, ","); +- } +- if ((ptr = strrchr(config_opts, ',')) != NULL) +- *ptr = '\0'; ++ po_remove_all(options, "default"); + +- free_all(); +- if (mount_opts) +- free(mount_opts); ++ po_join(options, &mount_opts); ++ po_destroy(options); + +- return config_opts; ++ return mount_opts; + } +diff --git a/utils/mount/network.c b/utils/mount/network.c +index d9c0b51..e803dbb 100644 +--- a/utils/mount/network.c ++++ b/utils/mount/network.c +@@ -1269,27 +1269,31 @@ int + nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *version) + { + char *version_key, *version_val = NULL, *cptr; +- int i, found = 0; ++ int i, found = -1; + + version->v_mode = V_DEFAULT; + + for (i = 0; nfs_version_opttbl[i]; i++) { + if (po_contains_prefix(options, nfs_version_opttbl[i], +- &version_key) == PO_FOUND) { +- found++; +- break; ++ &version_key, 0) == PO_FOUND) { ++ if (found >= 0) ++ goto ret_error_multiple; ++ if (po_contains_prefix(options, nfs_version_opttbl[i], ++ NULL, 1) == PO_FOUND) ++ goto ret_error_multiple; ++ found = i; + } + } + +- if (!found && strcmp(type, "nfs4") == 0) ++ if (found < 0 && strcmp(type, "nfs4") == 0) + version_val = type + 3; +- else if (!found) ++ else if (found < 0) + return 1; +- else if (i <= 2 ) { ++ else if (found <= 2 ) { + /* v2, v3, v4 */ + version_val = version_key + 1; + version->v_mode = V_SPECIFIC; +- } else if (i > 2 ) { ++ } else if (found > 2 ) { + /* vers=, nfsvers= */ + version_val = po_get(options, version_key); + } +@@ -1303,7 +1307,7 @@ nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *v + if (version->major == 4 && *cptr != '.' && + (version_val = po_get(options, "minorversion")) != NULL) { + version->minor = strtol(version_val, &cptr, 10); +- i = -1; ++ found = -1; + if (*cptr) + goto ret_error; + version->v_mode = V_SPECIFIC; +@@ -1319,7 +1323,7 @@ nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *v + if (version_val != NULL) { + version->minor = strtol(version_val, &cptr, 10); + version->v_mode = V_SPECIFIC; +- } else ++ } else + version->v_mode = V_GENERAL; + } + if (*cptr != '\0') +@@ -1327,17 +1331,21 @@ nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *v + + return 1; + ++ret_error_multiple: ++ nfs_error(_("%s: multiple version options not permitted"), ++ progname); ++ found = 10; /* avoid other errors */ + ret_error: +- if (i < 0) { ++ if (found < 0) { + nfs_error(_("%s: parsing error on 'minorversion=' option"), + progname); +- } else if (i <= 2 ) { ++ } else if (found <= 2 ) { + nfs_error(_("%s: parsing error on 'v' option"), + progname); +- } else if (i == 3 ) { ++ } else if (found == 3 ) { + nfs_error(_("%s: parsing error on 'vers=' option"), + progname); +- } else if (i == 4) { ++ } else if (found == 4) { + nfs_error(_("%s: parsing error on 'nfsvers=' option"), + progname); + } +diff --git a/utils/mount/nfsmount.conf.man b/utils/mount/nfsmount.conf.man +index 3aa3456..73c3e11 100644 +--- a/utils/mount/nfsmount.conf.man ++++ b/utils/mount/nfsmount.conf.man +@@ -1,53 +1,84 @@ +-.\"@(#)nfsmount.conf.5" +-.TH NFSMOUNT.CONF 5 "9 October 2012" ++."@(#)nfsmount.conf.5" ++.TH NFSMOUNT.CONF 5 "16 December 2020" + .SH NAME + nfsmount.conf - Configuration file for NFS mounts + .SH SYNOPSIS + Configuration file for NFS mounts that allows options + to be set globally, per server or per mount point. + .SH DESCRIPTION +-The configuration file is made up of multiple sections +-followed by variables associated with that section. +-A section is defined by a string enclosed by ++The configuration file is made up of multiple section headers ++followed by variable assignments associated with that section. ++A section header is defined by a string enclosed by + .BR [ +-and ++and + .BR ] +-branches. +-Variables are assignment statements that assign values +-to particular variables using the +-.BR = +-operator, as in ++brackets. ++Variable assignments are assignment statements that assign values ++to particular variables using the ++.BR = ++operator, as in + .BR Proto=Tcp . +-The variables that can be assigned are exactly the set of NFS specific ++The variables that can be assigned are the set of NFS specific + mount options listed in +-.BR nfs (5). ++.BR nfs (5) ++together with the filesystem-independant mount options listed in ++.BR mount (8) ++and three additions: ++.B Sloppy=True ++has the same effect as the ++.B -s ++option to ++.IR mount , ++and ++.B Foreground=True ++and ++.B Background=True ++have the same effect as ++.B bg ++and ++.BR fg . ++.PP ++Options in the config file may be given in upper, lower, or mixed case ++and will be shifted to lower case before being passed to the filesystem. ++.PP ++Boolean mount options which do not need an equals sign must be given as ++.RI \[dq] option =True". ++Instead of preceeding such an option with ++.RB \[dq] no \[dq] ++its negation must be given as ++.RI \[dq] option =False". + .PP + Sections are broken up into three basic categories: + Global options, Server options and Mount Point options. + .HP + .B [ NFSMount_Global_Options ] + - This statically named section +-defines all of the global mount options that can be ++defines all of the global mount options that can be + applied to every NFS mount. + .HP +-.B [ Server \(lqServer_Name\(rq ] +-- This section defines all the mount options that should +-be used on mounts to a particular NFS server. The +-.I \(lqServer_Name\(rq +-strings needs to be surrounded by '\(lq' and +-be an exact match of the server name used in the ++.B [ Server \[dq]Server_Name\[dq] ] ++- This section defines all the mount options that should ++be used on mounts to a particular NFS server. The ++.I \[dq]Server_Name\[dq] ++strings needs to be surrounded by '\[dq]' and be an exact match ++(ignoring case) of the server name used in the + .B mount +-command. ++command. + .HP +-.B [ MountPoint \(lqMount_Point\(rq ] +-- This section defines all the mount options that ++.B [ MountPoint \[dq]Mount_Point\[dq] ] ++- This section defines all the mount options that + should be used on a particular mount point. +-The +-.I \(lqMount_Point\(rq +-string needs to be surrounded by '\(lq' and be an +-exact match of the mount point used in the +-.BR mount +-command. ++The ++.I \[dq]Mount_Point\[dq] ++string needs to be surrounded by '\[dq]' and be an ++exact match of the mount point used in the ++.BR mount ++command. Though path names are usually case-sensitive, the Mount_Point ++name is matched insensitive to case. ++.PP ++The sections are processed in the reverse of the order listed above, and ++any options already seen, either in a previous section or on the ++command line, will be ignored when seen again. + .SH EXAMPLES + .PP + These are some example lines of how sections and variables +@@ -57,37 +88,43 @@ are defined in the configuration file. + .br + Proto=Tcp + .RS +-.HP ++.PP + The TCP/IPv4 protocol will be used on every NFS mount. +-.HP + .RE +-[ Server \(lqnfsserver.foo.com\(rq ] ++.PP ++[ Server \[dq]nfsserver.foo.com\[dq] ] + .br + rsize=32k + .br + wsize=32k + .br + proto=udp6 +-.HP + .RS ++.PP + A 32k (32768 bytes) block size will be used as the read and write + size on all mounts to the 'nfsserver.foo.com' server. UDP/IPv6 + is the protocol to be used. +-.HP + .RE +-.BR +-[ MountPoint \(lq/export/home\(rq ] ++.PP ++[ MountPoint \[dq]/export/home\[dq] ] + .br + Background=True + .RS +-.HP ++.PP + All mounts to the '/export/home' export will be performed in + the background (i.e. done asynchronously). +-.HP ++.RE + .SH FILES + .TP 10n + .I /etc/nfsmount.conf + Default NFS mount configuration file ++.TP 10n ++.I /etc/nfsmount.conf.d ++When this directory exists and files ending ++with ".conf" exist, those files will be ++used to set configuration variables. These ++files will override variables set ++in /etc/nfsmount.conf + .PD + .SH SEE ALSO + .BR nfs (5), +diff --git a/utils/mount/parse_opt.c b/utils/mount/parse_opt.c +index 7ba61c4..b6065ca 100644 +--- a/utils/mount/parse_opt.c ++++ b/utils/mount/parse_opt.c +@@ -414,19 +414,25 @@ po_found_t po_contains(struct mount_options *options, char *keyword) + * @options: pointer to mount options + * @prefix: pointer to prefix to match against a keyword + * @keyword: pointer to a C string containing the option keyword if found ++ * @n: number of instances to skip, so '0' returns the first. + * + * On success, *keyword contains the pointer of the matching option's keyword. + */ + po_found_t po_contains_prefix(struct mount_options *options, +- const char *prefix, char **keyword) ++ const char *prefix, char **keyword, int n) + { + struct mount_option *option; + + if (options && prefix) { + for (option = options->head; option; option = option->next) + if (strncmp(option->keyword, prefix, strlen(prefix)) == 0) { +- *keyword = option->keyword; +- return PO_FOUND; ++ if (n > 0) { ++ n -= 1; ++ } else { ++ if (keyword) ++ *keyword = option->keyword; ++ return PO_FOUND; ++ } + } + } + +diff --git a/utils/mount/parse_opt.h b/utils/mount/parse_opt.h +index 0745e0f..0a15376 100644 +--- a/utils/mount/parse_opt.h ++++ b/utils/mount/parse_opt.h +@@ -46,7 +46,8 @@ po_return_t po_join(struct mount_options *, char **); + po_return_t po_append(struct mount_options *, char *); + po_found_t po_contains(struct mount_options *, char *); + po_found_t po_contains_prefix(struct mount_options *options, +- const char *prefix, char **keyword); ++ const char *prefix, char **keyword, ++ int n); + char * po_get(struct mount_options *, char *); + po_found_t po_get_numeric(struct mount_options *, + char *, long *); +diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c +index a412a02..c9f0385 100644 +--- a/utils/nfsd/nfsd.c ++++ b/utils/nfsd/nfsd.c +@@ -162,7 +162,7 @@ main(int argc, char **argv) + } + } + +- while ((c = getopt_long(argc, argv, "dH:hN:V:p:P:stTituUrG:L:", longopts, NULL)) != EOF) { ++ while ((c = getopt_long(argc, argv, "dH:hN:V:p:P:stTuUrG:L:", longopts, NULL)) != EOF) { + switch(c) { + case 'd': + xlog_config(D_ALL, 1); diff --git a/nfs-utils.spec b/nfs-utils.spec index aeef9fa..860b693 100644 --- a/nfs-utils.spec +++ b/nfs-utils.spec @@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser Name: nfs-utils URL: http://linux-nfs.org/ Version: 2.5.2 -Release: 1.rc1%{?dist} +Release: 1.rc3%{?dist} Epoch: 1 # group all 32bit related archs @@ -16,7 +16,7 @@ Source4: nfsconvert.py Source5: nfsconvert.sh Source6: nfs-convert.service -Patch001: nfs-utils-2.5.3-rc1.patch +Patch001: nfs-utils-2.5.3-rc3.patch Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch @@ -365,6 +365,9 @@ fi %{_pkgdir}/*/var-lib-nfs-rpc_pipefs.mount %changelog +* Thu Dec 17 2020 Steve Dickson 2.5.2-1.rc3 +- Updated to the latest RC release: nfs-utils-2-5-3-rc3 (bz 1906841) + * Tue Nov 10 2020 Steve Dickson 2.5.2-1.rc1 - Updated to the latest RC release: nfs-utils-2-5-3-rc1 (bz 1896543)