1 /* 2 * Copyright 2005-2007, Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 * Michael Lotz <mmlr@mlotz.ch> 8 */ 9 #include <MessageAdapter.h> 10 #include <MessagePrivate.h> 11 #include <MessageUtils.h> 12 13 namespace BPrivate { 14 15 #define R5_MESSAGE_FLAG_VALID 0x01 16 #define R5_MESSAGE_FLAG_INCLUDE_TARGET 0x02 17 #define R5_MESSAGE_FLAG_INCLUDE_REPLY 0x04 18 #define R5_MESSAGE_FLAG_SCRIPT_MESSAGE 0x08 19 20 #define R5_FIELD_FLAG_VALID 0x01 21 #define R5_FIELD_FLAG_MINI_DATA 0x02 22 #define R5_FIELD_FLAG_FIXED_SIZE 0x04 23 #define R5_FIELD_FLAG_SINGLE_ITEM 0x08 24 25 26 enum { 27 SECTION_MESSAGE_HEADER = 'FOB2', 28 SECTION_OFFSET_TABLE = 'STof', 29 SECTION_TARGET_INFORMATION = 'ENwh', 30 SECTION_SINGLE_ITEM_DATA = 'SGDa', 31 SECTION_FIXED_SIZE_ARRAY_DATA = 'FADa', 32 SECTION_VARIABLE_SIZE_ARRAY_DATA = 'VADa', 33 SECTION_SORTED_INDEX_TABLE = 'DXIn', 34 SECTION_END_OF_DATA = 'DDEn' 35 }; 36 37 38 struct r5_message_header { 39 uint32 magic; 40 uint32 checksum; 41 ssize_t flattened_size; 42 int32 what; 43 uint8 flags; 44 } _PACKED; 45 46 struct dano_section_header { 47 uint32 code; 48 ssize_t size; 49 uint8 data[0]; 50 } _PACKED; 51 52 struct dano_message_header { 53 int32 what; 54 int32 padding; 55 } _PACKED; 56 57 typedef struct offset_table_s { 58 int32 indexTable; 59 int32 endOfData; 60 int64 padding; 61 } OffsetTable; 62 63 struct dano_single_item { 64 type_code type; 65 ssize_t item_size; 66 uint8 name_length; 67 char name[0]; 68 } _PACKED; 69 70 struct dano_fixed_size_array { 71 type_code type; 72 ssize_t size_per_item; 73 uint8 name_length; 74 char name[0]; 75 } _PACKED; 76 77 78 struct dano_variable_size_array { 79 type_code type; 80 int32 padding; 81 uint8 name_length; 82 char name[0]; 83 } _PACKED; 84 85 86 inline int32 87 pad_to_8(int32 value) 88 { 89 return (value + 7) & ~7; 90 } 91 92 93 ssize_t 94 MessageAdapter::FlattenedSize(uint32 format, const BMessage *from) 95 { 96 switch (format) { 97 case MESSAGE_FORMAT_R5: 98 case MESSAGE_FORMAT_R5_SWAPPED: 99 return _R5FlattenedSize(from); 100 } 101 102 return -1; 103 } 104 105 106 status_t 107 MessageAdapter::Flatten(uint32 format, const BMessage *from, char *buffer, 108 ssize_t *size) 109 { 110 switch (format) { 111 case MESSAGE_FORMAT_R5: 112 case MESSAGE_FORMAT_R5_SWAPPED: 113 return _FlattenR5Message(format, from, buffer, size); 114 } 115 116 return B_ERROR; 117 } 118 119 120 status_t 121 MessageAdapter::Flatten(uint32 format, const BMessage *from, BDataIO *stream, 122 ssize_t *size) 123 { 124 switch (format) { 125 case MESSAGE_FORMAT_R5: 126 case MESSAGE_FORMAT_R5_SWAPPED: 127 { 128 ssize_t flattenedSize = _R5FlattenedSize(from); 129 char *buffer = (char *)malloc(flattenedSize); 130 if (!buffer) 131 return B_NO_MEMORY; 132 133 status_t result = _FlattenR5Message(format, from, buffer, 134 &flattenedSize); 135 if (result < B_OK) { 136 free(buffer); 137 return result; 138 } 139 140 ssize_t written = stream->Write(buffer, flattenedSize); 141 if (written != flattenedSize) { 142 free(buffer); 143 return (written >= 0 ? B_ERROR : written); 144 } 145 146 if (size) 147 *size = flattenedSize; 148 149 free(buffer); 150 return B_OK; 151 } 152 } 153 154 return B_ERROR; 155 } 156 157 158 status_t 159 MessageAdapter::Unflatten(uint32 format, BMessage *into, const char *buffer) 160 { 161 if (format == KMessage::kMessageHeaderMagic) { 162 KMessage message; 163 status_t result = message.SetTo(buffer, 164 ((KMessage::Header *)buffer)->size); 165 if (result != B_OK) 166 return result; 167 168 return _ConvertKMessage(&message, into); 169 } 170 171 try { 172 switch (format) { 173 case MESSAGE_FORMAT_R5: 174 case MESSAGE_FORMAT_R5_SWAPPED: 175 { 176 r5_message_header *header = (r5_message_header *)buffer; 177 BMemoryIO stream(buffer + sizeof(uint32), 178 header->flattened_size - sizeof(uint32)); 179 return _UnflattenR5Message(format, into, &stream); 180 } 181 182 case MESSAGE_FORMAT_DANO: 183 case MESSAGE_FORMAT_DANO_SWAPPED: 184 { 185 dano_section_header *header = (dano_section_header *)buffer; 186 ssize_t size = header->size; 187 if (header->code == MESSAGE_FORMAT_DANO_SWAPPED) 188 size = __swap_int32(size); 189 190 BMemoryIO stream(buffer + sizeof(uint32), size - sizeof(uint32)); 191 return _UnflattenDanoMessage(format, into, &stream); 192 } 193 } 194 } catch (status_t error) { 195 into->MakeEmpty(); 196 return error; 197 } 198 199 return B_NOT_A_MESSAGE; 200 } 201 202 203 status_t 204 MessageAdapter::Unflatten(uint32 format, BMessage *into, BDataIO *stream) 205 { 206 try { 207 switch (format) { 208 case MESSAGE_FORMAT_R5: 209 case MESSAGE_FORMAT_R5_SWAPPED: 210 return _UnflattenR5Message(format, into, stream); 211 212 case MESSAGE_FORMAT_DANO: 213 case MESSAGE_FORMAT_DANO_SWAPPED: 214 return _UnflattenDanoMessage(format, into, stream); 215 } 216 } catch (status_t error) { 217 into->MakeEmpty(); 218 return error; 219 } 220 221 return B_NOT_A_MESSAGE; 222 } 223 224 225 status_t 226 MessageAdapter::_ConvertKMessage(const KMessage *fromMessage, 227 BMessage *toMessage) 228 { 229 if (!fromMessage || !toMessage) 230 return B_BAD_VALUE; 231 232 // make empty and init what of the target message 233 toMessage->MakeEmpty(); 234 toMessage->what = fromMessage->What(); 235 236 BMessage::Private toPrivate(toMessage); 237 toPrivate.SetTarget(fromMessage->TargetToken()); 238 toPrivate.SetReply(B_SYSTEM_TEAM, fromMessage->ReplyPort(), 239 fromMessage->ReplyToken()); 240 241 // iterate through the fields and import them in the target message 242 KMessageField field; 243 while (fromMessage->GetNextField(&field) == B_OK) { 244 int32 elementCount = field.CountElements(); 245 if (elementCount > 0) { 246 for (int32 i = 0; i < elementCount; i++) { 247 int32 size; 248 const void *data = field.ElementAt(i, &size); 249 status_t result; 250 251 if (field.TypeCode() == B_MESSAGE_TYPE) { 252 // message type: if it's a KMessage, convert it 253 KMessage message; 254 if (message.SetTo(data, size) == B_OK) { 255 BMessage bMessage; 256 result = _ConvertKMessage(&message, &bMessage); 257 if (result < B_OK) 258 return result; 259 260 result = toMessage->AddMessage(field.Name(), &bMessage); 261 } else { 262 // just add it 263 result = toMessage->AddData(field.Name(), 264 field.TypeCode(), data, size, 265 field.HasFixedElementSize(), 1); 266 } 267 } else { 268 result = toMessage->AddData(field.Name(), field.TypeCode(), 269 data, size, field.HasFixedElementSize(), 1); 270 } 271 272 if (result < B_OK) 273 return result; 274 } 275 } 276 } 277 278 return B_OK; 279 } 280 281 282 ssize_t 283 MessageAdapter::_R5FlattenedSize(const BMessage *from) 284 { 285 BMessage::Private messagePrivate((BMessage *)from); 286 BMessage::message_header* header = messagePrivate.GetMessageHeader(); 287 288 // header size (variable, depending on the flags) 289 290 ssize_t flattenedSize = sizeof(r5_message_header); 291 292 if (header->target != B_NULL_TOKEN) 293 flattenedSize += sizeof(int32); 294 295 if (header->reply_port >= 0 && header->reply_target != B_NULL_TOKEN 296 && header->reply_team >= 0) { 297 // reply info + big flags 298 flattenedSize += sizeof(port_id) + sizeof(int32) + sizeof(team_id) + 4; 299 } 300 301 // field size 302 303 uint8 *data = messagePrivate.GetMessageData(); 304 BMessage::field_header *field = messagePrivate.GetMessageFields(); 305 for (int32 i = 0; i < header->field_count; i++, field++) { 306 // flags and type 307 flattenedSize += 1 + sizeof(type_code); 308 309 #if 0 310 bool miniData = field->dataSize <= 255 && field->count <= 255; 311 #else 312 // ToDo: we don't know the R5 dataSize yet (padding) 313 bool miniData = false; 314 #endif 315 316 // item count 317 if (field->count > 1) 318 flattenedSize += (miniData ? sizeof(uint8) : sizeof(uint32)); 319 320 // data size 321 flattenedSize += (miniData ? sizeof(uint8) : sizeof(size_t)); 322 323 // name length and name 324 flattenedSize += 1 + min_c(field->name_length - 1, 255); 325 326 // data 327 if (field->flags & FIELD_FLAG_FIXED_SIZE) 328 flattenedSize += field->data_size; 329 else { 330 uint8 *source = data + field->offset + field->name_length; 331 332 for (int32 i = 0; i < field->count; i++) { 333 ssize_t itemSize = *(ssize_t *)source + sizeof(ssize_t); 334 flattenedSize += pad_to_8(itemSize); 335 source += itemSize; 336 } 337 } 338 } 339 340 // pseudo field with flags 0 341 return flattenedSize + 1; 342 } 343 344 345 status_t 346 MessageAdapter::_FlattenR5Message(uint32 format, const BMessage *from, 347 char *buffer, ssize_t *size) 348 { 349 BMessage::Private messagePrivate((BMessage *)from); 350 BMessage::message_header *header = messagePrivate.GetMessageHeader(); 351 uint8 *data = messagePrivate.GetMessageData(); 352 353 r5_message_header *r5header = (r5_message_header *)buffer; 354 uint8 *pointer = (uint8 *)buffer + sizeof(r5_message_header); 355 356 r5header->magic = MESSAGE_FORMAT_R5; 357 r5header->what = from->what; 358 r5header->checksum = 0; 359 360 uint8 flags = R5_MESSAGE_FLAG_VALID; 361 if (header->target != B_NULL_TOKEN) { 362 *(int32 *)pointer = header->target; 363 pointer += sizeof(int32); 364 flags |= R5_MESSAGE_FLAG_INCLUDE_TARGET; 365 } 366 367 if (header->reply_port >= 0 && header->reply_target != B_NULL_TOKEN 368 && header->reply_team >= 0) { 369 // reply info 370 *(port_id *)pointer = header->reply_port; 371 pointer += sizeof(port_id); 372 *(int32 *)pointer = header->reply_target; 373 pointer += sizeof(int32); 374 *(team_id *)pointer = header->reply_team; 375 pointer += sizeof(team_id); 376 377 // big flags 378 *pointer = (header->reply_target == B_PREFERRED_TOKEN ? 1 : 0); 379 pointer++; 380 381 *pointer = (header->flags & MESSAGE_FLAG_REPLY_REQUIRED ? 1 : 0); 382 pointer++; 383 384 *pointer = (header->flags & MESSAGE_FLAG_REPLY_DONE ? 1 : 0); 385 pointer++; 386 387 *pointer = (header->flags & MESSAGE_FLAG_IS_REPLY ? 1 : 0); 388 pointer++; 389 390 flags |= R5_MESSAGE_FLAG_INCLUDE_REPLY; 391 } 392 393 if (header->flags & MESSAGE_FLAG_HAS_SPECIFIERS) 394 flags |= R5_MESSAGE_FLAG_SCRIPT_MESSAGE; 395 396 r5header->flags = flags; 397 398 // store the header size - used for the checksum later 399 ssize_t headerSize = (uint32)pointer - (uint32)buffer; 400 401 // collect and add the data 402 BMessage::field_header *field = messagePrivate.GetMessageFields(); 403 for (int32 i = 0; i < header->field_count; i++, field++) { 404 flags = R5_FIELD_FLAG_VALID; 405 406 if (field->count == 1) 407 flags |= R5_FIELD_FLAG_SINGLE_ITEM; 408 // ToDo: we don't really know the data size now (padding missing) 409 if (field->data_size <= 255 && field->count <= 255) 410 ;//flags |= R5_FIELD_FLAG_MINI_DATA; 411 if (field->flags & FIELD_FLAG_FIXED_SIZE) 412 flags |= R5_FIELD_FLAG_FIXED_SIZE; 413 414 *pointer = flags; 415 pointer++; 416 417 *(type_code *)pointer = field->type; 418 pointer += sizeof(type_code); 419 420 if (!(flags & R5_FIELD_FLAG_SINGLE_ITEM)) { 421 if (flags & R5_FIELD_FLAG_MINI_DATA) { 422 *pointer = (uint8)field->count; 423 pointer++; 424 } else { 425 *(int32 *)pointer = field->count; 426 pointer += sizeof(int32); 427 } 428 } 429 430 // we may have to adjust this to account for padding later 431 uint8 *fieldSize = pointer; 432 if (flags & R5_FIELD_FLAG_MINI_DATA) { 433 *pointer = (uint8)field->data_size; 434 pointer++; 435 } else { 436 *(ssize_t *)pointer = field->data_size; 437 pointer += sizeof(ssize_t); 438 } 439 440 // name 441 int32 nameLength = min_c(field->name_length - 1, 255); 442 *pointer = (uint8)nameLength; 443 pointer++; 444 445 strncpy((char *)pointer, (char *)data + field->offset, nameLength); 446 pointer += nameLength; 447 448 // data 449 uint8 *source = data + field->offset + field->name_length; 450 if (flags & R5_FIELD_FLAG_FIXED_SIZE) { 451 memcpy(pointer, source, field->data_size); 452 pointer += field->data_size; 453 } else { 454 uint8 *previous = pointer; 455 for (int32 i = 0; i < field->count; i++) { 456 ssize_t itemSize = *(ssize_t *)source + sizeof(ssize_t); 457 memcpy(pointer, source, itemSize); 458 pointer += pad_to_8(itemSize); 459 source += itemSize; 460 } 461 462 // adjust the field size to the padded value 463 if (flags & R5_FIELD_FLAG_MINI_DATA) 464 *fieldSize = (uint8)(pointer - previous); 465 else 466 *(ssize_t *)fieldSize = (pointer - previous); 467 } 468 } 469 470 // terminate the fields with a pseudo field with flags 0 (not valid) 471 *pointer = 0; 472 pointer++; 473 474 // calculate the flattened size from the pointers 475 r5header->flattened_size = (uint32)pointer - (uint32)buffer; 476 r5header->checksum = CalculateChecksum((uint8 *)(buffer + 8), 477 headerSize - 8); 478 479 if (size) 480 *size = r5header->flattened_size; 481 482 return B_OK; 483 } 484 485 486 status_t 487 MessageAdapter::_UnflattenR5Message(uint32 format, BMessage *into, 488 BDataIO *stream) 489 { 490 into->MakeEmpty(); 491 492 BMessage::Private messagePrivate(into); 493 BMessage::message_header *header = messagePrivate.GetMessageHeader(); 494 495 TReadHelper reader(stream); 496 if (format == MESSAGE_FORMAT_R5_SWAPPED) 497 reader.SetSwap(true); 498 499 // the stream is already advanced by the size of the "format" 500 r5_message_header r5header; 501 reader(((uint8 *)&r5header) + sizeof(uint32), 502 sizeof(r5header) - sizeof(uint32)); 503 504 header->what = into->what = r5header.what; 505 if (r5header.flags & R5_MESSAGE_FLAG_INCLUDE_TARGET) 506 reader(header->target); 507 508 if (r5header.flags & R5_MESSAGE_FLAG_INCLUDE_REPLY) { 509 // reply info 510 reader(header->reply_port); 511 reader(header->reply_target); 512 reader(header->reply_team); 513 514 // big flags 515 uint8 bigFlag; 516 reader(bigFlag); 517 if (bigFlag) 518 header->reply_target = B_PREFERRED_TOKEN; 519 520 reader(bigFlag); 521 if (bigFlag) 522 header->flags |= MESSAGE_FLAG_REPLY_REQUIRED; 523 524 reader(bigFlag); 525 if (bigFlag) 526 header->flags |= MESSAGE_FLAG_REPLY_DONE; 527 528 reader(bigFlag); 529 if (bigFlag) 530 header->flags |= MESSAGE_FLAG_IS_REPLY; 531 } 532 533 if (r5header.flags & R5_MESSAGE_FLAG_SCRIPT_MESSAGE) 534 header->flags |= MESSAGE_FLAG_HAS_SPECIFIERS; 535 536 uint8 flags; 537 reader(flags); 538 while (flags & R5_FIELD_FLAG_VALID) { 539 bool fixedSize = flags & R5_FIELD_FLAG_FIXED_SIZE; 540 bool miniData = flags & R5_FIELD_FLAG_MINI_DATA; 541 bool singleItem = flags & R5_FIELD_FLAG_SINGLE_ITEM; 542 543 type_code type; 544 reader(type); 545 546 int32 itemCount; 547 if (!singleItem) { 548 if (miniData) { 549 uint8 miniCount; 550 reader(miniCount); 551 itemCount = miniCount; 552 } else 553 reader(itemCount); 554 } else 555 itemCount = 1; 556 557 ssize_t dataSize; 558 if (miniData) { 559 uint8 miniSize; 560 reader(miniSize); 561 dataSize = miniSize; 562 } else 563 reader(dataSize); 564 565 if (dataSize <= 0) 566 return B_ERROR; 567 568 // name 569 uint8 nameLength; 570 reader(nameLength); 571 572 char nameBuffer[256]; 573 reader(nameBuffer, nameLength); 574 nameBuffer[nameLength] = '\0'; 575 576 uint8 *buffer = (uint8 *)malloc(dataSize); 577 uint8 *pointer = buffer; 578 reader(buffer, dataSize); 579 580 status_t result = B_OK; 581 ssize_t itemSize = 0; 582 if (fixedSize) 583 itemSize = dataSize / itemCount; 584 585 for (int32 i = 0; i < itemCount; i++) { 586 if (!fixedSize) { 587 itemSize = *(ssize_t *)pointer; 588 pointer += sizeof(ssize_t); 589 } 590 591 result = into->AddData(nameBuffer, type, pointer, itemSize, 592 fixedSize, itemCount); 593 594 if (result < B_OK) { 595 free(buffer); 596 return result; 597 } 598 599 if (fixedSize) 600 pointer += itemSize; 601 else 602 pointer += pad_to_8(itemSize + sizeof(ssize_t)) - sizeof(ssize_t); 603 } 604 605 free(buffer); 606 607 // flags of next field or termination byte 608 reader(flags); 609 } 610 611 return B_OK; 612 } 613 614 615 status_t 616 MessageAdapter::_UnflattenDanoMessage(uint32 format, BMessage *into, 617 BDataIO *stream) 618 { 619 into->MakeEmpty(); 620 621 TReadHelper reader(stream); 622 if (format == MESSAGE_FORMAT_DANO_SWAPPED) 623 reader.SetSwap(true); 624 625 ssize_t size; 626 reader(size); 627 628 dano_message_header header; 629 reader(header); 630 into->what = header.what; 631 632 size -= sizeof(dano_section_header) + sizeof(dano_message_header); 633 int32 offset = 0; 634 635 while (offset < size) { 636 dano_section_header sectionHeader; 637 reader(sectionHeader); 638 639 // be safe. this shouldn't be necessary but in some testcases it was. 640 sectionHeader.size = pad_to_8(sectionHeader.size); 641 642 if (offset + sectionHeader.size > size || sectionHeader.size < 0) 643 return B_BAD_DATA; 644 645 ssize_t fieldSize = sectionHeader.size - sizeof(dano_section_header); 646 uint8 *fieldBuffer = NULL; 647 if (fieldSize > 0) { 648 // there may be no data. we shouldn't fail because of that 649 fieldBuffer = (uint8 *)malloc(fieldSize); 650 if (fieldBuffer == NULL) 651 throw (status_t)B_NO_MEMORY; 652 653 reader(fieldBuffer, fieldSize); 654 } 655 656 switch (sectionHeader.code) { 657 case SECTION_OFFSET_TABLE: 658 case SECTION_TARGET_INFORMATION: 659 case SECTION_SORTED_INDEX_TABLE: 660 case SECTION_END_OF_DATA: 661 // discard 662 break; 663 664 case SECTION_SINGLE_ITEM_DATA: { 665 dano_single_item *field = (dano_single_item *)fieldBuffer; 666 667 int32 dataOffset = sizeof(dano_single_item) 668 + field->name_length + 1; 669 dataOffset = pad_to_8(dataOffset); 670 671 if (offset + dataOffset + field->item_size > size) 672 return B_BAD_DATA; 673 674 // support for fixed size is not possible with a single item 675 bool fixedSize = false; 676 switch (field->type) { 677 case B_RECT_TYPE: 678 case B_POINT_TYPE: 679 case B_INT8_TYPE: 680 case B_INT16_TYPE: 681 case B_INT32_TYPE: 682 case B_INT64_TYPE: 683 case B_BOOL_TYPE: 684 case B_FLOAT_TYPE: 685 case B_DOUBLE_TYPE: 686 case B_POINTER_TYPE: 687 case B_MESSENGER_TYPE: 688 fixedSize = true; 689 break; 690 default: 691 break; 692 } 693 694 status_t result = into->AddData(field->name, field->type, 695 fieldBuffer + dataOffset, field->item_size, fixedSize); 696 697 if (result < B_OK) { 698 free(fieldBuffer); 699 throw result; 700 } 701 break; 702 } 703 704 case SECTION_FIXED_SIZE_ARRAY_DATA: { 705 dano_fixed_size_array *field 706 = (dano_fixed_size_array *)fieldBuffer; 707 708 int32 dataOffset = sizeof(dano_fixed_size_array) 709 + field->name_length + 1; 710 dataOffset = pad_to_8(dataOffset); 711 int32 count = *(int32 *)(fieldBuffer + dataOffset); 712 dataOffset += 8; /* count and padding */ 713 714 if (offset + dataOffset + count * field->size_per_item > size) 715 return B_BAD_DATA; 716 717 status_t result = B_OK; 718 for (int32 i = 0; i < count; i++) { 719 result = into->AddData(field->name, field->type, 720 fieldBuffer + dataOffset, field->size_per_item, true, 721 count); 722 723 if (result < B_OK) { 724 free(fieldBuffer); 725 throw result; 726 } 727 728 dataOffset += field->size_per_item; 729 } 730 break; 731 } 732 733 case SECTION_VARIABLE_SIZE_ARRAY_DATA: { 734 dano_variable_size_array *field 735 = (dano_variable_size_array *)fieldBuffer; 736 737 int32 dataOffset = sizeof(dano_variable_size_array) 738 + field->name_length + 1; 739 dataOffset = pad_to_8(dataOffset); 740 int32 count = *(int32 *)(fieldBuffer + dataOffset); 741 dataOffset += sizeof(int32); 742 ssize_t totalSize = *(ssize_t *)(fieldBuffer + dataOffset); 743 dataOffset += sizeof(ssize_t); 744 745 int32 *endPoints = (int32 *)(fieldBuffer + dataOffset 746 + totalSize); 747 748 status_t result = B_OK; 749 for (int32 i = 0; i < count; i++) { 750 int32 itemOffset = (i > 0 ? pad_to_8(endPoints[i - 1]) : 0); 751 752 result = into->AddData(field->name, field->type, 753 fieldBuffer + dataOffset + itemOffset, 754 endPoints[i] - itemOffset, false, count); 755 756 if (result < B_OK) { 757 free(fieldBuffer); 758 throw result; 759 } 760 } 761 break; 762 } 763 } 764 765 free(fieldBuffer); 766 offset += sectionHeader.size; 767 } 768 769 return B_OK; 770 } 771 772 } // namespace BPrivate 773