diff --git a/configure.ac b/configure.ac
index 5d1eca0..a5afb12 100644
--- a/configure.ac
+++ b/configure.ac
@@ -118,6 +118,11 @@ AC_CHECK_LIB([resolv], [__res_querydomain],
 		 AC_DEFINE([HAVE_LIBRESOLV], [1],
 			   [Define if you have libresolv])],
 		[AC_MSG_ERROR([libresolv not found.])])
+AC_CHECK_LIB([xml2], [xmlParseFile],
+		[AC_SUBST([LIBXML2], ["-lxml2"])
+		 AC_DEFINE([HAVE_LIBXML2], [1],
+			   [Define if you have libxml2])],
+		[AC_MSG_ERROR([libxml2 not found.])])
 
 # Checks for header files.
 AC_CHECK_HEADERS([fcntl.h langinfo.h locale.h memory.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h syslog.h termios.h unistd.h wchar.h])
diff --git a/src/fedfsc/Makefile.am b/src/fedfsc/Makefile.am
index 2659e35..87189e2 100644
--- a/src/fedfsc/Makefile.am
+++ b/src/fedfsc/Makefile.am
@@ -30,7 +30,7 @@ sbin_PROGRAMS		= fedfs-null \
 			  fedfs-lookup-junction \
 			  fedfs-create-replication fedfs-delete-replication \
 			  fedfs-lookup-replication
-LDADD			= $(LIBTIRPC) $(LIBLDAP) $(LIBLBER) \
+LDADD			= $(LIBTIRPC) $(LIBLDAP) $(LIBLBER) $(LIBXML2) \
 			  $(LIBSQLITE3) $(LIBIDN) $(LIBUUID) \
 			  $(top_builddir)/src/libadmin/libadmin.la \
 			  $(top_builddir)/src/libjunction/libjunction.la \
diff --git a/src/fedfsd/Makefile.am b/src/fedfsd/Makefile.am
index 37fd42f..47ee106 100644
--- a/src/fedfsd/Makefile.am
+++ b/src/fedfsd/Makefile.am
@@ -27,7 +27,7 @@ noinst_HEADERS		= fedfsd.h
 RPCPREFIX		= rpc.
 sbin_PROGRAMS		= fedfsd
 fedfsd_SOURCES		= listen.c main.c privilege.c svc.c
-fedfsd_LDADD		= $(LIBTIRPC) $(LIBLDAP) $(LIBLBER) \
+fedfsd_LDADD		= $(LIBTIRPC) $(LIBLDAP) $(LIBLBER) $(LIBXML2) \
 			  $(LIBSQLITE3) $(LIBIDN) $(LIBUUID) $(LIBCAP) \
 			  $(top_builddir)/src/libadmin/libadmin.la \
 			  $(top_builddir)/src/libnsdb/libnsdb.la \
diff --git a/src/libjunction/Makefile.am b/src/libjunction/Makefile.am
index e2a0a4f..9d91fef 100644
--- a/src/libjunction/Makefile.am
+++ b/src/libjunction/Makefile.am
@@ -26,7 +26,7 @@
 noinst_HEADERS		= junction-internal.h
 
 noinst_LTLIBRARIES	= libjunction.la
-libjunction_la_SOURCES	= export-cache.c fedfs.c junction.c
+libjunction_la_SOURCES	= export-cache.c fedfs.c junction.c xml.c
 
 CLEANFILES		= cscope.in.out cscope.out cscope.po.out *~
 DISTCLEANFILES		= Makefile.in
@@ -34,4 +34,5 @@ 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
+AM_CPPFLAGS		= -I. -I$(top_srcdir)/src/include \
+			  -I/usr/include/libxml2
diff --git a/src/libjunction/fedfs.c b/src/libjunction/fedfs.c
index d7af6cc..bbd0a65 100644
--- a/src/libjunction/fedfs.c
+++ b/src/libjunction/fedfs.c
@@ -23,6 +23,32 @@
  *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
  */
 
+/*
+ * A FedFS junction is an FSN, represented in a well-formed XML document:
+ *
+ * <?xml version="1.0" encoding="UTF-8"?>
+ * <junction>
+ *   <fileset>
+ *     <name fsnuuid="d2b09895-98ff-415e-ac73-565fad7b429b"
+ *           nsdbname="nsdb.example.net"
+ *           nsdbport="389" />
+ *   </fileset>
+ * </junction>
+ *
+ * FedFS 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>
 
@@ -31,14 +57,6 @@
 #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 <attr/xattr.h>
 
 #include "fedfs.h"
 #include "nsdb.h"
@@ -47,29 +65,30 @@
 #include "xlog.h"
 
 /**
- * Magic string planted in the "type" attribute of junctions
+ * Tag name of FSN element of a junction XML document
  */
