Rebase to OpenSolaris b103, in the process we are removing any code which did not...
[zfs.git] / zfs / lib / libnvpair / nvpair.c
index 9b8f51e..77891bf 100644 (file)
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-
+#pragma ident  "%Z%%M% %I%     %E% SMI"
 
 #include <sys/stropts.h>
 #include <sys/debug.h>
 
 #if defined(_KERNEL) && !defined(_BOOT)
 #include <sys/varargs.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
 #else
 #include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
 #include <strings.h>
 #endif
 
 #ifndef        offsetof
-#define        offsetof(s, m)  ((size_t)(&(((s *)0)->m)))
+#define        offsetof(s, m)          ((size_t)(&(((s *)0)->m)))
 #endif
-
+#define        skip_whitespace(p)      while ((*(p) == ' ') || (*(p) == '\t')) p++
 
 /*
  * nvpair.c - Provides kernel & userland interfaces for manipulating
@@ -204,7 +208,7 @@ nv_mem_free(nvpriv_t *nvp, void *buf, size_t size)
 static void
 nv_priv_init(nvpriv_t *priv, nv_alloc_t *nva, uint32_t stat)
 {
-       bzero(priv, sizeof (priv));
+       bzero(priv, sizeof (nvpriv_t));
 
        priv->nvp_nva = nva;
        priv->nvp_stat = stat;
@@ -398,6 +402,9 @@ i_validate_type_nelem(data_type_t type, uint_t nelem)
        case DATA_TYPE_STRING:
        case DATA_TYPE_HRTIME:
        case DATA_TYPE_NVLIST:
+#if !defined(_KERNEL)
+       case DATA_TYPE_DOUBLE:
+#endif
                if (nelem != 1)
                        return (EINVAL);
                break;
@@ -736,6 +743,11 @@ i_get_value_size(data_type_t type, const void *data, uint_t nelem)
        case DATA_TYPE_UINT64:
                value_sz = sizeof (uint64_t);
                break;
+#if !defined(_KERNEL)
+       case DATA_TYPE_DOUBLE:
+               value_sz = sizeof (double);
+               break;
+#endif
        case DATA_TYPE_STRING:
                if (data == NULL)
                        value_sz = 0;
@@ -1020,6 +1032,14 @@ nvlist_add_uint64(nvlist_t *nvl, const char *name, uint64_t val)
        return (nvlist_add_common(nvl, name, DATA_TYPE_UINT64, 1, &val));
 }
 
+#if !defined(_KERNEL)
+int
+nvlist_add_double(nvlist_t *nvl, const char *name, double val)
+{
+       return (nvlist_add_common(nvl, name, DATA_TYPE_DOUBLE, 1, &val));
+}
+#endif
+
 int
 nvlist_add_string(nvlist_t *nvl, const char *name, const char *val)
 {
@@ -1154,6 +1174,27 @@ nvpair_type(nvpair_t *nvp)
        return (NVP_TYPE(nvp));
 }
 
+int
+nvpair_type_is_array(nvpair_t *nvp)
+{
+       data_type_t type = NVP_TYPE(nvp);
+
+       if ((type == DATA_TYPE_BYTE_ARRAY) ||
+           (type == DATA_TYPE_UINT8_ARRAY) ||
+           (type == DATA_TYPE_INT16_ARRAY) ||
+           (type == DATA_TYPE_UINT16_ARRAY) ||
+           (type == DATA_TYPE_INT32_ARRAY) ||
+           (type == DATA_TYPE_UINT32_ARRAY) ||
+           (type == DATA_TYPE_INT64_ARRAY) ||
+           (type == DATA_TYPE_UINT64_ARRAY) ||
+           (type == DATA_TYPE_BOOLEAN_ARRAY) ||
+           (type == DATA_TYPE_STRING_ARRAY) ||
+           (type == DATA_TYPE_NVLIST_ARRAY))
+               return (1);
+       return (0);
+
+}
+
 static int
 nvpair_value_common(nvpair_t *nvp, data_type_t type, uint_t *nelem, void *data)
 {
@@ -1181,6 +1222,9 @@ nvpair_value_common(nvpair_t *nvp, data_type_t type, uint_t *nelem, void *data)
        case DATA_TYPE_INT64:
        case DATA_TYPE_UINT64:
        case DATA_TYPE_HRTIME:
+#if !defined(_KERNEL)
+       case DATA_TYPE_DOUBLE:
+#endif
                if (data == NULL)
                        return (EINVAL);
                bcopy(NVP_VALUE(nvp), data,
@@ -1317,6 +1361,14 @@ nvlist_lookup_uint64(nvlist_t *nvl, const char *name, uint64_t *val)
        return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT64, NULL, val));
 }
 
+#if !defined(_KERNEL)
+int
+nvlist_lookup_double(nvlist_t *nvl, const char *name, double *val)
+{
+       return (nvlist_lookup_common(nvl, name, DATA_TYPE_DOUBLE, NULL, val));
+}
+#endif
+
 int
 nvlist_lookup_string(nvlist_t *nvl, const char *name, char **val)
 {
@@ -1451,6 +1503,9 @@ nvlist_lookup_pairs(nvlist_t *nvl, int flag, ...)
                case DATA_TYPE_HRTIME:
                case DATA_TYPE_STRING:
                case DATA_TYPE_NVLIST:
+#if !defined(_KERNEL)
+               case DATA_TYPE_DOUBLE:
+#endif
                        val = va_arg(ap, void *);
                        ret = nvlist_lookup_common(nvl, name, type, NULL, val);
                        break;
@@ -1484,30 +1539,201 @@ nvlist_lookup_pairs(nvlist_t *nvl, int flag, ...)
        return (ret);
 }
 
-int
-nvlist_lookup_nvpair(nvlist_t *nvl, const char *name, nvpair_t **ret)
-{
-       nvpriv_t *priv;
-       nvpair_t *nvp;
-       i_nvp_t *curr;
-
-       if (name == NULL || nvl == NULL ||
-           (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL)
+/*
+ * Find the 'name'ed nvpair in the nvlist 'nvl'. If 'name' found, the function
+ * returns zero and a pointer to the matching nvpair is returned in '*ret'
+ * (given 'ret' is non-NULL). If 'sep' is specified then 'name' will penitrate
+ * multiple levels of embedded nvlists, with 'sep' as the separator. As an
+ * example, if sep is '.', name might look like: "a" or "a.b" or "a.c[3]" or
+ * "a.d[3].e[1]".  This matches the C syntax for array embed (for convience,
+ * code also supports "a.d[3]e[1]" syntax).
+ *
+ * If 'ip' is non-NULL and the last name component is an array, return the
+ * value of the "...[index]" array index in *ip. For an array reference that
+ * is not indexed, *ip will be returned as -1. If there is a syntax error in
+ * 'name', and 'ep' is non-NULL then *ep will be set to point to the location
+ * inside the 'name' string where the syntax error was detected.
+ */
+static int
+nvlist_lookup_nvpair_ei_sep(nvlist_t *nvl, const char *name, const char sep,
+    nvpair_t **ret, int *ip, char **ep)
+{
+       nvpair_t        *nvp;
+       const char      *np;
+       char            *sepp;
+       char            *idxp, *idxep;
+       nvlist_t        **nva;
+       long            idx;
+       int             n;
+
+       if (ip)
+               *ip = -1;                       /* not indexed */
+       if (ep)
+               *ep = NULL;
+
+       if ((nvl == NULL) || (name == NULL))
                return (EINVAL);
 
-       if (!(nvl->nvl_nvflag & NV_UNIQUE_NAME))
-               return (ENOTSUP);
+       /* step through components of name */
+       for (np = name; np && *np; np = sepp) {
+               /* ensure unique names */
+               if (!(nvl->nvl_nvflag & NV_UNIQUE_NAME))
+                       return (ENOTSUP);
 
-       for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) {
-               nvp = &curr->nvi_nvp;
+               /* skip white space */
+               skip_whitespace(np);
+               if (*np == 0)
+                       break;
 
-               if (strcmp(name, NVP_NAME(nvp)) == 0) {
-                       *ret = nvp;
-                       return (0);
+               /* set 'sepp' to end of current component 'np' */
+               if (sep)
+                       sepp = strchr(np, sep);
+               else
+                       sepp = NULL;
+
+               /* find start of next "[ index ]..." */
+               idxp = strchr(np, '[');
+
+               /* if sepp comes first, set idxp to NULL */
+               if (sepp && idxp && (sepp < idxp))
+                       idxp = NULL;
+
+               /*
+                * At this point 'idxp' is set if there is an index
+                * expected for the current component.
+                */
+               if (idxp) {
+                       /* set 'n' to length of current 'np' name component */
+                       n = idxp++ - np;
+
+                       /* keep sepp up to date for *ep use as we advance */
+                       skip_whitespace(idxp);
+                       sepp = idxp;
+
+                       /* determine the index value */
+#if defined(_KERNEL) && !defined(_BOOT)
+                       if (ddi_strtol(idxp, &idxep, 0, &idx))
+                               goto fail;
+#else
+                       idx = strtol(idxp, &idxep, 0);
+#endif
+                       if (idxep == idxp)
+                               goto fail;
+
+                       /* keep sepp up to date for *ep use as we advance */
+                       sepp = idxep;
+
+                       /* skip white space index value and check for ']' */
+                       skip_whitespace(sepp);
+                       if (*sepp++ != ']')
+                               goto fail;
+
+                       /* for embedded arrays, support C syntax: "a[1].b" */
+                       skip_whitespace(sepp);
+                       if (sep && (*sepp == sep))
+                               sepp++;
+               } else if (sepp) {
+                       n = sepp++ - np;
+               } else {
+                       n = strlen(np);
+               }
+
+               /* trim trailing whitespace by reducing length of 'np' */
+               if (n == 0)
+                       goto fail;
+               for (n--; (np[n] == ' ') || (np[n] == '\t'); n--)
+                       ;
+               n++;
+
+               /* skip whitespace, and set sepp to NULL if complete */
+               if (sepp) {
+                       skip_whitespace(sepp);
+                       if (*sepp == 0)
+                               sepp = NULL;
+               }
+
+               /*
+                * At this point:
+                * o  'n' is the length of current 'np' component.
+                * o  'idxp' is set if there was an index, and value 'idx'.
+                * o  'sepp' is set to the beginning of the next component,
+                *    and set to NULL if we have no more components.
+                *
+                * Search for nvpair with matching component name.
+                */
+               for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
+                   nvp = nvlist_next_nvpair(nvl, nvp)) {
+
+                       /* continue if no match on name */
+                       if (strncmp(np, nvpair_name(nvp), n) ||
+                           (strlen(nvpair_name(nvp)) != n))
+                               continue;
+
+                       /* if indexed, verify type is array oriented */
+                       if (idxp && !nvpair_type_is_array(nvp))
+                               goto fail;
+
+                       /*
+                        * Full match found, return nvp and idx if this
+                        * was the last component.
+                        */
+                       if (sepp == NULL) {
+                               if (ret)
+                                       *ret = nvp;
+                               if (ip && idxp)
+                                       *ip = (int)idx; /* return index */
+                               return (0);             /* found */
+                       }
+
+                       /*
+                        * More components: current match must be
+                        * of DATA_TYPE_NVLIST or DATA_TYPE_NVLIST_ARRAY
+                        * to support going deeper.
+                        */
+                       if (nvpair_type(nvp) == DATA_TYPE_NVLIST) {
+                               nvl = EMBEDDED_NVL(nvp);
+                               break;
+                       } else if (nvpair_type(nvp) == DATA_TYPE_NVLIST_ARRAY) {
+                               (void) nvpair_value_nvlist_array(nvp,
+                                   &nva, (uint_t *)&n);
+                               if ((n < 0) || (idx >= n))
+                                       goto fail;
+                               nvl = nva[idx];
+                               break;
+                       }
+
+                       /* type does not support more levels */
+                       goto fail;
                }
+               if (nvp == NULL)
+                       goto fail;              /* 'name' not found */
+
+               /* search for match of next component in embedded 'nvl' list */
        }
 
-       return (ENOENT);
+fail:  if (ep && sepp)
+               *ep = sepp;
+       return (EINVAL);
+}
+
+/*
+ * Return pointer to nvpair with specified 'name'.
+ */
+int
+nvlist_lookup_nvpair(nvlist_t *nvl, const char *name, nvpair_t **ret)
+{
+       return (nvlist_lookup_nvpair_ei_sep(nvl, name, 0, ret, NULL, NULL));
+}
+
+/*
+ * Determine if named nvpair exists in nvlist (use embedded separator of '.'
+ * and return array index).  See nvlist_lookup_nvpair_ei_sep for more detailed
+ * description.
+ */
+int nvlist_lookup_nvpair_embedded_index(nvlist_t *nvl,
+    const char *name, nvpair_t **ret, int *ip, char **ep)
+{
+       return (nvlist_lookup_nvpair_ei_sep(nvl, name, '.', ret, ip, ep));
 }
 
 boolean_t
