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