1 /* 2 * Copyright (c) 2001-2008, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Erik Jaesler (erik@cgsoftware.com) 7 */ 8 9 /*! BArchivable mix-in class defines the archiving protocol. 10 Also some global archiving functions. 11 */ 12 13 14 #include <ctype.h> 15 #include <errno.h> 16 #include <stdlib.h> 17 #include <stdio.h> 18 #include <string> 19 #include <syslog.h> 20 #include <typeinfo> 21 #include <vector> 22 23 #include <AppFileInfo.h> 24 #include <Archivable.h> 25 #include <Entry.h> 26 #include <List.h> 27 #include <OS.h> 28 #include <Path.h> 29 #include <Roster.h> 30 #include <String.h> 31 32 33 using std::string; 34 using std::vector; 35 36 const char* B_CLASS_FIELD = "class"; 37 const char* B_ADD_ON_FIELD = "add_on"; 38 const int32 FUNC_NAME_LEN = 1024; 39 40 // TODO: consider moving these to a separate module, and making them more 41 // full-featured (e.g., taking NS::ClassName::Function(Param p) instead 42 // of just NS::ClassName) 43 44 45 static status_t 46 demangle_class_name(const char* name, BString& out) 47 { 48 // TODO: add support for template classes 49 // _find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl 50 51 out = ""; 52 53 if (name[0] == 'Q') { 54 // The name is in a namespace 55 int namespaceCount = 0; 56 name++; 57 if (name[0] == '_') { 58 // more than 10 namespaces deep 59 if (!isdigit(*++name)) 60 return B_BAD_VALUE; 61 62 namespaceCount = strtoul(name, (char**)&name, 10); 63 if (name[0] != '_') 64 return B_BAD_VALUE; 65 } else 66 namespaceCount = name[0] - '0'; 67 68 name++; 69 70 for (int i = 0; i < namespaceCount - 1; i++) { 71 if (!isdigit(name[0])) 72 return B_BAD_VALUE; 73 74 int nameLength = strtoul(name, (char**)&name, 10); 75 out.Append(name, nameLength); 76 out += "::"; 77 name += nameLength; 78 } 79 } 80 81 int nameLength = strtoul(name, (char**)&name, 10); 82 out.Append(name, nameLength); 83 84 return B_OK; 85 } 86 87 88 static void 89 mangle_class_name(const char* name, BString& out) 90 { 91 // TODO: add support for template classes 92 // _find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl 93 94 // Chop this: 95 // testthree::testfour::Testthree::Testfour 96 // up into little bite-sized pieces 97 int count = 0; 98 string origName(name); 99 vector<string> spacenames; 100 101 string::size_type pos = 0; 102 string::size_type oldpos = 0; 103 while (pos != string::npos) { 104 pos = origName.find_first_of("::", oldpos); 105 spacenames.push_back(string(origName, oldpos, pos - oldpos)); 106 pos = origName.find_first_not_of("::", pos); 107 oldpos = pos; 108 ++count; 109 } 110 111 // Now mangle it into this: 112 // Q49testthree8testfour9Testthree8Testfour 113 out = ""; 114 if (count > 1) { 115 out += 'Q'; 116 if (count > 10) 117 out += '_'; 118 out << count; 119 if (count > 10) 120 out += '_'; 121 } 122 123 for (unsigned int i = 0; i < spacenames.size(); ++i) { 124 out << (int)spacenames[i].length(); 125 out += spacenames[i].c_str(); 126 } 127 } 128 129 130 static void 131 build_function_name(const BString& className, BString& funcName) 132 { 133 funcName = ""; 134 135 // This is what we're after: 136 // Instantiate__Q28OpenBeOS11BArchivableP8BMessage 137 mangle_class_name(className.String(), funcName); 138 #if __GNUC__ >= 4 139 funcName.Prepend("_ZN"); 140 funcName.Append("11InstantiateE"); 141 #else 142 funcName.Prepend("Instantiate__"); 143 #endif 144 funcName.Append("P8BMessage"); 145 } 146 147 148 static bool 149 add_private_namespace(BString& name) 150 { 151 if (name.Compare("_", 1) != 0) 152 return false; 153 154 name.Prepend("BPrivate::"); 155 return true; 156 } 157 158 159 static instantiation_func 160 find_function_in_image(BString& funcName, image_id id, status_t& err) 161 { 162 instantiation_func instantiationFunc = NULL; 163 err = get_image_symbol(id, funcName.String(), B_SYMBOL_TYPE_TEXT, 164 (void**)&instantiationFunc); 165 if (err != B_OK) 166 return NULL; 167 168 return instantiationFunc; 169 } 170 171 172 static status_t 173 check_signature(const char* signature, image_info& info) 174 { 175 if (signature == NULL) { 176 // If it wasn't specified, anything "matches" 177 return B_OK; 178 } 179 180 // Get image signature 181 BFile file(info.name, B_READ_ONLY); 182 status_t err = file.InitCheck(); 183 if (err != B_OK) 184 return err; 185 186 char imageSignature[B_MIME_TYPE_LENGTH]; 187 BAppFileInfo appFileInfo(&file); 188 err = appFileInfo.GetSignature(imageSignature); 189 if (err != B_OK) { 190 syslog(LOG_ERR, "instantiate_object - couldn't get mime sig for %s", 191 info.name); 192 return err; 193 } 194 195 if (strcmp(signature, imageSignature)) 196 return B_MISMATCHED_VALUES; 197 198 return B_OK; 199 } 200 201 202 // #pragma mark - 203 204 205 BArchivable::BArchivable() 206 { 207 } 208 209 210 BArchivable::BArchivable(BMessage* from) 211 { 212 } 213 214 215 BArchivable::~BArchivable() 216 { 217 } 218 219 220 status_t 221 BArchivable::Archive(BMessage* into, bool deep) const 222 { 223 if (!into) { 224 // TODO: logging/other error reporting? 225 return B_BAD_VALUE; 226 } 227 228 BString name; 229 status_t status = demangle_class_name(typeid(*this).name(), name); 230 if (status != B_OK) 231 return status; 232 233 return into->AddString(B_CLASS_FIELD, name); 234 } 235 236 237 BArchivable* 238 BArchivable::Instantiate(BMessage* from) 239 { 240 debugger("Can't create a plain BArchivable object"); 241 return NULL; 242 } 243 244 245 status_t 246 BArchivable::Perform(perform_code d, void* arg) 247 { 248 // TODO: Check against original 249 return B_ERROR; 250 } 251 252 253 void BArchivable::_ReservedArchivable1() {} 254 void BArchivable::_ReservedArchivable2() {} 255 void BArchivable::_ReservedArchivable3() {} 256 257 258 // #pragma mark - 259 260 261 BArchivable* 262 instantiate_object(BMessage* archive, image_id* _id) 263 { 264 status_t statusBuffer; 265 status_t* status = &statusBuffer; 266 if (_id != NULL) 267 status = _id; 268 269 // Check our params 270 if (archive == NULL) { 271 syslog(LOG_ERR, "instantiate_object failed: NULL BMessage argument"); 272 *status = B_BAD_VALUE; 273 return NULL; 274 } 275 276 // Get class name from archive 277 const char* className = NULL; 278 status_t err = archive->FindString(B_CLASS_FIELD, &className); 279 if (err) { 280 syslog(LOG_ERR, "instantiate_object failed: Failed to find an entry " 281 "defining the class name (%s).", strerror(err)); 282 *status = B_BAD_VALUE; 283 return NULL; 284 } 285 286 // Get sig from archive 287 const char* signature = NULL; 288 bool hasSignature = archive->FindString(B_ADD_ON_FIELD, &signature) == B_OK; 289 290 instantiation_func instantiationFunc = find_instantiation_func(className, 291 signature); 292 293 // if find_instantiation_func() can't locate Class::Instantiate() 294 // and a signature was specified 295 if (!instantiationFunc && hasSignature) { 296 // use BRoster::FindApp() to locate an app or add-on with the symbol 297 BRoster Roster; 298 entry_ref ref; 299 err = Roster.FindApp(signature, &ref); 300 301 // if an entry_ref is obtained 302 BEntry entry; 303 if (err == B_OK) 304 err = entry.SetTo(&ref); 305 306 BPath path; 307 if (err == B_OK) 308 err = entry.GetPath(&path); 309 310 if (err != B_OK) { 311 syslog(LOG_ERR, "instantiate_object failed: Error finding app " 312 "with signature \"%s\" (%s)", signature, strerror(err)); 313 *status = err; 314 return NULL; 315 } 316 317 // load the app/add-on 318 image_id addOn = load_add_on(path.Path()); 319 if (addOn < B_OK) { 320 syslog(LOG_ERR, "instantiate_object failed: Could not load " 321 "add-on %s: %s.", path.Path(), strerror(addOn)); 322 *status = addOn; 323 return NULL; 324 } 325 326 // Save the image_id 327 if (_id != NULL) 328 *_id = addOn; 329 330 BString name = className; 331 for (int32 pass = 0; pass < 2; pass++) { 332 BString funcName; 333 build_function_name(name, funcName); 334 335 instantiationFunc = find_function_in_image(funcName, addOn, err); 336 if (instantiationFunc != NULL) 337 break; 338 339 // Check if we have a private class, and add the BPrivate namespace 340 // (for backwards compatibility) 341 if (!add_private_namespace(name)) 342 break; 343 } 344 345 if (instantiationFunc == NULL) { 346 syslog(LOG_ERR, "instantiate_object failed: Failed to find exported " 347 "Instantiate static function for class %s.", className); 348 *status = B_NAME_NOT_FOUND; 349 return NULL; 350 } 351 } else if (instantiationFunc == NULL) { 352 syslog(LOG_ERR, "instantiate_object failed: No signature specified " 353 "in archive, looking for class \"%s\".", className); 354 *status = B_NAME_NOT_FOUND; 355 return NULL; 356 } 357 358 // if Class::Instantiate(BMessage*) was found 359 if (instantiationFunc != NULL) { 360 // use to create and return an object instance 361 return instantiationFunc(archive); 362 } 363 364 return NULL; 365 } 366 367 368 BArchivable* 369 instantiate_object(BMessage* from) 370 { 371 return instantiate_object(from, NULL); 372 } 373 374 375 bool 376 validate_instantiation(BMessage* from, const char* className) 377 { 378 // Make sure our params are kosher -- original skimped here =P 379 if (!from) { 380 errno = B_BAD_VALUE; 381 return false; 382 } 383 384 BString name = className; 385 for (int32 pass = 0; pass < 2; pass++) { 386 const char* archiveClassName; 387 for (int32 index = 0; from->FindString(B_CLASS_FIELD, index, 388 &archiveClassName) == B_OK; ++index) { 389 if (name == archiveClassName) 390 return true; 391 } 392 393 if (!add_private_namespace(name)) 394 break; 395 } 396 397 errno = B_MISMATCHED_VALUES; 398 syslog(LOG_ERR, "validate_instantiation failed on class %s.", className); 399 400 return false; 401 } 402 403 404 instantiation_func 405 find_instantiation_func(const char* className, const char* signature) 406 { 407 if (className == NULL) { 408 errno = B_BAD_VALUE; 409 return NULL; 410 } 411 412 thread_info threadInfo; 413 status_t err = get_thread_info(find_thread(NULL), &threadInfo); 414 if (err != B_OK) { 415 errno = err; 416 return NULL; 417 } 418 419 instantiation_func instantiationFunc = NULL; 420 image_info imageInfo; 421 422 BString name = className; 423 for (int32 pass = 0; pass < 2; pass++) { 424 BString funcName; 425 build_function_name(name, funcName); 426 427 // for each image_id in team_id 428 int32 cookie = 0; 429 while (instantiationFunc == NULL 430 && get_next_image_info(threadInfo.team, &cookie, &imageInfo) 431 == B_OK) { 432 instantiationFunc = find_function_in_image(funcName, imageInfo.id, 433 err); 434 } 435 if (instantiationFunc != NULL) 436 break; 437 438 // Check if we have a private class, and add the BPrivate namespace 439 // (for backwards compatibility) 440 if (!add_private_namespace(name)) 441 break; 442 } 443 444 if (instantiationFunc != NULL 445 && check_signature(signature, imageInfo) != B_OK) 446 return NULL; 447 448 return instantiationFunc; 449 } 450 451 452 instantiation_func 453 find_instantiation_func(const char* className) 454 { 455 return find_instantiation_func(className, NULL); 456 } 457 458 459 instantiation_func 460 find_instantiation_func(BMessage* archive) 461 { 462 if (archive == NULL) { 463 errno = B_BAD_VALUE; 464 return NULL; 465 } 466 467 const char* name = NULL; 468 const char* signature = NULL; 469 if (archive->FindString(B_CLASS_FIELD, &name) != B_OK 470 || archive->FindString(B_ADD_ON_FIELD, &signature)) { 471 errno = B_BAD_VALUE; 472 return NULL; 473 } 474 475 return find_instantiation_func(name, signature); 476 } 477 478