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