-#define FEDFS_JUNCTION_TYPE		"fedfs"
+#define FEDFS_XML_FSN_TAG		(const xmlChar *)"name"
 
 /**
- * Name of extended attribute containing junction type
+ * Name of FSN UUID attribute on a fileset name element
  */
-#define FEDFS_XATTR_NAME_TYPE		"trusted.junction.type"
+#define FEDFS_XML_FSN_UUID_ATTR		(const xmlChar *)"fsnuuid"
 
 /**
- * Name of extended attribute containing junction's FSN UUID
+ * Name of NSDB hostname attribute on a fileset name element
  */
-#define FEDFS_XATTR_NAME_FSNUUID	"trusted.junction.fsnuuid"
+#define FEDFS_XML_FSN_NSDBNAME_ATTR	(const xmlChar *)"nsdbname"
 
 /**
- * Name of extended attribute containing hostname of NSDB service
+ * Name of NSDB port attribute on a fileset name element
  */
-#define FEDFS_XATTR_NAME_NSDB		"trusted.junction.nsdbname"
+#define FEDFS_XML_FSN_NSDBPORT_ATTR	(const xmlChar *)"nsdbport"
 
 /**
- * Name of extended attribute containing port of NSDB service
+ * XPath path to first fileset name element in a junction document
  */
-#define FEDFS_XATTR_NAME_PORT		"trusted.junction.nsdbport"
+#define FEDFS_XML_FSN_XPATH		(const xmlChar *)	\
+						"/junction/fileset/name[1]"
 
 
 /**
@@ -90,63 +109,154 @@ fedfs_remove_fsn(const char *pathname)
 	if (retval != FEDFS_OK)
 		return retval;
 
-	retval = junction_remove_xattr(fd, pathname, FEDFS_XATTR_NAME_TYPE);
-	if (retval != FEDFS_OK)
-		goto out;
-	retval = junction_remove_xattr(fd, pathname, FEDFS_XATTR_NAME_FSNUUID);
-	if (retval != FEDFS_OK)
-		goto out;
-	retval = junction_remove_xattr(fd, pathname, FEDFS_XATTR_NAME_NSDB);
-	if (retval != FEDFS_OK)
-		goto out;
-	retval = junction_remove_xattr(fd, pathname, FEDFS_XATTR_NAME_PORT);
+	retval = junction_remove_xattr(fd, pathname, JUNCTION_XATTR_NAME_NFS);
 
-out:
 	(void)close(fd);
 	return retval;
 }
 
 /**
- * Store FedFS information into a junction
+ * Construct fileset's "name" element
  *
  * @param pathname NUL-terminated C string containing pathname of a junction
+ * @param fileset parent node of new FSN element
  * @param fsn_uuid NUL-terminated C string containing FSN UUID to store
- * @param host an initialized nsdb_t object
+ * @param nsdb_name a NUL-terminated C string containing NSDB hostname
+ * @param nsdb_port the port number of the NSDB
+ * @return a FedFsStatus code
+ */
+static FedFsStatus
+fedfs_name_xml(const char *pathname, xmlNodePtr fileset,
+		const char *fsn_uuid, const char *nsdb_name,
+		unsigned short nsdb_port)
+{
+	xmlNodePtr new;
+
+	new = xmlNewTextChild(fileset , NULL, FEDFS_XML_FSN_TAG, NULL);
+	if (new == NULL) {
+		xlog(D_GENERAL, "%s: Failed to add FSN element for %s\n",
+			__func__, pathname);
+		return FEDFS_ERR_SVRFAULT;
+	}
+
+	xmlSetProp(new, FEDFS_XML_FSN_UUID_ATTR, (const xmlChar *)fsn_uuid);
+	xmlSetProp(new, FEDFS_XML_FSN_NSDBNAME_ATTR, (const xmlChar *)nsdb_name);
+	if (nsdb_port != LDAP_PORT)
+		junction_xml_set_int_attribute(new,
+						FEDFS_XML_FSN_NSDBPORT_ATTR,
+						nsdb_port);
+
+	return FEDFS_OK;
+}
+
+/**
+ * Construct a "fileset" element
+ *
+ * @param pathname NUL-terminated C string containing pathname of a junction
+ * @param root root element of XML document tree
+ * @param fsn_uuid NUL-terminated C string containing FSN UUID to store
+ * @param nsdb_name a NUL-terminated C string containing NSDB hostname
+ * @param nsdb_port the port number of the NSDB
+ * @return a FedFsStatus code
+ */
+static FedFsStatus
+fedfs_fileset_xml(const char *pathname, xmlNodePtr root,
+		const char *fsn_uuid, const char *nsdb_name,
+		unsigned short nsdb_port)
+{
+	xmlNodePtr fileset;
+
+	fileset = xmlNewTextChild(root, NULL, JUNCTION_XML_FILESET_TAG, NULL);
+	if (fileset == NULL) {
+		xlog(D_GENERAL, "%s: Failed to add fileset element for %s\n",
+			__func__, pathname);
+		return FEDFS_ERR_SVRFAULT;
+	}
+
+	return fedfs_name_xml(pathname, fileset, fsn_uuid, nsdb_name, nsdb_port);
+}
+
+/**
+ * Construct FedFS junction XML document
+ *
+ * @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 fsn_uuid NUL-terminated C string containing FSN UUID to store
+ * @param nsdb_name a NUL-terminated C string containing NSDB hostname
+ * @param nsdb_port the port number of the NSDB
+ * @return a FedFsStatus code
+ */
+static FedFsStatus
+fedfs_build_xml(const char *pathname, xmlDocPtr doc,
+		const char *fsn_uuid, const char *nsdb_name,
+		unsigned short nsdb_port)
+{
+	xmlNodePtr root;
+
+	root = xmlNewNode(NULL, JUNCTION_XML_ROOT_TAG);
+	if (root == NULL) {
+		xlog(D_GENERAL, "%s: Failed to create root element for %s\n",
+			__func__, pathname);
+		return FEDFS_ERR_SVRFAULT;
+	}
+	(void)xmlDocSetRootElement(doc, root);
+
+	return fedfs_fileset_xml(pathname, root, fsn_uuid, nsdb_name, nsdb_port);
+}
+
+/**
+ * Write FedFS information into a FedFS 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 fsn_uuid NUL-terminated C string containing FSN UUID to store
+ * @param nsdb_name a NUL-terminated C string containing NSDB hostname
+ * @param nsdb_port the port number of the NSDB
  * @return a FedFsStatus code
  *
  * @note Access to trusted attributes requires CAP_SYS_ADMIN.
  */
 static FedFsStatus
