1 /*
2 * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include <package/hpkg/RepositoryWriterImpl.h>
8
9 #include <algorithm>
10 #include <new>
11
12 #include <ByteOrder.h>
13 #include <Message.h>
14 #include <Path.h>
15
16 #include <AutoDeleter.h>
17 #include <HashSet.h>
18
19 #include <package/hpkg/BlockBufferPoolNoLock.h>
20 #include <package/hpkg/HPKGDefsPrivate.h>
21 #include <package/hpkg/PackageDataReader.h>
22 #include <package/hpkg/PackageEntry.h>
23 #include <package/hpkg/PackageFileHeapWriter.h>
24 #include <package/hpkg/PackageInfoAttributeValue.h>
25 #include <package/hpkg/PackageReader.h>
26 #include <package/ChecksumAccessors.h>
27 #include <package/HashableString.h>
28 #include <package/PackageInfoContentHandler.h>
29 #include <package/RepositoryInfo.h>
30
31
32 namespace BPackageKit {
33
34 namespace BHPKG {
35
36 namespace BPrivate {
37
38
39 using BPackageKit::BPrivate::GeneralFileChecksumAccessor;
40 using BPackageKit::BPrivate::HashableString;
41
42
43 namespace {
44
45
46 // #pragma mark - PackageEntryDataFetcher
47
48
49 struct PackageEntryDataFetcher {
PackageEntryDataFetcherBPackageKit::BHPKG::BPrivate::__anon46e749f50111::PackageEntryDataFetcher50 PackageEntryDataFetcher(BErrorOutput* errorOutput,
51 BPackageData& packageData)
52 :
53 fErrorOutput(errorOutput),
54 fPackageData(packageData)
55 {
56 }
57
ReadIntoStringBPackageKit::BHPKG::BPrivate::__anon46e749f50111::PackageEntryDataFetcher58 status_t ReadIntoString(BAbstractBufferedDataReader* heapReader,
59 BString& _contents)
60 {
61 // create a PackageDataReader
62 BAbstractBufferedDataReader* reader;
63 status_t result = BPackageDataReaderFactory()
64 .CreatePackageDataReader(heapReader, fPackageData, reader);
65 if (result != B_OK)
66 return result;
67 ObjectDeleter<BAbstractBufferedDataReader> readerDeleter(reader);
68
69 // copy data into the given string
70 int32 bufferSize = fPackageData.Size();
71 char* buffer = _contents.LockBuffer(bufferSize);
72 if (buffer == NULL)
73 return B_NO_MEMORY;
74
75 result = reader->ReadData(0, buffer, bufferSize);
76 if (result != B_OK) {
77 fErrorOutput->PrintError("Error: Failed to read data: %s\n",
78 strerror(result));
79 _contents.UnlockBuffer(0);
80 } else
81 _contents.UnlockBuffer(bufferSize);
82
83 return result;
84 }
85
86 private:
87 BErrorOutput* fErrorOutput;
88 BPackageData& fPackageData;
89 };
90
91
92 // #pragma mark - PackageContentHandler
93
94
95 struct PackageContentHandler : public BPackageInfoContentHandler {
PackageContentHandlerBPackageKit::BHPKG::BPrivate::__anon46e749f50111::PackageContentHandler96 PackageContentHandler(BErrorOutput* errorOutput, BPackageInfo* packageInfo,
97 BAbstractBufferedDataReader* heapReader,
98 BRepositoryInfo* repositoryInfo)
99 :
100 BPackageInfoContentHandler(*packageInfo, errorOutput),
101 fHeapReader(heapReader),
102 fRepositoryInfo(repositoryInfo)
103 {
104 }
105
HandleEntryBPackageKit::BHPKG::BPrivate::__anon46e749f50111::PackageContentHandler106 virtual status_t HandleEntry(BPackageEntry* entry)
107 {
108 // if license must be approved, read any license files from package such
109 // that those can be stored in the repository later
110 if ((fPackageInfo.Flags() & B_PACKAGE_FLAG_APPROVE_LICENSE) == 0
111 || entry == NULL)
112 return B_OK;
113
114 // return if not in ./data/licenses folder
115 const BPackageEntry* parent = entry->Parent();
116 BString licenseFolderName("licenses");
117 if (parent == NULL || licenseFolderName != parent->Name())
118 return B_OK;
119
120 parent = parent->Parent();
121 BString dataFolderName("data");
122 if (parent == NULL || dataFolderName != parent->Name())
123 return B_OK;
124
125 if (parent->Parent() != NULL)
126 return B_OK;
127
128 // check if license already is in repository
129 const BStringList& licenseNames = fRepositoryInfo->LicenseNames();
130 for (int i = 0; i < licenseNames.CountStrings(); ++i) {
131 if (licenseNames.StringAt(i).ICompare(entry->Name()) == 0) {
132 // license already exists
133 return B_OK;
134 }
135 }
136
137 // fetch contents of license file
138 BPackageData& packageData = entry->Data();
139 PackageEntryDataFetcher dataFetcher(fErrorOutput, packageData);
140
141 BString licenseText;
142 status_t result = dataFetcher.ReadIntoString(fHeapReader, licenseText);
143 if (result != B_OK)
144 return result;
145
146 // add license to repository
147 return fRepositoryInfo->AddLicense(entry->Name(), licenseText);
148 }
149
HandleEntryAttributeBPackageKit::BHPKG::BPrivate::__anon46e749f50111::PackageContentHandler150 virtual status_t HandleEntryAttribute(BPackageEntry* entry,
151 BPackageEntryAttribute* attribute)
152 {
153 return B_OK;
154 }
155
HandleEntryDoneBPackageKit::BHPKG::BPrivate::__anon46e749f50111::PackageContentHandler156 virtual status_t HandleEntryDone(BPackageEntry* entry)
157 {
158 return B_OK;
159 }
160
HandleErrorOccurredBPackageKit::BHPKG::BPrivate::__anon46e749f50111::PackageContentHandler161 virtual void HandleErrorOccurred()
162 {
163 }
164
165 private:
166 BPackageReader* fPackageReader;
167 BAbstractBufferedDataReader* fHeapReader;
168 BRepositoryInfo* fRepositoryInfo;
169 };
170
171
172 } // anonymous namespace
173
174
175 // #pragma mark - PackageNameSet
176
177
178 struct RepositoryWriterImpl::PackageNameSet
179 : public ::BPrivate::HashSet<HashableString> {
180 };
181
182
183 // #pragma mark - RepositoryWriterImpl
184
185
RepositoryWriterImpl(BRepositoryWriterListener * listener,BRepositoryInfo * repositoryInfo)186 RepositoryWriterImpl::RepositoryWriterImpl(BRepositoryWriterListener* listener,
187 BRepositoryInfo* repositoryInfo)
188 :
189 inherited("repository", listener),
190 fListener(listener),
191 fRepositoryInfo(repositoryInfo),
192 fPackageCount(0),
193 fPackageNames(NULL)
194 {
195 }
196
197
~RepositoryWriterImpl()198 RepositoryWriterImpl::~RepositoryWriterImpl()
199 {
200 delete fPackageNames;
201 }
202
203
204 status_t
Init(const char * fileName)205 RepositoryWriterImpl::Init(const char* fileName)
206 {
207 try {
208 fPackageNames = new PackageNameSet();
209 status_t result = fPackageNames->InitCheck();
210 if (result != B_OK)
211 return result;
212 return _Init(fileName);
213 } catch (status_t error) {
214 return error;
215 } catch (std::bad_alloc&) {
216 fListener->PrintError("Out of memory!\n");
217 return B_NO_MEMORY;
218 }
219 }
220
221
222 status_t
AddPackage(const BEntry & packageEntry)223 RepositoryWriterImpl::AddPackage(const BEntry& packageEntry)
224 {
225 try {
226 return _AddPackage(packageEntry);
227 } catch (status_t error) {
228 return error;
229 } catch (std::bad_alloc&) {
230 fListener->PrintError("Out of memory!\n");
231 return B_NO_MEMORY;
232 }
233 }
234
235
236 status_t
AddPackageInfo(const BPackageInfo & packageInfo)237 RepositoryWriterImpl::AddPackageInfo(const BPackageInfo& packageInfo)
238 {
239 try {
240 return _AddPackageInfo(packageInfo);
241 } catch (status_t error) {
242 return error;
243 } catch (std::bad_alloc&) {
244 fListener->PrintError("Out of memory!\n");
245 return B_NO_MEMORY;
246 }
247 }
248
249
250 status_t
Finish()251 RepositoryWriterImpl::Finish()
252 {
253 try {
254 return _Finish();
255 } catch (status_t error) {
256 return error;
257 } catch (std::bad_alloc&) {
258 fListener->PrintError("Out of memory!\n");
259 return B_NO_MEMORY;
260 }
261 }
262
263
264 status_t
_Init(const char * fileName)265 RepositoryWriterImpl::_Init(const char* fileName)
266 {
267 status_t error = inherited::Init(NULL, false, fileName,
268 BPackageWriterParameters());
269 if (error != B_OK)
270 return error;
271
272 return InitHeapReader(sizeof(hpkg_repo_header));
273 }
274
275
276 status_t
_Finish()277 RepositoryWriterImpl::_Finish()
278 {
279 hpkg_repo_header header;
280
281 // write repository info
282 uint64 infoLength;
283 status_t result = _WriteRepositoryInfo(header, infoLength);
284 if (result != B_OK)
285 return result;
286
287 // write package attributes
288 uint64 packagesLength;
289 _WritePackageAttributes(header, packagesLength);
290
291 // flush the heap writer
292 result = fHeapWriter->Finish();
293 if (result != B_OK)
294 return result;
295 uint64 compressedHeapSize = fHeapWriter->CompressedHeapSize();
296 uint64 totalSize = fHeapWriter->HeapOffset() + compressedHeapSize;
297
298 header.heap_compression = B_HOST_TO_BENDIAN_INT16(
299 Parameters().Compression());
300 header.heap_chunk_size = B_HOST_TO_BENDIAN_INT32(fHeapWriter->ChunkSize());
301 header.heap_size_compressed = B_HOST_TO_BENDIAN_INT64(compressedHeapSize);
302 header.heap_size_uncompressed = B_HOST_TO_BENDIAN_INT64(
303 fHeapWriter->UncompressedHeapSize());
304
305 fListener->OnRepositoryDone(sizeof(header), infoLength,
306 fRepositoryInfo->LicenseNames().CountStrings(), fPackageCount,
307 packagesLength, totalSize);
308
309 // update the general header info and write the header
310 header.magic = B_HOST_TO_BENDIAN_INT32(B_HPKG_REPO_MAGIC);
311 header.header_size = B_HOST_TO_BENDIAN_INT16((uint16)sizeof(header));
312 header.version = B_HOST_TO_BENDIAN_INT16(B_HPKG_REPO_VERSION);
313 header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize);
314 header.minor_version = B_HOST_TO_BENDIAN_INT16(B_HPKG_REPO_MINOR_VERSION);
315
316 RawWriteBuffer(&header, sizeof(header), 0);
317
318 SetFinished(true);
319 return B_OK;
320 }
321
322
323 status_t
_AddPackage(const BEntry & packageEntry)324 RepositoryWriterImpl::_AddPackage(const BEntry& packageEntry)
325 {
326 status_t result = packageEntry.InitCheck();
327 if (result != B_OK) {
328 fListener->PrintError("entry not initialized!\n");
329 return result;
330 }
331
332 BPath packagePath;
333 if ((result = packageEntry.GetPath(&packagePath)) != B_OK) {
334 fListener->PrintError("can't get path for entry '%s'!\n",
335 packageEntry.Name());
336 return result;
337 }
338
339 BPackageReader packageReader(fListener);
340 if ((result = packageReader.Init(packagePath.Path())) != B_OK) {
341 fListener->PrintError("can't create package reader for '%s'!\n",
342 packagePath.Path());
343 return result;
344 }
345
346 fPackageInfo.Clear();
347
348 // parse package
349 PackageContentHandler contentHandler(fListener, &fPackageInfo,
350 packageReader.HeapReader(), fRepositoryInfo);
351 if ((result = packageReader.ParseContent(&contentHandler)) != B_OK)
352 return result;
353
354 // determine package's checksum
355 GeneralFileChecksumAccessor checksumAccessor(packageEntry);
356 BString checksum;
357 if ((result = checksumAccessor.GetChecksum(checksum)) != B_OK) {
358 fListener->PrintError("can't compute checksum of file '%s'!\n",
359 packagePath.Path());
360 return result;
361 }
362 fPackageInfo.SetChecksum(checksum);
363
364 // register package's attributes
365 if ((result = _RegisterCurrentPackageInfo()) != B_OK)
366 return result;
367
368 return B_OK;
369 }
370
371
372 status_t
_AddPackageInfo(const BPackageInfo & packageInfo)373 RepositoryWriterImpl::_AddPackageInfo(const BPackageInfo& packageInfo)
374 {
375 fPackageInfo = packageInfo;
376
377 // register package's attributes
378 status_t result = _RegisterCurrentPackageInfo();
379 if (result != B_OK)
380 return result;
381
382 return B_OK;
383 }
384
385
386 status_t
_RegisterCurrentPackageInfo()387 RepositoryWriterImpl::_RegisterCurrentPackageInfo()
388 {
389 status_t result = fPackageInfo.InitCheck();
390 if (result != B_OK) {
391 fListener->PrintError("package %s has incomplete package-info!\n",
392 fPackageInfo.Name().String());
393 return result;
394 }
395
396 // reject package with a name that we've seen already
397 if (fPackageNames->Contains(fPackageInfo.Name())) {
398 fListener->PrintError("package %s has already been added!\n",
399 fPackageInfo.Name().String());
400 return B_NAME_IN_USE;
401 }
402
403 // all packages must have the same vendor as the repository
404 const BString& expectedVendor = fRepositoryInfo->Vendor();
405 if (fPackageInfo.Vendor().ICompare(expectedVendor) != 0) {
406 fListener->PrintError("package '%s' has unexpected vendor '%s' "
407 "(expected '%s')!\n", fPackageInfo.Name().String(),
408 fPackageInfo.Vendor().String(), expectedVendor.String());
409 return B_BAD_DATA;
410 }
411
412 // all packages must have an architecture that's compatible with the one
413 // used by the repository
414 BPackageArchitecture expectedArchitecture = fRepositoryInfo->Architecture();
415 if (fPackageInfo.Architecture() != expectedArchitecture
416 && fPackageInfo.Architecture() != B_PACKAGE_ARCHITECTURE_ANY
417 && fPackageInfo.Architecture() != B_PACKAGE_ARCHITECTURE_SOURCE) {
418 fListener->PrintError(
419 "package '%s' has non-matching architecture '%s' "
420 "(expected '%s', '%s', or '%s')!\n", fPackageInfo.Name().String(),
421 BPackageInfo::kArchitectureNames[fPackageInfo.Architecture()],
422 BPackageInfo::kArchitectureNames[expectedArchitecture],
423 BPackageInfo::kArchitectureNames[B_PACKAGE_ARCHITECTURE_ANY],
424 BPackageInfo::kArchitectureNames[B_PACKAGE_ARCHITECTURE_SOURCE]);
425 return B_BAD_DATA;
426 }
427
428 if ((result = fPackageNames->Add(fPackageInfo.Name())) != B_OK)
429 return result;
430
431 PackageAttribute* packageAttribute = AddStringAttribute(
432 B_HPKG_ATTRIBUTE_ID_PACKAGE, fPackageInfo.Name(), PackageAttributes());
433 RegisterPackageInfo(packageAttribute->children, fPackageInfo);
434 fPackageCount++;
435 fListener->OnPackageAdded(fPackageInfo);
436
437 return B_OK;
438 }
439
440
441 status_t
_WriteRepositoryInfo(hpkg_repo_header & header,uint64 & _length)442 RepositoryWriterImpl::_WriteRepositoryInfo(hpkg_repo_header& header,
443 uint64& _length)
444 {
445 // archive and flatten the repository info and write it
446 BMessage archive;
447 status_t result = fRepositoryInfo->Archive(&archive);
448 if (result != B_OK) {
449 fListener->PrintError("can't archive repository header!\n");
450 return result;
451 }
452
453 ssize_t flattenedSize = archive.FlattenedSize();
454 char buffer[flattenedSize];
455 if ((result = archive.Flatten(buffer, flattenedSize)) != B_OK) {
456 fListener->PrintError("can't flatten repository header!\n");
457 return result;
458 }
459
460 WriteBuffer(buffer, flattenedSize);
461
462 // notify listener
463 fListener->OnRepositoryInfoSectionDone(flattenedSize);
464
465 // update the header
466 header.info_length = B_HOST_TO_BENDIAN_INT32(flattenedSize);
467
468 _length = flattenedSize;
469 return B_OK;
470 }
471
472
473 void
_WritePackageAttributes(hpkg_repo_header & header,uint64 & _length)474 RepositoryWriterImpl::_WritePackageAttributes(hpkg_repo_header& header,
475 uint64& _length)
476 {
477 // write the package attributes (zlib writer on top of a file writer)
478 uint64 startOffset = fHeapWriter->UncompressedHeapSize();
479
480 uint32 stringsLength;
481 uint32 stringsCount = WritePackageAttributes(PackageAttributes(),
482 stringsLength);
483
484 uint64 sectionSize = fHeapWriter->UncompressedHeapSize() - startOffset;
485
486 fListener->OnPackageAttributesSectionDone(stringsCount, sectionSize);
487
488 // update the header
489 header.packages_length = B_HOST_TO_BENDIAN_INT64(sectionSize);
490 header.packages_strings_count = B_HOST_TO_BENDIAN_INT64(stringsCount);
491 header.packages_strings_length = B_HOST_TO_BENDIAN_INT64(stringsLength);
492
493 _length = sectionSize;
494 }
495
496
497 } // namespace BPrivate
498
499 } // namespace BHPKG
500
501 } // namespace BPackageKit
502