1 /* $NetBSD: prop_data.c,v 1.5 2006/10/18 14:41:08 martin 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) != NULL && (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 if (! (prop_object_is_data(pd1) && 184 prop_object_is_data(pd2))) 185 return (FALSE); 186 187 if (pd1 == pd2) 188 return (TRUE); 189 if (pd1->pd_size != pd2->pd_size) 190 return (FALSE); 191 if (pd1->pd_size == 0) { 192 _PROP_ASSERT(pd1->pd_immutable == NULL); 193 _PROP_ASSERT(pd2->pd_immutable == NULL); 194 return (TRUE); 195 } 196 return (memcmp(pd1->pd_immutable, pd2->pd_immutable, 197 pd1->pd_size) == 0); 198 } 199 200 static prop_data_t 201 _prop_data_alloc(void) 202 { 203 prop_data_t pd; 204 205 pd = _PROP_POOL_GET(_prop_data_pool); 206 if (pd != NULL) { 207 _prop_object_init(&pd->pd_obj, &_prop_object_type_data); 208 209 pd->pd_mutable = NULL; 210 pd->pd_size = 0; 211 pd->pd_flags = 0; 212 } 213 214 return (pd); 215 } 216 217 /* 218 * prop_data_create_data -- 219 * Create a data container that contains a copy of the data. 220 */ 221 prop_data_t 222 prop_data_create_data(const void *v, size_t size) 223 { 224 prop_data_t pd; 225 void *nv; 226 227 pd = _prop_data_alloc(); 228 if (pd != NULL) { 229 nv = _PROP_MALLOC(size, M_PROP_DATA); 230 if (nv == NULL) { 231 _prop_data_free(pd); 232 return (NULL); 233 } 234 memcpy(nv, v, size); 235 pd->pd_mutable = nv; 236 pd->pd_size = size; 237 } 238 return (pd); 239 } 240 241 /* 242 * prop_data_create_data_nocopy -- 243 * Create an immutable data container that contains a refrence to the 244 * provided external data. 245 */ 246 prop_data_t 247 prop_data_create_data_nocopy(const void *v, size_t size) 248 { 249 prop_data_t pd; 250 251 pd = _prop_data_alloc(); 252 if (pd != NULL) { 253 pd->pd_immutable = v; 254 pd->pd_size = size; 255 pd->pd_flags |= PD_F_NOCOPY; 256 } 257 return (pd); 258 } 259 260 /* 261 * prop_data_copy -- 262 * Copy a data container. If the original data is external, then 263 * the copy is also references the same external data. 264 */ 265 prop_data_t 266 prop_data_copy(prop_data_t opd) 267 { 268 prop_data_t pd; 269 270 if (! prop_object_is_data(opd)) 271 return (NULL); 272 273 pd = _prop_data_alloc(); 274 if (pd != NULL) { 275 pd->pd_size = opd->pd_size; 276 pd->pd_flags = opd->pd_flags; 277 if (opd->pd_flags & PD_F_NOCOPY) 278 pd->pd_immutable = opd->pd_immutable; 279 else if (opd->pd_size != 0) { 280 void *nv = _PROP_MALLOC(pd->pd_size, M_PROP_DATA); 281 if (nv == NULL) { 282 _prop_data_free(pd); 283 return (NULL); 284 } 285 memcpy(nv, opd->pd_immutable, opd->pd_size); 286 pd->pd_mutable = nv; 287 } 288 } 289 return (pd); 290 } 291 292 /* 293 * prop_data_size -- 294 * Return the size of the data. 295 */ 296 size_t 297 prop_data_size(prop_data_t pd) 298 { 299 300 if (! prop_object_is_data(pd)) 301 return (0); 302 303 return (pd->pd_size); 304 } 305 306 /* 307 * prop_data_data -- 308 * Return a copy of the contents of the data container. 309 * The data is allocated with the M_TEMP malloc type. 310 * If the data container is empty, NULL is returned. 311 */ 312 void * 313 prop_data_data(prop_data_t pd) 314 { 315 void *v; 316 317 if (! prop_object_is_data(pd)) 318 return (NULL); 319 320 if (pd->pd_size == 0) { 321 _PROP_ASSERT(pd->pd_immutable == NULL); 322 return (NULL); 323 } 324 325 _PROP_ASSERT(pd->pd_immutable != NULL); 326 327 v = _PROP_MALLOC(pd->pd_size, M_TEMP); 328 if (v != NULL) 329 memcpy(v, pd->pd_immutable, pd->pd_size); 330 331 return (v); 332 } 333 334 /* 335 * prop_data_data_nocopy -- 336 * Return an immutable reference to the contents of the data 337 * container. 338 */ 339 const void * 340 prop_data_data_nocopy(prop_data_t pd) 341 { 342 343 if (! prop_object_is_data(pd)) 344 return (NULL); 345 346 _PROP_ASSERT((pd->pd_size == 0 && pd->pd_immutable == NULL) || 347 (pd->pd_size != 0 && pd->pd_immutable != NULL)); 348 349 return (pd->pd_immutable); 350 } 351 352 /* 353 * prop_data_equals -- 354 * Return TRUE if two strings are equivalent. 355 */ 356 boolean_t 357 prop_data_equals(prop_data_t pd1, prop_data_t pd2) 358 { 359 360 return (_prop_data_equals(pd1, pd2)); 361 } 362 363 /* 364 * prop_data_equals_data -- 365 * Return TRUE if the contained data is equivalent to the specified 366 * external data. 367 */ 368 boolean_t 369 prop_data_equals_data(prop_data_t pd, const void *v, size_t size) 370 { 371 372 if (! prop_object_is_data(pd)) 373 return (FALSE); 374 375 if (pd->pd_size != size) 376 return (FALSE); 377 return (memcmp(pd->pd_immutable, v, size) == 0); 378 } 379 380 static boolean_t 381 _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx, 382 uint8_t *target, size_t targsize, size_t *sizep, 383 const char **cpp) 384 { 385 const char *src; 386 size_t tarindex; 387 int state, ch; 388 const char *pos; 389 390 state = 0; 391 tarindex = 0; 392 src = ctx->poic_cp; 393 394 for (;;) { 395 ch = (unsigned char) *src++; 396 if (_PROP_EOF(ch)) 397 return (FALSE); 398 if (_PROP_ISSPACE(ch)) 399 continue; 400 if (ch == '<') { 401 src--; 402 break; 403 } 404 if (ch == _prop_data_pad64) 405 break; 406 407 pos = strchr(_prop_data_base64, ch); 408 if (pos == NULL) 409 return (FALSE); 410 411 switch (state) { 412 case 0: 413 if (target) { 414 if (tarindex >= targsize) 415 return (FALSE); 416 target[tarindex] = 417 (uint8_t)((pos - _prop_data_base64) << 2); 418 } 419 state = 1; 420 break; 421 422 case 1: 423 if (target) { 424 if (tarindex + 1 >= targsize) 425 return (FALSE); 426 target[tarindex] |= 427 (uint32_t)(pos - _prop_data_base64) >> 4; 428 target[tarindex + 1] = 429 (uint8_t)(((pos - _prop_data_base64) & 0xf) 430 << 4); 431 } 432 tarindex++; 433 state = 2; 434 break; 435 436 case 2: 437 if (target) { 438 if (tarindex + 1 >= targsize) 439 return (FALSE); 440 target[tarindex] |= 441 (uint32_t)(pos - _prop_data_base64) >> 2; 442 target[tarindex + 1] = 443 (uint8_t)(((pos - _prop_data_base64) 444 & 0x3) << 6); 445 } 446 tarindex++; 447 state = 3; 448 break; 449 450 case 3: 451 if (target) { 452 if (tarindex >= targsize) 453 return (FALSE); 454 target[tarindex] |= (uint8_t) 455 (pos - _prop_data_base64); 456 } 457 tarindex++; 458 state = 0; 459 break; 460 461 default: 462 _PROP_ASSERT(/*CONSTCOND*/0); 463 } 464 } 465 466 /* 467 * We are done decoding the Base64 characters. Let's see if we 468 * ended up on a byte boundary and/or with unrecognized trailing 469 * characters. 470 */ 471 if (ch == _prop_data_pad64) { 472 ch = (unsigned char) *src; /* src already advanced */ 473 if (_PROP_EOF(ch)) 474 return (FALSE); 475 switch (state) { 476 case 0: /* Invalid = in first position */ 477 case 1: /* Invalid = in second position */ 478 return (FALSE); 479 480 case 2: /* Valid, one byte of info */ 481 /* Skip whitespace */ 482 for (ch = (unsigned char) *src++; 483 ch != '<'; ch = (unsigned char) *src++) { 484 if (_PROP_EOF(ch)) 485 return (FALSE); 486 if (!_PROP_ISSPACE(ch)) 487 break; 488 } 489 /* Make sure there is another trailing = */ 490 if (ch != _prop_data_pad64) 491 return (FALSE); 492 ch = (unsigned char) *src; 493 /* FALLTHROUGH */ 494 495 case 3: /* Valid, two bytes of info */ 496 /* 497 * We know this char is a =. Is there anything but 498 * whitespace after it? 499 */ 500 for (; ch != '<'; ch = (unsigned char) *src++) { 501 if (_PROP_EOF(ch)) 502 return (FALSE); 503 if (!_PROP_ISSPACE(ch)) 504 return (FALSE); 505 } 506 } 507 } else { 508 /* 509 * We ended by seeing the end of the Base64 string. Make 510 * sure there are no partial bytes lying around. 511 */ 512 if (state != 0) 513 return (FALSE); 514 } 515 516 _PROP_ASSERT(*src == '<'); 517 if (sizep != NULL) 518 *sizep = tarindex; 519 if (cpp != NULL) 520 *cpp = src; 521 522 return (TRUE); 523 } 524 525 /* 526 * _prop_data_internalize -- 527 * Parse a <data>...</data> and return the object created from the 528 * external representation. 529 */ 530 prop_object_t 531 _prop_data_internalize(struct _prop_object_internalize_context *ctx) 532 { 533 prop_data_t data; 534 uint8_t *buf; 535 size_t len, alen; 536 537 /* We don't accept empty elements. */ 538 if (ctx->poic_is_empty_element) 539 return (NULL); 540 541 /* 542 * If we got a "size" attribute, get the size of the data blob 543 * from that. Otherwise, we have to figure it out from the base64. 544 */ 545 if (ctx->poic_tagattr != NULL) { 546 char *cp; 547 548 if (!_PROP_TAGATTR_MATCH(ctx, "size") || 549 ctx->poic_tagattrval_len == 0) 550 return (NULL); 551 552 #ifndef _KERNEL 553 errno = 0; 554 #endif 555 /* XXX Assumes size_t and unsigned long are the same size. */ 556 len = strtoul(ctx->poic_tagattrval, &cp, 0); 557 #ifndef _KERNEL /* XXX can't check for ERANGE in the kernel */ 558 if (len == ULONG_MAX && errno == ERANGE) 559 return (NULL); 560 #endif 561 if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len) 562 return (NULL); 563 _PROP_ASSERT(*cp == '\"'); 564 } else if (_prop_data_internalize_decode(ctx, NULL, 0, &len, 565 NULL) == FALSE) 566 return (NULL); 567 568 /* 569 * Always allocate one extra in case we don't land on an even byte 570 * boundary during the decode. 571 */ 572 buf = _PROP_MALLOC(len + 1, M_PROP_DATA); 573 if (buf == NULL) 574 return (NULL); 575 576 if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen, 577 &ctx->poic_cp) == FALSE) { 578 _PROP_FREE(buf, M_PROP_DATA); 579 return (NULL); 580 } 581 if (alen != len) { 582 _PROP_FREE(buf, M_PROP_DATA); 583 return (NULL); 584 } 585 586 if (_prop_object_internalize_find_tag(ctx, "data", 587 _PROP_TAG_TYPE_END) == FALSE) { 588 _PROP_FREE(buf, M_PROP_DATA); 589 return (NULL); 590 } 591 592 data = _prop_data_alloc(); 593 if (data == NULL) { 594 _PROP_FREE(buf, M_PROP_DATA); 595 return (NULL); 596 } 597 598 data->pd_mutable = buf; 599 data->pd_size = len; 600 601 return (data); 602 } 603