]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/contrib/fileutl.cc
Merge branch 'feature/acq-trans' into feature/expected-size
[apt.git] / apt-pkg / contrib / fileutl.cc
index 1c8acd513f5dedc8df202dfb2023944e413156e9..c5eb56f0e236ac7f7c9dba829118d2f330af7bfe 100644 (file)
@@ -1,6 +1,5 @@
 // -*- mode: cpp; mode: fold -*-
 // Description                                                         /*{{{*/
-// $Id: fileutl.cc,v 1.42 2002/09/14 05:29:22 jgg Exp $
 /* ######################################################################
    
    File Utilities
 #include <apt-pkg/sptr.h>
 #include <apt-pkg/aptconfiguration.h>
 #include <apt-pkg/configuration.h>
-
+#include <apt-pkg/macros.h>
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <sys/select.h>
+#include <time.h>
+#include <string>
+#include <vector>
 #include <cstdlib>
 #include <cstring>
 #include <cstdio>
-
 #include <iostream>
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/stat.h>
-#include <sys/types.h>
 #include <sys/time.h>
 #include <sys/wait.h>
 #include <dirent.h>
 #include <signal.h>
 #include <errno.h>
 #include <glob.h>
+#include <pwd.h>
+#include <grp.h>
 
 #include <set>
 #include <algorithm>
 #ifdef HAVE_BZ2
        #include <bzlib.h>
 #endif
+#ifdef HAVE_LZMA
+       #include <lzma.h>
+#endif
+#include <endian.h>
+#include <stdint.h>
 
-#ifdef WORDS_BIGENDIAN
-#include <inttypes.h>
+#if __gnu_linux__
+#include <sys/prctl.h>
 #endif
 
 #include <apti18n.h>
 
 using namespace std;
 
-class FileFdPrivate {
-       public:
-#ifdef HAVE_ZLIB
-       gzFile gz;
-#else
-       void* gz;
-#endif
-#ifdef HAVE_BZ2
-       BZFILE* bz2;
-#else
-       void* bz2;
-#endif
-       int compressed_fd;
-       pid_t compressor_pid;
-       bool pipe;
-       APT::Configuration::Compressor compressor;
-       unsigned int openmode;
-       unsigned long long seekpos;
-       FileFdPrivate() : gz(NULL), bz2(NULL),
-                         compressed_fd(-1), compressor_pid(-1), pipe(false),
-                         openmode(0), seekpos(0) {};
-       bool CloseDown(std::string const &FileName)
-       {
-          bool Res = true;
-#ifdef HAVE_ZLIB
-          if (gz != NULL) {
-             int const e = gzclose(gz);
-             gz = NULL;
-             // gzdclose() on empty files always fails with "buffer error" here, ignore that
-             if (e != 0 && e != Z_BUF_ERROR)
-                Res &= _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
-          }
-#endif
-#ifdef HAVE_BZ2
-          if (bz2 != NULL) {
-             BZ2_bzclose(bz2);
-             bz2 = NULL;
-          }
-#endif
-          if (compressor_pid > 0)
-             ExecWait(compressor_pid, "FileFdCompressor", true);
-          compressor_pid = -1;
-
-          return Res;
-       }
-       ~FileFdPrivate() { CloseDown(""); }
-};
-
 // RunScripts - Run a set of scripts from a configuration subtree      /*{{{*/
 // ---------------------------------------------------------------------
 /* */
@@ -143,7 +107,11 @@ bool RunScripts(const char *Cnf)
       {
         if (Opts->Value.empty() == true)
            continue;
-        
+
+         if(_config->FindB("Debug::RunScripts", false) == true)
+            std::clog << "Running external script: '"
+                      << Opts->Value << "'" << std::endl;
+
         if (system(Opts->Value.c_str()) != 0)
            _exit(100+Count);
       }
