fixup changelog
[ntk/apt.git] / apt-pkg / indexcopy.cc
CommitLineData
143abaeb
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
90f057fd 3// $Id: indexcopy.cc,v 1.10 2002/03/26 07:38:58 jgg Exp $
143abaeb
AL
4/* ######################################################################
5
6 Index Copying - Aid for copying and verifying the index files
7
8 This class helps apt-cache reconstruct a damaged index files.
9
10 ##################################################################### */
11 /*}}}*/
12// Include Files /*{{{*/
ea542140 13#include<config.h>
143abaeb
AL
14
15#include <apt-pkg/error.h>
16#include <apt-pkg/progress.h>
17#include <apt-pkg/strutl.h>
18#include <apt-pkg/fileutl.h>
19#include <apt-pkg/configuration.h>
20#include <apt-pkg/tagfile.h>
a75c6a6e
MZ
21#include <apt-pkg/indexrecords.h>
22#include <apt-pkg/md5.h>
23#include <apt-pkg/cdrom.h>
143abaeb 24
90f057fd 25#include <iostream>
a75c6a6e 26#include <sstream>
143abaeb
AL
27#include <unistd.h>
28#include <sys/stat.h>
cf440fac
DK
29#include <sys/types.h>
30#include <fcntl.h>
143abaeb 31#include <stdio.h>
650faab0 32#include <stdlib.h>
ea542140
DK
33
34#include "indexcopy.h"
35#include <apti18n.h>
143abaeb
AL
36 /*}}}*/
37
076d01b0
AL
38using namespace std;
39
22f8568d
MV
40
41
143abaeb
AL
42// IndexCopy::CopyPackages - Copy the package files from the CD /*{{{*/
43// ---------------------------------------------------------------------
44/* */
a75c6a6e
MZ
45bool IndexCopy::CopyPackages(string CDROM,string Name,vector<string> &List,
46 pkgCdromStatus *log)
143abaeb 47{
a75c6a6e 48 OpProgress *Progress = NULL;
f7f0d6c7 49 if (List.empty() == true)
143abaeb
AL
50 return true;
51
a75c6a6e
MZ
52 if(log)
53 Progress = log->GetOpProgress();
143abaeb
AL
54
55 bool NoStat = _config->FindB("APT::CDROM::Fast",false);
56 bool Debug = _config->FindB("Debug::aptcdrom",false);
57
58 // Prepare the progress indicator
650faab0 59 off_t TotalSize = 0;
f7f0d6c7 60 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
143abaeb
AL
61 {
62 struct stat Buf;
63 if (stat(string(*I + GetFileName()).c_str(),&Buf) != 0 &&
64 stat(string(*I + GetFileName() + ".gz").c_str(),&Buf) != 0)
65 return _error->Errno("stat","Stat failed for %s",
66 string(*I + GetFileName()).c_str());
67 TotalSize += Buf.st_size;
68 }
69
650faab0 70 off_t CurrentSize = 0;
143abaeb
AL
71 unsigned int NotFound = 0;
72 unsigned int WrongSize = 0;
73 unsigned int Packages = 0;
f7f0d6c7 74 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
143abaeb
AL
75 {
76 string OrigPath = string(*I,CDROM.length());
650faab0 77 off_t FileSize = 0;
143abaeb
AL
78
79 // Open the package file
80 FileFd Pkg;
8220213e 81 if (RealFileExists(*I + GetFileName()) == true)
143abaeb
AL
82 {
83 Pkg.Open(*I + GetFileName(),FileFd::ReadOnly);
84 FileSize = Pkg.Size();
85 }
86 else
87 {
88 FileFd From(*I + GetFileName() + ".gz",FileFd::ReadOnly);
89 if (_error->PendingError() == true)
90 return false;
91 FileSize = From.Size();
92
93 // Get a temp file
94 FILE *tmp = tmpfile();
95 if (tmp == 0)
96 return _error->Errno("tmpfile","Unable to create a tmp file");
97 Pkg.Fd(dup(fileno(tmp)));
98 fclose(tmp);
99
100 // Fork gzip
3826564e 101 pid_t Process = fork();
143abaeb
AL
102 if (Process < 0)
103 return _error->Errno("fork","Couldn't fork gzip");
104
105 // The child
106 if (Process == 0)
107 {
108 dup2(From.Fd(),STDIN_FILENO);
109 dup2(Pkg.Fd(),STDOUT_FILENO);
110 SetCloseExec(STDIN_FILENO,false);
111 SetCloseExec(STDOUT_FILENO,false);
112
113 const char *Args[3];
076d01b0
AL
114 string Tmp = _config->Find("Dir::bin::gzip","gzip");
115 Args[0] = Tmp.c_str();
143abaeb
AL
116 Args[1] = "-d";
117 Args[2] = 0;
118 execvp(Args[0],(char **)Args);
119 exit(100);
120 }
121
122 // Wait for gzip to finish
1ae93c94 123 if (ExecWait(Process,_config->Find("Dir::bin::gzip","gzip").c_str(),false) == false)
143abaeb 124 return _error->Error("gzip failed, perhaps the disk is full.");
1ae93c94 125
143abaeb
AL
126 Pkg.Seek(0);
127 }
b2e465d6 128 pkgTagFile Parser(&Pkg);
143abaeb
AL
129 if (_error->PendingError() == true)
130 return false;
131
132 // Open the output file
133 char S[400];
20ebd488
AL
134 snprintf(S,sizeof(S),"cdrom:[%s]/%s%s",Name.c_str(),
135 (*I).c_str() + CDROM.length(),GetFileName());
143abaeb
AL
136 string TargetF = _config->FindDir("Dir::State::lists") + "partial/";
137 TargetF += URItoFileName(S);
8deb53ab 138 FileFd Target;
143abaeb 139 if (_config->FindB("APT::CDROM::NoAct",false) == true)
8deb53ab 140 {
143abaeb 141 TargetF = "/dev/null";
8deb53ab
MV
142 Target.Open(TargetF,FileFd::WriteExists);
143 } else {
144 Target.Open(TargetF,FileFd::WriteAtomic);
145 }
b2e465d6 146 FILE *TargetFl = fdopen(dup(Target.Fd()),"w");
143abaeb
AL
147 if (_error->PendingError() == true)
148 return false;
b2e465d6
AL
149 if (TargetFl == 0)
150 return _error->Errno("fdopen","Failed to reopen fd");
143abaeb
AL
151
152 // Setup the progress meter
a75c6a6e
MZ
153 if(Progress)
154 Progress->OverallProgress(CurrentSize,TotalSize,FileSize,
155 string("Reading ") + Type() + " Indexes");
143abaeb
AL
156
157 // Parse
a75c6a6e
MZ
158 if(Progress)
159 Progress->SubProgress(Pkg.Size());
143abaeb
AL
160 pkgTagSection Section;
161 this->Section = &Section;
162 string Prefix;
163 unsigned long Hits = 0;
164 unsigned long Chop = 0;
165 while (Parser.Step(Section) == true)
166 {
a75c6a6e
MZ
167 if(Progress)
168 Progress->Progress(Parser.Offset());
143abaeb 169 string File;
650faab0 170 unsigned long long Size;
143abaeb 171 if (GetFile(File,Size) == false)
b2e465d6
AL
172 {
173 fclose(TargetFl);
143abaeb 174 return false;
b2e465d6 175 }
143abaeb
AL
176
177 if (Chop != 0)
178 File = OrigPath + ChopDirs(File,Chop);
179
180 // See if the file exists
181 bool Mangled = false;
182 if (NoStat == false || Hits < 10)
183 {
184 // Attempt to fix broken structure
185 if (Hits == 0)
186 {
187 if (ReconstructPrefix(Prefix,OrigPath,CDROM,File) == false &&
188 ReconstructChop(Chop,*I,File) == false)
189 {
190 if (Debug == true)
191 clog << "Missed: " << File << endl;
192 NotFound++;
193 continue;
194 }
195 if (Chop != 0)
196 File = OrigPath + ChopDirs(File,Chop);
197 }
198
199 // Get the size
200 struct stat Buf;
201 if (stat(string(CDROM + Prefix + File).c_str(),&Buf) != 0 ||
202 Buf.st_size == 0)
203 {
204 // Attempt to fix busted symlink support for one instance
205 string OrigFile = File;
206 string::size_type Start = File.find("binary-");
207 string::size_type End = File.find("/",Start+3);
208 if (Start != string::npos && End != string::npos)
209 {
210 File.replace(Start,End-Start,"binary-all");
211 Mangled = true;
212 }
213
214 if (Mangled == false ||
215 stat(string(CDROM + Prefix + File).c_str(),&Buf) != 0)
216 {
217 if (Debug == true)
218 clog << "Missed(2): " << OrigFile << endl;
219 NotFound++;
220 continue;
221 }
222 }
223
224 // Size match
650faab0 225 if ((unsigned long long)Buf.st_size != Size)
143abaeb
AL
226 {
227 if (Debug == true)
228 clog << "Wrong Size: " << File << endl;
229 WrongSize++;
230 continue;
231 }
232 }
233
234 Packages++;
235 Hits++;
236
b2e465d6 237 if (RewriteEntry(TargetFl,File) == false)
143abaeb 238 {
b2e465d6
AL
239 fclose(TargetFl);
240 return false;
143abaeb 241 }
143abaeb 242 }
b2e465d6 243 fclose(TargetFl);
143abaeb
AL
244
245 if (Debug == true)
246 cout << " Processed by using Prefix '" << Prefix << "' and chop " << Chop << endl;
247
248 if (_config->FindB("APT::CDROM::NoAct",false) == false)
249 {
250 // Move out of the partial directory
251 Target.Close();
252 string FinalF = _config->FindDir("Dir::State::lists");
253 FinalF += URItoFileName(S);
254 if (rename(TargetF.c_str(),FinalF.c_str()) != 0)
255 return _error->Errno("rename","Failed to rename");
143abaeb 256 }
a75c6a6e 257
143abaeb
AL
258 /* Mangle the source to be in the proper notation with
259 prefix dist [component] */
260 *I = string(*I,Prefix.length());
261 ConvertToSourceList(CDROM,*I);
262 *I = Prefix + ' ' + *I;
263
264 CurrentSize += FileSize;
265 }
a75c6a6e
MZ
266 if(Progress)
267 Progress->Done();
143abaeb
AL
268
269 // Some stats
a75c6a6e
MZ
270 if(log) {
271 stringstream msg;
272 if(NotFound == 0 && WrongSize == 0)
273 ioprintf(msg, _("Wrote %i records.\n"), Packages);
274 else if (NotFound != 0 && WrongSize == 0)
275 ioprintf(msg, _("Wrote %i records with %i missing files.\n"),
276 Packages, NotFound);
277 else if (NotFound == 0 && WrongSize != 0)
db0db9fe 278 ioprintf(msg, _("Wrote %i records with %i mismatched files\n"),
a75c6a6e
MZ
279 Packages, WrongSize);
280 if (NotFound != 0 && WrongSize != 0)
db0db9fe 281 ioprintf(msg, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages, NotFound, WrongSize);
a75c6a6e 282 }
143abaeb
AL
283
284 if (Packages == 0)
dd27443e
AL
285 _error->Warning("No valid records were found.");
286
143abaeb 287 if (NotFound + WrongSize > 10)
46e39c8e 288 _error->Warning("A lot of entries were discarded, something may be wrong.\n");
a75c6a6e 289
143abaeb
AL
290
291 return true;
292}
293 /*}}}*/
294// IndexCopy::ChopDirs - Chop off the leading directory components /*{{{*/
295// ---------------------------------------------------------------------
296/* */
297string IndexCopy::ChopDirs(string Path,unsigned int Depth)
298{
299 string::size_type I = 0;
300 do
301 {
302 I = Path.find('/',I+1);
303 Depth--;
304 }
305 while (I != string::npos && Depth != 0);
306
307 if (I == string::npos)
308 return string();
309
310 return string(Path,I+1);
311}
312 /*}}}*/
313// IndexCopy::ReconstructPrefix - Fix strange prefixing /*{{{*/
314// ---------------------------------------------------------------------
315/* This prepends dir components from the path to the package files to
316 the path to the deb until it is found */
317bool IndexCopy::ReconstructPrefix(string &Prefix,string OrigPath,string CD,
318 string File)
319{
320 bool Debug = _config->FindB("Debug::aptcdrom",false);
321 unsigned int Depth = 1;
322 string MyPrefix = Prefix;
323 while (1)
324 {
325 struct stat Buf;
326 if (stat(string(CD + MyPrefix + File).c_str(),&Buf) != 0)
327 {
328 if (Debug == true)
329 cout << "Failed, " << CD + MyPrefix + File << endl;
330 if (GrabFirst(OrigPath,MyPrefix,Depth++) == true)
331 continue;
332
333 return false;
334 }
335 else
336 {
337 Prefix = MyPrefix;
338 return true;
339 }
340 }
341 return false;
342}
343 /*}}}*/
344// IndexCopy::ReconstructChop - Fixes bad source paths /*{{{*/
345// ---------------------------------------------------------------------
346/* This removes path components from the filename and prepends the location
347 of the package files until a file is found */
348bool IndexCopy::ReconstructChop(unsigned long &Chop,string Dir,string File)
349{
350 // Attempt to reconstruct the filename
351 unsigned long Depth = 0;
352 while (1)
353 {
354 struct stat Buf;
355 if (stat(string(Dir + File).c_str(),&Buf) != 0)
356 {
357 File = ChopDirs(File,1);
358 Depth++;
359 if (File.empty() == false)
360 continue;
361 return false;
362 }
363 else
364 {
365 Chop = Depth;
366 return true;
367 }
368 }
369 return false;
370}
371 /*}}}*/
372// IndexCopy::ConvertToSourceList - Convert a Path to a sourcelist /*{{{*/
373// ---------------------------------------------------------------------
374/* We look for things in dists/ notation and convert them to
375 <dist> <component> form otherwise it is left alone. This also strips
0f770a0c
AL
376 the CD path.
377
378 This implements a regex sort of like:
379 (.*)/dists/([^/]*)/(.*)/binary-*
380 ^ ^ ^- Component
381 | |-------- Distribution
382 |------------------- Path
383
384 It was deciced to use only a single word for dist (rather than say
385 unstable/non-us) to increase the chance that each CD gets a single
386 line in sources.list.
387 */
143abaeb
AL
388void IndexCopy::ConvertToSourceList(string CD,string &Path)
389{
390 char S[300];
20ebd488 391 snprintf(S,sizeof(S),"binary-%s",_config->Find("Apt::Architecture").c_str());
143abaeb
AL
392
393 // Strip the cdrom base path
394 Path = string(Path,CD.length());
395 if (Path.empty() == true)
396 Path = "/";
397
398 // Too short to be a dists/ type
399 if (Path.length() < strlen("dists/"))
400 return;
401
402 // Not a dists type.
076d01b0 403 if (stringcmp(Path.c_str(),Path.c_str()+strlen("dists/"),"dists/") != 0)
143abaeb 404 return;
7834cb57 405
143abaeb
AL
406 // Isolate the dist
407 string::size_type Slash = strlen("dists/");
408 string::size_type Slash2 = Path.find('/',Slash + 1);
409 if (Slash2 == string::npos || Slash2 + 2 >= Path.length())
410 return;
411 string Dist = string(Path,Slash,Slash2 - Slash);
412
413 // Isolate the component
0f770a0c
AL
414 Slash = Slash2;
415 for (unsigned I = 0; I != 10; I++)
416 {
417 Slash = Path.find('/',Slash+1);
418 if (Slash == string::npos || Slash + 2 >= Path.length())
419 return;
420 string Comp = string(Path,Slash2+1,Slash - Slash2-1);
421
422 // Verify the trailing binary- bit
423 string::size_type BinSlash = Path.find('/',Slash + 1);
424 if (Slash == string::npos)
425 return;
426 string Binary = string(Path,Slash+1,BinSlash - Slash-1);
427
428 if (Binary != S && Binary != "source")
429 continue;
430
431 Path = Dist + ' ' + Comp;
143abaeb 432 return;
0f770a0c 433 }
143abaeb
AL
434}
435 /*}}}*/
436// IndexCopy::GrabFirst - Return the first Depth path components /*{{{*/
437// ---------------------------------------------------------------------
438/* */
439bool IndexCopy::GrabFirst(string Path,string &To,unsigned int Depth)
440{
441 string::size_type I = 0;
442 do
443 {
444 I = Path.find('/',I+1);
445 Depth--;
446 }
447 while (I != string::npos && Depth != 0);
448
449 if (I == string::npos)
450 return false;
451
452 To = string(Path,0,I+1);
453 return true;
454}
455 /*}}}*/
143abaeb
AL
456// PackageCopy::GetFile - Get the file information from the section /*{{{*/
457// ---------------------------------------------------------------------
458/* */
650faab0 459bool PackageCopy::GetFile(string &File,unsigned long long &Size)
143abaeb
AL
460{
461 File = Section->FindS("Filename");
462 Size = Section->FindI("Size");
463 if (File.empty() || Size == 0)
464 return _error->Error("Cannot find filename or size tag");
465 return true;
466}
467 /*}}}*/
468// PackageCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
469// ---------------------------------------------------------------------
470/* */
b2e465d6 471bool PackageCopy::RewriteEntry(FILE *Target,string File)
143abaeb 472{
b2e465d6
AL
473 TFRewriteData Changes[] = {{"Filename",File.c_str()},
474 {}};
475
476 if (TFRewrite(Target,*Section,TFRewritePackageOrder,Changes) == false)
477 return false;
478 fputc('\n',Target);
479 return true;
143abaeb
AL
480}
481 /*}}}*/
482// SourceCopy::GetFile - Get the file information from the section /*{{{*/
483// ---------------------------------------------------------------------
484/* */
650faab0 485bool SourceCopy::GetFile(string &File,unsigned long long &Size)
143abaeb
AL
486{
487 string Files = Section->FindS("Files");
488 if (Files.empty() == true)
489 return false;
490
491 // Stash the / terminated directory prefix
492 string Base = Section->FindS("Directory");
493 if (Base.empty() == false && Base[Base.length()-1] != '/')
494 Base += '/';
495
b2e465d6 496 // Read the first file triplet
143abaeb
AL
497 const char *C = Files.c_str();
498 string sSize;
499 string MD5Hash;
500
501 // Parse each of the elements
502 if (ParseQuoteWord(C,MD5Hash) == false ||
503 ParseQuoteWord(C,sSize) == false ||
504 ParseQuoteWord(C,File) == false)
505 return _error->Error("Error parsing file record");
506
507 // Parse the size and append the directory
650faab0 508 Size = strtoull(sSize.c_str(), NULL, 10);
143abaeb
AL
509 File = Base + File;
510 return true;
511}
512 /*}}}*/
513// SourceCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
514// ---------------------------------------------------------------------
515/* */
b2e465d6 516bool SourceCopy::RewriteEntry(FILE *Target,string File)
143abaeb 517{
b2e465d6
AL
518 string Dir(File,0,File.rfind('/'));
519 TFRewriteData Changes[] = {{"Directory",Dir.c_str()},
520 {}};
521
522 if (TFRewrite(Target,*Section,TFRewriteSourceOrder,Changes) == false)
523 return false;
524 fputc('\n',Target);
525 return true;
143abaeb
AL
526}
527 /*}}}*/
22f8568d
MV
528// SigVerify::Verify - Verify a files md5sum against its metaindex /*{{{*/
529// ---------------------------------------------------------------------
530/* */
a75c6a6e
MZ
531bool SigVerify::Verify(string prefix, string file, indexRecords *MetaIndex)
532{
533 const indexRecords::checkSum *Record = MetaIndex->Lookup(file);
534
8d357c52
MV
535 // we skip non-existing files in the verifcation to support a cdrom
536 // with no Packages file (just a Package.gz), see LP: #255545
537 // (non-existing files are not considered a error)
8220213e 538 if(!RealFileExists(prefix+file))
8d357c52 539 {
a1e42d1f 540 _error->Warning(_("Skipping nonexistent file %s"), string(prefix+file).c_str());
8d357c52
MV
541 return true;
542 }
543
a75c6a6e
MZ
544 if (!Record)
545 {
a1e42d1f 546 _error->Warning(_("Can't find authentication record for: %s"), file.c_str());
a75c6a6e
MZ
547 return false;
548 }
549
495e5cb2 550 if (!Record->Hash.VerifyFile(prefix+file))
a75c6a6e 551 {
a1e42d1f 552 _error->Warning(_("Hash mismatch for: %s"),file.c_str());
a75c6a6e
MZ
553 return false;
554 }
555
556 if(_config->FindB("Debug::aptcdrom",false))
557 {
558 cout << "File: " << prefix+file << endl;
495e5cb2 559 cout << "Expected Hash " << Record->Hash.toStr() << endl;
a75c6a6e
MZ
560 }
561
562 return true;
563}
92fcbfc1
DK
564 /*}}}*/
565bool SigVerify::CopyMetaIndex(string CDROM, string CDName, /*{{{*/
a75c6a6e
MZ
566 string prefix, string file)
567{
568 char S[400];
569 snprintf(S,sizeof(S),"cdrom:[%s]/%s%s",CDName.c_str(),
570 (prefix).c_str() + CDROM.length(),file.c_str());
571 string TargetF = _config->FindDir("Dir::State::lists");
572 TargetF += URItoFileName(S);
573
574 FileFd Target;
575 FileFd Rel;
22041bd2 576 Target.Open(TargetF,FileFd::WriteAtomic);
a75c6a6e
MZ
577 Rel.Open(prefix + file,FileFd::ReadOnly);
578 if (_error->PendingError() == true)
579 return false;
580 if (CopyFile(Rel,Target) == false)
581 return false;
582
583 return true;
584}
92fcbfc1
DK
585 /*}}}*/
586bool SigVerify::CopyAndVerify(string CDROM,string Name,vector<string> &SigList, /*{{{*/
a75c6a6e
MZ
587 vector<string> PkgList,vector<string> SrcList)
588{
f7f0d6c7 589 if (SigList.empty() == true)
a75c6a6e
MZ
590 return true;
591
592 bool Debug = _config->FindB("Debug::aptcdrom",false);
593
594 // Read all Release files
f7f0d6c7 595 for (vector<string>::iterator I = SigList.begin(); I != SigList.end(); ++I)
a75c6a6e
MZ
596 {
597 if(Debug)
598 cout << "Signature verify for: " << *I << endl;
599
600 indexRecords *MetaIndex = new indexRecords;
601 string prefix = *I;
602
a319c4ee
DK
603 string const releasegpg = *I+"Release.gpg";
604 string const release = *I+"Release";
605
a75c6a6e 606 // a Release.gpg without a Release should never happen
8220213e 607 if(RealFileExists(release) == false)
cfb3d242
DK
608 {
609 delete MetaIndex;
a75c6a6e 610 continue;
cfb3d242 611 }
a75c6a6e 612
a75c6a6e
MZ
613 pid_t pid = ExecFork();
614 if(pid < 0) {
615 _error->Error("Fork failed");
616 return false;
617 }
cf440fac
DK
618 if(pid == 0)
619 RunGPGV(release, releasegpg);
620
a75c6a6e
MZ
621 if(!ExecWait(pid, "gpgv")) {
622 _error->Warning("Signature verification failed for: %s",
a319c4ee 623 releasegpg.c_str());
a75c6a6e
MZ
624 // something went wrong, don't copy the Release.gpg
625 // FIXME: delete any existing gpg file?
626 continue;
627 }
628
629 // Open the Release file and add it to the MetaIndex
a319c4ee 630 if(!MetaIndex->Load(release))
a75c6a6e 631 {
9b5d79ec 632 _error->Error("%s",MetaIndex->ErrorText.c_str());
a75c6a6e
MZ
633 return false;
634 }
635
636 // go over the Indexfiles and see if they verify
637 // if so, remove them from our copy of the lists
638 vector<string> keys = MetaIndex->MetaKeys();
f7f0d6c7 639 for (vector<string>::iterator I = keys.begin(); I != keys.end(); ++I)
a75c6a6e
MZ
640 {
641 if(!Verify(prefix,*I, MetaIndex)) {
642 // something went wrong, don't copy the Release.gpg
643 // FIXME: delete any existing gpg file?
7efdcd3a 644 _error->Discard();
a75c6a6e
MZ
645 continue;
646 }
647 }
648
649 // we need a fresh one for the Release.gpg
650 delete MetaIndex;
651
652 // everything was fine, copy the Release and Release.gpg file
653 CopyMetaIndex(CDROM, Name, prefix, "Release");
654 CopyMetaIndex(CDROM, Name, prefix, "Release.gpg");
655 }
656
657 return true;
658}
92fcbfc1 659 /*}}}*/
cf440fac 660// SigVerify::RunGPGV - returns the command needed for verify /*{{{*/
a319c4ee
DK
661// ---------------------------------------------------------------------
662/* Generating the commandline for calling gpgv is somehow complicated as
663 we need to add multiple keyrings and user supplied options. Also, as
664 the cdrom code currently can not use the gpgv method we have two places
665 these need to be done - so the place for this method is wrong but better
666 than code duplication… */
cf440fac
DK
667bool SigVerify::RunGPGV(std::string const &File, std::string const &FileGPG,
668 int const &statusfd, int fd[2])
a319c4ee 669{
89c4c588
MV
670 if (File == FileGPG)
671 {
672 #define SIGMSG "-----BEGIN PGP SIGNED MESSAGE-----\n"
673 char buffer[sizeof(SIGMSG)];
674 FILE* gpg = fopen(File.c_str(), "r");
675 if (gpg == NULL)
676 return _error->Errno("RunGPGV", _("Could not open file %s"), File.c_str());
677 char const * const test = fgets(buffer, sizeof(buffer), gpg);
678 fclose(gpg);
679 if (test == NULL || strcmp(buffer, SIGMSG) != 0)
680 return _error->Error(_("File %s doesn't start with a clearsigned message"), File.c_str());
681 #undef SIGMSG
682 }
683
684
a319c4ee
DK
685 string const gpgvpath = _config->Find("Dir::Bin::gpg", "/usr/bin/gpgv");
686 // FIXME: remove support for deprecated APT::GPGV setting
1dc03a86 687 string const trustedFile = _config->Find("APT::GPGV::TrustedKeyring", _config->FindFile("Dir::Etc::Trusted"));
4368851d 688 string const trustedPath = _config->FindDir("Dir::Etc::TrustedParts");
a319c4ee 689
cf440fac
DK
690 bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
691
692 if (Debug == true)
a319c4ee
DK
693 {
694 std::clog << "gpgv path: " << gpgvpath << std::endl;
695 std::clog << "Keyring file: " << trustedFile << std::endl;
696 std::clog << "Keyring path: " << trustedPath << std::endl;
697 }
698
5b777e8f
MV
699 std::vector<string> keyrings;
700 if (DirectoryExists(trustedPath))
3d661e4a 701 keyrings = GetListOfFilesInDir(trustedPath, "gpg", false, true);
8220213e 702 if (RealFileExists(trustedFile) == true)
3d661e4a 703 keyrings.push_back(trustedFile);
a319c4ee
DK
704
705 std::vector<const char *> Args;
706 Args.reserve(30);
707
708 if (keyrings.empty() == true)
89c4c588
MV
709 {
710 // TRANSLATOR: %s is the trusted keyring parts directory
711 return _error->Error(_("No keyring installed in %s."),
712 _config->FindDir("Dir::Etc::TrustedParts").c_str());
713 }
a319c4ee
DK
714
715 Args.push_back(gpgvpath.c_str());
716 Args.push_back("--ignore-time-conflict");
717
cf440fac
DK
718 if (statusfd != -1)
719 {
720 Args.push_back("--status-fd");
721 char fd[10];
722 snprintf(fd, sizeof(fd), "%i", statusfd);
723 Args.push_back(fd);
724 }
725
a319c4ee
DK
726 for (vector<string>::const_iterator K = keyrings.begin();
727 K != keyrings.end(); ++K)
728 {
729 Args.push_back("--keyring");
730 Args.push_back(K->c_str());
731 }
732
733 Configuration::Item const *Opts;
734 Opts = _config->Tree("Acquire::gpgv::Options");
735 if (Opts != 0)
736 {
737 Opts = Opts->Child;
738 for (; Opts != 0; Opts = Opts->Next)
739 {
740 if (Opts->Value.empty() == true)
741 continue;
742 Args.push_back(Opts->Value.c_str());
743 }
744 }
745
cf440fac 746 Args.push_back(FileGPG.c_str());
fe0f7911
DK
747 if (FileGPG != File)
748 Args.push_back(File.c_str());
cf440fac
DK
749 Args.push_back(NULL);
750
751 if (Debug == true)
752 {
753 std::clog << "Preparing to exec: " << gpgvpath;
754 for (std::vector<const char *>::const_iterator a = Args.begin(); *a != NULL; ++a)
755 std::clog << " " << *a;
756 std::clog << std::endl;
757 }
758
759 if (statusfd != -1)
760 {
761 int const nullfd = open("/dev/null", O_RDONLY);
762 close(fd[0]);
763 // Redirect output to /dev/null; we read from the status fd
764 dup2(nullfd, STDOUT_FILENO);
765 dup2(nullfd, STDERR_FILENO);
766 // Redirect the pipe to the status fd (3)
767 dup2(fd[1], statusfd);
768
769 putenv((char *)"LANG=");
770 putenv((char *)"LC_ALL=");
771 putenv((char *)"LC_MESSAGES=");
772 }
773
774 execvp(gpgvpath.c_str(), (char **) &Args[0]);
775 return true;
a319c4ee
DK
776}
777 /*}}}*/
92fcbfc1
DK
778bool TranslationsCopy::CopyTranslations(string CDROM,string Name, /*{{{*/
779 vector<string> &List, pkgCdromStatus *log)
22f8568d
MV
780{
781 OpProgress *Progress = NULL;
f7f0d6c7 782 if (List.empty() == true)
22f8568d
MV
783 return true;
784
785 if(log)
786 Progress = log->GetOpProgress();
787
788 bool Debug = _config->FindB("Debug::aptcdrom",false);
789
790 // Prepare the progress indicator
650faab0 791 off_t TotalSize = 0;
f7f0d6c7 792 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
22f8568d
MV
793 {
794 struct stat Buf;
795 if (stat(string(*I).c_str(),&Buf) != 0 &&
796 stat(string(*I + ".gz").c_str(),&Buf) != 0)
797 return _error->Errno("stat","Stat failed for %s",
798 string(*I).c_str());
799 TotalSize += Buf.st_size;
800 }
801
650faab0 802 off_t CurrentSize = 0;
22f8568d
MV
803 unsigned int NotFound = 0;
804 unsigned int WrongSize = 0;
805 unsigned int Packages = 0;
f7f0d6c7 806 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
22f8568d
MV
807 {
808 string OrigPath = string(*I,CDROM.length());
650faab0 809 off_t FileSize = 0;
22f8568d
MV
810
811 // Open the package file
812 FileFd Pkg;
8220213e 813 if (RealFileExists(*I) == true)
22f8568d
MV
814 {
815 Pkg.Open(*I,FileFd::ReadOnly);
816 FileSize = Pkg.Size();
817 }
818 else
819 {
820 FileFd From(*I + ".gz",FileFd::ReadOnly);
821 if (_error->PendingError() == true)
822 return false;
823 FileSize = From.Size();
824
825 // Get a temp file
826 FILE *tmp = tmpfile();
827 if (tmp == 0)
828 return _error->Errno("tmpfile","Unable to create a tmp file");
829 Pkg.Fd(dup(fileno(tmp)));
830 fclose(tmp);
831
832 // Fork gzip
833 pid_t Process = fork();
834 if (Process < 0)
835 return _error->Errno("fork","Couldn't fork gzip");
836
837 // The child
838 if (Process == 0)
839 {
840 dup2(From.Fd(),STDIN_FILENO);
841 dup2(Pkg.Fd(),STDOUT_FILENO);
842 SetCloseExec(STDIN_FILENO,false);
843 SetCloseExec(STDOUT_FILENO,false);
844
845 const char *Args[3];
846 string Tmp = _config->Find("Dir::bin::gzip","gzip");
847 Args[0] = Tmp.c_str();
848 Args[1] = "-d";
849 Args[2] = 0;
850 execvp(Args[0],(char **)Args);
851 exit(100);
852 }
853
854 // Wait for gzip to finish
855 if (ExecWait(Process,_config->Find("Dir::bin::gzip","gzip").c_str(),false) == false)
856 return _error->Error("gzip failed, perhaps the disk is full.");
857
858 Pkg.Seek(0);
859 }
860 pkgTagFile Parser(&Pkg);
861 if (_error->PendingError() == true)
862 return false;
863
864 // Open the output file
865 char S[400];
866 snprintf(S,sizeof(S),"cdrom:[%s]/%s",Name.c_str(),
867 (*I).c_str() + CDROM.length());
868 string TargetF = _config->FindDir("Dir::State::lists") + "partial/";
869 TargetF += URItoFileName(S);
870 if (_config->FindB("APT::CDROM::NoAct",false) == true)
871 TargetF = "/dev/null";
22041bd2 872 FileFd Target(TargetF,FileFd::WriteAtomic);
22f8568d
MV
873 FILE *TargetFl = fdopen(dup(Target.Fd()),"w");
874 if (_error->PendingError() == true)
875 return false;
876 if (TargetFl == 0)
877 return _error->Errno("fdopen","Failed to reopen fd");
878
879 // Setup the progress meter
880 if(Progress)
881 Progress->OverallProgress(CurrentSize,TotalSize,FileSize,
882 string("Reading Translation Indexes"));
883
884 // Parse
885 if(Progress)
886 Progress->SubProgress(Pkg.Size());
887 pkgTagSection Section;
888 this->Section = &Section;
889 string Prefix;
890 unsigned long Hits = 0;
22f8568d
MV
891 while (Parser.Step(Section) == true)
892 {
893 if(Progress)
894 Progress->Progress(Parser.Offset());
895
896 const char *Start;
897 const char *Stop;
898 Section.GetSection(Start,Stop);
899 fwrite(Start,Stop-Start, 1, TargetFl);
900 fputc('\n',TargetFl);
901
902 Packages++;
903 Hits++;
904 }
905 fclose(TargetFl);
906
907 if (Debug == true)
91c03d37 908 cout << " Processed by using Prefix '" << Prefix << "' and chop " << endl;
22f8568d
MV
909
910 if (_config->FindB("APT::CDROM::NoAct",false) == false)
911 {
912 // Move out of the partial directory
913 Target.Close();
914 string FinalF = _config->FindDir("Dir::State::lists");
915 FinalF += URItoFileName(S);
916 if (rename(TargetF.c_str(),FinalF.c_str()) != 0)
917 return _error->Errno("rename","Failed to rename");
918 }
919
920
921 CurrentSize += FileSize;
922 }
923 if(Progress)
924 Progress->Done();
925
926 // Some stats
927 if(log) {
928 stringstream msg;
929 if(NotFound == 0 && WrongSize == 0)
930 ioprintf(msg, _("Wrote %i records.\n"), Packages);
931 else if (NotFound != 0 && WrongSize == 0)
932 ioprintf(msg, _("Wrote %i records with %i missing files.\n"),
933 Packages, NotFound);
934 else if (NotFound == 0 && WrongSize != 0)
935 ioprintf(msg, _("Wrote %i records with %i mismatched files\n"),
936 Packages, WrongSize);
937 if (NotFound != 0 && WrongSize != 0)
938 ioprintf(msg, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages, NotFound, WrongSize);
939 }
940
941 if (Packages == 0)
942 _error->Warning("No valid records were found.");
943
944 if (NotFound + WrongSize > 10)
46e39c8e 945 _error->Warning("A lot of entries were discarded, something may be wrong.\n");
22f8568d
MV
946
947
948 return true;
949}
92fcbfc1 950 /*}}}*/