diff --git a/nfs-utils-2.3.2-rc1.patch b/nfs-utils-2.3.2-rc1.patch new file mode 100644 index 0000000..3d0bf3a --- /dev/null +++ b/nfs-utils-2.3.2-rc1.patch @@ -0,0 +1,6449 @@ +diff --git a/.gitignore b/.gitignore +index f17db9c..692ab0e 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -72,6 +72,7 @@ tests/nsm_client/nlm_sm_inter_clnt.c + tests/nsm_client/nlm_sm_inter_svc.c + tests/nsm_client/nlm_sm_inter_xdr.c + utils/nfsidmap/nfsidmap ++utils/nfsref/nfsref + systemd/nfs-server-generator + systemd/rpc-pipefs-generator + systemd/nfs-config.service +diff --git a/aclocal/libxml2.m4 b/aclocal/libxml2.m4 +new file mode 100644 +index 0000000..5c399b2 +--- /dev/null ++++ b/aclocal/libxml2.m4 +@@ -0,0 +1,15 @@ ++dnl Checks for libxml2.so ++AC_DEFUN([AC_LIBXML2], [ ++ ++ if test "$enable_junction" = yes; then ++ ++ dnl look for the library; do not add to LIBS if found ++ AC_CHECK_LIB([xml2], [xmlParseFile], [LIBXML2=-lxml2], ++ [AC_MSG_ERROR([libxml2 not found.])]) ++ AC_SUBST(LIBXML2) ++ ++ dnl XXX should also check for presence of xml headers ++ ++ fi ++ ++])dnl +diff --git a/configure.ac b/configure.ac +index 672dd40..5a11636 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -180,6 +180,13 @@ else + enable_libmount=no + fi + ++AC_ARG_ENABLE(junction, ++ [AC_HELP_STRING([--enable-junction], ++ [enable support for NFS junctions @<:@default=no@:>@])], ++ enable_junction=$enableval, ++ enable_junction=no) ++AM_CONDITIONAL(CONFIG_JUNCTION, [test "$enable_junction" = "yes" ]) ++ + AC_ARG_ENABLE(tirpc, + [AC_HELP_STRING([--disable-tirpc], + [disable use of TI-RPC library @<:@default=no@:>@])], +@@ -244,6 +251,9 @@ AC_LIBTIRPC + dnl Check for -lcap + AC_LIBCAP + ++dnl Check for -lxml2 ++AC_LIBXML2 ++ + # Check whether user wants TCP wrappers support + AC_TCP_WRAPPERS + +@@ -298,8 +308,6 @@ 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 +@@ -362,7 +370,6 @@ AC_SUBST(LIBSOCKET) + AC_SUBST(LIBCRYPT) + AC_SUBST(LIBBSD) + AC_SUBST(LIBBLKID) +-AC_SUBST(LIBDL) + + if test "$enable_libmount" = yes; then + AC_CHECK_LIB(mount, mnt_context_do_mount, [LIBMOUNT="-lmount"], AC_MSG_ERROR([libmount needed])) +@@ -441,7 +448,7 @@ if test -n "$path_plugins" ; then + fi + AM_CONDITIONAL(PATH_PLUGINS, test -n "$path_plugins") + +-AC_SUBST(AM_CPPFLAGS, "$AM_CPPFLAGS -I../../support/nfsidmap") ++AC_SUBST(AM_CPPFLAGS, "$AM_CPPFLAGS") + AC_DEFINE([HAVE_NFS4_SET_DEBUG], 1, + [Bundled lib always has the `nfs4_set_debug' function.]) + +@@ -456,7 +463,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 nfs-plugin.h libio.h]) ++ ifaddrs.h]) + + dnl ************************************************************* + dnl Checks for typedefs, structures, and compiler characteristics +@@ -536,22 +543,46 @@ AC_SUBST(CXXFLAGS_FOR_BUILD) + AC_SUBST(CPPFLAGS_FOR_BUILD) + AC_SUBST(LDFLAGS_FOR_BUILD) + +-dnl ************************************************************* +-dnl Set up "global" CFLAGS +-dnl ************************************************************* +-dnl Use architecture-specific compile flags +-dnl (We use $host and not $build in case we are cross-compiling) +-dnl ************************************************************* +-dnl Note: we no longer have arch specific compile flags, but +-dnl the stub is left here in case they are needed one day. +-case $host in +- *) +- ARCHFLAGS="" ;; +-esac +- +-my_am_cflags="-Wall -Wextra -Wstrict-prototypes $ARCHFLAGS -pipe" +- +-AC_SUBST([AM_CFLAGS], ["$my_am_cflags"]) ++my_am_cflags="\ ++ -pipe \ ++ -Wall \ ++ -Wextra \ ++ -Werror=strict-prototypes \ ++ -Werror=missing-prototypes \ ++ -Werror=missing-declarations \ ++ -Werror=format=2 \ ++ -Werror=undef \ ++ -Werror=missing-include-dirs \ ++ -Werror=strict-aliasing=2 \ ++ -Werror=init-self \ ++ -Werror=implicit-function-declaration \ ++ -Werror=return-type \ ++ -Werror=switch \ ++ -Werror=overflow \ ++ -Werror=parentheses \ ++ -Werror=aggregate-return \ ++ -Werror=unused-result \ ++ -fno-strict-aliasing \ ++" ++ ++AC_DEFUN([CHECK_CCSUPPORT], [ ++ my_save_cflags="$CFLAGS" ++ CFLAGS=$1 ++ AC_MSG_CHECKING([whether CC supports $1]) ++ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], ++ [AC_MSG_RESULT([yes])] ++ [$2+=$1], ++ [AC_MSG_RESULT([no])] ++ ) ++ CFLAGS="$my_save_cflags" ++]) ++ ++CHECK_CCSUPPORT([-Werror=format-overflow=2], [flg1]) ++CHECK_CCSUPPORT([-Werror=int-conversion], [flg2]) ++CHECK_CCSUPPORT([-Werror=incompatible-pointer-types], [flg3]) ++CHECK_CCSUPPORT([-Werror=misleading-indentation], [flg4]) ++ ++AC_SUBST([AM_CFLAGS], ["$my_am_cflags $flg1 $flg2 $flg3 $flg4"]) + + # Make sure that $ACLOCAL_FLAGS are used during a rebuild + AC_SUBST([ACLOCAL_AMFLAGS], ["-I $ac_macro_dir \$(ACLOCAL_FLAGS)"]) +@@ -572,6 +603,7 @@ AC_CONFIG_FILES([ + support/include/sys/fs/Makefile + support/include/sys/Makefile + support/include/Makefile ++ support/junction/Makefile + support/misc/Makefile + support/nfs/Makefile + support/nsm/Makefile +@@ -593,6 +625,7 @@ AC_CONFIG_FILES([ + utils/mount/Makefile + utils/mountd/Makefile + utils/nfsd/Makefile ++ utils/nfsref/Makefile + utils/nfsstat/Makefile + utils/nfsidmap/Makefile + utils/showmount/Makefile +diff --git a/support/Makefile.am b/support/Makefile.am +index 8365d3b..c962d4d 100644 +--- a/support/Makefile.am ++++ b/support/Makefile.am +@@ -6,6 +6,10 @@ if CONFIG_NFSV4 + OPTDIRS += nfsidmap + endif + ++if CONFIG_JUNCTION ++OPTDIRS += junction ++endif ++ + SUBDIRS = export include misc nfs nsm $(OPTDIRS) + + MAINTAINERCLEANFILES = Makefile.in +diff --git a/support/export/Makefile.am b/support/export/Makefile.am +index be3de69..13f7a49 100644 +--- a/support/export/Makefile.am ++++ b/support/export/Makefile.am +@@ -35,7 +35,7 @@ $(GENFILES_CLNT): %_clnt.c: %.x $(RPCGEN) + + $(GENFILES_XDR): %_xdr.c: %.x $(RPCGEN) + test -f $@ && rm -rf $@ || true +- $(RPCGEN) -c -o $@ $< ++ $(RPCGEN) -c -i 0 -o $@ $< + + $(GENFILES_H): %.h: %.x $(RPCGEN) + test -f $@ && rm -rf $@ || true +diff --git a/support/include/Makefile.am b/support/include/Makefile.am +index 5c80c8b..599f500 100644 +--- a/support/include/Makefile.am ++++ b/support/include/Makefile.am +@@ -6,6 +6,7 @@ noinst_HEADERS = \ + cld.h \ + exportfs.h \ + ha-callout.h \ ++ junction.h \ + misc.h \ + nfs_mntent.h \ + nfs_paths.h \ +diff --git a/support/include/junction.h b/support/include/junction.h +new file mode 100644 +index 0000000..7257d80 +--- /dev/null ++++ b/support/include/junction.h +@@ -0,0 +1,167 @@ ++/* ++ * @file support/include/junction.h ++ * @brief Declarations for libjunction.a ++ */ ++ ++/* ++ * Copyright 2010, 2018 Oracle. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2.0 as ++ * published by the Free Software Foundation. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License version 2.0 for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * version 2.0 along with nfs-utils. If not, see: ++ * ++ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++ */ ++ ++#ifndef _NFS_JUNCTION_H_ ++#define _NFS_JUNCTION_H_ ++ ++#include ++ ++/* ++ * The libjunction APIs use the status codes from the FedFS ADMIN ++ * protocol, which includes non-errno codes like FEDFS_ERR_NOTJUNCT. ++ */ ++enum FedFsStatus { ++ FEDFS_OK = 0, ++ FEDFS_ERR_ACCESS = 1, ++ FEDFS_ERR_BADCHAR = 2, ++ FEDFS_ERR_BADNAME = 3, ++ FEDFS_ERR_NAMETOOLONG = 4, ++ FEDFS_ERR_LOOP = 5, ++ FEDFS_ERR_BADXDR = 6, ++ FEDFS_ERR_EXIST = 7, ++ FEDFS_ERR_INVAL = 8, ++ FEDFS_ERR_IO = 9, ++ FEDFS_ERR_NOSPC = 10, ++ FEDFS_ERR_NOTJUNCT = 11, ++ FEDFS_ERR_NOTLOCAL = 12, ++ FEDFS_ERR_PERM = 13, ++ FEDFS_ERR_ROFS = 14, ++ FEDFS_ERR_SVRFAULT = 15, ++ FEDFS_ERR_NOTSUPP = 16, ++ FEDFS_ERR_NSDB_ROUTE = 17, ++ FEDFS_ERR_NSDB_DOWN = 18, ++ FEDFS_ERR_NSDB_CONN = 19, ++ FEDFS_ERR_NSDB_AUTH = 20, ++ FEDFS_ERR_NSDB_LDAP = 21, ++ FEDFS_ERR_NSDB_LDAP_VAL = 22, ++ FEDFS_ERR_NSDB_NONCE = 23, ++ FEDFS_ERR_NSDB_NOFSN = 24, ++ FEDFS_ERR_NSDB_NOFSL = 25, ++ FEDFS_ERR_NSDB_RESPONSE = 26, ++ FEDFS_ERR_NSDB_FAULT = 27, ++ FEDFS_ERR_NSDB_PARAMS = 28, ++ FEDFS_ERR_NSDB_LDAP_REFERRAL = 29, ++ FEDFS_ERR_NSDB_LDAP_REFERRAL_VAL = 30, ++ FEDFS_ERR_NSDB_LDAP_REFERRAL_NOTFOLLOWED = 31, ++ FEDFS_ERR_NSDB_PARAMS_LDAP_REFERRAL = 32, ++ FEDFS_ERR_PATH_TYPE_UNSUPP = 33, ++ FEDFS_ERR_DELAY = 34, ++ FEDFS_ERR_NO_CACHE = 35, ++ FEDFS_ERR_UNKNOWN_CACHE = 36, ++ FEDFS_ERR_NO_CACHE_UPDATE = 37, ++}; ++typedef enum FedFsStatus FedFsStatus; ++ ++/** ++ * Contains NFS fileset location information ++ * ++ * Each of these represents one server:/rootpath pair. The NFS ++ * implementation can coalesce multiple pairs into a single ++ * fs_location4 result if jfl_rootpath is the same across ++ * multiple servers. ++ * ++ * The nfl_server field can contain either one presentation format ++ * IP address or one DNS hostname. ++ * ++ * See Section 11.9 and 11.10 of RFC 5661 or section 4.2.2.3 and ++ * 4.2.2.4 of the NSDB protocol draft for details. ++ */ ++ ++struct nfs_fsloc { ++ struct nfs_fsloc *nfl_next; ++ ++ char *nfl_hostname; ++ uint16_t nfl_hostport; ++ char **nfl_rootpath; ++ ++ struct { ++ _Bool nfl_varsub; ++ } nfl_flags; ++ int32_t nfl_currency; ++ int32_t nfl_validfor; ++ ++ struct { ++ _Bool nfl_writable, nfl_going, nfl_split; ++ } nfl_genflags; ++ struct { ++ _Bool nfl_rdma; ++ } nfl_transflags; ++ struct { ++ uint8_t nfl_simul, nfl_handle, nfl_fileid; ++ uint8_t nfl_writever, nfl_change, nfl_readdir; ++ uint8_t nfl_readrank, nfl_writerank; ++ uint8_t nfl_readorder, nfl_writeorder; ++ } nfl_info; ++}; ++ ++ ++/** ++ ** NFS location data management functions ++ **/ ++ ++void nfs_free_location(struct nfs_fsloc *location); ++void nfs_free_locations(struct nfs_fsloc *locations); ++struct nfs_fsloc *nfs_new_location(void); ++ ++__attribute_malloc__ ++char **nfs_dup_string_array(char **array); ++void nfs_free_string_array(char **array); ++ ++ ++/** ++ ** NFS junction management functions ++ **/ ++ ++FedFsStatus nfs_delete_junction(const char *pathname); ++FedFsStatus nfs_add_junction(const char *pathname, ++ struct nfs_fsloc *locations); ++FedFsStatus nfs_get_locations(const char *pathname, ++ struct nfs_fsloc **locations); ++FedFsStatus nfs_is_prejunction(const char *pathname); ++FedFsStatus nfs_is_junction(const char *pathname); ++ ++ ++/** ++ ** Flush kernel NFS server's export cache ++ **/ ++FedFsStatus junction_flush_exports_cache(void); ++ ++/** ++ ** Pathname conversion helpers ++ **/ ++void nsdb_free_string_array(char **strings); ++FedFsStatus nsdb_path_array_to_posix(char * const *path_array, ++ char **pathname); ++FedFsStatus nsdb_posix_to_path_array(const char *pathname, ++ char ***path_array); ++ ++/** ++ ** Readability helpers ++ **/ ++ ++const char *nsdb_display_fedfsstatus(const FedFsStatus status); ++void nsdb_print_fedfsstatus(const FedFsStatus status); ++ ++#endif /* !_NFS_JUNCTION_H_ */ +diff --git a/support/include/sockaddr.h b/support/include/sockaddr.h +index 446b537..eeebcdf 100644 +--- a/support/include/sockaddr.h ++++ b/support/include/sockaddr.h +@@ -24,9 +24,7 @@ + #include + #endif + +-#ifdef HAVE_LIBIO_H +-#include +-#endif ++#include + #include + #include + #include +diff --git a/support/junction/Makefile.am b/support/junction/Makefile.am +new file mode 100644 +index 0000000..97e7426 +--- /dev/null ++++ b/support/junction/Makefile.am +@@ -0,0 +1,34 @@ ++## ++## @file support/junction/Makefile.am ++## @brief Process this file with automake to produce src/libjunction/Makefile.in ++## ++ ++## ++## Copyright 2010, 2018 Oracle. All rights reserved. ++## ++## This file is part of nfs-utils. ++## ++## nfs-utils is free software; you can redistribute it and/or modify ++## it under the terms of the GNU General Public License version 2.0 as ++## published by the Free Software Foundation. ++## ++## nfs-utils is distributed in the hope that it will be useful, but ++## WITHOUT ANY WARRANTY; without even the implied warranty of ++## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++## GNU General Public License version 2.0 for more details. ++## ++## You should have received a copy of the GNU General Public License ++## version 2.0 along with nfs-utils. If not, see: ++## ++## http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++## ++ ++noinst_HEADERS = junction-internal.h ++ ++noinst_LTLIBRARIES = libjunction.la ++libjunction_la_SOURCES = display.c export-cache.c junction.c \ ++ locations.c nfs.c path.c xml.c ++ ++MAINTAINERCLEANFILES = Makefile.in ++ ++AM_CPPFLAGS = -I. -I../include -I/usr/include/libxml2 +diff --git a/support/junction/display.c b/support/junction/display.c +new file mode 100644 +index 0000000..e1e1af1 +--- /dev/null ++++ b/support/junction/display.c +@@ -0,0 +1,139 @@ ++/** ++ * @file support/junction/display.c ++ * @brief Shared display helper functions ++ */ ++ ++/* ++ * Copyright 2010, 2018 Oracle. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2.0 as ++ * published by the Free Software Foundation. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License version 2.0 for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * version 2.0 along with nfs-utils. If not, see: ++ * ++ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "junction.h" ++ ++/** ++ * Return human-readable equivalent of a FedFsStatus value ++ * ++ * @param status FedFsStatus code ++ * @return a static NUL-terminated C string ++ */ ++const char * ++nsdb_display_fedfsstatus(const FedFsStatus status) ++{ ++ switch (status) { ++ case FEDFS_OK: ++ return "FEDFS_OK"; ++ case FEDFS_ERR_ACCESS: ++ return "FEDFS_ERR_ACCESS"; ++ case FEDFS_ERR_BADCHAR: ++ return "FEDFS_ERR_BADCHAR"; ++ case FEDFS_ERR_BADNAME: ++ return "FEDFS_ERR_BADNAME"; ++ case FEDFS_ERR_NAMETOOLONG: ++ return "FEDFS_ERR_NAMETOOLONG"; ++ case FEDFS_ERR_LOOP: ++ return "FEDFS_ERR_LOOP"; ++ case FEDFS_ERR_BADXDR: ++ return "FEDFS_ERR_BADXDR"; ++ case FEDFS_ERR_EXIST: ++ return "FEDFS_ERR_EXIST"; ++ case FEDFS_ERR_INVAL: ++ return "FEDFS_ERR_INVAL"; ++ case FEDFS_ERR_IO: ++ return "FEDFS_ERR_IO"; ++ case FEDFS_ERR_NOSPC: ++ return "FEDFS_ERR_NOSPC"; ++ case FEDFS_ERR_NOTJUNCT: ++ return "FEDFS_ERR_NOTJUNCT"; ++ case FEDFS_ERR_NOTLOCAL: ++ return "FEDFS_ERR_NOTLOCAL"; ++ case FEDFS_ERR_PERM: ++ return "FEDFS_ERR_PERM"; ++ case FEDFS_ERR_ROFS: ++ return "FEDFS_ERR_ROFS"; ++ case FEDFS_ERR_SVRFAULT: ++ return "FEDFS_ERR_SVRFAULT"; ++ case FEDFS_ERR_NOTSUPP: ++ return "FEDFS_ERR_NOTSUPP"; ++ case FEDFS_ERR_NSDB_ROUTE: ++ return "FEDFS_ERR_NSDB_ROUTE"; ++ case FEDFS_ERR_NSDB_DOWN: ++ return "FEDFS_ERR_NSDB_DOWN"; ++ case FEDFS_ERR_NSDB_CONN: ++ return "FEDFS_ERR_NSDB_CONN"; ++ case FEDFS_ERR_NSDB_AUTH: ++ return "FEDFS_ERR_NSDB_AUTH"; ++ case FEDFS_ERR_NSDB_LDAP: ++ return "FEDFS_ERR_NSDB_LDAP"; ++ case FEDFS_ERR_NSDB_LDAP_VAL: ++ return "FEDFS_ERR_NSDB_LDAP_VAL"; ++ case FEDFS_ERR_NSDB_NONCE: ++ return "FEDFS_ERR_NSDB_NONCE"; ++ case FEDFS_ERR_NSDB_NOFSN: ++ return "FEDFS_ERR_NSDB_NOFSN"; ++ case FEDFS_ERR_NSDB_NOFSL: ++ return "FEDFS_ERR_NSDB_NOFSL"; ++ case FEDFS_ERR_NSDB_RESPONSE: ++ return "FEDFS_ERR_NSDB_RESPONSE"; ++ case FEDFS_ERR_NSDB_FAULT: ++ return "FEDFS_ERR_NSDB_FAULT"; ++ case FEDFS_ERR_NSDB_PARAMS: ++ return "FEDFS_ERR_NSDB_PARAMS"; ++ case FEDFS_ERR_NSDB_LDAP_REFERRAL: ++ return "FEDFS_ERR_NSDB_LDAP_REFERRAL"; ++ case FEDFS_ERR_NSDB_LDAP_REFERRAL_VAL: ++ return "FEDFS_ERR_NSDB_LDAP_REFERRAL_VAL"; ++ case FEDFS_ERR_NSDB_PARAMS_LDAP_REFERRAL: ++ return "FEDFS_ERR_NSDB_PARAMS_LDAP_REFERRAL"; ++ case FEDFS_ERR_PATH_TYPE_UNSUPP: ++ return "FEDFS_ERR_PATH_TYPE_UNSUPP"; ++ case FEDFS_ERR_DELAY: ++ return "FEDFS_ERR_DELAY"; ++ case FEDFS_ERR_NO_CACHE: ++ return "FEDFS_ERR_NO_CACHE"; ++ case FEDFS_ERR_UNKNOWN_CACHE: ++ return "FEDFS_ERR_UNKNOWN_CACHE"; ++ case FEDFS_ERR_NO_CACHE_UPDATE: ++ return "FEDFS_ERR_NO_CACHE_UPDATE"; ++ default: ++ break; ++ } ++ return "an unrecognized error code"; ++} ++ ++/** ++ * Display human-readable FedFsStatus on stderr ++ * ++ * @param status FedFsStatus value to display ++ */ ++void ++nsdb_print_fedfsstatus(const FedFsStatus status) ++{ ++ if (status == FEDFS_OK) { ++ printf("Call completed successfully\n"); ++ return; ++ } ++ ++ fprintf(stderr, "Server returned %s\n", ++ nsdb_display_fedfsstatus(status)); ++} +diff --git a/support/junction/export-cache.c b/support/junction/export-cache.c +new file mode 100644 +index 0000000..4e578c9 +--- /dev/null ++++ b/support/junction/export-cache.c +@@ -0,0 +1,118 @@ ++/** ++ * @file support/junction/export-cache.c ++ * @brief Try to flush NFSD's exports cache ++ */ ++ ++/* ++ * Copyright 2011, 2018 Oracle. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2.0 as ++ * published by the Free Software Foundation. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License version 2.0 for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * version 2.0 along with nfs-utils. If not, see: ++ * ++ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#include "junction.h" ++#include "xlog.h" ++ ++/** ++ * Ordered list of proc files to poke when requesting an NFSD cache flush ++ */ ++static const char *junction_proc_files[] = { ++ "/proc/net/rpc/auth.unix.ip/flush", ++ "/proc/net/rpc/auth.unix.gid/flush", ++ "/proc/net/rpc/nfsd.fh/flush", ++ "/proc/net/rpc/nfsd.export/flush", ++ NULL, ++}; ++ ++/** ++ * Write time into one file ++ * ++ * @param pathname NUL-terminated C string containing POSIX pathname of file to write ++ * @param flushtime NUL-terminated C string containing current time in seconds since the Epoch ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++junction_write_time(const char *pathname, const char *flushtime) ++{ ++ FedFsStatus retval; ++ ssize_t len; ++ int fd; ++ ++ fd = open(pathname, O_RDWR); ++ if (fd == -1) { ++ xlog(D_GENERAL, "%s: Failed to open %s: %m", ++ __func__, pathname); ++ /* If the proc files don't exist, no server ++ * is running on this system */ ++ return FEDFS_ERR_NO_CACHE_UPDATE; ++ } ++ ++ len = write(fd, flushtime, strlen(flushtime)); ++ if (len != (ssize_t)strlen(flushtime)) { ++ xlog(D_GENERAL, "%s: Failed to write %s: %m", ++ __func__, pathname); ++ /* If the proc files exist but the update failed, ++ * we don't know the state of the cache */ ++ retval = FEDFS_ERR_UNKNOWN_CACHE; ++ } else ++ /* Cache flush succeeded */ ++ retval = FEDFS_OK; ++ ++ (void)close(fd); ++ return retval; ++} ++ ++/** ++ * Flush the kernel NFSD's exports cache ++ * ++ * @return a FedFsStatus code ++ */ ++FedFsStatus ++junction_flush_exports_cache(void) ++{ ++ FedFsStatus retval; ++ char flushtime[20]; ++ unsigned int i; ++ time_t now; ++ ++ xlog(D_CALL, "%s: Flushing NFSD caches...", __func__); ++ ++ now = time(NULL); ++ if (now == -1) { ++ xlog(D_GENERAL, "%s: time(3) failed", __func__); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ snprintf(flushtime, sizeof(flushtime), "%ld\n", now); ++ ++ for (i = 0; junction_proc_files[i] != NULL; i++) { ++ retval = junction_write_time(junction_proc_files[i], flushtime); ++ if (retval != FEDFS_OK) ++ return retval; ++ } ++ return FEDFS_OK; ++} +diff --git a/support/junction/junction-internal.h b/support/junction/junction-internal.h +new file mode 100644 +index 0000000..3dff4cc +--- /dev/null ++++ b/support/junction/junction-internal.h +@@ -0,0 +1,121 @@ ++/* ++ * @file support/junction/junction-internal.h ++ * @brief Internal declarations for libjunction.a ++ */ ++ ++/* ++ * Copyright 2011, 2018 Oracle. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2.0 as ++ * published by the Free Software Foundation. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License version 2.0 for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * version 2.0 along with nfs-utils. If not, see: ++ * ++ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++ */ ++ ++#ifndef _FEDFS_JUNCTION_INTERNAL_H_ ++#define _FEDFS_JUNCTION_INTERNAL_H_ ++ ++#include ++#include ++ ++/** ++ ** Names of extended attributes that store junction data ++ **/ ++ ++/** ++ * Name of extended attribute containing saved mode bits ++ */ ++#define JUNCTION_XATTR_NAME_MODE "trusted.junction.mode" ++ ++/** ++ * Name of extended attribute containing NFS-related junction data ++ */ ++#define JUNCTION_XATTR_NAME_NFS "trusted.junction.nfs" ++ ++ ++/** ++ ** Names of XML elements and attributes that represent junction data ++ **/ ++ ++/** ++ * Tag name of root element of a junction XML document ++ */ ++#define JUNCTION_XML_ROOT_TAG (const xmlChar *)"junction" ++ ++/** ++ * Tag name of fileset element of a junction XML document ++ */ ++#define JUNCTION_XML_FILESET_TAG (const xmlChar *)"fileset" ++ ++/** ++ * Tag name of savedmode element of a junction XML document ++ */ ++#define JUNCTION_XML_SAVEDMODE_TAG (const xmlChar *)"savedmode" ++ ++/** ++ * Name of mode bits attribute on a savedmode element ++ */ ++#define JUNCTION_XML_MODEBITS_ATTR (const xmlChar *)"bits" ++ ++/** ++ ** Junction helper functions ++ **/ ++ ++FedFsStatus junction_open_path(const char *pathname, int *fd); ++FedFsStatus junction_is_directory(int fd, const char *path); ++FedFsStatus junction_is_sticky_bit_set(int fd, const char *path); ++FedFsStatus junction_set_sticky_bit(int fd, const char *path); ++FedFsStatus junction_is_xattr_present(int fd, const char *path, ++ const char *name); ++FedFsStatus junction_read_xattr(int fd, const char *path, const char *name, ++ char **contents); ++FedFsStatus junction_get_xattr(int fd, const char *path, const char *name, ++ void **contents, size_t *contentlen); ++FedFsStatus junction_set_xattr(int fd, const char *path, const char *name, ++ const void *contents, const size_t contentlen); ++FedFsStatus junction_remove_xattr(int fd, const char *pathname, ++ const char *name); ++FedFsStatus junction_get_mode(const char *pathname, mode_t *mode); ++FedFsStatus junction_save_mode(const char *pathname); ++FedFsStatus junction_restore_mode(const char *pathname); ++ ++ ++/** ++ ** XML helper functions ++ **/ ++ ++_Bool junction_xml_is_empty(const xmlChar *content); ++_Bool junction_xml_match_node_name(xmlNodePtr node, ++ const xmlChar *name); ++xmlNodePtr junction_xml_find_child_by_name(xmlNodePtr parent, ++ const xmlChar *name); ++_Bool junction_xml_get_bool_attribute(xmlNodePtr node, ++ const xmlChar *attrname, _Bool *value); ++void junction_xml_set_bool_attribute(xmlNodePtr node, ++ const xmlChar *attrname, _Bool value); ++_Bool junction_xml_get_u8_attribute(xmlNodePtr node, ++ const xmlChar *attrname, uint8_t *value); ++_Bool junction_xml_get_int_attribute(xmlNodePtr node, ++ const xmlChar *attrname, int *value); ++void junction_xml_set_int_attribute(xmlNodePtr node, ++ const xmlChar *attrname, int value); ++_Bool junction_xml_get_int_content(xmlNodePtr node, int *value); ++xmlNodePtr junction_xml_set_int_content(xmlNodePtr parent, ++ const xmlChar *name, int value); ++FedFsStatus junction_xml_parse(const char *pathname, const char *name, ++ xmlDocPtr *doc); ++FedFsStatus junction_xml_write(const char *pathname, const char *name, ++ xmlDocPtr doc); ++ ++#endif /* !_FEDFS_JUNCTION_INTERNAL_H_ */ +diff --git a/support/junction/junction.c b/support/junction/junction.c +new file mode 100644 +index 0000000..ab6caa6 +--- /dev/null ++++ b/support/junction/junction.c +@@ -0,0 +1,494 @@ ++/** ++ * @file support/junction/junction.c ++ * @brief Common utilities for managing junctions on the local file system ++ */ ++ ++/* ++ * Copyright 2010, 2018 Oracle. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2.0 as ++ * published by the Free Software Foundation. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License version 2.0 for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * version 2.0 along with nfs-utils. If not, see: ++ * ++ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "junction.h" ++#include "junction-internal.h" ++#include "xlog.h" ++ ++/** ++ * Open a file system object ++ * ++ * @param pathname NUL-terminated C string containing pathname of an object ++ * @param fd OUT: a file descriptor number is filled in ++ * @return a FedFsStatus code ++ */ ++FedFsStatus ++junction_open_path(const char *pathname, int *fd) ++{ ++ int tmp; ++ ++ if (pathname == NULL || fd == NULL) ++ return FEDFS_ERR_INVAL; ++ ++ tmp = open(pathname, O_DIRECTORY); ++ if (tmp == -1) { ++ switch (errno) { ++ case EPERM: ++ return FEDFS_ERR_ACCESS; ++ case EACCES: ++ return FEDFS_ERR_PERM; ++ default: ++ xlog(D_GENERAL, "%s: Failed to open path %s: %m", ++ __func__, pathname); ++ return FEDFS_ERR_INVAL; ++ } ++ } ++ ++ *fd = tmp; ++ return FEDFS_OK; ++} ++ ++/** ++ * Predicate: is object a directory? ++ * ++ * @param fd an open file descriptor ++ * @param path NUL-terminated C string containing pathname of a directory ++ * @return a FedFsStatus code ++ */ ++FedFsStatus ++junction_is_directory(int fd, const char *path) ++{ ++ struct stat stb; ++ ++ if (fstat(fd, &stb) == -1) { ++ xlog(D_GENERAL, "%s: failed to stat %s: %m", ++ __func__, path); ++ return FEDFS_ERR_ACCESS; ++ } ++ ++ if (!S_ISDIR(stb.st_mode)) { ++ xlog(D_CALL, "%s: %s is not a directory", ++ __func__, path); ++ return FEDFS_ERR_INVAL; ++ } ++ ++ xlog(D_CALL, "%s: %s is a directory", __func__, path); ++ return FEDFS_OK; ++} ++ ++/** ++ * Predicate: is a directory's sticky bit set? ++ * ++ * @param fd an open file descriptor ++ * @param path NUL-terminated C string containing pathname of a directory ++ * @return a FedFsStatus code ++ */ ++FedFsStatus ++junction_is_sticky_bit_set(int fd, const char *path) ++{ ++ struct stat stb; ++ ++ if (fstat(fd, &stb) == -1) { ++ xlog(D_GENERAL, "%s: failed to stat %s: %m", ++ __func__, path); ++ return FEDFS_ERR_ACCESS; ++ } ++ ++ if (stb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) { ++ xlog(D_CALL, "%s: execute bit set on %s", ++ __func__, path); ++ return FEDFS_ERR_NOTJUNCT; ++ } ++ ++ if (!(stb.st_mode & S_ISVTX)) { ++ xlog(D_CALL, "%s: sticky bit not set on %s", ++ __func__, path); ++ return FEDFS_ERR_NOTJUNCT; ++ } ++ ++ xlog(D_CALL, "%s: sticky bit is set on %s", __func__, path); ++ return FEDFS_OK; ++} ++ ++/** ++ * Set just a directory's sticky bit ++ * ++ * @param fd an open file descriptor ++ * @param path NUL-terminated C string containing pathname of a directory ++ * @return a FedFsStatus code ++ */ ++FedFsStatus ++junction_set_sticky_bit(int fd, const char *path) ++{ ++ struct stat stb; ++ ++ if (fstat(fd, &stb) == -1) { ++ xlog(D_GENERAL, "%s: failed to stat %s: %m", ++ __func__, path); ++ return FEDFS_ERR_ACCESS; ++ } ++ ++ stb.st_mode &= (unsigned int)~ALLPERMS; ++ stb.st_mode |= S_ISVTX; ++ ++ if (fchmod(fd, stb.st_mode) == -1) { ++ xlog(D_GENERAL, "%s: failed to set sticky bit on %s: %m", ++ __func__, path); ++ return FEDFS_ERR_ROFS; ++ } ++ ++ xlog(D_CALL, "%s: set sticky bit on %s", __func__, path); ++ return FEDFS_OK; ++} ++ ++/** ++ * Predicate: does a directory have an xattr named "name"? ++ * ++ * @param fd an open file descriptor ++ * @param path NUL-terminated C string containing pathname of a directory ++ * @param name NUL-terminated C string containing name of xattr to check ++ * @return a FedFsStatus code ++ * ++ * @note Access to trusted attributes requires CAP_SYS_ADMIN. ++ */ ++FedFsStatus ++junction_is_xattr_present(int fd, const char *path, const char *name) ++{ ++ ssize_t rc; ++ ++ /* ++ * Do not assume the total number of extended attributes ++ * this object may have. ++ */ ++ rc = fgetxattr(fd, name, NULL, 0); ++ if (rc == -1) { ++ switch (errno) { ++ case EPERM: ++ xlog(D_CALL, "%s: no access to xattr %s on %s", ++ __func__, name, path); ++ return FEDFS_ERR_PERM; ++ case ENODATA: ++ xlog(D_CALL, "%s: no xattr %s present on %s", ++ __func__, name, path); ++ return FEDFS_ERR_NOTJUNCT; ++ default: ++ xlog(D_CALL, "%s: xattr %s not found on %s: %m", ++ __func__, name, path); ++ return FEDFS_ERR_IO; ++ } ++ } ++ ++ xlog(D_CALL, "%s: xattr %s found on %s", ++ __func__, name, path); ++ return FEDFS_OK; ++} ++ ++/** ++ * Read the contents of xattr "name" ++ * ++ * @param fd an open file descriptor ++ * @param path NUL-terminated C string containing pathname of a directory ++ * @param name NUL-terminated C string containing name of xattr to retrieve ++ * @param contents OUT: NUL-terminated C string containing contents of xattr ++ * @return a FedFsStatus code ++ * ++ * If junction_read_xattr() returns FEDFS_OK, the caller must free "*contents" ++ * with free(3). ++ * ++ * @note Access to trusted attributes requires CAP_SYS_ADMIN. ++ */ ++FedFsStatus ++junction_read_xattr(int fd, const char *path, const char *name, char **contents) ++{ ++ char *xattrbuf = NULL; ++ ssize_t len; ++ ++ len = fgetxattr(fd, name, xattrbuf, 0); ++ if (len < 0) { ++ xlog(D_GENERAL, "%s: failed to get size of xattr %s on %s: %m", ++ __func__, name, path); ++ return FEDFS_ERR_ACCESS; ++ } ++ ++ xattrbuf = malloc((size_t)len + 1); ++ if (xattrbuf == NULL) { ++ xlog(D_GENERAL, "%s: failed to get buffer for xattr %s on %s", ++ __func__, name, path); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ if (fgetxattr(fd, name, xattrbuf, (size_t)len) == -1) { ++ xlog(D_GENERAL, "%s: failed to get xattr %s on %s: %m", ++ __func__, name, path); ++ free(xattrbuf); ++ return FEDFS_ERR_ACCESS; ++ } ++ xattrbuf[len] = '\0'; ++ ++ xlog(D_CALL, "%s: read xattr %s from path %s", ++ __func__, name, path); ++ *contents = xattrbuf; ++ return FEDFS_OK; ++} ++ ++/** ++ * Retrieve the contents of xattr "name" ++ * ++ * @param fd an open file descriptor ++ * @param path NUL-terminated C string containing pathname of a directory ++ * @param name NUL-terminated C string containing name of xattr to retrieve ++ * @param contents OUT: opaque byte array containing contents of xattr ++ * @param contentlen OUT: size of "contents" ++ * @return a FedFsStatus code ++ * ++ * If junction_get_xattr() returns FEDFS_OK, the caller must free "*contents" ++ * with free(3). ++ * ++ * @note Access to trusted attributes requires CAP_SYS_ADMIN. ++ */ ++FedFsStatus ++junction_get_xattr(int fd, const char *path, const char *name, void **contents, ++ size_t *contentlen) ++{ ++ void *xattrbuf = NULL; ++ ssize_t len; ++ ++ len = fgetxattr(fd, name, xattrbuf, 0); ++ if (len < 0) { ++ xlog(D_GENERAL, "%s: failed to get size of xattr %s on %s: %m", ++ __func__, name, path); ++ return FEDFS_ERR_ACCESS; ++ } ++ ++ xattrbuf = malloc((size_t)len); ++ if (xattrbuf == NULL) { ++ xlog(D_GENERAL, "%s: failed to get buffer for xattr %s on %s", ++ __func__, name, path); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ if (fgetxattr(fd, name, xattrbuf, (size_t)len) == -1) { ++ xlog(D_GENERAL, "%s: failed to get xattr %s on %s: %m", ++ __func__, name, path); ++ free(xattrbuf); ++ return FEDFS_ERR_ACCESS; ++ } ++ ++ xlog(D_CALL, "%s: read xattr %s from path %s", ++ __func__, name, path); ++ *contents = xattrbuf; ++ *contentlen = (size_t)len; ++ return FEDFS_OK; ++} ++ ++/** ++ * Update the contents of an xattr ++ * ++ * @param fd an open file descriptor ++ * @param path NUL-terminated C string containing pathname of a directory ++ * @param name NUL-terminated C string containing name of xattr to set ++ * @param contents opaque byte array containing contents of xattr ++ * @param contentlen size of "contents" ++ * @return a FedFsStatus code ++ * ++ * The extended attribute is created if it does not exist. ++ * Its contents are replaced if it does. ++ * ++ * @note Access to trusted attributes requires CAP_SYS_ADMIN. ++ */ ++FedFsStatus ++junction_set_xattr(int fd, const char *path, const char *name, ++ const void *contents, const size_t contentlen) ++{ ++ /* ++ * XXX: Eventually should distinguish among several errors: ++ * object isn't there, no root access, some other issue ++ */ ++ if (fsetxattr(fd, name, contents, contentlen, 0) == -1) { ++ xlog(D_GENERAL, "%s: Failed to set xattr %s on %s: %m", ++ __func__, name, path); ++ return FEDFS_ERR_IO; ++ } ++ ++ xlog(D_CALL, "%s: Wrote xattr %s from path %s", ++ __func__, name, path); ++ return FEDFS_OK; ++} ++ ++/** ++ * Remove one xattr ++ * ++ * @param fd an open file descriptor ++ * @param pathname NUL-terminated C string containing pathname of a directory ++ * @param name NUL-terminated C string containing name of xattr to set ++ * @return a FedFsStatus code ++ * ++ * @note Access to trusted attributes requires CAP_SYS_ADMIN. ++ */ ++FedFsStatus ++junction_remove_xattr(int fd, const char *pathname, const char *name) ++{ ++ /* ++ * XXX: Eventually should distinguish among several errors: ++ * object isn't there, no root access, some other issue ++ */ ++ if (fremovexattr(fd, name) == -1) { ++ xlog(D_GENERAL, "%s: failed to remove xattr %s from %s: %m", ++ __func__, name, pathname); ++ return FEDFS_ERR_ACCESS; ++ } ++ xlog(D_CALL, "%s: removed xattr %s from path %s", ++ __func__, name, pathname); ++ return FEDFS_OK; ++} ++ ++/** ++ * Retrieve object's mode bits. ++ * ++ * @param pathname NUL-terminated C string containing pathname of a directory ++ * @param mode OUT: mode bits ++ * @return a FedFsStatus code ++ */ ++FedFsStatus ++junction_get_mode(const char *pathname, mode_t *mode) ++{ ++ FedFsStatus retval; ++ struct stat stb; ++ int fd; ++ ++ retval = junction_open_path(pathname, &fd); ++ if (retval != FEDFS_OK) ++ return retval; ++ ++ if (fstat(fd, &stb) == -1) { ++ xlog(D_GENERAL, "%s: failed to stat %s: %m", ++ __func__, pathname); ++ (void)close(fd); ++ return FEDFS_ERR_ACCESS; ++ } ++ (void)close(fd); ++ ++ xlog(D_CALL, "%s: pathname %s has mode %o", ++ __func__, pathname, stb.st_mode); ++ *mode = stb.st_mode; ++ return FEDFS_OK; ++ ++} ++ ++/** ++ * Save the object's mode in an xattr. Saved mode is human-readable. ++ * ++ * @param pathname NUL-terminated C string containing pathname of a directory ++ * @return a FedFsStatus code ++ */ ++FedFsStatus ++junction_save_mode(const char *pathname) ++{ ++ FedFsStatus retval; ++ mode_t mode; ++ char buf[8]; ++ int fd; ++ ++ retval = junction_get_mode(pathname, &mode); ++ if (retval != FEDFS_OK) ++ return retval; ++ (void)snprintf(buf, sizeof(buf), "%o", ALLPERMS & mode); ++ ++ retval = junction_open_path(pathname, &fd); ++ if (retval != FEDFS_OK) ++ return retval; ++ ++ retval = junction_set_xattr(fd, pathname, JUNCTION_XATTR_NAME_MODE, ++ buf, strlen(buf)); ++ if (retval != FEDFS_OK) ++ goto out; ++ ++ retval = junction_set_sticky_bit(fd, pathname); ++ if (retval != FEDFS_OK) { ++ (void)junction_remove_xattr(fd, pathname, ++ JUNCTION_XATTR_NAME_MODE); ++ goto out; ++ } ++ ++ xlog(D_CALL, "%s: saved mode %o to %s", __func__, mode, pathname); ++ retval = FEDFS_OK; ++ ++out: ++ (void)close(fd); ++ return retval; ++ ++} ++ ++/** ++ * Restore an object's mode bits ++ * ++ * @param pathname NUL-terminated C string containing pathname of a directory ++ * @return a FedFsStatus code ++ */ ++FedFsStatus ++junction_restore_mode(const char *pathname) ++{ ++ FedFsStatus retval; ++ char *buf = NULL; ++ mode_t mode; ++ int fd; ++ ++ retval = junction_open_path(pathname, &fd); ++ if (retval != FEDFS_OK) ++ return retval; ++ ++ retval = junction_read_xattr(fd, pathname, JUNCTION_XATTR_NAME_MODE, &buf); ++ if (retval != FEDFS_OK) ++ goto out; ++ ++ retval = FEDFS_ERR_SVRFAULT; ++ if (sscanf((char *)buf, "%o", &mode) != 1) { ++ xlog(D_GENERAL, "%s: failed to parse saved mode on %s", ++ __func__, pathname); ++ goto out; ++ } ++ ++ retval = FEDFS_ERR_ROFS; ++ if (fchmod(fd, mode) == -1) { ++ xlog(D_GENERAL, "%s: failed to set mode of %s to %o: %m", ++ __func__, pathname, mode); ++ goto out; ++ } ++ ++ xlog(D_CALL, "%s: restored mode %o to %s", __func__, mode, pathname); ++ retval = FEDFS_OK; ++ ++out: ++ free(buf); ++ (void)close(fd); ++ return retval; ++} +diff --git a/support/junction/locations.c b/support/junction/locations.c +new file mode 100644 +index 0000000..c577981 +--- /dev/null ++++ b/support/junction/locations.c +@@ -0,0 +1,131 @@ ++/** ++ * @file support/junction/locations.c ++ * @brief Utility functions to manage NFS locations data ++ */ ++ ++/* ++ * Copyright 2011, 2018 Oracle. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2.0 as ++ * published by the Free Software Foundation. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License version 2.0 for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * version 2.0 along with nfs-utils. If not, see: ++ * ++ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "junction.h" ++ ++/** ++ * Free an array of NUL-terminated C strings ++ * ++ * @param array array of pointers to C strings ++ */ ++void ++nfs_free_string_array(char **array) ++{ ++ unsigned int i; ++ ++ if (array == NULL) ++ return; ++ for (i = 0; array[i] != NULL; i++) ++ free(array[i]); ++ free(array); ++} ++ ++/** ++ * Duplicate an array of NUL-terminated C strings ++ * ++ * @param array array of pointers to C strings ++ * @return freshly allocated array of points to C strings, or NULL ++ * ++ * Caller must free the returned array with nfs_free_string_array() ++ */ ++__attribute_malloc__ char ** ++nfs_dup_string_array(char **array) ++{ ++ unsigned int size, i; ++ char **result; ++ ++ if (array == NULL) ++ return NULL; ++ ++ for (size = 0; array[size] != NULL; size++); ++ ++ result = calloc(size + 1, sizeof(char *)); ++ if (result == NULL) ++ return NULL; ++ for (i = 0; i < size; i++) { ++ result[i] = strdup(array[i]); ++ if (result[i] == NULL) { ++ nfs_free_string_array(result); ++ return NULL; ++ } ++ } ++ return result; ++} ++ ++/** ++ * Free a single NFS location ++ * ++ * @param location pointer to nfs_fsloc data ++ */ ++void ++nfs_free_location(struct nfs_fsloc *location) ++{ ++ nfs_free_string_array(location->nfl_rootpath); ++ free(location->nfl_hostname); ++ free(location); ++} ++ ++/** ++ * Free a list of NFS locations ++ * ++ * @param locations pointer to list of one or more locations ++ */ ++void ++nfs_free_locations(struct nfs_fsloc *locations) ++{ ++ struct nfs_fsloc *fsloc; ++ ++ while (locations != NULL) { ++ fsloc = locations; ++ locations = fsloc->nfl_next; ++ nfs_free_location(fsloc); ++ } ++} ++ ++/** ++ * Allocate a fresh nfs_fsloc structure ++ * ++ * @return pointer to new empty nfs_fsloc data structure ++ * ++ * Caller must free returned locations with nfs_free_location(). ++ */ ++struct nfs_fsloc * ++nfs_new_location(void) ++{ ++ return calloc(1, sizeof(struct nfs_fsloc)); ++} +diff --git a/support/junction/nfs.c b/support/junction/nfs.c +new file mode 100644 +index 0000000..73e3533 +--- /dev/null ++++ b/support/junction/nfs.c +@@ -0,0 +1,1564 @@ ++/** ++ * @file support/junction/nfs.c ++ * @brief Create, delete, and read NFS junctions on the local file system ++ */ ++ ++/* ++ * Copyright 2011, 2018 Oracle. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2.0 as ++ * published by the Free Software Foundation. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License version 2.0 for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * version 2.0 along with nfs-utils. If not, see: ++ * ++ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++ */ ++ ++/* ++ * An NFS junction is a list of NFS FSLs, represented in a well-formed XML ++ * document: ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * foo ++ * bar ++ * baz ++ * ++ * -1 ++ * ++ * ++ * ++ * ++ * ++ * ++ * 0 ++ * ++ * ++ * .... ++ * ++ * ++ * ++ * ++ * NFS junction XML is stored in an extended attribute called ++ * "trusted.junction.nfs". The parent object is a directory. ++ * ++ * To help file servers discover junctions efficiently, the directory ++ * has no execute bits, and the sticky bit is set. In addition, an ++ * extended attribute called "trusted.junction.type" is added. The ++ * contents are ignored in user space. ++ * ++ * Finally, for pre-existing directories that are converted to ++ * junctions, their mode bits are saved in an extended attribute called ++ * "trusted.junction.mode". When the junction data is removed, the ++ * directory's mode bits are restored from this information. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "junction.h" ++#include "junction-internal.h" ++#include "xlog.h" ++ ++/** ++ * Tag name of NFS location element of a junction XML document ++ */ ++#define NFS_XML_LOCATION_TAG (const xmlChar *)"location" ++ ++/** ++ * Tag name of host child element of an NFS location element ++ */ ++#define NFS_XML_HOST_TAG (const xmlChar *)"host" ++ ++/** ++ * Name of hostname attribute of a host element ++ */ ++#define NFS_XML_HOST_NAME_ATTR (const xmlChar *)"name" ++ ++/** ++ * Name of IP port attribute of a host element ++ */ ++#define NFS_XML_HOST_PORT_ATTR (const xmlChar *)"port" ++ ++/** ++ * Tag name of path child element of an NFS location element ++ */ ++#define NFS_XML_PATH_TAG (const xmlChar *)"path" ++ ++/** ++ * Tag name of component child element of a path element ++ */ ++#define NFS_XML_COMPONENT_TAG (const xmlChar *)"component" ++ ++/** ++ * Tag name of currency child element of an NFS location element ++ */ ++#define NFS_XML_CURRENCY_TAG (const xmlChar *)"currency" ++ ++/** ++ * Tag name of genflags child element of an NFS location element ++ */ ++#define NFS_XML_GENFLAGS_TAG (const xmlChar *)"genflags" ++ ++/** ++ * Name of writable attribute of a genflags element ++ */ ++#define NFS_XML_GENFLAGS_WRITABLE_ATTR (const xmlChar *)"writable" ++ ++/** ++ * Name of going attribute of a genflags element ++ */ ++#define NFS_XML_GENFLAGS_GOING_ATTR (const xmlChar *)"going" ++ ++/** ++ * Name of split attribute of a genflags element ++ */ ++#define NFS_XML_GENFLAGS_SPLIT_ATTR (const xmlChar *)"split" ++ ++/** ++ * Tag name of transflags child element of an NFS location element ++ */ ++#define NFS_XML_TRANSFLAGS_TAG (const xmlChar *)"transflags" ++ ++/** ++ * Name of rdma attribute of a transflags element ++ */ ++#define NFS_XML_TRANSFLAGS_RDMA_ATTR (const xmlChar *)"rdma" ++ ++/** ++ * Tag name of class child element of an NFS location element ++ */ ++#define NFS_XML_CLASS_TAG (const xmlChar *)"class" ++ ++/** ++ * Name of simul attribute of a class element ++ */ ++#define NFS_XML_CLASS_SIMUL_ATTR (const xmlChar *)"simul" ++ ++/** ++ * Name of handle attribute of a class element ++ */ ++#define NFS_XML_CLASS_HANDLE_ATTR (const xmlChar *)"handle" ++ ++/** ++ * Name of fileid attribute of a class element ++ */ ++#define NFS_XML_CLASS_FILEID_ATTR (const xmlChar *)"fileid" ++ ++/** ++ * Name of writever attribute of a class element ++ */ ++#define NFS_XML_CLASS_WRITEVER_ATTR (const xmlChar *)"writever" ++ ++/** ++ * Name of change attribute of a class element ++ */ ++#define NFS_XML_CLASS_CHANGE_ATTR (const xmlChar *)"change" ++ ++/** ++ * Name of readdir attribute of a class element ++ */ ++#define NFS_XML_CLASS_READDIR_ATTR (const xmlChar *)"readdir" ++ ++/** ++ * Tag name of read child element of an NFS location element ++ */ ++#define NFS_XML_READ_TAG (const xmlChar *)"read" ++ ++/** ++ * Name of rank attribute of a read element ++ */ ++#define NFS_XML_READ_RANK_ATTR (const xmlChar *)"rank" ++ ++/** ++ * Name of order attribute of a read element ++ */ ++#define NFS_XML_READ_ORDER_ATTR (const xmlChar *)"order" ++ ++/** ++ * Tag name of write attribute of an NFS location element ++ */ ++#define NFS_XML_WRITE_TAG (const xmlChar *)"write" ++ ++/** ++ * Name of rank attribute of a write element ++ */ ++#define NFS_XML_WRITE_RANK_ATTR (const xmlChar *)"rank" ++ ++/** ++ * Name of order attribute of a write element ++ */ ++#define NFS_XML_WRITE_ORDER_ATTR (const xmlChar *)"order" ++ ++/** ++ * Tag name of flags child element of an NFS location element ++ */ ++#define NFS_XML_FLAGS_TAG (const xmlChar *)"flags" ++ ++/** ++ * Name of varsub attribute of a flags element ++ */ ++#define NFS_XML_FLAGS_VARSUB_ATTR (const xmlChar *)"varsub" ++ ++/** ++ * Tag name of a validfor child element of an NFS location element ++ */ ++#define NFS_XML_VALIDFOR_TAG (const xmlChar *)"validfor" ++ ++/** ++ * XPath path to NFS location elements in a junction document ++ */ ++#define NFS_XML_LOCATION_XPATH (const xmlChar *) \ ++ "/junction/fileset/location" ++ ++ ++/** ++ * Remove all NFS-related xattrs from a directory ++ * ++ * @param pathname NUL-terminated C string containing pathname of a directory ++ * @return a FedFsStatus code ++ * ++ * @note Access to trusted attributes requires CAP_SYS_ADMIN. ++ */ ++static FedFsStatus ++nfs_remove_locations(const char *pathname) ++{ ++ FedFsStatus retval; ++ int fd; ++ ++ retval = junction_open_path(pathname, &fd); ++ if (retval != FEDFS_OK) ++ return retval; ++ ++ retval = junction_remove_xattr(fd, pathname, JUNCTION_XATTR_NAME_NFS); ++ ++ (void)close(fd); ++ return retval; ++} ++ ++/** ++ * Add a "host" child to a "location" element ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param parent parent element to which to add "host" child ++ * @param fsloc NFS location containing host information to add ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_location_host_xml(const char *pathname, xmlNodePtr parent, ++ struct nfs_fsloc *fsloc) ++{ ++ uint16_t port = fsloc->nfl_hostport; ++ xmlNodePtr new; ++ ++ new = xmlNewTextChild(parent, NULL, NFS_XML_HOST_TAG, NULL); ++ if (new == NULL) { ++ xlog(D_GENERAL, "%s: Failed to add host element for %s", ++ __func__, pathname); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ xmlSetProp(new, NFS_XML_HOST_NAME_ATTR, ++ (const xmlChar *)fsloc->nfl_hostname); ++ if (port != NFS_PORT && port != 0) ++ junction_xml_set_int_attribute(new, NFS_XML_HOST_PORT_ATTR, ++ port); ++ ++ return FEDFS_OK; ++} ++ ++/** ++ * Add a "path" child to a "location" element ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param parent parent element to which to add "host" child ++ * @param fsloc NFS location containing host information to add ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_location_path_xml(const char *pathname, xmlNodePtr parent, ++ struct nfs_fsloc *fsloc) ++{ ++ xmlNodePtr new; ++ int i; ++ ++ new = xmlNewTextChild(parent, NULL, NFS_XML_PATH_TAG, NULL); ++ if (new == NULL) { ++ xlog(D_GENERAL, "%s: Failed to add path element for %s", ++ __func__, pathname); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ for (i = 0; fsloc->nfl_rootpath[i] != NULL; i++) { ++ xmlNodePtr component; ++ ++ component = xmlNewTextChild(new , NULL, ++ NFS_XML_COMPONENT_TAG, ++ (const xmlChar *) ++ fsloc->nfl_rootpath[i]); ++ if (component == NULL) { ++ xlog(D_GENERAL, "%s: Failed to add component " ++ "element for %s", ++ __func__, pathname); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ } ++ ++ return FEDFS_OK; ++} ++ ++/** ++ * Add a "currency" child to a "location" element ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param parent parent element to which to add "host" child ++ * @param fsloc NFS location containing host information to add ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_location_currency_xml(__attribute__((unused)) const char *pathname, ++ xmlNodePtr parent, struct nfs_fsloc *fsloc) ++{ ++ if (junction_xml_set_int_content(parent, NFS_XML_CURRENCY_TAG, ++ fsloc->nfl_currency) == NULL) ++ return FEDFS_ERR_SVRFAULT; ++ return FEDFS_OK; ++} ++ ++/** ++ * Add a "genflags" child to a "location" element ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param parent parent element to which to add "host" child ++ * @param fsloc NFS location containing host information to add ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_location_genflags_xml(const char *pathname, xmlNodePtr parent, ++ struct nfs_fsloc *fsloc) ++{ ++ xmlNodePtr new; ++ ++ new = xmlNewTextChild(parent, NULL, NFS_XML_GENFLAGS_TAG, NULL); ++ if (new == NULL) { ++ xlog(D_GENERAL, "%s: Failed to add genflags element for %s", ++ __func__, pathname); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ junction_xml_set_bool_attribute(new, NFS_XML_GENFLAGS_WRITABLE_ATTR, ++ fsloc->nfl_genflags.nfl_writable); ++ junction_xml_set_bool_attribute(new, NFS_XML_GENFLAGS_GOING_ATTR, ++ fsloc->nfl_genflags.nfl_going); ++ junction_xml_set_bool_attribute(new, NFS_XML_GENFLAGS_SPLIT_ATTR, ++ fsloc->nfl_genflags.nfl_split); ++ ++ return FEDFS_OK; ++} ++ ++/** ++ * Add a "transflags" child to a "location" element ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param parent parent element to which to add "host" child ++ * @param fsloc NFS location containing host information to add ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_location_transflags_xml(const char *pathname, xmlNodePtr parent, ++ struct nfs_fsloc *fsloc) ++{ ++ xmlNodePtr new; ++ ++ new = xmlNewTextChild(parent, NULL, NFS_XML_TRANSFLAGS_TAG, NULL); ++ if (new == NULL) { ++ xlog(D_GENERAL, "%s: Failed to add transflags element for %s", ++ __func__, pathname); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ junction_xml_set_bool_attribute(new, NFS_XML_TRANSFLAGS_RDMA_ATTR, ++ fsloc->nfl_transflags.nfl_rdma); ++ ++ return FEDFS_OK; ++} ++ ++/** ++ * Add a "class" child to a "location" element ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param parent parent element to which to add "host" child ++ * @param fsloc NFS location containing host information to add ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_location_class_xml(const char *pathname, xmlNodePtr parent, ++ struct nfs_fsloc *fsloc) ++{ ++ xmlNodePtr new; ++ ++ new = xmlNewTextChild(parent, NULL, NFS_XML_CLASS_TAG, NULL); ++ if (new == NULL) { ++ xlog(D_GENERAL, "%s: Failed to add class element for %s", ++ __func__, pathname); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ junction_xml_set_int_attribute(new, NFS_XML_CLASS_SIMUL_ATTR, ++ fsloc->nfl_info.nfl_simul); ++ junction_xml_set_int_attribute(new, NFS_XML_CLASS_HANDLE_ATTR, ++ fsloc->nfl_info.nfl_handle); ++ junction_xml_set_int_attribute(new, NFS_XML_CLASS_FILEID_ATTR, ++ fsloc->nfl_info.nfl_fileid); ++ junction_xml_set_int_attribute(new, NFS_XML_CLASS_WRITEVER_ATTR, ++ fsloc->nfl_info.nfl_writever); ++ junction_xml_set_int_attribute(new, NFS_XML_CLASS_CHANGE_ATTR, ++ fsloc->nfl_info.nfl_change); ++ junction_xml_set_int_attribute(new, NFS_XML_CLASS_READDIR_ATTR, ++ fsloc->nfl_info.nfl_readdir); ++ ++ return FEDFS_OK; ++} ++ ++/** ++ * Add a "read" child to a "location" element ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param parent parent element to which to add "host" child ++ * @param fsloc NFS location containing host information to add ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_location_read_xml(const char *pathname, xmlNodePtr parent, ++ struct nfs_fsloc *fsloc) ++{ ++ xmlNodePtr new; ++ ++ new = xmlNewTextChild(parent, NULL, NFS_XML_READ_TAG, NULL); ++ if (new == NULL) { ++ xlog(D_GENERAL, "%s: Failed to add read element for %s", ++ __func__, pathname); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ junction_xml_set_int_attribute(new, NFS_XML_READ_RANK_ATTR, ++ fsloc->nfl_info.nfl_readrank); ++ junction_xml_set_int_attribute(new, NFS_XML_READ_ORDER_ATTR, ++ fsloc->nfl_info.nfl_readorder); ++ ++ return FEDFS_OK; ++} ++ ++/** ++ * Add a "write" child to a "location" element ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param parent parent element to which to add "host" child ++ * @param fsloc NFS location containing host information to add ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_location_write_xml(const char *pathname, xmlNodePtr parent, ++ struct nfs_fsloc *fsloc) ++{ ++ xmlNodePtr new; ++ ++ new = xmlNewTextChild(parent, NULL, NFS_XML_WRITE_TAG, NULL); ++ if (new == NULL) { ++ xlog(D_GENERAL, "%s: Failed to add write element for %s", ++ __func__, pathname); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ junction_xml_set_int_attribute(new, NFS_XML_WRITE_RANK_ATTR, ++ fsloc->nfl_info.nfl_writerank); ++ junction_xml_set_int_attribute(new, NFS_XML_WRITE_ORDER_ATTR, ++ fsloc->nfl_info.nfl_writeorder); ++ ++ return FEDFS_OK; ++} ++ ++/** ++ * Add a "flags" child to a "location" element ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param parent parent element to which to add "host" child ++ * @param fsloc NFS location containing host information to add ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_location_flags_xml(const char *pathname, xmlNodePtr parent, ++ struct nfs_fsloc *fsloc) ++{ ++ xmlNodePtr new; ++ ++ new = xmlNewTextChild(parent, NULL, NFS_XML_FLAGS_TAG, NULL); ++ if (new == NULL) { ++ xlog(D_GENERAL, "%s: Failed to add flags element for %s", ++ __func__, pathname); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ junction_xml_set_bool_attribute(new, NFS_XML_FLAGS_VARSUB_ATTR, ++ fsloc->nfl_flags.nfl_varsub); ++ ++ return FEDFS_OK; ++} ++ ++/** ++ * Add a "validfor" child to a "location" element ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param parent parent element to which to add "host" child ++ * @param fsloc NFS location containing host information to add ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_location_validfor_xml(__attribute__((unused)) const char *pathname, ++ xmlNodePtr parent, struct nfs_fsloc *fsloc) ++{ ++ if (junction_xml_set_int_content(parent, NFS_XML_VALIDFOR_TAG, ++ fsloc->nfl_validfor) == NULL) ++ return FEDFS_ERR_SVRFAULT; ++ return FEDFS_OK; ++} ++ ++/** ++ * Construct and add one "location" element to a "fileset" ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param fileset fileset element of junction XML parse tree ++ * @param fsloc one NFS location to add ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_location_xml(const char *pathname, xmlNodePtr fileset, ++ struct nfs_fsloc *fsloc) ++{ ++ FedFsStatus retval; ++ xmlNodePtr new; ++ ++ new = xmlNewTextChild(fileset, NULL, NFS_XML_LOCATION_TAG, NULL); ++ if (new == NULL) { ++ xlog(D_GENERAL, "%s: Failed to add location element for %s", ++ __func__, pathname); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ retval = nfs_location_host_xml(pathname, new, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ retval = nfs_location_path_xml(pathname, new, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ retval = nfs_location_currency_xml(pathname, new, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ retval = nfs_location_genflags_xml(pathname, new, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ retval = nfs_location_transflags_xml(pathname, new, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ retval = nfs_location_class_xml(pathname, new, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ retval = nfs_location_read_xml(pathname, new, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ retval = nfs_location_write_xml(pathname, new, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ retval = nfs_location_flags_xml(pathname, new, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ return nfs_location_validfor_xml(pathname, new, fsloc); ++} ++ ++/** ++ * Construct and add a "fileset" element ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param root root element of junction XML parse tree ++ * @param fslocs list of NFS locations to add ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_fileset_xml(const char *pathname, xmlNodePtr root, ++ struct nfs_fsloc *fslocs) ++{ ++ struct nfs_fsloc *next; ++ xmlNodePtr fileset; ++ FedFsStatus retval; ++ ++ fileset = xmlNewTextChild(root, NULL, JUNCTION_XML_FILESET_TAG, NULL); ++ if (fileset == NULL) { ++ xlog(D_GENERAL, "%s: Failed to add fileset element for %s", ++ __func__, pathname); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ for (next = fslocs; next != NULL; next = next->nfl_next) { ++ retval = nfs_location_xml(pathname, fileset, next); ++ if (retval != FEDFS_OK) ++ return retval; ++ } ++ ++ return FEDFS_OK; ++} ++ ++/** ++ * Construct a "savedmode" element ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param root root element of XML document tree ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_savedmode_xml(const char *pathname, xmlNodePtr root) ++{ ++ xmlNodePtr savedmode; ++ FedFsStatus retval; ++ mode_t mode; ++ char buf[8]; ++ ++ retval = junction_get_mode(pathname, &mode); ++ if (retval != FEDFS_OK) ++ return retval; ++ ++ savedmode = xmlNewTextChild(root, NULL, JUNCTION_XML_SAVEDMODE_TAG, NULL); ++ if (savedmode == NULL) { ++ xlog(D_GENERAL, "%s: Failed to add savedmode element for %s\n", ++ __func__, pathname); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ (void)snprintf(buf, sizeof(buf), "%o", ALLPERMS & mode); ++ xmlSetProp(savedmode, JUNCTION_XML_MODEBITS_ATTR, (const xmlChar *)buf); ++ ++ return FEDFS_OK; ++} ++ ++/** ++ * Construct NFS junction XML document from list of NFS locations ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param doc an XML parse tree in which to construct the junction XML document ++ * @param fslocs list of NFS locations to add ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_junction_xml(const char *pathname, xmlDocPtr doc, ++ struct nfs_fsloc *fslocs) ++{ ++ FedFsStatus retval; ++ xmlNodePtr root; ++ ++ root = xmlNewNode(NULL, JUNCTION_XML_ROOT_TAG); ++ if (root == NULL) { ++ xlog(D_GENERAL, "%s: Failed to create root element for %s", ++ __func__, pathname); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ (void)xmlDocSetRootElement(doc, root); ++ ++ retval = nfs_savedmode_xml(pathname, root); ++ if (retval != FEDFS_OK) ++ return retval; ++ ++ return nfs_fileset_xml(pathname, root, fslocs); ++} ++ ++/** ++ * Write NFS locations information into an NFS junction extended attribute ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param doc an empty XML parse tree in which to construct the junction XML document ++ * @param fslocs list of NFS locations to add ++ * @return a FedFsStatus code ++ * ++ * @note Access to trusted attributes requires CAP_SYS_ADMIN. ++ */ ++static FedFsStatus ++nfs_write_junction(const char *pathname, xmlDocPtr doc, ++ struct nfs_fsloc *fslocs) ++{ ++ FedFsStatus retval; ++ ++ retval = nfs_junction_xml(pathname, doc, fslocs); ++ if (retval != FEDFS_OK) ++ return retval; ++ ++ return junction_xml_write(pathname, JUNCTION_XATTR_NAME_NFS, doc); ++} ++ ++/** ++ * Store NFS locations information into a junction object ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param fslocs list of NFS locations to add ++ * @return a FedFsStatus code ++ * ++ * @note Access to trusted attributes requires CAP_SYS_ADMIN. ++ */ ++static FedFsStatus ++nfs_store_locations(const char *pathname, struct nfs_fsloc *fslocs) ++{ ++ FedFsStatus retval; ++ xmlDocPtr doc; ++ ++ doc = xmlNewDoc((xmlChar *)"1.0"); ++ if (doc == NULL) { ++ xlog(D_GENERAL, "%s: Failed to create XML doc for %s", ++ __func__, pathname); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ retval = nfs_write_junction(pathname, doc, fslocs); ++ ++ xmlFreeDoc(doc); ++ return retval; ++} ++ ++/** ++ * Add NFS junction information to a pre-existing object ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param fslocs list of NFS locations to add ++ * @return a FedFsStatus code ++ * ++ * An error occurs if the object referred to by "pathname" does not ++ * exist or contains existing junction data. ++ */ ++FedFsStatus ++nfs_add_junction(const char *pathname, struct nfs_fsloc *fslocs) ++{ ++ FedFsStatus retval; ++ ++ if (fslocs == NULL) ++ return FEDFS_ERR_INVAL; ++ ++ retval = nfs_is_prejunction(pathname); ++ if (retval != FEDFS_ERR_NOTJUNCT) ++ return retval; ++ ++ retval = nfs_store_locations(pathname, fslocs); ++ if (retval != FEDFS_OK) ++ goto out_err; ++ ++ retval = junction_save_mode(pathname); ++ if (retval != FEDFS_OK) ++ goto out_err; ++ ++ return retval; ++ ++out_err: ++ (void)nfs_remove_locations(pathname); ++ return retval; ++} ++ ++/** ++ * Remove NFS junction information from an object ++ * ++ * @param pathname NUL-terminated C string containing pathname of a directory ++ * @return a FedFsStatus code ++ * ++ * An error occurs if the object referred to by "pathname" does not ++ * exist or does not contain NFS junction data. ++ */ ++FedFsStatus ++nfs_delete_junction(const char *pathname) ++{ ++ FedFsStatus retval; ++ ++ retval = nfs_is_junction(pathname); ++ if (retval != FEDFS_OK) ++ return retval; ++ ++ retval = junction_restore_mode(pathname); ++ if (retval != FEDFS_OK) ++ return retval; ++ ++ return nfs_remove_locations(pathname); ++} ++ ++/** ++ * Parse the first "host" child of "location" ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param location XML parse tree containing fileset location element ++ * @param fsloc a blank nfs_fsloc to fill in ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_parse_location_host(const char *pathname, xmlNodePtr location, ++ struct nfs_fsloc *fsloc) ++{ ++ FedFsStatus retval; ++ xmlChar *hostname; ++ xmlNodePtr node; ++ int hostport; ++ ++ retval = FEDFS_ERR_NOTJUNCT; ++ node = junction_xml_find_child_by_name(location, NFS_XML_HOST_TAG); ++ if (node == NULL) ++ return retval; ++ ++ hostname = xmlGetProp(node, NFS_XML_HOST_NAME_ATTR); ++ if (!junction_xml_get_int_attribute(node, NFS_XML_HOST_PORT_ATTR, ++ &hostport)) ++ fsloc->nfl_hostport = NFS_PORT; ++ else { ++ if (hostport < 1 || hostport > UINT16_MAX) { ++ xlog(D_GENERAL, "%s: Bad port attribute on %s", ++ __func__, pathname); ++ goto out; ++ } ++ fsloc->nfl_hostport = (uint16_t)hostport; ++ } ++ if (hostname == NULL) { ++ xlog(D_GENERAL, "%s: No hostname attribute on %s", ++ __func__, pathname); ++ goto out; ++ } ++ fsloc->nfl_hostname = strdup((const char *)hostname); ++ if (fsloc->nfl_hostname == NULL) { ++ retval = FEDFS_ERR_SVRFAULT; ++ goto out; ++ } ++ ++ retval = FEDFS_OK; ++ ++out: ++ xmlFree(hostname); ++ return retval; ++} ++ ++/** ++ * Parse the first "path" child of "location" into a path array ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param location XML parse tree containing fileset location element ++ * @param fsloc a blank nfs_fsloc to fill in ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_parse_location_path(const char *pathname, xmlNodePtr location, ++ struct nfs_fsloc *fsloc) ++{ ++ xmlNodePtr node, component; ++ unsigned int count; ++ xmlChar *value; ++ char **result; ++ ++ node = junction_xml_find_child_by_name(location, NFS_XML_PATH_TAG); ++ if (node == NULL) ++ return FEDFS_ERR_NOTJUNCT; ++ ++ count = 0; ++ for (component = node->children; ++ component != NULL; ++ component = component->next) { ++ if (!junction_xml_match_node_name(component, ++ NFS_XML_COMPONENT_TAG)) ++ continue; ++ value = xmlNodeGetContent(component); ++ if (junction_xml_is_empty(value)) { ++ xlog(D_GENERAL, "%s: Bad pathname component in %s", ++ __func__, pathname); ++ return FEDFS_ERR_NOTJUNCT; ++ } ++ xmlFree(value); ++ count++; ++ } ++ xlog(D_GENERAL, "%s: Found %u component(s)", __func__, count); ++ ++ if (count == 0) { ++ xlog(D_GENERAL, "%s: Zero-component pathname", __func__); ++ fsloc->nfl_rootpath = (char **)calloc(1, sizeof(char *)); ++ if (fsloc->nfl_rootpath == NULL) ++ return FEDFS_ERR_SVRFAULT; ++ fsloc->nfl_rootpath[0] = NULL; ++ return FEDFS_OK; ++ } ++ ++ result = calloc(count + 1, sizeof(char *)); ++ if (result == NULL) ++ return FEDFS_ERR_SVRFAULT; ++ ++ count = 0; ++ for (component = node->children; ++ component != NULL; ++ component = component->next) { ++ if (!junction_xml_match_node_name(component, ++ NFS_XML_COMPONENT_TAG)) ++ continue; ++ value = xmlNodeGetContent(component); ++ result[count] = strdup((const char *)value); ++ xmlFree(value); ++ if (result[count] == NULL) { ++ nfs_free_string_array(result); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ count++; ++ } ++ ++ fsloc->nfl_rootpath = result; ++ return FEDFS_OK; ++} ++ ++/** ++ * Parse the first "currency" child of "location" ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param location XML parse tree containing fileset location element ++ * @param fsloc a blank nfs_fsloc to fill in ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_parse_location_currency(const char *pathname, xmlNodePtr location, ++ struct nfs_fsloc *fsloc) ++{ ++ xmlNodePtr node; ++ ++ node = junction_xml_find_child_by_name(location, NFS_XML_CURRENCY_TAG); ++ if (node == NULL) ++ goto out_err; ++ ++ if (!junction_xml_get_int_content(node, &fsloc->nfl_currency)) ++ goto out_err; ++ ++ return FEDFS_OK; ++ ++out_err: ++ xlog(D_GENERAL, "%s: Missing or invalid currency element in %s", ++ __func__, pathname); ++ return FEDFS_ERR_NOTJUNCT; ++} ++ ++/** ++ * Parse the first "genflags" child of "location" ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param location XML parse tree containing fileset location element ++ * @param fsloc a blank nfs_fsloc to fill in ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_parse_location_genflags(const char *pathname, xmlNodePtr location, ++ struct nfs_fsloc *fsloc) ++{ ++ xmlNodePtr node; ++ ++ node = junction_xml_find_child_by_name(location, NFS_XML_GENFLAGS_TAG); ++ if (node == NULL) ++ goto out_err; ++ ++ if (!junction_xml_get_bool_attribute(node, ++ NFS_XML_GENFLAGS_WRITABLE_ATTR, ++ &fsloc->nfl_genflags.nfl_writable)) ++ goto out_err; ++ if (!junction_xml_get_bool_attribute(node, ++ NFS_XML_GENFLAGS_GOING_ATTR, ++ &fsloc->nfl_genflags.nfl_going)) ++ goto out_err; ++ if (!junction_xml_get_bool_attribute(node, ++ NFS_XML_GENFLAGS_SPLIT_ATTR, ++ &fsloc->nfl_genflags.nfl_split)) ++ goto out_err; ++ ++ return FEDFS_OK; ++ ++out_err: ++ xlog(D_GENERAL, "%s: Missing or invalid genflags element in %s", ++ __func__, pathname); ++ return FEDFS_ERR_NOTJUNCT; ++} ++ ++/** ++ * Parse the first "transflags" child of "location" ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param location XML parse tree containing fileset location element ++ * @param fsloc a blank nfs_fsloc to fill in ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_parse_location_transflags(const char *pathname, xmlNodePtr location, ++ struct nfs_fsloc *fsloc) ++{ ++ xmlNodePtr node; ++ ++ node = junction_xml_find_child_by_name(location, NFS_XML_TRANSFLAGS_TAG); ++ if (node == NULL) ++ goto out_err; ++ ++ if (!junction_xml_get_bool_attribute(node, ++ NFS_XML_TRANSFLAGS_RDMA_ATTR, ++ &fsloc->nfl_transflags.nfl_rdma)) ++ goto out_err; ++ ++ return FEDFS_OK; ++ ++out_err: ++ xlog(D_GENERAL, "%s: Missing or invalid transflags element in %s", ++ __func__, pathname); ++ return FEDFS_ERR_NOTJUNCT; ++} ++ ++/** ++ * Parse the first "class" child of "location" ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param location XML parse tree containing fileset location element ++ * @param fsloc a blank nfs_fsloc to fill in ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_parse_location_class(const char *pathname, xmlNodePtr location, ++ struct nfs_fsloc *fsloc) ++{ ++ xmlNodePtr node; ++ ++ node = junction_xml_find_child_by_name(location, NFS_XML_CLASS_TAG); ++ if (node == NULL) ++ goto out_err; ++ ++ if (!junction_xml_get_u8_attribute(node, ++ NFS_XML_CLASS_SIMUL_ATTR, ++ &fsloc->nfl_info.nfl_simul)) ++ goto out_err; ++ if (!junction_xml_get_u8_attribute(node, ++ NFS_XML_CLASS_HANDLE_ATTR, ++ &fsloc->nfl_info.nfl_handle)) ++ goto out_err; ++ if (!junction_xml_get_u8_attribute(node, ++ NFS_XML_CLASS_FILEID_ATTR, ++ &fsloc->nfl_info.nfl_fileid)) ++ goto out_err; ++ if (!junction_xml_get_u8_attribute(node, ++ NFS_XML_CLASS_WRITEVER_ATTR, ++ &fsloc->nfl_info.nfl_writever)) ++ goto out_err; ++ if (!junction_xml_get_u8_attribute(node, ++ NFS_XML_CLASS_WRITEVER_ATTR, ++ &fsloc->nfl_info.nfl_writever)) ++ goto out_err; ++ if (!junction_xml_get_u8_attribute(node, ++ NFS_XML_CLASS_CHANGE_ATTR, ++ &fsloc->nfl_info.nfl_change)) ++ goto out_err; ++ if (!junction_xml_get_u8_attribute(node, ++ NFS_XML_CLASS_READDIR_ATTR, ++ &fsloc->nfl_info.nfl_readdir)) ++ goto out_err; ++ ++ return FEDFS_OK; ++ ++out_err: ++ xlog(D_GENERAL, "%s: Missing or invalid class element in %s", ++ __func__, pathname); ++ return FEDFS_ERR_NOTJUNCT; ++} ++ ++/** ++ * Parse the first "read" child of "location" ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param location XML parse tree containing fileset location element ++ * @param fsloc a blank nfs_fsloc to fill in ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_parse_location_read(const char *pathname, xmlNodePtr location, ++ struct nfs_fsloc *fsloc) ++{ ++ xmlNodePtr node; ++ ++ node = junction_xml_find_child_by_name(location, NFS_XML_READ_TAG); ++ if (node == NULL) ++ goto out_err; ++ ++ if (!junction_xml_get_u8_attribute(node, ++ NFS_XML_READ_RANK_ATTR, ++ &fsloc->nfl_info.nfl_readrank)) ++ goto out_err; ++ if (!junction_xml_get_u8_attribute(node, ++ NFS_XML_READ_ORDER_ATTR, ++ &fsloc->nfl_info.nfl_readorder)) ++ goto out_err; ++ ++ return FEDFS_OK; ++ ++out_err: ++ xlog(D_GENERAL, "%s: Missing or invalid read element in %s", ++ __func__, pathname); ++ return FEDFS_ERR_NOTJUNCT; ++} ++ ++/** ++ * Parse the first "write" child of "location" ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param location XML parse tree containing fileset location element ++ * @param fsloc a blank nfs_fsloc to fill in ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_parse_location_write(const char *pathname, xmlNodePtr location, ++ struct nfs_fsloc *fsloc) ++{ ++ xmlNodePtr node; ++ ++ node = junction_xml_find_child_by_name(location, NFS_XML_WRITE_TAG); ++ if (node == NULL) ++ goto out_err; ++ ++ if (!junction_xml_get_u8_attribute(node, ++ NFS_XML_WRITE_RANK_ATTR, ++ &fsloc->nfl_info.nfl_writerank)) ++ goto out_err; ++ if (!junction_xml_get_u8_attribute(node, ++ NFS_XML_WRITE_ORDER_ATTR, ++ &fsloc->nfl_info.nfl_writeorder)) ++ goto out_err; ++ ++ return FEDFS_OK; ++ ++out_err: ++ xlog(D_GENERAL, "%s: Missing or invalid write element in %s", ++ __func__, pathname); ++ return FEDFS_ERR_NOTJUNCT; ++} ++ ++/** ++ * Parse the first "flags" child of "location" ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param location XML parse tree containing fileset location element ++ * @param fsloc a blank nfs_fsloc to fill in ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_parse_location_flags(const char *pathname, xmlNodePtr location, ++ struct nfs_fsloc *fsloc) ++{ ++ xmlNodePtr node; ++ ++ node = junction_xml_find_child_by_name(location, NFS_XML_FLAGS_TAG); ++ if (node == NULL) ++ goto out_err; ++ ++ if (!junction_xml_get_bool_attribute(node, ++ NFS_XML_FLAGS_VARSUB_ATTR, ++ &fsloc->nfl_flags.nfl_varsub)) ++ goto out_err; ++ ++ return FEDFS_OK; ++ ++out_err: ++ xlog(D_GENERAL, "%s: Missing or invalid flags element in %s", ++ __func__, pathname); ++ return FEDFS_ERR_NOTJUNCT; ++} ++ ++/** ++ * Parse the first "validfor" child of "location" ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param location XML parse tree containing fileset location element ++ * @param fsloc a blank nfs_fsloc to fill in ++ * @return a FedFsStatus code ++ */ ++static FedFsStatus ++nfs_parse_location_validfor(const char *pathname, xmlNodePtr location, ++ struct nfs_fsloc *fsloc) ++{ ++ xmlNodePtr node; ++ ++ node = junction_xml_find_child_by_name(location, NFS_XML_VALIDFOR_TAG); ++ if (node == NULL) ++ goto out_err; ++ ++ if (!junction_xml_get_int_content(node, &fsloc->nfl_validfor)) ++ goto out_err; ++ ++ return FEDFS_OK; ++ ++out_err: ++ xlog(D_GENERAL, "%s: Missing or invalid validfor element in %s", ++ __func__, pathname); ++ return FEDFS_ERR_NOTJUNCT; ++} ++ ++/** ++ * Parse children of NFS location element in an NFS junction ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param location XML parse tree containing fileset location element ++ * @param fsloc a blank nfs_fsloc to fill in ++ * @return a FedFsStatus code ++ * ++ * All children are required only-once elements, and may appear in any order. ++ * Extraneous or repeated elements are ignored for now. ++ */ ++static FedFsStatus ++nfs_parse_location_children(const char *pathname, xmlNodePtr location, ++ struct nfs_fsloc *fsloc) ++{ ++ FedFsStatus retval; ++ ++ retval = nfs_parse_location_host(pathname, location, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ retval = nfs_parse_location_path(pathname, location, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ retval = nfs_parse_location_currency(pathname, location, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ retval = nfs_parse_location_genflags(pathname, location, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ retval = nfs_parse_location_transflags(pathname, location, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ retval = nfs_parse_location_class(pathname, location, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ retval = nfs_parse_location_read(pathname, location, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ retval = nfs_parse_location_write(pathname, location, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ retval = nfs_parse_location_flags(pathname, location, fsloc); ++ if (retval != FEDFS_OK) ++ return retval; ++ return nfs_parse_location_validfor(pathname, location, fsloc); ++} ++ ++/** ++ * Parse NFS location element in an NFS junction ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param location XML parse tree containing fileset location element ++ * @param fsloc OUT: a single NFS location item ++ * @return a FedFsStatus code ++ * ++ * If nfs_parse_location() returns FEDFS_OK, caller must free the returned ++ * location with nfs_free_location(). ++ */ ++static FedFsStatus ++nfs_parse_node(const char *pathname, xmlNodePtr location, ++ struct nfs_fsloc **fsloc) ++{ ++ struct nfs_fsloc *tmp; ++ FedFsStatus retval; ++ ++ tmp = nfs_new_location(); ++ if (tmp == NULL) ++ return FEDFS_ERR_SVRFAULT; ++ ++ retval = nfs_parse_location_children(pathname, location, tmp); ++ if (retval != FEDFS_OK) ++ nfs_free_location(tmp); ++ else ++ *fsloc = tmp; ++ return retval; ++} ++ ++/** ++ * Build list of NFS locations from a nodeset ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param nodeset XML nodeset containing "location" elements ++ * @param fslocs OUT: pointer to a list of NFS locations ++ * @return a FedFsStatus code ++ * ++ * If nfs_parse_nodeset() returns FEDFS_OK, caller must free the returned ++ * list of locations with nfs_free_locations(). ++ */ ++static FedFsStatus ++nfs_parse_nodeset(const char *pathname, xmlNodeSetPtr nodeset, ++ struct nfs_fsloc **fslocs) ++{ ++ struct nfs_fsloc *location, *result = NULL; ++ FedFsStatus retval; ++ int i; ++ ++ if (xmlXPathNodeSetIsEmpty(nodeset)) { ++ xlog(D_GENERAL, "%s: No fileset locations found in %s", ++ __func__, pathname); ++ return FEDFS_ERR_NOTJUNCT; ++ } ++ ++ for (i = 0; i < nodeset->nodeNr; i++) { ++ xmlNodePtr node = nodeset->nodeTab[i]; ++ ++ retval = nfs_parse_node(pathname, node, &location); ++ if (retval != FEDFS_OK) { ++ nfs_free_locations(result); ++ return retval; ++ } ++ ++ if (result == NULL) ++ result = location; ++ else ++ result->nfl_next = location; ++ } ++ ++ *fslocs = result; ++ return FEDFS_OK; ++} ++ ++/** ++ * Parse fileset location information from junction XML ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param context XML path context containing junction XML ++ * @param fslocs OUT: pointer to a list of NFS locations ++ * @return a FedFsStatus code ++ * ++ * If nfs_parse_context() returns FEDFS_OK, caller must free the returned ++ * list of locations with nfs_free_locations(). ++ */ ++static FedFsStatus ++nfs_parse_context(const char *pathname, xmlXPathContextPtr context, ++ struct nfs_fsloc **fslocs) ++{ ++ xmlXPathObjectPtr object; ++ FedFsStatus retval; ++ ++ object = xmlXPathEvalExpression(NFS_XML_LOCATION_XPATH, context); ++ if (object == NULL) { ++ xlog(D_GENERAL, "%s: Failed to evaluate XML in %s", ++ __func__, pathname); ++ return FEDFS_ERR_NOTJUNCT; ++ } ++ ++ retval = nfs_parse_nodeset(pathname, object->nodesetval, fslocs); ++ ++ xmlXPathFreeObject(object); ++ return retval; ++} ++ ++/** ++ * Parse NFS locations information from junction XML ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param doc XML parse tree containing junction XML document ++ * @param fslocs OUT: pointer to a list of NFS locations ++ * @return a FedFsStatus code ++ * ++ * If nfs_parse_xml() returns FEDFS_OK, caller must free the returned ++ * list of locations with nfs_free_locations(). ++ */ ++static FedFsStatus ++nfs_parse_xml(const char *pathname, xmlDocPtr doc, struct nfs_fsloc **fslocs) ++{ ++ xmlXPathContextPtr context; ++ FedFsStatus retval; ++ ++ context = xmlXPathNewContext(doc); ++ if (context == NULL) { ++ xlog(D_GENERAL, "%s: Failed to create XPath context from %s", ++ __func__, pathname); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ retval = nfs_parse_context(pathname, context, fslocs); ++ ++ xmlXPathFreeContext(context); ++ return retval; ++} ++ ++/** ++ * Retrieve list of NFS locations from an NFS junction ++ * ++ * @param pathname NUL-terminated C string containing pathname of a junction ++ * @param fslocs OUT: pointer to a list of NFS locations ++ * @return a FedFsStatus code ++ * ++ * If nfs_get_locations() returns FEDFS_OK, caller must free the returned ++ * list of locations with nfs_free_locations(). ++ */ ++FedFsStatus ++nfs_get_locations(const char *pathname, struct nfs_fsloc **fslocs) ++{ ++ FedFsStatus retval; ++ xmlDocPtr doc; ++ ++ if (fslocs == NULL) ++ return FEDFS_ERR_INVAL; ++ ++ retval = junction_xml_parse(pathname, JUNCTION_XATTR_NAME_NFS, &doc); ++ if (retval != FEDFS_OK) ++ return retval; ++ ++ retval = nfs_parse_xml(pathname, doc, fslocs); ++ ++ xmlFreeDoc(doc); ++ return retval; ++} ++ ++/** ++ * Predicate: does "pathname" refer to an object that can become an NFS junction? ++ * ++ * @param pathname NUL-terminated C string containing pathname of a directory ++ * @return a FedFsStatus code ++ * ++ * Return values: ++ * FEDFS_ERR_NOTJUNCT: "pathname" refers to an object that can be ++ * made into a NFS junction ++ * FEDFS_ERR_EXIST: "pathname" refers to something that is ++ * already a junction ++ * FEDFS_ERR_INVAL: "pathname" does not exist ++ * Other: Some error occurred, "pathname" not ++ * investigated ++ */ ++FedFsStatus ++nfs_is_prejunction(const char *pathname) ++{ ++ FedFsStatus retval; ++ int fd; ++ ++ retval = junction_open_path(pathname, &fd); ++ if (retval != FEDFS_OK) ++ return retval; ++ ++ retval = junction_is_directory(fd, pathname); ++ if (retval != FEDFS_OK) ++ goto out_close; ++ ++ retval = junction_is_sticky_bit_set(fd, pathname); ++ switch (retval) { ++ case FEDFS_ERR_NOTJUNCT: ++ break; ++ case FEDFS_OK: ++ goto out_exist; ++ default: ++ goto out_close; ++ } ++ ++ retval = junction_is_xattr_present(fd, pathname, JUNCTION_XATTR_NAME_NFS); ++ switch (retval) { ++ case FEDFS_ERR_NOTJUNCT: ++ break; ++ case FEDFS_OK: ++ goto out_exist; ++ default: ++ goto out_close; ++ } ++ ++out_close: ++ (void)close(fd); ++ return retval; ++out_exist: ++ retval = FEDFS_ERR_EXIST; ++ goto out_close; ++} ++ ++/** ++ * Verify that junction contains NFS junction XML ++ * ++ * @param pathname NUL-terminated C string containing pathname of a directory ++ * @return a FedFsStatus code ++ * ++ * Return values: ++ * FEDFS_OK: "pathname" refers to an NFS junction ++ * FEDFS_ERR_NOTJUNCT: "pathname" refers to something that is ++ * not an NFS junction ++ * FEDFS_ERR_INVAL: "pathname" does not exist ++ * Other: Some error occurred, "pathname" not ++ * investigated ++ * ++ * NB: This is an expensive test. However, it is only done if the object ++ * actually has a junction extended attribute, meaning it should be done ++ * rarely. If this is really a problem, we can make the XML test cheaper. ++ */ ++static FedFsStatus ++nfs_is_junction_xml(const char *pathname) ++{ ++ struct nfs_fsloc *fslocs = NULL; ++ FedFsStatus retval; ++ xmlDocPtr doc; ++ ++ retval = junction_xml_parse(pathname, JUNCTION_XATTR_NAME_NFS, &doc); ++ if (retval != FEDFS_OK) ++ return retval; ++ ++ retval = nfs_parse_xml(pathname, doc, &fslocs); ++ nfs_free_locations(fslocs); ++ ++ xmlFreeDoc(doc); ++ return retval; ++} ++ ++/** ++ * Predicate: does "pathname" refer to an NFS junction? ++ * ++ * @param pathname NUL-terminated C string containing pathname of a directory ++ * @return a FedFsStatus code ++ * ++ * Return values: ++ * FEDFS_OK: "pathname" refers to an NFS junction ++ * FEDFS_ERR_NOTJUNCT: "pathname" refers to an object that is ++ * not a junction ++ * FEDFS_ERR_INVAL: "pathname" does not exist ++ * Other: Some error occurred, "pathname" not ++ * investigated ++ */ ++FedFsStatus ++nfs_is_junction(const char *pathname) ++{ ++ FedFsStatus retval; ++ int fd; ++ ++ retval = junction_open_path(pathname, &fd); ++ if (retval != FEDFS_OK) ++ return retval; ++ ++ retval = junction_is_directory(fd, pathname); ++ if (retval != FEDFS_OK) ++ goto out_close; ++ ++ retval = junction_is_sticky_bit_set(fd, pathname); ++ if (retval != FEDFS_OK) ++ goto out_close; ++ ++ retval = junction_is_xattr_present(fd, pathname, JUNCTION_XATTR_NAME_NFS); ++ if (retval != FEDFS_OK) ++ goto out_close; ++ ++ (void)close(fd); ++ ++ return nfs_is_junction_xml(pathname); ++ ++out_close: ++ (void)close(fd); ++ return retval; ++} +diff --git a/support/junction/path.c b/support/junction/path.c +new file mode 100644 +index 0000000..68a1d13 +--- /dev/null ++++ b/support/junction/path.c +@@ -0,0 +1,346 @@ ++/** ++ * @file support/junction/path.c ++ * @brief Encode and decode FedFS pathnames ++ */ ++ ++/* ++ * Copyright 2010, 2011, 2018 Oracle. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2.0 as ++ * published by the Free Software Foundation. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License version 2.0 for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * version 2.0 along with nfs-utils. If not, see: ++ * ++ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "junction.h" ++#include "xlog.h" ++ ++#define STRLEN_SLASH ((size_t)1) /* strlen("/") */ ++ ++#define XDR_UINT_BYTES (sizeof(uint32_t)) ++ ++/** ++ * Compute count of XDR 4-octet units from byte count ++ * ++ * @param bytes number of bytes to convert ++ * @return equivalent number of XDR 4-octet units ++ */ ++static inline size_t ++nsdb_quadlen(size_t bytes) ++{ ++ return (bytes + 3) >> 2; ++} ++ ++/** ++ * Free array of NUL-terminated C strings ++ * ++ * @param strings array of char * to be released ++ */ ++void ++nsdb_free_string_array(char **strings) ++{ ++ int i; ++ ++ if (strings == NULL) ++ return; ++ for (i = 0; strings[i] != NULL; i++) ++ free(strings[i]); ++ free(strings); ++} ++ ++static FedFsStatus ++nsdb_alloc_zero_component_pathname(char ***path_array) ++{ ++ char **result; ++ ++ xlog(D_GENERAL, "%s: Zero-component pathname", __func__); ++ ++ result = (char **)calloc(1, sizeof(char *)); ++ if (result == NULL) ++ return FEDFS_ERR_SVRFAULT; ++ result[0] = NULL; ++ *path_array = result; ++ return FEDFS_OK; ++} ++ ++/** ++ * Sanitize an incoming POSIX path ++ * ++ * @param pathname NUL-terminated C string containing a POSIX pathname ++ * @return NUL-terminated C string containing sanitized path ++ * ++ * Caller must free the returned pathname with free(3). ++ * ++ * Remove multiple sequential slashes and any trailing slashes, ++ * but leave "/" by itself alone. ++ */ ++static __attribute_malloc__ char * ++nsdb_normalize_path(const char *pathname) ++{ ++ size_t i, j, len; ++ char *result; ++ ++ len = strlen(pathname); ++ if (len == 0) { ++ xlog(D_CALL, "%s: NULL pathname", __func__); ++ return NULL; ++ } ++ ++ result = malloc(len + 1); ++ if (result == NULL) ++ return NULL; ++ ++ for (i = 0, j = 0; i < len; i++) { ++ if (pathname[i] == '/' && pathname[i + 1] == '/') ++ continue; ++ result[j++] = pathname[i]; ++ } ++ result[j] = '\0'; ++ ++ if (j > 1 && result[j - 1] == '/') ++ result[j - 1] = '\0'; ++ ++ xlog(D_CALL, "%s: result = '%s'", __func__, result); ++ return result; ++} ++ ++/** ++ * Count the number of components in a POSIX pathname ++ * ++ * @param pathname NUL-terminated C string containing a POSIX pathname ++ * @param len OUT: number of bytes the encoded XDR stream will consume ++ * @param cnt OUT: component count ++ * @return true when successful ++ */ ++static _Bool ++nsdb_count_components(const char *pathname, size_t *len, ++ unsigned int *cnt) ++{ ++ char *start, *component; ++ unsigned int count; ++ size_t length; ++ ++ /* strtok(3) will tromp on the string */ ++ start = strdup(pathname); ++ if (start == NULL) ++ return false; ++ ++ length = XDR_UINT_BYTES; ++ count = 0; ++ component = start; ++ for ( ;; ) { ++ char *next; ++ size_t tmp; ++ ++ if (*component == '/') ++ component++; ++ if (*component == '\0') ++ break; ++ next = strchrnul(component, '/'); ++ tmp = (size_t)(next - component); ++ if (tmp > 255) ++ return false; ++ length += XDR_UINT_BYTES + (nsdb_quadlen(tmp) << 2); ++ count++; ++ ++ if (*next == '\0') ++ break; ++ component = next; ++ } ++ ++ free(start); ++ ++ xlog(D_CALL, "%s: length = %zu, count = %u, path = '%s'", ++ __func__, length, count, pathname); ++ *len = length; ++ *cnt = count; ++ return true; ++} ++ ++/** ++ * Predicate: is input character set for a POSIX pathname valid UTF-8? ++ * ++ * @param pathname NUL-terminated C string containing a POSIX path ++ * @return true if the string is valid UTF-8 ++ * ++ * XXX: implement this ++ */ ++static _Bool ++nsdb_pathname_is_utf8(__attribute__((unused)) const char *pathname) ++{ ++ return true; ++} ++ ++/** ++ * Construct a local POSIX-style pathname from an array of component strings ++ * ++ * @param path_array array of pointers to NUL-terminated C strings ++ * @param pathname OUT: pointer to NUL-terminated UTF-8 C string containing a POSIX-style path ++ * @return a FedFsStatus code ++ * ++ * Caller must free the returned pathname with free(3). ++ */ ++FedFsStatus ++nsdb_path_array_to_posix(char * const *path_array, char **pathname) ++{ ++ char *component, *result; ++ unsigned int i, count; ++ size_t length, len; ++ ++ if (path_array == NULL || pathname == NULL) ++ return FEDFS_ERR_INVAL; ++ ++ if (path_array[0] == NULL) { ++ xlog(D_GENERAL, "%s: Zero-component pathname", __func__); ++ result = strdup("/"); ++ if (result == NULL) ++ return FEDFS_ERR_SVRFAULT; ++ *pathname = result; ++ return FEDFS_OK; ++ } ++ ++ for (length = 0, count = 0; ++ path_array[count] != NULL; ++ count++) { ++ component = path_array[count]; ++ len = strlen(component); ++ ++ if (len == 0) { ++ xlog(D_GENERAL, "%s: Zero-length component", __func__); ++ return FEDFS_ERR_BADNAME; ++ } ++ if (len > NAME_MAX) { ++ xlog(D_GENERAL, "%s: Component length too long", __func__); ++ return FEDFS_ERR_NAMETOOLONG; ++ } ++ if (strchr(component, '/') != NULL) { ++ xlog(D_GENERAL, "%s: Local separator character " ++ "found in component", __func__); ++ return FEDFS_ERR_BADNAME; ++ } ++ if (!nsdb_pathname_is_utf8(component)) { ++ xlog(D_GENERAL, "%s: Bad character in component", ++ __func__); ++ return FEDFS_ERR_BADCHAR; ++ } ++ ++ length += STRLEN_SLASH + len; ++ ++ if (length > PATH_MAX) { ++ xlog(D_GENERAL, "%s: Pathname too long", __func__); ++ return FEDFS_ERR_NAMETOOLONG; ++ } ++ } ++ ++ result = calloc(1, length + 1); ++ if (result == NULL) ++ return FEDFS_ERR_SVRFAULT; ++ ++ for (i = 0; i < count; i++) { ++ strcat(result, "/"); ++ strcat(result, path_array[i]); ++ } ++ *pathname = nsdb_normalize_path(result); ++ free(result); ++ if (*pathname == NULL) ++ return FEDFS_ERR_SVRFAULT; ++ return FEDFS_OK; ++} ++ ++/** ++ * Construct an array of component strings from a local POSIX-style pathname ++ * ++ * @param pathname NUL-terminated C string containing a POSIX-style pathname ++ * @param path_array OUT: pointer to array of pointers to NUL-terminated C strings ++ * @return a FedFsStatus code ++ * ++ * Caller must free "path_array" with nsdb_free_string_array(). ++ */ ++FedFsStatus ++nsdb_posix_to_path_array(const char *pathname, char ***path_array) ++{ ++ char *normalized, *component, **result; ++ unsigned int i, count; ++ size_t length; ++ ++ if (pathname == NULL || path_array == NULL) ++ return FEDFS_ERR_INVAL; ++ ++ if (!nsdb_pathname_is_utf8(pathname)) { ++ xlog(D_GENERAL, "%s: Bad character in pathname", __func__); ++ return FEDFS_ERR_BADCHAR; ++ } ++ ++ normalized = nsdb_normalize_path(pathname); ++ if (normalized == NULL) ++ return FEDFS_ERR_SVRFAULT; ++ ++ if (!nsdb_count_components(normalized, &length, &count)) { ++ free(normalized); ++ return FEDFS_ERR_BADNAME; ++ } ++ ++ if (count == 0) { ++ free(normalized); ++ return nsdb_alloc_zero_component_pathname(path_array); ++ } ++ ++ result = (char **)calloc(count + 1, sizeof(char *)); ++ if (result == NULL) { ++ free(normalized); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ component = normalized; ++ for (i = 0; ; i++) { ++ char *next; ++ ++ if (*component == '/') ++ component++; ++ if (*component == '\0') ++ break; ++ next = strchrnul(component, '/'); ++ length = (size_t)(next - component); ++ if (length > 255) ++ return FEDFS_ERR_SVRFAULT; ++ ++ result[i] = strndup(component, length); ++ if (result[i] == NULL) { ++ nsdb_free_string_array(result); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ if (*next == '\0') ++ break; ++ component = next; ++ } ++ ++ *path_array = result; ++ free(normalized); ++ return FEDFS_OK; ++} +diff --git a/support/junction/xml.c b/support/junction/xml.c +new file mode 100644 +index 0000000..79b0770 +--- /dev/null ++++ b/support/junction/xml.c +@@ -0,0 +1,401 @@ ++/** ++ * @file support/junction/xml.c ++ * @brief Common utilities for managing junction XML ++ */ ++ ++/* ++ * Copyright 2011, 2018 Oracle. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2.0 as ++ * published by the Free Software Foundation. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License version 2.0 for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * version 2.0 along with nfs-utils. If not, see: ++ * ++ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "junction.h" ++#include "junction-internal.h" ++#include "xlog.h" ++ ++/** ++ * Predicate: is element content empty? ++ * ++ * @param content element content to test ++ * @return true if content is empty ++ */ ++_Bool ++junction_xml_is_empty(const xmlChar *content) ++{ ++ return content == NULL || *content == '\0'; ++} ++ ++/** ++ * Match an XML parse tree node by its name ++ * ++ * @param node pointer to a node in an XML parse tree ++ * @param name NUL-terminated C string containing name to match ++ * @return true if "node" is named "name" ++ */ ++_Bool ++junction_xml_match_node_name(xmlNodePtr node, const xmlChar *name) ++{ ++ return (node->type == XML_ELEMENT_NODE) && ++ (xmlStrcmp(node->name, name) == 0); ++} ++ ++/** ++ * Find a first-level child of "parent" named "name" ++ * ++ * @param parent pointer to node whose children are to be searched ++ * @param name NUL-terminated C string containing name to match ++ * @return pointer to child of "parent" whose name is "name" ++ */ ++xmlNodePtr ++junction_xml_find_child_by_name(xmlNodePtr parent, const xmlChar *name) ++{ ++ xmlNodePtr node; ++ ++ for (node = parent->children; node != NULL; node = node->next) ++ if (junction_xml_match_node_name(node, name)) ++ return node; ++ return NULL; ++} ++ ++/** ++ * Read attribute into a boolean ++ * ++ * @param node pointer to a node in an XML parse tree ++ * @param attrname NUL-terminated C string containing attribute name ++ * @param value OUT: attribute's value converted to an integer ++ * @return true if attribute "attrname" has a valid boolean value ++ */ ++_Bool ++junction_xml_get_bool_attribute(xmlNodePtr node, const xmlChar *attrname, ++ _Bool *value) ++{ ++ xmlChar *prop; ++ _Bool retval; ++ ++ retval = false; ++ prop = xmlGetProp(node, attrname); ++ if (prop == NULL) ++ goto out; ++ ++ if (xmlStrcmp(prop, (const xmlChar *)"true") == 0) { ++ *value = true; ++ retval = true; ++ goto out; ++ } ++ ++ if (xmlStrcmp(prop, (const xmlChar *)"false") == 0) { ++ *value = false; ++ retval = true; ++ goto out; ++ } ++ ++out: ++ xmlFree(prop); ++ return retval; ++} ++ ++/** ++ * Set attribute to a boolean ++ * ++ * @param node pointer to a node in an XML parse tree ++ * @param attrname NUL-terminated C string containing attribute name ++ * @param value boolean value to set ++ */ ++void ++junction_xml_set_bool_attribute(xmlNodePtr node, const xmlChar *attrname, ++ _Bool value) ++{ ++ xmlSetProp(node, attrname, (const xmlChar *)(value ? "true" : "false")); ++} ++ ++/** ++ * Read attribute into an uint8_t ++ * ++ * @param node pointer to a node in an XML parse tree ++ * @param attrname NUL-terminated C string containing attribute name ++ * @param value OUT: attribute's value converted to an uint8_t ++ * @return true if attribute "attrname" has a valid uint8_t value ++ */ ++_Bool ++junction_xml_get_u8_attribute(xmlNodePtr node, const xmlChar *attrname, ++ uint8_t *value) ++{ ++ char *endptr; ++ _Bool retval; ++ char *prop; ++ long tmp; ++ ++ retval = false; ++ prop = (char *)xmlGetProp(node, attrname); ++ if (prop == NULL) ++ goto out; ++ ++ errno = 0; ++ tmp = strtol(prop, &endptr, 10); ++ if (errno != 0 || *endptr != '\0' || tmp > 255 || tmp < 0) ++ goto out; ++ ++ *value = (uint8_t)tmp; ++ retval = true; ++ ++out: ++ xmlFree(prop); ++ return retval; ++} ++ ++/** ++ * Read attribute into an integer ++ * ++ * @param node pointer to a node in an XML parse tree ++ * @param attrname NUL-terminated C string containing attribute name ++ * @param value OUT: attribute's value converted to an integer ++ * @return true if attribute "attrname" has a valid integer value ++ */ ++_Bool ++junction_xml_get_int_attribute(xmlNodePtr node, const xmlChar *attrname, ++ int *value) ++{ ++ char *endptr; ++ _Bool retval; ++ char *prop; ++ long tmp; ++ ++ retval = false; ++ prop = (char *)xmlGetProp(node, attrname); ++ if (prop == NULL) ++ goto out; ++ ++ errno = 0; ++ tmp = strtol(prop, &endptr, 10); ++ if (errno != 0 || *endptr != '\0' || tmp > INT32_MAX || tmp < INT32_MIN) ++ goto out; ++ ++ *value = (int)tmp; ++ retval = true; ++ ++out: ++ xmlFree(prop); ++ return retval; ++} ++ ++/** ++ * Set attribute to an integer ++ * ++ * @param node pointer to a node in an XML parse tree ++ * @param attrname NUL-terminated C string containing attribute name ++ * @param value integer value to set ++ */ ++void ++junction_xml_set_int_attribute(xmlNodePtr node, const xmlChar *attrname, ++ int value) ++{ ++ char buf[16]; ++ ++ snprintf(buf, sizeof(buf), "%d", value); ++ xmlSetProp(node, attrname, (const xmlChar *)buf); ++} ++ ++/** ++ * Read node content into an integer ++ * ++ * @param node pointer to a node in an XML parse tree ++ * @param value OUT: node's content converted to an integer ++ * @return true if "node" has valid integer content ++ */ ++_Bool ++junction_xml_get_int_content(xmlNodePtr node, int *value) ++{ ++ xmlChar *content; ++ char *endptr; ++ _Bool retval; ++ long tmp; ++ ++ retval = false; ++ content = xmlNodeGetContent(node); ++ if (content == NULL) ++ goto out; ++ ++ errno = 0; ++ tmp = strtol((const char *)content, &endptr, 10); ++ if (errno != 0 || *endptr != '\0' || tmp > INT32_MAX || tmp < INT32_MIN) ++ goto out; ++ ++ *value = (int)tmp; ++ retval = true; ++ ++out: ++ xmlFree(content); ++ return retval; ++} ++ ++/** ++ * Add a child node with integer content ++ * ++ * @param parent pointer to a node in an XML parse tree ++ * @param name NUL-terminated C string containing name of child to add ++ * @param value set node content to this value ++ * @return pointer to new child node ++ */ ++xmlNodePtr ++junction_xml_set_int_content(xmlNodePtr parent, const xmlChar *name, int value) ++{ ++ char buf[16]; ++ ++ snprintf(buf, sizeof(buf), "%d", value); ++ return xmlNewTextChild(parent, NULL, name, (const xmlChar *)buf); ++} ++ ++/** ++ * Parse XML document in a buffer into an XML document tree ++ * ++ * @param pathname NUL-terminated C string containing pathname of a directory ++ * @param name NUL-terminated C string containing name of xattr to replace ++ * @param buf opaque byte array containing XML to parse ++ * @param len size of "buf" in bytes ++ * @param doc OUT: an XML parse tree containing junction XML ++ * @return a FedFsStatus code ++ * ++ * If junction_parse_xml_buf() returns success, caller must free "*doc" ++ * using xmlFreeDoc(3). ++ * ++ * @note Access to trusted attributes requires CAP_SYS_ADMIN. ++ */ ++static FedFsStatus ++junction_parse_xml_buf(const char *pathname, const char *name, ++ void *buf, size_t len, xmlDocPtr *doc) ++{ ++ xmlDocPtr tmp; ++ ++ tmp = xmlParseMemory(buf, (int)len); ++ if (tmp == NULL) { ++ xlog(D_GENERAL, "Failed to parse XML in %s(%s)\n", ++ pathname, name); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ *doc = tmp; ++ return FEDFS_OK; ++} ++ ++/** ++ * Read an XML document from an extended attribute into an XML document tree ++ * ++ * @param pathname NUL-terminated C string containing pathname of a directory ++ * @param fd an open file descriptor ++ * @param name NUL-terminated C string containing name of xattr to replace ++ * @param doc OUT: an XML parse tree containing junction XML ++ * @return a FedFsStatus code ++ * ++ * If junction_parse_xml_read() returns success, caller must free "*doc" ++ * using xmlFreeDoc(3). ++ * ++ * @note Access to trusted attributes requires CAP_SYS_ADMIN. ++ */ ++static FedFsStatus ++junction_parse_xml_read(const char *pathname, int fd, const char *name, ++ xmlDocPtr *doc) ++{ ++ FedFsStatus retval; ++ void *buf = NULL; ++ size_t len; ++ ++ retval = junction_get_xattr(fd, pathname, name, &buf, &len); ++ if (retval != FEDFS_OK) ++ return retval; ++ ++ xlog(D_CALL, "%s: XML document contained in junction:\n%.*s", ++ __func__, len, buf); ++ ++ retval = junction_parse_xml_buf(pathname, name, buf, len, doc); ++ ++ free(buf); ++ return retval; ++} ++ ++/** ++ * Read an XML document from an extended attribute into an XML document tree ++ * ++ * @param pathname NUL-terminated C string containing pathname of a directory ++ * @param name NUL-terminated C string containing name of xattr to replace ++ * @param doc OUT: an XML parse tree containing junction XML ++ * @return a FedFsStatus code ++ * ++ * If junction_parse_xml() returns success, caller must free "*doc" ++ * using xmlFreeDoc(3). ++ * ++ * @note Access to trusted attributes requires CAP_SYS_ADMIN. ++ */ ++FedFsStatus ++junction_xml_parse(const char *pathname, const char *name, xmlDocPtr *doc) ++{ ++ FedFsStatus retval; ++ int fd; ++ ++ retval = junction_open_path(pathname, &fd); ++ if (retval != FEDFS_OK) ++ return retval; ++ ++ retval = junction_parse_xml_read(pathname, fd, name, doc); ++ ++ (void)close(fd); ++ return retval; ++} ++ ++/** ++ * Write an XML document into an extended attribute ++ * ++ * @param pathname NUL-terminated C string containing pathname of a directory ++ * @param name NUL-terminated C string containing name of xattr to replace ++ * @param doc an XML parse tree containing junction XML ++ * @return a FedFsStatus code ++ * ++ * @note Access to trusted attributes requires CAP_SYS_ADMIN. ++ */ ++FedFsStatus ++junction_xml_write(const char *pathname, const char *name, xmlDocPtr doc) ++{ ++ xmlChar *buf = NULL; ++ FedFsStatus retval; ++ int fd, len; ++ ++ retval = junction_open_path(pathname, &fd); ++ if (retval != FEDFS_OK) ++ return retval; ++ ++ retval = FEDFS_ERR_SVRFAULT; ++ xmlIndentTreeOutput = 1; ++ xmlDocDumpFormatMemoryEnc(doc, &buf, &len, "UTF-8", 1); ++ if (len < 0) ++ goto out; ++ ++ retval = junction_set_xattr(fd, pathname, name, buf, (size_t)len); ++ ++out: ++ xmlFree(buf); ++ (void)close(fd); ++ return retval; ++} +diff --git a/support/misc/mountpoint.c b/support/misc/mountpoint.c +index a72fb92..9f9ce44 100644 +--- a/support/misc/mountpoint.c ++++ b/support/misc/mountpoint.c +@@ -6,6 +6,7 @@ + #include + #include "xcommon.h" + #include ++#include "misc.h" + + int + is_mountpoint(char *path) +diff --git a/support/nfs/atomicio.c b/support/nfs/atomicio.c +index aa819ca..0e81838 100644 +--- a/support/nfs/atomicio.c ++++ b/support/nfs/atomicio.c +@@ -28,6 +28,8 @@ + #include + #include + ++#include "nfslib.h" ++ + /* + * ensure all of data on socket comes through. f==read || f==write + */ +diff --git a/support/nfs/cacheio.c b/support/nfs/cacheio.c +index 9912afa..9dc4cf1 100644 +--- a/support/nfs/cacheio.c ++++ b/support/nfs/cacheio.c +@@ -212,7 +212,7 @@ cache_flush(int force) + { + struct stat stb; + int c; +- char stime[20]; ++ char stime[32]; + char path[200]; + time_t now; + /* Note: the order of these caches is important. +diff --git a/support/nfs/closeall.c b/support/nfs/closeall.c +index a69bf35..e07253e 100644 +--- a/support/nfs/closeall.c ++++ b/support/nfs/closeall.c +@@ -9,6 +9,8 @@ + #include + #include + ++#include "nfslib.h" ++ + void + closeall(int min) + { +diff --git a/support/nfs/exports.c b/support/nfs/exports.c +index 92bd6e6..b59d187 100644 +--- a/support/nfs/exports.c ++++ b/support/nfs/exports.c +@@ -197,6 +197,7 @@ static const struct secinfo_flag_displaymap { + const char *set; + const char *unset; + } secinfo_flag_displaymap[] = { ++ { NFSEXP_READONLY, "ro", "rw" }, + { NFSEXP_INSECURE_PORT, "insecure", "secure" }, + { NFSEXP_ROOTSQUASH, "root_squash", "no_root_squash" }, + { NFSEXP_ALLSQUASH, "all_squash", "no_all_squash" }, +diff --git a/support/nfs/nfs_mntent.c b/support/nfs/nfs_mntent.c +index a2118a2..05a4c68 100644 +--- a/support/nfs/nfs_mntent.c ++++ b/support/nfs/nfs_mntent.c +@@ -13,6 +13,7 @@ + #include /* for isdigit */ + #include /* for umask */ + #include /* for ftruncate */ ++#include /* for errno */ + + #include "nfs_mntent.h" + #include "nls.h" +@@ -148,9 +149,12 @@ nfs_addmntent (mntFILE *mfp, struct mntent *mnt) { + free(m4); + if (res >= 0) { + res = fflush(mfp->mntent_fp); +- if (res < 0) ++ if (res < 0) { ++ nfs_error("Cant't flush out mtab: %s", strerror(errno)); + /* Avoid leaving a corrupt mtab file */ +- ftruncate(fileno(mfp->mntent_fp), length); ++ if (ftruncate(fileno(mfp->mntent_fp), length)) ++ {/* Ignore this failure; Why confuse things */} ++ } + } + return (res < 0) ? 1 : 0; + } +diff --git a/support/nfs/rpcmisc.c b/support/nfs/rpcmisc.c +index ae2c0a6..abe89ba 100644 +--- a/support/nfs/rpcmisc.c ++++ b/support/nfs/rpcmisc.c +@@ -32,6 +32,7 @@ + #include + #include + #include "nfslib.h" ++#include "rpcmisc.h" + + #if SIZEOF_SOCKLEN_T - 0 == 0 + #define socklen_t int +diff --git a/support/nfs/strlcat.c b/support/nfs/strlcat.c +index daedd7a..0edee14 100644 +--- a/support/nfs/strlcat.c ++++ b/support/nfs/strlcat.c +@@ -38,6 +38,8 @@ static char *rcsid = "$OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp + #include "config.h" + #endif /* HAVE_CONFIG_H */ + ++#include "nfslib.h" ++ + /* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters +diff --git a/support/nfs/strlcpy.c b/support/nfs/strlcpy.c +index a2653ee..23e3ae9 100644 +--- a/support/nfs/strlcpy.c ++++ b/support/nfs/strlcpy.c +@@ -38,6 +38,8 @@ static char *rcsid = "$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp + #include "config.h" + #endif /* HAVE_CONFIG_H */ + ++#include "nfslib.h" ++ + /* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). +diff --git a/support/nfs/svc_socket.c b/support/nfs/svc_socket.c +index 1fa0d15..1239712 100644 +--- a/support/nfs/svc_socket.c ++++ b/support/nfs/svc_socket.c +@@ -25,6 +25,8 @@ + #include + #include + #include "xlog.h" ++#include "rpcmisc.h" ++#include "nfslib.h" + + #include "config.h" + +diff --git a/support/nfs/wildmat.c b/support/nfs/wildmat.c +index c5b4c78..437b2d1 100644 +--- a/support/nfs/wildmat.c ++++ b/support/nfs/wildmat.c +@@ -41,9 +41,16 @@ + #endif + + #include ++#include "nfslib.h" + ++#ifndef TRUE + #define TRUE 1 ++#endif ++ ++#ifndef FALSE + #define FALSE 0 ++#endif ++ + #define ABORT -1 + + +diff --git a/support/nfsidmap/Makefile.am b/support/nfsidmap/Makefile.am +index 9466f92..8b5dfe4 100644 +--- a/support/nfsidmap/Makefile.am ++++ b/support/nfsidmap/Makefile.am +@@ -25,7 +25,7 @@ pkgplugin_LTLIBRARIES = nsswitch.la static.la $(UMICH_LDAP_LIB) $(GUMS_MAPPING_L + # The number of previous additional interfaces supported + # by this library. + +-libnfsidmap_la_SOURCES = libnfsidmap.c nfsidmap_internal.h nfsidmap_common.c ++libnfsidmap_la_SOURCES = libnfsidmap.c nfsidmap_common.c + libnfsidmap_la_LDFLAGS = -version-info 1:0:0 + libnfsidmap_la_LIBADD = -ldl ../../support/nfs/libnfsconf.la + +diff --git a/support/nfsidmap/libnfsidmap.c b/support/nfsidmap/libnfsidmap.c +index 3b44da6..35ddf01 100644 +--- a/support/nfsidmap/libnfsidmap.c ++++ b/support/nfsidmap/libnfsidmap.c +@@ -64,6 +64,7 @@ + + #pragma GCC visibility push(hidden) + ++void nfs4_cleanup_name_mapping(void); + static char *default_domain; + static struct mapping_plugin **nfs4_plugins = NULL; + static struct mapping_plugin **gss_plugins = NULL; +@@ -103,14 +104,6 @@ nfs4_idmap_log_function_t idmap_log_func = default_logger; + int idmap_verbosity = 2; + #pragma GCC visibility push(hidden) + +-static char * toupper_str(char *s) +-{ +- size_t i; +- for (i=0; i < strlen(s); i++) +- s[i] = toupper(s[i]); +- return s; +-} +- + static int id_as_chars(char *name, uid_t *id) + { + long int value; +@@ -327,7 +320,7 @@ out: + return ret; + } + +-char * get_default_domain(void) ++static char *get_default_domain(void) + { + int ret; + +@@ -353,24 +346,22 @@ void nfs4_cleanup_name_mapping(void) + + #pragma GCC visibility pop + ++const char * nfsidmap_conf_path = PATH_IDMAPDCONF; ++ + int nfs4_init_name_mapping(char *conffile) + { + int ret = -ENOENT; + int dflt = 0; + struct conf_list *nfs4_methods, *gss_methods; + char *nobody_user, *nobody_group; +- char *nostrip; +- char *reformatgroup; +- char *conf_path; + + /* XXX: need to be able to reload configurations... */ + if (nfs4_plugins) /* already succesfully initialized */ + return 0; + if (conffile) +- conf_path = conffile; +- else +- conf_path = PATH_IDMAPDCONF; +- conf_init_file(conf_path); ++ nfsidmap_conf_path = conffile; ++ conf_init_file(nfsidmap_conf_path); ++ + default_domain = conf_get_str("General", "Domain"); + if (default_domain == NULL) { + dflt = 1; +@@ -387,30 +378,8 @@ int nfs4_init_name_mapping(char *conffile) + IDMAP_LOG(1, ("libnfsidmap: using%s domain: %s", + (dflt ? " (default)" : ""), default_domain)); + +- /* Get list of "local equivalent" realms. Meaning the list of realms +- * where john@REALM.A is considered the same user as john@REALM.B +- * If not specified, default to upper-case of local domain name */ +- local_realms = conf_get_list("General", "Local-Realms"); +- if (local_realms == NULL) { +- struct conf_list_node *node; +- +- local_realms = malloc(sizeof *local_realms); +- if (local_realms == NULL) +- return -ENOMEM; +- local_realms->cnt = 0; +- TAILQ_INIT(&local_realms->fields); +- +- node = calloc(1, sizeof *node); +- if (node == NULL) +- return -ENOMEM; +- node->field = strdup(get_default_domain()); +- if (node->field == NULL) +- return -ENOMEM; +- toupper_str(node->field); +- +- TAILQ_INSERT_TAIL(&local_realms->fields, node, link); +- local_realms->cnt++; +- } ++ struct conf_list *local_realms = get_local_realms(); ++ if (local_realms == NULL) return -ENOMEM; + + if (idmap_verbosity >= 1) { + struct conf_list_node *r; +@@ -434,26 +403,6 @@ int nfs4_init_name_mapping(char *conffile) + IDMAP_LOG(1, ("libnfsidmap: Realms list: ")); + } + +- nostrip = conf_get_str_with_def("General", "No-Strip", "none"); +- if (strcasecmp(nostrip, "both") == 0) +- no_strip = IDTYPE_USER|IDTYPE_GROUP; +- else if (strcasecmp(nostrip, "group") == 0) +- no_strip = IDTYPE_GROUP; +- else if (strcasecmp(nostrip, "user") == 0) +- no_strip = IDTYPE_USER; +- else +- no_strip = 0; +- +- if (no_strip & IDTYPE_GROUP) { +- reformatgroup = conf_get_str_with_def("General", "Reformat-Group", "false"); +- if ((strcasecmp(reformatgroup, "true") == 0) || +- (strcasecmp(reformatgroup, "on") == 0) || +- (strcasecmp(reformatgroup, "yes") == 0)) +- reformat_group = 1; +- else +- reformat_group = 0; +- } +- + nfs4_methods = conf_get_list("Translation", "Method"); + if (nfs4_methods) { + IDMAP_LOG(1, ("libnfsidmap: processing 'Method' list")); +diff --git a/support/nfsidmap/nfsidmap_common.c b/support/nfsidmap/nfsidmap_common.c +index 891c855..5242c7e 100644 +--- a/support/nfsidmap/nfsidmap_common.c ++++ b/support/nfsidmap/nfsidmap_common.c +@@ -6,6 +6,9 @@ + * + * Code common to libnfsidmap and some of its bundled plugins + * ++ * If you make use of these functions you must initialise your own ++ * copy of the config file data using: conf_init_file(nfsidmap_conf_path) ++ * failure to do so will appear as if the config was empty + */ + + #include "config.h" +@@ -13,6 +16,8 @@ + #include + #include + #include ++#include ++#include + + #include "nfsidmap.h" + #include "nfsidmap_private.h" +@@ -21,13 +26,82 @@ + + #pragma GCC visibility push(hidden) + +-int reformat_group = 0; +-int no_strip = 0; +- +-struct conf_list *local_realms; ++static char * toupper_str(char *s) ++{ ++ size_t i; ++ for (i=0; i < strlen(s); i++) ++ s[i] = toupper(s[i]); ++ return s; ++} + ++/* Get list of "local equivalent" realms. Meaning the list of realms ++ * where john@REALM.A is considered the same user as john@REALM.B ++ * If not specified, default to upper-case of local domain name */ + struct conf_list *get_local_realms(void) + { ++ static struct conf_list *local_realms = NULL; ++ if (local_realms) return local_realms; ++ ++ local_realms = conf_get_list("General", "Local-Realms"); ++ if (local_realms == NULL) { ++ struct conf_list_node *node; ++ ++ local_realms = malloc(sizeof *local_realms); ++ if (local_realms == NULL) ++ return NULL; ++ local_realms->cnt = 0; ++ TAILQ_INIT(&local_realms->fields); ++ ++ node = calloc(1, sizeof *node); ++ if (node == NULL) ++ return NULL; ++ ++ node->field = calloc(1, NFS4_MAX_DOMAIN_LEN); ++ if (node->field == NULL) ++ return NULL; ++ ++ nfs4_get_default_domain(NULL, node->field, NFS4_MAX_DOMAIN_LEN); ++ toupper_str(node->field); ++ ++ TAILQ_INSERT_TAIL(&local_realms->fields, node, link); ++ local_realms->cnt++; ++ } + return local_realms; + } + ++static int no_strip = -1; ++static int reformat_group = 0; ++ ++int get_nostrip(void) ++{ ++ if (no_strip != -1) return no_strip; ++ ++ char * nostrip = conf_get_str_with_def("General", "No-Strip", "none"); ++ if (strcasecmp(nostrip, "both") == 0) ++ no_strip = IDTYPE_USER|IDTYPE_GROUP; ++ else if (strcasecmp(nostrip, "group") == 0) ++ no_strip = IDTYPE_GROUP; ++ else if (strcasecmp(nostrip, "user") == 0) ++ no_strip = IDTYPE_USER; ++ else ++ no_strip = 0; ++ ++ if (no_strip & IDTYPE_GROUP) { ++ char * reformatgroup = conf_get_str_with_def("General", "Reformat-Group", "false"); ++ if ((strcasecmp(reformatgroup, "true") == 0) || ++ (strcasecmp(reformatgroup, "on") == 0) || ++ (strcasecmp(reformatgroup, "yes") == 0)) ++ reformat_group = 1; ++ else ++ reformat_group = 0; ++ } ++ ++ return no_strip; ++} ++ ++int get_reformat_group(void) ++{ ++ if (no_strip != -1) return reformat_group; ++ ++ return reformat_group; ++} +diff --git a/support/nfsidmap/nfsidmap_plugin.h b/support/nfsidmap/nfsidmap_plugin.h +index e19efe5..66fcdaa 100644 +--- a/support/nfsidmap/nfsidmap_plugin.h ++++ b/support/nfsidmap/nfsidmap_plugin.h +@@ -51,6 +51,7 @@ struct trans_func { + + extern int idmap_verbosity; + extern nfs4_idmap_log_function_t idmap_log_func; ++struct trans_func *libnfsidmap_plugin_init(void); + + /* Level zero always prints, others print depending on verbosity level */ + #define IDMAP_LOG(LVL, MSG) \ +@@ -64,5 +65,6 @@ extern nfs4_idmap_log_function_t idmap_log_func; + #endif + #endif + ++extern const char *nfsidmap_conf_path; + extern const char *nfsidmap_config_get(const char *section, const char *tag); + +diff --git a/support/nfsidmap/nfsidmap_private.h b/support/nfsidmap/nfsidmap_private.h +index 2cc309e..f1af55f 100644 +--- a/support/nfsidmap/nfsidmap_private.h ++++ b/support/nfsidmap/nfsidmap_private.h +@@ -37,16 +37,14 @@ + #include "conffile.h" + + struct conf_list *get_local_realms(void); ++int get_nostrip(void); ++int get_reformat_group(void); + + typedef enum { + IDTYPE_USER = 1, + IDTYPE_GROUP = 2 + } idtypes; + +-extern int no_strip; +-extern int reformat_group; +-extern struct conf_list *local_realms; +- + typedef struct trans_func * (*libnfsidmap_plugin_init_t)(void); + + struct mapping_plugin { +diff --git a/support/nfsidmap/nss.c b/support/nfsidmap/nss.c +index 6f024dc..9d46499 100644 +--- a/support/nfsidmap/nss.c ++++ b/support/nfsidmap/nss.c +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -103,7 +104,7 @@ static int nss_uid_to_name(uid_t uid, char *domain, char *name, size_t len) + err = -ENOENT; + if (err) + goto out_buf; +- if (no_strip & IDTYPE_USER) ++ if (get_nostrip() & IDTYPE_USER) + err = write_name(name, pw->pw_name, domain, len, 0); + else + err = write_name(name, pw->pw_name, domain, len, 1); +@@ -140,7 +141,7 @@ static int nss_gid_to_name(gid_t gid, char *domain, char *name, size_t len) + + if (err) + goto out_buf; +- if (no_strip & IDTYPE_GROUP) ++ if (get_nostrip() & IDTYPE_GROUP) + err = write_name(name, gr->gr_name, domain, len, 0); + else + err = write_name(name, gr->gr_name, domain, len, 1); +@@ -247,7 +248,7 @@ static int nss_name_to_uid(char *name, uid_t *uid) + int err = -ENOENT; + + domain = get_default_domain(); +- if (no_strip & IDTYPE_USER) { ++ if (get_nostrip() & IDTYPE_USER) { + pw = nss_getpwnam(name, domain, &err, 0); + if (pw != NULL) + goto out_uid; +@@ -315,7 +316,7 @@ static int _nss_name_to_gid(char *name, gid_t *gid, int dostrip) + "into domain '%s'", name, domain)); + goto out; + } +- } else if (reformat_group) { ++ } else if (get_reformat_group()) { + ref_name = reformat_name(name); + if (ref_name == NULL) { + IDMAP_LOG(1, ("nss_name_to_gid: failed to reformat name '%s'", +@@ -335,7 +336,7 @@ static int _nss_name_to_gid(char *name, gid_t *gid, int dostrip) + goto out_name; + if (dostrip) + err = -getgrnam_r(localname, &grbuf, buf, buflen, &gr); +- else if (reformat_group) ++ else if (get_reformat_group()) + err = -getgrnam_r(ref_name, &grbuf, buf, buflen, &gr); + else + err = -getgrnam_r(name, &grbuf, buf, buflen, &gr); +@@ -343,7 +344,7 @@ static int _nss_name_to_gid(char *name, gid_t *gid, int dostrip) + if (dostrip) + IDMAP_LOG(1, ("nss_name_to_gid: name '%s' not found " + "in domain '%s'", localname, domain)); +- else if (reformat_group) ++ else if (get_reformat_group()) + IDMAP_LOG(1, ("nss_name_to_gid: name '%s' not found " + "(reformatted)", ref_name)); + else +@@ -366,7 +367,7 @@ out_buf: + out_name: + if (dostrip) + free(localname); +- if (reformat_group) ++ if (get_reformat_group()) + free(ref_name); + out: + return err; +@@ -376,7 +377,7 @@ static int nss_name_to_gid(char *name, gid_t *gid) + { + int err = 0; + +- if (no_strip & IDTYPE_GROUP) { ++ if (get_nostrip() & IDTYPE_GROUP) { + err = _nss_name_to_gid(name, gid, 0); + if (!err) + goto out; +@@ -436,7 +437,7 @@ out: + return err; + } + +-int nss_gss_princ_to_grouplist(char *secname, char *princ, ++static int nss_gss_princ_to_grouplist(char *secname, char *princ, + gid_t *groups, int *ngroups, + extra_mapping_params **UNUSED(ex)) + { +@@ -459,10 +460,17 @@ out: + return ret; + } + ++static int nss_plugin_init(void) ++{ ++ if (nfsidmap_conf_path) ++ conf_init_file(nfsidmap_conf_path); ++ return 0; ++} ++ + + struct trans_func nss_trans = { + .name = "nsswitch", +- .init = NULL, ++ .init = nss_plugin_init, + .princ_to_ids = nss_gss_princ_to_ids, + .name_to_uid = nss_name_to_uid, + .name_to_gid = nss_name_to_gid, +diff --git a/support/nfsidmap/static.c b/support/nfsidmap/static.c +index 0b1173f..f7b8a67 100644 +--- a/support/nfsidmap/static.c ++++ b/support/nfsidmap/static.c +@@ -317,6 +317,9 @@ static int static_init(void) { + for (i = 0; i < sizeof uid_mappings / sizeof uid_mappings[0]; i++) + LIST_INIT (&uid_mappings[i]); + ++ if (nfsidmap_conf_path) ++ conf_init_file(nfsidmap_conf_path); ++ + //get all principals for which we have mappings + princ_list = conf_get_tag_list("Static", NULL); + +diff --git a/support/nfsidmap/umich_ldap.c b/support/nfsidmap/umich_ldap.c +index e82828c..0e31b1c 100644 +--- a/support/nfsidmap/umich_ldap.c ++++ b/support/nfsidmap/umich_ldap.c +@@ -1101,6 +1101,9 @@ umichldap_init(void) + char missing_msg[128] = ""; + char *server_in, *canon_name; + ++ if (nfsidmap_conf_path) ++ conf_init_file(nfsidmap_conf_path); ++ + server_in = conf_get_str(LDAP_SECTION, "LDAP_server"); + ldap_info.base = conf_get_str(LDAP_SECTION, "LDAP_base"); + ldap_info.people_tree = conf_get_str(LDAP_SECTION, "LDAP_people_base"); +diff --git a/support/nsm/Makefile.am b/support/nsm/Makefile.am +index 2038e68..8f5874e 100644 +--- a/support/nsm/Makefile.am ++++ b/support/nsm/Makefile.am +@@ -32,11 +32,12 @@ $(GENFILES_SVC): %_svc.c: %.x $(RPCGEN) + + $(GENFILES_XDR): %_xdr.c: %.x $(RPCGEN) + test -f $@ && rm -rf $@ || true +- $(RPCGEN) -c -o $@ $< ++ $(RPCGEN) -c -i 0 -o $@ $< + + $(GENFILES_H): %.h: %.x $(RPCGEN) + test -f $@ && rm -rf $@ || true + $(RPCGEN) -h -o $@ $< ++ echo "void sm_prog_1(struct svc_req *, SVCXPRT *);" >> $@ + rm -f $(top_builddir)/support/include/sm_inter.h + $(LN_S) ../nsm/sm_inter.h $(top_builddir)/support/include/sm_inter.h + +diff --git a/systemd/systemd.c b/systemd/systemd.c +index 17820d4..c7bdb4d 100644 +--- a/systemd/systemd.c ++++ b/systemd/systemd.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include "systemd.h" + + static const char hex[16] = + { +diff --git a/tools/locktest/testlk.c b/tools/locktest/testlk.c +index 82ed765..b392f71 100644 +--- a/tools/locktest/testlk.c ++++ b/tools/locktest/testlk.c +@@ -81,7 +81,7 @@ main(int argc, char **argv) + if (fl.l_type == F_UNLCK) { + printf("%s: no conflicting lock\n", fname); + } else { +- printf("%s: conflicting lock by %d on (%lld;%lld)\n", ++ printf("%s: conflicting lock by %d on (%ld;%ld)\n", + fname, fl.l_pid, fl.l_start, fl.l_len); + } + return 0; +diff --git a/utils/Makefile.am b/utils/Makefile.am +index c75a5a0..d361aea 100644 +--- a/utils/Makefile.am ++++ b/utils/Makefile.am +@@ -23,6 +23,10 @@ if CONFIG_NFSDCLTRACK + OPTDIRS += nfsdcltrack + endif + ++if CONFIG_JUNCTION ++OPTDIRS += nfsref ++endif ++ + SUBDIRS = \ + exportfs \ + mountd \ +diff --git a/utils/blkmapd/device-discovery.c b/utils/blkmapd/device-discovery.c +index cae8c8d..3a202e0 100644 +--- a/utils/blkmapd/device-discovery.c ++++ b/utils/blkmapd/device-discovery.c +@@ -81,7 +81,7 @@ int bl_watch_fd, bl_pipe_fd, nfs_pipedir_wfd, rpc_pipedir_wfd; + int pidfd = -1; + + +-struct bl_disk_path *bl_get_path(const char *filepath, ++static struct bl_disk_path *bl_get_path(const char *filepath, + struct bl_disk_path *paths) + { + struct bl_disk_path *tmp = paths; +@@ -103,7 +103,7 @@ struct bl_disk_path *bl_get_path(const char *filepath, + * exist for each multipath device. If not, active device path will be + * chosen for device creation. + */ +-int bl_update_path(enum bl_path_state_e state, struct bl_disk *disk) ++static int bl_update_path(enum bl_path_state_e state, struct bl_disk *disk) + { + struct bl_disk_path *valid_path = disk->valid_path; + +@@ -112,7 +112,7 @@ int bl_update_path(enum bl_path_state_e state, struct bl_disk *disk) + return 1; + } + +-void bl_release_disk(void) ++static void bl_release_disk(void) + { + struct bl_disk *disk; + struct bl_disk_path *path = NULL; +@@ -133,7 +133,7 @@ void bl_release_disk(void) + } + } + +-void bl_add_disk(char *filepath) ++static void bl_add_disk(char *filepath) + { + struct bl_disk *disk = NULL; + int fd = 0; +@@ -239,7 +239,7 @@ int bl_discover_devices(void) + { + FILE *f; + int n; +- char buf[PATH_MAX], devname[PATH_MAX], fulldevname[PATH_MAX]; ++ char buf[PATH_MAX], devname[PATH_MAX], fulldevname[PATH_MAX+NAME_MAX]; + + /* release previous list */ + bl_release_disk(); +@@ -435,7 +435,7 @@ static int bl_event_helper(void) + return ret; + } + +-void sig_die(int signal) ++static void sig_die(int signal) + { + if (pidfd >= 0) { + close(pidfd); +@@ -504,9 +504,11 @@ int main(int argc, char **argv) + close(pidfd); + exit(1); + } +- ftruncate(pidfd, 0); ++ if (ftruncate(pidfd, 0) < 0) ++ BL_LOG_WARNING("ftruncate on %s failed: m\n", PID_FILE); + sprintf(pidbuf, "%d\n", getpid()); +- write(pidfd, pidbuf, strlen(pidbuf)); ++ if (write(pidfd, pidbuf, strlen(pidbuf)) != (ssize_t)strlen(pidbuf)) ++ BL_LOG_WARNING("write on %s failed: m\n", PID_FILE); + } + + signal(SIGINT, sig_die); +diff --git a/utils/blkmapd/dm-device.c b/utils/blkmapd/dm-device.c +index 24ffcbf..f2d4de4 100644 +--- a/utils/blkmapd/dm-device.c ++++ b/utils/blkmapd/dm-device.c +@@ -210,7 +210,7 @@ static int dm_device_remove_byname(const char *dev_name) + return ret; + } + +-int dm_device_remove(uint64_t dev) ++static int dm_device_remove(uint64_t dev) + { + struct dm_task *dmt; + struct dm_names *dmnames; +diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c +index 448f195..cd3c979 100644 +--- a/utils/exportfs/exportfs.c ++++ b/utils/exportfs/exportfs.c +@@ -695,10 +695,6 @@ dump(int verbose, int export_format) + continue; + } + c = '('; +- if (ep->e_flags & NFSEXP_READONLY) +- c = dumpopt(c, "ro"); +- else +- c = dumpopt(c, "rw"); + if (ep->e_flags & NFSEXP_ASYNC) + c = dumpopt(c, "async"); + else +diff --git a/utils/gssd/Makefile.am b/utils/gssd/Makefile.am +index beb3e8e..321046b 100644 +--- a/utils/gssd/Makefile.am ++++ b/utils/gssd/Makefile.am +@@ -5,6 +5,8 @@ if CONFIG_SVCGSS + man8_MANS += svcgssd.man + endif + ++AM_CPPFLAGS += -I ../../support/nfsidmap ++ + RPCPREFIX = rpc. + KPREFIX = @kprefix@ + sbin_PREFIXED = gssd +diff --git a/utils/gssd/err_util.c b/utils/gssd/err_util.c +index fe09eda..2b1132a 100644 +--- a/utils/gssd/err_util.c ++++ b/utils/gssd/err_util.c +@@ -36,6 +36,7 @@ + #include + #include + #include "xlog.h" ++#include "err_util.h" + + static int verbosity = 0; + static int fg = 0; +diff --git a/utils/gssd/gss_names.c b/utils/gssd/gss_names.c +index 047069d..2a7f3a1 100644 +--- a/utils/gssd/gss_names.c ++++ b/utils/gssd/gss_names.c +@@ -51,6 +51,7 @@ + + #include "svcgssd.h" + #include "gss_util.h" ++#include "gss_names.h" + #include "err_util.h" + #include "context.h" + #include "misc.h" +diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c +index 4fc81c3..ce73777 100644 +--- a/utils/gssd/gssd_proc.c ++++ b/utils/gssd/gssd_proc.c +@@ -473,7 +473,7 @@ change_identity(uid_t uid) + return 0; + } + +-AUTH * ++static AUTH * + krb5_not_machine_creds(struct clnt_info *clp, uid_t uid, char *tgtname, + int *downcall_err, int *chg_err, CLIENT **rpc_clnt) + { +@@ -519,7 +519,7 @@ out: + return auth; + } + +-AUTH * ++static AUTH * + krb5_use_machine_creds(struct clnt_info *clp, uid_t uid, char *tgtname, + char *service, CLIENT **rpc_clnt) + { +diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c +index b64818a..b342b06 100644 +--- a/utils/gssd/krb5_util.c ++++ b/utils/gssd/krb5_util.c +@@ -188,7 +188,7 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, + int found = 0; + struct dirent *best_match_dir = NULL; + struct stat best_match_stat, tmp_stat; +- char buf[1030]; ++ char buf[PATH_MAX+4+2+256]; + char *princname = NULL; + char *realm = NULL; + int score, best_match_score = 0, err = -EACCES; +@@ -202,39 +202,35 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, + dirname, strerror(errno)); + } + else if (n > 0) { +- char statname[1024]; + for (i = 0; i < n; i++) { +- snprintf(statname, sizeof(statname), ++ snprintf(buf, sizeof(buf), + "%s/%s", dirname, namelist[i]->d_name); + printerr(3, "CC '%s' being considered, " + "with preferred realm '%s'\n", +- statname, preferred_realm ? ++ buf, preferred_realm ? + preferred_realm : ""); +- if (lstat(statname, &tmp_stat)) { +- printerr(0, "Error doing stat on '%s'\n", +- statname); ++ if (lstat(buf, &tmp_stat)) { ++ printerr(0, "Error doing stat on '%s'\n", buf); + free(namelist[i]); + continue; + } + /* Only pick caches owned by the user (uid) */ + if (tmp_stat.st_uid != uid) { + printerr(3, "CC '%s' owned by %u, not %u\n", +- statname, tmp_stat.st_uid, uid); ++ buf, tmp_stat.st_uid, uid); + free(namelist[i]); + continue; + } + if (!S_ISREG(tmp_stat.st_mode) && + !S_ISDIR(tmp_stat.st_mode)) { + printerr(3, "CC '%s' is not a regular " +- "file or directory\n", +- statname); ++ "file or directory\n", buf); + free(namelist[i]); + continue; + } + if (uid == 0 && !root_uses_machine_creds && + strstr(namelist[i]->d_name, "machine_")) { +- printerr(3, "CC '%s' not available to root\n", +- statname); ++ printerr(3, "CC '%s' not available to root\n", buf); + free(namelist[i]); + continue; + } +@@ -333,7 +329,7 @@ gssd_get_single_krb5_cred(krb5_context context, + struct gssd_k5_kt_princ *ple, + int nocache) + { +-#if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS ++#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS + krb5_get_init_creds_opt *init_opts = NULL; + #else + krb5_get_init_creds_opt options; +@@ -372,7 +368,7 @@ gssd_get_single_krb5_cred(krb5_context context, + if ((krb5_unparse_name(context, ple->princ, &pname))) + pname = NULL; + +-#if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS ++#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS + code = krb5_get_init_creds_opt_alloc(context, &init_opts); + if (code) { + k5err = gssd_k5_err_msg(context, code); +@@ -454,7 +450,7 @@ gssd_get_single_krb5_cred(krb5_context context, + code = 0; + printerr(2, "%s: principal '%s' ccache:'%s'\n", __func__, pname, cc_name); + out: +-#if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS ++#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS + if (init_opts) + krb5_get_init_creds_opt_free(context, init_opts); + #endif +@@ -865,7 +861,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *tgtname, + if (strcmp(realm, default_realm) == 0) + tried_default = 1; + for (j = 0; svcnames[j] != NULL; j++) { +- char spn[300]; ++ char spn[NI_MAXHOST+2]; + + /* + * The special svcname "$" means 'try the active +@@ -1059,7 +1055,7 @@ err_cache: + int + gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirpattern) + { +- char buf[MAX_NETOBJ_SZ], dirname[PATH_MAX]; ++ char buf[PATH_MAX+2+256], dirname[PATH_MAX]; + const char *cctype; + struct dirent *d; + int err, i, j; +diff --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c +index 3514ae1..8e918cc 100644 +--- a/utils/gssd/svcgssd.c ++++ b/utils/gssd/svcgssd.c +@@ -63,7 +63,9 @@ + #include "err_util.h" + #include "conffile.h" + +-void ++struct state_paths etab; ++ ++static void + sig_die(int signal) + { + /* destroy krb5 machine creds */ +@@ -71,7 +73,7 @@ sig_die(int signal) + exit(0); + } + +-void ++static void + sig_hup(int signal) + { + /* don't exit on SIGHUP */ +@@ -101,7 +103,7 @@ main(int argc, char *argv[]) + char *principal = NULL; + char *s; + +- conf_init(NFS_CONFFILE); ++ conf_init_file(NFS_CONFFILE); + + s = conf_get_str("svcgssd", "principal"); + if (!s) +diff --git a/utils/gssd/svcgssd_mech2file.c b/utils/gssd/svcgssd_mech2file.c +index ecd908b..c26b435 100644 +--- a/utils/gssd/svcgssd_mech2file.c ++++ b/utils/gssd/svcgssd_mech2file.c +@@ -41,6 +41,7 @@ + #include + #include + ++char * mech2file(gss_OID mech); + + #define g_OID_equal(o1,o2) \ + (((o1)->length == (o2)->length) && \ +diff --git a/utils/idmapd/Makefile.am b/utils/idmapd/Makefile.am +index d768eec..e09e8c5 100644 +--- a/utils/idmapd/Makefile.am ++++ b/utils/idmapd/Makefile.am +@@ -2,6 +2,8 @@ + + man8_MANS = idmapd.man + ++AM_CPPFLAGS += -I ../../support/nfsidmap ++ + RPCPREFIX = rpc. + KPREFIX = @kprefix@ + sbin_PROGRAMS = idmapd +diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c +index 2b9ecea..b87c4dd 100644 +--- a/utils/idmapd/idmapd.c ++++ b/utils/idmapd/idmapd.c +@@ -169,7 +169,7 @@ static int + flush_nfsd_cache(char *path, time_t now) + { + int fd; +- char stime[20]; ++ char stime[32]; + + sprintf(stime, "%ld\n", now); + fd = open(path, O_RDWR); +@@ -196,7 +196,7 @@ flush_nfsd_idmap_cache(void) + return ret; + } + +-void usage(char *progname) ++static void usage(char *progname) + { + fprintf(stderr, "Usage: %s [-hfvCS] [-p path] [-c path]\n", + basename(progname)); +@@ -420,7 +420,7 @@ dirscancb(int UNUSED(fd), short UNUSED(which), void *data) + int nent, i; + struct dirent **ents; + struct idmap_client *ic, *nextic; +- char path[PATH_MAX]; ++ char path[PATH_MAX+256]; /* + sizeof(d_name) */ + struct idmap_clientq *icq = data; + + nent = scandir(pipefsdir, &ents, NULL, alphasort); +diff --git a/utils/mount/configfile.c b/utils/mount/configfile.c +index 64688bf..b48b25e 100644 +--- a/utils/mount/configfile.c ++++ b/utils/mount/configfile.c +@@ -35,6 +35,10 @@ + #include "network.h" + #include "conffile.h" + ++char *mountopts_convert(char *value); ++char *is_alias(char *opt); ++char *conf_get_mntopts(char *spec, char *mount_point, char *mount_opts); ++ + #define KBYTES(x) ((x) * (1024)) + #define MEGABYTES(x) ((x) * (1048576)) + #define GIGABYTES(x) ((x) * (1073741824)) +diff --git a/utils/mount/mount_libmount.c b/utils/mount/mount_libmount.c +index 2d40657..aa4ac5c 100644 +--- a/utils/mount/mount_libmount.c ++++ b/utils/mount/mount_libmount.c +@@ -45,6 +45,8 @@ + #include "error.h" + #include "utils.h" + ++char *retrieve_mount_options(struct libmnt_fs *fs); ++ + char *progname; + int nfs_mount_data_version; + int verbose; +diff --git a/utils/mount/network.c b/utils/mount/network.c +index 8ab5be8..9a2c878 100644 +--- a/utils/mount/network.c ++++ b/utils/mount/network.c +@@ -811,8 +811,12 @@ int start_statd(void) + switch (pid) { + case 0: /* child */ + setgroups(0, NULL); +- setgid(0); +- setuid(0); ++ if (setgid(0) < 0) ++ nfs_error(_("%s: setgid(0) failed: %s"), ++ progname, strerror(errno)); ++ if (setuid(0) < 0) ++ nfs_error(_("%s: setuid(0) failed: %s"), ++ progname, strerror(errno)); + execle(START_STATD, START_STATD, NULL, envp); + exit(1); + case -1: /* error */ +@@ -1275,8 +1279,8 @@ nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *v + } + } + +- if (!found && strcmp(type, "nfs4") == 0) +- version_val = type + 3; ++ if (!found && strncmp(type, "nfs", 3) == 0) ++ version_val = "4"; + else if (!found) + return 1; + else if (i <= 2 ) { +@@ -1308,9 +1312,14 @@ nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *v + if (!(version->minor = strtol(version_val, &cptr, 10)) && cptr == version_val) + goto ret_error; + version->v_mode = V_SPECIFIC; +- } else if (version->major > 3 && *cptr == '\0') +- version->v_mode = V_GENERAL; +- ++ } else if (version->major > 3 && *cptr == '\0') { ++ version_val = po_get(options, "minorversion"); ++ if (version_val != NULL) { ++ version->minor = strtol(version_val, &cptr, 10); ++ version->v_mode = V_SPECIFIC; ++ } else ++ version->v_mode = V_GENERAL; ++ } + if (*cptr != '\0') + goto ret_error; + +diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c +index 1217823..d1b0708 100644 +--- a/utils/mount/stropts.c ++++ b/utils/mount/stropts.c +@@ -761,9 +761,26 @@ static int nfs_do_mount_v4(struct nfsmount_info *mi, + fmt = "vers=%lu.%lu"; + break; + } ++#pragma GCC diagnostic ignored "-Wformat-nonliteral" + snprintf(version_opt, sizeof(version_opt) - 1, + fmt, mi->version.major, + mi->version.minor); ++#pragma GCC diagnostic warning "-Wformat-nonliteral" ++ ++ if (po_append(options, version_opt) == PO_FAILED) { ++ errno = EINVAL; ++ goto out_fail; ++ } ++ } else if (po_get(options, "minorversion") && ++ linux_version_code() > MAKE_VERSION(3, 4, 0)) { ++ /* ++ * convert minorversion= into vers=4.x ++ */ ++ po_remove_all(options, "minorversion"); ++ ++ snprintf(version_opt, sizeof(version_opt) - 1, ++ "vers=%lu.%lu", mi->version.major, ++ mi->version.minor); + + if (po_append(options, version_opt) == PO_FAILED) { + errno = EINVAL; +diff --git a/utils/mountd/Makefile.am b/utils/mountd/Makefile.am +index 153a90a..73eeb3f 100644 +--- a/utils/mountd/Makefile.am ++++ b/utils/mountd/Makefile.am +@@ -1,5 +1,10 @@ + ## Process this file with automake to produce Makefile.in + ++OPTLIBS = ++if CONFIG_JUNCTION ++OPTLIBS += ../../support/junction/libjunction.la $(LIBXML2) ++endif ++ + man8_MANS = mountd.man + EXTRA_DIST = $(man8_MANS) + +@@ -13,7 +18,8 @@ mountd_SOURCES = mountd.c mount_dispatch.c auth.c rmtab.c cache.c \ + mountd_LDADD = ../../support/export/libexport.a \ + ../../support/nfs/libnfs.la \ + ../../support/misc/libmisc.a \ +- $(LIBBSD) $(LIBWRAP) $(LIBNSL) $(LIBBLKID) $(LIBDL) $(LIBTIRPC) ++ $(OPTLIBS) \ ++ $(LIBBSD) $(LIBWRAP) $(LIBNSL) $(LIBBLKID) $(LIBTIRPC) + mountd_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) \ + -I$(top_builddir)/support/include \ + -I$(top_srcdir)/support/export +diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c +index e49300d..6f42512 100644 +--- a/utils/mountd/cache.c ++++ b/utils/mountd/cache.c +@@ -976,10 +976,15 @@ lookup_export(char *dom, char *path, struct addrinfo *ai) + return found; + } + +-#ifdef HAVE_NFS_PLUGIN_H +-#include +-#include +-#include ++#ifdef CONFIG_JUNCTION ++ ++#include "junction.h" ++ ++struct nfs_fsloc_set { ++ int ns_ttl; ++ struct nfs_fsloc *ns_current; ++ struct nfs_fsloc *ns_list; ++}; + + /* + * Find the export entry for the parent of "pathname". +@@ -1035,13 +1040,39 @@ out_default: + return mkexportent("*", "/", "insecure"); + } + ++static int get_next_location(struct nfs_fsloc_set *locset, ++ char **hostname, char **export_path, int *ttl) ++{ ++ char *hostname_tmp, *export_path_tmp; ++ struct nfs_fsloc *fsloc; ++ ++ if (locset->ns_current == NULL) ++ return ENOENT; ++ fsloc = locset->ns_current; ++ ++ hostname_tmp = strdup(fsloc->nfl_hostname); ++ if (hostname_tmp == NULL) ++ return ENOMEM; ++ ++ if (nsdb_path_array_to_posix(fsloc->nfl_rootpath, ++ &export_path_tmp)) { ++ free(hostname_tmp); ++ return EINVAL; ++ } ++ ++ *hostname = hostname_tmp; ++ *export_path = export_path_tmp; ++ *ttl = locset->ns_ttl; ++ locset->ns_current = locset->ns_current->nfl_next; ++ return 0; ++} ++ + /* + * Walk through a set of FS locations and build an e_fslocdata string. + * Returns true if all went to plan; otherwise, false. + */ +-static bool locations_to_fslocdata(struct jp_ops *ops, +- nfs_fsloc_set_t locations, char *fslocdata, +- size_t remaining, int *ttl) ++static bool locations_to_fslocdata(struct nfs_fsloc_set *locations, ++ char *fslocdata, size_t remaining, int *ttl) + { + char *server, *last_path, *rootpath, *ptr; + _Bool seen = false; +@@ -1056,13 +1087,13 @@ static bool locations_to_fslocdata(struct jp_ops *ops, + enum jp_status status; + int len; + +- status = ops->jp_get_next_location(locations, &server, ++ status = get_next_location(locations, &server, + &rootpath, ttl); +- if (status == JP_EMPTY) ++ if (status == ENOENT) + break; +- if (status != JP_OK) { ++ if (status) { + xlog(D_GENERAL, "%s: failed to parse location: %s", +- __func__, ops->jp_error(status)); ++ __func__, strerror(status)); + goto out_false; + } + xlog(D_GENERAL, "%s: Location: %s:%s", +@@ -1159,116 +1190,73 @@ out_nomem: + * Walk through the set of FS locations and build an exportent. + * Returns pointer to an exportent if "junction" refers to a junction. + */ +-static struct exportent *locations_to_export(struct jp_ops *ops, +- nfs_fsloc_set_t locations, const char *junction, +- struct exportent *parent) ++static struct exportent *locations_to_export(struct nfs_fsloc_set *locations, ++ const char *junction, struct exportent *parent) + { + static char fslocdata[BUFSIZ]; + int ttl; + + fslocdata[0] = '\0'; +- if (!locations_to_fslocdata(ops, locations, +- fslocdata, sizeof(fslocdata), &ttl)) ++ if (!locations_to_fslocdata(locations, fslocdata, sizeof(fslocdata), &ttl)) + return NULL; + return create_junction_exportent(parent, junction, fslocdata, ttl); + } + +-/* +- * Retrieve locations information in "junction" and dump it to the +- * kernel. Returns pointer to an exportent if "junction" refers +- * to a junction. +- */ +-static struct exportent *invoke_junction_ops(void *handle, char *dom, +- const char *junction, struct addrinfo *ai) ++static int ++nfs_get_basic_junction(const char *junct_path, struct nfs_fsloc_set **locset) + { +- struct exportent *parent, *exp = NULL; +- nfs_fsloc_set_t locations; +- 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; +- } +-#ifdef JP_API_VERSION +- if (ops->jp_api_version != JP_API_VERSION) { +- xlog(D_GENERAL, "%s: unrecognized junction API version: %u", +- __func__, ops->jp_api_version); +- return NULL; +- } +-#endif +- 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; ++ struct nfs_fsloc_set *new; ++ FedFsStatus retval; ++ ++ new = calloc(1, sizeof(struct nfs_fsloc_set)); ++ if (new == NULL) ++ return ENOMEM; ++ ++ retval = nfs_get_locations(junct_path, &new->ns_list); ++ if (retval) { ++ nfs_free_locations(new->ns_list); ++ free(new); ++ return EINVAL; + } + +- status = ops->jp_get_locations(junction, &locations); +- switch (status) { +- case JP_OK: +- break; +- case JP_NOTJUNCTION: ++ locset->ns_current = locset->ns_list; ++ new->ns_ttl = 300; ++ *locset = new; ++ return 0; ++} ++ ++static struct exportent *lookup_junction(char *dom, const char *pathname, ++ struct addrinfo *ai) ++{ ++ struct exportent *parent, *exp = NULL; ++ struct nfs_fsloc_set *locations; ++ int status; ++ ++ xmlInitParser(); ++ ++ if (nfs_is_junction(pathname)) { + xlog(D_GENERAL, "%s: %s is not a junction", +- __func__, junction); ++ __func__, pathname); + goto out; +- default: ++ } ++ status = nfs_get_basic_junction(pathname, &locations); ++ switch (status) { + xlog(L_WARNING, "Dangling junction %s: %s", +- junction, ops->jp_error(status)); ++ pathname, strerro(status)); + goto out; + } + +- parent = lookup_parent_export(dom, junction, ai); ++ parent = lookup_parent_export(dom, pathname, ai); + if (parent == NULL) + goto out; + +- exp = locations_to_export(ops, locations, junction, parent); ++ exp = locations_to_export(locations, pathname, parent); + +- ops->jp_put_locations(locations); ++ nfs_free_locations(locset->ns_list); ++ free(locset); + + out: +- 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. +- */ +-static struct exportent *lookup_junction(char *dom, const char *pathname, +- struct addrinfo *ai) +-{ +- struct exportent *exp; +- struct link_map *map; +- void *handle; +- +-#ifdef JP_NFSPLUGIN_SONAME +- handle = dlopen(JP_NFSPLUGIN_SONAME, RTLD_NOW); +-#else +- handle = dlopen("libnfsjunct.so.0", RTLD_NOW); +-#endif +- if (handle == NULL) { +- xlog(D_GENERAL, "%s: dlopen: %s", __func__, dlerror()); +- return NULL; +- } +- +- if (dlinfo(handle, RTLD_DI_LINKMAP, &map) == 0) +- xlog(D_GENERAL, "%s: loaded plug-in %s", +- __func__, map->l_name); +- +- (void)dlerror(); /* Clear any error */ +- +- exp = invoke_junction_ops(handle, dom, pathname, ai); +- +- /* 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); ++ xmlCleanupParser(); + return exp; + } + +@@ -1284,13 +1272,16 @@ static void lookup_nonexport(int f, char *buf, int buflen, char *dom, char *path + exportent_release(eep); + free(eep); + } +-#else /* !HAVE_NFS_PLUGIN_H */ ++ ++#else /* !CONFIG_JUNCTION */ ++ + static void lookup_nonexport(int f, char *buf, int buflen, char *dom, char *path, + struct addrinfo *UNUSED(ai)) + { + dump_to_cache(f, buf, buflen, dom, path, NULL, 0); + } +-#endif /* !HAVE_NFS_PLUGIN_H */ ++ ++#endif /* !CONFIG_JUNCTION */ + + static void nfsd_export(int f) + { +diff --git a/utils/mountd/svc_run.c b/utils/mountd/svc_run.c +index a572441..41b96d7 100644 +--- a/utils/mountd/svc_run.c ++++ b/utils/mountd/svc_run.c +@@ -57,6 +57,7 @@ + #include + #endif + ++void my_svc_run(void); + void cache_set_fds(fd_set *fdset); + int cache_process_req(fd_set *readfds); + +diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c +index fc36792..7923f5d 100644 +--- a/utils/nfsd/nfssvc.c ++++ b/utils/nfsd/nfssvc.c +@@ -68,7 +68,7 @@ nfssvc_mount_nfsdfs(char *progname) + * mount nfsdfs when nfsd.ko is plugged in. So, ignore the return + * code from it and just check for the "threads" file afterward. + */ +- system("/bin/mount -t nfsd nfsd " NFSD_FS_DIR " >/dev/null 2>&1"); ++ err = system("/bin/mount -t nfsd nfsd " NFSD_FS_DIR " >/dev/null 2>&1"); + + err = stat(NFSD_THREAD_FILE, &statbuf); + if (err == 0) +@@ -325,7 +325,8 @@ nfssvc_set_time(const char *type, const int seconds) + /* set same value for lockd */ + fd = open("/proc/sys/fs/nfs/nlm_grace_period", O_WRONLY); + if (fd >= 0) { +- write(fd, nbuf, strlen(nbuf)); ++ if (write(fd, nbuf, strlen(nbuf)) != (ssize_t)strlen(nbuf)) ++ xlog(L_ERROR, "Unable to write nlm_grace_period : %m"); + close(fd); + } + } +diff --git a/utils/nfsdcltrack/sqlite.c b/utils/nfsdcltrack/sqlite.c +index 1552eba..c59f777 100644 +--- a/utils/nfsdcltrack/sqlite.c ++++ b/utils/nfsdcltrack/sqlite.c +@@ -51,6 +51,7 @@ + #include + + #include "xlog.h" ++#include "sqlite.h" + + #define CLTRACK_SQLITE_LATEST_SCHEMA_VERSION 2 + +@@ -203,7 +204,7 @@ rollback: + * then insert schema version into the parameters table and commit the + * transaction. On any error, rollback the transaction. + */ +-int ++static int + sqlite_maindb_init_v2(void) + { + int ret, ret2; +diff --git a/utils/nfsidmap/Makefile.am b/utils/nfsidmap/Makefile.am +index 49158df..e5d7d04 100644 +--- a/utils/nfsidmap/Makefile.am ++++ b/utils/nfsidmap/Makefile.am +@@ -3,6 +3,8 @@ + man8_MANS = nfsidmap.man + sbin_PROGRAMS = nfsidmap + ++AM_CPPFLAGS += -I ../../support/nfsidmap ++ + nfsidmap_SOURCES = nfsidmap.c + nfsidmap_LDADD = -lkeyutils \ + ../../support/nfs/libnfs.la \ +diff --git a/utils/nfsref/Makefile.am b/utils/nfsref/Makefile.am +new file mode 100644 +index 0000000..2b2bb53 +--- /dev/null ++++ b/utils/nfsref/Makefile.am +@@ -0,0 +1,39 @@ ++## ++## @file utils/nfsref/Makefile.am ++## @brief Process this file with automake to produce utils/nfsref/Makefile.in ++## ++ ++## ++## Copyright 2011, 2018 Oracle. All rights reserved. ++## ++## This file is part of nfs-utils. ++## ++## nfs-utils is free software; you can redistribute it and/or modify ++## it under the terms of the GNU General Public License version 2.0 as ++## published by the Free Software Foundation. ++## ++## nfs-utils is distributed in the hope that it will be useful, but ++## WITHOUT ANY WARRANTY; without even the implied warranty of ++## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++## GNU General Public License version 2.0 for more details. ++## ++## You should have received a copy of the GNU General Public License ++## version 2.0 along with nfs-utils. If not, see: ++## ++## http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++## ++ ++noinst_HEADERS = nfsref.h ++ ++sbin_PROGRAMS = nfsref ++nfsref_SOURCES = add.c lookup.c nfsref.c remove.c ++LDADD = $(LIBXML2) $(LIBCAP) \ ++ ../../support/nfs/libnfs.la \ ++ ../../support/junction/libjunction.la ++ ++man8_MANS = nfsref.man ++ ++MAINTAINERCLEANFILES = Makefile.in ++ ++AM_CPPFLAGS = -I. -I../../support/include ++##AM_LDFLAGS = -Wl,--as-needed +diff --git a/utils/nfsref/add.c b/utils/nfsref/add.c +new file mode 100644 +index 0000000..781aeee +--- /dev/null ++++ b/utils/nfsref/add.c +@@ -0,0 +1,272 @@ ++/** ++ * @file utils/nfsref/add.c ++ * @brief Add junction metadata to a local file system object ++ */ ++ ++/* ++ * Copyright 2011, 2018 Oracle. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2.0 as ++ * published by the Free Software Foundation. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License version 2.0 for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * version 2.0 along with nfs-utils. If not, see: ++ * ++ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "junction.h" ++#include "xlog.h" ++#include "nfsref.h" ++ ++/** ++ * Default cache expiration for FSN information ++ */ ++#define FSN_DEFAULT_TTL (300) ++ ++/** ++ * Display help message for "add" subcommand ++ * ++ * @param progname NUL-terminated C string containing name of program ++ * @return program exit status ++ */ ++int ++nfsref_add_help(const char *progname) ++{ ++ fprintf(stderr, " \n"); ++ ++ fprintf(stderr, "Usage: %s [ -t type ] add " ++ " [ ... ]\n\n", ++ progname); ++ ++ fprintf(stderr, "Add a new junction containing the specified list " ++ "of fileset locations.\n"); ++ fprintf(stderr, " is the filename of the new junction. " ++ " is the hostname\n"); ++ fprintf(stderr, "or IP address of an NFS server where the fileset is " ++ "located. is the\n"); ++ fprintf(stderr, "export pathname of the fileset on that server.\n\n"); ++ ++ fprintf(stderr, "For NFS basic junctions, the location list is stored " ++ "locally in the junction.\n"); ++ fprintf(stderr, "For FedFS junctions, the location list is stored " ++ "as new FSN and FSL records\n"); ++ fprintf(stderr, "on an NSDB.\n"); ++ ++ return EXIT_SUCCESS; ++} ++ ++/** ++ * Fill in default settings for NFSv4.0 fs_locations4 ++ * ++ * @param new NFS location structure to fill in ++ * ++ * See section 5.1.3.2 of the NSDB protocol draft. ++ */ ++static void ++nfsref_add_fsloc_defaults(struct nfs_fsloc *new) ++{ ++ new->nfl_hostport = 0; ++ new->nfl_flags.nfl_varsub = false; ++ new->nfl_currency = -1; ++ new->nfl_validfor = 0; ++ new->nfl_genflags.nfl_writable = false; ++ new->nfl_genflags.nfl_going = false; ++ new->nfl_genflags.nfl_split = true; ++ new->nfl_transflags.nfl_rdma = true; ++ new->nfl_info.nfl_simul = 0; ++ new->nfl_info.nfl_handle = 0; ++ new->nfl_info.nfl_fileid = 0; ++ new->nfl_info.nfl_writever = 0; ++ new->nfl_info.nfl_change = 0; ++ new->nfl_info.nfl_readdir = 0; ++ new->nfl_info.nfl_readrank = 0; ++ new->nfl_info.nfl_readorder = 0; ++ new->nfl_info.nfl_writerank = 0; ++ new->nfl_info.nfl_writeorder = 0; ++} ++ ++/** ++ * Convert a pair of command line arguments to one nfs_fsloc structure ++ * ++ * @param server NUL-terminated C string containing file server hostname ++ * @param rootpath NUL-terminated C string containing POSIX-style export path ++ * @param fsloc OUT: NFS location structure ++ * @return a FedFsStatus code ++ * ++ * If nfsref_add_build_fsloc() returns FEDFS_OK, caller must free the ++ * returned fsloc with nfs_free_location(). ++ */ ++static FedFsStatus ++nfsref_add_build_fsloc(const char *server, const char *rootpath, ++ struct nfs_fsloc **fsloc) ++{ ++ struct nfs_fsloc *new; ++ FedFsStatus retval; ++ ++ if (server == NULL || rootpath == NULL) ++ return FEDFS_ERR_INVAL; ++ ++ xlog(D_GENERAL, "%s: Building fsloc for %s:%s", ++ __func__, server, rootpath); ++ ++ new = nfs_new_location(); ++ if (new == NULL) { ++ xlog(D_GENERAL, "%s: No memory", __func__); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ new->nfl_hostname = strdup(server); ++ if (new->nfl_hostname == NULL) { ++ nfs_free_location(new); ++ xlog(D_GENERAL, "%s: No memory", __func__); ++ return FEDFS_ERR_SVRFAULT; ++ } ++ ++ retval = nsdb_posix_to_path_array(rootpath, &new->nfl_rootpath); ++ if (retval != FEDFS_OK) { ++ nfs_free_location(new); ++ return retval; ++ } ++ ++ nfsref_add_fsloc_defaults(new); ++ *fsloc = new; ++ return FEDFS_OK; ++} ++ ++/** ++ * Convert array of command line arguments to list of nfs_fsloc structures ++ * ++ * @param argv array of pointers to NUL-terminated C strings contains arguments ++ * @param optind index of "argv" where "add" subcommand arguments start ++ * @param fslocs OUT: list of NFS locations ++ * @return a FedFsStatus code ++ * ++ * If nfsref_add_build_fsloc_list() returns FEDFS_OK, caller must free the ++ * returned list of fslocs with nfs_free_locations(). ++ */ ++static FedFsStatus ++nfsref_add_build_fsloc_list(char **argv, int optind, struct nfs_fsloc **fslocs) ++{ ++ struct nfs_fsloc *fsloc, *result = NULL; ++ FedFsStatus retval; ++ int i; ++ ++ for (i = optind + 2; argv[i] != NULL; i += 2) { ++ retval = nfsref_add_build_fsloc(argv[i], argv[i + 1], &fsloc); ++ if (retval != FEDFS_OK) { ++ nfs_free_locations(result); ++ return retval; ++ } ++ if (result == NULL) ++ result = fsloc; ++ else ++ result->nfl_next = fsloc; ++ } ++ if (result == NULL) ++ return FEDFS_ERR_INVAL; ++ ++ *fslocs = result; ++ return FEDFS_OK; ++} ++ ++/** ++ * Add NFS locations to a junction ++ * ++ * @param junct_path NUL-terminated C string containing pathname of junction ++ * @param argv array of pointers to NUL-terminated C strings contains arguments ++ * @param optind index of "argv" where "add" subcommand arguments start ++ * @return program exit status ++ */ ++static int ++nfsref_add_nfs_basic(const char *junct_path, char **argv, int optind) ++{ ++ struct nfs_fsloc *fslocs = NULL; ++ FedFsStatus retval; ++ ++ xlog(D_GENERAL, "%s: Adding basic junction to %s", ++ __func__, junct_path); ++ ++ retval = nfsref_add_build_fsloc_list(argv, optind, &fslocs); ++ switch (retval) { ++ case FEDFS_OK: ++ break; ++ case FEDFS_ERR_INVAL: ++ xlog(L_ERROR, "Missing arguments"); ++ return EXIT_FAILURE; ++ case FEDFS_ERR_SVRFAULT: ++ xlog(L_ERROR, "No memory"); ++ return EXIT_FAILURE; ++ default: ++ xlog(L_ERROR, "Failed to add NFS location metadata to %s: %s", ++ junct_path, nsdb_display_fedfsstatus(retval)); ++ return EXIT_FAILURE; ++ } ++ ++ retval = nfs_add_junction(junct_path, fslocs); ++ nfs_free_locations(fslocs); ++ switch (retval) { ++ case FEDFS_OK: ++ break; ++ case FEDFS_ERR_EXIST: ++ xlog(L_ERROR, "%s already contains junction metadata", ++ junct_path); ++ return EXIT_FAILURE; ++ default: ++ xlog(L_ERROR, "Failed to add NFS location metadata to %s: %s", ++ junct_path, nsdb_display_fedfsstatus(retval)); ++ return EXIT_FAILURE; ++ } ++ ++ printf("Created junction %s\n", junct_path); ++ return EXIT_SUCCESS; ++} ++ ++/** ++ * Add locations to a junction ++ * ++ * @param type type of junction to add ++ * @param junct_path NUL-terminated C string containing pathname of junction ++ * @param argv array of pointers to NUL-terminated C strings contains arguments ++ * @param optind index of "argv" where "add" subcommand arguments start ++ * @return program exit status ++ */ ++int ++nfsref_add(enum nfsref_type type, const char *junct_path, char **argv, int optind) ++{ ++ if (mkdir(junct_path, 0755) == -1) ++ if (errno != EEXIST) { ++ xlog(L_ERROR, "Failed to create junction object: %m"); ++ return EXIT_FAILURE; ++ } ++ ++ switch (type) { ++ case NFSREF_TYPE_UNSPECIFIED: ++ case NFSREF_TYPE_NFS_BASIC: ++ return nfsref_add_nfs_basic(junct_path, argv, optind); ++ default: ++ xlog(L_ERROR, "Unrecognized junction type"); ++ } ++ return EXIT_FAILURE; ++} +diff --git a/utils/nfsref/lookup.c b/utils/nfsref/lookup.c +new file mode 100644 +index 0000000..16fca2e +--- /dev/null ++++ b/utils/nfsref/lookup.c +@@ -0,0 +1,211 @@ ++/** ++ * @file utils/nfsref/lookup.c ++ * @brief Examine junction metadata from a local file system object ++ */ ++ ++/* ++ * Copyright 2011, 2018 Oracle. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2.0 as ++ * published by the Free Software Foundation. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License version 2.0 for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * version 2.0 along with nfs-utils. If not, see: ++ * ++ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "junction.h" ++#include "xlog.h" ++#include "nfsref.h" ++ ++/** ++ * Display help message for "lookup" subcommand ++ * ++ * @param progname NUL-terminated C string containing name of program ++ * @return program exit status ++ */ ++int ++nfsref_lookup_help(const char *progname) ++{ ++ fprintf(stderr, " \n"); ++ ++ fprintf(stderr, "Usage: %s [ -t type ] lookup \n\n", ++ progname); ++ ++ fprintf(stderr, "Display the contents of the junction at " ++ ". For NFS basic\n"); ++ fprintf(stderr, "junctions, the local contents of the junction " ++ "are displayed. For FedFS\n"); ++ fprintf(stderr, "junctions, FSL records are retrieved from the " ++ "NSDB and displayed.\n"); ++ ++ return EXIT_SUCCESS; ++} ++ ++/** ++ * Convert a boolean value into a displayable string constant ++ * ++ * @param value boolean value ++ * @return NUL-terminated static constant C string ++ */ ++static const char * ++nfsref_lookup_display_boolean(_Bool value) ++{ ++ return value ? "true" : "false"; ++} ++ ++/** ++ * Display a single NFS location ++ * ++ * @param fsloc pointer to an NFS location structure ++ */ ++static void ++nfsref_lookup_display_nfs_location(struct nfs_fsloc *fsloc) ++{ ++ char *rootpath; ++ ++ if (nsdb_path_array_to_posix(fsloc->nfl_rootpath, &rootpath) == FEDFS_OK) { ++ printf("%s:%s\n", fsloc->nfl_hostname, rootpath); ++ free(rootpath); ++ } else ++ printf("%s: - Invalid root path -\n", fsloc->nfl_hostname); ++ printf("\n"); ++ ++ printf("\tNFS port:\t%u\n", fsloc->nfl_hostport); ++ printf("\tValid for:\t%d\n", fsloc->nfl_validfor); ++ printf("\tCurrency:\t%d\n", fsloc->nfl_currency); ++ printf("\tFlags:\t\tvarsub(%s)\n", ++ nfsref_lookup_display_boolean(fsloc->nfl_flags.nfl_varsub)); ++ ++ printf("\tGenFlags:\twritable(%s), going(%s), split(%s)\n", ++ nfsref_lookup_display_boolean(fsloc->nfl_genflags.nfl_writable), ++ nfsref_lookup_display_boolean(fsloc->nfl_genflags.nfl_going), ++ nfsref_lookup_display_boolean(fsloc->nfl_genflags.nfl_split)); ++ printf("\tTransFlags:\trdma(%s)\n", ++ nfsref_lookup_display_boolean(fsloc->nfl_transflags.nfl_rdma)); ++ ++ printf("\tClass:\t\tsimul(%u), handle(%u), fileid(%u)\n", ++ fsloc->nfl_info.nfl_simul, ++ fsloc->nfl_info.nfl_handle, ++ fsloc->nfl_info.nfl_fileid); ++ printf("\tClass:\t\twritever(%u), change(%u), readdir(%u)\n", ++ fsloc->nfl_info.nfl_writever, ++ fsloc->nfl_info.nfl_change, ++ fsloc->nfl_info.nfl_readdir); ++ printf("\tRead:\t\trank(%u), order(%u)\n", ++ fsloc->nfl_info.nfl_readrank, fsloc->nfl_info.nfl_readorder); ++ printf("\tWrite:\t\trank(%u), order(%u)\n", ++ fsloc->nfl_info.nfl_writerank, fsloc->nfl_info.nfl_writeorder); ++ ++ printf("\n"); ++} ++ ++/** ++ * Display a list of NFS locations ++ * ++ * @param fslocs list of NFS locations to display ++ */ ++static void ++nfsref_lookup_display_nfs_locations(struct nfs_fsloc *fslocs) ++{ ++ struct nfs_fsloc *fsloc; ++ ++ for (fsloc = fslocs; fsloc != NULL; fsloc = fsloc->nfl_next) ++ nfsref_lookup_display_nfs_location(fsloc); ++} ++ ++/** ++ * List NFS locations in an nfs-basic junction ++ * ++ * @param junct_path NUL-terminated C string containing pathname of junction ++ * @return program exit status ++ */ ++static int ++nfsref_lookup_nfs_basic(const char *junct_path) ++{ ++ struct nfs_fsloc *fslocs = NULL; ++ FedFsStatus retval; ++ ++ xlog(D_GENERAL, "%s: Looking up basic junction in %s", ++ __func__, junct_path); ++ ++ retval = nfs_is_junction(junct_path); ++ switch (retval) { ++ case FEDFS_OK: ++ break; ++ case FEDFS_ERR_NOTJUNCT: ++ xlog(L_ERROR, "%s is not an nfs-basic junction", junct_path); ++ return EXIT_FAILURE; ++ default: ++ xlog(L_ERROR, "Failed to access %s: %s", ++ junct_path, nsdb_display_fedfsstatus(retval)); ++ return EXIT_FAILURE; ++ } ++ ++ retval = nfs_get_locations(junct_path, &fslocs); ++ if (retval != FEDFS_OK) { ++ xlog(L_ERROR, "Failed to access %s: %s", ++ junct_path, nsdb_display_fedfsstatus(retval)); ++ return EXIT_FAILURE; ++ } ++ ++ nfsref_lookup_display_nfs_locations(fslocs); ++ ++ nfs_free_locations(fslocs); ++ return EXIT_SUCCESS; ++} ++ ++/** ++ * Resolve either a FedFS or NFS basic junction ++ * ++ * @param junct_path NUL-terminated C string containing pathname of junction ++ * @return program exit status ++ */ ++static int ++nfsref_lookup_unspecified(const char *junct_path) ++{ ++ FedFsStatus retval; ++ ++ retval = nfs_is_junction(junct_path); ++ if (retval == FEDFS_OK) ++ return nfsref_lookup_nfs_basic(junct_path); ++ xlog(L_ERROR, "%s is not a junction", junct_path); ++ return EXIT_FAILURE; ++} ++ ++/** ++ * Enumerate metadata of a junction ++ * ++ * @param type type of junction to add ++ * @param junct_path NUL-terminated C string containing pathname of junction ++ * @return program exit status ++ */ ++int ++nfsref_lookup(enum nfsref_type type, const char *junct_path) ++{ ++ switch (type) { ++ case NFSREF_TYPE_UNSPECIFIED: ++ return nfsref_lookup_unspecified(junct_path); ++ case NFSREF_TYPE_NFS_BASIC: ++ return nfsref_lookup_nfs_basic(junct_path); ++ default: ++ xlog(L_ERROR, "Unrecognized junction type"); ++ } ++ return EXIT_FAILURE; ++} +diff --git a/utils/nfsref/nfsref.c b/utils/nfsref/nfsref.c +new file mode 100644 +index 0000000..7f97d01 +--- /dev/null ++++ b/utils/nfsref/nfsref.c +@@ -0,0 +1,189 @@ ++/** ++ * @file utils/nfsref/nfsref.c ++ * @brief Manage NFS referrals ++ */ ++ ++/* ++ * Copyright 2011, 2018 Oracle. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2.0 as ++ * published by the Free Software Foundation. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License version 2.0 for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * version 2.0 along with nfs-utils. If not, see: ++ * ++ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "junction.h" ++#include "xlog.h" ++#include "nfsref.h" ++ ++/** ++ * Short form command line options ++ */ ++static const char nfsref_opts[] = "?dt:"; ++ ++/** ++ * Long form command line options ++ */ ++static const struct option nfsref_longopts[] = { ++ { "debug", 0, NULL, 'd', }, ++ { "help", 0, NULL, '?', }, ++ { "type", 1, NULL, 't', }, ++ { NULL, 0, NULL, 0, }, ++}; ++ ++/** ++ * Display program synopsis ++ * ++ * @param progname NUL-terminated C string containing name of program ++ */ ++static void ++nfsref_usage(const char *progname) ++{ ++ fprintf(stderr, "Usage: %s [ -t type ] SUBCOMMAND [ ARGUMENTS ]\n\n", ++ progname); ++ ++ fprintf(stderr, "SUBCOMMAND is one of:\n"); ++ fprintf(stderr, "\tadd Add a new junction\n"); ++ fprintf(stderr, "\tremove Remove an existing junction\n"); ++ fprintf(stderr, "\tlookup Enumerate a junction\n"); ++ ++ fprintf(stderr, "\nUse \"%s SUBCOMMAND -?\" for details.\n", progname); ++} ++ ++/** ++ * Program entry point ++ * ++ * @param argc count of command line arguments ++ * @param argv array of NUL-terminated C strings containing command line arguments ++ * @return program exit status ++ */ ++int ++main(int argc, char **argv) ++{ ++ char *progname, *subcommand, *junct_path; ++ enum nfsref_type type; ++ int arg, exit_status; ++ _Bool help; ++ ++ (void)setlocale(LC_ALL, ""); ++ (void)umask(S_IWGRP | S_IWOTH); ++ ++ exit_status = EXIT_FAILURE; ++ ++ /* Set the basename */ ++ if ((progname = strrchr(argv[0], '/')) != NULL) ++ progname++; ++ else ++ progname = argv[0]; ++ ++ xlog_stderr(1); ++ xlog_syslog(0); ++ xlog_open(progname); ++ ++ if (argc < 2) { ++ nfsref_usage(progname); ++ goto out; ++ } ++ ++ help = false; ++ type = NFSREF_TYPE_UNSPECIFIED; ++ while ((arg = getopt_long(argc, argv, nfsref_opts, ++ nfsref_longopts, NULL)) != -1) { ++ switch (arg) { ++ case 'd': ++ xlog_config(D_ALL, 1); ++ break; ++ case 't': ++ if (strcmp(optarg, "nfs-basic") == 0) ++ type = NFSREF_TYPE_NFS_BASIC; ++ else if (strcmp(optarg, "nfs-fedfs") == 0) ++ type = NFSREF_TYPE_NFS_FEDFS; ++ else { ++ xlog(L_ERROR, ++ "Unrecognized junction type: %s", ++ optarg); ++ exit(EXIT_FAILURE); ++ } ++ break; ++ case '?': ++ help = true; ++ } ++ } ++ ++ if (argc < optind + 1) { ++ nfsref_usage(progname); ++ goto out; ++ } ++ ++ if (!help && geteuid() != 0) { ++ xlog(L_ERROR, "Root permission is required"); ++ goto out; ++ } ++ ++ subcommand = argv[optind]; ++ junct_path = argv[optind + 1]; ++ ++ if (strcasecmp(subcommand, "add") == 0) { ++ if (help) { ++ exit_status = nfsref_add_help(progname); ++ goto out; ++ } ++ if (argc < optind + 3) { ++ xlog(L_ERROR, "Not enough positional parameters"); ++ nfsref_usage(progname); ++ goto out; ++ } ++ exit_status = nfsref_add(type, junct_path, argv, optind); ++ if (exit_status == EXIT_SUCCESS) ++ (void)junction_flush_exports_cache(); ++ } else if (strcasecmp(subcommand, "remove") == 0) { ++ if (help) { ++ exit_status = nfsref_remove_help(progname); ++ goto out; ++ } ++ exit_status = nfsref_remove(type, junct_path); ++ if (exit_status == EXIT_SUCCESS) ++ (void)junction_flush_exports_cache(); ++ } else if (strcasecmp(subcommand, "lookup") == 0) { ++ if (help) { ++ exit_status = nfsref_lookup_help(progname); ++ goto out; ++ } ++ exit_status = nfsref_lookup(type, junct_path); ++ } else { ++ xlog(L_ERROR, "Unrecognized subcommand: %s", subcommand); ++ nfsref_usage(progname); ++ } ++ ++out: ++ exit(exit_status); ++} +diff --git a/utils/nfsref/nfsref.h b/utils/nfsref/nfsref.h +new file mode 100644 +index 0000000..bf0e70e +--- /dev/null ++++ b/utils/nfsref/nfsref.h +@@ -0,0 +1,47 @@ ++/** ++ * @file support/nfsref/nfsref.h ++ * @brief Declarations and definitions for nfsref command line tool ++ */ ++ ++/* ++ * Copyright 2011, 2018 Oracle. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2.0 as ++ * published by the Free Software Foundation. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License version 2.0 for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * version 2.0 along with nfs-utils. If not, see: ++ * ++ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++ */ ++ ++#ifndef UTILS_NFSREF_H ++#define UTILS_NFSREF_H ++ ++/** ++ * Junction types supported by the "nfsref" command ++ */ ++enum nfsref_type { ++ NFSREF_TYPE_UNSPECIFIED = 1, ++ NFSREF_TYPE_NFS_BASIC, ++ NFSREF_TYPE_NFS_FEDFS ++}; ++ ++int nfsref_add(enum nfsref_type type, const char *junct_path, char **argv, ++ int optind); ++int nfsref_remove(enum nfsref_type type, const char *junct_path); ++int nfsref_lookup(enum nfsref_type type, const char *junct_path); ++ ++int nfsref_add_help(const char *progname); ++int nfsref_remove_help(const char *progname); ++int nfsref_lookup_help(const char *progname); ++ ++#endif /* !UTILS_NFSREF_H */ +diff --git a/utils/nfsref/nfsref.man b/utils/nfsref/nfsref.man +new file mode 100644 +index 0000000..1261549 +--- /dev/null ++++ b/utils/nfsref/nfsref.man +@@ -0,0 +1,180 @@ ++.\"@(#)nfsref.8" ++.\" ++.\" @file utils/nfsref/nfsref.man ++.\" @brief man page for nfsref command ++.\" ++ ++.\" ++.\" Copyright 2011, 2018 Oracle. All rights reserved. ++.\" ++.\" This file is part of nfs-utils. ++.\" ++.\" nfs-utils is free software; you can redistribute it and/or modify ++.\" it under the terms of the GNU General Public License version 2.0 as ++.\" published by the Free Software Foundation. ++.\" ++.\" nfs-utils is distributed in the hope that it will be useful, but ++.\" WITHOUT ANY WARRANTY; without even the implied warranty of ++.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++.\" GNU General Public License version 2.0 for more details. ++.\" ++.\" You should have received a copy of the GNU General Public License ++.\" version 2.0 along with nfs-utils. If not, see: ++.\" ++.\" http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++.\" ++.TH NFSREF 8 "9 Jan 2018" ++.SH NAME ++nfsref \- manage NFS referrals ++.SH SYNOPSIS ++.B nfsref ++.RB [ \-?d ] ++.RB [ \-t ++.IB type ] ++.B add ++.I pathname server export ++.RI [ " server" ++.IR export " ... ]" ++.P ++.B nfsref ++.RB [ \-?d ] ++.RB [ \-t ++.IB type ] ++.B remove ++.I pathname ++.P ++.B nfsref ++.RB [ \-?d ] ++.RB [ \-t ++.IB type ] ++.B lookup ++.I pathname ++.SH INTRODUCTION ++NFS version 4 introduces the concept of ++.I file system referrals ++to NFS. ++A file system referral is like a symbolic link on a file server ++to another file system share, possibly on another file server. ++On an NFS client, a referral behaves like an automounted directory. ++The client, under the server's direction, mounts a new NFS export ++automatically when an application first accesses that directory. ++.P ++Referrals are typically used to construct a single file name space ++across multiple file servers. ++Because file servers control the shape of the name space, ++no client configuration is required, ++and all clients see the same referral information. ++.P ++The Linux NFS server supports NFS version 4 referrals. ++Administrators can specify the ++.B refer= ++export option in ++.I /etc/exports ++to configure a list of exports from which the client can choose. ++See ++.BR exports (5) ++for details. ++.P ++.SH DESCRIPTION ++The ++.BR nfsref (8) ++command is a simple way to get started managing junction metadata. ++Other administrative commands provide richer access to junction information. ++.SS Subcommands ++Valid ++.BR nfsref (8) ++subcommands are: ++.IP "\fBadd\fP" ++Adds junction information to the directory named by ++.IR pathname . ++The named directory must already exist, ++and must not already contain junction information. ++Regular directory contents are obscured to NFS clients by this operation. ++.IP ++A list of one or more file server and export path pairs ++is also specified on the command line. ++When creating an NFS basic junction, this list is ++stored in an extended attribute of the directory. ++.IP ++If junction creation is successful, the ++.BR nfsref (8) ++command flushes the kernel's export cache ++to remove previously cached junction information. ++.IP "\fBremove\fP" ++Removes junction information from the directory named by ++.IR pathname . ++The named directory must exist, ++and must contain junction information. ++Regular directory contents are made visible to NFS clients again by this operation. ++.IP ++If junction deletion is successful, the ++.BR nfsref (8) ++command flushes the kernel's export cache ++to remove previously cached junction information. ++.IP "\fBlookup\fP" ++Displays junction information stored in the directory named by ++.IR pathname . ++The named directory must exist, ++and must contain junction information. ++.IP ++When looking up an NFS basic junction, the junction information ++in the directory is listed on ++.IR stdout . ++.SS Command line options ++.IP "\fB\-d, \-\-debug" ++Enables debugging messages during operation. ++.IP "\fB\-t, \-\-type=\fIjunction-type\fP" ++Specifies the junction type for the operation. Valid values for ++.I junction-type ++are ++.B nfs-basic ++or ++.BR nfs-fedfs . ++.IP ++For the ++.B add ++subcommand, the default value if this option is not specified is ++.BR nfs-basic . ++For the ++.B remove ++and ++.B lookup ++subcommands, the ++.B \-\-type ++option is not required. The ++.BR nfsref (8) ++command operates on whatever junction contents are available. ++.SH EXAMPLES ++Suppose you have two file servers, ++.I top.example.net ++and ++.IR home.example.net . ++You want all your clients to mount ++.I top.example.net:/ ++and then see the files under ++.I home.example.net:/ ++automatically in ++.IR top:/home . ++.P ++On ++.IR top.example.net , ++you might issue this command as root: ++.RS ++.sp ++# mkdir /home ++.br ++# nfsref --type=nfs-basic add /home home.example.net / ++.br ++Created junction /home. ++.sp ++.RE ++.SH FILES ++.TP ++.I /etc/exports ++NFS server export table ++.SH "SEE ALSO" ++.BR exports (5) ++.sp ++RFC 5661 for a description of NFS version 4 referrals ++.SH "AUTHOR" ++Chuck Lever +diff --git a/utils/nfsref/remove.c b/utils/nfsref/remove.c +new file mode 100644 +index 0000000..1a4e371 +--- /dev/null ++++ b/utils/nfsref/remove.c +@@ -0,0 +1,145 @@ ++/** ++ * @file utils/nfsref/remove.c ++ * @brief Remove junction metadata from a local file system object ++ */ ++ ++/* ++ * Copyright 2011, 2018 Oracle. All rights reserved. ++ * ++ * This file is part of nfs-utils. ++ * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2.0 as ++ * published by the Free Software Foundation. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License version 2.0 for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * version 2.0 along with nfs-utils. If not, see: ++ * ++ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "junction.h" ++#include "xlog.h" ++#include "nfsref.h" ++ ++/** ++ * Display help message for "remove" subcommand ++ * ++ * @param progname NUL-terminated C string containing name of program ++ * @return program exit status ++ */ ++int ++nfsref_remove_help(const char *progname) ++{ ++ fprintf(stderr, " \n"); ++ ++ fprintf(stderr, "Usage: %s [ -t type ] remove \n\n", ++ progname); ++ ++ fprintf(stderr, "Remove the junction at . For FedFS " ++ "junctions, FSL and FSN\n"); ++ fprintf(stderr, "records are removed from the NSDB.\n"); ++ ++ return EXIT_SUCCESS; ++} ++ ++/** ++ * Remove an NFS locations-style junction ++ * ++ * @param junct_path NUL-terminated C string containing pathname of junction ++ * @return program exit status ++ */ ++static int ++nfsref_remove_nfs_basic(const char *junct_path) ++{ ++ int status = EXIT_FAILURE; ++ FedFsStatus retval; ++ ++ xlog(D_GENERAL, "%s: Removing FedFS junction from %s", ++ __func__, junct_path); ++ ++ retval = nfs_delete_junction(junct_path); ++ switch (retval) { ++ case FEDFS_OK: ++ printf("Removed nfs-basic junction from %s\n", junct_path); ++ status = EXIT_SUCCESS; ++ break; ++ case FEDFS_ERR_NOTJUNCT: ++ xlog(L_ERROR, "%s is not an nfs-basic junction", junct_path); ++ break; ++ default: ++ xlog(L_ERROR, "Failed to delete %s: %s", ++ junct_path, nsdb_display_fedfsstatus(retval)); ++ } ++ ++ return status; ++} ++ ++/** ++ * Remove any NFS junction information ++ * ++ * @param junct_path NUL-terminated C string containing pathname of junction ++ * @return program exit status ++ */ ++static int ++nfsref_remove_unspecified(const char *junct_path) ++{ ++ FedFsStatus retval; ++ ++ xlog(D_GENERAL, "%s: Removing junction from %s", ++ __func__, junct_path); ++ ++ retval = nfs_delete_junction(junct_path); ++ if (retval != FEDFS_OK) { ++ if (retval != FEDFS_ERR_NOTJUNCT) ++ goto out_err; ++ } ++ ++ printf("Removed junction from %s\n", junct_path); ++ return EXIT_SUCCESS; ++ ++out_err: ++ switch (retval) { ++ case FEDFS_ERR_NOTJUNCT: ++ xlog(L_ERROR, "No junction information found in %s", junct_path); ++ break; ++ default: ++ xlog(L_ERROR, "Failed to delete %s: %s", ++ junct_path, nsdb_display_fedfsstatus(retval)); ++ } ++ return EXIT_FAILURE; ++} ++ ++/** ++ * Remove an NFS junction ++ * ++ * @param type type of junction to add ++ * @param junct_path NUL-terminated C string containing pathname of junction ++ * @return program exit status ++ */ ++int ++nfsref_remove(enum nfsref_type type, const char *junct_path) ++{ ++ switch (type) { ++ case NFSREF_TYPE_UNSPECIFIED: ++ return nfsref_remove_unspecified(junct_path); ++ case NFSREF_TYPE_NFS_BASIC: ++ return nfsref_remove_nfs_basic(junct_path); ++ default: ++ xlog(L_ERROR, "Unrecognized junction type"); ++ } ++ return EXIT_FAILURE; ++} +diff --git a/utils/nfsstat/nfsstat.c b/utils/nfsstat/nfsstat.c +index eddbe9a..c779053 100644 +--- a/utils/nfsstat/nfsstat.c ++++ b/utils/nfsstat/nfsstat.c +@@ -300,7 +300,7 @@ int versions[] = { + PRNT_V4 + }; + +-void usage(char *name) ++static void usage(char *name) + { + printf("Usage: %s [OPTION]...\n\ + \n\ +@@ -980,8 +980,10 @@ more_stats: + } + bufp = buf; + for (; curindex < numvals; curindex++) { ++#pragma GCC diagnostic ignored "-Wformat-nonliteral" + n = sscanf(bufp, fmt, &ip->valptr[curindex], + &numconsumed); ++#pragma GCC diagnostic warning "-Wformat-nonliteral" + if (n != 1) + break; + if (is_proc) { +diff --git a/utils/statd/statd.c b/utils/statd/statd.c +index 197d853..563a272 100644 +--- a/utils/statd/statd.c ++++ b/utils/statd/statd.c +@@ -225,7 +225,8 @@ static void set_nlm_port(char *type, int port) + fd = open(pathbuf, O_WRONLY); + if (fd < 0 && errno == ENOENT) { + /* probably module not loaded */ +- system("modprobe lockd"); ++ if (system("modprobe lockd")) ++ {/* ignore return value */}; + fd = open(pathbuf, O_WRONLY); + } + if (fd >= 0) { +diff --git a/utils/statd/svc_run.c b/utils/statd/svc_run.c +index 28c1ad6..d1dbd74 100644 +--- a/utils/statd/svc_run.c ++++ b/utils/statd/svc_run.c +@@ -56,6 +56,7 @@ + #include "statd.h" + #include "notlist.h" + ++void my_svc_exit(void); + static int svc_stop = 0; + + /* diff --git a/nfs-utils.spec b/nfs-utils.spec index 0f018ef..5843e1d 100644 --- a/nfs-utils.spec +++ b/nfs-utils.spec @@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser Name: nfs-utils URL: http://linux-nfs.org/ Version: 2.3.1 -Release: 5%{?dist} +Release: 5.rc1%{?dist} Epoch: 1 # group all 32bit related archs @@ -15,6 +15,8 @@ Source3: nfs-utils_env.sh Source4: lockd.conf Source5: 24-nfs-server.conf +Patch001: nfs-utils-2.3.2-rc1.patch + Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch Patch102: nfs-utils-1.2.5-idmap-errmsg.patch @@ -319,6 +321,9 @@ fi %{_libdir}/libnfsidmap.so %changelog +* Fri Mar 2 2018 Steve Dickson 2.3.1-5.rc1 +- Updated to latest upstream RC release: nfs-utils-2-3-2-rc1 + * Wed Feb 14 2018 Kevin Fenzi - 1:2.3.1-5 - Rebuild for new libevent