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