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