]> git.saurik.com Git - apt.git/blame - apt-pkg/cdrom.cc
apt-cdrom should succeed if any drive succeeds
[apt.git] / apt-pkg / cdrom.cc
CommitLineData
a75c6a6e
MZ
1/*
2 */
ea542140 3#include<config.h>
a75c6a6e 4
a75c6a6e
MZ
5#include<apt-pkg/init.h>
6#include<apt-pkg/error.h>
7#include<apt-pkg/cdromutl.h>
8#include<apt-pkg/strutl.h>
9#include<apt-pkg/cdrom.h>
5dd4c8b8 10#include<apt-pkg/aptconfiguration.h>
472ff00e
DK
11#include<apt-pkg/configuration.h>
12#include<apt-pkg/fileutl.h>
5dd4c8b8 13
a75c6a6e
MZ
14#include<sstream>
15#include<fstream>
a75c6a6e
MZ
16#include <sys/stat.h>
17#include <fcntl.h>
18#include <dirent.h>
19#include <unistd.h>
20#include <stdio.h>
152ab79e 21#include <algorithm>
cbc9bed8 22#include <dlfcn.h>
a75c6a6e
MZ
23
24#include "indexcopy.h"
25
ea542140
DK
26#include<apti18n.h>
27
a75c6a6e
MZ
28using namespace std;
29
30// FindPackages - Find the package files on the CDROM /*{{{*/
31// ---------------------------------------------------------------------
32/* We look over the cdrom for package files. This is a recursive
33 search that short circuits when it his a package file in the dir.
34 This speeds it up greatly as the majority of the size is in the
35 binary-* sub dirs. */
22f8568d
MV
36bool pkgCdrom::FindPackages(string CD,
37 vector<string> &List,
38 vector<string> &SList,
39 vector<string> &SigList,
40 vector<string> &TransList,
a75c6a6e
MZ
41 string &InfoDir, pkgCdromStatus *log,
42 unsigned int Depth)
43{
44 static ino_t Inodes[9];
22f8568d 45 DIR *D;
a75c6a6e
MZ
46
47 // if we have a look we "pulse" now
48 if(log)
49 log->Update();
50
51 if (Depth >= 7)
52 return true;
53
54 if (CD[CD.length()-1] != '/')
55 CD += '/';
56
57 if (chdir(CD.c_str()) != 0)
58 return _error->Errno("chdir","Unable to change to %s",CD.c_str());
59
60 // Look for a .disk subdirectory
711078ae 61 if (DirectoryExists(".disk") == true)
a75c6a6e
MZ
62 {
63 if (InfoDir.empty() == true)
64 InfoDir = CD + ".disk/";
65 }
66
67 // Don't look into directories that have been marked to ingore.
711078ae 68 if (RealFileExists(".aptignr") == true)
a75c6a6e
MZ
69 return true;
70
a75c6a6e
MZ
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
73 the scanning
74 */
212080b8 75 if (RealFileExists("Release.gpg") == true || RealFileExists("InRelease") == true)
a75c6a6e
MZ
76 {
77 SigList.push_back(CD);
78 }
82c8f08e 79
a75c6a6e
MZ
80 /* Aha! We found some package files. We assume that everything under
81 this dir is controlled by those package files so we don't look down
82 anymore */
78c9276d
DK
83 std::vector<APT::Configuration::Compressor> const compressor = APT::Configuration::getCompressors();
84 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
85 c != compressor.end(); ++c)
86 {
711078ae 87 if (RealFileExists(std::string("Packages").append(c->Extension).c_str()) == false)
78c9276d
DK
88 continue;
89
90 if (_config->FindB("Debug::aptcdrom",false) == true)
91 std::clog << "Found Packages in " << CD << std::endl;
92 List.push_back(CD);
93
94 // Continue down if thorough is given
95 if (_config->FindB("APT::CDROM::Thorough",false) == false)
96 return true;
97 break;
98 }
99 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
100 c != compressor.end(); ++c)
101 {
711078ae 102 if (RealFileExists(std::string("Sources").append(c->Extension).c_str()) == false)
78c9276d
DK
103 continue;
104
105 if (_config->FindB("Debug::aptcdrom",false) == true)
106 std::clog << "Found Sources in " << CD << std::endl;
107 SList.push_back(CD);
108
109 // Continue down if thorough is given
110 if (_config->FindB("APT::CDROM::Thorough",false) == false)
111 return true;
112 break;
113 }
22f8568d 114
01366a44 115 // see if we find translation indices
78c9276d 116 if (DirectoryExists("i18n") == true)
22f8568d
MV
117 {
118 D = opendir("i18n");
119 for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
120 {
78c9276d
DK
121 if(strncmp(Dir->d_name, "Translation-", strlen("Translation-")) != 0)
122 continue;
123 string file = Dir->d_name;
124 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
125 c != compressor.end(); ++c)
22f8568d 126 {
78c9276d
DK
127 string fileext = flExtension(file);
128 if (file == fileext)
129 fileext.clear();
130 else if (fileext.empty() == false)
131 fileext = "." + fileext;
132
133 if (c->Extension == fileext)
134 {
135 if (_config->FindB("Debug::aptcdrom",false) == true)
136 std::clog << "Found translation " << Dir->d_name << " in " << CD << "i18n/" << std::endl;
4e86b003 137 file.erase(file.size() - fileext.size());
78c9276d
DK
138 TransList.push_back(CD + "i18n/" + file);
139 break;
140 }
22f8568d
MV
141 }
142 }
143 closedir(D);
144 }
145
22f8568d 146 D = opendir(".");
a75c6a6e
MZ
147 if (D == 0)
148 return _error->Errno("opendir","Unable to read %s",CD.c_str());
149
150 // Run over the directory
151 for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
152 {
153 // Skip some files..
154 if (strcmp(Dir->d_name,".") == 0 ||
155 strcmp(Dir->d_name,"..") == 0 ||
156 //strcmp(Dir->d_name,"source") == 0 ||
157 strcmp(Dir->d_name,".disk") == 0 ||
158 strcmp(Dir->d_name,"experimental") == 0 ||
159 strcmp(Dir->d_name,"binary-all") == 0 ||
160 strcmp(Dir->d_name,"debian-installer") == 0)
161 continue;
162
163 // See if the name is a sub directory
164 struct stat Buf;
165 if (stat(Dir->d_name,&Buf) != 0)
166 continue;
167
168 if (S_ISDIR(Buf.st_mode) == 0)
169 continue;
170
171 unsigned int I;
172 for (I = 0; I != Depth; I++)
173 if (Inodes[I] == Buf.st_ino)
174 break;
175 if (I != Depth)
176 continue;
177
178 // Store the inodes weve seen
179 Inodes[Depth] = Buf.st_ino;
180
181 // Descend
22f8568d 182 if (FindPackages(CD + Dir->d_name,List,SList,SigList,TransList,InfoDir,log,Depth+1) == false)
a75c6a6e
MZ
183 break;
184
185 if (chdir(CD.c_str()) != 0)
6070a346
DK
186 {
187 _error->Errno("chdir","Unable to change to %s", CD.c_str());
188 closedir(D);
189 return false;
190 }
a75c6a6e
MZ
191 };
192
193 closedir(D);
194
195 return !_error->PendingError();
196}
92fcbfc1 197 /*}}}*/
a75c6a6e
MZ
198// Score - We compute a 'score' for a path /*{{{*/
199// ---------------------------------------------------------------------
200/* Paths are scored based on how close they come to what I consider
201 normal. That is ones that have 'dist' 'stable' 'testing' will score
202 higher than ones without. */
203int pkgCdrom::Score(string Path)
204{
205 int Res = 0;
206 if (Path.find("stable/") != string::npos)
207 Res += 29;
208 if (Path.find("/binary-") != string::npos)
209 Res += 20;
210 if (Path.find("testing/") != string::npos)
211 Res += 28;
212 if (Path.find("unstable/") != string::npos)
213 Res += 27;
214 if (Path.find("/dists/") != string::npos)
215 Res += 40;
216 if (Path.find("/main/") != string::npos)
217 Res += 20;
218 if (Path.find("/contrib/") != string::npos)
219 Res += 20;
220 if (Path.find("/non-free/") != string::npos)
221 Res += 20;
222 if (Path.find("/non-US/") != string::npos)
223 Res += 20;
224 if (Path.find("/source/") != string::npos)
225 Res += 10;
226 if (Path.find("/debian/") != string::npos)
227 Res -= 10;
872b3d39
MV
228
229 // check for symlinks in the patch leading to the actual file
230 // a symlink gets a big penalty
231 struct stat Buf;
232 string statPath = flNotFile(Path);
710aba4a 233 string cdromPath = _config->FindDir("Acquire::cdrom::mount");
872b3d39
MV
234 while(statPath != cdromPath && statPath != "./") {
235 statPath.resize(statPath.size()-1); // remove the trailing '/'
236 if (lstat(statPath.c_str(),&Buf) == 0) {
237 if(S_ISLNK(Buf.st_mode)) {
238 Res -= 60;
239 break;
240 }
241 }
242 statPath = flNotFile(statPath); // descent
243 }
244
a75c6a6e
MZ
245 return Res;
246}
a75c6a6e
MZ
247 /*}}}*/
248// DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
249// ---------------------------------------------------------------------
250/* Here we drop everything that is not this machines arch */
251bool pkgCdrom::DropBinaryArch(vector<string> &List)
252{
5dd4c8b8 253
a75c6a6e
MZ
254 for (unsigned int I = 0; I < List.size(); I++)
255 {
256 const char *Str = List[I].c_str();
5dd4c8b8
DK
257 const char *Start, *End;
258 if ((Start = strstr(Str,"/binary-")) == 0)
a75c6a6e
MZ
259 continue;
260
5dd4c8b8
DK
261 // Between Start and End is the architecture
262 Start += 8;
263 if ((End = strstr(Start,"/")) != 0 && Start != End &&
73dfa041 264 APT::Configuration::checkArchitecture(string(Start, End)) == true)
5dd4c8b8
DK
265 continue; // okay, architecture is accepted
266
267 // not accepted -> Erase it
a75c6a6e 268 List.erase(List.begin() + I);
5dd4c8b8 269 --I; // the next entry is at the same index after the erase
a75c6a6e
MZ
270 }
271
c45233ea
DK
272 return true;
273}
274 /*}}}*/
275// DropTranslation - Dump unwanted Translation-<lang> files /*{{{*/
276// ---------------------------------------------------------------------
277/* Here we drop everything that is not configured in Acquire::Languages */
278bool pkgCdrom::DropTranslation(vector<string> &List)
279{
280 for (unsigned int I = 0; I < List.size(); I++)
281 {
282 const char *Start;
283 if ((Start = strstr(List[I].c_str(), "/Translation-")) == NULL)
284 continue;
285 Start += strlen("/Translation-");
286
287 if (APT::Configuration::checkLanguage(Start, true) == true)
288 continue;
289
290 // not accepted -> Erase it
291 List.erase(List.begin() + I);
292 --I; // the next entry is at the same index after the erase
293 }
294
a75c6a6e
MZ
295 return true;
296}
92fcbfc1 297 /*}}}*/
a75c6a6e
MZ
298// DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
299// ---------------------------------------------------------------------
300/* Here we go and stat every file that we found and strip dup inodes. */
301bool pkgCdrom::DropRepeats(vector<string> &List,const char *Name)
302{
2c405a44 303 bool couldFindAllFiles = true;
a75c6a6e
MZ
304 // Get a list of all the inodes
305 ino_t *Inodes = new ino_t[List.size()];
78c9276d 306 for (unsigned int I = 0; I != List.size(); ++I)
a75c6a6e
MZ
307 {
308 struct stat Buf;
78c9276d
DK
309 bool found = false;
310
311 std::vector<APT::Configuration::Compressor> const compressor = APT::Configuration::getCompressors();
312 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
313 c != compressor.end(); ++c)
82c8f08e 314 {
78c9276d 315 std::string filename = std::string(List[I]).append(Name).append(c->Extension);
82c8f08e 316 if (stat(filename.c_str(), &Buf) != 0)
78c9276d
DK
317 continue;
318 Inodes[I] = Buf.st_ino;
319 found = true;
320 break;
82c8f08e 321 }
78c9276d
DK
322
323 if (found == false)
2c405a44
DK
324 {
325 _error->Errno("stat","Failed to stat %s%s",List[I].c_str(), Name);
326 couldFindAllFiles = false;
327 Inodes[I] = 0;
328 }
a75c6a6e 329 }
78c9276d 330
a75c6a6e
MZ
331 // Look for dups
332 for (unsigned int I = 0; I != List.size(); I++)
333 {
2c405a44
DK
334 if (Inodes[I] == 0)
335 continue;
a75c6a6e
MZ
336 for (unsigned int J = I+1; J < List.size(); J++)
337 {
338 // No match
2c405a44 339 if (Inodes[J] == 0 || Inodes[J] != Inodes[I])
a75c6a6e
MZ
340 continue;
341
342 // We score the two paths.. and erase one
343 int ScoreA = Score(List[I]);
344 int ScoreB = Score(List[J]);
345 if (ScoreA < ScoreB)
346 {
347 List[I] = string();
348 break;
349 }
350
351 List[J] = string();
352 }
353 }
3a4477a4
DK
354 delete[] Inodes;
355
a75c6a6e
MZ
356 // Wipe erased entries
357 for (unsigned int I = 0; I < List.size();)
358 {
359 if (List[I].empty() == false)
360 I++;
361 else
362 List.erase(List.begin()+I);
363 }
364
2c405a44 365 return couldFindAllFiles;
a75c6a6e
MZ
366}
367 /*}}}*/
a75c6a6e
MZ
368// ReduceSourceList - Takes the path list and reduces it /*{{{*/
369// ---------------------------------------------------------------------
370/* This takes the list of source list expressed entires and collects
371 similar ones to form a single entry for each dist */
372void pkgCdrom::ReduceSourcelist(string CD,vector<string> &List)
373{
374 sort(List.begin(),List.end());
375
376 // Collect similar entries
f7f0d6c7 377 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
378 {
379 // Find a space..
380 string::size_type Space = (*I).find(' ');
381 if (Space == string::npos)
382 continue;
383 string::size_type SSpace = (*I).find(' ',Space + 1);
384 if (SSpace == string::npos)
385 continue;
386
387 string Word1 = string(*I,Space,SSpace-Space);
388 string Prefix = string(*I,0,Space);
8628c2f7 389 string Component = string(*I,SSpace);
f7f0d6c7 390 for (vector<string>::iterator J = List.begin(); J != I; ++J)
a75c6a6e
MZ
391 {
392 // Find a space..
393 string::size_type Space2 = (*J).find(' ');
394 if (Space2 == string::npos)
395 continue;
396 string::size_type SSpace2 = (*J).find(' ',Space2 + 1);
397 if (SSpace2 == string::npos)
398 continue;
399
400 if (string(*J,0,Space2) != Prefix)
401 continue;
402 if (string(*J,Space2,SSpace2-Space2) != Word1)
403 continue;
8628c2f7
DK
404
405 string Component2 = string(*J, SSpace2) + " ";
406 if (Component2.find(Component + " ") == std::string::npos)
407 *J += Component;
408 I->clear();
a75c6a6e
MZ
409 }
410 }
411
412 // Wipe erased entries
413 for (unsigned int I = 0; I < List.size();)
414 {
415 if (List[I].empty() == false)
416 I++;
417 else
418 List.erase(List.begin()+I);
419 }
420}
421 /*}}}*/
422// WriteDatabase - Write the CDROM Database file /*{{{*/
423// ---------------------------------------------------------------------
424/* We rewrite the configuration class associated with the cdrom database. */
425bool pkgCdrom::WriteDatabase(Configuration &Cnf)
426{
427 string DFile = _config->FindFile("Dir::State::cdroms");
428 string NewFile = DFile + ".new";
429
430 unlink(NewFile.c_str());
431 ofstream Out(NewFile.c_str());
432 if (!Out)
433 return _error->Errno("ofstream::ofstream",
434 "Failed to open %s.new",DFile.c_str());
435
436 /* Write out all of the configuration directives by walking the
437 configuration tree */
ab59c1ca 438 Cnf.Dump(Out, NULL, "%f \"%v\";\n", false);
a75c6a6e
MZ
439
440 Out.close();
849e64ac 441
c1cd1ac2
DK
442 if (FileExists(DFile) == true)
443 rename(DFile.c_str(), string(DFile + '~').c_str());
a75c6a6e
MZ
444 if (rename(NewFile.c_str(),DFile.c_str()) != 0)
445 return _error->Errno("rename","Failed to rename %s.new to %s",
446 DFile.c_str(),DFile.c_str());
447
448 return true;
449}
450 /*}}}*/
451// WriteSourceList - Write an updated sourcelist /*{{{*/
452// ---------------------------------------------------------------------
453/* This reads the old source list and copies it into the new one. It
454 appends the new CDROM entires just after the first block of comments.
455 This places them first in the file. It also removes any old entries
456 that were the same. */
457bool pkgCdrom::WriteSourceList(string Name,vector<string> &List,bool Source)
458{
f7f0d6c7 459 if (List.empty() == true)
a75c6a6e
MZ
460 return true;
461
462 string File = _config->FindFile("Dir::Etc::sourcelist");
463
464 // Open the stream for reading
465 ifstream F((FileExists(File)?File.c_str():"/dev/null"),
466 ios::in );
467 if (!F != 0)
468 return _error->Errno("ifstream::ifstream","Opening %s",File.c_str());
469
470 string NewFile = File + ".new";
471 unlink(NewFile.c_str());
472 ofstream Out(NewFile.c_str());
473 if (!Out)
474 return _error->Errno("ofstream::ofstream",
475 "Failed to open %s.new",File.c_str());
476
477 // Create a short uri without the path
478 string ShortURI = "cdrom:[" + Name + "]/";
479 string ShortURI2 = "cdrom:" + Name + "/"; // For Compatibility
480
481 string Type;
482 if (Source == true)
483 Type = "deb-src";
484 else
485 Type = "deb";
486
487 char Buffer[300];
488 int CurLine = 0;
489 bool First = true;
490 while (F.eof() == false)
491 {
492 F.getline(Buffer,sizeof(Buffer));
493 CurLine++;
13e8426f
MV
494 if (F.fail() && !F.eof())
495 return _error->Error(_("Line %u too long in source list %s."),
496 CurLine,File.c_str());
a75c6a6e
MZ
497 _strtabexpand(Buffer,sizeof(Buffer));
498 _strstrip(Buffer);
499
500 // Comment or blank
501 if (Buffer[0] == '#' || Buffer[0] == 0)
502 {
503 Out << Buffer << endl;
504 continue;
505 }
506
507 if (First == true)
508 {
f7f0d6c7 509 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
510 {
511 string::size_type Space = (*I).find(' ');
512 if (Space == string::npos)
513 return _error->Error("Internal error");
514 Out << Type << " cdrom:[" << Name << "]/" << string(*I,0,Space) <<
515 " " << string(*I,Space+1) << endl;
516 }
517 }
518 First = false;
519
520 // Grok it
521 string cType;
522 string URI;
523 const char *C = Buffer;
524 if (ParseQuoteWord(C,cType) == false ||
525 ParseQuoteWord(C,URI) == false)
526 {
527 Out << Buffer << endl;
528 continue;
529 }
530
531 // Emit lines like this one
532 if (cType != Type || (string(URI,0,ShortURI.length()) != ShortURI &&
533 string(URI,0,ShortURI.length()) != ShortURI2))
534 {
535 Out << Buffer << endl;
536 continue;
537 }
538 }
539
540 // Just in case the file was empty
541 if (First == true)
542 {
f7f0d6c7 543 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
544 {
545 string::size_type Space = (*I).find(' ');
546 if (Space == string::npos)
547 return _error->Error("Internal error");
548
549 Out << "deb cdrom:[" << Name << "]/" << string(*I,0,Space) <<
550 " " << string(*I,Space+1) << endl;
551 }
552 }
553
554 Out.close();
555
556 rename(File.c_str(),string(File + '~').c_str());
557 if (rename(NewFile.c_str(),File.c_str()) != 0)
558 return _error->Errno("rename","Failed to rename %s.new to %s",
559 File.c_str(),File.c_str());
560
561 return true;
562}
92fcbfc1
DK
563 /*}}}*/
564bool pkgCdrom::Ident(string &ident, pkgCdromStatus *log) /*{{{*/
a75c6a6e
MZ
565{
566 stringstream msg;
567
568 // Startup
710aba4a 569 string CDROM = _config->FindDir("Acquire::cdrom::mount");
a75c6a6e
MZ
570 if (CDROM[0] == '.')
571 CDROM= SafeGetCWD() + '/' + CDROM;
572
6070a346
DK
573 if (log != NULL)
574 {
a75c6a6e
MZ
575 msg.str("");
576 ioprintf(msg, _("Using CD-ROM mount point %s\nMounting CD-ROM\n"),
577 CDROM.c_str());
578 log->Update(msg.str());
579 }
62dcbf84
JO
580
581 // Unmount the CD and get the user to put in the one they want
582 if (_config->FindB("APT::CDROM::NoMount",false) == false)
583 {
584 if(log != NULL)
585 log->Update(_("Unmounting CD-ROM\n"), STEP_UNMOUNT);
586 UnmountCdrom(CDROM);
587
588 if(log != NULL)
589 {
590 log->Update(_("Waiting for disc...\n"), STEP_WAIT);
591 if(!log->ChangeCdrom()) {
592 // user aborted
593 return false;
594 }
595 }
596
597 // Mount the new CDROM
598 if(log != NULL)
599 log->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT);
600
601 if (MountCdrom(CDROM) == false)
602 return _error->Error("Failed to mount the cdrom.");
603 }
a75c6a6e
MZ
604
605 // Hash the CD to get an ID
6070a346 606 if (log != NULL)
a75c6a6e
MZ
607 log->Update(_("Identifying.. "));
608
609
610 if (IdentCdrom(CDROM,ident) == false)
611 {
612 ident = "";
613 return false;
614 }
615
6070a346
DK
616 if (log != NULL)
617 {
618 msg.str("");
619 ioprintf(msg, "[%s]\n",ident.c_str());
620 log->Update(msg.str());
621 }
a75c6a6e
MZ
622
623 // Read the database
624 Configuration Database;
625 string DFile = _config->FindFile("Dir::State::cdroms");
626 if (FileExists(DFile) == true)
627 {
628 if (ReadConfigFile(Database,DFile) == false)
629 return _error->Error("Unable to read the cdrom database %s",
630 DFile.c_str());
631 }
6070a346
DK
632 if (log != NULL)
633 {
a75c6a6e 634 msg.str("");
36fb926e
OS
635 ioprintf(msg, _("Stored label: %s\n"),
636 Database.Find("CD::"+ident).c_str());
a75c6a6e
MZ
637 log->Update(msg.str());
638 }
1fcbe14d
OS
639
640 // Unmount and finish
6070a346
DK
641 if (_config->FindB("APT::CDROM::NoMount",false) == false)
642 {
643 if (log != NULL)
644 log->Update(_("Unmounting CD-ROM...\n"), STEP_LAST);
1fcbe14d
OS
645 UnmountCdrom(CDROM);
646 }
647
a75c6a6e
MZ
648 return true;
649}
92fcbfc1
DK
650 /*}}}*/
651bool pkgCdrom::Add(pkgCdromStatus *log) /*{{{*/
a75c6a6e
MZ
652{
653 stringstream msg;
654
655 // Startup
a9239250 656 string CDROM = _config->FindDir("Acquire::cdrom::mount");
a75c6a6e
MZ
657 if (CDROM[0] == '.')
658 CDROM= SafeGetCWD() + '/' + CDROM;
659
6070a346
DK
660 if(log != NULL)
661 {
a75c6a6e
MZ
662 log->SetTotal(STEP_LAST);
663 msg.str("");
664 ioprintf(msg, _("Using CD-ROM mount point %s\n"), CDROM.c_str());
665 log->Update(msg.str(), STEP_PREPARE);
666 }
667
668 // Read the database
669 Configuration Database;
670 string DFile = _config->FindFile("Dir::State::cdroms");
671 if (FileExists(DFile) == true)
672 {
cdadf54b 673 if (ReadConfigFile(Database,DFile) == false)
a75c6a6e
MZ
674 return _error->Error("Unable to read the cdrom database %s",
675 DFile.c_str());
676 }
677
678 // Unmount the CD and get the user to put in the one they want
679 if (_config->FindB("APT::CDROM::NoMount",false) == false)
680 {
6070a346 681 if(log != NULL)
a75c6a6e
MZ
682 log->Update(_("Unmounting CD-ROM\n"), STEP_UNMOUNT);
683 UnmountCdrom(CDROM);
684
6070a346
DK
685 if(log != NULL)
686 {
a75c6a6e
MZ
687 log->Update(_("Waiting for disc...\n"), STEP_WAIT);
688 if(!log->ChangeCdrom()) {
689 // user aborted
690 return false;
691 }
692 }
693
694 // Mount the new CDROM
6070a346
DK
695 if(log != NULL)
696 log->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT);
697
a75c6a6e
MZ
698 if (MountCdrom(CDROM) == false)
699 return _error->Error("Failed to mount the cdrom.");
700 }
701
702 // Hash the CD to get an ID
6070a346 703 if(log != NULL)
a75c6a6e
MZ
704 log->Update(_("Identifying.. "), STEP_IDENT);
705 string ID;
706 if (IdentCdrom(CDROM,ID) == false)
707 {
6070a346
DK
708 if (log != NULL)
709 log->Update("\n");
a75c6a6e
MZ
710 return false;
711 }
6070a346
DK
712 if(log != NULL)
713 {
a75c6a6e 714 log->Update("["+ID+"]\n");
db0db9fe 715 log->Update(_("Scanning disc for index files..\n"),STEP_SCAN);
6070a346
DK
716 }
717
a75c6a6e
MZ
718 // Get the CD structure
719 vector<string> List;
720 vector<string> SourceList;
721 vector<string> SigList;
22f8568d 722 vector<string> TransList;
a75c6a6e
MZ
723 string StartDir = SafeGetCWD();
724 string InfoDir;
22f8568d 725 if (FindPackages(CDROM,List,SourceList, SigList,TransList,InfoDir,log) == false)
a75c6a6e 726 {
6070a346
DK
727 if (log != NULL)
728 log->Update("\n");
a75c6a6e
MZ
729 return false;
730 }
731
31bda500
DK
732 if (chdir(StartDir.c_str()) != 0)
733 return _error->Errno("chdir","Unable to change to %s", StartDir.c_str());
a75c6a6e
MZ
734
735 if (_config->FindB("Debug::aptcdrom",false) == true)
736 {
737 cout << "I found (binary):" << endl;
f7f0d6c7 738 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
739 cout << *I << endl;
740 cout << "I found (source):" << endl;
f7f0d6c7 741 for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); ++I)
a75c6a6e
MZ
742 cout << *I << endl;
743 cout << "I found (Signatures):" << endl;
f7f0d6c7 744 for (vector<string>::iterator I = SigList.begin(); I != SigList.end(); ++I)
a75c6a6e
MZ
745 cout << *I << endl;
746 }
747
748 //log->Update(_("Cleaning package lists..."), STEP_CLEAN);
749
750 // Fix up the list
751 DropBinaryArch(List);
752 DropRepeats(List,"Packages");
753 DropRepeats(SourceList,"Sources");
2c405a44
DK
754 // FIXME: We ignore stat() errors here as we usually have only one of those in use
755 // This has little potencial to drop 'valid' stat() errors as we know that one of these
756 // files need to exist, but it would be better if we would check it here
757 _error->PushToStack();
a75c6a6e 758 DropRepeats(SigList,"Release.gpg");
212080b8 759 DropRepeats(SigList,"InRelease");
2c405a44 760 _error->RevertToStack();
22f8568d 761 DropRepeats(TransList,"");
c45233ea
DK
762 if (_config->FindB("APT::CDROM::DropTranslation", true) == true)
763 DropTranslation(TransList);
6070a346 764 if(log != NULL) {
a75c6a6e 765 msg.str("");
a376d6fd
OS
766 ioprintf(msg, _("Found %zu package indexes, %zu source indexes, "
767 "%zu translation indexes and %zu signatures\n"),
22f8568d
MV
768 List.size(), SourceList.size(), TransList.size(),
769 SigList.size());
a75c6a6e
MZ
770 log->Update(msg.str(), STEP_SCAN);
771 }
772
f7f0d6c7 773 if (List.empty() == true && SourceList.empty() == true)
cdadf54b 774 {
50959877
MV
775 if (_config->FindB("APT::CDROM::NoMount",false) == false)
776 UnmountCdrom(CDROM);
d720a7d4 777 return _error->Error(_("Unable to locate any package files, perhaps this is not a Debian Disc or the wrong architecture?"));
cdadf54b 778 }
a75c6a6e
MZ
779
780 // Check if the CD is in the database
781 string Name;
782 if (Database.Exists("CD::" + ID) == false ||
783 _config->FindB("APT::CDROM::Rename",false) == true)
784 {
785 // Try to use the CDs label if at all possible
786 if (InfoDir.empty() == false &&
787 FileExists(InfoDir + "/info") == true)
788 {
789 ifstream F(string(InfoDir + "/info").c_str());
790 if (!F == 0)
791 getline(F,Name);
792
793 if (Name.empty() == false)
794 {
795 // Escape special characters
796 string::iterator J = Name.begin();
f7f0d6c7 797 for (; J != Name.end(); ++J)
a75c6a6e
MZ
798 if (*J == '"' || *J == ']' || *J == '[')
799 *J = '_';
800
6070a346
DK
801 if(log != NULL)
802 {
a75c6a6e 803 msg.str("");
ce86ff41 804 ioprintf(msg, _("Found label '%s'\n"), Name.c_str());
a75c6a6e
MZ
805 log->Update(msg.str());
806 }
807 Database.Set("CD::" + ID + "::Label",Name);
808 }
809 }
810
811 if (_config->FindB("APT::CDROM::Rename",false) == true ||
812 Name.empty() == true)
813 {
6070a346 814 if(log == NULL)
cdadf54b 815 {
50959877
MV
816 if (_config->FindB("APT::CDROM::NoMount",false) == false)
817 UnmountCdrom(CDROM);
a75c6a6e 818 return _error->Error("No disc name found and no way to ask for it");
cdadf54b 819 }
a75c6a6e
MZ
820
821 while(true) {
822 if(!log->AskCdromName(Name)) {
823 // user canceld
824 return false;
825 }
826 cout << "Name: '" << Name << "'" << endl;
827
828 if (Name.empty() == false &&
829 Name.find('"') == string::npos &&
830 Name.find('[') == string::npos &&
831 Name.find(']') == string::npos)
832 break;
833 log->Update(_("That is not a valid name, try again.\n"));
834 }
835 }
836 }
837 else
838 Name = Database.Find("CD::" + ID);
839
840 // Escape special characters
841 string::iterator J = Name.begin();
f7f0d6c7 842 for (; J != Name.end(); ++J)
a75c6a6e
MZ
843 if (*J == '"' || *J == ']' || *J == '[')
844 *J = '_';
845
846 Database.Set("CD::" + ID,Name);
6070a346
DK
847 if(log != NULL)
848 {
a75c6a6e 849 msg.str("");
db0db9fe 850 ioprintf(msg, _("This disc is called: \n'%s'\n"), Name.c_str());
a75c6a6e 851 log->Update(msg.str());
6070a346 852 log->Update(_("Copying package lists..."), STEP_COPY);
a75c6a6e 853 }
b2ea1a47
DK
854
855 // check for existence and possibly create state directory for copying
856 string const listDir = _config->FindDir("Dir::State::lists");
857 string const partialListDir = listDir + "partial/";
858 if (CreateAPTDirectoryIfNeeded(_config->FindDir("Dir::State"), partialListDir) == false &&
859 CreateAPTDirectoryIfNeeded(listDir, partialListDir) == false)
860 return _error->Errno("cdrom", _("List directory %spartial is missing."), listDir.c_str());
861
a75c6a6e
MZ
862 // take care of the signatures and copy them if they are ok
863 // (we do this before PackageCopy as it modifies "List" and "SourceList")
864 SigVerify SignVerify;
865 SignVerify.CopyAndVerify(CDROM, Name, SigList, List, SourceList);
866
867 // Copy the package files to the state directory
868 PackageCopy Copy;
869 SourceCopy SrcCopy;
22f8568d 870 TranslationsCopy TransCopy;
a75c6a6e 871 if (Copy.CopyPackages(CDROM,Name,List, log) == false ||
22f8568d
MV
872 SrcCopy.CopyPackages(CDROM,Name,SourceList, log) == false ||
873 TransCopy.CopyTranslations(CDROM,Name,TransList, log) == false)
a75c6a6e
MZ
874 return false;
875
876 // reduce the List so that it takes less space in sources.list
877 ReduceSourcelist(CDROM,List);
878 ReduceSourcelist(CDROM,SourceList);
879
880 // Write the database and sourcelist
881 if (_config->FindB("APT::cdrom::NoAct",false) == false)
882 {
883 if (WriteDatabase(Database) == false)
884 return false;
885
6070a346 886 if(log != NULL)
a75c6a6e 887 log->Update(_("Writing new source list\n"), STEP_WRITE);
a75c6a6e
MZ
888 if (WriteSourceList(Name,List,false) == false ||
889 WriteSourceList(Name,SourceList,true) == false)
890 return false;
891 }
892
893 // Print the sourcelist entries
6070a346 894 if(log != NULL)
db0db9fe 895 log->Update(_("Source list entries for this disc are:\n"));
a75c6a6e 896
f7f0d6c7 897 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
898 {
899 string::size_type Space = (*I).find(' ');
900 if (Space == string::npos)
cdadf54b 901 {
50959877
MV
902 if (_config->FindB("APT::CDROM::NoMount",false) == false)
903 UnmountCdrom(CDROM);
a75c6a6e 904 return _error->Error("Internal error");
cdadf54b 905 }
a75c6a6e 906
6070a346
DK
907 if(log != NULL)
908 {
a75c6a6e
MZ
909 msg.str("");
910 msg << "deb cdrom:[" << Name << "]/" << string(*I,0,Space) <<
911 " " << string(*I,Space+1) << endl;
912 log->Update(msg.str());
913 }
914 }
915
f7f0d6c7 916 for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); ++I)
a75c6a6e
MZ
917 {
918 string::size_type Space = (*I).find(' ');
919 if (Space == string::npos)
cdadf54b 920 {
50959877
MV
921 if (_config->FindB("APT::CDROM::NoMount",false) == false)
922 UnmountCdrom(CDROM);
a75c6a6e 923 return _error->Error("Internal error");
cdadf54b 924 }
a75c6a6e 925
6070a346 926 if(log != NULL) {
a75c6a6e
MZ
927 msg.str("");
928 msg << "deb-src cdrom:[" << Name << "]/" << string(*I,0,Space) <<
929 " " << string(*I,Space+1) << endl;
930 log->Update(msg.str());
931 }
932 }
933
a75c6a6e 934 // Unmount and finish
70dbf5f8 935 if (_config->FindB("APT::CDROM::NoMount",false) == false) {
6070a346
DK
936 if (log != NULL)
937 log->Update(_("Unmounting CD-ROM...\n"), STEP_LAST);
a75c6a6e
MZ
938 UnmountCdrom(CDROM);
939 }
940
941 return true;
942}
92fcbfc1 943 /*}}}*/
3e2d7cce 944pkgUdevCdromDevices::pkgUdevCdromDevices() /*{{{*/
76fe5db7 945 : libudev_handle(NULL)
cbc9bed8
MV
946{
947
948}
3e2d7cce 949 /*}}}*/
cbc9bed8
MV
950
951bool
3e2d7cce 952pkgUdevCdromDevices::Dlopen() /*{{{*/
cbc9bed8 953{
49cb36fc 954 // alread open
76fe5db7 955 if(libudev_handle != NULL)
49cb36fc
MV
956 return true;
957
cbc9bed8
MV
958 // see if we can get libudev
959 void *h = ::dlopen("libudev.so.0", RTLD_LAZY);
960 if(h == NULL)
961 return false;
962
963 // get the pointers to the udev structs
964 libudev_handle = h;
965 udev_new = (udev* (*)(void)) dlsym(h, "udev_new");
966 udev_enumerate_add_match_property = (int (*)(udev_enumerate*, const char*, const char*))dlsym(h, "udev_enumerate_add_match_property");
b7bc31eb 967 udev_enumerate_add_match_sysattr = (int (*)(udev_enumerate*, const char*, const char*))dlsym(h, "udev_enumerate_add_match_sysattr");
cbc9bed8
MV
968 udev_enumerate_scan_devices = (int (*)(udev_enumerate*))dlsym(h, "udev_enumerate_scan_devices");
969 udev_enumerate_get_list_entry = (udev_list_entry* (*)(udev_enumerate*))dlsym(h, "udev_enumerate_get_list_entry");
970 udev_device_new_from_syspath = (udev_device* (*)(udev*, const char*))dlsym(h, "udev_device_new_from_syspath");
971 udev_enumerate_get_udev = (udev* (*)(udev_enumerate*))dlsym(h, "udev_enumerate_get_udev");
972 udev_list_entry_get_name = (const char* (*)(udev_list_entry*))dlsym(h, "udev_list_entry_get_name");
973 udev_device_get_devnode = (const char* (*)(udev_device*))dlsym(h, "udev_device_get_devnode");
974 udev_enumerate_new = (udev_enumerate* (*)(udev*))dlsym(h, "udev_enumerate_new");
975 udev_list_entry_get_next = (udev_list_entry* (*)(udev_list_entry*))dlsym(h, "udev_list_entry_get_next");
976 udev_device_get_property_value = (const char* (*)(udev_device *, const char *))dlsym(h, "udev_device_get_property_value");
977
978 return true;
979}
3e2d7cce 980 /*}}}*/
f4c4a24e 981 /*{{{*/
f2e4a11d 982// convenience interface, this will just call ScanForRemovable
f4c4a24e
MV
983vector<CdromDevice>
984pkgUdevCdromDevices::Scan()
985{
c9952021
MV
986 bool CdromOnly = _config->FindB("APT::cdrom::CdromOnly", true);
987 return ScanForRemovable(CdromOnly);
f4c4a24e
MV
988};
989 /*}}}*/
990 /*{{{*/
cbc9bed8 991vector<CdromDevice>
f4c4a24e 992pkgUdevCdromDevices::ScanForRemovable(bool CdromOnly)
cbc9bed8
MV
993{
994 vector<CdromDevice> cdrom_devices;
995 struct udev_enumerate *enumerate;
996 struct udev_list_entry *l, *devices;
997 struct udev *udev_ctx;
998
999 if(libudev_handle == NULL)
1000 return cdrom_devices;
1001
1002 udev_ctx = udev_new();
1003 enumerate = udev_enumerate_new (udev_ctx);
f4c4a24e
MV
1004 if (CdromOnly)
1005 udev_enumerate_add_match_property(enumerate, "ID_CDROM", "1");
e344ad65 1006 else {
f4c4a24e 1007 udev_enumerate_add_match_sysattr(enumerate, "removable", "1");
e344ad65 1008 }
cbc9bed8
MV
1009
1010 udev_enumerate_scan_devices (enumerate);
1011 devices = udev_enumerate_get_list_entry (enumerate);
1012 for (l = devices; l != NULL; l = udev_list_entry_get_next (l))
1013 {
1014 CdromDevice cdrom;
1015 struct udev_device *udevice;
1016 udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerate), udev_list_entry_get_name (l));
1017 if (udevice == NULL)
1018 continue;
1019 const char* devnode = udev_device_get_devnode(udevice);
02aa6f67
MV
1020
1021 // try fstab_dir first
1022 string mountpath;
1023 const char* mp = udev_device_get_property_value(udevice, "FSTAB_DIR");
1024 if (mp)
1025 mountpath = string(mp);
1026 else
ef381816
MV
1027 mountpath = FindMountPointForDevice(devnode);
1028
cbc9bed8
MV
1029 // fill in the struct
1030 cdrom.DeviceName = string(devnode);
02aa6f67 1031 if (mountpath != "") {
cbc9bed8 1032 cdrom.MountPath = mountpath;
be5b5581
MV
1033 string s = string(mountpath);
1034 cdrom.Mounted = IsMounted(s);
cbc9bed8
MV
1035 } else {
1036 cdrom.Mounted = false;
1037 cdrom.MountPath = "";
1038 }
1039 cdrom_devices.push_back(cdrom);
1040 }
1041 return cdrom_devices;
1042}
3e2d7cce 1043 /*}}}*/
cbc9bed8 1044
3e2d7cce 1045pkgUdevCdromDevices::~pkgUdevCdromDevices() /*{{{*/
cbc9bed8 1046{
93adae19
MV
1047 if (libudev_handle != NULL)
1048 dlclose(libudev_handle);
cbc9bed8 1049}
3e2d7cce 1050 /*}}}*/