Merge "Support ZSTD in userspace COW"

This commit is contained in:
Treehugger Robot 2023-05-16 19:02:57 +00:00 committed by Gerrit Code Review
commit 9f6e88567b
9 changed files with 84 additions and 1 deletions

View File

@ -196,6 +196,7 @@ cc_binary {
"libfastbootshim", "libfastbootshim",
"libsnapshot_cow", "libsnapshot_cow",
"liblz4", "liblz4",
"libzstd",
"libsnapshot_nobinder", "libsnapshot_nobinder",
"update_metadata-protos", "update_metadata-protos",
"liburing", "liburing",

View File

@ -163,6 +163,7 @@ cc_defaults {
"libbrotli", "libbrotli",
"libz", "libz",
"liblz4", "liblz4",
"libzstd",
], ],
export_include_dirs: ["include"], export_include_dirs: ["include"],
} }

View File

@ -157,7 +157,8 @@ enum CowCompressionAlgorithm : uint8_t {
kCowCompressNone = 0, kCowCompressNone = 0,
kCowCompressGz = 1, kCowCompressGz = 1,
kCowCompressBrotli = 2, kCowCompressBrotli = 2,
kCowCompressLz4 = 3 kCowCompressLz4 = 3,
kCowCompressZstd = 4,
}; };
static constexpr uint8_t kCowReadAheadNotStarted = 0; static constexpr uint8_t kCowReadAheadNotStarted = 0;

View File

