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