r19832: better prototypes for the linearization functions:
[abartlet/samba.git/.git] / source4 / lib / ldb / ldb_tdb / ldb_pack.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Tridgell  2004
5
6      ** NOTE! The following LGPL license applies to the ldb
7      ** library. This does NOT imply that all of Samba is released
8      ** under the LGPL
9    
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 2 of the License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Lesser General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 */
24
25 /*
26  *  Name: ldb
27  *
28  *  Component: ldb pack/unpack
29  *
30  *  Description: pack/unpack routines for ldb messages as key/value blobs
31  *
32  *  Author: Andrew Tridgell
33  */
34
35 #include "includes.h"
36 #include "ldb/include/includes.h"
37
38 #include "ldb/ldb_tdb/ldb_tdb.h"
39
40 /* change this if the data format ever changes */
41 #define LTDB_PACKING_FORMAT 0x26011967
42
43 /* old packing formats */
44 #define LTDB_PACKING_FORMAT_NODN 0x26011966
45
46 /* use a portable integer format */
47 static void put_uint32(uint8_t *p, int ofs, unsigned int val)
48 {
49         p += ofs;
50         p[0] = val&0xFF;
51         p[1] = (val>>8)  & 0xFF;
52         p[2] = (val>>16) & 0xFF;
53         p[3] = (val>>24) & 0xFF;
54 }
55
56 static unsigned int pull_uint32(uint8_t *p, int ofs)
57 {
58         p += ofs;
59         return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
60 }
61
62 static int attribute_storable_values(const struct ldb_message_element *el)
63 {
64         if (el->num_values == 0) return 0;
65
66         if (ldb_attr_cmp(el->name, "dn") == 0) return 0;
67
68         if (ldb_attr_cmp(el->name, "distinguishedName") == 0) return 0;
69
70         return el->num_values;
71 }
72
73 /*
74   pack a ldb message into a linear buffer in a TDB_DATA
75
76   note that this routine avoids saving elements with zero values,
77   as these are equivalent to having no element
78
79   caller frees the data buffer after use
80 */
81 int ltdb_pack_data(struct ldb_module *module,
82                    const struct ldb_message *message,
83                    struct TDB_DATA *data)
84 {
85         struct ldb_context *ldb = module->ldb;
86         unsigned int i, j, real_elements=0;
87         size_t size;
88         const char *dn;
89         uint8_t *p;
90         size_t len;
91
92         dn = ldb_dn_get_linearized(message->dn);
93         if (dn == NULL) {
94                 errno = ENOMEM;
95                 return -1;
96         }
97
98         /* work out how big it needs to be */
99         size = 8;
100
101         size += 1 + strlen(dn);
102
103         for (i=0;i<message->num_elements;i++) {
104                 if (attribute_storable_values(&message->elements[i]) == 0) {
105                         continue;
106                 }
107
108                 real_elements++;
109
110                 size += 1 + strlen(message->elements[i].name) + 4;
111                 for (j=0;j<message->elements[i].num_values;j++) {
112                         size += 4 + message->elements[i].values[j].length + 1;
113                 }
114         }
115
116         /* allocate it */
117         data->dptr = talloc_array(ldb, uint8_t, size);
118         if (!data->dptr) {
119                 errno = ENOMEM;
120                 return -1;
121         }
122         data->dsize = size;
123
124         p = data->dptr;
125         put_uint32(p, 0, LTDB_PACKING_FORMAT); 
126         put_uint32(p, 4, real_elements); 
127         p += 8;
128
129         /* the dn needs to be packed so we can be case preserving
130            while hashing on a case folded dn */
131         len = strlen(dn);
132         memcpy(p, dn, len+1);
133         p += len + 1;
134         
135         for (i=0;i<message->num_elements;i++) {
136                 if (attribute_storable_values(&message->elements[i]) == 0) {
137                         continue;
138                 }
139                 len = strlen(message->elements[i].name);
140                 memcpy(p, message->elements[i].name, len+1);
141                 p += len + 1;
142                 put_uint32(p, 0, message->elements[i].num_values);
143                 p += 4;
144                 for (j=0;j<message->elements[i].num_values;j++) {
145                         put_uint32(p, 0, message->elements[i].values[j].length);
146                         memcpy(p+4, message->elements[i].values[j].data, 
147                                message->elements[i].values[j].length);
148                         p[4+message->elements[i].values[j].length] = 0;
149                         p += 4 + message->elements[i].values[j].length + 1;
150                 }
151         }
152
153         return 0;
154 }
155
156 /*
157   unpack a ldb message from a linear buffer in TDB_DATA
158
159   Free with ltdb_unpack_data_free()
160 */
161 int ltdb_unpack_data(struct ldb_module *module,
162                      const struct TDB_DATA *data,
163                      struct ldb_message *message)
164 {
165         struct ldb_context *ldb = module->ldb;
166         uint8_t *p;
167         unsigned int remaining;
168         unsigned int i, j;
169         unsigned format;
170         size_t len;
171
172         message->elements = NULL;
173
174         p = data->dptr;
175         if (data->dsize < 8) {
176                 errno = EIO;
177                 goto failed;
178         }
179
180         format = pull_uint32(p, 0);
181         message->num_elements = pull_uint32(p, 4);
182         p += 8;
183
184         remaining = data->dsize - 8;
185
186         switch (format) {
187         case LTDB_PACKING_FORMAT_NODN:
188                 message->dn = NULL;
189                 break;
190
191         case LTDB_PACKING_FORMAT:
192                 len = strnlen((char *)p, remaining);
193                 if (len == remaining) {
194                         errno = EIO;
195                         goto failed;
196                 }
197                 message->dn = ldb_dn_new(message, ldb, (char *)p);
198                 if (message->dn == NULL) {
199                         errno = ENOMEM;
200                         goto failed;
201                 }
202                 remaining -= len + 1;
203                 p += len + 1;
204                 break;
205
206         default:
207                 errno = EIO;
208                 goto failed;
209         }
210
211         if (message->num_elements == 0) {
212                 message->elements = NULL;
213                 return 0;
214         }
215         
216         if (message->num_elements > remaining / 6) {
217                 errno = EIO;
218                 goto failed;
219         }
220
221         message->elements = talloc_array(message, struct ldb_message_element, message->num_elements);
222         if (!message->elements) {
223                 errno = ENOMEM;
224                 goto failed;
225         }
226
227         memset(message->elements, 0, 
228                message->num_elements * sizeof(struct ldb_message_element));
229
230         for (i=0;i<message->num_elements;i++) {
231                 if (remaining < 10) {
232                         errno = EIO;
233                         goto failed;
234                 }
235                 len = strnlen((char *)p, remaining-6);
236                 if (len == remaining-6) {
237                         errno = EIO;
238                         goto failed;
239                 }
240                 message->elements[i].flags = 0;
241                 message->elements[i].name = talloc_strndup(message->elements, (char *)p, len);
242                 if (message->elements[i].name == NULL) {
243                         errno = ENOMEM;
244                         goto failed;
245                 }
246                 remaining -= len + 1;
247                 p += len + 1;
248                 message->elements[i].num_values = pull_uint32(p, 0);
249                 message->elements[i].values = NULL;
250                 if (message->elements[i].num_values != 0) {
251                         message->elements[i].values = talloc_array(message->elements,
252                                                                      struct ldb_val, 
253                                                                      message->elements[i].num_values);
254                         if (!message->elements[i].values) {
255                                 errno = ENOMEM;
256                                 goto failed;
257                         }
258                 }
259                 p += 4;
260                 remaining -= 4;
261                 for (j=0;j<message->elements[i].num_values;j++) {
262                         len = pull_uint32(p, 0);
263                         if (len > remaining-5) {
264                                 errno = EIO;
265                                 goto failed;
266                         }
267
268                         message->elements[i].values[j].length = len;
269                         message->elements[i].values[j].data = talloc_size(message->elements[i].values, len+1);
270                         if (message->elements[i].values[j].data == NULL) {
271                                 errno = ENOMEM;
272                                 goto failed;
273                         }
274                         memcpy(message->elements[i].values[j].data, p+4, len);
275                         message->elements[i].values[j].data[len] = 0;
276         
277                         remaining -= len+4+1;
278                         p += len+4+1;
279                 }
280         }
281
282         if (remaining != 0) {
283                 ldb_debug(ldb, LDB_DEBUG_ERROR, 
284                           "Error: %d bytes unread in ltdb_unpack_data\n", remaining);
285         }
286
287         return 0;
288
289 failed:
290         talloc_free(message->elements);
291         return -1;
292 }