1*3f7675e0Sriastradh /* $NetBSD: prop_data.c,v 1.18 2022/08/03 21:13:46 riastradh Exp $ */
2774eb1a3Sthorpej
3774eb1a3Sthorpej /*-
4a792b843Sthorpej * Copyright (c) 2006, 2020 The NetBSD Foundation, Inc.
5774eb1a3Sthorpej * All rights reserved.
6774eb1a3Sthorpej *
7774eb1a3Sthorpej * This code is derived from software contributed to The NetBSD Foundation
8774eb1a3Sthorpej * by Jason R. Thorpe.
9774eb1a3Sthorpej *
10774eb1a3Sthorpej * Redistribution and use in source and binary forms, with or without
11774eb1a3Sthorpej * modification, are permitted provided that the following conditions
12774eb1a3Sthorpej * are met:
13774eb1a3Sthorpej * 1. Redistributions of source code must retain the above copyright
14774eb1a3Sthorpej * notice, this list of conditions and the following disclaimer.
15774eb1a3Sthorpej * 2. Redistributions in binary form must reproduce the above copyright
16774eb1a3Sthorpej * notice, this list of conditions and the following disclaimer in the
17774eb1a3Sthorpej * documentation and/or other materials provided with the distribution.
18774eb1a3Sthorpej *
19774eb1a3Sthorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20774eb1a3Sthorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21774eb1a3Sthorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22774eb1a3Sthorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23774eb1a3Sthorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24774eb1a3Sthorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25774eb1a3Sthorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26774eb1a3Sthorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27774eb1a3Sthorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28774eb1a3Sthorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29774eb1a3Sthorpej * POSSIBILITY OF SUCH DAMAGE.
30774eb1a3Sthorpej */
31774eb1a3Sthorpej
32774eb1a3Sthorpej #include "prop_object_impl.h"
338319f966Sthorpej #include <prop/prop_data.h>
34774eb1a3Sthorpej
35774eb1a3Sthorpej #if defined(_KERNEL)
36774eb1a3Sthorpej #include <sys/systm.h>
37774eb1a3Sthorpej #elif defined(_STANDALONE)
38774eb1a3Sthorpej #include <sys/param.h>
39774eb1a3Sthorpej #include <lib/libkern/libkern.h>
40774eb1a3Sthorpej #else
41774eb1a3Sthorpej #include <errno.h>
42774eb1a3Sthorpej #include <limits.h>
43774eb1a3Sthorpej #include <stdlib.h>
44774eb1a3Sthorpej #endif
45774eb1a3Sthorpej
46774eb1a3Sthorpej struct _prop_data {
47774eb1a3Sthorpej struct _prop_object pd_obj;
48774eb1a3Sthorpej union {
49774eb1a3Sthorpej void * pdu_mutable;
50774eb1a3Sthorpej const void * pdu_immutable;
51774eb1a3Sthorpej } pd_un;
52774eb1a3Sthorpej #define pd_mutable pd_un.pdu_mutable
53774eb1a3Sthorpej #define pd_immutable pd_un.pdu_immutable
54774eb1a3Sthorpej size_t pd_size;
55774eb1a3Sthorpej int pd_flags;
56774eb1a3Sthorpej };
57774eb1a3Sthorpej
58774eb1a3Sthorpej #define PD_F_NOCOPY 0x01
59a792b843Sthorpej #define PD_F_MUTABLE 0x02
60774eb1a3Sthorpej
61774eb1a3Sthorpej _PROP_POOL_INIT(_prop_data_pool, sizeof(struct _prop_data), "propdata")
62774eb1a3Sthorpej
63774eb1a3Sthorpej _PROP_MALLOC_DEFINE(M_PROP_DATA, "prop data",
64774eb1a3Sthorpej "property data container object")
65774eb1a3Sthorpej
664ce0dc3aSthorpej static _prop_object_free_rv_t
674ce0dc3aSthorpej _prop_data_free(prop_stack_t, prop_object_t *);
6804377267Sthorpej static bool _prop_data_externalize(
693e69f1b2Sthorpej struct _prop_object_externalize_context *,
703e69f1b2Sthorpej void *);
714ce0dc3aSthorpej static _prop_object_equals_rv_t
724ce0dc3aSthorpej _prop_data_equals(prop_object_t, prop_object_t,
734deb5931Sjoerg void **, void **,
744deb5931Sjoerg prop_object_t *, prop_object_t *);
753e69f1b2Sthorpej
763e69f1b2Sthorpej static const struct _prop_object_type _prop_object_type_data = {
773e69f1b2Sthorpej .pot_type = PROP_TYPE_DATA,
783e69f1b2Sthorpej .pot_free = _prop_data_free,
793e69f1b2Sthorpej .pot_extern = _prop_data_externalize,
803e69f1b2Sthorpej .pot_equals = _prop_data_equals,
813e69f1b2Sthorpej };
823e69f1b2Sthorpej
833e69f1b2Sthorpej #define prop_object_is_data(x) \
84beabdd9bSthorpej ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_data)
85774eb1a3Sthorpej
86e835604cSjoerg /* ARGSUSED */
874ce0dc3aSthorpej static _prop_object_free_rv_t
_prop_data_free(prop_stack_t stack,prop_object_t * obj)88e835604cSjoerg _prop_data_free(prop_stack_t stack, prop_object_t *obj)
89774eb1a3Sthorpej {
90e835604cSjoerg prop_data_t pd = *obj;
91774eb1a3Sthorpej
92774eb1a3Sthorpej if ((pd->pd_flags & PD_F_NOCOPY) == 0 && pd->pd_mutable != NULL)
93774eb1a3Sthorpej _PROP_FREE(pd->pd_mutable, M_PROP_DATA);
94e835604cSjoerg _PROP_POOL_PUT(_prop_data_pool, pd);
95e835604cSjoerg
96e835604cSjoerg return (_PROP_OBJECT_FREE_DONE);
97774eb1a3Sthorpej }
98774eb1a3Sthorpej
99774eb1a3Sthorpej static const char _prop_data_base64[] =
100774eb1a3Sthorpej "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
101774eb1a3Sthorpej static const char _prop_data_pad64 = '=';
102774eb1a3Sthorpej
10304377267Sthorpej static bool
_prop_data_externalize(struct _prop_object_externalize_context * ctx,void * v)104774eb1a3Sthorpej _prop_data_externalize(struct _prop_object_externalize_context *ctx, void *v)
105774eb1a3Sthorpej {
106774eb1a3Sthorpej prop_data_t pd = v;
107774eb1a3Sthorpej size_t i, srclen;
108774eb1a3Sthorpej const uint8_t *src;
109774eb1a3Sthorpej uint8_t output[4];
110774eb1a3Sthorpej uint8_t input[3];
111774eb1a3Sthorpej
112774eb1a3Sthorpej if (pd->pd_size == 0)
113774eb1a3Sthorpej return (_prop_object_externalize_empty_tag(ctx, "data"));
114774eb1a3Sthorpej
11504377267Sthorpej if (_prop_object_externalize_start_tag(ctx, "data") == false)
11604377267Sthorpej return (false);
117774eb1a3Sthorpej
118774eb1a3Sthorpej for (src = pd->pd_immutable, srclen = pd->pd_size;
119774eb1a3Sthorpej srclen > 2; srclen -= 3) {
120774eb1a3Sthorpej input[0] = *src++;
121774eb1a3Sthorpej input[1] = *src++;
122774eb1a3Sthorpej input[2] = *src++;
123774eb1a3Sthorpej
124774eb1a3Sthorpej output[0] = (uint32_t)input[0] >> 2;
125774eb1a3Sthorpej output[1] = ((uint32_t)(input[0] & 0x03) << 4) +
126774eb1a3Sthorpej ((uint32_t)input[1] >> 4);
127267197ecSapb output[2] = ((uint32_t)(input[1] & 0x0f) << 2) +
128774eb1a3Sthorpej ((uint32_t)input[2] >> 6);
129774eb1a3Sthorpej output[3] = input[2] & 0x3f;
130774eb1a3Sthorpej _PROP_ASSERT(output[0] < 64);
131774eb1a3Sthorpej _PROP_ASSERT(output[1] < 64);
132774eb1a3Sthorpej _PROP_ASSERT(output[2] < 64);
133774eb1a3Sthorpej _PROP_ASSERT(output[3] < 64);
134774eb1a3Sthorpej
135774eb1a3Sthorpej if (_prop_object_externalize_append_char(ctx,
13604377267Sthorpej _prop_data_base64[output[0]]) == false ||
137774eb1a3Sthorpej _prop_object_externalize_append_char(ctx,
13804377267Sthorpej _prop_data_base64[output[1]]) == false ||
139774eb1a3Sthorpej _prop_object_externalize_append_char(ctx,
14004377267Sthorpej _prop_data_base64[output[2]]) == false ||
141774eb1a3Sthorpej _prop_object_externalize_append_char(ctx,
14204377267Sthorpej _prop_data_base64[output[3]]) == false)
14304377267Sthorpej return (false);
144774eb1a3Sthorpej }
145774eb1a3Sthorpej
146774eb1a3Sthorpej if (srclen != 0) {
147774eb1a3Sthorpej input[0] = input[1] = input[2] = '\0';
148774eb1a3Sthorpej for (i = 0; i < srclen; i++)
149774eb1a3Sthorpej input[i] = *src++;
150774eb1a3Sthorpej
151774eb1a3Sthorpej output[0] = (uint32_t)input[0] >> 2;
152774eb1a3Sthorpej output[1] = ((uint32_t)(input[0] & 0x03) << 4) +
153774eb1a3Sthorpej ((uint32_t)input[1] >> 4);
154267197ecSapb output[2] = ((uint32_t)(input[1] & 0x0f) << 2) +
155774eb1a3Sthorpej ((uint32_t)input[2] >> 6);
156774eb1a3Sthorpej _PROP_ASSERT(output[0] < 64);
157774eb1a3Sthorpej _PROP_ASSERT(output[1] < 64);
158774eb1a3Sthorpej _PROP_ASSERT(output[2] < 64);
159774eb1a3Sthorpej
160774eb1a3Sthorpej if (_prop_object_externalize_append_char(ctx,
16104377267Sthorpej _prop_data_base64[output[0]]) == false ||
162774eb1a3Sthorpej _prop_object_externalize_append_char(ctx,
16304377267Sthorpej _prop_data_base64[output[1]]) == false ||
164774eb1a3Sthorpej _prop_object_externalize_append_char(ctx,
165774eb1a3Sthorpej srclen == 1 ? _prop_data_pad64
16604377267Sthorpej : _prop_data_base64[output[2]]) == false ||
167774eb1a3Sthorpej _prop_object_externalize_append_char(ctx,
16804377267Sthorpej _prop_data_pad64) == false)
16904377267Sthorpej return (false);
170774eb1a3Sthorpej }
171774eb1a3Sthorpej
17204377267Sthorpej if (_prop_object_externalize_end_tag(ctx, "data") == false)
17304377267Sthorpej return (false);
174774eb1a3Sthorpej
17504377267Sthorpej return (true);
176774eb1a3Sthorpej }
177774eb1a3Sthorpej
1784deb5931Sjoerg /* ARGSUSED */
1794ce0dc3aSthorpej static _prop_object_equals_rv_t
_prop_data_equals(prop_object_t v1,prop_object_t v2,void ** stored_pointer1,void ** stored_pointer2,prop_object_t * next_obj1,prop_object_t * next_obj2)1804deb5931Sjoerg _prop_data_equals(prop_object_t v1, prop_object_t v2,
1814deb5931Sjoerg void **stored_pointer1, void **stored_pointer2,
1824deb5931Sjoerg prop_object_t *next_obj1, prop_object_t *next_obj2)
1833e69f1b2Sthorpej {
1843e69f1b2Sthorpej prop_data_t pd1 = v1;
1853e69f1b2Sthorpej prop_data_t pd2 = v2;
1863e69f1b2Sthorpej
1873e69f1b2Sthorpej if (pd1 == pd2)
1884deb5931Sjoerg return (_PROP_OBJECT_EQUALS_TRUE);
1893e69f1b2Sthorpej if (pd1->pd_size != pd2->pd_size)
1904deb5931Sjoerg return (_PROP_OBJECT_EQUALS_FALSE);
1913e69f1b2Sthorpej if (pd1->pd_size == 0) {
1923e69f1b2Sthorpej _PROP_ASSERT(pd1->pd_immutable == NULL);
1933e69f1b2Sthorpej _PROP_ASSERT(pd2->pd_immutable == NULL);
1944deb5931Sjoerg return (_PROP_OBJECT_EQUALS_TRUE);
1953e69f1b2Sthorpej }
1964deb5931Sjoerg if (memcmp(pd1->pd_immutable, pd2->pd_immutable, pd1->pd_size) == 0)
1974deb5931Sjoerg return _PROP_OBJECT_EQUALS_TRUE;
1984deb5931Sjoerg else
1994deb5931Sjoerg return _PROP_OBJECT_EQUALS_FALSE;
2003e69f1b2Sthorpej }
2013e69f1b2Sthorpej
202774eb1a3Sthorpej static prop_data_t
_prop_data_alloc(int const flags)203a792b843Sthorpej _prop_data_alloc(int const flags)
204774eb1a3Sthorpej {
205774eb1a3Sthorpej prop_data_t pd;
206774eb1a3Sthorpej
207774eb1a3Sthorpej pd = _PROP_POOL_GET(_prop_data_pool);
208774eb1a3Sthorpej if (pd != NULL) {
2093e69f1b2Sthorpej _prop_object_init(&pd->pd_obj, &_prop_object_type_data);
210774eb1a3Sthorpej
211774eb1a3Sthorpej pd->pd_mutable = NULL;
212774eb1a3Sthorpej pd->pd_size = 0;
213a792b843Sthorpej pd->pd_flags = flags;
214774eb1a3Sthorpej }
215774eb1a3Sthorpej
216774eb1a3Sthorpej return (pd);
217774eb1a3Sthorpej }
218774eb1a3Sthorpej
219a792b843Sthorpej static prop_data_t
_prop_data_instantiate(int const flags,const void * const data,size_t const len)220a792b843Sthorpej _prop_data_instantiate(int const flags, const void * const data,
221a792b843Sthorpej size_t const len)
222a792b843Sthorpej {
223a792b843Sthorpej prop_data_t pd;
224a792b843Sthorpej
225a792b843Sthorpej pd = _prop_data_alloc(flags);
226a792b843Sthorpej if (pd != NULL) {
227a792b843Sthorpej pd->pd_immutable = data;
228a792b843Sthorpej pd->pd_size = len;
229a792b843Sthorpej }
230a792b843Sthorpej
231a792b843Sthorpej return (pd);
232a792b843Sthorpej }
233a792b843Sthorpej
234a792b843Sthorpej _PROP_DEPRECATED(prop_data_create_data,
235a792b843Sthorpej "this program uses prop_data_create_data(); all functions "
236a792b843Sthorpej "supporting mutable prop_data objects are deprecated.")
237774eb1a3Sthorpej prop_data_t
prop_data_create_data(const void * v,size_t size)238774eb1a3Sthorpej prop_data_create_data(const void *v, size_t size)
239774eb1a3Sthorpej {
240774eb1a3Sthorpej prop_data_t pd;
241774eb1a3Sthorpej void *nv;
242774eb1a3Sthorpej
243a792b843Sthorpej pd = _prop_data_alloc(PD_F_MUTABLE);
244848e7ffeSdyoung if (pd != NULL && size != 0) {
245774eb1a3Sthorpej nv = _PROP_MALLOC(size, M_PROP_DATA);
246774eb1a3Sthorpej if (nv == NULL) {
247e835604cSjoerg prop_object_release(pd);
248774eb1a3Sthorpej return (NULL);
249774eb1a3Sthorpej }
250774eb1a3Sthorpej memcpy(nv, v, size);
251774eb1a3Sthorpej pd->pd_mutable = nv;
252774eb1a3Sthorpej pd->pd_size = size;
253774eb1a3Sthorpej }
254774eb1a3Sthorpej return (pd);
255774eb1a3Sthorpej }
256774eb1a3Sthorpej
257a792b843Sthorpej _PROP_DEPRECATED(prop_data_create_data_nocopy,
258a792b843Sthorpej "this program uses prop_data_create_data_nocopy(), "
259a792b843Sthorpej "which is deprecated; use prop_data_create_nocopy() instead.")
260774eb1a3Sthorpej prop_data_t
prop_data_create_data_nocopy(const void * v,size_t size)261774eb1a3Sthorpej prop_data_create_data_nocopy(const void *v, size_t size)
262774eb1a3Sthorpej {
263a792b843Sthorpej return prop_data_create_nocopy(v, size);
264774eb1a3Sthorpej }
265a792b843Sthorpej
266a792b843Sthorpej /*
267a792b843Sthorpej * prop_data_create_copy --
268a792b843Sthorpej * Create a data object with a copy of the provided data.
269a792b843Sthorpej */
270a792b843Sthorpej prop_data_t
prop_data_create_copy(const void * v,size_t size)271a792b843Sthorpej prop_data_create_copy(const void *v, size_t size)
272a792b843Sthorpej {
273a792b843Sthorpej prop_data_t pd;
274a792b843Sthorpej void *nv;
275a792b843Sthorpej
276a792b843Sthorpej /* Tolerate the creation of empty data objects. */
277a792b843Sthorpej if (v != NULL && size != 0) {
278a792b843Sthorpej nv = _PROP_MALLOC(size, M_PROP_DATA);
279a792b843Sthorpej if (nv == NULL)
280a792b843Sthorpej return (NULL);
281a792b843Sthorpej
282a792b843Sthorpej memcpy(nv, v, size);
283a792b843Sthorpej } else {
284a792b843Sthorpej nv = NULL;
285a792b843Sthorpej size = 0;
286a792b843Sthorpej }
287a792b843Sthorpej
288a792b843Sthorpej pd = _prop_data_instantiate(0, nv, size);
289a792b843Sthorpej if (pd == NULL && nv == NULL)
290a792b843Sthorpej _PROP_FREE(nv, M_PROP_DATA);
291a792b843Sthorpej
292774eb1a3Sthorpej return (pd);
293774eb1a3Sthorpej }
294774eb1a3Sthorpej
295774eb1a3Sthorpej /*
296a792b843Sthorpej * prop_data_create_nocopy --
297a792b843Sthorpej * Create a data object using the provided external data reference.
298a792b843Sthorpej */
299a792b843Sthorpej prop_data_t
prop_data_create_nocopy(const void * v,size_t size)300a792b843Sthorpej prop_data_create_nocopy(const void *v, size_t size)
301a792b843Sthorpej {
302a792b843Sthorpej
303a792b843Sthorpej /* Tolerate the creation of empty data objects. */
304a792b843Sthorpej if (v == NULL || size == 0) {
305a792b843Sthorpej v = NULL;
306a792b843Sthorpej size = 0;
307a792b843Sthorpej }
308a792b843Sthorpej
309a792b843Sthorpej return _prop_data_instantiate(PD_F_NOCOPY, v, size);
310a792b843Sthorpej }
311a792b843Sthorpej
312a792b843Sthorpej /*
313774eb1a3Sthorpej * prop_data_copy --
314774eb1a3Sthorpej * Copy a data container. If the original data is external, then
315774eb1a3Sthorpej * the copy is also references the same external data.
316774eb1a3Sthorpej */
317774eb1a3Sthorpej prop_data_t
prop_data_copy(prop_data_t opd)318774eb1a3Sthorpej prop_data_copy(prop_data_t opd)
319774eb1a3Sthorpej {
320774eb1a3Sthorpej prop_data_t pd;
321774eb1a3Sthorpej
322d21620b2Sthorpej if (! prop_object_is_data(opd))
323d21620b2Sthorpej return (NULL);
324774eb1a3Sthorpej
325a792b843Sthorpej if ((opd->pd_flags & PD_F_NOCOPY) != 0 ||
326a792b843Sthorpej (opd->pd_flags & PD_F_MUTABLE) == 0) {
327a792b843Sthorpej /* Just retain and return the original. */
328a792b843Sthorpej prop_object_retain(opd);
329a792b843Sthorpej return (opd);
330a792b843Sthorpej }
331a792b843Sthorpej
332a792b843Sthorpej pd = prop_data_create_copy(opd->pd_immutable, opd->pd_size);
333774eb1a3Sthorpej if (pd != NULL) {
334a792b843Sthorpej /* Preserve deprecated mutability semantics. */
335a792b843Sthorpej pd->pd_flags |= PD_F_MUTABLE;
336774eb1a3Sthorpej }
337a792b843Sthorpej
338774eb1a3Sthorpej return (pd);
339774eb1a3Sthorpej }
340774eb1a3Sthorpej
341774eb1a3Sthorpej /*
342774eb1a3Sthorpej * prop_data_size --
343774eb1a3Sthorpej * Return the size of the data.
344774eb1a3Sthorpej */
345774eb1a3Sthorpej size_t
prop_data_size(prop_data_t pd)346774eb1a3Sthorpej prop_data_size(prop_data_t pd)
347774eb1a3Sthorpej {
348774eb1a3Sthorpej
349d21620b2Sthorpej if (! prop_object_is_data(pd))
350d21620b2Sthorpej return (0);
351d21620b2Sthorpej
352774eb1a3Sthorpej return (pd->pd_size);
353774eb1a3Sthorpej }
354774eb1a3Sthorpej
355774eb1a3Sthorpej /*
356a792b843Sthorpej * prop_data_value --
357a792b843Sthorpej * Returns a pointer to the data object's value. This pointer
358a792b843Sthorpej * remains valid only as long as the data object.
359774eb1a3Sthorpej */
360a792b843Sthorpej const void *
prop_data_value(prop_data_t pd)361a792b843Sthorpej prop_data_value(prop_data_t pd)
362a792b843Sthorpej {
363a792b843Sthorpej
364a792b843Sthorpej if (! prop_object_is_data(pd))
365a792b843Sthorpej return (0);
366a792b843Sthorpej
367a792b843Sthorpej return (pd->pd_immutable);
368a792b843Sthorpej }
369a792b843Sthorpej
370a792b843Sthorpej /*
371a792b843Sthorpej * prop_data_copy_value --
372a792b843Sthorpej * Copy the data object's value into the supplied buffer.
373a792b843Sthorpej */
374a792b843Sthorpej bool
prop_data_copy_value(prop_data_t pd,void * buf,size_t buflen)375a792b843Sthorpej prop_data_copy_value(prop_data_t pd, void *buf, size_t buflen)
376a792b843Sthorpej {
377a792b843Sthorpej
378a792b843Sthorpej if (! prop_object_is_data(pd))
379a792b843Sthorpej return (false);
380a792b843Sthorpej
381a792b843Sthorpej if (buf == NULL || buflen < pd->pd_size)
382a792b843Sthorpej return (false);
383a792b843Sthorpej
384a792b843Sthorpej /* Tolerate empty data objects. */
385a792b843Sthorpej if (pd->pd_immutable == NULL || pd->pd_size == 0)
386a792b843Sthorpej return (false);
387a792b843Sthorpej
388a792b843Sthorpej memcpy(buf, pd->pd_immutable, pd->pd_size);
389a792b843Sthorpej
390a792b843Sthorpej return (true);
391a792b843Sthorpej }
392a792b843Sthorpej
393a792b843Sthorpej _PROP_DEPRECATED(prop_data_data,
394a792b843Sthorpej "this program uses prop_data_data(), "
395a792b843Sthorpej "which is deprecated; use prop_data_copy_value() instead.")
396774eb1a3Sthorpej void *
prop_data_data(prop_data_t pd)397774eb1a3Sthorpej prop_data_data(prop_data_t pd)
398774eb1a3Sthorpej {
399774eb1a3Sthorpej void *v;
400774eb1a3Sthorpej
401d21620b2Sthorpej if (! prop_object_is_data(pd))
402d21620b2Sthorpej return (NULL);
403774eb1a3Sthorpej
404774eb1a3Sthorpej if (pd->pd_size == 0) {
405774eb1a3Sthorpej _PROP_ASSERT(pd->pd_immutable == NULL);
406774eb1a3Sthorpej return (NULL);
407774eb1a3Sthorpej }
408774eb1a3Sthorpej
409774eb1a3Sthorpej _PROP_ASSERT(pd->pd_immutable != NULL);
410774eb1a3Sthorpej
411774eb1a3Sthorpej v = _PROP_MALLOC(pd->pd_size, M_TEMP);
412774eb1a3Sthorpej if (v != NULL)
413774eb1a3Sthorpej memcpy(v, pd->pd_immutable, pd->pd_size);
414774eb1a3Sthorpej
415774eb1a3Sthorpej return (v);
416774eb1a3Sthorpej }
417774eb1a3Sthorpej
418a792b843Sthorpej _PROP_DEPRECATED(prop_data_data_nocopy,
419a792b843Sthorpej "this program uses prop_data_data_nocopy(), "
420a792b843Sthorpej "which is deprecated; use prop_data_value() instead.")
421774eb1a3Sthorpej const void *
prop_data_data_nocopy(prop_data_t pd)422774eb1a3Sthorpej prop_data_data_nocopy(prop_data_t pd)
423774eb1a3Sthorpej {
424a792b843Sthorpej return prop_data_value(pd);
425774eb1a3Sthorpej }
426774eb1a3Sthorpej
427774eb1a3Sthorpej /*
428774eb1a3Sthorpej * prop_data_equals --
429a792b843Sthorpej * Return true if two data objects are equivalent.
430774eb1a3Sthorpej */
43104377267Sthorpej bool
prop_data_equals(prop_data_t pd1,prop_data_t pd2)432774eb1a3Sthorpej prop_data_equals(prop_data_t pd1, prop_data_t pd2)
433774eb1a3Sthorpej {
4344deb5931Sjoerg if (!prop_object_is_data(pd1) || !prop_object_is_data(pd2))
4354deb5931Sjoerg return (false);
436774eb1a3Sthorpej
4374deb5931Sjoerg return (prop_object_equals(pd1, pd2));
438774eb1a3Sthorpej }
439774eb1a3Sthorpej
440774eb1a3Sthorpej /*
441774eb1a3Sthorpej * prop_data_equals_data --
44204377267Sthorpej * Return true if the contained data is equivalent to the specified
443774eb1a3Sthorpej * external data.
444774eb1a3Sthorpej */
44504377267Sthorpej bool
prop_data_equals_data(prop_data_t pd,const void * v,size_t size)446774eb1a3Sthorpej prop_data_equals_data(prop_data_t pd, const void *v, size_t size)
447774eb1a3Sthorpej {
448774eb1a3Sthorpej
449d21620b2Sthorpej if (! prop_object_is_data(pd))
45004377267Sthorpej return (false);
451d21620b2Sthorpej
452a792b843Sthorpej if (pd->pd_size != size || v == NULL)
45304377267Sthorpej return (false);
454a792b843Sthorpej
455774eb1a3Sthorpej return (memcmp(pd->pd_immutable, v, size) == 0);
456774eb1a3Sthorpej }
457774eb1a3Sthorpej
45804377267Sthorpej static bool
_prop_data_internalize_decode(struct _prop_object_internalize_context * ctx,uint8_t * target,size_t targsize,size_t * sizep,const char ** cpp)459774eb1a3Sthorpej _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx,
460774eb1a3Sthorpej uint8_t *target, size_t targsize, size_t *sizep,
461774eb1a3Sthorpej const char **cpp)
462774eb1a3Sthorpej {
463774eb1a3Sthorpej const char *src;
464774eb1a3Sthorpej size_t tarindex;
465774eb1a3Sthorpej int state, ch;
466774eb1a3Sthorpej const char *pos;
467774eb1a3Sthorpej
468774eb1a3Sthorpej state = 0;
469774eb1a3Sthorpej tarindex = 0;
470774eb1a3Sthorpej src = ctx->poic_cp;
471774eb1a3Sthorpej
472774eb1a3Sthorpej for (;;) {
473774eb1a3Sthorpej ch = (unsigned char) *src++;
474774eb1a3Sthorpej if (_PROP_EOF(ch))
47504377267Sthorpej return (false);
476774eb1a3Sthorpej if (_PROP_ISSPACE(ch))
477774eb1a3Sthorpej continue;
478774eb1a3Sthorpej if (ch == '<') {
479774eb1a3Sthorpej src--;
480774eb1a3Sthorpej break;
481774eb1a3Sthorpej }
482774eb1a3Sthorpej if (ch == _prop_data_pad64)
483774eb1a3Sthorpej break;
484774eb1a3Sthorpej
485774eb1a3Sthorpej pos = strchr(_prop_data_base64, ch);
486774eb1a3Sthorpej if (pos == NULL)
48704377267Sthorpej return (false);
488774eb1a3Sthorpej
489774eb1a3Sthorpej switch (state) {
490774eb1a3Sthorpej case 0:
491774eb1a3Sthorpej if (target) {
492774eb1a3Sthorpej if (tarindex >= targsize)
49304377267Sthorpej return (false);
494774eb1a3Sthorpej target[tarindex] =
495b6f68b37Smartin (uint8_t)((pos - _prop_data_base64) << 2);
496774eb1a3Sthorpej }
497774eb1a3Sthorpej state = 1;
498774eb1a3Sthorpej break;
499774eb1a3Sthorpej
500774eb1a3Sthorpej case 1:
501774eb1a3Sthorpej if (target) {
502774eb1a3Sthorpej if (tarindex + 1 >= targsize)
50304377267Sthorpej return (false);
504774eb1a3Sthorpej target[tarindex] |=
505774eb1a3Sthorpej (uint32_t)(pos - _prop_data_base64) >> 4;
506774eb1a3Sthorpej target[tarindex + 1] =
507b6f68b37Smartin (uint8_t)(((pos - _prop_data_base64) & 0xf)
508b6f68b37Smartin << 4);
509774eb1a3Sthorpej }
510774eb1a3Sthorpej tarindex++;
511774eb1a3Sthorpej state = 2;
512774eb1a3Sthorpej break;
513774eb1a3Sthorpej
514774eb1a3Sthorpej case 2:
515774eb1a3Sthorpej if (target) {
516774eb1a3Sthorpej if (tarindex + 1 >= targsize)
51704377267Sthorpej return (false);
518774eb1a3Sthorpej target[tarindex] |=
519774eb1a3Sthorpej (uint32_t)(pos - _prop_data_base64) >> 2;
520774eb1a3Sthorpej target[tarindex + 1] =
521b6f68b37Smartin (uint8_t)(((pos - _prop_data_base64)
522b6f68b37Smartin & 0x3) << 6);
523774eb1a3Sthorpej }
524774eb1a3Sthorpej tarindex++;
525774eb1a3Sthorpej state = 3;
526774eb1a3Sthorpej break;
527774eb1a3Sthorpej
528774eb1a3Sthorpej case 3:
529774eb1a3Sthorpej if (target) {
530774eb1a3Sthorpej if (tarindex >= targsize)
53104377267Sthorpej return (false);
532b6f68b37Smartin target[tarindex] |= (uint8_t)
533b6f68b37Smartin (pos - _prop_data_base64);
534774eb1a3Sthorpej }
535774eb1a3Sthorpej tarindex++;
536774eb1a3Sthorpej state = 0;
537774eb1a3Sthorpej break;
538774eb1a3Sthorpej
539774eb1a3Sthorpej default:
540774eb1a3Sthorpej _PROP_ASSERT(/*CONSTCOND*/0);
541774eb1a3Sthorpej }
542774eb1a3Sthorpej }
543774eb1a3Sthorpej
544774eb1a3Sthorpej /*
545774eb1a3Sthorpej * We are done decoding the Base64 characters. Let's see if we
546774eb1a3Sthorpej * ended up on a byte boundary and/or with unrecognized trailing
547774eb1a3Sthorpej * characters.
548774eb1a3Sthorpej */
549774eb1a3Sthorpej if (ch == _prop_data_pad64) {
550774eb1a3Sthorpej ch = (unsigned char) *src; /* src already advanced */
551774eb1a3Sthorpej if (_PROP_EOF(ch))
55204377267Sthorpej return (false);
553774eb1a3Sthorpej switch (state) {
554774eb1a3Sthorpej case 0: /* Invalid = in first position */
555774eb1a3Sthorpej case 1: /* Invalid = in second position */
55604377267Sthorpej return (false);
557774eb1a3Sthorpej
558774eb1a3Sthorpej case 2: /* Valid, one byte of info */
559774eb1a3Sthorpej /* Skip whitespace */
560774eb1a3Sthorpej for (ch = (unsigned char) *src++;
561774eb1a3Sthorpej ch != '<'; ch = (unsigned char) *src++) {
562774eb1a3Sthorpej if (_PROP_EOF(ch))
56304377267Sthorpej return (false);
564774eb1a3Sthorpej if (!_PROP_ISSPACE(ch))
565774eb1a3Sthorpej break;
566774eb1a3Sthorpej }
567774eb1a3Sthorpej /* Make sure there is another trailing = */
568774eb1a3Sthorpej if (ch != _prop_data_pad64)
56904377267Sthorpej return (false);
570774eb1a3Sthorpej ch = (unsigned char) *src;
571774eb1a3Sthorpej /* FALLTHROUGH */
572774eb1a3Sthorpej
573774eb1a3Sthorpej case 3: /* Valid, two bytes of info */
574774eb1a3Sthorpej /*
575774eb1a3Sthorpej * We know this char is a =. Is there anything but
576774eb1a3Sthorpej * whitespace after it?
577774eb1a3Sthorpej */
5783827e4b3Sdillo for (ch = (unsigned char) *src++;
5793827e4b3Sdillo ch != '<'; ch = (unsigned char) *src++) {
580774eb1a3Sthorpej if (_PROP_EOF(ch))
58104377267Sthorpej return (false);
582774eb1a3Sthorpej if (!_PROP_ISSPACE(ch))
58304377267Sthorpej return (false);
584774eb1a3Sthorpej }
5853827e4b3Sdillo /* back up to '<' */
5863827e4b3Sdillo src--;
587774eb1a3Sthorpej }
588774eb1a3Sthorpej } else {
589774eb1a3Sthorpej /*
590774eb1a3Sthorpej * We ended by seeing the end of the Base64 string. Make
591774eb1a3Sthorpej * sure there are no partial bytes lying around.
592774eb1a3Sthorpej */
593774eb1a3Sthorpej if (state != 0)
59404377267Sthorpej return (false);
595774eb1a3Sthorpej }
596774eb1a3Sthorpej
597774eb1a3Sthorpej _PROP_ASSERT(*src == '<');
598774eb1a3Sthorpej if (sizep != NULL)
599774eb1a3Sthorpej *sizep = tarindex;
600774eb1a3Sthorpej if (cpp != NULL)
601774eb1a3Sthorpej *cpp = src;
602774eb1a3Sthorpej
60304377267Sthorpej return (true);
604774eb1a3Sthorpej }
605774eb1a3Sthorpej
606774eb1a3Sthorpej /*
607774eb1a3Sthorpej * _prop_data_internalize --
608774eb1a3Sthorpej * Parse a <data>...</data> and return the object created from the
609774eb1a3Sthorpej * external representation.
610774eb1a3Sthorpej */
611e835604cSjoerg
612e835604cSjoerg /* strtoul is used for parsing, enforce. */
613e835604cSjoerg typedef int PROP_DATA_ASSERT[/* CONSTCOND */sizeof(size_t) == sizeof(unsigned long) ? 1 : -1];
614e835604cSjoerg
615e835604cSjoerg /* ARGSUSED */
616e835604cSjoerg bool
_prop_data_internalize(prop_stack_t stack,prop_object_t * obj,struct _prop_object_internalize_context * ctx)617e835604cSjoerg _prop_data_internalize(prop_stack_t stack, prop_object_t *obj,
618e835604cSjoerg struct _prop_object_internalize_context *ctx)
619774eb1a3Sthorpej {
620774eb1a3Sthorpej prop_data_t data;
621774eb1a3Sthorpej uint8_t *buf;
622774eb1a3Sthorpej size_t len, alen;
623774eb1a3Sthorpej
62423dc01f7Scyber /*
62523dc01f7Scyber * We don't accept empty elements.
62623dc01f7Scyber * This actually only checks for the node to be <data/>
62723dc01f7Scyber * (Which actually causes another error if found.)
62823dc01f7Scyber */
629774eb1a3Sthorpej if (ctx->poic_is_empty_element)
630e835604cSjoerg return (true);
631774eb1a3Sthorpej
632774eb1a3Sthorpej /*
633774eb1a3Sthorpej * If we got a "size" attribute, get the size of the data blob
634774eb1a3Sthorpej * from that. Otherwise, we have to figure it out from the base64.
635774eb1a3Sthorpej */
636774eb1a3Sthorpej if (ctx->poic_tagattr != NULL) {
637774eb1a3Sthorpej char *cp;
638774eb1a3Sthorpej
639774eb1a3Sthorpej if (!_PROP_TAGATTR_MATCH(ctx, "size") ||
640774eb1a3Sthorpej ctx->poic_tagattrval_len == 0)
641e835604cSjoerg return (true);
642774eb1a3Sthorpej
643774eb1a3Sthorpej #ifndef _KERNEL
644774eb1a3Sthorpej errno = 0;
645774eb1a3Sthorpej #endif
646774eb1a3Sthorpej len = strtoul(ctx->poic_tagattrval, &cp, 0);
647774eb1a3Sthorpej #ifndef _KERNEL /* XXX can't check for ERANGE in the kernel */
648774eb1a3Sthorpej if (len == ULONG_MAX && errno == ERANGE)
649e835604cSjoerg return (true);
650774eb1a3Sthorpej #endif
651774eb1a3Sthorpej if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len)
652e835604cSjoerg return (true);
653774eb1a3Sthorpej _PROP_ASSERT(*cp == '\"');
654774eb1a3Sthorpej } else if (_prop_data_internalize_decode(ctx, NULL, 0, &len,
65504377267Sthorpej NULL) == false)
656e835604cSjoerg return (true);
657774eb1a3Sthorpej
658774eb1a3Sthorpej /*
659774eb1a3Sthorpej * Always allocate one extra in case we don't land on an even byte
660774eb1a3Sthorpej * boundary during the decode.
661774eb1a3Sthorpej */
662774eb1a3Sthorpej buf = _PROP_MALLOC(len + 1, M_PROP_DATA);
663774eb1a3Sthorpej if (buf == NULL)
664e835604cSjoerg return (true);
665774eb1a3Sthorpej
666774eb1a3Sthorpej if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen,
66704377267Sthorpej &ctx->poic_cp) == false) {
668774eb1a3Sthorpej _PROP_FREE(buf, M_PROP_DATA);
669e835604cSjoerg return (true);
670774eb1a3Sthorpej }
671774eb1a3Sthorpej if (alen != len) {
672774eb1a3Sthorpej _PROP_FREE(buf, M_PROP_DATA);
673e835604cSjoerg return (true);
674774eb1a3Sthorpej }
675774eb1a3Sthorpej
676774eb1a3Sthorpej if (_prop_object_internalize_find_tag(ctx, "data",
67704377267Sthorpej _PROP_TAG_TYPE_END) == false) {
678774eb1a3Sthorpej _PROP_FREE(buf, M_PROP_DATA);
679e835604cSjoerg return (true);
680774eb1a3Sthorpej }
681774eb1a3Sthorpej
68223dc01f7Scyber /*
68323dc01f7Scyber * Handle alternate type of empty node.
68423dc01f7Scyber * XML document could contain open/close tags, yet still be empty.
68523dc01f7Scyber */
68623dc01f7Scyber if (alen == 0) {
68723dc01f7Scyber _PROP_FREE(buf, M_PROP_DATA);
688a792b843Sthorpej buf = NULL;
68923dc01f7Scyber }
690a792b843Sthorpej
691a792b843Sthorpej data = _prop_data_instantiate(0, buf, len);
692a792b843Sthorpej if (data == NULL && buf != NULL)
693a792b843Sthorpej _PROP_FREE(buf, M_PROP_DATA);
694774eb1a3Sthorpej
695e835604cSjoerg *obj = data;
696e835604cSjoerg return (true);
697774eb1a3Sthorpej }
698