ldb: introduce ldb_unpack_data_withlist to unpack partial list of attributes
authorMatthieu Patou <mat@matws.net>
Fri, 28 Dec 2012 05:38:29 +0000 (21:38 -0800)
committerAndrew Bartlett <abartlet@samba.org>
Thu, 17 Dec 2015 02:23:20 +0000 (03:23 +0100)
When provided with non NULL list ldb_unpack_data_withlist will only
unpack attributes that are specified in the list (+ distinguished name)
ldb_unpack_data is changed to call ldb_unpack_data_withlist behind the
scene.

(for modifications found by testing, and re-indentation requested in review)
Signed-off-by: Adrian Cochrane <adrianc@catalyst.net.nz>
Sadly a signed-off-by was not available from Matthieu Patou for the original
version of this patch posted to samba-technical for comment, so instead:

(for supervision of Adrian)
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
BUG: https://bugzilla.samba.org/show_bug.cgi?id=11602

lib/ldb/common/ldb_pack.c
lib/ldb/include/ldb_module.h

index 4382d5b1be07beaf86c938e34472d5a819d82342..5524542dcd9255ab9c9492515e2a75bf0091b05d 100644 (file)
@@ -146,20 +146,52 @@ int ldb_pack_data(struct ldb_context *ldb,
        return 0;
 }
 
+static bool ldb_consume_element_data(uint8_t **pp, unsigned int *premaining)
+{
+       unsigned int remaining = *premaining;
+       uint8_t *p = *pp;
+       uint32_t num_values = pull_uint32(p, 0);
+       uint32_t len;
+       int j;
+
+       p += 4;
+       remaining -= 4;
+       for (j = 0; j < num_values; j++) {
+               len = pull_uint32(p, 0);
+               if (len > remaining - 5) {
+                       return false;
+               }
+               remaining -= len + 4 + 1;
+               p += len + 4 + 1;
+       }
+
+       *premaining = remaining;
+       *pp = p;
+       return true;
+}
 /*
   unpack a ldb message from a linear buffer in ldb_val
 
   Free with ldb_unpack_data_free()
 */
