#include <set>
#include <algorithm>
+// FIXME: Compressor Fds have some speed disadvantages and are a bit buggy currently,
+// so while the current implementation satisfies the testcases it is not a real option
+// to disable it for now
+#define APT_USE_ZLIB 1
+#ifdef APT_USE_ZLIB
#include <zlib.h>
+#endif
#ifdef WORDS_BIGENDIAN
#include <inttypes.h>
class FileFdPrivate {
public:
+#ifdef APT_USE_ZLIB
gzFile gz;
- FileFdPrivate() : gz(NULL) {};
+#else
+ void* gz;
+#endif
+ pid_t compressor_pid;
+ bool pipe;
+ APT::Configuration::Compressor compressor;
+ FileFd::OpenMode openmode;
+ FileFdPrivate() : gz(NULL), compressor_pid(-1), pipe(false) {};
};
// RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
}
/*}}}*/
+// ExecCompressor - Open a de/compressor pipe /*{{{*/
+// ---------------------------------------------------------------------
+/* This opens the compressor, either in compress mode or decompress
+ mode. FileFd is always the compressor input/output file,
+ OutFd is the created pipe, Input for Compress, Output for Decompress. */
+bool ExecCompressor(APT::Configuration::Compressor const &Prog,
+ pid_t *Pid, int const FileFd, int &OutFd, bool const Comp)
+{
+ if (Pid != NULL)
+ *Pid = -1;
+
+ // No compression
+ if (Prog.Binary.empty() == true)
+ {
+ OutFd = dup(FileFd);
+ return true;
+ }
+
+ // Handle 'decompression' of empty files
+ if (Comp == false)
+ {
+ struct stat Buf;
+ fstat(FileFd, &Buf);
+ if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
+ {
+ OutFd = FileFd;
+ return true;
+ }
+ }
+
+ // Create a data pipe
+ int Pipe[2] = {-1,-1};
+ if (pipe(Pipe) != 0)
+ return _error->Errno("pipe",_("Failed to create subprocess IPC"));
+ for (int J = 0; J != 2; J++)
+ SetCloseExec(Pipe[J],true);
+
+ if (Comp == true)
+ OutFd = Pipe[1];
+ else
+ OutFd = Pipe[0];
+
+ // The child..
+ pid_t child = ExecFork();
+ if (Pid != NULL)
+ *Pid = child;
+ if (child == 0)
+ {
+ if (Comp == true)
+ {
+ dup2(FileFd,STDOUT_FILENO);
+ dup2(Pipe[0],STDIN_FILENO);
+ }
+ else
+ {
+ dup2(FileFd,STDIN_FILENO);
+ dup2(Pipe[1],STDOUT_FILENO);
+ }
+
+ SetCloseExec(STDOUT_FILENO,false);
+ SetCloseExec(STDIN_FILENO,false);
+
+ std::vector<char const*> Args;
+ Args.push_back(Prog.Binary.c_str());
+ std::vector<std::string> const * const addArgs =
+ (Comp == true) ? &(Prog.CompressArgs) : &(Prog.UncompressArgs);
+ for (std::vector<std::string>::const_iterator a = addArgs->begin();
+ a != addArgs->end(); ++a)
+ Args.push_back(a->c_str());
+ Args.push_back(NULL);
+
+ execvp(Args[0],(char **)&Args[0]);
+ cerr << _("Failed to exec compressor ") << Args[0] << endl;
+ _exit(100);
+ }
+ if (Comp == true)
+ close(Pipe[0]);
+ else
+ close(Pipe[1]);
+
+ if (Pid == NULL)
+ ExecWait(child, Prog.Binary.c_str(), true);
+
+ return true;
+}
+bool ExecCompressor(APT::Configuration::Compressor const &Prog,
+ pid_t *Pid, std::string const &FileName, int &OutFd, bool const Comp)
+{
+ if (Pid != NULL)
+ *Pid = -1;
+
+ // No compression
+ if (Prog.Binary.empty() == true)
+ {
+ if (Comp == true)
+ OutFd = open(FileName.c_str(), O_WRONLY, 0666);
+ else
+ OutFd = open(FileName.c_str(), O_RDONLY);
+ return true;
+ }
+
+ // Handle 'decompression' of empty files
+ if (Comp == false)
+ {
+ struct stat Buf;
+ stat(FileName.c_str(), &Buf);
+ if (Buf.st_size == 0)
+ {
+ OutFd = open(FileName.c_str(), O_RDONLY);
+ return true;
+ }
+ }
+
+ // Create a data pipe
+ int Pipe[2] = {-1,-1};
+ if (pipe(Pipe) != 0)
+ return _error->Errno("pipe",_("Failed to create subprocess IPC"));
+ for (int J = 0; J != 2; J++)
+ SetCloseExec(Pipe[J],true);
+
+ if (Comp == true)
+ OutFd = Pipe[1];
+ else
+ OutFd = Pipe[0];
+
+ // The child..
+ pid_t child = ExecFork();
+ if (Pid != NULL)
+ *Pid = child;
+ if (child == 0)
+ {
+ if (Comp == true)
+ {
+ dup2(Pipe[0],STDIN_FILENO);
+ SetCloseExec(STDIN_FILENO,false);
+ }
+ else
+ {
+ dup2(Pipe[1],STDOUT_FILENO);
+ SetCloseExec(STDOUT_FILENO,false);
+ }
+
+ std::vector<char const*> Args;
+ Args.push_back(Prog.Binary.c_str());
+ std::vector<std::string> const * const addArgs =
+ (Comp == true) ? &(Prog.CompressArgs) : &(Prog.UncompressArgs);
+ for (std::vector<std::string>::const_iterator a = addArgs->begin();
+ a != addArgs->end(); ++a)
+ Args.push_back(a->c_str());
+ Args.push_back("--stdout");
+ Args.push_back(FileName.c_str());
+ Args.push_back(NULL);
+
+ execvp(Args[0],(char **)&Args[0]);
+ cerr << _("Failed to exec compressor ") << Args[0] << endl;
+ _exit(100);
+ }
+ if (Comp == true)
+ close(Pipe[0]);
+ else
+ close(Pipe[1]);
+
+ if (Pid == NULL)
+ ExecWait(child, Prog.Binary.c_str(), false);
+
+ return true;
+}
+ /*}}}*/
+
// FileFd::Open - Open a file /*{{{*/
// ---------------------------------------------------------------------
/* The most commonly used open mode combinations are given with Mode */
return Open(FileName, ReadOnly, Gzip, Perms);
Close();
d = new FileFdPrivate;
+ d->openmode = Mode;
Flags = AutoClose;
if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
// if we have them, use inbuilt compressors instead of forking
if (compressor != compressors.end())
{
+#ifdef APT_USE_ZLIB
if (compressor->Name == "gzip")
{
Compress = Gzip;
compressor = compressors.end();
}
- else if (compressor->Name == "." || Compress == None)
+ else
+#endif
+ if (compressor->Name == ".")
{
Compress = None;
compressor = compressors.end();
if (compressor != compressors.end())
{
if ((Mode & ReadWrite) == ReadWrite)
- _error->Error("External compressors like %s do not support readwrite mode for file %s", compressor->Name.c_str(), FileName.c_str());
+ return _error->Error("External compressors like %s do not support readwrite mode for file %s", compressor->Name.c_str(), FileName.c_str());
- _error->Error("Forking external compressor %s is not implemented for %s", compressor->Name.c_str(), FileName.c_str());
+ if (ExecCompressor(*compressor, NULL /*d->compressor_pid*/, FileName, iFd, ((Mode & ReadOnly) != ReadOnly)) == false)
+ return _error->Error("Forking external compressor %s is not implemented for %s", compressor->Name.c_str(), FileName.c_str());
+ d->pipe = true;
+ d->compressor = *compressor;
}
else
{
{
Close();
d = new FileFdPrivate;
+ d->openmode = Mode;
Flags = (AutoClose) ? FileFd::AutoClose : 0;
iFd = Fd;
if (OpenInternDescriptor(Mode, Compress) == false)
{
if (Compress == None)
return true;
+#ifdef APT_USE_ZLIB
else if (Compress == Gzip)
{
if ((Mode & ReadWrite) == ReadWrite)
return false;
Flags |= Compressed;
}
+#endif
else
- return false;
+ {
+ std::string name;
+ switch (Compress)
+ {
+ case Gzip: name = "gzip"; break;
+ case Bzip2: name = "bzip2"; break;
+ case Lzma: name = "lzma"; break;
+ case Xz: name = "xz"; break;
+ default: return _error->Error("Can't find a match for specified compressor mode for file %s", FileName.c_str());
+ }
+ std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
+ std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
+ for (; compressor != compressors.end(); ++compressor)
+ if (compressor->Name == name)
+ break;
+ if (compressor == compressors.end() ||
+ ExecCompressor(*compressor, NULL /*&(d->compressor_pid)*/,
+ FileName, iFd, ((Mode & ReadOnly) != ReadOnly)) == false)
+ return _error->Error("Forking external compressor %s is not implemented for %s", name.c_str(), FileName.c_str());
+ d->pipe = true;
+ }
return true;
}
/*}}}*/
errno = 0;
if (Actual != 0)
*Actual = 0;
-
+ *((char *)To) = '\0';
do
{
+#ifdef APT_USE_ZLIB
if (d->gz != NULL)
Res = gzread(d->gz,To,Size);
else
+#endif
Res = read(iFd,To,Size);
if (Res < 0 && errno == EINTR)
continue;
files because of the naive implementation! */
char* FileFd::ReadLine(char *To, unsigned long long const Size)
{
+ *To = '\0';
+#ifdef APT_USE_ZLIB
if (d->gz != NULL)
return gzgets(d->gz, To, Size);
+#endif
unsigned long long read = 0;
if (Read(To, Size, &read) == false)
errno = 0;
do
{
+#ifdef APT_USE_ZLIB
if (d->gz != NULL)
Res = gzwrite(d->gz,From,Size);
else
+#endif
Res = write(iFd,From,Size);
if (Res < 0 && errno == EINTR)
continue;
/* */
bool FileFd::Seek(unsigned long long To)
{
+ if (d->pipe == true)
+ {
+ // FIXME: What about OpenDescriptor() stuff here?
+ close(iFd);
+ bool result = ExecCompressor(d->compressor, NULL, FileName, iFd, (d->openmode & ReadOnly) != ReadOnly);
+ if (result == true && To != 0)
+ result &= Skip(To);
+ return result;
+ }
int res;
+#ifdef USE_ZLIB
if (d->gz)
res = gzseek(d->gz,To,SEEK_SET);
else
+#endif
res = lseek(iFd,To,SEEK_SET);
if (res != (signed)To)
{
bool FileFd::Skip(unsigned long long Over)
{
int res;
+#ifdef USE_ZLIB
if (d->gz != NULL)
res = gzseek(d->gz,Over,SEEK_CUR);
else
+#endif
res = lseek(iFd,Over,SEEK_CUR);
if (res < 0)
{
unsigned long long FileFd::Tell()
{
off_t Res;
+#ifdef USE_ZLIB
if (d->gz != NULL)
Res = gztell(d->gz);
else
+#endif
Res = lseek(iFd,0,SEEK_CUR);
if (Res == (off_t)-1)
_error->Errno("lseek","Failed to determine the current file position");
unsigned long long FileFd::FileSize()
{
struct stat Buf;
-
- if (fstat(iFd,&Buf) != 0)
+ if (d->pipe == false && fstat(iFd,&Buf) != 0)
return _error->Errno("fstat","Unable to determine the file size");
+
+ // for compressor pipes st_size is undefined and at 'best' zero
+ if (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
+ d->pipe = true;
+ if (stat(FileName.c_str(), &Buf) != 0)
+ return _error->Errno("stat","Unable to determine the file size");
+ }
+
return Buf.st_size;
}
/*}}}*/
{
unsigned long long size = FileSize();
+ // for compressor pipes st_size is undefined and at 'best' zero,
+ // so we 'read' the content and 'seek' back - see there
+ if (d->pipe == true)
+ {
+ // FIXME: If we have read first and then FileSize() the report is wrong
+ size = 0;
+ char ignore[1000];
+ unsigned long long read = 0;
+ do {
+ Read(ignore, sizeof(ignore), &read);
+ size += read;
+ } while(read != 0);
+ Seek(0);
+ }
+#ifdef USE_ZLIB
// only check gzsize if we are actually a gzip file, just checking for
// "gz" is not sufficient as uncompressed files could be opened with
// gzopen in "direct" mode as well
- if (d->gz && !gzdirect(d->gz) && size > 0)
+ else if (d->gz && !gzdirect(d->gz) && size > 0)
{
/* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
* this ourselves; the original (uncompressed) file size is the last 32
return _error->Errno("lseek","Unable to seek in gzipped file");
return size;
}
+#endif
return size;
}
time_t FileFd::ModificationTime()
{
struct stat Buf;
- if (fstat(iFd,&Buf) != 0)
+ if (d->pipe == false && fstat(iFd,&Buf) != 0)
{
_error->Errno("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->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
+ d->pipe = true;
+ if (stat(FileName.c_str(), &Buf) != 0)
+ {
+ _error->Errno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
+ return 0;
+ }
+ }
+
return Buf.st_mtime;
}
/*}}}*/
bool Res = true;
if ((Flags & AutoClose) == AutoClose)
{
+#ifdef USE_ZLIB
if (d != NULL && d->gz != NULL) {
int const e = gzclose(d->gz);
// gzdopen() 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());
} else
+#endif
if (iFd > 0 && close(iFd) != 0)
Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
}
if (d != NULL)
{
+// if (d->compressor_pid != -1)
+// ExecWait(d->compressor_pid, "FileFdCompressor", true);
delete d;
d = NULL;
}
return true;
}
/*}}}*/
-gzFile FileFd::gzFd() {return d->gz;};
+
+gzFile FileFd::gzFd() { return (gzFile) d->gz; }
using namespace std;
-// DecompressFile - wrapper for decompressing compressed files /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-bool DecompressFile(string Filename, int *fd, off_t *FileSize)
-{
- struct stat Buf;
- *fd = -1;
-
- std::vector<APT::Configuration::Compressor> const compressor = APT::Configuration::getCompressors();
- std::vector<APT::Configuration::Compressor>::const_iterator UnCompress;
- std::string file = std::string(Filename).append(UnCompress->Extension);
- for (UnCompress = compressor.begin(); UnCompress != compressor.end(); ++UnCompress)
- {
- if (stat(file.c_str(), &Buf) == 0)
- break;
- }
-
- if (UnCompress == compressor.end())
- return _error->Errno("decompressor", "Unable to parse file");
-
- *FileSize = Buf.st_size;
-
- // Create a data pipe
- int Pipe[2] = {-1,-1};
- if (pipe(Pipe) != 0)
- return _error->Errno("pipe",_("Failed to create subprocess IPC"));
- for (int J = 0; J != 2; J++)
- SetCloseExec(Pipe[J],true);
-
- *fd = Pipe[1];
-
- // The child..
- pid_t Pid = ExecFork();
- if (Pid == 0)
- {
- dup2(Pipe[1],STDOUT_FILENO);
- SetCloseExec(STDOUT_FILENO, false);
-
- std::vector<char const*> Args;
- Args.push_back(UnCompress->Binary.c_str());
- for (std::vector<std::string>::const_iterator a = UnCompress->UncompressArgs.begin();
- a != UnCompress->UncompressArgs.end(); ++a)
- Args.push_back(a->c_str());
- Args.push_back("--stdout");
- Args.push_back(file.c_str());
- Args.push_back(NULL);
-
- execvp(Args[0],(char **)&Args[0]);
- cerr << _("Failed to exec compressor ") << Args[0] << endl;
- _exit(100);
- }
-
- // Wait for decompress to finish
- if (ExecWait(Pid, UnCompress->Binary.c_str(), false) == false)
- return false;
-
- return true;
-}
- /*}}}*/
// IndexCopy::CopyPackages - Copy the package files from the CD /*{{{*/
// ---------------------------------------------------------------------
/* */
for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
{
string OrigPath = string(*I,CDROM.length());
- off_t FileSize = 0;
// Open the package file
- FileFd Pkg;
- if (RealFileExists(*I + GetFileName()) == true)
- {
- Pkg.Open(*I + GetFileName(),FileFd::ReadOnly);
- FileSize = Pkg.Size();
- }
- else
- {
- int fd;
- if (!DecompressFile(string(*I + GetFileName()), &fd, &FileSize))
- return _error->Errno("decompress","Decompress failed for %s",
- string(*I + GetFileName()).c_str());
- Pkg.Fd(dup(fd));
- Pkg.Seek(0);
- }
+ FileFd Pkg(*I + GetFileName(), FileFd::ReadOnly, FileFd::Extension);
+ off_t const FileSize = Pkg.Size();
pkgTagFile Parser(&Pkg);
if (_error->PendingError() == true)
for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
{
string OrigPath = string(*I,CDROM.length());
- off_t FileSize = 0;
-
+
// Open the package file
- FileFd Pkg;
- if (RealFileExists(*I) == true)
- {
- Pkg.Open(*I,FileFd::ReadOnly);
- FileSize = Pkg.Size();
- }
- else
- {
- int fd;
- if (!DecompressFile(*I, &fd, &FileSize))
- return _error->Errno("decompress","Decompress failed for %s", (*I).c_str());
- Pkg.Fd(dup(fd));
- Pkg.Seek(0);
- }
+ FileFd Pkg(*I, FileFd::ReadOnly, FileFd::Extension);
+ off_t const FileSize = Pkg.Size();
+
pkgTagFile Parser(&Pkg);
if (_error->PendingError() == true)
return false;
// Include Files /*{{{*/
#include <config.h>
+#include <apt-pkg/fileutl.h>
#include <apt-pkg/strutl.h>
#include <apt-pkg/error.h>
#include <apt-pkg/md5.h>
return true;
}
/*}}}*/
-// MultiCompress::OpenCompress - Open the compressor /*{{{*/
-// ---------------------------------------------------------------------
-/* This opens the compressor, either in compress mode or decompress
- mode. FileFd is always the compressor input/output file,
- OutFd is the created pipe, Input for Compress, Output for Decompress. */
-bool MultiCompress::OpenCompress(APT::Configuration::Compressor const &Prog,
- pid_t &Pid,int const &FileFd,int &OutFd,bool const &Comp)
-{
- Pid = -1;
-
- // No compression
- if (Prog.Binary.empty() == true)
- {
- OutFd = dup(FileFd);
- return true;
- }
-
- // Create a data pipe
- int Pipe[2] = {-1,-1};
- if (pipe(Pipe) != 0)
- return _error->Errno("pipe",_("Failed to create subprocess IPC"));
- for (int J = 0; J != 2; J++)
- SetCloseExec(Pipe[J],true);
-
- if (Comp == true)
- OutFd = Pipe[1];
- else
- OutFd = Pipe[0];
-
- // The child..
- Pid = ExecFork();
- if (Pid == 0)
- {
- if (Comp == true)
- {
- dup2(FileFd,STDOUT_FILENO);
- dup2(Pipe[0],STDIN_FILENO);
- }
- else
- {
- dup2(FileFd,STDIN_FILENO);
- dup2(Pipe[1],STDOUT_FILENO);
- }
-
- SetCloseExec(STDOUT_FILENO,false);
- SetCloseExec(STDIN_FILENO,false);
-
- std::vector<char const*> Args;
- Args.push_back(Prog.Binary.c_str());
- std::vector<std::string> const * const addArgs =
- (Comp == true) ? &(Prog.CompressArgs) : &(Prog.UncompressArgs);
- for (std::vector<std::string>::const_iterator a = addArgs->begin();
- a != addArgs->end(); ++a)
- Args.push_back(a->c_str());
- Args.push_back(NULL);
-
- execvp(Args[0],(char **)&Args[0]);
- cerr << _("Failed to exec compressor ") << Args[0] << endl;
- _exit(100);
- };
- if (Comp == true)
- close(Pipe[0]);
- else
- close(Pipe[1]);
- return true;
-}
- /*}}}*/
// MultiCompress::OpenOld - Open an old file /*{{{*/
// ---------------------------------------------------------------------
/* This opens one of the original output files, possibly decompressing it. */
return false;
// Decompress the file so we can read it
- if (OpenCompress(Best->CompressProg,Proc,F.Fd(),Fd,false) == false)
+ if (ExecCompressor(Best->CompressProg,&Proc,F.Fd(),Fd,false) == false)
return false;
return true;
// Start the compression children.
for (Files *I = Outputs; I != 0; I = I->Next)
{
- if (OpenCompress(I->CompressProg,I->CompressProc,I->TmpFile.Fd(),
- I->Fd,true) == false)
+ if (ExecCompressor(I->CompressProg,&(I->CompressProc),I->TmpFile.Fd(),
+ I->Fd,true) == false)
return false;
}