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
ZstdDecompressor()17 ZstdDecompressor::ZstdDecompressor() : dctx(ZSTD_createDStream())
18 {
19 ZSTD_initDStream(dctx);
20 }
21 #else
ZstdDecompressor()22 ZstdDecompressor::ZstdDecompressor() {};
23 #endif
24
decompress(boost::asio::const_buffer buffIn)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
~ZstdDecompressor()58 ZstdDecompressor::~ZstdDecompressor()
59 {
60 #ifdef HAVE_ZSTD
61 ZSTD_freeDStream(dctx);
62 #endif
63 }
64