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