#include <stdio.h>
#include <ctime>
#include <sstream>
+#include <numeric>
#include <apti18n.h>
/*}}}*/
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;
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)/*{{{*/
{
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
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
// 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 pkgAcqDiffIndex::GetMetaKey() const
{
- return Target.MetaKey + ".diff/Index";
+ return GetDiffIndexFileName(Target.MetaKey);
}
/*}}}*/
//pkgAcqTransactionItem::TransactionState and specialisations for child classes /*{{{*/
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
{
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);
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;
}
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())
unsigned short const sizeLimitPercent = _config->FindI("Acquire::PDiffs::SizeLimit", 100);
if (sizeLimitPercent > 0 && TransactionManager->MetaIndexParser != nullptr)
{
- // compressed case
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();
return false;
}
}
- // uncompressed case
- downloadSize = std::accumulate(available_patches.begin(),
- available_patches.end(), 0llu, [](unsigned long long const T, DiffInfo const &I) {
- return T + I.patch_hashes.FileSize();
- });
- if (downloadSize != 0)
- {
- unsigned long long const downloadSizeIdx = ServerSize;
- unsigned long long const sizeLimit = downloadSizeIdx * sizeLimitPercent;
- if ((sizeLimit/100) < downloadSize)
- {
- if (Debug)
- std::clog << "Need " << downloadSize << " uncompressed bytes (Limit is " << (sizeLimit/100) << ", "
- << "original is " << downloadSizeIdx << ") so fallback to complete download" << std::endl;
- return false;
- }
- }
}
// we have something, queue the diffs
// 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<std::string> 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;
}
}
for (std::vector<pkgAcqIndexMergeDiffs *>::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");
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);
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;
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);
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);
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);
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());
/*}}}*/
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())
ErrorText = errText;
else
ErrorText = errText + " (" + ErrorText + ")";
- return;
}
/*}}}*/
// AcqChangelog::Done - Item downloaded OK /*{{{*/
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;
}
RemoveFile("~pkgAcqChangelog", DestFile);
rmdir(TemporaryDirectory.c_str());
}
+ delete d;
}
/*}}}*/