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