@@ -222,7 +190,7 @@ int GetLock(string File,bool Errors)
    int FD = open(File.c_str(),O_RDWR | O_CREAT | O_NOFOLLOW,0640);
    if (FD < 0)
    {
-      // Read only .. cant have locking problems there.
+      // Read only .. can't have locking problems there.
       if (errno == EROFS)
       {
         _error->Warning(_("Not using locking for read only lock file %s"),File.c_str());
@@ -238,7 +206,7 @@ int GetLock(string File,bool Errors)
    }
    SetCloseExec(FD,true);
       
-   // Aquire a write lock
+   // Acquire a write lock
    struct flock fl;
    fl.l_type = F_WRLCK;
    fl.l_whence = SEEK_SET;
@@ -465,7 +433,7 @@ std::vector<string> GetListOfFilesInDir(string const &Dir, std::vector<string> c
       const char *C = Ent->d_name;
       for (; *C != 0; ++C)
         if (isalpha(*C) == 0 && isdigit(*C) == 0
-            && *C != '_' && *C != '-') {
+            && *C != '_' && *C != '-' && *C != ':') {
            // no required extension -> dot is a bad character
            if (*C == '.' && Ext.empty() == false)
               continue;
@@ -694,6 +662,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                           /*{{{*/
 // ---------------------------------------------------------------------
 /* */
@@ -874,13 +858,150 @@ bool ExecWait(pid_t Pid,const char *Name,bool Reap)
 }
                                                                        /*}}}*/
 
+
+// StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned     /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool StartsWithGPGClearTextSignature(string const &FileName)
+{
+   static const char* SIGMSG = "-----BEGIN PGP SIGNED MESSAGE-----\n";
+   char buffer[strlen(SIGMSG)+1];
+   FILE* gpg = fopen(FileName.c_str(), "r");
+   if (gpg == NULL)
+      return false;
+
+   char const * const test = fgets(buffer, sizeof(buffer), gpg);
+   fclose(gpg);
+   if (test == NULL || strcmp(buffer, SIGMSG) != 0)
+      return false;
+
+   return true;
+}
+
+
+class FileFdPrivate {                                                  /*{{{*/
+       public:
+#ifdef HAVE_ZLIB
+       gzFile gz;
+#endif
+#ifdef HAVE_BZ2
+       BZFILE* bz2;
+#endif
+#ifdef HAVE_LZMA
+       struct LZMAFILE {
+          FILE* file;
+          uint8_t buffer[4096];
+          lzma_stream stream;
+          lzma_ret err;
+          bool eof;
+          bool compressing;
+
+          LZMAFILE() : file(NULL), eof(false), compressing(false) { buffer[0] = '\0'; }
+          ~LZMAFILE() {
+             if (compressing == true)
+             {
+               for (;;) {
+                       stream.avail_out = sizeof(buffer)/sizeof(buffer[0]);
+                       stream.next_out = buffer;
+                       err = lzma_code(&stream, LZMA_FINISH);
+                       if (err != LZMA_OK && err != LZMA_STREAM_END)
+                       {
+                               _error->Error("~LZMAFILE: Compress finalisation failed");
+                               break;
+                       }
+                       size_t const n =  sizeof(buffer)/sizeof(buffer[0]) - stream.avail_out;
+                       if (n && fwrite(buffer, 1, n, file) != n)
+                       {
+                               _error->Errno("~LZMAFILE",_("Write error"));
+                               break;
+                       }
+                       if (err == LZMA_STREAM_END)
+                               break;
+               }
+             }
+             lzma_end(&stream);
+             fclose(file);
+          }
+       };
+       LZMAFILE* lzma;
+#endif
+       int compressed_fd;
+       pid_t compressor_pid;
+       bool pipe;
+       APT::Configuration::Compressor compressor;
+       unsigned int openmode;
+       unsigned long long seekpos;
+       FileFdPrivate() :
+#ifdef HAVE_ZLIB
+                         gz(NULL),
+#endif
+#ifdef HAVE_BZ2
+                         bz2(NULL),
+#endif
+#ifdef HAVE_LZMA
+                         lzma(NULL),
+#endif
+                         compressed_fd(-1), compressor_pid(-1), pipe(false),
+                         openmode(0), seekpos(0) {};
+       bool InternalClose(std::string const &FileName)
+       {
+          if (false)
+             /* dummy so that the rest can be 'else if's */;
+#ifdef HAVE_ZLIB
+          else if (gz != NULL) {
+             int const e = gzclose(gz);
+             gz = NULL;
+             // gzdclose() on empty files always fails with "buffer error" here, ignore that
+             if (e != 0 && e != Z_BUF_ERROR)
+                return _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
+          }
+#endif
+#ifdef HAVE_BZ2
+          else if (bz2 != NULL) {
+             BZ2_bzclose(bz2);
+             bz2 = NULL;
+          }
+#endif
+#ifdef HAVE_LZMA
+          else if (lzma != NULL) {
+             delete lzma;
+             lzma = NULL;
+          }
+#endif
+          return true;
+       }
+       bool CloseDown(std::string const &FileName)
+       {
+          bool const Res = InternalClose(FileName);
+
+          if (compressor_pid > 0)
+             ExecWait(compressor_pid, "FileFdCompressor", true);
+          compressor_pid = -1;
+
+          return Res;
+       }
+       bool InternalStream() const {
+          return false
+#ifdef HAVE_BZ2
+             || bz2 != NULL
+#endif
+#ifdef HAVE_LZMA
+             || lzma != NULL
+#endif
+             ;
+       }
+
+
+       ~FileFdPrivate() { CloseDown(""); }
+};
+                                                                       /*}}}*/
 // FileFd::Open - Open a file                                          /*{{{*/
 // ---------------------------------------------------------------------
 /* The most commonly used open mode combinations are given with Mode */
-bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const Perms)
+bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const AccessMode)
 {
    if (Mode == ReadOnlyGzip)
-      return Open(FileName, ReadOnly, Gzip, Perms);
+      return Open(FileName, ReadOnly, Gzip, AccessMode);
 
    if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
       return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
@@ -891,7 +1012,7 @@ bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress,
    {
       for (; compressor != compressors.end(); ++compressor)
       {
-        std::string file = std::string(FileName).append(compressor->Extension);
+        std::string file = FileName + compressor->Extension;
         if (FileExists(file) == false)
            continue;
         FileName = file;
@@ -947,9 +1068,9 @@ bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress,
 
    if (compressor == compressors.end())
       return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
-   return Open(FileName, Mode, *compressor, Perms);
+   return Open(FileName, Mode, *compressor, AccessMode);
 }
-bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const Perms)
+bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const AccessMode)
 {
    Close();
    Flags = AutoClose;
@@ -999,11 +1120,18 @@ bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Co
       TemporaryFileName = string(name);
       free(name);
 
-      if(Perms != 600 && fchmod(iFd, Perms) == -1)
+      // umask() will always set the umask and return the previous value, so
+      // we first set the umask and then reset it to the old value
+      mode_t const CurrentUmask = umask(0);
+      umask(CurrentUmask);
+      // calculate the actual file permissions (just like open/creat)
+      mode_t const FilePermissions = (AccessMode & ~CurrentUmask);
+
+      if(fchmod(iFd, FilePermissions) == -1)
           return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
    }
    else
