4c1241d0bd87916f22b14031fb9640978ada009a
[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 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 = talloc_array_p(ldb, char, 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   unpack a ldb message from a linear buffer in TDB_DATA
143
144   Free with ltdb_unpack_data_free()
145 */
146 int ltdb_unpack_data(struct ldb_module *module,
147                      const struct TDB_DATA *data,
148                      struct ldb_message *message)
149 {
150         struct ldb_context *ldb = module->ldb;
151         char *p;
152         unsigned int remaining;
153         unsigned int i, j;
154         unsigned format;
155         size_t len;
156
157         message->elements = NULL;
158
159         p = data->dptr;
160         if (data->dsize < 8) {
161                 errno = EIO;
162                 goto failed;
163         }
164
165         format = pull_uint32(p, 0);
166         message->num_elements = pull_uint32(p, 4);
167         p += 8;
168
169         remaining = data->dsize - 8;
170
171         switch (format) {
172         case LTDB_PACKING_FORMAT_NODN:
173                 message->dn = NULL;
174                 break;
175
176         case LTDB_PACKING_FORMAT:
177                 len = strnlen(p, remaining);
178                 if (len == remaining) {
179                         errno = EIO;
180                         goto failed;
181                 }
182                 message->dn = p;
183                 remaining -= len + 1;
184                 p += len + 1;
185                 break;
186
187         default:
188                 errno = EIO;
189                 goto failed;
190         }
191
192         if (message->num_elements == 0) {
193                 message->elements = NULL;
194                 return 0;
195         }
196         
197         if (message->num_elements > remaining / 6) {
198                 errno = EIO;
199                 goto failed;
200         }
201
202         message->elements = talloc_array_p(ldb, struct ldb_message_element, message->num_elements);
203         if (!message->elements) {
204                 errno = ENOMEM;
205                 goto failed;
206         }
207
208         memset(message->elements, 0, 
209                message->num_elements * sizeof(struct ldb_message_element));
210
211         for (i=0;i<message->num_elements;i++) {
212                 if (remaining < 10) {
213                         errno = EIO;
214                         goto failed;
215                 }
216                 len = strnlen(p, remaining-6);
217                 if (len == remaining-6) {
218                         errno = EIO;
219                         goto failed;
220                 }
221                 message->elements[i].flags = 0;
222                 message->elements[i].name = p;
223                 remaining -= len + 1;
224                 p += len + 1;
225                 message->elements[i].num_values = pull_uint32(p, 0);
226                 message->elements[i].values = NULL;
227                 if (message->elements[i].num_values != 0) {
228                         message->elements[i].values = talloc_array_p(message->elements,
229                                                                      struct ldb_val, 
230                                                                      message->elements[i].num_values);
231                         if (!message->elements[i].values) {
232                                 errno = ENOMEM;
233                                 goto failed;
234                         }
235                 }
236                 p += 4;
237                 remaining -= 4;
238                 for (j=0;j<message->elements[i].num_values;j++) {
239                         len = pull_uint32(p, 0);
240                         if (len > remaining-5) {
241                                 errno = EIO;
242                                 goto failed;
243                         }
244
245                         message->elements[i].values[j].length = len;
246                         message->elements[i].values[j].data = p+4;
247                         remaining -= len+4+1;
248                         p += len+4+1;
249                 }
250         }
251
252         if (remaining != 0) {
253                 ldb_debug(ldb, LDB_DEBUG_ERROR, 
254                           "Error: %d bytes unread in ltdb_unpack_data\n", remaining);
255         }
256
257         return 0;
258
259 failed:
260         talloc_free(message->elements);
261         return -1;
262 }