-fedfs_store_fsn(const char *pathname, const char *fsn_uuid, const nsdb_t host)
+fedfs_write_junction(const char *pathname, xmlDocPtr doc,
+		const char *fsn_uuid, const char *nsdb_name,
+		unsigned short nsdb_port)
 {
 	FedFsStatus retval;
-	char buf[20];
-	int fd, len;
 
-	retval = junction_open_path(pathname, &fd);
+	retval = fedfs_build_xml(pathname, doc, fsn_uuid, nsdb_name, nsdb_port);
 	if (retval != FEDFS_OK)
 		return retval;
 
-	retval = junction_set_xattr(fd, pathname, FEDFS_XATTR_NAME_TYPE,
-			FEDFS_XATTR_NAME_TYPE, sizeof(FEDFS_XATTR_NAME_TYPE));
-	if (retval != FEDFS_OK)
-		goto out;
+	return junction_xml_write(pathname, JUNCTION_XATTR_NAME_NFS, doc);
+}
 
-	retval = junction_set_xattr(fd, pathname, FEDFS_XATTR_NAME_FSNUUID,
-			fsn_uuid, strlen(fsn_uuid) + 1);
-	if (retval != FEDFS_OK)
-		goto out;
+/**
+ * Store FedFS information into a junction object
+ *
+ * @param pathname NUL-terminated C string containing pathname of a junction
+ * @param fsn_uuid NUL-terminated C string containing FSN UUID to store
+ * @param host an initialized nsdb_t object
+ * @return a FedFsStatus code
+ *
+ * @note Access to trusted attributes requires CAP_SYS_ADMIN.
+ */
+static FedFsStatus
+fedfs_store_fsn(const char *pathname, const char *fsn_uuid, const nsdb_t host)
+{
+	FedFsStatus retval;
+	xmlDocPtr doc;
 
-	retval = junction_set_xattr(fd, pathname, FEDFS_XATTR_NAME_NSDB,
-			nsdb_hostname(host), nsdb_hostname_len(host) + 1);
-	if (retval != FEDFS_OK)
-		goto out;
+	doc = xmlNewDoc((xmlChar *)"1.0");
+	if (doc == NULL) {
+		xlog(D_GENERAL, "%s: Failed to create XML doc for %s\n",
+			__func__, pathname);
+		return FEDFS_ERR_SVRFAULT;
+	}
 
-	len = snprintf(buf, sizeof(buf), "%u", nsdb_port(host));
-	retval = junction_set_xattr(fd, pathname, FEDFS_XATTR_NAME_PORT, buf, len + 1);
+	retval = fedfs_write_junction(pathname, doc, fsn_uuid,
+					nsdb_hostname(host), nsdb_port(host));
 
-out:
-	(void)close(fd);
+	xmlFreeDoc(doc);
 	return retval;
 }
 
