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