13810Skamg/* 212122Sdholmes * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de> 33810Skamg * Distributed under the terms of the MIT License. 43810Skamg */ 53810Skamg 63810Skamg 73810Skamg#include <package/hpkg/RepositoryWriterImpl.h> 83810Skamg 93810Skamg#include <algorithm> 103810Skamg#include <new> 113810Skamg 123810Skamg#include <ByteOrder.h> 133810Skamg#include <Message.h> 143810Skamg#include <Path.h> 153810Skamg 163810Skamg#include <AutoDeleter.h> 173810Skamg#include <HashSet.h> 183810Skamg 193810Skamg#include <package/hpkg/BlockBufferPoolNoLock.h> 203810Skamg#include <package/hpkg/HPKGDefsPrivate.h> 213810Skamg#include <package/hpkg/PackageDataReader.h> 223810Skamg#include <package/hpkg/PackageEntry.h> 233810Skamg#include <package/hpkg/PackageFileHeapWriter.h> 243810Skamg#include <package/hpkg/PackageInfoAttributeValue.h> 253810Skamg#include <package/hpkg/PackageReader.h> 263810Skamg#include <package/ChecksumAccessors.h> 273810Skamg#include <package/HashableString.h> 283810Skamg#include <package/PackageInfoContentHandler.h> 299306Srprotacio#include <package/RepositoryInfo.h> 303810Skamg 313810Skamg 323810Skamgnamespace BPackageKit { 339684Smgronlun 343810Skamgnamespace BHPKG { 353810Skamg 363810Skamgnamespace BPrivate { 373810Skamg 383810Skamg 393810Skamgusing BPackageKit::BPrivate::GeneralFileChecksumAccessor; 403810Skamgusing BPackageKit::BPrivate::HashableString; 413810Skamg 423810Skamg 433810Skamgnamespace { 443810Skamg 453810Skamg 463810Skamg// #pragma mark - PackageEntryDataFetcher 473810Skamg 483810Skamg 493810Skamgstruct PackageEntryDataFetcher { 503810Skamg PackageEntryDataFetcher(BErrorOutput* errorOutput, 513810Skamg BPackageData& packageData) 523810Skamg : 533810Skamg fErrorOutput(errorOutput), 543810Skamg fPackageData(packageData) 553810Skamg { 563810Skamg } 573810Skamg 583810Skamg status_t ReadIntoString(BAbstractBufferedDataReader* heapReader, 593810Skamg BString& _contents) 603810Skamg { 613810Skamg // create a PackageDataReader 623810Skamg BAbstractBufferedDataReader* reader; 633810Skamg status_t result = BPackageDataReaderFactory() 643810Skamg .CreatePackageDataReader(heapReader, fPackageData, reader); 653810Skamg if (result != B_OK) 663810Skamg return result; 673810Skamg ObjectDeleter<BAbstractBufferedDataReader> readerDeleter(reader); 683810Skamg 693810Skamg // copy data into the given string 703810Skamg int32 bufferSize = fPackageData.Size(); 713810Skamg char* buffer = _contents.LockBuffer(bufferSize); 723810Skamg if (buffer == NULL) 733810Skamg return B_NO_MEMORY; 743810Skamg 753810Skamg result = reader->ReadData(0, buffer, bufferSize); 763810Skamg if (result != B_OK) { 773810Skamg fErrorOutput->PrintError("Error: Failed to read data: %s\n", 783810Skamg strerror(result)); 793810Skamg _contents.UnlockBuffer(0); 803810Skamg } else 813810Skamg _contents.UnlockBuffer(bufferSize); 823810Skamg 833810Skamg return result; 843810Skamg } 853810Skamg 863810Skamgprivate: 873810Skamg BErrorOutput* fErrorOutput; 883810Skamg BPackageData& fPackageData; 893810Skamg}; 903810Skamg 913810Skamg 923810Skamg// #pragma mark - PackageContentHandler 933810Skamg 943810Skamg 953810Skamgstruct PackageContentHandler : public BPackageInfoContentHandler { 963810Skamg PackageContentHandler(BErrorOutput* errorOutput, BPackageInfo* packageInfo, 973810Skamg BAbstractBufferedDataReader* heapReader, 983810Skamg BRepositoryInfo* repositoryInfo) 993810Skamg : 1003810Skamg BPackageInfoContentHandler(*packageInfo, errorOutput), 1013810Skamg fHeapReader(heapReader), 1023810Skamg fRepositoryInfo(repositoryInfo) 1033810Skamg { 1043810Skamg } 1053810Skamg 1063810Skamg virtual status_t HandleEntry(BPackageEntry* entry) 1073810Skamg { 1083810Skamg // if license must be approved, read any license files from package such 1093810Skamg // that those can be stored in the repository later 1103810Skamg if ((fPackageInfo.Flags() & B_PACKAGE_FLAG_APPROVE_LICENSE) == 0 1113810Skamg || entry == NULL) 1123810Skamg return B_OK; 1133810Skamg 1143810Skamg // return if not in ./data/licenses folder 1153810Skamg const BPackageEntry* parent = entry->Parent(); 1163810Skamg BString licenseFolderName("licenses"); 1173810Skamg if (parent == NULL || licenseFolderName != parent->Name()) 1183810Skamg return B_OK; 1193810Skamg 1203810Skamg parent = parent->Parent(); 1213810Skamg BString dataFolderName("data"); 1223810Skamg if (parent == NULL || dataFolderName != parent->Name()) 1233810Skamg return B_OK; 1243810Skamg 1253810Skamg if (parent->Parent() != NULL) 1263810Skamg return B_OK; 1273810Skamg 1283810Skamg // check if license already is in repository 1293810Skamg const BStringList& licenseNames = fRepositoryInfo->LicenseNames(); 1303810Skamg for (int i = 0; i < licenseNames.CountStrings(); ++i) { 1313810Skamg if (licenseNames.StringAt(i).ICompare(entry->Name()) == 0) { 1323810Skamg // license already exists 1333810Skamg return B_OK; 1343810Skamg } 1353810Skamg } 1363810Skamg 1373810Skamg // fetch contents of license file 1383810Skamg BPackageData& packageData = entry->Data(); 1393810Skamg PackageEntryDataFetcher dataFetcher(fErrorOutput, packageData); 1403810Skamg 1413810Skamg BString licenseText; 1423810Skamg status_t result = dataFetcher.ReadIntoString(fHeapReader, licenseText); 1433810Skamg if (result != B_OK) 1443810Skamg return result; 1453810Skamg 1463810Skamg // add license to repository 1473810Skamg return fRepositoryInfo->AddLicense(entry->Name(), licenseText); 1483810Skamg } 1493810Skamg 1503810Skamg virtual status_t HandleEntryAttribute(BPackageEntry* entry, 1513810Skamg BPackageEntryAttribute* attribute) 1523810Skamg { 1533810Skamg return B_OK; 1543810Skamg } 1553810Skamg 1563810Skamg virtual status_t HandleEntryDone(BPackageEntry* entry) 1573810Skamg { 1583810Skamg return B_OK; 1593810Skamg } 1603810Skamg 1613810Skamg virtual void HandleErrorOccurred() 1623810Skamg { 1633810Skamg } 1643810Skamg 1653810Skamgprivate: 1663810Skamg BPackageReader* fPackageReader; 1673810Skamg BAbstractBufferedDataReader* fHeapReader; 1683810Skamg BRepositoryInfo* fRepositoryInfo; 1693810Skamg}; 1703810Skamg 1713810Skamg 1725645Sacorn} // anonymous namespace 1735645Sacorn 1745645Sacorn 1755645Sacorn// #pragma mark - PackageNameSet 1763810Skamg 1775645Sacorn 1783810Skamgstruct RepositoryWriterImpl::PackageNameSet 1793810Skamg : public ::BPrivate::HashSet<HashableString> { 1803810Skamg}; 1813810Skamg 1823810Skamg 1833810Skamg// #pragma mark - RepositoryWriterImpl 1843810Skamg 1853810Skamg 1863810SkamgRepositoryWriterImpl::RepositoryWriterImpl(BRepositoryWriterListener* listener, 1873810Skamg BRepositoryInfo* repositoryInfo) 1883810Skamg : 1893810Skamg inherited("repository", listener), 1903810Skamg fListener(listener), 1913810Skamg fRepositoryInfo(repositoryInfo), 1923810Skamg fPackageCount(0), 1933810Skamg fPackageNames(NULL) 1943810Skamg{ 1953810Skamg} 1963810Skamg 1973810Skamg 1983810SkamgRepositoryWriterImpl::~RepositoryWriterImpl() 1993810Skamg{ 2003810Skamg delete fPackageNames; 2013810Skamg} 2023810Skamg 2033810Skamg 2043810Skamgstatus_t 2053810SkamgRepositoryWriterImpl::Init(const char* fileName) 2063810Skamg{ 2073810Skamg try { 2083810Skamg fPackageNames = new PackageNameSet(); 2093810Skamg status_t result = fPackageNames->InitCheck(); 2103810Skamg if (result != B_OK) 2113810Skamg return result; 2123810Skamg return _Init(fileName); 2133810Skamg } catch (status_t error) { 2143810Skamg return error; 2153810Skamg } catch (std::bad_alloc&) { 2163810Skamg fListener->PrintError("Out of memory!\n"); 2173810Skamg return B_NO_MEMORY; 2183810Skamg } 2193810Skamg} 2203810Skamg 2213810Skamg 2223810Skamgstatus_t 2233810SkamgRepositoryWriterImpl::AddPackage(const BEntry& packageEntry) 2243810Skamg{ 2253810Skamg try { 2263810Skamg return _AddPackage(packageEntry); 2273810Skamg } catch (status_t error) { 2283810Skamg return error; 2293810Skamg } catch (std::bad_alloc&) { 2303810Skamg fListener->PrintError("Out of memory!\n"); 2313810Skamg return B_NO_MEMORY; 2323810Skamg } 2333810Skamg} 2343810Skamg 2353810Skamg 2363810Skamgstatus_t 2373810SkamgRepositoryWriterImpl::AddPackageInfo(const BPackageInfo& packageInfo) 2383810Skamg{ 2393810Skamg try { 2403810Skamg return _AddPackageInfo(packageInfo); 2413810Skamg } catch (status_t error) { 2423810Skamg return error; 2433810Skamg } catch (std::bad_alloc&) { 2443810Skamg fListener->PrintError("Out of memory!\n"); 2453810Skamg return B_NO_MEMORY; 2463810Skamg } 2473810Skamg} 2489306Srprotacio 2499306Srprotacio 2503810Skamgstatus_t 2513810SkamgRepositoryWriterImpl::Finish() 2523810Skamg{ 2539306Srprotacio try { 2549306Srprotacio return _Finish(); 2553810Skamg } catch (status_t error) { 2563810Skamg return error; 2573810Skamg } catch (std::bad_alloc&) { 2583810Skamg fListener->PrintError("Out of memory!\n"); 2593810Skamg return B_NO_MEMORY; 2609306Srprotacio } 2619306Srprotacio} 2623810Skamg 2633810Skamg 2643810Skamgstatus_t 2653810SkamgRepositoryWriterImpl::_Init(const char* fileName) 2663810Skamg{ 2673810Skamg status_t error = inherited::Init(NULL, false, fileName, 2683810Skamg BPackageWriterParameters()); 2693810Skamg if (error != B_OK) 2703810Skamg return error; 2713810Skamg 2723810Skamg return InitHeapReader(sizeof(hpkg_repo_header)); 2733810Skamg} 2743810Skamg 2753810Skamg 2763810Skamgstatus_t 2773810SkamgRepositoryWriterImpl::_Finish() 2783810Skamg{ 2793810Skamg hpkg_repo_header header; 2803810Skamg 2813810Skamg // write repository info 2823810Skamg uint64 infoLength; 2833810Skamg status_t result = _WriteRepositoryInfo(header, infoLength); 2843810Skamg if (result != B_OK) 2853810Skamg return result; 2863810Skamg 2873810Skamg // write package attributes 2883810Skamg uint64 packagesLength; 2893810Skamg _WritePackageAttributes(header, packagesLength); 2903810Skamg 2913810Skamg // flush the heap writer 2923810Skamg result = fHeapWriter->Finish(); 2933810Skamg if (result != B_OK) 2943810Skamg return result; 2953810Skamg uint64 compressedHeapSize = fHeapWriter->CompressedHeapSize(); 2963810Skamg uint64 totalSize = fHeapWriter->HeapOffset() + compressedHeapSize; 2973810Skamg 2983810Skamg header.heap_compression = B_HOST_TO_BENDIAN_INT16( 2993810Skamg Parameters().Compression()); 3003810Skamg header.heap_chunk_size = B_HOST_TO_BENDIAN_INT32(fHeapWriter->ChunkSize()); 3013810Skamg header.heap_size_compressed = B_HOST_TO_BENDIAN_INT64(compressedHeapSize); 3023810Skamg header.heap_size_uncompressed = B_HOST_TO_BENDIAN_INT64( 3033810Skamg fHeapWriter->UncompressedHeapSize()); 3043810Skamg 3053810Skamg fListener->OnRepositoryDone(sizeof(header), infoLength, 3063810Skamg fRepositoryInfo->LicenseNames().CountStrings(), fPackageCount, 3073810Skamg packagesLength, totalSize); 3083810Skamg 3093810Skamg // update the general header info and write the header 3103810Skamg header.magic = B_HOST_TO_BENDIAN_INT32(B_HPKG_REPO_MAGIC); 3113810Skamg header.header_size = B_HOST_TO_BENDIAN_INT16((uint16)sizeof(header)); 3123810Skamg header.version = B_HOST_TO_BENDIAN_INT16(B_HPKG_REPO_VERSION); 3133810Skamg header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize); 3143810Skamg header.minor_version = B_HOST_TO_BENDIAN_INT16(B_HPKG_REPO_MINOR_VERSION); 3154942Sacorn 3163810Skamg RawWriteBuffer(&header, sizeof(header), 0); 3174942Sacorn 3183810Skamg SetFinished(true); 3193810Skamg return B_OK; 3203810Skamg} 3214942Sacorn 3224942Sacorn 3233810Skamgstatus_t 3243810SkamgRepositoryWriterImpl::_AddPackage(const BEntry& packageEntry) 3253810Skamg{ 3263810Skamg status_t result = packageEntry.InitCheck(); 3273810Skamg if (result != B_OK) { 3283810Skamg fListener->PrintError("entry not initialized!\n"); 3293810Skamg return result; 3303810Skamg } 3315351Sacorn 3323810Skamg BPath packagePath; 3333810Skamg if ((result = packageEntry.GetPath(&packagePath)) != B_OK) { 3343810Skamg fListener->PrintError("can't get path for entry '%s'!\n", 3353810Skamg packageEntry.Name()); 3363810Skamg return result; 3373810Skamg } 3383810Skamg 3393810Skamg BPackageReader packageReader(fListener); 3403810Skamg if ((result = packageReader.Init(packagePath.Path())) != B_OK) { 3413810Skamg fListener->PrintError("can't create package reader for '%s'!\n", 3423810Skamg packagePath.Path()); 3433810Skamg return result; 3443810Skamg } 3453810Skamg 3464343Smorris fPackageInfo.Clear(); 3473810Skamg 3483810Skamg // parse package 3493810Skamg PackageContentHandler contentHandler(fListener, &fPackageInfo, 3503810Skamg packageReader.HeapReader(), fRepositoryInfo); 3515774Shseigel if ((result = packageReader.ParseContent(&contentHandler)) != B_OK) 3523810Skamg return result; 3533810Skamg 3543810Skamg // determine package's checksum 3553810Skamg GeneralFileChecksumAccessor checksumAccessor(packageEntry); 3564942Sacorn BString checksum; 3575351Sacorn if ((result = checksumAccessor.GetChecksum(checksum)) != B_OK) { 3583810Skamg fListener->PrintError("can't compute checksum of file '%s'!\n", 3593810Skamg packagePath.Path()); 3603810Skamg return result; 3613810Skamg } 3623810Skamg fPackageInfo.SetChecksum(checksum); 3633810Skamg 3643810Skamg // register package's attributes 3653810Skamg if ((result = _RegisterCurrentPackageInfo()) != B_OK) 3663810Skamg return result; 3673810Skamg 3683810Skamg return B_OK; 3693810Skamg} 3703810Skamg 3713810Skamg 3723810Skamgstatus_t 3733810SkamgRepositoryWriterImpl::_AddPackageInfo(const BPackageInfo& packageInfo) 3743810Skamg{ 3753810Skamg fPackageInfo = packageInfo; 3763810Skamg 3773810Skamg // register package's attributes 3783810Skamg status_t result = _RegisterCurrentPackageInfo(); 3793810Skamg if (result != B_OK) 3803810Skamg return result; 3813810Skamg 3823810Skamg return B_OK; 3833810Skamg} 3843810Skamg 3853810Skamg 3863810Skamgstatus_t 3873810SkamgRepositoryWriterImpl::_RegisterCurrentPackageInfo() 3883810Skamg{ 3893810Skamg status_t result = fPackageInfo.InitCheck(); 3905351Sacorn if (result != B_OK) { 3913810Skamg fListener->PrintError("package %s has incomplete package-info!\n", 3923810Skamg fPackageInfo.Name().String()); 3933810Skamg return result; 3943810Skamg } 3953810Skamg 3963810Skamg // reject package with a name that we've seen already 3973810Skamg if (fPackageNames->Contains(fPackageInfo.Name())) { 3985645Sacorn fListener->PrintError("package %s has already been added!\n", 3995645Sacorn fPackageInfo.Name().String()); 4003810Skamg return B_NAME_IN_USE; 4015625Sacorn } 4025625Sacorn 4035645Sacorn // all packages must have the same vendor as the repository 4043810Skamg const BString& expectedVendor = fRepositoryInfo->Vendor(); 4053810Skamg if (fPackageInfo.Vendor().ICompare(expectedVendor) != 0) { 4063810Skamg fListener->PrintError("package '%s' has unexpected vendor '%s' " 4073810Skamg "(expected '%s')!\n", fPackageInfo.Name().String(), 4085645Sacorn fPackageInfo.Vendor().String(), expectedVendor.String()); 4095625Sacorn return B_BAD_DATA; 4105625Sacorn } 4115645Sacorn 4125645Sacorn // all packages must have an architecture that's compatible with the one 4135625Sacorn // used by the repository 4143810Skamg BPackageArchitecture expectedArchitecture = fRepositoryInfo->Architecture(); 4153810Skamg if (fPackageInfo.Architecture() != expectedArchitecture 4163810Skamg && fPackageInfo.Architecture() != B_PACKAGE_ARCHITECTURE_ANY 4175774Shseigel && fPackageInfo.Architecture() != B_PACKAGE_ARCHITECTURE_SOURCE) { 4185804Shseigel fListener->PrintError( 4195804Shseigel "package '%s' has non-matching architecture '%s' " 4205804Shseigel "(expected '%s', '%s', or '%s')!\n", fPackageInfo.Name().String(), 4216288Slfoltan BPackageInfo::kArchitectureNames[fPackageInfo.Architecture()], 4226288Slfoltan BPackageInfo::kArchitectureNames[expectedArchitecture], 4236288Slfoltan BPackageInfo::kArchitectureNames[B_PACKAGE_ARCHITECTURE_ANY], 4246288Slfoltan BPackageInfo::kArchitectureNames[B_PACKAGE_ARCHITECTURE_SOURCE]); 4256288Slfoltan return B_BAD_DATA; 4265774Shseigel } 4276288Slfoltan 4285804Shseigel if ((result = fPackageNames->Add(fPackageInfo.Name())) != B_OK) 4295645Sacorn return result; 4305625Sacorn 4315625Sacorn PackageAttribute* packageAttribute = AddStringAttribute( 4325804Shseigel B_HPKG_ATTRIBUTE_ID_PACKAGE, fPackageInfo.Name(), PackageAttributes()); 4336288Slfoltan RegisterPackageInfo(packageAttribute->children, fPackageInfo); 4345804Shseigel fPackageCount++; 4355804Shseigel fListener->OnPackageAdded(fPackageInfo); 4369306Srprotacio 4379306Srprotacio return B_OK; 43810756Sstefank} 4399306Srprotacio 4409306Srprotacio 4415413Sacornstatus_t 4423810SkamgRepositoryWriterImpl::_WriteRepositoryInfo(hpkg_repo_header& header, 4433810Skamg uint64& _length) 4443810Skamg{ 4453810Skamg // archive and flatten the repository info and write it 4463810Skamg BMessage archive; 4473810Skamg status_t result = fRepositoryInfo->Archive(&archive); 4483810Skamg if (result != B_OK) { 4493810Skamg fListener->PrintError("can't archive repository header!\n"); 4503810Skamg return result; 4513810Skamg } 4523810Skamg 4533810Skamg ssize_t flattenedSize = archive.FlattenedSize(); 4543810Skamg char buffer[flattenedSize]; 4553810Skamg if ((result = archive.Flatten(buffer, flattenedSize)) != B_OK) { 4563810Skamg fListener->PrintError("can't flatten repository header!\n"); 4573810Skamg return result; 4583810Skamg } 4595246Sacorn 4605246Sacorn WriteBuffer(buffer, flattenedSize); 4619306Srprotacio 4625246Sacorn // notify listener 4636412Sdrchase fListener->OnRepositoryInfoSectionDone(flattenedSize); 4643810Skamg 4653810Skamg // update the header 4663810Skamg header.info_length = B_HOST_TO_BENDIAN_INT32(flattenedSize); 4673810Skamg 4685351Sacorn _length = flattenedSize; 4693810Skamg return B_OK; 4705351Sacorn} 4713810Skamg 4723810Skamg 4733810Skamgvoid 4743810SkamgRepositoryWriterImpl::_WritePackageAttributes(hpkg_repo_header& header, 4757421Sstefank uint64& _length) 4763810Skamg{ 4773810Skamg // write the package attributes (zlib writer on top of a file writer) 4785774Shseigel uint64 startOffset = fHeapWriter->UncompressedHeapSize(); 4795774Shseigel 4805774Shseigel uint32 stringsLength; 4815774Shseigel uint32 stringsCount = WritePackageAttributes(PackageAttributes(), 4825774Shseigel stringsLength); 4835774Shseigel 4845774Shseigel uint64 sectionSize = fHeapWriter->UncompressedHeapSize() - startOffset; 4855774Shseigel 4865774Shseigel fListener->OnPackageAttributesSectionDone(stringsCount, sectionSize); 4875774Shseigel 4887418Sstefank // update the header 4895774Shseigel header.packages_length = B_HOST_TO_BENDIAN_INT64(sectionSize); 4905774Shseigel header.packages_strings_count = B_HOST_TO_BENDIAN_INT64(stringsCount); 4913810Skamg header.packages_strings_length = B_HOST_TO_BENDIAN_INT64(stringsLength); 4923810Skamg 4933810Skamg _length = sectionSize; 4943810Skamg} 4953810Skamg 4963810Skamg 4973810Skamg} // namespace BPrivate 4983810Skamg 4993810Skamg} // namespace BHPKG 5003810Skamg 5013810Skamg} // namespace BPackageKit 5023810Skamg