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