/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "lz4_legacy.h" #include #include #include #include #include #include #include #include using android::base::ErrnoError; using android::base::Error; using android::base::ReadFully; using android::base::unique_fd; using android::base::WriteFully; using android::storage_literals::operator""_MiB; namespace android { android::base::Result Lz4DecompressLegacy(const char* input, const char* output) { constexpr uint32_t lz4_legacy_magic = 0x184C2102; constexpr auto lz4_legacy_block_size = 8_MiB; unique_fd ifd(TEMP_FAILURE_RETRY(open(input, O_RDONLY | O_CLOEXEC))); unique_fd ofd(TEMP_FAILURE_RETRY( open(output, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0640))); uint32_t magic; if (!ReadFully(ifd, &magic, sizeof(magic))) { return ErrnoError() << "read magic"; } // Android is little-endian. No need to convert magic. if (magic != lz4_legacy_magic) { return Error() << "magic is " << std::hex << magic << " but expected " << lz4_legacy_magic; } std::vector ibuf(LZ4_compressBound(lz4_legacy_block_size)); std::vector obuf(lz4_legacy_block_size); while (true) { uint32_t block_size; ssize_t read_bytes = TEMP_FAILURE_RETRY(read(ifd.get(), &block_size, sizeof(block_size))); if (read_bytes == 0) break; if (read_bytes < 0) { return ErrnoError() << "read block size"; } if (static_cast(read_bytes) != sizeof(block_size)) { return Error() << "Cannot read " << sizeof(block_size) << " bytes for block size"; } // Android is little-endian. No need to convert block_size. if (block_size == lz4_legacy_magic) { return Error() << "Found another lz4 compressed stream"; } if (block_size > ibuf.size()) { return Error() << "Block size is " << block_size << " but compress bound is " << ibuf.size(); } if (!ReadFully(ifd, ibuf.data(), block_size)) { return ErrnoError() << "read " << block_size << " bytes"; } int decoded_size = LZ4_decompress_safe(ibuf.data(), obuf.data(), block_size, obuf.size()); if (decoded_size < 0) { return Error() << "LZ4_decompress_safe returns " << decoded_size; } if (!WriteFully(ofd, obuf.data(), decoded_size)) { return ErrnoError() << "write " << decoded_size << " bytes"; } } return {}; } } // namespace android