]>
git.saurik.com Git - apt.git/blob - cmdline/indexcopy.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: indexcopy.cc,v 1.5 2000/05/10 06:02:26 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>
28 // IndexCopy::CopyPackages - Copy the package files from the CD /*{{{*/
29 // ---------------------------------------------------------------------
31 bool IndexCopy::CopyPackages(string CDROM
,string Name
,vector
<string
> &List
)
36 OpTextProgress Progress
;
38 bool NoStat
= _config
->FindB("APT::CDROM::Fast",false);
39 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
41 // Prepare the progress indicator
42 unsigned long TotalSize
= 0;
43 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
46 if (stat(string(*I
+ GetFileName()).c_str(),&Buf
) != 0 &&
47 stat(string(*I
+ GetFileName() + ".gz").c_str(),&Buf
) != 0)
48 return _error
->Errno("stat","Stat failed for %s",
49 string(*I
+ GetFileName()).c_str());
50 TotalSize
+= Buf
.st_size
;
53 unsigned long CurrentSize
= 0;
54 unsigned int NotFound
= 0;
55 unsigned int WrongSize
= 0;
56 unsigned int Packages
= 0;
57 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
59 string OrigPath
= string(*I
,CDROM
.length());
60 unsigned long FileSize
= 0;
62 // Open the package file
64 if (FileExists(*I
+ GetFileName()) == true)
66 Pkg
.Open(*I
+ GetFileName(),FileFd::ReadOnly
);
67 FileSize
= Pkg
.Size();
71 FileFd
From(*I
+ GetFileName() + ".gz",FileFd::ReadOnly
);
72 if (_error
->PendingError() == true)
74 FileSize
= From
.Size();
77 FILE *tmp
= tmpfile();
79 return _error
->Errno("tmpfile","Unable to create a tmp file");
80 Pkg
.Fd(dup(fileno(tmp
)));
86 return _error
->Errno("fork","Couldn't fork gzip");
91 dup2(From
.Fd(),STDIN_FILENO
);
92 dup2(Pkg
.Fd(),STDOUT_FILENO
);
93 SetCloseExec(STDIN_FILENO
,false);
94 SetCloseExec(STDOUT_FILENO
,false);
97 Args
[0] = _config
->Find("Dir::bin::gzip","gzip").c_str();
100 execvp(Args
[0],(char **)Args
);
104 // Wait for gzip to finish
105 if (ExecWait(Process
,_config
->Find("Dir::bin::gzip","gzip").c_str(),false) == false)
106 return _error
->Error("gzip failed, perhaps the disk is full.");
110 pkgTagFile
Parser(Pkg
);
111 if (_error
->PendingError() == true)
114 // Open the output file
116 sprintf(S
,"cdrom:[%s]/%s%s",Name
.c_str(),(*I
).c_str() + CDROM
.length(),
118 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
119 TargetF
+= URItoFileName(S
);
120 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
121 TargetF
= "/dev/null";
122 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
123 if (_error
->PendingError() == true)
126 // Setup the progress meter
127 Progress
.OverallProgress(CurrentSize
,TotalSize
,FileSize
,
128 string("Reading ") + Type() + " Indexes");
131 Progress
.SubProgress(Pkg
.Size());
132 pkgTagSection Section
;
133 this->Section
= &Section
;
135 unsigned long Hits
= 0;
136 unsigned long Chop
= 0;
137 while (Parser
.Step(Section
) == true)
139 Progress
.Progress(Parser
.Offset());
142 if (GetFile(File
,Size
) == false)
146 File
= OrigPath
+ ChopDirs(File
,Chop
);
148 // See if the file exists
149 bool Mangled
= false;
150 if (NoStat
== false || Hits
< 10)
152 // Attempt to fix broken structure
155 if (ReconstructPrefix(Prefix
,OrigPath
,CDROM
,File
) == false &&
156 ReconstructChop(Chop
,*I
,File
) == false)
159 clog
<< "Missed: " << File
<< endl
;
164 File
= OrigPath
+ ChopDirs(File
,Chop
);
169 if (stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 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
)
178 File
.replace(Start
,End
-Start
,"binary-all");
182 if (Mangled
== false ||
183 stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0)
186 clog
<< "Missed(2): " << OrigFile
<< endl
;
193 if ((unsigned)Buf
.st_size
!= Size
)
196 clog
<< "Wrong Size: " << File
<< endl
;
205 // Copy it to the target package file
206 if (Chop
!= 0 || Mangled
== true)
208 if (RewriteEntry(Target
,File
) == false)
215 Section
.GetSection(Start
,Stop
);
216 if (Target
.Write(Start
,Stop
-Start
) == false)
222 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << Chop
<< endl
;
224 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
226 // Move out of the partial directory
228 string FinalF
= _config
->FindDir("Dir::State::lists");
229 FinalF
+= URItoFileName(S
);
230 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
231 return _error
->Errno("rename","Failed to rename");
233 // Copy the release file
234 sprintf(S
,"cdrom:[%s]/%sRelease",Name
.c_str(),(*I
).c_str() + CDROM
.length());
235 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
236 TargetF
+= URItoFileName(S
);
237 if (FileExists(*I
+ "Release") == true)
239 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
240 FileFd
Rel(*I
+ "Release",FileFd::ReadOnly
);
241 if (_error
->PendingError() == true)
244 if (CopyFile(Rel
,Target
) == false)
249 // Empty release file
250 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
253 // Rename the release file
254 FinalF
= _config
->FindDir("Dir::State::lists");
255 FinalF
+= URItoFileName(S
);
256 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
257 return _error
->Errno("rename","Failed to rename");
260 /* Mangle the source to be in the proper notation with
261 prefix dist [component] */
262 *I
= string(*I
,Prefix
.length());
263 ConvertToSourceList(CDROM
,*I
);
264 *I
= Prefix
+ ' ' + *I
;
266 CurrentSize
+= FileSize
;
271 cout
<< "Wrote " << Packages
<< " records" ;
273 cout
<< " with " << NotFound
<< " missing files";
274 if (NotFound
!= 0 && WrongSize
!= 0)
277 cout
<< " with " << WrongSize
<< " mismatched files";
281 return _error
->Warning("No valid records were found.");
283 if (NotFound
+ WrongSize
> 10)
284 cout
<< "Alot of entries were discarded, something may be wrong." << endl
;
289 // IndexCopy::ChopDirs - Chop off the leading directory components /*{{{*/
290 // ---------------------------------------------------------------------
292 string
IndexCopy::ChopDirs(string Path
,unsigned int Depth
)
294 string::size_type I
= 0;
297 I
= Path
.find('/',I
+1);
300 while (I
!= string::npos
&& Depth
!= 0);
302 if (I
== string::npos
)
305 return string(Path
,I
+1);
308 // IndexCopy::ReconstructPrefix - Fix strange prefixing /*{{{*/
309 // ---------------------------------------------------------------------
310 /* This prepends dir components from the path to the package files to
311 the path to the deb until it is found */
312 bool IndexCopy::ReconstructPrefix(string
&Prefix
,string OrigPath
,string CD
,
315 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
316 unsigned int Depth
= 1;
317 string MyPrefix
= Prefix
;
321 if (stat(string(CD
+ MyPrefix
+ File
).c_str(),&Buf
) != 0)
324 cout
<< "Failed, " << CD
+ MyPrefix
+ File
<< endl
;
325 if (GrabFirst(OrigPath
,MyPrefix
,Depth
++) == true)
339 // IndexCopy::ReconstructChop - Fixes bad source paths /*{{{*/
340 // ---------------------------------------------------------------------
341 /* This removes path components from the filename and prepends the location
342 of the package files until a file is found */
343 bool IndexCopy::ReconstructChop(unsigned long &Chop
,string Dir
,string File
)
345 // Attempt to reconstruct the filename
346 unsigned long Depth
= 0;
350 if (stat(string(Dir
+ File
).c_str(),&Buf
) != 0)
352 File
= ChopDirs(File
,1);
354 if (File
.empty() == false)
367 // IndexCopy::ConvertToSourceList - Convert a Path to a sourcelist /*{{{*/
368 // ---------------------------------------------------------------------
369 /* We look for things in dists/ notation and convert them to
370 <dist> <component> form otherwise it is left alone. This also strips
373 This implements a regex sort of like:
374 (.*)/dists/([^/]*)/(.*)/binary-*
376 | |-------- Distribution
377 |------------------- Path
379 It was deciced to use only a single word for dist (rather than say
380 unstable/non-us) to increase the chance that each CD gets a single
381 line in sources.list.
383 void IndexCopy::ConvertToSourceList(string CD
,string
&Path
)
386 sprintf(S
,"binary-%s",_config
->Find("Apt::Architecture").c_str());
388 // Strip the cdrom base path
389 Path
= string(Path
,CD
.length());
390 if (Path
.empty() == true)
393 // Too short to be a dists/ type
394 if (Path
.length() < strlen("dists/"))
398 if (stringcmp(Path
.begin(),Path
.begin()+strlen("dists/"),"dists/") != 0)
402 string::size_type Slash
= strlen("dists/");
403 string::size_type Slash2
= Path
.find('/',Slash
+ 1);
404 if (Slash2
== string::npos
|| Slash2
+ 2 >= Path
.length())
406 string Dist
= string(Path
,Slash
,Slash2
- Slash
);
408 // Isolate the component
410 for (unsigned I
= 0; I
!= 10; I
++)
412 Slash
= Path
.find('/',Slash
+1);
413 if (Slash
== string::npos
|| Slash
+ 2 >= Path
.length())
415 string Comp
= string(Path
,Slash2
+1,Slash
- Slash2
-1);
417 // Verify the trailing binary- bit
418 string::size_type BinSlash
= Path
.find('/',Slash
+ 1);
419 if (Slash
== string::npos
)
421 string Binary
= string(Path
,Slash
+1,BinSlash
- Slash
-1);
423 if (Binary
!= S
&& Binary
!= "source")
426 Path
= Dist
+ ' ' + Comp
;
431 // IndexCopy::GrabFirst - Return the first Depth path components /*{{{*/
432 // ---------------------------------------------------------------------
434 bool IndexCopy::GrabFirst(string Path
,string
&To
,unsigned int Depth
)
436 string::size_type I
= 0;
439 I
= Path
.find('/',I
+1);
442 while (I
!= string::npos
&& Depth
!= 0);
444 if (I
== string::npos
)
447 To
= string(Path
,0,I
+1);
451 // IndexCopy::CopyWithReplace - Copy a section and replace text /*{{{*/
452 // ---------------------------------------------------------------------
454 bool IndexCopy::CopyWithReplace(FileFd
&Target
,const char *Tag
,string New
)
456 // Mangle the output filename
459 const char *Filename
;
460 Section
->Find(Tag
,Filename
,Stop
);
462 /* We need to rewrite the filename field so we emit
463 all fields except the filename file and rewrite that one */
464 for (unsigned int I
= 0; I
!= Section
->Count(); I
++)
466 Section
->Get(Start
,Stop
,I
);
467 if (Start
<= Filename
&& Stop
> Filename
)
470 sprintf(S
,"%s: %s\n",Tag
,New
.c_str());
471 if (I
+ 1 == Section
->Count())
473 if (Target
.Write(S
,strlen(S
)) == false)
478 if (Target
.Write(Start
,Stop
-Start
) == false)
480 if (Stop
[-1] != '\n')
481 if (Target
.Write("\n",1) == false)
485 if (Target
.Write("\n",1) == false)
489 // PackageCopy::GetFile - Get the file information from the section /*{{{*/
490 // ---------------------------------------------------------------------
492 bool PackageCopy::GetFile(string
&File
,unsigned long &Size
)
494 File
= Section
->FindS("Filename");
495 Size
= Section
->FindI("Size");
496 if (File
.empty() || Size
== 0)
497 return _error
->Error("Cannot find filename or size tag");
501 // PackageCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
502 // ---------------------------------------------------------------------
504 bool PackageCopy::RewriteEntry(FileFd
&Target
,string File
)
506 return CopyWithReplace(Target
,"Filename",File
);
509 // SourceCopy::GetFile - Get the file information from the section /*{{{*/
510 // ---------------------------------------------------------------------
512 bool SourceCopy::GetFile(string
&File
,unsigned long &Size
)
514 string Files
= Section
->FindS("Files");
515 if (Files
.empty() == true)
518 // Stash the / terminated directory prefix
519 string Base
= Section
->FindS("Directory");
520 if (Base
.empty() == false && Base
[Base
.length()-1] != '/')
523 // Iterate over the entire list grabbing each triplet
524 const char *C
= Files
.c_str();
528 // Parse each of the elements
529 if (ParseQuoteWord(C
,MD5Hash
) == false ||
530 ParseQuoteWord(C
,sSize
) == false ||
531 ParseQuoteWord(C
,File
) == false)
532 return _error
->Error("Error parsing file record");
534 // Parse the size and append the directory
535 Size
= atoi(sSize
.c_str());
540 // SourceCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
541 // ---------------------------------------------------------------------
543 bool SourceCopy::RewriteEntry(FileFd
&Target
,string File
)
545 return CopyWithReplace(Target
,"Directory",
546 string(File
,0,File
.rfind('/')));