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 _strtabexpand(Buffer
,sizeof(Buffer
));
453 if (Buffer
[0] == '#' || Buffer
[0] == 0)
455 Out
<< Buffer
<< endl
;
461 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
463 string::size_type Space
= (*I
).find(' ');
464 if (Space
== string::npos
)
465 return _error
->Error("Internal error");
466 Out
<< Type
<< " cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
467 " " << string(*I
,Space
+1) << endl
;
475 const char *C
= Buffer
;
476 if (ParseQuoteWord(C
,cType
) == false ||
477 ParseQuoteWord(C
,URI
) == false)
479 Out
<< Buffer
<< endl
;
483 // Emit lines like this one
484 if (cType
!= Type
|| (string(URI
,0,ShortURI
.length()) != ShortURI
&&
485 string(URI
,0,ShortURI
.length()) != ShortURI2
))
487 Out
<< Buffer
<< endl
;
492 // Just in case the file was empty
495 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
497 string::size_type Space
= (*I
).find(' ');
498 if (Space
== string::npos
)
499 return _error
->Error("Internal error");
501 Out
<< "deb cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
502 " " << string(*I
,Space
+1) << endl
;
508 rename(File
.c_str(),string(File
+ '~').c_str());
509 if (rename(NewFile
.c_str(),File
.c_str()) != 0)
510 return _error
->Errno("rename","Failed to rename %s.new to %s",
511 File
.c_str(),File
.c_str());
517 bool pkgCdrom::Ident(string
&ident
, pkgCdromStatus
*log
)
522 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
524 CDROM
= SafeGetCWD() + '/' + CDROM
;
528 ioprintf(msg
, _("Using CD-ROM mount point %s\nMounting CD-ROM\n"),
530 log
->Update(msg
.str());
532 if (MountCdrom(CDROM
) == false)
533 return _error
->Error("Failed to mount the cdrom.");
535 // Hash the CD to get an ID
537 log
->Update(_("Identifying.. "));
540 if (IdentCdrom(CDROM
,ident
) == false)
547 ioprintf(msg
, "[%s]\n",ident
.c_str());
548 log
->Update(msg
.str());
552 Configuration Database
;
553 string DFile
= _config
->FindFile("Dir::State::cdroms");
554 if (FileExists(DFile
) == true)
556 if (ReadConfigFile(Database
,DFile
) == false)
557 return _error
->Error("Unable to read the cdrom database %s",
562 ioprintf(msg
, _("Stored label: %s \n"),
563 Database
.Find("CD::"+ident
).c_str());
564 log
->Update(msg
.str());
570 bool pkgCdrom::Add(pkgCdromStatus
*log
)
575 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
577 CDROM
= SafeGetCWD() + '/' + CDROM
;
580 log
->SetTotal(STEP_LAST
);
582 ioprintf(msg
, _("Using CD-ROM mount point %s\n"), CDROM
.c_str());
583 log
->Update(msg
.str(), STEP_PREPARE
);
587 Configuration Database
;
588 string DFile
= _config
->FindFile("Dir::State::cdroms");
589 if (FileExists(DFile
) == true)
591 if (ReadConfigFile(Database
,DFile
) == false)
592 return _error
->Error("Unable to read the cdrom database %s",
596 // Unmount the CD and get the user to put in the one they want
597 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
600 log
->Update(_("Unmounting CD-ROM\n"), STEP_UNMOUNT
);
604 log
->Update(_("Waiting for disc...\n"), STEP_WAIT
);
605 if(!log
->ChangeCdrom()) {
611 // Mount the new CDROM
612 log
->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT
);
613 if (MountCdrom(CDROM
) == false)
614 return _error
->Error("Failed to mount the cdrom.");
617 // Hash the CD to get an ID
619 log
->Update(_("Identifying.. "), STEP_IDENT
);
621 if (IdentCdrom(CDROM
,ID
) == false)
627 log
->Update("["+ID
+"]\n");
630 log
->Update(_("Scanning disc for index files..\n"),STEP_SCAN
);
632 // Get the CD structure
634 vector
<string
> SourceList
;
635 vector
<string
> SigList
;
636 vector
<string
> TransList
;
637 string StartDir
= SafeGetCWD();
639 if (FindPackages(CDROM
,List
,SourceList
, SigList
,TransList
,InfoDir
,log
) == false)
645 chdir(StartDir
.c_str());
647 if (_config
->FindB("Debug::aptcdrom",false) == true)
649 cout
<< "I found (binary):" << endl
;
650 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
652 cout
<< "I found (source):" << endl
;
653 for (vector
<string
>::iterator I
= SourceList
.begin(); I
!= SourceList
.end(); I
++)
655 cout
<< "I found (Signatures):" << endl
;
656 for (vector
<string
>::iterator I
= SigList
.begin(); I
!= SigList
.end(); I
++)
660 //log->Update(_("Cleaning package lists..."), STEP_CLEAN);
663 DropBinaryArch(List
);
664 DropRepeats(List
,"Packages");
665 DropRepeats(SourceList
,"Sources");
666 DropRepeats(SigList
,"Release.gpg");
667 DropRepeats(TransList
,"");
670 ioprintf(msg
, _("Found %i package indexes, %i source indexes, "
671 "%i translation indexes and %i signatures\n"),
672 List
.size(), SourceList
.size(), TransList
.size(),
674 log
->Update(msg
.str(), STEP_SCAN
);
677 if (List
.size() == 0 && SourceList
.size() == 0)
678 return _error
->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
680 // Check if the CD is in the database
682 if (Database
.Exists("CD::" + ID
) == false ||
683 _config
->FindB("APT::CDROM::Rename",false) == true)
685 // Try to use the CDs label if at all possible
686 if (InfoDir
.empty() == false &&
687 FileExists(InfoDir
+ "/info") == true)
689 ifstream
F(string(InfoDir
+ "/info").c_str());
693 if (Name
.empty() == false)
695 // Escape special characters
696 string::iterator J
= Name
.begin();
697 for (; J
!= Name
.end(); J
++)
698 if (*J
== '"' || *J
== ']' || *J
== '[')
703 ioprintf(msg
, "Found label '%s'\n", Name
.c_str());
704 log
->Update(msg
.str());
706 Database
.Set("CD::" + ID
+ "::Label",Name
);
710 if (_config
->FindB("APT::CDROM::Rename",false) == true ||
711 Name
.empty() == true)
714 return _error
->Error("No disc name found and no way to ask for it");
717 if(!log
->AskCdromName(Name
)) {
721 cout
<< "Name: '" << Name
<< "'" << endl
;
723 if (Name
.empty() == false &&
724 Name
.find('"') == string::npos
&&
725 Name
.find('[') == string::npos
&&
726 Name
.find(']') == string::npos
)
728 log
->Update(_("That is not a valid name, try again.\n"));
733 Name
= Database
.Find("CD::" + ID
);
735 // Escape special characters
736 string::iterator J
= Name
.begin();
737 for (; J
!= Name
.end(); J
++)
738 if (*J
== '"' || *J
== ']' || *J
== '[')
741 Database
.Set("CD::" + ID
,Name
);
744 ioprintf(msg
, _("This disc is called: \n'%s'\n"), Name
.c_str());
745 log
->Update(msg
.str());
748 log
->Update(_("Copying package lists..."), STEP_COPY
);
749 // take care of the signatures and copy them if they are ok
750 // (we do this before PackageCopy as it modifies "List" and "SourceList")
751 SigVerify SignVerify
;
752 SignVerify
.CopyAndVerify(CDROM
, Name
, SigList
, List
, SourceList
);
754 // Copy the package files to the state directory
757 TranslationsCopy TransCopy
;
758 if (Copy
.CopyPackages(CDROM
,Name
,List
, log
) == false ||
759 SrcCopy
.CopyPackages(CDROM
,Name
,SourceList
, log
) == false ||
760 TransCopy
.CopyTranslations(CDROM
,Name
,TransList
, log
) == false)
763 // reduce the List so that it takes less space in sources.list
764 ReduceSourcelist(CDROM
,List
);
765 ReduceSourcelist(CDROM
,SourceList
);
767 // Write the database and sourcelist
768 if (_config
->FindB("APT::cdrom::NoAct",false) == false)
770 if (WriteDatabase(Database
) == false)
774 log
->Update(_("Writing new source list\n"), STEP_WRITE
);
776 if (WriteSourceList(Name
,List
,false) == false ||
777 WriteSourceList(Name
,SourceList
,true) == false)
781 // Print the sourcelist entries
783 log
->Update(_("Source list entries for this disc are:\n"));
785 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
787 string::size_type Space
= (*I
).find(' ');
788 if (Space
== string::npos
)
789 return _error
->Error("Internal error");
793 msg
<< "deb cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
794 " " << string(*I
,Space
+1) << endl
;
795 log
->Update(msg
.str());
799 for (vector
<string
>::iterator I
= SourceList
.begin(); I
!= SourceList
.end(); I
++)
801 string::size_type Space
= (*I
).find(' ');
802 if (Space
== string::npos
)
803 return _error
->Error("Internal error");
807 msg
<< "deb-src cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
808 " " << string(*I
,Space
+1) << endl
;
809 log
->Update(msg
.str());
815 // Unmount and finish
816 if (_config
->FindB("APT::CDROM::NoMount",false) == false) {
817 log
->Update(_("Unmounting CD-ROM..."), STEP_LAST
);