From 2eff033677f46b4c62ab44553242bf4b255e3dab Mon Sep 17 00:00:00 2001 From: Steve Dickson Date: Thu, 3 May 2012 15:23:17 -0400 Subject: [PATCH] Update to the latest RC release: nfs-utils-1.2.6-rc7 Signed-off-by: Steve Dickson --- nfs-utils-1.2.4-mountshortcut.patch | 52 - nfs-utils-1.2.5-gssd-nolibgssapi-krb5.patch | 26 - nfs-utils-1.2.5-gssd-usercreds.patch | 96 - nfs-utils-1.2.5-libidmap-hide-syms.patch | 26 - nfs-utils-1.2.5-nfsd-new-default.patch | 36 - nfs-utils-1.2.6-rc7.patch | 4192 +++++++++++++++++++ nfs-utils.spec | 18 +- 7 files changed, 4198 insertions(+), 248 deletions(-) delete mode 100644 nfs-utils-1.2.4-mountshortcut.patch delete mode 100644 nfs-utils-1.2.5-gssd-nolibgssapi-krb5.patch delete mode 100644 nfs-utils-1.2.5-gssd-usercreds.patch delete mode 100644 nfs-utils-1.2.5-libidmap-hide-syms.patch delete mode 100644 nfs-utils-1.2.5-nfsd-new-default.patch create mode 100644 nfs-utils-1.2.6-rc7.patch diff --git a/nfs-utils-1.2.4-mountshortcut.patch b/nfs-utils-1.2.4-mountshortcut.patch deleted file mode 100644 index 62af45a..0000000 --- a/nfs-utils-1.2.4-mountshortcut.patch +++ /dev/null @@ -1,52 +0,0 @@ -mount.nfs: Do not send pmap inquire when port is specified - -When the port is specified on the command line do not -send a pmap inquire asking for the port. Instead use -the given port in the NFS ping. If the ping fails, -assume a bad port was given and now go ask the server -for the correct port. - -Signed-off-by: Steve Dickson - -diff --git a/utils/mount/network.c b/utils/mount/network.c -index d1f91dc..405c320 100644 ---- a/utils/mount/network.c -+++ b/utils/mount/network.c -@@ -545,17 +545,18 @@ static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen, - const unsigned int prot = (u_int)pmap->pm_prot, *p_prot; - const u_short port = (u_short) pmap->pm_port; - unsigned long vers = pmap->pm_vers; -- unsigned short p_port; -+ unsigned short p_port = port; -+ int once = 1; - - memcpy(saddr, sap, salen); - p_prot = prot ? &prot : protos; - p_vers = vers ? &vers : versions; -- - for (;;) { - if (verbose) - printf(_("%s: prog %lu, trying vers=%lu, prot=%u\n"), - progname, prog, *p_vers, *p_prot); -- p_port = nfs_getport(saddr, salen, prog, *p_vers, *p_prot); -+ if (!p_port) -+ p_port = nfs_getport(saddr, salen, prog, *p_vers, *p_prot); - if (p_port) { - if (!port || port == p_port) { - nfs_set_port(saddr, p_port); -@@ -564,6 +565,15 @@ static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen, - if (nfs_rpc_ping(saddr, salen, prog, - *p_vers, *p_prot, NULL)) - goto out_ok; -+ if (port == p_port && once) { -+ /* -+ * Could be a bad port was specified. This -+ * time ask the server for the port but only -+ * do it once. -+ */ -+ p_port = once = 0; -+ continue; -+ } - } else - rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; - } diff --git a/nfs-utils-1.2.5-gssd-nolibgssapi-krb5.patch b/nfs-utils-1.2.5-gssd-nolibgssapi-krb5.patch deleted file mode 100644 index 222396c..0000000 --- a/nfs-utils-1.2.5-gssd-nolibgssapi-krb5.patch +++ /dev/null @@ -1,26 +0,0 @@ -commit 880e2efecb4469573a5c2e89aee4963f29288f88 -Author: Steve Dickson -Date: Thu Jan 26 14:56:13 2012 -0500 - - rpc.gssd: Links directly with libgssapi_krb5 which not needed. - - rpc.gssd and rpc.svcgssd both link with the libgssapi_krb5 and - libgssglue libraries which is not needed since libgssglue - will dynamically load the gssapi interface defined in the - /etc/gssapi_mech.conf. Most likely the libgssapi_krb5 library. - - Signed-off-by: Steve Dickson - -diff --git a/aclocal/kerberos5.m4 b/aclocal/kerberos5.m4 -index dfa5738..7574e2d 100644 ---- a/aclocal/kerberos5.m4 -+++ b/aclocal/kerberos5.m4 -@@ -31,7 +31,7 @@ AC_DEFUN([AC_KERBEROS_V5],[ - fi - if test "$K5CONFIG" != ""; then - KRBCFLAGS=`$K5CONFIG --cflags` -- KRBLIBS=`$K5CONFIG --libs gssapi` -+ KRBLIBS=`$K5CONFIG --libs` - K5VERS=`$K5CONFIG --version | head -n 1 | awk '{split($(4),v,"."); if (v@<:@"3"@:>@ == "") v@<:@"3"@:>@ = "0"; print v@<:@"1"@:>@v@<:@"2"@:>@v@<:@"3"@:>@ }'` - AC_DEFINE_UNQUOTED(KRB5_VERSION, $K5VERS, [Define this as the Kerberos version number]) - if test -f $dir/include/gssapi/gssapi_krb5.h -a \ diff --git a/nfs-utils-1.2.5-gssd-usercreds.patch b/nfs-utils-1.2.5-gssd-usercreds.patch deleted file mode 100644 index d9578fc..0000000 --- a/nfs-utils-1.2.5-gssd-usercreds.patch +++ /dev/null @@ -1,96 +0,0 @@ -commit 245fad6be5a32866b2cefad55b3e2d50f6b197af -Author: Steve Dickson -Date: Thu Mar 22 11:02:46 2012 -0400 - - gssd: Look for user creds in user defined directory - - The user credential cache currently is kept in /tmp. - In upcoming Kerberos release that will be moved to - /run/user//. This patch enables gssd to - look in both the old and new caches - - Signed-off-by: Steve Dickson - -diff -up nfs-utils-1.2.5/utils/gssd/gssd.c.orig nfs-utils-1.2.5/utils/gssd/gssd.c ---- nfs-utils-1.2.5/utils/gssd/gssd.c.orig 2011-09-24 07:55:15.000000000 -0400 -+++ nfs-utils-1.2.5/utils/gssd/gssd.c 2012-03-22 11:12:47.441219000 -0400 -@@ -57,7 +57,7 @@ - - char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR; - char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE; --char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR; -+char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR ":" GSSD_USER_CRED_DIR; - char *ccachesearch[GSSD_MAX_CCACHE_SEARCH + 1]; - int use_memcache = 0; - int root_uses_machine_creds = 1; -diff -up nfs-utils-1.2.5/utils/gssd/gssd.h.orig nfs-utils-1.2.5/utils/gssd/gssd.h ---- nfs-utils-1.2.5/utils/gssd/gssd.h.orig 2011-09-24 07:55:15.000000000 -0400 -+++ nfs-utils-1.2.5/utils/gssd/gssd.h 2012-03-22 11:12:47.447222000 -0400 -@@ -45,6 +45,7 @@ - #define DNOTIFY_SIGNAL (SIGRTMIN + 3) - - #define GSSD_DEFAULT_CRED_DIR "/tmp" -+#define GSSD_USER_CRED_DIR "/run/user" - #define GSSD_DEFAULT_CRED_PREFIX "krb5cc_" - #define GSSD_DEFAULT_MACHINE_CRED_SUFFIX "machine" - #define GSSD_DEFAULT_KEYTAB_FILE "/etc/krb5.keytab" -diff -up nfs-utils-1.2.5/utils/gssd/gssd_proc.c.orig nfs-utils-1.2.5/utils/gssd/gssd_proc.c ---- nfs-utils-1.2.5/utils/gssd/gssd_proc.c.orig 2011-09-24 07:55:15.000000000 -0400 -+++ nfs-utils-1.2.5/utils/gssd/gssd_proc.c 2012-03-22 11:12:47.455220000 -0400 -@@ -949,6 +949,23 @@ int create_auth_rpc_client(struct clnt_i - goto out; - } - -+static char * -+user_cachedir(char *dirname, uid_t uid) -+{ -+ struct passwd *pw; -+ char *ptr; -+ -+ if ((pw = getpwuid(uid)) == NULL) { -+ printerr(0, "user_cachedir: Failed to find '%d' uid" -+ " for cache directory\n"); -+ return NULL; -+ } -+ ptr = malloc(strlen(dirname)+strlen(pw->pw_name)+2); -+ if (ptr) -+ sprintf(ptr, "%s/%s", dirname, pw->pw_name); -+ -+ return ptr; -+} - /* - * this code uses the userland rpcsec gss library to create a krb5 - * context on behalf of the kernel -@@ -963,7 +980,7 @@ process_krb5_upcall(struct clnt_info *cl - gss_buffer_desc token; - char **credlist = NULL; - char **ccname; -- char **dirname; -+ char **dirname, *dir, *userdir; - int create_resp = -1; - int err, downcall_err = -EACCES; - -@@ -1006,7 +1023,22 @@ process_krb5_upcall(struct clnt_info *cl - service == NULL)) { - /* Tell krb5 gss which credentials cache to use */ - for (dirname = ccachesearch; *dirname != NULL; dirname++) { -- err = gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname); -+ /* See if the user name is needed */ -+ if (strncmp(*dirname, GSSD_USER_CRED_DIR, -+ strlen(GSSD_USER_CRED_DIR)) == 0) { -+ userdir = user_cachedir(*dirname, uid); -+ if (userdir == NULL) -+ continue; -+ dir = userdir; -+ } else -+ dir = *dirname; -+ -+ err = gssd_setup_krb5_user_gss_ccache(uid, clp->servername, dir); -+ -+ if (userdir) { -+ free(userdir); -+ userdir = NULL; -+ } - if (err == -EKEYEXPIRED) - downcall_err = -EKEYEXPIRED; - else if (!err) diff --git a/nfs-utils-1.2.5-libidmap-hide-syms.patch b/nfs-utils-1.2.5-libidmap-hide-syms.patch deleted file mode 100644 index 15a5272..0000000 --- a/nfs-utils-1.2.5-libidmap-hide-syms.patch +++ /dev/null @@ -1,26 +0,0 @@ -commit 3ce15aeaa66a2f523c6fa92bfe818734bdedfcea -Author: Noah Friedman -Date: Thu Mar 15 12:52:50 2012 -0400 - - rpc.idmap: Hide global symbols from libidmap plugins - - This patch limits the visibility of the symbols in the nfs-utils - conffile.c so that they are only visible to programs linked directly to - it. This forces the objects dynamically loaded via libnfsidmap to use - the functions defined in that shared library instead. - - Signed-off-by: Steve Dickson - -diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c -index 2f1e235..5015e94 100644 ---- a/support/nfs/conffile.c -+++ b/support/nfs/conffile.c -@@ -49,6 +49,8 @@ - #include "conffile.h" - #include "xlog.h" - -+#pragma GCC visibility push(hidden) -+ - static void conf_load_defaults(void); - static int conf_set(int , char *, char *, char *, - char *, int , int ); diff --git a/nfs-utils-1.2.5-nfsd-new-default.patch b/nfs-utils-1.2.5-nfsd-new-default.patch deleted file mode 100644 index 729f6de..0000000 --- a/nfs-utils-1.2.5-nfsd-new-default.patch +++ /dev/null @@ -1,36 +0,0 @@ -commit 5397edac120350bd5fd8284819c1a900cb41546c -Author: Steve Dickson -Date: Fri Mar 16 09:34:43 2012 -0400 - - nfsd: Bump up the default to 8 nprocs - - When the nproc argument is not given the rpc.nfsd - a default number of processes is created. This - patch bumps that default up from 1 to 8. - - Signed-off-by: Steve Dickson - -diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c -index 8bc5d3a..2a3f5cc 100644 ---- a/utils/nfsd/nfsd.c -+++ b/utils/nfsd/nfsd.c -@@ -27,6 +27,10 @@ - #include "nfssvc.h" - #include "xlog.h" - -+#ifndef NFSD_NPROC -+#define NFSD_NPROC 8 -+#endif -+ - static void usage(const char *); - - static struct option longopts[] = -@@ -90,7 +94,7 @@ nfsd_enable_protos(unsigned int *proto4, unsigned int *proto6) - int - main(int argc, char **argv) - { -- int count = 1, c, error = 0, portnum = 0, fd, found_one; -+ int count = NFSD_NPROC, c, error = 0, portnum = 0, fd, found_one; - char *p, *progname, *port; - char *haddr = NULL; - int socket_up = 0; diff --git a/nfs-utils-1.2.6-rc7.patch b/nfs-utils-1.2.6-rc7.patch new file mode 100644 index 0000000..e702124 --- /dev/null +++ b/nfs-utils-1.2.6-rc7.patch @@ -0,0 +1,4192 @@ +diff --git a/.gitignore b/.gitignore +index 7bd9921..96f9750 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -49,6 +49,7 @@ utils/rquotad/rquotad + utils/rquotad/rquota.h + utils/rquotad/rquota_xdr.c + utils/showmount/showmount ++utils/nfsdcld/nfsdcld + utils/statd/statd + tools/locktest/testlk + tools/getiversion/getiversion +diff --git a/README b/README +index e7588cf..348f5d4 100644 +--- a/README ++++ b/README +@@ -15,6 +15,8 @@ libraries. They are available from + http://www.citi.umich.edu/projects/nfsv4/linux/libnfsidmap/ + Otherwise use --disable-nfsv4 + ++To use the nfsdcld tracking daemon, nfsv4 support must be enabled, ++and the libsqlite3 development libraries must be installed. + + 1. COMPILING + +@@ -80,7 +82,7 @@ scripts can be written to work correctly. + and starting the nfsd server is not important. + idmapd is only needed for NFSv4 support. + svcgssd is only needed if exportfs NFS filesystem with crypto- +- security (Kerberos or SPKM3). ++ security (Kerberos). + + C/ exportfs -av ; rpc.mountd + It is important that exportfs be run before mountd so that +@@ -106,12 +108,31 @@ scripts can be written to work correctly. + the lock. + rpc.statd is only needed for NFSv2 and NFSv3 support. + +- E/ rpc.nfsd ++ E/ nfsdcld ++ This daemon is only needed on kernels that support the nfsdcld ++ upcall, and only if the legacy client ID tracking isn't used. It ++ is also not needed if the server does not support NFSv4. ++ ++ To determine whether you need this or not, do the following: ++ ++ # cat /proc/fs/nfsd/versions ++ ++ That should yield a list of NFS versions that this kernel supports, ++ if "4" or later is not in that list, or they are prefixed with a "-" ++ then you don't need to run this daemon. Next: ++ ++ # cat /proc/fs/nfsd/nfsv4recoverydir ++ ++ If that file is not present, or the directory that the above command ++ outputs is not present, then this daemon is required in order to ++ support lock recovery by the clients when the server reboots. ++ ++ F/ rpc.nfsd + Starting nfsd will automatically start lockd. The nfs server + will now be fully active and respond to any requests from + clients. + +- F/ sm-notify ++ G/ sm-notify + This will notify any client which might have locks from before + a reboot to try to reclaim their locks. This should start + immediately after rpc.nfsd is started so that clients have a +@@ -130,7 +151,7 @@ scripts can be written to work correctly. + B/ gssd ; idmapd + idmapd should be started before mounting any NFSv4 filesystems. + gssd should be started before mounting any NFS filesystems +- securely (with Kerberos of SPKM3). ++ securely (with Kerberos). + + C/ statd should be run before any NFSv2 or NFSv3 filesystem is + mounted with remote locking (i.e. without -o nolock). +diff --git a/aclocal/ipv6.m4 b/aclocal/ipv6.m4 +index 5ee8fb6..75a8582 100644 +--- a/aclocal/ipv6.m4 ++++ b/aclocal/ipv6.m4 +@@ -2,11 +2,6 @@ dnl Checks for IPv6 support + dnl + AC_DEFUN([AC_IPV6], [ + +- AC_CHECK_DECL([AI_ADDRCONFIG], +- [AC_DEFINE([HAVE_DECL_AI_ADDRCONFIG], 1, +- [Define this to 1 if AI_ADDRCONFIG macro is defined])], , +- [ #include ]) +- + if test "$enable_ipv6" = yes; then + + dnl TI-RPC required for IPv6 +@@ -15,15 +10,11 @@ AC_DEFUN([AC_IPV6], [ + fi + + dnl IPv6-enabled networking functions required for IPv6 +- AC_CHECK_FUNCS([getifaddrs getnameinfo bindresvport_sa], , ++ AC_CHECK_FUNCS([getifaddrs getnameinfo], , + [AC_MSG_ERROR([Missing library functions needed for IPv6.])]) + +- dnl Need to detect presence of IPv6 networking at run time via +- dnl getaddrinfo(3); old versions of glibc do not support ADDRCONFIG +- AC_CHECK_DECL([AI_ADDRCONFIG], , +- [AC_MSG_ERROR([full getaddrinfo(3) implementation needed for IPv6 support])], +- [ #include ]) +- ++ AC_CHECK_LIB([tirpc], [bindresvport_sa], [:], ++ [AC_MSG_ERROR([Missing library functions needed for IPv6.])]) + fi + + ])dnl +diff --git a/aclocal/kerberos5.m4 b/aclocal/kerberos5.m4 +index dfa5738..7574e2d 100644 +--- a/aclocal/kerberos5.m4 ++++ b/aclocal/kerberos5.m4 +@@ -31,7 +31,7 @@ AC_DEFUN([AC_KERBEROS_V5],[ + fi + if test "$K5CONFIG" != ""; then + KRBCFLAGS=`$K5CONFIG --cflags` +- KRBLIBS=`$K5CONFIG --libs gssapi` ++ KRBLIBS=`$K5CONFIG --libs` + K5VERS=`$K5CONFIG --version | head -n 1 | awk '{split($(4),v,"."); if (v@<:@"3"@:>@ == "") v@<:@"3"@:>@ = "0"; print v@<:@"1"@:>@v@<:@"2"@:>@v@<:@"3"@:>@ }'` + AC_DEFINE_UNQUOTED(KRB5_VERSION, $K5VERS, [Define this as the Kerberos version number]) + if test -f $dir/include/gssapi/gssapi_krb5.h -a \ +diff --git a/aclocal/libevent.m4 b/aclocal/libevent.m4 +index 3c962b3..b5ac00f 100644 +--- a/aclocal/libevent.m4 ++++ b/aclocal/libevent.m4 +@@ -2,8 +2,9 @@ dnl Checks for libevent + AC_DEFUN([AC_LIBEVENT], [ + + dnl Check for libevent, but do not add -levent to LIBS +- AC_CHECK_LIB([event], [event_dispatch], [libevent=1], ++ AC_CHECK_LIB([event], [event_dispatch], [LIBEVENT=-levent], + [AC_MSG_ERROR([libevent not found.])]) ++ AC_SUBST(LIBEVENT) + + AC_CHECK_HEADERS([event.h], , + [AC_MSG_ERROR([libevent headers not found.])]) +diff --git a/aclocal/libnfsidmap.m4 b/aclocal/libnfsidmap.m4 +index 484b1ec..ae697e8 100644 +--- a/aclocal/libnfsidmap.m4 ++++ b/aclocal/libnfsidmap.m4 +@@ -3,7 +3,7 @@ dnl + AC_DEFUN([AC_LIBNFSIDMAP], [ + + dnl Check for libnfsidmap, but do not add -lnfsidmap to LIBS +- AC_CHECK_LIB([nfsidmap], [nfs4_init_name_mapping], [libnfsidmap=1], ++ AC_CHECK_LIB([nfsidmap], [nfs4_init_name_mapping], [LIBNFSIDMAP=-lnfsidmap], + [AC_MSG_ERROR([libnfsidmap not found.])]) + + AC_CHECK_HEADERS([nfsidmap.h], , +@@ -14,7 +14,10 @@ AC_DEFUN([AC_LIBNFSIDMAP], [ + [AC_DEFINE([HAVE_NFS4_SET_DEBUG], 1, + [Define to 1 if you have the `nfs4_set_debug' function.])]) + +- dnl only enable nfsidmap when libnfsidmap supports it +- AC_CHECK_LIB([nfsidmap], [nfs4_owner_to_uid]) ++ dnl nfs4_owner_to_uid() doesn't appear in all versions of libnfsidmap ++ dnl We just need this test to set $ac_cv_lib_nfsidmap_nfs4_owner_to_uid ++ AC_CHECK_LIB([nfsidmap], [nfs4_owner_to_uid], [:]) ++ ++ AC_SUBST(LIBNFSIDMAP) + + ])dnl +diff --git a/aclocal/libsqlite3.m4 b/aclocal/libsqlite3.m4 +new file mode 100644 +index 0000000..73d1e46 +--- /dev/null ++++ b/aclocal/libsqlite3.m4 +@@ -0,0 +1,33 @@ ++dnl Checks for matching sqlite3 header and library, and ++dnl sufficient sqlite3 version. ++dnl ++AC_DEFUN([AC_SQLITE3_VERS], [ ++ AC_CHECK_HEADERS([sqlite3.h], ,) ++ ++ dnl look for the library; do not add to LIBS if found ++ AC_CHECK_LIB([sqlite3], [sqlite3_libversion_number], [LIBSQLITE=-lsqlite3], ,) ++ AC_SUBST(LIBSQLITE) ++ ++ AC_MSG_CHECKING(for suitable sqlite3 version) ++ ++ AC_CACHE_VAL([libsqlite3_cv_is_recent], ++ [ ++ saved_LIBS="$LIBS" ++ LIBS=-lsqlite3 ++ AC_TRY_RUN([ ++ #include ++ #include ++ int main() ++ { ++ int vers = sqlite3_libversion_number(); ++ ++ return vers != SQLITE_VERSION_NUMBER || ++ vers < 3003000; ++ } ++ ], [libsqlite3_cv_is_recent=yes], [libsqlite3_cv_is_recent=no], ++ [libsqlite3_cv_is_recent=unknown]) ++ LIBS="$saved_LIBS"]) ++ ++ AC_MSG_RESULT($libsqlite3_cv_is_recent) ++ AM_CONDITIONAL(CONFIG_SQLITE3, [test "$libsqlite3_cv_is_recent" = "yes"]) ++])dnl +diff --git a/aclocal/libtirpc.m4 b/aclocal/libtirpc.m4 +index 9f0fde0..19b8361 100644 +--- a/aclocal/libtirpc.m4 ++++ b/aclocal/libtirpc.m4 +@@ -13,8 +13,8 @@ AC_DEFUN([AC_LIBTIRPC], [ + + if test "$enable_tirpc" != "no"; then + +- dnl look for the library; add to LIBS if found +- AC_CHECK_LIB([tirpc], [clnt_tli_create], , ++ dnl look for the library ++ AC_CHECK_LIB([tirpc], [clnt_tli_create], [:], + [if test "$enable_tirpc" = "yes"; then + AC_MSG_ERROR([libtirpc not found.]) + else +@@ -37,4 +37,15 @@ AC_DEFUN([AC_LIBTIRPC], [ + + fi + ++ dnl now set $LIBTIRPC accordingly ++ if test "$enable_tirpc" != "no"; then ++ AC_DEFINE([HAVE_LIBTIRPC], 1, ++ [Define to 1 if you have and wish to use libtirpc.]) ++ LIBTIRPC="-ltirpc" ++ else ++ LIBTIRPC="" ++ fi ++ ++ AC_SUBST(LIBTIRPC) ++ + ])dnl +diff --git a/configure.ac b/configure.ac +index 80fb39d..20c452b 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -24,9 +24,8 @@ AC_ARG_WITH(statedir, + statedir=/var/lib/nfs) + AC_SUBST(statedir) + AC_ARG_WITH(statdpath, +- [AC_HELP_STRING([--with-statdpath=/foo @<:@default=/var/lib/nfs@:>@], +- [define statd's state dir as /foo instead of the NFS statedir] +- )], ++ [AC_HELP_STRING([--with-statdpath=/foo], ++ [define the statd state dir as /foo instead of the NFS statedir @<:@default=/var/lib/nfs@:>@])], + statdpath=$withval, + statdpath=$statedir + ) +@@ -186,6 +185,12 @@ else + AM_CONDITIONAL(MOUNT_CONFIG, [test "$enable_mount" = "yes"]) + fi + ++AC_ARG_ENABLE(nfsdcld, ++ [AC_HELP_STRING([--enable-nfsdcld], ++ [Create nfsdcld NFSv4 clientid tracking daemon. @<:@default=no@:>@])], ++ enable_nfsdcld=$enableval, ++ enable_nfsdcld="no") ++ + dnl Check for TI-RPC library and headers + AC_LIBTIRPC + +@@ -249,6 +254,8 @@ AC_CHECK_FUNC([getservbyname], , + + AC_CHECK_LIB([crypt], [crypt], [LIBCRYPT="-lcrypt"]) + ++AC_CHECK_LIB([dl], [dlclose], [LIBDL="-ldl"]) ++ + if test "$enable_nfsv4" = yes; then + dnl check for libevent libraries and headers + AC_LIBEVENT +@@ -259,12 +266,32 @@ if test "$enable_nfsv4" = yes; then + dnl check for the keyutils libraries and headers + AC_KEYUTILS + ++ dnl Check for sqlite3 ++ AC_SQLITE3_VERS ++ ++ if test "$enable_nfsdcld" = "yes"; then ++ AC_CHECK_HEADERS([libgen.h sys/inotify.h], , ++ AC_MSG_ERROR([Cannot find header needed for nfsdcld])) ++ ++ if test "$libsqlite3_cv_is_recent" != "yes" ; then ++ AC_MSG_ERROR([nfsdcld requires sqlite3]) ++ fi ++ fi ++ ++ AM_CONDITIONAL(CONFIG_NFSDCLD, [test "$enable_nfsdcld" = "yes" ]) ++ + dnl librpcsecgss already has a dependency on libgssapi, + dnl but we need to make sure we get the right version + if test "$enable_gss" = yes; then + AC_RPCSEC_VERSION + fi + fi ++ ++if test "$enable_nfsv41" = yes; then ++ AC_CHECK_LIB([devmapper], [dm_task_create], [LIBDEVMAPPER="-ldevmapper"], AC_MSG_ERROR([libdevmapper needed])) ++ AC_CHECK_HEADER(libdevmapper.h, , AC_MSG_ERROR([Cannot find devmapper header file libdevmapper.h])) ++fi ++ + dnl enable nfsidmap when its support by libnfsidmap + AM_CONDITIONAL(CONFIG_NFSIDMAP, [test "$ac_cv_header_keyutils_h$ac_cv_lib_nfsidmap_nfs4_owner_to_uid" = "yesyes"]) + +@@ -293,6 +320,7 @@ AC_SUBST(LIBSOCKET) + AC_SUBST(LIBCRYPT) + AC_SUBST(LIBBSD) + AC_SUBST(LIBBLKID) ++AC_SUBST(LIBDL) + + if test "$enable_libmount" != no; then + AC_CHECK_LIB(mount, mnt_context_do_mount, [LIBMOUNT="-lmount"], AC_MSG_ERROR([libmount needed])) +@@ -308,9 +336,6 @@ if test "$enable_gss" = yes; then + dnl 'gss' also depends on nfsidmap.h - at least for svcgssd_proc.c + AC_LIBNFSIDMAP + +- AC_CHECK_HEADERS([spkm3.h], , +- [AC_MSG_WARN([Could not locate SPKM3 header; will not have SPKM3 support])]) +- + dnl Check for Kerberos V5 + AC_KERBEROS_V5 + +@@ -330,7 +355,7 @@ AC_CHECK_HEADERS([arpa/inet.h fcntl.h libintl.h limits.h \ + stdlib.h string.h sys/file.h sys/ioctl.h sys/mount.h \ + sys/param.h sys/socket.h sys/time.h sys/vfs.h \ + syslog.h unistd.h com_err.h et/com_err.h \ +- ifaddrs.h]) ++ ifaddrs.h nfs-plugin.h]) + + dnl ************************************************************* + dnl Checks for typedefs, structures, and compiler characteristics +@@ -452,6 +477,7 @@ AC_CONFIG_FILES([ + tools/nfs-iostat/Makefile + utils/Makefile + utils/blkmapd/Makefile ++ utils/nfsdcld/Makefile + utils/exportfs/Makefile + utils/gssd/Makefile + utils/idmapd/Makefile +@@ -462,6 +488,7 @@ AC_CONFIG_FILES([ + utils/nfsidmap/Makefile + utils/showmount/Makefile + utils/statd/Makefile ++ utils/osd_login/Makefile + tests/Makefile + tests/nsm_client/Makefile]) + AC_OUTPUT +diff --git a/support/include/cld.h b/support/include/cld.h +new file mode 100644 +index 0000000..f14a9ab +--- /dev/null ++++ b/support/include/cld.h +@@ -0,0 +1,56 @@ ++/* ++ * Upcall description for nfsdcld communication ++ * ++ * Copyright (c) 2012 Red Hat, Inc. ++ * Author(s): Jeff Layton ++ * ++ * This program 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. ++ * ++ * This program 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 this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _NFSD_CLD_H ++#define _NFSD_CLD_H ++ ++/* latest upcall version available */ ++#define CLD_UPCALL_VERSION 1 ++ ++/* defined by RFC3530 */ ++#define NFS4_OPAQUE_LIMIT 1024 ++ ++enum cld_command { ++ Cld_Create, /* create a record for this cm_id */ ++ Cld_Remove, /* remove record of this cm_id */ ++ Cld_Check, /* is this cm_id allowed? */ ++ Cld_GraceDone, /* grace period is complete */ ++}; ++ ++/* representation of long-form NFSv4 client ID */ ++struct cld_name { ++ uint16_t cn_len; /* length of cm_id */ ++ unsigned char cn_id[NFS4_OPAQUE_LIMIT]; /* client-provided */ ++} __attribute__((packed)); ++ ++/* message struct for communication with userspace */ ++struct cld_msg { ++ uint8_t cm_vers; /* upcall version */ ++ uint8_t cm_cmd; /* upcall command */ ++ int16_t cm_status; /* return code */ ++ uint32_t cm_xid; /* transaction id */ ++ union { ++ int64_t cm_gracetime; /* grace period start time */ ++ struct cld_name cm_name; ++ } __attribute__((packed)) cm_u; ++} __attribute__((packed)); ++ ++#endif /* !_NFSD_CLD_H */ +diff --git a/support/include/exportfs.h b/support/include/exportfs.h +index 01e87dd..99916e5 100644 +--- a/support/include/exportfs.h ++++ b/support/include/exportfs.h +@@ -32,6 +32,10 @@ enum { + FSLOC_STUB + }; + ++#ifndef EXP_LOCKFILE ++#define EXP_LOCKFILE "/var/lib/nfs/export-lock" ++#endif ++ + typedef struct mclient { + struct mclient * m_next; + char * m_hostname; +diff --git a/support/include/nfs/debug.h b/support/include/nfs/debug.h +index d391e91..dbec5ba 100644 +--- a/support/include/nfs/debug.h ++++ b/support/include/nfs/debug.h +@@ -76,6 +76,9 @@ enum { + #define NFSDBG_CALLBACK 0x0100 + #define NFSDBG_CLIENT 0x0200 + #define NFSDBG_MOUNT 0x0400 ++#define NFSDBG_FSCACHE 0x0800 ++#define NFSDBG_PNFS 0x1000 ++#define NFSDBG_PNFS_LD 0x2000 + #define NFSDBG_ALL 0xFFFF + + #endif /* _NFS_DEBUG_H */ +diff --git a/support/include/pseudoflavors.h b/support/include/pseudoflavors.h +index c21087b..deb052b 100644 +--- a/support/include/pseudoflavors.h ++++ b/support/include/pseudoflavors.h +@@ -4,9 +4,6 @@ + #define RPC_AUTH_GSS_LKEY 390006 + #define RPC_AUTH_GSS_LKEYI 390007 + #define RPC_AUTH_GSS_LKEYP 390008 +-#define RPC_AUTH_GSS_SPKM 390009 +-#define RPC_AUTH_GSS_SPKMI 390010 +-#define RPC_AUTH_GSS_SPKMP 390011 + + struct flav_info { + char *flavour; +diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c +index fa0dc6b..5015e94 100644 +--- a/support/nfs/conffile.c ++++ b/support/nfs/conffile.c +@@ -49,6 +49,8 @@ + #include "conffile.h" + #include "xlog.h" + ++#pragma GCC visibility push(hidden) ++ + static void conf_load_defaults(void); + static int conf_set(int , char *, char *, char *, + char *, int , int ); +@@ -211,7 +213,7 @@ static void + conf_parse_line(int trans, char *line, size_t sz) + { + char *val, *ptr; +- size_t i; ++ size_t i, valsize; + size_t j; + static char *section = 0; + static char *arg = 0; +@@ -256,13 +258,14 @@ conf_parse_line(int trans, char *line, size_t sz) + val++, j++; + if (*val) + i = j; +- section = malloc(i); ++ section = malloc(i+1); + if (!section) { + xlog_warn("conf_parse_line: %d: malloc (%lu) failed", ln, + (unsigned long)i); + return; + } + strncpy(section, line, i); ++ section[i] = '\0'; + + if (arg) + free(arg); +@@ -297,23 +300,16 @@ conf_parse_line(int trans, char *line, size_t sz) + } + line[strcspn (line, " \t=")] = '\0'; + val = line + i + 1 + strspn (line + i + 1, " \t"); ++ valsize = 0; ++ while (val[valsize++]); + +- /* Skip trailing comments, if any */ +- for (j = 0; j < sz - (val - line); j++) { +- if (val[j] == '#' || val[j] == ';') { ++ /* Skip trailing spaces and comments */ ++ for (j = 0; j < valsize; j++) { ++ if (val[j] == '#' || val[j] == ';' || isspace(val[j])) { + val[j] = '\0'; + break; + } + } +- +- /* Skip trailing whitespace, if any */ +- for (j--; j > 0; j--) { +- if (isspace(val[j])) +- val[j] = '\0'; +- else +- break; +- } +- + /* XXX Perhaps should we not ignore errors? */ + conf_set(trans, section, arg, line, val, 0, 0); + return; +diff --git a/support/nfs/exports.c b/support/nfs/exports.c +index c96500f..84a2b08 100644 +--- a/support/nfs/exports.c ++++ b/support/nfs/exports.c +@@ -39,12 +39,6 @@ struct flav_info flav_map[] = { + { "krb5", RPC_AUTH_GSS_KRB5 }, + { "krb5i", RPC_AUTH_GSS_KRB5I }, + { "krb5p", RPC_AUTH_GSS_KRB5P }, +- { "lipkey", RPC_AUTH_GSS_LKEY }, +- { "lipkey-i", RPC_AUTH_GSS_LKEYI }, +- { "lipkey-p", RPC_AUTH_GSS_LKEYP }, +- { "spkm3", RPC_AUTH_GSS_SPKM }, +- { "spkm3i", RPC_AUTH_GSS_SPKMI }, +- { "spkm3p", RPC_AUTH_GSS_SPKMP }, + { "unix", AUTH_UNIX }, + { "sys", AUTH_SYS }, + { "null", AUTH_NULL }, +diff --git a/support/nfs/nfsctl.c b/support/nfs/nfsctl.c +index 89fa1a4..fec775f 100644 +--- a/support/nfs/nfsctl.c ++++ b/support/nfs/nfsctl.c +@@ -11,16 +11,22 @@ + #endif + + #include ++#include + #include + #include "nfslib.h" + + /* compatibility hack... */ +-#ifndef __NR_nfsctl ++#if !defined(__NR_nfsctl) && defined(__NR_nfsservctl) + #define __NR_nfsctl __NR_nfsservctl + #endif + + int + nfsctl (int cmd, struct nfsctl_arg * argp, union nfsctl_res * resp) + { ++#ifdef __NR_nfsctl + return syscall (__NR_nfsctl, cmd, argp, resp); ++#else ++ errno = ENOSYS; ++ return -1; ++#endif + } +diff --git a/tools/rpcdebug/rpcdebug.c b/tools/rpcdebug/rpcdebug.c +index 275a491..444616d 100644 +--- a/tools/rpcdebug/rpcdebug.c ++++ b/tools/rpcdebug/rpcdebug.c +@@ -167,6 +167,9 @@ static struct flagmap { + FLAG(NFS, CALLBACK), + FLAG(NFS, CLIENT), + FLAG(NFS, MOUNT), ++ FLAG(NFS, FSCACHE), ++ FLAG(NFS, PNFS), ++ FLAG(NFS, PNFS_LD), + FLAG(NFS, ALL), + + /* nfsd */ +diff --git a/tools/rpcgen/Makefile.am b/tools/rpcgen/Makefile.am +index 51a2bfa..8a9ec89 100644 +--- a/tools/rpcgen/Makefile.am ++++ b/tools/rpcgen/Makefile.am +@@ -12,6 +12,7 @@ rpcgen_SOURCES = rpc_clntout.c rpc_cout.c rpc_hout.c rpc_main.c \ + rpcgen_CFLAGS=$(CFLAGS_FOR_BUILD) + rpcgen_CPPLAGS=$(CPPFLAGS_FOR_BUILD) + rpcgen_LDFLAGS=$(LDFLAGS_FOR_BUILD) ++rpcgen_LDADD=$(LIBTIRPC) + + MAINTAINERCLEANFILES = Makefile.in + +diff --git a/utils/Makefile.am b/utils/Makefile.am +index d074b85..09045dd 100644 +--- a/utils/Makefile.am ++++ b/utils/Makefile.am +@@ -21,6 +21,10 @@ if CONFIG_MOUNT + OPTDIRS += mount + endif + ++if CONFIG_NFSDCLD ++OPTDIRS += nfsdcld ++endif ++ + SUBDIRS = \ + exportfs \ + mountd \ +@@ -28,6 +32,7 @@ SUBDIRS = \ + nfsstat \ + showmount \ + statd \ ++ osd_login \ + $(OPTDIRS) + + MAINTAINERCLEANFILES = Makefile.in +diff --git a/utils/blkmapd/device-process.c b/utils/blkmapd/device-process.c +index 27ff374..652a7a8 100644 +--- a/utils/blkmapd/device-process.c ++++ b/utils/blkmapd/device-process.c +@@ -296,7 +296,7 @@ decode_blk_volume(uint32_t **pp, uint32_t *end, struct bl_volume *vols, int voln + off_t stripe_unit = vol->param.bv_stripe_unit; + /* Check limitations imposed by device-mapper */ + if ((stripe_unit & (stripe_unit - 1)) != 0 +- || stripe_unit < (off_t) (PAGE_SIZE >> 9)) ++ || stripe_unit < (off_t) (sysconf(_SC_PAGE_SIZE) >> 9)) + return -EIO; + BLK_READBUF(p, end, 4); + READ32(vol->bv_vol_n); +diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c +index 7432a65..a3323d7 100644 +--- a/utils/exportfs/exportfs.c ++++ b/utils/exportfs/exportfs.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -43,6 +44,41 @@ static void usage(const char *progname); + static void validate_export(nfs_export *exp); + static int matchhostname(const char *hostname1, const char *hostname2); + static void export_d_read(const char *dname); ++static void grab_lockfile(void); ++static void release_lockfile(void); ++ ++static const char *lockfile = EXP_LOCKFILE; ++static int _lockfd = -1; ++ ++/* ++ * If we aren't careful, changes made by exportfs can be lost ++ * when multiple exports process run at once: ++ * ++ * exportfs process 1 exportfs process 2 ++ * ------------------------------------------ ++ * reads etab version A reads etab version A ++ * adds new export B adds new export C ++ * writes A+B writes A+C ++ * ++ * The locking in support/export/xtab.c will prevent mountd from ++ * seeing a partially written version of etab, and will prevent ++ * the two writers above from writing simultaneously and ++ * corrupting etab, but to prevent problems like the above we ++ * need these additional lockfile() routines. ++ */ ++static void ++grab_lockfile() ++{ ++ _lockfd = open(lockfile, O_CREAT|O_RDWR, 0666); ++ if (_lockfd != -1) ++ lockf(_lockfd, F_LOCK, 0); ++} ++static void ++release_lockfile() ++{ ++ if (_lockfd != -1) ++ lockf(_lockfd, F_ULOCK, 0); ++} + + int + main(int argc, char **argv) +@@ -129,6 +165,13 @@ main(int argc, char **argv) + return 0; + } + } ++ ++ /* ++ * Serialize things as best we can ++ */ ++ grab_lockfile(); ++ atexit(release_lockfile); ++ + if (f_export && ! f_ignore) { + export_read(_PATH_EXPORTS); + export_d_read(_PATH_EXPORTS_D); +diff --git a/utils/exportfs/exportfs.man b/utils/exportfs/exportfs.man +index 364f247..8853486 100644 +--- a/utils/exportfs/exportfs.man ++++ b/utils/exportfs/exportfs.man +@@ -177,7 +177,7 @@ In this way + .B exportfs + can be used to modify the export options of an already exported directory. + .SS Unexporting Directories +-The third synopsis shows how to unexported a currently exported directory. ++The third synopsis shows how to unexport a currently exported directory. + When using + .BR "exportfs -ua" , + all entries listed in +diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man +index 54adfeb..bc1de73 100644 +--- a/utils/exportfs/exports.man ++++ b/utils/exportfs/exports.man +@@ -293,24 +293,6 @@ be explicitly requested with either of the synonymous + .IR auth_nlm , + or + .IR secure_locks . +-.TP +-.IR no_acl +-On some specially patched kernels, and when exporting filesystems that +-support ACLs, this option tells +-.B nfsd +-not to reveal ACLs to clients, so +-they will see only a subset of actual permissions on the given file +-system. This option is safe for filesystems used by NFSv2 clients and +-old NFSv3 clients that perform access decisions locally. Current +-NFSv3 clients use the ACCESS RPC to perform all access decisions on +-the server. Note that the +-.I no_acl +-option only has effect on kernels specially patched to support it, and +-when exporting filesystems with ACL support. The default is to export +-with ACL support (i.e. by default, +-.I no_acl +-is off). +- + .\".TP + .\".I noaccess + .\"This makes everything below the directory inaccessible for the named +diff --git a/utils/exportfs/nfsd.man b/utils/exportfs/nfsd.man +index 7365a1b..47b73be 100644 +--- a/utils/exportfs/nfsd.man ++++ b/utils/exportfs/nfsd.man +@@ -12,7 +12,7 @@ nfsd \- special filesystem for controlling Linux NFS server + .SH DESCRIPTION + The + .B nfsd +-filesytem is a special filesystem which provides access to the Linux ++filesystem is a special filesystem which provides access to the Linux + NFS server. The filesystem consists of a single directory which + contains a number of files. These files are actually gateways into + the NFS server. Writing to them can affect the server. Reading from +@@ -86,7 +86,7 @@ should be followed by a newline, with white-space separating the + fields, and octal quoting of special characters. + + On writing this, the program will be able to read back a filehandle +-for that path as exported to the given client. The filehandles length ++for that path as exported to the given client. The filehandle's length + will be at most the number of bytes given. + + The filehandle will be represented in hex with a leading '\ex'. +@@ -165,7 +165,7 @@ file. The user-space program might then write + .ti +5 + nfsd 127.0.0.1 1057206953 localhost + .br +-to indicate that 127.0.0.1 should map to localhost, atleast for now. ++to indicate that 127.0.0.1 should map to localhost, at least for now. + + If the program uses select(2) or poll(2) to discover if it can read + from the +diff --git a/utils/gssd/Makefile.am b/utils/gssd/Makefile.am +index d7888ad..2365704 100644 +--- a/utils/gssd/Makefile.am ++++ b/utils/gssd/Makefile.am +@@ -17,7 +17,6 @@ COMMON_SRCS = \ + context_mit.c \ + context_heimdal.c \ + context_lucid.c \ +- context_spkm3.c \ + gss_util.c \ + gss_oids.c \ + err_util.c \ +@@ -40,7 +39,7 @@ gssd_SOURCES = \ + + gssd_LDADD = ../../support/nfs/libnfs.a \ + $(RPCSECGSS_LIBS) $(GSSGLUE_LIBS) $(KRBLIBS) +-gssd_LDFLAGS = $(KRBLDFLAGS) ++gssd_LDFLAGS = $(KRBLDFLAGS) $(LIBTIRPC) + + gssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \ + $(RPCSECGSS_CFLAGS) $(GSSGLUE_CFLAGS) $(KRBCFLAGS) +@@ -58,8 +57,8 @@ svcgssd_SOURCES = \ + + svcgssd_LDADD = \ + ../../support/nfs/libnfs.a \ +- $(RPCSECGSS_LIBS) $(GSSGLUE_LIBS) -lnfsidmap \ +- $(KRBLIBS) ++ $(RPCSECGSS_LIBS) $(GSSGLUE_LIBS) $(LIBNFSIDMAP) \ ++ $(KRBLIBS) $(LIBTIRPC) + + svcgssd_LDFLAGS = $(KRBLDFLAGS) + +diff --git a/utils/gssd/context.c b/utils/gssd/context.c +index 1e50bbf..fee7da2 100644 +--- a/utils/gssd/context.c ++++ b/utils/gssd/context.c +@@ -51,10 +51,6 @@ serialize_context_for_kernel(gss_ctx_id_t ctx, + { + if (g_OID_equal(&krb5oid, mech)) + return serialize_krb5_ctx(ctx, buf, endtime); +-#ifdef HAVE_SPKM3_H +- else if (g_OID_equal(&spkm3oid, mech)) +- return serialize_spkm3_ctx(ctx, buf, endtime); +-#endif + else { + printerr(0, "ERROR: attempting to serialize context with " + "unknown/unsupported mechanism oid\n"); +diff --git a/utils/gssd/context.h b/utils/gssd/context.h +index c9cb0bd..0e437f4 100644 +--- a/utils/gssd/context.h ++++ b/utils/gssd/context.h +@@ -43,8 +43,6 @@ + + int serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf, + gss_OID mech, int32_t *endtime); +-int serialize_spkm3_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, +- int32_t *endtime); + int serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, + int32_t *endtime); + +diff --git a/utils/gssd/context_lucid.c b/utils/gssd/context_lucid.c +index 3e695ab..64146d7 100644 +--- a/utils/gssd/context_lucid.c ++++ b/utils/gssd/context_lucid.c +@@ -80,6 +80,7 @@ prepare_krb5_rfc1964_buffer(gss_krb5_lucid_context_v1_t *lctx, + uint32_t i; + char *skd, *dkd; + gss_buffer_desc fakeoid; ++ int err; + + /* + * The new Kerberos interface to get the gss context +@@ -138,11 +139,10 @@ prepare_krb5_rfc1964_buffer(gss_krb5_lucid_context_v1_t *lctx, + dkd = (char *) enc_key.data; + for (i = 0; i < enc_key.length; i++) + dkd[i] = skd[i] ^ 0xf0; +- if (write_lucid_keyblock(&p, end, &enc_key)) { +- free(enc_key.data); +- goto out_err; +- } ++ err = write_lucid_keyblock(&p, end, &enc_key); + free(enc_key.data); ++ if (err) ++ goto out_err; + + if (write_lucid_keyblock(&p, end, &lctx->rfc1964_kd.ctx_key)) + goto out_err; +@@ -153,7 +153,6 @@ out_err: + printerr(0, "ERROR: failed serializing krb5 context for kernel\n"); + if (buf->value) free(buf->value); + buf->length = 0; +- if (enc_key.data) free(enc_key.data); + return -1; + } + +diff --git a/utils/gssd/context_spkm3.c b/utils/gssd/context_spkm3.c +deleted file mode 100644 +index b927475..0000000 +--- a/utils/gssd/context_spkm3.c ++++ /dev/null +@@ -1,184 +0,0 @@ +-/* +- Copyright (c) 2004 The Regents of the University of Michigan. +- All rights reserved. +- +- Redistribution and use in source and binary forms, with or without +- modification, are permitted provided that the following conditions +- are met: +- +- 1. Redistributions of source code must retain the above copyright +- notice, this list of conditions and the following disclaimer. +- 2. Redistributions in binary form must reproduce the above copyright +- notice, this list of conditions and the following disclaimer in the +- documentation and/or other materials provided with the distribution. +- 3. Neither the name of the University nor the names of its +- contributors may be used to endorse or promote products derived +- from this software without specific prior written permission. +- +- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +- DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +- BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-*/ +- +-#ifdef HAVE_CONFIG_H +-#include +-#endif /* HAVE_CONFIG_H */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include "gss_util.h" +-#include "gss_oids.h" +-#include "err_util.h" +-#include "context.h" +- +-#ifdef HAVE_SPKM3_H +- +-#include +- +-/* +- * Function: prepare_spkm3_ctx_buffer() +- * +- * Prepare spkm3 lucid context for the kernel +- * +- * buf->length should be: +- * +- * version 4 +- * ctx_id 4 + 12 +- * qop 4 +- * mech_used 4 + 7 +- * ret_fl 4 +- * req_fl 4 +- * share 4 + key_len +- * conf_alg 4 + oid_len +- * d_conf_key 4 + key_len +- * intg_alg 4 + oid_len +- * d_intg_key 4 + key_len +- * kyestb 4 + oid_len +- * owl alg 4 + oid_len +-*/ +-static int +-prepare_spkm3_ctx_buffer(gss_spkm3_lucid_ctx_t *lctx, gss_buffer_desc *buf) +-{ +- char *p, *end; +- unsigned int buf_size = 0; +- +- buf_size = sizeof(lctx->version) + +- lctx->ctx_id.length + sizeof(lctx->ctx_id.length) + +- sizeof(lctx->endtime) + +- sizeof(lctx->mech_used.length) + lctx->mech_used.length + +- sizeof(lctx->ret_flags) + +- sizeof(lctx->conf_alg.length) + lctx->conf_alg.length + +- sizeof(lctx->derived_conf_key.length) + +- lctx->derived_conf_key.length + +- sizeof(lctx->intg_alg.length) + lctx->intg_alg.length + +- sizeof(lctx->derived_integ_key.length) + +- lctx->derived_integ_key.length; +- +- if (!(buf->value = calloc(1, buf_size))) +- goto out_err; +- p = buf->value; +- end = buf->value + buf_size; +- +- if (WRITE_BYTES(&p, end, lctx->version)) +- goto out_err; +- printerr(2, "DEBUG: exporting version = %d\n", lctx->version); +- +- if (write_buffer(&p, end, &lctx->ctx_id)) +- goto out_err; +- printerr(2, "DEBUG: exporting ctx_id(%d)\n", lctx->ctx_id.length); +- +- if (WRITE_BYTES(&p, end, lctx->endtime)) +- goto out_err; +- printerr(2, "DEBUG: exporting endtime = %d\n", lctx->endtime); +- +- if (write_buffer(&p, end, &lctx->mech_used)) +- goto out_err; +- printerr(2, "DEBUG: exporting mech oid (%d)\n", lctx->mech_used.length); +- +- if (WRITE_BYTES(&p, end, lctx->ret_flags)) +- goto out_err; +- printerr(2, "DEBUG: exporting ret_flags = %d\n", lctx->ret_flags); +- +- if (write_buffer(&p, end, &lctx->conf_alg)) +- goto out_err; +- printerr(2, "DEBUG: exporting conf_alg oid (%d)\n", lctx->conf_alg.length); +- +- if (write_buffer(&p, end, &lctx->derived_conf_key)) +- goto out_err; +- printerr(2, "DEBUG: exporting conf key (%d)\n", lctx->derived_conf_key.length); +- +- if (write_buffer(&p, end, &lctx->intg_alg)) +- goto out_err; +- printerr(2, "DEBUG: exporting intg_alg oid (%d)\n", lctx->intg_alg.length); +- +- if (write_buffer(&p, end, &lctx->derived_integ_key)) +- goto out_err; +- printerr(2, "DEBUG: exporting intg key (%d)\n", lctx->derived_integ_key.length); +- +- buf->length = p - (char *)buf->value; +- return 0; +-out_err: +- printerr(0, "ERROR: failed serializing spkm3 context for kernel\n"); +- if (buf->value) free(buf->value); +- buf->length = 0; +- +- return -1; +-} +- +-/* ANDROS: need to determine which fields of the spkm3_gss_ctx_id_desc_t +- * are needed in the kernel for get_mic, validate, wrap, unwrap, and destroy +- * and only export those fields to the kernel. +- */ +-int +-serialize_spkm3_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, int32_t *endtime) +-{ +- OM_uint32 vers, ret, maj_stat, min_stat; +- void *ret_ctx = 0; +- gss_spkm3_lucid_ctx_t *lctx; +- +- printerr(1, "serialize_spkm3_ctx called\n"); +- +- printerr(2, "DEBUG: serialize_spkm3_ctx: lucid version!\n"); +- maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx, 1, &ret_ctx); +- if (maj_stat != GSS_S_COMPLETE) +- goto out_err; +- +- lctx = (gss_spkm3_lucid_ctx_t *)ret_ctx; +- +- vers = lctx->version; +- if (vers != 1) { +- printerr(0, "ERROR: unsupported spkm3 context version %d\n", +- vers); +- goto out_err; +- } +- ret = prepare_spkm3_ctx_buffer(lctx, buf); +- +- if (endtime) +- *endtime = lctx->endtime; +- +- maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, ret_ctx); +- +- if (maj_stat != GSS_S_COMPLETE) +- printerr(0, "WARN: failed to free lucid sec context\n"); +- if (ret) +- goto out_err; +- printerr(2, "DEBUG: serialize_spkm3_ctx: success\n"); +- return 0; +- +-out_err: +- printerr(2, "DEBUG: serialize_spkm3_ctx: failed\n"); +- return -1; +-} +-#endif /* HAVE_SPKM3_H */ +diff --git a/utils/gssd/gss_oids.c b/utils/gssd/gss_oids.c +index a59c4a6..4362de2 100644 +--- a/utils/gssd/gss_oids.c ++++ b/utils/gssd/gss_oids.c +@@ -38,6 +38,3 @@ + /* from kerberos source, gssapi_krb5.c */ + gss_OID_desc krb5oid = + {9, "\052\206\110\206\367\022\001\002\002"}; +- +-gss_OID_desc spkm3oid = +- {7, "\053\006\001\005\005\001\003"}; +diff --git a/utils/gssd/gss_oids.h b/utils/gssd/gss_oids.h +index 8b0a352..fde8532 100644 +--- a/utils/gssd/gss_oids.h ++++ b/utils/gssd/gss_oids.h +@@ -34,7 +34,6 @@ + #include + + extern gss_OID_desc krb5oid; +-extern gss_OID_desc spkm3oid; + + #ifndef g_OID_equal + #define g_OID_equal(o1,o2) \ +diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c +index ccadb07..7825255 100644 +--- a/utils/gssd/gssd.c ++++ b/utils/gssd/gssd.c +@@ -57,7 +57,7 @@ + + char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR; + char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE; +-char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR; ++char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR ":" GSSD_USER_CRED_DIR; + char *ccachesearch[GSSD_MAX_CCACHE_SEARCH + 1]; + int use_memcache = 0; + int root_uses_machine_creds = 1; +@@ -85,7 +85,7 @@ sig_hup(int signal) + static void + usage(char *progname) + { +- fprintf(stderr, "usage: %s [-f] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm]\n", ++ fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm]\n", + progname); + exit(1); + } +@@ -102,7 +102,7 @@ main(int argc, char *argv[]) + char *progname; + + memset(ccachesearch, 0, sizeof(ccachesearch)); +- while ((opt = getopt(argc, argv, "fvrmnMp:k:d:t:R:")) != -1) { ++ while ((opt = getopt(argc, argv, "fvrlmnMp:k:d:t:R")) != -1) { + switch (opt) { + case 'f': + fg = 1; +@@ -143,6 +143,13 @@ main(int argc, char *argv[]) + case 'R': + preferred_realm = strdup(optarg); + break; ++ case 'l': ++#ifdef HAVE_SET_ALLOWABLE_ENCTYPES ++ limit_to_legacy_enctypes = 1; ++#else ++ errx(1, "Setting encryption type not support by Kerberos libraries."); ++#endif ++ break; + default: + usage(argv[0]); + break; +diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h +index b1b5793..28a8206 100644 +--- a/utils/gssd/gssd.h ++++ b/utils/gssd/gssd.h +@@ -45,6 +45,7 @@ + #define DNOTIFY_SIGNAL (SIGRTMIN + 3) + + #define GSSD_DEFAULT_CRED_DIR "/tmp" ++#define GSSD_USER_CRED_DIR "/run/user" + #define GSSD_DEFAULT_CRED_PREFIX "krb5cc_" + #define GSSD_DEFAULT_MACHINE_CRED_SUFFIX "machine" + #define GSSD_DEFAULT_KEYTAB_FILE "/etc/krb5.keytab" +@@ -55,7 +56,7 @@ + /* + * The gss mechanisms that we can handle + */ +-enum {AUTHTYPE_KRB5, AUTHTYPE_SPKM3, AUTHTYPE_LIPKEY}; ++enum {AUTHTYPE_KRB5, AUTHTYPE_LIPKEY}; + + + +@@ -80,8 +81,6 @@ struct clnt_info { + char *protocol; + int krb5_fd; + int krb5_poll_index; +- int spkm3_fd; +- int spkm3_poll_index; + int gssd_fd; + int gssd_poll_index; + struct sockaddr_storage addr; +@@ -98,7 +97,6 @@ struct topdirs_info { + void init_client_list(void); + int update_client_list(void); + void handle_krb5_upcall(struct clnt_info *clp); +-void handle_spkm3_upcall(struct clnt_info *clp); + void handle_gssd_upcall(struct clnt_info *clp); + void gssd_run(void); + +diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man +index 073379d..d8138fa 100644 +--- a/utils/gssd/gssd.man ++++ b/utils/gssd/gssd.man +@@ -6,7 +6,7 @@ + .SH NAME + rpc.gssd \- rpcsec_gss daemon + .SH SYNOPSIS +-.B "rpc.gssd [-f] [-n] [-k keytab] [-p pipefsdir] [-v] [-r] [-d ccachedir]" ++.B "rpc.gssd [-f] [-n] [-k keytab] [-l] [-p pipefsdir] [-v] [-r] [-d ccachedir]" + .SH DESCRIPTION + The rpcsec_gss protocol gives a means of using the gss-api generic security + api to provide security for protocols using rpc (in particular, nfs). Before +@@ -70,6 +70,30 @@ for "machine credentials" is now: + If this search order does not use the correct key then provide a + keytab file that contains only correct keys. + .TP ++.B -l ++Tells ++.B rpc.gssd ++to limit session keys to Single DES even if the kernel supports stronger ++encryption types. Service ticket encryption is still governed by what ++the KDC believes the target server supports. This way the client can ++access a server that has strong keys in its keytab for ticket decryption ++but whose kernel only supports Single DES. ++.IP ++The alternative is to put only Single DES keys in the server's keytab ++and limit encryption types for its principal to Single DES on the KDC ++which will cause service tickets for this server to be encrypted using ++only Single DES and (as a side-effect) contain only Single DES session ++keys. ++.IP ++This legacy behaviour is only required for older servers ++(pre nfs-utils-1.2.4). If the server has a recent kernel, Kerberos ++implementation and nfs-utils it will work just fine with stronger ++encryption. ++.IP ++.B Note: ++This option is only available with Kerberos libraries that ++support setable encryption types. ++.TP + .B -p path + Tells + .B rpc.gssd +diff --git a/utils/gssd/gssd_main_loop.c b/utils/gssd/gssd_main_loop.c +index b06c223..cec09ea 100644 +--- a/utils/gssd/gssd_main_loop.c ++++ b/utils/gssd/gssd_main_loop.c +@@ -98,17 +98,6 @@ scan_poll_results(int ret) + if (!ret) + break; + } +- i = clp->spkm3_poll_index; +- if (i >= 0 && pollarray[i].revents) { +- if (pollarray[i].revents & POLLHUP) +- dir_changed = 1; +- if (pollarray[i].revents & POLLIN) +- handle_spkm3_upcall(clp); +- pollarray[clp->spkm3_poll_index].revents = 0; +- ret--; +- if (!ret) +- break; +- } + } + }; + +diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c +index 41328c9..aa39435 100644 +--- a/utils/gssd/gssd_proc.c ++++ b/utils/gssd/gssd_proc.c +@@ -299,15 +299,11 @@ destroy_client(struct clnt_info *clp) + if (clp->krb5_poll_index != -1) + memset(&pollarray[clp->krb5_poll_index], 0, + sizeof(struct pollfd)); +- if (clp->spkm3_poll_index != -1) +- memset(&pollarray[clp->spkm3_poll_index], 0, +- sizeof(struct pollfd)); + if (clp->gssd_poll_index != -1) + memset(&pollarray[clp->gssd_poll_index], 0, + sizeof(struct pollfd)); + if (clp->dir_fd != -1) close(clp->dir_fd); + if (clp->krb5_fd != -1) close(clp->krb5_fd); +- if (clp->spkm3_fd != -1) close(clp->spkm3_fd); + if (clp->gssd_fd != -1) close(clp->gssd_fd); + free(clp->dirname); + free(clp->servicename); +@@ -327,10 +323,8 @@ insert_new_clnt(void) + goto out; + } + clp->krb5_poll_index = -1; +- clp->spkm3_poll_index = -1; + clp->gssd_poll_index = -1; + clp->krb5_fd = -1; +- clp->spkm3_fd = -1; + clp->gssd_fd = -1; + clp->dir_fd = -1; + +@@ -355,30 +349,22 @@ process_clnt_dir_files(struct clnt_info * clp) + snprintf(name, sizeof(name), "%s/krb5", clp->dirname); + clp->krb5_fd = open(name, O_RDWR); + } +- if (clp->spkm3_fd == -1) { +- snprintf(name, sizeof(name), "%s/spkm3", clp->dirname); +- clp->spkm3_fd = open(name, O_RDWR); +- } + + /* If we opened a gss-specific pipe, let's try opening + * the new upcall pipe again. If we succeed, close + * gss-specific pipe(s). + */ +- if (clp->krb5_fd != -1 || clp->spkm3_fd != -1) { ++ if (clp->krb5_fd != -1) { + clp->gssd_fd = open(gname, O_RDWR); + if (clp->gssd_fd != -1) { + if (clp->krb5_fd != -1) + close(clp->krb5_fd); + clp->krb5_fd = -1; +- if (clp->spkm3_fd != -1) +- close(clp->spkm3_fd); +- clp->spkm3_fd = -1; + } + } + } + +- if ((clp->krb5_fd == -1) && (clp->spkm3_fd == -1) && +- (clp->gssd_fd == -1)) ++ if ((clp->krb5_fd == -1) && (clp->gssd_fd == -1)) + return -1; + snprintf(info_file_name, sizeof(info_file_name), "%s/info", + clp->dirname); +@@ -431,15 +417,6 @@ insert_clnt_poll(struct clnt_info *clp) + pollarray[clp->krb5_poll_index].events |= POLLIN; + } + +- if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) { +- if (get_poll_index(&clp->spkm3_poll_index)) { +- printerr(0, "ERROR: Too many spkm3 clients\n"); +- return -1; +- } +- pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd; +- pollarray[clp->spkm3_poll_index].events |= POLLIN; +- } +- + return 0; + } + +@@ -839,13 +816,6 @@ int create_auth_rpc_client(struct clnt_info *clp, + sec.mech = (gss_OID)&krb5oid; + sec.req_flags = GSS_C_MUTUAL_FLAG; + } +- else if (authtype == AUTHTYPE_SPKM3) { +- sec.mech = (gss_OID)&spkm3oid; +- /* XXX sec.req_flags = GSS_C_ANON_FLAG; +- * Need a way to switch.... +- */ +- sec.req_flags = GSS_C_MUTUAL_FLAG; +- } + else { + printerr(0, "ERROR: Invalid authentication type (%d) " + "in create_auth_rpc_client\n", authtype); +@@ -919,9 +889,8 @@ int create_auth_rpc_client(struct clnt_info *clp, + auth = authgss_create_default(rpc_clnt, clp->servicename, &sec); + if (!auth) { + /* Our caller should print appropriate message */ +- printerr(2, "WARNING: Failed to create %s context for " ++ printerr(2, "WARNING: Failed to create krb5 context for " + "user with uid %d for server %s\n", +- (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"), + uid, clp->servername); + goto out_fail; + } +@@ -949,6 +918,23 @@ int create_auth_rpc_client(struct clnt_info *clp, + goto out; + } + ++static char * ++user_cachedir(char *dirname, uid_t uid) ++{ ++ struct passwd *pw; ++ char *ptr; ++ ++ if ((pw = getpwuid(uid)) == NULL) { ++ printerr(0, "user_cachedir: Failed to find '%d' uid" ++ " for cache directory\n"); ++ return NULL; ++ } ++ ptr = malloc(strlen(dirname)+strlen(pw->pw_name)+2); ++ if (ptr) ++ sprintf(ptr, "%s/%s", dirname, pw->pw_name); ++ ++ return ptr; ++} + /* + * this code uses the userland rpcsec gss library to create a krb5 + * context on behalf of the kernel +@@ -963,7 +949,7 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname, + gss_buffer_desc token; + char **credlist = NULL; + char **ccname; +- char **dirname; ++ char **dirname, *dir, *userdir; + int create_resp = -1; + int err, downcall_err = -EACCES; + +@@ -1006,7 +992,22 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname, + service == NULL)) { + /* Tell krb5 gss which credentials cache to use */ + for (dirname = ccachesearch; *dirname != NULL; dirname++) { +- err = gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname); ++ /* See if the user name is needed */ ++ if (strncmp(*dirname, GSSD_USER_CRED_DIR, ++ strlen(GSSD_USER_CRED_DIR)) == 0) { ++ userdir = user_cachedir(*dirname, uid); ++ if (userdir == NULL) ++ continue; ++ dir = userdir; ++ } else ++ dir = *dirname; ++ ++ err = gssd_setup_krb5_user_gss_ccache(uid, clp->servername, dir); ++ ++ if (userdir) { ++ free(userdir); ++ userdir = NULL; ++ } + if (err == -EKEYEXPIRED) + downcall_err = -EKEYEXPIRED; + else if (!err) +@@ -1103,59 +1104,6 @@ out_return_error: + goto out; + } + +-/* +- * this code uses the userland rpcsec gss library to create an spkm3 +- * context on behalf of the kernel +- */ +-static void +-process_spkm3_upcall(struct clnt_info *clp, uid_t uid, int fd) +-{ +- CLIENT *rpc_clnt = NULL; +- AUTH *auth = NULL; +- struct authgss_private_data pd; +- gss_buffer_desc token; +- +- printerr(2, "handling spkm3 upcall (%s)\n", clp->dirname); +- +- token.length = 0; +- token.value = NULL; +- +- if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) { +- printerr(0, "WARNING: Failed to create spkm3 context for " +- "user with uid %d\n", uid); +- goto out_return_error; +- } +- +- if (!authgss_get_private_data(auth, &pd)) { +- printerr(0, "WARNING: Failed to obtain authentication " +- "data for user with uid %d for server %s\n", +- uid, clp->servername); +- goto out_return_error; +- } +- +- if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid, NULL)) { +- printerr(0, "WARNING: Failed to serialize spkm3 context for " +- "user with uid %d for server\n", +- uid, clp->servername); +- goto out_return_error; +- } +- +- do_downcall(fd, uid, &pd, &token); +- +-out: +- if (token.value) +- free(token.value); +- if (auth) +- AUTH_DESTROY(auth); +- if (rpc_clnt) +- clnt_destroy(rpc_clnt); +- return; +- +-out_return_error: +- do_error_downcall(fd, uid, -1); +- goto out; +-} +- + void + handle_krb5_upcall(struct clnt_info *clp) + { +@@ -1171,20 +1119,6 @@ handle_krb5_upcall(struct clnt_info *clp) + } + + void +-handle_spkm3_upcall(struct clnt_info *clp) +-{ +- uid_t uid; +- +- if (read(clp->spkm3_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) { +- printerr(0, "WARNING: failed reading uid from spkm3 " +- "upcall pipe: %s\n", strerror(errno)); +- return; +- } +- +- return process_spkm3_upcall(clp, uid, clp->spkm3_fd); +-} +- +-void + handle_gssd_upcall(struct clnt_info *clp) + { + uid_t uid; +@@ -1292,8 +1226,6 @@ handle_gssd_upcall(struct clnt_info *clp) + + if (strcmp(mech, "krb5") == 0) + process_krb5_upcall(clp, uid, clp->gssd_fd, target, service); +- else if (strcmp(mech, "spkm3") == 0) +- process_spkm3_upcall(clp, uid, clp->gssd_fd); + else + printerr(0, "WARNING: handle_gssd_upcall: " + "received unknown gss mech '%s'\n", mech); +diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c +index 4b13fa1..887d118 100644 +--- a/utils/gssd/krb5_util.c ++++ b/utils/gssd/krb5_util.c +@@ -129,6 +129,10 @@ + /* Global list of principals/cache file names for machine credentials */ + struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL; + ++#ifdef HAVE_SET_ALLOWABLE_ENCTYPES ++int limit_to_legacy_enctypes = 0; ++#endif ++ + /*==========================*/ + /*=== Internal routines ===*/ + /*==========================*/ +@@ -1342,7 +1346,7 @@ limit_krb5_enctypes(struct rpc_gss_sec *sec) + * If we failed for any reason to produce global + * list of supported enctypes, use local default here. + */ +- if (krb5_enctypes == NULL) ++ if (krb5_enctypes == NULL || limit_to_legacy_enctypes) + maj_stat = gss_set_allowable_enctypes(&min_stat, credh, + &krb5oid, num_enctypes, enctypes); + else +diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h +index b42b91e..cd6e107 100644 +--- a/utils/gssd/krb5_util.h ++++ b/utils/gssd/krb5_util.h +@@ -36,6 +36,7 @@ char *gssd_k5_err_msg(krb5_context context, krb5_error_code code); + void gssd_k5_get_default_realm(char **def_realm); + + #ifdef HAVE_SET_ALLOWABLE_ENCTYPES ++extern int limit_to_legacy_enctypes; + int limit_krb5_enctypes(struct rpc_gss_sec *sec); + #endif + +diff --git a/utils/gssd/svcgssd_mech2file.c b/utils/gssd/svcgssd_mech2file.c +index 65de8d0..ecd908b 100644 +--- a/utils/gssd/svcgssd_mech2file.c ++++ b/utils/gssd/svcgssd_mech2file.c +@@ -53,8 +53,6 @@ struct mech2file { + + struct mech2file m2f[] = { + {{9, "\052\206\110\206\367\022\001\002\002"}, "krb5"}, +- {{7, "\053\006\001\005\005\001\003"}, "spkm3"}, +- {{7, "\053\006\001\005\005\001\009"}, "lipkey"}, + {{0,0},""}, + }; + +diff --git a/utils/gssd/svcgssd_proc.c b/utils/gssd/svcgssd_proc.c +index c714d99..0d4f78d 100644 +--- a/utils/gssd/svcgssd_proc.c ++++ b/utils/gssd/svcgssd_proc.c +@@ -369,12 +369,8 @@ get_hostbased_client_name(gss_name_t client_name, gss_OID mech, + if (g_OID_equal(&krb5oid, mech)) { + if (get_krb5_hostbased_name(&name, &cname) == 0) + *hostbased_name = cname; +- } +- +- /* No support for SPKM3, just print a warning (for now) */ +- if (g_OID_equal(&spkm3oid, mech)) { +- printerr(1, "WARNING: get_hostbased_client_name: " +- "no hostbased_name support for SPKM3\n"); ++ } else { ++ printerr(1, "WARNING: unknown/unsupport mech OID\n"); + } + + res = 0; +diff --git a/utils/idmapd/Makefile.am b/utils/idmapd/Makefile.am +index 4328e41..58b33ec 100644 +--- a/utils/idmapd/Makefile.am ++++ b/utils/idmapd/Makefile.am +@@ -16,7 +16,7 @@ idmapd_SOURCES = \ + nfs_idmap.h \ + queue.h + +-idmapd_LDADD = -levent -lnfsidmap ../../support/nfs/libnfs.a ++idmapd_LDADD = $(LIBEVENT) $(LIBNFSIDMAP) ../../support/nfs/libnfs.a + + MAINTAINERCLEANFILES = Makefile.in + +diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c +index 19d9114..e80efb4 100644 +--- a/utils/idmapd/idmapd.c ++++ b/utils/idmapd/idmapd.c +@@ -778,8 +778,8 @@ nfsopen(struct idmap_client *ic) + } else { + event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfscb, ic); + event_add(&ic->ic_event, NULL); +- fcntl(ic->ic_dirfd, F_SETSIG, 0); + fcntl(ic->ic_dirfd, F_NOTIFY, 0); ++ fcntl(ic->ic_dirfd, F_SETSIG, 0); + if (verbose > 0) + xlog_warn("Opened %s", ic->ic_path); + } +diff --git a/utils/mount/Makefile.am b/utils/mount/Makefile.am +index 7bc3e2b..7627854 100644 +--- a/utils/mount/Makefile.am ++++ b/utils/mount/Makefile.am +@@ -24,7 +24,8 @@ EXTRA_DIST += nfsmount.conf + endif + + mount_nfs_LDADD = ../../support/nfs/libnfs.a \ +- ../../support/export/libexport.a ++ ../../support/export/libexport.a \ ++ $(LIBTIRPC) + + mount_nfs_SOURCES = $(mount_common) + +diff --git a/utils/mount/mount_libmount.c b/utils/mount/mount_libmount.c +index e450d79..e8f17a9 100644 +--- a/utils/mount/mount_libmount.c ++++ b/utils/mount/mount_libmount.c +@@ -346,6 +346,21 @@ static int mount_main(struct libmnt_context *cxt, int argc, char **argv) + + if (chk_mountpoint(mount_point)) + goto err; ++ ++ /* ++ * The libmount strictly uses only options from fstab if running in ++ * restricted mode (suid, non-root user). This is done in ++ * mnt_context_prepare_mount() by default. ++ * ++ * We have to read fstab before nfsmount.conf, otherwise the options ++ * from nfsmount.conf will be ignored (overwrited). ++ */ ++ rc = mnt_context_apply_fstab(cxt); ++ if (rc) { ++ nfs_error(_("%s: failed to apply fstab options\n"), progname); ++ goto err; ++ } ++ + /* + * Concatenate mount options from the configuration file + */ +diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man +index ce40933..0d20cf0 100644 +--- a/utils/mount/nfs.man ++++ b/utils/mount/nfs.man +@@ -372,14 +372,8 @@ Valid security flavors are + .BR sys , + .BR krb5 , + .BR krb5i , +-.BR krb5p , +-.BR lkey , +-.BR lkeyi , +-.BR lkeyp , +-.BR spkm , +-.BR spkmi , + and +-.BR spkmp . ++.BR krb5p , + Refer to the SECURITY CONSIDERATIONS section for details. + .TP 1.5i + .BR sharecache " / " nosharecache +@@ -1416,7 +1410,7 @@ security flavor encrypts every RPC request + to prevent data exposure during network transit; however, + expect some performance impact + when using integrity checking or encryption. +-Similar support for other forms of cryptographic security (such as lipkey and SPKM3) ++Similar support for other forms of cryptographic security + is also available. + .P + The NFS version 4 protocol allows +@@ -1561,10 +1555,10 @@ To ensure that the saved mount options are not erased during a remount, + specify either the local mount directory, or the server hostname and + export pathname, but not both, during a remount. For example, + .P +-.NF +-.TA 2.5i ++.nf ++.ta 8n + mount -o remount,ro /mnt +-.FI ++.fi + .P + merges the mount option + .B ro +diff --git a/utils/mount/nfs_mount.h b/utils/mount/nfs_mount.h +index 2becfb1..ec30c9b 100644 +--- a/utils/mount/nfs_mount.h ++++ b/utils/mount/nfs_mount.h +@@ -75,9 +75,6 @@ struct nfs_mount_data { + #define AUTH_GSS_LKEY 390006 + #define AUTH_GSS_LKEYI 390007 + #define AUTH_GSS_LKEYP 390008 +-#define AUTH_GSS_SPKM 390009 +-#define AUTH_GSS_SPKMI 390010 +-#define AUTH_GSS_SPKMP 390011 + #endif + + int nfsmount(const char *, const char *, int , char **, int, int); +diff --git a/utils/mount/nfsmount.c b/utils/mount/nfsmount.c +index 1298fe4..930622d 100644 +--- a/utils/mount/nfsmount.c ++++ b/utils/mount/nfsmount.c +@@ -294,18 +294,6 @@ parse_options(char *old_opts, struct nfs_mount_data *data, + data->pseudoflavor = AUTH_GSS_KRB5I; + else if (!strcmp(secflavor, "krb5p")) + data->pseudoflavor = AUTH_GSS_KRB5P; +- else if (!strcmp(secflavor, "lipkey")) +- data->pseudoflavor = AUTH_GSS_LKEY; +- else if (!strcmp(secflavor, "lipkey-i")) +- data->pseudoflavor = AUTH_GSS_LKEYI; +- else if (!strcmp(secflavor, "lipkey-p")) +- data->pseudoflavor = AUTH_GSS_LKEYP; +- else if (!strcmp(secflavor, "spkm3")) +- data->pseudoflavor = AUTH_GSS_SPKM; +- else if (!strcmp(secflavor, "spkm3i")) +- data->pseudoflavor = AUTH_GSS_SPKMI; +- else if (!strcmp(secflavor, "spkm3p")) +- data->pseudoflavor = AUTH_GSS_SPKMP; + else if (sloppy) + continue; + else { +diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c +index 314a806..e09aa7c 100644 +--- a/utils/mount/stropts.c ++++ b/utils/mount/stropts.c +@@ -540,6 +540,8 @@ nfs_rewrite_pmap_mount_options(struct mount_options *options) + errno = EOPNOTSUPP; + else if (rpc_createerr.cf_stat == RPC_AUTHERROR) + errno = EACCES; ++ else if (rpc_createerr.cf_stat == RPC_TIMEDOUT) ++ errno = ETIMEDOUT; + else if (rpc_createerr.cf_error.re_errno != 0) + errno = rpc_createerr.cf_error.re_errno; + return 0; +@@ -665,9 +667,10 @@ static int nfs_try_mount_v3v2(struct nfsmount_info *mi) + case EHOSTUNREACH: + continue; + default: +- break; ++ goto out; + } + } ++out: + return ret; + } + +@@ -751,9 +754,10 @@ static int nfs_try_mount_v4(struct nfsmount_info *mi) + case EHOSTUNREACH: + continue; + default: +- break; ++ goto out; + } + } ++out: + return ret; + } + +@@ -907,7 +911,8 @@ static int nfsmount_parent(struct nfsmount_info *mi) + if (nfs_try_mount(mi)) + return EX_SUCCESS; + +- if (nfs_is_permanent_error(errno)) { ++ /* retry background mounts when the server is not up */ ++ if (nfs_is_permanent_error(errno) && errno != EOPNOTSUPP) { + mount_error(mi->spec, mi->node, errno); + return EX_FAIL; + } +@@ -942,7 +947,8 @@ static int nfsmount_child(struct nfsmount_info *mi) + if (nfs_try_mount(mi)) + return EX_SUCCESS; + +- if (nfs_is_permanent_error(errno)) ++ /* retry background mounts when the server is not up */ ++ if (nfs_is_permanent_error(errno) && errno != EOPNOTSUPP) + break; + + if (time(NULL) > timeout) +diff --git a/utils/mountd/Makefile.am b/utils/mountd/Makefile.am +index eba81fc..7db968b 100644 +--- a/utils/mountd/Makefile.am ++++ b/utils/mountd/Makefile.am +@@ -12,7 +12,7 @@ mountd_SOURCES = mountd.c mount_dispatch.c auth.c rmtab.c cache.c \ + mountd_LDADD = ../../support/export/libexport.a \ + ../../support/nfs/libnfs.a \ + ../../support/misc/libmisc.a \ +- $(LIBBSD) $(LIBWRAP) $(LIBNSL) $(LIBBLKID) ++ $(LIBBSD) $(LIBWRAP) $(LIBNSL) $(LIBBLKID) $(LIBDL) $(LIBTIRPC) + mountd_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) \ + -I$(top_builddir)/support/include \ + -I$(top_srcdir)/support/export +diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c +index ccc849a..508040a 100644 +--- a/utils/mountd/auth.c ++++ b/utils/mountd/auth.c +@@ -112,15 +112,23 @@ auth_reload() + return counter; + } + ++static char *get_client_ipaddr_name(const struct sockaddr *caller) ++{ ++ char buf[INET6_ADDRSTRLEN + 1]; ++ ++ buf[0] = '$'; ++ host_ntop(caller, buf + 1, sizeof(buf) - 1); ++ return strdup(buf); ++} ++ + static char * + get_client_hostname(const struct sockaddr *caller, struct addrinfo *ai, + enum auth_error *error) + { +- char buf[INET6_ADDRSTRLEN]; + char *n; + + if (use_ipaddr) +- return strdup(host_ntop(caller, buf, sizeof(buf))); ++ return get_client_ipaddr_name(caller); + n = client_compose(ai); + *error = unknown_host; + if (!n) +@@ -131,6 +139,23 @@ get_client_hostname(const struct sockaddr *caller, struct addrinfo *ai, + return strdup("DEFAULT"); + } + ++bool ipaddr_client_matches(nfs_export *exp, struct addrinfo *ai) ++{ ++ return client_check(exp->m_client, ai); ++} ++ ++bool namelist_client_matches(nfs_export *exp, char *dom) ++{ ++ return client_member(dom, exp->m_client->m_hostname); ++} ++ ++bool client_matches(nfs_export *exp, char *dom, struct addrinfo *ai) ++{ ++ if (is_ipaddr_client(dom)) ++ return ipaddr_client_matches(exp, ai); ++ return namelist_client_matches(exp, dom); ++} ++ + /* return static nfs_export with details filled in */ + static nfs_export * + auth_authenticate_newcache(const struct sockaddr *caller, +@@ -155,9 +180,10 @@ auth_authenticate_newcache(const struct sockaddr *caller, + for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { + if (strcmp(path, exp->m_export.e_path)) + continue; +- if (!use_ipaddr && !client_member(my_client.m_hostname, exp->m_client->m_hostname)) ++ if (!client_matches(exp, my_client.m_hostname, ai)) + continue; +- if (use_ipaddr && !client_check(exp->m_client, ai)) ++ if (exp->m_export.e_flags & NFSEXP_V4ROOT) ++ /* not acceptable for v[23] export */ + continue; + break; + } +@@ -187,10 +213,6 @@ auth_authenticate_internal(const struct sockaddr *caller, const char *path, + return NULL; + } + } +- if (exp->m_export.e_flags & NFSEXP_V4ROOT) { +- *error = no_entry; +- return NULL; +- } + if (!(exp->m_export.e_flags & NFSEXP_INSECURE_PORT) && + nfs_get_port(caller) >= IPPORT_RESERVED) { + *error = illegal_port; +diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c +index d2ae456..7d80432 100644 +--- a/utils/mountd/cache.c ++++ b/utils/mountd/cache.c +@@ -84,7 +84,6 @@ static void auth_unix_ip(FILE *f) + char ipaddr[INET6_ADDRSTRLEN]; + char *client = NULL; + struct addrinfo *tmp = NULL; +- struct addrinfo *ai = NULL; + if (readline(fileno(f), &lbuf, &lbuflen) != 1) + return; + +@@ -107,12 +106,16 @@ static void auth_unix_ip(FILE *f) + + /* addr is a valid, interesting address, find the domain name... */ + if (!use_ipaddr) { ++ struct addrinfo *ai = NULL; ++ + ai = client_resolve(tmp->ai_addr); ++ if (ai == NULL) ++ goto out; + client = client_compose(ai); + freeaddrinfo(ai); ++ if (!client) ++ goto out; + } +- freeaddrinfo(tmp); +- + qword_print(f, "nfsd"); + qword_print(f, ipaddr); + qword_printuint(f, time(0) + DEFAULT_TTL); +@@ -124,6 +127,9 @@ static void auth_unix_ip(FILE *f) + xlog(D_CALL, "auth_unix_ip: client %p '%s'", client, client?client: "DEFAULT"); + + free(client); ++out: ++ freeaddrinfo(tmp); ++ + } + + static void auth_unix_gid(FILE *f) +@@ -495,6 +501,21 @@ static bool match_fsid(struct parsed_fsid *parsed, nfs_export *exp, char *path) + return false; + } + ++struct addrinfo *lookup_client_addr(char *dom) ++{ ++ struct addrinfo *ret; ++ struct addrinfo *tmp; ++ ++ dom++; /* skip initial "$" */ ++ ++ tmp = host_pton(dom); ++ if (tmp == NULL) ++ return NULL; ++ ret = client_resolve(tmp->ai_addr); ++ freeaddrinfo(tmp); ++ return ret; ++} ++ + static void nfsd_fh(FILE *f) + { + /* request are: +@@ -538,6 +559,12 @@ static void nfsd_fh(FILE *f) + + auth_reload(); + ++ if (is_ipaddr_client(dom)) { ++ ai = lookup_client_addr(dom); ++ if (!ai) ++ goto out; ++ } ++ + /* Now determine export point for this fsid/domain */ + for (i=0 ; i < MCL_MAXTYPES; i++) { + nfs_export *next_exp; +@@ -568,7 +595,8 @@ static void nfsd_fh(FILE *f) + next_exp = exp->m_next; + } + +- if (!use_ipaddr && !client_member(dom, exp->m_client->m_hostname)) ++ if (!is_ipaddr_client(dom) ++ && !namelist_client_matches(exp, dom)) + continue; + if (exp->m_export.e_mountpoint && + !is_mountpoint(exp->m_export.e_mountpoint[0]? +@@ -578,29 +606,29 @@ static void nfsd_fh(FILE *f) + + if (!match_fsid(&parsed, exp, path)) + continue; +- if (use_ipaddr) { +- if (ai == NULL) { +- struct addrinfo *tmp; +- tmp = host_pton(dom); +- if (tmp == NULL) +- goto out; +- ai = client_resolve(tmp->ai_addr); +- freeaddrinfo(tmp); +- } +- if (!client_check(exp->m_client, ai)) +- continue; +- } ++ if (is_ipaddr_client(dom) ++ && !ipaddr_client_matches(exp, ai)) ++ continue; + if (!found || subexport(&exp->m_export, found)) { + found = &exp->m_export; + free(found_path); + found_path = strdup(path); + if (found_path == NULL) + goto out; +- } else if (strcmp(found->e_path, exp->m_export.e_path) ++ } else if (strcmp(found->e_path, exp->m_export.e_path) != 0 + && !subexport(found, &exp->m_export)) + { + xlog(L_WARNING, "%s and %s have same filehandle for %s, using first", + found_path, path, dom); ++ } else { ++ /* same path, if one is V4ROOT, choose the other */ ++ if (found->e_flags & NFSEXP_V4ROOT) { ++ found = &exp->m_export; ++ free(found_path); ++ found_path = strdup(path); ++ if (found_path == NULL) ++ goto out; ++ } + } + } + } +@@ -742,14 +770,6 @@ static int path_matches(nfs_export *exp, char *path) + } + + static int +-client_matches(nfs_export *exp, char *dom, struct addrinfo *ai) +-{ +- if (use_ipaddr) +- return client_check(exp->m_client, ai); +- return client_member(dom, exp->m_client->m_hostname); +-} +- +-static int + export_matches(nfs_export *exp, char *dom, char *path, struct addrinfo *ai) + { + return path_matches(exp, path) && client_matches(exp, dom, ai); +@@ -772,10 +792,14 @@ lookup_export(char *dom, char *path, struct addrinfo *ai) + found_type = i; + continue; + } +- +- /* Always prefer non-V4ROOT mounts */ +- if (found->m_export.e_flags & NFSEXP_V4ROOT) ++ /* Always prefer non-V4ROOT exports */ ++ if (exp->m_export.e_flags & NFSEXP_V4ROOT) ++ continue; ++ if (found->m_export.e_flags & NFSEXP_V4ROOT) { ++ found = exp; ++ found_type = i; + continue; ++ } + + /* If one is a CROSSMOUNT, then prefer the longest path */ + if (((found->m_export.e_flags & NFSEXP_CROSSMOUNT) || +@@ -802,6 +826,229 @@ lookup_export(char *dom, char *path, struct addrinfo *ai) + return found; + } + ++#ifdef HAVE_NFS_PLUGIN_H ++#include ++#include ++ ++/* ++ * Walk through a set of FS locations and build a set of export options. ++ * Returns true if all went to plan; otherwise, false. ++ */ ++static _Bool ++locations_to_options(struct jp_ops *ops, nfs_fsloc_set_t locations, ++ char *options, size_t remaining, int *ttl) ++{ ++ char *server, *last_path, *rootpath, *ptr; ++ _Bool seen = false; ++ ++ last_path = NULL; ++ rootpath = NULL; ++ server = NULL; ++ ptr = options; ++ *ttl = 0; ++ ++ for (;;) { ++ enum jp_status status; ++ int len; ++ ++ status = ops->jp_get_next_location(locations, &server, ++ &rootpath, ttl); ++ if (status == JP_EMPTY) ++ break; ++ if (status != JP_OK) { ++ xlog(D_GENERAL, "%s: failed to parse location: %s", ++ __func__, ops->jp_error(status)); ++ goto out_false; ++ } ++ xlog(D_GENERAL, "%s: Location: %s:%s", ++ __func__, server, rootpath); ++ ++ if (last_path && strcmp(rootpath, last_path) == 0) { ++ len = snprintf(ptr, remaining, "+%s", server); ++ if (len < 0) { ++ xlog(D_GENERAL, "%s: snprintf: %m", __func__); ++ goto out_false; ++ } ++ if ((size_t)len >= remaining) { ++ xlog(D_GENERAL, "%s: options buffer overflow", __func__); ++ goto out_false; ++ } ++ remaining -= (size_t)len; ++ ptr += len; ++ } else { ++ if (last_path == NULL) ++ len = snprintf(ptr, remaining, "refer=%s@%s", ++ rootpath, server); ++ else ++ len = snprintf(ptr, remaining, ":%s@%s", ++ rootpath, server); ++ if (len < 0) { ++ xlog(D_GENERAL, "%s: snprintf: %m", __func__); ++ goto out_false; ++ } ++ if ((size_t)len >= remaining) { ++ xlog(D_GENERAL, "%s: options buffer overflow", ++ __func__); ++ goto out_false; ++ } ++ remaining -= (size_t)len; ++ ptr += len; ++ last_path = rootpath; ++ } ++ ++ seen = true; ++ free(rootpath); ++ free(server); ++ } ++ ++ xlog(D_CALL, "%s: options='%s', ttl=%d", ++ __func__, options, *ttl); ++ return seen; ++ ++out_false: ++ free(rootpath); ++ free(server); ++ return false; ++} ++ ++/* ++ * Walk through the set of FS locations and build an exportent. ++ * Returns pointer to an exportent if "junction" refers to a junction. ++ * ++ * Returned exportent points to static memory. ++ */ ++static struct exportent *do_locations_to_export(struct jp_ops *ops, ++ nfs_fsloc_set_t locations, const char *junction, ++ char *options, size_t options_len) ++{ ++ struct exportent *exp; ++ int ttl; ++ ++ if (!locations_to_options(ops, locations, options, options_len, &ttl)) ++ return NULL; ++ ++ exp = mkexportent("*", (char *)junction, options); ++ if (exp == NULL) { ++ xlog(L_ERROR, "%s: Failed to construct exportent", __func__); ++ return NULL; ++ } ++ ++ exp->e_uuid = NULL; ++ exp->e_ttl = ttl; ++ return exp; ++} ++ ++/* ++ * Convert set of FS locations to an exportent. Returns pointer to ++ * an exportent if "junction" refers to a junction. ++ * ++ * Returned exportent points to static memory. ++ */ ++static struct exportent *locations_to_export(struct jp_ops *ops, ++ nfs_fsloc_set_t locations, const char *junction) ++{ ++ struct exportent *exp; ++ char *options; ++ ++ options = malloc(BUFSIZ); ++ if (options == NULL) { ++ xlog(D_GENERAL, "%s: failed to allocate options buffer", ++ __func__); ++ return NULL; ++ } ++ options[0] = '\0'; ++ ++ exp = do_locations_to_export(ops, locations, junction, ++ options, BUFSIZ); ++ ++ free(options); ++ return exp; ++} ++ ++/* ++ * Retrieve locations information in "junction" and dump it to the ++ * kernel. Returns pointer to an exportent if "junction" refers ++ * to a junction. ++ * ++ * Returned exportent points to static memory. ++ */ ++static struct exportent *invoke_junction_ops(void *handle, ++ const char *junction) ++{ ++ nfs_fsloc_set_t locations; ++ struct exportent *exp; ++ enum jp_status status; ++ struct jp_ops *ops; ++ char *error; ++ ++ ops = (struct jp_ops *)dlsym(handle, "nfs_junction_ops"); ++ error = dlerror(); ++ if (error != NULL) { ++ xlog(D_GENERAL, "%s: dlsym(jp_junction_ops): %s", ++ __func__, error); ++ return NULL; ++ } ++ if (ops->jp_api_version != JP_API_VERSION) { ++ xlog(D_GENERAL, "%s: unrecognized junction API version: %u", ++ __func__, ops->jp_api_version); ++ return NULL; ++ } ++ ++ status = ops->jp_init(false); ++ if (status != JP_OK) { ++ xlog(D_GENERAL, "%s: failed to resolve %s: %s", ++ __func__, junction, ops->jp_error(status)); ++ return NULL; ++ } ++ ++ status = ops->jp_get_locations(junction, &locations); ++ if (status != JP_OK) { ++ xlog(D_GENERAL, "%s: failed to resolve %s: %s", ++ __func__, junction, ops->jp_error(status)); ++ return NULL; ++ } ++ ++ exp = locations_to_export(ops, locations, junction); ++ ++ ops->jp_put_locations(locations); ++ ops->jp_done(); ++ return exp; ++} ++ ++/* ++ * Load the junction plug-in, then try to resolve "pathname". ++ * Returns pointer to an initialized exportent if "junction" ++ * refers to a junction, or NULL if not. ++ * ++ * Returned exportent points to static memory. ++ */ ++static struct exportent *lookup_junction(const char *pathname) ++{ ++ struct exportent *exp; ++ void *handle; ++ ++ handle = dlopen("libnfsjunct.so", RTLD_NOW); ++ if (handle == NULL) { ++ xlog(D_GENERAL, "%s: dlopen: %s", __func__, dlerror()); ++ return NULL; ++ } ++ (void)dlerror(); /* Clear any error */ ++ ++ exp = invoke_junction_ops(handle, pathname); ++ ++ /* We could leave it loaded to make junction resolution ++ * faster next time. However, if we want to replace the ++ * library, that would require restarting mountd. */ ++ (void)dlclose(handle); ++ return exp; ++} ++#else /* !HAVE_NFS_PLUGIN_H */ ++static inline struct exportent *lookup_junction(const char *UNUSED(pathname)) ++{ ++ return NULL; ++} ++#endif /* !HAVE_NFS_PLUGIN_H */ ++ + static void nfsd_export(FILE *f) + { + /* requests are: +@@ -834,13 +1081,9 @@ static void nfsd_export(FILE *f) + + auth_reload(); + +- if (use_ipaddr) { +- struct addrinfo *tmp; +- tmp = host_pton(dom); +- if (tmp == NULL) +- goto out; +- ai = client_resolve(tmp->ai_addr); +- freeaddrinfo(tmp); ++ if (is_ipaddr_client(dom)) { ++ ai = lookup_client_addr(dom); ++ if (!ai) + goto out; + } + +@@ -854,7 +1097,7 @@ static void nfsd_export(FILE *f) + dump_to_cache(f, dom, path, NULL); + } + } else { +- dump_to_cache(f, dom, path, NULL); ++ dump_to_cache(f, dom, path, lookup_junction(path)); + } + out: + xlog(D_CALL, "nfsd_export: found %p path %s", found, path ? path : NULL); +diff --git a/utils/mountd/fsloc.c b/utils/mountd/fsloc.c +index e2add2d..bc737d1 100644 +--- a/utils/mountd/fsloc.c ++++ b/utils/mountd/fsloc.c +@@ -40,12 +40,12 @@ static void replicas_print(struct servers *sp) + { + int i; + if (!sp) { +- xlog(L_NOTICE, "NULL replicas pointer\n"); ++ xlog(L_NOTICE, "NULL replicas pointer"); + return; + } +- xlog(L_NOTICE, "replicas listsize=%i\n", sp->h_num); ++ xlog(L_NOTICE, "replicas listsize=%i", sp->h_num); + for (i=0; ih_num; i++) { +- xlog(L_NOTICE, " %s:%s\n", ++ xlog(L_NOTICE, " %s:%s", + sp->h_mp[i]->h_host, sp->h_mp[i]->h_path); + } + } +@@ -120,23 +120,37 @@ static struct servers *parse_list(char **list) + */ + static struct servers *method_list(char *data) + { +- char *copy, *ptr=data; ++ char *copy, *ptr=data, *p; + char **list; + int i, listsize; + struct servers *rv=NULL; ++ bool v6esc = false; + +- xlog(L_NOTICE, "method_list(%s)\n", data); ++ xlog(L_NOTICE, "method_list(%s)", data); + for (ptr--, listsize=1; ptr; ptr=index(ptr, ':'), listsize++) + ptr++; + list = malloc(listsize * sizeof(char *)); + copy = strdup(data); + if (copy) +- xlog(L_NOTICE, "converted to %s\n", copy); ++ xlog(L_NOTICE, "converted to %s", copy); + if (list && copy) { + ptr = copy; +- for (i=0; im_export; + + dupexportent(&eep, &pseudo_root.m_export); +- eep.e_hostname = strdup(curexp->e_hostname); ++ eep.e_hostname = curexp->e_hostname; + strncpy(eep.e_path, path, sizeof(eep.e_path)); + if (strcmp(path, "/") != 0) + eep.e_flags &= ~NFSEXP_FSID; +@@ -149,13 +150,13 @@ static int v4root_add_parents(nfs_export *exp) + "pseudo export for '%s'", exp->m_export.e_path); + return -ENOMEM; + } +- for (ptr = path + 1; ptr; ptr = strchr(ptr, '/')) { ++ for (ptr = path; ptr; ptr = strchr(ptr, '/')) { + int ret; + char saved; + + saved = *ptr; + *ptr = '\0'; +- ret = pseudofs_update(hostname, path, exp); ++ ret = pseudofs_update(hostname, *path ? path : "/", exp); + if (ret) + return ret; + *ptr = saved; +@@ -192,6 +193,13 @@ v4root_set() + */ + continue; + ++ if (strcmp(exp->m_export.e_path, "/") == 0 && ++ !(exp->m_export.e_flags & NFSEXP_FSID)) { ++ /* Force '/' to be exported as fsid == 0*/ ++ exp->m_export.e_flags |= NFSEXP_FSID; ++ exp->m_export.e_fsid = 0; ++ } ++ + v4root_add_parents(exp); + /* XXX: error handling! */ + } +diff --git a/utils/nfsd/Makefile.am b/utils/nfsd/Makefile.am +index c4c6fb0..1536065 100644 +--- a/utils/nfsd/Makefile.am ++++ b/utils/nfsd/Makefile.am +@@ -8,7 +8,7 @@ KPREFIX = @kprefix@ + sbin_PROGRAMS = nfsd + + nfsd_SOURCES = nfsd.c nfssvc.c +-nfsd_LDADD = ../../support/nfs/libnfs.a ++nfsd_LDADD = ../../support/nfs/libnfs.a $(LIBTIRPC) + + MAINTAINERCLEANFILES = Makefile.in + +diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c +index 8bc5d3a..2a3f5cc 100644 +--- a/utils/nfsd/nfsd.c ++++ b/utils/nfsd/nfsd.c +@@ -27,6 +27,10 @@ + #include "nfssvc.h" + #include "xlog.h" + ++#ifndef NFSD_NPROC ++#define NFSD_NPROC 8 ++#endif ++ + static void usage(const char *); + + static struct option longopts[] = +@@ -90,7 +94,7 @@ nfsd_enable_protos(unsigned int *proto4, unsigned int *proto6) + int + main(int argc, char **argv) + { +- int count = 1, c, error = 0, portnum = 0, fd, found_one; ++ int count = NFSD_NPROC, c, error = 0, portnum = 0, fd, found_one; + char *p, *progname, *port; + char *haddr = NULL; + int socket_up = 0; +diff --git a/utils/nfsd/nfsd.man b/utils/nfsd/nfsd.man +index d8988d2..1cf9296 100644 +--- a/utils/nfsd/nfsd.man ++++ b/utils/nfsd/nfsd.man +@@ -38,7 +38,7 @@ request on all known network addresses. This may change in future + releases of the Linux Kernel. + .TP + .B \-p " or " \-\-port port +-specify a diferent port to listen on for NFS requests. By default, ++specify a different port to listen on for NFS requests. By default, + .B rpc.nfsd + will listen on port 2049. + .TP +diff --git a/utils/nfsdcld/Makefile.am b/utils/nfsdcld/Makefile.am +new file mode 100644 +index 0000000..f320dff +--- /dev/null ++++ b/utils/nfsdcld/Makefile.am +@@ -0,0 +1,14 @@ ++## Process this file with automake to produce Makefile.in ++ ++man8_MANS = nfsdcld.man ++EXTRA_DIST = $(man8_MANS) ++ ++AM_CFLAGS += -D_LARGEFILE64_SOURCE ++sbin_PROGRAMS = nfsdcld ++ ++nfsdcld_SOURCES = nfsdcld.c sqlite.c ++ ++nfsdcld_LDADD = ../../support/nfs/libnfs.a $(LIBEVENT) $(LIBSQLITE) ++ ++MAINTAINERCLEANFILES = Makefile.in ++ +diff --git a/utils/nfsdcld/nfsdcld.c b/utils/nfsdcld/nfsdcld.c +new file mode 100644 +index 0000000..2f0b004 +--- /dev/null ++++ b/utils/nfsdcld/nfsdcld.c +@@ -0,0 +1,527 @@ ++/* ++ * nfsdcld.c -- NFSv4 client name tracking daemon ++ * ++ * Copyright (C) 2011 Red Hat, Jeff Layton ++ * ++ * This program 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. ++ * ++ * This program 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 this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif /* HAVE_CONFIG_H */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "xlog.h" ++#include "nfslib.h" ++#include "cld.h" ++#include "sqlite.h" ++ ++#ifndef PIPEFS_DIR ++#define PIPEFS_DIR NFS_STATEDIR "/rpc_pipefs" ++#endif ++ ++#define DEFAULT_CLD_PATH PIPEFS_DIR "/nfsd/cld" ++ ++#define UPCALL_VERSION 1 ++ ++/* private data structures */ ++struct cld_client { ++ int cl_fd; ++ struct event cl_event; ++ struct cld_msg cl_msg; ++}; ++ ++/* global variables */ ++static char *pipepath = DEFAULT_CLD_PATH; ++static int inotify_fd = -1; ++static struct event pipedir_event; ++ ++static struct option longopts[] = ++{ ++ { "help", 0, NULL, 'h' }, ++ { "foreground", 0, NULL, 'F' }, ++ { "debug", 0, NULL, 'd' }, ++ { "pipe", 1, NULL, 'p' }, ++ { "storagedir", 1, NULL, 's' }, ++ { NULL, 0, 0, 0 }, ++}; ++ ++/* forward declarations */ ++static void cldcb(int UNUSED(fd), short which, void *data); ++ ++static void ++usage(char *progname) ++{ ++ printf("%s [ -hFd ] [ -p pipe ] [ -s dir ]\n", progname); ++} ++ ++#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX) ++ ++static int ++cld_pipe_open(struct cld_client *clnt) ++{ ++ int fd; ++ ++ xlog(D_GENERAL, "%s: opening upcall pipe %s", __func__, pipepath); ++ fd = open(pipepath, O_RDWR, 0); ++ if (fd < 0) { ++ xlog(L_ERROR, "%s: open of %s failed: %m", __func__, pipepath); ++ return -errno; ++ } ++ ++ if (clnt->cl_event.ev_flags & EVLIST_INIT) ++ event_del(&clnt->cl_event); ++ if (clnt->cl_fd >= 0) ++ close(clnt->cl_fd); ++ ++ clnt->cl_fd = fd; ++ event_set(&clnt->cl_event, clnt->cl_fd, EV_READ, cldcb, clnt); ++ /* event_add is done by the caller */ ++ return 0; ++} ++ ++static void ++cld_inotify_cb(int UNUSED(fd), short which, void *data) ++{ ++ int ret; ++ size_t elen; ++ ssize_t rret; ++ char evbuf[INOTIFY_EVENT_MAX]; ++ char *dirc = NULL, *pname; ++ struct inotify_event *event = (struct inotify_event *)evbuf; ++ struct cld_client *clnt = data; ++ ++ if (which != EV_READ) ++ return; ++ ++ xlog(D_GENERAL, "%s: called for EV_READ", __func__); ++ ++ dirc = strndup(pipepath, PATH_MAX); ++ if (!dirc) { ++ xlog(L_ERROR, "%s: unable to allocate memory", __func__); ++ goto out; ++ } ++ ++ rret = read(inotify_fd, evbuf, INOTIFY_EVENT_MAX); ++ if (rret < 0) { ++ xlog(L_ERROR, "%s: read from inotify fd failed: %m", __func__); ++ goto out; ++ } ++ ++ /* check to see if we have a filename in the evbuf */ ++ if (!event->len) { ++ xlog(D_GENERAL, "%s: no filename in inotify event", __func__); ++ goto out; ++ } ++ ++ pname = basename(dirc); ++ elen = strnlen(event->name, event->len); ++ ++ /* does the filename match our pipe? */ ++ if (strlen(pname) != elen || memcmp(pname, event->name, elen)) { ++ xlog(D_GENERAL, "%s: wrong filename (%s)", __func__, ++ event->name); ++ goto out; ++ } ++ ++ ret = cld_pipe_open(clnt); ++ switch (ret) { ++ case 0: ++ /* readd the event for the cl_event pipe */ ++ event_add(&clnt->cl_event, NULL); ++ break; ++ case -ENOENT: ++ /* pipe must have disappeared, wait for it to come back */ ++ goto out; ++ default: ++ /* anything else is fatal */ ++ xlog(L_FATAL, "%s: unable to open new pipe (%d). Aborting.", ++ ret, __func__); ++ exit(ret); ++ } ++ ++out: ++ event_add(&pipedir_event, NULL); ++ free(dirc); ++} ++ ++static int ++cld_inotify_setup(void) ++{ ++ int ret; ++ char *dirc, *dname; ++ ++ dirc = strndup(pipepath, PATH_MAX); ++ if (!dirc) { ++ xlog_err("%s: unable to allocate memory", __func__); ++ ret = -ENOMEM; ++ goto out_free; ++ } ++ ++ dname = dirname(dirc); ++ ++ inotify_fd = inotify_init(); ++ if (inotify_fd < 0) { ++ xlog_err("%s: inotify_init failed: %m", __func__); ++ ret = -errno; ++ goto out_free; ++ } ++ ++ ret = inotify_add_watch(inotify_fd, dname, IN_CREATE); ++ if (ret < 0) { ++ xlog_err("%s: inotify_add_watch failed: %m", __func__); ++ ret = -errno; ++ goto out_err; ++ } ++ ++out_free: ++ free(dirc); ++ return 0; ++out_err: ++ close(inotify_fd); ++ goto out_free; ++} ++ ++/* ++ * Set an inotify watch on the directory that should contain the pipe, and then ++ * try to open it. If it fails with anything but -ENOENT, return the error ++ * immediately. ++ * ++ * If it succeeds, then set up the pipe event handler. At that point, set up ++ * the inotify event handler and go ahead and return success. ++ */ ++static int ++cld_pipe_init(struct cld_client *clnt) ++{ ++ int ret; ++ ++ xlog(D_GENERAL, "%s: init pipe handlers", __func__); ++ ++ ret = cld_inotify_setup(); ++ if (ret != 0) ++ goto out; ++ ++ clnt->cl_fd = -1; ++ ret = cld_pipe_open(clnt); ++ switch (ret) { ++ case 0: ++ /* add the event and we're good to go */ ++ event_add(&clnt->cl_event, NULL); ++ break; ++ case -ENOENT: ++ /* ignore this error -- cld_inotify_cb will handle it */ ++ ret = 0; ++ break; ++ default: ++ /* anything else is fatal */ ++ close(inotify_fd); ++ goto out; ++ } ++ ++ /* set event for inotify read */ ++ event_set(&pipedir_event, inotify_fd, EV_READ, cld_inotify_cb, clnt); ++ event_add(&pipedir_event, NULL); ++out: ++ return ret; ++} ++ ++static void ++cld_not_implemented(struct cld_client *clnt) ++{ ++ int ret; ++ ssize_t bsize, wsize; ++ struct cld_msg *cmsg = &clnt->cl_msg; ++ ++ xlog(D_GENERAL, "%s: downcalling with not implemented error", __func__); ++ ++ /* set up reply */ ++ cmsg->cm_status = -EOPNOTSUPP; ++ ++ bsize = sizeof(*cmsg); ++ ++ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize); ++ if (wsize != bsize) ++ xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m", ++ __func__, wsize); ++ ++ /* reopen pipe, just to be sure */ ++ ret = cld_pipe_open(clnt); ++ if (ret) { ++ xlog(L_FATAL, "%s: unable to reopen pipe: %d", __func__, ret); ++ exit(ret); ++ } ++} ++ ++static void ++cld_create(struct cld_client *clnt) ++{ ++ int ret; ++ ssize_t bsize, wsize; ++ struct cld_msg *cmsg = &clnt->cl_msg; ++ ++ xlog(D_GENERAL, "%s: create client record.", __func__); ++ ++ ret = sqlite_insert_client(cmsg->cm_u.cm_name.cn_id, ++ cmsg->cm_u.cm_name.cn_len); ++ ++ cmsg->cm_status = ret ? -EREMOTEIO : ret; ++ ++ bsize = sizeof(*cmsg); ++ ++ xlog(D_GENERAL, "Doing downcall with status %d", cmsg->cm_status); ++ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize); ++ if (wsize != bsize) { ++ xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m", ++ __func__, wsize); ++ ret = cld_pipe_open(clnt); ++ if (ret) { ++ xlog(L_FATAL, "%s: unable to reopen pipe: %d", ++ __func__, ret); ++ exit(ret); ++ } ++ } ++} ++ ++static void ++cld_remove(struct cld_client *clnt) ++{ ++ int ret; ++ ssize_t bsize, wsize; ++ struct cld_msg *cmsg = &clnt->cl_msg; ++ ++ xlog(D_GENERAL, "%s: remove client record.", __func__); ++ ++ ret = sqlite_remove_client(cmsg->cm_u.cm_name.cn_id, ++ cmsg->cm_u.cm_name.cn_len); ++ ++ cmsg->cm_status = ret ? -EREMOTEIO : ret; ++ ++ bsize = sizeof(*cmsg); ++ ++ xlog(D_GENERAL, "%s: downcall with status %d", __func__, ++ cmsg->cm_status); ++ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize); ++ if (wsize != bsize) { ++ xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m", ++ __func__, wsize); ++ ret = cld_pipe_open(clnt); ++ if (ret) { ++ xlog(L_FATAL, "%s: unable to reopen pipe: %d", ++ __func__, ret); ++ exit(ret); ++ } ++ } ++} ++ ++static void ++cld_check(struct cld_client *clnt) ++{ ++ int ret; ++ ssize_t bsize, wsize; ++ struct cld_msg *cmsg = &clnt->cl_msg; ++ ++ xlog(D_GENERAL, "%s: check client record", __func__); ++ ++ ret = sqlite_check_client(cmsg->cm_u.cm_name.cn_id, ++ cmsg->cm_u.cm_name.cn_len); ++ ++ /* set up reply */ ++ cmsg->cm_status = ret ? -EACCES : ret; ++ ++ bsize = sizeof(*cmsg); ++ ++ xlog(D_GENERAL, "%s: downcall with status %d", __func__, ++ cmsg->cm_status); ++ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize); ++ if (wsize != bsize) { ++ xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m", ++ __func__, wsize); ++ ret = cld_pipe_open(clnt); ++ if (ret) { ++ xlog(L_FATAL, "%s: unable to reopen pipe: %d", ++ __func__, ret); ++ exit(ret); ++ } ++ } ++} ++ ++static void ++cld_gracedone(struct cld_client *clnt) ++{ ++ int ret; ++ ssize_t bsize, wsize; ++ struct cld_msg *cmsg = &clnt->cl_msg; ++ ++ xlog(D_GENERAL, "%s: grace done. cm_gracetime=%ld", __func__, ++ cmsg->cm_u.cm_gracetime); ++ ++ ret = sqlite_remove_unreclaimed(cmsg->cm_u.cm_gracetime); ++ ++ /* set up reply: downcall with 0 status */ ++ cmsg->cm_status = ret ? -EREMOTEIO : ret; ++ ++ bsize = sizeof(*cmsg); ++ ++ xlog(D_GENERAL, "Doing downcall with status %d", cmsg->cm_status); ++ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize); ++ if (wsize != bsize) { ++ xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m", ++ __func__, wsize); ++ ret = cld_pipe_open(clnt); ++ if (ret) { ++ xlog(L_FATAL, "%s: unable to reopen pipe: %d", ++ __func__, ret); ++ exit(ret); ++ } ++ } ++} ++ ++static void ++cldcb(int UNUSED(fd), short which, void *data) ++{ ++ ssize_t len; ++ struct cld_client *clnt = data; ++ struct cld_msg *cmsg = &clnt->cl_msg; ++ ++ if (which != EV_READ) ++ goto out; ++ ++ len = atomicio(read, clnt->cl_fd, cmsg, sizeof(*cmsg)); ++ if (len <= 0) { ++ xlog(L_ERROR, "%s: pipe read failed: %m", __func__); ++ cld_pipe_open(clnt); ++ goto out; ++ } ++ ++ if (cmsg->cm_vers != UPCALL_VERSION) { ++ xlog(L_ERROR, "%s: unsupported upcall version: %hu", ++ cmsg->cm_vers); ++ cld_pipe_open(clnt); ++ goto out; ++ } ++ ++ switch(cmsg->cm_cmd) { ++ case Cld_Create: ++ cld_create(clnt); ++ break; ++ case Cld_Remove: ++ cld_remove(clnt); ++ break; ++ case Cld_Check: ++ cld_check(clnt); ++ break; ++ case Cld_GraceDone: ++ cld_gracedone(clnt); ++ break; ++ default: ++ xlog(L_WARNING, "%s: command %u is not yet implemented", ++ __func__, cmsg->cm_cmd); ++ cld_not_implemented(clnt); ++ } ++out: ++ event_add(&clnt->cl_event, NULL); ++} ++ ++int ++main(int argc, char **argv) ++{ ++ char arg; ++ int rc = 0; ++ bool foreground = false; ++ char *progname; ++ char *storagedir = NULL; ++ struct cld_client clnt; ++ ++ memset(&clnt, 0, sizeof(clnt)); ++ ++ progname = strdup(basename(argv[0])); ++ if (!progname) { ++ fprintf(stderr, "%s: unable to allocate memory.\n", argv[0]); ++ return 1; ++ } ++ ++ event_init(); ++ xlog_syslog(0); ++ xlog_stderr(1); ++ ++ /* process command-line options */ ++ while ((arg = getopt_long(argc, argv, "hdFp:s:", longopts, ++ NULL)) != EOF) { ++ switch (arg) { ++ case 'd': ++ xlog_config(D_ALL, 1); ++ break; ++ case 'F': ++ foreground = true; ++ break; ++ case 'p': ++ pipepath = optarg; ++ break; ++ case 's': ++ storagedir = optarg; ++ break; ++ default: ++ usage(progname); ++ return 0; ++ } ++ } ++ ++ ++ xlog_open(progname); ++ if (!foreground) { ++ xlog_syslog(1); ++ xlog_stderr(0); ++ rc = daemon(0, 0); ++ if (rc) { ++ xlog(L_ERROR, "Unable to daemonize: %m"); ++ goto out; ++ } ++ } ++ ++ /* set up storage db */ ++ rc = sqlite_maindb_init(storagedir); ++ if (rc) { ++ xlog(L_ERROR, "Failed to open main database: %d", rc); ++ goto out; ++ } ++ ++ /* set up event handler */ ++ rc = cld_pipe_init(&clnt); ++ if (rc) ++ goto out; ++ ++ xlog(D_GENERAL, "%s: Starting event dispatch handler.", __func__); ++ rc = event_dispatch(); ++ if (rc < 0) ++ xlog(L_ERROR, "%s: event_dispatch failed: %m", __func__); ++ ++ close(clnt.cl_fd); ++ close(inotify_fd); ++out: ++ free(progname); ++ return rc; ++} +diff --git a/utils/nfsdcld/nfsdcld.man b/utils/nfsdcld/nfsdcld.man +new file mode 100644 +index 0000000..bad5f34 +--- /dev/null ++++ b/utils/nfsdcld/nfsdcld.man +@@ -0,0 +1,180 @@ ++.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.13) ++.\" ++.\" Standard preamble: ++.\" ======================================================================== ++.de Sp \" Vertical space (when we can't use .PP) ++.if t .sp .5v ++.if n .sp ++.. ++.de Vb \" Begin verbatim text ++.ft CW ++.nf ++.ne \\$1 ++.. ++.de Ve \" End verbatim text ++.ft R ++.fi ++.. ++.\" Set up some character translations and predefined strings. \*(-- will ++.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left ++.\" double quote, and \*(R" will give a right double quote. \*(C+ will ++.\" give a nicer C++. Capital omega is used to do unbreakable dashes and ++.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, ++.\" nothing in troff, for use with C<>. ++.tr \(*W- ++.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' ++.ie n \{\ ++. ds -- \(*W- ++. ds PI pi ++. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch ++. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch ++. ds L" "" ++. ds R" "" ++. ds C` "" ++. ds C' "" ++'br\} ++.el\{\ ++. ds -- \|\(em\| ++. ds PI \(*p ++. ds L" `` ++. ds R" '' ++'br\} ++.\" ++.\" Escape single quotes in literal strings from groff's Unicode transform. ++.ie \n(.g .ds Aq \(aq ++.el .ds Aq ' ++.\" ++.\" If the F register is turned on, we'll generate index entries on stderr for ++.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index ++.\" entries marked with X<> in POD. Of course, you'll have to process the ++.\" output yourself in some meaningful fashion. ++.ie \nF \{\ ++. de IX ++. tm Index:\\$1\t\\n%\t"\\$2" ++.. ++. nr % 0 ++. rr F ++.\} ++.el \{\ ++. de IX ++.. ++.\} ++.\" ++.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). ++.\" Fear. Run. Save yourself. No user-serviceable parts. ++. \" fudge factors for nroff and troff ++.if n \{\ ++. ds #H 0 ++. ds #V .8m ++. ds #F .3m ++. ds #[ \f1 ++. ds #] \fP ++.\} ++.if t \{\ ++. ds #H ((1u-(\\\\n(.fu%2u))*.13m) ++. ds #V .6m ++. ds #F 0 ++. ds #[ \& ++. ds #] \& ++.\} ++. \" simple accents for nroff and troff ++.if n \{\ ++. ds ' \& ++. ds ` \& ++. ds ^ \& ++. ds , \& ++. ds ~ ~ ++. ds / ++.\} ++.if t \{\ ++. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" ++. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' ++. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' ++. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' ++. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' ++. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' ++.\} ++. \" troff and (daisy-wheel) nroff accents ++.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' ++.ds 8 \h'\*(#H'\(*b\h'-\*(#H' ++.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] ++.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' ++.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' ++.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] ++.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] ++.ds ae a\h'-(\w'a'u*4/10)'e ++.ds Ae A\h'-(\w'A'u*4/10)'E ++. \" corrections for vroff ++.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' ++.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' ++. \" for low resolution devices (crt and lpr) ++.if \n(.H>23 .if \n(.V>19 \ ++\{\ ++. ds : e ++. ds 8 ss ++. ds o a ++. ds d- d\h'-1'\(ga ++. ds D- D\h'-1'\(hy ++. ds th \o'bp' ++. ds Th \o'LP' ++. ds ae ae ++. ds Ae AE ++.\} ++.rm #[ #] #H #V #F C ++.\" ======================================================================== ++.\" ++.IX Title "NFSDCLD 8" ++.TH NFSDCLD 8 "2011-12-21" "" "" ++.\" For nroff, turn off justification. Always turn off hyphenation; it makes ++.\" way too many mistakes in technical documents. ++.if n .ad l ++.nh ++.SH "NAME" ++nfsdcld \- NFSv4 Client Tracking Daemon ++.SH "SYNOPSIS" ++.IX Header "SYNOPSIS" ++nfsdcld [\-d] [\-F] [\-p path] [\-s stable storage dir] ++.SH "DESCRIPTION" ++.IX Header "DESCRIPTION" ++nfsdcld is the NFSv4 client tracking daemon. It is not necessary to run ++this daemon on machines that are not acting as NFSv4 servers. ++.PP ++When a network partition is combined with a server reboot, there are ++edge conditions that can cause the server to grant lock reclaims when ++other clients have taken conflicting locks in the interim. A more detailed ++explanation of this issue is described in \s-1RFC\s0 3530, section 8.6.3. ++.PP ++In order to prevent these problems, the server must track a small amount ++of per-client information on stable storage. This daemon provides the ++userspace piece of that functionality. ++.SH "OPTIONS" ++.IX Header "OPTIONS" ++.IP "\fB\-d\fR, \fB\-\-debug\fR" 4 ++.IX Item "-d, --debug" ++Enable debug level logging. ++.IP "\fB\-F\fR, \fB\-\-foreground\fR" 4 ++.IX Item "-F, --foreground" ++Runs the daemon in the foreground and prints all output to stderr ++.IP "\fB\-p\fR \fIpipe\fR, \fB\-\-pipe\fR=\fIpipe\fR" 4 ++.IX Item "-p pipe, --pipe=pipe" ++Location of the \*(L"cld\*(R" upcall pipe. The default value is ++\&\fI/var/lib/nfs/rpc_pipefs/nfsd/cld\fR. If the pipe does not exist when the ++daemon starts then it will wait for it to be created. ++.IP "\fB\-s\fR \fIstoragedir\fR, \fB\-\-storagedir\fR=\fIstorage_dir\fR" 4 ++.IX Item "-s storagedir, --storagedir=storage_dir" ++Directory where stable storage information should be kept. The default ++value is \fI/var/lib/nfs/nfsdcld\fR. ++.SH "NOTES" ++.IX Header "NOTES" ++The Linux kernel NFSv4 server has historically tracked this information ++on stable storage by manipulating information on the filesystem ++directly, in the directory to which \fI/proc/fs/nfsd/nfsv4recoverydir\fR ++points. ++.PP ++This daemon requires a kernel that supports the nfsdcld upcall. If the ++kernel does not support the new upcall, or is using the legacy client ++name tracking code then it will not create the pipe that nfsdcld uses to ++talk to the kernel. ++.SH "AUTHORS" ++.IX Header "AUTHORS" ++The nfsdcld daemon was developed by Jeff Layton . +diff --git a/utils/nfsdcld/sqlite.c b/utils/nfsdcld/sqlite.c +new file mode 100644 +index 0000000..9e35774 +--- /dev/null ++++ b/utils/nfsdcld/sqlite.c +@@ -0,0 +1,390 @@ ++/* ++ * Copyright (C) 2011 Red Hat, Jeff Layton ++ * ++ * This program 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. ++ * ++ * This program 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 this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++/* ++ * Explanation: ++ * ++ * This file contains the code to manage the sqlite backend database for the ++ * clstated upcall daemon. ++ * ++ * The main database is called main.sqlite and contains the following tables: ++ * ++ * parameters: simple key/value pairs for storing database info ++ * ++ * clients: one column containing a BLOB with the as sent by the client ++ * and a timestamp (in epoch seconds) of when the record was ++ * established ++ * ++ * FIXME: should we also record the fsid being accessed? ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif /* HAVE_CONFIG_H */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "xlog.h" ++ ++#define CLD_SQLITE_SCHEMA_VERSION 1 ++ ++#ifndef CLD_SQLITE_TOPDIR ++#define CLD_SQLITE_TOPDIR NFS_STATEDIR "/nfsdcld" ++#endif ++ ++/* in milliseconds */ ++#define CLD_SQLITE_BUSY_TIMEOUT 10000 ++ ++/* private data structures */ ++ ++/* global variables */ ++ ++/* top level DB directory */ ++static char *sqlite_topdir; ++ ++/* reusable pathname and sql command buffer */ ++static char buf[PATH_MAX]; ++ ++/* global database handle */ ++static sqlite3 *dbh; ++ ++/* forward declarations */ ++ ++/* make a directory, ignoring EEXIST errors unless it's not a directory */ ++static int ++mkdir_if_not_exist(char *dirname) ++{ ++ int ret; ++ struct stat statbuf; ++ ++ ret = mkdir(dirname, S_IRWXU); ++ if (ret && errno != EEXIST) ++ return -errno; ++ ++ ret = stat(dirname, &statbuf); ++ if (ret) ++ return -errno; ++ ++ if (!S_ISDIR(statbuf.st_mode)) ++ ret = -ENOTDIR; ++ ++ return ret; ++} ++ ++/* ++ * Open the "main" database, and attempt to initialize it by creating the ++ * parameters table and inserting the schema version into it. Ignore any errors ++ * from that, and then attempt to select the version out of it again. If the ++ * version appears wrong, then assume that the DB is corrupt or has been ++ * upgraded, and return an error. If all of that works, then attempt to create ++ * the "clients" table. ++ */ ++int ++sqlite_maindb_init(char *topdir) ++{ ++ int ret; ++ char *err = NULL; ++ sqlite3_stmt *stmt = NULL; ++ ++ sqlite_topdir = topdir ? topdir : CLD_SQLITE_TOPDIR; ++ ++ ret = mkdir_if_not_exist(sqlite_topdir); ++ if (ret) ++ return ret; ++ ++ ret = snprintf(buf, PATH_MAX - 1, "%s/main.sqlite", sqlite_topdir); ++ if (ret < 0) ++ return ret; ++ ++ buf[PATH_MAX - 1] = '\0'; ++ ++ ret = sqlite3_open(buf, &dbh); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to open main database: %d", ret); ++ return ret; ++ } ++ ++ ret = sqlite3_busy_timeout(dbh, CLD_SQLITE_BUSY_TIMEOUT); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to set sqlite busy timeout: %d", ret); ++ goto out_err; ++ } ++ ++ /* Try to create table */ ++ ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS parameters " ++ "(key TEXT PRIMARY KEY, value TEXT);", ++ NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to create parameter table: %d", ret); ++ goto out_err; ++ } ++ ++ /* insert version into table -- ignore error if it fails */ ++ ret = snprintf(buf, sizeof(buf), ++ "INSERT OR IGNORE INTO parameters values (\"version\", " ++ "\"%d\");", CLD_SQLITE_SCHEMA_VERSION); ++ if (ret < 0) { ++ goto out_err; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ ret = -EINVAL; ++ goto out_err; ++ } ++ ++ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to insert into parameter table: %d", ++ ret); ++ goto out_err; ++ } ++ ++ ret = sqlite3_prepare_v2(dbh, ++ "SELECT value FROM parameters WHERE key == \"version\";", ++ -1, &stmt, NULL); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to prepare select statement: %d", ret); ++ goto out_err; ++ } ++ ++ /* check schema version */ ++ ret = sqlite3_step(stmt); ++ if (ret != SQLITE_ROW) { ++ xlog(L_ERROR, "Select statement execution failed: %s", ++ sqlite3_errmsg(dbh)); ++ goto out_err; ++ } ++ ++ /* process SELECT result */ ++ ret = sqlite3_column_int(stmt, 0); ++ if (ret != CLD_SQLITE_SCHEMA_VERSION) { ++ xlog(L_ERROR, "Unsupported database schema version! " ++ "Expected %d, got %d.", ++ CLD_SQLITE_SCHEMA_VERSION, ret); ++ ret = -EINVAL; ++ goto out_err; ++ } ++ ++ /* now create the "clients" table */ ++ ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS clients " ++ "(id BLOB PRIMARY KEY, time INTEGER);", ++ NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to create clients table: %s", err); ++ goto out_err; ++ } ++ ++ sqlite3_free(err); ++ sqlite3_finalize(stmt); ++ return 0; ++ ++out_err: ++ if (err) { ++ xlog(L_ERROR, "sqlite error: %s", err); ++ sqlite3_free(err); ++ } ++ sqlite3_finalize(stmt); ++ sqlite3_close(dbh); ++ return ret; ++} ++ ++/* ++ * Create a client record ++ * ++ * Returns a non-zero sqlite error code, or SQLITE_OK (aka 0) ++ */ ++int ++sqlite_insert_client(const unsigned char *clname, const size_t namelen) ++{ ++ int ret; ++ sqlite3_stmt *stmt = NULL; ++ ++ ret = sqlite3_prepare_v2(dbh, "INSERT OR REPLACE INTO clients VALUES " ++ "(?, strftime('%s', 'now'));", -1, ++ &stmt, NULL); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: insert statement prepare failed: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ return ret; ++ } ++ ++ ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen, ++ SQLITE_STATIC); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: bind blob failed: %s", __func__, ++ sqlite3_errmsg(dbh)); ++ goto out_err; ++ } ++ ++ ret = sqlite3_step(stmt); ++ if (ret == SQLITE_DONE) ++ ret = SQLITE_OK; ++ else ++ xlog(L_ERROR, "%s: unexpected return code from insert: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ ++out_err: ++ xlog(D_GENERAL, "%s: returning %d", __func__, ret); ++ sqlite3_finalize(stmt); ++ return ret; ++} ++ ++/* Remove a client record */ ++int ++sqlite_remove_client(const unsigned char *clname, const size_t namelen) ++{ ++ int ret; ++ sqlite3_stmt *stmt = NULL; ++ ++ ret = sqlite3_prepare_v2(dbh, "DELETE FROM clients WHERE id==?", -1, ++ &stmt, NULL); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: statement prepare failed: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ goto out_err; ++ } ++ ++ ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen, ++ SQLITE_STATIC); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: bind blob failed: %s", __func__, ++ sqlite3_errmsg(dbh)); ++ goto out_err; ++ } ++ ++ ret = sqlite3_step(stmt); ++ if (ret == SQLITE_DONE) ++ ret = SQLITE_OK; ++ else ++ xlog(L_ERROR, "%s: unexpected return code from delete: %d", ++ __func__, ret); ++ ++out_err: ++ xlog(D_GENERAL, "%s: returning %d", __func__, ret); ++ sqlite3_finalize(stmt); ++ return ret; ++} ++ ++/* ++ * Is the given clname in the clients table? If so, then update its timestamp ++ * and return success. If the record isn't present, or the update fails, then ++ * return an error. ++ */ ++int ++sqlite_check_client(const unsigned char *clname, const size_t namelen) ++{ ++ int ret; ++ sqlite3_stmt *stmt = NULL; ++ ++ ret = sqlite3_prepare_v2(dbh, "SELECT count(*) FROM clients WHERE " ++ "id==?", -1, &stmt, NULL); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: unable to prepare update statement: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ goto out_err; ++ } ++ ++ ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen, ++ SQLITE_STATIC); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: bind blob failed: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ goto out_err; ++ } ++ ++ ret = sqlite3_step(stmt); ++ if (ret != SQLITE_ROW) { ++ xlog(L_ERROR, "%s: unexpected return code from select: %d", ++ __func__, ret); ++ goto out_err; ++ } ++ ++ ret = sqlite3_column_int(stmt, 0); ++ xlog(D_GENERAL, "%s: select returned %d rows", ret); ++ if (ret != 1) { ++ ret = -EACCES; ++ goto out_err; ++ } ++ ++ sqlite3_finalize(stmt); ++ stmt = NULL; ++ ret = sqlite3_prepare_v2(dbh, "UPDATE OR FAIL clients SET " ++ "time=strftime('%s', 'now') WHERE id==?", ++ -1, &stmt, NULL); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: unable to prepare update statement: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ goto out_err; ++ } ++ ++ ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen, ++ SQLITE_STATIC); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: bind blob failed: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ goto out_err; ++ } ++ ++ ret = sqlite3_step(stmt); ++ if (ret == SQLITE_DONE) ++ ret = SQLITE_OK; ++ else ++ xlog(L_ERROR, "%s: unexpected return code from update: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ ++out_err: ++ xlog(D_GENERAL, "%s: returning %d", __func__, ret); ++ sqlite3_finalize(stmt); ++ return ret; ++} ++ ++/* ++ * remove any client records that were not reclaimed since grace_start. ++ */ ++int ++sqlite_remove_unreclaimed(time_t grace_start) ++{ ++ int ret; ++ char *err = NULL; ++ ++ ret = snprintf(buf, sizeof(buf), "DELETE FROM clients WHERE time < %ld", ++ grace_start); ++ if (ret < 0) { ++ return ret; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ ret = -EINVAL; ++ return ret; ++ } ++ ++ ret = sqlite3_exec(dbh, buf, NULL, NULL, &err); ++ if (ret != SQLITE_OK) ++ xlog(L_ERROR, "%s: delete failed: %s", __func__, err); ++ ++ xlog(D_GENERAL, "%s: returning %d", __func__, ret); ++ sqlite3_free(err); ++ return ret; ++} +diff --git a/utils/nfsdcld/sqlite.h b/utils/nfsdcld/sqlite.h +new file mode 100644 +index 0000000..c85e7d6 +--- /dev/null ++++ b/utils/nfsdcld/sqlite.h +@@ -0,0 +1,29 @@ ++/* ++ * Copyright (C) 2011 Red Hat, Jeff Layton ++ * ++ * This program 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. ++ * ++ * This program 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 this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef _SQLITE_H_ ++#define _SQLITE_H_ ++ ++int sqlite_maindb_init(char *topdir); ++int sqlite_insert_client(const unsigned char *clname, const size_t namelen); ++int sqlite_remove_client(const unsigned char *clname, const size_t namelen); ++int sqlite_check_client(const unsigned char *clname, const size_t namelen); ++int sqlite_remove_unreclaimed(const time_t grace_start); ++ ++#endif /* _SQLITE_H */ +diff --git a/utils/nfsidmap/Makefile.am b/utils/nfsidmap/Makefile.am +index f837b91..c0675c4 100644 +--- a/utils/nfsidmap/Makefile.am ++++ b/utils/nfsidmap/Makefile.am +@@ -4,6 +4,6 @@ man8_MANS = nfsidmap.man + + sbin_PROGRAMS = nfsidmap + nfsidmap_SOURCES = nfsidmap.c +-nfsidmap_LDADD = -lnfsidmap -lkeyutils ++nfsidmap_LDADD = $(LIBNFSIDMAP) -lkeyutils ../../support/nfs/libnfs.a + + MAINTAINERCLEANFILES = Makefile.in +diff --git a/utils/nfsidmap/nfsidmap.c b/utils/nfsidmap/nfsidmap.c +index 2d87381..cf11551 100644 +--- a/utils/nfsidmap/nfsidmap.c ++++ b/utils/nfsidmap/nfsidmap.c +@@ -3,21 +3,33 @@ + #include + #include + #include ++#include + + #include + #include + #include + #include + +-#include ++#include ++#include "xlog.h" + +-/* gcc nfsidmap.c -o nfsidmap -l nfsidmap -l keyutils */ ++int verbose = 0; ++char *usage="Usage: %s [-v] [-c || [-u|-g|-r key] || [-t timeout] key desc]"; + + #define MAX_ID_LEN 11 + #define IDMAP_NAMESZ 128 + #define USER 1 + #define GROUP 0 + ++#define PROCKEYS "/proc/keys" ++#ifndef DEFAULT_KEYRING ++#define DEFAULT_KEYRING "id_resolver" ++#endif ++ ++static int keyring_clear(char *keyring); ++ ++#define UIDKEYS 0x1 ++#define GIDKEYS 0x2 + + /* + * Find either a user or group id based on the name@domain string +@@ -36,9 +48,31 @@ int id_lookup(char *name_at_domain, key_serial_t key, int type) + rc = nfs4_group_owner_to_gid(name_at_domain, &gid); + sprintf(id, "%u", gid); + } ++ if (rc < 0) ++ xlog_err("id_lookup: %s: failed: %m", ++ (type == USER ? "nfs4_owner_to_uid" : "nfs4_group_owner_to_gid")); + +- if (rc == 0) ++ if (rc == 0) { + rc = keyctl_instantiate(key, id, strlen(id) + 1, 0); ++ if (rc < 0) { ++ switch(rc) { ++ case -EDQUOT: ++ case -ENFILE: ++ case -ENOMEM: ++ /* ++ * The keyring is full. Clear the keyring and try again ++ */ ++ rc = keyring_clear(DEFAULT_KEYRING); ++ if (rc == 0) ++ rc = keyctl_instantiate(key, id, strlen(id) + 1, 0); ++ break; ++ default: ++ break; ++ } ++ } ++ if (rc < 0) ++ xlog_err("id_lookup: keyctl_instantiate failed: %m"); ++ } + + return rc; + } +@@ -57,6 +91,7 @@ int name_lookup(char *id, key_serial_t key, int type) + rc = nfs4_get_default_domain(NULL, domain, NFS4_MAX_DOMAIN_LEN); + if (rc != 0) { + rc = -1; ++ xlog_err("name_lookup: nfs4_get_default_domain failed: %m"); + goto out; + } + +@@ -67,39 +102,206 @@ int name_lookup(char *id, key_serial_t key, int type) + gid = atoi(id); + rc = nfs4_gid_to_name(gid, domain, name, IDMAP_NAMESZ); + } ++ if (rc < 0) ++ xlog_err("name_lookup: %s: failed: %m", ++ (type == USER ? "nfs4_uid_to_name" : "nfs4_gid_to_name")); + +- if (rc == 0) ++ if (rc == 0) { + rc = keyctl_instantiate(key, &name, strlen(name), 0); +- ++ if (rc < 0) ++ xlog_err("name_lookup: keyctl_instantiate failed: %m"); ++ } + out: + return rc; + } ++/* ++ * Clear all the keys on the given keyring ++ */ ++static int keyring_clear(char *keyring) ++{ ++ FILE *fp; ++ char buf[BUFSIZ]; ++ key_serial_t key; ++ ++ if (keyring == NULL) ++ keyring = DEFAULT_KEYRING; ++ ++ if ((fp = fopen(PROCKEYS, "r")) == NULL) { ++ xlog_err("fopen(%s) failed: %m", PROCKEYS); ++ return 1; ++ } ++ ++ while(fgets(buf, BUFSIZ, fp) != NULL) { ++ if (strstr(buf, "keyring") == NULL) ++ continue; ++ if (strstr(buf, keyring) == NULL) ++ continue; ++ if (verbose) { ++ *(strchr(buf, '\n')) = '\0'; ++ xlog_warn("clearing '%s'", buf); ++ } ++ /* ++ * The key is the first arugment in the string ++ */ ++ *(strchr(buf, ' ')) = '\0'; ++ sscanf(buf, "%x", &key); ++ if (keyctl_clear(key) < 0) { ++ xlog_err("keyctl_clear(0x%x) failed: %m", key); ++ fclose(fp); ++ return 1; ++ } ++ fclose(fp); ++ return 0; ++ } ++ xlog_err("'%s' keyring was not found.", keyring); ++ fclose(fp); ++ return 1; ++} ++/* ++ * Revoke a key ++ */ ++static int key_revoke(char *keystr, int keymask) ++{ ++ FILE *fp; ++ char buf[BUFSIZ], *ptr; ++ key_serial_t key; ++ int mask; ++ ++ xlog_syslog(0); ++ ++ if ((fp = fopen(PROCKEYS, "r")) == NULL) { ++ xlog_err("fopen(%s) failed: %m", PROCKEYS); ++ return 1; ++ } ++ ++ while(fgets(buf, BUFSIZ, fp) != NULL) { ++ if (strstr(buf, "keyring") != NULL) ++ continue; ++ ++ mask = 0; ++ if ((ptr = strstr(buf, "uid:")) != NULL) ++ mask = UIDKEYS; ++ else if ((ptr = strstr(buf, "gid:")) != NULL) ++ mask = GIDKEYS; ++ else ++ continue; ++ ++ if ((keymask & mask) == 0) ++ continue; ++ ++ if (strncmp(ptr+4, keystr, strlen(keystr)) != 0) ++ continue; ++ ++ if (verbose) { ++ *(strchr(buf, '\n')) = '\0'; ++ xlog_warn("revoking '%s'", buf); ++ } ++ /* ++ * The key is the first arugment in the string ++ */ ++ *(strchr(buf, ' ')) = '\0'; ++ sscanf(buf, "%x", &key); ++ ++ if (keyctl_revoke(key) < 0) { ++ xlog_err("keyctl_revoke(0x%x) failed: %m", key); ++ fclose(fp); ++ return 1; ++ } ++ ++ keymask &= ~mask; ++ if (keymask == 0) { ++ fclose(fp); ++ return 0; ++ } ++ } ++ xlog_err("'%s' key was not found.", keystr); ++ fclose(fp); ++ return 1; ++} + + int main(int argc, char **argv) + { + char *arg; + char *value; + char *type; +- int rc = 1; ++ int rc = 1, opt; + int timeout = 600; + key_serial_t key; ++ char *progname, *keystr = NULL; ++ int clearing = 0, keymask = 0; ++ ++ /* Set the basename */ ++ if ((progname = strrchr(argv[0], '/')) != NULL) ++ progname++; ++ else ++ progname = argv[0]; + +- if (argc < 3) ++ xlog_open(progname); ++ ++ while ((opt = getopt(argc, argv, "u:g:r:ct:v")) != -1) { ++ switch (opt) { ++ case 'u': ++ keymask = UIDKEYS; ++ keystr = strdup(optarg); ++ break; ++ case 'g': ++ keymask = GIDKEYS; ++ keystr = strdup(optarg); ++ break; ++ case 'r': ++ keymask = GIDKEYS|UIDKEYS; ++ keystr = strdup(optarg); ++ break; ++ case 'c': ++ clearing++; ++ break; ++ case 'v': ++ verbose++; ++ break; ++ case 't': ++ timeout = atoi(optarg); ++ break; ++ default: ++ xlog_warn(usage, progname); ++ break; ++ } ++ } ++ ++ if (keystr) { ++ rc = key_revoke(keystr, keymask); ++ return rc; ++ } ++ if (clearing) { ++ xlog_syslog(0); ++ rc = keyring_clear(DEFAULT_KEYRING); ++ return rc; ++ } ++ ++ xlog_stderr(0); ++ if ((argc - optind) != 2) { ++ xlog_err("Bad arg count. Check /etc/request-key.conf"); ++ xlog_warn(usage, progname); + return 1; ++ } ++ ++ if (verbose) ++ nfs4_set_debug(verbose, NULL); ++ ++ key = strtol(argv[optind++], NULL, 10); + +- arg = malloc(sizeof(char) * strlen(argv[2]) + 1); +- strcpy(arg, argv[2]); ++ arg = strdup(argv[optind]); ++ if (arg == NULL) { ++ xlog_err("strdup failed: %m"); ++ return 1; ++ } + type = strtok(arg, ":"); + value = strtok(NULL, ":"); + +- if (argc == 4) { +- timeout = atoi(argv[3]); +- if (timeout < 0) +- timeout = 0; ++ if (verbose) { ++ xlog_warn("key: 0x%lx type: %s value: %s timeout %ld", ++ key, type, value, timeout); + } + +- key = strtol(argv[1], NULL, 10); +- + if (strcmp(type, "uid") == 0) + rc = id_lookup(value, key, USER); + else if (strcmp(type, "gid") == 0) +@@ -109,7 +311,7 @@ int main(int argc, char **argv) + else if (strcmp(type, "group") == 0) + rc = name_lookup(value, key, GROUP); + +- /* Set timeout to 5 (600 seconds) minutes */ ++ /* Set timeout to 10 (600 seconds) minutes */ + if (rc == 0) + keyctl_set_timeout(key, timeout); + +diff --git a/utils/nfsidmap/nfsidmap.man b/utils/nfsidmap/nfsidmap.man +index 2381908..3a3a523 100644 +--- a/utils/nfsidmap/nfsidmap.man ++++ b/utils/nfsidmap/nfsidmap.man +@@ -5,6 +5,12 @@ + .TH nfsidmap 5 "1 October 2010" + .SH NAME + nfsidmap \- The NFS idmapper upcall program ++.SH SYNOPSIS ++.B "nfsidmap [-v] [-t timeout] key desc" ++.br ++.B "nfsidmap [-v] [-c]" ++.br ++.B "nfsidmap [-v] [-u|-g|-r user]" + .SH DESCRIPTION + The file + .I /usr/sbin/nfsidmap +@@ -12,11 +18,36 @@ is used by the NFS idmapper to translate user and group ids into names, and to + translate user and group names into ids. Idmapper uses request-key to perform + the upcall and cache the result. + .I /usr/sbin/nfsidmap +-should only be called by request-key, and will perform the translation and ++is called by /sbin/request-key, and will perform the translation and + initialize a key with the resulting information. + .PP +-NFS_USE_NEW_IDMAPPER must be selected when configuring the kernel to use this +-feature. ++.I nfsidmap ++can also used to clear the keyring of all the keys or ++revoke one particular key. ++This is useful when the id mappings have failed to due ++to a lookup error resulting in all the cached uids/gids to be set ++to the user id nobody. ++.SH OPTIONS ++.TP ++.B -c ++Clear the keyring of all the keys. ++.TP ++.B -g user ++Revoke the gid key of the given user. ++.TP ++.B -r user ++Revoke both the uid and gid key of the given user. ++.TP ++.B -t timeout ++Set the expiration timer, in seconds, on the key. ++The default is 600 seconds (10 mins). ++.TP ++.B -u user ++Revoke the uid key of the given user. ++.TP ++.B -v ++Increases the verbosity of the output to syslog ++(can be specified multiple times). + .SH CONFIGURING + The file + .I /etc/request-key.conf +@@ -25,11 +56,13 @@ will need to be modified so + can properly direct the upcall. The following line should be added before a call + to keyctl negate: + .PP +-create id_resolver * * /usr/sbin/nfsidmap %k %d 600 ++create id_resolver * * /usr/sbin/nfsidmap -t 600 %k %d + .PP + This will direct all id_resolver requests to the program +-.I /usr/sbin/nfsidmap +-The last parameter, 600, defines how many seconds into the future the key will ++.I /usr/sbin/nfsidmap. ++The ++.B -t 600 ++defines how many seconds into the future the key will + expire. This is an optional parameter for + .I /usr/sbin/nfsidmap + and will default to 600 seconds when not specified. +@@ -48,9 +81,9 @@ You can choose to handle any of these individually, rather than using the + generic upcall program. If you would like to use your own program for a uid + lookup then you would edit your request-key.conf so it looks similar to this: + .PP +-create id_resolver uid:* * /some/other/program %k %d 600 ++create id_resolver uid:* * /some/other/program %k %d + .br +-create id_resolver * * /usr/sbin/nfsidmap %k %d 600 ++create id_resolver * * /usr/sbin/nfsidmap %k %d + .PP + Notice that the new line was added above the line for the generic program. + request-key will find the first matching line and run the corresponding program. +diff --git a/utils/osd_login/Makefile.am b/utils/osd_login/Makefile.am +new file mode 100644 +index 0000000..adc493a +--- /dev/null ++++ b/utils/osd_login/Makefile.am +@@ -0,0 +1,12 @@ ++## Process this file with automake to produce Makefile.in ++ ++OSD_LOGIN_FILES= osd_login ++ ++EXTRA_DIST= $(OSD_LOGIN_FILES) ++ ++all-local: $(OSD_LOGIN_FILES) ++ ++install-data-hook: ++ $(INSTALL) --mode 755 osd_login $(DESTDIR)/sbin/osd_login ++ ++MAINTAINERCLEANFILES = Makefile.in +diff --git a/utils/osd_login/osd_login b/utils/osd_login/osd_login +new file mode 100644 +index 0000000..08cd2d2 +--- /dev/null ++++ b/utils/osd_login/osd_login +@@ -0,0 +1,118 @@ ++#!/bin/bash ++# ++# osd_login : This script is part of the autologin feature ++# mandated by the pnfs-objects standard. ++# It is called from objlayoutdriver.ko in the kernel. ++ ++# Copyright (C) 2012, Sachin Bhamare ++# Copyright (C) 2012, Boaz Harrosh ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License version 2 as ++# published by the Free Software Foundation. ++# ++# This program 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 this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ++# MA 02110-1301 USA ++ ++umask 022 ++ ++PATH="/sbin:/usr/sbin:/bin:/usr/bin" ++ ++iscsiadm=/sbin/iscsiadm ++ ++PARENT_PID=$BASHPID ++WATCHDOG_TIMEOUT=15 ++ ++protocol="" ++portal="" ++uri="" ++osdname="" ++systemid="" ++ ++usage() ++{ ++ echo "Usage: $0 -u -o -s " ++ echo "Options:" ++ echo "-u target uri e.g. iscsi://:" ++ echo "-o osdname of the target OSD" ++ echo "-s systemid of the target OSD" ++} ++ ++parse_cmdline() ++{ ++ argc=$# ++ if [ $# -lt 3 ]; then ++ usage ++ exit 1 ++ fi ++ ++ # parse the input arguments ++ while getopts "u:o:s:" options; do ++ case $options in ++ u ) uri=$OPTARG;; ++ o ) osdname=$OPTARG;; ++ s ) systemid=$OPTARG;; ++ \? ) usage ++ exit 1;; ++ * ) usage ++ exit 1;; ++ esac ++ done ++ ++ echo "-u : $uri" ++ echo "-o : $osdname" ++ echo "-s : $systemid" ++ ++ protocol=`echo $uri | awk -F ':' '{print $1}'` ++ portal=`echo $uri | awk -F '//' '{print $2}'` ++} ++ ++watchdog() ++{ ++ timeout=$1 ++ portal=$2 ++ ++ sleep $timeout ++ if kill -9 $PARENT_PID; then ++ echo "watchdog : Timed out (>$timeout seconds) while login into $portal" | logger -t "osd_login" ++ fi ++ echo "watchdog: exiting .." ++ exit 2 ++} ++ ++login_iscsi_osd() ++{ ++ echo "login into: $1" ++ if ! $iscsiadm -m discovery -o nonpersistent -t sendtargets -p $1 --login; then ++ echo "$iscsiadm -m discovery -t sendtargets -p $1 --login returned error $? !" ++ sleep 1; ++ fi ++} ++ ++echo "============= osd_login =========" ++echo "progname : $0" ++parse_cmdline "$@" ++echo "protocol: $protocol" ++echo "portal: $portal" ++ ++watchdog $WATCHDOG_TIMEOUT $portal & ++watchdog_pid=$! ++ ++case $protocol in ++iscsi) ++ login_iscsi_osd $portal |& logger -t "osd_login" ++ ;; ++*) ++ echo "Error: protocol $protocol not supported !" | logger -t "osd_login" ++ ;; ++esac ++ ++kill -9 $watchdog_pid ++exit 0 +diff --git a/utils/showmount/Makefile.am b/utils/showmount/Makefile.am +index 077b2c7..4ba5ead 100644 +--- a/utils/showmount/Makefile.am ++++ b/utils/showmount/Makefile.am +@@ -7,7 +7,8 @@ sbin_PROGRAMS = showmount + showmount_SOURCES = showmount.c + showmount_LDADD = ../../support/export/libexport.a \ + ../../support/nfs/libnfs.a \ +- ../../support/misc/libmisc.a ++ ../../support/misc/libmisc.a \ ++ $(LIBTIRPC) + showmount_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) \ + -I$(top_builddir)/support/export + +diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am +index 1744791..dc2bfc4 100644 +--- a/utils/statd/Makefile.am ++++ b/utils/statd/Makefile.am +@@ -15,10 +15,10 @@ BUILT_SOURCES = $(GENFILES) + statd_LDADD = ../../support/nsm/libnsm.a \ + ../../support/nfs/libnfs.a \ + ../../support/misc/libmisc.a \ +- $(LIBWRAP) $(LIBNSL) $(LIBCAP) ++ $(LIBWRAP) $(LIBNSL) $(LIBCAP) $(LIBTIRPC) + sm_notify_LDADD = ../../support/nsm/libnsm.a \ + ../../support/nfs/libnfs.a \ +- $(LIBNSL) $(LIBCAP) ++ $(LIBNSL) $(LIBCAP) $(LIBTIRPC) + + EXTRA_DIST = sim_sm_inter.x $(man8_MANS) COPYRIGHT simulate.c + diff --git a/nfs-utils.spec b/nfs-utils.spec index 288ce45..6ef9b13 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: 1.2.5 -Release: 15%{?dist} +Release: 16%{?dist} Epoch: 1 # group all 32bit related archs @@ -30,12 +30,7 @@ Source52: nfs-server.postconfig Source60: nfs4-modalias.conf -Patch001: nfs-utils-1.2.6-rc6.patch -Patch002: nfs-utils-1.2.4-mountshortcut.patch -Patch003: nfs-utils-1.2.5-libidmap-hide-syms.patch -Patch004: nfs-utils-1.2.5-nfsd-new-default.patch -Patch005: nfs-utils-1.2.5-gssd-usercreds.patch -Patch006: nfs-utils-1.2.5-gssd-nolibgssapi-krb5.patch +Patch001: nfs-utils-1.2.6-rc7.patch Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch @@ -93,11 +88,6 @@ This package also contains the mount.nfs and umount.nfs program. %setup -q %patch001 -p1 -%patch002 -p1 -%patch003 -p1 -%patch004 -p1 -%patch005 -p1 -%patch006 -p1 %patch100 -p1 %patch101 -p1 @@ -262,6 +252,7 @@ fi %doc linux-nfs/ChangeLog linux-nfs/KNOWNBUGS linux-nfs/NEW linux-nfs/README %doc linux-nfs/THANKS linux-nfs/TODO /sbin/rpc.statd +/sbin/osd_login /usr/sbin/exportfs /usr/sbin/nfsstat /usr/sbin/rpcdebug @@ -290,6 +281,9 @@ fi %attr(4755,root,root) /sbin/umount.nfs4 %changelog +* Thu May 3 2012 Steve Dickson 1.2.5-16 +- Update to the latest RC release: nfs-utils-1.2.6-rc7 + * Thu Apr 26 2012 Josh Boyer 1.2.5-15 - Add modprobe config file to alias 'nfs4' to 'nfs' (bz 806333)