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