]> git.saurik.com Git - apt.git/blame - ftparchive/writer.cc
implement CopyFile without using FileFd::Size()
[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{
59 if (_config->FindB(Conf, true) == true)
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 /*{{{*/
88593886 74FTWScanner::FTWScanner(FileFd * const GivenOutput, string const &Arch): Arch(Arch), DoHashes(~0)
b2e465d6 75{
88593886
DK
76 if (GivenOutput == NULL)
77 {
78 Output = new FileFd;
3d8232bf 79 OwnsOutput = true;
88593886
DK
80 Output->OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly, false);
81 }
82 else
3d8232bf 83 {
88593886 84 Output = GivenOutput;
3d8232bf
DK
85 OwnsOutput = false;
86 }
b2e465d6
AL
87 ErrorPrinted = false;
88 NoLinkAct = !_config->FindB("APT::FTPArchive::DeLinkAct",true);
a311fb96 89 ConfigToDoHashes(DoHashes, "APT::FTPArchive");
b2e465d6
AL
90}
91 /*}}}*/
3d8232bf
DK
92FTWScanner::~FTWScanner()
93{
94 if (Output != NULL && OwnsOutput)
95 delete Output;
96}
b2e465d6
AL
97// FTWScanner::Scanner - FTW Scanner /*{{{*/
98// ---------------------------------------------------------------------
65512241 99/* This is the FTW scanner, it processes each directory element in the
b2e465d6 100 directory tree. */
65512241 101int FTWScanner::ScannerFTW(const char *File,const struct stat * /*sb*/,int Flag)
b2e465d6
AL
102{
103 if (Flag == FTW_DNR)
104 {
105 Owner->NewLine(1);
dc738e7a 106 ioprintf(c1out, _("W: Unable to read directory %s\n"), File);
b2e465d6
AL
107 }
108 if (Flag == FTW_NS)
109 {
110 Owner->NewLine(1);
dc738e7a 111 ioprintf(c1out, _("W: Unable to stat %s\n"), File);
b2e465d6
AL
112 }
113 if (Flag != FTW_F)
114 return 0;
115
cde41ae8
MV
116 return ScannerFile(File, true);
117}
118 /*}}}*/
119// FTWScanner::ScannerFile - File Scanner /*{{{*/
120// ---------------------------------------------------------------------
121/* */
9209ec47 122int FTWScanner::ScannerFile(const char *File, bool const &ReadLink)
cde41ae8 123{
98953965 124 const char *LastComponent = strrchr(File, '/');
ab3846c0
MV
125 char *RealPath = NULL;
126
98953965
AL
127 if (LastComponent == NULL)
128 LastComponent = File;
129 else
130 LastComponent++;
131
9209ec47 132 vector<string>::const_iterator I;
98953965
AL
133 for(I = Owner->Patterns.begin(); I != Owner->Patterns.end(); ++I)
134 {
135 if (fnmatch((*I).c_str(), LastComponent, 0) == 0)
136 break;
137 }
138 if (I == Owner->Patterns.end())
b2e465d6
AL
139 return 0;
140
141 /* Process it. If the file is a link then resolve it into an absolute
142 name.. This works best if the directory components the scanner are
143 given are not links themselves. */
144 char Jnk[2];
145 Owner->OriginalPath = File;
ab3846c0 146 if (ReadLink &&
cde41ae8 147 readlink(File,Jnk,sizeof(Jnk)) != -1 &&
ab3846c0
MV
148 (RealPath = realpath(File,NULL)) != 0)
149 {
150 Owner->DoPackage(RealPath);
151 free(RealPath);
152 }
b2e465d6
AL
153 else
154 Owner->DoPackage(File);
155
156 if (_error->empty() == false)
157 {
158 // Print any errors or warnings found
159 string Err;
160 bool SeenPath = false;
161 while (_error->empty() == false)
162 {
163 Owner->NewLine(1);
164
9209ec47 165 bool const Type = _error->PopMessage(Err);
b2e465d6 166 if (Type == true)
dc738e7a 167 cerr << _("E: ") << Err << endl;
b2e465d6 168 else
dc738e7a 169 cerr << _("W: ") << Err << endl;
b2e465d6
AL
170
171 if (Err.find(File) != string::npos)
172 SeenPath = true;
173 }
174
175 if (SeenPath == false)
dc738e7a 176 cerr << _("E: Errors apply to file ") << "'" << File << "'" << endl;
b2e465d6
AL
177 return 0;
178 }
179
180 return 0;
181}
182 /*}}}*/
183// FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
184// ---------------------------------------------------------------------
185/* */
9209ec47 186bool FTWScanner::RecursiveScan(string const &Dir)
b2e465d6 187{
ab3846c0 188 char *RealPath = NULL;
b2e465d6
AL
189 /* If noprefix is set then jam the scan root in, so we don't generate
190 link followed paths out of control */
191 if (InternalPrefix.empty() == true)
192 {
ab3846c0 193 if ((RealPath = realpath(Dir.c_str(),NULL)) == 0)
dc738e7a 194 return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
ab3846c0
MV
195 InternalPrefix = RealPath;
196 free(RealPath);
b2e465d6
AL
197 }
198
199 // Do recursive directory searching
200 Owner = this;
9209ec47 201 int const Res = ftw(Dir.c_str(),ScannerFTW,30);
b2e465d6
AL
202
203 // Error treewalking?
204 if (Res != 0)
205 {
206 if (_error->PendingError() == false)
dc738e7a 207 _error->Errno("ftw",_("Tree walking failed"));
b2e465d6
AL
208 return false;
209 }
210
211 return true;
212}
213 /*}}}*/
214// FTWScanner::LoadFileList - Load the file list from a file /*{{{*/
215// ---------------------------------------------------------------------
216/* This is an alternative to using FTW to locate files, it reads the list
217 of files from another file. */
9209ec47 218bool FTWScanner::LoadFileList(string const &Dir, string const &File)
b2e465d6 219{
ab3846c0 220 char *RealPath = NULL;
b2e465d6
AL
221 /* If noprefix is set then jam the scan root in, so we don't generate
222 link followed paths out of control */
223 if (InternalPrefix.empty() == true)
224 {
ab3846c0 225 if ((RealPath = realpath(Dir.c_str(),NULL)) == 0)
dc738e7a 226 return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
b2e465d6 227 InternalPrefix = RealPath;
ab3846c0 228 free(RealPath);
b2e465d6
AL
229 }
230
231 Owner = this;
232 FILE *List = fopen(File.c_str(),"r");
233 if (List == 0)
dc738e7a 234 return _error->Errno("fopen",_("Failed to open %s"),File.c_str());
b2e465d6
AL
235
236 /* We are a tad tricky here.. We prefix the buffer with the directory
237 name, that way if we need a full path with just use line.. Sneaky and
238 fully evil. */
239 char Line[1000];
240 char *FileStart;
241 if (Dir.empty() == true || Dir.end()[-1] != '/')
242 FileStart = Line + snprintf(Line,sizeof(Line),"%s/",Dir.c_str());
243 else
244 FileStart = Line + snprintf(Line,sizeof(Line),"%s",Dir.c_str());
245 while (fgets(FileStart,sizeof(Line) - (FileStart - Line),List) != 0)
246 {
247 char *FileName = _strstrip(FileStart);
248 if (FileName[0] == 0)
249 continue;
250
251 if (FileName[0] != '/')
252 {
253 if (FileName != FileStart)
254 memmove(FileStart,FileName,strlen(FileStart));
255 FileName = Line;
256 }
257
cde41ae8 258#if 0
b2e465d6
AL
259 struct stat St;
260 int Flag = FTW_F;
261 if (stat(FileName,&St) != 0)
262 Flag = FTW_NS;
cde41ae8 263#endif
b2e465d6 264
cde41ae8 265 if (ScannerFile(FileName, false) != 0)
b2e465d6
AL
266 break;
267 }
268
269 fclose(List);
270 return true;
271}
272 /*}}}*/
273// FTWScanner::Delink - Delink symlinks /*{{{*/
274// ---------------------------------------------------------------------
275/* */
276bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
650faab0
DK
277 unsigned long long &DeLinkBytes,
278 unsigned long long const &FileSize)
b2e465d6
AL
279{
280 // See if this isn't an internaly prefix'd file name.
281 if (InternalPrefix.empty() == false &&
282 InternalPrefix.length() < FileName.length() &&
283 stringcmp(FileName.begin(),FileName.begin() + InternalPrefix.length(),
284 InternalPrefix.begin(),InternalPrefix.end()) != 0)
285 {
286 if (DeLinkLimit != 0 && DeLinkBytes/1024 < DeLinkLimit)
287 {
288 // Tidy up the display
289 if (DeLinkBytes == 0)
290 cout << endl;
291
292 NewLine(1);
dc738e7a 293 ioprintf(c1out, _(" DeLink %s [%s]\n"), (OriginalPath + InternalPrefix.length()),
cde41ae8 294 SizeToStr(FileSize).c_str());
dc738e7a 295 c1out << flush;
b2e465d6
AL
296
297 if (NoLinkAct == false)
298 {
299 char OldLink[400];
300 if (readlink(OriginalPath,OldLink,sizeof(OldLink)) == -1)
dc738e7a 301 _error->Errno("readlink",_("Failed to readlink %s"),OriginalPath);
b2e465d6
AL
302 else
303 {
304 if (unlink(OriginalPath) != 0)
dc738e7a 305 _error->Errno("unlink",_("Failed to unlink %s"),OriginalPath);
b2e465d6
AL
306 else
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
329 return true;
330}
331 /*}}}*/
b2e465d6
AL
332
333// PackagesWriter::PackagesWriter - Constructor /*{{{*/
334// ---------------------------------------------------------------------
335/* */
3d8232bf
DK
336PackagesWriter::PackagesWriter(FileFd * const GivenOutput, TranslationWriter * const transWriter,
337 string const &DB,string const &Overrides,string const &ExtOverrides,
338 string const &Arch) :
339 FTWScanner(GivenOutput, Arch), Db(DB), Stats(Db.Stats), TransWriter(transWriter)
b2e465d6 340{
31981076 341 SetExts(".deb .udeb");
b2e465d6 342 DeLinkLimit = 0;
3cb3fe76 343
b2e465d6 344 // Process the command line options
a311fb96 345 ConfigToDoHashes(DoHashes, "APT::FTPArchive::Packages");
ff574e76 346 DoAlwaysStat = _config->FindB("APT::FTPArchive::AlwaysStat", false);
b2e465d6
AL
347 DoContents = _config->FindB("APT::FTPArchive::Contents",true);
348 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
9c24493f 349 LongDescription = _config->FindB("APT::FTPArchive::LongDescription",true);
b2e465d6
AL
350
351 if (Db.Loaded() == false)
352 DoContents = false;
66905344 353
b2e465d6
AL
354 // Read the override file
355 if (Overrides.empty() == false && Over.ReadOverride(Overrides) == false)
356 return;
357 else
358 NoOverride = true;
64177f17
AL
359
360 if (ExtOverrides.empty() == false)
361 Over.ReadExtraOverride(ExtOverrides);
362
b2e465d6
AL
363 _error->DumpErrors();
364}
98953965
AL
365 /*}}}*/
366// FTWScanner::SetExts - Set extensions to support /*{{{*/
367// ---------------------------------------------------------------------
368/* */
9209ec47 369bool FTWScanner::SetExts(string const &Vals)
98953965
AL
370{
371 ClearPatterns();
372 string::size_type Start = 0;
d6689735 373 while (Start <= Vals.length()-1)
98953965 374 {
31981076
DK
375 string::size_type const Space = Vals.find(' ',Start);
376 string::size_type const Length = ((Space == string::npos) ? Vals.length() : Space) - Start;
377 if ( Arch.empty() == false )
98953965 378 {
31981076
DK
379 AddPattern(string("*_") + Arch + Vals.substr(Start, Length));
380 AddPattern(string("*_all") + Vals.substr(Start, Length));
98953965 381 }
d6689735 382 else
31981076
DK
383 AddPattern(string("*") + Vals.substr(Start, Length));
384
d6689735 385 Start += Length + 1;
98953965
AL
386 }
387
388 return true;
389}
b2e465d6
AL
390 /*}}}*/
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/* */
88593886
DK
882ContentsWriter::ContentsWriter(FileFd * const GivenOutput, string const &DB, string const &Arch) :
883 FTWScanner(GivenOutput, Arch), Db(DB), Stats(Db.Stats)
b2e465d6
AL
884
885{
31981076 886 SetExts(".deb");
b2e465d6
AL
887}
888 /*}}}*/
889// ContentsWriter::DoPackage - Process a single package /*{{{*/
890// ---------------------------------------------------------------------
891/* If Package is the empty string the control record will be parsed to
892 determine what the package name is. */
9209ec47 893bool ContentsWriter::DoPackage(string FileName, string Package)
b2e465d6 894{
a311fb96
DK
895 if (!Db.GetFileInfo(FileName,
896 Package.empty(), /* DoControl */
897 true, /* DoContents */
898 false, /* GenContentsOnly */
899 false, /* DoSource */
900 0, /* DoHashes */
901 false /* checkMtime */))
cde41ae8 902 {
b2e465d6 903 return false;
cde41ae8 904 }
b2e465d6
AL
905
906 // Parse the package name
907 if (Package.empty() == true)
908 {
b2e465d6
AL
909 Package = Db.Control.Section.FindS("Package");
910 }
911
912 Db.Contents.Add(Gen,Package);
913
914 return Db.Finish();
915}
916 /*}}}*/
917// ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
918// ---------------------------------------------------------------------
919/* */
9209ec47 920bool ContentsWriter::ReadFromPkgs(string const &PkgFile,string const &PkgCompress)
b2e465d6
AL
921{
922 MultiCompress Pkgs(PkgFile,PkgCompress,0,false);
923 if (_error->PendingError() == true)
924 return false;
12d1f5b3 925
b2e465d6 926 // Open the package file
12d1f5b3
DK
927 FileFd Fd;
928 if (Pkgs.OpenOld(Fd) == false)
b2e465d6 929 return false;
12d1f5b3 930
b2e465d6
AL
931 pkgTagFile Tags(&Fd);
932 if (_error->PendingError() == true)
b2e465d6 933 return false;
12d1f5b3 934
b2e465d6
AL
935 // Parse.
936 pkgTagSection Section;
937 while (Tags.Step(Section) == true)
938 {
939 string File = flCombine(Prefix,Section.FindS("FileName"));
940 string Package = Section.FindS("Section");
941 if (Package.empty() == false && Package.end()[-1] != '/')
942 {
943 Package += '/';
944 Package += Section.FindS("Package");
945 }
946 else
947 Package += Section.FindS("Package");
948
949 DoPackage(File,Package);
950 if (_error->empty() == false)
951 {
952 _error->Error("Errors apply to file '%s'",File.c_str());
953 _error->DumpErrors();
954 }
955 }
12d1f5b3 956
b2e465d6 957 // Tidy the compressor
12d1f5b3
DK
958 Fd.Close();
959
b2e465d6
AL
960 return true;
961}
98953965 962
b2e465d6 963 /*}}}*/
98953965
AL
964
965// ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
966// ---------------------------------------------------------------------
967/* */
88593886 968ReleaseWriter::ReleaseWriter(FileFd * const GivenOutput, string const &/*DB*/) : FTWScanner(GivenOutput)
98953965 969{
3cb3fe76
DK
970 if (_config->FindB("APT::FTPArchive::Release::Default-Patterns", true) == true)
971 {
972 AddPattern("Packages");
973 AddPattern("Packages.gz");
974 AddPattern("Packages.bz2");
975 AddPattern("Packages.lzma");
b7080ced 976 AddPattern("Packages.xz");
8e3900d0 977 AddPattern("Translation-*");
3cb3fe76
DK
978 AddPattern("Sources");
979 AddPattern("Sources.gz");
980 AddPattern("Sources.bz2");
981 AddPattern("Sources.lzma");
b7080ced 982 AddPattern("Sources.xz");
3cb3fe76 983 AddPattern("Release");
0baf849d 984 AddPattern("Contents-*");
8d16c617 985 AddPattern("Index");
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);
cb12d0a6
DK
991
992 setlocale(LC_TIME, "C");
993
98953965
AL
994 char datestr[128];
995 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %H:%M:%S UTC",
996 gmtime(&now)) == 0)
997 {
998 datestr[0] = '\0';
999 }
1000
c99e48ec 1001 time_t const validuntil = now + _config->FindI("APT::FTPArchive::Release::ValidTime", 0);
cdb623ed 1002 char validstr[128];
c99e48ec
DK
1003 if (now == validuntil ||
1004 strftime(validstr, sizeof(validstr), "%a, %d %b %Y %H:%M:%S UTC",
1005 gmtime(&validuntil)) == 0)
1006 {
cdb623ed 1007 validstr[0] = '\0';
c99e48ec
DK
1008 }
1009
cb12d0a6
DK
1010 setlocale(LC_TIME, "");
1011
98953965
AL
1012 map<string,string> Fields;
1013 Fields["Origin"] = "";
1014 Fields["Label"] = "";
1015 Fields["Suite"] = "";
1016 Fields["Version"] = "";
1017 Fields["Codename"] = "";
1018 Fields["Date"] = datestr;
c99e48ec 1019 Fields["Valid-Until"] = validstr;
98953965
AL
1020 Fields["Architectures"] = "";
1021 Fields["Components"] = "";
1022 Fields["Description"] = "";
e8e52cd0 1023 if (_config->FindB("APT::FTPArchive::DoByHash", false) == true)
7852873a
MV
1024 Fields["Acquire-By-Hash"] = "true";
1025
98953965
AL
1026 for(map<string,string>::const_iterator I = Fields.begin();
1027 I != Fields.end();
1028 ++I)
1029 {
1030 string Config = string("APT::FTPArchive::Release::") + (*I).first;
1031 string Value = _config->Find(Config, (*I).second.c_str());
1032 if (Value == "")
1033 continue;
1034
88593886
DK
1035 std::string const out = I->first + ": " + Value + "\n";
1036 Output->Write(out.c_str(), out.length());
98953965 1037 }
3c54407f 1038
a311fb96 1039 ConfigToDoHashes(DoHashes, "APT::FTPArchive::Release");
98953965
AL
1040}
1041 /*}}}*/
1042// ReleaseWriter::DoPackage - Process a single package /*{{{*/
1043// ---------------------------------------------------------------------
1044bool ReleaseWriter::DoPackage(string FileName)
1045{
1046 // Strip the DirStrip prefix from the FileName and add the PathPrefix
1047 string NewFileName;
1048 if (DirStrip.empty() == false &&
1049 FileName.length() > DirStrip.length() &&
1050 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
1051 DirStrip.begin(),DirStrip.end()) == 0)
c0eb6bc6 1052 {
98953965 1053 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
c0eb6bc6 1054 while (NewFileName[0] == '/')
9202409d 1055 NewFileName = string(NewFileName.begin() + 1,NewFileName.end());
c0eb6bc6 1056 }
98953965
AL
1057 else
1058 NewFileName = FileName;
c0eb6bc6 1059
98953965
AL
1060 if (PathPrefix.empty() == false)
1061 NewFileName = flCombine(PathPrefix,NewFileName);
1062
1063 FileFd fd(FileName, FileFd::ReadOnly);
1064
1065 if (!fd.IsOpen())
1066 {
1067 return false;
1068 }
1069
c0eb6bc6 1070 CheckSums[NewFileName].size = fd.Size();
f7291f62 1071
9224ce3d
DK
1072 Hashes hs(DoHashes);
1073 hs.AddFD(fd);
a311fb96 1074 CheckSums[NewFileName].Hashes = hs.GetHashStringList();
98953965 1075 fd.Close();
8c4e1f97 1076
7852873a
MV
1077 // FIXME: wrong layer in the code(?)
1078 // FIXME2: symlink instead of create a copy
e8e52cd0 1079 if (_config->FindB("APT::FTPArchive::DoByHash", false) == true)
7852873a
MV
1080 {
1081 std::string Input = FileName;
1082 HashStringList hsl = hs.GetHashStringList();
1083 for(HashStringList::const_iterator h = hsl.begin();
1084 h != hsl.end(); ++h)
1085 {
1086 if (!h->usable())
1087 continue;
2861bd9a
MV
1088 if (flNotDir(FileName) == "Release" || flNotDir(FileName) == "InRelease")
1089 continue;
7852873a 1090
2861bd9a 1091 std::string ByHashOutputFile = GenByHashFilename(Input, *h);
7852873a
MV
1092 std::string ByHashOutputDir = flNotFile(ByHashOutputFile);
1093 if(!CreateDirectory(flNotFile(Input), ByHashOutputDir))
1094 return _error->Warning("can not create dir %s", flNotFile(ByHashOutputFile).c_str());
1095
1096 // write new hashes
1097 FileFd In(Input, FileFd::ReadOnly);
1098 FileFd Out(ByHashOutputFile, FileFd::WriteEmpty);
1099 if(!CopyFile(In, Out))
1100 return _error->Warning("failed to copy %s %s", Input.c_str(), ByHashOutputFile.c_str());
1101 }
1102 }
1103
98953965
AL
1104 return true;
1105}
f7291f62
AL
1106
1107 /*}}}*/
1108// ReleaseWriter::Finish - Output the checksums /*{{{*/
1109// ---------------------------------------------------------------------
88593886 1110static void printChecksumTypeRecord(FileFd &Output, char const * const Type, map<string, ReleaseWriter::CheckSum> const &CheckSums)
f7291f62 1111{
88593886
DK
1112 {
1113 std::string out;
1114 strprintf(out, "%s:\n", Type);
1115 Output.Write(out.c_str(), out.length());
1116 }
1117 for(map<string,ReleaseWriter::CheckSum>::const_iterator I = CheckSums.begin();
1118 I != CheckSums.end(); ++I)
1119 {
1120 HashString const * const hs = I->second.Hashes.find(Type);
1121 if (hs == NULL)
1122 continue;
1123 std::string out;
1124 strprintf(out, " %s %16llu %s\n",
1125 hs->HashValue().c_str(),
1126 (*I).second.size,
1127 (*I).first.c_str());
1128 Output.Write(out.c_str(), out.length());
1129 }
a311fb96
DK
1130}
1131void ReleaseWriter::Finish()
1132{
1133 if ((DoHashes & Hashes::MD5SUM) == Hashes::MD5SUM)
88593886 1134 printChecksumTypeRecord(*Output, "MD5Sum", CheckSums);
a311fb96 1135 if ((DoHashes & Hashes::SHA1SUM) == Hashes::SHA1SUM)
88593886 1136 printChecksumTypeRecord(*Output, "SHA1", CheckSums);
a311fb96 1137 if ((DoHashes & Hashes::SHA256SUM) == Hashes::SHA256SUM)
88593886 1138 printChecksumTypeRecord(*Output, "SHA256", CheckSums);
a311fb96 1139 if ((DoHashes & Hashes::SHA512SUM) == Hashes::SHA512SUM)
88593886 1140 printChecksumTypeRecord(*Output, "SHA512", CheckSums);
7852873a
MV
1141
1142 // go by-hash cleanup
1143 map<string,ReleaseWriter::CheckSum>::const_iterator prev = CheckSums.begin();
e8e52cd0 1144 if (_config->FindB("APT::FTPArchive::DoByHash", false) == true)
7852873a
MV
1145 {
1146 for(map<string,ReleaseWriter::CheckSum>::const_iterator I = CheckSums.begin();
1147 I != CheckSums.end(); ++I)
1148 {
1149 if (I->first == "Release" || I->first == "InRelease")
1150 continue;
1151
1152 // keep iterating until we find a new subdir
1153 if(flNotFile(I->first) == flNotFile(prev->first))
1154 continue;
1155
1156 // clean that subdir up
1157 int keepFiles = _config->FindI("APT::FTPArchive::By-Hash-Keep", 3);
1158 // calculate how many compressors are used (the amount of files
1159 // in that subdir generated for this run)
1160 keepFiles *= std::distance(prev, I);
1161 prev = I;
1162
1163 HashStringList hsl = prev->second.Hashes;
1164 for(HashStringList::const_iterator h = hsl.begin();
1165 h != hsl.end(); ++h)
1166 {
1167
1168 if (!h->usable())
1169 continue;
1170
1171 std::string RealFilename = DirStrip+"/"+prev->first;
1172 std::string ByHashOutputFile = GenByHashFilename(RealFilename, *h);
1173 DeleteAllButMostRecent(flNotFile(ByHashOutputFile), keepFiles);
1174 }
1175 }
1176 }
f7291f62 1177}