summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Vogt <mvo@debian.org>2014-05-07 18:10:52 +0200
committerMichael Vogt <mvo@debian.org>2014-05-07 18:10:52 +0200
commit00026265672f2663e0aa0ab1a3fa7333f718a643 (patch)
tree841fc7de4952b7ac0f535555ff8ddbce752b955e
parent38d2959ffb8c6f5f291b2910014a67b1b352ab4c (diff)
parentaaf677da5b62d3d0fdeb26f9b4c63fed544b63cd (diff)
Merge remote-tracking branch 'mvo/feature/apt-install-deb' into debian/experimental
-rw-r--r--apt-pkg/acquire-item.cc3
-rw-r--r--apt-pkg/contrib/fileutl.cc97
-rw-r--r--apt-pkg/contrib/fileutl.h18
-rw-r--r--apt-pkg/deb/debindexfile.cc107
-rw-r--r--apt-pkg/deb/debindexfile.h31
-rw-r--r--apt-pkg/deb/deblistparser.cc20
-rw-r--r--apt-pkg/deb/deblistparser.h15
-rw-r--r--apt-pkg/deb/debmetaindex.cc29
-rw-r--r--apt-pkg/deb/debmetaindex.h24
-rw-r--r--apt-pkg/deb/debrecords.h18
-rw-r--r--apt-pkg/sourcelist.h10
-rw-r--r--apt-private/private-cachefile.h23
-rw-r--r--apt-private/private-install.cc28
-rw-r--r--test/libapt/fileutl_test.cc58
14 files changed, 469 insertions, 12 deletions
diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc
index f8a652534..e851432e9 100644
--- a/apt-pkg/acquire-item.cc
+++ b/apt-pkg/acquire-item.cc
@@ -2095,7 +2095,8 @@ void pkgAcqArchive::Done(string Message,unsigned long long Size,string CalcHash,
}
// Check the hash
- if(ExpectedHash.toStr() != CalcHash)
+ // FIXME: could this empty() check impose *any* sort of security issue?
+ if(ExpectedHash.empty() == false && ExpectedHash.toStr() != CalcHash)
{
RenameOnError(HashSumMismatch);
return;
diff --git a/apt-pkg/contrib/fileutl.cc b/apt-pkg/contrib/fileutl.cc
index 5466feb4d..02b30dc1f 100644
--- a/apt-pkg/contrib/fileutl.cc
+++ b/apt-pkg/contrib/fileutl.cc
@@ -656,6 +656,22 @@ string flCombine(string Dir,string File)
return Dir + '/' + File;
}
/*}}}*/
+// flAbsPath - Return the absolute path of the filename /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+string flAbsPath(string File)
+{
+ char *p = realpath(File.c_str(), NULL);
+ if (p == NULL)
+ {
+ _error->Errno("realpath", "flAbsPath failed");
+ return "";
+ }
+ std::string AbsPath(p);
+ free(p);
+ return AbsPath;
+}
+ /*}}}*/
// SetCloseExec - Set the close on exec flag /*{{{*/
// ---------------------------------------------------------------------
/* */
@@ -1932,7 +1948,6 @@ bool FileFd::Close()
{
if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
-
if (d != NULL)
{
Res &= d->CloseDown(FileName);
@@ -2059,6 +2074,31 @@ std::string GetTempDir()
return string(tmpdir);
}
+FileFd* GetTempFile(std::string const &Prefix, bool ImmediateUnlink)
+{
+ char fn[512];
+ FileFd *Fd = new FileFd();
+
+ std::string tempdir = GetTempDir();
+ snprintf(fn, sizeof(fn), "%s/%s.XXXXXX",
+ tempdir.c_str(), Prefix.c_str());
+ int fd = mkstemp(fn);
+ if(ImmediateUnlink)
+ unlink(fn);
+ if (fd < 0)
+ {
+ _error->Errno("GetTempFile",_("Unable to mkstemp %s"), fn);
+ return NULL;
+ }
+ if (!Fd->OpenDescriptor(fd, FileFd::WriteOnly, FileFd::None, true))
+ {
+ _error->Errno("GetTempFile",_("Unable to write to %s"),fn);
+ return NULL;
+ }
+
+ return Fd;
+}
+
bool Rename(std::string From, std::string To)
{
if (rename(From.c_str(),To.c_str()) != 0)
@@ -2069,3 +2109,58 @@ bool Rename(std::string From, std::string To)
}
return true;
}
+
+bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode)
+{
+ int fd;
+ if (Mode != FileFd::ReadOnly && Mode != FileFd::WriteOnly)
+ return _error->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
+
+ int Pipe[2] = {-1, -1};
+ if(pipe(Pipe) != 0)
+ {
+ return _error->Errno("pipe", _("Failed to create subprocess IPC"));
+ return NULL;
+ }
+ std::set<int> keep_fds;
+ keep_fds.insert(Pipe[0]);
+ keep_fds.insert(Pipe[1]);
+ Child = ExecFork(keep_fds);
+ if(Child < 0)
+ return _error->Errno("fork", "Failed to fork");
+ if(Child == 0)
+ {
+ if(Mode == FileFd::ReadOnly)
+ {
+ close(Pipe[0]);
+ fd = Pipe[1];
+ }
+ else if(Mode == FileFd::WriteOnly)
+ {
+ close(Pipe[1]);
+ fd = Pipe[0];
+ }
+
+ if(Mode == FileFd::ReadOnly)
+ {
+ dup2(fd, 1);
+ dup2(fd, 2);
+ } else if(Mode == FileFd::WriteOnly)
+ dup2(fd, 0);
+
+ execv(Args[0], (char**)Args);
+ _exit(100);
+ }
+ if(Mode == FileFd::ReadOnly)
+ {
+ close(Pipe[1]);
+ fd = Pipe[0];
+ } else if(Mode == FileFd::WriteOnly)
+ {
+ close(Pipe[0]);
+ fd = Pipe[1];
+ }
+ Fd.OpenDescriptor(fd, Mode, FileFd::None, true);
+
+ return true;
+}
diff --git a/apt-pkg/contrib/fileutl.h b/apt-pkg/contrib/fileutl.h
index c9c80af39..0b4d94885 100644
--- a/apt-pkg/contrib/fileutl.h
+++ b/apt-pkg/contrib/fileutl.h
@@ -168,6 +168,8 @@ time_t GetModificationTime(std::string const &Path);
bool Rename(std::string From, std::string To);
std::string GetTempDir();
+FileFd* GetTempFile(std::string const &Prefix = "",
+ bool ImmediateUnlink = true);
/** \brief Ensure the existence of the given Path
*
@@ -201,7 +203,23 @@ std::string flNoLink(std::string File);
std::string flExtension(std::string File);
std::string flCombine(std::string Dir,std::string File);
+/** \brief Takes a file path and returns the absolute path
+ */
+std::string flAbsPath(std::string File);
+
// simple c++ glob
std::vector<std::string> Glob(std::string const &pattern, int flags=0);
+/** \brief Popen() implementation that execv() instead of using a shell
+ *
+ * \param Args the execv style command to run
+ * \param FileFd is a referenz to the FileFd to use for input or output
+ * \param Child a reference to the integer that stores the child pid
+ * Note that you must call ExecWait() or similar to cleanup
+ * \param Mode is either FileFd::ReadOnly or FileFd::WriteOnly
+ * \return true on success, false on failure with _error set
+ */
+bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode);
+
+
#endif
diff --git a/apt-pkg/deb/debindexfile.cc b/apt-pkg/deb/debindexfile.cc
index eee758b7a..86ef92bfb 100644
--- a/apt-pkg/deb/debindexfile.cc
+++ b/apt-pkg/deb/debindexfile.cc
@@ -30,6 +30,7 @@
#include <apt-pkg/pkgcachegen.h>
#include <apt-pkg/pkgrecords.h>
#include <apt-pkg/srcrecords.h>
+#include <apt-pkg/sptr.h>
#include <stdio.h>
#include <iostream>
@@ -667,6 +668,97 @@ APT_CONST bool debStatusIndex::Exists() const
}
/*}}}*/
+// debDebPkgFile - Single .deb file /*{{{*/
+// ---------------------------------------------------------------------
+debDebPkgFileIndex::debDebPkgFileIndex(std::string DebFile)
+ : pkgIndexFile(true), DebFile(DebFile)
+{
+ DebFileFullPath = flAbsPath(DebFile);
+}
+
+std::string debDebPkgFileIndex::ArchiveURI(std::string /*File*/) const
+{
+ return "file:" + DebFileFullPath;
+}
+
+bool debDebPkgFileIndex::Exists() const
+{
+ return FileExists(DebFile);
+}
+bool debDebPkgFileIndex::Merge(pkgCacheGenerator& Gen, OpProgress* Prog) const
+{
+ if(Prog)
+ Prog->SubProgress(0, "Reading deb file");
+
+ // get the control data out of the deb file vid dpkg -I
+ // ... can I haz libdpkg?
+ const char *Args[5] = {"/usr/bin/dpkg",
+ "-I",
+ DebFile.c_str(),
+ "control",
+ NULL};
+ FileFd PipeFd;
+ pid_t Child;
+ if(Popen(Args, PipeFd, Child, FileFd::ReadOnly) == false)
+ return _error->Error("Popen failed");
+ // FIXME: static buffer
+ char buf[8*1024];
+ unsigned long long n = 0;
+ if(PipeFd.Read(buf, sizeof(buf)-1, &n) == false)
+ return _error->Errno("read", "Failed to read dpkg pipe");
+ ExecWait(Child, "Popen");
+
+ // now write the control data to a tempfile
+ SPtr<FileFd> DebControl = GetTempFile("deb-file-" + DebFile);
+ if(DebControl == NULL)
+ return false;
+ DebControl->Write(buf, n);
+ // append size of the file
+ FileFd Fd(DebFile, FileFd::ReadOnly);
+ string Size;
+ strprintf(Size, "Size: %llu\n", Fd.Size());
+ DebControl->Write(Size.c_str(), Size.size());
+ // and rewind for the listparser
+ DebControl->Seek(0);
+
+ // and give it to the list parser
+ debDebFileParser Parser(DebControl, DebFile);
+ if(Gen.SelectFile(DebFile, "local", *this) == false)
+ return _error->Error("Problem with SelectFile %s", DebFile.c_str());
+
+ pkgCache::PkgFileIterator File = Gen.GetCurFile();
+ File->Size = DebControl->Size();
+ File->mtime = DebControl->ModificationTime();
+
+ if (Gen.MergeList(Parser) == false)
+ return _error->Error("Problem with MergeLister for %s", DebFile.c_str());
+
+ return true;
+}
+pkgCache::PkgFileIterator debDebPkgFileIndex::FindInCache(pkgCache &Cache) const
+{
+ // FIXME: we could simply always return pkgCache::PkgFileIterator(Cache);
+ // to indicate its never in the cache which will force a Merge()
+ pkgCache::PkgFileIterator File = Cache.FileBegin();
+ for (; File.end() == false; ++File)
+ {
+ if (File.FileName() == NULL || DebFile != File.FileName())
+ continue;
+
+ return File;
+ }
+
+ return File;
+}
+unsigned long debDebPkgFileIndex::Size() const
+{
+ struct stat buf;
+ if(stat(DebFile.c_str(), &buf) != 0)
+ return 0;
+ return buf.st_size;
+}
+ /*}}}*/
+
// Index File types for Debian /*{{{*/
class debIFTypeSrc : public pkgIndexFile::Type
{
@@ -699,10 +791,20 @@ class debIFTypeStatus : public pkgIndexFile::Type
};
debIFTypeStatus() {Label = "Debian dpkg status file";};
};
+class debIFTypeDebPkgFile : public pkgIndexFile::Type
+{
+ public:
+ virtual pkgRecords::Parser *CreatePkgParser(pkgCache::PkgFileIterator File) const
+ {
+ return new debDebFileRecordParser(File.FileName(),*File.Cache());
+ };
+ debIFTypeDebPkgFile() {Label = "deb Package file";};
+};
static debIFTypeSrc _apt_Src;
static debIFTypePkg _apt_Pkg;
static debIFTypeTrans _apt_Trans;
static debIFTypeStatus _apt_Status;
+static debIFTypeDebPkgFile _apt_DebPkgFile;
const pkgIndexFile::Type *debSourcesIndex::GetType() const
{
@@ -720,5 +822,8 @@ const pkgIndexFile::Type *debStatusIndex::GetType() const
{
return &_apt_Status;
}
-
+const pkgIndexFile::Type *debDebPkgFileIndex::GetType() const
+{
+ return &_apt_DebPkgFile;
+}
/*}}}*/
diff --git a/apt-pkg/deb/debindexfile.h b/apt-pkg/deb/debindexfile.h
index 017c69a0a..69754e79d 100644
--- a/apt-pkg/deb/debindexfile.h
+++ b/apt-pkg/deb/debindexfile.h
@@ -164,4 +164,35 @@ class debSourcesIndex : public pkgIndexFile
virtual ~debSourcesIndex() {};
};
+class debDebPkgFileIndex : public pkgIndexFile
+{
+ private:
+ void *d;
+ std::string DebFile;
+ std::string DebFileFullPath;
+
+ public:
+ virtual const Type *GetType() const APT_CONST;
+
+ virtual std::string Describe(bool /*Short*/) const {
+ return DebFile;
+ }
+
+ // Interface for the Cache Generator
+ virtual bool Exists() const;
+ virtual bool HasPackages() const {
+ return true;
+ };
+ virtual unsigned long Size() const;
+ virtual bool Merge(pkgCacheGenerator &Gen,OpProgress *Prog) const;
+ virtual pkgCache::PkgFileIterator FindInCache(pkgCache &Cache) const;
+
+ // Interface for acquire
+ virtual std::string ArchiveURI(std::string /*File*/) const;
+
+ debDebPkgFileIndex(std::string DebFile);
+ virtual ~debDebPkgFileIndex() {};
+
+};
+
#endif
diff --git a/apt-pkg/deb/deblistparser.cc b/apt-pkg/deb/deblistparser.cc
index a1bcfb710..d5e3ccb65 100644
--- a/apt-pkg/deb/deblistparser.cc
+++ b/apt-pkg/deb/deblistparser.cc
@@ -959,3 +959,23 @@ bool debListParser::SameVersion(unsigned short const Hash, /*{{{*/
}
/*}}}*/
#endif
+
+
+debDebFileParser::debDebFileParser(FileFd *File, std::string const &DebFile)
+ : debListParser(File, ""), DebFile(DebFile)
+{
+}
+
+bool debDebFileParser::UsePackage(pkgCache::PkgIterator &Pkg,
+ pkgCache::VerIterator &Ver)
+{
+ bool res = debListParser::UsePackage(Pkg, Ver);
+ // we use the full file path as a provides so that the file is found
+ // by its name
+ if(NewProvidesAllArch(Ver, DebFile, Ver.VerStr()) == false)
+ return false;
+ return res;
+}
+
+
+
diff --git a/apt-pkg/deb/deblistparser.h b/apt-pkg/deb/deblistparser.h
index baace79fe..92ec048b1 100644
--- a/apt-pkg/deb/deblistparser.h
+++ b/apt-pkg/deb/deblistparser.h
@@ -56,7 +56,8 @@ class debListParser : public pkgCacheGenerator::ListParser
bool ParseProvides(pkgCache::VerIterator &Ver);
bool NewProvidesAllArch(pkgCache::VerIterator &Ver, std::string const &Package, std::string const &Version);
static bool GrabWord(std::string Word,WordList *List,unsigned char &Out);
-
+ APT_HIDDEN unsigned char ParseMultiArch(bool const showErrors);
+
public:
static unsigned char GetPrio(std::string Str);
@@ -101,9 +102,17 @@ class debListParser : public pkgCacheGenerator::ListParser
debListParser(FileFd *File, std::string const &Arch = "");
virtual ~debListParser() {};
+};
- private:
- APT_HIDDEN unsigned char ParseMultiArch(bool const showErrors);
+class debDebFileParser : public debListParser
+{
+ private:
+ std::string DebFile;
+
+ public:
+ debDebFileParser(FileFd *File, std::string const &DebFile);
+ virtual bool UsePackage(pkgCache::PkgIterator &Pkg,
+ pkgCache::VerIterator &Ver);
};
#endif
diff --git a/apt-pkg/deb/debmetaindex.cc b/apt-pkg/deb/debmetaindex.cc
index 6fd12add8..56eecdca1 100644
--- a/apt-pkg/deb/debmetaindex.cc
+++ b/apt-pkg/deb/debmetaindex.cc
@@ -471,6 +471,15 @@ class debSLTypeDebian : public pkgSourceList::Type
}
};
+debDebFileMetaIndex::debDebFileMetaIndex(std::string const &DebFile)
+ : metaIndex(DebFile, "local-uri", "deb-dist"), DebFile(DebFile)
+{
+ DebIndex = new debDebPkgFileIndex(DebFile);
+ Indexes = new vector<pkgIndexFile *>();
+ Indexes->push_back(DebIndex);
+}
+
+
class debSLTypeDeb : public debSLTypeDebian
{
public:
@@ -507,5 +516,25 @@ class debSLTypeDebSrc : public debSLTypeDebian
}
};
+class debSLTypeDebFile : public pkgSourceList::Type
+{
+ public:
+
+ bool CreateItem(vector<metaIndex *> &List, string const &URI,
+ string const &Dist, string const &Section,
+ std::map<string, string> const &Options) const
+ {
+ metaIndex *mi = new debDebFileMetaIndex(URI);
+ List.push_back(mi);
+ return true;
+ }
+
+ debSLTypeDebFile()
+ {
+ Name = "deb-file";
+ Label = "Debian Deb File";
+ }
+};
debSLTypeDeb _apt_DebType;
debSLTypeDebSrc _apt_DebSrcType;
+debSLTypeDebFile _apt_DebFileType;
diff --git a/apt-pkg/deb/debmetaindex.h b/apt-pkg/deb/debmetaindex.h
index 2286fa8b2..0e70bba87 100644
--- a/apt-pkg/deb/debmetaindex.h
+++ b/apt-pkg/deb/debmetaindex.h
@@ -18,6 +18,7 @@
class pkgAcquire;
class pkgIndexFile;
+class debDebPkgFileIndex;
class debReleaseIndex : public metaIndex {
public:
@@ -71,4 +72,27 @@ class debReleaseIndex : public metaIndex {
void PushSectionEntry(const debSectionEntry *Entry);
};
+class debDebFileMetaIndex : public metaIndex
+{
+ private:
+ std::string DebFile;
+ debDebPkgFileIndex *DebIndex;
+ public:
+ virtual std::string ArchiveURI(std::string const& /*File*/) const {
+ return DebFile;
+ }
+ virtual bool GetIndexes(pkgAcquire* /*Owner*/, const bool& /*GetAll=false*/) const {
+ return true;
+ }
+ virtual std::vector<pkgIndexFile *> *GetIndexFiles() {
+ return Indexes;
+ }
+ virtual bool IsTrusted() const {
+ return true;
+ }
+ debDebFileMetaIndex(std::string const &DebFile);
+ virtual ~debDebFileMetaIndex() {};
+
+};
+
#endif
diff --git a/apt-pkg/deb/debrecords.h b/apt-pkg/deb/debrecords.h
index bdac6c90b..d572bc5c2 100644
--- a/apt-pkg/deb/debrecords.h
+++ b/apt-pkg/deb/debrecords.h
@@ -29,17 +29,16 @@ class debRecordParser : public pkgRecords::Parser
{
/** \brief dpointer placeholder (for later in case we need it) */
void *d;
-
+
+ protected:
FileFd File;
pkgTagFile Tags;
pkgTagSection Section;
- protected:
-
virtual bool Jump(pkgCache::VerFileIterator const &Ver);
virtual bool Jump(pkgCache::DescFileIterator const &Desc);
- public:
+ public:
// These refer to the archive file for the Version
virtual std::string FileName();
@@ -66,4 +65,15 @@ class debRecordParser : public pkgRecords::Parser
virtual ~debRecordParser() {};
};
+// custom record parser that reads deb files directly
+class debDebFileRecordParser : public debRecordParser
+{
+ public:
+ virtual std::string FileName() {
+ return File.Name();
+ }
+ debDebFileRecordParser(std::string FileName,pkgCache &Cache)
+ : debRecordParser(FileName, Cache) {};
+};
+
#endif
diff --git a/apt-pkg/sourcelist.h b/apt-pkg/sourcelist.h
index 9df0c1d74..99e83f454 100644
--- a/apt-pkg/sourcelist.h
+++ b/apt-pkg/sourcelist.h
@@ -52,7 +52,15 @@ class pkgAcquire;
class pkgIndexFile;
class metaIndex;
-class pkgSourceList
+class pkgSource
+{
+ protected:
+
+ std::vector<metaIndex *> SrcList;
+
+};
+
+class pkgSourceList : public pkgSource
{
public:
diff --git a/apt-private/private-cachefile.h b/apt-private/private-cachefile.h
index dce7e0a3a..1fddabfbd 100644
--- a/apt-private/private-cachefile.h
+++ b/apt-private/private-cachefile.h
@@ -6,7 +6,20 @@
#include <apt-pkg/configuration.h>
#include <apt-pkg/pkgcache.h>
#include <apt-pkg/macros.h>
+#include <apt-pkg/sourcelist.h>
+#include <apti18n.h>
+// FIXME: we need to find a way to export this
+class APT_PUBLIC SourceList : public pkgSourceList
+{
+
+ public:
+ // Add custom metaIndex (e.g. local files)
+ void AddMetaIndex(metaIndex *mi) {
+ SrcList.push_back(mi);
+ }
+
+};
// class CacheFile - Cover class for some dependency cache functions /*{{{*/
// ---------------------------------------------------------------------
@@ -28,6 +41,16 @@ class APT_PUBLIC CacheFile : public pkgCacheFile
return false;
return true;
}
+ // FIXME: this can go once the "libapt-pkg" pkgSourceList has a way
+ // to add custom metaIndexes (or custom local files or so)
+ bool BuildSourceList(OpProgress */*Progress*/ = NULL) {
+ if (SrcList != NULL)
+ return true;
+ SrcList = new SourceList();
+ if (SrcList->ReadMainList() == false)
+ return _error->Error(_("The list of sources could not be read."));
+ return true;
+ }
bool Open(bool WithLock = true)
{
OpTextProgress Prog(*_config);
diff --git a/apt-private/private-install.cc b/apt-private/private-install.cc
index 107ed398e..35649fcdb 100644
--- a/apt-private/private-install.cc
+++ b/apt-private/private-install.cc
@@ -19,6 +19,7 @@
#include <apt-pkg/macros.h>
#include <apt-pkg/packagemanager.h>
#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/sourcelist.h>
#include <errno.h>
#include <stdlib.h>
@@ -29,6 +30,7 @@
#include <iostream>
#include <set>
#include <vector>
+#include <map>
#include <apt-private/acqprogress.h>
#include <apt-private/private-install.h>
@@ -669,10 +671,34 @@ bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, CacheFile &Cache,
bool DoInstall(CommandLine &CmdL)
{
CacheFile Cache;
+ // first check for local pkgs and add them to the cache
+ for (const char **I = CmdL.FileList; *I != 0; I++)
+ {
+ if(FileExists(*I))
+ {
+ // FIXME: make this more elegant
+ std::string TypeStr = flExtension(*I) + "-file";
+ pkgSourceList::Type *Type = pkgSourceList::Type::GetType(TypeStr.c_str());
+ if(Type != 0)
+ {
+ std::vector<metaIndex *> List;
+ std::map<std::string, std::string> Options;
+ if(Type->CreateItem(List, *I, "", "", Options))
+ {
+ // we have our own CacheFile that gives us a SourceList
+ // with superpowerz
+ SourceList *sources = (SourceList*)Cache.GetSourceList();
+ sources->AddMetaIndex(List[0]);
+ }
+ }
+ }
+ }
+
+ // then open the cache
if (Cache.OpenForInstall() == false ||
Cache.CheckDeps(CmdL.FileSize() != 1) == false)
return false;
-
+
std::map<unsigned short, APT::VersionSet> verset;
if(!DoCacheManipulationFromCommandLine(CmdL, Cache, verset))
diff --git a/test/libapt/fileutl_test.cc b/test/libapt/fileutl_test.cc
index 643c02297..cdf7ea479 100644
--- a/test/libapt/fileutl_test.cc
+++ b/test/libapt/fileutl_test.cc
@@ -224,3 +224,61 @@ TEST(FileUtlTest, GetTempDir)
if (old_tmpdir.empty() == false)
setenv("TMPDIR", old_tmpdir.c_str(), 1);
}
+TEST(FileUtlTest, Popen)
+{
+ FileFd Fd;
+ pid_t Child;
+ char buf[1024];
+ std::string s;
+ unsigned long long n = 0;
+ std::vector<std::string> OpenFds;
+
+ // count Fds to ensure we don't have a resource leak
+ if(FileExists("/proc/self/fd"))
+ OpenFds = Glob("/proc/self/fd/*");
+
+ // output something
+ const char* Args[10] = {"/bin/echo", "meepmeep", NULL};
+ bool res = Popen(Args, Fd, Child, FileFd::ReadOnly);
+ Fd.Read(buf, sizeof(buf)-1, &n);
+ buf[n] = 0;
+ EXPECT_NE(n, 0);
+ EXPECT_EQ(res, true);
+ EXPECT_STREQ(buf, "meepmeep\n");
+
+ // wait for the child to exit and cleanup
+ ExecWait(Child, "PopenRead");
+ Fd.Close();
+
+ // ensure that after a close all is good again
+ if(FileExists("/proc/self/fd"))
+ EXPECT_EQ(Glob("/proc/self/fd/*").size(), OpenFds.size());
+
+
+ // ReadWrite is not supported
+ res = Popen(Args, Fd, Child, FileFd::ReadWrite);
+ EXPECT_EQ(res, false);
+ _error->Discard();
+
+ // write something
+ Args[0] = "/bin/bash";
+ Args[1] = "-c";
+ Args[2] = "read";
+ Args[3] = NULL;
+ res = Popen(Args, Fd, Child, FileFd::WriteOnly);
+ s = "\n";
+ Fd.Write(s.c_str(), s.size());
+ Fd.Close();
+ ExecWait(Child, "PopenWrite");
+}
+TEST(FileUtlTest, flAbsPath)
+{
+ std::string cwd = SafeGetCWD();
+ int res = chdir("/bin/");
+ EXPECT_EQ(res, 0);
+ std::string p = flAbsPath("ls");
+ EXPECT_EQ(p, "/bin/ls");
+
+ res = chdir(cwd.c_str());
+ EXPECT_EQ(res, 0);
+}