-      iFd = open(FileName.c_str(), fileflags, Perms);
+      iFd = open(FileName.c_str(), fileflags, AccessMode);
 
    this->FileName = FileName;
    if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false)
@@ -1061,30 +1189,12 @@ bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration:
 {
    Close();
    Flags = (AutoClose) ? FileFd::AutoClose : 0;
-   if (AutoClose == false && (
-#ifdef HAVE_ZLIB
-       compressor.Name == "gzip" ||
-#endif
-#ifdef HAVE_BZ2
-       compressor.Name == "bzip2" ||
-#endif
-       false))
-   {
-      // Need to duplicate fd here or gzclose for cleanup will close the fd as well
-      iFd = dup(Fd);
-   }
-   else
-      iFd = Fd;
+   iFd = Fd;
    this->FileName = "";
-   if (Fd == -1 || OpenInternDescriptor(Mode, compressor) == false)
+   if (OpenInternDescriptor(Mode, compressor) == false)
    {
       if (iFd != -1 && (
-#ifdef HAVE_ZLIB
-       compressor.Name == "gzip" ||
-#endif
-#ifdef HAVE_BZ2
-       compressor.Name == "bzip2" ||
-#endif
+       (Flags & Compressed) == Compressed ||
        AutoClose == true))
       {
         close (iFd);
@@ -1096,52 +1206,122 @@ bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration:
 }
 bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
 {
+   if (iFd == -1)
+      return false;
    if (compressor.Name == "." || compressor.Binary.empty() == true)
       return true;
 
+#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
+   // the API to open files is similar, so setup to avoid code duplicates later
+   // and while at it ensure that we close before opening (if its a reopen)
+   void* (*compress_open)(int, const char *) = NULL;
+   if (false)
+      /* dummy so that the rest can be 'else if's */;
+#define APT_COMPRESS_INIT(NAME,OPEN) \
+   else if (compressor.Name == NAME) \
+   { \
+      compress_open = (void*(*)(int, const char *)) OPEN; \
+      if (d != NULL) d->InternalClose(FileName); \
+   }
+#ifdef HAVE_ZLIB
+   APT_COMPRESS_INIT("gzip", gzdopen)
+#endif
+#ifdef HAVE_BZ2
+   APT_COMPRESS_INIT("bzip2", BZ2_bzdopen)
+#endif
+#ifdef HAVE_LZMA
+   APT_COMPRESS_INIT("xz", fdopen)
+   APT_COMPRESS_INIT("lzma", fdopen)
+#endif
+#undef APT_COMPRESS_INIT
+#endif
+
    if (d == NULL)
    {
       d = new FileFdPrivate();
       d->openmode = Mode;
       d->compressor = compressor;
+#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
+      if ((Flags & AutoClose) != AutoClose && compress_open != NULL)
+      {
+        // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
+        int const internFd = dup(iFd);
+        if (internFd == -1)
+           return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
+        iFd = internFd;
+      }
+#endif
    }
 
-#ifdef HAVE_ZLIB
-   if (compressor.Name == "gzip")
+#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
+   if (compress_open != NULL)
    {
-      if (d->gz != NULL)
-      {
-        gzclose(d->gz);
-        d->gz = NULL;
-      }
+      void* compress_struct = NULL;
       if ((Mode & ReadWrite) == ReadWrite)
-        d->gz = gzdopen(iFd, "r+");
+        compress_struct = compress_open(iFd, "r+");
       else if ((Mode & WriteOnly) == WriteOnly)
-        d->gz = gzdopen(iFd, "w");
+        compress_struct = compress_open(iFd, "w");
       else
-        d->gz = gzdopen(iFd, "r");
-      if (d->gz == NULL)
+        compress_struct = compress_open(iFd, "r");
+      if (compress_struct == NULL)
         return false;
-      Flags |= Compressed;
-      return true;
-   }
+
+      if (false)
+        /* dummy so that the rest can be 'else if's */;
+#ifdef HAVE_ZLIB
+      else if (compressor.Name == "gzip")
+        d->gz = (gzFile) compress_struct;
 #endif
 #ifdef HAVE_BZ2
-   if (compressor.Name == "bzip2")
-   {
-      if (d->bz2 != NULL)
+      else if (compressor.Name == "bzip2")
+        d->bz2 = (BZFILE*) compress_struct;
+#endif
+#ifdef HAVE_LZMA
+      else if (compressor.Name == "xz" || compressor.Name == "lzma")
       {
-        BZ2_bzclose(d->bz2);
-        d->bz2 = NULL;
+        uint32_t const xzlevel = 6;
+        uint64_t const memlimit = UINT64_MAX;
+        if (d->lzma == NULL)
+           d->lzma = new FileFdPrivate::LZMAFILE;
+        d->lzma->file = (FILE*) compress_struct;
+         lzma_stream tmp_stream = LZMA_STREAM_INIT;
+        d->lzma->stream = tmp_stream;
+
+        if ((Mode & ReadWrite) == ReadWrite)
+           return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
+
+        if ((Mode & WriteOnly) == WriteOnly)
+        {
+           if (compressor.Name == "xz")
+           {
+              if (lzma_easy_encoder(&d->lzma->stream, xzlevel, LZMA_CHECK_CRC32) != LZMA_OK)
+                 return false;
+           }
+           else
+           {
+              lzma_options_lzma options;
+              lzma_lzma_preset(&options, xzlevel);
+              if (lzma_alone_encoder(&d->lzma->stream, &options) != LZMA_OK)
+                 return false;
+           }
+           d->lzma->compressing = true;
+        }
+        else
+        {
+           if (compressor.Name == "xz")
+           {
+              if (lzma_auto_decoder(&d->lzma->stream, memlimit, 0) != LZMA_OK)
+                 return false;
+           }
+           else
+           {
+              if (lzma_alone_decoder(&d->lzma->stream, memlimit) != LZMA_OK)
+                 return false;
+           }
+           d->lzma->compressing = false;
+        }
       }
-      if ((Mode & ReadWrite) == ReadWrite)
-        d->bz2 = BZ2_bzdopen(iFd, "r+");
-      else if ((Mode & WriteOnly) == WriteOnly)
-        d->bz2 = BZ2_bzdopen(iFd, "w");
-      else
-        d->bz2 = BZ2_bzdopen(iFd, "r");
-      if (d->bz2 == NULL)
-        return false;
+#endif
       Flags |= Compressed;
       return true;
    }
@@ -1198,7 +1378,7 @@ bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::C
       }
       else
       {
-        if (FileName.empty() == true)
+        if (d->compressed_fd != -1)
            dup2(d->compressed_fd,STDIN_FILENO);
         dup2(Pipe[1],STDOUT_FILENO);
       }
@@ -1221,7 +1401,10 @@ bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::C
         Args.push_back(a->c_str());
       if (Comp == false && FileName.empty() == false)
       {
-        Args.push_back("--stdout");
+        // commands not needing arguments, do not need to be told about using standard output
+        // in reality, only testcases with tools like cat, rev, rot13, â€¦ are able to trigger this
+        if (compressor.CompressArgs.empty() == false && compressor.UncompressArgs.empty() == false)
+           Args.push_back("--stdout");
         if (TemporaryFileName.empty() == false)
            Args.push_back(TemporaryFileName.c_str());
         else
@@ -1256,7 +1439,7 @@ FileFd::~FileFd()
                                                                        /*}}}*/
 // FileFd::Read - Read a bit of the file                               /*{{{*/
 // ---------------------------------------------------------------------
-/* We are carefull to handle interruption by a signal while reading 
+/* We are careful to handle interruption by a signal while reading
    gracefully. */
 bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
 {
@@ -1267,24 +1450,68 @@ bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
    *((char *)To) = '\0';
    do
    {
+      if (false)
+        /* dummy so that the rest can be 'else if's */;
 #ifdef HAVE_ZLIB
-      if (d != NULL && d->gz != NULL)
+      else if (d != NULL && d->gz != NULL)
         Res = gzread(d->gz,To,Size);
-      else
 #endif
 #ifdef HAVE_BZ2
-      if (d != NULL && d->bz2 != NULL)
+      else if (d != NULL && d->bz2 != NULL)
         Res = BZ2_bzread(d->bz2,To,Size);
-      else
 #endif
+#ifdef HAVE_LZMA
+      else if (d != NULL && d->lzma != NULL)
+      {
+        if (d->lzma->eof == true)
+           break;
+
+        d->lzma->stream.next_out = (uint8_t *) To;
+        d->lzma->stream.avail_out = Size;
+        if (d->lzma->stream.avail_in == 0)
+        {
+           d->lzma->stream.next_in = d->lzma->buffer;
+           d->lzma->stream.avail_in = fread(d->lzma->buffer, 1, sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]), d->lzma->file);
+        }
+        d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
+        if (d->lzma->err == LZMA_STREAM_END)
+        {
+           d->lzma->eof = true;
+           Res = Size - d->lzma->stream.avail_out;
+        }
+        else if (d->lzma->err != LZMA_OK)
+        {
+           Res = -1;
+           errno = 0;
+        }
+        else
+        {
+           Res = Size - d->lzma->stream.avail_out;
+           if (Res == 0)
+           {
+              // lzma run was okay, but produced no output…
+              Res = -1;
+              errno = EINTR;
+           }
+        }
+      }
+#endif
+      else
          Res = read(iFd,To,Size);
 
       if (Res < 0)
       {
         if (errno == EINTR)
+        {
+           // trick the while-loop into running again
+           Res = 1;
+           errno = 0;
            continue;
+        }
+        if (false)
+           /* dummy so that the rest can be 'else if's */;
 #ifdef HAVE_ZLIB
-        if (d != NULL && d->gz != NULL)
+        else if (d != NULL && d->gz != NULL)
         {
            int err;
            char const * const errmsg = gzerror(d->gz, &err);
@@ -1293,13 +1520,17 @@ bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
         }
 #endif
 #ifdef HAVE_BZ2
-        if (d != NULL && d->bz2 != NULL)
+        else if (d != NULL && d->bz2 != NULL)
         {
            int err;
            char const * const errmsg = BZ2_bzerror(d->bz2, &err);
            if (err != BZ_IO_ERROR)
-              return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
+              return FileFdError("BZ2_bzread: %s %s (%d: %s)", FileName.c_str(), _("Read error"), err, errmsg);
         }
+#endif
+#ifdef HAVE_LZMA
+        else if (d != NULL && d->lzma != NULL)
+           return FileFdError("lzma_read: %s (%d)", _("Read error"), d->lzma->err);
 #endif
         return FileFdErrno("read",_("Read error"));
       }
@@ -1364,23 +1595,45 @@ bool FileFd::Write(const void *From,unsigned long long Size)
    errno = 0;
    do
    {
+      if (false)
+        /* dummy so that the rest can be 'else if's */;
 #ifdef HAVE_ZLIB
-      if (d != NULL && d->gz != NULL)
-         Res = gzwrite(d->gz,From,Size);
-      else
+      else if (d != NULL && d->gz != NULL)
+        Res = gzwrite(d->gz,From,Size);
 #endif
 #ifdef HAVE_BZ2
-      if (d != NULL && d->bz2 != NULL)
-         Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
-      else
+      else if (d != NULL && d->bz2 != NULL)
+        Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
 #endif
-         Res = write(iFd,From,Size);
+#ifdef HAVE_LZMA
+      else if (d != NULL && d->lzma != NULL)
+      {
+        d->lzma->stream.next_in = (uint8_t *)From;
+        d->lzma->stream.avail_in = Size;
+        d->lzma->stream.next_out = d->lzma->buffer;
+        d->lzma->stream.avail_out = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]);
+        d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
+        if (d->lzma->err != LZMA_OK)
+           return false;
+        size_t const n = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]) - d->lzma->stream.avail_out;
+        size_t const m = (n == 0) ? 0 : fwrite(d->lzma->buffer, 1, n, d->lzma->file);
+        if (m != n)
+           Res = -1;
+        else
+           Res = Size - d->lzma->stream.avail_in;
+      }
+#endif
+      else
+        Res = write(iFd,From,Size);
+
       if (Res < 0 && errno == EINTR)
         continue;
       if (Res < 0)
       {
+        if (false)
+           /* dummy so that the rest can be 'else if's */;
 #ifdef HAVE_ZLIB
-        if (d != NULL && d->gz != NULL)
+        else if (d != NULL && d->gz != NULL)
         {
            int err;
            char const * const errmsg = gzerror(d->gz, &err);
@@ -1389,18 +1642,22 @@ bool FileFd::Write(const void *From,unsigned long long Size)
         }
 #endif
 #ifdef HAVE_BZ2
-        if (d != NULL && d->bz2 != NULL)
+        else if (d != NULL && d->bz2 != NULL)
         {
            int err;
            char const * const errmsg = BZ2_bzerror(d->bz2, &err);
            if (err != BZ_IO_ERROR)
               return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
         }
+#endif
+#ifdef HAVE_LZMA
+        else if (d != NULL && d->lzma != NULL)
+           return FileFdErrno("lzma_fwrite", _("Write error"));
 #endif
         return FileFdErrno("write",_("Write error"));
       }
       
