]> git.saurik.com Git - apt.git/blame - apt-pkg/cdrom.cc
* apt-pkg/contrib/fileutl.h:
[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
272 return true;
273}
92fcbfc1 274 /*}}}*/
a75c6a6e
MZ
275// DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
276// ---------------------------------------------------------------------
277/* Here we go and stat every file that we found and strip dup inodes. */
278bool pkgCdrom::DropRepeats(vector<string> &List,const char *Name)
279{
2c405a44 280 bool couldFindAllFiles = true;
a75c6a6e
MZ
281 // Get a list of all the inodes
282 ino_t *Inodes = new ino_t[List.size()];
78c9276d 283 for (unsigned int I = 0; I != List.size(); ++I)
a75c6a6e
MZ
284 {
285 struct stat Buf;
78c9276d
DK
286 bool found = false;
287
288 std::vector<APT::Configuration::Compressor> const compressor = APT::Configuration::getCompressors();
289 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
290 c != compressor.end(); ++c)
82c8f08e 291 {
78c9276d 292 std::string filename = std::string(List[I]).append(Name).append(c->Extension);
82c8f08e 293 if (stat(filename.c_str(), &Buf) != 0)
78c9276d
DK
294 continue;
295 Inodes[I] = Buf.st_ino;
296 found = true;
297 break;
82c8f08e 298 }
78c9276d
DK
299
300 if (found == false)
2c405a44
DK
301 {
302 _error->Errno("stat","Failed to stat %s%s",List[I].c_str(), Name);
303 couldFindAllFiles = false;
304 Inodes[I] = 0;
305 }
a75c6a6e 306 }
78c9276d 307
a75c6a6e
MZ
308 // Look for dups
309 for (unsigned int I = 0; I != List.size(); I++)
310 {
2c405a44
DK
311 if (Inodes[I] == 0)
312 continue;
a75c6a6e
MZ
313 for (unsigned int J = I+1; J < List.size(); J++)
314 {
315 // No match
2c405a44 316 if (Inodes[J] == 0 || Inodes[J] != Inodes[I])
a75c6a6e
MZ
317 continue;
318
319 // We score the two paths.. and erase one
320 int ScoreA = Score(List[I]);
321 int ScoreB = Score(List[J]);
322 if (ScoreA < ScoreB)
323 {
324 List[I] = string();
325 break;
326 }
327
328 List[J] = string();
329 }
330 }
3a4477a4
DK
331 delete[] Inodes;
332
a75c6a6e
MZ
333 // Wipe erased entries
334 for (unsigned int I = 0; I < List.size();)
335 {
336 if (List[I].empty() == false)
337 I++;
338 else
339 List.erase(List.begin()+I);
340 }
341
2c405a44 342 return couldFindAllFiles;
a75c6a6e
MZ
343}
344 /*}}}*/
a75c6a6e
MZ
345// ReduceSourceList - Takes the path list and reduces it /*{{{*/
346// ---------------------------------------------------------------------
347/* This takes the list of source list expressed entires and collects
348 similar ones to form a single entry for each dist */
349void pkgCdrom::ReduceSourcelist(string CD,vector<string> &List)
350{
351 sort(List.begin(),List.end());
352
353 // Collect similar entries
f7f0d6c7 354 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
355 {
356 // Find a space..
357 string::size_type Space = (*I).find(' ');
358 if (Space == string::npos)
359 continue;
360 string::size_type SSpace = (*I).find(' ',Space + 1);
361 if (SSpace == string::npos)
362 continue;
363
364 string Word1 = string(*I,Space,SSpace-Space);
365 string Prefix = string(*I,0,Space);
f7f0d6c7 366 for (vector<string>::iterator J = List.begin(); J != I; ++J)
a75c6a6e
MZ
367 {
368 // Find a space..
369 string::size_type Space2 = (*J).find(' ');
370 if (Space2 == string::npos)
371 continue;
372 string::size_type SSpace2 = (*J).find(' ',Space2 + 1);
373 if (SSpace2 == string::npos)
374 continue;
375
376 if (string(*J,0,Space2) != Prefix)
377 continue;
378 if (string(*J,Space2,SSpace2-Space2) != Word1)
379 continue;
380
381 *J += string(*I,SSpace);
382 *I = string();
383 }
384 }
385
386 // Wipe erased entries
387 for (unsigned int I = 0; I < List.size();)
388 {
389 if (List[I].empty() == false)
390 I++;
391 else
392 List.erase(List.begin()+I);
393 }
394}
395 /*}}}*/
396// WriteDatabase - Write the CDROM Database file /*{{{*/
397// ---------------------------------------------------------------------
398/* We rewrite the configuration class associated with the cdrom database. */
399bool pkgCdrom::WriteDatabase(Configuration &Cnf)
400{
401 string DFile = _config->FindFile("Dir::State::cdroms");
402 string NewFile = DFile + ".new";
403
404 unlink(NewFile.c_str());
405 ofstream Out(NewFile.c_str());
406 if (!Out)
407 return _error->Errno("ofstream::ofstream",
408 "Failed to open %s.new",DFile.c_str());
409
410 /* Write out all of the configuration directives by walking the
411 configuration tree */
412 const Configuration::Item *Top = Cnf.Tree(0);
413 for (; Top != 0;)
414 {
415 // Print the config entry
416 if (Top->Value.empty() == false)
417 Out << Top->FullTag() + " \"" << Top->Value << "\";" << endl;
418
419 if (Top->Child != 0)
420 {
421 Top = Top->Child;
422 continue;
423 }
424
425 while (Top != 0 && Top->Next == 0)
426 Top = Top->Parent;
427 if (Top != 0)
428 Top = Top->Next;
429 }
430
431 Out.close();
432
15032eec 433 link(DFile.c_str(),string(DFile + '~').c_str());
a75c6a6e
MZ
434 if (rename(NewFile.c_str(),DFile.c_str()) != 0)
435 return _error->Errno("rename","Failed to rename %s.new to %s",
436 DFile.c_str(),DFile.c_str());
437
438 return true;
439}
440 /*}}}*/
441// WriteSourceList - Write an updated sourcelist /*{{{*/
442// ---------------------------------------------------------------------
443/* This reads the old source list and copies it into the new one. It
444 appends the new CDROM entires just after the first block of comments.
445 This places them first in the file. It also removes any old entries
446 that were the same. */
447bool pkgCdrom::WriteSourceList(string Name,vector<string> &List,bool Source)
448{
f7f0d6c7 449 if (List.empty() == true)
a75c6a6e
MZ
450 return true;
451
452 string File = _config->FindFile("Dir::Etc::sourcelist");
453
454 // Open the stream for reading
455 ifstream F((FileExists(File)?File.c_str():"/dev/null"),
456 ios::in );
457 if (!F != 0)
458 return _error->Errno("ifstream::ifstream","Opening %s",File.c_str());
459
460 string NewFile = File + ".new";
461 unlink(NewFile.c_str());
462 ofstream Out(NewFile.c_str());
463 if (!Out)
464 return _error->Errno("ofstream::ofstream",
465 "Failed to open %s.new",File.c_str());
466
467 // Create a short uri without the path
468 string ShortURI = "cdrom:[" + Name + "]/";
469 string ShortURI2 = "cdrom:" + Name + "/"; // For Compatibility
470
471 string Type;
472 if (Source == true)
473 Type = "deb-src";
474 else
475 Type = "deb";
476
477 char Buffer[300];
478 int CurLine = 0;
479 bool First = true;
480 while (F.eof() == false)
481 {
482 F.getline(Buffer,sizeof(Buffer));
483 CurLine++;
13e8426f
MV
484 if (F.fail() && !F.eof())
485 return _error->Error(_("Line %u too long in source list %s."),
486 CurLine,File.c_str());
a75c6a6e
MZ
487 _strtabexpand(Buffer,sizeof(Buffer));
488 _strstrip(Buffer);
489
490 // Comment or blank
491 if (Buffer[0] == '#' || Buffer[0] == 0)
492 {
493 Out << Buffer << endl;
494 continue;
495 }
496
497 if (First == true)
498 {
f7f0d6c7 499 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
500 {
501 string::size_type Space = (*I).find(' ');
502 if (Space == string::npos)
503 return _error->Error("Internal error");
504 Out << Type << " cdrom:[" << Name << "]/" << string(*I,0,Space) <<
505 " " << string(*I,Space+1) << endl;
506 }
507 }
508 First = false;
509
510 // Grok it
511 string cType;
512 string URI;
513 const char *C = Buffer;
514 if (ParseQuoteWord(C,cType) == false ||
515 ParseQuoteWord(C,URI) == false)
516 {
517 Out << Buffer << endl;
518 continue;
519 }
520
521 // Emit lines like this one
522 if (cType != Type || (string(URI,0,ShortURI.length()) != ShortURI &&
523 string(URI,0,ShortURI.length()) != ShortURI2))
524 {
525 Out << Buffer << endl;
526 continue;
527 }
528 }
529
530 // Just in case the file was empty
531 if (First == true)
532 {
f7f0d6c7 533 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
534 {
535 string::size_type Space = (*I).find(' ');
536 if (Space == string::npos)
537 return _error->Error("Internal error");
538
539 Out << "deb cdrom:[" << Name << "]/" << string(*I,0,Space) <<
540 " " << string(*I,Space+1) << endl;
541 }
542 }
543
544 Out.close();
545
546 rename(File.c_str(),string(File + '~').c_str());
547 if (rename(NewFile.c_str(),File.c_str()) != 0)
548 return _error->Errno("rename","Failed to rename %s.new to %s",
549 File.c_str(),File.c_str());
550
551 return true;
552}
92fcbfc1
DK
553 /*}}}*/
554bool pkgCdrom::Ident(string &ident, pkgCdromStatus *log) /*{{{*/
a75c6a6e
MZ
555{
556 stringstream msg;
557
558 // Startup
710aba4a 559 string CDROM = _config->FindDir("Acquire::cdrom::mount");
a75c6a6e
MZ
560 if (CDROM[0] == '.')
561 CDROM= SafeGetCWD() + '/' + CDROM;
562
6070a346
DK
563 if (log != NULL)
564 {
a75c6a6e
MZ
565 msg.str("");
566 ioprintf(msg, _("Using CD-ROM mount point %s\nMounting CD-ROM\n"),
567 CDROM.c_str());
568 log->Update(msg.str());
569 }
70dbf5f8
MV
570 if (MountCdrom(CDROM) == false)
571 return _error->Error("Failed to mount the cdrom.");
a75c6a6e
MZ
572
573 // Hash the CD to get an ID
6070a346 574 if (log != NULL)
a75c6a6e
MZ
575 log->Update(_("Identifying.. "));
576
577
578 if (IdentCdrom(CDROM,ident) == false)
579 {
580 ident = "";
581 return false;
582 }
583
6070a346
DK
584 if (log != NULL)
585 {
586 msg.str("");
587 ioprintf(msg, "[%s]\n",ident.c_str());
588 log->Update(msg.str());
589 }
a75c6a6e
MZ
590
591 // Read the database
592 Configuration Database;
593 string DFile = _config->FindFile("Dir::State::cdroms");
594 if (FileExists(DFile) == true)
595 {
596 if (ReadConfigFile(Database,DFile) == false)
597 return _error->Error("Unable to read the cdrom database %s",
598 DFile.c_str());
599 }
6070a346
DK
600 if (log != NULL)
601 {
a75c6a6e 602 msg.str("");
36fb926e
OS
603 ioprintf(msg, _("Stored label: %s\n"),
604 Database.Find("CD::"+ident).c_str());
a75c6a6e
MZ
605 log->Update(msg.str());
606 }
1fcbe14d
OS
607
608 // Unmount and finish
6070a346
DK
609 if (_config->FindB("APT::CDROM::NoMount",false) == false)
610 {
611 if (log != NULL)
612 log->Update(_("Unmounting CD-ROM...\n"), STEP_LAST);
1fcbe14d
OS
613 UnmountCdrom(CDROM);
614 }
615
a75c6a6e
MZ
616 return true;
617}
92fcbfc1
DK
618 /*}}}*/
619bool pkgCdrom::Add(pkgCdromStatus *log) /*{{{*/
a75c6a6e
MZ
620{
621 stringstream msg;
622
623 // Startup
a9239250 624 string CDROM = _config->FindDir("Acquire::cdrom::mount");
a75c6a6e
MZ
625 if (CDROM[0] == '.')
626 CDROM= SafeGetCWD() + '/' + CDROM;
627
6070a346
DK
628 if(log != NULL)
629 {
a75c6a6e
MZ
630 log->SetTotal(STEP_LAST);
631 msg.str("");
632 ioprintf(msg, _("Using CD-ROM mount point %s\n"), CDROM.c_str());
633 log->Update(msg.str(), STEP_PREPARE);
634 }
635
636 // Read the database
637 Configuration Database;
638 string DFile = _config->FindFile("Dir::State::cdroms");
639 if (FileExists(DFile) == true)
640 {
cdadf54b 641 if (ReadConfigFile(Database,DFile) == false)
a75c6a6e
MZ
642 return _error->Error("Unable to read the cdrom database %s",
643 DFile.c_str());
644 }
645
646 // Unmount the CD and get the user to put in the one they want
647 if (_config->FindB("APT::CDROM::NoMount",false) == false)
648 {
6070a346 649 if(log != NULL)
a75c6a6e
MZ
650 log->Update(_("Unmounting CD-ROM\n"), STEP_UNMOUNT);
651 UnmountCdrom(CDROM);
652
6070a346
DK
653 if(log != NULL)
654 {
a75c6a6e
MZ
655 log->Update(_("Waiting for disc...\n"), STEP_WAIT);
656 if(!log->ChangeCdrom()) {
657 // user aborted
658 return false;
659 }
660 }
661
662 // Mount the new CDROM
6070a346
DK
663 if(log != NULL)
664 log->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT);
665
a75c6a6e
MZ
666 if (MountCdrom(CDROM) == false)
667 return _error->Error("Failed to mount the cdrom.");
668 }
669
670 // Hash the CD to get an ID
6070a346 671 if(log != NULL)
a75c6a6e
MZ
672 log->Update(_("Identifying.. "), STEP_IDENT);
673 string ID;
674 if (IdentCdrom(CDROM,ID) == false)
675 {
6070a346
DK
676 if (log != NULL)
677 log->Update("\n");
a75c6a6e
MZ
678 return false;
679 }
6070a346
DK
680 if(log != NULL)
681 {
a75c6a6e 682 log->Update("["+ID+"]\n");
db0db9fe 683 log->Update(_("Scanning disc for index files..\n"),STEP_SCAN);
6070a346
DK
684 }
685
a75c6a6e
MZ
686 // Get the CD structure
687 vector<string> List;
688 vector<string> SourceList;
689 vector<string> SigList;
22f8568d 690 vector<string> TransList;
a75c6a6e
MZ
691 string StartDir = SafeGetCWD();
692 string InfoDir;
22f8568d 693 if (FindPackages(CDROM,List,SourceList, SigList,TransList,InfoDir,log) == false)
a75c6a6e 694 {
6070a346
DK
695 if (log != NULL)
696 log->Update("\n");
a75c6a6e
MZ
697 return false;
698 }
699
700 chdir(StartDir.c_str());
701
702 if (_config->FindB("Debug::aptcdrom",false) == true)
703 {
704 cout << "I found (binary):" << endl;
f7f0d6c7 705 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
706 cout << *I << endl;
707 cout << "I found (source):" << endl;
f7f0d6c7 708 for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); ++I)
a75c6a6e
MZ
709 cout << *I << endl;
710 cout << "I found (Signatures):" << endl;
f7f0d6c7 711 for (vector<string>::iterator I = SigList.begin(); I != SigList.end(); ++I)
a75c6a6e
MZ
712 cout << *I << endl;
713 }
714
715 //log->Update(_("Cleaning package lists..."), STEP_CLEAN);
716
717 // Fix up the list
718 DropBinaryArch(List);
719 DropRepeats(List,"Packages");
720 DropRepeats(SourceList,"Sources");
2c405a44
DK
721 // FIXME: We ignore stat() errors here as we usually have only one of those in use
722 // This has little potencial to drop 'valid' stat() errors as we know that one of these
723 // files need to exist, but it would be better if we would check it here
724 _error->PushToStack();
a75c6a6e 725 DropRepeats(SigList,"Release.gpg");
212080b8 726 DropRepeats(SigList,"InRelease");
2c405a44 727 _error->RevertToStack();
22f8568d 728 DropRepeats(TransList,"");
6070a346 729 if(log != NULL) {
a75c6a6e 730 msg.str("");
a376d6fd
OS
731 ioprintf(msg, _("Found %zu package indexes, %zu source indexes, "
732 "%zu translation indexes and %zu signatures\n"),
22f8568d
MV
733 List.size(), SourceList.size(), TransList.size(),
734 SigList.size());
a75c6a6e
MZ
735 log->Update(msg.str(), STEP_SCAN);
736 }
737
f7f0d6c7 738 if (List.empty() == true && SourceList.empty() == true)
cdadf54b 739 {
50959877
MV
740 if (_config->FindB("APT::CDROM::NoMount",false) == false)
741 UnmountCdrom(CDROM);
d720a7d4 742 return _error->Error(_("Unable to locate any package files, perhaps this is not a Debian Disc or the wrong architecture?"));
cdadf54b 743 }
a75c6a6e
MZ
744
745 // Check if the CD is in the database
746 string Name;
747 if (Database.Exists("CD::" + ID) == false ||
748 _config->FindB("APT::CDROM::Rename",false) == true)
749 {
750 // Try to use the CDs label if at all possible
751 if (InfoDir.empty() == false &&
752 FileExists(InfoDir + "/info") == true)
753 {
754 ifstream F(string(InfoDir + "/info").c_str());
755 if (!F == 0)
756 getline(F,Name);
757
758 if (Name.empty() == false)
759 {
760 // Escape special characters
761 string::iterator J = Name.begin();
f7f0d6c7 762 for (; J != Name.end(); ++J)
a75c6a6e
MZ
763 if (*J == '"' || *J == ']' || *J == '[')
764 *J = '_';
765
6070a346
DK
766 if(log != NULL)
767 {
a75c6a6e 768 msg.str("");
ce86ff41 769 ioprintf(msg, _("Found label '%s'\n"), Name.c_str());
a75c6a6e
MZ
770 log->Update(msg.str());
771 }
772 Database.Set("CD::" + ID + "::Label",Name);
773 }
774 }
775
776 if (_config->FindB("APT::CDROM::Rename",false) == true ||
777 Name.empty() == true)
778 {
6070a346 779 if(log == NULL)
cdadf54b 780 {
50959877
MV
781 if (_config->FindB("APT::CDROM::NoMount",false) == false)
782 UnmountCdrom(CDROM);
a75c6a6e 783 return _error->Error("No disc name found and no way to ask for it");
cdadf54b 784 }
a75c6a6e
MZ
785
786 while(true) {
787 if(!log->AskCdromName(Name)) {
788 // user canceld
789 return false;
790 }
791 cout << "Name: '" << Name << "'" << endl;
792
793 if (Name.empty() == false &&
794 Name.find('"') == string::npos &&
795 Name.find('[') == string::npos &&
796 Name.find(']') == string::npos)
797 break;
798 log->Update(_("That is not a valid name, try again.\n"));
799 }
800 }
801 }
802 else
803 Name = Database.Find("CD::" + ID);
804
805 // Escape special characters
806 string::iterator J = Name.begin();
f7f0d6c7 807 for (; J != Name.end(); ++J)
a75c6a6e
MZ
808 if (*J == '"' || *J == ']' || *J == '[')
809 *J = '_';
810
811 Database.Set("CD::" + ID,Name);
6070a346
DK
812 if(log != NULL)
813 {
a75c6a6e 814 msg.str("");
db0db9fe 815 ioprintf(msg, _("This disc is called: \n'%s'\n"), Name.c_str());
a75c6a6e 816 log->Update(msg.str());
6070a346 817 log->Update(_("Copying package lists..."), STEP_COPY);
a75c6a6e 818 }
a75c6a6e
MZ
819 // take care of the signatures and copy them if they are ok
820 // (we do this before PackageCopy as it modifies "List" and "SourceList")
821 SigVerify SignVerify;
822 SignVerify.CopyAndVerify(CDROM, Name, SigList, List, SourceList);
823
824 // Copy the package files to the state directory
825 PackageCopy Copy;
826 SourceCopy SrcCopy;
22f8568d 827 TranslationsCopy TransCopy;
a75c6a6e 828 if (Copy.CopyPackages(CDROM,Name,List, log) == false ||
22f8568d
MV
829 SrcCopy.CopyPackages(CDROM,Name,SourceList, log) == false ||
830 TransCopy.CopyTranslations(CDROM,Name,TransList, log) == false)
a75c6a6e
MZ
831 return false;
832
833 // reduce the List so that it takes less space in sources.list
834 ReduceSourcelist(CDROM,List);
835 ReduceSourcelist(CDROM,SourceList);
836
837 // Write the database and sourcelist
838 if (_config->FindB("APT::cdrom::NoAct",false) == false)
839 {
840 if (WriteDatabase(Database) == false)
841 return false;
842
6070a346 843 if(log != NULL)
a75c6a6e 844 log->Update(_("Writing new source list\n"), STEP_WRITE);
a75c6a6e
MZ
845 if (WriteSourceList(Name,List,false) == false ||
846 WriteSourceList(Name,SourceList,true) == false)
847 return false;
848 }
849
850 // Print the sourcelist entries
6070a346 851 if(log != NULL)
db0db9fe 852 log->Update(_("Source list entries for this disc are:\n"));
a75c6a6e 853
f7f0d6c7 854 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
855 {
856 string::size_type Space = (*I).find(' ');
857 if (Space == string::npos)
cdadf54b 858 {
50959877
MV
859 if (_config->FindB("APT::CDROM::NoMount",false) == false)
860 UnmountCdrom(CDROM);
a75c6a6e 861 return _error->Error("Internal error");
cdadf54b 862 }
a75c6a6e 863
6070a346
DK
864 if(log != NULL)
865 {
a75c6a6e
MZ
866 msg.str("");
867 msg << "deb cdrom:[" << Name << "]/" << string(*I,0,Space) <<
868 " " << string(*I,Space+1) << endl;
869 log->Update(msg.str());
870 }
871 }
872
f7f0d6c7 873 for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); ++I)
a75c6a6e
MZ
874 {
875 string::size_type Space = (*I).find(' ');
876 if (Space == string::npos)
cdadf54b 877 {
50959877
MV
878 if (_config->FindB("APT::CDROM::NoMount",false) == false)
879 UnmountCdrom(CDROM);
a75c6a6e 880 return _error->Error("Internal error");
cdadf54b 881 }
a75c6a6e 882
6070a346 883 if(log != NULL) {
a75c6a6e
MZ
884 msg.str("");
885 msg << "deb-src cdrom:[" << Name << "]/" << string(*I,0,Space) <<
886 " " << string(*I,Space+1) << endl;
887 log->Update(msg.str());
888 }
889 }
890
a75c6a6e 891 // Unmount and finish
70dbf5f8 892 if (_config->FindB("APT::CDROM::NoMount",false) == false) {
6070a346
DK
893 if (log != NULL)
894 log->Update(_("Unmounting CD-ROM...\n"), STEP_LAST);
a75c6a6e
MZ
895 UnmountCdrom(CDROM);
896 }
897
898 return true;
899}
92fcbfc1 900 /*}}}*/
3e2d7cce 901pkgUdevCdromDevices::pkgUdevCdromDevices() /*{{{*/
76fe5db7 902 : libudev_handle(NULL)
cbc9bed8
MV
903{
904
905}
3e2d7cce 906 /*}}}*/
cbc9bed8
MV
907
908bool
3e2d7cce 909pkgUdevCdromDevices::Dlopen() /*{{{*/
cbc9bed8 910{
49cb36fc 911 // alread open
76fe5db7 912 if(libudev_handle != NULL)
49cb36fc
MV
913 return true;
914
cbc9bed8
MV
915 // see if we can get libudev
916 void *h = ::dlopen("libudev.so.0", RTLD_LAZY);
917 if(h == NULL)
918 return false;
919
920 // get the pointers to the udev structs
921 libudev_handle = h;
922 udev_new = (udev* (*)(void)) dlsym(h, "udev_new");
923 udev_enumerate_add_match_property = (int (*)(udev_enumerate*, const char*, const char*))dlsym(h, "udev_enumerate_add_match_property");
b7bc31eb 924 udev_enumerate_add_match_sysattr = (int (*)(udev_enumerate*, const char*, const char*))dlsym(h, "udev_enumerate_add_match_sysattr");
cbc9bed8
MV
925 udev_enumerate_scan_devices = (int (*)(udev_enumerate*))dlsym(h, "udev_enumerate_scan_devices");
926 udev_enumerate_get_list_entry = (udev_list_entry* (*)(udev_enumerate*))dlsym(h, "udev_enumerate_get_list_entry");
927 udev_device_new_from_syspath = (udev_device* (*)(udev*, const char*))dlsym(h, "udev_device_new_from_syspath");
928 udev_enumerate_get_udev = (udev* (*)(udev_enumerate*))dlsym(h, "udev_enumerate_get_udev");
929 udev_list_entry_get_name = (const char* (*)(udev_list_entry*))dlsym(h, "udev_list_entry_get_name");
930 udev_device_get_devnode = (const char* (*)(udev_device*))dlsym(h, "udev_device_get_devnode");
931 udev_enumerate_new = (udev_enumerate* (*)(udev*))dlsym(h, "udev_enumerate_new");
932 udev_list_entry_get_next = (udev_list_entry* (*)(udev_list_entry*))dlsym(h, "udev_list_entry_get_next");
933 udev_device_get_property_value = (const char* (*)(udev_device *, const char *))dlsym(h, "udev_device_get_property_value");
934
935 return true;
936}
3e2d7cce 937 /*}}}*/
f4c4a24e 938 /*{{{*/
f2e4a11d 939// convenience interface, this will just call ScanForRemovable
f4c4a24e
MV
940vector<CdromDevice>
941pkgUdevCdromDevices::Scan()
942{
c9952021
MV
943 bool CdromOnly = _config->FindB("APT::cdrom::CdromOnly", true);
944 return ScanForRemovable(CdromOnly);
f4c4a24e
MV
945};
946 /*}}}*/
947 /*{{{*/
cbc9bed8 948vector<CdromDevice>
f4c4a24e 949pkgUdevCdromDevices::ScanForRemovable(bool CdromOnly)
cbc9bed8
MV
950{
951 vector<CdromDevice> cdrom_devices;
952 struct udev_enumerate *enumerate;
953 struct udev_list_entry *l, *devices;
954 struct udev *udev_ctx;
955
956 if(libudev_handle == NULL)
957 return cdrom_devices;
958
959 udev_ctx = udev_new();
960 enumerate = udev_enumerate_new (udev_ctx);
f4c4a24e
MV
961 if (CdromOnly)
962 udev_enumerate_add_match_property(enumerate, "ID_CDROM", "1");
e344ad65 963 else {
f4c4a24e 964 udev_enumerate_add_match_sysattr(enumerate, "removable", "1");
e344ad65 965 }
cbc9bed8
MV
966
967 udev_enumerate_scan_devices (enumerate);
968 devices = udev_enumerate_get_list_entry (enumerate);
969 for (l = devices; l != NULL; l = udev_list_entry_get_next (l))
970 {
971 CdromDevice cdrom;
972 struct udev_device *udevice;
973 udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerate), udev_list_entry_get_name (l));
974 if (udevice == NULL)
975 continue;
976 const char* devnode = udev_device_get_devnode(udevice);
02aa6f67
MV
977
978 // try fstab_dir first
979 string mountpath;
980 const char* mp = udev_device_get_property_value(udevice, "FSTAB_DIR");
981 if (mp)
982 mountpath = string(mp);
983 else
ef381816
MV
984 mountpath = FindMountPointForDevice(devnode);
985
cbc9bed8
MV
986 // fill in the struct
987 cdrom.DeviceName = string(devnode);
02aa6f67 988 if (mountpath != "") {
cbc9bed8 989 cdrom.MountPath = mountpath;
be5b5581
MV
990 string s = string(mountpath);
991 cdrom.Mounted = IsMounted(s);
cbc9bed8
MV
992 } else {
993 cdrom.Mounted = false;
994 cdrom.MountPath = "";
995 }
996 cdrom_devices.push_back(cdrom);
997 }
998 return cdrom_devices;
999}
3e2d7cce 1000 /*}}}*/
cbc9bed8 1001
3e2d7cce 1002pkgUdevCdromDevices::~pkgUdevCdromDevices() /*{{{*/
cbc9bed8 1003{
93adae19
MV
1004 if (libudev_handle != NULL)
1005 dlclose(libudev_handle);
cbc9bed8 1006}
3e2d7cce 1007 /*}}}*/