source: projects/synaptic/trunk/common/indexcopy.cc @ 280

Revision 280, 16.7 KB checked in by yasumichi, 15 years ago (diff)

first import

Line 
1// -*- mode: cpp; mode: fold -*-
2// Description                                                          /*{{{*/
3// $Id: indexcopy.cc,v 1.2 2002/07/25 18:07:18 niemeyer Exp $
4/* ######################################################################
5
6   Index Copying - Aid for copying and verifying the index files
7   
8   This class helps apt-cache reconstruct a damaged index files.
9   
10   ##################################################################### */
11                                                                        /*}}} */
12// Include Files                                                        /*{{{*/
13#include "indexcopy.h"
14#include "i18n.h"
15
16#include <apt-pkg/error.h>
17#include <apt-pkg/progress.h>
18#include <apt-pkg/strutl.h>
19#include <apt-pkg/fileutl.h>
20#include <apt-pkg/configuration.h>
21#include <apt-pkg/tagfile.h>
22
23#include <iostream>
24#include <unistd.h>
25#include <sys/stat.h>
26#include <stdio.h>
27                                                                        /*}}} */
28
29using namespace std;
30
31// IndexCopy::CopyPackages - Copy the package files from the CD         /*{{{*/
32// ---------------------------------------------------------------------
33/* */
34bool IndexCopy::CopyPackages(string CDROM, string Name,
35                             vector<string> &List)
36{
37   if (List.size() == 0)
38      return true;
39
40   OpTextProgress Progress;
41
42   bool NoStat = _config->FindB("APT::CDROM::Fast", false);
43   bool Debug = _config->FindB("Debug::aptcdrom", false);
44
45   // Prepare the progress indicator
46   unsigned long TotalSize = 0;
47   for (vector<string>::iterator I = List.begin(); I != List.end(); I++) {
48      struct stat Buf;
49      if (stat(string(*I + GetFileName()).c_str(), &Buf) != 0 &&
50          stat(string(*I + GetFileName() + ".gz").c_str(), &Buf) != 0)
51         return _error->Errno("stat", _("Stat failed for %s"),
52                              string(*I + GetFileName()).c_str());
53      TotalSize += Buf.st_size;
54   }
55
56   unsigned long CurrentSize = 0;
57   unsigned int NotFound = 0;
58   unsigned int WrongSize = 0;
59   unsigned int Packages = 0;
60   for (vector<string>::iterator I = List.begin(); I != List.end(); I++) {
61      string OrigPath = string(*I, CDROM.length());
62      unsigned long FileSize = 0;
63
64      // Open the package file
65      FileFd Pkg;
66      if (FileExists(*I + GetFileName()) == true) {
67         Pkg.Open(*I + GetFileName(), FileFd::ReadOnly);
68         FileSize = Pkg.Size();
69      } else {
70         FileFd From(*I + GetFileName() + ".gz", FileFd::ReadOnly);
71         if (_error->PendingError() == true)
72            return false;
73         FileSize = From.Size();
74
75         // Get a temp file
76         FILE *tmp = tmpfile();
77         if (tmp == 0)
78            return _error->Errno("tmpfile", _("Unable to create a tmp file"));
79         Pkg.Fd(dup(fileno(tmp)));
80         fclose(tmp);
81
82         // Fork gzip
83         int Process = fork();
84         if (Process < 0)
85            return _error->Errno("fork",
86                                 "Internal Error: Couldn't fork gzip. Please report.");
87
88         // The child
89         if (Process == 0) {
90            dup2(From.Fd(), STDIN_FILENO);
91            dup2(Pkg.Fd(), STDOUT_FILENO);
92            SetCloseExec(STDIN_FILENO, false);
93            SetCloseExec(STDOUT_FILENO, false);
94
95            const char *Args[3];
96            string Tmp = _config->Find("Dir::bin::gzip", "gzip");
97            Args[0] = Tmp.c_str();
98            Args[1] = "-d";
99            Args[2] = 0;
100            execvp(Args[0], (char **)Args);
101            exit(100);
102         }
103         // Wait for gzip to finish
104         if (ExecWait
105             (Process, _config->Find("Dir::bin::gzip", "gzip").c_str(),
106              false) == false)
107            return _error->Error(_("gzip failed, perhaps the disk is full."));
108
109         Pkg.Seek(0);
110      }
111      pkgTagFile Parser(&Pkg);
112      if (_error->PendingError() == true)
113         return false;
114
115      // Open the output file
116      char S[400];
117      snprintf(S, sizeof(S), "cdrom:[%s]/%s%s", Name.c_str(),
118               (*I).c_str() + CDROM.length(), GetFileName());
119      string TargetF = _config->FindDir("Dir::State::lists") + "partial/";
120      TargetF += URItoFileName(S);
121      if (_config->FindB("APT::CDROM::NoAct", false) == true)
122         TargetF = "/dev/null";
123      FileFd Target(TargetF, FileFd::WriteEmpty);
124      FILE *TargetFl = fdopen(dup(Target.Fd()), "w");
125      if (_error->PendingError() == true)
126         return false;
127      if (TargetFl == 0)
128         return _error->Errno("fdopen", _("Failed to reopen fd"));
129
130      // Setup the progress meter
131      Progress.OverallProgress(CurrentSize, TotalSize, FileSize,
132                               string("Reading ") + Type() + " Indexes");
133
134      // Parse
135      Progress.SubProgress(Pkg.Size());
136      pkgTagSection Section;
137      this->Section = &Section;
138      string Prefix;
139      unsigned long Hits = 0;
140      unsigned long Chop = 0;
141      while (Parser.Step(Section) == true) {
142         Progress.Progress(Parser.Offset());
143         string File;
144         unsigned long Size;
145         if (GetFile(File, Size) == false) {
146            fclose(TargetFl);
147            return false;
148         }
149
150         if (Chop != 0)
151            File = OrigPath + ChopDirs(File, Chop);
152
153         // See if the file exists
154         bool Mangled = false;
155         if (NoStat == false || Hits < 10) {
156            // Attempt to fix broken structure
157            if (Hits == 0) {
158               if (ReconstructPrefix(Prefix, OrigPath, CDROM, File) == false &&
159                   ReconstructChop(Chop, *I, File) == false) {
160                  if (Debug == true)
161                     clog << "Missed: " << File << endl;
162                  NotFound++;
163                  continue;
164               }
165               if (Chop != 0)
166                  File = OrigPath + ChopDirs(File, Chop);
167            }
168            // Get the size
169            struct stat Buf;
170            if (stat(string(CDROM + Prefix + File).c_str(), &Buf) != 0 ||
171                Buf.st_size == 0) {
172               // Attempt to fix busted symlink support for one instance
173               string OrigFile = File;
174               string::size_type Start = File.find("binary-");
175               string::size_type End = File.find("/", Start + 3);
176               if (Start != string::npos && End != string::npos) {
177                  File.replace(Start, End - Start, "binary-all");
178                  Mangled = true;
179               }
180
181               if (Mangled == false ||
182                   stat(string(CDROM + Prefix + File).c_str(), &Buf) != 0) {
183                  if (Debug == true)
184                     clog << "Missed(2): " << OrigFile << endl;
185                  NotFound++;
186                  continue;
187               }
188            }
189            // Size match
190            if ((unsigned)Buf.st_size != Size) {
191               if (Debug == true)
192                  clog << "Wrong Size: " << File << endl;
193               WrongSize++;
194               continue;
195            }
196         }
197
198         Packages++;
199         Hits++;
200
201         if (RewriteEntry(TargetFl, File) == false) {
202            fclose(TargetFl);
203            return false;
204         }
205      }
206      fclose(TargetFl);
207
208      if (Debug == true)
209         cout << " Processed by using Prefix '" << Prefix <<
210            "' and chop " << Chop << endl;
211
212      if (_config->FindB("APT::CDROM::NoAct", false) == false) {
213         // Move out of the partial directory
214         Target.Close();
215         string FinalF = _config->FindDir("Dir::State::lists");
216         FinalF += URItoFileName(S);
217         if (rename(TargetF.c_str(), FinalF.c_str()) != 0)
218            return _error->Errno("rename", _("Failed to rename"));
219
220         // Copy the release file
221         snprintf(S, sizeof(S), "cdrom:[%s]/%sRelease", Name.c_str(),
222                  (*I).c_str() + CDROM.length());
223         string TargetF = _config->FindDir("Dir::State::lists") + "partial/";
224         TargetF += URItoFileName(S);
225         if (FileExists(*I + "Release") == true) {
226            FileFd Target(TargetF, FileFd::WriteEmpty);
227            FileFd Rel(*I + "Release", FileFd::ReadOnly);
228            if (_error->PendingError() == true)
229               return false;
230
231            if (CopyFile(Rel, Target) == false)
232               return false;
233         } else {
234            // Empty release file
235            FileFd Target(TargetF, FileFd::WriteEmpty);
236         }
237
238         // Rename the release file
239         FinalF = _config->FindDir("Dir::State::lists");
240         FinalF += URItoFileName(S);
241         if (rename(TargetF.c_str(), FinalF.c_str()) != 0)
242            return _error->Errno("rename", _("Failed to rename"));
243      }
244
245      /* Mangle the source to be in the proper notation with
246         prefix dist [component] */
247      *I = string(*I, Prefix.length());
248      ConvertToSourceList(CDROM, *I);
249      *I = Prefix + ' ' + *I;
250
251      CurrentSize += FileSize;
252   }
253   Progress.Done();
254
255   // Some stats
256   cout << "Wrote " << Packages << " records";
257   if (NotFound != 0)
258      cout << " with " << NotFound << " missing files";
259   if (NotFound != 0 && WrongSize != 0)
260      cout << " and";
261   if (WrongSize != 0)
262      cout << " with " << WrongSize << " mismatched files";
263   cout << '.' << endl;
264
265   if (Packages == 0)
266      _error->Warning(_("No valid records were found."));
267
268   if (NotFound + WrongSize > 10)
269      cout << "Alot of entries were discarded, something may be wrong." <<
270         endl;
271
272   return true;
273}
274
275                                                                        /*}}} */
276// IndexCopy::ChopDirs - Chop off the leading directory components      /*{{{*/
277// ---------------------------------------------------------------------
278/* */
279string IndexCopy::ChopDirs(string Path, unsigned int Depth)
280{
281   string::size_type I = 0;
282   do {
283      I = Path.find('/', I + 1);
284      Depth--;
285   }
286   while (I != string::npos && Depth != 0);
287
288   if (I == string::npos)
289      return string();
290
291   return string(Path, I + 1);
292}
293
294                                                                        /*}}} */
295// IndexCopy::ReconstructPrefix - Fix strange prefixing                 /*{{{*/
296// ---------------------------------------------------------------------
297/* This prepends dir components from the path to the package files to
298   the path to the deb until it is found */
299bool IndexCopy::ReconstructPrefix(string &Prefix, string OrigPath, string CD,
300                                  string File)
301{
302   bool Debug = _config->FindB("Debug::aptcdrom", false);
303   unsigned int Depth = 1;
304   string MyPrefix = Prefix;
305   while (1) {
306      struct stat Buf;
307      if (stat(string(CD + MyPrefix + File).c_str(), &Buf) != 0) {
308         if (Debug == true)
309            cout << "Failed, " << CD + MyPrefix + File << endl;
310         if (GrabFirst(OrigPath, MyPrefix, Depth++) == true)
311            continue;
312
313         return false;
314      } else {
315         Prefix = MyPrefix;
316         return true;
317      }
318   }
319   return false;
320}
321
322                                                                        /*}}} */
323// IndexCopy::ReconstructChop - Fixes bad source paths                  /*{{{*/
324// ---------------------------------------------------------------------
325/* This removes path components from the filename and prepends the location
326   of the package files until a file is found */
327bool IndexCopy::ReconstructChop(unsigned long &Chop, string Dir, string File)
328{
329   // Attempt to reconstruct the filename
330   unsigned long Depth = 0;
331   while (1) {
332      struct stat Buf;
333      if (stat(string(Dir + File).c_str(), &Buf) != 0) {
334         File = ChopDirs(File, 1);
335         Depth++;
336         if (File.empty() == false)
337            continue;
338         return false;
339      } else {
340         Chop = Depth;
341         return true;
342      }
343   }
344   return false;
345}
346
347                                                                        /*}}} */
348// IndexCopy::ConvertToSourceList - Convert a Path to a sourcelist      /*{{{*/
349// ---------------------------------------------------------------------
350/* We look for things in dists/ notation and convert them to
351   <dist> <component> form otherwise it is left alone. This also strips
352   the CD path.
353 
354   This implements a regex sort of like:
355    (.*)/dists/([^/]*)/(.*)/binary-*
356     ^          ^      ^- Component
357     |          |-------- Distribution
358     |------------------- Path
359   
360   It was deciced to use only a single word for dist (rather than say
361   unstable/non-us) to increase the chance that each CD gets a single
362   line in sources.list.
363 */
364void IndexCopy::ConvertToSourceList(string CD, string &Path)
365{
366   char S[300];
367   snprintf(S, sizeof(S), "binary-%s",
368            _config->Find("Apt::Architecture").c_str());
369
370   // Strip the cdrom base path
371   Path = string(Path, CD.length());
372   if (Path.empty() == true)
373      Path = "/";
374
375   // Too short to be a dists/ type
376   if (Path.length() < strlen("dists/"))
377      return;
378
379   // Not a dists type.
380   if (stringcmp(Path.c_str(), Path.c_str() + strlen("dists/"), "dists/") != 0)
381      return;
382
383   // Isolate the dist
384   string::size_type Slash = strlen("dists/");
385   string::size_type Slash2 = Path.find('/', Slash + 1);
386   if (Slash2 == string::npos || Slash2 + 2 >= Path.length())
387      return;
388   string Dist = string(Path, Slash, Slash2 - Slash);
389
390   // Isolate the component
391   Slash = Slash2;
392   for (unsigned I = 0; I != 10; I++) {
393      Slash = Path.find('/', Slash + 1);
394      if (Slash == string::npos || Slash + 2 >= Path.length())
395         return;
396      string Comp = string(Path, Slash2 + 1, Slash - Slash2 - 1);
397
398      // Verify the trailing binary- bit
399      string::size_type BinSlash = Path.find('/', Slash + 1);
400      if (Slash == string::npos)
401         return;
402      string Binary = string(Path, Slash + 1, BinSlash - Slash - 1);
403
404      if (Binary != S && Binary != "source")
405         continue;
406
407      Path = Dist + ' ' + Comp;
408      return;
409   }
410}
411
412                                                                        /*}}} */
413// IndexCopy::GrabFirst - Return the first Depth path components        /*{{{*/
414// ---------------------------------------------------------------------
415/* */
416bool IndexCopy::GrabFirst(string Path, string &To, unsigned int Depth)
417{
418   string::size_type I = 0;
419   do {
420      I = Path.find('/', I + 1);
421      Depth--;
422   }
423   while (I != string::npos && Depth != 0);
424
425   if (I == string::npos)
426      return false;
427
428   To = string(Path, 0, I + 1);
429   return true;
430}
431
432                                                                        /*}}} */
433// PackageCopy::GetFile - Get the file information from the section     /*{{{*/
434// ---------------------------------------------------------------------
435/* */
436bool PackageCopy::GetFile(string &File, unsigned long &Size)
437{
438   File = Section->FindS("Filename");
439   Size = Section->FindI("Size");
440   if (File.empty() || Size == 0)
441      return _error->Error(_("Cannot find filename or size tag"));
442   return true;
443}
444
445                                                                        /*}}} */
446// PackageCopy::RewriteEntry - Rewrite the entry with a new filename    /*{{{*/
447// ---------------------------------------------------------------------
448/* */
449bool PackageCopy::RewriteEntry(FILE *Target, string File)
450{
451   TFRewriteData Changes[] = { {"Filename", File.c_str()}
452   ,
453   {}
454   };
455
456   if (TFRewrite(Target, *Section, TFRewritePackageOrder, Changes) == false)
457      return false;
458   fputc('\n', Target);
459   return true;
460}
461
462                                                                        /*}}} */
463// SourceCopy::GetFile - Get the file information from the section      /*{{{*/
464// ---------------------------------------------------------------------
465/* */
466bool SourceCopy::GetFile(string &File, unsigned long &Size)
467{
468   string Files = Section->FindS("Files");
469   if (Files.empty() == true)
470      return false;
471
472   // Stash the / terminated directory prefix
473   string Base = Section->FindS("Directory");
474   if (Base.empty() == false && Base[Base.length() - 1] != '/')
475      Base += '/';
476
477   // Read the first file triplet
478   const char *C = Files.c_str();
479   string sSize;
480   string MD5Hash;
481
482   // Parse each of the elements
483   if (ParseQuoteWord(C, MD5Hash) == false ||
484       ParseQuoteWord(C, sSize) == false || ParseQuoteWord(C, File) == false)
485      return _error->Error(_("Error parsing file record"));
486
487   // Parse the size and append the directory
488   Size = atoi(sSize.c_str());
489   File = Base + File;
490   return true;
491}
492
493                                                                        /*}}} */
494// SourceCopy::RewriteEntry - Rewrite the entry with a new filename     /*{{{*/
495// ---------------------------------------------------------------------
496/* */
497bool SourceCopy::RewriteEntry(FILE *Target, string File)
498{
499   string Dir(File, 0, File.rfind('/'));
500   TFRewriteData Changes[] = { {"Directory", Dir.c_str()}
501   ,
502   {}
503   };
504
505   if (TFRewrite(Target, *Section, TFRewriteSourceOrder, Changes) == false)
506      return false;
507   fputc('\n', Target);
508   return true;
509}
510
511                                                                        /*}}} */
Note: See TracBrowser for help on using the repository browser.