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
,
35 vector
<string
> &SList
,
36 vector
<string
> &SigList
,
37 vector
<string
> &TransList
,
38 string
&InfoDir
, pkgCdromStatus
*log
,
41 static ino_t Inodes
[9];
44 // if we have a look we "pulse" now
51 if (CD
[CD
.length()-1] != '/')
54 if (chdir(CD
.c_str()) != 0)
55 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
57 // Look for a .disk subdirectory
59 if (stat(".disk",&Buf
) == 0)
61 if (InfoDir
.empty() == true)
62 InfoDir
= CD
+ ".disk/";
65 // Don't look into directories that have been marked to ingore.
66 if (stat(".aptignr",&Buf
) == 0)
70 /* Check _first_ for a signature file as apt-cdrom assumes that all files
71 under a Packages/Source file are in control of that file and stops
74 if (stat("Release.gpg",&Buf
) == 0)
76 SigList
.push_back(CD
);
78 /* Aha! We found some package files. We assume that everything under
79 this dir is controlled by those package files so we don't look down
81 if (stat("Packages",&Buf
) == 0 || stat("Packages.gz",&Buf
) == 0)
85 // Continue down if thorough is given
86 if (_config
->FindB("APT::CDROM::Thorough",false) == false)
89 if (stat("Sources.gz",&Buf
) == 0 || stat("Sources",&Buf
) == 0)
93 // Continue down if thorough is given
94 if (_config
->FindB("APT::CDROM::Thorough",false) == false)
98 // see if we find translatin indexes
99 if (stat("i18n",&Buf
) == 0)
102 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
104 if(strstr(Dir
->d_name
,"Translation") != NULL
)
106 if (_config
->FindB("Debug::aptcdrom",false) == true)
107 std::clog
<< "found translations: " << Dir
->d_name
<< "\n";
108 string file
= Dir
->d_name
;
109 if(file
.substr(file
.size()-3,file
.size()) == ".gz")
110 file
= file
.substr(0,file
.size()-3);
111 TransList
.push_back(CD
+"i18n/"+ file
);
120 return _error
->Errno("opendir","Unable to read %s",CD
.c_str());
122 // Run over the directory
123 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
126 if (strcmp(Dir
->d_name
,".") == 0 ||
127 strcmp(Dir
->d_name
,"..") == 0 ||
128 //strcmp(Dir->d_name,"source") == 0 ||
129 strcmp(Dir
->d_name
,".disk") == 0 ||
130 strcmp(Dir
->d_name
,"experimental") == 0 ||
131 strcmp(Dir
->d_name
,"binary-all") == 0 ||
132 strcmp(Dir
->d_name
,"debian-installer") == 0)
135 // See if the name is a sub directory
137 if (stat(Dir
->d_name
,&Buf
) != 0)
140 if (S_ISDIR(Buf
.st_mode
) == 0)
144 for (I
= 0; I
!= Depth
; I
++)
145 if (Inodes
[I
] == Buf
.st_ino
)
150 // Store the inodes weve seen
151 Inodes
[Depth
] = Buf
.st_ino
;
154 if (FindPackages(CD
+ Dir
->d_name
,List
,SList
,SigList
,TransList
,InfoDir
,log
,Depth
+1) == false)
157 if (chdir(CD
.c_str()) != 0)
158 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
163 return !_error
->PendingError();
166 // Score - We compute a 'score' for a path /*{{{*/
167 // ---------------------------------------------------------------------
168 /* Paths are scored based on how close they come to what I consider
169 normal. That is ones that have 'dist' 'stable' 'testing' will score
170 higher than ones without. */
171 int pkgCdrom::Score(string Path
)
174 if (Path
.find("stable/") != string::npos
)
176 if (Path
.find("/binary-") != string::npos
)
178 if (Path
.find("testing/") != string::npos
)
180 if (Path
.find("unstable/") != string::npos
)
182 if (Path
.find("/dists/") != string::npos
)
184 if (Path
.find("/main/") != string::npos
)
186 if (Path
.find("/contrib/") != string::npos
)
188 if (Path
.find("/non-free/") != string::npos
)
190 if (Path
.find("/non-US/") != string::npos
)
192 if (Path
.find("/source/") != string::npos
)
194 if (Path
.find("/debian/") != string::npos
)
197 // check for symlinks in the patch leading to the actual file
198 // a symlink gets a big penalty
200 string statPath
= flNotFile(Path
);
201 string cdromPath
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
202 while(statPath
!= cdromPath
&& statPath
!= "./") {
203 statPath
.resize(statPath
.size()-1); // remove the trailing '/'
204 if (lstat(statPath
.c_str(),&Buf
) == 0) {
205 if(S_ISLNK(Buf
.st_mode
)) {
210 statPath
= flNotFile(statPath
); // descent
217 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
218 // ---------------------------------------------------------------------
219 /* Here we drop everything that is not this machines arch */
220 bool pkgCdrom::DropBinaryArch(vector
<string
> &List
)
223 snprintf(S
,sizeof(S
),"/binary-%s/",
224 _config
->Find("Apt::Architecture").c_str());
226 for (unsigned int I
= 0; I
< List
.size(); I
++)
228 const char *Str
= List
[I
].c_str();
231 if ((Res
= strstr(Str
,"/binary-")) == 0)
235 if (strlen(Res
) < strlen(S
))
237 List
.erase(List
.begin() + I
);
242 // See if it is our arch
243 if (stringcmp(Res
,Res
+ strlen(S
),S
) == 0)
247 List
.erase(List
.begin() + I
);
255 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
256 // ---------------------------------------------------------------------
257 /* Here we go and stat every file that we found and strip dup inodes. */
258 bool pkgCdrom::DropRepeats(vector
<string
> &List
,const char *Name
)
260 // Get a list of all the inodes
261 ino_t
*Inodes
= new ino_t
[List
.size()];
262 for (unsigned int I
= 0; I
!= List
.size(); I
++)
265 if (stat((List
[I
] + Name
).c_str(),&Buf
) != 0 &&
266 stat((List
[I
] + Name
+ ".gz").c_str(),&Buf
) != 0)
267 _error
->Errno("stat","Failed to stat %s%s",List
[I
].c_str(),
269 Inodes
[I
] = Buf
.st_ino
;
272 if (_error
->PendingError() == true)
276 for (unsigned int I
= 0; I
!= List
.size(); I
++)
278 for (unsigned int J
= I
+1; J
< List
.size(); J
++)
281 if (Inodes
[J
] != Inodes
[I
])
284 // We score the two paths.. and erase one
285 int ScoreA
= Score(List
[I
]);
286 int ScoreB
= Score(List
[J
]);
297 // Wipe erased entries
298 for (unsigned int I
= 0; I
< List
.size();)
300 if (List
[I
].empty() == false)
303 List
.erase(List
.begin()+I
);
310 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
311 // ---------------------------------------------------------------------
312 /* This takes the list of source list expressed entires and collects
313 similar ones to form a single entry for each dist */
314 void pkgCdrom::ReduceSourcelist(string CD
,vector
<string
> &List
)
316 sort(List
.begin(),List
.end());
318 // Collect similar entries
319 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
322 string::size_type Space
= (*I
).find(' ');
323 if (Space
== string::npos
)
325 string::size_type SSpace
= (*I
).find(' ',Space
+ 1);
326 if (SSpace
== string::npos
)
329 string Word1
= string(*I
,Space
,SSpace
-Space
);
330 string Prefix
= string(*I
,0,Space
);
331 for (vector
<string
>::iterator J
= List
.begin(); J
!= I
; J
++)
334 string::size_type Space2
= (*J
).find(' ');
335 if (Space2
== string::npos
)
337 string::size_type SSpace2
= (*J
).find(' ',Space2
+ 1);
338 if (SSpace2
== string::npos
)
341 if (string(*J
,0,Space2
) != Prefix
)
343 if (string(*J
,Space2
,SSpace2
-Space2
) != Word1
)
346 *J
+= string(*I
,SSpace
);
351 // Wipe erased entries
352 for (unsigned int I
= 0; I
< List
.size();)
354 if (List
[I
].empty() == false)
357 List
.erase(List
.begin()+I
);
361 // WriteDatabase - Write the CDROM Database file /*{{{*/
362 // ---------------------------------------------------------------------
363 /* We rewrite the configuration class associated with the cdrom database. */
364 bool pkgCdrom::WriteDatabase(Configuration
&Cnf
)
366 string DFile
= _config
->FindFile("Dir::State::cdroms");
367 string NewFile
= DFile
+ ".new";
369 unlink(NewFile
.c_str());
370 ofstream
Out(NewFile
.c_str());
372 return _error
->Errno("ofstream::ofstream",
373 "Failed to open %s.new",DFile
.c_str());
375 /* Write out all of the configuration directives by walking the
376 configuration tree */
377 const Configuration::Item
*Top
= Cnf
.Tree(0);
380 // Print the config entry
381 if (Top
->Value
.empty() == false)
382 Out
<< Top
->FullTag() + " \"" << Top
->Value
<< "\";" << endl
;
390 while (Top
!= 0 && Top
->Next
== 0)
398 rename(DFile
.c_str(),string(DFile
+ '~').c_str());
399 if (rename(NewFile
.c_str(),DFile
.c_str()) != 0)
400 return _error
->Errno("rename","Failed to rename %s.new to %s",
401 DFile
.c_str(),DFile
.c_str());
406 // WriteSourceList - Write an updated sourcelist /*{{{*/
407 // ---------------------------------------------------------------------
408 /* This reads the old source list and copies it into the new one. It
409 appends the new CDROM entires just after the first block of comments.
410 This places them first in the file. It also removes any old entries
411 that were the same. */
412 bool pkgCdrom::WriteSourceList(string Name
,vector
<string
> &List
,bool Source
)
414 if (List
.size() == 0)
417 string File
= _config
->FindFile("Dir::Etc::sourcelist");
419 // Open the stream for reading
420 ifstream
F((FileExists(File
)?File
.c_str():"/dev/null"),
423 return _error
->Errno("ifstream::ifstream","Opening %s",File
.c_str());
425 string NewFile
= File
+ ".new";
426 unlink(NewFile
.c_str());
427 ofstream
Out(NewFile
.c_str());
429 return _error
->Errno("ofstream::ofstream",
430 "Failed to open %s.new",File
.c_str());
432 // Create a short uri without the path
433 string ShortURI
= "cdrom:[" + Name
+ "]/";
434 string ShortURI2
= "cdrom:" + Name
+ "/"; // For Compatibility
445 while (F
.eof() == false)
447 F
.getline(Buffer
,sizeof(Buffer
));
449 if (F
.fail() && !F
.eof())
450 return _error
->Error(_("Line %u too long in source list %s."),
451 CurLine
,File
.c_str());
452 _strtabexpand(Buffer
,sizeof(Buffer
));
456 if (Buffer
[0] == '#' || Buffer
[0] == 0)
458 Out
<< Buffer
<< endl
;
464 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
466 string::size_type Space
= (*I
).find(' ');
467 if (Space
== string::npos
)
468 return _error
->Error("Internal error");
469 Out
<< Type
<< " cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
470 " " << string(*I
,Space
+1) << endl
;
478 const char *C
= Buffer
;
479 if (ParseQuoteWord(C
,cType
) == false ||
480 ParseQuoteWord(C
,URI
) == false)
482 Out
<< Buffer
<< endl
;
486 // Emit lines like this one
487 if (cType
!= Type
|| (string(URI
,0,ShortURI
.length()) != ShortURI
&&
488 string(URI
,0,ShortURI
.length()) != ShortURI2
))
490 Out
<< Buffer
<< endl
;
495 // Just in case the file was empty
498 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
500 string::size_type Space
= (*I
).find(' ');
501 if (Space
== string::npos
)
502 return _error
->Error("Internal error");
504 Out
<< "deb cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
505 " " << string(*I
,Space
+1) << endl
;
511 rename(File
.c_str(),string(File
+ '~').c_str());
512 if (rename(NewFile
.c_str(),File
.c_str()) != 0)
513 return _error
->Errno("rename","Failed to rename %s.new to %s",
514 File
.c_str(),File
.c_str());
520 bool pkgCdrom::Ident(string
&ident
, pkgCdromStatus
*log
)
525 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
527 CDROM
= SafeGetCWD() + '/' + CDROM
;
531 ioprintf(msg
, _("Using CD-ROM mount point %s\nMounting CD-ROM\n"),
533 log
->Update(msg
.str());
535 if (MountCdrom(CDROM
) == false)
536 return _error
->Error("Failed to mount the cdrom.");
538 // Hash the CD to get an ID
540 log
->Update(_("Identifying.. "));
543 if (IdentCdrom(CDROM
,ident
) == false)
550 ioprintf(msg
, "[%s]\n",ident
.c_str());
551 log
->Update(msg
.str());
555 Configuration Database
;
556 string DFile
= _config
->FindFile("Dir::State::cdroms");
557 if (FileExists(DFile
) == true)
559 if (ReadConfigFile(Database
,DFile
) == false)
560 return _error
->Error("Unable to read the cdrom database %s",
565 ioprintf(msg
, _("Stored label: %s \n"),
566 Database
.Find("CD::"+ident
).c_str());
567 log
->Update(msg
.str());
573 bool pkgCdrom::Add(pkgCdromStatus
*log
)
578 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
580 CDROM
= SafeGetCWD() + '/' + CDROM
;
583 log
->SetTotal(STEP_LAST
);
585 ioprintf(msg
, _("Using CD-ROM mount point %s\n"), CDROM
.c_str());
586 log
->Update(msg
.str(), STEP_PREPARE
);
590 Configuration Database
;
591 string DFile
= _config
->FindFile("Dir::State::cdroms");
592 if (FileExists(DFile
) == true)
594 if (ReadConfigFile(Database
,DFile
) == false)
595 return _error
->Error("Unable to read the cdrom database %s",
599 // Unmount the CD and get the user to put in the one they want
600 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
603 log
->Update(_("Unmounting CD-ROM\n"), STEP_UNMOUNT
);
607 log
->Update(_("Waiting for disc...\n"), STEP_WAIT
);
608 if(!log
->ChangeCdrom()) {
614 // Mount the new CDROM
615 log
->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT
);
616 if (MountCdrom(CDROM
) == false)
617 return _error
->Error("Failed to mount the cdrom.");
620 // Hash the CD to get an ID
622 log
->Update(_("Identifying.. "), STEP_IDENT
);
624 if (IdentCdrom(CDROM
,ID
) == false)
630 log
->Update("["+ID
+"]\n");
633 log
->Update(_("Scanning disc for index files..\n"),STEP_SCAN
);
635 // Get the CD structure
637 vector
<string
> SourceList
;
638 vector
<string
> SigList
;
639 vector
<string
> TransList
;
640 string StartDir
= SafeGetCWD();
642 if (FindPackages(CDROM
,List
,SourceList
, SigList
,TransList
,InfoDir
,log
) == false)
648 chdir(StartDir
.c_str());
650 if (_config
->FindB("Debug::aptcdrom",false) == true)
652 cout
<< "I found (binary):" << endl
;
653 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
655 cout
<< "I found (source):" << endl
;
656 for (vector
<string
>::iterator I
= SourceList
.begin(); I
!= SourceList
.end(); I
++)
658 cout
<< "I found (Signatures):" << endl
;
659 for (vector
<string
>::iterator I
= SigList
.begin(); I
!= SigList
.end(); I
++)
663 //log->Update(_("Cleaning package lists..."), STEP_CLEAN);
666 DropBinaryArch(List
);
667 DropRepeats(List
,"Packages");
668 DropRepeats(SourceList
,"Sources");
669 DropRepeats(SigList
,"Release.gpg");
670 DropRepeats(TransList
,"");
673 ioprintf(msg
, _("Found %i package indexes, %i source indexes, "
674 "%i translation indexes and %i signatures\n"),
675 List
.size(), SourceList
.size(), TransList
.size(),
677 log
->Update(msg
.str(), STEP_SCAN
);
680 if (List
.size() == 0 && SourceList
.size() == 0)
683 return _error
->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
686 // Check if the CD is in the database
688 if (Database
.Exists("CD::" + ID
) == false ||
689 _config
->FindB("APT::CDROM::Rename",false) == true)
691 // Try to use the CDs label if at all possible
692 if (InfoDir
.empty() == false &&
693 FileExists(InfoDir
+ "/info") == true)
695 ifstream
F(string(InfoDir
+ "/info").c_str());
699 if (Name
.empty() == false)
701 // Escape special characters
702 string::iterator J
= Name
.begin();
703 for (; J
!= Name
.end(); J
++)
704 if (*J
== '"' || *J
== ']' || *J
== '[')
709 ioprintf(msg
, "Found label '%s'\n", Name
.c_str());
710 log
->Update(msg
.str());
712 Database
.Set("CD::" + ID
+ "::Label",Name
);
716 if (_config
->FindB("APT::CDROM::Rename",false) == true ||
717 Name
.empty() == true)
722 return _error
->Error("No disc name found and no way to ask for it");
726 if(!log
->AskCdromName(Name
)) {
730 cout
<< "Name: '" << Name
<< "'" << endl
;
732 if (Name
.empty() == false &&
733 Name
.find('"') == string::npos
&&
734 Name
.find('[') == string::npos
&&
735 Name
.find(']') == string::npos
)
737 log
->Update(_("That is not a valid name, try again.\n"));
742 Name
= Database
.Find("CD::" + ID
);
744 // Escape special characters
745 string::iterator J
= Name
.begin();
746 for (; J
!= Name
.end(); J
++)
747 if (*J
== '"' || *J
== ']' || *J
== '[')
750 Database
.Set("CD::" + ID
,Name
);
753 ioprintf(msg
, _("This disc is called: \n'%s'\n"), Name
.c_str());
754 log
->Update(msg
.str());
757 log
->Update(_("Copying package lists..."), STEP_COPY
);
758 // take care of the signatures and copy them if they are ok
759 // (we do this before PackageCopy as it modifies "List" and "SourceList")
760 SigVerify SignVerify
;
761 SignVerify
.CopyAndVerify(CDROM
, Name
, SigList
, List
, SourceList
);
763 // Copy the package files to the state directory
766 TranslationsCopy TransCopy
;
767 if (Copy
.CopyPackages(CDROM
,Name
,List
, log
) == false ||
768 SrcCopy
.CopyPackages(CDROM
,Name
,SourceList
, log
) == false ||
769 TransCopy
.CopyTranslations(CDROM
,Name
,TransList
, log
) == false)
772 // reduce the List so that it takes less space in sources.list
773 ReduceSourcelist(CDROM
,List
);
774 ReduceSourcelist(CDROM
,SourceList
);
776 // Write the database and sourcelist
777 if (_config
->FindB("APT::cdrom::NoAct",false) == false)
779 if (WriteDatabase(Database
) == false)
783 log
->Update(_("Writing new source list\n"), STEP_WRITE
);
785 if (WriteSourceList(Name
,List
,false) == false ||
786 WriteSourceList(Name
,SourceList
,true) == false)
790 // Print the sourcelist entries
792 log
->Update(_("Source list entries for this disc are:\n"));
794 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
796 string::size_type Space
= (*I
).find(' ');
797 if (Space
== string::npos
)
800 return _error
->Error("Internal error");
805 msg
<< "deb cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
806 " " << string(*I
,Space
+1) << endl
;
807 log
->Update(msg
.str());
811 for (vector
<string
>::iterator I
= SourceList
.begin(); I
!= SourceList
.end(); I
++)
813 string::size_type Space
= (*I
).find(' ');
814 if (Space
== string::npos
)
817 return _error
->Error("Internal error");
822 msg
<< "deb-src cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
823 " " << string(*I
,Space
+1) << endl
;
824 log
->Update(msg
.str());
830 // Unmount and finish
831 if (_config
->FindB("APT::CDROM::NoMount",false) == false) {
832 log
->Update(_("Unmounting CD-ROM..."), STEP_LAST
);