2018-01-29 23:28:49

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH v4 0/5] NFS basic junction support for nfs-utils

Still UNTESTED.

A while back I announced the deprecation of fedfs-utils. There were
a handful of components in fedfs-utils that we decided to keep. One
of those keepers was the "nfsref" command. (The other was autofs
support for /nfs4, which I hope Ian Kent is making progress on ;-)

This series introduces "nfsref" to nfs-utils, minus the overhead of
the LDAP / FedFS machinery, and it builds support for NFS basic
junctions into mountd, replacing the need to install a plug-in DLL
from fedfs-utils.

I didn't apply a lot of brain cells to this port, so it's perhaps a
little larger than it needs to be. Still, it achieves a completely
LDAP-free implementation that resides 100% in nfs-utils. I'm
interested in comments about the approach before I do more testing
and refinement.

./configure --enable-junction --enable-caps

is needed before building.


Changes since v3:
Broke up the first patch because it was apparently too large to
be reflected through vger.kernel.org.


Changes since v2:
None.


Changes since RFC (v1):
Patch 2/3 in the original series added the libnfsjunct.so DLL to
nfs-utils. Series v2 instead replaces 2/3 with a patch that puts
support for junctions into mountd without the need for a DLL.

---

Chuck Lever (5):
Add headers to support libjunction
Add infrastructure for libjunction
Add LDAP-free version of libjunction to nfs-utils
mountd: Solder in support for NFS basic junctions
Add LDAP-free 'nfsref' command


aclocal/libxml2.m4 | 15
configure.ac | 17
support/Makefile.am | 4
support/include/Makefile.am | 2
support/include/fedfs_admin.h | 342 +++++++
support/include/junction.h | 125 +++
support/junction/Makefile.am | 34 +
support/junction/display.c | 159 +++
support/junction/export-cache.c | 118 +++
support/junction/junction-internal.h | 121 +++
support/junction/junction.c | 494 +++++++++++
support/junction/locations.c | 131 +++
support/junction/nfs.c | 1564 ++++++++++++++++++++++++++++++++++
support/junction/path.c | 345 ++++++++
support/junction/xml.c | 401 +++++++++
utils/Makefile.am | 4
utils/mountd/Makefile.am | 8
utils/mountd/cache.c | 189 ++--
utils/nfsref/Makefile.am | 39 +
utils/nfsref/add.c | 271 ++++++
utils/nfsref/lookup.c | 211 +++++
utils/nfsref/nfsref.c | 188 ++++
utils/nfsref/nfsref.h | 47 +
utils/nfsref/nfsref.man | 180 ++++
utils/nfsref/remove.c | 145 +++
25 files changed, 5050 insertions(+), 104 deletions(-)
create mode 100644 aclocal/libxml2.m4
create mode 100644 support/include/fedfs_admin.h
create mode 100644 support/include/junction.h
create mode 100644 support/junction/Makefile.am
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
create mode 100644 utils/nfsref/Makefile.am
create mode 100644 utils/nfsref/add.c
create mode 100644 utils/nfsref/lookup.c
create mode 100644 utils/nfsref/nfsref.c
create mode 100644 utils/nfsref/nfsref.h
create mode 100644 utils/nfsref/nfsref.man
create mode 100644 utils/nfsref/remove.c

--
Chuck Lever


2018-01-29 23:28:57

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH v4 1/5] Add headers to support libjunction

libjunction contains support for reading or updating a junction
that resides on an NFS server. It is used by mountd to read junction
information from an exported filesystem, and by the nfsref command
line tool. These will be introduced in subsequent patches.

The source code for libjunction used to be maintained in
fedfs-utils, but fedfs-utils is being retired. Although interest in
FedFS junctions has waned, NFS basic junctions are still appealing
enough that we've decided we want to continue supporting them.
Therefore this patch moves the non-LDAP parts of libjunction into
nfs-utils, to enable nfs-utils to also support the user-visible
pieces of NFS basic junctions.

Signed-off-by: Chuck Lever <[email protected]>
---
support/include/Makefile.am | 2
support/include/fedfs_admin.h | 342 +++++++++++++++++++++++++++++++++++++++++
support/include/junction.h | 125 +++++++++++++++
3 files changed, 469 insertions(+)
create mode 100644 support/include/fedfs_admin.h
create mode 100644 support/include/junction.h

