1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 4 #include "zstd_decompressor.hpp" 5 6 #include "logging.hpp" 7 8 #ifdef HAVE_ZSTD 9 #include <zstd.h> 10 #endif 11 #include <boost/asio/buffer.hpp> 12 13 #include <cstddef> 14 #include <optional> 15 16 #ifdef HAVE_ZSTD 17 ZstdDecompressor::ZstdDecompressor() : dctx(ZSTD_createDStream()) 18 { 19 ZSTD_initDStream(dctx); 20 } 21 #else 22 ZstdDecompressor::ZstdDecompressor() {}; 23 #endif 24 25 std::optional<boost::asio::const_buffer> ZstdDecompressor::decompress( 26 [[maybe_unused]] boost::asio::const_buffer buffIn) 27 { 28 #ifdef HAVE_ZSTD 29 compressionBuf.clear(); 30 ZSTD_inBuffer input = {buffIn.data(), buffIn.size(), 0}; 31 32 // Note, this loop is prone to compression bombs, decompressing chunks that 33 // appear very small, but decompress to be very large, given that they're 34 // highly decompressible. This algorithm assumes that at this time, the 35 // whole file will fit in ram. 36 while (input.pos != input.size) 37 { 38 constexpr size_t frameSize = 4096; 39 auto buffer = compressionBuf.prepare(frameSize); 40 ZSTD_outBuffer output = {buffer.data(), buffer.size(), 0}; 41 const size_t ret = ZSTD_decompressStream(dctx, &output, &input); 42 if (ZSTD_isError(ret) != 0) 43 { 44 BMCWEB_LOG_ERROR("Decompression Failed with code {}:{}", ret, 45 ZSTD_getErrorName(ret)); 46 return std::nullopt; 47 } 48 compressionBuf.commit(output.pos); 49 } 50 return compressionBuf.cdata(); 51 #else 52 BMCWEB_LOG_CRITICAL("Attempt to decompress, but libzstd not enabled"); 53 54 return std::nullopt; 55 #endif 56 } 57 58 ZstdDecompressor::~ZstdDecompressor() 59 { 60 #ifdef HAVE_ZSTD 61 ZSTD_freeDStream(dctx); 62 #endif 63 } 64