@@ -1591,6 +1817,14 @@ nvpair_value_uint64(nvpair_t *nvp, uint64_t *val)
        return (nvpair_value_common(nvp, DATA_TYPE_UINT64, NULL, val));
 }
 
+#if !defined(_KERNEL)
+int
+nvpair_value_double(nvpair_t *nvp, double *val)
+{
+       return (nvpair_value_common(nvp, DATA_TYPE_DOUBLE, NULL, val));
+}
+#endif
+
 int
 nvpair_value_string(nvpair_t *nvp, char **val)
 {
@@ -2728,7 +2962,11 @@ nvs_xdr_nvp_op(nvstream_t *nvs, nvpair_t *nvp)
                 */
                ret = xdr_longlong_t(xdr, (void *)buf);
                break;
-
+#if !defined(_KERNEL)
+       case DATA_TYPE_DOUBLE:
+               ret = xdr_double(xdr, (void *)buf);
+               break;
+#endif
        case DATA_TYPE_STRING:
                ret = xdr_string(xdr, &buf, buflen - 1);
                break;
@@ -2834,6 +3072,9 @@ nvs_xdr_nvp_size(nvstream_t *nvs, nvpair_t *nvp, size_t *size)
        case DATA_TYPE_INT64:
        case DATA_TYPE_UINT64:
        case DATA_TYPE_HRTIME:
+#if !defined(_KERNEL)
+       case DATA_TYPE_DOUBLE:
+#endif
                nvp_sz += 8;
                break;