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 { 50 PackageEntryDataFetcher(BErrorOutput* errorOutput, 51 BPackageData& packageData) 52 : 53 fErrorOutput(errorOutput), 54 fPackageData(packageData) 55 { 56 } 57 58 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 { 96 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 106 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 150 virtual status_t HandleEntryAttribute(BPackageEntry* entry, 151 BPackageEntryAttribute* attribute) 152 { 153 return B_OK; 154 } 155 156 virtual status_t HandleEntryDone(BPackageEntry* entry) 157 { 158 return B_OK; 159 } 160 161 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 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 198 RepositoryWriterImpl::~RepositoryWriterImpl() 199 { 200 delete fPackageNames; 201 } 202 203 204 status_t 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 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 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 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 265 RepositoryWriterImpl::_Init(const char* fileName) 266 { 267 return inherited::Init(fileName, sizeof(hpkg_repo_header), 268 BPackageWriterParameters()); 269 } 270 271 272 status_t 273 RepositoryWriterImpl::_Finish() 274 { 275 hpkg_repo_header header; 276 277 // write repository info 278 uint64 infoLength; 279 status_t result = _WriteRepositoryInfo(header, infoLength); 280 if (result != B_OK) 281 return result; 282 283 // write package attributes 284 uint64 packagesLength; 285 _WritePackageAttributes(header, packagesLength); 286 287 // flush the heap writer 288 result = fHeapWriter->Finish(); 289 if (result != B_OK) 290 return result; 291 uint64 compressedHeapSize = fHeapWriter->CompressedHeapSize(); 292 uint64 totalSize = fHeapWriter->HeapOffset() + compressedHeapSize; 293 294 header.heap_compression = B_HOST_TO_BENDIAN_INT16(B_HPKG_COMPRESSION_ZLIB); 295 header.heap_chunk_size = B_HOST_TO_BENDIAN_INT32(fHeapWriter->ChunkSize()); 296 header.heap_size_compressed = B_HOST_TO_BENDIAN_INT64(compressedHeapSize); 297 header.heap_size_uncompressed = B_HOST_TO_BENDIAN_INT64( 298 fHeapWriter->UncompressedHeapSize()); 299 300 fListener->OnRepositoryDone(sizeof(header), infoLength, 301 fRepositoryInfo->LicenseNames().CountStrings(), fPackageCount, 302 packagesLength, totalSize); 303 304 // update the general header info and write the header 305 header.magic = B_HOST_TO_BENDIAN_INT32(B_HPKG_REPO_MAGIC); 306 header.header_size = B_HOST_TO_BENDIAN_INT16((uint16)sizeof(header)); 307 header.version = B_HOST_TO_BENDIAN_INT16(B_HPKG_REPO_VERSION); 308 header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize); 309 header.minor_version = B_HOST_TO_BENDIAN_INT16(B_HPKG_REPO_MINOR_VERSION); 310 311 RawWriteBuffer(&header, sizeof(header), 0); 312 313 SetFinished(true); 314 return B_OK; 315 } 316 317 318 status_t 319 RepositoryWriterImpl::_AddPackage(const BEntry& packageEntry) 320 { 321 status_t result = packageEntry.InitCheck(); 322 if (result != B_OK) { 323 fListener->PrintError("entry not initialized!\n"); 324 return result; 325 } 326 327 BPath packagePath; 328 if ((result = packageEntry.GetPath(&packagePath)) != B_OK) { 329 fListener->PrintError("can't get path for entry '%s'!\n", 330 packageEntry.Name()); 331 return result; 332 } 333 334 BPackageReader packageReader(fListener); 335 if ((result = packageReader.Init(packagePath.Path())) != B_OK) { 336 fListener->PrintError("can't create package reader for '%s'!\n", 337 packagePath.Path()); 338 return result; 339 } 340 341 fPackageInfo.Clear(); 342 343 // parse package 344 PackageContentHandler contentHandler(fListener, &fPackageInfo, 345 packageReader.HeapReader(), fRepositoryInfo); 346 if ((result = packageReader.ParseContent(&contentHandler)) != B_OK) 347 return result; 348 349 // determine package's checksum 350 GeneralFileChecksumAccessor checksumAccessor(packageEntry); 351 BString checksum; 352 if ((result = checksumAccessor.GetChecksum(checksum)) != B_OK) { 353 fListener->PrintError("can't compute checksum of file '%s'!\n", 354 packagePath.Path()); 355 return result; 356 } 357 fPackageInfo.SetChecksum(checksum); 358 359 // register package's attributes 360 if ((result = _RegisterCurrentPackageInfo()) != B_OK) 361 return result; 362 363 return B_OK; 364 } 365 366 367 status_t 368 RepositoryWriterImpl::_AddPackageInfo(const BPackageInfo& packageInfo) 369 { 370 fPackageInfo = packageInfo; 371 372 // register package's attributes 373 status_t result = _RegisterCurrentPackageInfo(); 374 if (result != B_OK) 375 return result; 376 377 return B_OK; 378 } 379 380 381 status_t 382 RepositoryWriterImpl::_RegisterCurrentPackageInfo() 383 { 384 status_t result = fPackageInfo.InitCheck(); 385 if (result != B_OK) { 386 fListener->PrintError("package %s has incomplete package-info!\n", 387 fPackageInfo.Name().String()); 388 return result; 389 } 390 391 // reject package with a name that we've seen already 392 if (fPackageNames->Contains(fPackageInfo.Name())) { 393 fListener->PrintError("package %s has already been added!\n", 394 fPackageInfo.Name().String()); 395 return B_NAME_IN_USE; 396 } 397 398 // all packages must have the same vendor as the repository 399 const BString& expectedVendor = fRepositoryInfo->Vendor(); 400 if (fPackageInfo.Vendor().ICompare(expectedVendor) != 0) { 401 fListener->PrintError("package '%s' has unexpected vendor '%s' " 402 "(expected '%s')!\n", fPackageInfo.Name().String(), 403 fPackageInfo.Vendor().String(), expectedVendor.String()); 404 return B_BAD_DATA; 405 } 406 407 // all packages must have an architecture that's compatible with the one 408 // used by the repository 409 BPackageArchitecture expectedArchitecture = fRepositoryInfo->Architecture(); 410 if (fPackageInfo.Architecture() != expectedArchitecture 411 && fPackageInfo.Architecture() != B_PACKAGE_ARCHITECTURE_ANY 412 && fPackageInfo.Architecture() != B_PACKAGE_ARCHITECTURE_SOURCE) { 413 fListener->PrintError( 414 "package '%s' has non-matching architecture '%s' " 415 "(expected '%s', '%s', or '%s')!\n", fPackageInfo.Name().String(), 416 BPackageInfo::kArchitectureNames[fPackageInfo.Architecture()], 417 BPackageInfo::kArchitectureNames[expectedArchitecture], 418 BPackageInfo::kArchitectureNames[B_PACKAGE_ARCHITECTURE_ANY], 419 BPackageInfo::kArchitectureNames[B_PACKAGE_ARCHITECTURE_SOURCE]); 420 return B_BAD_DATA; 421 } 422 423 if ((result = fPackageNames->Add(fPackageInfo.Name())) != B_OK) 424 return result; 425 426 PackageAttribute* packageAttribute = AddStringAttribute( 427 B_HPKG_ATTRIBUTE_ID_PACKAGE, fPackageInfo.Name(), PackageAttributes()); 428 RegisterPackageInfo(packageAttribute->children, fPackageInfo); 429 fPackageCount++; 430 fListener->OnPackageAdded(fPackageInfo); 431 432 return B_OK; 433 } 434 435 436 status_t 437 RepositoryWriterImpl::_WriteRepositoryInfo(hpkg_repo_header& header, 438 uint64& _length) 439 { 440 // archive and flatten the repository info and write it 441 BMessage archive; 442 status_t result = fRepositoryInfo->Archive(&archive); 443 if (result != B_OK) { 444 fListener->PrintError("can't archive repository header!\n"); 445 return result; 446 } 447 448 ssize_t flattenedSize = archive.FlattenedSize(); 449 char buffer[flattenedSize]; 450 if ((result = archive.Flatten(buffer, flattenedSize)) != B_OK) { 451 fListener->PrintError("can't flatten repository header!\n"); 452 return result; 453 } 454 455 WriteBuffer(buffer, flattenedSize); 456 457 // notify listener 458 fListener->OnRepositoryInfoSectionDone(flattenedSize); 459 460 // update the header 461 header.info_length = B_HOST_TO_BENDIAN_INT32(flattenedSize); 462 463 _length = flattenedSize; 464 return B_OK; 465 } 466 467 468 void 469 RepositoryWriterImpl::_WritePackageAttributes(hpkg_repo_header& header, 470 uint64& _length) 471 { 472 // write the package attributes (zlib writer on top of a file writer) 473 uint64 startOffset = fHeapWriter->UncompressedHeapSize(); 474 475 uint32 stringsLength; 476 uint32 stringsCount = WritePackageAttributes(PackageAttributes(), 477 stringsLength); 478 479 uint64 sectionSize = fHeapWriter->UncompressedHeapSize() - startOffset; 480 481 fListener->OnPackageAttributesSectionDone(stringsCount, sectionSize); 482 483 // update the header 484 header.packages_length = B_HOST_TO_BENDIAN_INT64(sectionSize); 485 header.packages_strings_count = B_HOST_TO_BENDIAN_INT64(stringsCount); 486 header.packages_strings_length = B_HOST_TO_BENDIAN_INT64(stringsLength); 487 488 _length = sectionSize; 489 } 490 491 492 } // namespace BPrivate 493 494 } // namespace BHPKG 495 496 } // namespace BPackageKit 497