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 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 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 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 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 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 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 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