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