summaryrefslogtreecommitdiffstats
path: root/support/junction
diff options
context:
space:
mode:
Diffstat (limited to 'support/junction')
-rw-r--r--support/junction/Makefile.am32
-rw-r--r--support/junction/Makefile.in715
-rw-r--r--support/junction/display.c139
-rw-r--r--support/junction/export-cache.c118
-rw-r--r--support/junction/junction-internal.h121
-rw-r--r--support/junction/junction.c498
-rw-r--r--support/junction/locations.c131
-rw-r--r--support/junction/nfs.c1564
-rw-r--r--support/junction/path.c352
-rw-r--r--support/junction/xml.c401
10 files changed, 4071 insertions, 0 deletions
diff --git a/support/junction/Makefile.am b/support/junction/Makefile.am
new file mode 100644
index 0000000..be6958b
--- /dev/null
+++ b/support/junction/Makefile.am
@@ -0,0 +1,32 @@
+##
+## @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
diff --git a/support/junction/Makefile.in b/support/junction/Makefile.in
new file mode 100644
index 0000000..8ca8ad3
--- /dev/null
+++ b/support/junction/Makefile.in
@@ -0,0 +1,715 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = support/junction
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/aclocal/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/aclocal/bsdsignals.m4 \
+ $(top_srcdir)/aclocal/getrandom.m4 \
+ $(top_srcdir)/aclocal/ipv6.m4 \
+ $(top_srcdir)/aclocal/kerberos5.m4 \
+ $(top_srcdir)/aclocal/keyutils.m4 \
+ $(top_srcdir)/aclocal/libblkid.m4 \
+ $(top_srcdir)/aclocal/libcap.m4 \
+ $(top_srcdir)/aclocal/libevent.m4 \
+ $(top_srcdir)/aclocal/libpthread.m4 \
+ $(top_srcdir)/aclocal/libsqlite3.m4 \
+ $(top_srcdir)/aclocal/libtirpc.m4 \
+ $(top_srcdir)/aclocal/libxml2.m4 \
+ $(top_srcdir)/aclocal/nfs-utils.m4 \
+ $(top_srcdir)/aclocal/rpcsec_vers.m4 \
+ $(top_srcdir)/aclocal/tcp-wrappers.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/support/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libjunction_la_LIBADD =
+am_libjunction_la_OBJECTS = display.lo export-cache.lo junction.lo \
+ locations.lo nfs.lo path.lo xml.lo
+libjunction_la_OBJECTS = $(am_libjunction_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/support/include
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/display.Plo \
+ ./$(DEPDIR)/export-cache.Plo ./$(DEPDIR)/junction.Plo \
+ ./$(DEPDIR)/locations.Plo ./$(DEPDIR)/nfs.Plo \
+ ./$(DEPDIR)/path.Plo ./$(DEPDIR)/xml.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libjunction_la_SOURCES)
+DIST_SOURCES = $(libjunction_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_CPPFLAGS = @AM_CPPFLAGS@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CXXFLAGS_FOR_BUILD = @CXXFLAGS_FOR_BUILD@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GSSD = @GSSD@
+GSSGLUE_CFLAGS = @GSSGLUE_CFLAGS@
+GSSGLUE_LIBS = @GSSGLUE_LIBS@
+GSSKRB_CFLAGS = @GSSKRB_CFLAGS@
+GSSKRB_LIBS = @GSSKRB_LIBS@
+HAVE_GETRANDOM = @HAVE_GETRANDOM@
+HAVE_LIBWRAP = @HAVE_LIBWRAP@
+HAVE_TCP_WRAPPER = @HAVE_TCP_WRAPPER@
+IDMAPD = @IDMAPD@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+K5VERS = @K5VERS@
+KRBCFLAGS = @KRBCFLAGS@
+KRBDIR = @KRBDIR@
+KRBLDFLAGS = @KRBLDFLAGS@
+KRBLIBS = @KRBLIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBLKID = @LIBBLKID@
+LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
+LIBCRYPT = @LIBCRYPT@
+LIBEVENT = @LIBEVENT@
+LIBKEYUTILS = @LIBKEYUTILS@
+LIBMOUNT = @LIBMOUNT@
+LIBMOUNT_CFLAGS = @LIBMOUNT_CFLAGS@
+LIBMOUNT_LIBS = @LIBMOUNT_LIBS@
+LIBNSL = @LIBNSL@
+LIBOBJS = @LIBOBJS@
+LIBPTHREAD = @LIBPTHREAD@
+LIBS = @LIBS@
+LIBSOCKET = @LIBSOCKET@
+LIBSQLITE = @LIBSQLITE@
+LIBTIRPC = @LIBTIRPC@
+LIBTOOL = @LIBTOOL@
+LIBWRAP = @LIBWRAP@
+LIBXML2 = @LIBXML2@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_PLUGINS = @PATH_PLUGINS@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+RANLIB = @RANLIB@
+RELEASE = @RELEASE@
+RPCGEN_PATH = @RPCGEN_PATH@
+RPCSECGSS_CFLAGS = @RPCSECGSS_CFLAGS@
+RPCSECGSS_LIBS = @RPCSECGSS_LIBS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SVCGSSD = @SVCGSSD@
+TIRPC_CFLAGS = @TIRPC_CFLAGS@
+TIRPC_LIBS = @TIRPC_LIBS@
+VERSION = @VERSION@
+XML2_CFLAGS = @XML2_CFLAGS@
+XML2_LIBS = @XML2_LIBS@
+_rpc_pipefsmount = @_rpc_pipefsmount@
+_statedir = @_statedir@
+_sysconfdir = @_sysconfdir@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+enable_gss = @enable_gss@
+enable_ipv6 = @enable_ipv6@
+enable_mountconfig = @enable_mountconfig@
+enable_nfsv4 = @enable_nfsv4@
+enable_nfsv41 = @enable_nfsv41@
+enable_svcgss = @enable_svcgss@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+kprefix = @kprefix@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+mountfile = @mountfile@
+nfsconfig = @nfsconfig@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+rpc_pipefsmount = @rpc_pipefsmount@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+startstatd = @startstatd@
+statdpath = @statdpath@
+statduser = @statduser@
+statedir = @statedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+unitdir = @unitdir@
+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
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu support/junction/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu support/junction/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libjunction.la: $(libjunction_la_OBJECTS) $(libjunction_la_DEPENDENCIES) $(EXTRA_libjunction_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libjunction_la_OBJECTS) $(libjunction_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/display.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/export-cache.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/junction.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/locations.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfs.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/path.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xml.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/display.Plo
+ -rm -f ./$(DEPDIR)/export-cache.Plo
+ -rm -f ./$(DEPDIR)/junction.Plo
+ -rm -f ./$(DEPDIR)/locations.Plo
+ -rm -f ./$(DEPDIR)/nfs.Plo
+ -rm -f ./$(DEPDIR)/path.Plo
+ -rm -f ./$(DEPDIR)/xml.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/display.Plo
+ -rm -f ./$(DEPDIR)/export-cache.Plo
+ -rm -f ./$(DEPDIR)/junction.Plo
+ -rm -f ./$(DEPDIR)/locations.Plo
+ -rm -f ./$(DEPDIR)/nfs.Plo
+ -rm -f ./$(DEPDIR)/path.Plo
+ -rm -f ./$(DEPDIR)/xml.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
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..c1ec8ff
--- /dev/null
+++ b/support/junction/junction.c
@@ -0,0 +1,498 @@
+/**
+ * @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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#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 (fstatat(fd, "", &stb, AT_NO_AUTOMOUNT|AT_EMPTY_PATH) == -1) {
+ xlog(D_GENERAL, "%s: failed to stat %s: %m",
+ __func__, path);
+ return FEDFS_ERR_ACCESS;
+ }
+
+ 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 (fstatat(fd, "", &stb, AT_NO_AUTOMOUNT|AT_EMPTY_PATH) == -1) {
+ xlog(D_GENERAL, "%s: failed to stat %s: %m",
+ __func__, path);
+ return FEDFS_ERR_ACCESS;
+ }
+
+ 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 (fstatat(fd, "", &stb, AT_NO_AUTOMOUNT|AT_EMPTY_PATH) == -1) {
+ xlog(D_GENERAL, "%s: failed to stat %s: %m",
+ __func__, path);
+ return FEDFS_ERR_ACCESS;
+ }
+
+ 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 (fstatat(fd, "", &stb, AT_NO_AUTOMOUNT|AT_EMPTY_PATH) == -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..13a1438
--- /dev/null
+++ b/support/junction/path.c
@@ -0,0 +1,352 @@
+/**
+ * @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) {
+ free(start);
+ 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) {
+ nsdb_free_string_array(result);
+ free(normalized);
+ return FEDFS_ERR_SVRFAULT;
+ }
+
+ result[i] = strndup(component, length);
+ if (result[i] == NULL) {
+ free(normalized);
+ 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..813110b
--- /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%zu.%s",
+ __func__, len, (char *)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;
+}