r435: a major upgrade for ldb
[abartlet/samba.git/.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/ldb_tdb/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 /*
45   pack a ldb message into a linear buffer in a TDB_DATA
46
47   note that this routine avoids saving elements with zero values,
48   as these are equivalent to having no element
49
50   caller frees the data buffer after use
51 */
52 int ltdb_pack_data(struct ldb_context *ctx,
53                    const struct ldb_message *message,
54                    struct TDB_DATA *data)
55 {
56         int i, j;
57         size_t size;
58         char *p;
59         size_t len;
60
61         /* work out how big it needs to be */
62         size = 8;
63
64         size += 1 + strlen(message->dn);
65
66         for (i=0;i<message->num_elements;i++) {
67                 if (message->elements[i].num_values == 0) {
68                         continue;
69                 }
70                 size += 1 + strlen(message->elements[i].name) + 4;
71                 for (j=0;j<message->elements[i].num_values;j++) {
72                         size += 4 + message->elements[i].values[j].length + 1;
73                 }
74         }
75
76         /* allocate it */
77         data->dptr = malloc(size);
78         if (!data->dptr) {
79                 errno = ENOMEM;
80                 return -1;
81         }
82         data->dsize = size;
83
84         p = data->dptr;
85         SIVAL(p, 0, LTDB_PACKING_FORMAT); 
86         SIVAL(p, 4, message->num_elements); 
87         p += 8;
88
89         /* the dn needs to be packed so we can be case preserving
90            while hashing on a case folded dn */
91         len = strlen(message->dn);
92         memcpy(p, message->dn, len+1);
93         p += len + 1;
94         
95         for (i=0;i<message->num_elements;i++) {
96                 if (message->elements[i].num_values == 0) {
97                         continue;
98                 }
99                 len = strlen(message->elements[i].name);
100                 memcpy(p, message->elements[i].name, len+1);
101                 p += len + 1;
102                 SIVAL(p, 0, message->elements[i].num_values);
103                 p += 4;
104                 for (j=0;j<message->elements[i].num_values;j++) {
105                         SIVAL(p, 0, message->elements[i].values[j].length);
106                         memcpy(p+4, message->elements[i].values[j].data, 
107                                message->elements[i].values[j].length);
108                         p[4+message->elements[i].values[j].length] = 0;
109                         p += 4 + message->elements[i].values[j].length + 1;
110                 }
111         }
112
113         return 0;
114 }
115
116 /*
117   free the memory allocated from a ltdb_unpack_data()
118 */
119 void ltdb_unpack_data_free(struct ldb_message *message)
120 {
121         int i;
122
123         for (i=0;i<message->num_elements;i++) {
124                 if (message->elements[i].values) free(message->elements[i].values);
125         }
126         if (message->elements) free(message->elements);
127 }
128
129
130 /*
131   unpack a ldb message from a linear buffer in TDB_DATA
132
133   note that this does not fill in the class and key elements
134
135   caller frees. Memory for the elements[] and values[] arrays are
136   malloced, but the memory for the elements is re-used from the
137   TDB_DATA data. This means the caller only has to free the elements
138   and values arrays. This can be done with ltdb_unpack_data_free()
139 */
140 int ltdb_unpack_data(struct ldb_context *ctx,
141                      const struct TDB_DATA *data,
142                      struct ldb_message *message)
143 {
144         char *p;
145         unsigned int remaining;
146         int i, j;
147         unsigned format;
148         size_t len;
149
150         message->elements = NULL;
151
152         p = data->dptr;
153         if (data->dsize < 8) {
154                 errno = EIO;
155                 goto failed;
156         }
157
158         format = IVAL(p, 0);
159         message->num_elements = IVAL(p, 4);
160         p += 8;
161
162         remaining = data->dsize - 8;
163
164         switch (format) {
165         case LTDB_PACKING_FORMAT_NODN:
166                 message->dn = NULL;
167                 break;
168
169         case LTDB_PACKING_FORMAT:
170                 len = strnlen(p, remaining);
171                 if (len == remaining) {
172                         errno = EIO;
173                         goto failed;
174                 }
175                 message->dn = p;
176                 remaining -= len + 1;
177                 p += len + 1;
178                 break;
179
180         default:
181                 errno = EIO;
182                 goto failed;
183         }
184
185         if (message->num_elements == 0) {
186                 message->elements = NULL;
187                 return 0;
188         }
189         
190         if (message->num_elements > remaining / 6) {
191                 errno = EIO;
192                 goto failed;
193         }
194
195         message->elements = malloc_array_p(struct ldb_message_element,
196                                            message->num_elements);
197                                      
198         if (!message->elements) {
199                 errno = ENOMEM;
200                 goto failed;
201         }
202
203         for (i=0;i<message->num_elements;i++) {
204                 if (remaining < 10) {
205                         errno = EIO;
206                         goto failed;
207                 }
208                 len = strnlen(p, remaining-6);
209                 if (len == remaining-6) {
210                         errno = EIO;
211                         goto failed;
212                 }
213                 message->elements[i].flags = 0;
214                 message->elements[i].name = p;
215                 remaining -= len + 1;
216                 p += len + 1;
217                 message->elements[i].num_values = IVAL(p, 0);
218                 message->elements[i].values = NULL;
219                 if (message->elements[i].num_values != 0) {
220                         message->elements[i].values = malloc_array_p(struct ldb_val, 
221                                                                      message->elements[i].num_values);
222                         if (!message->elements[i].values) {
223                                 errno = ENOMEM;
224                                 goto failed;
225                         }
226                 }
227                 p += 4;
228                 for (j=0;j<message->elements[i].num_values;j++) {
229                         len = IVAL(p, 0);
230                         if (len > remaining-5) {
231                                 errno = EIO;
232                                 goto failed;
233                         }
234
235                         message->elements[i].values[j].length = len;
236                         message->elements[i].values[j].data = p+4;
237                         remaining -= len+4+1;
238                         p += len+4+1;
239                 }
240         }
241
242         return 0;
243
244 failed:
245         ltdb_unpack_data_free(message);
246
247         return -1;
248 }