]> git.saurik.com Git - apt.git/blame - ftparchive/writer.cc
policy: Get rid of old (pre-1.1) GetCandidateVer algorithm
[apt.git] / ftparchive / writer.cc
CommitLineData
b2e465d6
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
5200ec6f 3// $Id: writer.cc,v 1.14 2004/03/24 01:40:43 mdz Exp $
b2e465d6
AL
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 /*{{{*/
ea542140
DK
14#include <config.h>
15
b2e465d6 16#include <apt-pkg/configuration.h>
b2e465d6 17#include <apt-pkg/deblistparser.h>
453b82a3 18#include <apt-pkg/error.h>
f1828b69
DK
19#include <apt-pkg/fileutl.h>
20#include <apt-pkg/gpgv.h>
453b82a3
DK
21#include <apt-pkg/hashes.h>
22#include <apt-pkg/md5.h>
23#include <apt-pkg/strutl.h>
24#include <apt-pkg/debfile.h>
25#include <apt-pkg/pkgcache.h>
26#include <apt-pkg/sha1.h>
27#include <apt-pkg/sha2.h>
28#include <apt-pkg/tagfile.h>
b2e465d6 29
453b82a3
DK
30#include <ctype.h>
31#include <fnmatch.h>
32#include <ftw.h>
33#include <locale.h>
34#include <string.h>
35#include <sys/stat.h>
b2e465d6
AL
36#include <sys/types.h>
37#include <unistd.h>
98953965 38#include <ctime>
8c58f506 39#include <iostream>
bf99a6d3 40#include <sstream>
4f333a8b 41#include <memory>
453b82a3 42#include <utility>
7852873a 43#include <algorithm>
ea542140 44
453b82a3 45#include "apt-ftparchive.h"
ea542140 46#include "writer.h"
b2e465d6 47#include "cachedb.h"
b2e465d6 48#include "multicompress.h"
7852873a 49#include "byhash.h"
ea542140
DK
50
51#include <apti18n.h>
b2e465d6 52 /*}}}*/
8c58f506 53using namespace std;
b2e465d6
AL
54FTWScanner *FTWScanner::Owner;
55
a311fb96
DK
56// ConfigToDoHashes - which hashes to generate /*{{{*/
57static void SingleConfigToDoHashes(unsigned int &DoHashes, std::string const &Conf, unsigned int const Flag)
58{
2cc48828 59 if (_config->FindB(Conf, (DoHashes & Flag) == Flag) == true)
a311fb96
DK
60 DoHashes |= Flag;
61 else
62 DoHashes &= ~Flag;
63}
64static void ConfigToDoHashes(unsigned int &DoHashes, std::string const &Conf)
65{
66 SingleConfigToDoHashes(DoHashes, Conf + "::MD5", Hashes::MD5SUM);
67 SingleConfigToDoHashes(DoHashes, Conf + "::SHA1", Hashes::SHA1SUM);
68 SingleConfigToDoHashes(DoHashes, Conf + "::SHA256", Hashes::SHA256SUM);
69 SingleConfigToDoHashes(DoHashes, Conf + "::SHA512", Hashes::SHA512SUM);
70}
71 /*}}}*/
64177f17 72
b2e465d6 73// FTWScanner::FTWScanner - Constructor /*{{{*/
1dd20368
DK
74FTWScanner::FTWScanner(FileFd * const GivenOutput, string const &Arch, bool const IncludeArchAll)
75 : Arch(Arch), IncludeArchAll(IncludeArchAll), DoHashes(~0)
b2e465d6 76{
88593886
DK
77 if (GivenOutput == NULL)
78 {
79 Output = new FileFd;
3d8232bf 80 OwnsOutput = true;
88593886
DK
81 Output->OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly, false);
82 }
83 else
3d8232bf 84 {
88593886 85 Output = GivenOutput;
3d8232bf
DK
86 OwnsOutput = false;
87 }
b2e465d6
AL
88 ErrorPrinted = false;
89 NoLinkAct = !_config->FindB("APT::FTPArchive::DeLinkAct",true);
a311fb96 90 ConfigToDoHashes(DoHashes, "APT::FTPArchive");
b2e465d6
AL
91}
92 /*}}}*/
3d8232bf
DK
93FTWScanner::~FTWScanner()
94{
95 if (Output != NULL && OwnsOutput)
96 delete Output;
97}
b2e465d6
AL
98// FTWScanner::Scanner - FTW Scanner /*{{{*/
99// ---------------------------------------------------------------------
65512241 100/* This is the FTW scanner, it processes each directory element in the
b2e465d6 101 directory tree. */
65512241 102int FTWScanner::ScannerFTW(const char *File,const struct stat * /*sb*/,int Flag)
b2e465d6
AL
103{
104 if (Flag == FTW_DNR)
105 {
106 Owner->NewLine(1);
dc738e7a 107 ioprintf(c1out, _("W: Unable to read directory %s\n"), File);
b2e465d6
AL
108 }
109 if (Flag == FTW_NS)
110 {
111 Owner->NewLine(1);
dc738e7a 112 ioprintf(c1out, _("W: Unable to stat %s\n"), File);
b2e465d6
AL
113 }
114 if (Flag != FTW_F)
115 return 0;
116
cde41ae8
MV
117 return ScannerFile(File, true);
118}
119 /*}}}*/
120// FTWScanner::ScannerFile - File Scanner /*{{{*/
121// ---------------------------------------------------------------------
122/* */
9209ec47 123int FTWScanner::ScannerFile(const char *File, bool const &ReadLink)
cde41ae8 124{
98953965 125 const char *LastComponent = strrchr(File, '/');
ab3846c0
MV
126 char *RealPath = NULL;
127
98953965
AL
128 if (LastComponent == NULL)
129 LastComponent = File;
130 else
131 LastComponent++;
132
9209ec47 133 vector<string>::const_iterator I;
98953965
AL
134 for(I = Owner->Patterns.begin(); I != Owner->Patterns.end(); ++I)
135 {
136 if (fnmatch((*I).c_str(), LastComponent, 0) == 0)
137 break;
138 }
139 if (I == Owner->Patterns.end())
b2e465d6
AL
140 return 0;
141
142 /* Process it. If the file is a link then resolve it into an absolute
143 name.. This works best if the directory components the scanner are
144 given are not links themselves. */
145 char Jnk[2];
146 Owner->OriginalPath = File;
ab3846c0 147 if (ReadLink &&
cde41ae8 148 readlink(File,Jnk,sizeof(Jnk)) != -1 &&
ab3846c0
MV
149 (RealPath = realpath(File,NULL)) != 0)
150 {
151 Owner->DoPackage(RealPath);
152 free(RealPath);
153 }
b2e465d6
AL
154 else
155 Owner->DoPackage(File);
156
157 if (_error->empty() == false)
158 {
159 // Print any errors or warnings found
160 string Err;
161 bool SeenPath = false;
162 while (_error->empty() == false)
163 {
164 Owner->NewLine(1);
165
9209ec47 166 bool const Type = _error->PopMessage(Err);
b2e465d6 167 if (Type == true)
dc738e7a 168 cerr << _("E: ") << Err << endl;
b2e465d6 169 else
dc738e7a 170 cerr << _("W: ") << Err << endl;
b2e465d6
AL
171
172 if (Err.find(File) != string::npos)
173 SeenPath = true;
174 }
175
176 if (SeenPath == false)
dc738e7a 177 cerr << _("E: Errors apply to file ") << "'" << File << "'" << endl;
b2e465d6
AL
178 return 0;
179 }
180
181 return 0;
182}
183 /*}}}*/
184// FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
185// ---------------------------------------------------------------------
186/* */
9209ec47 187bool FTWScanner::RecursiveScan(string const &Dir)
b2e465d6 188{
ab3846c0 189 char *RealPath = NULL;
b2e465d6
AL
190 /* If noprefix is set then jam the scan root in, so we don't generate
191 link followed paths out of control */
192 if (InternalPrefix.empty() == true)
193 {
ab3846c0 194 if ((RealPath = realpath(Dir.c_str(),NULL)) == 0)
dc738e7a 195 return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
ab3846c0
MV
196 InternalPrefix = RealPath;
197 free(RealPath);
b2e465d6
AL
198 }
199
200 // Do recursive directory searching
201 Owner = this;
9209ec47 202 int const Res = ftw(Dir.c_str(),ScannerFTW,30);
b2e465d6
AL
203
204 // Error treewalking?
205 if (Res != 0)
206 {
207 if (_error->PendingError() == false)
dc738e7a 208 _error->Errno("ftw",_("Tree walking failed"));
b2e465d6
AL
209 return false;
210 }
211
212 return true;
213}
214 /*}}}*/
215// FTWScanner::LoadFileList - Load the file list from a file /*{{{*/
216// ---------------------------------------------------------------------
217/* This is an alternative to using FTW to locate files, it reads the list
218 of files from another file. */
9209ec47 219bool FTWScanner::LoadFileList(string const &Dir, string const &File)
b2e465d6 220{
ab3846c0 221 char *RealPath = NULL;
b2e465d6
AL
222 /* If noprefix is set then jam the scan root in, so we don't generate
223 link followed paths out of control */
224 if (InternalPrefix.empty() == true)
225 {
ab3846c0 226 if ((RealPath = realpath(Dir.c_str(),NULL)) == 0)
dc738e7a 227 return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
b2e465d6 228 InternalPrefix = RealPath;
ab3846c0 229 free(RealPath);
b2e465d6
AL
230 }
231
232 Owner = this;
233 FILE *List = fopen(File.c_str(),"r");
234 if (List == 0)
dc738e7a 235 return _error->Errno("fopen",_("Failed to open %s"),File.c_str());
b2e465d6
AL
236
237 /* We are a tad tricky here.. We prefix the buffer with the directory
238 name, that way if we need a full path with just use line.. Sneaky and
239 fully evil. */
240 char Line[1000];
241 char *FileStart;
242 if (Dir.empty() == true || Dir.end()[-1] != '/')
243 FileStart = Line + snprintf(Line,sizeof(Line),"%s/",Dir.c_str());
244 else
245 FileStart = Line + snprintf(Line,sizeof(Line),"%s",Dir.c_str());
246 while (fgets(FileStart,sizeof(Line) - (FileStart - Line),List) != 0)
247 {
248 char *FileName = _strstrip(FileStart);
249 if (FileName[0] == 0)
250 continue;
251
252 if (FileName[0] != '/')
253 {
254 if (FileName != FileStart)
255 memmove(FileStart,FileName,strlen(FileStart));
256 FileName = Line;
257 }
258
cde41ae8 259#if 0
b2e465d6
AL
260 struct stat St;
261 int Flag = FTW_F;
262 if (stat(FileName,&St) != 0)
263 Flag = FTW_NS;
cde41ae8 264#endif
b2e465d6 265
cde41ae8 266 if (ScannerFile(FileName, false) != 0)
b2e465d6
AL
267 break;
268 }
269
270 fclose(List);
271 return true;
272}
273 /*}}}*/
274// FTWScanner::Delink - Delink symlinks /*{{{*/
275// ---------------------------------------------------------------------
276/* */
277bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
650faab0
DK
278 unsigned long long &DeLinkBytes,
279 unsigned long long const &FileSize)
b2e465d6
AL
280{
281 // See if this isn't an internaly prefix'd file name.
282 if (InternalPrefix.empty() == false &&
283 InternalPrefix.length() < FileName.length() &&
284 stringcmp(FileName.begin(),FileName.begin() + InternalPrefix.length(),
285 InternalPrefix.begin(),InternalPrefix.end()) != 0)
286 {
287 if (DeLinkLimit != 0 && DeLinkBytes/1024 < DeLinkLimit)
288 {
289 // Tidy up the display
290 if (DeLinkBytes == 0)
291 cout << endl;
292
293 NewLine(1);
dc738e7a 294 ioprintf(c1out, _(" DeLink %s [%s]\n"), (OriginalPath + InternalPrefix.length()),
cde41ae8 295 SizeToStr(FileSize).c_str());
dc738e7a 296 c1out << flush;
b2e465d6
AL
297
298 if (NoLinkAct == false)
299 {
300 char OldLink[400];
301 if (readlink(OriginalPath,OldLink,sizeof(OldLink)) == -1)
dc738e7a 302 _error->Errno("readlink",_("Failed to readlink %s"),OriginalPath);
b2e465d6
AL
303 else
304 {
ce1f3a2c 305 if (RemoveFile("FTWScanner::Delink", OriginalPath))
b2e465d6
AL
306 {
307 if (link(FileName.c_str(),OriginalPath) != 0)
308 {
309 // Panic! Restore the symlink
f52037d6
MV
310 if (symlink(OldLink,OriginalPath) != 0)
311 _error->Errno("symlink", "failed to restore symlink");
dc738e7a 312 return _error->Errno("link",_("*** Failed to link %s to %s"),
b2e465d6
AL
313 FileName.c_str(),
314 OriginalPath);
315 }
316 }
317 }
318 }
319
cde41ae8 320 DeLinkBytes += FileSize;
b2e465d6 321 if (DeLinkBytes/1024 >= DeLinkLimit)
dc738e7a 322 ioprintf(c1out, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes).c_str());
b2e465d6
AL
323 }
324
325 FileName = OriginalPath;
326 }
327
1dd20368
DK
328 return true;
329}
330 /*}}}*/
331// FTWScanner::SetExts - Set extensions to support /*{{{*/
332// ---------------------------------------------------------------------
333/* */
334bool FTWScanner::SetExts(string const &Vals)
335{
336 ClearPatterns();
337 string::size_type Start = 0;
338 while (Start <= Vals.length()-1)
339 {
340 string::size_type const Space = Vals.find(' ',Start);
341 string::size_type const Length = ((Space == string::npos) ? Vals.length() : Space) - Start;
342 if ( Arch.empty() == false )
343 {
344 AddPattern(string("*_") + Arch + Vals.substr(Start, Length));
345 if (IncludeArchAll == true && Arch != "all")
346 AddPattern(string("*_all") + Vals.substr(Start, Length));
347 }
348 else
349 AddPattern(string("*") + Vals.substr(Start, Length));
350
351 Start += Length + 1;
352 }
353
b2e465d6
AL
354 return true;
355}
356 /*}}}*/
b2e465d6
AL
357
358// PackagesWriter::PackagesWriter - Constructor /*{{{*/
359// ---------------------------------------------------------------------
360/* */
3d8232bf
DK
361PackagesWriter::PackagesWriter(FileFd * const GivenOutput, TranslationWriter * const transWriter,
362 string const &DB,string const &Overrides,string const &ExtOverrides,
1dd20368
DK
363 string const &Arch, bool const IncludeArchAll) :
364 FTWScanner(GivenOutput, Arch, IncludeArchAll), Db(DB), Stats(Db.Stats), TransWriter(transWriter)
b2e465d6 365{
31981076 366 SetExts(".deb .udeb");
b2e465d6 367 DeLinkLimit = 0;
3cb3fe76 368
b2e465d6 369 // Process the command line options
a311fb96 370 ConfigToDoHashes(DoHashes, "APT::FTPArchive::Packages");
ff574e76 371 DoAlwaysStat = _config->FindB("APT::FTPArchive::AlwaysStat", false);
b2e465d6
AL
372 DoContents = _config->FindB("APT::FTPArchive::Contents",true);
373 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
9c24493f 374 LongDescription = _config->FindB("APT::FTPArchive::LongDescription",true);
b2e465d6
AL
375
376 if (Db.Loaded() == false)
377 DoContents = false;
66905344 378
b2e465d6
AL
379 // Read the override file
380 if (Overrides.empty() == false && Over.ReadOverride(Overrides) == false)
381 return;
382 else
383 NoOverride = true;
64177f17
AL
384
385 if (ExtOverrides.empty() == false)
386 Over.ReadExtraOverride(ExtOverrides);
387
b2e465d6
AL
388 _error->DumpErrors();
389}
98953965 390 /*}}}*/
b2e465d6
AL
391// PackagesWriter::DoPackage - Process a single package /*{{{*/
392// ---------------------------------------------------------------------
393/* This method takes a package and gets its control information and
cde41ae8
MV
394 MD5, SHA1 and SHA256 then writes out a control record with the proper fields
395 rewritten and the path/size/hash appended. */
b2e465d6
AL
396bool PackagesWriter::DoPackage(string FileName)
397{
b2e465d6 398 // Pull all the data we need form the DB
a311fb96
DK
399 if (Db.GetFileInfo(FileName,
400 true, /* DoControl */
401 DoContents,
402 true, /* GenContentsOnly */
403 false, /* DoSource */
404 DoHashes, DoAlwaysStat) == false)
cde41ae8 405 {
ce928105 406 return false;
cde41ae8 407 }
b2e465d6 408
650faab0 409 unsigned long long FileSize = Db.GetFileSize();
cde41ae8 410 if (Delink(FileName,OriginalPath,Stats.DeLinkBytes,FileSize) == false)
b2e465d6
AL
411 return false;
412
413 // Lookup the overide information
414 pkgTagSection &Tags = Db.Control.Section;
415 string Package = Tags.FindS("Package");
0b41e0e7
MV
416 string Architecture;
417 // if we generate a Packages file for a given arch, we use it to
418 // look for overrides. if we run in "simple" mode without the
419 // "Architecures" variable in the config we use the architecure value
420 // from the deb file
421 if(Arch != "")
422 Architecture = Arch;
423 else
424 Architecture = Tags.FindS("Architecture");
47c37a1b 425 unique_ptr<Override::Item> OverItem(Over.GetItem(Package,Architecture));
b2e465d6
AL
426
427 if (Package.empty() == true)
dc738e7a 428 return _error->Error(_("Archive had no package field"));
0b41e0e7 429
b2e465d6 430 // If we need to do any rewriting of the header do it now..
0b41e0e7 431 if (OverItem.get() == 0)
b2e465d6
AL
432 {
433 if (NoOverride == false)
434 {
435 NewLine(1);
dc738e7a 436 ioprintf(c1out, _(" %s has no override entry\n"), Package.c_str());
b2e465d6
AL
437 }
438
47c37a1b 439 OverItem = unique_ptr<Override::Item>(new Override::Item);
0b41e0e7
MV
440 OverItem->FieldOverride["Section"] = Tags.FindS("Section");
441 OverItem->Priority = Tags.FindS("Priority");
b2e465d6
AL
442 }
443
b2e465d6
AL
444 // Strip the DirStrip prefix from the FileName and add the PathPrefix
445 string NewFileName;
446 if (DirStrip.empty() == false &&
447 FileName.length() > DirStrip.length() &&
448 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
449 DirStrip.begin(),DirStrip.end()) == 0)
450 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
451 else
452 NewFileName = FileName;
453 if (PathPrefix.empty() == false)
454 NewFileName = flCombine(PathPrefix,NewFileName);
9c24493f
DK
455
456 /* Configuration says we don't want to include the long Description
457 in the package file - instead we want to ship a separated file */
458 string desc;
459 if (LongDescription == false) {
460 desc = Tags.FindS("Description").append("\n");
461 OverItem->FieldOverride["Description"] = desc.substr(0, desc.find('\n')).c_str();
462 }
463
b2e465d6 464 // This lists all the changes to the fields we are going to make.
88593886 465 std::vector<pkgTagSection::Tag> Changes;
64177f17 466
b8eba208
DK
467 std::string Size;
468 strprintf(Size, "%llu", (unsigned long long) FileSize);
88593886 469 Changes.push_back(pkgTagSection::Tag::Rewrite("Size", Size));
b8eba208 470
a311fb96
DK
471 for (HashStringList::const_iterator hs = Db.HashesList.begin(); hs != Db.HashesList.end(); ++hs)
472 {
473 if (hs->HashType() == "MD5Sum")
88593886 474 Changes.push_back(pkgTagSection::Tag::Rewrite("MD5sum", hs->HashValue()));
23397c9d
DK
475 else if (hs->HashType() == "Checksum-FileSize")
476 continue;
a311fb96 477 else
88593886 478 Changes.push_back(pkgTagSection::Tag::Rewrite(hs->HashType(), hs->HashValue()));
a311fb96 479 }
88593886
DK
480 Changes.push_back(pkgTagSection::Tag::Rewrite("Filename", NewFileName));
481 Changes.push_back(pkgTagSection::Tag::Rewrite("Priority", OverItem->Priority));
482 Changes.push_back(pkgTagSection::Tag::Remove("Status"));
483 Changes.push_back(pkgTagSection::Tag::Remove("Optional"));
64177f17 484
9c24493f
DK
485 string DescriptionMd5;
486 if (LongDescription == false) {
487 MD5Summation descmd5;
488 descmd5.Add(desc.c_str());
489 DescriptionMd5 = descmd5.Result().Value();
88593886 490 Changes.push_back(pkgTagSection::Tag::Rewrite("Description-md5", DescriptionMd5));
66905344
DK
491 if (TransWriter != NULL)
492 TransWriter->DoPackage(Package, desc, DescriptionMd5);
9c24493f
DK
493 }
494
b2e465d6
AL
495 // Rewrite the maintainer field if necessary
496 bool MaintFailed;
497 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
498 if (MaintFailed == true)
499 {
500 if (NoOverride == false)
501 {
502 NewLine(1);
dc738e7a
AL
503 ioprintf(c1out, _(" %s maintainer is %s not %s\n"),
504 Package.c_str(), Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
eb0d90f0 505 }
b2e465d6 506 }
eb0d90f0 507
b2e465d6 508 if (NewMaint.empty() == false)
88593886 509 Changes.push_back(pkgTagSection::Tag::Rewrite("Maintainer", NewMaint));
eb0d90f0 510
b2e465d6 511 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
c6474fb6 512 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
b2e465d6
AL
513 but dpkg does this append bit. So we do the append bit, at least that way the
514 status file and package file will remain similar. There are other transforms
515 but optional is the only legacy one still in use for some lazy reason. */
516 string OptionalStr = Tags.FindS("Optional");
517 if (OptionalStr.empty() == false)
518 {
519 if (Tags.FindS("Suggests").empty() == false)
520 OptionalStr = Tags.FindS("Suggests") + ", " + OptionalStr;
88593886 521 Changes.push_back(pkgTagSection::Tag::Rewrite("Suggests", OptionalStr));
b2e465d6 522 }
64177f17 523
eb0d90f0 524 for (map<string,string>::const_iterator I = OverItem->FieldOverride.begin();
f7f0d6c7 525 I != OverItem->FieldOverride.end(); ++I)
88593886 526 Changes.push_back(pkgTagSection::Tag::Rewrite(I->first, I->second));
64177f17 527
b2e465d6 528 // Rewrite and store the fields.
88593886
DK
529 if (Tags.Write(*Output, TFRewritePackageOrder, Changes) == false ||
530 Output->Write("\n", 1) == false)
b2e465d6 531 return false;
b2e465d6
AL
532
533 return Db.Finish();
534}
535 /*}}}*/
3d8232bf
DK
536PackagesWriter::~PackagesWriter() /*{{{*/
537{
538}
539 /*}}}*/
b2e465d6 540
66905344
DK
541// TranslationWriter::TranslationWriter - Constructor /*{{{*/
542// ---------------------------------------------------------------------
543/* Create a Translation-Master file for this Packages file */
34f1d96c 544TranslationWriter::TranslationWriter(string const &File, string const &TransCompress,
3d8232bf 545 mode_t const &Permissions) : Comp(NULL), Output(NULL)
66905344
DK
546{
547 if (File.empty() == true)
548 return;
549
34f1d96c 550 Comp = new MultiCompress(File, TransCompress, Permissions);
88593886 551 Output = &Comp->Input;
66905344
DK
552}
553 /*}}}*/
554// TranslationWriter::DoPackage - Process a single package /*{{{*/
555// ---------------------------------------------------------------------
556/* Create a Translation-Master file for this Packages file */
557bool TranslationWriter::DoPackage(string const &Pkg, string const &Desc,
558 string const &MD5)
559{
560 if (Output == NULL)
561 return true;
562
563 // Different archs can include different versions and therefore
564 // different descriptions - so we need to check for both name and md5.
565 string const Record = Pkg + ":" + MD5;
566
567 if (Included.find(Record) != Included.end())
568 return true;
569
88593886
DK
570 std::string out;
571 strprintf(out, "Package: %s\nDescription-md5: %s\nDescription-en: %s\n",
66905344 572 Pkg.c_str(), MD5.c_str(), Desc.c_str());
88593886 573 Output->Write(out.c_str(), out.length());
66905344
DK
574
575 Included.insert(Record);
576 return true;
577}
578 /*}}}*/
579// TranslationWriter::~TranslationWriter - Destructor /*{{{*/
580// ---------------------------------------------------------------------
581/* */
582TranslationWriter::~TranslationWriter()
583{
3d8232bf
DK
584 if (Comp != NULL)
585 delete Comp;
66905344
DK
586}
587 /*}}}*/
588
b2e465d6
AL
589// SourcesWriter::SourcesWriter - Constructor /*{{{*/
590// ---------------------------------------------------------------------
591/* */
88593886 592SourcesWriter::SourcesWriter(FileFd * const GivenOutput, string const &DB, string const &BOverrides,string const &SOverrides,
f6f06a8f 593 string const &ExtOverrides) :
88593886 594 FTWScanner(GivenOutput), Db(DB), Stats(Db.Stats)
b2e465d6 595{
98953965 596 AddPattern("*.dsc");
b2e465d6
AL
597 DeLinkLimit = 0;
598 Buffer = 0;
599 BufSize = 0;
600
601 // Process the command line options
a311fb96 602 ConfigToDoHashes(DoHashes, "APT::FTPArchive::Sources");
b2e465d6 603 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
f6f06a8f 604 DoAlwaysStat = _config->FindB("APT::FTPArchive::AlwaysStat", false);
b2e465d6
AL
605
606 // Read the override file
607 if (BOverrides.empty() == false && BOver.ReadOverride(BOverrides) == false)
608 return;
609 else
610 NoOverride = true;
64177f17 611
cde41ae8
MV
612 // WTF?? The logic above: if we can't read binary overrides, don't even try
613 // reading source overrides. if we can read binary overrides, then say there
614 // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
615
64177f17
AL
616 if (ExtOverrides.empty() == false)
617 SOver.ReadExtraOverride(ExtOverrides);
b2e465d6 618
64177f17
AL
619 if (SOverrides.empty() == false && FileExists(SOverrides) == true)
620 SOver.ReadOverride(SOverrides,true);
b2e465d6
AL
621}
622 /*}}}*/
623// SourcesWriter::DoPackage - Process a single package /*{{{*/
88593886 624static std::string getDscHash(unsigned int const DoHashes,
a311fb96
DK
625 Hashes::SupportedHashes const DoIt, pkgTagSection &Tags, char const * const FieldName,
626 HashString const * const Hash, unsigned long long Size, std::string FileName)
627{
628 if ((DoHashes & DoIt) != DoIt || Tags.Exists(FieldName) == false || Hash == NULL)
88593886
DK
629 return "";
630 std::ostringstream out;
a311fb96
DK
631 out << "\n " << Hash->HashValue() << " " << Size << " " << FileName
632 << "\n " << Tags.FindS(FieldName);
88593886 633 return out.str();
a311fb96 634}
b2e465d6 635bool SourcesWriter::DoPackage(string FileName)
f1828b69 636{
ce928105
MV
637 // Pull all the data we need form the DB
638 if (Db.GetFileInfo(FileName,
a311fb96
DK
639 false, /* DoControl */
640 false, /* DoContents */
641 false, /* GenContentsOnly */
642 true, /* DoSource */
643 DoHashes, DoAlwaysStat) == false)
b2e465d6 644 {
b2e465d6 645 return false;
ce928105 646 }
b2e465d6 647
ce928105
MV
648 // we need to perform a "write" here (this is what finish is doing)
649 // because the call to Db.GetFileInfo() in the loop will change
650 // the "db cursor"
651 Db.Finish();
f1828b69 652
f1828b69 653 pkgTagSection Tags;
31be38d2 654 if (Tags.Scan(Db.Dsc.Data.c_str(), Db.Dsc.Data.length()) == false)
f1828b69 655 return _error->Error("Could not find a record in the DSC '%s'",FileName.c_str());
31be38d2 656
ce928105
MV
657 if (Tags.Exists("Source") == false)
658 return _error->Error("Could not find a Source entry in the DSC '%s'",FileName.c_str());
b2e465d6 659 Tags.Trim();
f1828b69 660
b2e465d6
AL
661 // Lookup the overide information, finding first the best priority.
662 string BestPrio;
b2e465d6 663 string Bins = Tags.FindS("Binary");
5200ec6f 664 char Buffer[Bins.length() + 1];
47c37a1b 665 unique_ptr<Override::Item> OverItem(nullptr);
5200ec6f 666 if (Bins.empty() == false)
b2e465d6
AL
667 {
668 strcpy(Buffer,Bins.c_str());
669
670 // Ignore too-long errors.
671 char *BinList[400];
672 TokSplitString(',',Buffer,BinList,sizeof(BinList)/sizeof(BinList[0]));
673
674 // Look at all the binaries
675 unsigned char BestPrioV = pkgCache::State::Extra;
676 for (unsigned I = 0; BinList[I] != 0; I++)
677 {
47c37a1b 678 unique_ptr<Override::Item> Itm(BOver.GetItem(BinList[I]));
0b41e0e7 679 if (Itm.get() == 0)
b2e465d6 680 continue;
b2e465d6
AL
681
682 unsigned char NewPrioV = debListParser::GetPrio(Itm->Priority);
683 if (NewPrioV < BestPrioV || BestPrio.empty() == true)
684 {
685 BestPrioV = NewPrioV;
686 BestPrio = Itm->Priority;
687 }
7524e348
MV
688
689 if (OverItem.get() == 0)
47c37a1b 690 OverItem = std::move(Itm);
b2e465d6
AL
691 }
692 }
693
694 // If we need to do any rewriting of the header do it now..
0b41e0e7 695 if (OverItem.get() == 0)
b2e465d6
AL
696 {
697 if (NoOverride == false)
698 {
699 NewLine(1);
dc738e7a 700 ioprintf(c1out, _(" %s has no override entry\n"), Tags.FindS("Source").c_str());
b2e465d6
AL
701 }
702
47c37a1b 703 OverItem.reset(new Override::Item);
b2e465d6
AL
704 }
705
ce928105
MV
706 struct stat St;
707 if (stat(FileName.c_str(), &St) != 0)
708 return _error->Errno("fstat","Failed to stat %s",FileName.c_str());
709
47c37a1b
JAK
710 unique_ptr<Override::Item> SOverItem(SOver.GetItem(Tags.FindS("Source")));
711 // const unique_ptr<Override::Item> autoSOverItem(SOverItem);
0b41e0e7 712 if (SOverItem.get() == 0)
b2e465d6 713 {
cde41ae8 714 ioprintf(c1out, _(" %s has no source override entry\n"), Tags.FindS("Source").c_str());
47c37a1b 715 SOverItem = unique_ptr<Override::Item>(BOver.GetItem(Tags.FindS("Source")));
0b41e0e7
MV
716 if (SOverItem.get() == 0)
717 {
cde41ae8 718 ioprintf(c1out, _(" %s has no binary override entry either\n"), Tags.FindS("Source").c_str());
47c37a1b 719 SOverItem = unique_ptr<Override::Item>(new Override::Item);
0b41e0e7
MV
720 *SOverItem = *OverItem;
721 }
b2e465d6 722 }
a311fb96 723
b2e465d6 724 // Add the dsc to the files hash list
f99da908 725 string const strippedName = flNotDir(FileName);
88593886
DK
726 std::string const Files = getDscHash(DoHashes, Hashes::MD5SUM, Tags, "Files", Db.HashesList.find("MD5Sum"), St.st_size, strippedName);
727 std::string ChecksumsSha1 = getDscHash(DoHashes, Hashes::SHA1SUM, Tags, "Checksums-Sha1", Db.HashesList.find("SHA1"), St.st_size, strippedName);
728 std::string ChecksumsSha256 = getDscHash(DoHashes, Hashes::SHA256SUM, Tags, "Checksums-Sha256", Db.HashesList.find("SHA256"), St.st_size, strippedName);
729 std::string ChecksumsSha512 = getDscHash(DoHashes, Hashes::SHA512SUM, Tags, "Checksums-Sha512", Db.HashesList.find("SHA512"), St.st_size, strippedName);
9a961efc 730
b2e465d6
AL
731 // Strip the DirStrip prefix from the FileName and add the PathPrefix
732 string NewFileName;
733 if (DirStrip.empty() == false &&
734 FileName.length() > DirStrip.length() &&
8c58f506 735 stringcmp(DirStrip,OriginalPath,OriginalPath + DirStrip.length()) == 0)
b2e465d6
AL
736 NewFileName = string(OriginalPath + DirStrip.length());
737 else
738 NewFileName = OriginalPath;
739 if (PathPrefix.empty() == false)
740 NewFileName = flCombine(PathPrefix,NewFileName);
171c45bc 741
b2e465d6
AL
742 string Directory = flNotFile(OriginalPath);
743 string Package = Tags.FindS("Source");
171c45bc 744
f6f06a8f 745 // Perform operation over all of the files
b2e465d6 746 string ParseJnk;
bf99a6d3 747 const char *C = Files.c_str();
ab3846c0 748 char *RealPath = NULL;
b2e465d6
AL
749 for (;isspace(*C); C++);
750 while (*C != 0)
88593886 751 {
b2e465d6
AL
752 // Parse each of the elements
753 if (ParseQuoteWord(C,ParseJnk) == false ||
754 ParseQuoteWord(C,ParseJnk) == false ||
755 ParseQuoteWord(C,ParseJnk) == false)
756 return _error->Error("Error parsing file record");
f6f06a8f 757
b2e465d6 758 string OriginalPath = Directory + ParseJnk;
f6f06a8f
MV
759
760 // Add missing hashes to source files
a311fb96
DK
761 if (((DoHashes & Hashes::SHA1SUM) == Hashes::SHA1SUM && !Tags.Exists("Checksums-Sha1")) ||
762 ((DoHashes & Hashes::SHA256SUM) == Hashes::SHA256SUM && !Tags.Exists("Checksums-Sha256")) ||
763 ((DoHashes & Hashes::SHA512SUM) == Hashes::SHA512SUM && !Tags.Exists("Checksums-Sha512")))
f6f06a8f 764 {
a311fb96 765 if (Db.GetFileInfo(OriginalPath,
ce928105
MV
766 false, /* DoControl */
767 false, /* DoContents */
768 false, /* GenContentsOnly */
769 false, /* DoSource */
a311fb96 770 DoHashes,
ce928105 771 DoAlwaysStat) == false)
f6f06a8f
MV
772 {
773 return _error->Error("Error getting file info");
774 }
775
a311fb96
DK
776 for (HashStringList::const_iterator hs = Db.HashesList.begin(); hs != Db.HashesList.end(); ++hs)
777 {
23397c9d 778 if (hs->HashType() == "MD5Sum" || hs->HashType() == "Checksum-FileSize")
a311fb96
DK
779 continue;
780 char const * fieldname;
88593886 781 std::string * out;
a311fb96
DK
782 if (hs->HashType() == "SHA1")
783 {
784 fieldname = "Checksums-Sha1";
88593886 785 out = &ChecksumsSha1;
a311fb96
DK
786 }
787 else if (hs->HashType() == "SHA256")
788 {
789 fieldname = "Checksums-Sha256";
88593886 790 out = &ChecksumsSha256;
a311fb96
DK
791 }
792 else if (hs->HashType() == "SHA512")
793 {
794 fieldname = "Checksums-Sha512";
88593886 795 out = &ChecksumsSha512;
a311fb96
DK
796 }
797 else
798 {
799 _error->Warning("Ignoring unknown Checksumtype %s in SourcesWriter::DoPackages", hs->HashType().c_str());
800 continue;
801 }
802 if (Tags.Exists(fieldname) == true)
803 continue;
88593886
DK
804 std::ostringstream streamout;
805 streamout << "\n " << hs->HashValue() << " " << Db.GetFileSize() << " " << ParseJnk;
806 out->append(streamout.str());
a311fb96 807 }
ce928105 808
88593886 809 // write back the GetFileInfo() stats data
a311fb96 810 Db.Finish();
f6f06a8f
MV
811 }
812
813 // Perform the delinking operation
814 char Jnk[2];
815
ab3846c0
MV
816 if (readlink(OriginalPath.c_str(),Jnk,sizeof(Jnk)) != -1 &&
817 (RealPath = realpath(OriginalPath.c_str(),NULL)) != 0)
b2e465d6
AL
818 {
819 string RP = RealPath;
ab3846c0 820 free(RealPath);
cde41ae8 821 if (Delink(RP,OriginalPath.c_str(),Stats.DeLinkBytes,St.st_size) == false)
b2e465d6
AL
822 return false;
823 }
824 }
825
826 Directory = flNotFile(NewFileName);
827 if (Directory.length() > 2)
828 Directory.erase(Directory.end()-1);
171c45bc 829
b2e465d6 830 // This lists all the changes to the fields we are going to make.
f99da908 831 // (5 hardcoded + checksums + maintainer + end marker)
88593886 832 std::vector<pkgTagSection::Tag> Changes;
64177f17 833
88593886
DK
834 Changes.push_back(pkgTagSection::Tag::Remove("Source"));
835 Changes.push_back(pkgTagSection::Tag::Rewrite("Package", Package));
3c54407f 836 if (Files.empty() == false)
88593886 837 Changes.push_back(pkgTagSection::Tag::Rewrite("Files", Files));
3c54407f 838 if (ChecksumsSha1.empty() == false)
88593886 839 Changes.push_back(pkgTagSection::Tag::Rewrite("Checksums-Sha1", ChecksumsSha1));
3c54407f 840 if (ChecksumsSha256.empty() == false)
88593886 841 Changes.push_back(pkgTagSection::Tag::Rewrite("Checksums-Sha256", ChecksumsSha256));
12cd178d 842 if (ChecksumsSha512.empty() == false)
88593886 843 Changes.push_back(pkgTagSection::Tag::Rewrite("Checksums-Sha512", ChecksumsSha512));
171c45bc 844 if (Directory != "./")
88593886
DK
845 Changes.push_back(pkgTagSection::Tag::Rewrite("Directory", Directory));
846 Changes.push_back(pkgTagSection::Tag::Rewrite("Priority", BestPrio));
847 Changes.push_back(pkgTagSection::Tag::Remove("Status"));
b2e465d6
AL
848
849 // Rewrite the maintainer field if necessary
850 bool MaintFailed;
88593886 851 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"), MaintFailed);
b2e465d6
AL
852 if (MaintFailed == true)
853 {
854 if (NoOverride == false)
855 {
88593886 856 NewLine(1);
dc738e7a
AL
857 ioprintf(c1out, _(" %s maintainer is %s not %s\n"), Package.c_str(),
858 Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
88593886 859 }
b2e465d6
AL
860 }
861 if (NewMaint.empty() == false)
88593886
DK
862 Changes.push_back(pkgTagSection::Tag::Rewrite("Maintainer", NewMaint.c_str()));
863
864 for (map<string,string>::const_iterator I = SOverItem->FieldOverride.begin();
f7f0d6c7 865 I != SOverItem->FieldOverride.end(); ++I)
88593886 866 Changes.push_back(pkgTagSection::Tag::Rewrite(I->first, I->second));
64177f17 867
b2e465d6 868 // Rewrite and store the fields.
88593886
DK
869 if (Tags.Write(*Output, TFRewriteSourceOrder, Changes) == false ||
870 Output->Write("\n", 1) == false)
b2e465d6 871 return false;
b2e465d6
AL
872
873 Stats.Packages++;
874
ce928105 875 return true;
b2e465d6
AL
876}
877 /*}}}*/
878
879// ContentsWriter::ContentsWriter - Constructor /*{{{*/
880// ---------------------------------------------------------------------
881/* */
1dd20368
DK
882ContentsWriter::ContentsWriter(FileFd * const GivenOutput, string const &DB,
883 string const &Arch, bool const IncludeArchAll) :
884 FTWScanner(GivenOutput, Arch, IncludeArchAll), Db(DB), Stats(Db.Stats)
b2e465d6
AL
885
886{
31981076 887 SetExts(".deb");
b2e465d6
AL
888}
889 /*}}}*/
890// ContentsWriter::DoPackage - Process a single package /*{{{*/
891// ---------------------------------------------------------------------
892/* If Package is the empty string the control record will be parsed to
893 determine what the package name is. */
9209ec47 894bool ContentsWriter::DoPackage(string FileName, string Package)
b2e465d6 895{
a311fb96
DK
896 if (!Db.GetFileInfo(FileName,
897 Package.empty(), /* DoControl */
898 true, /* DoContents */
899 false, /* GenContentsOnly */
900 false, /* DoSource */
901 0, /* DoHashes */
902 false /* checkMtime */))
cde41ae8 903 {
b2e465d6 904 return false;
cde41ae8 905 }
b2e465d6
AL
906
907 // Parse the package name
908 if (Package.empty() == true)
909 {
b2e465d6
AL
910 Package = Db.Control.Section.FindS("Package");
911 }
912
913 Db.Contents.Add(Gen,Package);
914
915 return Db.Finish();
916}
917 /*}}}*/
918// ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
919// ---------------------------------------------------------------------
920/* */
9209ec47 921bool ContentsWriter::ReadFromPkgs(string const &PkgFile,string const &PkgCompress)
b2e465d6
AL
922{
923 MultiCompress Pkgs(PkgFile,PkgCompress,0,false);
924 if (_error->PendingError() == true)
925 return false;
12d1f5b3 926
b2e465d6 927 // Open the package file
12d1f5b3
DK
928 FileFd Fd;
929 if (Pkgs.OpenOld(Fd) == false)
b2e465d6 930 return false;
12d1f5b3 931
b2e465d6
AL
932 pkgTagFile Tags(&Fd);
933 if (_error->PendingError() == true)
b2e465d6 934 return false;
12d1f5b3 935
b2e465d6
AL
936 // Parse.
937 pkgTagSection Section;
938 while (Tags.Step(Section) == true)
939 {
940 string File = flCombine(Prefix,Section.FindS("FileName"));
941 string Package = Section.FindS("Section");
942 if (Package.empty() == false && Package.end()[-1] != '/')
943 {
944 Package += '/';
945 Package += Section.FindS("Package");
946 }
947 else
948 Package += Section.FindS("Package");
949
950 DoPackage(File,Package);
951 if (_error->empty() == false)
952 {
953 _error->Error("Errors apply to file '%s'",File.c_str());
954 _error->DumpErrors();
955 }
956 }
12d1f5b3 957
b2e465d6 958 // Tidy the compressor
12d1f5b3
DK
959 Fd.Close();
960
b2e465d6
AL
961 return true;
962}
98953965 963
b2e465d6 964 /*}}}*/
98953965
AL
965
966// ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
967// ---------------------------------------------------------------------
968/* */
88593886 969ReleaseWriter::ReleaseWriter(FileFd * const GivenOutput, string const &/*DB*/) : FTWScanner(GivenOutput)
98953965 970{
3cb3fe76
DK
971 if (_config->FindB("APT::FTPArchive::Release::Default-Patterns", true) == true)
972 {
973 AddPattern("Packages");
83758aed 974 AddPattern("Packages.*");
8e3900d0 975 AddPattern("Translation-*");
3cb3fe76 976 AddPattern("Sources");
83758aed 977 AddPattern("Sources.*");
3cb3fe76 978 AddPattern("Release");
0baf849d 979 AddPattern("Contents-*");
8d16c617 980 AddPattern("Index");
6d9b79dd
DK
981 AddPattern("icons-*.tar");
982 AddPattern("icons-*.tar.*");
983 AddPattern("Components-*.yml");
984 AddPattern("Components-*.yml.*");
3cb3fe76
DK
985 AddPattern("md5sum.txt");
986 }
987 AddPatterns(_config->FindVector("APT::FTPArchive::Release::Patterns"));
187492a6 988
9209ec47 989 time_t const now = time(NULL);
cb12d0a6
DK
990
991 setlocale(LC_TIME, "C");
992
98953965
AL
993 char datestr[128];
994 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %H:%M:%S UTC",
995 gmtime(&now)) == 0)
996 {
997 datestr[0] = '\0';
998 }
999
c99e48ec 1000 time_t const validuntil = now + _config->FindI("APT::FTPArchive::Release::ValidTime", 0);
cdb623ed 1001 char validstr[128];
c99e48ec
DK
1002 if (now == validuntil ||
1003 strftime(validstr, sizeof(validstr), "%a, %d %b %Y %H:%M:%S UTC",
1004 gmtime(&validuntil)) == 0)
1005 {
cdb623ed 1006 validstr[0] = '\0';
c99e48ec
DK
1007 }
1008
cb12d0a6
DK
1009 setlocale(LC_TIME, "");
1010
98953965
AL
1011 map<string,string> Fields;
1012 Fields["Origin"] = "";
1013 Fields["Label"] = "";
1014 Fields["Suite"] = "";
1015 Fields["Version"] = "";
1016 Fields["Codename"] = "";
1017 Fields["Date"] = datestr;
c99e48ec 1018 Fields["Valid-Until"] = validstr;
98953965
AL
1019 Fields["Architectures"] = "";
1020 Fields["Components"] = "";
1021 Fields["Description"] = "";
e8e52cd0 1022 if (_config->FindB("APT::FTPArchive::DoByHash", false) == true)
7852873a
MV
1023 Fields["Acquire-By-Hash"] = "true";
1024
98953965
AL
1025 for(map<string,string>::const_iterator I = Fields.begin();
1026 I != Fields.end();
1027 ++I)
1028 {
1029 string Config = string("APT::FTPArchive::Release::") + (*I).first;
1030 string Value = _config->Find(Config, (*I).second.c_str());
1031 if (Value == "")
1032 continue;
1033
88593886
DK
1034 std::string const out = I->first + ": " + Value + "\n";
1035 Output->Write(out.c_str(), out.length());
98953965 1036 }
3c54407f 1037
a311fb96 1038 ConfigToDoHashes(DoHashes, "APT::FTPArchive::Release");
98953965
AL
1039}
1040 /*}}}*/
1041// ReleaseWriter::DoPackage - Process a single package /*{{{*/
1042// ---------------------------------------------------------------------
1043bool ReleaseWriter::DoPackage(string FileName)
1044{
1045 // Strip the DirStrip prefix from the FileName and add the PathPrefix
1046 string NewFileName;
1047 if (DirStrip.empty() == false &&
1048 FileName.length() > DirStrip.length() &&
1049 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
1050 DirStrip.begin(),DirStrip.end()) == 0)
c0eb6bc6 1051 {
98953965 1052 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
c0eb6bc6 1053 while (NewFileName[0] == '/')
9202409d 1054 NewFileName = string(NewFileName.begin() + 1,NewFileName.end());
c0eb6bc6 1055 }
98953965
AL
1056 else
1057 NewFileName = FileName;
c0eb6bc6 1058
98953965
AL
1059 if (PathPrefix.empty() == false)
1060 NewFileName = flCombine(PathPrefix,NewFileName);
1061
1062 FileFd fd(FileName, FileFd::ReadOnly);
1063
1064 if (!fd.IsOpen())
1065 {
1066 return false;
1067 }
1068
c0eb6bc6 1069 CheckSums[NewFileName].size = fd.Size();
f7291f62 1070
9224ce3d
DK
1071 Hashes hs(DoHashes);
1072 hs.AddFD(fd);
a311fb96 1073 CheckSums[NewFileName].Hashes = hs.GetHashStringList();
98953965 1074 fd.Close();
8c4e1f97 1075
7852873a
MV
1076 // FIXME: wrong layer in the code(?)
1077 // FIXME2: symlink instead of create a copy
e8e52cd0 1078 if (_config->FindB("APT::FTPArchive::DoByHash", false) == true)
7852873a
MV
1079 {
1080 std::string Input = FileName;
1081 HashStringList hsl = hs.GetHashStringList();
1082 for(HashStringList::const_iterator h = hsl.begin();
1083 h != hsl.end(); ++h)
1084 {
1085 if (!h->usable())
1086 continue;
2861bd9a
MV
1087 if (flNotDir(FileName) == "Release" || flNotDir(FileName) == "InRelease")
1088 continue;
7852873a 1089
2861bd9a 1090 std::string ByHashOutputFile = GenByHashFilename(Input, *h);
7852873a
MV
1091 std::string ByHashOutputDir = flNotFile(ByHashOutputFile);
1092 if(!CreateDirectory(flNotFile(Input), ByHashOutputDir))
1093 return _error->Warning("can not create dir %s", flNotFile(ByHashOutputFile).c_str());
1094
1095 // write new hashes
1096 FileFd In(Input, FileFd::ReadOnly);
1097 FileFd Out(ByHashOutputFile, FileFd::WriteEmpty);
1098 if(!CopyFile(In, Out))
1099 return _error->Warning("failed to copy %s %s", Input.c_str(), ByHashOutputFile.c_str());
1100 }
1101 }
1102
98953965
AL
1103 return true;
1104}
f7291f62
AL
1105
1106 /*}}}*/
1107// ReleaseWriter::Finish - Output the checksums /*{{{*/
1108// ---------------------------------------------------------------------
88593886 1109static void printChecksumTypeRecord(FileFd &Output, char const * const Type, map<string, ReleaseWriter::CheckSum> const &CheckSums)
f7291f62 1110{
88593886
DK
1111 {
1112 std::string out;
1113 strprintf(out, "%s:\n", Type);
1114 Output.Write(out.c_str(), out.length());
1115 }
1116 for(map<string,ReleaseWriter::CheckSum>::const_iterator I = CheckSums.begin();
1117 I != CheckSums.end(); ++I)
1118 {
1119 HashString const * const hs = I->second.Hashes.find(Type);
1120 if (hs == NULL)
1121 continue;
1122 std::string out;
1123 strprintf(out, " %s %16llu %s\n",
1124 hs->HashValue().c_str(),
1125 (*I).second.size,
1126 (*I).first.c_str());
1127 Output.Write(out.c_str(), out.length());
1128 }
a311fb96
DK
1129}
1130void ReleaseWriter::Finish()
1131{
1132 if ((DoHashes & Hashes::MD5SUM) == Hashes::MD5SUM)
88593886 1133 printChecksumTypeRecord(*Output, "MD5Sum", CheckSums);
a311fb96 1134 if ((DoHashes & Hashes::SHA1SUM) == Hashes::SHA1SUM)
88593886 1135 printChecksumTypeRecord(*Output, "SHA1", CheckSums);
a311fb96 1136 if ((DoHashes & Hashes::SHA256SUM) == Hashes::SHA256SUM)
88593886 1137 printChecksumTypeRecord(*Output, "SHA256", CheckSums);
a311fb96 1138 if ((DoHashes & Hashes::SHA512SUM) == Hashes::SHA512SUM)
88593886 1139 printChecksumTypeRecord(*Output, "SHA512", CheckSums);
7852873a
MV
1140
1141 // go by-hash cleanup
1142 map<string,ReleaseWriter::CheckSum>::const_iterator prev = CheckSums.begin();
e8e52cd0 1143 if (_config->FindB("APT::FTPArchive::DoByHash", false) == true)
7852873a
MV
1144 {
1145 for(map<string,ReleaseWriter::CheckSum>::const_iterator I = CheckSums.begin();
1146 I != CheckSums.end(); ++I)
1147 {
1148 if (I->first == "Release" || I->first == "InRelease")
1149 continue;
1150
1151 // keep iterating until we find a new subdir
1152 if(flNotFile(I->first) == flNotFile(prev->first))
1153 continue;
1154
1155 // clean that subdir up
1156 int keepFiles = _config->FindI("APT::FTPArchive::By-Hash-Keep", 3);
1157 // calculate how many compressors are used (the amount of files
1158 // in that subdir generated for this run)
1159 keepFiles *= std::distance(prev, I);
1160 prev = I;
1161
1162 HashStringList hsl = prev->second.Hashes;
1163 for(HashStringList::const_iterator h = hsl.begin();
1164 h != hsl.end(); ++h)
1165 {
1166
1167 if (!h->usable())
1168 continue;
1169
1170 std::string RealFilename = DirStrip+"/"+prev->first;
1171 std::string ByHashOutputFile = GenByHashFilename(RealFilename, *h);
1172 DeleteAllButMostRecent(flNotFile(ByHashOutputFile), keepFiles);
1173 }
1174 }
1175 }
f7291f62 1176}