-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 */;
+class APT_HIDDEN FileFdPrivate { /*{{{*/
+protected:
+ FileFd * const filefd;
+ size_t buffersize_max = 0;
+ std::unique_ptr<char[]> buffer;
+ unsigned long long buffersize = 0;
+public:
+ int compressed_fd;
+ pid_t compressor_pid;
+ bool is_pipe;
+ APT::Configuration::Compressor compressor;
+ unsigned int openmode;
+ unsigned long long seekpos;
+ explicit FileFdPrivate(FileFd * const pfilefd) : filefd(pfilefd), buffer(nullptr),
+ compressed_fd(-1), compressor_pid(-1), is_pipe(false),
+ openmode(0), seekpos(0) {};
+
+ virtual bool InternalOpen(int const iFd, unsigned int const Mode) = 0;
+ ssize_t InternalRead(void * To, unsigned long long Size)
+ {
+ unsigned long long tmp = 0;
+ if (buffersize != 0)
+ {
+ if (buffersize >= Size)
+ {
+ memcpy(To, buffer.get(), Size);
+ if (buffersize == Size)
+ {
+ buffersize = 0;
+ }
+ else
+ {
+ buffersize -= Size;
+ memmove(buffer.get(), buffer.get() + Size, buffersize);
+ }
+ return Size;
+ }
+ else
+ {
+ memcpy(To, buffer.get(), buffersize);
+ Size -= buffersize;
+ To = static_cast<char *>(To) + buffersize;
+ tmp = buffersize;
+ buffersize = 0;
+ }
+ }
+ return InternalUnbufferedRead(To, Size) + tmp;
+ }
+ virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) = 0;
+ virtual bool InternalReadError() { return filefd->FileFdErrno("read",_("Read error")); }
+ virtual char * InternalReadLine(char * To, unsigned long long Size)
+ {
+ if (unlikely(Size == 0))
+ return nullptr;
+ --Size;
+ To[0] = '\0';
+ if (unlikely(Size == 0))
+ return To;
+ char * const InitialTo = To;
+
+ do {
+ if (buffersize == 0)
+ {
+ if (buffer.get() == nullptr)
+ {
+ buffer.reset(new char[Size]);
+ buffersize_max = Size;
+ }
+ unsigned long long actualread = 0;
+ if (filefd->Read(buffer.get(), buffersize_max, &actualread) == false)
+ return nullptr;
+ buffersize = actualread;
+ if (buffersize == 0)
+ {
+ buffer.reset(nullptr);
+ buffersize_max = 0;
+ if (To == InitialTo)
+ return nullptr;
+ break;
+ }
+ filefd->Flags &= ~FileFd::HitEof;
+ }
+
+ unsigned long long const OutputSize = std::min(Size, buffersize);
+ char const * const newline = static_cast<char const * const>(memchr(buffer.get(), '\n', OutputSize));
+ if (newline != nullptr)
+ {
+ size_t length = (newline - buffer.get());
+ ++length;
+ memcpy(To, buffer.get(), length);
+ To += length;
+ if (length < buffersize)
+ {
+ buffersize -= length;
+ memmove(buffer.get(), buffer.get() + length, buffersize);
+ }
+ else
+ buffersize = 0;
+ break;
+ }
+ else
+ {
+ memcpy(To, buffer.get(), OutputSize);
+ To += OutputSize;
+ Size -= OutputSize;
+ buffersize -= OutputSize;
+ memmove(buffer.get(), buffer.get() + OutputSize, buffersize);
+ }
+ } while (Size > 0);
+ *To = '\0';
+ return InitialTo;
+ }
+ virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) = 0;
+ virtual bool InternalWriteError() { return filefd->FileFdErrno("write",_("Write error")); }
+ virtual bool InternalSeek(unsigned long long const To)
+ {
+ // Our poor man seeking is costly, so try to avoid it
+ unsigned long long const iseekpos = filefd->Tell();
+ if (iseekpos == To)
+ return true;
+ else if (iseekpos < To)
+ return filefd->Skip(To - iseekpos);
+
+ if ((openmode & FileFd::ReadOnly) != FileFd::ReadOnly)
+ return filefd->FileFdError("Reopen is only implemented for read-only files!");
+ InternalClose(filefd->FileName);
+ if (filefd->iFd != -1)
+ close(filefd->iFd);
+ filefd->iFd = -1;
+ if (filefd->TemporaryFileName.empty() == false)
+ filefd->iFd = open(filefd->TemporaryFileName.c_str(), O_RDONLY);
+ else if (filefd->FileName.empty() == false)
+ filefd->iFd = open(filefd->FileName.c_str(), O_RDONLY);
+ else
+ {
+ if (compressed_fd > 0)
+ if (lseek(compressed_fd, 0, SEEK_SET) != 0)
+ filefd->iFd = compressed_fd;
+ if (filefd->iFd < 0)
+ return filefd->FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
+ }
+
+ if (filefd->OpenInternDescriptor(openmode, compressor) == false)
+ return filefd->FileFdError("Seek on file %s because it couldn't be reopened", filefd->FileName.c_str());
+
+ buffersize = 0;
+ if (To != 0)
+ return filefd->Skip(To);
+
+ seekpos = To;
+ return true;
+ }
+ virtual bool InternalSkip(unsigned long long Over)
+ {
+ unsigned long long constexpr buffersize = 1024;
+ char buffer[buffersize];
+ while (Over != 0)
+ {
+ unsigned long long toread = std::min(buffersize, Over);
+ if (filefd->Read(buffer, toread) == false)
+ return filefd->FileFdError("Unable to seek ahead %llu",Over);
+ Over -= toread;
+ }
+ return true;
+ }
+ virtual bool InternalTruncate(unsigned long long const)
+ {
+ return filefd->FileFdError("Truncating compressed files is not implemented (%s)", filefd->FileName.c_str());
+ }
+ virtual unsigned long long InternalTell()
+ {
+ // 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…
+ return seekpos - buffersize;
+ }
+ virtual unsigned long long InternalSize()
+ {
+ unsigned long long size = 0;
+ unsigned long long const oldSeek = filefd->Tell();
+ unsigned long long constexpr ignoresize = 1024;
+ char ignore[ignoresize];
+ unsigned long long read = 0;
+ do {
+ if (filefd->Read(ignore, ignoresize, &read) == false)
+ {
+ filefd->Seek(oldSeek);
+ return 0;
+ }
+ } while(read != 0);
+ size = filefd->Tell();
+ filefd->Seek(oldSeek);
+ return size;
+ }
+ virtual bool InternalClose(std::string const &FileName) = 0;
+ virtual bool InternalStream() const { return false; }
+ virtual bool InternalAlwaysAutoClose() const { return true; }
+
+ virtual ~FileFdPrivate() {}
+};
+ /*}}}*/
+class APT_HIDDEN GzipFileFdPrivate: public FileFdPrivate { /*{{{*/