writer.cc 36 KB

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