From bfa9b4e82ebbc48ee9f68a1fcd0cc8f9ae463f5c Mon Sep 17 00:00:00 2001 From: Fedor Date: Thu, 26 Nov 2020 05:41:46 +0200 Subject: [PATCH] Update libjar module. --- modules/libjar/nsJARInputStream.cpp | 55 +++++++++++++++++++++++++---- modules/libjar/nsJARInputStream.h | 16 +++++++-- modules/libjar/nsZipArchive.cpp | 38 ++++++++++++++++++-- modules/libjar/nsZipArchive.h | 2 ++ modules/libjar/zipstruct.h | 3 +- 5 files changed, 101 insertions(+), 13 deletions(-) diff --git a/modules/libjar/nsJARInputStream.cpp b/modules/libjar/nsJARInputStream.cpp index 086c21ab4..a1f6c1d56 100644 --- a/modules/libjar/nsJARInputStream.cpp +++ b/modules/libjar/nsJARInputStream.cpp @@ -7,6 +7,7 @@ #include "nsJARInputStream.h" #include "zipstruct.h" // defines ZIP compression codes +#include "brotli/decode.h" #include "nsZipArchive.h" #include "nsEscape.h" @@ -51,6 +52,13 @@ nsJARInputStream::InitFile(nsJAR *aJar, nsZipItem *item) mOutCrc = crc32(0L, Z_NULL, 0); break; + case MOZ_JAR_BROTLI: + mBrotliState = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr); + mMode = MODE_BROTLI; + mInCrc = item->CRC32(); + mOutCrc = crc32(0L, Z_NULL, 0); + break; + default: return NS_ERROR_NOT_IMPLEMENTED; } @@ -166,6 +174,7 @@ nsJARInputStream::Available(uint64_t *_retval) break; case MODE_INFLATE: + case MODE_BROTLI: case MODE_COPY: *_retval = mOutSize - mZs.total_out; break; @@ -195,6 +204,7 @@ MOZ_WIN_MEM_TRY_BEGIN return ReadDirectory(aBuffer, aCount, aBytesRead); case MODE_INFLATE: + case MODE_BROTLI: if (mZs.total_out < mOutSize) { rv = ContinueInflate(aBuffer, aCount, aBytesRead); } @@ -246,6 +256,9 @@ nsJARInputStream::Close() if (mMode == MODE_INFLATE) { inflateEnd(&mZs); } + if (mMode == MODE_BROTLI) { + BrotliDecoderDestroyInstance(mBrotliState); + } mMode = MODE_CLOSED; mFd = nullptr; return NS_OK; @@ -255,6 +268,8 @@ nsresult nsJARInputStream::ContinueInflate(char* aBuffer, uint32_t aCount, uint32_t* aBytesRead) { + bool finished = false; + // No need to check the args, ::Read did that, but assert them at least NS_ASSERTION(aBuffer,"aBuffer parameter must not be null"); NS_ASSERTION(aBytesRead,"aBytesRead parameter must not be null"); @@ -266,11 +281,35 @@ nsJARInputStream::ContinueInflate(char* aBuffer, uint32_t aCount, mZs.avail_out = std::min(aCount, (mOutSize-oldTotalOut)); mZs.next_out = (unsigned char*)aBuffer; - // now inflate - int zerr = inflate(&mZs, Z_SYNC_FLUSH); - if ((zerr != Z_OK) && (zerr != Z_STREAM_END)) { - nsZipArchive::sFileCorruptedReason = "nsJARInputStream: error while inflating"; - return NS_ERROR_FILE_CORRUPTED; + if (mMode == MODE_INFLATE) { + // now inflate + int zerr = inflate(&mZs, Z_SYNC_FLUSH); + if ((zerr != Z_OK) && (zerr != Z_STREAM_END)) { + nsZipArchive::sFileCorruptedReason = "nsJARInputStream: error while inflating"; + return NS_ERROR_FILE_CORRUPTED; + } + finished = (zerr == Z_STREAM_END); + } else { + MOZ_ASSERT(mMode == MODE_BROTLI); + /* The brotli library wants size_t, but z_stream only contains + * unsigned int for avail_* and unsigned long for total_*. + * So use temporary stack values. */ + size_t avail_in = mZs.avail_in; + size_t avail_out = mZs.avail_out; + size_t total_out = mZs.total_out; + BrotliDecoderResult result = BrotliDecoderDecompressStream( + mBrotliState, + &avail_in, const_cast(&mZs.next_in), + &avail_out, &mZs.next_out, &total_out); + /* We don't need to update avail_out, it's not used outside this + * function. */ + mZs.total_out = total_out; + mZs.avail_in = avail_in; + if (result == BROTLI_DECODER_RESULT_ERROR) { + nsZipArchive::sFileCorruptedReason = "nsJARInputStream: brotli decompression error"; + return NS_ERROR_FILE_CORRUPTED; + } + finished = (result == BROTLI_DECODER_RESULT_SUCCESS); } *aBytesRead = (mZs.total_out - oldTotalOut); @@ -280,8 +319,10 @@ nsJARInputStream::ContinueInflate(char* aBuffer, uint32_t aCount, // be aggressive about ending the inflation // for some reason we don't always get Z_STREAM_END - if (zerr == Z_STREAM_END || mZs.total_out == mOutSize) { - inflateEnd(&mZs); + if (finished || mZs.total_out == mOutSize) { + if (mMode == MODE_INFLATE) { + inflateEnd(&mZs); + } // stop returning valid data as soon as we know we have a bad CRC if (mOutCrc != mInCrc) { diff --git a/modules/libjar/nsJARInputStream.h b/modules/libjar/nsJARInputStream.h index 1c396aa10..60c3d763e 100644 --- a/modules/libjar/nsJARInputStream.h +++ b/modules/libjar/nsJARInputStream.h @@ -12,6 +12,8 @@ #include "nsTArray.h" #include "mozilla/Attributes.h" +struct BrotliDecoderStateStruct; + /*------------------------------------------------------------------------- * Class nsJARInputStream declaration. This class defines the type of the * object returned by calls to nsJAR::GetInputStream(filename) for the @@ -20,9 +22,15 @@ class nsJARInputStream final : public nsIInputStream { public: - nsJARInputStream() : - mOutSize(0), mInCrc(0), mOutCrc(0), mNameLen(0), - mCurPos(0), mArrPos(0), mMode(MODE_NOTINITED) + nsJARInputStream() + : mOutSize(0) + , mInCrc(0) + , mOutCrc(0) + , mBrotliState(nullptr) + , mNameLen(0) + , mCurPos(0) + , mArrPos(0) + , mMode(MODE_NOTINITED) { memset(&mZs, 0, sizeof(z_stream)); } @@ -45,6 +53,7 @@ class nsJARInputStream final : public nsIInputStream uint32_t mInCrc; // CRC as provided by the zipentry uint32_t mOutCrc; // CRC as calculated by me z_stream mZs; // zip data structure + BrotliDecoderStateStruct* mBrotliState; // Brotli decoder state /* For directory reading */ RefPtr mJar; // string reference to zipreader @@ -59,6 +68,7 @@ class nsJARInputStream final : public nsIInputStream MODE_CLOSED, MODE_DIRECTORY, MODE_INFLATE, + MODE_BROTLI, MODE_COPY } JISMode; diff --git a/modules/libjar/nsZipArchive.cpp b/modules/libjar/nsZipArchive.cpp index 2f12af5f0..b28fddc18 100644 --- a/modules/libjar/nsZipArchive.cpp +++ b/modules/libjar/nsZipArchive.cpp @@ -4,7 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* - * This module implements a simple archive extractor for the PKZIP format. + * This module implements a simple archive extractor. * * The underlying nsZipArchive is NOT thread-safe. Do not pass references * or pointers to it across thread boundaries. @@ -17,6 +17,7 @@ #define READTYPE int32_t #include "zlib.h" +#include "brotli/decode.h" #include "nsISupportsUtils.h" #include "prio.h" #include "plstr.h" @@ -1186,6 +1187,7 @@ nsZipCursor::nsZipCursor(nsZipItem *item, nsZipArchive *aZip, uint8_t* aBuf, : mItem(item) , mBuf(aBuf) , mBufSize(aBufSize) + , mBrotliState(nullptr) , mCRC(0) , mDoCRC(doCRC) { @@ -1200,6 +1202,10 @@ nsZipCursor::nsZipCursor(nsZipItem *item, nsZipArchive *aZip, uint8_t* aBuf, mZs.avail_in = item->Size(); mZs.next_in = (Bytef*)aZip->GetData(item); + + if (mItem->Compression() == MOZ_JAR_BROTLI) { + mBrotliState = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr); + } if (doCRC) mCRC = crc32(0L, Z_NULL, 0); @@ -1210,6 +1216,9 @@ nsZipCursor::~nsZipCursor() if (mItem->Compression() == DEFLATED) { inflateEnd(&mZs); } + if (mItem->Compression() == MOZ_JAR_BROTLI) { + BrotliDecoderDestroyInstance(mBrotliState); + } } uint8_t* nsZipCursor::ReadOrCopy(uint32_t *aBytesRead, bool aCopy) { @@ -1246,6 +1255,29 @@ MOZ_WIN_MEM_TRY_BEGIN *aBytesRead = mZs.next_out - buf; verifyCRC = (zerr == Z_STREAM_END); break; + case MOZ_JAR_BROTLI: { + buf = mBuf; + mZs.next_out = buf; + /* The brotli library wants size_t, but z_stream only contains + * unsigned int for avail_*. So use temporary stack values. */ + size_t avail_out = mBufSize; + size_t avail_in = mZs.avail_in; + BrotliDecoderResult result = BrotliDecoderDecompressStream( + mBrotliState, + &avail_in, const_cast(&mZs.next_in), + &avail_out, &mZs.next_out, nullptr); + /* We don't need to update avail_out, it's not used outside this + * function. */ + mZs.avail_in = avail_in; + + if (result == BROTLI_DECODER_RESULT_ERROR) { + return nullptr; + } + + *aBytesRead = mZs.next_out - buf; + verifyCRC = (result == BROTLI_DECODER_RESULT_SUCCESS); + break; + } default: return nullptr; } @@ -1272,7 +1304,9 @@ nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive *aZip, return; uint32_t size = 0; - if (item->Compression() == DEFLATED) { + bool compressed = (item->Compression() == DEFLATED) || + (item->Compression() == MOZ_JAR_BROTLI); + if (compressed) { size = item->RealSize(); mAutoBuf = MakeUniqueFallible(size); if (!mAutoBuf) { diff --git a/modules/libjar/nsZipArchive.h b/modules/libjar/nsZipArchive.h index 2de679032..6b758c9fd 100644 --- a/modules/libjar/nsZipArchive.h +++ b/modules/libjar/nsZipArchive.h @@ -37,6 +37,7 @@ class nsZipFind; struct PRFileDesc; +struct BrotliDecoderStateStruct; /** * This file defines some of the basic structures used by libjar to @@ -314,6 +315,7 @@ private: uint8_t *mBuf; uint32_t mBufSize; z_stream mZs; + BrotliDecoderStateStruct* mBrotliState; uint32_t mCRC; bool mDoCRC; }; diff --git a/modules/libjar/zipstruct.h b/modules/libjar/zipstruct.h index f06afe14e..f7393128a 100644 --- a/modules/libjar/zipstruct.h +++ b/modules/libjar/zipstruct.h @@ -102,6 +102,7 @@ typedef struct ZipEnd_ #define TOKENIZED 7 #define DEFLATED 8 #define UNSUPPORTED 0xFF - +/* non-standard extension */ +#define MOZ_JAR_BROTLI 0x81 #endif /* _zipstruct_h */