]>
git.saurik.com Git - apt.git/blob - apt-pkg/cdrom.cc
2416887859a6af0a58afdcfb9eb069a62672da6b
5 #pragma implementation "apt-pkg/cdrom.h"
7 #include<apt-pkg/init.h>
8 #include<apt-pkg/error.h>
9 #include<apt-pkg/cdromutl.h>
10 #include<apt-pkg/strutl.h>
11 #include<apt-pkg/cdrom.h>
23 #include "indexcopy.h"
27 // FindPackages - Find the package files on the CDROM /*{{{*/
28 // ---------------------------------------------------------------------
29 /* We look over the cdrom for package files. This is a recursive
30 search that short circuits when it his a package file in the dir.
31 This speeds it up greatly as the majority of the size is in the
33 bool pkgCdrom::FindPackages(string CD
,vector
<string
> &List
,
34 vector
<string
> &SList
, vector
<string
> &SigList
,
35 string
&InfoDir
, pkgCdromStatus
*log
,
38 static ino_t Inodes
[9];
40 // if we have a look we "pulse" now
47 if (CD
[CD
.length()-1] != '/')
50 if (chdir(CD
.c_str()) != 0)
51 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
53 // Look for a .disk subdirectory
55 if (stat(".disk",&Buf
) == 0)
57 if (InfoDir
.empty() == true)
58 InfoDir
= CD
+ ".disk/";
61 // Don't look into directories that have been marked to ingore.
62 if (stat(".aptignr",&Buf
) == 0)
66 /* Check _first_ for a signature file as apt-cdrom assumes that all files
67 under a Packages/Source file are in control of that file and stops
70 if (stat("Release.gpg",&Buf
) == 0)
72 SigList
.push_back(CD
);
74 /* Aha! We found some package files. We assume that everything under
75 this dir is controlled by those package files so we don't look down
77 if (stat("Packages",&Buf
) == 0 || stat("Packages.gz",&Buf
) == 0)
81 // Continue down if thorough is given
82 if (_config
->FindB("APT::CDROM::Thorough",false) == false)
85 if (stat("Sources.gz",&Buf
) == 0 || stat("Sources",&Buf
) == 0)
89 // Continue down if thorough is given
90 if (_config
->FindB("APT::CDROM::Thorough",false) == false)
94 DIR *D
= opendir(".");
96 return _error
->Errno("opendir","Unable to read %s",CD
.c_str());
98 // Run over the directory
99 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
102 if (strcmp(Dir
->d_name
,".") == 0 ||
103 strcmp(Dir
->d_name
,"..") == 0 ||
104 //strcmp(Dir->d_name,"source") == 0 ||
105 strcmp(Dir
->d_name
,".disk") == 0 ||
106 strcmp(Dir
->d_name
,"experimental") == 0 ||
107 strcmp(Dir
->d_name
,"binary-all") == 0 ||
108 strcmp(Dir
->d_name
,"debian-installer") == 0)
111 // See if the name is a sub directory
113 if (stat(Dir
->d_name
,&Buf
) != 0)
116 if (S_ISDIR(Buf
.st_mode
) == 0)
120 for (I
= 0; I
!= Depth
; I
++)
121 if (Inodes
[I
] == Buf
.st_ino
)
126 // Store the inodes weve seen
127 Inodes
[Depth
] = Buf
.st_ino
;
130 if (FindPackages(CD
+ Dir
->d_name
,List
,SList
,SigList
,InfoDir
,log
,Depth
+1) == false)
133 if (chdir(CD
.c_str()) != 0)
134 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
139 return !_error
->PendingError();
142 // Score - We compute a 'score' for a path /*{{{*/
143 // ---------------------------------------------------------------------
144 /* Paths are scored based on how close they come to what I consider
145 normal. That is ones that have 'dist' 'stable' 'testing' will score
146 higher than ones without. */
147 int pkgCdrom::Score(string Path
)
150 if (Path
.find("stable/") != string::npos
)
152 if (Path
.find("/binary-") != string::npos
)
154 if (Path
.find("testing/") != string::npos
)
156 if (Path
.find("unstable/") != string::npos
)
158 if (Path
.find("/dists/") != string::npos
)
160 if (Path
.find("/main/") != string::npos
)
162 if (Path
.find("/contrib/") != string::npos
)
164 if (Path
.find("/non-free/") != string::npos
)
166 if (Path
.find("/non-US/") != string::npos
)
168 if (Path
.find("/source/") != string::npos
)
170 if (Path
.find("/debian/") != string::npos
)
173 // check for symlinks in the patch leading to the actual file
174 // a symlink gets a big penalty
176 string statPath
= flNotFile(Path
);
177 string cdromPath
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
178 while(statPath
!= cdromPath
&& statPath
!= "./") {
179 statPath
.resize(statPath
.size()-1); // remove the trailing '/'
180 if (lstat(statPath
.c_str(),&Buf
) == 0) {
181 if(S_ISLNK(Buf
.st_mode
)) {
186 statPath
= flNotFile(statPath
); // descent
193 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
194 // ---------------------------------------------------------------------
195 /* Here we drop everything that is not this machines arch */
196 bool pkgCdrom::DropBinaryArch(vector
<string
> &List
)
199 snprintf(S
,sizeof(S
),"/binary-%s/",
200 _config
->Find("Apt::Architecture").c_str());
202 for (unsigned int I
= 0; I
< List
.size(); I
++)
204 const char *Str
= List
[I
].c_str();
207 if ((Res
= strstr(Str
,"/binary-")) == 0)
211 if (strlen(Res
) < strlen(S
))
213 List
.erase(List
.begin() + I
);
218 // See if it is our arch
219 if (stringcmp(Res
,Res
+ strlen(S
),S
) == 0)
223 List
.erase(List
.begin() + I
);
231 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
232 // ---------------------------------------------------------------------
233 /* Here we go and stat every file that we found and strip dup inodes. */
234 bool pkgCdrom::DropRepeats(vector
<string
> &List
,const char *Name
)
236 // Get a list of all the inodes
237 ino_t
*Inodes
= new ino_t
[List
.size()];
238 for (unsigned int I
= 0; I
!= List
.size(); I
++)
241 if (stat((List
[I
] + Name
).c_str(),&Buf
) != 0 &&
242 stat((List
[I
] + Name
+ ".gz").c_str(),&Buf
) != 0)
243 _error
->Errno("stat","Failed to stat %s%s",List
[I
].c_str(),
245 Inodes
[I
] = Buf
.st_ino
;
248 if (_error
->PendingError() == true)
252 for (unsigned int I
= 0; I
!= List
.size(); I
++)
254 for (unsigned int J
= I
+1; J
< List
.size(); J
++)
257 if (Inodes
[J
] != Inodes
[I
])
260 // We score the two paths.. and erase one
261 int ScoreA
= Score(List
[I
]);
262 int ScoreB
= Score(List
[J
]);
273 // Wipe erased entries
274 for (unsigned int I
= 0; I
< List
.size();)
276 if (List
[I
].empty() == false)
279 List
.erase(List
.begin()+I
);
286 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
287 // ---------------------------------------------------------------------
288 /* This takes the list of source list expressed entires and collects
289 similar ones to form a single entry for each dist */
290 void pkgCdrom::ReduceSourcelist(string CD
,vector
<string
> &List
)
292 sort(List
.begin(),List
.end());
294 // Collect similar entries
295 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
298 string::size_type Space
= (*I
).find(' ');
299 if (Space
== string::npos
)
301 string::size_type SSpace
= (*I
).find(' ',Space
+ 1);
302 if (SSpace
== string::npos
)
305 string Word1
= string(*I
,Space
,SSpace
-Space
);
306 string Prefix
= string(*I
,0,Space
);
307 for (vector
<string
>::iterator J
= List
.begin(); J
!= I
; J
++)
310 string::size_type Space2
= (*J
).find(' ');
311 if (Space2
== string::npos
)
313 string::size_type SSpace2
= (*J
).find(' ',Space2
+ 1);
314 if (SSpace2
== string::npos
)
317 if (string(*J
,0,Space2
) != Prefix
)
319 if (string(*J
,Space2
,SSpace2
-Space2
) != Word1
)
322 *J
+= string(*I
,SSpace
);
327 // Wipe erased entries
328 for (unsigned int I
= 0; I
< List
.size();)
330 if (List
[I
].empty() == false)
333 List
.erase(List
.begin()+I
);
337 // WriteDatabase - Write the CDROM Database file /*{{{*/
338 // ---------------------------------------------------------------------
339 /* We rewrite the configuration class associated with the cdrom database. */
340 bool pkgCdrom::WriteDatabase(Configuration
&Cnf
)
342 string DFile
= _config
->FindFile("Dir::State::cdroms");
343 string NewFile
= DFile
+ ".new";
345 unlink(NewFile
.c_str());
346 ofstream
Out(NewFile
.c_str());
348 return _error
->Errno("ofstream::ofstream",
349 "Failed to open %s.new",DFile
.c_str());
351 /* Write out all of the configuration directives by walking the
352 configuration tree */
353 const Configuration::Item
*Top
= Cnf
.Tree(0);
356 // Print the config entry
357 if (Top
->Value
.empty() == false)
358 Out
<< Top
->FullTag() + " \"" << Top
->Value
<< "\";" << endl
;
366 while (Top
!= 0 && Top
->Next
== 0)
374 rename(DFile
.c_str(),string(DFile
+ '~').c_str());
375 if (rename(NewFile
.c_str(),DFile
.c_str()) != 0)
376 return _error
->Errno("rename","Failed to rename %s.new to %s",
377 DFile
.c_str(),DFile
.c_str());
382 // WriteSourceList - Write an updated sourcelist /*{{{*/
383 // ---------------------------------------------------------------------
384 /* This reads the old source list and copies it into the new one. It
385 appends the new CDROM entires just after the first block of comments.
386 This places them first in the file. It also removes any old entries
387 that were the same. */
388 bool pkgCdrom::WriteSourceList(string Name
,vector
<string
> &List
,bool Source
)
390 if (List
.size() == 0)
393 string File
= _config
->FindFile("Dir::Etc::sourcelist");
395 // Open the stream for reading
396 ifstream
F((FileExists(File
)?File
.c_str():"/dev/null"),
399 return _error
->Errno("ifstream::ifstream","Opening %s",File
.c_str());
401 string NewFile
= File
+ ".new";
402 unlink(NewFile
.c_str());
403 ofstream
Out(NewFile
.c_str());
405 return _error
->Errno("ofstream::ofstream",
406 "Failed to open %s.new",File
.c_str());
408 // Create a short uri without the path
409 string ShortURI
= "cdrom:[" + Name
+ "]/";
410 string ShortURI2
= "cdrom:" + Name
+ "/"; // For Compatibility
421 while (F
.eof() == false)
423 F
.getline(Buffer
,sizeof(Buffer
));
425 if (F
.fail() && !F
.eof())
426 return _error
->Error(_("Line %u too long in source list %s."),
427 CurLine
,File
.c_str());
428 _strtabexpand(Buffer
,sizeof(Buffer
));
432 if (Buffer
[0] == '#' || Buffer
[0] == 0)
434 Out
<< Buffer
<< endl
;
440 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
442 string::size_type Space
= (*I
).find(' ');
443 if (Space
== string::npos
)
444 return _error
->Error("Internal error");
445 Out
<< Type
<< " cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
446 " " << string(*I
,Space
+1) << endl
;
454 const char *C
= Buffer
;
455 if (ParseQuoteWord(C
,cType
) == false ||
456 ParseQuoteWord(C
,URI
) == false)
458 Out
<< Buffer
<< endl
;
462 // Emit lines like this one
463 if (cType
!= Type
|| (string(URI
,0,ShortURI
.length()) != ShortURI
&&
464 string(URI
,0,ShortURI
.length()) != ShortURI2
))
466 Out
<< Buffer
<< endl
;
471 // Just in case the file was empty
474 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
476 string::size_type Space
= (*I
).find(' ');
477 if (Space
== string::npos
)
478 return _error
->Error("Internal error");
480 Out
<< "deb cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
481 " " << string(*I
,Space
+1) << endl
;
487 rename(File
.c_str(),string(File
+ '~').c_str());
488 if (rename(NewFile
.c_str(),File
.c_str()) != 0)
489 return _error
->Errno("rename","Failed to rename %s.new to %s",
490 File
.c_str(),File
.c_str());
496 bool pkgCdrom::Ident(string
&ident
, pkgCdromStatus
*log
)
501 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
503 CDROM
= SafeGetCWD() + '/' + CDROM
;
507 ioprintf(msg
, _("Using CD-ROM mount point %s\nMounting CD-ROM\n"),
509 log
->Update(msg
.str());
512 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
513 if (MountCdrom(CDROM
) == false)
514 return _error
->Error("Failed to mount the cdrom.");
516 // Hash the CD to get an ID
518 log
->Update(_("Identifying.. "));
521 if (IdentCdrom(CDROM
,ident
) == false)
528 ioprintf(msg
, "[%s]\n",ident
.c_str());
529 log
->Update(msg
.str());
533 Configuration Database
;
534 string DFile
= _config
->FindFile("Dir::State::cdroms");
535 if (FileExists(DFile
) == true)
537 if (ReadConfigFile(Database
,DFile
) == false)
538 return _error
->Error("Unable to read the cdrom database %s",
543 ioprintf(msg
, _("Stored label: %s \n"),
544 Database
.Find("CD::"+ident
).c_str());
545 log
->Update(msg
.str());
551 bool pkgCdrom::Add(pkgCdromStatus
*log
)
556 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
558 CDROM
= SafeGetCWD() + '/' + CDROM
;
561 log
->SetTotal(STEP_LAST
);
563 ioprintf(msg
, _("Using CD-ROM mount point %s\n"), CDROM
.c_str());
564 log
->Update(msg
.str(), STEP_PREPARE
);
568 Configuration Database
;
569 string DFile
= _config
->FindFile("Dir::State::cdroms");
570 if (FileExists(DFile
) == true)
572 if (ReadConfigFile(Database
,DFile
) == false)
573 return _error
->Error("Unable to read the cdrom database %s",
577 // Unmount the CD and get the user to put in the one they want
578 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
581 log
->Update(_("Unmounting CD-ROM\n"), STEP_UNMOUNT
);
585 log
->Update(_("Waiting for disc...\n"), STEP_WAIT
);
586 if(!log
->ChangeCdrom()) {
592 // Mount the new CDROM
593 log
->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT
);
594 if (MountCdrom(CDROM
) == false)
595 return _error
->Error("Failed to mount the cdrom.");
598 // Hash the CD to get an ID
600 log
->Update(_("Identifying.. "), STEP_IDENT
);
602 if (IdentCdrom(CDROM
,ID
) == false)
608 log
->Update("["+ID
+"]\n");
611 log
->Update(_("Scanning disc for index files..\n"),STEP_SCAN
);
613 // Get the CD structure
615 vector
<string
> SourceList
;
616 vector
<string
> SigList
;
617 string StartDir
= SafeGetCWD();
619 if (FindPackages(CDROM
,List
,SourceList
, SigList
,InfoDir
,log
) == false)
625 chdir(StartDir
.c_str());
627 if (_config
->FindB("Debug::aptcdrom",false) == true)
629 cout
<< "I found (binary):" << endl
;
630 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
632 cout
<< "I found (source):" << endl
;
633 for (vector
<string
>::iterator I
= SourceList
.begin(); I
!= SourceList
.end(); I
++)
635 cout
<< "I found (Signatures):" << endl
;
636 for (vector
<string
>::iterator I
= SigList
.begin(); I
!= SigList
.end(); I
++)
640 //log->Update(_("Cleaning package lists..."), STEP_CLEAN);
643 DropBinaryArch(List
);
644 DropRepeats(List
,"Packages");
645 DropRepeats(SourceList
,"Sources");
646 DropRepeats(SigList
,"Release.gpg");
649 ioprintf(msg
, _("Found %i package indexes, %i source indexes and "
651 List
.size(), SourceList
.size(), SigList
.size());
652 log
->Update(msg
.str(), STEP_SCAN
);
655 if (List
.size() == 0 && SourceList
.size() == 0)
657 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
659 return _error
->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
662 // Check if the CD is in the database
664 if (Database
.Exists("CD::" + ID
) == false ||
665 _config
->FindB("APT::CDROM::Rename",false) == true)
667 // Try to use the CDs label if at all possible
668 if (InfoDir
.empty() == false &&
669 FileExists(InfoDir
+ "/info") == true)
671 ifstream
F(string(InfoDir
+ "/info").c_str());
675 if (Name
.empty() == false)
677 // Escape special characters
678 string::iterator J
= Name
.begin();
679 for (; J
!= Name
.end(); J
++)
680 if (*J
== '"' || *J
== ']' || *J
== '[')
685 ioprintf(msg
, "Found label '%s'\n", Name
.c_str());
686 log
->Update(msg
.str());
688 Database
.Set("CD::" + ID
+ "::Label",Name
);
692 if (_config
->FindB("APT::CDROM::Rename",false) == true ||
693 Name
.empty() == true)
697 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
699 return _error
->Error("No disc name found and no way to ask for it");
703 if(!log
->AskCdromName(Name
)) {
707 cout
<< "Name: '" << Name
<< "'" << endl
;
709 if (Name
.empty() == false &&
710 Name
.find('"') == string::npos
&&
711 Name
.find('[') == string::npos
&&
712 Name
.find(']') == string::npos
)
714 log
->Update(_("That is not a valid name, try again.\n"));
719 Name
= Database
.Find("CD::" + ID
);
721 // Escape special characters
722 string::iterator J
= Name
.begin();
723 for (; J
!= Name
.end(); J
++)
724 if (*J
== '"' || *J
== ']' || *J
== '[')
727 Database
.Set("CD::" + ID
,Name
);
730 ioprintf(msg
, _("This disc is called: \n'%s'\n"), Name
.c_str());
731 log
->Update(msg
.str());
734 log
->Update(_("Copying package lists..."), STEP_COPY
);
735 // take care of the signatures and copy them if they are ok
736 // (we do this before PackageCopy as it modifies "List" and "SourceList")
737 SigVerify SignVerify
;
738 SignVerify
.CopyAndVerify(CDROM
, Name
, SigList
, List
, SourceList
);
740 // Copy the package files to the state directory
743 if (Copy
.CopyPackages(CDROM
,Name
,List
, log
) == false ||
744 SrcCopy
.CopyPackages(CDROM
,Name
,SourceList
, log
) == false)
747 // reduce the List so that it takes less space in sources.list
748 ReduceSourcelist(CDROM
,List
);
749 ReduceSourcelist(CDROM
,SourceList
);
751 // Write the database and sourcelist
752 if (_config
->FindB("APT::cdrom::NoAct",false) == false)
754 if (WriteDatabase(Database
) == false)
758 log
->Update(_("Writing new source list\n"), STEP_WRITE
);
760 if (WriteSourceList(Name
,List
,false) == false ||
761 WriteSourceList(Name
,SourceList
,true) == false)
765 // Print the sourcelist entries
767 log
->Update(_("Source list entries for this disc are:\n"));
769 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
771 string::size_type Space
= (*I
).find(' ');
772 if (Space
== string::npos
)
774 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
776 return _error
->Error("Internal error");
781 msg
<< "deb cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
782 " " << string(*I
,Space
+1) << endl
;
783 log
->Update(msg
.str());
787 for (vector
<string
>::iterator I
= SourceList
.begin(); I
!= SourceList
.end(); I
++)
789 string::size_type Space
= (*I
).find(' ');
790 if (Space
== string::npos
)
792 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
794 return _error
->Error("Internal error");
799 msg
<< "deb-src cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
800 " " << string(*I
,Space
+1) << endl
;
801 log
->Update(msg
.str());
807 // Unmount and finish
808 if (_config
->FindB("APT::CDROM::NoMount",false) == false &&
809 _config
->FindB("APT::CDROM::UnMountOnFinish",true) == true) {
810 log
->Update(_("Unmounting CD-ROM..."), STEP_LAST
);