#include <sstream>
#include <unistd.h>
#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
#include <stdio.h>
/*}}}*/
using namespace std;
+
+
// IndexCopy::CopyPackages - Copy the package files from the CD /*{{{*/
// ---------------------------------------------------------------------
/* */
(*I).c_str() + CDROM.length(),GetFileName());
string TargetF = _config->FindDir("Dir::State::lists") + "partial/";
TargetF += URItoFileName(S);
+ FileFd Target;
if (_config->FindB("APT::CDROM::NoAct",false) == true)
+ {
TargetF = "/dev/null";
- FileFd Target(TargetF,FileFd::WriteEmpty);
+ Target.Open(TargetF,FileFd::WriteExists);
+ } else {
+ Target.Open(TargetF,FileFd::WriteAtomic);
+ }
FILE *TargetFl = fdopen(dup(Target.Fd()),"w");
if (_error->PendingError() == true)
return false;
_error->Warning("No valid records were found.");
if (NotFound + WrongSize > 10)
- _error->Warning("Alot of entries were discarded, something may be wrong.\n");
+ _error->Warning("A lot of entries were discarded, something may be wrong.\n");
return true;
fputc('\n',Target);
return true;
}
-
-
/*}}}*/
-
+// SigVerify::Verify - Verify a files md5sum against its metaindex /*{{{*/
+// ---------------------------------------------------------------------
+/* */
bool SigVerify::Verify(string prefix, string file, indexRecords *MetaIndex)
{
const indexRecords::checkSum *Record = MetaIndex->Lookup(file);
+ // we skip non-existing files in the verifcation to support a cdrom
+ // with no Packages file (just a Package.gz), see LP: #255545
+ // (non-existing files are not considered a error)
+ if(!FileExists(prefix+file))
+ {
+ _error->Warning(_("Skipping nonexistent file %s"), string(prefix+file).c_str());
+ return true;
+ }
+
if (!Record)
{
- _error->Warning("Can't find authentication record for: %s",file.c_str());
+ _error->Warning(_("Can't find authentication record for: %s"), file.c_str());
return false;
}
- MD5Summation sum;
- FileFd Fd(prefix+file, FileFd::ReadOnly);
- sum.AddFD(Fd.Fd(), Fd.Size());
- Fd.Close();
- string MD5 = (string)sum.Result();
-
- if (Record->MD5Hash != MD5)
+ if (!Record->Hash.VerifyFile(prefix+file))
{
- _error->Warning("MD5 mismatch for: %s",file.c_str());
+ _error->Warning(_("Hash mismatch for: %s"),file.c_str());
return false;
}
if(_config->FindB("Debug::aptcdrom",false))
{
cout << "File: " << prefix+file << endl;
- cout << "Expected MD5sum: " << Record->MD5Hash << endl;
- cout << "got: " << MD5 << endl << endl;
+ cout << "Expected Hash " << Record->Hash.toStr() << endl;
}
return true;
}
-
-bool SigVerify::CopyMetaIndex(string CDROM, string CDName,
+ /*}}}*/
+bool SigVerify::CopyMetaIndex(string CDROM, string CDName, /*{{{*/
string prefix, string file)
{
char S[400];
FileFd Target;
FileFd Rel;
- Target.Open(TargetF,FileFd::WriteEmpty);
+ Target.Open(TargetF,FileFd::WriteAtomic);
Rel.Open(prefix + file,FileFd::ReadOnly);
if (_error->PendingError() == true)
return false;
return true;
}
-
-bool SigVerify::CopyAndVerify(string CDROM,string Name,vector<string> &SigList,
+ /*}}}*/
+bool SigVerify::CopyAndVerify(string CDROM,string Name,vector<string> &SigList, /*{{{*/
vector<string> PkgList,vector<string> SrcList)
{
if (SigList.size() == 0)
indexRecords *MetaIndex = new indexRecords;
string prefix = *I;
- // a Release.gpg without a Release should never happen
- if(!FileExists(*I+"Release"))
- continue;
-
-
- // verify the gpg signature of "Release"
- // gpg --verify "*I+Release.gpg", "*I+Release"
- const char *Args[400];
- unsigned int i = 0;
+ string const releasegpg = *I+"Release.gpg";
+ string const release = *I+"Release";
- string gpgvpath = _config->Find("Dir::Bin::gpg", "/usr/bin/gpgv");
- string pubringpath = _config->Find("Apt::GPGV::TrustedKeyring", "/etc/apt/trusted.gpg");
- string releasegpg = *I+"Release.gpg";
- string release = *I+"Release";
-
- Args[i++] = gpgvpath.c_str();
- Args[i++] = "--keyring";
- Args[i++] = pubringpath.c_str();
- Configuration::Item const *Opts;
- Opts = _config->Tree("Acquire::gpgv::Options");
- if (Opts != 0)
+ // a Release.gpg without a Release should never happen
+ if(FileExists(release) == false)
{
- Opts = Opts->Child;
- for (; Opts != 0; Opts = Opts->Next)
- {
- if (Opts->Value.empty() == true)
- continue;
- Args[i++] = Opts->Value.c_str();
- if(i >= 390) {
- _error->Error("Argument list from Acquire::gpgv::Options too long. Exiting.");
- return false;
- }
- }
+ delete MetaIndex;
+ continue;
}
-
- Args[i++] = releasegpg.c_str();
- Args[i++] = release.c_str();
- Args[i++] = NULL;
-
+
pid_t pid = ExecFork();
if(pid < 0) {
_error->Error("Fork failed");
return false;
}
- if(pid == 0) {
- execvp(gpgvpath.c_str(), (char**)Args);
- }
+ if(pid == 0)
+ RunGPGV(release, releasegpg);
+
if(!ExecWait(pid, "gpgv")) {
_error->Warning("Signature verification failed for: %s",
- string(*I+"Release.gpg").c_str());
+ releasegpg.c_str());
// something went wrong, don't copy the Release.gpg
// FIXME: delete any existing gpg file?
continue;
}
// Open the Release file and add it to the MetaIndex
- if(!MetaIndex->Load(*I+"Release"))
+ if(!MetaIndex->Load(release))
{
- _error->Error(MetaIndex->ErrorText.c_str());
+ _error->Error("%s",MetaIndex->ErrorText.c_str());
return false;
}
if(!Verify(prefix,*I, MetaIndex)) {
// something went wrong, don't copy the Release.gpg
// FIXME: delete any existing gpg file?
+ _error->Discard();
continue;
}
}
return true;
}
+ /*}}}*/
+// SigVerify::RunGPGV - returns the command needed for verify /*{{{*/
+// ---------------------------------------------------------------------
+/* Generating the commandline for calling gpgv is somehow complicated as
+ we need to add multiple keyrings and user supplied options. Also, as
+ the cdrom code currently can not use the gpgv method we have two places
+ these need to be done - so the place for this method is wrong but better
+ than code duplication… */
+bool SigVerify::RunGPGV(std::string const &File, std::string const &FileGPG,
+ int const &statusfd, int fd[2])
+{
+ string const gpgvpath = _config->Find("Dir::Bin::gpg", "/usr/bin/gpgv");
+ // FIXME: remove support for deprecated APT::GPGV setting
+ string const trustedFile = _config->Find("APT::GPGV::TrustedKeyring", _config->FindFile("Dir::Etc::Trusted"));
+ string const trustedPath = _config->FindDir("Dir::Etc::TrustedParts");
+
+ bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
+
+ if (Debug == true)
+ {
+ std::clog << "gpgv path: " << gpgvpath << std::endl;
+ std::clog << "Keyring file: " << trustedFile << std::endl;
+ std::clog << "Keyring path: " << trustedPath << std::endl;
+ }
+
+ std::vector<string> keyrings;
+ if (DirectoryExists(trustedPath))
+ keyrings = GetListOfFilesInDir(trustedPath, "gpg", false, true);
+ if (FileExists(trustedFile) == true)
+ keyrings.push_back(trustedFile);
+
+ std::vector<const char *> Args;
+ Args.reserve(30);
+
+ if (keyrings.empty() == true)
+ return false;
+
+ Args.push_back(gpgvpath.c_str());
+ Args.push_back("--ignore-time-conflict");
+
+ if (statusfd != -1)
+ {
+ Args.push_back("--status-fd");
+ char fd[10];
+ snprintf(fd, sizeof(fd), "%i", statusfd);
+ Args.push_back(fd);
+ }
+
+ for (vector<string>::const_iterator K = keyrings.begin();
+ K != keyrings.end(); ++K)
+ {
+ Args.push_back("--keyring");
+ Args.push_back(K->c_str());
+ }
+
+ Configuration::Item const *Opts;
+ Opts = _config->Tree("Acquire::gpgv::Options");
+ if (Opts != 0)
+ {
+ Opts = Opts->Child;
+ for (; Opts != 0; Opts = Opts->Next)
+ {
+ if (Opts->Value.empty() == true)
+ continue;
+ Args.push_back(Opts->Value.c_str());
+ }
+ }
+
+ Args.push_back(FileGPG.c_str());
+ Args.push_back(File.c_str());
+ Args.push_back(NULL);
+
+ if (Debug == true)
+ {
+ std::clog << "Preparing to exec: " << gpgvpath;
+ for (std::vector<const char *>::const_iterator a = Args.begin(); *a != NULL; ++a)
+ std::clog << " " << *a;
+ std::clog << std::endl;
+ }
+
+ if (statusfd != -1)
+ {
+ int const nullfd = open("/dev/null", O_RDONLY);
+ close(fd[0]);
+ // Redirect output to /dev/null; we read from the status fd
+ dup2(nullfd, STDOUT_FILENO);
+ dup2(nullfd, STDERR_FILENO);
+ // Redirect the pipe to the status fd (3)
+ dup2(fd[1], statusfd);
+
+ putenv((char *)"LANG=");
+ putenv((char *)"LC_ALL=");
+ putenv((char *)"LC_MESSAGES=");
+ }
+
+ execvp(gpgvpath.c_str(), (char **) &Args[0]);
+ return true;
+}
+ /*}}}*/
+bool TranslationsCopy::CopyTranslations(string CDROM,string Name, /*{{{*/
+ vector<string> &List, pkgCdromStatus *log)
+{
+ OpProgress *Progress = NULL;
+ if (List.size() == 0)
+ return true;
+
+ if(log)
+ Progress = log->GetOpProgress();
+
+ bool Debug = _config->FindB("Debug::aptcdrom",false);
+
+ // Prepare the progress indicator
+ unsigned long TotalSize = 0;
+ for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
+ {
+ struct stat Buf;
+ if (stat(string(*I).c_str(),&Buf) != 0 &&
+ stat(string(*I + ".gz").c_str(),&Buf) != 0)
+ return _error->Errno("stat","Stat failed for %s",
+ string(*I).c_str());
+ TotalSize += Buf.st_size;
+ }
+
+ unsigned long CurrentSize = 0;
+ unsigned int NotFound = 0;
+ unsigned int WrongSize = 0;
+ unsigned int Packages = 0;
+ for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
+ {
+ string OrigPath = string(*I,CDROM.length());
+ unsigned long FileSize = 0;
+
+ // Open the package file
+ FileFd Pkg;
+ if (FileExists(*I) == true)
+ {
+ Pkg.Open(*I,FileFd::ReadOnly);
+ FileSize = Pkg.Size();
+ }
+ else
+ {
+ FileFd From(*I + ".gz",FileFd::ReadOnly);
+ if (_error->PendingError() == true)
+ return false;
+ FileSize = From.Size();
+
+ // Get a temp file
+ FILE *tmp = tmpfile();
+ if (tmp == 0)
+ return _error->Errno("tmpfile","Unable to create a tmp file");
+ Pkg.Fd(dup(fileno(tmp)));
+ fclose(tmp);
+
+ // Fork gzip
+ pid_t Process = fork();
+ if (Process < 0)
+ return _error->Errno("fork","Couldn't fork gzip");
+
+ // The child
+ if (Process == 0)
+ {
+ dup2(From.Fd(),STDIN_FILENO);
+ dup2(Pkg.Fd(),STDOUT_FILENO);
+ SetCloseExec(STDIN_FILENO,false);
+ SetCloseExec(STDOUT_FILENO,false);
+
+ const char *Args[3];
+ string Tmp = _config->Find("Dir::bin::gzip","gzip");
+ Args[0] = Tmp.c_str();
+ Args[1] = "-d";
+ Args[2] = 0;
+ execvp(Args[0],(char **)Args);
+ exit(100);
+ }
+
+ // Wait for gzip to finish
+ if (ExecWait(Process,_config->Find("Dir::bin::gzip","gzip").c_str(),false) == false)
+ return _error->Error("gzip failed, perhaps the disk is full.");
+
+ Pkg.Seek(0);
+ }
+ pkgTagFile Parser(&Pkg);
+ if (_error->PendingError() == true)
+ return false;
+
+ // Open the output file
+ char S[400];
+ snprintf(S,sizeof(S),"cdrom:[%s]/%s",Name.c_str(),
+ (*I).c_str() + CDROM.length());
+ string TargetF = _config->FindDir("Dir::State::lists") + "partial/";
+ TargetF += URItoFileName(S);
+ if (_config->FindB("APT::CDROM::NoAct",false) == true)
+ TargetF = "/dev/null";
+ FileFd Target(TargetF,FileFd::WriteAtomic);
+ FILE *TargetFl = fdopen(dup(Target.Fd()),"w");
+ if (_error->PendingError() == true)
+ return false;
+ if (TargetFl == 0)
+ return _error->Errno("fdopen","Failed to reopen fd");
+
+ // Setup the progress meter
+ if(Progress)
+ Progress->OverallProgress(CurrentSize,TotalSize,FileSize,
+ string("Reading Translation Indexes"));
+
+ // Parse
+ if(Progress)
+ Progress->SubProgress(Pkg.Size());
+ pkgTagSection Section;
+ this->Section = &Section;
+ string Prefix;
+ unsigned long Hits = 0;
+ unsigned long Chop = 0;
+ while (Parser.Step(Section) == true)
+ {
+ if(Progress)
+ Progress->Progress(Parser.Offset());
+
+ const char *Start;
+ const char *Stop;
+ Section.GetSection(Start,Stop);
+ fwrite(Start,Stop-Start, 1, TargetFl);
+ fputc('\n',TargetFl);
+
+ Packages++;
+ Hits++;
+ }
+ fclose(TargetFl);
+
+ if (Debug == true)
+ cout << " Processed by using Prefix '" << Prefix << "' and chop " << Chop << endl;
+
+ if (_config->FindB("APT::CDROM::NoAct",false) == false)
+ {
+ // Move out of the partial directory
+ Target.Close();
+ string FinalF = _config->FindDir("Dir::State::lists");
+ FinalF += URItoFileName(S);
+ if (rename(TargetF.c_str(),FinalF.c_str()) != 0)
+ return _error->Errno("rename","Failed to rename");
+ }
+
+
+ CurrentSize += FileSize;
+ }
+ if(Progress)
+ Progress->Done();
+
+ // Some stats
+ if(log) {
+ stringstream msg;
+ if(NotFound == 0 && WrongSize == 0)
+ ioprintf(msg, _("Wrote %i records.\n"), Packages);
+ else if (NotFound != 0 && WrongSize == 0)
+ ioprintf(msg, _("Wrote %i records with %i missing files.\n"),
+ Packages, NotFound);
+ else if (NotFound == 0 && WrongSize != 0)
+ ioprintf(msg, _("Wrote %i records with %i mismatched files\n"),
+ Packages, WrongSize);
+ if (NotFound != 0 && WrongSize != 0)
+ ioprintf(msg, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages, NotFound, WrongSize);
+ }
+
+ if (Packages == 0)
+ _error->Warning("No valid records were found.");
+
+ if (NotFound + WrongSize > 10)
+ _error->Warning("A lot of entries were discarded, something may be wrong.\n");
+
+
+ return true;
+}
+ /*}}}*/