4 #include<apt-pkg/init.h>
5 #include<apt-pkg/error.h>
6 #include<apt-pkg/cdromutl.h>
7 #include<apt-pkg/strutl.h>
8 #include<apt-pkg/cdrom.h>
21 #include "indexcopy.h"
25 // FindPackages - Find the package files on the CDROM /*{{{*/
26 // ---------------------------------------------------------------------
27 /* We look over the cdrom for package files. This is a recursive
28 search that short circuits when it his a package file in the dir.
29 This speeds it up greatly as the majority of the size is in the
31 bool pkgCdrom::FindPackages(string CD
,
33 vector
<string
> &SList
,
34 vector
<string
> &SigList
,
35 vector
<string
> &TransList
,
36 string
&InfoDir
, pkgCdromStatus
*log
,
39 static ino_t Inodes
[9];
42 // if we have a look we "pulse" now
49 if (CD
[CD
.length()-1] != '/')
52 if (chdir(CD
.c_str()) != 0)
53 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
55 // Look for a .disk subdirectory
57 if (stat(".disk",&Buf
) == 0)
59 if (InfoDir
.empty() == true)
60 InfoDir
= CD
+ ".disk/";
63 // Don't look into directories that have been marked to ingore.
64 if (stat(".aptignr",&Buf
) == 0)
68 /* Check _first_ for a signature file as apt-cdrom assumes that all files
69 under a Packages/Source file are in control of that file and stops
72 if (stat("Release.gpg",&Buf
) == 0)
74 SigList
.push_back(CD
);
76 /* Aha! We found some package files. We assume that everything under
77 this dir is controlled by those package files so we don't look down
79 if (stat("Packages",&Buf
) == 0 || stat("Packages.gz",&Buf
) == 0)
83 // Continue down if thorough is given
84 if (_config
->FindB("APT::CDROM::Thorough",false) == false)
87 if (stat("Sources.gz",&Buf
) == 0 || stat("Sources",&Buf
) == 0)
91 // Continue down if thorough is given
92 if (_config
->FindB("APT::CDROM::Thorough",false) == false)
96 // see if we find translatin indexes
97 if (stat("i18n",&Buf
) == 0)
100 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
102 if(strstr(Dir
->d_name
,"Translation") != NULL
)
104 if (_config
->FindB("Debug::aptcdrom",false) == true)
105 std::clog
<< "found translations: " << Dir
->d_name
<< "\n";
106 string file
= Dir
->d_name
;
107 if(file
.substr(file
.size()-3,file
.size()) == ".gz")
108 file
= file
.substr(0,file
.size()-3);
109 TransList
.push_back(CD
+"i18n/"+ file
);
118 return _error
->Errno("opendir","Unable to read %s",CD
.c_str());
120 // Run over the directory
121 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
124 if (strcmp(Dir
->d_name
,".") == 0 ||
125 strcmp(Dir
->d_name
,"..") == 0 ||
126 //strcmp(Dir->d_name,"source") == 0 ||
127 strcmp(Dir
->d_name
,".disk") == 0 ||
128 strcmp(Dir
->d_name
,"experimental") == 0 ||
129 strcmp(Dir
->d_name
,"binary-all") == 0 ||
130 strcmp(Dir
->d_name
,"debian-installer") == 0)
133 // See if the name is a sub directory
135 if (stat(Dir
->d_name
,&Buf
) != 0)
138 if (S_ISDIR(Buf
.st_mode
) == 0)
142 for (I
= 0; I
!= Depth
; I
++)
143 if (Inodes
[I
] == Buf
.st_ino
)
148 // Store the inodes weve seen
149 Inodes
[Depth
] = Buf
.st_ino
;
152 if (FindPackages(CD
+ Dir
->d_name
,List
,SList
,SigList
,TransList
,InfoDir
,log
,Depth
+1) == false)
155 if (chdir(CD
.c_str()) != 0)
156 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
161 return !_error
->PendingError();
164 // Score - We compute a 'score' for a path /*{{{*/
165 // ---------------------------------------------------------------------
166 /* Paths are scored based on how close they come to what I consider
167 normal. That is ones that have 'dist' 'stable' 'testing' will score
168 higher than ones without. */
169 int pkgCdrom::Score(string Path
)
172 if (Path
.find("stable/") != string::npos
)
174 if (Path
.find("/binary-") != string::npos
)
176 if (Path
.find("testing/") != string::npos
)
178 if (Path
.find("unstable/") != string::npos
)
180 if (Path
.find("/dists/") != string::npos
)
182 if (Path
.find("/main/") != string::npos
)
184 if (Path
.find("/contrib/") != string::npos
)
186 if (Path
.find("/non-free/") != string::npos
)
188 if (Path
.find("/non-US/") != string::npos
)
190 if (Path
.find("/source/") != string::npos
)
192 if (Path
.find("/debian/") != string::npos
)
195 // check for symlinks in the patch leading to the actual file
196 // a symlink gets a big penalty
198 string statPath
= flNotFile(Path
);
199 string cdromPath
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
200 while(statPath
!= cdromPath
&& statPath
!= "./") {
201 statPath
.resize(statPath
.size()-1); // remove the trailing '/'
202 if (lstat(statPath
.c_str(),&Buf
) == 0) {
203 if(S_ISLNK(Buf
.st_mode
)) {
208 statPath
= flNotFile(statPath
); // descent
215 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
216 // ---------------------------------------------------------------------
217 /* Here we drop everything that is not this machines arch */
218 bool pkgCdrom::DropBinaryArch(vector
<string
> &List
)
221 snprintf(S
,sizeof(S
),"/binary-%s/",
222 _config
->Find("Apt::Architecture").c_str());
224 for (unsigned int I
= 0; I
< List
.size(); I
++)
226 const char *Str
= List
[I
].c_str();
229 if ((Res
= strstr(Str
,"/binary-")) == 0)
233 if (strlen(Res
) < strlen(S
))
235 List
.erase(List
.begin() + I
);
240 // See if it is our arch
241 if (stringcmp(Res
,Res
+ strlen(S
),S
) == 0)
245 List
.erase(List
.begin() + I
);
253 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
254 // ---------------------------------------------------------------------
255 /* Here we go and stat every file that we found and strip dup inodes. */
256 bool pkgCdrom::DropRepeats(vector
<string
> &List
,const char *Name
)
258 // Get a list of all the inodes
259 ino_t
*Inodes
= new ino_t
[List
.size()];
260 for (unsigned int I
= 0; I
!= List
.size(); I
++)
263 if (stat((List
[I
] + Name
).c_str(),&Buf
) != 0 &&
264 stat((List
[I
] + Name
+ ".gz").c_str(),&Buf
) != 0)
265 _error
->Errno("stat","Failed to stat %s%s",List
[I
].c_str(),
267 Inodes
[I
] = Buf
.st_ino
;
270 if (_error
->PendingError() == true)
274 for (unsigned int I
= 0; I
!= List
.size(); I
++)
276 for (unsigned int J
= I
+1; J
< List
.size(); J
++)
279 if (Inodes
[J
] != Inodes
[I
])
282 // We score the two paths.. and erase one
283 int ScoreA
= Score(List
[I
]);
284 int ScoreB
= Score(List
[J
]);
295 // Wipe erased entries
296 for (unsigned int I
= 0; I
< List
.size();)
298 if (List
[I
].empty() == false)
301 List
.erase(List
.begin()+I
);
308 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
309 // ---------------------------------------------------------------------
310 /* This takes the list of source list expressed entires and collects
311 similar ones to form a single entry for each dist */
312 void pkgCdrom::ReduceSourcelist(string CD
,vector
<string
> &List
)
314 sort(List
.begin(),List
.end());
316 // Collect similar entries
317 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
320 string::size_type Space
= (*I
).find(' ');
321 if (Space
== string::npos
)
323 string::size_type SSpace
= (*I
).find(' ',Space
+ 1);
324 if (SSpace
== string::npos
)
327 string Word1
= string(*I
,Space
,SSpace
-Space
);
328 string Prefix
= string(*I
,0,Space
);
329 for (vector
<string
>::iterator J
= List
.begin(); J
!= I
; J
++)
332 string::size_type Space2
= (*J
).find(' ');
333 if (Space2
== string::npos
)
335 string::size_type SSpace2
= (*J
).find(' ',Space2
+ 1);
336 if (SSpace2
== string::npos
)
339 if (string(*J
,0,Space2
) != Prefix
)
341 if (string(*J
,Space2
,SSpace2
-Space2
) != Word1
)
344 *J
+= string(*I
,SSpace
);
349 // Wipe erased entries
350 for (unsigned int I
= 0; I
< List
.size();)
352 if (List
[I
].empty() == false)
355 List
.erase(List
.begin()+I
);
359 // WriteDatabase - Write the CDROM Database file /*{{{*/
360 // ---------------------------------------------------------------------
361 /* We rewrite the configuration class associated with the cdrom database. */
362 bool pkgCdrom::WriteDatabase(Configuration
&Cnf
)
364 string DFile
= _config
->FindFile("Dir::State::cdroms");
365 string NewFile
= DFile
+ ".new";
367 unlink(NewFile
.c_str());
368 ofstream
Out(NewFile
.c_str());
370 return _error
->Errno("ofstream::ofstream",
371 "Failed to open %s.new",DFile
.c_str());
373 /* Write out all of the configuration directives by walking the
374 configuration tree */
375 const Configuration::Item
*Top
= Cnf
.Tree(0);
378 // Print the config entry
379 if (Top
->Value
.empty() == false)
380 Out
<< Top
->FullTag() + " \"" << Top
->Value
<< "\";" << endl
;
388 while (Top
!= 0 && Top
->Next
== 0)
396 rename(DFile
.c_str(),string(DFile
+ '~').c_str());
397 if (rename(NewFile
.c_str(),DFile
.c_str()) != 0)
398 return _error
->Errno("rename","Failed to rename %s.new to %s",
399 DFile
.c_str(),DFile
.c_str());
404 // WriteSourceList - Write an updated sourcelist /*{{{*/
405 // ---------------------------------------------------------------------
406 /* This reads the old source list and copies it into the new one. It
407 appends the new CDROM entires just after the first block of comments.
408 This places them first in the file. It also removes any old entries
409 that were the same. */
410 bool pkgCdrom::WriteSourceList(string Name
,vector
<string
> &List
,bool Source
)
412 if (List
.size() == 0)
415 string File
= _config
->FindFile("Dir::Etc::sourcelist");
417 // Open the stream for reading
418 ifstream
F((FileExists(File
)?File
.c_str():"/dev/null"),
421 return _error
->Errno("ifstream::ifstream","Opening %s",File
.c_str());
423 string NewFile
= File
+ ".new";
424 unlink(NewFile
.c_str());
425 ofstream
Out(NewFile
.c_str());
427 return _error
->Errno("ofstream::ofstream",
428 "Failed to open %s.new",File
.c_str());
430 // Create a short uri without the path
431 string ShortURI
= "cdrom:[" + Name
+ "]/";
432 string ShortURI2
= "cdrom:" + Name
+ "/"; // For Compatibility
443 while (F
.eof() == false)
445 F
.getline(Buffer
,sizeof(Buffer
));
447 if (F
.fail() && !F
.eof())
448 return _error
->Error(_("Line %u too long in source list %s."),
449 CurLine
,File
.c_str());
450 _strtabexpand(Buffer
,sizeof(Buffer
));
454 if (Buffer
[0] == '#' || Buffer
[0] == 0)
456 Out
<< Buffer
<< endl
;
462 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
464 string::size_type Space
= (*I
).find(' ');
465 if (Space
== string::npos
)
466 return _error
->Error("Internal error");
467 Out
<< Type
<< " cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
468 " " << string(*I
,Space
+1) << endl
;
476 const char *C
= Buffer
;
477 if (ParseQuoteWord(C
,cType
) == false ||
478 ParseQuoteWord(C
,URI
) == false)
480 Out
<< Buffer
<< endl
;
484 // Emit lines like this one
485 if (cType
!= Type
|| (string(URI
,0,ShortURI
.length()) != ShortURI
&&
486 string(URI
,0,ShortURI
.length()) != ShortURI2
))
488 Out
<< Buffer
<< endl
;
493 // Just in case the file was empty
496 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
498 string::size_type Space
= (*I
).find(' ');
499 if (Space
== string::npos
)
500 return _error
->Error("Internal error");
502 Out
<< "deb cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
503 " " << string(*I
,Space
+1) << endl
;
509 rename(File
.c_str(),string(File
+ '~').c_str());
510 if (rename(NewFile
.c_str(),File
.c_str()) != 0)
511 return _error
->Errno("rename","Failed to rename %s.new to %s",
512 File
.c_str(),File
.c_str());
518 bool pkgCdrom::Ident(string
&ident
, pkgCdromStatus
*log
)
523 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
525 CDROM
= SafeGetCWD() + '/' + CDROM
;
529 ioprintf(msg
, _("Using CD-ROM mount point %s\nMounting CD-ROM\n"),
531 log
->Update(msg
.str());
533 if (MountCdrom(CDROM
) == false)
534 return _error
->Error("Failed to mount the cdrom.");
536 // Hash the CD to get an ID
538 log
->Update(_("Identifying.. "));
541 if (IdentCdrom(CDROM
,ident
) == false)
548 ioprintf(msg
, "[%s]\n",ident
.c_str());
549 log
->Update(msg
.str());
553 Configuration Database
;
554 string DFile
= _config
->FindFile("Dir::State::cdroms");
555 if (FileExists(DFile
) == true)
557 if (ReadConfigFile(Database
,DFile
) == false)
558 return _error
->Error("Unable to read the cdrom database %s",
563 ioprintf(msg
, _("Stored label: %s\n"),
564 Database
.Find("CD::"+ident
).c_str());
565 log
->Update(msg
.str());
568 // Unmount and finish
569 if (_config
->FindB("APT::CDROM::NoMount",false) == false) {
570 log
->Update(_("Unmounting CD-ROM...\n"), STEP_LAST
);
578 bool pkgCdrom::Add(pkgCdromStatus
*log
)
583 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
585 CDROM
= SafeGetCWD() + '/' + CDROM
;
588 log
->SetTotal(STEP_LAST
);
590 ioprintf(msg
, _("Using CD-ROM mount point %s\n"), CDROM
.c_str());
591 log
->Update(msg
.str(), STEP_PREPARE
);
595 Configuration Database
;
596 string DFile
= _config
->FindFile("Dir::State::cdroms");
597 if (FileExists(DFile
) == true)
599 if (ReadConfigFile(Database
,DFile
) == false)
600 return _error
->Error("Unable to read the cdrom database %s",
604 // Unmount the CD and get the user to put in the one they want
605 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
608 log
->Update(_("Unmounting CD-ROM\n"), STEP_UNMOUNT
);
612 log
->Update(_("Waiting for disc...\n"), STEP_WAIT
);
613 if(!log
->ChangeCdrom()) {
619 // Mount the new CDROM
620 log
->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT
);
621 if (MountCdrom(CDROM
) == false)
622 return _error
->Error("Failed to mount the cdrom.");
625 // Hash the CD to get an ID
627 log
->Update(_("Identifying.. "), STEP_IDENT
);
629 if (IdentCdrom(CDROM
,ID
) == false)
635 log
->Update("["+ID
+"]\n");
638 log
->Update(_("Scanning disc for index files..\n"),STEP_SCAN
);
640 // Get the CD structure
642 vector
<string
> SourceList
;
643 vector
<string
> SigList
;
644 vector
<string
> TransList
;
645 string StartDir
= SafeGetCWD();
647 if (FindPackages(CDROM
,List
,SourceList
, SigList
,TransList
,InfoDir
,log
) == false)
653 chdir(StartDir
.c_str());
655 if (_config
->FindB("Debug::aptcdrom",false) == true)
657 cout
<< "I found (binary):" << endl
;
658 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
660 cout
<< "I found (source):" << endl
;
661 for (vector
<string
>::iterator I
= SourceList
.begin(); I
!= SourceList
.end(); I
++)
663 cout
<< "I found (Signatures):" << endl
;
664 for (vector
<string
>::iterator I
= SigList
.begin(); I
!= SigList
.end(); I
++)
668 //log->Update(_("Cleaning package lists..."), STEP_CLEAN);
671 DropBinaryArch(List
);
672 DropRepeats(List
,"Packages");
673 DropRepeats(SourceList
,"Sources");
674 DropRepeats(SigList
,"Release.gpg");
675 DropRepeats(TransList
,"");
678 ioprintf(msg
, _("Found %zu package indexes, %zu source indexes, "
679 "%zu translation indexes and %zu signatures\n"),
680 List
.size(), SourceList
.size(), TransList
.size(),
682 log
->Update(msg
.str(), STEP_SCAN
);
685 if (List
.size() == 0 && SourceList
.size() == 0)
687 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
689 return _error
->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
692 // Check if the CD is in the database
694 if (Database
.Exists("CD::" + ID
) == false ||
695 _config
->FindB("APT::CDROM::Rename",false) == true)
697 // Try to use the CDs label if at all possible
698 if (InfoDir
.empty() == false &&
699 FileExists(InfoDir
+ "/info") == true)
701 ifstream
F(string(InfoDir
+ "/info").c_str());
705 if (Name
.empty() == false)
707 // Escape special characters
708 string::iterator J
= Name
.begin();
709 for (; J
!= Name
.end(); J
++)
710 if (*J
== '"' || *J
== ']' || *J
== '[')
715 ioprintf(msg
, _("Found label '%s'\n"), Name
.c_str());
716 log
->Update(msg
.str());
718 Database
.Set("CD::" + ID
+ "::Label",Name
);
722 if (_config
->FindB("APT::CDROM::Rename",false) == true ||
723 Name
.empty() == true)
727 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
729 return _error
->Error("No disc name found and no way to ask for it");
733 if(!log
->AskCdromName(Name
)) {
737 cout
<< "Name: '" << Name
<< "'" << endl
;
739 if (Name
.empty() == false &&
740 Name
.find('"') == string::npos
&&
741 Name
.find('[') == string::npos
&&
742 Name
.find(']') == string::npos
)
744 log
->Update(_("That is not a valid name, try again.\n"));
749 Name
= Database
.Find("CD::" + ID
);
751 // Escape special characters
752 string::iterator J
= Name
.begin();
753 for (; J
!= Name
.end(); J
++)
754 if (*J
== '"' || *J
== ']' || *J
== '[')
757 Database
.Set("CD::" + ID
,Name
);
760 ioprintf(msg
, _("This disc is called: \n'%s'\n"), Name
.c_str());
761 log
->Update(msg
.str());
764 log
->Update(_("Copying package lists..."), STEP_COPY
);
765 // take care of the signatures and copy them if they are ok
766 // (we do this before PackageCopy as it modifies "List" and "SourceList")
767 SigVerify SignVerify
;
768 SignVerify
.CopyAndVerify(CDROM
, Name
, SigList
, List
, SourceList
);
770 // Copy the package files to the state directory
773 TranslationsCopy TransCopy
;
774 if (Copy
.CopyPackages(CDROM
,Name
,List
, log
) == false ||
775 SrcCopy
.CopyPackages(CDROM
,Name
,SourceList
, log
) == false ||
776 TransCopy
.CopyTranslations(CDROM
,Name
,TransList
, log
) == false)
779 // reduce the List so that it takes less space in sources.list
780 ReduceSourcelist(CDROM
,List
);
781 ReduceSourcelist(CDROM
,SourceList
);
783 // Write the database and sourcelist
784 if (_config
->FindB("APT::cdrom::NoAct",false) == false)
786 if (WriteDatabase(Database
) == false)
790 log
->Update(_("Writing new source list\n"), STEP_WRITE
);
792 if (WriteSourceList(Name
,List
,false) == false ||
793 WriteSourceList(Name
,SourceList
,true) == false)
797 // Print the sourcelist entries
799 log
->Update(_("Source list entries for this disc are:\n"));
801 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
803 string::size_type Space
= (*I
).find(' ');
804 if (Space
== string::npos
)
806 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
808 return _error
->Error("Internal error");
813 msg
<< "deb cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
814 " " << string(*I
,Space
+1) << endl
;
815 log
->Update(msg
.str());
819 for (vector
<string
>::iterator I
= SourceList
.begin(); I
!= SourceList
.end(); I
++)
821 string::size_type Space
= (*I
).find(' ');
822 if (Space
== string::npos
)
824 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
826 return _error
->Error("Internal error");
831 msg
<< "deb-src cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
832 " " << string(*I
,Space
+1) << endl
;
833 log
->Update(msg
.str());
839 // Unmount and finish
840 if (_config
->FindB("APT::CDROM::NoMount",false) == false) {
841 log
->Update(_("Unmounting CD-ROM...\n"), STEP_LAST
);