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