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