]> git.saurik.com Git - apt.git/blame - apt-pkg/cdrom.cc
implement PDiff patching for compressed files
[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
711078ae 64 if (DirectoryExists(".disk") == true)
a75c6a6e
MZ
65 {
66 if (InfoDir.empty() == true)
67 InfoDir = CD + ".disk/";
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 ||
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
22f8568d 185 if (FindPackages(CD + Dir->d_name,List,SList,SigList,TransList,InfoDir,log,Depth+1) == false)
a75c6a6e
MZ
186 break;
187
188 if (chdir(CD.c_str()) != 0)
6070a346
DK
189 {
190 _error->Errno("chdir","Unable to change to %s", CD.c_str());
191 closedir(D);
192 return false;
193 }
a75c6a6e
MZ
194 };
195
196 closedir(D);
197
198 return !_error->PendingError();
199}
92fcbfc1 200 /*}}}*/
a75c6a6e
MZ
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. */
206int 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;
872b3d39
MV
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);
710aba4a 236 string cdromPath = _config->FindDir("Acquire::cdrom::mount");
872b3d39
MV
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
a75c6a6e
MZ
248 return Res;
249}
a75c6a6e
MZ
250 /*}}}*/
251// DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
252// ---------------------------------------------------------------------
253/* Here we drop everything that is not this machines arch */
254bool pkgCdrom::DropBinaryArch(vector<string> &List)
255{
5dd4c8b8 256
a75c6a6e
MZ
257 for (unsigned int I = 0; I < List.size(); I++)
258 {
259 const char *Str = List[I].c_str();
5dd4c8b8
DK
260 const char *Start, *End;
261 if ((Start = strstr(Str,"/binary-")) == 0)
a75c6a6e
MZ
262 continue;
263
5dd4c8b8
DK
264 // Between Start and End is the architecture
265 Start += 8;
266 if ((End = strstr(Start,"/")) != 0 && Start != End &&
73dfa041 267 APT::Configuration::checkArchitecture(string(Start, End)) == true)
5dd4c8b8
DK
268 continue; // okay, architecture is accepted
269
270 // not accepted -> Erase it
a75c6a6e 271 List.erase(List.begin() + I);
5dd4c8b8 272 --I; // the next entry is at the same index after the erase
a75c6a6e
MZ
273 }
274
c45233ea
DK
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 */
281bool 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
a75c6a6e
MZ
298 return true;
299}
92fcbfc1 300 /*}}}*/
a75c6a6e
MZ
301// DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
302// ---------------------------------------------------------------------
303/* Here we go and stat every file that we found and strip dup inodes. */
304bool pkgCdrom::DropRepeats(vector<string> &List,const char *Name)
305{
2c405a44 306 bool couldFindAllFiles = true;
a75c6a6e
MZ
307 // Get a list of all the inodes
308 ino_t *Inodes = new ino_t[List.size()];
78c9276d 309 for (unsigned int I = 0; I != List.size(); ++I)
a75c6a6e
MZ
310 {
311 struct stat Buf;
78c9276d
DK
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)
82c8f08e 317 {
78c9276d 318 std::string filename = std::string(List[I]).append(Name).append(c->Extension);
82c8f08e 319 if (stat(filename.c_str(), &Buf) != 0)
78c9276d
DK
320 continue;
321 Inodes[I] = Buf.st_ino;
322 found = true;
323 break;
82c8f08e 324 }
78c9276d
DK
325
326 if (found == false)
2c405a44
DK
327 {
328 _error->Errno("stat","Failed to stat %s%s",List[I].c_str(), Name);
329 couldFindAllFiles = false;
330 Inodes[I] = 0;
331 }
a75c6a6e 332 }
78c9276d 333
a75c6a6e
MZ
334 // Look for dups
335 for (unsigned int I = 0; I != List.size(); I++)
336 {
2c405a44
DK
337 if (Inodes[I] == 0)
338 continue;
a75c6a6e
MZ
339 for (unsigned int J = I+1; J < List.size(); J++)
340 {
341 // No match
2c405a44 342 if (Inodes[J] == 0 || Inodes[J] != Inodes[I])
a75c6a6e
MZ
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 }
3a4477a4
DK
357 delete[] Inodes;
358
a75c6a6e
MZ
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
2c405a44 368 return couldFindAllFiles;
a75c6a6e
MZ
369}
370 /*}}}*/
a75c6a6e
MZ
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 */
65512241 375void pkgCdrom::ReduceSourcelist(string /*CD*/,vector<string> &List)
a75c6a6e
MZ
376{
377 sort(List.begin(),List.end());
378
379 // Collect similar entries
f7f0d6c7 380 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
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);
8628c2f7 392 string Component = string(*I,SSpace);
f7f0d6c7 393 for (vector<string>::iterator J = List.begin(); J != I; ++J)
a75c6a6e
MZ
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;
8628c2f7
DK
407
408 string Component2 = string(*J, SSpace2) + " ";
409 if (Component2.find(Component + " ") == std::string::npos)
410 *J += Component;
411 I->clear();
a75c6a6e
MZ
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. */
428bool 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 */
ab59c1ca 441 Cnf.Dump(Out, NULL, "%f \"%v\";\n", false);
a75c6a6e
MZ
442
443 Out.close();
849e64ac 444
c1cd1ac2 445 if (FileExists(DFile) == true)
e788a834 446 rename(DFile.c_str(), (DFile + '~').c_str());
a75c6a6e
MZ
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. */
460bool pkgCdrom::WriteSourceList(string Name,vector<string> &List,bool Source)
461{
f7f0d6c7 462 if (List.empty() == true)
a75c6a6e
MZ
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 );
2b4cead3 470 if (F.fail() == true)
a75c6a6e
MZ
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
2b4cead3 481 string ShortURI = "cdrom:[" + Name + "]/";
a75c6a6e
MZ
482 string ShortURI2 = "cdrom:" + Name + "/"; // For Compatibility
483
484 string Type;
485 if (Source == true)
486 Type = "deb-src";
487 else
488 Type = "deb";
2b4cead3 489
a75c6a6e
MZ
490 char Buffer[300];
491 int CurLine = 0;
492 bool First = true;
493 while (F.eof() == false)
2b4cead3 494 {
a75c6a6e
MZ
495 F.getline(Buffer,sizeof(Buffer));
496 CurLine++;
13e8426f
MV
497 if (F.fail() && !F.eof())
498 return _error->Error(_("Line %u too long in source list %s."),
499 CurLine,File.c_str());
a75c6a6e
MZ
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 {
f7f0d6c7 512 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
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 {
f7f0d6c7 546 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
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
e788a834 559 rename(File.c_str(), (File + '~').c_str());
a75c6a6e
MZ
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}
92fcbfc1 566 /*}}}*/
7187074b
JO
567bool 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 /*}}}*/
b374004b 576bool pkgCdrom::MountAndIdentCDROM(Configuration &Database, std::string &CDROM, std::string &ident, pkgCdromStatus * const log, bool const interactive)/*{{{*/
a75c6a6e 577{
a75c6a6e 578 // Startup
ce55512d 579 CDROM = _config->FindDir("Acquire::cdrom::mount");
a75c6a6e
MZ
580 if (CDROM[0] == '.')
581 CDROM= SafeGetCWD() + '/' + CDROM;
582
6070a346
DK
583 if (log != NULL)
584 {
ce55512d
DK
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);
a75c6a6e 589 }
62dcbf84
JO
590
591 // Unmount the CD and get the user to put in the one they want
ce55512d 592 if (_config->FindB("APT::CDROM::NoMount", false) == false)
62dcbf84 593 {
b374004b 594 if (interactive == true)
62dcbf84 595 {
7187074b 596 UnmountCDROM(CDROM, log);
b374004b
DK
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 }
62dcbf84
JO
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 }
a75c6a6e 615
7187074b
JO
616 if (IsMounted(CDROM) == false)
617 return _error->Error("Failed to mount the cdrom.");
618
a75c6a6e 619 // Hash the CD to get an ID
6070a346 620 if (log != NULL)
1166ea79 621 log->Update(_("Identifying... "), STEP_IDENT);
a75c6a6e
MZ
622
623 if (IdentCdrom(CDROM,ident) == false)
624 {
625 ident = "";
ce55512d
DK
626 if (log != NULL)
627 log->Update("\n");
7187074b 628 UnmountCDROM(CDROM, NULL);
a75c6a6e
MZ
629 return false;
630 }
631
6070a346
DK
632 if (log != NULL)
633 {
ce55512d
DK
634 string msg;
635 strprintf(msg, "[%s]\n", ident.c_str());
636 log->Update(msg);
6070a346 637 }
a75c6a6e
MZ
638
639 // Read the database
a75c6a6e
MZ
640 string DFile = _config->FindFile("Dir::State::cdroms");
641 if (FileExists(DFile) == true)
642 {
643 if (ReadConfigFile(Database,DFile) == false)
7187074b
JO
644 {
645 UnmountCDROM(CDROM, NULL);
a75c6a6e
MZ
646 return _error->Error("Unable to read the cdrom database %s",
647 DFile.c_str());
7187074b 648 }
a75c6a6e 649 }
ce55512d
DK
650 return true;
651}
652 /*}}}*/
653bool pkgCdrom::Ident(string &ident, pkgCdromStatus *log) /*{{{*/
654{
655 Configuration Database;
656 std::string CDROM;
b374004b 657 if (MountAndIdentCDROM(Database, CDROM, ident, log, false) == false)
ce55512d
DK
658 return false;
659
6070a346
DK
660 if (log != NULL)
661 {
ce55512d
DK
662 string msg;
663 strprintf(msg, _("Stored label: %s\n"),
664 Database.Find("CD::"+ident).c_str());
665 log->Update(msg);
a75c6a6e 666 }
1fcbe14d
OS
667
668 // Unmount and finish
7187074b 669 UnmountCDROM(CDROM, log);
a75c6a6e
MZ
670 return true;
671}
92fcbfc1
DK
672 /*}}}*/
673bool pkgCdrom::Add(pkgCdromStatus *log) /*{{{*/
a75c6a6e 674{
a75c6a6e 675 Configuration Database;
ce55512d 676 std::string ID, CDROM;
b374004b 677 if (MountAndIdentCDROM(Database, CDROM, ID, log, true) == false)
a75c6a6e 678 return false;
ce55512d 679
6070a346 680 if(log != NULL)
1166ea79 681 log->Update(_("Scanning disc for index files...\n"),STEP_SCAN);
6070a346 682
a75c6a6e
MZ
683 // Get the CD structure
684 vector<string> List;
685 vector<string> SourceList;
686 vector<string> SigList;
22f8568d 687 vector<string> TransList;
a75c6a6e
MZ
688 string StartDir = SafeGetCWD();
689 string InfoDir;
22f8568d 690 if (FindPackages(CDROM,List,SourceList, SigList,TransList,InfoDir,log) == false)
a75c6a6e 691 {
6070a346
DK
692 if (log != NULL)
693 log->Update("\n");
7187074b 694 UnmountCDROM(CDROM, NULL);
a75c6a6e
MZ
695 return false;
696 }
697
31bda500 698 if (chdir(StartDir.c_str()) != 0)
7187074b
JO
699 {
700 UnmountCDROM(CDROM, NULL);
31bda500 701 return _error->Errno("chdir","Unable to change to %s", StartDir.c_str());
7187074b 702 }
a75c6a6e
MZ
703
704 if (_config->FindB("Debug::aptcdrom",false) == true)
705 {
706 cout << "I found (binary):" << endl;
f7f0d6c7 707 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
708 cout << *I << endl;
709 cout << "I found (source):" << endl;
f7f0d6c7 710 for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); ++I)
a75c6a6e
MZ
711 cout << *I << endl;
712 cout << "I found (Signatures):" << endl;
f7f0d6c7 713 for (vector<string>::iterator I = SigList.begin(); I != SigList.end(); ++I)
a75c6a6e
MZ
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");
2c405a44
DK
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();
a75c6a6e 727 DropRepeats(SigList,"Release.gpg");
212080b8 728 DropRepeats(SigList,"InRelease");
2c405a44 729 _error->RevertToStack();
22f8568d 730 DropRepeats(TransList,"");
c45233ea
DK
731 if (_config->FindB("APT::CDROM::DropTranslation", true) == true)
732 DropTranslation(TransList);
6070a346 733 if(log != NULL) {
ce55512d
DK
734 string msg;
735 strprintf(msg, _("Found %zu package indexes, %zu source indexes, "
a376d6fd 736 "%zu translation indexes and %zu signatures\n"),
22f8568d
MV
737 List.size(), SourceList.size(), TransList.size(),
738 SigList.size());
ce55512d 739 log->Update(msg, STEP_SCAN);
a75c6a6e
MZ
740 }
741
f7f0d6c7 742 if (List.empty() == true && SourceList.empty() == true)
cdadf54b 743 {
7187074b 744 UnmountCDROM(CDROM, NULL);
d720a7d4 745 return _error->Error(_("Unable to locate any package files, perhaps this is not a Debian Disc or the wrong architecture?"));
cdadf54b 746 }
a75c6a6e
MZ
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 {
e788a834 757 ifstream F((InfoDir + "/info").c_str());
2b4cead3 758 if (F.good() == true)
a75c6a6e
MZ
759 getline(F,Name);
760
761 if (Name.empty() == false)
762 {
763 // Escape special characters
764 string::iterator J = Name.begin();
f7f0d6c7 765 for (; J != Name.end(); ++J)
a75c6a6e
MZ
766 if (*J == '"' || *J == ']' || *J == '[')
767 *J = '_';
768
6070a346
DK
769 if(log != NULL)
770 {
ce55512d
DK
771 string msg;
772 strprintf(msg, _("Found label '%s'\n"), Name.c_str());
773 log->Update(msg);
a75c6a6e
MZ
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 {
6070a346 782 if(log == NULL)
cdadf54b 783 {
7187074b 784 UnmountCDROM(CDROM, NULL);
a75c6a6e 785 return _error->Error("No disc name found and no way to ask for it");
cdadf54b 786 }
a75c6a6e
MZ
787
788 while(true) {
789 if(!log->AskCdromName(Name)) {
790 // user canceld
7187074b 791 UnmountCDROM(CDROM, NULL);
a75c6a6e
MZ
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();
f7f0d6c7 810 for (; J != Name.end(); ++J)
a75c6a6e
MZ
811 if (*J == '"' || *J == ']' || *J == '[')
812 *J = '_';
813
814 Database.Set("CD::" + ID,Name);
6070a346
DK
815 if(log != NULL)
816 {
ce55512d
DK
817 string msg;
818 strprintf(msg, _("This disc is called: \n'%s'\n"), Name.c_str());
819 log->Update(msg);
6070a346 820 log->Update(_("Copying package lists..."), STEP_COPY);
a75c6a6e 821 }
b2ea1a47
DK
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/";
8fe964f1
DK
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)
7187074b
JO
831 {
832 UnmountCDROM(CDROM, NULL);
b2ea1a47 833 return _error->Errno("cdrom", _("List directory %spartial is missing."), listDir.c_str());
7187074b 834 }
b2ea1a47 835
a75c6a6e
MZ
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;
22f8568d 844 TranslationsCopy TransCopy;
a75c6a6e 845 if (Copy.CopyPackages(CDROM,Name,List, log) == false ||
22f8568d
MV
846 SrcCopy.CopyPackages(CDROM,Name,SourceList, log) == false ||
847 TransCopy.CopyTranslations(CDROM,Name,TransList, log) == false)
7187074b
JO
848 {
849 UnmountCDROM(CDROM, NULL);
a75c6a6e 850 return false;
7187074b 851 }
a75c6a6e
MZ
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)
7187074b
JO
861 {
862 UnmountCDROM(CDROM, NULL);
a75c6a6e 863 return false;
7187074b
JO
864 }
865
6070a346 866 if(log != NULL)
a75c6a6e 867 log->Update(_("Writing new source list\n"), STEP_WRITE);
a75c6a6e
MZ
868 if (WriteSourceList(Name,List,false) == false ||
869 WriteSourceList(Name,SourceList,true) == false)
7187074b
JO
870 {
871 UnmountCDROM(CDROM, NULL);
a75c6a6e 872 return false;
7187074b 873 }
a75c6a6e
MZ
874 }
875
876 // Print the sourcelist entries
6070a346 877 if(log != NULL)
db0db9fe 878 log->Update(_("Source list entries for this disc are:\n"));
a75c6a6e 879
f7f0d6c7 880 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
a75c6a6e
MZ
881 {
882 string::size_type Space = (*I).find(' ');
883 if (Space == string::npos)
cdadf54b 884 {
7187074b 885 UnmountCDROM(CDROM, NULL);
a75c6a6e 886 return _error->Error("Internal error");
cdadf54b 887 }
a75c6a6e 888
6070a346
DK
889 if(log != NULL)
890 {
ce55512d 891 stringstream msg;
a75c6a6e
MZ
892 msg << "deb cdrom:[" << Name << "]/" << string(*I,0,Space) <<
893 " " << string(*I,Space+1) << endl;
894 log->Update(msg.str());
895 }
896 }
897
f7f0d6c7 898 for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); ++I)
a75c6a6e
MZ
899 {
900 string::size_type Space = (*I).find(' ');
901 if (Space == string::npos)
cdadf54b 902 {
7187074b 903 UnmountCDROM(CDROM, NULL);
a75c6a6e 904 return _error->Error("Internal error");
cdadf54b 905 }
a75c6a6e 906
6070a346 907 if(log != NULL) {
ce55512d 908 stringstream msg;
a75c6a6e
MZ
909 msg << "deb-src cdrom:[" << Name << "]/" << string(*I,0,Space) <<
910 " " << string(*I,Space+1) << endl;
911 log->Update(msg.str());
912 }
913 }
914
a75c6a6e 915 // Unmount and finish
7187074b 916 UnmountCDROM(CDROM, log);
a75c6a6e
MZ
917 return true;
918}
92fcbfc1 919 /*}}}*/
25613a61 920pkgUdevCdromDevices::pkgUdevCdromDevices() /*{{{*/
6c55f07a 921: d(NULL), libudev_handle(NULL), udev_new(NULL), udev_enumerate_add_match_property(NULL),
25613a61
DK
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)
cbc9bed8 927{
cbc9bed8 928}
3e2d7cce 929 /*}}}*/
cbc9bed8 930
d84da499 931bool pkgUdevCdromDevices::Dlopen() /*{{{*/
cbc9bed8 932{
49cb36fc 933 // alread open
76fe5db7 934 if(libudev_handle != NULL)
49cb36fc
MV
935 return true;
936
cbc9bed8
MV
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");
b7bc31eb 946 udev_enumerate_add_match_sysattr = (int (*)(udev_enumerate*, const char*, const char*))dlsym(h, "udev_enumerate_add_match_sysattr");
cbc9bed8
MV
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}
3e2d7cce 959 /*}}}*/
d84da499
DK
960// convenience interface, this will just call ScanForRemovable /*{{{*/
961vector<CdromDevice> pkgUdevCdromDevices::Scan()
d3e8fbb3 962{
c9952021 963 bool CdromOnly = _config->FindB("APT::cdrom::CdromOnly", true);
d3e8fbb3
DK
964 return ScanForRemovable(CdromOnly);
965}
f4c4a24e 966 /*}}}*/
d84da499 967vector<CdromDevice> pkgUdevCdromDevices::ScanForRemovable(bool CdromOnly)/*{{{*/
cbc9bed8
MV
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);
f4c4a24e
MV
979 if (CdromOnly)
980 udev_enumerate_add_match_property(enumerate, "ID_CDROM", "1");
e344ad65 981 else {
f4c4a24e 982 udev_enumerate_add_match_sysattr(enumerate, "removable", "1");
e344ad65 983 }
cbc9bed8
MV
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);
02aa6f67
MV
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
ef381816
MV
1002 mountpath = FindMountPointForDevice(devnode);
1003
cbc9bed8
MV
1004 // fill in the struct
1005 cdrom.DeviceName = string(devnode);
02aa6f67 1006 if (mountpath != "") {
cbc9bed8 1007 cdrom.MountPath = mountpath;
e788a834 1008 string s = mountpath;
be5b5581 1009 cdrom.Mounted = IsMounted(s);
cbc9bed8
MV
1010 } else {
1011 cdrom.Mounted = false;
1012 cdrom.MountPath = "";
1013 }
1014 cdrom_devices.push_back(cdrom);
1015 }
1016 return cdrom_devices;
1017}
3e2d7cce 1018 /*}}}*/
cbc9bed8 1019
3e2d7cce 1020pkgUdevCdromDevices::~pkgUdevCdromDevices() /*{{{*/
cbc9bed8 1021{
93adae19
MV
1022 if (libudev_handle != NULL)
1023 dlclose(libudev_handle);
cbc9bed8 1024}
3e2d7cce 1025 /*}}}*/
c8a4ce6c 1026
6c55f07a 1027pkgCdromStatus::pkgCdromStatus() : d(NULL), totalSteps(0) {}
c8a4ce6c
DK
1028pkgCdromStatus::~pkgCdromStatus() {}
1029
6c55f07a 1030pkgCdrom::pkgCdrom() : d(NULL) {}
c8a4ce6c 1031pkgCdrom::~pkgCdrom() {}