diff --git a/support/include/Makefile.am b/support/include/Makefile.am
index 5c80c8b..11cb162 100644
--- a/support/include/Makefile.am
+++ b/support/include/Makefile.am
@@ -5,7 +5,9 @@ SUBDIRS = nfs rpcsvc sys
noinst_HEADERS = \
cld.h \
exportfs.h \
+ fedfs_admin.h \
ha-callout.h \
+ junction.h \
misc.h \
nfs_mntent.h \
nfs_paths.h \
diff --git a/support/include/fedfs_admin.h b/support/include/fedfs_admin.h
new file mode 100644
index 0000000..d8b6715
--- /dev/null
+++ b/support/include/fedfs_admin.h
@@ -0,0 +1,342 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#ifndef _FEDFS_ADMIN_H_RPCGEN
+#define _FEDFS_ADMIN_H_RPCGEN
+
+#include <rpc/rpc.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+enum FedFsStatus {
+ FEDFS_OK = 0,
+ FEDFS_ERR_ACCESS = 1,
+ FEDFS_ERR_BADCHAR = 2,
+ FEDFS_ERR_BADNAME = 3,
+ FEDFS_ERR_NAMETOOLONG = 4,
+ FEDFS_ERR_LOOP = 5,
+ FEDFS_ERR_BADXDR = 6,
+ FEDFS_ERR_EXIST = 7,
+ FEDFS_ERR_INVAL = 8,
+ FEDFS_ERR_IO = 9,
+ FEDFS_ERR_NOSPC = 10,
+ FEDFS_ERR_NOTJUNCT = 11,
+ FEDFS_ERR_NOTLOCAL = 12,
+ FEDFS_ERR_PERM = 13,
+ FEDFS_ERR_ROFS = 14,
+ FEDFS_ERR_SVRFAULT = 15,
+ FEDFS_ERR_NOTSUPP = 16,
+ FEDFS_ERR_NSDB_ROUTE = 17,
+ FEDFS_ERR_NSDB_DOWN = 18,
+ FEDFS_ERR_NSDB_CONN = 19,
+ FEDFS_ERR_NSDB_AUTH = 20,
+ FEDFS_ERR_NSDB_LDAP = 21,
+ FEDFS_ERR_NSDB_LDAP_VAL = 22,
+ FEDFS_ERR_NSDB_NONCE = 23,
+ FEDFS_ERR_NSDB_NOFSN = 24,
+ FEDFS_ERR_NSDB_NOFSL = 25,
+ FEDFS_ERR_NSDB_RESPONSE = 26,
+ FEDFS_ERR_NSDB_FAULT = 27,
+ FEDFS_ERR_NSDB_PARAMS = 28,
+ FEDFS_ERR_NSDB_LDAP_REFERRAL = 29,
+ FEDFS_ERR_NSDB_LDAP_REFERRAL_VAL = 30,
+ FEDFS_ERR_NSDB_LDAP_REFERRAL_NOTFOLLOWED = 31,
+ FEDFS_ERR_NSDB_PARAMS_LDAP_REFERRAL = 32,
+ FEDFS_ERR_PATH_TYPE_UNSUPP = 33,
+ FEDFS_ERR_DELAY = 34,
+ FEDFS_ERR_NO_CACHE = 35,
+ FEDFS_ERR_UNKNOWN_CACHE = 36,
+ FEDFS_ERR_NO_CACHE_UPDATE = 37,
+};
+typedef enum FedFsStatus FedFsStatus;
+
+typedef struct {
+ u_int utf8string_len;
+ char *utf8string_val;
+} utf8string;
+
+typedef utf8string ascii_REQUIRED4;
+
+typedef utf8string utf8val_REQUIRED4;
+
+typedef char FedFsUuid[16];
+
+struct FedFsNsdbName {
+ u_int port;
+ utf8val_REQUIRED4 hostname;
+};
+typedef struct FedFsNsdbName FedFsNsdbName;
+
+typedef ascii_REQUIRED4 FedFsPathComponent;
+
+typedef struct {
+ u_int FedFsPathName_len;
+ FedFsPathComponent *FedFsPathName_val;
+} FedFsPathName;
+
+struct FedFsFsn {
+ FedFsUuid fsnUuid;
+ FedFsNsdbName nsdbName;
+};
+typedef struct FedFsFsn FedFsFsn;
+
+enum FedFsFslType {
+ FEDFS_NFS_FSL = 0,
+};
+typedef enum FedFsFslType FedFsFslType;
+
+struct FedFsNfsFsl {
+ FedFsUuid fslUuid;
+ u_int port;
+ utf8val_REQUIRED4 hostname;
+ FedFsPathName path;
+};
+typedef struct FedFsNfsFsl FedFsNfsFsl;
+
+struct FedFsFsl {
+ FedFsFslType type;
+ union {
+ FedFsNfsFsl nfsFsl;
+ } FedFsFsl_u;
+};
+typedef struct FedFsFsl FedFsFsl;
+
+enum FedFsPathType {
+ FEDFS_PATH_SYS = 0,
+ FEDFS_PATH_NFS = 1,
+};
+typedef enum FedFsPathType FedFsPathType;
+
+struct FedFsPath {
+ FedFsPathType type;
+ union {
+ FedFsPathName adminPath;
+ FedFsPathName nfsPath;
+ } FedFsPath_u;
+};
+typedef struct FedFsPath FedFsPath;
+
+struct FedFsCreateArgs {
+ FedFsPath path;
+ FedFsFsn fsn;
+};
+typedef struct FedFsCreateArgs FedFsCreateArgs;
+
+enum FedFsResolveType {
+ FEDFS_RESOLVE_NONE = 0,
+ FEDFS_RESOLVE_CACHE = 1,
+ FEDFS_RESOLVE_NSDB = 2,
+};
+typedef enum FedFsResolveType FedFsResolveType;
+
+struct FedFsLookupArgs {
+ FedFsPath path;
+ FedFsResolveType resolve;
+};
+typedef struct FedFsLookupArgs FedFsLookupArgs;
+
+struct FedFsLookupResOk {
+ FedFsFsn fsn;
+ struct {
+ u_int fsl_len;
+ FedFsFsl *fsl_val;
+ } fsl;
+};
+typedef struct FedFsLookupResOk FedFsLookupResOk;
+
+struct FedFsLookupResReferralVal {
+ FedFsNsdbName targetNsdb;
+ u_int ldapResultCode;
+};
+typedef struct FedFsLookupResReferralVal FedFsLookupResReferralVal;
+
+struct FedFsLookupRes {
+ FedFsStatus status;
+ union {
+ FedFsLookupResOk resok;
+ u_int ldapResultCode;
+ FedFsNsdbName targetNsdb;
+ FedFsLookupResReferralVal resReferralVal;
+ } FedFsLookupRes_u;
+};
+typedef struct FedFsLookupRes FedFsLookupRes;
+
+enum FedFsConnectionSec {
+ FEDFS_SEC_NONE = 0,
+ FEDFS_SEC_TLS = 1,
+};
+typedef enum FedFsConnectionSec FedFsConnectionSec;
+
+struct FedFsNsdbParams {
+ FedFsConnectionSec secType;
+ union {
+ struct {
+ u_int secData_len;
+ char *secData_val;
+ } secData;
+ } FedFsNsdbParams_u;
+};
+typedef struct FedFsNsdbParams FedFsNsdbParams;
+
+struct FedFsSetNsdbParamsArgs {
+ FedFsNsdbName nsdbName;
+ FedFsNsdbParams params;
+};
+typedef struct FedFsSetNsdbParamsArgs FedFsSetNsdbParamsArgs;
+
+struct FedFsGetNsdbParamsRes {
+ FedFsStatus status;
+ union {
+ FedFsNsdbParams params;
+ } FedFsGetNsdbParamsRes_u;
+};
+typedef struct FedFsGetNsdbParamsRes FedFsGetNsdbParamsRes;
+
+struct FedFsGetLimitedNsdbParamsRes {
+ FedFsStatus status;
+ union {
+ FedFsConnectionSec secType;
+ } FedFsGetLimitedNsdbParamsRes_u;
+};
+typedef struct FedFsGetLimitedNsdbParamsRes FedFsGetLimitedNsdbParamsRes;
+#define FEDFS_ADMIN_X
+
+#define FEDFS_PROG 100418
+#define FEDFS_V1 1
+
+#if defined(__STDC__) || defined(__cplusplus)
+#define FEDFS_NULL 0
+extern void * fedfs_null_1(void *, CLIENT *);
+extern void * fedfs_null_1_svc(void *, struct svc_req *);
+#define FEDFS_CREATE_JUNCTION 1
+extern FedFsStatus * fedfs_create_junction_1(FedFsCreateArgs *, CLIENT *);
+extern FedFsStatus * fedfs_create_junction_1_svc(FedFsCreateArgs *, struct svc_req *);
+#define FEDFS_DELETE_JUNCTION 2
+extern FedFsStatus * fedfs_delete_junction_1(FedFsPath *, CLIENT *);
+extern FedFsStatus * fedfs_delete_junction_1_svc(FedFsPath *, struct svc_req *);
+#define FEDFS_LOOKUP_JUNCTION 3
+extern FedFsLookupRes * fedfs_lookup_junction_1(FedFsLookupArgs *, CLIENT *);
+extern FedFsLookupRes * fedfs_lookup_junction_1_svc(FedFsLookupArgs *, struct svc_req *);
+#define FEDFS_CREATE_REPLICATION 7
+extern FedFsStatus * fedfs_create_replication_1(FedFsCreateArgs *, CLIENT *);
+extern FedFsStatus * fedfs_create_replication_1_svc(FedFsCreateArgs *, struct svc_req *);
+#define FEDFS_DELETE_REPLICATION 8
+extern FedFsStatus * fedfs_delete_replication_1(FedFsPath *, CLIENT *);
+extern FedFsStatus * fedfs_delete_replication_1_svc(FedFsPath *, struct svc_req *);
+#define FEDFS_LOOKUP_REPLICATION 9
+extern FedFsLookupRes * fedfs_lookup_replication_1(FedFsLookupArgs *, CLIENT *);
+extern FedFsLookupRes * fedfs_lookup_replication_1_svc(FedFsLookupArgs *, struct svc_req *);
+#define FEDFS_SET_NSDB_PARAMS 4
+extern FedFsStatus * fedfs_set_nsdb_params_1(FedFsSetNsdbParamsArgs *, CLIENT *);
+extern FedFsStatus * fedfs_set_nsdb_params_1_svc(FedFsSetNsdbParamsArgs *, struct svc_req *);
+#define FEDFS_GET_NSDB_PARAMS 5
+extern FedFsGetNsdbParamsRes * fedfs_get_nsdb_params_1(FedFsNsdbName *, CLIENT *);
+extern FedFsGetNsdbParamsRes * fedfs_get_nsdb_params_1_svc(FedFsNsdbName *, struct svc_req *);
+#define FEDFS_GET_LIMITED_NSDB_PARAMS 6
+extern FedFsGetLimitedNsdbParamsRes * fedfs_get_limited_nsdb_params_1(FedFsNsdbName *, CLIENT *);
+extern FedFsGetLimitedNsdbParamsRes * fedfs_get_limited_nsdb_params_1_svc(FedFsNsdbName *, struct svc_req *);
+extern int fedfs_prog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
+
+#else /* K&R C */
+#define FEDFS_NULL 0
+extern void * fedfs_null_1();
+extern void * fedfs_null_1_svc();
+#define FEDFS_CREATE_JUNCTION 1
+extern FedFsStatus * fedfs_create_junction_1();
+extern FedFsStatus * fedfs_create_junction_1_svc();
+#define FEDFS_DELETE_JUNCTION 2
+extern FedFsStatus * fedfs_delete_junction_1();
+extern FedFsStatus * fedfs_delete_junction_1_svc();
+#define FEDFS_LOOKUP_JUNCTION 3
+extern FedFsLookupRes * fedfs_lookup_junction_1();
+extern FedFsLookupRes * fedfs_lookup_junction_1_svc();
+#define FEDFS_CREATE_REPLICATION 7
+extern FedFsStatus * fedfs_create_replication_1();
+extern FedFsStatus * fedfs_create_replication_1_svc();
+#define FEDFS_DELETE_REPLICATION 8
+extern FedFsStatus * fedfs_delete_replication_1();
+extern FedFsStatus * fedfs_delete_replication_1_svc();
+#define FEDFS_LOOKUP_REPLICATION 9
+extern FedFsLookupRes * fedfs_lookup_replication_1();
+extern FedFsLookupRes * fedfs_lookup_replication_1_svc();
+#define FEDFS_SET_NSDB_PARAMS 4
+extern FedFsStatus * fedfs_set_nsdb_params_1();
+extern FedFsStatus * fedfs_set_nsdb_params_1_svc();
+#define FEDFS_GET_NSDB_PARAMS 5
+extern FedFsGetNsdbParamsRes * fedfs_get_nsdb_params_1();
+extern FedFsGetNsdbParamsRes * fedfs_get_nsdb_params_1_svc();
+#define FEDFS_GET_LIMITED_NSDB_PARAMS 6
+extern FedFsGetLimitedNsdbParamsRes * fedfs_get_limited_nsdb_params_1();
+extern FedFsGetLimitedNsdbParamsRes * fedfs_get_limited_nsdb_params_1_svc();
+extern int fedfs_prog_1_freeresult ();
+#endif /* K&R C */
+
+/* the xdr functions */
+
+#if defined(__STDC__) || defined(__cplusplus)
+extern bool_t xdr_FedFsStatus (XDR *, FedFsStatus*);
+extern bool_t xdr_utf8string (XDR *, utf8string*);
+extern bool_t xdr_ascii_REQUIRED4 (XDR *, ascii_REQUIRED4*);
+extern bool_t xdr_utf8val_REQUIRED4 (XDR *, utf8val_REQUIRED4*);
+extern bool_t xdr_FedFsUuid (XDR *, FedFsUuid);
+extern bool_t xdr_FedFsNsdbName (XDR *, FedFsNsdbName*);
+extern bool_t xdr_FedFsPathComponent (XDR *, FedFsPathComponent*);
+extern bool_t xdr_FedFsPathName (XDR *, FedFsPathName*);
+extern bool_t xdr_FedFsFsn (XDR *, FedFsFsn*);
+extern bool_t xdr_FedFsFslType (XDR *, FedFsFslType*);
+extern bool_t xdr_FedFsNfsFsl (XDR *, FedFsNfsFsl*);
+extern bool_t xdr_FedFsFsl (XDR *, FedFsFsl*);
+extern bool_t xdr_FedFsPathType (XDR *, FedFsPathType*);
+extern bool_t xdr_FedFsPath (XDR *, FedFsPath*);
+extern bool_t xdr_FedFsCreateArgs (XDR *, FedFsCreateArgs*);
+extern bool_t xdr_FedFsResolveType (XDR *, FedFsResolveType*);
+extern bool_t xdr_FedFsLookupArgs (XDR *, FedFsLookupArgs*);
+extern bool_t xdr_FedFsLookupResOk (XDR *, FedFsLookupResOk*);
+extern bool_t xdr_FedFsLookupResReferralVal (XDR *, FedFsLookupResReferralVal*);
+extern bool_t xdr_FedFsLookupRes (XDR *, FedFsLookupRes*);
+extern bool_t xdr_FedFsConnectionSec (XDR *, FedFsConnectionSec*);
+extern bool_t xdr_FedFsNsdbParams (XDR *, FedFsNsdbParams*);
+extern bool_t xdr_FedFsSetNsdbParamsArgs (XDR *, FedFsSetNsdbParamsArgs*);
+extern bool_t xdr_FedFsGetNsdbParamsRes (XDR *, FedFsGetNsdbParamsRes*);
+extern bool_t xdr_FedFsGetLimitedNsdbParamsRes (XDR *, FedFsGetLimitedNsdbParamsRes*);
+
+#else /* K&R C */
+extern bool_t xdr_FedFsStatus ();
+extern bool_t xdr_utf8string ();
+extern bool_t xdr_ascii_REQUIRED4 ();
+extern bool_t xdr_utf8val_REQUIRED4 ();
+extern bool_t xdr_FedFsUuid ();
+extern bool_t xdr_FedFsNsdbName ();
+extern bool_t xdr_FedFsPathComponent ();
+extern bool_t xdr_FedFsPathName ();
+extern bool_t xdr_FedFsFsn ();
+extern bool_t xdr_FedFsFslType ();
+extern bool_t xdr_FedFsNfsFsl ();
+extern bool_t xdr_FedFsFsl ();
+extern bool_t xdr_FedFsPathType ();
+extern bool_t xdr_FedFsPath ();
+extern bool_t xdr_FedFsCreateArgs ();
+extern bool_t xdr_FedFsResolveType ();
+extern bool_t xdr_FedFsLookupArgs ();
+extern bool_t xdr_FedFsLookupResOk ();
+extern bool_t xdr_FedFsLookupResReferralVal ();
+extern bool_t xdr_FedFsLookupRes ();
+extern bool_t xdr_FedFsConnectionSec ();
+extern bool_t xdr_FedFsNsdbParams ();
+extern bool_t xdr_FedFsSetNsdbParamsArgs ();
+extern bool_t xdr_FedFsGetNsdbParamsRes ();
+extern bool_t xdr_FedFsGetLimitedNsdbParamsRes ();
+
+#endif /* K&R C */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_FEDFS_ADMIN_H_RPCGEN */
diff --git a/support/include/junction.h b/support/include/junction.h
new file mode 100644
index 0000000..a5f3844
--- /dev/null
+++ b/support/include/junction.h
@@ -0,0 +1,125 @@
+/*
+ * @file support/include/junction.h
+ * @brief Declarations for libjunction.a
+ */
+
+/*
+ * Copyright 2010, 2018 Oracle. All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * nfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with nfs-utils. If not, see:
+ *
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#ifndef _NFS_JUNCTION_H_
+#define _NFS_JUNCTION_H_
+
+#include <stdint.h>
+
+/* Machine-generated XDR definitions for FedFS Admin protocol */
+#include "fedfs_admin.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;
+};
+
+
+/**
+ ** NFS location data management functions
+ **/
+
+void nfs_free_location(struct nfs_fsloc *location);
+void nfs_free_locations(struct nfs_fsloc *locations);
+struct nfs_fsloc *nfs_new_location(void);
+
+__attribute_malloc__
+char **nfs_dup_string_array(char **array);
+void nfs_free_string_array(char **array);
+
+
+/**
+ ** NFS junction management functions
+ **/
+
+FedFsStatus nfs_delete_junction(const char *pathname);
+FedFsStatus nfs_add_junction(const char *pathname,
+ struct nfs_fsloc *locations);
+FedFsStatus nfs_get_locations(const char *pathname,
+ struct nfs_fsloc **locations);
+FedFsStatus nfs_is_prejunction(const char *pathname);
+FedFsStatus nfs_is_junction(const char *pathname);
+
+
+/**
+ ** Flush kernel NFS server's export cache
+ **/
+FedFsStatus junction_flush_exports_cache(void);
+
+/**
+ ** Pathname conversion helpers
+ **/
+void nsdb_free_string_array(char **strings);
+FedFsStatus nsdb_path_array_to_posix(char * const *path_array,
+ char **pathname);
+FedFsStatus nsdb_posix_to_path_array(const char *pathname,
+ char ***path_array);
+
+/**
+ ** Readability helpers
+ **/
+
+const char *nsdb_display_fedfsconnectionsec(const FedFsConnectionSec sectype);
+const char *nsdb_display_fedfsstatus(const FedFsStatus status);
+void nsdb_print_fedfsstatus(const FedFsStatus status);
+
+#endif /* !_NFS_JUNCTION_H_ */


