]> git.saurik.com Git - apt.git/blame - apt-pkg/cdrom.cc
Merge branch 'debian/sid' into debian/experimental
[apt.git] / apt-pkg / cdrom.cc
CommitLineData
a75c6a6e
MZ
1/*
2 */
453b82a3
DK
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>
a75c6a6e 21#include <sys/stat.h>
a75c6a6e
MZ
22#include <dirent.h>
23#include <unistd.h>
24#include <stdio.h>
152ab79e 25#include <algorithm>
cbc9bed8 26#include <dlfcn.h>
a75c6a6e 27
ea542140
DK
28#include<apti18n.h>
29
a75c6a6e
MZ
30using 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. */
22f8568d
MV
38bool pkgCdrom::FindPackages(string CD,
39 vector<string> &List,
40 vector<string> &SList,
41 vector<string> &SigList,
42 vector<string> &TransList,
a75c6a6e
MZ
43 string &InfoDir, pkgCdromStatus *log,
44 unsigned int Depth)
45{
46 static ino_t Inodes[9];
22f8568d 47 DIR *D;
a75c6a6e
MZ
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
711078ae 63 if (DirectoryExists(".disk") == true)
a75c6a6e
MZ
64 {
65 if (InfoDir.empty() == true)
66 InfoDir = CD + ".disk/";
67 }
68
69 // Don't look into directories that have been marked to ingore.
711078ae 70 if (RealFileExists(".aptignr") == true)
a75c6a6e
MZ
71 return true;
72
a75c6a6e
MZ
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 */
212080b8 77 if (RealFileExists("Release.gpg") == true || RealFileExists("InRelease") == true)
a75c6a6e
MZ
78 {
79 SigList.push_back(CD);
80 }
82c8f08e 81
a75c6a6e
MZ
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 */
78c9276d
DK
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 {
711078ae 89 if (RealFileExists(std::string("Packages").append(c->Extension).c_str()) == false)
78c9276d
DK
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 {
711078ae 104 if (RealFileExists(std::string("Sources").append(c->Extension).c_str()) == false)
78c9276d
DK
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 }
22f8568d 116
01366a44 117 // see if we find translation indices
78c9276d 118 if (DirectoryExists("i18n") == true)
22f8568d
MV
119 {
120 D = opendir("i18n");
121 for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
122 {
78c9276d
DK
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)
22f8568d 128 {
78c9276d
DK
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;
4e86b003 139 file.erase(file.size() - fileext.size());
78c9276d
DK
140 TransList.push_back(CD + "i18n/" + file);
141 break;
142 }
22f8568d
MV
143 }
144 }
145 closedir(D);
146 }
147
22f8568d 148 D = opendir(".");
a75c6a6e
MZ
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
22f8568d 184 if (FindPackages(CD + Dir->d_name,List,SList,SigList,TransList,InfoDir,log,Depth+1) == false)
a75c6a6e
MZ
185 break;
186
187 if (chdir(CD.c_str()) != 0)
6070a346
DK
188 {
189 _error->Errno("chdir","Unable to change to %s", CD.c_str());
190 closedir(D);
191 return false;
192 }
a75c6a6e
MZ
193 };
194
195 closedir(D);
196
197 return !_error->PendingError();
198}
92fcbfc1 199 /*}}}*/
a75c6a6e
MZ
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. */
205int 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;
872b3d39
MV
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);
710aba4a 235 string cdromPath = _config->FindDir("Acquire::cdrom::mount");
872b3d39
MV
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
a75c6a6e
MZ
247 return Res;
248}
a75c6a6e
MZ
249 /*}}}*/
250// DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
251// ---------------------------------------------------------------------
252/* Here we drop everything that is not this machines arch */
253bool pkgCdrom::DropBinaryArch(vector<string> &List)
254{
5dd4c8b8 255
a75c6a6e
MZ
256 for (unsigned int I = 0; I < List.size(); I++)
257 {
258 const char *Str = List[I].c_str();
5dd4c8b8
DK
259 const char *Start, *End;
260 if ((Start = strstr(Str,"/binary-")) == 0)
a75c6a6e
MZ
261 continue;
262
5dd4c8b8
DK
263 // Between Start and End is the architecture
264 Start += 8;
265 if ((End = strstr(Start,"/")) != 0 && Start != End &&
73dfa041 266 APT::Configuration::checkArchitecture(string(Start, End)) == true)
5dd4c8b8
DK
267 continue; // okay, architecture is accepted
268
269 // not accepted -> Erase it
a75c6a6e 270 List.erase(List.begin() + I);
5dd4c8b8 271 --I; // the next entry is at the same index after the erase
a75c6a6e
MZ
272 }
273
c45233ea
DK
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 */
280bool 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
a75c6a6e
MZ
297 return true;
298}
92fcbfc1 299 /*}}}*/
a75c6a6e
MZ
300// DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
301// ---------------------------------------------------------------------
302/* Here we go and stat every file that we found and strip dup inodes. */
303bool pkgCdrom::DropRepeats(vector<string> &List,const char *Name)
304{
2c405a44 305 bool couldFindAllFiles = true;
a75c6a6e
MZ
306 // Get a list of all the inodes
307 ino_t *Inodes = new ino_t[List.size()];
78c9276d 308 for (unsigned int I = 0; I != List.size(); ++I)
a75c6a6e
MZ
309 {
310 struct stat Buf;
78c9276d
DK
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)
82c8f08e 316 {
78c9276d 317 std::string filename = std::string(List[I]).append(Name).append(c->Extension);
82c8f08e 318 if (stat(filename.c_str(), &Buf) != 0)
78c9276d
DK
319 continue;
320 Inodes[I] = Buf.st_ino;
321 found = true;
322 break;
82c8f08e 323 }
78c9276d
DK
324
325 if (found == false)
2c405a44
DK
326 {
327 _error->Errno("stat","Failed to stat %s%s",List[I].c_str(), Name);
328 couldFindAllFiles = false;
329 Inodes[I] = 0;
330 }
a75c6a6e 331 }
78c9276d 332
a75c6a6e
MZ
333 // Look for dups
334 for (unsigned int I = 0; I != List.size(); I++)
335 {
2c405a44
DK
336 if (Inodes[I] == 0)
337 continue;
a75c6a6e
MZ
338 for (unsigned int J = I+1; J < List.size(); J++)
339 {
340 // No match
2c405a44 341 if (Inodes[J] == 0 || Inodes[J] != Inodes[I])
a75c6a6e
MZ
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 }
3a4477a4
DK
356 delete[] Inodes;
357
a75c6a6e
MZ
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
2c405a44 367 return couldFindAllFiles;
a75c6a6e
MZ
368}
369 /*}}}*/
a75c6a6e
MZ
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 */
65512241 374void pkgCdrom::ReduceSourcelist(string /*CD*/,vector<string> &List)
a75c6a6e
MZ
375{
376 sort(List.begin(),List.end());
377
378 // Collect similar entries
f7f0d6c7 379 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
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);
8628c2f7 391 string Component = string(*I,SSpace);
f7f0d6c7 392 for (vector<string>::iterator J = List.begin(); J != I; ++J)
a75c6a6e
MZ
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;
8628c2f7
DK
406
407 string Component2 = string(*J, SSpace2) + " ";
408 if (Component2.find(Component + " ") == std::string::npos)
409 *J += Component;
410 I->clear();
a75c6a6e
MZ
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. */
427bool 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 */
ab59c1ca 440 Cnf.Dump(Out, NULL, "%f \"%v\";\n", false);
a75c6a6e
MZ
441
442 Out.close();
849e64ac 443
c1cd1ac2 444 if (FileExists(DFile) == true)
e788a834 445 rename(DFile.c_str(), (DFile + '~').c_str());
a75c6a6e
MZ
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. */
459bool pkgCdrom::WriteSourceList(string Name,vector<string> &List,bool Source)
460{
f7f0d6c7 461 if (List.empty() == true)
a75c6a6e
MZ
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++;
13e8426f
MV
496 if (F.fail() && !F.eof())
497 return _error->Error(_("Line %u too long in source list %s."),
498 CurLine,File.c_str());
a75c6a6e
MZ
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 {
f7f0d6c7 511 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
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 {
f7f0d6c7 545 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
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
e788a834 558 rename(File.c_str(), (File + '~').c_str());
a75c6a6e
MZ
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}
92fcbfc1 565 /*}}}*/
7187074b
JO
566bool 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 /*}}}*/
b374004b 575bool pkgCdrom::MountAndIdentCDROM(Configuration &Database, std::string &CDROM, std::string &ident, pkgCdromStatus * const log, bool const interactive)/*{{{*/
a75c6a6e 576{
a75c6a6e 577 // Startup
ce55512d 578 CDROM = _config->FindDir("Acquire::cdrom::mount");
a75c6a6e
MZ
579 if (CDROM[0] == '.')
580 CDROM= SafeGetCWD() + '/' + CDROM;
581
6070a346
DK
582 if (log != NULL)
583 {
ce55512d
DK
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);
a75c6a6e 588 }
62dcbf84
JO
589
590 // Unmount the CD and get the user to put in the one they want
ce55512d 591 if (_config->FindB("APT::CDROM::NoMount", false) == false)
62dcbf84 592 {
b374004b 593 if (interactive == true)
62dcbf84 594 {
7187074b 595 UnmountCDROM(CDROM, log);
b374004b
DK
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 }
62dcbf84
JO
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 }
a75c6a6e 614
7187074b
JO
615 if (IsMounted(CDROM) == false)
616 return _error->Error("Failed to mount the cdrom.");
617
a75c6a6e 618 // Hash the CD to get an ID
6070a346 619 if (log != NULL)
1166ea79 620 log->Update(_("Identifying... "), STEP_IDENT);
a75c6a6e
MZ
621
622 if (IdentCdrom(CDROM,ident) == false)
623 {
624 ident = "";
ce55512d
DK
625 if (log != NULL)
626 log->Update("\n");
7187074b 627 UnmountCDROM(CDROM, NULL);
a75c6a6e
MZ
628 return false;
629 }
630
6070a346
DK
631 if (log != NULL)
632 {
ce55512d
DK
633 string msg;
634 strprintf(msg, "[%s]\n", ident.c_str());
635 log->Update(msg);
6070a346 636 }
a75c6a6e
MZ
637
638 // Read the database
a75c6a6e
MZ
639 string DFile = _config->FindFile("Dir::State::cdroms");
640 if (FileExists(DFile) == true)
641 {
642 if (ReadConfigFile(Database,DFile) == false)
7187074b
JO
643 {
644 UnmountCDROM(CDROM, NULL);
a75c6a6e
MZ
645 return _error->Error("Unable to read the cdrom database %s",
646 DFile.c_str());
7187074b 647 }
a75c6a6e 648 }
ce55512d
DK
649 return true;
650}
651 /*}}}*/
652bool pkgCdrom::Ident(string &ident, pkgCdromStatus *log) /*{{{*/
653{
654 Configuration Database;
655 std::string CDROM;
b374004b 656 if (MountAndIdentCDROM(Database, CDROM, ident, log, false) == false)
ce55512d
DK
657 return false;
658
6070a346
DK
659 if (log != NULL)
660 {
ce55512d
DK
661 string msg;
662 strprintf(msg, _("Stored label: %s\n"),
663 Database.Find("CD::"+ident).c_str());
664 log->Update(msg);
a75c6a6e 665 }
1fcbe14d
OS
666
667 // Unmount and finish
7187074b 668 UnmountCDROM(CDROM, log);
a75c6a6e
MZ
669 return true;
670}
92fcbfc1
DK
671 /*}}}*/
672bool pkgCdrom::Add(pkgCdromStatus *log) /*{{{*/
a75c6a6e 673{
a75c6a6e 674 Configuration Database;
ce55512d 675 std::string ID, CDROM;
b374004b 676 if (MountAndIdentCDROM(Database, CDROM, ID, log, true) == false)
a75c6a6e 677 return false;
ce55512d 678
6070a346 679 if(log != NULL)
1166ea79 680 log->Update(_("Scanning disc for index files...\n"),STEP_SCAN);
6070a346 681
a75c6a6e
MZ
682 // Get the CD structure
683 vector<string> List;
684 vector<string> SourceList;
685 vector<string> SigList;
22f8568d 686 vector<string> TransList;
a75c6a6e
MZ
687 string StartDir = SafeGetCWD();
688 string InfoDir;
22f8568d 689 if (FindPackages(CDROM,List,SourceList, SigList,TransList,InfoDir,log) == false)
a75c6a6e 690 {
6070a346
DK
691 if (log != NULL)
692 log->Update("\n");
7187074b 693 UnmountCDROM(CDROM, NULL);
a75c6a6e
MZ
694 return false;
695 }
696
31bda500 697 if (chdir(StartDir.c_str()) != 0)
7187074b
JO
698 {
699 UnmountCDROM(CDROM, NULL);
31bda500 700 return _error->Errno("chdir","Unable to change to %s", StartDir.c_str());
7187074b 701 }
a75c6a6e
MZ
702
703 if (_config->FindB("Debug::aptcdrom",false) == true)
704 {
705 cout << "I found (binary):" << endl;
f7f0d6c7 706 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
707 cout << *I << endl;
708 cout << "I found (source):" << endl;
f7f0d6c7 709 for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); ++I)
a75c6a6e
MZ
710 cout << *I << endl;
711 cout << "I found (Signatures):" << endl;
f7f0d6c7 712 for (vector<string>::iterator I = SigList.begin(); I != SigList.end(); ++I)
a75c6a6e
MZ
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");
2c405a44
DK
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();
a75c6a6e 726 DropRepeats(SigList,"Release.gpg");
212080b8 727 DropRepeats(SigList,"InRelease");
2c405a44 728 _error->RevertToStack();
22f8568d 729 DropRepeats(TransList,"");
c45233ea
DK
730 if (_config->FindB("APT::CDROM::DropTranslation", true) == true)
731 DropTranslation(TransList);
6070a346 732 if(log != NULL) {
ce55512d
DK
733 string msg;
734 strprintf(msg, _("Found %zu package indexes, %zu source indexes, "
a376d6fd 735 "%zu translation indexes and %zu signatures\n"),
22f8568d
MV
736 List.size(), SourceList.size(), TransList.size(),
737 SigList.size());
ce55512d 738 log->Update(msg, STEP_SCAN);
a75c6a6e
MZ
739 }
740
f7f0d6c7 741 if (List.empty() == true && SourceList.empty() == true)
cdadf54b 742 {
7187074b 743 UnmountCDROM(CDROM, NULL);
d720a7d4 744 return _error->Error(_("Unable to locate any package files, perhaps this is not a Debian Disc or the wrong architecture?"));
cdadf54b 745 }
a75c6a6e
MZ
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 {
e788a834 756 ifstream F((InfoDir + "/info").c_str());
a75c6a6e
MZ
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();
f7f0d6c7 764 for (; J != Name.end(); ++J)
a75c6a6e
MZ
765 if (*J == '"' || *J == ']' || *J == '[')
766 *J = '_';
767
6070a346
DK
768 if(log != NULL)
769 {
ce55512d
DK
770 string msg;
771 strprintf(msg, _("Found label '%s'\n"), Name.c_str());
772 log->Update(msg);
a75c6a6e
MZ
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 {
6070a346 781 if(log == NULL)
cdadf54b 782 {
7187074b 783 UnmountCDROM(CDROM, NULL);
a75c6a6e 784 return _error->Error("No disc name found and no way to ask for it");
cdadf54b 785 }
a75c6a6e
MZ
786
787 while(true) {
788 if(!log->AskCdromName(Name)) {
789 // user canceld
7187074b 790 UnmountCDROM(CDROM, NULL);
a75c6a6e
MZ
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();
f7f0d6c7 809 for (; J != Name.end(); ++J)
a75c6a6e
MZ
810 if (*J == '"' || *J == ']' || *J == '[')
811 *J = '_';
812
813 Database.Set("CD::" + ID,Name);
6070a346
DK
814 if(log != NULL)
815 {
ce55512d
DK
816 string msg;
817 strprintf(msg, _("This disc is called: \n'%s'\n"), Name.c_str());
818 log->Update(msg);
6070a346 819 log->Update(_("Copying package lists..."), STEP_COPY);
a75c6a6e 820 }
b2ea1a47
DK
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)
7187074b
JO
827 {
828 UnmountCDROM(CDROM, NULL);
b2ea1a47 829 return _error->Errno("cdrom", _("List directory %spartial is missing."), listDir.c_str());
7187074b 830 }
b2ea1a47 831
a75c6a6e
MZ
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;
22f8568d 840 TranslationsCopy TransCopy;
a75c6a6e 841 if (Copy.CopyPackages(CDROM,Name,List, log) == false ||
22f8568d
MV
842 SrcCopy.CopyPackages(CDROM,Name,SourceList, log) == false ||
843 TransCopy.CopyTranslations(CDROM,Name,TransList, log) == false)
7187074b
JO
844 {
845 UnmountCDROM(CDROM, NULL);
a75c6a6e 846 return false;
7187074b 847 }
a75c6a6e
MZ
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)
7187074b
JO
857 {
858 UnmountCDROM(CDROM, NULL);
a75c6a6e 859 return false;
7187074b
JO
860 }
861
6070a346 862 if(log != NULL)
a75c6a6e 863 log->Update(_("Writing new source list\n"), STEP_WRITE);
a75c6a6e
MZ
864 if (WriteSourceList(Name,List,false) == false ||
865 WriteSourceList(Name,SourceList,true) == false)
7187074b
JO
866 {
867 UnmountCDROM(CDROM, NULL);
a75c6a6e 868 return false;
7187074b 869 }
a75c6a6e
MZ
870 }
871
872 // Print the sourcelist entries
6070a346 873 if(log != NULL)
db0db9fe 874 log->Update(_("Source list entries for this disc are:\n"));
a75c6a6e 875
f7f0d6c7 876 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
877 {
878 string::size_type Space = (*I).find(' ');
879 if (Space == string::npos)
cdadf54b 880 {
7187074b 881 UnmountCDROM(CDROM, NULL);
a75c6a6e 882 return _error->Error("Internal error");
cdadf54b 883 }
a75c6a6e 884
6070a346
DK
885 if(log != NULL)
886 {
ce55512d 887 stringstream msg;
a75c6a6e
MZ
888 msg << "deb cdrom:[" << Name << "]/" << string(*I,0,Space) <<
889 " " << string(*I,Space+1) << endl;
890 log->Update(msg.str());
891 }
892 }
893
f7f0d6c7 894 for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); ++I)
a75c6a6e
MZ
895 {
896 string::size_type Space = (*I).find(' ');
897 if (Space == string::npos)
cdadf54b 898 {
7187074b 899 UnmountCDROM(CDROM, NULL);
a75c6a6e 900 return _error->Error("Internal error");
cdadf54b 901 }
a75c6a6e 902
6070a346 903 if(log != NULL) {
ce55512d 904 stringstream msg;
a75c6a6e
MZ
905 msg << "deb-src cdrom:[" << Name << "]/" << string(*I,0,Space) <<
906 " " << string(*I,Space+1) << endl;
907 log->Update(msg.str());
908 }
909 }
910
a75c6a6e 911 // Unmount and finish
7187074b 912 UnmountCDROM(CDROM, log);
a75c6a6e
MZ
913 return true;
914}
92fcbfc1 915 /*}}}*/
25613a61
DK
916pkgUdevCdromDevices::pkgUdevCdromDevices() /*{{{*/
917: libudev_handle(NULL), udev_new(NULL), udev_enumerate_add_match_property(NULL),
918 udev_enumerate_scan_devices(NULL), udev_enumerate_get_list_entry(NULL),
919 udev_device_new_from_syspath(NULL), udev_enumerate_get_udev(NULL),
920 udev_list_entry_get_name(NULL), udev_device_get_devnode(NULL),
921 udev_enumerate_new(NULL), udev_list_entry_get_next(NULL),
922 udev_device_get_property_value(NULL), udev_enumerate_add_match_sysattr(NULL)
cbc9bed8 923{
cbc9bed8 924}
3e2d7cce 925 /*}}}*/
cbc9bed8
MV
926
927bool
3e2d7cce 928pkgUdevCdromDevices::Dlopen() /*{{{*/
cbc9bed8 929{
49cb36fc 930 // alread open
76fe5db7 931 if(libudev_handle != NULL)
49cb36fc
MV
932 return true;
933
cbc9bed8
MV
934 // see if we can get libudev
935 void *h = ::dlopen("libudev.so.0", RTLD_LAZY);
936 if(h == NULL)
937 return false;
938
939 // get the pointers to the udev structs
940 libudev_handle = h;
941 udev_new = (udev* (*)(void)) dlsym(h, "udev_new");
942 udev_enumerate_add_match_property = (int (*)(udev_enumerate*, const char*, const char*))dlsym(h, "udev_enumerate_add_match_property");
b7bc31eb 943 udev_enumerate_add_match_sysattr = (int (*)(udev_enumerate*, const char*, const char*))dlsym(h, "udev_enumerate_add_match_sysattr");
cbc9bed8
MV
944 udev_enumerate_scan_devices = (int (*)(udev_enumerate*))dlsym(h, "udev_enumerate_scan_devices");
945 udev_enumerate_get_list_entry = (udev_list_entry* (*)(udev_enumerate*))dlsym(h, "udev_enumerate_get_list_entry");
946 udev_device_new_from_syspath = (udev_device* (*)(udev*, const char*))dlsym(h, "udev_device_new_from_syspath");
947 udev_enumerate_get_udev = (udev* (*)(udev_enumerate*))dlsym(h, "udev_enumerate_get_udev");
948 udev_list_entry_get_name = (const char* (*)(udev_list_entry*))dlsym(h, "udev_list_entry_get_name");
949 udev_device_get_devnode = (const char* (*)(udev_device*))dlsym(h, "udev_device_get_devnode");
950 udev_enumerate_new = (udev_enumerate* (*)(udev*))dlsym(h, "udev_enumerate_new");
951 udev_list_entry_get_next = (udev_list_entry* (*)(udev_list_entry*))dlsym(h, "udev_list_entry_get_next");
952 udev_device_get_property_value = (const char* (*)(udev_device *, const char *))dlsym(h, "udev_device_get_property_value");
953
954 return true;
955}
3e2d7cce 956 /*}}}*/
f4c4a24e 957 /*{{{*/
f2e4a11d 958// convenience interface, this will just call ScanForRemovable
f4c4a24e
MV
959vector<CdromDevice>
960pkgUdevCdromDevices::Scan()
d3e8fbb3 961{
c9952021 962 bool CdromOnly = _config->FindB("APT::cdrom::CdromOnly", true);
d3e8fbb3
DK
963 return ScanForRemovable(CdromOnly);
964}
f4c4a24e
MV
965 /*}}}*/
966 /*{{{*/
cbc9bed8 967vector<CdromDevice>
f4c4a24e 968pkgUdevCdromDevices::ScanForRemovable(bool CdromOnly)
cbc9bed8
MV
969{
970 vector<CdromDevice> cdrom_devices;
971 struct udev_enumerate *enumerate;
972 struct udev_list_entry *l, *devices;
973 struct udev *udev_ctx;
974
975 if(libudev_handle == NULL)
976 return cdrom_devices;
977
978 udev_ctx = udev_new();
979 enumerate = udev_enumerate_new (udev_ctx);
f4c4a24e
MV
980 if (CdromOnly)
981 udev_enumerate_add_match_property(enumerate, "ID_CDROM", "1");
e344ad65 982 else {
f4c4a24e 983 udev_enumerate_add_match_sysattr(enumerate, "removable", "1");
e344ad65 984 }
cbc9bed8
MV
985
986 udev_enumerate_scan_devices (enumerate);
987 devices = udev_enumerate_get_list_entry (enumerate);
988 for (l = devices; l != NULL; l = udev_list_entry_get_next (l))
989 {
990 CdromDevice cdrom;
991 struct udev_device *udevice;
992 udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerate), udev_list_entry_get_name (l));
993 if (udevice == NULL)
994 continue;
995 const char* devnode = udev_device_get_devnode(udevice);
02aa6f67
MV
996
997 // try fstab_dir first
998 string mountpath;
999 const char* mp = udev_device_get_property_value(udevice, "FSTAB_DIR");
1000 if (mp)
1001 mountpath = string(mp);
1002 else
ef381816
MV
1003 mountpath = FindMountPointForDevice(devnode);
1004
cbc9bed8
MV
1005 // fill in the struct
1006 cdrom.DeviceName = string(devnode);
02aa6f67 1007 if (mountpath != "") {
cbc9bed8 1008 cdrom.MountPath = mountpath;
e788a834 1009 string s = mountpath;
be5b5581 1010 cdrom.Mounted = IsMounted(s);
cbc9bed8
MV
1011 } else {
1012 cdrom.Mounted = false;
1013 cdrom.MountPath = "";
1014 }
1015 cdrom_devices.push_back(cdrom);
1016 }
1017 return cdrom_devices;
1018}
3e2d7cce 1019 /*}}}*/
cbc9bed8 1020
3e2d7cce 1021pkgUdevCdromDevices::~pkgUdevCdromDevices() /*{{{*/
cbc9bed8 1022{
93adae19
MV
1023 if (libudev_handle != NULL)
1024 dlclose(libudev_handle);
cbc9bed8 1025}
3e2d7cce 1026 /*}}}*/