0435d8596e
Signed-off-by: Steve Dickson <steved@redhat.com>
1118 lines
30 KiB
Diff
1118 lines
30 KiB
Diff
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 <libgen.h>
|
|
#include <sys/file.h>
|
|
#include <time.h>
|
|
+#include <dirent.h>
|
|
|
|
#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..e865998 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 <nfs@redhat.com>
|
|
*
|
|
* - 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,68 @@ 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);
|
|
+ if (default_value(buf))
|
|
+ continue;
|
|
+
|
|
+ po_append(options, 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.
|
|
- */
|
|
- conf_parse_mntopts(NFSMOUNT_GLOBAL_OPTS, NULL, mount_opts);
|
|
-
|
|
- /*
|
|
- * If no mount options were found in the configuration file
|
|
- * just return what was passed in .
|
|
+ * Finally process all the global mount options.
|
|
*/
|
|
- if (SLIST_EMPTY(&head))
|
|
- return mount_opts;
|
|
+ conf_parse_mntopts(NFSMOUNT_GLOBAL_OPTS, NULL, options);
|
|
|
|
/*
|
|
- * Found options in the configuration file. So
|
|
- * concatenate the configuration options with the
|
|
- * options that were passed in
|
|
+ * Strip out defaults, which have already been handled,
|
|
+ * then join the rest and return.
|
|
*/
|
|
- 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/mountd/v4root.c b/utils/mountd/v4root.c
|
|
index dd9828e..6f640aa 100644
|
|
--- a/utils/mountd/v4root.c
|
|
+++ b/utils/mountd/v4root.c
|
|
@@ -34,9 +34,9 @@ static nfs_export pseudo_root = {
|
|
.m_export = {
|
|
.e_hostname = "*",
|
|
.e_path = "/",
|
|
- .e_flags = NFSEXP_READONLY | NFSEXP_ROOTSQUASH
|
|
+ .e_flags = NFSEXP_READONLY
|
|
| NFSEXP_NOSUBTREECHECK | NFSEXP_FSID
|
|
- | NFSEXP_V4ROOT,
|
|
+ | NFSEXP_V4ROOT | NFSEXP_INSECURE_PORT,
|
|
.e_anonuid = 65534,
|
|
.e_anongid = 65534,
|
|
.e_squids = NULL,
|
|
@@ -55,15 +55,11 @@ static nfs_export pseudo_root = {
|
|
};
|
|
|
|
static void
|
|
-set_pseudofs_security(struct exportent *pseudo, int flags)
|
|
+set_pseudofs_security(struct exportent *pseudo)
|
|
{
|
|
struct flav_info *flav;
|
|
int i;
|
|
|
|
- if (flags & NFSEXP_INSECURE_PORT)
|
|
- pseudo->e_flags |= NFSEXP_INSECURE_PORT;
|
|
- if ((flags & NFSEXP_ROOTSQUASH) == 0)
|
|
- pseudo->e_flags &= ~NFSEXP_ROOTSQUASH;
|
|
for (flav = flav_map; flav < flav_map + flav_map_size; flav++) {
|
|
struct sec_entry *new;
|
|
|
|
@@ -73,8 +69,7 @@ set_pseudofs_security(struct exportent *pseudo, int flags)
|
|
i = secinfo_addflavor(flav, pseudo);
|
|
new = &pseudo->e_secinfo[i];
|
|
|
|
- if (flags & NFSEXP_INSECURE_PORT)
|
|
- new->flags |= NFSEXP_INSECURE_PORT;
|
|
+ new->flags |= NFSEXP_INSECURE_PORT;
|
|
}
|
|
}
|
|
|
|
@@ -93,7 +88,7 @@ v4root_create(char *path, nfs_export *export)
|
|
strncpy(eep.e_path, path, sizeof(eep.e_path)-1);
|
|
if (strcmp(path, "/") != 0)
|
|
eep.e_flags &= ~NFSEXP_FSID;
|
|
- set_pseudofs_security(&eep, curexp->e_flags);
|
|
+ set_pseudofs_security(&eep);
|
|
exp = export_create(&eep, 0);
|
|
if (exp == NULL)
|
|
return NULL;
|
|
@@ -141,7 +136,7 @@ pseudofs_update(char *hostname, char *path, nfs_export *source)
|
|
return 0;
|
|
}
|
|
/* Update an existing V4ROOT export: */
|
|
- set_pseudofs_security(&exp->m_export, source->m_export.e_flags);
|
|
+ set_pseudofs_security(&exp->m_export);
|
|
return 0;
|
|
}
|
|
|
|
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);
|