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