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