xref: /haiku/src/apps/packageinstaller/PackageItem.cpp (revision 23d878482ed22e55dad6d1fca1df7bea42eb157c)
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 
31 enum {
32 	P_CHUNK_SIZE = 256
33 };
34 
35 static const uint32 kDefaultMode = 0777;
36 static const uint8 padding[7] = { 0, 0, 0, 0, 0, 0, 0 };
37 
38 enum {
39 	P_DATA = 0,
40 	P_ATTRIBUTE
41 };
42 
43 extern const char **environ;
44 
45 
46 status_t
47 inflate_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 
80 static inline int
81 inflate_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 
146 PackageItem::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 
153 PackageItem::~PackageItem()
154 {
155 }
156 
157 
158 void
159 PackageItem::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 
173 status_t
174 PackageItem::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 
211 status_t
212 PackageItem::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 
269 status_t
270 PackageItem::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 
384 status_t
385 PackageItem::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 
446 status_t
447 PackageItem::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 
500 PackageScript::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 
510 status_t
511 PackageScript::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 
566 const uint32
567 PackageScript::ItemKind()
568 {
569 	return P_KIND_SCRIPT;
570 }
571 
572 
573 status_t
574 PackageScript::_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 
635 status_t
636 PackageScript::_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 
697 PackageDirectory::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 
705 status_t
706 PackageDirectory::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 
742 const uint32
743 PackageDirectory::ItemKind()
744 {
745 	return P_KIND_DIRECTORY;
746 }
747 
748 
749 //	#pragma mark - PackageFile
750 
751 
752 PackageFile::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 
767 status_t
768 PackageFile::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 
908 const uint32
909 PackageFile::ItemKind()
910 {
911 	return P_KIND_FILE;
912 }
913 
914 
915 //	#pragma mark -
916 
917 
918 PackageLink::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 
929 status_t
930 PackageLink::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 
1043 const uint32
1044 PackageLink::ItemKind()
1045 {
1046 	return P_KIND_SYM_LINK;
1047 }
1048 
1049