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>
24 #include "indexcopy.h"
28 // FindPackages - Find the package files on the CDROM /*{{{*/
29 // ---------------------------------------------------------------------
30 /* We look over the cdrom for package files. This is a recursive
31 search that short circuits when it his a package file in the dir.
32 This speeds it up greatly as the majority of the size is in the
34 bool pkgCdrom::FindPackages(string CD
,
36 vector
<string
> &SList
,
37 vector
<string
> &SigList
,
38 vector
<string
> &TransList
,
39 string
&InfoDir
, pkgCdromStatus
*log
,
42 static ino_t Inodes
[9];
45 // if we have a look we "pulse" now
52 if (CD
[CD
.length()-1] != '/')
55 if (chdir(CD
.c_str()) != 0)
56 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
58 // Look for a .disk subdirectory
60 if (stat(".disk",&Buf
) == 0)
62 if (InfoDir
.empty() == true)
63 InfoDir
= CD
+ ".disk/";
66 // Don't look into directories that have been marked to ingore.
67 if (stat(".aptignr",&Buf
) == 0)
71 /* Check _first_ for a signature file as apt-cdrom assumes that all files
72 under a Packages/Source file are in control of that file and stops
75 if (stat("Release.gpg",&Buf
) == 0)
77 SigList
.push_back(CD
);
79 /* Aha! We found some package files. We assume that everything under
80 this dir is controlled by those package files so we don't look down
82 if (stat("Packages",&Buf
) == 0 || stat("Packages.gz",&Buf
) == 0)
86 // Continue down if thorough is given
87 if (_config
->FindB("APT::CDROM::Thorough",false) == false)
90 if (stat("Sources.gz",&Buf
) == 0 || stat("Sources",&Buf
) == 0)
94 // Continue down if thorough is given
95 if (_config
->FindB("APT::CDROM::Thorough",false) == false)
99 // see if we find translatin indexes
100 if (stat("i18n",&Buf
) == 0)
103 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
105 if(strstr(Dir
->d_name
,"Translation") != NULL
)
107 if (_config
->FindB("Debug::aptcdrom",false) == true)
108 std::clog
<< "found translations: " << Dir
->d_name
<< "\n";
109 string file
= Dir
->d_name
;
110 if(file
.substr(file
.size()-3,file
.size()) == ".gz")
111 file
= file
.substr(0,file
.size()-3);
112 TransList
.push_back(CD
+"i18n/"+ file
);
121 return _error
->Errno("opendir","Unable to read %s",CD
.c_str());
123 // Run over the directory
124 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
127 if (strcmp(Dir
->d_name
,".") == 0 ||
128 strcmp(Dir
->d_name
,"..") == 0 ||
129 //strcmp(Dir->d_name,"source") == 0 ||
130 strcmp(Dir
->d_name
,".disk") == 0 ||
131 strcmp(Dir
->d_name
,"experimental") == 0 ||
132 strcmp(Dir
->d_name
,"binary-all") == 0 ||
133 strcmp(Dir
->d_name
,"debian-installer") == 0)
136 // See if the name is a sub directory
138 if (stat(Dir
->d_name
,&Buf
) != 0)
141 if (S_ISDIR(Buf
.st_mode
) == 0)
145 for (I
= 0; I
!= Depth
; I
++)
146 if (Inodes
[I
] == Buf
.st_ino
)
151 // Store the inodes weve seen
152 Inodes
[Depth
] = Buf
.st_ino
;
155 if (FindPackages(CD
+ Dir
->d_name
,List
,SList
,SigList
,TransList
,InfoDir
,log
,Depth
+1) == false)
158 if (chdir(CD
.c_str()) != 0)
159 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
164 return !_error
->PendingError();
167 // Score - We compute a 'score' for a path /*{{{*/
168 // ---------------------------------------------------------------------
169 /* Paths are scored based on how close they come to what I consider
170 normal. That is ones that have 'dist' 'stable' 'testing' will score
171 higher than ones without. */
172 int pkgCdrom::Score(string Path
)
175 if (Path
.find("stable/") != string::npos
)
177 if (Path
.find("/binary-") != string::npos
)
179 if (Path
.find("testing/") != string::npos
)
181 if (Path
.find("unstable/") != string::npos
)
183 if (Path
.find("/dists/") != string::npos
)
185 if (Path
.find("/main/") != string::npos
)
187 if (Path
.find("/contrib/") != string::npos
)
189 if (Path
.find("/non-free/") != string::npos
)
191 if (Path
.find("/non-US/") != string::npos
)
193 if (Path
.find("/source/") != string::npos
)
195 if (Path
.find("/debian/") != string::npos
)
198 // check for symlinks in the patch leading to the actual file
199 // a symlink gets a big penalty
201 string statPath
= flNotFile(Path
);
202 string cdromPath
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
203 while(statPath
!= cdromPath
&& statPath
!= "./") {
204 statPath
.resize(statPath
.size()-1); // remove the trailing '/'
205 if (lstat(statPath
.c_str(),&Buf
) == 0) {
206 if(S_ISLNK(Buf
.st_mode
)) {
211 statPath
= flNotFile(statPath
); // descent
218 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
219 // ---------------------------------------------------------------------
220 /* Here we drop everything that is not this machines arch */
221 bool pkgCdrom::DropBinaryArch(vector
<string
> &List
)
224 snprintf(S
,sizeof(S
),"/binary-%s/",
225 _config
->Find("Apt::Architecture").c_str());
227 for (unsigned int I
= 0; I
< List
.size(); I
++)
229 const char *Str
= List
[I
].c_str();
232 if ((Res
= strstr(Str
,"/binary-")) == 0)
236 if (strlen(Res
) < strlen(S
))
238 List
.erase(List
.begin() + I
);
243 // See if it is our arch
244 if (stringcmp(Res
,Res
+ strlen(S
),S
) == 0)
248 List
.erase(List
.begin() + I
);
256 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
257 // ---------------------------------------------------------------------
258 /* Here we go and stat every file that we found and strip dup inodes. */
259 bool pkgCdrom::DropRepeats(vector
<string
> &List
,const char *Name
)
261 // Get a list of all the inodes
262 ino_t
*Inodes
= new ino_t
[List
.size()];
263 for (unsigned int I
= 0; I
!= List
.size(); I
++)
266 if (stat((List
[I
] + Name
).c_str(),&Buf
) != 0 &&
267 stat((List
[I
] + Name
+ ".gz").c_str(),&Buf
) != 0)
268 _error
->Errno("stat","Failed to stat %s%s",List
[I
].c_str(),
270 Inodes
[I
] = Buf
.st_ino
;
273 if (_error
->PendingError() == true)
277 for (unsigned int I
= 0; I
!= List
.size(); I
++)
279 for (unsigned int J
= I
+1; J
< List
.size(); J
++)
282 if (Inodes
[J
] != Inodes
[I
])
285 // We score the two paths.. and erase one
286 int ScoreA
= Score(List
[I
]);
287 int ScoreB
= Score(List
[J
]);
298 // Wipe erased entries
299 for (unsigned int I
= 0; I
< List
.size();)
301 if (List
[I
].empty() == false)
304 List
.erase(List
.begin()+I
);
311 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
312 // ---------------------------------------------------------------------
313 /* This takes the list of source list expressed entires and collects
314 similar ones to form a single entry for each dist */
315 void pkgCdrom::ReduceSourcelist(string CD
,vector
<string
> &List
)
317 sort(List
.begin(),List
.end());
319 // Collect similar entries
320 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
323 string::size_type Space
= (*I
).find(' ');
324 if (Space
== string::npos
)
326 string::size_type SSpace
= (*I
).find(' ',Space
+ 1);
327 if (SSpace
== string::npos
)
330 string Word1
= string(*I
,Space
,SSpace
-Space
);
331 string Prefix
= string(*I
,0,Space
);
332 for (vector
<string
>::iterator J
= List
.begin(); J
!= I
; J
++)
335 string::size_type Space2
= (*J
).find(' ');
336 if (Space2
== string::npos
)
338 string::size_type SSpace2
= (*J
).find(' ',Space2
+ 1);
339 if (SSpace2
== string::npos
)
342 if (string(*J
,0,Space2
) != Prefix
)
344 if (string(*J
,Space2
,SSpace2
-Space2
) != Word1
)
347 *J
+= string(*I
,SSpace
);
352 // Wipe erased entries
353 for (unsigned int I
= 0; I
< List
.size();)
355 if (List
[I
].empty() == false)
358 List
.erase(List
.begin()+I
);
362 // WriteDatabase - Write the CDROM Database file /*{{{*/
363 // ---------------------------------------------------------------------
364 /* We rewrite the configuration class associated with the cdrom database. */
365 bool pkgCdrom::WriteDatabase(Configuration
&Cnf
)
367 string DFile
= _config
->FindFile("Dir::State::cdroms");
368 string NewFile
= DFile
+ ".new";
370 unlink(NewFile
.c_str());
371 ofstream
Out(NewFile
.c_str());
373 return _error
->Errno("ofstream::ofstream",
374 "Failed to open %s.new",DFile
.c_str());
376 /* Write out all of the configuration directives by walking the
377 configuration tree */
378 const Configuration::Item
*Top
= Cnf
.Tree(0);
381 // Print the config entry
382 if (Top
->Value
.empty() == false)
383 Out
<< Top
->FullTag() + " \"" << Top
->Value
<< "\";" << endl
;
391 while (Top
!= 0 && Top
->Next
== 0)
399 rename(DFile
.c_str(),string(DFile
+ '~').c_str());
400 if (rename(NewFile
.c_str(),DFile
.c_str()) != 0)
401 return _error
->Errno("rename","Failed to rename %s.new to %s",
402 DFile
.c_str(),DFile
.c_str());
407 // WriteSourceList - Write an updated sourcelist /*{{{*/
408 // ---------------------------------------------------------------------
409 /* This reads the old source list and copies it into the new one. It
410 appends the new CDROM entires just after the first block of comments.
411 This places them first in the file. It also removes any old entries
412 that were the same. */
413 bool pkgCdrom::WriteSourceList(string Name
,vector
<string
> &List
,bool Source
)
415 if (List
.size() == 0)
418 string File
= _config
->FindFile("Dir::Etc::sourcelist");
420 // Open the stream for reading
421 ifstream
F((FileExists(File
)?File
.c_str():"/dev/null"),
424 return _error
->Errno("ifstream::ifstream","Opening %s",File
.c_str());
426 string NewFile
= File
+ ".new";
427 unlink(NewFile
.c_str());
428 ofstream
Out(NewFile
.c_str());
430 return _error
->Errno("ofstream::ofstream",
431 "Failed to open %s.new",File
.c_str());
433 // Create a short uri without the path
434 string ShortURI
= "cdrom:[" + Name
+ "]/";
435 string ShortURI2
= "cdrom:" + Name
+ "/"; // For Compatibility
446 while (F
.eof() == false)
448 F
.getline(Buffer
,sizeof(Buffer
));
450 if (F
.fail() && !F
.eof())
451 return _error
->Error(_("Line %u too long in source list %s."),
452 CurLine
,File
.c_str());
453 _strtabexpand(Buffer
,sizeof(Buffer
));
457 if (Buffer
[0] == '#' || Buffer
[0] == 0)
459 Out
<< Buffer
<< endl
;
465 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
467 string::size_type Space
= (*I
).find(' ');
468 if (Space
== string::npos
)
469 return _error
->Error("Internal error");
470 Out
<< Type
<< " cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
471 " " << string(*I
,Space
+1) << endl
;
479 const char *C
= Buffer
;
480 if (ParseQuoteWord(C
,cType
) == false ||
481 ParseQuoteWord(C
,URI
) == false)
483 Out
<< Buffer
<< endl
;
487 // Emit lines like this one
488 if (cType
!= Type
|| (string(URI
,0,ShortURI
.length()) != ShortURI
&&
489 string(URI
,0,ShortURI
.length()) != ShortURI2
))
491 Out
<< Buffer
<< endl
;
496 // Just in case the file was empty
499 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
501 string::size_type Space
= (*I
).find(' ');
502 if (Space
== string::npos
)
503 return _error
->Error("Internal error");
505 Out
<< "deb cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
506 " " << string(*I
,Space
+1) << endl
;
512 rename(File
.c_str(),string(File
+ '~').c_str());
513 if (rename(NewFile
.c_str(),File
.c_str()) != 0)
514 return _error
->Errno("rename","Failed to rename %s.new to %s",
515 File
.c_str(),File
.c_str());
521 bool pkgCdrom::Ident(string
&ident
, pkgCdromStatus
*log
)
526 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
528 CDROM
= SafeGetCWD() + '/' + CDROM
;
532 ioprintf(msg
, _("Using CD-ROM mount point %s\nMounting CD-ROM\n"),
534 log
->Update(msg
.str());
536 if (MountCdrom(CDROM
) == false)
537 return _error
->Error("Failed to mount the cdrom.");
539 // Hash the CD to get an ID
541 log
->Update(_("Identifying.. "));
544 if (IdentCdrom(CDROM
,ident
) == false)
551 ioprintf(msg
, "[%s]\n",ident
.c_str());
552 log
->Update(msg
.str());
556 Configuration Database
;
557 string DFile
= _config
->FindFile("Dir::State::cdroms");
558 if (FileExists(DFile
) == true)
560 if (ReadConfigFile(Database
,DFile
) == false)
561 return _error
->Error("Unable to read the cdrom database %s",
566 ioprintf(msg
, _("Stored label: %s \n"),
567 Database
.Find("CD::"+ident
).c_str());
568 log
->Update(msg
.str());
574 bool pkgCdrom::Add(pkgCdromStatus
*log
)
579 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
581 CDROM
= SafeGetCWD() + '/' + CDROM
;
584 log
->SetTotal(STEP_LAST
);
586 ioprintf(msg
, _("Using CD-ROM mount point %s\n"), CDROM
.c_str());
587 log
->Update(msg
.str(), STEP_PREPARE
);
591 Configuration Database
;
592 string DFile
= _config
->FindFile("Dir::State::cdroms");
593 if (FileExists(DFile
) == true)
595 if (ReadConfigFile(Database
,DFile
) == false)
596 return _error
->Error("Unable to read the cdrom database %s",
600 // Unmount the CD and get the user to put in the one they want
601 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
604 log
->Update(_("Unmounting CD-ROM\n"), STEP_UNMOUNT
);
608 log
->Update(_("Waiting for disc...\n"), STEP_WAIT
);
609 if(!log
->ChangeCdrom()) {
615 // Mount the new CDROM
616 log
->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT
);
617 if (MountCdrom(CDROM
) == false)
618 return _error
->Error("Failed to mount the cdrom.");
621 // Hash the CD to get an ID
623 log
->Update(_("Identifying.. "), STEP_IDENT
);
625 if (IdentCdrom(CDROM
,ID
) == false)
631 log
->Update("["+ID
+"]\n");
634 log
->Update(_("Scanning disc for index files..\n"),STEP_SCAN
);
636 // Get the CD structure
638 vector
<string
> SourceList
;
639 vector
<string
> SigList
;
640 vector
<string
> TransList
;
641 string StartDir
= SafeGetCWD();
643 if (FindPackages(CDROM
,List
,SourceList
, SigList
,TransList
,InfoDir
,log
) == false)
649 chdir(StartDir
.c_str());
651 if (_config
->FindB("Debug::aptcdrom",false) == true)
653 cout
<< "I found (binary):" << endl
;
654 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
656 cout
<< "I found (source):" << endl
;
657 for (vector
<string
>::iterator I
= SourceList
.begin(); I
!= SourceList
.end(); I
++)
659 cout
<< "I found (Signatures):" << endl
;
660 for (vector
<string
>::iterator I
= SigList
.begin(); I
!= SigList
.end(); I
++)
664 //log->Update(_("Cleaning package lists..."), STEP_CLEAN);
667 DropBinaryArch(List
);
668 DropRepeats(List
,"Packages");
669 DropRepeats(SourceList
,"Sources");
670 DropRepeats(SigList
,"Release.gpg");
671 DropRepeats(TransList
,"");
674 ioprintf(msg
, _("Found %i package indexes, %i source indexes, "
675 "%i translation indexes and %i signatures\n"),
676 List
.size(), SourceList
.size(), TransList
.size(),
678 log
->Update(msg
.str(), STEP_SCAN
);
681 if (List
.size() == 0 && SourceList
.size() == 0)
683 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
685 return _error
->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
688 // Check if the CD is in the database
690 if (Database
.Exists("CD::" + ID
) == false ||
691 _config
->FindB("APT::CDROM::Rename",false) == true)
693 // Try to use the CDs label if at all possible
694 if (InfoDir
.empty() == false &&
695 FileExists(InfoDir
+ "/info") == true)
697 ifstream
F(string(InfoDir
+ "/info").c_str());
701 if (Name
.empty() == false)
703 // Escape special characters
704 string::iterator J
= Name
.begin();
705 for (; J
!= Name
.end(); J
++)
706 if (*J
== '"' || *J
== ']' || *J
== '[')
711 ioprintf(msg
, _("Found label '%s'\n"), Name
.c_str());
712 log
->Update(msg
.str());
714 Database
.Set("CD::" + ID
+ "::Label",Name
);
718 if (_config
->FindB("APT::CDROM::Rename",false) == true ||
719 Name
.empty() == true)
723 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
725 return _error
->Error("No disc name found and no way to ask for it");
729 if(!log
->AskCdromName(Name
)) {
733 cout
<< "Name: '" << Name
<< "'" << endl
;
735 if (Name
.empty() == false &&
736 Name
.find('"') == string::npos
&&
737 Name
.find('[') == string::npos
&&
738 Name
.find(']') == string::npos
)
740 log
->Update(_("That is not a valid name, try again.\n"));
745 Name
= Database
.Find("CD::" + ID
);
747 // Escape special characters
748 string::iterator J
= Name
.begin();
749 for (; J
!= Name
.end(); J
++)
750 if (*J
== '"' || *J
== ']' || *J
== '[')
753 Database
.Set("CD::" + ID
,Name
);
756 ioprintf(msg
, _("This disc is called: \n'%s'\n"), Name
.c_str());
757 log
->Update(msg
.str());
760 log
->Update(_("Copying package lists..."), STEP_COPY
);
761 // take care of the signatures and copy them if they are ok
762 // (we do this before PackageCopy as it modifies "List" and "SourceList")
763 SigVerify SignVerify
;
764 SignVerify
.CopyAndVerify(CDROM
, Name
, SigList
, List
, SourceList
);
766 // Copy the package files to the state directory
769 TranslationsCopy TransCopy
;
770 if (Copy
.CopyPackages(CDROM
,Name
,List
, log
) == false ||
771 SrcCopy
.CopyPackages(CDROM
,Name
,SourceList
, log
) == false ||
772 TransCopy
.CopyTranslations(CDROM
,Name
,TransList
, log
) == false)
775 // reduce the List so that it takes less space in sources.list
776 ReduceSourcelist(CDROM
,List
);
777 ReduceSourcelist(CDROM
,SourceList
);
779 // Write the database and sourcelist
780 if (_config
->FindB("APT::cdrom::NoAct",false) == false)
782 if (WriteDatabase(Database
) == false)
786 log
->Update(_("Writing new source list\n"), STEP_WRITE
);
788 if (WriteSourceList(Name
,List
,false) == false ||
789 WriteSourceList(Name
,SourceList
,true) == false)
793 // Print the sourcelist entries
795 log
->Update(_("Source list entries for this disc are:\n"));
797 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
799 string::size_type Space
= (*I
).find(' ');
800 if (Space
== string::npos
)
802 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
804 return _error
->Error("Internal error");
809 msg
<< "deb cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
810 " " << string(*I
,Space
+1) << endl
;
811 log
->Update(msg
.str());
815 for (vector
<string
>::iterator I
= SourceList
.begin(); I
!= SourceList
.end(); I
++)
817 string::size_type Space
= (*I
).find(' ');
818 if (Space
== string::npos
)
820 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
822 return _error
->Error("Internal error");
827 msg
<< "deb-src cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
828 " " << string(*I
,Space
+1) << endl
;
829 log
->Update(msg
.str());
835 // Unmount and finish
836 if (_config
->FindB("APT::CDROM::NoMount",false) == false) {
837 log
->Update(_("Unmounting CD-ROM...\n"), STEP_LAST
);