summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--Android.mk5
-rw-r--r--PREUPLOAD.cfg6
-rw-r--r--applypatch/applypatch.cpp26
-rw-r--r--applypatch/bspatch.cpp18
-rw-r--r--applypatch/freecache.cpp7
-rw-r--r--applypatch/imgdiff.cpp199
-rw-r--r--applypatch/imgpatch.cpp53
-rw-r--r--applypatch/include/applypatch/applypatch.h26
-rw-r--r--applypatch/include/applypatch/imgdiff_image.h7
-rw-r--r--bootloader_message/bootloader_message.cpp33
-rw-r--r--bootloader_message/include/bootloader_message/bootloader_message.h5
-rw-r--r--fuse_sdcard_provider.cpp88
-rw-r--r--fuse_sideload.cpp545
-rw-r--r--fuse_sideload.h26
-rw-r--r--minadbd/Android.mk13
-rw-r--r--minadbd/fuse_adb_provider.cpp53
-rw-r--r--minadbd/fuse_adb_provider.h8
-rw-r--r--minadbd/fuse_adb_provider_test.cpp6
-rw-r--r--otautil/include/otautil/rangeset.h50
-rw-r--r--otautil/rangeset.cpp125
-rw-r--r--roots.cpp4
-rw-r--r--tests/Android.mk10
-rw-r--r--tests/component/applypatch_test.cpp131
-rw-r--r--tests/component/bootloader_message_test.cpp140
-rw-r--r--tests/component/imgdiff_test.cpp14
-rw-r--r--tests/component/sideload_test.cpp70
-rw-r--r--tests/component/uncrypt_test.cpp99
-rw-r--r--tests/unit/rangeset_test.cpp158
-rw-r--r--uncrypt/Android.bp39
-rw-r--r--uncrypt/Android.mk31
-rw-r--r--update_verifier/Android.mk8
-rw-r--r--update_verifier/update_verifier.cpp45
-rw-r--r--updater/blockimg.cpp24
-rw-r--r--updater/install.cpp25
35 files changed, 1153 insertions, 945 deletions
diff --git a/Android.bp b/Android.bp
index 22407e0e2..f8c6a4b71 100644
--- a/Android.bp
+++ b/Android.bp
@@ -4,4 +4,5 @@ subdirs = [
"edify",
"otafault",
"otautil",
+ "uncrypt",
]
diff --git a/Android.mk b/Android.mk
index 7e34c10f9..d9966b7cc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -158,7 +158,6 @@ LOCAL_STATIC_LIBRARIES := \
libziparchive \
libotautil \
libmounts \
- libz \
libminadbd \
libasyncio \
libfusesideload \
@@ -173,7 +172,8 @@ LOCAL_STATIC_LIBRARIES := \
libcutils \
libutils \
liblog \
- libselinux
+ libselinux \
+ libz
LOCAL_HAL_STATIC_LIBRARIES := libhealthd
@@ -263,6 +263,5 @@ include \
$(LOCAL_PATH)/minui/Android.mk \
$(LOCAL_PATH)/tests/Android.mk \
$(LOCAL_PATH)/tools/Android.mk \
- $(LOCAL_PATH)/uncrypt/Android.mk \
$(LOCAL_PATH)/updater/Android.mk \
$(LOCAL_PATH)/update_verifier/Android.mk \
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 000000000..b5f5f0362
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,6 @@
+[Builtin Hooks]
+clang_format = true
+
+[Builtin Hooks Options]
+# Handle native codes only.
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
index c8b75df79..41a72eb15 100644
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -42,6 +42,8 @@
#include "otafault/ota_io.h"
#include "otautil/print_sha1.h"
+std::string cache_temp_source = "/cache/saved.file";
+
static int LoadPartitionContents(const std::string& filename, FileContents* file);
static size_t FileSink(const unsigned char* data, size_t len, int fd);
static int GenerateTarget(const FileContents& source_file, const std::unique_ptr<Value>& patch,
@@ -411,12 +413,10 @@ int applypatch_check(const char* filename, const std::vector<std::string>& patch
(!patch_sha1_str.empty() && FindMatchingPatch(file.sha1, patch_sha1_str) < 0)) {
printf("file \"%s\" doesn't have any of expected sha1 sums; checking cache\n", filename);
- // If the source file is missing or corrupted, it might be because
- // we were killed in the middle of patching it. A copy of it
- // should have been made in CACHE_TEMP_SOURCE. If that file
- // exists and matches the sha1 we're looking for, the check still
- // passes.
- if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
+ // If the source file is missing or corrupted, it might be because we were killed in the middle
+ // of patching it. A copy of it should have been made in cache_temp_source. If that file
+ // exists and matches the sha1 we're looking for, the check still passes.
+ if (LoadFileContents(cache_temp_source.c_str(), &file) != 0) {
printf("failed to load cache file\n");
return 1;
}
@@ -539,7 +539,7 @@ int applypatch(const char* source_filename, const char* target_filename,
printf("source file is bad; trying copy\n");
FileContents copy_file;
- if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file) < 0) {
+ if (LoadFileContents(cache_temp_source.c_str(), &copy_file) < 0) {
printf("failed to read copy file\n");
return 1;
}
@@ -634,7 +634,7 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr
printf("not enough free space on /cache\n");
return 1;
}
- if (SaveFileContents(CACHE_TEMP_SOURCE, &source_file) < 0) {
+ if (SaveFileContents(cache_temp_source.c_str(), &source_file) < 0) {
printf("failed to back up source file\n");
return 1;
}
@@ -651,11 +651,11 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr
int result;
if (use_bsdiff) {
- result = ApplyBSDiffPatch(source_file.data.data(), source_file.data.size(), patch.get(), 0,
- sink, &ctx);
+ result =
+ ApplyBSDiffPatch(source_file.data.data(), source_file.data.size(), *patch, 0, sink, &ctx);
} else {
- result = ApplyImagePatch(source_file.data.data(), source_file.data.size(), patch.get(), sink,
- &ctx, bonus_data);
+ result = ApplyImagePatch(source_file.data.data(), source_file.data.size(), *patch, sink, &ctx,
+ bonus_data);
}
if (result != 0) {
@@ -680,7 +680,7 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr
}
// Delete the backup copy of the source.
- unlink(CACHE_TEMP_SOURCE);
+ unlink(cache_temp_source.c_str());
// Success!
return 0;
diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp
index c291464a8..912dbbdd8 100644
--- a/applypatch/bspatch.cpp
+++ b/applypatch/bspatch.cpp
@@ -26,7 +26,7 @@
#include <string>
#include <android-base/logging.h>
-#include <bspatch.h>
+#include <bsdiff/bspatch.h>
#include <openssl/sha.h>
#include "applypatch/applypatch.h"
@@ -65,7 +65,7 @@ void ShowBSDiffLicense() {
);
}
-int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch,
+int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value& patch,
size_t patch_offset, SinkFn sink, SHA_CTX* ctx) {
auto sha_sink = [&sink, &ctx](const uint8_t* data, size_t len) {
len = sink(data, len);
@@ -73,22 +73,20 @@ int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value
return len;
};
- CHECK(patch != nullptr);
- CHECK_LE(patch_offset, patch->data.size());
+ CHECK_LE(patch_offset, patch.data.size());
int result = bsdiff::bspatch(old_data, old_size,
- reinterpret_cast<const uint8_t*>(&patch->data[patch_offset]),
- patch->data.size() - patch_offset, sha_sink);
+ reinterpret_cast<const uint8_t*>(&patch.data[patch_offset]),
+ patch.data.size() - patch_offset, sha_sink);
if (result != 0) {
LOG(ERROR) << "bspatch failed, result: " << result;
// print SHA1 of the patch in the case of a data error.
if (result == 2) {
uint8_t digest[SHA_DIGEST_LENGTH];
- SHA1(reinterpret_cast<const uint8_t*>(patch->data.data() + patch_offset),
- patch->data.size() - patch_offset, digest);
+ SHA1(reinterpret_cast<const uint8_t*>(patch.data.data() + patch_offset),
+ patch.data.size() - patch_offset, digest);
std::string patch_sha1 = print_sha1(digest);
- LOG(ERROR) << "Patch may be corrupted, offset: " << patch_offset << ", SHA1: "
- << patch_sha1;
+ LOG(ERROR) << "Patch may be corrupted, offset: " << patch_offset << ", SHA1: " << patch_sha1;
}
}
return result;
diff --git a/applypatch/freecache.cpp b/applypatch/freecache.cpp
index 331cae265..0a40baa97 100644
--- a/applypatch/freecache.cpp
+++ b/applypatch/freecache.cpp
@@ -90,10 +90,9 @@ static std::set<std::string> FindExpendableFiles() {
while ((de = readdir(d.get())) != 0) {
std::string path = std::string(dirs[i]) + "/" + de->d_name;
- // We can't delete CACHE_TEMP_SOURCE; if it's there we might have
- // restarted during installation and could be depending on it to
- // be there.
- if (path == CACHE_TEMP_SOURCE) {
+ // We can't delete cache_temp_source; if it's there we might have restarted during
+ // installation and could be depending on it to be there.
+ if (path == cache_temp_source) {
continue;
}
diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp
index f57e7942c..3dae63d4b 100644
--- a/applypatch/imgdiff.cpp
+++ b/applypatch/imgdiff.cpp
@@ -175,7 +175,7 @@ using android::base::get_unaligned;
static constexpr size_t VERSION = 2;
// We assume the header "IMGDIFF#" is 8 bytes.
-static_assert(VERSION <= 9, "VERSION occupies more than one byte.");
+static_assert(VERSION <= 9, "VERSION occupies more than one byte");
static constexpr size_t BLOCK_SIZE = 4096;
static constexpr size_t BUFFER_SIZE = 0x8000;
@@ -229,8 +229,8 @@ static bool RemoveUsedBlocks(size_t* start, size_t* length, const SortedRangeSet
}
// TODO find the largest non-overlap chunk.
- printf("Removing block %s from %zu - %zu\n", used_ranges.ToString().c_str(), *start,
- *start + *length - 1);
+ LOG(INFO) << "Removing block " << used_ranges.ToString() << " from " << *start << " - "
+ << *start + *length - 1;
// If there's no duplicate entry name, we should only overlap in the head or tail block. Try to
// trim both blocks. Skip this source chunk in case it still overlaps with the used ranges.
@@ -241,7 +241,7 @@ static bool RemoveUsedBlocks(size_t* start, size_t* length, const SortedRangeSet
return true;
}
- printf("Failed to remove the overlapped block ranges; skip the source\n");
+ LOG(WARNING) << "Failed to remove the overlapped block ranges; skip the source";
return false;
}
@@ -251,6 +251,7 @@ static const struct option OPTIONS[] = {
{ "block-limit", required_argument, nullptr, 0 },
{ "debug-dir", required_argument, nullptr, 0 },
{ "split-info", required_argument, nullptr, 0 },
+ { "verbose", no_argument, nullptr, 'v' },
{ nullptr, 0, nullptr, 0 },
};
@@ -284,6 +285,11 @@ size_t ImageChunk::DataLengthForPatch() const {
return raw_data_len_;
}
+void ImageChunk::Dump(size_t index) const {
+ LOG(INFO) << "chunk: " << index << ", type: " << type_ << ", start: " << start_
+ << ", len: " << DataLengthForPatch() << ", name: " << entry_name_;
+}
+
bool ImageChunk::operator==(const ImageChunk& other) const {
if (type_ != other.type_) {
return false;
@@ -334,7 +340,7 @@ bool ImageChunk::MakePatch(const ImageChunk& tgt, const ImageChunk& src,
int fd = mkstemp(ptemp);
if (fd == -1) {
- printf("MakePatch failed to create a temporary file: %s\n", strerror(errno));
+ PLOG(ERROR) << "MakePatch failed to create a temporary file";
return false;
}
close(fd);
@@ -342,18 +348,18 @@ bool ImageChunk::MakePatch(const ImageChunk& tgt, const ImageChunk& src,
int r = bsdiff::bsdiff(src.DataForPatch(), src.DataLengthForPatch(), tgt.DataForPatch(),
tgt.DataLengthForPatch(), ptemp, bsdiff_cache);
if (r != 0) {
- printf("bsdiff() failed: %d\n", r);
+ LOG(ERROR) << "bsdiff() failed: " << r;
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));
+ PLOG(ERROR) << "Failed to open " << ptemp;
return false;
}
struct stat st;
if (fstat(patch_fd, &st) != 0) {
- printf("failed to stat patch file %s: %s\n", ptemp, strerror(errno));
+ PLOG(ERROR) << "Failed to stat patch file " << ptemp;
return false;
}
@@ -361,7 +367,7 @@ bool ImageChunk::MakePatch(const ImageChunk& tgt, const ImageChunk& src,
patch_data->resize(sz);
if (!android::base::ReadFully(patch_fd, patch_data->data(), sz)) {
- printf("failed to read \"%s\" %s\n", ptemp, strerror(errno));
+ PLOG(ERROR) << "Failed to read " << ptemp;
unlink(ptemp);
return false;
}
@@ -373,7 +379,7 @@ bool ImageChunk::MakePatch(const ImageChunk& tgt, const ImageChunk& src,
bool ImageChunk::ReconstructDeflateChunk() {
if (type_ != CHUNK_DEFLATE) {
- printf("attempt to reconstruct non-deflate chunk\n");
+ LOG(ERROR) << "Attempted to reconstruct non-deflate chunk";
return false;
}
@@ -403,7 +409,7 @@ bool ImageChunk::TryReconstruction(int level) {
strm.next_in = uncompressed_data_.data();
int ret = deflateInit2(&strm, level, METHOD, WINDOWBITS, MEMLEVEL, STRATEGY);
if (ret < 0) {
- printf("failed to initialize deflate: %d\n", ret);
+ LOG(ERROR) << "Failed to initialize deflate: " << ret;
return false;
}
@@ -414,7 +420,7 @@ bool ImageChunk::TryReconstruction(int level) {
strm.next_out = buffer.data();
ret = deflate(&strm, Z_FINISH);
if (ret < 0) {
- printf("failed to deflate: %d\n", ret);
+ LOG(ERROR) << "Failed to deflate: " << ret;
return false;
}
@@ -490,17 +496,19 @@ size_t PatchChunk::GetHeaderSize() const {
}
// Return the offset of the next patch into the patch data.
-size_t PatchChunk::WriteHeaderToFd(int fd, size_t offset) const {
+size_t PatchChunk::WriteHeaderToFd(int fd, size_t offset, size_t index) const {
Write4(fd, type_);
switch (type_) {
case CHUNK_NORMAL:
- printf("normal (%10zu, %10zu) %10zu\n", target_start_, target_len_, data_.size());
+ LOG(INFO) << android::base::StringPrintf("chunk %zu: normal (%10zu, %10zu) %10zu", index,
+ target_start_, target_len_, data_.size());
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 + data_.size();
case CHUNK_DEFLATE:
- printf("deflate (%10zu, %10zu) %10zu\n", target_start_, target_len_, data_.size());
+ LOG(INFO) << android::base::StringPrintf("chunk %zu: deflate (%10zu, %10zu) %10zu", index,
+ target_start_, target_len_, data_.size());
Write8(fd, static_cast<int64_t>(source_start_));
Write8(fd, static_cast<int64_t>(source_len_));
Write8(fd, static_cast<int64_t>(offset));
@@ -513,10 +521,11 @@ size_t PatchChunk::WriteHeaderToFd(int fd, size_t offset) const {
Write4(fd, ImageChunk::STRATEGY);
return offset + data_.size();
case CHUNK_RAW:
- printf("raw (%10zu, %10zu)\n", target_start_, target_len_);
+ LOG(INFO) << android::base::StringPrintf("chunk %zu: raw (%10zu, %10zu)", index,
+ target_start_, target_len_);
Write4(fd, static_cast<int32_t>(data_.size()));
if (!android::base::WriteFully(fd, data_.data(), data_.size())) {
- CHECK(false) << "failed to write " << data_.size() << " bytes patch";
+ CHECK(false) << "Failed to write " << data_.size() << " bytes patch";
}
return offset;
default:
@@ -545,14 +554,14 @@ bool PatchChunk::WritePatchDataToFd(const std::vector<PatchChunk>& patch_chunks,
// Write out the headers.
if (!android::base::WriteStringToFd("IMGDIFF" + std::to_string(VERSION), patch_fd)) {
- printf("failed to write \"IMGDIFF%zu\": %s\n", VERSION, strerror(errno));
+ PLOG(ERROR) << "Failed to write \"IMGDIFF" << VERSION << "\"";
return false;
}
Write4(patch_fd, static_cast<int32_t>(patch_chunks.size()));
+ LOG(INFO) << "Writing " << patch_chunks.size() << " patch headers...";
for (size_t i = 0; i < patch_chunks.size(); ++i) {
- printf("chunk %zu: ", i);
- offset = patch_chunks[i].WriteHeaderToFd(patch_fd, offset);
+ offset = patch_chunks[i].WriteHeaderToFd(patch_fd, offset, i);
}
// Append each chunk's bsdiff patch, in order.
@@ -561,7 +570,7 @@ bool PatchChunk::WritePatchDataToFd(const std::vector<PatchChunk>& patch_chunks,
continue;
}
if (!android::base::WriteFully(patch_fd, patch.data_.data(), patch.data_.size())) {
- printf("failed to write %zu bytes patch to patch_fd\n", patch.data_.size());
+ PLOG(ERROR) << "Failed to write " << patch.data_.size() << " bytes patch to patch_fd";
return false;
}
}
@@ -603,10 +612,9 @@ void Image::MergeAdjacentNormalChunks() {
void Image::DumpChunks() const {
std::string type = is_source_ ? "source" : "target";
- printf("Dumping chunks for %s\n", type.c_str());
+ LOG(INFO) << "Dumping chunks for " << type;
for (size_t i = 0; i < chunks_.size(); ++i) {
- printf("chunk %zu: ", i);
- chunks_[i].Dump();
+ chunks_[i].Dump(i);
}
}
@@ -615,19 +623,19 @@ bool Image::ReadFile(const std::string& filename, std::vector<uint8_t>* file_con
android::base::unique_fd fd(open(filename.c_str(), O_RDONLY));
if (fd == -1) {
- printf("failed to open \"%s\" %s\n", filename.c_str(), strerror(errno));
+ PLOG(ERROR) << "Failed to open " << filename;
return false;
}
struct stat st;
if (fstat(fd, &st) != 0) {
- printf("failed to stat \"%s\": %s\n", filename.c_str(), strerror(errno));
+ PLOG(ERROR) << "Failed to stat " << filename;
return false;
}
size_t sz = static_cast<size_t>(st.st_size);
file_content->resize(sz);
if (!android::base::ReadFully(fd, file_content->data(), sz)) {
- printf("failed to read \"%s\" %s\n", filename.c_str(), strerror(errno));
+ PLOG(ERROR) << "Failed to read " << filename;
return false;
}
fd.reset();
@@ -643,14 +651,14 @@ bool ZipModeImage::Initialize(const std::string& filename) {
// Omit the trailing zeros before we pass the file to ziparchive handler.
size_t zipfile_size;
if (!GetZipFileSize(&zipfile_size)) {
- printf("failed to parse the actual size of %s\n", filename.c_str());
+ LOG(ERROR) << "Failed to parse the actual size of " << filename;
return false;
}
ZipArchiveHandle handle;
int err = OpenArchiveFromMemory(const_cast<uint8_t*>(file_content_.data()), zipfile_size,
filename.c_str(), &handle);
if (err != 0) {
- printf("failed to open zip file %s: %s\n", filename.c_str(), ErrorCodeString(err));
+ LOG(ERROR) << "Failed to open zip file " << filename << ": " << ErrorCodeString(err);
CloseArchive(handle);
return false;
}
@@ -669,7 +677,7 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl
void* cookie;
int ret = StartIteration(handle, &cookie, nullptr, nullptr);
if (ret != 0) {
- printf("failed to iterate over entries in %s: %s\n", filename.c_str(), ErrorCodeString(ret));
+ LOG(ERROR) << "Failed to iterate over entries in " << filename << ": " << ErrorCodeString(ret);
return false;
}
@@ -685,7 +693,7 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl
}
if (ret != -1) {
- printf("Error while iterating over zip entries: %s\n", ErrorCodeString(ret));
+ LOG(ERROR) << "Error while iterating over zip entries: " << ErrorCodeString(ret);
return false;
}
std::sort(temp_entries.begin(), temp_entries.end(),
@@ -697,7 +705,7 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl
if (is_source_) {
for (auto& entry : temp_entries) {
if (!AddZipEntryToChunks(handle, entry.first, &entry.second)) {
- printf("Failed to add %s to source chunks\n", entry.first.c_str());
+ LOG(ERROR) << "Failed to add " << entry.first << " to source chunks";
return false;
}
}
@@ -725,7 +733,7 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl
// Add the next zip entry.
std::string entry_name = temp_entries[nextentry].first;
if (!AddZipEntryToChunks(handle, entry_name, &temp_entries[nextentry].second)) {
- printf("Failed to add %s to target chunks\n", entry_name.c_str());
+ LOG(ERROR) << "Failed to add " << entry_name << " to target chunks";
return false;
}
@@ -771,8 +779,8 @@ bool ZipModeImage::AddZipEntryToChunks(ZipArchiveHandle handle, const std::strin
std::vector<uint8_t> uncompressed_data(uncompressed_len);
int ret = ExtractToMemory(handle, entry, uncompressed_data.data(), uncompressed_len);
if (ret != 0) {
- printf("failed to extract %s with size %zu: %s\n", entry_name.c_str(), uncompressed_len,
- ErrorCodeString(ret));
+ LOG(ERROR) << "Failed to extract " << entry_name << " with size " << uncompressed_len << ": "
+ << ErrorCodeString(ret);
return false;
}
ImageChunk curr(CHUNK_DEFLATE, entry->offset, &file_content_, compressed_len, entry_name);
@@ -793,7 +801,7 @@ bool ZipModeImage::AddZipEntryToChunks(ZipArchiveHandle handle, const std::strin
// offset 22: comment, n bytes
bool ZipModeImage::GetZipFileSize(size_t* input_file_size) {
if (file_content_.size() < 22) {
- printf("file is too small to be a zip file\n");
+ LOG(ERROR) << "File is too small to be a zip file";
return false;
}
@@ -872,8 +880,8 @@ bool ZipModeImage::CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage*
} else if (!tgt_chunk.ReconstructDeflateChunk()) {
// We cannot recompress the data and get exactly the same bits as are in the input target
// image. Treat the chunk as a normal non-deflated chunk.
- printf("failed to reconstruct target deflate chunk [%s]; treating as normal\n",
- tgt_chunk.GetEntryName().c_str());
+ LOG(WARNING) << "Failed to reconstruct target deflate chunk [" << tgt_chunk.GetEntryName()
+ << "]; treating as normal";
tgt_chunk.ChangeDeflateChunkToNormal();
src_chunk->ChangeDeflateChunkToNormal();
@@ -902,7 +910,7 @@ bool ZipModeImage::SplitZipModeImageWithLimit(const ZipModeImage& tgt_image,
size_t limit = tgt_image.limit_;
src_image.DumpChunks();
- printf("Splitting %zu tgt chunks...\n", tgt_image.NumOfChunks());
+ LOG(INFO) << "Splitting " << tgt_image.NumOfChunks() << " tgt chunks...";
SortedRangeSet used_src_ranges; // ranges used for previous split source images.
@@ -1049,7 +1057,7 @@ void ZipModeImage::ValidateSplitImages(const std::vector<ZipModeImage>& split_tg
size_t total_tgt_size) {
CHECK_EQ(split_tgt_images.size(), split_src_images.size());
- printf("Validating %zu images\n", split_tgt_images.size());
+ LOG(INFO) << "Validating " << split_tgt_images.size() << " images";
// Verify that the target image pieces is continuous and can add up to the total size.
size_t last_offset = 0;
@@ -1081,7 +1089,7 @@ void ZipModeImage::ValidateSplitImages(const std::vector<ZipModeImage>& split_tg
bool ZipModeImage::GeneratePatchesInternal(const ZipModeImage& tgt_image,
const ZipModeImage& src_image,
std::vector<PatchChunk>* patch_chunks) {
- printf("Construct patches for %zu chunks...\n", tgt_image.NumOfChunks());
+ LOG(INFO) << "Constructing patches for " << tgt_image.NumOfChunks() << " chunks...";
patch_chunks->clear();
bsdiff::SuffixArrayIndexInterface* bsdiff_cache = nullptr;
@@ -1103,12 +1111,12 @@ bool ZipModeImage::GeneratePatchesInternal(const ZipModeImage& tgt_image,
std::vector<uint8_t> patch_data;
if (!ImageChunk::MakePatch(tgt_chunk, src_ref, &patch_data, bsdiff_cache_ptr)) {
- printf("Failed to generate patch, name: %s\n", tgt_chunk.GetEntryName().c_str());
+ LOG(ERROR) << "Failed to generate patch, name: " << tgt_chunk.GetEntryName();
return false;
}
- printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data.size(),
- tgt_chunk.GetRawDataLength());
+ LOG(INFO) << "patch " << i << " is " << patch_data.size() << " bytes (of "
+ << tgt_chunk.GetRawDataLength() << ")";
if (PatchChunk::RawDataIsSmaller(tgt_chunk, patch_data.size())) {
patch_chunks->emplace_back(tgt_chunk);
@@ -1133,7 +1141,7 @@ bool ZipModeImage::GeneratePatches(const ZipModeImage& tgt_image, const ZipModeI
android::base::unique_fd patch_fd(
open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR));
if (patch_fd == -1) {
- printf("failed to open \"%s\": %s\n", patch_name.c_str(), strerror(errno));
+ PLOG(ERROR) << "Failed to open " << patch_name;
return false;
}
@@ -1146,12 +1154,12 @@ bool ZipModeImage::GeneratePatches(const std::vector<ZipModeImage>& split_tgt_im
const std::string& patch_name,
const std::string& split_info_file,
const std::string& debug_dir) {
- printf("Construct patches for %zu split images...\n", split_tgt_images.size());
+ LOG(INFO) << "Constructing patches for " << split_tgt_images.size() << " split images...";
android::base::unique_fd patch_fd(
open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR));
if (patch_fd == -1) {
- printf("failed to open \"%s\": %s\n", patch_name.c_str(), strerror(errno));
+ PLOG(ERROR) << "Failed to open " << patch_name;
return false;
}
@@ -1160,7 +1168,7 @@ bool ZipModeImage::GeneratePatches(const std::vector<ZipModeImage>& split_tgt_im
std::vector<PatchChunk> patch_chunks;
if (!ZipModeImage::GeneratePatchesInternal(split_tgt_images[i], split_src_images[i],
&patch_chunks)) {
- printf("failed to generate split patch\n");
+ LOG(ERROR) << "Failed to generate split patch";
return false;
}
@@ -1188,12 +1196,12 @@ bool ZipModeImage::GeneratePatches(const std::vector<ZipModeImage>& split_tgt_im
open(src_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR));
if (fd == -1) {
- printf("Failed to open %s\n", src_name.c_str());
+ PLOG(ERROR) << "Failed to open " << src_name;
return false;
}
if (!android::base::WriteFully(fd, split_src_images[i].PseudoSource().DataForPatch(),
split_src_images[i].PseudoSource().DataLengthForPatch())) {
- printf("Failed to write split source data into %s\n", src_name.c_str());
+ PLOG(ERROR) << "Failed to write split source data into " << src_name;
return false;
}
@@ -1201,7 +1209,7 @@ bool ZipModeImage::GeneratePatches(const std::vector<ZipModeImage>& split_tgt_im
fd.reset(open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR));
if (fd == -1) {
- printf("Failed to open %s\n", patch_name.c_str());
+ PLOG(ERROR) << "Failed to open " << patch_name;
return false;
}
if (!PatchChunk::WritePatchDataToFd(patch_chunks, fd)) {
@@ -1219,8 +1227,7 @@ bool ZipModeImage::GeneratePatches(const std::vector<ZipModeImage>& split_tgt_im
std::string split_info_string = android::base::StringPrintf(
"%zu\n%zu\n", VERSION, split_info_list.size()) + android::base::Join(split_info_list, '\n');
if (!android::base::WriteStringToFile(split_info_string, split_info_file)) {
- printf("failed to write split info to \"%s\": %s\n", split_info_file.c_str(),
- strerror(errno));
+ PLOG(ERROR) << "Failed to write split info to " << split_info_file;
return false;
}
@@ -1265,7 +1272,7 @@ bool ImageModeImage::Initialize(const std::string& filename) {
// not expect zlib headers.
int ret = inflateInit2(&strm, -15);
if (ret < 0) {
- printf("failed to initialize inflate: %d\n", ret);
+ LOG(ERROR) << "Failed to initialize inflate: " << ret;
return false;
}
@@ -1277,8 +1284,8 @@ bool ImageModeImage::Initialize(const std::string& filename) {
strm.next_out = uncompressed_data.data() + uncompressed_len;
ret = inflate(&strm, Z_NO_FLUSH);
if (ret < 0) {
- printf("Warning: inflate failed [%s] at offset [%zu], treating as a normal chunk\n",
- strm.msg, chunk_offset);
+ LOG(WARNING) << "Inflate failed [" << strm.msg << "] at offset [" << chunk_offset
+ << "]; treating as a normal chunk";
break;
}
uncompressed_len = allocated - strm.avail_out;
@@ -1299,13 +1306,13 @@ bool ImageModeImage::Initialize(const std::string& filename) {
// matches the size of the data we got when we actually did the decompression.
size_t footer_index = pos + raw_data_len + GZIP_FOOTER_LEN - 4;
if (sz - footer_index < 4) {
- printf("Warning: invalid footer position; treating as a nomal chunk\n");
+ LOG(WARNING) << "invalid footer position; treating as a normal chunk";
continue;
}
size_t footer_size = get_unaligned<uint32_t>(file_content_.data() + footer_index);
if (footer_size != uncompressed_len) {
- printf("Warning: footer size %zu != decompressed size %zu; treating as a nomal chunk\n",
- footer_size, uncompressed_len);
+ LOG(WARNING) << "footer size " << footer_size << " != " << uncompressed_len
+ << "; treating as a normal chunk";
continue;
}
@@ -1345,12 +1352,12 @@ bool ImageModeImage::Initialize(const std::string& filename) {
bool ImageModeImage::SetBonusData(const std::vector<uint8_t>& bonus_data) {
CHECK(is_source_);
if (chunks_.size() < 2 || !chunks_[1].SetBonusData(bonus_data)) {
- printf("Failed to set bonus data\n");
+ LOG(ERROR) << "Failed to set bonus data";
DumpChunks();
return false;
}
- printf(" using %zu bytes of bonus data\n", bonus_data.size());
+ LOG(INFO) << " using " << bonus_data.size() << " bytes of bonus data";
return true;
}
@@ -1362,14 +1369,14 @@ bool ImageModeImage::CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeI
src_image->MergeAdjacentNormalChunks();
if (tgt_image->NumOfChunks() != src_image->NumOfChunks()) {
- printf("source and target don't have same number of chunks!\n");
+ LOG(ERROR) << "Source and target don't have same number of chunks!";
tgt_image->DumpChunks();
src_image->DumpChunks();
return false;
}
for (size_t i = 0; i < tgt_image->NumOfChunks(); ++i) {
if ((*tgt_image)[i].GetType() != (*src_image)[i].GetType()) {
- printf("source and target don't have same chunk structure! (chunk %zu)\n", i);
+ LOG(ERROR) << "Source and target don't have same chunk structure! (chunk " << i << ")";
tgt_image->DumpChunks();
src_image->DumpChunks();
return false;
@@ -1390,8 +1397,8 @@ bool ImageModeImage::CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeI
} else if (!tgt_chunk.ReconstructDeflateChunk()) {
// We cannot recompress the data and get exactly the same bits as are in the input target
// image, fall back to normal
- printf("failed to reconstruct target deflate chunk %zu [%s]; treating as normal\n", i,
- tgt_chunk.GetEntryName().c_str());
+ LOG(WARNING) << "Failed to reconstruct target deflate chunk " << i << " ["
+ << tgt_chunk.GetEntryName() << "]; treating as normal";
tgt_chunk.ChangeDeflateChunkToNormal();
src_chunk.ChangeDeflateChunkToNormal();
}
@@ -1403,7 +1410,7 @@ bool ImageModeImage::CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeI
src_image->MergeAdjacentNormalChunks();
if (tgt_image->NumOfChunks() != src_image->NumOfChunks()) {
// This shouldn't happen.
- printf("merging normal chunks went awry\n");
+ LOG(ERROR) << "Merging normal chunks went awry";
return false;
}
@@ -1415,7 +1422,7 @@ bool ImageModeImage::CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeI
bool ImageModeImage::GeneratePatches(const ImageModeImage& tgt_image,
const ImageModeImage& src_image,
const std::string& patch_name) {
- printf("Construct patches for %zu chunks...\n", tgt_image.NumOfChunks());
+ LOG(INFO) << "Constructing patches for " << tgt_image.NumOfChunks() << " chunks...";
std::vector<PatchChunk> patch_chunks;
patch_chunks.reserve(tgt_image.NumOfChunks());
@@ -1430,11 +1437,11 @@ bool ImageModeImage::GeneratePatches(const ImageModeImage& tgt_image,
std::vector<uint8_t> patch_data;
if (!ImageChunk::MakePatch(tgt_chunk, src_chunk, &patch_data, nullptr)) {
- printf("Failed to generate patch for target chunk %zu: ", i);
+ LOG(ERROR) << "Failed to generate patch for target chunk " << i;
return false;
}
- printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data.size(),
- tgt_chunk.GetRawDataLength());
+ LOG(INFO) << "patch " << i << " is " << patch_data.size() << " bytes (of "
+ << tgt_chunk.GetRawDataLength() << ")";
if (PatchChunk::RawDataIsSmaller(tgt_chunk, patch_data.size())) {
patch_chunks.emplace_back(tgt_chunk);
@@ -1448,7 +1455,7 @@ bool ImageModeImage::GeneratePatches(const ImageModeImage& tgt_image,
android::base::unique_fd patch_fd(
open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR));
if (patch_fd == -1) {
- printf("failed to open \"%s\": %s\n", patch_name.c_str(), strerror(errno));
+ PLOG(ERROR) << "Failed to open " << patch_name;
return false;
}
@@ -1456,6 +1463,7 @@ bool ImageModeImage::GeneratePatches(const ImageModeImage& tgt_image,
}
int imgdiff(int argc, const char** argv) {
+ bool verbose = false;
bool zip_mode = false;
std::vector<uint8_t> bonus_data;
size_t blocks_limit = 0;
@@ -1464,9 +1472,10 @@ int imgdiff(int argc, const char** argv) {
int opt;
int option_index;
- optind = 1; // Reset the getopt state so that we can call it multiple times for test.
+ optind = 0; // Reset the getopt state so that we can call it multiple times for test.
- while ((opt = getopt_long(argc, const_cast<char**>(argv), "zb:", OPTIONS, &option_index)) != -1) {
+ while ((opt = getopt_long(argc, const_cast<char**>(argv), "zb:v", OPTIONS, &option_index)) !=
+ -1) {
switch (opt) {
case 'z':
zip_mode = true;
@@ -1474,27 +1483,30 @@ int imgdiff(int argc, const char** argv) {
case 'b': {
android::base::unique_fd fd(open(optarg, O_RDONLY));
if (fd == -1) {
- printf("failed to open bonus file %s: %s\n", optarg, strerror(errno));
+ PLOG(ERROR) << "Failed to open bonus file " << optarg;
return 1;
}
struct stat st;
if (fstat(fd, &st) != 0) {
- printf("failed to stat bonus file %s: %s\n", optarg, strerror(errno));
+ PLOG(ERROR) << "Failed to stat bonus file " << optarg;
return 1;
}
size_t bonus_size = st.st_size;
bonus_data.resize(bonus_size);
if (!android::base::ReadFully(fd, bonus_data.data(), bonus_size)) {
- printf("failed to read bonus file %s: %s\n", optarg, strerror(errno));
+ PLOG(ERROR) << "Failed to read bonus file " << optarg;
return 1;
}
break;
}
+ case 'v':
+ verbose = true;
+ break;
case 0: {
std::string name = OPTIONS[option_index].name;
if (name == "block-limit" && !android::base::ParseUint(optarg, &blocks_limit)) {
- printf("failed to parse size blocks_limit: %s\n", optarg);
+ LOG(ERROR) << "Failed to parse size blocks_limit: " << optarg;
return 1;
} else if (name == "split-info") {
split_info_file = optarg;
@@ -1504,22 +1516,28 @@ int imgdiff(int argc, const char** argv) {
break;
}
default:
- printf("unexpected opt: %s\n", optarg);
+ LOG(ERROR) << "unexpected opt: " << static_cast<char>(opt);
return 2;
}
}
+ if (!verbose) {
+ android::base::SetMinimumLogSeverity(android::base::WARNING);
+ }
+
if (argc - optind != 3) {
- printf("usage: %s [options] <src-img> <tgt-img> <patch-file>\n", argv[0]);
- printf(
- " -z <zip-mode>, Generate patches in zip mode, src and tgt should be zip files.\n"
- " -b <bonus-file>, Bonus file in addition to src, image mode only.\n"
- " --block-limit, For large zips, split the src and tgt based on the block limit;\n"
- " and generate patches between each pair of pieces. Concatenate these\n"
- " patches together and output them into <patch-file>.\n"
- " --split-info, Output the split information (patch_size, tgt_size, src_ranges);\n"
- " zip mode with block-limit only.\n"
- " --debug_dir, Debug directory to put the split srcs and patches, zip mode only.\n");
+ LOG(ERROR) << "usage: " << argv[0] << " [options] <src-img> <tgt-img> <patch-file>";
+ LOG(ERROR)
+ << " -z <zip-mode>, Generate patches in zip mode, src and tgt should be zip files.\n"
+ " -b <bonus-file>, Bonus file in addition to src, image mode only.\n"
+ " --block-limit, For large zips, split the src and tgt based on the block limit;\n"
+ " and generate patches between each pair of pieces. Concatenate "
+ "these\n"
+ " patches together and output them into <patch-file>.\n"
+ " --split-info, Output the split information (patch_size, tgt_size, src_ranges);\n"
+ " zip mode with block-limit only.\n"
+ " --debug_dir, Debug directory to put the split srcs and patches, zip mode only.\n"
+ " -v, --verbose, Enable verbose logging.";
return 2;
}
@@ -1538,14 +1556,11 @@ int imgdiff(int argc, const char** argv) {
return 1;
}
- // TODO save and output the split information so that caller can create split transfer lists
- // accordingly.
-
// Compute bsdiff patches for each chunk's data (the uncompressed data, in the case of
// deflate chunks).
if (blocks_limit > 0) {
if (split_info_file.empty()) {
- printf("split-info path cannot be empty when generating patches with a block-limit.\n");
+ LOG(ERROR) << "split-info path cannot be empty when generating patches with a block-limit";
return 1;
}
diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp
index 25ba0a182..3682d6115 100644
--- a/applypatch/imgpatch.cpp
+++ b/applypatch/imgpatch.cpp
@@ -50,7 +50,7 @@ static inline int32_t Read4(const void *address) {
// This function is a wrapper of ApplyBSDiffPatch(). It has a custom sink function to deflate the
// patched data and stream the deflated data to output.
static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_len,
- const Value* patch, size_t patch_offset,
+ const Value& patch, size_t patch_offset,
const char* deflate_header, SinkFn sink, SHA_CTX* ctx) {
size_t expected_target_length = static_cast<size_t>(Read8(deflate_header + 32));
int level = Read4(deflate_header + 40);
@@ -135,48 +135,39 @@ static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_
int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data,
size_t patch_size, SinkFn sink) {
Value patch(VAL_BLOB, std::string(reinterpret_cast<const char*>(patch_data), patch_size));
-
- return ApplyImagePatch(old_data, old_size, &patch, sink, nullptr, nullptr);
+ return ApplyImagePatch(old_data, old_size, patch, sink, nullptr, nullptr);
}
-/*
- * Apply the patch given in 'patch_filename' to the source data given
- * by (old_data, old_size). Write the patched output to the 'output'
- * file, and update the SHA context with the output data as well.
- * Return 0 on success.
- */
-int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink,
+int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& patch, SinkFn sink,
SHA_CTX* ctx, const Value* bonus_data) {
- if (patch->data.size() < 12) {
+ if (patch.data.size() < 12) {
printf("patch too short to contain header\n");
return -1;
}
- // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
- // (IMGDIFF1, which is no longer supported, used CHUNK_NORMAL and
- // CHUNK_GZIP.)
- size_t pos = 12;
- const char* header = &patch->data[0];
- if (memcmp(header, "IMGDIFF2", 8) != 0) {
+ // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW. (IMGDIFF1, which is no longer
+ // supported, used CHUNK_NORMAL and CHUNK_GZIP.)
+ const char* const patch_header = patch.data.data();
+ if (memcmp(patch_header, "IMGDIFF2", 8) != 0) {
printf("corrupt patch file header (magic number)\n");
return -1;
}
- int num_chunks = Read4(header + 8);
-
+ int num_chunks = Read4(patch_header + 8);
+ size_t pos = 12;
for (int i = 0; i < num_chunks; ++i) {
// each chunk's header record starts with 4 bytes.
- if (pos + 4 > patch->data.size()) {
+ if (pos + 4 > patch.data.size()) {
printf("failed to read chunk %d record\n", i);
return -1;
}
- int type = Read4(&patch->data[pos]);
+ int type = Read4(patch_header + pos);
pos += 4;
if (type == CHUNK_NORMAL) {
- const char* normal_header = &patch->data[pos];
+ const char* normal_header = patch_header + pos;
pos += 24;
- if (pos > patch->data.size()) {
+ if (pos > patch.data.size()) {
printf("failed to read chunk %d normal header data\n", i);
return -1;
}
@@ -194,30 +185,32 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value*
return -1;
}
} else if (type == CHUNK_RAW) {
- const char* raw_header = &patch->data[pos];
+ const char* raw_header = patch_header + pos;
pos += 4;
- if (pos > patch->data.size()) {
+ if (pos > patch.data.size()) {
printf("failed to read chunk %d raw header data\n", i);
return -1;
}
size_t data_len = static_cast<size_t>(Read4(raw_header));
- if (pos + data_len > patch->data.size()) {
+ if (pos + data_len > patch.data.size()) {
printf("failed to read chunk %d raw data\n", i);
return -1;
}
- if (ctx) SHA1_Update(ctx, &patch->data[pos], data_len);
- if (sink(reinterpret_cast<const unsigned char*>(&patch->data[pos]), data_len) != data_len) {
+ if (ctx) {
+ SHA1_Update(ctx, patch_header + pos, data_len);
+ }
+ if (sink(reinterpret_cast<const unsigned char*>(patch_header + pos), data_len) != data_len) {
printf("failed to write chunk %d raw data\n", i);
return -1;
}
pos += data_len;
} else if (type == CHUNK_DEFLATE) {
// deflate chunks have an additional 60 bytes in their chunk header.
- const char* deflate_header = &patch->data[pos];
+ const char* deflate_header = patch_header + pos;
pos += 60;
- if (pos > patch->data.size()) {
+ if (pos > patch.data.size()) {
printf("failed to read chunk %d deflate header data\n", i);
return -1;
}
diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h
index 2a3b3ef39..6d7ffd78c 100644
--- a/applypatch/include/applypatch/applypatch.h
+++ b/applypatch/include/applypatch/applypatch.h
@@ -36,16 +36,16 @@ struct FileContents {
struct stat st;
};
-// When there isn't enough room on the target filesystem to hold the
-// patched version of the file, we copy the original here and delete
-// it to free up space. If the expected source file doesn't exist, or
-// is corrupted, we look to see if this file contains the bits we want
-// and use it as the source instead.
-#define CACHE_TEMP_SOURCE "/cache/saved.file"
+// When there isn't enough room on the target filesystem to hold the patched version of the file,
+// we copy the original here and delete it to free up space. If the expected source file doesn't
+// exist, or is corrupted, we look to see if the cached file contains the bits we want and use it as
+// the source instead. The default location for the cached source is "/cache/saved.file".
+extern std::string cache_temp_source;
using SinkFn = std::function<size_t(const unsigned char*, size_t)>;
// applypatch.cpp
+
int ShowLicenses();
size_t FreeSpaceForFile(const char* filename);
int CacheSizeCheck(size_t bytes);
@@ -67,15 +67,25 @@ int LoadFileContents(const char* filename, FileContents* file);
int SaveFileContents(const char* filename, const FileContents* file);
// bspatch.cpp
+
void ShowBSDiffLicense();
-int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch,
+
+// Applies the bsdiff-patch given in 'patch' (from offset 'patch_offset' to the end) to the source
+// data given by (old_data, old_size). Writes the patched output through the given 'sink', and
+// updates the SHA-1 context with the output data. Returns 0 on success.
+int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value& patch,
size_t patch_offset, SinkFn sink, SHA_CTX* ctx);
// imgpatch.cpp
-int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink,
+
+// Applies the imgdiff-patch given in 'patch' to the source data given by (old_data, old_size), with
+// the optional bonus data. Writes the patched output through the given 'sink', and updates the
+// SHA-1 context with the output data. Returns 0 on success.
+int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& patch, SinkFn sink,
SHA_CTX* ctx, const Value* bonus_data);
// freecache.cpp
+
int MakeFreeSpaceOnCache(size_t bytes_needed);
#endif
diff --git a/applypatch/include/applypatch/imgdiff_image.h b/applypatch/include/applypatch/imgdiff_image.h
index 00a84f3a9..0f74420f0 100644
--- a/applypatch/include/applypatch/imgdiff_image.h
+++ b/applypatch/include/applypatch/imgdiff_image.h
@@ -62,10 +62,7 @@ class ImageChunk {
const uint8_t* DataForPatch() const;
size_t DataLengthForPatch() const;
- void Dump() const {
- printf("type: %d, start: %zu, len: %zu, name: %s\n", type_, start_, DataLengthForPatch(),
- entry_name_.c_str());
- }
+ void Dump(size_t index) const;
void SetUncompressedData(std::vector<uint8_t> data);
bool SetBonusData(const std::vector<uint8_t>& bonus_data);
@@ -140,7 +137,7 @@ class PatchChunk {
private:
size_t GetHeaderSize() const;
- size_t WriteHeaderToFd(int fd, size_t offset) const;
+ size_t WriteHeaderToFd(int fd, size_t offset, size_t index) const;
// The patch chunk type is the same as the target chunk type. The only exception is we change
// the |type_| to CHUNK_RAW if target length is smaller than the patch size.
diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp
index f91446b43..aaeffdc5c 100644
--- a/bootloader_message/bootloader_message.cpp
+++ b/bootloader_message/bootloader_message.cpp
@@ -159,14 +159,8 @@ bool clear_bootloader_message(std::string* err) {
bool write_bootloader_message(const std::vector<std::string>& options, std::string* err) {
bootloader_message boot = {};
- strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
- strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
- for (const auto& s : options) {
- strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
- if (s.back() != '\n') {
- strlcat(boot.recovery, "\n", sizeof(boot.recovery));
- }
- }
+ update_bootloader_message_in_struct(&boot, options);
+
return write_bootloader_message(boot, err);
}
@@ -175,20 +169,27 @@ bool update_bootloader_message(const std::vector<std::string>& options, std::str
if (!read_bootloader_message(&boot, err)) {
return false;
}
+ update_bootloader_message_in_struct(&boot, options);
- // Zero out the entire fields.
- memset(boot.command, 0, sizeof(boot.command));
- memset(boot.recovery, 0, sizeof(boot.recovery));
+ return write_bootloader_message(boot, err);
+}
+
+bool update_bootloader_message_in_struct(bootloader_message* boot,
+ const std::vector<std::string>& options) {
+ if (!boot) return false;
+ // Replace the command & recovery fields.
+ memset(boot->command, 0, sizeof(boot->command));
+ memset(boot->recovery, 0, sizeof(boot->recovery));
- strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
- strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
+ strlcpy(boot->command, "boot-recovery", sizeof(boot->command));
+ strlcpy(boot->recovery, "recovery\n", sizeof(boot->recovery));
for (const auto& s : options) {
- strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
+ strlcat(boot->recovery, s.c_str(), sizeof(boot->recovery));
if (s.back() != '\n') {
- strlcat(boot.recovery, "\n", sizeof(boot.recovery));
+ strlcat(boot->recovery, "\n", sizeof(boot->recovery));
}
}
- return write_bootloader_message(boot, err);
+ return true;
}
bool write_reboot_bootloader(std::string* err) {
diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h
index 2ffbfc9e3..798f3bb8c 100644
--- a/bootloader_message/include/bootloader_message/bootloader_message.h
+++ b/bootloader_message/include/bootloader_message/bootloader_message.h
@@ -207,6 +207,11 @@ bool write_bootloader_message(const std::vector<std::string>& options, std::stri
// only update the command and recovery fields.
bool update_bootloader_message(const std::vector<std::string>& options, std::string* err);
+// Update bootloader message (boots into recovery with the |options|) in |boot|. Will only update
+// the command and recovery fields.
+bool update_bootloader_message_in_struct(bootloader_message* boot,
+ const std::vector<std::string>& options);
+
// Clear BCB.
bool clear_bootloader_message(std::string* err);
diff --git a/fuse_sdcard_provider.cpp b/fuse_sdcard_provider.cpp
index b0ecf96be..46bdf1774 100644
--- a/fuse_sdcard_provider.cpp
+++ b/fuse_sdcard_provider.cpp
@@ -14,72 +14,70 @@
* limitations under the License.
*/
-#include <stdlib.h>
+#include "fuse_sdcard_provider.h"
+
+#include <errno.h>
+#include <fcntl.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include <errno.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <unistd.h>
-#include <fcntl.h>
+
+#include <functional>
#include <android-base/file.h>
#include "fuse_sideload.h"
struct file_data {
- int fd; // the underlying sdcard file
+ int fd; // the underlying sdcard file
- uint64_t file_size;
- uint32_t block_size;
+ uint64_t file_size;
+ uint32_t block_size;
};
-static int read_block_file(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
- file_data* fd = reinterpret_cast<file_data*>(cookie);
-
- off64_t offset = ((off64_t) block) * fd->block_size;
- if (TEMP_FAILURE_RETRY(lseek64(fd->fd, offset, SEEK_SET)) == -1) {
- fprintf(stderr, "seek on sdcard failed: %s\n", strerror(errno));
- return -EIO;
- }
+static int read_block_file(const file_data& fd, uint32_t block, uint8_t* buffer,
+ uint32_t fetch_size) {
+ off64_t offset = static_cast<off64_t>(block) * fd.block_size;
+ if (TEMP_FAILURE_RETRY(lseek64(fd.fd, offset, SEEK_SET)) == -1) {
+ fprintf(stderr, "seek on sdcard failed: %s\n", strerror(errno));
+ return -EIO;
+ }
- if (!android::base::ReadFully(fd->fd, buffer, fetch_size)) {
- fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno));
- return -EIO;
- }
-
- return 0;
-}
+ if (!android::base::ReadFully(fd.fd, buffer, fetch_size)) {
+ fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno));
+ return -EIO;
+ }
-static void close_file(void* cookie) {
- file_data* fd = reinterpret_cast<file_data*>(cookie);
- close(fd->fd);
+ return 0;
}
bool start_sdcard_fuse(const char* path) {
- struct stat sb;
- if (stat(path, &sb) == -1) {
- fprintf(stderr, "failed to stat %s: %s\n", path, strerror(errno));
- return false;
- }
+ struct stat sb;
+ if (stat(path, &sb) == -1) {
+ fprintf(stderr, "failed to stat %s: %s\n", path, strerror(errno));
+ return false;
+ }
- file_data fd;
- fd.fd = open(path, O_RDONLY);
- if (fd.fd == -1) {
- fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno));
- return false;
- }
- fd.file_size = sb.st_size;
- fd.block_size = 65536;
+ file_data fd;
+ fd.fd = open(path, O_RDONLY);
+ if (fd.fd == -1) {
+ fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno));
+ return false;
+ }
+ fd.file_size = sb.st_size;
+ fd.block_size = 65536;
- provider_vtab vtab;
- vtab.read_block = read_block_file;
- vtab.close = close_file;
+ provider_vtab vtab;
+ vtab.read_block = std::bind(&read_block_file, fd, std::placeholders::_1, std::placeholders::_2,
+ std::placeholders::_3);
+ vtab.close = [&fd]() { close(fd.fd); };
- // The installation process expects to find the sdcard unmounted.
- // Unmount it with MNT_DETACH so that our open file continues to
- // work but new references see it as unmounted.
- umount2("/sdcard", MNT_DETACH);
+ // The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so
+ // that our open file continues to work but new references see it as unmounted.
+ umount2("/sdcard", MNT_DETACH);
- return run_fuse_sideload(&vtab, &fd, fd.file_size, fd.block_size) == 0;
+ return run_fuse_sideload(vtab, fd.file_size, fd.block_size) == 0;
}
diff --git a/fuse_sideload.cpp b/fuse_sideload.cpp
index 219374fdb..1c7e98f01 100644
--- a/fuse_sideload.cpp
+++ b/fuse_sideload.cpp
@@ -41,337 +41,310 @@
// two files is implemented. In particular, you can't opendir() or
// readdir() on the "/sideload" directory; ls on it won't work.
-#include <ctype.h>
-#include <dirent.h>
+#include "fuse_sideload.h"
+
#include <errno.h>
#include <fcntl.h>
-#include <limits.h>
+#include <limits.h> // PATH_MAX
#include <linux/fuse.h>
-#include <pthread.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/inotify.h>
#include <sys/mount.h>
-#include <sys/param.h>
-#include <sys/resource.h>
+#include <sys/param.h> // MIN
#include <sys/stat.h>
-#include <sys/statfs.h>
-#include <sys/time.h>
#include <sys/uio.h>
#include <unistd.h>
+#include <array>
#include <string>
+#include <vector>
#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
#include <openssl/sha.h>
-#include "fuse_sideload.h"
+static constexpr uint64_t PACKAGE_FILE_ID = FUSE_ROOT_ID + 1;
+static constexpr uint64_t EXIT_FLAG_ID = FUSE_ROOT_ID + 2;
-#define PACKAGE_FILE_ID (FUSE_ROOT_ID+1)
-#define EXIT_FLAG_ID (FUSE_ROOT_ID+2)
+static constexpr int NO_STATUS = 1;
+static constexpr int NO_STATUS_EXIT = 2;
-#define NO_STATUS 1
-#define NO_STATUS_EXIT 2
+using SHA256Digest = std::array<uint8_t, SHA256_DIGEST_LENGTH>;
struct fuse_data {
- int ffd; // file descriptor for the fuse socket
+ android::base::unique_fd ffd; // file descriptor for the fuse socket
- struct provider_vtab* vtab;
- void* cookie;
+ provider_vtab vtab;
- uint64_t file_size; // bytes
+ uint64_t file_size; // bytes
- uint32_t block_size; // block size that the adb host is using to send the file to us
- uint32_t file_blocks; // file size in block_size blocks
+ uint32_t block_size; // block size that the adb host is using to send the file to us
+ uint32_t file_blocks; // file size in block_size blocks
- uid_t uid;
- gid_t gid;
+ uid_t uid;
+ gid_t gid;
- uint32_t curr_block; // cache the block most recently read from the host
- uint8_t* block_data;
+ uint32_t curr_block; // cache the block most recently read from the host
+ uint8_t* block_data;
- uint8_t* extra_block; // another block of storage for reads that
- // span two blocks
+ uint8_t* extra_block; // another block of storage for reads that span two blocks
- uint8_t* hashes; // SHA-256 hash of each block (all zeros
- // if block hasn't been read yet)
+ std::vector<SHA256Digest>
+ hashes; // SHA-256 hash of each block (all zeros if block hasn't been read yet)
};
-static void fuse_reply(struct fuse_data* fd, __u64 unique, const void *data, size_t len)
-{
- struct fuse_out_header hdr;
- struct iovec vec[2];
- int res;
-
- hdr.len = len + sizeof(hdr);
- hdr.error = 0;
- hdr.unique = unique;
-
- vec[0].iov_base = &hdr;
- vec[0].iov_len = sizeof(hdr);
- vec[1].iov_base = /* const_cast */(void*)(data);
- vec[1].iov_len = len;
-
- res = writev(fd->ffd, vec, 2);
- if (res < 0) {
- printf("*** REPLY FAILED *** %s\n", strerror(errno));
- }
+static void fuse_reply(const fuse_data* fd, uint64_t unique, const void* data, size_t len) {
+ fuse_out_header hdr;
+ hdr.len = len + sizeof(hdr);
+ hdr.error = 0;
+ hdr.unique = unique;
+
+ struct iovec vec[2];
+ vec[0].iov_base = &hdr;
+ vec[0].iov_len = sizeof(hdr);
+ vec[1].iov_base = const_cast<void*>(data);
+ vec[1].iov_len = len;
+
+ int res = writev(fd->ffd, vec, 2);
+ if (res == -1) {
+ printf("*** REPLY FAILED *** %s\n", strerror(errno));
+ }
}
-static int handle_init(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
- const struct fuse_init_in* req = reinterpret_cast<const struct fuse_init_in*>(data);
- struct fuse_init_out out;
- size_t fuse_struct_size;
-
-
- /* Kernel 2.6.16 is the first stable kernel with struct fuse_init_out
- * defined (fuse version 7.6). The structure is the same from 7.6 through
- * 7.22. Beginning with 7.23, the structure increased in size and added
- * new parameters.
- */
- if (req->major != FUSE_KERNEL_VERSION || req->minor < 6) {
- printf("Fuse kernel version mismatch: Kernel version %d.%d, Expected at least %d.6",
- req->major, req->minor, FUSE_KERNEL_VERSION);
- return -1;
- }
+static int handle_init(void* data, fuse_data* fd, const fuse_in_header* hdr) {
+ const fuse_init_in* req = static_cast<const fuse_init_in*>(data);
+
+ // Kernel 2.6.16 is the first stable kernel with struct fuse_init_out defined (fuse version 7.6).
+ // The structure is the same from 7.6 through 7.22. Beginning with 7.23, the structure increased
+ // in size and added new parameters.
+ if (req->major != FUSE_KERNEL_VERSION || req->minor < 6) {
+ printf("Fuse kernel version mismatch: Kernel version %d.%d, Expected at least %d.6", req->major,
+ req->minor, FUSE_KERNEL_VERSION);
+ return -1;
+ }
- out.minor = MIN(req->minor, FUSE_KERNEL_MINOR_VERSION);
- fuse_struct_size = sizeof(out);
+ fuse_init_out out;
+ out.minor = MIN(req->minor, FUSE_KERNEL_MINOR_VERSION);
+ size_t fuse_struct_size = sizeof(out);
#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE)
- /* FUSE_KERNEL_VERSION >= 23. */
+ /* FUSE_KERNEL_VERSION >= 23. */
- /* If the kernel only works on minor revs older than or equal to 22,
- * then use the older structure size since this code only uses the 7.22
- * version of the structure. */
- if (req->minor <= 22) {
- fuse_struct_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
- }
+ // If the kernel only works on minor revs older than or equal to 22, then use the older structure
+ // size since this code only uses the 7.22 version of the structure.
+ if (req->minor <= 22) {
+ fuse_struct_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
+ }
#endif
- out.major = FUSE_KERNEL_VERSION;
- out.max_readahead = req->max_readahead;
- out.flags = 0;
- out.max_background = 32;
- out.congestion_threshold = 32;
- out.max_write = 4096;
- fuse_reply(fd, hdr->unique, &out, fuse_struct_size);
+ out.major = FUSE_KERNEL_VERSION;
+ out.max_readahead = req->max_readahead;
+ out.flags = 0;
+ out.max_background = 32;
+ out.congestion_threshold = 32;
+ out.max_write = 4096;
+ fuse_reply(fd, hdr->unique, &out, fuse_struct_size);
- return NO_STATUS;
+ return NO_STATUS;
}
-static void fill_attr(struct fuse_attr* attr, struct fuse_data* fd,
- uint64_t nodeid, uint64_t size, uint32_t mode) {
- memset(attr, 0, sizeof(*attr));
- attr->nlink = 1;
- attr->uid = fd->uid;
- attr->gid = fd->gid;
- attr->blksize = 4096;
-
- attr->ino = nodeid;
- attr->size = size;
- attr->blocks = (size == 0) ? 0 : (((size-1) / attr->blksize) + 1);
- attr->mode = mode;
+static void fill_attr(fuse_attr* attr, const fuse_data* fd, uint64_t nodeid, uint64_t size,
+ uint32_t mode) {
+ *attr = {};
+ attr->nlink = 1;
+ attr->uid = fd->uid;
+ attr->gid = fd->gid;
+ attr->blksize = 4096;
+
+ attr->ino = nodeid;
+ attr->size = size;
+ attr->blocks = (size == 0) ? 0 : (((size - 1) / attr->blksize) + 1);
+ attr->mode = mode;
}
-static int handle_getattr(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) {
- struct fuse_attr_out out;
- memset(&out, 0, sizeof(out));
- out.attr_valid = 10;
-
- if (hdr->nodeid == FUSE_ROOT_ID) {
- fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555);
- } else if (hdr->nodeid == PACKAGE_FILE_ID) {
- fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444);
- } else if (hdr->nodeid == EXIT_FLAG_ID) {
- fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0);
- } else {
- return -ENOENT;
- }
+static int handle_getattr(void* /* data */, const fuse_data* fd, const fuse_in_header* hdr) {
+ fuse_attr_out out = {};
+ out.attr_valid = 10;
+
+ if (hdr->nodeid == FUSE_ROOT_ID) {
+ fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555);
+ } else if (hdr->nodeid == PACKAGE_FILE_ID) {
+ fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444);
+ } else if (hdr->nodeid == EXIT_FLAG_ID) {
+ fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0);
+ } else {
+ return -ENOENT;
+ }
- fuse_reply(fd, hdr->unique, &out, sizeof(out));
- return (hdr->nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS;
+ fuse_reply(fd, hdr->unique, &out, sizeof(out));
+ return (hdr->nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS;
}
-static int handle_lookup(void* data, struct fuse_data* fd,
- const struct fuse_in_header* hdr) {
- struct fuse_entry_out out;
- memset(&out, 0, sizeof(out));
- out.entry_valid = 10;
- out.attr_valid = 10;
-
- if (strncmp(FUSE_SIDELOAD_HOST_FILENAME, reinterpret_cast<const char*>(data),
- sizeof(FUSE_SIDELOAD_HOST_FILENAME)) == 0) {
- out.nodeid = PACKAGE_FILE_ID;
- out.generation = PACKAGE_FILE_ID;
- fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444);
- } else if (strncmp(FUSE_SIDELOAD_HOST_EXIT_FLAG, reinterpret_cast<const char*>(data),
- sizeof(FUSE_SIDELOAD_HOST_EXIT_FLAG)) == 0) {
- out.nodeid = EXIT_FLAG_ID;
- out.generation = EXIT_FLAG_ID;
- fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0);
- } else {
- return -ENOENT;
- }
+static int handle_lookup(void* data, const fuse_data* fd, const fuse_in_header* hdr) {
+ if (data == nullptr) return -ENOENT;
+
+ fuse_entry_out out = {};
+ out.entry_valid = 10;
+ out.attr_valid = 10;
+
+ std::string filename(static_cast<const char*>(data));
+ if (filename == FUSE_SIDELOAD_HOST_FILENAME) {
+ out.nodeid = PACKAGE_FILE_ID;
+ out.generation = PACKAGE_FILE_ID;
+ fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444);
+ } else if (filename == FUSE_SIDELOAD_HOST_EXIT_FLAG) {
+ out.nodeid = EXIT_FLAG_ID;
+ out.generation = EXIT_FLAG_ID;
+ fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0);
+ } else {
+ return -ENOENT;
+ }
- fuse_reply(fd, hdr->unique, &out, sizeof(out));
- return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS;
+ fuse_reply(fd, hdr->unique, &out, sizeof(out));
+ return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS;
}
-static int handle_open(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) {
- if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM;
- if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT;
+static int handle_open(void* /* data */, const fuse_data* fd, const fuse_in_header* hdr) {
+ if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM;
+ if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT;
- struct fuse_open_out out;
- memset(&out, 0, sizeof(out));
- out.fh = 10; // an arbitrary number; we always use the same handle
- fuse_reply(fd, hdr->unique, &out, sizeof(out));
- return NO_STATUS;
+ fuse_open_out out = {};
+ out.fh = 10; // an arbitrary number; we always use the same handle
+ fuse_reply(fd, hdr->unique, &out, sizeof(out));
+ return NO_STATUS;
}
-static int handle_flush(void* /* data */, struct fuse_data* /* fd */,
- const struct fuse_in_header* /* hdr */) {
- return 0;
+static int handle_flush(void* /* data */, fuse_data* /* fd */, const fuse_in_header* /* hdr */) {
+ return 0;
}
-static int handle_release(void* /* data */, struct fuse_data* /* fd */,
- const struct fuse_in_header* /* hdr */) {
- return 0;
+static int handle_release(void* /* data */, fuse_data* /* fd */, const fuse_in_header* /* hdr */) {
+ return 0;
}
// Fetch a block from the host into fd->curr_block and fd->block_data.
// Returns 0 on successful fetch, negative otherwise.
-static int fetch_block(struct fuse_data* fd, uint32_t block) {
- if (block == fd->curr_block) {
- return 0;
- }
+static int fetch_block(fuse_data* fd, uint32_t block) {
+ if (block == fd->curr_block) {
+ return 0;
+ }
- if (block >= fd->file_blocks) {
- memset(fd->block_data, 0, fd->block_size);
- fd->curr_block = block;
- return 0;
- }
+ if (block >= fd->file_blocks) {
+ memset(fd->block_data, 0, fd->block_size);
+ fd->curr_block = block;
+ return 0;
+ }
- size_t fetch_size = fd->block_size;
- if (block * fd->block_size + fetch_size > fd->file_size) {
- // If we're reading the last (partial) block of the file,
- // expect a shorter response from the host, and pad the rest
- // of the block with zeroes.
- fetch_size = fd->file_size - (block * fd->block_size);
- memset(fd->block_data + fetch_size, 0, fd->block_size - fetch_size);
- }
+ size_t fetch_size = fd->block_size;
+ if (block * fd->block_size + fetch_size > fd->file_size) {
+ // If we're reading the last (partial) block of the file, expect a shorter response from the
+ // host, and pad the rest of the block with zeroes.
+ fetch_size = fd->file_size - (block * fd->block_size);
+ memset(fd->block_data + fetch_size, 0, fd->block_size - fetch_size);
+ }
- int result = fd->vtab->read_block(fd->cookie, block, fd->block_data, fetch_size);
- if (result < 0) return result;
+ int result = fd->vtab.read_block(block, fd->block_data, fetch_size);
+ if (result < 0) return result;
- fd->curr_block = block;
+ fd->curr_block = block;
- // Verify the hash of the block we just got from the host.
- //
- // - If the hash of the just-received data matches the stored hash
- // for the block, accept it.
- // - If the stored hash is all zeroes, store the new hash and
- // accept the block (this is the first time we've read this
- // block).
- // - Otherwise, return -EINVAL for the read.
-
- uint8_t hash[SHA256_DIGEST_LENGTH];
- SHA256(fd->block_data, fd->block_size, hash);
- uint8_t* blockhash = fd->hashes + block * SHA256_DIGEST_LENGTH;
- if (memcmp(hash, blockhash, SHA256_DIGEST_LENGTH) == 0) {
- return 0;
- }
+ // Verify the hash of the block we just got from the host.
+ //
+ // - If the hash of the just-received data matches the stored hash for the block, accept it.
+ // - If the stored hash is all zeroes, store the new hash and accept the block (this is the first
+ // time we've read this block).
+ // - Otherwise, return -EINVAL for the read.
- int i;
- for (i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
- if (blockhash[i] != 0) {
- fd->curr_block = -1;
- return -EIO;
- }
- }
+ SHA256Digest hash;
+ SHA256(fd->block_data, fd->block_size, hash.data());
- memcpy(blockhash, hash, SHA256_DIGEST_LENGTH);
+ const SHA256Digest& blockhash = fd->hashes[block];
+ if (hash == blockhash) {
return 0;
+ }
+
+ for (uint8_t i : blockhash) {
+ if (i != 0) {
+ fd->curr_block = -1;
+ return -EIO;
+ }
+ }
+
+ fd->hashes[block] = hash;
+ return 0;
}
-static int handle_read(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
- const struct fuse_read_in* req = reinterpret_cast<const struct fuse_read_in*>(data);
- struct fuse_out_header outhdr;
- struct iovec vec[3];
- int vec_used;
- int result;
-
- if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT;
-
- uint64_t offset = req->offset;
- uint32_t size = req->size;
-
- // The docs on the fuse kernel interface are vague about what to
- // do when a read request extends past the end of the file. We
- // can return a short read -- the return structure does include a
- // length field -- but in testing that caused the program using
- // the file to segfault. (I speculate that this is due to the
- // reading program accessing it via mmap; maybe mmap dislikes when
- // you return something short of a whole page?) To fix this we
- // zero-pad reads that extend past the end of the file so we're
- // always returning exactly as many bytes as were requested.
- // (Users of the mapped file have to know its real length anyway.)
-
- outhdr.len = sizeof(outhdr) + size;
- outhdr.error = 0;
- outhdr.unique = hdr->unique;
- vec[0].iov_base = &outhdr;
- vec[0].iov_len = sizeof(outhdr);
-
- uint32_t block = offset / fd->block_size;
- result = fetch_block(fd, block);
+static int handle_read(void* data, fuse_data* fd, const fuse_in_header* hdr) {
+ if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT;
+
+ const fuse_read_in* req = static_cast<const fuse_read_in*>(data);
+ uint64_t offset = req->offset;
+ uint32_t size = req->size;
+
+ // The docs on the fuse kernel interface are vague about what to do when a read request extends
+ // past the end of the file. We can return a short read -- the return structure does include a
+ // length field -- but in testing that caused the program using the file to segfault. (I
+ // speculate that this is due to the reading program accessing it via mmap; maybe mmap dislikes
+ // when you return something short of a whole page?) To fix this we zero-pad reads that extend
+ // past the end of the file so we're always returning exactly as many bytes as were requested.
+ // (Users of the mapped file have to know its real length anyway.)
+
+ fuse_out_header outhdr;
+ outhdr.len = sizeof(outhdr) + size;
+ outhdr.error = 0;
+ outhdr.unique = hdr->unique;
+
+ struct iovec vec[3];
+ vec[0].iov_base = &outhdr;
+ vec[0].iov_len = sizeof(outhdr);
+
+ uint32_t block = offset / fd->block_size;
+ int result = fetch_block(fd, block);
+ if (result != 0) return result;
+
+ // Two cases:
+ //
+ // - the read request is entirely within this block. In this case we can reply immediately.
+ //
+ // - the read request goes over into the next block. Note that since we mount the filesystem
+ // with max_read=block_size, a read can never span more than two blocks. In this case we copy
+ // the block to extra_block and issue a fetch for the following block.
+
+ uint32_t block_offset = offset - (block * fd->block_size);
+
+ int vec_used;
+ if (size + block_offset <= fd->block_size) {
+ // First case: the read fits entirely in the first block.
+
+ vec[1].iov_base = fd->block_data + block_offset;
+ vec[1].iov_len = size;
+ vec_used = 2;
+ } else {
+ // Second case: the read spills over into the next block.
+
+ memcpy(fd->extra_block, fd->block_data + block_offset, fd->block_size - block_offset);
+ vec[1].iov_base = fd->extra_block;
+ vec[1].iov_len = fd->block_size - block_offset;
+
+ result = fetch_block(fd, block + 1);
if (result != 0) return result;
+ vec[2].iov_base = fd->block_data;
+ vec[2].iov_len = size - vec[1].iov_len;
+ vec_used = 3;
+ }
- // Two cases:
- //
- // - the read request is entirely within this block. In this
- // case we can reply immediately.
- //
- // - the read request goes over into the next block. Note that
- // since we mount the filesystem with max_read=block_size, a
- // read can never span more than two blocks. In this case we
- // copy the block to extra_block and issue a fetch for the
- // following block.
-
- uint32_t block_offset = offset - (block * fd->block_size);
-
- if (size + block_offset <= fd->block_size) {
- // First case: the read fits entirely in the first block.
-
- vec[1].iov_base = fd->block_data + block_offset;
- vec[1].iov_len = size;
- vec_used = 2;
- } else {
- // Second case: the read spills over into the next block.
-
- memcpy(fd->extra_block, fd->block_data + block_offset,
- fd->block_size - block_offset);
- vec[1].iov_base = fd->extra_block;
- vec[1].iov_len = fd->block_size - block_offset;
-
- result = fetch_block(fd, block+1);
- if (result != 0) return result;
- vec[2].iov_base = fd->block_data;
- vec[2].iov_len = size - vec[1].iov_len;
- vec_used = 3;
- }
-
- if (writev(fd->ffd, vec, vec_used) < 0) {
- printf("*** READ REPLY FAILED: %s ***\n", strerror(errno));
- }
- return NO_STATUS;
+ if (writev(fd->ffd, vec, vec_used) == -1) {
+ printf("*** READ REPLY FAILED: %s ***\n", strerror(errno));
+ }
+ return NO_STATUS;
}
-int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_size,
- uint32_t block_size) {
+int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t block_size,
+ const char* mount_point) {
// If something's already mounted on our mountpoint, try to remove it. (Mostly in case of a
// previous abnormal exit.)
- umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_FORCE);
+ umount2(mount_point, MNT_FORCE);
// fs/fuse/inode.c in kernel code uses the greater of 4096 and the passed-in max_read.
if (block_size < 4096) {
@@ -383,9 +356,8 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_si
return -1;
}
- struct fuse_data fd = {};
+ fuse_data fd = {};
fd.vtab = vtab;
- fd.cookie = cookie;
fd.file_size = file_size;
fd.block_size = block_size;
fd.file_blocks = (file_size == 0) ? 0 : (((file_size - 1) / block_size) + 1);
@@ -397,33 +369,27 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_si
goto done;
}
- fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_LENGTH);
- if (fd.hashes == NULL) {
- fprintf(stderr, "failed to allocate %d bites for hashes\n",
- fd.file_blocks * SHA256_DIGEST_LENGTH);
- result = -1;
- goto done;
- }
-
+ // All hashes will be zero-initialized.
+ fd.hashes.resize(fd.file_blocks);
fd.uid = getuid();
fd.gid = getgid();
fd.curr_block = -1;
- fd.block_data = (uint8_t*)malloc(block_size);
- if (fd.block_data == NULL) {
+ fd.block_data = static_cast<uint8_t*>(malloc(block_size));
+ if (fd.block_data == nullptr) {
fprintf(stderr, "failed to allocate %d bites for block_data\n", block_size);
result = -1;
goto done;
}
- fd.extra_block = (uint8_t*)malloc(block_size);
- if (fd.extra_block == NULL) {
+ fd.extra_block = static_cast<uint8_t*>(malloc(block_size));
+ if (fd.extra_block == nullptr) {
fprintf(stderr, "failed to allocate %d bites for extra_block\n", block_size);
result = -1;
goto done;
}
- fd.ffd = open("/dev/fuse", O_RDWR);
- if (fd.ffd < 0) {
+ fd.ffd.reset(open("/dev/fuse", O_RDWR));
+ if (!fd.ffd) {
perror("open /dev/fuse");
result = -1;
goto done;
@@ -431,18 +397,18 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_si
{
std::string opts = android::base::StringPrintf(
- "fd=%d,user_id=%d,group_id=%d,max_read=%u,allow_other,rootmode=040000", fd.ffd, fd.uid,
- fd.gid, block_size);
+ "fd=%d,user_id=%d,group_id=%d,max_read=%u,allow_other,rootmode=040000", fd.ffd.get(),
+ fd.uid, fd.gid, block_size);
- result = mount("/dev/fuse", FUSE_SIDELOAD_HOST_MOUNTPOINT, "fuse",
- MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, opts.c_str());
- if (result < 0) {
+ result = mount("/dev/fuse", mount_point, "fuse", MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC,
+ opts.c_str());
+ if (result == -1) {
perror("mount");
goto done;
}
}
- uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX * 8];
+ uint8_t request_buffer[sizeof(fuse_in_header) + PATH_MAX * 8];
for (;;) {
ssize_t len = TEMP_FAILURE_RETRY(read(fd.ffd, request_buffer, sizeof(request_buffer)));
if (len == -1) {
@@ -454,13 +420,13 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_si
continue;
}
- if (static_cast<size_t>(len) < sizeof(struct fuse_in_header)) {
+ if (static_cast<size_t>(len) < sizeof(fuse_in_header)) {
fprintf(stderr, "request too short: len=%zd\n", len);
continue;
}
- struct fuse_in_header* hdr = reinterpret_cast<struct fuse_in_header*>(request_buffer);
- void* data = request_buffer + sizeof(struct fuse_in_header);
+ fuse_in_header* hdr = reinterpret_cast<fuse_in_header*>(request_buffer);
+ void* data = request_buffer + sizeof(fuse_in_header);
result = -ENOSYS;
@@ -504,7 +470,7 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_si
}
if (result != NO_STATUS) {
- struct fuse_out_header outhdr;
+ fuse_out_header outhdr;
outhdr.len = sizeof(outhdr);
outhdr.error = result;
outhdr.unique = hdr->unique;
@@ -513,15 +479,12 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_si
}
done:
- fd.vtab->close(fd.cookie);
+ fd.vtab.close();
- result = umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_DETACH);
- if (result < 0) {
- printf("fuse_sideload umount failed: %s\n", strerror(errno));
+ if (umount2(mount_point, MNT_DETACH) == -1) {
+ fprintf(stderr, "fuse_sideload umount failed: %s\n", strerror(errno));
}
- if (fd.ffd) close(fd.ffd);
- free(fd.hashes);
free(fd.block_data);
free(fd.extra_block);
diff --git a/fuse_sideload.h b/fuse_sideload.h
index c0b16efbe..1b34cbdb0 100644
--- a/fuse_sideload.h
+++ b/fuse_sideload.h
@@ -17,22 +17,24 @@
#ifndef __FUSE_SIDELOAD_H
#define __FUSE_SIDELOAD_H
-// define the filenames created by the sideload FUSE filesystem
-#define FUSE_SIDELOAD_HOST_MOUNTPOINT "/sideload"
-#define FUSE_SIDELOAD_HOST_FILENAME "package.zip"
-#define FUSE_SIDELOAD_HOST_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_FILENAME)
-#define FUSE_SIDELOAD_HOST_EXIT_FLAG "exit"
-#define FUSE_SIDELOAD_HOST_EXIT_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_EXIT_FLAG)
+#include <functional>
+
+// Define the filenames created by the sideload FUSE filesystem.
+static constexpr const char* FUSE_SIDELOAD_HOST_MOUNTPOINT = "/sideload";
+static constexpr const char* FUSE_SIDELOAD_HOST_FILENAME = "package.zip";
+static constexpr const char* FUSE_SIDELOAD_HOST_PATHNAME = "/sideload/package.zip";
+static constexpr const char* FUSE_SIDELOAD_HOST_EXIT_FLAG = "exit";
+static constexpr const char* FUSE_SIDELOAD_HOST_EXIT_PATHNAME = "/sideload/exit";
struct provider_vtab {
- // read a block
- int (*read_block)(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size);
+ // read a block
+ std::function<int(uint32_t block, uint8_t* buffer, uint32_t fetch_size)> read_block;
- // close down
- void (*close)(void* cookie);
+ // close down
+ std::function<void(void)> close;
};
-int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
- uint64_t file_size, uint32_t block_size);
+int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t block_size,
+ const char* mount_point = FUSE_SIDELOAD_HOST_MOUNTPOINT);
#endif
diff --git a/minadbd/Android.mk b/minadbd/Android.mk
index 803171d99..50e3b34ef 100644
--- a/minadbd/Android.mk
+++ b/minadbd/Android.mk
@@ -16,10 +16,9 @@ LOCAL_PATH:= $(call my-dir)
minadbd_cflags := \
-Wall -Werror \
- -Wno-missing-field-initializers \
-DADB_HOST=0 \
-# libadbd (static library)
+# libminadbd (static library)
# ===============================
include $(CLEAR_VARS)
@@ -30,7 +29,6 @@ LOCAL_SRC_FILES := \
LOCAL_MODULE := libminadbd
LOCAL_CFLAGS := $(minadbd_cflags)
-LOCAL_CONLY_FLAGS := -Wimplicit-function-declaration
LOCAL_C_INCLUDES := bootable/recovery system/core/adb
LOCAL_WHOLE_STATIC_LIBRARIES := libadbd
LOCAL_STATIC_LIBRARIES := libcrypto libbase
@@ -46,7 +44,12 @@ LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_SRC_FILES := fuse_adb_provider_test.cpp
LOCAL_CFLAGS := $(minadbd_cflags)
LOCAL_C_INCLUDES := $(LOCAL_PATH) system/core/adb
-LOCAL_STATIC_LIBRARIES := libminadbd
-LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
+LOCAL_STATIC_LIBRARIES := \
+ libBionicGtestMain \
+ libminadbd
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libbase \
+ libcutils
include $(BUILD_NATIVE_TEST)
diff --git a/minadbd/fuse_adb_provider.cpp b/minadbd/fuse_adb_provider.cpp
index 0f4c2563d..9bd3f2392 100644
--- a/minadbd/fuse_adb_provider.cpp
+++ b/minadbd/fuse_adb_provider.cpp
@@ -14,46 +14,43 @@
* limitations under the License.
*/
-#include <stdlib.h>
+#include "fuse_adb_provider.h"
+
+#include <errno.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include <errno.h>
+
+#include <functional>
#include "adb.h"
#include "adb_io.h"
-#include "fuse_adb_provider.h"
#include "fuse_sideload.h"
-int read_block_adb(void* data, uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
- adb_data* ad = reinterpret_cast<adb_data*>(data);
-
- if (!WriteFdFmt(ad->sfd, "%08u", block)) {
- fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno));
- return -EIO;
- }
+int read_block_adb(const adb_data& ad, uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
+ if (!WriteFdFmt(ad.sfd, "%08u", block)) {
+ fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno));
+ return -EIO;
+ }
- if (!ReadFdExactly(ad->sfd, buffer, fetch_size)) {
- fprintf(stderr, "failed to read from adb host: %s\n", strerror(errno));
- return -EIO;
- }
-
- return 0;
-}
+ if (!ReadFdExactly(ad.sfd, buffer, fetch_size)) {
+ fprintf(stderr, "failed to read from adb host: %s\n", strerror(errno));
+ return -EIO;
+ }
-static void close_adb(void* data) {
- adb_data* ad = reinterpret_cast<adb_data*>(data);
- WriteFdExactly(ad->sfd, "DONEDONE");
+ return 0;
}
int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size) {
- adb_data ad;
- ad.sfd = sfd;
- ad.file_size = file_size;
- ad.block_size = block_size;
+ adb_data ad;
+ ad.sfd = sfd;
+ ad.file_size = file_size;
+ ad.block_size = block_size;
- provider_vtab vtab;
- vtab.read_block = read_block_adb;
- vtab.close = close_adb;
+ provider_vtab vtab;
+ vtab.read_block = std::bind(read_block_adb, ad, std::placeholders::_1, std::placeholders::_2,
+ std::placeholders::_3);
+ vtab.close = [&ad]() { WriteFdExactly(ad.sfd, "DONEDONE"); };
- return run_fuse_sideload(&vtab, &ad, file_size, block_size);
+ return run_fuse_sideload(vtab, file_size, block_size);
}
diff --git a/minadbd/fuse_adb_provider.h b/minadbd/fuse_adb_provider.h
index 9941709b9..36d86d539 100644
--- a/minadbd/fuse_adb_provider.h
+++ b/minadbd/fuse_adb_provider.h
@@ -20,13 +20,13 @@
#include <stdint.h>
struct adb_data {
- int sfd; // file descriptor for the adb channel
+ int sfd; // file descriptor for the adb channel
- uint64_t file_size;
- uint32_t block_size;
+ uint64_t file_size;
+ uint32_t block_size;
};
-int read_block_adb(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size);
+int read_block_adb(const adb_data& ad, uint32_t block, uint8_t* buffer, uint32_t fetch_size);
int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size);
#endif
diff --git a/minadbd/fuse_adb_provider_test.cpp b/minadbd/fuse_adb_provider_test.cpp
index 31be2a64e..00250e505 100644
--- a/minadbd/fuse_adb_provider_test.cpp
+++ b/minadbd/fuse_adb_provider_test.cpp
@@ -46,8 +46,8 @@ TEST(fuse_adb_provider, read_block_adb) {
uint32_t block = 1234U;
const char expected_block[] = "00001234";
- ASSERT_EQ(0, read_block_adb(static_cast<void*>(&data), block,
- reinterpret_cast<uint8_t*>(block_data), sizeof(expected_data) - 1));
+ ASSERT_EQ(0, read_block_adb(data, block, reinterpret_cast<uint8_t*>(block_data),
+ sizeof(expected_data) - 1));
// Check that read_block_adb requested the right block.
char block_req[sizeof(expected_block)] = {};
@@ -84,7 +84,7 @@ TEST(fuse_adb_provider, read_block_adb_fail_write) {
signal(SIGPIPE, SIG_IGN);
char buf[1];
- ASSERT_EQ(-EIO, read_block_adb(static_cast<void*>(&data), 0, reinterpret_cast<uint8_t*>(buf), 1));
+ ASSERT_EQ(-EIO, read_block_adb(data, 0, reinterpret_cast<uint8_t*>(buf), 1));
close(sockets[0]);
}
diff --git a/otautil/include/otautil/rangeset.h b/otautil/include/otautil/rangeset.h
index c4234d181..e91d02ca6 100644
--- a/otautil/include/otautil/rangeset.h
+++ b/otautil/include/otautil/rangeset.h
@@ -30,28 +30,43 @@ class RangeSet {
explicit RangeSet(std::vector<Range>&& pairs);
+ // Parses the given string into a RangeSet. Returns the parsed RangeSet, or an empty RangeSet on
+ // errors.
static RangeSet Parse(const std::string& range_text);
+ // Appends the given Range to the current RangeSet.
+ bool PushBack(Range range);
+
+ // Clears all the ranges from the RangeSet.
+ void Clear();
+
std::string ToString() const;
- // Get the block number for the i-th (starting from 0) block in the RangeSet.
+ // Gets the block number for the i-th (starting from 0) block in the RangeSet.
size_t GetBlockNumber(size_t idx) const;
- // RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5"
- // and "5,7" are not overlapped.
+ // Returns whether the current RangeSet overlaps with other. RangeSet has half-closed half-open
+ // bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" and "5,7" are not overlapped.
bool Overlaps(const RangeSet& other) const;
- // size() gives the number of Range's in this RangeSet.
+ // Returns a vector of RangeSets that contain the same set of blocks represented by the current
+ // RangeSet. The RangeSets in the vector contain similar number of blocks, with a maximum delta
+ // of 1-block between any two of them. For example, 14 blocks would be split into 4 + 4 + 3 + 3,
+ // as opposed to 4 + 4 + 4 + 2. If the total number of blocks (T) is less than groups, it
+ // returns a vector of T 1-block RangeSets. Otherwise the number of the returned RangeSets must
+ // equal to groups. The current RangeSet remains intact after the split.
+ std::vector<RangeSet> Split(size_t groups) const;
+
+ // Returns the number of Range's in this RangeSet.
size_t size() const {
return ranges_.size();
}
- // blocks() gives the number of all blocks in this RangeSet.
+ // Returns the total number of blocks in this RangeSet.
size_t blocks() const {
return blocks_;
}
- // We provide const iterators only.
std::vector<Range>::const_iterator cbegin() const {
return ranges_.cbegin();
}
@@ -60,13 +75,20 @@ class RangeSet {
return ranges_.cend();
}
- // Need to provide begin()/end() since range-based loop expects begin()/end().
+ std::vector<Range>::iterator begin() {
+ return ranges_.begin();
+ }
+
+ std::vector<Range>::iterator end() {
+ return ranges_.end();
+ }
+
std::vector<Range>::const_iterator begin() const {
- return ranges_.cbegin();
+ return ranges_.begin();
}
std::vector<Range>::const_iterator end() const {
- return ranges_.cend();
+ return ranges_.end();
}
// Reverse const iterators for MoveRange().
@@ -78,6 +100,11 @@ class RangeSet {
return ranges_.crend();
}
+ // Returns whether the RangeSet is valid (i.e. non-empty).
+ explicit operator bool() const {
+ return !ranges_.empty();
+ }
+
const Range& operator[](size_t i) const {
return ranges_[i];
}
@@ -109,6 +136,9 @@ class RangeSet {
// every block in the original source.
class SortedRangeSet : public RangeSet {
public:
+ // The block size when working with offset and file length.
+ static constexpr size_t kBlockSize = 4096;
+
SortedRangeSet() {}
// Ranges in the the set should be mutually exclusive; and they're sorted by the start block.
@@ -122,8 +152,6 @@ class SortedRangeSet : public RangeSet {
// Compute the block range the file occupies, and insert that range.
void Insert(size_t start, size_t len);
- void Clear();
-
using RangeSet::Overlaps;
bool Overlaps(size_t start, size_t len) const;
diff --git a/otautil/rangeset.cpp b/otautil/rangeset.cpp
index a121a4efc..96955b9d0 100644
--- a/otautil/rangeset.cpp
+++ b/otautil/rangeset.cpp
@@ -16,8 +16,10 @@
#include "otautil/rangeset.h"
+#include <limits.h>
#include <stddef.h>
+#include <algorithm>
#include <string>
#include <utility>
#include <vector>
@@ -28,47 +30,119 @@
#include <android-base/strings.h>
RangeSet::RangeSet(std::vector<Range>&& pairs) {
- CHECK_NE(pairs.size(), static_cast<size_t>(0)) << "Invalid number of tokens";
+ blocks_ = 0;
+ if (pairs.empty()) {
+ LOG(ERROR) << "Invalid number of tokens";
+ return;
+ }
- // Sanity check the input.
- size_t result = 0;
for (const auto& range : pairs) {
- CHECK_LT(range.first, range.second) << "Empty or negative range: " << range.first << ", "
- << range.second;
- size_t sz = range.second - range.first;
- CHECK_LE(result, SIZE_MAX - sz) << "RangeSet size overflow";
- result += sz;
+ if (!PushBack(range)) {
+ Clear();
+ return;
+ }
}
-
- ranges_ = pairs;
- blocks_ = result;
}
RangeSet RangeSet::Parse(const std::string& range_text) {
std::vector<std::string> pieces = android::base::Split(range_text, ",");
- CHECK_GE(pieces.size(), static_cast<size_t>(3)) << "Invalid range text: " << range_text;
+ if (pieces.size() < 3) {
+ LOG(ERROR) << "Invalid range text: " << range_text;
+ return {};
+ }
size_t num;
- CHECK(android::base::ParseUint(pieces[0], &num, static_cast<size_t>(INT_MAX)))
- << "Failed to parse the number of tokens: " << range_text;
-
- CHECK_NE(num, static_cast<size_t>(0)) << "Invalid number of tokens: " << range_text;
- CHECK_EQ(num % 2, static_cast<size_t>(0)) << "Number of tokens must be even: " << range_text;
- CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text;
+ if (!android::base::ParseUint(pieces[0], &num, static_cast<size_t>(INT_MAX))) {
+ LOG(ERROR) << "Failed to parse the number of tokens: " << range_text;
+ return {};
+ }
+ if (num == 0) {
+ LOG(ERROR) << "Invalid number of tokens: " << range_text;
+ return {};
+ }
+ if (num % 2 != 0) {
+ LOG(ERROR) << "Number of tokens must be even: " << range_text;
+ return {};
+ }
+ if (num != pieces.size() - 1) {
+ LOG(ERROR) << "Mismatching number of tokens: " << range_text;
+ return {};
+ }
std::vector<Range> pairs;
for (size_t i = 0; i < num; i += 2) {
size_t first;
- CHECK(android::base::ParseUint(pieces[i + 1], &first, static_cast<size_t>(INT_MAX)));
size_t second;
- CHECK(android::base::ParseUint(pieces[i + 2], &second, static_cast<size_t>(INT_MAX)));
-
+ if (!android::base::ParseUint(pieces[i + 1], &first, static_cast<size_t>(INT_MAX)) ||
+ !android::base::ParseUint(pieces[i + 2], &second, static_cast<size_t>(INT_MAX))) {
+ return {};
+ }
pairs.emplace_back(first, second);
}
-
return RangeSet(std::move(pairs));
}
+bool RangeSet::PushBack(Range range) {
+ if (range.first >= range.second) {
+ LOG(ERROR) << "Empty or negative range: " << range.first << ", " << range.second;
+ return false;
+ }
+ size_t sz = range.second - range.first;
+ if (blocks_ >= SIZE_MAX - sz) {
+ LOG(ERROR) << "RangeSet size overflow";
+ return false;
+ }
+
+ ranges_.push_back(std::move(range));
+ blocks_ += sz;
+ return true;
+}
+
+void RangeSet::Clear() {
+ ranges_.clear();
+ blocks_ = 0;
+}
+
+std::vector<RangeSet> RangeSet::Split(size_t groups) const {
+ if (ranges_.empty() || groups == 0) return {};
+
+ if (blocks_ < groups) {
+ groups = blocks_;
+ }
+
+ // Evenly distribute blocks, with the first few groups possibly containing one more.
+ size_t mean = blocks_ / groups;
+ std::vector<size_t> blocks_per_group(groups, mean);
+ std::fill_n(blocks_per_group.begin(), blocks_ % groups, mean + 1);
+
+ std::vector<RangeSet> result;
+
+ // Forward iterate Ranges and fill up each group with the desired number of blocks.
+ auto it = ranges_.cbegin();
+ Range range = *it;
+ for (const auto& blocks : blocks_per_group) {
+ RangeSet buffer;
+ size_t needed = blocks;
+ while (needed > 0) {
+ size_t range_blocks = range.second - range.first;
+ if (range_blocks > needed) {
+ // Split the current range and don't advance the iterator.
+ buffer.PushBack({ range.first, range.first + needed });
+ range.first = range.first + needed;
+ break;
+ }
+ buffer.PushBack(range);
+ it++;
+ if (it != ranges_.cend()) {
+ range = *it;
+ }
+ needed -= range_blocks;
+ }
+ result.push_back(std::move(buffer));
+ }
+ return result;
+}
+
std::string RangeSet::ToString() const {
if (ranges_.empty()) {
return "";
@@ -114,8 +188,6 @@ bool RangeSet::Overlaps(const RangeSet& other) const {
return false;
}
-static constexpr size_t kBlockSize = 4096;
-
// Ranges in the the set should be mutually exclusive; and they're sorted by the start block.
SortedRangeSet::SortedRangeSet(std::vector<Range>&& pairs) : RangeSet(std::move(pairs)) {
std::sort(ranges_.begin(), ranges_.end());
@@ -158,11 +230,6 @@ void SortedRangeSet::Insert(size_t start, size_t len) {
Insert(to_insert);
}
-void SortedRangeSet::Clear() {
- blocks_ = 0;
- ranges_.clear();
-}
-
bool SortedRangeSet::Overlaps(size_t start, size_t len) const {
RangeSet rs({ { start / kBlockSize, (start + len - 1) / kBlockSize + 1 } });
return Overlaps(rs);
diff --git a/roots.cpp b/roots.cpp
index c0348d715..3cc7b4188 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -321,7 +321,9 @@ int format_volume(const char* volume, const char* directory) {
}
// Has to be f2fs because we checked earlier.
- std::vector<std::string> f2fs_args = { "/sbin/mkfs.f2fs", "-t", "-d1", v->blk_device };
+ std::vector<std::string> f2fs_args = { "/sbin/mkfs.f2fs", "-d1", "-f",
+ "-O", "encrypt", "-O", "quota",
+ v->blk_device };
if (length >= 512) {
f2fs_args.push_back(std::to_string(length / 512));
}
diff --git a/tests/Android.mk b/tests/Android.mk
index 8ebb60308..d911c25e4 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -30,7 +30,8 @@ LOCAL_STATIC_LIBRARIES := \
libutils \
libz \
libselinux \
- libbase
+ libbase \
+ libBionicGtestMain
LOCAL_SRC_FILES := \
unit/asn1_decoder_test.cpp \
@@ -50,7 +51,8 @@ LOCAL_CFLAGS := -Wall -Werror
LOCAL_MODULE := recovery_manual_test
LOCAL_STATIC_LIBRARIES := \
libminui \
- libbase
+ libbase \
+ libBionicGtestMain
LOCAL_SRC_FILES := manual/recovery_test.cpp
LOCAL_SHARED_LIBRARIES := \
@@ -163,6 +165,7 @@ LOCAL_STATIC_LIBRARIES := \
libsquashfs_utils \
libcutils \
libbrotli \
+ libBionicGtestMain \
$(tune2fs_static_libraries)
testdata_files := $(call find-subdir-files, testdata/*)
@@ -212,7 +215,8 @@ LOCAL_STATIC_LIBRARIES := \
libbz \
libdivsufsort64 \
libdivsufsort \
- libz
+ libz \
+ libBionicGtestMain
LOCAL_SHARED_LIBRARIES := \
liblog
include $(BUILD_HOST_NATIVE_TEST)
diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp
index 15ec08fe7..21c9a52dc 100644
--- a/tests/component/applypatch_test.cpp
+++ b/tests/component/applypatch_test.cpp
@@ -53,8 +53,7 @@ static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize =
}
static void mangle_file(const std::string& fname) {
- std::string content;
- content.reserve(1024);
+ std::string content(1024, '\0');
for (size_t i = 0; i < 1024; i++) {
content[i] = rand() % 256;
}
@@ -63,16 +62,11 @@ static void mangle_file(const std::string& fname) {
class ApplyPatchTest : public ::testing::Test {
public:
- static void SetUpTestCase() {
+ virtual void SetUp() override {
// set up files
old_file = from_testdata_base("old.file");
new_file = from_testdata_base("new.file");
- patch_file = from_testdata_base("patch.bsdiff");
- rand_file = "/cache/applypatch_test_rand.file";
- cache_file = "/cache/saved.file";
-
- // write stuff to rand_file
- ASSERT_TRUE(android::base::WriteStringToFile("hello", rand_file));
+ nonexistent_file = from_testdata_base("nonexistent.file");
// set up SHA constants
sha1sum(old_file, &old_sha1, &old_size);
@@ -82,56 +76,35 @@ class ApplyPatchTest : public ::testing::Test {
bad_sha1_b = android::base::StringPrintf("%040x", rand());
}
- static std::string old_file;
- static std::string new_file;
- static std::string rand_file;
- static std::string cache_file;
- static std::string patch_file;
+ std::string old_file;
+ std::string new_file;
+ std::string nonexistent_file;
- static std::string old_sha1;
- static std::string new_sha1;
- static std::string bad_sha1_a;
- static std::string bad_sha1_b;
+ std::string old_sha1;
+ std::string new_sha1;
+ std::string bad_sha1_a;
+ std::string bad_sha1_b;
- static size_t old_size;
- static size_t new_size;
+ size_t old_size;
+ size_t new_size;
};
-static void cp(const std::string& src, const std::string& tgt) {
- std::string cmd = "cp " + src + " " + tgt;
- system(cmd.c_str());
-}
-
-static void backup_old() {
- cp(ApplyPatchTest::old_file, ApplyPatchTest::cache_file);
-}
-
-static void restore_old() {
- cp(ApplyPatchTest::cache_file, ApplyPatchTest::old_file);
-}
-
class ApplyPatchCacheTest : public ApplyPatchTest {
- public:
- virtual void SetUp() {
- backup_old();
+ protected:
+ void SetUp() override {
+ ApplyPatchTest::SetUp();
+ cache_temp_source = old_file;
}
+};
- virtual void TearDown() {
- restore_old();
+class ApplyPatchModesTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ cache_temp_source = cache_source.path;
}
-};
-std::string ApplyPatchTest::old_file;
-std::string ApplyPatchTest::new_file;
-std::string ApplyPatchTest::rand_file;
-std::string ApplyPatchTest::patch_file;
-std::string ApplyPatchTest::cache_file;
-std::string ApplyPatchTest::old_sha1;
-std::string ApplyPatchTest::new_sha1;
-std::string ApplyPatchTest::bad_sha1_a;
-std::string ApplyPatchTest::bad_sha1_b;
-size_t ApplyPatchTest::old_size;
-size_t ApplyPatchTest::new_size;
+ TemporaryFile cache_source;
+};
TEST_F(ApplyPatchTest, CheckModeSkip) {
std::vector<std::string> sha1s;
@@ -189,43 +162,31 @@ TEST_F(ApplyPatchTest, CheckModeEmmcTarget) {
ASSERT_EQ(0, applypatch_check(src_file.c_str(), sha1s));
}
-TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSingle) {
- mangle_file(old_file);
- std::vector<std::string> sha1s = { old_sha1 };
- ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
+TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSourceSingle) {
+ TemporaryFile temp_file;
+ mangle_file(temp_file.path);
+ std::vector<std::string> sha1s_single = { old_sha1 };
+ ASSERT_EQ(0, applypatch_check(temp_file.path, sha1s_single));
+ ASSERT_EQ(0, applypatch_check(nonexistent_file.c_str(), sha1s_single));
}
-TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedMultiple) {
- mangle_file(old_file);
- std::vector<std::string> sha1s = { bad_sha1_a, old_sha1, bad_sha1_b };
- ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
+TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSourceMultiple) {
+ TemporaryFile temp_file;
+ mangle_file(temp_file.path);
+ std::vector<std::string> sha1s_multiple = { bad_sha1_a, old_sha1, bad_sha1_b };
+ ASSERT_EQ(0, applypatch_check(temp_file.path, sha1s_multiple));
+ ASSERT_EQ(0, applypatch_check(nonexistent_file.c_str(), sha1s_multiple));
}
-TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedFailure) {
- mangle_file(old_file);
- std::vector<std::string> sha1s = { bad_sha1_a, bad_sha1_b };
- ASSERT_NE(0, applypatch_check(&old_file[0], sha1s));
-}
-
-TEST_F(ApplyPatchCacheTest, CheckCacheMissingSingle) {
- unlink(&old_file[0]);
- std::vector<std::string> sha1s = { old_sha1 };
- ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
-}
-
-TEST_F(ApplyPatchCacheTest, CheckCacheMissingMultiple) {
- unlink(&old_file[0]);
- std::vector<std::string> sha1s = { bad_sha1_a, old_sha1, bad_sha1_b };
- ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
-}
-
-TEST_F(ApplyPatchCacheTest, CheckCacheMissingFailure) {
- unlink(&old_file[0]);
- std::vector<std::string> sha1s = { bad_sha1_a, bad_sha1_b };
- ASSERT_NE(0, applypatch_check(&old_file[0], sha1s));
+TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSourceFailure) {
+ TemporaryFile temp_file;
+ mangle_file(temp_file.path);
+ std::vector<std::string> sha1s_failure = { bad_sha1_a, bad_sha1_b };
+ ASSERT_NE(0, applypatch_check(temp_file.path, sha1s_failure));
+ ASSERT_NE(0, applypatch_check(nonexistent_file.c_str(), sha1s_failure));
}
-TEST(ApplyPatchModesTest, InvalidArgs) {
+TEST_F(ApplyPatchModesTest, InvalidArgs) {
// At least two args (including the filename).
ASSERT_EQ(2, applypatch_modes(1, (const char* []){ "applypatch" }));
@@ -233,7 +194,7 @@ TEST(ApplyPatchModesTest, InvalidArgs) {
ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-x" }));
}
-TEST(ApplyPatchModesTest, PatchModeEmmcTarget) {
+TEST_F(ApplyPatchModesTest, PatchModeEmmcTarget) {
std::string boot_img = from_testdata_base("boot.img");
size_t boot_img_size;
std::string boot_img_sha1;
@@ -303,7 +264,7 @@ TEST(ApplyPatchModesTest, PatchModeEmmcTarget) {
ASSERT_EQ(0, applypatch_modes(args3.size(), args3.data()));
}
-TEST(ApplyPatchModesTest, PatchModeInvalidArgs) {
+TEST_F(ApplyPatchModesTest, PatchModeInvalidArgs) {
// Invalid bonus file.
ASSERT_NE(0, applypatch_modes(3, (const char* []){ "applypatch", "-b", "/doesntexist" }));
@@ -364,11 +325,11 @@ TEST(ApplyPatchModesTest, PatchModeInvalidArgs) {
ASSERT_NE(0, applypatch_modes(args6.size(), args6.data()));
}
-TEST(ApplyPatchModesTest, CheckModeInvalidArgs) {
+TEST_F(ApplyPatchModesTest, CheckModeInvalidArgs) {
// Insufficient args.
ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-c" }));
}
-TEST(ApplyPatchModesTest, ShowLicenses) {
+TEST_F(ApplyPatchModesTest, ShowLicenses) {
ASSERT_EQ(0, applypatch_modes(2, (const char* []){ "applypatch", "-l" }));
}
diff --git a/tests/component/bootloader_message_test.cpp b/tests/component/bootloader_message_test.cpp
index b38bc7134..6cc59a495 100644
--- a/tests/component/bootloader_message_test.cpp
+++ b/tests/component/bootloader_message_test.cpp
@@ -18,53 +18,12 @@
#include <vector>
#include <android-base/strings.h>
+#include <android-base/test_utils.h>
#include <bootloader_message/bootloader_message.h>
#include <gtest/gtest.h>
-class BootloaderMessageTest : public ::testing::Test {
- protected:
- BootloaderMessageTest() : has_misc(true) {}
-
- virtual void SetUp() override {
- std::string err;
- has_misc = !get_bootloader_message_blk_device(&err).empty();
- }
-
- virtual void TearDown() override {
- // Clear the BCB.
- if (has_misc) {
- std::string err;
- ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err;
- }
- }
-
- bool has_misc;
-};
-
-TEST_F(BootloaderMessageTest, clear_bootloader_message) {
- if (!has_misc) {
- GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
- return;
- }
-
- // Clear the BCB.
- std::string err;
- ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err;
-
- // Verify the content.
- bootloader_message boot;
- ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
-
- // All the bytes should be cleared.
- ASSERT_EQ(std::string(sizeof(boot), '\0'),
- std::string(reinterpret_cast<const char*>(&boot), sizeof(boot)));
-}
-
-TEST_F(BootloaderMessageTest, read_and_write_bootloader_message) {
- if (!has_misc) {
- GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
- return;
- }
+TEST(BootloaderMessageTest, read_and_write_bootloader_message) {
+ TemporaryFile temp_misc;
// Write the BCB.
bootloader_message boot = {};
@@ -73,90 +32,71 @@ TEST_F(BootloaderMessageTest, read_and_write_bootloader_message) {
strlcpy(boot.status, "status1", sizeof(boot.status));
std::string err;
- ASSERT_TRUE(write_bootloader_message(boot, &err)) << "Failed to write BCB: " << err;
+ ASSERT_TRUE(write_bootloader_message_to(boot, temp_misc.path, &err))
+ << "Failed to write BCB: " << err;
// Read and verify.
bootloader_message boot_verify;
- ASSERT_TRUE(read_bootloader_message(&boot_verify, &err)) << "Failed to read BCB: " << err;
+ ASSERT_TRUE(read_bootloader_message_from(&boot_verify, temp_misc.path, &err))
+ << "Failed to read BCB: " << err;
ASSERT_EQ(std::string(reinterpret_cast<const char*>(&boot), sizeof(boot)),
std::string(reinterpret_cast<const char*>(&boot_verify), sizeof(boot_verify)));
}
-TEST_F(BootloaderMessageTest, write_bootloader_message_options) {
- if (!has_misc) {
- GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
- return;
- }
-
+TEST(BootloaderMessageTest, update_bootloader_message_in_struct) {
// Write the options to BCB.
std::vector<std::string> options = { "option1", "option2" };
- std::string err;
- ASSERT_TRUE(write_bootloader_message(options, &err)) << "Failed to write BCB: " << err;
- // Inject some bytes into boot, which should be overwritten while reading.
- bootloader_message boot;
+ bootloader_message boot = {};
+ // Inject some bytes into boot.
strlcpy(boot.recovery, "random message", sizeof(boot.recovery));
+ strlcpy(boot.status, "status bytes", sizeof(boot.status));
+ strlcpy(boot.stage, "stage bytes", sizeof(boot.stage));
strlcpy(boot.reserved, "reserved bytes", sizeof(boot.reserved));
- ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
+ ASSERT_TRUE(update_bootloader_message_in_struct(&boot, options));
// Verify that command and recovery fields should be set.
ASSERT_EQ("boot-recovery", std::string(boot.command));
std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n";
ASSERT_EQ(expected, std::string(boot.recovery));
- // The rest should be cleared.
- ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status)));
- ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage)));
- ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'),
- std::string(boot.reserved, sizeof(boot.reserved)));
+ // The rest should be intact.
+ ASSERT_EQ("status bytes", std::string(boot.status));
+ ASSERT_EQ("stage bytes", std::string(boot.stage));
+ ASSERT_EQ("reserved bytes", std::string(boot.reserved));
}
-TEST_F(BootloaderMessageTest, write_bootloader_message_options_empty) {
- if (!has_misc) {
- GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
- return;
- }
-
+TEST(BootloaderMessageTest, update_bootloader_message_recovery_options_empty) {
// Write empty vector.
std::vector<std::string> options;
- std::string err;
- ASSERT_TRUE(write_bootloader_message(options, &err)) << "Failed to write BCB: " << err;
// Read and verify.
- bootloader_message boot;
- ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
+ bootloader_message boot = {};
+ ASSERT_TRUE(update_bootloader_message_in_struct(&boot, options));
// command and recovery fields should be set.
ASSERT_EQ("boot-recovery", std::string(boot.command));
ASSERT_EQ("recovery\n", std::string(boot.recovery));
- // The rest should be cleared.
+ // The rest should be empty.
ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status)));
ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage)));
ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'),
std::string(boot.reserved, sizeof(boot.reserved)));
}
-TEST_F(BootloaderMessageTest, write_bootloader_message_options_long) {
- if (!has_misc) {
- GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
- return;
- }
-
+TEST(BootloaderMessageTest, update_bootloader_message_recovery_options_long) {
// Write super long message.
std::vector<std::string> options;
for (int i = 0; i < 100; i++) {
options.push_back("option: " + std::to_string(i));
}
- std::string err;
- ASSERT_TRUE(write_bootloader_message(options, &err)) << "Failed to write BCB: " << err;
-
// Read and verify.
- bootloader_message boot;
- ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
+ bootloader_message boot = {};
+ ASSERT_TRUE(update_bootloader_message_in_struct(&boot, options));
// Make sure it's long enough.
std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n";
@@ -167,40 +107,10 @@ TEST_F(BootloaderMessageTest, write_bootloader_message_options_long) {
ASSERT_EQ(expected.substr(0, sizeof(boot.recovery) - 1), std::string(boot.recovery));
ASSERT_EQ('\0', boot.recovery[sizeof(boot.recovery) - 1]);
- // The rest should be cleared.
+ // The rest should be empty.
ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status)));
ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage)));
ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'),
std::string(boot.reserved, sizeof(boot.reserved)));
}
-TEST_F(BootloaderMessageTest, update_bootloader_message) {
- if (!has_misc) {
- GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
- return;
- }
-
- // Inject some bytes into boot, which should be not overwritten later.
- bootloader_message boot;
- strlcpy(boot.recovery, "random message", sizeof(boot.recovery));
- strlcpy(boot.reserved, "reserved bytes", sizeof(boot.reserved));
- std::string err;
- ASSERT_TRUE(write_bootloader_message(boot, &err)) << "Failed to write BCB: " << err;
-
- // Update the BCB message.
- std::vector<std::string> options = { "option1", "option2" };
- ASSERT_TRUE(update_bootloader_message(options, &err)) << "Failed to update BCB: " << err;
-
- bootloader_message boot_verify;
- ASSERT_TRUE(read_bootloader_message(&boot_verify, &err)) << "Failed to read BCB: " << err;
-
- // Verify that command and recovery fields should be set.
- ASSERT_EQ("boot-recovery", std::string(boot_verify.command));
- std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n";
- ASSERT_EQ(expected, std::string(boot_verify.recovery));
-
- // The rest should be intact.
- ASSERT_EQ(std::string(boot.status), std::string(boot_verify.status));
- ASSERT_EQ(std::string(boot.stage), std::string(boot_verify.stage));
- ASSERT_EQ(std::string(boot.reserved), std::string(boot_verify.reserved));
-}
diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp
index 6de804e06..728b6cc76 100644
--- a/tests/component/imgdiff_test.cpp
+++ b/tests/component/imgdiff_test.cpp
@@ -657,19 +657,23 @@ static void construct_deflate_entry(const std::vector<std::tuple<std::string, si
}
}
-// Look for the generated source and patch pieces in the debug_dir and generate the target on
-// each pair. Concatenate the split target and match against the orignal one.
+// Look for the source and patch pieces in debug_dir. Generate a target piece from each pair.
+// Concatenate all the target pieces and match against the orignal one. Used pieces in debug_dir
+// will be cleaned up.
static void GenerateAndCheckSplitTarget(const std::string& debug_dir, size_t count,
const std::string& tgt) {
std::string patched;
for (size_t i = 0; i < count; i++) {
std::string split_src_path = android::base::StringPrintf("%s/src-%zu", debug_dir.c_str(), i);
- std::string split_patch_path = android::base::StringPrintf("%s/patch-%zu", debug_dir.c_str(), i);
-
std::string split_src;
- std::string split_patch;
ASSERT_TRUE(android::base::ReadFileToString(split_src_path, &split_src));
+ ASSERT_EQ(0, unlink(split_src_path.c_str()));
+
+ std::string split_patch_path =
+ android::base::StringPrintf("%s/patch-%zu", debug_dir.c_str(), i);
+ std::string split_patch;
ASSERT_TRUE(android::base::ReadFileToString(split_patch_path, &split_patch));
+ ASSERT_EQ(0, unlink(split_patch_path.c_str()));
std::string split_tgt;
GenerateTarget(split_src, split_patch, &split_tgt);
diff --git a/tests/component/sideload_test.cpp b/tests/component/sideload_test.cpp
index 40cfc6975..b7109fcc2 100644
--- a/tests/component/sideload_test.cpp
+++ b/tests/component/sideload_test.cpp
@@ -16,6 +16,12 @@
#include <unistd.h>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include "fuse_sideload.h"
@@ -26,11 +32,67 @@ TEST(SideloadTest, fuse_device) {
TEST(SideloadTest, run_fuse_sideload_wrong_parameters) {
provider_vtab vtab;
- vtab.close = [](void*) {};
+ vtab.close = [](void) {};
- ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, 4096, 4095));
- ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, 4096, (1 << 22) + 1));
+ ASSERT_EQ(-1, run_fuse_sideload(vtab, 4096, 4095));
+ ASSERT_EQ(-1, run_fuse_sideload(vtab, 4096, (1 << 22) + 1));
// Too many blocks.
- ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, ((1 << 18) + 1) * 4096, 4096));
+ ASSERT_EQ(-1, run_fuse_sideload(vtab, ((1 << 18) + 1) * 4096, 4096));
+}
+
+TEST(SideloadTest, run_fuse_sideload) {
+ const std::vector<std::string> blocks = {
+ std::string(2048, 'a') + std::string(2048, 'b'),
+ std::string(2048, 'c') + std::string(2048, 'd'),
+ std::string(2048, 'e') + std::string(2048, 'f'),
+ std::string(2048, 'g') + std::string(2048, 'h'),
+ };
+ const std::string content = android::base::Join(blocks, "");
+ ASSERT_EQ(16384U, content.size());
+
+ provider_vtab vtab;
+ vtab.close = [](void) {};
+ vtab.read_block = [&blocks](uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
+ if (block >= 4) return -1;
+ blocks[block].copy(reinterpret_cast<char*>(buffer), fetch_size);
+ return 0;
+ };
+
+ TemporaryDir mount_point;
+ pid_t pid = fork();
+ if (pid == 0) {
+ ASSERT_EQ(0, run_fuse_sideload(vtab, 16384, 4096, mount_point.path));
+ _exit(EXIT_SUCCESS);
+ }
+
+ std::string package = std::string(mount_point.path) + "/" + FUSE_SIDELOAD_HOST_FILENAME;
+ int status;
+ static constexpr int kSideloadInstallTimeout = 10;
+ for (int i = 0; i < kSideloadInstallTimeout; ++i) {
+ ASSERT_NE(-1, waitpid(pid, &status, WNOHANG));
+
+ struct stat sb;
+ if (stat(package.c_str(), &sb) == 0) {
+ break;
+ }
+
+ if (errno == ENOENT && i < kSideloadInstallTimeout - 1) {
+ sleep(1);
+ continue;
+ }
+ FAIL() << "Timed out waiting for the fuse-provided package.";
+ }
+
+ std::string content_via_fuse;
+ ASSERT_TRUE(android::base::ReadFileToString(package, &content_via_fuse));
+ ASSERT_EQ(content, content_via_fuse);
+
+ std::string exit_flag = std::string(mount_point.path) + "/" + FUSE_SIDELOAD_HOST_EXIT_FLAG;
+ struct stat sb;
+ ASSERT_EQ(0, stat(exit_flag.c_str(), &sb));
+
+ waitpid(pid, &status, 0);
+ ASSERT_EQ(0, WEXITSTATUS(status));
+ ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
}
diff --git a/tests/component/uncrypt_test.cpp b/tests/component/uncrypt_test.cpp
index 3925236a5..55baca2e3 100644
--- a/tests/component/uncrypt_test.cpp
+++ b/tests/component/uncrypt_test.cpp
@@ -20,6 +20,7 @@
#include <sys/un.h>
#include <unistd.h>
+#include <algorithm>
#include <string>
#include <android-base/file.h>
@@ -38,43 +39,49 @@ static const std::string INIT_SVC_CLEAR_BCB = "init.svc.clear-bcb";
static const std::string INIT_SVC_UNCRYPT = "init.svc.uncrypt";
static constexpr int SOCKET_CONNECTION_MAX_RETRY = 30;
+static void StopService() {
+ ASSERT_TRUE(android::base::SetProperty("ctl.stop", "setup-bcb"));
+ ASSERT_TRUE(android::base::SetProperty("ctl.stop", "clear-bcb"));
+ ASSERT_TRUE(android::base::SetProperty("ctl.stop", "uncrypt"));
+
+ bool success = false;
+ for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
+ std::string setup_bcb = android::base::GetProperty(INIT_SVC_SETUP_BCB, "");
+ std::string clear_bcb = android::base::GetProperty(INIT_SVC_CLEAR_BCB, "");
+ std::string uncrypt = android::base::GetProperty(INIT_SVC_UNCRYPT, "");
+ GTEST_LOG_(INFO) << "setup-bcb: [" << setup_bcb << "] clear-bcb: [" << clear_bcb
+ << "] uncrypt: [" << uncrypt << "]";
+ if (setup_bcb != "running" && clear_bcb != "running" && uncrypt != "running") {
+ success = true;
+ break;
+ }
+ sleep(1);
+ }
+
+ ASSERT_TRUE(success) << "uncrypt service is not available.";
+}
+
class UncryptTest : public ::testing::Test {
protected:
UncryptTest() : has_misc(true) {}
- virtual void SetUp() override {
- ASSERT_TRUE(android::base::SetProperty("ctl.stop", "setup-bcb"));
- ASSERT_TRUE(android::base::SetProperty("ctl.stop", "clear-bcb"));
- ASSERT_TRUE(android::base::SetProperty("ctl.stop", "uncrypt"));
-
- bool success = false;
- for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
- std::string setup_bcb = android::base::GetProperty(INIT_SVC_SETUP_BCB, "");
- std::string clear_bcb = android::base::GetProperty(INIT_SVC_CLEAR_BCB, "");
- std::string uncrypt = android::base::GetProperty(INIT_SVC_UNCRYPT, "");
- LOG(INFO) << "setup-bcb: [" << setup_bcb << "] clear-bcb: [" << clear_bcb << "] uncrypt: ["
- << uncrypt << "]";
- if (setup_bcb != "running" && clear_bcb != "running" && uncrypt != "running") {
- success = true;
- break;
- }
- sleep(1);
- }
-
- ASSERT_TRUE(success) << "uncrypt service is not available.";
-
+ void SetUp() override {
std::string err;
has_misc = !get_bootloader_message_blk_device(&err).empty();
}
- void SetupOrClearBcb(bool isSetup, const std::string& message,
- const std::string& message_in_bcb) const {
- if (!has_misc) {
- GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
- return;
+ void TearDown() override {
+ // Clear the BCB.
+ if (has_misc) {
+ std::string err;
+ ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err;
}
+ }
- // Trigger the setup-bcb service.
+ void SetupOrClearBcb(bool isSetup, const std::string& message,
+ const std::string& message_in_bcb) const {
+ // Restart the setup-bcb service.
+ StopService();
ASSERT_TRUE(android::base::SetProperty("ctl.start", isSetup ? "setup-bcb" : "clear-bcb"));
// Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected").
@@ -144,27 +151,49 @@ class UncryptTest : public ::testing::Test {
}
}
+ void VerifyBootloaderMessage(const std::string& expected) {
+ std::string err;
+ bootloader_message boot;
+ ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
+
+ // Check that we have all the expected bytes.
+ ASSERT_EQ(expected, std::string(reinterpret_cast<const char*>(&boot), sizeof(boot)));
+ }
+
bool has_misc;
};
TEST_F(UncryptTest, setup_bcb) {
+ if (!has_misc) {
+ GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
+ return;
+ }
+
+ std::string random_data;
+ random_data.reserve(sizeof(bootloader_message));
+ generate_n(back_inserter(random_data), sizeof(bootloader_message), []() { return rand() % 128; });
+
+ bootloader_message boot;
+ memcpy(&boot, random_data.c_str(), random_data.size());
+
+ std::string err;
+ ASSERT_TRUE(write_bootloader_message(boot, &err)) << "Failed to write BCB: " << err;
+ VerifyBootloaderMessage(random_data);
+
+ ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err;
+ VerifyBootloaderMessage(std::string(sizeof(bootloader_message), '\0'));
+
std::string message = "--update_message=abc value";
std::string message_in_bcb = "recovery\n--update_message=abc value\n";
SetupOrClearBcb(true, message, message_in_bcb);
-}
-TEST_F(UncryptTest, clear_bcb) {
SetupOrClearBcb(false, "", "");
-}
-TEST_F(UncryptTest, setup_bcb_wipe_ab) {
TemporaryFile wipe_package;
ASSERT_TRUE(android::base::WriteStringToFile(std::string(345, 'a'), wipe_package.path));
// It's expected to store a wipe package in /misc, with the package size passed to recovery.
- std::string message =
- "--wipe_ab\n--wipe_package="s + wipe_package.path + "\n--reason=wipePackage"s;
- std::string message_in_bcb =
- "recovery\n--wipe_ab\n--wipe_package_size=345\n--reason=wipePackage\n";
+ message = "--wipe_ab\n--wipe_package="s + wipe_package.path + "\n--reason=wipePackage"s;
+ message_in_bcb = "recovery\n--wipe_ab\n--wipe_package_size=345\n--reason=wipePackage\n";
SetupOrClearBcb(true, message, message_in_bcb);
}
diff --git a/tests/unit/rangeset_test.cpp b/tests/unit/rangeset_test.cpp
index b3ed99215..7ae193e18 100644
--- a/tests/unit/rangeset_test.cpp
+++ b/tests/unit/rangeset_test.cpp
@@ -17,12 +17,24 @@
#include <signal.h>
#include <sys/types.h>
+#include <limits>
#include <vector>
#include <gtest/gtest.h>
#include "otautil/rangeset.h"
+TEST(RangeSetTest, ctor) {
+ RangeSet rs(std::vector<Range>{ Range{ 8, 10 }, Range{ 1, 5 } });
+ ASSERT_TRUE(rs);
+
+ RangeSet rs2(std::vector<Range>{});
+ ASSERT_FALSE(rs2);
+
+ RangeSet rs3(std::vector<Range>{ Range{ 8, 10 }, Range{ 5, 1 } });
+ ASSERT_FALSE(rs3);
+}
+
TEST(RangeSetTest, Parse_smoke) {
RangeSet rs = RangeSet::Parse("2,1,10");
ASSERT_EQ(static_cast<size_t>(1), rs.size());
@@ -37,27 +49,64 @@ TEST(RangeSetTest, Parse_smoke) {
// Leading zeros are fine. But android::base::ParseUint() doesn't like trailing zeros like "10 ".
ASSERT_EQ(rs, RangeSet::Parse(" 2, 1, 10"));
- ASSERT_EXIT(RangeSet::Parse("2,1,10 "), ::testing::KilledBySignal(SIGABRT), "");
+ ASSERT_FALSE(RangeSet::Parse("2,1,10 "));
}
TEST(RangeSetTest, Parse_InvalidCases) {
// Insufficient number of tokens.
- ASSERT_EXIT(RangeSet::Parse(""), ::testing::KilledBySignal(SIGABRT), "");
- ASSERT_EXIT(RangeSet::Parse("2,1"), ::testing::KilledBySignal(SIGABRT), "");
+ ASSERT_FALSE(RangeSet::Parse(""));
+ ASSERT_FALSE(RangeSet::Parse("2,1"));
// The first token (i.e. the number of following tokens) is invalid.
- ASSERT_EXIT(RangeSet::Parse("a,1,1"), ::testing::KilledBySignal(SIGABRT), "");
- ASSERT_EXIT(RangeSet::Parse("3,1,1"), ::testing::KilledBySignal(SIGABRT), "");
- ASSERT_EXIT(RangeSet::Parse("-3,1,1"), ::testing::KilledBySignal(SIGABRT), "");
- ASSERT_EXIT(RangeSet::Parse("2,1,2,3"), ::testing::KilledBySignal(SIGABRT), "");
+ ASSERT_FALSE(RangeSet::Parse("a,1,1"));
+ ASSERT_FALSE(RangeSet::Parse("3,1,1"));
+ ASSERT_FALSE(RangeSet::Parse("-3,1,1"));
+ ASSERT_FALSE(RangeSet::Parse("2,1,2,3"));
// Invalid tokens.
- ASSERT_EXIT(RangeSet::Parse("2,1,10a"), ::testing::KilledBySignal(SIGABRT), "");
- ASSERT_EXIT(RangeSet::Parse("2,,10"), ::testing::KilledBySignal(SIGABRT), "");
+ ASSERT_FALSE(RangeSet::Parse("2,1,10a"));
+ ASSERT_FALSE(RangeSet::Parse("2,,10"));
// Empty or negative range.
- ASSERT_EXIT(RangeSet::Parse("2,2,2"), ::testing::KilledBySignal(SIGABRT), "");
- ASSERT_EXIT(RangeSet::Parse("2,2,1"), ::testing::KilledBySignal(SIGABRT), "");
+ ASSERT_FALSE(RangeSet::Parse("2,2,2"));
+ ASSERT_FALSE(RangeSet::Parse("2,2,1"));
+}
+
+TEST(RangeSetTest, Clear) {
+ RangeSet rs = RangeSet::Parse("2,1,6");
+ ASSERT_TRUE(rs);
+ rs.Clear();
+ ASSERT_FALSE(rs);
+
+ // No-op to clear an empty RangeSet.
+ rs.Clear();
+ ASSERT_FALSE(rs);
+}
+
+TEST(RangeSetTest, PushBack) {
+ RangeSet rs;
+ ASSERT_FALSE(rs);
+
+ ASSERT_TRUE(rs.PushBack({ 3, 5 }));
+ ASSERT_EQ(RangeSet::Parse("2,3,5"), rs);
+
+ ASSERT_TRUE(rs.PushBack({ 5, 15 }));
+ ASSERT_EQ(RangeSet::Parse("4,3,5,5,15"), rs);
+ ASSERT_EQ(static_cast<size_t>(2), rs.size());
+ ASSERT_EQ(static_cast<size_t>(12), rs.blocks());
+}
+
+TEST(RangeSetTest, PushBack_InvalidInput) {
+ RangeSet rs;
+ ASSERT_FALSE(rs);
+ ASSERT_FALSE(rs.PushBack({ 5, 3 }));
+ ASSERT_FALSE(rs);
+ ASSERT_FALSE(rs.PushBack({ 15, 15 }));
+ ASSERT_FALSE(rs);
+
+ ASSERT_TRUE(rs.PushBack({ 5, 15 }));
+ ASSERT_FALSE(rs.PushBack({ 5, std::numeric_limits<size_t>::max() - 2 }));
+ ASSERT_EQ(RangeSet::Parse("2,5,15"), rs);
}
TEST(RangeSetTest, Overlaps) {
@@ -74,6 +123,86 @@ TEST(RangeSetTest, Overlaps) {
ASSERT_FALSE(RangeSet::Parse("2,5,7").Overlaps(RangeSet::Parse("2,3,5")));
}
+TEST(RangeSetTest, Split) {
+ RangeSet rs1 = RangeSet::Parse("2,1,2");
+ ASSERT_TRUE(rs1);
+ ASSERT_EQ((std::vector<RangeSet>{ RangeSet::Parse("2,1,2") }), rs1.Split(1));
+
+ RangeSet rs2 = RangeSet::Parse("2,5,10");
+ ASSERT_TRUE(rs2);
+ ASSERT_EQ((std::vector<RangeSet>{ RangeSet::Parse("2,5,8"), RangeSet::Parse("2,8,10") }),
+ rs2.Split(2));
+
+ RangeSet rs3 = RangeSet::Parse("4,0,1,5,10");
+ ASSERT_TRUE(rs3);
+ ASSERT_EQ((std::vector<RangeSet>{ RangeSet::Parse("4,0,1,5,7"), RangeSet::Parse("2,7,10") }),
+ rs3.Split(2));
+
+ RangeSet rs4 = RangeSet::Parse("6,1,3,3,4,4,5");
+ ASSERT_TRUE(rs4);
+ ASSERT_EQ((std::vector<RangeSet>{ RangeSet::Parse("2,1,3"), RangeSet::Parse("2,3,4"),
+ RangeSet::Parse("2,4,5") }),
+ rs4.Split(3));
+
+ RangeSet rs5 = RangeSet::Parse("2,0,10");
+ ASSERT_TRUE(rs5);
+ ASSERT_EQ((std::vector<RangeSet>{ RangeSet::Parse("2,0,3"), RangeSet::Parse("2,3,6"),
+ RangeSet::Parse("2,6,8"), RangeSet::Parse("2,8,10") }),
+ rs5.Split(4));
+
+ RangeSet rs6 = RangeSet::Parse(
+ "20,0,268,269,271,286,447,8350,32770,33022,98306,98558,163842,164094,196609,204800,229378,"
+ "229630,294914,295166,457564");
+ ASSERT_TRUE(rs6);
+ size_t rs6_blocks = rs6.blocks();
+ auto splits = rs6.Split(4);
+ ASSERT_EQ(
+ (std::vector<RangeSet>{
+ RangeSet::Parse("12,0,268,269,271,286,447,8350,32770,33022,98306,98558,118472"),
+ RangeSet::Parse("8,118472,163842,164094,196609,204800,229378,229630,237216"),
+ RangeSet::Parse("4,237216,294914,295166,347516"), RangeSet::Parse("2,347516,457564") }),
+ splits);
+ size_t sum = 0;
+ for (const auto& element : splits) {
+ sum += element.blocks();
+ }
+ ASSERT_EQ(rs6_blocks, sum);
+}
+
+TEST(RangeSetTest, Split_EdgeCases) {
+ // Empty RangeSet.
+ RangeSet rs1;
+ ASSERT_FALSE(rs1);
+ ASSERT_EQ((std::vector<RangeSet>{}), rs1.Split(2));
+ ASSERT_FALSE(rs1);
+
+ // Zero group.
+ RangeSet rs2 = RangeSet::Parse("2,1,5");
+ ASSERT_TRUE(rs2);
+ ASSERT_EQ((std::vector<RangeSet>{}), rs2.Split(0));
+
+ // The number of blocks equals to the number of groups.
+ RangeSet rs3 = RangeSet::Parse("2,1,5");
+ ASSERT_TRUE(rs3);
+ ASSERT_EQ((std::vector<RangeSet>{ RangeSet::Parse("2,1,2"), RangeSet::Parse("2,2,3"),
+ RangeSet::Parse("2,3,4"), RangeSet::Parse("2,4,5") }),
+ rs3.Split(4));
+
+ // Less blocks than the number of groups.
+ RangeSet rs4 = RangeSet::Parse("2,1,5");
+ ASSERT_TRUE(rs4);
+ ASSERT_EQ((std::vector<RangeSet>{ RangeSet::Parse("2,1,2"), RangeSet::Parse("2,2,3"),
+ RangeSet::Parse("2,3,4"), RangeSet::Parse("2,4,5") }),
+ rs4.Split(8));
+
+ // Less blocks than the number of groups.
+ RangeSet rs5 = RangeSet::Parse("2,0,3");
+ ASSERT_TRUE(rs5);
+ ASSERT_EQ((std::vector<RangeSet>{ RangeSet::Parse("2,0,1"), RangeSet::Parse("2,1,2"),
+ RangeSet::Parse("2,2,3") }),
+ rs5.Split(4));
+}
+
TEST(RangeSetTest, GetBlockNumber) {
RangeSet rs = RangeSet::Parse("2,1,10");
ASSERT_EQ(static_cast<size_t>(1), rs.GetBlockNumber(0));
@@ -90,7 +219,7 @@ TEST(RangeSetTest, equality) {
ASSERT_NE(RangeSet::Parse("2,1,6"), RangeSet::Parse("2,1,7"));
ASSERT_NE(RangeSet::Parse("2,1,6"), RangeSet::Parse("2,2,7"));
- // The orders of Range's matter. "4,1,5,8,10" != "4,8,10,1,5".
+ // The orders of Range's matter, e.g. "4,1,5,8,10" != "4,8,10,1,5".
ASSERT_NE(RangeSet::Parse("4,1,5,8,10"), RangeSet::Parse("4,8,10,1,5"));
}
@@ -111,13 +240,14 @@ TEST(RangeSetTest, iterators) {
ASSERT_EQ((std::vector<Range>{ Range{ 8, 10 }, Range{ 1, 5 } }), ranges);
}
-TEST(RangeSetTest, tostring) {
+TEST(RangeSetTest, ToString) {
+ ASSERT_EQ("", RangeSet::Parse("").ToString());
ASSERT_EQ("2,1,6", RangeSet::Parse("2,1,6").ToString());
ASSERT_EQ("4,1,5,8,10", RangeSet::Parse("4,1,5,8,10").ToString());
ASSERT_EQ("6,1,3,4,6,15,22", RangeSet::Parse("6,1,3,4,6,15,22").ToString());
}
-TEST(SortedRangeSetTest, insertion) {
+TEST(SortedRangeSetTest, Insert) {
SortedRangeSet rs({ { 2, 3 }, { 4, 6 }, { 8, 14 } });
rs.Insert({ 1, 2 });
ASSERT_EQ(SortedRangeSet({ { 1, 3 }, { 4, 6 }, { 8, 14 } }), rs);
diff --git a/uncrypt/Android.bp b/uncrypt/Android.bp
new file mode 100644
index 000000000..aa56d2f74
--- /dev/null
+++ b/uncrypt/Android.bp
@@ -0,0 +1,39 @@
+// Copyright (C) 2017 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.
+
+cc_binary {
+ name: "uncrypt",
+
+ srcs: [
+ "uncrypt.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ static_libs: [
+ "libbootloader_message",
+ "libotautil",
+ "libfs_mgr",
+ "libbase",
+ "libcutils",
+ "liblog",
+ ],
+
+ init_rc: [
+ "uncrypt.rc",
+ ],
+}
diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk
deleted file mode 100644
index 601f9276e..000000000
--- a/uncrypt/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2014 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := uncrypt.cpp
-LOCAL_MODULE := uncrypt
-LOCAL_STATIC_LIBRARIES := \
- libbootloader_message \
- libotautil \
- libbase \
- liblog \
- libfs_mgr \
- libcutils
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_INIT_RC := uncrypt.rc
-
-include $(BUILD_EXECUTABLE)
diff --git a/update_verifier/Android.mk b/update_verifier/Android.mk
index 33c5fe9e7..0ff88546f 100644
--- a/update_verifier/Android.mk
+++ b/update_verifier/Android.mk
@@ -22,6 +22,10 @@ LOCAL_SRC_FILES := \
update_verifier.cpp
LOCAL_MODULE := libupdate_verifier
+
+LOCAL_STATIC_LIBRARIES := \
+ libotautil
+
LOCAL_SHARED_LIBRARIES := \
libbase \
libcutils \
@@ -54,7 +58,9 @@ LOCAL_SRC_FILES := \
LOCAL_MODULE := update_verifier
LOCAL_STATIC_LIBRARIES := \
- libupdate_verifier
+ libupdate_verifier \
+ libotautil
+
LOCAL_SHARED_LIBRARIES := \
libbase \
libcutils \
diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp
index ba7b7aec4..c5e154f03 100644
--- a/update_verifier/update_verifier.cpp
+++ b/update_verifier/update_verifier.cpp
@@ -58,6 +58,8 @@
#include <android/hardware/boot/1.0/IBootControl.h>
#include <cutils/android_reboot.h>
+#include "otautil/rangeset.h"
+
using android::sp;
using android::hardware::boot::V1_0::IBootControl;
using android::hardware::boot::V1_0::BoolResult;
@@ -129,42 +131,33 @@ static bool read_blocks(const std::string& partition, const std::string& range_s
// followed by 'count' number comma separated integers. Every two integers reprensent a
// block range with the first number included in range but second number not included.
// For example '4,64536,65343,74149,74150' represents: [64536,65343) and [74149,74150).
- std::vector<std::string> ranges = android::base::Split(range_str, ",");
- size_t range_count;
- bool status = android::base::ParseUint(ranges[0], &range_count);
- if (!status || (range_count == 0) || (range_count % 2 != 0) ||
- (range_count != ranges.size() - 1)) {
- LOG(ERROR) << "Error in parsing range string.";
+ RangeSet ranges = RangeSet::Parse(range_str);
+ if (!ranges) {
+ LOG(ERROR) << "Error parsing RangeSet string " << range_str;
return false;
}
- range_count /= 2;
- std::vector<std::future<bool>> threads;
+ // RangeSet::Split() splits the ranges into multiple groups with same number of blocks (except for
+ // the last group).
size_t thread_num = std::thread::hardware_concurrency() ?: 4;
- thread_num = std::min(thread_num, range_count);
- size_t group_range_count = (range_count + thread_num - 1) / thread_num;
+ std::vector<RangeSet> groups = ranges.Split(thread_num);
- for (size_t t = 0; t < thread_num; t++) {
- auto thread_func = [t, group_range_count, &dm_block_device, &ranges, &partition]() {
- size_t blk_count = 0;
- static constexpr size_t kBlockSize = 4096;
- std::vector<uint8_t> buf(1024 * kBlockSize);
+ std::vector<std::future<bool>> threads;
+ for (const auto& group : groups) {
+ auto thread_func = [&group, &dm_block_device, &partition]() {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
if (fd.get() == -1) {
PLOG(ERROR) << "Error reading " << dm_block_device << " for partition " << partition;
return false;
}
- for (size_t i = group_range_count * 2 * t + 1;
- i < std::min(group_range_count * 2 * (t + 1) + 1, ranges.size()); i += 2) {
- unsigned int range_start, range_end;
- bool parse_status = android::base::ParseUint(ranges[i], &range_start);
- parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end);
- if (!parse_status || range_start >= range_end) {
- LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i + 1];
- return false;
- }
+ static constexpr size_t kBlockSize = 4096;
+ std::vector<uint8_t> buf(1024 * kBlockSize);
+ size_t block_count = 0;
+ for (const auto& range : group) {
+ size_t range_start = range.first;
+ size_t range_end = range.second;
if (lseek64(fd.get(), static_cast<off64_t>(range_start) * kBlockSize, SEEK_SET) == -1) {
PLOG(ERROR) << "lseek to " << range_start << " failed";
return false;
@@ -179,9 +172,9 @@ static bool read_blocks(const std::string& partition, const std::string& range_s
}
remain -= to_read;
}
- blk_count += (range_end - range_start);
+ block_count += (range_end - range_start);
}
- LOG(INFO) << "Finished reading " << blk_count << " blocks on " << dm_block_device;
+ LOG(INFO) << "Finished reading " << block_count << " blocks on " << dm_block_device;
return true;
};
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index 6c7b3efcf..08f9930ea 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -492,6 +492,10 @@ static void PrintHashForCorruptedSourceBlocks(const CommandParameters& params,
}
RangeSet src = RangeSet::Parse(params.tokens[pos++]);
+ if (!src) {
+ LOG(ERROR) << "Failed to parse range in " << params.cmdline;
+ return;
+ }
RangeSet locs;
// If there's no stashed blocks, content in the buffer is consecutive and has the same
@@ -936,6 +940,7 @@ static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size
params.cpos++;
} else {
RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]);
+ CHECK(static_cast<bool>(src));
*overlap = src.Overlaps(tgt);
if (ReadBlocks(src, params.buffer, params.fd) == -1) {
@@ -948,6 +953,7 @@ static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size
}
RangeSet locs = RangeSet::Parse(params.tokens[params.cpos++]);
+ CHECK(static_cast<bool>(locs));
MoveRange(params.buffer, locs, params.buffer);
}
@@ -970,6 +976,7 @@ static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size
}
RangeSet locs = RangeSet::Parse(tokens[1]);
+ CHECK(static_cast<bool>(locs));
MoveRange(params.buffer, locs, stash);
}
@@ -1034,6 +1041,7 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t*
// <tgt_range>
tgt = RangeSet::Parse(params.tokens[params.cpos++]);
+ CHECK(static_cast<bool>(tgt));
std::vector<uint8_t> tgtbuffer(tgt.blocks() * BLOCKSIZE);
if (ReadBlocks(tgt, tgtbuffer, params.fd) == -1) {
@@ -1146,6 +1154,7 @@ static int PerformCommandStash(CommandParameters& params) {
}
RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]);
+ CHECK(static_cast<bool>(src));
allocate(src.blocks() * BLOCKSIZE, params.buffer);
if (ReadBlocks(src, params.buffer, params.fd) == -1) {
@@ -1196,6 +1205,7 @@ static int PerformCommandZero(CommandParameters& params) {
}
RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
+ CHECK(static_cast<bool>(tgt));
LOG(INFO) << " zeroing " << tgt.blocks() << " blocks";
@@ -1238,6 +1248,7 @@ static int PerformCommandNew(CommandParameters& params) {
}
RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
+ CHECK(static_cast<bool>(tgt));
if (params.canwrite) {
LOG(INFO) << " writing " << tgt.blocks() << " blocks of new data";
@@ -1307,7 +1318,7 @@ static int PerformCommandDiff(CommandParameters& params) {
RangeSinkWriter writer(params.fd, tgt);
if (params.cmdname[0] == 'i') { // imgdiff
- if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value,
+ if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, patch_value,
std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1,
std::placeholders::_2),
nullptr, nullptr) != 0) {
@@ -1316,7 +1327,7 @@ static int PerformCommandDiff(CommandParameters& params) {
return -1;
}
} else {
- if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, 0,
+ if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, patch_value, 0,
std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1,
std::placeholders::_2),
nullptr) != 0) {
@@ -1368,6 +1379,7 @@ static int PerformCommandErase(CommandParameters& params) {
}
RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
+ CHECK(static_cast<bool>(tgt));
if (params.canwrite) {
LOG(INFO) << " erasing " << tgt.blocks() << " blocks";
@@ -1773,6 +1785,7 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique
}
RangeSet rs = RangeSet::Parse(ranges->data);
+ CHECK(static_cast<bool>(rs));
SHA_CTX ctx;
SHA1_Init(&ctx);
@@ -1884,6 +1897,11 @@ Value* BlockImageRecoverFn(const char* name, State* state,
ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name);
return StringValue("");
}
+ RangeSet rs = RangeSet::Parse(ranges->data);
+ if (!rs) {
+ ErrorAbort(state, kArgsParsingFailure, "failed to parse ranges: %s", ranges->data.c_str());
+ return StringValue("");
+ }
// Output notice to log when recover is attempted
LOG(INFO) << filename->data << " image corrupted, attempting to recover...";
@@ -1909,7 +1927,7 @@ Value* BlockImageRecoverFn(const char* name, State* state,
}
uint8_t buffer[BLOCKSIZE];
- for (const auto& range : RangeSet::Parse(ranges->data)) {
+ for (const auto& range : rs) {
for (size_t j = range.first; j < range.second; ++j) {
// Stay within the data area, libfec validates and corrects metadata
if (status.data_size <= static_cast<uint64_t>(j) * BLOCKSIZE) {
diff --git a/updater/install.cpp b/updater/install.cpp
index 9425d1872..a111f4b79 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -49,7 +49,6 @@
#include <applypatch/applypatch.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
-#include <ext4_utils/make_ext4fs.h>
#include <ext4_utils/wipe.h>
#include <openssl/sha.h>
#include <selinux/label.h>
@@ -284,14 +283,8 @@ Value* FormatFn(const char* name, State* state, const std::vector<std::unique_pt
int status = exec_cmd(mke2fs_argv[0], const_cast<char**>(mke2fs_argv));
if (status != 0) {
- LOG(WARNING) << name << ": mke2fs failed (" << status << ") on " << location
- << ", falling back to make_ext4fs";
- status = make_ext4fs(location.c_str(), size, mount_point.c_str(), sehandle);
- if (status != 0) {
- LOG(ERROR) << name << ": make_ext4fs failed (" << status << ") on " << location;
- return StringValue("");
- }
- return StringValue(location);
+ LOG(ERROR) << name << ": mke2fs failed (" << status << ") on " << location;
+ return StringValue("");
}
const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", "-e", "-a", mount_point.c_str(),
@@ -310,10 +303,16 @@ Value* FormatFn(const char* name, State* state, const std::vector<std::unique_pt
std::string num_sectors = std::to_string(size / 512);
const char* f2fs_path = "/sbin/mkfs.f2fs";
- const char* f2fs_argv[] = {
- "mkfs.f2fs", "-t", "-d1", location.c_str(), (size < 512) ? nullptr : num_sectors.c_str(),
- nullptr
- };
+ const char* f2fs_argv[] = { "mkfs.f2fs",
+ "-d1",
+ "-f",
+ "-O",
+ "encrypt",
+ "-O",
+ "quota",
+ location.c_str(),
+ (size < 512) ? nullptr : num_sectors.c_str(),
+ nullptr };
int status = exec_cmd(f2fs_path, const_cast<char**>(f2fs_argv));
if (status != 0) {
LOG(ERROR) << name << ": mkfs.f2fs failed (" << status << ") on " << location;