diff mbox

[6/8] libnsdb: Add URI pathname parser helpers

Message ID 20121129015016.2497.67318.stgit@seurat.1015granger.net
State Accepted
Headers show

Commit Message

Chuck Lever Nov. 29, 2012, 1:50 a.m. UTC
We're about to introduce support for encoding NFSv4 pathnames in
an NFS URI.  Add helpers for handling the details.

Note that this commit adds a new build dependency: liburiparser.
Not all current distributions have liburiparser.  RHEL / OL 6
do not have it, for example.

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

 configure.ac               |    5 +
 src/fedfsd/Makefile.am     |    1 
 src/include/nsdb.h         |    7 +
 src/libnsdb/path.c         |  229 ++++++++++++++++++++++++++++++++++++++++++++
 src/nfsref/Makefile.am     |    1 
 src/nsdbc/Makefile.am      |    1 
 src/nsdbparams/Makefile.am |    1 
 7 files changed, 245 insertions(+), 0 deletions(-)
diff mbox

Patch

diff --git a/configure.ac b/configure.ac
index 9a848c5..31634cb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -123,6 +123,11 @@  AC_CHECK_LIB([xml2], [xmlParseFile],
 		 AC_DEFINE([HAVE_LIBXML2], [1],
 			   [Define if you have libxml2])],
 		[AC_MSG_ERROR([libxml2 not found.])])