@@ -181,6 +291,11 @@ fedfs_add_junction(const char *pathname, const char *fsn_uuid, const nsdb_t host
 	if (retval != FEDFS_OK)
 		goto out_err;
 
+	/* The content of this attribute is ignored */
+	retval = junction_add_type(pathname, "nfs");
+	if (retval != FEDFS_OK)
+		return retval;
+
 	return retval;
 
 out_err:
@@ -206,6 +321,10 @@ fedfs_delete_junction(const char *pathname)
 	if (retval != FEDFS_OK)
 		return retval;
 
+	retval = junction_remove_type(pathname);
+	if (retval != FEDFS_OK)
+		return retval;
+
 	retval = junction_restore_mode(pathname);
 	if (retval != FEDFS_OK)
 		return retval;
@@ -214,72 +333,198 @@ fedfs_delete_junction(const char *pathname)
 }
 
 /**
- * Retrieve FSN information from a FedFS junction
+ * Parse fileset name information from an XML node
  *
  * @param pathname NUL-terminated C string containing pathname of a junction
+ * @param node root of XML parse subtree containing fileset name element
  * @param fsn_uuid OUT: NUL-terminated C string containing FSN UUID to store
  * @param host OUT: an initialized nsdb_t object
  * @return a FedFsStatus code
  *
- * Caller must free the string returned in "fsn_uuid" with free(3), and
- * free the NSDB host returned in "host" with nsdb_free_nsdb().
+ * If fedfs_parse_node() returns FEDFS_OK, caller must free the string
+ * returned in "fsn_uuid" with free(3) and the NSDB host returned in "host"
+ * with nsdb_free_nsdb().
  */
-FedFsStatus
-fedfs_get_fsn(const char *pathname, char **fsn_uuid, nsdb_t *host)
+static FedFsStatus
+fedfs_parse_node(const char *pathname, xmlNodePtr node,
+		char **fsn_uuid, nsdb_t *host)
 {
-	void *uuid_tmp = NULL;
-	void *nsdbname_tmp = NULL;
-	void *port_tmp = NULL;
-	nsdb_t host_tmp = NULL;
-	unsigned short port;
+	xmlChar *nsdb_name_tmp, *fsn_uuid_tmp;
 	FedFsStatus retval;
-	size_t len;
-	int fd;
-
-	if (fsn_uuid == NULL || host == NULL)
-		return FEDFS_ERR_INVAL;
+	nsdb_t host_tmp;
+	char *tmp;
+	int port;
 
-	retval = junction_open_path(pathname, &fd);
-	if (retval != FEDFS_OK)
-		return retval;
+	fsn_uuid_tmp = xmlGetProp(node, FEDFS_XML_FSN_UUID_ATTR);
+	nsdb_name_tmp = xmlGetProp(node, FEDFS_XML_FSN_NSDBNAME_ATTR);
 
-	retval = junction_get_xattr(fd, pathname, FEDFS_XATTR_NAME_FSNUUID,
-							&uuid_tmp, &len);
-	if (retval != FEDFS_OK)
-		goto out_err;
+	retval = FEDFS_ERR_NOTJUNCT;
+	if (fsn_uuid_tmp == NULL) {
+		xlog(D_GENERAL, "%s: No UUID found in %s\n",
+			__func__, pathname);
+		goto out;
+	}
+	if (nsdb_name_tmp == NULL) {
+		xlog(D_GENERAL, "%s: No NSDB name found in %s\n",
+			__func__, pathname);
+		goto out;
+	}
 
-	retval = junction_get_xattr(fd, pathname, FEDFS_XATTR_NAME_NSDB,
-					&nsdbname_tmp, &len);
-	if (retval != FEDFS_OK)
-		goto out_err;
-	retval = junction_get_xattr(fd, pathname, FEDFS_XATTR_NAME_PORT,
-					&port_tmp, &len);
-	if (retval != FEDFS_OK)
-		goto out_err;
+	if (!junction_xml_get_int_attribute(node,
+					FEDFS_XML_FSN_NSDBPORT_ATTR, &port))
+		port = LDAP_PORT;
+	if (port < 1 || port > UINT16_MAX) {
+		xlog(D_GENERAL, "%s: Bad NSDB port value in %s\n",
+			__func__, pathname);
+		goto out;
+	}
 
 	retval = FEDFS_ERR_SVRFAULT;
-	if (!nsdb_parse_port_string(port_tmp, &port))
-		goto out_err;
+	tmp = strdup((char *)fsn_uuid_tmp);
+	if (tmp == NULL)
+		goto out;
 
 	retval = FEDFS_ERR_NSDB_PARAMS;
-	if (nsdb_lookup_nsdb(nsdbname_tmp, port, &host_tmp, NULL) != FEDFS_OK)
-		goto out_err;
+	if (nsdb_lookup_nsdb((const char *)nsdb_name_tmp, (unsigned short)port,
+					&host_tmp, NULL) != FEDFS_OK) {
+		xlog(D_GENERAL, "%s: No NSDB params for %s\n",
+			__func__, nsdb_name_tmp);
+		free(tmp);
+		goto out;
+	}
 
-	*fsn_uuid = uuid_tmp;
+	*fsn_uuid = tmp;
 	*host = host_tmp;
 	retval = FEDFS_OK;
 
 out:
-	free(port_tmp);
-	free(nsdbname_tmp);
-	(void)close(fd);
+	xmlFree(nsdb_name_tmp);
+	xmlFree(fsn_uuid_tmp);
 	return retval;
+}
 
