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