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