@@ -395,4 +395,17 @@ FedFsStatus nsdb_posix_path_to_xdr(const char *pathname,
FedFsStatus nsdb_xdr_to_posix_path(struct berval *xdr_path,
char **pathname);
+FedFsStatus nsdb_path_array_to_xdr(char * const *path_array,
+ struct berval *xdr_path);
+FedFsStatus nsdb_xdr_to_path_array(const struct berval *xdr_path,
+ char ***path_array);
+FedFsStatus nsdb_path_array_to_posix(char * const *path_array,
+ char **pathname);
+FedFsStatus nsdb_posix_to_path_array(const char *pathname,
+ char ***path_array);
+FedFsStatus nsdb_path_array_to_fedfspathname(char * const *path_array,
+ FedFsPathName *fpath);
+FedFsStatus nsdb_fedfspathname_to_path_array(FedFsPathName fpath,
+ char ***path_array);
+
#endif /* !_FEDFS_NSDB_H_ */
@@ -4,7 +4,7 @@
*/
/*
- * Copyright 2010 Oracle. All rights reserved.
+ * Copyright 2010, 2011 Oracle. All rights reserved.
*
* This file is part of fedfs-utils.
*
@@ -55,12 +55,49 @@
* @return equivalent number of XDR 4-octet units
*/
static inline unsigned int
-nsdb_quadlen(const unsigned int bytes)
+nsdb_quadlen(unsigned int bytes)
{
return (bytes + 3) >> 2;
}
/**
+ * Bounded search for a character inside a string
+ *
+ * @param haystack C string to search
+ * @param needle character to find
+ * @param size number of character in "haystack" to search
+ * @return pointer to "needle" in "haystack," or NULL
+ */
+static const char *
+nsdb_strnchr(const char *haystack, char needle, size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ if (haystack[i] == needle)
+ return &haystack[i];
+ return NULL;
+}
+
+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) {
+ xlog(L_ERROR, "%s: Failed to allocate array",
+ __func__);
+ 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
@@ -379,6 +416,8 @@ nsdb_free_fedfspathname(FedFsPathName *fpath)
for (i = 0; i < fpath->FedFsPathName_len; i++)
nsdb_free_component(&fpath->FedFsPathName_val[i]);
free(fpath->FedFsPathName_val);
+ fpath->FedFsPathName_val = NULL;
+ fpath->FedFsPathName_len = 0;
}
/**
@@ -501,7 +540,7 @@ nsdb_fedfspathname_to_posix(const FedFsPathName fpath, char **pathname)
return FEDFS_ERR_NAMETOOLONG;
}
- if (strchr(component, '/') != NULL) {
+ if (nsdb_strnchr(component, '/', len) != NULL) {
xlog(D_GENERAL, "%s: Local separator "
"character found in component",
__func__);
@@ -532,3 +571,471 @@ nsdb_fedfspathname_to_posix(const FedFsPathName fpath, char **pathname)
return FEDFS_ERR_SVRFAULT;
return FEDFS_OK;
}
+
+/**
+ * XDR encode an array of component strings
+ *
+ * @param path_array array of pointers to NUL-terminated C strings
+ * @param xdr_path OUT: berval set up with XDR-encoded binary path
+ * @return a FedFsStatus code
+ *
+ * Caller must free "xdr_path.bval" with ber_memfree(3t).
+ */
+FedFsStatus
+nsdb_path_array_to_xdr(char * const *path_array, struct berval *xdr_path)
+{
+ unsigned int p, count;
+ size_t len, length;
+ uint32_t *xdrbuf;
+ char *component;
+
+ if (path_array == NULL || xdr_path == NULL) {
+ xlog(L_ERROR, "%s: Invalid argument", __func__);
+ return FEDFS_ERR_INVAL;
+ }
+
+ for (length = XDR_UINT_BYTES, 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 (!nsdb_pathname_is_utf8(component)) {
+ xlog(D_GENERAL, "%s: Bad character in component",
+ __func__);
+ return FEDFS_ERR_BADCHAR;
+ }
+ length += XDR_UINT_BYTES + (nsdb_quadlen(len) << 2);
+ }
+
+ xdrbuf = (uint32_t *)ber_memcalloc(1, (ber_len_t)length);
+ if (xdrbuf == NULL) {
+ xlog(L_ERROR, "%s: Failed to allocate XDR buffer",
+ __func__);
+ return FEDFS_ERR_SVRFAULT;
+ }
+
+ xdrbuf[0] = htonl(count);
+ for (p = 1, count = 0; path_array[count] != NULL; count++) {
+ component = path_array[count];
+ len = strlen(component);
+
+ xdrbuf[p++] = htonl(len);
+ memcpy(&xdrbuf[p], component, len);
+ p += nsdb_quadlen(len);
+ }
+
+ xdr_path->bv_val = (char *)xdrbuf;
+ xdr_path->bv_len = (ber_len_t)(length);
+ return FEDFS_OK;
+}
+
+/**
+ * XDR decode an XDR byte stream into an array of component strings
+ *
+ * @param xdr_path berval with XDR-encoded binary path
+ * @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().
+ *
+ * NB: The use of fixed constants for NAME_MAX and PATH_MAX are required
+ * here because, on the client side, the pathname likely does not
+ * exist, so pathconf(3) cannot be used.
+ */
+FedFsStatus
+nsdb_xdr_to_path_array(const struct berval *xdr_path, char ***path_array)
+{
+ uint32_t *xdrbuf = (uint32_t *)xdr_path->bv_val;
+ unsigned int len, i, p, count, length;
+ char **result;
+
+ if (xdr_path == NULL || path_array == NULL) {
+ xlog(L_ERROR, "%s: Invalid argument", __func__);
+ return FEDFS_ERR_INVAL;
+ }
+ length = nsdb_quadlen((unsigned int)xdr_path->bv_len);
+ xlog(D_CALL, "%s: Received %u XDR'd quads", __func__, length);
+
+ p = 0;
+ count = ntohl(xdrbuf[p]);
+ if (count == 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;
+ }
+
+ p = 1;
+ for (i = 0; i < count; i++) {
+ len = ntohl(xdrbuf[p++]);
+ if (len > NAME_MAX) {
+ nsdb_free_string_array(result);
+ xlog(L_ERROR, "%s: Component too long", __func__);
+ return FEDFS_ERR_BADNAME;
+ }
+ if (p + nsdb_quadlen(len) > length) {
+ nsdb_free_string_array(result);
+ xlog(L_ERROR, "%s: XDR buffer overflow", __func__);
+ return FEDFS_ERR_BADXDR;
+ }
+
+ result[i] = strndup((char *)&xdrbuf[p], len);
+ if (result[i] == NULL) {
+ nsdb_free_string_array(result);
+ xlog(L_ERROR, "%s: Failed to allocate component string",
+ __func__);
+ return FEDFS_ERR_SVRFAULT;
+ }
+
+ if (!nsdb_pathname_is_utf8(result[i])) {
+ nsdb_free_string_array(result);
+ xlog(D_GENERAL, "%s: Bad character in pathname", __func__);
+ return FEDFS_ERR_BADCHAR;
+ }
+ p += nsdb_quadlen(len);
+ }
+
+ *path_array = result;
+ return FEDFS_OK;
+}
+
+/**
+ * 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) {
+ xlog(L_ERROR, "%s: Invalid argument", __func__);
+ return FEDFS_ERR_INVAL;
+ }
+
+ if (path_array[0] == NULL) {
+ xlog(D_GENERAL, "%s: Zero-component pathname", __func__);
+ result = strdup("/");
+ if (result == NULL) {
+ xlog(D_GENERAL, "%s: Failed to allocate buffer for result",
+ __func__);
+ 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) {
+ xlog(D_GENERAL, "%s: Failed to allocate buffer for result",
+ __func__);
+ 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) {
+ xlog(L_ERROR, "%s: Invalid argument", __func__);
+ 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) {
+ xlog(L_ERROR, "%s: Failed to allocate array",
+ __func__);
+ return FEDFS_ERR_SVRFAULT;
+ }
+
+ component = normalized;
+ for (i = 0; ; i++) {
+ char *next;
+
+ if (*component == '/')
+ component++;
+ if (*component == '\0')
+ break;
+ next = strchrnul(component, '/');
+ length = next - component;
+
+ result[i] = strndup(component, length);
+ if (result[i] == NULL) {
+ xlog(D_GENERAL, "%s: Failed to allocate "
+ "new pathname component", __func__);
+ nsdb_free_string_array(result);
+ return FEDFS_ERR_SVRFAULT;
+ }
+
+ if (*next == '\0')
+ break;
+ component = next;
+ }
+
+ *path_array = result;
+ free(normalized);
+ return FEDFS_OK;
+}
+
+/**
+ * Construct a FedFsPathName from an array of component strings
+ *
+ * @param path_array array of pointers to NUL-terminated C strings
+ * @param fpath OUT: pointer to FedFsPathName in which to construct path
+ * @return a FedFsStatus code
+ *
+ * Caller must free "fpath" with nsdb_free_fedfspathname().
+ */
+FedFsStatus
+nsdb_path_array_to_fedfspathname(char * const *path_array, FedFsPathName *fpath)
+{
+ unsigned int i, count;
+ size_t length, len;
+ char *component;
+
+ if (path_array == NULL || fpath == NULL) {
+ xlog(L_ERROR, "%s: Invalid argument", __func__);
+ return FEDFS_ERR_INVAL;
+ }
+
+ /* The path "/" MUST be encoded as an array with zero components. */
+ if (path_array[0] == NULL) {
+ xlog(D_GENERAL, "%s: Zero-component pathname", __func__);
+ fpath->FedFsPathName_val = NULL;
+ fpath->FedFsPathName_len = 0;
+ 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;
+ }
+ }
+
+ fpath->FedFsPathName_val = calloc(count + 1, sizeof(FedFsPathComponent));
+ if (fpath->FedFsPathName_val == NULL) {
+ return FEDFS_ERR_SVRFAULT;
+ }
+ fpath->FedFsPathName_len = count;
+
+ for (i = 0; i < count; i++) {
+ component = path_array[i];
+ len = strlen(component);
+
+ if (!nsdb_new_component(component, len,
+ &fpath->FedFsPathName_val[i])) {
+ xlog(D_GENERAL, "%s: Failed to allocate "
+ "new pathname component", __func__);
+ nsdb_free_fedfspathname(fpath);
+ return FEDFS_ERR_SVRFAULT;
+ }
+ }
+
+ return FEDFS_OK;
+}
+
+/**
+ * Construct an array of component strings from a FedFsPathName
+ *
+ * @param fpath FedFsPathName from which to construct path
+ * @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().
+ *
+ * NB: The use of fixed constants for NAME_MAX and PATH_MAX are required
+ * here because, on the client side, the pathname likely does not
+ * exist, so pathconf(3) cannot be used.
+ */
+FedFsStatus
+nsdb_fedfspathname_to_path_array(FedFsPathName fpath, char ***path_array)
+{
+ char *component, **result;
+ FedFsPathComponent fcomp;
+ unsigned int i, len;
+ size_t length;
+
+ if (path_array == NULL) {
+ xlog(L_ERROR, "%s: Invalid argument", __func__);
+ return FEDFS_ERR_INVAL;
+ }
+
+ if (fpath.FedFsPathName_len == 0)
+ return nsdb_alloc_zero_component_pathname(path_array);
+
+ length = 0;
+ for (i = 0; i < fpath.FedFsPathName_len; i++) {
+ fcomp = fpath.FedFsPathName_val[i];
+ len = fcomp.utf8string_len;
+ component = fcomp.utf8string_val;
+
+ 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 (nsdb_strnchr(component, '/', len) != 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: FedFsPathName too long", __func__);
+ return FEDFS_ERR_NAMETOOLONG;
+ }
+ }
+
+ result = (char **)calloc(fpath.FedFsPathName_len + 1, sizeof(char *));
+ if (result == NULL) {
+ xlog(L_ERROR, "%s: Failed to allocate array",
+ __func__);
+ return FEDFS_ERR_SVRFAULT;
+ }
+
+ for (i = 0; i < fpath.FedFsPathName_len; i++) {
+ fcomp = fpath.FedFsPathName_val[i];
+ len = fcomp.utf8string_len;
+ component = fcomp.utf8string_val;
+
+ result[i] = strndup(component, (size_t)len);
+ if (result[i] == NULL) {
+ xlog(D_GENERAL, "%s: Failed to allocate "
+ "new pathname component", __func__);
+ nsdb_free_string_array(result);
+ return FEDFS_ERR_SVRFAULT;
+ }
+ }
+
+ *path_array = result;
+ return FEDFS_OK;
+}
Since the beginning, I've tried to stick with representing local pathnames using a C string, and converting to the array form only when I need to transmit the pathname off-system. That seemed more natural for operating on Linux, whose VFS is POSIX-style. However, if we want to allow creation of junctions that point to servers that don't necessarily use '/' as the pathname component separator, we will need a more general way to represent pathnames in struct fedfs_fsl and in other places. This commit adds pathname parsing functions in src/libnsdb/path.c that can handle a new pathname type, which is an array of character pointers. Each pointer is to a single component of the pathname. This data structure parallels FedFsPathName and the equivalent data structures in NFS's fs_locations4 and fs_locations_info4. Aside from exposing the richer fs_locations data structure to FedFS callers, this change moves an important part of pathname resolution policy out of libnsdb and into callers. It should also provide an opportunity for stronger and more consistent checks on incoming pathnames. During testing it was discovered that the old pathname parsing logic is full of bugs, and that these new routines may not be completely compatible. This may mean that reading some existing FSLs may result in errors, and therefore they must be replaced. However, the new routines are more likely to interoperate with other FedFS implementations. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> --- src/include/nsdb.h | 13 + src/libnsdb/path.c | 513 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 523 insertions(+), 3 deletions(-)