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