@ -29,6 +29,7 @@
#include <libsnapshot/cow_writer.h> #include <libsnapshot/cow_writer.h>
#include <lz4.h> #include <lz4.h>
#include <zlib.h> #include <zlib.h>
#include <zstd.h>
namespace android { namespace android {
namespace snapshot { namespace snapshot {
@ -40,6 +41,8 @@ std::optional<CowCompressionAlgorithm> CompressionAlgorithmFromString(std::strin
return {kCowCompressBrotli}; return {kCowCompressBrotli};
} else if (name == "lz4") { } else if (name == "lz4") {
return {kCowCompressLz4}; return {kCowCompressLz4};
} else if (name == "zstd") {
return {kCowCompressZstd};
} else if (name == "none" || name.empty()) { } else if (name == "none" || name.empty()) {
return {kCowCompressNone}; return {kCowCompressNone};
} else { } else {
@ -112,6 +115,23 @@ std::basic_string<uint8_t> CompressWorker::Compress(CowCompressionAlgorithm comp
} }
return buffer; return buffer;
} }
case kCowCompressZstd: {
std::basic_string<uint8_t> buffer(ZSTD_compressBound(length), '\0');
const auto compressed_size =
ZSTD_compress(buffer.data(), buffer.size(), data, length, 0);
if (compressed_size <= 0) {
LOG(ERROR) << "ZSTD compression failed " << compressed_size;
return {};
}
// Don't run compression if the compressed output is larger
if (compressed_size >= length) {
buffer.resize(length);
memcpy(buffer.data(), data, length);
} else {
buffer.resize(compressed_size);
}
return buffer;
}
default: default:
LOG(ERROR) << "unhandled compression type: " << compression; LOG(ERROR) << "unhandled compression type: " << compression;
break; break;

View File

@ -17,12 +17,15 @@
#include "cow_decompress.h" #include "cow_decompress.h"
#include <array> #include <array>
#include <cstring>
#include <utility> #include <utility>
#include <vector>
#include <android-base/logging.h> #include <android-base/logging.h>
#include <brotli/decode.h> #include <brotli/decode.h>
#include <lz4.h> #include <lz4.h>
#include <zlib.h> #include <zlib.h>
#include <zstd.h>
namespace android { namespace android {
namespace snapshot { namespace snapshot {
@ -336,9 +339,56 @@ class Lz4Decompressor final : public IDecompressor {
} }
}; };
class ZstdDecompressor final : public IDecompressor {
public:
ssize_t Decompress(void* buffer, size_t buffer_size, size_t decompressed_size,
size_t ignore_bytes = 0) override {
if (buffer_size < decompressed_size - ignore_bytes) {
LOG(INFO) << "buffer size " << buffer_size
<< " is not large enough to hold decompressed data. Decompressed size "
<< decompressed_size << ", ignore_bytes " << ignore_bytes;
return -1;
}
if (ignore_bytes == 0) {
if (!Decompress(buffer, decompressed_size)) {
return -1;
}
return decompressed_size;
}
std::vector<unsigned char> ignore_buf(decompressed_size);
if (!Decompress(buffer, decompressed_size)) {
return -1;
}
memcpy(buffer, ignore_buf.data() + ignore_bytes, buffer_size);
return decompressed_size;
}
bool Decompress(void* output_buffer, const size_t output_size) {
std::string input_buffer;
input_buffer.resize(stream_->Size());
size_t bytes_read = stream_->Read(input_buffer.data(), input_buffer.size());
if (bytes_read != input_buffer.size()) {
LOG(ERROR) << "Failed to read all input at once. Expected: " << input_buffer.size()
<< " actual: " << bytes_read;
return false;
}
const auto bytes_decompressed = ZSTD_decompress(output_buffer, output_size,
input_buffer.data(), input_buffer.size());
if (bytes_decompressed != output_size) {
LOG(ERROR) << "Failed to decompress ZSTD block, expected output size: " << output_size
<< ", actual: " << bytes_decompressed;
return false;
}
return true;
}
};
std::unique_ptr<IDecompressor> IDecompressor::Lz4() { std::unique_ptr<IDecompressor> IDecompressor::Lz4() {
return std::make_unique<Lz4Decompressor>(); return std::make_unique<Lz4Decompressor>();
} }
std::unique_ptr<IDecompressor> IDecompressor::Zstd() {
return std::make_unique<ZstdDecompressor>();
}
} // namespace snapshot } // namespace snapshot
} // namespace android } // namespace android

View File

@ -47,6 +47,7 @@ class IDecompressor {
static std::unique_ptr<IDecompressor> Gz(); static std::unique_ptr<IDecompressor> Gz();
static std::unique_ptr<IDecompressor> Brotli(); static std::unique_ptr<IDecompressor> Brotli();
static std::unique_ptr<IDecompressor> Lz4(); static std::unique_ptr<IDecompressor> Lz4();
static std::unique_ptr<IDecompressor> Zstd();
static std::unique_ptr<IDecompressor> FromString(std::string_view compressor); static std::unique_ptr<IDecompressor> FromString(std::string_view compressor);

View File

@ -30,6 +30,7 @@
#include <zlib.h> #include <zlib.h>
#include "cow_decompress.h" #include "cow_decompress.h"
#include "libsnapshot/cow_format.h"
namespace android { namespace android {
namespace snapshot { namespace snapshot {
@ -777,6 +778,11 @@ ssize_t CowReader::ReadData(const CowOperation* op, void* buffer, size_t buffer_
case kCowCompressBrotli: case kCowCompressBrotli:
decompressor = IDecompressor::Brotli(); decompressor = IDecompressor::Brotli();
break; break;
case kCowCompressZstd:
if (header_.block_size != op->data_length) {
decompressor = IDecompressor::Zstd();
}
break;
case kCowCompressLz4: case kCowCompressLz4:
if (header_.block_size != op->data_length) { if (header_.block_size != op->data_length) {
decompressor = IDecompressor::Lz4(); decompressor = IDecompressor::Lz4();

View File

@ -112,6 +112,7 @@ cc_defaults {
"liblz4", "liblz4",
"libext4_utils", "libext4_utils",
"liburing", "liburing",
"libzstd",
], ],
header_libs: [ header_libs: [

View File

@ -169,6 +169,7 @@ libinit_cc_defaults {
"libfsverity_init", "libfsverity_init",
"liblmkd_utils", "liblmkd_utils",
"liblz4", "liblz4",
"libzstd",
"libmini_keyctl_static", "libmini_keyctl_static",
"libmodprobe", "libmodprobe",
"libprocinfo", "libprocinfo",
@ -370,6 +371,7 @@ cc_binary {
"libprotobuf-cpp-lite", "libprotobuf-cpp-lite",
"libsnapshot_cow", "libsnapshot_cow",
"liblz4", "liblz4",
"libzstd",
"libsnapshot_init", "libsnapshot_init",
"update_metadata-protos", "update_metadata-protos",
"libprocinfo", "libprocinfo",