From b8c30c6b2ff053e58be3bb0c73f30f6150461460 Mon Sep 17 00:00:00 2001 From: Steve Dickson Date: Apr 18 2023 11:02:26 +0000 Subject: Updated to the latest RC release: nfs-utils-2-6-3-rc8 (bz 2184788) Signed-off-by: Steve Dickson --- diff --git a/nfs-utils.2.6.3-rc8.patch b/nfs-utils.2.6.3-rc8.patch new file mode 100644 index 0000000..5dbbbfd --- /dev/null +++ b/nfs-utils.2.6.3-rc8.patch @@ -0,0 +1,1587 @@ +diff --git a/.gitignore b/.gitignore +index df791a83..682153d5 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -86,3 +86,5 @@ systemd/rpc-gssd.service + cscope.* + # generic editor backup et al + *~ ++# file generated by ctags ++tags +diff --git a/NEWS b/NEWS +index e70ae8ab..77872c5a 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,32 +1,32 @@ + Significant changes for nfs-utils 1.1.0 - March/April 2007 + +- - rpc.lockd is gone. One 3 old kernel releases need it. +- - rpc.rquotad is gone. Use the one from the 'quota' package. +- Everone else does. ++ - rpc.lockd is gone. One 3 old kernel releases need it. ++ - rpc.rquotad is gone. Use the one from the 'quota' package. ++ Everyone else does. + - /sbin/{u,}mount.nfs{,4} are now installed so 'mount' will + use these to mount nfs filesystems instead of internal code. + + mount.nfs will check for 'statd' to be running when mounting +- a filesystem which requires it. If it is not running it will ++ a filesystem which requires it. If it is not running it will + run "/usr/sbin/start-statd" to try to start it. + If statd is not running and cannot be started, mount.nfs will + refuse to mount the filesystem and will suggest the 'nolock' + option. + - Substantial changes to statd + + The 'notify' process that must happen at boot has been split +- into a separate program "sm-notify". It ensures that it +- only runs once even if you restart statd. This is correct ++ into a separate program "sm-notify". It ensures that it ++ only runs once even if you restart statd. This is correct + behaviour. + + statd stores state in the files in /var/lib/nfs/sm/ so that + if you kill and restart it, it will restore that state and + continue working correctly. + + statd makes more use of DNS lookup and should handle +- multi-homed peers better. In particular, files in ++ multi-homed peers better. In particular, files in + /var/lib/nfs/sm/ are named with the Full Qualified Domain Name + if available. + - If you export a directory as 'crossmnt', all filesystems + mounted beneath are automatically exported with the same + options (unless explicitly exported with different options). +- - subtree_check is no-longer the default. The default is now ++ - subtree_check is no-longer the default. The default is now + no_subtree_check. + - By default the system 'rpcgen' is used while building + nfs-utils rather than the internal one. +@@ -43,14 +43,14 @@ Significant changes for nfs-utils 1.1.0 - March/April 2007 + + - A new option, -n, was added to rpc.gssd which specifies that + accesses by root should not use 'machine credentials' when +- accessing NFS file systems mounted with Kerberos. Using this ++ accessing NFS file systems mounted with Kerberos. Using this + option allows the root user to access the NFS space using any + Kerberos principal, rather than always using the machine +- credentials. However, its use also requires that root manually ++ credentials. However, its use also requires that root manually + authenticate before attempting a mount with Kerberos. + + When rpc.gssd uses machine credentials, the selection algorithm has +- been changed. Instead of simply using the first "nfs/*" key in the ++ been changed. Instead of simply using the first "nfs/*" key in the + keytab, the keytab is now searched for keys in the following + defined order: + +diff --git a/README b/README +index 5e982409..3b0e771f 100644 +--- a/README ++++ b/README +@@ -25,7 +25,7 @@ Unpack the sources and run these commands: + # ./configure + # make + +-To install binaries and documenation, run this command: ++To install binaries and documentation, run this command: + + # make install + +@@ -40,7 +40,7 @@ Updating to the latest head after you've already got it. + + git pull + +-Building requires that autotools be installed. To invoke them ++Building requires that autotools be installed. To invoke them + simply + + sh autogen.sh +@@ -70,7 +70,7 @@ scripts can be written to work correctly. + 3.1. SERVER STARTUP + + +- A/ mount -t nfsd /proc/fs/nfsd ++ A/ mount -t nfsd nfsd /proc/fs/nfsd + This filesystem needs to be mount before most daemons, + particularly exportfs, mountd, svcgssd, idmapd. + It could be mounted once, or the script that starts each daemon +@@ -95,27 +95,27 @@ scripts can be written to work correctly. + + D/ rpc.statd --no-notify + It is best if statd is started before nfsd though this isn't +- critical. Certainly it should be at most a few seconds after ++ critical. Certainly, it should be at most a few seconds after + nfsd. + When nfsd starts it will start lockd. If lockd then receives a +- lock request it will communicate with statd. If statd is not ++ lock request, it will communicate with statd. If statd is not + running lockd will retry, but it won't wait forever for a + reply. + Note that if statd is started before nfsd, the --no-notify +- option must be used. If notify requests are sent out before ++ option must be used. If notify requests are sent out before + nfsd start, clients may try to reclaim locks and, on finding + that lockd isn't running, they will give up and never reclaim + the lock. + rpc.statd is only needed for NFSv2 and NFSv3 support. + + E/ rpc.nfsd +- Starting nfsd will automatically start lockd. The nfs server ++ Starting nfsd will automatically start lockd. The nfs server + will now be fully active and respond to any requests from + clients. + + F/ sm-notify + This will notify any client which might have locks from before +- a reboot to try to reclaim their locks. This should start ++ a reboot to try to reclaim their locks. This should start + immediately after rpc.nfsd is started so that clients have a + chance to reclaim locks within the 90 second grace period. + sm-notify is only needed for NFSv2 and NFSv3 support. +diff --git a/configure.ac b/configure.ac +index f1c46c5c..7672a760 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -71,18 +71,6 @@ AC_ARG_WITH(systemd, + AM_CONDITIONAL(INSTALL_SYSTEMD, [test "$use_systemd" = 1]) + AC_SUBST(unitdir) + +-modprobedir=/usr/lib/modprobe.d +-AC_ARG_WITH(modprobedir, +- [AS_HELP_STRING([--with-modprobedir@<:@=modprobe-dir-path@:>@],[install modprobe config files @<:@Default: /usr/lib/modprobe.d@:>@])], +- if test "$withval" != "no" ; then +- modprobedir=$withval +- else +- modprobedir= +- fi +- ) +- AM_CONDITIONAL(INSTALL_MODPROBEDIR, [test -n "$modprobedir"]) +- AC_SUBST(modprobedir) +- + AC_ARG_ENABLE(nfsv4, + [AS_HELP_STRING([--disable-nfsv4],[disable support for NFSv4 @<:@default=no@:>@])], + enable_nfsv4=$enableval, +@@ -249,6 +237,16 @@ AC_ARG_ENABLE(nfsdcld, + enable_nfsdcld=$enableval, + enable_nfsdcld="yes") + ++AC_ARG_ENABLE(nfsrahead, ++ [AS_HELP_STRING([--disable-nfsrahead],[disable nfsrahead command @<:@default=no@:>@])], ++ enable_nfsrahead=$enableval, ++ enable_nfsrahead="yes") ++ AM_CONDITIONAL(CONFIG_NFSRAHEAD, [test "$enable_nfsrahead" = "yes" ]) ++ if test "$enable_nfsrahead" = yes; then ++ dnl Check for -lmount ++ PKG_CHECK_MODULES([LIBMOUNT], [mount]) ++ fi ++ + AC_ARG_ENABLE(nfsdcltrack, + [AS_HELP_STRING([--disable-nfsdcltrack],[disable NFSv4 clientid tracking programs @<:@default=no@:>@])], + enable_nfsdcltrack=$enableval, +@@ -678,12 +676,12 @@ AC_SUBST([AM_CFLAGS], ["$my_am_cflags $flg1 $flg2 $flg3 $flg4 $flg5"]) + # Make sure that $ACLOCAL_FLAGS are used during a rebuild + AC_SUBST([ACLOCAL_AMFLAGS], ["-I $ac_macro_dir \$(ACLOCAL_FLAGS)"]) + +-# make _sysconfdir available for substituion in config files ++# make _sysconfdir available for substitution in config files + # 2 "evals" needed late to expand variable names. + AC_SUBST([_sysconfdir]) + AC_CONFIG_COMMANDS_PRE([eval eval _sysconfdir=$sysconfdir]) + +-# make _statedir available for substituion in config files ++# make _statedir available for substitution in config files + # 2 "evals" needed late to expand variable names. + AC_SUBST([_statedir]) + AC_CONFIG_COMMANDS_PRE([eval eval _statedir=$statedir]) +@@ -695,7 +693,7 @@ else + fi + AC_SUBST(rpc_pipefsmount) + +-# make _rpc_pipefsmount available for substituion in config files ++# make _rpc_pipefsmount available for substitution in config files + # 2 "evals" needed late to expand variable names. + AC_SUBST([_rpc_pipefsmount]) + AC_CONFIG_COMMANDS_PRE([eval eval _rpc_pipefsmount=$rpc_pipefsmount]) +diff --git a/support/export/auth.c b/support/export/auth.c +index 03ce4b8a..2d7960f1 100644 +--- a/support/export/auth.c ++++ b/support/export/auth.c +@@ -82,7 +82,7 @@ check_useipaddr(void) + } + + unsigned int +-auth_reload() ++auth_reload(void) + { + struct stat stb; + static ino_t last_inode; +diff --git a/support/export/cache.c b/support/export/cache.c +index a5823e92..0a37703b 100644 +--- a/support/export/cache.c ++++ b/support/export/cache.c +@@ -346,27 +346,27 @@ static int uuid_by_path(char *path, int type, size_t uuidlen, char *uuid) + + /* Possible sources of uuid are + * - blkid uuid +- * - statfs64 uuid ++ * - statfs uuid + * +- * On some filesystems (e.g. vfat) the statfs64 uuid is simply an ++ * On some filesystems (e.g. vfat) the statfs uuid is simply an + * encoding of the device that the filesystem is mounted from, so + * it we be very bad to use that (as device numbers change). blkid + * must be preferred. +- * On other filesystems (e.g. btrfs) the statfs64 uuid contains ++ * On other filesystems (e.g. btrfs) the statfs uuid contains + * important info that the blkid uuid cannot contain: This happens + * when multiple subvolumes are exported (they have the same +- * blkid uuid but different statfs64 uuids). ++ * blkid uuid but different statfs uuids). + * We rely on get_uuid_blkdev *knowing* which is which and not returning +- * a uuid for filesystems where the statfs64 uuid is better. ++ * a uuid for filesystems where the statfs uuid is better. + * + */ +- struct statfs64 st; ++ struct statfs st; + char fsid_val[17]; + const char *blkid_val = NULL; + const char *val; + int rc; + +- rc = nfsd_path_statfs64(path, &st); ++ rc = nfsd_path_statfs(path, &st); + + if (type == 0 && rc == 0) { + const unsigned long *bad; +@@ -410,12 +410,16 @@ static char *next_mnt(void **v, char *p) + *v = f; + } else + f = *v; +- while ((me = getmntent(f)) != NULL && l > 1) { ++ while ((me = getmntent(f)) != NULL && l >= 1) { + char *mnt_dir = nfsd_path_strip_root(me->mnt_dir); + + if (!mnt_dir) + continue; + ++ /* Everything below "/" is a proper sub-mount */ ++ if (strcmp(p, "/") == 0) ++ return mnt_dir; ++ + if (strncmp(mnt_dir, p, l) == 0 && mnt_dir[l] == '/') + return mnt_dir; + } +@@ -932,6 +936,7 @@ static void write_fsloc(char **bp, int *blen, struct exportent *ep) + release_replicas(servers); + } + #endif ++ + static void write_secinfo(char **bp, int *blen, struct exportent *ep, int flag_mask) + { + struct sec_entry *p; +@@ -949,7 +954,20 @@ static void write_secinfo(char **bp, int *blen, struct exportent *ep, int flag_m + qword_addint(bp, blen, p->flav->fnum); + qword_addint(bp, blen, p->flags & flag_mask); + } ++} ++ ++static void write_xprtsec(char **bp, int *blen, struct exportent *ep) ++{ ++ struct xprtsec_entry *p; ++ ++ for (p = ep->e_xprtsec; p->info; p++); ++ if (p == ep->e_xprtsec) ++ return; + ++ qword_add(bp, blen, "xprtsec"); ++ qword_addint(bp, blen, p - ep->e_xprtsec); ++ for (p = ep->e_xprtsec; p->info; p++) ++ qword_addint(bp, blen, p->info->number); + } + + static int dump_to_cache(int f, char *buf, int blen, char *domain, +@@ -992,6 +1010,7 @@ static int dump_to_cache(int f, char *buf, int blen, char *domain, + qword_add(&bp, &blen, "uuid"); + qword_addhex(&bp, &blen, u, 16); + } ++ write_xprtsec(&bp, &blen, exp); + xlog(D_AUTH, "granted access to %s for %s", + path, *domain == '$' ? domain+1 : domain); + } else { +diff --git a/support/export/client.c b/support/export/client.c +index ea4f89d3..79164fef 100644 +--- a/support/export/client.c ++++ b/support/export/client.c +@@ -699,6 +699,9 @@ check_netgroup(const nfs_client *clp, const struct addrinfo *ai) + + /* check whether the IP itself is in the netgroup */ + ip = calloc(INET6_ADDRSTRLEN, 1); ++ if (ip == NULL) ++ goto out; ++ + if (inet_ntop(ai->ai_family, &(((struct sockaddr_in *)ai->ai_addr)->sin_addr), ip, INET6_ADDRSTRLEN) == ip) { + if (innetgr(netgroup, ip, NULL, NULL)) { + free(hname); +diff --git a/support/export/v4clients.c b/support/export/v4clients.c +index 5f15b614..32302512 100644 +--- a/support/export/v4clients.c ++++ b/support/export/v4clients.c +@@ -26,7 +26,7 @@ void v4clients_init(void) + { + struct stat sb; + +- if (!stat("/proc/fs/nfsd/clients", &sb) == 0 || ++ if (stat("/proc/fs/nfsd/clients", &sb) != 0 || + !S_ISDIR(sb.st_mode)) + return; + if (clients_fd >= 0) +diff --git a/support/export/v4root.c b/support/export/v4root.c +index c12a7d85..fbb0ad5f 100644 +--- a/support/export/v4root.c ++++ b/support/export/v4root.c +@@ -198,7 +198,7 @@ static int v4root_add_parents(nfs_export *exp) + * looking for components of the v4 mount. + */ + void +-v4root_set() ++v4root_set(void) + { + nfs_export *exp; + int i; +diff --git a/support/export/xtab.c b/support/export/xtab.c +index c888a80a..e210ca99 100644 +--- a/support/export/xtab.c ++++ b/support/export/xtab.c +@@ -135,7 +135,7 @@ xtab_write(char *xtab, char *xtabtmp, char *lockfn, int is_export) + } + + int +-xtab_export_write() ++xtab_export_write(void) + { + return xtab_write(etab.statefn, etab.tmpfn, etab.lockfn, 1); + } +diff --git a/support/include/nfs/export.h b/support/include/nfs/export.h +index 0eca828e..be5867cf 100644 +--- a/support/include/nfs/export.h ++++ b/support/include/nfs/export.h +@@ -40,4 +40,18 @@ + #define NFSEXP_OLD_SECINFO_FLAGS (NFSEXP_READONLY | NFSEXP_ROOTSQUASH \ + | NFSEXP_ALLSQUASH) + ++/* ++ * Transport layer security policies that are permitted to access ++ * an export ++ */ ++#define NFSEXP_XPRTSEC_NONE 0x0001 ++#define NFSEXP_XPRTSEC_TLS 0x0002 ++#define NFSEXP_XPRTSEC_MTLS 0x0004 ++ ++#define NFSEXP_XPRTSEC_NUM (3) ++ ++#define NFSEXP_XPRTSEC_ALL (NFSEXP_XPRTSEC_NONE | \ ++ NFSEXP_XPRTSEC_TLS | \ ++ NFSEXP_XPRTSEC_MTLS) ++ + #endif /* _NSF_EXPORT_H */ +diff --git a/support/include/nfsd_path.h b/support/include/nfsd_path.h +index 3b73aadd..aa1e1dd0 100644 +--- a/support/include/nfsd_path.h ++++ b/support/include/nfsd_path.h +@@ -7,7 +7,7 @@ + #include + + struct file_handle; +-struct statfs64; ++struct statfs; + + void nfsd_path_init(void); + +@@ -18,8 +18,8 @@ char * nfsd_path_prepend_dir(const char *dir, const char *pathname); + int nfsd_path_stat(const char *pathname, struct stat *statbuf); + int nfsd_path_lstat(const char *pathname, struct stat *statbuf); + +-int nfsd_path_statfs64(const char *pathname, +- struct statfs64 *statbuf); ++int nfsd_path_statfs(const char *pathname, ++ struct statfs *statbuf); + + char * nfsd_realpath(const char *path, char *resolved_path); + +diff --git a/support/include/nfslib.h b/support/include/nfslib.h +index 6faba71b..61c19933 100644 +--- a/support/include/nfslib.h ++++ b/support/include/nfslib.h +@@ -62,6 +62,18 @@ struct sec_entry { + int flags; + }; + ++#define XPRTSECMODE_COUNT 3 ++ ++struct xprtsec_info { ++ const char *name; ++ int number; ++}; ++ ++struct xprtsec_entry { ++ const struct xprtsec_info *info; ++ int flags; ++}; ++ + /* + * Data related to a single exports entry as returned by getexportent. + * FIXME: export options should probably be parsed at a later time to +@@ -83,6 +95,7 @@ struct exportent { + char * e_fslocdata; + char * e_uuid; + struct sec_entry e_secinfo[SECFLAVOR_COUNT+1]; ++ struct xprtsec_entry e_xprtsec[XPRTSECMODE_COUNT + 1]; + unsigned int e_ttl; + char * e_realpath; + }; +@@ -99,6 +112,7 @@ struct rmtabent { + void setexportent(char *fname, char *type); + struct exportent * getexportent(int,int); + void secinfo_show(FILE *fp, struct exportent *ep); ++void xprtsecinfo_show(FILE *fp, struct exportent *ep); + void putexportent(struct exportent *xep); + void endexportent(void); + struct exportent * mkexportent(char *hname, char *path, char *opts); +diff --git a/support/junction/junction.c b/support/junction/junction.c +index 41cce261..0628bb0f 100644 +--- a/support/junction/junction.c ++++ b/support/junction/junction.c +@@ -63,7 +63,7 @@ junction_open_path(const char *pathname, int *fd) + if (pathname == NULL || fd == NULL) + return FEDFS_ERR_INVAL; + +- tmp = open(pathname, O_DIRECTORY); ++ tmp = open(pathname, O_PATH|O_DIRECTORY); + if (tmp == -1) { + switch (errno) { + case EPERM: +@@ -93,7 +93,7 @@ junction_is_directory(int fd, const char *path) + { + struct stat stb; + +- if (fstat(fd, &stb) == -1) { ++ if (fstatat(fd, "", &stb, AT_NO_AUTOMOUNT|AT_EMPTY_PATH) == -1) { + xlog(D_GENERAL, "%s: failed to stat %s: %m", + __func__, path); + return FEDFS_ERR_ACCESS; +@@ -121,7 +121,7 @@ junction_is_sticky_bit_set(int fd, const char *path) + { + struct stat stb; + +- if (fstat(fd, &stb) == -1) { ++ if (fstatat(fd, "", &stb, AT_NO_AUTOMOUNT|AT_EMPTY_PATH) == -1) { + xlog(D_GENERAL, "%s: failed to stat %s: %m", + __func__, path); + return FEDFS_ERR_ACCESS; +@@ -155,7 +155,7 @@ junction_set_sticky_bit(int fd, const char *path) + { + struct stat stb; + +- if (fstat(fd, &stb) == -1) { ++ if (fstatat(fd, "", &stb, AT_NO_AUTOMOUNT|AT_EMPTY_PATH) == -1) { + xlog(D_GENERAL, "%s: failed to stat %s: %m", + __func__, path); + return FEDFS_ERR_ACCESS; +@@ -393,7 +393,7 @@ junction_get_mode(const char *pathname, mode_t *mode) + if (retval != FEDFS_OK) + return retval; + +- if (fstat(fd, &stb) == -1) { ++ if (fstatat(fd, "", &stb, AT_NO_AUTOMOUNT|AT_EMPTY_PATH) == -1) { + xlog(D_GENERAL, "%s: failed to stat %s: %m", + __func__, pathname); + (void)close(fd); +diff --git a/support/misc/nfsd_path.c b/support/misc/nfsd_path.c +index 65e53c13..c3dea4f0 100644 +--- a/support/misc/nfsd_path.c ++++ b/support/misc/nfsd_path.c +@@ -184,46 +184,46 @@ nfsd_path_lstat(const char *pathname, struct stat *statbuf) + return nfsd_run_stat(nfsd_wq, nfsd_lstatfunc, pathname, statbuf); + } + +-struct nfsd_statfs64_data { ++struct nfsd_statfs_data { + const char *pathname; +- struct statfs64 *statbuf; ++ struct statfs *statbuf; + int ret; + int err; + }; + + static void +-nfsd_statfs64func(void *data) ++nfsd_statfsfunc(void *data) + { +- struct nfsd_statfs64_data *d = data; ++ struct nfsd_statfs_data *d = data; + +- d->ret = statfs64(d->pathname, d->statbuf); ++ d->ret = statfs(d->pathname, d->statbuf); + if (d->ret < 0) + d->err = errno; + } + + static int +-nfsd_run_statfs64(struct xthread_workqueue *wq, ++nfsd_run_statfs(struct xthread_workqueue *wq, + const char *pathname, +- struct statfs64 *statbuf) ++ struct statfs *statbuf) + { +- struct nfsd_statfs64_data data = { ++ struct nfsd_statfs_data data = { + pathname, + statbuf, + 0, + 0 + }; +- xthread_work_run_sync(wq, nfsd_statfs64func, &data); ++ xthread_work_run_sync(wq, nfsd_statfsfunc, &data); + if (data.ret < 0) + errno = data.err; + return data.ret; + } + + int +-nfsd_path_statfs64(const char *pathname, struct statfs64 *statbuf) ++nfsd_path_statfs(const char *pathname, struct statfs *statbuf) + { + if (!nfsd_wq) +- return statfs64(pathname, statbuf); +- return nfsd_run_statfs64(nfsd_wq, pathname, statbuf); ++ return statfs(pathname, statbuf); ++ return nfsd_run_statfs(nfsd_wq, pathname, statbuf); + } + + struct nfsd_realpath_data { +diff --git a/support/nfs/exports.c b/support/nfs/exports.c +index 2c8f0752..da8ace3a 100644 +--- a/support/nfs/exports.c ++++ b/support/nfs/exports.c +@@ -99,6 +99,7 @@ static void init_exportent (struct exportent *ee, int fromkernel) + ee->e_fslocmethod = FSLOC_NONE; + ee->e_fslocdata = NULL; + ee->e_secinfo[0].flav = NULL; ++ ee->e_xprtsec[0].info = NULL; + ee->e_nsquids = 0; + ee->e_nsqgids = 0; + ee->e_uuid = NULL; +@@ -122,7 +123,7 @@ getexportent(int fromkernel, int fromexports) + if (first || (ok = getexport(exp, sizeof(exp))) == 0) { + has_default_opts = 0; + has_default_subtree_opts = 0; +- ++ + init_exportent(&def_ee, fromkernel); + + ok = getpath(def_ee.e_path, sizeof(def_ee.e_path)); +@@ -146,7 +147,7 @@ getexportent(int fromkernel, int fromexports) + if (exp[0] == '-' && !fromkernel) { + if (parseopts(exp + 1, &def_ee, 0, &has_default_subtree_opts) < 0) + return NULL; +- ++ + has_default_opts = 1; + + ok = getexport(exp, sizeof(exp)); +@@ -239,7 +240,6 @@ void secinfo_show(FILE *fp, struct exportent *ep) + if (ep->e_secinfo[0].flav == NULL) + secinfo_addflavor(find_flavor("sys"), ep); + for (p1=ep->e_secinfo; p1->flav; p1=p2) { +- + fprintf(fp, ",sec=%s", p1->flav->flavour); + for (p2=p1+1; (p2->flav != NULL) && (p1->flags == p2->flags); + p2++) { +@@ -249,6 +249,17 @@ void secinfo_show(FILE *fp, struct exportent *ep) + } + } + ++void xprtsecinfo_show(FILE *fp, struct exportent *ep) ++{ ++ struct xprtsec_entry *p1, *p2; ++ ++ for (p1 = ep->e_xprtsec; p1->info; p1 = p2) { ++ fprintf(fp, ",xprtsec=%s", p1->info->name); ++ for (p2 = p1 + 1; p2->info && (p1->flags == p2->flags); p2++) ++ fprintf(fp, ":%s", p2->info->name); ++ } ++} ++ + static void + fprintpath(FILE *fp, const char *path) + { +@@ -345,6 +356,7 @@ putexportent(struct exportent *ep) + } + fprintf(fp, "anonuid=%d,anongid=%d", ep->e_anonuid, ep->e_anongid); + secinfo_show(fp, ep); ++ xprtsecinfo_show(fp, ep); + fprintf(fp, ")\n"); + } + +@@ -483,6 +495,75 @@ static unsigned int parse_flavors(char *str, struct exportent *ep) + return out; + } + ++static const struct xprtsec_info xprtsec_name2info[] = { ++ { "none", NFSEXP_XPRTSEC_NONE }, ++ { "tls", NFSEXP_XPRTSEC_TLS }, ++ { "mtls", NFSEXP_XPRTSEC_MTLS }, ++ { NULL, 0 } ++}; ++ ++static const struct xprtsec_info *find_xprtsec_info(const char *name) ++{ ++ const struct xprtsec_info *info; ++ ++ for (info = xprtsec_name2info; info->name; info++) ++ if (strcmp(info->name, name) == 0) ++ return info; ++ return NULL; ++} ++ ++/* ++ * Append the given xprtsec mode to the exportent's e_xprtsec array, ++ * or do nothing if it's already there. Returns the index of flavor in ++ * the resulting array in any case. ++ */ ++static int xprtsec_addmode(const struct xprtsec_info *info, struct exportent *ep) ++{ ++ struct xprtsec_entry *p; ++ ++ for (p = ep->e_xprtsec; p->info; p++) ++ if (p->info == info || p->info->number == info->number) ++ return p - ep->e_xprtsec; ++ ++ if (p - ep->e_xprtsec >= XPRTSECMODE_COUNT) { ++ xlog(L_ERROR, "more than %d xprtsec modes on an export\n", ++ XPRTSECMODE_COUNT); ++ return -1; ++ } ++ p->info = info; ++ p->flags = ep->e_flags; ++ (p + 1)->info = NULL; ++ return p - ep->e_xprtsec; ++} ++ ++/* ++ * @str is a colon seperated list of transport layer security modes. ++ * Their order is recorded in @ep, and a bitmap corresponding to the ++ * list is returned. ++ * ++ * A zero return indicates an error. ++ */ ++static unsigned int parse_xprtsec(char *str, struct exportent *ep) ++{ ++ unsigned int out = 0; ++ char *name; ++ ++ while ((name = strsep(&str, ":"))) { ++ const struct xprtsec_info *info = find_xprtsec_info(name); ++ int bit; ++ ++ if (!info) { ++ xlog(L_ERROR, "unknown xprtsec mode %s\n", name); ++ return 0; ++ } ++ bit = xprtsec_addmode(info, ep); ++ if (bit < 0) ++ return 0; ++ out |= 1 << bit; ++ } ++ return out; ++} ++ + /* Sets the bits in @mask for the appropriate security flavor flags. */ + static void setflags(int mask, unsigned int active, struct exportent *ep) + { +@@ -621,7 +702,7 @@ parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr) + ep->e_anonuid = strtol(opt+8, &oe, 10); + if (opt[8]=='\0' || *oe != '\0') { + xlog(L_ERROR, "%s: %d: bad anonuid \"%s\"\n", +- flname, flline, opt); ++ flname, flline, opt); + bad_option: + free(opt); + return -1; +@@ -631,7 +712,7 @@ bad_option: + ep->e_anongid = strtol(opt+8, &oe, 10); + if (opt[8]=='\0' || *oe != '\0') { + xlog(L_ERROR, "%s: %d: bad anongid \"%s\"\n", +- flname, flline, opt); ++ flname, flline, opt); + goto bad_option; + } + } else if (strncmp(opt, "squash_uids=", 12) == 0) { +@@ -649,13 +730,13 @@ bad_option: + setflags(NFSEXP_FSID, active, ep); + } else { + ep->e_fsid = strtoul(opt+5, &oe, 0); +- if (opt[5]!='\0' && *oe == '\0') ++ if (opt[5]!='\0' && *oe == '\0') + setflags(NFSEXP_FSID, active, ep); + else if (valid_uuid(opt+5)) + ep->e_uuid = strdup(opt+5); + else { + xlog(L_ERROR, "%s: %d: bad fsid \"%s\"\n", +- flname, flline, opt); ++ flname, flline, opt); + goto bad_option; + } + } +@@ -688,6 +769,9 @@ bad_option: + active = parse_flavors(opt+4, ep); + if (!active) + goto bad_option; ++ } else if (strncmp(opt, "xprtsec=", 8) == 0) { ++ if (!parse_xprtsec(opt + 8, ep)) ++ goto bad_option; + } else { + xlog(L_ERROR, "%s:%d: unknown keyword \"%s\"\n", + flname, flline, opt); +@@ -709,7 +793,7 @@ out: + if (warn && !had_subtree_opt) + xlog(L_WARNING, "%s [%d]: Neither 'subtree_check' or 'no_subtree_check' specified for export \"%s:%s\".\n" + " Assuming default behaviour ('no_subtree_check').\n" +- " NOTE: this default has changed since nfs-utils version 1.0.x\n", ++ " NOTE: this default has changed since nfs-utils version 1.0.x\n", + + flname, flline, + ep->e_hostname, ep->e_path); +diff --git a/support/nfs/xlog.c b/support/nfs/xlog.c +index e5861b9d..fa125cef 100644 +--- a/support/nfs/xlog.c ++++ b/support/nfs/xlog.c +@@ -46,11 +46,13 @@ int export_errno = 0; + + static void xlog_toggle(int sig); + static struct xlog_debugfac debugnames[] = { ++ { "0", 0, }, + { "general", D_GENERAL, }, + { "call", D_CALL, }, + { "auth", D_AUTH, }, + { "parse", D_PARSE, }, + { "all", D_ALL, }, ++ { "1", D_ALL, }, + { NULL, 0, }, + }; + +@@ -119,13 +121,14 @@ xlog_sconfig(char *kind, int on) + { + struct xlog_debugfac *tbl = debugnames; + +- while (tbl->df_name != NULL && strcasecmp(tbl->df_name, kind)) ++ while (tbl->df_name != NULL && strcasecmp(tbl->df_name, kind)) + tbl++; + if (!tbl->df_name) { + xlog (L_WARNING, "Invalid debug facility: %s\n", kind); + return; + } +- xlog_config(tbl->df_fac, on); ++ if (tbl->df_fac) ++ xlog_config(tbl->df_fac, on); + } + + void +diff --git a/support/nfsidmap/regex.c b/support/nfsidmap/regex.c +index 958b4ac8..8424179f 100644 +--- a/support/nfsidmap/regex.c ++++ b/support/nfsidmap/regex.c +@@ -542,7 +542,7 @@ struct trans_func regex_trans = { + .gss_princ_to_grouplist = regex_gss_princ_to_grouplist, + }; + +-struct trans_func *libnfsidmap_plugin_init() ++struct trans_func *libnfsidmap_plugin_init(void) + { + return (®ex_trans); + } +diff --git a/systemd/50-nfs.conf b/systemd/50-nfs.conf +deleted file mode 100644 +index 19e8ee73..00000000 +--- a/systemd/50-nfs.conf ++++ /dev/null +@@ -1,16 +0,0 @@ +-# Ensure all NFS systctl settings get applied when modules load +- +-# sunrpc module supports "sunrpc.*" sysctls +-install sunrpc /sbin/modprobe --ignore-install sunrpc $CMDLINE_OPTS && { /sbin/sysctl -q --pattern sunrpc --system; exit 0; } +- +-# rpcrdma module supports sunrpc.svc_rdma.* +-install rpcrdma /sbin/modprobe --ignore-install rpcrdma $CMDLINE_OPTS && { /sbin/sysctl -q --pattern sunrpc.svc_rdma --system; exit 0; } +- +-# lockd module supports "fs.nfs.nlm*" and "fs.nfs.nsm*" sysctls +-install lockd /sbin/modprobe --ignore-install lockd $CMDLINE_OPTS && { /sbin/sysctl -q --pattern fs.nfs.n[sl]m --system; exit 0; } +- +-# nfsv4 module supports "fs.nfs.*" sysctls (nfs_callback_tcpport and idmap_cache_timeout) +-install nfsv4 /sbin/modprobe --ignore-install nfsv4 $CMDLINE_OPTS && { /sbin/sysctl -q --pattern 'fs.nfs.(nfs_callback_tcpport|idmap_cache_timeout)' --system; exit 0; } +- +-# nfs module supports "fs.nfs.*" sysctls +-install nfs /sbin/modprobe --ignore-install nfs $CMDLINE_OPTS && { /sbin/sysctl -q --pattern fs.nfs --system; exit 0; } +diff --git a/systemd/60-nfs.rules b/systemd/60-nfs.rules +new file mode 100644 +index 00000000..188423c1 +--- /dev/null ++++ b/systemd/60-nfs.rules +@@ -0,0 +1,21 @@ ++# Ensure all NFS systctl settings get applied when modules load ++ ++# sunrpc module supports "sunrpc.*" sysctls ++ACTION=="add", SUBSYSTEM=="module", KERNEL=="sunrpc", \ ++ RUN+="/sbin/sysctl -q --pattern ^sunrpc --system" ++ ++# rpcrdma module supports sunrpc.svc_rdma.* ++ACTION=="add", SUBSYSTEM=="module", KERNEL=="rpcrdma", \ ++ RUN+="/sbin/sysctl -q --pattern ^sunrpc.svc_rdma --system" ++ ++# lockd module supports "fs.nfs.nlm*" and "fs.nfs.nsm*" sysctls ++ACTION=="add", SUBSYSTEM=="module", KERNEL=="lockd", \ ++ RUN+="/sbin/sysctl -q --pattern ^fs.nfs.n[sl]m --system" ++ ++# nfsv4 module supports "fs.nfs.*" sysctls (nfs_callback_tcpport and idmap_cache_timeout) ++ACTION=="add", SUBSYSTEM=="module", KERNEL=="nfsv4", \ ++ RUN+="/sbin/sysctl -q --pattern ^fs.nfs.(nfs_callback_tcpport|idmap_cache_timeout) --system" ++ ++# nfs module supports "fs.nfs.*" sysctls ++ACTION=="add", SUBSYSTEM=="module", KERNEL=="nfs", \ ++ RUN+="/sbin/sysctl -q --pattern ^fs.nfs --system" +diff --git a/systemd/Makefile.am b/systemd/Makefile.am +index 7b5ab84b..577c6a22 100644 +--- a/systemd/Makefile.am ++++ b/systemd/Makefile.am +@@ -2,7 +2,8 @@ + + MAINTAINERCLEANFILES = Makefile.in + +-modprobe_files = 50-nfs.conf ++udev_rulesdir = /usr/lib/udev/rules.d/ ++udev_files = 60-nfs.rules + + unit_files = \ + nfs-client.target \ +@@ -53,7 +54,7 @@ endif + + man5_MANS = nfs.conf.man + man7_MANS = nfs.systemd.man +-EXTRA_DIST = $(unit_files) $(modprobe_files) $(man5_MANS) $(man7_MANS) ++EXTRA_DIST = $(unit_files) $(udev_files) $(man5_MANS) $(man7_MANS) + + generator_dir = $(unitdir)/../system-generators + +@@ -75,14 +76,10 @@ rpc_pipefs_generator_LDADD = ../support/nfs/libnfs.la + + if INSTALL_SYSTEMD + genexec_PROGRAMS = nfs-server-generator rpc-pipefs-generator +-install-data-hook: $(unit_files) $(modprobe_files) ++install-data-hook: $(unit_files) $(udev_files) + mkdir -p $(DESTDIR)/$(unitdir) + cp $(unit_files) $(DESTDIR)/$(unitdir) + cp $(rpc_pipefs_mount_file) $(DESTDIR)/$(unitdir)/$(rpc_pipefsmount) +-else +-install-data-hook: $(modprobe_files) +-endif +-if INSTALL_MODPROBEDIR +- mkdir -p $(DESTDIR)$(modprobedir) +- cp $(modprobe_files) $(DESTDIR)$(modprobedir) ++ mkdir -p $(DESTDIR)/$(udev_rulesdir) ++ cp $(udev_files) $(DESTDIR)/$(udev_rulesdir) + endif +diff --git a/systemd/auth-rpcgss-module.service b/systemd/auth-rpcgss-module.service +index 45482833..4a69a7b7 100644 +--- a/systemd/auth-rpcgss-module.service ++++ b/systemd/auth-rpcgss-module.service +@@ -8,8 +8,9 @@ + Description=Kernel Module supporting RPCSEC_GSS + DefaultDependencies=no + Before=gssproxy.service rpc-svcgssd.service rpc-gssd.service +-Wants=gssproxy.service rpc-svcgssd.service rpc-gssd.service ++Wants=gssproxy.service rpc-gssd.service + ConditionPathExists=/etc/krb5.keytab ++ConditionVirtualization=!container + + [Service] + Type=oneshot +diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service +index b432f910..2cdd7868 100644 +--- a/systemd/nfs-server.service ++++ b/systemd/nfs-server.service +@@ -15,7 +15,7 @@ After=nfsdcld.service + Before=rpc-statd-notify.service + + # GSS services dependencies and ordering +-Wants=auth-rpcgss-module.service ++Wants=auth-rpcgss-module.service rpc-svcgssd.service + After=rpc-gssd.service gssproxy.service rpc-svcgssd.service + + [Service] +diff --git a/systemd/nfs.conf.man b/systemd/nfs.conf.man +index e74083e9..bfd3380f 100644 +--- a/systemd/nfs.conf.man ++++ b/systemd/nfs.conf.man +@@ -98,6 +98,12 @@ value, which can be one or more from the list + .BR parse , + .BR all . + When a list is given, the members should be comma-separated. ++The values ++.BR 0 ++and ++.BR 1 ++are also accepted, with '0' making no changes to the debug level, and '1' equivalent to specifying 'all'. ++ + .TP + .B general + Recognized values: +@@ -166,6 +172,7 @@ for details. + Recognized values: + .BR threads , + .BR host , ++.BR scope , + .BR port , + .BR grace-time , + .BR lease-time , +diff --git a/tools/Makefile.am b/tools/Makefile.am +index 40c17c37..48fd0cdf 100644 +--- a/tools/Makefile.am ++++ b/tools/Makefile.am +@@ -12,6 +12,10 @@ if CONFIG_NFSDCLD + OPTDIRS += nfsdclddb + endif + +-SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat rpcctl nfsdclnts nfsrahead $(OPTDIRS) ++if CONFIG_NFSRAHEAD ++OPTDIRS += nfsrahead ++endif ++ ++SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat rpcctl nfsdclnts $(OPTDIRS) + + MAINTAINERCLEANFILES = Makefile.in +diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py +index 1df74ba8..85294fb9 100755 +--- a/tools/nfs-iostat/nfs-iostat.py ++++ b/tools/nfs-iostat/nfs-iostat.py +@@ -43,7 +43,7 @@ NfsEventCounters = [ + 'vfspermission', + 'vfsupdatepage', + 'vfsreadpage', +- 'vfsreadpages', ++ 'vfsreadpages', # or vfsreadahead in statvers=1.2 or above + 'vfswritepage', + 'vfswritepages', + 'vfsreaddir', +@@ -86,14 +86,14 @@ class DeviceData: + self.__nfs_data['export'] = words[1] + self.__nfs_data['mountpoint'] = words[4] + self.__nfs_data['fstype'] = words[7] +- if words[7] == 'nfs': +- self.__nfs_data['statvers'] = words[8] ++ if words[7] == 'nfs' or words[7] == 'nfs4': ++ self.__nfs_data['statvers'] = float(words[8].split('=',1)[1]) + elif 'nfs' in words or 'nfs4' in words: + self.__nfs_data['export'] = words[0] + self.__nfs_data['mountpoint'] = words[3] + self.__nfs_data['fstype'] = words[6] + if words[6] == 'nfs': +- self.__nfs_data['statvers'] = words[7] ++ self.__nfs_data['statvers'] = float(words[7].split('=',1)[1]) + elif words[0] == 'age:': + self.__nfs_data['age'] = int(words[1]) + elif words[0] == 'opts:': +@@ -294,8 +294,11 @@ class DeviceData: + print() + print('%d nfs_readpage() calls read %d pages' % \ + (vfsreadpage, vfsreadpage)) +- print('%d nfs_readpages() calls read %d pages' % \ +- (vfsreadpages, pages_read - vfsreadpage)) ++ multipageread = "readpages" ++ if self.__nfs_data['statvers'] >= 1.2: ++ multipageread = "readahead" ++ print('%d nfs_%s() calls read %d pages' % \ ++ (vfsreadpages, multipageread, pages_read - vfsreadpage)) + if vfsreadpages != 0: + print('(%.1f pages per call)' % \ + (float(pages_read - vfsreadpage) / vfsreadpages)) +diff --git a/tools/nfsrahead/Makefile.am b/tools/nfsrahead/Makefile.am +index 845ea0d5..7e08233a 100644 +--- a/tools/nfsrahead/Makefile.am ++++ b/tools/nfsrahead/Makefile.am +@@ -1,6 +1,6 @@ + libexec_PROGRAMS = nfsrahead + nfsrahead_SOURCES = main.c +-nfsrahead_LDFLAGS= -lmount ++nfsrahead_LDFLAGS= $(LIBMOUNT_LIBS) + nfsrahead_LDADD = ../../support/nfs/libnfsconf.la + + man5_MANS = nfsrahead.man +diff --git a/tools/nfsrahead/main.c b/tools/nfsrahead/main.c +index c83c6f71..8a11cf1a 100644 +--- a/tools/nfsrahead/main.c ++++ b/tools/nfsrahead/main.c +@@ -167,7 +167,7 @@ int main(int argc, char **argv) + if ((ret = get_device_info(argv[optind], &device)) == 0) + break; + +- if (ret != 0) { ++ if (ret != 0 || device.fstype == NULL) { + xlog(D_GENERAL, "unable to find device %s\n", argv[optind]); + goto out; + } +diff --git a/tools/rpcdebug/rpcdebug.c b/tools/rpcdebug/rpcdebug.c +index 68206cc5..ec05179e 100644 +--- a/tools/rpcdebug/rpcdebug.c ++++ b/tools/rpcdebug/rpcdebug.c +@@ -257,7 +257,7 @@ get_flags(char *module) + perror(filename); + exit(1); + } +- if ((len = read(sysfd, buffer, sizeof(buffer))) < 0) { ++ if ((len = read(sysfd, buffer, sizeof(buffer))) <= 0) { + perror("read"); + exit(1); + } +diff --git a/utils/blkmapd/device-discovery.c b/utils/blkmapd/device-discovery.c +index 2736ac89..a565fdbd 100644 +--- a/utils/blkmapd/device-discovery.c ++++ b/utils/blkmapd/device-discovery.c +@@ -187,10 +187,7 @@ static void bl_add_disk(char *filepath) + } + + if (disk && diskpath) { +- if (serial) { +- free(serial->data); +- free(serial); +- } ++ bl_free_scsi_string(serial); + return; + } + +@@ -228,10 +225,7 @@ static void bl_add_disk(char *filepath) + disk->size = size; + disk->valid_path = path; + } +- if (serial) { +- free(serial->data); +- free(serial); +- } ++ bl_free_scsi_string(serial); + } + return; + +@@ -241,10 +235,7 @@ static void bl_add_disk(char *filepath) + free(path->full_path); + free(path); + } +- if (serial) { +- free(serial->data); +- free(serial); +- } ++ bl_free_scsi_string(serial); + return; + } + +@@ -462,7 +453,7 @@ static void sig_die(int signal) + unlink(PID_FILE); + } + BL_LOG_ERR("exit on signal(%d)\n", signal); +- exit(1); ++ exit(0); + } + static void usage(void) + { +@@ -507,28 +498,44 @@ int main(int argc, char **argv) + if (fg) { + openlog("blkmapd", LOG_PERROR, 0); + } else { +- if (daemon(0, 0) != 0) { +- fprintf(stderr, "Daemonize failed\n"); ++ pid_t pid = fork(); ++ if (pid < 0) { ++ BL_LOG_ERR("fork error\n"); + exit(1); ++ } else if (pid != 0) { ++ pidfd = open(PID_FILE, O_WRONLY | O_CREAT, 0644); ++ if (pidfd < 0) { ++ BL_LOG_ERR("Create pid file %s failed\n", PID_FILE); ++ exit(1); ++ } ++ ++ if (lockf(pidfd, F_TLOCK, 0) < 0) { ++ BL_LOG_ERR("Already running; Exiting!"); ++ close(pidfd); ++ exit(1); ++ } ++ if (ftruncate(pidfd, 0) < 0) ++ BL_LOG_ERR("ftruncate on %s failed: m\n", PID_FILE); ++ sprintf(pidbuf, "%d\n", pid); ++ if (write(pidfd, pidbuf, strlen(pidbuf)) != (ssize_t)strlen(pidbuf)) ++ BL_LOG_ERR("write on %s failed: m\n", PID_FILE); ++ exit(0); + } + +- openlog("blkmapd", LOG_PID, 0); +- pidfd = open(PID_FILE, O_WRONLY | O_CREAT, 0644); +- if (pidfd < 0) { +- BL_LOG_ERR("Create pid file %s failed\n", PID_FILE); +- exit(1); ++ (void)setsid(); ++ if (chdir("/")) { ++ BL_LOG_ERR("chdir error\n"); + } ++ int fd = open("/dev/null", O_RDWR, 0); ++ if (fd >= 0) { ++ (void)dup2(fd, STDIN_FILENO); ++ (void)dup2(fd, STDOUT_FILENO); ++ (void)dup2(fd, STDERR_FILENO); + +- if (lockf(pidfd, F_TLOCK, 0) < 0) { +- BL_LOG_ERR("Already running; Exiting!"); +- close(pidfd); +- exit(1); ++ (void)close(fd); + } +- if (ftruncate(pidfd, 0) < 0) +- BL_LOG_WARNING("ftruncate on %s failed: m\n", PID_FILE); +- sprintf(pidbuf, "%d\n", getpid()); +- if (write(pidfd, pidbuf, strlen(pidbuf)) != (ssize_t)strlen(pidbuf)) +- BL_LOG_WARNING("write on %s failed: m\n", PID_FILE); ++ ++ openlog("blkmapd", LOG_PID, 0); + } + + signal(SIGINT, sig_die); +diff --git a/utils/blkmapd/device-discovery.h b/utils/blkmapd/device-discovery.h +index a86eed99..462aa943 100644 +--- a/utils/blkmapd/device-discovery.h ++++ b/utils/blkmapd/device-discovery.h +@@ -151,6 +151,8 @@ uint64_t process_deviceinfo(const char *dev_addr_buf, + + extern ssize_t atomicio(ssize_t(*f) (int, void *, size_t), + int fd, void *_s, size_t n); ++extern struct bl_serial *bl_create_scsi_string(int len, const char *bytes); ++extern void bl_free_scsi_string(struct bl_serial *str); + extern struct bl_serial *bldev_read_serial(int fd, const char *filename); + extern enum bl_path_state_e bldev_read_ap_state(int fd); + extern int bl_discover_devices(void); +diff --git a/utils/blkmapd/device-inq.c b/utils/blkmapd/device-inq.c +index c7952c3e..9e5749ef 100644 +--- a/utils/blkmapd/device-inq.c ++++ b/utils/blkmapd/device-inq.c +@@ -53,7 +53,7 @@ + #define DEF_ALLOC_LEN 255 + #define MX_ALLOC_LEN (0xc000 + 0x80) + +-static struct bl_serial *bl_create_scsi_string(int len, const char *bytes) ++struct bl_serial *bl_create_scsi_string(int len, const char *bytes) + { + struct bl_serial *s; + +@@ -66,7 +66,7 @@ static struct bl_serial *bl_create_scsi_string(int len, const char *bytes) + return s; + } + +-static void bl_free_scsi_string(struct bl_serial *str) ++void bl_free_scsi_string(struct bl_serial *str) + { + if (str) + free(str); +diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c +index 6ba615d1..37b9e4b3 100644 +--- a/utils/exportfs/exportfs.c ++++ b/utils/exportfs/exportfs.c +@@ -69,14 +69,14 @@ static int _lockfd = -1; + * need these additional lockfile() routines. + */ + static void +-grab_lockfile() ++grab_lockfile(void) + { + _lockfd = open(lockfile, O_CREAT|O_RDWR, 0666); + if (_lockfd != -1) + lockf(_lockfd, F_LOCK, 0); + } + static void +-release_lockfile() ++release_lockfile(void) + { + if (_lockfd != -1) { + lockf(_lockfd, F_ULOCK, 0); +@@ -513,7 +513,7 @@ validate_export(nfs_export *exp) + */ + struct stat stb; + char *path = exportent_realpath(&exp->m_export); +- struct statfs64 stf; ++ struct statfs stf; + int fs_has_fsid = 0; + + if (stat(path, &stb) < 0) { +@@ -528,7 +528,7 @@ validate_export(nfs_export *exp) + if (!can_test()) + return; + +- if (!statfs64(path, &stf) && ++ if (!statfs(path, &stf) && + (stf.f_fsid.__val[0] || stf.f_fsid.__val[1])) + fs_has_fsid = 1; + +@@ -743,6 +743,7 @@ dump(int verbose, int export_format) + #endif + } + secinfo_show(stdout, ep); ++ xprtsecinfo_show(stdout, ep); + printf("%c\n", (c != '(')? ')' : ' '); + } + } +diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man +index 54b3f877..83dd6807 100644 +--- a/utils/exportfs/exports.man ++++ b/utils/exportfs/exports.man +@@ -125,7 +125,55 @@ In that case you may include multiple sec= options, and following options + will be enforced only for access using flavors listed in the immediately + preceding sec= option. The only options that are permitted to vary in + this way are ro, rw, no_root_squash, root_squash, and all_squash. ++.SS Transport layer security ++The Linux NFS server allows the use of RPC-with-TLS (RFC 9289) to ++protect RPC traffic between itself and its clients. ++Alternately, administrators can secure NFS traffic using a VPN, ++or an ssh tunnel or similar mechanism, in a way that is transparent ++to the server. + .PP ++To enable the use of RPC-with-TLS, the server's administrator must ++install and configure ++.BR tlshd ++to handle transport layer security handshake requests from the local ++kernel. ++Clients can then choose to use RPC-with-TLS or they may continue ++operating without it. ++.PP ++Administrators may require the use of RPC-with-TLS to protect access ++to individual exports. ++This is particularly useful when using non-cryptographic security ++flavors such as ++.IR sec=sys . ++The ++.I xprtsec= ++option, followed by an unordered colon-delimited list of security policies, ++can restrict access to the export to only clients that have negotiated ++transport-layer security. ++Currently supported transport layer security policies include: ++.TP ++.IR none ++The server permits clients to access the export ++without the use of transport layer security. ++.TP ++.IR tls ++The server permits clients that have negotiated an RPC-with-TLS session ++without peer authentication (confidentiality only) to access the export. ++Clients are not required to offer an x.509 certificate ++when establishing a transport layer security session. ++.TP ++.IR mtls ++The server permits clients that have negotiated an RPC-with-TLS session ++with peer authentication to access the export. ++The server requires clients to offer an x.509 certificate ++when establishing a transport layer security session. ++.PP ++If RPC-with-TLS is configured and enabled and the ++.I xprtsec= ++option is not specified, the default setting for an export is ++.IR xprtsec=none:tls:mtls . ++With this setting, the server permits clients to use any transport ++layer security mechanism or none at all to access the export. + .SS General Options + .BR exportfs + understands the following export options: +@@ -581,7 +629,8 @@ a character class wildcard match. + .BR netgroup (5), + .BR mountd (8), + .BR nfsd (8), +-.BR showmount (8). ++.BR showmount (8), ++.BR tlshd (8). + .\".SH DIAGNOSTICS + .\"An error parsing the file is reported using syslogd(8) as level NOTICE from + .\"a DAEMON whenever +diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c +index e79c124d..cd9a965f 100644 +--- a/utils/idmapd/idmapd.c ++++ b/utils/idmapd/idmapd.c +@@ -867,7 +867,7 @@ nfsdreopen_one(struct idmap_client *ic) + } + + static void +-nfsdreopen() ++nfsdreopen(void) + { + nfsdreopen_one(&nfsd_ic[IC_NAMEID]); + nfsdreopen_one(&nfsd_ic[IC_IDNAME]); +diff --git a/utils/mount/error.c b/utils/mount/error.c +index 73295bf0..9ddbcc09 100644 +--- a/utils/mount/error.c ++++ b/utils/mount/error.c +@@ -207,16 +207,17 @@ void mount_error(const char *spec, const char *mount_point, int error) + progname, spec); + break; + case EINVAL: +- nfs_error(_("%s: an incorrect mount option was specified"), progname); ++ nfs_error(_("%s: an incorrect mount option was specified for %s"), ++ progname, mount_point); + break; + case EOPNOTSUPP: +- nfs_error(_("%s: requested NFS version or transport protocol is not supported"), +- progname); ++ nfs_error(_("%s: requested NFS version or transport protocol is not supported for %s"), ++ progname, mount_point); + break; + case ENOTDIR: + if (spec) +- nfs_error(_("%s: mount spec %s or point %s is not a " +- "directory"), progname, spec, mount_point); ++ nfs_error(_("%s: mount spec %s or point %s is not a directory"), ++ progname, spec, mount_point); + else + nfs_error(_("%s: mount point %s is not a directory"), + progname, mount_point); +@@ -227,31 +228,31 @@ void mount_error(const char *spec, const char *mount_point, int error) + break; + case ENOENT: + if (spec) +- nfs_error(_("%s: mounting %s failed, " +- "reason given by server: %s"), +- progname, spec, strerror(error)); ++ nfs_error(_("%s: mounting %s failed, reason given by server: %s"), ++ progname, spec, strerror(error)); + else + nfs_error(_("%s: mount point %s does not exist"), +- progname, mount_point); ++ progname, mount_point); + break; + case ESPIPE: + rpc_mount_errors((char *)spec, 0, 0); + break; + case EIO: +- nfs_error(_("%s: mount system call failed"), progname); ++ nfs_error(_("%s: mount system call failed for %s"), ++ progname, mount_point); + break; + case EFAULT: +- nfs_error(_("%s: encountered unexpected error condition."), +- progname); ++ nfs_error(_("%s: encountered unexpected error condition for %s."), ++ progname, mount_point); + nfs_error(_("%s: please report the error to" PACKAGE_BUGREPORT), +- progname); ++ progname); + break; + case EALREADY: + /* Error message has already been provided */ + break; + default: +- nfs_error(_("%s: %s"), +- progname, strerror(error)); ++ nfs_error(_("%s: %s for %s on %s"), ++ progname, strerror(error), spec, mount_point); + } + } + +diff --git a/utils/mount/network.c b/utils/mount/network.c +index ed2f8253..01ead49f 100644 +--- a/utils/mount/network.c ++++ b/utils/mount/network.c +@@ -179,7 +179,7 @@ static const unsigned long probe_mnt3_only[] = { + + static const unsigned int *nfs_default_proto(void); + #ifdef MOUNT_CONFIG +-static const unsigned int *nfs_default_proto() ++static const unsigned int *nfs_default_proto(void) + { + extern unsigned long config_default_proto; + /* +diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man +index d9f34df3..7a410422 100644 +--- a/utils/mount/nfs.man ++++ b/utils/mount/nfs.man +@@ -574,7 +574,39 @@ The + .B sloppy + option is an alternative to specifying + .BR mount.nfs " -s " option. +- ++.TP 1.5i ++.BI xprtsec= policy ++Specifies the use of transport layer security to protect NFS network ++traffic on behalf of this mount point. ++.I policy ++can be one of ++.BR none , ++.BR tls , ++or ++.BR mtls . ++.IP ++If ++.B none ++is specified, ++transport layer security is forced off, even if the NFS server supports ++transport layer security. ++If ++.B tls ++is specified, the client uses RPC-with-TLS to provide in-transit ++confidentiality. ++If ++.B mtls ++is specified, the client uses RPC-with-TLS to authenticate itself and ++to provide in-transit confidentiality. ++If the server does not support RPC-with-TLS or peer authentication ++fails, the mount attempt fails. ++.IP ++If the ++.B xprtsec= ++option is not specified, ++the default behavior depends on the kernel, ++but is usually equivalent to ++.BR "xprtsec=none" . + .SS "Options for NFS versions 2 and 3 only" + Use these options, along with the options in the above subsection, + for NFS versions 2 and 3 only. +diff --git a/utils/mount/nfsmount.conf b/utils/mount/nfsmount.conf +index 342063f7..c498eb80 100644 +--- a/utils/mount/nfsmount.conf ++++ b/utils/mount/nfsmount.conf +@@ -59,13 +59,13 @@ + # acregmin=30 + # + # The Maximum time (in seconds) file attributes are cached +-# acregmin=60 ++# acregmax=60 + # + # The minimum time (in seconds) directory attributes are cached +-# acregmin=30 ++# acdirmin=30 + # + # The Maximum time (in seconds) directory attributes are cached +-# acregmin=60 ++# acdirmax=60 + # + # Enable Access Control Lists + # Acl=False +diff --git a/utils/mount/parse_dev.c b/utils/mount/parse_dev.c +index 0d3bcb95..2ade5d5d 100644 +--- a/utils/mount/parse_dev.c ++++ b/utils/mount/parse_dev.c +@@ -170,7 +170,8 @@ static int nfs_parse_square_bracket(const char *dev, + if (pathname) { + *pathname = strndup(cbrace, path_len); + if (*pathname == NULL) { +- free(*hostname); ++ if (hostname) ++ free(*hostname); + return nfs_pdn_nomem_err(); + } + } +diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c +index 4016a761..249df00b 100644 +--- a/utils/nfsd/nfsd.c ++++ b/utils/nfsd/nfsd.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + #include "conffile.h" + #include "nfslib.h" +@@ -39,6 +40,7 @@ static void usage(const char *); + static struct option longopts[] = + { + { "host", 1, 0, 'H' }, ++ { "scope", 1, 0, 'S'}, + { "help", 0, 0, 'h' }, + { "no-nfs-version", 1, 0, 'N' }, + { "nfs-version", 1, 0, 'V' }, +@@ -69,6 +71,7 @@ main(int argc, char **argv) + int count = NFSD_NPROC, c, i, error = 0, portnum, fd, found_one; + char *p, *progname, *port, *rdma_port = NULL; + char **haddr = NULL; ++ char *scope = NULL; + int hcounter = 0; + struct conf_list *hosts; + int socket_up = 0; +@@ -168,8 +171,9 @@ main(int argc, char **argv) + hcounter++; + } + } ++ scope = conf_get_str("nfsd", "scope"); + +- while ((c = getopt_long(argc, argv, "dH:hN:V:p:P:stTuUrG:L:", longopts, NULL)) != EOF) { ++ while ((c = getopt_long(argc, argv, "dH:S:hN:V:p:P:stTuUrG:L:", longopts, NULL)) != EOF) { + switch(c) { + case 'd': + xlog_config(D_ALL, 1); +@@ -190,6 +194,9 @@ main(int argc, char **argv) + haddr[hcounter] = optarg; + hcounter++; + break; ++ case 'S': ++ scope = optarg; ++ break; + case 'P': /* XXX for nfs-server compatibility */ + case 'p': + /* only the last -p option has any effect */ +@@ -367,6 +374,14 @@ main(int argc, char **argv) + if (lease > 0) + nfssvc_set_time("lease", lease); + ++ if (scope) { ++ if (unshare(CLONE_NEWUTS) < 0 || ++ sethostname(scope, strlen(scope)) < 0) { ++ xlog(L_ERROR, "Unable to set server scope: %m"); ++ error = -1; ++ goto out; ++ } ++ } + i = 0; + do { + error = nfssvc_set_sockets(protobits, haddr[i], port); +diff --git a/utils/nfsd/nfsd.man b/utils/nfsd/nfsd.man +index 634b8a63..6f4fc1df 100644 +--- a/utils/nfsd/nfsd.man ++++ b/utils/nfsd/nfsd.man +@@ -35,9 +35,17 @@ Note that + .B lockd + (which performs file locking services for NFS) may still accept + request on all known network addresses. This may change in future +-releases of the Linux Kernel. This option can be used multiple time ++releases of the Linux Kernel. This option can be used multiple times + to listen to more than one interface. + .TP ++.B \-S " or " \-\-scope scope ++NFSv4.1 and later require the server to report a "scope" which is used ++by the clients to detect if two connections are to the same server. ++By default Linux NFSD uses the host name as the scope. ++.sp ++It is particularly important for high-availablity configurations to ensure ++that all potential server nodes report the same server scope. ++.TP + .B \-p " or " \-\-port port + specify a different port to listen on for NFS requests. By default, + .B rpc.nfsd +@@ -134,6 +142,9 @@ will listen on. Use of the + .B --host + option replaces all host names listed here. + .TP ++.B scope ++Set the server scope. ++.TP + .B grace-time + The grace time, for both NFSv4 and NLM, in seconds. + .TP +@@ -159,7 +170,9 @@ Enable or disable TCP support. + .B vers3 + .TP + .B vers4 +-Enable or disable a major NFS version. 3 and 4 are normally enabled ++Enable or disable ++.B all ++NFSv4 versions. All versions are normally enabled + by default. + .TP + .B vers4.1 diff --git a/nfs-utils.spec b/nfs-utils.spec index 0f2a796..98701aa 100644 --- a/nfs-utils.spec +++ b/nfs-utils.spec @@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser Name: nfs-utils URL: http://linux-nfs.org/ Version: 2.6.2 -Release: 2.rc6%{?dist}.1 +Release: 2.rc8%{?dist} Epoch: 1 # group all 32bit related archs @@ -14,7 +14,7 @@ Source2: lockd.conf Source3: 24-nfs-server.conf Source4: 10-nfsv4.conf -Patch001: nfs-utils.2.6.3-rc6.patch +Patch001: nfs-utils.2.6.3-rc8.patch Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch @@ -455,6 +455,9 @@ rm -rf /etc/systemd/system/rpc-*.requires %{_mandir}/*/nfsiostat.8.gz %changelog +* Sat Apr 15 2023 Steve Dickson 2.6.2-2.rc8 +- Updated to the latest RC release: nfs-utils-2-6-3-rc8 (bz 2184788) + * Sat Jan 21 2023 Steve Dickson 2.6.2-2.rc6 - Updated to the latest RC release: nfs-utils-2-6-3-rc6 (bz 2160189)