2018-01-29 23:29:05

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH v4 2/5] Add infrastructure for libjunction

The source code for libjunction used to be maintained in
fedfs-utils, but fedfs-utils is being retired. Although interest in
FedFS junctions has waned, NFS basic junctions are still appealing
enough that we've decided we want to continue supporting them.
Therefore this patch moves the non-LDAP parts of libjunction into
nfs-utils, to enable nfs-utils to also support the user-visible
pieces of NFS basic junctions.

I've added yet another configure command line option to control
whether libjunction is built. It defaults to "disabled" for the
moment. Reasons you might want to leave it disabled:

- Your distribution still provides fedfs-utils, which already has
a working libjunction which is installed separately

- Your distribution does not provide libxml2, which is required
to support libjunction

When it's disabled, nfs-utils builds just like it did before this
patch, and there's no new dependency on libxml2.

Signed-off-by: Chuck Lever <[email protected]>
---
aclocal/libxml2.m4 | 15 ++++
configure.ac | 11 +++
support/Makefile.am | 4 +
support/junction/Makefile.am | 30 ++++++++
support/junction/junction-internal.h | 121 ++++++++++++++++++++++++++++++++++
5 files changed, 181 insertions(+)
create mode 100644 aclocal/libxml2.m4
create mode 100644 support/junction/Makefile.am
create mode 100644 support/junction/junction-internal.h

diff --git a/aclocal/libxml2.m4 b/aclocal/libxml2.m4
new file mode 100644
index 0000000..5c399b2
--- /dev/null
+++ b/aclocal/libxml2.m4
@@ -0,0 +1,15 @@
+dnl Checks for libxml2.so
+AC_DEFUN([AC_LIBXML2], [
+
+ if test "$enable_junction" = yes; then
+
+ dnl look for the library; do not add to LIBS if found
+ AC_CHECK_LIB([xml2], [xmlParseFile], [LIBXML2=-lxml2],
+ [AC_MSG_ERROR([libxml2 not found.])])
+ AC_SUBST(LIBXML2)
+
+ dnl XXX should also check for presence of xml headers
+
+ fi
+
+])dnl
diff --git a/configure.ac b/configure.ac
index 672dd40..8e7f036 100644
--- a/configure.ac
+++ b/configure.ac
@@ -180,6 +180,13 @@ else
enable_libmount=no
fi

+AC_ARG_ENABLE(junction,
+ [AC_HELP_STRING([--enable-junction],
+ [enable support for NFS junctions @<:@default=no@:>@])],
+ enable_junction=$enableval,
+ enable_junction=no)
+AM_CONDITIONAL(CONFIG_JUNCTION, [test "$enable_junction" = "yes" ])
+
AC_ARG_ENABLE(tirpc,
[AC_HELP_STRING([--disable-tirpc],
[disable use of TI-RPC library @<:@default=no@:>@])],
@@ -244,6 +251,9 @@ AC_LIBTIRPC
dnl Check for -lcap
AC_LIBCAP

+dnl Check for -lxml2
+AC_LIBXML2
+
# Check whether user wants TCP wrappers support
AC_TCP_WRAPPERS

@@ -572,6 +582,7 @@ AC_CONFIG_FILES([
support/include/sys/fs/Makefile
support/include/sys/Makefile
support/include/Makefile
+ support/junction/Makefile
support/misc/Makefile
support/nfs/Makefile
support/nsm/Makefile
diff --git a/support/Makefile.am b/support/Makefile.am
index 8365d3b..c962d4d 100644
--- a/support/Makefile.am
+++ b/support/Makefile.am
@@ -6,6 +6,10 @@ if CONFIG_NFSV4
OPTDIRS += nfsidmap
endif

+if CONFIG_JUNCTION
+OPTDIRS += junction
+endif
+
SUBDIRS = export include misc nfs nsm $(OPTDIRS)

MAINTAINERCLEANFILES = Makefile.in
diff --git a/support/junction/Makefile.am b/support/junction/Makefile.am
new file mode 100644
index 0000000..0d43b0a
--- /dev/null
+++ b/support/junction/Makefile.am
@@ -0,0 +1,30 @@
+##
+## @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
+
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = -I. -I../include -I/usr/include/libxml2
diff --git a/support/junction/junction-internal.h b/support/junction/junction-internal.h
new file mode 100644
index 0000000..3dff4cc
--- /dev/null
+++ b/support/junction/junction-internal.h
@@ -0,0 +1,121 @@
+/*
+ * @file support/junction/junction-internal.h
+ * @brief Internal declarations for libjunction.a
+ */
+
+/*
+ * Copyright 2011, 2018 Oracle. All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * nfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with nfs-utils. If not, see:
+ *
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#ifndef _FEDFS_JUNCTION_INTERNAL_H_
+#define _FEDFS_JUNCTION_INTERNAL_H_
+
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+
+/**
+ ** Names of extended attributes that store junction data
+ **/
+
+/**
+ * Name of extended attribute containing saved mode bits
+ */
+#define JUNCTION_XATTR_NAME_MODE "trusted.junction.mode"
+
+/**
+ * Name of extended attribute containing NFS-related junction data
+ */
+#define JUNCTION_XATTR_NAME_NFS "trusted.junction.nfs"
+
+
+/**
+ ** Names of XML elements and attributes that represent junction data
+ **/
+
+/**
+ * Tag name of root element of a junction XML document
+ */
+#define JUNCTION_XML_ROOT_TAG (const xmlChar *)"junction"
+
+/**
+ * Tag name of fileset element of a junction XML document
+ */
+#define JUNCTION_XML_FILESET_TAG (const xmlChar *)"fileset"
+
+/**
+ * Tag name of savedmode element of a junction XML document
+ */
+#define JUNCTION_XML_SAVEDMODE_TAG (const xmlChar *)"savedmode"
+
+/**
+ * Name of mode bits attribute on a savedmode element
+ */
+#define JUNCTION_XML_MODEBITS_ATTR (const xmlChar *)"bits"
+
+/**
+ ** Junction helper functions
+ **/
+
+FedFsStatus junction_open_path(const char *pathname, int *fd);
+FedFsStatus junction_is_directory(int fd, const char *path);
+FedFsStatus junction_is_sticky_bit_set(int fd, const char *path);
+FedFsStatus junction_set_sticky_bit(int fd, const char *path);
+FedFsStatus junction_is_xattr_present(int fd, const char *path,
+ const char *name);
+FedFsStatus junction_read_xattr(int fd, const char *path, const char *name,
+ char **contents);
+FedFsStatus junction_get_xattr(int fd, const char *path, const char *name,
+ void **contents, size_t *contentlen);
+FedFsStatus junction_set_xattr(int fd, const char *path, const char *name,
+ const void *contents, const size_t contentlen);
+FedFsStatus junction_remove_xattr(int fd, const char *pathname,
+ const char *name);
+FedFsStatus junction_get_mode(const char *pathname, mode_t *mode);
+FedFsStatus junction_save_mode(const char *pathname);
+FedFsStatus junction_restore_mode(const char *pathname);
+
+
+/**
+ ** XML helper functions
+ **/
+
+_Bool junction_xml_is_empty(const xmlChar *content);
+_Bool junction_xml_match_node_name(xmlNodePtr node,
+ const xmlChar *name);
+xmlNodePtr junction_xml_find_child_by_name(xmlNodePtr parent,
+ const xmlChar *name);
+_Bool junction_xml_get_bool_attribute(xmlNodePtr node,
+ const xmlChar *attrname, _Bool *value);
+void junction_xml_set_bool_attribute(xmlNodePtr node,
+ const xmlChar *attrname, _Bool value);
+_Bool junction_xml_get_u8_attribute(xmlNodePtr node,
+ const xmlChar *attrname, uint8_t *value);
+_Bool junction_xml_get_int_attribute(xmlNodePtr node,
+ const xmlChar *attrname, int *value);
+void junction_xml_set_int_attribute(xmlNodePtr node,
+ const xmlChar *attrname, int value);
+_Bool junction_xml_get_int_content(xmlNodePtr node, int *value);
+xmlNodePtr junction_xml_set_int_content(xmlNodePtr parent,
+ const xmlChar *name, int value);
+FedFsStatus junction_xml_parse(const char *pathname, const char *name,
+ xmlDocPtr *doc);
+FedFsStatus junction_xml_write(const char *pathname, const char *name,
+ xmlDocPtr doc);
+
+#endif /* !_FEDFS_JUNCTION_INTERNAL_H_ */


2018-01-29 23:29:15

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH v4 3/5] Add LDAP-free version of libjunction to nfs-utils

A "junction" is a physical filesystem object that contains an NFS
referral. On Linux, a junction is a directory with special mode bits
that has an extended attribute containing referral location
information, represented as XML.

A junction can contain either a list of target locations, or it
can contain the location of an LDAP directory entry where the actual
referral information is maintained. The former is called an "NFS
basic junction", the latter is a "FedFS junction".

libjunction contains support for reading or updating a junction
that resides on an NFS server.

Signed-off-by: Chuck Lever <[email protected]>
---
support/junction/Makefile.am | 4
support/junction/display.c | 159 ++++
support/junction/export-cache.c | 118 +++
support/junction/junction.c | 494 ++++++++++++
support/junction/locations.c | 131 +++
support/junction/nfs.c | 1564 +++++++++++++++++++++++++++++++++++++++
support/junction/path.c | 345 +++++++++
support/junction/xml.c | 401 ++++++++++
8 files changed, 3216 insertions(+)
create mode 100644 support/junction/display.c
create mode 100644 support/junction/export-cache.c
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

diff --git a/support/junction/Makefile.am b/support/junction/Makefile.am
index 0d43b0a..97e7426 100644
--- a/support/junction/Makefile.am
+++ b/support/junction/Makefile.am
@@ -25,6 +25,10 @@

noinst_HEADERS = junction-internal.h

+noinst_LTLIBRARIES = libjunction.la
+libjunction_la_SOURCES = display.c export-cache.c junction.c \
+ locations.c nfs.c path.c xml.c
+
MAINTAINERCLEANFILES = Makefile.in

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


2018-01-29 23:29:22

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH v4 4/5] mountd: Solder in support for NFS basic junctions

This patch does two things:

1. Reverts ab74900ff59e ("mountd: Support junction management
plug-ins")

2. Re-implements support for NFS basic junctions directly in mountd

So no more support for FedFS junctions, and no more need to have a
dynamically load component installed from another package.

The downside is that mountd has to be linked with libxml2 to get
this support. Thus to make the use of libxml2 optional, built-in
support for junctions is enabled only when --enable-junction is
specified on the command line.

Signed-off-by: Chuck Lever <[email protected]>
---
configure.ac | 5 -
utils/mountd/Makefile.am | 8 ++
utils/mountd/cache.c | 189 ++++++++++++++++++++++------------------------
3 files changed, 98 insertions(+), 104 deletions(-)

diff --git a/configure.ac b/configure.ac
index 8e7f036..d2e48a5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -308,8 +308,6 @@ AC_CHECK_FUNC([getservbyname], ,

AC_CHECK_LIB([crypt], [crypt], [LIBCRYPT="-lcrypt"])

-AC_CHECK_LIB([dl], [dlclose], [LIBDL="-ldl"])
-
if test "$enable_nfsv4" = yes; then
dnl check for libevent libraries and headers
AC_LIBEVENT
@@ -372,7 +370,6 @@ AC_SUBST(LIBSOCKET)
AC_SUBST(LIBCRYPT)
AC_SUBST(LIBBSD)
AC_SUBST(LIBBLKID)
-AC_SUBST(LIBDL)

if test "$enable_libmount" = yes; then
AC_CHECK_LIB(mount, mnt_context_do_mount, [LIBMOUNT="-lmount"], AC_MSG_ERROR([libmount needed]))
@@ -466,7 +463,7 @@ AC_CHECK_HEADERS([arpa/inet.h fcntl.h libintl.h limits.h \
stdlib.h string.h sys/file.h sys/ioctl.h sys/mount.h \
sys/param.h sys/socket.h sys/time.h sys/vfs.h \
syslog.h unistd.h com_err.h et/com_err.h \
- ifaddrs.h nfs-plugin.h libio.h])
+ ifaddrs.h libio.h])

dnl *************************************************************
dnl Checks for typedefs, structures, and compiler characteristics
diff --git a/utils/mountd/Makefile.am b/utils/mountd/Makefile.am
index 153a90a..73eeb3f 100644
--- a/utils/mountd/Makefile.am
+++ b/utils/mountd/Makefile.am
@@ -1,5 +1,10 @@
## Process this file with automake to produce Makefile.in

+OPTLIBS =
+if CONFIG_JUNCTION
+OPTLIBS += ../../support/junction/libjunction.la $(LIBXML2)
+endif
+
man8_MANS = mountd.man
EXTRA_DIST = $(man8_MANS)

@@ -13,7 +18,8 @@ mountd_SOURCES = mountd.c mount_dispatch.c auth.c rmtab.c cache.c \
mountd_LDADD = ../../support/export/libexport.a \
../../support/nfs/libnfs.la \
../../support/misc/libmisc.a \
- $(LIBBSD) $(LIBWRAP) $(LIBNSL) $(LIBBLKID) $(LIBDL) $(LIBTIRPC)
+ $(OPTLIBS) \
+ $(LIBBSD) $(LIBWRAP) $(LIBNSL) $(LIBBLKID) $(LIBTIRPC)
mountd_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) \
-I$(top_builddir)/support/include \
-I$(top_srcdir)/support/export
diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
index e49300d..6f42512 100644
--- a/utils/mountd/cache.c
+++ b/utils/mountd/cache.c
@@ -976,10 +976,15 @@ lookup_export(char *dom, char *path, struct addrinfo *ai)
return found;
}

