diff mbox

[2/3] plugin: Introduce the junction plug-in API

Message ID 20120112163313.27284.98032.stgit@degas.1015granger.net
State Accepted
Headers show

Commit Message

Chuck Lever Jan. 12, 2012, 4:33 p.m. UTC
Define an external API/ABI to allow applications to access NFS
location data stored in junctions.  This interface is specific to NFS
to continue to provide strong data and function type checking.  Other
file system protocols will need to define their own API.

A junction plug-in implementation is also provided.

Note that the interface currently returns a flat POSIX pathname rather
than an array of components.  The only consumer of this interface so
far is mountd, who is not currently capable of passing an array of
components to the kernel.  To keep things simple for now, I'm going to
handle pathname mangling inside the plug-in rather than in mountd.

At some later point, a second "get location" interface can be provided
that gives access to all of the fields in each location record, to
support fs_locations_info4.  At that time support for component arrays
can be introduced.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 INSTALL                   |   23 ++
 configure.ac              |    1 
 src/Makefile.am           |    2 
 src/include/Makefile.am   |    1 
 src/include/nfs-plugin.h  |   96 ++++++++
 src/plug-ins/Makefile.am  |   41 +++
 src/plug-ins/nfs-plugin.c |  567 +++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 728 insertions(+), 3 deletions(-)
 create mode 100644 src/include/nfs-plugin.h
 create mode 100644 src/plug-ins/Makefile.am
 create mode 100644 src/plug-ins/nfs-plugin.c
diff mbox

Patch

diff --git a/INSTALL b/INSTALL
index 186678d..a2ef958 100644
--- a/INSTALL
+++ b/INSTALL
@@ -121,9 +121,9 @@  FedFS file server
   o Install kernel with patches to do export cache upcalls when
     encountering a junction
 
-  o Install resolve-junction program
+  o Install junction resolution plug-in
 
-  o Install mountd with patches to resolve junctions
+  o Install mountd with patches to invoke the plug-in
 
   o Install the nfsref program
 
@@ -177,3 +177,22 @@  FedFS admin client
 In addition to these tasks, you should choose one of the FedFS-enabled
 file servers on which to host the domain root directory, and set up a
 DNS SRV record to allow FedFS file system clients to find it.
+
+
+Configuring the junction resolution plug-in
+
+By default, the junction resolution plug-in library is installed in
+/usr/local/lib.  Mountd's junction plug-in support searches the default
+library path for this plug-in, but mountd's library path does not
+normally contain /usr/local/lib.
+
+There are several options:
+
+  o Specify an LD_LIBRARY_PATH when starting mountd that includes
+    /usr/local/lib,
+
+  o Move the installed plug-in files to a directory that is included
+    in mountd's existing library path, or
+
+  o Use the --libdir= command line option on fedfs-utils's ./configure
+    to change the installed location of the plug-in library.
diff --git a/configure.ac b/configure.ac
index bbe3f19..c27ba78 100644
--- a/configure.ac
+++ b/configure.ac
@@ -173,5 +173,6 @@  AC_CONFIG_FILES([Makefile
                  src/nfsref/Makefile
                  src/nsdbc/Makefile
                  src/nsdbparams/Makefile
+                 src/plug-ins/Makefile
                  src/resolve-junction/Makefile])
 AC_OUTPUT
diff --git a/src/Makefile.am b/src/Makefile.am
index fcb84e6..777b618 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -26,7 +26,7 @@ 
 SUBDIRS			= include libxlog libadmin libnsdb libjunction \
 			  libparser libsi \
 			  fedfsc fedfsd mount nfsref nsdbc nsdbparams \
-			  resolve-junction
+			  plug-ins resolve-junction
 
 CLEANFILES		= cscope.in.out cscope.out cscope.po.out *~
 DISTCLEANFILES		= Makefile.in
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
index 5f779b3..bda3d3c 100644
--- a/src/include/Makefile.am
+++ b/src/include/Makefile.am
@@ -23,6 +23,7 @@ 
 ##	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 ##
 
+include_HEADERS		= nfs-plugin.h
 noinst_HEADERS		= fedfs_admin.h fedfs.h getsrvinfo.h gpl-boiler.h \
 			  junction.h list.h nsdb.h parse_opt.h \
 			  token.h xlog.h
