The branch, master has been updated via a666a99e4dc ldb: ldbdump key and pack format version comments via 365838311b1 ldb: ldb_key_value_test fix via 64cdd0383f9 ldb: removing unnecessary module pointer via bea24253f08 ldb: Release ldb 2.0.2 via 8c0724fa10b ldb: pack function for new pack format via 38feff07312 ldb: unpack function for new pack format via 5bf6f0ae327 ldb: replacing length increments with constants in pack via df1f8832047 ldb: push and pull macros for pack format via 474e5552322 ldb: baseinfo pack format check on init from 3e6661fd73b s4 librpc rpc pyrpc: Fix flapping dcerpc.bare tests
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit a666a99e4dc594bc153cd26b24cddd547c1cc750 Author: Aaron Haslett <aaronhasl...@catalyst.net.nz> Date: Mon May 20 16:19:51 2019 +1200 ldb: ldbdump key and pack format version comments For testing we need to know the actual KV level key of records and each record's pack format version. This patch makes ldbdump add comments with that info. We will parse it out in python tests. Signed-off-by: Aaron Haslett <aaronhasl...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Gary Lockyer <g...@catalyst.net.nz> Autobuild-User(master): Andrew Bartlett <abart...@samba.org> Autobuild-Date(master): Wed May 22 05:58:17 UTC 2019 on sn-devel-184 commit 365838311b139b8163cc8f41b4cf2058809ae437 Author: Aaron Haslett <aaronhasl...@catalyst.net.nz> Date: Fri May 17 13:27:20 2019 +1200 ldb: ldb_key_value_test fix In future commits we'll be adding more logging to LDB, which breaks the ldb_key_value_test suite. By removing the debug handler, a bug involving an expired debug_string variable being written to is avoided. Signed-off-by: Aaron Haslett <aaronhasl...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Gary Lockyer <g...@catalyst.net.nz> commit 64cdd0383f9083d84d38dd19168b013906ca9cf8 Author: Aaron Haslett <aaronhasl...@catalyst.net.nz> Date: Thu May 16 10:53:38 2019 +1200 ldb: removing unnecessary module pointer We want to reuse the reindex context struct for repacking, but it has an unnecessary module pointer on it. Turns out the existing code doesn't need it either, so this patch deletes the pointer. Signed-off-by: Aaron Haslett <aaronhasl...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Gary Lockyer <g...@catalyst.net.nz> commit bea24253f08d6aa6534d7743c7af0a5202446154 Author: Aaron Haslett <aaronhasl...@catalyst.net.nz> Date: Tue May 21 12:34:38 2019 +1200 ldb: Release ldb 2.0.2 * Checking pack format is version 1 and erroring if not (will change soon) * Pack format routines for unpack and pack version 2 (but not used) * Test fixes for issues caused by upcoming repack functionality for upgrade * Making ldbdump print out pack format info and keys so we have low level visibility for testing in python Signed-off-by: Aaron Haslett <aaronhasl...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Gary Lockyer <g...@catalyst.net.nz> commit 8c0724fa10bc32980e5378114e0611b049924f74 Author: Aaron Haslett <aaronhasl...@catalyst.net.nz> Date: Fri May 10 18:10:55 2019 +1200 ldb: pack function for new pack format Pack function for new pack format with values separated from other data so that while unpacking, the value section (which is probably large) doesn't have to be loaded into cache/memory. The new format is disabled for now. Two tests are added that operate on a detailed binary breakdown of the new format. NOTE: Configure with --abi-check-disable to build this commit. This patch is part of a set of LDB ABI changes, and the version update is done on the last commit. Signed-off-by: Aaron Haslett <aaronhasl...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Gary Lockyer <g...@catalyst.net.nz> commit 38feff073120ea87c100c05d8c9c3f6f771e5029 Author: Aaron Haslett <aaronhasl...@catalyst.net.nz> Date: Wed May 1 13:36:36 2019 +1200 ldb: unpack function for new pack format Unpack function for new pack format with values separated from other data so that while unpacking, the value section (which is probably large) doesn't have to be loaded into cache/memory. Additionally, width of length field can now vary per-element to save space. The old unpack routine is still present and is called if the old pack format version number is found. LDB torture suite is modified to run relevant tests on both old and new pack format. NOTE: Configure with --abi-check-disable to build this commit. This patch is part of a set of LDB ABI changes, and the version update is done on the last commit. Signed-off-by: Aaron Haslett <aaronhasl...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Gary Lockyer <g...@catalyst.net.nz> commit 5bf6f0ae3271cb1a24416523f1132b09d998adf3 Author: Aaron Haslett <aaronhasl...@catalyst.net.nz> Date: Tue May 21 15:18:10 2019 +1200 ldb: replacing length increments with constants in pack Since we're about to introduce a new packing format, it's a good time to improve our code style and change some magic numbers into explicit constants. Signed-off-by: Aaron Haslett <aaronhasl...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Gary Lockyer <g...@catalyst.net.nz> commit df1f883204722a947c52497aaa79d75444a24b63 Author: Aaron Haslett <aaronhasl...@catalyst.net.nz> Date: Fri May 17 16:34:52 2019 +1200 ldb: push and pull macros for pack format Replacing push and pull functions (which may cause issues with Undefined Sanitizer) with Andreas Schneider's excellent macros which are a work in progress and not yet merged into master. Once his work is upstream, I'll rebase and change this code to import his headers. Signed-off-by: Aaron Haslett <aaronhasl...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Gary Lockyer <g...@catalyst.net.nz> commit 474e55523224430781ed22aa2d0c8a474306e794 Author: Aaron Haslett <aaronhasl...@catalyst.net.nz> Date: Fri May 10 18:10:51 2019 +1200 ldb: baseinfo pack format check on init We will be adding a new packing format in forthcoming commits and there may be more versions in the future. We need to make sure the database contains records in a format we know how to read and write. Done by fetching the @BASEINFO record and reading the first 4 bytes which contain the packing format version. NOTE: Configure with --abi-check-disable to build this commit. This patch is part of a set of LDB ABI changes, and the version update is done on the last commit. Signed-off-by: Aaron Haslett <aaronhasl...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Gary Lockyer <g...@catalyst.net.nz> ----------------------------------------------------------------------- Summary of changes: lib/ldb/ABI/{ldb-2.0.1.sigs => ldb-2.0.2.sigs} | 3 +- ...yldb-util-1.1.10.sigs => pyldb-util-2.0.2.sigs} | 0 lib/ldb/common/ldb_pack.c | 845 +++++++++++++++++++-- lib/ldb/include/ldb_module.h | 13 +- lib/ldb/ldb_key_value/ldb_kv.c | 5 +- lib/ldb/ldb_key_value/ldb_kv.h | 2 +- lib/ldb/ldb_key_value/ldb_kv_cache.c | 37 + lib/ldb/ldb_key_value/ldb_kv_index.c | 5 +- lib/ldb/ldb_tdb/ldb_tdb.c | 2 +- lib/ldb/tests/ldb_mod_op_test.c | 2 + lib/ldb/tools/ldbdump.c | 24 + lib/ldb/wscript | 2 +- source4/torture/ldb/ldb.c | 431 ++++++++++- 13 files changed, 1257 insertions(+), 114 deletions(-) copy lib/ldb/ABI/{ldb-2.0.1.sigs => ldb-2.0.2.sigs} (99%) copy lib/ldb/ABI/{pyldb-util-1.1.10.sigs => pyldb-util-2.0.2.sigs} (100%) Changeset truncated at 500 lines: diff --git a/lib/ldb/ABI/ldb-2.0.1.sigs b/lib/ldb/ABI/ldb-2.0.2.sigs similarity index 99% copy from lib/ldb/ABI/ldb-2.0.1.sigs copy to lib/ldb/ABI/ldb-2.0.2.sigs index f782d73afb1..5fc5560ee21 100644 --- a/lib/ldb/ABI/ldb-2.0.1.sigs +++ b/lib/ldb/ABI/ldb-2.0.2.sigs @@ -197,7 +197,7 @@ ldb_next_request: int (struct ldb_module *, struct ldb_request *) ldb_next_start_trans: int (struct ldb_module *) ldb_op_default_callback: int (struct ldb_request *, struct ldb_reply *) ldb_options_find: const char *(struct ldb_context *, const char **, const char *) -ldb_pack_data: int (struct ldb_context *, const struct ldb_message *, struct ldb_val *) +ldb_pack_data: int (struct ldb_context *, const struct ldb_message *, struct ldb_val *, uint32_t) ldb_parse_control_from_string: struct ldb_control *(struct ldb_context *, TALLOC_CTX *, const char *) ldb_parse_control_strings: struct ldb_control **(struct ldb_context *, TALLOC_CTX *, const char **) ldb_parse_tree: struct ldb_parse_tree *(TALLOC_CTX *, const char *) @@ -269,6 +269,7 @@ ldb_transaction_prepare_commit: int (struct ldb_context *) ldb_transaction_start: int (struct ldb_context *) ldb_unpack_data: int (struct ldb_context *, const struct ldb_val *, struct ldb_message *) ldb_unpack_data_flags: int (struct ldb_context *, const struct ldb_val *, struct ldb_message *, unsigned int) +ldb_unpack_get_format: int (const struct ldb_val *, uint32_t *) ldb_val_dup: struct ldb_val (TALLOC_CTX *, const struct ldb_val *) ldb_val_equal_exact: int (const struct ldb_val *, const struct ldb_val *) ldb_val_map_local: struct ldb_val (struct ldb_module *, void *, const struct ldb_map_attribute *, const struct ldb_val *) diff --git a/lib/ldb/ABI/pyldb-util-1.1.10.sigs b/lib/ldb/ABI/pyldb-util-2.0.2.sigs similarity index 100% copy from lib/ldb/ABI/pyldb-util-1.1.10.sigs copy to lib/ldb/ABI/pyldb-util-2.0.2.sigs diff --git a/lib/ldb/common/ldb_pack.c b/lib/ldb/common/ldb_pack.c index 5360a36cccc..9d87a10b9f1 100644 --- a/lib/ldb/common/ldb_pack.c +++ b/lib/ldb/common/ldb_pack.c @@ -33,27 +33,58 @@ #include "ldb_private.h" -/* change this if the data format ever changes */ -#define LDB_PACKING_FORMAT 0x26011967 - -/* old packing formats */ -#define LDB_PACKING_FORMAT_NODN 0x26011966 - -/* use a portable integer format */ -static void put_uint32(uint8_t *p, int ofs, unsigned int val) -{ - p += ofs; - p[0] = val&0xFF; - p[1] = (val>>8) & 0xFF; - p[2] = (val>>16) & 0xFF; - p[3] = (val>>24) & 0xFF; -} - -static unsigned int pull_uint32(uint8_t *p, int ofs) -{ - p += ofs; - return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); -} +/* + * These macros are from byte_array.h via libssh + * TODO: This will be replaced with use of the byte_array.h header when it + * becomes available. + * + * Macros for handling integer types in byte arrays + * + * This file is originally from the libssh.org project + * + * Copyright (c) 2018 Andreas Schneider <a...@cryptomilk.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#define _DATA_BYTE_CONST(data, pos) \ + ((uint8_t)(((const uint8_t *)(data))[(pos)])) +#define PULL_LE_U8(data, pos) \ + (_DATA_BYTE_CONST(data, pos)) +#define PULL_LE_U16(data, pos) \ + ((uint16_t)PULL_LE_U8(data, pos) |\ + ((uint16_t)(PULL_LE_U8(data, (pos) + 1))) << 8) +#define PULL_LE_U32(data, pos) \ + ((uint32_t)(PULL_LE_U16(data, pos) |\ + ((uint32_t)PULL_LE_U16(data, (pos) + 2)) << 16)) + +#define _DATA_BYTE(data, pos) \ + (((uint8_t *)(data))[(pos)]) +#define PUSH_LE_U8(data, pos, val) \ + (_DATA_BYTE(data, pos) = ((uint8_t)(val))) +#define PUSH_LE_U16(data, pos, val) \ + (PUSH_LE_U8((data), (pos), (uint8_t)((uint16_t)(val) & 0xff)),\ + PUSH_LE_U8((data), (pos) + 1,\ + (uint8_t)((uint16_t)(val) >> 8))) +#define PUSH_LE_U32(data, pos, val) \ + (PUSH_LE_U16((data), (pos), (uint16_t)((uint32_t)(val) & 0xffff)),\ + PUSH_LE_U16((data), (pos) + 2, (uint16_t)((uint32_t)(val) >> 16))) + +#define U32_LEN 4 +#define U16_LEN 2 +#define U8_LEN 1 +#define NULL_PAD_BYTE_LEN 1 static int attribute_storable_values(const struct ldb_message_element *el) { @@ -64,17 +95,9 @@ static int attribute_storable_values(const struct ldb_message_element *el) return el->num_values; } -/* - pack a ldb message into a linear buffer in a ldb_val - - note that this routine avoids saving elements with zero values, - as these are equivalent to having no element - - caller frees the data buffer after use -*/ -int ldb_pack_data(struct ldb_context *ldb, - const struct ldb_message *message, - struct ldb_val *data) +static int ldb_pack_data_v1(struct ldb_context *ldb, + const struct ldb_message *message, + struct ldb_val *data) { unsigned int i, j, real_elements=0; size_t size, dn_len, attr_len, value_len; @@ -89,9 +112,7 @@ int ldb_pack_data(struct ldb_context *ldb, } /* work out how big it needs to be */ - size = 8; - - size += 1; + size = U32_LEN * 2 + NULL_PAD_BYTE_LEN; dn_len = strlen(dn); if (size + dn_len < size) { @@ -111,11 +132,11 @@ int ldb_pack_data(struct ldb_context *ldb, real_elements++; - if (size + 5 < size) { + if (size + U32_LEN + NULL_PAD_BYTE_LEN < size) { errno = ENOMEM; return -1; } - size += 5; + size += U32_LEN + NULL_PAD_BYTE_LEN; attr_len = strlen(message->elements[i].name); if (size + attr_len < size) { @@ -125,11 +146,11 @@ int ldb_pack_data(struct ldb_context *ldb, size += attr_len; for (j=0;j<message->elements[i].num_values;j++) { - if (size + 5 < size) { + if (size + U32_LEN + NULL_PAD_BYTE_LEN < size) { errno = ENOMEM; return -1; } - size += 5; + size += U32_LEN + NULL_PAD_BYTE_LEN; value_len = message->elements[i].values[j].length; if (size + value_len < size) { @@ -149,31 +170,35 @@ int ldb_pack_data(struct ldb_context *ldb, data->length = size; p = data->data; - put_uint32(p, 0, LDB_PACKING_FORMAT); - put_uint32(p, 4, real_elements); - p += 8; + PUSH_LE_U32(p, 0, LDB_PACKING_FORMAT); + p += U32_LEN; + PUSH_LE_U32(p, 0, real_elements); + p += U32_LEN; /* the dn needs to be packed so we can be case preserving while hashing on a case folded dn */ len = dn_len; - memcpy(p, dn, len+1); - p += len + 1; + memcpy(p, dn, len+NULL_PAD_BYTE_LEN); + p += len + NULL_PAD_BYTE_LEN; for (i=0;i<message->num_elements;i++) { if (attribute_storable_values(&message->elements[i]) == 0) { continue; } len = strlen(message->elements[i].name); - memcpy(p, message->elements[i].name, len+1); - p += len + 1; - put_uint32(p, 0, message->elements[i].num_values); - p += 4; + memcpy(p, message->elements[i].name, len+NULL_PAD_BYTE_LEN); + p += len + NULL_PAD_BYTE_LEN; + PUSH_LE_U32(p, 0, message->elements[i].num_values); + p += U32_LEN; for (j=0;j<message->elements[i].num_values;j++) { - put_uint32(p, 0, message->elements[i].values[j].length); - memcpy(p+4, message->elements[i].values[j].data, + PUSH_LE_U32(p, 0, + message->elements[i].values[j].length); + p += U32_LEN; + memcpy(p, message->elements[i].values[j].data, message->elements[i].values[j].length); - p[4+message->elements[i].values[j].length] = 0; - p += 4 + message->elements[i].values[j].length + 1; + p[message->elements[i].values[j].length] = 0; + p += message->elements[i].values[j].length + + NULL_PAD_BYTE_LEN; } } @@ -181,21 +206,343 @@ int ldb_pack_data(struct ldb_context *ldb, } /* - * Unpack a ldb message from a linear buffer in ldb_val + * New pack version designed based on performance profiling of version 1. + * The approach is to separate value data from the rest of the record's data. + * This improves performance because value data is not needed during unpacking + * or filtering of the message's attribute list. During filtering we only copy + * attributes which are present in the attribute list, however at the parse + * stage we need to point to all attributes as they may be referenced in the + * search expression. + * With this new format, we don't lose time loading data (eg via + * talloc_memdup()) that is never needed (for the vast majority of attributes + * are are never found in either the search expression or attribute list). + * Additional changes include adding a canonicalized DN (for later + * optimizations) and variable width length fields for faster unpacking. + * The pack and unpack performance improvement is tested in the torture + * test torture_ldb_pack_format_perf. * - * Providing a list of attributes to this function allows selective unpacking. - * Giving a NULL list (or a list_size of 0) unpacks all the attributes. + * Layout: + * + * Version (4 bytes) + * Number of Elements (4 bytes) + * DN length (4 bytes) + * DN with null terminator (DN length + 1 bytes) + * Canonicalized DN length (4 bytes) + * Canonicalized DN with null terminator (Canonicalized DN length + 1 bytes) + * Number of bytes from here to value data section (4 bytes) + * # For each element: + * Element name length (4 bytes) + * Element name with null terminator (Element name length + 1 bytes) + * Number of values (4 bytes) + * Width of value lengths + * # For each value: + * Value data length (#bytes given by width field above) + * # For each element: + * # For each value: + * Value data (#bytes given by corresponding length above) */ -int ldb_unpack_data_flags(struct ldb_context *ldb, - const struct ldb_val *data, - struct ldb_message *message, - unsigned int flags) +static int ldb_pack_data_v2(struct ldb_context *ldb, + const struct ldb_message *message, + struct ldb_val *data) +{ + unsigned int i, j, real_elements=0; + size_t size, dn_len, dn_canon_len, attr_len, value_len; + const char *dn, *dn_canon; + uint8_t *p, *q; + size_t len; + size_t max_val_len; + uint8_t val_len_width; + + /* + * First half of this function will calculate required size for + * packed data. Initial size is 20 = 5 * 4. 5 fixed fields are: + * version, num elements, dn len, canon dn len, attr section len + */ + size = U32_LEN * 5; + + /* + * Get linearized and canonicalized form of the DN and add the lengths + * of each to size, plus 1 for null terminator. + */ + dn = ldb_dn_get_linearized(message->dn); + if (dn == NULL) { + errno = ENOMEM; + return -1; + } + + dn_len = strlen(dn) + NULL_PAD_BYTE_LEN; + if (size + dn_len < size) { + errno = ENOMEM; + return -1; + } + size += dn_len; + + if (ldb_dn_is_special(message->dn)) { + dn_canon_len = NULL_PAD_BYTE_LEN; + dn_canon = discard_const_p(char, "\0"); + } else { + dn_canon = ldb_dn_canonical_string(message->dn, message->dn); + if (dn_canon == NULL) { + errno = ENOMEM; + return -1; + } + + dn_canon_len = strlen(dn_canon) + NULL_PAD_BYTE_LEN; + if (size + dn_canon_len < size) { + errno = ENOMEM; + return -1; + } + } + size += dn_canon_len; + + /* Add the size required by each element */ + for (i=0;i<message->num_elements;i++) { + if (attribute_storable_values(&message->elements[i]) == 0) { + continue; + } + + real_elements++; + + /* + * Add length of element name + 9 for: + * 1 for null terminator + * 4 for element name length field + * 4 for number of values field + */ + attr_len = strlen(message->elements[i].name); + if (size + attr_len + U32_LEN * 2 + NULL_PAD_BYTE_LEN < size) { + errno = ENOMEM; + return -1; + } + size += attr_len + U32_LEN * 2 + NULL_PAD_BYTE_LEN; + + /* + * Find the max value length, so we can calculate the width + * required for the value length fields. + */ + max_val_len = 0; + for (j=0;j<message->elements[i].num_values;j++) { + value_len = message->elements[i].values[j].length; + if (value_len > max_val_len) { + max_val_len = value_len; + } + + if (size + value_len + NULL_PAD_BYTE_LEN < size) { + errno = ENOMEM; + return -1; + } + size += value_len + NULL_PAD_BYTE_LEN; + } + + if (max_val_len <= UCHAR_MAX) { + val_len_width = U8_LEN; + } else if (max_val_len <= USHRT_MAX) { + val_len_width = U16_LEN; + } else if (max_val_len <= UINT_MAX) { + val_len_width = U32_LEN; + } else { + errno = EMSGSIZE; + return -1; + } + + /* Total size required for val lengths (re-using variable) */ + max_val_len = (val_len_width*message->elements[i].num_values); + + /* Add one for storing the width */ + max_val_len += U8_LEN; + if (size + max_val_len < size) { + errno = ENOMEM; + return -1; + } + size += max_val_len; + } + + /* Allocate */ + data->data = talloc_array(ldb, uint8_t, size); + if (!data->data) { + errno = ENOMEM; + return -1; + } + data->length = size; + + /* Packing format version and number of element */ + p = data->data; + PUSH_LE_U32(p, 0, LDB_PACKING_FORMAT_V2); + p += U32_LEN; + PUSH_LE_U32(p, 0, real_elements); + p += U32_LEN; + + /* Pack DN and Canonicalized DN */ + PUSH_LE_U32(p, 0, dn_len-NULL_PAD_BYTE_LEN); + p += U32_LEN; + memcpy(p, dn, dn_len); + p += dn_len; + + PUSH_LE_U32(p, 0, dn_canon_len-NULL_PAD_BYTE_LEN); + p += U32_LEN; + memcpy(p, dn_canon, dn_canon_len); + p += dn_canon_len; + + /* + * Save pointer at this point and leave a U32_LEN gap for + * storing the size of the attribute names and value lengths + * section + */ + q = p; + p += U32_LEN; + + for (i=0;i<message->num_elements;i++) { + if (attribute_storable_values(&message->elements[i]) == 0) { + continue; + } + + /* Length of el name */ + len = strlen(message->elements[i].name); + PUSH_LE_U32(p, 0, len); + p += U32_LEN; + + /* + * Even though we have the element name's length, put a null + * terminator at the end so if any code uses the name + * directly, it'll be safe to do things requiring null + * termination like strlen + */ + memcpy(p, message->elements[i].name, len+NULL_PAD_BYTE_LEN); + p += len + NULL_PAD_BYTE_LEN; + /* Num values */ + PUSH_LE_U32(p, 0, message->elements[i].num_values); + p += U32_LEN; + + /* + * Calculate value length width again. It's faster to + * calculate it again than do the array management to + * store the result during size calculation. + */ + max_val_len = 0; + for (j=0;j<message->elements[i].num_values;j++) { + value_len = message->elements[i].values[j].length; + if (value_len > max_val_len) { + max_val_len = value_len; + } + } + + if (max_val_len <= UCHAR_MAX) { + val_len_width = U8_LEN; + } else if (max_val_len <= USHRT_MAX) { + val_len_width = U16_LEN; + } else if (max_val_len <= UINT_MAX) { + val_len_width = U32_LEN; + } else { + errno = EMSGSIZE; + return -1; + } + + /* Pack the width */ + *p = val_len_width & 0xFF; + p += U8_LEN; + + /* + * Pack each value's length using the minimum number of bytes + * required, which we just calculated. We repeat the loop + * for each case here so the compiler can inline code. + */ + if (val_len_width == U8_LEN) { + for (j=0;j<message->elements[i].num_values;j++) { + PUSH_LE_U8(p, 0, + message->elements[i].values[j].length); + p += U8_LEN; + } + } else if (val_len_width == U16_LEN) { + for (j=0;j<message->elements[i].num_values;j++) { + PUSH_LE_U16(p, 0, + message->elements[i].values[j].length); + p += U16_LEN; + } + } else if (val_len_width == U32_LEN) { + for (j=0;j<message->elements[i].num_values;j++) { + PUSH_LE_U32(p, 0, + message->elements[i].values[j].length); + p += U32_LEN; + } + } + } + + /* + * We've finished packing the attr names and value lengths + * section, so store the size in the U32_LEN gap we left + * earlier + */ + PUSH_LE_U32(q, 0, p-q); + -- Samba Shared Repository