prop_data.c revision 1.20 1 1.20 thorpej /* $NetBSD: prop_data.c,v 1.20 2025/04/26 17:13:23 thorpej Exp $ */
2 1.1 thorpej
3 1.1 thorpej /*-
4 1.19 thorpej * Copyright (c) 2006, 2020, 2025 The NetBSD Foundation, Inc.
5 1.1 thorpej * All rights reserved.
6 1.1 thorpej *
7 1.1 thorpej * This code is derived from software contributed to The NetBSD Foundation
8 1.1 thorpej * by Jason R. Thorpe.
9 1.1 thorpej *
10 1.1 thorpej * Redistribution and use in source and binary forms, with or without
11 1.1 thorpej * modification, are permitted provided that the following conditions
12 1.1 thorpej * are met:
13 1.1 thorpej * 1. Redistributions of source code must retain the above copyright
14 1.1 thorpej * notice, this list of conditions and the following disclaimer.
15 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 thorpej * notice, this list of conditions and the following disclaimer in the
17 1.1 thorpej * documentation and/or other materials provided with the distribution.
18 1.1 thorpej *
19 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 thorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 thorpej * POSSIBILITY OF SUCH DAMAGE.
30 1.1 thorpej */
31 1.1 thorpej
32 1.15 thorpej #include "prop_object_impl.h"
33 1.1 thorpej #include <prop/prop_data.h>
34 1.1 thorpej
35 1.1 thorpej #if defined(_KERNEL)
36 1.1 thorpej #include <sys/systm.h>
37 1.1 thorpej #elif defined(_STANDALONE)
38 1.1 thorpej #include <sys/param.h>
39 1.1 thorpej #include <lib/libkern/libkern.h>
40 1.1 thorpej #else
41 1.1 thorpej #include <errno.h>
42 1.1 thorpej #include <limits.h>
43 1.1 thorpej #include <stdlib.h>
44 1.1 thorpej #endif
45 1.1 thorpej
46 1.1 thorpej struct _prop_data {
47 1.1 thorpej struct _prop_object pd_obj;
48 1.1 thorpej union {
49 1.1 thorpej void * pdu_mutable;
50 1.1 thorpej const void * pdu_immutable;
51 1.1 thorpej } pd_un;
52 1.1 thorpej #define pd_mutable pd_un.pdu_mutable
53 1.1 thorpej #define pd_immutable pd_un.pdu_immutable
54 1.1 thorpej size_t pd_size;
55 1.1 thorpej int pd_flags;
56 1.1 thorpej };
57 1.1 thorpej
58 1.1 thorpej #define PD_F_NOCOPY 0x01
59 1.16 thorpej #define PD_F_MUTABLE 0x02
60 1.1 thorpej
61 1.1 thorpej _PROP_POOL_INIT(_prop_data_pool, sizeof(struct _prop_data), "propdata")
62 1.1 thorpej _PROP_MALLOC_DEFINE(M_PROP_DATA, "prop data",
63 1.1 thorpej "property data container object")
64 1.1 thorpej
65 1.19 thorpej static const struct _prop_object_type_tags _prop_data_type_tags = {
66 1.19 thorpej .xml_tag = "data",
67 1.19 thorpej };
68 1.19 thorpej
69 1.13 thorpej static _prop_object_free_rv_t
70 1.13 thorpej _prop_data_free(prop_stack_t, prop_object_t *);
71 1.7 thorpej static bool _prop_data_externalize(
72 1.2 thorpej struct _prop_object_externalize_context *,
73 1.2 thorpej void *);
74 1.13 thorpej static _prop_object_equals_rv_t
75 1.13 thorpej _prop_data_equals(prop_object_t, prop_object_t,
76 1.9 joerg void **, void **,
77 1.9 joerg prop_object_t *, prop_object_t *);
78 1.2 thorpej
79 1.2 thorpej static const struct _prop_object_type _prop_object_type_data = {
80 1.2 thorpej .pot_type = PROP_TYPE_DATA,
81 1.2 thorpej .pot_free = _prop_data_free,
82 1.2 thorpej .pot_extern = _prop_data_externalize,
83 1.2 thorpej .pot_equals = _prop_data_equals,
84 1.2 thorpej };
85 1.2 thorpej
86 1.2 thorpej #define prop_object_is_data(x) \
87 1.4 thorpej ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_data)
88 1.1 thorpej
89 1.8 joerg /* ARGSUSED */
90 1.13 thorpej static _prop_object_free_rv_t
91 1.8 joerg _prop_data_free(prop_stack_t stack, prop_object_t *obj)
92 1.1 thorpej {
93 1.8 joerg prop_data_t pd = *obj;
94 1.1 thorpej
95 1.1 thorpej if ((pd->pd_flags & PD_F_NOCOPY) == 0 && pd->pd_mutable != NULL)
96 1.1 thorpej _PROP_FREE(pd->pd_mutable, M_PROP_DATA);
97 1.8 joerg _PROP_POOL_PUT(_prop_data_pool, pd);
98 1.8 joerg
99 1.8 joerg return (_PROP_OBJECT_FREE_DONE);
100 1.1 thorpej }
101 1.1 thorpej
102 1.1 thorpej static const char _prop_data_base64[] =
103 1.1 thorpej "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
104 1.1 thorpej static const char _prop_data_pad64 = '=';
105 1.1 thorpej
106 1.7 thorpej static bool
107 1.1 thorpej _prop_data_externalize(struct _prop_object_externalize_context *ctx, void *v)
108 1.1 thorpej {
109 1.1 thorpej prop_data_t pd = v;
110 1.1 thorpej size_t i, srclen;
111 1.1 thorpej const uint8_t *src;
112 1.1 thorpej uint8_t output[4];
113 1.1 thorpej uint8_t input[3];
114 1.1 thorpej
115 1.19 thorpej _PROP_ASSERT(ctx->poec_format == PROP_FORMAT_XML ||
116 1.19 thorpej ctx->poec_format == PROP_FORMAT_JSON);
117 1.19 thorpej
118 1.19 thorpej /*
119 1.19 thorpej * JSON does not have a syntax for serialized binary data.
120 1.19 thorpej */
121 1.19 thorpej if (ctx->poec_format == PROP_FORMAT_JSON) {
122 1.19 thorpej return false;
123 1.19 thorpej }
124 1.19 thorpej
125 1.1 thorpej if (pd->pd_size == 0)
126 1.19 thorpej return (_prop_object_externalize_empty_tag(ctx,
127 1.19 thorpej &_prop_data_type_tags));
128 1.1 thorpej
129 1.19 thorpej if (_prop_object_externalize_start_tag(ctx,
130 1.19 thorpej &_prop_data_type_tags, NULL) == false)
131 1.7 thorpej return (false);
132 1.1 thorpej
133 1.1 thorpej for (src = pd->pd_immutable, srclen = pd->pd_size;
134 1.1 thorpej srclen > 2; srclen -= 3) {
135 1.1 thorpej input[0] = *src++;
136 1.1 thorpej input[1] = *src++;
137 1.1 thorpej input[2] = *src++;
138 1.1 thorpej
139 1.1 thorpej output[0] = (uint32_t)input[0] >> 2;
140 1.1 thorpej output[1] = ((uint32_t)(input[0] & 0x03) << 4) +
141 1.1 thorpej ((uint32_t)input[1] >> 4);
142 1.10 apb output[2] = ((uint32_t)(input[1] & 0x0f) << 2) +
143 1.1 thorpej ((uint32_t)input[2] >> 6);
144 1.1 thorpej output[3] = input[2] & 0x3f;
145 1.1 thorpej _PROP_ASSERT(output[0] < 64);
146 1.1 thorpej _PROP_ASSERT(output[1] < 64);
147 1.1 thorpej _PROP_ASSERT(output[2] < 64);
148 1.1 thorpej _PROP_ASSERT(output[3] < 64);
149 1.1 thorpej
150 1.1 thorpej if (_prop_object_externalize_append_char(ctx,
151 1.7 thorpej _prop_data_base64[output[0]]) == false ||
152 1.1 thorpej _prop_object_externalize_append_char(ctx,
153 1.7 thorpej _prop_data_base64[output[1]]) == false ||
154 1.1 thorpej _prop_object_externalize_append_char(ctx,
155 1.7 thorpej _prop_data_base64[output[2]]) == false ||
156 1.1 thorpej _prop_object_externalize_append_char(ctx,
157 1.7 thorpej _prop_data_base64[output[3]]) == false)
158 1.7 thorpej return (false);
159 1.1 thorpej }
160 1.1 thorpej
161 1.1 thorpej if (srclen != 0) {
162 1.1 thorpej input[0] = input[1] = input[2] = '\0';
163 1.1 thorpej for (i = 0; i < srclen; i++)
164 1.1 thorpej input[i] = *src++;
165 1.1 thorpej
166 1.1 thorpej output[0] = (uint32_t)input[0] >> 2;
167 1.1 thorpej output[1] = ((uint32_t)(input[0] & 0x03) << 4) +
168 1.1 thorpej ((uint32_t)input[1] >> 4);
169 1.10 apb output[2] = ((uint32_t)(input[1] & 0x0f) << 2) +
170 1.1 thorpej ((uint32_t)input[2] >> 6);
171 1.1 thorpej _PROP_ASSERT(output[0] < 64);
172 1.1 thorpej _PROP_ASSERT(output[1] < 64);
173 1.1 thorpej _PROP_ASSERT(output[2] < 64);
174 1.1 thorpej
175 1.1 thorpej if (_prop_object_externalize_append_char(ctx,
176 1.7 thorpej _prop_data_base64[output[0]]) == false ||
177 1.1 thorpej _prop_object_externalize_append_char(ctx,
178 1.7 thorpej _prop_data_base64[output[1]]) == false ||
179 1.1 thorpej _prop_object_externalize_append_char(ctx,
180 1.1 thorpej srclen == 1 ? _prop_data_pad64
181 1.7 thorpej : _prop_data_base64[output[2]]) == false ||
182 1.1 thorpej _prop_object_externalize_append_char(ctx,
183 1.7 thorpej _prop_data_pad64) == false)
184 1.7 thorpej return (false);
185 1.1 thorpej }
186 1.1 thorpej
187 1.19 thorpej if (_prop_object_externalize_end_tag(ctx,
188 1.19 thorpej &_prop_data_type_tags) == false)
189 1.7 thorpej return (false);
190 1.18 riastrad
191 1.7 thorpej return (true);
192 1.1 thorpej }
193 1.1 thorpej
194 1.9 joerg /* ARGSUSED */
195 1.13 thorpej static _prop_object_equals_rv_t
196 1.9 joerg _prop_data_equals(prop_object_t v1, prop_object_t v2,
197 1.9 joerg void **stored_pointer1, void **stored_pointer2,
198 1.9 joerg prop_object_t *next_obj1, prop_object_t *next_obj2)
199 1.2 thorpej {
200 1.2 thorpej prop_data_t pd1 = v1;
201 1.2 thorpej prop_data_t pd2 = v2;
202 1.2 thorpej
203 1.2 thorpej if (pd1 == pd2)
204 1.9 joerg return (_PROP_OBJECT_EQUALS_TRUE);
205 1.2 thorpej if (pd1->pd_size != pd2->pd_size)
206 1.9 joerg return (_PROP_OBJECT_EQUALS_FALSE);
207 1.2 thorpej if (pd1->pd_size == 0) {
208 1.2 thorpej _PROP_ASSERT(pd1->pd_immutable == NULL);
209 1.2 thorpej _PROP_ASSERT(pd2->pd_immutable == NULL);
210 1.9 joerg return (_PROP_OBJECT_EQUALS_TRUE);
211 1.2 thorpej }
212 1.9 joerg if (memcmp(pd1->pd_immutable, pd2->pd_immutable, pd1->pd_size) == 0)
213 1.9 joerg return _PROP_OBJECT_EQUALS_TRUE;
214 1.9 joerg else
215 1.9 joerg return _PROP_OBJECT_EQUALS_FALSE;
216 1.2 thorpej }
217 1.2 thorpej
218 1.1 thorpej static prop_data_t
219 1.16 thorpej _prop_data_alloc(int const flags)
220 1.1 thorpej {
221 1.1 thorpej prop_data_t pd;
222 1.1 thorpej
223 1.1 thorpej pd = _PROP_POOL_GET(_prop_data_pool);
224 1.1 thorpej if (pd != NULL) {
225 1.2 thorpej _prop_object_init(&pd->pd_obj, &_prop_object_type_data);
226 1.1 thorpej
227 1.1 thorpej pd->pd_mutable = NULL;
228 1.1 thorpej pd->pd_size = 0;
229 1.16 thorpej pd->pd_flags = flags;
230 1.1 thorpej }
231 1.1 thorpej
232 1.1 thorpej return (pd);
233 1.1 thorpej }
234 1.1 thorpej
235 1.16 thorpej static prop_data_t
236 1.16 thorpej _prop_data_instantiate(int const flags, const void * const data,
237 1.16 thorpej size_t const len)
238 1.16 thorpej {
239 1.16 thorpej prop_data_t pd;
240 1.16 thorpej
241 1.16 thorpej pd = _prop_data_alloc(flags);
242 1.16 thorpej if (pd != NULL) {
243 1.16 thorpej pd->pd_immutable = data;
244 1.16 thorpej pd->pd_size = len;
245 1.16 thorpej }
246 1.16 thorpej
247 1.16 thorpej return (pd);
248 1.16 thorpej }
249 1.16 thorpej
250 1.16 thorpej _PROP_DEPRECATED(prop_data_create_data,
251 1.16 thorpej "this program uses prop_data_create_data(); all functions "
252 1.16 thorpej "supporting mutable prop_data objects are deprecated.")
253 1.20 thorpej _PROP_EXPORT prop_data_t
254 1.1 thorpej prop_data_create_data(const void *v, size_t size)
255 1.1 thorpej {
256 1.1 thorpej prop_data_t pd;
257 1.1 thorpej void *nv;
258 1.1 thorpej
259 1.16 thorpej pd = _prop_data_alloc(PD_F_MUTABLE);
260 1.12 dyoung if (pd != NULL && size != 0) {
261 1.1 thorpej nv = _PROP_MALLOC(size, M_PROP_DATA);
262 1.1 thorpej if (nv == NULL) {
263 1.8 joerg prop_object_release(pd);
264 1.1 thorpej return (NULL);
265 1.1 thorpej }
266 1.1 thorpej memcpy(nv, v, size);
267 1.1 thorpej pd->pd_mutable = nv;
268 1.1 thorpej pd->pd_size = size;
269 1.1 thorpej }
270 1.1 thorpej return (pd);
271 1.1 thorpej }
272 1.1 thorpej
273 1.16 thorpej _PROP_DEPRECATED(prop_data_create_data_nocopy,
274 1.16 thorpej "this program uses prop_data_create_data_nocopy(), "
275 1.16 thorpej "which is deprecated; use prop_data_create_nocopy() instead.")
276 1.20 thorpej _PROP_EXPORT prop_data_t
277 1.16 thorpej prop_data_create_data_nocopy(const void *v, size_t size)
278 1.16 thorpej {
279 1.16 thorpej return prop_data_create_nocopy(v, size);
280 1.16 thorpej }
281 1.16 thorpej
282 1.1 thorpej /*
283 1.16 thorpej * prop_data_create_copy --
284 1.16 thorpej * Create a data object with a copy of the provided data.
285 1.1 thorpej */
286 1.20 thorpej _PROP_EXPORT prop_data_t
287 1.16 thorpej prop_data_create_copy(const void *v, size_t size)
288 1.1 thorpej {
289 1.1 thorpej prop_data_t pd;
290 1.16 thorpej void *nv;
291 1.16 thorpej
292 1.16 thorpej /* Tolerate the creation of empty data objects. */
293 1.16 thorpej if (v != NULL && size != 0) {
294 1.16 thorpej nv = _PROP_MALLOC(size, M_PROP_DATA);
295 1.16 thorpej if (nv == NULL)
296 1.16 thorpej return (NULL);
297 1.16 thorpej
298 1.16 thorpej memcpy(nv, v, size);
299 1.16 thorpej } else {
300 1.16 thorpej nv = NULL;
301 1.16 thorpej size = 0;
302 1.1 thorpej }
303 1.16 thorpej
304 1.16 thorpej pd = _prop_data_instantiate(0, nv, size);
305 1.16 thorpej if (pd == NULL && nv == NULL)
306 1.16 thorpej _PROP_FREE(nv, M_PROP_DATA);
307 1.16 thorpej
308 1.1 thorpej return (pd);
309 1.1 thorpej }
310 1.1 thorpej
311 1.1 thorpej /*
312 1.16 thorpej * prop_data_create_nocopy --
313 1.16 thorpej * Create a data object using the provided external data reference.
314 1.16 thorpej */
315 1.20 thorpej _PROP_EXPORT prop_data_t
316 1.16 thorpej prop_data_create_nocopy(const void *v, size_t size)
317 1.16 thorpej {
318 1.16 thorpej
319 1.16 thorpej /* Tolerate the creation of empty data objects. */
320 1.16 thorpej if (v == NULL || size == 0) {
321 1.16 thorpej v = NULL;
322 1.16 thorpej size = 0;
323 1.16 thorpej }
324 1.16 thorpej
325 1.16 thorpej return _prop_data_instantiate(PD_F_NOCOPY, v, size);
326 1.16 thorpej }
327 1.16 thorpej
328 1.16 thorpej /*
329 1.1 thorpej * prop_data_copy --
330 1.1 thorpej * Copy a data container. If the original data is external, then
331 1.1 thorpej * the copy is also references the same external data.
332 1.1 thorpej */
333 1.20 thorpej _PROP_EXPORT prop_data_t
334 1.1 thorpej prop_data_copy(prop_data_t opd)
335 1.1 thorpej {
336 1.1 thorpej prop_data_t pd;
337 1.1 thorpej
338 1.3 thorpej if (! prop_object_is_data(opd))
339 1.3 thorpej return (NULL);
340 1.1 thorpej
341 1.16 thorpej if ((opd->pd_flags & PD_F_NOCOPY) != 0 ||
342 1.16 thorpej (opd->pd_flags & PD_F_MUTABLE) == 0) {
343 1.16 thorpej /* Just retain and return the original. */
344 1.16 thorpej prop_object_retain(opd);
345 1.16 thorpej return (opd);
346 1.16 thorpej }
347 1.16 thorpej
348 1.16 thorpej pd = prop_data_create_copy(opd->pd_immutable, opd->pd_size);
349 1.1 thorpej if (pd != NULL) {
350 1.16 thorpej /* Preserve deprecated mutability semantics. */
351 1.16 thorpej pd->pd_flags |= PD_F_MUTABLE;
352 1.1 thorpej }
353 1.16 thorpej
354 1.1 thorpej return (pd);
355 1.1 thorpej }
356 1.1 thorpej
357 1.1 thorpej /*
358 1.1 thorpej * prop_data_size --
359 1.1 thorpej * Return the size of the data.
360 1.1 thorpej */
361 1.20 thorpej _PROP_EXPORT size_t
362 1.1 thorpej prop_data_size(prop_data_t pd)
363 1.1 thorpej {
364 1.1 thorpej
365 1.3 thorpej if (! prop_object_is_data(pd))
366 1.3 thorpej return (0);
367 1.3 thorpej
368 1.1 thorpej return (pd->pd_size);
369 1.1 thorpej }
370 1.1 thorpej
371 1.1 thorpej /*
372 1.16 thorpej * prop_data_value --
373 1.16 thorpej * Returns a pointer to the data object's value. This pointer
374 1.16 thorpej * remains valid only as long as the data object.
375 1.16 thorpej */
376 1.20 thorpej _PROP_EXPORT const void *
377 1.16 thorpej prop_data_value(prop_data_t pd)
378 1.16 thorpej {
379 1.16 thorpej
380 1.16 thorpej if (! prop_object_is_data(pd))
381 1.16 thorpej return (0);
382 1.16 thorpej
383 1.16 thorpej return (pd->pd_immutable);
384 1.16 thorpej }
385 1.16 thorpej
386 1.16 thorpej /*
387 1.16 thorpej * prop_data_copy_value --
388 1.16 thorpej * Copy the data object's value into the supplied buffer.
389 1.1 thorpej */
390 1.20 thorpej _PROP_EXPORT bool
391 1.16 thorpej prop_data_copy_value(prop_data_t pd, void *buf, size_t buflen)
392 1.16 thorpej {
393 1.16 thorpej
394 1.16 thorpej if (! prop_object_is_data(pd))
395 1.16 thorpej return (false);
396 1.18 riastrad
397 1.16 thorpej if (buf == NULL || buflen < pd->pd_size)
398 1.16 thorpej return (false);
399 1.16 thorpej
400 1.16 thorpej /* Tolerate empty data objects. */
401 1.16 thorpej if (pd->pd_immutable == NULL || pd->pd_size == 0)
402 1.16 thorpej return (false);
403 1.16 thorpej
404 1.16 thorpej memcpy(buf, pd->pd_immutable, pd->pd_size);
405 1.16 thorpej
406 1.16 thorpej return (true);
407 1.16 thorpej }
408 1.16 thorpej
409 1.16 thorpej _PROP_DEPRECATED(prop_data_data,
410 1.16 thorpej "this program uses prop_data_data(), "
411 1.16 thorpej "which is deprecated; use prop_data_copy_value() instead.")
412 1.20 thorpej _PROP_EXPORT void *
413 1.1 thorpej prop_data_data(prop_data_t pd)
414 1.1 thorpej {
415 1.1 thorpej void *v;
416 1.1 thorpej
417 1.3 thorpej if (! prop_object_is_data(pd))
418 1.3 thorpej return (NULL);
419 1.1 thorpej
420 1.1 thorpej if (pd->pd_size == 0) {
421 1.1 thorpej _PROP_ASSERT(pd->pd_immutable == NULL);
422 1.1 thorpej return (NULL);
423 1.1 thorpej }
424 1.1 thorpej
425 1.1 thorpej _PROP_ASSERT(pd->pd_immutable != NULL);
426 1.1 thorpej
427 1.1 thorpej v = _PROP_MALLOC(pd->pd_size, M_TEMP);
428 1.1 thorpej if (v != NULL)
429 1.1 thorpej memcpy(v, pd->pd_immutable, pd->pd_size);
430 1.18 riastrad
431 1.1 thorpej return (v);
432 1.1 thorpej }
433 1.1 thorpej
434 1.16 thorpej _PROP_DEPRECATED(prop_data_data_nocopy,
435 1.16 thorpej "this program uses prop_data_data_nocopy(), "
436 1.16 thorpej "which is deprecated; use prop_data_value() instead.")
437 1.20 thorpej _PROP_EXPORT const void *
438 1.1 thorpej prop_data_data_nocopy(prop_data_t pd)
439 1.1 thorpej {
440 1.16 thorpej return prop_data_value(pd);
441 1.1 thorpej }
442 1.1 thorpej
443 1.1 thorpej /*
444 1.1 thorpej * prop_data_equals --
445 1.16 thorpej * Return true if two data objects are equivalent.
446 1.1 thorpej */
447 1.20 thorpej _PROP_EXPORT bool
448 1.1 thorpej prop_data_equals(prop_data_t pd1, prop_data_t pd2)
449 1.1 thorpej {
450 1.9 joerg if (!prop_object_is_data(pd1) || !prop_object_is_data(pd2))
451 1.9 joerg return (false);
452 1.1 thorpej
453 1.9 joerg return (prop_object_equals(pd1, pd2));
454 1.1 thorpej }
455 1.1 thorpej
456 1.1 thorpej /*
457 1.1 thorpej * prop_data_equals_data --
458 1.7 thorpej * Return true if the contained data is equivalent to the specified
459 1.1 thorpej * external data.
460 1.1 thorpej */
461 1.20 thorpej _PROP_EXPORT bool
462 1.1 thorpej prop_data_equals_data(prop_data_t pd, const void *v, size_t size)
463 1.1 thorpej {
464 1.1 thorpej
465 1.3 thorpej if (! prop_object_is_data(pd))
466 1.7 thorpej return (false);
467 1.3 thorpej
468 1.16 thorpej if (pd->pd_size != size || v == NULL)
469 1.7 thorpej return (false);
470 1.16 thorpej
471 1.1 thorpej return (memcmp(pd->pd_immutable, v, size) == 0);
472 1.1 thorpej }
473 1.1 thorpej
474 1.7 thorpej static bool
475 1.1 thorpej _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx,
476 1.1 thorpej uint8_t *target, size_t targsize, size_t *sizep,
477 1.1 thorpej const char **cpp)
478 1.1 thorpej {
479 1.1 thorpej const char *src;
480 1.1 thorpej size_t tarindex;
481 1.1 thorpej int state, ch;
482 1.1 thorpej const char *pos;
483 1.1 thorpej
484 1.1 thorpej state = 0;
485 1.1 thorpej tarindex = 0;
486 1.1 thorpej src = ctx->poic_cp;
487 1.1 thorpej
488 1.1 thorpej for (;;) {
489 1.1 thorpej ch = (unsigned char) *src++;
490 1.1 thorpej if (_PROP_EOF(ch))
491 1.7 thorpej return (false);
492 1.1 thorpej if (_PROP_ISSPACE(ch))
493 1.1 thorpej continue;
494 1.1 thorpej if (ch == '<') {
495 1.1 thorpej src--;
496 1.1 thorpej break;
497 1.1 thorpej }
498 1.1 thorpej if (ch == _prop_data_pad64)
499 1.1 thorpej break;
500 1.1 thorpej
501 1.1 thorpej pos = strchr(_prop_data_base64, ch);
502 1.1 thorpej if (pos == NULL)
503 1.7 thorpej return (false);
504 1.1 thorpej
505 1.1 thorpej switch (state) {
506 1.1 thorpej case 0:
507 1.1 thorpej if (target) {
508 1.1 thorpej if (tarindex >= targsize)
509 1.7 thorpej return (false);
510 1.1 thorpej target[tarindex] =
511 1.5 martin (uint8_t)((pos - _prop_data_base64) << 2);
512 1.1 thorpej }
513 1.1 thorpej state = 1;
514 1.1 thorpej break;
515 1.1 thorpej
516 1.1 thorpej case 1:
517 1.1 thorpej if (target) {
518 1.1 thorpej if (tarindex + 1 >= targsize)
519 1.7 thorpej return (false);
520 1.1 thorpej target[tarindex] |=
521 1.1 thorpej (uint32_t)(pos - _prop_data_base64) >> 4;
522 1.1 thorpej target[tarindex + 1] =
523 1.5 martin (uint8_t)(((pos - _prop_data_base64) & 0xf)
524 1.5 martin << 4);
525 1.1 thorpej }
526 1.1 thorpej tarindex++;
527 1.1 thorpej state = 2;
528 1.1 thorpej break;
529 1.1 thorpej
530 1.1 thorpej case 2:
531 1.1 thorpej if (target) {
532 1.1 thorpej if (tarindex + 1 >= targsize)
533 1.7 thorpej return (false);
534 1.1 thorpej target[tarindex] |=
535 1.1 thorpej (uint32_t)(pos - _prop_data_base64) >> 2;
536 1.1 thorpej target[tarindex + 1] =
537 1.5 martin (uint8_t)(((pos - _prop_data_base64)
538 1.5 martin & 0x3) << 6);
539 1.1 thorpej }
540 1.1 thorpej tarindex++;
541 1.1 thorpej state = 3;
542 1.1 thorpej break;
543 1.1 thorpej
544 1.1 thorpej case 3:
545 1.1 thorpej if (target) {
546 1.1 thorpej if (tarindex >= targsize)
547 1.7 thorpej return (false);
548 1.5 martin target[tarindex] |= (uint8_t)
549 1.5 martin (pos - _prop_data_base64);
550 1.1 thorpej }
551 1.1 thorpej tarindex++;
552 1.1 thorpej state = 0;
553 1.1 thorpej break;
554 1.1 thorpej
555 1.1 thorpej default:
556 1.1 thorpej _PROP_ASSERT(/*CONSTCOND*/0);
557 1.1 thorpej }
558 1.1 thorpej }
559 1.1 thorpej
560 1.1 thorpej /*
561 1.1 thorpej * We are done decoding the Base64 characters. Let's see if we
562 1.1 thorpej * ended up on a byte boundary and/or with unrecognized trailing
563 1.1 thorpej * characters.
564 1.1 thorpej */
565 1.1 thorpej if (ch == _prop_data_pad64) {
566 1.1 thorpej ch = (unsigned char) *src; /* src already advanced */
567 1.1 thorpej if (_PROP_EOF(ch))
568 1.7 thorpej return (false);
569 1.1 thorpej switch (state) {
570 1.1 thorpej case 0: /* Invalid = in first position */
571 1.1 thorpej case 1: /* Invalid = in second position */
572 1.7 thorpej return (false);
573 1.1 thorpej
574 1.1 thorpej case 2: /* Valid, one byte of info */
575 1.1 thorpej /* Skip whitespace */
576 1.1 thorpej for (ch = (unsigned char) *src++;
577 1.1 thorpej ch != '<'; ch = (unsigned char) *src++) {
578 1.1 thorpej if (_PROP_EOF(ch))
579 1.7 thorpej return (false);
580 1.1 thorpej if (!_PROP_ISSPACE(ch))
581 1.1 thorpej break;
582 1.1 thorpej }
583 1.1 thorpej /* Make sure there is another trailing = */
584 1.1 thorpej if (ch != _prop_data_pad64)
585 1.7 thorpej return (false);
586 1.1 thorpej ch = (unsigned char) *src;
587 1.1 thorpej /* FALLTHROUGH */
588 1.18 riastrad
589 1.1 thorpej case 3: /* Valid, two bytes of info */
590 1.1 thorpej /*
591 1.1 thorpej * We know this char is a =. Is there anything but
592 1.1 thorpej * whitespace after it?
593 1.1 thorpej */
594 1.6 dillo for (ch = (unsigned char) *src++;
595 1.6 dillo ch != '<'; ch = (unsigned char) *src++) {
596 1.1 thorpej if (_PROP_EOF(ch))
597 1.7 thorpej return (false);
598 1.1 thorpej if (!_PROP_ISSPACE(ch))
599 1.7 thorpej return (false);
600 1.1 thorpej }
601 1.6 dillo /* back up to '<' */
602 1.6 dillo src--;
603 1.1 thorpej }
604 1.1 thorpej } else {
605 1.1 thorpej /*
606 1.1 thorpej * We ended by seeing the end of the Base64 string. Make
607 1.1 thorpej * sure there are no partial bytes lying around.
608 1.1 thorpej */
609 1.1 thorpej if (state != 0)
610 1.7 thorpej return (false);
611 1.1 thorpej }
612 1.1 thorpej
613 1.1 thorpej _PROP_ASSERT(*src == '<');
614 1.1 thorpej if (sizep != NULL)
615 1.1 thorpej *sizep = tarindex;
616 1.1 thorpej if (cpp != NULL)
617 1.1 thorpej *cpp = src;
618 1.1 thorpej
619 1.7 thorpej return (true);
620 1.1 thorpej }
621 1.1 thorpej
622 1.1 thorpej /*
623 1.1 thorpej * _prop_data_internalize --
624 1.1 thorpej * Parse a <data>...</data> and return the object created from the
625 1.1 thorpej * external representation.
626 1.1 thorpej */
627 1.8 joerg
628 1.8 joerg /* strtoul is used for parsing, enforce. */
629 1.8 joerg typedef int PROP_DATA_ASSERT[/* CONSTCOND */sizeof(size_t) == sizeof(unsigned long) ? 1 : -1];
630 1.8 joerg
631 1.8 joerg /* ARGSUSED */
632 1.8 joerg bool
633 1.8 joerg _prop_data_internalize(prop_stack_t stack, prop_object_t *obj,
634 1.8 joerg struct _prop_object_internalize_context *ctx)
635 1.1 thorpej {
636 1.1 thorpej prop_data_t data;
637 1.1 thorpej uint8_t *buf;
638 1.1 thorpej size_t len, alen;
639 1.1 thorpej
640 1.19 thorpej /* No JSON binary data object representation. */
641 1.19 thorpej if (ctx->poic_format == PROP_FORMAT_JSON) {
642 1.19 thorpej return true;
643 1.19 thorpej }
644 1.19 thorpej
645 1.14 cyber /*
646 1.14 cyber * We don't accept empty elements.
647 1.14 cyber * This actually only checks for the node to be <data/>
648 1.14 cyber * (Which actually causes another error if found.)
649 1.14 cyber */
650 1.1 thorpej if (ctx->poic_is_empty_element)
651 1.8 joerg return (true);
652 1.8 joerg
653 1.1 thorpej /*
654 1.1 thorpej * If we got a "size" attribute, get the size of the data blob
655 1.1 thorpej * from that. Otherwise, we have to figure it out from the base64.
656 1.1 thorpej */
657 1.1 thorpej if (ctx->poic_tagattr != NULL) {
658 1.1 thorpej char *cp;
659 1.1 thorpej
660 1.1 thorpej if (!_PROP_TAGATTR_MATCH(ctx, "size") ||
661 1.1 thorpej ctx->poic_tagattrval_len == 0)
662 1.8 joerg return (true);
663 1.1 thorpej
664 1.1 thorpej #ifndef _KERNEL
665 1.1 thorpej errno = 0;
666 1.1 thorpej #endif
667 1.1 thorpej len = strtoul(ctx->poic_tagattrval, &cp, 0);
668 1.1 thorpej #ifndef _KERNEL /* XXX can't check for ERANGE in the kernel */
669 1.1 thorpej if (len == ULONG_MAX && errno == ERANGE)
670 1.8 joerg return (true);
671 1.1 thorpej #endif
672 1.1 thorpej if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len)
673 1.8 joerg return (true);
674 1.1 thorpej _PROP_ASSERT(*cp == '\"');
675 1.1 thorpej } else if (_prop_data_internalize_decode(ctx, NULL, 0, &len,
676 1.7 thorpej NULL) == false)
677 1.8 joerg return (true);
678 1.1 thorpej
679 1.1 thorpej /*
680 1.1 thorpej * Always allocate one extra in case we don't land on an even byte
681 1.1 thorpej * boundary during the decode.
682 1.1 thorpej */
683 1.1 thorpej buf = _PROP_MALLOC(len + 1, M_PROP_DATA);
684 1.1 thorpej if (buf == NULL)
685 1.8 joerg return (true);
686 1.18 riastrad
687 1.1 thorpej if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen,
688 1.7 thorpej &ctx->poic_cp) == false) {
689 1.1 thorpej _PROP_FREE(buf, M_PROP_DATA);
690 1.8 joerg return (true);
691 1.1 thorpej }
692 1.1 thorpej if (alen != len) {
693 1.1 thorpej _PROP_FREE(buf, M_PROP_DATA);
694 1.8 joerg return (true);
695 1.1 thorpej }
696 1.1 thorpej
697 1.1 thorpej if (_prop_object_internalize_find_tag(ctx, "data",
698 1.7 thorpej _PROP_TAG_TYPE_END) == false) {
699 1.1 thorpej _PROP_FREE(buf, M_PROP_DATA);
700 1.8 joerg return (true);
701 1.1 thorpej }
702 1.1 thorpej
703 1.14 cyber /*
704 1.14 cyber * Handle alternate type of empty node.
705 1.14 cyber * XML document could contain open/close tags, yet still be empty.
706 1.14 cyber */
707 1.14 cyber if (alen == 0) {
708 1.14 cyber _PROP_FREE(buf, M_PROP_DATA);
709 1.16 thorpej buf = NULL;
710 1.14 cyber }
711 1.16 thorpej
712 1.16 thorpej data = _prop_data_instantiate(0, buf, len);
713 1.16 thorpej if (data == NULL && buf != NULL)
714 1.16 thorpej _PROP_FREE(buf, M_PROP_DATA);
715 1.1 thorpej
716 1.8 joerg *obj = data;
717 1.8 joerg return (true);
718 1.1 thorpej }
719