-int ldb_unpack_data(struct ldb_context *ldb,
-                   const struct ldb_val *data,
-                   struct ldb_message *message)
+int ldb_unpack_data_withlist(struct ldb_context *ldb,
+                            const struct ldb_val *data,
+                            struct ldb_message *message,
+                            const char * const *list,
+                            unsigned int list_size,
+                            unsigned int *nb_elements_in_db)
 {
        uint8_t *p;
        unsigned int remaining;
        unsigned int i, j;
        unsigned format;
+       unsigned int nelem = 0;
        size_t len;
+       unsigned int found = 0;
+
+       if (list == NULL) {
+               list_size = 0;
+       }
 
        message->elements = NULL;
 
@@ -172,6 +204,9 @@ int ldb_unpack_data(struct ldb_context *ldb,
        format = pull_uint32(p, 0);
        message->num_elements = pull_uint32(p, 4);
        p += 8;
+       if (nb_elements_in_db) {
+               *nb_elements_in_db = message->num_elements;
+       }
 
        remaining = data->length - 8;
 
@@ -209,7 +244,8 @@ int ldb_unpack_data(struct ldb_context *ldb,
                goto failed;
        }
 
-       message->elements = talloc_array(message, struct ldb_message_element, message->num_elements);
+       message->elements = talloc_array(message, struct ldb_message_element,
+                                        message->num_elements);
        if (!message->elements) {
                errno = ENOMEM;
                goto failed;
@@ -219,6 +255,9 @@ int ldb_unpack_data(struct ldb_context *ldb,
               message->num_elements * sizeof(struct ldb_message_element));
 
        for (i=0;i<message->num_elements;i++) {
+               const char *attr = NULL;
+               struct ldb_message_element *element = NULL;
+
                if (remaining < 10) {
                        errno = EIO;
                        goto failed;
@@ -232,51 +271,93 @@ int ldb_unpack_data(struct ldb_context *ldb,
                        errno = EIO;
                        goto failed;
                }
-               message->elements[i].flags = 0;
-               message->elements[i].name = talloc_strndup(message->elements, (char *)p, len);
-               if (message->elements[i].name == NULL) {
+               attr = (char *)p;
+               /*
+                * This is a bit expensive but normally the list is pretty small
+                * also the cost of freeing unused attributes is quite important
+                * and can dwarf the cost of looping
+                */
+               if (list_size != 0) {
+                       bool keep = false;
+                       int h;
+
+                       /*
+                        * We know that p has a \0 terminator before the
+                        * end of the buffer due to the check above.
+                        */
+                       for (h = 0; h < list_size && found < list_size; h++) {
+                               if (ldb_attr_cmp(attr, list[h]) == 0) {
+                                       keep = true;
+                                       found++;
+                                       break;
+                               }
+                       }
+
+                       if (!keep) {
+                               remaining -= len + 1;
+                               p += len + 1;
+                               if (!ldb_consume_element_data(&p, &remaining)) {
+                                       errno = EIO;
+                                       goto failed;
+                               }
+                               continue;
+                       }
+               }
+               element = &message->elements[nelem];
+               element->name = talloc_strndup(message->elements, (char *)p, len);
+               if (element->name == NULL) {
                        errno = ENOMEM;
                        goto failed;
                }
+               element->flags = 0;
                remaining -= len + 1;
                p += len + 1;
-               message->elements[i].num_values = pull_uint32(p, 0);
-               message->elements[i].values = NULL;
-               if (message->elements[i].num_values != 0) {
-                       message->elements[i].values = talloc_array(message->elements,
-                                                                    struct ldb_val,
-                                                                    message->elements[i].num_values);
-                       if (!message->elements[i].values) {
+               element->num_values = pull_uint32(p, 0);
+               element->values = NULL;
+               if (element->num_values != 0) {
+                       element->values = talloc_array(message->elements,
+                                                      struct ldb_val,
+                                                      element->num_values);
+                       if (!element->values) {
                                errno = ENOMEM;
                                goto failed;
                        }
                }
                p += 4;
                remaining -= 4;
-               for (j=0;j<message->elements[i].num_values;j++) {
+               for (j = 0; j < element->num_values; j++) {
                        len = pull_uint32(p, 0);
                        if (len > remaining-5) {
                                errno = EIO;
                                goto failed;
                        }
 
-                       message->elements[i].values[j].length = len;
-                       message->elements[i].values[j].data = talloc_size(message->elements[i].values, len+1);
-                       if (message->elements[i].values[j].data == NULL) {
+                       element->values[j].length = len;
+                       element->values[j].data = talloc_size(element->values, len+1);
+                       if (element->values[j].data == NULL) {
                                errno = ENOMEM;
                                goto failed;
                        }
-                       memcpy(message->elements[i].values[j].data, p+4, len);
-                       message->elements[i].values[j].data[len] = 0;
+                       memcpy(element->values[j].data, p + 4,
+                              len);
+                       element->values[j].data[len] = 0;
 
                        remaining -= len+4+1;
                        p += len+4+1;
                }
+               nelem++;
        }
+       /*
+        * Adapt the number of elements to the real number of unpacked elements,
+        * it means that we overallocated elements array, I guess it's not that
+        * bad.
+        */
+       message->num_elements = nelem;
 
        if (remaining != 0) {
                ldb_debug(ldb, LDB_DEBUG_ERROR,
-                         "Error: %d bytes unread in ldb_unpack_data", remaining);
+                         "Error: %d bytes unread in ldb_unpack_data_withlist",
+                         remaining);
        }
 
        return 0;
@@ -285,3 +366,10 @@ failed:
        talloc_free(message->elements);
        return -1;
 }
+
+int ldb_unpack_data(struct ldb_context *ldb,
+                   const struct ldb_val *data,
+                   struct ldb_message *message)
+{
+       return ldb_unpack_data_withlist(ldb, data, message, NULL, 0, NULL);
+}
index f00ba50dc0eebc7ea7c0c3189690d2624987a2e7..7176721655f1640f79f54e9bd13c39e38156bfab 100644 (file)
@@ -390,6 +390,12 @@ int ldb_register_extended_match_rule(struct ldb_context *ldb,
 int ldb_pack_data(struct ldb_context *ldb,
                  const struct ldb_message *message,
                  struct ldb_val *data);
+int ldb_unpack_data_withlist(struct ldb_context *ldb,
+                            const struct ldb_val *data,
+                            struct ldb_message *message,
+                            const char* const * list,
+                            unsigned int list_size,
+                            unsigned int *nb_attributes_indb);
 int ldb_unpack_data(struct ldb_context *ldb,
                    const struct ldb_val *data,
                    struct ldb_message *message);