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