-out_err:
-	nsdb_free_nsdb(host_tmp);
-	free(uuid_tmp);
-	goto out;
-	
+/**
+ * Parse fileset name information from a nodeset
+ *
+ * @param pathname NUL-terminated C string containing pathname of a junction
+ * @param nodeset XML path context containing junction XML
+ * @param fsn_uuid OUT: NUL-terminated C string containing FSN UUID to store
+ * @param host OUT: an initialized nsdb_t object
+ * @return a FedFsStatus code
+ *
+ * If fedfs_parse_nodeset() returns FEDFS_OK, caller must free the string
+ * returned in "fsn_uuid" with free(3) and the NSDB host returned in "host"
+ * with nsdb_free_nsdb().
+ */
+static FedFsStatus
+fedfs_parse_nodeset(const char *pathname, xmlNodeSetPtr nodeset,
+		char **fsn_uuid, nsdb_t *host)
+{
+	if (xmlXPathNodeSetIsEmpty(nodeset)) {
+		xlog(D_GENERAL, "%s: No fileset names found in %s\n",
+			__func__, pathname);
+		return FEDFS_ERR_NOTJUNCT;
+	}
+
+	return fedfs_parse_node(pathname, nodeset->nodeTab[0],
+					fsn_uuid, host);
+}
+
+/**
+ * Parse fileset name information from junction XML
+ *
+ * @param pathname NUL-terminated C string containing pathname of a junction
+ * @param context XML path context containing junction XML
+ * @param fsn_uuid OUT: NUL-terminated C string containing FSN UUID to store
+ * @param host OUT: an initialized nsdb_t object
+ * @return a FedFsStatus code
+ *
+ * If fedfs_parse_context() returns FEDFS_OK, caller must free the string
+ * returned in "fsn_uuid" with free(3) and the NSDB host returned in "host"
+ * with nsdb_free_nsdb().
+ */
+static FedFsStatus
+fedfs_parse_context(const char *pathname, xmlXPathContextPtr context,
+		char **fsn_uuid, nsdb_t *host)
+{
+	xmlXPathObjectPtr object;
+	FedFsStatus retval;
+
+	object = xmlXPathEvalExpression(FEDFS_XML_FSN_XPATH, context);
+	if (object == NULL) {
+		xlog(D_GENERAL, "%s: Failed to evaluate XML in %s\n",
+			__func__, pathname);
+		return FEDFS_ERR_NOTJUNCT;
+	}
+
+	retval = fedfs_parse_nodeset(pathname, object->nodesetval,
+					fsn_uuid, host);
+
+	xmlXPathFreeObject(object);
+	return retval;
+}
+
+/**
+ * Parse fileset name 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 fsn_uuid OUT: NUL-terminated C string containing FSN UUID to store
+ * @param host OUT: an initialized nsdb_t object
+ * @return a FedFsStatus code
+ *
+ * If fedfs_parse_xml() returns FEDFS_OK, caller must free the string returned
+ * in "fsn_uuid" with free(3) and the NSDB host returned in "host" with
+ * nsdb_free_nsdb().
+ */
+static FedFsStatus
+fedfs_parse_xml(const char *pathname, xmlDocPtr doc, char **fsn_uuid, nsdb_t *host)
+{
+	xmlXPathContextPtr context;
+	FedFsStatus retval;
+
+	context = xmlXPathNewContext(doc);
+	if (context == NULL) {
+		xlog(D_GENERAL, "%s: Failed to create XPath context from %s\n",
+			__func__, pathname);
+		return FEDFS_ERR_SVRFAULT;
+	}
+
+	retval = fedfs_parse_context(pathname, context, fsn_uuid, host);
+
+	xmlXPathFreeContext(context);
+	return retval;
+}
+
+/**
+ * Retrieve FSN information from a FedFS junction
+ *
+ * @param pathname NUL-terminated C string containing pathname of a junction
+ * @param fsn_uuid OUT: NUL-terminated C string containing FSN UUID to store
+ * @param host OUT: an initialized nsdb_t object
+ * @return a FedFsStatus code
+ *
+ * If fedfs_get_fsn() returns FEDFS_OK, caller must free the string returned
+ * in "fsn_uuid" with free(3) and the NSDB host returned in "host" with
+ * nsdb_free_nsdb().
+ */
+FedFsStatus
+fedfs_get_fsn(const char *pathname, char **fsn_uuid, nsdb_t *host)
+{
+	FedFsStatus retval;
+	xmlDocPtr doc;
+
+	if (fsn_uuid == NULL || host == NULL)
+		return FEDFS_ERR_INVAL;
+
+	retval = junction_xml_parse(pathname, JUNCTION_XATTR_NAME_NFS, &doc);
+	if (retval != FEDFS_OK)
+		return retval;
+
+	retval = fedfs_parse_xml(pathname, doc, fsn_uuid, host);
+
+	xmlFreeDoc(doc);
+	return retval;
 }
 
 /**
@@ -292,7 +537,7 @@ out_err:
  *	FEDFS_ERR_NOTJUNCT:	"pathname" refers to an object that can be
  *				made into a FedFS junction
  *	FEDFS_ERR_EXIST:	"pathname" refers to something that is
- *				already a FedFS junction
+ *				already a junction
  *	FEDFS_ERR_INVAL:	"pathname" does not exist
  *	Other:			Some error occurred, "pathname" not
  *				investigated
@@ -321,37 +566,7 @@ fedfs_is_prejunction(const char *pathname)
 		goto out_close;
 	}
 
-	retval = junction_is_xattr_present(fd, pathname, FEDFS_XATTR_NAME_TYPE);
-	switch (retval) {
-	case FEDFS_ERR_NOTJUNCT:
-		break;
-	case FEDFS_OK:
-		goto out_exist;
-	default:
-		goto out_close;
-	}
-
-	retval = junction_is_xattr_present(fd, pathname, FEDFS_XATTR_NAME_FSNUUID);
-	switch (retval) {
-	case FEDFS_ERR_NOTJUNCT:
-		break;
-	case FEDFS_OK:
-		goto out_exist;
-	default:
-		goto out_close;
-	}
-
-	retval = junction_is_xattr_present(fd, pathname, FEDFS_XATTR_NAME_NSDB);
-	switch (retval) {
-	case FEDFS_ERR_NOTJUNCT:
-		break;
-	case FEDFS_OK:
-		goto out_exist;
-	default:
-		goto out_close;
-	}
-	
-	retval = junction_is_xattr_present(fd, pathname, FEDFS_XATTR_NAME_PORT);
+	retval = junction_is_xattr_present(fd, pathname, JUNCTION_XATTR_NAME_NFS);
 	switch (retval) {
 	case FEDFS_ERR_NOTJUNCT:
 		break;
@@ -370,20 +585,56 @@ out_exist:
 }
 
 /**
- * Predicate: does "pathname" refer to a FedFS junction?
+ * Verify that junction contains FedFS junction XML
  *
  * @param pathname NUL-terminated C string containing pathname of a directory
  * @return a FedFsStatus code
  *
- * Returns FEDFS_OK if "pathname" refers to a junction, or
- * FEDFS_ERR_NOTJUNCT if "pathname" does not refer to a junction, or
- * FEDFS_ERR_INVAL if "pathname" refers to something that does not exist.
- * Other errors may trickle up from lower layers.
+ * Return values:
+ *	FEDFS_OK:		"pathname" refers to a FedFS junction
+ *	FEDFS_ERR_NOTJUNCT:	"pathname" refers to something that is
+ *				not a FedFS 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
+fedfs_is_junction_xml(const char *pathname)
+{
+	FedFsStatus retval;
+	char *fsn_uuid;
+	xmlDocPtr doc;
+	nsdb_t host;
+
+	retval = junction_xml_parse(pathname, JUNCTION_XATTR_NAME_NFS, &doc);
+	if (retval != FEDFS_OK)
+		return retval;
+
+	retval = fedfs_parse_xml(pathname, doc, &fsn_uuid, &host);
+	if (retval != FEDFS_OK)
+		goto out;
+
+	free(fsn_uuid);
+	nsdb_free_nsdb(host);
+out:
+	xmlFreeDoc(doc);
+	return retval;
+}
+
+/**
+ * Predicate: does "pathname" refer to a FedFS junction?
+ *
+ * @param pathname NUL-terminated C string containing pathname of a directory
+ * @return a FedFsStatus code
  *
  * Return values:
  *	FEDFS_OK:		"pathname" refers to a FedFS junction
- *	FEDFS_ERR_NOTJUNCT:	"pathname" refers to an object that can be
- *				made into a FedFS junction
+ *	FEDFS_ERR_NOTJUNCT:	"pathname" refers to an object that is
+ *				not a FedFS junction
  *	FEDFS_ERR_INVAL:	"pathname" does not exist
  *	Other:			Some error occurred, "pathname" not
  *				investigated
@@ -406,19 +657,13 @@ fedfs_is_junction(const char *pathname)
 	if (retval != FEDFS_OK)
 		goto out_close;
 
-	retval = junction_is_xattr_present(fd, pathname, FEDFS_XATTR_NAME_TYPE);
+	retval = junction_is_xattr_present(fd, pathname, JUNCTION_XATTR_NAME_NFS);
 	if (retval != FEDFS_OK)
 		goto out_close;
 
-	retval = junction_is_xattr_present(fd, pathname, FEDFS_XATTR_NAME_FSNUUID);
-	if (retval != FEDFS_OK)
-		goto out_close;
+	(void)close(fd);
 
-	retval = junction_is_xattr_present(fd, pathname, FEDFS_XATTR_NAME_NSDB);
-	if (retval != FEDFS_OK)
-		goto out_close;
-	
-	retval = junction_is_xattr_present(fd, pathname, FEDFS_XATTR_NAME_PORT);
+	return fedfs_is_junction_xml(pathname);
 
 out_close:
 	(void)close(fd);
diff --git a/src/libjunction/junction-internal.h b/src/libjunction/junction-internal.h
index 5be355d..a2c35c0 100644
--- a/src/libjunction/junction-internal.h
+++ b/src/libjunction/junction-internal.h
@@ -26,6 +26,48 @@
 #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 junction type
+ */
+#define JUNCTION_XATTR_NAME_TYPE	"trusted.junction.type"
+
+/**
+ * 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"
+
+
+/**
+ ** 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);
@@ -42,5 +84,24 @@ FedFsStatus	 junction_remove_xattr(int fd, const char *pathname,
 			const char *name);
 FedFsStatus	 junction_save_mode(const char *pathname);
 FedFsStatus	 junction_restore_mode(const char *pathname);
+FedFsStatus	 junction_add_type(const char *pathname, const char *type);
+FedFsStatus	 junction_remove_type(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);
+_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);
+FedFsStatus	 junction_xml_parse(const char *pathname, const char *name,
+			xmlDocPtr *doc);
+FedFsStatus	 junction_xml_write(const char *pathname, const char *name,
+			xmlDocPtr doc);
 
 #endif	/* !_FEDFS_JUNCTION_INTERNAL_H_ */
