X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/8d20b69d2fd7a8fec82bb559f0e39059bbaecf1b..cc0a4c82b3c132abba9b9ec35fd61bc8b45a1b80:/apt-pkg/acquire-worker.cc?ds=inline diff --git a/apt-pkg/acquire-worker.cc b/apt-pkg/acquire-worker.cc index 54be8e99f..9ed7b5b28 100644 --- a/apt-pkg/acquire-worker.cc +++ b/apt-pkg/acquire-worker.cc @@ -23,10 +23,10 @@ #include #include +#include #include #include #include -#include #include #include @@ -34,6 +34,7 @@ #include #include #include +#include #include /*}}}*/ @@ -41,34 +42,16 @@ using namespace std; // Worker::Worker - Constructor for Queue startup /*{{{*/ -// --------------------------------------------------------------------- -/* */ -pkgAcquire::Worker::Worker(Queue *Q,MethodConfig *Cnf, - pkgAcquireStatus *Log) : Log(Log) +pkgAcquire::Worker::Worker(Queue *Q, MethodConfig *Cnf, pkgAcquireStatus *log) : + d(NULL), OwnerQ(Q), Log(log), Config(Cnf), Access(Cnf->Access), + CurrentItem(nullptr), CurrentSize(0), TotalSize(0) { - OwnerQ = Q; - Config = Cnf; - Access = Cnf->Access; - CurrentItem = 0; - TotalSize = 0; - CurrentSize = 0; - - Construct(); + Construct(); } /*}}}*/ // Worker::Worker - Constructor for method config startup /*{{{*/ -// --------------------------------------------------------------------- -/* */ -pkgAcquire::Worker::Worker(MethodConfig *Cnf) +pkgAcquire::Worker::Worker(MethodConfig *Cnf) : Worker(nullptr, Cnf, nullptr) { - OwnerQ = 0; - Config = Cnf; - Access = Cnf->Access; - CurrentItem = 0; - TotalSize = 0; - CurrentSize = 0; - - Construct(); } /*}}}*/ // Worker::Construct - Constructor helper /*{{{*/ @@ -133,7 +116,7 @@ bool pkgAcquire::Worker::Start() } for (int I = 0; I != 4; I++) SetCloseExec(Pipes[I],true); - + // Fork off the process Process = ExecFork(); if (Process == 0) @@ -142,9 +125,9 @@ bool pkgAcquire::Worker::Start() dup2(Pipes[1],STDOUT_FILENO); dup2(Pipes[2],STDIN_FILENO); SetCloseExec(STDOUT_FILENO,false); - SetCloseExec(STDIN_FILENO,false); + SetCloseExec(STDIN_FILENO,false); SetCloseExec(STDERR_FILENO,false); - + const char *Args[2]; Args[0] = Method.c_str(); Args[1] = 0; @@ -162,7 +145,7 @@ bool pkgAcquire::Worker::Start() close(Pipes[2]); OutReady = false; InReady = true; - + // Read the configuration data if (WaitFd(InFd) == false || ReadMessages() == false) @@ -171,7 +154,7 @@ bool pkgAcquire::Worker::Start() RunMessages(); if (OwnerQ != 0) SendConfiguration(); - + return true; } /*}}}*/ @@ -189,6 +172,25 @@ bool pkgAcquire::Worker::ReadMessages() // --------------------------------------------------------------------- /* This takes the messages from the message queue and runs them through the parsers in order. */ +enum class APT_HIDDEN MessageType { + CAPABILITIES = 100, + LOG = 101, + STATUS = 102, + REDIRECT = 103, + WARNING = 104, + URI_START = 200, + URI_DONE = 201, + URI_FAILURE = 400, + GENERAL_FAILURE = 401, + MEDIA_CHANGE = 403 +}; +static bool isDoomedItem(pkgAcquire::Item const * const Itm) +{ + auto const TransItm = dynamic_cast(Itm); + if (TransItm == nullptr) + return false; + return TransItm->TransactionManager->State != pkgAcqTransactionItem::TransactionStarted; +} bool pkgAcquire::Worker::RunMessages() { while (MessageQueue.empty() == false) @@ -198,217 +200,320 @@ bool pkgAcquire::Worker::RunMessages() if (Debug == true) clog << " <- " << Access << ':' << QuoteString(Message,"\n") << endl; - + // Fetch the message number char *End; - int Number = strtol(Message.c_str(),&End,10); + MessageType const Number = static_cast(strtoul(Message.c_str(),&End,10)); if (End == Message.c_str()) return _error->Error("Invalid message from method %s: %s",Access.c_str(),Message.c_str()); string URI = LookupTag(Message,"URI"); - pkgAcquire::Queue::QItem *Itm = 0; + pkgAcquire::Queue::QItem *Itm = NULL; if (URI.empty() == false) Itm = OwnerQ->FindItem(URI,this); - // update used mirror - string UsedMirror = LookupTag(Message,"UsedMirror", ""); - if (!UsedMirror.empty() && - Itm && - Itm->Description.find(" ") != string::npos) + if (Itm != NULL) { - Itm->Description.replace(0, Itm->Description.find(" "), UsedMirror); - // FIXME: will we need this as well? - //Itm->ShortDesc = UsedMirror; + // update used mirror + string UsedMirror = LookupTag(Message,"UsedMirror", ""); + if (UsedMirror.empty() == false) + { + for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) + (*O)->UsedMirror = UsedMirror; + + if (Itm->Description.find(" ") != string::npos) + Itm->Description.replace(0, Itm->Description.find(" "), UsedMirror); + } } - + // Determine the message number and dispatch switch (Number) { - // 100 Capabilities - case 100: + case MessageType::CAPABILITIES: if (Capabilities(Message) == false) return _error->Error("Unable to process Capabilities message from %s",Access.c_str()); break; - - // 101 Log - case 101: + + case MessageType::LOG: if (Debug == true) clog << " <- (log) " << LookupTag(Message,"Message") << endl; break; - - // 102 Status - case 102: + + case MessageType::STATUS: Status = LookupTag(Message,"Message"); break; - - // 103 Redirect - case 103: + + case MessageType::REDIRECT: { - if (Itm == 0) + if (Itm == nullptr) { _error->Error("Method gave invalid 103 Redirect message"); break; } - - string NewURI = LookupTag(Message,"New-URI",URI.c_str()); + + std::string const NewURI = LookupTag(Message,"New-URI",URI.c_str()); Itm->URI = NewURI; 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 (auto const &O: Itm->Owners) + O->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 const ItmOwners = Itm->Owners; OwnerQ->ItemDone(Itm); - OwnerQ->Owner->Enqueue(Desc); + Itm = nullptr; + for (auto const &Owner: ItmOwners) + { + pkgAcquire::ItemDesc &desc = Owner->GetItemDesc(); + if (Log != nullptr) + Log->Done(desc); - if (Log != 0) - Log->Done(Desc); + // if we change site, treat it as a mirror change + if (URI::SiteOnly(NewURI) != URI::SiteOnly(desc.URI)) + { + auto const firstSpace = desc.Description.find(" "); + if (firstSpace != std::string::npos) + { + std::string const OldSite = desc.Description.substr(0, firstSpace); + if (likely(APT::String::Startswith(desc.URI, OldSite))) + { + std::string const OldExtra = desc.URI.substr(OldSite.length() + 1); + if (likely(APT::String::Endswith(NewURI, OldExtra))) + { + std::string const NewSite = NewURI.substr(0, NewURI.length() - OldExtra.length()); + Owner->UsedMirror = URI::ArchiveOnly(NewSite); + desc.Description.replace(0, firstSpace, Owner->UsedMirror); + } + } + } + } + desc.URI = NewURI; + if (isDoomedItem(Owner) == false) + OwnerQ->Owner->Enqueue(desc); + } break; } - - // 200 URI Start - case 200: + + case MessageType::WARNING: + _error->Warning("%s: %s", Itm->Owner->DescURI().c_str(), LookupTag(Message,"Message").c_str()); + break; + + case MessageType::URI_START: { - if (Itm == 0) + if (Itm == nullptr) { _error->Error("Method gave invalid 200 URI Start message"); break; } - + CurrentItem = Itm; 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,strtoull(LookupTag(Message,"Size","0").c_str(), NULL, 10)); - - // Display update before completion - if (Log != 0 && Log->MorePulses == true) - Log->Pulse(Itm->Owner->GetOwner()); - - if (Log != 0) - Log->Fetch(*Itm); + for (auto const Owner: Itm->Owners) + { + Owner->Start(Message, TotalSize); + // Display update before completion + if (Log != nullptr) + { + if (Log->MorePulses == true) + Log->Pulse(Owner->GetOwner()); + Log->Fetch(Owner->GetItemDesc()); + } + } break; } - - // 201 URI Done - case 201: + + case MessageType::URI_DONE: { - if (Itm == 0) + if (Itm == nullptr) { _error->Error("Method gave invalid 201 URI Done message"); break; } - - pkgAcquire::Item *Owner = Itm->Owner; - pkgAcquire::ItemDesc Desc = *Itm; - + + PrepareFiles("201::URIDone", Itm); + // Display update before completion if (Log != 0 && Log->MorePulses == true) - Log->Pulse(Owner->GetOwner()); - - OwnerQ->ItemDone(Itm); - unsigned long long const ServerSize = strtoull(LookupTag(Message,"Size","0").c_str(), NULL, 10); - bool isHit = StringToBool(LookupTag(Message,"IMS-Hit"),false) || - StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false); - // Using the https method the server might return 200, but the - // If-Modified-Since condition is not satsified, libcurl will - // discard the download. In this case, however, TotalSize will be - // set to the actual size of the file, while ServerSize will be set - // to 0. Therefore, if the item is marked as a hit and the - // downloaded size (ServerSize) is 0, we ignore TotalSize. - if (TotalSize != 0 && (!isHit || ServerSize != 0) && ServerSize != TotalSize) - _error->Warning("Size of file %s is not what the server reported %s %llu", - Owner->DestFile.c_str(), LookupTag(Message,"Size","0").c_str(),TotalSize); - - // see if there is a hash to verify - HashStringList RecivedHashes; - HashStringList expectedHashes = Owner->HashSums(); - for (HashStringList::const_iterator hs = expectedHashes.begin(); hs != expectedHashes.end(); ++hs) - { - std::string const tagname = hs->HashType() + "-Hash"; - std::string const hashsum = LookupTag(Message, tagname.c_str()); - if (hashsum.empty() == false) - RecivedHashes.push_back(HashString(hs->HashType(), hashsum)); - } + for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) + Log->Pulse((*O)->GetOwner()); - if(_config->FindB("Debug::pkgAcquire::Auth", false) == true) + HashStringList ReceivedHashes; { - std::clog << "201 URI Done: " << Owner->DescURI() << endl - << "RecivedHash:" << endl; - for (HashStringList::const_iterator hs = RecivedHashes.begin(); hs != RecivedHashes.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; + std::string const givenfilename = LookupTag(Message, "Filename"); + std::string const filename = givenfilename.empty() ? Itm->Owner->DestFile : givenfilename; + // see if we got hashes to verify + 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 (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(); + } + } + + // only local files can refer other filenames and counting them as fetched would be unfair + if (Log != NULL && Itm->Owner->Complete == false && Itm->Owner->Local == false && givenfilename == filename) + Log->Fetched(ReceivedHashes.FileSize(),atoi(LookupTag(Message,"Resume-Point","0").c_str())); } - Owner->Done(Message, ServerSize, RecivedHashes, Config); - ItemDone(); - // Log that we are done - if (Log != 0) + std::vector const ItmOwners = Itm->Owners; + OwnerQ->ItemDone(Itm); + Itm = NULL; + + bool const isIMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false) || + StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false); + auto const forcedHash = _config->Find("Acquire::ForceHash"); + for (auto const Owner: ItmOwners) { - if (isHit) + HashStringList const ExpectedHashes = Owner->GetExpectedHashes(); + 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; + } + + // decide if what we got is what we expected + bool consideredOkay = false; + if ((forcedHash.empty() && ExpectedHashes.empty() == false) || + (forcedHash.empty() == false && ExpectedHashes.usable())) + { + if (ReceivedHashes.empty()) + { + /* 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; + + } + else + consideredOkay = !Owner->HashesRequired(); + + if (consideredOkay == true) + consideredOkay = Owner->VerifyDone(Message, Config); + else // hashsum mismatch + Owner->Status = pkgAcquire::Item::StatAuthError; + + + if (consideredOkay == true) { - /* Hide 'hits' for local only sources - we also manage to - hide gets */ - if (Config->LocalOnly == false) - Log->IMSHit(Desc); - } + if (isDoomedItem(Owner) == false) + Owner->Done(Message, ReceivedHashes, Config); + if (Log != nullptr) + { + if (isIMSHit) + Log->IMSHit(Owner->GetItemDesc()); + else + Log->Done(Owner->GetItemDesc()); + } + } else - Log->Done(Desc); + { + if (isDoomedItem(Owner) == false) + { + if (Message.find("\nFailReason:") == std::string::npos) + { + if (ReceivedHashes != ExpectedHashes) + Message.append("\nFailReason: HashSumMismatch"); + else + Message.append("\nFailReason: WeakHashSums"); + } + Owner->Failed(Message,Config); + } + if (Log != nullptr) + Log->Fail(Owner->GetItemDesc()); + } } + ItemDone(); break; - } - - // 400 URI Failure - case 400: + } + + case MessageType::URI_FAILURE: { - if (Itm == 0) + if (Itm == nullptr) { - _error->Error("Method gave invalid 400 URI Failure message"); + std::string const msg = LookupTag(Message,"Message"); + _error->Error("Method gave invalid 400 URI Failure message: %s", msg.c_str()); 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 (Log != nullptr && Log->MorePulses == true) + for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) + Log->Pulse((*O)->GetOwner()); + + std::vector const ItmOwners = Itm->Owners; OwnerQ->ItemDone(Itm); + Itm = nullptr; - // 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; + bool errTransient = false, errAuthErr = false; + { + std::string const failReason = LookupTag(Message, "FailReason"); + { + auto const reasons = { "Timeout", "ConnectionRefused", + "ConnectionTimedOut", "ResolveFailure", "TmpResolveFailure" }; + errTransient = std::find(std::begin(reasons), std::end(reasons), failReason) != std::end(reasons); + } + if (errTransient == false) + { + auto const reasons = { "HashSumMismatch", "WeakHashSums", "MaximumSizeExceeded" }; + errAuthErr = std::find(std::begin(reasons), std::end(reasons), failReason) != std::end(reasons); + } + } - Owner->Failed(Message,Config); + for (auto const Owner: ItmOwners) + { + if (errAuthErr && Owner->GetExpectedHashes().empty() == false) + Owner->Status = pkgAcquire::Item::StatAuthError; + else if (errTransient) + Owner->Status = pkgAcquire::Item::StatTransientNetworkError; + + if (isDoomedItem(Owner) == false) + Owner->Failed(Message,Config); + if (Log != nullptr) + Log->Fail(Owner->GetItemDesc()); + } ItemDone(); - if (Log != 0) - Log->Fail(Desc); - break; - } - - // 401 General Failure - case 401: + } + + case MessageType::GENERAL_FAILURE: _error->Error("Method %s General failure: %s",Access.c_str(),LookupTag(Message,"Message").c_str()); break; - - // 403 Media Change - case 403: - MediaChange(Message); + + case MessageType::MEDIA_CHANGE: + MediaChange(Message); break; - } + } } return true; } @@ -421,7 +526,7 @@ bool pkgAcquire::Worker::Capabilities(string Message) { if (Config == 0) return true; - + Config->Version = LookupTag(Message,"Version"); Config->SingleInstance = StringToBool(LookupTag(Message,"Single-Instance"),false); Config->Pipeline = StringToBool(LookupTag(Message,"Pipeline"),false); @@ -436,13 +541,13 @@ bool pkgAcquire::Worker::Capabilities(string Message) clog << "Configured access method " << Config->Access << endl; clog << "Version:" << Config->Version << " SingleInstance:" << Config->SingleInstance << - " Pipeline:" << Config->Pipeline << - " SendConfig:" << Config->SendConfig << - " LocalOnly: " << Config->LocalOnly << - " NeedsCleanup: " << Config->NeedsCleanup << + " Pipeline:" << Config->Pipeline << + " SendConfig:" << Config->SendConfig << + " LocalOnly: " << Config->LocalOnly << + " NeedsCleanup: " << Config->NeedsCleanup << " Removable: " << Config->Removable << endl; } - + return true; } /*}}}*/ @@ -452,14 +557,14 @@ bool pkgAcquire::Worker::Capabilities(string Message) bool pkgAcquire::Worker::MediaChange(string Message) { int status_fd = _config->FindI("APT::Status-Fd",-1); - if(status_fd > 0) + if(status_fd > 0) { string Media = LookupTag(Message,"Media"); - string Drive = LookupTag(Message,"Drive"); + string Drive = LookupTag(Message,"Drive"); ostringstream msg,status; ioprintf(msg,_("Please insert the disc labeled: " "'%s' " - "in the drive '%s' and press enter."), + "in the drive '%s' and press [Enter]."), Media.c_str(),Drive.c_str()); status << "media-change: " // message << Media << ":" // media @@ -525,22 +630,67 @@ bool pkgAcquire::Worker::QueueItem(pkgAcquire::Queue::QItem *Item) { if (OutFd == -1) return false; - + + HashStringList const hsl = Item->GetExpectedHashes(); + + if (isDoomedItem(Item->Owner)) + return true; + + if (hsl.usable() == false && Item->Owner->HashesRequired() && + _config->Exists("Acquire::ForceHash") == false) + { + std::string const Message = "400 URI Failure" + "\nURI: " + Item->URI + + "\nFilename: " + Item->Owner->DestFile + + "\nFailReason: WeakHashSums"; + + auto const ItmOwners = Item->Owners; + for (auto &O: ItmOwners) + { + O->Status = pkgAcquire::Item::StatAuthError; + O->Failed(Message, Config); + if (Log != nullptr) + Log->Fail(O->GetItemDesc()); + } + // "queued" successfully, the item just instantly failed + return true; + } + string Message = "600 URI Acquire\n"; Message.reserve(300); Message += "URI: " + Item->URI; Message += "\nFilename: " + Item->Owner->DestFile; - HashStringList const hsl = Item->Owner->HashSums(); + for (HashStringList::const_iterator hs = hsl.begin(); hs != hsl.end(); ++hs) Message += "\nExpected-" + hs->HashType() + ": " + hs->HashValue(); - Message += Item->Owner->Custom600Headers(); + + if (hsl.FileSize() == 0) + { + unsigned long long FileSize = Item->GetMaximumSize(); + if(FileSize > 0) + { + string MaximumSize; + strprintf(MaximumSize, "%llu", FileSize); + Message += "\nMaximum-Size: " + MaximumSize; + } + } + + Item->SyncDestinationFiles(); + Message += Item->Custom600Headers(); Message += "\n\n"; - + + if (RealFileExists(Item->Owner->DestFile)) + { + std::string const SandboxUser = _config->Find("APT::Sandbox::User"); + ChangeOwnerAndPermissionOfFile("Item::QueueURI", Item->Owner->DestFile.c_str(), + SandboxUser.c_str(), "root", 0600); + } + if (Debug == true) clog << " -> " << Access << ':' << QuoteString(Message,"\n") << endl; OutQueue += Message; OutReady = true; - + return true; } /*}}}*/ @@ -562,7 +712,7 @@ bool pkgAcquire::Worker::OutFdReady() OutQueue.erase(0,Res); if (OutQueue.empty() == true) OutReady = false; - + return true; } /*}}}*/ @@ -584,7 +734,7 @@ bool pkgAcquire::Worker::InFdReady() bool pkgAcquire::Worker::MethodFailure() { _error->Error("Method %s has died unexpectedly!",Access.c_str()); - + // do not reap the child here to show meaningfull error to the user ExecWait(Process,Access.c_str(),false); Process = -1; @@ -596,26 +746,22 @@ bool pkgAcquire::Worker::MethodFailure() InReady = false; OutQueue = string(); MessageQueue.erase(MessageQueue.begin(),MessageQueue.end()); - + return false; } /*}}}*/ -// Worker::Pulse - Called periodically /*{{{*/ +// Worker::Pulse - Called periodically /*{{{*/ // --------------------------------------------------------------------- /* */ void pkgAcquire::Worker::Pulse() { if (CurrentItem == 0) return; - + struct stat Buf; if (stat(CurrentItem->Owner->DestFile.c_str(),&Buf) != 0) return; CurrentSize = Buf.st_size; - - // Hmm? Should not happen... - if (CurrentSize > TotalSize && TotalSize != 0) - TotalSize = CurrentSize; } /*}}}*/ // Worker::ItemDone - Called when the current item is finished /*{{{*/ @@ -629,3 +775,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 || filename == "/dev/null") + continue; + RemoveFile("PrepareFiles", Owner->DestFile); + if (link(filename.c_str(), Owner->DestFile.c_str()) != 0) + { + // different 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) + RemoveFile("PrepareFiles", (*O)->DestFile); + } +} + /*}}}*/