-      From = (char *)From + Res;
+      From = (char const *)From + Res;
       Size -= Res;
       if (d != NULL)
         d->seekpos += Res;
@@ -1424,7 +1681,7 @@ bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
       if (Res < 0)
         return _error->Errno("write",_("Write error"));
 
-      From = (char *)From + Res;
+      From = (char const *)From + Res;
       Size -= Res;
    }
    while (Res > 0 && Size > 0);
@@ -1440,11 +1697,9 @@ bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
 /* */
 bool FileFd::Seek(unsigned long long To)
 {
-   if (d != NULL && (d->pipe == true
-#ifdef HAVE_BZ2
-                       || d->bz2 != NULL
-#endif
-       ))
+   Flags &= ~HitEof;
+
+   if (d != NULL && (d->pipe == true || d->InternalStream() == true))
    {
       // Our poor man seeking in pipes is costly, so try to avoid it
       unsigned long long seekpos = Tell();
@@ -1455,13 +1710,7 @@ bool FileFd::Seek(unsigned long long To)
 
       if ((d->openmode & ReadOnly) != ReadOnly)
         return FileFdError("Reopen is only implemented for read-only files!");
-#ifdef HAVE_BZ2
-     if (d->bz2 != NULL) 
-     {
-       BZ2_bzclose(d->bz2);
-       d->bz2 = NULL;
-     }
-#endif
+      d->InternalClose(FileName);
       if (iFd != -1)
         close(iFd);
       iFd = -1;
@@ -1507,13 +1756,8 @@ bool FileFd::Seek(unsigned long long To)
 /* */
 bool FileFd::Skip(unsigned long long Over)
 {
-   if (d != NULL && (d->pipe == true
-#ifdef HAVE_BZ2
-                       || d->bz2 != NULL
-#endif
-       ))
+   if (d != NULL && (d->pipe == true || d->InternalStream() == true))
    {
-      d->seekpos += Over;
       char buffer[1024];
       while (Over != 0)
       {
@@ -1548,8 +1792,12 @@ bool FileFd::Truncate(unsigned long long To)
    // truncating /dev/null is always successful - as we get an error otherwise
    if (To == 0 && FileName == "/dev/null")
       return true;
-#if defined HAVE_ZLIB || defined HAVE_BZ2
-   if (d != NULL && (d->gz != NULL || d->bz2 != NULL))
+#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
+   if (d != NULL && (d->InternalStream() == true
+#ifdef HAVE_ZLIB
+           || d->gz != NULL
+#endif
+           ))
       return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
 #endif
    if (ftruncate(iFd,To) != 0)
@@ -1567,11 +1815,7 @@ unsigned long long FileFd::Tell()
    // seeking around, but not all users of FileFd use always Seek() and co
    // so d->seekpos isn't always true and we can just use it as a hint if
    // we have nothing else, but not always as an authority…
-   if (d != NULL && (d->pipe == true
-#ifdef HAVE_BZ2
-                       || d->bz2 != NULL
-#endif
-       ))
+   if (d != NULL && (d->pipe == true || d->InternalStream() == true))
       return d->seekpos;
 
    off_t Res;
@@ -1588,29 +1832,56 @@ unsigned long long FileFd::Tell()
    return Res;
 }
                                                                        /*}}}*/
-// FileFd::FileSize - Return the size of the file                      /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-unsigned long long FileFd::FileSize()
+static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
 {
-   struct stat Buf;
-   if ((d == NULL || d->pipe == false) && fstat(iFd,&Buf) != 0)
-      return FileFdErrno("fstat","Unable to determine the file size");
+   bool ispipe = (d != NULL && d->pipe == true);
+   if (ispipe == false)
+   {
+      if (fstat(iFd,&Buf) != 0)
+        // higher-level code will generate more meaningful messages,
+        // even translated this would be meaningless for users
+        return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
+      if (FileName.empty() == false)
+        ispipe = S_ISFIFO(Buf.st_mode);
+   }
 
    // for compressor pipes st_size is undefined and at 'best' zero
-   if ((d != NULL && d->pipe == true) || S_ISFIFO(Buf.st_mode))
+   if (ispipe == true)
    {
       // we set it here, too, as we get the info here for free
       // in theory the Open-methods should take care of it already
       if (d != NULL)
         d->pipe = true;
       if (stat(FileName.c_str(), &Buf) != 0)
-        return FileFdErrno("stat","Unable to determine the file size");
+        return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
+   }
+   return true;
+}
+                                                                       /*}}}*/
+// FileFd::FileSize - Return the size of the file                      /*{{{*/
+unsigned long long FileFd::FileSize()
+{
+   struct stat Buf;
+   if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
+   {
+      Flags |= Fail;
+      return 0;
    }
-
    return Buf.st_size;
 }
                                                                        /*}}}*/
+// FileFd::ModificationTime - Return the time of last touch            /*{{{*/
+time_t FileFd::ModificationTime()
+{
+   struct stat Buf;
+   if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
+   {
+      Flags |= Fail;
+      return 0;
+   }
+   return Buf.st_mtime;
+}
+                                                                       /*}}}*/
 // FileFd::Size - Return the size of the content in the file           /*{{{*/
 // ---------------------------------------------------------------------
 /* */
@@ -1620,11 +1891,7 @@ unsigned long long FileFd::Size()
 
    // for compressor pipes st_size is undefined and at 'best' zero,
    // so we 'read' the content and 'seek' back - see there
-   if (d != NULL && (d->pipe == true
-#ifdef HAVE_BZ2
-                       || (d->bz2 && size > 0)
-#endif
-       ))
+   if (d != NULL && (d->pipe == true || (d->InternalStream() == true && size > 0)))
    {
       unsigned long long const oldSeek = Tell();
       char ignore[1000];
@@ -1655,19 +1922,13 @@ unsigned long long FileFd::Size()
          FileFdErrno("lseek","Unable to seek to end of gzipped file");
          return 0;
        }
-       size = 0;
+       uint32_t size = 0;
        if (read(iFd, &size, 4) != 4)
        {
          FileFdErrno("read","Unable to read original size of gzipped file");
          return 0;
        }
-
-#ifdef WORDS_BIGENDIAN
-       uint32_t tmp_size = size;
-       uint8_t const * const p = (uint8_t const * const) &tmp_size;
-       tmp_size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
-       size = tmp_size;
-#endif
+       size = le32toh(size);
 
        if (lseek(iFd, oldPos, SEEK_SET) < 0)
        {
@@ -1682,35 +1943,6 @@ unsigned long long FileFd::Size()
    return size;
 }
                                                                        /*}}}*/
-// FileFd::ModificationTime - Return the time of last touch            /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-time_t FileFd::ModificationTime()
-{
-   struct stat Buf;
-   if ((d == NULL || d->pipe == false) && fstat(iFd,&Buf) != 0)
-   {
-      FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
-      return 0;
-   }
-
-   // for compressor pipes st_size is undefined and at 'best' zero
-   if ((d != NULL && d->pipe == true) || S_ISFIFO(Buf.st_mode))
-   {
-      // we set it here, too, as we get the info here for free
-      // in theory the Open-methods should take care of it already
-      if (d != NULL)
-        d->pipe = true;
-      if (stat(FileName.c_str(), &Buf) != 0)
-      {
-        FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
-        return 0;
-      }
-   }
-
-   return Buf.st_mtime;
-}
-                                                                       /*}}}*/
 // FileFd::Close - Close the file if the close flag is set             /*{{{*/
 // ---------------------------------------------------------------------
 /* */
@@ -1724,7 +1956,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);
@@ -1796,7 +2027,13 @@ bool FileFd::FileFdError(const char *Description,...) {
 }
                                                                        /*}}}*/
 
-gzFile FileFd::gzFd() { return (gzFile) d->gz; }
+APT_DEPRECATED gzFile FileFd::gzFd() {
+#ifdef HAVE_ZLIB
+   return d->gz;
+#else
+   return NULL;
+#endif
+}
 
 
 // Glob - wrapper around "glob()"                                      /*{{{*/
@@ -1844,3 +2081,181 @@ 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)
+   {
+      _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
+                    From.c_str(),To.c_str());
+      return false;
+   }   
+   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"));
+
+   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;
+}
+
+bool DropPrivs()
+{
+   // uid will be 0 in the end, but gid might be different anyway
+   uid_t old_uid = getuid();
+   gid_t old_gid = getgid();
+
+   if (old_uid != 0)
+      return true;
+   if(_config->FindB("Debug::NoDropPrivs", false) == true)
+      return true;
+
+   const std::string toUser = _config->Find("APT::Sandbox::User", "_apt");
+   struct passwd *pw = getpwnam(toUser.c_str());
+   if (pw == NULL)
+      return _error->Error("No user %s, can not drop rights", toUser.c_str());
+
+#if __gnu_linux__
+   // see prctl(2), needs linux3.5
+   int ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0,0, 0);
+   if(ret < 0)
+      _error->Warning("PR_SET_NO_NEW_PRIVS failed with %i", ret);
+#endif
+   // Do not change the order here, it might break things
+   if (setgroups(1, &pw->pw_gid))
+      return _error->Errno("setgroups", "Failed to setgroups");
+
+   if (setegid(pw->pw_gid) != 0)
+      return _error->Errno("setegid", "Failed to setegid");
+
+   if (setgid(pw->pw_gid) != 0)
+      return _error->Errno("setgid", "Failed to setgid");
+
+   if (setuid(pw->pw_uid) != 0)
+      return _error->Errno("setuid", "Failed to setuid");
+
+   // the seteuid() is probably uneeded (at least thats what the linux
+   // man-page says about setuid(2)) but we cargo culted it anyway
+   if (seteuid(pw->pw_uid) != 0)
+      return _error->Errno("seteuid", "Failed to seteuid");
+
+   // Verify that the user has only a single group, and the correct one
+   gid_t groups[1];
+   if (getgroups(1, groups) != 1)
+      return _error->Errno("getgroups", "Could not get new groups");
+   if (groups[0] != pw->pw_gid)
+      return _error->Error("Could not switch group");
+
+   // Verify that gid, egid, uid, and euid changed
+   if (getgid() != pw->pw_gid)
+      return _error->Error("Could not switch group");
+   if (getegid() != pw->pw_gid)
+      return _error->Error("Could not switch effective group");
+   if (getuid() != pw->pw_uid)
+      return _error->Error("Could not switch user");
+   if (geteuid() != pw->pw_uid)
+      return _error->Error("Could not switch effective user");
+
+#ifdef HAVE_GETRESUID
+   // verify that the saved set-user-id was changed as well
+   uid_t ruid = 0;
+   uid_t euid = 0;
+   uid_t suid = 0;
+   if (getresuid(&ruid, &euid, &suid))
+      return _error->Errno("getresuid", "Could not get saved set-user-ID");
+   if (suid != pw->pw_uid)
+      return _error->Error("Could not switch saved set-user-ID");
+#endif
+
+#ifdef HAVE_GETRESGID
+   // verify that the saved set-group-id was changed as well
+   gid_t rgid = 0;
+   gid_t egid = 0;
+   gid_t sgid = 0;
+   if (getresgid(&rgid, &egid, &sgid))
+      return _error->Errno("getresuid", "Could not get saved set-group-ID");
+   if (sgid != pw->pw_gid)
+      return _error->Error("Could not switch saved set-group-ID");
+#endif
+
+   // Check that uid and gid changes do not work anymore
+   if (pw->pw_gid != old_gid && (setgid(old_gid) != -1 || setegid(old_gid) != -1))
+      return _error->Error("Could restore a gid to root, privilege dropping did not work");
+
+   if (pw->pw_uid != old_uid && (setuid(old_uid) != -1 || seteuid(old_uid) != -1))
+      return _error->Error("Could restore a uid to root, privilege dropping did not work");
+
+   return true;
+}