1 // -*- mode: cpp; mode: fold -*-
3 // $Id: acquire-worker.cc,v 1.34 2001/05/22 04:42:54 jgg Exp $
4 /* ######################################################################
8 The worker process can startup either as a Configuration prober
9 or as a queue runner. As a configuration prober it only reads the
10 configuration message and
12 ##################################################################### */
14 // Include Files /*{{{*/
17 #include <apt-pkg/acquire.h>
18 #include <apt-pkg/acquire-worker.h>
19 #include <apt-pkg/acquire-item.h>
20 #include <apt-pkg/configuration.h>
21 #include <apt-pkg/error.h>
22 #include <apt-pkg/fileutl.h>
23 #include <apt-pkg/strutl.h>
24 #include <apt-pkg/hashes.h>
44 // Worker::Worker - Constructor for Queue startup /*{{{*/
45 pkgAcquire::Worker::Worker(Queue
*Q
, MethodConfig
*Cnf
, pkgAcquireStatus
*log
) :
46 d(NULL
), OwnerQ(Q
), Log(log
), Config(Cnf
), Access(Cnf
->Access
),
47 CurrentItem(nullptr), CurrentSize(0), TotalSize(0)
52 // Worker::Worker - Constructor for method config startup /*{{{*/
53 pkgAcquire::Worker::Worker(MethodConfig
*Cnf
) : Worker(nullptr, Cnf
, nullptr)
57 // Worker::Construct - Constructor helper /*{{{*/
58 // ---------------------------------------------------------------------
60 void pkgAcquire::Worker::Construct()
69 Debug
= _config
->FindB("Debug::pkgAcquire::Worker",false);
72 // Worker::~Worker - Destructor /*{{{*/
73 // ---------------------------------------------------------------------
75 pkgAcquire::Worker::~Worker()
82 /* Closing of stdin is the signal to exit and die when the process
83 indicates it needs cleanup */
84 if (Config
->NeedsCleanup
== false)
86 ExecWait(Process
,Access
.c_str(),true);
90 // Worker::Start - Start the worker process /*{{{*/
91 // ---------------------------------------------------------------------
92 /* This forks the method and inits the communication channel */
93 bool pkgAcquire::Worker::Start()
95 // Get the method path
96 constexpr char const * const methodsDir
= "Dir::Bin::Methods";
97 std::string
const confItem
= std::string(methodsDir
) + "::" + Access
;
99 if (_config
->Exists(confItem
))
100 Method
= _config
->FindFile(confItem
.c_str());
102 Method
= _config
->FindDir(methodsDir
) + Access
;
103 if (FileExists(Method
) == false)
105 if (flNotDir(Method
) == "false")
107 _error
->Error(_("The method '%s' is explicitly disabled via configuration."), Access
.c_str());
108 if (Access
== "http" || Access
== "https")
109 _error
->Notice(_("If you meant to use Tor remember to use %s instead of %s."), ("tor+" + Access
).c_str(), Access
.c_str());
112 _error
->Error(_("The method driver %s could not be found."),Method
.c_str());
113 std::string
const A(Access
.cbegin(), std::find(Access
.cbegin(), Access
.cend(), '+'));
115 strprintf(pkg
, "apt-transport-%s", A
.c_str());
116 _error
->Notice(_("Is the package %s installed?"), pkg
.c_str());
119 std::string
const Calling
= _config
->FindDir(methodsDir
) + Access
;
123 std::clog
<< "Starting method '" << Calling
<< "'";
124 if (Calling
!= Method
)
125 std::clog
<< " ( via " << Method
<< " )";
130 int Pipes
[4] = {-1,-1,-1,-1};
131 if (pipe(Pipes
) != 0 || pipe(Pipes
+2) != 0)
133 _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
134 for (int I
= 0; I
!= 4; I
++)
138 for (int I
= 0; I
!= 4; I
++)
139 SetCloseExec(Pipes
[I
],true);
141 // Fork off the process
142 Process
= ExecFork();
146 dup2(Pipes
[1],STDOUT_FILENO
);
147 dup2(Pipes
[2],STDIN_FILENO
);
148 SetCloseExec(STDOUT_FILENO
,false);
149 SetCloseExec(STDIN_FILENO
,false);
150 SetCloseExec(STDERR_FILENO
,false);
152 const char * const Args
[] = { Calling
.c_str(), nullptr };
153 execv(Method
.c_str() ,const_cast<char **>(Args
));
154 std::cerr
<< "Failed to exec method " << Calling
<< " ( via " << Method
<< ")" << endl
;
161 SetNonBlock(Pipes
[0],true);
162 SetNonBlock(Pipes
[3],true);
168 // Read the configuration data
169 if (WaitFd(InFd
) == false ||
170 ReadMessages() == false)
171 return _error
->Error(_("Method %s did not start correctly"),Method
.c_str());
180 // Worker::ReadMessages - Read all pending messages into the list /*{{{*/
181 // ---------------------------------------------------------------------
183 bool pkgAcquire::Worker::ReadMessages()
185 if (::ReadMessages(InFd
,MessageQueue
) == false)
186 return MethodFailure();
190 // Worker::RunMessage - Empty the message queue /*{{{*/
191 // ---------------------------------------------------------------------
192 /* This takes the messages from the message queue and runs them through
193 the parsers in order. */
194 enum class APT_HIDDEN MessageType
{
203 GENERAL_FAILURE
= 401,
206 static bool isDoomedItem(pkgAcquire::Item
const * const Itm
)
208 auto const TransItm
= dynamic_cast<pkgAcqTransactionItem
const * const>(Itm
);
209 if (TransItm
== nullptr)
211 return TransItm
->TransactionManager
->State
!= pkgAcqTransactionItem::TransactionStarted
;
213 bool pkgAcquire::Worker::RunMessages()
215 while (MessageQueue
.empty() == false)
217 string Message
= MessageQueue
.front();
218 MessageQueue
.erase(MessageQueue
.begin());
221 clog
<< " <- " << Access
<< ':' << QuoteString(Message
,"\n") << endl
;
223 // Fetch the message number
225 MessageType
const Number
= static_cast<MessageType
>(strtoul(Message
.c_str(),&End
,10));
226 if (End
== Message
.c_str())
227 return _error
->Error("Invalid message from method %s: %s",Access
.c_str(),Message
.c_str());
229 string URI
= LookupTag(Message
,"URI");
230 pkgAcquire::Queue::QItem
*Itm
= NULL
;
231 if (URI
.empty() == false)
232 Itm
= OwnerQ
->FindItem(URI
,this);
236 // update used mirror
237 string UsedMirror
= LookupTag(Message
,"UsedMirror", "");
238 if (UsedMirror
.empty() == false)
240 for (pkgAcquire::Queue::QItem::owner_iterator O
= Itm
->Owners
.begin(); O
!= Itm
->Owners
.end(); ++O
)
241 (*O
)->UsedMirror
= UsedMirror
;
243 if (Itm
->Description
.find(" ") != string::npos
)
244 Itm
->Description
.replace(0, Itm
->Description
.find(" "), UsedMirror
);
248 // Determine the message number and dispatch
251 case MessageType::CAPABILITIES
:
252 if (Capabilities(Message
) == false)
253 return _error
->Error("Unable to process Capabilities message from %s",Access
.c_str());
256 case MessageType::LOG
:
258 clog
<< " <- (log) " << LookupTag(Message
,"Message") << endl
;
261 case MessageType::STATUS
:
262 Status
= LookupTag(Message
,"Message");
265 case MessageType::REDIRECT
:
269 _error
->Error("Method gave invalid 103 Redirect message");
273 std::string
const NewURI
= LookupTag(Message
,"New-URI",URI
.c_str());
278 // Change the status so that it can be dequeued
279 for (auto const &O
: Itm
->Owners
)
280 O
->Status
= pkgAcquire::Item::StatIdle
;
281 // Mark the item as done (taking care of all queues)
282 // and then put it in the main queue again
283 std::vector
<Item
*> const ItmOwners
= Itm
->Owners
;
284 OwnerQ
->ItemDone(Itm
);
286 for (auto const &Owner
: ItmOwners
)
288 pkgAcquire::ItemDesc
&desc
= Owner
->GetItemDesc();
289 if (Owner
->IsRedirectionLoop(NewURI
))
291 std::string msg
= Message
;
292 msg
.append("\nFailReason: RedirectionLoop");
293 Owner
->Failed(msg
, Config
);
295 Log
->Fail(Owner
->GetItemDesc());
302 // if we change site, treat it as a mirror change
303 if (URI::SiteOnly(NewURI
) != URI::SiteOnly(desc
.URI
))
305 auto const firstSpace
= desc
.Description
.find(" ");
306 if (firstSpace
!= std::string::npos
)
308 std::string
const OldSite
= desc
.Description
.substr(0, firstSpace
);
309 if (likely(APT::String::Startswith(desc
.URI
, OldSite
)))
311 std::string
const OldExtra
= desc
.URI
.substr(OldSite
.length() + 1);
312 if (likely(APT::String::Endswith(NewURI
, OldExtra
)))
314 std::string
const NewSite
= NewURI
.substr(0, NewURI
.length() - OldExtra
.length());
315 Owner
->UsedMirror
= URI::ArchiveOnly(NewSite
);
316 desc
.Description
.replace(0, firstSpace
, Owner
->UsedMirror
);
322 if (isDoomedItem(Owner
) == false)
323 OwnerQ
->Owner
->Enqueue(desc
);
328 case MessageType::WARNING
:
329 _error
->Warning("%s: %s", Itm
->Owner
->DescURI().c_str(), LookupTag(Message
,"Message").c_str());
332 case MessageType::URI_START
:
336 _error
->Error("Method gave invalid 200 URI Start message");
342 TotalSize
= strtoull(LookupTag(Message
,"Size","0").c_str(), NULL
, 10);
343 ResumePoint
= strtoull(LookupTag(Message
,"Resume-Point","0").c_str(), NULL
, 10);
344 for (auto const Owner
: Itm
->Owners
)
346 Owner
->Start(Message
, TotalSize
);
347 // Display update before completion
350 if (Log
->MorePulses
== true)
351 Log
->Pulse(Owner
->GetOwner());
352 Log
->Fetch(Owner
->GetItemDesc());
359 case MessageType::URI_DONE
:
363 _error
->Error("Method gave invalid 201 URI Done message");
367 PrepareFiles("201::URIDone", Itm
);
369 // Display update before completion
370 if (Log
!= 0 && Log
->MorePulses
== true)
371 for (pkgAcquire::Queue::QItem::owner_iterator O
= Itm
->Owners
.begin(); O
!= Itm
->Owners
.end(); ++O
)
372 Log
->Pulse((*O
)->GetOwner());
374 HashStringList ReceivedHashes
;
376 std::string
const givenfilename
= LookupTag(Message
, "Filename");
377 std::string
const filename
= givenfilename
.empty() ? Itm
->Owner
->DestFile
: givenfilename
;
378 // see if we got hashes to verify
379 for (char const * const * type
= HashString::SupportedHashes(); *type
!= NULL
; ++type
)
381 std::string
const tagname
= std::string(*type
) + "-Hash";
382 std::string
const hashsum
= LookupTag(Message
, tagname
.c_str());
383 if (hashsum
.empty() == false)
384 ReceivedHashes
.push_back(HashString(*type
, hashsum
));
386 // not all methods always sent Hashes our way
387 if (ReceivedHashes
.usable() == false)
389 HashStringList
const ExpectedHashes
= Itm
->GetExpectedHashes();
390 if (ExpectedHashes
.usable() == true && RealFileExists(filename
))
392 Hashes
calc(ExpectedHashes
);
393 FileFd
file(filename
, FileFd::ReadOnly
, FileFd::None
);
395 ReceivedHashes
= calc
.GetHashStringList();
399 // only local files can refer other filenames and counting them as fetched would be unfair
400 if (Log
!= NULL
&& Itm
->Owner
->Complete
== false && Itm
->Owner
->Local
== false && givenfilename
== filename
)
401 Log
->Fetched(ReceivedHashes
.FileSize(),atoi(LookupTag(Message
,"Resume-Point","0").c_str()));
404 std::vector
<Item
*> const ItmOwners
= Itm
->Owners
;
405 OwnerQ
->ItemDone(Itm
);
408 bool const isIMSHit
= StringToBool(LookupTag(Message
,"IMS-Hit"),false) ||
409 StringToBool(LookupTag(Message
,"Alt-IMS-Hit"),false);
410 auto const forcedHash
= _config
->Find("Acquire::ForceHash");
411 for (auto const Owner
: ItmOwners
)
413 HashStringList
const ExpectedHashes
= Owner
->GetExpectedHashes();
414 if(_config
->FindB("Debug::pkgAcquire::Auth", false) == true)
416 std::clog
<< "201 URI Done: " << Owner
->DescURI() << endl
417 << "ReceivedHash:" << endl
;
418 for (HashStringList::const_iterator hs
= ReceivedHashes
.begin(); hs
!= ReceivedHashes
.end(); ++hs
)
419 std::clog
<< "\t- " << hs
->toStr() << std::endl
;
420 std::clog
<< "ExpectedHash:" << endl
;
421 for (HashStringList::const_iterator hs
= ExpectedHashes
.begin(); hs
!= ExpectedHashes
.end(); ++hs
)
422 std::clog
<< "\t- " << hs
->toStr() << std::endl
;
426 // decide if what we got is what we expected
427 bool consideredOkay
= false;
428 if ((forcedHash
.empty() && ExpectedHashes
.empty() == false) ||
429 (forcedHash
.empty() == false && ExpectedHashes
.usable()))
431 if (ReceivedHashes
.empty())
433 /* IMS-Hits can't be checked here as we will have uncompressed file,
434 but the hashes for the compressed file. What we have was good through
435 so all we have to ensure later is that we are not stalled. */
436 consideredOkay
= isIMSHit
;
438 else if (ReceivedHashes
== ExpectedHashes
)
439 consideredOkay
= true;
441 consideredOkay
= false;
445 consideredOkay
= !Owner
->HashesRequired();
447 if (consideredOkay
== true)
448 consideredOkay
= Owner
->VerifyDone(Message
, Config
);
449 else // hashsum mismatch
450 Owner
->Status
= pkgAcquire::Item::StatAuthError
;
453 if (consideredOkay
== true)
455 if (isDoomedItem(Owner
) == false)
456 Owner
->Done(Message
, ReceivedHashes
, Config
);
460 Log
->IMSHit(Owner
->GetItemDesc());
462 Log
->Done(Owner
->GetItemDesc());
467 auto SavedDesc
= Owner
->GetItemDesc();
468 if (isDoomedItem(Owner
) == false)
470 if (Message
.find("\nFailReason:") == std::string::npos
)
472 if (ReceivedHashes
!= ExpectedHashes
)
473 Message
.append("\nFailReason: HashSumMismatch");
475 Message
.append("\nFailReason: WeakHashSums");
477 Owner
->Failed(Message
,Config
);
480 Log
->Fail(SavedDesc
);
487 case MessageType::URI_FAILURE
:
491 std::string
const msg
= LookupTag(Message
,"Message");
492 _error
->Error("Method gave invalid 400 URI Failure message: %s", msg
.c_str());
496 PrepareFiles("400::URIFailure", Itm
);
498 // Display update before completion
499 if (Log
!= nullptr && Log
->MorePulses
== true)
500 for (pkgAcquire::Queue::QItem::owner_iterator O
= Itm
->Owners
.begin(); O
!= Itm
->Owners
.end(); ++O
)
501 Log
->Pulse((*O
)->GetOwner());
503 std::vector
<Item
*> const ItmOwners
= Itm
->Owners
;
504 OwnerQ
->ItemDone(Itm
);
507 bool errTransient
= false, errAuthErr
= false;
509 std::string
const failReason
= LookupTag(Message
, "FailReason");
511 auto const reasons
= { "Timeout", "ConnectionRefused",
512 "ConnectionTimedOut", "ResolveFailure", "TmpResolveFailure" };
513 errTransient
= std::find(std::begin(reasons
), std::end(reasons
), failReason
) != std::end(reasons
);
515 if (errTransient
== false)
517 auto const reasons
= { "HashSumMismatch", "WeakHashSums", "MaximumSizeExceeded" };
518 errAuthErr
= std::find(std::begin(reasons
), std::end(reasons
), failReason
) != std::end(reasons
);
522 for (auto const Owner
: ItmOwners
)
524 if (errAuthErr
&& Owner
->GetExpectedHashes().empty() == false)
525 Owner
->Status
= pkgAcquire::Item::StatAuthError
;
526 else if (errTransient
)
527 Owner
->Status
= pkgAcquire::Item::StatTransientNetworkError
;
528 auto SavedDesc
= Owner
->GetItemDesc();
529 if (isDoomedItem(Owner
) == false)
530 Owner
->Failed(Message
,Config
);
532 Log
->Fail(SavedDesc
);
539 case MessageType::GENERAL_FAILURE
:
540 _error
->Error("Method %s General failure: %s",Access
.c_str(),LookupTag(Message
,"Message").c_str());
543 case MessageType::MEDIA_CHANGE
:
544 MediaChange(Message
);
551 // Worker::Capabilities - 100 Capabilities handler /*{{{*/
552 // ---------------------------------------------------------------------
553 /* This parses the capabilities message and dumps it into the configuration
555 bool pkgAcquire::Worker::Capabilities(string Message
)
560 Config
->Version
= LookupTag(Message
,"Version");
561 Config
->SingleInstance
= StringToBool(LookupTag(Message
,"Single-Instance"),false);
562 Config
->Pipeline
= StringToBool(LookupTag(Message
,"Pipeline"),false);
563 Config
->SendConfig
= StringToBool(LookupTag(Message
,"Send-Config"),false);
564 Config
->LocalOnly
= StringToBool(LookupTag(Message
,"Local-Only"),false);
565 Config
->NeedsCleanup
= StringToBool(LookupTag(Message
,"Needs-Cleanup"),false);
566 Config
->Removable
= StringToBool(LookupTag(Message
,"Removable"),false);
571 clog
<< "Configured access method " << Config
->Access
<< endl
;
572 clog
<< "Version:" << Config
->Version
<<
573 " SingleInstance:" << Config
->SingleInstance
<<
574 " Pipeline:" << Config
->Pipeline
<<
575 " SendConfig:" << Config
->SendConfig
<<
576 " LocalOnly: " << Config
->LocalOnly
<<
577 " NeedsCleanup: " << Config
->NeedsCleanup
<<
578 " Removable: " << Config
->Removable
<< endl
;
584 // Worker::MediaChange - Request a media change /*{{{*/
585 // ---------------------------------------------------------------------
587 bool pkgAcquire::Worker::MediaChange(string Message
)
589 int status_fd
= _config
->FindI("APT::Status-Fd",-1);
592 string Media
= LookupTag(Message
,"Media");
593 string Drive
= LookupTag(Message
,"Drive");
594 ostringstream msg
,status
;
595 ioprintf(msg
,_("Please insert the disc labeled: "
597 "in the drive '%s' and press [Enter]."),
598 Media
.c_str(),Drive
.c_str());
599 status
<< "media-change: " // message
600 << Media
<< ":" // media
601 << Drive
<< ":" // drive
602 << msg
.str() // l10n message
605 std::string
const dlstatus
= status
.str();
606 FileFd::Write(status_fd
, dlstatus
.c_str(), dlstatus
.size());
609 if (Log
== 0 || Log
->MediaChange(LookupTag(Message
,"Media"),
610 LookupTag(Message
,"Drive")) == false)
613 snprintf(S
,sizeof(S
),"603 Media Changed\nFailed: true\n\n");
615 clog
<< " -> " << Access
<< ':' << QuoteString(S
,"\n") << endl
;
622 snprintf(S
,sizeof(S
),"603 Media Changed\n\n");
624 clog
<< " -> " << Access
<< ':' << QuoteString(S
,"\n") << endl
;
630 // Worker::SendConfiguration - Send the config to the method /*{{{*/
631 // ---------------------------------------------------------------------
633 bool pkgAcquire::Worker::SendConfiguration()
635 if (Config
->SendConfig
== false)
641 /* Write out all of the configuration directives by walking the
642 configuration tree */
643 std::ostringstream Message
;
644 Message
<< "601 Configuration\n";
645 _config
->Dump(Message
, NULL
, "Config-Item: %F=%V\n", false);
649 clog
<< " -> " << Access
<< ':' << QuoteString(Message
.str(),"\n") << endl
;
650 OutQueue
+= Message
.str();
656 // Worker::QueueItem - Add an item to the outbound queue /*{{{*/
657 // ---------------------------------------------------------------------
658 /* Send a URI Acquire message to the method */
659 bool pkgAcquire::Worker::QueueItem(pkgAcquire::Queue::QItem
*Item
)
664 if (isDoomedItem(Item
->Owner
))
667 string Message
= "600 URI Acquire\n";
668 Message
.reserve(300);
669 Message
+= "URI: " + Item
->URI
;
670 Message
+= "\nFilename: " + Item
->Owner
->DestFile
;
672 HashStringList
const hsl
= Item
->GetExpectedHashes();
673 for (HashStringList::const_iterator hs
= hsl
.begin(); hs
!= hsl
.end(); ++hs
)
674 Message
+= "\nExpected-" + hs
->HashType() + ": " + hs
->HashValue();
676 if (hsl
.FileSize() == 0)
678 unsigned long long FileSize
= Item
->GetMaximumSize();
682 strprintf(MaximumSize
, "%llu", FileSize
);
683 Message
+= "\nMaximum-Size: " + MaximumSize
;
687 Item
->SyncDestinationFiles();
688 Message
+= Item
->Custom600Headers();
691 if (RealFileExists(Item
->Owner
->DestFile
))
693 std::string
const SandboxUser
= _config
->Find("APT::Sandbox::User");
694 ChangeOwnerAndPermissionOfFile("Item::QueueURI", Item
->Owner
->DestFile
.c_str(),
695 SandboxUser
.c_str(), "root", 0600);
699 clog
<< " -> " << Access
<< ':' << QuoteString(Message
,"\n") << endl
;
706 // Worker::OutFdRead - Out bound FD is ready /*{{{*/
707 // ---------------------------------------------------------------------
709 bool pkgAcquire::Worker::OutFdReady()
714 Res
= write(OutFd
,OutQueue
.c_str(),OutQueue
.length());
716 while (Res
< 0 && errno
== EINTR
);
719 return MethodFailure();
721 OutQueue
.erase(0,Res
);
722 if (OutQueue
.empty() == true)
728 // Worker::InFdRead - In bound FD is ready /*{{{*/
729 // ---------------------------------------------------------------------
731 bool pkgAcquire::Worker::InFdReady()
733 if (ReadMessages() == false)
739 // Worker::MethodFailure - Called when the method fails /*{{{*/
740 // ---------------------------------------------------------------------
741 /* This is called when the method is believed to have failed, probably because
743 bool pkgAcquire::Worker::MethodFailure()
745 _error
->Error("Method %s has died unexpectedly!",Access
.c_str());
747 // do not reap the child here to show meaningfull error to the user
748 ExecWait(Process
,Access
.c_str(),false);
757 MessageQueue
.erase(MessageQueue
.begin(),MessageQueue
.end());
762 // Worker::Pulse - Called periodically /*{{{*/
763 // ---------------------------------------------------------------------
765 void pkgAcquire::Worker::Pulse()
767 if (CurrentItem
== 0)
771 if (stat(CurrentItem
->Owner
->DestFile
.c_str(),&Buf
) != 0)
773 CurrentSize
= Buf
.st_size
;
776 // Worker::ItemDone - Called when the current item is finished /*{{{*/
777 // ---------------------------------------------------------------------
779 void pkgAcquire::Worker::ItemDone()
787 void pkgAcquire::Worker::PrepareFiles(char const * const caller
, pkgAcquire::Queue::QItem
const * const Itm
)/*{{{*/
789 if (RealFileExists(Itm
->Owner
->DestFile
))
791 ChangeOwnerAndPermissionOfFile(caller
, Itm
->Owner
->DestFile
.c_str(), "root", "root", 0644);
792 std::string
const filename
= Itm
->Owner
->DestFile
;
793 for (pkgAcquire::Queue::QItem::owner_iterator O
= Itm
->Owners
.begin(); O
!= Itm
->Owners
.end(); ++O
)
795 pkgAcquire::Item
const * const Owner
= *O
;
796 if (Owner
->DestFile
== filename
|| filename
== "/dev/null")
798 RemoveFile("PrepareFiles", Owner
->DestFile
);
799 if (link(filename
.c_str(), Owner
->DestFile
.c_str()) != 0)
801 // different mounts can't happen for us as we download to lists/ by default,
802 // but if the system is reused by others the locations can potentially be on
803 // different disks, so use symlink as poor-men replacement.
804 // FIXME: Real copying as last fallback, but that is costly, so offload to a method preferable
805 if (symlink(filename
.c_str(), Owner
->DestFile
.c_str()) != 0)
806 _error
->Error("Can't create (sym)link of file %s to %s", filename
.c_str(), Owner
->DestFile
.c_str());
812 for (pkgAcquire::Queue::QItem::owner_iterator O
= Itm
->Owners
.begin(); O
!= Itm
->Owners
.end(); ++O
)
813 RemoveFile("PrepareFiles", (*O
)->DestFile
);