merge from the lp:~mvo/apt/mvo branch
[ntk/apt.git] / apt-pkg / cdrom.cc
1 /*
2 */
3 #include<config.h>
4
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>
10 #include<apt-pkg/aptconfiguration.h>
11 #include<apt-pkg/configuration.h>
12 #include<apt-pkg/fileutl.h>
13
14 #include<sstream>
15 #include<fstream>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <dirent.h>
19 #include <unistd.h>
20 #include <stdio.h>
21 #include <algorithm>
22 #include <dlfcn.h>
23
24 #include "indexcopy.h"
25
26 #include<apti18n.h>
27
28 using 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. */
36 bool pkgCdrom::FindPackages(string CD,
37 vector<string> &List,
38 vector<string> &SList,
39 vector<string> &SigList,
40 vector<string> &TransList,
41 string &InfoDir, pkgCdromStatus *log,
42 unsigned int Depth)
43 {
44 static ino_t Inodes[9];
45 DIR *D;
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
61 if (DirectoryExists(".disk") == true)
62 {
63 if (InfoDir.empty() == true)
64 InfoDir = CD + ".disk/";
65 }
66
67 // Don't look into directories that have been marked to ingore.
68 if (RealFileExists(".aptignr") == true)
69 return true;
70
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 */
75 if (RealFileExists("Release.gpg") == true || RealFileExists("InRelease") == true)
76 {
77 SigList.push_back(CD);
78 }
79
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 */
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 {
87 if (RealFileExists(std::string("Packages").append(c->Extension).c_str()) == false)
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 {
102 if (RealFileExists(std::string("Sources").append(c->Extension).c_str()) == false)
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 }
114
115 // see if we find translation indices
116 if (DirectoryExists("i18n") == true)
117 {
118 D = opendir("i18n");
119 for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
120 {
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)
126 {
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;
137 file.erase(file.size() - fileext.size());
138 TransList.push_back(CD + "i18n/" + file);
139 break;
140 }
141 }
142 }
143 closedir(D);
144 }
145
146 D = opendir(".");
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
182 if (FindPackages(CD + Dir->d_name,List,SList,SigList,TransList,InfoDir,log,Depth+1) == false)
183 break;
184
185 if (chdir(CD.c_str()) != 0)
186 {
187 _error->Errno("chdir","Unable to change to %s", CD.c_str());
188 closedir(D);
189 return false;
190 }
191 };
192
193 closedir(D);
194
195 return !_error->PendingError();
196 }
197 /*}}}*/
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. */
203 int 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;
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);
233 string cdromPath = _config->FindDir("Acquire::cdrom::mount");
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
245 return Res;
246 }
247 /*}}}*/
248 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
249 // ---------------------------------------------------------------------
250 /* Here we drop everything that is not this machines arch */
251 bool pkgCdrom::DropBinaryArch(vector<string> &List)
252 {
253
254 for (unsigned int I = 0; I < List.size(); I++)
255 {
256 const char *Str = List[I].c_str();
257 const char *Start, *End;
258 if ((Start = strstr(Str,"/binary-")) == 0)
259 continue;
260
261 // Between Start and End is the architecture
262 Start += 8;
263 if ((End = strstr(Start,"/")) != 0 && Start != End &&
264 APT::Configuration::checkArchitecture(string(Start, End)) == true)
265 continue; // okay, architecture is accepted
266
267 // not accepted -> Erase it
268 List.erase(List.begin() + I);
269 --I; // the next entry is at the same index after the erase
270 }
271
272 return true;
273 }
274 /*}}}*/
275 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
276 // ---------------------------------------------------------------------
277 /* Here we go and stat every file that we found and strip dup inodes. */
278 bool pkgCdrom::DropRepeats(vector<string> &List,const char *Name)
279 {
280 bool couldFindAllFiles = true;
281 // Get a list of all the inodes
282 ino_t *Inodes = new ino_t[List.size()];
283 for (unsigned int I = 0; I != List.size(); ++I)
284 {
285 struct stat Buf;
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)
291 {
292 std::string filename = std::string(List[I]).append(Name).append(c->Extension);
293 if (stat(filename.c_str(), &Buf) != 0)
294 continue;
295 Inodes[I] = Buf.st_ino;
296 found = true;
297 break;
298 }
299
300 if (found == false)
301 {
302 _error->Errno("stat","Failed to stat %s%s",List[I].c_str(), Name);
303 couldFindAllFiles = false;
304 Inodes[I] = 0;
305 }
306 }
307
308 // Look for dups
309 for (unsigned int I = 0; I != List.size(); I++)
310 {
311 if (Inodes[I] == 0)
312 continue;
313 for (unsigned int J = I+1; J < List.size(); J++)
314 {
315 // No match
316 if (Inodes[J] == 0 || Inodes[J] != Inodes[I])
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 }
331 delete[] Inodes;
332
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
342 return couldFindAllFiles;
343 }
344 /*}}}*/
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 */
349 void pkgCdrom::ReduceSourcelist(string CD,vector<string> &List)
350 {
351 sort(List.begin(),List.end());
352
353 // Collect similar entries
354 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
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);
366 for (vector<string>::iterator J = List.begin(); J != I; ++J)
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. */
399 bool 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
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());
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. */
448 bool pkgCdrom::WriteSourceList(string Name,vector<string> &List,bool Source)
449 {
450 if (List.empty() == true)
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++;
485 if (F.fail() && !F.eof())
486 return _error->Error(_("Line %u too long in source list %s."),
487 CurLine,File.c_str());
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 {
500 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
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 {
534 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
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 }
554 /*}}}*/
555 bool pkgCdrom::Ident(string &ident, pkgCdromStatus *log) /*{{{*/
556 {
557 stringstream msg;
558
559 // Startup
560 string CDROM = _config->FindDir("Acquire::cdrom::mount");
561 if (CDROM[0] == '.')
562 CDROM= SafeGetCWD() + '/' + CDROM;
563
564 if (log != NULL)
565 {
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 }
571 if (MountCdrom(CDROM) == false)
572 return _error->Error("Failed to mount the cdrom.");
573
574 // Hash the CD to get an ID
575 if (log != NULL)
576 log->Update(_("Identifying.. "));
577
578
579 if (IdentCdrom(CDROM,ident) == false)
580 {
581 ident = "";
582 return false;
583 }
584
585 if (log != NULL)
586 {
587 msg.str("");
588 ioprintf(msg, "[%s]\n",ident.c_str());
589 log->Update(msg.str());
590 }
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 }
601 if (log != NULL)
602 {
603 msg.str("");
604 ioprintf(msg, _("Stored label: %s\n"),
605 Database.Find("CD::"+ident).c_str());
606 log->Update(msg.str());
607 }
608
609 // Unmount and finish
610 if (_config->FindB("APT::CDROM::NoMount",false) == false)
611 {
612 if (log != NULL)
613 log->Update(_("Unmounting CD-ROM...\n"), STEP_LAST);
614 UnmountCdrom(CDROM);
615 }
616
617 return true;
618 }
619 /*}}}*/
620 bool pkgCdrom::Add(pkgCdromStatus *log) /*{{{*/
621 {
622 stringstream msg;
623
624 // Startup
625 string CDROM = _config->FindDir("Acquire::cdrom::mount");
626 if (CDROM[0] == '.')
627 CDROM= SafeGetCWD() + '/' + CDROM;
628
629 if(log != NULL)
630 {
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 {
642 if (ReadConfigFile(Database,DFile) == false)
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 {
650 if(log != NULL)
651 log->Update(_("Unmounting CD-ROM\n"), STEP_UNMOUNT);
652 UnmountCdrom(CDROM);
653
654 if(log != NULL)
655 {
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
664 if(log != NULL)
665 log->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT);
666
667 if (MountCdrom(CDROM) == false)
668 return _error->Error("Failed to mount the cdrom.");
669 }
670
671 // Hash the CD to get an ID
672 if(log != NULL)
673 log->Update(_("Identifying.. "), STEP_IDENT);
674 string ID;
675 if (IdentCdrom(CDROM,ID) == false)
676 {
677 if (log != NULL)
678 log->Update("\n");
679 return false;
680 }
681 if(log != NULL)
682 {
683 log->Update("["+ID+"]\n");
684 log->Update(_("Scanning disc for index files..\n"),STEP_SCAN);
685 }
686
687 // Get the CD structure
688 vector<string> List;
689 vector<string> SourceList;
690 vector<string> SigList;
691 vector<string> TransList;
692 string StartDir = SafeGetCWD();
693 string InfoDir;
694 if (FindPackages(CDROM,List,SourceList, SigList,TransList,InfoDir,log) == false)
695 {
696 if (log != NULL)
697 log->Update("\n");
698 return false;
699 }
700
701 if (chdir(StartDir.c_str()) != 0)
702 return _error->Errno("chdir","Unable to change to %s", StartDir.c_str());
703
704 if (_config->FindB("Debug::aptcdrom",false) == true)
705 {
706 cout << "I found (binary):" << endl;
707 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
708 cout << *I << endl;
709 cout << "I found (source):" << endl;
710 for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); ++I)
711 cout << *I << endl;
712 cout << "I found (Signatures):" << endl;
713 for (vector<string>::iterator I = SigList.begin(); I != SigList.end(); ++I)
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");
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();
727 DropRepeats(SigList,"Release.gpg");
728 DropRepeats(SigList,"InRelease");
729 _error->RevertToStack();
730 DropRepeats(TransList,"");
731 if(log != NULL) {
732 msg.str("");
733 ioprintf(msg, _("Found %zu package indexes, %zu source indexes, "
734 "%zu translation indexes and %zu signatures\n"),
735 List.size(), SourceList.size(), TransList.size(),
736 SigList.size());
737 log->Update(msg.str(), STEP_SCAN);
738 }
739
740 if (List.empty() == true && SourceList.empty() == true)
741 {
742 if (_config->FindB("APT::CDROM::NoMount",false) == false)
743 UnmountCdrom(CDROM);
744 return _error->Error(_("Unable to locate any package files, perhaps this is not a Debian Disc or the wrong architecture?"));
745 }
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();
764 for (; J != Name.end(); ++J)
765 if (*J == '"' || *J == ']' || *J == '[')
766 *J = '_';
767
768 if(log != NULL)
769 {
770 msg.str("");
771 ioprintf(msg, _("Found label '%s'\n"), Name.c_str());
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 {
781 if(log == NULL)
782 {
783 if (_config->FindB("APT::CDROM::NoMount",false) == false)
784 UnmountCdrom(CDROM);
785 return _error->Error("No disc name found and no way to ask for it");
786 }
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();
809 for (; J != Name.end(); ++J)
810 if (*J == '"' || *J == ']' || *J == '[')
811 *J = '_';
812
813 Database.Set("CD::" + ID,Name);
814 if(log != NULL)
815 {
816 msg.str("");
817 ioprintf(msg, _("This disc is called: \n'%s'\n"), Name.c_str());
818 log->Update(msg.str());
819 log->Update(_("Copying package lists..."), STEP_COPY);
820 }
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;
829 TranslationsCopy TransCopy;
830 if (Copy.CopyPackages(CDROM,Name,List, log) == false ||
831 SrcCopy.CopyPackages(CDROM,Name,SourceList, log) == false ||
832 TransCopy.CopyTranslations(CDROM,Name,TransList, log) == false)
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
845 if(log != NULL)
846 log->Update(_("Writing new source list\n"), STEP_WRITE);
847 if (WriteSourceList(Name,List,false) == false ||
848 WriteSourceList(Name,SourceList,true) == false)
849 return false;
850 }
851
852 // Print the sourcelist entries
853 if(log != NULL)
854 log->Update(_("Source list entries for this disc are:\n"));
855
856 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
857 {
858 string::size_type Space = (*I).find(' ');
859 if (Space == string::npos)
860 {
861 if (_config->FindB("APT::CDROM::NoMount",false) == false)
862 UnmountCdrom(CDROM);
863 return _error->Error("Internal error");
864 }
865
866 if(log != NULL)
867 {
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
875 for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); ++I)
876 {
877 string::size_type Space = (*I).find(' ');
878 if (Space == string::npos)
879 {
880 if (_config->FindB("APT::CDROM::NoMount",false) == false)
881 UnmountCdrom(CDROM);
882 return _error->Error("Internal error");
883 }
884
885 if(log != NULL) {
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
893 // Unmount and finish
894 if (_config->FindB("APT::CDROM::NoMount",false) == false) {
895 if (log != NULL)
896 log->Update(_("Unmounting CD-ROM...\n"), STEP_LAST);
897 UnmountCdrom(CDROM);
898 }
899
900 return true;
901 }
902 /*}}}*/
903 pkgUdevCdromDevices::pkgUdevCdromDevices() /*{{{*/
904 : libudev_handle(NULL)
905 {
906
907 }
908 /*}}}*/
909
910 bool
911 pkgUdevCdromDevices::Dlopen() /*{{{*/
912 {
913 // alread open
914 if(libudev_handle != NULL)
915 return true;
916
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");
926 udev_enumerate_add_match_sysattr = (int (*)(udev_enumerate*, const char*, const char*))dlsym(h, "udev_enumerate_add_match_sysattr");
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 }
939 /*}}}*/
940 /*{{{*/
941 // convenience interface, this will just call ScanForRemovable
942 vector<CdromDevice>
943 pkgUdevCdromDevices::Scan()
944 {
945 bool CdromOnly = _config->FindB("APT::cdrom::CdromOnly", true);
946 return ScanForRemovable(CdromOnly);
947 };
948 /*}}}*/
949 /*{{{*/
950 vector<CdromDevice>
951 pkgUdevCdromDevices::ScanForRemovable(bool CdromOnly)
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);
963 if (CdromOnly)
964 udev_enumerate_add_match_property(enumerate, "ID_CDROM", "1");
965 else {
966 udev_enumerate_add_match_sysattr(enumerate, "removable", "1");
967 }
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);
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
986 mountpath = FindMountPointForDevice(devnode);
987
988 // fill in the struct
989 cdrom.DeviceName = string(devnode);
990 if (mountpath != "") {
991 cdrom.MountPath = mountpath;
992 string s = string(mountpath);
993 cdrom.Mounted = IsMounted(s);
994 } else {
995 cdrom.Mounted = false;
996 cdrom.MountPath = "";
997 }
998 cdrom_devices.push_back(cdrom);
999 }
1000 return cdrom_devices;
1001 }
1002 /*}}}*/
1003
1004 pkgUdevCdromDevices::~pkgUdevCdromDevices() /*{{{*/
1005 {
1006 if (libudev_handle != NULL)
1007 dlclose(libudev_handle);
1008 }
1009 /*}}}*/