]>
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>
23 #include "indexcopy.h"
27 // FindPackages - Find the package files on the CDROM /*{{{*/
28 // ---------------------------------------------------------------------
29 /* We look over the cdrom for package files. This is a recursive
30 search that short circuits when it his a package file in the dir.
31 This speeds it up greatly as the majority of the size is in the
33 bool pkgCdrom::FindPackages(string CD
,vector
<string
> &List
,
34 vector
<string
> &SList
, vector
<string
> &SigList
,
35 string
&InfoDir
, pkgCdromStatus
*log
,
38 static ino_t Inodes
[9];
40 // if we have a look we "pulse" now
47 if (CD
[CD
.length()-1] != '/')
50 if (chdir(CD
.c_str()) != 0)
51 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
53 // Look for a .disk subdirectory
55 if (stat(".disk",&Buf
) == 0)
57 if (InfoDir
.empty() == true)
58 InfoDir
= CD
+ ".disk/";
61 // Don't look into directories that have been marked to ingore.
62 if (stat(".aptignr",&Buf
) == 0)
66 /* Check _first_ for a signature file as apt-cdrom assumes that all files
67 under a Packages/Source file are in control of that file and stops
70 if (stat("Release.gpg",&Buf
) == 0)
72 SigList
.push_back(CD
);
74 /* Aha! We found some package files. We assume that everything under
75 this dir is controlled by those package files so we don't look down
77 if (stat("Packages",&Buf
) == 0 || stat("Packages.gz",&Buf
) == 0)
81 // Continue down if thorough is given
82 if (_config
->FindB("APT::CDROM::Thorough",false) == false)
85 if (stat("Sources.gz",&Buf
) == 0 || stat("Sources",&Buf
) == 0)
89 // Continue down if thorough is given
90 if (_config
->FindB("APT::CDROM::Thorough",false) == false)
94 DIR *D
= opendir(".");
96 return _error
->Errno("opendir","Unable to read %s",CD
.c_str());
98 // Run over the directory
99 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
102 if (strcmp(Dir
->d_name
,".") == 0 ||
103 strcmp(Dir
->d_name
,"..") == 0 ||
104 //strcmp(Dir->d_name,"source") == 0 ||
105 strcmp(Dir
->d_name
,".disk") == 0 ||
106 strcmp(Dir
->d_name
,"experimental") == 0 ||
107 strcmp(Dir
->d_name
,"binary-all") == 0 ||
108 strcmp(Dir
->d_name
,"debian-installer") == 0)
111 // See if the name is a sub directory
113 if (stat(Dir
->d_name
,&Buf
) != 0)
116 if (S_ISDIR(Buf
.st_mode
) == 0)
120 for (I
= 0; I
!= Depth
; I
++)
121 if (Inodes
[I
] == Buf
.st_ino
)
126 // Store the inodes weve seen
127 Inodes
[Depth
] = Buf
.st_ino
;
130 if (FindPackages(CD
+ Dir
->d_name
,List
,SList
,SigList
,InfoDir
,log
,Depth
+1) == false)
133 if (chdir(CD
.c_str()) != 0)
134 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
139 return !_error
->PendingError();
142 // Score - We compute a 'score' for a path /*{{{*/
143 // ---------------------------------------------------------------------
144 /* Paths are scored based on how close they come to what I consider
145 normal. That is ones that have 'dist' 'stable' 'testing' will score
146 higher than ones without. */
147 int pkgCdrom::Score(string Path
)
150 if (Path
.find("stable/") != string::npos
)
152 if (Path
.find("/binary-") != string::npos
)
154 if (Path
.find("testing/") != string::npos
)
156 if (Path
.find("unstable/") != string::npos
)
158 if (Path
.find("/dists/") != string::npos
)
160 if (Path
.find("/main/") != string::npos
)
162 if (Path
.find("/contrib/") != string::npos
)
164 if (Path
.find("/non-free/") != string::npos
)
166 if (Path
.find("/non-US/") != string::npos
)
168 if (Path
.find("/source/") != string::npos
)
170 if (Path
.find("/debian/") != string::npos
)
176 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
177 // ---------------------------------------------------------------------
178 /* Here we drop everything that is not this machines arch */
179 bool pkgCdrom::DropBinaryArch(vector
<string
> &List
)
182 snprintf(S
,sizeof(S
),"/binary-%s/",
183 _config
->Find("Apt::Architecture").c_str());
185 for (unsigned int I
= 0; I
< List
.size(); I
++)
187 const char *Str
= List
[I
].c_str();
190 if ((Res
= strstr(Str
,"/binary-")) == 0)
194 if (strlen(Res
) < strlen(S
))
196 List
.erase(List
.begin() + I
);
201 // See if it is our arch
202 if (stringcmp(Res
,Res
+ strlen(S
),S
) == 0)
206 List
.erase(List
.begin() + I
);
214 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
215 // ---------------------------------------------------------------------
216 /* Here we go and stat every file that we found and strip dup inodes. */
217 bool pkgCdrom::DropRepeats(vector
<string
> &List
,const char *Name
)
219 // Get a list of all the inodes
220 ino_t
*Inodes
= new ino_t
[List
.size()];
221 for (unsigned int I
= 0; I
!= List
.size(); I
++)
224 if (stat((List
[I
] + Name
).c_str(),&Buf
) != 0 &&
225 stat((List
[I
] + Name
+ ".gz").c_str(),&Buf
) != 0)
226 _error
->Errno("stat","Failed to stat %s%s",List
[I
].c_str(),
228 Inodes
[I
] = Buf
.st_ino
;
231 if (_error
->PendingError() == true)
235 for (unsigned int I
= 0; I
!= List
.size(); I
++)
237 for (unsigned int J
= I
+1; J
< List
.size(); J
++)
240 if (Inodes
[J
] != Inodes
[I
])
243 // We score the two paths.. and erase one
244 int ScoreA
= Score(List
[I
]);
245 int ScoreB
= Score(List
[J
]);
256 // Wipe erased entries
257 for (unsigned int I
= 0; I
< List
.size();)
259 if (List
[I
].empty() == false)
262 List
.erase(List
.begin()+I
);
269 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
270 // ---------------------------------------------------------------------
271 /* This takes the list of source list expressed entires and collects
272 similar ones to form a single entry for each dist */
273 void pkgCdrom::ReduceSourcelist(string CD
,vector
<string
> &List
)
275 sort(List
.begin(),List
.end());
277 // Collect similar entries
278 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
281 string::size_type Space
= (*I
).find(' ');
282 if (Space
== string::npos
)
284 string::size_type SSpace
= (*I
).find(' ',Space
+ 1);
285 if (SSpace
== string::npos
)
288 string Word1
= string(*I
,Space
,SSpace
-Space
);
289 string Prefix
= string(*I
,0,Space
);
290 for (vector
<string
>::iterator J
= List
.begin(); J
!= I
; J
++)
293 string::size_type Space2
= (*J
).find(' ');
294 if (Space2
== string::npos
)
296 string::size_type SSpace2
= (*J
).find(' ',Space2
+ 1);
297 if (SSpace2
== string::npos
)
300 if (string(*J
,0,Space2
) != Prefix
)
302 if (string(*J
,Space2
,SSpace2
-Space2
) != Word1
)
305 *J
+= string(*I
,SSpace
);
310 // Wipe erased entries
311 for (unsigned int I
= 0; I
< List
.size();)
313 if (List
[I
].empty() == false)
316 List
.erase(List
.begin()+I
);
320 // WriteDatabase - Write the CDROM Database file /*{{{*/
321 // ---------------------------------------------------------------------
322 /* We rewrite the configuration class associated with the cdrom database. */
323 bool pkgCdrom::WriteDatabase(Configuration
&Cnf
)
325 string DFile
= _config
->FindFile("Dir::State::cdroms");
326 string NewFile
= DFile
+ ".new";
328 unlink(NewFile
.c_str());
329 ofstream
Out(NewFile
.c_str());
331 return _error
->Errno("ofstream::ofstream",
332 "Failed to open %s.new",DFile
.c_str());
334 /* Write out all of the configuration directives by walking the
335 configuration tree */
336 const Configuration::Item
*Top
= Cnf
.Tree(0);
339 // Print the config entry
340 if (Top
->Value
.empty() == false)
341 Out
<< Top
->FullTag() + " \"" << Top
->Value
<< "\";" << endl
;
349 while (Top
!= 0 && Top
->Next
== 0)
357 rename(DFile
.c_str(),string(DFile
+ '~').c_str());
358 if (rename(NewFile
.c_str(),DFile
.c_str()) != 0)
359 return _error
->Errno("rename","Failed to rename %s.new to %s",
360 DFile
.c_str(),DFile
.c_str());
365 // WriteSourceList - Write an updated sourcelist /*{{{*/
366 // ---------------------------------------------------------------------
367 /* This reads the old source list and copies it into the new one. It
368 appends the new CDROM entires just after the first block of comments.
369 This places them first in the file. It also removes any old entries
370 that were the same. */
371 bool pkgCdrom::WriteSourceList(string Name
,vector
<string
> &List
,bool Source
)
373 if (List
.size() == 0)
376 string File
= _config
->FindFile("Dir::Etc::sourcelist");
378 // Open the stream for reading
379 ifstream
F((FileExists(File
)?File
.c_str():"/dev/null"),
382 return _error
->Errno("ifstream::ifstream","Opening %s",File
.c_str());
384 string NewFile
= File
+ ".new";
385 unlink(NewFile
.c_str());
386 ofstream
Out(NewFile
.c_str());
388 return _error
->Errno("ofstream::ofstream",
389 "Failed to open %s.new",File
.c_str());
391 // Create a short uri without the path
392 string ShortURI
= "cdrom:[" + Name
+ "]/";
393 string ShortURI2
= "cdrom:" + Name
+ "/"; // For Compatibility
404 while (F
.eof() == false)
406 F
.getline(Buffer
,sizeof(Buffer
));
408 _strtabexpand(Buffer
,sizeof(Buffer
));
412 if (Buffer
[0] == '#' || Buffer
[0] == 0)
414 Out
<< Buffer
<< endl
;
420 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
422 string::size_type Space
= (*I
).find(' ');
423 if (Space
== string::npos
)
424 return _error
->Error("Internal error");
425 Out
<< Type
<< " cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
426 " " << string(*I
,Space
+1) << endl
;
434 const char *C
= Buffer
;
435 if (ParseQuoteWord(C
,cType
) == false ||
436 ParseQuoteWord(C
,URI
) == false)
438 Out
<< Buffer
<< endl
;
442 // Emit lines like this one
443 if (cType
!= Type
|| (string(URI
,0,ShortURI
.length()) != ShortURI
&&
444 string(URI
,0,ShortURI
.length()) != ShortURI2
))
446 Out
<< Buffer
<< endl
;
451 // Just in case the file was empty
454 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
456 string::size_type Space
= (*I
).find(' ');
457 if (Space
== string::npos
)
458 return _error
->Error("Internal error");
460 Out
<< "deb cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
461 " " << string(*I
,Space
+1) << endl
;
467 rename(File
.c_str(),string(File
+ '~').c_str());
468 if (rename(NewFile
.c_str(),File
.c_str()) != 0)
469 return _error
->Errno("rename","Failed to rename %s.new to %s",
470 File
.c_str(),File
.c_str());
476 bool pkgCdrom::Ident(string
&ident
, pkgCdromStatus
*log
)
481 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
483 CDROM
= SafeGetCWD() + '/' + CDROM
;
487 ioprintf(msg
, _("Using CD-ROM mount point %s\nMounting CD-ROM\n"),
489 log
->Update(msg
.str());
491 if (MountCdrom(CDROM
) == false)
492 return _error
->Error("Failed to mount the cdrom.");
494 // Hash the CD to get an ID
496 log
->Update(_("Identifying.. "));
499 if (IdentCdrom(CDROM
,ident
) == false)
506 ioprintf(msg
, "[%s]\n",ident
.c_str());
507 log
->Update(msg
.str());
511 Configuration Database
;
512 string DFile
= _config
->FindFile("Dir::State::cdroms");
513 if (FileExists(DFile
) == true)
515 if (ReadConfigFile(Database
,DFile
) == false)
516 return _error
->Error("Unable to read the cdrom database %s",
521 ioprintf(msg
, _("Stored Label: %s \n"),
522 Database
.Find("CD::"+ident
).c_str());
523 log
->Update(msg
.str());
529 bool pkgCdrom::Add(pkgCdromStatus
*log
)
534 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
536 CDROM
= SafeGetCWD() + '/' + CDROM
;
539 log
->SetTotal(STEP_LAST
);
541 ioprintf(msg
, _("Using CD-ROM mount point %s\n"), CDROM
.c_str());
542 log
->Update(msg
.str(), STEP_PREPARE
);
546 Configuration Database
;
547 string DFile
= _config
->FindFile("Dir::State::cdroms");
548 if (FileExists(DFile
) == true)
550 if (ReadConfigFile(Database
,DFile
) == false)
551 return _error
->Error("Unable to read the cdrom database %s",
555 // Unmount the CD and get the user to put in the one they want
556 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
559 log
->Update(_("Unmounting CD-ROM\n"), STEP_UNMOUNT
);
563 log
->Update(_("Waiting for disc...\n"), STEP_WAIT
);
564 if(!log
->ChangeCdrom()) {
570 // Mount the new CDROM
571 log
->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT
);
572 if (MountCdrom(CDROM
) == false)
573 return _error
->Error("Failed to mount the cdrom.");
576 // Hash the CD to get an ID
578 log
->Update(_("Identifying.. "), STEP_IDENT
);
580 if (IdentCdrom(CDROM
,ID
) == false)
586 log
->Update("["+ID
+"]\n");
589 log
->Update(_("Scanning Disc for index files..\n"),STEP_SCAN
);
591 // Get the CD structure
593 vector
<string
> SourceList
;
594 vector
<string
> SigList
;
595 string StartDir
= SafeGetCWD();
597 if (FindPackages(CDROM
,List
,SourceList
, SigList
,InfoDir
,log
) == false)
603 chdir(StartDir
.c_str());
605 if (_config
->FindB("Debug::aptcdrom",false) == true)
607 cout
<< "I found (binary):" << endl
;
608 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
610 cout
<< "I found (source):" << endl
;
611 for (vector
<string
>::iterator I
= SourceList
.begin(); I
!= SourceList
.end(); I
++)
613 cout
<< "I found (Signatures):" << endl
;
614 for (vector
<string
>::iterator I
= SigList
.begin(); I
!= SigList
.end(); I
++)
618 //log->Update(_("Cleaning package lists..."), STEP_CLEAN);
621 DropBinaryArch(List
);
622 DropRepeats(List
,"Packages");
623 DropRepeats(SourceList
,"Sources");
624 DropRepeats(SigList
,"Release.gpg");
627 ioprintf(msg
, _("Found %i package indexes, %i source indexes and "
629 List
.size(), SourceList
.size(), SigList
.size());
630 log
->Update(msg
.str(), STEP_SCAN
);
633 if (List
.size() == 0 && SourceList
.size() == 0)
634 return _error
->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
636 // Check if the CD is in the database
638 if (Database
.Exists("CD::" + ID
) == false ||
639 _config
->FindB("APT::CDROM::Rename",false) == true)
641 // Try to use the CDs label if at all possible
642 if (InfoDir
.empty() == false &&
643 FileExists(InfoDir
+ "/info") == true)
645 ifstream
F(string(InfoDir
+ "/info").c_str());
649 if (Name
.empty() == false)
651 // Escape special characters
652 string::iterator J
= Name
.begin();
653 for (; J
!= Name
.end(); J
++)
654 if (*J
== '"' || *J
== ']' || *J
== '[')
659 ioprintf(msg
, "Found label '%s'\n", Name
.c_str());
660 log
->Update(msg
.str());
662 Database
.Set("CD::" + ID
+ "::Label",Name
);
666 if (_config
->FindB("APT::CDROM::Rename",false) == true ||
667 Name
.empty() == true)
670 return _error
->Error("No disc name found and no way to ask for it");
673 if(!log
->AskCdromName(Name
)) {
677 cout
<< "Name: '" << Name
<< "'" << endl
;
679 if (Name
.empty() == false &&
680 Name
.find('"') == string::npos
&&
681 Name
.find('[') == string::npos
&&
682 Name
.find(']') == string::npos
)
684 log
->Update(_("That is not a valid name, try again.\n"));
689 Name
= Database
.Find("CD::" + ID
);
691 // Escape special characters
692 string::iterator J
= Name
.begin();
693 for (; J
!= Name
.end(); J
++)
694 if (*J
== '"' || *J
== ']' || *J
== '[')
697 Database
.Set("CD::" + ID
,Name
);
700 ioprintf(msg
, _("This Disc is called: \n'%s'\n"), Name
.c_str());
701 log
->Update(msg
.str());
704 log
->Update(_("Copying package lists..."), STEP_COPY
);
705 // take care of the signatures and copy them if they are ok
706 // (we do this before PackageCopy as it modifies "List" and "SourceList")
707 SigVerify SignVerify
;
708 SignVerify
.CopyAndVerify(CDROM
, Name
, SigList
, List
, SourceList
);
710 // Copy the package files to the state directory
713 if (Copy
.CopyPackages(CDROM
,Name
,List
, log
) == false ||
714 SrcCopy
.CopyPackages(CDROM
,Name
,SourceList
, log
) == false)
717 // reduce the List so that it takes less space in sources.list
718 ReduceSourcelist(CDROM
,List
);
719 ReduceSourcelist(CDROM
,SourceList
);
721 // Write the database and sourcelist
722 if (_config
->FindB("APT::cdrom::NoAct",false) == false)
724 if (WriteDatabase(Database
) == false)
728 log
->Update(_("Writing new source list\n"), STEP_WRITE
);
730 if (WriteSourceList(Name
,List
,false) == false ||
731 WriteSourceList(Name
,SourceList
,true) == false)
735 // Print the sourcelist entries
737 log
->Update(_("Source List entries for this Disc are:\n"));
739 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
741 string::size_type Space
= (*I
).find(' ');
742 if (Space
== string::npos
)
743 return _error
->Error("Internal error");
747 msg
<< "deb cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
748 " " << string(*I
,Space
+1) << endl
;
749 log
->Update(msg
.str());
753 for (vector
<string
>::iterator I
= SourceList
.begin(); I
!= SourceList
.end(); I
++)
755 string::size_type Space
= (*I
).find(' ');
756 if (Space
== string::npos
)
757 return _error
->Error("Internal error");
761 msg
<< "deb-src cdrom:[" << Name
<< "]/" << string(*I
,0,Space
) <<
762 " " << string(*I
,Space
+1) << endl
;
763 log
->Update(msg
.str());
769 // Unmount and finish
770 if (_config
->FindB("APT::CDROM::NoMount",false) == false) {
771 log
->Update(_("Unmounting CD-ROM..."), STEP_LAST
);