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