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
33enum {
34	P_CHUNK_SIZE = 256
35};
36
37static const uint32 kDefaultMode = 0777;
38static const uint8 padding[7] = { 0, 0, 0, 0, 0, 0, 0 };
39
40extern bool gVerbose;
41
42enum {
43	P_DATA = 0,
44	P_ATTRIBUTE
45};
46
47
48status_t
49inflate_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
82static inline int
83inflate_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
147PackageItem::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
154PackageItem::~PackageItem()
155{
156}
157
158
159void
160PackageItem::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
174status_t
175PackageItem::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
257status_t
258PackageItem::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
315status_t
316PackageItem::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
430status_t
431PackageItem::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
492status_t
493PackageItem::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
544PackageScript::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
554status_t
555PackageScript::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
662const uint32
663PackageScript::ItemKind()
664{
665	return P_KIND_SCRIPT;
666}
667
668
669status_t
670PackageScript::_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
733status_t
734PackageScript::_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
804PackageDirectory::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
812status_t
813PackageDirectory::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
849const uint32
850PackageDirectory::ItemKind()
851{
852	return P_KIND_DIRECTORY;
853}
854
855
856//	#pragma mark - PackageFile
857
858
859PackageFile::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
874status_t
875PackageFile::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
1015const uint32
1016PackageFile::ItemKind()
1017{
1018	return P_KIND_FILE;
1019}
1020
1021
1022//	#pragma mark -
1023
1024
1025PackageLink::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
1036status_t
1037PackageLink::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
1150const uint32
1151PackageLink::ItemKind()
1152{
1153	return P_KIND_SYM_LINK;
1154}
1155
1156