diff --git a/src/libjunction/junction.c b/src/libjunction/junction.c
index 832c19f..baf7a90 100644
--- a/src/libjunction/junction.c
+++ b/src/libjunction/junction.c
@@ -47,12 +47,6 @@
 #include "xlog.h"
 
 /**
- * Name of extended attribute containing saved mode bits
- */
-#define JUNCTION_XATTR_NAME_MODE	"trusted.junction.mode"
-
-
-/**
  * Open a file system object
  *
  * @param pathname NUL-terminated C string containing pathname of an object
@@ -471,3 +465,56 @@ out:
 	(void)close(fd);
 	return retval;
 }
+
+/**
+ * Add the TYPE xattr
+ *
+ * @param pathname NUL-terminated C string containing pathname of a directory
+ * @param type NUL-terminated C string containing the contents of the new xattr
+ * @return a FedFsStatus code
+ *
+ * @note Access to trusted attributes requires CAP_SYS_ADMIN.
+ *
+ * The TYPE xattr is read by local file servers to know when they should
+ * perform junction resolution.
+ */
+FedFsStatus
+junction_add_type(const char *pathname, const char *type)
+{
+	FedFsStatus retval;
+	int fd;
+
+	retval = junction_open_path(pathname, &fd);
+	if (retval != FEDFS_OK)
+		return retval;
+
+	retval = junction_set_xattr(fd, pathname, JUNCTION_XATTR_NAME_TYPE,
+						type, strlen(type) + 1);
+
+	(void)close(fd);
+	return retval;
+}
+
+/**
+ * Remove the TYPE xattr
+ *
+ * @param pathname NUL-terminated C string containing pathname of a directory
+ * @return a FedFsStatus code
+ *
+ * @note Access to trusted attributes requires CAP_SYS_ADMIN.
+ */
+FedFsStatus
+junction_remove_type(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_TYPE);
+
+	(void)close(fd);
+	return retval;
+}
diff --git a/src/libjunction/xml.c b/src/libjunction/xml.c
new file mode 100644
index 0000000..bc77fdc
--- /dev/null
+++ b/src/libjunction/xml.c
@@ -0,0 +1,244 @@
+/**
+ * @file src/libjunction/xml.c
+ * @brief Common utilities for managing junction XML
+ */
+
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "fedfs.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);
+}
+
+/**
+ * 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
+ * @return true if attribute "attrname" has a valid integer value
+ */
+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);
+}
+
+/**
+ * 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;
+
+	xmlIndentTreeOutput = 1;
+	xmlDocDumpFormatMemoryEnc(doc, &buf, &len, "UTF-8", 1);
+	retval = junction_set_xattr(fd, pathname, name, buf, len);
+	xmlFree(buf);
+
+	(void)close(fd);
+	return retval;
+}
diff --git a/src/nsdbc/Makefile.am b/src/nsdbc/Makefile.am
index a52d91c..bf0e057 100644
--- a/src/nsdbc/Makefile.am
+++ b/src/nsdbc/Makefile.am
@@ -28,7 +28,7 @@ sbin_PROGRAMS		= nsdb-annotate nsdb-describe nsdb-list \
 			  nsdb-delete-nsdb \
 			  nsdb-create-fsn nsdb-delete-fsn nsdb-resolve-fsn \
 			  nsdb-create-fsl nsdb-delete-fsl nsdb-update-fsl
-LDADD			= $(LIBLDAP) $(LIBLBER) \
+LDADD			= $(LIBLDAP) $(LIBLBER) $(LIBXML2) \
 			  $(LIBSQLITE3) $(LIBIDN) $(LIBUUID) \
 			  $(top_builddir)/src/libnsdb/libnsdb.la \
 			  $(top_builddir)/src/libxlog/libxlog.la \
diff --git a/src/nsdbparams/Makefile.am b/src/nsdbparams/Makefile.am
index b21220e..7b7911a 100644
--- a/src/nsdbparams/Makefile.am
+++ b/src/nsdbparams/Makefile.am
@@ -26,7 +26,7 @@
 noinst_HEADERS		= nsdbparams.h
 sbin_PROGRAMS		= nsdbparams
 nsdbparams_SOURCES	= delete.c list.c main.c show.c update.c
-LDADD			= $(LIBLDAP) $(LIBLBER) \
+LDADD			= $(LIBLDAP) $(LIBLBER) $(LIBXML2) \
 			  $(LIBSQLITE3) $(LIBIDN) $(LIBUUID) $(LIBCAP) \
 			  $(top_builddir)/src/libnsdb/libnsdb.la \
 			  $(top_builddir)/src/libjunction/libjunction.la \
diff --git a/src/resolve-junction/Makefile.am b/src/resolve-junction/Makefile.am
index 323a1d9..51ca299 100644
--- a/src/resolve-junction/Makefile.am
+++ b/src/resolve-junction/Makefile.am
@@ -27,7 +27,7 @@ noinst_HEADERS		= privilege.h
 
 sbin_PROGRAMS		= resolve-junction
 resolve_junction_SOURCES = main.c privilege.c privilege.h
-LDADD			= $(LIBLDAP) $(LIBLBER) \
+LDADD			= $(LIBLDAP) $(LIBLBER) $(LIBXML2) \
 			  $(LIBSQLITE3) $(LIBIDN) $(LIBUUID) $(LIBCAP) \
 			  $(top_builddir)/src/libadmin/libadmin.la \
 			  $(top_builddir)/src/libnsdb/libnsdb.la \
