]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-worker.cc
enhance apt-key debugging options
[apt.git] / apt-pkg / acquire-worker.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: acquire-worker.cc,v 1.34 2001/05/22 04:42:54 jgg Exp $
4 /* ######################################################################
5
6 Acquire Worker
7
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
11
12 ##################################################################### */
13 /*}}}*/
14 // Include Files /*{{{*/
15 #include <config.h>
16
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>
25
26 #include <string>
27 #include <vector>
28 #include <iostream>
29 #include <sstream>
30
31 #include <sys/stat.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <signal.h>
35 #include <stdio.h>
36 #include <errno.h>
37 #include <sys/types.h>
38 #include <pwd.h>
39 #include <grp.h>
40
41 #include <apti18n.h>
42 /*}}}*/
43
44 using namespace std;
45
46 // Worker::Worker - Constructor for Queue startup /*{{{*/
47 // ---------------------------------------------------------------------
48 /* */
49 pkgAcquire::Worker::Worker(Queue *Q,MethodConfig *Cnf,
50 pkgAcquireStatus *log) : d(NULL), Log(log)
51 {
52 OwnerQ = Q;
53 Config = Cnf;
54 Access = Cnf->Access;
55 CurrentItem = 0;
56 TotalSize = 0;
57 CurrentSize = 0;
58
59 Construct();
60 }
61 /*}}}*/
62 // Worker::Worker - Constructor for method config startup /*{{{*/
63 // ---------------------------------------------------------------------
64 /* */
65 pkgAcquire::Worker::Worker(MethodConfig *Cnf) : d(NULL), OwnerQ(NULL), Config(Cnf),
66 Access(Cnf->Access), CurrentItem(NULL),
67 CurrentSize(0), TotalSize(0)
68 {
69 Construct();
70 }
71 /*}}}*/
72 // Worker::Construct - Constructor helper /*{{{*/
73 // ---------------------------------------------------------------------
74 /* */
75 void pkgAcquire::Worker::Construct()
76 {
77 NextQueue = 0;
78 NextAcquire = 0;
79 Process = -1;
80 InFd = -1;
81 OutFd = -1;
82 OutReady = false;
83 InReady = false;
84 Debug = _config->FindB("Debug::pkgAcquire::Worker",false);
85 }
86 /*}}}*/
87 // Worker::~Worker - Destructor /*{{{*/
88 // ---------------------------------------------------------------------
89 /* */
90 pkgAcquire::Worker::~Worker()
91 {
92 close(InFd);
93 close(OutFd);
94
95 if (Process > 0)
96 {
97 /* Closing of stdin is the signal to exit and die when the process
98 indicates it needs cleanup */
99 if (Config->NeedsCleanup == false)
100 kill(Process,SIGINT);
101 ExecWait(Process,Access.c_str(),true);
102 }
103 }
104 /*}}}*/
105 // Worker::Start - Start the worker process /*{{{*/
106 // ---------------------------------------------------------------------
107 /* This forks the method and inits the communication channel */
108 bool pkgAcquire::Worker::Start()
109 {
110 // Get the method path
111 string Method = _config->FindDir("Dir::Bin::Methods") + Access;
112 if (FileExists(Method) == false)
113 {
114 _error->Error(_("The method driver %s could not be found."),Method.c_str());
115 if (Access == "https")
116 _error->Notice(_("Is the package %s installed?"), "apt-transport-https");
117 return false;
118 }
119
120 if (Debug == true)
121 clog << "Starting method '" << Method << '\'' << endl;
122
123 // Create the pipes
124 int Pipes[4] = {-1,-1,-1,-1};
125 if (pipe(Pipes) != 0 || pipe(Pipes+2) != 0)
126 {
127 _error->Errno("pipe","Failed to create IPC pipe to subprocess");
128 for (int I = 0; I != 4; I++)
129 close(Pipes[I]);
130 return false;
131 }
132 for (int I = 0; I != 4; I++)
133 SetCloseExec(Pipes[I],true);
134
135 // Fork off the process
136 Process = ExecFork();
137 if (Process == 0)
138 {
139 // Setup the FDs
140 dup2(Pipes[1],STDOUT_FILENO);
141 dup2(Pipes[2],STDIN_FILENO);
142 SetCloseExec(STDOUT_FILENO,false);
143 SetCloseExec(STDIN_FILENO,false);
144 SetCloseExec(STDERR_FILENO,false);
145
146 const char *Args[2];
147 Args[0] = Method.c_str();
148 Args[1] = 0;
149 execv(Args[0],(char **)Args);
150 cerr << "Failed to exec method " << Args[0] << endl;
151 _exit(100);
152 }
153
154 // Fix up our FDs
155 InFd = Pipes[0];
156 OutFd = Pipes[3];
157 SetNonBlock(Pipes[0],true);
158 SetNonBlock(Pipes[3],true);
159 close(Pipes[1]);
160 close(Pipes[2]);
161 OutReady = false;
162 InReady = true;
163
164 // Read the configuration data
165 if (WaitFd(InFd) == false ||
166 ReadMessages() == false)
167 return _error->Error(_("Method %s did not start correctly"),Method.c_str());
168
169 RunMessages();
170 if (OwnerQ != 0)
171 SendConfiguration();
172
173 return true;
174 }
175 /*}}}*/
176 // Worker::ReadMessages - Read all pending messages into the list /*{{{*/
177 // ---------------------------------------------------------------------
178 /* */
179 bool pkgAcquire::Worker::ReadMessages()
180 {
181 if (::ReadMessages(InFd,MessageQueue) == false)
182 return MethodFailure();
183 return true;
184 }
185 /*}}}*/
186 // Worker::RunMessage - Empty the message queue /*{{{*/
187 // ---------------------------------------------------------------------
188 /* This takes the messages from the message queue and runs them through
189 the parsers in order. */
190 bool pkgAcquire::Worker::RunMessages()
191 {
192 while (MessageQueue.empty() == false)
193 {
194 string Message = MessageQueue.front();
195 MessageQueue.erase(MessageQueue.begin());
196
197 if (Debug == true)
198 clog << " <- " << Access << ':' << QuoteString(Message,"\n") << endl;
199
200 // Fetch the message number
201 char *End;
202 int Number = strtol(Message.c_str(),&End,10);
203 if (End == Message.c_str())
204 return _error->Error("Invalid message from method %s: %s",Access.c_str(),Message.c_str());
205
206 string URI = LookupTag(Message,"URI");
207 pkgAcquire::Queue::QItem *Itm = 0;
208 if (URI.empty() == false)
209 Itm = OwnerQ->FindItem(URI,this);
210
211 // update used mirror
212 string UsedMirror = LookupTag(Message,"UsedMirror", "");
213 if (!UsedMirror.empty() &&
214 Itm &&
215 Itm->Description.find(" ") != string::npos)
216 {
217 Itm->Description.replace(0, Itm->Description.find(" "), UsedMirror);
218 // FIXME: will we need this as well?
219 //Itm->ShortDesc = UsedMirror;
220 }
221
222 // Determine the message number and dispatch
223 switch (Number)
224 {
225 // 100 Capabilities
226 case 100:
227 if (Capabilities(Message) == false)
228 return _error->Error("Unable to process Capabilities message from %s",Access.c_str());
229 break;
230
231 // 101 Log
232 case 101:
233 if (Debug == true)
234 clog << " <- (log) " << LookupTag(Message,"Message") << endl;
235 break;
236
237 // 102 Status
238 case 102:
239 Status = LookupTag(Message,"Message");
240 break;
241
242 // 103 Redirect
243 case 103:
244 {
245 if (Itm == 0)
246 {
247 _error->Error("Method gave invalid 103 Redirect message");
248 break;
249 }
250
251 string NewURI = LookupTag(Message,"New-URI",URI.c_str());
252 Itm->URI = NewURI;
253
254 ItemDone();
255
256 // Change the status so that it can be dequeued
257 for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O)
258 {
259 pkgAcquire::Item *Owner = *O;
260 Owner->Status = pkgAcquire::Item::StatIdle;
261 }
262 // Mark the item as done (taking care of all queues)
263 // and then put it in the main queue again
264 std::vector<Item*> const ItmOwners = Itm->Owners;
265 OwnerQ->ItemDone(Itm);
266 Itm = NULL;
267 for (pkgAcquire::Queue::QItem::owner_iterator O = ItmOwners.begin(); O != ItmOwners.end(); ++O)
268 {
269 pkgAcquire::Item *Owner = *O;
270 pkgAcquire::ItemDesc desc = Owner->GetItemDesc();
271 desc.URI = NewURI;
272 OwnerQ->Owner->Enqueue(desc);
273
274 if (Log != 0)
275 Log->Done(Owner->GetItemDesc());
276 }
277 break;
278 }
279
280 // 200 URI Start
281 case 200:
282 {
283 if (Itm == 0)
284 {
285 _error->Error("Method gave invalid 200 URI Start message");
286 break;
287 }
288
289 CurrentItem = Itm;
290 CurrentSize = 0;
291 TotalSize = strtoull(LookupTag(Message,"Size","0").c_str(), NULL, 10);
292 ResumePoint = strtoull(LookupTag(Message,"Resume-Point","0").c_str(), NULL, 10);
293 for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O)
294 {
295 (*O)->Start(Message, TotalSize);
296
297 // Display update before completion
298 if (Log != 0)
299 {
300 if (Log->MorePulses == true)
301 Log->Pulse((*O)->GetOwner());
302 Log->Fetch((*O)->GetItemDesc());
303 }
304 }
305
306 break;
307 }
308
309 // 201 URI Done
310 case 201:
311 {
312 if (Itm == 0)
313 {
314 _error->Error("Method gave invalid 201 URI Done message");
315 break;
316 }
317
318 PrepareFiles("201::URIDone", Itm);
319
320 // Display update before completion
321 if (Log != 0 && Log->MorePulses == true)
322 for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O)
323 Log->Pulse((*O)->GetOwner());
324
325 std::string const filename = LookupTag(Message, "Filename", Itm->Owner->DestFile.c_str());
326 HashStringList ReceivedHashes;
327 {
328 // see if we got hashes to verify
329 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
330 {
331 std::string const tagname = std::string(*type) + "-Hash";
332 std::string const hashsum = LookupTag(Message, tagname.c_str());
333 if (hashsum.empty() == false)
334 ReceivedHashes.push_back(HashString(*type, hashsum));
335 }
336 // not all methods always sent Hashes our way
337 if (ReceivedHashes.usable() == false)
338 {
339 HashStringList const ExpectedHashes = Itm->GetExpectedHashes();
340 if (ExpectedHashes.usable() == true && RealFileExists(filename))
341 {
342 Hashes calc(ExpectedHashes);
343 FileFd file(filename, FileFd::ReadOnly, FileFd::None);
344 calc.AddFD(file);
345 ReceivedHashes = calc.GetHashStringList();
346 }
347 }
348 }
349
350 // only local files can refer other filenames and counting them as fetched would be unfair
351 if (Log != NULL && filename != Itm->Owner->DestFile)
352 Log->Fetched(ReceivedHashes.FileSize(),atoi(LookupTag(Message,"Resume-Point","0").c_str()));
353
354 std::vector<Item*> const ItmOwners = Itm->Owners;
355 OwnerQ->ItemDone(Itm);
356 Itm = NULL;
357
358 bool const isIMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false) ||
359 StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false);
360 for (pkgAcquire::Queue::QItem::owner_iterator O = ItmOwners.begin(); O != ItmOwners.end(); ++O)
361 {
362 pkgAcquire::Item * const Owner = *O;
363 HashStringList const ExpectedHashes = Owner->GetExpectedHashes();
364 if(_config->FindB("Debug::pkgAcquire::Auth", false) == true)
365 {
366 std::clog << "201 URI Done: " << Owner->DescURI() << endl
367 << "ReceivedHash:" << endl;
368 for (HashStringList::const_iterator hs = ReceivedHashes.begin(); hs != ReceivedHashes.end(); ++hs)
369 std::clog << "\t- " << hs->toStr() << std::endl;
370 std::clog << "ExpectedHash:" << endl;
371 for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
372 std::clog << "\t- " << hs->toStr() << std::endl;
373 std::clog << endl;
374 }
375
376 // decide if what we got is what we expected
377 bool consideredOkay = false;
378 if (ExpectedHashes.usable())
379 {
380 if (ReceivedHashes.usable() == false)
381 {
382 /* IMS-Hits can't be checked here as we will have uncompressed file,
383 but the hashes for the compressed file. What we have was good through
384 so all we have to ensure later is that we are not stalled. */
385 consideredOkay = isIMSHit;
386 }
387 else if (ReceivedHashes == ExpectedHashes)
388 consideredOkay = true;
389 else
390 consideredOkay = false;
391
392 }
393 else if (Owner->HashesRequired() == true)
394 consideredOkay = false;
395 else
396 consideredOkay = true;
397
398 if (consideredOkay == true)
399 {
400 Owner->Done(Message, ReceivedHashes, Config);
401
402 // Log that we are done
403 if (Log != 0)
404 {
405 if (isIMSHit)
406 Log->IMSHit(Owner->GetItemDesc());
407 else
408 Log->Done(Owner->GetItemDesc());
409 }
410 }
411 else
412 {
413 Owner->Status = pkgAcquire::Item::StatAuthError;
414 Owner->Failed(Message,Config);
415
416 if (Log != 0)
417 Log->Fail(Owner->GetItemDesc());
418 }
419 }
420 ItemDone();
421 break;
422 }
423
424 // 400 URI Failure
425 case 400:
426 {
427 if (Itm == 0)
428 {
429 std::string const msg = LookupTag(Message,"Message");
430 _error->Error("Method gave invalid 400 URI Failure message: %s", msg.c_str());
431 break;
432 }
433
434 PrepareFiles("400::URIFailure", Itm);
435
436 // Display update before completion
437 if (Log != 0 && Log->MorePulses == true)
438 for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O)
439 Log->Pulse((*O)->GetOwner());
440
441 std::vector<Item*> const ItmOwners = Itm->Owners;
442 OwnerQ->ItemDone(Itm);
443 Itm = NULL;
444
445 for (pkgAcquire::Queue::QItem::owner_iterator O = ItmOwners.begin(); O != ItmOwners.end(); ++O)
446 {
447 // set some status
448 if(LookupTag(Message,"FailReason") == "Timeout" ||
449 LookupTag(Message,"FailReason") == "TmpResolveFailure" ||
450 LookupTag(Message,"FailReason") == "ResolveFailure" ||
451 LookupTag(Message,"FailReason") == "ConnectionRefused")
452 (*O)->Status = pkgAcquire::Item::StatTransientNetworkError;
453
454 (*O)->Failed(Message,Config);
455
456 if (Log != 0)
457 Log->Fail((*O)->GetItemDesc());
458 }
459 ItemDone();
460
461 break;
462 }
463
464 // 401 General Failure
465 case 401:
466 _error->Error("Method %s General failure: %s",Access.c_str(),LookupTag(Message,"Message").c_str());
467 break;
468
469 // 403 Media Change
470 case 403:
471 MediaChange(Message);
472 break;
473 }
474 }
475 return true;
476 }
477 /*}}}*/
478 // Worker::Capabilities - 100 Capabilities handler /*{{{*/
479 // ---------------------------------------------------------------------
480 /* This parses the capabilities message and dumps it into the configuration
481 structure. */
482 bool pkgAcquire::Worker::Capabilities(string Message)
483 {
484 if (Config == 0)
485 return true;
486
487 Config->Version = LookupTag(Message,"Version");
488 Config->SingleInstance = StringToBool(LookupTag(Message,"Single-Instance"),false);
489 Config->Pipeline = StringToBool(LookupTag(Message,"Pipeline"),false);
490 Config->SendConfig = StringToBool(LookupTag(Message,"Send-Config"),false);
491 Config->LocalOnly = StringToBool(LookupTag(Message,"Local-Only"),false);
492 Config->NeedsCleanup = StringToBool(LookupTag(Message,"Needs-Cleanup"),false);
493 Config->Removable = StringToBool(LookupTag(Message,"Removable"),false);
494
495 // Some debug text
496 if (Debug == true)
497 {
498 clog << "Configured access method " << Config->Access << endl;
499 clog << "Version:" << Config->Version <<
500 " SingleInstance:" << Config->SingleInstance <<
501 " Pipeline:" << Config->Pipeline <<
502 " SendConfig:" << Config->SendConfig <<
503 " LocalOnly: " << Config->LocalOnly <<
504 " NeedsCleanup: " << Config->NeedsCleanup <<
505 " Removable: " << Config->Removable << endl;
506 }
507
508 return true;
509 }
510 /*}}}*/
511 // Worker::MediaChange - Request a media change /*{{{*/
512 // ---------------------------------------------------------------------
513 /* */
514 bool pkgAcquire::Worker::MediaChange(string Message)
515 {
516 int status_fd = _config->FindI("APT::Status-Fd",-1);
517 if(status_fd > 0)
518 {
519 string Media = LookupTag(Message,"Media");
520 string Drive = LookupTag(Message,"Drive");
521 ostringstream msg,status;
522 ioprintf(msg,_("Please insert the disc labeled: "
523 "'%s' "
524 "in the drive '%s' and press enter."),
525 Media.c_str(),Drive.c_str());
526 status << "media-change: " // message
527 << Media << ":" // media
528 << Drive << ":" // drive
529 << msg.str() // l10n message
530 << endl;
531
532 std::string const dlstatus = status.str();
533 FileFd::Write(status_fd, dlstatus.c_str(), dlstatus.size());
534 }
535
536 if (Log == 0 || Log->MediaChange(LookupTag(Message,"Media"),
537 LookupTag(Message,"Drive")) == false)
538 {
539 char S[300];
540 snprintf(S,sizeof(S),"603 Media Changed\nFailed: true\n\n");
541 if (Debug == true)
542 clog << " -> " << Access << ':' << QuoteString(S,"\n") << endl;
543 OutQueue += S;
544 OutReady = true;
545 return true;
546 }
547
548 char S[300];
549 snprintf(S,sizeof(S),"603 Media Changed\n\n");
550 if (Debug == true)
551 clog << " -> " << Access << ':' << QuoteString(S,"\n") << endl;
552 OutQueue += S;
553 OutReady = true;
554 return true;
555 }
556 /*}}}*/
557 // Worker::SendConfiguration - Send the config to the method /*{{{*/
558 // ---------------------------------------------------------------------
559 /* */
560 bool pkgAcquire::Worker::SendConfiguration()
561 {
562 if (Config->SendConfig == false)
563 return true;
564
565 if (OutFd == -1)
566 return false;
567
568 /* Write out all of the configuration directives by walking the
569 configuration tree */
570 std::ostringstream Message;
571 Message << "601 Configuration\n";
572 _config->Dump(Message, NULL, "Config-Item: %F=%V\n", false);
573 Message << '\n';
574
575 if (Debug == true)
576 clog << " -> " << Access << ':' << QuoteString(Message.str(),"\n") << endl;
577 OutQueue += Message.str();
578 OutReady = true;
579
580 return true;
581 }
582 /*}}}*/
583 // Worker::QueueItem - Add an item to the outbound queue /*{{{*/
584 // ---------------------------------------------------------------------
585 /* Send a URI Acquire message to the method */
586 bool pkgAcquire::Worker::QueueItem(pkgAcquire::Queue::QItem *Item)
587 {
588 if (OutFd == -1)
589 return false;
590
591 string Message = "600 URI Acquire\n";
592 Message.reserve(300);
593 Message += "URI: " + Item->URI;
594 Message += "\nFilename: " + Item->Owner->DestFile;
595
596 HashStringList const hsl = Item->GetExpectedHashes();
597 for (HashStringList::const_iterator hs = hsl.begin(); hs != hsl.end(); ++hs)
598 Message += "\nExpected-" + hs->HashType() + ": " + hs->HashValue();
599
600 if (hsl.FileSize() == 0)
601 {
602 unsigned long long FileSize = Item->GetMaximumSize();
603 if(FileSize > 0)
604 {
605 string MaximumSize;
606 strprintf(MaximumSize, "%llu", FileSize);
607 Message += "\nMaximum-Size: " + MaximumSize;
608 }
609 }
610
611 Item->SyncDestinationFiles();
612 Message += Item->Custom600Headers();
613 Message += "\n\n";
614
615 if (RealFileExists(Item->Owner->DestFile))
616 {
617 std::string SandboxUser = _config->Find("APT::Sandbox::User");
618 ChangeOwnerAndPermissionOfFile("Item::QueueURI", Item->Owner->DestFile.c_str(),
619 SandboxUser.c_str(), "root", 0600);
620 }
621
622 if (Debug == true)
623 clog << " -> " << Access << ':' << QuoteString(Message,"\n") << endl;
624 OutQueue += Message;
625 OutReady = true;
626
627 return true;
628 }
629 /*}}}*/
630 // Worker::OutFdRead - Out bound FD is ready /*{{{*/
631 // ---------------------------------------------------------------------
632 /* */
633 bool pkgAcquire::Worker::OutFdReady()
634 {
635 int Res;
636 do
637 {
638 Res = write(OutFd,OutQueue.c_str(),OutQueue.length());
639 }
640 while (Res < 0 && errno == EINTR);
641
642 if (Res <= 0)
643 return MethodFailure();
644
645 OutQueue.erase(0,Res);
646 if (OutQueue.empty() == true)
647 OutReady = false;
648
649 return true;
650 }
651 /*}}}*/
652 // Worker::InFdRead - In bound FD is ready /*{{{*/
653 // ---------------------------------------------------------------------
654 /* */
655 bool pkgAcquire::Worker::InFdReady()
656 {
657 if (ReadMessages() == false)
658 return false;
659 RunMessages();
660 return true;
661 }
662 /*}}}*/
663 // Worker::MethodFailure - Called when the method fails /*{{{*/
664 // ---------------------------------------------------------------------
665 /* This is called when the method is believed to have failed, probably because
666 read returned -1. */
667 bool pkgAcquire::Worker::MethodFailure()
668 {
669 _error->Error("Method %s has died unexpectedly!",Access.c_str());
670
671 // do not reap the child here to show meaningfull error to the user
672 ExecWait(Process,Access.c_str(),false);
673 Process = -1;
674 close(InFd);
675 close(OutFd);
676 InFd = -1;
677 OutFd = -1;
678 OutReady = false;
679 InReady = false;
680 OutQueue = string();
681 MessageQueue.erase(MessageQueue.begin(),MessageQueue.end());
682
683 return false;
684 }
685 /*}}}*/
686 // Worker::Pulse - Called periodically /*{{{*/
687 // ---------------------------------------------------------------------
688 /* */
689 void pkgAcquire::Worker::Pulse()
690 {
691 if (CurrentItem == 0)
692 return;
693
694 struct stat Buf;
695 if (stat(CurrentItem->Owner->DestFile.c_str(),&Buf) != 0)
696 return;
697 CurrentSize = Buf.st_size;
698 }
699 /*}}}*/
700 // Worker::ItemDone - Called when the current item is finished /*{{{*/
701 // ---------------------------------------------------------------------
702 /* */
703 void pkgAcquire::Worker::ItemDone()
704 {
705 CurrentItem = 0;
706 CurrentSize = 0;
707 TotalSize = 0;
708 Status = string();
709 }
710 /*}}}*/
711 void pkgAcquire::Worker::PrepareFiles(char const * const caller, pkgAcquire::Queue::QItem const * const Itm)/*{{{*/
712 {
713 if (RealFileExists(Itm->Owner->DestFile))
714 {
715 ChangeOwnerAndPermissionOfFile(caller, Itm->Owner->DestFile.c_str(), "root", "root", 0644);
716 std::string const filename = Itm->Owner->DestFile;
717 for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O)
718 {
719 pkgAcquire::Item const * const Owner = *O;
720 if (Owner->DestFile == filename)
721 continue;
722 unlink(Owner->DestFile.c_str());
723 if (link(filename.c_str(), Owner->DestFile.c_str()) != 0)
724 {
725 // different mounts can't happen for us as we download to lists/ by default,
726 // but if the system is reused by others the locations can potentially be on
727 // different disks, so use symlink as poor-men replacement.
728 // FIXME: Real copying as last fallback, but that is costly, so offload to a method preferable
729 if (symlink(filename.c_str(), Owner->DestFile.c_str()) != 0)
730 _error->Error("Can't create (sym)link of file %s to %s", filename.c_str(), Owner->DestFile.c_str());
731 }
732 }
733 }
734 else
735 {
736 for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O)
737 unlink((*O)->DestFile.c_str());
738 }
739 }
740 /*}}}*/