986 lines
28 KiB
C++
986 lines
28 KiB
C++
/*
|
|
*/
|
|
#include <config.h>
|
|
|
|
#include <apt-pkg/aptconfiguration.h>
|
|
#include <apt-pkg/cdrom.h>
|
|
#include <apt-pkg/cdromutl.h>
|
|
#include <apt-pkg/configuration.h>
|
|
#include <apt-pkg/error.h>
|
|
#include <apt-pkg/fileutl.h>
|
|
#include <apt-pkg/indexcopy.h>
|
|
#include <apt-pkg/strutl.h>
|
|
|
|
#include <algorithm>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <dirent.h>
|
|
#include <dlfcn.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include <apti18n.h>
|
|
|
|
#ifdef HAVE_UDEV
|
|
#include <libudev.h>
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
// FindPackages - Find the package files on the CDROM /*{{{*/
|
|
// ---------------------------------------------------------------------
|
|
/* We look over the cdrom for package files. This is a recursive
|
|
search that short circuits when it his a package file in the dir.
|
|
This speeds it up greatly as the majority of the size is in the
|
|
binary-* sub dirs. */
|
|
bool pkgCdrom::FindPackages(string CD,
|
|
vector<string> &List,
|
|
vector<string> &SList,
|
|
vector<string> &SigList,
|
|
vector<string> &TransList,
|
|
string &InfoDir, pkgCdromStatus *log,
|
|
unsigned int Depth)
|
|
{
|
|
static ino_t Inodes[9];
|
|
DIR *D;
|
|
|
|
// if we have a look we "pulse" now
|
|
if(log)
|
|
log->Update();
|
|
|
|
if (Depth >= 7)
|
|
return true;
|
|
|
|
if (CD[CD.length()-1] != '/')
|
|
CD += '/';
|
|
|
|
if (chdir(CD.c_str()) != 0)
|
|
return _error->Errno("chdir","Unable to change to %s",CD.c_str());
|
|
|
|
// Look for a .disk subdirectory
|
|
if (InfoDir.empty() == true)
|
|
{
|
|
if (DirectoryExists(".disk") == true)
|
|
InfoDir = InfoDir + CD + ".disk/";
|
|
}
|
|
|
|
// Don't look into directories that have been marked to ignore.
|
|
if (RealFileExists(".aptignr") == true)
|
|
return true;
|
|
|
|
/* Check _first_ for a signature file as apt-cdrom assumes that all files
|
|
under a Packages/Source file are in control of that file and stops
|
|
the scanning
|
|
*/
|
|
if (RealFileExists("Release.gpg") == true || RealFileExists("InRelease") == true)
|
|
{
|
|
SigList.push_back(CD);
|
|
}
|
|
|
|
/* Aha! We found some package files. We assume that everything under
|
|
this dir is controlled by those package files so we don't look down
|
|
anymore */
|
|
std::vector<APT::Configuration::Compressor> const compressor = APT::Configuration::getCompressors();
|
|
for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
|
|
c != compressor.end(); ++c)
|
|
{
|
|
if (RealFileExists(std::string("Packages").append(c->Extension).c_str()) == false)
|
|
continue;
|
|
|
|
if (_config->FindB("Debug::aptcdrom",false) == true)
|
|
std::clog << "Found Packages in " << CD << std::endl;
|
|
List.push_back(CD);
|
|
|
|
// Continue down if thorough is given
|
|
if (_config->FindB("APT::CDROM::Thorough",false) == false)
|
|
return true;
|
|
break;
|
|
}
|
|
for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
|
|
c != compressor.end(); ++c)
|
|
{
|
|
if (RealFileExists(std::string("Sources").append(c->Extension).c_str()) == false)
|
|
continue;
|
|
|
|
if (_config->FindB("Debug::aptcdrom",false) == true)
|
|
std::clog << "Found Sources in " << CD << std::endl;
|
|
SList.push_back(CD);
|
|
|
|
// Continue down if thorough is given
|
|
if (_config->FindB("APT::CDROM::Thorough",false) == false)
|
|
return true;
|
|
break;
|
|
}
|
|
|
|
// see if we find translation indices
|
|
if (DirectoryExists("i18n") == true)
|
|
{
|
|
D = opendir("i18n");
|
|
for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
|
|
{
|
|
if(strncmp(Dir->d_name, "Translation-", strlen("Translation-")) != 0)
|
|
continue;
|
|
string file = Dir->d_name;
|
|
for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
|
|
c != compressor.end(); ++c)
|
|
{
|
|
string fileext{flExtension(file)};
|
|
if (file == fileext)
|
|
fileext.clear();
|
|
else if (fileext.empty() == false)
|
|
fileext = "." + fileext;
|
|
|
|
if (c->Extension == fileext)
|
|
{
|
|
if (_config->FindB("Debug::aptcdrom",false) == true)
|
|
std::clog << "Found translation " << Dir->d_name << " in " << CD << "i18n/" << std::endl;
|
|
file.erase(file.size() - fileext.size());
|
|
TransList.push_back(CD + "i18n/" + file);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
closedir(D);
|
|
}
|
|
|
|
D = opendir(".");
|
|
if (D == 0)
|
|
return _error->Errno("opendir","Unable to read %s",CD.c_str());
|
|
|
|
// Run over the directory
|
|
for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
|
|
{
|
|
// Skip some files..
|
|
if (strcmp(Dir->d_name,".") == 0 ||
|
|
strcmp(Dir->d_name,"..") == 0 ||
|
|
strcmp(Dir->d_name,".disk") == 0 ||
|
|
strcmp(Dir->d_name,"debian-installer") == 0)
|
|
continue;
|
|
|
|
// See if the name is a sub directory
|
|
struct stat Buf;
|
|
if (stat(Dir->d_name,&Buf) != 0)
|
|
continue;
|
|
|
|
if (S_ISDIR(Buf.st_mode) == 0)
|
|
continue;
|
|
|
|
unsigned int I;
|
|
for (I = 0; I != Depth; I++)
|
|
if (Inodes[I] == Buf.st_ino)
|
|
break;
|
|
if (I != Depth)
|
|
continue;
|
|
|
|
// Store the inodes weve seen
|
|
Inodes[Depth] = Buf.st_ino;
|
|
|
|
// Descend
|
|
if (FindPackages(CD + Dir->d_name,List,SList,SigList,TransList,InfoDir,log,Depth+1) == false)
|
|
break;
|
|
|
|
if (chdir(CD.c_str()) != 0)
|
|
{
|
|
_error->Errno("chdir","Unable to change to %s", CD.c_str());
|
|
closedir(D);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
closedir(D);
|
|
|
|
return !_error->PendingError();
|
|
}
|
|
/*}}}*/
|
|
// Score - We compute a 'score' for a path /*{{{*/
|
|
// ---------------------------------------------------------------------
|
|
/* Paths are scored based on how close they come to what I consider
|
|
normal. That is ones that have 'dist' 'stable' 'testing' will score
|
|
higher than ones without. */
|
|
int pkgCdrom::Score(string Path)
|
|
{
|
|
int Res = 0;
|
|
if (Path.find("stable/") != string::npos)
|
|
Res += 29;
|
|
if (Path.find("/binary-") != string::npos)
|
|
Res += 20;
|
|
if (Path.find("testing/") != string::npos)
|
|
Res += 28;
|
|
if (Path.find("unstable/") != string::npos)
|
|
Res += 27;
|
|
if (Path.find("/dists/") != string::npos)
|
|
Res += 40;
|
|
if (Path.find("/main/") != string::npos)
|
|
Res += 20;
|
|
if (Path.find("/contrib/") != string::npos)
|
|
Res += 20;
|
|
if (Path.find("/non-free/") != string::npos)
|
|
Res += 20;
|
|
if (Path.find("/non-free-firmware/") != string::npos)
|
|
Res += 20;
|
|
if (Path.find("/source/") != string::npos)
|
|
Res += 10;
|
|
if (Path.find("/debian/") != string::npos)
|
|
Res -= 10;
|
|
|
|
// check for symlinks in the patch leading to the actual file
|
|
// a symlink gets a big penalty
|
|
struct stat Buf;
|
|
string statPath = flNotFile(Path);
|
|
string cdromPath = _config->FindDir("Acquire::cdrom::mount");
|
|
while(statPath != cdromPath && statPath != "./") {
|
|
statPath.resize(statPath.size()-1); // remove the trailing '/'
|
|
if (lstat(statPath.c_str(),&Buf) == 0) {
|
|
if(S_ISLNK(Buf.st_mode)) {
|
|
Res -= 60;
|
|
break;
|
|
}
|
|
}
|
|
statPath = flNotFile(statPath); // descent
|
|
}
|
|
|
|
return Res;
|
|
}
|
|
/*}}}*/
|
|
// DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
|
|
// ---------------------------------------------------------------------
|
|
/* Here we drop everything that is not this machines arch */
|
|
bool pkgCdrom::DropBinaryArch(vector<string> &List)
|
|
{
|
|
|
|
for (unsigned int I = 0; I < List.size(); I++)
|
|
{
|
|
const char *Str = List[I].c_str();
|
|
const char *Start, *End;
|
|
if ((Start = strstr(Str,"/binary-")) == 0)
|
|
continue;
|
|
|
|
// Between Start and End is the architecture
|
|
Start += 8;
|
|
if ((End = strstr(Start,"/")) != 0 && Start != End &&
|
|
APT::Configuration::checkArchitecture(string(Start, End)) == true)
|
|
continue; // okay, architecture is accepted
|
|
|
|
// not accepted -> Erase it
|
|
List.erase(List.begin() + I);
|
|
--I; // the next entry is at the same index after the erase
|
|
}
|
|
|
|
return true;
|
|
}
|
|
/*}}}*/
|
|
// DropTranslation - Dump unwanted Translation-<lang> files /*{{{*/
|
|
// ---------------------------------------------------------------------
|
|
/* Here we drop everything that is not configured in Acquire::Languages */
|
|
bool pkgCdrom::DropTranslation(vector<string> &List)
|
|
{
|
|
for (unsigned int I = 0; I < List.size(); I++)
|
|
{
|
|
const char *Start;
|
|
if ((Start = strstr(List[I].c_str(), "/Translation-")) == NULL)
|
|
continue;
|
|
Start += strlen("/Translation-");
|
|
|
|
if (APT::Configuration::checkLanguage(Start, true) == true)
|
|
continue;
|
|
|
|
// not accepted -> Erase it
|
|
List.erase(List.begin() + I);
|
|
--I; // the next entry is at the same index after the erase
|
|
}
|
|
|
|
return true;
|
|
}
|
|
/*}}}*/
|
|
// DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
|
|
// ---------------------------------------------------------------------
|
|
/* Here we go and stat every file that we found and strip dup inodes. */
|
|
bool pkgCdrom::DropRepeats(vector<string> &List,const char *Name)
|
|
{
|
|
bool couldFindAllFiles = true;
|
|
// Get a list of all the inodes
|
|
ino_t *Inodes = new ino_t[List.size()];
|
|
for (unsigned int I = 0; I != List.size(); ++I)
|
|
{
|
|
struct stat Buf;
|
|
bool found = false;
|
|
|
|
std::vector<APT::Configuration::Compressor> const compressor = APT::Configuration::getCompressors();
|
|
for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
|
|
c != compressor.end(); ++c)
|
|
{
|
|
std::string const filename = List[I] + Name + c->Extension;
|
|
if (stat(filename.c_str(), &Buf) != 0)
|
|
continue;
|
|
Inodes[I] = Buf.st_ino;
|
|
found = true;
|
|
break;
|
|
}
|
|
|
|
if (found == false)
|
|
{
|
|
_error->Errno("stat","Failed to stat %s%s",List[I].c_str(), Name);
|
|
couldFindAllFiles = false;
|
|
Inodes[I] = 0;
|
|
}
|
|
}
|
|
|
|
// Look for dups
|
|
for (unsigned int I = 0; I != List.size(); I++)
|
|
{
|
|
if (Inodes[I] == 0)
|
|
continue;
|
|
for (unsigned int J = I+1; J < List.size(); J++)
|
|
{
|
|
// No match
|
|
if (Inodes[J] == 0 || Inodes[J] != Inodes[I])
|
|
continue;
|
|
|
|
// We score the two paths.. and erase one
|
|
int ScoreA = Score(List[I]);
|
|
int ScoreB = Score(List[J]);
|
|
if (ScoreA < ScoreB)
|
|
{
|
|
List[I] = string();
|
|
break;
|
|
}
|
|
|
|
List[J] = string();
|
|
}
|
|
}
|
|
delete[] Inodes;
|
|
|
|
// Wipe erased entries
|
|
for (unsigned int I = 0; I < List.size();)
|
|
{
|
|
if (List[I].empty() == false)
|
|
I++;
|
|
else
|
|
List.erase(List.begin()+I);
|
|
}
|
|
|
|
return couldFindAllFiles;
|
|
}
|
|
/*}}}*/
|
|
// ReduceSourceList - Takes the path list and reduces it /*{{{*/
|
|
// ---------------------------------------------------------------------
|
|
/* This takes the list of source list expressed entries and collects
|
|
similar ones to form a single entry for each dist */
|
|
void pkgCdrom::ReduceSourcelist(string /*CD*/,vector<string> &List)
|
|
{
|
|
sort(List.begin(),List.end());
|
|
|
|
// Collect similar entries
|
|
for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
|
|
{
|
|
// Find a space..
|
|
string::size_type Space = (*I).find(' ');
|
|
if (Space == string::npos)
|
|
continue;
|
|
string::size_type SSpace = (*I).find(' ',Space + 1);
|
|
if (SSpace == string::npos)
|
|
continue;
|
|
|
|
string Word1 = string(*I,Space,SSpace-Space);
|
|
string Prefix = string(*I,0,Space);
|
|
string Component = string(*I,SSpace);
|
|
for (vector<string>::iterator J = List.begin(); J != I; ++J)
|
|
{
|
|
// Find a space..
|
|
string::size_type Space2 = (*J).find(' ');
|
|
if (Space2 == string::npos)
|
|
continue;
|
|
string::size_type SSpace2 = (*J).find(' ',Space2 + 1);
|
|
if (SSpace2 == string::npos)
|
|
continue;
|
|
|
|
if (string(*J,0,Space2) != Prefix)
|
|
continue;
|
|
if (string(*J,Space2,SSpace2-Space2) != Word1)
|
|
continue;
|
|
|
|
string Component2 = string(*J, SSpace2) + " ";
|
|
if (Component2.find(Component + " ") == std::string::npos)
|
|
*J += Component;
|
|
I->clear();
|
|
}
|
|
}
|
|
|
|
// Wipe erased entries
|
|
for (unsigned int I = 0; I < List.size();)
|
|
{
|
|
if (List[I].empty() == false)
|
|
I++;
|
|
else
|
|
List.erase(List.begin()+I);
|
|
}
|
|
}
|
|
/*}}}*/
|
|
// WriteDatabase - Write the CDROM Database file /*{{{*/
|
|
// ---------------------------------------------------------------------
|
|
/* We rewrite the configuration class associated with the cdrom database. */
|
|
bool pkgCdrom::WriteDatabase(Configuration &Cnf)
|
|
{
|
|
string DFile = _config->FindFile("Dir::State::cdroms");
|
|
string NewFile = DFile + ".new";
|
|
|
|
RemoveFile("WriteDatabase", NewFile);
|
|
ofstream Out(NewFile.c_str());
|
|
if (!Out)
|
|
return _error->Errno("ofstream::ofstream",
|
|
"Failed to open %s.new",DFile.c_str());
|
|
|
|
/* Write out all of the configuration directives by walking the
|
|
configuration tree */
|
|
Cnf.Dump(Out, NULL, "%F \"%v\";\n", false);
|
|
|
|
Out.close();
|
|
|
|
if (FileExists(DFile) == true)
|
|
rename(DFile.c_str(), (DFile + '~').c_str());
|
|
if (rename(NewFile.c_str(),DFile.c_str()) != 0)
|
|
return _error->Errno("rename","Failed to rename %s.new to %s",
|
|
DFile.c_str(),DFile.c_str());
|
|
|
|
return true;
|
|
}
|
|
/*}}}*/
|
|
// WriteSourceList - Write an updated sourcelist /*{{{*/
|
|
// ---------------------------------------------------------------------
|
|
/* This reads the old source list and copies it into the new one. It
|
|
appends the new CDROM entries just after the first block of comments.
|
|
This places them first in the file. It also removes any old entries
|
|
that were the same. */
|
|
bool pkgCdrom::WriteSourceList(string Name,vector<string> &List,bool Source)
|
|
{
|
|
if (List.empty() == true)
|
|
return true;
|
|
|
|
string File = _config->FindFile("Dir::Etc::sourcelist");
|
|
|
|
FileFd F(FileExists(File) ? File : "/dev/null", FileFd::ReadOnly);
|
|
if (not F.IsOpen() || F.Failed())
|
|
return _error->Errno("WriteSourceList", "Opening %s failed", File.c_str());
|
|
|
|
string NewFile = File + ".new";
|
|
RemoveFile("WriteSourceList", NewFile);
|
|
ofstream Out(NewFile.c_str());
|
|
if (!Out)
|
|
return _error->Errno("ofstream::ofstream",
|
|
"Failed to open %s.new",File.c_str());
|
|
|
|
// Create a short uri without the path
|
|
string ShortURI = "cdrom:[" + Name + "]/";
|
|
string ShortURI2 = "cdrom:" + Name + "/"; // For Compatibility
|
|
|
|
string Type;
|
|
if (Source == true)
|
|
Type = "deb-src";
|
|
else
|
|
Type = "deb";
|
|
|
|
int CurLine = 0;
|
|
bool First = true;
|
|
std::string Buffer;
|
|
while (F.ReadLine(Buffer))
|
|
{
|
|
++CurLine;
|
|
std::string const Cleaned{APT::String::Strip(SubstVar(Buffer, "\t", " "))};
|
|
|
|
// Comment or blank
|
|
if (Cleaned.empty() || Cleaned[0] == '#')
|
|
{
|
|
Out << Buffer << endl;
|
|
continue;
|
|
}
|
|
|
|
if (First == true)
|
|
{
|
|
for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
|
|
{
|
|
string::size_type Space = (*I).find(' ');
|
|
if (Space == string::npos)
|
|
return _error->Error("Internal error");
|
|
Out << Type << " cdrom:[" << Name << "]/" << string(*I,0,Space) <<
|
|
" " << string(*I,Space+1) << endl;
|
|
}
|
|
}
|
|
First = false;
|
|
|
|
// Grok it
|
|
string cType;
|
|
string URI;
|
|
const char *C = Cleaned.c_str();
|
|
if (ParseQuoteWord(C,cType) == false ||
|
|
ParseQuoteWord(C,URI) == false)
|
|
{
|
|
Out << Buffer << endl;
|
|
continue;
|
|
}
|
|
|
|
// Emit lines like this one
|
|
if (cType != Type || (string(URI,0,ShortURI.length()) != ShortURI &&
|
|
string(URI,0,ShortURI.length()) != ShortURI2))
|
|
{
|
|
Out << Buffer << endl;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Just in case the file was empty
|
|
if (First == true)
|
|
{
|
|
for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
|
|
{
|
|
string::size_type Space = (*I).find(' ');
|
|
if (Space == string::npos)
|
|
return _error->Error("Internal error");
|
|
|
|
Out << "deb cdrom:[" << Name << "]/" << string(*I,0,Space) <<
|
|
" " << string(*I,Space+1) << endl;
|
|
}
|
|
}
|
|
|
|
Out.close();
|
|
|
|
rename(File.c_str(), (File + '~').c_str());
|
|
if (rename(NewFile.c_str(),File.c_str()) != 0)
|
|
return _error->Errno("rename","Failed to rename %s.new to %s",
|
|
File.c_str(),File.c_str());
|
|
|
|
return true;
|
|
}
|
|
/*}}}*/
|
|
bool pkgCdrom::UnmountCDROM(std::string const &CDROM, pkgCdromStatus * const log)/*{{{*/
|
|
{
|
|
if (_config->FindB("APT::CDROM::NoMount",false) == true)
|
|
return true;
|
|
if (log != NULL)
|
|
log->Update(_("Unmounting CD-ROM...\n"), STEP_LAST);
|
|
return UnmountCdrom(CDROM);
|
|
}
|
|
/*}}}*/
|
|
bool pkgCdrom::MountAndIdentCDROM(Configuration &Database, std::string &CDROM, std::string &ident, pkgCdromStatus * const log, bool const interactive)/*{{{*/
|
|
{
|
|
// Startup
|
|
CDROM = _config->FindDir("Acquire::cdrom::mount");
|
|
if (CDROM[0] == '.')
|
|
CDROM= SafeGetCWD() + '/' + CDROM;
|
|
|
|
if (log != NULL)
|
|
{
|
|
string msg;
|
|
log->SetTotal(STEP_LAST);
|
|
strprintf(msg, _("Using CD-ROM mount point %s\n"), CDROM.c_str());
|
|
log->Update(msg, STEP_PREPARE);
|
|
}
|
|
|
|
// Unmount the CD and get the user to put in the one they want
|
|
if (_config->FindB("APT::CDROM::NoMount", false) == false)
|
|
{
|
|
if (interactive == true)
|
|
{
|
|
UnmountCDROM(CDROM, log);
|
|
|
|
if(log != NULL)
|
|
{
|
|
log->Update(_("Waiting for disc...\n"), STEP_WAIT);
|
|
if(!log->ChangeCdrom()) {
|
|
// user aborted
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mount the new CDROM
|
|
if(log != NULL)
|
|
log->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT);
|
|
|
|
if (MountCdrom(CDROM) == false)
|
|
return _error->Error("Failed to mount the cdrom.");
|
|
}
|
|
|
|
if (IsMounted(CDROM) == false)
|
|
return _error->Error("Failed to mount the cdrom.");
|
|
|
|
// Hash the CD to get an ID
|
|
if (log != NULL)
|
|
log->Update(_("Identifying... "), STEP_IDENT);
|
|
|
|
if (IdentCdrom(CDROM,ident) == false)
|
|
{
|
|
ident = "";
|
|
if (log != NULL)
|
|
log->Update("\n");
|
|
UnmountCDROM(CDROM, NULL);
|
|
return false;
|
|
}
|
|
|
|
if (log != NULL)
|
|
{
|
|
string msg;
|
|
strprintf(msg, "[%s]\n", ident.c_str());
|
|
log->Update(msg);
|
|
}
|
|
|
|
// Read the database
|
|
string DFile = _config->FindFile("Dir::State::cdroms");
|
|
if (FileExists(DFile) == true)
|
|
{
|
|
if (ReadConfigFile(Database,DFile) == false)
|
|
{
|
|
UnmountCDROM(CDROM, NULL);
|
|
return _error->Error("Unable to read the cdrom database %s",
|
|
DFile.c_str());
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
/*}}}*/
|
|
bool pkgCdrom::Ident(string &ident, pkgCdromStatus *log) /*{{{*/
|
|
{
|
|
Configuration Database;
|
|
std::string CDROM;
|
|
if (MountAndIdentCDROM(Database, CDROM, ident, log, false) == false)
|
|
return false;
|
|
|
|
if (log != NULL)
|
|
{
|
|
string msg;
|
|
strprintf(msg, _("Stored label: %s\n"),
|
|
Database.Find("CD::"+ident).c_str());
|
|
log->Update(msg);
|
|
}
|
|
|
|
// Unmount and finish
|
|
UnmountCDROM(CDROM, log);
|
|
return true;
|
|
}
|
|
/*}}}*/
|
|
bool pkgCdrom::Add(pkgCdromStatus *log) /*{{{*/
|
|
{
|
|
Configuration Database;
|
|
std::string ID, CDROM;
|
|
if (MountAndIdentCDROM(Database, CDROM, ID, log, true) == false)
|
|
return false;
|
|
|
|
if(log != NULL)
|
|
log->Update(_("Scanning disc for index files...\n"),STEP_SCAN);
|
|
|
|
// Get the CD structure
|
|
vector<string> List;
|
|
vector<string> SourceList;
|
|
vector<string> SigList;
|
|
vector<string> TransList;
|
|
string StartDir = SafeGetCWD();
|
|
string InfoDir;
|
|
if (FindPackages(CDROM,List,SourceList, SigList,TransList,InfoDir,log) == false)
|
|
{
|
|
if (log != NULL)
|
|
log->Update("\n");
|
|
UnmountCDROM(CDROM, NULL);
|
|
return false;
|
|
}
|
|
|
|
if (chdir(StartDir.c_str()) != 0)
|
|
{
|
|
UnmountCDROM(CDROM, NULL);
|
|
return _error->Errno("chdir","Unable to change to %s", StartDir.c_str());
|
|
}
|
|
|
|
if (_config->FindB("Debug::aptcdrom",false) == true)
|
|
{
|
|
cout << "I found (binary):" << endl;
|
|
for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
|
|
cout << *I << endl;
|
|
cout << "I found (source):" << endl;
|
|
for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); ++I)
|
|
cout << *I << endl;
|
|
cout << "I found (Signatures):" << endl;
|
|
for (vector<string>::iterator I = SigList.begin(); I != SigList.end(); ++I)
|
|
cout << *I << endl;
|
|
}
|
|
|
|
//log->Update(_("Cleaning package lists..."), STEP_CLEAN);
|
|
|
|
// Fix up the list
|
|
DropBinaryArch(List);
|
|
DropRepeats(List,"Packages");
|
|
DropRepeats(SourceList,"Sources");
|
|
// FIXME: We ignore stat() errors here as we usually have only one of those in use
|
|
// This has little potential to drop 'valid' stat() errors as we know that one of these
|
|
// files need to exist, but it would be better if we would check it here
|
|
_error->PushToStack();
|
|
DropRepeats(SigList,"Release.gpg");
|
|
DropRepeats(SigList,"InRelease");
|
|
_error->RevertToStack();
|
|
DropRepeats(TransList,"");
|
|
if (_config->FindB("APT::CDROM::DropTranslation", true) == true)
|
|
DropTranslation(TransList);
|
|
if(log != NULL) {
|
|
string msg;
|
|
strprintf(msg, _("Found %zu package indexes, %zu source indexes, "
|
|
"%zu translation indexes and %zu signatures\n"),
|
|
List.size(), SourceList.size(), TransList.size(),
|
|
SigList.size());
|
|
log->Update(msg, STEP_SCAN);
|
|
}
|
|
|
|
if (List.empty() == true && SourceList.empty() == true)
|
|
{
|
|
UnmountCDROM(CDROM, NULL);
|
|
return _error->Error(_("Unable to locate any package files, perhaps this is not a Debian Disc or the wrong architecture?"));
|
|
}
|
|
|
|
// Check if the CD is in the database
|
|
string Name;
|
|
if (Database.Exists("CD::" + ID) == false ||
|
|
_config->FindB("APT::CDROM::Rename",false) == true)
|
|
{
|
|
// Try to use the CDs label if at all possible
|
|
if (InfoDir.empty() == false &&
|
|
FileExists(InfoDir + "/info") == true)
|
|
{
|
|
ifstream F((InfoDir + "/info").c_str());
|
|
if (F.good() == true)
|
|
getline(F,Name);
|
|
|
|
if (Name.empty() == false)
|
|
{
|
|
// Escape special characters
|
|
string::iterator J = Name.begin();
|
|
for (; J != Name.end(); ++J)
|
|
if (*J == '"' || *J == ']' || *J == '[')
|
|
*J = '_';
|
|
|
|
if(log != NULL)
|
|
{
|
|
string msg;
|
|
strprintf(msg, _("Found label '%s'\n"), Name.c_str());
|
|
log->Update(msg);
|
|
}
|
|
Database.Set("CD::" + ID + "::Label",Name);
|
|
}
|
|
}
|
|
|
|
if (_config->FindB("APT::CDROM::Rename",false) == true ||
|
|
Name.empty() == true)
|
|
{
|
|
if(log == NULL)
|
|
{
|
|
UnmountCDROM(CDROM, NULL);
|
|
return _error->Error("No disc name found and no way to ask for it");
|
|
}
|
|
|
|
while(true) {
|
|
if(!log->AskCdromName(Name)) {
|
|
// user canceld
|
|
UnmountCDROM(CDROM, NULL);
|
|
return false;
|
|
}
|
|
cout << "Name: '" << Name << "'" << endl;
|
|
|
|
if (Name.empty() == false &&
|
|
Name.find('"') == string::npos &&
|
|
Name.find('[') == string::npos &&
|
|
Name.find(']') == string::npos)
|
|
break;
|
|
log->Update(_("That is not a valid name, try again.\n"));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
Name = Database.Find("CD::" + ID);
|
|
|
|
// Escape special characters
|
|
string::iterator J = Name.begin();
|
|
for (; J != Name.end(); ++J)
|
|
if (*J == '"' || *J == ']' || *J == '[')
|
|
*J = '_';
|
|
|
|
Database.Set("CD::" + ID,Name);
|
|
if(log != NULL)
|
|
{
|
|
string msg;
|
|
strprintf(msg, _("This disc is called: \n'%s'\n"), Name.c_str());
|
|
log->Update(msg);
|
|
log->Update(_("Copying package lists..."), STEP_COPY);
|
|
}
|
|
|
|
// check for existence and possibly create state directory for copying
|
|
string const listDir = _config->FindDir("Dir::State::lists");
|
|
string const partialListDir = listDir + "partial/";
|
|
mode_t const mode = umask(S_IWGRP | S_IWOTH);
|
|
bool const creation_fail = (CreateAPTDirectoryIfNeeded(_config->FindDir("Dir::State"), partialListDir) == false &&
|
|
CreateAPTDirectoryIfNeeded(listDir, partialListDir) == false);
|
|
umask(mode);
|
|
if (creation_fail == true)
|
|
{
|
|
UnmountCDROM(CDROM, NULL);
|
|
return _error->Errno("cdrom", _("List directory %s is missing."), (listDir + "partial").c_str());
|
|
}
|
|
|
|
// take care of the signatures and copy them if they are ok
|
|
// (we do this before PackageCopy as it modifies "List" and "SourceList")
|
|
SigVerify SignVerify;
|
|
SignVerify.CopyAndVerify(CDROM, Name, SigList, List, SourceList);
|
|
|
|
// Copy the package files to the state directory
|
|
PackageCopy Copy;
|
|
SourceCopy SrcCopy;
|
|
TranslationsCopy TransCopy;
|
|
if (Copy.CopyPackages(CDROM,Name,List, log) == false ||
|
|
SrcCopy.CopyPackages(CDROM,Name,SourceList, log) == false ||
|
|
TransCopy.CopyTranslations(CDROM,Name,TransList, log) == false)
|
|
{
|
|
UnmountCDROM(CDROM, NULL);
|
|
return false;
|
|
}
|
|
|
|
// reduce the List so that it takes less space in sources.list
|
|
ReduceSourcelist(CDROM,List);
|
|
ReduceSourcelist(CDROM,SourceList);
|
|
|
|
// Write the database and sourcelist
|
|
if (_config->FindB("APT::cdrom::NoAct",false) == false)
|
|
{
|
|
if (WriteDatabase(Database) == false)
|
|
{
|
|
UnmountCDROM(CDROM, NULL);
|
|
return false;
|
|
}
|
|
|
|
if(log != NULL)
|
|
log->Update(_("Writing new source list\n"), STEP_WRITE);
|
|
if (WriteSourceList(Name,List,false) == false ||
|
|
WriteSourceList(Name,SourceList,true) == false)
|
|
{
|
|
UnmountCDROM(CDROM, NULL);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Print the sourcelist entries
|
|
if(log != NULL)
|
|
log->Update(_("Source list entries for this disc are:\n"));
|
|
|
|
for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
|
|
{
|
|
string::size_type Space = (*I).find(' ');
|
|
if (Space == string::npos)
|
|
{
|
|
UnmountCDROM(CDROM, NULL);
|
|
return _error->Error("Internal error");
|
|
}
|
|
|
|
if(log != NULL)
|
|
{
|
|
stringstream msg;
|
|
msg << "deb cdrom:[" << Name << "]/" << string(*I,0,Space) <<
|
|
" " << string(*I,Space+1) << endl;
|
|
log->Update(msg.str());
|
|
}
|
|
}
|
|
|
|
for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); ++I)
|
|
{
|
|
string::size_type Space = (*I).find(' ');
|
|
if (Space == string::npos)
|
|
{
|
|
UnmountCDROM(CDROM, NULL);
|
|
return _error->Error("Internal error");
|
|
}
|
|
|
|
if(log != NULL) {
|
|
stringstream msg;
|
|
msg << "deb-src cdrom:[" << Name << "]/" << string(*I,0,Space) <<
|
|
" " << string(*I,Space+1) << endl;
|
|
log->Update(msg.str());
|
|
}
|
|
}
|
|
|
|
// Unmount and finish
|
|
UnmountCDROM(CDROM, log);
|
|
return true;
|
|
}
|
|
/*}}}*/
|
|
|
|
pkgUdevCdromDevices::pkgUdevCdromDevices() /*{{{*/
|
|
: d(NULL)
|
|
{
|
|
}
|
|
/*}}}*/
|
|
// convenience interface, this will just call ScanForRemovable /*{{{*/
|
|
vector<CdromDevice> pkgUdevCdromDevices::Scan()
|
|
{
|
|
bool CdromOnly = _config->FindB("APT::cdrom::CdromOnly", true);
|
|
return ScanForRemovable(CdromOnly);
|
|
}
|
|
/*}}}*/
|
|
vector<CdromDevice> pkgUdevCdromDevices::ScanForRemovable(bool CdromOnly)/*{{{*/
|
|
{
|
|
vector<CdromDevice> cdrom_devices;
|
|
#ifdef HAVE_UDEV
|
|
struct udev_enumerate *enumerate;
|
|
struct udev_list_entry *l, *devices;
|
|
struct udev *udev_ctx;
|
|
|
|
udev_ctx = udev_new();
|
|
enumerate = udev_enumerate_new (udev_ctx);
|
|
if (CdromOnly)
|
|
udev_enumerate_add_match_property(enumerate, "ID_CDROM", "1");
|
|
else {
|
|
udev_enumerate_add_match_sysattr(enumerate, "removable", "1");
|
|
}
|
|
|
|
udev_enumerate_scan_devices (enumerate);
|
|
devices = udev_enumerate_get_list_entry (enumerate);
|
|
for (l = devices; l != NULL; l = udev_list_entry_get_next (l))
|
|
{
|
|
CdromDevice cdrom;
|
|
struct udev_device *udevice;
|
|
udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerate), udev_list_entry_get_name (l));
|
|
if (udevice == NULL)
|
|
continue;
|
|
const char* devnode = udev_device_get_devnode(udevice);
|
|
|
|
// try fstab_dir first
|
|
string mountpath;
|
|
const char* mp = udev_device_get_property_value(udevice, "FSTAB_DIR");
|
|
if (mp)
|
|
mountpath = string(mp);
|
|
else
|
|
mountpath = FindMountPointForDevice(devnode);
|
|
|
|
// fill in the struct
|
|
cdrom.DeviceName = string(devnode);
|
|
if (mountpath != "") {
|
|
cdrom.MountPath = mountpath;
|
|
string s = mountpath;
|
|
cdrom.Mounted = IsMounted(s);
|
|
} else {
|
|
cdrom.Mounted = false;
|
|
cdrom.MountPath = "";
|
|
}
|
|
cdrom_devices.push_back(cdrom);
|
|
}
|
|
#endif
|
|
return cdrom_devices;
|
|
}
|
|
/*}}}*/
|
|
|
|
pkgUdevCdromDevices::~pkgUdevCdromDevices() /*{{{*/
|
|
{
|
|
}
|
|
/*}}}*/
|
|
|
|
pkgCdromStatus::pkgCdromStatus() : d(NULL), totalSteps(0) {}
|
|
pkgCdromStatus::~pkgCdromStatus() {}
|
|
|
|
pkgCdrom::pkgCdrom() : d(NULL) {}
|
|
pkgCdrom::~pkgCdrom() {}
|