summaryrefslogtreecommitdiff
path: root/cmdline
diff options
context:
space:
mode:
Diffstat (limited to 'cmdline')
-rw-r--r--cmdline/apt-cache.cc149
-rw-r--r--cmdline/apt-dump-solver.cc2
-rw-r--r--cmdline/apt-extracttemplates.cc17
-rw-r--r--cmdline/apt-extracttemplates.h3
-rw-r--r--cmdline/apt-get.cc245
-rw-r--r--cmdline/apt-helper.cc10
-rw-r--r--cmdline/apt-internal-solver.cc7
-rw-r--r--cmdline/apt-key.in407
8 files changed, 532 insertions, 308 deletions
diff --git a/cmdline/apt-cache.cc b/cmdline/apt-cache.cc
index ac0d48a36..0f4f7e1ce 100644
--- a/cmdline/apt-cache.cc
+++ b/cmdline/apt-cache.cc
@@ -191,7 +191,7 @@ static bool UnMet(CommandLine &CmdL)
{
CacheSetHelperVirtuals helper(true, GlobalError::NOTICE);
APT::VersionList verset = APT::VersionList::FromCommandLine(CacheFile, CmdL.FileList + 1,
- APT::VersionList::CANDIDATE, helper);
+ APT::CacheSetHelper::CANDIDATE, helper);
for (APT::VersionList::iterator V = verset.begin(); V != verset.end(); ++V)
if (ShowUnMet(V, Important) == false)
return false;
@@ -264,6 +264,46 @@ static bool DumpPackage(CommandLine &CmdL)
return true;
}
/*}}}*/
+// ShowHashTableStats - Show stats about a hashtable /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+template<class T>
+static void ShowHashTableStats(std::string Type,
+ T *StartP,
+ map_pointer_t *Hashtable,
+ unsigned long Size)
+{
+ // hashtable stats for the HashTable
+ unsigned long NumBuckets = Size;
+ unsigned long UsedBuckets = 0;
+ unsigned long UnusedBuckets = 0;
+ unsigned long LongestBucket = 0;
+ unsigned long ShortestBucket = NumBuckets;
+ unsigned long Entries = 0;
+ for (unsigned int i=0; i < NumBuckets; ++i)
+ {
+ T *P = StartP + Hashtable[i];
+ if(P == 0 || P == StartP)
+ {
+ ++UnusedBuckets;
+ continue;
+ }
+ ++UsedBuckets;
+ unsigned long ThisBucketSize = 0;
+ for (; P != StartP; P = StartP + P->Next)
+ ++ThisBucketSize;
+ Entries += ThisBucketSize;
+ LongestBucket = std::max(ThisBucketSize, LongestBucket);
+ ShortestBucket = std::min(ThisBucketSize, ShortestBucket);
+ }
+ cout << "Total buckets in " << Type << ": " << NumBuckets << std::endl;
+ cout << " Unused: " << UnusedBuckets << std::endl;
+ cout << " Used: " << UsedBuckets << std::endl;
+ cout << " Average entries: " << Entries/(double)NumBuckets << std::endl;
+ cout << " Longest: " << LongestBucket << std::endl;
+ cout << " Shortest: " << ShortestBucket << std::endl;
+}
+ /*}}}*/
// Stats - Dump some nice statistics /*{{{*/
// ---------------------------------------------------------------------
/* */
@@ -335,45 +375,84 @@ static bool Stats(CommandLine &)
SizeToStr(Cache->Head().DescFileCount*Cache->Head().DescFileSz) << ')' << endl;
cout << _("Total Provides mappings: ") << Cache->Head().ProvidesCount << " (" <<
SizeToStr(Cache->Head().ProvidesCount*Cache->Head().ProvidesSz) << ')' << endl;
-
- // String list stats
- unsigned long Size = 0;
- unsigned long Count = 0;
- for (pkgCache::StringItem *I = Cache->StringItemP + Cache->Head().StringList;
- I!= Cache->StringItemP; I = Cache->StringItemP + I->NextItem)
- {
- Count++;
- Size += strlen(Cache->StrP + I->String) + 1;
- }
- cout << _("Total globbed strings: ") << Count << " (" << SizeToStr(Size) << ')' << endl;
- unsigned long DepVerSize = 0;
+ // String list stats
+ std::set<map_stringitem_t> stritems;
+ for (pkgCache::GrpIterator G = Cache->GrpBegin(); G.end() == false; ++G)
+ stritems.insert(G->Name);
for (pkgCache::PkgIterator P = Cache->PkgBegin(); P.end() == false; ++P)
{
+ stritems.insert(P->Arch);
for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
{
+ if (V->VerStr != 0)
+ stritems.insert(V->VerStr);
+ if (V->Section != 0)
+ stritems.insert(V->Section);
+ stritems.insert(V->SourcePkgName);
+ stritems.insert(V->SourceVerStr);
for (pkgCache::DepIterator D = V.DependsList(); D.end() == false; ++D)
{
if (D->Version != 0)
- DepVerSize += strlen(D.TargetVer()) + 1;
+ stritems.insert(D->Version);
}
+ for (pkgCache::DescIterator D = V.DescriptionList(); D.end() == false; ++D)
+ {
+ stritems.insert(D->md5sum);
+ stritems.insert(D->language_code);
+ }
+ }
+ for (pkgCache::PrvIterator Prv = P.ProvidesList(); Prv.end() == false; ++Prv)
+ {
+ if (Prv->ProvideVersion != 0)
+ stritems.insert(Prv->ProvideVersion);
}
}
- cout << _("Total dependency version space: ") << SizeToStr(DepVerSize) << endl;
-
+ for (pkgCache::PkgFileIterator F = Cache->FileBegin(); F != Cache->FileEnd(); ++F)
+ {
+ stritems.insert(F->FileName);
+ stritems.insert(F->Archive);
+ stritems.insert(F->Codename);
+ stritems.insert(F->Component);
+ stritems.insert(F->Version);
+ stritems.insert(F->Origin);
+ stritems.insert(F->Label);
+ stritems.insert(F->Architecture);
+ stritems.insert(F->Site);
+ stritems.insert(F->IndexType);
+ }
+ unsigned long Size = 0;
+ for (std::set<map_stringitem_t>::const_iterator i = stritems.begin(); i != stritems.end(); ++i)
+ Size += strlen(Cache->StrP + *i) + 1;
+
+ cout << _("Total globbed strings: ") << stritems.size() << " (" << SizeToStr(Size) << ')' << endl;
+ stritems.clear();
+
unsigned long Slack = 0;
for (int I = 0; I != 7; I++)
Slack += Cache->Head().Pools[I].ItemSize*Cache->Head().Pools[I].Count;
cout << _("Total slack space: ") << SizeToStr(Slack) << endl;
-
+
unsigned long Total = 0;
- Total = Slack + Size + Cache->Head().DependsCount*Cache->Head().DependencySz +
- Cache->Head().VersionCount*Cache->Head().VersionSz +
- Cache->Head().PackageCount*Cache->Head().PackageSz +
- Cache->Head().VerFileCount*Cache->Head().VerFileSz +
- Cache->Head().ProvidesCount*Cache->Head().ProvidesSz;
+#define APT_CACHESIZE(X,Y) (Cache->Head().X * Cache->Head().Y)
+ Total = Slack + Size +
+ APT_CACHESIZE(GroupCount, GroupSz) +
+ APT_CACHESIZE(PackageCount, PackageSz) +
+ APT_CACHESIZE(VersionCount, VersionSz) +
+ APT_CACHESIZE(DescriptionCount, DescriptionSz) +
+ APT_CACHESIZE(DependsCount, DependencySz) +
+ APT_CACHESIZE(PackageFileCount, PackageFileSz) +
+ APT_CACHESIZE(VerFileCount, VerFileSz) +
+ APT_CACHESIZE(DescFileCount, DescFileSz) +
+ APT_CACHESIZE(ProvidesCount, ProvidesSz) +
+ (2 * Cache->Head().HashTableSize * sizeof(map_id_t));
cout << _("Total space accounted for: ") << SizeToStr(Total) << endl;
-
+#undef APT_CACHESIZE
+
+ // hashtable stats
+ ShowHashTableStats<pkgCache::Package>("PkgHashTable", Cache->PkgP, Cache->Head().PkgHashTable(), Cache->Head().HashTableSize);
+ ShowHashTableStats<pkgCache::Group>("GrpHashTable", Cache->GrpP, Cache->Head().GrpHashTable(), Cache->Head().HashTableSize);
+
return true;
}
/*}}}*/
@@ -579,7 +658,7 @@ static bool ShowDepends(CommandLine &CmdL, bool const RevDepends)
return false;
CacheSetHelperVirtuals helper(false);
- APT::VersionList verset = APT::VersionList::FromCommandLine(CacheFile, CmdL.FileList + 1, APT::VersionList::CANDIDATE, helper);
+ APT::VersionList verset = APT::VersionList::FromCommandLine(CacheFile, CmdL.FileList + 1, APT::CacheSetHelper::CANDIDATE, helper);
if (verset.empty() == true && helper.virtualPkgs.empty() == true)
return _error->Error(_("No packages found"));
std::vector<bool> Shown(Cache->Head().PackageCount);
@@ -647,7 +726,7 @@ static bool ShowDepends(CommandLine &CmdL, bool const RevDepends)
if (Recurse == true && Shown[Trg->ID] == false)
{
Shown[Trg->ID] = true;
- verset.insert(APT::VersionSet::FromPackage(CacheFile, Trg, APT::VersionSet::CANDIDATE, helper));
+ verset.insert(APT::VersionSet::FromPackage(CacheFile, Trg, APT::CacheSetHelper::CANDIDATE, helper));
}
}
@@ -666,7 +745,7 @@ static bool ShowDepends(CommandLine &CmdL, bool const RevDepends)
if (Recurse == true && Shown[V.ParentPkg()->ID] == false)
{
Shown[V.ParentPkg()->ID] = true;
- verset.insert(APT::VersionSet::FromPackage(CacheFile, V.ParentPkg(), APT::VersionSet::CANDIDATE, helper));
+ verset.insert(APT::VersionSet::FromPackage(CacheFile, V.ParentPkg(), APT::CacheSetHelper::CANDIDATE, helper));
}
}
@@ -761,9 +840,9 @@ static bool XVcg(CommandLine &CmdL)
// Load the list of packages from the command line into the show list
APT::CacheSetHelper helper(true, GlobalError::NOTICE);
- std::list<APT::PackageSet::Modifier> mods;
- mods.push_back(APT::PackageSet::Modifier(0, ",", APT::PackageSet::Modifier::POSTFIX));
- mods.push_back(APT::PackageSet::Modifier(1, "^", APT::PackageSet::Modifier::POSTFIX));
+ std::list<APT::CacheSetHelper::PkgModifier> mods;
+ mods.push_back(APT::CacheSetHelper::PkgModifier(0, ",", APT::PackageSet::Modifier::POSTFIX));
+ mods.push_back(APT::CacheSetHelper::PkgModifier(1, "^", APT::PackageSet::Modifier::POSTFIX));
std::map<unsigned short, APT::PackageSet> pkgsets =
APT::PackageSet::GroupedFromCommandLine(CacheFile, CmdL.FileList + 1, mods, 0, helper);
@@ -973,9 +1052,9 @@ static bool Dotty(CommandLine &CmdL)
// Load the list of packages from the command line into the show list
APT::CacheSetHelper helper(true, GlobalError::NOTICE);
- std::list<APT::PackageSet::Modifier> mods;
- mods.push_back(APT::PackageSet::Modifier(0, ",", APT::PackageSet::Modifier::POSTFIX));
- mods.push_back(APT::PackageSet::Modifier(1, "^", APT::PackageSet::Modifier::POSTFIX));
+ std::list<APT::CacheSetHelper::PkgModifier> mods;
+ mods.push_back(APT::CacheSetHelper::PkgModifier(0, ",", APT::PackageSet::Modifier::POSTFIX));
+ mods.push_back(APT::CacheSetHelper::PkgModifier(1, "^", APT::PackageSet::Modifier::POSTFIX));
std::map<unsigned short, APT::PackageSet> pkgsets =
APT::PackageSet::GroupedFromCommandLine(CacheFile, CmdL.FileList + 1, mods, 0, helper);
@@ -1231,7 +1310,7 @@ static bool DisplayRecord(pkgCacheFile &CacheFile, pkgCache::VerIterator V)
struct ExDescFile
{
pkgCache::DescFile *Df;
- map_ptrloc ID;
+ map_id_t ID;
};
// Search - Perform a search /*{{{*/
@@ -1429,8 +1508,8 @@ static bool ShowPackage(CommandLine &CmdL)
{
pkgCacheFile CacheFile;
CacheSetHelperVirtuals helper(true, GlobalError::NOTICE);
- APT::VersionList::Version const select = _config->FindB("APT::Cache::AllVersions", true) ?
- APT::VersionList::ALL : APT::VersionList::CANDIDATE;
+ APT::CacheSetHelper::VerSelector const select = _config->FindB("APT::Cache::AllVersions", true) ?
+ APT::CacheSetHelper::ALL : APT::CacheSetHelper::CANDIDATE;
APT::VersionList const verset = APT::VersionList::FromCommandLine(CacheFile, CmdL.FileList + 1, select, helper);
for (APT::VersionList::const_iterator Ver = verset.begin(); Ver != verset.end(); ++Ver)
if (DisplayRecord(CacheFile, Ver) == false)
diff --git a/cmdline/apt-dump-solver.cc b/cmdline/apt-dump-solver.cc
index 04e13bde9..f765234c5 100644
--- a/cmdline/apt-dump-solver.cc
+++ b/cmdline/apt-dump-solver.cc
@@ -40,6 +40,8 @@ int main(int argc,const char *argv[]) /*{{{*/
ShowHelp();
return 0;
}
+ // we really don't need anything
+ DropPrivileges();
FILE* input = fdopen(STDIN_FILENO, "r");
FILE* output = fopen("/tmp/dump.edsp", "w");
diff --git a/cmdline/apt-extracttemplates.cc b/cmdline/apt-extracttemplates.cc
index e4428e051..f95b9e5ba 100644
--- a/cmdline/apt-extracttemplates.cc
+++ b/cmdline/apt-extracttemplates.cc
@@ -52,7 +52,7 @@ pkgCache *DebFile::Cache = 0;
// ---------------------------------------------------------------------
/* */
DebFile::DebFile(const char *debfile)
- : File(debfile, FileFd::ReadOnly), Size(0), Control(NULL), ControlLen(0),
+ : File(debfile, FileFd::ReadOnly), Control(NULL), ControlLen(0),
DepOp(0), PreDepOp(0), Config(0), Template(0), Which(None)
{
}
@@ -103,10 +103,12 @@ bool DebFile::DoItem(Item &I, int &Fd)
if (strcmp(I.Name, "control") == 0)
{
delete [] Control;
- Control = new char[I.Size+1];
- Control[I.Size] = 0;
+ Control = new char[I.Size+3];
+ Control[I.Size] = '\n';
+ Control[I.Size + 1] = '\n';
+ Control[I.Size + 2] = '\0';
Which = IsControl;
- ControlLen = I.Size;
+ ControlLen = I.Size + 3;
// make it call the Process method below. this is so evil
Fd = -2;
}
@@ -138,7 +140,7 @@ bool DebFile::DoItem(Item &I, int &Fd)
// ---------------------------------------------------------------------
/* */
bool DebFile::Process(Item &/*I*/, const unsigned char *data,
- unsigned long size, unsigned long pos)
+ unsigned long long size, unsigned long long pos)
{
switch (Which)
{
@@ -162,9 +164,10 @@ bool DebFile::Process(Item &/*I*/, const unsigned char *data,
bool DebFile::ParseInfo()
{
if (Control == NULL) return false;
-
+
pkgTagSection Section;
- Section.Scan(Control, ControlLen);
+ if (Section.Scan(Control, ControlLen) == false)
+ return false;
Package = Section.FindS("Package");
Version = GetInstalledVer(Package);
diff --git a/cmdline/apt-extracttemplates.h b/cmdline/apt-extracttemplates.h
index 9cc3f5f25..829cdae75 100644
--- a/cmdline/apt-extracttemplates.h
+++ b/cmdline/apt-extracttemplates.h
@@ -20,7 +20,6 @@ class pkgCache;
class DebFile : public pkgDirStream
{
FileFd File;
- unsigned long Size;
char *Control;
unsigned long ControlLen;
@@ -29,7 +28,7 @@ public:
~DebFile();
bool DoItem(Item &I, int &fd);
bool Process(pkgDirStream::Item &I, const unsigned char *data,
- unsigned long size, unsigned long pos);
+ unsigned long long size, unsigned long long pos);
bool Go();
bool ParseInfo();
diff --git a/cmdline/apt-get.cc b/cmdline/apt-get.cc
index cfa79339b..b6786faf8 100644
--- a/cmdline/apt-get.cc
+++ b/cmdline/apt-get.cc
@@ -78,10 +78,11 @@
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
-#include <sys/statfs.h>
-#include <sys/statvfs.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+
#include <algorithm>
#include <fstream>
#include <iostream>
@@ -195,7 +196,7 @@ static std::string GetReleaseForSourceRecord(pkgSourceList *SrcList,
// FindSrc - Find a source record /*{{{*/
// ---------------------------------------------------------------------
/* */
-static pkgSrcRecords::Parser *FindSrc(const char *Name,pkgRecords &Recs,
+static pkgSrcRecords::Parser *FindSrc(const char *Name,
pkgSrcRecords &SrcRecs,string &Src,
CacheFile &CacheFile)
{
@@ -303,16 +304,10 @@ static pkgSrcRecords::Parser *FindSrc(const char *Name,pkgRecords &Recs,
(VF.File().Archive() != 0 && VF.File().Archive() == RelTag) ||
(VF.File().Codename() != 0 && VF.File().Codename() == RelTag))
{
- pkgRecords::Parser &Parse = Recs.Lookup(VF);
- Src = Parse.SourcePkg();
- // no SourcePkg name, so it is the "binary" name
- if (Src.empty() == true)
- Src = TmpSrc;
+ Src = Ver.SourcePkgName();
// the Version we have is possibly fuzzy or includes binUploads,
- // so we use the Version of the SourcePkg (empty if same as package)
- VerTag = Parse.SourceVer();
- if (VerTag.empty() == true)
- VerTag = Ver.VerStr();
+ // so we use the Version of the SourcePkg
+ VerTag = Ver.SourceVerStr();
break;
}
}
@@ -343,10 +338,10 @@ static pkgSrcRecords::Parser *FindSrc(const char *Name,pkgRecords &Recs,
pkgCache::VerIterator Ver = Cache->GetCandidateVer(Pkg);
if (Ver.end() == false)
{
- pkgRecords::Parser &Parse = Recs.Lookup(Ver.FileList());
- Src = Parse.SourcePkg();
- if (VerTag.empty() == true)
- VerTag = Parse.SourceVer();
+ if (strcmp(Ver.SourcePkgName(),Ver.ParentPkg().Name()) != 0)
+ Src = Ver.SourcePkgName();
+ if (VerTag.empty() == true && strcmp(Ver.SourceVerStr(),Ver.VerStr()) != 0)
+ VerTag = Ver.SourceVerStr();
}
}
}
@@ -540,7 +535,7 @@ static bool DoDSelectUpgrade(CommandLine &)
}
// Now upgrade everything
- if (pkgAllUpgrade(Cache) == false)
+ if (APT::Upgrade::Upgrade(Cache, APT::Upgrade::FORBID_REMOVE_PACKAGES | APT::Upgrade::FORBID_INSTALL_NEW_PACKAGES) == false)
{
ShowBroken(c1out,Cache,false);
return _error->Error(_("Internal error, problem resolver broke stuff"));
@@ -555,30 +550,44 @@ static bool DoDSelectUpgrade(CommandLine &)
static bool DoClean(CommandLine &)
{
std::string const archivedir = _config->FindDir("Dir::Cache::archives");
- std::string const pkgcache = _config->FindFile("Dir::cache::pkgcache");
- std::string const srcpkgcache = _config->FindFile("Dir::cache::srcpkgcache");
+ std::string const listsdir = _config->FindDir("Dir::state::lists");
if (_config->FindB("APT::Get::Simulate") == true)
{
+ std::string const pkgcache = _config->FindFile("Dir::cache::pkgcache");
+ std::string const srcpkgcache = _config->FindFile("Dir::cache::srcpkgcache");
cout << "Del " << archivedir << "* " << archivedir << "partial/*"<< endl
+ << "Del " << listsdir << "partial/*" << endl
<< "Del " << pkgcache << " " << srcpkgcache << endl;
return true;
}
-
+
+ bool const NoLocking = _config->FindB("Debug::NoLocking",false);
// Lock the archive directory
FileFd Lock;
- if (_config->FindB("Debug::NoLocking",false) == false)
+ if (NoLocking == false)
{
int lock_fd = GetLock(archivedir + "lock");
if (lock_fd < 0)
- return _error->Error(_("Unable to lock the download directory"));
+ return _error->Error(_("Unable to lock directory %s"), archivedir.c_str());
Lock.Fd(lock_fd);
}
-
+
pkgAcquire Fetcher;
Fetcher.Clean(archivedir);
Fetcher.Clean(archivedir + "partial/");
+ if (NoLocking == false)
+ {
+ Lock.Close();
+ int lock_fd = GetLock(listsdir + "lock");
+ if (lock_fd < 0)
+ return _error->Error(_("Unable to lock directory %s"), listsdir.c_str());
+ Lock.Fd(lock_fd);
+ }
+
+ Fetcher.Clean(listsdir + "partial/");
+
pkgCacheFile::RemoveCaches();
return true;
@@ -632,15 +641,13 @@ static bool DoDownload(CommandLine &CmdL)
APT::CacheSetHelper helper(c0out);
APT::VersionSet verset = APT::VersionSet::FromCommandLine(Cache,
- CmdL.FileList + 1, APT::VersionSet::CANDIDATE, helper);
+ CmdL.FileList + 1, APT::CacheSetHelper::CANDIDATE, helper);
if (verset.empty() == true)
return false;
AcqTextStatus Stat(ScreenWidth, _config->FindI("quiet", 0));
- pkgAcquire Fetcher;
- if (Fetcher.Setup(&Stat) == false)
- return false;
+ pkgAcquire Fetcher(&Stat);
pkgRecords Recs(Cache);
pkgSourceList *SrcList = Cache.GetSourceList();
@@ -655,6 +662,8 @@ static bool DoDownload(CommandLine &CmdL)
Ver != verset.end(); ++Ver, ++i)
{
pkgAcquire::Item *I = new pkgAcqArchive(&Fetcher, SrcList, &Recs, *Ver, storefile[i]);
+ if (storefile[i].empty())
+ continue;
std::string const filename = cwd + flNotDir(storefile[i]);
storefile[i].assign(filename);
I->DestFile.assign(filename);
@@ -670,6 +679,9 @@ static bool DoDownload(CommandLine &CmdL)
return true;
}
+ // Disable drop-privs if "_apt" can not write to the target dir
+ CheckDropPrivsMustBeDisabled(Fetcher);
+
if (_error->PendingError() == true || CheckAuth(Fetcher, false) == false)
return false;
@@ -731,15 +743,13 @@ static bool DoSource(CommandLine &CmdL)
pkgSourceList *List = Cache.GetSourceList();
// Create the text record parsers
- pkgRecords Recs(Cache);
pkgSrcRecords SrcRecs(*List);
if (_error->PendingError() == true)
return false;
// Create the download object
- AcqTextStatus Stat(ScreenWidth,_config->FindI("quiet",0));
- pkgAcquire Fetcher;
- Fetcher.SetLog(&Stat);
+ AcqTextStatus Stat(ScreenWidth,_config->FindI("quiet",0));
+ pkgAcquire Fetcher(&Stat);
SPtrArray<DscFile> Dsc = new DscFile[CmdL.FileSize()];
@@ -760,7 +770,7 @@ static bool DoSource(CommandLine &CmdL)
for (const char **I = CmdL.FileList + 1; *I != 0; I++, J++)
{
string Src;
- pkgSrcRecords::Parser *Last = FindSrc(*I,Recs,SrcRecs,Src,Cache);
+ pkgSrcRecords::Parser *Last = FindSrc(*I,SrcRecs,Src,Cache);
if (Last == 0) {
return _error->Error(_("Unable to find a source package for %s"),Src.c_str());
@@ -832,58 +842,36 @@ static bool DoSource(CommandLine &CmdL)
queued.insert(Last->Index().ArchiveURI(I->Path));
// check if we have a file with that md5 sum already localy
- if(!I->MD5Hash.empty() && FileExists(flNotDir(I->Path)))
- {
- FileFd Fd(flNotDir(I->Path), FileFd::ReadOnly);
- MD5Summation sum;
- sum.AddFD(Fd.Fd(), Fd.Size());
- Fd.Close();
- if((string)sum.Result() == I->MD5Hash)
+ std::string localFile = flNotDir(I->Path);
+ if (FileExists(localFile) == true)
+ if(I->Hashes.VerifyFile(localFile) == true)
{
ioprintf(c1out,_("Skipping already downloaded file '%s'\n"),
- flNotDir(I->Path).c_str());
+ localFile.c_str());
continue;
}
+
+ // see if we have a hash (Acquire::ForceHash is the only way to have none)
+ if (I->Hashes.usable() == false && _config->FindB("APT::Get::AllowUnauthenticated",false) == false)
+ {
+ ioprintf(c1out, "Skipping download of file '%s' as requested hashsum is not available for authentication\n",
+ localFile.c_str());
+ continue;
}
new pkgAcqFile(&Fetcher,Last->Index().ArchiveURI(I->Path),
- I->MD5Hash,I->Size,
- Last->Index().SourceInfo(*Last,*I),Src);
+ I->Hashes, I->Size, Last->Index().SourceInfo(*Last,*I), Src);
}
}
- // check authentication status of the source as well
- if (UntrustedList != "" && !AuthPrompt(UntrustedList, false))
- return false;
-
// Display statistics
unsigned long long FetchBytes = Fetcher.FetchNeeded();
unsigned long long FetchPBytes = Fetcher.PartialPresent();
unsigned long long DebBytes = Fetcher.TotalNeeded();
- // Check for enough free space
- struct statvfs Buf;
- string OutputDir = ".";
- if (statvfs(OutputDir.c_str(),&Buf) != 0) {
- if (errno == EOVERFLOW)
- return _error->WarningE("statvfs",_("Couldn't determine free space in %s"),
- OutputDir.c_str());
- else
- return _error->Errno("statvfs",_("Couldn't determine free space in %s"),
- OutputDir.c_str());
- } else if (unsigned(Buf.f_bfree) < (FetchBytes - FetchPBytes)/Buf.f_bsize)
- {
- struct statfs Stat;
- if (statfs(OutputDir.c_str(),&Stat) != 0
-#if HAVE_STRUCT_STATFS_F_TYPE
- || unsigned(Stat.f_type) != RAMFS_MAGIC
-#endif
- ) {
- return _error->Error(_("You don't have enough free space in %s"),
- OutputDir.c_str());
- }
- }
-
+ if (CheckFreeSpaceBeforeDownload(".", (FetchBytes - FetchPBytes)) == false)
+ return false;
+
// Number of bytes
if (DebBytes != FetchBytes)
//TRANSLATOR: The required space between number and unit is already included
@@ -902,7 +890,7 @@ static bool DoSource(CommandLine &CmdL)
ioprintf(cout,_("Fetch source %s\n"),Dsc[I].Package.c_str());
return true;
}
-
+
// Just print out the uris an exit if the --print-uris flag was used
if (_config->FindB("APT::Get::Print-URIs") == true)
{
@@ -913,6 +901,13 @@ static bool DoSource(CommandLine &CmdL)
return true;
}
+ // Disable drop-privs if "_apt" can not write to the target dir
+ CheckDropPrivsMustBeDisabled(Fetcher);
+
+ // check authentication status of the source as well
+ if (UntrustedList != "" && !AuthPrompt(UntrustedList, false))
+ return false;
+
// Run it
bool Failed = false;
if (AcquireRun(Fetcher, 0, &Failed, NULL) == false || Failed == true)
@@ -1035,17 +1030,10 @@ static bool DoBuildDep(CommandLine &CmdL)
pkgSourceList *List = Cache.GetSourceList();
// Create the text record parsers
- pkgRecords Recs(Cache);
pkgSrcRecords SrcRecs(*List);
if (_error->PendingError() == true)
return false;
- // Create the download object
- AcqTextStatus Stat(ScreenWidth,_config->FindI("quiet",0));
- pkgAcquire Fetcher;
- if (Fetcher.Setup(&Stat) == false)
- return false;
-
bool StripMultiArch;
string hostArch = _config->Find("APT::Get::Host-Architecture");
if (hostArch.empty() == false)
@@ -1062,7 +1050,35 @@ static bool DoBuildDep(CommandLine &CmdL)
for (const char **I = CmdL.FileList + 1; *I != 0; I++, J++)
{
string Src;
- pkgSrcRecords::Parser *Last = FindSrc(*I,Recs,SrcRecs,Src,Cache);
+ pkgSrcRecords::Parser *Last = 0;
+
+ // an unpacked debian source tree
+ using APT::String::Startswith;
+ if ((Startswith(*I, "./") || Startswith(*I, "/")) &&
+ DirectoryExists(*I))
+ {
+ ioprintf(c1out, _("Note, using directory '%s' to get the build dependencies\n"), *I);
+ // FIXME: how can we make this more elegant?
+ std::string TypeName = "debian/control File Source Index";
+ pkgIndexFile::Type *Type = pkgIndexFile::Type::GetType(TypeName.c_str());
+ if(Type != NULL)
+ Last = Type->CreateSrcPkgParser(*I);
+ }
+ // if its a local file (e.g. .dsc) use this
+ else if (FileExists(*I))
+ {
+ ioprintf(c1out, _("Note, using file '%s' to get the build dependencies\n"), *I);
+
+ // see if we can get a parser for this pkgIndexFile type
+ string TypeName = flExtension(*I) + " File Source Index";
+ pkgIndexFile::Type *Type = pkgIndexFile::Type::GetType(TypeName.c_str());
+ if(Type != NULL)
+ Last = Type->CreateSrcPkgParser(*I);
+ } else {
+ // normal case, search the cache for the source file
+ Last = FindSrc(*I,SrcRecs,Src,Cache);
+ }
+
if (Last == 0)
return _error->Error(_("Unable to find a source package for %s"),Src.c_str());
@@ -1080,7 +1096,7 @@ static bool DoBuildDep(CommandLine &CmdL)
}
else if (Last->BuildDepends(BuildDeps, _config->FindB("APT::Get::Arch-Only", false), StripMultiArch) == false)
return _error->Error(_("Unable to get build-dependency information for %s"),Src.c_str());
-
+
// Also ensure that build-essential packages are present
Configuration::Item const *Opts = _config->Tree("APT::Build-Essential");
if (Opts)
@@ -1411,21 +1427,15 @@ static bool DoBuildDep(CommandLine &CmdL)
* pool/ next to the deb itself)
* Example return: "pool/main/a/apt/apt_0.8.8ubuntu3"
*/
-static string GetChangelogPath(CacheFile &Cache,
- pkgCache::PkgIterator Pkg,
+static string GetChangelogPath(CacheFile &Cache,
pkgCache::VerIterator Ver)
{
- string path;
-
pkgRecords Recs(Cache);
pkgRecords::Parser &rec=Recs.Lookup(Ver.FileList());
- string srcpkg = rec.SourcePkg().empty() ? Pkg.Name() : rec.SourcePkg();
- string ver = Ver.VerStr();
- // if there is a source version it always wins
- if (rec.SourceVer() != "")
- ver = rec.SourceVer();
- path = flNotFile(rec.FileName());
- path += srcpkg + "_" + StripEpoch(ver);
+ string path = flNotFile(rec.FileName());
+ path.append(Ver.SourcePkgName());
+ path.append("_");
+ path.append(StripEpoch(Ver.SourceVerStr()));
return path;
}
/*}}}*/
@@ -1439,7 +1449,6 @@ static string GetChangelogPath(CacheFile &Cache,
* http://packages.medibuntu.org/pool/non-free/m/mplayer/mplayer_1.0~rc4~try1.dsfg1-1ubuntu1+medibuntu1.changelog
*/
static bool GuessThirdPartyChangelogUri(CacheFile &Cache,
- pkgCache::PkgIterator Pkg,
pkgCache::VerIterator Ver,
string &out_uri)
{
@@ -1454,7 +1463,7 @@ static bool GuessThirdPartyChangelogUri(CacheFile &Cache,
return false;
// get archive uri for the binary deb
- string path_without_dot_changelog = GetChangelogPath(Cache, Pkg, Ver);
+ string path_without_dot_changelog = GetChangelogPath(Cache, Ver);
out_uri = index->ArchiveURI(path_without_dot_changelog + ".changelog");
// now strip away the filename and add srcpkg_srcver.changelog
@@ -1472,44 +1481,45 @@ static bool DownloadChangelog(CacheFile &CacheFile, pkgAcquire &Fetcher,
* GuessThirdPartyChangelogUri for details how)
*/
{
- string path;
- string descr;
- string server;
- string changelog_uri;
-
- // data structures we need
- pkgCache::PkgIterator Pkg = Ver.ParentPkg();
-
// make the server root configurable
- server = _config->Find("Apt::Changelogs::Server",
+ string const server = _config->Find("Apt::Changelogs::Server",
"http://packages.debian.org/changelogs");
- path = GetChangelogPath(CacheFile, Pkg, Ver);
- strprintf(changelog_uri, "%s/%s/changelog", server.c_str(), path.c_str());
+ string const path = GetChangelogPath(CacheFile, Ver);
+ string changelog_uri;
+ if (APT::String::Endswith(server, "/") == true)
+ strprintf(changelog_uri, "%s%s/changelog", server.c_str(), path.c_str());
+ else
+ strprintf(changelog_uri, "%s/%s/changelog", server.c_str(), path.c_str());
if (_config->FindB("APT::Get::Print-URIs", false) == true)
{
std::cout << '\'' << changelog_uri << '\'' << std::endl;
return true;
}
+ pkgCache::PkgIterator const Pkg = Ver.ParentPkg();
+ string descr;
strprintf(descr, _("Changelog for %s (%s)"), Pkg.Name(), changelog_uri.c_str());
// queue it
- new pkgAcqFile(&Fetcher, changelog_uri, "", 0, descr, Pkg.Name(), "ignored", targetfile);
+ pkgAcquire::Item const * itm = new pkgAcqFile(&Fetcher, changelog_uri, "", 0, descr, Pkg.Name(), "ignored", targetfile);
+
+ // Disable drop-privs if "_apt" can not write to the target dir
+ CheckDropPrivsMustBeDisabled(Fetcher);
// try downloading it, if that fails, try third-party-changelogs location
// FIXME: Fetcher.Run() is "Continue" even if I get a 404?!?
Fetcher.Run();
- if (!FileExists(targetfile))
+ if (itm->Status != pkgAcquire::Item::StatDone)
{
string third_party_uri;
- if (GuessThirdPartyChangelogUri(CacheFile, Pkg, Ver, third_party_uri))
+ if (GuessThirdPartyChangelogUri(CacheFile, Ver, third_party_uri))
{
strprintf(descr, _("Changelog for %s (%s)"), Pkg.Name(), third_party_uri.c_str());
- new pkgAcqFile(&Fetcher, third_party_uri, "", 0, descr, Pkg.Name(), "ignored", targetfile);
+ itm = new pkgAcqFile(&Fetcher, third_party_uri, "", 0, descr, Pkg.Name(), "ignored", targetfile);
Fetcher.Run();
}
}
- if (FileExists(targetfile))
+ if (itm->Status == pkgAcquire::Item::StatDone)
return true;
// error
@@ -1526,7 +1536,7 @@ static bool DoChangelog(CommandLine &CmdL)
APT::CacheSetHelper helper(c0out);
APT::VersionList verset = APT::VersionList::FromCommandLine(Cache,
- CmdL.FileList + 1, APT::VersionList::CANDIDATE, helper);
+ CmdL.FileList + 1, APT::CacheSetHelper::CANDIDATE, helper);
if (verset.empty() == true)
return false;
pkgAcquire Fetcher;
@@ -1541,7 +1551,7 @@ static bool DoChangelog(CommandLine &CmdL)
}
AcqTextStatus Stat(ScreenWidth, _config->FindI("quiet",0));
- Fetcher.Setup(&Stat);
+ Fetcher.SetLog(&Stat);
bool const downOnly = _config->FindB("APT::Get::Download-Only", false);
@@ -1555,6 +1565,19 @@ static bool DoChangelog(CommandLine &CmdL)
tmpdir = mkdtemp(tmpname);
if (tmpdir == NULL)
return _error->Errno("mkdtemp", "mkdtemp failed");
+
+ std::string const SandboxUser = _config->Find("APT::Sandbox::User");
+ if (getuid() == 0 && SandboxUser.empty() == false) // if we aren't root, we can't chown, so don't try it
+ {
+ struct passwd const * const pw = getpwnam(SandboxUser.c_str());
+ struct group const * const gr = getgrnam("root");
+ if (pw != NULL && gr != NULL)
+ {
+ // chown the tmp dir directory we use to the sandbox user
+ if(chown(tmpdir, pw->pw_uid, gr->gr_gid) != 0)
+ _error->WarningE("DoChangelog", "chown to %s:%s of directory %s failed", SandboxUser.c_str(), "root", tmpdir);
+ }
+ }
}
for (APT::VersionList::const_iterator Ver = verset.begin();
@@ -1570,7 +1593,7 @@ static bool DoChangelog(CommandLine &CmdL)
{
DisplayFileInPager(changelogfile);
// cleanup temp file
- unlink(changelogfile.c_str());
+ unlink(changelogfile.c_str());
}
}
// clenaup tmp dir
diff --git a/cmdline/apt-helper.cc b/cmdline/apt-helper.cc
index dd43ea1bc..27abb2013 100644
--- a/cmdline/apt-helper.cc
+++ b/cmdline/apt-helper.cc
@@ -48,10 +48,9 @@ static bool DoDownloadFile(CommandLine &CmdL)
if (CmdL.FileSize() <= 2)
return _error->Error(_("Must specify at least one pair url/filename"));
-
- pkgAcquire Fetcher;
AcqTextStatus Stat(ScreenWidth, _config->FindI("quiet",0));
- Fetcher.Setup(&Stat);
+ pkgAcquire Fetcher(&Stat);
+
std::string download_uri = CmdL.FileList[1];
std::string targetfile = CmdL.FileList[2];
std::string hash;
@@ -60,7 +59,10 @@ static bool DoDownloadFile(CommandLine &CmdL)
// we use download_uri as descr and targetfile as short-descr
new pkgAcqFile(&Fetcher, download_uri, hash, 0, download_uri, targetfile,
"dest-dir-ignored", targetfile);
- Fetcher.Run();
+
+ // Disable drop-privs if "_apt" can not write to the target dir
+ CheckDropPrivsMustBeDisabled(Fetcher);
+
bool Failed = false;
if (AcquireRun(Fetcher, 0, &Failed, NULL) == false || Failed == true ||
FileExists(targetfile) == false)
diff --git a/cmdline/apt-internal-solver.cc b/cmdline/apt-internal-solver.cc
index 5fda7b6a0..92a4429e5 100644
--- a/cmdline/apt-internal-solver.cc
+++ b/cmdline/apt-internal-solver.cc
@@ -76,6 +76,9 @@ int main(int argc,const char *argv[]) /*{{{*/
{'o',"option",0,CommandLine::ArbItem},
{0,0,0,0}};
+ // we really don't need anything
+ DropPrivileges();
+
CommandLine CmdL(Args,_config);
if (pkgInitConfig(*_config) == false ||
CmdL.Parse(argc,argv) == false) {
@@ -172,10 +175,10 @@ int main(int argc,const char *argv[]) /*{{{*/
std::string failure;
if (upgrade == true) {
- if (pkgAllUpgrade(CacheFile) == false)
+ if (APT::Upgrade::Upgrade(CacheFile, APT::Upgrade::FORBID_REMOVE_PACKAGES | APT::Upgrade::FORBID_INSTALL_NEW_PACKAGES) == false)
failure = "ERR_UNSOLVABLE_UPGRADE";
} else if (distUpgrade == true) {
- if (pkgDistUpgrade(CacheFile) == false)
+ if (APT::Upgrade::Upgrade(CacheFile, APT::Upgrade::ALLOW_EVERYTHING) == false)
failure = "ERR_UNSOLVABLE_DIST_UPGRADE";
} else if (Fix.Resolve() == false)
failure = "ERR_UNSOLVABLE";
diff --git a/cmdline/apt-key.in b/cmdline/apt-key.in
index 0774cf4b7..cf0b9a96f 100644
--- a/cmdline/apt-key.in
+++ b/cmdline/apt-key.in
@@ -3,29 +3,6 @@
set -e
unset GREP_OPTIONS
-GPG_CMD="gpg --ignore-time-conflict --no-options --no-default-keyring"
-
-# gpg needs (in different versions more or less) files to function correctly,
-# so we give it its own homedir and generate some valid content for it
-GPGHOMEDIR="$(mktemp -d)"
-CURRENTTRAP="${CURRENTTRAP} rm -rf '${GPGHOMEDIR}';"
-trap "${CURRENTTRAP}" 0 HUP INT QUIT ILL ABRT FPE SEGV PIPE TERM
-chmod 700 "$GPGHOMEDIR"
-# We don't use a secret keyring, of course, but gpg panics and
-# implodes if there isn't one available - and writeable for imports
-SECRETKEYRING="${GPGHOMEDIR}/secring.gpg"
-touch $SECRETKEYRING
-GPG_CMD="$GPG_CMD --homedir $GPGHOMEDIR"
-# create the trustdb with an (empty) dummy keyring
-# older gpgs required it, newer gpgs even warn that it isn't needed,
-# but require it nonetheless for some commands, so we just play safe
-# here for the foreseeable future and create a dummy one
-$GPG_CMD --quiet --check-trustdb --keyring $SECRETKEYRING >/dev/null 2>&1
-# tell gpg that it shouldn't try to maintain a trustdb file
-GPG_CMD="$GPG_CMD --no-auto-check-trustdb --trust-model always"
-
-GPG="$GPG_CMD"
-
APT_DIR="/"
eval $(apt-config shell APT_DIR Dir)
@@ -37,22 +14,26 @@ REMOVED_KEYS='&keyring-removed-filename;'
eval $(apt-config shell REMOVED_KEYS APT::Key::RemovedKeys)
ARCHIVE_KEYRING_URI='&keyring-uri;'
eval $(apt-config shell ARCHIVE_KEYRING_URI APT::Key::ArchiveKeyringURI)
-TMP_KEYRING=${APT_DIR}/var/lib/apt/keyrings/maybe-import-keyring.gpg
+
+aptkey_echo() { echo "$@"; }
requires_root() {
if [ "$(id -u)" -ne 0 ]; then
- echo >&1 "ERROR: This command can only be used by root."
+ echo >&2 "ERROR: This command can only be used by root."
exit 1
fi
}
-# gpg defaults to mode 0600 for new keyrings. Create one with 0644 instead.
-init_keyring() {
- for path; do
- if ! [ -e "$path" ]; then
- touch -- "$path"
- chmod 0644 -- "$path"
- fi
+get_fingerprints_of_keyring() {
+ $GPG_CMD --keyring "$1" --with-colons --fingerprint | while read publine; do
+ # search for a public key
+ if [ "${publine%%:*}" != 'pub' ]; then continue; fi
+ # search for the associated fingerprint (should be the very next line)
+ while read fprline; do
+ if [ "${fprline%%:*}" = 'sub' ]; then break; # should never happen
+ elif [ "${fprline%%:*}" != 'fpr' ]; then continue; fi
+ echo "$fprline" | cut -d':' -f 10
+ done
done
}
@@ -61,11 +42,11 @@ add_keys_with_verify_against_master_keyring() {
MASTER=$2
if [ ! -f "$ADD_KEYRING" ]; then
- echo "ERROR: '$ADD_KEYRING' not found"
+ echo >&2 "ERROR: '$ADD_KEYRING' not found"
return
- fi
+ fi
if [ ! -f "$MASTER" ]; then
- echo "ERROR: '$MASTER' not found"
+ echo >&2 "ERROR: '$MASTER' not found"
return
fi
@@ -73,7 +54,7 @@ add_keys_with_verify_against_master_keyring() {
# is honored. so:
# all keys that are exported must have a valid signature
# from a key in the $distro-master-keyring
- add_keys=`$GPG_CMD --keyring $ADD_KEYRING --with-colons --list-keys | grep ^pub | cut -d: -f5`
+ add_keys="$(get_fingerprints_of_keyring "$ADD_KEYRING")"
all_add_keys=`$GPG_CMD --keyring $ADD_KEYRING --with-colons --list-keys | grep ^[ps]ub | cut -d: -f5`
master_keys=`$GPG_CMD --keyring $MASTER --with-colons --list-keys | grep ^pub | cut -d: -f5`
@@ -86,24 +67,28 @@ add_keys_with_verify_against_master_keyring() {
fi
done
done
-
+
for add_key in $add_keys; do
# export the add keyring one-by-one
- rm -f $TMP_KEYRING
- $GPG_CMD --keyring $ADD_KEYRING --output $TMP_KEYRING --export $add_key
- # check if signed with the master key and only add in this case
- ADDED=0
+ local TMP_KEYRING="${GPGHOMEDIR}/tmp-keyring.gpg"
+ $GPG_CMD --batch --yes --keyring "$ADD_KEYRING" --output "$TMP_KEYRING" --export "$add_key"
+ if ! $GPG_CMD --batch --yes --keyring "$TMP_KEYRING" --import "$MASTER" > "${GPGHOMEDIR}/gpgoutput.log" 2>&1; then
+ cat "${GPGHOMEDIR}/gpgoutput.log"
+ false
+ fi
+ # check if signed with the master key and only add in this case
+ ADDED=0
for master_key in $master_keys; do
- if $GPG_CMD --keyring $MASTER --keyring $TMP_KEYRING --check-sigs --with-colons $add_key | grep '^sig:!:' | cut -d: -f5 | grep -q $master_key; then
- $GPG --import $TMP_KEYRING
+ if $GPG_CMD --keyring $TMP_KEYRING --check-sigs --with-colons $add_key | grep '^sig:!:' | cut -d: -f5 | grep -q $master_key; then
+ $GPG_CMD --batch --yes --keyring "$ADD_KEYRING" --export "$add_key" | $GPG --batch --yes --import
ADDED=1
fi
done
if [ $ADDED = 0 ]; then
echo >&2 "Key '$add_key' not added. It is not signed with a master key"
fi
+ rm -f "${TMP_KEYRING}"
done
- rm -f $TMP_KEYRING
}
# update the current archive signing keyring from a network URI
@@ -121,7 +106,6 @@ net_update() {
echo >&2 "ERROR: Your distribution is not supported in net-update as no uri for the archive-keyring is set"
exit 1
fi
- requires_root
# in theory we would need to depend on wget for this, but this feature
# isn't useable in debian anyway as we have no keyring uri nor a master key
if ! which wget >/dev/null 2>&1; then
@@ -142,7 +126,7 @@ net_update() {
fi
new_mtime=$(stat -c %Y $keyring)
if [ $new_mtime -ne $old_mtime ]; then
- echo "Checking for new archive signing keys now"
+ aptkey_echo "Checking for new archive signing keys now"
add_keys_with_verify_against_master_keyring $keyring $MASTER_KEYRING
fi
}
@@ -153,7 +137,6 @@ update() {
echo >&2 "Is the &keyring-package; package installed?"
exit 1
fi
- requires_root
# add new keys from the package;
@@ -166,71 +149,159 @@ update() {
if [ -r "$REMOVED_KEYS" ]; then
# remove no-longer supported/used keys
- keys=`$GPG_CMD --keyring $REMOVED_KEYS --with-colons --list-keys | grep ^pub | cut -d: -f5`
- for key in $keys; do
- if $GPG --list-keys --with-colons | grep ^pub | cut -d: -f5 | grep -q $key; then
- $GPG --quiet --batch --delete-key --yes ${key}
- fi
+ get_fingerprints_of_keyring "$REMOVED_KEYS" | while read key; do
+ foreach_keyring_do 'remove_key_from_keyring' "$key"
done
else
- echo "Warning: removed keys keyring $REMOVED_KEYS missing or not readable" >&2
+ echo >&2 "Warning: removed keys keyring $REMOVED_KEYS missing or not readable"
fi
}
remove_key_from_keyring() {
- local GPG="$GPG_CMD --keyring $1"
- # check if the key is in this keyring: the key id is in the 5 column at the end
- if ! $GPG --with-colons --list-keys 2>&1 | grep -q "^pub:[^:]*:[^:]*:[^:]*:[0-9A-F]\+$2:"; then
- return
- fi
- if [ ! -w "$1" ]; then
- echo >&2 "Key ${2} is in keyring ${1}, but can't be removed as it is read only."
- return
+ local KEYRINGFILE="$1"
+ shift
+ # non-existent keyrings have by definition no keys
+ if [ ! -e "$KEYRINGFILE" ]; then
+ return
fi
- # check if it is the only key in the keyring and if so remove the keyring altogether
- if [ '1' = "$($GPG --with-colons --list-keys | grep "^pub:[^:]*:[^:]*:[^:]*:[0-9A-F]\+:" | wc -l)" ]; then
- mv -f "$1" "${1}~" # behave like gpg
- return
- fi
- # we can't just modify pointed to files as these might be in /usr or something
- local REALTARGET
- if [ -L "$1" ]; then
- REALTARGET="$(readlink -f "$1")"
- mv -f "$1" "${1}.dpkg-tmp"
- cp -a "$REALTARGET" "$1"
- ls "$(dirname $1)"
- fi
- # delete the key from the keyring
- $GPG --batch --delete-key --yes "$2"
- if [ -n "$REALTARGET" ]; then
- # the real backup is the old link, not the copy we made
- mv -f "${1}.dpkg-tmp" "${1}~"
- fi
-}
-remove_key() {
- requires_root
+ local GPG="$GPG_CMD --keyring $KEYRINGFILE"
+ for KEY in "$@"; do
+ # check if the key is in this keyring: the key id is in the 5 column at the end
+ if ! get_fingerprints_of_keyring "$KEYRINGFILE" | grep -q "^[0-9A-F]*${KEY}$"; then
+ continue
+ fi
+ if [ ! -w "$KEYRINGFILE" ]; then
+ echo >&2 "Key ${KEY} is in keyring ${KEYRINGFILE}, but can't be removed as it is read only."
+ continue
+ fi
+ # check if it is the only key in the keyring and if so remove the keyring altogether
+ if [ '1' = "$(get_fingerprints_of_keyring "$KEYRINGFILE" | wc -l)" ]; then
+ mv -f "$KEYRINGFILE" "${KEYRINGFILE}~" # behave like gpg
+ return
+ fi
+ # we can't just modify pointed to files as these might be in /usr or something
+ local REALTARGET
+ if [ -L "$KEYRINGFILE" ]; then
+ REALTARGET="$(readlink -f "$KEYRINGFILE")"
+ mv -f "$KEYRINGFILE" "${KEYRINGFILE}.dpkg-tmp"
+ cp -a "$REALTARGET" "$KEYRINGFILE"
+ fi
+ # delete the key from the keyring
+ $GPG --batch --delete-key --yes "$KEY"
+ if [ -n "$REALTARGET" ]; then
+ # the real backup is the old link, not the copy we made
+ mv -f "${KEYRINGFILE}.dpkg-tmp" "${KEYRINGFILE}~"
+ fi
+ done
+}
- # if a --keyring was given, just remove from there
- if [ -n "$FORCED_KEYRING" ]; then
- remove_key_from_keyring "$FORCED_KEYRING" "$1"
- else
+foreach_keyring_do() {
+ local ACTION="$1"
+ shift
+ # if a --keyring was given, just remove from there
+ if [ -n "$FORCED_KEYRING" ]; then
+ $ACTION "$FORCED_KEYRING" "$@"
+ else
# otherwise all known keyrings are up for inspection
- local TRUSTEDFILE="/etc/apt/trusted.gpg"
- eval $(apt-config shell TRUSTEDFILE Apt::GPGV::TrustedKeyring)
- eval $(apt-config shell TRUSTEDFILE Dir::Etc::Trusted/f)
- remove_key_from_keyring "$TRUSTEDFILE" "$1"
- TRUSTEDPARTS="/etc/apt/trusted.gpg.d"
+ if [ -s "$TRUSTEDFILE" ]; then
+ $ACTION "$TRUSTEDFILE" "$@"
+ fi
+ local TRUSTEDPARTS="/etc/apt/trusted.gpg.d"
eval $(apt-config shell TRUSTEDPARTS Dir::Etc::TrustedParts/d)
if [ -d "$TRUSTEDPARTS" ]; then
+ # strip / suffix as gpg will double-slash in that case (#665411)
+ local STRIPPED_TRUSTEDPARTS="${TRUSTEDPARTS%/}"
+ if [ "${STRIPPED_TRUSTEDPARTS}/" = "$TRUSTEDPARTS" ]; then
+ TRUSTEDPARTS="$STRIPPED_TRUSTEDPARTS"
+ fi
for trusted in $(run-parts --list "$TRUSTEDPARTS" --regex '^.*\.gpg$'); do
- remove_key_from_keyring "$trusted" "$1"
+ if [ -s "$trusted" ]; then
+ $ACTION "$trusted" "$@"
+ fi
done
fi
+ fi
+}
+
+run_cmd_on_keyring() {
+ local KEYRINGFILE="$1"
+ shift
+ # fingerprint and co will fail if key isn't in this keyring
+ $GPG_CMD --keyring "$KEYRINGFILE" --batch "$@" 2>/dev/null || true
+}
+
+import_keys_from_keyring() {
+ local IMPORT="$1"
+ local KEYRINGFILE="$2"
+ if ! $GPG_CMD --keyring "$KEYRINGFILE" --batch --import "$IMPORT" > "${GPGHOMEDIR}/gpgoutput.log" 2>&1; then
+ cat "${GPGHOMEDIR}/gpgoutput.log"
+ false
+ fi
+}
+
+merge_keys_into_keyrings() {
+ local KEYRINGFILE="$1"
+ local IMPORT="$2"
+ if ! $GPG_CMD --keyring "$KEYRINGFILE" --batch --import --import-options 'merge-only' "$IMPORT" > "${GPGHOMEDIR}/gpgoutput.log" 2>&1; then
+ cat "${GPGHOMEDIR}/gpgoutput.log"
+ false
+ fi
+}
+
+merge_back_changes() {
+ if [ -n "$FORCED_KEYRING" ]; then
+ # if the keyring was forced merge is already done
+ return
+ fi
+ if [ -s "${GPGHOMEDIR}/pubring.gpg" ]; then
+ # merge all updated keys
+ foreach_keyring_do 'merge_keys_into_keyrings' "${GPGHOMEDIR}/pubring.gpg"
+ fi
+ # look for keys which were added or removed
+ get_fingerprints_of_keyring "${GPGHOMEDIR}/pubring.orig.gpg" > "${GPGHOMEDIR}/pubring.orig.keylst"
+ get_fingerprints_of_keyring "${GPGHOMEDIR}/pubring.gpg" > "${GPGHOMEDIR}/pubring.keylst"
+ sort "${GPGHOMEDIR}/pubring.keylst" "${GPGHOMEDIR}/pubring.orig.keylst" | uniq --unique | while read key; do
+ if grep -q "^${key}$" "${GPGHOMEDIR}/pubring.orig.keylst"; then
+ # key isn't part of new keyring, so remove
+ foreach_keyring_do 'remove_key_from_keyring' "$key"
+ elif grep -q "^${key}$" "${GPGHOMEDIR}/pubring.keylst"; then
+ # key is part of new keyring, so we need to import it
+ create_new_keyring "$TRUSTEDFILE"
+ if ! $GPG --batch --yes --export "$key" | $GPG_CMD --keyring "$TRUSTEDFILE" --batch --yes --import > "${GPGHOMEDIR}/gpgoutput.log" 2>&1; then
+ cat "${GPGHOMEDIR}/gpgoutput.log"
+ false
+ fi
+ else
+ echo >&2 "Errror: Key ${key} (dis)appeared out of nowhere"
+ fi
+ done
+}
+
+setup_merged_keyring() {
+ if [ -z "$FORCED_KEYRING" ]; then
+ foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/pubring.gpg"
+ if [ -r "${GPGHOMEDIR}/pubring.gpg" ]; then
+ cp -a "${GPGHOMEDIR}/pubring.gpg" "${GPGHOMEDIR}/pubring.orig.gpg"
+ else
+ touch "${GPGHOMEDIR}/pubring.gpg" "${GPGHOMEDIR}/pubring.orig.gpg"
+ fi
+ GPG="$GPG --keyring ${GPGHOMEDIR}/pubring.gpg"
+ else
+ GPG="$GPG --keyring $TRUSTEDFILE"
+ create_new_keyring "$TRUSTEDFILE"
fi
- echo "OK"
}
+create_new_keyring() {
+ # gpg defaults to mode 0600 for new keyrings. Create one with 0644 instead.
+ if ! [ -e "$TRUSTEDFILE" ]; then
+ if [ -w "$(dirname "$TRUSTEDFILE")" ]; then
+ touch -- "$TRUSTEDFILE"
+ chmod 0644 -- "$TRUSTEDFILE"
+ fi
+ fi
+}
usage() {
echo "Usage: apt-key [--keyring file] [command] [arguments]"
@@ -256,17 +327,19 @@ while [ -n "$1" ]; do
shift
TRUSTEDFILE="$1"
FORCED_KEYRING="$1"
- if [ -r "$TRUSTEDFILE" ] || [ "$2" = 'add' ] || [ "$2" = 'adv' ]; then
- GPG="$GPG --keyring $TRUSTEDFILE --primary-keyring $TRUSTEDFILE"
- else
- echo >&2 "Error: The specified keyring »$TRUSTEDFILE« is missing or not readable"
- exit 1
- fi
+ ;;
+ --secret-keyring)
shift
+ FORCED_SECRET_KEYRING="$1"
+ ;;
+ --readonly)
+ merge_back_changes() { true; }
;;
--fakeroot)
requires_root() { true; }
- shift
+ ;;
+ --quiet)
+ aptkey_echo() { true; }
;;
--*)
echo >&2 "Unknown option: $1"
@@ -275,28 +348,13 @@ while [ -n "$1" ]; do
*)
break;;
esac
+ shift
done
if [ -z "$TRUSTEDFILE" ]; then
TRUSTEDFILE="/etc/apt/trusted.gpg"
eval $(apt-config shell TRUSTEDFILE Apt::GPGV::TrustedKeyring)
eval $(apt-config shell TRUSTEDFILE Dir::Etc::Trusted/f)
- if [ -r "$TRUSTEDFILE" ]; then
- GPG="$GPG --keyring $TRUSTEDFILE"
- fi
- GPG="$GPG --primary-keyring $TRUSTEDFILE"
- TRUSTEDPARTS="/etc/apt/trusted.gpg.d"
- eval $(apt-config shell TRUSTEDPARTS Dir::Etc::TrustedParts/d)
- if [ -d "$TRUSTEDPARTS" ]; then
- # strip / suffix as gpg will double-slash in that case (#665411)
- STRIPPED_TRUSTEDPARTS="${TRUSTEDPARTS%/}"
- if [ "${STRIPPED_TRUSTEDPARTS}/" = "$TRUSTEDPARTS" ]; then
- TRUSTEDPARTS="$STRIPPED_TRUSTEDPARTS"
- fi
- for trusted in $(run-parts --list "$TRUSTEDPARTS" --regex '^.*\.gpg$'); do
- GPG="$GPG --keyring $trusted"
- done
- fi
fi
command="$1"
@@ -306,52 +364,107 @@ if [ -z "$command" ]; then
fi
shift
-if [ "$command" != "help" ] && ! which gpg >/dev/null 2>&1; then
- echo >&2 "Warning: gnupg does not seem to be installed."
- echo >&2 "Warning: apt-key requires gnupg for most operations."
- echo >&2
+if [ "$command" != "help" ]; then
+ eval $(apt-config shell GPG_EXE Apt::Key::gpgcommand)
+
+ if [ -n "$GPG_EXE" ] && which "$GPG_EXE" >/dev/null 2>&1; then
+ true
+ elif which gpg >/dev/null 2>&1; then
+ GPG_EXE="gpg"
+ elif which gpg2 >/dev/null 2>&1; then
+ GPG_EXE="gpg2"
+ else
+ echo >&2 "Error: gnupg or gnupg2 do not seem to be installed,"
+ echo >&2 "Error: but apt-key requires gnupg or gnupg2 for operation."
+ echo >&2
+ exit 255
+ fi
+
+ GPG_CMD="$GPG_EXE --ignore-time-conflict --no-options --no-default-keyring"
+
+ # gpg needs (in different versions more or less) files to function correctly,
+ # so we give it its own homedir and generate some valid content for it
+ if [ -n "$TMPDIR" ]; then
+ # tmpdir is a directory and current user has rwx access to it
+ # same tests as in apt-pkg/contrib/fileutl.cc GetTempDir()
+ if [ ! -d "$TMPDIR" ] || [ ! -r "$TMPDIR" ] || [ ! -w "$TMPDIR" ] || [ ! -x "$TMPDIR" ]; then
+ unset TMPDIR
+ fi
+ fi
+ GPGHOMEDIR="$(mktemp -d)"
+ CURRENTTRAP="${CURRENTTRAP} rm -rf '${GPGHOMEDIR}';"
+ trap "${CURRENTTRAP}" 0 HUP INT QUIT ILL ABRT FPE SEGV PIPE TERM
+ chmod 700 "$GPGHOMEDIR"
+ # We don't use a secret keyring, of course, but gpg panics and
+ # implodes if there isn't one available - and writeable for imports
+ SECRETKEYRING="${GPGHOMEDIR}/secring.gpg"
+ touch $SECRETKEYRING
+ GPG_CMD="$GPG_CMD --homedir $GPGHOMEDIR"
+ # create the trustdb with an (empty) dummy keyring
+ # older gpgs required it, newer gpgs even warn that it isn't needed,
+ # but require it nonetheless for some commands, so we just play safe
+ # here for the foreseeable future and create a dummy one
+ $GPG_CMD --quiet --check-trustdb --keyring $SECRETKEYRING >/dev/null 2>&1
+ # tell gpg that it shouldn't try to maintain a trustdb file
+ GPG_CMD="$GPG_CMD --no-auto-check-trustdb --trust-model always"
+ GPG="$GPG_CMD"
+
+ # for advanced operations, we might really need a secret keyring after all
+ if [ -n "$FORCED_SECRET_KEYRING" ] && [ -r "$FORCED_SECRET_KEYRING" ]; then
+ rm -f "$SECRETKEYRING"
+ cp -a "$FORCED_SECRET_KEYRING" "$SECRETKEYRING"
+ fi
fi
case "$command" in
add)
- requires_root
- init_keyring "$TRUSTEDFILE"
- $GPG --quiet --batch --import "$1"
- echo "OK"
+ requires_root
+ setup_merged_keyring
+ $GPG --quiet --batch --import "$@"
+ merge_back_changes
+ aptkey_echo "OK"
;;
del|rm|remove)
- init_keyring "$TRUSTEDFILE"
- remove_key "$1"
+ requires_root
+ foreach_keyring_do 'remove_key_from_keyring' "$@"
+ aptkey_echo "OK"
;;
update)
- init_keyring "$TRUSTEDFILE"
+ requires_root
+ setup_merged_keyring
update
+ merge_back_changes
;;
net-update)
- init_keyring "$TRUSTEDFILE"
+ requires_root
+ setup_merged_keyring
net_update
+ merge_back_changes
;;
list)
- init_keyring "$TRUSTEDFILE"
- $GPG --batch --list-keys
- ;;
+ foreach_keyring_do 'run_cmd_on_keyring' --list-keys "$@"
+ ;;
finger*)
- init_keyring "$TRUSTEDFILE"
- $GPG --batch --fingerprint
- ;;
- export)
- init_keyring "$TRUSTEDFILE"
- $GPG --armor --export "$1"
- ;;
- exportall)
- init_keyring "$TRUSTEDFILE"
- $GPG --armor --export
- ;;
+ foreach_keyring_do 'run_cmd_on_keyring' --fingerprint "$@"
+ ;;
+ export|exportall)
+ foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/pubring.gpg"
+ $GPG_CMD --keyring "${GPGHOMEDIR}/pubring.gpg" --armor --export "$@"
+ ;;
adv*)
- init_keyring "$TRUSTEDFILE"
- echo "Executing: $GPG $*"
- $GPG $*
- ;;
+ setup_merged_keyring
+ aptkey_echo "Executing: $GPG $*"
+ $GPG "$@"
+ merge_back_changes
+ ;;
+ verify)
+ setup_merged_keyring
+ if which gpgv >/dev/null 2>&1; then
+ gpgv --homedir "${GPGHOMEDIR}" --keyring "${GPGHOMEDIR}/pubring.gpg" --ignore-time-conflict "$@"
+ else
+ $GPG --verify "$@"
+ fi
+ ;;
help)
usage
;;