diff --git a/src/include/nfs-plugin.h b/src/include/nfs-plugin.h
new file mode 100644
index 0000000..60d2993
--- /dev/null
+++ b/src/include/nfs-plugin.h
@@ -0,0 +1,96 @@ 
+/*
+ * @file src/include/nfs-plugin.h
+ * @brief Definition of NFS junction plug-in API
+ */
+
+/*
+ * Copyright 2011 Oracle.  All rights reserved.
+ *
+ * This file is part of fedfs-utils.
+ *
+ * fedfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * fedfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with fedfs-utils.  If not, see:
+ *
+ *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+/*
+ * The purpose of this API is to provide an opaque mechanism for
+ * the NFS mountd daemon to resolve NFS basic and FedFS junctions.
+ * This interface is therefore quite specific to NFS.
+ */
+
+#ifndef FEDFS_NFS_PLUGIN_H
+#define FEDFS_NFS_PLUGIN_H
+
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+/**
+ * Current version of API
+ */
+#define JP_API_VERSION		(1)
+
+/**
+ * A set of NFS FS locations
+ */
+struct nfs_fsloc_set;
+typedef struct nfs_fsloc_set	 *nfs_fsloc_set_t;
+
+/**
+ * Junction operation status codes
+ */
+enum jp_status {
+	JP_OK			=  0,
+	JP_INVAL		= -1,
+	JP_ACCESS		= -2,
+	JP_EXIST		= -3,
+	JP_TYPE_NOT_SUPP	= -4,
+	JP_OP_NOT_SUPP		= -5,
+	JP_ISJUNCTION		= -6,
+	JP_NOTJUNCTION		= -7,
+	JP_NSDBLOCAL		= -8,
+	JP_NSDBREMOTE		= -9,
+	JP_MEMORY		= -10,
+	JP_SYSTEM		= -11,
+	JP_PARSE		= -1000,
+	JP_EMPTY		= -1001,
+};
+
+/**
+ * Vector of methods provided by a junction plug-in
+ */
+struct jp_ops {
+	unsigned int	  jp_api_version;
+
+	enum jp_status	  (*jp_init)(_Bool want_debugging);
+	void		  (*jp_done)(void);
+
+	const char *	  (*jp_error)(enum jp_status status);
+	void		  (*jp_put_locations)(nfs_fsloc_set_t locset);
+	enum jp_status	  (*jp_get_locations)(const char *junct_path,
+					nfs_fsloc_set_t *locset);
+	void		  (*jp_rewind_locations)(nfs_fsloc_set_t locset);
+	enum jp_status	  (*jp_get_next_location)(nfs_fsloc_set_t locset,
+					char **hostname, char **export_path,
+					int *ttl);
+};
+
+/**
+ * Load this symbol to get access to the junction API
+ */
+extern struct jp_ops	  nfs_junction_ops;
+
+__END_DECLS
+
+#endif	/* !FEDFS_NFS_PLUGIN_H */
diff --git a/src/plug-ins/Makefile.am b/src/plug-ins/Makefile.am
new file mode 100644
index 0000000..1427708
--- /dev/null
+++ b/src/plug-ins/Makefile.am
@@ -0,0 +1,41 @@ 
+##
+## @file src/plug-ins/Makefile.am
+## @brief Process this file with automake to produce src/plug-ins/Makefile.in
+##
+
+##
+## Copyright 2011 Oracle.  All rights reserved.
+##
+## This file is part of fedfs-utils.
+##
+## fedfs-utils is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License version 2.0 as
+## published by the Free Software Foundation.
+##
+## fedfs-utils is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License version 2.0 for more details.
+##
+## You should have received a copy of the GNU General Public License
+## version 2.0 along with fedfs-utils.  If not, see:
+##
+##	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+##
+
+lib_LTLIBRARIES		= libnfsjunct.la
+libnfsjunct_la_SOURCES	= nfs-plugin.c
+libnfsjunct_la_LIBADD	= $(LIBLDAP) $(LIBLBER) $(LIBXML2) \
+			  $(LIBSQLITE3) $(LIBIDN) $(LIBUUID) \
+			  $(top_builddir)/src/libnsdb/libnsdb.la \
+			  $(top_builddir)/src/libxlog/libxlog.la \
+			  $(top_builddir)/src/libjunction/libjunction.la
+
+CLEANFILES		= cscope.in.out cscope.out cscope.po.out *~
+DISTCLEANFILES		= Makefile.in
+
+AM_CFLAGS		= -ggdb -fstrict-aliasing -fPIC \
+			  -Wall -Wextra -pedantic -Wformat=2 \
+			  -Wstrict-aliasing=2 -Wp,-D_FORTIFY_SOURCE=2
+AM_CPPFLAGS		= -I. -I$(top_srcdir)/src/include \
+			  -I/usr/include/libxml2
diff --git a/src/plug-ins/nfs-plugin.c b/src/plug-ins/nfs-plugin.c
new file mode 100644
index 0000000..275caf1
--- /dev/null
+++ b/src/plug-ins/nfs-plugin.c
@@ -0,0 +1,567 @@ 
+/**
+ * @file src/plug-ins/nfs-plugin.c
+ * @brief DLL to resolve junction information
+ */
+
+/*
+ * Copyright 2011 Oracle.  All rights reserved.
+ *
+ * This file is part of fedfs-utils.
+ *
+ * fedfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * fedfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with fedfs-utils.  If not, see:
+ *
+ *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <libxml/parser.h>
+
+#include "fedfs_admin.h"
+#include "nfs-plugin.h"
+#include "junction.h"
+
+struct nfs_fsloc_set {
+	struct nfs_fsloc	 *ns_current;
+	struct nfs_fsloc	 *ns_list;
+};
+
+static _Bool debug = false;
+
+/**
+ * Write a debugging message to stderr
+ *
+ * @param fmt NUL-terminated C string containing output format specification
+ *
+ * NB:	Caller may have already opened syslog for her own use.  We can't
+ *	hijack it here, so using xlog() is right out.  Thus output is
+ *	directed to stderr via fprintf(3).
+ */
+static void
+nfs_jp_debug(const char *fmt, ...)
+{
+	va_list args;
+
+	if (!debug)
+		return;
+
+	va_start(args, fmt);
+	vfprintf(stderr, fmt, args);
+	va_end(args);
+}
+
+/**
+ * Perform any plug-in startup processing
+ *
+ * @param want_debugging true if caller wants to enable debugging output
+ * @return a junction status code
+ */
+static enum jp_status
+nfs_jp_init(_Bool want_debugging)
+{
+	debug = want_debugging;
+	nfs_jp_debug("%s: Junction plug-in version " VERSION "\n", __func__);
+	xmlInitParser();
+	return JP_OK;
+}
+
+/**
+ * Perform any plug-in shutdown processing
+ *
+ * Nothing to be done for NFS junctions.
+ */
+static void
+nfs_jp_done(void)
+{
+	nfs_jp_debug("%s: Finishing\n", __func__);
+	xmlCleanupParser();
+	return;
+}
+
+/**
+ * Given an status code, return a pointer to a static error message
+ *
+ * @param status a junction plug-in status code
+ * @return a static NUL-terminated string
+ */
+static const char *
+nfs_jp_error(enum jp_status status)
+{
+	static char buf[128];
+
+	switch (status) {
+	case JP_OK:
+		return "Success";
+	case JP_INVAL:
+		return "Invalid parameter";
+	case JP_ACCESS:
+		return "Permission denied";
+	case JP_EXIST:
+		return "Object cannot be made into a junction";
+	case JP_TYPE_NOT_SUPP:
+		return "Junction type not supported";
+	case JP_OP_NOT_SUPP:
+		return "Junction method not supported";
+	case JP_ISJUNCTION:
+		return "Object is a junction";
+	case JP_NOTJUNCTION:
+		return "Object is not a junction";
+	case JP_NSDBLOCAL:
+		return "A local NSDB configuration error occurred";
+	case JP_NSDBREMOTE:
+		return "An error occurred on the NSDB";
+	case JP_MEMORY:
+		return "Memory allocation failure";
+	case JP_SYSTEM:
+		snprintf(buf, sizeof(buf), "System error (%d): %s",
+				status, strerror(errno));
+		return buf;
+	case JP_PARSE:
+		return "Failed to parse locations data";
+	case JP_EMPTY:
+		return "No more locations in location set";
+	}
+
+	snprintf(buf, sizeof(buf), "Unknown error (%d)", status);
+	return buf;
+}
+
+/**
+ * Release a set of NFS locations
+ *
+ * @param locset set of NFS locations to release
+ */
+static void
+nfs_jp_put_locations(nfs_fsloc_set_t locset)
+{
+	if (locset == NULL) {
+		nfs_jp_debug("%s: Invalid parameters\n", __func__);
+		return;
+	}
+
+	nfs_jp_debug("%s: Freeing location set %p, ns_list=%p\n",
+		__func__, locset, locset->ns_list);
+	nfs_free_locations(locset->ns_list);
+	free(locset);
+}
+
+/**
+ * Internal function to allocate a set of NFS locations
+ *
+ * @return dynamically allocated nfs_fsloc_set_t object
+ *
+ * If return value is non-NULL, caller must free it with
+ * nfs_jp_put_locations().
+ */
+__attribute_malloc__
+static nfs_fsloc_set_t
+nfs_jp_alloc_locations(void)
+{
+	return calloc(1, sizeof(struct nfs_fsloc_set));
+}
+
+/**
+ * Internal function to rewind a set of locations
+ *
+ * @param locset set of NFS locations to rewind
+ */
+static void
+nfs_jp_do_rewind_locations(nfs_fsloc_set_t locset)
+{
+	locset->ns_current = locset->ns_list;
+}
+
+/**
+ * Resolve NFS basic junction information into a set of NFS locations
+ *
+ * @param junct_path NUL-terminated C string containing POSIX path of junction
+ * @param locset OUT set of NFS locations
+ * @return a junction status code
+ *
+ * If this entry point returns JP_OK, the caller must free the returned
+ * set of locations by calling the jp_put_locations entry point.
+ */
+static enum jp_status
+nfs_jp_get_basic(const char *junct_path, nfs_fsloc_set_t *locset)
+{
+	nfs_fsloc_set_t new;
+	FedFsStatus retval;
+
+	new = nfs_jp_alloc_locations();
+	if (new == NULL) {
+		nfs_jp_debug("%s: No memory\n", __func__);
+		return JP_MEMORY;
+	}
+
+	retval = nfs_get_locations(junct_path, &new->ns_list);
+	if (retval != FEDFS_OK) {
+		nfs_jp_debug("%s: Failed to get locations: %s\n",
+			__func__, nsdb_display_fedfsstatus(retval));
+		nfs_jp_put_locations(new);
+		return JP_PARSE;
+	}
+
+	nfs_jp_debug("%s: Returning location set %p\n", __func__, new);
+	nfs_jp_do_rewind_locations(new);
+	*locset = new;
+	return JP_OK;
+}
+
+/**
+ * Convert one FedFS fileset location into one NFS location
+ *
+ * @param fsl a FedFS fileset location
+ * @param fsloc OUT an NFS location
+ * @return a junction status code
+ *
+ * If nfs_jp_convert_fedfs_fsl() returns JP_OK, the caller must free the
+ * returned location by calling nfs_free_location().
+ */
+static enum jp_status
+nfs_jp_convert_fedfs_fsl(struct fedfs_fsl *fsl, struct nfs_fsloc **fsloc)
+{
+	struct fedfs_nfs_fsl *nfs_fsl = &fsl->fl_u.fl_nfsfsl;
+	struct nfs_fsloc *new;
+
+	new = nfs_new_location();
+	if (new == NULL) {
+		nfs_jp_debug("%s: No memory\n", __func__);
+		return JP_MEMORY;
+	}
+
+	new->nfl_hostname = strdup(fsl->fl_fslhost);
+	new->nfl_rootpath = nfs_dup_string_array(nfs_fsl->fn_nfspath);
+	if (new->nfl_hostname == NULL || new->nfl_rootpath == NULL) {
+		nfs_free_location(new);
+		nfs_jp_debug("%s: No memory\n", __func__);
+		return JP_MEMORY;
+	}
+
+	new->nfl_hostport = fsl->fl_fslport;
+	new->nfl_ttl = fsl->fl_fslttl;
+
+	new->nfl_flags.nfl_varsub = nfs_fsl->fn_varsub;
+	new->nfl_currency = nfs_fsl->fn_currency;
+	new->nfl_validfor = nfs_fsl->fn_validfor;
+	new->nfl_genflags.nfl_writable = nfs_fsl->fn_gen_writable;
+	new->nfl_genflags.nfl_going = nfs_fsl->fn_gen_going;
+	new->nfl_genflags.nfl_split = nfs_fsl->fn_gen_split;
+	new->nfl_transflags.nfl_rdma = nfs_fsl->fn_trans_rdma;
+	new->nfl_info.nfl_simul = nfs_fsl->fn_class_simul;
+	new->nfl_info.nfl_handle = nfs_fsl->fn_class_handle;
+	new->nfl_info.nfl_fileid = nfs_fsl->fn_class_fileid;
+	new->nfl_info.nfl_writever = nfs_fsl->fn_class_writever;
+	new->nfl_info.nfl_change = nfs_fsl->fn_class_change;
+	new->nfl_info.nfl_readdir = nfs_fsl->fn_class_readdir;
+	new->nfl_info.nfl_readrank = nfs_fsl->fn_readrank;
+	new->nfl_info.nfl_writerank = nfs_fsl->fn_writerank;
+	new->nfl_info.nfl_readorder = nfs_fsl->fn_readorder;
+	new->nfl_info.nfl_writeorder = nfs_fsl->fn_writeorder;
+	new->nfl_majorver = nfs_fsl->fn_majorver;
+	new->nfl_minorver = nfs_fsl->fn_minorver;
+
+	*fsloc = new;
+	return JP_OK;
+}
+
+/**
+ * Convert FedFS fileset locations into a set of NFS locations
+ *
+ * @param fsls a list of FedFS fileset locations
+ * @param new empty set of NFS locations to fill in
+ * @return a junction status code
+ *
+ * If nfs_jp_convert_fedfs_fsls() returns JP_OK, the caller must free the returned
+ * set of locations by calling nfs_jp_put_locations().
+ */
+static enum jp_status
+nfs_jp_convert_fedfs_fsls(struct fedfs_fsl *fsls, nfs_fsloc_set_t new)
+{
+	struct fedfs_fsl *fsl;
+
+	if (fsls == NULL) {
+		nfs_jp_debug("%s: No locations\n", __func__);
+		return JP_EMPTY;
+	}
+
+	for (fsl = fsls; fsl != NULL; fsl = fsl->fl_next) {
+		struct nfs_fsloc *fsloc;
+		enum jp_status status;
+
+		status = nfs_jp_convert_fedfs_fsl(fsl, &fsloc);
+		if (status != JP_OK) {
+			nfs_jp_put_locations(new);
+			return status;
+		}
+
+		if (new->ns_list == NULL)
+			new->ns_list = fsloc;
+		else {
+			fsloc->nfl_next = new->ns_list;
+			new->ns_list = fsloc;
+		}
+	}
+
+	nfs_jp_do_rewind_locations(new);
+	return JP_OK;
+}
+
+/**
+ * Resolve a FedFS fileset name into a set of NFS locations
+ *
+ * @param fsn_uuid NUL-terminated C string containing FSN UUID to resolve
+ * @param host an initialized NSDB host object
+ * @param new empty set of NFS locations
+ * @return a junction status code
+ *
+ * If nfs_jp_resolve_fsn() returns JP_OK, the caller must free the returned
+ * set of locations by calling nfs_jp_put_locations().
+ */
+static enum jp_status
+nfs_jp_resolve_fsn(const char *fsn_uuid, nsdb_t host,
+		nfs_fsloc_set_t new)
+{
+	enum jp_status status = JP_NSDBREMOTE;
+	struct fedfs_fsl *fsls;
+	unsigned int ldap_err;
+	FedFsStatus retval;
+
+	if (nsdb_open_nsdb(host, NULL, NULL, &ldap_err) != FEDFS_OK)
+		return JP_NSDBLOCAL;
+
+	retval = nsdb_resolve_fsn_s(host, NULL, fsn_uuid, &fsls, &ldap_err);
+	switch (retval) {
+	case FEDFS_OK:
+		status = nfs_jp_convert_fedfs_fsls(fsls, new);
+		nfs_jp_debug("%s: Returning %p, ns_list=%p\n",
+			__func__, new, new->ns_list);
+		nsdb_free_fedfs_fsls(fsls);
+		break;
+	case FEDFS_ERR_NSDB_NOFSL:
+		nfs_jp_debug("%s: No FSL entries for FSN %s\n",
+			__func__, fsn_uuid);
+		break;
+	case FEDFS_ERR_NSDB_NOFSN:
+		nfs_jp_debug("%s: No FSN %s found\n",
+			__func__, fsn_uuid);
+		break;
+	case FEDFS_ERR_NSDB_LDAP_VAL:
+		nfs_jp_debug("%s: NSDB operation failed with %s\n",
+			__func__, ldap_err2string(ldap_err));
+		break;
+	default:
+		nfs_jp_debug("%s: Failed to resolve FSN %s: %s\n",
+			__func__, fsn_uuid, nsdb_display_fedfsstatus(status));
+	}
+
+	nsdb_close_nsdb(host);
+	return status;
+}
+
+/**
+ * Resolve FedFS junction information into a set of NFS locations
+ *
+ * @param junct_path NUL-terminated C string containing POSIX path of junction
+ * @param new empty set of NFS locations
+ * @return a junction status code
+ *
+ * If nfs_jp_resolve_fedfs_junction() returns JP_OK, the caller must free
+ * the returned set of locations by calling nfs_jp_put_locations().
+ */
+static enum jp_status
+nfs_jp_resolve_fedfs_junction(const char *junct_path, nfs_fsloc_set_t new)
+{
+	enum jp_status status;
+	FedFsStatus retval;
+	char *fsn_uuid;
+	nsdb_t host;
+
+	retval = fedfs_get_fsn(junct_path, &fsn_uuid, &host);
+	/* XXX: Needs expansion */
+	if (retval != FEDFS_OK) {
+		nfs_jp_debug("%s: Failed to get FSN: %s\n",
+			__func__, nsdb_display_fedfsstatus(retval));
+		return JP_NSDBREMOTE;
+	}
+
+	status = nfs_jp_resolve_fsn(fsn_uuid, host, new);
+
+	nsdb_free_nsdb(host);
+	free(fsn_uuid);
+	return status;
+}
+
+/**
+ * Resolve FedFS junction into a set of NFS locations
+ *
+ * @param junct_path NUL-terminated C string containing POSIX path of junction
+ * @param locset OUT set of NFS locations
+ * @return a junction status code
+ *
+ * If nfs_jp_get_fedfs() returns JP_OK, the caller must free the returned
+ * set of locations by calling nfs_jp_put_locations().
+ */
+static enum jp_status
+nfs_jp_get_fedfs(const char *junct_path, nfs_fsloc_set_t *locset)
+{
+	enum jp_status status;
+	nfs_fsloc_set_t new;
+
+	new = nfs_jp_alloc_locations();
+	if (new == NULL) {
+		nfs_jp_debug("%s: No memory\n", __func__);
+		return JP_MEMORY;
+	}
+
+	status = nfs_jp_resolve_fedfs_junction(junct_path, new);
+	if (status != JP_OK) {
+		nfs_jp_put_locations(new);
+		return status;
+	}
+
+	*locset = new;
+	nfs_jp_debug("%s: Returning location set %p\n", __func__, new);
+	return JP_OK;
+}
+
+/**
+ * Resolve junction information into a set of NFS locations
+ *
+ * @param junct_path NUL-terminated C string containing POSIX path of junction
+ * @param locset OUT set of NFS locations
+ * @return a junction status code
+ *
+ * If this entry point returns JP_OK, the caller must free the returned
+ * set of locations by calling the jp_put_locations entry point.
+ */
+static enum jp_status
+nfs_jp_get_locations(const char *junct_path, nfs_fsloc_set_t *locset)
+{
+	FedFsStatus retval;
+
+	if (junct_path == NULL || locset == NULL) {
+		nfs_jp_debug("%s: Invalid parameters\n", __func__);
+		return JP_INVAL;
+	}
+	nfs_jp_debug("%s: %s\n", __func__, junct_path);
+
+	retval = nfs_is_junction(junct_path);
+	if (retval == FEDFS_OK)
+		return nfs_jp_get_basic(junct_path, locset);
+	if (retval != FEDFS_ERR_NOTJUNCT)
+		return JP_NOTJUNCTION;
+	retval = fedfs_is_junction(junct_path);
+	if (retval == FEDFS_OK)
+		return nfs_jp_get_fedfs(junct_path, locset);
+
+	nfs_jp_debug("%s: Not a junction\n", __func__);
+	return JP_NOTJUNCTION;
+}
+
+/**
+ * Reset the current location to the first location in the list
+ *
+ * @param locset set of NFS locations
+ */
+static void
+nfs_jp_rewind_locations(nfs_fsloc_set_t locset)
+{
+	if (locset == NULL) {
+		nfs_jp_debug("%s: Invalid parameters\n", __func__);
+		return;
+	}
+
+	nfs_jp_debug("%s: Rewinding %p\n", __func__, locset);
+	nfs_jp_do_rewind_locations(locset);
+}
+
+/**
+ * Get the fileserver hostname and export path from the next location in the set
+ *
+ * @param locset set of NFS locations
+ * @param hostname OUT NUL-terminated C string containing hostname of fileserver
+ * @param export_path OUT NUL-terminated C string containing export path
+ * @param ttl OUT cache time-to-live, in seconds
+ * @return a junction status code
+ *
+ * If this entry point returns JP_OK, the caller must free the hostname
+ * and export_path strings with free(3).
+ */
+static enum jp_status
+nfs_jp_get_next_location(nfs_fsloc_set_t locset,
+		char **hostname, char **export_path, int *ttl)
+{
+	char *hostname_tmp, *export_path_tmp;
+	struct nfs_fsloc *fsloc;
+
+	if (locset == NULL || hostname == NULL ||
+	    export_path == NULL || ttl == NULL) {
+		nfs_jp_debug("%s: Invalid parameters\n", __func__);
+		return JP_INVAL;
+	}
+	nfs_jp_debug("%s: locset=%p, ns_current=%p, ns_list=%p\n",
+		__func__, locset, locset->ns_current, locset->ns_list);
+
+	if (locset->ns_current == NULL) {
+		nfs_jp_debug("%s: No locations\n", __func__);
+		return JP_EMPTY;
+	}
+	fsloc = locset->ns_current;
+
+	hostname_tmp = strdup(fsloc->nfl_hostname);
+	if (hostname_tmp == NULL) {
+		nfs_jp_debug("%s: No memory\n", __func__);
+		return JP_MEMORY;
+	}
+
+	if (nsdb_path_array_to_posix(fsloc->nfl_rootpath,
+					&export_path_tmp) != FEDFS_OK) {
+		free(hostname_tmp);
+		nfs_jp_debug("%s: Failed to parse\n", __func__);
+		return JP_PARSE;
+	}
+
+	nfs_jp_debug("%s: Success; hostname=%s path=%s\n",
+		__func__, hostname_tmp, export_path_tmp);
+	*hostname = hostname_tmp;
+	*export_path = export_path_tmp;
+	*ttl = fsloc->nfl_ttl;
+	locset->ns_current = locset->ns_current->nfl_next;
+	return JP_OK;
+}
+
+/**
+ * Vector of methods provided by this plug-in
+ */
+struct jp_ops nfs_junction_ops = {
+	.jp_api_version		= JP_API_VERSION,
+	.jp_init		= nfs_jp_init,
+	.jp_done		= nfs_jp_done,
+	.jp_error		= nfs_jp_error,
+	.jp_put_locations	= nfs_jp_put_locations,
+	.jp_get_locations	= nfs_jp_get_locations,
+	.jp_rewind_locations	= nfs_jp_rewind_locations,
+	.jp_get_next_location	= nfs_jp_get_next_location,
+};