nfs-utils/nfs-utils-2.3.2-rc1.patch
Steve Dickson 595d9d2f5e Updated the nfs-utils-2.3.2-rc1.patch with the latest upstream bits.
Signed-off-by: Steve Dickson <steved@redhat.com>
2018-03-10 13:11:58 -05:00

6481 lines
175 KiB
Diff

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 <stdint.h>
+
+/*
+ * 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 <config.h>
#endif
-#ifdef HAVE_LIBIO_H
-#include <libio.h>
-#endif
+#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/socket.h>
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 <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#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 <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+
+
+#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 <libxml/tree.h>
+#include <libxml/xpath.h>
+
+/**
+ ** 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 <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <wchar.h>
+#include <memory.h>
+#include <signal.h>
+#include <errno.h>
+#include <dirent.h>
+
+#include <sys/xattr.h>
+
+#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 <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#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:
+ *
+ * <?xml version="1.0" encoding="UTF-8"?>
+ * <junction>
+ * <savedmode bits="1777" />
+ * <fileset>
+ * <location>
+ * <host name="fileserver.example.net" port="2049" />
+ * <path>
+ * <component>foo</component>
+ * <component>bar</component>
+ * <component>baz</component>
+ * </path>
+ * <currency>-1</currency>
+ * <genflags writable="false" going="false" split="true" />
+ * <transflags rdma="true" />
+ * <class simul="0" handle="0" fileid="0"
+ * writever="0" change="0" readdir="0" />
+ * <read rank="0" order="0" />
+ * <write rank="0" order="0" />
+ * <flags varsub="false" />
+ * <validfor>0</validfor>
+ * </location>
+ *
+ * ....
+ *
+ * </fileset>
+ * </junction>
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rpcsvc/nfs_prot.h>
+
+#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 <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdbool.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+
+#include <netinet/in.h>
+
+#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 <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#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 <string.h>
#include "xcommon.h"
#include <sys/stat.h>
+#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 <unistd.h>
#include <errno.h>
+#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 <dirent.h>
#include <errno.h>
+#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 <ctype.h> /* for isdigit */
#include <sys/stat.h> /* for umask */
#include <unistd.h> /* for ftruncate */
+#include <errno.h> /* 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 <unistd.h>
#include <time.h>
#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 <sys/fcntl.h>
#include <errno.h>
#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 <ctype.h>
+#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
# <age> 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: <NULL> "));
}
- 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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
#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 <errno.h>
#include <unistd.h>
#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
@@ -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 <stdlib.h>
#include <ctype.h>
#include <string.h>
+#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..ee20d54 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;
@@ -400,15 +400,17 @@ uint64_t dm_device_create(struct bl_volume *vols, int num_vols)
}
dev = node->bv_vols[0]->param.bv_dev;
tmp = table->params;
- BL_LOG_INFO("%s: major %lu minor %lu", __func__,
- MAJOR(dev), MINOR(dev));
+ BL_LOG_INFO("%s: major %llu minor %llu", __func__,
+ (long long unsigned)MAJOR(dev),
+ (long long unsigned)MINOR(dev));
if (!dm_format_dev(tmp, DM_PARAMS_LEN,
MAJOR(dev), MINOR(dev))) {
free(table);
goto out;
}
tmp += strlen(tmp);
- sprintf(tmp, " %lu", node->param.bv_offset);
+ sprintf(tmp, " %llu",
+ (long long unsigned)node->param.bv_offset);
add_to_bl_dm_table(&bl_table_head, table);
break;
case BLOCK_VOLUME_STRIPE:
@@ -462,7 +464,8 @@ uint64_t dm_device_create(struct bl_volume *vols, int num_vols)
tmp = table->params;
dev = node->bv_vols[i]->param.bv_dev;
BL_LOG_INFO("%s: major %lu minor %lu", __func__,
- MAJOR(dev), MINOR(dev));
+ (long unsigned int)MAJOR(dev),
+ (long unsigned int)MINOR(dev));
if (!dm_format_dev(tmp, DM_PARAMS_LEN,
MAJOR(dev), MINOR(dev))) {
free(table);
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 <stdarg.h>
#include <string.h>
#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 : "<none selected>");
- 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 <gssapi/gssapi.h>
#include <string.h>
+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 <dlfcn.h>
-#include <link.h>
-#include <nfs-plugin.h>
+#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 <rpc/rpc_com.h>
#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 <linux/limits.h>
#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 <sys/stat.h>
+#include <sys/types.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <uuid/uuid.h>
+
+#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 <junction path> "
+ "<server> <export> [ <server> <export> ... ]\n\n",
+ progname);
+
+ fprintf(stderr, "Add a new junction containing the specified list "
+ "of fileset locations.\n");
+ fprintf(stderr, "<junction path> is the filename of the new junction. "
+ "<server> is the hostname\n");
+ fprintf(stderr, "or IP address of an NFS server where the fileset is "
+ "located. <export> 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 <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <rpcsvc/nfs_prot.h>
+
+#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 <junction path>\n\n",
+ progname);
+
+ fprintf(stderr, "Display the contents of the junction at "
+ "<junction path>. 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 <sys/types.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <time.h>
+
+#include <locale.h>
+#include <langinfo.h>
+
+#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 <chuck.lever@oracle.com>
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 <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <unistd.h>
+#include <errno.h>
+
+#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 <junction path>\n\n",
+ progname);
+
+ fprintf(stderr, "Remove the junction at <junction path>. 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;
/*