~ubuntu-branches/ubuntu/warty/synaptic/warty

« back to all changes in this revision

Viewing changes to common/indexcopy.cc

  • Committer: Bazaar Package Importer
  • Author(s): Michael Vogt
  • Date: 2002-04-04 12:23:30 UTC
  • Revision ID: james.westby@ubuntu.com-20020404122330-il87fkpjajirckb2
Tags: upstream-0.16
ImportĀ upstreamĀ versionĀ 0.16

Show diffs side-by-side

added added

removed removed

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