+AC_CHECK_LIB([uriparser], [uriParseUriA],
+		[AC_SUBST([LIBURIPARSER], ["-luriparser"])
+		 AC_DEFINE([HAVE_LIBURIPARSER], [1],
+			   [Define if you have liburiparser])],
+		[AC_MSG_ERROR([liburiparser 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/fedfsd/Makefile.am b/src/fedfsd/Makefile.am
index 47ee106..29050c2 100644
--- a/src/fedfsd/Makefile.am
+++ b/src/fedfsd/Makefile.am
@@ -29,6 +29,7 @@  sbin_PROGRAMS		= fedfsd
 fedfsd_SOURCES		= listen.c main.c privilege.c svc.c
 fedfsd_LDADD		= $(LIBTIRPC) $(LIBLDAP) $(LIBLBER) $(LIBXML2) \
 			  $(LIBSQLITE3) $(LIBIDN) $(LIBUUID) $(LIBCAP) \
+			  $(LIBURIPARSER) \
 			  $(top_builddir)/src/libadmin/libadmin.la \
 			  $(top_builddir)/src/libnsdb/libnsdb.la \
 			  $(top_builddir)/src/libjunction/libjunction.la \
diff --git a/src/include/nsdb.h b/src/include/nsdb.h
index a115fae..d6c6841 100644
--- a/src/include/nsdb.h
+++ b/src/include/nsdb.h
@@ -28,6 +28,7 @@ 
 
 #include <netdb.h>
 #include <ldap.h>
+#include <uriparser/Uri.h>
 
 #include "fedfs_admin.h"
 #include "fedfs.h"
@@ -414,5 +415,11 @@  FedFsStatus	 nsdb_path_array_to_fedfspathname(char * const *path_array,
 				FedFsPathName *fpath);
 FedFsStatus	 nsdb_fedfspathname_to_path_array(FedFsPathName fpath,
 				char ***path_array);
+void		 nsdb_assign_textrange(UriTextRangeA *text,
+				const char *string);
+FedFsStatus	 nsdb_path_array_to_uri_pathname(char * const *path_array,
+				UriUriA *uri);
+FedFsStatus	 nsdb_uri_pathname_to_path_array(const UriUriA *uri,
+				char ***path_array);
 
 #endif	/* !_FEDFS_NSDB_H_ */
diff --git a/src/libnsdb/path.c b/src/libnsdb/path.c
index e5506ea..6894ee2 100644
--- a/src/libnsdb/path.c
+++ b/src/libnsdb/path.c
@@ -38,6 +38,7 @@ 
 #include <ldap.h>
 
 #include <netinet/in.h>
+#include <uriparser/Uri.h>
 
 #include "nsdb.h"
 #include "junction.h"
@@ -723,3 +724,231 @@  nsdb_fedfspathname_to_path_array(FedFsPathName fpath, char ***path_array)
 	*path_array = result;
 	return FEDFS_OK;
 }
+
+/**
+ * Assign the value of "string" to a UriTextRangeA field
+ *
+ * @param text UriTextRangeA field to assign
+ * @param string NUL-terminated C string
+ *
+ * Note: "string" must not be freed until the text range
+ * is no longer used.
+ *
+ * Note: string is assumed to contain only single-width
+ * characters.
+ */
+void
+nsdb_assign_textrange(UriTextRangeA *text, const char *string)
+{
+	text->first = string;
+	text->afterLast = string + strlen(string);
+}
+
+/**
+ * Allocate a UriPathSegmentA
+ *
+ * @param name NUL-terminated C string containing path segment
+ * @return freshly allocated UriPathSegmentA object
+ */
+static UriPathSegmentA *
+nsdb_new_uri_path_segment(const char *name)
+{
+	UriPathSegmentA *new;
+
+	new = (UriPathSegmentA *)calloc(1, sizeof(*new));
+	if (new != NULL)
+		nsdb_assign_textrange(&new->text, name);
+	return new;
+}
+
+/**
+ * Release a list of UriPathSegmentA objects
+ *
+ * @param pos head of UriPathSegmentA list
+ */
+static void
+nsdb_free_path_segments(UriPathSegmentA *pos)
+{
+	UriPathSegmentA *next;
+
+	while (pos != NULL) {
+		next = pos->next;
+		free(pos);
+		pos = next;
+	}
+}
+
+/**
+ * Marshal the pathname component of an NFS URI
+ *
+ * @param path_array array of pointers to NUL-terminated C strings
+ * @param uri OUT: a filled-in UriUriA structure
+ * @return a FedFsStatus code
+ *
+ * Caller must free the members of the UriUriA object with
+ * uriFreeUriMembersA().
+ *
+ * @todo Proper i18n of pathname segments
+ */
+FedFsStatus
+nsdb_path_array_to_uri_pathname(char * const *path_array, UriUriA *uri)
+{
+	UriPathSegmentA *pos, *result;
+	size_t length, len;
+	char *component;
+	unsigned int i;
+
+	pos = nsdb_new_uri_path_segment("");
+	if (pos == NULL)
+		return FEDFS_ERR_SVRFAULT;
+	result = pos;
+
+	length = 0;
+	for (i = 0; path_array[i] != NULL; i++) {
+		component = path_array[i];
+		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;
+		}
+
+		pos->next = nsdb_new_uri_path_segment(component);
+		if (pos->next == NULL) {
+			nsdb_free_path_segments(result);
+			return FEDFS_ERR_SVRFAULT;
+		}
+		pos = pos->next;
+	}
+
+	uri->pathHead = result;
+	return FEDFS_OK;
+}
+
+/**
+ * Return length in bytes of a URI pathname segment
+ *
+ * @param segment URI pathname segment
+ * @return count of bytes in segment
+ *
+ * XXX: Isn't there a uriparser API that does this?
+ */
+static size_t
+nsdb_uri_pathname_segment_size(const UriPathSegmentA *segment)
+{
+	if (segment->text.first == NULL)
+		return 0;
+	return segment->text.afterLast - segment->text.first;
+}
+
+/**
+ * Return number of segments in a URI pathname
+ *
+ * @param uri filled-in URI
+ * @return count of segments
+ *
+ * XXX: Isn't there a uriparser API that does this?
+ */
+static unsigned int
+nsdb_uri_pathname_segment_count(const UriUriA *uri)
+{
+	UriPathSegmentA *pos;
+	unsigned int result;
+
+	if (uri->pathHead->text.first == NULL)
+		return 0;
+
+	result = 1;
+	for (pos = uri->pathHead; pos != uri->pathTail; pos = pos->next)
+		result++;
+	return result;
+}
+
+/**
+ * Unmarshal the pathname component of an NFS URI
+ *
+ * @param uri a filled-in UriUriA structure
+ * @param path_array OUT: array of pointers to NUL-terminated C strings
+ * @return a FedFsStatus code
+ *
+ * Caller must free "path_array" with nsdb_free_string_array().
+ *
+ * @todo Proper i18n of pathname segments
+ * @todo Handling too many slashes in various places
+ */
+FedFsStatus
+nsdb_uri_pathname_to_path_array(const UriUriA *uri, char ***path_array)
+{
+	unsigned int i, count;
+	UriPathSegmentA *pos;
+	char **result = NULL;
+
+	if (uri->pathHead == NULL) {
+		xlog(D_GENERAL, "%s: NFS URI has no pathname component", __func__);
+		return FEDFS_ERR_BADNAME;
+	}
+
+	count = nsdb_uri_pathname_segment_count(uri);
+	if (count < 2) {
+		xlog(D_GENERAL, "%s: NFS URI has short pathname component", __func__);
+		return FEDFS_ERR_BADNAME;
+	}
+
+	pos = uri->pathHead->next;
+	if (count == 2 && nsdb_uri_pathname_segment_size(pos) == 0)
+		return nsdb_alloc_zero_component_pathname(path_array);
+
+	result = (char **)calloc(count + 1, sizeof(char *));
+	if (result == NULL) {
+		xlog(L_ERROR, "%s: Failed to allocate array",
+			__func__);
+		return FEDFS_ERR_SVRFAULT;
+	}
+
+	for (i = 0; pos != NULL; pos = pos->next) {
+		size_t len;
+
+		len = nsdb_uri_pathname_segment_size(pos);
+		if (len > NAME_MAX) {
+			nsdb_free_string_array(result);
+			xlog(D_GENERAL, "%s: Component length too long",
+				__func__);
+			return FEDFS_ERR_NAMETOOLONG;
+		}
+		if (len == 0)
+			continue;
+
+		result[i] = strndup((char *)pos->text.first, len);
+		if (result[i] == NULL) {
+			nsdb_free_string_array(result);
+			xlog(L_ERROR, "%s: Failed to allocate component string",
+				__func__);
+			return FEDFS_ERR_SVRFAULT;
+		}
+		i++;
+	}
+
+	*path_array = result;
+	return FEDFS_OK;
+}
diff --git a/src/nfsref/Makefile.am b/src/nfsref/Makefile.am
index 284d4b4..c8ef8c9 100644
--- a/src/nfsref/Makefile.am
+++ b/src/nfsref/Makefile.am
@@ -28,6 +28,7 @@  sbin_PROGRAMS		= nfsref
 nfsref_SOURCES		= add.c lookup.c nfsref.c remove.c
 LDADD			= $(LIBLDAP) $(LIBLBER) $(LIBXML2) \
 			  $(LIBSQLITE3) $(LIBIDN) $(LIBUUID) \
+			  $(LIBURIPARSER) \
 			  $(top_builddir)/src/libnsdb/libnsdb.la \
 			  $(top_builddir)/src/libxlog/libxlog.la \
 			  $(top_builddir)/src/libjunction/libjunction.la
diff --git a/src/nsdbc/Makefile.am b/src/nsdbc/Makefile.am
index bf0e057..e64d259 100644
--- a/src/nsdbc/Makefile.am
+++ b/src/nsdbc/Makefile.am
@@ -30,6 +30,7 @@  sbin_PROGRAMS		= nsdb-annotate nsdb-describe nsdb-list \
 			  nsdb-create-fsl nsdb-delete-fsl nsdb-update-fsl
 LDADD			= $(LIBLDAP) $(LIBLBER) $(LIBXML2) \
 			  $(LIBSQLITE3) $(LIBIDN) $(LIBUUID) \
+			  $(LIBURIPARSER) \
 			  $(top_builddir)/src/libnsdb/libnsdb.la \
 			  $(top_builddir)/src/libxlog/libxlog.la \
 			  $(top_builddir)/src/libjunction/libjunction.la
diff --git a/src/nsdbparams/Makefile.am b/src/nsdbparams/Makefile.am
index 7b7911a..9deb9ea 100644
--- a/src/nsdbparams/Makefile.am
+++ b/src/nsdbparams/Makefile.am
@@ -28,6 +28,7 @@  sbin_PROGRAMS		= nsdbparams
 nsdbparams_SOURCES	= delete.c list.c main.c show.c update.c
 LDADD			= $(LIBLDAP) $(LIBLBER) $(LIBXML2) \
 			  $(LIBSQLITE3) $(LIBIDN) $(LIBUUID) $(LIBCAP) \
+			  $(LIBURIPARSER) \
 			  $(top_builddir)/src/libnsdb/libnsdb.la \
 			  $(top_builddir)/src/libjunction/libjunction.la \
 			  $(top_builddir)/src/libxlog/libxlog.la