diff options
-rw-r--r-- | applypatch/Android.mk | 15 | ||||
-rw-r--r-- | applypatch/imgdiff.cpp | 185 | ||||
-rw-r--r-- | applypatch/imgpatch.cpp | 25 | ||||
-rw-r--r-- | applypatch/utils.cpp | 65 | ||||
-rw-r--r-- | applypatch/utils.h | 31 | ||||
-rw-r--r-- | minui/events.cpp | 2 | ||||
-rw-r--r-- | tests/Android.mk | 9 | ||||
-rw-r--r-- | tests/component/imgdiff_test.cpp | 9 | ||||
-rw-r--r-- | tests/component/updater_test.cpp | 120 | ||||
-rw-r--r-- | ui.cpp | 1 | ||||
-rw-r--r-- | updater/blockimg.cpp | 126 |
11 files changed, 309 insertions, 279 deletions
diff --git a/applypatch/Android.mk b/applypatch/Android.mk index 8be5c36be..a7412d238 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -21,8 +21,7 @@ LOCAL_SRC_FILES := \ applypatch.cpp \ bspatch.cpp \ freecache.cpp \ - imgpatch.cpp \ - utils.cpp + imgpatch.cpp LOCAL_MODULE := libapplypatch LOCAL_MODULE_TAGS := eng LOCAL_C_INCLUDES := \ @@ -46,8 +45,7 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bspatch.cpp \ - imgpatch.cpp \ - utils.cpp + imgpatch.cpp LOCAL_MODULE := libimgpatch LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include \ @@ -56,6 +54,7 @@ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := \ libcrypto \ libbspatch \ + libbase \ libbz \ libz LOCAL_CFLAGS := \ @@ -68,8 +67,7 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bspatch.cpp \ - imgpatch.cpp \ - utils.cpp + imgpatch.cpp LOCAL_MODULE := libimgpatch LOCAL_MODULE_HOST_OS := linux LOCAL_C_INCLUDES := \ @@ -79,6 +77,7 @@ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := \ libcrypto \ libbspatch \ + libbase \ libbz \ libz LOCAL_CFLAGS := \ @@ -123,9 +122,7 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) -libimgdiff_src_files := \ - imgdiff.cpp \ - utils.cpp +libimgdiff_src_files := imgdiff.cpp # libbsdiff is compiled with -D_FILE_OFFSET_BITS=64. libimgdiff_cflags := \ diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index fba74e836..41d73ab98 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -145,12 +145,22 @@ #include <bsdiff.h> #include <zlib.h> -#include "utils.h" - using android::base::get_unaligned; static constexpr auto BUFFER_SIZE = 0x8000; +// If we use this function to write the offset and length (type size_t), their values should not +// exceed 2^63; because the signed bit will be casted away. +static inline bool Write8(int fd, int64_t value) { + return android::base::WriteFully(fd, &value, sizeof(int64_t)); +} + +// Similarly, the value should not exceed 2^31 if we are casting from size_t (e.g. target chunk +// size). +static inline bool Write4(int fd, int32_t value) { + return android::base::WriteFully(fd, &value, sizeof(int32_t)); +} + class ImageChunk { public: static constexpr auto WINDOWBITS = -15; // 32kb window; negative to indicate a raw stream. @@ -163,11 +173,12 @@ class ImageChunk { start_(start), input_file_ptr_(file_content), raw_data_len_(raw_data_len), - entry_name_(""), compress_level_(6), source_start_(0), source_len_(0), - source_uncompressed_len_(0) {} + source_uncompressed_len_(0) { + CHECK(file_content != nullptr) << "input file container can't be nullptr"; + } int GetType() const { return type_; @@ -199,7 +210,8 @@ class ImageChunk { } size_t GetHeaderSize(size_t patch_size) const; - size_t WriteHeaderToFile(FILE* f, const std::vector<uint8_t> patch, size_t offset); + // Return the offset of the next patch into the patch data. + size_t WriteHeaderToFd(int fd, const std::vector<uint8_t>& patch, size_t offset); /* * Cause a gzip chunk to be treated as a normal chunk (ie, as a blob @@ -222,9 +234,9 @@ class ImageChunk { void MergeAdjacentNormal(const ImageChunk& other); private: - int type_; // CHUNK_NORMAL, CHUNK_DEFLATE, CHUNK_RAW - size_t start_; // offset of chunk in the original input file - const std::vector<uint8_t>* input_file_ptr_; // pointer to the full content of original input file + int type_; // CHUNK_NORMAL, CHUNK_DEFLATE, CHUNK_RAW + size_t start_; // offset of chunk in the original input file + const std::vector<uint8_t>* input_file_ptr_; // ptr to the full content of original input file size_t raw_data_len_; // --- for CHUNK_DEFLATE chunks only: --- @@ -280,11 +292,11 @@ void ImageChunk::SetSourceInfo(const ImageChunk& src) { } void ImageChunk::SetEntryName(std::string entryname) { - entry_name_ = entryname; + entry_name_ = std::move(entryname); } void ImageChunk::SetUncompressedData(std::vector<uint8_t> data) { - uncompressed_data_ = data; + uncompressed_data_ = std::move(data); } bool ImageChunk::SetBonusData(const std::vector<uint8_t>& bonus_data) { @@ -295,7 +307,7 @@ bool ImageChunk::SetBonusData(const std::vector<uint8_t>& bonus_data) { return true; } -// Convert CHUNK_NORMAL & CHUNK_DEFLATE to CHUNK_RAW if the terget size is +// Convert CHUNK_NORMAL & CHUNK_DEFLATE to CHUNK_RAW if the target size is // smaller. Also take the header size into account during size comparison. bool ImageChunk::ChangeChunkToRaw(size_t patch_size) { if (type_ == CHUNK_RAW) { @@ -310,6 +322,7 @@ bool ImageChunk::ChangeChunkToRaw(size_t patch_size) { void ImageChunk::ChangeDeflateChunkToNormal() { if (type_ != CHUNK_DEFLATE) return; type_ = CHUNK_NORMAL; + entry_name_.clear(); uncompressed_data_.clear(); } @@ -317,7 +330,7 @@ void ImageChunk::ChangeDeflateChunkToNormal() { // header_type 4 bytes // CHUNK_NORMAL 8*3 = 24 bytes // CHUNK_DEFLATE 8*5 + 4*5 = 60 bytes -// CHUNK_RAW 4 bytes +// CHUNK_RAW 4 bytes + patch_size size_t ImageChunk::GetHeaderSize(size_t patch_size) const { switch (type_) { case CHUNK_NORMAL: @@ -327,43 +340,43 @@ size_t ImageChunk::GetHeaderSize(size_t patch_size) const { case CHUNK_RAW: return 4 + 4 + patch_size; default: - printf("unexpected chunk type: %d\n", type_); // should not reach here. - CHECK(false); + CHECK(false) << "unexpected chunk type: " << type_; // Should not reach here. return 0; } } -size_t ImageChunk::WriteHeaderToFile(FILE* f, const std::vector<uint8_t> patch, size_t offset) { - Write4(type_, f); +size_t ImageChunk::WriteHeaderToFd(int fd, const std::vector<uint8_t>& patch, size_t offset) { + Write4(fd, type_); switch (type_) { case CHUNK_NORMAL: printf("normal (%10zu, %10zu) %10zu\n", start_, raw_data_len_, patch.size()); - Write8(source_start_, f); - Write8(source_len_, f); - Write8(offset, f); + Write8(fd, static_cast<int64_t>(source_start_)); + Write8(fd, static_cast<int64_t>(source_len_)); + Write8(fd, static_cast<int64_t>(offset)); return offset + patch.size(); case CHUNK_DEFLATE: printf("deflate (%10zu, %10zu) %10zu %s\n", start_, raw_data_len_, patch.size(), entry_name_.c_str()); - Write8(source_start_, f); - Write8(source_len_, f); - Write8(offset, f); - Write8(source_uncompressed_len_, f); - Write8(uncompressed_data_.size(), f); - Write4(compress_level_, f); - Write4(METHOD, f); - Write4(WINDOWBITS, f); - Write4(MEMLEVEL, f); - Write4(STRATEGY, f); + Write8(fd, static_cast<int64_t>(source_start_)); + Write8(fd, static_cast<int64_t>(source_len_)); + Write8(fd, static_cast<int64_t>(offset)); + Write8(fd, static_cast<int64_t>(source_uncompressed_len_)); + Write8(fd, static_cast<int64_t>(uncompressed_data_.size())); + Write4(fd, compress_level_); + Write4(fd, METHOD); + Write4(fd, WINDOWBITS); + Write4(fd, MEMLEVEL); + Write4(fd, STRATEGY); return offset + patch.size(); case CHUNK_RAW: printf("raw (%10zu, %10zu)\n", start_, raw_data_len_); - Write4(patch.size(), f); - fwrite(patch.data(), 1, patch.size(), f); + Write4(fd, static_cast<int32_t>(patch.size())); + if (!android::base::WriteFully(fd, patch.data(), patch.size())) { + CHECK(false) << "failed to write " << patch.size() <<" bytes patch"; + } return offset; default: - printf("unexpected chunk type: %d\n", type_); - CHECK(false); + CHECK(false) << "unexpected chunk type: " << type_; return offset; } } @@ -480,20 +493,21 @@ static bool GetZipFileSize(const std::vector<uint8_t>& zip_file, size_t* input_f static bool ReadZip(const char* filename, std::vector<ImageChunk>* chunks, std::vector<uint8_t>* zip_file, bool include_pseudo_chunk) { - CHECK(zip_file != nullptr); + CHECK(chunks != nullptr && zip_file != nullptr); + + android::base::unique_fd fd(open(filename, O_RDONLY)); + if (fd == -1) { + printf("failed to open \"%s\" %s\n", filename, strerror(errno)); + return false; + } struct stat st; - if (stat(filename, &st) != 0) { + if (fstat(fd, &st) != 0) { printf("failed to stat \"%s\": %s\n", filename, strerror(errno)); return false; } size_t sz = static_cast<size_t>(st.st_size); zip_file->resize(sz); - android::base::unique_fd fd(open(filename, O_RDONLY)); - if (fd == -1) { - printf("failed to open \"%s\" %s\n", filename, strerror(errno)); - return false; - } if (!android::base::ReadFully(fd, zip_file->data(), sz)) { printf("failed to read \"%s\" %s\n", filename, strerror(errno)); return false; @@ -596,20 +610,21 @@ static bool ReadZip(const char* filename, std::vector<ImageChunk>* chunks, // Read the given file and break it up into chunks, and putting the data in to a vector. static bool ReadImage(const char* filename, std::vector<ImageChunk>* chunks, std::vector<uint8_t>* img) { - CHECK(img != nullptr); + CHECK(chunks != nullptr && img != nullptr); + + android::base::unique_fd fd(open(filename, O_RDONLY)); + if (fd == -1) { + printf("failed to open \"%s\" %s\n", filename, strerror(errno)); + return false; + } struct stat st; - if (stat(filename, &st) != 0) { + if (fstat(fd, &st) != 0) { printf("failed to stat \"%s\": %s\n", filename, strerror(errno)); return false; } size_t sz = static_cast<size_t>(st.st_size); img->resize(sz); - android::base::unique_fd fd(open(filename, O_RDONLY)); - if (fd == -1) { - printf("failed to open \"%s\" %s\n", filename, strerror(errno)); - return false; - } if (!android::base::ReadFully(fd, img->data(), sz)) { printf("failed to read \"%s\" %s\n", filename, strerror(errno)); return false; @@ -618,9 +633,8 @@ static bool ReadImage(const char* filename, std::vector<ImageChunk>* chunks, size_t pos = 0; while (pos < sz) { - if (sz - pos >= 4 && img->at(pos) == 0x1f && img->at(pos + 1) == 0x8b && - img->at(pos + 2) == 0x08 && // deflate compression - img->at(pos + 3) == 0x00) { // no header flags + // 0x00 no header flags, 0x08 deflate compression, 0x1f8b gzip magic number + if (sz - pos >= 4 && get_unaligned<uint32_t>(img->data() + pos) == 0x00088b1f) { // 'pos' is the offset of the start of a gzip chunk. size_t chunk_offset = pos; @@ -695,7 +709,7 @@ static bool ReadImage(const char* filename, std::vector<ImageChunk>* chunks, // the uncompressed data. Double-check to make sure that it // matches the size of the data we got when we actually did // the decompression. - size_t footer_size = Read4(img->data() + pos - 4); + size_t footer_size = get_unaligned<uint32_t>(img->data() + pos - 4); if (footer_size != body.DataLengthForPatch()) { printf("Error: footer size %zu != decompressed size %zu\n", footer_size, body.GetRawDataLength()); @@ -708,9 +722,8 @@ static bool ReadImage(const char* filename, std::vector<ImageChunk>* chunks, // Scan forward until we find a gzip header. size_t data_len = 0; while (data_len + pos < sz) { - if (data_len + pos + 4 <= sz && img->at(pos + data_len) == 0x1f && - img->at(pos + data_len + 1) == 0x8b && img->at(pos + data_len + 2) == 0x08 && - img->at(pos + data_len + 3) == 0x00) { + if (data_len + pos + 4 <= sz && + get_unaligned<uint32_t>(img->data() + pos + data_len) == 0x00088b1f) { break; } data_len++; @@ -759,13 +772,19 @@ static bool MakePatch(const ImageChunk* src, ImageChunk* tgt, std::vector<uint8_ return false; } + android::base::unique_fd patch_fd(open(ptemp, O_RDONLY)); + if (patch_fd == -1) { + printf("failed to open %s: %s\n", ptemp, strerror(errno)); + return false; + } struct stat st; - if (stat(ptemp, &st) != 0) { + if (fstat(patch_fd, &st) != 0) { printf("failed to stat patch file %s: %s\n", ptemp, strerror(errno)); return false; } size_t sz = static_cast<size_t>(st.st_size); + // Change the chunk type to raw if the patch takes less space that way. if (tgt->ChangeChunkToRaw(sz)) { unlink(ptemp); size_t patch_size = tgt->DataLengthForPatch(); @@ -773,12 +792,6 @@ static bool MakePatch(const ImageChunk* src, ImageChunk* tgt, std::vector<uint8_ std::copy(tgt->DataForPatch(), tgt->DataForPatch() + patch_size, patch_data->begin()); return true; } - - android::base::unique_fd patch_fd(open(ptemp, O_RDONLY)); - if (patch_fd == -1) { - printf("failed to open %s: %s\n", ptemp, strerror(errno)); - return false; - } patch_data->resize(sz); if (!android::base::ReadFully(patch_fd, patch_data->data(), sz)) { printf("failed to read \"%s\" %s\n", ptemp, strerror(errno)); @@ -845,18 +858,19 @@ int imgdiff(int argc, const char** argv) { std::vector<uint8_t> bonus_data; if (argc >= 3 && strcmp(argv[1], "-b") == 0) { + android::base::unique_fd fd(open(argv[2], O_RDONLY)); + if (fd == -1) { + printf("failed to open bonus file %s: %s\n", argv[2], strerror(errno)); + return 1; + } struct stat st; - if (stat(argv[2], &st) != 0) { + if (fstat(fd, &st) != 0) { printf("failed to stat bonus file %s: %s\n", argv[2], strerror(errno)); return 1; } + size_t bonus_size = st.st_size; bonus_data.resize(bonus_size); - android::base::unique_fd fd(open(argv[2], O_RDONLY)); - if (fd == -1) { - printf("failed to open bonus file %s: %s\n", argv[2], strerror(errno)); - return 1; - } if (!android::base::ReadFully(fd, bonus_data.data(), bonus_size)) { printf("failed to read bonus file %s: %s\n", argv[2], strerror(errno)); return 1; @@ -999,9 +1013,15 @@ int imgdiff(int argc, const char** argv) { ImageChunk* src; if (tgt_chunks[i].GetType() == CHUNK_DEFLATE && (src = FindChunkByName(tgt_chunks[i].GetEntryName(), src_chunks))) { - MakePatch(src, &tgt_chunks[i], &patch_data[i], nullptr); + if (!MakePatch(src, &tgt_chunks[i], &patch_data[i], nullptr)) { + printf("Failed to generate patch for target chunk %zu: ", i); + return 1; + } } else { - MakePatch(&src_chunks[0], &tgt_chunks[i], &patch_data[i], &bsdiff_cache); + if (!MakePatch(&src_chunks[0], &tgt_chunks[i], &patch_data[i], &bsdiff_cache)) { + printf("Failed to generate patch for target chunk %zu: ", i); + return 1; + } } } else { if (i == 1 && !bonus_data.empty()) { @@ -1009,7 +1029,10 @@ int imgdiff(int argc, const char** argv) { src_chunks[i].SetBonusData(bonus_data); } - MakePatch(&src_chunks[i], &tgt_chunks[i], &patch_data[i], nullptr); + if (!MakePatch(&src_chunks[i], &tgt_chunks[i], &patch_data[i], nullptr)) { + printf("Failed to generate patch for target chunk %zu: ", i); + return 1; + } } printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data[i].size(), src_chunks[i].GetRawDataLength()); @@ -1030,28 +1053,32 @@ int imgdiff(int argc, const char** argv) { size_t offset = total_header_size; - FILE* f = fopen(argv[3], "wb"); - if (f == nullptr) { + android::base::unique_fd patch_fd(open(argv[3], O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)); + if (patch_fd == -1) { printf("failed to open \"%s\": %s\n", argv[3], strerror(errno)); + return 1; } // Write out the headers. - - fwrite("IMGDIFF2", 1, 8, f); - Write4(static_cast<int32_t>(tgt_chunks.size()), f); + if (!android::base::WriteStringToFd("IMGDIFF2", patch_fd)) { + printf("failed to write \"IMGDIFF2\" to \"%s\": %s\n", argv[3], strerror(errno)); + return 1; + } + Write4(patch_fd, static_cast<int32_t>(tgt_chunks.size())); for (size_t i = 0; i < tgt_chunks.size(); ++i) { printf("chunk %zu: ", i); - offset = tgt_chunks[i].WriteHeaderToFile(f, patch_data[i], offset); + offset = tgt_chunks[i].WriteHeaderToFd(patch_fd, patch_data[i], offset); } // Append each chunk's bsdiff patch, in order. for (size_t i = 0; i < tgt_chunks.size(); ++i) { if (tgt_chunks[i].GetType() != CHUNK_RAW) { - fwrite(patch_data[i].data(), 1, patch_data[i].size(), f); + if (!android::base::WriteFully(patch_fd, patch_data[i].data(), patch_data[i].size())) { + CHECK(false) << "failed to write " << patch_data[i].size() << " bytes patch for chunk " + << i; + } } } - fclose(f); - return 0; } diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index 8f4a2a42b..adcc61fd6 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -31,10 +31,17 @@ #include <applypatch/applypatch.h> #include <applypatch/imgdiff.h> +#include <android-base/memory.h> #include <openssl/sha.h> #include <zlib.h> -#include "utils.h" +static inline int64_t Read8(const void *address) { + return android::base::get_unaligned<int64_t>(address); +} + +static inline int32_t Read4(const void *address) { + return android::base::get_unaligned<int32_t>(address); +} int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const unsigned char* patch_data, ssize_t patch_size, @@ -86,9 +93,9 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value return -1; } - size_t src_start = Read8(normal_header); - size_t src_len = Read8(normal_header + 8); - size_t patch_offset = Read8(normal_header + 16); + size_t src_start = static_cast<size_t>(Read8(normal_header)); + size_t src_len = static_cast<size_t>(Read8(normal_header + 8)); + size_t patch_offset = static_cast<size_t>(Read8(normal_header + 16)); if (src_start + src_len > static_cast<size_t>(old_size)) { printf("source data too short\n"); @@ -125,11 +132,11 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value return -1; } - size_t src_start = Read8(deflate_header); - size_t src_len = Read8(deflate_header + 8); - size_t patch_offset = Read8(deflate_header + 16); - size_t expanded_len = Read8(deflate_header + 24); - size_t target_len = Read8(deflate_header + 32); + size_t src_start = static_cast<size_t>(Read8(deflate_header)); + size_t src_len = static_cast<size_t>(Read8(deflate_header + 8)); + size_t patch_offset = static_cast<size_t>(Read8(deflate_header + 16)); + size_t expanded_len = static_cast<size_t>(Read8(deflate_header + 24)); + size_t target_len = static_cast<size_t>(Read8(deflate_header + 32)); int level = Read4(deflate_header + 40); int method = Read4(deflate_header + 44); int windowBits = Read4(deflate_header + 48); diff --git a/applypatch/utils.cpp b/applypatch/utils.cpp deleted file mode 100644 index 450dc8d76..000000000 --- a/applypatch/utils.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2009 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 <stdio.h> - -#include "utils.h" - -/** Write a 4-byte value to f in little-endian order. */ -void Write4(int value, FILE* f) { - fputc(value & 0xff, f); - fputc((value >> 8) & 0xff, f); - fputc((value >> 16) & 0xff, f); - fputc((value >> 24) & 0xff, f); -} - -/** Write an 8-byte value to f in little-endian order. */ -void Write8(int64_t value, FILE* f) { - fputc(value & 0xff, f); - fputc((value >> 8) & 0xff, f); - fputc((value >> 16) & 0xff, f); - fputc((value >> 24) & 0xff, f); - fputc((value >> 32) & 0xff, f); - fputc((value >> 40) & 0xff, f); - fputc((value >> 48) & 0xff, f); - fputc((value >> 56) & 0xff, f); -} - -int Read2(const void* pv) { - const unsigned char* p = reinterpret_cast<const unsigned char*>(pv); - return (int)(((unsigned int)p[1] << 8) | - (unsigned int)p[0]); -} - -int Read4(const void* pv) { - const unsigned char* p = reinterpret_cast<const unsigned char*>(pv); - return (int)(((unsigned int)p[3] << 24) | - ((unsigned int)p[2] << 16) | - ((unsigned int)p[1] << 8) | - (unsigned int)p[0]); -} - -int64_t Read8(const void* pv) { - const unsigned char* p = reinterpret_cast<const unsigned char*>(pv); - return (int64_t)(((uint64_t)p[7] << 56) | - ((uint64_t)p[6] << 48) | - ((uint64_t)p[5] << 40) | - ((uint64_t)p[4] << 32) | - ((uint64_t)p[3] << 24) | - ((uint64_t)p[2] << 16) | - ((uint64_t)p[1] << 8) | - (uint64_t)p[0]); -} diff --git a/applypatch/utils.h b/applypatch/utils.h deleted file mode 100644 index c7c8e90e2..000000000 --- a/applypatch/utils.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2009 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. - */ - -#ifndef _BUILD_TOOLS_APPLYPATCH_UTILS_H -#define _BUILD_TOOLS_APPLYPATCH_UTILS_H - -#include <inttypes.h> -#include <stdio.h> - -// Read and write little-endian values of various sizes. - -void Write4(int value, FILE* f); -void Write8(int64_t value, FILE* f); -int Read2(const void* p); -int Read4(const void* p); -int64_t Read8(const void* p); - -#endif // _BUILD_TOOLS_APPLYPATCH_UTILS_H diff --git a/minui/events.cpp b/minui/events.cpp index fa44033d2..0e1fd44a0 100644 --- a/minui/events.cpp +++ b/minui/events.cpp @@ -24,6 +24,8 @@ #include <sys/ioctl.h> #include <unistd.h> +#include <functional> + #include "minui/minui.h" #define MAX_DEVICES 16 diff --git a/tests/Android.mk b/tests/Android.mk index ec971b38c..65f736d13 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -81,7 +81,10 @@ include $(BUILD_NATIVE_TEST) # Component tests include $(CLEAR_VARS) -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := \ + -Werror \ + -D_FILE_OFFSET_BITS=64 + LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE := recovery_component_test LOCAL_C_INCLUDES := bootable/recovery @@ -136,6 +139,10 @@ LOCAL_STATIC_LIBRARIES := \ libz \ libbase \ libtune2fs \ + libfec \ + libfec_rs \ + libsquashfs_utils \ + libcutils \ $(tune2fs_static_libraries) testdata_files := $(call find-subdir-files, testdata/*) diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index be2dd385b..2f648501c 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -18,13 +18,14 @@ #include <vector> #include <android-base/file.h> +#include <android-base/memory.h> #include <android-base/test_utils.h> #include <applypatch/imgdiff.h> #include <applypatch/imgpatch.h> #include <gtest/gtest.h> #include <ziparchive/zip_writer.h> -#include "applypatch/utils.h" +using android::base::get_unaligned; static ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) { std::string* s = static_cast<std::string*>(token); @@ -41,7 +42,7 @@ static void verify_patch_header(const std::string& patch, size_t* num_normal, si ASSERT_GE(size, 12U); ASSERT_EQ("IMGDIFF2", std::string(data, 8)); - const int num_chunks = Read4(data + 8); + const int num_chunks = get_unaligned<int32_t>(data + 8); ASSERT_GE(num_chunks, 0); size_t normal = 0; @@ -51,7 +52,7 @@ static void verify_patch_header(const std::string& patch, size_t* num_normal, si size_t pos = 12; for (int i = 0; i < num_chunks; ++i) { ASSERT_LE(pos + 4, size); - int type = Read4(data + pos); + int type = get_unaligned<int32_t>(data + pos); pos += 4; if (type == CHUNK_NORMAL) { pos += 24; @@ -59,7 +60,7 @@ static void verify_patch_header(const std::string& patch, size_t* num_normal, si normal++; } else if (type == CHUNK_RAW) { ASSERT_LE(pos + 4, size); - ssize_t data_len = Read4(data + pos); + ssize_t data_len = get_unaligned<int32_t>(data + pos); ASSERT_GT(data_len, 0); pos += 4 + data_len; ASSERT_LE(pos, size); diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 8c4bdbaa4..4f8349e2f 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -20,6 +20,7 @@ #include <unistd.h> #include <string> +#include <vector> #include <android-base/file.h> #include <android-base/properties.h> @@ -27,12 +28,17 @@ #include <android-base/strings.h> #include <android-base/test_utils.h> #include <bootloader_message/bootloader_message.h> +#include <bsdiff.h> #include <gtest/gtest.h> #include <ziparchive/zip_archive.h> +#include <ziparchive/zip_writer.h> #include "common/test_constants.h" #include "edify/expr.h" #include "error_code.h" +#include "otautil/SysUtil.h" +#include "print_sha1.h" +#include "updater/blockimg.h" #include "updater/install.h" #include "updater/updater.h" @@ -64,12 +70,19 @@ static void expect(const char* expected, const char* expr_str, CauseCode cause_c ASSERT_EQ(cause_code, state.cause_code); } +static std::string get_sha1(const std::string& content) { + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1(reinterpret_cast<const uint8_t*>(content.c_str()), content.size(), digest); + return print_sha1(digest); +} + class UpdaterTest : public ::testing::Test { - protected: - virtual void SetUp() { - RegisterBuiltins(); - RegisterInstallFunctions(); - } + protected: + virtual void SetUp() override { + RegisterBuiltins(); + RegisterInstallFunctions(); + RegisterBlockImageFunctions(); + } }; TEST_F(UpdaterTest, getprop) { @@ -447,3 +460,100 @@ TEST_F(UpdaterTest, show_progress) { // recovery-updater protocol expects 3 tokens ("progress <frac> <secs>"). ASSERT_EQ(3U, android::base::Split(cmd, " ").size()); } + +TEST_F(UpdaterTest, block_image_update) { + // Create a zip file with new_data and patch_data. + TemporaryFile zip_file; + FILE* zip_file_ptr = fdopen(zip_file.fd, "wb"); + ZipWriter zip_writer(zip_file_ptr); + + // Add a dummy new data. + ASSERT_EQ(0, zip_writer.StartEntry("new_data", 0)); + ASSERT_EQ(0, zip_writer.FinishEntry()); + + // Generate and add the patch data. + std::string src_content = std::string(4096, 'a') + std::string(4096, 'c'); + std::string tgt_content = std::string(4096, 'b') + std::string(4096, 'd'); + TemporaryFile patch_file; + ASSERT_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src_content.data()), + src_content.size(), reinterpret_cast<const uint8_t*>(tgt_content.data()), + tgt_content.size(), patch_file.path, nullptr)); + std::string patch_content; + ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch_content)); + ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0)); + ASSERT_EQ(0, zip_writer.WriteBytes(patch_content.data(), patch_content.size())); + ASSERT_EQ(0, zip_writer.FinishEntry()); + + // Add two transfer lists. The first one contains a bsdiff; and we expect the update to succeed. + std::string src_hash = get_sha1(src_content); + std::string tgt_hash = get_sha1(tgt_content); + std::vector<std::string> transfer_list = { + "4", + "2", + "0", + "2", + "stash " + src_hash + " 2,0,2", + android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,2 2 - %s:2,0,2", patch_content.size(), + src_hash.c_str(), tgt_hash.c_str(), src_hash.c_str()), + "free " + src_hash, + }; + ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0)); + std::string commands = android::base::Join(transfer_list, '\n'); + ASSERT_EQ(0, zip_writer.WriteBytes(commands.data(), commands.size())); + ASSERT_EQ(0, zip_writer.FinishEntry()); + + // Stash and free some blocks, then fail the 2nd update intentionally. + std::vector<std::string> fail_transfer_list = { + "4", + "2", + "0", + "2", + "stash " + tgt_hash + " 2,0,2", + "free " + tgt_hash, + "fail", + }; + ASSERT_EQ(0, zip_writer.StartEntry("fail_transfer_list", 0)); + std::string fail_commands = android::base::Join(fail_transfer_list, '\n'); + ASSERT_EQ(0, zip_writer.WriteBytes(fail_commands.data(), fail_commands.size())); + ASSERT_EQ(0, zip_writer.FinishEntry()); + ASSERT_EQ(0, zip_writer.Finish()); + ASSERT_EQ(0, fclose(zip_file_ptr)); + + MemMapping map; + ASSERT_EQ(0, sysMapFile(zip_file.path, &map)); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); + + // Set up the handler, command_pipe, patch offset & length. + UpdaterInfo updater_info; + updater_info.package_zip = handle; + TemporaryFile temp_pipe; + updater_info.cmd_pipe = fopen(temp_pipe.path, "wb"); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; + + // Execute the commands in the 1st transfer list. + TemporaryFile update_file; + ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); + std::string script = "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list"), "new_data", "patch_data"))"; + expect("t", script.c_str(), kNoCause, &updater_info); + // The update_file should be patched correctly. + std::string updated_content; + ASSERT_TRUE(android::base::ReadFileToString(update_file.path, &updated_content)); + ASSERT_EQ(tgt_hash, get_sha1(updated_content)); + + // Expect the 2nd update to fail, but expect the stashed blocks to be freed. + script = "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("fail_transfer_list"), "new_data", "patch_data"))"; + expect("", script.c_str(), kNoCause, &updater_info); + // Updater generates the stash name based on the input file name. + std::string name_digest = get_sha1(update_file.path); + std::string stash_base = "/cache/recovery/" + name_digest; + ASSERT_EQ(0, access(stash_base.c_str(), F_OK)); + ASSERT_EQ(-1, access((stash_base + tgt_hash).c_str(), F_OK)); + ASSERT_EQ(0, rmdir(stash_base.c_str())); + + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); + CloseArchive(handle); +} @@ -30,6 +30,7 @@ #include <time.h> #include <unistd.h> +#include <functional> #include <string> #include <android-base/file.h> diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index c2897a83e..0fa83d9d5 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -32,6 +32,7 @@ #include <unistd.h> #include <fec/io.h> +#include <functional> #include <memory> #include <string> #include <unordered_map> @@ -599,88 +600,54 @@ static std::string GetStashFileName(const std::string& base, const std::string& return fn; } -typedef void (*StashCallback)(const std::string&, void*); +// Does a best effort enumeration of stash files. Ignores possible non-file items in the stash +// directory and continues despite of errors. Calls the 'callback' function for each file. +static void EnumerateStash(const std::string& dirname, + const std::function<void(const std::string&)>& callback) { + if (dirname.empty()) return; -// Does a best effort enumeration of stash files. Ignores possible non-file -// items in the stash directory and continues despite of errors. Calls the -// 'callback' function for each file and passes 'data' to the function as a -// parameter. + std::unique_ptr<DIR, decltype(&closedir)> directory(opendir(dirname.c_str()), closedir); -static void EnumerateStash(const std::string& dirname, StashCallback callback, void* data) { - if (dirname.empty() || callback == nullptr) { - return; + if (directory == nullptr) { + if (errno != ENOENT) { + PLOG(ERROR) << "opendir \"" << dirname << "\" failed"; } - - std::unique_ptr<DIR, int(*)(DIR*)> directory(opendir(dirname.c_str()), closedir); - - if (directory == nullptr) { - if (errno != ENOENT) { - PLOG(ERROR) << "opendir \"" << dirname << "\" failed"; - } - return; - } - - struct dirent* item; - while ((item = readdir(directory.get())) != nullptr) { - if (item->d_type != DT_REG) { - continue; - } - - std::string fn = dirname + "/" + std::string(item->d_name); - callback(fn, data); - } -} - -static void UpdateFileSize(const std::string& fn, void* data) { - if (fn.empty() || !data) { return; } - struct stat sb; - if (stat(fn.c_str(), &sb) == -1) { - PLOG(ERROR) << "stat \"" << fn << "\" failed"; - return; + dirent* item; + while ((item = readdir(directory.get())) != nullptr) { + if (item->d_type != DT_REG) continue; + callback(dirname + "/" + item->d_name); } - - size_t* size = static_cast<size_t*>(data); - *size += sb.st_size; } // Deletes the stash directory and all files in it. Assumes that it only // contains files. There is nothing we can do about unlikely, but possible // errors, so they are merely logged. +static void DeleteFile(const std::string& fn) { + if (fn.empty()) return; -static void DeleteFile(const std::string& fn, void* /* data */) { - if (!fn.empty()) { - LOG(INFO) << "deleting " << fn; - - if (unlink(fn.c_str()) == -1 && errno != ENOENT) { - PLOG(ERROR) << "unlink \"" << fn << "\" failed"; - } - } -} + LOG(INFO) << "deleting " << fn; -static void DeletePartial(const std::string& fn, void* data) { - if (android::base::EndsWith(fn, ".partial")) { - DeleteFile(fn, data); - } + if (unlink(fn.c_str()) == -1 && errno != ENOENT) { + PLOG(ERROR) << "unlink \"" << fn << "\" failed"; + } } static void DeleteStash(const std::string& base) { - if (base.empty()) { - return; - } + if (base.empty()) return; - LOG(INFO) << "deleting stash " << base; + LOG(INFO) << "deleting stash " << base; - std::string dirname = GetStashFileName(base, "", ""); - EnumerateStash(dirname, DeleteFile, nullptr); + std::string dirname = GetStashFileName(base, "", ""); + EnumerateStash(dirname, DeleteFile); - if (rmdir(dirname.c_str()) == -1) { - if (errno != ENOENT && errno != ENOTDIR) { - PLOG(ERROR) << "rmdir \"" << dirname << "\" failed"; - } + if (rmdir(dirname.c_str()) == -1) { + if (errno != ENOENT && errno != ENOTDIR) { + PLOG(ERROR) << "rmdir \"" << dirname << "\" failed"; } + } } static int LoadStash(CommandParameters& params, const std::string& base, const std::string& id, @@ -759,7 +726,7 @@ static int LoadStash(CommandParameters& params, const std::string& base, const s const RangeSet& src = stash_map[id]; PrintHashForCorruptedStashedBlocks(id, buffer, src); } - DeleteFile(fn, nullptr); + DeleteFile(fn); return -1; } @@ -885,13 +852,24 @@ static int CreateStash(State* state, size_t maxblocks, const std::string& blockd LOG(INFO) << "using existing stash " << dirname; - // If the directory already exists, calculate the space already allocated to - // stash files and check if there's enough for all required blocks. Delete any - // partially completed stash files first. + // If the directory already exists, calculate the space already allocated to stash files and check + // if there's enough for all required blocks. Delete any partially completed stash files first. + EnumerateStash(dirname, [](const std::string& fn) { + if (android::base::EndsWith(fn, ".partial")) { + DeleteFile(fn); + } + }); - EnumerateStash(dirname, DeletePartial, nullptr); size_t existing = 0; - EnumerateStash(dirname, UpdateFileSize, &existing); + EnumerateStash(dirname, [&existing](const std::string& fn) { + if (fn.empty()) return; + struct stat sb; + if (stat(fn.c_str(), &sb) == -1) { + PLOG(ERROR) << "stat \"" << fn << "\" failed"; + return; + } + existing += static_cast<size_t>(sb.st_size); + }); if (max_stash_size > existing) { size_t needed = max_stash_size - existing; @@ -952,14 +930,13 @@ static int SaveStash(CommandParameters& params, const std::string& base, } static int FreeStash(const std::string& base, const std::string& id) { - if (base.empty() || id.empty()) { - return -1; - } + if (base.empty() || id.empty()) { + return -1; + } - std::string fn = GetStashFileName(base, id, ""); - DeleteFile(fn, nullptr); + DeleteFile(GetStashFileName(base, id, "")); - return 0; + return 0; } static void MoveRange(std::vector<uint8_t>& dest, const RangeSet& locs, @@ -1233,7 +1210,6 @@ static int PerformCommandFree(CommandParameters& params) { if (stash_map.find(id) != stash_map.end()) { stash_map.erase(id); - return 0; } if (params.createdstash || params.canwrite) { @@ -1375,10 +1351,8 @@ static int PerformCommandDiff(CommandParameters& params) { if (params.canwrite) { if (status == 0) { LOG(INFO) << "patching " << blocks << " blocks to " << tgt.size; - Value patch_value(VAL_BLOB, std::string(reinterpret_cast<const char*>(params.patch_start + offset), len)); - RangeSinkState rss(tgt); rss.fd = params.fd; rss.p_block = 0; |