]>
git.saurik.com Git - apt.git/blob - cmdline/indexcopy.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: indexcopy.cc,v 1.10 2002/03/26 07:38:58 jgg Exp $
4 /* ######################################################################
6 Index Copying - Aid for copying and verifying the index files
8 This class helps apt-cache reconstruct a damaged index files.
10 ##################################################################### */
12 // Include Files /*{{{*/
13 #include "indexcopy.h"
15 #include <apt-pkg/error.h>
16 #include <apt-pkg/progress.h>
17 #include <apt-pkg/strutl.h>
18 #include <apt-pkg/fileutl.h>
19 #include <apt-pkg/configuration.h>
20 #include <apt-pkg/tagfile.h>
30 // IndexCopy::CopyPackages - Copy the package files from the CD /*{{{*/
31 // ---------------------------------------------------------------------
33 bool IndexCopy::CopyPackages(string CDROM
,string Name
,vector
<string
> &List
)
38 OpTextProgress Progress
;
40 bool NoStat
= _config
->FindB("APT::CDROM::Fast",false);
41 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
43 // Prepare the progress indicator
44 unsigned long TotalSize
= 0;
45 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
48 if (stat(string(*I
+ GetFileName()).c_str(),&Buf
) != 0 &&
49 stat(string(*I
+ GetFileName() + ".gz").c_str(),&Buf
) != 0)
50 return _error
->Errno("stat","Stat failed for %s",
51 string(*I
+ GetFileName()).c_str());
52 TotalSize
+= Buf
.st_size
;
55 unsigned long CurrentSize
= 0;
56 unsigned int NotFound
= 0;
57 unsigned int WrongSize
= 0;
58 unsigned int Packages
= 0;
59 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
61 string OrigPath
= string(*I
,CDROM
.length());
62 unsigned long FileSize
= 0;
64 // Open the package file
66 if (FileExists(*I
+ GetFileName()) == true)
68 Pkg
.Open(*I
+ GetFileName(),FileFd::ReadOnly
);
69 FileSize
= Pkg
.Size();
73 FileFd
From(*I
+ GetFileName() + ".gz",FileFd::ReadOnly
);
74 if (_error
->PendingError() == true)
76 FileSize
= From
.Size();
79 FILE *tmp
= tmpfile();
81 return _error
->Errno("tmpfile","Unable to create a tmp file");
82 Pkg
.Fd(dup(fileno(tmp
)));
86 pid_t Process
= fork();
88 return _error
->Errno("fork","Couldn't fork gzip");
93 dup2(From
.Fd(),STDIN_FILENO
);
94 dup2(Pkg
.Fd(),STDOUT_FILENO
);
95 SetCloseExec(STDIN_FILENO
,false);
96 SetCloseExec(STDOUT_FILENO
,false);
99 string Tmp
= _config
->Find("Dir::bin::gzip","gzip");
100 Args
[0] = Tmp
.c_str();
103 execvp(Args
[0],(char **)Args
);
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.");
113 pkgTagFile
Parser(&Pkg
);
114 if (_error
->PendingError() == true)
117 // Open the output file
119 snprintf(S
,sizeof(S
),"cdrom:[%s]/%s%s",Name
.c_str(),
120 (*I
).c_str() + CDROM
.length(),GetFileName());
121 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
122 TargetF
+= URItoFileName(S
);
123 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
124 TargetF
= "/dev/null";
125 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
126 FILE *TargetFl
= fdopen(dup(Target
.Fd()),"w");
127 if (_error
->PendingError() == true)
130 return _error
->Errno("fdopen","Failed to reopen fd");
132 // Setup the progress meter
133 Progress
.OverallProgress(CurrentSize
,TotalSize
,FileSize
,
134 string("Reading ") + Type() + " Indexes");
137 Progress
.SubProgress(Pkg
.Size());
138 pkgTagSection Section
;
139 this->Section
= &Section
;
141 unsigned long Hits
= 0;
142 unsigned long Chop
= 0;
143 while (Parser
.Step(Section
) == true)
145 Progress
.Progress(Parser
.Offset());
148 if (GetFile(File
,Size
) == false)
155 File
= OrigPath
+ ChopDirs(File
,Chop
);
157 // See if the file exists
158 bool Mangled
= false;
159 if (NoStat
== false || Hits
< 10)
161 // Attempt to fix broken structure
164 if (ReconstructPrefix(Prefix
,OrigPath
,CDROM
,File
) == false &&
165 ReconstructChop(Chop
,*I
,File
) == false)
168 clog
<< "Missed: " << File
<< endl
;
173 File
= OrigPath
+ ChopDirs(File
,Chop
);
178 if (stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0 ||
181 // Attempt to fix busted symlink support for one instance
182 string OrigFile
= File
;
183 string::size_type Start
= File
.find("binary-");
184 string::size_type End
= File
.find("/",Start
+3);
185 if (Start
!= string::npos
&& End
!= string::npos
)
187 File
.replace(Start
,End
-Start
,"binary-all");
191 if (Mangled
== false ||
192 stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0)
195 clog
<< "Missed(2): " << OrigFile
<< endl
;
202 if ((unsigned)Buf
.st_size
!= Size
)
205 clog
<< "Wrong Size: " << File
<< endl
;
214 if (RewriteEntry(TargetFl
,File
) == false)
223 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << Chop
<< endl
;
225 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
227 // Move out of the partial directory
229 string FinalF
= _config
->FindDir("Dir::State::lists");
230 FinalF
+= URItoFileName(S
);
231 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
232 return _error
->Errno("rename","Failed to rename");
234 // Copy the release file
235 snprintf(S
,sizeof(S
),"cdrom:[%s]/%sRelease",Name
.c_str(),
236 (*I
).c_str() + CDROM
.length());
237 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
238 TargetF
+= URItoFileName(S
);
239 if (FileExists(*I
+ "Release") == true)
241 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
242 FileFd
Rel(*I
+ "Release",FileFd::ReadOnly
);
243 if (_error
->PendingError() == true)
246 if (CopyFile(Rel
,Target
) == false)
251 // Empty release file
252 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
255 // Rename the release file
256 FinalF
= _config
->FindDir("Dir::State::lists");
257 FinalF
+= URItoFileName(S
);
258 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
259 return _error
->Errno("rename","Failed to rename");
262 /* Mangle the source to be in the proper notation with
263 prefix dist [component] */
264 *I
= string(*I
,Prefix
.length());
265 ConvertToSourceList(CDROM
,*I
);
266 *I
= Prefix
+ ' ' + *I
;
268 CurrentSize
+= FileSize
;
273 cout
<< "Wrote " << Packages
<< " records" ;
275 cout
<< " with " << NotFound
<< " missing files";
276 if (NotFound
!= 0 && WrongSize
!= 0)
279 cout
<< " with " << WrongSize
<< " mismatched files";
283 _error
->Warning("No valid records were found.");
285 if (NotFound
+ WrongSize
> 10)
286 cout
<< "Alot of entries were discarded, something may be wrong." << endl
;
291 // IndexCopy::ChopDirs - Chop off the leading directory components /*{{{*/
292 // ---------------------------------------------------------------------
294 string
IndexCopy::ChopDirs(string Path
,unsigned int Depth
)
296 string::size_type I
= 0;
299 I
= Path
.find('/',I
+1);
302 while (I
!= string::npos
&& Depth
!= 0);
304 if (I
== string::npos
)
307 return string(Path
,I
+1);
310 // IndexCopy::ReconstructPrefix - Fix strange prefixing /*{{{*/
311 // ---------------------------------------------------------------------
312 /* This prepends dir components from the path to the package files to
313 the path to the deb until it is found */
314 bool IndexCopy::ReconstructPrefix(string
&Prefix
,string OrigPath
,string CD
,
317 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
318 unsigned int Depth
= 1;
319 string MyPrefix
= Prefix
;
323 if (stat(string(CD
+ MyPrefix
+ File
).c_str(),&Buf
) != 0)
326 cout
<< "Failed, " << CD
+ MyPrefix
+ File
<< endl
;
327 if (GrabFirst(OrigPath
,MyPrefix
,Depth
++) == true)
341 // IndexCopy::ReconstructChop - Fixes bad source paths /*{{{*/
342 // ---------------------------------------------------------------------
343 /* This removes path components from the filename and prepends the location
344 of the package files until a file is found */
345 bool IndexCopy::ReconstructChop(unsigned long &Chop
,string Dir
,string File
)
347 // Attempt to reconstruct the filename
348 unsigned long Depth
= 0;
352 if (stat(string(Dir
+ File
).c_str(),&Buf
) != 0)
354 File
= ChopDirs(File
,1);
356 if (File
.empty() == false)
369 // IndexCopy::ConvertToSourceList - Convert a Path to a sourcelist /*{{{*/
370 // ---------------------------------------------------------------------
371 /* We look for things in dists/ notation and convert them to
372 <dist> <component> form otherwise it is left alone. This also strips
375 This implements a regex sort of like:
376 (.*)/dists/([^/]*)/(.*)/binary-*
378 | |-------- Distribution
379 |------------------- Path
381 It was deciced to use only a single word for dist (rather than say
382 unstable/non-us) to increase the chance that each CD gets a single
383 line in sources.list.
385 void IndexCopy::ConvertToSourceList(string CD
,string
&Path
)
388 snprintf(S
,sizeof(S
),"binary-%s",_config
->Find("Apt::Architecture").c_str());
390 // Strip the cdrom base path
391 Path
= string(Path
,CD
.length());
392 if (Path
.empty() == true)
395 // Too short to be a dists/ type
396 if (Path
.length() < strlen("dists/"))
400 if (stringcmp(Path
.c_str(),Path
.c_str()+strlen("dists/"),"dists/") != 0)
404 string::size_type Slash
= strlen("dists/");
405 string::size_type Slash2
= Path
.find('/',Slash
+ 1);
406 if (Slash2
== string::npos
|| Slash2
+ 2 >= Path
.length())
408 string Dist
= string(Path
,Slash
,Slash2
- Slash
);
410 // Isolate the component
412 for (unsigned I
= 0; I
!= 10; I
++)
414 Slash
= Path
.find('/',Slash
+1);
415 if (Slash
== string::npos
|| Slash
+ 2 >= Path
.length())
417 string Comp
= string(Path
,Slash2
+1,Slash
- Slash2
-1);
419 // Verify the trailing binary- bit
420 string::size_type BinSlash
= Path
.find('/',Slash
+ 1);
421 if (Slash
== string::npos
)
423 string Binary
= string(Path
,Slash
+1,BinSlash
- Slash
-1);
425 if (Binary
!= S
&& Binary
!= "source")
428 Path
= Dist
+ ' ' + Comp
;
433 // IndexCopy::GrabFirst - Return the first Depth path components /*{{{*/
434 // ---------------------------------------------------------------------
436 bool IndexCopy::GrabFirst(string Path
,string
&To
,unsigned int Depth
)
438 string::size_type I
= 0;
441 I
= Path
.find('/',I
+1);
444 while (I
!= string::npos
&& Depth
!= 0);
446 if (I
== string::npos
)
449 To
= string(Path
,0,I
+1);
453 // PackageCopy::GetFile - Get the file information from the section /*{{{*/
454 // ---------------------------------------------------------------------
456 bool PackageCopy::GetFile(string
&File
,unsigned long &Size
)
458 File
= Section
->FindS("Filename");
459 Size
= Section
->FindI("Size");
460 if (File
.empty() || Size
== 0)
461 return _error
->Error("Cannot find filename or size tag");
465 // PackageCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
466 // ---------------------------------------------------------------------
468 bool PackageCopy::RewriteEntry(FILE *Target
,string File
)
470 TFRewriteData Changes
[] = {{"Filename",File
.c_str()},
473 if (TFRewrite(Target
,*Section
,TFRewritePackageOrder
,Changes
) == false)
479 // SourceCopy::GetFile - Get the file information from the section /*{{{*/
480 // ---------------------------------------------------------------------
482 bool SourceCopy::GetFile(string
&File
,unsigned long &Size
)
484 string Files
= Section
->FindS("Files");
485 if (Files
.empty() == true)
488 // Stash the / terminated directory prefix
489 string Base
= Section
->FindS("Directory");
490 if (Base
.empty() == false && Base
[Base
.length()-1] != '/')
493 // Read the first file triplet
494 const char *C
= Files
.c_str();
498 // Parse each of the elements
499 if (ParseQuoteWord(C
,MD5Hash
) == false ||
500 ParseQuoteWord(C
,sSize
) == false ||
501 ParseQuoteWord(C
,File
) == false)
502 return _error
->Error("Error parsing file record");
504 // Parse the size and append the directory
505 Size
= atoi(sSize
.c_str());
510 // SourceCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
511 // ---------------------------------------------------------------------
513 bool SourceCopy::RewriteEntry(FILE *Target
,string File
)
515 string
Dir(File
,0,File
.rfind('/'));
516 TFRewriteData Changes
[] = {{"Directory",Dir
.c_str()},
519 if (TFRewrite(Target
,*Section
,TFRewriteSourceOrder
,Changes
) == false)