r23795: more v2->v3 conversion
[gd/samba/.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 3 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 "ldb_includes.h"
36 #include "ldb_tdb.h"
37
38 /* change this if the data format ever changes */
39 #define LTDB_PACKING_FORMAT 0x26011967
40
41 /* old packing formats */
42 #define LTDB_PACKING_FORMAT_NODN 0x26011966
43
44 /* use a portable integer format */
45 static void put_uint32(uint8_t *p, int ofs, unsigned int val)
46 {
47         p += ofs;
48         p[0] = val&0xFF;
49         p[1] = (val>>8)  & 0xFF;
50         p[2] = (val>>16) & 0xFF;
51         p[3] = (val>>24) & 0xFF;
52 }
53
54 static unsigned int pull_uint32(uint8_t *p, int ofs)
55 {
56         p += ofs;
57         return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
58 }
59
60 static int attribute_storable_values(const struct ldb_message_element *el)
61 {
62         if (el->num_values == 0) return 0;
63
64         if (ldb_attr_cmp(el->name, "dn") == 0) return 0;
65
66         if (ldb_attr_cmp(el->name, "distinguishedName") == 0) return 0;
67
68         return el->num_values;
69 }
70
71 /*
72   pack a ldb message into a linear buffer in a TDB_DATA
73
74   note that this routine avoids saving elements with zero values,
75   as these are equivalent to having no element
76
77   caller frees the data buffer after use
78 */
79 int ltdb_pack_data(struct ldb_module *module,
80                    const struct ldb_message *message,
81                    struct TDB_DATA *data)
82 {
83         struct ldb_context *ldb = module->ldb;
84         unsigned int i, j, real_elements=0;
85         size_t size;
86         const char *dn;
87         uint8_t *p;
88         size_t len;
89
90         dn = ldb_dn_get_linearized(message->dn);
91         if (dn == NULL) {
92                 errno = ENOMEM;
93                 return -1;
94         }
95
96         /* work out how big it needs to be */
97         size = 8;
98
99         size += 1 + strlen(dn);
100
101         for (i=0;i<message->num_elements;i++) {
102                 if (attribute_storable_values(&message->elements[i]) == 0) {
103                         continue;
104                 }
105
106                 real_elements++;
107
108                 size += 1 + strlen(message->elements[i].name) + 4;
109                 for (j=0;j<message->elements[i].num_values;j++) {
110                         size += 4 + message->elements[i].values[j].length + 1;
111                 }
112         }
113
114         /* allocate it */
115         data->dptr = talloc_array(ldb, uint8_t, size);
116         if (!data->dptr) {
117                 errno = ENOMEM;
118                 return -1;
119         }
120         data->dsize = size;
121
122         p = data->dptr;
123         put_uint32(p, 0, LTDB_PACKING_FORMAT); 
124         put_uint32(p, 4, real_elements); 
125         p += 8;
126
127         /* the dn needs to be packed so we can be case preserving
128            while hashing on a case folded dn */
129         len = strlen(dn);
130         memcpy(p, dn, len+1);
131         p += len + 1;
132         
133         for (i=0;i<message->num_elements;i++) {
134                 if (attribute_storable_values(&message->elements[i]) == 0) {
135                         continue;
136                 }
137                 len = strlen(message->elements[i].name);
138                 memcpy(p, message->elements[i].name, len+1);
139                 p += len + 1;
140                 put_uint32(p, 0, message->elements[i].num_values);
141                 p += 4;
142                 for (j=0;j<message->elements[i].num_values;j++) {
143                         put_uint32(p, 0, message->elements[i].values[j].length);
144                         memcpy(p+4, message->elements[i].values[j].data, 
145                                message->elements[i].values[j].length);
146                         p[4+message->elements[i].values[j].length] = 0;
147                         p += 4 + message->elements[i].values[j].length + 1;
148                 }
149         }
150
151         return 0;
152 }
153
154 /*
155   unpack a ldb message from a linear buffer in TDB_DATA
156
157   Free with ltdb_unpack_data_free()
158 */
159 int ltdb_unpack_data(struct ldb_module *module,
160                      const struct TDB_DATA *data,
161                      struct ldb_message *message)
162 {
163         struct ldb_context *ldb = module->ldb;
164         uint8_t *p;
165         unsigned int remaining;
166         unsigned int i, j;
167         unsigned format;
168         size_t len;
169
170         message->elements = NULL;
171
172         p = data->dptr;
173         if (data->dsize < 8) {
174                 errno = EIO;
175                 goto failed;
176         }
177
178         format = pull_uint32(p, 0);
179         message->num_elements = pull_uint32(p, 4);
180         p += 8;
181
182         remaining = data->dsize - 8;
183
184         switch (format) {
185         case LTDB_PACKING_FORMAT_NODN:
186                 message->dn = NULL;
187                 break;
188
189         case LTDB_PACKING_FORMAT:
190                 len = strnlen((char *)p, remaining);
191                 if (len == remaining) {
192                         errno = EIO;
193                         goto failed;
194                 }
195                 message->dn = ldb_dn_new(message, ldb, (char *)p);
196                 if (message->dn == NULL) {
197                         errno = ENOMEM;
198                         goto failed;
199                 }
200                 remaining -= len + 1;
201                 p += len + 1;
202                 break;
203
204         default:
205                 errno = EIO;
206                 goto failed;
207         }
208
209         if (message->num_elements == 0) {
210                 message->elements = NULL;
211                 return 0;
212         }
213         
214         if (message->num_elements > remaining / 6) {
215                 errno = EIO;
216                 goto failed;
217         }
218
219         message->elements = talloc_array(message, struct ldb_message_element, message->num_elements);
220         if (!message->elements) {
221                 errno = ENOMEM;
222                 goto failed;
223         }
224
225         memset(message->elements, 0, 
226                message->num_elements * sizeof(struct ldb_message_element));
227
228         for (i=0;i<message->num_elements;i++) {
229                 if (remaining < 10) {
230                         errno = EIO;
231                         goto failed;
232                 }
233                 len = strnlen((char *)p, remaining-6);
234                 if (len == remaining-6) {
235                         errno = EIO;
236                         goto failed;
237                 }
238                 message->elements[i].flags = 0;
239                 message->elements[i].name = talloc_strndup(message->elements, (char *)p, len);
240                 if (message->elements[i].name == NULL) {
241                         errno = ENOMEM;
242                         goto failed;
243                 }
244                 remaining -= len + 1;
245                 p += len + 1;
246                 message->elements[i].num_values = pull_uint32(p, 0);
247                 message->elements[i].values = NULL;
248                 if (message->elements[i].num_values != 0) {
249                         message->elements[i].values = talloc_array(message->elements,
250                                                                      struct ldb_val, 
251                                                                      message->elements[i].num_values);
252                         if (!message->elements[i].values) {
253                                 errno = ENOMEM;
254                                 goto failed;
255                         }
256                 }
257                 p += 4;
258                 remaining -= 4;
259                 for (j=0;j<message->elements[i].num_values;j++) {
260                         len = pull_uint32(p, 0);
261                         if (len > remaining-5) {
262                                 errno = EIO;
263                                 goto failed;
264                         }
265
266                         message->elements[i].values[j].length = len;
267                         message->elements[i].values[j].data = talloc_size(message->elements[i].values, len+1);
268                         if (message->elements[i].values[j].data == NULL) {
269                                 errno = ENOMEM;
270                                 goto failed;
271                         }
272                         memcpy(message->elements[i].values[j].data, p+4, len);
273                         message->elements[i].values[j].data[len] = 0;
274         
275                         remaining -= len+4+1;
276                         p += len+4+1;
277                 }
278         }
279
280         if (remaining != 0) {
281                 ldb_debug(ldb, LDB_DEBUG_ERROR, 
282                           "Error: %d bytes unread in ltdb_unpack_data\n", remaining);
283         }
284
285         return 0;
286
287 failed:
288         talloc_free(message->elements);
289         return -1;
290 }