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
_strtabexpand(Buffer,sizeof(Buffer));
429
if (Buffer[0] == '#' || Buffer[0] == 0)
431
Out << Buffer << endl;
437
for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
439
string::size_type Space = (*I).find(' ');
440
if (Space == string::npos)
441
return _error->Error("Internal error");
442
Out << Type << " cdrom:[" << Name << "]/" << string(*I,0,Space) <<
443
" " << string(*I,Space+1) << endl;
451
const char *C = Buffer;
452
if (ParseQuoteWord(C,cType) == false ||
453
ParseQuoteWord(C,URI) == false)
455
Out << Buffer << endl;
459
// Emit lines like this one
460
if (cType != Type || (string(URI,0,ShortURI.length()) != ShortURI &&
461
string(URI,0,ShortURI.length()) != ShortURI2))
463
Out << Buffer << endl;
468
// Just in case the file was empty
471
for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
473
string::size_type Space = (*I).find(' ');
474
if (Space == string::npos)
475
return _error->Error("Internal error");
477
Out << "deb cdrom:[" << Name << "]/" << string(*I,0,Space) <<
478
" " << string(*I,Space+1) << endl;
484
rename(File.c_str(),string(File + '~').c_str());
485
if (rename(NewFile.c_str(),File.c_str()) != 0)
486
return _error->Errno("rename","Failed to rename %s.new to %s",
487
File.c_str(),File.c_str());
493
bool pkgCdrom::Ident(string &ident, pkgCdromStatus *log)
498
string CDROM = _config->FindDir("Acquire::cdrom::mount","/cdrom/");
500
CDROM= SafeGetCWD() + '/' + CDROM;
504
ioprintf(msg, _("Using CD-ROM mount point %s\nMounting CD-ROM\n"),
506
log->Update(msg.str());
508
if (MountCdrom(CDROM) == false)
509
return _error->Error("Failed to mount the cdrom.");
511
// Hash the CD to get an ID
513
log->Update(_("Identifying.. "));
516
if (IdentCdrom(CDROM,ident) == false)
523
ioprintf(msg, "[%s]\n",ident.c_str());
524
log->Update(msg.str());
528
Configuration Database;
529
string DFile = _config->FindFile("Dir::State::cdroms");
530
if (FileExists(DFile) == true)
532
if (ReadConfigFile(Database,DFile) == false)
533
return _error->Error("Unable to read the cdrom database %s",
538
ioprintf(msg, _("Stored label: %s \n"),
539
Database.Find("CD::"+ident).c_str());
540
log->Update(msg.str());
546
bool pkgCdrom::Add(pkgCdromStatus *log)
551
string CDROM = _config->FindDir("Acquire::cdrom::mount","/cdrom/");
553
CDROM= SafeGetCWD() + '/' + CDROM;
556
log->SetTotal(STEP_LAST);
558
ioprintf(msg, _("Using CD-ROM mount point %s\n"), CDROM.c_str());
559
log->Update(msg.str(), STEP_PREPARE);
563
Configuration Database;
564
string DFile = _config->FindFile("Dir::State::cdroms");
565
if (FileExists(DFile) == true)
567
if (ReadConfigFile(Database,DFile) == false)
568
return _error->Error("Unable to read the cdrom database %s",
572
// Unmount the CD and get the user to put in the one they want
573
if (_config->FindB("APT::CDROM::NoMount",false) == false)
576
log->Update(_("Unmounting CD-ROM\n"), STEP_UNMOUNT);
580
log->Update(_("Waiting for disc...\n"), STEP_WAIT);
581
if(!log->ChangeCdrom()) {
587
// Mount the new CDROM
588
log->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT);
589
if (MountCdrom(CDROM) == false)
590
return _error->Error("Failed to mount the cdrom.");
593
// Hash the CD to get an ID
595
log->Update(_("Identifying.. "), STEP_IDENT);
597
if (IdentCdrom(CDROM,ID) == false)
603
log->Update("["+ID+"]\n");
606
log->Update(_("Scanning disc for index files..\n"),STEP_SCAN);
608
// Get the CD structure
610
vector<string> SourceList;
611
vector<string> SigList;
612
string StartDir = SafeGetCWD();
614
if (FindPackages(CDROM,List,SourceList, SigList,InfoDir,log) == false)
620
chdir(StartDir.c_str());
622
if (_config->FindB("Debug::aptcdrom",false) == true)
624
cout << "I found (binary):" << endl;
625
for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
627
cout << "I found (source):" << endl;
628
for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); I++)
630
cout << "I found (Signatures):" << endl;
631
for (vector<string>::iterator I = SigList.begin(); I != SigList.end(); I++)
635
//log->Update(_("Cleaning package lists..."), STEP_CLEAN);
638
DropBinaryArch(List);
639
DropRepeats(List,"Packages");
640
DropRepeats(SourceList,"Sources");
641
DropRepeats(SigList,"Release.gpg");
644
ioprintf(msg, _("Found %i package indexes, %i source indexes and "
646
List.size(), SourceList.size(), SigList.size());
647
log->Update(msg.str(), STEP_SCAN);
650
if (List.size() == 0 && SourceList.size() == 0)
651
return _error->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
653
// Check if the CD is in the database
655
if (Database.Exists("CD::" + ID) == false ||
656
_config->FindB("APT::CDROM::Rename",false) == true)
658
// Try to use the CDs label if at all possible
659
if (InfoDir.empty() == false &&
660
FileExists(InfoDir + "/info") == true)
662
ifstream F(string(InfoDir + "/info").c_str());
666
if (Name.empty() == false)
668
// Escape special characters
669
string::iterator J = Name.begin();
670
for (; J != Name.end(); J++)
671
if (*J == '"' || *J == ']' || *J == '[')
676
ioprintf(msg, "Found label '%s'\n", Name.c_str());
677
log->Update(msg.str());
679
Database.Set("CD::" + ID + "::Label",Name);
683
if (_config->FindB("APT::CDROM::Rename",false) == true ||
684
Name.empty() == true)
687
return _error->Error("No disc name found and no way to ask for it");
690
if(!log->AskCdromName(Name)) {
694
cout << "Name: '" << Name << "'" << endl;
696
if (Name.empty() == false &&
697
Name.find('"') == string::npos &&
698
Name.find('[') == string::npos &&
699
Name.find(']') == string::npos)
701
log->Update(_("That is not a valid name, try again.\n"));
706
Name = Database.Find("CD::" + ID);
708
// Escape special characters
709
string::iterator J = Name.begin();
710
for (; J != Name.end(); J++)
711
if (*J == '"' || *J == ']' || *J == '[')
714
Database.Set("CD::" + ID,Name);
717
ioprintf(msg, _("This disc is called: \n'%s'\n"), Name.c_str());
718
log->Update(msg.str());
721
log->Update(_("Copying package lists..."), STEP_COPY);
722
// take care of the signatures and copy them if they are ok
723
// (we do this before PackageCopy as it modifies "List" and "SourceList")
724
SigVerify SignVerify;
725
SignVerify.CopyAndVerify(CDROM, Name, SigList, List, SourceList);
727
// Copy the package files to the state directory
730
if (Copy.CopyPackages(CDROM,Name,List, log) == false ||
731
SrcCopy.CopyPackages(CDROM,Name,SourceList, log) == false)
734
// reduce the List so that it takes less space in sources.list
735
ReduceSourcelist(CDROM,List);
736
ReduceSourcelist(CDROM,SourceList);
738
// Write the database and sourcelist
739
if (_config->FindB("APT::cdrom::NoAct",false) == false)
741
if (WriteDatabase(Database) == false)
745
log->Update(_("Writing new source list\n"), STEP_WRITE);
747
if (WriteSourceList(Name,List,false) == false ||
748
WriteSourceList(Name,SourceList,true) == false)
752
// Print the sourcelist entries
754
log->Update(_("Source list entries for this disc are:\n"));
756
for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
758
string::size_type Space = (*I).find(' ');
759
if (Space == string::npos)
760
return _error->Error("Internal error");
764
msg << "deb cdrom:[" << Name << "]/" << string(*I,0,Space) <<
765
" " << string(*I,Space+1) << endl;
766
log->Update(msg.str());
770
for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); I++)
772
string::size_type Space = (*I).find(' ');
773
if (Space == string::npos)
774
return _error->Error("Internal error");
778
msg << "deb-src cdrom:[" << Name << "]/" << string(*I,0,Space) <<
779
" " << string(*I,Space+1) << endl;
780
log->Update(msg.str());
786
// Unmount and finish
787
if (_config->FindB("APT::CDROM::NoMount",false) == false) {
788
log->Update(_("Unmounting CD-ROM..."), STEP_LAST);