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/HPKGDefsPrivate.h> 20 #include <package/hpkg/PackageDataReader.h> 21 #include <package/hpkg/PackageEntry.h> 22 #include <package/hpkg/PackageInfoAttributeValue.h> 23 #include <package/hpkg/PackageReader.h> 24 #include <package/BlockBufferCacheNoLock.h> 25 #include <package/ChecksumAccessors.h> 26 #include <package/HashableString.h> 27 #include <package/RepositoryInfo.h> 28 29 30 namespace BPackageKit { 31 32 namespace BHPKG { 33 34 namespace BPrivate { 35 36 37 using BPackageKit::BPrivate::GeneralFileChecksumAccessor; 38 using BPackageKit::BPrivate::HashableString; 39 40 41 namespace { 42 43 44 struct PackageEntryDataFetcher { 45 PackageEntryDataFetcher(BErrorOutput* errorOutput, 46 BPackageData& packageData) 47 : 48 fErrorOutput(errorOutput), 49 fBufferCache(B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB, 2), 50 fPackageData(packageData) 51 { 52 } 53 54 status_t ReadIntoString(BDataReader* dataReader, BString& _contents) 55 { 56 // create a BPackageDataReader 57 BPackageDataReader* reader; 58 status_t result = BPackageDataReaderFactory(&fBufferCache) 59 .CreatePackageDataReader(dataReader, fPackageData, reader); 60 if (result != B_OK) 61 return result; 62 ObjectDeleter<BPackageDataReader> readerDeleter(reader); 63 64 // copy data into the given string 65 int32 bufferSize = fPackageData.UncompressedSize(); 66 char* buffer = _contents.LockBuffer(bufferSize); 67 if (buffer == NULL) 68 return B_NO_MEMORY; 69 70 result = reader->ReadData(0, buffer, bufferSize); 71 if (result != B_OK) { 72 fErrorOutput->PrintError("Error: Failed to read data: %s\n", 73 strerror(result)); 74 _contents.UnlockBuffer(0); 75 } else 76 _contents.UnlockBuffer(bufferSize); 77 78 return result; 79 } 80 81 private: 82 BErrorOutput* fErrorOutput; 83 BBlockBufferCacheNoLock fBufferCache; 84 BPackageData& fPackageData; 85 }; 86 87 88 struct PackageContentHandler : public BPackageContentHandler { 89 PackageContentHandler(BErrorOutput* errorOutput, BPackageInfo* packageInfo, 90 int packageFileFD, BRepositoryInfo* repositoryInfo) 91 : 92 fErrorOutput(errorOutput), 93 fPackageInfo(packageInfo), 94 fPackageFileReader(packageFileFD), 95 fRepositoryInfo(repositoryInfo) 96 { 97 } 98 99 virtual status_t HandleEntry(BPackageEntry* entry) 100 { 101 // if license must be approved, read any license files from package such 102 // that those can be stored in the repository later 103 if ((fPackageInfo->Flags() & B_PACKAGE_FLAG_APPROVE_LICENSE) == 0 104 || entry == NULL) 105 return B_OK; 106 107 // return if not in ./data/licenses folder 108 const BPackageEntry* parent = entry->Parent(); 109 BString licenseFolderName("licenses"); 110 if (parent == NULL || licenseFolderName != parent->Name()) 111 return B_OK; 112 113 parent = parent->Parent(); 114 BString dataFolderName("data"); 115 if (parent == NULL || dataFolderName != parent->Name()) 116 return B_OK; 117 118 if (parent->Parent() != NULL) 119 return B_OK; 120 121 // check if license already is in repository 122 const BObjectList<BString>& licenseNames 123 = fRepositoryInfo->LicenseNames(); 124 for (int i = 0; i < licenseNames.CountItems(); ++i) { 125 if (licenseNames.ItemAt(i)->ICompare(entry->Name()) == 0) { 126 // license already exists 127 return B_OK; 128 } 129 } 130 131 // fetch contents of license file 132 BPackageData& packageData = entry->Data(); 133 PackageEntryDataFetcher dataFetcher(fErrorOutput, packageData); 134 135 BString licenseText; 136 status_t result; 137 if (packageData.IsEncodedInline()) { 138 BBufferDataReader dataReader(packageData.InlineData(), 139 packageData.CompressedSize()); 140 result = dataFetcher.ReadIntoString(&dataReader, licenseText); 141 } else { 142 result 143 = dataFetcher.ReadIntoString(&fPackageFileReader, licenseText); 144 } 145 146 if (result != B_OK) 147 return result; 148 149 // add license to repository 150 return fRepositoryInfo->AddLicense(entry->Name(), licenseText); 151 } 152 153 virtual status_t HandleEntryAttribute(BPackageEntry* entry, 154 BPackageEntryAttribute* attribute) 155 { 156 return B_OK; 157 } 158 159 virtual status_t HandleEntryDone(BPackageEntry* entry) 160 { 161 return B_OK; 162 } 163 164 virtual status_t HandlePackageAttribute( 165 const BPackageInfoAttributeValue& value) 166 { 167 switch (value.attributeID) { 168 case B_PACKAGE_INFO_NAME: 169 fPackageInfo->SetName(value.string); 170 break; 171 172 case B_PACKAGE_INFO_SUMMARY: 173 fPackageInfo->SetSummary(value.string); 174 break; 175 176 case B_PACKAGE_INFO_DESCRIPTION: 177 fPackageInfo->SetDescription(value.string); 178 break; 179 180 case B_PACKAGE_INFO_VENDOR: 181 fPackageInfo->SetVendor(value.string); 182 break; 183 184 case B_PACKAGE_INFO_PACKAGER: 185 fPackageInfo->SetPackager(value.string); 186 break; 187 188 case B_PACKAGE_INFO_FLAGS: 189 fPackageInfo->SetFlags(value.unsignedInt); 190 break; 191 192 case B_PACKAGE_INFO_ARCHITECTURE: 193 fPackageInfo->SetArchitecture( 194 (BPackageArchitecture)value.unsignedInt); 195 break; 196 197 case B_PACKAGE_INFO_VERSION: 198 fPackageInfo->SetVersion(value.version); 199 break; 200 201 case B_PACKAGE_INFO_COPYRIGHTS: 202 fPackageInfo->AddCopyright(value.string); 203 break; 204 205 case B_PACKAGE_INFO_LICENSES: 206 fPackageInfo->AddLicense(value.string); 207 break; 208 209 case B_PACKAGE_INFO_PROVIDES: 210 fPackageInfo->AddProvides(value.resolvable); 211 break; 212 213 case B_PACKAGE_INFO_REQUIRES: 214 fPackageInfo->AddRequires(value.resolvableExpression); 215 break; 216 217 case B_PACKAGE_INFO_SUPPLEMENTS: 218 fPackageInfo->AddSupplements(value.resolvableExpression); 219 break; 220 221 case B_PACKAGE_INFO_CONFLICTS: 222 fPackageInfo->AddConflicts(value.resolvableExpression); 223 break; 224 225 case B_PACKAGE_INFO_FRESHENS: 226 fPackageInfo->AddFreshens(value.resolvableExpression); 227 break; 228 229 case B_PACKAGE_INFO_REPLACES: 230 fPackageInfo->AddReplaces(value.string); 231 break; 232 233 default: 234 fErrorOutput->PrintError( 235 "Invalid package attribute section: unexpected package " 236 "attribute id %d encountered\n", value.attributeID); 237 return B_BAD_DATA; 238 } 239 240 return B_OK; 241 } 242 243 virtual void HandleErrorOccurred() 244 { 245 } 246 247 private: 248 BErrorOutput* fErrorOutput; 249 BPackageInfo* fPackageInfo; 250 BPackageReader* fPackageReader; 251 BFDDataReader fPackageFileReader; 252 BRepositoryInfo* fRepositoryInfo; 253 }; 254 255 256 } // anonymous namespace 257 258 259 struct RepositoryWriterImpl::PackageNameSet 260 : public ::BPrivate::HashSet<HashableString> { 261 }; 262 263 264 RepositoryWriterImpl::RepositoryWriterImpl(BRepositoryWriterListener* listener, 265 BRepositoryInfo* repositoryInfo) 266 : 267 inherited(listener), 268 fListener(listener), 269 fRepositoryInfo(repositoryInfo), 270 fPackageCount(0), 271 fPackageNames(NULL) 272 { 273 } 274 275 276 RepositoryWriterImpl::~RepositoryWriterImpl() 277 { 278 delete fPackageNames; 279 } 280 281 282 status_t 283 RepositoryWriterImpl::Init(const char* fileName) 284 { 285 try { 286 fPackageNames = new PackageNameSet(); 287 status_t result = fPackageNames->InitCheck(); 288 if (result != B_OK) 289 return result; 290 return _Init(fileName); 291 } catch (status_t error) { 292 return error; 293 } catch (std::bad_alloc) { 294 fListener->PrintError("Out of memory!\n"); 295 return B_NO_MEMORY; 296 } 297 } 298 299 300 status_t 301 RepositoryWriterImpl::AddPackage(const BEntry& packageEntry) 302 { 303 try { 304 return _AddPackage(packageEntry); 305 } catch (status_t error) { 306 return error; 307 } catch (std::bad_alloc) { 308 fListener->PrintError("Out of memory!\n"); 309 return B_NO_MEMORY; 310 } 311 } 312 313 314 status_t 315 RepositoryWriterImpl::Finish() 316 { 317 try { 318 return _Finish(); 319 } catch (status_t error) { 320 return error; 321 } catch (std::bad_alloc) { 322 fListener->PrintError("Out of memory!\n"); 323 return B_NO_MEMORY; 324 } 325 } 326 327 328 status_t 329 RepositoryWriterImpl::_Init(const char* fileName) 330 { 331 return inherited::Init(fileName, "repository"); 332 } 333 334 335 status_t 336 RepositoryWriterImpl::_Finish() 337 { 338 hpkg_repo_header header; 339 340 // write repository header 341 ssize_t infoLengthCompressed; 342 status_t result = _WriteRepositoryInfo(header, infoLengthCompressed); 343 if (result != B_OK) 344 return result; 345 346 // write package attributes 347 ssize_t packagesLengthCompressed; 348 off_t totalSize = _WritePackageAttributes(header, 349 sizeof(header) + infoLengthCompressed, packagesLengthCompressed); 350 351 fListener->OnRepositoryDone(sizeof(header), infoLengthCompressed, 352 fRepositoryInfo->LicenseNames().CountItems(), fPackageCount, 353 packagesLengthCompressed, totalSize); 354 355 // general 356 header.magic = B_HOST_TO_BENDIAN_INT32(B_HPKG_REPO_MAGIC); 357 header.header_size = B_HOST_TO_BENDIAN_INT16((uint16)sizeof(header)); 358 header.version = B_HOST_TO_BENDIAN_INT16(B_HPKG_REPO_VERSION); 359 header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize); 360 361 // write the header 362 WriteBuffer(&header, sizeof(header), 0); 363 364 SetFinished(true); 365 return B_OK; 366 } 367 368 369 status_t 370 RepositoryWriterImpl::_AddPackage(const BEntry& packageEntry) 371 { 372 status_t result = packageEntry.InitCheck(); 373 if (result != B_OK) { 374 fListener->PrintError("entry not initialized!\n"); 375 return result; 376 } 377 378 BPath packagePath; 379 if ((result = packageEntry.GetPath(&packagePath)) != B_OK) { 380 fListener->PrintError("can't get path for entry!\n"); 381 return result; 382 } 383 384 BPackageReader packageReader(fListener); 385 if ((result = packageReader.Init(packagePath.Path())) != B_OK) { 386 fListener->PrintError("can't create package reader!\n"); 387 return result; 388 } 389 390 fPackageInfo.Clear(); 391 392 // parse package 393 PackageContentHandler contentHandler(fListener, &fPackageInfo, 394 packageReader.PackageFileFD(), fRepositoryInfo); 395 if ((result = packageReader.ParseContent(&contentHandler)) != B_OK) 396 return result; 397 398 // determine package's checksum 399 GeneralFileChecksumAccessor checksumAccessor(packageEntry); 400 BString checksum; 401 if ((result = checksumAccessor.GetChecksum(checksum)) != B_OK) { 402 fListener->PrintError("can't compute checksum!\n"); 403 return result; 404 } 405 fPackageInfo.SetChecksum(checksum); 406 407 // register package's attributes 408 if ((result = _RegisterCurrentPackageInfo()) != B_OK) 409 return result; 410 411 return B_OK; 412 } 413 414 415 status_t 416 RepositoryWriterImpl::_RegisterCurrentPackageInfo() 417 { 418 status_t result = fPackageInfo.InitCheck(); 419 if (result != B_OK) { 420 fListener->PrintError("package %s has incomplete package-info!\n", 421 fPackageInfo.Name().String()); 422 return result; 423 } 424 425 // reject package with a name that we've seen already 426 if (fPackageNames->Contains(fPackageInfo.Name())) { 427 fListener->PrintError("package %s has already been added!\n", 428 fPackageInfo.Name().String()); 429 return B_NAME_IN_USE; 430 } 431 432 // all packages must have the same vendor as the repository 433 const BString& expectedVendor = fRepositoryInfo->Vendor(); 434 if (fPackageInfo.Vendor().ICompare(expectedVendor) != 0) { 435 fListener->PrintError("package '%s' has unexpected vendor '%s' " 436 "(expected '%s')!\n", fPackageInfo.Name().String(), 437 fPackageInfo.Vendor().String(), expectedVendor.String()); 438 return B_BAD_DATA; 439 } 440 441 // all packages must have an architecture that's compatible with the one 442 // used by the repository 443 BPackageArchitecture expectedArchitecture = fRepositoryInfo->Architecture(); 444 if (fPackageInfo.Architecture() != expectedArchitecture 445 && fPackageInfo.Architecture() != B_PACKAGE_ARCHITECTURE_ANY) { 446 fListener->PrintError( 447 "package '%s' has non-matching architecture '%s' " 448 "(expected '%s' or '%s')!\n", fPackageInfo.Name().String(), 449 BPackageInfo::kArchitectureNames[fPackageInfo.Architecture()], 450 BPackageInfo::kArchitectureNames[expectedArchitecture], 451 BPackageInfo::kArchitectureNames[B_PACKAGE_ARCHITECTURE_ANY] 452 ); 453 return B_BAD_DATA; 454 } 455 456 if ((result = fPackageNames->Add(fPackageInfo.Name())) != B_OK) 457 return result; 458 459 RegisterPackageInfo(PackageAttributes(), fPackageInfo); 460 fPackageCount++; 461 fListener->OnPackageAdded(fPackageInfo); 462 463 return B_OK; 464 } 465 466 467 status_t 468 RepositoryWriterImpl::_WriteRepositoryInfo(hpkg_repo_header& header, 469 ssize_t& _infoLengthCompressed) 470 { 471 BMessage archive; 472 status_t result = fRepositoryInfo->Archive(&archive); 473 if (result != B_OK) { 474 fListener->PrintError("can't archive repository header!\n"); 475 return result; 476 } 477 478 ssize_t flattenedSize = archive.FlattenedSize(); 479 char buffer[flattenedSize]; 480 if ((result = archive.Flatten(buffer, flattenedSize)) != B_OK) { 481 fListener->PrintError("can't flatten repository header!\n"); 482 return result; 483 } 484 485 off_t startOffset = sizeof(hpkg_repo_header); 486 487 // write the package attributes (zlib writer on top of a file writer) 488 FDDataWriter realWriter(FD(), startOffset, fListener); 489 ZlibDataWriter zlibWriter(&realWriter); 490 SetDataWriter(&zlibWriter); 491 zlibWriter.Init(); 492 493 DataWriter()->WriteDataThrows(buffer, flattenedSize); 494 495 zlibWriter.Finish(); 496 SetDataWriter(NULL); 497 498 fListener->OnRepositoryInfoSectionDone(zlibWriter.BytesWritten()); 499 500 _infoLengthCompressed = realWriter.BytesWritten(); 501 502 // update the header 503 header.info_compression 504 = B_HOST_TO_BENDIAN_INT32(B_HPKG_COMPRESSION_ZLIB); 505 header.info_length_compressed 506 = B_HOST_TO_BENDIAN_INT32(_infoLengthCompressed); 507 header.info_length_uncompressed 508 = B_HOST_TO_BENDIAN_INT32(flattenedSize); 509 510 return B_OK; 511 } 512 513 514 off_t 515 RepositoryWriterImpl::_WritePackageAttributes(hpkg_repo_header& header, 516 off_t startOffset, ssize_t& _packagesLengthCompressed) 517 { 518 // write the package attributes (zlib writer on top of a file writer) 519 FDDataWriter realWriter(FD(), startOffset, fListener); 520 ZlibDataWriter zlibWriter(&realWriter); 521 SetDataWriter(&zlibWriter); 522 zlibWriter.Init(); 523 524 // write cached strings and package attributes tree 525 uint32 stringsLengthUncompressed; 526 uint32 stringsCount = WritePackageAttributes(PackageAttributes(), 527 stringsLengthUncompressed); 528 529 zlibWriter.Finish(); 530 off_t endOffset = realWriter.Offset(); 531 SetDataWriter(NULL); 532 533 fListener->OnPackageAttributesSectionDone(stringsCount, 534 zlibWriter.BytesWritten()); 535 536 _packagesLengthCompressed = endOffset - startOffset; 537 538 // update the header 539 header.packages_compression 540 = B_HOST_TO_BENDIAN_INT32(B_HPKG_COMPRESSION_ZLIB); 541 header.packages_length_compressed 542 = B_HOST_TO_BENDIAN_INT64(_packagesLengthCompressed); 543 header.packages_length_uncompressed 544 = B_HOST_TO_BENDIAN_INT64(zlibWriter.BytesWritten()); 545 header.packages_strings_count = B_HOST_TO_BENDIAN_INT64(stringsCount); 546 header.packages_strings_length 547 = B_HOST_TO_BENDIAN_INT64(stringsLengthUncompressed); 548 549 return endOffset; 550 } 551 552 553 } // namespace BPrivate 554 555 } // namespace BHPKG 556 557 } // namespace BPackageKit 558