// -*- 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>
-// 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>
+#ifdef HAVE_ZLIB
+ #include <zlib.h>
+#endif
+#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 APT_USE_ZLIB
- gzFile gz;
-#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 /*{{{*/
// ---------------------------------------------------------------------
/* */
{
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);
}
/* The caller is expected to set things so that failure causes erasure */
bool CopyFile(FileFd &From,FileFd &To)
{
- if (From.IsOpen() == false || To.IsOpen() == false)
+ if (From.IsOpen() == false || To.IsOpen() == false ||
+ From.Failed() == true || To.Failed() == true)
return false;
// Buffered copy between fds
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());
}
SetCloseExec(FD,true);
- // Aquire a write lock
+ // Acquire a write lock
struct flock fl;
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
fl.l_len = 0;
if (fcntl(FD,F_SETLK,&fl) == -1)
{
+ // always close to not leak resources
+ int Tmp = errno;
+ close(FD);
+ errno = Tmp;
+
if (errno == ENOLCK)
{
_error->Warning(_("Not using locking for nfs mounted lock file %s"),File.c_str());
return dup(0); // Need something for the caller to close
- }
+ }
+
if (Errors == true)
_error->Errno("open",_("Could not get lock %s"),File.c_str());
- int Tmp = errno;
- close(FD);
- errno = Tmp;
return -1;
}
return false;
// we are not going to create directories "into the blue"
- if (Path.find(Parent, 0) != 0)
+ if (Path.compare(0, Parent.length(), Parent) != 0)
return false;
vector<string> const dirs = VectorizeString(Path.substr(Parent.size()), '/');
std::vector<string> List;
- if (DirectoryExists(Dir.c_str()) == false)
+ if (DirectoryExists(Dir) == false)
{
_error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
return List;
if (Ent->d_type != DT_REG)
#endif
{
- if (RealFileExists(File.c_str()) == false)
+ if (RealFileExists(File) == false)
{
+ // do not show ignoration warnings for directories
+ if (
+#ifdef _DIRENT_HAVE_D_TYPE
+ Ent->d_type == DT_DIR ||
+#endif
+ DirectoryExists(File) == true)
+ continue;
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;
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;
}
closedir(D);
+ if (SortList == true)
+ std::sort(List.begin(),List.end());
+ return List;
+}
+std::vector<string> GetListOfFilesInDir(string const &Dir, bool SortList)
+{
+ bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
+ if (Debug == true)
+ std::clog << "Accept in " << Dir << " all regular files" << std::endl;
+
+ std::vector<string> List;
+
+ if (DirectoryExists(Dir) == false)
+ {
+ _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
+ return List;
+ }
+
+ DIR *D = opendir(Dir.c_str());
+ if (D == 0)
+ {
+ _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
+ return List;
+ }
+
+ for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
+ {
+ // skip "hidden" files
+ 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) == false)
+ {
+ if (Debug == true)
+ std::clog << "Bad file: " << Ent->d_name << " → it is not a real file" << std::endl;
+ continue;
+ }
+ }
+
+ // Skip bad filenames ala run-parts
+ const char *C = Ent->d_name;
+ for (; *C != 0; ++C)
+ if (isalpha(*C) == 0 && isdigit(*C) == 0
+ && *C != '_' && *C != '-' && *C != '.')
+ break;
+
+ // we don't reach the end of the name -> bad character included
+ if (*C != 0)
+ {
+ if (Debug == true)
+ std::clog << "Bad file: " << Ent->d_name << " → bad character »" << *C << "« in filename" << std::endl;
+ continue;
+ }
+
+ // skip filenames which end with a period. These are never valid
+ if (*(C - 1) == '.')
+ {
+ if (Debug == true)
+ std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
+ continue;
+ }
+
+ if (Debug == true)
+ std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
+ List.push_back(File);
+ }
+ closedir(D);
+
if (SortList == true)
std::sort(List.begin(),List.end());
return List;
while (1)
{
// Read the link
- int Res;
+ ssize_t Res;
if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
- (unsigned)Res >= sizeof(Buffer))
+ (size_t)Res >= sizeof(Buffer))
return File;
// Append or replace the previous path
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 /*{{{*/
// ---------------------------------------------------------------------
/* */
return true;
}
/*}}}*/
+// MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
+// ---------------------------------------------------------------------
+/* This is used to merge the APT::Keep-Fds with the provided KeepFDs
+ * set.
+ */
+void MergeKeepFdsFromConfiguration(std::set<int> &KeepFDs)
+{
+ Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
+ if (Opts != 0 && Opts->Child != 0)
+ {
+ Opts = Opts->Child;
+ for (; Opts != 0; Opts = Opts->Next)
+ {
+ if (Opts->Value.empty() == true)
+ continue;
+ int fd = atoi(Opts->Value.c_str());
+ KeepFDs.insert(fd);
+ }
+ }
+}
+ /*}}}*/
// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
// ---------------------------------------------------------------------
/* This is used if you want to cleanse the environment for the forked
child, it fixes up the important signals and nukes all of the fds,
otherwise acts like normal fork. */
pid_t ExecFork()
+{
+ set<int> KeepFDs;
+ // we need to merge the Keep-Fds as external tools like
+ // debconf-apt-progress use it
+ MergeKeepFdsFromConfiguration(KeepFDs);
+ return ExecFork(KeepFDs);
+}
+
+pid_t ExecFork(std::set<int> KeepFDs)
{
// Fork off the process
pid_t Process = fork();
signal(SIGCONT,SIG_DFL);
signal(SIGTSTP,SIG_DFL);
- set<int> KeepFDs;
- Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
- if (Opts != 0 && Opts->Child != 0)
- {
- Opts = Opts->Child;
- for (; Opts != 0; Opts = Opts->Next)
- {
- if (Opts->Value.empty() == true)
- continue;
- int fd = atoi(Opts->Value.c_str());
- KeepFDs.insert(fd);
- }
- }
-
+ long ScOpenMax = sysconf(_SC_OPEN_MAX);
// Close all of our FDs - just in case
- for (int K = 3; K != 40; K++)
+ for (int K = 3; K != ScOpenMax; K++)
{
if(KeepFDs.find(K) == KeepFDs.end())
fcntl(K,F_SETFD,FD_CLOEXEC);
return true;
}
/*}}}*/
-
-// 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)
+// StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned /*{{{*/
+bool StartsWithGPGClearTextSignature(string const &FileName)
{
- if (Pid != NULL)
- *Pid = -1;
+ 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;
- // No compression
- if (Prog.Binary.empty() == true)
- {
- OutFd = dup(FileFd);
- return true;
- }
+ char const * const test = fgets(buffer, sizeof(buffer), gpg);
+ fclose(gpg);
+ if (test == NULL || strcmp(buffer, SIGMSG) != 0)
+ return false;
- // Handle 'decompression' of empty files
- if (Comp == false)
+ return true;
+}
+ /*}}}*/
+// ChangeOwnerAndPermissionOfFile - set file attributes to requested values /*{{{*/
+bool ChangeOwnerAndPermissionOfFile(char const * const requester, char const * const file, char const * const user, char const * const group, mode_t const mode)
+{
+ if (strcmp(file, "/dev/null") == 0)
+ return true;
+ bool Res = true;
+ if (getuid() == 0 && strlen(user) != 0 && strlen(group) != 0) // if we aren't root, we can't chown, so don't try it
{
- struct stat Buf;
- fstat(FileFd, &Buf);
- if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
- {
- OutFd = FileFd;
- return true;
- }
+ // ensure the file is owned by root and has good permissions
+ struct passwd const * const pw = getpwnam(user);
+ struct group const * const gr = getgrnam(group);
+ if (pw != NULL && gr != NULL && chown(file, pw->pw_uid, gr->gr_gid) != 0)
+ Res &= _error->WarningE(requester, "chown to %s:%s of file %s failed", user, group, file);
}
+ if (chmod(file, mode) != 0)
+ Res &= _error->WarningE(requester, "chmod 0%o of file %s failed", mode, file);
+ return Res;
+}
+ /*}}}*/
- // 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);
+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
+ ;
+ }
- if (Comp == true)
- OutFd = Pipe[1];
- else
- OutFd = Pipe[0];
- // The child..
- pid_t child = ExecFork();
- if (Pid != NULL)
- *Pid = child;
- if (child == 0)
+ ~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 AccessMode)
+{
+ if (Mode == ReadOnlyGzip)
+ 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());
+
+ std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
+ std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
+ if (Compress == Auto)
{
- if (Comp == true)
+ for (; compressor != compressors.end(); ++compressor)
{
- dup2(FileFd,STDOUT_FILENO);
- dup2(Pipe[0],STDIN_FILENO);
+ std::string file = FileName + compressor->Extension;
+ if (FileExists(file) == false)
+ continue;
+ FileName = file;
+ break;
}
- else
+ }
+ else if (Compress == Extension)
+ {
+ std::string::size_type const found = FileName.find_last_of('.');
+ std::string ext;
+ if (found != std::string::npos)
{
- dup2(FileFd,STDIN_FILENO);
- dup2(Pipe[1],STDOUT_FILENO);
+ 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();
+ }
}
-
- 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);
+ 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;
}
- if (Comp == true)
- close(Pipe[0]);
else
- close(Pipe[1]);
-
- if (Pid == NULL)
- ExecWait(child, Prog.Binary.c_str(), true);
+ {
+ 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 FileFdError("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 FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
+ }
- return true;
+ 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, AccessMode);
}
-bool ExecCompressor(APT::Configuration::Compressor const &Prog,
- pid_t *Pid, std::string const &FileName, int &OutFd, bool const Comp)
+bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const AccessMode)
{
- if (Pid != NULL)
- *Pid = -1;
+ Close();
+ Flags = AutoClose;
+
+ if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
+ return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
+ if ((Mode & ReadWrite) == 0)
+ return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
- // No compression
- if (Prog.Binary.empty() == true)
+ if ((Mode & Atomic) == Atomic)
{
- if (Comp == true)
- OutFd = open(FileName.c_str(), O_WRONLY, 0666);
- else
- OutFd = open(FileName.c_str(), O_RDONLY);
- return true;
+ Flags |= Replace;
}
-
- // Handle 'decompression' of empty files
- if (Comp == false)
+ else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
{
- struct stat Buf;
- stat(FileName.c_str(), &Buf);
- if (Buf.st_size == 0)
- {
- OutFd = open(FileName.c_str(), O_RDONLY);
- return true;
- }
+ // for atomic, this will be done by rename in Close()
+ unlink(FileName.c_str());
}
-
- // 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 ((Mode & Empty) == Empty)
{
- 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);
+ struct stat Buf;
+ if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
+ unlink(FileName.c_str());
}
- 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 */
-bool FileFd::Open(string FileName,OpenMode Mode,CompressMode Compress, unsigned long const Perms)
-{
- if (Mode == ReadOnlyGzip)
- return Open(FileName, ReadOnly, Gzip, Perms);
- Close();
- d = new FileFdPrivate;
- d->openmode = Mode;
- Flags = AutoClose;
-
- if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
- return _error->Error("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
- 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());
int fileflags = 0;
-#define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
+ #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);
- else return _error->Error("No openmode provided in FileFd::Open for %s", FileName.c_str());
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_FLAGGED_SET(Exclusive, O_EXCL);
+ #undef if_FLAGGED_SET
- // 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)
- {
- Compress = None;
- for (; compressor != compressors.end(); ++compressor)
- {
- std::string file = std::string(FileName).append(compressor->Extension);
- if (FileExists(file) == false)
- continue;
- FileName = file;
- if (compressor->Binary == ".")
- Compress = None;
- else
- Compress = Extension;
- break;
- }
- }
- else if (Compress == Extension)
- {
- Compress = None;
- std::string ext = flExtension(FileName);
- if (ext != FileName)
- {
- ext = "." + ext;
- for (; compressor != compressors.end(); ++compressor)
- if (ext == compressor->Extension)
- break;
- }
- }
- else if (Compress != None)
+ if ((Mode & Atomic) == Atomic)
{
- 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());
- }
- for (; compressor != compressors.end(); ++compressor)
- if (compressor->Name == name)
- break;
- if (compressor == compressors.end() && name != "gzip")
- return _error->Error("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
- }
+ char *name = strdup((FileName + ".XXXXXX").c_str());
- // if we have them, use inbuilt compressors instead of forking
- if (compressor != compressors.end())
- {
-#ifdef APT_USE_ZLIB
- if (compressor->Name == "gzip")
+ if((iFd = mkstemp(name)) == -1)
{
- Compress = Gzip;
- compressor = compressors.end();
+ free(name);
+ return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str());
}
- else
-#endif
- if (compressor->Name == ".")
- {
- Compress = None;
- compressor = compressors.end();
- }
- }
- if ((Mode & Atomic) == Atomic)
- {
- Flags |= Replace;
- char *name = strdup((FileName + ".XXXXXX").c_str());
- TemporaryFileName = string(mktemp(name));
+ TemporaryFileName = string(name);
free(name);
- }
- else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
- {
- // for atomic, this will be done by rename in Close()
- unlink(FileName.c_str());
- }
- if ((Mode & Empty) == Empty)
- {
- struct stat Buf;
- if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
- unlink(FileName.c_str());
- }
- if (compressor != compressors.end())
- {
- 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());
+ // 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 (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;
+ if(fchmod(iFd, FilePermissions) == -1)
+ return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
}
else
- {
- if (TemporaryFileName.empty() == false)
- iFd = open(TemporaryFileName.c_str(), fileflags, Perms);
- 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)
+ {
if (iFd != -1)
{
- if (OpenInternDescriptor(Mode, Compress) == false)
- {
- close (iFd);
- iFd = -1;
- }
+ close (iFd);
+ iFd = -1;
}
+ return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
}
- 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, OpenMode Mode, CompressMode Compress, bool AutoClose)
+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;
+
+ // compat with the old API
+ if (Mode == ReadOnlyGzip && Compress == None)
+ Compress = Gzip;
+
+ 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:
+ if (AutoClose == true && Fd != -1)
+ close(Fd);
+ return FileFdError("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())
+ {
+ if (AutoClose == true && Fd != -1)
+ close(Fd);
+ return FileFdError("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, Compress) == false)
+ this->FileName = "";
+ if (OpenInternDescriptor(Mode, compressor) == false)
{
- if (AutoClose)
+ if (iFd != -1 && (
+ (Flags & Compressed) == Compressed ||
+ AutoClose == true))
+ {
close (iFd);
- return _error->Errno("gzdopen",_("Could not open file descriptor %d"), Fd);
+ iFd = -1;
+ }
+ return FileFdError(_("Could not open file descriptor %d"), Fd);
}
- this->FileName = "";
return true;
}
-bool FileFd::OpenInternDescriptor(OpenMode Mode, CompressMode Compress)
+bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
{
- if (Compress == None)
+ if (iFd == -1)
+ return false;
+ if (compressor.Name == "." || compressor.Binary.empty() == true)
return true;
-#ifdef APT_USE_ZLIB
- else if (Compress == Gzip)
+
+#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
+ }
+
+#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
+ if (compress_open != 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;
+
+ 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
+ else if (compressor.Name == "bzip2")
+ d->bz2 = (BZFILE*) compress_struct;
+#endif
+#ifdef HAVE_LZMA
+ else if (compressor.Name == "xz" || compressor.Name == "lzma")
+ {
+ 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;
+ }
+ }
+#endif
Flags |= Compressed;
+ return true;
}
#endif
+
+ // collect zombies here in case we reopen
+ if (d->compressor_pid > 0)
+ ExecWait(d->compressor_pid, "FileFdCompressor", true);
+
+ if ((Mode & ReadWrite) == ReadWrite)
+ return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
+
+ bool const Comp = (Mode & WriteOnly) == WriteOnly;
+ if (Comp == false)
+ {
+ // Handle 'decompression' of empty files
+ struct stat Buf;
+ fstat(iFd, &Buf);
+ if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
+ return true;
+
+ // We don't need the file open - instead let the compressor open it
+ // as he properly knows better how to efficiently read from 'his' file
+ if (FileName.empty() == false)
+ {
+ close(iFd);
+ iFd = -1;
+ }
+ }
+
+ // Create a data pipe
+ int Pipe[2] = {-1,-1};
+ if (pipe(Pipe) != 0)
+ return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
+ for (int J = 0; J != 2; J++)
+ SetCloseExec(Pipe[J],true);
+
+ d->compressed_fd = iFd;
+ d->pipe = true;
+
+ if (Comp == true)
+ iFd = Pipe[1];
else
+ iFd = Pipe[0];
+
+ // The child..
+ d->compressor_pid = ExecFork();
+ if (d->compressor_pid == 0)
{
- std::string name;
- switch (Compress)
+ if (Comp == true)
{
- 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());
+ dup2(d->compressed_fd,STDOUT_FILENO);
+ dup2(Pipe[0],STDIN_FILENO);
}
- 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;
+ else
+ {
+ if (d->compressed_fd != -1)
+ dup2(d->compressed_fd,STDIN_FILENO);
+ dup2(Pipe[1],STDOUT_FILENO);
+ }
+ int const nullfd = open("/dev/null", O_WRONLY);
+ if (nullfd != -1)
+ {
+ dup2(nullfd,STDERR_FILENO);
+ close(nullfd);
+ }
+
+ SetCloseExec(STDOUT_FILENO,false);
+ SetCloseExec(STDIN_FILENO,false);
+
+ std::vector<char const*> Args;
+ Args.push_back(compressor.Binary.c_str());
+ std::vector<std::string> const * const addArgs =
+ (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
+ for (std::vector<std::string>::const_iterator a = addArgs->begin();
+ a != addArgs->end(); ++a)
+ Args.push_back(a->c_str());
+ if (Comp == false && FileName.empty() == false)
+ {
+ // 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
+ 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]);
+
return true;
}
/*}}}*/
FileFd::~FileFd()
{
Close();
+ if (d != NULL)
+ d->CloseDown(FileName);
+ delete d;
+ d = NULL;
}
/*}}}*/
// 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)
{
- int Res;
+ ssize_t Res;
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
+ if (false)
+ /* dummy so that the rest can be 'else if's */;
+#ifdef HAVE_ZLIB
+ else if (d != NULL && d->gz != NULL)
+ Res = gzread(d->gz,To,Size);
+#endif
+#ifdef HAVE_BZ2
+ else if (d != NULL && d->bz2 != NULL)
+ Res = BZ2_bzread(d->bz2,To,Size);
+#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 && errno == EINTR)
- continue;
+
if (Res < 0)
{
- Flags |= Fail;
- return _error->Errno("read",_("Read error"));
+ 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
+ else if (d != NULL && d->gz != NULL)
+ {
+ int err;
+ char const * const errmsg = gzerror(d->gz, &err);
+ if (err != Z_ERRNO)
+ return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
+ }
+#endif
+#ifdef HAVE_BZ2
+ 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 %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"));
}
To = (char *)To + Res;
Size -= Res;
+ if (d != NULL)
+ d->seekpos += Res;
if (Actual != 0)
*Actual += Res;
}
Flags |= HitEof;
return true;
}
-
- Flags |= Fail;
- return _error->Error(_("read, still have %llu to read but none left"), Size);
+
+ return FileFdError(_("read, still have %llu to read but none left"), Size);
}
/*}}}*/
// FileFd::ReadLine - Read a complete line from the file /*{{{*/
char* FileFd::ReadLine(char *To, unsigned long long const Size)
{
*To = '\0';
-#ifdef APT_USE_ZLIB
- if (d->gz != NULL)
+#ifdef HAVE_ZLIB
+ if (d != NULL && d->gz != NULL)
return gzgets(d->gz, To, Size);
#endif
unsigned long long read = 0;
- if (Read(To, Size, &read) == false)
+ while ((Size - 1) != read)
+ {
+ unsigned long long done = 0;
+ if (Read(To + read, 1, &done) == false)
+ return NULL;
+ if (done == 0)
+ break;
+ if (To[read++] == '\n')
+ break;
+ }
+ if (read == 0)
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);
+ To[read] = '\0';
return To;
}
/*}}}*/
/* */
bool FileFd::Write(const void *From,unsigned long long Size)
{
- int Res;
+ ssize_t Res;
errno = 0;
do
{
-#ifdef APT_USE_ZLIB
- if (d->gz != NULL)
- Res = gzwrite(d->gz,From,Size);
- else
+ if (false)
+ /* dummy so that the rest can be 'else if's */;
+#ifdef HAVE_ZLIB
+ else if (d != NULL && d->gz != NULL)
+ Res = gzwrite(d->gz,From,Size);
+#endif
+#ifdef HAVE_BZ2
+ else if (d != NULL && d->bz2 != NULL)
+ Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
+#endif
+#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
- Res = write(iFd,From,Size);
+ else
+ Res = write(iFd,From,Size);
+
if (Res < 0 && errno == EINTR)
continue;
if (Res < 0)
{
- Flags |= Fail;
- return _error->Errno("write",_("Write error"));
+ if (false)
+ /* dummy so that the rest can be 'else if's */;
+#ifdef HAVE_ZLIB
+ else if (d != NULL && d->gz != NULL)
+ {
+ int err;
+ char const * const errmsg = gzerror(d->gz, &err);
+ if (err != Z_ERRNO)
+ return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
+ }
+#endif
+#ifdef HAVE_BZ2
+ 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;
}
while (Res > 0 && Size > 0);
if (Size == 0)
return true;
-
- Flags |= Fail;
+
+ return FileFdError(_("write, still have %llu to write but couldn't"), Size);
+}
+bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
+{
+ ssize_t Res;
+ errno = 0;
+ do
+ {
+ Res = write(Fd,From,Size);
+ if (Res < 0 && errno == EINTR)
+ continue;
+ if (Res < 0)
+ return _error->Errno("write",_("Write error"));
+
+ From = (char const *)From + Res;
+ Size -= Res;
+ }
+ while (Res > 0 && Size > 0);
+
+ if (Size == 0)
+ return true;
+
return _error->Error(_("write, still have %llu to write but couldn't"), Size);
}
/*}}}*/
/* */
bool FileFd::Seek(unsigned long long To)
{
- if (d->pipe == true)
+ Flags &= ~HitEof;
+
+ if (d != NULL && (d->pipe == true || d->InternalStream() == 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;
+ // Our poor man seeking in pipes is costly, so try to avoid it
+ unsigned long long seekpos = Tell();
+ if (seekpos == To)
+ return true;
+ else if (seekpos < To)
+ return Skip(To - seekpos);
+
+ if ((d->openmode & ReadOnly) != ReadOnly)
+ return FileFdError("Reopen is only implemented for read-only files!");
+ d->InternalClose(FileName);
+ if (iFd != -1)
+ close(iFd);
+ iFd = -1;
+ if (TemporaryFileName.empty() == false)
+ iFd = open(TemporaryFileName.c_str(), O_RDONLY);
+ else if (FileName.empty() == false)
+ iFd = open(FileName.c_str(), O_RDONLY);
+ else
+ {
+ if (d->compressed_fd > 0)
+ if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
+ iFd = d->compressed_fd;
+ if (iFd < 0)
+ return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
+ }
+
+ if (OpenInternDescriptor(d->openmode, d->compressor) == false)
+ return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
+
+ if (To != 0)
+ return Skip(To);
+
+ d->seekpos = To;
+ return true;
}
- int res;
-#ifdef APT_USE_ZLIB
- if (d->gz)
+ off_t res;
+#ifdef HAVE_ZLIB
+ if (d != NULL && 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 %llu", To);
- }
-
+ if (res != (off_t)To)
+ return FileFdError("Unable to seek to %llu", To);
+
+ if (d != NULL)
+ d->seekpos = To;
return true;
}
/*}}}*/
/* */
bool FileFd::Skip(unsigned long long Over)
{
- int res;
-#ifdef APT_USE_ZLIB
- if (d->gz != NULL)
+ if (d != NULL && (d->pipe == true || d->InternalStream() == true))
+ {
+ char buffer[1024];
+ while (Over != 0)
+ {
+ unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
+ if (Read(buffer, toread) == false)
+ return FileFdError("Unable to seek ahead %llu",Over);
+ Over -= toread;
+ }
+ return true;
+ }
+
+ off_t res;
+#ifdef HAVE_ZLIB
+ if (d != NULL && 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 %llu",Over);
- }
-
+ return FileFdError("Unable to seek ahead %llu",Over);
+ if (d != NULL)
+ d->seekpos = res;
+
return true;
}
/*}}}*/
/* */
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());
- }
+ // 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 || 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)
- {
- Flags |= Fail;
- return _error->Error("Unable to truncate to %llu",To);
- }
-
+ return FileFdError("Unable to truncate to %llu",To);
+
return true;
}
/*}}}*/
/* */
unsigned long long FileFd::Tell()
{
+ // In theory, we could just return seekpos here always instead of
+ // 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 || d->InternalStream() == true))
+ return d->seekpos;
+
off_t Res;
-#ifdef APT_USE_ZLIB
- if (d->gz != NULL)
+#ifdef HAVE_ZLIB
+ if (d != NULL && 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");
+ FileFdErrno("lseek","Failed to determine the current file position");
+ if (d != NULL)
+ d->seekpos = Res;
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->pipe == false && fstat(iFd,&Buf) != 0)
- return _error->Errno("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->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
- d->pipe = true;
+ if (d != NULL)
+ d->pipe = true;
if (stat(FileName.c_str(), &Buf) != 0)
- return _error->Errno("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 /*{{{*/
// ---------------------------------------------------------------------
/* */
// 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)
+ if (d != NULL && (d->pipe == true || (d->InternalStream() == true && size > 0)))
{
- // FIXME: If we have read first and then FileSize() the report is wrong
- size = 0;
+ unsigned long long const oldSeek = Tell();
char ignore[1000];
unsigned long long read = 0;
do {
- Read(ignore, sizeof(ignore), &read);
- size += read;
+ if (Read(ignore, sizeof(ignore), &read) == false)
+ {
+ Seek(oldSeek);
+ return 0;
+ }
} while(read != 0);
- Seek(0);
+ size = Tell();
+ Seek(oldSeek);
}
-#ifdef APT_USE_ZLIB
+#ifdef HAVE_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)
+ else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
{
+ off_t const oldPos = lseek(iFd,0,SEEK_CUR);
/* 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;
+ {
+ FileFdErrno("lseek","Unable to seek to end of gzipped file");
+ return 0;
+ }
+ uint32_t size = 0;
if (read(iFd, &size, 4) != 4)
- return _error->Errno("read","Unable to read original size of gzipped file");
+ {
+ FileFdErrno("read","Unable to read original size of gzipped file");
+ return 0;
+ }
+ size = le32toh(size);
+
+ if (lseek(iFd, oldPos, SEEK_SET) < 0)
+ {
+ FileFdErrno("lseek","Unable to seek in 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
-
- 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 Res = true;
if ((Flags & AutoClose) == AutoClose)
{
-#ifdef 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 & 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);
+ delete d;
+ d = NULL;
+ }
}
- if ((Flags & Replace) == Replace && iFd >= 0) {
+ if ((Flags & Replace) == Replace) {
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());
if (unlink(FileName.c_str()) != 0)
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;
- }
-
+ if (Res == false)
+ Flags |= Fail;
return Res;
}
/*}}}*/
/* */
bool FileFd::Sync()
{
-#ifdef _POSIX_SYNCHRONIZED_IO
if (fsync(iFd) != 0)
- return _error->Errno("sync",_("Problem syncing the file"));
+ return FileFdErrno("sync",_("Problem syncing the file"));
+ return true;
+}
+ /*}}}*/
+// FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
+bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
+{
+ Flags |= Fail;
+ va_list args;
+ size_t msgSize = 400;
+ int const errsv = errno;
+ while (true)
+ {
+ va_start(args,Description);
+ if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
+ break;
+ va_end(args);
+ }
+ return false;
+}
+ /*}}}*/
+// FileFd::FileFdError - set Fail and call _error->Error *{{{*/
+bool FileFd::FileFdError(const char *Description,...) {
+ Flags |= Fail;
+ va_list args;
+ size_t msgSize = 400;
+ while (true)
+ {
+ va_start(args,Description);
+ if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
+ break;
+ va_end(args);
+ }
+ return false;
+}
+ /*}}}*/
+
+APT_DEPRECATED gzFile FileFd::gzFd() {
+#ifdef HAVE_ZLIB
+ return d->gz;
+#else
+ return NULL;
+#endif
+}
+
+// Glob - wrapper around "glob()" /*{{{*/
+std::vector<std::string> Glob(std::string const &pattern, int flags)
+{
+ std::vector<std::string> result;
+ glob_t globbuf;
+ int glob_res;
+ unsigned int i;
+
+ glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
+
+ if (glob_res != 0)
+ {
+ if(glob_res != GLOB_NOMATCH) {
+ _error->Errno("glob", "Problem with glob");
+ return result;
+ }
+ }
+
+ // append results
+ for(i=0;i<globbuf.gl_pathc;i++)
+ result.push_back(string(globbuf.gl_pathv[i]));
+
+ globfree(&globbuf);
+ return result;
+}
+ /*}}}*/
+std::string GetTempDir() /*{{{*/
+{
+ const char *tmpdir = getenv("TMPDIR");
+
+#ifdef P_tmpdir
+ if (!tmpdir)
+ tmpdir = P_tmpdir;
#endif
+
+ struct stat st;
+ if (!tmpdir || strlen(tmpdir) == 0 || // tmpdir is set
+ stat(tmpdir, &st) != 0 || (st.st_mode & S_IFDIR) == 0 || // exists and is directory
+ access(tmpdir, R_OK | W_OK | X_OK) != 0 // current user has rwx access to directory
+ )
+ tmpdir = "/tmp";
+
+ 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 DropPrivileges() /*{{{*/
+{
+ if(_config->FindB("Debug::NoDropPrivs", false) == true)
+ return true;
+
+#if __gnu_linux__
+#if defined(PR_SET_NO_NEW_PRIVS) && ( PR_SET_NO_NEW_PRIVS != 38 )
+#error "PR_SET_NO_NEW_PRIVS is defined, but with a different value than expected!"
+#endif
+ // see prctl(2), needs linux3.5 at runtime - magic constant to avoid it at buildtime
+ int ret = prctl(38, 1, 0, 0, 0);
+ // ignore EINVAL - kernel is too old to understand the option
+ if(ret < 0 && errno != EINVAL)
+ _error->Warning("PR_SET_NO_NEW_PRIVS failed with %i", ret);
+#endif
+
+ // empty setting disables privilege dropping - this also ensures
+ // backward compatibility, see bug #764506
+ const std::string toUser = _config->Find("APT::Sandbox::User");
+ if (toUser.empty())
+ return true;
+
+ // uid will be 0 in the end, but gid might be different anyway
+ uid_t const old_uid = getuid();
+ gid_t const old_gid = getgid();
+
+ if (old_uid != 0)
+ return true;
+
+ struct passwd *pw = getpwnam(toUser.c_str());
+ if (pw == NULL)
+ return _error->Error("No user %s, can not drop rights", toUser.c_str());
+
+ // 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");
-gzFile FileFd::gzFd() { return (gzFile) d->gz; }
+ 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;
+}
+ /*}}}*/