BTRFS: Implement BTree::Path and change _Find.
[haiku.git] / src / apps / packageinstaller / PackageItem.cpp
blob43fa932ba550934b860cd741d2a40b6e28dec44c
1 /*
2 * Copyright 2007-2009, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
5 * Author:
6 * Ɓukasz 'Sil2100' Zemczak <sil2100@vexillium.org>
7 */
10 #include "PackageItem.h"
12 #include <stdlib.h>
13 #include <string.h>
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>
27 #include "zlib.h"
30 #undef B_TRANSLATION_CONTEXT
31 #define B_TRANSLATION_CONTEXT "PackageItem"
33 enum {
34 P_CHUNK_SIZE = 256
37 static const uint32 kDefaultMode = 0777;
38 static const uint8 padding[7] = { 0, 0, 0, 0, 0, 0, 0 };
40 extern bool gVerbose;
42 enum {
43 P_DATA = 0,
44 P_ATTRIBUTE
48 status_t
49 inflate_data(uint8 *in, uint32 inSize, uint8 *out, uint32 outSize)
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;
61 ret = inflateInit(&stream);
62 if (ret != Z_OK) {
63 parser_debug("inflatInit failed\n");
64 return B_ERROR;
67 stream.avail_out = outSize;
68 stream.next_out = out;
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;
77 inflateEnd(&stream);
78 return B_OK;
82 static inline int
83 inflate_file_to_file(BFile *in, uint64 in_size, BFile *out, uint64 out_size)
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;
93 uint8 buffer_out[P_CHUNK_SIZE], buffer_in[P_CHUNK_SIZE];
94 uint64 bytes_read = 0, read = P_CHUNK_SIZE, write = 0;
96 ret = inflateInit(&stream);
97 if (ret != Z_OK) {
98 parser_debug("inflate_file_to_file: inflateInit failed\n");
99 return B_ERROR;
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;
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;
115 stream.next_in = buffer_in;
117 do {
118 stream.avail_out = P_CHUNK_SIZE;
119 stream.next_out = buffer_out;
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;
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;
135 } while (stream.avail_out == 0);
136 } while (bytes_read != in_size);
138 (void)inflateEnd(&stream);
140 return B_OK;
144 // #pragma mark - PackageItem
147 PackageItem::PackageItem(BFile* parent, const BString& path, uint8 type,
148 uint32 ctime, uint32 mtime, uint64 offset, uint64 size)
150 SetTo(parent, path, type, ctime, mtime, offset, size);
154 PackageItem::~PackageItem()
159 void
160 PackageItem::SetTo(BFile* parent, const BString& path, uint8 type, uint32 ctime,
161 uint32 mtime, uint64 offset, uint64 size)
163 fPackage = parent;
164 fPath = path;
166 fOffset = offset;
167 fSize = size;
168 fPathType = type;
169 fCreationTime = ctime;
170 fModificationTime = mtime;
174 status_t
175 PackageItem::InitPath(const char* path, BPath* destination)
177 status_t ret = B_OK;
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;
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;
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());
213 if (ret != B_OK) {
214 fprintf(stderr, "InitPath(%s): %s\n", path, strerror(ret));
215 return ret;
218 BString pathString(destination->Path());
220 // Hardcoded paths, the .pkg files hardcode this to the same
221 if (pathString.FindFirst("non-packaged") < 0) {
222 bool wasRewritten = false;
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;
246 if (wasRewritten) {
247 if (gVerbose)
248 printf("rewritten: %s\n", pathString.String());
249 destination->SetTo(pathString.String());
253 return ret;
257 status_t
258 PackageItem::HandleAttributes(BPath *destination, BNode *node,
259 const char *header)
261 status_t ret = B_OK;
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;
269 ret = fPackage->InitCheck();
270 if (ret != B_OK)
271 return ret;
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");
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;
287 uint64 attrCSize = 0, attrOSize = 0;
288 uint32 attrType = 0; // type_code type
289 bool attrStarted = false, done = false;
291 while (fPackage->Read(buffer, 7) == 7) {
292 if (!memcmp(buffer, "FBeA", 5))
293 continue;
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());
303 break;
307 delete[] attrData;
308 delete[] temp;
311 return ret;
315 status_t
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)
321 status_t ret = B_OK;
322 uint32 length;
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;
331 *attrStarted = true;
332 } else if (!memcmp(buffer, "BeAN", 5)) {
333 if (!*attrStarted) {
334 ret = B_ERROR;
335 return ret;
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);
343 if (*nameSize < (length + 1)) {
344 delete[] *attrName;
345 *nameSize = length + 1;
346 *attrName = new char[*nameSize];
348 fPackage->Read(*attrName, length);
349 (*attrName)[length] = 0;
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;
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;
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);
373 fPackage->Read(attrOSize, 8);
374 swap_data(B_UINT64_TYPE, attrOSize, sizeof(*attrOSize),
375 B_SWAP_BENDIAN_TO_HOST);
377 fPackage->Seek(4, SEEK_CUR); // TODO: Check what this means
379 if (*tempSize < *attrCSize) {
380 delete[] *temp;
381 *tempSize = *attrCSize;
382 *temp = new uint8[*tempSize];
384 if (*dataSize < *attrOSize) {
385 delete[] *attrData;
386 *dataSize = *attrOSize;
387 *attrData = new uint8[*dataSize];
390 if (fPackage->Read(*temp, *attrCSize)
391 != static_cast<ssize_t>(*attrCSize)) {
392 ret = B_ERROR;
393 return ret;
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;
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;
414 *attrStarted = false;
415 if (*attrName)
416 *attrName[0] = 0;
417 *attrCSize = 0;
418 *attrOSize = 0;
420 parser_debug(" > Attribute added.\n");
421 } else {
422 parser_debug(" Unknown attribute\n");
423 ret = B_ERROR;
426 return ret;
430 status_t
431 PackageItem::SkipAttribute(uint8* buffer, bool* attrStarted, bool* done)
433 status_t ret = B_OK;
434 uint32 length;
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;
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);
450 fPackage->Seek(length, SEEK_CUR);
451 } else if (!memcmp(buffer, "BeAT", 5)) {
452 if (!*attrStarted) {
453 ret = B_ERROR;
454 return ret;
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;
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);
471 fPackage->Seek(12 + length64, SEEK_CUR);
473 parser_debug(" Data skipped successfuly.\n");
474 } else if (!memcmp(buffer, padding, 7)) {
475 if (!*attrStarted) {
476 *done = true;
477 return ret;
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;
488 return ret;
492 status_t
493 PackageItem::ParseData(uint8* buffer, BFile* file, uint64 originalSize,
494 bool *done)
496 status_t ret = B_OK;
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);
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);
511 if (original != originalSize) {
512 parser_debug(" File size mismatch\n");
513 return B_ERROR; // File size mismatch
515 parser_debug(" Still good...\n");
517 if (fPackage->Read(buffer, 4) != 4) {
518 parser_debug(" Read(buffer, 4) failed\n");
519 return B_ERROR;
521 parser_debug(" Still good...\n");
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;
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;
537 return ret;
541 // #pragma mark - PackageScript
544 PackageScript::PackageScript(BFile* parent, const BString& path, uint8 type,
545 uint64 offset, uint64 size, uint64 originalSize)
547 PackageItem(parent, path, type, 0, 0, offset, size),
548 fOriginalSize(originalSize),
549 fThreadId(-1)
554 status_t
555 PackageScript::DoInstall(const char* path, ItemState* state)
557 status_t ret = B_OK;
558 parser_debug("Script: DoInstall() called!\n");
560 if (fOffset) {
561 parser_debug("We have an offset\n");
562 if (!fPackage)
563 return B_ERROR;
565 ret = fPackage->InitCheck();
566 if (ret != B_OK)
567 return ret;
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;
574 uint8 section = P_ATTRIBUTE;
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;
587 switch (section) {
588 case P_ATTRIBUTE:
589 ret = SkipAttribute(buffer, &attrStarted, &done);
590 break;
592 case P_DATA:
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.
634 if (gVerbose)
635 printf("%s\n", script.String());
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);
645 break;
648 default:
649 return B_ERROR;
652 if (ret != B_OK || done)
653 break;
657 parser_debug("Ret: %ld %s\n", ret, strerror(ret));
658 return ret;
662 const uint32
663 PackageScript::ItemKind()
665 return P_KIND_SCRIPT;
669 status_t
670 PackageScript::_ParseScript(uint8 *buffer, uint64 originalSize,
671 BString& _script, bool *done)
673 status_t ret = B_OK;
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);
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);
688 if (original != originalSize) {
689 parser_debug(" File size mismatch\n");
690 return B_ERROR; // File size mismatch
692 parser_debug(" Still good...\n");
694 if (fPackage->Read(buffer, 4) != 4) {
695 parser_debug(" Read(buffer, 4) failed\n");
696 return B_ERROR;
698 parser_debug(" Still good...\n");
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;
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;
716 _script.SetTo((char*)script, originalSize);
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;
729 return ret;
733 status_t
734 PackageScript::_RunScript(const char* workingDirectory, const BString& script)
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
741 // Change current working directory to install path
742 char oldWorkingDirectory[B_PATH_NAME_LENGTH];
743 getcwd(oldWorkingDirectory, sizeof(oldWorkingDirectory));
744 chdir(workingDirectory);
746 // Save current FDs
747 int old_in = dup(0);
748 int old_out = dup(1);
749 int old_err = dup(2);
751 int filedes[2];
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]);
759 const char **argv = new const char * [3];
760 argv[0] = strdup("/bin/sh");
761 argv[1] = strdup("-s");
762 argv[2] = NULL;
764 // "load" command.
765 fThreadId = load_image(2, argv, (const char**)environ);
767 int i;
768 for (i = 0; i < 2; i++)
769 delete argv[i];
770 delete [] argv;
772 if (fThreadId < B_OK)
773 return fThreadId;
775 // thread id is now suspended.
776 setpgid(fThreadId, fThreadId);
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);
783 set_thread_priority(fThreadId, B_LOW_PRIORITY);
784 resume_thread(fThreadId);
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;
794 // Restore current working directory
795 chdir(oldWorkingDirectory);
797 return B_OK;
801 // #pragma mark - PackageDirectory
804 PackageDirectory::PackageDirectory(BFile* parent, const BString& path,
805 uint8 type, uint32 ctime, uint32 mtime, uint64 offset, uint64 size)
807 PackageItem(parent, path, type, ctime, mtime, offset, size)
812 status_t
813 PackageDirectory::DoInstall(const char* path, ItemState* state)
815 BPath &destination = state->destination;
816 status_t ret;
817 parser_debug("Directory: %s DoInstall() called!\n", fPath.String());
819 ret = InitPath(path, &destination);
820 parser_debug("Ret: %ld %s\n", ret, strerror(ret));
821 if (ret != B_OK)
822 return ret;
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");
833 if (fCreationTime)
834 dir.SetCreationTime(static_cast<time_t>(fCreationTime));
836 if (fModificationTime)
837 dir.SetModificationTime(static_cast<time_t>(fModificationTime));
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");
844 parser_debug("Ret: %ld %s\n", ret, strerror(ret));
845 return ret;
849 const uint32
850 PackageDirectory::ItemKind()
852 return P_KIND_DIRECTORY;
856 // #pragma mark - PackageFile
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)
864 PackageItem(parent, path, type, ctime, mtime, offset, size),
865 fOriginalSize(originalSize),
866 fPlatform(platform),
867 fMode(mode),
868 fMimeType(mime),
869 fSignature(signature)
874 status_t
875 PackageFile::DoInstall(const char* path, ItemState* state)
877 if (state == NULL)
878 return B_ERROR;
880 BPath& destination = state->destination;
881 status_t ret = B_OK;
882 parser_debug("File: %s DoInstall() called!\n", fPath.String());
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;
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;
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;
902 if (ret != B_OK)
903 return ret;
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;
913 case P_EXISTS_NONE:
914 case P_EXISTS_ASK:
915 ret = B_FILE_EXISTS;
916 break;
918 case P_EXISTS_SKIP:
919 return B_OK;
923 if (ret != B_OK)
924 return ret;
926 parser_debug(" File created!\n");
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));
935 if (ret != B_OK)
936 return ret;
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;
945 if (fSignature.Length() > 0) {
946 ret = info.SetPreferredApp(fSignature.String());
947 if (ret != B_OK)
948 return ret;
951 if (fOffset) {
952 parser_debug("We have an offset\n");
953 if (!fPackage)
954 return B_ERROR;
956 ret = fPackage->InitCheck();
957 if (ret != B_OK)
958 return ret;
960 // We need to parse the data section now
961 fPackage->Seek(fOffset, SEEK_SET);
962 uint8 buffer[7];
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;
971 uint64 attrCSize = 0, attrOSize = 0;
972 uint32 attrType = 0; // type_code type
973 bool attrStarted = false, done = false;
975 uint8 section = P_ATTRIBUTE;
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;
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;
995 case P_DATA:
996 ret = ParseData(buffer, &file, fOriginalSize, &done);
997 break;
999 default:
1000 return B_ERROR;
1003 if (ret != B_OK || done)
1004 break;
1007 delete[] attrData;
1008 delete[] temp;
1011 return ret;
1015 const uint32
1016 PackageFile::ItemKind()
1018 return P_KIND_FILE;
1022 // #pragma mark -
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)
1029 PackageItem(parent, path, type, ctime, mtime, offset, size),
1030 fMode(mode),
1031 fLink(link)
1036 status_t
1037 PackageLink::DoInstall(const char *path, ItemState *state)
1039 if (state == NULL)
1040 return B_ERROR;
1042 status_t ret = B_OK;
1043 BSymLink symlink;
1044 parser_debug("Symlink: %s DoInstall() called!\n", fPath.String());
1046 BPath &destination = state->destination;
1047 BDirectory *dir = &state->parent;
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;
1056 BString linkName(destination.Leaf());
1057 parser_debug("%s:%s:%s\n", fPath.String(), destination.Path(),
1058 linkName.String());
1060 BPath dirPath;
1061 ret = destination.GetParent(&dirPath);
1062 ret = dir->SetTo(dirPath.Path());
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;
1071 if (ret != B_OK) {
1072 parser_debug("destination InitCheck failed %s for %s\n",
1073 strerror(ret), dirPath.Path());
1074 return ret;
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;
1084 ret = symlink.MakeLinkedPath(dir, &oldLink);
1085 chdir(dirPath.Path());
1087 if (ret == B_BAD_VALUE || oldLink != fLink.String())
1088 state->status = ret = B_FILE_EXISTS;
1089 else
1090 ret = B_OK;
1094 if (state->status == B_FILE_EXISTS) {
1095 switch (state->policy) {
1096 case P_EXISTS_OVERWRITE:
1098 BEntry entry;
1099 ret = entry.SetTo(destination.Path());
1100 if (ret != B_OK)
1101 return ret;
1103 entry.Remove();
1104 ret = dir->CreateSymLink(destination.Path(), fLink.String(),
1105 &symlink);
1106 break;
1109 case P_EXISTS_NONE:
1110 case P_EXISTS_ASK:
1111 ret = B_FILE_EXISTS;
1112 break;
1114 case P_EXISTS_SKIP:
1115 return B_OK;
1119 if (ret != B_OK) {
1120 parser_debug("CreateSymLink failed\n");
1121 return ret;
1124 parser_debug(" Symlink created!\n");
1126 ret = symlink.SetPermissions(static_cast<mode_t>(fMode));
1128 if (fCreationTime && ret == B_OK)
1129 ret = symlink.SetCreationTime(static_cast<time_t>(fCreationTime));
1131 if (fModificationTime && ret == B_OK) {
1132 ret = symlink.SetModificationTime(static_cast<time_t>(
1133 fModificationTime));
1136 if (ret != B_OK) {
1137 parser_debug("Failed to set symlink attributes\n");
1138 return ret;
1141 if (fOffset) {
1142 // Symlinks also seem to have attributes - so parse them
1143 ret = HandleAttributes(&destination, &symlink, "LnDa");
1146 return ret;
1150 const uint32
1151 PackageLink::ItemKind()
1153 return P_KIND_SYM_LINK;