]> git.saurik.com Git - apt.git/blob - ftparchive/writer.cc
test-indexes.sh: Add source related tests, which uncovers two regressions
[apt.git] / ftparchive / writer.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: writer.cc,v 1.14 2004/03/24 01:40:43 mdz Exp $
4 /* ######################################################################
5
6 Writer
7
8 The file writer classes. These write various types of output, sources,
9 packages and contents.
10
11 ##################################################################### */
12 /*}}}*/
13 // Include Files /*{{{*/
14 #include "writer.h"
15
16 #include <apti18n.h>
17 #include <apt-pkg/strutl.h>
18 #include <apt-pkg/error.h>
19 #include <apt-pkg/configuration.h>
20 #include <apt-pkg/md5.h>
21 #include <apt-pkg/sha1.h>
22 #include <apt-pkg/sha256.h>
23 #include <apt-pkg/deblistparser.h>
24
25 #include <sys/types.h>
26 #include <unistd.h>
27 #include <ctime>
28 #include <ftw.h>
29 #include <fnmatch.h>
30 #include <iostream>
31 #include <memory>
32
33 #include "cachedb.h"
34 #include "apt-ftparchive.h"
35 #include "multicompress.h"
36 /*}}}*/
37 using namespace std;
38 FTWScanner *FTWScanner::Owner;
39
40 // SetTFRewriteData - Helper for setting rewrite lists /*{{{*/
41 // ---------------------------------------------------------------------
42 /* */
43 inline void SetTFRewriteData(struct TFRewriteData &tfrd,
44 const char *tag,
45 const char *rewrite,
46 const char *newtag = 0)
47 {
48 tfrd.Tag = tag;
49 tfrd.Rewrite = rewrite;
50 tfrd.NewTag = newtag;
51 }
52 /*}}}*/
53
54 // FTWScanner::FTWScanner - Constructor /*{{{*/
55 // ---------------------------------------------------------------------
56 /* */
57 FTWScanner::FTWScanner(string const &Arch): Arch(Arch)
58 {
59 ErrorPrinted = false;
60 NoLinkAct = !_config->FindB("APT::FTPArchive::DeLinkAct",true);
61 }
62 /*}}}*/
63 // FTWScanner::Scanner - FTW Scanner /*{{{*/
64 // ---------------------------------------------------------------------
65 /* This is the FTW scanner, it processes each directory element in the
66 directory tree. */
67 int FTWScanner::ScannerFTW(const char *File,const struct stat *sb,int Flag)
68 {
69 if (Flag == FTW_DNR)
70 {
71 Owner->NewLine(1);
72 ioprintf(c1out, _("W: Unable to read directory %s\n"), File);
73 }
74 if (Flag == FTW_NS)
75 {
76 Owner->NewLine(1);
77 ioprintf(c1out, _("W: Unable to stat %s\n"), File);
78 }
79 if (Flag != FTW_F)
80 return 0;
81
82 return ScannerFile(File, true);
83 }
84 /*}}}*/
85 // FTWScanner::ScannerFile - File Scanner /*{{{*/
86 // ---------------------------------------------------------------------
87 /* */
88 int FTWScanner::ScannerFile(const char *File, bool const &ReadLink)
89 {
90 const char *LastComponent = strrchr(File, '/');
91 char *RealPath = NULL;
92
93 if (LastComponent == NULL)
94 LastComponent = File;
95 else
96 LastComponent++;
97
98 vector<string>::const_iterator I;
99 for(I = Owner->Patterns.begin(); I != Owner->Patterns.end(); ++I)
100 {
101 if (fnmatch((*I).c_str(), LastComponent, 0) == 0)
102 break;
103 }
104 if (I == Owner->Patterns.end())
105 return 0;
106
107 /* Process it. If the file is a link then resolve it into an absolute
108 name.. This works best if the directory components the scanner are
109 given are not links themselves. */
110 char Jnk[2];
111 Owner->OriginalPath = File;
112 if (ReadLink &&
113 readlink(File,Jnk,sizeof(Jnk)) != -1 &&
114 (RealPath = realpath(File,NULL)) != 0)
115 {
116 Owner->DoPackage(RealPath);
117 free(RealPath);
118 }
119 else
120 Owner->DoPackage(File);
121
122 if (_error->empty() == false)
123 {
124 // Print any errors or warnings found
125 string Err;
126 bool SeenPath = false;
127 while (_error->empty() == false)
128 {
129 Owner->NewLine(1);
130
131 bool const Type = _error->PopMessage(Err);
132 if (Type == true)
133 cerr << _("E: ") << Err << endl;
134 else
135 cerr << _("W: ") << Err << endl;
136
137 if (Err.find(File) != string::npos)
138 SeenPath = true;
139 }
140
141 if (SeenPath == false)
142 cerr << _("E: Errors apply to file ") << "'" << File << "'" << endl;
143 return 0;
144 }
145
146 return 0;
147 }
148 /*}}}*/
149 // FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
150 // ---------------------------------------------------------------------
151 /* */
152 bool FTWScanner::RecursiveScan(string const &Dir)
153 {
154 char *RealPath = NULL;
155 /* If noprefix is set then jam the scan root in, so we don't generate
156 link followed paths out of control */
157 if (InternalPrefix.empty() == true)
158 {
159 if ((RealPath = realpath(Dir.c_str(),NULL)) == 0)
160 return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
161 InternalPrefix = RealPath;
162 free(RealPath);
163 }
164
165 // Do recursive directory searching
166 Owner = this;
167 int const Res = ftw(Dir.c_str(),ScannerFTW,30);
168
169 // Error treewalking?
170 if (Res != 0)
171 {
172 if (_error->PendingError() == false)
173 _error->Errno("ftw",_("Tree walking failed"));
174 return false;
175 }
176
177 return true;
178 }
179 /*}}}*/
180 // FTWScanner::LoadFileList - Load the file list from a file /*{{{*/
181 // ---------------------------------------------------------------------
182 /* This is an alternative to using FTW to locate files, it reads the list
183 of files from another file. */
184 bool FTWScanner::LoadFileList(string const &Dir, string const &File)
185 {
186 char *RealPath = NULL;
187 /* If noprefix is set then jam the scan root in, so we don't generate
188 link followed paths out of control */
189 if (InternalPrefix.empty() == true)
190 {
191 if ((RealPath = realpath(Dir.c_str(),NULL)) == 0)
192 return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
193 InternalPrefix = RealPath;
194 free(RealPath);
195 }
196
197 Owner = this;
198 FILE *List = fopen(File.c_str(),"r");
199 if (List == 0)
200 return _error->Errno("fopen",_("Failed to open %s"),File.c_str());
201
202 /* We are a tad tricky here.. We prefix the buffer with the directory
203 name, that way if we need a full path with just use line.. Sneaky and
204 fully evil. */
205 char Line[1000];
206 char *FileStart;
207 if (Dir.empty() == true || Dir.end()[-1] != '/')
208 FileStart = Line + snprintf(Line,sizeof(Line),"%s/",Dir.c_str());
209 else
210 FileStart = Line + snprintf(Line,sizeof(Line),"%s",Dir.c_str());
211 while (fgets(FileStart,sizeof(Line) - (FileStart - Line),List) != 0)
212 {
213 char *FileName = _strstrip(FileStart);
214 if (FileName[0] == 0)
215 continue;
216
217 if (FileName[0] != '/')
218 {
219 if (FileName != FileStart)
220 memmove(FileStart,FileName,strlen(FileStart));
221 FileName = Line;
222 }
223
224 #if 0
225 struct stat St;
226 int Flag = FTW_F;
227 if (stat(FileName,&St) != 0)
228 Flag = FTW_NS;
229 #endif
230
231 if (ScannerFile(FileName, false) != 0)
232 break;
233 }
234
235 fclose(List);
236 return true;
237 }
238 /*}}}*/
239 // FTWScanner::Delink - Delink symlinks /*{{{*/
240 // ---------------------------------------------------------------------
241 /* */
242 bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
243 unsigned long &DeLinkBytes,
244 off_t const &FileSize)
245 {
246 // See if this isn't an internaly prefix'd file name.
247 if (InternalPrefix.empty() == false &&
248 InternalPrefix.length() < FileName.length() &&
249 stringcmp(FileName.begin(),FileName.begin() + InternalPrefix.length(),
250 InternalPrefix.begin(),InternalPrefix.end()) != 0)
251 {
252 if (DeLinkLimit != 0 && DeLinkBytes/1024 < DeLinkLimit)
253 {
254 // Tidy up the display
255 if (DeLinkBytes == 0)
256 cout << endl;
257
258 NewLine(1);
259 ioprintf(c1out, _(" DeLink %s [%s]\n"), (OriginalPath + InternalPrefix.length()),
260 SizeToStr(FileSize).c_str());
261 c1out << flush;
262
263 if (NoLinkAct == false)
264 {
265 char OldLink[400];
266 if (readlink(OriginalPath,OldLink,sizeof(OldLink)) == -1)
267 _error->Errno("readlink",_("Failed to readlink %s"),OriginalPath);
268 else
269 {
270 if (unlink(OriginalPath) != 0)
271 _error->Errno("unlink",_("Failed to unlink %s"),OriginalPath);
272 else
273 {
274 if (link(FileName.c_str(),OriginalPath) != 0)
275 {
276 // Panic! Restore the symlink
277 symlink(OldLink,OriginalPath);
278 return _error->Errno("link",_("*** Failed to link %s to %s"),
279 FileName.c_str(),
280 OriginalPath);
281 }
282 }
283 }
284 }
285
286 DeLinkBytes += FileSize;
287 if (DeLinkBytes/1024 >= DeLinkLimit)
288 ioprintf(c1out, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes).c_str());
289 }
290
291 FileName = OriginalPath;
292 }
293
294 return true;
295 }
296 /*}}}*/
297
298 // PackagesWriter::PackagesWriter - Constructor /*{{{*/
299 // ---------------------------------------------------------------------
300 /* */
301 PackagesWriter::PackagesWriter(string const &DB,string const &Overrides,string const &ExtOverrides,
302 string const &Arch) :
303 FTWScanner(Arch), Db(DB), Stats(Db.Stats)
304 {
305 Output = stdout;
306 SetExts(".deb .udeb");
307 DeLinkLimit = 0;
308
309 // Process the command line options
310 DoMD5 = _config->FindB("APT::FTPArchive::MD5",true);
311 DoSHA1 = _config->FindB("APT::FTPArchive::SHA1",true);
312 DoSHA256 = _config->FindB("APT::FTPArchive::SHA256",true);
313 DoAlwaysStat = _config->FindB("APT::FTPArchive::AlwaysStat", false);
314 DoContents = _config->FindB("APT::FTPArchive::Contents",true);
315 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
316 LongDescription = _config->FindB("APT::FTPArchive::LongDescription",true);
317
318 if (Db.Loaded() == false)
319 DoContents = false;
320
321 // Read the override file
322 if (Overrides.empty() == false && Over.ReadOverride(Overrides) == false)
323 return;
324 else
325 NoOverride = true;
326
327 if (ExtOverrides.empty() == false)
328 Over.ReadExtraOverride(ExtOverrides);
329
330 _error->DumpErrors();
331 }
332 /*}}}*/
333 // FTWScanner::SetExts - Set extensions to support /*{{{*/
334 // ---------------------------------------------------------------------
335 /* */
336 bool FTWScanner::SetExts(string const &Vals)
337 {
338 ClearPatterns();
339 string::size_type Start = 0;
340 while (Start <= Vals.length()-1)
341 {
342 string::size_type const Space = Vals.find(' ',Start);
343 string::size_type const Length = ((Space == string::npos) ? Vals.length() : Space) - Start;
344 if ( Arch.empty() == false )
345 {
346 AddPattern(string("*_") + Arch + Vals.substr(Start, Length));
347 AddPattern(string("*_all") + Vals.substr(Start, Length));
348 }
349 else
350 AddPattern(string("*") + Vals.substr(Start, Length));
351
352 Start += Length + 1;
353 }
354
355 return true;
356 }
357
358 /*}}}*/
359 // PackagesWriter::DoPackage - Process a single package /*{{{*/
360 // ---------------------------------------------------------------------
361 /* This method takes a package and gets its control information and
362 MD5, SHA1 and SHA256 then writes out a control record with the proper fields
363 rewritten and the path/size/hash appended. */
364 bool PackagesWriter::DoPackage(string FileName)
365 {
366 // Pull all the data we need form the DB
367 if (Db.GetFileInfo(FileName, true, DoContents, true, DoMD5, DoSHA1, DoSHA256, DoAlwaysStat)
368 == false)
369 {
370 return false;
371 }
372
373 off_t FileSize = Db.GetFileSize();
374 if (Delink(FileName,OriginalPath,Stats.DeLinkBytes,FileSize) == false)
375 return false;
376
377 // Lookup the overide information
378 pkgTagSection &Tags = Db.Control.Section;
379 string Package = Tags.FindS("Package");
380 string Architecture;
381 // if we generate a Packages file for a given arch, we use it to
382 // look for overrides. if we run in "simple" mode without the
383 // "Architecures" variable in the config we use the architecure value
384 // from the deb file
385 if(Arch != "")
386 Architecture = Arch;
387 else
388 Architecture = Tags.FindS("Architecture");
389 auto_ptr<Override::Item> OverItem(Over.GetItem(Package,Architecture));
390
391 if (Package.empty() == true)
392 return _error->Error(_("Archive had no package field"));
393
394 // If we need to do any rewriting of the header do it now..
395 if (OverItem.get() == 0)
396 {
397 if (NoOverride == false)
398 {
399 NewLine(1);
400 ioprintf(c1out, _(" %s has no override entry\n"), Package.c_str());
401 }
402
403 OverItem = auto_ptr<Override::Item>(new Override::Item);
404 OverItem->FieldOverride["Section"] = Tags.FindS("Section");
405 OverItem->Priority = Tags.FindS("Priority");
406 }
407
408 char Size[40];
409 sprintf(Size,"%lu", (unsigned long) FileSize);
410
411 // Strip the DirStrip prefix from the FileName and add the PathPrefix
412 string NewFileName;
413 if (DirStrip.empty() == false &&
414 FileName.length() > DirStrip.length() &&
415 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
416 DirStrip.begin(),DirStrip.end()) == 0)
417 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
418 else
419 NewFileName = FileName;
420 if (PathPrefix.empty() == false)
421 NewFileName = flCombine(PathPrefix,NewFileName);
422
423 /* Configuration says we don't want to include the long Description
424 in the package file - instead we want to ship a separated file */
425 string desc;
426 if (LongDescription == false) {
427 desc = Tags.FindS("Description").append("\n");
428 OverItem->FieldOverride["Description"] = desc.substr(0, desc.find('\n')).c_str();
429 }
430
431 // This lists all the changes to the fields we are going to make.
432 // (7 hardcoded + maintainer + suggests + end marker)
433 TFRewriteData Changes[6+2+OverItem->FieldOverride.size()+1+1];
434
435 unsigned int End = 0;
436 SetTFRewriteData(Changes[End++], "Size", Size);
437 SetTFRewriteData(Changes[End++], "MD5sum", Db.MD5Res.c_str());
438 SetTFRewriteData(Changes[End++], "SHA1", Db.SHA1Res.c_str());
439 SetTFRewriteData(Changes[End++], "SHA256", Db.SHA256Res.c_str());
440 SetTFRewriteData(Changes[End++], "Filename", NewFileName.c_str());
441 SetTFRewriteData(Changes[End++], "Priority", OverItem->Priority.c_str());
442 SetTFRewriteData(Changes[End++], "Status", 0);
443 SetTFRewriteData(Changes[End++], "Optional", 0);
444
445 string DescriptionMd5;
446 if (LongDescription == false) {
447 MD5Summation descmd5;
448 descmd5.Add(desc.c_str());
449 DescriptionMd5 = descmd5.Result().Value();
450 SetTFRewriteData(Changes[End++], "Description-md5", DescriptionMd5.c_str());
451 }
452
453 // Rewrite the maintainer field if necessary
454 bool MaintFailed;
455 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
456 if (MaintFailed == true)
457 {
458 if (NoOverride == false)
459 {
460 NewLine(1);
461 ioprintf(c1out, _(" %s maintainer is %s not %s\n"),
462 Package.c_str(), Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
463 }
464 }
465
466 if (NewMaint.empty() == false)
467 SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
468
469 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
470 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
471 but dpkg does this append bit. So we do the append bit, at least that way the
472 status file and package file will remain similar. There are other transforms
473 but optional is the only legacy one still in use for some lazy reason. */
474 string OptionalStr = Tags.FindS("Optional");
475 if (OptionalStr.empty() == false)
476 {
477 if (Tags.FindS("Suggests").empty() == false)
478 OptionalStr = Tags.FindS("Suggests") + ", " + OptionalStr;
479 SetTFRewriteData(Changes[End++], "Suggests", OptionalStr.c_str());
480 }
481
482 for (map<string,string>::const_iterator I = OverItem->FieldOverride.begin();
483 I != OverItem->FieldOverride.end(); I++)
484 SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
485
486 SetTFRewriteData(Changes[End++], 0, 0);
487
488 // Rewrite and store the fields.
489 if (TFRewrite(Output,Tags,TFRewritePackageOrder,Changes) == false)
490 return false;
491 fprintf(Output,"\n");
492
493 return Db.Finish();
494 }
495 /*}}}*/
496
497 // SourcesWriter::SourcesWriter - Constructor /*{{{*/
498 // ---------------------------------------------------------------------
499 /* */
500 SourcesWriter::SourcesWriter(string const &BOverrides,string const &SOverrides,
501 string const &ExtOverrides)
502 {
503 Output = stdout;
504 AddPattern("*.dsc");
505 DeLinkLimit = 0;
506 Buffer = 0;
507 BufSize = 0;
508
509 // Process the command line options
510 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
511
512 // Read the override file
513 if (BOverrides.empty() == false && BOver.ReadOverride(BOverrides) == false)
514 return;
515 else
516 NoOverride = true;
517
518 // WTF?? The logic above: if we can't read binary overrides, don't even try
519 // reading source overrides. if we can read binary overrides, then say there
520 // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
521
522 if (ExtOverrides.empty() == false)
523 SOver.ReadExtraOverride(ExtOverrides);
524
525 if (SOverrides.empty() == false && FileExists(SOverrides) == true)
526 SOver.ReadOverride(SOverrides,true);
527 }
528 /*}}}*/
529 // SourcesWriter::DoPackage - Process a single package /*{{{*/
530 // ---------------------------------------------------------------------
531 /* */
532 bool SourcesWriter::DoPackage(string FileName)
533 {
534 // Open the archive
535 FileFd F(FileName,FileFd::ReadOnly);
536 if (_error->PendingError() == true)
537 return false;
538
539 // Stat the file for later
540 struct stat St;
541 if (fstat(F.Fd(),&St) != 0)
542 return _error->Errno("fstat","Failed to stat %s",FileName.c_str());
543
544 if (St.st_size > 128*1024)
545 return _error->Error("DSC file '%s' is too large!",FileName.c_str());
546
547 if (BufSize < (unsigned)St.st_size+1)
548 {
549 BufSize = St.st_size+1;
550 Buffer = (char *)realloc(Buffer,St.st_size+1);
551 }
552
553 if (F.Read(Buffer,St.st_size) == false)
554 return false;
555
556 // Hash the file
557 char *Start = Buffer;
558 char *BlkEnd = Buffer + St.st_size;
559 MD5Summation MD5;
560 MD5.Add((unsigned char *)Start,BlkEnd - Start);
561
562 SHA1Summation SHA1;
563 SHA256Summation SHA256;
564 SHA1.Add((unsigned char *)Start,BlkEnd - Start);
565 SHA256.Add((unsigned char *)Start,BlkEnd - Start);
566
567 // Add an extra \n to the end, just in case
568 *BlkEnd++ = '\n';
569
570 /* Remove the PGP trailer. Some .dsc's have this without a blank line
571 before */
572 const char *Key = "-----BEGIN PGP SIGNATURE-----";
573 for (char *MsgEnd = Start; MsgEnd < BlkEnd - strlen(Key) -1; MsgEnd++)
574 {
575 if (*MsgEnd == '\n' && strncmp(MsgEnd+1,Key,strlen(Key)) == 0)
576 {
577 MsgEnd[1] = '\n';
578 break;
579 }
580 }
581
582 /* Read records until we locate the Source record. This neatly skips the
583 GPG header (which is RFC822 formed) without any trouble. */
584 pkgTagSection Tags;
585 do
586 {
587 unsigned Pos;
588 if (Tags.Scan(Start,BlkEnd - Start) == false)
589 return _error->Error("Could not find a record in the DSC '%s'",FileName.c_str());
590 if (Tags.Find("Source",Pos) == true)
591 break;
592 Start += Tags.size();
593 }
594 while (1);
595 Tags.Trim();
596
597 // Lookup the overide information, finding first the best priority.
598 string BestPrio;
599 string Bins = Tags.FindS("Binary");
600 char Buffer[Bins.length() + 1];
601 auto_ptr<Override::Item> OverItem(0);
602 if (Bins.empty() == false)
603 {
604 strcpy(Buffer,Bins.c_str());
605
606 // Ignore too-long errors.
607 char *BinList[400];
608 TokSplitString(',',Buffer,BinList,sizeof(BinList)/sizeof(BinList[0]));
609
610 // Look at all the binaries
611 unsigned char BestPrioV = pkgCache::State::Extra;
612 for (unsigned I = 0; BinList[I] != 0; I++)
613 {
614 auto_ptr<Override::Item> Itm(BOver.GetItem(BinList[I]));
615 if (Itm.get() == 0)
616 continue;
617
618 unsigned char NewPrioV = debListParser::GetPrio(Itm->Priority);
619 if (NewPrioV < BestPrioV || BestPrio.empty() == true)
620 {
621 BestPrioV = NewPrioV;
622 BestPrio = Itm->Priority;
623 }
624
625 if (OverItem.get() == 0)
626 OverItem = Itm;
627 }
628 }
629
630 // If we need to do any rewriting of the header do it now..
631 if (OverItem.get() == 0)
632 {
633 if (NoOverride == false)
634 {
635 NewLine(1);
636 ioprintf(c1out, _(" %s has no override entry\n"), Tags.FindS("Source").c_str());
637 }
638
639 OverItem = auto_ptr<Override::Item>(new Override::Item);
640 }
641
642 auto_ptr<Override::Item> SOverItem(SOver.GetItem(Tags.FindS("Source")));
643 // const auto_ptr<Override::Item> autoSOverItem(SOverItem);
644 if (SOverItem.get() == 0)
645 {
646 ioprintf(c1out, _(" %s has no source override entry\n"), Tags.FindS("Source").c_str());
647 SOverItem = auto_ptr<Override::Item>(BOver.GetItem(Tags.FindS("Source")));
648 if (SOverItem.get() == 0)
649 {
650 ioprintf(c1out, _(" %s has no binary override entry either\n"), Tags.FindS("Source").c_str());
651 SOverItem = auto_ptr<Override::Item>(new Override::Item);
652 *SOverItem = *OverItem;
653 }
654 }
655
656 // Add the dsc to the files hash list
657 string const strippedName = flNotDir(FileName);
658 char Files[1000];
659 snprintf(Files,sizeof(Files),"\n %s %lu %s\n %s",
660 string(MD5.Result()).c_str(),St.st_size,
661 strippedName.c_str(),
662 Tags.FindS("Files").c_str());
663
664 char ChecksumsSha1[1000];
665 snprintf(ChecksumsSha1,sizeof(ChecksumsSha1),"\n %s %lu %s\n %s",
666 string(SHA1.Result()).c_str(),St.st_size,
667 strippedName.c_str(),
668 Tags.FindS("Checksums-Sha1").c_str());
669
670 char ChecksumsSha256[1000];
671 snprintf(ChecksumsSha256,sizeof(ChecksumsSha256),"\n %s %lu %s\n %s",
672 string(SHA256.Result()).c_str(),St.st_size,
673 strippedName.c_str(),
674 Tags.FindS("Checksums-Sha256").c_str());
675
676 // Strip the DirStrip prefix from the FileName and add the PathPrefix
677 string NewFileName;
678 if (DirStrip.empty() == false &&
679 FileName.length() > DirStrip.length() &&
680 stringcmp(DirStrip,OriginalPath,OriginalPath + DirStrip.length()) == 0)
681 NewFileName = string(OriginalPath + DirStrip.length());
682 else
683 NewFileName = OriginalPath;
684 if (PathPrefix.empty() == false)
685 NewFileName = flCombine(PathPrefix,NewFileName);
686
687 string Directory = flNotFile(OriginalPath);
688 string Package = Tags.FindS("Source");
689
690 // Perform the delinking operation over all of the files
691 string ParseJnk;
692 const char *C = Files;
693 char *RealPath = NULL;
694 for (;isspace(*C); C++);
695 while (*C != 0)
696 {
697 // Parse each of the elements
698 if (ParseQuoteWord(C,ParseJnk) == false ||
699 ParseQuoteWord(C,ParseJnk) == false ||
700 ParseQuoteWord(C,ParseJnk) == false)
701 return _error->Error("Error parsing file record");
702
703 char Jnk[2];
704 string OriginalPath = Directory + ParseJnk;
705 if (readlink(OriginalPath.c_str(),Jnk,sizeof(Jnk)) != -1 &&
706 (RealPath = realpath(OriginalPath.c_str(),NULL)) != 0)
707 {
708 string RP = RealPath;
709 free(RealPath);
710 if (Delink(RP,OriginalPath.c_str(),Stats.DeLinkBytes,St.st_size) == false)
711 return false;
712 }
713 }
714
715 Directory = flNotFile(NewFileName);
716 if (Directory.length() > 2)
717 Directory.erase(Directory.end()-1);
718
719 // This lists all the changes to the fields we are going to make.
720 // (5 hardcoded + checksums + maintainer + end marker)
721 TFRewriteData Changes[5+2+1+SOverItem->FieldOverride.size()+1];
722
723 unsigned int End = 0;
724 SetTFRewriteData(Changes[End++],"Source",Package.c_str(),"Package");
725 SetTFRewriteData(Changes[End++],"Files",Files);
726 SetTFRewriteData(Changes[End++],"Checksums-Sha1",ChecksumsSha1);
727 SetTFRewriteData(Changes[End++],"Checksums-Sha256",ChecksumsSha256);
728 if (Directory != "./")
729 SetTFRewriteData(Changes[End++],"Directory",Directory.c_str());
730 SetTFRewriteData(Changes[End++],"Priority",BestPrio.c_str());
731 SetTFRewriteData(Changes[End++],"Status",0);
732
733 // Rewrite the maintainer field if necessary
734 bool MaintFailed;
735 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
736 if (MaintFailed == true)
737 {
738 if (NoOverride == false)
739 {
740 NewLine(1);
741 ioprintf(c1out, _(" %s maintainer is %s not %s\n"), Package.c_str(),
742 Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
743 }
744 }
745 if (NewMaint.empty() == false)
746 SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
747
748 for (map<string,string>::const_iterator I = SOverItem->FieldOverride.begin();
749 I != SOverItem->FieldOverride.end(); I++)
750 SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
751
752 SetTFRewriteData(Changes[End++], 0, 0);
753
754 // Rewrite and store the fields.
755 if (TFRewrite(Output,Tags,TFRewriteSourceOrder,Changes) == false)
756 return false;
757 fprintf(Output,"\n");
758
759 Stats.Packages++;
760
761 return true;
762 }
763 /*}}}*/
764
765 // ContentsWriter::ContentsWriter - Constructor /*{{{*/
766 // ---------------------------------------------------------------------
767 /* */
768 ContentsWriter::ContentsWriter(string const &DB, string const &Arch) :
769 FTWScanner(Arch), Db(DB), Stats(Db.Stats)
770
771 {
772 SetExts(".deb");
773 Output = stdout;
774 }
775 /*}}}*/
776 // ContentsWriter::DoPackage - Process a single package /*{{{*/
777 // ---------------------------------------------------------------------
778 /* If Package is the empty string the control record will be parsed to
779 determine what the package name is. */
780 bool ContentsWriter::DoPackage(string FileName, string Package)
781 {
782 if (!Db.GetFileInfo(FileName, Package.empty(), true, false, false, false, false, false))
783 {
784 return false;
785 }
786
787 // Parse the package name
788 if (Package.empty() == true)
789 {
790 Package = Db.Control.Section.FindS("Package");
791 }
792
793 Db.Contents.Add(Gen,Package);
794
795 return Db.Finish();
796 }
797 /*}}}*/
798 // ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
799 // ---------------------------------------------------------------------
800 /* */
801 bool ContentsWriter::ReadFromPkgs(string const &PkgFile,string const &PkgCompress)
802 {
803 MultiCompress Pkgs(PkgFile,PkgCompress,0,false);
804 if (_error->PendingError() == true)
805 return false;
806
807 // Open the package file
808 int CompFd = -1;
809 pid_t Proc = -1;
810 if (Pkgs.OpenOld(CompFd,Proc) == false)
811 return false;
812
813 // No auto-close FD
814 FileFd Fd(CompFd,false);
815 pkgTagFile Tags(&Fd);
816 if (_error->PendingError() == true)
817 {
818 Pkgs.CloseOld(CompFd,Proc);
819 return false;
820 }
821
822 // Parse.
823 pkgTagSection Section;
824 while (Tags.Step(Section) == true)
825 {
826 string File = flCombine(Prefix,Section.FindS("FileName"));
827 string Package = Section.FindS("Section");
828 if (Package.empty() == false && Package.end()[-1] != '/')
829 {
830 Package += '/';
831 Package += Section.FindS("Package");
832 }
833 else
834 Package += Section.FindS("Package");
835
836 DoPackage(File,Package);
837 if (_error->empty() == false)
838 {
839 _error->Error("Errors apply to file '%s'",File.c_str());
840 _error->DumpErrors();
841 }
842 }
843
844 // Tidy the compressor
845 if (Pkgs.CloseOld(CompFd,Proc) == false)
846 return false;
847
848 return true;
849 }
850
851 /*}}}*/
852
853 // ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
854 // ---------------------------------------------------------------------
855 /* */
856 ReleaseWriter::ReleaseWriter(string const &DB)
857 {
858 AddPattern("Packages");
859 AddPattern("Packages.gz");
860 AddPattern("Packages.bz2");
861 AddPattern("Packages.lzma");
862 AddPattern("Sources");
863 AddPattern("Sources.gz");
864 AddPattern("Sources.bz2");
865 AddPattern("Sources.lzma");
866 AddPattern("Release");
867 AddPattern("md5sum.txt");
868
869 Output = stdout;
870 time_t const now = time(NULL);
871 char datestr[128];
872 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %H:%M:%S UTC",
873 gmtime(&now)) == 0)
874 {
875 datestr[0] = '\0';
876 }
877
878 map<string,string> Fields;
879 Fields["Origin"] = "";
880 Fields["Label"] = "";
881 Fields["Suite"] = "";
882 Fields["Version"] = "";
883 Fields["Codename"] = "";
884 Fields["Date"] = datestr;
885 Fields["Architectures"] = "";
886 Fields["Components"] = "";
887 Fields["Description"] = "";
888
889 for(map<string,string>::const_iterator I = Fields.begin();
890 I != Fields.end();
891 ++I)
892 {
893 string Config = string("APT::FTPArchive::Release::") + (*I).first;
894 string Value = _config->Find(Config, (*I).second.c_str());
895 if (Value == "")
896 continue;
897
898 fprintf(Output, "%s: %s\n", (*I).first.c_str(), Value.c_str());
899 }
900 }
901 /*}}}*/
902 // ReleaseWriter::DoPackage - Process a single package /*{{{*/
903 // ---------------------------------------------------------------------
904 bool ReleaseWriter::DoPackage(string FileName)
905 {
906 // Strip the DirStrip prefix from the FileName and add the PathPrefix
907 string NewFileName;
908 if (DirStrip.empty() == false &&
909 FileName.length() > DirStrip.length() &&
910 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
911 DirStrip.begin(),DirStrip.end()) == 0)
912 {
913 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
914 while (NewFileName[0] == '/')
915 NewFileName = string(NewFileName.begin() + 1,NewFileName.end());
916 }
917 else
918 NewFileName = FileName;
919
920 if (PathPrefix.empty() == false)
921 NewFileName = flCombine(PathPrefix,NewFileName);
922
923 FileFd fd(FileName, FileFd::ReadOnly);
924
925 if (!fd.IsOpen())
926 {
927 return false;
928 }
929
930 CheckSums[NewFileName].size = fd.Size();
931
932 MD5Summation MD5;
933 MD5.AddFD(fd.Fd(), fd.Size());
934 CheckSums[NewFileName].MD5 = MD5.Result();
935
936 fd.Seek(0);
937 SHA1Summation SHA1;
938 SHA1.AddFD(fd.Fd(), fd.Size());
939 CheckSums[NewFileName].SHA1 = SHA1.Result();
940
941 fd.Seek(0);
942 SHA256Summation SHA256;
943 SHA256.AddFD(fd.Fd(), fd.Size());
944 CheckSums[NewFileName].SHA256 = SHA256.Result();
945
946 fd.Close();
947
948 return true;
949 }
950
951 /*}}}*/
952 // ReleaseWriter::Finish - Output the checksums /*{{{*/
953 // ---------------------------------------------------------------------
954 void ReleaseWriter::Finish()
955 {
956 fprintf(Output, "MD5Sum:\n");
957 for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
958 I != CheckSums.end();
959 ++I)
960 {
961 fprintf(Output, " %s %16ld %s\n",
962 (*I).second.MD5.c_str(),
963 (*I).second.size,
964 (*I).first.c_str());
965 }
966
967 fprintf(Output, "SHA1:\n");
968 for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
969 I != CheckSums.end();
970 ++I)
971 {
972 fprintf(Output, " %s %16ld %s\n",
973 (*I).second.SHA1.c_str(),
974 (*I).second.size,
975 (*I).first.c_str());
976 }
977
978 fprintf(Output, "SHA256:\n");
979 for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
980 I != CheckSums.end();
981 ++I)
982 {
983 fprintf(Output, " %s %16ld %s\n",
984 (*I).second.SHA256.c_str(),
985 (*I).second.size,
986 (*I).first.c_str());
987 }
988 }
989