From patchwork Wed Jan 4 21:07:16 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chuck Lever X-Patchwork-Id: 134363 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from acsinet15.oracle.com (acsinet15.oracle.com [141.146.126.227]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "acsinet15.oracle.com", Issuer "VeriSign Class 3 International Server CA - G3" (verified OK)) by ozlabs.org (Postfix) with ESMTPS id 33B361007D7 for ; Thu, 5 Jan 2012 08:07:30 +1100 (EST) Received: from acsinet21.oracle.com (acsinet21.oracle.com [141.146.126.237]) by acsinet15.oracle.com (Switch-3.4.4/Switch-3.4.4) with ESMTP id q04L7NmZ030464 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Wed, 4 Jan 2012 21:07:24 GMT Received: from oss.oracle.com (oss.oracle.com [141.146.12.120]) by acsinet21.oracle.com (8.14.4+Sun/8.14.4) with ESMTP id q04L7Nn5004436 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Wed, 4 Jan 2012 21:07:23 GMT Received: from localhost ([127.0.0.1] helo=oss.oracle.com) by oss.oracle.com with esmtp (Exim 4.63) (envelope-from ) id 1RiY3P-0001hx-4E; Wed, 04 Jan 2012 13:07:23 -0800 Received: from rcsinet12.oracle.com ([148.87.113.124]) by oss.oracle.com with esmtp (Exim 4.63) (envelope-from ) id 1RiY3L-0001ho-WA for fedfs-utils-devel@oss.oracle.com; Wed, 04 Jan 2012 13:07:20 -0800 Received: from mail-gy0-f171.google.com (mail-gy0-f171.google.com [209.85.160.171]) by rcsinet12.oracle.com (Sentrion-MTA-4.2.0/Sentrion-MTA-4.2.0) with ESMTP id q04L6DpL008243 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=OK) for ; Wed, 4 Jan 2012 21:07:19 GMT Received: by mail-gy0-f171.google.com with SMTP id 10so9357234ghy.2 for ; Wed, 04 Jan 2012 13:07:19 -0800 (PST) Received: by 10.100.244.7 with SMTP id r7mr17008239anh.10.1325711239047; Wed, 04 Jan 2012 13:07:19 -0800 (PST) Received: from degas.1015granger.net (adsl-99-26-161-222.dsl.sfldmi.sbcglobal.net. [99.26.161.222]) by mx.google.com with ESMTPS id b6sm5777029ani.16.2012.01.04.13.07.17 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 04 Jan 2012 13:07:18 -0800 (PST) From: Chuck Lever To: fedfs-utils-devel@oss.oracle.com Date: Wed, 04 Jan 2012 16:07:16 -0500 Message-ID: <20120104210716.8810.69697.stgit@degas.1015granger.net> In-Reply-To: <20120104204207.8810.10569.stgit@degas.1015granger.net> References: <20120104204207.8810.10569.stgit@degas.1015granger.net> User-Agent: StGIT/0.14.3 MIME-Version: 1.0 X-Flow-Control-Info: class=Default ip=209.85.160.171 ct-class=R6 ct-vol1=0 ct-vol2=0 ct-vol3=0 ct-risk=68 ct-spam1=0 ct-spam2=0 ct-bulk=0 rcpts=1 size=63568 Subject: [fedfs-utils] [PATCH 7/7] libjunction: Add support for nfs-basic junctions X-BeenThere: fedfs-utils-devel@oss.oracle.com X-Mailman-Version: 2.1.9 Precedence: list Reply-To: fedfs-utils Developers List-Id: fedfs-utils Developers List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: fedfs-utils-devel-bounces@oss.oracle.com Errors-To: fedfs-utils-devel-bounces@oss.oracle.com X-Source-IP: acsinet21.oracle.com [141.146.126.237] X-CT-RefId: str=0001.0A090205.4F04BF8D.0019:SCFSTAT1119972, ss=1, re=-10.500, fgs=0 X-Auth-Type: Internal IP Now that we have some example XML support in libjunction, we can begin to sketch in support for NFS junctions. Instead of storing a FedFS fileset name which is resolved to a set of fileset locations, an NFS junction stores the actual set of fileset locations. The XML for an NFS junction looks something like this: fileserver.example.net foo bar baz -1 0 .... Basically the element is a FedFS NFS fileset location record, expressed in XML. A list of these is parsed out of a junction XML document and then presented to, say, mountd. Although NFS supports both fs_locations and fs_locations_info, there is only one type of NFS fileset location record. This is exactly the same kind of information mountd would get if it resolved a FedFS FSN. Non-3530 information can be discarded to construct a regular fs_locations in response to an NFSv4.0 client. Signed-off-by: Chuck Lever --- src/include/junction.h | 77 ++ src/libjunction/Makefile.am | 3 src/libjunction/junction-internal.h | 11 src/libjunction/locations.c | 131 +++ src/libjunction/nfs.c | 1681 +++++++++++++++++++++++++++++++++++ src/libjunction/xml.c | 155 +++ 6 files changed, 2056 insertions(+), 2 deletions(-) create mode 100644 src/libjunction/locations.c create mode 100644 src/libjunction/nfs.c diff --git a/src/include/junction.h b/src/include/junction.h index de2f496..f0d041f 100644 --- a/src/include/junction.h +++ b/src/include/junction.h @@ -26,8 +26,85 @@ #ifndef _FEDFS_JUNCTION_H_ #define _FEDFS_JUNCTION_H_ +#include #include "nsdb.h" +/** + * Contains NFS fileset location information + * + * Each of these represents one server:/rootpath pair. The NFS + * implementation can coalesce multiple pairs into a single + * fs_location4 result if jfl_rootpath is the same across + * multiple servers. + * + * The nfl_server field can contain either one presentation format + * IP address or one DNS hostname. + * + * See Section 11.9 and 11.10 of RFC 5661 or section 4.2.2.3 and + * 4.2.2.4 of the NSDB protocol draft for details. + */ + +struct nfs_fsloc { + struct nfs_fsloc *nfl_next; + + char *nfl_hostname; + uint16_t nfl_hostport; + char **nfl_rootpath; + + struct { + _Bool nfl_varsub; + } nfl_flags; + int32_t nfl_currency; + int32_t nfl_validfor; + + struct { + _Bool nfl_writable, nfl_going, nfl_split; + } nfl_genflags; + struct { + _Bool nfl_rdma; + } nfl_transflags; + struct { + uint8_t nfl_simul, nfl_handle, nfl_fileid; + uint8_t nfl_writever, nfl_change, nfl_readdir; + uint8_t nfl_readrank, nfl_writerank; + uint8_t nfl_readorder, nfl_writeorder; + } nfl_info; + + int32_t nfl_majorver, nfl_minorver; + int32_t nfl_ttl; +}; + + +/** + ** NFS location data management functions + **/ + +void nfs_free_location(struct nfs_fsloc *location); +void nfs_free_locations(struct nfs_fsloc *locations); +struct nfs_fsloc *nfs_new_location(void); + +__attribute_malloc__ +char **nfs_dup_string_array(char **array); +void nfs_free_string_array(char **array); + + +/** + ** NFS junction management functions + **/ + +FedFsStatus nfs_delete_junction(const char *pathname); +FedFsStatus nfs_add_junction(const char *pathname, + struct nfs_fsloc *locations); +FedFsStatus nfs_get_locations(const char *pathname, + struct nfs_fsloc **locations); +FedFsStatus nfs_is_prejunction(const char *pathname); +FedFsStatus nfs_is_junction(const char *pathname); + + +/** + ** FedFS junction management functions + **/ + FedFsStatus fedfs_delete_junction(const char *pathname); FedFsStatus fedfs_add_junction(const char *pathname, const char *uuid, const nsdb_t host); diff --git a/src/libjunction/Makefile.am b/src/libjunction/Makefile.am index 9d91fef..4898d01 100644 --- a/src/libjunction/Makefile.am +++ b/src/libjunction/Makefile.am @@ -26,7 +26,8 @@ noinst_HEADERS = junction-internal.h noinst_LTLIBRARIES = libjunction.la -libjunction_la_SOURCES = export-cache.c fedfs.c junction.c xml.c +libjunction_la_SOURCES = export-cache.c fedfs.c junction.c \ + locations.c nfs.c xml.c CLEANFILES = cscope.in.out cscope.out cscope.po.out *~ DISTCLEANFILES = Makefile.in diff --git a/src/libjunction/junction-internal.h b/src/libjunction/junction-internal.h index a2c35c0..f362d0d 100644 --- a/src/libjunction/junction-internal.h +++ b/src/libjunction/junction-internal.h @@ -95,10 +95,21 @@ FedFsStatus junction_remove_type(const char *pathname); _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, diff --git a/src/libjunction/locations.c b/src/libjunction/locations.c new file mode 100644 index 0000000..58e5543 --- /dev/null +++ b/src/libjunction/locations.c @@ -0,0 +1,131 @@ +/** + * @file src/libjunction/locations.c + * @brief Utility functions to manage NFS locations data + */ + +/* + * Copyright 2011 Oracle. All rights reserved. + * + * This file is part of fedfs-utils. + * + * fedfs-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. + * + * fedfs-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 fedfs-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/src/libjunction/nfs.c b/src/libjunction/nfs.c new file mode 100644 index 0000000..f47d848 --- /dev/null +++ b/src/libjunction/nfs.c @@ -0,0 +1,1681 @@ +/** + * @file src/libjunction/nfs.c + * @brief Create, delete, and read NFS junctions on the local file system + */ + +/* + * Copyright 2011 Oracle. All rights reserved. + * + * This file is part of fedfs-utils. + * + * fedfs-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. + * + * fedfs-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 fedfs-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 "fedfs.h" +#include "nsdb.h" +#include "junction.h" +#include "junction-internal.h" +#include "xlog.h" + +#ifndef NFS_PORT +#define NFS_PORT (2049) +#endif + +/** + * 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 version child element of an NFS location element + */ +#define NFS_XML_VERSION_TAG (const xmlChar *)"version" + +/** + * Name of major version attribute of a version element + */ +#define NFS_XML_VERSION_MAJOR_ATTR (const xmlChar *)"major" + +/** + * Name of minor version attribute of a version element + */ +#define NFS_XML_VERSION_MINOR_ATTR (const xmlChar *)"minor" + +/** + * 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" + +/** + * Tag name of a ttl child element of an NFS location element + */ +#define NFS_XML_TTL_TAG (const xmlChar *)"ttl" + +/** + * 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 "version" 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_version_xml(const char *pathname, xmlNodePtr parent, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr new; + + new = xmlNewTextChild(parent, NULL, NFS_XML_VERSION_TAG, NULL); + if (new == NULL) { + xlog(D_GENERAL, "%s: Failed to add version element for %s", + __func__, pathname); + return FEDFS_ERR_SVRFAULT; + } + + junction_xml_set_int_attribute(new, NFS_XML_VERSION_MAJOR_ATTR, + fsloc->nfl_majorver); + junction_xml_set_int_attribute(new, NFS_XML_VERSION_MINOR_ATTR, + fsloc->nfl_minorver); + + 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; +} + +/** + * 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_ttl_xml(__attribute__((unused)) const char *pathname, + xmlNodePtr parent, struct nfs_fsloc *fsloc) +{ + if (junction_xml_set_int_content(parent, NFS_XML_TTL_TAG, + fsloc->nfl_ttl) == 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_version_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; + retval = nfs_location_validfor_xml(pathname, new, fsloc); + if (retval != FEDFS_OK) + return retval; + return nfs_location_ttl_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 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) +{ + 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); + + 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; + + /* The content of this attribute is ignored */ + retval = junction_add_type(pathname, "nfs"); + if (retval != FEDFS_OK) + return retval; + + 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_remove_type(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 = 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 "version" 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_version(const char *pathname, xmlNodePtr location, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr node; + + node = junction_xml_find_child_by_name(location, NFS_XML_VERSION_TAG); + if (node == NULL) + goto out_err; + + if (!junction_xml_get_int_attribute(node, NFS_XML_VERSION_MAJOR_ATTR, + &fsloc->nfl_majorver)) + goto out_err; + if (!junction_xml_get_int_attribute(node, NFS_XML_VERSION_MINOR_ATTR, + &fsloc->nfl_minorver)) + goto out_err; + + return FEDFS_OK; + +out_err: + xlog(D_GENERAL, "%s: Missing or invalid version element in %s", + __func__, pathname); + return FEDFS_ERR_NOTJUNCT; +} + +/** + * 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 the first "ttl" 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_ttl(const char *pathname, xmlNodePtr location, + struct nfs_fsloc *fsloc) +{ + xmlNodePtr node; + + node = junction_xml_find_child_by_name(location, NFS_XML_TTL_TAG); + if (node == NULL) + goto out_err; + + if (!junction_xml_get_int_content(node, &fsloc->nfl_ttl)) + goto out_err; + + return FEDFS_OK; + +out_err: + xlog(D_GENERAL, "%s: Missing or invalid ttl 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_version(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; + retval = nfs_parse_location_validfor(pathname, location, fsloc); + if (retval != FEDFS_OK) + return retval; + return nfs_parse_location_ttl(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/src/libjunction/xml.c b/src/libjunction/xml.c index bc77fdc..e6f9e89 100644 --- a/src/libjunction/xml.c +++ b/src/libjunction/xml.c @@ -65,6 +65,110 @@ junction_xml_match_node_name(xmlNodePtr node, const xmlChar *name) } /** + * 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 @@ -105,7 +209,6 @@ out: * @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 - * @return true if attribute "attrname" has a valid integer value */ void junction_xml_set_int_attribute(xmlNodePtr node, const xmlChar *attrname, @@ -118,6 +221,56 @@ junction_xml_set_int_attribute(xmlNodePtr node, const xmlChar *attrname, } /** + * 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