Most of this source is placed in the Public Domain, do with it what
you will
It was originally written by Jason Gunthorpe <jgg@debian.org>.
+ FileFd gzip support added by Martin Pitt <martin.pitt@canonical.com>
The exception is RunScripts() it is under the GPLv2
##################################################################### */
/*}}}*/
// Include Files /*{{{*/
+#include <config.h>
+
#include <apt-pkg/fileutl.h>
#include <apt-pkg/strutl.h>
#include <apt-pkg/error.h>
#include <apt-pkg/sptr.h>
+#include <apt-pkg/aptconfiguration.h>
#include <apt-pkg/configuration.h>
-#include <apti18n.h>
-
#include <cstdlib>
#include <cstring>
+#include <cstdio>
#include <iostream>
#include <unistd.h>
#include <errno.h>
#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
+#if APT_USE_ZLIB
+#include <zlib.h>
+#else
+#warning "Usage of zlib is DISABLED!"
+#endif
+
+#ifdef WORDS_BIGENDIAN
+#include <inttypes.h>
+#endif
+
+#include <apti18n.h>
/*}}}*/
using namespace std;
+class FileFdPrivate {
+ public:
+#if APT_USE_ZLIB
+ gzFile gz;
+#else
+ void* gz;
+#endif
+ pid_t compressor_pid;
+ bool pipe;
+ APT::Configuration::Compressor compressor;
+ unsigned int openmode;
+ FileFdPrivate() : gz(NULL), compressor_pid(-1), pipe(false) {};
+};
+
// RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
// ---------------------------------------------------------------------
/* */
// This is the child
if (Child == 0)
{
+ if (_config->FindDir("DPkg::Chroot-Directory","/") != "/")
+ {
+ std::cerr << "Chrooting into "
+ << _config->FindDir("DPkg::Chroot-Directory")
+ << std::endl;
+ if (chroot(_config->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
+ _exit(100);
+ }
+
if (chdir("/tmp/") != 0)
_exit(100);
// Buffered copy between fds
SPtrArray<unsigned char> Buf = new unsigned char[64000];
- unsigned long Size = From.Size();
+ unsigned long long Size = From.Size();
while (Size != 0)
{
- unsigned long ToRead = Size;
+ unsigned long long ToRead = Size;
if (Size > 64000)
ToRead = 64000;
/*}}}*/
// FileExists - Check if a file exists /*{{{*/
// ---------------------------------------------------------------------
-/* */
+/* Beware: Directories are also files! */
bool FileExists(string File)
{
struct stat Buf;
return true;
}
/*}}}*/
+// RealFileExists - Check if a file exists and if it is really a file /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool RealFileExists(string File)
+{
+ struct stat Buf;
+ if (stat(File.c_str(),&Buf) != 0)
+ return false;
+ return ((Buf.st_mode & S_IFREG) != 0);
+}
+ /*}}}*/
// DirectoryExists - Check if a directory exists and is really one /*{{{*/
// ---------------------------------------------------------------------
/* */
return true;
}
/*}}}*/
+// CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
+// ---------------------------------------------------------------------
+/* a small wrapper around CreateDirectory to check if it exists and to
+ remove the trailing "/apt/" from the parent directory if needed */
+bool CreateAPTDirectoryIfNeeded(string const &Parent, string const &Path)
+{
+ if (DirectoryExists(Path) == true)
+ return true;
+
+ size_t const len = Parent.size();
+ if (len > 5 && Parent.find("/apt/", len - 6, 5) == len - 5)
+ {
+ if (CreateDirectory(Parent.substr(0,len-5), Path) == true)
+ return true;
+ }
+ else if (CreateDirectory(Parent, Path) == true)
+ return true;
+
+ return false;
+}
+ /*}}}*/
// GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
// ---------------------------------------------------------------------
/* If an extension is given only files with this extension are included
}
std::vector<string> List;
+
+ if (DirectoryExists(Dir.c_str()) == false)
+ {
+ _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
+ return List;
+ }
+
+ Configuration::MatchAgainstConfig SilentIgnore("Dir::Ignore-Files-Silently");
DIR *D = opendir(Dir.c_str());
if (D == 0)
{
if (Ent->d_name[0] == '.')
continue;
+ // Make sure it is a file and not something else
+ string const File = flCombine(Dir,Ent->d_name);
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (Ent->d_type != DT_REG)
+#endif
+ {
+ if (RealFileExists(File.c_str()) == false)
+ {
+ if (SilentIgnore.Match(Ent->d_name) == false)
+ _error->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent->d_name, Dir.c_str());
+ continue;
+ }
+ }
+
// check for accepted extension:
// no extension given -> periods are bad as hell!
// extensions given -> "" extension allows no extension
{
if (Debug == true)
std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
+ if (SilentIgnore.Match(Ent->d_name) == false)
+ _error->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent->d_name, Dir.c_str());
continue;
}
}
{
if (Debug == true)
std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
+ if (SilentIgnore.Match(Ent->d_name) == false)
+ _error->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent->d_name, Dir.c_str());
continue;
}
}
continue;
}
- // Make sure it is a file and not something else
- string const File = flCombine(Dir,Ent->d_name);
- struct stat St;
- if (stat(File.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0)
- {
- if (Debug == true)
- std::clog << "Bad file: " << Ent->d_name << " → stat says not a good file" << std::endl;
- continue;
- }
-
if (Debug == true)
std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
List.push_back(File);
return S;
}
/*}}}*/
+// GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
+// ---------------------------------------------------------------------
+/* We return / on failure. */
+time_t GetModificationTime(string const &Path)
+{
+ struct stat St;
+ if (stat(Path.c_str(), &St) < 0)
+ return -1;
+ return St.st_mtime;
+}
+ /*}}}*/
// flNotDir - Strip the directory from the filename /*{{{*/
// ---------------------------------------------------------------------
/* */
}
/*}}}*/
+// 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);
+
+ int FileFd = -1;
+ if (Comp == true)
+ {
+ OutFd = Pipe[1];
+ // FIXME: we should handle openmode and permission from Open() here
+ FileFd = open(FileName.c_str(), O_WRONLY, 0666);
+ }
+ 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);
+ dup2(FileFd,STDOUT_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());
+ if (Comp == false)
+ {
+ 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]);
+ close(FileFd);
+ }
+ 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 */
-bool FileFd::Open(string FileName,OpenMode Mode, unsigned long Perms)
+bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const Perms)
{
- Close();
- Flags = AutoClose;
- switch (Mode)
+ if (Mode == ReadOnlyGzip)
+ return Open(FileName, ReadOnly, Gzip, Perms);
+
+ if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
+ return _error->Error("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
+
+ // FIXME: Denote inbuilt compressors somehow - as we don't need to have the binaries for them
+ std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
+ std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
+ if (Compress == Auto)
{
- case ReadOnly:
- iFd = open(FileName.c_str(),O_RDONLY);
- break;
-
- case WriteEmpty:
+ for (; compressor != compressors.end(); ++compressor)
{
- struct stat Buf;
- if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
- unlink(FileName.c_str());
- iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
+ std::string file = std::string(FileName).append(compressor->Extension);
+ if (FileExists(file) == false)
+ continue;
+ FileName = file;
break;
}
-
- case WriteExists:
- iFd = open(FileName.c_str(),O_RDWR);
- break;
+ }
+ else if (Compress == Extension)
+ {
+ std::string::size_type const found = FileName.find_last_of('.');
+ std::string ext;
+ if (found != std::string::npos)
+ {
+ ext = FileName.substr(found);
+ if (ext == ".new" || ext == ".bak")
+ {
+ std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
+ if (found2 != std::string::npos)
+ ext = FileName.substr(found2, found - found2);
+ else
+ ext.clear();
+ }
+ }
+ for (; compressor != compressors.end(); ++compressor)
+ if (ext == compressor->Extension)
+ break;
+ // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
+ if (compressor == compressors.end())
+ for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
+ if (compressor->Name == ".")
+ break;
+ }
+ else
+ {
+ std::string name;
+ switch (Compress)
+ {
+ case None: name = "."; break;
+ case Gzip: name = "gzip"; break;
+ case Bzip2: name = "bzip2"; break;
+ case Lzma: name = "lzma"; break;
+ case Xz: name = "xz"; break;
+ case Auto:
+ case Extension:
+ // Unreachable
+ return _error->Error("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
+ }
+ for (; compressor != compressors.end(); ++compressor)
+ if (compressor->Name == name)
+ break;
+ if (compressor == compressors.end())
+ return _error->Error("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
+ }
+
+ if (compressor == compressors.end())
+ return _error->Error("Can't find a match for specified compressor mode for file %s", FileName.c_str());
+ return Open(FileName, Mode, *compressor, Perms);
+}
+bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const Perms)
+{
+ Close();
+ d = new FileFdPrivate;
+ d->openmode = Mode;
+ Flags = AutoClose;
- case WriteAny:
- iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
- break;
+ if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
+ return _error->Error("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
+ if ((Mode & ReadWrite) == 0)
+ return _error->Error("No openmode provided in FileFd::Open for %s", FileName.c_str());
- case WriteTemp:
+ if ((Mode & Atomic) == Atomic)
+ {
+ Flags |= Replace;
+ char *name = strdup((FileName + ".XXXXXX").c_str());
+ TemporaryFileName = string(mktemp(name));
+ free(name);
+ }
+ else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
+ {
+ // for atomic, this will be done by rename in Close()
unlink(FileName.c_str());
- iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
- break;
- }
+ }
+ if ((Mode & Empty) == Empty)
+ {
+ struct stat Buf;
+ if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
+ unlink(FileName.c_str());
+ }
+
+ // if we have them, use inbuilt compressors instead of forking
+ if (compressor.Name != "."
+#if APT_USE_ZLIB
+ && compressor.Name != "gzip"
+#endif
+ )
+ {
+ if ((Mode & ReadWrite) == ReadWrite)
+ return _error->Error("External compressors like %s do not support readwrite mode for file %s", compressor.Name.c_str(), FileName.c_str());
+
+ if ((Mode & (WriteOnly | Create)) == (WriteOnly | Create))
+ {
+ if (TemporaryFileName.empty() == false)
+ {
+ if (RealFileExists(TemporaryFileName) == false)
+ {
+ iFd = open(TemporaryFileName.c_str(), O_WRONLY | O_CREAT, Perms);
+ close(iFd);
+ iFd = -1;
+ }
+ }
+ else if (RealFileExists(FileName) == false)
+ {
+ iFd = open(FileName.c_str(), O_WRONLY | O_CREAT, Perms);
+ close(iFd);
+ iFd = -1;
+ }
+ }
+
+ if (TemporaryFileName.empty() == false)
+ {
+ if (ExecCompressor(compressor, &(d->compressor_pid), TemporaryFileName, iFd, ((Mode & ReadOnly) != ReadOnly)) == false)
+ return _error->Error("Forking external compressor %s is not implemented for %s", compressor.Name.c_str(), TemporaryFileName.c_str());
+ }
+ else
+ {
+ if (ExecCompressor(compressor, &(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
+ {
+ int fileflags = 0;
+ #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
+ if_FLAGGED_SET(ReadWrite, O_RDWR);
+ else if_FLAGGED_SET(ReadOnly, O_RDONLY);
+ else if_FLAGGED_SET(WriteOnly, O_WRONLY);
+
+ if_FLAGGED_SET(Create, O_CREAT);
+ if_FLAGGED_SET(Exclusive, O_EXCL);
+ else if_FLAGGED_SET(Atomic, O_EXCL);
+ if_FLAGGED_SET(Empty, O_TRUNC);
+ #undef if_FLAGGED_SET
+
+ if (TemporaryFileName.empty() == false)
+ iFd = open(TemporaryFileName.c_str(), fileflags, Perms);
+ else
+ iFd = open(FileName.c_str(), fileflags, Perms);
+
+ if (iFd != -1)
+ {
+ if (OpenInternDescriptor(Mode, compressor) == false)
+ {
+ close (iFd);
+ iFd = -1;
+ }
+ }
+ }
- if (iFd < 0)
+ if (iFd == -1)
return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
-
+
this->FileName = FileName;
SetCloseExec(iFd,true);
return true;
}
/*}}}*/
+// FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
+{
+ std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
+ std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
+ std::string name;
+ switch (Compress)
+ {
+ case None: name = "."; break;
+ case Gzip: name = "gzip"; break;
+ case Bzip2: name = "bzip2"; break;
+ case Lzma: name = "lzma"; break;
+ case Xz: name = "xz"; break;
+ case Auto:
+ case Extension:
+ return _error->Error("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
+ }
+ for (; compressor != compressors.end(); ++compressor)
+ if (compressor->Name == name)
+ break;
+ if (compressor == compressors.end())
+ return _error->Error("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
+
+ return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
+}
+bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
+{
+ Close();
+ d = new FileFdPrivate;
+ d->openmode = Mode;
+ Flags = (AutoClose) ? FileFd::AutoClose : 0;
+ iFd = Fd;
+ if (OpenInternDescriptor(Mode, compressor) == false)
+ {
+ if (AutoClose)
+ close (iFd);
+ return _error->Errno("gzdopen",_("Could not open file descriptor %d"), Fd);
+ }
+ this->FileName = "";
+ return true;
+}
+bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
+{
+ if (compressor.Name == ".")
+ return true;
+#if APT_USE_ZLIB
+ else if (compressor.Name == "gzip")
+ {
+ if ((Mode & ReadWrite) == ReadWrite)
+ d->gz = gzdopen(iFd, "r+");
+ else if ((Mode & WriteOnly) == WriteOnly)
+ d->gz = gzdopen(iFd, "w");
+ else
+ d->gz = gzdopen (iFd, "r");
+ if (d->gz == NULL)
+ return false;
+ Flags |= Compressed;
+ }
+#endif
+ else
+ return _error->Error("Can't find a match for specified compressor %s for file %s", compressor.Name.c_str(), FileName.c_str());
+ return true;
+}
+ /*}}}*/
// FileFd::~File - Closes the file /*{{{*/
// ---------------------------------------------------------------------
/* If the proper modes are selected then we close the Fd and possibly
// ---------------------------------------------------------------------
/* We are carefull to handle interruption by a signal while reading
gracefully. */
-bool FileFd::Read(void *To,unsigned long Size,unsigned long *Actual)
+bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
{
int Res;
errno = 0;
if (Actual != 0)
*Actual = 0;
-
+ *((char *)To) = '\0';
do
{
- Res = read(iFd,To,Size);
+#if 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;
if (Res < 0)
}
Flags |= Fail;
- return _error->Error(_("read, still have %lu to read but none left"),Size);
+ return _error->Error(_("read, still have %llu to read but none left"), Size);
+}
+ /*}}}*/
+// FileFd::ReadLine - Read a complete line from the file /*{{{*/
+// ---------------------------------------------------------------------
+/* Beware: This method can be quiet slow for big buffers on UNcompressed
+ files because of the naive implementation! */
+char* FileFd::ReadLine(char *To, unsigned long long const Size)
+{
+ *To = '\0';
+#if 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)
+ return NULL;
+ char* c = To;
+ for (; *c != '\n' && *c != '\0' && read != 0; --read, ++c)
+ ; // find the end of the line
+ if (*c != '\0')
+ *c = '\0';
+ if (read != 0)
+ Seek(Tell() - read);
+ return To;
}
/*}}}*/
// FileFd::Write - Write to the file /*{{{*/
// ---------------------------------------------------------------------
/* */
-bool FileFd::Write(const void *From,unsigned long Size)
+bool FileFd::Write(const void *From,unsigned long long Size)
{
int Res;
errno = 0;
do
{
- Res = write(iFd,From,Size);
+#if 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;
if (Res < 0)
return true;
Flags |= Fail;
- return _error->Error(_("write, still have %lu to write but couldn't"),Size);
+ return _error->Error(_("write, still have %llu to write but couldn't"), Size);
}
/*}}}*/
// FileFd::Seek - Seek in the file /*{{{*/
// ---------------------------------------------------------------------
/* */
-bool FileFd::Seek(unsigned long To)
+bool FileFd::Seek(unsigned long long To)
{
- if (lseek(iFd,To,SEEK_SET) != (signed)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;
+#if APT_USE_ZLIB
+ if (d->gz)
+ res = gzseek(d->gz,To,SEEK_SET);
+ else
+#endif
+ res = lseek(iFd,To,SEEK_SET);
+ if (res != (signed)To)
{
Flags |= Fail;
- return _error->Error("Unable to seek to %lu",To);
+ return _error->Error("Unable to seek to %llu", To);
}
return true;
// FileFd::Skip - Seek in the file /*{{{*/
// ---------------------------------------------------------------------
/* */
-bool FileFd::Skip(unsigned long Over)
+bool FileFd::Skip(unsigned long long Over)
{
- if (lseek(iFd,Over,SEEK_CUR) < 0)
+ int res;
+#if APT_USE_ZLIB
+ if (d->gz != NULL)
+ res = gzseek(d->gz,Over,SEEK_CUR);
+ else
+#endif
+ res = lseek(iFd,Over,SEEK_CUR);
+ if (res < 0)
{
Flags |= Fail;
- return _error->Error("Unable to seek ahead %lu",Over);
+ return _error->Error("Unable to seek ahead %llu",Over);
}
return true;
// FileFd::Truncate - Truncate the file /*{{{*/
// ---------------------------------------------------------------------
/* */
-bool FileFd::Truncate(unsigned long To)
+bool FileFd::Truncate(unsigned long long To)
{
+ if (d->gz != NULL)
+ {
+ Flags |= Fail;
+ return _error->Error("Truncating gzipped files is not implemented (%s)", FileName.c_str());
+ }
if (ftruncate(iFd,To) != 0)
{
Flags |= Fail;
- return _error->Error("Unable to truncate to %lu",To);
+ return _error->Error("Unable to truncate to %llu",To);
}
return true;
// FileFd::Tell - Current seek position /*{{{*/
// ---------------------------------------------------------------------
/* */
-unsigned long FileFd::Tell()
+unsigned long long FileFd::Tell()
{
- off_t Res = lseek(iFd,0,SEEK_CUR);
+ off_t Res;
+#if APT_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");
return Res;
}
/*}}}*/
-// FileFd::Size - Return the size of the file /*{{{*/
+// FileFd::FileSize - Return the size of the file /*{{{*/
// ---------------------------------------------------------------------
/* */
-unsigned long FileFd::Size()
+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;
}
/*}}}*/
+// FileFd::Size - Return the size of the content in the file /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+unsigned long long FileFd::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);
+ }
+#if APT_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
+ 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
+ * bits of the file */
+ // FIXME: Size for gz-files is limited by 32bit… no largefile support
+ off_t orig_pos = lseek(iFd, 0, SEEK_CUR);
+ if (lseek(iFd, -4, SEEK_END) < 0)
+ return _error->Errno("lseek","Unable to seek to end of gzipped file");
+ size = 0L;
+ if (read(iFd, &size, 4) != 4)
+ return _error->Errno("read","Unable to read original size of gzipped file");
+
+#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
+
+ if (lseek(iFd, orig_pos, SEEK_SET) < 0)
+ return _error->Errno("lseek","Unable to seek in gzipped file");
+ return size;
+ }
+#endif
+
+ return size;
+}
+ /*}}}*/
+// FileFd::ModificationTime - Return the time of last touch /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+time_t FileFd::ModificationTime()
+{
+ struct stat Buf;
+ 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;
+}
+ /*}}}*/
// FileFd::Close - Close the file if the close flag is set /*{{{*/
// ---------------------------------------------------------------------
/* */
bool FileFd::Close()
{
+ if (iFd == -1)
+ return true;
+
bool Res = true;
if ((Flags & AutoClose) == AutoClose)
- if (iFd >= 0 && close(iFd) != 0)
- Res &= _error->Errno("close",_("Problem closing the file"));
+ {
+#if APT_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 ((Flags & Replace) == Replace && iFd >= 0) {
+ if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
+ Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
+
+ FileName = TemporaryFileName; // for the unlink() below.
+ TemporaryFileName.clear();
+ }
+
iFd = -1;
-
+
if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
FileName.empty() == false)
if (unlink(FileName.c_str()) != 0)
- Res &= _error->WarningE("unlnk",_("Problem unlinking the file"));
+ Res &= _error->WarningE("unlnk",_("Problem unlinking 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 Res;
}
/*}}}*/
return true;
}
/*}}}*/
+
+gzFile FileFd::gzFd() { return (gzFile) d->gz; }