From 93a5c3e3cadf419e44167a3701f8901a72ee0139 Mon Sep 17 00:00:00 2001 From: Steve Dickson Date: Jun 01 2017 15:01:36 +0000 Subject: Updated to the latest RC releease: nfs-utils-2-1-2-rc3 (bz 1457921) Signed-off-by: Steve Dickson --- diff --git a/nfs-utils-2.1.1-network-online.patch b/nfs-utils-2.1.1-network-online.patch deleted file mode 100644 index 0b01e16..0000000 --- a/nfs-utils-2.1.1-network-online.patch +++ /dev/null @@ -1,66 +0,0 @@ -commit 09e5c6c2a3f8eac91d5353e6d4ff6aee7757ab08 -Author: Steve Dickson -Date: Mon Apr 24 11:25:39 2017 -0400 - - systemd: Afters are also needed for the Wants=network-online.target - - Commit 9d4fc3fb added Wants=network-online.target which - is not enough to ensure the network is completely up - before the NFS server is started. After=network-online.target - is also needed. - - Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1419351 - - Signed-off-by: Steve Dickson - -diff --git a/systemd/nfs-mountd.service b/systemd/nfs-mountd.service -index fec0399..e8ece53 100644 ---- a/systemd/nfs-mountd.service -+++ b/systemd/nfs-mountd.service -@@ -4,6 +4,7 @@ DefaultDependencies=no - Requires=proc-fs-nfsd.mount - Wants=network-online.target - After=proc-fs-nfsd.mount -+After=network-online.target local-fs.target - After=rpcbind.socket - BindsTo=nfs-server.service - -diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service -index e6b8f58..136552b 100644 ---- a/systemd/nfs-server.service -+++ b/systemd/nfs-server.service -@@ -7,7 +7,7 @@ Wants=rpcbind.socket network-online.target - Wants=rpc-statd.service nfs-idmapd.service - Wants=rpc-statd-notify.service - --After= local-fs.target -+After= network-online.target local-fs.target - After= proc-fs-nfsd.mount rpcbind.socket nfs-mountd.service - After= nfs-idmapd.service rpc-statd.service - Before= rpc-statd-notify.service -diff --git a/systemd/rpc-statd-notify.service b/systemd/rpc-statd-notify.service -index f54d4c5..687fe31 100644 ---- a/systemd/rpc-statd-notify.service -+++ b/systemd/rpc-statd-notify.service -@@ -2,7 +2,7 @@ - Description=Notify NFS peers of a restart - DefaultDependencies=no - Wants=network-online.target --After=local-fs.target network.target nss-lookup.target -+After=local-fs.target network-online.target nss-lookup.target - - # if we run an nfs server, it needs to be running before we - # tell clients that it has restarted. -diff --git a/systemd/rpc-statd.service b/systemd/rpc-statd.service -index 8cef022..f41ae20 100644 ---- a/systemd/rpc-statd.service -+++ b/systemd/rpc-statd.service -@@ -4,7 +4,7 @@ DefaultDependencies=no - Conflicts=umount.target - Requires=nss-lookup.target rpcbind.socket - Wants=network-online.target --After=nss-lookup.target rpcbind.socket -+After=network-online.target nss-lookup.target rpcbind.socket - - PartOf=nfs-utils.service - diff --git a/nfs-utils-2.1.1-nfsdcltrack-errors.patch b/nfs-utils-2.1.1-nfsdcltrack-errors.patch deleted file mode 100644 index 54af57f..0000000 --- a/nfs-utils-2.1.1-nfsdcltrack-errors.patch +++ /dev/null @@ -1,37 +0,0 @@ -commit 06bbb4ee6f5186e8e83d50767ad5b3b41968e5a6 -Author: Jeff Layton -Date: Wed Apr 26 12:13:50 2017 -0400 - - nfsdcltrack: silence some expected errors - - On a new install, we're unable to select from the parameters table, as - it doesn't exist yet. The code is set up to log that fact at L_ERROR - now, but it's an expected situation. Change it to log that at D_GENERAL - instead. - - Reported-and-Tested-by: ChunYu Wang - Signed-off-by: Jeff Layton - Signed-off-by: Steve Dickson - -diff --git a/utils/nfsdcltrack/sqlite.c b/utils/nfsdcltrack/sqlite.c -index 54cd748..1552eba 100644 ---- a/utils/nfsdcltrack/sqlite.c -+++ b/utils/nfsdcltrack/sqlite.c -@@ -101,7 +101,7 @@ sqlite_query_schema_version(void) - "SELECT value FROM parameters WHERE key == \"version\";", - -1, &stmt, NULL); - if (ret != SQLITE_OK) { -- xlog(L_ERROR, "Unable to prepare select statement: %s", -+ xlog(D_GENERAL, "Unable to prepare select statement: %s", - sqlite3_errmsg(dbh)); - ret = 0; - goto out; -@@ -110,7 +110,7 @@ sqlite_query_schema_version(void) - /* query schema version */ - ret = sqlite3_step(stmt); - if (ret != SQLITE_ROW) { -- xlog(L_ERROR, "Select statement execution failed: %s", -+ xlog(D_GENERAL, "Select statement execution failed: %s", - sqlite3_errmsg(dbh)); - ret = 0; - goto out; diff --git a/nfs-utils-2.1.2-rc1.patch b/nfs-utils-2.1.2-rc1.patch deleted file mode 100644 index cc848bd..0000000 --- a/nfs-utils-2.1.2-rc1.patch +++ /dev/null @@ -1,1040 +0,0 @@ -diff --git a/support/export/xtab.c b/support/export/xtab.c -index 22cf539..d42eeef 100644 ---- a/support/export/xtab.c -+++ b/support/export/xtab.c -@@ -14,12 +14,20 @@ - #include - #include - #include -+#include -+#include -+#include -+#include - - #include "nfslib.h" - #include "exportfs.h" - #include "xio.h" - #include "xlog.h" - #include "v4root.h" -+#include "misc.h" -+ -+static char state_base_dirname[PATH_MAX] = NFS_STATEDIR; -+extern struct state_paths etab; - - int v4root_needed; - static void cond_rename(char *newfile, char *oldfile); -@@ -65,7 +73,7 @@ xtab_read(char *xtab, char *lockfn, int is_export) - int - xtab_export_read(void) - { -- return xtab_read(_PATH_ETAB, _PATH_ETABLCK, 1); -+ return xtab_read(etab.statefn, etab.lockfn, 1); - } - - /* -@@ -112,7 +120,7 @@ xtab_write(char *xtab, char *xtabtmp, char *lockfn, int is_export) - int - xtab_export_write() - { -- return xtab_write(_PATH_ETAB, _PATH_ETABTMP, _PATH_ETABLCK, 1); -+ return xtab_write(etab.statefn, etab.tmpfn, etab.lockfn, 1); - } - - /* -@@ -158,3 +166,74 @@ static void cond_rename(char *newfile, char *oldfile) - rename(newfile, oldfile); - return; - } -+ -+/* -+ * Returns a dynamically allocated, '\0'-terminated buffer -+ * containing an appropriate pathname, or NULL if an error -+ * occurs. Caller must free the returned result with free(3). -+ */ -+static char * -+state_make_pathname(const char *tabname) -+{ -+ return generic_make_pathname(state_base_dirname, tabname); -+} -+ -+/** -+ * state_setup_basedir - set up basedir -+ * @progname: C string containing name of program, for error messages -+ * @parentdir: C string containing pathname to on-disk state, or NULL -+ * -+ * This runs before logging is set up, so error messages are directed -+ * to stderr. -+ * -+ * Returns true and sets up our basedir, if @parentdir was valid -+ * and usable; otherwise false is returned. -+ */ -+_Bool -+state_setup_basedir(const char *progname, const char *parentdir) -+{ -+ return generic_setup_basedir(progname, parentdir, state_base_dirname, -+ PATH_MAX); -+} -+ -+int -+setup_state_path_names(const char *progname, const char *statefn, -+ const char *tmpfn, const char *lockfn, -+ struct state_paths *paths) -+{ -+ paths->statefn = state_make_pathname(statefn); -+ if (!paths->statefn) { -+ fprintf(stderr, "%s: state_make_pathname(%s) failed\n", -+ progname, statefn); -+ goto out_err; -+ } -+ paths->tmpfn = state_make_pathname(tmpfn); -+ if (!paths->tmpfn) { -+ fprintf(stderr, "%s: state_make_pathname(%s) failed\n", -+ progname, tmpfn); -+ goto out_free_statefn; -+ } -+ paths->lockfn = state_make_pathname(lockfn); -+ if (!paths->lockfn) { -+ fprintf(stderr, "%s: state_make_pathname(%s) failed\n", -+ progname, lockfn); -+ goto out_free_tmpfn; -+ } -+ return 1; -+ -+out_free_tmpfn: -+ free(paths->tmpfn); -+out_free_statefn: -+ free(paths->statefn); -+out_err: -+ return 0; -+ -+} -+ -+void -+free_state_path_names(struct state_paths *paths) -+{ -+ free(paths->statefn); -+ free(paths->tmpfn); -+ free(paths->lockfn); -+} -diff --git a/support/include/misc.h b/support/include/misc.h -index eedc1fe..06e2a0c 100644 ---- a/support/include/misc.h -+++ b/support/include/misc.h -@@ -15,6 +15,9 @@ - int randomkey(unsigned char *keyout, int len); - int weakrandomkey(unsigned char *keyout, int len); - -+char *generic_make_pathname(const char *, const char *); -+_Bool generic_setup_basedir(const char *, const char *, char *, const size_t); -+ - extern int is_mountpoint(char *path); - - /* size of the file pointer buffers for rpc procfs files */ -diff --git a/support/include/nfslib.h b/support/include/nfslib.h -index 1498977..ab8b2bf 100644 ---- a/support/include/nfslib.h -+++ b/support/include/nfslib.h -@@ -35,29 +35,24 @@ - #ifndef _PATH_IDMAPDCONF - #define _PATH_IDMAPDCONF "/etc/idmapd.conf" - #endif --#ifndef _PATH_ETAB --#define _PATH_ETAB NFS_STATEDIR "/etab" --#endif --#ifndef _PATH_ETABTMP --#define _PATH_ETABTMP NFS_STATEDIR "/etab.tmp" --#endif --#ifndef _PATH_ETABLCK --#define _PATH_ETABLCK NFS_STATEDIR "/.etab.lock" --#endif --#ifndef _PATH_RMTAB --#define _PATH_RMTAB NFS_STATEDIR "/rmtab" --#endif --#ifndef _PATH_RMTABTMP --#define _PATH_RMTABTMP _PATH_RMTAB ".tmp" --#endif --#ifndef _PATH_RMTABLCK --#define _PATH_RMTABLCK NFS_STATEDIR "/.rmtab.lock" --#endif - #ifndef _PATH_PROC_EXPORTS - #define _PATH_PROC_EXPORTS "/proc/fs/nfs/exports" - #define _PATH_PROC_EXPORTS_ALT "/proc/fs/nfsd/exports" - #endif - -+#define ETAB "etab" -+#define ETABTMP "etab.tmp" -+#define ETABLCK ".etab.lock" -+#define RMTAB "rmtab" -+#define RMTABTMP "rmtab.tmp" -+#define RMTABLCK ".rmtab.lock" -+ -+struct state_paths { -+ char *statefn; -+ char *tmpfn; -+ char *lockfn; -+}; -+ - /* Maximum number of security flavors on an export: */ - #define SECFLAVOR_COUNT 8 - -@@ -120,6 +115,10 @@ void fputrmtabent(FILE *fp, struct rmtabent *xep, long *pos); - void fendrmtabent(FILE *fp); - void frewindrmtabent(FILE *fp); - -+_Bool state_setup_basedir(const char *, const char *); -+int setup_state_path_names(const char *, const char *, const char *, const char *, struct state_paths *); -+void free_state_path_names(struct state_paths *); -+ - /* mydaemon */ - void daemon_init(bool fg); - void daemon_ready(void); -diff --git a/support/misc/Makefile.am b/support/misc/Makefile.am -index 1048580..8936b0d 100644 ---- a/support/misc/Makefile.am -+++ b/support/misc/Makefile.am -@@ -1,6 +1,6 @@ - ## Process this file with automake to produce Makefile.in - - noinst_LIBRARIES = libmisc.a --libmisc_a_SOURCES = tcpwrapper.c from_local.c mountpoint.c -+libmisc_a_SOURCES = tcpwrapper.c from_local.c mountpoint.c file.c - - MAINTAINERCLEANFILES = Makefile.in -diff --git a/support/misc/file.c b/support/misc/file.c -new file mode 100644 -index 0000000..63597df ---- /dev/null -+++ b/support/misc/file.c -@@ -0,0 +1,110 @@ -+/* -+ * Copyright 2009 Oracle. All rights reserved. -+ * Copyright 2017 Red Hat, Inc. All rights reserved. -+ * -+ * This file is part of nfs-utils. -+ * -+ * nfs-utils is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * nfs-utils is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with nfs-utils. If not, see . -+ */ -+ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "xlog.h" -+#include "misc.h" -+ -+/* -+ * Returns a dynamically allocated, '\0'-terminated buffer -+ * containing an appropriate pathname, or NULL if an error -+ * occurs. Caller must free the returned result with free(3). -+ */ -+__attribute__((__malloc__)) -+char * -+generic_make_pathname(const char *base, const char *leaf) -+{ -+ size_t size; -+ char *path; -+ int len; -+ -+ size = strlen(base) + strlen(leaf) + 2; -+ if (size > PATH_MAX) -+ return NULL; -+ -+ path = malloc(size); -+ if (path == NULL) -+ return NULL; -+ -+ len = snprintf(path, size, "%s/%s", base, leaf); -+ if ((len < 0) || ((size_t)len >= size)) { -+ free(path); -+ return NULL; -+ } -+ -+ return path; -+} -+ -+ -+/** -+ * generic_setup_basedir - set up basedir -+ * @progname: C string containing name of program, for error messages -+ * @parentdir: C string containing pathname to on-disk state, or NULL -+ * @base: character buffer to contain the basedir that is set up -+ * @baselen: size of @base in bytes -+ * -+ * This runs before logging is set up, so error messages are directed -+ * to stderr. -+ * -+ * Returns true and sets up our basedir, if @parentdir was valid -+ * and usable; otherwise false is returned. -+ */ -+_Bool -+generic_setup_basedir(const char *progname, const char *parentdir, char *base, -+ const size_t baselen) -+{ -+ static char buf[PATH_MAX]; -+ struct stat st; -+ char *path; -+ -+ /* First: test length of name and whether it exists */ -+ if ((strlen(parentdir) >= baselen) || (strlen(parentdir) >= PATH_MAX)) { -+ (void)fprintf(stderr, "%s: Directory name too long: %s", -+ progname, parentdir); -+ return false; -+ } -+ if (lstat(parentdir, &st) == -1) { -+ (void)fprintf(stderr, "%s: Failed to stat %s: %s", -+ progname, parentdir, strerror(errno)); -+ return false; -+ } -+ -+ /* Ensure we have a clean directory pathname */ -+ strncpy(buf, parentdir, sizeof(buf)); -+ path = dirname(buf); -+ if (*path == '.') { -+ (void)fprintf(stderr, "%s: Unusable directory %s", -+ progname, parentdir); -+ return false; -+ } -+ -+ xlog(D_CALL, "Using %s as the state directory", parentdir); -+ strcpy(base, parentdir); -+ return true; -+} -diff --git a/support/nfs/cacheio.c b/support/nfs/cacheio.c -index e5e2579..9912afa 100644 ---- a/support/nfs/cacheio.c -+++ b/support/nfs/cacheio.c -@@ -27,6 +27,8 @@ - #include - #include - -+extern struct state_paths etab; -+ - void qword_add(char **bpp, int *lp, char *str) - { - char *bp = *bpp; -@@ -199,7 +201,7 @@ int qword_get_uint(char **bpp, unsigned int *anint) - } - - /* flush the kNFSd caches. -- * Set the flush time to the mtime of _PATH_ETAB or -+ * Set the flush time to the mtime of the etab state file or - * if force, to now. - * the caches to flush are: - * auth.unix.ip nfsd.export nfsd.fh -@@ -228,7 +230,7 @@ cache_flush(int force) - }; - now = time(0); - if (force || -- stat(_PATH_ETAB, &stb) != 0 || -+ stat(etab.statefn, &stb) != 0 || - stb.st_mtime > now) - stb.st_mtime = time(0); - -diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c -index e717c1e..203efd2 100644 ---- a/support/nfs/conffile.c -+++ b/support/nfs/conffile.c -@@ -533,7 +533,7 @@ retry: - * or from environment - */ - char *env = getenv(cb->value+1); -- if (env) -+ if (env && *env) - return env; - section = "environment"; - tag = cb->value + 1; -diff --git a/support/nfs/rmtab.c b/support/nfs/rmtab.c -index 59dfbdf..2ecb2cc 100644 ---- a/support/nfs/rmtab.c -+++ b/support/nfs/rmtab.c -@@ -33,12 +33,14 @@ - - static FILE *rmfp = NULL; - -+extern struct state_paths rmtab; -+ - int - setrmtabent(char *type) - { - if (rmfp) - fclose(rmfp); -- rmfp = fsetrmtabent(_PATH_RMTAB, type); -+ rmfp = fsetrmtabent(rmtab.statefn, type); - return (rmfp != NULL); - } - -diff --git a/support/nsm/file.c b/support/nsm/file.c -index aafa755..52f5401 100644 ---- a/support/nsm/file.c -+++ b/support/nsm/file.c -@@ -88,6 +88,7 @@ - - #include "xlog.h" - #include "nsm.h" -+#include "misc.h" - - #define RPCARGSLEN (4 * (8 + 1)) - #define LINELEN (RPCARGSLEN + SM_PRIV_SIZE * 2 + 1) -@@ -170,25 +171,7 @@ __attribute__((__malloc__)) - static char * - nsm_make_pathname(const char *directory) - { -- size_t size; -- char *path; -- int len; -- -- size = strlen(nsm_base_dirname) + strlen(directory) + 2; -- if (size > PATH_MAX) -- return NULL; -- -- path = malloc(size); -- if (path == NULL) -- return NULL; -- -- len = snprintf(path, size, "%s/%s", nsm_base_dirname, directory); -- if (error_check(len, size)) { -- free(path); -- return NULL; -- } -- -- return path; -+ return generic_make_pathname(nsm_base_dirname, directory); - } - - /* -@@ -293,29 +276,8 @@ out: - _Bool - nsm_setup_pathnames(const char *progname, const char *parentdir) - { -- static char buf[PATH_MAX]; -- struct stat st; -- char *path; -- -- /* First: test length of name and whether it exists */ -- if (lstat(parentdir, &st) == -1) { -- (void)fprintf(stderr, "%s: Failed to stat %s: %s", -- progname, parentdir, strerror(errno)); -- return false; -- } -- -- /* Ensure we have a clean directory pathname */ -- strncpy(buf, parentdir, sizeof(buf)); -- path = dirname(buf); -- if (*path == '.') { -- (void)fprintf(stderr, "%s: Unusable directory %s", -- progname, parentdir); -- return false; -- } -- -- xlog(D_CALL, "Using %s as the state directory", parentdir); -- strncpy(nsm_base_dirname, parentdir, sizeof(nsm_base_dirname)); -- return true; -+ return generic_setup_basedir(progname, parentdir, nsm_base_dirname, -+ PATH_MAX); - } - - /** -diff --git a/systemd/nfs-mountd.service b/systemd/nfs-mountd.service -index 15e828b..b0a8bc0 100644 ---- a/systemd/nfs-mountd.service -+++ b/systemd/nfs-mountd.service -@@ -4,6 +4,7 @@ DefaultDependencies=no - Requires=proc-fs-nfsd.mount - After=proc-fs-nfsd.mount - After=network.target local-fs.target -+After=rpcbind.socket - BindsTo=nfs-server.service - - [Service] -diff --git a/systemd/nfs-server-generator.c b/systemd/nfs-server-generator.c -index cc99969..4aa6509 100644 ---- a/systemd/nfs-server-generator.c -+++ b/systemd/nfs-server-generator.c -@@ -84,6 +84,28 @@ static void systemd_escape(FILE *f, char *path) - } - } - -+static int has_noauto_flag(char *path) -+{ -+ FILE *fstab; -+ struct mntent *mnt; -+ -+ fstab = setmntent("/etc/fstab", "r"); -+ if (!fstab) -+ return 0; -+ -+ while ((mnt = getmntent(fstab)) != NULL) { -+ int l = strlen(mnt->mnt_dir); -+ if (strncmp(mnt->mnt_dir, path, l) != 0) -+ continue; -+ if (path[l] && path[l] != '/') -+ continue; -+ if (hasmntopt(mnt, "noauto")) -+ break; -+ } -+ fclose(fstab); -+ return mnt != NULL; -+} -+ - int main(int argc, char *argv[]) - { - char *path; -@@ -124,6 +146,10 @@ int main(int argc, char *argv[]) - for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { - if (!is_unique(&list, exp->m_export.e_path)) - continue; -+ if (exp->m_export.e_mountpoint) -+ continue; -+ if (has_noauto_flag(exp->m_export.e_path)) -+ continue; - if (strchr(exp->m_export.e_path, ' ')) - fprintf(f, "RequiresMountsFor=\"%s\"\n", - exp->m_export.e_path); -diff --git a/systemd/nfs.conf.man b/systemd/nfs.conf.man -index 91c49a0..bdc0988 100644 ---- a/systemd/nfs.conf.man -+++ b/systemd/nfs.conf.man -@@ -154,6 +154,13 @@ section, are used to configure mountd. See - .BR rpc.mountd (8) - for details. - -+The -+.B state-directory-path -+value in the -+.B [mountd] -+section is also used by -+.BR exportfs (8). -+ - .TP - .B statd - Recognized values: -diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c -index 61dddfb..02d5b6d 100644 ---- a/utils/exportfs/exportfs.c -+++ b/utils/exportfs/exportfs.c -@@ -52,6 +52,8 @@ static const char *lockfile = EXP_LOCKFILE; - static int _lockfd = -1; - char *conf_path = NFS_CONFFILE; - -+struct state_paths etab; -+ - /* - * If we aren't careful, changes made by exportfs can be lost - * when multiple exports process run at once: -@@ -95,6 +97,7 @@ main(int argc, char **argv) - int f_ignore = 0; - int i, c; - int force_flush = 0; -+ char *s; - - if ((progname = strrchr(argv[0], '/')) != NULL) - progname++; -@@ -108,6 +111,11 @@ main(int argc, char **argv) - conf_init(); - xlog_from_conffile("exportfs"); - -+ /* NOTE: following uses "mountd" section of nfs.conf !!!! */ -+ s = conf_get_str("mountd", "state-directory-path"); -+ if (s && !state_setup_basedir(argv[0], s)) -+ exit(1); -+ - while ((c = getopt(argc, argv, "ad:fhio:ruvs")) != EOF) { - switch(c) { - case 'a': -@@ -159,13 +167,17 @@ main(int argc, char **argv) - xlog(L_ERROR, "-r and -u are incompatible"); - return 1; - } -+ if (!setup_state_path_names(progname, ETAB, ETABTMP, ETABLCK, &etab)) -+ return 1; - if (optind == argc && ! f_all) { - if (force_flush) { - cache_flush(1); -+ free_state_path_names(&etab); - return 0; - } else { - xtab_export_read(); - dump(f_verbose, f_export_format); -+ free_state_path_names(&etab); - return 0; - } - } -@@ -206,6 +218,7 @@ main(int argc, char **argv) - } - xtab_export_write(); - cache_flush(force_flush); -+ free_state_path_names(&etab); - - return export_errno; - } -diff --git a/utils/exportfs/exportfs.man b/utils/exportfs/exportfs.man -index 45b6d83..91d3589 100644 ---- a/utils/exportfs/exportfs.man -+++ b/utils/exportfs/exportfs.man -@@ -148,6 +148,29 @@ options. - .TP - .B -s - Display the current export list suitable for /etc/exports. -+ -+.SH CONFIGURATION FILE -+The -+.B [exportfs] -+section of the -+.I /etc/nfs.conf -+configuration file can contain a -+.B debug -+value, which can be one or more from the list -+.BR general , -+.BR call , -+.BR auth , -+.BR parse , -+.BR all . -+When a list is given, the members should be comma-separated. -+ -+.B exportfs -+will also recognize the -+.B state-directory-path -+value from the -+.B [mountd] -+section. -+ - .SH DISCUSSION - .SS Exporting Directories - The first synopsis shows how to invoke -diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c -index 4d18d35..77125f1 100644 ---- a/utils/gssd/gssd.c -+++ b/utils/gssd/gssd.c -@@ -87,6 +87,7 @@ int root_uses_machine_creds = 1; - unsigned int context_timeout = 0; - unsigned int rpc_timeout = 5; - char *preferred_realm = NULL; -+char *ccachedir = NULL; - /* Avoid DNS reverse lookups on server names */ - static bool avoid_dns = true; - int thread_started = false; -@@ -837,18 +838,9 @@ usage(char *progname) - exit(1); - } - --int --main(int argc, char *argv[]) -+inline static void -+read_gss_conf(void) - { -- int fg = 0; -- int verbosity = 0; -- int rpc_verbosity = 0; -- int opt; -- int i; -- extern char *optarg; -- char *progname; -- char *ccachedir = NULL; -- struct event sighup_ev; - char *s; - - conf_init(); -@@ -877,6 +869,22 @@ main(int argc, char *argv[]) - if (s) - preferred_realm = s; - -+} -+ -+int -+main(int argc, char *argv[]) -+{ -+ int fg = 0; -+ int verbosity = 0; -+ int rpc_verbosity = 0; -+ int opt; -+ int i; -+ extern char *optarg; -+ char *progname; -+ struct event sighup_ev; -+ -+ read_gss_conf(); -+ - while ((opt = getopt(argc, argv, "DfvrlmnMp:k:d:t:T:R:")) != -1) { - switch (opt) { - case 'f': -diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c -index d74d372..4fc81c3 100644 ---- a/utils/gssd/gssd_proc.c -+++ b/utils/gssd/gssd_proc.c -@@ -729,10 +729,18 @@ handle_gssd_upcall(struct clnt_upcall_info *info) - char *target = NULL; - char *service = NULL; - char *enctypes = NULL; -+ char *upcall_str; -+ char *pbuf = info->lbuf; - - printerr(2, "\n%s: '%s' (%s)\n", __func__, info->lbuf, clp->relpath); - -- for (p = strtok(info->lbuf, " "); p; p = strtok(NULL, " ")) { -+ upcall_str = strdup(info->lbuf); -+ if (upcall_str == NULL) { -+ printerr(0, "ERROR: malloc failure\n"); -+ goto out_nomem; -+ } -+ -+ while ((p = strsep(&pbuf, " "))) { - if (!strncmp(p, "mech=", strlen("mech="))) - mech = p + strlen("mech="); - else if (!strncmp(p, "uid=", strlen("uid="))) -@@ -748,7 +756,7 @@ handle_gssd_upcall(struct clnt_upcall_info *info) - if (!mech || strlen(mech) < 1) { - printerr(0, "WARNING: handle_gssd_upcall: " - "failed to find gss mechanism name " -- "in upcall string '%s'\n", info->lbuf); -+ "in upcall string '%s'\n", upcall_str); - goto out; - } - -@@ -761,7 +769,7 @@ handle_gssd_upcall(struct clnt_upcall_info *info) - if (!uidstr) { - printerr(0, "WARNING: handle_gssd_upcall: " - "failed to find uid " -- "in upcall string '%s'\n", info->lbuf); -+ "in upcall string '%s'\n", upcall_str); - goto out; - } - -@@ -774,7 +782,7 @@ handle_gssd_upcall(struct clnt_upcall_info *info) - if (target && strlen(target) < 1) { - printerr(0, "WARNING: handle_gssd_upcall: " - "failed to parse target name " -- "in upcall string '%s'\n", info->lbuf); -+ "in upcall string '%s'\n", upcall_str); - goto out; - } - -@@ -789,7 +797,7 @@ handle_gssd_upcall(struct clnt_upcall_info *info) - if (service && strlen(service) < 1) { - printerr(0, "WARNING: handle_gssd_upcall: " - "failed to parse service type " -- "in upcall string '%s'\n", info->lbuf); -+ "in upcall string '%s'\n", upcall_str); - goto out; - } - -@@ -802,6 +810,8 @@ handle_gssd_upcall(struct clnt_upcall_info *info) - do_error_downcall(clp->gssd_fd, uid, -EACCES); - } - out: -+ free(upcall_str); -+out_nomem: - free(info); - return; - } -diff --git a/utils/mount/network.c b/utils/mount/network.c -index 7dceb2d..281e935 100644 ---- a/utils/mount/network.c -+++ b/utils/mount/network.c -@@ -33,6 +33,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -804,6 +805,7 @@ int start_statd(void) - pid_t pid = fork(); - switch (pid) { - case 0: /* child */ -+ setgroups(0, NULL); - setgid(0); - setuid(0); - execle(START_STATD, START_STATD, NULL, envp); -@@ -1638,6 +1640,7 @@ int nfs_options2pmap(struct mount_options *options, - struct pmap *nfs_pmap, struct pmap *mnt_pmap) - { - struct nfs_version version; -+ memset(&version, 0, sizeof(version)); - - if (!nfs_nfs_program(options, &nfs_pmap->pm_prog)) - return 0; -diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c -index 387d734..a9ff95d 100644 ---- a/utils/mount/stropts.c -+++ b/utils/mount/stropts.c -@@ -517,6 +517,10 @@ nfs_rewrite_pmap_mount_options(struct mount_options *options, int checkv4) - unsigned long protocol; - struct pmap mnt_pmap; - -+ /* initialize structs */ -+ memset(&nfs_pmap, 0, sizeof(struct pmap)); -+ memset(&mnt_pmap, 0, sizeof(struct pmap)); -+ - /* - * Version and transport negotiation is not required - * and does not work for RDMA mounts. -diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c -index d065830..8299256 100644 ---- a/utils/mountd/auth.c -+++ b/utils/mountd/auth.c -@@ -41,6 +41,8 @@ static nfs_client my_client; - - extern int use_ipaddr; - -+extern struct state_paths etab; -+ - void - auth_init(void) - { -@@ -84,10 +86,10 @@ auth_reload() - static unsigned int counter; - int fd; - -- if ((fd = open(_PATH_ETAB, O_RDONLY)) < 0) { -- xlog(L_FATAL, "couldn't open %s", _PATH_ETAB); -+ if ((fd = open(etab.statefn, O_RDONLY)) < 0) { -+ xlog(L_FATAL, "couldn't open %s", etab.statefn); - } else if (fstat(fd, &stb) < 0) { -- xlog(L_FATAL, "couldn't stat %s", _PATH_ETAB); -+ xlog(L_FATAL, "couldn't stat %s", etab.statefn); - close(fd); - } else if (last_fd != -1 && stb.st_ino == last_inode) { - /* We opened the etab file before, and its inode -diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c -index 61699e6..bbadfaf 100644 ---- a/utils/mountd/mountd.c -+++ b/utils/mountd/mountd.c -@@ -29,6 +29,7 @@ - #include "mountd.h" - #include "rpcmisc.h" - #include "pseudoflavors.h" -+#include "nfslib.h" - - extern void my_svc_run(void); - -@@ -40,6 +41,9 @@ int reverse_resolve = 0; - int manage_gids; - int use_ipaddr = -1; - -+struct state_paths etab; -+struct state_paths rmtab; -+ - char *conf_path = NFS_CONFFILE; - - /* PRC: a high-availability callout program can be specified with -H -@@ -110,8 +114,8 @@ unregister_services (void) - static void - cleanup_lockfiles (void) - { -- unlink(_PATH_ETABLCK); -- unlink(_PATH_RMTABLCK); -+ unlink(etab.lockfn); -+ unlink(rmtab.lockfn); - } - - /* Wait for all worker child processes to exit and reap them */ -@@ -181,6 +185,8 @@ fork_workers(void) - wait_for_workers(); - unregister_services(); - cleanup_lockfiles(); -+ free_state_path_names(&etab); -+ free_state_path_names(&rmtab); - xlog(L_NOTICE, "mountd: no more workers, exiting\n"); - exit(0); - } -@@ -198,6 +204,8 @@ killer (int sig) - wait_for_workers(); - } - cleanup_lockfiles(); -+ free_state_path_names(&etab); -+ free_state_path_names(&rmtab); - xlog (L_NOTICE, "Caught signal %d, un-registering and exiting.", sig); - exit(0); - } -@@ -656,7 +664,6 @@ get_exportlist(void) - int - main(int argc, char **argv) - { -- char *state_dir = NFS_STATEDIR; - char *progname; - char *s; - unsigned int listeners = 0; -@@ -684,8 +691,8 @@ main(int argc, char **argv) - ha_callout_prog = conf_get_str("mountd", "ha-callout"); - - s = conf_get_str("mountd", "state-directory-path"); -- if (s) -- state_dir = s; -+ if (s && !state_setup_basedir(argv[0], s)) -+ exit(1); - - /* NOTE: following uses "nfsd" section of nfs.conf !!!! */ - if (conf_get_bool("nfsd", "udp", NFSCTL_UDPISSET(_rpcprotobits))) -@@ -758,7 +765,8 @@ main(int argc, char **argv) - reverse_resolve = 1; - break; - case 's': -- state_dir = xstrdup(optarg); -+ if (!state_setup_basedir(argv[0], optarg)) -+ exit(1); - break; - case 't': - num_threads = atoi (optarg); -@@ -790,11 +798,10 @@ main(int argc, char **argv) - fprintf(stderr, "%s: No protocol versions specified!\n", progname); - usage(progname, 1); - } -- if (chdir(state_dir)) { -- fprintf(stderr, "%s: chdir(%s) failed: %s\n", -- progname, state_dir, strerror(errno)); -- exit(1); -- } -+ if (!setup_state_path_names(progname, ETAB, ETABTMP, ETABLCK, &etab)) -+ return 1; -+ if (!setup_state_path_names(progname, RMTAB, RMTABTMP, RMTABLCK, &rmtab)) -+ return 1; - - if (getrlimit (RLIMIT_NOFILE, &rlim) != 0) - fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n", -@@ -888,6 +895,8 @@ main(int argc, char **argv) - - xlog(L_ERROR, "RPC service loop terminated unexpectedly. Exiting...\n"); - unregister_services(); -+ free_state_path_names(&etab); -+ free_state_path_names(&rmtab); - exit(1); - } - -diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man -index 9f0a51f..9978afc 100644 ---- a/utils/mountd/mountd.man -+++ b/utils/mountd/mountd.man -@@ -144,7 +144,7 @@ Instead, mount the nfsd filesystem on - .IR /proc/fs/nfsd . - .TP - .BI "\-s," "" " \-\-state\-directory\-path " directory --Specify a directory in which to place statd state information. -+Specify a directory in which to place state information (etab and rmtab). - If this option is not specified the default of - .I /var/lib/nfs - is used. -diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c -index 527377f..3ae0dbb 100644 ---- a/utils/mountd/rmtab.c -+++ b/utils/mountd/rmtab.c -@@ -28,6 +28,8 @@ - - extern int reverse_resolve; - -+extern struct state_paths rmtab; -+ - /* If new path is a link do not destroy it but place the - * file where the link points. - */ -@@ -59,7 +61,7 @@ mountlist_add(char *host, const char *path) - int lockid; - long pos; - -- if ((lockid = xflock(_PATH_RMTABLCK, "a")) < 0) -+ if ((lockid = xflock(rmtab.lockfn, "a")) < 0) - return; - setrmtabent("r+"); - while ((rep = getrmtabent(1, &pos)) != NULL) { -@@ -99,13 +101,13 @@ mountlist_del(char *hname, const char *path) - int lockid; - int match; - -- if ((lockid = xflock(_PATH_RMTABLCK, "w")) < 0) -+ if ((lockid = xflock(rmtab.lockfn, "w")) < 0) - return; - if (!setrmtabent("r")) { - xfunlock(lockid); - return; - } -- if (!(fp = fsetrmtabent(_PATH_RMTABTMP, "w"))) { -+ if (!(fp = fsetrmtabent(rmtab.tmpfn, "w"))) { - endrmtabent(); - xfunlock(lockid); - return; -@@ -121,9 +123,9 @@ mountlist_del(char *hname, const char *path) - if (!match || rep->r_count) - fputrmtabent(fp, rep, NULL); - } -- if (slink_safe_rename(_PATH_RMTABTMP, _PATH_RMTAB) < 0) { -+ if (slink_safe_rename(rmtab.tmpfn, rmtab.statefn) < 0) { - xlog(L_ERROR, "couldn't rename %s to %s", -- _PATH_RMTABTMP, _PATH_RMTAB); -+ rmtab.tmpfn, rmtab.statefn); - } - endrmtabent(); /* close & unlink */ - fendrmtabent(fp); -@@ -138,7 +140,7 @@ mountlist_del_all(const struct sockaddr *sap) - FILE *fp; - int lockid; - -- if ((lockid = xflock(_PATH_RMTABLCK, "w")) < 0) -+ if ((lockid = xflock(rmtab.lockfn, "w")) < 0) - return; - hostname = host_canonname(sap); - if (hostname == NULL) { -@@ -151,7 +153,7 @@ mountlist_del_all(const struct sockaddr *sap) - if (!setrmtabent("r")) - goto out_free; - -- if (!(fp = fsetrmtabent(_PATH_RMTABTMP, "w"))) -+ if (!(fp = fsetrmtabent(rmtab.tmpfn, "w"))) - goto out_close; - - while ((rep = getrmtabent(1, NULL)) != NULL) { -@@ -160,9 +162,9 @@ mountlist_del_all(const struct sockaddr *sap) - continue; - fputrmtabent(fp, rep, NULL); - } -- if (slink_safe_rename(_PATH_RMTABTMP, _PATH_RMTAB) < 0) { -+ if (slink_safe_rename(rmtab.tmpfn, rmtab.statefn) < 0) { - xlog(L_ERROR, "couldn't rename %s to %s", -- _PATH_RMTABTMP, _PATH_RMTAB); -+ rmtab.tmpfn, rmtab.statefn); - } - fendrmtabent(fp); - out_close: -@@ -195,11 +197,11 @@ mountlist_list(void) - struct stat stb; - int lockid; - -- if ((lockid = xflock(_PATH_RMTABLCK, "r")) < 0) -+ if ((lockid = xflock(rmtab.lockfn, "r")) < 0) - return NULL; -- if (stat(_PATH_RMTAB, &stb) < 0) { -+ if (stat(rmtab.statefn, &stb) < 0) { - xlog(L_ERROR, "can't stat %s: %s", -- _PATH_RMTAB, strerror(errno)); -+ rmtab.statefn, strerror(errno)); - xfunlock(lockid); - return NULL; - } -diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am -index 152b680..ea32075 100644 ---- a/utils/statd/Makefile.am -+++ b/utils/statd/Makefile.am -@@ -18,6 +18,7 @@ statd_LDADD = ../../support/nsm/libnsm.a \ - $(LIBWRAP) $(LIBNSL) $(LIBCAP) $(LIBTIRPC) - sm_notify_LDADD = ../../support/nsm/libnsm.a \ - ../../support/nfs/libnfs.a \ -+ ../../support/misc/libmisc.a \ - $(LIBNSL) $(LIBCAP) $(LIBTIRPC) - - EXTRA_DIST = sim_sm_inter.x $(man8_MANS) simulate.c diff --git a/nfs-utils-2.1.2-rc3.patch b/nfs-utils-2.1.2-rc3.patch new file mode 100644 index 0000000..aec3cc4 --- /dev/null +++ b/nfs-utils-2.1.2-rc3.patch @@ -0,0 +1,2706 @@ +diff --git a/.gitignore b/.gitignore +index 126d12c..941aca0 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -70,6 +70,7 @@ tests/nsm_client/nlm_sm_inter_svc.c + tests/nsm_client/nlm_sm_inter_xdr.c + utils/nfsidmap/nfsidmap + systemd/nfs-server-generator ++systemd/rpc-pipefs-generator + systemd/nfs-config.service + systemd/rpc-gssd.service + # cscope database files +diff --git a/nfs.conf b/nfs.conf +index 81ece06..0d0ec9b 100644 +--- a/nfs.conf ++++ b/nfs.conf +@@ -1,7 +1,10 @@ + # +-# This is a general conifguration for the ++# This is a general configuration for the + # NFS daemons and tools + # ++#[general] ++# pipefs-directory=/var/lib/nfs/rpc_pipefs ++# + #[exportfs] + # debug=0 + # +@@ -12,7 +15,6 @@ + # limit-to-legacy-enctypes=0 + # context-timeout=0 + # rpc-timeout=5 +-# pipefs-directory=/var/lib/nfs/rpc_pipefs + # keytab-file=/etc/krb5.keytab + # cred-cache-directory= + # preferred-realm= +@@ -42,7 +44,7 @@ + # port=0 + # grace-time=90 + # lease-time=90 +-# udp=y ++# udp=n + # tcp=y + # vers2=n + # vers3=y +@@ -65,6 +67,7 @@ + # retry-time=900 + # outgoing-port= + # outgoing-addr= ++# lift-grace=y + # + #[svcgssd] + # principal= +diff --git a/support/export/xtab.c b/support/export/xtab.c +index 22cf539..d42eeef 100644 +--- a/support/export/xtab.c ++++ b/support/export/xtab.c +@@ -14,12 +14,20 @@ + #include + #include + #include ++#include ++#include ++#include ++#include + + #include "nfslib.h" + #include "exportfs.h" + #include "xio.h" + #include "xlog.h" + #include "v4root.h" ++#include "misc.h" ++ ++static char state_base_dirname[PATH_MAX] = NFS_STATEDIR; ++extern struct state_paths etab; + + int v4root_needed; + static void cond_rename(char *newfile, char *oldfile); +@@ -65,7 +73,7 @@ xtab_read(char *xtab, char *lockfn, int is_export) + int + xtab_export_read(void) + { +- return xtab_read(_PATH_ETAB, _PATH_ETABLCK, 1); ++ return xtab_read(etab.statefn, etab.lockfn, 1); + } + + /* +@@ -112,7 +120,7 @@ xtab_write(char *xtab, char *xtabtmp, char *lockfn, int is_export) + int + xtab_export_write() + { +- return xtab_write(_PATH_ETAB, _PATH_ETABTMP, _PATH_ETABLCK, 1); ++ return xtab_write(etab.statefn, etab.tmpfn, etab.lockfn, 1); + } + + /* +@@ -158,3 +166,74 @@ static void cond_rename(char *newfile, char *oldfile) + rename(newfile, oldfile); + return; + } ++ ++/* ++ * Returns a dynamically allocated, '\0'-terminated buffer ++ * containing an appropriate pathname, or NULL if an error ++ * occurs. Caller must free the returned result with free(3). ++ */ ++static char * ++state_make_pathname(const char *tabname) ++{ ++ return generic_make_pathname(state_base_dirname, tabname); ++} ++ ++/** ++ * state_setup_basedir - set up basedir ++ * @progname: C string containing name of program, for error messages ++ * @parentdir: C string containing pathname to on-disk state, or NULL ++ * ++ * This runs before logging is set up, so error messages are directed ++ * to stderr. ++ * ++ * Returns true and sets up our basedir, if @parentdir was valid ++ * and usable; otherwise false is returned. ++ */ ++_Bool ++state_setup_basedir(const char *progname, const char *parentdir) ++{ ++ return generic_setup_basedir(progname, parentdir, state_base_dirname, ++ PATH_MAX); ++} ++ ++int ++setup_state_path_names(const char *progname, const char *statefn, ++ const char *tmpfn, const char *lockfn, ++ struct state_paths *paths) ++{ ++ paths->statefn = state_make_pathname(statefn); ++ if (!paths->statefn) { ++ fprintf(stderr, "%s: state_make_pathname(%s) failed\n", ++ progname, statefn); ++ goto out_err; ++ } ++ paths->tmpfn = state_make_pathname(tmpfn); ++ if (!paths->tmpfn) { ++ fprintf(stderr, "%s: state_make_pathname(%s) failed\n", ++ progname, tmpfn); ++ goto out_free_statefn; ++ } ++ paths->lockfn = state_make_pathname(lockfn); ++ if (!paths->lockfn) { ++ fprintf(stderr, "%s: state_make_pathname(%s) failed\n", ++ progname, lockfn); ++ goto out_free_tmpfn; ++ } ++ return 1; ++ ++out_free_tmpfn: ++ free(paths->tmpfn); ++out_free_statefn: ++ free(paths->statefn); ++out_err: ++ return 0; ++ ++} ++ ++void ++free_state_path_names(struct state_paths *paths) ++{ ++ free(paths->statefn); ++ free(paths->tmpfn); ++ free(paths->lockfn); ++} +diff --git a/support/include/conffile.h b/support/include/conffile.h +index 3fe3a78..20b1a32 100644 +--- a/support/include/conffile.h ++++ b/support/include/conffile.h +@@ -48,8 +48,6 @@ struct conf_list { + TAILQ_HEAD(conf_list_fields_head, conf_list_node) fields; + }; + +-extern char *conf_path; +- + extern int conf_begin(void); + extern int conf_decode_base64(uint8_t *, uint32_t *, unsigned char *); + extern int conf_end(int, int); +@@ -61,9 +59,8 @@ extern int conf_get_num(char *, char *, int); + extern _Bool conf_get_bool(char *, char *, _Bool); + extern char *conf_get_str(char *, char *); + extern char *conf_get_section(char *, char *, char *); +-extern void conf_init(void); ++extern void conf_init(const char *); + extern int conf_match_num(char *, char *, int); +-extern void conf_reinit(void); + extern int conf_remove(int, char *, char *); + extern int conf_remove_section(int, char *); + extern void conf_report(void); +diff --git a/support/include/misc.h b/support/include/misc.h +index eedc1fe..06e2a0c 100644 +--- a/support/include/misc.h ++++ b/support/include/misc.h +@@ -15,6 +15,9 @@ + int randomkey(unsigned char *keyout, int len); + int weakrandomkey(unsigned char *keyout, int len); + ++char *generic_make_pathname(const char *, const char *); ++_Bool generic_setup_basedir(const char *, const char *, char *, const size_t); ++ + extern int is_mountpoint(char *path); + + /* size of the file pointer buffers for rpc procfs files */ +diff --git a/support/include/nfs/nfs.h b/support/include/nfs/nfs.h +index 15ecc6b..7933ff5 100644 +--- a/support/include/nfs/nfs.h ++++ b/support/include/nfs/nfs.h +@@ -16,8 +16,8 @@ + #define NFSD_MINVERS 2 + #define NFSD_MAXVERS 4 + +-#define NFS4_MINMINOR 1 +-#define NFS4_MAXMINOR WORD_BIT ++#define NFS4_MINMINOR 0 ++#define NFS4_MAXMINOR (WORD_BIT-1) + + struct nfs_fh_len { + int fh_size; +@@ -27,21 +27,24 @@ struct nfs_fh_len { + + #define NFSCTL_UDPBIT (1 << (17 - 1)) + #define NFSCTL_TCPBIT (1 << (18 - 1)) ++#define NFSCTL_PROTODEFAULT (NFSCTL_TCPBIT) + + #define NFSCTL_VERUNSET(_cltbits, _v) ((_cltbits) &= ~(1 << ((_v) - 1))) ++#define NFSCTL_MINORUNSET(_cltbits, _v) ((_cltbits) &= ~(1 << (_v))) + #define NFSCTL_UDPUNSET(_cltbits) ((_cltbits) &= ~NFSCTL_UDPBIT) + #define NFSCTL_TCPUNSET(_cltbits) ((_cltbits) &= ~NFSCTL_TCPBIT) + + #define NFSCTL_VERISSET(_cltbits, _v) ((_cltbits) & (1 << ((_v) - 1))) ++#define NFSCTL_MINORISSET(_cltbits, _v) ((_cltbits) & (1 << (_v))) + #define NFSCTL_UDPISSET(_cltbits) ((_cltbits) & NFSCTL_UDPBIT) + #define NFSCTL_TCPISSET(_cltbits) ((_cltbits) & NFSCTL_TCPBIT) + + #define NFSCTL_VERDEFAULT (0xc) /* versions 3 and 4 */ + #define NFSCTL_VERSET(_cltbits, _v) ((_cltbits) |= (1 << ((_v) - 1))) ++#define NFSCTL_MINORSET(_cltbits, _v) ((_cltbits) |= (1 << (_v))) + #define NFSCTL_UDPSET(_cltbits) ((_cltbits) |= NFSCTL_UDPBIT) + #define NFSCTL_TCPSET(_cltbits) ((_cltbits) |= NFSCTL_TCPBIT) + + #define NFSCTL_ANYPROTO(_cltbits) ((_cltbits) & (NFSCTL_UDPBIT | NFSCTL_TCPBIT)) +-#define NFSCTL_ALLBITS (~0) + + #endif /* _NFS_NFS_H */ +diff --git a/support/include/nfslib.h b/support/include/nfslib.h +index 1498977..ab8b2bf 100644 +--- a/support/include/nfslib.h ++++ b/support/include/nfslib.h +@@ -35,29 +35,24 @@ + #ifndef _PATH_IDMAPDCONF + #define _PATH_IDMAPDCONF "/etc/idmapd.conf" + #endif +-#ifndef _PATH_ETAB +-#define _PATH_ETAB NFS_STATEDIR "/etab" +-#endif +-#ifndef _PATH_ETABTMP +-#define _PATH_ETABTMP NFS_STATEDIR "/etab.tmp" +-#endif +-#ifndef _PATH_ETABLCK +-#define _PATH_ETABLCK NFS_STATEDIR "/.etab.lock" +-#endif +-#ifndef _PATH_RMTAB +-#define _PATH_RMTAB NFS_STATEDIR "/rmtab" +-#endif +-#ifndef _PATH_RMTABTMP +-#define _PATH_RMTABTMP _PATH_RMTAB ".tmp" +-#endif +-#ifndef _PATH_RMTABLCK +-#define _PATH_RMTABLCK NFS_STATEDIR "/.rmtab.lock" +-#endif + #ifndef _PATH_PROC_EXPORTS + #define _PATH_PROC_EXPORTS "/proc/fs/nfs/exports" + #define _PATH_PROC_EXPORTS_ALT "/proc/fs/nfsd/exports" + #endif + ++#define ETAB "etab" ++#define ETABTMP "etab.tmp" ++#define ETABLCK ".etab.lock" ++#define RMTAB "rmtab" ++#define RMTABTMP "rmtab.tmp" ++#define RMTABLCK ".rmtab.lock" ++ ++struct state_paths { ++ char *statefn; ++ char *tmpfn; ++ char *lockfn; ++}; ++ + /* Maximum number of security flavors on an export: */ + #define SECFLAVOR_COUNT 8 + +@@ -120,6 +115,10 @@ void fputrmtabent(FILE *fp, struct rmtabent *xep, long *pos); + void fendrmtabent(FILE *fp); + void frewindrmtabent(FILE *fp); + ++_Bool state_setup_basedir(const char *, const char *); ++int setup_state_path_names(const char *, const char *, const char *, const char *, struct state_paths *); ++void free_state_path_names(struct state_paths *); ++ + /* mydaemon */ + void daemon_init(bool fg); + void daemon_ready(void); +diff --git a/support/misc/Makefile.am b/support/misc/Makefile.am +index 1048580..8936b0d 100644 +--- a/support/misc/Makefile.am ++++ b/support/misc/Makefile.am +@@ -1,6 +1,6 @@ + ## Process this file with automake to produce Makefile.in + + noinst_LIBRARIES = libmisc.a +-libmisc_a_SOURCES = tcpwrapper.c from_local.c mountpoint.c ++libmisc_a_SOURCES = tcpwrapper.c from_local.c mountpoint.c file.c + + MAINTAINERCLEANFILES = Makefile.in +diff --git a/support/misc/file.c b/support/misc/file.c +new file mode 100644 +index 0000000..63597df +--- /dev/null ++++ b/support/misc/file.c +@@ -0,0 +1,110 @@ ++/* ++ * Copyright 2009 Oracle. All rights reserved. ++ * Copyright 2017 Red Hat, Inc. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with nfs-utils. If not, see . ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "xlog.h" ++#include "misc.h" ++ ++/* ++ * Returns a dynamically allocated, '\0'-terminated buffer ++ * containing an appropriate pathname, or NULL if an error ++ * occurs. Caller must free the returned result with free(3). ++ */ ++__attribute__((__malloc__)) ++char * ++generic_make_pathname(const char *base, const char *leaf) ++{ ++ size_t size; ++ char *path; ++ int len; ++ ++ size = strlen(base) + strlen(leaf) + 2; ++ if (size > PATH_MAX) ++ return NULL; ++ ++ path = malloc(size); ++ if (path == NULL) ++ return NULL; ++ ++ len = snprintf(path, size, "%s/%s", base, leaf); ++ if ((len < 0) || ((size_t)len >= size)) { ++ free(path); ++ return NULL; ++ } ++ ++ return path; ++} ++ ++ ++/** ++ * generic_setup_basedir - set up basedir ++ * @progname: C string containing name of program, for error messages ++ * @parentdir: C string containing pathname to on-disk state, or NULL ++ * @base: character buffer to contain the basedir that is set up ++ * @baselen: size of @base in bytes ++ * ++ * This runs before logging is set up, so error messages are directed ++ * to stderr. ++ * ++ * Returns true and sets up our basedir, if @parentdir was valid ++ * and usable; otherwise false is returned. ++ */ ++_Bool ++generic_setup_basedir(const char *progname, const char *parentdir, char *base, ++ const size_t baselen) ++{ ++ static char buf[PATH_MAX]; ++ struct stat st; ++ char *path; ++ ++ /* First: test length of name and whether it exists */ ++ if ((strlen(parentdir) >= baselen) || (strlen(parentdir) >= PATH_MAX)) { ++ (void)fprintf(stderr, "%s: Directory name too long: %s", ++ progname, parentdir); ++ return false; ++ } ++ if (lstat(parentdir, &st) == -1) { ++ (void)fprintf(stderr, "%s: Failed to stat %s: %s", ++ progname, parentdir, strerror(errno)); ++ return false; ++ } ++ ++ /* Ensure we have a clean directory pathname */ ++ strncpy(buf, parentdir, sizeof(buf)); ++ path = dirname(buf); ++ if (*path == '.') { ++ (void)fprintf(stderr, "%s: Unusable directory %s", ++ progname, parentdir); ++ return false; ++ } ++ ++ xlog(D_CALL, "Using %s as the state directory", parentdir); ++ strcpy(base, parentdir); ++ return true; ++} +diff --git a/support/nfs/cacheio.c b/support/nfs/cacheio.c +index e5e2579..9912afa 100644 +--- a/support/nfs/cacheio.c ++++ b/support/nfs/cacheio.c +@@ -27,6 +27,8 @@ + #include + #include + ++extern struct state_paths etab; ++ + void qword_add(char **bpp, int *lp, char *str) + { + char *bp = *bpp; +@@ -199,7 +201,7 @@ int qword_get_uint(char **bpp, unsigned int *anint) + } + + /* flush the kNFSd caches. +- * Set the flush time to the mtime of _PATH_ETAB or ++ * Set the flush time to the mtime of the etab state file or + * if force, to now. + * the caches to flush are: + * auth.unix.ip nfsd.export nfsd.fh +@@ -228,7 +230,7 @@ cache_flush(int force) + }; + now = time(0); + if (force || +- stat(_PATH_ETAB, &stb) != 0 || ++ stat(etab.statefn, &stb) != 0 || + stb.st_mtime > now) + stb.st_mtime = time(0); + +diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c +index e717c1e..efbdc8b 100644 +--- a/support/nfs/conffile.c ++++ b/support/nfs/conffile.c +@@ -30,6 +30,10 @@ + * This code was written under funding by Ericsson Radio Systems. + */ + ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ + #include + #include + #include +@@ -52,7 +56,7 @@ + #pragma GCC visibility push(hidden) + + static void conf_load_defaults(void); +-static int conf_load(int trans, char *path); ++static int conf_load(int trans, const char *path); + static int conf_set(int , char *, char *, char *, + char *, int , int ); + +@@ -73,8 +77,10 @@ TAILQ_HEAD (conf_trans_head, conf_trans) conf_trans_queue; + /* + * Radix-64 Encoding. + */ ++#if 0 + static const uint8_t bin2asc[] + = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; ++#endif + + static const uint8_t asc2bin[] = + { +@@ -105,7 +111,6 @@ struct conf_binding { + int is_default; + }; + +-char *conf_path; + LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256]; + + static __inline__ uint8_t +@@ -369,20 +374,8 @@ conf_load_defaults(void) + return; + } + +-void +-conf_init (void) +-{ +- unsigned int i; +- +- for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) +- LIST_INIT (&conf_bindings[i]); +- +- TAILQ_INIT (&conf_trans_queue); +- conf_reinit(); +-} +- + static int +-conf_load(int trans, char *path) ++conf_load(int trans, const char *path) + { + struct stat sb; + if ((stat (path, &sb) == 0) || (errno != ENOENT)) { +@@ -421,15 +414,15 @@ conf_load(int trans, char *path) + } + + /* Open the config file and map it into our address space, then parse it. */ +-void +-conf_reinit(void) ++static void ++conf_reinit(const char *conf_file) + { + struct conf_binding *cb = 0; + int trans; + unsigned int i; + + trans = conf_begin(); +- if (conf_load(trans, conf_path) < 0) ++ if (conf_load(trans, conf_file) < 0) + return; + + /* Load default configuration values. */ +@@ -446,6 +439,20 @@ conf_reinit(void) + return; + } + ++void ++conf_init (const char *conf_file) ++{ ++ unsigned int i; ++ ++ 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; ++ conf_reinit(conf_file); ++} ++ + /* + * Return the numeric value denoted by TAG in section SECTION or DEF + * if that tag does not exist. +@@ -533,7 +540,7 @@ retry: + * or from environment + */ + char *env = getenv(cb->value+1); +- if (env) ++ if (env && *env) + return env; + section = "environment"; + tag = cb->value + 1; +diff --git a/support/nfs/rmtab.c b/support/nfs/rmtab.c +index 59dfbdf..2ecb2cc 100644 +--- a/support/nfs/rmtab.c ++++ b/support/nfs/rmtab.c +@@ -33,12 +33,14 @@ + + static FILE *rmfp = NULL; + ++extern struct state_paths rmtab; ++ + int + setrmtabent(char *type) + { + if (rmfp) + fclose(rmfp); +- rmfp = fsetrmtabent(_PATH_RMTAB, type); ++ rmfp = fsetrmtabent(rmtab.statefn, type); + return (rmfp != NULL); + } + +diff --git a/support/nsm/file.c b/support/nsm/file.c +index aafa755..52f5401 100644 +--- a/support/nsm/file.c ++++ b/support/nsm/file.c +@@ -88,6 +88,7 @@ + + #include "xlog.h" + #include "nsm.h" ++#include "misc.h" + + #define RPCARGSLEN (4 * (8 + 1)) + #define LINELEN (RPCARGSLEN + SM_PRIV_SIZE * 2 + 1) +@@ -170,25 +171,7 @@ __attribute__((__malloc__)) + static char * + nsm_make_pathname(const char *directory) + { +- size_t size; +- char *path; +- int len; +- +- size = strlen(nsm_base_dirname) + strlen(directory) + 2; +- if (size > PATH_MAX) +- return NULL; +- +- path = malloc(size); +- if (path == NULL) +- return NULL; +- +- len = snprintf(path, size, "%s/%s", nsm_base_dirname, directory); +- if (error_check(len, size)) { +- free(path); +- return NULL; +- } +- +- return path; ++ return generic_make_pathname(nsm_base_dirname, directory); + } + + /* +@@ -293,29 +276,8 @@ out: + _Bool + nsm_setup_pathnames(const char *progname, const char *parentdir) + { +- static char buf[PATH_MAX]; +- struct stat st; +- char *path; +- +- /* First: test length of name and whether it exists */ +- if (lstat(parentdir, &st) == -1) { +- (void)fprintf(stderr, "%s: Failed to stat %s: %s", +- progname, parentdir, strerror(errno)); +- return false; +- } +- +- /* Ensure we have a clean directory pathname */ +- strncpy(buf, parentdir, sizeof(buf)); +- path = dirname(buf); +- if (*path == '.') { +- (void)fprintf(stderr, "%s: Unusable directory %s", +- progname, parentdir); +- return false; +- } +- +- xlog(D_CALL, "Using %s as the state directory", parentdir); +- strncpy(nsm_base_dirname, parentdir, sizeof(nsm_base_dirname)); +- return true; ++ return generic_setup_basedir(progname, parentdir, nsm_base_dirname, ++ PATH_MAX); + } + + /** +diff --git a/systemd/Makefile.am b/systemd/Makefile.am +index 0d15b9f..eef53c4 100644 +--- a/systemd/Makefile.am ++++ b/systemd/Makefile.am +@@ -4,6 +4,7 @@ MAINTAINERCLEANFILES = Makefile.in + + unit_files = \ + nfs-client.target \ ++ rpc_pipefs.target \ + \ + nfs-mountd.service \ + nfs-server.service \ +@@ -42,14 +43,23 @@ EXTRA_DIST = $(unit_files) $(man5_MANS) $(man7_MANS) + unit_dir = /usr/lib/systemd/system + generator_dir = /usr/lib/systemd/system-generators + +-EXTRA_PROGRAMS = nfs-server-generator ++EXTRA_PROGRAMS = nfs-server-generator rpc-pipefs-generator + genexecdir = $(generator_dir) ++ ++COMMON_SRCS = systemd.c systemd.h ++ ++nfs_server_generator_SOURCES = $(COMMON_SRCS) nfs-server-generator.c ++ ++rpc_pipefs_generator_SOURCES = $(COMMON_SRCS) rpc-pipefs-generator.c ++ + nfs_server_generator_LDADD = ../support/export/libexport.a \ + ../support/nfs/libnfs.a \ + ../support/misc/libmisc.a + ++rpc_pipefs_generator_LDADD = ../support/nfs/libnfs.a ++ + if INSTALL_SYSTEMD +-genexec_PROGRAMS = nfs-server-generator ++genexec_PROGRAMS = nfs-server-generator rpc-pipefs-generator + install-data-hook: $(unit_files) + mkdir -p $(DESTDIR)/$(unitdir) + cp $(unit_files) $(DESTDIR)/$(unitdir) +diff --git a/systemd/nfs-blkmap.service b/systemd/nfs-blkmap.service +index ddc324e..2bbcee6 100644 +--- a/systemd/nfs-blkmap.service ++++ b/systemd/nfs-blkmap.service +@@ -2,8 +2,8 @@ + Description=pNFS block layout mapping daemon + DefaultDependencies=no + Conflicts=umount.target +-After=var-lib-nfs-rpc_pipefs.mount +-Requires=var-lib-nfs-rpc_pipefs.mount ++After=rpc_pipefs.target ++Requires=rpc_pipefs.target + + PartOf=nfs-utils.service + +diff --git a/systemd/nfs-idmapd.service b/systemd/nfs-idmapd.service +index acca86b..f38fe52 100644 +--- a/systemd/nfs-idmapd.service ++++ b/systemd/nfs-idmapd.service +@@ -1,8 +1,8 @@ + [Unit] + Description=NFSv4 ID-name mapping service + DefaultDependencies=no +-Requires=var-lib-nfs-rpc_pipefs.mount +-After=var-lib-nfs-rpc_pipefs.mount local-fs.target ++Requires=rpc_pipefs.target ++After=rpc_pipefs.target local-fs.target + + BindsTo=nfs-server.service + +diff --git a/systemd/nfs-mountd.service b/systemd/nfs-mountd.service +index 15e828b..e8ece53 100644 +--- a/systemd/nfs-mountd.service ++++ b/systemd/nfs-mountd.service +@@ -2,8 +2,10 @@ + Description=NFS Mount Daemon + DefaultDependencies=no + Requires=proc-fs-nfsd.mount ++Wants=network-online.target + After=proc-fs-nfsd.mount +-After=network.target local-fs.target ++After=network-online.target local-fs.target ++After=rpcbind.socket + BindsTo=nfs-server.service + + [Service] +diff --git a/systemd/nfs-server-generator.c b/systemd/nfs-server-generator.c +index cc99969..737f109 100644 +--- a/systemd/nfs-server-generator.c ++++ b/systemd/nfs-server-generator.c +@@ -29,6 +29,7 @@ + #include "misc.h" + #include "nfslib.h" + #include "exportfs.h" ++#include "systemd.h" + + /* A simple "set of strings" to remove duplicates + * found in /etc/exports +@@ -55,38 +56,31 @@ static int is_unique(struct list **lp, char *path) + return 1; + } + +-/* We need to convert a path name to a systemd unit +- * name. This requires some translation ('/' -> '-') +- * and some escaping. +- */ +-static void systemd_escape(FILE *f, char *path) ++static int has_noauto_flag(char *path) + { +- while (*path == '/') +- path++; +- if (!*path) { +- /* "/" becomes "-", otherwise leading "/" is ignored */ +- fputs("-", f); +- return; +- } +- while (*path) { +- char c = *path++; +- +- if (c == '/') { +- /* multiple non-trailing slashes become '-' */ +- while (*path == '/') +- path++; +- if (*path) +- fputs("-", f); +- } else if (isalnum(c) || c == ':' || c == '.') +- fputc(c, f); +- else +- fprintf(f, "\\x%02x", c & 0xff); ++ FILE *fstab; ++ struct mntent *mnt; ++ ++ fstab = setmntent("/etc/fstab", "r"); ++ if (!fstab) ++ return 0; ++ ++ while ((mnt = getmntent(fstab)) != NULL) { ++ int l = strlen(mnt->mnt_dir); ++ if (strncmp(mnt->mnt_dir, path, l) != 0) ++ continue; ++ if (path[l] && path[l] != '/') ++ continue; ++ if (hasmntopt(mnt, "noauto")) ++ break; + } ++ fclose(fstab); ++ return mnt != NULL; + } + + int main(int argc, char *argv[]) + { +- char *path; ++ char *path, *spath; + char dirbase[] = "/nfs-server.service.d"; + char filebase[] = "/order-with-mounts.conf"; + nfs_export *exp; +@@ -124,6 +118,10 @@ int main(int argc, char *argv[]) + for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { + if (!is_unique(&list, exp->m_export.e_path)) + continue; ++ if (exp->m_export.e_mountpoint) ++ continue; ++ if (has_noauto_flag(exp->m_export.e_path)) ++ continue; + if (strchr(exp->m_export.e_path, ' ')) + fprintf(f, "RequiresMountsFor=\"%s\"\n", + exp->m_export.e_path); +@@ -141,9 +139,15 @@ int main(int argc, char *argv[]) + if (strcmp(mnt->mnt_type, "nfs") != 0 && + strcmp(mnt->mnt_type, "nfs4") != 0) + continue; +- fprintf(f, "Before= "); +- systemd_escape(f, mnt->mnt_dir); +- fprintf(f, ".mount\n"); ++ ++ spath = systemd_escape(mnt->mnt_dir, ".mount"); ++ if (!spath) { ++ fprintf(stderr, ++ "nfs-server-generator: convert path failed: %s\n", ++ mnt->mnt_dir); ++ continue; ++ } ++ fprintf(f, "Before=%s\n", spath); + } + + fclose(fstab); +diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service +index 5be5de6..136552b 100644 +--- a/systemd/nfs-server.service ++++ b/systemd/nfs-server.service +@@ -3,12 +3,12 @@ Description=NFS server and services + DefaultDependencies=no + Requires= network.target proc-fs-nfsd.mount + Requires= nfs-mountd.service +-Wants=rpcbind.socket ++Wants=rpcbind.socket network-online.target + Wants=rpc-statd.service nfs-idmapd.service + Wants=rpc-statd-notify.service + +-After= local-fs.target +-After= network.target proc-fs-nfsd.mount rpcbind.socket nfs-mountd.service ++After= network-online.target local-fs.target ++After= proc-fs-nfsd.mount rpcbind.socket nfs-mountd.service + After= nfs-idmapd.service rpc-statd.service + Before= rpc-statd-notify.service + +diff --git a/systemd/nfs.conf.man b/systemd/nfs.conf.man +index 91c49a0..189b052 100644 +--- a/systemd/nfs.conf.man ++++ b/systemd/nfs.conf.man +@@ -96,6 +96,18 @@ value, which can be one or more from the list + .BR all . + When a list is given, the members should be comma-separated. + .TP ++.B general ++Recognized values: ++.BR pipefs-directory . ++ ++See ++.BR blkmapd (8), ++.BR rpc.idmapd (8), ++and ++.BR rpc.gssd (8) ++for details. ++ ++.TP + .B nfsdcltrack + Recognized values: + .BR storagedir . +@@ -154,6 +166,13 @@ section, are used to configure mountd. See + .BR rpc.mountd (8) + for details. + ++The ++.B state-directory-path ++value in the ++.B [mountd] ++section is also used by ++.BR exportfs (8). ++ + .TP + .B statd + Recognized values: +@@ -198,7 +217,6 @@ Recognized values: + .BR limit-to-legacy-enctypes , + .BR context-timeout , + .BR rpc-timeout , +-.BR pipefs-directory , + .BR keytab-file , + .BR cred-cache-directory , + .BR preferred-realm . +diff --git a/systemd/rpc-gssd.service.in b/systemd/rpc-gssd.service.in +index b353027..6807db3 100644 +--- a/systemd/rpc-gssd.service.in ++++ b/systemd/rpc-gssd.service.in +@@ -2,8 +2,8 @@ + Description=RPC security service for NFS client and server + DefaultDependencies=no + Conflicts=umount.target +-Requires=var-lib-nfs-rpc_pipefs.mount +-After=var-lib-nfs-rpc_pipefs.mount ++Requires=rpc_pipefs.target ++After=rpc_pipefs.target + + ConditionPathExists=@_sysconfdir@/krb5.keytab + +diff --git a/systemd/rpc-pipefs-generator.c b/systemd/rpc-pipefs-generator.c +new file mode 100644 +index 0000000..59eee87 +--- /dev/null ++++ b/systemd/rpc-pipefs-generator.c +@@ -0,0 +1,137 @@ ++/* ++ * rpc-pipefs-generator: ++ * systemd generator to create ordering dependencies between ++ * nfs services and the rpc_pipefs mountpoint ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "nfslib.h" ++#include "conffile.h" ++#include "systemd.h" ++ ++#define RPC_PIPEFS_DEFAULT "/var/lib/nfs/rpc_pipefs" ++ ++static int generate_mount_unit(const char *pipefs_path, const char *pipefs_unit, ++ const char *dirname) ++{ ++ char *path; ++ FILE *f; ++ ++ path = malloc(strlen(dirname) + 1 + strlen(pipefs_unit)); ++ if (!path) ++ return 1; ++ sprintf(path, "%s/%s", dirname, pipefs_unit); ++ f = fopen(path, "w"); ++ if (!f) ++ return 1; ++ ++ fprintf(f, "# Automatically generated by rpc-pipefs-generator\n\n[Unit]\n"); ++ fprintf(f, "Description=RPC Pipe File System\n"); ++ fprintf(f, "DefaultDependencies=no\n"); ++ fprintf(f, "After=systemd-tmpfiles-setup.service\n"); ++ fprintf(f, "Conflicts=umount.target\n"); ++ fprintf(f, "\n[Mount]\n"); ++ fprintf(f, "What=sunrpc\n"); ++ fprintf(f, "Where=%s\n", pipefs_path); ++ fprintf(f, "Type=rpc_pipefs\n"); ++ ++ fclose(f); ++ return 0; ++} ++ ++static ++int generate_target(char *pipefs_path, const char *dirname) ++{ ++ char *path; ++ char filebase[] = "/rpc_pipefs.target"; ++ char *pipefs_unit; ++ FILE *f; ++ int ret = 0; ++ ++ pipefs_unit = systemd_escape(pipefs_path, ".mount"); ++ if (!pipefs_unit) ++ return 1; ++ ++ ret = generate_mount_unit(pipefs_path, pipefs_unit, dirname); ++ if (ret) ++ return ret; ++ ++ path = malloc(strlen(dirname) + 1 + sizeof(filebase)); ++ if (!path) ++ return 2; ++ sprintf(path, "%s", dirname); ++ mkdir(path, 0755); ++ strcat(path, filebase); ++ f = fopen(path, "w"); ++ if (!f) ++ return 1; ++ ++ fprintf(f, "# Automatically generated by rpc-pipefs-generator\n\n[Unit]\n"); ++ fprintf(f, "Requires=%s\n", pipefs_unit); ++ fprintf(f, "After=%s\n", pipefs_unit); ++ fclose(f); ++ ++ return 0; ++} ++ ++static int is_non_pipefs_mountpoint(char *path) ++{ ++ FILE *mtab; ++ struct mntent *mnt; ++ ++ mtab = setmntent("/etc/mtab", "r"); ++ if (!mtab) ++ return 0; ++ ++ while ((mnt = getmntent(mtab)) != NULL) { ++ if (strlen(mnt->mnt_dir) != strlen(path)) ++ continue; ++ if (strncmp(mnt->mnt_dir, path, strlen(mnt->mnt_dir))) ++ continue; ++ if (strncmp(mnt->mnt_type, "rpc_pipefs", strlen(mnt->mnt_type))) ++ break; ++ } ++ fclose(mtab); ++ return mnt != NULL; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int ret; ++ char *s; ++ ++ /* Avoid using any external services */ ++ xlog_syslog(0); ++ ++ if (argc != 4 || argv[1][0] != '/') { ++ fprintf(stderr, "rpc-pipefs-generator: create systemd dependencies for nfs services\n"); ++ fprintf(stderr, "Usage: normal-dir early-dir late-dir\n"); ++ exit(1); ++ } ++ ++ conf_init(NFS_CONFFILE); ++ s = conf_get_str("general", "pipefs-directory"); ++ if (!s) ++ exit(0); ++ if (strlen(s) == strlen(RPC_PIPEFS_DEFAULT) && ++ strcmp(s, RPC_PIPEFS_DEFAULT) == 0) ++ exit(0); ++ ++ if (is_non_pipefs_mountpoint(s)) ++ exit(1); ++ ++ ret = generate_target(s, argv[1]); ++ exit(ret); ++} +diff --git a/systemd/rpc-statd-notify.service b/systemd/rpc-statd-notify.service +index 7bfc9b1..687fe31 100644 +--- a/systemd/rpc-statd-notify.service ++++ b/systemd/rpc-statd-notify.service +@@ -1,8 +1,8 @@ + [Unit] + Description=Notify NFS peers of a restart + DefaultDependencies=no +-Requires=network.target +-After=local-fs.target network.target nss-lookup.target ++Wants=network-online.target ++After=local-fs.target network-online.target nss-lookup.target + + # if we run an nfs server, it needs to be running before we + # tell clients that it has restarted. +diff --git a/systemd/rpc-statd.service b/systemd/rpc-statd.service +index 60d600f..f41ae20 100644 +--- a/systemd/rpc-statd.service ++++ b/systemd/rpc-statd.service +@@ -3,7 +3,8 @@ Description=NFS status monitor for NFSv2/3 locking. + DefaultDependencies=no + Conflicts=umount.target + Requires=nss-lookup.target rpcbind.socket +-After=network.target nss-lookup.target rpcbind.socket ++Wants=network-online.target ++After=network-online.target nss-lookup.target rpcbind.socket + + PartOf=nfs-utils.service + +diff --git a/systemd/rpc-svcgssd.service b/systemd/rpc-svcgssd.service +index 7187e3c..cb2bcd4 100644 +--- a/systemd/rpc-svcgssd.service ++++ b/systemd/rpc-svcgssd.service +@@ -1,8 +1,7 @@ + [Unit] + Description=RPC security service for NFS server + DefaultDependencies=no +-Requires=var-lib-nfs-rpc_pipefs.mount +-After=var-lib-nfs-rpc_pipefs.mount local-fs.target ++After=local-fs.target + PartOf=nfs-server.service + PartOf=nfs-utils.service + +diff --git a/systemd/rpc_pipefs.target b/systemd/rpc_pipefs.target +new file mode 100644 +index 0000000..01d4d27 +--- /dev/null ++++ b/systemd/rpc_pipefs.target +@@ -0,0 +1,3 @@ ++[Unit] ++Requires=var-lib-nfs-rpc_pipefs.mount ++After=var-lib-nfs-rpc_pipefs.mount +diff --git a/systemd/systemd.c b/systemd/systemd.c +new file mode 100644 +index 0000000..17820d4 +--- /dev/null ++++ b/systemd/systemd.c +@@ -0,0 +1,133 @@ ++/* ++ * Helper functions for systemd generators in nfs-utils. ++ * ++ * Currently just systemd_escape(). ++ */ ++ ++#include ++#include ++#include ++#include ++ ++static const char hex[16] = ++{ ++ '0', '1', '2', '3', '4', '5', '6', '7', ++ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', ++}; ++ ++/* ++ * determine length of the string that systemd_escape() needs to allocate ++ */ ++static int systemd_len(char *path) ++{ ++ char *p; ++ int len = 0; ++ ++ p = path; ++ while (*p == '/') ++ /* multiple leading "/" are ignored */ ++ p++; ++ ++ if (!*p) ++ /* root directory "/" becomes is encoded as a single "-" */ ++ return 1; ++ ++ if (*p == '.') ++ /* ++ * replace "." with "\x2d" escape sequence if ++ * it's the first character in escaped path ++ * */ ++ len += 4; ++ ++ while (*p) { ++ unsigned char c = *p++; ++ ++ if (c == '/') { ++ /* multiple non-trailing slashes become '-' */ ++ while (*p == '/') ++ p++; ++ if (*p) ++ len++; ++ } else if (isalnum(c) || c == ':' || c == '.' || c == '_') ++ /* these characters are not replaced */ ++ len++; ++ else ++ /* replace with "\x2d" escape sequence */ ++ len += 4; ++ } ++ ++ return len; ++} ++ ++/* ++ * convert c to "\x2d" escape sequence and append to string ++ * at position p, advancing p ++ */ ++static char *hexify(unsigned char c, char *p) ++{ ++ *p++ = '\\'; ++ *p++ = 'x'; ++ *p++ = hex[c >> 4]; ++ *p++ = hex[c & 0xf]; ++ return p; ++} ++ ++/* ++ * convert a path to a unit name according to the logic in systemd.unit(5): ++ * ++ * Basically, given a path, "/" is replaced by "-", and all other ++ * characters which are not ASCII alphanumerics are replaced by C-style ++ * "\x2d" escapes (except that "_" is never replaced and "." is only ++ * replaced when it would be the first character in the escaped path). ++ * The root directory "/" is encoded as single dash, while otherwise the ++ * initial and ending "/" are removed from all paths during ++ * transformation. ++ * ++ * NB: Although the systemd.unit(5) doesn't mention it, the ':' character ++ * is not escaped. ++ */ ++char *systemd_escape(char *path, char *suffix) ++{ ++ char *result; ++ char *p; ++ int len; ++ ++ len = systemd_len(path); ++ result = malloc(len + strlen(suffix) + 1); ++ p = result; ++ while (*path == '/') ++ /* multiple leading "/" are ignored */ ++ path++; ++ if (!*path) { ++ /* root directory "/" becomes is encoded as a single "-" */ ++ *p++ = '-'; ++ goto out; ++ } ++ if (*path == '.') ++ /* ++ * replace "." with "\x2d" escape sequence if ++ * it's the first character in escaped path ++ * */ ++ p = hexify(*path++, p); ++ ++ while (*path) { ++ unsigned char c = *path++; ++ ++ if (c == '/') { ++ /* multiple non-trailing slashes become '-' */ ++ while (*path == '/') ++ path++; ++ if (*path) ++ *p++ = '-'; ++ } else if (isalnum(c) || c == ':' || c == '.' || c == '_') ++ /* these characters are not replaced */ ++ *p++ = c; ++ else ++ /* replace with "\x2d" escape sequence */ ++ p = hexify(c, p); ++ } ++ ++out: ++ sprintf(p, "%s", suffix); ++ return result; ++} +diff --git a/systemd/systemd.h b/systemd/systemd.h +new file mode 100644 +index 0000000..25235ec +--- /dev/null ++++ b/systemd/systemd.h +@@ -0,0 +1,6 @@ ++#ifndef SYSTEMD_H ++#define SYSTEMD_H ++ ++char *systemd_escape(char *path, char *suffix); ++ ++#endif /* SYSTEMD_H */ +diff --git a/utils/blkmapd/blkmapd.man b/utils/blkmapd/blkmapd.man +index 914b80f..4b3d3f0 100644 +--- a/utils/blkmapd/blkmapd.man ++++ b/utils/blkmapd/blkmapd.man +@@ -43,9 +43,24 @@ Performs device discovery only then exits. + Runs + .B blkmapd + in the foreground and sends output to stderr (as opposed to syslogd) ++.SH CONFIGURATION FILE ++The ++.B blkmapd ++daemon recognizes the following value from the ++.B [general] ++section of the ++.I /etc/nfs.conf ++configuration file: ++.TP ++.B pipefs-directory ++Tells ++.B blkmapd ++where to look for the rpc_pipefs filesystem. The default value is ++.IR /var/lib/nfs/rpc_pipefs . + .SH SEE ALSO + .BR nfs (5), +-.BR dmsetup (8) ++.BR dmsetup (8), ++.BR nfs.conf (5) + .sp + RFC 5661 for the NFS version 4.1 specification. + .br +diff --git a/utils/blkmapd/device-discovery.c b/utils/blkmapd/device-discovery.c +index 8eb3fd0..c66669d 100644 +--- a/utils/blkmapd/device-discovery.c ++++ b/utils/blkmapd/device-discovery.c +@@ -50,21 +50,36 @@ + #include + #include + ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif /* HAVE_CONFIG_H */ ++ + #include "device-discovery.h" + #include "xcommon.h" ++#include "nfslib.h" ++#include "conffile.h" + + #define EVENT_SIZE (sizeof(struct inotify_event)) + #define EVENT_BUFSIZE (1024 * EVENT_SIZE) + +-#define BL_PIPE_FILE "/var/lib/nfs/rpc_pipefs/nfs/blocklayout" +-#define NFSPIPE_DIR "/var/lib/nfs/rpc_pipefs/nfs" + #define RPCPIPE_DIR "/var/lib/nfs/rpc_pipefs" + #define PID_FILE "/var/run/blkmapd.pid" + ++#define CONF_SAVE(w, f) do { \ ++ char *p = f; \ ++ if (p != NULL) \ ++ (w) = p; \ ++} while (0) ++ ++static char bl_pipe_file[PATH_MAX]; ++static char nfspipe_dir[PATH_MAX]; ++static char rpcpipe_dir[PATH_MAX]; ++ + struct bl_disk *visible_disk_list; + int bl_watch_fd, bl_pipe_fd, nfs_pipedir_wfd, rpc_pipedir_wfd; + int pidfd = -1; + ++ + struct bl_disk_path *bl_get_path(const char *filepath, + struct bl_disk_path *paths) + { +@@ -358,8 +373,8 @@ static void bl_rpcpipe_cb(void) + continue; + if (event->mask & IN_CREATE) { + BL_LOG_WARNING("nfs pipe dir created\n"); +- bl_watch_dir(NFSPIPE_DIR, &nfs_pipedir_wfd); +- bl_pipe_fd = open(BL_PIPE_FILE, O_RDWR); ++ bl_watch_dir(nfspipe_dir, &nfs_pipedir_wfd); ++ bl_pipe_fd = open(bl_pipe_file, O_RDWR); + } else if (event->mask & IN_DELETE) { + BL_LOG_WARNING("nfs pipe dir deleted\n"); + inotify_rm_watch(bl_watch_fd, nfs_pipedir_wfd); +@@ -372,7 +387,7 @@ static void bl_rpcpipe_cb(void) + continue; + if (event->mask & IN_CREATE) { + BL_LOG_WARNING("blocklayout pipe file created\n"); +- bl_pipe_fd = open(BL_PIPE_FILE, O_RDWR); ++ bl_pipe_fd = open(bl_pipe_file, O_RDWR); + if (bl_pipe_fd < 0) + BL_LOG_ERR("open %s failed: %s\n", + event->name, strerror(errno)); +@@ -437,6 +452,18 @@ int main(int argc, char **argv) + { + int opt, dflag = 0, fg = 0, ret = 1; + char pidbuf[64]; ++ char *xrpcpipe_dir = NULL; ++ ++ strncpy(rpcpipe_dir, RPCPIPE_DIR, sizeof(rpcpipe_dir)); ++ conf_init(NFS_CONFFILE); ++ CONF_SAVE(xrpcpipe_dir, conf_get_str("general", "pipefs-directory")); ++ if (xrpcpipe_dir != NULL) ++ strlcpy(rpcpipe_dir, xrpcpipe_dir, sizeof(rpcpipe_dir)); ++ ++ strncpy(nfspipe_dir, rpcpipe_dir, sizeof(nfspipe_dir)); ++ strlcat(nfspipe_dir, "/nfs", sizeof(nfspipe_dir)); ++ strncpy(bl_pipe_file, rpcpipe_dir, sizeof(bl_pipe_file)); ++ strlcat(bl_pipe_file, "/nfs/blocklayout", sizeof(bl_pipe_file)); + + while ((opt = getopt(argc, argv, "hdf")) != -1) { + switch (opt) { +@@ -496,12 +523,12 @@ int main(int argc, char **argv) + } + + /* open pipe file */ +- bl_watch_dir(RPCPIPE_DIR, &rpc_pipedir_wfd); +- bl_watch_dir(NFSPIPE_DIR, &nfs_pipedir_wfd); ++ bl_watch_dir(rpcpipe_dir, &rpc_pipedir_wfd); ++ bl_watch_dir(nfspipe_dir, &nfs_pipedir_wfd); + +- bl_pipe_fd = open(BL_PIPE_FILE, O_RDWR); ++ bl_pipe_fd = open(bl_pipe_file, O_RDWR); + if (bl_pipe_fd < 0) +- BL_LOG_ERR("open pipe file %s failed: %s\n", BL_PIPE_FILE, strerror(errno)); ++ BL_LOG_ERR("open pipe file %s failed: %s\n", bl_pipe_file, strerror(errno)); + + while (1) { + /* discover device when needed */ +diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c +index 61dddfb..beed1b3 100644 +--- a/utils/exportfs/exportfs.c ++++ b/utils/exportfs/exportfs.c +@@ -50,7 +50,8 @@ static void release_lockfile(void); + + static const char *lockfile = EXP_LOCKFILE; + static int _lockfd = -1; +-char *conf_path = NFS_CONFFILE; ++ ++struct state_paths etab; + + /* + * If we aren't careful, changes made by exportfs can be lost +@@ -95,6 +96,7 @@ main(int argc, char **argv) + int f_ignore = 0; + int i, c; + int force_flush = 0; ++ char *s; + + if ((progname = strrchr(argv[0], '/')) != NULL) + progname++; +@@ -105,9 +107,14 @@ main(int argc, char **argv) + xlog_stderr(1); + xlog_syslog(0); + +- conf_init(); ++ conf_init(NFS_CONFFILE); + xlog_from_conffile("exportfs"); + ++ /* NOTE: following uses "mountd" section of nfs.conf !!!! */ ++ s = conf_get_str("mountd", "state-directory-path"); ++ if (s && !state_setup_basedir(argv[0], s)) ++ exit(1); ++ + while ((c = getopt(argc, argv, "ad:fhio:ruvs")) != EOF) { + switch(c) { + case 'a': +@@ -159,13 +166,17 @@ main(int argc, char **argv) + xlog(L_ERROR, "-r and -u are incompatible"); + return 1; + } ++ if (!setup_state_path_names(progname, ETAB, ETABTMP, ETABLCK, &etab)) ++ return 1; + if (optind == argc && ! f_all) { + if (force_flush) { + cache_flush(1); ++ free_state_path_names(&etab); + return 0; + } else { + xtab_export_read(); + dump(f_verbose, f_export_format); ++ free_state_path_names(&etab); + return 0; + } + } +@@ -206,6 +217,7 @@ main(int argc, char **argv) + } + xtab_export_write(); + cache_flush(force_flush); ++ free_state_path_names(&etab); + + return export_errno; + } +diff --git a/utils/exportfs/exportfs.man b/utils/exportfs/exportfs.man +index 45b6d83..91d3589 100644 +--- a/utils/exportfs/exportfs.man ++++ b/utils/exportfs/exportfs.man +@@ -148,6 +148,29 @@ options. + .TP + .B -s + Display the current export list suitable for /etc/exports. ++ ++.SH CONFIGURATION FILE ++The ++.B [exportfs] ++section of the ++.I /etc/nfs.conf ++configuration file can contain a ++.B debug ++value, which can be one or more from the list ++.BR general , ++.BR call , ++.BR auth , ++.BR parse , ++.BR all . ++When a list is given, the members should be comma-separated. ++ ++.B exportfs ++will also recognize the ++.B state-directory-path ++value from the ++.B [mountd] ++section. ++ + .SH DISCUSSION + .SS Exporting Directories + The first synopsis shows how to invoke +diff --git a/utils/exportfs/nfsd.man b/utils/exportfs/nfsd.man +index 0c516fa..9efa29f 100644 +--- a/utils/exportfs/nfsd.man ++++ b/utils/exportfs/nfsd.man +@@ -105,11 +105,6 @@ clients have for different filesystems. + The caches are: + + .TP +-.B auth.domain +-This cache maps the name of a client (or domain) to an internal data +-structure. The only access that is possible is to flush the cache. +- +-.TP + .B auth.unix.ip + This cache contains a mapping from IP address to the name of the + authentication domain that the ipaddress should be treated as part of. +@@ -133,7 +128,8 @@ are: + .B flush + When a number of seconds since epoch (1 Jan 1970) is written to this + file, all entries in the cache that were last updated before that file +-become invalidated and will be flushed out. Writing 1 will flush ++become invalidated and will be flushed out. Writing a time in the ++future (in seconds since epoch) will flush + everything. This is the only file that will always be present. + + .TP +diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c +index 4d18d35..053a223 100644 +--- a/utils/gssd/gssd.c ++++ b/utils/gssd/gssd.c +@@ -79,7 +79,6 @@ static int pipefs_fd; + static int inotify_fd; + struct event inotify_ev; + +-char *conf_path = NFS_CONFFILE; + char *keytabfile = GSSD_DEFAULT_KEYTAB_FILE; + char **ccachesearch; + int use_memcache = 0; +@@ -87,6 +86,7 @@ int root_uses_machine_creds = 1; + unsigned int context_timeout = 0; + unsigned int rpc_timeout = 5; + char *preferred_realm = NULL; ++char *ccachedir = NULL; + /* Avoid DNS reverse lookups on server names */ + static bool avoid_dns = true; + int thread_started = false; +@@ -837,21 +837,12 @@ usage(char *progname) + exit(1); + } + +-int +-main(int argc, char *argv[]) ++inline static void ++read_gss_conf(void) + { +- int fg = 0; +- int verbosity = 0; +- int rpc_verbosity = 0; +- int opt; +- int i; +- extern char *optarg; +- char *progname; +- char *ccachedir = NULL; +- struct event sighup_ev; + char *s; + +- conf_init(); ++ conf_init(NFS_CONFFILE); + use_memcache = conf_get_bool("gssd", "use-memcache", use_memcache); + root_uses_machine_creds = conf_get_bool("gssd", "use-machine-creds", + root_uses_machine_creds); +@@ -865,6 +856,10 @@ main(int argc, char *argv[]) + s = conf_get_str("gssd", "pipefs-directory"); + if (!s) + s = conf_get_str("general", "pipefs-directory"); ++ else ++ printerr(0, "WARNING: Specifying pipefs-directory in the [gssd] " ++ "section of %s is deprecated. Use the [general] " ++ "section instead.", NFS_CONFFILE); + if (s) + pipefs_path = s; + s = conf_get_str("gssd", "keytab-file"); +@@ -877,6 +872,22 @@ main(int argc, char *argv[]) + if (s) + preferred_realm = s; + ++} ++ ++int ++main(int argc, char *argv[]) ++{ ++ int fg = 0; ++ int verbosity = 0; ++ int rpc_verbosity = 0; ++ int opt; ++ int i; ++ extern char *optarg; ++ char *progname; ++ struct event sighup_ev; ++ ++ read_gss_conf(); ++ + while ((opt = getopt(argc, argv, "DfvrlmnMp:k:d:t:T:R:")) != -1) { + switch (opt) { + case 'f': +diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man +index 87eef02..e620f0d 100644 +--- a/utils/gssd/gssd.man ++++ b/utils/gssd/gssd.man +@@ -335,10 +335,6 @@ Equivalent to + Equivalent to + .BR -t . + .TP +-.B pipefs-directory +-Equivalent to +-.BR -p . +-.TP + .B keytab-file + Equivalent to + .BR -k . +@@ -350,6 +346,14 @@ Equivalent to + .B preferred-realm + Equivalent to + .BR -R . ++.P ++In addtion, the following value is recognized from the ++.B [general] ++section: ++.TP ++.B pipefs-directory ++Equivalent to ++.BR -p . + + .SH SEE ALSO + .BR rpc.svcgssd (8), +diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c +index d74d372..4fc81c3 100644 +--- a/utils/gssd/gssd_proc.c ++++ b/utils/gssd/gssd_proc.c +@@ -729,10 +729,18 @@ handle_gssd_upcall(struct clnt_upcall_info *info) + char *target = NULL; + char *service = NULL; + char *enctypes = NULL; ++ char *upcall_str; ++ char *pbuf = info->lbuf; + + printerr(2, "\n%s: '%s' (%s)\n", __func__, info->lbuf, clp->relpath); + +- for (p = strtok(info->lbuf, " "); p; p = strtok(NULL, " ")) { ++ upcall_str = strdup(info->lbuf); ++ if (upcall_str == NULL) { ++ printerr(0, "ERROR: malloc failure\n"); ++ goto out_nomem; ++ } ++ ++ while ((p = strsep(&pbuf, " "))) { + if (!strncmp(p, "mech=", strlen("mech="))) + mech = p + strlen("mech="); + else if (!strncmp(p, "uid=", strlen("uid="))) +@@ -748,7 +756,7 @@ handle_gssd_upcall(struct clnt_upcall_info *info) + if (!mech || strlen(mech) < 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to find gss mechanism name " +- "in upcall string '%s'\n", info->lbuf); ++ "in upcall string '%s'\n", upcall_str); + goto out; + } + +@@ -761,7 +769,7 @@ handle_gssd_upcall(struct clnt_upcall_info *info) + if (!uidstr) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to find uid " +- "in upcall string '%s'\n", info->lbuf); ++ "in upcall string '%s'\n", upcall_str); + goto out; + } + +@@ -774,7 +782,7 @@ handle_gssd_upcall(struct clnt_upcall_info *info) + if (target && strlen(target) < 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to parse target name " +- "in upcall string '%s'\n", info->lbuf); ++ "in upcall string '%s'\n", upcall_str); + goto out; + } + +@@ -789,7 +797,7 @@ handle_gssd_upcall(struct clnt_upcall_info *info) + if (service && strlen(service) < 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to parse service type " +- "in upcall string '%s'\n", info->lbuf); ++ "in upcall string '%s'\n", upcall_str); + goto out; + } + +@@ -802,6 +810,8 @@ handle_gssd_upcall(struct clnt_upcall_info *info) + do_error_downcall(clp->gssd_fd, uid, -EACCES); + } + out: ++ free(upcall_str); ++out_nomem: + free(info); + return; + } +diff --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c +index 1fb579a..3514ae1 100644 +--- a/utils/gssd/svcgssd.c ++++ b/utils/gssd/svcgssd.c +@@ -63,8 +63,6 @@ + #include "err_util.h" + #include "conffile.h" + +-char *conf_path = NFS_CONFFILE; +- + void + sig_die(int signal) + { +@@ -103,7 +101,7 @@ main(int argc, char *argv[]) + char *principal = NULL; + char *s; + +- conf_init(); ++ conf_init(NFS_CONFFILE); + + s = conf_get_str("svcgssd", "principal"); + if (!s) +diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c +index f4e083a..c12e878 100644 +--- a/utils/idmapd/idmapd.c ++++ b/utils/idmapd/idmapd.c +@@ -165,9 +165,6 @@ static char *nobodyuser, *nobodygroup; + static uid_t nobodyuid; + static gid_t nobodygid; + +-/* Used by conffile.c in libnfs.a */ +-char *conf_path; +- + static int + flush_nfsd_cache(char *path, time_t now) + { +@@ -219,8 +216,8 @@ main(int argc, char **argv) + int serverstart = 1, clientstart = 1; + int ret; + char *progname; ++ char *conf_path = NULL; + +- conf_path = _PATH_IDMAPDCONF; + nobodyuser = NFS4NOBODY_USER; + nobodygroup = NFS4NOBODY_GROUP; + strlcpy(pipefsdir, PIPEFS_DIR, sizeof(pipefsdir)); +@@ -234,8 +231,11 @@ main(int argc, char **argv) + #define GETOPTSTR "hvfd:p:U:G:c:CS" + opterr=0; /* Turn off error messages */ + while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) { +- if (opt == 'c') ++ if (opt == 'c') { ++ warnx("-c is deprecated and may be removed in the " ++ "future. See idmapd(8)."); + conf_path = optarg; ++ } + if (opt == '?') { + if (strchr(GETOPTSTR, optopt)) + warnx("'-%c' option requires an argument.", optopt); +@@ -247,17 +247,33 @@ main(int argc, char **argv) + } + optind = 1; + +- if (stat(conf_path, &sb) == -1 && (errno == ENOENT || errno == EACCES)) { +- warn("Skipping configuration file \"%s\"", conf_path); +- conf_path = NULL; ++ if (conf_path) { /* deprecated -c option was specified */ ++ if (stat(conf_path, &sb) == -1 && (errno == ENOENT || errno == EACCES)) { ++ warn("Skipping configuration file \"%s\"", conf_path); ++ conf_path = NULL; ++ } else { ++ conf_init(conf_path); ++ verbose = conf_get_num("General", "Verbosity", 0); ++ cache_entry_expiration = conf_get_num("General", ++ "Cache-Expiration", DEFAULT_IDMAP_CACHE_EXPIRY); ++ CONF_SAVE(xpipefsdir, conf_get_str("General", "Pipefs-Directory")); ++ if (xpipefsdir != NULL) ++ strlcpy(pipefsdir, xpipefsdir, sizeof(pipefsdir)); ++ CONF_SAVE(nobodyuser, conf_get_str("Mapping", "Nobody-User")); ++ CONF_SAVE(nobodygroup, conf_get_str("Mapping", "Nobody-Group")); ++ } + } else { +- conf_init(); +- verbose = conf_get_num("General", "Verbosity", 0); +- cache_entry_expiration = conf_get_num("General", +- "Cache-Expiration", DEFAULT_IDMAP_CACHE_EXPIRY); ++ conf_path = NFS_CONFFILE; ++ conf_init(conf_path); + CONF_SAVE(xpipefsdir, conf_get_str("General", "Pipefs-Directory")); + if (xpipefsdir != NULL) + strlcpy(pipefsdir, xpipefsdir, sizeof(pipefsdir)); ++ ++ conf_path = _PATH_IDMAPDCONF; ++ conf_init(conf_path); ++ verbose = conf_get_num("General", "Verbosity", 0); ++ cache_entry_expiration = conf_get_num("General", ++ "cache-expiration", DEFAULT_IDMAP_CACHE_EXPIRY); + CONF_SAVE(nobodyuser, conf_get_str("Mapping", "Nobody-User")); + CONF_SAVE(nobodygroup, conf_get_str("Mapping", "Nobody-Group")); + } +diff --git a/utils/idmapd/idmapd.man b/utils/idmapd/idmapd.man +index d4ab894..5f34d2b 100644 +--- a/utils/idmapd/idmapd.man ++++ b/utils/idmapd/idmapd.man +@@ -73,11 +73,28 @@ The default value is \&"/var/lib/nfs/rpc_pipefs\&". + .It Fl c Ar path + Use configuration file + .Ar path . ++This option is deprecated. + .It Fl C + Client-only: perform no idmapping for any NFS server, even if one is detected. + .It Fl S + Server-only: perform no idmapping for any NFS client, even if one is detected. + .El ++.Sh CONFIGURATION FILES ++.Nm ++recognizes the following value from the ++.Sy [general] ++section of the ++.Pa /etc/nfs.conf ++configuration file: ++.Bl -tag -width Ds_imagedir ++.It Sy pipefs-directory ++Equivalent to ++.Sy -p . ++.El ++.Pp ++All other settings related to id mapping are found in the ++.Pa /etc/idmapd.conf ++configuration file. + .Sh EXAMPLES + .Cm rpc.idmapd -f -vvv + .Pp +@@ -94,9 +111,11 @@ messages to console, and with a verbosity level of 3. + .\" This next request is for sections 1, 6, 7 & 8 only. + .\" .Sh ENVIRONMENT + .Sh FILES +-.Pa /etc/idmapd.conf ++.Pa /etc/idmapd.conf , ++.Pa /etc/nfs.conf + .Sh SEE ALSO + .Xr idmapd.conf 5 , ++.Xr nfs.conf 5 , + .Xr nfsidmap 8 + .\".Sh SEE ALSO + .\".Xr nylon.conf 4 +diff --git a/utils/mount/configfile.c b/utils/mount/configfile.c +index 0a4cc04..dc964c7 100644 +--- a/utils/mount/configfile.c ++++ b/utils/mount/configfile.c +@@ -51,10 +51,6 @@ + #define NFSMOUNT_SERVER "Server" + #endif + +-#ifndef MOUNTOPTS_CONFFILE +-#define MOUNTOPTS_CONFFILE "/etc/nfsmount.conf" +-#endif +-char *conf_path = MOUNTOPTS_CONFFILE; + enum { + MNT_NOARG=0, + MNT_INTARG, +diff --git a/utils/mount/mount_config.h b/utils/mount/mount_config.h +index 69ffd1e..e4f8511 100644 +--- a/utils/mount/mount_config.h ++++ b/utils/mount/mount_config.h +@@ -20,6 +20,10 @@ + #include "conffile.h" + #include "xlog.h" + ++#ifndef MOUNTOPTS_CONFFILE ++#define MOUNTOPTS_CONFFILE "/etc/nfsmount.conf" ++#endif ++ + extern char *conf_get_mntopts(char *, char *, char *); + + static inline void mount_config_init(char *program) +@@ -28,7 +32,7 @@ static inline void mount_config_init(char *program) + /* + * Read the the default mount options + */ +- conf_init(); ++ conf_init(MOUNTOPTS_CONFFILE); + } + + static inline char *mount_config_opts(char *spec, +diff --git a/utils/mount/network.c b/utils/mount/network.c +index 7dceb2d..281e935 100644 +--- a/utils/mount/network.c ++++ b/utils/mount/network.c +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -804,6 +805,7 @@ int start_statd(void) + pid_t pid = fork(); + switch (pid) { + case 0: /* child */ ++ setgroups(0, NULL); + setgid(0); + setuid(0); + execle(START_STATD, START_STATD, NULL, envp); +@@ -1638,6 +1640,7 @@ int nfs_options2pmap(struct mount_options *options, + struct pmap *nfs_pmap, struct pmap *mnt_pmap) + { + struct nfs_version version; ++ memset(&version, 0, sizeof(version)); + + if (!nfs_nfs_program(options, &nfs_pmap->pm_prog)) + return 0; +diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c +index 387d734..c0266e5 100644 +--- a/utils/mount/stropts.c ++++ b/utils/mount/stropts.c +@@ -315,9 +315,10 @@ static int nfs_set_version(struct nfsmount_info *mi) + if (!nfs_nfs_version(mi->options, &mi->version)) + return 0; + +- if (strncmp(mi->type, "nfs4", 4) == 0) ++ if (strncmp(mi->type, "nfs4", 4) == 0) { + mi->version.major = 4; +- ++ mi->version.v_mode = V_GENERAL; ++ } + /* + * Before 2.6.32, the kernel NFS client didn't + * support "-t nfs vers=4" mounts, so NFS version +@@ -517,6 +518,10 @@ nfs_rewrite_pmap_mount_options(struct mount_options *options, int checkv4) + unsigned long protocol; + struct pmap mnt_pmap; + ++ /* initialize structs */ ++ memset(&nfs_pmap, 0, sizeof(struct pmap)); ++ memset(&mnt_pmap, 0, sizeof(struct pmap)); ++ + /* + * Version and transport negotiation is not required + * and does not work for RDMA mounts. +@@ -834,9 +839,6 @@ check_result: + case EINVAL: + /* A less clear indication that our client + * does not support NFSv4 minor version. */ +- if (mi->version.v_mode == V_GENERAL && +- mi->version.minor == 0) +- return result; + if (mi->version.v_mode != V_SPECIFIC) { + if (mi->version.minor > 0) { + mi->version.minor--; +@@ -858,19 +860,28 @@ check_result: + /* UDP-Only servers won't support v4, but maybe it + * just isn't ready yet. So try v3, but double-check + * with rpcbind for v4. */ ++ if (mi->version.v_mode == V_GENERAL) ++ /* Mustn't try v2,v3 */ ++ return result; + result = nfs_try_mount_v3v2(mi, TRUE); + if (result == 0 && errno == EAGAIN) { + /* v4 server seems to be registered now. */ + result = nfs_try_mount_v4(mi); + if (result == 0 && errno != ECONNREFUSED) + goto check_result; +- } ++ } else if (result == 0) ++ /* Restore original errno with v3 failures */ ++ errno = ECONNREFUSED; ++ + return result; + default: + return result; + } + + fall_back: ++ if (mi->version.v_mode == V_GENERAL) ++ /* v2,3 fallback not allowed */ ++ return result; + return nfs_try_mount_v3v2(mi, FALSE); + } + +diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c +index d065830..8299256 100644 +--- a/utils/mountd/auth.c ++++ b/utils/mountd/auth.c +@@ -41,6 +41,8 @@ static nfs_client my_client; + + extern int use_ipaddr; + ++extern struct state_paths etab; ++ + void + auth_init(void) + { +@@ -84,10 +86,10 @@ auth_reload() + static unsigned int counter; + int fd; + +- if ((fd = open(_PATH_ETAB, O_RDONLY)) < 0) { +- xlog(L_FATAL, "couldn't open %s", _PATH_ETAB); ++ if ((fd = open(etab.statefn, O_RDONLY)) < 0) { ++ xlog(L_FATAL, "couldn't open %s", etab.statefn); + } else if (fstat(fd, &stb) < 0) { +- xlog(L_FATAL, "couldn't stat %s", _PATH_ETAB); ++ xlog(L_FATAL, "couldn't stat %s", etab.statefn); + close(fd); + } else if (last_fd != -1 && stb.st_ino == last_inode) { + /* We opened the etab file before, and its inode +diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c +index 61699e6..829f803 100644 +--- a/utils/mountd/mountd.c ++++ b/utils/mountd/mountd.c +@@ -29,6 +29,7 @@ + #include "mountd.h" + #include "rpcmisc.h" + #include "pseudoflavors.h" ++#include "nfslib.h" + + extern void my_svc_run(void); + +@@ -40,7 +41,8 @@ int reverse_resolve = 0; + int manage_gids; + int use_ipaddr = -1; + +-char *conf_path = NFS_CONFFILE; ++struct state_paths etab; ++struct state_paths rmtab; + + /* PRC: a high-availability callout program can be specified with -H + * When this is done, the program will receive callouts whenever clients +@@ -110,8 +112,8 @@ unregister_services (void) + static void + cleanup_lockfiles (void) + { +- unlink(_PATH_ETABLCK); +- unlink(_PATH_RMTABLCK); ++ unlink(etab.lockfn); ++ unlink(rmtab.lockfn); + } + + /* Wait for all worker child processes to exit and reap them */ +@@ -181,6 +183,8 @@ fork_workers(void) + wait_for_workers(); + unregister_services(); + cleanup_lockfiles(); ++ free_state_path_names(&etab); ++ free_state_path_names(&rmtab); + xlog(L_NOTICE, "mountd: no more workers, exiting\n"); + exit(0); + } +@@ -198,6 +202,8 @@ killer (int sig) + wait_for_workers(); + } + cleanup_lockfiles(); ++ free_state_path_names(&etab); ++ free_state_path_names(&rmtab); + xlog (L_NOTICE, "Caught signal %d, un-registering and exiting.", sig); + exit(0); + } +@@ -656,7 +662,6 @@ get_exportlist(void) + int + main(int argc, char **argv) + { +- char *state_dir = NFS_STATEDIR; + char *progname; + char *s; + unsigned int listeners = 0; +@@ -674,7 +679,7 @@ main(int argc, char **argv) + else + progname = argv[0]; + +- conf_init(); ++ conf_init(NFS_CONFFILE); + xlog_from_conffile("mountd"); + manage_gids = conf_get_bool("mountd", "manage-gids", manage_gids); + descriptors = conf_get_num("mountd", "descriptors", descriptors); +@@ -684,8 +689,8 @@ main(int argc, char **argv) + ha_callout_prog = conf_get_str("mountd", "ha-callout"); + + s = conf_get_str("mountd", "state-directory-path"); +- if (s) +- state_dir = s; ++ if (s && !state_setup_basedir(argv[0], s)) ++ exit(1); + + /* NOTE: following uses "nfsd" section of nfs.conf !!!! */ + if (conf_get_bool("nfsd", "udp", NFSCTL_UDPISSET(_rpcprotobits))) +@@ -758,7 +763,8 @@ main(int argc, char **argv) + reverse_resolve = 1; + break; + case 's': +- state_dir = xstrdup(optarg); ++ if (!state_setup_basedir(argv[0], optarg)) ++ exit(1); + break; + case 't': + num_threads = atoi (optarg); +@@ -790,11 +796,10 @@ main(int argc, char **argv) + fprintf(stderr, "%s: No protocol versions specified!\n", progname); + usage(progname, 1); + } +- if (chdir(state_dir)) { +- fprintf(stderr, "%s: chdir(%s) failed: %s\n", +- progname, state_dir, strerror(errno)); +- exit(1); +- } ++ if (!setup_state_path_names(progname, ETAB, ETABTMP, ETABLCK, &etab)) ++ return 1; ++ if (!setup_state_path_names(progname, RMTAB, RMTABTMP, RMTABLCK, &rmtab)) ++ return 1; + + if (getrlimit (RLIMIT_NOFILE, &rlim) != 0) + fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n", +@@ -888,6 +893,8 @@ main(int argc, char **argv) + + xlog(L_ERROR, "RPC service loop terminated unexpectedly. Exiting...\n"); + unregister_services(); ++ free_state_path_names(&etab); ++ free_state_path_names(&rmtab); + exit(1); + } + +diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man +index 9f0a51f..9978afc 100644 +--- a/utils/mountd/mountd.man ++++ b/utils/mountd/mountd.man +@@ -144,7 +144,7 @@ Instead, mount the nfsd filesystem on + .IR /proc/fs/nfsd . + .TP + .BI "\-s," "" " \-\-state\-directory\-path " directory +-Specify a directory in which to place statd state information. ++Specify a directory in which to place state information (etab and rmtab). + If this option is not specified the default of + .I /var/lib/nfs + is used. +diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c +index 527377f..3ae0dbb 100644 +--- a/utils/mountd/rmtab.c ++++ b/utils/mountd/rmtab.c +@@ -28,6 +28,8 @@ + + extern int reverse_resolve; + ++extern struct state_paths rmtab; ++ + /* If new path is a link do not destroy it but place the + * file where the link points. + */ +@@ -59,7 +61,7 @@ mountlist_add(char *host, const char *path) + int lockid; + long pos; + +- if ((lockid = xflock(_PATH_RMTABLCK, "a")) < 0) ++ if ((lockid = xflock(rmtab.lockfn, "a")) < 0) + return; + setrmtabent("r+"); + while ((rep = getrmtabent(1, &pos)) != NULL) { +@@ -99,13 +101,13 @@ mountlist_del(char *hname, const char *path) + int lockid; + int match; + +- if ((lockid = xflock(_PATH_RMTABLCK, "w")) < 0) ++ if ((lockid = xflock(rmtab.lockfn, "w")) < 0) + return; + if (!setrmtabent("r")) { + xfunlock(lockid); + return; + } +- if (!(fp = fsetrmtabent(_PATH_RMTABTMP, "w"))) { ++ if (!(fp = fsetrmtabent(rmtab.tmpfn, "w"))) { + endrmtabent(); + xfunlock(lockid); + return; +@@ -121,9 +123,9 @@ mountlist_del(char *hname, const char *path) + if (!match || rep->r_count) + fputrmtabent(fp, rep, NULL); + } +- if (slink_safe_rename(_PATH_RMTABTMP, _PATH_RMTAB) < 0) { ++ if (slink_safe_rename(rmtab.tmpfn, rmtab.statefn) < 0) { + xlog(L_ERROR, "couldn't rename %s to %s", +- _PATH_RMTABTMP, _PATH_RMTAB); ++ rmtab.tmpfn, rmtab.statefn); + } + endrmtabent(); /* close & unlink */ + fendrmtabent(fp); +@@ -138,7 +140,7 @@ mountlist_del_all(const struct sockaddr *sap) + FILE *fp; + int lockid; + +- if ((lockid = xflock(_PATH_RMTABLCK, "w")) < 0) ++ if ((lockid = xflock(rmtab.lockfn, "w")) < 0) + return; + hostname = host_canonname(sap); + if (hostname == NULL) { +@@ -151,7 +153,7 @@ mountlist_del_all(const struct sockaddr *sap) + if (!setrmtabent("r")) + goto out_free; + +- if (!(fp = fsetrmtabent(_PATH_RMTABTMP, "w"))) ++ if (!(fp = fsetrmtabent(rmtab.tmpfn, "w"))) + goto out_close; + + while ((rep = getrmtabent(1, NULL)) != NULL) { +@@ -160,9 +162,9 @@ mountlist_del_all(const struct sockaddr *sap) + continue; + fputrmtabent(fp, rep, NULL); + } +- if (slink_safe_rename(_PATH_RMTABTMP, _PATH_RMTAB) < 0) { ++ if (slink_safe_rename(rmtab.tmpfn, rmtab.statefn) < 0) { + xlog(L_ERROR, "couldn't rename %s to %s", +- _PATH_RMTABTMP, _PATH_RMTAB); ++ rmtab.tmpfn, rmtab.statefn); + } + fendrmtabent(fp); + out_close: +@@ -195,11 +197,11 @@ mountlist_list(void) + struct stat stb; + int lockid; + +- if ((lockid = xflock(_PATH_RMTABLCK, "r")) < 0) ++ if ((lockid = xflock(rmtab.lockfn, "r")) < 0) + return NULL; +- if (stat(_PATH_RMTAB, &stb) < 0) { ++ if (stat(rmtab.statefn, &stb) < 0) { + xlog(L_ERROR, "can't stat %s: %s", +- _PATH_RMTAB, strerror(errno)); ++ rmtab.statefn, strerror(errno)); + xfunlock(lockid); + return NULL; + } +diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c +index 20f4b79..111058f 100644 +--- a/utils/nfsd/nfsd.c ++++ b/utils/nfsd/nfsd.c +@@ -34,8 +34,6 @@ + #define NFSD_NPROC 8 + #endif + +-char *conf_path = NFS_CONFFILE; +- + static void usage(const char *); + + static struct option longopts[] = +@@ -44,7 +42,9 @@ static struct option longopts[] = + { "help", 0, 0, 'h' }, + { "no-nfs-version", 1, 0, 'N' }, + { "nfs-version", 1, 0, 'V' }, ++ { "tcp", 0, 0, 't' }, + { "no-tcp", 0, 0, 'T' }, ++ { "udp", 0, 0, 'u' }, + { "no-udp", 0, 0, 'U' }, + { "port", 1, 0, 'P' }, + { "port", 1, 0, 'p' }, +@@ -67,8 +67,9 @@ main(int argc, char **argv) + int socket_up = 0; + unsigned int minorvers = 0; + unsigned int minorversset = 0; ++ unsigned int minormask = 0; + unsigned int versbits = NFSCTL_VERDEFAULT; +- unsigned int protobits = NFSCTL_ALLBITS; ++ unsigned int protobits = NFSCTL_PROTODEFAULT; + int grace = -1; + int lease = -1; + +@@ -79,7 +80,7 @@ main(int argc, char **argv) + xlog_syslog(0); + xlog_stderr(1); + +- conf_init(); ++ conf_init(NFS_CONFFILE); + xlog_from_conffile("nfsd"); + count = conf_get_num("nfsd", "threads", count); + grace = conf_get_num("nfsd", "grace-time", grace); +@@ -104,10 +105,16 @@ main(int argc, char **argv) + else + NFSCTL_VERUNSET(versbits, i); + } ++ ++ nfssvc_get_minormask(&minormask); + /* We assume the kernel will default all minor versions to 'on', + * and allow the config file to disable some. + */ +- for (i = NFS4_MINMINOR; i <= NFS4_MAXMINOR; i++) { ++ if (NFSCTL_VERISSET(versbits, 4)) { ++ NFSCTL_MINORSET(minorversset, 0); ++ NFSCTL_MINORSET(minorvers, 0); ++ } ++ for (i = 1; i <= NFS4_MAXMINOR; i++) { + char tag[20]; + sprintf(tag, "vers4.%d", i); + /* The default for minor version support is to let the +@@ -119,12 +126,12 @@ main(int argc, char **argv) + * (i.e. don't set the bit in minorversset). + */ + if (!conf_get_bool("nfsd", tag, 1)) { +- NFSCTL_VERSET(minorversset, i); +- NFSCTL_VERUNSET(minorvers, i); ++ NFSCTL_MINORSET(minorversset, i); ++ NFSCTL_MINORUNSET(minorvers, i); + } + if (conf_get_bool("nfsd", tag, 0)) { +- NFSCTL_VERSET(minorversset, i); +- NFSCTL_VERSET(minorvers, i); ++ NFSCTL_MINORSET(minorversset, i); ++ NFSCTL_MINORSET(minorvers, i); + } + } + +@@ -138,7 +145,7 @@ main(int argc, char **argv) + } + } + +- while ((c = getopt_long(argc, argv, "dH:hN:V:p:P:sTUrG:L:", longopts, NULL)) != EOF) { ++ while ((c = getopt_long(argc, argv, "dH:hN:V:p:P:stTitUrG:L:", longopts, NULL)) != EOF) { + switch(c) { + case 'd': + xlog_config(D_ALL, 1); +@@ -179,13 +186,17 @@ main(int argc, char **argv) + case 4: + if (*p == '.') { + int i = atoi(p+1); +- if (i < NFS4_MINMINOR || i > NFS4_MAXMINOR) { ++ if (i < 0 || i > NFS4_MAXMINOR) { + fprintf(stderr, "%s: unsupported minor version\n", optarg); + exit(1); + } +- NFSCTL_VERSET(minorversset, i); +- NFSCTL_VERUNSET(minorvers, i); +- break; ++ NFSCTL_MINORSET(minorversset, i); ++ NFSCTL_MINORUNSET(minorvers, i); ++ if (minorvers != 0) ++ break; ++ } else { ++ minorvers = 0; ++ minorversset = minormask; + } + case 3: + case 2: +@@ -201,14 +212,14 @@ main(int argc, char **argv) + case 4: + if (*p == '.') { + int i = atoi(p+1); +- if (i < NFS4_MINMINOR || i > NFS4_MAXMINOR) { ++ if (i < 0 || i > NFS4_MAXMINOR) { + fprintf(stderr, "%s: unsupported minor version\n", optarg); + exit(1); + } +- NFSCTL_VERSET(minorversset, i); +- NFSCTL_VERSET(minorvers, i); +- break; +- } ++ NFSCTL_MINORSET(minorversset, i); ++ NFSCTL_MINORSET(minorvers, i); ++ } else ++ minorvers = minorversset = minormask; + case 3: + case 2: + NFSCTL_VERSET(versbits, c); +@@ -222,9 +233,15 @@ main(int argc, char **argv) + xlog_syslog(1); + xlog_stderr(0); + break; ++ case 't': ++ NFSCTL_TCPSET(protobits); ++ break; + case 'T': + NFSCTL_TCPUNSET(protobits); + break; ++ case 'u': ++ NFSCTL_UDPSET(protobits); ++ break; + case 'U': + NFSCTL_UDPUNSET(protobits); + break; +@@ -372,9 +389,9 @@ usage(const char *prog) + { + fprintf(stderr, "Usage:\n" + "%s [-d|--debug] [-H hostname] [-p|-P|--port port]\n" +- " [-N|--no-nfs-version version] [-V|--nfs-version version]\n" +- " [-s|--syslog] [-T|--no-tcp] [-U|--no-udp] [-r|--rdma=]\n" +- " [-G|--grace-time secs] [-L|--leasetime secs] nrservs\n", ++ " [-N|--no-nfs-version version] [-V|--nfs-version version]\n" ++ " [-s|--syslog] [-t|--tcp] [-T|--no-tcp] [-u|--udp] [-U|--no-udp]\n" ++ " [-r|--rdma=] [-G|--grace-time secs] [-L|--leasetime secs] nrservs\n", + prog); + exit(2); + } +diff --git a/utils/nfsd/nfsd.man b/utils/nfsd/nfsd.man +index 8901fb6..d83ef86 100644 +--- a/utils/nfsd/nfsd.man ++++ b/utils/nfsd/nfsd.man +@@ -57,7 +57,7 @@ This option can be used to request that + .B rpc.nfsd + does not offer certain versions of NFS. The current version of + .B rpc.nfsd +-can support major NFS versions 2,3,4 and the minor versions 4.1 and 4.2. ++can support major NFS versions 2,3,4 and the minor versions 4.0, 4.1 and 4.2. + .TP + .B \-s " or " \-\-syslog + By default, +@@ -67,22 +67,24 @@ logs error messages (and debug messages, if enabled) to stderr. This option make + log these messages to syslog instead. Note that errors encountered during + option processing will still be logged to stderr regardless of this option. + .TP ++.B \-t " or " \-\-tcp ++Instruct the kernel nfs server to open and listen on a TCP socket. This is the default. ++.TP + .B \-T " or " \-\-no-tcp +-Disable +-.B rpc.nfsd +-from accepting TCP connections from clients. ++Instruct the kernel nfs server not to open and listen on a TCP socket. ++.TP ++.B \-u " or " \-\-udp ++Instruct the kernel nfs server to open and listen on a UDP socket. + .TP + .B \-U " or " \-\-no-udp +-Disable +-.B rpc.nfsd +-from accepting UDP connections from clients. ++Instruct the kernel nfs server not to open and listen on a UDP socket. This is the default. + .TP + .B \-V " or " \-\-nfs-version vers + This option can be used to request that + .B rpc.nfsd + offer certain versions of NFS. The current version of + .B rpc.nfsd +-can support major NFS versions 2,3,4 and the minor versions 4.1 and 4.2. ++can support major NFS versions 2,3,4 and the minor versions 4.0, 4.1 and 4.2. + .TP + .B \-L " or " \-\-lease-time seconds + Set the lease-time used for NFSv4. This corresponds to how often +diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c +index 07f6ff1..e8609c1 100644 +--- a/utils/nfsd/nfssvc.c ++++ b/utils/nfsd/nfssvc.c +@@ -330,36 +330,78 @@ nfssvc_set_time(const char *type, const int seconds) + } + + void ++nfssvc_get_minormask(unsigned int *mask) ++{ ++ int fd; ++ char *ptr = buf; ++ ssize_t size; ++ ++ fd = open(NFSD_VERS_FILE, O_RDONLY); ++ if (fd < 0) ++ return; ++ ++ size = read(fd, buf, sizeof(buf)); ++ if (size < 0) { ++ xlog(L_ERROR, "Getting versions failed: errno %d (%m)", errno); ++ goto out; ++ } ++ ptr[size] = '\0'; ++ for (;;) { ++ unsigned vers, minor = 0; ++ char *token = strtok(ptr, " "); ++ ++ if (!token) ++ break; ++ ptr = NULL; ++ if (*token != '+' && *token != '-') ++ continue; ++ if (sscanf(++token, "%u.%u", &vers, &minor) > 0 && ++ vers == 4 && minor <= NFS4_MAXMINOR) ++ NFSCTL_MINORSET(*mask, minor); ++ } ++out: ++ close(fd); ++ return; ++} ++ ++static int ++nfssvc_print_vers(char *ptr, unsigned size, unsigned vers, unsigned minorvers, ++ int isset) ++{ ++ char sign = isset ? '+' : '-'; ++ if (minorvers == 0) ++ return snprintf(ptr, size, "%c%u ", sign, vers); ++ return snprintf(ptr, size, "%c%u.%u ", sign, vers, minorvers); ++} ++ ++void + nfssvc_setvers(unsigned int ctlbits, unsigned int minorvers, unsigned int minorversset) + { + int fd, n, off; +- char *ptr; + +- ptr = buf; + off = 0; + fd = open(NFSD_VERS_FILE, O_WRONLY); + if (fd < 0) + return; + +- for (n = NFS4_MINMINOR; n <= NFS4_MAXMINOR; n++) { +- if (NFSCTL_VERISSET(minorversset, n)) { +- if (NFSCTL_VERISSET(minorvers, n)) +- off += snprintf(ptr+off, sizeof(buf) - off, "+4.%d ", n); +- else +- off += snprintf(ptr+off, sizeof(buf) - off, "-4.%d ", n); +- } +- } +- for (n = NFSD_MINVERS; n <= NFSD_MAXVERS; n++) { +- if (NFSCTL_VERISSET(ctlbits, n)) +- off += snprintf(ptr+off, sizeof(buf) - off, "+%d ", n); +- else +- off += snprintf(ptr+off, sizeof(buf) - off, "-%d ", n); ++ for (n = NFSD_MINVERS; n <= ((NFSD_MAXVERS < 3) ? NFSD_MAXVERS : 3); n++) ++ off += nfssvc_print_vers(&buf[off], sizeof(buf) - off, ++ n, 0, NFSCTL_VERISSET(ctlbits, n)); ++ ++ for (n = 0; n <= NFS4_MAXMINOR; n++) { ++ if (!NFSCTL_MINORISSET(minorversset, n)) ++ continue; ++ off += nfssvc_print_vers(&buf[off], sizeof(buf) - off, ++ 4, n, NFSCTL_MINORISSET(minorvers, n)); + } ++ if (!off--) ++ goto out; ++ buf[off] = '\0'; + xlog(D_GENERAL, "Writing version string to kernel: %s", buf); +- snprintf(ptr+off, sizeof(buf) - off, "\n"); ++ snprintf(&buf[off], sizeof(buf) - off, "\n"); + if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) + xlog(L_ERROR, "Setting version failed: errno %d (%m)", errno); +- ++out: + close(fd); + + return; +diff --git a/utils/nfsd/nfssvc.h b/utils/nfsd/nfssvc.h +index cd5a7e8..39ebf37 100644 +--- a/utils/nfsd/nfssvc.h ++++ b/utils/nfsd/nfssvc.h +@@ -28,3 +28,4 @@ void nfssvc_set_time(const char *type, const int seconds); + int nfssvc_set_rdmaport(const char *port); + void nfssvc_setvers(unsigned int ctlbits, unsigned int minorvers4, unsigned int minorvers4set); + int nfssvc_threads(int nrservs); ++void nfssvc_get_minormask(unsigned int *mask); +diff --git a/utils/nfsdcltrack/nfsdcltrack.c b/utils/nfsdcltrack/nfsdcltrack.c +index 7af9efb..124c923 100644 +--- a/utils/nfsdcltrack/nfsdcltrack.c ++++ b/utils/nfsdcltrack/nfsdcltrack.c +@@ -56,8 +56,6 @@ + /* defined by RFC 3530 */ + #define NFS4_OPAQUE_LIMIT 1024 + +-char *conf_path = NFS_CONFFILE; +- + /* private data structures */ + struct cltrack_cmd { + char *name; +@@ -566,7 +564,7 @@ main(int argc, char **argv) + xlog_syslog(1); + xlog_stderr(0); + +- conf_init(); ++ conf_init(NFS_CONFFILE); + xlog_from_conffile("nfsdcltrack"); + val = conf_get_str("nfsdcltrack", "storagedir"); + if (val) +diff --git a/utils/nfsdcltrack/sqlite.c b/utils/nfsdcltrack/sqlite.c +index 54cd748..1552eba 100644 +--- a/utils/nfsdcltrack/sqlite.c ++++ b/utils/nfsdcltrack/sqlite.c +@@ -101,7 +101,7 @@ sqlite_query_schema_version(void) + "SELECT value FROM parameters WHERE key == \"version\";", + -1, &stmt, NULL); + if (ret != SQLITE_OK) { +- xlog(L_ERROR, "Unable to prepare select statement: %s", ++ xlog(D_GENERAL, "Unable to prepare select statement: %s", + sqlite3_errmsg(dbh)); + ret = 0; + goto out; +@@ -110,7 +110,7 @@ sqlite_query_schema_version(void) + /* query schema version */ + ret = sqlite3_step(stmt); + if (ret != SQLITE_ROW) { +- xlog(L_ERROR, "Select statement execution failed: %s", ++ xlog(D_GENERAL, "Select statement execution failed: %s", + sqlite3_errmsg(dbh)); + ret = 0; + goto out; +diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am +index 152b680..ea32075 100644 +--- a/utils/statd/Makefile.am ++++ b/utils/statd/Makefile.am +@@ -18,6 +18,7 @@ statd_LDADD = ../../support/nsm/libnsm.a \ + $(LIBWRAP) $(LIBNSL) $(LIBCAP) $(LIBTIRPC) + sm_notify_LDADD = ../../support/nsm/libnsm.a \ + ../../support/nfs/libnfs.a \ ++ ../../support/misc/libmisc.a \ + $(LIBNSL) $(LIBCAP) $(LIBTIRPC) + + EXTRA_DIST = sim_sm_inter.x $(man8_MANS) simulate.c +diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c +index 623213e..d216ddb 100644 +--- a/utils/statd/sm-notify.c ++++ b/utils/statd/sm-notify.c +@@ -45,6 +45,8 @@ + + #define NLM_END_GRACE_FILE "/proc/fs/lockd/nlm_end_grace" + ++int lift_grace = 1; ++ + struct nsm_host { + struct nsm_host * next; + char * name; +@@ -67,7 +69,6 @@ static _Bool opt_update_state = true; + static unsigned int opt_max_retry = 15 * 60; + static char * opt_srcaddr = NULL; + static char * opt_srcport = NULL; +-char * conf_path = NFS_CONFFILE; + + static void notify(const int sock); + static int notify_host(int, struct nsm_host *); +@@ -489,11 +490,12 @@ main(int argc, char **argv) + else + progname = argv[0]; + +- conf_init(); ++ conf_init(NFS_CONFFILE); + xlog_from_conffile("sm-notify"); + opt_max_retry = conf_get_num("sm-notify", "retry-time", opt_max_retry / 60) * 60; + opt_srcport = conf_get_str("sm-notify", "outgoing-port"); + opt_srcaddr = conf_get_str("sm-notify", "outgoing-addr"); ++ lift_grace = conf_get_bool("sm-notify", "lift-grace", lift_grace); + s = conf_get_str("statd", "state-directory-path"); + if (s && !nsm_setup_pathnames(argv[0], s)) + exit(1); +@@ -570,7 +572,8 @@ usage: fprintf(stderr, + (void)nsm_retire_monitored_hosts(); + if (nsm_load_notify_list(smn_get_host) == 0) { + xlog(D_GENERAL, "No hosts to notify; exiting"); +- nsm_lift_grace_period(); ++ if (lift_grace) ++ nsm_lift_grace_period(); + return 0; + } + +diff --git a/utils/statd/sm-notify.man b/utils/statd/sm-notify.man +index bb7f6e0..cfe1e4b 100644 +--- a/utils/statd/sm-notify.man ++++ b/utils/statd/sm-notify.man +@@ -241,6 +241,24 @@ These have the same effect as the command line options + .B v + respectively. + ++An additional value recognized in the ++.B [sm-notify] ++section is ++.BR lift-grace . ++By default, ++.B sm-notify ++will lift lockd's grace period early if it has no hosts to notify. ++Some high availability configurations will run one ++.B sm-notify ++per floating IP address. In these configurations, lifting the ++grace period early may prevent clients from reclaiming locks. ++.RB "Setting " lift-grace " to " n ++will prevent ++.B sm-notify ++from ending the grace period early. ++.B lift-grace ++has no corresponding command line option. ++ + The value recognized in the + .B [statd] + section is +diff --git a/utils/statd/statd.c b/utils/statd/statd.c +index d333b29..1443715 100644 +--- a/utils/statd/statd.c ++++ b/utils/statd/statd.c +@@ -37,7 +37,6 @@ + #include + + int run_mode = 0; /* foreground logging mode */ +-char *conf_path = NFS_CONFFILE; + + /* LH - I had these local to main, but it seemed silly to have + * two copies of each - one in main(), one static in log.c... +@@ -274,7 +273,7 @@ int main (int argc, char **argv) + /* Set hostname */ + MY_NAME = NULL; + +- conf_init(); ++ conf_init(NFS_CONFFILE); + xlog_from_conffile("statd"); + out_port = conf_get_num("statd", "outgoing-port", out_port); + port = conf_get_num("statd", "port", port); diff --git a/nfs-utils.spec b/nfs-utils.spec index 1561a2b..7b91f1a 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://sourceforge.net/projects/nfs Version: 2.1.1 -Release: 5.rc2%{?dist} +Release: 5.rc3%{?dist} Epoch: 1 # group all 32bit related archs @@ -15,9 +15,7 @@ Source3: nfs-utils_env.sh Source4: lockd.conf Source5: 24-nfs-server.conf -Patch001: nfs-utils-2.1.2-rc2.patch -Patch002: nfs-utils-2.1.1-network-online.patch -Patch003: nfs-utils-2.1.1-nfsdcltrack-errors.patch +Patch001: nfs-utils-2.1.2-rc3.patch Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch @@ -77,8 +75,6 @@ This package also contains the mount.nfs and umount.nfs program. %setup -q %patch001 -p1 -%patch002 -p1 -%patch003 -p1 %patch100 -p1 %patch101 -p1 @@ -292,6 +288,9 @@ fi /sbin/umount.nfs4 %changelog +* Thu Jun 1 2017 Steve Dickson 2.1.1-5.rc3 +- Updated to the latest RC releease: nfs-utils-2-1-2-rc3 (bz 1457921) + * Wed Apr 26 2017 Steve Dickson 2.1.1-5.rc2 - Conditionally restart gssproxy now that config file is installed (bz 1440885) - systemd: Afters are also needed for the Wants=network-online.target (bz 1419351)