1/* 2 * Copyright 2001-2012 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Rene Gollent (rene@gollent.com) 7 * Erik Jaesler (erik@cgsoftware.com) 8 * Alex Wilson (yourpalal2@gmail.com) 9 */ 10 11/*! BArchivable mix-in class defines the archiving protocol. 12 Also some global archiving functions. 13*/ 14 15 16#include <ctype.h> 17#include <errno.h> 18#include <stdlib.h> 19#include <stdio.h> 20#include <string> 21#include <syslog.h> 22#include <typeinfo> 23#include <vector> 24 25#include <AppFileInfo.h> 26#include <Archivable.h> 27#include <Entry.h> 28#include <List.h> 29#include <OS.h> 30#include <Path.h> 31#include <Roster.h> 32#include <String.h> 33 34#include <binary_compatibility/Support.h> 35 36#include "ArchivingManagers.h" 37 38 39using std::string; 40using std::vector; 41 42using namespace BPrivate::Archiving; 43 44const char* B_CLASS_FIELD = "class"; 45const char* B_ADD_ON_FIELD = "add_on"; 46const int32 FUNC_NAME_LEN = 1024; 47 48// TODO: consider moving these to a separate module, and making them more 49// full-featured (e.g., taking NS::ClassName::Function(Param p) instead 50// of just NS::ClassName) 51 52 53static status_t 54demangle_class_name(const char* name, BString& out) 55{ 56// TODO: add support for template classes 57// _find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl 58 59 out = ""; 60 61#if __GNUC__ >= 4 62 if (name[0] == 'N') 63 name++; 64 int nameLen; 65 bool first = true; 66 while ((nameLen = strtoul(name, (char**)&name, 10))) { 67 if (!first) 68 out += "::"; 69 else 70 first = false; 71 out.Append(name, nameLen); 72 name += nameLen; 73 } 74 if (first) 75 return B_BAD_VALUE; 76 77#else 78 if (name[0] == 'Q') { 79 // The name is in a namespace 80 int namespaceCount = 0; 81 name++; 82 if (name[0] == '_') { 83 // more than 10 namespaces deep 84 if (!isdigit(*++name)) 85 return B_BAD_VALUE; 86 87 namespaceCount = strtoul(name, (char**)&name, 10); 88 if (name[0] != '_') 89 return B_BAD_VALUE; 90 } else 91 namespaceCount = name[0] - '0'; 92 93 name++; 94 95 for (int i = 0; i < namespaceCount - 1; i++) { 96 if (!isdigit(name[0])) 97 return B_BAD_VALUE; 98 99 int nameLength = strtoul(name, (char**)&name, 10); 100 out.Append(name, nameLength); 101 out += "::"; 102 name += nameLength; 103 } 104 } 105 106 int nameLength = strtoul(name, (char**)&name, 10); 107 out.Append(name, nameLength); 108#endif 109 110 return B_OK; 111} 112 113 114static void 115mangle_class_name(const char* name, BString& out) 116{ 117// TODO: add support for template classes 118// _find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl 119 120 // Chop this: 121 // testthree::testfour::Testthree::Testfour 122 // up into little bite-sized pieces 123 int count = 0; 124 string origName(name); 125 vector<string> spacenames; 126 127 string::size_type pos = 0; 128 string::size_type oldpos = 0; 129 while (pos != string::npos) { 130 pos = origName.find_first_of("::", oldpos); 131 spacenames.push_back(string(origName, oldpos, pos - oldpos)); 132 pos = origName.find_first_not_of("::", pos); 133 oldpos = pos; 134 ++count; 135 } 136 137 // Now mangle it into this: 138 // 9testthree8testfour9Testthree8Testfour 139 // (for __GNUC__ > 2) 140 // this isn't always the proper mangled class name, it should 141 // actually have an 'N' prefix and 'E' suffix if the name is 142 // in > 0 namespaces, but these would have to be removed in 143 // build_function_name() (the only place this function is called) 144 // so we don't add them. 145 // or this: 146 // Q49testthree8testfour9Testthree8Testfour 147 // (for __GNUC__ == 2) 148 149 out = ""; 150#if __GNUC__ == 2 151 if (count > 1) { 152 out += 'Q'; 153 if (count > 10) 154 out += '_'; 155 out << count; 156 if (count > 10) 157 out += '_'; 158 } 159#endif 160 161 for (unsigned int i = 0; i < spacenames.size(); ++i) { 162 out << (int)spacenames[i].length(); 163 out += spacenames[i].c_str(); 164 } 165} 166 167 168static void 169build_function_name(const BString& className, BString& funcName) 170{ 171 funcName = ""; 172 173 // This is what we're after: 174 // Instantiate__Q28OpenBeOS11BArchivableP8BMessage 175 mangle_class_name(className.String(), funcName); 176#if __GNUC__ >= 4 177 funcName.Prepend("_ZN"); 178 funcName.Append("11InstantiateE"); 179#else 180 funcName.Prepend("Instantiate__"); 181#endif 182 funcName.Append("P8BMessage"); 183} 184 185 186static bool 187add_private_namespace(BString& name) 188{ 189 if (name.Compare("_", 1) != 0) 190 return false; 191 192 name.Prepend("BPrivate::"); 193 return true; 194} 195 196 197static instantiation_func 198find_function_in_image(BString& funcName, image_id id, status_t& err) 199{ 200 instantiation_func instantiationFunc = NULL; 201 err = get_image_symbol(id, funcName.String(), B_SYMBOL_TYPE_TEXT, 202 (void**)&instantiationFunc); 203 if (err != B_OK) 204 return NULL; 205 206 return instantiationFunc; 207} 208 209 210static status_t 211check_signature(const char* signature, image_info& info) 212{ 213 if (signature == NULL) { 214 // If it wasn't specified, anything "matches" 215 return B_OK; 216 } 217 218 // Get image signature 219 BFile file(info.name, B_READ_ONLY); 220 status_t err = file.InitCheck(); 221 if (err != B_OK) 222 return err; 223 224 char imageSignature[B_MIME_TYPE_LENGTH]; 225 BAppFileInfo appFileInfo(&file); 226 err = appFileInfo.GetSignature(imageSignature); 227 if (err != B_OK) { 228 syslog(LOG_ERR, "instantiate_object - couldn't get mime sig for %s", 229 info.name); 230 return err; 231 } 232 233 if (strcmp(signature, imageSignature) != 0) 234 return B_MISMATCHED_VALUES; 235 236 return B_OK; 237} 238 239 240namespace BPrivate { 241 242instantiation_func 243find_instantiation_func(const char* className, const char* signature, 244 image_id* id) 245{ 246 if (className == NULL) { 247 errno = B_BAD_VALUE; 248 return NULL; 249 } 250 251 thread_info threadInfo; 252 status_t err = get_thread_info(find_thread(NULL), &threadInfo); 253 if (err != B_OK) { 254 errno = err; 255 return NULL; 256 } 257 258 instantiation_func instantiationFunc = NULL; 259 image_info imageInfo; 260 261 BString name = className; 262 for (int32 pass = 0; pass < 2; pass++) { 263 BString funcName; 264 build_function_name(name, funcName); 265 266 // for each image_id in team_id 267 int32 cookie = 0; 268 while (instantiationFunc == NULL 269 && get_next_image_info(threadInfo.team, &cookie, &imageInfo) 270 == B_OK) { 271 instantiationFunc = find_function_in_image(funcName, imageInfo.id, 272 err); 273 } 274 if (instantiationFunc != NULL) { 275 // if requested, save the image id in 276 // which the function was found 277 if (id != NULL) 278 *id = imageInfo.id; 279 break; 280 } 281 282 // Check if we have a private class, and add the BPrivate namespace 283 // (for backwards compatibility) 284 if (!add_private_namespace(name)) 285 break; 286 } 287 288 if (instantiationFunc != NULL 289 && check_signature(signature, imageInfo) != B_OK) 290 return NULL; 291 292 return instantiationFunc; 293} 294 295} // namespace BPrivate 296 297 298// #pragma mark - BArchivable 299 300 301BArchivable::BArchivable() 302 : 303 fArchivingToken(NULL_TOKEN) 304{ 305} 306 307 308BArchivable::BArchivable(BMessage* from) 309 : 310 fArchivingToken(NULL_TOKEN) 311{ 312 if (BUnarchiver::IsArchiveManaged(from)) { 313 BUnarchiver::PrepareArchive(from); 314 BUnarchiver(from).RegisterArchivable(this); 315 } 316} 317 318 319BArchivable::~BArchivable() 320{ 321} 322 323 324status_t 325BArchivable::Archive(BMessage* into, bool deep) const 326{ 327 if (!into) { 328 // TODO: logging/other error reporting? 329 return B_BAD_VALUE; 330 } 331 332 if (BManagerBase::ArchiveManager(into)) 333 BArchiver(into).RegisterArchivable(this); 334 335 BString name; 336 status_t status = demangle_class_name(typeid(*this).name(), name); 337 if (status != B_OK) 338 return status; 339 340 return into->AddString(B_CLASS_FIELD, name); 341} 342 343 344BArchivable* 345BArchivable::Instantiate(BMessage* from) 346{ 347 debugger("Can't create a plain BArchivable object"); 348 return NULL; 349} 350 351 352status_t 353BArchivable::Perform(perform_code d, void* arg) 354{ 355 switch (d) { 356 case PERFORM_CODE_ALL_UNARCHIVED: 357 { 358 perform_data_all_unarchived* data = 359 (perform_data_all_unarchived*)arg; 360 361 data->return_value = BArchivable::AllUnarchived(data->archive); 362 return B_OK; 363 } 364 365 case PERFORM_CODE_ALL_ARCHIVED: 366 { 367 perform_data_all_archived* data = 368 (perform_data_all_archived*)arg; 369 370 data->return_value = BArchivable::AllArchived(data->archive); 371 return B_OK; 372 } 373 } 374 375 return B_NAME_NOT_FOUND; 376} 377 378 379status_t 380BArchivable::AllUnarchived(const BMessage* archive) 381{ 382 return B_OK; 383} 384 385 386status_t 387BArchivable::AllArchived(BMessage* archive) const 388{ 389 return B_OK; 390} 391 392 393// #pragma mark - BArchiver 394 395 396BArchiver::BArchiver(BMessage* archive) 397 : 398 fManager(BManagerBase::ArchiveManager(archive)), 399 fArchive(archive), 400 fFinished(false) 401{ 402 if (fManager == NULL) 403 fManager = new BArchiveManager(this); 404} 405 406 407BArchiver::~BArchiver() 408{ 409 if (!fFinished) 410 fManager->ArchiverLeaving(this, B_OK); 411} 412 413 414status_t 415BArchiver::AddArchivable(const char* name, BArchivable* archivable, bool deep) 416{ 417 int32 token; 418 status_t err = GetTokenForArchivable(archivable, deep, token); 419 420 if (err != B_OK) 421 return err; 422 423 return fArchive->AddInt32(name, token); 424} 425 426 427status_t 428BArchiver::GetTokenForArchivable(BArchivable* archivable, 429 bool deep, int32& _token) 430{ 431 return fManager->ArchiveObject(archivable, deep, _token); 432} 433 434 435bool 436BArchiver::IsArchived(BArchivable* archivable) 437{ 438 return fManager->IsArchived(archivable); 439} 440 441 442status_t 443BArchiver::Finish(status_t err) 444{ 445 if (fFinished) 446 debugger("Finish() called multiple times on same BArchiver."); 447 448 fFinished = true; 449 450 return fManager->ArchiverLeaving(this, err); 451} 452 453 454BMessage* 455BArchiver::ArchiveMessage() const 456{ 457 return fArchive; 458} 459 460 461void 462BArchiver::RegisterArchivable(const BArchivable* archivable) 463{ 464 fManager->RegisterArchivable(archivable); 465} 466 467 468// #pragma mark - BUnarchiver 469 470 471BUnarchiver::BUnarchiver(const BMessage* archive) 472 : 473 fManager(BManagerBase::UnarchiveManager(archive)), 474 fArchive(archive), 475 fFinished(false) 476{ 477} 478 479 480BUnarchiver::~BUnarchiver() 481{ 482 if (!fFinished && fManager) 483 fManager->UnarchiverLeaving(this, B_OK); 484} 485 486 487template<> 488status_t 489BUnarchiver::GetObject<BArchivable>(int32 token, 490 ownership_policy owning, BArchivable*& object) 491{ 492 _CallDebuggerIfManagerNull(); 493 return fManager->GetArchivableForToken(token, owning, object); 494} 495 496 497template<> 498status_t 499BUnarchiver::FindObject<BArchivable>(const char* name, 500 int32 index, ownership_policy owning, BArchivable*& archivable) 501{ 502 archivable = NULL; 503 int32 token; 504 status_t err = fArchive->FindInt32(name, index, &token); 505 if (err != B_OK) 506 return err; 507 508 return GetObject(token, owning, archivable); 509} 510 511 512bool 513BUnarchiver::IsInstantiated(int32 token) 514{ 515 _CallDebuggerIfManagerNull(); 516 return fManager->IsInstantiated(token); 517} 518 519 520bool 521BUnarchiver::IsInstantiated(const char* field, int32 index) 522{ 523 int32 token; 524 if (fArchive->FindInt32(field, index, &token) == B_OK) 525 return IsInstantiated(token); 526 527 return false; 528} 529 530 531status_t 532BUnarchiver::Finish(status_t err) 533{ 534 if (fFinished) 535 debugger("Finish() called multiple times on same BArchiver."); 536 537 fFinished = true; 538 if (fManager) 539 return fManager->UnarchiverLeaving(this, err); 540 else 541 return B_OK; 542} 543 544 545const BMessage* 546BUnarchiver::ArchiveMessage() const 547{ 548 return fArchive; 549} 550 551 552void 553BUnarchiver::AssumeOwnership(BArchivable* archivable) 554{ 555 _CallDebuggerIfManagerNull(); 556 fManager->AssumeOwnership(archivable); 557} 558 559 560void 561BUnarchiver::RelinquishOwnership(BArchivable* archivable) 562{ 563 _CallDebuggerIfManagerNull(); 564 fManager->RelinquishOwnership(archivable); 565} 566 567 568bool 569BUnarchiver::IsArchiveManaged(const BMessage* archive) 570{ 571 // managed child archives will return here 572 if (BManagerBase::ManagerPointer(archive)) 573 return true; 574 575 if (archive == NULL) 576 return false; 577 578 // managed top level archives return here 579 bool dummy; 580 if (archive->FindBool(kManagedField, &dummy) == B_OK) 581 return true; 582 583 return false; 584} 585 586 587template<> 588status_t 589BUnarchiver::InstantiateObject<BArchivable>(BMessage* from, 590 BArchivable* &object) 591{ 592 BUnarchiver unarchiver(BUnarchiver::PrepareArchive(from)); 593 object = instantiate_object(from); 594 return unarchiver.Finish(); 595} 596 597 598BMessage* 599BUnarchiver::PrepareArchive(BMessage* &archive) 600{ 601 // this check allows PrepareArchive to be 602 // called on new or old-style archives 603 if (BUnarchiver::IsArchiveManaged(archive)) { 604 BUnarchiveManager* manager = BManagerBase::UnarchiveManager(archive); 605 if (!manager) 606 manager = new BUnarchiveManager(archive); 607 608 manager->Acquire(); 609 } 610 611 return archive; 612} 613 614 615void 616BUnarchiver::RegisterArchivable(BArchivable* archivable) 617{ 618 _CallDebuggerIfManagerNull(); 619 fManager->RegisterArchivable(archivable); 620} 621 622 623void 624BUnarchiver::_CallDebuggerIfManagerNull() 625{ 626 if (!fManager) 627 debugger("BUnarchiver used with legacy or unprepared archive."); 628} 629 630 631// #pragma mark - 632 633 634BArchivable* 635instantiate_object(BMessage* archive, image_id* _id) 636{ 637 status_t statusBuffer; 638 status_t* status = &statusBuffer; 639 if (_id != NULL) 640 status = _id; 641 642 // Check our params 643 if (archive == NULL) { 644 syslog(LOG_ERR, "instantiate_object failed: NULL BMessage argument"); 645 *status = B_BAD_VALUE; 646 return NULL; 647 } 648 649 // Get class name from archive 650 const char* className = NULL; 651 status_t err = archive->FindString(B_CLASS_FIELD, &className); 652 if (err) { 653 syslog(LOG_ERR, "instantiate_object failed: Failed to find an entry " 654 "defining the class name (%s).", strerror(err)); 655 *status = B_BAD_VALUE; 656 return NULL; 657 } 658 659 // Get sig from archive 660 const char* signature = NULL; 661 bool hasSignature = archive->FindString(B_ADD_ON_FIELD, &signature) == B_OK; 662 663 instantiation_func instantiationFunc = BPrivate::find_instantiation_func( 664 className, signature, _id); 665 666 // if find_instantiation_func() can't locate Class::Instantiate() 667 // and a signature was specified 668 if (!instantiationFunc && hasSignature) { 669 // use BRoster::FindApp() to locate an app or add-on with the symbol 670 BRoster Roster; 671 entry_ref ref; 672 err = Roster.FindApp(signature, &ref); 673 674 // if an entry_ref is obtained 675 BEntry entry; 676 if (err == B_OK) 677 err = entry.SetTo(&ref); 678 679 BPath path; 680 if (err == B_OK) 681 err = entry.GetPath(&path); 682 683 if (err != B_OK) { 684 syslog(LOG_ERR, "instantiate_object failed: Error finding app " 685 "with signature \"%s\" (%s)", signature, strerror(err)); 686 *status = err; 687 return NULL; 688 } 689 690 // load the app/add-on 691 image_id addOn = load_add_on(path.Path()); 692 if (addOn < B_OK) { 693 syslog(LOG_ERR, "instantiate_object failed: Could not load " 694 "add-on %s: %s.", path.Path(), strerror(addOn)); 695 *status = addOn; 696 return NULL; 697 } 698 699 // Save the image_id 700 if (_id != NULL) 701 *_id = addOn; 702 703 BString name = className; 704 for (int32 pass = 0; pass < 2; pass++) { 705 BString funcName; 706 build_function_name(name, funcName); 707 708 instantiationFunc = find_function_in_image(funcName, addOn, err); 709 if (instantiationFunc != NULL) 710 break; 711 712 // Check if we have a private class, and add the BPrivate namespace 713 // (for backwards compatibility) 714 if (!add_private_namespace(name)) 715 break; 716 } 717 718 if (instantiationFunc == NULL) { 719 syslog(LOG_ERR, "instantiate_object failed: Failed to find exported " 720 "Instantiate static function for class %s.", className); 721 *status = B_NAME_NOT_FOUND; 722 return NULL; 723 } 724 } else if (instantiationFunc == NULL) { 725 syslog(LOG_ERR, "instantiate_object failed: No signature specified " 726 "in archive, looking for class \"%s\".", className); 727 *status = B_NAME_NOT_FOUND; 728 return NULL; 729 } 730 731 // if Class::Instantiate(BMessage*) was found 732 if (instantiationFunc != NULL) { 733 // use to create and return an object instance 734 return instantiationFunc(archive); 735 } 736 737 return NULL; 738} 739 740 741BArchivable* 742instantiate_object(BMessage* from) 743{ 744 return instantiate_object(from, NULL); 745} 746 747 748// #pragma mark - support_globals 749 750 751bool 752validate_instantiation(BMessage* from, const char* className) 753{ 754 // Make sure our params are kosher -- original skimped here =P 755 if (!from) { 756 errno = B_BAD_VALUE; 757 return false; 758 } 759 760 BString name = className; 761 for (int32 pass = 0; pass < 2; pass++) { 762 const char* archiveClassName; 763 for (int32 index = 0; from->FindString(B_CLASS_FIELD, index, 764 &archiveClassName) == B_OK; ++index) { 765 if (name == archiveClassName) { 766 errno = B_OK; 767 return true; 768 } 769 } 770 771 if (!add_private_namespace(name)) 772 break; 773 } 774 775 errno = B_MISMATCHED_VALUES; 776 syslog(LOG_ERR, "validate_instantiation failed on class %s.", className); 777 778 return false; 779} 780 781 782instantiation_func 783find_instantiation_func(const char* className, const char* signature) 784{ 785 return BPrivate::find_instantiation_func(className, signature, NULL); 786} 787 788 789instantiation_func 790find_instantiation_func(const char* className) 791{ 792 return find_instantiation_func(className, NULL); 793} 794 795 796instantiation_func 797find_instantiation_func(BMessage* archive) 798{ 799 if (archive == NULL) { 800 errno = B_BAD_VALUE; 801 return NULL; 802 } 803 804 const char* name = NULL; 805 const char* signature = NULL; 806 if (archive->FindString(B_CLASS_FIELD, &name) != B_OK 807 || archive->FindString(B_ADD_ON_FIELD, &signature)) { 808 errno = B_BAD_VALUE; 809 return NULL; 810 } 811 812 return find_instantiation_func(name, signature); 813} 814 815 816// #pragma mark - BArchivable binary compatibility 817 818 819#if __GNUC__ == 2 820 821extern "C" status_t 822_ReservedArchivable1__11BArchivable(BArchivable* archivable, 823 const BMessage* archive) 824{ 825 // AllUnarchived 826 perform_data_all_unarchived performData; 827 performData.archive = archive; 828 829 archivable->Perform(PERFORM_CODE_ALL_UNARCHIVED, &performData); 830 return performData.return_value; 831} 832 833 834extern "C" status_t 835_ReservedArchivable2__11BArchivable(BArchivable* archivable, 836 BMessage* archive) 837{ 838 // AllArchived 839 perform_data_all_archived performData; 840 performData.archive = archive; 841 842 archivable->Perform(PERFORM_CODE_ALL_ARCHIVED, &performData); 843 return performData.return_value; 844} 845 846 847#elif __GNUC__ > 2 848 849extern "C" status_t 850_ZN11BArchivable20_ReservedArchivable1Ev(BArchivable* archivable, 851 const BMessage* archive) 852{ 853 // AllUnarchived 854 perform_data_all_unarchived performData; 855 performData.archive = archive; 856 857 archivable->Perform(PERFORM_CODE_ALL_UNARCHIVED, &performData); 858 return performData.return_value; 859} 860 861 862extern "C" status_t 863_ZN11BArchivable20_ReservedArchivable2Ev(BArchivable* archivable, 864 BMessage* archive) 865{ 866 // AllArchived 867 perform_data_all_archived performData; 868 performData.archive = archive; 869 870 archivable->Perform(PERFORM_CODE_ALL_ARCHIVED, &performData); 871 return performData.return_value; 872} 873 874#endif // _GNUC__ > 2 875 876 877void BArchivable::_ReservedArchivable3() {} 878