summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--applypatch/Android.mk15
-rw-r--r--applypatch/imgdiff.cpp185
-rw-r--r--applypatch/imgpatch.cpp25
-rw-r--r--applypatch/utils.cpp65
-rw-r--r--applypatch/utils.h31
-rw-r--r--minui/events.cpp2
-rw-r--r--tests/Android.mk9
-rw-r--r--tests/component/imgdiff_test.cpp9
-rw-r--r--tests/component/updater_test.cpp120
-rw-r--r--ui.cpp1
-rw-r--r--updater/blockimg.cpp126
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);
+}
diff --git a/ui.cpp b/ui.cpp
index 3ecd6d125..a796461c8 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -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;