xref: /haiku/src/kits/support/Archivable.cpp (revision e7d5c75dce28921de0dc981ed840205a67a0c0e5)
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 	// managed top level archives return here
515 	bool dummy;
516 	if (archive->FindBool(kManagedField, &dummy) == B_OK)
517 		return true;
518 
519 	return false;
520 }
521 
522 
523 template<>
524 status_t
525 BUnarchiver::InstantiateObject<BArchivable>(BMessage* from,
526 	BArchivable*& object)
527 {
528 	BUnarchiver unarchiver(BUnarchiver::PrepareArchive(from));
529 	object = instantiate_object(from);
530 	return unarchiver.Finish();
531 }
532 
533 
534 BMessage*
535 BUnarchiver::PrepareArchive(BMessage*& archive)
536 {
537 	// this check allows PrepareArchive to be
538 	// called on new or old-style archives
539 	if (BUnarchiver::IsArchiveManaged(archive)) {
540 		BUnarchiveManager* manager = BManagerBase::UnarchiveManager(archive);
541 		if (!manager)
542 			manager = new BUnarchiveManager(archive);
543 		manager->Acquire();
544 	}
545 	return archive;
546 }
547 
548 
549 void
550 BUnarchiver::RegisterArchivable(BArchivable* archivable)
551 {
552 	_CallDebuggerIfManagerNull();
553 	fManager->RegisterArchivable(archivable);
554 }
555 
556 
557 void
558 BUnarchiver::_CallDebuggerIfManagerNull()
559 {
560 	if (!fManager)
561 		debugger("BUnarchiver used with legacy or unprepared archive.");
562 }
563 
564 
565 // #pragma mark -
566 
567 
568 BArchivable*
569 instantiate_object(BMessage* archive, image_id* _id)
570 {
571 	status_t statusBuffer;
572 	status_t* status = &statusBuffer;
573 	if (_id != NULL)
574 		status = _id;
575 
576 	// Check our params
577 	if (archive == NULL) {
578 		syslog(LOG_ERR, "instantiate_object failed: NULL BMessage argument");
579 		*status = B_BAD_VALUE;
580 		return NULL;
581 	}
582 
583 	// Get class name from archive
584 	const char* className = NULL;
585 	status_t err = archive->FindString(B_CLASS_FIELD, &className);
586 	if (err) {
587 		syslog(LOG_ERR, "instantiate_object failed: Failed to find an entry "
588 			"defining the class name (%s).", strerror(err));
589 		*status = B_BAD_VALUE;
590 		return NULL;
591 	}
592 
593 	// Get sig from archive
594 	const char* signature = NULL;
595 	bool hasSignature = archive->FindString(B_ADD_ON_FIELD, &signature) == B_OK;
596 
597 	instantiation_func instantiationFunc = find_instantiation_func(className,
598 		signature);
599 
600 	// if find_instantiation_func() can't locate Class::Instantiate()
601 	// and a signature was specified
602 	if (!instantiationFunc && hasSignature) {
603 		// use BRoster::FindApp() to locate an app or add-on with the symbol
604 		BRoster Roster;
605 		entry_ref ref;
606 		err = Roster.FindApp(signature, &ref);
607 
608 		// if an entry_ref is obtained
609 		BEntry entry;
610 		if (err == B_OK)
611 			err = entry.SetTo(&ref);
612 
613 		BPath path;
614 		if (err == B_OK)
615 			err = entry.GetPath(&path);
616 
617 		if (err != B_OK) {
618 			syslog(LOG_ERR, "instantiate_object failed: Error finding app "
619 				"with signature \"%s\" (%s)", signature, strerror(err));
620 			*status = err;
621 			return NULL;
622 		}
623 
624 		// load the app/add-on
625 		image_id addOn = load_add_on(path.Path());
626 		if (addOn < B_OK) {
627 			syslog(LOG_ERR, "instantiate_object failed: Could not load "
628 				"add-on %s: %s.", path.Path(), strerror(addOn));
629 			*status = addOn;
630 			return NULL;
631 		}
632 
633 		// Save the image_id
634 		if (_id != NULL)
635 			*_id = addOn;
636 
637 		BString name = className;
638 		for (int32 pass = 0; pass < 2; pass++) {
639 			BString funcName;
640 			build_function_name(name, funcName);
641 
642 			instantiationFunc = find_function_in_image(funcName, addOn, err);
643 			if (instantiationFunc != NULL)
644 				break;
645 
646 			// Check if we have a private class, and add the BPrivate namespace
647 			// (for backwards compatibility)
648 			if (!add_private_namespace(name))
649 				break;
650 		}
651 
652 		if (instantiationFunc == NULL) {
653 			syslog(LOG_ERR, "instantiate_object failed: Failed to find exported "
654 				"Instantiate static function for class %s.", className);
655 			*status = B_NAME_NOT_FOUND;
656 			return NULL;
657 		}
658 	} else if (instantiationFunc == NULL) {
659 		syslog(LOG_ERR, "instantiate_object failed: No signature specified "
660 			"in archive, looking for class \"%s\".", className);
661 		*status = B_NAME_NOT_FOUND;
662 		return NULL;
663 	}
664 
665 	// if Class::Instantiate(BMessage*) was found
666 	if (instantiationFunc != NULL) {
667 		// use to create and return an object instance
668 		return instantiationFunc(archive);
669 	}
670 
671 	return NULL;
672 }
673 
674 
675 BArchivable*
676 instantiate_object(BMessage* from)
677 {
678 	return instantiate_object(from, NULL);
679 }
680 
681 
682 bool
683 validate_instantiation(BMessage* from, const char* className)
684 {
685 	// Make sure our params are kosher -- original skimped here =P
686 	if (!from) {
687 		errno = B_BAD_VALUE;
688 		return false;
689 	}
690 
691 	BString name = className;
692 	for (int32 pass = 0; pass < 2; pass++) {
693 		const char* archiveClassName;
694 		for (int32 index = 0; from->FindString(B_CLASS_FIELD, index,
695 				&archiveClassName) == B_OK; ++index) {
696 			if (name == archiveClassName)
697 				return true;
698 		}
699 
700 		if (!add_private_namespace(name))
701 			break;
702 	}
703 
704 	errno = B_MISMATCHED_VALUES;
705 	syslog(LOG_ERR, "validate_instantiation failed on class %s.", className);
706 
707 	return false;
708 }
709 
710 
711 instantiation_func
712 find_instantiation_func(const char* className, const char* signature)
713 {
714 	if (className == NULL) {
715 		errno = B_BAD_VALUE;
716 		return NULL;
717 	}
718 
719 	thread_info threadInfo;
720 	status_t err = get_thread_info(find_thread(NULL), &threadInfo);
721 	if (err != B_OK) {
722 		errno = err;
723 		return NULL;
724 	}
725 
726 	instantiation_func instantiationFunc = NULL;
727 	image_info imageInfo;
728 
729 	BString name = className;
730 	for (int32 pass = 0; pass < 2; pass++) {
731 		BString funcName;
732 		build_function_name(name, funcName);
733 
734 		// for each image_id in team_id
735 		int32 cookie = 0;
736 		while (instantiationFunc == NULL
737 			&& get_next_image_info(threadInfo.team, &cookie, &imageInfo)
738 				== B_OK) {
739 			instantiationFunc = find_function_in_image(funcName, imageInfo.id,
740 				err);
741 		}
742 		if (instantiationFunc != NULL)
743 			break;
744 
745 		// Check if we have a private class, and add the BPrivate namespace
746 		// (for backwards compatibility)
747 		if (!add_private_namespace(name))
748 			break;
749 	}
750 
751 	if (instantiationFunc != NULL
752 		&& check_signature(signature, imageInfo) != B_OK)
753 		return NULL;
754 
755 	return instantiationFunc;
756 }
757 
758 
759 instantiation_func
760 find_instantiation_func(const char* className)
761 {
762 	return find_instantiation_func(className, NULL);
763 }
764 
765 
766 instantiation_func
767 find_instantiation_func(BMessage* archive)
768 {
769 	if (archive == NULL) {
770 		errno = B_BAD_VALUE;
771 		return NULL;
772 	}
773 
774 	const char* name = NULL;
775 	const char* signature = NULL;
776 	if (archive->FindString(B_CLASS_FIELD, &name) != B_OK
777 		|| archive->FindString(B_ADD_ON_FIELD, &signature)) {
778 		errno = B_BAD_VALUE;
779 		return NULL;
780 	}
781 
782 	return find_instantiation_func(name, signature);
783 }
784 
785 
786 // BArchivable binary compatability
787 #if __GNUC__ == 2
788 
789 
790 extern "C" status_t
791 _ReservedArchivable1__11BArchivable(BArchivable* archivable,
792 	const BMessage* archive)
793 {
794 	// AllUnarchived
795 	perform_data_all_unarchived performData;
796 	performData.archive = archive;
797 
798 	archivable->Perform(PERFORM_CODE_ALL_UNARCHIVED, &performData);
799 	return performData.return_value;
800 }
801 
802 
803 extern "C" status_t
804 _ReservedArchivable2__11BArchivable(BArchivable* archivable,
805 	BMessage* archive)
806 {
807 	// AllArchived
808 	perform_data_all_archived performData;
809 	performData.archive = archive;
810 
811 	archivable->Perform(PERFORM_CODE_ALL_ARCHIVED, &performData);
812 	return performData.return_value;
813 }
814 
815 
816 #elif __GNUC__ > 2
817 
818 
819 extern "C" status_t
820 _ZN11BArchivable20_ReservedArchivable1Ev(BArchivable* archivable,
821 	const BMessage* archive)
822 {
823 	// AllUnarchived
824 	perform_data_all_unarchived performData;
825 	performData.archive = archive;
826 
827 	archivable->Perform(PERFORM_CODE_ALL_UNARCHIVED, &performData);
828 	return performData.return_value;
829 }
830 
831 
832 extern "C" status_t
833 _ZN11BArchivable20_ReservedArchivable2Ev(BArchivable* archivable,
834 	BMessage* archive)
835 {
836 	// AllArchived
837 	perform_data_all_archived performData;
838 	performData.archive = archive;
839 
840 	archivable->Perform(PERFORM_CODE_ALL_ARCHIVED, &performData);
841 	return performData.return_value;
842 }
843 
844 
845 #endif // _GNUC__ > 2
846 
847 
848 void BArchivable::_ReservedArchivable3() {}
849 
850 
851