X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/73e0ee1e7f97571d7be3277e3c25c2f41b2bb946..3b5607fc31371190470074371793cb8500b5139e:/apt-pkg/acquire-item.cc?ds=inline diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc index 91f3d0605..6cb9b012a 100644 --- a/apt-pkg/acquire-item.cc +++ b/apt-pkg/acquire-item.cc @@ -13,25 +13,39 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ +#include + #include #include +#include #include -#include #include #include #include -#include #include #include - -#include - +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include #include #include #include #include #include #include +#include + +#include /*}}}*/ using namespace std; @@ -80,7 +94,7 @@ void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf) Status = StatError; Dequeue(); } - + // report mirror failure back to LP if we actually use a mirror string FailReason = LookupTag(Message, "FailReason"); if(FailReason.size() != 0) @@ -93,7 +107,7 @@ void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf) // --------------------------------------------------------------------- /* Stash status and the file size. Note that setting Complete means sub-phases of the acquire process such as decompresion are operating */ -void pkgAcquire::Item::Start(string /*Message*/,unsigned long Size) +void pkgAcquire::Item::Start(string /*Message*/,unsigned long long Size) { Status = StatFetching; if (FileSize == 0 && Complete == false) @@ -103,8 +117,8 @@ void pkgAcquire::Item::Start(string /*Message*/,unsigned long Size) // Acquire::Item::Done - Item downloaded OK /*{{{*/ // --------------------------------------------------------------------- /* */ -void pkgAcquire::Item::Done(string Message,unsigned long Size,string Hash, - pkgAcquire::MethodConfig *Cnf) +void pkgAcquire::Item::Done(string Message,unsigned long long Size,string /*Hash*/, + pkgAcquire::MethodConfig * /*Cnf*/) { // We just downloaded something.. string FileName = LookupTag(Message,"Filename"); @@ -124,7 +138,7 @@ void pkgAcquire::Item::Done(string Message,unsigned long Size,string Hash, /*}}}*/ // Acquire::Item::Rename - Rename a file /*{{{*/ // --------------------------------------------------------------------- -/* This helper function is used by alot of item methods as thier final +/* This helper function is used by a lot of item methods as their final step */ void pkgAcquire::Item::Rename(string From,string To) { @@ -138,7 +152,34 @@ void pkgAcquire::Item::Rename(string From,string To) } } /*}}}*/ +bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const error)/*{{{*/ +{ + if(FileExists(DestFile)) + Rename(DestFile, DestFile + ".FAILED"); + switch (error) + { + case HashSumMismatch: + ErrorText = _("Hash Sum mismatch"); + Status = StatAuthError; + ReportMirrorFailure("HashChecksumFailure"); + break; + case SizeMismatch: + ErrorText = _("Size mismatch"); + Status = StatAuthError; + ReportMirrorFailure("SizeFailure"); + break; + case InvalidFormat: + ErrorText = _("Invalid file format"); + Status = StatError; + // do not report as usually its not the mirrors fault, but Portal/Proxy + break; + } + return false; +} + /*}}}*/ +// Acquire::Item::ReportMirrorFailure /*{{{*/ +// --------------------------------------------------------------------- void pkgAcquire::Item::ReportMirrorFailure(string FailCode) { // we only act if a mirror was used at all @@ -180,12 +221,122 @@ void pkgAcquire::Item::ReportMirrorFailure(string FailCode) _config->Find("Methods::Mirror::ProblemReporting").c_str()); } } + /*}}}*/ +// AcqSubIndex::AcqSubIndex - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* Get a sub-index file based on checksums from a 'master' file and + possibly query additional files */ +pkgAcqSubIndex::pkgAcqSubIndex(pkgAcquire *Owner, string const &URI, + string const &URIDesc, string const &ShortDesc, + HashString const &ExpectedHash) + : Item(Owner), ExpectedHash(ExpectedHash) +{ + /* XXX: Beware: Currently this class does nothing (of value) anymore ! */ + Debug = _config->FindB("Debug::pkgAcquire::SubIndex",false); + + DestFile = _config->FindDir("Dir::State::lists") + "partial/"; + DestFile += URItoFileName(URI); + Desc.URI = URI; + Desc.Description = URIDesc; + Desc.Owner = this; + Desc.ShortDesc = ShortDesc; + QueueURI(Desc); -// AcqDiffIndex::AcqDiffIndex - Constructor + if(Debug) + std::clog << "pkgAcqSubIndex: " << Desc.URI << std::endl; +} + /*}}}*/ +// AcqSubIndex::Custom600Headers - Insert custom request headers /*{{{*/ // --------------------------------------------------------------------- -/* Get the DiffIndex file first and see if there are patches availabe +/* The only header we use is the last-modified header. */ +string pkgAcqSubIndex::Custom600Headers() +{ + string Final = _config->FindDir("Dir::State::lists"); + Final += URItoFileName(Desc.URI); + + struct stat Buf; + if (stat(Final.c_str(),&Buf) != 0) + return "\nIndex-File: true\nFail-Ignore: true\n"; + return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime); +} + /*}}}*/ +void pkgAcqSubIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/ +{ + if(Debug) + std::clog << "pkgAcqSubIndex failed: " << Desc.URI << " with " << Message << std::endl; + + Complete = false; + Status = StatDone; + Dequeue(); + + // No good Index is provided +} + /*}}}*/ +void pkgAcqSubIndex::Done(string Message,unsigned long long Size,string Md5Hash, /*{{{*/ + pkgAcquire::MethodConfig *Cnf) +{ + if(Debug) + std::clog << "pkgAcqSubIndex::Done(): " << Desc.URI << std::endl; + + string FileName = LookupTag(Message,"Filename"); + if (FileName.empty() == true) + { + Status = StatError; + ErrorText = "Method gave a blank filename"; + return; + } + + if (FileName != DestFile) + { + Local = true; + Desc.URI = "copy:" + FileName; + QueueURI(Desc); + return; + } + + Item::Done(Message,Size,Md5Hash,Cnf); + + string FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(Desc.URI); + + /* Downloaded invalid transindex => Error (LP: #346386) (Closes: #627642) */ + indexRecords SubIndexParser; + if (FileExists(DestFile) == true && !SubIndexParser.Load(DestFile)) { + Status = StatError; + ErrorText = SubIndexParser.ErrorText; + return; + } + + // success in downloading the index + // rename the index + if(Debug) + std::clog << "Renaming: " << DestFile << " -> " << FinalFile << std::endl; + Rename(DestFile,FinalFile); + chmod(FinalFile.c_str(),0644); + DestFile = FinalFile; + + if(ParseIndex(DestFile) == false) + return Failed("", NULL); + + Complete = true; + Status = StatDone; + Dequeue(); + return; +} + /*}}}*/ +bool pkgAcqSubIndex::ParseIndex(string const &IndexFile) /*{{{*/ +{ + indexRecords SubIndexParser; + if (FileExists(IndexFile) == false || SubIndexParser.Load(IndexFile) == false) + return false; + // so something with the downloaded index + return true; +} + /*}}}*/ +// AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* Get the DiffIndex file first and see if there are patches available * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the * patches. If anything goes wrong in that process, it will fall back to * the original packages file @@ -205,7 +356,7 @@ pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner, Desc.URI = URI + ".diff/Index"; DestFile = _config->FindDir("Dir::State::lists") + "partial/"; - DestFile += URItoFileName(URI) + string(".DiffIndex"); + DestFile += URItoFileName(Desc.URI); if(Debug) std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl; @@ -227,21 +378,21 @@ pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner, return; } - if(Debug) - std::clog << "pkgAcqIndexDiffs::pkgAcqIndexDiffs(): " - << CurrentPackagesFile << std::endl; - + if(Debug) + std::clog << "pkgAcqDiffIndex::pkgAcqDiffIndex(): " + << CurrentPackagesFile << std::endl; + QueueURI(Desc); } - + /*}}}*/ // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/ // --------------------------------------------------------------------- /* The only header we use is the last-modified header. */ string pkgAcqDiffIndex::Custom600Headers() { string Final = _config->FindDir("Dir::State::lists"); - Final += URItoFileName(RealURI) + string(".IndexDiff"); + Final += URItoFileName(Desc.URI); if(Debug) std::clog << "Custom600Header-IMS: " << Final << std::endl; @@ -252,13 +403,12 @@ string pkgAcqDiffIndex::Custom600Headers() return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime); } - - -bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile) + /*}}}*/ +bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile) /*{{{*/ { if(Debug) - std::clog << "pkgAcqIndexDiffs::ParseIndexDiff() " << IndexDiffFile - << std::endl; + std::clog << "pkgAcqDiffIndex::ParseIndexDiff() " << IndexDiffFile + << std::endl; pkgTagSection Tags; string ServerSha1; @@ -271,62 +421,123 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile) if(TF.Step(Tags) == true) { - string local_sha1; bool found = false; DiffInfo d; string size; - string tmp = Tags.FindS("SHA1-Current"); + string const tmp = Tags.FindS("SHA1-Current"); std::stringstream ss(tmp); - ss >> ServerSha1; + ss >> ServerSha1 >> size; + unsigned long const ServerSize = atol(size.c_str()); FileFd fd(CurrentPackagesFile, FileFd::ReadOnly); SHA1Summation SHA1; - SHA1.AddFD(fd.Fd(), fd.Size()); - local_sha1 = string(SHA1.Result()); + SHA1.AddFD(fd); + string const local_sha1 = SHA1.Result(); - if(local_sha1 == ServerSha1) + if(local_sha1 == ServerSha1) { - // we have the same sha1 as the server + // we have the same sha1 as the server so we are done here if(Debug) std::clog << "Package file is up-to-date" << std::endl; - // set found to true, this will queue a pkgAcqIndexDiffs with - // a empty availabe_patches - found = true; - } - else + // list cleanup needs to know that this file as well as the already + // present index is ours, so we create an empty diff to save it for us + new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc, + ExpectedHash, ServerSha1, available_patches); + return true; + } + else { if(Debug) - std::clog << "SHA1-Current: " << ServerSha1 << std::endl; + std::clog << "SHA1-Current: " << ServerSha1 << " and we start at "<< fd.Name() << " " << fd.Size() << " " << local_sha1 << std::endl; // check the historie and see what patches we need - string history = Tags.FindS("SHA1-History"); + string const history = Tags.FindS("SHA1-History"); std::stringstream hist(history); - while(hist >> d.sha1 >> size >> d.file) + while(hist >> d.sha1 >> size >> d.file) { - d.size = atoi(size.c_str()); // read until the first match is found + // from that point on, we probably need all diffs if(d.sha1 == local_sha1) found=true; - // from that point on, we probably need all diffs - if(found) + else if (found == false) + continue; + + if(Debug) + std::clog << "Need to get diff: " << d.file << std::endl; + available_patches.push_back(d); + } + + if (available_patches.empty() == 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()) + { + if (Debug) + std::clog << "Need " << available_patches.size() << " diffs (Limit is " << fileLimit + << ") so fallback to complete download" << std::endl; + return false; + } + + // see if the patches are too big + found = false; // it was true and it will be true again at the end + d = *available_patches.begin(); + string const firstPatch = d.file; + unsigned long patchesSize = 0; + std::stringstream patches(Tags.FindS("SHA1-Patches")); + while(patches >> d.sha1 >> size >> d.file) + { + if (firstPatch == d.file) + found = true; + else if (found == false) + continue; + + patchesSize += atol(size.c_str()); + } + unsigned long const sizeLimit = ServerSize * _config->FindI("Acquire::PDiffs::SizeLimit", 100); + if (sizeLimit > 0 && (sizeLimit/100) < patchesSize) { - if(Debug) - std::clog << "Need to get diff: " << d.file << std::endl; - available_patches.push_back(d); + if (Debug) + std::clog << "Need " << patchesSize << " bytes (Limit is " << sizeLimit/100 + << ") so fallback to complete download" << std::endl; + return false; } } } // we have something, queue the next diff - if(found) + if(found) { // queue the diffs - string::size_type last_space = Description.rfind(" "); + string::size_type const last_space = Description.rfind(" "); if(last_space != string::npos) Description.erase(last_space, Description.size()-last_space); - new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc, - ExpectedHash, available_patches); + + /* decide if we should download patches one by one or in one go: + The first is good if the server merges patches, but many don't so client + based merging can be attempt in which case the second is better. + "bad things" will happen if patches are merged on the server, + but client side merging is attempt as well */ + bool pdiff_merge = _config->FindB("Acquire::PDiffs::Merge", true); + if (pdiff_merge == true) + { + // reprepro adds this flag if it has merged patches on the server + std::string const precedence = Tags.FindS("X-Patch-Precedence"); + pdiff_merge = (precedence != "merged"); + } + + if (pdiff_merge == false) + new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc, + ExpectedHash, ServerSha1, available_patches); + else + { + std::vector *diffs = new std::vector(available_patches.size()); + for(size_t i = 0; i < available_patches.size(); ++i) + (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner, RealURI, Description, Desc.ShortDesc, ExpectedHash, + available_patches[i], diffs); + } + Complete = false; Status = StatDone; Dequeue(); @@ -341,12 +552,12 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile) std::clog << "Can't find a patch in the index file" << std::endl; return false; } - -void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) + /*}}}*/ +void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/ { if(Debug) - std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << std::endl - << "Falling back to normal index file aquire" << std::endl; + std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl + << "Falling back to normal index file acquire" << std::endl; new pkgAcqIndex(Owner, RealURI, Description, Desc.ShortDesc, ExpectedHash); @@ -355,8 +566,8 @@ void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) Status = StatDone; Dequeue(); } - -void pkgAcqDiffIndex::Done(string Message,unsigned long Size,string Md5Hash, + /*}}}*/ +void pkgAcqDiffIndex::Done(string Message,unsigned long long Size,string Md5Hash, /*{{{*/ pkgAcquire::MethodConfig *Cnf) { if(Debug) @@ -367,7 +578,7 @@ void pkgAcqDiffIndex::Done(string Message,unsigned long Size,string Md5Hash, string FinalFile; FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI); - // sucess in downloading the index + // success in downloading the index // rename the index FinalFile += string(".IndexDiff"); if(Debug) @@ -385,10 +596,8 @@ void pkgAcqDiffIndex::Done(string Message,unsigned long Size,string Md5Hash, Dequeue(); return; } - - - -// AcqIndexDiffs::AcqIndexDiffs - Constructor + /*}}}*/ +// AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/ // --------------------------------------------------------------------- /* The package diff is added to the queue. one object is constructed * for each diff and the index @@ -396,9 +605,10 @@ void pkgAcqDiffIndex::Done(string Message,unsigned long Size,string Md5Hash, pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner, string URI,string URIDesc,string ShortDesc, HashString ExpectedHash, + string ServerSha1, vector diffs) : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash), - available_patches(diffs) + available_patches(diffs), ServerSha1(ServerSha1) { DestFile = _config->FindDir("Dir::State::lists") + "partial/"; @@ -410,7 +620,7 @@ pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner, Desc.Owner = this; Desc.ShortDesc = ShortDesc; - if(available_patches.size() == 0) + if(available_patches.empty() == true) { // we are done (yeah!) Finish(true); @@ -422,20 +632,18 @@ pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner, QueueNextDiff(); } } - - -void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig *Cnf) + /*}}}*/ +void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/ { if(Debug) - std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << std::endl - << "Falling back to normal index file aquire" << std::endl; + std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl + << "Falling back to normal index file acquire" << std::endl; new pkgAcqIndex(Owner, RealURI, Description,Desc.ShortDesc, ExpectedHash); Finish(); } - - -// helper that cleans the item out of the fetcher queue + /*}}}*/ +// Finish - helper that cleans the item out of the fetcher queue /*{{{*/ void pkgAcqIndexDiffs::Finish(bool allDone) { // we restore the original name, this is required, otherwise @@ -447,9 +655,7 @@ void pkgAcqIndexDiffs::Finish(bool allDone) if(!ExpectedHash.empty() && !ExpectedHash.VerifyFile(DestFile)) { - Status = StatAuthError; - ErrorText = _("MD5Sum mismatch"); - Rename(DestFile,DestFile + ".FAILED"); + RenameOnError(HashSumMismatch); Dequeue(); return; } @@ -470,10 +676,8 @@ void pkgAcqIndexDiffs::Finish(bool allDone) Dequeue(); return; } - - - -bool pkgAcqIndexDiffs::QueueNextDiff() + /*}}}*/ +bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/ { // calc sha1 of the just patched file @@ -482,32 +686,39 @@ bool pkgAcqIndexDiffs::QueueNextDiff() FileFd fd(FinalFile, FileFd::ReadOnly); SHA1Summation SHA1; - SHA1.AddFD(fd.Fd(), fd.Size()); + SHA1.AddFD(fd); string local_sha1 = string(SHA1.Result()); if(Debug) std::clog << "QueueNextDiff: " << FinalFile << " (" << local_sha1 << ")"<::iterator I=available_patches.begin(); - available_patches.size() > 0 && + available_patches.empty() == false && I != available_patches.end() && - (*I).sha1 != local_sha1; - I++) + I->sha1 != local_sha1; + ++I) { available_patches.erase(I); } // error checking and falling back if no patch was found - if(available_patches.size() == 0) - { + if(available_patches.empty() == true) + { Failed("", NULL); return false; } // queue the right diff - Desc.URI = string(RealURI) + ".diff/" + available_patches[0].file + ".gz"; + Desc.URI = RealURI + ".diff/" + available_patches[0].file + ".gz"; Desc.Description = Description + " " + available_patches[0].file + string(".pdiff"); DestFile = _config->FindDir("Dir::State::lists") + "partial/"; DestFile += URItoFileName(RealURI + ".diff/" + available_patches[0].file); @@ -519,10 +730,8 @@ bool pkgAcqIndexDiffs::QueueNextDiff() return true; } - - - -void pkgAcqIndexDiffs::Done(string Message,unsigned long Size,string Md5Hash, + /*}}}*/ +void pkgAcqIndexDiffs::Done(string Message,unsigned long long Size,string Md5Hash, /*{{{*/ pkgAcquire::MethodConfig *Cnf) { if(Debug) @@ -533,25 +742,8 @@ void pkgAcqIndexDiffs::Done(string Message,unsigned long Size,string Md5Hash, string FinalFile; FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI); - // sucess in downloading a diff, enter ApplyDiff state - if(State == StateFetchDiff) - { - - if(Debug) - std::clog << "Sending to gzip method: " << FinalFile << std::endl; - - string FileName = LookupTag(Message,"Filename"); - State = StateUnzipDiff; - Local = true; - Desc.URI = "gzip:" + FileName; - DestFile += ".decomp"; - QueueURI(Desc); - Mode = "gzip"; - return; - } - - // sucess in downloading a diff, enter ApplyDiff state - if(State == StateUnzipDiff) + // success in downloading a diff, enter ApplyDiff state + if(State == StateFetchDiff) { // rred excepts the patch as $FinalFile.ed @@ -574,6 +766,7 @@ void pkgAcqIndexDiffs::Done(string Message,unsigned long Size,string Md5Hash, { // remove the just applied patch available_patches.erase(available_patches.begin()); + unlink((FinalFile + ".ed").c_str()); // move into place if(Debug) @@ -585,16 +778,140 @@ void pkgAcqIndexDiffs::Done(string Message,unsigned long Size,string Md5Hash, chmod(FinalFile.c_str(),0644); // see if there is more to download - if(available_patches.size() > 0) { + if(available_patches.empty() == false) { new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc, - ExpectedHash, available_patches); + ExpectedHash, ServerSha1, available_patches); return Finish(); } else return Finish(true); } } + /*}}}*/ +// AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/ +pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire *Owner, + string const &URI, string const &URIDesc, + string const &ShortDesc, HashString const &ExpectedHash, + DiffInfo const &patch, + std::vector const * const allPatches) + : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash), + patch(patch),allPatches(allPatches), State(StateFetchDiff) +{ + DestFile = _config->FindDir("Dir::State::lists") + "partial/"; + DestFile += URItoFileName(URI); + Debug = _config->FindB("Debug::pkgAcquire::Diffs",false); + + Description = URIDesc; + Desc.Owner = this; + Desc.ShortDesc = ShortDesc; + + Desc.URI = RealURI + ".diff/" + patch.file + ".gz"; + Desc.Description = Description + " " + patch.file + string(".pdiff"); + DestFile = _config->FindDir("Dir::State::lists") + "partial/"; + DestFile += URItoFileName(RealURI + ".diff/" + patch.file); + + if(Debug) + std::clog << "pkgAcqIndexMergeDiffs: " << Desc.URI << std::endl; + + QueueURI(Desc); +} + /*}}}*/ +void pkgAcqIndexMergeDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/ +{ + if(Debug) + std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl; + Complete = false; + Status = StatDone; + Dequeue(); + + // check if we are the first to fail, otherwise we are done here + State = StateDoneDiff; + for (std::vector::const_iterator I = allPatches->begin(); + I != allPatches->end(); ++I) + if ((*I)->State == StateErrorDiff) + return; + + // first failure means we should fallback + State = StateErrorDiff; + std::clog << "Falling back to normal index file acquire" << std::endl; + new pkgAcqIndex(Owner, RealURI, Description,Desc.ShortDesc, + ExpectedHash); +} + /*}}}*/ +void pkgAcqIndexMergeDiffs::Done(string Message,unsigned long long Size,string Md5Hash, /*{{{*/ + pkgAcquire::MethodConfig *Cnf) +{ + if(Debug) + std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl; + + Item::Done(Message,Size,Md5Hash,Cnf); + + string const FinalFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI); + + if (State == StateFetchDiff) + { + // rred expects the patch as $FinalFile.ed.$patchname.gz + Rename(DestFile, FinalFile + ".ed." + patch.file + ".gz"); + + // check if this is the last completed diff + State = StateDoneDiff; + for (std::vector::const_iterator I = allPatches->begin(); + I != allPatches->end(); ++I) + if ((*I)->State != StateDoneDiff) + { + if(Debug) + std::clog << "Not the last done diff in the batch: " << Desc.URI << std::endl; + return; + } + + // this is the last completed diff, so we are ready to apply now + State = StateApplyDiff; + + if(Debug) + std::clog << "Sending to rred method: " << FinalFile << std::endl; + + Local = true; + Desc.URI = "rred:" + FinalFile; + QueueURI(Desc); + Mode = "rred"; + return; + } + // success in download/apply all diffs, clean up + else if (State == StateApplyDiff) + { + // see if we really got the expected file + if(!ExpectedHash.empty() && !ExpectedHash.VerifyFile(DestFile)) + { + RenameOnError(HashSumMismatch); + return; + } + + // move the result into place + if(Debug) + std::clog << "Moving patched file in place: " << std::endl + << DestFile << " -> " << FinalFile << std::endl; + Rename(DestFile, FinalFile); + chmod(FinalFile.c_str(), 0644); + + // otherwise lists cleanup will eat the file + DestFile = FinalFile; + + // ensure the ed's are gone regardless of list-cleanup + for (std::vector::const_iterator I = allPatches->begin(); + I != allPatches->end(); ++I) + { + std::string patch = FinalFile + ".ed." + (*I)->patch.file + ".gz"; + unlink(patch.c_str()); + } + + // all set and done + Complete = true; + if(Debug) + std::clog << "allDone: " << DestFile << "\n" << std::endl; + } +} + /*}}}*/ // AcqIndex::AcqIndex - Constructor /*{{{*/ // --------------------------------------------------------------------- /* The package file is added to the queue and a second class is @@ -604,28 +921,61 @@ pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner, HashString ExpectedHash, string comprExt) : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash) { + if(comprExt.empty() == true) + { + // autoselect the compression method + std::vector types = APT::Configuration::getCompressionTypes(); + for (std::vector::const_iterator t = types.begin(); t != types.end(); ++t) + comprExt.append(*t).append(" "); + if (comprExt.empty() == false) + comprExt.erase(comprExt.end()-1); + } + CompressionExtension = comprExt; + + Init(URI, URIDesc, ShortDesc); +} +pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner, IndexTarget const *Target, + HashString const &ExpectedHash, indexRecords const *MetaIndexParser) + : Item(Owner), RealURI(Target->URI), ExpectedHash(ExpectedHash) +{ + // autoselect the compression method + std::vector types = APT::Configuration::getCompressionTypes(); + CompressionExtension = ""; + if (ExpectedHash.empty() == false) + { + for (std::vector::const_iterator t = types.begin(); t != types.end(); ++t) + if (*t == "uncompressed" || MetaIndexParser->Exists(string(Target->MetaKey).append(".").append(*t)) == true) + CompressionExtension.append(*t).append(" "); + } + else + { + for (std::vector::const_iterator t = types.begin(); t != types.end(); ++t) + CompressionExtension.append(*t).append(" "); + } + if (CompressionExtension.empty() == false) + CompressionExtension.erase(CompressionExtension.end()-1); + + Init(Target->URI, Target->Description, Target->ShortDesc); +} + /*}}}*/ +// AcqIndex::Init - defered Constructor /*{{{*/ +void pkgAcqIndex::Init(string const &URI, string const &URIDesc, string const &ShortDesc) { Decompression = false; Erase = false; DestFile = _config->FindDir("Dir::State::lists") + "partial/"; DestFile += URItoFileName(URI); - if(comprExt.empty()) - { - // autoselect the compression method - if(FileExists("/bin/bzip2")) - CompressionExtension = ".bz2"; - else - CompressionExtension = ".gz"; - } else { - CompressionExtension = (comprExt == "plain" ? "" : comprExt); - } - Desc.URI = URI + CompressionExtension; + std::string const comprExt = CompressionExtension.substr(0, CompressionExtension.find(' ')); + if (comprExt == "uncompressed") + Desc.URI = URI; + else + Desc.URI = URI + '.' + comprExt; Desc.Description = URIDesc; Desc.Owner = this; Desc.ShortDesc = ShortDesc; - + QueueURI(Desc); } /*}}}*/ @@ -636,51 +986,41 @@ string pkgAcqIndex::Custom600Headers() { string Final = _config->FindDir("Dir::State::lists"); Final += URItoFileName(RealURI); + if (_config->FindB("Acquire::GzipIndexes",false)) + Final += ".gz"; + string msg = "\nIndex-File: true"; + // FIXME: this really should use "IndexTarget::IsOptional()" but that + // seems to be difficult without breaking ABI + if (ShortDesc().find("Translation") != 0) + msg += "\nFail-Ignore: true"; struct stat Buf; - if (stat(Final.c_str(),&Buf) != 0) - return "\nIndex-File: true"; - return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime); + if (stat(Final.c_str(),&Buf) == 0) + msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime); + + return msg; } /*}}}*/ - -void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) +void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/ { - bool descChanged = false; - // no .bz2 found, retry with .gz - if(Desc.URI.substr(Desc.URI.size()-3) == "bz2") { - Desc.URI = Desc.URI.substr(0,Desc.URI.size()-3) + "gz"; - - new pkgAcqIndex(Owner, RealURI, Desc.Description,Desc.ShortDesc, - ExpectedHash, string(".gz")); - descChanged = true; - } - // no .gz found, retry with uncompressed - else if(Desc.URI.substr(Desc.URI.size()-2) == "gz") { - Desc.URI = Desc.URI.substr(0,Desc.URI.size()-2); - - new pkgAcqIndex(Owner, RealURI, Desc.Description,Desc.ShortDesc, - ExpectedHash, string("plain")); - descChanged = true; - } - if (descChanged) { - Status = StatDone; - Complete = false; - Dequeue(); + size_t const nextExt = CompressionExtension.find(' '); + if (nextExt != std::string::npos) + { + CompressionExtension = CompressionExtension.substr(nextExt+1); + Init(RealURI, Desc.Description, Desc.ShortDesc); return; } // on decompression failure, remove bad versions in partial/ - if(Decompression && Erase) { + if (Decompression && Erase) { string s = _config->FindDir("Dir::State::lists") + "partial/"; - s += URItoFileName(RealURI); + s.append(URItoFileName(RealURI)); unlink(s.c_str()); } Item::Failed(Message,Cnf); } - - + /*}}}*/ // AcqIndex::Done - Finished a fetch /*{{{*/ // --------------------------------------------------------------------- /* This goes through a number of states.. On the initial fetch the @@ -688,7 +1028,7 @@ void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) to the uncompressed version of the file. If this is so the file is copied into the partial directory. In all other cases the file is decompressed with a gzip uri. */ -void pkgAcqIndex::Done(string Message,unsigned long Size,string Hash, +void pkgAcqIndex::Done(string Message,unsigned long long Size,string Hash, pkgAcquire::MethodConfig *Cfg) { Item::Done(Message,Size,Hash,Cfg); @@ -703,12 +1043,31 @@ void pkgAcqIndex::Done(string Message,unsigned long Size,string Hash, if (!ExpectedHash.empty() && ExpectedHash.toStr() != Hash) { - Status = StatAuthError; - ErrorText = _("Hash Sum mismatch"); - Rename(DestFile,DestFile + ".FAILED"); - ReportMirrorFailure("HashChecksumFailure"); + RenameOnError(HashSumMismatch); return; } + + // FIXME: this can go away once we only ever download stuff that + // has a valid hash and we never do GET based probing + // + /* Always verify the index file for correctness (all indexes must + * have a Package field) (LP: #346386) (Closes: #627642) + */ + FileFd fd(DestFile, FileFd::ReadOnly); + // Only test for correctness if the file is not empty (empty is ok) + if (fd.FileSize() > 0) + { + pkgTagSection sec; + pkgTagFile tag(&fd); + + // all our current indexes have a field 'Package' in each section + if (_error->PendingError() == true || tag.Step(sec) == false || sec.Exists("Package") == false) + { + RenameOnError(InvalidFormat); + return; + } + } + // Done, move it into position string FinalFile = _config->FindDir("Dir::State::lists"); FinalFile += URItoFileName(RealURI); @@ -751,28 +1110,41 @@ void pkgAcqIndex::Done(string Message,unsigned long Size,string Hash, Status = StatError; ErrorText = "Method gave a blank filename"; } - + + std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' ')); + // The files timestamp matches - if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true) + if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true) { + if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz") + // Update DestFile for .gz suffix so that the clean operation keeps it + DestFile += ".gz"; return; + } if (FileName == DestFile) Erase = true; else Local = true; - string compExt = flExtension(flNotDir(URI(Desc.URI).Path)); - const char *decompProg; - if(compExt == "bz2") - decompProg = "bzip2"; - else if(compExt == "gz") - decompProg = "gzip"; - // flExtensions returns the full name if no extension is found - // this is why we have this complicated compare operation here - // FIMXE: add a new flJustExtension() that return "" if no - // extension is found and use that above so that it can - // be tested against "" - else if(compExt == flNotDir(URI(Desc.URI).Path)) + string decompProg; + + // If we enable compressed indexes and already have gzip, keep it + if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz" && !Local) { + string FinalFile = _config->FindDir("Dir::State::lists"); + FinalFile += URItoFileName(RealURI) + ".gz"; + Rename(DestFile,FinalFile); + chmod(FinalFile.c_str(),0644); + + // Update DestFile for .gz suffix so that the clean operation keeps it + DestFile = _config->FindDir("Dir::State::lists") + "partial/"; + DestFile += URItoFileName(RealURI) + ".gz"; + return; + } + + // get the binary name for your used compression type + decompProg = _config->Find(string("Acquire::CompressionTypes::").append(compExt),""); + if(decompProg.empty() == false); + else if(compExt == "uncompressed") decompProg = "copy"; else { _error->Error("Unsupported extension: %s", compExt.c_str()); @@ -781,11 +1153,13 @@ void pkgAcqIndex::Done(string Message,unsigned long Size,string Hash, Decompression = true; DestFile += ".decomp"; - Desc.URI = string(decompProg) + ":" + FileName; + Desc.URI = decompProg + ":" + FileName; QueueURI(Desc); - Mode = decompProg; -} + // FIXME: this points to a c++ string that goes out of scope + Mode = decompProg.c_str(); +} + /*}}}*/ // AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/ // --------------------------------------------------------------------- /* The Translation file is added to the queue */ @@ -794,13 +1168,39 @@ pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner, : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashString(), "") { } +pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner, IndexTarget const *Target, + HashString const &ExpectedHash, indexRecords const *MetaIndexParser) + : pkgAcqIndex(Owner, Target, ExpectedHash, MetaIndexParser) +{ +} + /*}}}*/ +// AcqIndexTrans::Custom600Headers - Insert custom request headers /*{{{*/ +// --------------------------------------------------------------------- +string pkgAcqIndexTrans::Custom600Headers() +{ + string Final = _config->FindDir("Dir::State::lists"); + Final += URItoFileName(RealURI); + struct stat Buf; + if (stat(Final.c_str(),&Buf) != 0) + return "\nFail-Ignore: true\nIndex-File: true"; + return "\nFail-Ignore: true\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime); +} /*}}}*/ // AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/ // --------------------------------------------------------------------- /* */ void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf) { + size_t const nextExt = CompressionExtension.find(' '); + if (nextExt != std::string::npos) + { + CompressionExtension = CompressionExtension.substr(nextExt+1); + Init(RealURI, Desc.Description, Desc.ShortDesc); + Status = StatIdle; + return; + } + if (Cnf->LocalOnly == true || StringToBool(LookupTag(Message,"Transient-Failure"),false) == false) { @@ -810,12 +1210,11 @@ void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf) Dequeue(); return; } - + Item::Failed(Message,Cnf); } /*}}}*/ - -pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, +pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, /*{{{*/ string URI,string URIDesc,string ShortDesc, string MetaIndexURI, string MetaIndexURIDesc, string MetaIndexShortDesc, @@ -841,11 +1240,10 @@ pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, string Final = _config->FindDir("Dir::State::lists"); Final += URItoFileName(RealURI); - struct stat Buf; - if (stat(Final.c_str(),&Buf) == 0) + if (RealFileExists(Final) == true) { // File was already in place. It needs to be re-downloaded/verified - // because Release might have changed, we do give it a differnt + // because Release might have changed, we do give it a different // name than DestFile because otherwise the http method will // send If-Range requests and there are too many broken servers // out there that do not understand them @@ -854,6 +1252,19 @@ pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, } QueueURI(Desc); +} + /*}}}*/ +pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/ +{ + // if the file was never queued undo file-changes done in the constructor + if (QueueCounter == 1 && Status == StatIdle && FileSize == 0 && Complete == false && + LastGoodSig.empty() == false) + { + string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI); + if (RealFileExists(Final) == false && RealFileExists(LastGoodSig) == true) + Rename(LastGoodSig, Final); + } + } /*}}}*/ // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/ @@ -868,7 +1279,7 @@ string pkgAcqMetaSig::Custom600Headers() return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime); } -void pkgAcqMetaSig::Done(string Message,unsigned long Size,string MD5, +void pkgAcqMetaSig::Done(string Message,unsigned long long Size,string MD5, pkgAcquire::MethodConfig *Cfg) { Item::Done(Message,Size,MD5,Cfg); @@ -905,7 +1316,7 @@ void pkgAcqMetaSig::Done(string Message,unsigned long Size,string MD5, } /*}}}*/ -void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) +void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/ { string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI); @@ -941,8 +1352,8 @@ void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) Item::Failed(Message,Cnf); } - -pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, + /*}}}*/ +pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, /*{{{*/ string URI,string URIDesc,string ShortDesc, string SigFile, const vector* IndexTargets, @@ -961,7 +1372,6 @@ pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, QueueURI(Desc); } - /*}}}*/ // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/ // --------------------------------------------------------------------- @@ -977,8 +1387,8 @@ string pkgAcqMetaIndex::Custom600Headers() return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime); } - -void pkgAcqMetaIndex::Done(string Message,unsigned long Size,string Hash, + /*}}}*/ +void pkgAcqMetaIndex::Done(string Message,unsigned long long Size,string Hash, /*{{{*/ pkgAcquire::MethodConfig *Cfg) { Item::Done(Message,Size,Hash,Cfg); @@ -993,12 +1403,6 @@ void pkgAcqMetaIndex::Done(string Message,unsigned long Size,string Hash, // all cool, move Release file into place Complete = true; - - string FinalFile = _config->FindDir("Dir::State::lists"); - FinalFile += URItoFileName(RealURI); - Rename(DestFile,FinalFile); - chmod(FinalFile.c_str(),0644); - DestFile = FinalFile; } else { @@ -1010,7 +1414,8 @@ void pkgAcqMetaIndex::Done(string Message,unsigned long Size,string Hash, if (SigFile == "") { // There was no signature file, so we are finished. Download - // the indexes without verification. + // the indexes and do only hashsum verification if possible + MetaIndexParser->Load(DestFile); QueueIndexes(false); } else @@ -1025,11 +1430,23 @@ void pkgAcqMetaIndex::Done(string Message,unsigned long Size,string Hash, Desc.URI = "gpgv:" + SigFile; QueueURI(Desc); Mode = "gpgv"; + return; } } -} -void pkgAcqMetaIndex::RetrievalDone(string Message) + if (Complete == true) + { + string FinalFile = _config->FindDir("Dir::State::lists"); + FinalFile += URItoFileName(RealURI); + if (SigFile == DestFile) + SigFile = FinalFile; + Rename(DestFile,FinalFile); + chmod(FinalFile.c_str(),0644); + DestFile = FinalFile; + } +} + /*}}}*/ +void pkgAcqMetaIndex::RetrievalDone(string Message) /*{{{*/ { // We have just finished downloading a Release file (it is not // verified yet) @@ -1056,12 +1473,21 @@ void pkgAcqMetaIndex::RetrievalDone(string Message) { string FinalFile = _config->FindDir("Dir::State::lists"); FinalFile += URItoFileName(RealURI); + if (SigFile == DestFile) + { + SigFile = FinalFile; + // constructor of pkgAcqMetaClearSig moved it out of the way, + // now move it back in on IMS hit for the 'old' file + string const OldClearSig = DestFile + ".reverify"; + if (RealFileExists(OldClearSig) == true) + Rename(OldClearSig, FinalFile); + } DestFile = FinalFile; } Complete = true; } - -void pkgAcqMetaIndex::AuthDone(string Message) + /*}}}*/ +void pkgAcqMetaIndex::AuthDone(string Message) /*{{{*/ { // At this point, the gpgv method has succeeded, so there is a // valid signature from a key in the trusted keyring. We @@ -1087,81 +1513,116 @@ void pkgAcqMetaIndex::AuthDone(string Message) // Download further indexes with verification QueueIndexes(true); + // is it a clearsigned MetaIndex file? + if (DestFile == SigFile) + return; + // Done, move signature file into position string VerifiedSigFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI) + ".gpg"; Rename(SigFile,VerifiedSigFile); chmod(VerifiedSigFile.c_str(),0644); } - -void pkgAcqMetaIndex::QueueIndexes(bool verify) + /*}}}*/ +void pkgAcqMetaIndex::QueueIndexes(bool verify) /*{{{*/ { +#if 0 + /* Reject invalid, existing Release files (LP: #346386) (Closes: #627642) + * FIXME: Disabled; it breaks unsigned repositories without hashes */ + if (!verify && FileExists(DestFile) && !MetaIndexParser->Load(DestFile)) + { + Status = StatError; + ErrorText = MetaIndexParser->ErrorText; + return; + } +#endif + bool transInRelease = false; + { + std::vector const keys = MetaIndexParser->MetaKeys(); + for (std::vector::const_iterator k = keys.begin(); k != keys.end(); ++k) + // FIXME: Feels wrong to check for hardcoded string here, but what should we do else… + if (k->find("Translation-") != std::string::npos) + { + transInRelease = true; + break; + } + } + for (vector ::const_iterator Target = IndexTargets->begin(); Target != IndexTargets->end(); - Target++) + ++Target) { HashString ExpectedIndexHash; - if (verify) + const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey); + bool compressedAvailable = false; + if (Record == NULL) { - const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey); - if (!Record) - { - Status = StatAuthError; - ErrorText = "Unable to find expected entry " - + (*Target)->MetaKey + " in Meta-index file (malformed Release file?)"; - return; - } - ExpectedIndexHash = Record->Hash; - if (_config->FindB("Debug::pkgAcquire::Auth", false)) - { - std::cerr << "Queueing: " << (*Target)->URI << std::endl; - std::cerr << "Expected Hash: " << ExpectedIndexHash.toStr() << std::endl; - } - if (ExpectedIndexHash.empty()) - { - Status = StatAuthError; - ErrorText = "Unable to find hash sum for " - + (*Target)->MetaKey + " in Meta-index file"; - return; - } + if ((*Target)->IsOptional() == true) + { + std::vector types = APT::Configuration::getCompressionTypes(); + for (std::vector::const_iterator t = types.begin(); t != types.end(); ++t) + if (MetaIndexParser->Exists((*Target)->MetaKey + "." + *t) == true) + { + compressedAvailable = true; + break; + } + } + else if (verify == true) + { + Status = StatAuthError; + strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), (*Target)->MetaKey.c_str()); + return; + } } - - // Queue Packages file (either diff or full packages files, depending - // on the users option) - if(_config->FindB("Acquire::PDiffs",false) == true) + else + { + ExpectedIndexHash = Record->Hash; + if (_config->FindB("Debug::pkgAcquire::Auth", false)) + { + std::cerr << "Queueing: " << (*Target)->URI << std::endl; + std::cerr << "Expected Hash: " << ExpectedIndexHash.toStr() << std::endl; + std::cerr << "For: " << Record->MetaKeyFilename << std::endl; + } + if (verify == true && ExpectedIndexHash.empty() == true && (*Target)->IsOptional() == false) + { + Status = StatAuthError; + strprintf(ErrorText, _("Unable to find hash sum for '%s' in Release file"), (*Target)->MetaKey.c_str()); + return; + } + } + + if ((*Target)->IsOptional() == true) + { + if ((*Target)->IsSubIndex() == true) + new pkgAcqSubIndex(Owner, (*Target)->URI, (*Target)->Description, + (*Target)->ShortDesc, ExpectedIndexHash); + else if (transInRelease == false || Record != NULL || compressedAvailable == true) + { + if (_config->FindB("Acquire::PDiffs",true) == true && transInRelease == true && + MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true) + new pkgAcqDiffIndex(Owner, (*Target)->URI, (*Target)->Description, + (*Target)->ShortDesc, ExpectedIndexHash); + else + new pkgAcqIndexTrans(Owner, *Target, ExpectedIndexHash, MetaIndexParser); + } + continue; + } + + /* Queue Packages file (either diff or full packages files, depending + on the users option) - we also check if the PDiff Index file is listed + in the Meta-Index file. Ideal would be if pkgAcqDiffIndex would test this + instead, but passing the required info to it is to much hassle */ + if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false || + MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true)) new pkgAcqDiffIndex(Owner, (*Target)->URI, (*Target)->Description, (*Target)->ShortDesc, ExpectedIndexHash); - else - new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description, - (*Target)->ShortDesc, ExpectedIndexHash); + else + new pkgAcqIndex(Owner, *Target, ExpectedIndexHash, MetaIndexParser); } } - -bool pkgAcqMetaIndex::VerifyVendor(string Message) + /*}}}*/ +bool pkgAcqMetaIndex::VerifyVendor(string Message) /*{{{*/ { -// // Maybe this should be made available from above so we don't have -// // to read and parse it every time? -// pkgVendorList List; -// List.ReadMainList(); - -// const Vendor* Vndr = NULL; -// for (std::vector::const_iterator I = GPGVOutput.begin(); I != GPGVOutput.end(); I++) -// { -// string::size_type pos = (*I).find("VALIDSIG "); -// if (_config->FindB("Debug::Vendor", false)) -// std::cerr << "Looking for VALIDSIG in \"" << (*I) << "\": pos " << pos -// << std::endl; -// if (pos != std::string::npos) -// { -// string Fingerprint = (*I).substr(pos+sizeof("VALIDSIG")); -// if (_config->FindB("Debug::Vendor", false)) -// std::cerr << "Looking for \"" << Fingerprint << "\" in vendor..." << -// std::endl; -// Vndr = List.FindVendor(Fingerprint) != ""; -// if (Vndr != NULL); -// break; -// } -// } string::size_type pos; // check for missing sigs (that where not fatal because otherwise we had @@ -1177,7 +1638,7 @@ bool pkgAcqMetaIndex::VerifyVendor(string Message) missingkeys += (Fingerprint); } if(!missingkeys.empty()) - _error->Warning("%s", string(msg+missingkeys).c_str()); + _error->Warning("%s", (msg + missingkeys).c_str()); string Transformed = MetaIndexParser->GetExpectedDist(); @@ -1197,6 +1658,19 @@ bool pkgAcqMetaIndex::VerifyVendor(string Message) Transformed = ""; } + if (_config->FindB("Acquire::Check-Valid-Until", true) == true && + MetaIndexParser->GetValidUntil() > 0) { + time_t const invalid_since = time(NULL) - MetaIndexParser->GetValidUntil(); + if (invalid_since > 0) + // TRANSLATOR: The first %s is the URL of the bad Release file, the second is + // the time since then the file is invalid - formated in the same way as in + // the download progress display (e.g. 7d 3h 42min 1s) + return _error->Error( + _("Release file for %s is expired (invalid since %s). " + "Updates for this repository will not be applied."), + RealURI.c_str(), TimeToStr(invalid_since).c_str()); + } + if (_config->FindB("Debug::pkgAcquire::Auth", false)) { std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl; @@ -1214,7 +1688,7 @@ bool pkgAcqMetaIndex::VerifyVendor(string Message) // return false; if (!Transformed.empty()) { - _error->Warning("Conflicting distribution: %s (expected %s but got %s)", + _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"), Desc.Description.c_str(), Transformed.c_str(), MetaIndexParser->GetDist().c_str()); @@ -1223,32 +1697,41 @@ bool pkgAcqMetaIndex::VerifyVendor(string Message) return true; } - /*}}}*/ -// pkgAcqMetaIndex::Failed - no Release file present or no signature -// file present /*{{{*/ + /*}}}*/ +// pkgAcqMetaIndex::Failed - no Release file present or no signature file present /*{{{*/ // --------------------------------------------------------------------- /* */ -void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) +void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/) { if (AuthPass == true) { // gpgv method failed, if we have a good signature - string LastGoodSigFile = _config->FindDir("Dir::State::lists") + - "partial/" + URItoFileName(RealURI) + ".gpg.reverify"; + string LastGoodSigFile = _config->FindDir("Dir::State::lists").append("partial/").append(URItoFileName(RealURI)); + if (DestFile != SigFile) + LastGoodSigFile.append(".gpg"); + LastGoodSigFile.append(".reverify"); + if(FileExists(LastGoodSigFile)) { - string VerifiedSigFile = _config->FindDir("Dir::State::lists") + - URItoFileName(RealURI) + ".gpg"; - Rename(LastGoodSigFile,VerifiedSigFile); + string VerifiedSigFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI); + if (DestFile != SigFile) + VerifiedSigFile.append(".gpg"); + Rename(LastGoodSigFile, VerifiedSigFile); Status = StatTransientNetworkError; - _error->Warning(_("A error occurred during the signature " + _error->Warning(_("An error occurred during the signature " "verification. The repository is not updated " - "and the previous index files will be used." + "and the previous index files will be used. " "GPG error: %s: %s\n"), Desc.Description.c_str(), LookupTag(Message,"Message").c_str()); RunScripts("APT::Update::Auth-Failure"); return; + } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) { + /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */ + _error->Error(_("GPG error: %s: %s"), + Desc.Description.c_str(), + LookupTag(Message,"Message").c_str()); + return; } else { _error->Warning(_("GPG error: %s: %s"), Desc.Description.c_str(), @@ -1258,13 +1741,106 @@ void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) ReportMirrorFailure("GPGFailure"); } + /* Always move the meta index, even if gpgv failed. This ensures + * that PackageFile objects are correctly filled in */ + if (FileExists(DestFile)) { + string FinalFile = _config->FindDir("Dir::State::lists"); + FinalFile += URItoFileName(RealURI); + /* InRelease files become Release files, otherwise + * they would be considered as trusted later on */ + if (SigFile == DestFile) { + RealURI = RealURI.replace(RealURI.rfind("InRelease"), 9, + "Release"); + FinalFile = FinalFile.replace(FinalFile.rfind("InRelease"), 9, + "Release"); + SigFile = FinalFile; + } + Rename(DestFile,FinalFile); + chmod(FinalFile.c_str(),0644); + + DestFile = FinalFile; + } + // No Release file was present, or verification failed, so fall // back to queueing Packages files without verification QueueIndexes(false); } + /*}}}*/ +pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner, /*{{{*/ + string const &URI, string const &URIDesc, string const &ShortDesc, + string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc, + string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc, + const vector* IndexTargets, + indexRecords* MetaIndexParser) : + pkgAcqMetaIndex(Owner, URI, URIDesc, ShortDesc, "", IndexTargets, MetaIndexParser), + MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc), + MetaSigURI(MetaSigURI), MetaSigURIDesc(MetaSigURIDesc), MetaSigShortDesc(MetaSigShortDesc) +{ + SigFile = DestFile; + + // keep the old InRelease around in case of transistent network errors + string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI); + if (RealFileExists(Final) == true) + { + string const LastGoodSig = DestFile + ".reverify"; + Rename(Final,LastGoodSig); + } +} + /*}}}*/ +pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/ +{ + // if the file was never queued undo file-changes done in the constructor + if (QueueCounter == 1 && Status == StatIdle && FileSize == 0 && Complete == false) + { + string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI); + string const LastGoodSig = DestFile + ".reverify"; + if (RealFileExists(Final) == false && RealFileExists(LastGoodSig) == true) + Rename(LastGoodSig, Final); + } +} + /*}}}*/ +// pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/ +// --------------------------------------------------------------------- +// FIXME: this can go away once the InRelease file is used widely +string pkgAcqMetaClearSig::Custom600Headers() +{ + string Final = _config->FindDir("Dir::State::lists"); + Final += URItoFileName(RealURI); + + struct stat Buf; + if (stat(Final.c_str(),&Buf) != 0) + { + Final = DestFile + ".reverify"; + if (stat(Final.c_str(),&Buf) != 0) + return "\nIndex-File: true\nFail-Ignore: true\n"; + } + return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime); +} /*}}}*/ +void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/ +{ + if (AuthPass == false) + { + // Remove the 'old' InRelease file if we try Release.gpg now as otherwise + // the file will stay around and gives a false-auth impression (CVE-2012-0214) + string FinalFile = _config->FindDir("Dir::State::lists"); + FinalFile.append(URItoFileName(RealURI)); + if (FileExists(FinalFile)) + unlink(FinalFile.c_str()); + new pkgAcqMetaSig(Owner, + MetaSigURI, MetaSigURIDesc, MetaSigShortDesc, + MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc, + IndexTargets, MetaIndexParser); + if (Cnf->LocalOnly == true || + StringToBool(LookupTag(Message, "Transient-Failure"), false) == false) + Dequeue(); + } + else + pkgAcqMetaIndex::Failed(Message, Cnf); +} + /*}}}*/ // AcqArchive::AcqArchive - Constructor /*{{{*/ // --------------------------------------------------------------------- /* This just sets up the initial fetch environment and queues the first @@ -1283,7 +1859,7 @@ pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources, _error->Error(_("I wasn't able to locate a file for the %s package. " "This might mean you need to manually fix this package. " "(due to missing arch)"), - Version.ParentPkg().Name()); + Version.ParentPkg().FullName().c_str()); return; } @@ -1291,7 +1867,7 @@ pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources, assumption here that all the available sources for this version share the same extension.. */ // Skip not source sources, they do not have file fields. - for (; Vf.end() == false; Vf++) + for (; Vf.end() == false; ++Vf) { if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0) continue; @@ -1314,34 +1890,40 @@ pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources, } // check if we have one trusted source for the package. if so, switch - // to "TrustedOnly" mode - for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; i++) + // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode + bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false); + bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false); + bool seenUntrusted = false; + for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i) { pkgIndexFile *Index; if (Sources->FindIndex(i.File(),Index) == false) continue; - if (_config->FindB("Debug::pkgAcquire::Auth", false)) - { + + if (debugAuth == true) std::cerr << "Checking index: " << Index->Describe() - << "(Trusted=" << Index->IsTrusted() << ")\n"; - } - if (Index->IsTrusted()) { + << "(Trusted=" << Index->IsTrusted() << ")" << std::endl; + + if (Index->IsTrusted() == true) + { Trusted = true; - break; + if (allowUnauth == false) + break; } + else + seenUntrusted = true; } // "allow-unauthenticated" restores apts old fetching behaviour // that means that e.g. unauthenticated file:// uris are higher // priority than authenticated http:// uris - if (_config->FindB("APT::Get::AllowUnauthenticated",false) == true) + if (allowUnauth == true && seenUntrusted == true) Trusted = false; // Select a source if (QueueNext() == false && _error->PendingError() == false) - _error->Error(_("I wasn't able to locate file for the %s package. " - "This might mean you need to manually fix this package."), - Version.ParentPkg().Name()); + _error->Error(_("Can't find a source to download version '%s' of '%s'"), + Version.VerStr(), Version.ParentPkg().FullName(false).c_str()); } /*}}}*/ // AcqArchive::QueueNext - Queue the next file source /*{{{*/ @@ -1350,8 +1932,9 @@ pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources, the archive is already available in the cache and stashs the MD5 for checking later. */ bool pkgAcqArchive::QueueNext() -{ - for (; Vf.end() == false; Vf++) +{ + string const ForceHash = _config->Find("Acquire::ForceHash"); + for (; Vf.end() == false; ++Vf) { // Ignore not source sources if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0) @@ -1373,12 +1956,29 @@ bool pkgAcqArchive::QueueNext() return false; string PkgFile = Parse.FileName(); - if(Parse.SHA256Hash() != "") - ExpectedHash = HashString("SHA256", Parse.SHA256Hash()); - else if (Parse.SHA1Hash() != "") - ExpectedHash = HashString("SHA1", Parse.SHA1Hash()); - else - ExpectedHash = HashString("MD5Sum", Parse.MD5Hash()); + if (ForceHash.empty() == false) + { + if(stringcasecmp(ForceHash, "sha512") == 0) + ExpectedHash = HashString("SHA512", Parse.SHA512Hash()); + else if(stringcasecmp(ForceHash, "sha256") == 0) + ExpectedHash = HashString("SHA256", Parse.SHA256Hash()); + else if (stringcasecmp(ForceHash, "sha1") == 0) + ExpectedHash = HashString("SHA1", Parse.SHA1Hash()); + else + ExpectedHash = HashString("MD5Sum", Parse.MD5Hash()); + } + else + { + string Hash; + if ((Hash = Parse.SHA512Hash()).empty() == false) + ExpectedHash = HashString("SHA512", Hash); + else if ((Hash = Parse.SHA256Hash()).empty() == false) + ExpectedHash = HashString("SHA256", Hash); + else if ((Hash = Parse.SHA1Hash()).empty() == false) + ExpectedHash = HashString("SHA1", Hash); + else + ExpectedHash = HashString("MD5Sum", Parse.MD5Hash()); + } if (PkgFile.empty() == true) return _error->Error(_("The package index files are corrupted. No Filename: " "field for package %s."), @@ -1387,7 +1987,7 @@ bool pkgAcqArchive::QueueNext() Desc.URI = Index->ArchiveURI(PkgFile); Desc.Description = Index->ArchiveInfo(Version); Desc.Owner = this; - Desc.ShortDesc = Version.ParentPkg().Name(); + Desc.ShortDesc = Version.ParentPkg().FullName(true); // See if we already have the file. (Legacy filenames) FileSize = Version->Size; @@ -1396,7 +1996,7 @@ bool pkgAcqArchive::QueueNext() if (stat(FinalFile.c_str(),&Buf) == 0) { // Make sure the size matches - if ((unsigned)Buf.st_size == Version->Size) + if ((unsigned long long)Buf.st_size == Version->Size) { Complete = true; Local = true; @@ -1415,7 +2015,7 @@ bool pkgAcqArchive::QueueNext() if (stat(FinalFile.c_str(),&Buf) == 0) { // Make sure the size matches - if ((unsigned)Buf.st_size == Version->Size) + if ((unsigned long long)Buf.st_size == Version->Size) { Complete = true; Local = true; @@ -1424,7 +2024,7 @@ bool pkgAcqArchive::QueueNext() return true; } - /* Hmm, we have a file and its size does not match, this shouldnt + /* Hmm, we have a file and its size does not match, this shouldn't happen.. */ unlink(FinalFile.c_str()); } @@ -1435,21 +2035,28 @@ bool pkgAcqArchive::QueueNext() if (stat(DestFile.c_str(),&Buf) == 0) { // Hmm, the partial file is too big, erase it - if ((unsigned)Buf.st_size > Version->Size) + if ((unsigned long long)Buf.st_size > Version->Size) unlink(DestFile.c_str()); else PartialSize = Buf.st_size; } - + + // Disables download of archives - useful if no real installation follows, + // e.g. if we are just interested in proposed installation order + if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true) + { + Complete = true; + Local = true; + Status = StatDone; + StoreFilename = DestFile = FinalFile; + return true; + } + // Create the item Local = false; - Desc.URI = Index->ArchiveURI(PkgFile); - Desc.Description = Index->ArchiveInfo(Version); - Desc.Owner = this; - Desc.ShortDesc = Version.ParentPkg().Name(); QueueURI(Desc); - Vf++; + ++Vf; return true; } return false; @@ -1458,7 +2065,7 @@ bool pkgAcqArchive::QueueNext() // AcqArchive::Done - Finished fetching /*{{{*/ // --------------------------------------------------------------------- /* */ -void pkgAcqArchive::Done(string Message,unsigned long Size,string CalcHash, +void pkgAcqArchive::Done(string Message,unsigned long long Size,string CalcHash, pkgAcquire::MethodConfig *Cfg) { Item::Done(Message,Size,CalcHash,Cfg); @@ -1466,18 +2073,14 @@ void pkgAcqArchive::Done(string Message,unsigned long Size,string CalcHash, // Check the size if (Size != Version->Size) { - Status = StatError; - ErrorText = _("Size mismatch"); + RenameOnError(SizeMismatch); return; } // Check the hash if(ExpectedHash.toStr() != CalcHash) { - Status = StatError; - ErrorText = _("Hash Sum mismatch"); - if(FileExists(DestFile)) - Rename(DestFile,DestFile + ".FAILED"); + RenameOnError(HashSumMismatch); return; } @@ -1523,7 +2126,7 @@ void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf) StringToBool(LookupTag(Message,"Transient-Failure"),false) == true) { // Vf = Version.FileList(); - while (Vf.end() == false) Vf++; + while (Vf.end() == false) ++Vf; StoreFilename = string(); Item::Failed(Message,Cnf); return; @@ -1547,14 +2150,13 @@ void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf) } } /*}}}*/ -// AcqArchive::IsTrusted - Determine whether this archive comes from a -// trusted source /*{{{*/ +// AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/ // --------------------------------------------------------------------- -bool pkgAcqArchive::IsTrusted() +APT_PURE bool pkgAcqArchive::IsTrusted() { return Trusted; } - + /*}}}*/ // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -1566,14 +2168,14 @@ void pkgAcqArchive::Finished() StoreFilename = string(); } /*}}}*/ - // AcqFile::pkgAcqFile - Constructor /*{{{*/ // --------------------------------------------------------------------- /* The file is added to the queue */ pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string Hash, - unsigned long Size,string Dsc,string ShortDesc, - const string &DestDir, const string &DestFilename) : - Item(Owner), ExpectedHash(Hash) + unsigned long long Size,string Dsc,string ShortDesc, + const string &DestDir, const string &DestFilename, + bool IsIndexFile) : + Item(Owner), ExpectedHash(Hash), IsIndexFile(IsIndexFile) { Retries = _config->FindI("Acquire::Retries",0); @@ -1598,7 +2200,7 @@ pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string Hash, if (stat(DestFile.c_str(),&Buf) == 0) { // Hmm, the partial file is too big, erase it - if ((unsigned)Buf.st_size > Size) + if ((Size > 0) && (unsigned long long)Buf.st_size > Size) unlink(DestFile.c_str()); else PartialSize = Buf.st_size; @@ -1610,7 +2212,7 @@ pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string Hash, // AcqFile::Done - Item downloaded OK /*{{{*/ // --------------------------------------------------------------------- /* */ -void pkgAcqFile::Done(string Message,unsigned long Size,string CalcHash, +void pkgAcqFile::Done(string Message,unsigned long long Size,string CalcHash, pkgAcquire::MethodConfig *Cnf) { Item::Done(Message,Size,CalcHash,Cnf); @@ -1618,9 +2220,7 @@ void pkgAcqFile::Done(string Message,unsigned long Size,string CalcHash, // Check the hash if(!ExpectedHash.empty() && ExpectedHash.toStr() != CalcHash) { - Status = StatError; - ErrorText = "Hash Sum mismatch"; - Rename(DestFile,DestFile + ".FAILED"); + RenameOnError(HashSumMismatch); return; } @@ -1688,3 +2288,13 @@ void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf) Item::Failed(Message,Cnf); } /*}}}*/ +// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/ +// --------------------------------------------------------------------- +/* The only header we use is the last-modified header. */ +string pkgAcqFile::Custom600Headers() +{ + if (IsIndexFile) + return "\nIndex-File: true"; + return ""; +} + /*}}}*/