X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/e8d72d2faa67283a2c7e691c1a7a440c4cdd179f..1e67a553e86625052b140e9868c3b51759a71b1f:/apt-pkg/indexcopy.cc diff --git a/apt-pkg/indexcopy.cc b/apt-pkg/indexcopy.cc index b1e75e30f..84f9fd420 100644 --- a/apt-pkg/indexcopy.cc +++ b/apt-pkg/indexcopy.cc @@ -10,7 +10,7 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ -#include "indexcopy.h" +#include #include #include @@ -21,19 +21,99 @@ #include #include #include -#include #include #include #include #include +#include +#include #include +#include + +#include "indexcopy.h" +#include /*}}}*/ using namespace std; - - +// DecompressFile - wrapper for decompressing gzip/bzip2/xz compressed files /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool DecompressFile(string Filename, int *fd, off_t *FileSize) +{ + string CompressProg; + string CompressProgFind; + FileFd From; + struct stat Buf; + *fd = -1; + + if (stat((Filename + ".gz").c_str(), &Buf) == 0) + { + CompressProg = "gzip"; + CompressProgFind = "Dir::bin::gzip"; + From.Open(Filename + ".gz",FileFd::ReadOnly); + } + else if (stat((Filename + ".bz2").c_str(), &Buf) == 0) + { + CompressProg = "bzip2"; + CompressProgFind = "Dir::bin::bzip2"; + From.Open(Filename + ".bz2",FileFd::ReadOnly); + } + else if (stat((Filename + ".xz").c_str(), &Buf) == 0) + { + CompressProg = "xz"; + CompressProgFind = "Dir::bin::xz"; + From.Open(Filename + ".xz",FileFd::ReadOnly); + } + else + { + return _error->Errno("decompressor", "Unable to parse file"); + } + + if (_error->PendingError() == true) + return -1; + + *FileSize = Buf.st_size; + + // Get a temp file + FILE *tmp = tmpfile(); + if (tmp == 0) + return _error->Errno("tmpfile","Unable to create a tmp file"); + *fd = dup(fileno(tmp)); + fclose(tmp); + + // Fork decompressor + pid_t Process = fork(); + if (Process < 0) + return _error->Errno("fork","Couldn't fork to run decompressor"); + + // The child + if (Process == 0) + { + dup2(From.Fd(),STDIN_FILENO); + dup2(*fd,STDOUT_FILENO); + SetCloseExec(STDIN_FILENO,false); + SetCloseExec(STDOUT_FILENO,false); + + const char *Args[3]; + string Tmp = _config->Find(CompressProgFind, CompressProg); + Args[0] = Tmp.c_str(); + Args[1] = "-d"; + Args[2] = 0; + if(execvp(Args[0],(char **)Args)) + return(_error->Errno("decompressor","decompress failed")); + /* Should never get here */ + exit(100); + } + + // Wait for decompress to finish + if (ExecWait(Process,CompressProg.c_str(),false) == false) + return false; + + return true; +} + /*}}}*/ // IndexCopy::CopyPackages - Copy the package files from the CD /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -41,7 +121,7 @@ bool IndexCopy::CopyPackages(string CDROM,string Name,vector &List, pkgCdromStatus *log) { OpProgress *Progress = NULL; - if (List.size() == 0) + if (List.empty() == true) return true; if(log) @@ -51,75 +131,45 @@ bool IndexCopy::CopyPackages(string CDROM,string Name,vector &List, bool Debug = _config->FindB("Debug::aptcdrom",false); // Prepare the progress indicator - unsigned long TotalSize = 0; - for (vector::iterator I = List.begin(); I != List.end(); I++) + off_t TotalSize = 0; + for (vector::iterator I = List.begin(); I != List.end(); ++I) { struct stat Buf; if (stat(string(*I + GetFileName()).c_str(),&Buf) != 0 && - stat(string(*I + GetFileName() + ".gz").c_str(),&Buf) != 0) + stat(string(*I + GetFileName() + ".gz").c_str(),&Buf) != 0 && + stat(string(*I + GetFileName() + ".xz").c_str(),&Buf) != 0 && + stat(string(*I + GetFileName() + ".bz2").c_str(),&Buf) != 0) return _error->Errno("stat","Stat failed for %s", string(*I + GetFileName()).c_str()); TotalSize += Buf.st_size; } - unsigned long CurrentSize = 0; + off_t CurrentSize = 0; unsigned int NotFound = 0; unsigned int WrongSize = 0; unsigned int Packages = 0; - for (vector::iterator I = List.begin(); I != List.end(); I++) + for (vector::iterator I = List.begin(); I != List.end(); ++I) { string OrigPath = string(*I,CDROM.length()); - unsigned long FileSize = 0; + off_t FileSize = 0; // Open the package file FileFd Pkg; - if (FileExists(*I + GetFileName()) == true) + if (RealFileExists(*I + GetFileName()) == true) { Pkg.Open(*I + GetFileName(),FileFd::ReadOnly); FileSize = Pkg.Size(); } else { - FileFd From(*I + GetFileName() + ".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."); - + int fd; + if (!DecompressFile(string(*I + GetFileName()), &fd, &FileSize)) + return _error->Errno("decompress","Decompress failed for %s", + string(*I + GetFileName()).c_str()); + Pkg.Fd(dup(fd)); Pkg.Seek(0); } + pkgTagFile Parser(&Pkg); if (_error->PendingError() == true) return false; @@ -130,9 +180,14 @@ bool IndexCopy::CopyPackages(string CDROM,string Name,vector &List, (*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; @@ -157,7 +212,7 @@ bool IndexCopy::CopyPackages(string CDROM,string Name,vector &List, if(Progress) Progress->Progress(Parser.Offset()); string File; - unsigned long Size; + unsigned long long Size; if (GetFile(File,Size) == false) { fclose(TargetFl); @@ -212,7 +267,7 @@ bool IndexCopy::CopyPackages(string CDROM,string Name,vector &List, } // Size match - if ((unsigned)Buf.st_size != Size) + if ((unsigned long long)Buf.st_size != Size) { if (Debug == true) clog << "Wrong Size: " << File << endl; @@ -275,7 +330,7 @@ bool IndexCopy::CopyPackages(string CDROM,string Name,vector &List, _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; @@ -446,7 +501,7 @@ bool IndexCopy::GrabFirst(string Path,string &To,unsigned int Depth) // PackageCopy::GetFile - Get the file information from the section /*{{{*/ // --------------------------------------------------------------------- /* */ -bool PackageCopy::GetFile(string &File,unsigned long &Size) +bool PackageCopy::GetFile(string &File,unsigned long long &Size) { File = Section->FindS("Filename"); Size = Section->FindI("Size"); @@ -472,7 +527,7 @@ bool PackageCopy::RewriteEntry(FILE *Target,string File) // SourceCopy::GetFile - Get the file information from the section /*{{{*/ // --------------------------------------------------------------------- /* */ -bool SourceCopy::GetFile(string &File,unsigned long &Size) +bool SourceCopy::GetFile(string &File,unsigned long long &Size) { string Files = Section->FindS("Files"); if (Files.empty() == true) @@ -495,7 +550,7 @@ bool SourceCopy::GetFile(string &File,unsigned long &Size) return _error->Error("Error parsing file record"); // Parse the size and append the directory - Size = atoi(sSize.c_str()); + Size = strtoull(sSize.c_str(), NULL, 10); File = Base + File; return true; } @@ -525,21 +580,21 @@ bool SigVerify::Verify(string prefix, string file, indexRecords *MetaIndex) // 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)) + if(!RealFileExists(prefix+file)) { - _error->Warning("Skipping non-exisiting file %s", string(prefix+file).c_str()); + _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; } if (!Record->Hash.VerifyFile(prefix+file)) { - _error->Warning("Hash mismatch for: %s",file.c_str()); + _error->Warning(_("Hash mismatch for: %s"),file.c_str()); return false; } @@ -551,8 +606,8 @@ bool SigVerify::Verify(string prefix, string file, indexRecords *MetaIndex) return true; } - -bool SigVerify::CopyMetaIndex(string CDROM, string CDName, + /*}}}*/ +bool SigVerify::CopyMetaIndex(string CDROM, string CDName, /*{{{*/ string prefix, string file) { char S[400]; @@ -563,7 +618,7 @@ bool SigVerify::CopyMetaIndex(string CDROM, string CDName, 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; @@ -572,17 +627,17 @@ bool SigVerify::CopyMetaIndex(string CDROM, string CDName, return true; } - -bool SigVerify::CopyAndVerify(string CDROM,string Name,vector &SigList, + /*}}}*/ +bool SigVerify::CopyAndVerify(string CDROM,string Name,vector &SigList, /*{{{*/ vector PkgList,vector SrcList) { - if (SigList.size() == 0) + if (SigList.empty() == true) return true; bool Debug = _config->FindB("Debug::aptcdrom",false); // Read all Release files - for (vector::iterator I = SigList.begin(); I != SigList.end(); I++) + for (vector::iterator I = SigList.begin(); I != SigList.end(); ++I) { if(Debug) cout << "Signature verify for: " << *I << endl; @@ -590,63 +645,34 @@ bool SigVerify::CopyAndVerify(string CDROM,string Name,vector &SigList, 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(RealFileExists(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("%s",MetaIndex->ErrorText.c_str()); return false; @@ -655,11 +681,12 @@ bool SigVerify::CopyAndVerify(string CDROM,string Name,vector &SigList, // go over the Indexfiles and see if they verify // if so, remove them from our copy of the lists vector keys = MetaIndex->MetaKeys(); - for (vector::iterator I = keys.begin(); I != keys.end(); I++) + for (vector::iterator I = keys.begin(); I != keys.end(); ++I) { if(!Verify(prefix,*I, MetaIndex)) { // something went wrong, don't copy the Release.gpg // FIXME: delete any existing gpg file? + _error->Discard(); continue; } } @@ -674,13 +701,130 @@ bool SigVerify::CopyAndVerify(string CDROM,string Name,vector &SigList, 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]) +{ + if (File == FileGPG) + { + #define SIGMSG "-----BEGIN PGP SIGNED MESSAGE-----\n" + char buffer[sizeof(SIGMSG)]; + FILE* gpg = fopen(File.c_str(), "r"); + if (gpg == NULL) + return _error->Errno("RunGPGV", _("Could not open file %s"), File.c_str()); + char const * const test = fgets(buffer, sizeof(buffer), gpg); + fclose(gpg); + if (test == NULL || strcmp(buffer, SIGMSG) != 0) + return _error->Error(_("File %s doesn't start with a clearsigned message"), File.c_str()); + #undef SIGMSG + } -bool TranslationsCopy::CopyTranslations(string CDROM,string Name,vector &List, - pkgCdromStatus *log) + 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 keyrings; + if (DirectoryExists(trustedPath)) + keyrings = GetListOfFilesInDir(trustedPath, "gpg", false, true); + if (RealFileExists(trustedFile) == true) + keyrings.push_back(trustedFile); + + std::vector Args; + Args.reserve(30); + + if (keyrings.empty() == true) + { + // TRANSLATOR: %s is the trusted keyring parts directory + return _error->Error(_("No keyring installed in %s."), + _config->FindDir("Dir::Etc::TrustedParts").c_str()); + } + + 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::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()); + if (FileGPG != File) + Args.push_back(File.c_str()); + Args.push_back(NULL); + + if (Debug == true) + { + std::clog << "Preparing to exec: " << gpgvpath; + for (std::vector::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 &List, pkgCdromStatus *log) { OpProgress *Progress = NULL; - if (List.size() == 0) + if (List.empty() == true) return true; if(log) @@ -689,73 +833,42 @@ bool TranslationsCopy::CopyTranslations(string CDROM,string Name,vector bool Debug = _config->FindB("Debug::aptcdrom",false); // Prepare the progress indicator - unsigned long TotalSize = 0; - for (vector::iterator I = List.begin(); I != List.end(); I++) + off_t TotalSize = 0; + for (vector::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) + stat(string(*I + ".gz").c_str(),&Buf) != 0 && + stat(string(*I + ".bz2").c_str(),&Buf) != 0 && + stat(string(*I + ".xz").c_str(),&Buf) != 0) return _error->Errno("stat","Stat failed for %s", string(*I).c_str()); TotalSize += Buf.st_size; } - unsigned long CurrentSize = 0; + off_t CurrentSize = 0; unsigned int NotFound = 0; unsigned int WrongSize = 0; unsigned int Packages = 0; - for (vector::iterator I = List.begin(); I != List.end(); I++) + for (vector::iterator I = List.begin(); I != List.end(); ++I) { string OrigPath = string(*I,CDROM.length()); - unsigned long FileSize = 0; + off_t FileSize = 0; // Open the package file FileFd Pkg; - if (FileExists(*I) == true) + if (RealFileExists(*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."); - + int fd; + if (!DecompressFile(*I, &fd, &FileSize)) + return _error->Errno("decompress","Decompress failed for %s", (*I).c_str()); + Pkg.Fd(dup(fd)); Pkg.Seek(0); } pkgTagFile Parser(&Pkg); @@ -770,7 +883,7 @@ bool TranslationsCopy::CopyTranslations(string CDROM,string Name,vector TargetF += URItoFileName(S); if (_config->FindB("APT::CDROM::NoAct",false) == true) TargetF = "/dev/null"; - FileFd Target(TargetF,FileFd::WriteEmpty); + FileFd Target(TargetF,FileFd::WriteAtomic); FILE *TargetFl = fdopen(dup(Target.Fd()),"w"); if (_error->PendingError() == true) return false; @@ -789,7 +902,6 @@ bool TranslationsCopy::CopyTranslations(string CDROM,string Name,vector this->Section = &Section; string Prefix; unsigned long Hits = 0; - unsigned long Chop = 0; while (Parser.Step(Section) == true) { if(Progress) @@ -807,7 +919,7 @@ bool TranslationsCopy::CopyTranslations(string CDROM,string Name,vector fclose(TargetFl); if (Debug == true) - cout << " Processed by using Prefix '" << Prefix << "' and chop " << Chop << endl; + cout << " Processed by using Prefix '" << Prefix << "' and chop " << endl; if (_config->FindB("APT::CDROM::NoAct",false) == false) { @@ -844,8 +956,9 @@ bool TranslationsCopy::CopyTranslations(string CDROM,string Name,vector _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; } + /*}}}*/