s4-dbcheck: support the 'none' option for prompts
[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_tdb.h"
35
36 /* change this if the data format ever changes */
37 #define LTDB_PACKING_FORMAT 0x26011967
38
39 /* old packing formats */
40 #define LTDB_PACKING_FORMAT_NODN 0x26011966
41
42 /* use a portable integer format */
43 static void put_uint32(uint8_t *p, int ofs, unsigned int val)
44 {
45         p += ofs;
46         p[0] = val&0xFF;
47         p[1] = (val>>8)  & 0xFF;
48         p[2] = (val>>16) & 0xFF;
49         p[3] = (val>>24) & 0xFF;
50 }
51
52 static unsigned int pull_uint32(uint8_t *p, int ofs)
53 {
54         p += ofs;
55         return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
56 }
57
58 static int attribute_storable_values(const struct ldb_message_element *el)
59 {
60         if (el->num_values == 0) return 0;
61
62         if (ldb_attr_cmp(el->name, "distinguishedName") == 0) return 0;
63
64         return el->num_values;
65 }
66
67 /*
68   pack a ldb message into a linear buffer in a TDB_DATA
69
70   note that this routine avoids saving elements with zero values,
71   as these are equivalent to having no element
72
73   caller frees the data buffer after use
74 */
75 int ltdb_pack_data(struct ldb_module *module,
76                    const struct ldb_message *message,
77                    TDB_DATA *data)
78 {
79         struct ldb_context *ldb;
80         unsigned int i, j, real_elements=0;
81         size_t size;
82         const char *dn;
83         uint8_t *p;
84         size_t len;
85
86         ldb = ldb_module_get_ctx(module);
87
88         dn = ldb_dn_get_linearized(message->dn);
89         if (dn == NULL) {
90                 errno = ENOMEM;
91                 return -1;
92         }
93
94         /* work out how big it needs to be */
95         size = 8;
96
97         size += 1 + strlen(dn);
98
99         for (i=0;i<message->num_elements;i++) {
100                 if (attribute_storable_values(&message->elements[i]) == 0) {
101                         continue;
102                 }
103
104                 real_elements++;
105
106                 size += 1 + strlen(message->elements[i].name) + 4;
107                 for (j=0;j<message->elements[i].num_values;j++) {
108                         size += 4 + message->elements[i].values[j].length + 1;
109                 }
110         }
111
112         /* allocate it */
113         data->dptr = talloc_array(ldb, uint8_t, size);
114         if (!data->dptr) {
115                 errno = ENOMEM;
116                 return -1;
117         }
118         data->dsize = size;
119
120         p = data->dptr;
121         put_uint32(p, 0, LTDB_PACKING_FORMAT); 
122         put_uint32(p, 4, real_elements); 
123         p += 8;
124
125         /* the dn needs to be packed so we can be case preserving
126            while hashing on a case folded dn */
127         len = strlen(dn);
128         memcpy(p, dn, len+1);
129         p += len + 1;
130         
131         for (i=0;i<message->num_elements;i++) {
132                 if (attribute_storable_values(&message->elements[i]) == 0) {
133                         continue;
134                 }
135                 len = strlen(message->elements[i].name);
136                 memcpy(p, message->elements[i].name, len+1);
137                 p += len + 1;
138                 put_uint32(p, 0, message->elements[i].num_values);
139                 p += 4;
140                 for (j=0;j<message->elements[i].num_values;j++) {
141                         put_uint32(p, 0, message->elements[i].values[j].length);
142                         memcpy(p+4, message->elements[i].values[j].data, 
143                                message->elements[i].values[j].length);
144                         p[4+message->elements[i].values[j].length] = 0;
145                         p += 4 + message->elements[i].values[j].length + 1;
146                 }
147         }
148
149         return 0;
150 }
151
152 /*
153   unpack a ldb message from a linear buffer in TDB_DATA
154
155   Free with ltdb_unpack_data_free()
156 */
157 int ltdb_unpack_data(struct ldb_module *module,
158                      const TDB_DATA *data,
159                      struct ldb_message *message)
160 {
161         struct ldb_context *ldb;
162         uint8_t *p;
163         unsigned int remaining;
164         unsigned int i, j;
165         unsigned format;
166         size_t len;
167
168         ldb = ldb_module_get_ctx(module);
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                 return 0;
210         }
211         
212         if (message->num_elements > remaining / 6) {
213                 errno = EIO;
214                 goto failed;
215         }
216
217         message->elements = talloc_array(message, struct ldb_message_element, message->num_elements);
218         if (!message->elements) {
219                 errno = ENOMEM;
220                 goto failed;
221         }
222
223         memset(message->elements, 0, 
224                message->num_elements * sizeof(struct ldb_message_element));
225
226         for (i=0;i<message->num_elements;i++) {
227                 if (remaining < 10) {
228                         errno = EIO;
229                         goto failed;
230                 }
231                 len = strnlen((char *)p, remaining-6);
232                 if (len == remaining-6) {
233                         errno = EIO;
234                         goto failed;
235                 }
236                 if (len == 0) {
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", remaining);
285         }
286
287         return 0;
288
289 failed:
290         talloc_free(message->elements);
291         return -1;
292 }