From 4897093455a2bf08f3db3a1132cc2f6f5484d77c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 08:03:02 +0200 Subject: Adding upstream version 1:2.6.4. Signed-off-by: Daniel Baumann --- support/junction/Makefile.am | 32 + support/junction/Makefile.in | 715 ++++++++++++++++ support/junction/display.c | 139 +++ support/junction/export-cache.c | 118 +++ support/junction/junction-internal.h | 121 +++ support/junction/junction.c | 498 +++++++++++ support/junction/locations.c | 131 +++ support/junction/nfs.c | 1564 ++++++++++++++++++++++++++++++++++ support/junction/path.c | 352 ++++++++ support/junction/xml.c | 401 +++++++++ 10 files changed, 4071 insertions(+) create mode 100644 support/junction/Makefile.am create mode 100644 support/junction/Makefile.in create mode 100644 support/junction/display.c create mode 100644 support/junction/export-cache.c create mode 100644 support/junction/junction-internal.h create mode 100644 support/junction/junction.c create mode 100644 support/junction/locations.c create mode 100644 support/junction/nfs.c create mode 100644 support/junction/path.c create mode 100644 support/junction/xml.c (limited to 'support/junction') 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 +#include +#include +#include +#include + +#include "junction.h" + +/** + * Return human-readable equivalent of a FedFsStatus value + * + * @param status FedFsStatus code + * @return a static NUL-terminated C string + */ +const char * +nsdb_display_fedfsstatus(const FedFsStatus status) +{ + switch (status) { + case FEDFS_OK: + return "FEDFS_OK"; + case FEDFS_ERR_ACCESS: + return "FEDFS_ERR_ACCESS"; + case FEDFS_ERR_BADCHAR: + return "FEDFS_ERR_BADCHAR"; + case FEDFS_ERR_BADNAME: + return "FEDFS_ERR_BADNAME"; + case FEDFS_ERR_NAMETOOLONG: + return "FEDFS_ERR_NAMETOOLONG"; + case FEDFS_ERR_LOOP: + return "FEDFS_ERR_LOOP"; + case FEDFS_ERR_BADXDR: + return "FEDFS_ERR_BADXDR"; + case FEDFS_ERR_EXIST: + return "FEDFS_ERR_EXIST"; + case FEDFS_ERR_INVAL: + return "FEDFS_ERR_INVAL"; + case FEDFS_ERR_IO: + return "FEDFS_ERR_IO"; + case FEDFS_ERR_NOSPC: + return "FEDFS_ERR_NOSPC"; + case FEDFS_ERR_NOTJUNCT: + return "FEDFS_ERR_NOTJUNCT"; + case FEDFS_ERR_NOTLOCAL: + return "FEDFS_ERR_NOTLOCAL"; + case FEDFS_ERR_PERM: + return "FEDFS_ERR_PERM"; + case FEDFS_ERR_ROFS: + return "FEDFS_ERR_ROFS"; + case FEDFS_ERR_SVRFAULT: + return "FEDFS_ERR_SVRFAULT"; + case FEDFS_ERR_NOTSUPP: + return "FEDFS_ERR_NOTSUPP"; + case FEDFS_ERR_NSDB_ROUTE: + return "FEDFS_ERR_NSDB_ROUTE"; + case FEDFS_ERR_NSDB_DOWN: + return "FEDFS_ERR_NSDB_DOWN"; + case FEDFS_ERR_NSDB_CONN: + return "FEDFS_ERR_NSDB_CONN"; + case FEDFS_ERR_NSDB_AUTH: + return "FEDFS_ERR_NSDB_AUTH"; + case FEDFS_ERR_NSDB_LDAP: + return "FEDFS_ERR_NSDB_LDAP"; + case FEDFS_ERR_NSDB_LDAP_VAL: + return "FEDFS_ERR_NSDB_LDAP_VAL"; + case FEDFS_ERR_NSDB_NONCE: + return "FEDFS_ERR_NSDB_NONCE"; + case FEDFS_ERR_NSDB_NOFSN: + return "FEDFS_ERR_NSDB_NOFSN"; + case FEDFS_ERR_NSDB_NOFSL: + return "FEDFS_ERR_NSDB_NOFSL"; + case FEDFS_ERR_NSDB_RESPONSE: + return "FEDFS_ERR_NSDB_RESPONSE"; + case FEDFS_ERR_NSDB_FAULT: + return "FEDFS_ERR_NSDB_FAULT"; + case FEDFS_ERR_NSDB_PARAMS: + return "FEDFS_ERR_NSDB_PARAMS"; + case FEDFS_ERR_NSDB_LDAP_REFERRAL: + return "FEDFS_ERR_NSDB_LDAP_REFERRAL"; + case FEDFS_ERR_NSDB_LDAP_REFERRAL_VAL: + return "FEDFS_ERR_NSDB_LDAP_REFERRAL_VAL"; + case FEDFS_ERR_NSDB_PARAMS_LDAP_REFERRAL: + return "FEDFS_ERR_NSDB_PARAMS_LDAP_REFERRAL"; + case FEDFS_ERR_PATH_TYPE_UNSUPP: + return "FEDFS_ERR_PATH_TYPE_UNSUPP"; + case FEDFS_ERR_DELAY: + return "FEDFS_ERR_DELAY"; + case FEDFS_ERR_NO_CACHE: + return "FEDFS_ERR_NO_CACHE"; + case FEDFS_ERR_UNKNOWN_CACHE: + return "FEDFS_ERR_UNKNOWN_CACHE"; + case FEDFS_ERR_NO_CACHE_UPDATE: + return "FEDFS_ERR_NO_CACHE_UPDATE"; + default: + break; + } + return "an unrecognized error code"; +} + +/** + * Display human-readable FedFsStatus on stderr + * + * @param status FedFsStatus value to display + */ +void +nsdb_print_fedfsstatus(const FedFsStatus status) +{ + if (status == FEDFS_OK) { + printf("Call completed successfully\n"); + return; + } + + fprintf(stderr, "Server returned %s\n", + nsdb_display_fedfsstatus(status)); +} diff --git a/support/junction/export-cache.c b/support/junction/export-cache.c new file mode 100644 index 0000000..4e578c9 --- /dev/null +++ b/support/junction/export-cache.c @@ -0,0 +1,118 @@ +/** + * @file support/junction/export-cache.c + * @brief Try to flush NFSD's exports cache + */ + +/* + * Copyright 2011, 2018 Oracle. All rights reserved. + * + * This file is part of nfs-utils. + * + * nfs-utils is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 as + * published by the Free Software Foundation. + * + * nfs-utils is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License version 2.0 for more details. + * + * You should have received a copy of the GNU General Public License + * version 2.0 along with nfs-utils. If not, see: + * + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +#include "junction.h" +#include "xlog.h" + +/** + * Ordered list of proc files to poke when requesting an NFSD cache flush + */ +static const char *junction_proc_files[] = { + "/proc/net/rpc/auth.unix.ip/flush", + "/proc/net/rpc/auth.unix.gid/flush", + "/proc/net/rpc/nfsd.fh/flush", + "/proc/net/rpc/nfsd.export/flush", + NULL, +}; + +/** + * Write time into one file + * + * @param pathname NUL-terminated C string containing POSIX pathname of file to write + * @param flushtime NUL-terminated C string containing current time in seconds since the Epoch + * @return a FedFsStatus code + */ +static FedFsStatus +junction_write_time(const char *pathname, const char *flushtime) +{ + FedFsStatus retval; + ssize_t len; + int fd; + + fd = open(pathname, O_RDWR); + if (fd == -1) { + xlog(D_GENERAL, "%s: Failed to open %s: %m", + __func__, pathname); + /* If the proc files don't exist, no server + * is running on this system */ + return FEDFS_ERR_NO_CACHE_UPDATE; + } + + len = write(fd, flushtime, strlen(flushtime)); + if (len != (ssize_t)strlen(flushtime)) { + xlog(D_GENERAL, "%s: Failed to write %s: %m", + __func__, pathname); + /* If the proc files exist but the update failed, + * we don't know the state of the cache */ + retval = FEDFS_ERR_UNKNOWN_CACHE; + } else + /* Cache flush succeeded */ + retval = FEDFS_OK; + + (void)close(fd); + return retval; +} + +/** + * Flush the kernel NFSD's exports cache + * + * @return a FedFsStatus code + */ +FedFsStatus +junction_flush_exports_cache(void) +{ + FedFsStatus retval; + char flushtime[20]; + unsigned int i; + time_t now; + + xlog(D_CALL, "%s: Flushing NFSD caches...", __func__); + + now = time(NULL); + if (now == -1) { + xlog(D_GENERAL, "%s: time(3) failed", __func__); + return FEDFS_ERR_SVRFAULT; + } + snprintf(flushtime, sizeof(flushtime), "%ld\n", now); + + for (i = 0; junction_proc_files[i] != NULL; i++) { + retval = junction_write_time(junction_proc_files[i], flushtime); + if (retval != FEDFS_OK) + return retval; + } + return FEDFS_OK; +} diff --git a/support/junction/junction-internal.h b/support/junction/junction-internal.h new file mode 100644 index 0000000..3dff4cc --- /dev/null +++ b/support/junction/junction-internal.h @@ -0,0 +1,121 @@ +/* + * @file support/junction/junction-internal.h + * @brief Internal declarations for libjunction.a + */ + +/* + * Copyright 2011, 2018 Oracle. All rights reserved. + * + * This file is part of nfs-utils. + * + * nfs-utils is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 as + * published by the Free Software Foundation. + * + * nfs-utils is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License version 2.0 for more details. + * + * You should have received a copy of the GNU General Public License + * version 2.0 along with nfs-utils. If not, see: + * + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + */ + +#ifndef _FEDFS_JUNCTION_INTERNAL_H_ +#define _FEDFS_JUNCTION_INTERNAL_H_ + +#include +#include + +/** + ** Names of extended attributes that store junction data + **/ + +/** + * Name of extended attribute containing saved mode bits + */ +#define JUNCTION_XATTR_NAME_MODE "trusted.junction.mode" + +/** + * Name of extended attribute containing NFS-related junction data + */ +#define JUNCTION_XATTR_NAME_NFS "trusted.junction.nfs" + + +/** + ** Names of XML elements and attributes that represent junction data + **/ + +/** + * Tag name of root element of a junction XML document + */ +#define JUNCTION_XML_ROOT_TAG (const xmlChar *)"junction" + +/** + * Tag name of fileset element of a junction XML document + */ +#define JUNCTION_XML_FILESET_TAG (const xmlChar *)"fileset" + +/** + * Tag name of savedmode element of a junction XML document + */ +#define JUNCTION_XML_SAVEDMODE_TAG (const xmlChar *)"savedmode" + +/** + * Name of mode bits attribute on a savedmode element + */ +#define JUNCTION_XML_MODEBITS_ATTR (const xmlChar *)"bits" + +/** + ** Junction helper functions + **/ + +FedFsStatus junction_open_path(const char *pathname, int *fd); +FedFsStatus junction_is_directory(int fd, const char *path); +FedFsStatus junction_is_sticky_bit_set(int fd, const char *path); +FedFsStatus junction_set_sticky_bit(int fd, const char *path); +FedFsStatus junction_is_xattr_present(int fd, const char *path, + const char *name); +FedFsStatus junction_read_xattr(int fd, const char *path, const char *name, + char **contents); +FedFsStatus junction_get_xattr(int fd, const char *path, const char *name, + void **contents, size_t *contentlen); +FedFsStatus junction_set_xattr(int fd, const char *path, const char *name, + const void *contents, const size_t contentlen); +FedFsStatus junction_remove_xattr(int fd, const char *pathname, + const char *name); +FedFsStatus junction_get_mode(const char *pathname, mode_t *mode); +FedFsStatus junction_save_mode(const char *pathname); +FedFsStatus junction_restore_mode(const char *pathname); + + +/** + ** XML helper functions + **/ + +_Bool junction_xml_is_empty(const xmlChar *content); +_Bool junction_xml_match_node_name(xmlNodePtr node, + const xmlChar *name); +xmlNodePtr junction_xml_find_child_by_name(xmlNodePtr parent, + const xmlChar *name); +_Bool junction_xml_get_bool_attribute(xmlNodePtr node, + const xmlChar *attrname, _Bool *value); +void junction_xml_set_bool_attribute(xmlNodePtr node, + const xmlChar *attrname, _Bool value); +_Bool junction_xml_get_u8_attribute(xmlNodePtr node, + const xmlChar *attrname, uint8_t *value); +_Bool junction_xml_get_int_attribute(xmlNodePtr node, + const xmlChar *attrname, int *value); +void junction_xml_set_int_attribute(xmlNodePtr node, + const xmlChar *attrname, int value); +_Bool junction_xml_get_int_content(xmlNodePtr node, int *value); +xmlNodePtr junction_xml_set_int_content(xmlNodePtr parent, + const xmlChar *name, int value); +FedFsStatus junction_xml_parse(const char *pathname, const char *name, + xmlDocPtr *doc); +FedFsStatus junction_xml_write(const char *pathname, const char *name, + xmlDocPtr doc); + +#endif /* !_FEDFS_JUNCTION_INTERNAL_H_ */ diff --git a/support/junction/junction.c b/support/junction/junction.c new file mode 100644 index 0000000..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 +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "junction.h" +#include "junction-internal.h" +#include "xlog.h" + +/** + * Open a file system object + * + * @param pathname NUL-terminated C string containing pathname of an object + * @param fd OUT: a file descriptor number is filled in + * @return a FedFsStatus code + */ +FedFsStatus +junction_open_path(const char *pathname, int *fd) +{ + int tmp; + + if (pathname == NULL || fd == NULL) + return FEDFS_ERR_INVAL; + + tmp = open(pathname, O_DIRECTORY); + if (tmp == -1) { + switch (errno) { + case EPERM: + return FEDFS_ERR_ACCESS; + case EACCES: + return FEDFS_ERR_PERM; + default: + xlog(D_GENERAL, "%s: Failed to open path %s: %m", + __func__, pathname); + return FEDFS_ERR_INVAL; + } + } + + *fd = tmp; + return FEDFS_OK; +} + +/** + * Predicate: is object a directory? + * + * @param fd an open file descriptor + * @param path NUL-terminated C string containing pathname of a directory + * @return a FedFsStatus code + */ +FedFsStatus +junction_is_directory(int fd, const char *path) +{ + struct stat stb; + + if (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 +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "junction.h" + +/** + * Free an array of NUL-terminated C strings + * + * @param array array of pointers to C strings + */ +void +nfs_free_string_array(char **array) +{ + unsigned int i; + + if (array == NULL) + return; + for (i = 0; array[i] != NULL; i++) + free(array[i]); + free(array); +} + +/** + * Duplicate an array of NUL-terminated C strings + * + * @param array array of pointers to C strings + * @return freshly allocated array of points to C strings, or NULL + * + * Caller must free the returned array with nfs_free_string_array() + */ +__attribute_malloc__ char ** +nfs_dup_string_array(char **array) +{ + unsigned int size, i; + char **result; + + if (array == NULL) + return NULL; + + for (size = 0; array[size] != NULL; size++); + + result = calloc(size + 1, sizeof(char *)); + if (result == NULL) + return NULL; + for (i = 0; i < size; i++) { + result[i] = strdup(array[i]); + if (result[i] == NULL) { + nfs_free_string_array(result); + return NULL; + } + } + return result; +} + +/** + * Free a single NFS location + * + * @param location pointer to nfs_fsloc data + */ +void +nfs_free_location(struct nfs_fsloc *location) +{ + nfs_free_string_array(location->nfl_rootpath); + free(location->nfl_hostname); + free(location); +} + +/** + * Free a list of NFS locations + * + * @param locations pointer to list of one or more locations + */ +void +nfs_free_locations(struct nfs_fsloc *locations) +{ + struct nfs_fsloc *fsloc; + + while (locations != NULL) { + fsloc = locations; + locations = fsloc->nfl_next; + nfs_free_location(fsloc); + } +} + +/** + * Allocate a fresh nfs_fsloc structure + * + * @return pointer to new empty nfs_fsloc data structure + * + * Caller must free returned locations with nfs_free_location(). + */ +struct nfs_fsloc * +nfs_new_location(void) +{ + return calloc(1, sizeof(struct nfs_fsloc)); +} diff --git a/support/junction/nfs.c b/support/junction/nfs.c new file mode 100644 index 0000000..73e3533 --- /dev/null +++ b/support/junction/nfs.c @@ -0,0 +1,1564 @@ +/** + * @file support/junction/nfs.c + * @brief Create, delete, and read NFS junctions on the local file system + */ + +/* + * Copyright 2011, 2018 Oracle. All rights reserved. + * + * This file is part of nfs-utils. + * + * nfs-utils is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 as + * published by the Free Software Foundation. + * + * nfs-utils is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License version 2.0 for more details. + * + * You should have received a copy of the GNU General Public License + * version 2.0 along with nfs-utils. If not, see: + * + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + */ + +/* + * An NFS junction is a list of NFS FSLs, represented in a well-formed XML + * document: + * + * + * + * + * + * + * + * + * foo + * bar + * baz + * + * -1 + * + * + * + * + * + * + * 0 + * + * + * .... + * + * + * + * + * NFS junction XML is stored in an extended attribute called + * "trusted.junction.nfs". The parent object is a directory. + * + * To help file servers discover junctions efficiently, the directory + * has no execute bits, and the sticky bit is set. In addition, an + * extended attribute called "trusted.junction.type" is added. The + * contents are ignored in user space. + * + * Finally, for pre-existing directories that are converted to + * junctions, their mode bits are saved in an extended attribute called + * "trusted.junction.mode". When the junction data is removed, the + * directory's mode bits are restored from this information. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "junction.h" +#include "junction-internal.h" +#include "xlog.h" + +/** + * Tag name of NFS location element of a junction XML document + */ +#define NFS_XML_LOCATION_TAG (const xmlChar *)"location" + +/** + * Tag name of host child element of an NFS location element + */ +#define NFS_XML_HOST_TAG (const xmlChar *)"host" + +/** + * Name of hostname attribute of a host element + */ +#define NFS_XML_HOST_NAME_ATTR (const xmlChar *)"name" + +/** + * Name of IP port attribute of a host element + */ +#define NFS_XML_HOST_PORT_ATTR (const xmlChar *)"port" + +/** + * Tag name of path child element of an NFS location element + */ +#define NFS_XML_PATH_TAG (const xmlChar *)"path" + +/** + * Tag name of component child element of a path element + */ +#define NFS_XML_COMPONENT_TAG (const xmlChar *)"component" + +/** + * Tag name of currency child element of an NFS location element + */ +#define NFS_XML_CURRENCY_TAG (const xmlChar *)"currency" + +/** + * Tag name of genflags child element of an NFS location element + */ +#define NFS_XML_GENFLAGS_TAG (const xmlChar *)"genflags" + +/** + * Name of writable attribute of a genflags element + */ +#define NFS_XML_GENFLAGS_WRITABLE_ATTR (const xmlChar *)"writable" + +/** + * Name of going attribute of a genflags element + */ +#define NFS_XML_GENFLAGS_GOING_ATTR (const xmlChar *)"going" + +/** + * Name of split attribute of a genflags element + */ +#define NFS_XML_GENFLAGS_SPLIT_ATTR (const xmlChar *)"split" + +/** + * Tag name of transflags child element of an NFS location element + */ +#define NFS_XML_TRANSFLAGS_TAG (const xmlChar *)"transflags" + +/** + * Name of rdma attribute of a transflags element + */ +#define NFS_XML_TRANSFLAGS_RDMA_ATTR (const xmlChar *)"rdma" + +/** + * Tag name of class child element of an NFS location element + */ +#define NFS_XML_CLASS_TAG (const xmlChar *)"class" + +/** + * Name of simul attribute of a class element + */ +#define NFS_XML_CLASS_SIMUL_ATTR (const xmlChar *)"simul" + +/** + * Name of handle attribute of a class element + */ +#define NFS_XML_CLASS_HANDLE_ATTR (const xmlChar *)"handle" + +/** + * Name of fileid attribute of a class element + */ +#define NFS_XML_CLASS_FILEID_ATTR (const xmlChar *)"fileid" + +/** + * Name of writever attribute of a class element + */ +#define NFS_XML_CLASS_WRITEVER_ATTR (const xmlChar *)"writever" + +/** + * Name of change attribute of a class element + */ +#define NFS_XML_CLASS_CHANGE_ATTR (const xmlChar *)"change" + +/** + * Name of readdir attribute of a class element + */ +#define NFS_XML_CLASS_READDIR_ATTR (const xmlChar *)"readdir" + +/** + * Tag name of read child element of an NFS location element + */ +#define NFS_XML_READ_TAG (const xmlChar *)"read" + +/** + * Name of rank attribute of a read element + */ +#define NFS_XML_READ_RANK_ATTR (const xmlChar *)"rank" + +/** + * Name of order attribute of a read element + */ +#define NFS_XML_READ_ORDER_ATTR (const xmlChar *)"order" + +/** + * Tag name of write attribute of an NFS location element + */ +#define NFS_XML_WRITE_TAG (const xmlChar *)"write" + +/** + * Name of rank attribute of a write element + */ +#define NFS_XML_WRITE_RANK_ATTR (const xmlChar *)"rank" + +/** + * Name of order attribute of a write element + */ +#define NFS_XML_WRITE_ORDER_ATTR (const xmlChar *)"order" + +/** + * Tag name of flags child element of an NFS location element + */ +#define NFS_XML_FLAGS_TAG (const xmlChar *)"flags" + +/** + * Name of varsub attribute of a flags element + */ +#define NFS_XML_FLAGS_VARSUB_ATTR (const xmlChar *)"varsub" + +/** + * Tag name of a validfor child element of an NFS location element + */ +#define NFS_XML_VALIDFOR_TAG (const xmlChar *)"validfor" + +/** + * XPath path to NFS location elements in a junction document + */ +#define NFS_XML_LOCATION_XPATH (const xmlChar *) \ + "/junction/fileset/location" + + +/** + * Remove all NFS-related xattrs from a directory + * + * @param pathname NUL-terminated C string containing pathname of a directory + * @return a FedFsStatus code + * + * @note Access to trusted attributes requires CAP_SYS_ADMIN. + */ +static FedFsStatus +nfs_remove_locations(const char *pathname) +{ + FedFsStatus retval; + int fd; + + retval = junction_open_path(pathname, &fd); + if (retval != FEDFS_OK) + return retval; + + retval = junction_remove_xattr(fd, pathname, JUNCTION_XATTR_NAME_NFS); + + (void)close(fd); + return retval; +} + +/** + * Add a "host" child to a "location" element + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param parent parent element to which to add "host" child + * @param fsloc NFS location containing host information to add + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_location_host_xml(const char *pathname, xmlNodePtr parent, + struct nfs_fsloc *fsloc) +{ + uint16_t port = fsloc->nfl_hostport; + xmlNodePtr new; + + new = xmlNewTextChild(parent, NULL, NFS_XML_HOST_TAG, NULL); + if (new == NULL) { + xlog(D_GENERAL, "%s: Failed to add host element for %s", + __func__, pathname); + return FEDFS_ERR_SVRFAULT; + } + + xmlSetProp(new, NFS_XML_HOST_NAME_ATTR, + (const xmlChar *)fsloc->nfl_hostname); + if (port != NFS_PORT && port != 0) + junction_xml_set_int_attribute(new, NFS_XML_HOST_PORT_ATTR, + port); + + return FEDFS_OK; +} + +/** + * Add a "path" child to a "location" element + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param parent parent element to which to add "host" child + * @param fsloc NFS location containing host information to add + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_location_path_xml(const char *pathname, xmlNodePtr parent, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr new; + int i; + + new = xmlNewTextChild(parent, NULL, NFS_XML_PATH_TAG, NULL); + if (new == NULL) { + xlog(D_GENERAL, "%s: Failed to add path element for %s", + __func__, pathname); + return FEDFS_ERR_SVRFAULT; + } + + for (i = 0; fsloc->nfl_rootpath[i] != NULL; i++) { + xmlNodePtr component; + + component = xmlNewTextChild(new , NULL, + NFS_XML_COMPONENT_TAG, + (const xmlChar *) + fsloc->nfl_rootpath[i]); + if (component == NULL) { + xlog(D_GENERAL, "%s: Failed to add component " + "element for %s", + __func__, pathname); + return FEDFS_ERR_SVRFAULT; + } + } + + return FEDFS_OK; +} + +/** + * Add a "currency" child to a "location" element + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param parent parent element to which to add "host" child + * @param fsloc NFS location containing host information to add + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_location_currency_xml(__attribute__((unused)) const char *pathname, + xmlNodePtr parent, struct nfs_fsloc *fsloc) +{ + if (junction_xml_set_int_content(parent, NFS_XML_CURRENCY_TAG, + fsloc->nfl_currency) == NULL) + return FEDFS_ERR_SVRFAULT; + return FEDFS_OK; +} + +/** + * Add a "genflags" child to a "location" element + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param parent parent element to which to add "host" child + * @param fsloc NFS location containing host information to add + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_location_genflags_xml(const char *pathname, xmlNodePtr parent, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr new; + + new = xmlNewTextChild(parent, NULL, NFS_XML_GENFLAGS_TAG, NULL); + if (new == NULL) { + xlog(D_GENERAL, "%s: Failed to add genflags element for %s", + __func__, pathname); + return FEDFS_ERR_SVRFAULT; + } + + junction_xml_set_bool_attribute(new, NFS_XML_GENFLAGS_WRITABLE_ATTR, + fsloc->nfl_genflags.nfl_writable); + junction_xml_set_bool_attribute(new, NFS_XML_GENFLAGS_GOING_ATTR, + fsloc->nfl_genflags.nfl_going); + junction_xml_set_bool_attribute(new, NFS_XML_GENFLAGS_SPLIT_ATTR, + fsloc->nfl_genflags.nfl_split); + + return FEDFS_OK; +} + +/** + * Add a "transflags" child to a "location" element + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param parent parent element to which to add "host" child + * @param fsloc NFS location containing host information to add + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_location_transflags_xml(const char *pathname, xmlNodePtr parent, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr new; + + new = xmlNewTextChild(parent, NULL, NFS_XML_TRANSFLAGS_TAG, NULL); + if (new == NULL) { + xlog(D_GENERAL, "%s: Failed to add transflags element for %s", + __func__, pathname); + return FEDFS_ERR_SVRFAULT; + } + + junction_xml_set_bool_attribute(new, NFS_XML_TRANSFLAGS_RDMA_ATTR, + fsloc->nfl_transflags.nfl_rdma); + + return FEDFS_OK; +} + +/** + * Add a "class" child to a "location" element + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param parent parent element to which to add "host" child + * @param fsloc NFS location containing host information to add + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_location_class_xml(const char *pathname, xmlNodePtr parent, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr new; + + new = xmlNewTextChild(parent, NULL, NFS_XML_CLASS_TAG, NULL); + if (new == NULL) { + xlog(D_GENERAL, "%s: Failed to add class element for %s", + __func__, pathname); + return FEDFS_ERR_SVRFAULT; + } + + junction_xml_set_int_attribute(new, NFS_XML_CLASS_SIMUL_ATTR, + fsloc->nfl_info.nfl_simul); + junction_xml_set_int_attribute(new, NFS_XML_CLASS_HANDLE_ATTR, + fsloc->nfl_info.nfl_handle); + junction_xml_set_int_attribute(new, NFS_XML_CLASS_FILEID_ATTR, + fsloc->nfl_info.nfl_fileid); + junction_xml_set_int_attribute(new, NFS_XML_CLASS_WRITEVER_ATTR, + fsloc->nfl_info.nfl_writever); + junction_xml_set_int_attribute(new, NFS_XML_CLASS_CHANGE_ATTR, + fsloc->nfl_info.nfl_change); + junction_xml_set_int_attribute(new, NFS_XML_CLASS_READDIR_ATTR, + fsloc->nfl_info.nfl_readdir); + + return FEDFS_OK; +} + +/** + * Add a "read" child to a "location" element + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param parent parent element to which to add "host" child + * @param fsloc NFS location containing host information to add + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_location_read_xml(const char *pathname, xmlNodePtr parent, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr new; + + new = xmlNewTextChild(parent, NULL, NFS_XML_READ_TAG, NULL); + if (new == NULL) { + xlog(D_GENERAL, "%s: Failed to add read element for %s", + __func__, pathname); + return FEDFS_ERR_SVRFAULT; + } + + junction_xml_set_int_attribute(new, NFS_XML_READ_RANK_ATTR, + fsloc->nfl_info.nfl_readrank); + junction_xml_set_int_attribute(new, NFS_XML_READ_ORDER_ATTR, + fsloc->nfl_info.nfl_readorder); + + return FEDFS_OK; +} + +/** + * Add a "write" child to a "location" element + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param parent parent element to which to add "host" child + * @param fsloc NFS location containing host information to add + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_location_write_xml(const char *pathname, xmlNodePtr parent, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr new; + + new = xmlNewTextChild(parent, NULL, NFS_XML_WRITE_TAG, NULL); + if (new == NULL) { + xlog(D_GENERAL, "%s: Failed to add write element for %s", + __func__, pathname); + return FEDFS_ERR_SVRFAULT; + } + + junction_xml_set_int_attribute(new, NFS_XML_WRITE_RANK_ATTR, + fsloc->nfl_info.nfl_writerank); + junction_xml_set_int_attribute(new, NFS_XML_WRITE_ORDER_ATTR, + fsloc->nfl_info.nfl_writeorder); + + return FEDFS_OK; +} + +/** + * Add a "flags" child to a "location" element + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param parent parent element to which to add "host" child + * @param fsloc NFS location containing host information to add + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_location_flags_xml(const char *pathname, xmlNodePtr parent, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr new; + + new = xmlNewTextChild(parent, NULL, NFS_XML_FLAGS_TAG, NULL); + if (new == NULL) { + xlog(D_GENERAL, "%s: Failed to add flags element for %s", + __func__, pathname); + return FEDFS_ERR_SVRFAULT; + } + + junction_xml_set_bool_attribute(new, NFS_XML_FLAGS_VARSUB_ATTR, + fsloc->nfl_flags.nfl_varsub); + + return FEDFS_OK; +} + +/** + * Add a "validfor" child to a "location" element + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param parent parent element to which to add "host" child + * @param fsloc NFS location containing host information to add + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_location_validfor_xml(__attribute__((unused)) const char *pathname, + xmlNodePtr parent, struct nfs_fsloc *fsloc) +{ + if (junction_xml_set_int_content(parent, NFS_XML_VALIDFOR_TAG, + fsloc->nfl_validfor) == NULL) + return FEDFS_ERR_SVRFAULT; + return FEDFS_OK; +} + +/** + * Construct and add one "location" element to a "fileset" + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param fileset fileset element of junction XML parse tree + * @param fsloc one NFS location to add + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_location_xml(const char *pathname, xmlNodePtr fileset, + struct nfs_fsloc *fsloc) +{ + FedFsStatus retval; + xmlNodePtr new; + + new = xmlNewTextChild(fileset, NULL, NFS_XML_LOCATION_TAG, NULL); + if (new == NULL) { + xlog(D_GENERAL, "%s: Failed to add location element for %s", + __func__, pathname); + return FEDFS_ERR_SVRFAULT; + } + + retval = nfs_location_host_xml(pathname, new, fsloc); + if (retval != FEDFS_OK) + return retval; + retval = nfs_location_path_xml(pathname, new, fsloc); + if (retval != FEDFS_OK) + return retval; + retval = nfs_location_currency_xml(pathname, new, fsloc); + if (retval != FEDFS_OK) + return retval; + retval = nfs_location_genflags_xml(pathname, new, fsloc); + if (retval != FEDFS_OK) + return retval; + retval = nfs_location_transflags_xml(pathname, new, fsloc); + if (retval != FEDFS_OK) + return retval; + retval = nfs_location_class_xml(pathname, new, fsloc); + if (retval != FEDFS_OK) + return retval; + retval = nfs_location_read_xml(pathname, new, fsloc); + if (retval != FEDFS_OK) + return retval; + retval = nfs_location_write_xml(pathname, new, fsloc); + if (retval != FEDFS_OK) + return retval; + retval = nfs_location_flags_xml(pathname, new, fsloc); + if (retval != FEDFS_OK) + return retval; + return nfs_location_validfor_xml(pathname, new, fsloc); +} + +/** + * Construct and add a "fileset" element + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param root root element of junction XML parse tree + * @param fslocs list of NFS locations to add + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_fileset_xml(const char *pathname, xmlNodePtr root, + struct nfs_fsloc *fslocs) +{ + struct nfs_fsloc *next; + xmlNodePtr fileset; + FedFsStatus retval; + + fileset = xmlNewTextChild(root, NULL, JUNCTION_XML_FILESET_TAG, NULL); + if (fileset == NULL) { + xlog(D_GENERAL, "%s: Failed to add fileset element for %s", + __func__, pathname); + return FEDFS_ERR_SVRFAULT; + } + + for (next = fslocs; next != NULL; next = next->nfl_next) { + retval = nfs_location_xml(pathname, fileset, next); + if (retval != FEDFS_OK) + return retval; + } + + return FEDFS_OK; +} + +/** + * Construct a "savedmode" element + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param root root element of XML document tree + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_savedmode_xml(const char *pathname, xmlNodePtr root) +{ + xmlNodePtr savedmode; + FedFsStatus retval; + mode_t mode; + char buf[8]; + + retval = junction_get_mode(pathname, &mode); + if (retval != FEDFS_OK) + return retval; + + savedmode = xmlNewTextChild(root, NULL, JUNCTION_XML_SAVEDMODE_TAG, NULL); + if (savedmode == NULL) { + xlog(D_GENERAL, "%s: Failed to add savedmode element for %s\n", + __func__, pathname); + return FEDFS_ERR_SVRFAULT; + } + + (void)snprintf(buf, sizeof(buf), "%o", ALLPERMS & mode); + xmlSetProp(savedmode, JUNCTION_XML_MODEBITS_ATTR, (const xmlChar *)buf); + + return FEDFS_OK; +} + +/** + * Construct NFS junction XML document from list of NFS locations + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param doc an XML parse tree in which to construct the junction XML document + * @param fslocs list of NFS locations to add + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_junction_xml(const char *pathname, xmlDocPtr doc, + struct nfs_fsloc *fslocs) +{ + FedFsStatus retval; + xmlNodePtr root; + + root = xmlNewNode(NULL, JUNCTION_XML_ROOT_TAG); + if (root == NULL) { + xlog(D_GENERAL, "%s: Failed to create root element for %s", + __func__, pathname); + return FEDFS_ERR_SVRFAULT; + } + (void)xmlDocSetRootElement(doc, root); + + retval = nfs_savedmode_xml(pathname, root); + if (retval != FEDFS_OK) + return retval; + + return nfs_fileset_xml(pathname, root, fslocs); +} + +/** + * Write NFS locations information into an NFS junction extended attribute + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param doc an empty XML parse tree in which to construct the junction XML document + * @param fslocs list of NFS locations to add + * @return a FedFsStatus code + * + * @note Access to trusted attributes requires CAP_SYS_ADMIN. + */ +static FedFsStatus +nfs_write_junction(const char *pathname, xmlDocPtr doc, + struct nfs_fsloc *fslocs) +{ + FedFsStatus retval; + + retval = nfs_junction_xml(pathname, doc, fslocs); + if (retval != FEDFS_OK) + return retval; + + return junction_xml_write(pathname, JUNCTION_XATTR_NAME_NFS, doc); +} + +/** + * Store NFS locations information into a junction object + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param fslocs list of NFS locations to add + * @return a FedFsStatus code + * + * @note Access to trusted attributes requires CAP_SYS_ADMIN. + */ +static FedFsStatus +nfs_store_locations(const char *pathname, struct nfs_fsloc *fslocs) +{ + FedFsStatus retval; + xmlDocPtr doc; + + doc = xmlNewDoc((xmlChar *)"1.0"); + if (doc == NULL) { + xlog(D_GENERAL, "%s: Failed to create XML doc for %s", + __func__, pathname); + return FEDFS_ERR_SVRFAULT; + } + + retval = nfs_write_junction(pathname, doc, fslocs); + + xmlFreeDoc(doc); + return retval; +} + +/** + * Add NFS junction information to a pre-existing object + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param fslocs list of NFS locations to add + * @return a FedFsStatus code + * + * An error occurs if the object referred to by "pathname" does not + * exist or contains existing junction data. + */ +FedFsStatus +nfs_add_junction(const char *pathname, struct nfs_fsloc *fslocs) +{ + FedFsStatus retval; + + if (fslocs == NULL) + return FEDFS_ERR_INVAL; + + retval = nfs_is_prejunction(pathname); + if (retval != FEDFS_ERR_NOTJUNCT) + return retval; + + retval = nfs_store_locations(pathname, fslocs); + if (retval != FEDFS_OK) + goto out_err; + + retval = junction_save_mode(pathname); + if (retval != FEDFS_OK) + goto out_err; + + return retval; + +out_err: + (void)nfs_remove_locations(pathname); + return retval; +} + +/** + * Remove NFS junction information from an object + * + * @param pathname NUL-terminated C string containing pathname of a directory + * @return a FedFsStatus code + * + * An error occurs if the object referred to by "pathname" does not + * exist or does not contain NFS junction data. + */ +FedFsStatus +nfs_delete_junction(const char *pathname) +{ + FedFsStatus retval; + + retval = nfs_is_junction(pathname); + if (retval != FEDFS_OK) + return retval; + + retval = junction_restore_mode(pathname); + if (retval != FEDFS_OK) + return retval; + + return nfs_remove_locations(pathname); +} + +/** + * Parse the first "host" child of "location" + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param location XML parse tree containing fileset location element + * @param fsloc a blank nfs_fsloc to fill in + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_parse_location_host(const char *pathname, xmlNodePtr location, + struct nfs_fsloc *fsloc) +{ + FedFsStatus retval; + xmlChar *hostname; + xmlNodePtr node; + int hostport; + + retval = FEDFS_ERR_NOTJUNCT; + node = junction_xml_find_child_by_name(location, NFS_XML_HOST_TAG); + if (node == NULL) + return retval; + + hostname = xmlGetProp(node, NFS_XML_HOST_NAME_ATTR); + if (!junction_xml_get_int_attribute(node, NFS_XML_HOST_PORT_ATTR, + &hostport)) + fsloc->nfl_hostport = NFS_PORT; + else { + if (hostport < 1 || hostport > UINT16_MAX) { + xlog(D_GENERAL, "%s: Bad port attribute on %s", + __func__, pathname); + goto out; + } + fsloc->nfl_hostport = (uint16_t)hostport; + } + if (hostname == NULL) { + xlog(D_GENERAL, "%s: No hostname attribute on %s", + __func__, pathname); + goto out; + } + fsloc->nfl_hostname = strdup((const char *)hostname); + if (fsloc->nfl_hostname == NULL) { + retval = FEDFS_ERR_SVRFAULT; + goto out; + } + + retval = FEDFS_OK; + +out: + xmlFree(hostname); + return retval; +} + +/** + * Parse the first "path" child of "location" into a path array + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param location XML parse tree containing fileset location element + * @param fsloc a blank nfs_fsloc to fill in + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_parse_location_path(const char *pathname, xmlNodePtr location, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr node, component; + unsigned int count; + xmlChar *value; + char **result; + + node = junction_xml_find_child_by_name(location, NFS_XML_PATH_TAG); + if (node == NULL) + return FEDFS_ERR_NOTJUNCT; + + count = 0; + for (component = node->children; + component != NULL; + component = component->next) { + if (!junction_xml_match_node_name(component, + NFS_XML_COMPONENT_TAG)) + continue; + value = xmlNodeGetContent(component); + if (junction_xml_is_empty(value)) { + xlog(D_GENERAL, "%s: Bad pathname component in %s", + __func__, pathname); + return FEDFS_ERR_NOTJUNCT; + } + xmlFree(value); + count++; + } + xlog(D_GENERAL, "%s: Found %u component(s)", __func__, count); + + if (count == 0) { + xlog(D_GENERAL, "%s: Zero-component pathname", __func__); + fsloc->nfl_rootpath = (char **)calloc(1, sizeof(char *)); + if (fsloc->nfl_rootpath == NULL) + return FEDFS_ERR_SVRFAULT; + fsloc->nfl_rootpath[0] = NULL; + return FEDFS_OK; + } + + result = calloc(count + 1, sizeof(char *)); + if (result == NULL) + return FEDFS_ERR_SVRFAULT; + + count = 0; + for (component = node->children; + component != NULL; + component = component->next) { + if (!junction_xml_match_node_name(component, + NFS_XML_COMPONENT_TAG)) + continue; + value = xmlNodeGetContent(component); + result[count] = strdup((const char *)value); + xmlFree(value); + if (result[count] == NULL) { + nfs_free_string_array(result); + return FEDFS_ERR_SVRFAULT; + } + count++; + } + + fsloc->nfl_rootpath = result; + return FEDFS_OK; +} + +/** + * Parse the first "currency" child of "location" + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param location XML parse tree containing fileset location element + * @param fsloc a blank nfs_fsloc to fill in + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_parse_location_currency(const char *pathname, xmlNodePtr location, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr node; + + node = junction_xml_find_child_by_name(location, NFS_XML_CURRENCY_TAG); + if (node == NULL) + goto out_err; + + if (!junction_xml_get_int_content(node, &fsloc->nfl_currency)) + goto out_err; + + return FEDFS_OK; + +out_err: + xlog(D_GENERAL, "%s: Missing or invalid currency element in %s", + __func__, pathname); + return FEDFS_ERR_NOTJUNCT; +} + +/** + * Parse the first "genflags" child of "location" + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param location XML parse tree containing fileset location element + * @param fsloc a blank nfs_fsloc to fill in + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_parse_location_genflags(const char *pathname, xmlNodePtr location, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr node; + + node = junction_xml_find_child_by_name(location, NFS_XML_GENFLAGS_TAG); + if (node == NULL) + goto out_err; + + if (!junction_xml_get_bool_attribute(node, + NFS_XML_GENFLAGS_WRITABLE_ATTR, + &fsloc->nfl_genflags.nfl_writable)) + goto out_err; + if (!junction_xml_get_bool_attribute(node, + NFS_XML_GENFLAGS_GOING_ATTR, + &fsloc->nfl_genflags.nfl_going)) + goto out_err; + if (!junction_xml_get_bool_attribute(node, + NFS_XML_GENFLAGS_SPLIT_ATTR, + &fsloc->nfl_genflags.nfl_split)) + goto out_err; + + return FEDFS_OK; + +out_err: + xlog(D_GENERAL, "%s: Missing or invalid genflags element in %s", + __func__, pathname); + return FEDFS_ERR_NOTJUNCT; +} + +/** + * Parse the first "transflags" child of "location" + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param location XML parse tree containing fileset location element + * @param fsloc a blank nfs_fsloc to fill in + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_parse_location_transflags(const char *pathname, xmlNodePtr location, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr node; + + node = junction_xml_find_child_by_name(location, NFS_XML_TRANSFLAGS_TAG); + if (node == NULL) + goto out_err; + + if (!junction_xml_get_bool_attribute(node, + NFS_XML_TRANSFLAGS_RDMA_ATTR, + &fsloc->nfl_transflags.nfl_rdma)) + goto out_err; + + return FEDFS_OK; + +out_err: + xlog(D_GENERAL, "%s: Missing or invalid transflags element in %s", + __func__, pathname); + return FEDFS_ERR_NOTJUNCT; +} + +/** + * Parse the first "class" child of "location" + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param location XML parse tree containing fileset location element + * @param fsloc a blank nfs_fsloc to fill in + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_parse_location_class(const char *pathname, xmlNodePtr location, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr node; + + node = junction_xml_find_child_by_name(location, NFS_XML_CLASS_TAG); + if (node == NULL) + goto out_err; + + if (!junction_xml_get_u8_attribute(node, + NFS_XML_CLASS_SIMUL_ATTR, + &fsloc->nfl_info.nfl_simul)) + goto out_err; + if (!junction_xml_get_u8_attribute(node, + NFS_XML_CLASS_HANDLE_ATTR, + &fsloc->nfl_info.nfl_handle)) + goto out_err; + if (!junction_xml_get_u8_attribute(node, + NFS_XML_CLASS_FILEID_ATTR, + &fsloc->nfl_info.nfl_fileid)) + goto out_err; + if (!junction_xml_get_u8_attribute(node, + NFS_XML_CLASS_WRITEVER_ATTR, + &fsloc->nfl_info.nfl_writever)) + goto out_err; + if (!junction_xml_get_u8_attribute(node, + NFS_XML_CLASS_WRITEVER_ATTR, + &fsloc->nfl_info.nfl_writever)) + goto out_err; + if (!junction_xml_get_u8_attribute(node, + NFS_XML_CLASS_CHANGE_ATTR, + &fsloc->nfl_info.nfl_change)) + goto out_err; + if (!junction_xml_get_u8_attribute(node, + NFS_XML_CLASS_READDIR_ATTR, + &fsloc->nfl_info.nfl_readdir)) + goto out_err; + + return FEDFS_OK; + +out_err: + xlog(D_GENERAL, "%s: Missing or invalid class element in %s", + __func__, pathname); + return FEDFS_ERR_NOTJUNCT; +} + +/** + * Parse the first "read" child of "location" + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param location XML parse tree containing fileset location element + * @param fsloc a blank nfs_fsloc to fill in + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_parse_location_read(const char *pathname, xmlNodePtr location, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr node; + + node = junction_xml_find_child_by_name(location, NFS_XML_READ_TAG); + if (node == NULL) + goto out_err; + + if (!junction_xml_get_u8_attribute(node, + NFS_XML_READ_RANK_ATTR, + &fsloc->nfl_info.nfl_readrank)) + goto out_err; + if (!junction_xml_get_u8_attribute(node, + NFS_XML_READ_ORDER_ATTR, + &fsloc->nfl_info.nfl_readorder)) + goto out_err; + + return FEDFS_OK; + +out_err: + xlog(D_GENERAL, "%s: Missing or invalid read element in %s", + __func__, pathname); + return FEDFS_ERR_NOTJUNCT; +} + +/** + * Parse the first "write" child of "location" + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param location XML parse tree containing fileset location element + * @param fsloc a blank nfs_fsloc to fill in + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_parse_location_write(const char *pathname, xmlNodePtr location, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr node; + + node = junction_xml_find_child_by_name(location, NFS_XML_WRITE_TAG); + if (node == NULL) + goto out_err; + + if (!junction_xml_get_u8_attribute(node, + NFS_XML_WRITE_RANK_ATTR, + &fsloc->nfl_info.nfl_writerank)) + goto out_err; + if (!junction_xml_get_u8_attribute(node, + NFS_XML_WRITE_ORDER_ATTR, + &fsloc->nfl_info.nfl_writeorder)) + goto out_err; + + return FEDFS_OK; + +out_err: + xlog(D_GENERAL, "%s: Missing or invalid write element in %s", + __func__, pathname); + return FEDFS_ERR_NOTJUNCT; +} + +/** + * Parse the first "flags" child of "location" + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param location XML parse tree containing fileset location element + * @param fsloc a blank nfs_fsloc to fill in + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_parse_location_flags(const char *pathname, xmlNodePtr location, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr node; + + node = junction_xml_find_child_by_name(location, NFS_XML_FLAGS_TAG); + if (node == NULL) + goto out_err; + + if (!junction_xml_get_bool_attribute(node, + NFS_XML_FLAGS_VARSUB_ATTR, + &fsloc->nfl_flags.nfl_varsub)) + goto out_err; + + return FEDFS_OK; + +out_err: + xlog(D_GENERAL, "%s: Missing or invalid flags element in %s", + __func__, pathname); + return FEDFS_ERR_NOTJUNCT; +} + +/** + * Parse the first "validfor" child of "location" + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param location XML parse tree containing fileset location element + * @param fsloc a blank nfs_fsloc to fill in + * @return a FedFsStatus code + */ +static FedFsStatus +nfs_parse_location_validfor(const char *pathname, xmlNodePtr location, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr node; + + node = junction_xml_find_child_by_name(location, NFS_XML_VALIDFOR_TAG); + if (node == NULL) + goto out_err; + + if (!junction_xml_get_int_content(node, &fsloc->nfl_validfor)) + goto out_err; + + return FEDFS_OK; + +out_err: + xlog(D_GENERAL, "%s: Missing or invalid validfor element in %s", + __func__, pathname); + return FEDFS_ERR_NOTJUNCT; +} + +/** + * Parse children of NFS location element in an NFS junction + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param location XML parse tree containing fileset location element + * @param fsloc a blank nfs_fsloc to fill in + * @return a FedFsStatus code + * + * All children are required only-once elements, and may appear in any order. + * Extraneous or repeated elements are ignored for now. + */ +static FedFsStatus +nfs_parse_location_children(const char *pathname, xmlNodePtr location, + struct nfs_fsloc *fsloc) +{ + FedFsStatus retval; + + retval = nfs_parse_location_host(pathname, location, fsloc); + if (retval != FEDFS_OK) + return retval; + retval = nfs_parse_location_path(pathname, location, fsloc); + if (retval != FEDFS_OK) + return retval; + retval = nfs_parse_location_currency(pathname, location, fsloc); + if (retval != FEDFS_OK) + return retval; + retval = nfs_parse_location_genflags(pathname, location, fsloc); + if (retval != FEDFS_OK) + return retval; + retval = nfs_parse_location_transflags(pathname, location, fsloc); + if (retval != FEDFS_OK) + return retval; + retval = nfs_parse_location_class(pathname, location, fsloc); + if (retval != FEDFS_OK) + return retval; + retval = nfs_parse_location_read(pathname, location, fsloc); + if (retval != FEDFS_OK) + return retval; + retval = nfs_parse_location_write(pathname, location, fsloc); + if (retval != FEDFS_OK) + return retval; + retval = nfs_parse_location_flags(pathname, location, fsloc); + if (retval != FEDFS_OK) + return retval; + return nfs_parse_location_validfor(pathname, location, fsloc); +} + +/** + * Parse NFS location element in an NFS junction + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param location XML parse tree containing fileset location element + * @param fsloc OUT: a single NFS location item + * @return a FedFsStatus code + * + * If nfs_parse_location() returns FEDFS_OK, caller must free the returned + * location with nfs_free_location(). + */ +static FedFsStatus +nfs_parse_node(const char *pathname, xmlNodePtr location, + struct nfs_fsloc **fsloc) +{ + struct nfs_fsloc *tmp; + FedFsStatus retval; + + tmp = nfs_new_location(); + if (tmp == NULL) + return FEDFS_ERR_SVRFAULT; + + retval = nfs_parse_location_children(pathname, location, tmp); + if (retval != FEDFS_OK) + nfs_free_location(tmp); + else + *fsloc = tmp; + return retval; +} + +/** + * Build list of NFS locations from a nodeset + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param nodeset XML nodeset containing "location" elements + * @param fslocs OUT: pointer to a list of NFS locations + * @return a FedFsStatus code + * + * If nfs_parse_nodeset() returns FEDFS_OK, caller must free the returned + * list of locations with nfs_free_locations(). + */ +static FedFsStatus +nfs_parse_nodeset(const char *pathname, xmlNodeSetPtr nodeset, + struct nfs_fsloc **fslocs) +{ + struct nfs_fsloc *location, *result = NULL; + FedFsStatus retval; + int i; + + if (xmlXPathNodeSetIsEmpty(nodeset)) { + xlog(D_GENERAL, "%s: No fileset locations found in %s", + __func__, pathname); + return FEDFS_ERR_NOTJUNCT; + } + + for (i = 0; i < nodeset->nodeNr; i++) { + xmlNodePtr node = nodeset->nodeTab[i]; + + retval = nfs_parse_node(pathname, node, &location); + if (retval != FEDFS_OK) { + nfs_free_locations(result); + return retval; + } + + if (result == NULL) + result = location; + else + result->nfl_next = location; + } + + *fslocs = result; + return FEDFS_OK; +} + +/** + * Parse fileset location information from junction XML + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param context XML path context containing junction XML + * @param fslocs OUT: pointer to a list of NFS locations + * @return a FedFsStatus code + * + * If nfs_parse_context() returns FEDFS_OK, caller must free the returned + * list of locations with nfs_free_locations(). + */ +static FedFsStatus +nfs_parse_context(const char *pathname, xmlXPathContextPtr context, + struct nfs_fsloc **fslocs) +{ + xmlXPathObjectPtr object; + FedFsStatus retval; + + object = xmlXPathEvalExpression(NFS_XML_LOCATION_XPATH, context); + if (object == NULL) { + xlog(D_GENERAL, "%s: Failed to evaluate XML in %s", + __func__, pathname); + return FEDFS_ERR_NOTJUNCT; + } + + retval = nfs_parse_nodeset(pathname, object->nodesetval, fslocs); + + xmlXPathFreeObject(object); + return retval; +} + +/** + * Parse NFS locations information from junction XML + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param doc XML parse tree containing junction XML document + * @param fslocs OUT: pointer to a list of NFS locations + * @return a FedFsStatus code + * + * If nfs_parse_xml() returns FEDFS_OK, caller must free the returned + * list of locations with nfs_free_locations(). + */ +static FedFsStatus +nfs_parse_xml(const char *pathname, xmlDocPtr doc, struct nfs_fsloc **fslocs) +{ + xmlXPathContextPtr context; + FedFsStatus retval; + + context = xmlXPathNewContext(doc); + if (context == NULL) { + xlog(D_GENERAL, "%s: Failed to create XPath context from %s", + __func__, pathname); + return FEDFS_ERR_SVRFAULT; + } + + retval = nfs_parse_context(pathname, context, fslocs); + + xmlXPathFreeContext(context); + return retval; +} + +/** + * Retrieve list of NFS locations from an NFS junction + * + * @param pathname NUL-terminated C string containing pathname of a junction + * @param fslocs OUT: pointer to a list of NFS locations + * @return a FedFsStatus code + * + * If nfs_get_locations() returns FEDFS_OK, caller must free the returned + * list of locations with nfs_free_locations(). + */ +FedFsStatus +nfs_get_locations(const char *pathname, struct nfs_fsloc **fslocs) +{ + FedFsStatus retval; + xmlDocPtr doc; + + if (fslocs == NULL) + return FEDFS_ERR_INVAL; + + retval = junction_xml_parse(pathname, JUNCTION_XATTR_NAME_NFS, &doc); + if (retval != FEDFS_OK) + return retval; + + retval = nfs_parse_xml(pathname, doc, fslocs); + + xmlFreeDoc(doc); + return retval; +} + +/** + * Predicate: does "pathname" refer to an object that can become an NFS junction? + * + * @param pathname NUL-terminated C string containing pathname of a directory + * @return a FedFsStatus code + * + * Return values: + * FEDFS_ERR_NOTJUNCT: "pathname" refers to an object that can be + * made into a NFS junction + * FEDFS_ERR_EXIST: "pathname" refers to something that is + * already a junction + * FEDFS_ERR_INVAL: "pathname" does not exist + * Other: Some error occurred, "pathname" not + * investigated + */ +FedFsStatus +nfs_is_prejunction(const char *pathname) +{ + FedFsStatus retval; + int fd; + + retval = junction_open_path(pathname, &fd); + if (retval != FEDFS_OK) + return retval; + + retval = junction_is_directory(fd, pathname); + if (retval != FEDFS_OK) + goto out_close; + + retval = junction_is_sticky_bit_set(fd, pathname); + switch (retval) { + case FEDFS_ERR_NOTJUNCT: + break; + case FEDFS_OK: + goto out_exist; + default: + goto out_close; + } + + retval = junction_is_xattr_present(fd, pathname, JUNCTION_XATTR_NAME_NFS); + switch (retval) { + case FEDFS_ERR_NOTJUNCT: + break; + case FEDFS_OK: + goto out_exist; + default: + goto out_close; + } + +out_close: + (void)close(fd); + return retval; +out_exist: + retval = FEDFS_ERR_EXIST; + goto out_close; +} + +/** + * Verify that junction contains NFS junction XML + * + * @param pathname NUL-terminated C string containing pathname of a directory + * @return a FedFsStatus code + * + * Return values: + * FEDFS_OK: "pathname" refers to an NFS junction + * FEDFS_ERR_NOTJUNCT: "pathname" refers to something that is + * not an NFS junction + * FEDFS_ERR_INVAL: "pathname" does not exist + * Other: Some error occurred, "pathname" not + * investigated + * + * NB: This is an expensive test. However, it is only done if the object + * actually has a junction extended attribute, meaning it should be done + * rarely. If this is really a problem, we can make the XML test cheaper. + */ +static FedFsStatus +nfs_is_junction_xml(const char *pathname) +{ + struct nfs_fsloc *fslocs = NULL; + FedFsStatus retval; + xmlDocPtr doc; + + retval = junction_xml_parse(pathname, JUNCTION_XATTR_NAME_NFS, &doc); + if (retval != FEDFS_OK) + return retval; + + retval = nfs_parse_xml(pathname, doc, &fslocs); + nfs_free_locations(fslocs); + + xmlFreeDoc(doc); + return retval; +} + +/** + * Predicate: does "pathname" refer to an NFS junction? + * + * @param pathname NUL-terminated C string containing pathname of a directory + * @return a FedFsStatus code + * + * Return values: + * FEDFS_OK: "pathname" refers to an NFS junction + * FEDFS_ERR_NOTJUNCT: "pathname" refers to an object that is + * not a junction + * FEDFS_ERR_INVAL: "pathname" does not exist + * Other: Some error occurred, "pathname" not + * investigated + */ +FedFsStatus +nfs_is_junction(const char *pathname) +{ + FedFsStatus retval; + int fd; + + retval = junction_open_path(pathname, &fd); + if (retval != FEDFS_OK) + return retval; + + retval = junction_is_directory(fd, pathname); + if (retval != FEDFS_OK) + goto out_close; + + retval = junction_is_sticky_bit_set(fd, pathname); + if (retval != FEDFS_OK) + goto out_close; + + retval = junction_is_xattr_present(fd, pathname, JUNCTION_XATTR_NAME_NFS); + if (retval != FEDFS_OK) + goto out_close; + + (void)close(fd); + + return nfs_is_junction_xml(pathname); + +out_close: + (void)close(fd); + return retval; +} diff --git a/support/junction/path.c b/support/junction/path.c new file mode 100644 index 0000000..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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "junction.h" +#include "xlog.h" + +#define STRLEN_SLASH ((size_t)1) /* strlen("/") */ + +#define XDR_UINT_BYTES (sizeof(uint32_t)) + +/** + * Compute count of XDR 4-octet units from byte count + * + * @param bytes number of bytes to convert + * @return equivalent number of XDR 4-octet units + */ +static inline size_t +nsdb_quadlen(size_t bytes) +{ + return (bytes + 3) >> 2; +} + +/** + * Free array of NUL-terminated C strings + * + * @param strings array of char * to be released + */ +void +nsdb_free_string_array(char **strings) +{ + int i; + + if (strings == NULL) + return; + for (i = 0; strings[i] != NULL; i++) + free(strings[i]); + free(strings); +} + +static FedFsStatus +nsdb_alloc_zero_component_pathname(char ***path_array) +{ + char **result; + + xlog(D_GENERAL, "%s: Zero-component pathname", __func__); + + result = (char **)calloc(1, sizeof(char *)); + if (result == NULL) + return FEDFS_ERR_SVRFAULT; + result[0] = NULL; + *path_array = result; + return FEDFS_OK; +} + +/** + * Sanitize an incoming POSIX path + * + * @param pathname NUL-terminated C string containing a POSIX pathname + * @return NUL-terminated C string containing sanitized path + * + * Caller must free the returned pathname with free(3). + * + * Remove multiple sequential slashes and any trailing slashes, + * but leave "/" by itself alone. + */ +static __attribute_malloc__ char * +nsdb_normalize_path(const char *pathname) +{ + size_t i, j, len; + char *result; + + len = strlen(pathname); + if (len == 0) { + xlog(D_CALL, "%s: NULL pathname", __func__); + return NULL; + } + + result = malloc(len + 1); + if (result == NULL) + return NULL; + + for (i = 0, j = 0; i < len; i++) { + if (pathname[i] == '/' && pathname[i + 1] == '/') + continue; + result[j++] = pathname[i]; + } + result[j] = '\0'; + + if (j > 1 && result[j - 1] == '/') + result[j - 1] = '\0'; + + xlog(D_CALL, "%s: result = '%s'", __func__, result); + return result; +} + +/** + * Count the number of components in a POSIX pathname + * + * @param pathname NUL-terminated C string containing a POSIX pathname + * @param len OUT: number of bytes the encoded XDR stream will consume + * @param cnt OUT: component count + * @return true when successful + */ +static _Bool +nsdb_count_components(const char *pathname, size_t *len, + unsigned int *cnt) +{ + char *start, *component; + unsigned int count; + size_t length; + + /* strtok(3) will tromp on the string */ + start = strdup(pathname); + if (start == NULL) + return false; + + length = XDR_UINT_BYTES; + count = 0; + component = start; + for ( ;; ) { + char *next; + size_t tmp; + + if (*component == '/') + component++; + if (*component == '\0') + break; + next = strchrnul(component, '/'); + tmp = (size_t)(next - component); + if (tmp > 255) { + 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 +#include + +#include +#include +#include +#include +#include +#include + +#include "junction.h" +#include "junction-internal.h" +#include "xlog.h" + +/** + * Predicate: is element content empty? + * + * @param content element content to test + * @return true if content is empty + */ +_Bool +junction_xml_is_empty(const xmlChar *content) +{ + return content == NULL || *content == '\0'; +} + +/** + * Match an XML parse tree node by its name + * + * @param node pointer to a node in an XML parse tree + * @param name NUL-terminated C string containing name to match + * @return true if "node" is named "name" + */ +_Bool +junction_xml_match_node_name(xmlNodePtr node, const xmlChar *name) +{ + return (node->type == XML_ELEMENT_NODE) && + (xmlStrcmp(node->name, name) == 0); +} + +/** + * Find a first-level child of "parent" named "name" + * + * @param parent pointer to node whose children are to be searched + * @param name NUL-terminated C string containing name to match + * @return pointer to child of "parent" whose name is "name" + */ +xmlNodePtr +junction_xml_find_child_by_name(xmlNodePtr parent, const xmlChar *name) +{ + xmlNodePtr node; + + for (node = parent->children; node != NULL; node = node->next) + if (junction_xml_match_node_name(node, name)) + return node; + return NULL; +} + +/** + * Read attribute into a boolean + * + * @param node pointer to a node in an XML parse tree + * @param attrname NUL-terminated C string containing attribute name + * @param value OUT: attribute's value converted to an integer + * @return true if attribute "attrname" has a valid boolean value + */ +_Bool +junction_xml_get_bool_attribute(xmlNodePtr node, const xmlChar *attrname, + _Bool *value) +{ + xmlChar *prop; + _Bool retval; + + retval = false; + prop = xmlGetProp(node, attrname); + if (prop == NULL) + goto out; + + if (xmlStrcmp(prop, (const xmlChar *)"true") == 0) { + *value = true; + retval = true; + goto out; + } + + if (xmlStrcmp(prop, (const xmlChar *)"false") == 0) { + *value = false; + retval = true; + goto out; + } + +out: + xmlFree(prop); + return retval; +} + +/** + * Set attribute to a boolean + * + * @param node pointer to a node in an XML parse tree + * @param attrname NUL-terminated C string containing attribute name + * @param value boolean value to set + */ +void +junction_xml_set_bool_attribute(xmlNodePtr node, const xmlChar *attrname, + _Bool value) +{ + xmlSetProp(node, attrname, (const xmlChar *)(value ? "true" : "false")); +} + +/** + * Read attribute into an uint8_t + * + * @param node pointer to a node in an XML parse tree + * @param attrname NUL-terminated C string containing attribute name + * @param value OUT: attribute's value converted to an uint8_t + * @return true if attribute "attrname" has a valid uint8_t value + */ +_Bool +junction_xml_get_u8_attribute(xmlNodePtr node, const xmlChar *attrname, + uint8_t *value) +{ + char *endptr; + _Bool retval; + char *prop; + long tmp; + + retval = false; + prop = (char *)xmlGetProp(node, attrname); + if (prop == NULL) + goto out; + + errno = 0; + tmp = strtol(prop, &endptr, 10); + if (errno != 0 || *endptr != '\0' || tmp > 255 || tmp < 0) + goto out; + + *value = (uint8_t)tmp; + retval = true; + +out: + xmlFree(prop); + return retval; +} + +/** + * Read attribute into an integer + * + * @param node pointer to a node in an XML parse tree + * @param attrname NUL-terminated C string containing attribute name + * @param value OUT: attribute's value converted to an integer + * @return true if attribute "attrname" has a valid integer value + */ +_Bool +junction_xml_get_int_attribute(xmlNodePtr node, const xmlChar *attrname, + int *value) +{ + char *endptr; + _Bool retval; + char *prop; + long tmp; + + retval = false; + prop = (char *)xmlGetProp(node, attrname); + if (prop == NULL) + goto out; + + errno = 0; + tmp = strtol(prop, &endptr, 10); + if (errno != 0 || *endptr != '\0' || tmp > INT32_MAX || tmp < INT32_MIN) + goto out; + + *value = (int)tmp; + retval = true; + +out: + xmlFree(prop); + return retval; +} + +/** + * Set attribute to an integer + * + * @param node pointer to a node in an XML parse tree + * @param attrname NUL-terminated C string containing attribute name + * @param value integer value to set + */ +void +junction_xml_set_int_attribute(xmlNodePtr node, const xmlChar *attrname, + int value) +{ + char buf[16]; + + snprintf(buf, sizeof(buf), "%d", value); + xmlSetProp(node, attrname, (const xmlChar *)buf); +} + +/** + * Read node content into an integer + * + * @param node pointer to a node in an XML parse tree + * @param value OUT: node's content converted to an integer + * @return true if "node" has valid integer content + */ +_Bool +junction_xml_get_int_content(xmlNodePtr node, int *value) +{ + xmlChar *content; + char *endptr; + _Bool retval; + long tmp; + + retval = false; + content = xmlNodeGetContent(node); + if (content == NULL) + goto out; + + errno = 0; + tmp = strtol((const char *)content, &endptr, 10); + if (errno != 0 || *endptr != '\0' || tmp > INT32_MAX || tmp < INT32_MIN) + goto out; + + *value = (int)tmp; + retval = true; + +out: + xmlFree(content); + return retval; +} + +/** + * Add a child node with integer content + * + * @param parent pointer to a node in an XML parse tree + * @param name NUL-terminated C string containing name of child to add + * @param value set node content to this value + * @return pointer to new child node + */ +xmlNodePtr +junction_xml_set_int_content(xmlNodePtr parent, const xmlChar *name, int value) +{ + char buf[16]; + + snprintf(buf, sizeof(buf), "%d", value); + return xmlNewTextChild(parent, NULL, name, (const xmlChar *)buf); +} + +/** + * Parse XML document in a buffer into an XML document tree + * + * @param pathname NUL-terminated C string containing pathname of a directory + * @param name NUL-terminated C string containing name of xattr to replace + * @param buf opaque byte array containing XML to parse + * @param len size of "buf" in bytes + * @param doc OUT: an XML parse tree containing junction XML + * @return a FedFsStatus code + * + * If junction_parse_xml_buf() returns success, caller must free "*doc" + * using xmlFreeDoc(3). + * + * @note Access to trusted attributes requires CAP_SYS_ADMIN. + */ +static FedFsStatus +junction_parse_xml_buf(const char *pathname, const char *name, + void *buf, size_t len, xmlDocPtr *doc) +{ + xmlDocPtr tmp; + + tmp = xmlParseMemory(buf, (int)len); + if (tmp == NULL) { + xlog(D_GENERAL, "Failed to parse XML in %s(%s)\n", + pathname, name); + return FEDFS_ERR_SVRFAULT; + } + + *doc = tmp; + return FEDFS_OK; +} + +/** + * Read an XML document from an extended attribute into an XML document tree + * + * @param pathname NUL-terminated C string containing pathname of a directory + * @param fd an open file descriptor + * @param name NUL-terminated C string containing name of xattr to replace + * @param doc OUT: an XML parse tree containing junction XML + * @return a FedFsStatus code + * + * If junction_parse_xml_read() returns success, caller must free "*doc" + * using xmlFreeDoc(3). + * + * @note Access to trusted attributes requires CAP_SYS_ADMIN. + */ +static FedFsStatus +junction_parse_xml_read(const char *pathname, int fd, const char *name, + xmlDocPtr *doc) +{ + FedFsStatus retval; + void *buf = NULL; + size_t len; + + retval = junction_get_xattr(fd, pathname, name, &buf, &len); + if (retval != FEDFS_OK) + return retval; + + xlog(D_CALL, "%s: XML document contained in junction:\n%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; +} -- cgit v1.2.3