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 if (isDoomedItem(Owner
) == false)
469 if (Message
.find("\nFailReason:") == std::string::npos
)
471 if (ReceivedHashes
!= ExpectedHashes
)
472 Message
.append("\nFailReason: HashSumMismatch");
474 Message
.append("\nFailReason: WeakHashSums");
476 Owner
->Failed(Message
,Config
);
479 Log
->Fail(Owner
->GetItemDesc());
486 case MessageType::URI_FAILURE
:
490 std::string
const msg
= LookupTag(Message
,"Message");
491 _error
->Error("Method gave invalid 400 URI Failure message: %s", msg
.c_str());
495 PrepareFiles("400::URIFailure", Itm
);
497 // Display update before completion
498 if (Log
!= nullptr && Log
->MorePulses
== true)
499 for (pkgAcquire::Queue::QItem::owner_iterator O
= Itm
->Owners
.begin(); O
!= Itm
->Owners
.end(); ++O
)
500 Log
->Pulse((*O
)->GetOwner());
502 std::vector
<Item
*> const ItmOwners
= Itm
->Owners
;
503 OwnerQ
->ItemDone(Itm
);
506 bool errTransient
= false, errAuthErr
= false;
508 std::string
const failReason
= LookupTag(Message
, "FailReason");
510 auto const reasons
= { "Timeout", "ConnectionRefused",
511 "ConnectionTimedOut", "ResolveFailure", "TmpResolveFailure" };
512 errTransient
= std::find(std::begin(reasons
), std::end(reasons
), failReason
) != std::end(reasons
);
514 if (errTransient
== false)
516 auto const reasons
= { "HashSumMismatch", "WeakHashSums", "MaximumSizeExceeded" };
517 errAuthErr
= std::find(std::begin(reasons
), std::end(reasons
), failReason
) != std::end(reasons
);
521 for (auto const Owner
: ItmOwners
)
523 if (errAuthErr
&& Owner
->GetExpectedHashes().empty() == false)
524 Owner
->Status
= pkgAcquire::Item::StatAuthError
;
525 else if (errTransient
)
526 Owner
->Status
= pkgAcquire::Item::StatTransientNetworkError
;
528 if (isDoomedItem(Owner
) == false)
529 Owner
->Failed(Message
,Config
);
531 Log
->Fail(Owner
->GetItemDesc());
538 case MessageType::GENERAL_FAILURE
:
539 _error
->Error("Method %s General failure: %s",Access
.c_str(),LookupTag(Message
,"Message").c_str());
542 case MessageType::MEDIA_CHANGE
:
543 MediaChange(Message
);
550 // Worker::Capabilities - 100 Capabilities handler /*{{{*/
551 // ---------------------------------------------------------------------
552 /* This parses the capabilities message and dumps it into the configuration
554 bool pkgAcquire::Worker::Capabilities(string Message
)
559 Config
->Version
= LookupTag(Message
,"Version");
560 Config
->SingleInstance
= StringToBool(LookupTag(Message
,"Single-Instance"),false);
561 Config
->Pipeline
= StringToBool(LookupTag(Message
,"Pipeline"),false);
562 Config
->SendConfig
= StringToBool(LookupTag(Message
,"Send-Config"),false);
563 Config
->LocalOnly
= StringToBool(LookupTag(Message
,"Local-Only"),false);
564 Config
->NeedsCleanup
= StringToBool(LookupTag(Message
,"Needs-Cleanup"),false);
565 Config
->Removable
= StringToBool(LookupTag(Message
,"Removable"),false);
570 clog
<< "Configured access method " << Config
->Access
<< endl
;
571 clog
<< "Version:" << Config
->Version
<<
572 " SingleInstance:" << Config
->SingleInstance
<<
573 " Pipeline:" << Config
->Pipeline
<<
574 " SendConfig:" << Config
->SendConfig
<<
575 " LocalOnly: " << Config
->LocalOnly
<<
576 " NeedsCleanup: " << Config
->NeedsCleanup
<<
577 " Removable: " << Config
->Removable
<< endl
;
583 // Worker::MediaChange - Request a media change /*{{{*/
584 // ---------------------------------------------------------------------
586 bool pkgAcquire::Worker::MediaChange(string Message
)
588 int status_fd
= _config
->FindI("APT::Status-Fd",-1);
591 string Media
= LookupTag(Message
,"Media");
592 string Drive
= LookupTag(Message
,"Drive");
593 ostringstream msg
,status
;
594 ioprintf(msg
,_("Please insert the disc labeled: "
596 "in the drive '%s' and press [Enter]."),
597 Media
.c_str(),Drive
.c_str());
598 status
<< "media-change: " // message
599 << Media
<< ":" // media
600 << Drive
<< ":" // drive
601 << msg
.str() // l10n message
604 std::string
const dlstatus
= status
.str();
605 FileFd::Write(status_fd
, dlstatus
.c_str(), dlstatus
.size());
608 if (Log
== 0 || Log
->MediaChange(LookupTag(Message
,"Media"),
609 LookupTag(Message
,"Drive")) == false)
612 snprintf(S
,sizeof(S
),"603 Media Changed\nFailed: true\n\n");
614 clog
<< " -> " << Access
<< ':' << QuoteString(S
,"\n") << endl
;
621 snprintf(S
,sizeof(S
),"603 Media Changed\n\n");
623 clog
<< " -> " << Access
<< ':' << QuoteString(S
,"\n") << endl
;
629 // Worker::SendConfiguration - Send the config to the method /*{{{*/
630 // ---------------------------------------------------------------------
632 bool pkgAcquire::Worker::SendConfiguration()
634 if (Config
->SendConfig
== false)
640 /* Write out all of the configuration directives by walking the
641 configuration tree */
642 std::ostringstream Message
;
643 Message
<< "601 Configuration\n";
644 _config
->Dump(Message
, NULL
, "Config-Item: %F=%V\n", false);
648 clog
<< " -> " << Access
<< ':' << QuoteString(Message
.str(),"\n") << endl
;
649 OutQueue
+= Message
.str();
655 // Worker::QueueItem - Add an item to the outbound queue /*{{{*/
656 // ---------------------------------------------------------------------
657 /* Send a URI Acquire message to the method */
658 bool pkgAcquire::Worker::QueueItem(pkgAcquire::Queue::QItem
*Item
)
663 HashStringList
const hsl
= Item
->GetExpectedHashes();
665 if (isDoomedItem(Item
->Owner
))
668 if (hsl
.usable() == false && Item
->Owner
->HashesRequired() &&
669 _config
->Exists("Acquire::ForceHash") == false)
671 std::string
const Message
= "400 URI Failure"
672 "\nURI: " + Item
->URI
+
673 "\nFilename: " + Item
->Owner
->DestFile
+
674 "\nFailReason: WeakHashSums";
676 auto const ItmOwners
= Item
->Owners
;
677 for (auto &O
: ItmOwners
)
679 O
->Status
= pkgAcquire::Item::StatAuthError
;
680 O
->Failed(Message
, Config
);
682 Log
->Fail(O
->GetItemDesc());
684 // "queued" successfully, the item just instantly failed
688 string Message
= "600 URI Acquire\n";
689 Message
.reserve(300);
690 Message
+= "URI: " + Item
->URI
;
691 Message
+= "\nFilename: " + Item
->Owner
->DestFile
;
693 for (HashStringList::const_iterator hs
= hsl
.begin(); hs
!= hsl
.end(); ++hs
)
694 Message
+= "\nExpected-" + hs
->HashType() + ": " + hs
->HashValue();
696 if (hsl
.FileSize() == 0)
698 unsigned long long FileSize
= Item
->GetMaximumSize();
702 strprintf(MaximumSize
, "%llu", FileSize
);
703 Message
+= "\nMaximum-Size: " + MaximumSize
;
707 Item
->SyncDestinationFiles();
708 Message
+= Item
->Custom600Headers();
711 if (RealFileExists(Item
->Owner
->DestFile
))
713 std::string
const SandboxUser
= _config
->Find("APT::Sandbox::User");
714 ChangeOwnerAndPermissionOfFile("Item::QueueURI", Item
->Owner
->DestFile
.c_str(),
715 SandboxUser
.c_str(), "root", 0600);
719 clog
<< " -> " << Access
<< ':' << QuoteString(Message
,"\n") << endl
;
726 // Worker::OutFdRead - Out bound FD is ready /*{{{*/
727 // ---------------------------------------------------------------------
729 bool pkgAcquire::Worker::OutFdReady()
734 Res
= write(OutFd
,OutQueue
.c_str(),OutQueue
.length());
736 while (Res
< 0 && errno
== EINTR
);
739 return MethodFailure();
741 OutQueue
.erase(0,Res
);
742 if (OutQueue
.empty() == true)
748 // Worker::InFdRead - In bound FD is ready /*{{{*/
749 // ---------------------------------------------------------------------
751 bool pkgAcquire::Worker::InFdReady()
753 if (ReadMessages() == false)
759 // Worker::MethodFailure - Called when the method fails /*{{{*/
760 // ---------------------------------------------------------------------
761 /* This is called when the method is believed to have failed, probably because
763 bool pkgAcquire::Worker::MethodFailure()
765 _error
->Error("Method %s has died unexpectedly!",Access
.c_str());
767 // do not reap the child here to show meaningfull error to the user
768 ExecWait(Process
,Access
.c_str(),false);
777 MessageQueue
.erase(MessageQueue
.begin(),MessageQueue
.end());
782 // Worker::Pulse - Called periodically /*{{{*/
783 // ---------------------------------------------------------------------
785 void pkgAcquire::Worker::Pulse()
787 if (CurrentItem
== 0)
791 if (stat(CurrentItem
->Owner
->DestFile
.c_str(),&Buf
) != 0)
793 CurrentSize
= Buf
.st_size
;
796 // Worker::ItemDone - Called when the current item is finished /*{{{*/
797 // ---------------------------------------------------------------------
799 void pkgAcquire::Worker::ItemDone()
807 void pkgAcquire::Worker::PrepareFiles(char const * const caller
, pkgAcquire::Queue::QItem
const * const Itm
)/*{{{*/
809 if (RealFileExists(Itm
->Owner
->DestFile
))
811 ChangeOwnerAndPermissionOfFile(caller
, Itm
->Owner
->DestFile
.c_str(), "root", "root", 0644);
812 std::string
const filename
= Itm
->Owner
->DestFile
;
813 for (pkgAcquire::Queue::QItem::owner_iterator O
= Itm
->Owners
.begin(); O
!= Itm
->Owners
.end(); ++O
)
815 pkgAcquire::Item
const * const Owner
= *O
;
816 if (Owner
->DestFile
== filename
|| filename
== "/dev/null")
818 RemoveFile("PrepareFiles", Owner
->DestFile
);
819 if (link(filename
.c_str(), Owner
->DestFile
.c_str()) != 0)
821 // different mounts can't happen for us as we download to lists/ by default,
822 // but if the system is reused by others the locations can potentially be on
823 // different disks, so use symlink as poor-men replacement.
824 // FIXME: Real copying as last fallback, but that is costly, so offload to a method preferable
825 if (symlink(filename
.c_str(), Owner
->DestFile
.c_str()) != 0)
826 _error
->Error("Can't create (sym)link of file %s to %s", filename
.c_str(), Owner
->DestFile
.c_str());
832 for (pkgAcquire::Queue::QItem::owner_iterator O
= Itm
->Owners
.begin(); O
!= Itm
->Owners
.end(); ++O
)
833 RemoveFile("PrepareFiles", (*O
)->DestFile
);