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