From 842d63f4ab5d03f3b07260f4edd0731c67708946 Mon Sep 17 00:00:00 2001 From: Frederic Marchal Date: Sun, 30 Oct 2016 11:53:21 +0100 Subject: [PATCH] Add support to decompress xz files Xz files are supported if liblzma is available when compiling sarg. --- configure.ac | 30 ++++++++ decomp.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 231 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index e084593..1a598ad 100644 --- a/configure.ac +++ b/configure.ac @@ -222,6 +222,30 @@ AS_IF([test "x$with_bzlib" != "xno" ], bzlib_status="disabled" ]) +# Build with liblzma +AC_ARG_WITH([liblzma], + AS_HELP_STRING([--with-liblzma],[Compile with support to decompress xz files]), + [],[with_liblzma=check]) +AS_IF([test "x$with_liblzma" != "xno" ], +[ + AC_CHECK_HEADERS(lzma.h) + AS_IF([test "x$ac_cv_header_lzma_h" = "xyes"], + [ + AC_CHECK_LIB([lzma],[lzma_stream_decoder], + [ + LIBS="-llzma ${LIBS}" + HAVE_LIBLZMA_LIB="yes" + ],[ + HAVE_LIBLZMA_LIB="" + ]) + AS_IF([test "x$HAVE_LIBLZMA_LIB" != "xyes"],[AC_MSG_ERROR([liblzma was not found])]) + ],[ + liblzma_status="not found" + ]) +],[ + liblzma_status="disabled" +]) + dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_STRUCT_TM @@ -480,3 +504,9 @@ AS_IF([test "x$bzlib_status" = "xdisabled"],[ ],[test "x$bzlib_status" = "xnot found"],[ AC_MSG_NOTICE([bzlib.h was not found so it won't be possible to process bzipped files]) ]) + +AS_IF([test "x$liblzma_status" = "xdisabled"],[ + AC_MSG_NOTICE([Not building with liblzma as requested on the configuration command line]) +],[test "x$liblzma_status" = "xnot found"],[ + AC_MSG_NOTICE([lzma.h was not found so it won't be possible to process xz files]) +]) diff --git a/decomp.c b/decomp.c index 93d5de2..36a3f0e 100644 --- a/decomp.c +++ b/decomp.c @@ -32,6 +32,9 @@ #ifdef HAVE_BZLIB_H #include "bzlib.h" #endif +#ifdef HAVE_LZMA_H +#include "lzma.h" +#endif #ifdef HAVE_ZLIB_H /*! @@ -277,6 +280,193 @@ static FileObject *Bzip_Open(int fd) } #endif +#ifdef HAVE_LZMA_H + +struct LzmaInternalFile +{ + //! Lzma stream. + lzma_stream Stream; + //! \c True if end of file is reached. + bool Eof; + //! Original file in case a rewind is necessary. + FILE *File; + //! Input buffer to store data read from the log file. + unsigned char InputBuffer[128*1024]; +}; + +/*! + * Read from xz file. + * + * \param Data The file object. + * \param Buffer The boffer to store the data read. + * \param Size How many bytes to read. + * + * \return The number of bytes read. + */ +static int Lzma_Read(void *Data,void *Buffer,int Size) +{ + struct LzmaInternalFile *BData=(struct LzmaInternalFile *)Data; + lzma_ret zerr; + + BData->Stream.next_out=Buffer; + BData->Stream.avail_out=Size; + while (BData->Stream.avail_out>0 && !BData->Eof) + { + if (BData->Stream.avail_in==0 && !BData->Eof) + { + BData->Stream.next_in=BData->InputBuffer; + BData->Stream.avail_in=fread(BData->InputBuffer,1,sizeof(BData->InputBuffer),BData->File); + if (feof(BData->File)) + BData->Eof=true; + } + zerr=lzma_code(&BData->Stream,(BData->Eof) ? LZMA_FINISH : LZMA_RUN); + if (zerr==LZMA_STREAM_END) + { + BData->Eof=true; + } + else if (zerr!=LZMA_OK) + { + debuga(__FILE__,__LINE__,_("Error decompressiong xz file (lzma library returned error %d)"),zerr); + return(0); + } + } + return(Size-BData->Stream.avail_out); +} + +/*! + * Check if end of file is reached. + * + * \param Data The file object. + * + * \return \c True if end of file is reached. + */ +static int Lzma_Eof(void *Data) +{ + struct LzmaInternalFile *BData=(struct LzmaInternalFile *)Data; + return(BData->Eof); +} + +/*! + * Initialize the lzma decoding stream. + * + * \param Stream Lzma stream to initialize. + * + * \return 0 on success or -1 if the intialization failed. A suitable + * error message is displayed in case of error. + */ +static int Lzma_InitDecoder(lzma_stream *Stream) +{ + lzma_ret zerr; + + zerr=lzma_stream_decoder(Stream,UINT64_MAX,LZMA_CONCATENATED); + if (zerr!=LZMA_OK) + { + switch (zerr) + { + case LZMA_MEM_ERROR: + FileObject_SetLastOpenError(_("Not enough memory to initialize LZMA decoder")); + break; + case LZMA_OPTIONS_ERROR: + FileObject_SetLastOpenError(_("Failed to initialize LZMA decoder due to invalid option passed to the decoder")); + break; + default: + { + char ErrMsg[80]; + + snprintf(ErrMsg,sizeof(ErrMsg),_("Failed to initialize LZMA decoder with unknown error %d"),zerr); + FileObject_SetLastOpenError(ErrMsg); + break; + } + } + return(-1); + } + return(0); +} + +/*! + * Return to the beginnig of the file. + * + * \param Data The file object. + */ +static void Lzma_Rewind(void *Data) +{ + struct LzmaInternalFile *BData=(struct LzmaInternalFile *)Data; + + rewind(BData->File); + BData->Eof=false; + memset(&BData->Stream,0,sizeof(BData->Stream)); + if (Lzma_InitDecoder(&BData->Stream)<0) + { + debuga(__FILE__,__LINE__,_("Failed to rewind the xz file (see previous LZMA error)\n")); + exit(EXIT_FAILURE); + } +} + +/*! + * Close the file. + * + * \param Data File to close. + * + * \return 0 on success or -1 on error. + */ +static int Lzma_Close(void *Data) +{ + struct LzmaInternalFile *BData=(struct LzmaInternalFile *)Data; + + fclose(BData->File); + lzma_end(&BData->Stream); + free(BData); + return(0); +} + +/*! + * Open a file object to read from a xz file. + * + * \return The object to pass to other function in this module. + */ +static FileObject *Lzma_Open(int fd) +{ + FileObject *File; + struct LzmaInternalFile *BData; + + FileObject_SetLastOpenError(NULL); + File=calloc(1,sizeof(*File)); + if (!File) + { + FileObject_SetLastOpenError(_("Not enough memory")); + return(NULL); + } + BData=calloc(1,sizeof(*BData)); + if (!BData) + { + free(File); + FileObject_SetLastOpenError(_("Not enough memory")); + return(NULL); + } + BData->File=fdopen(fd,"rb"); + if (BData->File==NULL) + { + free(BData); + free(File); + FileObject_SetLastOpenError(_("Error duplicating file descriptor")); + return(NULL); + } + if (Lzma_InitDecoder(&BData->Stream)<0) + { + fclose(BData->File); + free(BData); + free(File); + return(NULL); + } + File->Data=BData; + File->Read=Lzma_Read; + File->Eof=Lzma_Eof; + File->Rewind=Lzma_Rewind; + File->Close=Lzma_Close; + return(File); +} +#endif + /*! Open the log file. If it is compressed, uncompress it with the proper library. @@ -292,7 +482,7 @@ FileObject *decomp(const char *arq) { int fd; FileObject *fi; - unsigned char buf[3]; + unsigned char buf[5]; ssize_t nread; // guess file type @@ -306,7 +496,7 @@ FileObject *decomp(const char *arq) debuga(__FILE__,__LINE__,_("Error while reading \"%s\" to guess its type: %s\n"),arq,strerror(errno)); exit(EXIT_FAILURE); } - if (nread<3) { + if (nread