X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/699b209e5122f8fcd85fc4666c9b7020286ab0d0..21b3eac8f9ab8e12b43fa8998a5aa5465f29adc5:/apt-pkg/contrib/fileutl.cc diff --git a/apt-pkg/contrib/fileutl.cc b/apt-pkg/contrib/fileutl.cc index 727d3ddb5..69406a9bf 100644 --- a/apt-pkg/contrib/fileutl.cc +++ b/apt-pkg/contrib/fileutl.cc @@ -1,6 +1,5 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: fileutl.cc,v 1.42 2002/09/14 05:29:22 jgg Exp $ /* ###################################################################### File Utilities @@ -26,30 +25,41 @@ #include #include #include - +#include + +#include +#include +#include +#include +#include +#include +#include #include #include #include - #include #include #include #include -#include #include #include #include #include #include +#include + #include #include -// 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 +#ifdef HAVE_ZLIB + #include +#endif +#ifdef HAVE_BZ2 + #include +#endif +#ifdef HAVE_LZMA + #include + #include #endif #ifdef WORDS_BIGENDIAN @@ -61,20 +71,6 @@ 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 /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -151,7 +147,8 @@ bool RunScripts(const char *Cnf) /* 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 @@ -186,7 +183,7 @@ int GetLock(string File,bool Errors) int FD = open(File.c_str(),O_RDWR | O_CREAT | O_NOFOLLOW,0640); if (FD < 0) { - // Read only .. cant have locking problems there. + // Read only .. can't have locking problems there. if (errno == EROFS) { _error->Warning(_("Not using locking for read only lock file %s"),File.c_str()); @@ -202,7 +199,7 @@ int GetLock(string File,bool Errors) } SetCloseExec(FD,true); - // Aquire a write lock + // Acquire a write lock struct flock fl; fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; @@ -210,17 +207,20 @@ int GetLock(string File,bool Errors) 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; } @@ -280,7 +280,7 @@ bool CreateDirectory(string const &Parent, string const &Path) 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 const dirs = VectorizeString(Path.substr(Parent.size()), '/'); @@ -354,7 +354,7 @@ std::vector GetListOfFilesInDir(string const &Dir, std::vector c std::vector 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; @@ -380,8 +380,15 @@ std::vector GetListOfFilesInDir(string const &Dir, std::vector c 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; @@ -419,7 +426,7 @@ std::vector GetListOfFilesInDir(string const &Dir, std::vector c const char *C = Ent->d_name; for (; *C != 0; ++C) if (isalpha(*C) == 0 && isdigit(*C) == 0 - && *C != '_' && *C != '-') { + && *C != '_' && *C != '-' && *C != ':') { // no required extension -> dot is a bad character if (*C == '.' && Ext.empty() == false) continue; @@ -449,6 +456,80 @@ std::vector GetListOfFilesInDir(string const &Dir, std::vector c } closedir(D); + if (SortList == true) + std::sort(List.begin(),List.end()); + return List; +} +std::vector 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 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; @@ -536,9 +617,9 @@ string flNoLink(string File) 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 @@ -640,12 +721,42 @@ bool WaitFd(int Fd,bool write,unsigned long timeout) 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 &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 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 KeepFDs) { // Fork off the process pid_t Process = fork(); @@ -666,22 +777,8 @@ pid_t ExecFork() signal(SIGCONT,SIG_DFL); signal(SIGTSTP,SIG_DFL); - set 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); - } - } - // Close all of our FDs - just in case - for (int K = 3; K != 40; K++) + for (int K = 3; K != sysconf(_SC_OPEN_MAX); K++) { if(KeepFDs.find(K) == KeepFDs.end()) fcntl(K,F_SETFD,FD_CLOEXEC); @@ -738,385 +835,537 @@ bool ExecWait(pid_t Pid,const char *Name,bool Reap) } /*}}}*/ -// ExecCompressor - Open a de/compressor pipe /*{{{*/ +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) {} + ~LZMAFILE() { + if (compressing == true) + { + for (;;) { + stream.avail_out = sizeof(buffer)/sizeof(buffer[0]); + stream.next_out = buffer; + err = lzma_code(&stream, LZMA_FINISH); + if (err != LZMA_OK && err != LZMA_STREAM_END) + { + _error->Error("~LZMAFILE: Compress finalisation failed"); + break; + } + size_t const n = sizeof(buffer)/sizeof(buffer[0]) - stream.avail_out; + if (n && fwrite(buffer, 1, n, file) != n) + { + _error->Errno("~LZMAFILE",_("Write error")); + break; + } + if (err == LZMA_STREAM_END) + break; + } + } + lzma_end(&stream); + fclose(file); + } + }; + LZMAFILE* lzma; +#endif + int compressed_fd; + pid_t compressor_pid; + bool pipe; + APT::Configuration::Compressor compressor; + unsigned int openmode; + unsigned long long seekpos; + FileFdPrivate() : +#ifdef HAVE_ZLIB + gz(NULL), +#endif +#ifdef HAVE_BZ2 + bz2(NULL), +#endif +#ifdef HAVE_LZMA + lzma(NULL), +#endif + compressed_fd(-1), compressor_pid(-1), pipe(false), + openmode(0), seekpos(0) {}; + bool InternalClose(std::string const &FileName) + { + if (false) + /* dummy so that the rest can be 'else if's */; +#ifdef HAVE_ZLIB + else if (gz != NULL) { + int const e = gzclose(gz); + gz = NULL; + // gzdclose() on empty files always fails with "buffer error" here, ignore that + if (e != 0 && e != Z_BUF_ERROR) + return _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str()); + } +#endif +#ifdef HAVE_BZ2 + else if (bz2 != NULL) { + BZ2_bzclose(bz2); + bz2 = NULL; + } +#endif +#ifdef HAVE_LZMA + else if (lzma != NULL) { + delete lzma; + lzma = NULL; + } +#endif + return true; + } + bool CloseDown(std::string const &FileName) + { + bool const Res = InternalClose(FileName); + + if (compressor_pid > 0) + ExecWait(compressor_pid, "FileFdCompressor", true); + compressor_pid = -1; + + return Res; + } + bool InternalStream() const { + return false +#ifdef HAVE_BZ2 + || bz2 != NULL +#endif +#ifdef HAVE_LZMA + || lzma != NULL +#endif + ; + } + + + ~FileFdPrivate() { CloseDown(""); } +}; + /*}}}*/ +// FileFd::Open - Open a file /*{{{*/ // --------------------------------------------------------------------- -/* 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) +/* The most commonly used open mode combinations are given with Mode */ +bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const Perms) { - if (Pid != NULL) - *Pid = -1; + if (Mode == ReadOnlyGzip) + return Open(FileName, ReadOnly, Gzip, Perms); - // No compression - if (Prog.Binary.empty() == true) - { - OutFd = dup(FileFd); - return true; - } + if (Compress == Auto && (Mode & WriteOnly) == WriteOnly) + return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str()); - // Handle 'decompression' of empty files - if (Comp == false) + std::vector const compressors = APT::Configuration::getCompressors(); + std::vector::const_iterator compressor = compressors.begin(); + if (Compress == Auto) { - struct stat Buf; - fstat(FileFd, &Buf); - if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false) + for (; compressor != compressors.end(); ++compressor) { - OutFd = FileFd; - return true; + std::string file = FileName + compressor->Extension; + if (FileExists(file) == false) + continue; + FileName = file; + break; } } - - // 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) + else if (Compress == Extension) { - if (Comp == true) + std::string::size_type const found = FileName.find_last_of('.'); + std::string ext; + if (found != std::string::npos) { - dup2(FileFd,STDOUT_FILENO); - dup2(Pipe[0],STDIN_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(); + } } - else + 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) { - dup2(FileFd,STDIN_FILENO); - dup2(Pipe[1],STDOUT_FILENO); + 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()); } - - SetCloseExec(STDOUT_FILENO,false); - SetCloseExec(STDIN_FILENO,false); - - std::vector Args; - Args.push_back(Prog.Binary.c_str()); - std::vector const * const addArgs = - (Comp == true) ? &(Prog.CompressArgs) : &(Prog.UncompressArgs); - for (std::vector::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 (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()); } - if (Comp == true) - close(Pipe[0]); - else - close(Pipe[1]); - - if (Pid == NULL) - ExecWait(child, Prog.Binary.c_str(), true); - 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, Perms); } -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 Perms) { - 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)) + { + // for atomic, this will be done by rename in Close() + unlink(FileName.c_str()); + } + if ((Mode & Empty) == Empty) { struct stat Buf; - stat(FileName.c_str(), &Buf); - if (Buf.st_size == 0) - { - OutFd = open(FileName.c_str(), O_RDONLY); - return true; - } + if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode)) + 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); + 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 (Comp == true) - OutFd = Pipe[1]; - else - OutFd = Pipe[0]; + if_FLAGGED_SET(Create, O_CREAT); + if_FLAGGED_SET(Empty, O_TRUNC); + if_FLAGGED_SET(Exclusive, O_EXCL); + #undef if_FLAGGED_SET - // The child.. - pid_t child = ExecFork(); - if (Pid != NULL) - *Pid = child; - if (child == 0) + if ((Mode & Atomic) == Atomic) { - if (Comp == true) + char *name = strdup((FileName + ".XXXXXX").c_str()); + + if((iFd = mkstemp(name)) == -1) { - dup2(Pipe[0],STDIN_FILENO); - SetCloseExec(STDIN_FILENO,false); - } - else - { - dup2(Pipe[1],STDOUT_FILENO); - SetCloseExec(STDOUT_FILENO,false); + free(name); + return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str()); } - std::vector Args; - Args.push_back(Prog.Binary.c_str()); - std::vector const * const addArgs = - (Comp == true) ? &(Prog.CompressArgs) : &(Prog.UncompressArgs); - for (std::vector::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); + TemporaryFileName = string(name); + free(name); - execvp(Args[0],(char **)&Args[0]); - cerr << _("Failed to exec compressor ") << Args[0] << endl; - _exit(100); + if(Perms != 600 && fchmod(iFd, Perms) == -1) + return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str()); } - if (Comp == true) - close(Pipe[0]); else - close(Pipe[1]); + iFd = open(FileName.c_str(), fileflags, Perms); - if (Pid == NULL) - ExecWait(child, Prog.Binary.c_str(), false); + this->FileName = FileName; + if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false) + { + if (iFd != -1) + { + close (iFd); + iFd = -1; + } + return FileFdErrno("open",_("Could not open file %s"), FileName.c_str()); + } + SetCloseExec(iFd,true); return true; } /*}}}*/ - -// FileFd::Open - Open a file /*{{{*/ +// FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/ // --------------------------------------------------------------------- -/* The most commonly used open mode combinations are given with Mode */ -bool FileFd::Open(string FileName,OpenMode Mode,CompressMode Compress, unsigned long const Perms) +/* */ +bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose) { - 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 - 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 - - // FIXME: Denote inbuilt compressors somehow - as we don't need to have the binaries for them std::vector const compressors = APT::Configuration::getCompressors(); std::vector::const_iterator compressor = compressors.begin(); - if (Compress == Auto) + std::string name; + + // compat with the old API + if (Mode == ReadOnlyGzip && Compress == None) + Compress = Gzip; + + switch (Compress) { - 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; + 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()); } - else if (Compress == Extension) + return OpenDescriptor(Fd, Mode, *compressor, AutoClose); +} +bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose) +{ + Close(); + Flags = (AutoClose) ? FileFd::AutoClose : 0; + iFd = Fd; + this->FileName = ""; + if (OpenInternDescriptor(Mode, compressor) == false) { - Compress = None; - std::string ext = flExtension(FileName); - if (ext != FileName) + if (iFd != -1 && ( + (Flags & Compressed) == Compressed || + AutoClose == true)) { - ext = "." + ext; - for (; compressor != compressors.end(); ++compressor) - if (ext == compressor->Extension) - break; + close (iFd); + iFd = -1; } + return FileFdError(_("Could not open file descriptor %d"), Fd); + } + return true; +} +bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor) +{ + if (iFd == -1) + return false; + if (compressor.Name == "." || compressor.Binary.empty() == true) + return true; + +#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA + // the API to open files is similar, so setup to avoid code duplicates later + // and while at it ensure that we close before opening (if its a reopen) + void* (*compress_open)(int, const char *) = NULL; + if (false) + /* dummy so that the rest can be 'else if's */; +#define APT_COMPRESS_INIT(NAME,OPEN) \ + else if (compressor.Name == NAME) \ + { \ + compress_open = (void*(*)(int, const char *)) OPEN; \ + if (d != NULL) d->InternalClose(FileName); \ } - else if (Compress != None) +#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) { - std::string name; - switch (Compress) + 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) { - 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()); + // 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; } - 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()); +#endif } - // if we have them, use inbuilt compressors instead of forking - if (compressor != compressors.end()) +#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA + if (compress_open != NULL) { -#ifdef APT_USE_ZLIB - if (compressor->Name == "gzip") - { - Compress = Gzip; - compressor = compressors.end(); - } + void* compress_struct = NULL; + if ((Mode & ReadWrite) == ReadWrite) + compress_struct = compress_open(iFd, "r+"); + else if ((Mode & WriteOnly) == WriteOnly) + compress_struct = compress_open(iFd, "w"); else + 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 - if (compressor->Name == ".") +#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") { - Compress = None; - compressor = compressors.end(); + 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; + d->lzma->stream = LZMA_STREAM_INIT; + + 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 - 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()); - } - if ((Mode & Empty) == Empty) + // 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; - if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode)) - unlink(FileName.c_str()); + 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; + } } - 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()); + // 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); - 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; - } + 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) { - if (TemporaryFileName.empty() == false) - iFd = open(TemporaryFileName.c_str(), fileflags, Perms); + if (Comp == true) + { + dup2(d->compressed_fd,STDOUT_FILENO); + dup2(Pipe[0],STDIN_FILENO); + } else - iFd = open(FileName.c_str(), fileflags, Perms); - - if (iFd != -1) { - if (OpenInternDescriptor(Mode, Compress) == false) - { - close (iFd); - iFd = -1; - } + 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); } - } - if (iFd == -1) - return _error->Errno("open",_("Could not open file %s"),FileName.c_str()); + SetCloseExec(STDOUT_FILENO,false); + SetCloseExec(STDIN_FILENO,false); - this->FileName = FileName; - SetCloseExec(iFd,true); - return true; -} - /*}}}*/ -// FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/ -// --------------------------------------------------------------------- -/* */ -bool FileFd::OpenDescriptor(int Fd, OpenMode Mode, CompressMode Compress, bool AutoClose) -{ - Close(); - d = new FileFdPrivate; - d->openmode = Mode; - Flags = (AutoClose) ? FileFd::AutoClose : 0; - iFd = Fd; - if (OpenInternDescriptor(Mode, Compress) == false) - { - if (AutoClose) - close (iFd); - return _error->Errno("gzdopen",_("Could not open file descriptor %d"), Fd); - } - this->FileName = ""; - return true; -} -bool FileFd::OpenInternDescriptor(OpenMode Mode, CompressMode Compress) -{ - if (Compress == None) - return true; -#ifdef APT_USE_ZLIB - else if (Compress == 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 - { - std::string name; - switch (Compress) + std::vector Args; + Args.push_back(compressor.Binary.c_str()); + std::vector const * const addArgs = + (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs); + for (std::vector::const_iterator a = addArgs->begin(); + a != addArgs->end(); ++a) + Args.push_back(a->c_str()); + if (Comp == false && FileName.empty() == false) { - 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()); + Args.push_back("--stdout"); + if (TemporaryFileName.empty() == false) + Args.push_back(TemporaryFileName.c_str()); + else + Args.push_back(FileName.c_str()); } - std::vector const compressors = APT::Configuration::getCompressors(); - std::vector::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; + 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; } /*}}}*/ @@ -1127,37 +1376,114 @@ bool FileFd::OpenInternDescriptor(OpenMode Mode, CompressMode Compress) 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 (%d: %s)", _("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; } @@ -1172,9 +1498,8 @@ bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual) 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 /*{{{*/ @@ -1184,21 +1509,25 @@ bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual) 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; } /*}}}*/ @@ -1207,33 +1536,104 @@ char* FileFd::ReadLine(char *To, unsigned long long const Size) /* */ 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); } /*}}}*/ @@ -1242,28 +1642,55 @@ bool FileFd::Write(const void *From,unsigned long long Size) /* */ bool FileFd::Seek(unsigned long long To) { - if (d->pipe == true) + 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 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; } /*}}}*/ @@ -1272,19 +1699,32 @@ bool FileFd::Seek(unsigned long long To) /* */ bool FileFd::Skip(unsigned long long Over) { - int res; -#ifdef USE_ZLIB - if (d->gz != NULL) + if (d != NULL && (d->pipe == true || d->InternalStream() == true)) + { + d->seekpos += Over; + 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; } /*}}}*/ @@ -1293,17 +1733,20 @@ bool FileFd::Skip(unsigned long long Over) /* */ 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; } /*}}}*/ @@ -1312,40 +1755,76 @@ bool FileFd::Truncate(unsigned long long To) /* */ 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 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); + 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 /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -1355,34 +1834,43 @@ unsigned long long FileFd::Size() // for compressor pipes st_size is undefined and at 'best' zero, // so we 'read' the content and 'seek' back - see there - if (d->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 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; + } + 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; + } #ifdef WORDS_BIGENDIAN uint32_t tmp_size = size; @@ -1391,8 +1879,12 @@ unsigned long long FileFd::Size() size = tmp_size; #endif - if (lseek(iFd, orig_pos, SEEK_SET) < 0) - return _error->Errno("lseek","Unable to seek in gzipped file"); + if (lseek(iFd, oldPos, SEEK_SET) < 0) + { + FileFdErrno("lseek","Unable to seek in gzipped file"); + return 0; + } + return size; } #endif @@ -1400,34 +1892,6 @@ unsigned long long FileFd::Size() return size; } /*}}}*/ -// FileFd::ModificationTime - Return the time of last touch /*{{{*/ -// --------------------------------------------------------------------- -/* */ -time_t FileFd::ModificationTime() -{ - struct stat Buf; - if (d->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 /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -1439,19 +1903,18 @@ bool FileFd::Close() 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 ((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()); @@ -1466,14 +1929,8 @@ bool FileFd::Close() 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; } /*}}}*/ @@ -1482,12 +1939,106 @@ bool FileFd::Close() /* */ bool FileFd::Sync() { -#ifdef _POSIX_SYNCHRONIZED_IO if (fsync(iFd) != 0) - return _error->Errno("sync",_("Problem syncing the file")); -#endif + 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; +} + /*}}}*/ -gzFile FileFd::gzFd() { return (gzFile) d->gz; } +APT_DEPRECATED gzFile FileFd::gzFd() { +#ifdef HAVE_ZLIB + return d->gz; +#else + return NULL; +#endif +} + + +// Glob - wrapper around "glob()" /*{{{*/ +// --------------------------------------------------------------------- +/* */ +std::vector Glob(std::string const &pattern, int flags) +{ + std::vector 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;iError(_("rename failed, %s (%s -> %s)."),strerror(errno), + From.c_str(),To.c_str()); + return false; + } + return true; +}