xref: /haiku/src/kits/support/Archivable.cpp (revision aed35104852941f0f6f3d1dcc5338b5f337d0a3c)
1 /*
2  * Copyright (c) 2001-2012, Haiku, Inc.
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 
39 using std::string;
40 using std::vector;
41 
42 using namespace BPrivate::Archiving;
43 
44 const char* B_CLASS_FIELD = "class";
45 const char* B_ADD_ON_FIELD = "add_on";
46 const 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 
53 static status_t
54 demangle_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 
114 static void
115 mangle_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 
168 static void
169 build_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 
186 static bool
187 add_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 
197 static instantiation_func
198 find_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 
210 static status_t
211 check_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))
234 		return B_MISMATCHED_VALUES;
235 
236 	return B_OK;
237 }
238 
239 
240 namespace BPrivate
241 {
242 
243 
244 instantiation_func
245 find_instantiation_func(const char* className, const char* signature,
246 	image_id* id)
247 {
248 	if (className == NULL) {
249 		errno = B_BAD_VALUE;
250 		return NULL;
251 	}
252 
253 	thread_info threadInfo;
254 	status_t err = get_thread_info(find_thread(NULL), &threadInfo);
255 	if (err != B_OK) {
256 		errno = err;
257 		return NULL;
258 	}
259 
260 	instantiation_func instantiationFunc = NULL;
261 	image_info imageInfo;
262 
263 	BString name = className;
264 	for (int32 pass = 0; pass < 2; pass++) {
265 		BString funcName;
266 		build_function_name(name, funcName);
267 
268 		// for each image_id in team_id
269 		int32 cookie = 0;
270 		while (instantiationFunc == NULL
271 			&& get_next_image_info(threadInfo.team, &cookie, &imageInfo)
272 				== B_OK) {
273 			instantiationFunc = find_function_in_image(funcName, imageInfo.id,
274 				err);
275 		}
276 		if (instantiationFunc != NULL) {
277 			// if requested, save the image id in
278 			// which the function was found
279 			if (id != NULL)
280 				*id = imageInfo.id;
281 			break;
282 		}
283 
284 		// Check if we have a private class, and add the BPrivate namespace
285 		// (for backwards compatibility)
286 		if (!add_private_namespace(name))
287 			break;
288 	}
289 
290 	if (instantiationFunc != NULL
291 		&& check_signature(signature, imageInfo) != B_OK)
292 		return NULL;
293 
294 	return instantiationFunc;
295 }
296 
297 
298 }
299 
300 
301 //	#pragma mark -
302 
303 
304 BArchivable::BArchivable()
305 	:
306 	fArchivingToken(NULL_TOKEN)
307 {
308 }
309 
310 
311 BArchivable::BArchivable(BMessage* from)
312 	:
313 	fArchivingToken(NULL_TOKEN)
314 {
315 	if (BUnarchiver::IsArchiveManaged(from)) {
316 		BUnarchiver::PrepareArchive(from);
317 		BUnarchiver(from).RegisterArchivable(this);
318 	}
319 }
320 
321 
322 BArchivable::~BArchivable()
323 {
324 }
325 
326 
327 status_t
328 BArchivable::Archive(BMessage* into, bool deep) const
329 {
330 	if (!into) {
331 		// TODO: logging/other error reporting?
332 		return B_BAD_VALUE;
333 	}
334 
335 	if (BManagerBase::ArchiveManager(into))
336 		BArchiver(into).RegisterArchivable(this);
337 
338 	BString name;
339 	status_t status = demangle_class_name(typeid(*this).name(), name);
340 	if (status != B_OK)
341 		return status;
342 
343 	return into->AddString(B_CLASS_FIELD, name);
344 }
345 
346 
347 BArchivable*
348 BArchivable::Instantiate(BMessage* from)
349 {
350 	debugger("Can't create a plain BArchivable object");
351 	return NULL;
352 }
353 
354 
355 status_t
356 BArchivable::Perform(perform_code d, void* arg)
357 {
358 	switch (d) {
359 		case PERFORM_CODE_ALL_UNARCHIVED:
360 		{
361 			perform_data_all_unarchived* data =
362 				(perform_data_all_unarchived*)arg;
363 
364 			data->return_value = BArchivable::AllUnarchived(data->archive);
365 			return B_OK;
366 		}
367 
368 		case PERFORM_CODE_ALL_ARCHIVED:
369 		{
370 			perform_data_all_archived* data =
371 				(perform_data_all_archived*)arg;
372 
373 			data->return_value = BArchivable::AllArchived(data->archive);
374 			return B_OK;
375 		}
376 	}
377 	return B_NAME_NOT_FOUND;
378 }
379 
380 
381 status_t
382 BArchivable::AllUnarchived(const BMessage* archive)
383 {
384 	return B_OK;
385 }
386 
387 
388 status_t
389 BArchivable::AllArchived(BMessage* archive) const
390 {
391 	return B_OK;
392 }
393 
394 
395 // #pragma mark -
396 
397 
398 BArchiver::BArchiver(BMessage* archive)
399 	:
400 	fManager(BManagerBase::ArchiveManager(archive)),
401 	fArchive(archive),
402 	fFinished(false)
403 {
404 	if (!fManager)
405 		fManager = new BArchiveManager(this);
406 }
407 
408 
409 BArchiver::~BArchiver()
410 {
411 	if (!fFinished)
412 		fManager->ArchiverLeaving(this, B_OK);
413 }
414 
415 
416 status_t
417 BArchiver::AddArchivable(const char* name, BArchivable* archivable, bool deep)
418 {
419 	int32 token;
420 	status_t err = GetTokenForArchivable(archivable, deep, token);
421 
422 	if (err != B_OK)
423 		return err;
424 
425 	return fArchive->AddInt32(name, token);
426 }
427 
428 
429 status_t
430 BArchiver::GetTokenForArchivable(BArchivable* archivable,
431 	bool deep, int32& _token)
432 {
433 	return fManager->ArchiveObject(archivable, deep, _token);
434 }
435 
436 
437 bool
438 BArchiver::IsArchived(BArchivable* archivable)
439 {
440 	return fManager->IsArchived(archivable);
441 }
442 
443 
444 status_t
445 BArchiver::Finish(status_t err)
446 {
447 	if (fFinished)
448 		debugger("Finish() called multiple times on same BArchiver.");
449 
450 	fFinished = true;
451 
452 	return fManager->ArchiverLeaving(this, err);
453 }
454 
455 
456 BMessage*
457 BArchiver::ArchiveMessage() const
458 {
459 	return fArchive;
460 }
461 
462 
463 void
464 BArchiver::RegisterArchivable(const BArchivable* archivable)
465 {
466 	fManager->RegisterArchivable(archivable);
467 }
468 
469 
470 // #pragma mark -
471 
472 
473 BUnarchiver::BUnarchiver(const BMessage* archive)
474 	:
475 	fManager(BManagerBase::UnarchiveManager(archive)),
476 	fArchive(archive),
477 	fFinished(false)
478 {
479 }
480 
481 
482 BUnarchiver::~BUnarchiver()
483 {
484 	if (!fFinished && fManager)
485 		fManager->UnarchiverLeaving(this, B_OK);
486 }
487 
488 
489 template<>
490 status_t
491 BUnarchiver::GetObject<BArchivable>(int32 token,
492 	ownership_policy owning, BArchivable*& object)
493 {
494 	_CallDebuggerIfManagerNull();
495 	return fManager->GetArchivableForToken(token, owning, object);
496 }
497 
498 
499 template<>
500 status_t
501 BUnarchiver::FindObject<BArchivable>(const char* name,
502 	int32 index, ownership_policy owning, BArchivable*& archivable)
503 {
504 	archivable = NULL;
505 	int32 token;
506 	status_t err = fArchive->FindInt32(name, index, &token);
507 	if (err != B_OK)
508 		return err;
509 
510 	return GetObject(token, owning, archivable);
511 }
512 
513 
514 bool
515 BUnarchiver::IsInstantiated(int32 token)
516 {
517 	_CallDebuggerIfManagerNull();
518 	return fManager->IsInstantiated(token);
519 }
520 
521 
522 bool
523 BUnarchiver::IsInstantiated(const char* field, int32 index)
524 {
525 	int32 token;
526 	if (fArchive->FindInt32(field, index, &token) == B_OK)
527 		return IsInstantiated(token);
528 	return false;
529 }
530 
531 
532 status_t
533 BUnarchiver::Finish(status_t err)
534 {
535 	if (fFinished)
536 		debugger("Finish() called multiple times on same BArchiver.");
537 
538 	fFinished = true;
539 	if (fManager)
540 		return fManager->UnarchiverLeaving(this, err);
541 	else
542 		return B_OK;
543 }
544 
545 
546 const BMessage*
547 BUnarchiver::ArchiveMessage() const
548 {
549 	return fArchive;
550 }
551 
552 
553 void
554 BUnarchiver::AssumeOwnership(BArchivable* archivable)
555 {
556 	_CallDebuggerIfManagerNull();
557 	fManager->AssumeOwnership(archivable);
558 }
559 
560 
561 void
562 BUnarchiver::RelinquishOwnership(BArchivable* archivable)
563 {
564 	_CallDebuggerIfManagerNull();
565 	fManager->RelinquishOwnership(archivable);
566 }
567 
568 
569 bool
570 BUnarchiver::IsArchiveManaged(const BMessage* archive)
571 {
572 	// managed child archives will return here
573 	if (BManagerBase::ManagerPointer(archive))
574 		return true;
575 
576 	if (!archive)
577 		return false;
578 
579 	// managed top level archives return here
580 	bool dummy;
581 	if (archive->FindBool(kManagedField, &dummy) == B_OK)
582 		return true;
583 
584 	return false;
585 }
586 
587 
588 template<>
589 status_t
590 BUnarchiver::InstantiateObject<BArchivable>(BMessage* from,
591 	BArchivable*& object)
592 {
593 	BUnarchiver unarchiver(BUnarchiver::PrepareArchive(from));
594 	object = instantiate_object(from);
595 	return unarchiver.Finish();
596 }
597 
598 
599 BMessage*
600 BUnarchiver::PrepareArchive(BMessage*& archive)
601 {
602 	// this check allows PrepareArchive to be
603 	// called on new or old-style archives
604 	if (BUnarchiver::IsArchiveManaged(archive)) {
605 		BUnarchiveManager* manager = BManagerBase::UnarchiveManager(archive);
606 		if (!manager)
607 			manager = new BUnarchiveManager(archive);
608 		manager->Acquire();
609 	}
610 	return archive;
611 }
612 
613 
614 void
615 BUnarchiver::RegisterArchivable(BArchivable* archivable)
616 {
617 	_CallDebuggerIfManagerNull();
618 	fManager->RegisterArchivable(archivable);
619 }
620 
621 
622 void
623 BUnarchiver::_CallDebuggerIfManagerNull()
624 {
625 	if (!fManager)
626 		debugger("BUnarchiver used with legacy or unprepared archive.");
627 }
628 
629 
630 // #pragma mark -
631 
632 
633 BArchivable*
634 instantiate_object(BMessage* archive, image_id* _id)
635 {
636 	status_t statusBuffer;
637 	status_t* status = &statusBuffer;
638 	if (_id != NULL)
639 		status = _id;
640 
641 	// Check our params
642 	if (archive == NULL) {
643 		syslog(LOG_ERR, "instantiate_object failed: NULL BMessage argument");
644 		*status = B_BAD_VALUE;
645 		return NULL;
646 	}
647 
648 	// Get class name from archive
649 	const char* className = NULL;
650 	status_t err = archive->FindString(B_CLASS_FIELD, &className);
651 	if (err) {
652 		syslog(LOG_ERR, "instantiate_object failed: Failed to find an entry "
653 			"defining the class name (%s).", strerror(err));
654 		*status = B_BAD_VALUE;
655 		return NULL;
656 	}
657 
658 	// Get sig from archive
659 	const char* signature = NULL;
660 	bool hasSignature = archive->FindString(B_ADD_ON_FIELD, &signature) == B_OK;
661 
662 	instantiation_func instantiationFunc = BPrivate::find_instantiation_func(
663 		className, signature, _id);
664 
665 	// if find_instantiation_func() can't locate Class::Instantiate()
666 	// and a signature was specified
667 	if (!instantiationFunc && hasSignature) {
668 		// use BRoster::FindApp() to locate an app or add-on with the symbol
669 		BRoster Roster;
670 		entry_ref ref;
671 		err = Roster.FindApp(signature, &ref);
672 
673 		// if an entry_ref is obtained
674 		BEntry entry;
675 		if (err == B_OK)
676 			err = entry.SetTo(&ref);
677 
678 		BPath path;
679 		if (err == B_OK)
680 			err = entry.GetPath(&path);
681 
682 		if (err != B_OK) {
683 			syslog(LOG_ERR, "instantiate_object failed: Error finding app "
684 				"with signature \"%s\" (%s)", signature, strerror(err));
685 			*status = err;
686 			return NULL;
687 		}
688 
689 		// load the app/add-on
690 		image_id addOn = load_add_on(path.Path());
691 		if (addOn < B_OK) {
692 			syslog(LOG_ERR, "instantiate_object failed: Could not load "
693 				"add-on %s: %s.", path.Path(), strerror(addOn));
694 			*status = addOn;
695 			return NULL;
696 		}
697 
698 		// Save the image_id
699 		if (_id != NULL)
700 			*_id = addOn;
701 
702 		BString name = className;
703 		for (int32 pass = 0; pass < 2; pass++) {
704 			BString funcName;
705 			build_function_name(name, funcName);
706 
707 			instantiationFunc = find_function_in_image(funcName, addOn, err);
708 			if (instantiationFunc != NULL)
709 				break;
710 
711 			// Check if we have a private class, and add the BPrivate namespace
712 			// (for backwards compatibility)
713 			if (!add_private_namespace(name))
714 				break;
715 		}
716 
717 		if (instantiationFunc == NULL) {
718 			syslog(LOG_ERR, "instantiate_object failed: Failed to find exported "
719 				"Instantiate static function for class %s.", className);
720 			*status = B_NAME_NOT_FOUND;
721 			return NULL;
722 		}
723 	} else if (instantiationFunc == NULL) {
724 		syslog(LOG_ERR, "instantiate_object failed: No signature specified "
725 			"in archive, looking for class \"%s\".", className);
726 		*status = B_NAME_NOT_FOUND;
727 		return NULL;
728 	}
729 
730 	// if Class::Instantiate(BMessage*) was found
731 	if (instantiationFunc != NULL) {
732 		// use to create and return an object instance
733 		return instantiationFunc(archive);
734 	}
735 
736 	return NULL;
737 }
738 
739 
740 BArchivable*
741 instantiate_object(BMessage* from)
742 {
743 	return instantiate_object(from, NULL);
744 }
745 
746 
747 bool
748 validate_instantiation(BMessage* from, const char* className)
749 {
750 	// Make sure our params are kosher -- original skimped here =P
751 	if (!from) {
752 		errno = B_BAD_VALUE;
753 		return false;
754 	}
755 
756 	BString name = className;
757 	for (int32 pass = 0; pass < 2; pass++) {
758 		const char* archiveClassName;
759 		for (int32 index = 0; from->FindString(B_CLASS_FIELD, index,
760 				&archiveClassName) == B_OK; ++index) {
761 			if (name == archiveClassName)
762 				return true;
763 		}
764 
765 		if (!add_private_namespace(name))
766 			break;
767 	}
768 
769 	errno = B_MISMATCHED_VALUES;
770 	syslog(LOG_ERR, "validate_instantiation failed on class %s.", className);
771 
772 	return false;
773 }
774 
775 
776 instantiation_func
777 find_instantiation_func(const char* className, const char* signature)
778 {
779 	return BPrivate::find_instantiation_func(className, signature, NULL);
780 }
781 
782 
783 instantiation_func
784 find_instantiation_func(const char* className)
785 {
786 	return find_instantiation_func(className, NULL);
787 }
788 
789 
790 instantiation_func
791 find_instantiation_func(BMessage* archive)
792 {
793 	if (archive == NULL) {
794 		errno = B_BAD_VALUE;
795 		return NULL;
796 	}
797 
798 	const char* name = NULL;
799 	const char* signature = NULL;
800 	if (archive->FindString(B_CLASS_FIELD, &name) != B_OK
801 		|| archive->FindString(B_ADD_ON_FIELD, &signature)) {
802 		errno = B_BAD_VALUE;
803 		return NULL;
804 	}
805 
806 	return find_instantiation_func(name, signature);
807 }
808 
809 
810 // BArchivable binary compatibility
811 #if __GNUC__ == 2
812 
813 
814 extern "C" status_t
815 _ReservedArchivable1__11BArchivable(BArchivable* archivable,
816 	const BMessage* archive)
817 {
818 	// AllUnarchived
819 	perform_data_all_unarchived performData;
820 	performData.archive = archive;
821 
822 	archivable->Perform(PERFORM_CODE_ALL_UNARCHIVED, &performData);
823 	return performData.return_value;
824 }
825 
826 
827 extern "C" status_t
828 _ReservedArchivable2__11BArchivable(BArchivable* archivable,
829 	BMessage* archive)
830 {
831 	// AllArchived
832 	perform_data_all_archived performData;
833 	performData.archive = archive;
834 
835 	archivable->Perform(PERFORM_CODE_ALL_ARCHIVED, &performData);
836 	return performData.return_value;
837 }
838 
839 
840 #elif __GNUC__ > 2
841 
842 
843 extern "C" status_t
844 _ZN11BArchivable20_ReservedArchivable1Ev(BArchivable* archivable,
845 	const BMessage* archive)
846 {
847 	// AllUnarchived
848 	perform_data_all_unarchived performData;
849 	performData.archive = archive;
850 
851 	archivable->Perform(PERFORM_CODE_ALL_UNARCHIVED, &performData);
852 	return performData.return_value;
853 }
854 
855 
856 extern "C" status_t
857 _ZN11BArchivable20_ReservedArchivable2Ev(BArchivable* archivable,
858 	BMessage* archive)
859 {
860 	// AllArchived
861 	perform_data_all_archived performData;
862 	performData.archive = archive;
863 
864 	archivable->Perform(PERFORM_CODE_ALL_ARCHIVED, &performData);
865 	return performData.return_value;
866 }
867 
868 
869 #endif // _GNUC__ > 2
870 
871 
872 void BArchivable::_ReservedArchivable3() {}
873 
874 
875