diff options
Diffstat (limited to '')
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, ©_file) < 0) { + if (LoadFileContents(cache_temp_source.c_str(), ©_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); @@ -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; |