diff --git a/fastboot/Android.bp b/fastboot/Android.bp index 5c07eb093..22665edc9 100644 --- a/fastboot/Android.bp +++ b/fastboot/Android.bp @@ -190,6 +190,7 @@ cc_binary { "libhealthhalutils", "libhealthshim", "libsnapshot_cow", + "liblz4", "libsnapshot_nobinder", "update_metadata-protos", ], @@ -254,6 +255,7 @@ cc_defaults { "libsparse", "libutils", "liblog", + "liblz4", "libz", "libdiagnose_usb", "libbase", diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp index 6db8f139f..dffb2e7c6 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -44,7 +44,6 @@ cc_defaults { "libext2_uuid", "libext4_utils", "libfstab", - "libsnapshot_cow", "libsnapshot_snapuserd", "libz", ], @@ -154,22 +153,6 @@ cc_defaults { "-Wall", "-Werror", ], - export_include_dirs: ["include"], - srcs: [ - "cow_decompress.cpp", - "cow_reader.cpp", - "cow_writer.cpp", - "cow_format.cpp", - ], -} - -cc_library_static { - name: "libsnapshot_cow", - defaults: [ - "libsnapshot_cow_defaults", - ], - host_supported: true, - recovery_available: true, shared_libs: [ "libbase", "liblog", @@ -177,7 +160,24 @@ cc_library_static { static_libs: [ "libbrotli", "libz", + "liblz4", ], + export_include_dirs: ["include"], +} + +cc_library_static { + name: "libsnapshot_cow", + defaults: [ + "libsnapshot_cow_defaults", + ], + srcs: [ + "cow_decompress.cpp", + "cow_reader.cpp", + "cow_writer.cpp", + "cow_format.cpp", + ], + host_supported: true, + recovery_available: true, ramdisk_available: true, vendor_ramdisk_available: true, } @@ -214,7 +214,7 @@ cc_library_static { cc_defaults { name: "libsnapshot_test_defaults", - defaults: ["libsnapshot_defaults"], + defaults: ["libsnapshot_defaults", "libsnapshot_cow_defaults"], srcs: [ "partition_cow_creator_test.cpp", "snapshot_metadata_updater_test.cpp", @@ -295,6 +295,7 @@ cc_test { cc_binary { name: "snapshotctl", + defaults: ["libsnapshot_cow_defaults", "libsnapshot_hal_deps"], srcs: [ "snapshotctl.cpp", ], @@ -343,6 +344,9 @@ cc_test { cc_defaults { name: "libsnapshot_fuzzer_defaults", + defaults: [ + "libsnapshot_cow_defaults", + ], native_coverage : true, srcs: [ // Compile the protobuf definition again with type full. @@ -416,6 +420,7 @@ cc_test { name: "cow_api_test", defaults: [ "fs_mgr_defaults", + "libsnapshot_cow_defaults", ], srcs: [ "cow_api_test.cpp", @@ -471,6 +476,7 @@ cc_binary { "libsparse", "libxz", "libz", + "liblz4", "libziparchive", "update_metadata-protos", ], @@ -486,6 +492,9 @@ cc_binary { cc_binary { name: "estimate_cow_from_nonab_ota", + defaults: [ + "libsnapshot_cow_defaults", + ], host_supported: true, device_supported: false, cflags: [ @@ -519,6 +528,7 @@ cc_binary { name: "inspect_cow", host_supported: true, device_supported: true, + defaults: ["libsnapshot_cow_defaults"], cflags: [ "-D_FILE_OFFSET_BITS=64", "-Wall", diff --git a/fs_mgr/libsnapshot/cow_decompress.cpp b/fs_mgr/libsnapshot/cow_decompress.cpp index faceafe17..a4d22773a 100644 --- a/fs_mgr/libsnapshot/cow_decompress.cpp +++ b/fs_mgr/libsnapshot/cow_decompress.cpp @@ -20,6 +20,7 @@ #include #include +#include #include namespace android { @@ -260,5 +261,43 @@ std::unique_ptr IDecompressor::Brotli() { return std::unique_ptr(new BrotliDecompressor()); } +class Lz4Decompressor final : public IDecompressor { + public: + ~Lz4Decompressor() override = default; + + bool Decompress(const size_t output_size) override { + size_t actual_buffer_size = 0; + auto&& output_buffer = sink_->GetBuffer(output_size, &actual_buffer_size); + if (actual_buffer_size != output_size) { + LOG(ERROR) << "Failed to allocate buffer of size " << output_size << " only got " + << actual_buffer_size << " bytes"; + return false; + } + std::string input_buffer; + input_buffer.resize(stream_->Size()); + size_t bytes_read = 0; + stream_->Read(input_buffer.data(), input_buffer.size(), &bytes_read); + 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 int bytes_decompressed = + LZ4_decompress_safe(input_buffer.data(), static_cast(output_buffer), + input_buffer.size(), output_size); + if (bytes_decompressed != output_size) { + LOG(ERROR) << "Failed to decompress LZ4 block, expected output size: " << output_size + << ", actual: " << bytes_decompressed; + return false; + } + sink_->ReturnData(output_buffer, output_size); + return true; + } +}; + +std::unique_ptr IDecompressor::Lz4() { + return std::make_unique(); +} + } // namespace snapshot } // namespace android diff --git a/fs_mgr/libsnapshot/cow_decompress.h b/fs_mgr/libsnapshot/cow_decompress.h index f4852561f..7f74eda00 100644 --- a/fs_mgr/libsnapshot/cow_decompress.h +++ b/fs_mgr/libsnapshot/cow_decompress.h @@ -41,6 +41,7 @@ class IDecompressor { static std::unique_ptr Uncompressed(); static std::unique_ptr Gz(); static std::unique_ptr Brotli(); + static std::unique_ptr Lz4(); // |output_bytes| is the expected total number of bytes to sink. virtual bool Decompress(size_t output_bytes) = 0; diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp index 746feeb8d..653492ca4 100644 --- a/fs_mgr/libsnapshot/cow_reader.cpp +++ b/fs_mgr/libsnapshot/cow_reader.cpp @@ -775,6 +775,9 @@ bool CowReader::ReadData(const CowOperation& op, IByteSink* sink) { case kCowCompressBrotli: decompressor = IDecompressor::Brotli(); break; + case kCowCompressLz4: + decompressor = IDecompressor::Lz4(); + break; default: LOG(ERROR) << "Unknown compression type: " << op.compression; return false; diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp index 5ce1d3bb2..7281fc209 100644 --- a/fs_mgr/libsnapshot/cow_writer.cpp +++ b/fs_mgr/libsnapshot/cow_writer.cpp @@ -24,8 +24,10 @@ #include #include #include +#include #include #include +#include #include namespace android { @@ -129,6 +131,8 @@ bool CowWriter::ParseOptions() { compression_ = kCowCompressGz; } else if (options_.compression == "brotli") { compression_ = kCowCompressBrotli; + } else if (options_.compression == "lz4") { + compression_ = kCowCompressLz4; } else if (options_.compression == "none") { compression_ = kCowCompressNone; } else if (!options_.compression.empty()) { @@ -403,35 +407,56 @@ bool CowWriter::EmitClusterIfNeeded() { std::basic_string CowWriter::Compress(const void* data, size_t length) { switch (compression_) { case kCowCompressGz: { - auto bound = compressBound(length); - auto buffer = std::make_unique(bound); + const auto bound = compressBound(length); + std::basic_string buffer(bound, '\0'); uLongf dest_len = bound; - auto rv = compress2(buffer.get(), &dest_len, reinterpret_cast(data), + auto rv = compress2(buffer.data(), &dest_len, reinterpret_cast(data), length, Z_BEST_COMPRESSION); if (rv != Z_OK) { LOG(ERROR) << "compress2 returned: " << rv; return {}; } - return std::basic_string(buffer.get(), dest_len); + buffer.resize(dest_len); + return buffer; } case kCowCompressBrotli: { - auto bound = BrotliEncoderMaxCompressedSize(length); + const auto bound = BrotliEncoderMaxCompressedSize(length); if (!bound) { LOG(ERROR) << "BrotliEncoderMaxCompressedSize returned 0"; return {}; } - auto buffer = std::make_unique(bound); + std::basic_string buffer(bound, '\0'); size_t encoded_size = bound; auto rv = BrotliEncoderCompress( BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, length, - reinterpret_cast(data), &encoded_size, buffer.get()); + reinterpret_cast(data), &encoded_size, buffer.data()); if (!rv) { LOG(ERROR) << "BrotliEncoderCompress failed"; return {}; } - return std::basic_string(buffer.get(), encoded_size); + buffer.resize(encoded_size); + return buffer; + } + case kCowCompressLz4: { + const auto bound = LZ4_compressBound(length); + if (!bound) { + LOG(ERROR) << "LZ4_compressBound returned 0"; + return {}; + } + std::basic_string buffer(bound, '\0'); + + const auto compressed_size = LZ4_compress_default( + static_cast(data), reinterpret_cast(buffer.data()), length, + buffer.size()); + if (compressed_size <= 0) { + LOG(ERROR) << "LZ4_compress_default failed, input size: " << length + << ", compression bound: " << bound << ", ret: " << compressed_size; + return {}; + } + buffer.resize(compressed_size); + return buffer; } default: LOG(ERROR) << "unhandled compression type: " << compression_; diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h index 9f4ddbb61..ba75a8df4 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h @@ -154,9 +154,12 @@ static constexpr uint8_t kCowXorOp = 6; static constexpr uint8_t kCowSequenceOp = 7; static constexpr uint8_t kCowFooterOp = -1; -static constexpr uint8_t kCowCompressNone = 0; -static constexpr uint8_t kCowCompressGz = 1; -static constexpr uint8_t kCowCompressBrotli = 2; +enum CowCompressionAlgorithm : uint8_t { + kCowCompressNone = 0, + kCowCompressGz = 1, + kCowCompressBrotli = 2, + kCowCompressLz4 = 3 +}; static constexpr uint8_t kCowReadAheadNotStarted = 0; static constexpr uint8_t kCowReadAheadInProgress = 1; diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h index e17b5c6a1..e7a2f02f0 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h @@ -155,7 +155,7 @@ class CowWriter : public ICowWriter { android::base::borrowed_fd fd_; CowHeader header_{}; CowFooter footer_{}; - int compression_ = 0; + CowCompressionAlgorithm compression_ = kCowCompressNone; uint64_t next_op_pos_ = 0; uint64_t next_data_pos_ = 0; uint32_t cluster_size_ = 0; diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp index 57c599c25..64e0b8aff 100644 --- a/fs_mgr/libsnapshot/snapuserd/Android.bp +++ b/fs_mgr/libsnapshot/snapuserd/Android.bp @@ -87,6 +87,7 @@ cc_defaults { "liblog", "libsnapshot_cow", "libz", + "liblz4", "libext4_utils", "liburing", ], @@ -145,6 +146,7 @@ cc_test { name: "cow_snapuserd_test", defaults: [ "fs_mgr_defaults", + "libsnapshot_cow_defaults", ], srcs: [ "dm-snapshot-merge/cow_snapuserd_test.cpp", @@ -186,6 +188,7 @@ cc_test { name: "snapuserd_test", defaults: [ "fs_mgr_defaults", + "libsnapshot_cow_defaults", ], srcs: [ "user-space-merge/snapuserd_test.cpp", diff --git a/init/Android.bp b/init/Android.bp index b4bc170eb..2dd968369 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -164,6 +164,7 @@ libinit_cc_defaults { "libcgrouprc_format", "libfsverity_init", "liblmkd_utils", + "liblz4", "libmini_keyctl_static", "libmodprobe", "libprocinfo", @@ -362,6 +363,7 @@ cc_binary { "libext2_uuid", "libprotobuf-cpp-lite", "libsnapshot_cow", + "liblz4", "libsnapshot_init", "update_metadata-protos", "libprocinfo",