1 /* $NetBSD: prop_data.c,v 1.2 2006/05/18 03:05:19 thorpej 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <prop/prop_data.h> 40 #include "prop_object_impl.h" 41 42 #if defined(_KERNEL) 43 #include <sys/systm.h> 44 #elif defined(_STANDALONE) 45 #include <sys/param.h> 46 #include <lib/libkern/libkern.h> 47 #else 48 #include <errno.h> 49 #include <limits.h> 50 #include <stdlib.h> 51 #endif 52 53 struct _prop_data { 54 struct _prop_object pd_obj; 55 union { 56 void * pdu_mutable; 57 const void * pdu_immutable; 58 } pd_un; 59 #define pd_mutable pd_un.pdu_mutable 60 #define pd_immutable pd_un.pdu_immutable 61 size_t pd_size; 62 int pd_flags; 63 }; 64 65 #define PD_F_NOCOPY 0x01 66 67 _PROP_POOL_INIT(_prop_data_pool, sizeof(struct _prop_data), "propdata") 68 69 _PROP_MALLOC_DEFINE(M_PROP_DATA, "prop data", 70 "property data container object") 71 72 static void _prop_data_free(void *); 73 static boolean_t _prop_data_externalize( 74 struct _prop_object_externalize_context *, 75 void *); 76 static boolean_t _prop_data_equals(void *, void *); 77 78 static const struct _prop_object_type _prop_object_type_data = { 79 .pot_type = PROP_TYPE_DATA, 80 .pot_free = _prop_data_free, 81 .pot_extern = _prop_data_externalize, 82 .pot_equals = _prop_data_equals, 83 }; 84 85 #define prop_object_is_data(x) \ 86 ((x)->pd_obj.po_type == &_prop_object_type_data) 87 88 static void 89 _prop_data_free(void *v) 90 { 91 prop_data_t pd = v; 92 93 if ((pd->pd_flags & PD_F_NOCOPY) == 0 && pd->pd_mutable != NULL) 94 _PROP_FREE(pd->pd_mutable, M_PROP_DATA); 95 _PROP_POOL_PUT(_prop_data_pool, v); 96 } 97 98 static const char _prop_data_base64[] = 99 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 100 static const char _prop_data_pad64 = '='; 101 102 static boolean_t 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] = ((u_int32_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] = ((u_int32_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 static boolean_t 178 _prop_data_equals(void *v1, void *v2) 179 { 180 prop_data_t pd1 = v1; 181 prop_data_t pd2 = v2; 182 183 _PROP_ASSERT(prop_object_is_data(pd1)); 184 _PROP_ASSERT(prop_object_is_data(pd2)); 185 if (pd1 == pd2) 186 return (TRUE); 187 if (pd1->pd_size != pd2->pd_size) 188 return (FALSE); 189 if (pd1->pd_size == 0) { 190 _PROP_ASSERT(pd1->pd_immutable == NULL); 191 _PROP_ASSERT(pd2->pd_immutable == NULL); 192 return (TRUE); 193 } 194 return (memcmp(pd1->pd_immutable, pd2->pd_immutable, 195 pd1->pd_size) == 0); 196 } 197 198 static prop_data_t 199 _prop_data_alloc(void) 200 { 201 prop_data_t pd; 202 203 pd = _PROP_POOL_GET(_prop_data_pool); 204 if (pd != NULL) { 205 _prop_object_init(&pd->pd_obj, &_prop_object_type_data); 206 207 pd->pd_mutable = NULL; 208 pd->pd_size = 0; 209 pd->pd_flags = 0; 210 } 211 212 return (pd); 213 } 214 215 /* 216 * prop_data_create_data -- 217 * Create a data container that contains a copy of the data. 218 */ 219 prop_data_t 220 prop_data_create_data(const void *v, size_t size) 221 { 222 prop_data_t pd; 223 void *nv; 224 225 pd = _prop_data_alloc(); 226 if (pd != NULL) { 227 nv = _PROP_MALLOC(size, M_PROP_DATA); 228 if (nv == NULL) { 229 _prop_data_free(pd); 230 return (NULL); 231 } 232 memcpy(nv, v, size); 233 pd->pd_mutable = nv; 234 pd->pd_size = size; 235 } 236 return (pd); 237 } 238 239 /* 240 * prop_data_create_data_nocopy -- 241 * Create an immutable data container that contains a refrence to the 242 * provided external data. 243 */ 244 prop_data_t 245 prop_data_create_data_nocopy(const void *v, size_t size) 246 { 247 prop_data_t pd; 248 249 pd = _prop_data_alloc(); 250 if (pd != NULL) { 251 pd->pd_immutable = v; 252 pd->pd_size = size; 253 pd->pd_flags |= PD_F_NOCOPY; 254 } 255 return (pd); 256 } 257 258 /* 259 * prop_data_copy -- 260 * Copy a data container. If the original data is external, then 261 * the copy is also references the same external data. 262 */ 263 prop_data_t 264 prop_data_copy(prop_data_t opd) 265 { 266 prop_data_t pd; 267 268 _PROP_ASSERT(prop_object_is_data(opd)); 269 270 pd = _prop_data_alloc(); 271 if (pd != NULL) { 272 pd->pd_size = opd->pd_size; 273 pd->pd_flags = opd->pd_flags; 274 if (opd->pd_flags & PD_F_NOCOPY) 275 pd->pd_immutable = opd->pd_immutable; 276 else if (opd->pd_size != 0) { 277 void *nv = _PROP_MALLOC(pd->pd_size, M_PROP_DATA); 278 if (nv == NULL) { 279 _prop_data_free(pd); 280 return (NULL); 281 } 282 memcpy(nv, opd->pd_immutable, opd->pd_size); 283 pd->pd_mutable = nv; 284 } 285 } 286 return (pd); 287 } 288 289 /* 290 * prop_data_size -- 291 * Return the size of the data. 292 */ 293 size_t 294 prop_data_size(prop_data_t pd) 295 { 296 297 _PROP_ASSERT(prop_object_is_data(pd)); 298 return (pd->pd_size); 299 } 300 301 /* 302 * prop_data_data -- 303 * Return a copy of the contents of the data container. 304 * The data is allocated with the M_TEMP malloc type. 305 * If the data container is empty, NULL is returned. 306 */ 307 void * 308 prop_data_data(prop_data_t pd) 309 { 310 void *v; 311 312 _PROP_ASSERT(prop_object_is_data(pd)); 313 314 if (pd->pd_size == 0) { 315 _PROP_ASSERT(pd->pd_immutable == NULL); 316 return (NULL); 317 } 318 319 _PROP_ASSERT(pd->pd_immutable != NULL); 320 321 v = _PROP_MALLOC(pd->pd_size, M_TEMP); 322 if (v != NULL) 323 memcpy(v, pd->pd_immutable, pd->pd_size); 324 325 return (v); 326 } 327 328 /* 329 * prop_data_data_nocopy -- 330 * Return an immutable reference to the contents of the data 331 * container. 332 */ 333 const void * 334 prop_data_data_nocopy(prop_data_t pd) 335 { 336 337 _PROP_ASSERT(prop_object_is_data(pd)); 338 _PROP_ASSERT((pd->pd_size == 0 && pd->pd_immutable == NULL) || 339 (pd->pd_size != 0 && pd->pd_immutable != NULL)); 340 341 return (pd->pd_immutable); 342 } 343 344 /* 345 * prop_data_equals -- 346 * Return TRUE if two strings are equivalent. 347 */ 348 boolean_t 349 prop_data_equals(prop_data_t pd1, prop_data_t pd2) 350 { 351 352 return (_prop_data_equals(pd1, pd2)); 353 } 354 355 /* 356 * prop_data_equals_data -- 357 * Return TRUE if the contained data is equivalent to the specified 358 * external data. 359 */ 360 boolean_t 361 prop_data_equals_data(prop_data_t pd, const void *v, size_t size) 362 { 363 364 _PROP_ASSERT(prop_object_is_data(pd)); 365 if (pd->pd_size != size) 366 return (FALSE); 367 return (memcmp(pd->pd_immutable, v, size) == 0); 368 } 369 370 static boolean_t 371 _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx, 372 uint8_t *target, size_t targsize, size_t *sizep, 373 const char **cpp) 374 { 375 const char *src; 376 size_t tarindex; 377 int state, ch; 378 const char *pos; 379 380 state = 0; 381 tarindex = 0; 382 src = ctx->poic_cp; 383 384 for (;;) { 385 ch = (unsigned char) *src++; 386 if (_PROP_EOF(ch)) 387 return (FALSE); 388 if (_PROP_ISSPACE(ch)) 389 continue; 390 if (ch == '<') { 391 src--; 392 break; 393 } 394 if (ch == _prop_data_pad64) 395 break; 396 397 pos = strchr(_prop_data_base64, ch); 398 if (pos == NULL) 399 return (FALSE); 400 401 switch (state) { 402 case 0: 403 if (target) { 404 if (tarindex >= targsize) 405 return (FALSE); 406 target[tarindex] = 407 (pos - _prop_data_base64) << 2; 408 } 409 state = 1; 410 break; 411 412 case 1: 413 if (target) { 414 if (tarindex + 1 >= targsize) 415 return (FALSE); 416 target[tarindex] |= 417 (uint32_t)(pos - _prop_data_base64) >> 4; 418 target[tarindex + 1] = 419 ((pos - _prop_data_base64) & 0xf) << 4; 420 } 421 tarindex++; 422 state = 2; 423 break; 424 425 case 2: 426 if (target) { 427 if (tarindex + 1 >= targsize) 428 return (FALSE); 429 target[tarindex] |= 430 (uint32_t)(pos - _prop_data_base64) >> 2; 431 target[tarindex + 1] = 432 ((pos - _prop_data_base64) & 0x3) << 6; 433 } 434 tarindex++; 435 state = 3; 436 break; 437 438 case 3: 439 if (target) { 440 if (tarindex >= targsize) 441 return (FALSE); 442 target[tarindex] |= (pos - _prop_data_base64); 443 } 444 tarindex++; 445 state = 0; 446 break; 447 448 default: 449 _PROP_ASSERT(/*CONSTCOND*/0); 450 } 451 } 452 453 /* 454 * We are done decoding the Base64 characters. Let's see if we 455 * ended up on a byte boundary and/or with unrecognized trailing 456 * characters. 457 */ 458 if (ch == _prop_data_pad64) { 459 ch = (unsigned char) *src; /* src already advanced */ 460 if (_PROP_EOF(ch)) 461 return (FALSE); 462 switch (state) { 463 case 0: /* Invalid = in first position */ 464 case 1: /* Invalid = in second position */ 465 return (FALSE); 466 467 case 2: /* Valid, one byte of info */ 468 /* Skip whitespace */ 469 for (ch = (unsigned char) *src++; 470 ch != '<'; ch = (unsigned char) *src++) { 471 if (_PROP_EOF(ch)) 472 return (FALSE); 473 if (!_PROP_ISSPACE(ch)) 474 break; 475 } 476 /* Make sure there is another trailing = */ 477 if (ch != _prop_data_pad64) 478 return (FALSE); 479 ch = (unsigned char) *src; 480 /* FALLTHROUGH */ 481 482 case 3: /* Valid, two bytes of info */ 483 /* 484 * We know this char is a =. Is there anything but 485 * whitespace after it? 486 */ 487 for (; ch != '<'; ch = (unsigned char) *src++) { 488 if (_PROP_EOF(ch)) 489 return (FALSE); 490 if (!_PROP_ISSPACE(ch)) 491 return (FALSE); 492 } 493 } 494 } else { 495 /* 496 * We ended by seeing the end of the Base64 string. Make 497 * sure there are no partial bytes lying around. 498 */ 499 if (state != 0) 500 return (FALSE); 501 } 502 503 _PROP_ASSERT(*src == '<'); 504 if (sizep != NULL) 505 *sizep = tarindex; 506 if (cpp != NULL) 507 *cpp = src; 508 509 return (TRUE); 510 } 511 512 /* 513 * _prop_data_internalize -- 514 * Parse a <data>...</data> and return the object created from the 515 * external representation. 516 */ 517 prop_object_t 518 _prop_data_internalize(struct _prop_object_internalize_context *ctx) 519 { 520 prop_data_t data; 521 uint8_t *buf; 522 size_t len, alen; 523 524 /* We don't accept empty elements. */ 525 if (ctx->poic_is_empty_element) 526 return (NULL); 527 528 /* 529 * If we got a "size" attribute, get the size of the data blob 530 * from that. Otherwise, we have to figure it out from the base64. 531 */ 532 if (ctx->poic_tagattr != NULL) { 533 char *cp; 534 535 if (!_PROP_TAGATTR_MATCH(ctx, "size") || 536 ctx->poic_tagattrval_len == 0) 537 return (NULL); 538 539 #ifndef _KERNEL 540 errno = 0; 541 #endif 542 /* XXX Assumes size_t and unsigned long are the same size. */ 543 len = strtoul(ctx->poic_tagattrval, &cp, 0); 544 #ifndef _KERNEL /* XXX can't check for ERANGE in the kernel */ 545 if (len == ULONG_MAX && errno == ERANGE) 546 return (NULL); 547 #endif 548 if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len) 549 return (NULL); 550 _PROP_ASSERT(*cp == '\"'); 551 } else if (_prop_data_internalize_decode(ctx, NULL, 0, &len, 552 NULL) == FALSE) 553 return (NULL); 554 555 /* 556 * Always allocate one extra in case we don't land on an even byte 557 * boundary during the decode. 558 */ 559 buf = _PROP_MALLOC(len + 1, M_PROP_DATA); 560 if (buf == NULL) 561 return (NULL); 562 563 if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen, 564 &ctx->poic_cp) == FALSE) { 565 _PROP_FREE(buf, M_PROP_DATA); 566 return (NULL); 567 } 568 if (alen != len) { 569 _PROP_FREE(buf, M_PROP_DATA); 570 return (NULL); 571 } 572 573 if (_prop_object_internalize_find_tag(ctx, "data", 574 _PROP_TAG_TYPE_END) == FALSE) { 575 _PROP_FREE(buf, M_PROP_DATA); 576 return (NULL); 577 } 578 579 data = _prop_data_alloc(); 580 if (data == NULL) { 581 _PROP_FREE(buf, M_PROP_DATA); 582 return (NULL); 583 } 584 585 data->pd_mutable = buf; 586 data->pd_size = len; 587 588 return (data); 589 } 590