-#ifdef HAVE_NFS_PLUGIN_H
-#include <dlfcn.h>
-#include <link.h>
-#include <nfs-plugin.h>
+#ifdef CONFIG_JUNCTION
+
+#include "junction.h"
+
+struct nfs_fsloc_set {
+ int ns_ttl;
+ struct nfs_fsloc *ns_current;
+ struct nfs_fsloc *ns_list;
+};

/*
* Find the export entry for the parent of "pathname".
@@ -1035,13 +1040,39 @@ out_default:
return mkexportent("*", "/", "insecure");
}

+static int get_next_location(struct nfs_fsloc_set *locset,
+ char **hostname, char **export_path, int *ttl)
+{
+ char *hostname_tmp, *export_path_tmp;
+ struct nfs_fsloc *fsloc;
+
+ if (locset->ns_current == NULL)
+ return ENOENT;
+ fsloc = locset->ns_current;
+
+ hostname_tmp = strdup(fsloc->nfl_hostname);
+ if (hostname_tmp == NULL)
+ return ENOMEM;
+
+ if (nsdb_path_array_to_posix(fsloc->nfl_rootpath,
+ &export_path_tmp)) {
+ free(hostname_tmp);
+ return EINVAL;
+ }
+
+ *hostname = hostname_tmp;
+ *export_path = export_path_tmp;
+ *ttl = locset->ns_ttl;
+ locset->ns_current = locset->ns_current->nfl_next;
+ return 0;
+}
+
/*
* Walk through a set of FS locations and build an e_fslocdata string.
* Returns true if all went to plan; otherwise, false.
*/
-static bool locations_to_fslocdata(struct jp_ops *ops,
- nfs_fsloc_set_t locations, char *fslocdata,
- size_t remaining, int *ttl)
+static bool locations_to_fslocdata(struct nfs_fsloc_set *locations,
+ char *fslocdata, size_t remaining, int *ttl)
{
char *server, *last_path, *rootpath, *ptr;
_Bool seen = false;
@@ -1056,13 +1087,13 @@ static bool locations_to_fslocdata(struct jp_ops *ops,
enum jp_status status;
int len;

- status = ops->jp_get_next_location(locations, &server,
+ status = get_next_location(locations, &server,
&rootpath, ttl);
- if (status == JP_EMPTY)
+ if (status == ENOENT)
break;
- if (status != JP_OK) {
+ if (status) {
xlog(D_GENERAL, "%s: failed to parse location: %s",
- __func__, ops->jp_error(status));
+ __func__, strerror(status));
goto out_false;
}
xlog(D_GENERAL, "%s: Location: %s:%s",
@@ -1159,116 +1190,73 @@ out_nomem:
* Walk through the set of FS locations and build an exportent.
* Returns pointer to an exportent if "junction" refers to a junction.
*/
-static struct exportent *locations_to_export(struct jp_ops *ops,
- nfs_fsloc_set_t locations, const char *junction,
- struct exportent *parent)
+static struct exportent *locations_to_export(struct nfs_fsloc_set *locations,
+ const char *junction, struct exportent *parent)
{
static char fslocdata[BUFSIZ];
int ttl;

fslocdata[0] = '\0';
- if (!locations_to_fslocdata(ops, locations,
- fslocdata, sizeof(fslocdata), &ttl))
+ if (!locations_to_fslocdata(locations, fslocdata, sizeof(fslocdata), &ttl))
return NULL;
return create_junction_exportent(parent, junction, fslocdata, ttl);
}

-/*
- * Retrieve locations information in "junction" and dump it to the
- * kernel. Returns pointer to an exportent if "junction" refers
- * to a junction.
- */
-static struct exportent *invoke_junction_ops(void *handle, char *dom,
- const char *junction, struct addrinfo *ai)
+static int
+nfs_get_basic_junction(const char *junct_path, struct nfs_fsloc_set **locset)
{
- struct exportent *parent, *exp = NULL;
- nfs_fsloc_set_t locations;
- enum jp_status status;
- struct jp_ops *ops;
- char *error;
-
- ops = (struct jp_ops *)dlsym(handle, "nfs_junction_ops");
- error = dlerror();
- if (error != NULL) {
- xlog(D_GENERAL, "%s: dlsym(jp_junction_ops): %s",
- __func__, error);
- return NULL;
- }
-#ifdef JP_API_VERSION
- if (ops->jp_api_version != JP_API_VERSION) {
- xlog(D_GENERAL, "%s: unrecognized junction API version: %u",
- __func__, ops->jp_api_version);
- return NULL;
- }
-#endif
- status = ops->jp_init(false);
- if (status != JP_OK) {
- xlog(D_GENERAL, "%s: failed to resolve %s: %s",
- __func__, junction, ops->jp_error(status));
- return NULL;
+ struct nfs_fsloc_set *new;
+ FedFsStatus retval;
+
+ new = calloc(1, sizeof(struct nfs_fsloc_set));
+ if (new == NULL)
+ return ENOMEM;
+
+ retval = nfs_get_locations(junct_path, &new->ns_list);
+ if (retval) {
+ nfs_free_locations(new->ns_list);
+ free(new);
+ return EINVAL;
}

- status = ops->jp_get_locations(junction, &locations);
- switch (status) {
- case JP_OK:
- break;
- case JP_NOTJUNCTION:
+ locset->ns_current = locset->ns_list;
+ new->ns_ttl = 300;
+ *locset = new;
+ return 0;
+}
+
+static struct exportent *lookup_junction(char *dom, const char *pathname,
+ struct addrinfo *ai)
+{
+ struct exportent *parent, *exp = NULL;
+ struct nfs_fsloc_set *locations;
+ int status;
+
+ xmlInitParser();
+
+ if (nfs_is_junction(pathname)) {
xlog(D_GENERAL, "%s: %s is not a junction",
- __func__, junction);
+ __func__, pathname);
goto out;
- default:
+ }
+ status = nfs_get_basic_junction(pathname, &locations);
+ switch (status) {
xlog(L_WARNING, "Dangling junction %s: %s",
- junction, ops->jp_error(status));
+ pathname, strerro(status));
goto out;
}

- parent = lookup_parent_export(dom, junction, ai);
+ parent = lookup_parent_export(dom, pathname, ai);
if (parent == NULL)
goto out;

- exp = locations_to_export(ops, locations, junction, parent);
+ exp = locations_to_export(locations, pathname, parent);

- ops->jp_put_locations(locations);
+ nfs_free_locations(locset->ns_list);
+ free(locset);

out:
- ops->jp_done();
- return exp;
-}
-
-/*
- * Load the junction plug-in, then try to resolve "pathname".
- * Returns pointer to an initialized exportent if "junction"
- * refers to a junction, or NULL if not.
- */
-static struct exportent *lookup_junction(char *dom, const char *pathname,
- struct addrinfo *ai)
-{
- struct exportent *exp;
- struct link_map *map;
- void *handle;
-
-#ifdef JP_NFSPLUGIN_SONAME
- handle = dlopen(JP_NFSPLUGIN_SONAME, RTLD_NOW);
-#else
- handle = dlopen("libnfsjunct.so.0", RTLD_NOW);
-#endif
- if (handle == NULL) {
- xlog(D_GENERAL, "%s: dlopen: %s", __func__, dlerror());
- return NULL;
- }
-
- if (dlinfo(handle, RTLD_DI_LINKMAP, &map) == 0)
- xlog(D_GENERAL, "%s: loaded plug-in %s",
- __func__, map->l_name);
-
- (void)dlerror(); /* Clear any error */
-
- exp = invoke_junction_ops(handle, dom, pathname, ai);
-
- /* We could leave it loaded to make junction resolution
- * faster next time. However, if we want to replace the
- * library, that would require restarting mountd. */
- (void)dlclose(handle);
+ xmlCleanupParser();
return exp;
}

@@ -1284,13 +1272,16 @@ static void lookup_nonexport(int f, char *buf, int buflen, char *dom, char *path
exportent_release(eep);
free(eep);
}
-#else /* !HAVE_NFS_PLUGIN_H */
+
+#else /* !CONFIG_JUNCTION */
+
static void lookup_nonexport(int f, char *buf, int buflen, char *dom, char *path,
struct addrinfo *UNUSED(ai))
{
dump_to_cache(f, buf, buflen, dom, path, NULL, 0);
}
-#endif /* !HAVE_NFS_PLUGIN_H */
+
+#endif /* !CONFIG_JUNCTION */

static void nfsd_export(int f)
{


2018-01-29 23:29:30

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH v4 5/5] Add LDAP-free 'nfsref' command

The 'nfsref' command administers junctions. On an NFS server, it
can create new junctions, remove existing junctions, or display
the contents of junctions.

A man page with more details is included.

Signed-off-by: Chuck Lever <[email protected]>
---
configure.ac | 1
utils/Makefile.am | 4 +
utils/nfsref/Makefile.am | 39 +++++++
utils/nfsref/add.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++
utils/nfsref/lookup.c | 211 ++++++++++++++++++++++++++++++++++++
utils/nfsref/nfsref.c | 188 ++++++++++++++++++++++++++++++++
utils/nfsref/nfsref.h | 47 ++++++++
utils/nfsref/nfsref.man | 180 +++++++++++++++++++++++++++++++
utils/nfsref/remove.c | 145 +++++++++++++++++++++++++
9 files changed, 1086 insertions(+)
create mode 100644 utils/nfsref/Makefile.am
create mode 100644 utils/nfsref/add.c
create mode 100644 utils/nfsref/lookup.c
create mode 100644 utils/nfsref/nfsref.c
create mode 100644 utils/nfsref/nfsref.h
create mode 100644 utils/nfsref/nfsref.man
create mode 100644 utils/nfsref/remove.c

diff --git a/configure.ac b/configure.ac
index d2e48a5..fba9064 100644
--- a/configure.ac
+++ b/configure.ac
@@ -601,6 +601,7 @@ AC_CONFIG_FILES([
utils/mount/Makefile
utils/mountd/Makefile
utils/nfsd/Makefile
+ utils/nfsref/Makefile
utils/nfsstat/Makefile
utils/nfsidmap/Makefile
utils/showmount/Makefile
diff --git a/utils/Makefile.am b/utils/Makefile.am
index c75a5a0..d361aea 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -23,6 +23,10 @@ if CONFIG_NFSDCLTRACK
OPTDIRS += nfsdcltrack
endif

+if CONFIG_JUNCTION
+OPTDIRS += nfsref
+endif
+
SUBDIRS = \
exportfs \
mountd \
diff --git a/utils/nfsref/Makefile.am b/utils/nfsref/Makefile.am
new file mode 100644
index 0000000..2b2bb53
--- /dev/null
+++ b/utils/nfsref/Makefile.am
@@ -0,0 +1,39 @@
+##
+## @file utils/nfsref/Makefile.am
+## @brief Process this file with automake to produce utils/nfsref/Makefile.in
+##
+
+##
+## Copyright 2011, 2018 Oracle. All rights reserved.
+##
+## This file is part of nfs-utils.
+##
+## nfs-utils is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License version 2.0 as
+## published by the Free Software Foundation.
+##
+## nfs-utils is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License version 2.0 for more details.
+##
+## You should have received a copy of the GNU General Public License
+## version 2.0 along with nfs-utils. If not, see:
+##
+## http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+##
+
+noinst_HEADERS = nfsref.h
+
+sbin_PROGRAMS = nfsref
+nfsref_SOURCES = add.c lookup.c nfsref.c remove.c
+LDADD = $(LIBXML2) $(LIBCAP) \
+ ../../support/nfs/libnfs.la \
+ ../../support/junction/libjunction.la
+
+man8_MANS = nfsref.man
+
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = -I. -I../../support/include
+##AM_LDFLAGS = -Wl,--as-needed
diff --git a/utils/nfsref/add.c b/utils/nfsref/add.c
new file mode 100644
index 0000000..d5d0cf8
--- /dev/null
+++ b/utils/nfsref/add.c
@@ -0,0 +1,271 @@
+/**
+ * @file utils/nfsref/add.c
+ * @brief Add junction metadata to a local file system object
+ */
+
+/*
+ * Copyright 2011, 2018 Oracle. All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * nfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with nfs-utils. If not, see:
+ *
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <uuid/uuid.h>
+
+#include "junction.h"
+#include "xlog.h"
+#include "nfsref.h"
+
+/**
+ * Default cache expiration for FSN information
+ */
+#define FSN_DEFAULT_TTL (300)
+
+/**
+ * Display help message for "add" subcommand
+ *
+ * @param progname NUL-terminated C string containing name of program
+ * @return program exit status
+ */
+int
+nfsref_add_help(const char *progname)
+{
+ fprintf(stderr, " \n");
+
+ fprintf(stderr, "Usage: %s [ -t type ] add <junction path> "
+ "<server> <export> [ <server> <export> ... ]\n\n",
+ progname);
+
+ fprintf(stderr, "Add a new junction containing the specified list "
+ "of fileset locations.\n");
+ fprintf(stderr, "<junction path> is the filename of the new junction. "
+ "<server> is the hostname\n");
+ fprintf(stderr, "or IP address of an NFS server where the fileset is "
+ "located. <export> is the\n");
+ fprintf(stderr, "export pathname of the fileset on that server.\n\n");
+
+ fprintf(stderr, "For NFS basic junctions, the location list is stored "
+ "locally in the junction.\n");
+ fprintf(stderr, "For FedFS junctions, the location list is stored "
+ "as new FSN and FSL records\n");
+ fprintf(stderr, "on an NSDB.\n");
+
+ return EXIT_SUCCESS;
+}
+
+/**
+ * Fill in default settings for NFSv4.0 fs_locations4
+ *
+ * @param new NFS location structure to fill in
+ *
+ * See section 5.1.3.2 of the NSDB protocol draft.
+ */
+static void
+nfsref_add_fsloc_defaults(struct nfs_fsloc *new)
+{
+ new->nfl_hostport = 0;
+ new->nfl_flags.nfl_varsub = false;
+ new->nfl_currency = -1;
+ new->nfl_validfor = 0;
+ new->nfl_genflags.nfl_writable = false;
+ new->nfl_genflags.nfl_going = false;
+ new->nfl_genflags.nfl_split = true;
+ new->nfl_transflags.nfl_rdma = true;
+ new->nfl_info.nfl_simul = 0;
+ new->nfl_info.nfl_handle = 0;
+ new->nfl_info.nfl_fileid = 0;
+ new->nfl_info.nfl_writever = 0;
+ new->nfl_info.nfl_change = 0;
+ new->nfl_info.nfl_readdir = 0;
+ new->nfl_info.nfl_readrank = 0;
+ new->nfl_info.nfl_readorder = 0;
+ new->nfl_info.nfl_writerank = 0;
+ new->nfl_info.nfl_writeorder = 0;
+}
+
+/**
+ * Convert a pair of command line arguments to one nfs_fsloc structure
+ *
+ * @param server NUL-terminated C string containing file server hostname
+ * @param rootpath NUL-terminated C string containing POSIX-style export path
+ * @param fsloc OUT: NFS location structure
+ * @return a FedFsStatus code
+ *
+ * If nfsref_add_build_fsloc() returns FEDFS_OK, caller must free the
+ * returned fsloc with nfs_free_location().
+ */
+static FedFsStatus
+nfsref_add_build_fsloc(const char *server, const char *rootpath,
+ struct nfs_fsloc **fsloc)
+{
+ struct nfs_fsloc *new;
+ FedFsStatus retval;
+
+ if (server == NULL || rootpath == NULL)
+ return FEDFS_ERR_INVAL;
+
+ xlog(D_GENERAL, "%s: Building fsloc for %s:%s",
+ __func__, server, rootpath);
+
+ new = nfs_new_location();
+ if (new == NULL) {
+ xlog(D_GENERAL, "%s: No memory", __func__);
+ return FEDFS_ERR_SVRFAULT;
+ }
+
+ new->nfl_hostname = strdup(server);
+ if (new->nfl_hostname == NULL) {
+ nfs_free_location(new);
+ xlog(D_GENERAL, "%s: No memory", __func__);
+ return FEDFS_ERR_SVRFAULT;
+ }
+
+ retval = nsdb_posix_to_path_array(rootpath, &new->nfl_rootpath);
+ if (retval != FEDFS_OK) {
+ nfs_free_location(new);
+ return retval;
+ }
+
+ nfsref_add_fsloc_defaults(new);
+ *fsloc = new;
+ return FEDFS_OK;
+}
+
+/**
+ * Convert array of command line arguments to list of nfs_fsloc structures
+ *
+ * @param argv array of pointers to NUL-terminated C strings contains arguments
+ * @param optind index of "argv" where "add" subcommand arguments start
+ * @param fslocs OUT: list of NFS locations
+ * @return a FedFsStatus code
+ *
+ * If nfsref_add_build_fsloc_list() returns FEDFS_OK, caller must free the
+ * returned list of fslocs with nfs_free_locations().
+ */
+static FedFsStatus
+nfsref_add_build_fsloc_list(char **argv, int optind, struct nfs_fsloc **fslocs)
+{
+ struct nfs_fsloc *fsloc, *result = NULL;
+ FedFsStatus retval;
+ int i;
+
+ for (i = optind + 2; argv[i] != NULL; i += 2) {
+ retval = nfsref_add_build_fsloc(argv[i], argv[i + 1], &fsloc);
+ if (retval != FEDFS_OK) {
+ nfs_free_locations(result);
+ return retval;
+ }
+ if (result == NULL)
+ result = fsloc;
+ else
+ result->nfl_next = fsloc;
+ }
+ if (result == NULL)
+ return FEDFS_ERR_INVAL;
+
+ *fslocs = result;
+ return FEDFS_OK;
+}
+
+/**
+ * Add NFS locations to a junction
+ *
+ * @param junct_path NUL-terminated C string containing pathname of junction
+ * @param argv array of pointers to NUL-terminated C strings contains arguments
+ * @param optind index of "argv" where "add" subcommand arguments start
+ * @return program exit status
+ */
+static int
+nfsref_add_nfs_basic(const char *junct_path, char **argv, int optind)
+{
+ struct nfs_fsloc *fslocs = NULL;
+ FedFsStatus retval;
+
+ xlog(D_GENERAL, "%s: Adding basic junction to %s",
+ __func__, junct_path);
+
+ retval = nfsref_add_build_fsloc_list(argv, optind, &fslocs);
+ switch (retval) {
+ case FEDFS_OK:
+ break;
+ case FEDFS_ERR_INVAL:
+ xlog(L_ERROR, "Missing arguments");
+ return EXIT_FAILURE;
+ case FEDFS_ERR_SVRFAULT:
+ xlog(L_ERROR, "No memory");
+ return EXIT_FAILURE;
+ default:
+ xlog(L_ERROR, "Failed to add NFS location metadata to %s: %s",
+ junct_path, nsdb_display_fedfsstatus(retval));
+ return EXIT_FAILURE;
+ }
+
+ retval = nfs_add_junction(junct_path, fslocs);
+ nfs_free_locations(fslocs);
+ switch (retval) {
+ case FEDFS_OK:
+ break;
+ case FEDFS_ERR_EXIST:
+ xlog(L_ERROR, "%s already contains junction metadata",
+ junct_path);
+ return EXIT_FAILURE;
+ default:
+ xlog(L_ERROR, "Failed to add NFS location metadata to %s: %s",
+ junct_path, nsdb_display_fedfsstatus(retval));
+ return EXIT_FAILURE;
+ }
+
+ printf("Created junction %s\n", junct_path);
+ return EXIT_SUCCESS;
+}
+
+/**
+ * Add locations to a junction
+ *
+ * @param type type of junction to add
+ * @param junct_path NUL-terminated C string containing pathname of junction
+ * @param argv array of pointers to NUL-terminated C strings contains arguments
+ * @param optind index of "argv" where "add" subcommand arguments start
+ * @return program exit status
+ */
+int
+nfsref_add(enum nfsref_type type, const char *junct_path, char **argv, int optind)
+{
+ if (mkdir(junct_path, 0755) == -1)
+ if (errno != EEXIST) {
+ xlog(L_ERROR, "Failed to create junction object: %m");
+ return EXIT_FAILURE;
+ }
+
+ switch (type) {
+ case NFSREF_TYPE_UNSPECIFIED:
+ case NFSREF_TYPE_NFS_BASIC:
+ return nfsref_add_nfs_basic(junct_path, argv, optind);
+ default:
+ xlog(L_ERROR, "Unrecognized junction type");
+ }
+ return EXIT_FAILURE;
+}
diff --git a/utils/nfsref/lookup.c b/utils/nfsref/lookup.c
new file mode 100644
index 0000000..16fca2e
--- /dev/null
+++ b/utils/nfsref/lookup.c
@@ -0,0 +1,211 @@
+/**
+ * @file utils/nfsref/lookup.c
+ * @brief Examine junction metadata from a local file system object
+ */
+
+/*
+ * Copyright 2011, 2018 Oracle. All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * nfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with nfs-utils. If not, see:
+ *
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <rpcsvc/nfs_prot.h>
+
+#include "junction.h"
+#include "xlog.h"
+#include "nfsref.h"
+
+/**
+ * Display help message for "lookup" subcommand
+ *
+ * @param progname NUL-terminated C string containing name of program
+ * @return program exit status
+ */
+int
+nfsref_lookup_help(const char *progname)
+{
+ fprintf(stderr, " \n");
+
+ fprintf(stderr, "Usage: %s [ -t type ] lookup <junction path>\n\n",
+ progname);
+
+ fprintf(stderr, "Display the contents of the junction at "
+ "<junction path>. For NFS basic\n");
+ fprintf(stderr, "junctions, the local contents of the junction "
+ "are displayed. For FedFS\n");
+ fprintf(stderr, "junctions, FSL records are retrieved from the "
+ "NSDB and displayed.\n");
+
+ return EXIT_SUCCESS;
+}
+
+/**
+ * Convert a boolean value into a displayable string constant
+ *
+ * @param value boolean value
+ * @return NUL-terminated static constant C string
+ */
+static const char *
+nfsref_lookup_display_boolean(_Bool value)
+{
+ return value ? "true" : "false";
+}
+
+/**
+ * Display a single NFS location
+ *
+ * @param fsloc pointer to an NFS location structure
+ */
+static void
+nfsref_lookup_display_nfs_location(struct nfs_fsloc *fsloc)
+{
+ char *rootpath;
+
+ if (nsdb_path_array_to_posix(fsloc->nfl_rootpath, &rootpath) == FEDFS_OK) {
+ printf("%s:%s\n", fsloc->nfl_hostname, rootpath);
+ free(rootpath);
+ } else
+ printf("%s: - Invalid root path -\n", fsloc->nfl_hostname);
+ printf("\n");
+
+ printf("\tNFS port:\t%u\n", fsloc->nfl_hostport);
+ printf("\tValid for:\t%d\n", fsloc->nfl_validfor);
+ printf("\tCurrency:\t%d\n", fsloc->nfl_currency);
+ printf("\tFlags:\t\tvarsub(%s)\n",
+ nfsref_lookup_display_boolean(fsloc->nfl_flags.nfl_varsub));
+
+ printf("\tGenFlags:\twritable(%s), going(%s), split(%s)\n",
+ nfsref_lookup_display_boolean(fsloc->nfl_genflags.nfl_writable),
+ nfsref_lookup_display_boolean(fsloc->nfl_genflags.nfl_going),
+ nfsref_lookup_display_boolean(fsloc->nfl_genflags.nfl_split));
+ printf("\tTransFlags:\trdma(%s)\n",
+ nfsref_lookup_display_boolean(fsloc->nfl_transflags.nfl_rdma));
+
+ printf("\tClass:\t\tsimul(%u), handle(%u), fileid(%u)\n",
+ fsloc->nfl_info.nfl_simul,
+ fsloc->nfl_info.nfl_handle,
+ fsloc->nfl_info.nfl_fileid);
+ printf("\tClass:\t\twritever(%u), change(%u), readdir(%u)\n",
+ fsloc->nfl_info.nfl_writever,
+ fsloc->nfl_info.nfl_change,
+ fsloc->nfl_info.nfl_readdir);
+ printf("\tRead:\t\trank(%u), order(%u)\n",
+ fsloc->nfl_info.nfl_readrank, fsloc->nfl_info.nfl_readorder);
+ printf("\tWrite:\t\trank(%u), order(%u)\n",
+ fsloc->nfl_info.nfl_writerank, fsloc->nfl_info.nfl_writeorder);
+
+ printf("\n");
+}
+
+/**
+ * Display a list of NFS locations
+ *
+ * @param fslocs list of NFS locations to display
+ */
+static void
+nfsref_lookup_display_nfs_locations(struct nfs_fsloc *fslocs)
+{
+ struct nfs_fsloc *fsloc;
+
+ for (fsloc = fslocs; fsloc != NULL; fsloc = fsloc->nfl_next)
+ nfsref_lookup_display_nfs_location(fsloc);
+}
+
+/**
+ * List NFS locations in an nfs-basic junction
+ *
+ * @param junct_path NUL-terminated C string containing pathname of junction
+ * @return program exit status
+ */
+static int
+nfsref_lookup_nfs_basic(const char *junct_path)
+{
+ struct nfs_fsloc *fslocs = NULL;
+ FedFsStatus retval;
+
+ xlog(D_GENERAL, "%s: Looking up basic junction in %s",
+ __func__, junct_path);
+
+ retval = nfs_is_junction(junct_path);
+ switch (retval) {
+ case FEDFS_OK:
+ break;
+ case FEDFS_ERR_NOTJUNCT:
+ xlog(L_ERROR, "%s is not an nfs-basic junction", junct_path);
+ return EXIT_FAILURE;
+ default:
+ xlog(L_ERROR, "Failed to access %s: %s",
+ junct_path, nsdb_display_fedfsstatus(retval));
+ return EXIT_FAILURE;
+ }
+
+ retval = nfs_get_locations(junct_path, &fslocs);
+ if (retval != FEDFS_OK) {
+ xlog(L_ERROR, "Failed to access %s: %s",
+ junct_path, nsdb_display_fedfsstatus(retval));
+ return EXIT_FAILURE;
+ }
+
+ nfsref_lookup_display_nfs_locations(fslocs);
+
+ nfs_free_locations(fslocs);
+ return EXIT_SUCCESS;
+}
+
+/**
+ * Resolve either a FedFS or NFS basic junction
+ *
+ * @param junct_path NUL-terminated C string containing pathname of junction
+ * @return program exit status
+ */
+static int
+nfsref_lookup_unspecified(const char *junct_path)
+{
+ FedFsStatus retval;
+
+ retval = nfs_is_junction(junct_path);
+ if (retval == FEDFS_OK)
+ return nfsref_lookup_nfs_basic(junct_path);
+ xlog(L_ERROR, "%s is not a junction", junct_path);
+ return EXIT_FAILURE;
+}
+
+/**
+ * Enumerate metadata of a junction
+ *
+ * @param type type of junction to add
+ * @param junct_path NUL-terminated C string containing pathname of junction
+ * @return program exit status
+ */
+int
+nfsref_lookup(enum nfsref_type type, const char *junct_path)
+{
+ switch (type) {
+ case NFSREF_TYPE_UNSPECIFIED:
+ return nfsref_lookup_unspecified(junct_path);
+ case NFSREF_TYPE_NFS_BASIC:
+ return nfsref_lookup_nfs_basic(junct_path);
+ default:
+ xlog(L_ERROR, "Unrecognized junction type");
+ }
+ return EXIT_FAILURE;
+}
diff --git a/utils/nfsref/nfsref.c b/utils/nfsref/nfsref.c
new file mode 100644
index 0000000..ff7013e
--- /dev/null
+++ b/utils/nfsref/nfsref.c
@@ -0,0 +1,188 @@
+/**
+ * @file utils/nfsref/nfsref.c
+ * @brief Manage NFS referrals
+ */
+
+/*
+ * Copyright 2011, 2018 Oracle. All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * nfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with nfs-utils. If not, see:
+ *
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <sys/types.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <time.h>
+
+#include <locale.h>
+#include <langinfo.h>
+
+#include "junction.h"
+#include "xlog.h"
+#include "nfsref.h"
+
+/**
+ * Short form command line options
+ */
+static const char nfsref_opts[] = "?dt:";
+
+/**
+ * Long form command line options
+ */
+static const struct option nfsref_longopts[] = {
+ { "debug", 0, NULL, 'd', },
+ { "help", 0, NULL, '?', },
+ { "type", 1, NULL, 't', },
+ { NULL, 0, NULL, 0, },
+};
+
+/**
+ * Display program synopsis
+ *
+ * @param progname NUL-terminated C string containing name of program
+ */
+static void
+nfsref_usage(const char *progname)
+{
+ fprintf(stderr, "Usage: %s [ -t type ] SUBCOMMAND [ ARGUMENTS ]\n\n",
+ progname);
+
+ fprintf(stderr, "SUBCOMMAND is one of:\n");
+ fprintf(stderr, "\tadd Add a new junction\n");
+ fprintf(stderr, "\tremove Remove an existing junction\n");
+ fprintf(stderr, "\tlookup Enumerate a junction\n");
+
+ fprintf(stderr, "\nUse \"%s SUBCOMMAND -?\" for details.\n", progname);
+}
+
+/**
+ * Program entry point
+ *
+ * @param argc count of command line arguments
+ * @param argv array of NUL-terminated C strings containing command line arguments
+ * @return program exit status
+ */
+int
+main(int argc, char **argv)
+{
+ char *progname, *subcommand, *junct_path;
+ enum nfsref_type type;
+ int arg, exit_status;
+ _Bool help;
+
+ (void)setlocale(LC_ALL, "");
+ (void)umask(S_IWGRP | S_IWOTH);
+
+ exit_status = EXIT_FAILURE;
+
+ /* Set the basename */
+ if ((progname = strrchr(argv[0], '/')) != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ xlog_stderr(1);
+ xlog_syslog(0);
+ xlog_open(progname);
+
+ if (argc < 2) {
+ nfsref_usage(progname);
+ goto out;
+ }
+
+ help = false;
+ type = NFSREF_TYPE_UNSPECIFIED;
+ while ((arg = getopt_long(argc, argv, nfsref_opts,
+ nfsref_longopts, NULL)) != -1) {
+ switch (arg) {
+ case 'd':
+ xlog_config(D_ALL, 1);
+ break;
+ case 't':
+ if (strcmp(optarg, "nfs-basic") == 0)
+ type = NFSREF_TYPE_NFS_BASIC;
+ else if (strcmp(optarg, "nfs-fedfs") == 0)
+ type = NFSREF_TYPE_NFS_FEDFS;
+ else {
+ xlog(L_ERROR,
+ "Unrecognized junction type: %s",
+ optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case '?':
+ help = true;
+ }
+ }
+
+ if (argc < optind + 1) {
+ nfsref_usage(progname);
+ goto out;
+ }
+
+ if (!help && geteuid() != 0) {
+ xlog(L_ERROR, "Root permission is required");
+ goto out;
+ }
+
+ subcommand = argv[optind];
+ junct_path = argv[optind + 1];
+
+ if (strcasecmp(subcommand, "add") == 0) {
+ if (help) {
+ exit_status = nfsref_add_help(progname);
+ goto out;
+ }
+ if (argc < optind + 3) {
+ xlog(L_ERROR, "Not enough positional parameters");
+ nfsref_usage(progname);
+ goto out;
+ }
+ exit_status = nfsref_add(type, junct_path, argv, optind);
+ if (exit_status == EXIT_SUCCESS)
+ (void)junction_flush_exports_cache();
+ } else if (strcasecmp(subcommand, "remove") == 0) {
+ if (help) {
+ exit_status = nfsref_remove_help(progname);
+ goto out;
+ }
+ exit_status = nfsref_remove(type, junct_path);
+ if (exit_status == EXIT_SUCCESS)
+ (void)junction_flush_exports_cache();
+ } else if (strcasecmp(subcommand, "lookup") == 0) {
+ if (help) {
+ exit_status = nfsref_lookup_help(progname);
+ goto out;
+ }
+ exit_status = nfsref_lookup(type, junct_path);
+ } else {
+ xlog(L_ERROR, "Unrecognized subcommand: %s", subcommand);
+ nfsref_usage(progname);
+ }
+
+out:
+ exit(exit_status);
+}
diff --git a/utils/nfsref/nfsref.h b/utils/nfsref/nfsref.h
new file mode 100644
index 0000000..bf0e70e
--- /dev/null
+++ b/utils/nfsref/nfsref.h
@@ -0,0 +1,47 @@
+/**
+ * @file support/nfsref/nfsref.h
+ * @brief Declarations and definitions for nfsref command line tool
+ */
+
+/*
+ * Copyright 2011, 2018 Oracle. All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * nfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with nfs-utils. If not, see:
+ *
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#ifndef UTILS_NFSREF_H
+#define UTILS_NFSREF_H
+
+/**
+ * Junction types supported by the "nfsref" command
+ */
+enum nfsref_type {
+ NFSREF_TYPE_UNSPECIFIED = 1,
+ NFSREF_TYPE_NFS_BASIC,
+ NFSREF_TYPE_NFS_FEDFS
+};
+
+int nfsref_add(enum nfsref_type type, const char *junct_path, char **argv,
+ int optind);
+int nfsref_remove(enum nfsref_type type, const char *junct_path);
+int nfsref_lookup(enum nfsref_type type, const char *junct_path);
+
+int nfsref_add_help(const char *progname);
+int nfsref_remove_help(const char *progname);
+int nfsref_lookup_help(const char *progname);
+
+#endif /* !UTILS_NFSREF_H */
diff --git a/utils/nfsref/nfsref.man b/utils/nfsref/nfsref.man
new file mode 100644
index 0000000..1261549
--- /dev/null
+++ b/utils/nfsref/nfsref.man
@@ -0,0 +1,180 @@
+.\"@(#)nfsref.8"
+.\"
+.\" @file utils/nfsref/nfsref.man
+.\" @brief man page for nfsref command
+.\"
+
+.\"
+.\" Copyright 2011, 2018 Oracle. All rights reserved.
+.\"
+.\" This file is part of nfs-utils.
+.\"
+.\" nfs-utils is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License version 2.0 as
+.\" published by the Free Software Foundation.
+.\"
+.\" nfs-utils is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License version 2.0 for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" version 2.0 along with nfs-utils. If not, see:
+.\"
+.\" http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+.\"
+.TH NFSREF 8 "9 Jan 2018"
+.SH NAME
+nfsref \- manage NFS referrals
+.SH SYNOPSIS
+.B nfsref
+.RB [ \-?d ]
+.RB [ \-t
+.IB type ]
+.B add
+.I pathname server export
+.RI [ " server"
+.IR export " ... ]"
+.P
+.B nfsref
+.RB [ \-?d ]
+.RB [ \-t
+.IB type ]
+.B remove
+.I pathname
+.P
+.B nfsref
+.RB [ \-?d ]
+.RB [ \-t
+.IB type ]
+.B lookup
+.I pathname
+.SH INTRODUCTION
+NFS version 4 introduces the concept of
+.I file system referrals
+to NFS.
+A file system referral is like a symbolic link on a file server
+to another file system share, possibly on another file server.
+On an NFS client, a referral behaves like an automounted directory.
+The client, under the server's direction, mounts a new NFS export
+automatically when an application first accesses that directory.
+.P
+Referrals are typically used to construct a single file name space
+across multiple file servers.
+Because file servers control the shape of the name space,
+no client configuration is required,
+and all clients see the same referral information.
+.P
+The Linux NFS server supports NFS version 4 referrals.
+Administrators can specify the
+.B refer=
+export option in
+.I /etc/exports
+to configure a list of exports from which the client can choose.
+See
+.BR exports (5)
+for details.
+.P
+.SH DESCRIPTION
+The
+.BR nfsref (8)
+command is a simple way to get started managing junction metadata.
+Other administrative commands provide richer access to junction information.
+.SS Subcommands
+Valid
+.BR nfsref (8)
+subcommands are:
+.IP "\fBadd\fP"
+Adds junction information to the directory named by
+.IR pathname .
+The named directory must already exist,
+and must not already contain junction information.
+Regular directory contents are obscured to NFS clients by this operation.
+.IP
+A list of one or more file server and export path pairs
+is also specified on the command line.
+When creating an NFS basic junction, this list is
+stored in an extended attribute of the directory.
+.IP
+If junction creation is successful, the
+.BR nfsref (8)
+command flushes the kernel's export cache
+to remove previously cached junction information.
+.IP "\fBremove\fP"
+Removes junction information from the directory named by
+.IR pathname .
+The named directory must exist,
+and must contain junction information.
+Regular directory contents are made visible to NFS clients again by this operation.
+.IP
+If junction deletion is successful, the
+.BR nfsref (8)
+command flushes the kernel's export cache
+to remove previously cached junction information.
+.IP "\fBlookup\fP"
+Displays junction information stored in the directory named by
+.IR pathname .
+The named directory must exist,
+and must contain junction information.
+.IP
+When looking up an NFS basic junction, the junction information
+in the directory is listed on
+.IR stdout .
+.SS Command line options
+.IP "\fB\-d, \-\-debug"
+Enables debugging messages during operation.
+.IP "\fB\-t, \-\-type=\fIjunction-type\fP"
+Specifies the junction type for the operation. Valid values for
+.I junction-type
+are
+.B nfs-basic
+or
+.BR nfs-fedfs .
+.IP
+For the
+.B add
+subcommand, the default value if this option is not specified is
+.BR nfs-basic .
+For the
+.B remove
+and
+.B lookup
+subcommands, the
+.B \-\-type
+option is not required. The
+.BR nfsref (8)
+command operates on whatever junction contents are available.
+.SH EXAMPLES
+Suppose you have two file servers,
+.I top.example.net
+and
+.IR home.example.net .
+You want all your clients to mount
+.I top.example.net:/
+and then see the files under
+.I home.example.net:/
+automatically in
+.IR top:/home .
+.P
+On
+.IR top.example.net ,
+you might issue this command as root:
+.RS
+.sp
+# mkdir /home
+.br
+# nfsref --type=nfs-basic add /home home.example.net /
+.br
+Created junction /home.
+.sp
+.RE
+.SH FILES
+.TP
+.I /etc/exports
+NFS server export table
+.SH "SEE ALSO"
+.BR exports (5)
+.sp
+RFC 5661 for a description of NFS version 4 referrals
+.SH "AUTHOR"
+Chuck Lever <[email protected]>
diff --git a/utils/nfsref/remove.c b/utils/nfsref/remove.c
new file mode 100644
index 0000000..1a4e371
--- /dev/null
+++ b/utils/nfsref/remove.c
@@ -0,0 +1,145 @@
+/**
+ * @file utils/nfsref/remove.c
+ * @brief Remove junction metadata from a local file system object
+ */
+
+/*
+ * Copyright 2011, 2018 Oracle. All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * nfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with nfs-utils. If not, see:
+ *
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <unistd.h>
+#include <errno.h>
+
+#include "junction.h"
+#include "xlog.h"
+#include "nfsref.h"
+
+/**
+ * Display help message for "remove" subcommand
+ *
+ * @param progname NUL-terminated C string containing name of program
+ * @return program exit status
+ */
+int
+nfsref_remove_help(const char *progname)
+{
+ fprintf(stderr, " \n");
+
+ fprintf(stderr, "Usage: %s [ -t type ] remove <junction path>\n\n",
+ progname);
+
+ fprintf(stderr, "Remove the junction at <junction path>. For FedFS "
+ "junctions, FSL and FSN\n");
+ fprintf(stderr, "records are removed from the NSDB.\n");
+
+ return EXIT_SUCCESS;
+}
+
+/**
+ * Remove an NFS locations-style junction
+ *
+ * @param junct_path NUL-terminated C string containing pathname of junction
+ * @return program exit status
+ */
+static int
+nfsref_remove_nfs_basic(const char *junct_path)
+{
+ int status = EXIT_FAILURE;
+ FedFsStatus retval;
+
+ xlog(D_GENERAL, "%s: Removing FedFS junction from %s",
+ __func__, junct_path);
+
+ retval = nfs_delete_junction(junct_path);
+ switch (retval) {
+ case FEDFS_OK:
+ printf("Removed nfs-basic junction from %s\n", junct_path);
+ status = EXIT_SUCCESS;
+ break;
+ case FEDFS_ERR_NOTJUNCT:
+ xlog(L_ERROR, "%s is not an nfs-basic junction", junct_path);
+ break;
+ default:
+ xlog(L_ERROR, "Failed to delete %s: %s",
+ junct_path, nsdb_display_fedfsstatus(retval));
+ }
+
+ return status;
+}
+
+/**
+ * Remove any NFS junction information
+ *
+ * @param junct_path NUL-terminated C string containing pathname of junction
+ * @return program exit status
+ */
+static int
+nfsref_remove_unspecified(const char *junct_path)
+{
+ FedFsStatus retval;
+
+ xlog(D_GENERAL, "%s: Removing junction from %s",
+ __func__, junct_path);
+
+ retval = nfs_delete_junction(junct_path);
+ if (retval != FEDFS_OK) {
+ if (retval != FEDFS_ERR_NOTJUNCT)
+ goto out_err;
+ }
+
+ printf("Removed junction from %s\n", junct_path);
+ return EXIT_SUCCESS;
+
+out_err:
+ switch (retval) {
+ case FEDFS_ERR_NOTJUNCT:
+ xlog(L_ERROR, "No junction information found in %s", junct_path);
+ break;
+ default:
+ xlog(L_ERROR, "Failed to delete %s: %s",
+ junct_path, nsdb_display_fedfsstatus(retval));
+ }
+ return EXIT_FAILURE;
+}
+
+/**
+ * Remove an NFS junction
+ *
+ * @param type type of junction to add
+ * @param junct_path NUL-terminated C string containing pathname of junction
+ * @return program exit status
+ */
+int
+nfsref_remove(enum nfsref_type type, const char *junct_path)
+{
+ switch (type) {
+ case NFSREF_TYPE_UNSPECIFIED:
+ return nfsref_remove_unspecified(junct_path);
+ case NFSREF_TYPE_NFS_BASIC:
+ return nfsref_remove_nfs_basic(junct_path);
+ default:
+ xlog(L_ERROR, "Unrecognized junction type");
+ }
+ return EXIT_FAILURE;
+}


2018-02-07 06:30:16

by NeilBrown

[permalink] [raw]
Subject: Re: [PATCH v4 0/5] NFS basic junction support for nfs-utils

On Mon, Jan 29 2018, Chuck Lever wrote:

> Still UNTESTED.
>
> A while back I announced the deprecation of fedfs-utils. There were
> a handful of components in fedfs-utils that we decided to keep. One
> of those keepers was the "nfsref" command. (The other was autofs
> support for /nfs4, which I hope Ian Kent is making progress on ;-)
>
> This series introduces "nfsref" to nfs-utils, minus the overhead of
> the LDAP / FedFS machinery, and it builds support for NFS basic
> junctions into mountd, replacing the need to install a plug-in DLL
> from fedfs-utils.
>
> I didn't apply a lot of brain cells to this port, so it's perhaps a
> little larger than it needs to be. Still, it achieves a completely
> LDAP-free implementation that resides 100% in nfs-utils. I'm
> interested in comments about the approach before I do more testing
> and refinement.

It looks sensible to me, though I must confess that I haven't given much
thought to the use of junctions, and have never tried working with them.

If I understand correctly, you can already do junctions by putting
archane magic in /etc/exports, and possibly performing
mount --bind /foo /foo
With the patch to mountd, you can put the archane magic in an
xattr on the directory, and skip the --bind mount.
Then "nfsref" is a funky tool which adds/lists/removes the archane magic
in a human-readable fashion.

That sounds like a generally sensible idea. There seems to be rather
more code than I would expect for that functionality, but you've already
admitted that, so no surprise there.

I was rather surprised to see an rpcgen generated file without the .x.
I think I understand why that is, and that is one place that you will
probably remove a lot of unnecessary content, including the comment
about rpcgen.

So: +1

Thanks,
NeilBrown


>
> ./configure --enable-junction --enable-caps
>
> is needed before building.
>
>
> Changes since v3:
> Broke up the first patch because it was apparently too large to
> be reflected through vger.kernel.org.
>
>
> Changes since v2:
> None.
>
>
> Changes since RFC (v1):
> Patch 2/3 in the original series added the libnfsjunct.so DLL to
> nfs-utils. Series v2 instead replaces 2/3 with a patch that puts
> support for junctions into mountd without the need for a DLL.
>
> ---
>
> Chuck Lever (5):
> Add headers to support libjunction
> Add infrastructure for libjunction
> Add LDAP-free version of libjunction to nfs-utils
> mountd: Solder in support for NFS basic junctions
> Add LDAP-free 'nfsref' command
>
>
> aclocal/libxml2.m4 | 15
> configure.ac | 17
> support/Makefile.am | 4
> support/include/Makefile.am | 2
> support/include/fedfs_admin.h | 342 +++++++
> support/include/junction.h | 125 +++
> support/junction/Makefile.am | 34 +
> support/junction/display.c | 159 +++
> support/junction/export-cache.c | 118 +++
> support/junction/junction-internal.h | 121 +++
> support/junction/junction.c | 494 +++++++++++
> support/junction/locations.c | 131 +++
> support/junction/nfs.c | 1564 ++++++++++++++++++++++++++++++++++
> support/junction/path.c | 345 ++++++++
> support/junction/xml.c | 401 +++++++++
> utils/Makefile.am | 4
> utils/mountd/Makefile.am | 8
> utils/mountd/cache.c | 189 ++--
> utils/nfsref/Makefile.am | 39 +
> utils/nfsref/add.c | 271 ++++++
> utils/nfsref/lookup.c | 211 +++++
> utils/nfsref/nfsref.c | 188 ++++
> utils/nfsref/nfsref.h | 47 +
> utils/nfsref/nfsref.man | 180 ++++
> utils/nfsref/remove.c | 145 +++
> 25 files changed, 5050 insertions(+), 104 deletions(-)
> create mode 100644 aclocal/libxml2.m4
> create mode 100644 support/include/fedfs_admin.h
> create mode 100644 support/include/junction.h
> create mode 100644 support/junction/Makefile.am
> 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
> create mode 100644 utils/nfsref/Makefile.am
> create mode 100644 utils/nfsref/add.c
> create mode 100644 utils/nfsref/lookup.c
> create mode 100644 utils/nfsref/nfsref.c
> create mode 100644 utils/nfsref/nfsref.h
> create mode 100644 utils/nfsref/nfsref.man
> create mode 100644 utils/nfsref/remove.c
>
> --
> Chuck Lever
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html


Attachments:
signature.asc (832.00 B)

2018-02-07 16:18:31

by Chuck Lever III

[permalink] [raw]
Subject: Re: [PATCH v4 0/5] NFS basic junction support for nfs-utils



> On Feb 7, 2018, at 1:30 AM, NeilBrown <[email protected]> wrote:
>=20
> On Mon, Jan 29 2018, Chuck Lever wrote:
>=20
>> Still UNTESTED.
>>=20
>> A while back I announced the deprecation of fedfs-utils. There were
>> a handful of components in fedfs-utils that we decided to keep. One
>> of those keepers was the "nfsref" command. (The other was autofs
>> support for /nfs4, which I hope Ian Kent is making progress on ;-)
>>=20
>> This series introduces "nfsref" to nfs-utils, minus the overhead of
>> the LDAP / FedFS machinery, and it builds support for NFS basic
>> junctions into mountd, replacing the need to install a plug-in DLL
>> from fedfs-utils.
>>=20
>> I didn't apply a lot of brain cells to this port, so it's perhaps a
>> little larger than it needs to be. Still, it achieves a completely
>> LDAP-free implementation that resides 100% in nfs-utils. I'm
>> interested in comments about the approach before I do more testing
>> and refinement.
>=20
> It looks sensible to me, though I must confess that I haven't given =
much
> thought to the use of junctions, and have never tried working with =
them.
>=20
> If I understand correctly, you can already do junctions by putting
> archane magic in /etc/exports, and possibly performing
> mount --bind /foo /foo
> With the patch to mountd, you can put the archane magic in an
> xattr on the directory, and skip the --bind mount.
> Then "nfsref" is a funky tool which adds/lists/removes the archane =
magic
> in a human-readable fashion.
>=20
> That sounds like a generally sensible idea.

Yes, we would like to make the Linux server's implementation of
NFSv4 referrals a little less "black magic," and a little more
"sane administrative interface."


> There seems to be rather
> more code than I would expect for that functionality, but you've =
already
> admitted that, so no surprise there.

This code had to support at least two types of junctions: NFS
basic, and FedFS. That's why the junction extended attributes
contain XML. Though we're not carrying support for FedFS junctions
forward, parsing that XML appears to be the bulkiest part of
this port.


> I was rather surprised to see an rpcgen generated file without the .x.
> I think I understand why that is, and that is one place that you will
> probably remove a lot of unnecessary content, including the comment
> about rpcgen.

Thinking about it, the fedfs_admin.h file is probably needed only
for its definitions of the FEDFS_ERR status codes, which are used
throughout the internal APIs in libjunction. We're certainly not
including an implementation of the FedFS ADMIN protocol.

As a first step I could copy just those to the other new header
file, and that could allow me to get rid of fedfs_admin.h all
together.


> So: +1

Thanks, Neil!


>=20
> Thanks,
> NeilBrown
>=20
>=20
>>=20
>> ./configure --enable-junction --enable-caps
>>=20
>> is needed before building.
>>=20
>>=20
>> Changes since v3:
>> Broke up the first patch because it was apparently too large to
>> be reflected through vger.kernel.org.
>>=20
>>=20
>> Changes since v2:
>> None.
>>=20
>>=20
>> Changes since RFC (v1):
>> Patch 2/3 in the original series added the libnfsjunct.so DLL to
>> nfs-utils. Series v2 instead replaces 2/3 with a patch that puts
>> support for junctions into mountd without the need for a DLL.
>>=20
>> ---
>>=20
>> Chuck Lever (5):
>> Add headers to support libjunction
>> Add infrastructure for libjunction
>> Add LDAP-free version of libjunction to nfs-utils
>> mountd: Solder in support for NFS basic junctions
>> Add LDAP-free 'nfsref' command
>>=20
>>=20
>> aclocal/libxml2.m4 | 15=20
>> configure.ac | 17=20
>> support/Makefile.am | 4=20
>> support/include/Makefile.am | 2=20
>> support/include/fedfs_admin.h | 342 +++++++
>> support/include/junction.h | 125 +++
>> support/junction/Makefile.am | 34 +
>> support/junction/display.c | 159 +++
>> support/junction/export-cache.c | 118 +++
>> support/junction/junction-internal.h | 121 +++
>> support/junction/junction.c | 494 +++++++++++
>> support/junction/locations.c | 131 +++
>> support/junction/nfs.c | 1564 =
++++++++++++++++++++++++++++++++++
>> support/junction/path.c | 345 ++++++++
>> support/junction/xml.c | 401 +++++++++
>> utils/Makefile.am | 4=20
>> utils/mountd/Makefile.am | 8=20
>> utils/mountd/cache.c | 189 ++--
>> utils/nfsref/Makefile.am | 39 +
>> utils/nfsref/add.c | 271 ++++++
>> utils/nfsref/lookup.c | 211 +++++
>> utils/nfsref/nfsref.c | 188 ++++
>> utils/nfsref/nfsref.h | 47 +
>> utils/nfsref/nfsref.man | 180 ++++
>> utils/nfsref/remove.c | 145 +++
>> 25 files changed, 5050 insertions(+), 104 deletions(-)
>> create mode 100644 aclocal/libxml2.m4
>> create mode 100644 support/include/fedfs_admin.h
>> create mode 100644 support/include/junction.h
>> create mode 100644 support/junction/Makefile.am
>> 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
>> create mode 100644 utils/nfsref/Makefile.am
>> create mode 100644 utils/nfsref/add.c
>> create mode 100644 utils/nfsref/lookup.c
>> create mode 100644 utils/nfsref/nfsref.c
>> create mode 100644 utils/nfsref/nfsref.h
>> create mode 100644 utils/nfsref/nfsref.man
>> create mode 100644 utils/nfsref/remove.c
>>=20
>> --
>> Chuck Lever
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-nfs" =
in
>> the body of a message to [email protected]
>> More majordomo info at http://vger.kernel.org/majordomo-info.html

--
Chuck Lever




2018-02-07 18:50:00

by Steve Dickson

[permalink] [raw]
Subject: Re: [PATCH v4 0/5] NFS basic junction support for nfs-utils



On 01/29/2018 06:28 PM, Chuck Lever wrote:
> Still UNTESTED.
>
> A while back I announced the deprecation of fedfs-utils. There were
> a handful of components in fedfs-utils that we decided to keep. One
> of those keepers was the "nfsref" command. (The other was autofs
> support for /nfs4, which I hope Ian Kent is making progress on ;-)
>
> This series introduces "nfsref" to nfs-utils, minus the overhead of
> the LDAP / FedFS machinery, and it builds support for NFS basic
> junctions into mountd, replacing the need to install a plug-in DLL
> from fedfs-utils.
>
> I didn't apply a lot of brain cells to this port, so it's perhaps a
> little larger than it needs to be. Still, it achieves a completely
> LDAP-free implementation that resides 100% in nfs-utils. I'm
> interested in comments about the approach before I do more testing
> and refinement.
>
> ./configure --enable-junction --enable-caps
>
> is needed before building.
The series was committed... thanks!

steved.

>
>
> Changes since v3:
> Broke up the first patch because it was apparently too large to
> be reflected through vger.kernel.org.
>
>
> Changes since v2:
> None.
>
>
> Changes since RFC (v1):
> Patch 2/3 in the original series added the libnfsjunct.so DLL to
> nfs-utils. Series v2 instead replaces 2/3 with a patch that puts
> support for junctions into mountd without the need for a DLL.
>
> ---
>
> Chuck Lever (5):
> Add headers to support libjunction
> Add infrastructure for libjunction
> Add LDAP-free version of libjunction to nfs-utils
> mountd: Solder in support for NFS basic junctions
> Add LDAP-free 'nfsref' command
>
>
> aclocal/libxml2.m4 | 15
> configure.ac | 17
> support/Makefile.am | 4
> support/include/Makefile.am | 2
> support/include/fedfs_admin.h | 342 +++++++
> support/include/junction.h | 125 +++
> support/junction/Makefile.am | 34 +
> support/junction/display.c | 159 +++
> support/junction/export-cache.c | 118 +++
> support/junction/junction-internal.h | 121 +++
> support/junction/junction.c | 494 +++++++++++
> support/junction/locations.c | 131 +++
> support/junction/nfs.c | 1564 ++++++++++++++++++++++++++++++++++
> support/junction/path.c | 345 ++++++++
> support/junction/xml.c | 401 +++++++++
> utils/Makefile.am | 4
> utils/mountd/Makefile.am | 8
> utils/mountd/cache.c | 189 ++--
> utils/nfsref/Makefile.am | 39 +
> utils/nfsref/add.c | 271 ++++++
> utils/nfsref/lookup.c | 211 +++++
> utils/nfsref/nfsref.c | 188 ++++
> utils/nfsref/nfsref.h | 47 +
> utils/nfsref/nfsref.man | 180 ++++
> utils/nfsref/remove.c | 145 +++
> 25 files changed, 5050 insertions(+), 104 deletions(-)
> create mode 100644 aclocal/libxml2.m4
> create mode 100644 support/include/fedfs_admin.h
> create mode 100644 support/include/junction.h
> create mode 100644 support/junction/Makefile.am
> 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
> create mode 100644 utils/nfsref/Makefile.am
> create mode 100644 utils/nfsref/add.c
> create mode 100644 utils/nfsref/lookup.c
> create mode 100644 utils/nfsref/nfsref.c
> create mode 100644 utils/nfsref/nfsref.h
> create mode 100644 utils/nfsref/nfsref.man
> create mode 100644 utils/nfsref/remove.c
>
> --
> Chuck Lever
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>