From b4bc034399ab32972091302fdecb504279689e74 Mon Sep 17 00:00:00 2001 From: Steve Dickson Date: Jul 31 2017 14:00:20 +0000 Subject: Updated to the latest RC releease: nfs-utils-2-1-2-rc5 Signed-off-by: Steve Dickson --- diff --git a/nfs-utils-2.1.2-rc2.patch b/nfs-utils-2.1.2-rc2.patch deleted file mode 100644 index abff896..0000000 --- a/nfs-utils-2.1.2-rc2.patch +++ /dev/null @@ -1,2338 +0,0 @@ -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/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..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/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..fec0399 100644 ---- a/systemd/nfs-mountd.service -+++ b/systemd/nfs-mountd.service -@@ -2,8 +2,9 @@ - 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=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..e6b8f58 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= 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..66addb9 ---- /dev/null -+++ b/systemd/rpc-pipefs-generator.c -@@ -0,0 +1,138 @@ -+/* -+ * 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" -+char *conf_path = NFS_CONFFILE; -+ -+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(); -+ 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..f54d4c5 100644 ---- a/systemd/rpc-statd-notify.service -+++ b/systemd/rpc-statd-notify.service -@@ -1,7 +1,7 @@ - [Unit] - Description=Notify NFS peers of a restart - DefaultDependencies=no --Requires=network.target -+Wants=network-online.target - After=local-fs.target network.target nss-lookup.target - - # if we run an nfs server, it needs to be running before we -diff --git a/systemd/rpc-statd.service b/systemd/rpc-statd.service -index 60d600f..8cef022 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=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..d2da764 100644 ---- a/utils/blkmapd/device-discovery.c -+++ b/utils/blkmapd/device-discovery.c -@@ -50,20 +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; -+char *conf_path = NULL; -+ - - struct bl_disk_path *bl_get_path(const char *filepath, - struct bl_disk_path *paths) -@@ -358,8 +374,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 +388,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 +453,19 @@ 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_path = NFS_CONFFILE; -+ conf_init(); -+ 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 +525,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..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/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..28f9649 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(); -@@ -865,6 +857,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 +873,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/idmapd/idmapd.c b/utils/idmapd/idmapd.c -index f4e083a..56bf67e 100644 ---- a/utils/idmapd/idmapd.c -+++ b/utils/idmapd/idmapd.c -@@ -166,7 +166,7 @@ static uid_t nobodyuid; - static gid_t nobodygid; - - /* Used by conffile.c in libnfs.a */ --char *conf_path; -+char *conf_path = NULL; - - static int - flush_nfsd_cache(char *path, time_t now) -@@ -220,7 +220,6 @@ main(int argc, char **argv) - int ret; - char *progname; - -- conf_path = _PATH_IDMAPDCONF; - nobodyuser = NFS4NOBODY_USER; - nobodygroup = NFS4NOBODY_GROUP; - strlcpy(pipefsdir, PIPEFS_DIR, sizeof(pipefsdir)); -@@ -234,8 +233,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 +249,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(); -+ 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_path = NFS_CONFFILE; - conf_init(); -- 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_path = _PATH_IDMAPDCONF; -+ conf_init(); -+ 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/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/nfsd/nfsd.c b/utils/nfsd/nfsd.c -index 20f4b79..cea850d 100644 ---- a/utils/nfsd/nfsd.c -+++ b/utils/nfsd/nfsd.c -@@ -44,7 +44,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 +69,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; - -@@ -104,10 +107,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 +128,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 +147,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 +188,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 +214,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 +235,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 +391,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/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..0c6766f 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; -@@ -494,6 +496,7 @@ main(int argc, char **argv) - 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 +573,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/nfs-utils-2.1.2-rc3.patch b/nfs-utils-2.1.2-rc3.patch deleted file mode 100644 index aec3cc4..0000000 --- a/nfs-utils-2.1.2-rc3.patch +++ /dev/null @@ -1,2706 +0,0 @@ -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-2.1.2-rc5.patch b/nfs-utils-2.1.2-rc5.patch new file mode 100644 index 0000000..81fdcd1 --- /dev/null +++ b/nfs-utils-2.1.2-rc5.patch @@ -0,0 +1,3599 @@ +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..2d11a52 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,9 @@ 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 void conf_cleanup(void); + 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/misc/tcpwrapper.c b/support/misc/tcpwrapper.c +index 06b0a46..3128053 100644 +--- a/support/misc/tcpwrapper.c ++++ b/support/misc/tcpwrapper.c +@@ -72,6 +72,7 @@ present_address(const struct sockaddr *sap, char *buf, const size_t buflen) + case AF_INET: + if (inet_ntop(AF_INET, &sin->sin_addr, buf, len) != 0) + return; ++ break; + case AF_INET6: + if (inet_ntop(AF_INET6, &sin6->sin6_addr, buf, len) != 0) + return; +diff --git a/support/nfs/atomicio.c b/support/nfs/atomicio.c +index 5e760e6..aa819ca 100644 +--- a/support/nfs/atomicio.c ++++ b/support/nfs/atomicio.c +@@ -42,6 +42,7 @@ ssize_t atomicio(ssize_t(*f) (int, void *, size_t), int fd, void *_s, size_t n) + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; ++ /* FALLTHRU */ + case 0: + if (pos != 0) + return pos; +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..8690218 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,9 +56,11 @@ + #pragma GCC visibility push(hidden) + + static void conf_load_defaults(void); +-static int conf_load(int trans, char *path); ++static char * conf_load(const char *path); + static int conf_set(int , char *, char *, char *, + char *, int , int ); ++static void conf_parse(int trans, char *buf, ++ char **section, char **subsection); + + struct conf_trans { + TAILQ_ENTRY (conf_trans) link; +@@ -73,8 +79,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 +113,6 @@ struct conf_binding { + int is_default; + }; + +-char *conf_path; + LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256]; + + static __inline__ uint8_t +@@ -199,7 +206,6 @@ conf_set_now(char *section, char *arg, char *tag, + node->tag = strdup(tag); + node->value = strdup(value); + node->is_default = is_default; +- + LIST_INSERT_HEAD(&conf_bindings[conf_hash (section)], node, link); + return 0; + } +@@ -209,17 +215,10 @@ conf_set_now(char *section, char *arg, char *tag, + * headers and feed tag-value pairs into our configuration database. + */ + static void +-conf_parse_line(int trans, char *line, size_t sz) ++conf_parse_line(int trans, char *line, int lineno, char **section, char **subsection) + { + char *val, *ptr; +- size_t i; +- size_t j; +- static char *section = 0; +- static char *arg = 0; +- static int ln = 0; + +- /* Lines starting with '#' or ';' are comments. */ +- ln++; + /* Ignore blank lines */ + if (*line == '\0') + return; +@@ -228,123 +227,188 @@ conf_parse_line(int trans, char *line, size_t sz) + while (isblank(*line)) + line++; + ++ /* Lines starting with '#' or ';' are comments. */ + if (*line == '#' || *line == ';') + return; + + /* '[section]' parsing... */ + if (*line == '[') { + line++; ++ ++ if (*section) { ++ free(*section); ++ *section = NULL; ++ } ++ if (*subsection) { ++ free(*subsection); ++ *subsection = NULL; ++ } ++ + /* Strip off any blanks after '[' */ + while (isblank(*line)) + line++; +- for (i = 0; i < sz; i++) { +- if (line[i] == ']') { +- break; +- } +- } +- if (section) +- free(section); +- if (i == sz) { ++ ++ /* find the closing ] */ ++ ptr = strchr(line, ']'); ++ if (ptr == NULL) { + xlog_warn("config file error: line %d: " +- "non-matched ']', ignoring until next section", ln); +- section = 0; ++ "non-matched ']', ignoring until next section", lineno); + return; + } ++ ++ /* just ignore everything after the closing ] */ ++ *(ptr--) = '\0'; ++ + /* Strip off any blanks before ']' */ +- val = line; +- j=0; +- while (*val && !isblank(*val)) +- val++, j++; +- if (*val) +- i = j; +- section = malloc(i+1); +- if (!section) { +- xlog_warn("conf_parse_line: %d: malloc (%lu) failed", ln, +- (unsigned long)i); ++ while (ptr >= line && isblank(*ptr)) ++ *(ptr--)='\0'; ++ ++ /* look for an arg to split from the section name */ ++ val = strchr(line, '"'); ++ if (val != NULL) { ++ ptr = val - 1; ++ *(val++) = '\0'; ++ ++ /* trim away any whitespace before the " */ ++ while (ptr > line && isblank(*ptr)) ++ *(ptr--)='\0'; ++ } ++ ++ /* copy the section name */ ++ *section = strdup(line); ++ if (!*section) { ++ xlog_warn("conf_parse_line: %d: malloc failed", lineno); + return; + } +- strncpy(section, line, i); +- section[i] = '\0'; + +- if (arg) +- free(arg); +- arg = 0; ++ /* there is no arg, we are done */ ++ if (val == NULL) return; + ++ /* check for the closing " */ + ptr = strchr(val, '"'); +- if (ptr == NULL) +- return; +- line = ++ptr; +- while (*ptr && *ptr != '"' && *ptr != ']') +- ptr++; +- if (*ptr == '\0' || *ptr == ']') { ++ if (ptr == NULL) { + xlog_warn("config file error: line %d: " +- "non-matched '\"', ignoring until next section", ln); +- } else { +- *ptr = '\0'; +- arg = strdup(line); +- if (!arg) +- xlog_warn("conf_parse_line: %d: malloc arg failed", ln); ++ "non-matched '\"', ignoring until next section", lineno); ++ return; + } ++ *ptr = '\0'; ++ *subsection = strdup(val); ++ if (!*subsection) ++ xlog_warn("conf_parse_line: %d: malloc arg failed", lineno); + return; + } + + /* Deal with assignments. */ +- for (i = 0; i < sz; i++) { +- if (line[i] == '=') { +- /* If no section, we are ignoring the lines. */ +- if (!section) { ++ ptr = strchr(line, '='); ++ ++ /* not an assignment line */ ++ if (ptr == NULL) { ++ /* Other non-empty lines are weird. */ ++ if (line[strspn(line, " \t")]) + xlog_warn("config file error: line %d: " +- "ignoring line due to no section", ln); +- return; +- } +- line[strcspn (line, " \t=")] = '\0'; +- val = line + i + 1 + strspn (line + i + 1, " \t"); +- +- if (val[0] == '"') { +- val ++; +- j = strcspn(val, "\""); +- val[j] = 0; +- } else if (val[0] == '\'') { +- val ++; +- j = strcspn(val, "'"); +- val[j] = 0; +- } else { +- /* Skip trailing spaces and comments */ +- for (j = 0; val[j]; j++) { +- if ((val[j] == '#' || val[j] == ';') +- && (j == 0 || isspace(val[j-1]))) { +- val[j] = '\0'; +- break; +- } +- } +- while (j && isspace(val[j-1])) +- val[--j] = '\0'; +- } +- if (strcasecmp(line, "include") == 0) +- conf_load(trans, val); +- else +- /* XXX Perhaps should we not ignore errors? */ +- conf_set(trans, section, arg, line, val, 0, 0); ++ "line not empty and not an assignment", lineno); ++ return; ++ } ++ ++ /* If no section, we are ignoring the line. */ ++ if (!*section) { ++ xlog_warn("config file error: line %d: " ++ "ignoring line due to no section", lineno); ++ return; ++ } ++ ++ val = ptr + 1; ++ *(ptr--) = '\0'; ++ ++ /* strip spaces before and after the = */ ++ while (ptr >= line && isblank(*ptr)) ++ *(ptr--)='\0'; ++ while (*val != '\0' && isblank(*val)) ++ val++; ++ ++ if (*val == '"') { ++ val++; ++ ptr = strchr(val, '"'); ++ if (ptr == NULL) { ++ xlog_warn("config file error: line %d: " ++ "unmatched quotes", lineno); ++ return; ++ } ++ *ptr = '\0'; ++ } else ++ if (*val == '\'') { ++ val++; ++ ptr = strchr(val, '\''); ++ if (ptr == NULL) { ++ xlog_warn("config file error: line %d: " ++ "unmatched quotes", lineno); + return; + } ++ *ptr = '\0'; ++ } else { ++ /* Trim any trailing spaces and comments */ ++ if ((ptr=strchr(val, '#'))!=NULL) ++ *ptr = '\0'; ++ if ((ptr=strchr(val, ';'))!=NULL) ++ *ptr = '\0'; ++ ++ ptr = val + strlen(val) - 1; ++ while (ptr > val && isspace(*ptr)) ++ *(ptr--) = '\0'; + } +- /* Other non-empty lines are weird. */ +- i = strspn(line, " \t"); +- if (line[i]) +- xlog_warn("config file error: line %d:", ln); + +- return; ++ if (*line == '\0') { ++ xlog_warn("config file error: line %d: " ++ "missing tag in assignment", lineno); ++ return; ++ } ++ if (*val == '\0') { ++ xlog_warn("config file error: line %d: " ++ "missing value in assignment", lineno); ++ return; ++ } ++ ++ if (strcasecmp(line, "include")==0) { ++ /* load and parse subordinate config files */ ++ char * subconf = conf_load(val); ++ if (subconf == NULL) { ++ xlog_warn("config file error: line %d: " ++ "error loading included config", lineno); ++ return; ++ } ++ ++ /* copy the section data so the included file can inherit it ++ * without accidentally changing it for us */ ++ char * inc_section = NULL; ++ char * inc_subsection = NULL; ++ if (*section != NULL) { ++ inc_section = strdup(*section); ++ if (*subsection != NULL) ++ inc_subsection = strdup(*subsection); ++ } ++ ++ conf_parse(trans, subconf, &inc_section, &inc_subsection); ++ ++ if (inc_section) free(inc_section); ++ if (inc_subsection) free(inc_subsection); ++ free(subconf); ++ } else { ++ /* XXX Perhaps should we not ignore errors? */ ++ conf_set(trans, *section, *subsection, line, val, 0, 0); ++ } + } + + /* Parse the mapped configuration file. */ + static void +-conf_parse(int trans, char *buf, size_t sz) ++conf_parse(int trans, char *buf, char **section, char **subsection) + { + char *cp = buf; +- char *bufend = buf + sz; ++ char *bufend = NULL; + char *line; ++ int lineno = 0; + + line = cp; ++ bufend = buf + strlen(buf); + while (cp < bufend) { + if (*cp == '\n') { + /* Check for escaped newlines. */ +@@ -352,7 +416,8 @@ conf_parse(int trans, char *buf, size_t sz) + *(cp - 1) = *cp = ' '; + else { + *cp = '\0'; +- conf_parse_line(trans, line, cp - line); ++ lineno++; ++ conf_parse_line(trans, line, lineno, section, subsection); + line = cp + 1; + } + } +@@ -369,33 +434,21 @@ 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) ++static char * ++conf_load(const char *path) + { + struct stat sb; + if ((stat (path, &sb) == 0) || (errno != ENOENT)) { +- char *new_conf_addr; ++ char *new_conf_addr = NULL; + size_t sz = sb.st_size; + int fd = open (path, O_RDONLY, 0); + + if (fd == -1) { + xlog_warn("conf_reinit: open (\"%s\", O_RDONLY) failed", path); +- return -1; ++ return NULL; + } + +- new_conf_addr = malloc(sz); ++ new_conf_addr = malloc(sz+1); + if (!new_conf_addr) { + xlog_warn("conf_reinit: malloc (%lu) failed", (unsigned long)sz); + goto fail; +@@ -410,42 +463,103 @@ conf_load(int trans, char *path) + close(fd); + + /* XXX Should we not care about errors and rollback? */ +- conf_parse(trans, new_conf_addr, sz); +- free(new_conf_addr); +- return 0; ++ new_conf_addr[sz] = '\0'; ++ return new_conf_addr; + fail: + close(fd); +- free(new_conf_addr); ++ if (new_conf_addr) free(new_conf_addr); ++ } ++ return NULL; ++} ++ ++/* remove and free up any existing config state */ ++static void conf_free_bindings(void) ++{ ++ unsigned int i; ++ for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) { ++ struct conf_binding *cb, *next; ++ ++ cb = LIST_FIRST(&conf_bindings[i]); ++ for (; cb; cb = next) { ++ next = LIST_NEXT(cb, link); ++ LIST_REMOVE(cb, link); ++ free(cb->section); ++ free(cb->arg); ++ free(cb->tag); ++ free(cb->value); ++ free(cb); ++ } ++ LIST_INIT(&conf_bindings[i]); + } +- return -1; + } + + /* 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; ++ char * conf_data; + + trans = conf_begin(); +- if (conf_load(trans, conf_path) < 0) ++ conf_data = conf_load(conf_file); ++ ++ if (conf_data == NULL) + return; + + /* Load default configuration values. */ + conf_load_defaults(); + ++ /* Parse config contents into the transaction queue */ ++ char *section = NULL; ++ char *subsection = NULL; ++ conf_parse(trans, conf_data, §ion, &subsection); ++ if (section) free(section); ++ if (subsection) free(subsection); ++ free(conf_data); ++ + /* Free potential existing configuration. */ +- for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) { +- cb = LIST_FIRST (&conf_bindings[i]); +- for (; cb; cb = LIST_FIRST (&conf_bindings[i])) +- conf_remove_now(cb->section, cb->tag); +- } ++ conf_free_bindings(); + ++ /* Apply the new configuration values */ + conf_end(trans, 1); + 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); ++} ++ ++/* ++ * Empty the config and free up any used memory ++ */ ++void ++conf_cleanup(void) ++{ ++ conf_free_bindings(); ++ ++ struct conf_trans *node, *next; ++ for (node = TAILQ_FIRST(&conf_trans_queue); node; node = next) { ++ next = TAILQ_NEXT(node, link); ++ TAILQ_REMOVE (&conf_trans_queue, node, link); ++ if (node->section) free(node->section); ++ if (node->arg) free(node->arg); ++ if (node->tag) free(node->tag); ++ if (node->value) free(node->value); ++ free (node); ++ } ++ TAILQ_INIT(&conf_trans_queue); ++} ++ + /* + * Return the numeric value denoted by TAG in section SECTION or DEF + * if that tag does not exist. +@@ -533,7 +647,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/support/nsm/rpc.c b/support/nsm/rpc.c +index 4e5f40e..0a8e56f 100644 +--- a/support/nsm/rpc.c ++++ b/support/nsm/rpc.c +@@ -38,6 +38,7 @@ + #include + #include + ++#include + #include + #include + #include +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/nfs.systemd.man b/systemd/nfs.systemd.man +index 01801eb..46b476a 100644 +--- a/systemd/nfs.systemd.man ++++ b/systemd/nfs.systemd.man +@@ -79,7 +79,7 @@ unit should be enabled. + Several other units which might be considered to be optional, such as + .I rpc-gssd.service + are careful to only start if the required configuration file exists. +-.I rpc-gsdd.service ++.I rpc-gssd.service + will not start if the + .I krb5.keytab + file does not exist (typically in +@@ -120,10 +120,11 @@ be needed to reduce system load to an absolute minimum, or to reduce + attack surface by not running daemons that are not absolutely + required. + .PP +-Two particular services which this can apply to are +-.I rpcbind ++Three particular services which this can apply to are ++.IR rpcbind , ++.IR idmapd , + and +-.IR idmapd . ++.IR rpc-gssd . + .I rpcbind + is not part of the + .I nfs-utils +@@ -155,6 +156,15 @@ is not needed and not wanted, it can be masked with + .RS + .B systemctl mask idmapd + .RE ++.I rpc-gssd ++is assumed to be needed if the ++.I krb5.keytab ++file is present. If a site needs this file present but does not want ++.I rpc-gssd ++running, it can be masked with ++.RS ++.B systemctl mask rpc-gssd ++.RE + .SH FILES + /etc/nfs.conf + .br +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/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py +index 88ccdae..a68d702 100644 +--- a/tools/mountstats/mountstats.py ++++ b/tools/mountstats/mountstats.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/python + # -*- python-mode -*- + """Parse /proc/self/mountstats and display it in human readable form + """ +diff --git a/tools/rpcdebug/rpcdebug.c b/tools/rpcdebug/rpcdebug.c +index 18b1622..68206cc 100644 +--- a/tools/rpcdebug/rpcdebug.c ++++ b/tools/rpcdebug/rpcdebug.c +@@ -74,7 +74,8 @@ main(int argc, char **argv) + opt_c = 1; + break; + case 'h': +- usage(0, module); ++ usage(0, module); /* usage does not return */ ++ break; + case 'm': + module = optarg; + break; +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..29bafb2 100644 +--- a/utils/blkmapd/device-discovery.c ++++ b/utils/blkmapd/device-discovery.c +@@ -26,6 +26,7 @@ + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + ++#include + #include + #include + #include +@@ -50,21 +51,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 +374,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 +388,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 +453,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 +524,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/blkmapd/device-inq.c b/utils/blkmapd/device-inq.c +index 0062a8f..c7952c3 100644 +--- a/utils/blkmapd/device-inq.c ++++ b/utils/blkmapd/device-inq.c +@@ -216,6 +216,7 @@ struct bl_serial *bldev_read_serial(int fd, const char *filename) + if ((dev_id->len != 8) && (dev_id->len != 12) && + (dev_id->len != 16)) + break; ++ /* FALLTHRU */ + case 3: /* NAA */ + /* TODO: NAA validity judgement too complicated, + * so just ingore it here. +@@ -224,6 +225,7 @@ struct bl_serial *bldev_read_serial(int fd, const char *filename) + BL_LOG_ERR("Binary code_set expected\n"); + break; + } ++ /* FALLTHRU */ + case 0: /* vendor specific */ + case 1: /* T10 vendor identification */ + current_id = dev_id->ids & 0xf; +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..e4b39ef 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, +@@ -264,7 +260,7 @@ default_value(char *mopt) + } + } else if (strncasecmp(field, "vers", strlen("vers")) == 0) { + if ((options = po_split(field)) != NULL) { +- if (!nfs_nfs_version(options, &config_default_vers)) { ++ if (!nfs_nfs_version("nfs", options, &config_default_vers)) { + xlog_warn("Unable to set default version: %s", + strerror(errno)); + +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/mount_libmount.c b/utils/mount/mount_libmount.c +index 1f01f7f..2d40657 100644 +--- a/utils/mount/mount_libmount.c ++++ b/utils/mount/mount_libmount.c +@@ -188,6 +188,7 @@ static int umount_main(struct libmnt_context *cxt, int argc, char **argv) + }; + + mnt_context_init_helper(cxt, MNT_ACT_UMOUNT, 0); ++ mnt_context_disable_canonicalize(cxt, 1); + + while ((c = getopt_long (argc, argv, "fvnrlh", longopts, NULL)) != -1) { + +diff --git a/utils/mount/network.c b/utils/mount/network.c +index 7dceb2d..8ab5be8 100644 +--- a/utils/mount/network.c ++++ b/utils/mount/network.c +@@ -33,12 +33,19 @@ + #include + #include + #include ++#include + + #include + #include + #include + #include +-#include ++#if defined(__GLIBC__) && (__GLIBC__ < 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 24) ++/* Cannot safely include linux/in6.h in old glibc, so hardcode the needed values */ ++# define IPV6_PREFER_SRC_PUBLIC 2 ++# define IPV6_ADDR_PREFERENCES 72 ++#else ++# include ++#endif + #include + #include + #include +@@ -93,7 +100,6 @@ static const char *nfs_version_opttbl[] = { + "v4", + "vers", + "nfsvers", +- "minorversion", + NULL, + }; + +@@ -804,6 +810,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); +@@ -1233,6 +1240,7 @@ nfs_nfs_program(struct mount_options *options, unsigned long *program) + *program = tmp; + return 1; + } ++ /* FALLTHRU */ + case PO_BAD_VALUE: + nfs_error(_("%s: invalid value for 'nfsprog=' option"), + progname); +@@ -1252,7 +1260,7 @@ nfs_nfs_program(struct mount_options *options, unsigned long *program) + * or FALSE if the option was specified with an invalid value. + */ + int +-nfs_nfs_version(struct mount_options *options, struct nfs_version *version) ++nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *version) + { + char *version_key, *version_val, *cptr; + int i, found = 0; +@@ -1267,10 +1275,11 @@ nfs_nfs_version(struct mount_options *options, struct nfs_version *version) + } + } + +- if (!found) ++ if (!found && strcmp(type, "nfs4") == 0) ++ version_val = type + 3; ++ else if (!found) + return 1; +- +- if (i <= 2 ) { ++ else if (i <= 2 ) { + /* v2, v3, v4 */ + version_val = version_key + 1; + version->v_mode = V_SPECIFIC; +@@ -1282,17 +1291,19 @@ nfs_nfs_version(struct mount_options *options, struct nfs_version *version) + if (!version_val) + goto ret_error; + +- if (!(version->major = strtol(version_val, &cptr, 10))) ++ version->major = strtol(version_val, &cptr, 10); ++ if (cptr == version_val || (*cptr && *cptr != '.')) + goto ret_error; +- +- if (strcmp(nfs_version_opttbl[i], "minorversion") == 0) { ++ if (version->major == 4 && *cptr != '.' && ++ (version_val = po_get(options, "minorversion")) != NULL) { ++ version->minor = strtol(version_val, &cptr, 10); ++ i = -1; ++ if (*cptr) ++ goto ret_error; + version->v_mode = V_SPECIFIC; +- version->minor = version->major; +- version->major = 4; + } else if (version->major < 4) + version->v_mode = V_SPECIFIC; +- +- if (*cptr == '.') { ++ else if (*cptr == '.') { + version_val = ++cptr; + if (!(version->minor = strtol(version_val, &cptr, 10)) && cptr == version_val) + goto ret_error; +@@ -1306,7 +1317,10 @@ nfs_nfs_version(struct mount_options *options, struct nfs_version *version) + return 1; + + ret_error: +- if (i <= 2 ) { ++ if (i < 0) { ++ nfs_error(_("%s: parsing error on 'minorversion=' option"), ++ progname); ++ } else if (i <= 2 ) { + nfs_error(_("%s: parsing error on 'v' option"), + progname); + } else if (i == 3 ) { +@@ -1381,6 +1395,7 @@ nfs_nfs_port(struct mount_options *options, unsigned long *port) + *port = tmp; + return 1; + } ++ /* FALLTHRU */ + case PO_BAD_VALUE: + nfs_error(_("%s: invalid value for 'port=' option"), + progname); +@@ -1476,6 +1491,7 @@ nfs_mount_program(struct mount_options *options, unsigned long *program) + *program = tmp; + return 1; + } ++ /* FALLTHRU */ + case PO_BAD_VALUE: + nfs_error(_("%s: invalid value for 'mountprog=' option"), + progname); +@@ -1507,6 +1523,7 @@ nfs_mount_version(struct mount_options *options, unsigned long *version) + *version = tmp; + return 1; + } ++ /* FALLTHRU */ + case PO_BAD_VALUE: + nfs_error(_("%s: invalid value for 'mountvers=' option"), + progname); +@@ -1573,6 +1590,7 @@ nfs_mount_port(struct mount_options *options, unsigned long *port) + *port = tmp; + return 1; + } ++ /* FALLTHRU */ + case PO_BAD_VALUE: + nfs_error(_("%s: invalid value for 'mountport=' option"), + progname); +@@ -1638,10 +1656,11 @@ 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; +- if (!nfs_nfs_version(options, &version)) ++ if (!nfs_nfs_version("nfs", options, &version)) + return 0; + if (version.v_mode == V_DEFAULT) + nfs_pmap->pm_vers = 0; +diff --git a/utils/mount/network.h b/utils/mount/network.h +index 9cc5dec..ecaac33 100644 +--- a/utils/mount/network.h ++++ b/utils/mount/network.h +@@ -72,7 +72,7 @@ struct nfs_version { + + int nfs_nfs_proto_family(struct mount_options *options, sa_family_t *family); + int nfs_mount_proto_family(struct mount_options *options, sa_family_t *family); +-int nfs_nfs_version(struct mount_options *options, struct nfs_version *version); ++int nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *version); + int nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol); + + int nfs_options2pmap(struct mount_options *, +diff --git a/utils/mount/nfs4mount.c b/utils/mount/nfs4mount.c +index 028e7cd..89629ed 100644 +--- a/utils/mount/nfs4mount.c ++++ b/utils/mount/nfs4mount.c +@@ -444,6 +444,7 @@ int nfs4mount(const char *spec, const char *node, int flags, + case RPC_SYSTEMERROR: + if (errno == ETIMEDOUT) + break; ++ /* FALLTHRU */ + default: + rpc_mount_errors(hostname, 0, bg); + goto fail; +diff --git a/utils/mount/nfsmount.c b/utils/mount/nfsmount.c +index 930622d..ae4a3da 100644 +--- a/utils/mount/nfsmount.c ++++ b/utils/mount/nfsmount.c +@@ -683,6 +683,7 @@ nfsmount(const char *spec, const char *node, int flags, + case RPC_SYSTEMERROR: + if (errno == ETIMEDOUT) + break; ++ /* FALLTHRU */ + default: + rpc_mount_errors(*nfs_server.hostname, 0, bg); + goto fail; +diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c +index de284f2..e16fb6a 100644 +--- a/utils/mount/nfsumount.c ++++ b/utils/mount/nfsumount.c +@@ -180,7 +180,7 @@ static int nfs_umount_is_vers4(const struct mntentchn *mc) + options = po_split(pmc->m.mnt_opts); + if (options != NULL) { + struct nfs_version version; +- int rc = nfs_nfs_version(options, &version); ++ int rc = nfs_nfs_version("nfs", options, &version); + po_destroy(options); + if (rc && version.major == 4) + goto out_nfs4; +diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c +index 387d734..1d30d34 100644 +--- a/utils/mount/stropts.c ++++ b/utils/mount/stropts.c +@@ -73,6 +73,13 @@ + #define NFS_DEF_BG_TIMEOUT_MINUTES (10000u) + #endif + ++#ifndef NFS_DEFAULT_MAJOR ++#define NFS_DEFAULT_MAJOR 4 ++#endif ++#ifndef NFS_DEFAULT_MINOR ++#define NFS_DEFAULT_MINOR 2 ++#endif ++ + extern int nfs_mount_data_version; + extern char *progname; + extern int verbose; +@@ -80,8 +87,8 @@ extern int sloppy; + + struct nfsmount_info { + const char *spec, /* server:/path */ +- *node, /* mounted-on dir */ +- *type; /* "nfs" or "nfs4" */ ++ *node; /* mounted-on dir */ ++ char *type; /* "nfs" or "nfs4" */ + char *hostname; /* server's hostname */ + struct addrinfo *address; /* server's addresses */ + sa_family_t family; /* Address family */ +@@ -112,20 +119,28 @@ static void nfs_default_version(struct nfsmount_info *mi) + if (mi->version.v_mode == V_DEFAULT && + config_default_vers.v_mode != V_DEFAULT) { + mi->version.major = config_default_vers.major; +- mi->version.minor = config_default_vers.minor; ++ if (config_default_vers.v_mode == V_SPECIFIC) ++ mi->version.minor = config_default_vers.minor; ++ else ++ mi->version.minor = NFS_DEFAULT_MINOR; + return; + } + + if (mi->version.v_mode == V_GENERAL) { + if (config_default_vers.v_mode != V_DEFAULT && +- mi->version.major == config_default_vers.major) +- mi->version.minor = config_default_vers.minor; ++ mi->version.major == config_default_vers.major) { ++ if (config_default_vers.v_mode == V_SPECIFIC) ++ mi->version.minor = config_default_vers.minor; ++ else ++ mi->version.minor = NFS_DEFAULT_MINOR; ++ } else ++ mi->version.minor = NFS_DEFAULT_MINOR; + return; + } + + #endif /* MOUNT_CONFIG */ +- mi->version.major = 4; +- mi->version.minor = 2; ++ mi->version.major = NFS_DEFAULT_MAJOR; ++ mi->version.minor = NFS_DEFAULT_MINOR; + } + + /* +@@ -312,12 +327,9 @@ static int nfs_append_sloppy_option(struct mount_options *options) + + static int nfs_set_version(struct nfsmount_info *mi) + { +- if (!nfs_nfs_version(mi->options, &mi->version)) ++ if (!nfs_nfs_version(mi->type, mi->options, &mi->version)) + return 0; + +- if (strncmp(mi->type, "nfs4", 4) == 0) +- mi->version.major = 4; +- + /* + * Before 2.6.32, the kernel NFS client didn't + * support "-t nfs vers=4" mounts, so NFS version +@@ -517,6 +529,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. +@@ -705,7 +721,7 @@ static int nfs_do_mount_v4(struct nfsmount_info *mi, + { + struct mount_options *options = po_dup(mi->options); + int result = 0; +- char version_opt[16]; ++ char version_opt[32]; + char *extra_opts = NULL; + + if (!options) { +@@ -727,13 +743,25 @@ static int nfs_do_mount_v4(struct nfsmount_info *mi, + } + + if (mi->version.v_mode != V_SPECIFIC) { +- if (mi->version.v_mode == V_GENERAL) +- snprintf(version_opt, sizeof(version_opt) - 1, +- "vers=%lu", mi->version.major); +- else +- snprintf(version_opt, sizeof(version_opt) - 1, +- "vers=%lu.%lu", mi->version.major, +- mi->version.minor); ++ char *fmt; ++ switch (mi->version.minor) { ++ /* Old kernels don't support the new "vers=x.y" ++ * option, but do support old versions of NFS4. ++ * So use the format that is most widely understood. ++ */ ++ case 0: ++ fmt = "vers=%lu"; ++ break; ++ case 1: ++ fmt = "vers=%lu,minorversion=%lu"; ++ break; ++ default: ++ fmt = "vers=%lu.%lu"; ++ break; ++ } ++ snprintf(version_opt, sizeof(version_opt) - 1, ++ fmt, mi->version.major, ++ mi->version.minor); + + if (po_append(options, version_opt) == PO_FAILED) { + errno = EINVAL; +@@ -834,9 +862,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 +883,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); + } + +@@ -1165,7 +1199,7 @@ static int nfsmount_start(struct nfsmount_info *mi) + * + * Returns a valid mount command exit code. + */ +-int nfsmount_string(const char *spec, const char *node, const char *type, ++int nfsmount_string(const char *spec, const char *node, char *type, + int flags, char **extra_opts, int fake, int child) + { + struct nfsmount_info mi = { +diff --git a/utils/mount/stropts.h b/utils/mount/stropts.h +index 37316eb..6acd2ac 100644 +--- a/utils/mount/stropts.h ++++ b/utils/mount/stropts.h +@@ -24,7 +24,7 @@ + #ifndef _NFS_UTILS_MOUNT_STROPTS_H + #define _NFS_UTILS_MOUNT_STROPTS_H + +-int nfsmount_string(const char *, const char *, const char *, int, ++int nfsmount_string(const char *, const char *, char *, int, + char **, int, int); + + #endif /* _NFS_UTILS_MOUNT_STROPTS_H */ +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/cache.c b/utils/mountd/cache.c +index ca6c84f..e49300d 100644 +--- a/utils/mountd/cache.c ++++ b/utils/mountd/cache.c +@@ -11,6 +11,7 @@ + #include + #endif + ++#include + #include + #include + #include +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..f973203 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:stTituUrG:L:", longopts, NULL)) != EOF) { + switch(c) { + case 'd': + xlog_config(D_ALL, 1); +@@ -179,14 +186,19 @@ 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; + } ++ /* FALLTHRU */ + case 3: + case 2: + NFSCTL_VERUNSET(versbits, c); +@@ -201,14 +213,15 @@ 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; ++ /* FALLTHRU */ + case 3: + case 2: + NFSCTL_VERSET(versbits, c); +@@ -222,9 +235,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; +@@ -244,6 +263,7 @@ main(int argc, char **argv) + break; + default: + fprintf(stderr, "Invalid argument: '%c'\n", c); ++ /* FALLTHRU */ + case 'h': + usage(progname); + } +@@ -372,9 +392,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..0baaa3e 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) +@@ -581,6 +579,7 @@ main(int argc, char **argv) + switch (arg) { + case 'd': + xlog_config(D_ALL, 1); ++ break; + case 'f': + xlog_syslog(0); + xlog_stderr(1); +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 2876f7b..4728d53 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: 6.rc4%{?dist}.1 +Release: 6.rc5%{?dist} Epoch: 1 # group all 32bit related archs @@ -15,8 +15,7 @@ Source3: nfs-utils_env.sh Source4: lockd.conf Source5: 24-nfs-server.conf -Patch001: nfs-utils-2.1.2-rc4.patch -Patch002: nfs-utils-2.1.1-rpc-include.patch +Patch001: nfs-utils-2.1.2-rc5.patch Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch @@ -76,7 +75,6 @@ This package also contains the mount.nfs and umount.nfs program. %setup -q %patch001 -p1 -%patch002 -p1 %patch100 -p1 %patch101 -p1 @@ -290,6 +288,9 @@ fi /sbin/umount.nfs4 %changelog +* Thu Jul 27 2017 Steve Dickson 2.1.1-6.rc5 +- Updated to the latest RC releease: nfs-utils-2-1-2-rc5 + * Wed Jul 26 2017 Fedora Release Engineering - 1:2.1.1-6.rc4.1 - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild