]>
git.saurik.com Git - apt.git/blob - apt-pkg/cdrom.cc
5 #pragma implementation "apt-pkg/cdrom.h"
7 #include<apt-pkg/init.h>
8 #include<apt-pkg/error.h>
9 #include<apt-pkg/cdromutl.h>
10 #include<apt-pkg/strutl.h>
11 #include<apt-pkg/cdrom.h>
24 #include "indexcopy.h"
28 // FindPackages - Find the package files on the CDROM /*{{{*/
29 // ---------------------------------------------------------------------
30 /* We look over the cdrom for package files. This is a recursive
31 search that short circuits when it his a package file in the dir.
32 This speeds it up greatly as the majority of the size is in the
34 bool pkgCdrom::FindPackages(string CD
,vector
<string
> &List
,
35 vector
<string
> &SList
, vector
<string
> &SigList
,
36 string
&InfoDir
, pkgCdromStatus
*log
,
39 static ino_t Inodes
[9];
41 // if we have a look we "pulse" now
48 if (CD
[CD
.length()-1] != '/')
51 if (chdir(CD
.c_str()) != 0)
52 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
54 // Look for a .disk subdirectory
56 if (stat(".disk",&Buf
) == 0)
58 if (InfoDir
.empty() == true)
59 InfoDir
= CD
+ ".disk/";
62 // Don't look into directories that have been marked to ingore.
63 if (stat(".aptignr",&Buf
) == 0)
67 /* Check _first_ for a signature file as apt-cdrom assumes that all files
68 under a Packages/Source file are in control of that file and stops
71 if (stat("Release.gpg",&Buf
) == 0)
73 SigList
.push_back(CD
);
75 /* Aha! We found some package files. We assume that everything under
76 this dir is controlled by those package files so we don't look down
78 if (stat("Packages",&Buf
) == 0 || stat("Packages.gz",&Buf
) == 0)
82 // Continue down if thorough is given
83 if (_config
->FindB("APT::CDROM::Thorough",false) == false)
86 if (stat("Sources.gz",&Buf
) == 0 || stat("Sources",&Buf
) == 0)
90 // Continue down if thorough is given
91 if (_config
->FindB("APT::CDROM::Thorough",false) == false)
95 DIR *D
= opendir(".");
97 return _error
->Errno("opendir","Unable to read %s",CD
.c_str());
99 // Run over the directory
100 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
103 if (strcmp(Dir
->d_name
,".") == 0 ||
104 strcmp(Dir
->d_name
,"..") == 0 ||
105 //strcmp(Dir->d_name,"source") == 0 ||
106 strcmp(Dir
->d_name
,".disk") == 0 ||
107 strcmp(Dir
->d_name
,"experimental") == 0 ||
108 strcmp(Dir
->d_name
,"binary-all") == 0 ||
109 strcmp(Dir
->d_name
,"debian-installer") == 0)
112 // See if the name is a sub directory
114 if (stat(Dir
->d_name
,&Buf
) != 0)
117 if (S_ISDIR(Buf
.st_mode
) == 0)
121 for (I
= 0; I
!= Depth
; I
++)
122 if (Inodes
[I
] == Buf
.st_ino
)
127 // Store the inodes weve seen
128 Inodes
[Depth
] = Buf
.st_ino
;
131 if (FindPackages(CD
+ Dir
->d_name
,List
,SList
,SigList
,InfoDir
,log
,Depth
+1) == false)
134 if (chdir(CD
.c_str()) != 0)
135 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
140 return !_error
->PendingError();
143 // Score - We compute a 'score' for a path /*{{{*/
144 // ---------------------------------------------------------------------
145 /* Paths are scored based on how close they come to what I consider
146 normal. That is ones that have 'dist' 'stable' 'testing' will score
147 higher than ones without. */
148 int pkgCdrom::Score(string Path
)
151 if (Path
.find("stable/") != string::npos
)
153 if (Path
.find("/binary-") != string::npos
)
155 if (Path
.find("testing/") != string::npos
)
157 if (Path
.find("unstable/") != string::npos
)
159 if (Path
.find("/dists/") != string::npos
)
161 if (Path
.find("/main/") != string::npos
)
163 if (Path
.find("/contrib/") != string::npos
)
165 if (Path
.find("/non-free/") != string::npos
)
167 if (Path
.find("/non-US/") != string::npos
)
169 if (Path
.find("/source/") != string::npos
)
171 if (Path
.find("/debian/") != string::npos
)
174 // check for symlinks in the patch leading to the actual file
175 // a symlink gets a big penalty
177 string statPath
= flNotFile(Path
);
178 string cdromPath
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
179 while(statPath
!= cdromPath
&& statPath
!= "./") {
180 statPath
.resize(statPath
.size()-1); // remove the trailing '/'
181 if (lstat(statPath
.c_str(),&Buf
) == 0) {
182 if(S_ISLNK(Buf
.st_mode
)) {
187 statPath
= flNotFile(statPath
); // descent
194 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
195 // ---------------------------------------------------------------------
196 /* Here we drop everything that is not this machines arch */
197 bool pkgCdrom::DropBinaryArch(vector
<string
> &List
)
200 snprintf(S
,sizeof(S
),"/binary-%s/",
201 _config
->Find("Apt::Architecture").c_str());
203 for (unsigned int I
= 0; I
< List
.size(); I
++)
205 const char *Str
= List
[I
].c_str();
208 if ((Res
= strstr(Str
,"/binary-")) == 0)
212 if (strlen(Res
) < strlen(S
))
214 List
.erase(List
.begin() + I
);
219 // See if it is our arch
220 if (stringcmp(Res
,Res
+ strlen(S
),S
) == 0)
224 List
.erase(List
.begin() + I
);
232 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
233 // ---------------------------------------------------------------------
234 /* Here we go and stat every file that we found and strip dup inodes. */
235 bool pkgCdrom::DropRepeats(vector
<string
> &List
,const char *Name
)
237 // Get a list of all the inodes
238 ino_t
*Inodes
= new ino_t
[List
.size()];
239 for (unsigned int I
= 0; I
!= List
.size(); I
++)
242 if (stat((List
[I
] + Name
).c_str(),&Buf
) != 0 &&
243 stat((List
[I
] + Name
+ ".gz").c_str(),&Buf
) != 0)
244 _error
->Errno("stat","Failed to stat %s%s",List
[I
].c_str(),
246 Inodes
[I
] = Buf
.st_ino
;
249 if (_error
->PendingError() == true)
253 for (unsigned int I
= 0; I
!= List
.size(); I
++)
255 for (unsigned int J
= I
+1; J
< List
.size(); J
++)
258 if (Inodes
[J
] != Inodes
[I
])
261 // We score the two paths.. and erase one
262 int ScoreA
= Score(List
[I
]);
263 int ScoreB
= Score(List
[J
]);
274 // Wipe erased entries
275 for (unsigned int I
= 0; I
< List
.size();)
277 if (List
[I
].empty() == false)
280 List
.erase(List
.begin()+I
);
287 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
288 // ---------------------------------------------------------------------
289 /* This takes the list of source list expressed entires and collects
290 similar ones to form a single entry for each dist */
291 void pkgCdrom::ReduceSourcelist(string CD
,vector
<string
> &List
)
293 sort(List
.begin(),List
.end());
295 // Collect similar entries
296 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
299 string::size_type Space
= (*I
).find(' ');
300 if (Space
== string::npos
)
302 string::size_type SSpace
= (*I
).find(' ',Space
+ 1);
303 if (SSpace
== string::npos
)
306 string Word1
= string(*I
,Space
,SSpace
-Space
);
307 string Prefix
= string(*I
,0,Space
);
308 for (vector
<string
>::iterator J
= List
.begin(); J
!= I
; J
++)
311 string::size_type Space2
= (*J
).find(' ');
312 if (Space2
== string::npos
)
314 string::size_type SSpace2
= (*J
).find(' ',Space2
+ 1);
315 if (SSpace2
== string::npos
)
318 if (string(*J
,0,Space2
) != Prefix
)
320 if (string(*J
,Space2
,SSpace2
-Space2
) != Word1
)
323 *J
+= string(*I
,SSpace
);
328 // Wipe erased entries
329 for (unsigned int I
= 0; I
< List
.size();)
331 if (List
[I
].empty() == false)
334 List
.erase(List
.begin()+I
);
338 // WriteDatabase - Write the CDROM Database file /*{{{*/
339 // ---------------------------------------------------------------------
340 /* We rewrite the configuration class associated with the cdrom database. */
341 bool pkgCdrom::WriteDatabase(Configuration
&Cnf
)
343 string DFile
= _config
->FindFile("Dir::State::cdroms");
344 string NewFile
= DFile
+ ".new";
346 unlink(NewFile
.c_str());
347 ofstream
Out(NewFile
.c_str());
349 return _error
->Errno("ofstream::ofstream",
350 "Failed to open %s.new",DFile
.c_str());
352 /* Write out all of the configuration directives by walking the
353 configuration tree */
354 const Configuration::Item
*Top
= Cnf
.Tree(0);
357 // Print the config entry
358 if (Top
->Value
.empty() == false)
359 Out
<< Top
->FullTag() + " \"" << Top
->Value
<< "\";" << endl
;
367 while (Top
!= 0 && Top
->Next
== 0)
375 rename(DFile
.c_str(),string(DFile
+ '~').c_str());
376 if (rename(NewFile
.c_str(),DFile
.c_str()) != 0)
377 return _error
->Errno("rename","Failed to rename %s.new to %s",
378 DFile
.c_str(),DFile
.c_str());
383 // WriteSourceList - Write an updated sourcelist /*{{{*/
384 // ---------------------------------------------------------------------
385 /* This reads the old source list and copies it into the new one. It
386 appends the new CDROM entires just after the first block of comments.
387 This places them first in the file. It also removes any old entries
388 that were the same. */
389 bool pkgCdrom::WriteSourceList(string Name
,vector
<string
> &List
,bool Source
)
391 if (List
.size() == 0)
394 string File
= _config
->FindFile("Dir::Etc::sourcelist");
396 // Open the stream for reading
397 ifstream
F((FileExists(File
)?File
.c_str():"/dev/null"),
400 return _error
->Errno("ifstream::ifstream","Opening %s",File
.c_str());
402 string NewFile
= File
+ ".new";
403 unlink(NewFile
.c_str());
404 ofstream
Out(NewFile
.c_str());
406 return _error
->Errno("ofstream::ofstream",
407 "Failed to open %s.new",File
.c_str());
409 // Create a short uri without the path
410 string ShortURI
= "cdrom:[" + Name
+ "]/";
411 string ShortURI2
= "cdrom:" + Name
+ "/"; // For Compatibility
422 while (F
.eof() == false)
424 F
.getline(Buffer
,sizeof(Buffer
));
426 if (F
.fail() && !F
.eof())
427 return _error
->Error(_("Line %u too long in source list %s."),
428 CurLine
,File
.c_str());
429 _strtabexpand(Buffer
,sizeof(Buffer
));
433 if (Buffer
[0] == '#' || Buffer
[0] == 0)
435 Out
<< Buffer
<< endl
;
441 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
443 string::size_type Space
= (*I
).find(' ');
444 if (Space
== string::npos
)
445 return _error
->Error("Internal error");
446 Out
<< Type
<< " cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
447 " " << string(*I
,Space
+1) << endl
;
455 const char *C
= Buffer
;
456 if (ParseQuoteWord(C
,cType
) == false ||
457 ParseQuoteWord(C
,URI
) == false)
459 Out
<< Buffer
<< endl
;
463 // Emit lines like this one
464 if (cType
!= Type
|| (string(URI
,0,ShortURI
.length()) != ShortURI
&&
465 string(URI
,0,ShortURI
.length()) != ShortURI2
))
467 Out
<< Buffer
<< endl
;
472 // Just in case the file was empty
475 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
477 string::size_type Space
= (*I
).find(' ');
478 if (Space
== string::npos
)
479 return _error
->Error("Internal error");
481 Out
<< "deb cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
482 " " << string(*I
,Space
+1) << endl
;
488 rename(File
.c_str(),string(File
+ '~').c_str());
489 if (rename(NewFile
.c_str(),File
.c_str()) != 0)
490 return _error
->Errno("rename","Failed to rename %s.new to %s",
491 File
.c_str(),File
.c_str());
497 bool pkgCdrom::Ident(string
&ident
, pkgCdromStatus
*log
)
502 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
504 CDROM
= SafeGetCWD() + '/' + CDROM
;
508 ioprintf(msg
, _("Using CD-ROM mount point %s\nMounting CD-ROM\n"),
510 log
->Update(msg
.str());
512 if (MountCdrom(CDROM
) == false)
513 return _error
->Error("Failed to mount the cdrom.");
515 // Hash the CD to get an ID
517 log
->Update(_("Identifying.. "));
520 if (IdentCdrom(CDROM
,ident
) == false)
527 ioprintf(msg
, "[%s]\n",ident
.c_str());
528 log
->Update(msg
.str());
532 Configuration Database
;
533 string DFile
= _config
->FindFile("Dir::State::cdroms");
534 if (FileExists(DFile
) == true)
536 if (ReadConfigFile(Database
,DFile
) == false)
537 return _error
->Error("Unable to read the cdrom database %s",
542 ioprintf(msg
, _("Stored label: %s \n"),
543 Database
.Find("CD::"+ident
).c_str());
544 log
->Update(msg
.str());
550 bool pkgCdrom::Add(pkgCdromStatus
*log
)
555 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
557 CDROM
= SafeGetCWD() + '/' + CDROM
;
560 log
->SetTotal(STEP_LAST
);
562 ioprintf(msg
, _("Using CD-ROM mount point %s\n"), CDROM
.c_str());
563 log
->Update(msg
.str(), STEP_PREPARE
);
567 Configuration Database
;
568 string DFile
= _config
->FindFile("Dir::State::cdroms");
569 if (FileExists(DFile
) == true)
571 if (ReadConfigFile(Database
,DFile
) == false)
572 return _error
->Error("Unable to read the cdrom database %s",
576 // Unmount the CD and get the user to put in the one they want
577 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
580 log
->Update(_("Unmounting CD-ROM\n"), STEP_UNMOUNT
);
584 log
->Update(_("Waiting for disc...\n"), STEP_WAIT
);
585 if(!log
->ChangeCdrom()) {
591 // Mount the new CDROM
592 log
->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT
);
593 if (MountCdrom(CDROM
) == false)
594 return _error
->Error("Failed to mount the cdrom.");
597 // Hash the CD to get an ID
599 log
->Update(_("Identifying.. "), STEP_IDENT
);
601 if (IdentCdrom(CDROM
,ID
) == false)
607 log
->Update("["+ID
+"]\n");
610 log
->Update(_("Scanning disc for index files..\n"),STEP_SCAN
);
612 // Get the CD structure
614 vector
<string
> SourceList
;
615 vector
<string
> SigList
;
616 string StartDir
= SafeGetCWD();
618 if (FindPackages(CDROM
,List
,SourceList
, SigList
,InfoDir
,log
) == false)
624 chdir(StartDir
.c_str());
626 if (_config
->FindB("Debug::aptcdrom",false) == true)
628 cout
<< "I found (binary):" << endl
;
629 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
631 cout
<< "I found (source):" << endl
;
632 for (vector
<string
>::iterator I
= SourceList
.begin(); I
!= SourceList
.end(); I
++)
634 cout
<< "I found (Signatures):" << endl
;
635 for (vector
<string
>::iterator I
= SigList
.begin(); I
!= SigList
.end(); I
++)
639 //log->Update(_("Cleaning package lists..."), STEP_CLEAN);
642 DropBinaryArch(List
);
643 DropRepeats(List
,"Packages");
644 DropRepeats(SourceList
,"Sources");
645 DropRepeats(SigList
,"Release.gpg");
648 ioprintf(msg
, _("Found %i package indexes, %i source indexes and "
650 List
.size(), SourceList
.size(), SigList
.size());
651 log
->Update(msg
.str(), STEP_SCAN
);
654 if (List
.size() == 0 && SourceList
.size() == 0)
656 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
658 return _error
->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
661 // Check if the CD is in the database
663 if (Database
.Exists("CD::" + ID
) == false ||
664 _config
->FindB("APT::CDROM::Rename",false) == true)
666 // Try to use the CDs label if at all possible
667 if (InfoDir
.empty() == false &&
668 FileExists(InfoDir
+ "/info") == true)
670 ifstream
F(string(InfoDir
+ "/info").c_str());
674 if (Name
.empty() == false)
676 // Escape special characters
677 string::iterator J
= Name
.begin();
678 for (; J
!= Name
.end(); J
++)
679 if (*J
== '"' || *J
== ']' || *J
== '[')
684 ioprintf(msg
, _("Found label '%s'\n"), Name
.c_str());
685 log
->Update(msg
.str());
687 Database
.Set("CD::" + ID
+ "::Label",Name
);
691 if (_config
->FindB("APT::CDROM::Rename",false) == true ||
692 Name
.empty() == true)
696 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
698 return _error
->Error("No disc name found and no way to ask for it");
702 if(!log
->AskCdromName(Name
)) {
706 cout
<< "Name: '" << Name
<< "'" << endl
;
708 if (Name
.empty() == false &&
709 Name
.find('"') == string::npos
&&
710 Name
.find('[') == string::npos
&&
711 Name
.find(']') == string::npos
)
713 log
->Update(_("That is not a valid name, try again.\n"));
718 Name
= Database
.Find("CD::" + ID
);
720 // Escape special characters
721 string::iterator J
= Name
.begin();
722 for (; J
!= Name
.end(); J
++)
723 if (*J
== '"' || *J
== ']' || *J
== '[')
726 Database
.Set("CD::" + ID
,Name
);
729 ioprintf(msg
, _("This disc is called: \n'%s'\n"), Name
.c_str());
730 log
->Update(msg
.str());
733 log
->Update(_("Copying package lists..."), STEP_COPY
);
734 // take care of the signatures and copy them if they are ok
735 // (we do this before PackageCopy as it modifies "List" and "SourceList")
736 SigVerify SignVerify
;
737 SignVerify
.CopyAndVerify(CDROM
, Name
, SigList
, List
, SourceList
);
739 // Copy the package files to the state directory
742 if (Copy
.CopyPackages(CDROM
,Name
,List
, log
) == false ||
743 SrcCopy
.CopyPackages(CDROM
,Name
,SourceList
, log
) == false)
746 // reduce the List so that it takes less space in sources.list
747 ReduceSourcelist(CDROM
,List
);
748 ReduceSourcelist(CDROM
,SourceList
);
750 // Write the database and sourcelist
751 if (_config
->FindB("APT::cdrom::NoAct",false) == false)
753 if (WriteDatabase(Database
) == false)
757 log
->Update(_("Writing new source list\n"), STEP_WRITE
);
759 if (WriteSourceList(Name
,List
,false) == false ||
760 WriteSourceList(Name
,SourceList
,true) == false)
764 // Print the sourcelist entries
766 log
->Update(_("Source list entries for this disc are:\n"));
768 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
770 string::size_type Space
= (*I
).find(' ');
771 if (Space
== string::npos
)
773 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
775 return _error
->Error("Internal error");
780 msg
<< "deb cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
781 " " << string(*I
,Space
+1) << endl
;
782 log
->Update(msg
.str());
786 for (vector
<string
>::iterator I
= SourceList
.begin(); I
!= SourceList
.end(); I
++)
788 string::size_type Space
= (*I
).find(' ');
789 if (Space
== string::npos
)
791 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
793 return _error
->Error("Internal error");
798 msg
<< "deb-src cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
799 " " << string(*I
,Space
+1) << endl
;
800 log
->Update(msg
.str());
806 // Unmount and finish
807 if (_config
->FindB("APT::CDROM::NoMount",false) == false) {
808 log
->Update(_("Unmounting CD-ROM...\n"), STEP_LAST
);