r4040: sorry today is not my day...
[ira/wip.git] / source / 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/ldb.h"
37 #include "ldb/include/ldb_private.h"
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 /*
63   pack a ldb message into a linear buffer in a TDB_DATA
64
65   note that this routine avoids saving elements with zero values,
66   as these are equivalent to having no element
67
68   caller frees the data buffer after use
69 */
70 int ltdb_pack_data(struct ldb_module *module,
71                    const struct ldb_message *message,
72                    struct TDB_DATA *data)
73 {
74         struct ldb_context *ldb = module->ldb;
75         unsigned int i, j, real_elements=0;
76         size_t size;
77         char *p;
78         size_t len;
79
80         for (i=0;i<message->num_elements;i++) {
81                 if (message->elements[i].num_values != 0) {
82                         real_elements++;
83                 }
84         }
85
86         /* work out how big it needs to be */
87         size = 8;
88
89         size += 1 + strlen(message->dn);
90
91         for (i=0;i<message->num_elements;i++) {
92                 if (message->elements[i].num_values == 0) {
93                         continue;
94                 }
95                 size += 1 + strlen(message->elements[i].name) + 4;
96                 for (j=0;j<message->elements[i].num_values;j++) {
97                         size += 4 + message->elements[i].values[j].length + 1;
98                 }
99         }
100
101         /* allocate it */
102         data->dptr = ldb_malloc(ldb, size);
103         if (!data->dptr) {
104                 errno = ENOMEM;
105                 return -1;
106         }
107         data->dsize = size;
108
109         p = data->dptr;
110         put_uint32(p, 0, LTDB_PACKING_FORMAT); 
111         put_uint32(p, 4, real_elements); 
112         p += 8;
113
114         /* the dn needs to be packed so we can be case preserving
115            while hashing on a case folded dn */
116         len = strlen(message->dn);
117         memcpy(p, message->dn, len+1);
118         p += len + 1;
119         
120         for (i=0;i<message->num_elements;i++) {
121                 if (message->elements[i].num_values == 0) {
122                         continue;
123                 }
124                 len = strlen(message->elements[i].name);
125                 memcpy(p, message->elements[i].name, len+1);
126                 p += len + 1;
127                 put_uint32(p, 0, message->elements[i].num_values);
128                 p += 4;
129                 for (j=0;j<message->elements[i].num_values;j++) {
130                         put_uint32(p, 0, message->elements[i].values[j].length);
131                         memcpy(p+4, message->elements[i].values[j].data, 
132                                message->elements[i].values[j].length);
133                         p[4+message->elements[i].values[j].length] = 0;
134                         p += 4 + message->elements[i].values[j].length + 1;
135                 }
136         }
137
138         return 0;
139 }
140
141 /*
142   free the memory allocated from a ltdb_unpack_data()
143 */
144 void ltdb_unpack_data_free(struct ldb_module *module,
145                            struct ldb_message *message)
146 {
147         struct ldb_context *ldb = module->ldb;
148         unsigned int i;
149
150         for (i=0;i<message->num_elements;i++) {
151                 if (message->elements[i].values) ldb_free(ldb, message->elements[i].values);
152         }
153         if (message->elements) ldb_free(ldb, message->elements);
154 }
155
156
157 /*
158   unpack a ldb message from a linear buffer in TDB_DATA
159
160   note that this does not fill in the class and key elements
161
162   caller frees. Memory for the elements[] and values[] arrays are
163   malloced, but the memory for the elements is re-used from the
164   TDB_DATA data. This means the caller only has to free the elements
165   and values arrays. This can be done with ltdb_unpack_data_free()
166 */
167 int ltdb_unpack_data(struct ldb_module *module,
168                      const struct TDB_DATA *data,
169                      struct ldb_message *message)
170 {
171         struct ldb_context *ldb = module->ldb;
172         char *p;
173         unsigned int remaining;
174         unsigned int i, j;
175         unsigned format;
176         size_t len;
177
178         message->elements = NULL;
179
180         p = data->dptr;
181         if (data->dsize < 8) {
182                 errno = EIO;
183                 goto failed;
184         }
185
186         format = pull_uint32(p, 0);
187         message->num_elements = pull_uint32(p, 4);
188         p += 8;
189
190         remaining = data->dsize - 8;
191
192         switch (format) {
193         case LTDB_PACKING_FORMAT_NODN:
194                 message->dn = NULL;
195                 break;
196
197         case LTDB_PACKING_FORMAT:
198                 len = strnlen(p, remaining);
199                 if (len == remaining) {
200                         errno = EIO;
201                         goto failed;
202                 }
203                 message->dn = p;
204                 remaining -= len + 1;
205                 p += len + 1;
206                 break;
207
208         default:
209                 errno = EIO;
210                 goto failed;
211         }
212
213         if (message->num_elements == 0) {
214                 message->elements = NULL;
215                 return 0;
216         }
217         
218         if (message->num_elements > remaining / 6) {
219                 errno = EIO;
220                 goto failed;
221         }
222
223         message->elements = ldb_malloc_array_p(ldb, struct ldb_message_element,
224                                                message->num_elements);
225         if (!message->elements) {
226                 errno = ENOMEM;
227                 goto failed;
228         }
229
230         memset(message->elements, 0, 
231                message->num_elements * sizeof(struct ldb_message_element));
232
233         for (i=0;i<message->num_elements;i++) {
234                 if (remaining < 10) {
235                         errno = EIO;
236                         goto failed;
237                 }
238                 len = strnlen(p, remaining-6);
239                 if (len == remaining-6) {
240                         errno = EIO;
241                         goto failed;
242                 }
243                 message->elements[i].flags = 0;
244                 message->elements[i].name = p;
245                 remaining -= len + 1;
246                 p += len + 1;
247                 message->elements[i].num_values = pull_uint32(p, 0);
248                 message->elements[i].values = NULL;
249                 if (message->elements[i].num_values != 0) {
250                         message->elements[i].values = ldb_malloc_array_p(ldb,
251                                                                          struct ldb_val, 
252                                                                          message->elements[i].num_values);
253                         if (!message->elements[i].values) {
254                                 errno = ENOMEM;
255                                 goto failed;
256                         }
257                 }
258                 p += 4;
259                 remaining -= 4;
260                 for (j=0;j<message->elements[i].num_values;j++) {
261                         len = pull_uint32(p, 0);
262                         if (len > remaining-5) {
263                                 errno = EIO;
264                                 goto failed;
265                         }
266
267                         message->elements[i].values[j].length = len;
268                         message->elements[i].values[j].data = p+4;
269                         remaining -= len+4+1;
270                         p += len+4+1;
271                 }
272         }
273
274         if (remaining != 0) {
275                 ldb_debug(ldb, LDB_DEBUG_ERROR, 
276                           "Error: %d bytes unread in ltdb_unpack_data\n", remaining);
277         }
278
279         return 0;
280
281 failed:
282         ltdb_unpack_data_free(module, message);
283
284         return -1;
285 }