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