1 /*
2 * Copyright 2009-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include <ctype.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <getopt.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <unistd.h>
16
17 #include <algorithm>
18 #include <new>
19
20 #include <fs_attr.h>
21 #include <String.h>
22
23 #include <AutoDeleter.h>
24 #include <HashString.h>
25
26 #include <util/OpenHashTable.h>
27
28 #include <package/hpkg/BlockBufferPoolNoLock.h>
29 #include <package/hpkg/PackageContentHandler.h>
30 #include <package/hpkg/PackageDataReader.h>
31 #include <package/hpkg/PackageEntry.h>
32 #include <package/hpkg/PackageEntryAttribute.h>
33 #include <package/hpkg/PackageReader.h>
34 #include <package/hpkg/StandardErrorOutput.h>
35 #include <package/hpkg/v1/PackageContentHandler.h>
36 #include <package/hpkg/v1/PackageDataReader.h>
37 #include <package/hpkg/v1/PackageEntry.h>
38 #include <package/hpkg/v1/PackageEntryAttribute.h>
39 #include <package/hpkg/v1/PackageReader.h>
40
41 #include "package.h"
42
43
44 using BPackageKit::BHPKG::BAbstractBufferedDataReader;
45 using BPackageKit::BHPKG::BBlockBufferPoolNoLock;
46 using BPackageKit::BHPKG::BBufferDataReader;
47 using BPackageKit::BHPKG::BBufferPool;
48 using BPackageKit::BHPKG::BDataReader;
49 using BPackageKit::BHPKG::BErrorOutput;
50 using BPackageKit::BHPKG::BFDDataReader;
51 using BPackageKit::BHPKG::BPackageInfoAttributeValue;
52 using BPackageKit::BHPKG::BStandardErrorOutput;
53
54
55 struct VersionPolicyV1 {
56 typedef BPackageKit::BHPKG::V1::BPackageContentHandler
57 PackageContentHandler;
58 typedef BPackageKit::BHPKG::V1::BPackageData PackageData;
59 typedef BPackageKit::BHPKG::V1::BPackageEntry PackageEntry;
60 typedef BPackageKit::BHPKG::V1::BPackageEntryAttribute
61 PackageEntryAttribute;
62 typedef BPackageKit::BHPKG::V1::BPackageReader PackageReader;
63 typedef BDataReader HeapReaderBase;
64
BufferSizeVersionPolicyV165 static inline size_t BufferSize()
66 {
67 return BPackageKit::BHPKG::V1::B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB;
68 }
69
PackageInfoFileNameVersionPolicyV170 static inline const char* PackageInfoFileName()
71 {
72 return BPackageKit::BHPKG::V1::B_HPKG_PACKAGE_INFO_FILE_NAME;
73 }
74
PackageDataCompressedSizeVersionPolicyV175 static inline uint64 PackageDataCompressedSize(const PackageData& data)
76 {
77 return data.CompressedSize();
78 }
79
PackageDataUncompressedSizeVersionPolicyV180 static inline uint64 PackageDataUncompressedSize(const PackageData& data)
81 {
82 return data.UncompressedSize();
83 }
84
InitReaderVersionPolicyV185 static inline status_t InitReader(PackageReader& packageReader,
86 const char* fileName)
87 {
88 return packageReader.Init(fileName);
89 }
90
GetHeapReaderVersionPolicyV191 static status_t GetHeapReader(PackageReader& packageReader,
92 HeapReaderBase*& _heapReader, bool& _mustDelete)
93 {
94 _heapReader = new(std::nothrow) BFDDataReader(
95 packageReader.PackageFileFD());
96 _mustDelete = false;
97 return _heapReader != NULL ? B_OK : B_NO_MEMORY;
98 }
99
CreatePackageDataReaderVersionPolicyV1100 static status_t CreatePackageDataReader(BBufferPool* bufferPool,
101 HeapReaderBase* heapReader, const PackageData& data,
102 BAbstractBufferedDataReader*& _reader)
103 {
104 return BPackageKit::BHPKG::V1::BPackageDataReaderFactory(bufferPool)
105 .CreatePackageDataReader(heapReader, data, _reader);
106 }
107 };
108
109 struct VersionPolicyV2 {
110 typedef BPackageKit::BHPKG::BPackageContentHandler PackageContentHandler;
111 typedef BPackageKit::BHPKG::BPackageData PackageData;
112 typedef BPackageKit::BHPKG::BPackageEntry PackageEntry;
113 typedef BPackageKit::BHPKG::BPackageEntryAttribute PackageEntryAttribute;
114 typedef BPackageKit::BHPKG::BPackageReader PackageReader;
115 typedef BAbstractBufferedDataReader HeapReaderBase;
116
BufferSizeVersionPolicyV2117 static inline size_t BufferSize()
118 {
119 return 64 * 1024;
120 }
121
PackageInfoFileNameVersionPolicyV2122 static inline const char* PackageInfoFileName()
123 {
124 return BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME;
125 }
126
PackageDataCompressedSizeVersionPolicyV2127 static inline uint64 PackageDataCompressedSize(const PackageData& data)
128 {
129 return data.Size();
130 }
131
PackageDataUncompressedSizeVersionPolicyV2132 static inline uint64 PackageDataUncompressedSize(const PackageData& data)
133 {
134 return data.Size();
135 }
136
InitReaderVersionPolicyV2137 static inline status_t InitReader(PackageReader& packageReader,
138 const char* fileName)
139 {
140 return packageReader.Init(fileName,
141 BPackageKit::BHPKG
142 ::B_HPKG_READER_DONT_PRINT_VERSION_MISMATCH_MESSAGE);
143 }
144
GetHeapReaderVersionPolicyV2145 static status_t GetHeapReader(PackageReader& packageReader,
146 HeapReaderBase*& _heapReader, bool& _mustDelete)
147 {
148 _heapReader = packageReader.HeapReader();
149 _mustDelete = false;
150 return B_OK;
151 }
152
CreatePackageDataReaderVersionPolicyV2153 static status_t CreatePackageDataReader(BBufferPool* bufferPool,
154 HeapReaderBase* heapReader, const PackageData& data,
155 BAbstractBufferedDataReader*& _reader)
156 {
157 return BPackageKit::BHPKG::BPackageDataReaderFactory()
158 .CreatePackageDataReader(heapReader, data, _reader);
159 }
160 };
161
162
163 struct Entry {
EntryEntry164 Entry(Entry* parent, char* name, bool implicit)
165 :
166 fParent(parent),
167 fName(name),
168 fImplicit(implicit),
169 fSeen(false)
170 {
171 }
172
~EntryEntry173 ~Entry()
174 {
175 _DeleteChildren();
176
177 free(fName);
178 }
179
InitEntry180 status_t Init()
181 {
182 return fChildren.Init();
183 }
184
CreateEntry185 static status_t Create(Entry* parent, const char* name, bool implicit,
186 Entry*& _entry)
187 {
188 if (parent != NULL) {
189 Entry* entryInParent = parent->FindChild(name);
190 if (entryInParent != NULL) {
191 _entry = entryInParent;
192 return B_OK;
193 }
194 }
195
196 char* clonedName = strdup(name);
197 if (clonedName == NULL)
198 return B_NO_MEMORY;
199
200 Entry* entry = new(std::nothrow) Entry(parent, clonedName, implicit);
201 if (entry == NULL) {
202 free(clonedName);
203 return B_NO_MEMORY;
204 }
205
206 status_t error = entry->Init();
207 if (error != B_OK) {
208 delete entry;
209 return error;
210 }
211
212 if (parent != NULL)
213 parent->fChildren.Insert(entry);
214
215 _entry = entry;
216 return B_OK;
217 }
218
ParentEntry219 Entry* Parent() const
220 {
221 return fParent;
222 }
223
NameEntry224 const char* Name() const
225 {
226 return fName;
227 }
228
IsImplicitEntry229 bool IsImplicit() const
230 {
231 return fImplicit;
232 }
233
SetExplicitEntry234 void SetExplicit()
235 {
236 // remove all children and set this entry non-implicit
237 _DeleteChildren();
238 fImplicit = false;
239 }
240
SetSeenEntry241 void SetSeen()
242 {
243 fSeen = true;
244 }
245
SeenEntry246 bool Seen() const
247 {
248 return fSeen;
249 }
250
FindChildEntry251 Entry* FindChild(const char* name) const
252 {
253 return fChildren.Lookup(name);
254 }
255
256 private:
257 struct ChildHashDefinition {
258 typedef const char* KeyType;
259 typedef Entry ValueType;
260
HashKeyEntry::ChildHashDefinition261 size_t HashKey(const char* key) const
262 {
263 return BString::HashValue(key);
264 }
265
HashEntry::ChildHashDefinition266 size_t Hash(const Entry* value) const
267 {
268 return HashKey(value->Name());
269 }
270
CompareEntry::ChildHashDefinition271 bool Compare(const char* key, const Entry* value) const
272 {
273 return strcmp(value->Name(), key) == 0;
274 }
275
GetLinkEntry::ChildHashDefinition276 Entry*& GetLink(Entry* value) const
277 {
278 return value->fHashTableNext;
279 }
280 };
281
282 typedef BOpenHashTable<ChildHashDefinition> ChildTable;
283
284 private:
_DeleteChildrenEntry285 void _DeleteChildren()
286 {
287 Entry* child = fChildren.Clear(true);
288 while (child != NULL) {
289 Entry* next = child->fHashTableNext;
290 delete child;
291 child = next;
292 }
293 }
294
295 public:
296 Entry* fHashTableNext;
297
298 private:
299 Entry* fParent;
300 char* fName;
301 bool fImplicit;
302 bool fSeen;
303 ChildTable fChildren;
304 };
305
306
307 template<typename VersionPolicy>
308 struct PackageContentExtractHandler : VersionPolicy::PackageContentHandler {
PackageContentExtractHandlerPackageContentExtractHandler309 PackageContentExtractHandler(BBufferPool* bufferPool,
310 typename VersionPolicy::HeapReaderBase* heapReader)
311 :
312 fBufferPool(bufferPool),
313 fPackageFileReader(heapReader),
314 fDataBuffer(NULL),
315 fDataBufferSize(0),
316 fRootFilterEntry(NULL, NULL, true),
317 fBaseDirectory(AT_FDCWD),
318 fInfoFileName(NULL),
319 fErrorOccurred(false)
320 {
321 }
322
~PackageContentExtractHandlerPackageContentExtractHandler323 ~PackageContentExtractHandler()
324 {
325 free(fDataBuffer);
326 }
327
InitPackageContentExtractHandler328 status_t Init()
329 {
330 status_t error = fRootFilterEntry.Init();
331 if (error != B_OK)
332 return error;
333
334 fDataBufferSize = 64 * 1024;
335 fDataBuffer = malloc(fDataBufferSize);
336 if (fDataBuffer == NULL)
337 return B_NO_MEMORY;
338
339 return B_OK;
340 }
341
SetBaseDirectoryPackageContentExtractHandler342 void SetBaseDirectory(int fd)
343 {
344 fBaseDirectory = fd;
345 }
346
SetPackageInfoFilePackageContentExtractHandler347 void SetPackageInfoFile(const char* infoFileName)
348 {
349 fInfoFileName = infoFileName;
350 }
351
SetExtractAllPackageContentExtractHandler352 void SetExtractAll()
353 {
354 fRootFilterEntry.SetExplicit();
355 }
356
AddFilterEntryPackageContentExtractHandler357 status_t AddFilterEntry(const char* fileName)
358 {
359 // add all components of the path
360 Entry* entry = &fRootFilterEntry;
361 while (*fileName != 0) {
362 const char* nextSlash = strchr(fileName, '/');
363 // no slash, just add the file name
364 if (nextSlash == NULL) {
365 return _AddFilterEntry(entry, fileName, strlen(fileName),
366 false, entry);
367 }
368
369 // find the start of the next component, skipping slashes
370 const char* nextComponent = nextSlash + 1;
371 while (*nextComponent == '/')
372 nextComponent++;
373
374 status_t error = _AddFilterEntry(entry, fileName,
375 nextSlash - fileName, *nextComponent != '\0', entry);
376 if (error != B_OK)
377 return error;
378
379 fileName = nextComponent;
380 }
381
382 return B_OK;
383 }
384
FindFilterEntryPackageContentExtractHandler385 Entry* FindFilterEntry(const char* fileName)
386 {
387 // add all components of the path
388 Entry* entry = &fRootFilterEntry;
389 while (entry != NULL && *fileName != 0) {
390 const char* nextSlash = strchr(fileName, '/');
391 // no slash, just add the file name
392 if (nextSlash == NULL)
393 return entry->FindChild(fileName);
394
395 // find the start of the next component, skipping slashes
396 const char* nextComponent = nextSlash + 1;
397 while (*nextComponent == '/')
398 nextComponent++;
399
400 BString componentName(fileName, nextSlash - fileName);
401 entry = entry->FindChild(componentName);
402
403 fileName = nextComponent;
404 }
405
406 return entry;
407 }
408
HandleEntryPackageContentExtractHandler409 virtual status_t HandleEntry(typename VersionPolicy::PackageEntry* entry)
410 {
411 // create a token
412 Token* token = new(std::nothrow) Token;
413 if (token == NULL)
414 return B_NO_MEMORY;
415 ObjectDeleter<Token> tokenDeleter(token);
416
417 // check whether this entry shall be ignored or is implicit
418 Entry* parentFilterEntry;
419 bool implicit;
420 if (entry->Parent() != NULL) {
421 Token* parentToken = (Token*)entry->Parent()->UserToken();
422 if (parentToken == NULL) {
423 // parent is ignored, so ignore this entry, too
424 return B_OK;
425 }
426
427 parentFilterEntry = parentToken->filterEntry;
428 implicit = parentToken->implicit;
429 } else {
430 parentFilterEntry = &fRootFilterEntry;
431 implicit = fRootFilterEntry.IsImplicit();
432 }
433
434 Entry* filterEntry = parentFilterEntry != NULL
435 ? parentFilterEntry->FindChild(entry->Name()) : NULL;
436
437 if (implicit && filterEntry == NULL) {
438 // parent is implicit and the filter doesn't include this entry
439 // -- ignore it
440 return B_OK;
441 }
442
443 // If the entry is in the filter, get its implicit flag.
444 if (filterEntry != NULL) {
445 implicit = filterEntry->IsImplicit();
446 filterEntry->SetSeen();
447 }
448
449 token->filterEntry = filterEntry;
450 token->implicit = implicit;
451
452 // get parent FD and the entry name
453 int parentFD;
454 const char* entryName;
455 _GetParentFDAndEntryName(entry, parentFD, entryName);
456
457 // check whether something is in the way
458 struct stat st;
459 bool entryExists = fstatat(parentFD, entryName, &st,
460 AT_SYMLINK_NOFOLLOW) == 0;
461 if (entryExists) {
462 if (S_ISREG(entry->Mode()) || S_ISLNK(entry->Mode())) {
463 // If the entry in the way is a regular file or a symlink,
464 // remove it, otherwise fail.
465 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
466 fprintf(stderr, "Error: Can't create entry \"%s\", since "
467 "something is in the way\n",
468 _EntryPath(entry).String());
469 return B_FILE_EXISTS;
470 }
471
472 if (unlinkat(parentFD, entryName, 0) != 0) {
473 fprintf(stderr, "Error: Failed to unlink entry \"%s\": %s\n",
474 _EntryPath(entry).String(), strerror(errno));
475 return errno;
476 }
477
478 entryExists = false;
479 } else if (S_ISDIR(entry->Mode())) {
480 // If the entry in the way is a directory, merge, otherwise
481 // fail.
482 if (!S_ISDIR(st.st_mode)) {
483 fprintf(stderr, "Error: Can't create directory \"%s\", "
484 "since something is in the way\n",
485 _EntryPath(entry).String());
486 return B_FILE_EXISTS;
487 }
488 }
489 }
490
491 // create the entry
492 int fd = -1;
493 if (S_ISREG(entry->Mode())) {
494 if (implicit) {
495 fprintf(stderr, "Error: File \"%s\" was specified as a "
496 "path directory component.\n", _EntryPath(entry).String());
497 return B_BAD_VALUE;
498 }
499
500 // create the file
501 fd = openat(parentFD, entryName, O_RDWR | O_CREAT | O_EXCL,
502 S_IRUSR | S_IWUSR);
503 // Note: We use read+write user permissions now -- so write
504 // operations (e.g. attributes) won't fail, but set them to the
505 // desired ones in HandleEntryDone().
506 if (fd < 0) {
507 fprintf(stderr, "Error: Failed to create file \"%s\": %s\n",
508 _EntryPath(entry).String(), strerror(errno));
509 return errno;
510 }
511
512 // write data
513 status_t error = _ExtractFileData(fPackageFileReader, entry->Data(),
514 fd);
515 if (error != B_OK)
516 return error;
517 } else if (S_ISLNK(entry->Mode())) {
518 if (implicit) {
519 fprintf(stderr, "Error: Symlink \"%s\" was specified as a "
520 "path directory component.\n", _EntryPath(entry).String());
521 return B_BAD_VALUE;
522 }
523
524 // create the symlink
525 const char* symlinkPath = entry->SymlinkPath();
526 if (symlinkat(symlinkPath != NULL ? symlinkPath : "", parentFD,
527 entryName) != 0) {
528 fprintf(stderr, "Error: Failed to create symlink \"%s\": %s\n",
529 _EntryPath(entry).String(), strerror(errno));
530 return errno;
531 }
532 // TODO: Set symlink permissions?
533 } else if (S_ISDIR(entry->Mode())) {
534 // create the directory, if necessary
535 if (!entryExists
536 && mkdirat(parentFD, entryName, S_IRWXU) != 0) {
537 // Note: We use read+write+exec user permissions now -- so write
538 // operations (e.g. attributes) won't fail, but set them to the
539 // desired ones in HandleEntryDone().
540 fprintf(stderr, "Error: Failed to create directory \"%s\": "
541 "%s\n", _EntryPath(entry).String(), strerror(errno));
542 return errno;
543 }
544 } else {
545 fprintf(stderr, "Error: Invalid file type for entry \"%s\"\n",
546 _EntryPath(entry).String());
547 return B_BAD_DATA;
548 }
549
550 // If not done yet (symlink, dir), open the node -- we need the FD.
551 if (fd < 0 && (!implicit || S_ISDIR(entry->Mode()))) {
552 fd = openat(parentFD, entryName, O_RDONLY | O_NOTRAVERSE);
553 if (fd < 0) {
554 fprintf(stderr, "Error: Failed to open entry \"%s\": %s\n",
555 _EntryPath(entry).String(), strerror(errno));
556 return errno;
557 }
558 }
559 token->fd = fd;
560
561 // set the file times
562 if (!entryExists && !implicit) {
563 timespec times[2] = {entry->AccessTime(), entry->ModifiedTime()};
564 futimens(fd, times);
565
566 // set user/group
567 // TODO:...
568 }
569
570 entry->SetUserToken(tokenDeleter.Detach());
571 return B_OK;
572 }
573
HandleEntryAttributePackageContentExtractHandler574 virtual status_t HandleEntryAttribute(
575 typename VersionPolicy::PackageEntry* entry,
576 typename VersionPolicy::PackageEntryAttribute* attribute)
577 {
578 // don't write attributes of ignored or implicit entries
579 Token* token = (Token*)entry->UserToken();
580 if (token == NULL || token->implicit)
581 return B_OK;
582
583 int entryFD = token->fd;
584
585 // create the attribute
586 int fd = fs_fopen_attr(entryFD, attribute->Name(), attribute->Type(),
587 O_WRONLY | O_CREAT | O_TRUNC);
588 if (fd < 0) {
589 int parentFD;
590 const char* entryName;
591 _GetParentFDAndEntryName(entry, parentFD, entryName);
592
593 fprintf(stderr, "Error: Failed to create attribute \"%s\" of "
594 "file \"%s\": %s\n", attribute->Name(),
595 _EntryPath(entry).String(), strerror(errno));
596 return errno;
597 }
598
599 // write data
600 status_t error = _ExtractFileData(fPackageFileReader, attribute->Data(),
601 fd);
602
603 fs_close_attr(fd);
604
605 return error;
606 }
607
HandleEntryDonePackageContentExtractHandler608 virtual status_t HandleEntryDone(
609 typename VersionPolicy::PackageEntry* entry)
610 {
611 Token* token = (Token*)entry->UserToken();
612
613 // set the node permissions for non-symlinks
614 if (token != NULL && !S_ISLNK(entry->Mode())) {
615 // get parent FD and entry name
616 int parentFD;
617 const char* entryName;
618 _GetParentFDAndEntryName(entry, parentFD, entryName);
619
620 if (fchmodat(parentFD, entryName, entry->Mode() & ALLPERMS,
621 /*AT_SYMLINK_NOFOLLOW*/0) != 0) {
622 fprintf(stderr, "Warning: Failed to set permissions of file "
623 "\"%s\": %s\n", _EntryPath(entry).String(),
624 strerror(errno));
625 }
626 }
627
628 if (token != NULL) {
629 delete token;
630 entry->SetUserToken(NULL);
631 }
632
633 return B_OK;
634 }
635
HandlePackageAttributePackageContentExtractHandler636 virtual status_t HandlePackageAttribute(
637 const BPackageInfoAttributeValue& value)
638 {
639 return B_OK;
640 }
641
HandleErrorOccurredPackageContentExtractHandler642 virtual void HandleErrorOccurred()
643 {
644 fErrorOccurred = true;
645 }
646
647 private:
648 struct Token {
649 Entry* filterEntry;
650 int fd;
651 bool implicit;
652
TokenPackageContentExtractHandler::Token653 Token()
654 :
655 filterEntry(NULL),
656 fd(-1),
657 implicit(true)
658 {
659 }
660
~TokenPackageContentExtractHandler::Token661 ~Token()
662 {
663 if (fd >= 0)
664 close(fd);
665 }
666 };
667
668 private:
_AddFilterEntryPackageContentExtractHandler669 status_t _AddFilterEntry(Entry* parentEntry, const char* _name,
670 size_t nameLength, bool implicit, Entry*& _entry)
671 {
672 BString name(_name, nameLength);
673 if (name.IsEmpty())
674 return B_NO_MEMORY;
675
676 return Entry::Create(parentEntry, name.String(), implicit, _entry);
677 }
678
_GetParentFDAndEntryNamePackageContentExtractHandler679 void _GetParentFDAndEntryName(typename VersionPolicy::PackageEntry* entry,
680 int& _parentFD, const char*& _entryName)
681 {
682 _entryName = entry->Name();
683
684 if (fInfoFileName != NULL
685 && strcmp(_entryName, VersionPolicy::PackageInfoFileName()) == 0) {
686 _parentFD = AT_FDCWD;
687 _entryName = fInfoFileName;
688 } else {
689 _parentFD = entry->Parent() != NULL
690 ? ((Token*)entry->Parent()->UserToken())->fd : fBaseDirectory;
691 }
692 }
693
_EntryPathPackageContentExtractHandler694 BString _EntryPath(const typename VersionPolicy::PackageEntry* entry)
695 {
696 BString path;
697
698 if (const typename VersionPolicy::PackageEntry* parent
699 = entry->Parent()) {
700 path = _EntryPath(parent);
701 path << '/';
702 }
703
704 path << entry->Name();
705 return path;
706 }
707
_ExtractFileDataPackageContentExtractHandler708 status_t _ExtractFileData(
709 typename VersionPolicy::HeapReaderBase* dataReader,
710 const typename VersionPolicy::PackageData& data, int fd)
711 {
712 // create a PackageDataReader
713 BAbstractBufferedDataReader* reader;
714 status_t error = VersionPolicy::CreatePackageDataReader(fBufferPool,
715 dataReader, data, reader);
716 if (error != B_OK)
717 return error;
718 ObjectDeleter<BAbstractBufferedDataReader> readerDeleter(reader);
719
720 // write the data
721 off_t bytesRemaining = VersionPolicy::PackageDataUncompressedSize(data);
722 off_t offset = 0;
723 while (bytesRemaining > 0) {
724 // read
725 size_t toCopy = std::min((off_t)fDataBufferSize, bytesRemaining);
726 error = reader->ReadData(offset, fDataBuffer, toCopy);
727 if (error != B_OK) {
728 fprintf(stderr, "Error: Failed to read data: %s\n",
729 strerror(error));
730 return error;
731 }
732
733 // write
734 ssize_t bytesWritten = write_pos(fd, offset, fDataBuffer, toCopy);
735 if (bytesWritten < 0) {
736 fprintf(stderr, "Error: Failed to write data: %s\n",
737 strerror(errno));
738 return errno;
739 }
740 if ((size_t)bytesWritten != toCopy) {
741 fprintf(stderr, "Error: Failed to write all data (%zd of "
742 "%zu)\n", bytesWritten, toCopy);
743 return B_ERROR;
744 }
745
746 offset += toCopy;
747 bytesRemaining -= toCopy;
748 }
749
750 return B_OK;
751 }
752
753 private:
754 BBufferPool* fBufferPool;
755 typename VersionPolicy::HeapReaderBase* fPackageFileReader;
756 void* fDataBuffer;
757 size_t fDataBufferSize;
758 Entry fRootFilterEntry;
759 int fBaseDirectory;
760 const char* fInfoFileName;
761 bool fErrorOccurred;
762 };
763
764
765 template<typename VersionPolicy>
766 static void
do_extract(const char * packageFileName,const char * changeToDirectory,const char * packageInfoFileName,const char * const * explicitEntries,int explicitEntryCount,bool ignoreVersionError)767 do_extract(const char* packageFileName, const char* changeToDirectory,
768 const char* packageInfoFileName, const char* const* explicitEntries,
769 int explicitEntryCount, bool ignoreVersionError)
770 {
771 // open package
772 BStandardErrorOutput errorOutput;
773 BBlockBufferPoolNoLock bufferPool(VersionPolicy::BufferSize(), 2);
774 if (bufferPool.Init() != B_OK) {
775 errorOutput.PrintError("Error: Out of memory!\n");
776 exit(1);
777 }
778
779 typename VersionPolicy::PackageReader packageReader(&errorOutput);
780 status_t error = VersionPolicy::InitReader(packageReader, packageFileName);
781 if (error != B_OK) {
782 if (ignoreVersionError && error == B_MISMATCHED_VALUES)
783 return;
784 exit(1);
785 }
786
787 typename VersionPolicy::HeapReaderBase* heapReader;
788 bool mustDeleteHeapReader;
789 error = VersionPolicy::GetHeapReader(packageReader, heapReader,
790 mustDeleteHeapReader);
791 if (error != B_OK) {
792 fprintf(stderr, "Error: Failed to create heap reader: \"%s\"\n",
793 strerror(error));
794 exit(1);
795 }
796 ObjectDeleter<BDataReader> heapReaderDeleter(
797 mustDeleteHeapReader ? heapReader : NULL);
798
799 PackageContentExtractHandler<VersionPolicy> handler(&bufferPool,
800 heapReader);
801 error = handler.Init();
802 if (error != B_OK)
803 exit(1);
804
805 // If entries to extract have been specified explicitly, add those to the
806 // filtered ones.
807 if (explicitEntryCount > 0) {
808 for (int i = 0; i < explicitEntryCount; i++) {
809 const char* entryName = explicitEntries[i];
810 if (entryName[0] == '\0' || entryName[0] == '/') {
811 fprintf(stderr, "Error: Invalid entry name: \"%s\"\n",
812 entryName);
813 exit(1);
814 }
815 if (handler.AddFilterEntry(entryName) != B_OK)
816 exit(1);
817 }
818 } else
819 handler.SetExtractAll();
820
821 // get the target directory, if requested
822 if (changeToDirectory != NULL) {
823 int currentDirFD = open(changeToDirectory, O_RDONLY);
824 if (currentDirFD < 0) {
825 fprintf(stderr, "Error: Failed to change the current working "
826 "directory to \"%s\": %s\n", changeToDirectory,
827 strerror(errno));
828 exit(1);
829 }
830
831 handler.SetBaseDirectory(currentDirFD);
832 }
833
834 // If a package info file name is given, set it.
835 if (packageInfoFileName != NULL)
836 handler.SetPackageInfoFile(packageInfoFileName);
837
838 // extract
839 error = packageReader.ParseContent(&handler);
840 if (error != B_OK)
841 exit(1);
842
843 // check whether all explicitly specified entries have been extracted
844 if (explicitEntryCount > 0) {
845 for (int i = 0; i < explicitEntryCount; i++) {
846 if (Entry* entry = handler.FindFilterEntry(explicitEntries[i])) {
847 if (!entry->Seen()) {
848 fprintf(stderr, "Warning: Entry \"%s\" not found.\n",
849 explicitEntries[i]);
850 }
851 }
852 }
853 }
854
855 exit(0);
856 }
857
858
859 int
command_extract(int argc,const char * const * argv)860 command_extract(int argc, const char* const* argv)
861 {
862 const char* changeToDirectory = NULL;
863 const char* packageInfoFileName = NULL;
864
865 while (true) {
866 static struct option sLongOptions[] = {
867 { "help", no_argument, 0, 'h' },
868 { 0, 0, 0, 0 }
869 };
870
871 opterr = 0; // don't print errors
872 int c = getopt_long(argc, (char**)argv, "+C:hi:", sLongOptions, NULL);
873 if (c == -1)
874 break;
875
876 switch (c) {
877 case 'C':
878 changeToDirectory = optarg;
879 break;
880
881 case 'h':
882 print_usage_and_exit(false);
883 break;
884
885 case 'i':
886 packageInfoFileName = optarg;
887 break;
888
889 default:
890 print_usage_and_exit(true);
891 break;
892 }
893 }
894
895 // At least one argument should remain -- the package file name. Any further
896 // arguments are the names of the entries to extract.
897 if (optind + 1 > argc)
898 print_usage_and_exit(true);
899
900 const char* packageFileName = argv[optind++];
901 const char* const* explicitEntries = argv + optind;
902 int explicitEntryCount = argc - optind;
903 do_extract<VersionPolicyV2>(packageFileName, changeToDirectory,
904 packageInfoFileName, explicitEntries, explicitEntryCount, true);
905 do_extract<VersionPolicyV1>(packageFileName, changeToDirectory,
906 packageInfoFileName, explicitEntries, explicitEntryCount, false);
907
908 return 0;
909 }
910