xref: /haiku/src/apps/packageinstaller/PackageItem.cpp (revision 8e8f7748d39f8407894a5ab79f7b4f93bc4f4652)
1 /*
2  * Copyright 2007-2009, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Author:
6  *		Łukasz 'Sil2100' Zemczak <sil2100@vexillium.org>
7  */
8 
9 
10 #include "PackageItem.h"
11 
12 #include <string.h>
13 
14 #include <Alert.h>
15 #include <ByteOrder.h>
16 #include <Catalog.h>
17 #include <Directory.h>
18 #include <FindDirectory.h>
19 #include <fs_info.h>
20 #include <Locale.h>
21 #include <NodeInfo.h>
22 #include <OS.h>
23 #include <SymLink.h>
24 #include <Volume.h>
25 
26 #include "zlib.h"
27 
28 
29 #undef B_TRANSLATION_CONTEXT
30 #define B_TRANSLATION_CONTEXT "PackageItem"
31 
32 enum {
33 	P_CHUNK_SIZE = 256
34 };
35 
36 static const uint32 kDefaultMode = 0777;
37 static const uint8 padding[7] = { 0, 0, 0, 0, 0, 0, 0 };
38 
39 enum {
40 	P_DATA = 0,
41 	P_ATTRIBUTE
42 };
43 
44 extern const char **environ;
45 
46 
47 status_t
48 inflate_data(uint8 *in, uint32 inSize, uint8 *out, uint32 outSize)
49 {
50 	parser_debug("inflate_data() called - input_size: %ld, output_size: %ld\n",
51 		inSize, outSize);
52 	z_stream stream;
53 	stream.zalloc = Z_NULL;
54 	stream.zfree = Z_NULL;
55 	stream.opaque = Z_NULL;
56 	stream.avail_in = inSize;
57 	stream.next_in = in;
58 	status_t ret;
59 
60 	ret = inflateInit(&stream);
61 	if (ret != Z_OK) {
62 		parser_debug("inflatInit failed\n");
63 		return B_ERROR;
64 	}
65 
66 	stream.avail_out = outSize;
67 	stream.next_out = out;
68 
69 	ret = inflate(&stream, Z_NO_FLUSH);
70 	if (ret != Z_STREAM_END) {
71 		// Uncompressed file size in package info corrupted
72 		parser_debug("Left: %d\n", stream.avail_out);
73 		return B_ERROR;
74 	}
75 
76 	inflateEnd(&stream);
77 	return B_OK;
78 }
79 
80 
81 static inline int
82 inflate_file_to_file(BFile *in, uint64 in_size, BFile *out, uint64 out_size)
83 {
84 	z_stream stream;
85 	stream.zalloc = Z_NULL;
86 	stream.zfree = Z_NULL;
87 	stream.opaque = Z_NULL;
88 	stream.avail_in = 0;
89 	stream.next_in = Z_NULL;
90 	status_t ret;
91 
92 	uint8 buffer_out[P_CHUNK_SIZE], buffer_in[P_CHUNK_SIZE];
93 	uint64 bytes_read = 0, read = P_CHUNK_SIZE, write = 0;
94 
95 	ret = inflateInit(&stream);
96 	if (ret != Z_OK) {
97 		parser_debug("inflate_file_to_file: inflateInit failed\n");
98 		return B_ERROR;
99 	}
100 
101 	do {
102 		bytes_read += P_CHUNK_SIZE;
103 		if (bytes_read > in_size) {
104 			read = in_size - (bytes_read - P_CHUNK_SIZE);
105 			bytes_read = in_size;
106 		}
107 
108 		stream.avail_in = in->Read(buffer_in, read);
109 		if (stream.avail_in != read) {
110 			parser_debug("inflate_file_to_file: read failed\n");
111 			(void)inflateEnd(&stream);
112 			return B_ERROR;
113 		}
114 		stream.next_in = buffer_in;
115 
116 		do {
117 			stream.avail_out = P_CHUNK_SIZE;
118 			stream.next_out = buffer_out;
119 
120 			ret = inflate(&stream, Z_NO_FLUSH);
121 			if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) {
122 				parser_debug("inflate_file_to_file: inflate failed\n");
123 				(void)inflateEnd(&stream);
124 				return B_ERROR;
125 			}
126 
127 			write = P_CHUNK_SIZE - stream.avail_out;
128 			if (static_cast<uint64>(out->Write(buffer_out, write)) != write) {
129 				parser_debug("inflate_file_to_file: write failed\n");
130 				(void)inflateEnd(&stream);
131 				return B_ERROR;
132 			}
133 		} while (stream.avail_out == 0);
134 	} while (bytes_read != in_size);
135 
136 	(void)inflateEnd(&stream);
137 
138 	return B_OK;
139 }
140 
141 
142 // #pragma mark - PackageItem
143 
144 
145 PackageItem::PackageItem(BFile* parent, const BString& path, uint8 type,
146 	uint32 ctime, uint32 mtime, uint64 offset, uint64 size)
147 {
148 	SetTo(parent, path, type, ctime, mtime, offset, size);
149 }
150 
151 
152 PackageItem::~PackageItem()
153 {
154 }
155 
156 
157 void
158 PackageItem::SetTo(BFile* parent, const BString& path, uint8 type, uint32 ctime,
159 	uint32 mtime, uint64 offset, uint64 size)
160 {
161 	fPackage = parent;
162 	fPath = path;
163 
164 	fOffset = offset;
165 	fSize = size;
166 	fPathType = type;
167 	fCreationTime = ctime;
168 	fModificationTime = mtime;
169 }
170 
171 
172 status_t
173 PackageItem::InitPath(const char* path, BPath* destination)
174 {
175 	status_t ret = B_OK;
176 
177 	if (fPathType == P_INSTALL_PATH) {
178 //		printf("InitPath - relative: %s + %s\n", path, fPath.String());
179 		if (path == NULL) {
180 			parser_debug("InitPath path is NULL\n");
181 			return B_ERROR;
182 		}
183 		ret = destination->SetTo(path, fPath.String());
184 	} else if (fPathType == P_SYSTEM_PATH) {
185 //		printf("InitPath - absolute: %s\n", fPath.String());
186 		ret = destination->SetTo(fPath.String());
187 	} else {
188 //		printf("InitPath - volume: %s + %s\n", path, fPath.String());
189 		if (path == NULL) {
190 			parser_debug("InitPath path is NULL\n");
191 			return B_ERROR;
192 		}
193 
194 		BVolume volume(dev_for_path(path));
195 		ret = volume.InitCheck();
196 		if (ret == B_OK) {
197 			BDirectory temp;
198 			ret = volume.GetRootDirectory(&temp);
199 			if (ret == B_OK) {
200 				BPath mountPoint(&temp, NULL);
201 				ret = destination->SetTo(mountPoint.Path(), fPath.String());
202 			}
203 		}
204 	}
205 
206 	if (ret != B_OK) {
207 		fprintf(stderr, "InitPath(%s): %s\n", path, strerror(ret));
208 		return ret;
209 	}
210 
211 	BString pathString(destination->Path());
212 
213 	// Hardcoded paths, the .pkg files hardcode this to the same
214 	if (pathString.FindFirst("non-packaged") < 0) {
215 		bool wasRewritten = false;
216 
217 		if (pathString.StartsWith("/boot/beos/system")) {
218 			BPath systemNonPackagedDir;
219 			find_directory(B_SYSTEM_NONPACKAGED_DIRECTORY,
220 				&systemNonPackagedDir);
221 			pathString.ReplaceFirst("/boot/beos/system",
222 				systemNonPackagedDir.Path());
223 			wasRewritten = true;
224 		} else if (pathString.StartsWith("/boot/home/config")) {
225 			BPath userNonPackagedDir;
226 			find_directory(B_USER_NONPACKAGED_DIRECTORY, &userNonPackagedDir);
227 			pathString.ReplaceFirst("/boot/home/config",
228 				userNonPackagedDir.Path());
229 			wasRewritten = true;
230 		}
231 
232 		if (wasRewritten) {
233 //			printf("rewritten: %s\n", pathString.String());
234 			destination->SetTo(pathString.String());
235 		}
236 	}
237 
238 	return ret;
239 }
240 
241 
242 status_t
243 PackageItem::HandleAttributes(BPath *destination, BNode *node,
244 	const char *header)
245 {
246 	status_t ret = B_OK;
247 
248 	BVolume volume(dev_for_path(destination->Path()));
249 	if (volume.KnowsAttr()) {
250 		parser_debug("We have an offset\n");
251 		if (!fPackage)
252 			return B_ERROR;
253 
254 		ret = fPackage->InitCheck();
255 		if (ret != B_OK)
256 			return ret;
257 
258 		// We need to parse the data section now
259 		fPackage->Seek(fOffset, SEEK_SET);
260 		uint8 buffer[7];
261 		if (fPackage->Read(buffer, 7) != 7 || memcmp(buffer, header, 5))
262 			return B_ERROR;
263 		parser_debug("Header validated!\n");
264 
265 		char *attrName = 0;
266 		uint32 nameSize = 0;
267 		uint8 *attrData = new uint8[P_CHUNK_SIZE];
268 		uint64 dataSize = P_CHUNK_SIZE;
269 		uint8 *temp = new uint8[P_CHUNK_SIZE];
270 		uint64 tempSize = P_CHUNK_SIZE;
271 
272 		uint64 attrCSize = 0, attrOSize = 0;
273 		uint32 attrType = 0; // type_code type
274 		bool attrStarted = false, done = false;
275 
276 		while (fPackage->Read(buffer, 7) == 7) {
277 			if (!memcmp(buffer, "FBeA", 5))
278 				continue;
279 
280 			ret = ParseAttribute(buffer, node, &attrName, &nameSize, &attrType,
281 				&attrData, &dataSize, &temp, &tempSize, &attrCSize, &attrOSize,
282 				&attrStarted, &done);
283 			if (ret != B_OK || done) {
284 				if (ret != B_OK) {
285 					parser_debug("_ParseAttribute failed for %s\n",
286 						destination->Path());
287 				}
288 				break;
289 			}
290 		}
291 
292 		delete[] attrData;
293 		delete[] temp;
294 	}
295 
296 	return ret;
297 }
298 
299 
300 status_t
301 PackageItem::ParseAttribute(uint8* buffer, BNode* node, char** attrName,
302 	uint32* nameSize, uint32* attrType, uint8** attrData, uint64* dataSize,
303 	uint8** temp, uint64* tempSize, uint64* attrCSize, uint64* attrOSize,
304 	bool* attrStarted, bool* done)
305 {
306 	status_t ret = B_OK;
307 	uint32 length;
308 
309 	if (!memcmp(buffer, "BeAI", 5)) {
310 		parser_debug(" Attribute started.\n");
311 		if (*attrName)
312 			*attrName[0] = 0;
313 		*attrCSize = 0;
314 		*attrOSize = 0;
315 
316 		*attrStarted = true;
317 	} else if (!memcmp(buffer, "BeAN", 5)) {
318 		if (!*attrStarted) {
319 			ret = B_ERROR;
320 			return ret;
321 		}
322 
323 		parser_debug(" BeAN.\n");
324 		fPackage->Read(&length, 4);
325 		swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
326 			B_SWAP_BENDIAN_TO_HOST);
327 
328 		if (*nameSize < (length + 1)) {
329 			delete[] *attrName;
330 			*nameSize = length + 1;
331 			*attrName = new char[*nameSize];
332 		}
333 		fPackage->Read(*attrName, length);
334 		(*attrName)[length] = 0;
335 
336 		parser_debug(" (%ld) = %s\n", length, *attrName);
337 	} else if (!memcmp(buffer, "BeAT", 5)) {
338 		if (!*attrStarted) {
339 			ret = B_ERROR;
340 			return ret;
341 		}
342 
343 		parser_debug(" BeAT.\n");
344 		fPackage->Read(attrType, 4);
345 		swap_data(B_UINT32_TYPE, attrType, sizeof(*attrType),
346 				B_SWAP_BENDIAN_TO_HOST);
347 	} else if (!memcmp(buffer, "BeAD", 5)) {
348 		if (!*attrStarted) {
349 			ret = B_ERROR;
350 			return ret;
351 		}
352 
353 		parser_debug(" BeAD.\n");
354 		fPackage->Read(attrCSize, 8);
355 		swap_data(B_UINT64_TYPE, attrCSize, sizeof(*attrCSize),
356 				B_SWAP_BENDIAN_TO_HOST);
357 
358 		fPackage->Read(attrOSize, 8);
359 		swap_data(B_UINT64_TYPE, attrOSize, sizeof(*attrOSize),
360 				B_SWAP_BENDIAN_TO_HOST);
361 
362 		fPackage->Seek(4, SEEK_CUR); // TODO: Check what this means
363 
364 		if (*tempSize < *attrCSize) {
365 			delete[] *temp;
366 			*tempSize = *attrCSize;
367 			*temp = new uint8[*tempSize];
368 		}
369 		if (*dataSize < *attrOSize) {
370 			delete[] *attrData;
371 			*dataSize = *attrOSize;
372 			*attrData = new uint8[*dataSize];
373 		}
374 
375 		if (fPackage->Read(*temp, *attrCSize)
376 				!= static_cast<ssize_t>(*attrCSize)) {
377 			ret = B_ERROR;
378 			return ret;
379 		}
380 
381 		parser_debug("  Data read successfuly. Inflating!\n");
382 		ret = inflate_data(*temp, *tempSize, *attrData, *dataSize);
383 		if (ret != B_OK)
384 			return ret;
385 	} else if (!memcmp(buffer, padding, 7)) {
386 		if (!*attrStarted) {
387 			*done = true;
388 			return ret;
389 		}
390 
391 		parser_debug(" Padding.\n");
392 		ssize_t wrote = node->WriteAttr(*attrName, *attrType, 0, *attrData,
393 			*attrOSize);
394 		if (wrote != static_cast<ssize_t>(*attrOSize)) {
395 			parser_debug("Failed to write attribute %s %s\n", *attrName, strerror(wrote));
396 			return B_ERROR;
397 		}
398 
399 		*attrStarted = false;
400 		if (*attrName)
401 			*attrName[0] = 0;
402 		*attrCSize = 0;
403 		*attrOSize = 0;
404 
405 		parser_debug(" > Attribute added.\n");
406 	} else {
407 		parser_debug(" Unknown attribute\n");
408 		ret = B_ERROR;
409 	}
410 
411 	return ret;
412 }
413 
414 
415 status_t
416 PackageItem::SkipAttribute(uint8* buffer, bool* attrStarted, bool* done)
417 {
418 	status_t ret = B_OK;
419 	uint32 length;
420 
421 	if (!memcmp(buffer, "BeAI", 5)) {
422 		parser_debug(" Attribute started.\n");
423 		*attrStarted = true;
424 	} else if (!memcmp(buffer, "BeAN", 5)) {
425 		if (!*attrStarted) {
426 			ret = B_ERROR;
427 			return ret;
428 		}
429 
430 		parser_debug(" BeAN.\n");
431 		fPackage->Read(&length, 4);
432 		swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
433 			B_SWAP_BENDIAN_TO_HOST);
434 
435 		fPackage->Seek(length, SEEK_CUR);
436 	} else if (!memcmp(buffer, "BeAT", 5)) {
437 		if (!*attrStarted) {
438 			ret = B_ERROR;
439 			return ret;
440 		}
441 
442 		parser_debug(" BeAT.\n");
443 		fPackage->Seek(4, SEEK_CUR);
444 	} else if (!memcmp(buffer, "BeAD", 5)) {
445 		if (!*attrStarted) {
446 			ret = B_ERROR;
447 			return ret;
448 		}
449 
450 		parser_debug(" BeAD.\n");
451 		uint64 length64;
452 		fPackage->Read(&length64, 8);
453 		swap_data(B_UINT64_TYPE, &length64, sizeof(length64),
454 			B_SWAP_BENDIAN_TO_HOST);
455 
456 		fPackage->Seek(12 + length64, SEEK_CUR);
457 
458 		parser_debug("  Data skipped successfuly.\n");
459 	} else if (!memcmp(buffer, padding, 7)) {
460 		if (!*attrStarted) {
461 			*done = true;
462 			return ret;
463 		}
464 
465 		parser_debug(" Padding.\n");
466 		*attrStarted = false;
467 		parser_debug(" > Attribute skipped.\n");
468 	} else {
469 		parser_debug(" Unknown attribute\n");
470 		ret = B_ERROR;
471 	}
472 
473 	return ret;
474 }
475 
476 
477 status_t
478 PackageItem::ParseData(uint8* buffer, BFile* file, uint64 originalSize,
479 	bool *done)
480 {
481 	status_t ret = B_OK;
482 
483 	if (!memcmp(buffer, "FiMF", 5)) {
484 		parser_debug(" Found file data.\n");
485 		uint64 compressed, original;
486 		fPackage->Read(&compressed, 8);
487 		swap_data(B_UINT64_TYPE, &compressed, sizeof(uint64),
488 				B_SWAP_BENDIAN_TO_HOST);
489 
490 		fPackage->Read(&original, 8);
491 		swap_data(B_UINT64_TYPE, &original, sizeof(uint64),
492 				B_SWAP_BENDIAN_TO_HOST);
493 		parser_debug(" Still good... (%llu : %llu)\n", original,
494 				originalSize);
495 
496 		if (original != originalSize) {
497 			parser_debug(" File size mismatch\n");
498 			return B_ERROR; // File size mismatch
499 		}
500 		parser_debug(" Still good...\n");
501 
502 		if (fPackage->Read(buffer, 4) != 4) {
503 			parser_debug(" Read(buffer, 4) failed\n");
504 			return B_ERROR;
505 		}
506 		parser_debug(" Still good...\n");
507 
508 		ret = inflate_file_to_file(fPackage, compressed, file, original);
509 		if (ret != B_OK) {
510 			parser_debug(" inflate_file_to_file failed\n");
511 			return ret;
512 		}
513 		parser_debug(" File data inflation complete!\n");
514 	} else if (!memcmp(buffer, padding, 7)) {
515 		*done = true;
516 		return ret;
517 	} else {
518 		parser_debug("_ParseData unknown tag\n");
519 		ret = B_ERROR;
520 	}
521 
522 	return ret;
523 }
524 
525 
526 // #pragma mark - PackageScript
527 
528 
529 PackageScript::PackageScript(BFile* parent, const BString& path, uint8 type,
530 		uint64 offset, uint64 size, uint64 originalSize)
531 	:
532 	PackageItem(parent, path, type, 0, 0, offset, size),
533 	fOriginalSize(originalSize),
534 	fThreadId(-1)
535 {
536 }
537 
538 
539 status_t
540 PackageScript::DoInstall(const char* path, ItemState* state)
541 {
542 	status_t ret = B_OK;
543 	parser_debug("Script: DoInstall() called!\n");
544 
545 	if (fOffset) {
546 		parser_debug("We have an offset\n");
547 		if (!fPackage)
548 			return B_ERROR;
549 
550 		ret = fPackage->InitCheck();
551 		if (ret != B_OK)
552 			return ret;
553 
554 		// We need to parse the data section now
555 		fPackage->Seek(fOffset, SEEK_SET);
556 		uint8 buffer[7];
557 		bool attrStarted = false, done = false;
558 
559 		uint8 section = P_ATTRIBUTE;
560 
561 		while (fPackage->Read(buffer, 7) == 7) {
562 			if (!memcmp(buffer, "FBeA", 5)) {
563 				parser_debug("-> Attribute\n");
564 				section = P_ATTRIBUTE;
565 				continue;
566 			} else if (!memcmp(buffer, "FiDa", 5)) {
567 				parser_debug("-> File data\n");
568 				section = P_DATA;
569 				continue;
570 			}
571 
572 			switch (section) {
573 				case P_ATTRIBUTE:
574 					ret = SkipAttribute(buffer, &attrStarted, &done);
575 					break;
576 
577 				case P_DATA:
578 				{
579 					BString script;
580 					ret = _ParseScript(buffer, fOriginalSize, script, &done);
581 					if (ret == B_OK) {
582 						// Rewrite Deskbar entry targets. NOTE: It would
583 						// also work to Replace("/config/be", "/config...")
584 						// but it would be less save. For example, an app
585 						// could have a folder named "config/be..." inside
586 						// its installation folder.
587 						// TODO: Use find_paths() or we are no better than
588 						// these scripts.
589 						script.ReplaceAll(
590 							"~/config/be",
591 							"~/config/settings/deskbar/menu");
592 						script.ReplaceAll(
593 							"/boot/home/config/be",
594 							"/boot/home/config/settings/deskbar/menu");
595 						// Rewrite all sorts of other old BeOS paths
596 						script.ReplaceAll(
597 							"/boot/preferences",
598 							"/boot/system/preferences");
599 						script.ReplaceAll(
600 							"~/config/add-ons/Screen\\ Savers",
601 							"~/config/non-packaged/add-ons/Screen\\ Savers");
602 						// TODO: More. These should also be put into a
603 						// common source location, since it can also be used
604 						// for the retargetting of install file locations.
605 						// Packages seem to declare which system paths they
606 						// use, and then package items can reference one of
607 						// those global paths by index. A more elegent solution
608 						// compared to what happens now in InitPath() would be
609 						// to replace those global package paths. Or maybe
610 						// that's more fragile... but a common source for
611 						// the rewriting of BeOS paths is needed.
612 
613 //						printf("%s\n", script.String());
614 
615 						BPath workingDirectory;
616 						if (path != NULL)
617 							ret = InitPath(path, &workingDirectory);
618 						else
619 							ret = workingDirectory.SetTo(".");
620 						if (ret == B_OK)
621 							ret = _RunScript(workingDirectory.Path(), script);
622 					}
623 					break;
624 				}
625 
626 				default:
627 					return B_ERROR;
628 			}
629 
630 			if (ret != B_OK || done)
631 				break;
632 		}
633 	}
634 
635 	parser_debug("Ret: %ld %s\n", ret, strerror(ret));
636 	return ret;
637 }
638 
639 
640 const uint32
641 PackageScript::ItemKind()
642 {
643 	return P_KIND_SCRIPT;
644 }
645 
646 
647 status_t
648 PackageScript::_ParseScript(uint8 *buffer, uint64 originalSize,
649 	BString& _script, bool *done)
650 {
651 	status_t ret = B_OK;
652 
653 	if (!memcmp(buffer, "FiMF", 5)) {
654 		parser_debug(" Found file (script) data.\n");
655 		uint64 compressed, original;
656 		fPackage->Read(&compressed, 8);
657 		swap_data(B_UINT64_TYPE, &compressed, sizeof(uint64),
658 				B_SWAP_BENDIAN_TO_HOST);
659 
660 		fPackage->Read(&original, 8);
661 		swap_data(B_UINT64_TYPE, &original, sizeof(uint64),
662 				B_SWAP_BENDIAN_TO_HOST);
663 		parser_debug(" Still good... (%llu : %llu)\n", original,
664 				originalSize);
665 
666 		if (original != originalSize) {
667 			parser_debug(" File size mismatch\n");
668 			return B_ERROR; // File size mismatch
669 		}
670 		parser_debug(" Still good...\n");
671 
672 		if (fPackage->Read(buffer, 4) != 4) {
673 			parser_debug(" Read(buffer, 4) failed\n");
674 			return B_ERROR;
675 		}
676 		parser_debug(" Still good...\n");
677 
678 		uint8 *temp = new uint8[compressed];
679 		if (fPackage->Read(temp, compressed) != (int64)compressed) {
680 			parser_debug(" Read(temp, compressed) failed\n");
681 			delete[] temp;
682 			return B_ERROR;
683 		}
684 
685 		uint8* script = new uint8[original];
686 		ret = inflate_data(temp, compressed, script, original);
687 		if (ret != B_OK) {
688 			parser_debug(" inflate_data failed\n");
689 			delete[] temp;
690 			delete[] script;
691 			return ret;
692 		}
693 
694 		_script.SetTo((char*)script, originalSize);
695 
696 		delete[] script;
697 		delete[] temp;
698 		parser_debug(" Script data inflation complete!\n");
699 	} else if (!memcmp(buffer, padding, 7)) {
700 		*done = true;
701 		return ret;
702 	} else {
703 		parser_debug("_ParseData unknown tag\n");
704 		ret = B_ERROR;
705 	}
706 
707 	return ret;
708 }
709 
710 
711 status_t
712 PackageScript::_RunScript(const char* workingDirectory, const BString& script)
713 {
714 	// This function written by Peter Folk <pfolk@uni.uiuc.edu>
715 	// and published in the BeDevTalk FAQ, modified for use in the
716 	// PackageInstaller
717 	// http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209
718 
719 	// Change current working directory to install path
720 	char oldWorkingDirectory[B_PATH_NAME_LENGTH];
721 	getcwd(oldWorkingDirectory, sizeof(oldWorkingDirectory));
722 	chdir(workingDirectory);
723 
724 	// Save current FDs
725 	int old_in  =  dup(0);
726 	int old_out  =  dup(1);
727 	int old_err  =  dup(2);
728 
729 	int filedes[2];
730 
731 	/* Create new pipe FDs as stdin, stdout, stderr */
732 	pipe(filedes);  dup2(filedes[0], 0); close(filedes[0]);
733 	int in = filedes[1];  // Write to in, appears on cmd's stdin
734 	pipe(filedes);  dup2(filedes[1], 1); close(filedes[1]);
735 	pipe(filedes);  dup2(filedes[1], 2); close(filedes[1]);
736 
737 	const char **argv = new const char * [3];
738 	argv[0] = strdup("/bin/sh");
739 	argv[1] = strdup("-s");
740 	argv[2] = NULL;
741 
742 	// "load" command.
743 	fThreadId = load_image(2, argv, environ);
744 
745 	int i;
746 	for (i = 0; i < 2; i++)
747 		delete argv[i];
748 	delete [] argv;
749 
750 	if (fThreadId < B_OK)
751 		return fThreadId;
752 
753 	// thread id is now suspended.
754 	setpgid(fThreadId, fThreadId);
755 
756 	// Restore old FDs
757 	close(0); dup(old_in); close(old_in);
758 	close(1); dup(old_out); close(old_out);
759 	close(2); dup(old_err); close(old_err);
760 
761 	set_thread_priority(fThreadId, B_LOW_PRIORITY);
762 	resume_thread(fThreadId);
763 
764 	// Write the script
765 	if (write(in, script.String(), script.Length() - 1) != script.Length() - 1
766 		|| write(in, "\nexit\n", 6) != 6) {
767 		parser_debug("Writing script failed\n");
768 		kill_thread(fThreadId);
769 		return B_ERROR;
770 	}
771 
772 	// Restore current working directory
773 	chdir(oldWorkingDirectory);
774 
775 	return B_OK;
776 }
777 
778 
779 // #pragma mark - PackageDirectory
780 
781 
782 PackageDirectory::PackageDirectory(BFile* parent, const BString& path,
783 		uint8 type, uint32 ctime, uint32 mtime, uint64 offset, uint64 size)
784 	:
785 	PackageItem(parent, path, type, ctime, mtime, offset, size)
786 {
787 }
788 
789 
790 status_t
791 PackageDirectory::DoInstall(const char* path, ItemState* state)
792 {
793 	BPath &destination = state->destination;
794 	status_t ret;
795 	parser_debug("Directory: %s DoInstall() called!\n", fPath.String());
796 
797 	ret = InitPath(path, &destination);
798 	parser_debug("Ret: %ld %s\n", ret, strerror(ret));
799 	if (ret != B_OK)
800 		return ret;
801 
802 	// Since Haiku is single-user right now, we give the newly
803 	// created directory default permissions
804 	ret = create_directory(destination.Path(), kDefaultMode);
805 	parser_debug("Create dir ret: %ld %s\n", ret, strerror(ret));
806 	if (ret != B_OK)
807 		return ret;
808 	BDirectory dir(destination.Path());
809 	parser_debug("Directory created!\n");
810 
811 	if (fCreationTime)
812 		dir.SetCreationTime(static_cast<time_t>(fCreationTime));
813 
814 	if (fModificationTime)
815 		dir.SetModificationTime(static_cast<time_t>(fModificationTime));
816 
817 	// Since directories can only have attributes in the offset section,
818 	// we can check here whether it is necessary to continue
819 	if (fOffset)
820 		ret = HandleAttributes(&destination, &dir, "FoDa");
821 
822 	parser_debug("Ret: %ld %s\n", ret, strerror(ret));
823 	return ret;
824 }
825 
826 
827 const uint32
828 PackageDirectory::ItemKind()
829 {
830 	return P_KIND_DIRECTORY;
831 }
832 
833 
834 //	#pragma mark - PackageFile
835 
836 
837 PackageFile::PackageFile(BFile *parent, const BString &path, uint8 type,
838 		uint32 ctime, uint32 mtime, uint64 offset, uint64 size,
839 		uint64 originalSize, uint32 platform, const BString &mime,
840 		const BString &signature, uint32 mode)
841 	:
842 	PackageItem(parent, path, type, ctime, mtime, offset, size),
843 	fOriginalSize(originalSize),
844 	fPlatform(platform),
845 	fMode(mode),
846 	fMimeType(mime),
847 	fSignature(signature)
848 {
849 }
850 
851 
852 status_t
853 PackageFile::DoInstall(const char* path, ItemState* state)
854 {
855 	if (state == NULL)
856 		return B_ERROR;
857 
858 	BPath& destination = state->destination;
859 	status_t ret = B_OK;
860 	parser_debug("File: %s DoInstall() called!\n", fPath.String());
861 
862 	BFile file;
863 	if (state->status == B_NO_INIT || destination.InitCheck() != B_OK) {
864 		ret = InitPath(path, &destination);
865 		if (ret != B_OK)
866 			return ret;
867 
868 		ret = file.SetTo(destination.Path(),
869 			B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS);
870 		if (ret == B_ENTRY_NOT_FOUND) {
871 			BPath directory;
872 			destination.GetParent(&directory);
873 			if (create_directory(directory.Path(), kDefaultMode) != B_OK)
874 				return B_ERROR;
875 
876 			ret = file.SetTo(destination.Path(), B_WRITE_ONLY | B_CREATE_FILE);
877 		} else if (ret == B_FILE_EXISTS)
878 			state->status = B_FILE_EXISTS;
879 
880 		if (ret != B_OK)
881 			return ret;
882 	}
883 
884 	if (state->status == B_FILE_EXISTS) {
885 		switch (state->policy) {
886 			case P_EXISTS_OVERWRITE:
887 				ret = file.SetTo(destination.Path(),
888 					B_WRITE_ONLY | B_ERASE_FILE);
889 				break;
890 
891 			case P_EXISTS_NONE:
892 			case P_EXISTS_ASK:
893 				ret = B_FILE_EXISTS;
894 				break;
895 
896 			case P_EXISTS_SKIP:
897 				return B_OK;
898 		}
899 	}
900 
901 	if (ret != B_OK)
902 		return ret;
903 
904 	parser_debug(" File created!\n");
905 
906 	// Set the file permissions, creation and modification times
907 	ret = file.SetPermissions(static_cast<mode_t>(fMode));
908 	if (fCreationTime && ret == B_OK)
909 		ret = file.SetCreationTime(static_cast<time_t>(fCreationTime));
910 	if (fModificationTime && ret == B_OK)
911 		ret = file.SetModificationTime(static_cast<time_t>(fModificationTime));
912 
913 	if (ret != B_OK)
914 		return ret;
915 
916 	// Set the mimetype and application signature if present
917 	BNodeInfo info(&file);
918 	if (fMimeType.Length() > 0) {
919 		ret = info.SetType(fMimeType.String());
920 		if (ret != B_OK)
921 			return ret;
922 	}
923 	if (fSignature.Length() > 0) {
924 		ret = info.SetPreferredApp(fSignature.String());
925 		if (ret != B_OK)
926 			return ret;
927 	}
928 
929 	if (fOffset) {
930 		parser_debug("We have an offset\n");
931 		if (!fPackage)
932 			return B_ERROR;
933 
934 		ret = fPackage->InitCheck();
935 		if (ret != B_OK)
936 			return ret;
937 
938 		// We need to parse the data section now
939 		fPackage->Seek(fOffset, SEEK_SET);
940 		uint8 buffer[7];
941 
942 		char *attrName = 0;
943 		uint32 nameSize = 0;
944 		uint8 *attrData = new uint8[P_CHUNK_SIZE];
945 		uint64 dataSize = P_CHUNK_SIZE;
946 		uint8 *temp = new uint8[P_CHUNK_SIZE];
947 		uint64 tempSize = P_CHUNK_SIZE;
948 
949 		uint64 attrCSize = 0, attrOSize = 0;
950 		uint32 attrType = 0; // type_code type
951 		bool attrStarted = false, done = false;
952 
953 		uint8 section = P_ATTRIBUTE;
954 
955 		while (fPackage->Read(buffer, 7) == 7) {
956 			if (!memcmp(buffer, "FBeA", 5)) {
957 				parser_debug("-> Attribute\n");
958 				section = P_ATTRIBUTE;
959 				continue;
960 			} else if (!memcmp(buffer, "FiDa", 5)) {
961 				parser_debug("-> File data\n");
962 				section = P_DATA;
963 				continue;
964 			}
965 
966 			switch (section) {
967 				case P_ATTRIBUTE:
968 					ret = ParseAttribute(buffer, &file, &attrName, &nameSize,
969 						&attrType, &attrData, &dataSize, &temp, &tempSize,
970 						&attrCSize, &attrOSize, &attrStarted, &done);
971 					break;
972 
973 				case P_DATA:
974 					ret = ParseData(buffer, &file, fOriginalSize, &done);
975 					break;
976 
977 				default:
978 					return B_ERROR;
979 			}
980 
981 			if (ret != B_OK || done)
982 				break;
983 		}
984 
985 		delete[] attrData;
986 		delete[] temp;
987 	}
988 
989 	return ret;
990 }
991 
992 
993 const uint32
994 PackageFile::ItemKind()
995 {
996 	return P_KIND_FILE;
997 }
998 
999 
1000 //	#pragma mark -
1001 
1002 
1003 PackageLink::PackageLink(BFile *parent, const BString &path,
1004 		const BString &link, uint8 type, uint32 ctime, uint32 mtime,
1005 		uint32 mode, uint64 offset, uint64 size)
1006 	:
1007 	PackageItem(parent, path, type, ctime, mtime, offset, size),
1008 	fMode(mode),
1009 	fLink(link)
1010 {
1011 }
1012 
1013 
1014 status_t
1015 PackageLink::DoInstall(const char *path, ItemState *state)
1016 {
1017 	if (state == NULL)
1018 		return B_ERROR;
1019 
1020 	status_t ret = B_OK;
1021 	BSymLink symlink;
1022 	parser_debug("Symlink: %s DoInstall() called!\n", fPath.String());
1023 
1024 	BPath &destination = state->destination;
1025 	BDirectory *dir = &state->parent;
1026 
1027 	if (state->status == B_NO_INIT || destination.InitCheck() != B_OK
1028 		|| dir->InitCheck() != B_OK) {
1029 		// Not yet initialized
1030 		ret = InitPath(path, &destination);
1031 		if (ret != B_OK)
1032 			return ret;
1033 
1034 		BString linkName(destination.Leaf());
1035 		parser_debug("%s:%s:%s\n", fPath.String(), destination.Path(),
1036 			linkName.String());
1037 
1038 		BPath dirPath;
1039 		ret = destination.GetParent(&dirPath);
1040 		ret = dir->SetTo(dirPath.Path());
1041 
1042 		if (ret == B_ENTRY_NOT_FOUND) {
1043 			ret = create_directory(dirPath.Path(), kDefaultMode);
1044 			if (ret != B_OK) {
1045 				parser_debug("create_directory()) failed\n");
1046 				return B_ERROR;
1047 			}
1048 		}
1049 		if (ret != B_OK) {
1050 			parser_debug("destination InitCheck failed %s for %s\n",
1051 				strerror(ret), dirPath.Path());
1052 			return ret;
1053 		}
1054 
1055 		ret = dir->CreateSymLink(destination.Path(), fLink.String(), &symlink);
1056 		if (ret == B_FILE_EXISTS) {
1057 			// We need to check if the existing symlink is pointing at the same path
1058 			// as our new one - if not, let's prompt the user
1059 			symlink.SetTo(destination.Path());
1060 			BPath oldLink;
1061 
1062 			ret = symlink.MakeLinkedPath(dir, &oldLink);
1063 			chdir(dirPath.Path());
1064 
1065 			if (ret == B_BAD_VALUE || oldLink != fLink.String())
1066 				state->status = ret = B_FILE_EXISTS;
1067 			else
1068 				ret = B_OK;
1069 		}
1070 	}
1071 
1072 	if (state->status == B_FILE_EXISTS) {
1073 		switch (state->policy) {
1074 			case P_EXISTS_OVERWRITE:
1075 			{
1076 				BEntry entry;
1077 				ret = entry.SetTo(destination.Path());
1078 				if (ret != B_OK)
1079 					return ret;
1080 
1081 				entry.Remove();
1082 				ret = dir->CreateSymLink(destination.Path(), fLink.String(),
1083 					&symlink);
1084 				break;
1085 			}
1086 
1087 			case P_EXISTS_NONE:
1088 			case P_EXISTS_ASK:
1089 				ret = B_FILE_EXISTS;
1090 				break;
1091 
1092 			case P_EXISTS_SKIP:
1093 				return B_OK;
1094 		}
1095 	}
1096 
1097 	if (ret != B_OK) {
1098 		parser_debug("CreateSymLink failed\n");
1099 		return ret;
1100 	}
1101 
1102 	parser_debug(" Symlink created!\n");
1103 
1104 	ret = symlink.SetPermissions(static_cast<mode_t>(fMode));
1105 
1106 	if (fCreationTime && ret == B_OK)
1107 		ret = symlink.SetCreationTime(static_cast<time_t>(fCreationTime));
1108 
1109 	if (fModificationTime && ret == B_OK) {
1110 		ret = symlink.SetModificationTime(static_cast<time_t>(
1111 			fModificationTime));
1112 	}
1113 
1114 	if (ret != B_OK) {
1115 		parser_debug("Failed to set symlink attributes\n");
1116 		return ret;
1117 	}
1118 
1119 	if (fOffset) {
1120 		// Symlinks also seem to have attributes - so parse them
1121 		ret = HandleAttributes(&destination, &symlink, "LnDa");
1122 	}
1123 
1124 	return ret;
1125 }
1126 
1127 
1128 const uint32
1129 PackageLink::ItemKind()
1130 {
1131 	return P_KIND_SYM_LINK;
1132 }
1133 
1134