]>
git.saurik.com Git - apt.git/blob - ftparchive/writer.cc
0bd2be566fa8a59916a202d6967b5f6bbbc20f6a
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: writer.cc,v 1.14 2004/03/24 01:40:43 mdz Exp $
4 /* ######################################################################
8 The file writer classes. These write various types of output, sources,
11 ##################################################################### */
13 // Include Files /*{{{*/
16 #include <apt-pkg/configuration.h>
17 #include <apt-pkg/deblistparser.h>
18 #include <apt-pkg/error.h>
19 #include <apt-pkg/fileutl.h>
20 #include <apt-pkg/gpgv.h>
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>
36 #include <sys/types.h>
45 #include "apt-ftparchive.h"
48 #include "multicompress.h"
54 FTWScanner
*FTWScanner::Owner
;
56 // ConfigToDoHashes - which hashes to generate /*{{{*/
57 static void SingleConfigToDoHashes(unsigned int &DoHashes
, std::string
const &Conf
, unsigned int const Flag
)
59 if (_config
->FindB(Conf
, (DoHashes
& Flag
) == Flag
) == true)
64 static void ConfigToDoHashes(unsigned int &DoHashes
, std::string
const &Conf
)
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
);
73 // FTWScanner::FTWScanner - Constructor /*{{{*/
74 FTWScanner::FTWScanner(FileFd
* const GivenOutput
, string
const &Arch
, bool const IncludeArchAll
)
75 : Arch(Arch
), IncludeArchAll(IncludeArchAll
), DoHashes(~0)
77 if (GivenOutput
== NULL
)
81 Output
->OpenDescriptor(STDOUT_FILENO
, FileFd::WriteOnly
, false);
89 NoLinkAct
= !_config
->FindB("APT::FTPArchive::DeLinkAct",true);
90 ConfigToDoHashes(DoHashes
, "APT::FTPArchive");
93 FTWScanner::~FTWScanner()
95 if (Output
!= NULL
&& OwnsOutput
)
98 // FTWScanner::Scanner - FTW Scanner /*{{{*/
99 // ---------------------------------------------------------------------
100 /* This is the FTW scanner, it processes each directory element in the
102 int FTWScanner::ScannerFTW(const char *File
,const struct stat
* /*sb*/,int Flag
)
107 ioprintf(c1out
, _("W: Unable to read directory %s\n"), File
);
112 ioprintf(c1out
, _("W: Unable to stat %s\n"), File
);
117 return ScannerFile(File
, true);
120 // FTWScanner::ScannerFile - File Scanner /*{{{*/
121 // ---------------------------------------------------------------------
123 int FTWScanner::ScannerFile(const char *File
, bool const &ReadLink
)
125 const char *LastComponent
= strrchr(File
, '/');
126 char *RealPath
= NULL
;
128 if (LastComponent
== NULL
)
129 LastComponent
= File
;
133 vector
<string
>::const_iterator I
;
134 for(I
= Owner
->Patterns
.begin(); I
!= Owner
->Patterns
.end(); ++I
)
136 if (fnmatch((*I
).c_str(), LastComponent
, 0) == 0)
139 if (I
== Owner
->Patterns
.end())
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. */
146 Owner
->OriginalPath
= File
;
148 readlink(File
,Jnk
,sizeof(Jnk
)) != -1 &&
149 (RealPath
= realpath(File
,NULL
)) != 0)
151 Owner
->DoPackage(RealPath
);
155 Owner
->DoPackage(File
);
157 if (_error
->empty() == false)
159 // Print any errors or warnings found
161 bool SeenPath
= false;
162 while (_error
->empty() == false)
166 bool const Type
= _error
->PopMessage(Err
);
168 cerr
<< _("E: ") << Err
<< endl
;
170 cerr
<< _("W: ") << Err
<< endl
;
172 if (Err
.find(File
) != string::npos
)
176 if (SeenPath
== false)
177 cerr
<< _("E: Errors apply to file ") << "'" << File
<< "'" << endl
;
184 // FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
185 // ---------------------------------------------------------------------
187 bool FTWScanner::RecursiveScan(string
const &Dir
)
189 char *RealPath
= NULL
;
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)
194 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
195 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
196 InternalPrefix
= RealPath
;
200 // Do recursive directory searching
202 int const Res
= ftw(Dir
.c_str(),ScannerFTW
,30);
204 // Error treewalking?
207 if (_error
->PendingError() == false)
208 _error
->Errno("ftw",_("Tree walking failed"));
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. */
219 bool FTWScanner::LoadFileList(string
const &Dir
, string
const &File
)
221 char *RealPath
= NULL
;
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)
226 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
227 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
228 InternalPrefix
= RealPath
;
233 FILE *List
= fopen(File
.c_str(),"r");
235 return _error
->Errno("fopen",_("Failed to open %s"),File
.c_str());
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
242 if (Dir
.empty() == true || Dir
.end()[-1] != '/')
243 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s/",Dir
.c_str());
245 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s",Dir
.c_str());
246 while (fgets(FileStart
,sizeof(Line
) - (FileStart
- Line
),List
) != 0)
248 char *FileName
= _strstrip(FileStart
);
249 if (FileName
[0] == 0)
252 if (FileName
[0] != '/')
254 if (FileName
!= FileStart
)
255 memmove(FileStart
,FileName
,strlen(FileStart
));
262 if (stat(FileName
,&St
) != 0)
266 if (ScannerFile(FileName
, false) != 0)
274 // FTWScanner::Delink - Delink symlinks /*{{{*/
275 // ---------------------------------------------------------------------
277 bool FTWScanner::Delink(string
&FileName
,const char *OriginalPath
,
278 unsigned long long &DeLinkBytes
,
279 unsigned long long const &FileSize
)
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)
287 if (DeLinkLimit
!= 0 && DeLinkBytes
/1024 < DeLinkLimit
)
289 // Tidy up the display
290 if (DeLinkBytes
== 0)
294 ioprintf(c1out
, _(" DeLink %s [%s]\n"), (OriginalPath
+ InternalPrefix
.length()),
295 SizeToStr(FileSize
).c_str());
298 if (NoLinkAct
== false)
301 if (readlink(OriginalPath
,OldLink
,sizeof(OldLink
)) == -1)
302 _error
->Errno("readlink",_("Failed to readlink %s"),OriginalPath
);
305 if (unlink(OriginalPath
) != 0)
306 _error
->Errno("unlink",_("Failed to unlink %s"),OriginalPath
);
309 if (link(FileName
.c_str(),OriginalPath
) != 0)
311 // Panic! Restore the symlink
312 if (symlink(OldLink
,OriginalPath
) != 0)
313 _error
->Errno("symlink", "failed to restore symlink");
314 return _error
->Errno("link",_("*** Failed to link %s to %s"),
322 DeLinkBytes
+= FileSize
;
323 if (DeLinkBytes
/1024 >= DeLinkLimit
)
324 ioprintf(c1out
, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes
).c_str());
327 FileName
= OriginalPath
;
333 // FTWScanner::SetExts - Set extensions to support /*{{{*/
334 // ---------------------------------------------------------------------
336 bool FTWScanner::SetExts(string
const &Vals
)
339 string::size_type Start
= 0;
340 while (Start
<= Vals
.length()-1)
342 string::size_type
const Space
= Vals
.find(' ',Start
);
343 string::size_type
const Length
= ((Space
== string::npos
) ? Vals
.length() : Space
) - Start
;
344 if ( Arch
.empty() == false )
346 AddPattern(string("*_") + Arch
+ Vals
.substr(Start
, Length
));
347 if (IncludeArchAll
== true && Arch
!= "all")
348 AddPattern(string("*_all") + Vals
.substr(Start
, Length
));
351 AddPattern(string("*") + Vals
.substr(Start
, Length
));
360 // PackagesWriter::PackagesWriter - Constructor /*{{{*/
361 // ---------------------------------------------------------------------
363 PackagesWriter::PackagesWriter(FileFd
* const GivenOutput
, TranslationWriter
* const transWriter
,
364 string
const &DB
,string
const &Overrides
,string
const &ExtOverrides
,
365 string
const &Arch
, bool const IncludeArchAll
) :
366 FTWScanner(GivenOutput
, Arch
, IncludeArchAll
), Db(DB
), Stats(Db
.Stats
), TransWriter(transWriter
)
368 SetExts(".deb .udeb");
371 // Process the command line options
372 ConfigToDoHashes(DoHashes
, "APT::FTPArchive::Packages");
373 DoAlwaysStat
= _config
->FindB("APT::FTPArchive::AlwaysStat", false);
374 DoContents
= _config
->FindB("APT::FTPArchive::Contents",true);
375 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
376 LongDescription
= _config
->FindB("APT::FTPArchive::LongDescription",true);
378 if (Db
.Loaded() == false)
381 // Read the override file
382 if (Overrides
.empty() == false && Over
.ReadOverride(Overrides
) == false)
387 if (ExtOverrides
.empty() == false)
388 Over
.ReadExtraOverride(ExtOverrides
);
390 _error
->DumpErrors();
393 // PackagesWriter::DoPackage - Process a single package /*{{{*/
394 // ---------------------------------------------------------------------
395 /* This method takes a package and gets its control information and
396 MD5, SHA1 and SHA256 then writes out a control record with the proper fields
397 rewritten and the path/size/hash appended. */
398 bool PackagesWriter::DoPackage(string FileName
)
400 // Pull all the data we need form the DB
401 if (Db
.GetFileInfo(FileName
,
402 true, /* DoControl */
404 true, /* GenContentsOnly */
405 false, /* DoSource */
406 DoHashes
, DoAlwaysStat
) == false)
411 unsigned long long FileSize
= Db
.GetFileSize();
412 if (Delink(FileName
,OriginalPath
,Stats
.DeLinkBytes
,FileSize
) == false)
415 // Lookup the overide information
416 pkgTagSection
&Tags
= Db
.Control
.Section
;
417 string Package
= Tags
.FindS("Package");
419 // if we generate a Packages file for a given arch, we use it to
420 // look for overrides. if we run in "simple" mode without the
421 // "Architecures" variable in the config we use the architecure value
426 Architecture
= Tags
.FindS("Architecture");
427 unique_ptr
<Override::Item
> OverItem(Over
.GetItem(Package
,Architecture
));
429 if (Package
.empty() == true)
430 return _error
->Error(_("Archive had no package field"));
432 // If we need to do any rewriting of the header do it now..
433 if (OverItem
.get() == 0)
435 if (NoOverride
== false)
438 ioprintf(c1out
, _(" %s has no override entry\n"), Package
.c_str());
441 OverItem
= unique_ptr
<Override::Item
>(new Override::Item
);
442 OverItem
->FieldOverride
["Section"] = Tags
.FindS("Section");
443 OverItem
->Priority
= Tags
.FindS("Priority");
446 // Strip the DirStrip prefix from the FileName and add the PathPrefix
448 if (DirStrip
.empty() == false &&
449 FileName
.length() > DirStrip
.length() &&
450 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
451 DirStrip
.begin(),DirStrip
.end()) == 0)
452 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
454 NewFileName
= FileName
;
455 if (PathPrefix
.empty() == false)
456 NewFileName
= flCombine(PathPrefix
,NewFileName
);
458 /* Configuration says we don't want to include the long Description
459 in the package file - instead we want to ship a separated file */
461 if (LongDescription
== false) {
462 desc
= Tags
.FindS("Description").append("\n");
463 OverItem
->FieldOverride
["Description"] = desc
.substr(0, desc
.find('\n')).c_str();
466 // This lists all the changes to the fields we are going to make.
467 std::vector
<pkgTagSection::Tag
> Changes
;
470 strprintf(Size
, "%llu", (unsigned long long) FileSize
);
471 Changes
.push_back(pkgTagSection::Tag::Rewrite("Size", Size
));
473 for (HashStringList::const_iterator hs
= Db
.HashesList
.begin(); hs
!= Db
.HashesList
.end(); ++hs
)
475 if (hs
->HashType() == "MD5Sum")
476 Changes
.push_back(pkgTagSection::Tag::Rewrite("MD5sum", hs
->HashValue()));
477 else if (hs
->HashType() == "Checksum-FileSize")
480 Changes
.push_back(pkgTagSection::Tag::Rewrite(hs
->HashType(), hs
->HashValue()));
482 Changes
.push_back(pkgTagSection::Tag::Rewrite("Filename", NewFileName
));
483 Changes
.push_back(pkgTagSection::Tag::Rewrite("Priority", OverItem
->Priority
));
484 Changes
.push_back(pkgTagSection::Tag::Remove("Status"));
485 Changes
.push_back(pkgTagSection::Tag::Remove("Optional"));
487 string DescriptionMd5
;
488 if (LongDescription
== false) {
489 MD5Summation descmd5
;
490 descmd5
.Add(desc
.c_str());
491 DescriptionMd5
= descmd5
.Result().Value();
492 Changes
.push_back(pkgTagSection::Tag::Rewrite("Description-md5", DescriptionMd5
));
493 if (TransWriter
!= NULL
)
494 TransWriter
->DoPackage(Package
, desc
, DescriptionMd5
);
497 // Rewrite the maintainer field if necessary
499 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
500 if (MaintFailed
== true)
502 if (NoOverride
== false)
505 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"),
506 Package
.c_str(), Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
510 if (NewMaint
.empty() == false)
511 Changes
.push_back(pkgTagSection::Tag::Rewrite("Maintainer", NewMaint
));
513 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
514 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
515 but dpkg does this append bit. So we do the append bit, at least that way the
516 status file and package file will remain similar. There are other transforms
517 but optional is the only legacy one still in use for some lazy reason. */
518 string OptionalStr
= Tags
.FindS("Optional");
519 if (OptionalStr
.empty() == false)
521 if (Tags
.FindS("Suggests").empty() == false)
522 OptionalStr
= Tags
.FindS("Suggests") + ", " + OptionalStr
;
523 Changes
.push_back(pkgTagSection::Tag::Rewrite("Suggests", OptionalStr
));
526 for (map
<string
,string
>::const_iterator I
= OverItem
->FieldOverride
.begin();
527 I
!= OverItem
->FieldOverride
.end(); ++I
)
528 Changes
.push_back(pkgTagSection::Tag::Rewrite(I
->first
, I
->second
));
530 // Rewrite and store the fields.
531 if (Tags
.Write(*Output
, TFRewritePackageOrder
, Changes
) == false ||
532 Output
->Write("\n", 1) == false)
538 PackagesWriter::~PackagesWriter() /*{{{*/
543 // TranslationWriter::TranslationWriter - Constructor /*{{{*/
544 // ---------------------------------------------------------------------
545 /* Create a Translation-Master file for this Packages file */
546 TranslationWriter::TranslationWriter(string
const &File
, string
const &TransCompress
,
547 mode_t
const &Permissions
) : Comp(NULL
), Output(NULL
)
549 if (File
.empty() == true)
552 Comp
= new MultiCompress(File
, TransCompress
, Permissions
);
553 Output
= &Comp
->Input
;
556 // TranslationWriter::DoPackage - Process a single package /*{{{*/
557 // ---------------------------------------------------------------------
558 /* Create a Translation-Master file for this Packages file */
559 bool TranslationWriter::DoPackage(string
const &Pkg
, string
const &Desc
,
565 // Different archs can include different versions and therefore
566 // different descriptions - so we need to check for both name and md5.
567 string
const Record
= Pkg
+ ":" + MD5
;
569 if (Included
.find(Record
) != Included
.end())
573 strprintf(out
, "Package: %s\nDescription-md5: %s\nDescription-en: %s\n",
574 Pkg
.c_str(), MD5
.c_str(), Desc
.c_str());
575 Output
->Write(out
.c_str(), out
.length());
577 Included
.insert(Record
);
581 // TranslationWriter::~TranslationWriter - Destructor /*{{{*/
582 // ---------------------------------------------------------------------
584 TranslationWriter::~TranslationWriter()
591 // SourcesWriter::SourcesWriter - Constructor /*{{{*/
592 // ---------------------------------------------------------------------
594 SourcesWriter::SourcesWriter(FileFd
* const GivenOutput
, string
const &DB
, string
const &BOverrides
,string
const &SOverrides
,
595 string
const &ExtOverrides
) :
596 FTWScanner(GivenOutput
), Db(DB
), Stats(Db
.Stats
)
603 // Process the command line options
604 ConfigToDoHashes(DoHashes
, "APT::FTPArchive::Sources");
605 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
606 DoAlwaysStat
= _config
->FindB("APT::FTPArchive::AlwaysStat", false);
608 // Read the override file
609 if (BOverrides
.empty() == false && BOver
.ReadOverride(BOverrides
) == false)
614 // WTF?? The logic above: if we can't read binary overrides, don't even try
615 // reading source overrides. if we can read binary overrides, then say there
616 // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
618 if (ExtOverrides
.empty() == false)
619 SOver
.ReadExtraOverride(ExtOverrides
);
621 if (SOverrides
.empty() == false && FileExists(SOverrides
) == true)
622 SOver
.ReadOverride(SOverrides
,true);
625 // SourcesWriter::DoPackage - Process a single package /*{{{*/
626 static std::string
getDscHash(unsigned int const DoHashes
,
627 Hashes::SupportedHashes
const DoIt
, pkgTagSection
&Tags
, char const * const FieldName
,
628 HashString
const * const Hash
, unsigned long long Size
, std::string FileName
)
630 if ((DoHashes
& DoIt
) != DoIt
|| Tags
.Exists(FieldName
) == false || Hash
== NULL
)
632 std::ostringstream out
;
633 out
<< "\n " << Hash
->HashValue() << " " << Size
<< " " << FileName
634 << "\n " << Tags
.FindS(FieldName
);
637 bool SourcesWriter::DoPackage(string FileName
)
639 // Pull all the data we need form the DB
640 if (Db
.GetFileInfo(FileName
,
641 false, /* DoControl */
642 false, /* DoContents */
643 false, /* GenContentsOnly */
645 DoHashes
, DoAlwaysStat
) == false)
650 // we need to perform a "write" here (this is what finish is doing)
651 // because the call to Db.GetFileInfo() in the loop will change
656 if (Tags
.Scan(Db
.Dsc
.Data
.c_str(), Db
.Dsc
.Data
.length()) == false)
657 return _error
->Error("Could not find a record in the DSC '%s'",FileName
.c_str());
659 if (Tags
.Exists("Source") == false)
660 return _error
->Error("Could not find a Source entry in the DSC '%s'",FileName
.c_str());
663 // Lookup the overide information, finding first the best priority.
665 string Bins
= Tags
.FindS("Binary");
666 char Buffer
[Bins
.length() + 1];
667 unique_ptr
<Override::Item
> OverItem(nullptr);
668 if (Bins
.empty() == false)
670 strcpy(Buffer
,Bins
.c_str());
672 // Ignore too-long errors.
674 TokSplitString(',',Buffer
,BinList
,sizeof(BinList
)/sizeof(BinList
[0]));
676 // Look at all the binaries
677 unsigned char BestPrioV
= pkgCache::State::Extra
;
678 for (unsigned I
= 0; BinList
[I
] != 0; I
++)
680 unique_ptr
<Override::Item
> Itm(BOver
.GetItem(BinList
[I
]));
684 unsigned char NewPrioV
= debListParser::GetPrio(Itm
->Priority
);
685 if (NewPrioV
< BestPrioV
|| BestPrio
.empty() == true)
687 BestPrioV
= NewPrioV
;
688 BestPrio
= Itm
->Priority
;
691 if (OverItem
.get() == 0)
692 OverItem
= std::move(Itm
);
696 // If we need to do any rewriting of the header do it now..
697 if (OverItem
.get() == 0)
699 if (NoOverride
== false)
702 ioprintf(c1out
, _(" %s has no override entry\n"), Tags
.FindS("Source").c_str());
705 OverItem
.reset(new Override::Item
);
709 if (stat(FileName
.c_str(), &St
) != 0)
710 return _error
->Errno("fstat","Failed to stat %s",FileName
.c_str());
712 unique_ptr
<Override::Item
> SOverItem(SOver
.GetItem(Tags
.FindS("Source")));
713 // const unique_ptr<Override::Item> autoSOverItem(SOverItem);
714 if (SOverItem
.get() == 0)
716 ioprintf(c1out
, _(" %s has no source override entry\n"), Tags
.FindS("Source").c_str());
717 SOverItem
= unique_ptr
<Override::Item
>(BOver
.GetItem(Tags
.FindS("Source")));
718 if (SOverItem
.get() == 0)
720 ioprintf(c1out
, _(" %s has no binary override entry either\n"), Tags
.FindS("Source").c_str());
721 SOverItem
= unique_ptr
<Override::Item
>(new Override::Item
);
722 *SOverItem
= *OverItem
;
726 // Add the dsc to the files hash list
727 string
const strippedName
= flNotDir(FileName
);
728 std::string
const Files
= getDscHash(DoHashes
, Hashes::MD5SUM
, Tags
, "Files", Db
.HashesList
.find("MD5Sum"), St
.st_size
, strippedName
);
729 std::string ChecksumsSha1
= getDscHash(DoHashes
, Hashes::SHA1SUM
, Tags
, "Checksums-Sha1", Db
.HashesList
.find("SHA1"), St
.st_size
, strippedName
);
730 std::string ChecksumsSha256
= getDscHash(DoHashes
, Hashes::SHA256SUM
, Tags
, "Checksums-Sha256", Db
.HashesList
.find("SHA256"), St
.st_size
, strippedName
);
731 std::string ChecksumsSha512
= getDscHash(DoHashes
, Hashes::SHA512SUM
, Tags
, "Checksums-Sha512", Db
.HashesList
.find("SHA512"), St
.st_size
, strippedName
);
733 // Strip the DirStrip prefix from the FileName and add the PathPrefix
735 if (DirStrip
.empty() == false &&
736 FileName
.length() > DirStrip
.length() &&
737 stringcmp(DirStrip
,OriginalPath
,OriginalPath
+ DirStrip
.length()) == 0)
738 NewFileName
= string(OriginalPath
+ DirStrip
.length());
740 NewFileName
= OriginalPath
;
741 if (PathPrefix
.empty() == false)
742 NewFileName
= flCombine(PathPrefix
,NewFileName
);
744 string Directory
= flNotFile(OriginalPath
);
745 string Package
= Tags
.FindS("Source");
747 // Perform operation over all of the files
749 const char *C
= Files
.c_str();
750 char *RealPath
= NULL
;
751 for (;isspace(*C
); C
++);
754 // Parse each of the elements
755 if (ParseQuoteWord(C
,ParseJnk
) == false ||
756 ParseQuoteWord(C
,ParseJnk
) == false ||
757 ParseQuoteWord(C
,ParseJnk
) == false)
758 return _error
->Error("Error parsing file record");
760 string OriginalPath
= Directory
+ ParseJnk
;
762 // Add missing hashes to source files
763 if (((DoHashes
& Hashes::SHA1SUM
) == Hashes::SHA1SUM
&& !Tags
.Exists("Checksums-Sha1")) ||
764 ((DoHashes
& Hashes::SHA256SUM
) == Hashes::SHA256SUM
&& !Tags
.Exists("Checksums-Sha256")) ||
765 ((DoHashes
& Hashes::SHA512SUM
) == Hashes::SHA512SUM
&& !Tags
.Exists("Checksums-Sha512")))
767 if (Db
.GetFileInfo(OriginalPath
,
768 false, /* DoControl */
769 false, /* DoContents */
770 false, /* GenContentsOnly */
771 false, /* DoSource */
773 DoAlwaysStat
) == false)
775 return _error
->Error("Error getting file info");
778 for (HashStringList::const_iterator hs
= Db
.HashesList
.begin(); hs
!= Db
.HashesList
.end(); ++hs
)
780 if (hs
->HashType() == "MD5Sum" || hs
->HashType() == "Checksum-FileSize")
782 char const * fieldname
;
784 if (hs
->HashType() == "SHA1")
786 fieldname
= "Checksums-Sha1";
787 out
= &ChecksumsSha1
;
789 else if (hs
->HashType() == "SHA256")
791 fieldname
= "Checksums-Sha256";
792 out
= &ChecksumsSha256
;
794 else if (hs
->HashType() == "SHA512")
796 fieldname
= "Checksums-Sha512";
797 out
= &ChecksumsSha512
;
801 _error
->Warning("Ignoring unknown Checksumtype %s in SourcesWriter::DoPackages", hs
->HashType().c_str());
804 if (Tags
.Exists(fieldname
) == true)
806 std::ostringstream streamout
;
807 streamout
<< "\n " << hs
->HashValue() << " " << Db
.GetFileSize() << " " << ParseJnk
;
808 out
->append(streamout
.str());
811 // write back the GetFileInfo() stats data
815 // Perform the delinking operation
818 if (readlink(OriginalPath
.c_str(),Jnk
,sizeof(Jnk
)) != -1 &&
819 (RealPath
= realpath(OriginalPath
.c_str(),NULL
)) != 0)
821 string RP
= RealPath
;
823 if (Delink(RP
,OriginalPath
.c_str(),Stats
.DeLinkBytes
,St
.st_size
) == false)
828 Directory
= flNotFile(NewFileName
);
829 if (Directory
.length() > 2)
830 Directory
.erase(Directory
.end()-1);
832 // This lists all the changes to the fields we are going to make.
833 // (5 hardcoded + checksums + maintainer + end marker)
834 std::vector
<pkgTagSection::Tag
> Changes
;
836 Changes
.push_back(pkgTagSection::Tag::Remove("Source"));
837 Changes
.push_back(pkgTagSection::Tag::Rewrite("Package", Package
));
838 if (Files
.empty() == false)
839 Changes
.push_back(pkgTagSection::Tag::Rewrite("Files", Files
));
840 if (ChecksumsSha1
.empty() == false)
841 Changes
.push_back(pkgTagSection::Tag::Rewrite("Checksums-Sha1", ChecksumsSha1
));
842 if (ChecksumsSha256
.empty() == false)
843 Changes
.push_back(pkgTagSection::Tag::Rewrite("Checksums-Sha256", ChecksumsSha256
));
844 if (ChecksumsSha512
.empty() == false)
845 Changes
.push_back(pkgTagSection::Tag::Rewrite("Checksums-Sha512", ChecksumsSha512
));
846 if (Directory
!= "./")
847 Changes
.push_back(pkgTagSection::Tag::Rewrite("Directory", Directory
));
848 Changes
.push_back(pkgTagSection::Tag::Rewrite("Priority", BestPrio
));
849 Changes
.push_back(pkgTagSection::Tag::Remove("Status"));
851 // Rewrite the maintainer field if necessary
853 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"), MaintFailed
);
854 if (MaintFailed
== true)
856 if (NoOverride
== false)
859 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"), Package
.c_str(),
860 Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
863 if (NewMaint
.empty() == false)
864 Changes
.push_back(pkgTagSection::Tag::Rewrite("Maintainer", NewMaint
.c_str()));
866 for (map
<string
,string
>::const_iterator I
= SOverItem
->FieldOverride
.begin();
867 I
!= SOverItem
->FieldOverride
.end(); ++I
)
868 Changes
.push_back(pkgTagSection::Tag::Rewrite(I
->first
, I
->second
));
870 // Rewrite and store the fields.
871 if (Tags
.Write(*Output
, TFRewriteSourceOrder
, Changes
) == false ||
872 Output
->Write("\n", 1) == false)
881 // ContentsWriter::ContentsWriter - Constructor /*{{{*/
882 // ---------------------------------------------------------------------
884 ContentsWriter::ContentsWriter(FileFd
* const GivenOutput
, string
const &DB
,
885 string
const &Arch
, bool const IncludeArchAll
) :
886 FTWScanner(GivenOutput
, Arch
, IncludeArchAll
), Db(DB
), Stats(Db
.Stats
)
892 // ContentsWriter::DoPackage - Process a single package /*{{{*/
893 // ---------------------------------------------------------------------
894 /* If Package is the empty string the control record will be parsed to
895 determine what the package name is. */
896 bool ContentsWriter::DoPackage(string FileName
, string Package
)
898 if (!Db
.GetFileInfo(FileName
,
899 Package
.empty(), /* DoControl */
900 true, /* DoContents */
901 false, /* GenContentsOnly */
902 false, /* DoSource */
904 false /* checkMtime */))
909 // Parse the package name
910 if (Package
.empty() == true)
912 Package
= Db
.Control
.Section
.FindS("Package");
915 Db
.Contents
.Add(Gen
,Package
);
920 // ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
921 // ---------------------------------------------------------------------
923 bool ContentsWriter::ReadFromPkgs(string
const &PkgFile
,string
const &PkgCompress
)
925 MultiCompress
Pkgs(PkgFile
,PkgCompress
,0,false);
926 if (_error
->PendingError() == true)
929 // Open the package file
931 if (Pkgs
.OpenOld(Fd
) == false)
934 pkgTagFile
Tags(&Fd
);
935 if (_error
->PendingError() == true)
939 pkgTagSection Section
;
940 while (Tags
.Step(Section
) == true)
942 string File
= flCombine(Prefix
,Section
.FindS("FileName"));
943 string Package
= Section
.FindS("Section");
944 if (Package
.empty() == false && Package
.end()[-1] != '/')
947 Package
+= Section
.FindS("Package");
950 Package
+= Section
.FindS("Package");
952 DoPackage(File
,Package
);
953 if (_error
->empty() == false)
955 _error
->Error("Errors apply to file '%s'",File
.c_str());
956 _error
->DumpErrors();
960 // Tidy the compressor
968 // ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
969 // ---------------------------------------------------------------------
971 ReleaseWriter::ReleaseWriter(FileFd
* const GivenOutput
, string
const &/*DB*/) : FTWScanner(GivenOutput
)
973 if (_config
->FindB("APT::FTPArchive::Release::Default-Patterns", true) == true)
975 AddPattern("Packages");
976 AddPattern("Packages.gz");
977 AddPattern("Packages.bz2");
978 AddPattern("Packages.lzma");
979 AddPattern("Packages.xz");
980 AddPattern("Translation-*");
981 AddPattern("Sources");
982 AddPattern("Sources.gz");
983 AddPattern("Sources.bz2");
984 AddPattern("Sources.lzma");
985 AddPattern("Sources.xz");
986 AddPattern("Release");
987 AddPattern("Contents-*");
989 AddPattern("md5sum.txt");
991 AddPatterns(_config
->FindVector("APT::FTPArchive::Release::Patterns"));
993 time_t const now
= time(NULL
);
995 setlocale(LC_TIME
, "C");
998 if (strftime(datestr
, sizeof(datestr
), "%a, %d %b %Y %H:%M:%S UTC",
1004 time_t const validuntil
= now
+ _config
->FindI("APT::FTPArchive::Release::ValidTime", 0);
1006 if (now
== validuntil
||
1007 strftime(validstr
, sizeof(validstr
), "%a, %d %b %Y %H:%M:%S UTC",
1008 gmtime(&validuntil
)) == 0)
1013 setlocale(LC_TIME
, "");
1015 map
<string
,string
> Fields
;
1016 Fields
["Origin"] = "";
1017 Fields
["Label"] = "";
1018 Fields
["Suite"] = "";
1019 Fields
["Version"] = "";
1020 Fields
["Codename"] = "";
1021 Fields
["Date"] = datestr
;
1022 Fields
["Valid-Until"] = validstr
;
1023 Fields
["Architectures"] = "";
1024 Fields
["Components"] = "";
1025 Fields
["Description"] = "";
1026 if (_config
->FindB("APT::FTPArchive::DoByHash", false) == true)
1027 Fields
["Acquire-By-Hash"] = "true";
1029 for(map
<string
,string
>::const_iterator I
= Fields
.begin();
1033 string Config
= string("APT::FTPArchive::Release::") + (*I
).first
;
1034 string Value
= _config
->Find(Config
, (*I
).second
.c_str());
1038 std::string
const out
= I
->first
+ ": " + Value
+ "\n";
1039 Output
->Write(out
.c_str(), out
.length());
1042 ConfigToDoHashes(DoHashes
, "APT::FTPArchive::Release");
1045 // ReleaseWriter::DoPackage - Process a single package /*{{{*/
1046 // ---------------------------------------------------------------------
1047 bool ReleaseWriter::DoPackage(string FileName
)
1049 // Strip the DirStrip prefix from the FileName and add the PathPrefix
1051 if (DirStrip
.empty() == false &&
1052 FileName
.length() > DirStrip
.length() &&
1053 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
1054 DirStrip
.begin(),DirStrip
.end()) == 0)
1056 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
1057 while (NewFileName
[0] == '/')
1058 NewFileName
= string(NewFileName
.begin() + 1,NewFileName
.end());
1061 NewFileName
= FileName
;
1063 if (PathPrefix
.empty() == false)
1064 NewFileName
= flCombine(PathPrefix
,NewFileName
);
1066 FileFd
fd(FileName
, FileFd::ReadOnly
);
1073 CheckSums
[NewFileName
].size
= fd
.Size();
1075 Hashes
hs(DoHashes
);
1077 CheckSums
[NewFileName
].Hashes
= hs
.GetHashStringList();
1080 // FIXME: wrong layer in the code(?)
1081 // FIXME2: symlink instead of create a copy
1082 if (_config
->FindB("APT::FTPArchive::DoByHash", false) == true)
1084 std::string Input
= FileName
;
1085 HashStringList hsl
= hs
.GetHashStringList();
1086 for(HashStringList::const_iterator h
= hsl
.begin();
1087 h
!= hsl
.end(); ++h
)
1091 if (flNotDir(FileName
) == "Release" || flNotDir(FileName
) == "InRelease")
1094 std::string ByHashOutputFile
= GenByHashFilename(Input
, *h
);
1095 std::string ByHashOutputDir
= flNotFile(ByHashOutputFile
);
1096 if(!CreateDirectory(flNotFile(Input
), ByHashOutputDir
))
1097 return _error
->Warning("can not create dir %s", flNotFile(ByHashOutputFile
).c_str());
1100 FileFd
In(Input
, FileFd::ReadOnly
);
1101 FileFd
Out(ByHashOutputFile
, FileFd::WriteEmpty
);
1102 if(!CopyFile(In
, Out
))
1103 return _error
->Warning("failed to copy %s %s", Input
.c_str(), ByHashOutputFile
.c_str());
1111 // ReleaseWriter::Finish - Output the checksums /*{{{*/
1112 // ---------------------------------------------------------------------
1113 static void printChecksumTypeRecord(FileFd
&Output
, char const * const Type
, map
<string
, ReleaseWriter::CheckSum
> const &CheckSums
)
1117 strprintf(out
, "%s:\n", Type
);
1118 Output
.Write(out
.c_str(), out
.length());
1120 for(map
<string
,ReleaseWriter::CheckSum
>::const_iterator I
= CheckSums
.begin();
1121 I
!= CheckSums
.end(); ++I
)
1123 HashString
const * const hs
= I
->second
.Hashes
.find(Type
);
1127 strprintf(out
, " %s %16llu %s\n",
1128 hs
->HashValue().c_str(),
1130 (*I
).first
.c_str());
1131 Output
.Write(out
.c_str(), out
.length());
1134 void ReleaseWriter::Finish()
1136 if ((DoHashes
& Hashes::MD5SUM
) == Hashes::MD5SUM
)
1137 printChecksumTypeRecord(*Output
, "MD5Sum", CheckSums
);
1138 if ((DoHashes
& Hashes::SHA1SUM
) == Hashes::SHA1SUM
)
1139 printChecksumTypeRecord(*Output
, "SHA1", CheckSums
);
1140 if ((DoHashes
& Hashes::SHA256SUM
) == Hashes::SHA256SUM
)
1141 printChecksumTypeRecord(*Output
, "SHA256", CheckSums
);
1142 if ((DoHashes
& Hashes::SHA512SUM
) == Hashes::SHA512SUM
)
1143 printChecksumTypeRecord(*Output
, "SHA512", CheckSums
);
1145 // go by-hash cleanup
1146 map
<string
,ReleaseWriter::CheckSum
>::const_iterator prev
= CheckSums
.begin();
1147 if (_config
->FindB("APT::FTPArchive::DoByHash", false) == true)
1149 for(map
<string
,ReleaseWriter::CheckSum
>::const_iterator I
= CheckSums
.begin();
1150 I
!= CheckSums
.end(); ++I
)
1152 if (I
->first
== "Release" || I
->first
== "InRelease")
1155 // keep iterating until we find a new subdir
1156 if(flNotFile(I
->first
) == flNotFile(prev
->first
))
1159 // clean that subdir up
1160 int keepFiles
= _config
->FindI("APT::FTPArchive::By-Hash-Keep", 3);
1161 // calculate how many compressors are used (the amount of files
1162 // in that subdir generated for this run)
1163 keepFiles
*= std::distance(prev
, I
);
1166 HashStringList hsl
= prev
->second
.Hashes
;
1167 for(HashStringList::const_iterator h
= hsl
.begin();
1168 h
!= hsl
.end(); ++h
)
1174 std::string RealFilename
= DirStrip
+"/"+prev
->first
;
1175 std::string ByHashOutputFile
= GenByHashFilename(RealFilename
, *h
);
1176 DeleteAllButMostRecent(flNotFile(ByHashOutputFile
), keepFiles
);