1
0
mirror of https://github.com/samba-team/samba.git synced 2025-11-26 04:23:49 +03:00
Files
samba-mirror/source/lib/ldb/ldb_tdb/ldb_pack.c
Simo Sorce a580c871d3 r19831: Big ldb_dn optimization and interfaces enhancement patch
This patch changes a lot of the code in ldb_dn.c, and also
removes and add a number of manipulation functions around.

The aim is to avoid validating a dn if not necessary as the
validation code is necessarily slow. This is mainly to speed up
internal operations where input is not user generated and so we
can assume the DNs need no validation. The code is designed to
keep the data as a string if possible.

The code is not yet 100% perfect, but pass all the tests so far.
A memleak is certainly present, I'll work on that next.

Simo.
2007-10-10 14:28:22 -05:00

295 lines
6.8 KiB
C

/*
ldb database library
Copyright (C) Andrew Tridgell 2004
** NOTE! The following LGPL license applies to the ldb
** library. This does NOT imply that all of Samba is released
** under the LGPL
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Name: ldb
*
* Component: ldb pack/unpack
*
* Description: pack/unpack routines for ldb messages as key/value blobs
*
* Author: Andrew Tridgell
*/
#include "includes.h"
#include "ldb/include/includes.h"
#include "ldb/ldb_tdb/ldb_tdb.h"
/* change this if the data format ever changes */
#define LTDB_PACKING_FORMAT 0x26011967
/* old packing formats */
#define LTDB_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);
}
static int attribute_storable_values(const struct ldb_message_element *el)
{
if (el->num_values == 0) return 0;
if (ldb_attr_cmp(el->name, "dn") == 0) return 0;
if (ldb_attr_cmp(el->name, "distinguishedName") == 0) return 0;
return el->num_values;
}
/*
pack a ldb message into a linear buffer in a TDB_DATA
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 ltdb_pack_data(struct ldb_module *module,
const struct ldb_message *message,
struct TDB_DATA *data)
{
struct ldb_context *ldb = module->ldb;
unsigned int i, j, real_elements=0;
size_t size;
char *dn;
uint8_t *p;
size_t len;
dn = ldb_dn_linearize(ldb, message->dn);
if (dn == NULL) {
errno = ENOMEM;
return -1;
}
/* work out how big it needs to be */
size = 8;
size += 1 + strlen(dn);
for (i=0;i<message->num_elements;i++) {
if (attribute_storable_values(&message->elements[i]) == 0) {
continue;
}
real_elements++;
size += 1 + strlen(message->elements[i].name) + 4;
for (j=0;j<message->elements[i].num_values;j++) {
size += 4 + message->elements[i].values[j].length + 1;
}
}
/* allocate it */
data->dptr = talloc_array(ldb, uint8_t, size);
if (!data->dptr) {
talloc_free(dn);
errno = ENOMEM;
return -1;
}
data->dsize = size;
p = data->dptr;
put_uint32(p, 0, LTDB_PACKING_FORMAT);
put_uint32(p, 4, real_elements);
p += 8;
/* the dn needs to be packed so we can be case preserving
while hashing on a case folded dn */
len = strlen(dn);
memcpy(p, dn, len+1);
p += len + 1;
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;
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,
message->elements[i].values[j].length);
p[4+message->elements[i].values[j].length] = 0;
p += 4 + message->elements[i].values[j].length + 1;
}
}
talloc_free(dn);
return 0;
}
/*
unpack a ldb message from a linear buffer in TDB_DATA
Free with ltdb_unpack_data_free()
*/
int ltdb_unpack_data(struct ldb_module *module,
const struct TDB_DATA *data,
struct ldb_message *message)
{
struct ldb_context *ldb = module->ldb;
uint8_t *p;
unsigned int remaining;
unsigned int i, j;
unsigned format;
size_t len;
message->elements = NULL;
p = data->dptr;
if (data->dsize < 8) {
errno = EIO;
goto failed;
}
format = pull_uint32(p, 0);
message->num_elements = pull_uint32(p, 4);
p += 8;
remaining = data->dsize - 8;
switch (format) {
case LTDB_PACKING_FORMAT_NODN:
message->dn = NULL;
break;
case LTDB_PACKING_FORMAT:
len = strnlen((char *)p, remaining);
if (len == remaining) {
errno = EIO;
goto failed;
}
message->dn = ldb_dn_new(message, ldb, (char *)p);
if (message->dn == NULL) {
errno = ENOMEM;
goto failed;
}
remaining -= len + 1;
p += len + 1;
break;
default:
errno = EIO;
goto failed;
}
if (message->num_elements == 0) {
message->elements = NULL;
return 0;
}
if (message->num_elements > remaining / 6) {
errno = EIO;
goto failed;
}
message->elements = talloc_array(message, struct ldb_message_element, message->num_elements);
if (!message->elements) {
errno = ENOMEM;
goto failed;
}
memset(message->elements, 0,
message->num_elements * sizeof(struct ldb_message_element));
for (i=0;i<message->num_elements;i++) {
if (remaining < 10) {
errno = EIO;
goto failed;
}
len = strnlen((char *)p, remaining-6);
if (len == remaining-6) {
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) {
errno = ENOMEM;
goto failed;
}
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) {
errno = ENOMEM;
goto failed;
}
}
p += 4;
remaining -= 4;
for (j=0;j<message->elements[i].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) {
errno = ENOMEM;
goto failed;
}
memcpy(message->elements[i].values[j].data, p+4, len);
message->elements[i].values[j].data[len] = 0;
remaining -= len+4+1;
p += len+4+1;
}
}
if (remaining != 0) {
ldb_debug(ldb, LDB_DEBUG_ERROR,
"Error: %d bytes unread in ltdb_unpack_data\n", remaining);
}
return 0;
failed:
talloc_free(message->elements);
return -1;
}