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