X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/4e3c5633b1e74b4f58b95f339cfbbf4cbf21ab3e..421807e1d38c58b776be0b20faed94c5316d38eb:/apt-pkg/acquire-item.cc diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc index 82ccaff64..2180a5a41 100644 --- a/apt-pkg/acquire-item.cc +++ b/apt-pkg/acquire-item.cc @@ -45,13 +45,14 @@ #include #include #include +#include #include /*}}}*/ using namespace std; -static void printHashSumComparision(std::string const &URI, HashStringList const &Expected, HashStringList const &Actual) /*{{{*/ +static void printHashSumComparison(std::string const &URI, HashStringList const &Expected, HashStringList const &Actual) /*{{{*/ { if (_config->FindB("Debug::Acquire::HashSumMismatch", false) == false) return; @@ -85,32 +86,16 @@ static std::string GetKeepCompressedFileName(std::string file, IndexTarget const if (Target.KeepCompressed == false) return file; - std::string const CompressionTypes = Target.Option(IndexTarget::COMPRESSIONTYPES); - if (CompressionTypes.empty() == false) + std::string const KeepCompressedAs = Target.Option(IndexTarget::KEEPCOMPRESSEDAS); + if (KeepCompressedAs.empty() == false) { - std::string const ext = CompressionTypes.substr(0, CompressionTypes.find(' ')); + std::string const ext = KeepCompressedAs.substr(0, KeepCompressedAs.find(' ')); if (ext != "uncompressed") file.append(".").append(ext); } return file; } /*}}}*/ -static std::string GetCompressedFileName(IndexTarget const &Target, std::string const &Name, std::string const &Ext) /*{{{*/ -{ - if (Ext.empty() || Ext == "uncompressed") - return Name; - - // do not reverify cdrom sources as apt-cdrom may rewrite the Packages - // file when its doing the indexcopy - if (Target.URI.substr(0,6) == "cdrom:") - return Name; - - // adjust DestFile if its compressed on disk - if (Target.KeepCompressed == true) - return Name + '.' + Ext; - return Name; -} - /*}}}*/ static std::string GetMergeDiffsPatchFileName(std::string const &Final, std::string const &Patch)/*{{{*/ { // rred expects the patch as $FinalFile.ed.$patchname.gz @@ -136,6 +121,16 @@ static std::string GetExistingFilename(std::string const &File) /*{{{*/ return ""; } /*}}}*/ +static std::string GetDiffIndexFileName(std::string const &Name) /*{{{*/ +{ + return Name + ".diff/Index"; +} + /*}}}*/ +static std::string GetDiffIndexURI(IndexTarget const &Target) /*{{{*/ +{ + return Target.URI + ".diff/Index"; +} + /*}}}*/ static bool MessageInsecureRepository(bool const isError, std::string const &msg)/*{{{*/ { @@ -221,12 +216,12 @@ HashStringList pkgAcqMetaBase::GetExpectedHashes() const APT_CONST bool pkgAcqIndexDiffs::HashesRequired() const { - /* We don't always have the diff of the downloaded pdiff file. - What we have for sure is hashes for the uncompressed file, - but rred uncompresses them on the fly while parsing, so not handled here. - Hashes are (also) checked while searching for (next) patch to apply. */ + /* We can't check hashes of rred result as we don't know what the + hash of the file will be. We just know the hash of the patch(es), + the hash of the file they will apply on and the hash of the resulting + file. */ if (State == StateFetchDiff) - return available_patches[0].download_hashes.empty() == false; + return true; return false; } HashStringList pkgAcqIndexDiffs::GetExpectedHashes() const @@ -242,7 +237,7 @@ APT_CONST bool pkgAcqIndexMergeDiffs::HashesRequired() const we can check the rred result after all patches are applied as we know the expected result rather than potentially apply more patches */ if (State == StateFetchDiff) - return patch.download_hashes.empty() == false; + return true; return State == StateApplyDiff; } HashStringList pkgAcqIndexMergeDiffs::GetExpectedHashes() const @@ -316,17 +311,17 @@ bool pkgAcqDiffIndex::QueueURI(pkgAcquire::ItemDesc &Item) // Acquire::Item::GetFinalFilename and specialisations for child classes /*{{{*/ std::string pkgAcquire::Item::GetFinalFilename() const { + // Beware: Desc.URI is modified by redirections return GetFinalFileNameFromURI(Desc.URI); } std::string pkgAcqDiffIndex::GetFinalFilename() const { - // the logic we inherent from pkgAcqBaseIndex isn't what we need here - return pkgAcquire::Item::GetFinalFilename(); + return GetFinalFileNameFromURI(GetDiffIndexURI(Target)); } std::string pkgAcqIndex::GetFinalFilename() const { std::string const FinalFile = GetFinalFileNameFromURI(Target.URI); - return GetCompressedFileName(Target, FinalFile, CurrentCompressionExtension); + return GetKeepCompressedFileName(FinalFile, Target); } std::string pkgAcqMetaSig::GetFinalFilename() const { @@ -358,7 +353,7 @@ std::string pkgAcqIndex::GetMetaKey() const } std::string pkgAcqDiffIndex::GetMetaKey() const { - return Target.MetaKey + ".diff/Index"; + return GetDiffIndexFileName(Target.MetaKey); } /*}}}*/ //pkgAcqTransactionItem::TransactionState and specialisations for child classes /*{{{*/ @@ -379,7 +374,30 @@ bool pkgAcqTransactionItem::TransactionState(TransactionStates const state) case TransactionCommit: if(PartialFile.empty() == false) { - if (PartialFile != DestFile) + bool sameFile = (PartialFile == DestFile); + // we use symlinks on IMS-Hit to avoid copies + if (RealFileExists(DestFile)) + { + struct stat Buf; + if (lstat(PartialFile.c_str(), &Buf) != -1) + { + if (S_ISLNK(Buf.st_mode) && Buf.st_size > 0) + { + char partial[Buf.st_size + 1]; + ssize_t const sp = readlink(PartialFile.c_str(), partial, Buf.st_size); + if (sp == -1) + _error->Errno("pkgAcqTransactionItem::TransactionState-sp", _("Failed to readlink %s"), PartialFile.c_str()); + else + { + partial[sp] = '\0'; + sameFile = (DestFile == partial); + } + } + } + else + _error->Errno("pkgAcqTransactionItem::TransactionState-stat", _("Failed to stat %s"), PartialFile.c_str()); + } + if (sameFile == false) { // ensure that even without lists-cleanup all compressions are nuked std::string FinalFile = GetFinalFileNameFromURI(Target.URI); @@ -1158,14 +1176,14 @@ void pkgAcqMetaBase::QueueIndexes(bool const verify) /*{{{*/ if (filename.empty() == false) { new NoActionItem(Owner, *Target, filename); - std::string const idxfilename = GetFinalFileNameFromURI(Target->URI + ".diff/Index"); + std::string const idxfilename = GetFinalFileNameFromURI(GetDiffIndexURI(*Target)); if (FileExists(idxfilename)) new NoActionItem(Owner, *Target, idxfilename); continue; } // check if we have patches available - trypdiff &= TransactionManager->MetaIndexParser->Exists(Target->MetaKey + ".diff/Index"); + trypdiff &= TransactionManager->MetaIndexParser->Exists(GetDiffIndexFileName(Target->MetaKey)); } else { @@ -1720,9 +1738,9 @@ pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire * const Owner, Debug = _config->FindB("Debug::pkgAcquire::Diffs",false); Desc.Owner = this; - Desc.Description = Target.Description + ".diff/Index"; + Desc.Description = GetDiffIndexFileName(Target.Description); Desc.ShortDesc = Target.ShortDesc; - Desc.URI = Target.URI + ".diff/Index"; + Desc.URI = GetDiffIndexURI(Target); DestFile = GetPartialFileNameFromURI(Desc.URI); @@ -1813,7 +1831,7 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile) /*{{{*/ if (Debug == true) { std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Index has different hashes than parser, probably older, so fail pdiffing" << std::endl; - printHashSumComparision(CurrentPackagesFile, ServerHashes, TargetFileHashes); + printHashSumComparison(CurrentPackagesFile, ServerHashes, TargetFileHashes); } return false; } @@ -2014,6 +2032,17 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile) /*{{{*/ return false; } + for (auto const &patch: available_patches) + if (patch.result_hashes.usable() == false || + patch.patch_hashes.usable() == false || + patch.download_hashes.usable() == false) + { + if (Debug) + std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": provides no usable hashes for " << patch.file + << " so fallback to complete download" << std::endl; + return false; + } + // patching with too many files is rather slow compared to a fast download unsigned long const fileLimit = _config->FindI("Acquire::PDiffs::FileLimit", 0); if (fileLimit != 0 && fileLimit < available_patches.size()) @@ -2025,18 +2054,37 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile) /*{{{*/ } // calculate the size of all patches we have to get - // note that all sizes are uncompressed, while we download compressed files - unsigned long long patchesSize = 0; - for (std::vector::const_iterator cur = available_patches.begin(); - cur != available_patches.end(); ++cur) - patchesSize += cur->patch_hashes.FileSize(); - unsigned long long const sizeLimit = ServerSize * _config->FindI("Acquire::PDiffs::SizeLimit", 100); - if (sizeLimit > 0 && (sizeLimit/100) < patchesSize) - { - if (Debug) - std::clog << "Need " << patchesSize << " bytes (Limit is " << sizeLimit/100 - << ") so fallback to complete download" << std::endl; - return false; + unsigned short const sizeLimitPercent = _config->FindI("Acquire::PDiffs::SizeLimit", 100); + if (sizeLimitPercent > 0 && TransactionManager->MetaIndexParser != nullptr) + { + unsigned long long downloadSize = std::accumulate(available_patches.begin(), + available_patches.end(), 0llu, [](unsigned long long const T, DiffInfo const &I) { + return T + I.download_hashes.FileSize(); + }); + if (downloadSize != 0) + { + unsigned long long downloadSizeIdx = 0; + auto const types = VectorizeString(Target.Option(IndexTarget::COMPRESSIONTYPES), ' '); + for (auto const &t : types) + { + std::string MetaKey = Target.MetaKey; + if (t != "uncompressed") + MetaKey += '.' + t; + HashStringList const hsl = GetExpectedHashesFor(MetaKey); + if (unlikely(hsl.usable() == false)) + continue; + downloadSizeIdx = hsl.FileSize(); + break; + } + unsigned long long const sizeLimit = downloadSizeIdx * sizeLimitPercent; + if ((sizeLimit/100) < downloadSize) + { + if (Debug) + std::clog << "Need " << downloadSize << " compressed bytes (Limit is " << (sizeLimit/100) << ", " + << "original is " << downloadSizeIdx << ") so fallback to complete download" << std::endl; + return false; + } + } } // we have something, queue the diffs @@ -2059,22 +2107,35 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile) /*{{{*/ // clean the plate { + std::string const Final = GetExistingFilename(CurrentPackagesFile); + if (unlikely(Final.empty())) // because we wouldn't be called in such a case + return false; std::string const PartialFile = GetPartialFileNameFromURI(Target.URI); - std::vector exts = APT::Configuration::getCompressorExtensions(); - for (auto const &ext : exts) + if (FileExists(PartialFile) && RemoveFile("Bootstrap-linking", PartialFile) == false) + { + if (Debug) + std::clog << "Bootstrap-linking for patching " << CurrentPackagesFile + << " by removing stale " << PartialFile << " failed!" << std::endl; + return false; + } + for (auto const &ext : APT::Configuration::getCompressorExtensions()) { std::string const Partial = PartialFile + ext; - if (FileExists(Partial)) - RemoveFile("PDiffs-Bootstrap", Partial); + if (FileExists(Partial) && RemoveFile("Bootstrap-linking", Partial) == false) + { + if (Debug) + std::clog << "Bootstrap-linking for patching " << CurrentPackagesFile + << " by removing stale " << Partial << " failed!" << std::endl; + return false; + } } - std::string const Final = GetExistingFilename(CurrentPackagesFile); - if (unlikely(Final.empty())) // because we wouldn't be called in such a case - return false; std::string const Ext = Final.substr(CurrentPackagesFile.length()); std::string const Partial = PartialFile + Ext; if (symlink(Final.c_str(), Partial.c_str()) != 0) { - std::clog << "Bootstrap-linking for patching " << CurrentPackagesFile << " by linking " << Final << " to " << Partial << " failed!" << std::endl; + if (Debug) + std::clog << "Bootstrap-linking for patching " << CurrentPackagesFile + << " by linking " << Final << " to " << Partial << " failed!" << std::endl; return false; } } @@ -2393,16 +2454,18 @@ void pkgAcqIndexMergeDiffs::Failed(string const &Message,pkgAcquire::MethodConfi for (std::vector::const_iterator I = allPatches->begin(); I != allPatches->end(); ++I) if ((*I)->State == StateErrorDiff) + { + State = StateErrorDiff; return; + } // first failure means we should fallback State = StateErrorDiff; if (Debug) std::clog << "Falling back to normal index file acquire" << std::endl; RenameOnError(PDiffError); - std::string const patchname = GetPartialFileNameFromURI(Desc.URI); - if (RealFileExists(patchname)) - Rename(patchname, patchname + ".FAILED"); + if (RealFileExists(DestFile)) + Rename(DestFile, DestFile + ".FAILED"); std::string const UnpatchedFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI)); if (UnpatchedFile.empty() == false && FileExists(UnpatchedFile)) Rename(UnpatchedFile, UnpatchedFile + ".FAILED"); @@ -2418,8 +2481,23 @@ void pkgAcqIndexMergeDiffs::Done(string const &Message, HashStringList const &Ha Item::Done(Message, Hashes, Cnf); + if (std::any_of(allPatches->begin(), allPatches->end(), + [](pkgAcqIndexMergeDiffs const * const P) { return P->State == StateErrorDiff; })) + { + if(Debug) + std::clog << "Another patch failed already, no point in processing this one." << std::endl; + State = StateErrorDiff; + return; + } + std::string const UncompressedUnpatchedFile = GetPartialFileNameFromURI(Target.URI); std::string const UnpatchedFile = GetExistingFilename(UncompressedUnpatchedFile); + if (UnpatchedFile.empty()) + { + _error->Fatal("Unpatched file %s doesn't exist (anymore)!", UncompressedUnpatchedFile.c_str()); + State = StateErrorDiff; + return; + } std::string const PatchFile = GetMergeDiffsPatchFileName(UnpatchedFile, patch.file); std::string const PatchedFile = GetKeepCompressedFileName(UncompressedUnpatchedFile, Target); @@ -2532,6 +2610,10 @@ void pkgAcqIndex::Init(string const &URI, string const &URIDesc, DestFile = GetPartialFileNameFromURI(URI); NextCompressionExtension(CurrentCompressionExtension, CompressionExtensions, false); + // store file size of the download to ensure the fetcher gives + // accurate progress reporting + FileSize = GetExpectedHashes().FileSize(); + if (CurrentCompressionExtension == "uncompressed") { Desc.URI = URI; @@ -2621,20 +2703,6 @@ void pkgAcqIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * TransactionManager->AbortTransaction(); } /*}}}*/ -// AcqIndex::ReverifyAfterIMS - Reverify index after an ims-hit /*{{{*/ -void pkgAcqIndex::ReverifyAfterIMS() -{ - // update destfile to *not* include the compression extension when doing - // a reverify (as its uncompressed on disk already) - DestFile = GetCompressedFileName(Target, GetPartialFileNameFromURI(Target.URI), CurrentCompressionExtension); - - // copy FinalFile into partial/ so that we check the hash again - string FinalFile = GetFinalFilename(); - Stage = STAGE_DECOMPRESS_AND_VERIFY; - Desc.URI = "copy:" + FinalFile; - QueueURI(Desc); -} - /*}}}*/ // AcqIndex::Done - Finished a fetch /*{{{*/ // --------------------------------------------------------------------- /* This goes through a number of states.. On the initial fetch the @@ -2651,107 +2719,93 @@ void pkgAcqIndex::Done(string const &Message, switch(Stage) { case STAGE_DOWNLOAD: - StageDownloadDone(Message, Hashes, Cfg); + StageDownloadDone(Message); break; case STAGE_DECOMPRESS_AND_VERIFY: - StageDecompressDone(Message, Hashes, Cfg); + StageDecompressDone(); break; } } /*}}}*/ // AcqIndex::StageDownloadDone - Queue for decompress and verify /*{{{*/ -void pkgAcqIndex::StageDownloadDone(string const &Message, HashStringList const &, - pkgAcquire::MethodConfig const * const) +void pkgAcqIndex::StageDownloadDone(string const &Message) { + Local = true; Complete = true; - // Handle the unzipd case - std::string FileName = LookupTag(Message,"Alt-Filename"); - if (FileName.empty() == false) + std::string const AltFilename = LookupTag(Message,"Alt-Filename"); + std::string Filename = LookupTag(Message,"Filename"); + + // we need to verify the file against the current Release file again + // on if-modfied-since hit to avoid a stale attack against us + if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true) { + // copy FinalFile into partial/ so that we check the hash again + string const FinalFile = GetExistingFilename(GetFinalFileNameFromURI(Target.URI)); + if (symlink(FinalFile.c_str(), DestFile.c_str()) != 0) + _error->WarningE("pkgAcqIndex::StageDownloadDone", "Symlinking final file %s back to %s failed", FinalFile.c_str(), DestFile.c_str()); + else + { + EraseFileName = DestFile; + Filename = DestFile; + } Stage = STAGE_DECOMPRESS_AND_VERIFY; - Local = true; - if (CurrentCompressionExtension != "uncompressed") - DestFile.erase(DestFile.length() - (CurrentCompressionExtension.length() + 1)); - Desc.URI = "copy:" + FileName; + Desc.URI = "store:" + Filename; QueueURI(Desc); - SetActiveSubprocess("copy"); + SetActiveSubprocess(::URI(Desc.URI).Access); return; } - FileName = LookupTag(Message,"Filename"); - + // methods like file:// give us an alternative (uncompressed) file + else if (Target.KeepCompressed == false && AltFilename.empty() == false) + { + if (CurrentCompressionExtension != "uncompressed") + DestFile.erase(DestFile.length() - (CurrentCompressionExtension.length() + 1)); + Filename = AltFilename; + } // Methods like e.g. "file:" will give us a (compressed) FileName that is // not the "DestFile" we set, in this case we uncompress from the local file - if (FileName != DestFile && RealFileExists(DestFile) == false) + else if (Filename != DestFile && RealFileExists(DestFile) == false) { - Local = true; - if (Target.KeepCompressed == true) - { - // but if we don't keep the uncompress we copy the compressed file first - Stage = STAGE_DOWNLOAD; - Desc.URI = "copy:" + FileName; - QueueURI(Desc); - SetActiveSubprocess("copy"); - return; - } + // symlinking ensures that the filename can be used for compression detection + // that is e.g. needed for by-hash which has no extension over file + if (symlink(Filename.c_str(),DestFile.c_str()) != 0) + _error->WarningE("pkgAcqIndex::StageDownloadDone", "Symlinking file %s to %s failed", Filename.c_str(), DestFile.c_str()); else { - // symlinking ensures that the filename can be used for compression detection - // that is e.g. needed for by-hash over file - if (symlink(FileName.c_str(),DestFile.c_str()) != 0) - _error->WarningE("pkgAcqIndex::StageDownloadDone", "Symlinking file %s to %s failed", FileName.c_str(), DestFile.c_str()); - else - { - EraseFileName = DestFile; - FileName = DestFile; - } + EraseFileName = DestFile; + Filename = DestFile; } } - else - EraseFileName = FileName; - // we need to verify the file against the current Release file again - // on if-modfied-since hit to avoid a stale attack against us - if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true) - { - // The files timestamp matches, reverify by copy into partial/ - EraseFileName = ""; - ReverifyAfterIMS(); - return; - } - - string decompProg = "store"; - if (Target.KeepCompressed == true) - { - DestFile = "/dev/null"; - EraseFileName.clear(); - } + Stage = STAGE_DECOMPRESS_AND_VERIFY; + DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target); + if (Filename != DestFile && flExtension(Filename) == flExtension(DestFile)) + Desc.URI = "copy:" + Filename; else + Desc.URI = "store:" + Filename; + if (DestFile == Filename) { if (CurrentCompressionExtension == "uncompressed") - decompProg = "copy"; - else - DestFile.erase(DestFile.length() - (CurrentCompressionExtension.length() + 1)); + return StageDecompressDone(); + DestFile = "/dev/null"; } + if (EraseFileName.empty()) + EraseFileName = Filename; + // queue uri for the next stage - Stage = STAGE_DECOMPRESS_AND_VERIFY; - Desc.URI = decompProg + ":" + FileName; QueueURI(Desc); - SetActiveSubprocess(decompProg); + SetActiveSubprocess(::URI(Desc.URI).Access); } /*}}}*/ // AcqIndex::StageDecompressDone - Final verification /*{{{*/ -void pkgAcqIndex::StageDecompressDone(string const &, - HashStringList const &, - pkgAcquire::MethodConfig const * const) +void pkgAcqIndex::StageDecompressDone() { - if (Target.KeepCompressed == true && DestFile == "/dev/null") - DestFile = GetPartialFileNameFromURI(Target.URI + '.' + CurrentCompressionExtension); + if (DestFile == "/dev/null") + DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target); // Done, queue for rename on transaction finished TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename()); - return; } /*}}}*/ pkgAcqIndex::~pkgAcqIndex() {} @@ -3047,9 +3101,14 @@ std::string pkgAcqArchive::ShortDesc() const /*{{{*/ pkgAcqArchive::~pkgAcqArchive() {} // AcqChangelog::pkgAcqChangelog - Constructors /*{{{*/ +class pkgAcqChangelog::Private +{ + public: + std::string FinalFile; +}; pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::VerIterator const &Ver, std::string const &DestDir, std::string const &DestFilename) : - pkgAcquire::Item(Owner), d(NULL), SrcName(Ver.SourcePkgName()), SrcVersion(Ver.SourceVerStr()) + pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(Ver.SourcePkgName()), SrcVersion(Ver.SourceVerStr()) { Desc.URI = URI(Ver); Init(DestDir, DestFilename); @@ -3058,7 +3117,7 @@ pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::VerIterator pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::RlsFileIterator const &RlsFile, char const * const Component, char const * const SrcName, char const * const SrcVersion, const string &DestDir, const string &DestFilename) : - pkgAcquire::Item(Owner), d(NULL), SrcName(SrcName), SrcVersion(SrcVersion) + pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(SrcName), SrcVersion(SrcVersion) { Desc.URI = URI(RlsFile, Component, SrcName, SrcVersion); Init(DestDir, DestFilename); @@ -3066,7 +3125,7 @@ pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::RlsFileIter pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, std::string const &URI, char const * const SrcName, char const * const SrcVersion, const string &DestDir, const string &DestFilename) : - pkgAcquire::Item(Owner), d(NULL), SrcName(SrcName), SrcVersion(SrcVersion) + pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(SrcName), SrcVersion(SrcVersion) { Desc.URI = URI; Init(DestDir, DestFilename); @@ -3087,30 +3146,44 @@ void pkgAcqChangelog::Init(std::string const &DestDir, std::string const &DestFi return; } - if (DestDir.empty()) + std::string DestFileName; + if (DestFilename.empty()) + DestFileName = flCombine(DestFile, SrcName + ".changelog"); + else + DestFileName = flCombine(DestFile, DestFilename); + + std::string const SandboxUser = _config->Find("APT::Sandbox::User"); + std::string const systemTemp = GetTempDir(SandboxUser); + char tmpname[1000]; + snprintf(tmpname, sizeof(tmpname), "%s/apt-changelog-XXXXXX", systemTemp.c_str()); + if (NULL == mkdtemp(tmpname)) { - std::string const SandboxUser = _config->Find("APT::Sandbox::User"); - std::string const systemTemp = GetTempDir(SandboxUser); - char tmpname[100]; - snprintf(tmpname, sizeof(tmpname), "%s/apt-changelog-XXXXXX", systemTemp.c_str()); - if (NULL == mkdtemp(tmpname)) + _error->Errno("mkdtemp", "mkdtemp failed in changelog acquire of %s %s", SrcName.c_str(), SrcVersion.c_str()); + Status = StatError; + return; + } + TemporaryDirectory = tmpname; + + ChangeOwnerAndPermissionOfFile("Item::QueueURI", TemporaryDirectory.c_str(), + SandboxUser.c_str(), "root", 0700); + + DestFile = flCombine(TemporaryDirectory, DestFileName); + if (DestDir.empty() == false) + { + d->FinalFile = flCombine(DestDir, DestFileName); + if (RealFileExists(d->FinalFile)) { - _error->Errno("mkdtemp", "mkdtemp failed in changelog acquire of %s %s", SrcName.c_str(), SrcVersion.c_str()); - Status = StatError; - return; + FileFd file1, file2; + if (file1.Open(DestFile, FileFd::WriteOnly | FileFd::Create | FileFd::Exclusive) && + file2.Open(d->FinalFile, FileFd::ReadOnly) && CopyFile(file2, file1)) + { + struct timeval times[2]; + times[0].tv_sec = times[1].tv_sec = file2.ModificationTime(); + times[0].tv_usec = times[1].tv_usec = 0; + utimes(DestFile.c_str(), times); + } } - DestFile = TemporaryDirectory = tmpname; - - ChangeOwnerAndPermissionOfFile("Item::QueueURI", DestFile.c_str(), - SandboxUser.c_str(), "root", 0700); } - else - DestFile = DestDir; - - if (DestFilename.empty()) - DestFile = flCombine(DestFile, SrcName + ".changelog"); - else - DestFile = flCombine(DestFile, DestFilename); Desc.ShortDesc = "Changelog"; strprintf(Desc.Description, "%s %s %s Changelog", URI::SiteOnly(Desc.URI).c_str(), SrcName.c_str(), SrcVersion.c_str()); @@ -3120,16 +3193,47 @@ void pkgAcqChangelog::Init(std::string const &DestDir, std::string const &DestFi /*}}}*/ std::string pkgAcqChangelog::URI(pkgCache::VerIterator const &Ver) /*{{{*/ { + std::string const confOnline = "Acquire::Changelogs::AlwaysOnline"; + bool AlwaysOnline = _config->FindB(confOnline, false); + if (AlwaysOnline == false) + for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF) + { + pkgCache::PkgFileIterator const PF = VF.File(); + if (PF.Flagged(pkgCache::Flag::NotSource) || PF->Release == 0) + continue; + pkgCache::RlsFileIterator const RF = PF.ReleaseFile(); + if (RF->Origin != 0 && _config->FindB(confOnline + "::Origin::" + RF.Origin(), false)) + { + AlwaysOnline = true; + break; + } + } + if (AlwaysOnline == false) + { + pkgCache::PkgIterator const Pkg = Ver.ParentPkg(); + if (Pkg->CurrentVer != 0 && Pkg.CurrentVer() == Ver) + { + std::string const basename = std::string("/usr/share/doc/") + Pkg.Name() + "/changelog"; + std::string const debianname = basename + ".Debian"; + if (FileExists(debianname)) + return "copy://" + debianname; + else if (FileExists(debianname + ".gz")) + return "gzip://" + debianname + ".gz"; + else if (FileExists(basename)) + return "copy://" + basename; + else if (FileExists(basename + ".gz")) + return "gzip://" + basename + ".gz"; + } + } + char const * const SrcName = Ver.SourcePkgName(); char const * const SrcVersion = Ver.SourceVerStr(); - pkgCache::PkgFileIterator PkgFile; // find the first source for this version which promises a changelog for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF) { pkgCache::PkgFileIterator const PF = VF.File(); if (PF.Flagged(pkgCache::Flag::NotSource) || PF->Release == 0) continue; - PkgFile = PF; pkgCache::RlsFileIterator const RF = PF.ReleaseFile(); std::string const uri = URI(RF, PF.Component(), SrcName, SrcVersion); if (uri.empty()) @@ -3226,7 +3330,6 @@ void pkgAcqChangelog::Failed(string const &Message, pkgAcquire::MethodConfig con ErrorText = errText; else ErrorText = errText + " (" + ErrorText + ")"; - return; } /*}}}*/ // AcqChangelog::Done - Item downloaded OK /*{{{*/ @@ -3234,6 +3337,12 @@ void pkgAcqChangelog::Done(string const &Message,HashStringList const &CalcHashe pkgAcquire::MethodConfig const * const Cnf) { Item::Done(Message,CalcHashes,Cnf); + if (d->FinalFile.empty() == false) + { + if (RemoveFile("pkgAcqChangelog::Done", d->FinalFile) == false || + Rename(DestFile, d->FinalFile) == false) + Status = StatError; + } Complete = true; } @@ -3245,6 +3354,7 @@ pkgAcqChangelog::~pkgAcqChangelog() /*{{{*/ RemoveFile("~pkgAcqChangelog", DestFile); rmdir(TemporaryDirectory.c_str()); } + delete d; } /*}}}*/