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