* apt-pkg/cdrom.cc:
[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 // Get a list of all the inodes
281 ino_t *Inodes = new ino_t[List.size()];
282 for (unsigned int I = 0; I != List.size(); ++I)
283 {
284 struct stat Buf;
285 bool found = false;
286
287 std::vector<APT::Configuration::Compressor> const compressor = APT::Configuration::getCompressors();
288 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
289 c != compressor.end(); ++c)
290 {
291 std::string filename = std::string(List[I]).append(Name).append(c->Extension);
292 if (stat(filename.c_str(), &Buf) != 0)
293 continue;
294 Inodes[I] = Buf.st_ino;
295 found = true;
296 break;
297 }
298
299 if (found == false)
300 _error->Errno("stat","Failed to stat %s%s",List[I].c_str(), Name);
301 }
302
303 if (_error->PendingError() == true) {
304 delete[] Inodes;
305 return false;
306 }
307
308 // Look for dups
309 for (unsigned int I = 0; I != List.size(); I++)
310 {
311 for (unsigned int J = I+1; J < List.size(); J++)
312 {
313 // No match
314 if (Inodes[J] != Inodes[I])
315 continue;
316
317 // We score the two paths.. and erase one
318 int ScoreA = Score(List[I]);
319 int ScoreB = Score(List[J]);
320 if (ScoreA < ScoreB)
321 {
322 List[I] = string();
323 break;
324 }
325
326 List[J] = string();
327 }
328 }
329 delete[] Inodes;
330
331 // Wipe erased entries
332 for (unsigned int I = 0; I < List.size();)
333 {
334 if (List[I].empty() == false)
335 I++;
336 else
337 List.erase(List.begin()+I);
338 }
339
340 return true;
341 }
342 /*}}}*/
343 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
344 // ---------------------------------------------------------------------
345 /* This takes the list of source list expressed entires and collects
346 similar ones to form a single entry for each dist */
347 void pkgCdrom::ReduceSourcelist(string CD,vector<string> &List)
348 {
349 sort(List.begin(),List.end());
350
351 // Collect similar entries
352 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
353 {
354 // Find a space..
355 string::size_type Space = (*I).find(' ');
356 if (Space == string::npos)
357 continue;
358 string::size_type SSpace = (*I).find(' ',Space + 1);
359 if (SSpace == string::npos)
360 continue;
361
362 string Word1 = string(*I,Space,SSpace-Space);
363 string Prefix = string(*I,0,Space);
364 for (vector<string>::iterator J = List.begin(); J != I; ++J)
365 {
366 // Find a space..
367 string::size_type Space2 = (*J).find(' ');
368 if (Space2 == string::npos)
369 continue;
370 string::size_type SSpace2 = (*J).find(' ',Space2 + 1);
371 if (SSpace2 == string::npos)
372 continue;
373
374 if (string(*J,0,Space2) != Prefix)
375 continue;
376 if (string(*J,Space2,SSpace2-Space2) != Word1)
377 continue;
378
379 *J += string(*I,SSpace);
380 *I = string();
381 }
382 }
383
384 // Wipe erased entries
385 for (unsigned int I = 0; I < List.size();)
386 {
387 if (List[I].empty() == false)
388 I++;
389 else
390 List.erase(List.begin()+I);
391 }
392 }
393 /*}}}*/
394 // WriteDatabase - Write the CDROM Database file /*{{{*/
395 // ---------------------------------------------------------------------
396 /* We rewrite the configuration class associated with the cdrom database. */
397 bool pkgCdrom::WriteDatabase(Configuration &Cnf)
398 {
399 string DFile = _config->FindFile("Dir::State::cdroms");
400 string NewFile = DFile + ".new";
401
402 unlink(NewFile.c_str());
403 ofstream Out(NewFile.c_str());
404 if (!Out)
405 return _error->Errno("ofstream::ofstream",
406 "Failed to open %s.new",DFile.c_str());
407
408 /* Write out all of the configuration directives by walking the
409 configuration tree */
410 const Configuration::Item *Top = Cnf.Tree(0);
411 for (; Top != 0;)
412 {
413 // Print the config entry
414 if (Top->Value.empty() == false)
415 Out << Top->FullTag() + " \"" << Top->Value << "\";" << endl;
416
417 if (Top->Child != 0)
418 {
419 Top = Top->Child;
420 continue;
421 }
422
423 while (Top != 0 && Top->Next == 0)
424 Top = Top->Parent;
425 if (Top != 0)
426 Top = Top->Next;
427 }
428
429 Out.close();
430
431 link(DFile.c_str(),string(DFile + '~').c_str());
432 if (rename(NewFile.c_str(),DFile.c_str()) != 0)
433 return _error->Errno("rename","Failed to rename %s.new to %s",
434 DFile.c_str(),DFile.c_str());
435
436 return true;
437 }
438 /*}}}*/
439 // WriteSourceList - Write an updated sourcelist /*{{{*/
440 // ---------------------------------------------------------------------
441 /* This reads the old source list and copies it into the new one. It
442 appends the new CDROM entires just after the first block of comments.
443 This places them first in the file. It also removes any old entries
444 that were the same. */
445 bool pkgCdrom::WriteSourceList(string Name,vector<string> &List,bool Source)
446 {
447 if (List.empty() == true)
448 return true;
449
450 string File = _config->FindFile("Dir::Etc::sourcelist");
451
452 // Open the stream for reading
453 ifstream F((FileExists(File)?File.c_str():"/dev/null"),
454 ios::in );
455 if (!F != 0)
456 return _error->Errno("ifstream::ifstream","Opening %s",File.c_str());
457
458 string NewFile = File + ".new";
459 unlink(NewFile.c_str());
460 ofstream Out(NewFile.c_str());
461 if (!Out)
462 return _error->Errno("ofstream::ofstream",
463 "Failed to open %s.new",File.c_str());
464
465 // Create a short uri without the path
466 string ShortURI = "cdrom:[" + Name + "]/";
467 string ShortURI2 = "cdrom:" + Name + "/"; // For Compatibility
468
469 string Type;
470 if (Source == true)
471 Type = "deb-src";
472 else
473 Type = "deb";
474
475 char Buffer[300];
476 int CurLine = 0;
477 bool First = true;
478 while (F.eof() == false)
479 {
480 F.getline(Buffer,sizeof(Buffer));
481 CurLine++;
482 if (F.fail() && !F.eof())
483 return _error->Error(_("Line %u too long in source list %s."),
484 CurLine,File.c_str());
485 _strtabexpand(Buffer,sizeof(Buffer));
486 _strstrip(Buffer);
487
488 // Comment or blank
489 if (Buffer[0] == '#' || Buffer[0] == 0)
490 {
491 Out << Buffer << endl;
492 continue;
493 }
494
495 if (First == true)
496 {
497 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
498 {
499 string::size_type Space = (*I).find(' ');
500 if (Space == string::npos)
501 return _error->Error("Internal error");
502 Out << Type << " cdrom:[" << Name << "]/" << string(*I,0,Space) <<
503 " " << string(*I,Space+1) << endl;
504 }
505 }
506 First = false;
507
508 // Grok it
509 string cType;
510 string URI;
511 const char *C = Buffer;
512 if (ParseQuoteWord(C,cType) == false ||
513 ParseQuoteWord(C,URI) == false)
514 {
515 Out << Buffer << endl;
516 continue;
517 }
518
519 // Emit lines like this one
520 if (cType != Type || (string(URI,0,ShortURI.length()) != ShortURI &&
521 string(URI,0,ShortURI.length()) != ShortURI2))
522 {
523 Out << Buffer << endl;
524 continue;
525 }
526 }
527
528 // Just in case the file was empty
529 if (First == true)
530 {
531 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
532 {
533 string::size_type Space = (*I).find(' ');
534 if (Space == string::npos)
535 return _error->Error("Internal error");
536
537 Out << "deb cdrom:[" << Name << "]/" << string(*I,0,Space) <<
538 " " << string(*I,Space+1) << endl;
539 }
540 }
541
542 Out.close();
543
544 rename(File.c_str(),string(File + '~').c_str());
545 if (rename(NewFile.c_str(),File.c_str()) != 0)
546 return _error->Errno("rename","Failed to rename %s.new to %s",
547 File.c_str(),File.c_str());
548
549 return true;
550 }
551 /*}}}*/
552 bool pkgCdrom::Ident(string &ident, pkgCdromStatus *log) /*{{{*/
553 {
554 stringstream msg;
555
556 // Startup
557 string CDROM = _config->FindDir("Acquire::cdrom::mount");
558 if (CDROM[0] == '.')
559 CDROM= SafeGetCWD() + '/' + CDROM;
560
561 if (log != NULL)
562 {
563 msg.str("");
564 ioprintf(msg, _("Using CD-ROM mount point %s\nMounting CD-ROM\n"),
565 CDROM.c_str());
566 log->Update(msg.str());
567 }
568 if (MountCdrom(CDROM) == false)
569 return _error->Error("Failed to mount the cdrom.");
570
571 // Hash the CD to get an ID
572 if (log != NULL)
573 log->Update(_("Identifying.. "));
574
575
576 if (IdentCdrom(CDROM,ident) == false)
577 {
578 ident = "";
579 return false;
580 }
581
582 if (log != NULL)
583 {
584 msg.str("");
585 ioprintf(msg, "[%s]\n",ident.c_str());
586 log->Update(msg.str());
587 }
588
589 // Read the database
590 Configuration Database;
591 string DFile = _config->FindFile("Dir::State::cdroms");
592 if (FileExists(DFile) == true)
593 {
594 if (ReadConfigFile(Database,DFile) == false)
595 return _error->Error("Unable to read the cdrom database %s",
596 DFile.c_str());
597 }
598 if (log != NULL)
599 {
600 msg.str("");
601 ioprintf(msg, _("Stored label: %s\n"),
602 Database.Find("CD::"+ident).c_str());
603 log->Update(msg.str());
604 }
605
606 // Unmount and finish
607 if (_config->FindB("APT::CDROM::NoMount",false) == false)
608 {
609 if (log != NULL)
610 log->Update(_("Unmounting CD-ROM...\n"), STEP_LAST);
611 UnmountCdrom(CDROM);
612 }
613
614 return true;
615 }
616 /*}}}*/
617 bool pkgCdrom::Add(pkgCdromStatus *log) /*{{{*/
618 {
619 stringstream msg;
620
621 // Startup
622 string CDROM = _config->FindDir("Acquire::cdrom::mount");
623 if (CDROM[0] == '.')
624 CDROM= SafeGetCWD() + '/' + CDROM;
625
626 if(log != NULL)
627 {
628 log->SetTotal(STEP_LAST);
629 msg.str("");
630 ioprintf(msg, _("Using CD-ROM mount point %s\n"), CDROM.c_str());
631 log->Update(msg.str(), STEP_PREPARE);
632 }
633
634 // Read the database
635 Configuration Database;
636 string DFile = _config->FindFile("Dir::State::cdroms");
637 if (FileExists(DFile) == true)
638 {
639 if (ReadConfigFile(Database,DFile) == false)
640 return _error->Error("Unable to read the cdrom database %s",
641 DFile.c_str());
642 }
643
644 // Unmount the CD and get the user to put in the one they want
645 if (_config->FindB("APT::CDROM::NoMount",false) == false)
646 {
647 if(log != NULL)
648 log->Update(_("Unmounting CD-ROM\n"), STEP_UNMOUNT);
649 UnmountCdrom(CDROM);
650
651 if(log != NULL)
652 {
653 log->Update(_("Waiting for disc...\n"), STEP_WAIT);
654 if(!log->ChangeCdrom()) {
655 // user aborted
656 return false;
657 }
658 }
659
660 // Mount the new CDROM
661 if(log != NULL)
662 log->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT);
663
664 if (MountCdrom(CDROM) == false)
665 return _error->Error("Failed to mount the cdrom.");
666 }
667
668 // Hash the CD to get an ID
669 if(log != NULL)
670 log->Update(_("Identifying.. "), STEP_IDENT);
671 string ID;
672 if (IdentCdrom(CDROM,ID) == false)
673 {
674 if (log != NULL)
675 log->Update("\n");
676 return false;
677 }
678 if(log != NULL)
679 {
680 log->Update("["+ID+"]\n");
681 log->Update(_("Scanning disc for index files..\n"),STEP_SCAN);
682 }
683
684 // Get the CD structure
685 vector<string> List;
686 vector<string> SourceList;
687 vector<string> SigList;
688 vector<string> TransList;
689 string StartDir = SafeGetCWD();
690 string InfoDir;
691 if (FindPackages(CDROM,List,SourceList, SigList,TransList,InfoDir,log) == false)
692 {
693 if (log != NULL)
694 log->Update("\n");
695 return false;
696 }
697
698 chdir(StartDir.c_str());
699
700 if (_config->FindB("Debug::aptcdrom",false) == true)
701 {
702 cout << "I found (binary):" << endl;
703 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
704 cout << *I << endl;
705 cout << "I found (source):" << endl;
706 for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); ++I)
707 cout << *I << endl;
708 cout << "I found (Signatures):" << endl;
709 for (vector<string>::iterator I = SigList.begin(); I != SigList.end(); ++I)
710 cout << *I << endl;
711 }
712
713 //log->Update(_("Cleaning package lists..."), STEP_CLEAN);
714
715 // Fix up the list
716 DropBinaryArch(List);
717 DropRepeats(List,"Packages");
718 DropRepeats(SourceList,"Sources");
719 DropRepeats(SigList,"Release.gpg");
720 DropRepeats(SigList,"InRelease");
721 DropRepeats(TransList,"");
722 if(log != NULL) {
723 msg.str("");
724 ioprintf(msg, _("Found %zu package indexes, %zu source indexes, "
725 "%zu translation indexes and %zu signatures\n"),
726 List.size(), SourceList.size(), TransList.size(),
727 SigList.size());
728 log->Update(msg.str(), STEP_SCAN);
729 }
730
731 if (List.empty() == true && SourceList.empty() == true)
732 {
733 if (_config->FindB("APT::CDROM::NoMount",false) == false)
734 UnmountCdrom(CDROM);
735 return _error->Error(_("Unable to locate any package files, perhaps this is not a Debian Disc or the wrong architecture?"));
736 }
737
738 // Check if the CD is in the database
739 string Name;
740 if (Database.Exists("CD::" + ID) == false ||
741 _config->FindB("APT::CDROM::Rename",false) == true)
742 {
743 // Try to use the CDs label if at all possible
744 if (InfoDir.empty() == false &&
745 FileExists(InfoDir + "/info") == true)
746 {
747 ifstream F(string(InfoDir + "/info").c_str());
748 if (!F == 0)
749 getline(F,Name);
750
751 if (Name.empty() == false)
752 {
753 // Escape special characters
754 string::iterator J = Name.begin();
755 for (; J != Name.end(); ++J)
756 if (*J == '"' || *J == ']' || *J == '[')
757 *J = '_';
758
759 if(log != NULL)
760 {
761 msg.str("");
762 ioprintf(msg, _("Found label '%s'\n"), Name.c_str());
763 log->Update(msg.str());
764 }
765 Database.Set("CD::" + ID + "::Label",Name);
766 }
767 }
768
769 if (_config->FindB("APT::CDROM::Rename",false) == true ||
770 Name.empty() == true)
771 {
772 if(log == NULL)
773 {
774 if (_config->FindB("APT::CDROM::NoMount",false) == false)
775 UnmountCdrom(CDROM);
776 return _error->Error("No disc name found and no way to ask for it");
777 }
778
779 while(true) {
780 if(!log->AskCdromName(Name)) {
781 // user canceld
782 return false;
783 }
784 cout << "Name: '" << Name << "'" << endl;
785
786 if (Name.empty() == false &&
787 Name.find('"') == string::npos &&
788 Name.find('[') == string::npos &&
789 Name.find(']') == string::npos)
790 break;
791 log->Update(_("That is not a valid name, try again.\n"));
792 }
793 }
794 }
795 else
796 Name = Database.Find("CD::" + ID);
797
798 // Escape special characters
799 string::iterator J = Name.begin();
800 for (; J != Name.end(); ++J)
801 if (*J == '"' || *J == ']' || *J == '[')
802 *J = '_';
803
804 Database.Set("CD::" + ID,Name);
805 if(log != NULL)
806 {
807 msg.str("");
808 ioprintf(msg, _("This disc is called: \n'%s'\n"), Name.c_str());
809 log->Update(msg.str());
810 log->Update(_("Copying package lists..."), STEP_COPY);
811 }
812 // take care of the signatures and copy them if they are ok
813 // (we do this before PackageCopy as it modifies "List" and "SourceList")
814 SigVerify SignVerify;
815 SignVerify.CopyAndVerify(CDROM, Name, SigList, List, SourceList);
816
817 // Copy the package files to the state directory
818 PackageCopy Copy;
819 SourceCopy SrcCopy;
820 TranslationsCopy TransCopy;
821 if (Copy.CopyPackages(CDROM,Name,List, log) == false ||
822 SrcCopy.CopyPackages(CDROM,Name,SourceList, log) == false ||
823 TransCopy.CopyTranslations(CDROM,Name,TransList, log) == false)
824 return false;
825
826 // reduce the List so that it takes less space in sources.list
827 ReduceSourcelist(CDROM,List);
828 ReduceSourcelist(CDROM,SourceList);
829
830 // Write the database and sourcelist
831 if (_config->FindB("APT::cdrom::NoAct",false) == false)
832 {
833 if (WriteDatabase(Database) == false)
834 return false;
835
836 if(log != NULL)
837 log->Update(_("Writing new source list\n"), STEP_WRITE);
838 if (WriteSourceList(Name,List,false) == false ||
839 WriteSourceList(Name,SourceList,true) == false)
840 return false;
841 }
842
843 // Print the sourcelist entries
844 if(log != NULL)
845 log->Update(_("Source list entries for this disc are:\n"));
846
847 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
848 {
849 string::size_type Space = (*I).find(' ');
850 if (Space == string::npos)
851 {
852 if (_config->FindB("APT::CDROM::NoMount",false) == false)
853 UnmountCdrom(CDROM);
854 return _error->Error("Internal error");
855 }
856
857 if(log != NULL)
858 {
859 msg.str("");
860 msg << "deb cdrom:[" << Name << "]/" << string(*I,0,Space) <<
861 " " << string(*I,Space+1) << endl;
862 log->Update(msg.str());
863 }
864 }
865
866 for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); ++I)
867 {
868 string::size_type Space = (*I).find(' ');
869 if (Space == string::npos)
870 {
871 if (_config->FindB("APT::CDROM::NoMount",false) == false)
872 UnmountCdrom(CDROM);
873 return _error->Error("Internal error");
874 }
875
876 if(log != NULL) {
877 msg.str("");
878 msg << "deb-src cdrom:[" << Name << "]/" << string(*I,0,Space) <<
879 " " << string(*I,Space+1) << endl;
880 log->Update(msg.str());
881 }
882 }
883
884 // Unmount and finish
885 if (_config->FindB("APT::CDROM::NoMount",false) == false) {
886 if (log != NULL)
887 log->Update(_("Unmounting CD-ROM...\n"), STEP_LAST);
888 UnmountCdrom(CDROM);
889 }
890
891 return true;
892 }
893 /*}}}*/
894 pkgUdevCdromDevices::pkgUdevCdromDevices() /*{{{*/
895 : libudev_handle(NULL)
896 {
897
898 }
899 /*}}}*/
900
901 bool
902 pkgUdevCdromDevices::Dlopen() /*{{{*/
903 {
904 // alread open
905 if(libudev_handle != NULL)
906 return true;
907
908 // see if we can get libudev
909 void *h = ::dlopen("libudev.so.0", RTLD_LAZY);
910 if(h == NULL)
911 return false;
912
913 // get the pointers to the udev structs
914 libudev_handle = h;
915 udev_new = (udev* (*)(void)) dlsym(h, "udev_new");
916 udev_enumerate_add_match_property = (int (*)(udev_enumerate*, const char*, const char*))dlsym(h, "udev_enumerate_add_match_property");
917 udev_enumerate_add_match_sysattr = (int (*)(udev_enumerate*, const char*, const char*))dlsym(h, "udev_enumerate_add_match_sysattr");
918 udev_enumerate_scan_devices = (int (*)(udev_enumerate*))dlsym(h, "udev_enumerate_scan_devices");
919 udev_enumerate_get_list_entry = (udev_list_entry* (*)(udev_enumerate*))dlsym(h, "udev_enumerate_get_list_entry");
920 udev_device_new_from_syspath = (udev_device* (*)(udev*, const char*))dlsym(h, "udev_device_new_from_syspath");
921 udev_enumerate_get_udev = (udev* (*)(udev_enumerate*))dlsym(h, "udev_enumerate_get_udev");
922 udev_list_entry_get_name = (const char* (*)(udev_list_entry*))dlsym(h, "udev_list_entry_get_name");
923 udev_device_get_devnode = (const char* (*)(udev_device*))dlsym(h, "udev_device_get_devnode");
924 udev_enumerate_new = (udev_enumerate* (*)(udev*))dlsym(h, "udev_enumerate_new");
925 udev_list_entry_get_next = (udev_list_entry* (*)(udev_list_entry*))dlsym(h, "udev_list_entry_get_next");
926 udev_device_get_property_value = (const char* (*)(udev_device *, const char *))dlsym(h, "udev_device_get_property_value");
927
928 return true;
929 }
930 /*}}}*/
931 /*{{{*/
932 // convenience interface, this will just call ScanForRemovable
933 vector<CdromDevice>
934 pkgUdevCdromDevices::Scan()
935 {
936 bool CdromOnly = _config->FindB("APT::cdrom::CdromOnly", true);
937 return ScanForRemovable(CdromOnly);
938 };
939 /*}}}*/
940 /*{{{*/
941 vector<CdromDevice>
942 pkgUdevCdromDevices::ScanForRemovable(bool CdromOnly)
943 {
944 vector<CdromDevice> cdrom_devices;
945 struct udev_enumerate *enumerate;
946 struct udev_list_entry *l, *devices;
947 struct udev *udev_ctx;
948
949 if(libudev_handle == NULL)
950 return cdrom_devices;
951
952 udev_ctx = udev_new();
953 enumerate = udev_enumerate_new (udev_ctx);
954 if (CdromOnly)
955 udev_enumerate_add_match_property(enumerate, "ID_CDROM", "1");
956 else {
957 udev_enumerate_add_match_sysattr(enumerate, "removable", "1");
958 }
959
960 udev_enumerate_scan_devices (enumerate);
961 devices = udev_enumerate_get_list_entry (enumerate);
962 for (l = devices; l != NULL; l = udev_list_entry_get_next (l))
963 {
964 CdromDevice cdrom;
965 struct udev_device *udevice;
966 udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerate), udev_list_entry_get_name (l));
967 if (udevice == NULL)
968 continue;
969 const char* devnode = udev_device_get_devnode(udevice);
970
971 // try fstab_dir first
972 string mountpath;
973 const char* mp = udev_device_get_property_value(udevice, "FSTAB_DIR");
974 if (mp)
975 mountpath = string(mp);
976 else
977 mountpath = FindMountPointForDevice(devnode);
978
979 // fill in the struct
980 cdrom.DeviceName = string(devnode);
981 if (mountpath != "") {
982 cdrom.MountPath = mountpath;
983 string s = string(mountpath);
984 cdrom.Mounted = IsMounted(s);
985 } else {
986 cdrom.Mounted = false;
987 cdrom.MountPath = "";
988 }
989 cdrom_devices.push_back(cdrom);
990 }
991 return cdrom_devices;
992 }
993 /*}}}*/
994
995 pkgUdevCdromDevices::~pkgUdevCdromDevices() /*{{{*/
996 {
997 if (libudev_handle != NULL)
998 dlclose(libudev_handle);
999 }
1000 /*}}}*/