diff options
-rw-r--r-- | apt-pkg/acquire-item.cc | 25 | ||||
-rw-r--r-- | apt-pkg/acquire-item.h | 1 | ||||
-rw-r--r-- | apt-pkg/acquire-worker.cc | 280 | ||||
-rw-r--r-- | apt-pkg/acquire-worker.h | 3 | ||||
-rw-r--r-- | apt-pkg/acquire.cc | 126 | ||||
-rw-r--r-- | apt-pkg/acquire.h | 44 | ||||
-rwxr-xr-x | test/integration/test-acquire-same-file-multiple-times | 80 | ||||
-rwxr-xr-x | test/integration/test-apt-get-download | 2 |
8 files changed, 409 insertions, 152 deletions
diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc index 4bf4e62f8..dc4f61b56 100644 --- a/apt-pkg/acquire-item.cc +++ b/apt-pkg/acquire-item.cc @@ -438,6 +438,11 @@ APT_PURE pkgAcquire * pkgAcquire::Item::GetOwner() const /*{{{*/ return Owner; } /*}}}*/ +pkgAcquire::ItemDesc &pkgAcquire::Item::GetItemDesc() /*{{{*/ +{ + return Desc; +} + /*}}}*/ APT_CONST bool pkgAcquire::Item::IsTrusted() const /*{{{*/ { return false; @@ -840,7 +845,7 @@ bool pkgAcqMetaBase::CheckDownloadDone(pkgAcqTransactionItem * const I, const st return false; } - if (FileName != I->DestFile) + if (FileName != I->DestFile && RealFileExists(I->DestFile) == false) { I->Local = true; I->Desc.URI = "copy:" + FileName; @@ -2482,7 +2487,7 @@ void pkgAcqIndex::StageDownloadDone(string const &Message, HashStringList const // 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) + if (FileName != DestFile && RealFileExists(DestFile) == false) Local = true; else EraseFileName = FileName; @@ -2760,7 +2765,7 @@ void pkgAcqArchive::Done(string const &Message, HashStringList const &Hashes, } // Reference filename - if (FileName != DestFile) + if (DestFile != FileName && RealFileExists(DestFile) == false) { StoreFilename = DestFile = FileName; Local = true; @@ -2903,18 +2908,6 @@ void pkgAcqChangelog::Init(std::string const &DestDir, std::string const &DestFi strprintf(Desc.Description, "%s %s %s Changelog", URI::SiteOnly(Desc.URI).c_str(), SrcName.c_str(), SrcVersion.c_str()); Desc.Owner = this; QueueURI(Desc); - - if (Status == StatDone) // this happens if we queue the same changelog two times - { - Complete = true; - for (pkgAcquire::UriIterator I = Owner->UriBegin(); I != Owner->UriEnd(); ++I) - if (I->URI == Desc.URI) - if (DestFile != I->Owner->DestFile) - if (symlink(I->Owner->DestFile.c_str(), DestFile.c_str()) != 0) - { - ; // ignore error, there isn't anthing we could do to handle the edgecase of an edgecase - } - } } /*}}}*/ std::string pkgAcqChangelog::URI(pkgCache::VerIterator const &Ver) /*{{{*/ @@ -3107,7 +3100,7 @@ void pkgAcqFile::Done(string const &Message,HashStringList const &CalcHashes, return; // We have to copy it into place - if (FileName != DestFile) + if (RealFileExists(DestFile.c_str()) == false) { Local = true; if (_config->FindB("Acquire::Source-Symlinks",true) == false || diff --git a/apt-pkg/acquire-item.h b/apt-pkg/acquire-item.h index 606fd4173..9dbacc1ea 100644 --- a/apt-pkg/acquire-item.h +++ b/apt-pkg/acquire-item.h @@ -249,6 +249,7 @@ class pkgAcquire::Item : public WeakPointable /*{{{*/ /** \return the acquire process with which this item is associated. */ pkgAcquire *GetOwner() const; + pkgAcquire::ItemDesc &GetItemDesc(); /** \return \b true if this object is being fetched from a trusted source. */ virtual bool IsTrusted() const; diff --git a/apt-pkg/acquire-worker.cc b/apt-pkg/acquire-worker.cc index 099a1f87d..d6318a21b 100644 --- a/apt-pkg/acquire-worker.cc +++ b/apt-pkg/acquire-worker.cc @@ -258,18 +258,27 @@ bool pkgAcquire::Worker::RunMessages() ItemDone(); - pkgAcquire::Item *Owner = Itm->Owner; - pkgAcquire::ItemDesc Desc = *Itm; - // Change the status so that it can be dequeued - Owner->Status = pkgAcquire::Item::StatIdle; + for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) + { + pkgAcquire::Item *Owner = *O; + Owner->Status = pkgAcquire::Item::StatIdle; + } // Mark the item as done (taking care of all queues) // and then put it in the main queue again + std::vector<Item*> const ItmOwners = Itm->Owners; OwnerQ->ItemDone(Itm); - OwnerQ->Owner->Enqueue(Desc); + Itm = NULL; + for (pkgAcquire::Queue::QItem::owner_iterator O = ItmOwners.begin(); O != ItmOwners.end(); ++O) + { + pkgAcquire::Item *Owner = *O; + pkgAcquire::ItemDesc desc = Owner->GetItemDesc(); + desc.URI = NewURI; + OwnerQ->Owner->Enqueue(desc); - if (Log != 0) - Log->Done(Desc); + if (Log != 0) + Log->Done(Owner->GetItemDesc()); + } break; } @@ -286,14 +295,18 @@ bool pkgAcquire::Worker::RunMessages() CurrentSize = 0; TotalSize = strtoull(LookupTag(Message,"Size","0").c_str(), NULL, 10); ResumePoint = strtoull(LookupTag(Message,"Resume-Point","0").c_str(), NULL, 10); - Itm->Owner->Start(Message, TotalSize); - - // Display update before completion - if (Log != 0 && Log->MorePulses == true) - Log->Pulse(Itm->Owner->GetOwner()); + for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) + { + (*O)->Start(Message, TotalSize); - if (Log != 0) - Log->Fetch(*Itm); + // Display update before completion + if (Log != 0) + { + if (Log->MorePulses == true) + Log->Pulse((*O)->GetOwner()); + Log->Fetch((*O)->GetItemDesc()); + } + } break; } @@ -307,105 +320,110 @@ bool pkgAcquire::Worker::RunMessages() break; } - pkgAcquire::Item *Owner = Itm->Owner; - pkgAcquire::ItemDesc Desc = *Itm; - - if (RealFileExists(Owner->DestFile)) - ChangeOwnerAndPermissionOfFile("201::URIDone", Owner->DestFile.c_str(), "root", "root", 0644); + PrepareFiles("201::URIDone", Itm); // Display update before completion if (Log != 0 && Log->MorePulses == true) - Log->Pulse(Owner->GetOwner()); + for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) + Log->Pulse((*O)->GetOwner()); - OwnerQ->ItemDone(Itm); - - HashStringList const ExpectedHashes = Owner->GetExpectedHashes(); - // see if we got hashes to verify + std::string const filename = LookupTag(Message, "Filename", Itm->Owner->DestFile.c_str()); HashStringList ReceivedHashes; - for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type) { - std::string const tagname = std::string(*type) + "-Hash"; - std::string const hashsum = LookupTag(Message, tagname.c_str()); - if (hashsum.empty() == false) - ReceivedHashes.push_back(HashString(*type, hashsum)); - } - // not all methods always sent Hashes our way - if (ExpectedHashes.usable() == true && ReceivedHashes.usable() == false) - { - std::string const filename = LookupTag(Message, "Filename", Owner->DestFile.c_str()); - if (filename.empty() == false && RealFileExists(filename)) + // see if we got hashes to verify + for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type) { - Hashes calc(ExpectedHashes); - FileFd file(filename, FileFd::ReadOnly, FileFd::None); - calc.AddFD(file); - ReceivedHashes = calc.GetHashStringList(); + std::string const tagname = std::string(*type) + "-Hash"; + std::string const hashsum = LookupTag(Message, tagname.c_str()); + if (hashsum.empty() == false) + ReceivedHashes.push_back(HashString(*type, hashsum)); + } + // not all methods always sent Hashes our way + if (ReceivedHashes.usable() == false) + { + HashStringList const ExpectedHashes = Itm->GetExpectedHashes(); + if (ExpectedHashes.usable() == true && RealFileExists(filename)) + { + Hashes calc(ExpectedHashes); + FileFd file(filename, FileFd::ReadOnly, FileFd::None); + calc.AddFD(file); + ReceivedHashes = calc.GetHashStringList(); + } } } - if(_config->FindB("Debug::pkgAcquire::Auth", false) == true) - { - std::clog << "201 URI Done: " << Owner->DescURI() << endl - << "ReceivedHash:" << endl; - for (HashStringList::const_iterator hs = ReceivedHashes.begin(); hs != ReceivedHashes.end(); ++hs) - std::clog << "\t- " << hs->toStr() << std::endl; - std::clog << "ExpectedHash:" << endl; - for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs) - std::clog << "\t- " << hs->toStr() << std::endl; - std::clog << endl; - } + // only local files can refer other filenames and counting them as fetched would be unfair + if (Log != NULL && filename != Itm->Owner->DestFile) + Log->Fetched(ReceivedHashes.FileSize(),atoi(LookupTag(Message,"Resume-Point","0").c_str())); + + std::vector<Item*> const ItmOwners = Itm->Owners; + OwnerQ->ItemDone(Itm); + Itm = NULL; - // decide if what we got is what we expected - bool consideredOkay = false; bool const isIMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false) || StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false); - if (ExpectedHashes.usable()) + + for (pkgAcquire::Queue::QItem::owner_iterator O = ItmOwners.begin(); O != ItmOwners.end(); ++O) { - if (ReceivedHashes.usable() == false) + pkgAcquire::Item * const Owner = *O; + HashStringList const ExpectedHashes = Owner->GetExpectedHashes(); + if(_config->FindB("Debug::pkgAcquire::Auth", false) == true) { - /* IMS-Hits can't be checked here as we will have uncompressed file, - but the hashes for the compressed file. What we have was good through - so all we have to ensure later is that we are not stalled. */ - consideredOkay = isIMSHit; + std::clog << "201 URI Done: " << Owner->DescURI() << endl + << "ReceivedHash:" << endl; + for (HashStringList::const_iterator hs = ReceivedHashes.begin(); hs != ReceivedHashes.end(); ++hs) + std::clog << "\t- " << hs->toStr() << std::endl; + std::clog << "ExpectedHash:" << endl; + for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs) + std::clog << "\t- " << hs->toStr() << std::endl; + std::clog << endl; } - else if (ReceivedHashes == ExpectedHashes) - consideredOkay = true; - else - consideredOkay = false; - } - else if (Owner->HashesRequired() == true) - consideredOkay = false; - else - consideredOkay = true; + // decide if what we got is what we expected + bool consideredOkay = false; + if (ExpectedHashes.usable()) + { + if (ReceivedHashes.usable() == false) + { + /* IMS-Hits can't be checked here as we will have uncompressed file, + but the hashes for the compressed file. What we have was good through + so all we have to ensure later is that we are not stalled. */ + consideredOkay = isIMSHit; + } + else if (ReceivedHashes == ExpectedHashes) + consideredOkay = true; + else + consideredOkay = false; - if (consideredOkay == true) - { - Owner->Done(Message, ReceivedHashes, Config); - ItemDone(); + } + else if (Owner->HashesRequired() == true) + consideredOkay = false; + else + consideredOkay = true; - // Log that we are done - if (Log != 0) + if (consideredOkay == true) { - if (isIMSHit) + Owner->Done(Message, ReceivedHashes, Config); + + // Log that we are done + if (Log != 0) { - /* Hide 'hits' for local only sources - we also manage to - hide gets */ - if (Config->LocalOnly == false) - Log->IMSHit(Desc); + if (isIMSHit) + Log->IMSHit(Owner->GetItemDesc()); + else + Log->Done(Owner->GetItemDesc()); } - else - Log->Done(Desc); } - } - else - { - Owner->Status = pkgAcquire::Item::StatAuthError; - Owner->Failed(Message,Config); - ItemDone(); + else + { + Owner->Status = pkgAcquire::Item::StatAuthError; + Owner->Failed(Message,Config); - if (Log != 0) - Log->Fail(Desc); + if (Log != 0) + Log->Fail(Owner->GetItemDesc()); + } } + ItemDone(); break; } @@ -419,30 +437,32 @@ bool pkgAcquire::Worker::RunMessages() break; } + PrepareFiles("400::URIFailure", Itm); + // Display update before completion if (Log != 0 && Log->MorePulses == true) - Log->Pulse(Itm->Owner->GetOwner()); - - pkgAcquire::Item *Owner = Itm->Owner; - pkgAcquire::ItemDesc Desc = *Itm; - - if (RealFileExists(Owner->DestFile)) - ChangeOwnerAndPermissionOfFile("400::URIFailure", Owner->DestFile.c_str(), "root", "root", 0644); + for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) + Log->Pulse((*O)->GetOwner()); + std::vector<Item*> const ItmOwners = Itm->Owners; OwnerQ->ItemDone(Itm); + Itm = NULL; - // set some status - if(LookupTag(Message,"FailReason") == "Timeout" || - LookupTag(Message,"FailReason") == "TmpResolveFailure" || - LookupTag(Message,"FailReason") == "ResolveFailure" || - LookupTag(Message,"FailReason") == "ConnectionRefused") - Owner->Status = pkgAcquire::Item::StatTransientNetworkError; + for (pkgAcquire::Queue::QItem::owner_iterator O = ItmOwners.begin(); O != ItmOwners.end(); ++O) + { + // set some status + if(LookupTag(Message,"FailReason") == "Timeout" || + LookupTag(Message,"FailReason") == "TmpResolveFailure" || + LookupTag(Message,"FailReason") == "ResolveFailure" || + LookupTag(Message,"FailReason") == "ConnectionRefused") + (*O)->Status = pkgAcquire::Item::StatTransientNetworkError; - Owner->Failed(Message,Config); - ItemDone(); + (*O)->Failed(Message,Config); - if (Log != 0) - Log->Fail(Desc); + if (Log != 0) + Log->Fail((*O)->GetItemDesc()); + } + ItemDone(); break; } @@ -578,16 +598,24 @@ bool pkgAcquire::Worker::QueueItem(pkgAcquire::Queue::QItem *Item) Message.reserve(300); Message += "URI: " + Item->URI; Message += "\nFilename: " + Item->Owner->DestFile; - HashStringList const hsl = Item->Owner->GetExpectedHashes(); + + HashStringList const hsl = Item->GetExpectedHashes(); for (HashStringList::const_iterator hs = hsl.begin(); hs != hsl.end(); ++hs) Message += "\nExpected-" + hs->HashType() + ": " + hs->HashValue(); - if(Item->Owner->FileSize > 0) + + if (hsl.FileSize() == 0) { - string MaximumSize; - strprintf(MaximumSize, "%llu", Item->Owner->FileSize); - Message += "\nMaximum-Size: " + MaximumSize; + unsigned long long FileSize = Item->GetMaximumSize(); + if(FileSize > 0) + { + string MaximumSize; + strprintf(MaximumSize, "%llu", FileSize); + Message += "\nMaximum-Size: " + MaximumSize; + } } - Message += Item->Owner->Custom600Headers(); + + Item->SyncDestinationFiles(); + Message += Item->Custom600Headers(); Message += "\n\n"; if (RealFileExists(Item->Owner->DestFile)) @@ -686,3 +714,33 @@ void pkgAcquire::Worker::ItemDone() Status = string(); } /*}}}*/ +void pkgAcquire::Worker::PrepareFiles(char const * const caller, pkgAcquire::Queue::QItem const * const Itm)/*{{{*/ +{ + if (RealFileExists(Itm->Owner->DestFile)) + { + ChangeOwnerAndPermissionOfFile(caller, Itm->Owner->DestFile.c_str(), "root", "root", 0644); + std::string const filename = Itm->Owner->DestFile; + for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) + { + pkgAcquire::Item const * const Owner = *O; + if (Owner->DestFile == filename) + continue; + unlink(Owner->DestFile.c_str()); + if (link(filename.c_str(), Owner->DestFile.c_str()) != 0) + { + // diferent mounts can't happen for us as we download to lists/ by default, + // but if the system is reused by others the locations can potentially be on + // different disks, so use symlink as poor-men replacement. + // FIXME: Real copying as last fallback, but that is costly, so offload to a method preferable + if (symlink(filename.c_str(), Owner->DestFile.c_str()) != 0) + _error->Error("Can't create (sym)link of file %s to %s", filename.c_str(), Owner->DestFile.c_str()); + } + } + } + else + { + for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) + unlink((*O)->DestFile.c_str()); + } +} + /*}}}*/ diff --git a/apt-pkg/acquire-worker.h b/apt-pkg/acquire-worker.h index db8889c8e..3a3ef706d 100644 --- a/apt-pkg/acquire-worker.h +++ b/apt-pkg/acquire-worker.h @@ -326,6 +326,9 @@ class pkgAcquire::Worker : public WeakPointable * \b false, also rudely interrupts the worker with a SIGINT. */ virtual ~Worker(); + +private: + APT_HIDDEN void PrepareFiles(char const * const caller, pkgAcquire::Queue::QItem const * const Itm); }; /** @} */ diff --git a/apt-pkg/acquire.cc b/apt-pkg/acquire.cc index 0c815c005..14c8863dc 100644 --- a/apt-pkg/acquire.cc +++ b/apt-pkg/acquire.cc @@ -23,6 +23,7 @@ #include <apt-pkg/strutl.h> #include <apt-pkg/fileutl.h> +#include <algorithm> #include <string> #include <vector> #include <iostream> @@ -682,7 +683,8 @@ bool pkgAcquire::Queue::Enqueue(ItemDesc &Item) for (; *I != 0; I = &(*I)->Next) if (Item.URI == (*I)->URI) { - Item.Owner->Status = Item::StatDone; + (*I)->Owners.push_back(Item.Owner); + Item.Owner->Status = (*I)->Owner->Status; return false; } @@ -705,13 +707,13 @@ bool pkgAcquire::Queue::Dequeue(Item *Owner) { if (Owner->Status == pkgAcquire::Item::StatFetching) return _error->Error("Tried to dequeue a fetching object"); - + bool Res = false; - + QItem **I = &Items; for (; *I != 0;) { - if ((*I)->Owner == Owner) + if (Owner == (*I)->Owner) { QItem *Jnk= *I; *I = (*I)->Next; @@ -722,7 +724,7 @@ bool pkgAcquire::Queue::Dequeue(Item *Owner) else I = &(*I)->Next; } - + return Res; } /*}}}*/ @@ -799,9 +801,12 @@ pkgAcquire::Queue::QItem *pkgAcquire::Queue::FindItem(string URI,pkgAcquire::Wor bool pkgAcquire::Queue::ItemDone(QItem *Itm) { PipeDepth--; - if (Itm->Owner->Status == pkgAcquire::Item::StatFetching) - Itm->Owner->Status = pkgAcquire::Item::StatDone; - + for (QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) + { + if ((*O)->Status == pkgAcquire::Item::StatFetching) + (*O)->Status = pkgAcquire::Item::StatDone; + } + if (Itm->Owner->QueueCounter <= 1) Owner->Dequeue(Itm->Owner); else @@ -809,7 +814,7 @@ bool pkgAcquire::Queue::ItemDone(QItem *Itm) Dequeue(Itm->Owner); Owner->Bump(); } - + return Cycle(); } /*}}}*/ @@ -824,7 +829,7 @@ bool pkgAcquire::Queue::Cycle() if (PipeDepth < 0) return _error->Error("Pipedepth failure"); - + // Look for a queable item QItem *I = Items; while (PipeDepth < (signed)MaxPipeDepth) @@ -832,18 +837,19 @@ bool pkgAcquire::Queue::Cycle() for (; I != 0; I = I->Next) if (I->Owner->Status == pkgAcquire::Item::StatIdle) break; - + // Nothing to do, queue is idle. if (I == 0) return true; - + I->Worker = Workers; - I->Owner->Status = pkgAcquire::Item::StatFetching; + for (QItem::owner_iterator O = I->Owners.begin(); O != I->Owners.end(); ++O) + (*O)->Status = pkgAcquire::Item::StatFetching; PipeDepth++; if (Workers->QueueItem(I) == false) return false; } - + return true; } /*}}}*/ @@ -855,6 +861,94 @@ void pkgAcquire::Queue::Bump() Cycle(); } /*}}}*/ +HashStringList pkgAcquire::Queue::QItem::GetExpectedHashes() const /*{{{*/ +{ + /* each Item can have multiple owners and each owner might have different + hashes, even if that is unlikely in practice and if so at least some + owners will later fail. There is one situation through which is not a + failure and still needs this handling: Two owners who expect the same + file, but one owner only knows the SHA1 while the other only knows SHA256. */ + HashStringList superhsl; + for (pkgAcquire::Queue::QItem::owner_iterator O = Owners.begin(); O != Owners.end(); ++O) + { + HashStringList const hsl = (*O)->GetExpectedHashes(); + if (hsl.usable() == false) + continue; + if (superhsl.usable() == false) + superhsl = hsl; + else + { + // we merge both lists - if we find disagreement send no hashes + HashStringList::const_iterator hs = hsl.begin(); + for (; hs != hsl.end(); ++hs) + if (superhsl.push_back(*hs) == false) + break; + if (hs != hsl.end()) + { + superhsl.clear(); + break; + } + } + } + return superhsl; +} + /*}}}*/ +APT_PURE unsigned long long pkgAcquire::Queue::QItem::GetMaximumSize() const /*{{{*/ +{ + unsigned long long Maximum = std::numeric_limits<unsigned long long>::max(); + for (pkgAcquire::Queue::QItem::owner_iterator O = Owners.begin(); O != Owners.end(); ++O) + { + if ((*O)->FileSize == 0) + continue; + Maximum = std::min(Maximum, (*O)->FileSize); + } + if (Maximum == std::numeric_limits<unsigned long long>::max()) + return 0; + return Maximum; +} + /*}}}*/ +void pkgAcquire::Queue::QItem::SyncDestinationFiles() const /*{{{*/ +{ + /* ensure that the first owner has the best partial file of all and + the rest have (potentially dangling) symlinks to it so that + everything (like progress reporting) finds it easily */ + std::string superfile = Owner->DestFile; + off_t supersize = 0; + for (pkgAcquire::Queue::QItem::owner_iterator O = Owners.begin(); O != Owners.end(); ++O) + { + if ((*O)->DestFile == superfile) + continue; + struct stat file; + if (lstat((*O)->DestFile.c_str(),&file) == 0) + { + if ((file.st_mode & S_IFREG) == 0) + unlink((*O)->DestFile.c_str()); + else if (supersize < file.st_size) + { + supersize = file.st_size; + unlink(superfile.c_str()); + rename((*O)->DestFile.c_str(), superfile.c_str()); + } + else + unlink((*O)->DestFile.c_str()); + if (symlink(superfile.c_str(), (*O)->DestFile.c_str()) != 0) + { + ; // not a problem per-se and no real alternative + } + } + } +} + /*}}}*/ +std::string pkgAcquire::Queue::QItem::Custom600Headers() const /*{{{*/ +{ + /* The others are relatively easy to merge, but this one? + Lets not merge and see how far we can run with it… + Likely, nobody will ever notice as all the items will + be of the same class and hence generate the same headers. */ + return Owner->Custom600Headers(); +} + /*}}}*/ + // AcquireStatus::pkgAcquireStatus - Constructor /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -914,9 +1008,9 @@ bool pkgAcquireStatus::Pulse(pkgAcquire *Owner) { CurrentBytes += I->CurrentSize; ResumeSize += I->ResumePoint; - + // Files with unknown size always have 100% completion - if (I->CurrentItem->Owner->FileSize == 0 && + if (I->CurrentItem->Owner->FileSize == 0 && I->CurrentItem->Owner->Complete == false) TotalBytes += I->CurrentSize; } diff --git a/apt-pkg/acquire.h b/apt-pkg/acquire.h index fc90624e1..02031dafd 100644 --- a/apt-pkg/acquire.h +++ b/apt-pkg/acquire.h @@ -68,9 +68,10 @@ #include <apt-pkg/macros.h> #include <apt-pkg/weakptr.h> +#include <apt-pkg/hashes.h> -#include <vector> #include <string> +#include <vector> #include <stddef.h> #include <sys/time.h> @@ -390,13 +391,13 @@ class pkgAcquire */ struct pkgAcquire::ItemDesc : public WeakPointable { - /** \brief The URI from which to download this item. */ + /** \brief URI from which to download this item. */ std::string URI; - /** brief A description of this item. */ + /** \brief description of this item. */ std::string Description; - /** brief A shorter description of this item. */ + /** \brief shorter description of this item. */ std::string ShortDesc; - /** brief The underlying item which is to be downloaded. */ + /** \brief underlying item which is to be downloaded. */ Item *Owner; }; /*}}}*/ @@ -419,13 +420,26 @@ class pkgAcquire::Queue protected: /** \brief A single item placed in this queue. */ - struct QItem : pkgAcquire::ItemDesc + struct QItem : public WeakPointable { /** \brief The next item in the queue. */ QItem *Next; /** \brief The worker associated with this item, if any. */ pkgAcquire::Worker *Worker; + /** \brief The URI from which to download this item. */ + std::string URI; + /** \brief A description of this item. */ + std::string Description; + /** \brief A shorter description of this item. */ + std::string ShortDesc; + /** \brief The underlying items interested in the download */ + std::vector<Item*> Owners; + // both, backward compatibility and easy access as syncing is interal + Item * Owner; + + typedef std::vector<Item*>::const_iterator owner_iterator; + /** \brief Assign the ItemDesc portion of this QItem from * another ItemDesc */ @@ -434,10 +448,24 @@ class pkgAcquire::Queue URI = I.URI; Description = I.Description; ShortDesc = I.ShortDesc; + Owners.clear(); + Owners.push_back(I.Owner); Owner = I.Owner; }; + + /** @return the sum of all expected hashes by all owners */ + HashStringList GetExpectedHashes() const; + + /** @return smallest maximum size of all owners */ + unsigned long long GetMaximumSize() const; + + /** \brief get partial files in order */ + void SyncDestinationFiles() const; + + /** @return the custom headers to use for this item */ + std::string Custom600Headers() const; }; - + /** \brief The name of this queue. */ std::string Name; @@ -590,7 +618,7 @@ class pkgAcquire::UriIterator } }; - inline pkgAcquire::ItemDesc const *operator ->() const {return CurItem;}; + inline pkgAcquire::Queue::QItem const *operator ->() const {return CurItem;}; inline bool operator !=(UriIterator const &rhs) const {return rhs.CurQ != CurQ || rhs.CurItem != CurItem;}; inline bool operator ==(UriIterator const &rhs) const {return rhs.CurQ == CurQ && rhs.CurItem == CurItem;}; diff --git a/test/integration/test-acquire-same-file-multiple-times b/test/integration/test-acquire-same-file-multiple-times new file mode 100755 index 000000000..d329a39cb --- /dev/null +++ b/test/integration/test-acquire-same-file-multiple-times @@ -0,0 +1,80 @@ +#!/bin/sh +set -e + +TESTDIR=$(readlink -f $(dirname $0)) +. $TESTDIR/framework +setupenvironment +configarchitecture 'amd64' + +TESTFILE="$TESTDIR/framework" +cp $TESTFILE aptarchive/foo +APTARCHIVE="$(readlink -f ./aptarchive)" + +filedown() { + msgtest 'Downloading the same URI twice over file' "$1" + testsuccess --nomsg apthelper download-file file:///$APTARCHIVE/foo ./downloaded/foo1 '' file:///$APTARCHIVE/foo ./downloaded/foo2 '' -o Debug::pkgAcquire::Worker=1 + cp rootdir/tmp/testsuccess.output download.log + #cat download.log + testsuccess cmp $TESTFILE ./downloaded/foo1 + testsuccess cmp ./downloaded/foo1 ./downloaded/foo2 + #testequal '1' grep -c '200%20URI%20Start' ./download.log + testequal '1' grep -c '201%20URI%20Done' ./download.log + rm -f ./downloaded/foo1 ./downloaded/foo2 +} + +testrun() { + $1 'no partial' + cp $TESTFILE ./downloaded/foo1 + $1 'complete partial 1' + cp $TESTFILE ./downloaded/foo2 + $1 'complete partial 2' + cp $TESTFILE ./downloaded/foo1 + cp $TESTFILE ./downloaded/foo2 + $1 'complete partial 1+2' + dd if=$TESTFILE of=./downloaded/foo1 bs=500 count=1 2>/dev/null + $1 'partial partial 1' + dd if=$TESTFILE of=./downloaded/foo2 bs=500 count=1 2>/dev/null + $1 'partial partial 2' + dd if=$TESTFILE of=./downloaded/foo1 bs=500 count=1 2>/dev/null + dd if=$TESTFILE of=./downloaded/foo2 bs=500 count=1 2>/dev/null + $1 'partial partial 1+2' +} +testrun 'filedown' + +changetowebserver -o aptwebserver::redirect::replace::/foo2=/foo + +httpdown() { + msgtest 'Downloading the same URI to different files' 'twice over http' + testsuccess --nomsg apthelper download-file http://localhost:8080/foo ./downloaded/foo1 '' http://localhost:8080/foo ./downloaded/foo2 '' -o Debug::pkgAcquire::Worker=1 + cp rootdir/tmp/testsuccess.output download.log + testsuccess cmp $TESTDIR/framework ./downloaded/foo1 + testsuccess cmp ./downloaded/foo1 ./downloaded/foo2 + testequal '1' grep -c '200%20URI%20Start' ./download.log + testequal '1' grep -c '201%20URI%20Done' ./download.log + rm -f ./downloaded/foo1 ./downloaded/foo2 +} +testrun 'httpdown' + +httpredirectdown() { + msgtest 'Redirect leads' 'first URI to the second URI' + testsuccess --nomsg apthelper download-file http://localhost:8080/foo2 ./downloaded/foo1 '' http://localhost:8080/foo ./downloaded/foo2 '' -o Debug::pkgAcquire::Worker=1 + cp rootdir/tmp/testsuccess.output download.log + testsuccess cmp $TESTDIR/framework ./downloaded/foo1 + testsuccess cmp ./downloaded/foo1 ./downloaded/foo2 + testequal '1' grep -c '200%20URI%20Start' ./download.log + testequal '1' grep -c '103%20Redirect' ./download.log + testequal '1' grep -c '201%20URI%20Done' ./download.log + rm -f ./downloaded/foo1 ./downloaded/foo2 +} +testrun 'httpredirectdown' + +httpsamedown() { + msgtest 'Downloading two files via the same URI to' 'the same file' + testsuccess --nomsg apthelper download-file http://localhost:8080/foo ./downloaded/foo1 '' http://localhost:8080/foo ./downloaded/foo1 '' -o Debug::pkgAcquire::Worker=1 + cp rootdir/tmp/testsuccess.output download.log + testsuccess cmp $TESTDIR/framework ./downloaded/foo1 + testequal '1' grep -c '200%20URI%20Start' ./download.log + testequal '1' grep -c '201%20URI%20Done' ./download.log + rm -f ./downloaded/foo1 +} +testrun 'httpsamedown' diff --git a/test/integration/test-apt-get-download b/test/integration/test-apt-get-download index 6503bbd1c..fa0b65672 100755 --- a/test/integration/test-apt-get-download +++ b/test/integration/test-apt-get-download @@ -38,7 +38,7 @@ testdownload() { APT="${APT}/${3}" fi msgtest "Test download of package file $1 with" "$APT" - testsuccess --nomsg aptget download ${APT} + testsuccess --nomsg aptget download ${APT} -o Debug::pkgAcquire::Worker=1 -o Debug::pkgAcquire::Auth=1 testsuccess test -f "$1" rm -f "$1" } |