rm -rf non-AB code
Bug: 324360816
Test: th
Change-Id: I3d82d9031446be355d8a1d077ab83283c7cc769c
diff --git a/Android.bp b/Android.bp
index 9b2f80f..67ece85 100644
--- a/Android.bp
+++ b/Android.bp
@@ -35,7 +35,10 @@
// See: http://go/android-license-faq
license {
name: "bootable_recovery_license",
- visibility: [":__subpackages__"],
+ visibility: [
+ ":__subpackages__",
+ "//bootable/deprecated-ota:__subpackages__",
+ ],
license_kinds: [
"SPDX-license-identifier-Apache-2.0",
"SPDX-license-identifier-MIT",
diff --git a/Android.mk b/Android.mk
index 8506040..3d1ee39 100644
--- a/Android.mk
+++ b/Android.mk
@@ -84,5 +84,3 @@
include $(BUILD_PHONY_PACKAGE)
-include \
- $(LOCAL_PATH)/updater/Android.mk \
diff --git a/applypatch/Android.bp b/applypatch/Android.bp
deleted file mode 100644
index 0d6d23b..0000000
--- a/applypatch/Android.bp
+++ /dev/null
@@ -1,201 +0,0 @@
-// 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.
-
-package {
- default_applicable_licenses: ["bootable_recovery_applypatch_license"],
-}
-
-// Added automatically by a large-scale-change
-// See: http://go/android-license-faq
-license {
- name: "bootable_recovery_applypatch_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- ],
- license_text: [
- "NOTICE",
- ],
-}
-
-cc_defaults {
- name: "applypatch_defaults",
-
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- "-DZLIB_CONST",
- "-Wall",
- "-Werror",
- ],
-
- local_include_dirs: [
- "include",
- ],
-}
-
-cc_library_static {
- name: "libapplypatch",
-
- host_supported: true,
- vendor_available: true,
-
- defaults: [
- "applypatch_defaults",
- ],
-
- srcs: [
- "applypatch.cpp",
- "bspatch.cpp",
- "freecache.cpp",
- "imgpatch.cpp",
- ],
-
- export_include_dirs: [
- "include",
- ],
-
- static_libs: [
- "libbase",
- "libbspatch",
- "libbz",
- "libedify",
- "libotautil",
- "libz_stable",
- ],
-
- shared_libs: [
- "libcrypto",
- ],
-
- target: {
- darwin: {
- enabled: false,
- },
- },
-}
-
-cc_library_static {
- name: "libapplypatch_modes",
- vendor_available: true,
-
- defaults: [
- "applypatch_defaults",
- ],
-
- srcs: [
- "applypatch_modes.cpp",
- ],
-
- static_libs: [
- "libapplypatch",
- "libbase",
- "libedify",
- "libotautil",
- ],
-
- shared_libs: [
- "libcrypto",
- ],
-}
-
-cc_binary {
- name: "applypatch",
- vendor: true,
-
- defaults: [
- "applypatch_defaults",
- ],
-
- srcs: [
- "applypatch_main.cpp",
- ],
-
- static_libs: [
- "libapplypatch_modes",
- "libapplypatch",
- "libedify",
- "libotautil",
-
- // External dependencies.
- "libbspatch",
- "libbrotli",
- "libbz",
- ],
-
- shared_libs: [
- "libbase",
- "libcrypto",
- "liblog",
- "libz_stable",
- "libziparchive",
- ],
-
- init_rc: [
- "vendor_flash_recovery.rc",
- ],
-}
-
-cc_library_static {
- name: "libimgdiff",
- host_supported: true,
- defaults: [
- "applypatch_defaults",
- ],
-
- srcs: [
- "imgdiff.cpp",
- ],
-
- export_include_dirs: [
- "include",
- ],
-
- static_libs: [
- "libbase",
- "libbsdiff",
- "libdivsufsort",
- "libdivsufsort64",
- "liblog",
- "libotautil",
- "libutils",
- "libz_stable",
- "libziparchive",
- ],
-}
-
-cc_binary_host {
- name: "imgdiff",
- srcs: [
- "imgdiff_main.cpp",
- ],
-
- defaults: [
- "applypatch_defaults",
- ],
-
- static_libs: [
- "libimgdiff",
- "libotautil",
- "libbsdiff",
- "libdivsufsort",
- "libdivsufsort64",
- "libziparchive",
- "libbase",
- "libutils",
- "liblog",
- "libbrotli",
- "libbz",
- "libz_stable",
- ],
-}
diff --git a/applypatch/NOTICE b/applypatch/NOTICE
deleted file mode 100644
index 6156a0c..0000000
--- a/applypatch/NOTICE
+++ /dev/null
@@ -1,41 +0,0 @@
-Copyright (C) 2009 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-
-bsdiff.c
-bspatch.c
-
-Copyright 2003-2005 Colin Percival
-All rights reserved
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted providing that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
-IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
deleted file mode 100644
index adda697..0000000
--- a/applypatch/applypatch.cpp
+++ /dev/null
@@ -1,457 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "applypatch/applypatch.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <libgen.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <functional>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <openssl/sha.h>
-
-#include "edify/expr.h"
-#include "otautil/paths.h"
-#include "otautil/print_sha1.h"
-
-using namespace std::string_literals;
-
-static bool GenerateTarget(const Partition& target, const FileContents& source_file,
- const Value& patch, const Value* bonus_data, bool backup_source);
-
-bool LoadFileContents(const std::string& filename, FileContents* file) {
- // No longer allow loading contents from eMMC partitions.
- if (android::base::StartsWith(filename, "EMMC:")) {
- return false;
- }
-
- std::string data;
- if (!android::base::ReadFileToString(filename, &data)) {
- PLOG(ERROR) << "Failed to read \"" << filename << "\"";
- return false;
- }
-
- file->data = std::vector<unsigned char>(data.begin(), data.end());
- SHA1(file->data.data(), file->data.size(), file->sha1);
- return true;
-}
-
-// Reads the contents of a Partition to the given FileContents buffer.
-static bool ReadPartitionToBuffer(const Partition& partition, FileContents* out,
- bool check_backup) {
- uint8_t expected_sha1[SHA_DIGEST_LENGTH];
- if (ParseSha1(partition.hash, expected_sha1) != 0) {
- LOG(ERROR) << "Failed to parse target hash \"" << partition.hash << "\"";
- return false;
- }
-
- android::base::unique_fd dev(open(partition.name.c_str(), O_RDONLY));
- if (dev == -1) {
- PLOG(ERROR) << "Failed to open eMMC partition \"" << partition << "\"";
- } else {
- std::vector<unsigned char> buffer(partition.size);
- if (!android::base::ReadFully(dev, buffer.data(), buffer.size())) {
- PLOG(ERROR) << "Failed to read " << buffer.size() << " bytes of data for partition "
- << partition;
- } else {
- SHA1(buffer.data(), buffer.size(), out->sha1);
- if (memcmp(out->sha1, expected_sha1, SHA_DIGEST_LENGTH) == 0) {
- out->data = std::move(buffer);
- return true;
- }
- }
- }
-
- if (!check_backup) {
- LOG(ERROR) << "Partition contents don't have the expected checksum";
- return false;
- }
-
- if (LoadFileContents(Paths::Get().cache_temp_source(), out) &&
- memcmp(out->sha1, expected_sha1, SHA_DIGEST_LENGTH) == 0) {
- return true;
- }
-
- LOG(ERROR) << "Both of partition contents and backup don't have the expected checksum";
- return false;
-}
-
-bool SaveFileContents(const std::string& filename, const FileContents* file) {
- android::base::unique_fd fd(
- open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR));
- if (fd == -1) {
- PLOG(ERROR) << "Failed to open \"" << filename << "\" for write";
- return false;
- }
-
- if (!android::base::WriteFully(fd, file->data.data(), file->data.size())) {
- PLOG(ERROR) << "Failed to write " << file->data.size() << " bytes of data to " << filename;
- return false;
- }
-
- if (fsync(fd) != 0) {
- PLOG(ERROR) << "Failed to fsync \"" << filename << "\"";
- return false;
- }
-
- if (close(fd.release()) != 0) {
- PLOG(ERROR) << "Failed to close \"" << filename << "\"";
- return false;
- }
-
- return true;
-}
-
-// Writes a memory buffer to 'target' Partition.
-static bool WriteBufferToPartition(const FileContents& file_contents, const Partition& partition) {
- const unsigned char* data = file_contents.data.data();
- size_t len = file_contents.data.size();
- size_t start = 0;
- bool success = false;
- for (size_t attempt = 0; attempt < 2; ++attempt) {
- android::base::unique_fd fd(open(partition.name.c_str(), O_RDWR));
- if (fd == -1) {
- PLOG(ERROR) << "Failed to open \"" << partition << "\"";
- return false;
- }
-
- if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) {
- PLOG(ERROR) << "Failed to seek to " << start << " on \"" << partition << "\"";
- return false;
- }
-
- if (!android::base::WriteFully(fd, data + start, len - start)) {
- PLOG(ERROR) << "Failed to write " << len - start << " bytes to \"" << partition << "\"";
- return false;
- }
-
- if (fsync(fd) != 0) {
- PLOG(ERROR) << "Failed to sync \"" << partition << "\"";
- return false;
- }
- if (close(fd.release()) != 0) {
- PLOG(ERROR) << "Failed to close \"" << partition << "\"";
- return false;
- }
-
- fd.reset(open(partition.name.c_str(), O_RDONLY));
- if (fd == -1) {
- PLOG(ERROR) << "Failed to reopen \"" << partition << "\" for verification";
- return false;
- }
-
- // Drop caches so our subsequent verification read won't just be reading the cache.
- sync();
- std::string drop_cache = "/proc/sys/vm/drop_caches";
- if (!android::base::WriteStringToFile("3\n", drop_cache)) {
- PLOG(ERROR) << "Failed to write to " << drop_cache;
- } else {
- LOG(INFO) << " caches dropped";
- }
- sleep(1);
-
- // Verify.
- if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) {
- PLOG(ERROR) << "Failed to seek to 0 on " << partition;
- return false;
- }
-
- unsigned char buffer[4096];
- start = len;
- for (size_t p = 0; p < len; p += sizeof(buffer)) {
- size_t to_read = len - p;
- if (to_read > sizeof(buffer)) {
- to_read = sizeof(buffer);
- }
-
- if (!android::base::ReadFully(fd, buffer, to_read)) {
- PLOG(ERROR) << "Failed to verify-read " << partition << " at " << p;
- return false;
- }
-
- if (memcmp(buffer, data + p, to_read) != 0) {
- LOG(ERROR) << "Verification failed starting at " << p;
- start = p;
- break;
- }
- }
-
- if (start == len) {
- LOG(INFO) << "Verification read succeeded (attempt " << attempt + 1 << ")";
- success = true;
- break;
- }
-
- if (close(fd.release()) != 0) {
- PLOG(ERROR) << "Failed to close " << partition;
- return false;
- }
- }
-
- if (!success) {
- LOG(ERROR) << "Failed to verify after all attempts";
- return false;
- }
-
- sync();
-
- return true;
-}
-
-int ParseSha1(const std::string& str, uint8_t* digest) {
- const char* ps = str.c_str();
- uint8_t* pd = digest;
- for (int i = 0; i < SHA_DIGEST_LENGTH * 2; ++i, ++ps) {
- int digit;
- if (*ps >= '0' && *ps <= '9') {
- digit = *ps - '0';
- } else if (*ps >= 'a' && *ps <= 'f') {
- digit = *ps - 'a' + 10;
- } else if (*ps >= 'A' && *ps <= 'F') {
- digit = *ps - 'A' + 10;
- } else {
- return -1;
- }
- if (i % 2 == 0) {
- *pd = digit << 4;
- } else {
- *pd |= digit;
- ++pd;
- }
- }
- if (*ps != '\0') return -1;
- return 0;
-}
-
-bool PatchPartitionCheck(const Partition& target, const Partition& source) {
- FileContents target_file;
- FileContents source_file;
- return (ReadPartitionToBuffer(target, &target_file, false) ||
- ReadPartitionToBuffer(source, &source_file, true));
-}
-
-int ShowLicenses() {
- ShowBSDiffLicense();
- return 0;
-}
-
-bool PatchPartition(const Partition& target, const Partition& source, const Value& patch,
- const Value* bonus, bool backup_source) {
- LOG(INFO) << "Patching " << target.name;
-
- // We try to load and check against the target hash first.
- FileContents target_file;
- if (ReadPartitionToBuffer(target, &target_file, false)) {
- // The early-exit case: the patch was already applied, this file has the desired hash, nothing
- // for us to do.
- LOG(INFO) << " already " << target.hash.substr(0, 8);
- return true;
- }
-
- FileContents source_file;
- if (ReadPartitionToBuffer(source, &source_file, backup_source)) {
- return GenerateTarget(target, source_file, patch, bonus, backup_source);
- }
-
- LOG(ERROR) << "Failed to find any match";
- return false;
-}
-
-bool FlashPartition(const Partition& partition, const std::string& source_filename) {
- LOG(INFO) << "Flashing " << partition;
-
- // We try to load and check against the target hash first.
- FileContents target_file;
- if (ReadPartitionToBuffer(partition, &target_file, false)) {
- // The early-exit case: the patch was already applied, this file has the desired hash, nothing
- // for us to do.
- LOG(INFO) << " already " << partition.hash.substr(0, 8);
- return true;
- }
-
- FileContents source_file;
- if (!LoadFileContents(source_filename, &source_file)) {
- LOG(ERROR) << "Failed to load source file";
- return false;
- }
-
- uint8_t expected_sha1[SHA_DIGEST_LENGTH];
- if (ParseSha1(partition.hash, expected_sha1) != 0) {
- LOG(ERROR) << "Failed to parse source hash \"" << partition.hash << "\"";
- return false;
- }
-
- if (memcmp(source_file.sha1, expected_sha1, SHA_DIGEST_LENGTH) != 0) {
- // The source doesn't have desired checksum.
- LOG(ERROR) << "source \"" << source_filename << "\" doesn't have expected SHA-1 sum";
- LOG(ERROR) << "expected: " << partition.hash.substr(0, 8)
- << ", found: " << short_sha1(source_file.sha1);
- return false;
- }
- if (!WriteBufferToPartition(source_file, partition)) {
- LOG(ERROR) << "Failed to write to " << partition;
- return false;
- }
- return true;
-}
-
-static bool GenerateTarget(const Partition& target, const FileContents& source_file,
- const Value& patch, const Value* bonus_data, bool backup_source) {
- uint8_t expected_sha1[SHA_DIGEST_LENGTH];
- if (ParseSha1(target.hash, expected_sha1) != 0) {
- LOG(ERROR) << "Failed to parse target hash \"" << target.hash << "\"";
- return false;
- }
-
- if (patch.type != Value::Type::BLOB) {
- LOG(ERROR) << "patch is not a blob";
- return false;
- }
-
- const char* header = patch.data.data();
- size_t header_bytes_read = patch.data.size();
- bool use_bsdiff = false;
- if (header_bytes_read >= 8 && memcmp(header, "BSDIFF40", 8) == 0) {
- use_bsdiff = true;
- } else if (header_bytes_read >= 8 && memcmp(header, "IMGDIFF2", 8) == 0) {
- use_bsdiff = false;
- } else {
- LOG(ERROR) << "Unknown patch file format";
- return false;
- }
-
- // We write the original source to cache, in case the partition write is interrupted.
- if (backup_source && !CheckAndFreeSpaceOnCache(source_file.data.size())) {
- LOG(ERROR) << "Not enough free space on /cache";
- return false;
- }
- if (backup_source && !SaveFileContents(Paths::Get().cache_temp_source(), &source_file)) {
- LOG(ERROR) << "Failed to back up source file";
- return false;
- }
-
- // We store the decoded output in memory.
- FileContents patched;
- SHA_CTX ctx;
- SHA1_Init(&ctx);
- SinkFn sink = [&patched, &ctx](const unsigned char* data, size_t len) {
- SHA1_Update(&ctx, data, len);
- patched.data.insert(patched.data.end(), data, data + len);
- return len;
- };
-
- int result;
- if (use_bsdiff) {
- result = ApplyBSDiffPatch(source_file.data.data(), source_file.data.size(), patch, 0, sink);
- } else {
- result =
- ApplyImagePatch(source_file.data.data(), source_file.data.size(), patch, sink, bonus_data);
- }
-
- if (result != 0) {
- LOG(ERROR) << "Failed to apply the patch: " << result;
- return false;
- }
-
- SHA1_Final(patched.sha1, &ctx);
- if (memcmp(patched.sha1, expected_sha1, SHA_DIGEST_LENGTH) != 0) {
- LOG(ERROR) << "Patching did not produce the expected SHA-1 of " << short_sha1(expected_sha1);
-
- LOG(ERROR) << "target size " << patched.data.size() << " SHA-1 " << short_sha1(patched.sha1);
- LOG(ERROR) << "source size " << source_file.data.size() << " SHA-1 "
- << short_sha1(source_file.sha1);
-
- uint8_t patch_digest[SHA_DIGEST_LENGTH];
- SHA1(reinterpret_cast<const uint8_t*>(patch.data.data()), patch.data.size(), patch_digest);
- LOG(ERROR) << "patch size " << patch.data.size() << " SHA-1 " << short_sha1(patch_digest);
-
- if (bonus_data != nullptr) {
- uint8_t bonus_digest[SHA_DIGEST_LENGTH];
- SHA1(reinterpret_cast<const uint8_t*>(bonus_data->data.data()), bonus_data->data.size(),
- bonus_digest);
- LOG(ERROR) << "bonus size " << bonus_data->data.size() << " SHA-1 "
- << short_sha1(bonus_digest);
- }
-
- return false;
- }
-
- LOG(INFO) << " now " << short_sha1(expected_sha1);
-
- // Write back the temp file to the partition.
- if (!WriteBufferToPartition(patched, target)) {
- LOG(ERROR) << "Failed to write patched data to " << target.name;
- return false;
- }
-
- // Delete the backup copy of the source.
- if (backup_source) {
- unlink(Paths::Get().cache_temp_source().c_str());
- }
-
- // Success!
- return true;
-}
-
-bool CheckPartition(const Partition& partition) {
- FileContents target_file;
- return ReadPartitionToBuffer(partition, &target_file, false);
-}
-
-Partition Partition::Parse(const std::string& input_str, std::string* err) {
- std::vector<std::string> pieces = android::base::Split(input_str, ":");
- if (pieces.size() != 4 || pieces[0] != "EMMC") {
- *err = "Invalid number of tokens or non-eMMC target";
- return {};
- }
-
- size_t size;
- if (!android::base::ParseUint(pieces[2], &size) || size == 0) {
- *err = "Failed to parse \"" + pieces[2] + "\" as byte count";
- return {};
- }
-
- return Partition(pieces[1], size, pieces[3]);
-}
-
-std::string Partition::ToString() const {
- if (*this) {
- return "EMMC:"s + name + ":" + std::to_string(size) + ":" + hash;
- }
- return "<invalid-partition>";
-}
-
-std::ostream& operator<<(std::ostream& os, const Partition& partition) {
- os << partition.ToString();
- return os;
-}
diff --git a/applypatch/applypatch_main.cpp b/applypatch/applypatch_main.cpp
deleted file mode 100644
index 92d2b3f..0000000
--- a/applypatch/applypatch_main.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "applypatch_modes.h"
-
-#include <android-base/logging.h>
-
-// See the comments for applypatch() function.
-int main(int argc, char** argv) {
- android::base::InitLogging(argv);
- return applypatch_modes(argc, argv);
-}
diff --git a/applypatch/applypatch_modes.cpp b/applypatch/applypatch_modes.cpp
deleted file mode 100644
index bb5eeae..0000000
--- a/applypatch/applypatch_modes.cpp
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "applypatch_modes.h"
-
-#include <getopt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-#include <android-base/strings.h>
-#include <openssl/sha.h>
-
-#include "applypatch/applypatch.h"
-#include "edify/expr.h"
-
-static int CheckMode(const std::string& target_emmc) {
- std::string err;
- auto target = Partition::Parse(target_emmc, &err);
- if (!target) {
- LOG(ERROR) << "Failed to parse target \"" << target_emmc << "\": " << err;
- return 2;
- }
- return CheckPartition(target) ? 0 : 1;
-}
-
-static int FlashMode(const std::string& target_emmc, const std::string& source_file) {
- std::string err;
- auto target = Partition::Parse(target_emmc, &err);
- if (!target) {
- LOG(ERROR) << "Failed to parse target \"" << target_emmc << "\": " << err;
- return 2;
- }
- return FlashPartition(target, source_file) ? 0 : 1;
-}
-
-static int PatchMode(const std::string& target_emmc, const std::string& source_emmc,
- const std::string& patch_file, const std::string& bonus_file) {
- std::string err;
- auto target = Partition::Parse(target_emmc, &err);
- if (!target) {
- LOG(ERROR) << "Failed to parse target \"" << target_emmc << "\": " << err;
- return 2;
- }
-
- auto source = Partition::Parse(source_emmc, &err);
- if (!source) {
- LOG(ERROR) << "Failed to parse source \"" << source_emmc << "\": " << err;
- return 2;
- }
-
- std::string patch_contents;
- if (!android::base::ReadFileToString(patch_file, &patch_contents)) {
- PLOG(ERROR) << "Failed to read patch file \"" << patch_file << "\"";
- return 1;
- }
-
- Value patch(Value::Type::BLOB, std::move(patch_contents));
- std::unique_ptr<Value> bonus;
- if (!bonus_file.empty()) {
- std::string bonus_contents;
- if (!android::base::ReadFileToString(bonus_file, &bonus_contents)) {
- PLOG(ERROR) << "Failed to read bonus file \"" << bonus_file << "\"";
- return 1;
- }
- bonus = std::make_unique<Value>(Value::Type::BLOB, std::move(bonus_contents));
- }
-
- return PatchPartition(target, source, patch, bonus.get(), false) ? 0 : 1;
-}
-
-static void Usage() {
- printf(
- "Usage: \n"
- "check mode\n"
- " applypatch --check EMMC:<target-file>:<target-size>:<target-sha1>\n\n"
- "flash mode\n"
- " applypatch --flash <source-file>\n"
- " --target EMMC:<target-file>:<target-size>:<target-sha1>\n\n"
- "patch mode\n"
- " applypatch [--bonus <bonus-file>]\n"
- " --patch <patch-file>\n"
- " --target EMMC:<target-file>:<target-size>:<target-sha1>\n"
- " --source EMMC:<source-file>:<source-size>:<source-sha1>\n\n"
- "show license\n"
- " applypatch --license\n"
- "\n\n");
-}
-
-int applypatch_modes(int argc, char* argv[]) {
- static constexpr struct option OPTIONS[]{
- // clang-format off
- { "bonus", required_argument, nullptr, 0 },
- { "check", required_argument, nullptr, 0 },
- { "flash", required_argument, nullptr, 0 },
- { "license", no_argument, nullptr, 0 },
- { "patch", required_argument, nullptr, 0 },
- { "source", required_argument, nullptr, 0 },
- { "target", required_argument, nullptr, 0 },
- { nullptr, 0, nullptr, 0 },
- // clang-format on
- };
-
- std::string check_target;
- std::string source;
- std::string target;
- std::string patch;
- std::string bonus;
-
- bool check_mode = false;
- bool flash_mode = false;
- bool patch_mode = false;
-
- optind = 1;
-
- int arg;
- int option_index;
- while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) {
- switch (arg) {
- case 0: {
- std::string option = OPTIONS[option_index].name;
- if (option == "bonus") {
- bonus = optarg;
- } else if (option == "check") {
- check_target = optarg;
- check_mode = true;
- } else if (option == "flash") {
- source = optarg;
- flash_mode = true;
- } else if (option == "license") {
- return ShowLicenses();
- } else if (option == "patch") {
- patch = optarg;
- patch_mode = true;
- } else if (option == "source") {
- source = optarg;
- } else if (option == "target") {
- target = optarg;
- }
- break;
- }
- case '?':
- default:
- LOG(ERROR) << "Invalid argument";
- Usage();
- return 2;
- }
- }
-
- if (check_mode) {
- return CheckMode(check_target);
- }
- if (flash_mode) {
- if (!bonus.empty()) {
- LOG(ERROR) << "bonus file not supported in flash mode";
- return 1;
- }
- return FlashMode(target, source);
- }
- if (patch_mode) {
- return PatchMode(target, source, patch, bonus);
- }
-
- Usage();
- return 2;
-}
diff --git a/applypatch/applypatch_modes.h b/applypatch/applypatch_modes.h
deleted file mode 100644
index aa60a43..0000000
--- a/applypatch/applypatch_modes.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _APPLYPATCH_MODES_H
-#define _APPLYPATCH_MODES_H
-
-int applypatch_modes(int argc, char* argv[]);
-
-#endif // _APPLYPATCH_MODES_H
diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp
deleted file mode 100644
index ba33c3a..0000000
--- a/applypatch/bspatch.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-// This file is a nearly line-for-line copy of bspatch.c from the
-// bsdiff-4.3 distribution; the primary differences being how the
-// input and output data are read and the error handling. Running
-// applypatch with the -l option will display the bsdiff license
-// notice.
-
-#include <stdio.h>
-#include <sys/types.h>
-
-#include <string>
-
-#include <android-base/logging.h>
-#include <bsdiff/bspatch.h>
-#include <openssl/sha.h>
-
-#include "applypatch/applypatch.h"
-#include "edify/expr.h"
-#include "otautil/print_sha1.h"
-
-void ShowBSDiffLicense() {
- puts("The bsdiff library used herein is:\n"
- "\n"
- "Copyright 2003-2005 Colin Percival\n"
- "All rights reserved\n"
- "\n"
- "Redistribution and use in source and binary forms, with or without\n"
- "modification, are permitted providing that the following conditions\n"
- "are met:\n"
- "1. Redistributions of source code must retain the above copyright\n"
- " notice, this list of conditions and the following disclaimer.\n"
- "2. Redistributions in binary form must reproduce the above copyright\n"
- " notice, this list of conditions and the following disclaimer in the\n"
- " documentation and/or other materials provided with the distribution.\n"
- "\n"
- "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
- "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
- "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
- "ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n"
- "DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n"
- "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n"
- "OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n"
- "HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n"
- "STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n"
- "IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
- "POSSIBILITY OF SUCH DAMAGE.\n"
- "\n------------------\n\n"
- "This program uses Julian R Seward's \"libbzip2\" library, available\n"
- "from http://www.bzip.org/.\n"
- );
-}
-
-int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value& patch,
- size_t patch_offset, SinkFn sink) {
- 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, 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);
- std::string patch_sha1 = print_sha1(digest);
- LOG(ERROR) << "Patch may be corrupted, offset: " << patch_offset << ", SHA1: " << patch_sha1;
- }
- }
- return result;
-}
diff --git a/applypatch/freecache.cpp b/applypatch/freecache.cpp
deleted file mode 100644
index 3868ef2..0000000
--- a/applypatch/freecache.cpp
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <dirent.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/statfs.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <limits>
-#include <memory>
-#include <set>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-
-#include "applypatch/applypatch.h"
-#include "otautil/paths.h"
-
-static int EliminateOpenFiles(const std::string& dirname, std::set<std::string>* files) {
- std::unique_ptr<DIR, decltype(&closedir)> d(opendir("/proc"), closedir);
- if (!d) {
- PLOG(ERROR) << "Failed to open /proc";
- return -1;
- }
- struct dirent* de;
- while ((de = readdir(d.get())) != 0) {
- unsigned int pid;
- if (!android::base::ParseUint(de->d_name, &pid)) {
- continue;
- }
- std::string path = android::base::StringPrintf("/proc/%s/fd/", de->d_name);
-
- struct dirent* fdde;
- std::unique_ptr<DIR, decltype(&closedir)> fdd(opendir(path.c_str()), closedir);
- if (!fdd) {
- PLOG(ERROR) << "Failed to open " << path;
- continue;
- }
- while ((fdde = readdir(fdd.get())) != 0) {
- std::string fd_path = path + fdde->d_name;
- char link[FILENAME_MAX];
-
- int count = readlink(fd_path.c_str(), link, sizeof(link)-1);
- if (count >= 0) {
- link[count] = '\0';
- if (android::base::StartsWith(link, dirname)) {
- if (files->erase(link) > 0) {
- LOG(INFO) << link << " is open by " << de->d_name;
- }
- }
- }
- }
- }
- return 0;
-}
-
-static std::vector<std::string> FindExpendableFiles(
- const std::string& dirname, const std::function<bool(const std::string&)>& name_filter) {
- std::unique_ptr<DIR, decltype(&closedir)> d(opendir(dirname.c_str()), closedir);
- if (!d) {
- PLOG(ERROR) << "Failed to open " << dirname;
- return {};
- }
-
- // Look for regular files in the directory (not in any subdirectories).
- std::set<std::string> files;
- struct dirent* de;
- while ((de = readdir(d.get())) != 0) {
- std::string path = dirname + "/" + 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 == Paths::Get().cache_temp_source()) {
- continue;
- }
-
- // Do not delete the file if it doesn't have the expected format.
- if (name_filter != nullptr && !name_filter(de->d_name)) {
- continue;
- }
-
- struct stat st;
- if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
- files.insert(path);
- }
- }
-
- LOG(INFO) << files.size() << " regular files in deletable directory";
- if (EliminateOpenFiles(dirname, &files) < 0) {
- return {};
- }
-
- return std::vector<std::string>(files.begin(), files.end());
-}
-
-// Parses the index of given log file, e.g. 3 for last_log.3; returns max number if the log name
-// doesn't have the expected format so that we'll delete these ones first.
-static unsigned int GetLogIndex(const std::string& log_name) {
- if (log_name == "last_log" || log_name == "last_kmsg") {
- return 0;
- }
-
- unsigned int index;
- if (sscanf(log_name.c_str(), "last_log.%u", &index) == 1 ||
- sscanf(log_name.c_str(), "last_kmsg.%u", &index) == 1) {
- return index;
- }
-
- return std::numeric_limits<unsigned int>::max();
-}
-
-// Returns the amount of free space (in bytes) on the filesystem containing filename, or -1 on
-// error.
-static int64_t FreeSpaceForFile(const std::string& filename) {
- struct statfs sf;
- if (statfs(filename.c_str(), &sf) == -1) {
- PLOG(ERROR) << "Failed to statfs " << filename;
- return -1;
- }
-
- auto f_bsize = static_cast<int64_t>(sf.f_bsize);
- auto free_space = sf.f_bsize * sf.f_bavail;
- if (f_bsize == 0 || free_space / f_bsize != static_cast<int64_t>(sf.f_bavail)) {
- LOG(ERROR) << "Invalid block size or overflow (sf.f_bsize " << sf.f_bsize << ", sf.f_bavail "
- << sf.f_bavail << ")";
- return -1;
- }
- return free_space;
-}
-
-bool CheckAndFreeSpaceOnCache(size_t bytes) {
-#ifndef __ANDROID__
- // TODO(xunchang): Implement a heuristic cache size check during host simulation.
- LOG(WARNING) << "Skipped making (" << bytes
- << ") bytes free space on /cache; program is running on host";
- return true;
-#endif
-
- std::vector<std::string> dirs{ "/cache", Paths::Get().cache_log_directory() };
- for (const auto& dirname : dirs) {
- if (RemoveFilesInDirectory(bytes, dirname, FreeSpaceForFile)) {
- return true;
- }
- }
-
- return false;
-}
-
-bool RemoveFilesInDirectory(size_t bytes_needed, const std::string& dirname,
- const std::function<int64_t(const std::string&)>& space_checker) {
- // The requested size cannot exceed max int64_t.
- if (static_cast<uint64_t>(bytes_needed) >
- static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
- LOG(ERROR) << "Invalid arg of bytes_needed: " << bytes_needed;
- return false;
- }
-
- struct stat st;
- if (stat(dirname.c_str(), &st) == -1) {
- PLOG(ERROR) << "Failed to stat " << dirname;
- return false;
- }
- if (!S_ISDIR(st.st_mode)) {
- LOG(ERROR) << dirname << " is not a directory";
- return false;
- }
-
- int64_t free_now = space_checker(dirname);
- if (free_now == -1) {
- LOG(ERROR) << "Failed to check free space for " << dirname;
- return false;
- }
- LOG(INFO) << free_now << " bytes free on " << dirname << " (" << bytes_needed << " needed)";
-
- if (free_now >= static_cast<int64_t>(bytes_needed)) {
- return true;
- }
-
- std::vector<std::string> files;
- if (dirname == Paths::Get().cache_log_directory()) {
- // Deletes the log files only.
- auto log_filter = [](const std::string& file_name) {
- return android::base::StartsWith(file_name, "last_log") ||
- android::base::StartsWith(file_name, "last_kmsg");
- };
-
- files = FindExpendableFiles(dirname, log_filter);
-
- // Older logs will come to the top of the queue.
- auto comparator = [](const std::string& name1, const std::string& name2) -> bool {
- unsigned int index1 = GetLogIndex(android::base::Basename(name1));
- unsigned int index2 = GetLogIndex(android::base::Basename(name2));
- if (index1 == index2) {
- return name1 < name2;
- }
-
- return index1 > index2;
- };
-
- std::sort(files.begin(), files.end(), comparator);
- } else {
- // We're allowed to delete unopened regular files in the directory.
- files = FindExpendableFiles(dirname, nullptr);
- }
-
- for (const auto& file : files) {
- if (unlink(file.c_str()) == -1) {
- PLOG(ERROR) << "Failed to delete " << file;
- continue;
- }
-
- free_now = space_checker(dirname);
- if (free_now == -1) {
- LOG(ERROR) << "Failed to check free space for " << dirname;
- return false;
- }
- LOG(INFO) << "Deleted " << file << "; now " << free_now << " bytes free";
- if (free_now >= static_cast<int64_t>(bytes_needed)) {
- return true;
- }
- }
-
- return false;
-}
diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp
deleted file mode 100644
index 33ed330..0000000
--- a/applypatch/imgdiff.cpp
+++ /dev/null
@@ -1,1641 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * This program constructs binary patches for images -- such as boot.img and recovery.img -- that
- * consist primarily of large chunks of gzipped data interspersed with uncompressed data. Doing a
- * naive bsdiff of these files is not useful because small changes in the data lead to large
- * changes in the compressed bitstream; bsdiff patches of gzipped data are typically as large as
- * the data itself.
- *
- * To patch these usefully, we break the source and target images up into chunks of two types:
- * "normal" and "gzip". Normal chunks are simply patched using a plain bsdiff. Gzip chunks are
- * first expanded, then a bsdiff is applied to the uncompressed data, then the patched data is
- * gzipped using the same encoder parameters. Patched chunks are concatenated together to create
- * the output file; the output image should be *exactly* the same series of bytes as the target
- * image used originally to generate the patch.
- *
- * To work well with this tool, the gzipped sections of the target image must have been generated
- * using the same deflate encoder that is available in applypatch, namely, the one in the zlib
- * library. In practice this means that images should be compressed using the toybox "gzip" toy,
- * not the GNU gzip program.
- *
- * An "imgdiff" patch consists of a header describing the chunk structure of the file and any
- * encoding parameters needed for the gzipped chunks, followed by N bsdiff patches, one per chunk.
- *
- * For a diff to be generated, the source and target must be in well-formed zip archive format;
- * or they are image files with the same "chunk" structure: that is, the same number of gzipped and
- * normal chunks in the same order. Android boot and recovery images currently consist of five
- * chunks: a small normal header, a gzipped kernel, a small normal section, a gzipped ramdisk, and
- * finally a small normal footer.
- *
- * Caveats: we locate gzipped sections within the source and target images by searching for the
- * byte sequence 1f8b0800: 1f8b is the gzip magic number; 08 specifies the "deflate" encoding
- * [the only encoding supported by the gzip standard]; and 00 is the flags byte. We do not
- * currently support any extra header fields (which would be indicated by a nonzero flags byte).
- * We also don't handle the case when that byte sequence appears spuriously in the file. (Note
- * that it would have to occur spuriously within a normal chunk to be a problem.)
- *
- *
- * The imgdiff patch header looks like this:
- *
- * "IMGDIFF2" (8) [magic number and version]
- * chunk count (4)
- * for each chunk:
- * chunk type (4) [CHUNK_{NORMAL, GZIP, DEFLATE, RAW}]
- * if chunk type == CHUNK_NORMAL:
- * source start (8)
- * source len (8)
- * bsdiff patch offset (8) [from start of patch file]
- * if chunk type == CHUNK_GZIP: (version 1 only)
- * source start (8)
- * source len (8)
- * bsdiff patch offset (8) [from start of patch file]
- * source expanded len (8) [size of uncompressed source]
- * target expected len (8) [size of uncompressed target]
- * gzip level (4)
- * method (4)
- * windowBits (4)
- * memLevel (4)
- * strategy (4)
- * gzip header len (4)
- * gzip header (gzip header len)
- * gzip footer (8)
- * if chunk type == CHUNK_DEFLATE: (version 2 only)
- * source start (8)
- * source len (8)
- * bsdiff patch offset (8) [from start of patch file]
- * source expanded len (8) [size of uncompressed source]
- * target expected len (8) [size of uncompressed target]
- * gzip level (4)
- * method (4)
- * windowBits (4)
- * memLevel (4)
- * strategy (4)
- * if chunk type == RAW: (version 2 only)
- * target len (4)
- * data (target len)
- *
- * All integers are little-endian. "source start" and "source len" specify the section of the
- * input image that comprises this chunk, including the gzip header and footer for gzip chunks.
- * "source expanded len" is the size of the uncompressed source data. "target expected len" is the
- * size of the uncompressed data after applying the bsdiff patch. The next five parameters
- * specify the zlib parameters to be used when compressing the patched data, and the next three
- * specify the header and footer to be wrapped around the compressed data to create the output
- * chunk (so that header contents like the timestamp are recreated exactly).
- *
- * After the header there are 'chunk count' bsdiff patches; the offset of each from the beginning
- * of the file is specified in the header.
- *
- * This tool can take an optional file of "bonus data". This is an extra file of data that is
- * appended to chunk #1 after it is compressed (it must be a CHUNK_DEFLATE chunk). The same file
- * must be available (and passed to applypatch with -b) when applying the patch. This is used to
- * reduce the size of recovery-from-boot patches by combining the boot image with recovery ramdisk
- * information that is stored on the system partition.
- *
- * When generating the patch between two zip files, this tool has an option "--block-limit" to
- * split the large source/target files into several pair of pieces, with each piece has at most
- * *limit* blocks. When this option is used, we also need to output the split info into the file
- * path specified by "--split-info".
- *
- * Format of split info file:
- * 2 [version of imgdiff]
- * n [count of split pieces]
- * <patch_size>, <tgt_size>, <src_range> [size and ranges for split piece#1]
- * ...
- * <patch_size>, <tgt_size>, <src_range> [size and ranges for split piece#n]
- *
- * To split a pair of large zip files, we walk through the chunks in target zip and search by its
- * entry_name in the source zip. If the entry_name is non-empty and a matching entry in source
- * is found, we'll add the source entry to the current split source image; otherwise we'll skip
- * this chunk and later do bsdiff between all the skipped trunks and the whole split source image.
- * We move on to the next pair of pieces if the size of the split source image reaches the block
- * limit.
- *
- * After the split, the target pieces are continuous and block aligned, while the source pieces
- * are mutually exclusive. Some of the source blocks may not be used if there's no matching
- * entry_name in the target; as a result, they won't be included in any of these split source
- * images. Then we will generate patches accordingly between each split image pairs; in particular,
- * the unmatched trunks in the split target will diff against the entire split source image.
- *
- * For example:
- * Input: [src_image, tgt_image]
- * Split: [src-0, tgt-0; src-1, tgt-1, src-2, tgt-2]
- * Diff: [ patch-0; patch-1; patch-2]
- *
- * Patch: [(src-0, patch-0) = tgt-0; (src-1, patch-1) = tgt-1; (src-2, patch-2) = tgt-2]
- * Concatenate: [tgt-0 + tgt-1 + tgt-2 = tgt_image]
- */
-
-#include "applypatch/imgdiff.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/memory.h>
-#include <android-base/parseint.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <bsdiff/bsdiff.h>
-#include <ziparchive/zip_archive.h>
-#include <zlib.h>
-
-#include "applypatch/imgdiff_image.h"
-#include "otautil/rangeset.h"
-
-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 constexpr size_t BLOCK_SIZE = 4096;
-static constexpr size_t BUFFER_SIZE = 0x8000;
-
-// If we use this function to write the offset and length (type size_t), their values should not
-// exceed 2^63; because the signed bit will be casted away.
-static inline bool Write8(int fd, int64_t value) {
- return android::base::WriteFully(fd, &value, sizeof(int64_t));
-}
-
-// Similarly, the value should not exceed 2^31 if we are casting from size_t (e.g. target chunk
-// size).
-static inline bool Write4(int fd, int32_t value) {
- return android::base::WriteFully(fd, &value, sizeof(int32_t));
-}
-
-// Trim the head or tail to align with the block size. Return false if the chunk has nothing left
-// after alignment.
-static bool AlignHead(size_t* start, size_t* length) {
- size_t residual = (*start % BLOCK_SIZE == 0) ? 0 : BLOCK_SIZE - *start % BLOCK_SIZE;
-
- if (*length <= residual) {
- *length = 0;
- return false;
- }
-
- // Trim the data in the beginning.
- *start += residual;
- *length -= residual;
- return true;
-}
-
-static bool AlignTail(size_t* start, size_t* length) {
- size_t residual = (*start + *length) % BLOCK_SIZE;
- if (*length <= residual) {
- *length = 0;
- return false;
- }
-
- // Trim the data in the end.
- *length -= residual;
- return true;
-}
-
-// Remove the used blocks from the source chunk to make sure the source ranges are mutually
-// exclusive after split. Return false if we fail to get the non-overlapped ranges. In such
-// a case, we'll skip the entire source chunk.
-static bool RemoveUsedBlocks(size_t* start, size_t* length, const SortedRangeSet& used_ranges) {
- if (!used_ranges.Overlaps(*start, *length)) {
- return true;
- }
-
- // TODO find the largest non-overlap chunk.
- 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.
- if (AlignHead(start, length) && !used_ranges.Overlaps(*start, *length)) {
- return true;
- }
- if (AlignTail(start, length) && !used_ranges.Overlaps(*start, *length)) {
- return true;
- }
-
- LOG(WARNING) << "Failed to remove the overlapped block ranges; skip the source";
- return false;
-}
-
-static const struct option OPTIONS[] = {
- { "zip-mode", no_argument, nullptr, 'z' },
- { "bonus-file", required_argument, nullptr, 'b' },
- { "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 },
-};
-
-ImageChunk::ImageChunk(int type, size_t start, const std::vector<uint8_t>* file_content,
- size_t raw_data_len, std::string entry_name)
- : type_(type),
- start_(start),
- input_file_ptr_(file_content),
- raw_data_len_(raw_data_len),
- compress_level_(6),
- entry_name_(std::move(entry_name)) {
- CHECK(file_content != nullptr) << "input file container can't be nullptr";
-}
-
-const uint8_t* ImageChunk::GetRawData() const {
- CHECK_LE(start_ + raw_data_len_, input_file_ptr_->size());
- return input_file_ptr_->data() + start_;
-}
-
-const uint8_t * ImageChunk::DataForPatch() const {
- if (type_ == CHUNK_DEFLATE) {
- return uncompressed_data_.data();
- }
- return GetRawData();
-}
-
-size_t ImageChunk::DataLengthForPatch() const {
- if (type_ == CHUNK_DEFLATE) {
- return uncompressed_data_.size();
- }
- 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;
- }
- return (raw_data_len_ == other.raw_data_len_ &&
- memcmp(GetRawData(), other.GetRawData(), raw_data_len_) == 0);
-}
-
-void ImageChunk::SetUncompressedData(std::vector<uint8_t> data) {
- uncompressed_data_ = std::move(data);
-}
-
-bool ImageChunk::SetBonusData(const std::vector<uint8_t>& bonus_data) {
- if (type_ != CHUNK_DEFLATE) {
- return false;
- }
- uncompressed_data_.insert(uncompressed_data_.end(), bonus_data.begin(), bonus_data.end());
- return true;
-}
-
-void ImageChunk::ChangeDeflateChunkToNormal() {
- if (type_ != CHUNK_DEFLATE) return;
- type_ = CHUNK_NORMAL;
- // No need to clear the entry name.
- uncompressed_data_.clear();
-}
-
-bool ImageChunk::IsAdjacentNormal(const ImageChunk& other) const {
- if (type_ != CHUNK_NORMAL || other.type_ != CHUNK_NORMAL) {
- return false;
- }
- return (other.start_ == start_ + raw_data_len_);
-}
-
-void ImageChunk::MergeAdjacentNormal(const ImageChunk& other) {
- CHECK(IsAdjacentNormal(other));
- raw_data_len_ = raw_data_len_ + other.raw_data_len_;
-}
-
-bool ImageChunk::MakePatch(const ImageChunk& tgt, const ImageChunk& src,
- std::vector<uint8_t>* patch_data,
- bsdiff::SuffixArrayIndexInterface** bsdiff_cache) {
-#if defined(__ANDROID__)
- char ptemp[] = "/data/local/tmp/imgdiff-patch-XXXXXX";
-#else
- char ptemp[] = "/tmp/imgdiff-patch-XXXXXX";
-#endif
-
- int fd = mkstemp(ptemp);
- if (fd == -1) {
- PLOG(ERROR) << "MakePatch failed to create a temporary file";
- return false;
- }
- close(fd);
-
- int r = bsdiff::bsdiff(src.DataForPatch(), src.DataLengthForPatch(), tgt.DataForPatch(),
- tgt.DataLengthForPatch(), ptemp, bsdiff_cache);
- if (r != 0) {
- LOG(ERROR) << "bsdiff() failed: " << r;
- return false;
- }
-
- android::base::unique_fd patch_fd(open(ptemp, O_RDONLY));
- if (patch_fd == -1) {
- PLOG(ERROR) << "Failed to open " << ptemp;
- return false;
- }
- struct stat st;
- if (fstat(patch_fd, &st) != 0) {
- PLOG(ERROR) << "Failed to stat patch file " << ptemp;
- return false;
- }
-
- size_t sz = static_cast<size_t>(st.st_size);
-
- patch_data->resize(sz);
- if (!android::base::ReadFully(patch_fd, patch_data->data(), sz)) {
- PLOG(ERROR) << "Failed to read " << ptemp;
- unlink(ptemp);
- return false;
- }
-
- unlink(ptemp);
-
- return true;
-}
-
-bool ImageChunk::ReconstructDeflateChunk() {
- if (type_ != CHUNK_DEFLATE) {
- LOG(ERROR) << "Attempted to reconstruct non-deflate chunk";
- return false;
- }
-
- // We only check two combinations of encoder parameters: level 6 (the default) and level 9
- // (the maximum).
- for (int level = 6; level <= 9; level += 3) {
- if (TryReconstruction(level)) {
- compress_level_ = level;
- return true;
- }
- }
-
- return false;
-}
-
-/*
- * Takes the uncompressed data stored in the chunk, compresses it using the zlib parameters stored
- * in the chunk, and checks that it matches exactly the compressed data we started with (also
- * stored in the chunk).
- */
-bool ImageChunk::TryReconstruction(int level) {
- z_stream strm;
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- strm.avail_in = uncompressed_data_.size();
- strm.next_in = uncompressed_data_.data();
- int ret = deflateInit2(&strm, level, METHOD, WINDOWBITS, MEMLEVEL, STRATEGY);
- if (ret < 0) {
- LOG(ERROR) << "Failed to initialize deflate: " << ret;
- return false;
- }
-
- std::vector<uint8_t> buffer(BUFFER_SIZE);
- size_t offset = 0;
- do {
- strm.avail_out = buffer.size();
- strm.next_out = buffer.data();
- ret = deflate(&strm, Z_FINISH);
- if (ret < 0) {
- LOG(ERROR) << "Failed to deflate: " << ret;
- return false;
- }
-
- size_t compressed_size = buffer.size() - strm.avail_out;
- if (memcmp(buffer.data(), input_file_ptr_->data() + start_ + offset, compressed_size) != 0) {
- // mismatch; data isn't the same.
- deflateEnd(&strm);
- return false;
- }
- offset += compressed_size;
- } while (ret != Z_STREAM_END);
- deflateEnd(&strm);
-
- if (offset != raw_data_len_) {
- // mismatch; ran out of data before we should have.
- return false;
- }
- return true;
-}
-
-PatchChunk::PatchChunk(const ImageChunk& tgt, const ImageChunk& src, std::vector<uint8_t> data)
- : type_(tgt.GetType()),
- source_start_(src.GetStartOffset()),
- source_len_(src.GetRawDataLength()),
- source_uncompressed_len_(src.DataLengthForPatch()),
- target_start_(tgt.GetStartOffset()),
- target_len_(tgt.GetRawDataLength()),
- target_uncompressed_len_(tgt.DataLengthForPatch()),
- target_compress_level_(tgt.GetCompressLevel()),
- data_(std::move(data)) {}
-
-// Construct a CHUNK_RAW patch from the target data directly.
-PatchChunk::PatchChunk(const ImageChunk& tgt)
- : type_(CHUNK_RAW),
- source_start_(0),
- source_len_(0),
- source_uncompressed_len_(0),
- target_start_(tgt.GetStartOffset()),
- target_len_(tgt.GetRawDataLength()),
- target_uncompressed_len_(tgt.DataLengthForPatch()),
- target_compress_level_(tgt.GetCompressLevel()),
- data_(tgt.GetRawData(), tgt.GetRawData() + tgt.GetRawDataLength()) {}
-
-// Return true if raw data is smaller than the patch size.
-bool PatchChunk::RawDataIsSmaller(const ImageChunk& tgt, size_t patch_size) {
- size_t target_len = tgt.GetRawDataLength();
- return target_len < patch_size || (tgt.GetType() == CHUNK_NORMAL && target_len <= 160);
-}
-
-void PatchChunk::UpdateSourceOffset(const SortedRangeSet& src_range) {
- if (type_ == CHUNK_DEFLATE) {
- source_start_ = src_range.GetOffsetInRangeSet(source_start_);
- }
-}
-
-// Header size:
-// header_type 4 bytes
-// CHUNK_NORMAL 8*3 = 24 bytes
-// CHUNK_DEFLATE 8*5 + 4*5 = 60 bytes
-// CHUNK_RAW 4 bytes + patch_size
-size_t PatchChunk::GetHeaderSize() const {
- switch (type_) {
- case CHUNK_NORMAL:
- return 4 + 8 * 3;
- case CHUNK_DEFLATE:
- return 4 + 8 * 5 + 4 * 5;
- case CHUNK_RAW:
- return 4 + 4 + data_.size();
- default:
- CHECK(false) << "unexpected chunk type: " << type_; // Should not reach here.
- return 0;
- }
-}
-
-// Return the offset of the next patch into the patch data.
-size_t PatchChunk::WriteHeaderToFd(int fd, size_t offset, size_t index) const {
- Write4(fd, type_);
- switch (type_) {
- case CHUNK_NORMAL:
- 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:
- 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));
- Write8(fd, static_cast<int64_t>(source_uncompressed_len_));
- Write8(fd, static_cast<int64_t>(target_uncompressed_len_));
- Write4(fd, target_compress_level_);
- Write4(fd, ImageChunk::METHOD);
- Write4(fd, ImageChunk::WINDOWBITS);
- Write4(fd, ImageChunk::MEMLEVEL);
- Write4(fd, ImageChunk::STRATEGY);
- return offset + data_.size();
- case CHUNK_RAW:
- 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";
- }
- return offset;
- default:
- CHECK(false) << "unexpected chunk type: " << type_;
- return offset;
- }
-}
-
-size_t PatchChunk::PatchSize() const {
- if (type_ == CHUNK_RAW) {
- return GetHeaderSize();
- }
- return GetHeaderSize() + data_.size();
-}
-
-// Write the contents of |patch_chunks| to |patch_fd|.
-bool PatchChunk::WritePatchDataToFd(const std::vector<PatchChunk>& patch_chunks, int patch_fd) {
- // Figure out how big the imgdiff file header is going to be, so that we can correctly compute
- // the offset of each bsdiff patch within the file.
- size_t total_header_size = 12;
- for (const auto& patch : patch_chunks) {
- total_header_size += patch.GetHeaderSize();
- }
-
- size_t offset = total_header_size;
-
- // Write out the headers.
- if (!android::base::WriteStringToFd("IMGDIFF" + std::to_string(VERSION), patch_fd)) {
- 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) {
- offset = patch_chunks[i].WriteHeaderToFd(patch_fd, offset, i);
- }
-
- // Append each chunk's bsdiff patch, in order.
- for (const auto& patch : patch_chunks) {
- if (patch.type_ == CHUNK_RAW) {
- continue;
- }
- if (!android::base::WriteFully(patch_fd, patch.data_.data(), patch.data_.size())) {
- PLOG(ERROR) << "Failed to write " << patch.data_.size() << " bytes patch to patch_fd";
- return false;
- }
- }
-
- return true;
-}
-
-ImageChunk& Image::operator[](size_t i) {
- CHECK_LT(i, chunks_.size());
- return chunks_[i];
-}
-
-const ImageChunk& Image::operator[](size_t i) const {
- CHECK_LT(i, chunks_.size());
- return chunks_[i];
-}
-
-void Image::MergeAdjacentNormalChunks() {
- size_t merged_last = 0, cur = 0;
- while (cur < chunks_.size()) {
- // Look for normal chunks adjacent to the current one. If such chunk exists, extend the
- // length of the current normal chunk.
- size_t to_check = cur + 1;
- while (to_check < chunks_.size() && chunks_[cur].IsAdjacentNormal(chunks_[to_check])) {
- chunks_[cur].MergeAdjacentNormal(chunks_[to_check]);
- to_check++;
- }
-
- if (merged_last != cur) {
- chunks_[merged_last] = std::move(chunks_[cur]);
- }
- merged_last++;
- cur = to_check;
- }
- if (merged_last < chunks_.size()) {
- chunks_.erase(chunks_.begin() + merged_last, chunks_.end());
- }
-}
-
-void Image::DumpChunks() const {
- std::string type = is_source_ ? "source" : "target";
- LOG(INFO) << "Dumping chunks for " << type;
- for (size_t i = 0; i < chunks_.size(); ++i) {
- chunks_[i].Dump(i);
- }
-}
-
-bool Image::ReadFile(const std::string& filename, std::vector<uint8_t>* file_content) {
- CHECK(file_content != nullptr);
-
- android::base::unique_fd fd(open(filename.c_str(), O_RDONLY));
- if (fd == -1) {
- PLOG(ERROR) << "Failed to open " << filename;
- return false;
- }
- struct stat st;
- if (fstat(fd, &st) != 0) {
- 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)) {
- PLOG(ERROR) << "Failed to read " << filename;
- return false;
- }
- fd.reset();
-
- return true;
-}
-
-bool ZipModeImage::Initialize(const std::string& filename) {
- if (!ReadFile(filename, &file_content_)) {
- return false;
- }
-
- // Omit the trailing zeros before we pass the file to ziparchive handler.
- size_t zipfile_size;
- if (!GetZipFileSize(&zipfile_size)) {
- 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) {
- LOG(ERROR) << "Failed to open zip file " << filename << ": " << ErrorCodeString(err);
- CloseArchive(handle);
- return false;
- }
-
- if (!InitializeChunks(filename, handle)) {
- CloseArchive(handle);
- return false;
- }
-
- CloseArchive(handle);
- return true;
-}
-
-// Iterate the zip entries and compose the image chunks accordingly.
-bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandle handle) {
- void* cookie;
- int ret = StartIteration(handle, &cookie);
- if (ret != 0) {
- LOG(ERROR) << "Failed to iterate over entries in " << filename << ": " << ErrorCodeString(ret);
- return false;
- }
-
- // Create a list of deflated zip entries, sorted by offset.
- std::vector<std::pair<std::string, ZipEntry64>> temp_entries;
- std::string name;
- ZipEntry64 entry;
- while ((ret = Next(cookie, &entry, &name)) == 0) {
- if (entry.method == kCompressDeflated || limit_ > 0) {
- temp_entries.emplace_back(name, entry);
- }
- }
-
- if (ret != -1) {
- LOG(ERROR) << "Error while iterating over zip entries: " << ErrorCodeString(ret);
- return false;
- }
- std::sort(temp_entries.begin(), temp_entries.end(),
- [](auto& entry1, auto& entry2) { return entry1.second.offset < entry2.second.offset; });
-
- EndIteration(cookie);
-
- // For source chunks, we don't need to compose chunks for the metadata.
- if (is_source_) {
- for (auto& entry : temp_entries) {
- if (!AddZipEntryToChunks(handle, entry.first, &entry.second)) {
- LOG(ERROR) << "Failed to add " << entry.first << " to source chunks";
- return false;
- }
- }
-
- // Add the end of zip file (mainly central directory) as a normal chunk.
- size_t entries_end = 0;
- if (!temp_entries.empty()) {
- CHECK_GE(temp_entries.back().second.offset, 0);
- if (__builtin_add_overflow(temp_entries.back().second.offset,
- temp_entries.back().second.compressed_length, &entries_end)) {
- LOG(ERROR) << "`entries_end` overflows on entry with offset "
- << temp_entries.back().second.offset << " and compressed_length "
- << temp_entries.back().second.compressed_length;
- return false;
- }
- }
- CHECK_LT(entries_end, file_content_.size());
- chunks_.emplace_back(CHUNK_NORMAL, entries_end, &file_content_,
- file_content_.size() - entries_end);
-
- return true;
- }
-
- // For target chunks, add the deflate entries as CHUNK_DEFLATE and the contents between two
- // deflate entries as CHUNK_NORMAL.
- size_t pos = 0;
- size_t nextentry = 0;
- while (pos < file_content_.size()) {
- if (nextentry < temp_entries.size() &&
- static_cast<off64_t>(pos) == temp_entries[nextentry].second.offset) {
- // Add the next zip entry.
- std::string entry_name = temp_entries[nextentry].first;
- if (!AddZipEntryToChunks(handle, entry_name, &temp_entries[nextentry].second)) {
- LOG(ERROR) << "Failed to add " << entry_name << " to target chunks";
- return false;
- }
- if (temp_entries[nextentry].second.compressed_length > std::numeric_limits<size_t>::max()) {
- LOG(ERROR) << "Entry " << name << " compressed size exceeds size of address space. "
- << entry.compressed_length;
- return false;
- }
- if (__builtin_add_overflow(pos, temp_entries[nextentry].second.compressed_length, &pos)) {
- LOG(ERROR) << "`pos` overflows after adding "
- << temp_entries[nextentry].second.compressed_length;
- return false;
- }
- ++nextentry;
- continue;
- }
-
- // Use a normal chunk to take all the data up to the start of the next entry.
- size_t raw_data_len;
- if (nextentry < temp_entries.size()) {
- raw_data_len = temp_entries[nextentry].second.offset - pos;
- } else {
- raw_data_len = file_content_.size() - pos;
- }
- chunks_.emplace_back(CHUNK_NORMAL, pos, &file_content_, raw_data_len);
-
- pos += raw_data_len;
- }
-
- return true;
-}
-
-bool ZipModeImage::AddZipEntryToChunks(ZipArchiveHandle handle, const std::string& entry_name,
- ZipEntry64* entry) {
- if (entry->compressed_length > std::numeric_limits<size_t>::max()) {
- LOG(ERROR) << "Failed to add " << entry_name
- << " because's compressed size exceeds size of address space. "
- << entry->compressed_length;
- return false;
- }
- size_t compressed_len = entry->compressed_length;
- if (compressed_len == 0) return true;
-
- // Split the entry into several normal chunks if it's too large.
- if (limit_ > 0 && compressed_len > limit_) {
- int count = 0;
- while (compressed_len > 0) {
- size_t length = std::min(limit_, compressed_len);
- std::string name = entry_name + "-" + std::to_string(count);
- chunks_.emplace_back(CHUNK_NORMAL, entry->offset + limit_ * count, &file_content_, length,
- name);
-
- count++;
- compressed_len -= length;
- }
- } else if (entry->method == kCompressDeflated) {
- size_t uncompressed_len = entry->uncompressed_length;
- if (uncompressed_len > std::numeric_limits<size_t>::max()) {
- LOG(ERROR) << "Failed to add " << entry_name
- << " because's compressed size exceeds size of address space. "
- << uncompressed_len;
- return false;
- }
- std::vector<uint8_t> uncompressed_data(uncompressed_len);
- int ret = ExtractToMemory(handle, entry, uncompressed_data.data(), uncompressed_len);
- if (ret != 0) {
- 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);
- curr.SetUncompressedData(std::move(uncompressed_data));
- chunks_.push_back(std::move(curr));
- } else {
- chunks_.emplace_back(CHUNK_NORMAL, entry->offset, &file_content_, compressed_len, entry_name);
- }
-
- return true;
-}
-
-// EOCD record
-// offset 0: signature 0x06054b50, 4 bytes
-// offset 4: number of this disk, 2 bytes
-// ...
-// offset 20: comment length, 2 bytes
-// offset 22: comment, n bytes
-bool ZipModeImage::GetZipFileSize(size_t* input_file_size) {
- if (file_content_.size() < 22) {
- LOG(ERROR) << "File is too small to be a zip file";
- return false;
- }
-
- // Look for End of central directory record of the zip file, and calculate the actual
- // zip_file size.
- for (int i = file_content_.size() - 22; i >= 0; i--) {
- if (file_content_[i] == 0x50) {
- if (get_unaligned<uint32_t>(&file_content_[i]) == 0x06054b50) {
- // double-check: this archive consists of a single "disk".
- CHECK_EQ(get_unaligned<uint16_t>(&file_content_[i + 4]), 0);
-
- uint16_t comment_length = get_unaligned<uint16_t>(&file_content_[i + 20]);
- size_t file_size = i + 22 + comment_length;
- CHECK_LE(file_size, file_content_.size());
- *input_file_size = file_size;
- return true;
- }
- }
- }
-
- // EOCD not found, this file is likely not a valid zip file.
- return false;
-}
-
-ImageChunk ZipModeImage::PseudoSource() const {
- CHECK(is_source_);
- return ImageChunk(CHUNK_NORMAL, 0, &file_content_, file_content_.size());
-}
-
-const ImageChunk* ZipModeImage::FindChunkByName(const std::string& name, bool find_normal) const {
- if (name.empty()) {
- return nullptr;
- }
- for (auto& chunk : chunks_) {
- if (chunk.GetType() != CHUNK_DEFLATE && !find_normal) {
- continue;
- }
-
- if (chunk.GetEntryName() == name) {
- return &chunk;
- }
-
- // Edge case when target chunk is split due to size limit but source chunk isn't.
- if (name == (chunk.GetEntryName() + "-0") || chunk.GetEntryName() == (name + "-0")) {
- return &chunk;
- }
-
- // TODO handle the .so files with incremental version number.
- // (e.g. lib/arm64-v8a/libcronet.59.0.3050.4.so)
- }
-
- return nullptr;
-}
-
-ImageChunk* ZipModeImage::FindChunkByName(const std::string& name, bool find_normal) {
- return const_cast<ImageChunk*>(
- static_cast<const ZipModeImage*>(this)->FindChunkByName(name, find_normal));
-}
-
-bool ZipModeImage::CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage* src_image) {
- for (auto& tgt_chunk : *tgt_image) {
- if (tgt_chunk.GetType() != CHUNK_DEFLATE) {
- continue;
- }
-
- ImageChunk* src_chunk = src_image->FindChunkByName(tgt_chunk.GetEntryName());
- if (src_chunk == nullptr) {
- tgt_chunk.ChangeDeflateChunkToNormal();
- } else if (tgt_chunk == *src_chunk) {
- // If two deflate chunks are identical (eg, the kernel has not changed between two builds),
- // treat them as normal chunks. This makes applypatch much faster -- it can apply a trivial
- // patch to the compressed data, rather than uncompressing and recompressing to apply the
- // trivial patch to the uncompressed data.
- tgt_chunk.ChangeDeflateChunkToNormal();
- src_chunk->ChangeDeflateChunkToNormal();
- } 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.
- LOG(WARNING) << "Failed to reconstruct target deflate chunk [" << tgt_chunk.GetEntryName()
- << "]; treating as normal";
-
- tgt_chunk.ChangeDeflateChunkToNormal();
- src_chunk->ChangeDeflateChunkToNormal();
- }
- }
-
- // For zips, we only need merge normal chunks for the target: deflated chunks are matched via
- // filename, and normal chunks are patched using the entire source file as the source.
- if (tgt_image->limit_ == 0) {
- tgt_image->MergeAdjacentNormalChunks();
- tgt_image->DumpChunks();
- }
-
- return true;
-}
-
-// For each target chunk, look for the corresponding source chunk by the zip_entry name. If
-// found, add the range of this chunk in the original source file to the block aligned source
-// ranges. Construct the split src & tgt image once the size of source range reaches limit.
-bool ZipModeImage::SplitZipModeImageWithLimit(const ZipModeImage& tgt_image,
- const ZipModeImage& src_image,
- std::vector<ZipModeImage>* split_tgt_images,
- std::vector<ZipModeImage>* split_src_images,
- std::vector<SortedRangeSet>* split_src_ranges) {
- CHECK_EQ(tgt_image.limit_, src_image.limit_);
- size_t limit = tgt_image.limit_;
-
- src_image.DumpChunks();
- LOG(INFO) << "Splitting " << tgt_image.NumOfChunks() << " tgt chunks...";
-
- SortedRangeSet used_src_ranges; // ranges used for previous split source images.
-
- // Reserve the central directory in advance for the last split image.
- const auto& central_directory = src_image.cend() - 1;
- CHECK_EQ(CHUNK_NORMAL, central_directory->GetType());
- used_src_ranges.Insert(central_directory->GetStartOffset(),
- central_directory->DataLengthForPatch());
-
- SortedRangeSet src_ranges;
- std::vector<ImageChunk> split_src_chunks;
- std::vector<ImageChunk> split_tgt_chunks;
- for (auto tgt = tgt_image.cbegin(); tgt != tgt_image.cend(); tgt++) {
- const ImageChunk* src = src_image.FindChunkByName(tgt->GetEntryName(), true);
- if (src == nullptr) {
- split_tgt_chunks.emplace_back(CHUNK_NORMAL, tgt->GetStartOffset(), &tgt_image.file_content_,
- tgt->GetRawDataLength());
- continue;
- }
-
- size_t src_offset = src->GetStartOffset();
- size_t src_length = src->GetRawDataLength();
-
- CHECK(src_length > 0);
- CHECK_LE(src_length, limit);
-
- // Make sure this source range hasn't been used before so that the src_range pieces don't
- // overlap with each other.
- if (!RemoveUsedBlocks(&src_offset, &src_length, used_src_ranges)) {
- split_tgt_chunks.emplace_back(CHUNK_NORMAL, tgt->GetStartOffset(), &tgt_image.file_content_,
- tgt->GetRawDataLength());
- } else if (src_ranges.blocks() * BLOCK_SIZE + src_length <= limit) {
- src_ranges.Insert(src_offset, src_length);
-
- // Add the deflate source chunk if it hasn't been aligned.
- if (src->GetType() == CHUNK_DEFLATE && src_length == src->GetRawDataLength()) {
- split_src_chunks.push_back(*src);
- split_tgt_chunks.push_back(*tgt);
- } else {
- // TODO split smarter to avoid alignment of large deflate chunks
- split_tgt_chunks.emplace_back(CHUNK_NORMAL, tgt->GetStartOffset(), &tgt_image.file_content_,
- tgt->GetRawDataLength());
- }
- } else {
- bool added_image = ZipModeImage::AddSplitImageFromChunkList(
- tgt_image, src_image, src_ranges, split_tgt_chunks, split_src_chunks, split_tgt_images,
- split_src_images);
-
- split_tgt_chunks.clear();
- split_src_chunks.clear();
- // No need to update the split_src_ranges if we don't update the split source images.
- if (added_image) {
- used_src_ranges.Insert(src_ranges);
- split_src_ranges->push_back(std::move(src_ranges));
- }
- src_ranges = {};
-
- // We don't have enough space for the current chunk; start a new split image and handle
- // this chunk there.
- tgt--;
- }
- }
-
- // TODO Trim it in case the CD exceeds limit too much.
- src_ranges.Insert(central_directory->GetStartOffset(), central_directory->DataLengthForPatch());
- bool added_image = ZipModeImage::AddSplitImageFromChunkList(tgt_image, src_image, src_ranges,
- split_tgt_chunks, split_src_chunks,
- split_tgt_images, split_src_images);
- if (added_image) {
- split_src_ranges->push_back(std::move(src_ranges));
- }
-
- ValidateSplitImages(*split_tgt_images, *split_src_images, *split_src_ranges,
- tgt_image.file_content_.size());
-
- return true;
-}
-
-bool ZipModeImage::AddSplitImageFromChunkList(const ZipModeImage& tgt_image,
- const ZipModeImage& src_image,
- const SortedRangeSet& split_src_ranges,
- const std::vector<ImageChunk>& split_tgt_chunks,
- const std::vector<ImageChunk>& split_src_chunks,
- std::vector<ZipModeImage>* split_tgt_images,
- std::vector<ZipModeImage>* split_src_images) {
- CHECK(!split_tgt_chunks.empty());
-
- std::vector<ImageChunk> aligned_tgt_chunks;
-
- // Align the target chunks in the beginning with BLOCK_SIZE.
- size_t i = 0;
- while (i < split_tgt_chunks.size()) {
- size_t tgt_start = split_tgt_chunks[i].GetStartOffset();
- size_t tgt_length = split_tgt_chunks[i].GetRawDataLength();
-
- // Current ImageChunk is long enough to align.
- if (AlignHead(&tgt_start, &tgt_length)) {
- aligned_tgt_chunks.emplace_back(CHUNK_NORMAL, tgt_start, &tgt_image.file_content_,
- tgt_length);
- break;
- }
-
- i++;
- }
-
- // Nothing left after alignment in the current split tgt chunks; skip adding the split_tgt_image.
- if (i == split_tgt_chunks.size()) {
- return false;
- }
-
- aligned_tgt_chunks.insert(aligned_tgt_chunks.end(), split_tgt_chunks.begin() + i + 1,
- split_tgt_chunks.end());
- CHECK(!aligned_tgt_chunks.empty());
-
- // Add a normal chunk to align the contents in the end.
- size_t end_offset =
- aligned_tgt_chunks.back().GetStartOffset() + aligned_tgt_chunks.back().GetRawDataLength();
- if (end_offset % BLOCK_SIZE != 0 && end_offset < tgt_image.file_content_.size()) {
- size_t tail_block_length = std::min<size_t>(tgt_image.file_content_.size() - end_offset,
- BLOCK_SIZE - (end_offset % BLOCK_SIZE));
- aligned_tgt_chunks.emplace_back(CHUNK_NORMAL, end_offset, &tgt_image.file_content_,
- tail_block_length);
- }
-
- ZipModeImage split_tgt_image(false);
- split_tgt_image.Initialize(aligned_tgt_chunks, {});
- split_tgt_image.MergeAdjacentNormalChunks();
-
- // Construct the split source file based on the split src ranges.
- std::vector<uint8_t> split_src_content;
- for (const auto& r : split_src_ranges) {
- size_t end = std::min(src_image.file_content_.size(), r.second * BLOCK_SIZE);
- split_src_content.insert(split_src_content.end(),
- src_image.file_content_.begin() + r.first * BLOCK_SIZE,
- src_image.file_content_.begin() + end);
- }
-
- // We should not have an empty src in our design; otherwise we will encounter an error in
- // bsdiff since split_src_content.data() == nullptr.
- CHECK(!split_src_content.empty());
-
- ZipModeImage split_src_image(true);
- split_src_image.Initialize(split_src_chunks, split_src_content);
-
- split_tgt_images->push_back(std::move(split_tgt_image));
- split_src_images->push_back(std::move(split_src_image));
-
- return true;
-}
-
-void ZipModeImage::ValidateSplitImages(const std::vector<ZipModeImage>& split_tgt_images,
- const std::vector<ZipModeImage>& split_src_images,
- std::vector<SortedRangeSet>& split_src_ranges,
- size_t total_tgt_size) {
- CHECK_EQ(split_tgt_images.size(), split_src_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;
- for (const auto& tgt_image : split_tgt_images) {
- CHECK(!tgt_image.chunks_.empty());
-
- CHECK_EQ(last_offset, tgt_image.chunks_.front().GetStartOffset());
- CHECK(last_offset % BLOCK_SIZE == 0);
-
- // Check the target chunks within the split image are continuous.
- for (const auto& chunk : tgt_image.chunks_) {
- CHECK_EQ(last_offset, chunk.GetStartOffset());
- last_offset += chunk.GetRawDataLength();
- }
- }
- CHECK_EQ(total_tgt_size, last_offset);
-
- // Verify that the source ranges are mutually exclusive.
- CHECK_EQ(split_src_images.size(), split_src_ranges.size());
- SortedRangeSet used_src_ranges;
- for (size_t i = 0; i < split_src_ranges.size(); i++) {
- CHECK(!used_src_ranges.Overlaps(split_src_ranges[i]))
- << "src range " << split_src_ranges[i].ToString() << " overlaps "
- << used_src_ranges.ToString();
- used_src_ranges.Insert(split_src_ranges[i]);
- }
-}
-
-bool ZipModeImage::GeneratePatchesInternal(const ZipModeImage& tgt_image,
- const ZipModeImage& src_image,
- std::vector<PatchChunk>* patch_chunks) {
- LOG(INFO) << "Constructing patches for " << tgt_image.NumOfChunks() << " chunks...";
- patch_chunks->clear();
-
- bsdiff::SuffixArrayIndexInterface* bsdiff_cache = nullptr;
- for (size_t i = 0; i < tgt_image.NumOfChunks(); i++) {
- const auto& tgt_chunk = tgt_image[i];
-
- if (PatchChunk::RawDataIsSmaller(tgt_chunk, 0)) {
- patch_chunks->emplace_back(tgt_chunk);
- continue;
- }
-
- const ImageChunk* src_chunk = (tgt_chunk.GetType() != CHUNK_DEFLATE)
- ? nullptr
- : src_image.FindChunkByName(tgt_chunk.GetEntryName());
-
- const auto& src_ref = (src_chunk == nullptr) ? src_image.PseudoSource() : *src_chunk;
- bsdiff::SuffixArrayIndexInterface** bsdiff_cache_ptr =
- (src_chunk == nullptr) ? &bsdiff_cache : nullptr;
-
- std::vector<uint8_t> patch_data;
- if (!ImageChunk::MakePatch(tgt_chunk, src_ref, &patch_data, bsdiff_cache_ptr)) {
- LOG(ERROR) << "Failed to generate patch, name: " << tgt_chunk.GetEntryName();
- return false;
- }
-
- 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);
- } else {
- patch_chunks->emplace_back(tgt_chunk, src_ref, std::move(patch_data));
- }
- }
- delete bsdiff_cache;
-
- CHECK_EQ(patch_chunks->size(), tgt_image.NumOfChunks());
- return true;
-}
-
-bool ZipModeImage::GeneratePatches(const ZipModeImage& tgt_image, const ZipModeImage& src_image,
- const std::string& patch_name) {
- std::vector<PatchChunk> patch_chunks;
-
- ZipModeImage::GeneratePatchesInternal(tgt_image, src_image, &patch_chunks);
-
- CHECK_EQ(tgt_image.NumOfChunks(), patch_chunks.size());
-
- 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) {
- PLOG(ERROR) << "Failed to open " << patch_name;
- return false;
- }
-
- return PatchChunk::WritePatchDataToFd(patch_chunks, patch_fd);
-}
-
-bool ZipModeImage::GeneratePatches(const std::vector<ZipModeImage>& split_tgt_images,
- const std::vector<ZipModeImage>& split_src_images,
- const std::vector<SortedRangeSet>& split_src_ranges,
- const std::string& patch_name,
- const std::string& split_info_file,
- const std::string& debug_dir) {
- 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) {
- PLOG(ERROR) << "Failed to open " << patch_name;
- return false;
- }
-
- std::vector<std::string> split_info_list;
- for (size_t i = 0; i < split_tgt_images.size(); i++) {
- std::vector<PatchChunk> patch_chunks;
- if (!ZipModeImage::GeneratePatchesInternal(split_tgt_images[i], split_src_images[i],
- &patch_chunks)) {
- LOG(ERROR) << "Failed to generate split patch";
- return false;
- }
-
- size_t total_patch_size = 12;
- for (auto& p : patch_chunks) {
- p.UpdateSourceOffset(split_src_ranges[i]);
- total_patch_size += p.PatchSize();
- }
-
- if (!PatchChunk::WritePatchDataToFd(patch_chunks, patch_fd)) {
- return false;
- }
-
- size_t split_tgt_size = split_tgt_images[i].chunks_.back().GetStartOffset() +
- split_tgt_images[i].chunks_.back().GetRawDataLength() -
- split_tgt_images[i].chunks_.front().GetStartOffset();
- std::string split_info = android::base::StringPrintf(
- "%zu %zu %s", total_patch_size, split_tgt_size, split_src_ranges[i].ToString().c_str());
- split_info_list.push_back(split_info);
-
- // Write the split source & patch into the debug directory.
- if (!debug_dir.empty()) {
- std::string src_name = android::base::StringPrintf("%s/src-%zu", debug_dir.c_str(), i);
- android::base::unique_fd fd(
- open(src_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR));
-
- if (fd == -1) {
- 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())) {
- PLOG(ERROR) << "Failed to write split source data into " << src_name;
- return false;
- }
-
- std::string patch_name = android::base::StringPrintf("%s/patch-%zu", debug_dir.c_str(), i);
- fd.reset(open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR));
-
- if (fd == -1) {
- PLOG(ERROR) << "Failed to open " << patch_name;
- return false;
- }
- if (!PatchChunk::WritePatchDataToFd(patch_chunks, fd)) {
- return false;
- }
- }
- }
-
- // Store the split in the following format:
- // Line 0: imgdiff version#
- // Line 1: number of pieces
- // Line 2: patch_size_1 tgt_size_1 src_range_1
- // ...
- // Line n+1: patch_size_n tgt_size_n src_range_n
- 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)) {
- PLOG(ERROR) << "Failed to write split info to " << split_info_file;
- return false;
- }
-
- return true;
-}
-
-bool ImageModeImage::Initialize(const std::string& filename) {
- if (!ReadFile(filename, &file_content_)) {
- return false;
- }
-
- size_t sz = file_content_.size();
- size_t pos = 0;
- while (pos < sz) {
- // 0x00 no header flags, 0x08 deflate compression, 0x1f8b gzip magic number
- if (sz - pos >= 4 && get_unaligned<uint32_t>(file_content_.data() + pos) == 0x00088b1f) {
- // 'pos' is the offset of the start of a gzip chunk.
- size_t chunk_offset = pos;
-
- // The remaining data is too small to be a gzip chunk; treat them as a normal chunk.
- if (sz - pos < GZIP_HEADER_LEN + GZIP_FOOTER_LEN) {
- chunks_.emplace_back(CHUNK_NORMAL, pos, &file_content_, sz - pos);
- break;
- }
-
- // We need three chunks for the deflated image in total, one normal chunk for the header,
- // one deflated chunk for the body, and another normal chunk for the footer.
- chunks_.emplace_back(CHUNK_NORMAL, pos, &file_content_, GZIP_HEADER_LEN);
- pos += GZIP_HEADER_LEN;
-
- // We must decompress this chunk in order to discover where it ends, and so we can update
- // the uncompressed_data of the image body and its length.
-
- z_stream strm;
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- strm.avail_in = sz - pos;
- strm.next_in = file_content_.data() + pos;
-
- // -15 means we are decoding a 'raw' deflate stream; zlib will
- // not expect zlib headers.
- int ret = inflateInit2(&strm, -15);
- if (ret < 0) {
- LOG(ERROR) << "Failed to initialize inflate: " << ret;
- return false;
- }
-
- size_t allocated = BUFFER_SIZE;
- std::vector<uint8_t> uncompressed_data(allocated);
- size_t uncompressed_len = 0, raw_data_len = 0;
- do {
- strm.avail_out = allocated - uncompressed_len;
- strm.next_out = uncompressed_data.data() + uncompressed_len;
- ret = inflate(&strm, Z_NO_FLUSH);
- if (ret < 0) {
- LOG(WARNING) << "Inflate failed [" << strm.msg << "] at offset [" << chunk_offset
- << "]; treating as a normal chunk";
- break;
- }
- uncompressed_len = allocated - strm.avail_out;
- if (strm.avail_out == 0) {
- allocated *= 2;
- uncompressed_data.resize(allocated);
- }
- } while (ret != Z_STREAM_END);
-
- raw_data_len = sz - strm.avail_in - pos;
- inflateEnd(&strm);
-
- if (ret < 0) {
- continue;
- }
-
- // The footer contains the size of the uncompressed data. Double-check to make sure that it
- // matches the size of the data we got when we actually did the decompression.
- size_t footer_index = pos + raw_data_len + GZIP_FOOTER_LEN - 4;
- if (sz - footer_index < 4) {
- 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) {
- LOG(WARNING) << "footer size " << footer_size << " != " << uncompressed_len
- << "; treating as a normal chunk";
- continue;
- }
-
- ImageChunk body(CHUNK_DEFLATE, pos, &file_content_, raw_data_len);
- uncompressed_data.resize(uncompressed_len);
- body.SetUncompressedData(std::move(uncompressed_data));
- chunks_.push_back(std::move(body));
-
- pos += raw_data_len;
-
- // create a normal chunk for the footer
- chunks_.emplace_back(CHUNK_NORMAL, pos, &file_content_, GZIP_FOOTER_LEN);
-
- pos += GZIP_FOOTER_LEN;
- } else {
- // Use a normal chunk to take all the contents until the next gzip chunk (or EOF); we expect
- // the number of chunks to be small (5 for typical boot and recovery images).
-
- // Scan forward until we find a gzip header.
- size_t data_len = 0;
- while (data_len + pos < sz) {
- if (data_len + pos + 4 <= sz &&
- get_unaligned<uint32_t>(file_content_.data() + pos + data_len) == 0x00088b1f) {
- break;
- }
- data_len++;
- }
- chunks_.emplace_back(CHUNK_NORMAL, pos, &file_content_, data_len);
-
- pos += data_len;
- }
- }
-
- return true;
-}
-
-bool ImageModeImage::SetBonusData(const std::vector<uint8_t>& bonus_data) {
- CHECK(is_source_);
- if (chunks_.size() < 2 || !chunks_[1].SetBonusData(bonus_data)) {
- LOG(ERROR) << "Failed to set bonus data";
- DumpChunks();
- return false;
- }
-
- LOG(INFO) << " using " << bonus_data.size() << " bytes of bonus data";
- return true;
-}
-
-// In Image Mode, verify that the source and target images have the same chunk structure (ie, the
-// same sequence of deflate and normal chunks).
-bool ImageModeImage::CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeImage* src_image) {
- // In image mode, merge the gzip header and footer in with any adjacent normal chunks.
- tgt_image->MergeAdjacentNormalChunks();
- src_image->MergeAdjacentNormalChunks();
-
- if (tgt_image->NumOfChunks() != src_image->NumOfChunks()) {
- 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()) {
- LOG(ERROR) << "Source and target don't have same chunk structure! (chunk " << i << ")";
- tgt_image->DumpChunks();
- src_image->DumpChunks();
- return false;
- }
- }
-
- for (size_t i = 0; i < tgt_image->NumOfChunks(); ++i) {
- auto& tgt_chunk = (*tgt_image)[i];
- auto& src_chunk = (*src_image)[i];
- if (tgt_chunk.GetType() != CHUNK_DEFLATE) {
- continue;
- }
-
- // If two deflate chunks are identical treat them as normal chunks.
- if (tgt_chunk == src_chunk) {
- tgt_chunk.ChangeDeflateChunkToNormal();
- src_chunk.ChangeDeflateChunkToNormal();
- } 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
- LOG(WARNING) << "Failed to reconstruct target deflate chunk " << i << " ["
- << tgt_chunk.GetEntryName() << "]; treating as normal";
- tgt_chunk.ChangeDeflateChunkToNormal();
- src_chunk.ChangeDeflateChunkToNormal();
- }
- }
-
- // For images, we need to maintain the parallel structure of the chunk lists, so do the merging
- // in both the source and target lists.
- tgt_image->MergeAdjacentNormalChunks();
- src_image->MergeAdjacentNormalChunks();
- if (tgt_image->NumOfChunks() != src_image->NumOfChunks()) {
- // This shouldn't happen.
- LOG(ERROR) << "Merging normal chunks went awry";
- return false;
- }
-
- return true;
-}
-
-// In image mode, generate patches against the given source chunks and bonus_data; write the
-// result to |patch_name|.
-bool ImageModeImage::GeneratePatches(const ImageModeImage& tgt_image,
- const ImageModeImage& src_image,
- const std::string& patch_name) {
- LOG(INFO) << "Constructing patches for " << tgt_image.NumOfChunks() << " chunks...";
- std::vector<PatchChunk> patch_chunks;
- patch_chunks.reserve(tgt_image.NumOfChunks());
-
- for (size_t i = 0; i < tgt_image.NumOfChunks(); i++) {
- const auto& tgt_chunk = tgt_image[i];
- const auto& src_chunk = src_image[i];
-
- if (PatchChunk::RawDataIsSmaller(tgt_chunk, 0)) {
- patch_chunks.emplace_back(tgt_chunk);
- continue;
- }
-
- std::vector<uint8_t> patch_data;
- if (!ImageChunk::MakePatch(tgt_chunk, src_chunk, &patch_data, nullptr)) {
- LOG(ERROR) << "Failed to generate patch for target chunk " << i;
- return false;
- }
- 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);
- } else {
- patch_chunks.emplace_back(tgt_chunk, src_chunk, std::move(patch_data));
- }
- }
-
- CHECK_EQ(tgt_image.NumOfChunks(), patch_chunks.size());
-
- 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) {
- PLOG(ERROR) << "Failed to open " << patch_name;
- return false;
- }
-
- return PatchChunk::WritePatchDataToFd(patch_chunks, patch_fd);
-}
-
-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;
- std::string split_info_file;
- std::string debug_dir;
-
- int opt;
- int option_index;
- 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:v", OPTIONS, &option_index)) !=
- -1) {
- switch (opt) {
- case 'z':
- zip_mode = true;
- break;
- case 'b': {
- android::base::unique_fd fd(open(optarg, O_RDONLY));
- if (fd == -1) {
- PLOG(ERROR) << "Failed to open bonus file " << optarg;
- return 1;
- }
- struct stat st;
- if (fstat(fd, &st) != 0) {
- 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)) {
- 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)) {
- LOG(ERROR) << "Failed to parse size blocks_limit: " << optarg;
- return 1;
- } else if (name == "split-info") {
- split_info_file = optarg;
- } else if (name == "debug-dir") {
- debug_dir = optarg;
- }
- break;
- }
- default:
- LOG(ERROR) << "unexpected opt: " << static_cast<char>(opt);
- return 2;
- }
- }
-
- if (!verbose) {
- android::base::SetMinimumLogSeverity(android::base::WARNING);
- }
-
- if (argc - optind != 3) {
- 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;
- }
-
- if (zip_mode) {
- ZipModeImage src_image(true, blocks_limit * BLOCK_SIZE);
- ZipModeImage tgt_image(false, blocks_limit * BLOCK_SIZE);
-
- if (!src_image.Initialize(argv[optind])) {
- return 1;
- }
- if (!tgt_image.Initialize(argv[optind + 1])) {
- return 1;
- }
-
- if (!ZipModeImage::CheckAndProcessChunks(&tgt_image, &src_image)) {
- return 1;
- }
-
- // 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()) {
- LOG(ERROR) << "split-info path cannot be empty when generating patches with a block-limit";
- return 1;
- }
-
- std::vector<ZipModeImage> split_tgt_images;
- std::vector<ZipModeImage> split_src_images;
- std::vector<SortedRangeSet> split_src_ranges;
- ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images,
- &split_src_images, &split_src_ranges);
-
- if (!ZipModeImage::GeneratePatches(split_tgt_images, split_src_images, split_src_ranges,
- argv[optind + 2], split_info_file, debug_dir)) {
- return 1;
- }
-
- } else if (!ZipModeImage::GeneratePatches(tgt_image, src_image, argv[optind + 2])) {
- return 1;
- }
- } else {
- ImageModeImage src_image(true);
- ImageModeImage tgt_image(false);
-
- if (!src_image.Initialize(argv[optind])) {
- return 1;
- }
- if (!tgt_image.Initialize(argv[optind + 1])) {
- return 1;
- }
-
- if (!ImageModeImage::CheckAndProcessChunks(&tgt_image, &src_image)) {
- return 1;
- }
-
- if (!bonus_data.empty() && !src_image.SetBonusData(bonus_data)) {
- return 1;
- }
-
- if (!ImageModeImage::GeneratePatches(tgt_image, src_image, argv[optind + 2])) {
- return 1;
- }
- }
-
- return 0;
-}
diff --git a/applypatch/imgdiff_main.cpp b/applypatch/imgdiff_main.cpp
deleted file mode 100644
index 7d5bdf9..0000000
--- a/applypatch/imgdiff_main.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "applypatch/imgdiff.h"
-
-int main(int argc, char** argv) {
- return imgdiff(argc, const_cast<const char**>(argv));
-}
diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp
deleted file mode 100644
index f4c33e5..0000000
--- a/applypatch/imgpatch.cpp
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// See imgdiff.cpp in this directory for a description of the patch file
-// format.
-
-#include <applypatch/imgpatch.h>
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/cdefs.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/logging.h>
-#include <android-base/memory.h>
-#include <applypatch/applypatch.h>
-#include <applypatch/imgdiff.h>
-#include <openssl/sha.h>
-#include <zlib.h>
-
-#include "edify/expr.h"
-#include "otautil/print_sha1.h"
-
-static inline int64_t Read8(const void *address) {
- return android::base::get_unaligned<int64_t>(address);
-}
-
-static inline int32_t Read4(const void *address) {
- return android::base::get_unaligned<int32_t>(address);
-}
-
-// 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 char* deflate_header, SinkFn sink) {
- size_t expected_target_length = static_cast<size_t>(Read8(deflate_header + 32));
- CHECK_GT(expected_target_length, static_cast<size_t>(0));
- int level = Read4(deflate_header + 40);
- int method = Read4(deflate_header + 44);
- int window_bits = Read4(deflate_header + 48);
- int mem_level = Read4(deflate_header + 52);
- int strategy = Read4(deflate_header + 56);
-
- z_stream strm;
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- strm.avail_in = 0;
- strm.next_in = nullptr;
- int ret = deflateInit2(&strm, level, method, window_bits, mem_level, strategy);
- if (ret != Z_OK) {
- LOG(ERROR) << "Failed to init uncompressed data deflation: " << ret;
- return false;
- }
-
- // Define a custom sink wrapper that feeds to bspatch. It deflates the available patch data on
- // the fly and outputs the compressed data to the given sink.
- size_t actual_target_length = 0;
- size_t total_written = 0;
- static constexpr size_t buffer_size = 32768;
- auto compression_sink = [&strm, &actual_target_length, &expected_target_length, &total_written,
- &ret, &sink](const uint8_t* data, size_t len) -> size_t {
- // The input patch length for an update never exceeds INT_MAX.
- strm.avail_in = len;
- strm.next_in = data;
- do {
- std::vector<uint8_t> buffer(buffer_size);
- strm.avail_out = buffer_size;
- strm.next_out = buffer.data();
- if (actual_target_length + len < expected_target_length) {
- ret = deflate(&strm, Z_NO_FLUSH);
- } else {
- ret = deflate(&strm, Z_FINISH);
- }
- if (ret != Z_OK && ret != Z_STREAM_END) {
- LOG(ERROR) << "Failed to deflate stream: " << ret;
- // zero length indicates an error in the sink function of bspatch().
- return 0;
- }
-
- size_t have = buffer_size - strm.avail_out;
- total_written += have;
- if (sink(buffer.data(), have) != have) {
- LOG(ERROR) << "Failed to write " << have << " compressed bytes to output.";
- return 0;
- }
- } while ((strm.avail_in != 0 || strm.avail_out == 0) && ret != Z_STREAM_END);
-
- actual_target_length += len;
- return len;
- };
-
- int bspatch_result = ApplyBSDiffPatch(src_data, src_len, patch, patch_offset, compression_sink);
- deflateEnd(&strm);
-
- if (bspatch_result != 0) {
- return false;
- }
-
- if (ret != Z_STREAM_END) {
- LOG(ERROR) << "ret is expected to be Z_STREAM_END, but it's " << ret;
- return false;
- }
-
- if (expected_target_length != actual_target_length) {
- LOG(ERROR) << "target length is expected to be " << expected_target_length << ", but it's "
- << actual_target_length;
- return false;
- }
- LOG(DEBUG) << "bspatch wrote " << total_written << " bytes in total to streaming output.";
-
- return true;
-}
-
-int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data,
- size_t patch_size, SinkFn sink) {
- Value patch(Value::Type::BLOB,
- std::string(reinterpret_cast<const char*>(patch_data), patch_size));
- return ApplyImagePatch(old_data, old_size, patch, sink, nullptr);
-}
-
-int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& patch, SinkFn sink,
- const Value* bonus_data) {
- 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.)
- 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(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()) {
- printf("failed to read chunk %d record\n", i);
- return -1;
- }
- int type = Read4(patch_header + pos);
- pos += 4;
-
- if (type == CHUNK_NORMAL) {
- const char* normal_header = patch_header + pos;
- pos += 24;
- if (pos > patch.data.size()) {
- printf("failed to read chunk %d normal header data\n", i);
- return -1;
- }
-
- size_t src_start = static_cast<size_t>(Read8(normal_header));
- size_t src_len = static_cast<size_t>(Read8(normal_header + 8));
- size_t patch_offset = static_cast<size_t>(Read8(normal_header + 16));
-
- if (src_start + src_len > old_size) {
- printf("source data too short\n");
- return -1;
- }
- if (ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink) != 0) {
- printf("Failed to apply bsdiff patch.\n");
- return -1;
- }
-
- LOG(DEBUG) << "Processed chunk type normal";
- } else if (type == CHUNK_RAW) {
- const char* raw_header = patch_header + pos;
- pos += 4;
- 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()) {
- printf("failed to read chunk %d raw data\n", i);
- return -1;
- }
- 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;
-
- LOG(DEBUG) << "Processed chunk type raw";
- } else if (type == CHUNK_DEFLATE) {
- // deflate chunks have an additional 60 bytes in their chunk header.
- const char* deflate_header = patch_header + pos;
- pos += 60;
- if (pos > patch.data.size()) {
- printf("failed to read chunk %d deflate header data\n", i);
- return -1;
- }
-
- size_t src_start = static_cast<size_t>(Read8(deflate_header));
- size_t src_len = static_cast<size_t>(Read8(deflate_header + 8));
- size_t patch_offset = static_cast<size_t>(Read8(deflate_header + 16));
- size_t expanded_len = static_cast<size_t>(Read8(deflate_header + 24));
-
- if (src_start + src_len > old_size) {
- printf("source data too short\n");
- return -1;
- }
-
- // Decompress the source data; the chunk header tells us exactly
- // how big we expect it to be when decompressed.
-
- // Note: expanded_len will include the bonus data size if the patch was constructed with
- // bonus data. The deflation will come up 'bonus_size' bytes short; these must be appended
- // from the bonus_data value.
- size_t bonus_size = (i == 1 && bonus_data != nullptr) ? bonus_data->data.size() : 0;
-
- std::vector<unsigned char> expanded_source(expanded_len);
-
- // inflate() doesn't like strm.next_out being a nullptr even with
- // avail_out being zero (Z_STREAM_ERROR).
- if (expanded_len != 0) {
- z_stream strm;
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- strm.avail_in = src_len;
- strm.next_in = old_data + src_start;
- strm.avail_out = expanded_len;
- strm.next_out = expanded_source.data();
-
- int ret = inflateInit2(&strm, -15);
- if (ret != Z_OK) {
- printf("failed to init source inflation: %d\n", ret);
- return -1;
- }
-
- // Because we've provided enough room to accommodate the output
- // data, we expect one call to inflate() to suffice.
- ret = inflate(&strm, Z_SYNC_FLUSH);
- if (ret != Z_STREAM_END) {
- printf("source inflation returned %d\n", ret);
- return -1;
- }
- // We should have filled the output buffer exactly, except
- // for the bonus_size.
- if (strm.avail_out != bonus_size) {
- printf("source inflation short by %zu bytes\n", strm.avail_out - bonus_size);
- return -1;
- }
- inflateEnd(&strm);
-
- if (bonus_size) {
- memcpy(expanded_source.data() + (expanded_len - bonus_size), bonus_data->data.data(),
- bonus_size);
- }
- }
-
- if (!ApplyBSDiffPatchAndStreamOutput(expanded_source.data(), expanded_len, patch,
- patch_offset, deflate_header, sink)) {
- LOG(ERROR) << "Fail to apply streaming bspatch.";
- return -1;
- }
-
- LOG(DEBUG) << "Processed chunk type deflate";
- } else {
- printf("patch chunk %d is unknown type %d\n", i, type);
- return -1;
- }
- }
-
- return 0;
-}
diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h
deleted file mode 100644
index 799f4b2..0000000
--- a/applypatch/include/applypatch/applypatch.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _APPLYPATCH_H
-#define _APPLYPATCH_H
-
-#include <stdint.h>
-
-#include <functional>
-#include <memory>
-#include <ostream>
-#include <string>
-#include <vector>
-
-#include <openssl/sha.h>
-
-// Forward declaration to avoid including "edify/expr.h" in the header.
-struct Value;
-
-struct FileContents {
- uint8_t sha1[SHA_DIGEST_LENGTH];
- std::vector<unsigned char> data;
-};
-
-using SinkFn = std::function<size_t(const unsigned char*, size_t)>;
-
-// applypatch.cpp
-
-int ShowLicenses();
-
-// Parses a given string of 40 hex digits into 20-byte array 'digest'. 'str' may contain only the
-// digest or be of the form "<digest>:<anything>". Returns 0 on success, or -1 on any error.
-int ParseSha1(const std::string& str, uint8_t* digest);
-
-struct Partition {
- Partition() = default;
-
- Partition(const std::string& name, size_t size, const std::string& hash)
- : name(name), size(size), hash(hash) {}
-
- // Parses and returns the given string into a Partition object. The input string is of the form
- // "EMMC:<device>:<size>:<hash>". Returns the parsed Partition, or an empty object on error.
- static Partition Parse(const std::string& partition, std::string* err);
-
- std::string ToString() const;
-
- // Returns whether the current Partition object is valid.
- explicit operator bool() const {
- return !name.empty();
- }
-
- std::string name;
- size_t size;
- std::string hash;
-};
-
-std::ostream& operator<<(std::ostream& os, const Partition& partition);
-
-// Applies the given 'patch' to the 'source' Partition, verifies then writes the patching result to
-// the 'target' Partition. While patching, it will backup the data on the source partition to
-// /cache, so that the patching could be resumed on interruption even if both of the source and
-// target partitions refer to the same device. The function is idempotent if called multiple times.
-// 'bonus' can be provided if the patch was generated with a bonus output, or nullptr.
-// 'backup_source' indicates whether the source partition should be backed up prior to the update
-// (e.g. when doing in-place update). Returns the patching result.
-bool PatchPartition(const Partition& target, const Partition& source, const Value& patch,
- const Value* bonus, bool backup_source);
-
-// Returns whether the contents of the eMMC target or the cached file match the embedded hash.
-// It will look for the backup on /cache if the given partition doesn't match the checksum.
-bool PatchPartitionCheck(const Partition& target, const Partition& source);
-
-// Checks whether the contents of the given partition has the desired hash. It will NOT look for
-// the backup on /cache if the given partition doesn't have the expected checksum.
-bool CheckPartition(const Partition& target);
-
-// Flashes a given image in 'source_filename' to the eMMC target partition. It verifies the target
-// checksum first, and will return if target already has the desired hash. Otherwise it checks the
-// checksum of the given source image, flashes, and verifies the target partition afterwards. The
-// function is idempotent. Returns the flashing result.
-bool FlashPartition(const Partition& target, const std::string& source_filename);
-
-// Reads a file into memory; stores the file contents and associated metadata in *file.
-bool LoadFileContents(const std::string& filename, FileContents* file);
-
-// Saves the given FileContents object to the given filename.
-bool SaveFileContents(const std::string& filename, const FileContents* file);
-
-// bspatch.cpp
-
-void ShowBSDiffLicense();
-
-// 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'. Returns
-// 0 on success.
-int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value& patch,
- size_t patch_offset, SinkFn sink);
-
-// imgpatch.cpp
-
-// 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'. Returns 0 on
-// success.
-int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& patch, SinkFn sink,
- const Value* bonus_data);
-
-// freecache.cpp
-
-// Checks whether /cache partition has at least 'bytes'-byte free space. Returns true immediately
-// if so. Otherwise, it will try to free some space by removing older logs, checks again and
-// returns the checking result.
-bool CheckAndFreeSpaceOnCache(size_t bytes);
-
-// Removes the files in |dirname| until we have at least |bytes_needed| bytes of free space on the
-// partition. |space_checker| should return the size of the free space, or -1 on error.
-bool RemoveFilesInDirectory(size_t bytes_needed, const std::string& dirname,
- const std::function<int64_t(const std::string&)>& space_checker);
-#endif
diff --git a/applypatch/include/applypatch/imgdiff.h b/applypatch/include/applypatch/imgdiff.h
deleted file mode 100644
index 22cbd4f..0000000
--- a/applypatch/include/applypatch/imgdiff.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _APPLYPATCH_IMGDIFF_H
-#define _APPLYPATCH_IMGDIFF_H
-
-#include <stddef.h>
-
-// Image patch chunk types
-#define CHUNK_NORMAL 0
-#define CHUNK_GZIP 1 // version 1 only
-#define CHUNK_DEFLATE 2 // version 2 only
-#define CHUNK_RAW 3 // version 2 only
-
-// The gzip header size is actually variable, but we currently don't
-// support gzipped data with any of the optional fields, so for now it
-// will always be ten bytes. See RFC 1952 for the definition of the
-// gzip format.
-static constexpr size_t GZIP_HEADER_LEN = 10;
-
-// The gzip footer size really is fixed.
-static constexpr size_t GZIP_FOOTER_LEN = 8;
-
-int imgdiff(int argc, const char** argv);
-
-#endif // _APPLYPATCH_IMGDIFF_H
diff --git a/applypatch/include/applypatch/imgdiff_image.h b/applypatch/include/applypatch/imgdiff_image.h
deleted file mode 100644
index b579e56..0000000
--- a/applypatch/include/applypatch/imgdiff_image.h
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _APPLYPATCH_IMGDIFF_IMAGE_H
-#define _APPLYPATCH_IMGDIFF_IMAGE_H
-
-#include <stddef.h>
-#include <stdio.h>
-#include <sys/types.h>
-
-#include <string>
-#include <vector>
-
-#include <bsdiff/bsdiff.h>
-#include <ziparchive/zip_archive.h>
-#include <zlib.h>
-
-#include "imgdiff.h"
-#include "otautil/rangeset.h"
-
-class ImageChunk {
- public:
- static constexpr auto WINDOWBITS = -15; // 32kb window; negative to indicate a raw stream.
- static constexpr auto MEMLEVEL = 8; // the default value.
- static constexpr auto METHOD = Z_DEFLATED;
- static constexpr auto STRATEGY = Z_DEFAULT_STRATEGY;
-
- ImageChunk(int type, size_t start, const std::vector<uint8_t>* file_content, size_t raw_data_len,
- std::string entry_name = {});
-
- int GetType() const {
- return type_;
- }
-
- const uint8_t* GetRawData() const;
- size_t GetRawDataLength() const {
- return raw_data_len_;
- }
- const std::string& GetEntryName() const {
- return entry_name_;
- }
- size_t GetStartOffset() const {
- return start_;
- }
- int GetCompressLevel() const {
- return compress_level_;
- }
-
- // CHUNK_DEFLATE will return the uncompressed data for diff, while other types will simply return
- // the raw data.
- const uint8_t* DataForPatch() const;
- size_t DataLengthForPatch() const;
-
- void Dump(size_t index) const;
-
- void SetUncompressedData(std::vector<uint8_t> data);
- bool SetBonusData(const std::vector<uint8_t>& bonus_data);
-
- bool operator==(const ImageChunk& other) const;
- bool operator!=(const ImageChunk& other) const {
- return !(*this == other);
- }
-
- /*
- * Cause a gzip chunk to be treated as a normal chunk (ie, as a blob of uninterpreted data).
- * The resulting patch will likely be about as big as the target file, but it lets us handle
- * the case of images where some gzip chunks are reconstructible but others aren't (by treating
- * the ones that aren't as normal chunks).
- */
- void ChangeDeflateChunkToNormal();
-
- /*
- * Verify that we can reproduce exactly the same compressed data that we started with. Sets the
- * level, method, windowBits, memLevel, and strategy fields in the chunk to the encoding
- * parameters needed to produce the right output.
- */
- bool ReconstructDeflateChunk();
- bool IsAdjacentNormal(const ImageChunk& other) const;
- void MergeAdjacentNormal(const ImageChunk& other);
-
- /*
- * Compute a bsdiff patch between |src| and |tgt|; Store the result in the patch_data.
- * |bsdiff_cache| can be used to cache the suffix array if the same |src| chunk is used
- * repeatedly, pass nullptr if not needed.
- */
- static bool MakePatch(const ImageChunk& tgt, const ImageChunk& src,
- std::vector<uint8_t>* patch_data,
- bsdiff::SuffixArrayIndexInterface** bsdiff_cache);
-
- private:
- bool TryReconstruction(int level);
-
- int type_; // CHUNK_NORMAL, CHUNK_DEFLATE, CHUNK_RAW
- size_t start_; // offset of chunk in the original input file
- const std::vector<uint8_t>* input_file_ptr_; // ptr to the full content of original input file
- size_t raw_data_len_;
-
- // deflate encoder parameters
- int compress_level_;
-
- // --- for CHUNK_DEFLATE chunks only: ---
- std::vector<uint8_t> uncompressed_data_;
- std::string entry_name_; // used for zip entries
-};
-
-// PatchChunk stores the patch data between a source chunk and a target chunk. It also keeps track
-// of the metadata of src&tgt chunks (e.g. offset, raw data length, uncompressed data length).
-class PatchChunk {
- public:
- PatchChunk(const ImageChunk& tgt, const ImageChunk& src, std::vector<uint8_t> data);
-
- // Construct a CHUNK_RAW patch from the target data directly.
- explicit PatchChunk(const ImageChunk& tgt);
-
- // Return true if raw data size is smaller than the patch size.
- static bool RawDataIsSmaller(const ImageChunk& tgt, size_t patch_size);
-
- // Update the source start with the new offset within the source range.
- void UpdateSourceOffset(const SortedRangeSet& src_range);
-
- // Return the total size (header + data) of the patch.
- size_t PatchSize() const;
-
- static bool WritePatchDataToFd(const std::vector<PatchChunk>& patch_chunks, int patch_fd);
-
- private:
- size_t GetHeaderSize() 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.
- int type_;
-
- size_t source_start_;
- size_t source_len_;
- size_t source_uncompressed_len_;
-
- size_t target_start_; // offset of the target chunk within the target file
- size_t target_len_;
- size_t target_uncompressed_len_;
- size_t target_compress_level_; // the deflate compression level of the target chunk.
-
- std::vector<uint8_t> data_; // storage for the patch data
-};
-
-// Interface for zip_mode and image_mode images. We initialize the image from an input file and
-// split the file content into a list of image chunks.
-class Image {
- public:
- explicit Image(bool is_source) : is_source_(is_source) {}
-
- virtual ~Image() {}
-
- // Create a list of image chunks from input file.
- virtual bool Initialize(const std::string& filename) = 0;
-
- // Look for runs of adjacent normal chunks and compress them down into a single chunk. (Such
- // runs can be produced when deflate chunks are changed to normal chunks.)
- void MergeAdjacentNormalChunks();
-
- void DumpChunks() const;
-
- // Non const iterators to access the stored ImageChunks.
- std::vector<ImageChunk>::iterator begin() {
- return chunks_.begin();
- }
-
- std::vector<ImageChunk>::iterator end() {
- return chunks_.end();
- }
-
- std::vector<ImageChunk>::const_iterator cbegin() const {
- return chunks_.cbegin();
- }
-
- std::vector<ImageChunk>::const_iterator cend() const {
- return chunks_.cend();
- }
-
- ImageChunk& operator[](size_t i);
- const ImageChunk& operator[](size_t i) const;
-
- size_t NumOfChunks() const {
- return chunks_.size();
- }
-
- protected:
- bool ReadFile(const std::string& filename, std::vector<uint8_t>* file_content);
-
- bool is_source_; // True if it's for source chunks.
- std::vector<ImageChunk> chunks_; // Internal storage of ImageChunk.
- std::vector<uint8_t> file_content_; // Store the whole input file in memory.
-};
-
-class ZipModeImage : public Image {
- public:
- explicit ZipModeImage(bool is_source, size_t limit = 0) : Image(is_source), limit_(limit) {}
-
- bool Initialize(const std::string& filename) override;
-
- // Initialize a fake ZipModeImage from an existing ImageChunk vector. For src img pieces, we
- // reconstruct a new file_content based on the source ranges; but it's not needed for the tgt img
- // pieces; because for each chunk both the data and their offset within the file are unchanged.
- void Initialize(const std::vector<ImageChunk>& chunks, const std::vector<uint8_t>& file_content) {
- chunks_ = chunks;
- file_content_ = file_content;
- }
-
- // The pesudo source chunk for bsdiff if there's no match for the given target chunk. It's in
- // fact the whole source file.
- ImageChunk PseudoSource() const;
-
- // Find the matching deflate source chunk by entry name. Search for normal chunks also if
- // |find_normal| is true.
- ImageChunk* FindChunkByName(const std::string& name, bool find_normal = false);
-
- const ImageChunk* FindChunkByName(const std::string& name, bool find_normal = false) const;
-
- // Verify that we can reconstruct the deflate chunks; also change the type to CHUNK_NORMAL if
- // src and tgt are identical.
- static bool CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage* src_image);
-
- // Compute the patch between tgt & src images, and write the data into |patch_name|.
- static bool GeneratePatches(const ZipModeImage& tgt_image, const ZipModeImage& src_image,
- const std::string& patch_name);
-
- // Compute the patch based on the lists of split src and tgt images. Generate patches for each
- // pair of split pieces and write the data to |patch_name|. If |debug_dir| is specified, write
- // each split src data and patch data into that directory.
- static bool GeneratePatches(const std::vector<ZipModeImage>& split_tgt_images,
- const std::vector<ZipModeImage>& split_src_images,
- const std::vector<SortedRangeSet>& split_src_ranges,
- const std::string& patch_name, const std::string& split_info_file,
- const std::string& debug_dir);
-
- // Split the tgt chunks and src chunks based on the size limit.
- static bool SplitZipModeImageWithLimit(const ZipModeImage& tgt_image,
- const ZipModeImage& src_image,
- std::vector<ZipModeImage>* split_tgt_images,
- std::vector<ZipModeImage>* split_src_images,
- std::vector<SortedRangeSet>* split_src_ranges);
-
- private:
- // Initialize image chunks based on the zip entries.
- bool InitializeChunks(const std::string& filename, ZipArchiveHandle handle);
- // Add the a zip entry to the list.
- bool AddZipEntryToChunks(ZipArchiveHandle handle, const std::string& entry_name,
- ZipEntry64* entry);
- // Return the real size of the zip file. (omit the trailing zeros that used for alignment)
- bool GetZipFileSize(size_t* input_file_size);
-
- static void ValidateSplitImages(const std::vector<ZipModeImage>& split_tgt_images,
- const std::vector<ZipModeImage>& split_src_images,
- std::vector<SortedRangeSet>& split_src_ranges,
- size_t total_tgt_size);
- // Construct the fake split images based on the chunks info and source ranges; and move them into
- // the given vectors. Return true if we add a new split image into |split_tgt_images|, and
- // false otherwise.
- static bool AddSplitImageFromChunkList(const ZipModeImage& tgt_image,
- const ZipModeImage& src_image,
- const SortedRangeSet& split_src_ranges,
- const std::vector<ImageChunk>& split_tgt_chunks,
- const std::vector<ImageChunk>& split_src_chunks,
- std::vector<ZipModeImage>* split_tgt_images,
- std::vector<ZipModeImage>* split_src_images);
-
- // Function that actually iterates the tgt_chunks and makes patches.
- static bool GeneratePatchesInternal(const ZipModeImage& tgt_image, const ZipModeImage& src_image,
- std::vector<PatchChunk>* patch_chunks);
-
- // size limit in bytes of each chunk. Also, if the length of one zip_entry exceeds the limit,
- // we'll split that entry into several smaller chunks in advance.
- size_t limit_;
-};
-
-class ImageModeImage : public Image {
- public:
- explicit ImageModeImage(bool is_source) : Image(is_source) {}
-
- // Initialize the image chunks list by searching the magic numbers in an image file.
- bool Initialize(const std::string& filename) override;
-
- bool SetBonusData(const std::vector<uint8_t>& bonus_data);
-
- // In Image Mode, verify that the source and target images have the same chunk structure (ie, the
- // same sequence of deflate and normal chunks).
- static bool CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeImage* src_image);
-
- // In image mode, generate patches against the given source chunks and bonus_data; write the
- // result to |patch_name|.
- static bool GeneratePatches(const ImageModeImage& tgt_image, const ImageModeImage& src_image,
- const std::string& patch_name);
-};
-
-#endif // _APPLYPATCH_IMGDIFF_IMAGE_H
diff --git a/applypatch/include/applypatch/imgpatch.h b/applypatch/include/applypatch/imgpatch.h
deleted file mode 100644
index 07c6609..0000000
--- a/applypatch/include/applypatch/imgpatch.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _APPLYPATCH_IMGPATCH_H
-#define _APPLYPATCH_IMGPATCH_H
-
-#include <sys/types.h>
-
-#include <functional>
-
-using SinkFn = std::function<size_t(const unsigned char*, size_t)>;
-
-int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data,
- size_t patch_size, SinkFn sink);
-
-#endif // _APPLYPATCH_IMGPATCH_H
diff --git a/applypatch/vendor_flash_recovery.rc b/applypatch/vendor_flash_recovery.rc
deleted file mode 100644
index a6003be..0000000
--- a/applypatch/vendor_flash_recovery.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-service vendor_flash_recovery /vendor/bin/install-recovery.sh
- class main
- oneshot
- user root
diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp
index 2d743e7..f010a14 100644
--- a/bootloader_message/bootloader_message.cpp
+++ b/bootloader_message/bootloader_message.cpp
@@ -27,6 +27,7 @@
#include <android-base/file.h>
#include <android-base/hex.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
@@ -175,6 +176,7 @@
bool clear_bootloader_message(std::string* err) {
bootloader_message boot = {};
+ LOG(INFO) << "Clearing BCB";
return write_bootloader_message(boot, err);
}
@@ -194,11 +196,12 @@
}
bool update_bootloader_message(const std::vector<std::string>& options, std::string* err) {
- bootloader_message boot;
+ bootloader_message boot{};
if (!read_bootloader_message(&boot, err)) {
return false;
}
update_bootloader_message_in_struct(&boot, options);
+ LOG(INFO) << "Writing BCB " << boot.command << " " << boot.recovery;
return write_bootloader_message(boot, err);
}
@@ -224,7 +227,7 @@
}
bool write_reboot_bootloader(std::string* err) {
- bootloader_message boot;
+ bootloader_message boot{};
if (!read_bootloader_message(&boot, err)) {
return false;
}
@@ -233,6 +236,7 @@
return false;
}
strlcpy(boot.command, "bootonce-bootloader", sizeof(boot.command));
+ LOG(INFO) << "Writing BCB cmd: " << boot.command << " args: " << boot.recovery;
return write_bootloader_message(boot, err);
}
diff --git a/edify/Android.bp b/edify/Android.bp
deleted file mode 100644
index 62ff911..0000000
--- a/edify/Android.bp
+++ /dev/null
@@ -1,56 +0,0 @@
-// 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.
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "bootable_recovery_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["bootable_recovery_license"],
-}
-
-cc_library_static {
- name: "libedify",
-
- host_supported: true,
- vendor_available: true,
- recovery_available: true,
-
- srcs: [
- "expr.cpp",
- "lexer.ll",
- "parser.yy",
- ],
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-deprecated-register",
- "-Wno-unused-parameter",
- ],
-
- export_include_dirs: [
- "include",
- ],
-
- local_include_dirs: [
- "include",
- ],
-
- static_libs: [
- "libbase",
- "libotautil",
- ],
-}
diff --git a/edify/README.md b/edify/README.md
deleted file mode 100644
index b3330e2..0000000
--- a/edify/README.md
+++ /dev/null
@@ -1,111 +0,0 @@
-edify
-=====
-
-Update scripts (from donut onwards) are written in a new little
-scripting language ("edify") that is superficially somewhat similar to
-the old one ("amend"). This is a brief overview of the new language.
-
-- The entire script is a single expression.
-
-- All expressions are string-valued.
-
-- String literals appear in double quotes. \n, \t, \", and \\ are
- understood, as are hexadecimal escapes like \x4a.
-
-- String literals consisting of only letters, numbers, colons,
- underscores, slashes, and periods don't need to be in double quotes.
-
-- The following words are reserved:
-
- if then else endif
-
- They have special meaning when unquoted. (In quotes, they are just
- string literals.)
-
-- When used as a boolean, the empty string is "false" and all other
- strings are "true".
-
-- All functions are actually macros (in the Lisp sense); the body of
- the function can control which (if any) of the arguments are
- evaluated. This means that functions can act as control
- structures.
-
-- Operators (like "&&" and "||") are just syntactic sugar for builtin
- functions, so they can act as control structures as well.
-
-- ";" is a binary operator; evaluating it just means to first evaluate
- the left side, then the right. It can also appear after any
- expression.
-
-- Comments start with "#" and run to the end of the line.
-
-
-
-Some examples:
-
-- There's no distinction between quoted and unquoted strings; the
- quotes are only needed if you want characters like whitespace to
- appear in the string. The following expressions all evaluate to the
- same string.
-
- "a b"
- a + " " + b
- "a" + " " + "b"
- "a\x20b"
- a + "\x20b"
- concat(a, " ", "b")
- "concat"(a, " ", "b")
-
- As shown in the last example, function names are just strings,
- too. They must be string *literals*, however. This is not legal:
-
- ("con" + "cat")(a, " ", b) # syntax error!
-
-
-- The ifelse() builtin takes three arguments: it evaluates exactly
- one of the second and third, depending on whether the first one is
- true. There is also some syntactic sugar to make expressions that
- look like if/else statements:
-
- # these are all equivalent
- ifelse(something(), "yes", "no")
- if something() then yes else no endif
- if something() then "yes" else "no" endif
-
- The else part is optional.
-
- if something() then "yes" endif # if something() is false,
- # evaluates to false
-
- ifelse(condition(), "", abort()) # abort() only called if
- # condition() is false
-
- The last example is equivalent to:
-
- assert(condition())
-
-
-- The && and || operators can be used similarly; they evaluate their
- second argument only if it's needed to determine the truth of the
- expression. Their value is the value of the last-evaluated
- argument:
-
- file_exists("/data/system/bad") && delete("/data/system/bad")
-
- file_exists("/data/system/missing") || create("/data/system/missing")
-
- get_it() || "xxx" # returns value of get_it() if that value is
- # true, otherwise returns "xxx"
-
-
-- The purpose of ";" is to simulate imperative statements, of course,
- but the operator can be used anywhere. Its value is the value of
- its right side:
-
- concat(a;b;c, d, e;f) # evaluates to "cdf"
-
- A more useful example might be something like:
-
- ifelse(condition(),
- (first_step(); second_step();), # second ; is optional
- alternative_procedure())
diff --git a/edify/expr.cpp b/edify/expr.cpp
deleted file mode 100644
index e5e0e24..0000000
--- a/edify/expr.cpp
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "edify/expr.h"
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include <android-base/parseint.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-
-#include "otautil/error_code.h"
-
-// Functions should:
-//
-// - return a malloc()'d string
-// - if Evaluate() on any argument returns nullptr, return nullptr.
-
-static bool BooleanString(const std::string& s) {
- return !s.empty();
-}
-
-bool Evaluate(State* state, const std::unique_ptr<Expr>& expr, std::string* result) {
- if (result == nullptr) {
- return false;
- }
-
- std::unique_ptr<Value> v(expr->fn(expr->name.c_str(), state, expr->argv));
- if (!v) {
- return false;
- }
- if (v->type != Value::Type::STRING) {
- ErrorAbort(state, kArgsParsingFailure, "expecting string, got value type %d", v->type);
- return false;
- }
-
- *result = v->data;
- return true;
-}
-
-Value* EvaluateValue(State* state, const std::unique_ptr<Expr>& expr) {
- return expr->fn(expr->name.c_str(), state, expr->argv);
-}
-
-Value* StringValue(const char* str) {
- if (str == nullptr) {
- return nullptr;
- }
- return new Value(Value::Type::STRING, str);
-}
-
-Value* StringValue(const std::string& str) {
- return StringValue(str.c_str());
-}
-
-Value* ConcatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.empty()) {
- return StringValue("");
- }
- std::string result;
- for (size_t i = 0; i < argv.size(); ++i) {
- std::string str;
- if (!Evaluate(state, argv[i], &str)) {
- return nullptr;
- }
- result += str;
- }
-
- return StringValue(result);
-}
-
-Value* IfElseFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 2 && argv.size() != 3) {
- state->errmsg = "ifelse expects 2 or 3 arguments";
- return nullptr;
- }
-
- std::string cond;
- if (!Evaluate(state, argv[0], &cond)) {
- return nullptr;
- }
-
- if (!cond.empty()) {
- return EvaluateValue(state, argv[1]);
- } else if (argv.size() == 3) {
- return EvaluateValue(state, argv[2]);
- }
-
- return StringValue("");
-}
-
-Value* AbortFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- std::string msg;
- if (!argv.empty() && Evaluate(state, argv[0], &msg)) {
- state->errmsg += msg;
- } else {
- state->errmsg += "called abort()";
- }
- return nullptr;
-}
-
-Value* AssertFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- for (size_t i = 0; i < argv.size(); ++i) {
- std::string result;
- if (!Evaluate(state, argv[i], &result)) {
- return nullptr;
- }
- if (result.empty()) {
- int len = argv[i]->end - argv[i]->start;
- state->errmsg = "assert failed: " + state->script.substr(argv[i]->start, len);
- return nullptr;
- }
- }
- return StringValue("");
-}
-
-Value* SleepFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- std::string val;
- if (!Evaluate(state, argv[0], &val)) {
- return nullptr;
- }
-
- int v;
- if (!android::base::ParseInt(val.c_str(), &v, 0)) {
- return nullptr;
- }
- sleep(v);
-
- return StringValue(val);
-}
-
-Value* StdoutFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- for (size_t i = 0; i < argv.size(); ++i) {
- std::string v;
- if (!Evaluate(state, argv[i], &v)) {
- return nullptr;
- }
- fputs(v.c_str(), stdout);
- }
- return StringValue("");
-}
-
-Value* LogicalAndFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- std::string left;
- if (!Evaluate(state, argv[0], &left)) {
- return nullptr;
- }
- if (BooleanString(left)) {
- return EvaluateValue(state, argv[1]);
- } else {
- return StringValue("");
- }
-}
-
-Value* LogicalOrFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- std::string left;
- if (!Evaluate(state, argv[0], &left)) {
- return nullptr;
- }
- if (!BooleanString(left)) {
- return EvaluateValue(state, argv[1]);
- } else {
- return StringValue(left);
- }
-}
-
-Value* LogicalNotFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- std::string val;
- if (!Evaluate(state, argv[0], &val)) {
- return nullptr;
- }
-
- return StringValue(BooleanString(val) ? "" : "t");
-}
-
-Value* SubstringFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- std::string needle;
- if (!Evaluate(state, argv[0], &needle)) {
- return nullptr;
- }
-
- std::string haystack;
- if (!Evaluate(state, argv[1], &haystack)) {
- return nullptr;
- }
-
- std::string result = (haystack.find(needle) != std::string::npos) ? "t" : "";
- return StringValue(result);
-}
-
-Value* EqualityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- std::string left;
- if (!Evaluate(state, argv[0], &left)) {
- return nullptr;
- }
- std::string right;
- if (!Evaluate(state, argv[1], &right)) {
- return nullptr;
- }
-
- const char* result = (left == right) ? "t" : "";
- return StringValue(result);
-}
-
-Value* InequalityFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- std::string left;
- if (!Evaluate(state, argv[0], &left)) {
- return nullptr;
- }
- std::string right;
- if (!Evaluate(state, argv[1], &right)) {
- return nullptr;
- }
-
- const char* result = (left != right) ? "t" : "";
- return StringValue(result);
-}
-
-Value* SequenceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- std::unique_ptr<Value> left(EvaluateValue(state, argv[0]));
- if (!left) {
- return nullptr;
- }
- return EvaluateValue(state, argv[1]);
-}
-
-Value* LessThanIntFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 2) {
- state->errmsg = "less_than_int expects 2 arguments";
- return nullptr;
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return nullptr;
- }
-
- // Parse up to at least long long or 64-bit integers.
- int64_t l_int;
- if (!android::base::ParseInt(args[0].c_str(), &l_int)) {
- state->errmsg = "failed to parse int in " + args[0];
- return nullptr;
- }
-
- int64_t r_int;
- if (!android::base::ParseInt(args[1].c_str(), &r_int)) {
- state->errmsg = "failed to parse int in " + args[1];
- return nullptr;
- }
-
- return StringValue(l_int < r_int ? "t" : "");
-}
-
-Value* GreaterThanIntFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 2) {
- state->errmsg = "greater_than_int expects 2 arguments";
- return nullptr;
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return nullptr;
- }
-
- // Parse up to at least long long or 64-bit integers.
- int64_t l_int;
- if (!android::base::ParseInt(args[0].c_str(), &l_int)) {
- state->errmsg = "failed to parse int in " + args[0];
- return nullptr;
- }
-
- int64_t r_int;
- if (!android::base::ParseInt(args[1].c_str(), &r_int)) {
- state->errmsg = "failed to parse int in " + args[1];
- return nullptr;
- }
-
- return StringValue(l_int > r_int ? "t" : "");
-}
-
-Value* Literal(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- return StringValue(name);
-}
-
-// -----------------------------------------------------------------
-// the function table
-// -----------------------------------------------------------------
-
-static std::unordered_map<std::string, Function> fn_table;
-
-void RegisterFunction(const std::string& name, Function fn) {
- fn_table[name] = fn;
-}
-
-Function FindFunction(const std::string& name) {
- if (fn_table.find(name) == fn_table.end()) {
- return nullptr;
- } else {
- return fn_table[name];
- }
-}
-
-void RegisterBuiltins() {
- RegisterFunction("ifelse", IfElseFn);
- RegisterFunction("abort", AbortFn);
- RegisterFunction("assert", AssertFn);
- RegisterFunction("concat", ConcatFn);
- RegisterFunction("is_substring", SubstringFn);
- RegisterFunction("stdout", StdoutFn);
- RegisterFunction("sleep", SleepFn);
-
- RegisterFunction("less_than_int", LessThanIntFn);
- RegisterFunction("greater_than_int", GreaterThanIntFn);
-}
-
-
-// -----------------------------------------------------------------
-// convenience methods for functions
-// -----------------------------------------------------------------
-
-// Evaluate the expressions in argv, and put the results of strings in args. If any expression
-// evaluates to nullptr, return false. Return true on success.
-bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
- std::vector<std::string>* args) {
- return ReadArgs(state, argv, args, 0, argv.size());
-}
-
-bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
- std::vector<std::string>* args, size_t start, size_t len) {
- if (args == nullptr) {
- return false;
- }
- if (start + len > argv.size()) {
- return false;
- }
- for (size_t i = start; i < start + len; ++i) {
- std::string var;
- if (!Evaluate(state, argv[i], &var)) {
- args->clear();
- return false;
- }
- args->push_back(var);
- }
- return true;
-}
-
-// Evaluate the expressions in argv, and put the results of Value* in args. If any expression
-// evaluate to nullptr, return false. Return true on success.
-bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
- std::vector<std::unique_ptr<Value>>* args) {
- return ReadValueArgs(state, argv, args, 0, argv.size());
-}
-
-bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
- std::vector<std::unique_ptr<Value>>* args, size_t start, size_t len) {
- if (args == nullptr) {
- return false;
- }
- if (len == 0 || start + len > argv.size()) {
- return false;
- }
- for (size_t i = start; i < start + len; ++i) {
- std::unique_ptr<Value> v(EvaluateValue(state, argv[i]));
- if (!v) {
- args->clear();
- return false;
- }
- args->push_back(std::move(v));
- }
- return true;
-}
-
-// Use printf-style arguments to compose an error message to put into
-// *state. Returns nullptr.
-Value* ErrorAbort(State* state, const char* format, ...) {
- va_list ap;
- va_start(ap, format);
- android::base::StringAppendV(&state->errmsg, format, ap);
- va_end(ap);
- return nullptr;
-}
-
-Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) {
- std::string err_message;
- va_list ap;
- va_start(ap, format);
- android::base::StringAppendV(&err_message, format, ap);
- va_end(ap);
- // Ensure that there's exactly one line break at the end of the error message.
- state->errmsg = android::base::Trim(err_message) + "\n";
- state->cause_code = cause_code;
- return nullptr;
-}
-
-State::State(const std::string& script, UpdaterInterface* interface)
- : script(script), updater(interface), error_code(kNoError), cause_code(kNoCause) {}
diff --git a/edify/include/edify/expr.h b/edify/include/edify/expr.h
deleted file mode 100644
index 3ddf7f5..0000000
--- a/edify/include/edify/expr.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _EXPRESSION_H
-#define _EXPRESSION_H
-
-#include <unistd.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "edify/updater_interface.h"
-
-// Forward declaration to avoid including "otautil/error_code.h".
-enum ErrorCode : int;
-enum CauseCode : int;
-
-struct State {
- State(const std::string& script, UpdaterInterface* cookie);
-
- // The source of the original script.
- const std::string& script;
-
- // A pointer to app-specific data; the libedify doesn't use this value.
- UpdaterInterface* updater;
-
- // The error message (if any) returned if the evaluation aborts.
- // Should be empty initially, will be either empty or a string that
- // Evaluate() returns.
- std::string errmsg;
-
- // error code indicates the type of failure (e.g. failure to update system image)
- // during the OTA process.
- ErrorCode error_code;
-
- // cause code provides more detailed reason of an OTA failure (e.g. fsync error)
- // in addition to the error code.
- CauseCode cause_code;
-
- bool is_retry = false;
-};
-
-struct Value {
- enum class Type {
- STRING = 1,
- BLOB = 2,
- };
-
- Value(Type type, std::string str) : type(type), data(std::move(str)) {}
-
- Type type;
- std::string data;
-};
-
-struct Expr;
-
-using Function = Value* (*)(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv);
-
-struct Expr {
- Function fn;
- std::string name;
- std::vector<std::unique_ptr<Expr>> argv;
- int start, end;
-
- Expr(Function fn, const std::string& name, int start, int end) :
- fn(fn),
- name(name),
- start(start),
- end(end) {}
-};
-
-// Evaluate the input expr, return the resulting Value.
-Value* EvaluateValue(State* state, const std::unique_ptr<Expr>& expr);
-
-// Evaluate the input expr, assert that it is a string, and update the result parameter. This
-// function returns true if the evaluation succeeds. This is a convenience function for older
-// functions that want to deal only with strings.
-bool Evaluate(State* state, const std::unique_ptr<Expr>& expr, std::string* result);
-
-// Glue to make an Expr out of a literal.
-Value* Literal(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
-
-// Functions corresponding to various syntactic sugar operators.
-// ("concat" is also available as a builtin function, to concatenate
-// more than two strings.)
-Value* ConcatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
-Value* LogicalAndFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
-Value* LogicalOrFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
-Value* LogicalNotFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
-Value* SubstringFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
-Value* EqualityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
-Value* InequalityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
-Value* SequenceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
-
-// Global builtins, registered by RegisterBuiltins().
-Value* IfElseFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
-Value* AssertFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
-Value* AbortFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
-
-// Register a new function. The same Function may be registered under
-// multiple names, but a given name should only be used once.
-void RegisterFunction(const std::string& name, Function fn);
-
-// Register all the builtins.
-void RegisterBuiltins();
-
-// Find the Function for a given name; return NULL if no such function
-// exists.
-Function FindFunction(const std::string& name);
-
-// --- convenience functions for use in functions ---
-
-// Evaluate the expressions in argv, and put the results of strings in args. If any expression
-// evaluates to nullptr, return false. Return true on success.
-bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
- std::vector<std::string>* args);
-bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
- std::vector<std::string>* args, size_t start, size_t len);
-
-// Evaluate the expressions in argv, and put the results of Value* in args. If any
-// expression evaluate to nullptr, return false. Return true on success.
-bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
- std::vector<std::unique_ptr<Value>>* args);
-bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
- std::vector<std::unique_ptr<Value>>* args, size_t start, size_t len);
-
-// Use printf-style arguments to compose an error message to put into
-// *state. Returns NULL.
-Value* ErrorAbort(State* state, const char* format, ...)
- __attribute__((format(printf, 2, 3), deprecated));
-
-// ErrorAbort has an optional (but recommended) argument 'cause_code'. If the cause code
-// is set, it will be logged into last_install and provides reason of OTA failures.
-Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...)
- __attribute__((format(printf, 3, 4)));
-
-// Copying the string into a Value.
-Value* StringValue(const char* str);
-
-Value* StringValue(const std::string& str);
-
-int ParseString(const std::string& str, std::unique_ptr<Expr>* root, int* error_count);
-
-#endif // _EXPRESSION_H
diff --git a/edify/include/edify/updater_interface.h b/edify/include/edify/updater_interface.h
deleted file mode 100644
index aa977e3..0000000
--- a/edify/include/edify/updater_interface.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-#include <string>
-#include <string_view>
-
-struct ZipArchive;
-typedef ZipArchive* ZipArchiveHandle;
-
-class UpdaterRuntimeInterface;
-
-class UpdaterInterface {
- public:
- virtual ~UpdaterInterface() = default;
-
- // Writes the message to command pipe, adds a new line in the end.
- virtual void WriteToCommandPipe(const std::string_view message, bool flush = false) const = 0;
-
- // Sends over the message to recovery to print it on the screen.
- virtual void UiPrint(const std::string_view message) const = 0;
-
- // Given the name of the block device, returns |name| for updates on the device; or the file path
- // to the fake block device for simulations.
- virtual std::string FindBlockDeviceName(const std::string_view name) const = 0;
-
- virtual UpdaterRuntimeInterface* GetRuntime() const = 0;
- virtual ZipArchiveHandle GetPackageHandle() const = 0;
- virtual std::string GetResult() const = 0;
- virtual uint8_t* GetMappedPackageAddress() const = 0;
- virtual size_t GetMappedPackageLength() const = 0;
-};
diff --git a/edify/include/edify/updater_runtime_interface.h b/edify/include/edify/updater_runtime_interface.h
deleted file mode 100644
index bdd6aec..0000000
--- a/edify/include/edify/updater_runtime_interface.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#pragma once
-
-#include <string>
-#include <string_view>
-#include <vector>
-
-// This class serves as the base to updater runtime. It wraps the runtime dependent functions; and
-// updates on device and host simulations can have different implementations. e.g. block devices
-// during host simulation merely a temporary file. With this class, the caller side in registered
-// updater's functions will stay the same for both update and simulation.
-class UpdaterRuntimeInterface {
- public:
- virtual ~UpdaterRuntimeInterface() = default;
-
- // Returns true if it's a runtime instance for simulation.
- virtual bool IsSimulator() const = 0;
-
- // Returns the value of system property |key|. If the property doesn't exist, returns
- // |default_value|.
- virtual std::string GetProperty(const std::string_view key,
- const std::string_view default_value) const = 0;
-
- // Given the name of the block device, returns |name| for updates on the device; or the file path
- // to the fake block device for simulations.
- virtual std::string FindBlockDeviceName(const std::string_view name) const = 0;
-
- // Mounts the |location| on |mount_point|. Returns 0 on success.
- virtual int Mount(const std::string_view location, const std::string_view mount_point,
- const std::string_view fs_type, const std::string_view mount_options) = 0;
-
- // Returns true if |mount_point| is mounted.
- virtual bool IsMounted(const std::string_view mount_point) const = 0;
-
- // Unmounts the |mount_point|. Returns a pair of results with the first value indicating
- // if the |mount_point| is mounted, and the second value indicating the result of umount(2).
- virtual std::pair<bool, int> Unmount(const std::string_view mount_point) = 0;
-
- // Reads |filename| and puts its value to |content|.
- virtual bool ReadFileToString(const std::string_view filename, std::string* content) const = 0;
-
- // Updates the content of |filename| with |content|.
- virtual bool WriteStringToFile(const std::string_view content,
- const std::string_view filename) const = 0;
-
- // Wipes the first |len| bytes of block device in |filename|.
- virtual int WipeBlockDevice(const std::string_view filename, size_t len) const = 0;
-
- // Starts a child process and runs the program with |args|. Uses vfork(2) if |is_vfork| is true.
- virtual int RunProgram(const std::vector<std::string>& args, bool is_vfork) const = 0;
-
- // Runs tune2fs with arguments |args|.
- virtual int Tune2Fs(const std::vector<std::string>& args) const = 0;
-
- // Dynamic partition related functions.
- virtual bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) = 0;
- virtual bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) = 0;
- virtual bool UpdateDynamicPartitions(const std::string_view op_list_value) = 0;
-
- // On devices supports A/B, add current slot suffix to arg. Otherwise, return |arg| as is.
- virtual std::string AddSlotSuffix(const std::string_view arg) const = 0;
-};
diff --git a/edify/lexer.ll b/edify/lexer.ll
deleted file mode 100644
index 4e04003..0000000
--- a/edify/lexer.ll
+++ /dev/null
@@ -1,112 +0,0 @@
-%{
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <string.h>
-#include <string>
-
-#include "edify/expr.h"
-#include "yydefs.h"
-#include "parser.h"
-
-int gLine = 1;
-int gColumn = 1;
-int gPos = 0;
-
-std::string string_buffer;
-
-#define ADVANCE do {yylloc.start=gPos; yylloc.end=gPos+yyleng; \
- gColumn+=yyleng; gPos+=yyleng;} while(0)
-
-%}
-
-%x STR
-
-%option noinput
-%option nounput
-%option noyywrap
-
-%%
-
-
-\" {
- BEGIN(STR);
- string_buffer.clear();
- yylloc.start = gPos;
- ++gColumn;
- ++gPos;
-}
-
-<STR>{
- \" {
- ++gColumn;
- ++gPos;
- BEGIN(INITIAL);
- yylval.str = strdup(string_buffer.c_str());
- yylloc.end = gPos;
- return STRING;
- }
-
- \\n { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\n'); }
- \\t { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\t'); }
- \\\" { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\"'); }
- \\\\ { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\\'); }
-
- \\x[0-9a-fA-F]{2} {
- gColumn += yyleng;
- gPos += yyleng;
- int val;
- sscanf(yytext+2, "%x", &val);
- string_buffer.push_back(static_cast<char>(val));
- }
-
- \n {
- ++gLine;
- ++gPos;
- gColumn = 1;
- string_buffer.push_back(yytext[0]);
- }
-
- . {
- ++gColumn;
- ++gPos;
- string_buffer.push_back(yytext[0]);
- }
-}
-
-if ADVANCE; return IF;
-then ADVANCE; return THEN;
-else ADVANCE; return ELSE;
-endif ADVANCE; return ENDIF;
-
-[a-zA-Z0-9_:/.]+ {
- ADVANCE;
- yylval.str = strdup(yytext);
- return STRING;
-}
-
-\&\& ADVANCE; return AND;
-\|\| ADVANCE; return OR;
-== ADVANCE; return EQ;
-!= ADVANCE; return NE;
-
-[+(),!;] ADVANCE; return yytext[0];
-
-[ \t]+ ADVANCE;
-
-(#.*)?\n gPos += yyleng; ++gLine; gColumn = 1;
-
-. return BAD;
diff --git a/edify/parser.yy b/edify/parser.yy
deleted file mode 100644
index 5e1e847..0000000
--- a/edify/parser.yy
+++ /dev/null
@@ -1,145 +0,0 @@
-%{
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/macros.h>
-
-#include "edify/expr.h"
-#include "yydefs.h"
-#include "parser.h"
-
-extern int gLine;
-extern int gColumn;
-
-void yyerror(std::unique_ptr<Expr>* root, int* error_count, const char* s);
-int yyparse(std::unique_ptr<Expr>* root, int* error_count);
-
-struct yy_buffer_state;
-void yy_switch_to_buffer(struct yy_buffer_state* new_buffer);
-struct yy_buffer_state* yy_scan_string(const char* yystr);
-
-// Convenience function for building expressions with a fixed number
-// of arguments.
-static Expr* Build(Function fn, YYLTYPE loc, size_t count, ...) {
- va_list v;
- va_start(v, count);
- Expr* e = new Expr(fn, "(operator)", loc.start, loc.end);
- for (size_t i = 0; i < count; ++i) {
- e->argv.emplace_back(va_arg(v, Expr*));
- }
- va_end(v);
- return e;
-}
-
-%}
-
-%locations
-
-%union {
- char* str;
- Expr* expr;
- std::vector<std::unique_ptr<Expr>>* args;
-}
-
-%token AND OR SUBSTR SUPERSTR EQ NE IF THEN ELSE ENDIF
-%token <str> STRING BAD
-%type <expr> expr
-%type <args> arglist
-
-%destructor { delete $$; } expr
-%destructor { delete $$; } arglist
-
-%parse-param {std::unique_ptr<Expr>* root}
-%parse-param {int* error_count}
-%define parse.error verbose
-
-/* declarations in increasing order of precedence */
-%left ';'
-%left ','
-%left OR
-%left AND
-%left EQ NE
-%left '+'
-%right '!'
-
-%%
-
-input: expr { root->reset($1); }
-;
-
-expr: STRING {
- $$ = new Expr(Literal, $1, @$.start, @$.end);
-}
-| '(' expr ')' { $$ = $2; $$->start=@$.start; $$->end=@$.end; }
-| expr ';' { $$ = $1; $$->start=@1.start; $$->end=@1.end; }
-| expr ';' expr { $$ = Build(SequenceFn, @$, 2, $1, $3); }
-| error ';' expr { $$ = $3; $$->start=@$.start; $$->end=@$.end; }
-| expr '+' expr { $$ = Build(ConcatFn, @$, 2, $1, $3); }
-| expr EQ expr { $$ = Build(EqualityFn, @$, 2, $1, $3); }
-| expr NE expr { $$ = Build(InequalityFn, @$, 2, $1, $3); }
-| expr AND expr { $$ = Build(LogicalAndFn, @$, 2, $1, $3); }
-| expr OR expr { $$ = Build(LogicalOrFn, @$, 2, $1, $3); }
-| '!' expr { $$ = Build(LogicalNotFn, @$, 1, $2); }
-| IF expr THEN expr ENDIF { $$ = Build(IfElseFn, @$, 2, $2, $4); }
-| IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, @$, 3, $2, $4, $6); }
-| STRING '(' arglist ')' {
- Function fn = FindFunction($1);
- if (fn == nullptr) {
- std::string msg = "unknown function \"" + std::string($1) + "\"";
- yyerror(root, error_count, msg.c_str());
- YYERROR;
- }
- $$ = new Expr(fn, $1, @$.start, @$.end);
- $$->argv = std::move(*$3);
-}
-;
-
-arglist: /* empty */ {
- $$ = new std::vector<std::unique_ptr<Expr>>;
-}
-| expr {
- $$ = new std::vector<std::unique_ptr<Expr>>;
- $$->emplace_back($1);
-}
-| arglist ',' expr {
- UNUSED($1);
- $$->push_back(std::unique_ptr<Expr>($3));
-}
-;
-
-%%
-
-void yyerror(std::unique_ptr<Expr>* root, int* error_count, const char* s) {
- if (strlen(s) == 0) {
- s = "syntax error";
- }
- printf("line %d col %d: %s\n", gLine, gColumn, s);
- ++*error_count;
-}
-
-int ParseString(const std::string& str, std::unique_ptr<Expr>* root, int* error_count) {
- yy_switch_to_buffer(yy_scan_string(str.c_str()));
- return yyparse(root, error_count);
-}
diff --git a/edify/yydefs.h b/edify/yydefs.h
deleted file mode 100644
index aca398f..0000000
--- a/edify/yydefs.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _YYDEFS_H_
-#define _YYDEFS_H_
-
-#define YYLTYPE YYLTYPE
-typedef struct {
- int start, end;
-} YYLTYPE;
-
-#define YYLLOC_DEFAULT(Current, Rhs, N) \
- do { \
- if (N) { \
- (Current).start = YYRHSLOC(Rhs, 1).start; \
- (Current).end = YYRHSLOC(Rhs, N).end; \
- } else { \
- (Current).start = YYRHSLOC(Rhs, 0).start; \
- (Current).end = YYRHSLOC(Rhs, 0).end; \
- } \
- } while (0)
-
-int yylex();
-
-#endif
diff --git a/recovery_utils/Android.bp b/recovery_utils/Android.bp
index c31c407..a48ce00 100644
--- a/recovery_utils/Android.bp
+++ b/recovery_utils/Android.bp
@@ -98,5 +98,6 @@
"//bootable/recovery/install",
"//bootable/recovery/minadbd",
"//bootable/recovery/tests",
+ "//bootable/deprecated-ota:__subpackages__",
],
}
diff --git a/tests/Android.bp b/tests/Android.bp
index 1fd26c4..54e1173 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -59,24 +59,6 @@
},
}
-// libapplypatch, libapplypatch_modes
-libapplypatch_static_libs = [
- "libapplypatch_modes",
- "libapplypatch",
- "libedify",
- "libotautil",
- "libbsdiff",
- "libbspatch",
- "libdivsufsort",
- "libdivsufsort64",
- "libutils",
- "libbase",
- "libbrotli",
- "libbz",
- "libz_stable",
- "libziparchive",
-]
-
// librecovery_defaults uses many shared libs that we want to avoid using in tests (e.g. we don't
// have 32-bit android.hardware.health@2.0.so or libbootloader_message.so on marlin).
librecovery_static_libs = [
@@ -125,16 +107,10 @@
defaults: [
"recovery_test_defaults",
- "libupdater_defaults",
- "libupdater_device_defaults",
],
test_suites: ["device-tests"],
- tidy_timeout_srcs: [
- "unit/commands_test.cpp",
- ],
-
srcs: [
"unit/*.cpp",
],
@@ -143,7 +119,7 @@
"libbinder_ndk",
],
- static_libs: libapplypatch_static_libs + librecovery_static_libs + [
+ static_libs: librecovery_static_libs + [
"android.hardware.health-translate-ndk",
"android.hardware.health-V3-ndk",
"libhealthshim",
@@ -152,8 +128,6 @@
"libminui",
"librecovery_utils",
"libotautil",
- "libupdater_device",
- "libupdater_core",
"libupdate_verifier",
"libprotobuf-cpp-lite",
@@ -184,47 +158,6 @@
],
}
-cc_test_host {
- name: "recovery_host_test",
- isolated: true,
-
- defaults: [
- "recovery_test_defaults",
- "libupdater_defaults",
- ],
-
- tidy_timeout_srcs: [
- "unit/host/imgdiff_test.cpp",
- ],
-
- srcs: [
- "unit/host/*",
- ],
-
- static_libs: [
- "libupdater_host",
- "libupdater_core",
- "libimgdiff",
- "libbsdiff",
- "libdivsufsort64",
- "libdivsufsort",
- "libfstab",
- "libc++fs",
- ],
-
- test_suites: ["general-tests"],
- test_config: "RecoveryHostTest.xml",
-
- data: ["testdata/*"],
-
- target: {
- darwin: {
- // libapplypatch in "libupdater_defaults" is not available on the Mac.
- enabled: false,
- },
- },
-}
-
cc_fuzz {
name: "libinstall_verify_package_fuzzer",
defaults: [
diff --git a/tests/RecoveryHostTest.xml b/tests/RecoveryHostTest.xml
deleted file mode 100644
index 0ac75e4..0000000
--- a/tests/RecoveryHostTest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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.
--->
-<configuration description="Runs recovery_host_test.">
- <option name="null-device" value="true" />
-
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
- <option name="force-root" value="false" />
- </target_preparer>
- <option name="not-shardable" value="true" />
-
- <test class="com.android.tradefed.testtype.HostGTest" >
- <option name="module-name" value="recovery_host_test" />
- <option name="native-test-timeout" value="5m"/>
- </test>
-</configuration>
diff --git a/tests/unit/applypatch_modes_test.cpp b/tests/unit/applypatch_modes_test.cpp
deleted file mode 100644
index 08414b7..0000000
--- a/tests/unit/applypatch_modes_test.cpp
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2016 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 agree to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <bsdiff/bsdiff.h>
-#include <gtest/gtest.h>
-#include <openssl/sha.h>
-
-#include "applypatch/applypatch_modes.h"
-#include "common/test_constants.h"
-#include "otautil/paths.h"
-#include "otautil/print_sha1.h"
-#include "otautil/sysutil.h"
-
-using namespace std::string_literals;
-
-// Loads a given partition and returns a string of form "EMMC:name:size:hash".
-static std::string GetEmmcTargetString(const std::string& filename,
- const std::string& display_name = "") {
- std::string data;
- if (!android::base::ReadFileToString(filename, &data)) {
- PLOG(ERROR) << "Failed to read " << filename;
- return {};
- }
-
- uint8_t digest[SHA_DIGEST_LENGTH];
- SHA1(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), digest);
-
- return "EMMC:"s + (display_name.empty() ? filename : display_name) + ":" +
- std::to_string(data.size()) + ":" + print_sha1(digest);
-}
-
-class ApplyPatchModesTest : public ::testing::Test {
- protected:
- void SetUp() override {
- source = GetEmmcTargetString(from_testdata_base("boot.img"));
- ASSERT_FALSE(source.empty());
-
- std::string recovery_file = from_testdata_base("recovery.img");
- recovery = GetEmmcTargetString(recovery_file);
- ASSERT_FALSE(recovery.empty());
-
- ASSERT_TRUE(android::base::WriteStringToFile("", patched_file_.path));
- target = GetEmmcTargetString(recovery_file, patched_file_.path);
- ASSERT_FALSE(target.empty());
-
- Paths::Get().set_cache_temp_source(cache_source_.path);
- }
-
- std::string source;
- std::string target;
- std::string recovery;
-
- private:
- TemporaryFile cache_source_;
- TemporaryFile patched_file_;
-};
-
-static int InvokeApplyPatchModes(const std::vector<std::string>& args) {
- auto args_to_call = StringVectorToNullTerminatedArray(args);
- return applypatch_modes(args_to_call.size() - 1, args_to_call.data());
-}
-
-static void VerifyPatchedTarget(const std::string& target) {
- std::vector<std::string> pieces = android::base::Split(target, ":");
- ASSERT_EQ(4, pieces.size());
- ASSERT_EQ("EMMC", pieces[0]);
-
- std::string patched_emmc = GetEmmcTargetString(pieces[1]);
- ASSERT_FALSE(patched_emmc.empty());
- ASSERT_EQ(target, patched_emmc);
-}
-
-TEST_F(ApplyPatchModesTest, InvalidArgs) {
- // At least two args (including the filename).
- ASSERT_EQ(2, InvokeApplyPatchModes({ "applypatch" }));
-
- // Unrecognized args.
- ASSERT_EQ(2, InvokeApplyPatchModes({ "applypatch", "-x" }));
-}
-
-TEST_F(ApplyPatchModesTest, PatchModeEmmcTarget) {
- std::vector<std::string> args{
- "applypatch",
- "--bonus",
- from_testdata_base("bonus.file"),
- "--patch",
- from_testdata_base("recovery-from-boot.p"),
- "--target",
- target,
- "--source",
- source,
- };
- ASSERT_EQ(0, InvokeApplyPatchModes(args));
- VerifyPatchedTarget(target);
-}
-
-// Tests patching an eMMC target without a separate bonus file (i.e. recovery-from-boot patch has
-// everything).
-TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithoutBonusFile) {
- std::vector<std::string> args{
- "applypatch", "--patch", from_testdata_base("recovery-from-boot-with-bonus.p"),
- "--target", target, "--source",
- source,
- };
-
- ASSERT_EQ(0, InvokeApplyPatchModes(args));
- VerifyPatchedTarget(target);
-}
-
-// Ensures that applypatch works with a bsdiff based recovery-from-boot.p.
-TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithBsdiffPatch) {
- // Generate the bsdiff patch of recovery-from-boot.p.
- std::string src_content;
- ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("boot.img"), &src_content));
-
- std::string tgt_content;
- ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("recovery.img"), &tgt_content));
-
- TemporaryFile patch_file;
- ASSERT_EQ(0,
- bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src_content.data()), src_content.size(),
- reinterpret_cast<const uint8_t*>(tgt_content.data()), tgt_content.size(),
- patch_file.path, nullptr));
-
- std::vector<std::string> args{
- "applypatch", "--patch", patch_file.path, "--target", target, "--source", source,
- };
- ASSERT_EQ(0, InvokeApplyPatchModes(args));
- VerifyPatchedTarget(target);
-}
-
-TEST_F(ApplyPatchModesTest, PatchModeInvalidArgs) {
- // Invalid bonus file.
- std::vector<std::string> args{
- "applypatch", "--bonus", "/doesntexist", "--patch", from_testdata_base("recovery-from-boot.p"),
- "--target", target, "--source", source,
- };
- ASSERT_NE(0, InvokeApplyPatchModes(args));
-
- // With bonus file, but missing args.
- ASSERT_NE(0,
- InvokeApplyPatchModes({ "applypatch", "--bonus", from_testdata_base("bonus.file") }));
-}
-
-TEST_F(ApplyPatchModesTest, FlashMode) {
- std::vector<std::string> args{
- "applypatch", "--flash", from_testdata_base("recovery.img"), "--target", target,
- };
- ASSERT_EQ(0, InvokeApplyPatchModes(args));
- VerifyPatchedTarget(target);
-}
-
-TEST_F(ApplyPatchModesTest, FlashModeInvalidArgs) {
- std::vector<std::string> args{
- "applypatch", "--bonus", from_testdata_base("bonus.file"), "--flash", source,
- "--target", target,
- };
- ASSERT_NE(0, InvokeApplyPatchModes(args));
-}
-
-TEST_F(ApplyPatchModesTest, CheckMode) {
- ASSERT_EQ(0, InvokeApplyPatchModes({ "applypatch", "--check", recovery }));
- ASSERT_EQ(0, InvokeApplyPatchModes({ "applypatch", "--check", source }));
-}
-
-TEST_F(ApplyPatchModesTest, CheckModeInvalidArgs) {
- ASSERT_EQ(2, InvokeApplyPatchModes({ "applypatch", "--check" }));
-}
-
-TEST_F(ApplyPatchModesTest, CheckModeNonEmmcTarget) {
- ASSERT_NE(0, InvokeApplyPatchModes({ "applypatch", "--check", from_testdata_base("boot.img") }));
-}
-
-TEST_F(ApplyPatchModesTest, ShowLicenses) {
- ASSERT_EQ(0, InvokeApplyPatchModes({ "applypatch", "--license" }));
-}
diff --git a/tests/unit/applypatch_test.cpp b/tests/unit/applypatch_test.cpp
deleted file mode 100644
index 218a224..0000000
--- a/tests/unit/applypatch_test.cpp
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Copyright (C) 2016 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 agree to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <dirent.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
-
-#include "applypatch/applypatch.h"
-#include "common/test_constants.h"
-#include "edify/expr.h"
-#include "otautil/paths.h"
-#include "otautil/print_sha1.h"
-
-using namespace std::string_literals;
-
-class ApplyPatchTest : public ::testing::Test {
- protected:
- void SetUp() override {
- source_file = from_testdata_base("boot.img");
- FileContents boot_fc;
- ASSERT_TRUE(LoadFileContents(source_file, &boot_fc));
- source_size = boot_fc.data.size();
- source_sha1 = print_sha1(boot_fc.sha1);
-
- target_file = from_testdata_base("recovery.img");
- FileContents recovery_fc;
- ASSERT_TRUE(LoadFileContents(target_file, &recovery_fc));
- target_size = recovery_fc.data.size();
- target_sha1 = print_sha1(recovery_fc.sha1);
-
- source_partition = Partition(source_file, source_size, source_sha1);
- target_partition = Partition(partition_file.path, target_size, target_sha1);
-
- srand(time(nullptr));
- bad_sha1_a = android::base::StringPrintf("%040x", rand());
- bad_sha1_b = android::base::StringPrintf("%040x", rand());
-
- // Reset the cache backup file.
- Paths::Get().set_cache_temp_source(cache_temp_source.path);
- }
-
- void TearDown() override {
- ASSERT_TRUE(android::base::RemoveFileIfExists(cache_temp_source.path));
- }
-
- std::string source_file;
- std::string source_sha1;
- size_t source_size;
-
- std::string target_file;
- std::string target_sha1;
- size_t target_size;
-
- std::string bad_sha1_a;
- std::string bad_sha1_b;
-
- Partition source_partition;
- Partition target_partition;
-
- private:
- TemporaryFile partition_file;
- TemporaryFile cache_temp_source;
-};
-
-TEST_F(ApplyPatchTest, CheckPartition) {
- ASSERT_TRUE(CheckPartition(source_partition));
-}
-
-TEST_F(ApplyPatchTest, CheckPartition_Mismatching) {
- ASSERT_FALSE(CheckPartition(Partition(source_file, target_size, target_sha1)));
- ASSERT_FALSE(CheckPartition(Partition(source_file, source_size, bad_sha1_a)));
-
- ASSERT_FALSE(CheckPartition(Partition(source_file, source_size - 1, source_sha1)));
- ASSERT_FALSE(CheckPartition(Partition(source_file, source_size + 1, source_sha1)));
-}
-
-TEST_F(ApplyPatchTest, PatchPartitionCheck) {
- ASSERT_TRUE(PatchPartitionCheck(target_partition, source_partition));
-
- ASSERT_TRUE(
- PatchPartitionCheck(Partition(source_file, source_size - 1, source_sha1), source_partition));
-
- ASSERT_TRUE(
- PatchPartitionCheck(Partition(source_file, source_size + 1, source_sha1), source_partition));
-}
-
-TEST_F(ApplyPatchTest, PatchPartitionCheck_UseBackup) {
- ASSERT_FALSE(
- PatchPartitionCheck(target_partition, Partition(target_file, source_size, source_sha1)));
-
- Paths::Get().set_cache_temp_source(source_file);
- ASSERT_TRUE(
- PatchPartitionCheck(target_partition, Partition(target_file, source_size, source_sha1)));
-}
-
-TEST_F(ApplyPatchTest, PatchPartitionCheck_UseBackup_BothCorrupted) {
- ASSERT_FALSE(
- PatchPartitionCheck(target_partition, Partition(target_file, source_size, source_sha1)));
-
- Paths::Get().set_cache_temp_source(target_file);
- ASSERT_FALSE(
- PatchPartitionCheck(target_partition, Partition(target_file, source_size, source_sha1)));
-}
-
-TEST_F(ApplyPatchTest, PatchPartition) {
- FileContents patch_fc;
- ASSERT_TRUE(LoadFileContents(from_testdata_base("recovery-from-boot.p"), &patch_fc));
- Value patch(Value::Type::BLOB, std::string(patch_fc.data.cbegin(), patch_fc.data.cend()));
-
- FileContents bonus_fc;
- ASSERT_TRUE(LoadFileContents(from_testdata_base("bonus.file"), &bonus_fc));
- Value bonus(Value::Type::BLOB, std::string(bonus_fc.data.cbegin(), bonus_fc.data.cend()));
-
- ASSERT_TRUE(PatchPartition(target_partition, source_partition, patch, &bonus, false));
-}
-
-// Tests patching an eMMC target without a separate bonus file (i.e. recovery-from-boot patch has
-// everything).
-TEST_F(ApplyPatchTest, PatchPartitionWithoutBonusFile) {
- FileContents patch_fc;
- ASSERT_TRUE(LoadFileContents(from_testdata_base("recovery-from-boot-with-bonus.p"), &patch_fc));
- Value patch(Value::Type::BLOB, std::string(patch_fc.data.cbegin(), patch_fc.data.cend()));
-
- ASSERT_TRUE(PatchPartition(target_partition, source_partition, patch, nullptr, false));
-}
-
-class FreeCacheTest : public ::testing::Test {
- protected:
- static constexpr size_t PARTITION_SIZE = 4096 * 10;
-
- // Returns a sorted list of files in |dirname|.
- static std::vector<std::string> FindFilesInDir(const std::string& dirname) {
- std::vector<std::string> file_list;
-
- std::unique_ptr<DIR, decltype(&closedir)> d(opendir(dirname.c_str()), closedir);
- struct dirent* de;
- while ((de = readdir(d.get())) != 0) {
- std::string path = dirname + "/" + de->d_name;
-
- struct stat st;
- if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
- file_list.emplace_back(de->d_name);
- }
- }
-
- std::sort(file_list.begin(), file_list.end());
- return file_list;
- }
-
- void AddFilesToDir(const std::string& dir, const std::vector<std::string>& files) {
- std::string zeros(4096, 0);
- for (const auto& file : files) {
- temporary_files_.push_back(dir + "/" + file);
- ASSERT_TRUE(android::base::WriteStringToFile(zeros, temporary_files_.back()));
- }
- }
-
- void SetUp() override {
- Paths::Get().set_cache_log_directory(mock_log_dir.path);
- temporary_files_.clear();
- }
-
- void TearDown() override {
- for (const auto& file : temporary_files_) {
- ASSERT_TRUE(android::base::RemoveFileIfExists(file));
- }
- }
-
- // A mock method to calculate the free space. It assumes the partition has a total size of 40960
- // bytes and all files are 4096 bytes in size.
- static size_t MockFreeSpaceChecker(const std::string& dirname) {
- std::vector<std::string> files = FindFilesInDir(dirname);
- return PARTITION_SIZE - 4096 * files.size();
- }
-
- TemporaryDir mock_cache;
- TemporaryDir mock_log_dir;
-
- private:
- std::vector<std::string> temporary_files_;
-};
-
-TEST_F(FreeCacheTest, FreeCacheSmoke) {
- std::vector<std::string> files = { "file1", "file2", "file3" };
- AddFilesToDir(mock_cache.path, files);
- ASSERT_EQ(files, FindFilesInDir(mock_cache.path));
- ASSERT_EQ(4096 * 7, MockFreeSpaceChecker(mock_cache.path));
-
- ASSERT_TRUE(RemoveFilesInDirectory(4096 * 9, mock_cache.path, MockFreeSpaceChecker));
-
- ASSERT_EQ(std::vector<std::string>{ "file3" }, FindFilesInDir(mock_cache.path));
- ASSERT_EQ(4096 * 9, MockFreeSpaceChecker(mock_cache.path));
-}
-
-TEST_F(FreeCacheTest, FreeCacheFreeSpaceCheckerError) {
- std::vector<std::string> files{ "file1", "file2", "file3" };
- AddFilesToDir(mock_cache.path, files);
- ASSERT_EQ(files, FindFilesInDir(mock_cache.path));
- ASSERT_EQ(4096 * 7, MockFreeSpaceChecker(mock_cache.path));
-
- ASSERT_FALSE(
- RemoveFilesInDirectory(4096 * 9, mock_cache.path, [](const std::string&) { return -1; }));
-}
-
-TEST_F(FreeCacheTest, FreeCacheOpenFile) {
- std::vector<std::string> files = { "file1", "file2" };
- AddFilesToDir(mock_cache.path, files);
- ASSERT_EQ(files, FindFilesInDir(mock_cache.path));
- ASSERT_EQ(4096 * 8, MockFreeSpaceChecker(mock_cache.path));
-
- std::string file1_path = mock_cache.path + "/file1"s;
- android::base::unique_fd fd(open(file1_path.c_str(), O_RDONLY));
-
- // file1 can't be deleted as it's opened by us.
- ASSERT_FALSE(RemoveFilesInDirectory(4096 * 10, mock_cache.path, MockFreeSpaceChecker));
-
- ASSERT_EQ(std::vector<std::string>{ "file1" }, FindFilesInDir(mock_cache.path));
-}
-
-TEST_F(FreeCacheTest, FreeCacheLogsSmoke) {
- std::vector<std::string> log_files = { "last_log", "last_log.1", "last_kmsg.2", "last_log.5",
- "last_log.10" };
- AddFilesToDir(mock_log_dir.path, log_files);
- ASSERT_EQ(4096 * 5, MockFreeSpaceChecker(mock_log_dir.path));
-
- ASSERT_TRUE(RemoveFilesInDirectory(4096 * 8, mock_log_dir.path, MockFreeSpaceChecker));
-
- // Logs with a higher index will be deleted first
- std::vector<std::string> expected = { "last_log", "last_log.1" };
- ASSERT_EQ(expected, FindFilesInDir(mock_log_dir.path));
- ASSERT_EQ(4096 * 8, MockFreeSpaceChecker(mock_log_dir.path));
-}
-
-TEST_F(FreeCacheTest, FreeCacheLogsStringComparison) {
- std::vector<std::string> log_files = { "last_log.1", "last_kmsg.1", "last_log.not_number",
- "last_kmsgrandom" };
- AddFilesToDir(mock_log_dir.path, log_files);
- ASSERT_EQ(4096 * 6, MockFreeSpaceChecker(mock_log_dir.path));
-
- ASSERT_TRUE(RemoveFilesInDirectory(4096 * 9, mock_log_dir.path, MockFreeSpaceChecker));
-
- // Logs with incorrect format will be deleted first; and the last_kmsg with the same index is
- // deleted before last_log.
- std::vector<std::string> expected = { "last_log.1" };
- ASSERT_EQ(expected, FindFilesInDir(mock_log_dir.path));
- ASSERT_EQ(4096 * 9, MockFreeSpaceChecker(mock_log_dir.path));
-}
-
-TEST_F(FreeCacheTest, FreeCacheLogsOtherFiles) {
- std::vector<std::string> log_files = { "last_install", "command", "block.map", "last_log",
- "last_kmsg.1" };
- AddFilesToDir(mock_log_dir.path, log_files);
- ASSERT_EQ(4096 * 5, MockFreeSpaceChecker(mock_log_dir.path));
-
- ASSERT_FALSE(RemoveFilesInDirectory(4096 * 8, mock_log_dir.path, MockFreeSpaceChecker));
-
- // Non log files in /cache/recovery won't be deleted.
- std::vector<std::string> expected = { "block.map", "command", "last_install" };
- ASSERT_EQ(expected, FindFilesInDir(mock_log_dir.path));
-}
diff --git a/tests/unit/commands_test.cpp b/tests/unit/commands_test.cpp
deleted file mode 100644
index 8a54df7..0000000
--- a/tests/unit/commands_test.cpp
+++ /dev/null
@@ -1,554 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <algorithm>
-#include <string>
-
-#include <android-base/strings.h>
-#include <gtest/gtest.h>
-#include <openssl/sha.h>
-
-#include "otautil/print_sha1.h"
-#include "otautil/rangeset.h"
-#include "private/commands.h"
-
-TEST(CommandsTest, ParseType) {
- ASSERT_EQ(Command::Type::ZERO, Command::ParseType("zero"));
- ASSERT_EQ(Command::Type::NEW, Command::ParseType("new"));
- ASSERT_EQ(Command::Type::ERASE, Command::ParseType("erase"));
- ASSERT_EQ(Command::Type::MOVE, Command::ParseType("move"));
- ASSERT_EQ(Command::Type::BSDIFF, Command::ParseType("bsdiff"));
- ASSERT_EQ(Command::Type::IMGDIFF, Command::ParseType("imgdiff"));
- ASSERT_EQ(Command::Type::STASH, Command::ParseType("stash"));
- ASSERT_EQ(Command::Type::FREE, Command::ParseType("free"));
- ASSERT_EQ(Command::Type::COMPUTE_HASH_TREE, Command::ParseType("compute_hash_tree"));
-}
-
-TEST(CommandsTest, ParseType_InvalidCommand) {
- ASSERT_EQ(Command::Type::LAST, Command::ParseType("foo"));
- ASSERT_EQ(Command::Type::LAST, Command::ParseType("bar"));
-}
-
-TEST(CommandsTest, ParseTargetInfoAndSourceInfo_SourceBlocksOnly) {
- const std::vector<std::string> tokens{
- "4,569884,569904,591946,592043",
- "117",
- "4,566779,566799,591946,592043",
- };
- TargetInfo target;
- SourceInfo source;
- std::string err;
- ASSERT_TRUE(Command::ParseTargetInfoAndSourceInfo(
- tokens, "1d74d1a60332fd38cf9405f1bae67917888da6cb", &target,
- "1d74d1a60332fd38cf9405f1bae67917888da6cb", &source, &err));
- ASSERT_EQ(TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
- RangeSet({ { 569884, 569904 }, { 591946, 592043 } })),
- target);
- ASSERT_EQ(SourceInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
- RangeSet({ { 566779, 566799 }, { 591946, 592043 } }), {}, {}),
- source);
- ASSERT_EQ(117, source.blocks());
-}
-
-TEST(CommandsTest, ParseTargetInfoAndSourceInfo_StashesOnly) {
- const std::vector<std::string> tokens{
- "2,350729,350731",
- "2",
- "-",
- "6ebcf8cf1f6be0bc49e7d4a864214251925d1d15:2,0,2",
- };
- TargetInfo target;
- SourceInfo source;
- std::string err;
- ASSERT_TRUE(Command::ParseTargetInfoAndSourceInfo(
- tokens, "6ebcf8cf1f6be0bc49e7d4a864214251925d1d15", &target,
- "1c25ba04d3278d6b65a1b9f17abac78425ec8b8d", &source, &err));
- ASSERT_EQ(
- TargetInfo("6ebcf8cf1f6be0bc49e7d4a864214251925d1d15", RangeSet({ { 350729, 350731 } })),
- target);
- ASSERT_EQ(
- SourceInfo("1c25ba04d3278d6b65a1b9f17abac78425ec8b8d", {}, {},
- {
- StashInfo("6ebcf8cf1f6be0bc49e7d4a864214251925d1d15", RangeSet({ { 0, 2 } })),
- }),
- source);
- ASSERT_EQ(2, source.blocks());
-}
-
-TEST(CommandsTest, ParseTargetInfoAndSourceInfo_SourceBlocksAndStashes) {
- const std::vector<std::string> tokens{
- "4,611641,611643,636981,637075",
- "96",
- "4,636981,637075,770665,770666",
- "4,0,94,95,96",
- "9eedf00d11061549e32503cadf054ec6fbfa7a23:2,94,95",
- };
- TargetInfo target;
- SourceInfo source;
- std::string err;
- ASSERT_TRUE(Command::ParseTargetInfoAndSourceInfo(
- tokens, "4734d1b241eb3d0f993714aaf7d665fae43772b6", &target,
- "a6cbdf3f416960f02189d3a814ec7e9e95c44a0d", &source, &err));
- ASSERT_EQ(TargetInfo("4734d1b241eb3d0f993714aaf7d665fae43772b6",
- RangeSet({ { 611641, 611643 }, { 636981, 637075 } })),
- target);
- ASSERT_EQ(SourceInfo(
- "a6cbdf3f416960f02189d3a814ec7e9e95c44a0d",
- RangeSet({ { 636981, 637075 }, { 770665, 770666 } }), // source ranges
- RangeSet({ { 0, 94 }, { 95, 96 } }), // source location
- {
- StashInfo("9eedf00d11061549e32503cadf054ec6fbfa7a23", RangeSet({ { 94, 95 } })),
- }),
- source);
- ASSERT_EQ(96, source.blocks());
-}
-
-TEST(CommandsTest, ParseTargetInfoAndSourceInfo_InvalidInput) {
- const std::vector<std::string> tokens{
- "4,611641,611643,636981,637075",
- "96",
- "4,636981,637075,770665,770666",
- "4,0,94,95,96",
- "9eedf00d11061549e32503cadf054ec6fbfa7a23:2,94,95",
- };
- TargetInfo target;
- SourceInfo source;
- std::string err;
-
- // Mismatching block count.
- {
- std::vector<std::string> tokens_copy(tokens);
- tokens_copy[1] = "97";
- ASSERT_FALSE(Command::ParseTargetInfoAndSourceInfo(
- tokens_copy, "1d74d1a60332fd38cf9405f1bae67917888da6cb", &target,
- "1d74d1a60332fd38cf9405f1bae67917888da6cb", &source, &err));
- }
-
- // Excess stashes (causing block count mismatch).
- {
- std::vector<std::string> tokens_copy(tokens);
- tokens_copy.push_back("e145a2f83a33334714ac65e34969c1f115e54a6f:2,0,22");
- ASSERT_FALSE(Command::ParseTargetInfoAndSourceInfo(
- tokens_copy, "1d74d1a60332fd38cf9405f1bae67917888da6cb", &target,
- "1d74d1a60332fd38cf9405f1bae67917888da6cb", &source, &err));
- }
-
- // Invalid args.
- for (size_t i = 0; i < tokens.size(); i++) {
- TargetInfo target;
- SourceInfo source;
- std::string err;
- ASSERT_FALSE(Command::ParseTargetInfoAndSourceInfo(
- std::vector<std::string>(tokens.cbegin() + i + 1, tokens.cend()),
- "1d74d1a60332fd38cf9405f1bae67917888da6cb", &target,
- "1d74d1a60332fd38cf9405f1bae67917888da6cb", &source, &err));
- }
-}
-
-TEST(CommandsTest, Parse_EmptyInput) {
- std::string err;
- ASSERT_FALSE(Command::Parse("", 0, &err));
- ASSERT_EQ("invalid type", err);
-}
-
-TEST(CommandsTest, Parse_ABORT_Allowed) {
- Command::abort_allowed_ = true;
-
- const std::string input{ "abort" };
- std::string err;
- Command command = Command::Parse(input, 0, &err);
- ASSERT_TRUE(command);
-
- ASSERT_EQ(TargetInfo(), command.target());
- ASSERT_EQ(SourceInfo(), command.source());
- ASSERT_EQ(StashInfo(), command.stash());
- ASSERT_EQ(PatchInfo(), command.patch());
-}
-
-TEST(CommandsTest, Parse_ABORT_NotAllowed) {
- const std::string input{ "abort" };
- std::string err;
- Command command = Command::Parse(input, 0, &err);
- ASSERT_FALSE(command);
-}
-
-TEST(CommandsTest, Parse_BSDIFF) {
- const std::string input{
- "bsdiff 0 148 "
- "f201a4e04bd3860da6ad47b957ef424d58a58f8c 9d5d223b4bc5c45dbd25a799c4f1a98466731599 "
- "4,565704,565752,566779,566799 "
- "68 4,64525,64545,565704,565752"
- };
- std::string err;
- Command command = Command::Parse(input, 1, &err);
- ASSERT_TRUE(command);
-
- ASSERT_EQ(Command::Type::BSDIFF, command.type());
- ASSERT_EQ(1, command.index());
- ASSERT_EQ(input, command.cmdline());
-
- ASSERT_EQ(TargetInfo("9d5d223b4bc5c45dbd25a799c4f1a98466731599",
- RangeSet({ { 565704, 565752 }, { 566779, 566799 } })),
- command.target());
- ASSERT_EQ(SourceInfo("f201a4e04bd3860da6ad47b957ef424d58a58f8c",
- RangeSet({ { 64525, 64545 }, { 565704, 565752 } }), RangeSet(), {}),
- command.source());
- ASSERT_EQ(StashInfo(), command.stash());
- ASSERT_EQ(PatchInfo(0, 148), command.patch());
-}
-
-TEST(CommandsTest, Parse_ERASE) {
- const std::string input{ "erase 2,5,10" };
- std::string err;
- Command command = Command::Parse(input, 2, &err);
- ASSERT_TRUE(command);
-
- ASSERT_EQ(Command::Type::ERASE, command.type());
- ASSERT_EQ(2, command.index());
- ASSERT_EQ(input, command.cmdline());
-
- ASSERT_EQ(TargetInfo("unknown-hash", RangeSet({ { 5, 10 } })), command.target());
- ASSERT_EQ(SourceInfo(), command.source());
- ASSERT_EQ(StashInfo(), command.stash());
- ASSERT_EQ(PatchInfo(), command.patch());
-}
-
-TEST(CommandsTest, Parse_FREE) {
- const std::string input{ "free hash1" };
- std::string err;
- Command command = Command::Parse(input, 3, &err);
- ASSERT_TRUE(command);
-
- ASSERT_EQ(Command::Type::FREE, command.type());
- ASSERT_EQ(3, command.index());
- ASSERT_EQ(input, command.cmdline());
-
- ASSERT_EQ(TargetInfo(), command.target());
- ASSERT_EQ(SourceInfo(), command.source());
- ASSERT_EQ(StashInfo("hash1", RangeSet()), command.stash());
- ASSERT_EQ(PatchInfo(), command.patch());
-}
-
-TEST(CommandsTest, Parse_IMGDIFF) {
- const std::string input{
- "imgdiff 29629269 185 "
- "a6b1c49aed1b57a2aab1ec3e1505b945540cd8db 51978f65035f584a8ef7afa941dacb6d5e862164 "
- "2,90851,90852 "
- "1 2,90851,90852"
- };
- std::string err;
- Command command = Command::Parse(input, 4, &err);
- ASSERT_TRUE(command);
-
- ASSERT_EQ(Command::Type::IMGDIFF, command.type());
- ASSERT_EQ(4, command.index());
- ASSERT_EQ(input, command.cmdline());
-
- ASSERT_EQ(TargetInfo("51978f65035f584a8ef7afa941dacb6d5e862164", RangeSet({ { 90851, 90852 } })),
- command.target());
- ASSERT_EQ(SourceInfo("a6b1c49aed1b57a2aab1ec3e1505b945540cd8db", RangeSet({ { 90851, 90852 } }),
- RangeSet(), {}),
- command.source());
- ASSERT_EQ(StashInfo(), command.stash());
- ASSERT_EQ(PatchInfo(29629269, 185), command.patch());
-}
-
-TEST(CommandsTest, Parse_MOVE) {
- const std::string input{
- "move 1d74d1a60332fd38cf9405f1bae67917888da6cb "
- "4,569884,569904,591946,592043 117 4,566779,566799,591946,592043"
- };
- std::string err;
- Command command = Command::Parse(input, 5, &err);
- ASSERT_TRUE(command);
-
- ASSERT_EQ(Command::Type::MOVE, command.type());
- ASSERT_EQ(5, command.index());
- ASSERT_EQ(input, command.cmdline());
-
- ASSERT_EQ(TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
- RangeSet({ { 569884, 569904 }, { 591946, 592043 } })),
- command.target());
- ASSERT_EQ(SourceInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
- RangeSet({ { 566779, 566799 }, { 591946, 592043 } }), RangeSet(), {}),
- command.source());
- ASSERT_EQ(StashInfo(), command.stash());
- ASSERT_EQ(PatchInfo(), command.patch());
-}
-
-TEST(CommandsTest, Parse_NEW) {
- const std::string input{ "new 4,3,5,10,12" };
- std::string err;
- Command command = Command::Parse(input, 6, &err);
- ASSERT_TRUE(command);
-
- ASSERT_EQ(Command::Type::NEW, command.type());
- ASSERT_EQ(6, command.index());
- ASSERT_EQ(input, command.cmdline());
-
- ASSERT_EQ(TargetInfo("unknown-hash", RangeSet({ { 3, 5 }, { 10, 12 } })), command.target());
- ASSERT_EQ(SourceInfo(), command.source());
- ASSERT_EQ(StashInfo(), command.stash());
- ASSERT_EQ(PatchInfo(), command.patch());
-}
-
-TEST(CommandsTest, Parse_STASH) {
- const std::string input{ "stash hash1 2,5,10" };
- std::string err;
- Command command = Command::Parse(input, 7, &err);
- ASSERT_TRUE(command);
-
- ASSERT_EQ(Command::Type::STASH, command.type());
- ASSERT_EQ(7, command.index());
- ASSERT_EQ(input, command.cmdline());
-
- ASSERT_EQ(TargetInfo(), command.target());
- ASSERT_EQ(SourceInfo(), command.source());
- ASSERT_EQ(StashInfo("hash1", RangeSet({ { 5, 10 } })), command.stash());
- ASSERT_EQ(PatchInfo(), command.patch());
-}
-
-TEST(CommandsTest, Parse_ZERO) {
- const std::string input{ "zero 2,1,5" };
- std::string err;
- Command command = Command::Parse(input, 8, &err);
- ASSERT_TRUE(command);
-
- ASSERT_EQ(Command::Type::ZERO, command.type());
- ASSERT_EQ(8, command.index());
- ASSERT_EQ(input, command.cmdline());
-
- ASSERT_EQ(TargetInfo("unknown-hash", RangeSet({ { 1, 5 } })), command.target());
- ASSERT_EQ(SourceInfo(), command.source());
- ASSERT_EQ(StashInfo(), command.stash());
- ASSERT_EQ(PatchInfo(), command.patch());
-}
-
-TEST(CommandsTest, Parse_COMPUTE_HASH_TREE) {
- const std::string input{ "compute_hash_tree 2,0,1 2,3,4 sha1 unknown-salt unknown-root-hash" };
- std::string err;
- Command command = Command::Parse(input, 9, &err);
- ASSERT_TRUE(command);
-
- ASSERT_EQ(Command::Type::COMPUTE_HASH_TREE, command.type());
- ASSERT_EQ(9, command.index());
- ASSERT_EQ(input, command.cmdline());
-
- HashTreeInfo expected_info(RangeSet({ { 0, 1 } }), RangeSet({ { 3, 4 } }), "sha1", "unknown-salt",
- "unknown-root-hash");
- ASSERT_EQ(expected_info, command.hash_tree_info());
- ASSERT_EQ(TargetInfo(), command.target());
- ASSERT_EQ(SourceInfo(), command.source());
- ASSERT_EQ(StashInfo(), command.stash());
- ASSERT_EQ(PatchInfo(), command.patch());
-}
-
-TEST(CommandsTest, Parse_InvalidNumberOfArgs) {
- Command::abort_allowed_ = true;
-
- // Note that the case of having excess args in BSDIFF, IMGDIFF and MOVE is covered by
- // ParseTargetInfoAndSourceInfo_InvalidInput.
- std::vector<std::string> inputs{
- "abort foo",
- "bsdiff",
- "compute_hash_tree, 2,0,1 2,0,1 unknown-algorithm unknown-salt",
- "erase",
- "erase 4,3,5,10,12 hash1",
- "free",
- "free id1 id2",
- "imgdiff",
- "move",
- "new",
- "new 4,3,5,10,12 hash1",
- "stash",
- "stash id1",
- "stash id1 4,3,5,10,12 id2",
- "zero",
- "zero 4,3,5,10,12 hash2",
- };
- for (const auto& input : inputs) {
- std::string err;
- ASSERT_FALSE(Command::Parse(input, 0, &err));
- }
-}
-
-TEST(SourceInfoTest, Overlaps) {
- ASSERT_TRUE(SourceInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
- RangeSet({ { 7, 9 }, { 16, 20 } }), {}, {})
- .Overlaps(TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
- RangeSet({ { 7, 9 }, { 16, 20 } }))));
-
- ASSERT_TRUE(SourceInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
- RangeSet({ { 7, 9 }, { 16, 20 } }), {}, {})
- .Overlaps(TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
- RangeSet({ { 4, 7 }, { 16, 23 } }))));
-
- ASSERT_FALSE(SourceInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
- RangeSet({ { 7, 9 }, { 16, 20 } }), {}, {})
- .Overlaps(TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
- RangeSet({ { 9, 16 } }))));
-}
-
-TEST(SourceInfoTest, Overlaps_EmptySourceOrTarget) {
- ASSERT_FALSE(SourceInfo().Overlaps(TargetInfo()));
-
- ASSERT_FALSE(SourceInfo().Overlaps(
- TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb", RangeSet({ { 7, 9 }, { 16, 20 } }))));
-
- ASSERT_FALSE(SourceInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
- RangeSet({ { 7, 9 }, { 16, 20 } }), {}, {})
- .Overlaps(TargetInfo()));
-}
-
-TEST(SourceInfoTest, Overlaps_WithStashes) {
- ASSERT_FALSE(SourceInfo("a6cbdf3f416960f02189d3a814ec7e9e95c44a0d",
- RangeSet({ { 81, 175 }, { 265, 266 } }), // source ranges
- RangeSet({ { 0, 94 }, { 95, 96 } }), // source location
- { StashInfo("9eedf00d11061549e32503cadf054ec6fbfa7a23",
- RangeSet({ { 94, 95 } })) })
- .Overlaps(TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
- RangeSet({ { 175, 265 } }))));
-
- ASSERT_TRUE(SourceInfo("a6cbdf3f416960f02189d3a814ec7e9e95c44a0d",
- RangeSet({ { 81, 175 }, { 265, 266 } }), // source ranges
- RangeSet({ { 0, 94 }, { 95, 96 } }), // source location
- { StashInfo("9eedf00d11061549e32503cadf054ec6fbfa7a23",
- RangeSet({ { 94, 95 } })) })
- .Overlaps(TargetInfo("1d74d1a60332fd38cf9405f1bae67917888da6cb",
- RangeSet({ { 265, 266 } }))));
-}
-
-// The block size should be specified by the caller of ReadAll (i.e. from Command instance during
-// normal run).
-constexpr size_t kBlockSize = 4096;
-
-TEST(SourceInfoTest, ReadAll) {
- // "2727756cfee3fbfe24bf5650123fd7743d7b3465" is the SHA-1 hex digest of 8192 * 'a'.
- const SourceInfo source("2727756cfee3fbfe24bf5650123fd7743d7b3465", RangeSet({ { 0, 2 } }), {},
- {});
- auto block_reader = [](const RangeSet& src, std::vector<uint8_t>* block_buffer) -> int {
- std::fill_n(block_buffer->begin(), src.blocks() * kBlockSize, 'a');
- return 0;
- };
- auto stash_reader = [](const std::string&, std::vector<uint8_t>*) -> int { return 0; };
- std::vector<uint8_t> buffer(source.blocks() * kBlockSize);
- ASSERT_TRUE(source.ReadAll(&buffer, kBlockSize, block_reader, stash_reader));
- ASSERT_EQ(source.blocks() * kBlockSize, buffer.size());
-
- uint8_t digest[SHA_DIGEST_LENGTH];
- SHA1(buffer.data(), buffer.size(), digest);
- ASSERT_EQ(source.hash(), print_sha1(digest));
-}
-
-TEST(SourceInfoTest, ReadAll_WithStashes) {
- const SourceInfo source(
- // SHA-1 hex digest of 8192 * 'a' + 4096 * 'b'.
- "ee3ebea26130769c10ad13604712100346d48660", RangeSet({ { 0, 2 } }), RangeSet({ { 0, 2 } }),
- { StashInfo("1e41f7a59e80c6eb4dc043caae80d273f130bed8", RangeSet({ { 2, 3 } })) });
- auto block_reader = [](const RangeSet& src, std::vector<uint8_t>* block_buffer) -> int {
- std::fill_n(block_buffer->begin(), src.blocks() * kBlockSize, 'a');
- return 0;
- };
- auto stash_reader = [](const std::string&, std::vector<uint8_t>* stash_buffer) -> int {
- std::fill_n(stash_buffer->begin(), kBlockSize, 'b');
- return 0;
- };
- std::vector<uint8_t> buffer(source.blocks() * kBlockSize);
- ASSERT_TRUE(source.ReadAll(&buffer, kBlockSize, block_reader, stash_reader));
- ASSERT_EQ(source.blocks() * kBlockSize, buffer.size());
-
- uint8_t digest[SHA_DIGEST_LENGTH];
- SHA1(buffer.data(), buffer.size(), digest);
- ASSERT_EQ(source.hash(), print_sha1(digest));
-}
-
-TEST(SourceInfoTest, ReadAll_BufferTooSmall) {
- const SourceInfo source("2727756cfee3fbfe24bf5650123fd7743d7b3465", RangeSet({ { 0, 2 } }), {},
- {});
- auto block_reader = [](const RangeSet&, std::vector<uint8_t>*) -> int { return 0; };
- auto stash_reader = [](const std::string&, std::vector<uint8_t>*) -> int { return 0; };
- std::vector<uint8_t> buffer(source.blocks() * kBlockSize - 1);
- ASSERT_FALSE(source.ReadAll(&buffer, kBlockSize, block_reader, stash_reader));
-}
-
-TEST(SourceInfoTest, ReadAll_FailingReader) {
- const SourceInfo source(
- "ee3ebea26130769c10ad13604712100346d48660", RangeSet({ { 0, 2 } }), RangeSet({ { 0, 2 } }),
- { StashInfo("1e41f7a59e80c6eb4dc043caae80d273f130bed8", RangeSet({ { 2, 3 } })) });
- std::vector<uint8_t> buffer(source.blocks() * kBlockSize);
- auto failing_block_reader = [](const RangeSet&, std::vector<uint8_t>*) -> int { return -1; };
- auto stash_reader = [](const std::string&, std::vector<uint8_t>*) -> int { return 0; };
- ASSERT_FALSE(source.ReadAll(&buffer, kBlockSize, failing_block_reader, stash_reader));
-
- auto block_reader = [](const RangeSet&, std::vector<uint8_t>*) -> int { return 0; };
- auto failing_stash_reader = [](const std::string&, std::vector<uint8_t>*) -> int { return -1; };
- ASSERT_FALSE(source.ReadAll(&buffer, kBlockSize, block_reader, failing_stash_reader));
-}
-
-TEST(TransferListTest, Parse) {
- std::vector<std::string> input_lines{
- "4", // version
- "2", // total blocks
- "1", // max stashed entries
- "1", // max stashed blocks
- "stash 1d74d1a60332fd38cf9405f1bae67917888da6cb 2,0,1",
- "move 1d74d1a60332fd38cf9405f1bae67917888da6cb 2,0,1 1 2,0,1",
- };
-
- std::string err;
- TransferList transfer_list = TransferList::Parse(android::base::Join(input_lines, '\n'), &err);
- ASSERT_TRUE(static_cast<bool>(transfer_list));
- ASSERT_EQ(4, transfer_list.version());
- ASSERT_EQ(2, transfer_list.total_blocks());
- ASSERT_EQ(1, transfer_list.stash_max_entries());
- ASSERT_EQ(1, transfer_list.stash_max_blocks());
- ASSERT_EQ(2U, transfer_list.commands().size());
- ASSERT_EQ(Command::Type::STASH, transfer_list.commands()[0].type());
- ASSERT_EQ(Command::Type::MOVE, transfer_list.commands()[1].type());
-}
-
-TEST(TransferListTest, Parse_InvalidCommand) {
- std::vector<std::string> input_lines{
- "4", // version
- "2", // total blocks
- "1", // max stashed entries
- "1", // max stashed blocks
- "stash 1d74d1a60332fd38cf9405f1bae67917888da6cb 2,0,1",
- "move 1d74d1a60332fd38cf9405f1bae67917888da6cb 2,0,1 1",
- };
-
- std::string err;
- TransferList transfer_list = TransferList::Parse(android::base::Join(input_lines, '\n'), &err);
- ASSERT_FALSE(static_cast<bool>(transfer_list));
-}
-
-TEST(TransferListTest, Parse_ZeroTotalBlocks) {
- std::vector<std::string> input_lines{
- "4", // version
- "0", // total blocks
- "0", // max stashed entries
- "0", // max stashed blocks
- };
-
- std::string err;
- TransferList transfer_list = TransferList::Parse(android::base::Join(input_lines, '\n'), &err);
- ASSERT_TRUE(static_cast<bool>(transfer_list));
- ASSERT_EQ(4, transfer_list.version());
- ASSERT_EQ(0, transfer_list.total_blocks());
- ASSERT_EQ(0, transfer_list.stash_max_entries());
- ASSERT_EQ(0, transfer_list.stash_max_blocks());
- ASSERT_TRUE(transfer_list.commands().empty());
-}
diff --git a/tests/unit/edify_test.cpp b/tests/unit/edify_test.cpp
deleted file mode 100644
index 8397bd3..0000000
--- a/tests/unit/edify_test.cpp
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <memory>
-#include <string>
-
-#include <gtest/gtest.h>
-
-#include "edify/expr.h"
-
-static void expect(const std::string& expr_str, const char* expected) {
- std::unique_ptr<Expr> e;
- int error_count = 0;
- EXPECT_EQ(0, ParseString(expr_str, &e, &error_count));
- EXPECT_EQ(0, error_count);
-
- State state(expr_str, nullptr);
-
- std::string result;
- bool status = Evaluate(&state, e, &result);
-
- if (expected == nullptr) {
- EXPECT_FALSE(status);
- } else {
- EXPECT_STREQ(expected, result.c_str());
- }
-}
-
-class EdifyTest : public ::testing::Test {
- protected:
- void SetUp() {
- RegisterBuiltins();
- }
-};
-
-TEST_F(EdifyTest, parsing) {
- expect("a", "a");
- expect("\"a\"", "a");
- expect("\"\\x61\"", "a");
- expect("# this is a comment\n"
- " a\n"
- " \n",
- "a");
-}
-
-TEST_F(EdifyTest, sequence) {
- // sequence operator
- expect("a; b; c", "c");
-}
-
-TEST_F(EdifyTest, concat) {
- // string concat operator
- expect("a + b", "ab");
- expect("a + \n \"b\"", "ab");
- expect("a + b +\nc\n", "abc");
-
- // string concat function
- expect("concat(a, b)", "ab");
- expect("concat(a,\n \"b\")", "ab");
- expect("concat(a + b,\nc,\"d\")", "abcd");
- expect("\"concat\"(a + b,\nc,\"d\")", "abcd");
-}
-
-TEST_F(EdifyTest, logical) {
- // logical and
- expect("a && b", "b");
- expect("a && \"\"", "");
- expect("\"\" && b", "");
- expect("\"\" && \"\"", "");
- expect("\"\" && abort()", ""); // test short-circuiting
- expect("t && abort()", nullptr);
-
- // logical or
- expect("a || b", "a");
- expect("a || \"\"", "a");
- expect("\"\" || b", "b");
- expect("\"\" || \"\"", "");
- expect("a || abort()", "a"); // test short-circuiting
- expect("\"\" || abort()", NULL);
-
- // logical not
- expect("!a", "");
- expect("! \"\"", "t");
- expect("!!a", "t");
-}
-
-TEST_F(EdifyTest, precedence) {
- // precedence
- expect("\"\" == \"\" && b", "b");
- expect("a + b == ab", "t");
- expect("ab == a + b", "t");
- expect("a + (b == ab)", "a");
- expect("(ab == a) + b", "b");
-}
-
-TEST_F(EdifyTest, substring) {
- // substring function
- expect("is_substring(cad, abracadabra)", "t");
- expect("is_substring(abrac, abracadabra)", "t");
- expect("is_substring(dabra, abracadabra)", "t");
- expect("is_substring(cad, abracxadabra)", "");
- expect("is_substring(abrac, axbracadabra)", "");
- expect("is_substring(dabra, abracadabrxa)", "");
-}
-
-TEST_F(EdifyTest, ifelse) {
- // ifelse function
- expect("ifelse(t, yes, no)", "yes");
- expect("ifelse(!t, yes, no)", "no");
- expect("ifelse(t, yes, abort())", "yes");
- expect("ifelse(!t, abort(), no)", "no");
-}
-
-TEST_F(EdifyTest, if_statement) {
- // if "statements"
- expect("if t then yes else no endif", "yes");
- expect("if \"\" then yes else no endif", "no");
- expect("if \"\" then yes endif", "");
- expect("if \"\"; t then yes endif", "yes");
-}
-
-TEST_F(EdifyTest, comparison) {
- // numeric comparisons
- expect("less_than_int(3, 14)", "t");
- expect("less_than_int(14, 3)", "");
- expect("less_than_int(x, 3)", "");
- expect("less_than_int(3, x)", "");
- expect("greater_than_int(3, 14)", "");
- expect("greater_than_int(14, 3)", "t");
- expect("greater_than_int(x, 3)", "");
- expect("greater_than_int(3, x)", "");
-}
-
-TEST_F(EdifyTest, big_string) {
- expect(std::string(8192, 's'), std::string(8192, 's').c_str());
-}
-
-TEST_F(EdifyTest, unknown_function) {
- const char* script1 = "unknown_function()";
- std::unique_ptr<Expr> expr;
- int error_count = 0;
- EXPECT_EQ(1, ParseString(script1, &expr, &error_count));
- EXPECT_EQ(1, error_count);
-
- const char* script2 = "abc; unknown_function()";
- error_count = 0;
- EXPECT_EQ(1, ParseString(script2, &expr, &error_count));
- EXPECT_EQ(1, error_count);
-
- const char* script3 = "unknown_function1() || yes";
- error_count = 0;
- EXPECT_EQ(1, ParseString(script3, &expr, &error_count));
- EXPECT_EQ(1, error_count);
-}
diff --git a/tests/unit/host/imgdiff_test.cpp b/tests/unit/host/imgdiff_test.cpp
deleted file mode 100644
index 978ac7c..0000000
--- a/tests/unit/host/imgdiff_test.cpp
+++ /dev/null
@@ -1,1113 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-
-#include <algorithm>
-#include <string>
-#include <tuple>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/memory.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <applypatch/imgdiff.h>
-#include <applypatch/imgdiff_image.h>
-#include <applypatch/imgpatch.h>
-#include <gtest/gtest.h>
-#include <ziparchive/zip_writer.h>
-
-#include "common/test_constants.h"
-
-using android::base::get_unaligned;
-
-static void verify_patch_header(const std::string& patch, size_t* num_normal, size_t* num_raw,
- size_t* num_deflate) {
- const size_t size = patch.size();
- const char* data = patch.data();
-
- ASSERT_GE(size, 12U);
- ASSERT_EQ("IMGDIFF2", std::string(data, 8));
-
- const int num_chunks = get_unaligned<int32_t>(data + 8);
- ASSERT_GE(num_chunks, 0);
-
- size_t normal = 0;
- size_t raw = 0;
- size_t deflate = 0;
-
- size_t pos = 12;
- for (int i = 0; i < num_chunks; ++i) {
- ASSERT_LE(pos + 4, size);
- int type = get_unaligned<int32_t>(data + pos);
- pos += 4;
- if (type == CHUNK_NORMAL) {
- pos += 24;
- ASSERT_LE(pos, size);
- normal++;
- } else if (type == CHUNK_RAW) {
- ASSERT_LE(pos + 4, size);
- ssize_t data_len = get_unaligned<int32_t>(data + pos);
- ASSERT_GT(data_len, 0);
- pos += 4 + data_len;
- ASSERT_LE(pos, size);
- raw++;
- } else if (type == CHUNK_DEFLATE) {
- pos += 60;
- ASSERT_LE(pos, size);
- deflate++;
- } else {
- FAIL() << "Invalid patch type: " << type;
- }
- }
-
- if (num_normal != nullptr) *num_normal = normal;
- if (num_raw != nullptr) *num_raw = raw;
- if (num_deflate != nullptr) *num_deflate = deflate;
-}
-
-static void GenerateTarget(const std::string& src, const std::string& patch, std::string* patched) {
- patched->clear();
- ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
- reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
- [&](const unsigned char* data, size_t len) {
- patched->append(reinterpret_cast<const char*>(data), len);
- return len;
- }));
-}
-
-static void verify_patched_image(const std::string& src, const std::string& patch,
- const std::string& tgt) {
- std::string patched;
- GenerateTarget(src, patch, &patched);
- ASSERT_EQ(tgt, patched);
-}
-
-TEST(ImgdiffTest, invalid_args) {
- // Insufficient inputs.
- ASSERT_EQ(2, imgdiff(1, (const char* []){ "imgdiff" }));
- ASSERT_EQ(2, imgdiff(2, (const char* []){ "imgdiff", "-z" }));
- ASSERT_EQ(2, imgdiff(2, (const char* []){ "imgdiff", "-b" }));
- ASSERT_EQ(2, imgdiff(3, (const char* []){ "imgdiff", "-z", "-b" }));
-
- // Failed to read bonus file.
- ASSERT_EQ(1, imgdiff(3, (const char* []){ "imgdiff", "-b", "doesntexist" }));
-
- // Failed to read input files.
- ASSERT_EQ(1, imgdiff(4, (const char* []){ "imgdiff", "doesntexist", "doesntexist", "output" }));
- ASSERT_EQ(
- 1, imgdiff(5, (const char* []){ "imgdiff", "-z", "doesntexist", "doesntexist", "output" }));
-}
-
-TEST(ImgdiffTest, image_mode_smoke) {
- // Random bytes.
- const std::string src("abcdefg");
- TemporaryFile src_file;
- ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
-
- const std::string tgt("abcdefgxyz");
- TemporaryFile tgt_file;
- ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
-
- TemporaryFile patch_file;
- std::vector<const char*> args = {
- "imgdiff", src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- // Verify.
- std::string patch;
- ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
-
- // Expect one CHUNK_RAW entry.
- size_t num_normal;
- size_t num_raw;
- size_t num_deflate;
- verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
- ASSERT_EQ(0U, num_normal);
- ASSERT_EQ(0U, num_deflate);
- ASSERT_EQ(1U, num_raw);
-
- verify_patched_image(src, patch, tgt);
-}
-
-TEST(ImgdiffTest, zip_mode_smoke_store) {
- // Construct src and tgt zip files.
- TemporaryFile src_file;
- FILE* src_file_ptr = fdopen(src_file.release(), "wb");
- ZipWriter src_writer(src_file_ptr);
- ASSERT_EQ(0, src_writer.StartEntry("file1.txt", 0)); // Store mode.
- const std::string src_content("abcdefg");
- ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
- ASSERT_EQ(0, src_writer.FinishEntry());
- ASSERT_EQ(0, src_writer.Finish());
- ASSERT_EQ(0, fclose(src_file_ptr));
-
- TemporaryFile tgt_file;
- FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
- ZipWriter tgt_writer(tgt_file_ptr);
- ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", 0)); // Store mode.
- const std::string tgt_content("abcdefgxyz");
- ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
- ASSERT_EQ(0, tgt_writer.FinishEntry());
- ASSERT_EQ(0, tgt_writer.Finish());
- ASSERT_EQ(0, fclose(tgt_file_ptr));
-
- // Compute patch.
- TemporaryFile patch_file;
- std::vector<const char*> args = {
- "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- // Verify.
- std::string tgt;
- ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
- std::string src;
- ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
- std::string patch;
- ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
-
- // Expect one CHUNK_RAW entry.
- size_t num_normal;
- size_t num_raw;
- size_t num_deflate;
- verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
- ASSERT_EQ(0U, num_normal);
- ASSERT_EQ(0U, num_deflate);
- ASSERT_EQ(1U, num_raw);
-
- verify_patched_image(src, patch, tgt);
-}
-
-TEST(ImgdiffTest, zip_mode_smoke_compressed) {
- // Generate 1 block of random data.
- std::string random_data;
- random_data.reserve(4096);
- generate_n(back_inserter(random_data), 4096, []() { return rand() % 256; });
-
- // Construct src and tgt zip files.
- TemporaryFile src_file;
- FILE* src_file_ptr = fdopen(src_file.release(), "wb");
- ZipWriter src_writer(src_file_ptr);
- ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress));
- const std::string src_content = random_data;
- ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
- ASSERT_EQ(0, src_writer.FinishEntry());
- ASSERT_EQ(0, src_writer.Finish());
- ASSERT_EQ(0, fclose(src_file_ptr));
-
- TemporaryFile tgt_file;
- FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
- ZipWriter tgt_writer(tgt_file_ptr);
- ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress));
- const std::string tgt_content = random_data + "extra contents";
- ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
- ASSERT_EQ(0, tgt_writer.FinishEntry());
- ASSERT_EQ(0, tgt_writer.Finish());
- ASSERT_EQ(0, fclose(tgt_file_ptr));
-
- // Compute patch.
- TemporaryFile patch_file;
- std::vector<const char*> args = {
- "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- // Verify.
- std::string tgt;
- ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
- std::string src;
- ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
- std::string patch;
- ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
-
- // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
- size_t num_normal;
- size_t num_raw;
- size_t num_deflate;
- verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
- ASSERT_EQ(0U, num_normal);
- ASSERT_EQ(1U, num_deflate);
- ASSERT_EQ(2U, num_raw);
-
- verify_patched_image(src, patch, tgt);
-}
-
-TEST(ImgdiffTest, zip_mode_empty_target) {
- TemporaryFile src_file;
- FILE* src_file_ptr = fdopen(src_file.release(), "wb");
- ZipWriter src_writer(src_file_ptr);
- ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress));
- const std::string src_content = "abcdefg";
- ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
- ASSERT_EQ(0, src_writer.FinishEntry());
- ASSERT_EQ(0, src_writer.Finish());
- ASSERT_EQ(0, fclose(src_file_ptr));
-
- // Construct a empty entry in the target zip.
- TemporaryFile tgt_file;
- FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
- ZipWriter tgt_writer(tgt_file_ptr);
- ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress));
- const std::string tgt_content;
- ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
- ASSERT_EQ(0, tgt_writer.FinishEntry());
- ASSERT_EQ(0, tgt_writer.Finish());
-
- // Compute patch.
- TemporaryFile patch_file;
- std::vector<const char*> args = {
- "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- // Verify.
- std::string tgt;
- ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
- std::string src;
- ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
- std::string patch;
- ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
-
- verify_patched_image(src, patch, tgt);
-}
-
-TEST(ImgdiffTest, zip_mode_smoke_trailer_zeros) {
- // Generate 1 block of random data.
- std::string random_data;
- random_data.reserve(4096);
- generate_n(back_inserter(random_data), 4096, []() { return rand() % 256; });
-
- // Construct src and tgt zip files.
- TemporaryFile src_file;
- FILE* src_file_ptr = fdopen(src_file.release(), "wb");
- ZipWriter src_writer(src_file_ptr);
- ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress));
- const std::string src_content = random_data;
- ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
- ASSERT_EQ(0, src_writer.FinishEntry());
- ASSERT_EQ(0, src_writer.Finish());
- ASSERT_EQ(0, fclose(src_file_ptr));
-
- TemporaryFile tgt_file;
- FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
- ZipWriter tgt_writer(tgt_file_ptr);
- ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress));
- const std::string tgt_content = random_data + "abcdefg";
- ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
- ASSERT_EQ(0, tgt_writer.FinishEntry());
- ASSERT_EQ(0, tgt_writer.Finish());
- // Add trailing zeros to the target zip file.
- std::vector<uint8_t> zeros(10);
- ASSERT_EQ(zeros.size(), fwrite(zeros.data(), sizeof(uint8_t), zeros.size(), tgt_file_ptr));
- ASSERT_EQ(0, fclose(tgt_file_ptr));
-
- // Compute patch.
- TemporaryFile patch_file;
- std::vector<const char*> args = {
- "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- // Verify.
- std::string tgt;
- ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
- std::string src;
- ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
- std::string patch;
- ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
-
- // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
- size_t num_normal;
- size_t num_raw;
- size_t num_deflate;
- verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
- ASSERT_EQ(0U, num_normal);
- ASSERT_EQ(1U, num_deflate);
- ASSERT_EQ(2U, num_raw);
-
- verify_patched_image(src, patch, tgt);
-}
-
-TEST(ImgdiffTest, image_mode_simple) {
- std::string gzipped_source_path = from_testdata_base("gzipped_source");
- std::string gzipped_source;
- ASSERT_TRUE(android::base::ReadFileToString(gzipped_source_path, &gzipped_source));
-
- const std::string src = "abcdefg" + gzipped_source;
- TemporaryFile src_file;
- ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
-
- std::string gzipped_target_path = from_testdata_base("gzipped_target");
- std::string gzipped_target;
- ASSERT_TRUE(android::base::ReadFileToString(gzipped_target_path, &gzipped_target));
- const std::string tgt = "abcdefgxyz" + gzipped_target;
-
- TemporaryFile tgt_file;
- ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
-
- TemporaryFile patch_file;
- std::vector<const char*> args = {
- "imgdiff", src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- // Verify.
- std::string patch;
- ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
-
- // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
- size_t num_normal;
- size_t num_raw;
- size_t num_deflate;
- verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
- ASSERT_EQ(0U, num_normal);
- ASSERT_EQ(1U, num_deflate);
- ASSERT_EQ(2U, num_raw);
-
- verify_patched_image(src, patch, tgt);
-}
-
-TEST(ImgdiffTest, image_mode_bad_gzip) {
- // Modify the uncompressed length in the gzip footer.
- const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
- 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
- '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac',
- '\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03',
- '\xff', '\xff', '\xff' };
- const std::string src(src_data.cbegin(), src_data.cend());
- TemporaryFile src_file;
- ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
-
- // Modify the uncompressed length in the gzip footer.
- const std::vector<char> tgt_data = {
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
- '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
- '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\xff', '\xff', '\xff'
- };
- const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
- TemporaryFile tgt_file;
- ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
-
- TemporaryFile patch_file;
- std::vector<const char*> args = {
- "imgdiff", src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- // Verify.
- std::string patch;
- ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
- verify_patched_image(src, patch, tgt);
-}
-
-TEST(ImgdiffTest, image_mode_different_num_chunks) {
- // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd) + gzipped "test".
- const std::vector<char> src_data = {
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\x1f', '\x8b', '\x08',
- '\x00', '\xc4', '\x1e', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac', '\x02',
- '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03', '\x00', '\x00', '\x00', '\x1f', '\x8b',
- '\x08', '\x00', '\xb2', '\x3a', '\x53', '\x58', '\x00', '\x03', '\x2b', '\x49', '\x2d',
- '\x2e', '\x01', '\x00', '\x0c', '\x7e', '\x7f', '\xd8', '\x04', '\x00', '\x00', '\x00'
- };
- const std::string src(src_data.cbegin(), src_data.cend());
- TemporaryFile src_file;
- ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
-
- // tgt: "abcdefgxyz" + gzipped "xxyyzz".
- const std::vector<char> tgt_data = {
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
- '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
- '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00'
- };
- const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
- TemporaryFile tgt_file;
- ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
-
- TemporaryFile patch_file;
- std::vector<const char*> args = {
- "imgdiff", src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(1, imgdiff(args.size(), args.data()));
-}
-
-TEST(ImgdiffTest, image_mode_merge_chunks) {
- // src: "abcdefg" + gzipped_source.
- std::string gzipped_source_path = from_testdata_base("gzipped_source");
- std::string gzipped_source;
- ASSERT_TRUE(android::base::ReadFileToString(gzipped_source_path, &gzipped_source));
-
- const std::string src = "abcdefg" + gzipped_source;
- TemporaryFile src_file;
- ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
-
- // tgt: gzipped_target + "abcdefgxyz".
- std::string gzipped_target_path = from_testdata_base("gzipped_target");
- std::string gzipped_target;
- ASSERT_TRUE(android::base::ReadFileToString(gzipped_target_path, &gzipped_target));
-
- const std::string tgt = gzipped_target + "abcdefgxyz";
- TemporaryFile tgt_file;
- ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
-
- // Since a gzipped entry will become CHUNK_RAW (header) + CHUNK_DEFLATE (data) +
- // CHUNK_RAW (footer), they both should contain the same chunk types after merging.
-
- TemporaryFile patch_file;
- std::vector<const char*> args = {
- "imgdiff", src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- // Verify.
- std::string patch;
- ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
-
- // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
- size_t num_normal;
- size_t num_raw;
- size_t num_deflate;
- verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
- ASSERT_EQ(0U, num_normal);
- ASSERT_EQ(1U, num_deflate);
- ASSERT_EQ(2U, num_raw);
-
- verify_patched_image(src, patch, tgt);
-}
-
-TEST(ImgdiffTest, image_mode_spurious_magic) {
- // src: "abcdefgh" + '0x1f8b0b00' + some bytes.
- const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
- 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
- '\x53', '\x58', 't', 'e', 's', 't' };
- const std::string src(src_data.cbegin(), src_data.cend());
- TemporaryFile src_file;
- ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
-
- // tgt: "abcdefgxyz".
- const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
- const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
- TemporaryFile tgt_file;
- ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
-
- TemporaryFile patch_file;
- std::vector<const char*> args = {
- "imgdiff", src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- // Verify.
- std::string patch;
- ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
-
- // Expect one CHUNK_RAW (header) entry.
- size_t num_normal;
- size_t num_raw;
- size_t num_deflate;
- verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
- ASSERT_EQ(0U, num_normal);
- ASSERT_EQ(0U, num_deflate);
- ASSERT_EQ(1U, num_raw);
-
- verify_patched_image(src, patch, tgt);
-}
-
-TEST(ImgdiffTest, image_mode_short_input1) {
- // src: "abcdefgh" + '0x1f8b0b'.
- const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f',
- 'g', 'h', '\x1f', '\x8b', '\x08' };
- const std::string src(src_data.cbegin(), src_data.cend());
- TemporaryFile src_file;
- ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
-
- // tgt: "abcdefgxyz".
- const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
- const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
- TemporaryFile tgt_file;
- ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
-
- TemporaryFile patch_file;
- std::vector<const char*> args = {
- "imgdiff", src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- // Verify.
- std::string patch;
- ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
-
- // Expect one CHUNK_RAW (header) entry.
- size_t num_normal;
- size_t num_raw;
- size_t num_deflate;
- verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
- ASSERT_EQ(0U, num_normal);
- ASSERT_EQ(0U, num_deflate);
- ASSERT_EQ(1U, num_raw);
-
- verify_patched_image(src, patch, tgt);
-}
-
-TEST(ImgdiffTest, image_mode_short_input2) {
- // src: "abcdefgh" + '0x1f8b0b00'.
- const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f',
- 'g', 'h', '\x1f', '\x8b', '\x08', '\x00' };
- const std::string src(src_data.cbegin(), src_data.cend());
- TemporaryFile src_file;
- ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
-
- // tgt: "abcdefgxyz".
- const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
- const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
- TemporaryFile tgt_file;
- ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
-
- TemporaryFile patch_file;
- std::vector<const char*> args = {
- "imgdiff", src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- // Verify.
- std::string patch;
- ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
-
- // Expect one CHUNK_RAW (header) entry.
- size_t num_normal;
- size_t num_raw;
- size_t num_deflate;
- verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
- ASSERT_EQ(0U, num_normal);
- ASSERT_EQ(0U, num_deflate);
- ASSERT_EQ(1U, num_raw);
-
- verify_patched_image(src, patch, tgt);
-}
-
-TEST(ImgdiffTest, image_mode_single_entry_long) {
- // src: "abcdefgh" + '0x1f8b0b00' + some bytes.
- const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
- 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
- '\x53', '\x58', 't', 'e', 's', 't' };
- const std::string src(src_data.cbegin(), src_data.cend());
- TemporaryFile src_file;
- ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
-
- // tgt: "abcdefgxyz" + 200 bytes.
- std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
- tgt_data.resize(tgt_data.size() + 200);
-
- const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
- TemporaryFile tgt_file;
- ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
-
- TemporaryFile patch_file;
- std::vector<const char*> args = {
- "imgdiff", src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- // Verify.
- std::string patch;
- ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
-
- // Expect one CHUNK_NORMAL entry, since it's exceeding the 160-byte limit for RAW.
- size_t num_normal;
- size_t num_raw;
- size_t num_deflate;
- verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
- ASSERT_EQ(1U, num_normal);
- ASSERT_EQ(0U, num_deflate);
- ASSERT_EQ(0U, num_raw);
-
- verify_patched_image(src, patch, tgt);
-}
-
-TEST(ImgpatchTest, image_mode_patch_corruption) {
- // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd).
- const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
- 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
- '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac',
- '\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03',
- '\x00', '\x00', '\x00' };
- const std::string src(src_data.cbegin(), src_data.cend());
- TemporaryFile src_file;
- ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
-
- // tgt: "abcdefgxyz" + gzipped "xxyyzz".
- const std::vector<char> tgt_data = {
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
- '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
- '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00'
- };
- const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
- TemporaryFile tgt_file;
- ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
-
- TemporaryFile patch_file;
- std::vector<const char*> args = {
- "imgdiff", src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- // Verify.
- std::string patch;
- ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
- verify_patched_image(src, patch, tgt);
-
- // Corrupt the end of the patch and expect the ApplyImagePatch to fail.
- patch.insert(patch.end() - 10, 10, '0');
- ASSERT_EQ(-1, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
- reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
- [](const unsigned char* /*data*/, size_t len) { return len; }));
-}
-
-static void construct_store_entry(const std::vector<std::tuple<std::string, size_t, char>>& info,
- ZipWriter* writer) {
- for (auto& t : info) {
- // Create t(1) blocks of t(2), and write the data to t(0)
- ASSERT_EQ(0, writer->StartEntry(std::get<0>(t).c_str(), 0));
- const std::string content(std::get<1>(t) * 4096, std::get<2>(t));
- ASSERT_EQ(0, writer->WriteBytes(content.data(), content.size()));
- ASSERT_EQ(0, writer->FinishEntry());
- }
-}
-
-static void construct_deflate_entry(const std::vector<std::tuple<std::string, size_t, size_t>>& info,
- ZipWriter* writer, const std::string& data) {
- for (auto& t : info) {
- // t(0): entry_name; t(1): block offset; t(2) length in blocks.
- ASSERT_EQ(0, writer->StartEntry(std::get<0>(t).c_str(), ZipWriter::kCompress));
- ASSERT_EQ(0, writer->WriteBytes(data.data() + std::get<1>(t) * 4096, std::get<2>(t) * 4096));
- ASSERT_EQ(0, writer->FinishEntry());
- }
-}
-
-// 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_src;
- 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);
- patched += split_tgt;
- }
-
- // Verify we can get back the original target image.
- ASSERT_EQ(tgt, patched);
-}
-
-std::vector<ImageChunk> ConstructImageChunks(
- const std::vector<uint8_t>& content, const std::vector<std::tuple<std::string, size_t>>& info) {
- std::vector<ImageChunk> chunks;
- size_t start = 0;
- for (const auto& t : info) {
- size_t length = std::get<1>(t);
- chunks.emplace_back(CHUNK_NORMAL, start, &content, length, std::get<0>(t));
- start += length;
- }
-
- return chunks;
-}
-
-TEST(ImgdiffTest, zip_mode_split_image_smoke) {
- std::vector<uint8_t> content;
- content.reserve(4096 * 50);
- uint8_t n = 0;
- generate_n(back_inserter(content), 4096 * 50, [&n]() { return n++ / 4096; });
-
- ZipModeImage tgt_image(false, 4096 * 10);
- std::vector<ImageChunk> tgt_chunks = ConstructImageChunks(content, { { "a", 100 },
- { "b", 4096 * 2 },
- { "c", 4096 * 3 },
- { "d", 300 },
- { "e-0", 4096 * 10 },
- { "e-1", 4096 * 5 },
- { "CD", 200 } });
- tgt_image.Initialize(std::move(tgt_chunks),
- std::vector<uint8_t>(content.begin(), content.begin() + 82520));
-
- tgt_image.DumpChunks();
-
- ZipModeImage src_image(true, 4096 * 10);
- std::vector<ImageChunk> src_chunks = ConstructImageChunks(content, { { "b", 4096 * 3 },
- { "c-0", 4096 * 10 },
- { "c-1", 4096 * 2 },
- { "a", 4096 * 5 },
- { "e-0", 4096 * 10 },
- { "e-1", 10000 },
- { "CD", 5000 } });
- src_image.Initialize(std::move(src_chunks),
- std::vector<uint8_t>(content.begin(), content.begin() + 137880));
-
- std::vector<ZipModeImage> split_tgt_images;
- std::vector<ZipModeImage> split_src_images;
- std::vector<SortedRangeSet> split_src_ranges;
-
- ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images,
- &split_src_images, &split_src_ranges);
-
- // src_piece 1: a 5 blocks, b 3 blocks
- // src_piece 2: c-0 10 blocks
- // src_piece 3: d 0 block, e-0 10 blocks
- // src_piece 4: e-1 2 blocks; CD 2 blocks
- ASSERT_EQ(split_tgt_images.size(), split_src_images.size());
- ASSERT_EQ(static_cast<size_t>(4), split_tgt_images.size());
-
- ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[0].NumOfChunks());
- ASSERT_EQ(static_cast<size_t>(12288), split_tgt_images[0][0].DataLengthForPatch());
- ASSERT_EQ("4,0,3,15,20", split_src_ranges[0].ToString());
-
- ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[1].NumOfChunks());
- ASSERT_EQ(static_cast<size_t>(12288), split_tgt_images[1][0].DataLengthForPatch());
- ASSERT_EQ("2,3,13", split_src_ranges[1].ToString());
-
- ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[2].NumOfChunks());
- ASSERT_EQ(static_cast<size_t>(40960), split_tgt_images[2][0].DataLengthForPatch());
- ASSERT_EQ("2,20,30", split_src_ranges[2].ToString());
-
- ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[3].NumOfChunks());
- ASSERT_EQ(static_cast<size_t>(16984), split_tgt_images[3][0].DataLengthForPatch());
- ASSERT_EQ("2,30,34", split_src_ranges[3].ToString());
-}
-
-TEST(ImgdiffTest, zip_mode_store_large_apk) {
- // Construct src and tgt zip files with limit = 10 blocks.
- // src tgt
- // 12 blocks 'd' 3 blocks 'a'
- // 8 blocks 'c' 3 blocks 'b'
- // 3 blocks 'b' 8 blocks 'c' (exceeds limit)
- // 3 blocks 'a' 12 blocks 'd' (exceeds limit)
- // 3 blocks 'e'
- TemporaryFile tgt_file;
- FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
- ZipWriter tgt_writer(tgt_file_ptr);
- construct_store_entry(
- { { "a", 3, 'a' }, { "b", 3, 'b' }, { "c", 8, 'c' }, { "d", 12, 'd' }, { "e", 3, 'e' } },
- &tgt_writer);
- ASSERT_EQ(0, tgt_writer.Finish());
- ASSERT_EQ(0, fclose(tgt_file_ptr));
-
- TemporaryFile src_file;
- FILE* src_file_ptr = fdopen(src_file.release(), "wb");
- ZipWriter src_writer(src_file_ptr);
- construct_store_entry({ { "d", 12, 'd' }, { "c", 8, 'c' }, { "b", 3, 'b' }, { "a", 3, 'a' } },
- &src_writer);
- ASSERT_EQ(0, src_writer.Finish());
- ASSERT_EQ(0, fclose(src_file_ptr));
-
- // Compute patch.
- TemporaryFile patch_file;
- TemporaryFile split_info_file;
- TemporaryDir debug_dir;
- std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
- std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
- std::vector<const char*> args = {
- "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(),
- src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- std::string tgt;
- ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
-
- // Expect 4 pieces of patch. (Roughly 3'a',3'b'; 8'c'; 10'd'; 2'd'3'e')
- GenerateAndCheckSplitTarget(debug_dir.path, 4, tgt);
-}
-
-TEST(ImgdiffTest, zip_mode_deflate_large_apk) {
- // Src and tgt zip files are constructed as follows.
- // src tgt
- // 22 blocks, "d" 4 blocks, "a"
- // 5 blocks, "b" 4 blocks, "b"
- // 3 blocks, "a" 8 blocks, "c" (exceeds limit)
- // 1 block, "g" 20 blocks, "d" (exceeds limit)
- // 8 blocks, "c" 2 blocks, "e"
- // 1 block, "f" 1 block , "f"
- std::string tgt_path = from_testdata_base("deflate_tgt.zip");
- std::string src_path = from_testdata_base("deflate_src.zip");
-
- ZipModeImage src_image(true, 10 * 4096);
- ZipModeImage tgt_image(false, 10 * 4096);
- ASSERT_TRUE(src_image.Initialize(src_path));
- ASSERT_TRUE(tgt_image.Initialize(tgt_path));
- ASSERT_TRUE(ZipModeImage::CheckAndProcessChunks(&tgt_image, &src_image));
-
- src_image.DumpChunks();
- tgt_image.DumpChunks();
-
- std::vector<ZipModeImage> split_tgt_images;
- std::vector<ZipModeImage> split_src_images;
- std::vector<SortedRangeSet> split_src_ranges;
- ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images,
- &split_src_images, &split_src_ranges);
-
- // Expected split images with limit = 10 blocks.
- // src_piece 0: a 3 blocks, b 5 blocks
- // src_piece 1: c 8 blocks
- // src_piece 2: d-0 10 block
- // src_piece 3: d-1 10 blocks
- // src_piece 4: e 1 block, CD
- ASSERT_EQ(split_tgt_images.size(), split_src_images.size());
- ASSERT_EQ(static_cast<size_t>(5), split_tgt_images.size());
-
- ASSERT_EQ(static_cast<size_t>(2), split_src_images[0].NumOfChunks());
- ASSERT_EQ("a", split_src_images[0][0].GetEntryName());
- ASSERT_EQ("b", split_src_images[0][1].GetEntryName());
-
- ASSERT_EQ(static_cast<size_t>(1), split_src_images[1].NumOfChunks());
- ASSERT_EQ("c", split_src_images[1][0].GetEntryName());
-
- ASSERT_EQ(static_cast<size_t>(0), split_src_images[2].NumOfChunks());
- ASSERT_EQ(static_cast<size_t>(0), split_src_images[3].NumOfChunks());
- ASSERT_EQ(static_cast<size_t>(0), split_src_images[4].NumOfChunks());
-
- // Compute patch.
- TemporaryFile patch_file;
- TemporaryFile split_info_file;
- TemporaryDir debug_dir;
- ASSERT_TRUE(ZipModeImage::GeneratePatches(split_tgt_images, split_src_images, split_src_ranges,
- patch_file.path, split_info_file.path, debug_dir.path));
-
- // Verify the content of split info.
- // Expect 5 pieces of patch. ["a","b"; "c"; "d-0"; "d-1"; "e"]
- std::string split_info_string;
- android::base::ReadFileToString(split_info_file.path, &split_info_string);
- std::vector<std::string> info_list =
- android::base::Split(android::base::Trim(split_info_string), "\n");
-
- ASSERT_EQ(static_cast<size_t>(7), info_list.size());
- ASSERT_EQ("2", android::base::Trim(info_list[0]));
- ASSERT_EQ("5", android::base::Trim(info_list[1]));
-
- std::string tgt;
- ASSERT_TRUE(android::base::ReadFileToString(tgt_path, &tgt));
- ASSERT_EQ(static_cast<size_t>(160385), tgt.size());
- std::vector<std::string> tgt_file_ranges = {
- "36864 2,22,31", "32768 2,31,40", "40960 2,0,11", "40960 2,11,21", "8833 4,21,22,40,41",
- };
-
- for (size_t i = 0; i < 5; i++) {
- struct stat st;
- std::string path = android::base::StringPrintf("%s/patch-%zu", debug_dir.path, i);
- ASSERT_EQ(0, stat(path.c_str(), &st));
- ASSERT_EQ(std::to_string(st.st_size) + " " + tgt_file_ranges[i],
- android::base::Trim(info_list[i + 2]));
- }
-
- GenerateAndCheckSplitTarget(debug_dir.path, 5, tgt);
-}
-
-TEST(ImgdiffTest, zip_mode_no_match_source) {
- // Generate 20 blocks of random data.
- std::string random_data;
- random_data.reserve(4096 * 20);
- generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; });
-
- TemporaryFile tgt_file;
- FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
- ZipWriter tgt_writer(tgt_file_ptr);
-
- construct_deflate_entry({ { "a", 0, 4 }, { "b", 5, 5 }, { "c", 11, 5 } }, &tgt_writer,
- random_data);
-
- ASSERT_EQ(0, tgt_writer.Finish());
- ASSERT_EQ(0, fclose(tgt_file_ptr));
-
- // We don't have a matching source entry.
- TemporaryFile src_file;
- FILE* src_file_ptr = fdopen(src_file.release(), "wb");
- ZipWriter src_writer(src_file_ptr);
- construct_store_entry({ { "d", 1, 'd' } }, &src_writer);
- ASSERT_EQ(0, src_writer.Finish());
- ASSERT_EQ(0, fclose(src_file_ptr));
-
- // Compute patch.
- TemporaryFile patch_file;
- TemporaryFile split_info_file;
- TemporaryDir debug_dir;
- std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
- std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
- std::vector<const char*> args = {
- "imgdiff", "-z", "--block-limit=10", debug_dir_arg.c_str(), split_info_arg.c_str(),
- src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- std::string tgt;
- ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
-
- // Expect 1 pieces of patch due to no matching source entry.
- GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt);
-}
-
-TEST(ImgdiffTest, zip_mode_large_enough_limit) {
- // Generate 20 blocks of random data.
- std::string random_data;
- random_data.reserve(4096 * 20);
- generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; });
-
- TemporaryFile tgt_file;
- FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
- ZipWriter tgt_writer(tgt_file_ptr);
-
- construct_deflate_entry({ { "a", 0, 10 }, { "b", 10, 5 } }, &tgt_writer, random_data);
-
- ASSERT_EQ(0, tgt_writer.Finish());
- ASSERT_EQ(0, fclose(tgt_file_ptr));
-
- // Construct 10 blocks of source.
- TemporaryFile src_file;
- FILE* src_file_ptr = fdopen(src_file.release(), "wb");
- ZipWriter src_writer(src_file_ptr);
- construct_deflate_entry({ { "a", 1, 10 } }, &src_writer, random_data);
- ASSERT_EQ(0, src_writer.Finish());
- ASSERT_EQ(0, fclose(src_file_ptr));
-
- // Compute patch with a limit of 20 blocks.
- TemporaryFile patch_file;
- TemporaryFile split_info_file;
- TemporaryDir debug_dir;
- std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
- std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
- std::vector<const char*> args = {
- "imgdiff", "-z", "--block-limit=20", split_info_arg.c_str(), debug_dir_arg.c_str(),
- src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- std::string tgt;
- ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
-
- // Expect 1 piece of patch since limit is larger than the zip file size.
- GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt);
-}
-
-TEST(ImgdiffTest, zip_mode_large_apk_small_target_chunk) {
- TemporaryFile tgt_file;
- FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
- ZipWriter tgt_writer(tgt_file_ptr);
-
- // The first entry is less than 4096 bytes, followed immediately by an entry that has a very
- // large counterpart in the source file. Therefore the first entry will be patched separately.
- std::string small_chunk("a", 2000);
- ASSERT_EQ(0, tgt_writer.StartEntry("a", 0));
- ASSERT_EQ(0, tgt_writer.WriteBytes(small_chunk.data(), small_chunk.size()));
- ASSERT_EQ(0, tgt_writer.FinishEntry());
- construct_store_entry(
- {
- { "b", 12, 'b' }, { "c", 3, 'c' },
- },
- &tgt_writer);
- ASSERT_EQ(0, tgt_writer.Finish());
- ASSERT_EQ(0, fclose(tgt_file_ptr));
-
- TemporaryFile src_file;
- FILE* src_file_ptr = fdopen(src_file.release(), "wb");
- ZipWriter src_writer(src_file_ptr);
- construct_store_entry({ { "a", 1, 'a' }, { "b", 13, 'b' }, { "c", 1, 'c' } }, &src_writer);
- ASSERT_EQ(0, src_writer.Finish());
- ASSERT_EQ(0, fclose(src_file_ptr));
-
- // Compute patch.
- TemporaryFile patch_file;
- TemporaryFile split_info_file;
- TemporaryDir debug_dir;
- std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
- std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
- std::vector<const char*> args = {
- "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(),
- src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- std::string tgt;
- ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
-
- // Expect three split src images:
- // src_piece 0: a 1 blocks
- // src_piece 1: b-0 10 blocks
- // src_piece 2: b-1 3 blocks, c 1 blocks, CD
- GenerateAndCheckSplitTarget(debug_dir.path, 3, tgt);
-}
-
-TEST(ImgdiffTest, zip_mode_large_apk_skipped_small_target_chunk) {
- TemporaryFile tgt_file;
- FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
- ZipWriter tgt_writer(tgt_file_ptr);
-
- construct_store_entry(
- {
- { "a", 11, 'a' },
- },
- &tgt_writer);
-
- // Construct a tiny target entry of 1 byte, which will be skipped due to the tail alignment of
- // the previous entry.
- std::string small_chunk("b", 1);
- ASSERT_EQ(0, tgt_writer.StartEntry("b", 0));
- ASSERT_EQ(0, tgt_writer.WriteBytes(small_chunk.data(), small_chunk.size()));
- ASSERT_EQ(0, tgt_writer.FinishEntry());
-
- ASSERT_EQ(0, tgt_writer.Finish());
- ASSERT_EQ(0, fclose(tgt_file_ptr));
-
- TemporaryFile src_file;
- FILE* src_file_ptr = fdopen(src_file.release(), "wb");
- ZipWriter src_writer(src_file_ptr);
- construct_store_entry(
- {
- { "a", 11, 'a' }, { "b", 11, 'b' },
- },
- &src_writer);
- ASSERT_EQ(0, src_writer.Finish());
- ASSERT_EQ(0, fclose(src_file_ptr));
-
- // Compute patch.
- TemporaryFile patch_file;
- TemporaryFile split_info_file;
- TemporaryDir debug_dir;
- std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
- std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
- std::vector<const char*> args = {
- "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(),
- src_file.path, tgt_file.path, patch_file.path,
- };
- ASSERT_EQ(0, imgdiff(args.size(), args.data()));
-
- std::string tgt;
- ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
-
- // Expect two split src images:
- // src_piece 0: a-0 10 blocks
- // src_piece 1: a-0 1 block, CD
- GenerateAndCheckSplitTarget(debug_dir.path, 2, tgt);
-}
diff --git a/tests/unit/host/update_simulator_test.cpp b/tests/unit/host/update_simulator_test.cpp
deleted file mode 100644
index 1603982..0000000
--- a/tests/unit/host/update_simulator_test.cpp
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <stdio.h>
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <bsdiff/bsdiff.h>
-#include <gtest/gtest.h>
-#include <ziparchive/zip_writer.h>
-
-#include "otautil/paths.h"
-#include "otautil/print_sha1.h"
-#include "updater/blockimg.h"
-#include "updater/build_info.h"
-#include "updater/install.h"
-#include "updater/simulator_runtime.h"
-#include "updater/target_files.h"
-#include "updater/updater.h"
-
-using std::string;
-
-// echo -n "system.img" > system.img && img2simg system.img sparse_system_string_.img 4096 &&
-// hexdump -v -e '" " 12/1 "0x%02x, " "\n"' sparse_system_string_.img
-// The total size of the result sparse image is 4136 bytes; and we can append 0s in the end to get
-// the full image.
-constexpr uint8_t SPARSE_SYSTEM_HEADER[] = {
- 0x3a, 0xff, 0x26, 0xed, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x0c, 0x00, 0x00, 0x10, 0x00,
- 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xca,
- 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x10, 0x00, 0x00, 0x73, 0x79, 0x73, 0x74, 0x65,
- 0x6d, 0x2e, 0x69, 0x6d, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-static void AddZipEntries(int fd, const std::map<string, string>& entries) {
- FILE* zip_file = fdopen(fd, "w");
- ZipWriter writer(zip_file);
- for (const auto& pair : entries) {
- ASSERT_EQ(0, writer.StartEntry(pair.first.c_str(), 0));
- ASSERT_EQ(0, writer.WriteBytes(pair.second.data(), pair.second.size()));
- ASSERT_EQ(0, writer.FinishEntry());
- }
- ASSERT_EQ(0, writer.Finish());
- ASSERT_EQ(0, fclose(zip_file));
-}
-
-static string CalculateSha1(const string& data) {
- uint8_t digest[SHA_DIGEST_LENGTH];
- SHA1(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), digest);
- return print_sha1(digest);
-}
-
-static void CreateBsdiffPatch(const string& src, const string& tgt, string* patch) {
- TemporaryFile patch_file;
- ASSERT_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src.data()), src.size(),
- reinterpret_cast<const uint8_t*>(tgt.data()), tgt.size(),
- patch_file.path, nullptr));
- ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, patch));
-}
-
-static void RunSimulation(std::string_view src_tf, std::string_view ota_package, bool expected) {
- TemporaryFile cmd_pipe;
- TemporaryFile temp_saved_source;
- TemporaryFile temp_last_command;
- TemporaryDir temp_stash_base;
-
- Paths::Get().set_cache_temp_source(temp_saved_source.path);
- Paths::Get().set_last_command_file(temp_last_command.path);
- Paths::Get().set_stash_directory_base(temp_stash_base.path);
-
- // Configure edify's functions.
- RegisterBuiltins();
- RegisterInstallFunctions();
- RegisterBlockImageFunctions();
-
- // Run the update simulation and check the result.
- TemporaryDir work_dir;
- BuildInfo build_info(work_dir.path, false);
- ASSERT_TRUE(build_info.ParseTargetFile(src_tf, false));
- Updater updater(std::make_unique<SimulatorRuntime>(&build_info));
- ASSERT_TRUE(updater.Init(cmd_pipe.release(), ota_package, false));
- ASSERT_EQ(expected, updater.RunUpdate());
- // TODO(xunchang) check the recovery&system has the expected contents.
-}
-
-class DISABLED_UpdateSimulatorTest : public ::testing::Test {
- protected:
- void SetUp() override {
- std::vector<string> props = {
- "import /oem/oem.prop oem*",
- "# begin build properties",
- "# autogenerated by buildinfo.sh",
- "ro.build.id=OPR1.170510.001",
- "ro.build.display.id=OPR1.170510.001 dev-keys",
- "ro.build.version.incremental=3993052",
- "ro.build.version.release=O",
- "ro.build.date=Wed May 10 11:10:29 UTC 2017",
- "ro.build.date.utc=1494414629",
- "ro.build.type=user",
- "ro.build.tags=dev-keys",
- "ro.build.flavor=angler-user",
- "ro.product.system.brand=google",
- "ro.product.system.name=angler",
- "ro.product.system.device=angler",
- };
- build_prop_string_ = android::base::Join(props, "\n");
-
- fstab_content_ = R"(
-#<src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>
-# More comments.....
-
-/dev/block/by-name/system /system ext4 ro,barrier=1 wait
-/dev/block/by-name/vendor /vendor ext4 ro wait,verify=/dev/metadata
-/dev/block/by-name/cache /cache ext4 noatime,errors=panic wait,check
-/dev/block/by-name/modem /firmware vfat ro,uid=1000,gid=1000, wait
-/dev/block/by-name/boot /boot emmc defaults defaults
-/dev/block/by-name/recovery /recovery emmc defaults defaults
-/dev/block/by-name/misc /misc emmc defaults
-/dev/block/by-name/modem /modem emmc defaults defaults)";
-
- raw_system_string_ = "system.img" + string(4086, '\0'); // raw image is 4096 bytes in total
- sparse_system_string_ = string(SPARSE_SYSTEM_HEADER, std::end(SPARSE_SYSTEM_HEADER)) +
- string(4136 - sizeof(SPARSE_SYSTEM_HEADER), '\0');
- }
-
- string build_prop_string_;
- string fstab_content_;
- string raw_system_string_;
- string sparse_system_string_;
-};
-
-TEST_F(DISABLED_UpdateSimulatorTest, TargetFile_ExtractImage) {
- TemporaryFile zip_file;
- AddZipEntries(zip_file.release(), { { "META/misc_info.txt", "extfs_sparse_flag=-s" },
- { "IMAGES/system.img", sparse_system_string_ } });
- TargetFile target_file(zip_file.path, false);
- ASSERT_TRUE(target_file.Open());
-
- TemporaryDir temp_dir;
- TemporaryFile raw_image;
- ASSERT_TRUE(target_file.ExtractImage(
- "IMAGES/system.img", FstabInfo("/dev/system", "system", "ext4"), temp_dir.path, &raw_image));
-
- // Check the raw image has expected contents.
- string content;
- ASSERT_TRUE(android::base::ReadFileToString(raw_image.path, &content));
- string expected_content = "system.img" + string(4086, '\0');
- ASSERT_EQ(expected_content, content);
-}
-
-TEST_F(DISABLED_UpdateSimulatorTest, TargetFile_ParseFstabInfo) {
- TemporaryFile zip_file;
- AddZipEntries(zip_file.release(),
- { { "META/misc_info.txt", "" },
- { "RECOVERY/RAMDISK/system/etc/recovery.fstab", fstab_content_ } });
- TargetFile target_file(zip_file.path, false);
- ASSERT_TRUE(target_file.Open());
-
- std::vector<FstabInfo> fstab_info;
- EXPECT_TRUE(target_file.ParseFstabInfo(&fstab_info));
-
- std::vector<std::vector<string>> transformed;
- std::transform(fstab_info.begin(), fstab_info.end(), std::back_inserter(transformed),
- [](const FstabInfo& info) {
- return std::vector<string>{ info.blockdev_name, info.mount_point, info.fs_type };
- });
-
- std::vector<std::vector<string>> expected = {
- { "/dev/block/by-name/system", "/system", "ext4" },
- { "/dev/block/by-name/vendor", "/vendor", "ext4" },
- { "/dev/block/by-name/cache", "/cache", "ext4" },
- { "/dev/block/by-name/boot", "/boot", "emmc" },
- { "/dev/block/by-name/recovery", "/recovery", "emmc" },
- { "/dev/block/by-name/misc", "/misc", "emmc" },
- { "/dev/block/by-name/modem", "/modem", "emmc" },
- };
- EXPECT_EQ(expected, transformed);
-}
-
-TEST_F(DISABLED_UpdateSimulatorTest, BuildInfo_ParseTargetFile) {
- std::map<string, string> entries = {
- { "META/misc_info.txt", "" },
- { "SYSTEM/build.prop", build_prop_string_ },
- { "RECOVERY/RAMDISK/system/etc/recovery.fstab", fstab_content_ },
- { "IMAGES/recovery.img", "" },
- { "IMAGES/boot.img", "" },
- { "IMAGES/misc.img", "" },
- { "IMAGES/system.map", "" },
- { "IMAGES/system.img", sparse_system_string_ },
- };
-
- TemporaryFile zip_file;
- AddZipEntries(zip_file.release(), entries);
-
- TemporaryDir temp_dir;
- BuildInfo build_info(temp_dir.path, false);
- ASSERT_TRUE(build_info.ParseTargetFile(zip_file.path, false));
-
- std::map<string, string> expected_result = {
- { "ro.build.id", "OPR1.170510.001" },
- { "ro.build.display.id", "OPR1.170510.001 dev-keys" },
- { "ro.build.version.incremental", "3993052" },
- { "ro.build.version.release", "O" },
- { "ro.build.date", "Wed May 10 11:10:29 UTC 2017" },
- { "ro.build.date.utc", "1494414629" },
- { "ro.build.type", "user" },
- { "ro.build.tags", "dev-keys" },
- { "ro.build.flavor", "angler-user" },
- { "ro.product.brand", "google" },
- { "ro.product.name", "angler" },
- { "ro.product.device", "angler" },
- };
-
- for (const auto& [key, value] : expected_result) {
- ASSERT_EQ(value, build_info.GetProperty(key, ""));
- }
-
- // Check that the temp files for each block device are created successfully.
- for (auto name : { "/dev/block/by-name/system", "/dev/block/by-name/recovery",
- "/dev/block/by-name/boot", "/dev/block/by-name/misc" }) {
- ASSERT_EQ(0, access(build_info.FindBlockDeviceName(name).c_str(), R_OK));
- }
-}
-
-TEST_F(DISABLED_UpdateSimulatorTest, RunUpdateSmoke) {
- string recovery_img_string = "recovery.img";
- string boot_img_string = "boot.img";
-
- std::map<string, string> src_entries{
- { "META/misc_info.txt", "extfs_sparse_flag=-s" },
- { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ },
- { "SYSTEM/build.prop", build_prop_string_ },
- { "IMAGES/recovery.img", "" },
- { "IMAGES/boot.img", boot_img_string },
- { "IMAGES/system.img", sparse_system_string_ },
- };
-
- // Construct the source target-files.
- TemporaryFile src_tf;
- AddZipEntries(src_tf.release(), src_entries);
-
- string recovery_from_boot;
- CreateBsdiffPatch(boot_img_string, recovery_img_string, &recovery_from_boot);
-
- // Set up the apply patch commands to patch the recovery image.
- string recovery_sha1 = CalculateSha1(recovery_img_string);
- string boot_sha1 = CalculateSha1(boot_img_string);
- string apply_patch_source_string = android::base::StringPrintf(
- "EMMC:/dev/block/by-name/boot:%zu:%s", boot_img_string.size(), boot_sha1.c_str());
- string apply_patch_target_string = android::base::StringPrintf(
- "EMMC:/dev/block/by-name/recovery:%zu:%s", recovery_img_string.size(), recovery_sha1.c_str());
- string check_command = android::base::StringPrintf(
- R"(patch_partition_check("%s", "%s") || abort("check failed");)",
- apply_patch_target_string.c_str(), apply_patch_source_string.c_str());
- string patch_command = android::base::StringPrintf(
- R"(patch_partition("%s", "%s", package_extract_file("patch.p")) || abort("patch failed");)",
- apply_patch_target_string.c_str(), apply_patch_source_string.c_str());
-
- // Add the commands to update the system image. Test common commands:
- // * getprop
- // * ui_print
- // * patch_partition
- // * package_extract_file (single argument)
- // * block_image_verify, block_image_update
- string tgt_system_string = string(4096, 'a');
- string system_patch;
- CreateBsdiffPatch(raw_system_string_, tgt_system_string, &system_patch);
-
- string tgt_system_hash = CalculateSha1(tgt_system_string);
- string src_system_hash = CalculateSha1(raw_system_string_);
-
- std::vector<std::string> transfer_list = {
- "4",
- "1",
- "0",
- "0",
- android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,1 1 2,0,1", system_patch.size(),
- src_system_hash.c_str(), tgt_system_hash.c_str()),
- };
-
- // Construct the updater_script.
- std::vector<string> updater_commands = {
- R"(getprop("ro.product.device") == "angler" || abort("This package is for \"angler\"");)",
- R"(ui_print("Source: angler/OPR1.170510.001");)",
- check_command,
- patch_command,
- R"(block_image_verify("/dev/block/by-name/system", )"
- R"(package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") || )"
- R"(abort("Failed to verify system.");)",
- R"(block_image_update("/dev/block/by-name/system", )"
- R"(package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") || )"
- R"(abort("Failed to verify system.");)",
- };
- string updater_script = android::base::Join(updater_commands, '\n');
-
- // Construct the ota update package.
- std::map<string, string> ota_entries{
- { "system.new.dat", "" },
- { "system.patch.dat", system_patch },
- { "system.transfer.list", android::base::Join(transfer_list, '\n') },
- { "META-INF/com/google/android/updater-script", updater_script },
- { "patch.p", recovery_from_boot },
- };
-
- TemporaryFile ota_package;
- AddZipEntries(ota_package.release(), ota_entries);
-
- RunSimulation(src_tf.path, ota_package.path, true);
-}
-
-TEST_F(DISABLED_UpdateSimulatorTest, RunUpdateUnrecognizedFunction) {
- std::map<string, string> src_entries{
- { "META/misc_info.txt", "extfs_sparse_flag=-s" },
- { "IMAGES/system.img", sparse_system_string_ },
- { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ },
- { "SYSTEM/build.prop", build_prop_string_ },
- };
-
- TemporaryFile src_tf;
- AddZipEntries(src_tf.release(), src_entries);
-
- std::map<string, string> ota_entries{
- { "system.new.dat", "" },
- { "system.patch.dat", "" },
- { "system.transfer.list", "" },
- { "META-INF/com/google/android/updater-script", R"(bad_function("");)" },
- };
-
- TemporaryFile ota_package;
- AddZipEntries(ota_package.release(), ota_entries);
-
- RunSimulation(src_tf.path, ota_package.path, false);
-}
-
-TEST_F(DISABLED_UpdateSimulatorTest, RunUpdateApplyPatchFailed) {
- string recovery_img_string = "recovery.img";
- string boot_img_string = "boot.img";
-
- std::map<string, string> src_entries{
- { "META/misc_info.txt", "extfs_sparse_flag=-s" },
- { "IMAGES/recovery.img", "" },
- { "IMAGES/boot.img", boot_img_string },
- { "IMAGES/system.img", sparse_system_string_ },
- { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ },
- { "SYSTEM/build.prop", build_prop_string_ },
- };
-
- TemporaryFile src_tf;
- AddZipEntries(src_tf.release(), src_entries);
-
- string recovery_sha1 = CalculateSha1(recovery_img_string);
- string boot_sha1 = CalculateSha1(boot_img_string);
- string apply_patch_source_string = android::base::StringPrintf(
- "EMMC:/dev/block/by-name/boot:%zu:%s", boot_img_string.size(), boot_sha1.c_str());
- string apply_patch_target_string = android::base::StringPrintf(
- "EMMC:/dev/block/by-name/recovery:%zu:%s", recovery_img_string.size(), recovery_sha1.c_str());
- string check_command = android::base::StringPrintf(
- R"(patch_partition_check("%s", "%s") || abort("check failed");)",
- apply_patch_target_string.c_str(), apply_patch_source_string.c_str());
- string patch_command = android::base::StringPrintf(
- R"(patch_partition("%s", "%s", package_extract_file("patch.p")) || abort("failed");)",
- apply_patch_target_string.c_str(), apply_patch_source_string.c_str());
-
- // Give an invalid recovery patch and expect the apply patch to fail.
- // TODO(xunchang) check the cause code.
- std::vector<string> updater_commands = {
- R"(ui_print("Source: angler/OPR1.170510.001");)",
- check_command,
- patch_command,
- };
-
- string updater_script = android::base::Join(updater_commands, '\n');
- std::map<string, string> ota_entries{
- { "system.new.dat", "" },
- { "system.patch.dat", "" },
- { "system.transfer.list", "" },
- { "META-INF/com/google/android/updater-script", updater_script },
- { "patch.p", "random string" },
- };
-
- TemporaryFile ota_package;
- AddZipEntries(ota_package.release(), ota_entries);
-
- RunSimulation(src_tf.path, ota_package.path, false);
-}
diff --git a/tests/unit/updater_test.cpp b/tests/unit/updater_test.cpp
deleted file mode 100644
index 8993dd8..0000000
--- a/tests/unit/updater_test.cpp
+++ /dev/null
@@ -1,1227 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <memory>
-#include <string>
-#include <string_view>
-#include <unordered_map>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <bootloader_message/bootloader_message.h>
-#include <brotli/encode.h>
-#include <bsdiff/bsdiff.h>
-#include <gtest/gtest.h>
-#include <verity/hash_tree_builder.h>
-#include <ziparchive/zip_archive.h>
-#include <ziparchive/zip_writer.h>
-
-#include "applypatch/applypatch.h"
-#include "common/test_constants.h"
-#include "edify/expr.h"
-#include "otautil/error_code.h"
-#include "otautil/paths.h"
-#include "otautil/print_sha1.h"
-#include "otautil/sysutil.h"
-#include "private/commands.h"
-#include "updater/blockimg.h"
-#include "updater/install.h"
-#include "updater/updater.h"
-#include "updater/updater_runtime.h"
-
-using namespace std::string_literals;
-
-using PackageEntries = std::unordered_map<std::string, std::string>;
-
-static void expect(const char* expected, const std::string& expr_str, CauseCode cause_code,
- Updater* updater) {
- std::unique_ptr<Expr> e;
- int error_count = 0;
- ASSERT_EQ(0, ParseString(expr_str, &e, &error_count));
- ASSERT_EQ(0, error_count);
-
- State state(expr_str, updater);
-
- std::string result;
- bool status = Evaluate(&state, e, &result);
-
- if (expected == nullptr) {
- ASSERT_FALSE(status);
- } else {
- ASSERT_TRUE(status) << "Evaluate() finished with error message: " << state.errmsg;
- ASSERT_STREQ(expected, result.c_str());
- }
-
- // Error code is set in updater/updater.cpp only, by parsing State.errmsg.
- ASSERT_EQ(kNoError, state.error_code);
-
- // Cause code should always be available.
- ASSERT_EQ(cause_code, state.cause_code);
-}
-
-static void expect(const char* expected, const std::string& expr_str, CauseCode cause_code) {
- Updater updater(std::make_unique<UpdaterRuntime>(nullptr));
- expect(expected, expr_str, cause_code, &updater);
-}
-
-static void BuildUpdatePackage(const PackageEntries& entries, int fd) {
- FILE* zip_file_ptr = fdopen(fd, "wb");
- ZipWriter zip_writer(zip_file_ptr);
-
- for (const auto& entry : entries) {
- // All the entries are written as STORED.
- ASSERT_EQ(0, zip_writer.StartEntry(entry.first.c_str(), 0));
- if (!entry.second.empty()) {
- ASSERT_EQ(0, zip_writer.WriteBytes(entry.second.data(), entry.second.size()));
- }
- ASSERT_EQ(0, zip_writer.FinishEntry());
- }
-
- ASSERT_EQ(0, zip_writer.Finish());
- ASSERT_EQ(0, fclose(zip_file_ptr));
-}
-
-static std::string GetSha1(std::string_view content) {
- uint8_t digest[SHA_DIGEST_LENGTH];
- SHA1(reinterpret_cast<const uint8_t*>(content.data()), content.size(), digest);
- return print_sha1(digest);
-}
-
-static Value* BlobToString(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 1) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
- }
-
- std::vector<std::unique_ptr<Value>> args;
- if (!ReadValueArgs(state, argv, &args)) {
- return nullptr;
- }
-
- if (args[0]->type != Value::Type::BLOB) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects a BLOB argument", name);
- }
-
- args[0]->type = Value::Type::STRING;
- return args[0].release();
-}
-
-class UpdaterTestBase {
- protected:
- UpdaterTestBase() : updater_(std::make_unique<UpdaterRuntime>(nullptr)) {}
-
- void SetUp() {
- RegisterBuiltins();
- RegisterInstallFunctions();
- RegisterBlockImageFunctions();
-
- // Each test is run in a separate process (isolated mode). Shared temporary files won't cause
- // conflicts.
- Paths::Get().set_cache_temp_source(temp_saved_source_.path);
- Paths::Get().set_last_command_file(temp_last_command_.path);
- Paths::Get().set_stash_directory_base(temp_stash_base_.path);
-
- last_command_file_ = temp_last_command_.path;
- image_file_ = image_temp_file_.path;
- }
-
- void TearDown() {
- // Clean up the last_command_file if any.
- ASSERT_TRUE(android::base::RemoveFileIfExists(last_command_file_));
-
- // Clear partition updated marker if any.
- std::string updated_marker{ temp_stash_base_.path };
- updated_marker += "/" + GetSha1(image_temp_file_.path) + ".UPDATED";
- ASSERT_TRUE(android::base::RemoveFileIfExists(updated_marker));
- }
-
- void RunBlockImageUpdate(bool is_verify, PackageEntries entries, const std::string& image_file,
- const std::string& result, CauseCode cause_code = kNoCause) {
- CHECK(entries.find("transfer_list") != entries.end());
- std::string new_data =
- entries.find("new_data.br") != entries.end() ? "new_data.br" : "new_data";
- std::string script = is_verify ? "block_image_verify" : "block_image_update";
- script += R"((")" + image_file + R"(", package_extract_file("transfer_list"), ")" + new_data +
- R"(", "patch_data"))";
- entries.emplace(Updater::SCRIPT_NAME, script);
-
- // Build the update package.
- TemporaryFile zip_file;
- BuildUpdatePackage(entries, zip_file.release());
-
- // Set up the handler, command_pipe, patch offset & length.
- TemporaryFile temp_pipe;
- ASSERT_TRUE(updater_.Init(temp_pipe.release(), zip_file.path, false));
- ASSERT_TRUE(updater_.RunUpdate());
- ASSERT_EQ(result, updater_.GetResult());
-
- // Parse the cause code written to the command pipe.
- int received_cause_code = kNoCause;
- std::string pipe_content;
- ASSERT_TRUE(android::base::ReadFileToString(temp_pipe.path, &pipe_content));
- auto lines = android::base::Split(pipe_content, "\n");
- for (std::string_view line : lines) {
- if (android::base::ConsumePrefix(&line, "log cause: ")) {
- ASSERT_TRUE(android::base::ParseInt(line.data(), &received_cause_code));
- }
- }
- ASSERT_EQ(cause_code, received_cause_code);
- }
-
- TemporaryFile temp_saved_source_;
- TemporaryDir temp_stash_base_;
- std::string last_command_file_;
- std::string image_file_;
-
- Updater updater_;
-
- private:
- TemporaryFile temp_last_command_;
- TemporaryFile image_temp_file_;
-};
-
-class UpdaterTest : public UpdaterTestBase, public ::testing::Test {
- protected:
- void SetUp() override {
- UpdaterTestBase::SetUp();
-
- RegisterFunction("blob_to_string", BlobToString);
- // Enable a special command "abort" to simulate interruption.
- Command::abort_allowed_ = true;
- }
-
- void TearDown() override {
- UpdaterTestBase::TearDown();
- }
-
- void SetUpdaterCmdPipe(int fd) {
- FILE* cmd_pipe = fdopen(fd, "w");
- ASSERT_NE(nullptr, cmd_pipe);
- updater_.cmd_pipe_.reset(cmd_pipe);
- }
-
- void SetUpdaterOtaPackageHandle(ZipArchiveHandle handle) {
- updater_.package_handle_ = handle;
- }
-
- void FlushUpdaterCommandPipe() const {
- fflush(updater_.cmd_pipe_.get());
- }
-};
-
-TEST_F(UpdaterTest, getprop) {
- expect(android::base::GetProperty("ro.product.device", "").c_str(),
- "getprop(\"ro.product.device\")",
- kNoCause);
-
- expect(android::base::GetProperty("ro.build.fingerprint", "").c_str(),
- "getprop(\"ro.build.fingerprint\")",
- kNoCause);
-
- // getprop() accepts only one parameter.
- expect(nullptr, "getprop()", kArgsParsingFailure);
- expect(nullptr, "getprop(\"arg1\", \"arg2\")", kArgsParsingFailure);
-}
-
-TEST_F(UpdaterTest, patch_partition_check) {
- // Zero argument is not valid.
- expect(nullptr, "patch_partition_check()", kArgsParsingFailure);
-
- std::string source_file = from_testdata_base("boot.img");
- std::string source_content;
- ASSERT_TRUE(android::base::ReadFileToString(source_file, &source_content));
- size_t source_size = source_content.size();
- std::string source_hash = GetSha1(source_content);
- Partition source(source_file, source_size, source_hash);
-
- std::string target_file = from_testdata_base("recovery.img");
- std::string target_content;
- ASSERT_TRUE(android::base::ReadFileToString(target_file, &target_content));
- size_t target_size = target_content.size();
- std::string target_hash = GetSha1(target_content);
- Partition target(target_file, target_size, target_hash);
-
- // One argument is not valid.
- expect(nullptr, "patch_partition_check(\"" + source.ToString() + "\")", kArgsParsingFailure);
- expect(nullptr, "patch_partition_check(\"" + target.ToString() + "\")", kArgsParsingFailure);
-
- // Both of the source and target have the desired checksum.
- std::string cmd =
- "patch_partition_check(\"" + source.ToString() + "\", \"" + target.ToString() + "\")";
- expect("t", cmd, kNoCause);
-
- // Only source partition has the desired checksum.
- Partition bad_target(target_file, target_size - 1, target_hash);
- cmd = "patch_partition_check(\"" + source.ToString() + "\", \"" + bad_target.ToString() + "\")";
- expect("t", cmd, kNoCause);
-
- // Only target partition has the desired checksum.
- Partition bad_source(source_file, source_size + 1, source_hash);
- cmd = "patch_partition_check(\"" + bad_source.ToString() + "\", \"" + target.ToString() + "\")";
- expect("t", cmd, kNoCause);
-
- // Neither of the source or target has the desired checksum.
- cmd =
- "patch_partition_check(\"" + bad_source.ToString() + "\", \"" + bad_target.ToString() + "\")";
- expect("", cmd, kNoCause);
-}
-
-TEST_F(UpdaterTest, file_getprop) {
- // file_getprop() expects two arguments.
- expect(nullptr, "file_getprop()", kArgsParsingFailure);
- expect(nullptr, "file_getprop(\"arg1\")", kArgsParsingFailure);
- expect(nullptr, "file_getprop(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
-
- // File doesn't exist.
- expect(nullptr, "file_getprop(\"/doesntexist\", \"key1\")", kFreadFailure);
-
- // Reject too large files (current limit = 65536).
- TemporaryFile temp_file1;
- std::string buffer(65540, '\0');
- ASSERT_TRUE(android::base::WriteStringToFile(buffer, temp_file1.path));
-
- // Read some keys.
- TemporaryFile temp_file2;
- std::string content("ro.product.name=tardis\n"
- "# comment\n\n\n"
- "ro.product.model\n"
- "ro.product.board = magic \n");
- ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file2.path));
-
- std::string script1("file_getprop(\"" + std::string(temp_file2.path) +
- "\", \"ro.product.name\")");
- expect("tardis", script1, kNoCause);
-
- std::string script2("file_getprop(\"" + std::string(temp_file2.path) +
- "\", \"ro.product.board\")");
- expect("magic", script2, kNoCause);
-
- // No match.
- std::string script3("file_getprop(\"" + std::string(temp_file2.path) +
- "\", \"ro.product.wrong\")");
- expect("", script3, kNoCause);
-
- std::string script4("file_getprop(\"" + std::string(temp_file2.path) +
- "\", \"ro.product.name=\")");
- expect("", script4, kNoCause);
-
- std::string script5("file_getprop(\"" + std::string(temp_file2.path) +
- "\", \"ro.product.nam\")");
- expect("", script5, kNoCause);
-
- std::string script6("file_getprop(\"" + std::string(temp_file2.path) +
- "\", \"ro.product.model\")");
- expect("", script6, kNoCause);
-}
-
-// TODO: Test extracting to block device.
-TEST_F(UpdaterTest, package_extract_file) {
- // package_extract_file expects 1 or 2 arguments.
- expect(nullptr, "package_extract_file()", kArgsParsingFailure);
- expect(nullptr, "package_extract_file(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
-
- std::string zip_path = from_testdata_base("ziptest_valid.zip");
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
-
- // Need to set up the ziphandle.
- SetUpdaterOtaPackageHandle(handle);
-
- // Two-argument version.
- TemporaryFile temp_file1;
- std::string script("package_extract_file(\"a.txt\", \"" + std::string(temp_file1.path) + "\")");
- expect("t", script, kNoCause, &updater_);
-
- // Verify the extracted entry.
- std::string data;
- ASSERT_TRUE(android::base::ReadFileToString(temp_file1.path, &data));
- ASSERT_EQ(kATxtContents, data);
-
- // Now extract another entry to the same location, which should overwrite.
- script = "package_extract_file(\"b.txt\", \"" + std::string(temp_file1.path) + "\")";
- expect("t", script, kNoCause, &updater_);
-
- ASSERT_TRUE(android::base::ReadFileToString(temp_file1.path, &data));
- ASSERT_EQ(kBTxtContents, data);
-
- // Missing zip entry. The two-argument version doesn't abort.
- script = "package_extract_file(\"doesntexist\", \"" + std::string(temp_file1.path) + "\")";
- expect("", script, kNoCause, &updater_);
-
- // Extract to /dev/full should fail.
- script = "package_extract_file(\"a.txt\", \"/dev/full\")";
- expect("", script, kNoCause, &updater_);
-
- // One-argument version. package_extract_file() gives a VAL_BLOB, which needs to be converted to
- // VAL_STRING for equality test.
- script = "blob_to_string(package_extract_file(\"a.txt\")) == \"" + kATxtContents + "\"";
- expect("t", script, kNoCause, &updater_);
-
- script = "blob_to_string(package_extract_file(\"b.txt\")) == \"" + kBTxtContents + "\"";
- expect("t", script, kNoCause, &updater_);
-
- // Missing entry. The one-argument version aborts the evaluation.
- script = "package_extract_file(\"doesntexist\")";
- expect(nullptr, script, kPackageExtractFileFailure, &updater_);
-}
-
-TEST_F(UpdaterTest, read_file) {
- // read_file() expects one argument.
- expect(nullptr, "read_file()", kArgsParsingFailure);
- expect(nullptr, "read_file(\"arg1\", \"arg2\")", kArgsParsingFailure);
-
- // Write some value to file and read back.
- TemporaryFile temp_file;
- std::string script("write_value(\"foo\", \""s + temp_file.path + "\");");
- expect("t", script, kNoCause);
-
- script = "read_file(\""s + temp_file.path + "\") == \"foo\"";
- expect("t", script, kNoCause);
-
- script = "read_file(\""s + temp_file.path + "\") == \"bar\"";
- expect("", script, kNoCause);
-
- // It should fail gracefully when read fails.
- script = "read_file(\"/doesntexist\")";
- expect("", script, kNoCause);
-}
-
-TEST_F(UpdaterTest, compute_hash_tree_smoke) {
- std::string data;
- for (unsigned char i = 0; i < 128; i++) {
- data += std::string(4096, i);
- }
- // Appends an additional block for verity data.
- data += std::string(4096, 0);
- ASSERT_EQ(129 * 4096, data.size());
- ASSERT_TRUE(android::base::WriteStringToFile(data, image_file_));
-
- std::string salt = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7";
- std::string expected_root_hash =
- "7e0a8d8747f54384014ab996f5b2dc4eb7ff00c630eede7134c9e3f05c0dd8ca";
- // hash_tree_ranges, source_ranges, hash_algorithm, salt_hex, root_hash
- std::vector<std::string> tokens{ "compute_hash_tree", "2,128,129", "2,0,128", "sha256", salt,
- expected_root_hash };
- std::string hash_tree_command = android::base::Join(tokens, " ");
-
- std::vector<std::string> transfer_list{
- "4", "2", "0", "2", hash_tree_command,
- };
-
- PackageEntries entries{
- { "new_data", "" },
- { "patch_data", "" },
- { "transfer_list", android::base::Join(transfer_list, "\n") },
- };
-
- RunBlockImageUpdate(false, entries, image_file_, "t");
-
- std::string updated;
- ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated));
- ASSERT_EQ(129 * 4096, updated.size());
- ASSERT_EQ(data.substr(0, 128 * 4096), updated.substr(0, 128 * 4096));
-
- // Computes the SHA256 of the salt + hash_tree_data and expects the result to match with the
- // root_hash.
- std::vector<unsigned char> salt_bytes;
- ASSERT_TRUE(HashTreeBuilder::ParseBytesArrayFromString(salt, &salt_bytes));
- std::vector<unsigned char> hash_tree = std::move(salt_bytes);
- hash_tree.insert(hash_tree.end(), updated.begin() + 128 * 4096, updated.end());
-
- std::vector<unsigned char> digest(SHA256_DIGEST_LENGTH);
- SHA256(hash_tree.data(), hash_tree.size(), digest.data());
- ASSERT_EQ(expected_root_hash, HashTreeBuilder::BytesArrayToString(digest));
-}
-
-TEST_F(UpdaterTest, compute_hash_tree_root_mismatch) {
- std::string data;
- for (size_t i = 0; i < 128; i++) {
- data += std::string(4096, i);
- }
- // Appends an additional block for verity data.
- data += std::string(4096, 0);
- ASSERT_EQ(129 * 4096, data.size());
- // Corrupts one bit
- data[4096] = 'A';
- ASSERT_TRUE(android::base::WriteStringToFile(data, image_file_));
-
- std::string salt = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7";
- std::string expected_root_hash =
- "7e0a8d8747f54384014ab996f5b2dc4eb7ff00c630eede7134c9e3f05c0dd8ca";
- // hash_tree_ranges, source_ranges, hash_algorithm, salt_hex, root_hash
- std::vector<std::string> tokens{ "compute_hash_tree", "2,128,129", "2,0,128", "sha256", salt,
- expected_root_hash };
- std::string hash_tree_command = android::base::Join(tokens, " ");
-
- std::vector<std::string> transfer_list{
- "4", "2", "0", "2", hash_tree_command,
- };
-
- PackageEntries entries{
- { "new_data", "" },
- { "patch_data", "" },
- { "transfer_list", android::base::Join(transfer_list, "\n") },
- };
-
- RunBlockImageUpdate(false, entries, image_file_, "", kHashTreeComputationFailure);
-}
-
-TEST_F(UpdaterTest, write_value) {
- // write_value() expects two arguments.
- expect(nullptr, "write_value()", kArgsParsingFailure);
- expect(nullptr, "write_value(\"arg1\")", kArgsParsingFailure);
- expect(nullptr, "write_value(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
-
- // filename cannot be empty.
- expect(nullptr, "write_value(\"value\", \"\")", kArgsParsingFailure);
-
- // Write some value to file.
- TemporaryFile temp_file;
- std::string value = "magicvalue";
- std::string script("write_value(\"" + value + "\", \"" + std::string(temp_file.path) + "\")");
- expect("t", script, kNoCause);
-
- // Verify the content.
- std::string content;
- ASSERT_TRUE(android::base::ReadFileToString(temp_file.path, &content));
- ASSERT_EQ(value, content);
-
- // Allow writing empty string.
- script = "write_value(\"\", \"" + std::string(temp_file.path) + "\")";
- expect("t", script, kNoCause);
-
- // Verify the content.
- ASSERT_TRUE(android::base::ReadFileToString(temp_file.path, &content));
- ASSERT_EQ("", content);
-
- // It should fail gracefully when write fails.
- script = "write_value(\"value\", \"/proc/0/file1\")";
- expect("", script, kNoCause);
-}
-
-TEST_F(UpdaterTest, get_stage) {
- // get_stage() expects one argument.
- expect(nullptr, "get_stage()", kArgsParsingFailure);
- expect(nullptr, "get_stage(\"arg1\", \"arg2\")", kArgsParsingFailure);
- expect(nullptr, "get_stage(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
-
- // Set up a local file as BCB.
- TemporaryFile tf;
- std::string temp_file(tf.path);
- bootloader_message boot;
- strlcpy(boot.stage, "2/3", sizeof(boot.stage));
- std::string err;
- ASSERT_TRUE(write_bootloader_message_to(boot, temp_file, &err));
-
- // Can read the stage value.
- std::string script("get_stage(\"" + temp_file + "\")");
- expect("2/3", script, kNoCause);
-
- // Bad BCB path.
- script = "get_stage(\"doesntexist\")";
- expect("", script, kNoCause);
-}
-
-TEST_F(UpdaterTest, set_stage) {
- // set_stage() expects two arguments.
- expect(nullptr, "set_stage()", kArgsParsingFailure);
- expect(nullptr, "set_stage(\"arg1\")", kArgsParsingFailure);
- expect(nullptr, "set_stage(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
-
- // Set up a local file as BCB.
- TemporaryFile tf;
- std::string temp_file(tf.path);
- bootloader_message boot;
- strlcpy(boot.command, "command", sizeof(boot.command));
- strlcpy(boot.stage, "2/3", sizeof(boot.stage));
- std::string err;
- ASSERT_TRUE(write_bootloader_message_to(boot, temp_file, &err));
-
- // Write with set_stage().
- std::string script("set_stage(\"" + temp_file + "\", \"1/3\")");
- expect(tf.path, script, kNoCause);
-
- // Verify.
- bootloader_message boot_verify;
- ASSERT_TRUE(read_bootloader_message_from(&boot_verify, temp_file, &err));
-
- // Stage should be updated, with command part untouched.
- ASSERT_STREQ("1/3", boot_verify.stage);
- ASSERT_STREQ(boot.command, boot_verify.command);
-
- // Bad BCB path.
- script = "set_stage(\"doesntexist\", \"1/3\")";
- expect("", script, kNoCause);
-
- script = "set_stage(\"/dev/full\", \"1/3\")";
- expect("", script, kNoCause);
-}
-
-TEST_F(UpdaterTest, set_progress) {
- // set_progress() expects one argument.
- expect(nullptr, "set_progress()", kArgsParsingFailure);
- expect(nullptr, "set_progress(\"arg1\", \"arg2\")", kArgsParsingFailure);
-
- // Invalid progress argument.
- expect(nullptr, "set_progress(\"arg1\")", kArgsParsingFailure);
- expect(nullptr, "set_progress(\"3x+5\")", kArgsParsingFailure);
- expect(nullptr, "set_progress(\".3.5\")", kArgsParsingFailure);
-
- TemporaryFile tf;
- SetUpdaterCmdPipe(tf.release());
- expect(".52", "set_progress(\".52\")", kNoCause, &updater_);
- FlushUpdaterCommandPipe();
-
- std::string cmd;
- ASSERT_TRUE(android::base::ReadFileToString(tf.path, &cmd));
- ASSERT_EQ(android::base::StringPrintf("set_progress %f\n", .52), cmd);
- // recovery-updater protocol expects 2 tokens ("set_progress <frac>").
- ASSERT_EQ(2U, android::base::Split(cmd, " ").size());
-}
-
-TEST_F(UpdaterTest, show_progress) {
- // show_progress() expects two arguments.
- expect(nullptr, "show_progress()", kArgsParsingFailure);
- expect(nullptr, "show_progress(\"arg1\")", kArgsParsingFailure);
- expect(nullptr, "show_progress(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
-
- // Invalid progress arguments.
- expect(nullptr, "show_progress(\"arg1\", \"arg2\")", kArgsParsingFailure);
- expect(nullptr, "show_progress(\"3x+5\", \"10\")", kArgsParsingFailure);
- expect(nullptr, "show_progress(\".3\", \"5a\")", kArgsParsingFailure);
-
- TemporaryFile tf;
- SetUpdaterCmdPipe(tf.release());
- expect(".52", "show_progress(\".52\", \"10\")", kNoCause, &updater_);
- FlushUpdaterCommandPipe();
-
- std::string cmd;
- ASSERT_TRUE(android::base::ReadFileToString(tf.path, &cmd));
- ASSERT_EQ(android::base::StringPrintf("progress %f %d\n", .52, 10), cmd);
- // recovery-updater protocol expects 3 tokens ("progress <frac> <secs>").
- ASSERT_EQ(3U, android::base::Split(cmd, " ").size());
-}
-
-TEST_F(UpdaterTest, block_image_update_parsing_error) {
- std::vector<std::string> transfer_list{
- // clang-format off
- "4",
- "2",
- "0",
- // clang-format on
- };
-
- PackageEntries entries{
- { "new_data", "" },
- { "patch_data", "" },
- { "transfer_list", android::base::Join(transfer_list, '\n') },
- };
-
- RunBlockImageUpdate(false, entries, image_file_, "", kArgsParsingFailure);
-}
-
-// Generates the bsdiff of the given source and target images, and writes the result entries.
-// target_blocks specifies the block count to be written into the `bsdiff` command, which may be
-// different from the given target size in order to trigger overrun / underrun paths.
-static void GetEntriesForBsdiff(std::string_view source, std::string_view target,
- size_t target_blocks, PackageEntries* entries) {
- // Generate the patch data.
- TemporaryFile patch_file;
- ASSERT_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(source.data()), source.size(),
- reinterpret_cast<const uint8_t*>(target.data()), target.size(),
- patch_file.path, nullptr));
- std::string patch_content;
- ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch_content));
-
- // Create the transfer list that contains a bsdiff.
- std::string src_hash = GetSha1(source);
- std::string tgt_hash = GetSha1(target);
- size_t source_blocks = source.size() / 4096;
- std::vector<std::string> transfer_list{
- // clang-format off
- "4",
- std::to_string(target_blocks),
- "0",
- "0",
- // bsdiff patch_offset patch_length source_hash target_hash target_range source_block_count
- // source_range
- android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,%zu %zu 2,0,%zu", patch_content.size(),
- src_hash.c_str(), tgt_hash.c_str(), target_blocks, source_blocks,
- source_blocks),
- // clang-format on
- };
-
- *entries = {
- { "new_data", "" },
- { "patch_data", patch_content },
- { "transfer_list", android::base::Join(transfer_list, '\n') },
- };
-}
-
-TEST_F(UpdaterTest, block_image_update_patch_data) {
- // Both source and target images have 10 blocks.
- std::string source =
- std::string(4096, 'a') + std::string(4096, 'c') + std::string(4096 * 3, '\0');
- std::string target =
- std::string(4096, 'b') + std::string(4096, 'd') + std::string(4096 * 3, '\0');
- ASSERT_TRUE(android::base::WriteStringToFile(source, image_file_));
-
- PackageEntries entries;
- GetEntriesForBsdiff(std::string_view(source).substr(0, 4096 * 2),
- std::string_view(target).substr(0, 4096 * 2), 2, &entries);
- RunBlockImageUpdate(false, entries, image_file_, "t");
-
- // The update_file should be patched correctly.
- std::string updated;
- ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated));
- ASSERT_EQ(target, updated);
-}
-
-TEST_F(UpdaterTest, block_image_update_patch_overrun) {
- // Both source and target images have 10 blocks.
- std::string source =
- std::string(4096, 'a') + std::string(4096, 'c') + std::string(4096 * 3, '\0');
- std::string target =
- std::string(4096, 'b') + std::string(4096, 'd') + std::string(4096 * 3, '\0');
- ASSERT_TRUE(android::base::WriteStringToFile(source, image_file_));
-
- // Provide one less block to trigger the overrun path.
- PackageEntries entries;
- GetEntriesForBsdiff(std::string_view(source).substr(0, 4096 * 2),
- std::string_view(target).substr(0, 4096 * 2), 1, &entries);
-
- // The update should fail due to overrun.
- RunBlockImageUpdate(false, entries, image_file_, "", kPatchApplicationFailure);
-}
-
-TEST_F(UpdaterTest, block_image_update_patch_underrun) {
- // Both source and target images have 10 blocks.
- std::string source =
- std::string(4096, 'a') + std::string(4096, 'c') + std::string(4096 * 3, '\0');
- std::string target =
- std::string(4096, 'b') + std::string(4096, 'd') + std::string(4096 * 3, '\0');
- ASSERT_TRUE(android::base::WriteStringToFile(source, image_file_));
-
- // Provide one more block to trigger the overrun path.
- PackageEntries entries;
- GetEntriesForBsdiff(std::string_view(source).substr(0, 4096 * 2),
- std::string_view(target).substr(0, 4096 * 2), 3, &entries);
-
- // The update should fail due to underrun.
- RunBlockImageUpdate(false, entries, image_file_, "", kPatchApplicationFailure);
-}
-
-TEST_F(UpdaterTest, block_image_update_fail) {
- std::string src_content(4096 * 2, 'e');
- std::string src_hash = GetSha1(src_content);
- // Stash and free some blocks, then fail the update intentionally.
- std::vector<std::string> transfer_list{
- // clang-format off
- "4",
- "2",
- "0",
- "2",
- "stash " + src_hash + " 2,0,2",
- "free " + src_hash,
- "abort",
- // clang-format on
- };
-
- // Add a new data of 10 bytes to test the deadlock.
- PackageEntries entries{
- { "new_data", std::string(10, 0) },
- { "patch_data", "" },
- { "transfer_list", android::base::Join(transfer_list, '\n') },
- };
-
- ASSERT_TRUE(android::base::WriteStringToFile(src_content, image_file_));
-
- RunBlockImageUpdate(false, entries, image_file_, "");
-
- // Updater generates the stash name based on the input file name.
- std::string name_digest = GetSha1(image_file_);
- std::string stash_base = std::string(temp_stash_base_.path) + "/" + name_digest;
- ASSERT_EQ(0, access(stash_base.c_str(), F_OK));
- // Expect the stashed blocks to be freed.
- ASSERT_EQ(-1, access((stash_base + src_hash).c_str(), F_OK));
- ASSERT_EQ(0, rmdir(stash_base.c_str()));
-}
-
-TEST_F(UpdaterTest, new_data_over_write) {
- std::vector<std::string> transfer_list{
- // clang-format off
- "4",
- "1",
- "0",
- "0",
- "new 2,0,1",
- // clang-format on
- };
-
- // Write 4096 + 100 bytes of new data.
- PackageEntries entries{
- { "new_data", std::string(4196, 0) },
- { "patch_data", "" },
- { "transfer_list", android::base::Join(transfer_list, '\n') },
- };
-
- RunBlockImageUpdate(false, entries, image_file_, "t");
-}
-
-TEST_F(UpdaterTest, new_data_short_write) {
- std::vector<std::string> transfer_list{
- // clang-format off
- "4",
- "1",
- "0",
- "0",
- "new 2,0,1",
- // clang-format on
- };
-
- PackageEntries entries{
- { "patch_data", "" },
- { "transfer_list", android::base::Join(transfer_list, '\n') },
- };
-
- // Updater should report the failure gracefully rather than stuck in deadlock.
- entries["new_data"] = "";
- RunBlockImageUpdate(false, entries, image_file_, "");
-
- entries["new_data"] = std::string(10, 'a');
- RunBlockImageUpdate(false, entries, image_file_, "");
-
- // Expect to write 1 block of new data successfully.
- entries["new_data"] = std::string(4096, 'a');
- RunBlockImageUpdate(false, entries, image_file_, "t");
-}
-
-TEST_F(UpdaterTest, brotli_new_data) {
- auto generator = []() { return rand() % 128; };
- // Generate 100 blocks of random data.
- std::string brotli_new_data;
- brotli_new_data.reserve(4096 * 100);
- generate_n(back_inserter(brotli_new_data), 4096 * 100, generator);
-
- size_t encoded_size = BrotliEncoderMaxCompressedSize(brotli_new_data.size());
- std::string encoded_data(encoded_size, 0);
- ASSERT_TRUE(BrotliEncoderCompress(
- BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, brotli_new_data.size(),
- reinterpret_cast<const uint8_t*>(brotli_new_data.data()), &encoded_size,
- reinterpret_cast<uint8_t*>(const_cast<char*>(encoded_data.data()))));
- encoded_data.resize(encoded_size);
-
- // Write a few small chunks of new data, then a large chunk, and finally a few small chunks.
- // This helps us to catch potential short writes.
- std::vector<std::string> transfer_list = {
- "4",
- "100",
- "0",
- "0",
- "new 2,0,1",
- "new 2,1,2",
- "new 4,2,50,50,97",
- "new 2,97,98",
- "new 2,98,99",
- "new 2,99,100",
- };
-
- PackageEntries entries{
- { "new_data.br", std::move(encoded_data) },
- { "patch_data", "" },
- { "transfer_list", android::base::Join(transfer_list, '\n') },
- };
-
- RunBlockImageUpdate(false, entries, image_file_, "t");
-
- std::string updated_content;
- ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated_content));
- ASSERT_EQ(brotli_new_data, updated_content);
-}
-
-TEST_F(UpdaterTest, last_command_update) {
- std::string block1(4096, '1');
- std::string block2(4096, '2');
- std::string block3(4096, '3');
- std::string block1_hash = GetSha1(block1);
- std::string block2_hash = GetSha1(block2);
- std::string block3_hash = GetSha1(block3);
-
- // Compose the transfer list to fail the first update.
- std::vector<std::string> transfer_list_fail{
- // clang-format off
- "4",
- "2",
- "0",
- "2",
- "stash " + block1_hash + " 2,0,1",
- "move " + block1_hash + " 2,1,2 1 2,0,1",
- "stash " + block3_hash + " 2,2,3",
- "abort",
- // clang-format on
- };
-
- // Mimic a resumed update with the same transfer commands.
- std::vector<std::string> transfer_list_continue{
- // clang-format off
- "4",
- "2",
- "0",
- "2",
- "stash " + block1_hash + " 2,0,1",
- "move " + block1_hash + " 2,1,2 1 2,0,1",
- "stash " + block3_hash + " 2,2,3",
- "move " + block1_hash + " 2,2,3 1 2,0,1",
- // clang-format on
- };
-
- ASSERT_TRUE(android::base::WriteStringToFile(block1 + block2 + block3, image_file_));
-
- PackageEntries entries{
- { "new_data", "" },
- { "patch_data", "" },
- { "transfer_list", android::base::Join(transfer_list_fail, '\n') },
- };
-
- // "2\nstash " + block3_hash + " 2,2,3"
- std::string last_command_content =
- "2\n" + transfer_list_fail[TransferList::kTransferListHeaderLines + 2];
-
- RunBlockImageUpdate(false, entries, image_file_, "");
-
- // Expect last_command to contain the last stash command.
- std::string last_command_actual;
- ASSERT_TRUE(android::base::ReadFileToString(last_command_file_, &last_command_actual));
- EXPECT_EQ(last_command_content, last_command_actual);
-
- std::string updated_contents;
- ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated_contents));
- ASSERT_EQ(block1 + block1 + block3, updated_contents);
-
- // "Resume" the update. Expect the first 'move' to be skipped but the second 'move' to be
- // executed. Note that we intentionally reset the image file.
- entries["transfer_list"] = android::base::Join(transfer_list_continue, '\n');
- ASSERT_TRUE(android::base::WriteStringToFile(block1 + block2 + block3, image_file_));
- RunBlockImageUpdate(false, entries, image_file_, "t");
-
- ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated_contents));
- ASSERT_EQ(block1 + block2 + block1, updated_contents);
-}
-
-TEST_F(UpdaterTest, last_command_update_unresumable) {
- std::string block1(4096, '1');
- std::string block2(4096, '2');
- std::string block1_hash = GetSha1(block1);
- std::string block2_hash = GetSha1(block2);
-
- // Construct an unresumable update with source blocks mismatch.
- std::vector<std::string> transfer_list_unresumable{
- // clang-format off
- "4",
- "2",
- "0",
- "2",
- "stash " + block1_hash + " 2,0,1",
- "move " + block2_hash + " 2,1,2 1 2,0,1",
- // clang-format on
- };
-
- PackageEntries entries{
- { "new_data", "" },
- { "patch_data", "" },
- { "transfer_list", android::base::Join(transfer_list_unresumable, '\n') },
- };
-
- ASSERT_TRUE(android::base::WriteStringToFile(block1 + block1, image_file_));
-
- std::string last_command_content =
- "0\n" + transfer_list_unresumable[TransferList::kTransferListHeaderLines];
- ASSERT_TRUE(android::base::WriteStringToFile(last_command_content, last_command_file_));
-
- RunBlockImageUpdate(false, entries, image_file_, "");
-
- // The last_command_file will be deleted if the update encounters an unresumable failure later.
- ASSERT_EQ(-1, access(last_command_file_.c_str(), R_OK));
-}
-
-TEST_F(UpdaterTest, last_command_verify) {
- std::string block1(4096, '1');
- std::string block2(4096, '2');
- std::string block3(4096, '3');
- std::string block1_hash = GetSha1(block1);
- std::string block2_hash = GetSha1(block2);
- std::string block3_hash = GetSha1(block3);
-
- std::vector<std::string> transfer_list_verify{
- // clang-format off
- "4",
- "2",
- "0",
- "2",
- "stash " + block1_hash + " 2,0,1",
- "move " + block1_hash + " 2,0,1 1 2,0,1",
- "move " + block1_hash + " 2,1,2 1 2,0,1",
- "stash " + block3_hash + " 2,2,3",
- // clang-format on
- };
-
- PackageEntries entries{
- { "new_data", "" },
- { "patch_data", "" },
- { "transfer_list", android::base::Join(transfer_list_verify, '\n') },
- };
-
- ASSERT_TRUE(android::base::WriteStringToFile(block1 + block1 + block3, image_file_));
-
- // Last command: "move " + block1_hash + " 2,1,2 1 2,0,1"
- std::string last_command_content =
- "2\n" + transfer_list_verify[TransferList::kTransferListHeaderLines + 2];
-
- // First run: expect the verification to succeed and the last_command_file is intact.
- ASSERT_TRUE(android::base::WriteStringToFile(last_command_content, last_command_file_));
-
- RunBlockImageUpdate(true, entries, image_file_, "t");
-
- std::string last_command_actual;
- ASSERT_TRUE(android::base::ReadFileToString(last_command_file_, &last_command_actual));
- EXPECT_EQ(last_command_content, last_command_actual);
-
- // Second run with a mismatching block image: expect the verification to succeed but
- // last_command_file to be deleted; because the target blocks in the last command don't have the
- // expected contents for the second move command.
- ASSERT_TRUE(android::base::WriteStringToFile(block1 + block2 + block3, image_file_));
- RunBlockImageUpdate(true, entries, image_file_, "t");
- ASSERT_EQ(-1, access(last_command_file_.c_str(), R_OK));
-}
-
-class ResumableUpdaterTest : public UpdaterTestBase, public testing::TestWithParam<size_t> {
- protected:
- void SetUp() override {
- UpdaterTestBase::SetUp();
- // Enable a special command "abort" to simulate interruption.
- Command::abort_allowed_ = true;
- index_ = GetParam();
- }
-
- void TearDown() override {
- UpdaterTestBase::TearDown();
- }
-
- size_t index_;
-};
-
-static std::string g_source_image;
-static std::string g_target_image;
-static PackageEntries g_entries;
-
-static std::vector<std::string> GenerateTransferList() {
- std::string a(4096, 'a');
- std::string b(4096, 'b');
- std::string c(4096, 'c');
- std::string d(4096, 'd');
- std::string e(4096, 'e');
- std::string f(4096, 'f');
- std::string g(4096, 'g');
- std::string h(4096, 'h');
- std::string i(4096, 'i');
- std::string zero(4096, '\0');
-
- std::string a_hash = GetSha1(a);
- std::string b_hash = GetSha1(b);
- std::string c_hash = GetSha1(c);
- std::string e_hash = GetSha1(e);
-
- auto loc = [](const std::string& range_text) {
- std::vector<std::string> pieces = android::base::Split(range_text, "-");
- size_t left;
- size_t right;
- if (pieces.size() == 1) {
- CHECK(android::base::ParseUint(pieces[0], &left));
- right = left + 1;
- } else {
- CHECK_EQ(2u, pieces.size());
- CHECK(android::base::ParseUint(pieces[0], &left));
- CHECK(android::base::ParseUint(pieces[1], &right));
- right++;
- }
- return android::base::StringPrintf("2,%zu,%zu", left, right);
- };
-
- // patch 1: "b d c" -> "g"
- TemporaryFile patch_file_bdc_g;
- std::string bdc = b + d + c;
- std::string bdc_hash = GetSha1(bdc);
- std::string g_hash = GetSha1(g);
- CHECK_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(bdc.data()), bdc.size(),
- reinterpret_cast<const uint8_t*>(g.data()), g.size(),
- patch_file_bdc_g.path, nullptr));
- std::string patch_bdc_g;
- CHECK(android::base::ReadFileToString(patch_file_bdc_g.path, &patch_bdc_g));
-
- // patch 2: "a b c d" -> "d c b"
- TemporaryFile patch_file_abcd_dcb;
- std::string abcd = a + b + c + d;
- std::string abcd_hash = GetSha1(abcd);
- std::string dcb = d + c + b;
- std::string dcb_hash = GetSha1(dcb);
- CHECK_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(abcd.data()), abcd.size(),
- reinterpret_cast<const uint8_t*>(dcb.data()), dcb.size(),
- patch_file_abcd_dcb.path, nullptr));
- std::string patch_abcd_dcb;
- CHECK(android::base::ReadFileToString(patch_file_abcd_dcb.path, &patch_abcd_dcb));
-
- std::vector<std::string> transfer_list{
- "4",
- "10", // total blocks written
- "2", // maximum stash entries
- "2", // maximum number of stashed blocks
-
- // a b c d e a b c d e
- "stash " + b_hash + " " + loc("1"),
- // a b c d e a b c d e [b(1)]
- "stash " + c_hash + " " + loc("2"),
- // a b c d e a b c d e [b(1)][c(2)]
- "new " + loc("1-2"),
- // a i h d e a b c d e [b(1)][c(2)]
- "zero " + loc("0"),
- // 0 i h d e a b c d e [b(1)][c(2)]
-
- // bsdiff "b d c" (from stash, 3, stash) to get g(3)
- android::base::StringPrintf(
- "bsdiff 0 %zu %s %s %s 3 %s %s %s:%s %s:%s",
- patch_bdc_g.size(), // patch start (0), patch length
- bdc_hash.c_str(), // source hash
- g_hash.c_str(), // target hash
- loc("3").c_str(), // target range
- loc("3").c_str(), loc("1").c_str(), // load "d" from block 3, into buffer at offset 1
- b_hash.c_str(), loc("0").c_str(), // load "b" from stash, into buffer at offset 0
- c_hash.c_str(), loc("2").c_str()), // load "c" from stash, into buffer at offset 2
-
- // 0 i h g e a b c d e [b(1)][c(2)]
- "free " + b_hash,
- // 0 i h g e a b c d e [c(2)]
- "free " + a_hash,
- // 0 i h g e a b c d e
- "stash " + a_hash + " " + loc("5"),
- // 0 i h g e a b c d e [a(5)]
- "move " + e_hash + " " + loc("5") + " 1 " + loc("4"),
- // 0 i h g e e b c d e [a(5)]
-
- // bsdiff "a b c d" (from stash, 6-8) to "d c b" (6-8)
- android::base::StringPrintf( //
- "bsdiff %zu %zu %s %s %s 4 %s %s %s:%s",
- patch_bdc_g.size(), // patch start
- patch_bdc_g.size() + patch_abcd_dcb.size(), // patch length
- abcd_hash.c_str(), // source hash
- dcb_hash.c_str(), // target hash
- loc("6-8").c_str(), // target range
- loc("6-8").c_str(), // load "b c d" from blocks 6-8
- loc("1-3").c_str(), // into buffer at offset 1-3
- a_hash.c_str(), // load "a" from stash
- loc("0").c_str()), // into buffer at offset 0
-
- // 0 i h g e e d c b e [a(5)]
- "new " + loc("4"),
- // 0 i h g f e d c b e [a(5)]
- "move " + a_hash + " " + loc("9") + " 1 - " + a_hash + ":" + loc("0"),
- // 0 i h g f e d c b a [a(5)]
- "free " + a_hash,
- // 0 i h g f e d c b a
- };
-
- std::string new_data = i + h + f;
- std::string patch_data = patch_bdc_g + patch_abcd_dcb;
-
- g_entries = {
- { "new_data", new_data },
- { "patch_data", patch_data },
- };
- g_source_image = a + b + c + d + e + a + b + c + d + e;
- g_target_image = zero + i + h + g + f + e + d + c + b + a;
-
- return transfer_list;
-}
-
-static const std::vector<std::string> g_transfer_list = GenerateTransferList();
-
-INSTANTIATE_TEST_CASE_P(InterruptAfterEachCommand, ResumableUpdaterTest,
- ::testing::Range(static_cast<size_t>(0),
- g_transfer_list.size() -
- TransferList::kTransferListHeaderLines));
-
-TEST_P(ResumableUpdaterTest, InterruptVerifyResume) {
- ASSERT_TRUE(android::base::WriteStringToFile(g_source_image, image_file_));
-
- LOG(INFO) << "Interrupting at line " << index_ << " ("
- << g_transfer_list[TransferList::kTransferListHeaderLines + index_] << ")";
-
- std::vector<std::string> transfer_list_copy{ g_transfer_list };
- transfer_list_copy[TransferList::kTransferListHeaderLines + index_] = "abort";
-
- g_entries["transfer_list"] = android::base::Join(transfer_list_copy, '\n');
-
- // Run update that's expected to fail.
- RunBlockImageUpdate(false, g_entries, image_file_, "");
-
- std::string last_command_expected;
-
- // Assert the last_command_file.
- if (index_ == 0) {
- ASSERT_EQ(-1, access(last_command_file_.c_str(), R_OK));
- } else {
- last_command_expected = std::to_string(index_ - 1) + "\n" +
- g_transfer_list[TransferList::kTransferListHeaderLines + index_ - 1];
- std::string last_command_actual;
- ASSERT_TRUE(android::base::ReadFileToString(last_command_file_, &last_command_actual));
- ASSERT_EQ(last_command_expected, last_command_actual);
- }
-
- g_entries["transfer_list"] = android::base::Join(g_transfer_list, '\n');
-
- // Resume the interrupted update, by doing verification first.
- RunBlockImageUpdate(true, g_entries, image_file_, "t");
-
- // last_command_file should remain intact.
- if (index_ == 0) {
- ASSERT_EQ(-1, access(last_command_file_.c_str(), R_OK));
- } else {
- std::string last_command_actual;
- ASSERT_TRUE(android::base::ReadFileToString(last_command_file_, &last_command_actual));
- ASSERT_EQ(last_command_expected, last_command_actual);
- }
-
- // Resume the update.
- RunBlockImageUpdate(false, g_entries, image_file_, "t");
-
- // last_command_file should be gone after successful update.
- ASSERT_EQ(-1, access(last_command_file_.c_str(), R_OK));
-
- std::string updated_image_actual;
- ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated_image_actual));
- ASSERT_EQ(g_target_image, updated_image_actual);
-}
diff --git a/updater/Android.bp b/updater/Android.bp
deleted file mode 100644
index 4fc3c49..0000000
--- a/updater/Android.bp
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright (C) 2018 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.
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "bootable_recovery_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["bootable_recovery_license"],
-}
-
-cc_defaults {
- name: "libupdater_static_libs",
-
- static_libs: [
- "libapplypatch",
- "libbootloader_message",
- "libbspatch",
- "libedify",
- "libotautil",
- "libext4_utils",
- "libdm",
- "libfec",
- "libfec_rs",
- "libavb",
- "libverity_tree",
- "liblog",
- "liblp",
- "libselinux",
- "libsparse",
- "libsquashfs_utils",
- "libbrotli",
- "libbz",
- "libziparchive",
- "libz_stable",
- "libbase",
- "libcrypto_utils",
- "libcutils",
- "libutils",
- ],
- header_libs: [
- "libgtest_prod_headers",
- ],
-}
-
-cc_defaults {
- name: "libupdater_defaults",
-
- defaults: [
- "recovery_defaults",
- "libupdater_static_libs",
- ],
-
- shared_libs: [
- "libcrypto",
- ],
-}
-
-cc_defaults {
- name: "libupdater_device_defaults",
-
- static_libs: [
- "libfs_mgr",
- "libtune2fs",
-
- "libext2_com_err",
- "libext2_blkid",
- "libext2_quota",
- "libext2_uuid",
- "libext2_e2p",
- "libext2fs",
- ],
-}
-
-cc_library_static {
- name: "libupdater_core",
-
- host_supported: true,
-
- defaults: [
- "recovery_defaults",
- "libupdater_defaults",
- ],
-
- srcs: [
- "blockimg.cpp",
- "commands.cpp",
- "install.cpp",
- "mounts.cpp",
- "updater.cpp",
- ],
-
- target: {
- darwin: {
- enabled: false,
- },
- },
-
- export_include_dirs: [
- "include",
- ],
-}
-
-cc_library_static {
- name: "libupdater_device",
-
- defaults: [
- "recovery_defaults",
- "libupdater_defaults",
- "libupdater_device_defaults",
- ],
-
- srcs: [
- "dynamic_partitions.cpp",
- "updater_runtime.cpp",
- "updater_runtime_dynamic_partitions.cpp",
- ],
-
- static_libs: [
- "libupdater_core",
- ],
-
- include_dirs: [
- "external/e2fsprogs/misc",
- ],
-
- export_include_dirs: [
- "include",
- ],
-}
-
-cc_library_host_static {
- name: "libupdater_host",
-
- defaults: [
- "recovery_defaults",
- "libupdater_defaults",
- ],
-
- srcs: [
- "build_info.cpp",
- "dynamic_partitions.cpp",
- "simulator_runtime.cpp",
- "target_files.cpp",
- ],
-
- static_libs: [
- "libupdater_core",
- "libfstab",
- "libc++fs",
- ],
-
- target: {
- darwin: {
- enabled: false,
- },
- },
-
- export_include_dirs: [
- "include",
- ],
-}
-
-cc_binary_host {
- name: "update_host_simulator",
- defaults: ["libupdater_static_libs"],
-
- srcs: ["update_simulator_main.cpp"],
-
- cflags: [
- "-Wall",
- "-Werror",
- ],
-
- static_libs: [
- "libupdater_host",
- "libupdater_core",
- "libcrypto_static",
- "libfstab",
- "libc++fs",
- ],
-
- target: {
- darwin: {
- enabled: false,
- },
- },
-}
diff --git a/updater/Android.mk b/updater/Android.mk
deleted file mode 100644
index 2fd5639..0000000
--- a/updater/Android.mk
+++ /dev/null
@@ -1,118 +0,0 @@
-# Copyright 2009 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-tune2fs_static_libraries := \
- libext2_com_err \
- libext2_blkid \
- libext2_quota \
- libext2_uuid \
- libext2_e2p \
- libext2fs
-
-updater_common_static_libraries := \
- libapplypatch \
- libbootloader_message \
- libbspatch \
- libedify \
- libotautil \
- libext4_utils \
- libdm \
- libfec \
- libfec_rs \
- libavb \
- libverity_tree \
- liblog \
- liblp \
- libselinux \
- libsparse \
- libsquashfs_utils \
- libbrotli \
- libbz \
- libziparchive \
- libz_stable \
- libbase \
- libcrypto_static \
- libcrypto_utils \
- libcutils \
- libutils
-
-
-# Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function
-# named "Register_<libname>()". Here we emit a little C function that
-# gets #included by updater.cpp. It calls all those registration
-# functions.
-# $(1): the path to the register.inc file
-# $(2): a list of TARGET_RECOVERY_UPDATER_LIBS
-define generate-register-inc
- $(hide) mkdir -p $(dir $(1))
- $(hide) echo "" > $(1)
- $(hide) $(foreach lib,$(2),echo "extern void Register_$(lib)(void);" >> $(1);)
- $(hide) echo "void RegisterDeviceExtensions() {" >> $(1)
- $(hide) $(foreach lib,$(2),echo " Register_$(lib)();" >> $(1);)
- $(hide) echo "}" >> $(1)
-endef
-
-
-# updater (static executable)
-# ===============================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := updater
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
-
-LOCAL_SRC_FILES := \
- updater_main.cpp
-
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/include
-
-LOCAL_CFLAGS := \
- -Wall \
- -Werror
-
-LOCAL_STATIC_LIBRARIES := \
- libupdater_device \
- libupdater_core \
- $(TARGET_RECOVERY_UPDATER_LIBS) \
- $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS) \
- $(updater_common_static_libraries) \
- libfs_mgr \
- libtune2fs \
- $(tune2fs_static_libraries)
-
-LOCAL_HEADER_LIBRARIES := libgtest_prod_headers
-
-LOCAL_MODULE_CLASS := EXECUTABLES
-inc := $(call local-generated-sources-dir)/register.inc
-
-# Devices can also add libraries to TARGET_RECOVERY_UPDATER_EXTRA_LIBS.
-# These libs are also linked in with updater, but we don't try to call
-# any sort of registration function for these. Use this variable for
-# any subsidiary static libraries required for your registered
-# extension libs.
-$(inc) : libs := $(TARGET_RECOVERY_UPDATER_LIBS)
-$(inc) :
- $(call generate-register-inc,$@,$(libs))
-
-LOCAL_GENERATED_SOURCES := $(inc)
-
-inc :=
-
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-
-include $(BUILD_EXECUTABLE)
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
deleted file mode 100644
index b29aa8c..0000000
--- a/updater/blockimg.cpp
+++ /dev/null
@@ -1,2303 +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.
- */
-
-#include <ctype.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <linux/fs.h>
-#include <pthread.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <functional>
-#include <limits>
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <applypatch/applypatch.h>
-#include <brotli/decode.h>
-#include <fec/io.h>
-#include <openssl/sha.h>
-#include <verity/hash_tree_builder.h>
-#include <ziparchive/zip_archive.h>
-
-#include "edify/expr.h"
-#include "edify/updater_interface.h"
-#include "otautil/dirutil.h"
-#include "otautil/error_code.h"
-#include "otautil/paths.h"
-#include "otautil/print_sha1.h"
-#include "otautil/rangeset.h"
-#include "private/commands.h"
-#include "updater/install.h"
-
-#ifdef __ANDROID__
-#include <private/android_filesystem_config.h>
-// Set this to 0 to interpret 'erase' transfers to mean do a BLKDISCARD ioctl (the normal behavior).
-// Set to 1 to interpret erase to mean fill the region with zeroes.
-#define DEBUG_ERASE 0
-#else
-#define DEBUG_ERASE 1
-#define AID_SYSTEM -1
-#endif // __ANDROID__
-
-static constexpr size_t BLOCKSIZE = 4096;
-static constexpr mode_t STASH_DIRECTORY_MODE = 0700;
-static constexpr mode_t STASH_FILE_MODE = 0600;
-static constexpr mode_t MARKER_DIRECTORY_MODE = 0700;
-
-static CauseCode failure_type = kNoCause;
-static bool is_retry = false;
-static std::unordered_map<std::string, RangeSet> stash_map;
-
-static void DeleteLastCommandFile() {
- const std::string& last_command_file = Paths::Get().last_command_file();
- if (unlink(last_command_file.c_str()) == -1 && errno != ENOENT) {
- PLOG(ERROR) << "Failed to unlink: " << last_command_file;
- }
-}
-
-// Parse the last command index of the last update and save the result to |last_command_index|.
-// Return true if we successfully read the index.
-static bool ParseLastCommandFile(size_t* last_command_index) {
- const std::string& last_command_file = Paths::Get().last_command_file();
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(last_command_file.c_str(), O_RDONLY)));
- if (fd == -1) {
- if (errno != ENOENT) {
- PLOG(ERROR) << "Failed to open " << last_command_file;
- return false;
- }
-
- LOG(INFO) << last_command_file << " doesn't exist.";
- return false;
- }
-
- // Now that the last_command file exists, parse the last command index of previous update.
- std::string content;
- if (!android::base::ReadFdToString(fd.get(), &content)) {
- LOG(ERROR) << "Failed to read: " << last_command_file;
- return false;
- }
-
- std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n");
- if (lines.size() != 2) {
- LOG(ERROR) << "Unexpected line counts in last command file: " << content;
- return false;
- }
-
- if (!android::base::ParseUint(lines[0], last_command_index)) {
- LOG(ERROR) << "Failed to parse integer in: " << lines[0];
- return false;
- }
-
- return true;
-}
-
-static bool FsyncDir(const std::string& dirname) {
- android::base::unique_fd dfd(TEMP_FAILURE_RETRY(open(dirname.c_str(), O_RDONLY | O_DIRECTORY)));
- if (dfd == -1) {
- failure_type = errno == EIO ? kEioFailure : kFileOpenFailure;
- PLOG(ERROR) << "Failed to open " << dirname;
- return false;
- }
- if (fsync(dfd) == -1) {
- failure_type = errno == EIO ? kEioFailure : kFsyncFailure;
- PLOG(ERROR) << "Failed to fsync " << dirname;
- return false;
- }
- return true;
-}
-
-// Update the last executed command index in the last_command_file.
-static bool UpdateLastCommandIndex(size_t command_index, const std::string& command_string) {
- const std::string& last_command_file = Paths::Get().last_command_file();
- std::string last_command_tmp = last_command_file + ".tmp";
- std::string content = std::to_string(command_index) + "\n" + command_string;
- android::base::unique_fd wfd(
- TEMP_FAILURE_RETRY(open(last_command_tmp.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0660)));
- if (wfd == -1 || !android::base::WriteStringToFd(content, wfd)) {
- PLOG(ERROR) << "Failed to update last command";
- return false;
- }
-
- if (fsync(wfd) == -1) {
- PLOG(ERROR) << "Failed to fsync " << last_command_tmp;
- return false;
- }
-
- if (chown(last_command_tmp.c_str(), AID_SYSTEM, AID_SYSTEM) == -1) {
- PLOG(ERROR) << "Failed to change owner for " << last_command_tmp;
- return false;
- }
-
- if (rename(last_command_tmp.c_str(), last_command_file.c_str()) == -1) {
- PLOG(ERROR) << "Failed to rename" << last_command_tmp;
- return false;
- }
-
- if (!FsyncDir(android::base::Dirname(last_command_file))) {
- return false;
- }
-
- return true;
-}
-
-bool SetUpdatedMarker(const std::string& marker) {
- auto dirname = android::base::Dirname(marker);
- auto res = mkdir(dirname.c_str(), MARKER_DIRECTORY_MODE);
- if (res == -1 && errno != EEXIST) {
- PLOG(ERROR) << "Failed to create directory for marker: " << dirname;
- return false;
- }
-
- if (!android::base::WriteStringToFile("", marker)) {
- PLOG(ERROR) << "Failed to write to marker file " << marker;
- return false;
- }
- if (!FsyncDir(dirname)) {
- return false;
- }
- LOG(INFO) << "Wrote updated marker to " << marker;
- return true;
-}
-
-static bool discard_blocks(int fd, off64_t offset, uint64_t size, bool force = false) {
- // Don't discard blocks unless the update is a retry run or force == true
- if (!is_retry && !force) {
- return true;
- }
-
- uint64_t args[2] = { static_cast<uint64_t>(offset), size };
- if (ioctl(fd, BLKDISCARD, &args) == -1) {
- // On devices that does not support BLKDISCARD, ignore the error.
- if (errno == EOPNOTSUPP) {
- return true;
- }
- PLOG(ERROR) << "BLKDISCARD ioctl failed";
- return false;
- }
- return true;
-}
-
-static bool check_lseek(int fd, off64_t offset, int whence) {
- off64_t rc = TEMP_FAILURE_RETRY(lseek64(fd, offset, whence));
- if (rc == -1) {
- failure_type = kLseekFailure;
- PLOG(ERROR) << "lseek64 failed";
- return false;
- }
- return true;
-}
-
-static void allocate(size_t size, std::vector<uint8_t>* buffer) {
- // If the buffer's big enough, reuse it.
- if (size <= buffer->size()) return;
- buffer->resize(size);
-}
-
-/**
- * RangeSinkWriter reads data from the given FD, and writes them to the destination specified by the
- * given RangeSet.
- */
-class RangeSinkWriter {
- public:
- RangeSinkWriter(int fd, const RangeSet& tgt)
- : fd_(fd),
- tgt_(tgt),
- next_range_(0),
- current_range_left_(0),
- bytes_written_(0) {
- CHECK_NE(tgt.size(), static_cast<size_t>(0));
- };
-
- bool Finished() const {
- return next_range_ == tgt_.size() && current_range_left_ == 0;
- }
-
- size_t AvailableSpace() const {
- return tgt_.blocks() * BLOCKSIZE - bytes_written_;
- }
-
- // Return number of bytes written; and 0 indicates a writing failure.
- size_t Write(const uint8_t* data, size_t size) {
- if (Finished()) {
- LOG(ERROR) << "range sink write overrun; can't write " << size << " bytes";
- return 0;
- }
-
- size_t written = 0;
- while (size > 0) {
- // Move to the next range as needed.
- if (!SeekToOutputRange()) {
- break;
- }
-
- size_t write_now = size;
- if (current_range_left_ < write_now) {
- write_now = current_range_left_;
- }
-
- if (!android::base::WriteFully(fd_, data, write_now)) {
- failure_type = errno == EIO ? kEioFailure : kFwriteFailure;
- PLOG(ERROR) << "Failed to write " << write_now << " bytes of data";
- break;
- }
-
- data += write_now;
- size -= write_now;
-
- current_range_left_ -= write_now;
- written += write_now;
- }
-
- bytes_written_ += written;
- return written;
- }
-
- size_t BytesWritten() const {
- return bytes_written_;
- }
-
- private:
- // Set up the output cursor, move to next range if needed.
- bool SeekToOutputRange() {
- // We haven't finished the current range yet.
- if (current_range_left_ != 0) {
- return true;
- }
- // We can't write any more; let the write function return how many bytes have been written
- // so far.
- if (next_range_ >= tgt_.size()) {
- return false;
- }
-
- const Range& range = tgt_[next_range_];
- off64_t offset = static_cast<off64_t>(range.first) * BLOCKSIZE;
- current_range_left_ = (range.second - range.first) * BLOCKSIZE;
- next_range_++;
-
- if (!discard_blocks(fd_, offset, current_range_left_)) {
- return false;
- }
- if (!check_lseek(fd_, offset, SEEK_SET)) {
- return false;
- }
- return true;
- }
-
- // The output file descriptor.
- int fd_;
- // The destination ranges for the data.
- const RangeSet& tgt_;
- // The next range that we should write to.
- size_t next_range_;
- // The number of bytes to write before moving to the next range.
- size_t current_range_left_;
- // Total bytes written by the writer.
- size_t bytes_written_;
-};
-
-/**
- * All of the data for all the 'new' transfers is contained in one file in the update package,
- * concatenated together in the order in which transfers.list will need it. We want to stream it out
- * of the archive (it's compressed) without writing it to a temp file, but we can't write each
- * section until it's that transfer's turn to go.
- *
- * To achieve this, we expand the new data from the archive in a background thread, and block that
- * threads 'receive uncompressed data' function until the main thread has reached a point where we
- * want some new data to be written. We signal the background thread with the destination for the
- * data and block the main thread, waiting for the background thread to complete writing that
- * section. Then it signals the main thread to wake up and goes back to blocking waiting for a
- * transfer.
- *
- * NewThreadInfo is the struct used to pass information back and forth between the two threads. When
- * the main thread wants some data written, it sets writer to the destination location and signals
- * the condition. When the background thread is done writing, it clears writer and signals the
- * condition again.
- */
-struct NewThreadInfo {
- ZipArchiveHandle za;
- ZipEntry64 entry{};
- bool brotli_compressed;
-
- std::unique_ptr<RangeSinkWriter> writer;
- BrotliDecoderState* brotli_decoder_state;
- bool receiver_available;
-
- pthread_mutex_t mu;
- pthread_cond_t cv;
-};
-
-static bool receive_new_data(const uint8_t* data, size_t size, void* cookie) {
- NewThreadInfo* nti = static_cast<NewThreadInfo*>(cookie);
-
- while (size > 0) {
- // Wait for nti->writer to be non-null, indicating some of this data is wanted.
- pthread_mutex_lock(&nti->mu);
- while (nti->writer == nullptr) {
- // End the new data receiver if we encounter an error when performing block image update.
- if (!nti->receiver_available) {
- pthread_mutex_unlock(&nti->mu);
- return false;
- }
- pthread_cond_wait(&nti->cv, &nti->mu);
- }
- pthread_mutex_unlock(&nti->mu);
-
- // At this point nti->writer is set, and we own it. The main thread is waiting for it to
- // disappear from nti.
- size_t write_now = std::min(size, nti->writer->AvailableSpace());
- if (nti->writer->Write(data, write_now) != write_now) {
- LOG(ERROR) << "Failed to write " << write_now << " bytes.";
- return false;
- }
-
- data += write_now;
- size -= write_now;
-
- if (nti->writer->Finished()) {
- // We have written all the bytes desired by this writer.
-
- pthread_mutex_lock(&nti->mu);
- nti->writer = nullptr;
- pthread_cond_broadcast(&nti->cv);
- pthread_mutex_unlock(&nti->mu);
- }
- }
-
- return true;
-}
-
-static bool receive_brotli_new_data(const uint8_t* data, size_t size, void* cookie) {
- NewThreadInfo* nti = static_cast<NewThreadInfo*>(cookie);
-
- while (size > 0 || BrotliDecoderHasMoreOutput(nti->brotli_decoder_state)) {
- // Wait for nti->writer to be non-null, indicating some of this data is wanted.
- pthread_mutex_lock(&nti->mu);
- while (nti->writer == nullptr) {
- // End the receiver if we encounter an error when performing block image update.
- if (!nti->receiver_available) {
- pthread_mutex_unlock(&nti->mu);
- return false;
- }
- pthread_cond_wait(&nti->cv, &nti->mu);
- }
- pthread_mutex_unlock(&nti->mu);
-
- // At this point nti->writer is set, and we own it. The main thread is waiting for it to
- // disappear from nti.
-
- size_t buffer_size = std::min<size_t>(32768, nti->writer->AvailableSpace());
- if (buffer_size == 0) {
- LOG(ERROR) << "No space left in output range";
- return false;
- }
- uint8_t buffer[buffer_size];
- size_t available_in = size;
- size_t available_out = buffer_size;
- uint8_t* next_out = buffer;
-
- // The brotli decoder will update |data|, |available_in|, |next_out| and |available_out|.
- BrotliDecoderResult result = BrotliDecoderDecompressStream(
- nti->brotli_decoder_state, &available_in, &data, &available_out, &next_out, nullptr);
-
- if (result == BROTLI_DECODER_RESULT_ERROR) {
- LOG(ERROR) << "Decompression failed with "
- << BrotliDecoderErrorString(BrotliDecoderGetErrorCode(nti->brotli_decoder_state));
- return false;
- }
-
- LOG(DEBUG) << "bytes to write: " << buffer_size - available_out << ", bytes consumed "
- << size - available_in << ", decoder status " << result;
-
- size_t write_now = buffer_size - available_out;
- if (nti->writer->Write(buffer, write_now) != write_now) {
- LOG(ERROR) << "Failed to write " << write_now << " bytes.";
- return false;
- }
-
- // Update the remaining size. The input data ptr is already updated by brotli decoder function.
- size = available_in;
-
- if (nti->writer->Finished()) {
- // We have written all the bytes desired by this writer.
-
- pthread_mutex_lock(&nti->mu);
- nti->writer = nullptr;
- pthread_cond_broadcast(&nti->cv);
- pthread_mutex_unlock(&nti->mu);
- }
- }
-
- return true;
-}
-
-static void* unzip_new_data(void* cookie) {
- NewThreadInfo* nti = static_cast<NewThreadInfo*>(cookie);
- if (nti->brotli_compressed) {
- ProcessZipEntryContents(nti->za, &nti->entry, receive_brotli_new_data, nti);
- } else {
- ProcessZipEntryContents(nti->za, &nti->entry, receive_new_data, nti);
- }
- pthread_mutex_lock(&nti->mu);
- nti->receiver_available = false;
- if (nti->writer != nullptr) {
- pthread_cond_broadcast(&nti->cv);
- }
- pthread_mutex_unlock(&nti->mu);
- return nullptr;
-}
-
-static int ReadBlocks(const RangeSet& src, std::vector<uint8_t>* buffer, int fd) {
- size_t p = 0;
- for (const auto& [begin, end] : src) {
- if (!check_lseek(fd, static_cast<off64_t>(begin) * BLOCKSIZE, SEEK_SET)) {
- return -1;
- }
-
- size_t size = (end - begin) * BLOCKSIZE;
- if (!android::base::ReadFully(fd, buffer->data() + p, size)) {
- failure_type = errno == EIO ? kEioFailure : kFreadFailure;
- PLOG(ERROR) << "Failed to read " << size << " bytes of data";
- return -1;
- }
-
- p += size;
- }
-
- return 0;
-}
-
-static int WriteBlocks(const RangeSet& tgt, const std::vector<uint8_t>& buffer, int fd) {
- size_t written = 0;
- for (const auto& [begin, end] : tgt) {
- off64_t offset = static_cast<off64_t>(begin) * BLOCKSIZE;
- size_t size = (end - begin) * BLOCKSIZE;
- if (!discard_blocks(fd, offset, size)) {
- return -1;
- }
-
- if (!check_lseek(fd, offset, SEEK_SET)) {
- return -1;
- }
-
- if (!android::base::WriteFully(fd, buffer.data() + written, size)) {
- failure_type = errno == EIO ? kEioFailure : kFwriteFailure;
- PLOG(ERROR) << "Failed to write " << size << " bytes of data";
- return -1;
- }
-
- written += size;
- }
-
- return 0;
-}
-
-// Parameters for transfer list command functions
-struct CommandParameters {
- std::vector<std::string> tokens;
- size_t cpos;
- std::string cmdname;
- std::string cmdline;
- std::string freestash;
- std::string stashbase;
- bool canwrite;
- int createdstash;
- android::base::unique_fd fd;
- bool foundwrites;
- bool isunresumable;
- int version;
- size_t written;
- size_t stashed;
- NewThreadInfo nti;
- pthread_t thread;
- std::vector<uint8_t> buffer;
- uint8_t* patch_start;
- bool target_verified; // The target blocks have expected contents already.
-};
-
-// Print the hash in hex for corrupted source blocks (excluding the stashed blocks which is
-// handled separately).
-static void PrintHashForCorruptedSourceBlocks(const CommandParameters& params,
- const std::vector<uint8_t>& buffer) {
- LOG(INFO) << "unexpected contents of source blocks in cmd:\n" << params.cmdline;
- CHECK(params.tokens[0] == "move" || params.tokens[0] == "bsdiff" ||
- params.tokens[0] == "imgdiff");
-
- size_t pos = 0;
- // Command example:
- // move <onehash> <tgt_range> <src_blk_count> <src_range> [<loc_range> <stashed_blocks>]
- // bsdiff <offset> <len> <src_hash> <tgt_hash> <tgt_range> <src_blk_count> <src_range>
- // [<loc_range> <stashed_blocks>]
- if (params.tokens[0] == "move") {
- // src_range for move starts at the 4th position.
- if (params.tokens.size() < 5) {
- LOG(ERROR) << "failed to parse source range in cmd:\n" << params.cmdline;
- return;
- }
- pos = 4;
- } else {
- // src_range for diff starts at the 7th position.
- if (params.tokens.size() < 8) {
- LOG(ERROR) << "failed to parse source range in cmd:\n" << params.cmdline;
- return;
- }
- pos = 7;
- }
-
- // Source blocks in stash only, no work to do.
- if (params.tokens[pos] == "-") {
- return;
- }
-
- 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
- // order as the source blocks.
- if (pos == params.tokens.size()) {
- locs = RangeSet(std::vector<Range>{ Range{ 0, src.blocks() } });
- } else {
- // Otherwise, the next token is the offset of the source blocks in the target range.
- // Example: for the tokens <4,63946,63947,63948,63979> <4,6,7,8,39> <stashed_blocks>;
- // We want to print SHA-1 for the data in buffer[6], buffer[8], buffer[9] ... buffer[38];
- // this corresponds to the 32 src blocks #63946, #63948, #63949 ... #63978.
- locs = RangeSet::Parse(params.tokens[pos++]);
- CHECK_EQ(src.blocks(), locs.blocks());
- }
-
- LOG(INFO) << "printing hash in hex for " << src.blocks() << " source blocks";
- for (size_t i = 0; i < src.blocks(); i++) {
- size_t block_num = src.GetBlockNumber(i);
- size_t buffer_index = locs.GetBlockNumber(i);
- CHECK_LE((buffer_index + 1) * BLOCKSIZE, buffer.size());
-
- uint8_t digest[SHA_DIGEST_LENGTH];
- SHA1(buffer.data() + buffer_index * BLOCKSIZE, BLOCKSIZE, digest);
- std::string hexdigest = print_sha1(digest);
- LOG(INFO) << " block number: " << block_num << ", SHA-1: " << hexdigest;
- }
-}
-
-// If the calculated hash for the whole stash doesn't match the stash id, print the SHA-1
-// in hex for each block.
-static void PrintHashForCorruptedStashedBlocks(const std::string& id,
- const std::vector<uint8_t>& buffer,
- const RangeSet& src) {
- LOG(INFO) << "printing hash in hex for stash_id: " << id;
- CHECK_EQ(src.blocks() * BLOCKSIZE, buffer.size());
-
- for (size_t i = 0; i < src.blocks(); i++) {
- size_t block_num = src.GetBlockNumber(i);
-
- uint8_t digest[SHA_DIGEST_LENGTH];
- SHA1(buffer.data() + i * BLOCKSIZE, BLOCKSIZE, digest);
- std::string hexdigest = print_sha1(digest);
- LOG(INFO) << " block number: " << block_num << ", SHA-1: " << hexdigest;
- }
-}
-
-// If the stash file doesn't exist, read the source blocks this stash contains and print the
-// SHA-1 for these blocks.
-static void PrintHashForMissingStashedBlocks(const std::string& id, int fd) {
- if (stash_map.find(id) == stash_map.end()) {
- LOG(ERROR) << "No stash saved for id: " << id;
- return;
- }
-
- LOG(INFO) << "print hash in hex for source blocks in missing stash: " << id;
- const RangeSet& src = stash_map[id];
- std::vector<uint8_t> buffer(src.blocks() * BLOCKSIZE);
- if (ReadBlocks(src, &buffer, fd) == -1) {
- LOG(ERROR) << "failed to read source blocks for stash: " << id;
- return;
- }
- PrintHashForCorruptedStashedBlocks(id, buffer, src);
-}
-
-static int VerifyBlocks(const std::string& expected, const std::vector<uint8_t>& buffer,
- const size_t blocks, bool printerror) {
- uint8_t digest[SHA_DIGEST_LENGTH];
- const uint8_t* data = buffer.data();
-
- SHA1(data, blocks * BLOCKSIZE, digest);
-
- std::string hexdigest = print_sha1(digest);
-
- if (hexdigest != expected) {
- if (printerror) {
- LOG(ERROR) << "failed to verify blocks (expected " << expected << ", read " << hexdigest
- << ")";
- }
- return -1;
- }
-
- return 0;
-}
-
-static std::string GetStashFileName(const std::string& base, const std::string& id,
- const std::string& postfix) {
- if (base.empty()) {
- return "";
- }
- std::string filename = Paths::Get().stash_directory_base() + "/" + base;
- if (id.empty() && postfix.empty()) {
- return filename;
- }
- return filename + "/" + id + postfix;
-}
-
-// Does a best effort enumeration of stash files. Ignores possible non-file items in the stash
-// directory and continues despite of errors. Calls the 'callback' function for each file.
-static void EnumerateStash(const std::string& dirname,
- const std::function<void(const std::string&)>& callback) {
- if (dirname.empty()) return;
-
- std::unique_ptr<DIR, decltype(&closedir)> directory(opendir(dirname.c_str()), closedir);
-
- if (directory == nullptr) {
- if (errno != ENOENT) {
- PLOG(ERROR) << "opendir \"" << dirname << "\" failed";
- }
- return;
- }
-
- dirent* item;
- while ((item = readdir(directory.get())) != nullptr) {
- if (item->d_type != DT_REG) continue;
- callback(dirname + "/" + item->d_name);
- }
-}
-
-// Deletes the stash directory and all files in it. Assumes that it only
-// contains files. There is nothing we can do about unlikely, but possible
-// errors, so they are merely logged.
-static void DeleteFile(const std::string& fn) {
- if (fn.empty()) return;
-
- LOG(INFO) << "deleting " << fn;
-
- if (unlink(fn.c_str()) == -1 && errno != ENOENT) {
- PLOG(ERROR) << "unlink \"" << fn << "\" failed";
- }
-}
-
-static void DeleteStash(const std::string& base) {
- if (base.empty()) return;
-
- LOG(INFO) << "deleting stash " << base;
-
- std::string dirname = GetStashFileName(base, "", "");
- EnumerateStash(dirname, DeleteFile);
-
- if (rmdir(dirname.c_str()) == -1) {
- if (errno != ENOENT && errno != ENOTDIR) {
- PLOG(ERROR) << "rmdir \"" << dirname << "\" failed";
- }
- }
-}
-
-static int LoadStash(const CommandParameters& params, const std::string& id, bool verify,
- std::vector<uint8_t>* buffer, bool printnoent) {
- // In verify mode, if source range_set was saved for the given hash, check contents in the source
- // blocks first. If the check fails, search for the stashed files on /cache as usual.
- if (!params.canwrite) {
- if (stash_map.find(id) != stash_map.end()) {
- const RangeSet& src = stash_map[id];
- allocate(src.blocks() * BLOCKSIZE, buffer);
-
- if (ReadBlocks(src, buffer, params.fd) == -1) {
- LOG(ERROR) << "failed to read source blocks in stash map.";
- return -1;
- }
- if (VerifyBlocks(id, *buffer, src.blocks(), true) != 0) {
- LOG(ERROR) << "failed to verify loaded source blocks in stash map.";
- if (!is_retry) {
- PrintHashForCorruptedStashedBlocks(id, *buffer, src);
- }
- return -1;
- }
- return 0;
- }
- }
-
- std::string fn = GetStashFileName(params.stashbase, id, "");
-
- struct stat sb;
- if (stat(fn.c_str(), &sb) == -1) {
- if (errno != ENOENT || printnoent) {
- PLOG(ERROR) << "stat \"" << fn << "\" failed";
- PrintHashForMissingStashedBlocks(id, params.fd);
- }
- return -1;
- }
-
- LOG(INFO) << " loading " << fn;
-
- if ((sb.st_size % BLOCKSIZE) != 0) {
- LOG(ERROR) << fn << " size " << sb.st_size << " not multiple of block size " << BLOCKSIZE;
- return -1;
- }
-
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(fn.c_str(), O_RDONLY)));
- if (fd == -1) {
- failure_type = errno == EIO ? kEioFailure : kFileOpenFailure;
- PLOG(ERROR) << "open \"" << fn << "\" failed";
- return -1;
- }
-
- allocate(sb.st_size, buffer);
-
- if (!android::base::ReadFully(fd, buffer->data(), sb.st_size)) {
- failure_type = errno == EIO ? kEioFailure : kFreadFailure;
- PLOG(ERROR) << "Failed to read " << sb.st_size << " bytes of data";
- return -1;
- }
-
- size_t blocks = sb.st_size / BLOCKSIZE;
- if (verify && VerifyBlocks(id, *buffer, blocks, true) != 0) {
- LOG(ERROR) << "unexpected contents in " << fn;
- if (stash_map.find(id) == stash_map.end()) {
- LOG(ERROR) << "failed to find source blocks number for stash " << id
- << " when executing command: " << params.cmdname;
- } else {
- const RangeSet& src = stash_map[id];
- PrintHashForCorruptedStashedBlocks(id, *buffer, src);
- }
- DeleteFile(fn);
- return -1;
- }
-
- return 0;
-}
-
-static int WriteStash(const std::string& base, const std::string& id, int blocks,
- const std::vector<uint8_t>& buffer, bool checkspace, bool* exists) {
- if (base.empty()) {
- return -1;
- }
-
- if (checkspace && !CheckAndFreeSpaceOnCache(blocks * BLOCKSIZE)) {
- LOG(ERROR) << "not enough space to write stash";
- return -1;
- }
-
- std::string fn = GetStashFileName(base, id, ".partial");
- std::string cn = GetStashFileName(base, id, "");
-
- if (exists) {
- struct stat sb;
- int res = stat(cn.c_str(), &sb);
-
- if (res == 0) {
- // The file already exists and since the name is the hash of the contents,
- // it's safe to assume the contents are identical (accidental hash collisions
- // are unlikely)
- LOG(INFO) << " skipping " << blocks << " existing blocks in " << cn;
- *exists = true;
- return 0;
- }
-
- *exists = false;
- }
-
- LOG(INFO) << " writing " << blocks << " blocks to " << cn;
-
- android::base::unique_fd fd(
- TEMP_FAILURE_RETRY(open(fn.c_str(), O_WRONLY | O_CREAT | O_TRUNC, STASH_FILE_MODE)));
- if (fd == -1) {
- failure_type = errno == EIO ? kEioFailure : kFileOpenFailure;
- PLOG(ERROR) << "failed to create \"" << fn << "\"";
- return -1;
- }
-
- if (fchown(fd, AID_SYSTEM, AID_SYSTEM) != 0) { // system user
- PLOG(ERROR) << "failed to chown \"" << fn << "\"";
- return -1;
- }
-
- if (!android::base::WriteFully(fd, buffer.data(), blocks * BLOCKSIZE)) {
- failure_type = errno == EIO ? kEioFailure : kFwriteFailure;
- PLOG(ERROR) << "Failed to write " << blocks * BLOCKSIZE << " bytes of data";
- return -1;
- }
-
- if (fsync(fd) == -1) {
- failure_type = errno == EIO ? kEioFailure : kFsyncFailure;
- PLOG(ERROR) << "fsync \"" << fn << "\" failed";
- return -1;
- }
-
- if (rename(fn.c_str(), cn.c_str()) == -1) {
- PLOG(ERROR) << "rename(\"" << fn << "\", \"" << cn << "\") failed";
- return -1;
- }
-
- std::string dname = GetStashFileName(base, "", "");
- if (!FsyncDir(dname)) {
- return -1;
- }
-
- return 0;
-}
-
-// Creates a directory for storing stash files and checks if the /cache partition
-// hash enough space for the expected amount of blocks we need to store. Returns
-// >0 if we created the directory, zero if it existed already, and <0 of failure.
-static int CreateStash(State* state, size_t maxblocks, const std::string& base) {
- std::string dirname = GetStashFileName(base, "", "");
- struct stat sb;
- int res = stat(dirname.c_str(), &sb);
- if (res == -1 && errno != ENOENT) {
- ErrorAbort(state, kStashCreationFailure, "stat \"%s\" failed: %s", dirname.c_str(),
- strerror(errno));
- return -1;
- }
-
- size_t max_stash_size = maxblocks * BLOCKSIZE;
- if (res == -1) {
- LOG(INFO) << "creating stash " << dirname;
- res = mkdir_recursively(dirname, STASH_DIRECTORY_MODE, false, nullptr);
-
- if (res != 0) {
- ErrorAbort(state, kStashCreationFailure, "mkdir \"%s\" failed: %s", dirname.c_str(),
- strerror(errno));
- return -1;
- }
-
- if (chown(dirname.c_str(), AID_SYSTEM, AID_SYSTEM) != 0) { // system user
- ErrorAbort(state, kStashCreationFailure, "chown \"%s\" failed: %s", dirname.c_str(),
- strerror(errno));
- return -1;
- }
-
- if (!CheckAndFreeSpaceOnCache(max_stash_size)) {
- ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu needed)",
- max_stash_size);
- return -1;
- }
-
- return 1; // Created directory
- }
-
- LOG(INFO) << "using existing stash " << dirname;
-
- // If the directory already exists, calculate the space already allocated to stash files and check
- // if there's enough for all required blocks. Delete any partially completed stash files first.
- EnumerateStash(dirname, [](const std::string& fn) {
- if (android::base::EndsWith(fn, ".partial")) {
- DeleteFile(fn);
- }
- });
-
- size_t existing = 0;
- EnumerateStash(dirname, [&existing](const std::string& fn) {
- if (fn.empty()) return;
- struct stat sb;
- if (stat(fn.c_str(), &sb) == -1) {
- PLOG(ERROR) << "stat \"" << fn << "\" failed";
- return;
- }
- existing += static_cast<size_t>(sb.st_size);
- });
-
- if (max_stash_size > existing) {
- size_t needed = max_stash_size - existing;
- if (!CheckAndFreeSpaceOnCache(needed)) {
- ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu more needed)",
- needed);
- return -1;
- }
- }
-
- return 0; // Using existing directory
-}
-
-static int FreeStash(const std::string& base, const std::string& id) {
- if (base.empty() || id.empty()) {
- return -1;
- }
-
- DeleteFile(GetStashFileName(base, id, ""));
-
- return 0;
-}
-
-// Source contains packed data, which we want to move to the locations given in locs in the dest
-// buffer. source and dest may be the same buffer.
-static void MoveRange(std::vector<uint8_t>& dest, const RangeSet& locs,
- const std::vector<uint8_t>& source) {
- const uint8_t* from = source.data();
- uint8_t* to = dest.data();
- size_t start = locs.blocks();
- // Must do the movement backward.
- for (auto it = locs.crbegin(); it != locs.crend(); it++) {
- size_t blocks = it->second - it->first;
- start -= blocks;
- memmove(to + (it->first * BLOCKSIZE), from + (start * BLOCKSIZE), blocks * BLOCKSIZE);
- }
-}
-
-/**
- * We expect to parse the remainder of the parameter tokens as one of:
- *
- * <src_block_count> <src_range>
- * (loads data from source image only)
- *
- * <src_block_count> - <[stash_id:stash_range] ...>
- * (loads data from stashes only)
- *
- * <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...>
- * (loads data from both source image and stashes)
- *
- * On return, params.buffer is filled with the loaded source data (rearranged and combined with
- * stashed data as necessary). buffer may be reallocated if needed to accommodate the source data.
- * tgt is the target RangeSet for detecting overlaps. Any stashes required are loaded using
- * LoadStash.
- */
-static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size_t* src_blocks,
- bool* overlap) {
- CHECK(src_blocks != nullptr);
- CHECK(overlap != nullptr);
-
- // <src_block_count>
- const std::string& token = params.tokens[params.cpos++];
- if (!android::base::ParseUint(token, src_blocks)) {
- LOG(ERROR) << "invalid src_block_count \"" << token << "\"";
- return -1;
- }
-
- allocate(*src_blocks * BLOCKSIZE, ¶ms.buffer);
-
- // "-" or <src_range> [<src_loc>]
- if (params.tokens[params.cpos] == "-") {
- // no source ranges, only stashes
- params.cpos++;
- } else {
- RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]);
- CHECK(static_cast<bool>(src));
- *overlap = src.Overlaps(tgt);
-
- if (ReadBlocks(src, ¶ms.buffer, params.fd) == -1) {
- return -1;
- }
-
- if (params.cpos >= params.tokens.size()) {
- // no stashes, only source range
- return 0;
- }
-
- RangeSet locs = RangeSet::Parse(params.tokens[params.cpos++]);
- CHECK(static_cast<bool>(locs));
- MoveRange(params.buffer, locs, params.buffer);
- }
-
- // <[stash_id:stash_range]>
- while (params.cpos < params.tokens.size()) {
- // Each word is a an index into the stash table, a colon, and then a RangeSet describing where
- // in the source block that stashed data should go.
- std::vector<std::string> tokens = android::base::Split(params.tokens[params.cpos++], ":");
- if (tokens.size() != 2) {
- LOG(ERROR) << "invalid parameter";
- return -1;
- }
-
- std::vector<uint8_t> stash;
- if (LoadStash(params, tokens[0], false, &stash, true) == -1) {
- // These source blocks will fail verification if used later, but we
- // will let the caller decide if this is a fatal failure
- LOG(ERROR) << "failed to load stash " << tokens[0];
- continue;
- }
-
- RangeSet locs = RangeSet::Parse(tokens[1]);
- CHECK(static_cast<bool>(locs));
- MoveRange(params.buffer, locs, stash);
- }
-
- return 0;
-}
-
-/**
- * Do a source/target load for move/bsdiff/imgdiff in version 3.
- *
- * We expect to parse the remainder of the parameter tokens as one of:
- *
- * <tgt_range> <src_block_count> <src_range>
- * (loads data from source image only)
- *
- * <tgt_range> <src_block_count> - <[stash_id:stash_range] ...>
- * (loads data from stashes only)
- *
- * <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...>
- * (loads data from both source image and stashes)
- *
- * 'onehash' tells whether to expect separate source and targe block hashes, or if they are both the
- * same and only one hash should be expected. params.isunresumable will be set to true if block
- * verification fails in a way that the update cannot be resumed anymore.
- *
- * If the function is unable to load the necessary blocks or their contents don't match the hashes,
- * the return value is -1 and the command should be aborted.
- *
- * If the return value is 1, the command has already been completed according to the contents of the
- * target blocks, and should not be performed again.
- *
- * If the return value is 0, source blocks have expected content and the command can be performed.
- */
-static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet* tgt, size_t* src_blocks,
- bool onehash) {
- CHECK(src_blocks != nullptr);
-
- if (params.cpos >= params.tokens.size()) {
- LOG(ERROR) << "missing source hash";
- return -1;
- }
-
- std::string srchash = params.tokens[params.cpos++];
- std::string tgthash;
-
- if (onehash) {
- tgthash = srchash;
- } else {
- if (params.cpos >= params.tokens.size()) {
- LOG(ERROR) << "missing target hash";
- return -1;
- }
- tgthash = params.tokens[params.cpos++];
- }
-
- // At least it needs to provide three parameters: <tgt_range>, <src_block_count> and
- // "-"/<src_range>.
- if (params.cpos + 2 >= params.tokens.size()) {
- LOG(ERROR) << "invalid parameters";
- return -1;
- }
-
- // <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) {
- return -1;
- }
-
- // Return now if target blocks already have expected content.
- if (VerifyBlocks(tgthash, tgtbuffer, tgt->blocks(), false) == 0) {
- return 1;
- }
-
- // Load source blocks.
- bool overlap = false;
- if (LoadSourceBlocks(params, *tgt, src_blocks, &overlap) == -1) {
- return -1;
- }
-
- if (VerifyBlocks(srchash, params.buffer, *src_blocks, true) == 0) {
- // If source and target blocks overlap, stash the source blocks so we can resume from possible
- // write errors. In verify mode, we can skip stashing because the source blocks won't be
- // overwritten.
- if (overlap && params.canwrite) {
- LOG(INFO) << "stashing " << *src_blocks << " overlapping blocks to " << srchash;
-
- bool stash_exists = false;
- if (WriteStash(params.stashbase, srchash, *src_blocks, params.buffer, true,
- &stash_exists) != 0) {
- LOG(ERROR) << "failed to stash overlapping source blocks";
- return -1;
- }
-
- params.stashed += *src_blocks;
- // Can be deleted when the write has completed.
- if (!stash_exists) {
- params.freestash = srchash;
- }
- }
-
- // Source blocks have expected content, command can proceed.
- return 0;
- }
-
- if (overlap && LoadStash(params, srchash, true, ¶ms.buffer, true) == 0) {
- // Overlapping source blocks were previously stashed, command can proceed. We are recovering
- // from an interrupted command, so we don't know if the stash can safely be deleted after this
- // command.
- return 0;
- }
-
- // Valid source data not available, update cannot be resumed.
- LOG(ERROR) << "partition has unexpected contents";
- PrintHashForCorruptedSourceBlocks(params, params.buffer);
-
- params.isunresumable = true;
-
- return -1;
-}
-
-static int PerformCommandMove(CommandParameters& params) {
- size_t blocks = 0;
- RangeSet tgt;
- int status = LoadSrcTgtVersion3(params, &tgt, &blocks, true);
-
- if (status == -1) {
- LOG(ERROR) << "failed to read blocks for move";
- return -1;
- }
-
- if (status == 0) {
- params.foundwrites = true;
- } else {
- params.target_verified = true;
- if (params.foundwrites) {
- LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]";
- }
- }
-
- if (params.canwrite) {
- if (status == 0) {
- LOG(INFO) << " moving " << blocks << " blocks";
-
- if (WriteBlocks(tgt, params.buffer, params.fd) == -1) {
- return -1;
- }
- } else {
- LOG(INFO) << "skipping " << blocks << " already moved blocks";
- }
- }
-
- if (!params.freestash.empty()) {
- FreeStash(params.stashbase, params.freestash);
- params.freestash.clear();
- }
-
- params.written += tgt.blocks();
-
- return 0;
-}
-
-static int PerformCommandStash(CommandParameters& params) {
- // <stash_id> <src_range>
- if (params.cpos + 1 >= params.tokens.size()) {
- LOG(ERROR) << "missing id and/or src range fields in stash command";
- return -1;
- }
-
- const std::string& id = params.tokens[params.cpos++];
- if (LoadStash(params, id, true, ¶ms.buffer, false) == 0) {
- // Stash file already exists and has expected contents. Do not read from source again, as the
- // source may have been already overwritten during a previous attempt.
- return 0;
- }
-
- RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]);
- CHECK(static_cast<bool>(src));
-
- size_t blocks = src.blocks();
- allocate(blocks * BLOCKSIZE, ¶ms.buffer);
- if (ReadBlocks(src, ¶ms.buffer, params.fd) == -1) {
- return -1;
- }
- stash_map[id] = src;
-
- if (VerifyBlocks(id, params.buffer, blocks, true) != 0) {
- // Source blocks have unexpected contents. If we actually need this data later, this is an
- // unrecoverable error. However, the command that uses the data may have already completed
- // previously, so the possible failure will occur during source block verification.
- LOG(ERROR) << "failed to load source blocks for stash " << id;
- return 0;
- }
-
- // In verify mode, we don't need to stash any blocks.
- if (!params.canwrite) {
- return 0;
- }
-
- LOG(INFO) << "stashing " << blocks << " blocks to " << id;
- int result = WriteStash(params.stashbase, id, blocks, params.buffer, false, nullptr);
- if (result == 0) {
- params.stashed += blocks;
- }
- return result;
-}
-
-static int PerformCommandFree(CommandParameters& params) {
- // <stash_id>
- if (params.cpos >= params.tokens.size()) {
- LOG(ERROR) << "missing stash id in free command";
- return -1;
- }
-
- const std::string& id = params.tokens[params.cpos++];
- stash_map.erase(id);
-
- if (params.createdstash || params.canwrite) {
- return FreeStash(params.stashbase, id);
- }
-
- return 0;
-}
-
-static int PerformCommandZero(CommandParameters& params) {
- if (params.cpos >= params.tokens.size()) {
- LOG(ERROR) << "missing target blocks for zero";
- return -1;
- }
-
- RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
- CHECK(static_cast<bool>(tgt));
-
- LOG(INFO) << " zeroing " << tgt.blocks() << " blocks";
-
- allocate(BLOCKSIZE, ¶ms.buffer);
- memset(params.buffer.data(), 0, BLOCKSIZE);
-
- if (params.canwrite) {
- for (const auto& [begin, end] : tgt) {
- off64_t offset = static_cast<off64_t>(begin) * BLOCKSIZE;
- size_t size = (end - begin) * BLOCKSIZE;
- if (!discard_blocks(params.fd, offset, size)) {
- return -1;
- }
-
- if (!check_lseek(params.fd, offset, SEEK_SET)) {
- return -1;
- }
-
- for (size_t j = begin; j < end; ++j) {
- if (!android::base::WriteFully(params.fd, params.buffer.data(), BLOCKSIZE)) {
- failure_type = errno == EIO ? kEioFailure : kFwriteFailure;
- PLOG(ERROR) << "Failed to write " << BLOCKSIZE << " bytes of data";
- return -1;
- }
- }
- }
- }
-
- if (params.cmdname[0] == 'z') {
- // Update only for the zero command, as the erase command will call
- // this if DEBUG_ERASE is defined.
- params.written += tgt.blocks();
- }
-
- return 0;
-}
-
-static int PerformCommandNew(CommandParameters& params) {
- if (params.cpos >= params.tokens.size()) {
- LOG(ERROR) << "missing target blocks for new";
- return -1;
- }
-
- 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";
-
- pthread_mutex_lock(¶ms.nti.mu);
- params.nti.writer = std::make_unique<RangeSinkWriter>(params.fd, tgt);
- pthread_cond_broadcast(¶ms.nti.cv);
-
- while (params.nti.writer != nullptr) {
- if (!params.nti.receiver_available) {
- LOG(ERROR) << "missing " << (tgt.blocks() * BLOCKSIZE - params.nti.writer->BytesWritten())
- << " bytes of new data";
- pthread_mutex_unlock(¶ms.nti.mu);
- return -1;
- }
- pthread_cond_wait(¶ms.nti.cv, ¶ms.nti.mu);
- }
-
- pthread_mutex_unlock(¶ms.nti.mu);
- }
-
- params.written += tgt.blocks();
-
- return 0;
-}
-
-static int PerformCommandDiff(CommandParameters& params) {
- // <offset> <length>
- if (params.cpos + 1 >= params.tokens.size()) {
- LOG(ERROR) << "missing patch offset or length for " << params.cmdname;
- return -1;
- }
-
- size_t offset;
- if (!android::base::ParseUint(params.tokens[params.cpos++], &offset)) {
- LOG(ERROR) << "invalid patch offset";
- return -1;
- }
-
- size_t len;
- if (!android::base::ParseUint(params.tokens[params.cpos++], &len)) {
- LOG(ERROR) << "invalid patch len";
- return -1;
- }
-
- RangeSet tgt;
- size_t blocks = 0;
- int status = LoadSrcTgtVersion3(params, &tgt, &blocks, false);
-
- if (status == -1) {
- LOG(ERROR) << "failed to read blocks for diff";
- return -1;
- }
-
- if (status == 0) {
- params.foundwrites = true;
- } else {
- params.target_verified = true;
- if (params.foundwrites) {
- LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]";
- }
- }
-
- if (params.canwrite) {
- if (status == 0) {
- LOG(INFO) << "patching " << blocks << " blocks to " << tgt.blocks();
- Value patch_value(
- Value::Type::BLOB,
- std::string(reinterpret_cast<const char*>(params.patch_start + offset), len));
-
- RangeSinkWriter writer(params.fd, tgt);
- if (params.cmdname[0] == 'i') { // imgdiff
- if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, patch_value,
- std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1,
- std::placeholders::_2),
- nullptr) != 0) {
- LOG(ERROR) << "Failed to apply image patch.";
- failure_type = kPatchApplicationFailure;
- return -1;
- }
- } else {
- if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, patch_value, 0,
- std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1,
- std::placeholders::_2)) != 0) {
- LOG(ERROR) << "Failed to apply bsdiff patch.";
- failure_type = kPatchApplicationFailure;
- return -1;
- }
- }
-
- // We expect the output of the patcher to fill the tgt ranges exactly.
- if (!writer.Finished()) {
- LOG(ERROR) << "Failed to fully write target blocks (range sink underrun): Missing "
- << writer.AvailableSpace() << " bytes";
- failure_type = kPatchApplicationFailure;
- return -1;
- }
- } else {
- LOG(INFO) << "skipping " << blocks << " blocks already patched to " << tgt.blocks() << " ["
- << params.cmdline << "]";
- }
- }
-
- if (!params.freestash.empty()) {
- FreeStash(params.stashbase, params.freestash);
- params.freestash.clear();
- }
-
- params.written += tgt.blocks();
-
- return 0;
-}
-
-static int PerformCommandErase(CommandParameters& params) {
- if (DEBUG_ERASE) {
- return PerformCommandZero(params);
- }
-
- struct stat sb;
- if (fstat(params.fd, &sb) == -1) {
- PLOG(ERROR) << "failed to fstat device to erase";
- return -1;
- }
-
- if (!S_ISBLK(sb.st_mode)) {
- LOG(ERROR) << "not a block device; skipping erase";
- return -1;
- }
-
- if (params.cpos >= params.tokens.size()) {
- LOG(ERROR) << "missing target blocks for erase";
- return -1;
- }
-
- RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
- CHECK(static_cast<bool>(tgt));
-
- if (params.canwrite) {
- LOG(INFO) << " erasing " << tgt.blocks() << " blocks";
-
- for (const auto& [begin, end] : tgt) {
- off64_t offset = static_cast<off64_t>(begin) * BLOCKSIZE;
- size_t size = (end - begin) * BLOCKSIZE;
- if (!discard_blocks(params.fd, offset, size, true /* force */)) {
- return -1;
- }
- }
- }
-
- return 0;
-}
-
-static int PerformCommandAbort(CommandParameters&) {
- LOG(INFO) << "Aborting as instructed";
- return -1;
-}
-
-// Computes the hash_tree bytes based on the parameters, checks if the root hash of the tree
-// matches the expected hash and writes the result to the specified range on the block_device.
-// Hash_tree computation arguments:
-// hash_tree_ranges
-// source_ranges
-// hash_algorithm
-// salt_hex
-// root_hash
-static int PerformCommandComputeHashTree(CommandParameters& params) {
- if (params.cpos + 5 != params.tokens.size()) {
- LOG(ERROR) << "Invaild arguments count in hash computation " << params.cmdline;
- return -1;
- }
-
- // Expects the hash_tree data to be contiguous.
- RangeSet hash_tree_ranges = RangeSet::Parse(params.tokens[params.cpos++]);
- if (!hash_tree_ranges || hash_tree_ranges.size() != 1) {
- LOG(ERROR) << "Invalid hash tree ranges in " << params.cmdline;
- return -1;
- }
-
- RangeSet source_ranges = RangeSet::Parse(params.tokens[params.cpos++]);
- if (!source_ranges) {
- LOG(ERROR) << "Invalid source ranges in " << params.cmdline;
- return -1;
- }
-
- auto hash_function = HashTreeBuilder::HashFunction(params.tokens[params.cpos++]);
- if (hash_function == nullptr) {
- LOG(ERROR) << "Invalid hash algorithm in " << params.cmdline;
- return -1;
- }
-
- std::vector<unsigned char> salt;
- std::string salt_hex = params.tokens[params.cpos++];
- if (salt_hex.empty() || !HashTreeBuilder::ParseBytesArrayFromString(salt_hex, &salt)) {
- LOG(ERROR) << "Failed to parse salt in " << params.cmdline;
- return -1;
- }
-
- std::string expected_root_hash = params.tokens[params.cpos++];
- if (expected_root_hash.empty()) {
- LOG(ERROR) << "Invalid root hash in " << params.cmdline;
- return -1;
- }
-
- // Starts the hash_tree computation.
- HashTreeBuilder builder(BLOCKSIZE, hash_function);
- if (!builder.Initialize(static_cast<int64_t>(source_ranges.blocks()) * BLOCKSIZE, salt)) {
- LOG(ERROR) << "Failed to initialize hash tree computation, source " << source_ranges.ToString()
- << ", salt " << salt_hex;
- return -1;
- }
-
- // Iterates through every block in the source_ranges and updates the hash tree structure
- // accordingly.
- for (const auto& [begin, end] : source_ranges) {
- uint8_t buffer[BLOCKSIZE];
- if (!check_lseek(params.fd, static_cast<off64_t>(begin) * BLOCKSIZE, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to block: " << begin;
- return -1;
- }
-
- for (size_t i = begin; i < end; i++) {
- if (!android::base::ReadFully(params.fd, buffer, BLOCKSIZE)) {
- failure_type = errno == EIO ? kEioFailure : kFreadFailure;
- LOG(ERROR) << "Failed to read data in " << begin << ":" << end;
- return -1;
- }
-
- if (!builder.Update(reinterpret_cast<unsigned char*>(buffer), BLOCKSIZE)) {
- LOG(ERROR) << "Failed to update hash tree builder";
- return -1;
- }
- }
- }
-
- if (!builder.BuildHashTree()) {
- LOG(ERROR) << "Failed to build hash tree";
- return -1;
- }
-
- std::string root_hash_hex = HashTreeBuilder::BytesArrayToString(builder.root_hash());
- if (root_hash_hex != expected_root_hash) {
- LOG(ERROR) << "Root hash of the verity hash tree doesn't match the expected value. Expected: "
- << expected_root_hash << ", actual: " << root_hash_hex;
- return -1;
- }
-
- uint64_t write_offset = static_cast<uint64_t>(hash_tree_ranges.GetBlockNumber(0)) * BLOCKSIZE;
- if (params.canwrite && !builder.WriteHashTreeToFd(params.fd, write_offset)) {
- LOG(ERROR) << "Failed to write hash tree to output";
- return -1;
- }
-
- // TODO(xunchang) validates the written bytes
-
- return 0;
-}
-
-using CommandFunction = std::function<int(CommandParameters&)>;
-
-using CommandMap = std::unordered_map<Command::Type, CommandFunction>;
-
-static bool Sha1DevicePath(const std::string& path, uint8_t digest[SHA_DIGEST_LENGTH]) {
- auto device_name = android::base::Basename(path);
- auto dm_target_name_path = "/sys/block/" + device_name + "/dm/name";
-
- struct stat sb;
- if (stat(dm_target_name_path.c_str(), &sb) == 0) {
- // This is a device mapper target. Use partition name as part of the hash instead. Do not
- // include extents as part of the hash, because the size of a partition may be shrunk after
- // the patches are applied.
- std::string dm_target_name;
- if (!android::base::ReadFileToString(dm_target_name_path, &dm_target_name)) {
- PLOG(ERROR) << "Cannot read " << dm_target_name_path;
- return false;
- }
- SHA1(reinterpret_cast<const uint8_t*>(dm_target_name.data()), dm_target_name.size(), digest);
- return true;
- }
-
- if (errno != ENOENT) {
- // This is a device mapper target, but its name cannot be retrieved.
- PLOG(ERROR) << "Cannot get dm target name for " << path;
- return false;
- }
-
- // This doesn't appear to be a device mapper target, but if its name starts with dm-, something
- // else might have gone wrong.
- if (android::base::StartsWith(device_name, "dm-")) {
- LOG(WARNING) << "Device " << path << " starts with dm- but is not mapped by device-mapper.";
- }
-
- // Stash directory should be different for each partition to avoid conflicts when updating
- // multiple partitions at the same time, so we use the hash of the block device name as the base
- // directory.
- SHA1(reinterpret_cast<const uint8_t*>(path.data()), path.size(), digest);
- return true;
-}
-
-static Value* PerformBlockImageUpdate(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv,
- const CommandMap& command_map, bool dryrun) {
- CommandParameters params{};
- stash_map.clear();
- params.canwrite = !dryrun;
-
- LOG(INFO) << "performing " << (dryrun ? "verification" : "update");
- if (state->is_retry) {
- is_retry = true;
- LOG(INFO) << "This update is a retry.";
- }
- if (argv.size() != 4) {
- ErrorAbort(state, kArgsParsingFailure, "block_image_update expects 4 arguments, got %zu",
- argv.size());
- return StringValue("");
- }
-
- std::vector<std::unique_ptr<Value>> args;
- if (!ReadValueArgs(state, argv, &args)) {
- return nullptr;
- }
-
- // args:
- // - block device (or file) to modify in-place
- // - transfer list (blob)
- // - new data stream (filename within package.zip)
- // - patch stream (filename within package.zip, must be uncompressed)
- const std::unique_ptr<Value>& blockdev_filename = args[0];
- const std::unique_ptr<Value>& transfer_list_value = args[1];
- const std::unique_ptr<Value>& new_data_fn = args[2];
- const std::unique_ptr<Value>& patch_data_fn = args[3];
-
- if (blockdev_filename->type != Value::Type::STRING) {
- ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", name);
- return StringValue("");
- }
- if (transfer_list_value->type != Value::Type::BLOB) {
- ErrorAbort(state, kArgsParsingFailure, "transfer_list argument to %s must be blob", name);
- return StringValue("");
- }
- if (new_data_fn->type != Value::Type::STRING) {
- ErrorAbort(state, kArgsParsingFailure, "new_data_fn argument to %s must be string", name);
- return StringValue("");
- }
- if (patch_data_fn->type != Value::Type::STRING) {
- ErrorAbort(state, kArgsParsingFailure, "patch_data_fn argument to %s must be string", name);
- return StringValue("");
- }
-
- auto updater = state->updater;
- auto block_device_path = updater->FindBlockDeviceName(blockdev_filename->data);
- if (block_device_path.empty()) {
- LOG(ERROR) << "Block device path for " << blockdev_filename->data << " not found. " << name
- << " failed.";
- return StringValue("");
- }
-
- ZipArchiveHandle za = updater->GetPackageHandle();
- if (za == nullptr) {
- return StringValue("");
- }
-
- std::string_view path_data(patch_data_fn->data);
- ZipEntry64 patch_entry;
- if (FindEntry(za, path_data, &patch_entry) != 0) {
- LOG(ERROR) << name << "(): no file \"" << patch_data_fn->data << "\" in package";
- return StringValue("");
- }
- params.patch_start = updater->GetMappedPackageAddress() + patch_entry.offset;
-
- std::string_view new_data(new_data_fn->data);
- ZipEntry64 new_entry;
- if (FindEntry(za, new_data, &new_entry) != 0) {
- LOG(ERROR) << name << "(): no file \"" << new_data_fn->data << "\" in package";
- return StringValue("");
- }
-
- params.fd.reset(TEMP_FAILURE_RETRY(open(block_device_path.c_str(), O_RDWR)));
- if (params.fd == -1) {
- failure_type = errno == EIO ? kEioFailure : kFileOpenFailure;
- PLOG(ERROR) << "open \"" << block_device_path << "\" failed";
- return StringValue("");
- }
-
- uint8_t digest[SHA_DIGEST_LENGTH];
- if (!Sha1DevicePath(block_device_path, digest)) {
- return StringValue("");
- }
- params.stashbase = print_sha1(digest);
-
- // Possibly do return early on retry, by checking the marker. If the update on this partition has
- // been finished (but interrupted at a later point), there could be leftover on /cache that would
- // fail the no-op retry.
- std::string updated_marker = GetStashFileName(params.stashbase + ".UPDATED", "", "");
- if (is_retry) {
- struct stat sb;
- int result = stat(updated_marker.c_str(), &sb);
- if (result == 0) {
- LOG(INFO) << "Skipping already updated partition " << block_device_path << " based on marker";
- return StringValue("t");
- }
- } else {
- // Delete the obsolete marker if any.
- std::string err;
- if (!android::base::RemoveFileIfExists(updated_marker, &err)) {
- LOG(ERROR) << "Failed to remove partition updated marker " << updated_marker << ": " << err;
- return StringValue("");
- }
- }
-
- static constexpr size_t kTransferListHeaderLines = 4;
- std::vector<std::string> lines = android::base::Split(transfer_list_value->data, "\n");
- if (lines.size() < kTransferListHeaderLines) {
- ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zu]",
- lines.size());
- return StringValue("");
- }
-
- // First line in transfer list is the version number.
- if (!android::base::ParseInt(lines[0], ¶ms.version, 3, 4)) {
- LOG(ERROR) << "unexpected transfer list version [" << lines[0] << "]";
- return StringValue("");
- }
-
- LOG(INFO) << "blockimg version is " << params.version;
-
- // Second line in transfer list is the total number of blocks we expect to write.
- size_t total_blocks;
- if (!android::base::ParseUint(lines[1], &total_blocks)) {
- ErrorAbort(state, kArgsParsingFailure, "unexpected block count [%s]", lines[1].c_str());
- return StringValue("");
- }
-
- if (total_blocks == 0) {
- return StringValue("t");
- }
-
- // Third line is how many stash entries are needed simultaneously.
- LOG(INFO) << "maximum stash entries " << lines[2];
-
- // Fourth line is the maximum number of blocks that will be stashed simultaneously
- size_t stash_max_blocks;
- if (!android::base::ParseUint(lines[3], &stash_max_blocks)) {
- ErrorAbort(state, kArgsParsingFailure, "unexpected maximum stash blocks [%s]",
- lines[3].c_str());
- return StringValue("");
- }
-
- int res = CreateStash(state, stash_max_blocks, params.stashbase);
- if (res == -1) {
- return StringValue("");
- }
- params.createdstash = res;
-
- // Set up the new data writer.
- if (params.canwrite) {
- params.nti.za = za;
- params.nti.entry = new_entry;
- params.nti.brotli_compressed = android::base::EndsWith(new_data_fn->data, ".br");
- if (params.nti.brotli_compressed) {
- // Initialize brotli decoder state.
- params.nti.brotli_decoder_state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
- }
- params.nti.receiver_available = true;
-
- pthread_mutex_init(¶ms.nti.mu, nullptr);
- pthread_cond_init(¶ms.nti.cv, nullptr);
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-
- int error = pthread_create(¶ms.thread, &attr, unzip_new_data, ¶ms.nti);
- if (error != 0) {
- LOG(ERROR) << "pthread_create failed: " << strerror(error);
- return StringValue("");
- }
- }
-
- // When performing an update, save the index and cmdline of the current command into the
- // last_command_file.
- // Upon resuming an update, read the saved index first; then
- // 1. In verification mode, check if the 'move' or 'diff' commands before the saved index has
- // the expected target blocks already. If not, these commands cannot be skipped and we need
- // to attempt to execute them again. Therefore, we will delete the last_command_file so that
- // the update will resume from the start of the transfer list.
- // 2. In update mode, skip all commands before the saved index. Therefore, we can avoid deleting
- // stashes with duplicate id unintentionally (b/69858743); and also speed up the update.
- // If an update succeeds or is unresumable, delete the last_command_file.
- bool skip_executed_command = true;
- size_t saved_last_command_index;
- if (!ParseLastCommandFile(&saved_last_command_index)) {
- DeleteLastCommandFile();
- // We failed to parse the last command. Disallow skipping executed commands.
- skip_executed_command = false;
- }
-
- int rc = -1;
-
- // Subsequent lines are all individual transfer commands
- for (size_t i = kTransferListHeaderLines; i < lines.size(); i++) {
- const std::string& line = lines[i];
- if (line.empty()) continue;
-
- size_t cmdindex = i - kTransferListHeaderLines;
- params.tokens = android::base::Split(line, " ");
- params.cpos = 0;
- params.cmdname = params.tokens[params.cpos++];
- params.cmdline = line;
- params.target_verified = false;
-
- Command::Type cmd_type = Command::ParseType(params.cmdname);
- if (cmd_type == Command::Type::LAST) {
- LOG(ERROR) << "unexpected command [" << params.cmdname << "]";
- goto pbiudone;
- }
-
- const CommandFunction& performer = command_map.at(cmd_type);
-
- // Skip the command if we explicitly set the corresponding function pointer to nullptr, e.g.
- // "erase" during block_image_verify.
- if (performer == nullptr) {
- LOG(DEBUG) << "skip executing command [" << line << "]";
- continue;
- }
-
- // Skip all commands before the saved last command index when resuming an update, except for
- // "new" command. Because new commands read in the data sequentially.
- if (params.canwrite && skip_executed_command && cmdindex <= saved_last_command_index &&
- cmd_type != Command::Type::NEW) {
- LOG(INFO) << "Skipping already executed command: " << cmdindex
- << ", last executed command for previous update: " << saved_last_command_index;
- continue;
- }
-
- if (performer(params) == -1) {
- LOG(ERROR) << "failed to execute command [" << line << "]";
- if (cmd_type == Command::Type::COMPUTE_HASH_TREE && failure_type == kNoCause) {
- failure_type = kHashTreeComputationFailure;
- }
- goto pbiudone;
- }
-
- // In verify mode, check if the commands before the saved last_command_index have been executed
- // correctly. If some target blocks have unexpected contents, delete the last command file so
- // that we will resume the update from the first command in the transfer list.
- if (!params.canwrite && skip_executed_command && cmdindex <= saved_last_command_index) {
- // TODO(xunchang) check that the cmdline of the saved index is correct.
- if ((cmd_type == Command::Type::MOVE || cmd_type == Command::Type::BSDIFF ||
- cmd_type == Command::Type::IMGDIFF) &&
- !params.target_verified) {
- LOG(WARNING) << "Previously executed command " << saved_last_command_index << ": "
- << params.cmdline << " doesn't produce expected target blocks.";
- skip_executed_command = false;
- DeleteLastCommandFile();
- }
- }
-
- if (params.canwrite) {
- if (fsync(params.fd) == -1) {
- failure_type = errno == EIO ? kEioFailure : kFsyncFailure;
- PLOG(ERROR) << "fsync failed";
- goto pbiudone;
- }
-
- if (!UpdateLastCommandIndex(cmdindex, params.cmdline)) {
- LOG(WARNING) << "Failed to update the last command file.";
- }
-
- updater->WriteToCommandPipe(
- android::base::StringPrintf("set_progress %.4f",
- static_cast<double>(params.written) / total_blocks),
- true);
- }
- }
-
- rc = 0;
-
-pbiudone:
- if (params.canwrite) {
- pthread_mutex_lock(¶ms.nti.mu);
- if (params.nti.receiver_available) {
- LOG(WARNING) << "new data receiver is still available after executing all commands.";
- }
- params.nti.receiver_available = false;
- pthread_cond_broadcast(¶ms.nti.cv);
- pthread_mutex_unlock(¶ms.nti.mu);
- int ret = pthread_join(params.thread, nullptr);
- if (ret != 0) {
- LOG(WARNING) << "pthread join returned with " << strerror(ret);
- }
-
- if (rc == 0) {
- LOG(INFO) << "wrote " << params.written << " blocks; expected " << total_blocks;
- LOG(INFO) << "stashed " << params.stashed << " blocks";
- LOG(INFO) << "max alloc needed was " << params.buffer.size();
-
- const char* partition = strrchr(block_device_path.c_str(), '/');
- if (partition != nullptr && *(partition + 1) != 0) {
- updater->WriteToCommandPipe(
- android::base::StringPrintf("log bytes_written_%s: %" PRIu64, partition + 1,
- static_cast<uint64_t>(params.written) * BLOCKSIZE));
- updater->WriteToCommandPipe(
- android::base::StringPrintf("log bytes_stashed_%s: %" PRIu64, partition + 1,
- static_cast<uint64_t>(params.stashed) * BLOCKSIZE),
- true);
- }
- // Delete stash only after successfully completing the update, as it may contain blocks needed
- // to complete the update later.
- DeleteStash(params.stashbase);
- DeleteLastCommandFile();
-
- // Create a marker on /cache partition, which allows skipping the update on this partition on
- // retry. The marker will be removed once booting into normal boot, or before starting next
- // fresh install.
- if (!SetUpdatedMarker(updated_marker)) {
- LOG(WARNING) << "Failed to set updated marker; continuing";
- }
- }
-
- pthread_mutex_destroy(¶ms.nti.mu);
- pthread_cond_destroy(¶ms.nti.cv);
- } else if (rc == 0) {
- LOG(INFO) << "verified partition contents; update may be resumed";
- }
-
- if (fsync(params.fd) == -1) {
- failure_type = errno == EIO ? kEioFailure : kFsyncFailure;
- PLOG(ERROR) << "fsync failed";
- }
- // params.fd will be automatically closed because it's a unique_fd.
-
- if (params.nti.brotli_decoder_state != nullptr) {
- BrotliDecoderDestroyInstance(params.nti.brotli_decoder_state);
- }
-
- // Delete the last command file if the update cannot be resumed.
- if (params.isunresumable) {
- DeleteLastCommandFile();
- }
-
- // Only delete the stash if the update cannot be resumed, or it's a verification run and we
- // created the stash.
- if (params.isunresumable || (!params.canwrite && params.createdstash)) {
- DeleteStash(params.stashbase);
- }
-
- if (failure_type != kNoCause && state->cause_code == kNoCause) {
- state->cause_code = failure_type;
- }
-
- return StringValue(rc == 0 ? "t" : "");
-}
-
-/**
- * The transfer list is a text file containing commands to transfer data from one place to another
- * on the target partition. We parse it and execute the commands in order:
- *
- * zero [rangeset]
- * - Fill the indicated blocks with zeros.
- *
- * new [rangeset]
- * - Fill the blocks with data read from the new_data file.
- *
- * erase [rangeset]
- * - Mark the given blocks as empty.
- *
- * move <...>
- * bsdiff <patchstart> <patchlen> <...>
- * imgdiff <patchstart> <patchlen> <...>
- * - Read the source blocks, apply a patch (or not in the case of move), write result to target
- * blocks. bsdiff or imgdiff specifies the type of patch; move means no patch at all.
- *
- * See the comments in LoadSrcTgtVersion3() for a description of the <...> format.
- *
- * stash <stash_id> <src_range>
- * - Load the given source range and stash the data in the given slot of the stash table.
- *
- * free <stash_id>
- * - Free the given stash data.
- *
- * The creator of the transfer list will guarantee that no block is read (ie, used as the source for
- * a patch or move) after it has been written.
- *
- * The creator will guarantee that a given stash is loaded (with a stash command) before it's used
- * in a move/bsdiff/imgdiff command.
- *
- * Within one command the source and target ranges may overlap so in general we need to read the
- * entire source into memory before writing anything to the target blocks.
- *
- * All the patch data is concatenated into one patch_data file in the update package. It must be
- * stored uncompressed because we memory-map it in directly from the archive. (Since patches are
- * already compressed, we lose very little by not compressing their concatenation.)
- *
- * Commands that read data from the partition (i.e. move/bsdiff/imgdiff/stash) have one or more
- * additional hashes before the range parameters, which are used to check if the command has already
- * been completed and verify the integrity of the source data.
- */
-Value* BlockImageVerifyFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- // Commands which are not allowed are set to nullptr to skip them completely.
- const CommandMap command_map{
- // clang-format off
- { Command::Type::ABORT, PerformCommandAbort },
- { Command::Type::BSDIFF, PerformCommandDiff },
- { Command::Type::COMPUTE_HASH_TREE, nullptr },
- { Command::Type::ERASE, nullptr },
- { Command::Type::FREE, PerformCommandFree },
- { Command::Type::IMGDIFF, PerformCommandDiff },
- { Command::Type::MOVE, PerformCommandMove },
- { Command::Type::NEW, nullptr },
- { Command::Type::STASH, PerformCommandStash },
- { Command::Type::ZERO, nullptr },
- // clang-format on
- };
- CHECK_EQ(static_cast<size_t>(Command::Type::LAST), command_map.size());
-
- // Perform a dry run without writing to test if an update can proceed.
- return PerformBlockImageUpdate(name, state, argv, command_map, true);
-}
-
-Value* BlockImageUpdateFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- const CommandMap command_map{
- // clang-format off
- { Command::Type::ABORT, PerformCommandAbort },
- { Command::Type::BSDIFF, PerformCommandDiff },
- { Command::Type::COMPUTE_HASH_TREE, PerformCommandComputeHashTree },
- { Command::Type::ERASE, PerformCommandErase },
- { Command::Type::FREE, PerformCommandFree },
- { Command::Type::IMGDIFF, PerformCommandDiff },
- { Command::Type::MOVE, PerformCommandMove },
- { Command::Type::NEW, PerformCommandNew },
- { Command::Type::STASH, PerformCommandStash },
- { Command::Type::ZERO, PerformCommandZero },
- // clang-format on
- };
- CHECK_EQ(static_cast<size_t>(Command::Type::LAST), command_map.size());
-
- return PerformBlockImageUpdate(name, state, argv, command_map, false);
-}
-
-Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 2) {
- ErrorAbort(state, kArgsParsingFailure, "range_sha1 expects 2 arguments, got %zu", argv.size());
- return StringValue("");
- }
-
- std::vector<std::unique_ptr<Value>> args;
- if (!ReadValueArgs(state, argv, &args)) {
- return nullptr;
- }
-
- const std::unique_ptr<Value>& blockdev_filename = args[0];
- const std::unique_ptr<Value>& ranges = args[1];
-
- if (blockdev_filename->type != Value::Type::STRING) {
- ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", name);
- return StringValue("");
- }
- if (ranges->type != Value::Type::STRING) {
- ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name);
- return StringValue("");
- }
-
- auto block_device_path = state->updater->FindBlockDeviceName(blockdev_filename->data);
- if (block_device_path.empty()) {
- LOG(ERROR) << "Block device path for " << blockdev_filename->data << " not found. " << name
- << " failed.";
- return StringValue("");
- }
-
- android::base::unique_fd fd(open(block_device_path.c_str(), O_RDWR));
- if (fd == -1) {
- CauseCode cause_code = errno == EIO ? kEioFailure : kFileOpenFailure;
- ErrorAbort(state, cause_code, "open \"%s\" failed: %s", block_device_path.c_str(),
- strerror(errno));
- return StringValue("");
- }
-
- RangeSet rs = RangeSet::Parse(ranges->data);
- CHECK(static_cast<bool>(rs));
-
- SHA_CTX ctx;
- SHA1_Init(&ctx);
-
- std::vector<uint8_t> buffer(BLOCKSIZE);
- for (const auto& [begin, end] : rs) {
- if (!check_lseek(fd, static_cast<off64_t>(begin) * BLOCKSIZE, SEEK_SET)) {
- ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", block_device_path.c_str(),
- strerror(errno));
- return StringValue("");
- }
-
- for (size_t j = begin; j < end; ++j) {
- if (!android::base::ReadFully(fd, buffer.data(), BLOCKSIZE)) {
- CauseCode cause_code = errno == EIO ? kEioFailure : kFreadFailure;
- ErrorAbort(state, cause_code, "failed to read %s: %s", block_device_path.c_str(),
- strerror(errno));
- return StringValue("");
- }
-
- SHA1_Update(&ctx, buffer.data(), BLOCKSIZE);
- }
- }
- uint8_t digest[SHA_DIGEST_LENGTH];
- SHA1_Final(digest, &ctx);
-
- return StringValue(print_sha1(digest));
-}
-
-// This function checks if a device has been remounted R/W prior to an incremental
-// OTA update. This is an common cause of update abortion. The function reads the
-// 1st block of each partition and check for mounting time/count. It return string "t"
-// if executes successfully and an empty string otherwise.
-
-Value* CheckFirstBlockFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 1) {
- ErrorAbort(state, kArgsParsingFailure, "check_first_block expects 1 argument, got %zu",
- argv.size());
- return StringValue("");
- }
-
- std::vector<std::unique_ptr<Value>> args;
- if (!ReadValueArgs(state, argv, &args)) {
- return nullptr;
- }
-
- const std::unique_ptr<Value>& arg_filename = args[0];
-
- if (arg_filename->type != Value::Type::STRING) {
- ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name);
- return StringValue("");
- }
-
- auto block_device_path = state->updater->FindBlockDeviceName(arg_filename->data);
- if (block_device_path.empty()) {
- LOG(ERROR) << "Block device path for " << arg_filename->data << " not found. " << name
- << " failed.";
- return StringValue("");
- }
-
- android::base::unique_fd fd(open(block_device_path.c_str(), O_RDONLY));
- if (fd == -1) {
- CauseCode cause_code = errno == EIO ? kEioFailure : kFileOpenFailure;
- ErrorAbort(state, cause_code, "open \"%s\" failed: %s", block_device_path.c_str(),
- strerror(errno));
- return StringValue("");
- }
-
- RangeSet blk0(std::vector<Range>{ Range{ 0, 1 } });
- std::vector<uint8_t> block0_buffer(BLOCKSIZE);
-
- if (ReadBlocks(blk0, &block0_buffer, fd) == -1) {
- CauseCode cause_code = errno == EIO ? kEioFailure : kFreadFailure;
- ErrorAbort(state, cause_code, "failed to read %s: %s", block_device_path.c_str(),
- strerror(errno));
- return StringValue("");
- }
-
- // https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout
- // Super block starts from block 0, offset 0x400
- // 0x2C: len32 Mount time
- // 0x30: len32 Write time
- // 0x34: len16 Number of mounts since the last fsck
- // 0x38: len16 Magic signature 0xEF53
-
- time_t mount_time = *reinterpret_cast<uint32_t*>(&block0_buffer[0x400 + 0x2C]);
- uint16_t mount_count = *reinterpret_cast<uint16_t*>(&block0_buffer[0x400 + 0x34]);
-
- if (mount_count > 0) {
- state->updater->UiPrint(
- android::base::StringPrintf("Device was remounted R/W %" PRIu16 " times", mount_count));
- state->updater->UiPrint(
- android::base::StringPrintf("Last remount happened on %s", ctime(&mount_time)));
- }
-
- return StringValue("t");
-}
-
-Value* BlockImageRecoverFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 2) {
- ErrorAbort(state, kArgsParsingFailure, "block_image_recover expects 2 arguments, got %zu",
- argv.size());
- return StringValue("");
- }
-
- std::vector<std::unique_ptr<Value>> args;
- if (!ReadValueArgs(state, argv, &args)) {
- return nullptr;
- }
-
- const std::unique_ptr<Value>& filename = args[0];
- const std::unique_ptr<Value>& ranges = args[1];
-
- if (filename->type != Value::Type::STRING) {
- ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name);
- return StringValue("");
- }
- if (ranges->type != Value::Type::STRING) {
- 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("");
- }
-
- auto block_device_path = state->updater->FindBlockDeviceName(filename->data);
- if (block_device_path.empty()) {
- LOG(ERROR) << "Block device path for " << filename->data << " not found. " << name
- << " failed.";
- return StringValue("");
- }
-
- // Output notice to log when recover is attempted
- LOG(INFO) << block_device_path << " image corrupted, attempting to recover...";
-
- // When opened with O_RDWR, libfec rewrites corrupted blocks when they are read
- fec::io fh(block_device_path, O_RDWR);
-
- if (!fh) {
- ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", block_device_path.c_str(),
- strerror(errno));
- return StringValue("");
- }
-
- if (!fh.has_ecc() || !fh.has_verity()) {
- ErrorAbort(state, kLibfecFailure, "unable to use metadata to correct errors");
- return StringValue("");
- }
-
- fec_status status;
- if (!fh.get_status(status)) {
- ErrorAbort(state, kLibfecFailure, "failed to read FEC status");
- return StringValue("");
- }
-
- uint8_t buffer[BLOCKSIZE];
- for (const auto& [begin, end] : rs) {
- for (size_t j = begin; j < end; ++j) {
- // Stay within the data area, libfec validates and corrects metadata
- if (status.data_size <= static_cast<uint64_t>(j) * BLOCKSIZE) {
- continue;
- }
-
- if (fh.pread(buffer, BLOCKSIZE, static_cast<off64_t>(j) * BLOCKSIZE) != BLOCKSIZE) {
- ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s",
- block_device_path.c_str(), j, strerror(errno));
- return StringValue("");
- }
-
- // If we want to be able to recover from a situation where rewriting a corrected
- // block doesn't guarantee the same data will be returned when re-read later, we
- // can save a copy of corrected blocks to /cache. Note:
- //
- // 1. Maximum space required from /cache is the same as the maximum number of
- // corrupted blocks we can correct. For RS(255, 253) and a 2 GiB partition,
- // this would be ~16 MiB, for example.
- //
- // 2. To find out if this block was corrupted, call fec_get_status after each
- // read and check if the errors field value has increased.
- }
- }
- LOG(INFO) << "..." << block_device_path << " image recovered successfully.";
- return StringValue("t");
-}
-
-void RegisterBlockImageFunctions() {
- RegisterFunction("block_image_verify", BlockImageVerifyFn);
- RegisterFunction("block_image_update", BlockImageUpdateFn);
- RegisterFunction("block_image_recover", BlockImageRecoverFn);
- RegisterFunction("check_first_block", CheckFirstBlockFn);
- RegisterFunction("range_sha1", RangeSha1Fn);
-}
diff --git a/updater/build_info.cpp b/updater/build_info.cpp
deleted file mode 100644
index f168008..0000000
--- a/updater/build_info.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "updater/build_info.h"
-
-#include <stdio.h>
-
-#include <set>
-#include <vector>
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-
-#include "updater/target_files.h"
-
-bool BuildInfo::ParseTargetFile(const std::string_view target_file_path, bool extracted_input) {
- TargetFile target_file(std::string(target_file_path), extracted_input);
- if (!target_file.Open()) {
- return false;
- }
-
- if (!target_file.GetBuildProps(&build_props_)) {
- return false;
- }
-
- std::vector<FstabInfo> fstab_info_list;
- if (!target_file.ParseFstabInfo(&fstab_info_list)) {
- return false;
- }
-
- for (const auto& fstab_info : fstab_info_list) {
- for (const auto& directory : { "IMAGES", "RADIO" }) {
- std::string entry_name = directory + fstab_info.mount_point + ".img";
- if (!target_file.EntryExists(entry_name)) {
- LOG(WARNING) << "Failed to find the image entry in the target file: " << entry_name;
- continue;
- }
-
- temp_files_.emplace_back(work_dir_);
- auto& image_file = temp_files_.back();
- if (!target_file.ExtractImage(entry_name, fstab_info, work_dir_, &image_file)) {
- LOG(ERROR) << "Failed to set up source image files.";
- return false;
- }
-
- std::string mapped_path = image_file.path;
- // Rename the images to more readable ones if we want to keep the image.
- if (keep_images_) {
- mapped_path = work_dir_ + fstab_info.mount_point + ".img";
- image_file.release();
- if (rename(image_file.path, mapped_path.c_str()) != 0) {
- PLOG(ERROR) << "Failed to rename " << image_file.path << " to " << mapped_path;
- return false;
- }
- }
-
- LOG(INFO) << "Mounted " << fstab_info.mount_point << "\nMapping: " << fstab_info.blockdev_name
- << " to " << mapped_path;
-
- blockdev_map_.emplace(
- fstab_info.blockdev_name,
- FakeBlockDevice(fstab_info.blockdev_name, fstab_info.mount_point, mapped_path));
- break;
- }
- }
-
- return true;
-}
-
-std::string BuildInfo::GetProperty(const std::string_view key,
- const std::string_view default_value) const {
- // The logic to parse the ro.product properties should be in line with the generation script.
- // More details in common.py BuildInfo.GetBuildProp.
- // TODO(xunchang) handle the oem property and the source order defined in
- // ro.product.property_source_order
- const std::set<std::string, std::less<>> ro_product_props = {
- "ro.product.brand", "ro.product.device", "ro.product.manufacturer", "ro.product.model",
- "ro.product.name"
- };
- const std::vector<std::string> source_order = {
- "product", "odm", "vendor", "system_ext", "system",
- };
- if (ro_product_props.find(key) != ro_product_props.end()) {
- std::string_view key_suffix(key);
- CHECK(android::base::ConsumePrefix(&key_suffix, "ro.product"));
- for (const auto& source : source_order) {
- std::string resolved_key = "ro.product." + source + std::string(key_suffix);
- if (auto entry = build_props_.find(resolved_key); entry != build_props_.end()) {
- return entry->second;
- }
- }
- LOG(WARNING) << "Failed to find property: " << key;
- return std::string(default_value);
- } else if (key == "ro.build.fingerprint") {
- // clang-format off
- return android::base::StringPrintf("%s/%s/%s:%s/%s/%s:%s/%s",
- GetProperty("ro.product.brand", "").c_str(),
- GetProperty("ro.product.name", "").c_str(),
- GetProperty("ro.product.device", "").c_str(),
- GetProperty("ro.build.version.release", "").c_str(),
- GetProperty("ro.build.id", "").c_str(),
- GetProperty("ro.build.version.incremental", "").c_str(),
- GetProperty("ro.build.type", "").c_str(),
- GetProperty("ro.build.tags", "").c_str());
- // clang-format on
- }
-
- auto entry = build_props_.find(key);
- if (entry == build_props_.end()) {
- LOG(WARNING) << "Failed to find property: " << key;
- return std::string(default_value);
- }
-
- return entry->second;
-}
-
-std::string BuildInfo::FindBlockDeviceName(const std::string_view name) const {
- auto entry = blockdev_map_.find(name);
- if (entry == blockdev_map_.end()) {
- LOG(WARNING) << "Failed to find path to block device " << name;
- return "";
- }
-
- return entry->second.mounted_file_path;
-}
diff --git a/updater/commands.cpp b/updater/commands.cpp
deleted file mode 100644
index 1a7c272..0000000
--- a/updater/commands.cpp
+++ /dev/null
@@ -1,453 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "private/commands.h"
-
-#include <stdint.h>
-#include <string.h>
-
-#include <functional>
-#include <ostream>
-#include <string>
-#include <vector>
-
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <openssl/sha.h>
-
-#include "otautil/print_sha1.h"
-#include "otautil/rangeset.h"
-
-using namespace std::string_literals;
-
-bool Command::abort_allowed_ = false;
-
-Command::Command(Type type, size_t index, std::string cmdline, HashTreeInfo hash_tree_info)
- : type_(type),
- index_(index),
- cmdline_(std::move(cmdline)),
- hash_tree_info_(std::move(hash_tree_info)) {
- CHECK(type == Type::COMPUTE_HASH_TREE);
-}
-
-Command::Type Command::ParseType(const std::string& type_str) {
- if (type_str == "abort") {
- if (!abort_allowed_) {
- LOG(ERROR) << "ABORT disallowed";
- return Type::LAST;
- }
- return Type::ABORT;
- } else if (type_str == "bsdiff") {
- return Type::BSDIFF;
- } else if (type_str == "compute_hash_tree") {
- return Type::COMPUTE_HASH_TREE;
- } else if (type_str == "erase") {
- return Type::ERASE;
- } else if (type_str == "free") {
- return Type::FREE;
- } else if (type_str == "imgdiff") {
- return Type::IMGDIFF;
- } else if (type_str == "move") {
- return Type::MOVE;
- } else if (type_str == "new") {
- return Type::NEW;
- } else if (type_str == "stash") {
- return Type::STASH;
- } else if (type_str == "zero") {
- return Type::ZERO;
- }
- return Type::LAST;
-};
-
-bool Command::ParseTargetInfoAndSourceInfo(const std::vector<std::string>& tokens,
- const std::string& tgt_hash, TargetInfo* target,
- const std::string& src_hash, SourceInfo* source,
- std::string* err) {
- // We expect the given args (in 'tokens' vector) in one of the following formats.
- //
- // <tgt_ranges> <src_block_count> - <[stash_id:location] ...>
- // (loads data from stashes only)
- //
- // <tgt_ranges> <src_block_count> <src_ranges>
- // (loads data from source image only)
- //
- // <tgt_ranges> <src_block_count> <src_ranges> <src_ranges_location> <[stash_id:location] ...>
- // (loads data from both of source image and stashes)
-
- // At least it needs to provide three args: <tgt_ranges>, <src_block_count> and "-"/<src_ranges>.
- if (tokens.size() < 3) {
- *err = "invalid number of args";
- return false;
- }
-
- size_t pos = 0;
- RangeSet tgt_ranges = RangeSet::Parse(tokens[pos++]);
- if (!tgt_ranges) {
- *err = "invalid target ranges";
- return false;
- }
- *target = TargetInfo(tgt_hash, tgt_ranges);
-
- // <src_block_count>
- const std::string& token = tokens[pos++];
- size_t src_blocks;
- if (!android::base::ParseUint(token, &src_blocks)) {
- *err = "invalid src_block_count \""s + token + "\"";
- return false;
- }
-
- RangeSet src_ranges;
- RangeSet src_ranges_location;
- // "-" or <src_ranges> [<src_ranges_location>]
- if (tokens[pos] == "-") {
- // no source ranges, only stashes
- pos++;
- } else {
- src_ranges = RangeSet::Parse(tokens[pos++]);
- if (!src_ranges) {
- *err = "invalid source ranges";
- return false;
- }
-
- if (pos >= tokens.size()) {
- // No stashes, only source ranges.
- SourceInfo result(src_hash, src_ranges, {}, {});
-
- if (result.blocks() != src_blocks) {
- *err =
- android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(),
- src_ranges.ToString().c_str(), src_blocks);
- return false;
- }
-
- *source = result;
- return true;
- }
-
- src_ranges_location = RangeSet::Parse(tokens[pos++]);
- if (!src_ranges_location) {
- *err = "invalid source ranges location";
- return false;
- }
- }
-
- // <[stash_id:stash_location]>
- std::vector<StashInfo> stashes;
- while (pos < tokens.size()) {
- // Each word is a an index into the stash table, a colon, and then a RangeSet describing where
- // in the source block that stashed data should go.
- std::vector<std::string> pairs = android::base::Split(tokens[pos++], ":");
- if (pairs.size() != 2) {
- *err = "invalid stash info";
- return false;
- }
- RangeSet stash_location = RangeSet::Parse(pairs[1]);
- if (!stash_location) {
- *err = "invalid stash location";
- return false;
- }
- stashes.emplace_back(pairs[0], stash_location);
- }
-
- SourceInfo result(src_hash, src_ranges, src_ranges_location, stashes);
- if (src_blocks != result.blocks()) {
- *err = android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(),
- src_ranges.ToString().c_str(), src_blocks);
- return false;
- }
-
- *source = result;
- return true;
-}
-
-Command Command::Parse(const std::string& line, size_t index, std::string* err) {
- std::vector<std::string> tokens = android::base::Split(line, " ");
- size_t pos = 0;
- // tokens.size() will be 1 at least.
- Type op = ParseType(tokens[pos++]);
- if (op == Type::LAST) {
- *err = "invalid type";
- return {};
- }
-
- PatchInfo patch_info;
- TargetInfo target_info;
- SourceInfo source_info;
- StashInfo stash_info;
-
- if (op == Type::ZERO || op == Type::NEW || op == Type::ERASE) {
- // zero/new/erase <rangeset>
- if (pos + 1 != tokens.size()) {
- *err = android::base::StringPrintf("invalid number of args: %zu (expected 1)",
- tokens.size() - pos);
- return {};
- }
- RangeSet tgt_ranges = RangeSet::Parse(tokens[pos++]);
- if (!tgt_ranges) {
- return {};
- }
- static const std::string kUnknownHash{ "unknown-hash" };
- target_info = TargetInfo(kUnknownHash, tgt_ranges);
- } else if (op == Type::STASH) {
- // stash <stash_id> <src_ranges>
- if (pos + 2 != tokens.size()) {
- *err = android::base::StringPrintf("invalid number of args: %zu (expected 2)",
- tokens.size() - pos);
- return {};
- }
- const std::string& id = tokens[pos++];
- RangeSet src_ranges = RangeSet::Parse(tokens[pos++]);
- if (!src_ranges) {
- *err = "invalid token";
- return {};
- }
- stash_info = StashInfo(id, src_ranges);
- } else if (op == Type::FREE) {
- // free <stash_id>
- if (pos + 1 != tokens.size()) {
- *err = android::base::StringPrintf("invalid number of args: %zu (expected 1)",
- tokens.size() - pos);
- return {};
- }
- stash_info = StashInfo(tokens[pos++], {});
- } else if (op == Type::MOVE) {
- // <hash>
- if (pos + 1 > tokens.size()) {
- *err = "missing hash";
- return {};
- }
- std::string hash = tokens[pos++];
- if (!ParseTargetInfoAndSourceInfo(
- std::vector<std::string>(tokens.cbegin() + pos, tokens.cend()), hash, &target_info,
- hash, &source_info, err)) {
- return {};
- }
- } else if (op == Type::BSDIFF || op == Type::IMGDIFF) {
- // <offset> <length> <srchash> <dsthash>
- if (pos + 4 > tokens.size()) {
- *err = android::base::StringPrintf("invalid number of args: %zu (expected 4+)",
- tokens.size() - pos);
- return {};
- }
- size_t offset;
- size_t length;
- if (!android::base::ParseUint(tokens[pos++], &offset) ||
- !android::base::ParseUint(tokens[pos++], &length)) {
- *err = "invalid patch offset/length";
- return {};
- }
- patch_info = PatchInfo(offset, length);
-
- std::string src_hash = tokens[pos++];
- std::string dst_hash = tokens[pos++];
- if (!ParseTargetInfoAndSourceInfo(
- std::vector<std::string>(tokens.cbegin() + pos, tokens.cend()), dst_hash, &target_info,
- src_hash, &source_info, err)) {
- return {};
- }
- } else if (op == Type::ABORT) {
- // Abort takes no arguments, so there's nothing else to check.
- if (pos != tokens.size()) {
- *err = android::base::StringPrintf("invalid number of args: %zu (expected 0)",
- tokens.size() - pos);
- return {};
- }
- } else if (op == Type::COMPUTE_HASH_TREE) {
- // <hash_tree_ranges> <source_ranges> <hash_algorithm> <salt_hex> <root_hash>
- if (pos + 5 != tokens.size()) {
- *err = android::base::StringPrintf("invalid number of args: %zu (expected 5)",
- tokens.size() - pos);
- return {};
- }
-
- // Expects the hash_tree data to be contiguous.
- RangeSet hash_tree_ranges = RangeSet::Parse(tokens[pos++]);
- if (!hash_tree_ranges || hash_tree_ranges.size() != 1) {
- *err = "invalid hash tree ranges in: " + line;
- return {};
- }
-
- RangeSet source_ranges = RangeSet::Parse(tokens[pos++]);
- if (!source_ranges) {
- *err = "invalid source ranges in: " + line;
- return {};
- }
-
- std::string hash_algorithm = tokens[pos++];
- std::string salt_hex = tokens[pos++];
- std::string root_hash = tokens[pos++];
- if (hash_algorithm.empty() || salt_hex.empty() || root_hash.empty()) {
- *err = "invalid hash tree arguments in " + line;
- return {};
- }
-
- HashTreeInfo hash_tree_info(std::move(hash_tree_ranges), std::move(source_ranges),
- std::move(hash_algorithm), std::move(salt_hex),
- std::move(root_hash));
- return Command(op, index, line, std::move(hash_tree_info));
- } else {
- *err = "invalid op";
- return {};
- }
-
- return Command(op, index, line, patch_info, target_info, source_info, stash_info);
-}
-
-bool SourceInfo::Overlaps(const TargetInfo& target) const {
- return ranges_.Overlaps(target.ranges());
-}
-
-// Moves blocks in the 'source' vector to the specified locations (as in 'locs') in the 'dest'
-// vector. Note that source and dest may be the same buffer.
-static void MoveRange(std::vector<uint8_t>* dest, const RangeSet& locs,
- const std::vector<uint8_t>& source, size_t block_size) {
- const uint8_t* from = source.data();
- uint8_t* to = dest->data();
- size_t start = locs.blocks();
- // Must do the movement backward.
- for (auto it = locs.crbegin(); it != locs.crend(); it++) {
- size_t blocks = it->second - it->first;
- start -= blocks;
- memmove(to + (it->first * block_size), from + (start * block_size), blocks * block_size);
- }
-}
-
-bool SourceInfo::ReadAll(
- std::vector<uint8_t>* buffer, size_t block_size,
- const std::function<int(const RangeSet&, std::vector<uint8_t>*)>& block_reader,
- const std::function<int(const std::string&, std::vector<uint8_t>*)>& stash_reader) const {
- if (buffer->size() < blocks() * block_size) {
- return false;
- }
-
- // Read in the source ranges.
- if (ranges_) {
- if (block_reader(ranges_, buffer) != 0) {
- return false;
- }
- if (location_) {
- MoveRange(buffer, location_, *buffer, block_size);
- }
- }
-
- // Read in the stashes.
- for (const StashInfo& stash : stashes_) {
- std::vector<uint8_t> stash_buffer(stash.blocks() * block_size);
- if (stash_reader(stash.id(), &stash_buffer) != 0) {
- return false;
- }
- MoveRange(buffer, stash.ranges(), stash_buffer, block_size);
- }
- return true;
-}
-
-void SourceInfo::DumpBuffer(const std::vector<uint8_t>& buffer, size_t block_size) const {
- LOG(INFO) << "Dumping hashes in hex for " << ranges_.blocks() << " source blocks";
-
- const RangeSet& location = location_ ? location_ : RangeSet({ Range{ 0, ranges_.blocks() } });
- for (size_t i = 0; i < ranges_.blocks(); i++) {
- size_t block_num = ranges_.GetBlockNumber(i);
- size_t buffer_index = location.GetBlockNumber(i);
- CHECK_LE((buffer_index + 1) * block_size, buffer.size());
-
- uint8_t digest[SHA_DIGEST_LENGTH];
- SHA1(buffer.data() + buffer_index * block_size, block_size, digest);
- std::string hexdigest = print_sha1(digest);
- LOG(INFO) << " block number: " << block_num << ", SHA-1: " << hexdigest;
- }
-}
-
-std::ostream& operator<<(std::ostream& os, const Command& command) {
- os << command.index() << ": " << command.cmdline();
- return os;
-}
-
-std::ostream& operator<<(std::ostream& os, const TargetInfo& target) {
- os << target.blocks() << " blocks (" << target.hash_ << "): " << target.ranges_.ToString();
- return os;
-}
-
-std::ostream& operator<<(std::ostream& os, const StashInfo& stash) {
- os << stash.blocks() << " blocks (" << stash.id_ << "): " << stash.ranges_.ToString();
- return os;
-}
-
-std::ostream& operator<<(std::ostream& os, const SourceInfo& source) {
- os << source.blocks_ << " blocks (" << source.hash_ << "): ";
- if (source.ranges_) {
- os << source.ranges_.ToString();
- if (source.location_) {
- os << " (location: " << source.location_.ToString() << ")";
- }
- }
- if (!source.stashes_.empty()) {
- os << " " << source.stashes_.size() << " stash(es)";
- }
- return os;
-}
-
-TransferList TransferList::Parse(const std::string& transfer_list_str, std::string* err) {
- TransferList result{};
-
- std::vector<std::string> lines = android::base::Split(transfer_list_str, "\n");
- if (lines.size() < kTransferListHeaderLines) {
- *err = android::base::StringPrintf("too few lines in the transfer list [%zu]", lines.size());
- return TransferList{};
- }
-
- // First line in transfer list is the version number.
- if (!android::base::ParseInt(lines[0], &result.version_, 3, 4)) {
- *err = "unexpected transfer list version ["s + lines[0] + "]";
- return TransferList{};
- }
-
- // Second line in transfer list is the total number of blocks we expect to write.
- if (!android::base::ParseUint(lines[1], &result.total_blocks_)) {
- *err = "unexpected block count ["s + lines[1] + "]";
- return TransferList{};
- }
-
- // Third line is how many stash entries are needed simultaneously.
- if (!android::base::ParseUint(lines[2], &result.stash_max_entries_)) {
- return TransferList{};
- }
-
- // Fourth line is the maximum number of blocks that will be stashed simultaneously.
- if (!android::base::ParseUint(lines[3], &result.stash_max_blocks_)) {
- *err = "unexpected maximum stash blocks ["s + lines[3] + "]";
- return TransferList{};
- }
-
- // Subsequent lines are all individual transfer commands.
- for (size_t i = kTransferListHeaderLines; i < lines.size(); i++) {
- const std::string& line = lines[i];
- if (line.empty()) continue;
-
- size_t cmdindex = i - kTransferListHeaderLines;
- std::string parsing_error;
- Command command = Command::Parse(line, cmdindex, &parsing_error);
- if (!command) {
- *err = android::base::StringPrintf("Failed to parse command %zu [%s]: %s", cmdindex,
- line.c_str(), parsing_error.c_str());
- return TransferList{};
- }
- result.commands_.push_back(command);
- }
-
- return result;
-}
diff --git a/updater/dynamic_partitions.cpp b/updater/dynamic_partitions.cpp
deleted file mode 100644
index a340116..0000000
--- a/updater/dynamic_partitions.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "updater/dynamic_partitions.h"
-
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-
-#include "edify/expr.h"
-#include "edify/updater_runtime_interface.h"
-#include "otautil/error_code.h"
-#include "otautil/paths.h"
-#include "private/utils.h"
-
-static std::vector<std::string> ReadStringArgs(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv,
- const std::vector<std::string>& arg_names) {
- if (argv.size() != arg_names.size()) {
- ErrorAbort(state, kArgsParsingFailure, "%s expects %zu arguments, got %zu", name,
- arg_names.size(), argv.size());
- return {};
- }
-
- std::vector<std::unique_ptr<Value>> args;
- if (!ReadValueArgs(state, argv, &args)) {
- return {};
- }
-
- CHECK_EQ(args.size(), arg_names.size());
-
- for (size_t i = 0; i < arg_names.size(); ++i) {
- if (args[i]->type != Value::Type::STRING) {
- ErrorAbort(state, kArgsParsingFailure, "%s argument to %s must be string",
- arg_names[i].c_str(), name);
- return {};
- }
- }
-
- std::vector<std::string> ret;
- std::transform(args.begin(), args.end(), std::back_inserter(ret),
- [](const auto& arg) { return arg->data; });
- return ret;
-}
-
-Value* UnmapPartitionFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- auto args = ReadStringArgs(name, state, argv, { "name" });
- if (args.empty()) return StringValue("");
-
- auto updater_runtime = state->updater->GetRuntime();
- return updater_runtime->UnmapPartitionOnDeviceMapper(args[0]) ? StringValue("t")
- : StringValue("");
-}
-
-Value* MapPartitionFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- auto args = ReadStringArgs(name, state, argv, { "name" });
- if (args.empty()) return StringValue("");
-
- std::string path;
- auto updater_runtime = state->updater->GetRuntime();
- bool result = updater_runtime->MapPartitionOnDeviceMapper(args[0], &path);
- return result ? StringValue(path) : StringValue("");
-}
-
-static constexpr char kMetadataUpdatedMarker[] = "/dynamic_partition_metadata.UPDATED";
-
-Value* UpdateDynamicPartitionsFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 1) {
- ErrorAbort(state, kArgsParsingFailure, "%s expects 1 arguments, got %zu", name, argv.size());
- return StringValue("");
- }
- std::vector<std::unique_ptr<Value>> args;
- if (!ReadValueArgs(state, argv, &args)) {
- return nullptr;
- }
- const std::unique_ptr<Value>& op_list_value = args[0];
- if (op_list_value->type != Value::Type::BLOB) {
- ErrorAbort(state, kArgsParsingFailure, "op_list argument to %s must be blob", name);
- return StringValue("");
- }
-
- std::string updated_marker = Paths::Get().stash_directory_base() + kMetadataUpdatedMarker;
- if (state->is_retry) {
- struct stat sb;
- int result = stat(updated_marker.c_str(), &sb);
- if (result == 0) {
- LOG(INFO) << "Skipping already updated dynamic partition metadata based on marker";
- return StringValue("t");
- }
- } else {
- // Delete the obsolete marker if any.
- std::string err;
- if (!android::base::RemoveFileIfExists(updated_marker, &err)) {
- LOG(ERROR) << "Failed to remove dynamic partition metadata updated marker " << updated_marker
- << ": " << err;
- return StringValue("");
- }
- }
-
- auto updater_runtime = state->updater->GetRuntime();
- if (!updater_runtime->UpdateDynamicPartitions(op_list_value->data)) {
- return StringValue("");
- }
-
- if (!SetUpdatedMarker(updated_marker)) {
- LOG(ERROR) << "Failed to set metadata updated marker.";
- return StringValue("");
- }
-
- return StringValue("t");
-}
-
-void RegisterDynamicPartitionsFunctions() {
- RegisterFunction("unmap_partition", UnmapPartitionFn);
- RegisterFunction("map_partition", MapPartitionFn);
- RegisterFunction("update_dynamic_partitions", UpdateDynamicPartitionsFn);
-}
diff --git a/updater/include/private/commands.h b/updater/include/private/commands.h
deleted file mode 100644
index 7a23bb7..0000000
--- a/updater/include/private/commands.h
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-#include <functional>
-#include <ostream>
-#include <string>
-#include <vector>
-
-#include <gtest/gtest_prod.h> // FRIEND_TEST
-
-#include "otautil/rangeset.h"
-
-// Represents the target info used in a Command. TargetInfo contains the ranges of the blocks and
-// the expected hash.
-class TargetInfo {
- public:
- TargetInfo() = default;
-
- TargetInfo(std::string hash, RangeSet ranges)
- : hash_(std::move(hash)), ranges_(std::move(ranges)) {}
-
- const std::string& hash() const {
- return hash_;
- }
-
- const RangeSet& ranges() const {
- return ranges_;
- }
-
- size_t blocks() const {
- return ranges_.blocks();
- }
-
- bool operator==(const TargetInfo& other) const {
- return hash_ == other.hash_ && ranges_ == other.ranges_;
- }
-
- private:
- friend std::ostream& operator<<(std::ostream& os, const TargetInfo& source);
-
- // The hash of the data represented by the object.
- std::string hash_;
- // The block ranges that the data should be written to.
- RangeSet ranges_;
-};
-
-std::ostream& operator<<(std::ostream& os, const TargetInfo& source);
-
-// Represents the stash info used in a Command.
-class StashInfo {
- public:
- StashInfo() = default;
-
- StashInfo(std::string id, RangeSet ranges) : id_(std::move(id)), ranges_(std::move(ranges)) {}
-
- size_t blocks() const {
- return ranges_.blocks();
- }
-
- const std::string& id() const {
- return id_;
- }
-
- const RangeSet& ranges() const {
- return ranges_;
- }
-
- bool operator==(const StashInfo& other) const {
- return id_ == other.id_ && ranges_ == other.ranges_;
- }
-
- private:
- friend std::ostream& operator<<(std::ostream& os, const StashInfo& stash);
-
- // The id (i.e. hash) of the stash.
- std::string id_;
- // The matching location of the stash.
- RangeSet ranges_;
-};
-
-std::ostream& operator<<(std::ostream& os, const StashInfo& stash);
-
-// Represents the source info in a Command, whose data could come from source image, stashed blocks,
-// or both.
-class SourceInfo {
- public:
- SourceInfo() = default;
-
- SourceInfo(std::string hash, RangeSet ranges, RangeSet location, std::vector<StashInfo> stashes)
- : hash_(std::move(hash)),
- ranges_(std::move(ranges)),
- location_(std::move(location)),
- stashes_(std::move(stashes)) {
- blocks_ = ranges_.blocks();
- for (const auto& stash : stashes_) {
- blocks_ += stash.ranges().blocks();
- }
- }
-
- // Reads all the data specified by this SourceInfo object into the given 'buffer', by calling the
- // given readers. Caller needs to specify the block size for the represented blocks. The given
- // buffer needs to be sufficiently large. Otherwise it returns false. 'block_reader' and
- // 'stash_reader' read the specified data into the given buffer (guaranteed to be large enough)
- // respectively. The readers should return 0 on success, or -1 on error.
- bool ReadAll(
- std::vector<uint8_t>* buffer, size_t block_size,
- const std::function<int(const RangeSet&, std::vector<uint8_t>*)>& block_reader,
- const std::function<int(const std::string&, std::vector<uint8_t>*)>& stash_reader) const;
-
- // Whether this SourceInfo overlaps with the given TargetInfo object.
- bool Overlaps(const TargetInfo& target) const;
-
- // Dumps the hashes in hex for the given buffer that's loaded from this SourceInfo object
- // (excluding the stashed blocks which are handled separately).
- void DumpBuffer(const std::vector<uint8_t>& buffer, size_t block_size) const;
-
- const std::string& hash() const {
- return hash_;
- }
-
- size_t blocks() const {
- return blocks_;
- }
-
- bool operator==(const SourceInfo& other) const {
- return hash_ == other.hash_ && ranges_ == other.ranges_ && location_ == other.location_ &&
- stashes_ == other.stashes_;
- }
-
- private:
- friend std::ostream& operator<<(std::ostream& os, const SourceInfo& source);
-
- // The hash of the data represented by the object.
- std::string hash_;
- // The block ranges from the source image to read data from. This could be a subset of all the
- // blocks represented by the object, or empty if all the data should be loaded from stash.
- RangeSet ranges_;
- // The location in the buffer to load ranges_ into. Empty if ranges_ alone covers all the blocks
- // (i.e. nothing needs to be loaded from stash).
- RangeSet location_;
- // The info for the stashed blocks that are part of the source. Empty if there's none.
- std::vector<StashInfo> stashes_;
- // Total number of blocks represented by the object.
- size_t blocks_{ 0 };
-};
-
-std::ostream& operator<<(std::ostream& os, const SourceInfo& source);
-
-class PatchInfo {
- public:
- PatchInfo() = default;
-
- PatchInfo(size_t offset, size_t length) : offset_(offset), length_(length) {}
-
- size_t offset() const {
- return offset_;
- }
-
- size_t length() const {
- return length_;
- }
-
- bool operator==(const PatchInfo& other) const {
- return offset_ == other.offset_ && length_ == other.length_;
- }
-
- private:
- size_t offset_{ 0 };
- size_t length_{ 0 };
-};
-
-// The arguments to build a hash tree from blocks on the block device.
-class HashTreeInfo {
- public:
- HashTreeInfo() = default;
-
- HashTreeInfo(RangeSet hash_tree_ranges, RangeSet source_ranges, std::string hash_algorithm,
- std::string salt_hex, std::string root_hash)
- : hash_tree_ranges_(std::move(hash_tree_ranges)),
- source_ranges_(std::move(source_ranges)),
- hash_algorithm_(std::move(hash_algorithm)),
- salt_hex_(std::move(salt_hex)),
- root_hash_(std::move(root_hash)) {}
-
- const RangeSet& hash_tree_ranges() const {
- return hash_tree_ranges_;
- }
- const RangeSet& source_ranges() const {
- return source_ranges_;
- }
-
- const std::string& hash_algorithm() const {
- return hash_algorithm_;
- }
- const std::string& salt_hex() const {
- return salt_hex_;
- }
- const std::string& root_hash() const {
- return root_hash_;
- }
-
- bool operator==(const HashTreeInfo& other) const {
- return hash_tree_ranges_ == other.hash_tree_ranges_ && source_ranges_ == other.source_ranges_ &&
- hash_algorithm_ == other.hash_algorithm_ && salt_hex_ == other.salt_hex_ &&
- root_hash_ == other.root_hash_;
- }
-
- private:
- RangeSet hash_tree_ranges_;
- RangeSet source_ranges_;
- std::string hash_algorithm_;
- std::string salt_hex_;
- std::string root_hash_;
-};
-
-// Command class holds the info for an update command that performs block-based OTA (BBOTA). Each
-// command consists of one or several args, namely TargetInfo, SourceInfo, StashInfo and PatchInfo.
-// The currently used BBOTA version is v4.
-//
-// zero <tgt_ranges>
-// - Fill the indicated blocks with zeros.
-// - Meaningful args: TargetInfo
-//
-// new <tgt_ranges>
-// - Fill the blocks with data read from the new_data file.
-// - Meaningful args: TargetInfo
-//
-// erase <tgt_ranges>
-// - Mark the given blocks as empty.
-// - Meaningful args: TargetInfo
-//
-// move <hash> <...>
-// - Read the source blocks, write result to target blocks.
-// - Meaningful args: TargetInfo, SourceInfo
-//
-// See the note below for <...>.
-//
-// bsdiff <patchstart> <patchlen> <srchash> <dsthash> <...>
-// imgdiff <patchstart> <patchlen> <srchash> <dsthash> <...>
-// - Read the source blocks, apply a patch, and write result to target blocks.
-// - Meaningful args: PatchInfo, TargetInfo, SourceInfo
-//
-// It expects <...> in one of the following formats:
-//
-// <tgt_ranges> <src_block_count> - <[stash_id:stash_location] ...>
-// (loads data from stashes only)
-//
-// <tgt_ranges> <src_block_count> <src_ranges>
-// (loads data from source image only)
-//
-// <tgt_ranges> <src_block_count> <src_ranges> <src_ranges_location>
-// <[stash_id:stash_location] ...>
-// (loads data from both of source image and stashes)
-//
-// stash <stash_id> <src_ranges>
-// - Load the given source blocks and stash the data in the given slot of the stash table.
-// - Meaningful args: StashInfo
-//
-// free <stash_id>
-// - Free the given stash data.
-// - Meaningful args: StashInfo
-//
-// compute_hash_tree <hash_tree_ranges> <source_ranges> <hash_algorithm> <salt_hex> <root_hash>
-// - Computes the hash_tree bytes and writes the result to the specified range on the
-// block_device.
-//
-// abort
-// - Abort the current update. Allowed for testing code only.
-//
-class Command {
- public:
- enum class Type {
- ABORT,
- BSDIFF,
- COMPUTE_HASH_TREE,
- ERASE,
- FREE,
- IMGDIFF,
- MOVE,
- NEW,
- STASH,
- ZERO,
- LAST, // Not a valid type.
- };
-
- Command() = default;
-
- Command(Type type, size_t index, std::string cmdline, PatchInfo patch, TargetInfo target,
- SourceInfo source, StashInfo stash)
- : type_(type),
- index_(index),
- cmdline_(std::move(cmdline)),
- patch_(patch),
- target_(std::move(target)),
- source_(std::move(source)),
- stash_(std::move(stash)) {}
-
- Command(Type type, size_t index, std::string cmdline, HashTreeInfo hash_tree_info);
-
- // Parses the given command 'line' into a Command object and returns it. The 'index' is specified
- // by the caller to index the object. On parsing error, it returns an empty Command object that
- // evaluates to false, and the specific error message will be set in 'err'.
- static Command Parse(const std::string& line, size_t index, std::string* err);
-
- // Parses the command type from the given string.
- static Type ParseType(const std::string& type_str);
-
- Type type() const {
- return type_;
- }
-
- size_t index() const {
- return index_;
- }
-
- const std::string& cmdline() const {
- return cmdline_;
- }
-
- const PatchInfo& patch() const {
- return patch_;
- }
-
- const TargetInfo& target() const {
- return target_;
- }
-
- const SourceInfo& source() const {
- return source_;
- }
-
- const StashInfo& stash() const {
- return stash_;
- }
-
- const HashTreeInfo& hash_tree_info() const {
- return hash_tree_info_;
- }
-
- size_t block_size() const {
- return block_size_;
- }
-
- constexpr explicit operator bool() const {
- return type_ != Type::LAST;
- }
-
- private:
- friend class ResumableUpdaterTest;
- friend class UpdaterTest;
-
- FRIEND_TEST(CommandsTest, Parse_ABORT_Allowed);
- FRIEND_TEST(CommandsTest, Parse_InvalidNumberOfArgs);
- FRIEND_TEST(CommandsTest, ParseTargetInfoAndSourceInfo_InvalidInput);
- FRIEND_TEST(CommandsTest, ParseTargetInfoAndSourceInfo_StashesOnly);
- FRIEND_TEST(CommandsTest, ParseTargetInfoAndSourceInfo_SourceBlocksAndStashes);
- FRIEND_TEST(CommandsTest, ParseTargetInfoAndSourceInfo_SourceBlocksOnly);
-
- // Parses the target and source info from the given 'tokens' vector. Saves the parsed info into
- // 'target' and 'source' objects. Returns the parsing result. Error message will be set in 'err'
- // on parsing error, and the contents in 'target' and 'source' will be undefined.
- static bool ParseTargetInfoAndSourceInfo(const std::vector<std::string>& tokens,
- const std::string& tgt_hash, TargetInfo* target,
- const std::string& src_hash, SourceInfo* source,
- std::string* err);
-
- // Allows parsing ABORT command, which should be used for testing purpose only.
- static bool abort_allowed_;
-
- // The type of the command.
- Type type_{ Type::LAST };
- // The index of the Command object, which is specified by the caller.
- size_t index_{ 0 };
- // The input string that the Command object is parsed from.
- std::string cmdline_;
- // The patch info. Only meaningful for BSDIFF and IMGDIFF commands.
- PatchInfo patch_;
- // The target info, where the command should be written to.
- TargetInfo target_;
- // The source info to load the source blocks for the command.
- SourceInfo source_;
- // The stash info. Only meaningful for STASH and FREE commands. Note that although SourceInfo may
- // also load data from stash, such info will be owned and managed by SourceInfo (i.e. in source_).
- StashInfo stash_;
- // The hash_tree info. Only meaningful for COMPUTE_HASH_TREE.
- HashTreeInfo hash_tree_info_;
- // The unit size of each block to be used in this command.
- size_t block_size_{ 4096 };
-};
-
-std::ostream& operator<<(std::ostream& os, const Command& command);
-
-// TransferList represents the info for a transfer list, which is parsed from input text lines
-// containing commands to transfer data from one place to another on the target partition.
-//
-// The creator of the transfer list will guarantee that no block is read (i.e., used as the source
-// for a patch or move) after it has been written.
-//
-// The creator will guarantee that a given stash is loaded (with a stash command) before it's used
-// in a move/bsdiff/imgdiff command.
-//
-// Within one command the source and target ranges may overlap so in general we need to read the
-// entire source into memory before writing anything to the target blocks.
-//
-// All the patch data is concatenated into one patch_data file in the update package. It must be
-// stored uncompressed because we memory-map it in directly from the archive. (Since patches are
-// already compressed, we lose very little by not compressing their concatenation.)
-//
-// Commands that read data from the partition (i.e. move/bsdiff/imgdiff/stash) have one or more
-// additional hashes before the range parameters, which are used to check if the command has
-// already been completed and verify the integrity of the source data.
-class TransferList {
- public:
- // Number of header lines.
- static constexpr size_t kTransferListHeaderLines = 4;
-
- TransferList() = default;
-
- // Parses the given input string and returns a TransferList object. Sets error message if any.
- static TransferList Parse(const std::string& transfer_list_str, std::string* err);
-
- int version() const {
- return version_;
- }
-
- size_t total_blocks() const {
- return total_blocks_;
- }
-
- size_t stash_max_entries() const {
- return stash_max_entries_;
- }
-
- size_t stash_max_blocks() const {
- return stash_max_blocks_;
- }
-
- const std::vector<Command>& commands() const {
- return commands_;
- }
-
- // Returns whether the TransferList is valid.
- constexpr explicit operator bool() const {
- return version_ != 0;
- }
-
- private:
- // BBOTA version.
- int version_{ 0 };
- // Total number of blocks to be written in this transfer.
- size_t total_blocks_;
- // Maximum number of stashes that exist at the same time.
- size_t stash_max_entries_;
- // Maximum number of blocks to be stashed.
- size_t stash_max_blocks_;
- // Commands in this transfer.
- std::vector<Command> commands_;
-};
diff --git a/updater/include/private/utils.h b/updater/include/private/utils.h
deleted file mode 100644
index 33cf615..0000000
--- a/updater/include/private/utils.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#pragma once
-
-#include <string>
-
-bool SetUpdatedMarker(const std::string& marker);
diff --git a/updater/include/updater/blockimg.h b/updater/include/updater/blockimg.h
deleted file mode 100644
index 71733b3..0000000
--- a/updater/include/updater/blockimg.h
+++ /dev/null
@@ -1,24 +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.
- */
-
-#ifndef _UPDATER_BLOCKIMG_H_
-#define _UPDATER_BLOCKIMG_H_
-
-#include <string>
-
-void RegisterBlockImageFunctions();
-
-#endif
diff --git a/updater/include/updater/build_info.h b/updater/include/updater/build_info.h
deleted file mode 100644
index 0073bfa..0000000
--- a/updater/include/updater/build_info.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#pragma once
-
-#include <list>
-#include <map>
-#include <string>
-#include <string_view>
-
-#include <android-base/file.h>
-
-// This class serves as the aggregation of the fake block device information during update
-// simulation on host. In specific, it has the name of the block device, its mount point, and the
-// path to the temporary file that fakes this block device.
-class FakeBlockDevice {
- public:
- FakeBlockDevice(std::string block_device, std::string mount_point, std::string temp_file_path)
- : blockdev_name(std::move(block_device)),
- mount_point(std::move(mount_point)),
- mounted_file_path(std::move(temp_file_path)) {}
-
- std::string blockdev_name;
- std::string mount_point;
- std::string mounted_file_path; // path to the temp file that mocks the block device
-};
-
-// This class stores the information of the source build. For example, it creates and maintains
-// the temporary files to simulate the block devices on host. Therefore, the simulator runtime can
-// query the information and run the update on host.
-class BuildInfo {
- public:
- BuildInfo(const std::string_view work_dir, bool keep_images)
- : work_dir_(work_dir), keep_images_(keep_images) {}
- // Returns the value of the build properties.
- std::string GetProperty(const std::string_view key, const std::string_view default_value) const;
- // Returns the path to the mock block device.
- std::string FindBlockDeviceName(const std::string_view name) const;
- // Parses the given target-file, initializes the build properties and extracts the images.
- bool ParseTargetFile(const std::string_view target_file_path, bool extracted_input);
-
- std::string GetOemSettings() const {
- return oem_settings_;
- }
- void SetOemSettings(const std::string_view oem_settings) {
- oem_settings_ = oem_settings;
- }
-
- private:
- // A map to store the system properties during simulation.
- std::map<std::string, std::string, std::less<>> build_props_;
- // A file that contains the oem properties.
- std::string oem_settings_;
- // A map from the blockdev_name to the FakeBlockDevice object, which contains the path to the
- // temporary file.
- std::map<std::string, FakeBlockDevice, std::less<>> blockdev_map_;
-
- std::list<TemporaryFile> temp_files_;
- std::string work_dir_; // A temporary directory to store the extracted image files
- bool keep_images_;
-};
diff --git a/updater/include/updater/dynamic_partitions.h b/updater/include/updater/dynamic_partitions.h
deleted file mode 100644
index 31cf859..0000000
--- a/updater/include/updater/dynamic_partitions.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#pragma once
-
-void RegisterDynamicPartitionsFunctions();
diff --git a/updater/include/updater/install.h b/updater/include/updater/install.h
deleted file mode 100644
index 9fe2031..0000000
--- a/updater/include/updater/install.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-void RegisterInstallFunctions();
diff --git a/updater/include/updater/simulator_runtime.h b/updater/include/updater/simulator_runtime.h
deleted file mode 100644
index fa878db..0000000
--- a/updater/include/updater/simulator_runtime.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#pragma once
-
-#include <map>
-#include <memory>
-#include <string>
-#include <string_view>
-#include <utility>
-#include <vector>
-
-#include "edify/updater_runtime_interface.h"
-#include "updater/build_info.h"
-
-class SimulatorRuntime : public UpdaterRuntimeInterface {
- public:
- explicit SimulatorRuntime(BuildInfo* source) : source_(source) {}
-
- bool IsSimulator() const override {
- return true;
- }
-
- std::string GetProperty(const std::string_view key,
- const std::string_view default_value) const override;
-
- int Mount(const std::string_view location, const std::string_view mount_point,
- const std::string_view fs_type, const std::string_view mount_options) override;
- bool IsMounted(const std::string_view mount_point) const override;
- std::pair<bool, int> Unmount(const std::string_view mount_point) override;
-
- bool ReadFileToString(const std::string_view filename, std::string* content) const override;
- bool WriteStringToFile(const std::string_view content,
- const std::string_view filename) const override;
-
- int WipeBlockDevice(const std::string_view filename, size_t len) const override;
- int RunProgram(const std::vector<std::string>& args, bool is_vfork) const override;
- int Tune2Fs(const std::vector<std::string>& args) const override;
-
- bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) override;
- bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) override;
- bool UpdateDynamicPartitions(const std::string_view op_list_value) override;
- std::string AddSlotSuffix(const std::string_view arg) const override;
-
- private:
- std::string FindBlockDeviceName(const std::string_view name) const override;
-
- BuildInfo* source_;
- std::map<std::string, std::string, std::less<>> mounted_partitions_;
-};
diff --git a/updater/include/updater/target_files.h b/updater/include/updater/target_files.h
deleted file mode 100644
index 860d47a..0000000
--- a/updater/include/updater/target_files.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#pragma once
-
-#include <map>
-#include <string>
-#include <string_view>
-#include <vector>
-
-#include <android-base/file.h>
-#include <ziparchive/zip_archive.h>
-
-// This class represents the mount information for each line in a fstab file.
-class FstabInfo {
- public:
- FstabInfo(std::string blockdev_name, std::string mount_point, std::string fs_type)
- : blockdev_name(std::move(blockdev_name)),
- mount_point(std::move(mount_point)),
- fs_type(std::move(fs_type)) {}
-
- std::string blockdev_name;
- std::string mount_point;
- std::string fs_type;
-};
-
-// This class parses a target file from a zip file or an extracted directory. It also provides the
-// function to read the its content for simulation.
-class TargetFile {
- public:
- TargetFile(std::string path, bool extracted_input)
- : path_(std::move(path)), extracted_input_(extracted_input) {}
-
- // Opens the input target file (or extracted directory) and parses the misc_info.txt.
- bool Open();
- // Parses the build properties in all possible locations and save them in |props_map|
- bool GetBuildProps(std::map<std::string, std::string, std::less<>>* props_map) const;
- // Parses the fstab and save the information about each partition to mount into |fstab_info_list|.
- bool ParseFstabInfo(std::vector<FstabInfo>* fstab_info_list) const;
- // Returns true if the given entry exists in the target file.
- bool EntryExists(const std::string_view name) const;
- // Extracts the image file |entry_name|. Returns true on success.
- bool ExtractImage(const std::string_view entry_name, const FstabInfo& fstab_info,
- const std::string_view work_dir, TemporaryFile* image_file) const;
-
- private:
- // Wrapper functions to read the entry from either the zipped target-file, or the extracted input
- // directory.
- bool ReadEntryToString(const std::string_view name, std::string* content) const;
- bool ExtractEntryToTempFile(const std::string_view name, TemporaryFile* temp_file) const;
-
- std::string path_; // Path to the zipped target-file or an extracted directory.
- bool extracted_input_; // True if the target-file has been extracted.
- ZipArchiveHandle handle_{ nullptr };
-
- // The properties under META/misc_info.txt
- std::map<std::string, std::string, std::less<>> misc_info_;
-};
diff --git a/updater/include/updater/updater.h b/updater/include/updater/updater.h
deleted file mode 100644
index 8676b60..0000000
--- a/updater/include/updater/updater.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <stdio.h>
-
-#include <memory>
-#include <string>
-#include <string_view>
-
-#include <ziparchive/zip_archive.h>
-
-#include "edify/expr.h"
-#include "edify/updater_interface.h"
-#include "otautil/error_code.h"
-#include "otautil/sysutil.h"
-
-class Updater : public UpdaterInterface {
- public:
- explicit Updater(std::unique_ptr<UpdaterRuntimeInterface> run_time)
- : runtime_(std::move(run_time)) {}
-
- ~Updater() override;
-
- // Memory-maps the OTA package and opens it as a zip file. Also sets up the command pipe and
- // UpdaterRuntime.
- bool Init(int fd, const std::string_view package_filename, bool is_retry);
-
- // Parses and evaluates the updater-script in the OTA package. Reports the error code if the
- // evaluation fails.
- bool RunUpdate();
-
- // Writes the message to command pipe, adds a new line in the end.
- void WriteToCommandPipe(const std::string_view message, bool flush = false) const override;
-
- // Sends over the message to recovery to print it on the screen.
- void UiPrint(const std::string_view message) const override;
-
- std::string FindBlockDeviceName(const std::string_view name) const override;
-
- UpdaterRuntimeInterface* GetRuntime() const override {
- return runtime_.get();
- }
- ZipArchiveHandle GetPackageHandle() const override {
- return package_handle_;
- }
- std::string GetResult() const override {
- return result_;
- }
- uint8_t* GetMappedPackageAddress() const override {
- return mapped_package_.addr;
- }
- size_t GetMappedPackageLength() const override {
- return mapped_package_.length;
- }
-
- private:
- friend class UpdaterTestBase;
- friend class UpdaterTest;
- // Where in the package we expect to find the edify script to execute.
- // (Note it's "updateR-script", not the older "update-script".)
- static constexpr const char* SCRIPT_NAME = "META-INF/com/google/android/updater-script";
-
- // Reads the entry |name| in the zip archive and put the result in |content|.
- bool ReadEntryToString(ZipArchiveHandle za, const std::string& entry_name, std::string* content);
-
- // Parses the error code embedded in state->errmsg; and reports the error code and cause code.
- void ParseAndReportErrorCode(State* state);
-
- std::unique_ptr<UpdaterRuntimeInterface> runtime_;
-
- MemMapping mapped_package_;
- ZipArchiveHandle package_handle_{ nullptr };
- std::string updater_script_;
-
- bool is_retry_{ false };
- std::unique_ptr<FILE, decltype(&fclose)> cmd_pipe_{ nullptr, fclose };
-
- std::string result_;
- std::vector<std::string> skipped_functions_;
-};
diff --git a/updater/include/updater/updater_runtime.h b/updater/include/updater/updater_runtime.h
deleted file mode 100644
index b943dfc..0000000
--- a/updater/include/updater/updater_runtime.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#pragma once
-
-#include <memory>
-#include <string>
-#include <string_view>
-#include <utility>
-#include <vector>
-
-#include "edify/updater_runtime_interface.h"
-
-struct selabel_handle;
-
-class UpdaterRuntime : public UpdaterRuntimeInterface {
- public:
- explicit UpdaterRuntime(struct selabel_handle* sehandle) : sehandle_(sehandle) {}
- ~UpdaterRuntime() override = default;
-
- bool IsSimulator() const override {
- return false;
- }
-
- std::string GetProperty(const std::string_view key,
- const std::string_view default_value) const override;
-
- std::string FindBlockDeviceName(const std::string_view name) const override;
-
- int Mount(const std::string_view location, const std::string_view mount_point,
- const std::string_view fs_type, const std::string_view mount_options) override;
- bool IsMounted(const std::string_view mount_point) const override;
- std::pair<bool, int> Unmount(const std::string_view mount_point) override;
-
- bool ReadFileToString(const std::string_view filename, std::string* content) const override;
- bool WriteStringToFile(const std::string_view content,
- const std::string_view filename) const override;
-
- int WipeBlockDevice(const std::string_view filename, size_t len) const override;
- int RunProgram(const std::vector<std::string>& args, bool is_vfork) const override;
- int Tune2Fs(const std::vector<std::string>& args) const override;
-
- bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) override;
- bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) override;
- bool UpdateDynamicPartitions(const std::string_view op_list_value) override;
- std::string AddSlotSuffix(const std::string_view arg) const override;
-
- private:
- struct selabel_handle* sehandle_{ nullptr };
-};
diff --git a/updater/install.cpp b/updater/install.cpp
deleted file mode 100644
index 2959650..0000000
--- a/updater/install.cpp
+++ /dev/null
@@ -1,910 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "updater/install.h"
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <ftw.h>
-#include <inttypes.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/capability.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/xattr.h>
-#include <time.h>
-#include <unistd.h>
-#include <utime.h>
-
-#include <limits>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/parsedouble.h>
-#include <android-base/parseint.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <applypatch/applypatch.h>
-#include <bootloader_message/bootloader_message.h>
-#include <ext4_utils/wipe.h>
-#include <openssl/sha.h>
-#include <selinux/label.h>
-#include <selinux/selinux.h>
-#include <ziparchive/zip_archive.h>
-
-#include "edify/expr.h"
-#include "edify/updater_interface.h"
-#include "edify/updater_runtime_interface.h"
-#include "otautil/dirutil.h"
-#include "otautil/error_code.h"
-#include "otautil/print_sha1.h"
-#include "otautil/sysutil.h"
-
-#ifndef __ANDROID__
-#include <cutils/memory.h> // for strlcpy
-#endif
-
-static bool UpdateBlockDeviceNameForPartition(UpdaterInterface* updater, Partition* partition) {
- CHECK(updater);
- std::string name = updater->FindBlockDeviceName(partition->name);
- if (name.empty()) {
- LOG(ERROR) << "Failed to find the block device " << partition->name;
- return false;
- }
-
- partition->name = std::move(name);
- return true;
-}
-
-// This is the updater side handler for ui_print() in edify script. Contents will be sent over to
-// the recovery side for on-screen display.
-Value* UIPrintFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name);
- }
-
- std::string buffer = android::base::Join(args, "");
- state->updater->UiPrint(buffer);
- return StringValue(buffer);
-}
-
-// package_extract_file(package_file[, dest_file])
-// Extracts a single package_file from the update package and writes it to dest_file,
-// overwriting existing files if necessary. Without the dest_file argument, returns the
-// contents of the package file as a binary blob.
-Value* PackageExtractFileFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() < 1 || argv.size() > 2) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 or 2 args, got %zu", name,
- argv.size());
- }
-
- if (argv.size() == 2) {
- // The two-argument version extracts to a file.
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %zu args", name,
- argv.size());
- }
- const std::string& zip_path = args[0];
- std::string dest_path = args[1];
-
- ZipArchiveHandle za = state->updater->GetPackageHandle();
- ZipEntry64 entry;
- if (FindEntry(za, zip_path, &entry) != 0) {
- LOG(ERROR) << name << ": no " << zip_path << " in package";
- return StringValue("");
- }
-
- // Update the destination of package_extract_file if it's a block device. During simulation the
- // destination will map to a fake file.
- if (std::string block_device_name = state->updater->FindBlockDeviceName(dest_path);
- !block_device_name.empty()) {
- dest_path = block_device_name;
- }
-
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(
- open(dest_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)));
- if (fd == -1) {
- PLOG(ERROR) << name << ": can't open " << dest_path << " for write";
- return StringValue("");
- }
-
- bool success = true;
- int32_t ret = ExtractEntryToFile(za, &entry, fd);
- if (ret != 0) {
- LOG(ERROR) << name << ": Failed to extract entry \"" << zip_path << "\" ("
- << entry.uncompressed_length << " bytes) to \"" << dest_path
- << "\": " << ErrorCodeString(ret);
- success = false;
- }
- if (fsync(fd) == -1) {
- PLOG(ERROR) << "fsync of \"" << dest_path << "\" failed";
- success = false;
- }
-
- if (close(fd.release()) != 0) {
- PLOG(ERROR) << "close of \"" << dest_path << "\" failed";
- success = false;
- }
-
- return StringValue(success ? "t" : "");
- } else {
- // The one-argument version returns the contents of the file as the result.
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %zu args", name,
- argv.size());
- }
- const std::string& zip_path = args[0];
-
- ZipArchiveHandle za = state->updater->GetPackageHandle();
- ZipEntry64 entry;
- if (FindEntry(za, zip_path, &entry) != 0) {
- return ErrorAbort(state, kPackageExtractFileFailure, "%s(): no %s in package", name,
- zip_path.c_str());
- }
-
- std::string buffer;
- if (entry.uncompressed_length > std::numeric_limits<size_t>::max()) {
- return ErrorAbort(state, kPackageExtractFileFailure,
- "%s(): Entry `%s` Uncompressed size exceeds size of address space.", name,
- zip_path.c_str());
- }
- buffer.resize(entry.uncompressed_length);
-
- int32_t ret =
- ExtractToMemory(za, &entry, reinterpret_cast<uint8_t*>(&buffer[0]), buffer.size());
- if (ret != 0) {
- return ErrorAbort(state, kPackageExtractFileFailure,
- "%s: Failed to extract entry \"%s\" (%zu bytes) to memory: %s", name,
- zip_path.c_str(), buffer.size(), ErrorCodeString(ret));
- }
-
- return new Value(Value::Type::BLOB, buffer);
- }
-}
-
-// patch_partition_check(target_partition, source_partition)
-// Checks if the target and source partitions have the desired checksums to be patched. It returns
-// directly, if the target partition already has the expected checksum. Otherwise it in turn
-// checks the integrity of the source partition and the backup file on /cache.
-//
-// For example, patch_partition_check(
-// "EMMC:/dev/block/boot:12342568:8aaacf187a6929d0e9c3e9e46ea7ff495b43424d",
-// "EMMC:/dev/block/boot:12363048:06b0b16299dcefc94900efed01e0763ff644ffa4")
-Value* PatchPartitionCheckFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 2) {
- return ErrorAbort(state, kArgsParsingFailure,
- "%s(): Invalid number of args (expected 2, got %zu)", name, argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args, 0, 2)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name);
- }
-
- std::string err;
- auto target = Partition::Parse(args[0], &err);
- if (!target) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse target \"%s\": %s", name,
- args[0].c_str(), err.c_str());
- }
-
- auto source = Partition::Parse(args[1], &err);
- if (!source) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse source \"%s\": %s", name,
- args[1].c_str(), err.c_str());
- }
-
- if (!UpdateBlockDeviceNameForPartition(state->updater, &source) ||
- !UpdateBlockDeviceNameForPartition(state->updater, &target)) {
- return StringValue("");
- }
-
- bool result = PatchPartitionCheck(target, source);
- return StringValue(result ? "t" : "");
-}
-
-// patch_partition(target, source, patch)
-// Applies the given patch to the source partition, and writes the result to the target partition.
-//
-// For example, patch_partition(
-// "EMMC:/dev/block/boot:12342568:8aaacf187a6929d0e9c3e9e46ea7ff495b43424d",
-// "EMMC:/dev/block/boot:12363048:06b0b16299dcefc94900efed01e0763ff644ffa4",
-// package_extract_file("boot.img.p"))
-Value* PatchPartitionFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 3) {
- return ErrorAbort(state, kArgsParsingFailure,
- "%s(): Invalid number of args (expected 3, got %zu)", name, argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args, 0, 2)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name);
- }
-
- std::string err;
- auto target = Partition::Parse(args[0], &err);
- if (!target) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse target \"%s\": %s", name,
- args[0].c_str(), err.c_str());
- }
-
- auto source = Partition::Parse(args[1], &err);
- if (!source) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse source \"%s\": %s", name,
- args[1].c_str(), err.c_str());
- }
-
- std::vector<std::unique_ptr<Value>> values;
- if (!ReadValueArgs(state, argv, &values, 2, 1) || values[0]->type != Value::Type::BLOB) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): Invalid patch arg", name);
- }
-
- if (!UpdateBlockDeviceNameForPartition(state->updater, &source) ||
- !UpdateBlockDeviceNameForPartition(state->updater, &target)) {
- return StringValue("");
- }
-
- bool result = PatchPartition(target, source, *values[0], nullptr, true);
- return StringValue(result ? "t" : "");
-}
-
-// mount(fs_type, partition_type, location, mount_point)
-// mount(fs_type, partition_type, location, mount_point, mount_options)
-
-// fs_type="ext4" partition_type="EMMC" location=device
-Value* MountFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 4 && argv.size() != 5) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 4-5 args, got %zu", name,
- argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
- }
- const std::string& fs_type = args[0];
- const std::string& partition_type = args[1];
- const std::string& location = args[2];
- const std::string& mount_point = args[3];
- std::string mount_options;
-
- if (argv.size() == 5) {
- mount_options = args[4];
- }
-
- if (fs_type.empty()) {
- return ErrorAbort(state, kArgsParsingFailure, "fs_type argument to %s() can't be empty", name);
- }
- if (partition_type.empty()) {
- return ErrorAbort(state, kArgsParsingFailure, "partition_type argument to %s() can't be empty",
- name);
- }
- if (location.empty()) {
- return ErrorAbort(state, kArgsParsingFailure, "location argument to %s() can't be empty", name);
- }
- if (mount_point.empty()) {
- return ErrorAbort(state, kArgsParsingFailure, "mount_point argument to %s() can't be empty",
- name);
- }
-
- auto updater = state->updater;
- if (updater->GetRuntime()->Mount(location, mount_point, fs_type, mount_options) != 0) {
- updater->UiPrint(android::base::StringPrintf("%s: Failed to mount %s at %s: %s", name,
- location.c_str(), mount_point.c_str(),
- strerror(errno)));
- return StringValue("");
- }
-
- return StringValue(mount_point);
-}
-
-// is_mounted(mount_point)
-Value* IsMountedFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 1) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
- }
- const std::string& mount_point = args[0];
- if (mount_point.empty()) {
- return ErrorAbort(state, kArgsParsingFailure,
- "mount_point argument to unmount() can't be empty");
- }
-
- auto updater_runtime = state->updater->GetRuntime();
- if (!updater_runtime->IsMounted(mount_point)) {
- return StringValue("");
- }
-
- return StringValue(mount_point);
-}
-
-Value* UnmountFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 1) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
- }
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
- }
- const std::string& mount_point = args[0];
- if (mount_point.empty()) {
- return ErrorAbort(state, kArgsParsingFailure,
- "mount_point argument to unmount() can't be empty");
- }
-
- auto updater = state->updater;
- auto [mounted, result] = updater->GetRuntime()->Unmount(mount_point);
- if (!mounted) {
- updater->UiPrint(
- android::base::StringPrintf("Failed to unmount %s: No such volume", mount_point.c_str()));
- return nullptr;
- } else if (result != 0) {
- updater->UiPrint(android::base::StringPrintf("Failed to unmount %s: %s", mount_point.c_str(),
- strerror(errno)));
- }
-
- return StringValue(mount_point);
-}
-
-// format(fs_type, partition_type, location, fs_size, mount_point)
-//
-// fs_type="ext4" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location>
-// fs_type="f2fs" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location>
-// if fs_size == 0, then make fs uses the entire partition.
-// if fs_size > 0, that is the size to use
-// if fs_size < 0, then reserve that many bytes at the end of the partition (not for "f2fs")
-Value* FormatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 5) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 5 args, got %zu", name,
- argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
- }
- const std::string& fs_type = args[0];
- const std::string& partition_type = args[1];
- const std::string& location = args[2];
- const std::string& fs_size = args[3];
- const std::string& mount_point = args[4];
-
- if (fs_type.empty()) {
- return ErrorAbort(state, kArgsParsingFailure, "fs_type argument to %s() can't be empty", name);
- }
- if (partition_type.empty()) {
- return ErrorAbort(state, kArgsParsingFailure, "partition_type argument to %s() can't be empty",
- name);
- }
- if (location.empty()) {
- return ErrorAbort(state, kArgsParsingFailure, "location argument to %s() can't be empty", name);
- }
- if (mount_point.empty()) {
- return ErrorAbort(state, kArgsParsingFailure, "mount_point argument to %s() can't be empty",
- name);
- }
-
- int64_t size;
- if (!android::base::ParseInt(fs_size, &size)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s: failed to parse int in %s", name,
- fs_size.c_str());
- }
-
- auto updater_runtime = state->updater->GetRuntime();
- if (fs_type == "ext4") {
- std::vector<std::string> mke2fs_args = {
- "/system/bin/mke2fs", "-t", "ext4", "-b", "4096", location
- };
- if (size != 0) {
- mke2fs_args.push_back(std::to_string(size / 4096LL));
- }
-
- if (auto status = updater_runtime->RunProgram(mke2fs_args, true); status != 0) {
- LOG(ERROR) << name << ": mke2fs failed (" << status << ") on " << location;
- return StringValue("");
- }
-
- if (auto status = updater_runtime->RunProgram(
- { "/system/bin/e2fsdroid", "-e", "-a", mount_point, location }, true);
- status != 0) {
- LOG(ERROR) << name << ": e2fsdroid failed (" << status << ") on " << location;
- return StringValue("");
- }
- return StringValue(location);
- }
-
- if (fs_type == "f2fs") {
- if (size < 0) {
- LOG(ERROR) << name << ": fs_size can't be negative for f2fs: " << fs_size;
- return StringValue("");
- }
- std::vector<std::string> f2fs_args = {
- "/system/bin/make_f2fs", "-g", "android", "-w", "512", location
- };
- if (size >= 512) {
- f2fs_args.push_back(std::to_string(size / 512));
- }
- if (auto status = updater_runtime->RunProgram(f2fs_args, true); status != 0) {
- LOG(ERROR) << name << ": make_f2fs failed (" << status << ") on " << location;
- return StringValue("");
- }
-
- if (auto status = updater_runtime->RunProgram(
- { "/system/bin/sload_f2fs", "-t", mount_point, location }, true);
- status != 0) {
- LOG(ERROR) << name << ": sload_f2fs failed (" << status << ") on " << location;
- return StringValue("");
- }
-
- return StringValue(location);
- }
-
- LOG(ERROR) << name << ": unsupported fs_type \"" << fs_type << "\" partition_type \""
- << partition_type << "\"";
- return nullptr;
-}
-
-Value* ShowProgressFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 2) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
- argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
- }
- const std::string& frac_str = args[0];
- const std::string& sec_str = args[1];
-
- double frac;
- if (!android::base::ParseDouble(frac_str.c_str(), &frac)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s: failed to parse double in %s", name,
- frac_str.c_str());
- }
- int sec;
- if (!android::base::ParseInt(sec_str.c_str(), &sec)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s: failed to parse int in %s", name,
- sec_str.c_str());
- }
-
- state->updater->WriteToCommandPipe(android::base::StringPrintf("progress %f %d", frac, sec));
-
- return StringValue(frac_str);
-}
-
-Value* SetProgressFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 1) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
- }
- const std::string& frac_str = args[0];
-
- double frac;
- if (!android::base::ParseDouble(frac_str.c_str(), &frac)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s: failed to parse double in %s", name,
- frac_str.c_str());
- }
-
- state->updater->WriteToCommandPipe(android::base::StringPrintf("set_progress %f", frac));
-
- return StringValue(frac_str);
-}
-
-Value* GetPropFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 1) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
- }
- std::string key;
- if (!Evaluate(state, argv[0], &key)) {
- return nullptr;
- }
-
- auto updater_runtime = state->updater->GetRuntime();
- std::string value = updater_runtime->GetProperty(key, "");
-
- return StringValue(value);
-}
-
-// file_getprop(file, key)
-//
-// interprets 'file' as a getprop-style file (key=value pairs, one
-// per line. # comment lines, blank lines, lines without '=' ignored),
-// and returns the value for 'key' (or "" if it isn't defined).
-Value* FileGetPropFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 2) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
- argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
- }
- const std::string& filename = args[0];
- const std::string& key = args[1];
-
- std::string buffer;
- auto updater_runtime = state->updater->GetRuntime();
- if (!updater_runtime->ReadFileToString(filename, &buffer)) {
- ErrorAbort(state, kFreadFailure, "%s: failed to read %s", name, filename.c_str());
- return nullptr;
- }
-
- std::vector<std::string> lines = android::base::Split(buffer, "\n");
- for (size_t i = 0; i < lines.size(); i++) {
- std::string line = android::base::Trim(lines[i]);
-
- // comment or blank line: skip to next line
- if (line.empty() || line[0] == '#') {
- continue;
- }
- size_t equal_pos = line.find('=');
- if (equal_pos == std::string::npos) {
- continue;
- }
-
- // trim whitespace between key and '='
- std::string str = android::base::Trim(line.substr(0, equal_pos));
-
- // not the key we're looking for
- if (key != str) continue;
-
- return StringValue(android::base::Trim(line.substr(equal_pos + 1)));
- }
-
- return StringValue("");
-}
-
-// apply_patch_space(bytes)
-Value* ApplyPatchSpaceFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 1) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 args, got %zu", name,
- argv.size());
- }
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
- }
- const std::string& bytes_str = args[0];
-
- size_t bytes;
- if (!android::base::ParseUint(bytes_str.c_str(), &bytes)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count", name,
- bytes_str.c_str());
- }
-
- // Skip the cache size check if the update is a retry.
- if (state->is_retry || CheckAndFreeSpaceOnCache(bytes)) {
- return StringValue("t");
- }
- return StringValue("");
-}
-
-Value* WipeCacheFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (!argv.empty()) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %zu", name,
- argv.size());
- }
-
- state->updater->WriteToCommandPipe("wipe_cache");
- return StringValue("t");
-}
-
-Value* RunProgramFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() < 1) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name);
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
- }
-
- auto updater_runtime = state->updater->GetRuntime();
- auto status = updater_runtime->RunProgram(args, false);
- return StringValue(std::to_string(status));
-}
-
-// read_file(filename)
-// Reads a local file 'filename' and returns its contents as a string Value.
-Value* ReadFileFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 1) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name);
- }
- const std::string& filename = args[0];
-
- std::string contents;
- auto updater_runtime = state->updater->GetRuntime();
- if (updater_runtime->ReadFileToString(filename, &contents)) {
- return new Value(Value::Type::STRING, std::move(contents));
- }
-
- // Leave it to caller to handle the failure.
- PLOG(ERROR) << name << ": Failed to read " << filename;
- return StringValue("");
-}
-
-// write_value(value, filename)
-// Writes 'value' to 'filename'.
-// Example: write_value("960000", "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq")
-Value* WriteValueFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 2) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
- argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name);
- }
-
- const std::string& filename = args[1];
- if (filename.empty()) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): Filename cannot be empty", name);
- }
-
- const std::string& value = args[0];
- auto updater_runtime = state->updater->GetRuntime();
- if (!updater_runtime->WriteStringToFile(value, filename)) {
- PLOG(ERROR) << name << ": Failed to write to \"" << filename << "\"";
- return StringValue("");
- }
- return StringValue("t");
-}
-
-// Immediately reboot the device. Recovery is not finished normally,
-// so if you reboot into recovery it will re-start applying the
-// current package (because nothing has cleared the copy of the
-// arguments stored in the BCB).
-//
-// The argument is the partition name passed to the android reboot
-// property. It can be "recovery" to boot from the recovery
-// partition, or "" (empty string) to boot from the regular boot
-// partition.
-Value* RebootNowFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 2) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
- argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name);
- }
- const std::string& filename = args[0];
- const std::string& property = args[1];
-
- // Zero out the 'command' field of the bootloader message. Leave the rest intact.
- bootloader_message boot;
- std::string err;
- if (!read_bootloader_message_from(&boot, filename, &err)) {
- LOG(ERROR) << name << "(): Failed to read from \"" << filename << "\": " << err;
- return StringValue("");
- }
- memset(boot.command, 0, sizeof(boot.command));
- if (!write_bootloader_message_to(boot, filename, &err)) {
- LOG(ERROR) << name << "(): Failed to write to \"" << filename << "\": " << err;
- return StringValue("");
- }
-
- Reboot(property);
-
- return ErrorAbort(state, kRebootFailure, "%s() failed to reboot", name);
-}
-
-// Store a string value somewhere that future invocations of recovery
-// can access it. This value is called the "stage" and can be used to
-// drive packages that need to do reboots in the middle of
-// installation and keep track of where they are in the multi-stage
-// install.
-//
-// The first argument is the block device for the misc partition
-// ("/misc" in the fstab), which is where this value is stored. The
-// second argument is the string to store; it should not exceed 31
-// bytes.
-Value* SetStageFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 2) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
- argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
- }
- const std::string& filename = args[0];
- const std::string& stagestr = args[1];
-
- // Store this value in the misc partition, immediately after the
- // bootloader message that the main recovery uses to save its
- // arguments in case of the device restarting midway through
- // package installation.
- bootloader_message boot;
- std::string err;
- if (!read_bootloader_message_from(&boot, filename, &err)) {
- LOG(ERROR) << name << "(): Failed to read from \"" << filename << "\": " << err;
- return StringValue("");
- }
- strlcpy(boot.stage, stagestr.c_str(), sizeof(boot.stage));
- if (!write_bootloader_message_to(boot, filename, &err)) {
- LOG(ERROR) << name << "(): Failed to write to \"" << filename << "\": " << err;
- return StringValue("");
- }
-
- return StringValue(filename);
-}
-
-// Return the value most recently saved with SetStageFn. The argument
-// is the block device for the misc partition.
-Value* GetStageFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 1) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
- }
- const std::string& filename = args[0];
-
- bootloader_message boot;
- std::string err;
- if (!read_bootloader_message_from(&boot, filename, &err)) {
- LOG(ERROR) << name << "(): Failed to read from \"" << filename << "\": " << err;
- return StringValue("");
- }
-
- return StringValue(boot.stage);
-}
-
-Value* WipeBlockDeviceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 2) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
- argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
- }
- const std::string& filename = args[0];
- const std::string& len_str = args[1];
-
- size_t len;
- if (!android::base::ParseUint(len_str.c_str(), &len)) {
- return nullptr;
- }
-
- auto updater_runtime = state->updater->GetRuntime();
- int status = updater_runtime->WipeBlockDevice(filename, len);
- return StringValue(status == 0 ? "t" : "");
-}
-
-Value* EnableRebootFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (!argv.empty()) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %zu", name,
- argv.size());
- }
- state->updater->WriteToCommandPipe("enable_reboot");
- return StringValue("t");
-}
-
-Value* Tune2FsFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.empty()) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects args, got %zu", name, argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() could not read args", name);
- }
-
- // tune2fs expects the program name as its first arg.
- args.insert(args.begin(), "tune2fs");
- auto updater_runtime = state->updater->GetRuntime();
- if (auto result = updater_runtime->Tune2Fs(args); result != 0) {
- return ErrorAbort(state, kTune2FsFailure, "%s() returned error code %d", name, result);
- }
- return StringValue("t");
-}
-
-Value* AddSlotSuffixFn(const char* name, State* state,
- const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() != 1) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
- }
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
- }
- const std::string& arg = args[0];
- auto updater_runtime = state->updater->GetRuntime();
- return StringValue(updater_runtime->AddSlotSuffix(arg));
-}
-
-void RegisterInstallFunctions() {
- RegisterFunction("mount", MountFn);
- RegisterFunction("is_mounted", IsMountedFn);
- RegisterFunction("unmount", UnmountFn);
- RegisterFunction("format", FormatFn);
- RegisterFunction("show_progress", ShowProgressFn);
- RegisterFunction("set_progress", SetProgressFn);
- RegisterFunction("package_extract_file", PackageExtractFileFn);
-
- RegisterFunction("getprop", GetPropFn);
- RegisterFunction("file_getprop", FileGetPropFn);
-
- RegisterFunction("apply_patch_space", ApplyPatchSpaceFn);
- RegisterFunction("patch_partition", PatchPartitionFn);
- RegisterFunction("patch_partition_check", PatchPartitionCheckFn);
-
- RegisterFunction("wipe_block_device", WipeBlockDeviceFn);
-
- RegisterFunction("read_file", ReadFileFn);
- RegisterFunction("write_value", WriteValueFn);
-
- RegisterFunction("wipe_cache", WipeCacheFn);
-
- RegisterFunction("ui_print", UIPrintFn);
-
- RegisterFunction("run_program", RunProgramFn);
-
- RegisterFunction("reboot_now", RebootNowFn);
- RegisterFunction("get_stage", GetStageFn);
- RegisterFunction("set_stage", SetStageFn);
-
- RegisterFunction("enable_reboot", EnableRebootFn);
- RegisterFunction("tune2fs", Tune2FsFn);
-
- RegisterFunction("add_slot_suffix", AddSlotSuffixFn);
-}
diff --git a/updater/mounts.cpp b/updater/mounts.cpp
deleted file mode 100644
index 943d35c..0000000
--- a/updater/mounts.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "mounts.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <mntent.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-
-#include <string>
-#include <vector>
-
-#include <android-base/logging.h>
-
-struct MountedVolume {
- std::string device;
- std::string mount_point;
- std::string filesystem;
- std::string flags;
-};
-
-static std::vector<MountedVolume*> g_mounts_state;
-
-bool scan_mounted_volumes() {
- for (size_t i = 0; i < g_mounts_state.size(); ++i) {
- delete g_mounts_state[i];
- }
- g_mounts_state.clear();
-
- // Open and read mount table entries.
- FILE* fp = setmntent("/proc/mounts", "re");
- if (fp == NULL) {
- return false;
- }
- mntent* e;
- while ((e = getmntent(fp)) != NULL) {
- MountedVolume* v = new MountedVolume;
- v->device = e->mnt_fsname;
- v->mount_point = e->mnt_dir;
- v->filesystem = e->mnt_type;
- v->flags = e->mnt_opts;
- g_mounts_state.push_back(v);
- }
- endmntent(fp);
- return true;
-}
-
-MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point) {
- for (size_t i = 0; i < g_mounts_state.size(); ++i) {
- if (g_mounts_state[i]->mount_point == mount_point) return g_mounts_state[i];
- }
- return nullptr;
-}
-
-int unmount_mounted_volume(MountedVolume* volume) {
- // Intentionally pass the empty string to umount if the caller tries to unmount a volume they
- // already unmounted using this function.
- std::string mount_point = volume->mount_point;
- volume->mount_point.clear();
- int result = umount(mount_point.c_str());
- if (result == -1) {
- PLOG(WARNING) << "Failed to umount " << mount_point;
- }
- return result;
-}
diff --git a/updater/mounts.h b/updater/mounts.h
deleted file mode 100644
index 6786c8d..0000000
--- a/updater/mounts.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-#pragma once
-
-struct MountedVolume;
-
-bool scan_mounted_volumes();
-
-MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point);
-
-int unmount_mounted_volume(MountedVolume* volume);
diff --git a/updater/simulator_runtime.cpp b/updater/simulator_runtime.cpp
deleted file mode 100644
index 57dfb32..0000000
--- a/updater/simulator_runtime.cpp
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "updater/simulator_runtime.h"
-
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <unordered_set>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/wipe.h>
-#include <selinux/label.h>
-
-#include "mounts.h"
-#include "otautil/sysutil.h"
-
-std::string SimulatorRuntime::GetProperty(const std::string_view key,
- const std::string_view default_value) const {
- return source_->GetProperty(key, default_value);
-}
-
-int SimulatorRuntime::Mount(const std::string_view location, const std::string_view mount_point,
- const std::string_view /* fs_type */,
- const std::string_view /* mount_options */) {
- if (auto mounted_location = mounted_partitions_.find(mount_point);
- mounted_location != mounted_partitions_.end() && mounted_location->second != location) {
- LOG(ERROR) << mount_point << " has been mounted at " << mounted_location->second;
- return -1;
- }
-
- mounted_partitions_.emplace(mount_point, location);
- return 0;
-}
-
-bool SimulatorRuntime::IsMounted(const std::string_view mount_point) const {
- return mounted_partitions_.find(mount_point) != mounted_partitions_.end();
-}
-
-std::pair<bool, int> SimulatorRuntime::Unmount(const std::string_view mount_point) {
- if (!IsMounted(mount_point)) {
- return { false, -1 };
- }
-
- mounted_partitions_.erase(std::string(mount_point));
- return { true, 0 };
-}
-
-std::string SimulatorRuntime::FindBlockDeviceName(const std::string_view name) const {
- return source_->FindBlockDeviceName(name);
-}
-
-// TODO(xunchang) implement the utility functions in simulator.
-int SimulatorRuntime::RunProgram(const std::vector<std::string>& args, bool /* is_vfork */) const {
- LOG(INFO) << "Running program with args " << android::base::Join(args, " ");
- return 0;
-}
-
-int SimulatorRuntime::Tune2Fs(const std::vector<std::string>& args) const {
- LOG(INFO) << "Running Tune2Fs with args " << android::base::Join(args, " ");
- return 0;
-}
-
-int SimulatorRuntime::WipeBlockDevice(const std::string_view filename, size_t /* len */) const {
- LOG(INFO) << "SKip wiping block device " << filename;
- return 0;
-}
-
-bool SimulatorRuntime::ReadFileToString(const std::string_view filename,
- std::string* content) const {
- if (android::base::EndsWith(filename, "oem.prop")) {
- return android::base::ReadFileToString(source_->GetOemSettings(), content);
- }
-
- LOG(INFO) << "SKip reading filename " << filename;
- return true;
-}
-
-bool SimulatorRuntime::WriteStringToFile(const std::string_view content,
- const std::string_view filename) const {
- LOG(INFO) << "SKip writing " << content.size() << " bytes to file " << filename;
- return true;
-}
-
-bool SimulatorRuntime::MapPartitionOnDeviceMapper(const std::string& partition_name,
- std::string* path) {
- *path = partition_name;
- return true;
-}
-
-bool SimulatorRuntime::UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
- LOG(INFO) << "Skip unmapping " << partition_name;
- return true;
-}
-
-bool SimulatorRuntime::UpdateDynamicPartitions(const std::string_view op_list_value) {
- const std::unordered_set<std::string> commands{
- "resize", "remove", "add", "move",
- "add_group", "resize_group", "remove_group", "remove_all_groups",
- };
-
- std::vector<std::string> lines = android::base::Split(std::string(op_list_value), "\n");
- for (const auto& line : lines) {
- if (line.empty() || line[0] == '#') continue;
- auto tokens = android::base::Split(line, " ");
- if (commands.find(tokens[0]) == commands.end()) {
- LOG(ERROR) << "Unknown operation in op_list: " << line;
- return false;
- }
- }
- return true;
-}
-
-std::string SimulatorRuntime::AddSlotSuffix(const std::string_view arg) const {
- LOG(INFO) << "Skip adding slot suffix to " << arg;
- return std::string(arg);
-}
diff --git a/updater/target_files.cpp b/updater/target_files.cpp
deleted file mode 100644
index 207146f..0000000
--- a/updater/target_files.cpp
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "updater/target_files.h"
-
-#include <unistd.h>
-
-#include <algorithm>
-#include <filesystem>
-#include <memory>
-
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <sparse/sparse.h>
-
-static bool SimgToImg(int input_fd, int output_fd) {
- if (lseek64(input_fd, 0, SEEK_SET) == -1) {
- PLOG(ERROR) << "Failed to lseek64 on the input sparse image";
- return false;
- }
-
- if (lseek64(output_fd, 0, SEEK_SET) == -1) {
- PLOG(ERROR) << "Failed to lseek64 on the output raw image";
- return false;
- }
-
- std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> s_file(
- sparse_file_import(input_fd, true, false), sparse_file_destroy);
- if (!s_file) {
- LOG(ERROR) << "Failed to import the sparse image.";
- return false;
- }
-
- if (sparse_file_write(s_file.get(), output_fd, false, false, false) < 0) {
- PLOG(ERROR) << "Failed to output the raw image file.";
- return false;
- }
-
- return true;
-}
-
-static bool ParsePropertyFile(const std::string_view prop_content,
- std::map<std::string, std::string, std::less<>>* props_map) {
- LOG(INFO) << "Start parsing build property\n";
- std::vector<std::string> lines = android::base::Split(std::string(prop_content), "\n");
- for (const auto& line : lines) {
- if (line.empty() || line[0] == '#') continue;
- auto pos = line.find('=');
- if (pos == std::string::npos) continue;
- std::string key = line.substr(0, pos);
- std::string value = line.substr(pos + 1);
- LOG(INFO) << key << ": " << value;
- props_map->emplace(key, value);
- }
-
- return true;
-}
-
-static bool ParseFstab(const std::string_view fstab, std::vector<FstabInfo>* fstab_info_list) {
- LOG(INFO) << "parsing fstab\n";
- std::vector<std::string> lines = android::base::Split(std::string(fstab), "\n");
- for (const auto& line : lines) {
- if (line.empty() || line[0] == '#') continue;
-
- // <block_device> <mount_point> <fs_type> <mount_flags> optional:<fs_mgr_flags>
- std::vector<std::string> tokens = android::base::Split(line, " ");
- tokens.erase(std::remove(tokens.begin(), tokens.end(), ""), tokens.end());
- if (tokens.size() != 4 && tokens.size() != 5) {
- LOG(ERROR) << "Unexpected token size: " << tokens.size() << std::endl
- << "Error parsing fstab line: " << line;
- return false;
- }
-
- const auto& blockdev = tokens[0];
- const auto& mount_point = tokens[1];
- const auto& fs_type = tokens[2];
- if (!android::base::StartsWith(mount_point, "/")) {
- LOG(WARNING) << "mount point '" << mount_point << "' does not start with '/'";
- continue;
- }
-
- // The simulator only supports ext4 and emmc for now.
- if (fs_type != "ext4" && fs_type != "emmc") {
- LOG(WARNING) << "Unsupported fs_type in " << line;
- continue;
- }
-
- fstab_info_list->emplace_back(blockdev, mount_point, fs_type);
- }
-
- return true;
-}
-
-bool TargetFile::EntryExists(const std::string_view name) const {
- if (extracted_input_) {
- std::string entry_path = path_ + "/" + std::string(name);
- if (access(entry_path.c_str(), O_RDONLY) != 0) {
- PLOG(WARNING) << "Failed to access " << entry_path;
- return false;
- }
- return true;
- }
-
- CHECK(handle_);
- ZipEntry64 img_entry;
- return FindEntry(handle_, name, &img_entry) == 0;
-}
-
-bool TargetFile::ReadEntryToString(const std::string_view name, std::string* content) const {
- if (extracted_input_) {
- std::string entry_path = path_ + "/" + std::string(name);
- return android::base::ReadFileToString(entry_path, content);
- }
-
- CHECK(handle_);
- ZipEntry64 entry;
- if (auto find_err = FindEntry(handle_, name, &entry); find_err != 0) {
- LOG(ERROR) << "failed to find " << name << " in the package: " << ErrorCodeString(find_err);
- return false;
- }
-
- if (entry.uncompressed_length == 0) {
- content->clear();
- return true;
- }
-
- if (entry.uncompressed_length > std::numeric_limits<size_t>::max()) {
- LOG(ERROR) << "Failed to extract " << name
- << " because's uncompressed size exceeds size of address space. "
- << entry.uncompressed_length;
- return false;
- }
-
- content->resize(entry.uncompressed_length);
- if (auto extract_err = ExtractToMemory(
- handle_, &entry, reinterpret_cast<uint8_t*>(&content->at(0)), entry.uncompressed_length);
- extract_err != 0) {
- LOG(ERROR) << "failed to read " << name << " from package: " << ErrorCodeString(extract_err);
- return false;
- }
-
- return true;
-}
-
-bool TargetFile::ExtractEntryToTempFile(const std::string_view name,
- TemporaryFile* temp_file) const {
- if (extracted_input_) {
- std::string entry_path = path_ + "/" + std::string(name);
- return std::filesystem::copy_file(entry_path, temp_file->path,
- std::filesystem::copy_options::overwrite_existing);
- }
-
- CHECK(handle_);
- ZipEntry64 entry;
- if (auto find_err = FindEntry(handle_, name, &entry); find_err != 0) {
- LOG(ERROR) << "failed to find " << name << " in the package: " << ErrorCodeString(find_err);
- return false;
- }
-
- if (auto status = ExtractEntryToFile(handle_, &entry, temp_file->fd); status != 0) {
- LOG(ERROR) << "Failed to extract zip entry " << name << " : " << ErrorCodeString(status);
- return false;
- }
- return true;
-}
-
-bool TargetFile::Open() {
- if (!extracted_input_) {
- if (auto ret = OpenArchive(path_.c_str(), &handle_); ret != 0) {
- LOG(ERROR) << "failed to open source target file " << path_ << ": " << ErrorCodeString(ret);
- return false;
- }
- }
-
- // Parse the misc info.
- std::string misc_info_content;
- if (!ReadEntryToString("META/misc_info.txt", &misc_info_content)) {
- return false;
- }
- if (!ParsePropertyFile(misc_info_content, &misc_info_)) {
- return false;
- }
-
- return true;
-}
-
-bool TargetFile::GetBuildProps(std::map<std::string, std::string, std::less<>>* props_map) const {
- props_map->clear();
- // Parse the source zip to mock the system props and block devices. We try all the possible
- // locations for build props.
- constexpr std::string_view kPropLocations[] = {
- "SYSTEM/build.prop",
- "VENDOR/build.prop",
- "PRODUCT/build.prop",
- "SYSTEM_EXT/build.prop",
- "SYSTEM/vendor/build.prop",
- "SYSTEM/product/build.prop",
- "SYSTEM/system_ext/build.prop",
- "ODM/build.prop", // legacy
- "ODM/etc/build.prop",
- "VENDOR/odm/build.prop", // legacy
- "VENDOR/odm/etc/build.prop",
- };
- for (const auto& name : kPropLocations) {
- std::string build_prop_content;
- if (!ReadEntryToString(name, &build_prop_content)) {
- continue;
- }
- std::map<std::string, std::string, std::less<>> props;
- if (!ParsePropertyFile(build_prop_content, &props)) {
- LOG(ERROR) << "Failed to parse build prop in " << name;
- return false;
- }
- for (const auto& [key, value] : props) {
- if (auto it = props_map->find(key); it != props_map->end() && it->second != value) {
- LOG(WARNING) << "Property " << key << " has different values in property files, we got "
- << it->second << " and " << value;
- }
- props_map->emplace(key, value);
- }
- }
-
- return true;
-}
-
-bool TargetFile::ExtractImage(const std::string_view entry_name, const FstabInfo& fstab_info,
- const std::string_view work_dir, TemporaryFile* image_file) const {
- if (!EntryExists(entry_name)) {
- return false;
- }
-
- // We don't need extra work for 'emmc'; use the image file as the block device.
- if (fstab_info.fs_type == "emmc" || misc_info_.find("extfs_sparse_flag") == misc_info_.end()) {
- if (!ExtractEntryToTempFile(entry_name, image_file)) {
- return false;
- }
- } else { // treated as ext4 sparse image
- TemporaryFile sparse_image{ std::string(work_dir) };
- if (!ExtractEntryToTempFile(entry_name, &sparse_image)) {
- return false;
- }
-
- // Convert the sparse image to raw.
- if (!SimgToImg(sparse_image.fd, image_file->fd)) {
- LOG(ERROR) << "Failed to convert " << fstab_info.mount_point << " to raw.";
- return false;
- }
- }
-
- return true;
-}
-
-bool TargetFile::ParseFstabInfo(std::vector<FstabInfo>* fstab_info_list) const {
- // Parse the fstab file and extract the image files. The location of the fstab actually depends
- // on some flags e.g. "no_recovery", "recovery_as_boot". Here we just try all possibilities.
- constexpr std::string_view kRecoveryFstabLocations[] = {
- "RECOVERY/RAMDISK/system/etc/recovery.fstab",
- "RECOVERY/RAMDISK/etc/recovery.fstab",
- "BOOT/RAMDISK/system/etc/recovery.fstab",
- "BOOT/RAMDISK/etc/recovery.fstab",
- };
- std::string fstab_content;
- for (const auto& name : kRecoveryFstabLocations) {
- if (std::string content; ReadEntryToString(name, &content)) {
- fstab_content = std::move(content);
- break;
- }
- }
- if (fstab_content.empty()) {
- LOG(ERROR) << "Failed to parse the recovery fstab file";
- return false;
- }
-
- // Extract the images and convert them to raw.
- if (!ParseFstab(fstab_content, fstab_info_list)) {
- LOG(ERROR) << "Failed to mount the block devices for source build.";
- return false;
- }
-
- return true;
-}
diff --git a/updater/update_simulator_main.cpp b/updater/update_simulator_main.cpp
deleted file mode 100644
index 6c6989b..0000000
--- a/updater/update_simulator_main.cpp
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <getopt.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <string>
-#include <string_view>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-
-#include "edify/expr.h"
-#include "otautil/error_code.h"
-#include "otautil/paths.h"
-#include "updater/blockimg.h"
-#include "updater/build_info.h"
-#include "updater/dynamic_partitions.h"
-#include "updater/install.h"
-#include "updater/simulator_runtime.h"
-#include "updater/updater.h"
-
-using namespace std::string_literals;
-
-void Usage(std::string_view name) {
- LOG(INFO) << "Usage: " << name << "[--oem_settings <oem_property_file>]"
- << "[--skip_functions <skip_function_file>]"
- << " --source <source_target_file>"
- << " --ota_package <ota_package>";
-}
-
-Value* SimulatorPlaceHolderFn(const char* name, State* /* state */,
- const std::vector<std::unique_ptr<Expr>>& /* argv */) {
- LOG(INFO) << "Skip function " << name << " in host simulation";
- return StringValue("t");
-}
-
-int main(int argc, char** argv) {
- // Write the logs to stdout.
- android::base::InitLogging(argv, &android::base::StderrLogger);
-
- std::string oem_settings;
- std::string skip_function_file;
- std::string source_target_file;
- std::string package_name;
- std::string work_dir;
- bool keep_images = false;
-
- constexpr struct option OPTIONS[] = {
- { "keep_images", no_argument, nullptr, 0 },
- { "oem_settings", required_argument, nullptr, 0 },
- { "ota_package", required_argument, nullptr, 0 },
- { "skip_functions", required_argument, nullptr, 0 },
- { "source", required_argument, nullptr, 0 },
- { "work_dir", required_argument, nullptr, 0 },
- { nullptr, 0, nullptr, 0 },
- };
-
- int arg;
- int option_index;
- while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) {
- if (arg != 0) {
- LOG(ERROR) << "Invalid command argument";
- Usage(argv[0]);
- return EXIT_FAILURE;
- }
- auto option_name = OPTIONS[option_index].name;
- // The same oem property file used during OTA generation. It's needed for file_getprop() to
- // return the correct value for the source build.
- if (option_name == "oem_settings"s) {
- oem_settings = optarg;
- } else if (option_name == "skip_functions"s) {
- skip_function_file = optarg;
- } else if (option_name == "source"s) {
- source_target_file = optarg;
- } else if (option_name == "ota_package"s) {
- package_name = optarg;
- } else if (option_name == "keep_images"s) {
- keep_images = true;
- } else if (option_name == "work_dir"s) {
- work_dir = optarg;
- } else {
- Usage(argv[0]);
- return EXIT_FAILURE;
- }
- }
-
- if (source_target_file.empty() || package_name.empty()) {
- Usage(argv[0]);
- return EXIT_FAILURE;
- }
-
- // Configure edify's functions.
- RegisterBuiltins();
- RegisterInstallFunctions();
- RegisterBlockImageFunctions();
- RegisterDynamicPartitionsFunctions();
-
- if (!skip_function_file.empty()) {
- std::string content;
- if (!android::base::ReadFileToString(skip_function_file, &content)) {
- PLOG(ERROR) << "Failed to read " << skip_function_file;
- return EXIT_FAILURE;
- }
-
- auto lines = android::base::Split(content, "\n");
- for (const auto& line : lines) {
- if (line.empty() || android::base::StartsWith(line, "#")) {
- continue;
- }
- RegisterFunction(line, SimulatorPlaceHolderFn);
- }
- }
-
- TemporaryFile temp_saved_source;
- TemporaryFile temp_last_command;
- TemporaryDir temp_stash_base;
-
- Paths::Get().set_cache_temp_source(temp_saved_source.path);
- Paths::Get().set_last_command_file(temp_last_command.path);
- Paths::Get().set_stash_directory_base(temp_stash_base.path);
-
- TemporaryFile cmd_pipe;
- TemporaryDir source_temp_dir;
- if (work_dir.empty()) {
- work_dir = source_temp_dir.path;
- }
-
- BuildInfo source_build_info(work_dir, keep_images);
- if (!source_build_info.ParseTargetFile(source_target_file, false)) {
- LOG(ERROR) << "Failed to parse the target file " << source_target_file;
- return EXIT_FAILURE;
- }
-
- if (!oem_settings.empty()) {
- CHECK_EQ(0, access(oem_settings.c_str(), R_OK));
- source_build_info.SetOemSettings(oem_settings);
- }
-
- Updater updater(std::make_unique<SimulatorRuntime>(&source_build_info));
- if (!updater.Init(cmd_pipe.release(), package_name, false)) {
- return EXIT_FAILURE;
- }
-
- if (!updater.RunUpdate()) {
- return EXIT_FAILURE;
- }
-
- LOG(INFO) << "\nscript succeeded, result: " << updater.GetResult();
-
- return 0;
-}
diff --git a/updater/updater.cpp b/updater/updater.cpp
deleted file mode 100644
index c526734..0000000
--- a/updater/updater.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "updater/updater.h"
-
-#include <string.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-
-#include "edify/updater_runtime_interface.h"
-
-Updater::~Updater() {
- if (package_handle_) {
- CloseArchive(package_handle_);
- }
-}
-
-bool Updater::Init(int fd, const std::string_view package_filename, bool is_retry) {
- // Set up the pipe for sending commands back to the parent process.
- cmd_pipe_.reset(fdopen(fd, "wb"));
- if (!cmd_pipe_) {
- LOG(ERROR) << "Failed to open the command pipe";
- return false;
- }
-
- setlinebuf(cmd_pipe_.get());
-
- if (!mapped_package_.MapFile(std::string(package_filename))) {
- LOG(ERROR) << "failed to map package " << package_filename;
- return false;
- }
- if (int open_err = OpenArchiveFromMemory(mapped_package_.addr, mapped_package_.length,
- std::string(package_filename).c_str(), &package_handle_);
- open_err != 0) {
- LOG(ERROR) << "failed to open package " << package_filename << ": "
- << ErrorCodeString(open_err);
- return false;
- }
- if (!ReadEntryToString(package_handle_, SCRIPT_NAME, &updater_script_)) {
- return false;
- }
-
- is_retry_ = is_retry;
-
- return true;
-}
-
-bool Updater::RunUpdate() {
- CHECK(runtime_);
-
- // Parse the script.
- std::unique_ptr<Expr> root;
- int error_count = 0;
- int error = ParseString(updater_script_, &root, &error_count);
- if (error != 0 || error_count > 0) {
- LOG(ERROR) << error_count << " parse errors";
- return false;
- }
-
- // Evaluate the parsed script.
- State state(updater_script_, this);
- state.is_retry = is_retry_;
-
- bool status = Evaluate(&state, root, &result_);
- if (status) {
- fprintf(cmd_pipe_.get(), "ui_print script succeeded: result was [%s]\n", result_.c_str());
- // Even though the script doesn't abort, still log the cause code if result is empty.
- if (result_.empty() && state.cause_code != kNoCause) {
- fprintf(cmd_pipe_.get(), "log cause: %d\n", state.cause_code);
- }
- for (const auto& func : skipped_functions_) {
- LOG(WARNING) << "Skipped executing function " << func;
- }
- return true;
- }
-
- ParseAndReportErrorCode(&state);
- return false;
-}
-
-void Updater::WriteToCommandPipe(const std::string_view message, bool flush) const {
- fprintf(cmd_pipe_.get(), "%s\n", std::string(message).c_str());
- if (flush) {
- fflush(cmd_pipe_.get());
- }
-}
-
-void Updater::UiPrint(const std::string_view message) const {
- // "line1\nline2\n" will be split into 3 tokens: "line1", "line2" and "".
- // so skip sending empty strings to ui.
- std::vector<std::string> lines = android::base::Split(std::string(message), "\n");
- for (const auto& line : lines) {
- if (!line.empty()) {
- fprintf(cmd_pipe_.get(), "ui_print %s\n", line.c_str());
- }
- }
-
- // on the updater side, we need to dump the contents to stderr (which has
- // been redirected to the log file). because the recovery will only print
- // the contents to screen when processing pipe command ui_print.
- LOG(INFO) << message;
-}
-
-std::string Updater::FindBlockDeviceName(const std::string_view name) const {
- return runtime_->FindBlockDeviceName(name);
-}
-
-void Updater::ParseAndReportErrorCode(State* state) {
- CHECK(state);
- if (state->errmsg.empty()) {
- LOG(ERROR) << "script aborted (no error message)";
- fprintf(cmd_pipe_.get(), "ui_print script aborted (no error message)\n");
- } else {
- LOG(ERROR) << "script aborted: " << state->errmsg;
- const std::vector<std::string> lines = android::base::Split(state->errmsg, "\n");
- for (const std::string& line : lines) {
- // Parse the error code in abort message.
- // Example: "E30: This package is for bullhead devices."
- if (!line.empty() && line[0] == 'E') {
- if (sscanf(line.c_str(), "E%d: ", &state->error_code) != 1) {
- LOG(ERROR) << "Failed to parse error code: [" << line << "]";
- }
- }
- fprintf(cmd_pipe_.get(), "ui_print %s\n", line.c_str());
- }
- }
-
- // Installation has been aborted. Set the error code to kScriptExecutionFailure unless
- // a more specific code has been set in errmsg.
- if (state->error_code == kNoError) {
- state->error_code = kScriptExecutionFailure;
- }
- fprintf(cmd_pipe_.get(), "log error: %d\n", state->error_code);
- // Cause code should provide additional information about the abort.
- if (state->cause_code != kNoCause) {
- fprintf(cmd_pipe_.get(), "log cause: %d\n", state->cause_code);
- if (state->cause_code == kPatchApplicationFailure) {
- LOG(INFO) << "Patch application failed, retry update.";
- fprintf(cmd_pipe_.get(), "retry_update\n");
- } else if (state->cause_code == kEioFailure) {
- LOG(INFO) << "Update failed due to EIO, retry update.";
- fprintf(cmd_pipe_.get(), "retry_update\n");
- }
- }
-}
-
-bool Updater::ReadEntryToString(ZipArchiveHandle za, const std::string& entry_name,
- std::string* content) {
- ZipEntry64 entry;
- int find_err = FindEntry(za, entry_name, &entry);
- if (find_err != 0) {
- LOG(ERROR) << "failed to find " << entry_name
- << " in the package: " << ErrorCodeString(find_err);
- return false;
- }
- if (entry.uncompressed_length > std::numeric_limits<size_t>::max()) {
- LOG(ERROR) << "Failed to extract " << entry_name
- << " because's uncompressed size exceeds size of address space. "
- << entry.uncompressed_length;
- return false;
- }
- content->resize(entry.uncompressed_length);
- int extract_err = ExtractToMemory(za, &entry, reinterpret_cast<uint8_t*>(&content->at(0)),
- entry.uncompressed_length);
- if (extract_err != 0) {
- LOG(ERROR) << "failed to read " << entry_name
- << " from package: " << ErrorCodeString(extract_err);
- return false;
- }
-
- return true;
-}
diff --git a/updater/updater_main.cpp b/updater/updater_main.cpp
deleted file mode 100644
index 33d5b5b..0000000
--- a/updater/updater_main.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-#include <openssl/crypto.h>
-#include <selinux/android.h>
-#include <selinux/label.h>
-#include <selinux/selinux.h>
-
-#include "edify/expr.h"
-#include "updater/blockimg.h"
-#include "updater/dynamic_partitions.h"
-#include "updater/install.h"
-#include "updater/updater.h"
-#include "updater/updater_runtime.h"
-
-// Generated by the makefile, this function defines the
-// RegisterDeviceExtensions() function, which calls all the
-// registration functions for device-specific extensions.
-#include "register.inc"
-
-static void UpdaterLogger(android::base::LogId /* id */, android::base::LogSeverity /* severity */,
- const char* /* tag */, const char* /* file */, unsigned int /* line */,
- const char* message) {
- fprintf(stdout, "%s\n", message);
-}
-
-int main(int argc, char** argv) {
- // Various things log information to stdout or stderr more or less
- // at random (though we've tried to standardize on stdout). The
- // log file makes more sense if buffering is turned off so things
- // appear in the right order.
- setbuf(stdout, nullptr);
- setbuf(stderr, nullptr);
-
- // We don't have logcat yet under recovery. Update logs will always be written to stdout
- // (which is redirected to recovery.log).
- android::base::InitLogging(argv, &UpdaterLogger);
-
- // Run the libcrypto KAT(known answer tests) based self tests.
- if (BORINGSSL_self_test() != 1) {
- LOG(ERROR) << "Failed to run the boringssl self tests";
- return EXIT_FAILURE;
- }
-
- if (argc != 4 && argc != 5) {
- LOG(ERROR) << "unexpected number of arguments: " << argc;
- return EXIT_FAILURE;
- }
-
- char* version = argv[1];
- if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') {
- // We support version 1, 2, or 3.
- LOG(ERROR) << "wrong updater binary API; expected 1, 2, or 3; got " << argv[1];
- return EXIT_FAILURE;
- }
-
- int fd;
- if (!android::base::ParseInt(argv[2], &fd)) {
- LOG(ERROR) << "Failed to parse fd in " << argv[2];
- return EXIT_FAILURE;
- }
-
- std::string package_name = argv[3];
-
- bool is_retry = false;
- if (argc == 5) {
- if (strcmp(argv[4], "retry") == 0) {
- is_retry = true;
- } else {
- LOG(ERROR) << "unexpected argument: " << argv[4];
- return EXIT_FAILURE;
- }
- }
-
- // Configure edify's functions.
- RegisterBuiltins();
- RegisterInstallFunctions();
- RegisterBlockImageFunctions();
- RegisterDynamicPartitionsFunctions();
- RegisterDeviceExtensions();
-
- auto sehandle = selinux_android_file_context_handle();
- selinux_android_set_sehandle(sehandle);
-
- Updater updater(std::make_unique<UpdaterRuntime>(sehandle));
- if (!updater.Init(fd, package_name, is_retry)) {
- return EXIT_FAILURE;
- }
-
- if (!updater.RunUpdate()) {
- return EXIT_FAILURE;
- }
-
- return EXIT_SUCCESS;
-}
\ No newline at end of file
diff --git a/updater/updater_runtime.cpp b/updater/updater_runtime.cpp
deleted file mode 100644
index bac078c..0000000
--- a/updater/updater_runtime.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "updater/updater_runtime.h"
-
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/wipe.h>
-#include <fs_mgr.h>
-#include <selinux/label.h>
-#include <tune2fs.h>
-
-#include "mounts.h"
-#include "otautil/sysutil.h"
-
-std::string UpdaterRuntime::GetProperty(const std::string_view key,
- const std::string_view default_value) const {
- return android::base::GetProperty(std::string(key), std::string(default_value));
-}
-
-std::string UpdaterRuntime::FindBlockDeviceName(const std::string_view name) const {
- return std::string(name);
-}
-
-static bool setMountFlag(const std::string& flag, unsigned* mount_flags) {
- static constexpr std::pair<const char*, unsigned> mount_flags_list[] = {
- { "noatime", MS_NOATIME },
- { "noexec", MS_NOEXEC },
- { "nosuid", MS_NOSUID },
- { "nodev", MS_NODEV },
- { "nodiratime", MS_NODIRATIME },
- { "ro", MS_RDONLY },
- { "rw", 0 },
- { "remount", MS_REMOUNT },
- { "bind", MS_BIND },
- { "rec", MS_REC },
- { "unbindable", MS_UNBINDABLE },
- { "private", MS_PRIVATE },
- { "slave", MS_SLAVE },
- { "shared", MS_SHARED },
- { "defaults", 0 },
- };
-
- for (const auto& [name, value] : mount_flags_list) {
- if (flag == name) {
- *mount_flags |= value;
- return true;
- }
- }
- return false;
-}
-
-static bool parseMountFlags(const std::string& flags, unsigned* mount_flags,
- std::string* fs_options) {
- bool is_flag_set = false;
- std::vector<std::string> flag_list;
- for (const auto& flag : android::base::Split(flags, ",")) {
- if (!setMountFlag(flag, mount_flags)) {
- // Unknown flag, so it must be a filesystem specific option.
- flag_list.push_back(flag);
- } else {
- is_flag_set = true;
- }
- }
- *fs_options = android::base::Join(flag_list, ',');
- return is_flag_set;
-}
-
-int UpdaterRuntime::Mount(const std::string_view location, const std::string_view mount_point,
- const std::string_view fs_type, const std::string_view mount_options) {
- std::string mount_point_string(mount_point);
- std::string mount_options_string(mount_options);
- char* secontext = nullptr;
- unsigned mount_flags = 0;
- std::string fs_options;
-
- if (sehandle_) {
- selabel_lookup(sehandle_, &secontext, mount_point_string.c_str(), 0755);
- setfscreatecon(secontext);
- }
-
- mkdir(mount_point_string.c_str(), 0755);
-
- if (secontext) {
- freecon(secontext);
- setfscreatecon(nullptr);
- }
-
- if (!parseMountFlags(mount_options_string, &mount_flags, &fs_options)) {
- // Fall back to default
- mount_flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME;
- }
-
- return mount(std::string(location).c_str(), mount_point_string.c_str(),
- std::string(fs_type).c_str(), mount_flags, fs_options.c_str());
-}
-
-bool UpdaterRuntime::IsMounted(const std::string_view mount_point) const {
- scan_mounted_volumes();
- MountedVolume* vol = find_mounted_volume_by_mount_point(std::string(mount_point).c_str());
- return vol != nullptr;
-}
-
-std::pair<bool, int> UpdaterRuntime::Unmount(const std::string_view mount_point) {
- scan_mounted_volumes();
- MountedVolume* vol = find_mounted_volume_by_mount_point(std::string(mount_point).c_str());
- if (vol == nullptr) {
- return { false, -1 };
- }
-
- int ret = unmount_mounted_volume(vol);
- return { true, ret };
-}
-
-bool UpdaterRuntime::ReadFileToString(const std::string_view filename, std::string* content) const {
- return android::base::ReadFileToString(std::string(filename), content);
-}
-
-bool UpdaterRuntime::WriteStringToFile(const std::string_view content,
- const std::string_view filename) const {
- return android::base::WriteStringToFile(std::string(content), std::string(filename));
-}
-
-int UpdaterRuntime::WipeBlockDevice(const std::string_view filename, size_t len) const {
- android::base::unique_fd fd(open(std::string(filename).c_str(), O_WRONLY));
- if (fd == -1) {
- PLOG(ERROR) << "Failed to open " << filename;
- return false;
- }
- // The wipe_block_device function in ext4_utils returns 0 on success and 1 for failure.
- return wipe_block_device(fd, len);
-}
-
-int UpdaterRuntime::RunProgram(const std::vector<std::string>& args, bool is_vfork) const {
- CHECK(!args.empty());
- auto argv = StringVectorToNullTerminatedArray(args);
- LOG(INFO) << "about to run program [" << args[0] << "] with " << argv.size() << " args";
-
- pid_t child = is_vfork ? vfork() : fork();
- if (child == 0) {
- execv(argv[0], argv.data());
- PLOG(ERROR) << "run_program: execv failed";
- _exit(EXIT_FAILURE);
- }
-
- int status;
- waitpid(child, &status, 0);
- if (WIFEXITED(status)) {
- if (WEXITSTATUS(status) != 0) {
- LOG(ERROR) << "run_program: child exited with status " << WEXITSTATUS(status);
- }
- } else if (WIFSIGNALED(status)) {
- LOG(ERROR) << "run_program: child terminated by signal " << WTERMSIG(status);
- }
-
- return status;
-}
-
-int UpdaterRuntime::Tune2Fs(const std::vector<std::string>& args) const {
- auto tune2fs_args = StringVectorToNullTerminatedArray(args);
- // tune2fs changes the filesystem parameters on an ext2 filesystem; it returns 0 on success.
- return tune2fs_main(tune2fs_args.size() - 1, tune2fs_args.data());
-}
-
-std::string UpdaterRuntime::AddSlotSuffix(const std::string_view arg) const {
- return std::string(arg) + fs_mgr_get_slot_suffix();
-}
diff --git a/updater/updater_runtime_dynamic_partitions.cpp b/updater/updater_runtime_dynamic_partitions.cpp
deleted file mode 100644
index 6570cff..0000000
--- a/updater/updater_runtime_dynamic_partitions.cpp
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "updater/updater_runtime.h"
-
-#include <algorithm>
-#include <chrono>
-#include <iterator>
-#include <optional>
-#include <string>
-#include <type_traits>
-#include <vector>
-
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-#include <android-base/strings.h>
-#include <fs_mgr.h>
-#include <fs_mgr_dm_linear.h>
-#include <libdm/dm.h>
-#include <liblp/builder.h>
-
-using android::dm::DeviceMapper;
-using android::dm::DmDeviceState;
-using android::fs_mgr::CreateLogicalPartition;
-using android::fs_mgr::CreateLogicalPartitionParams;
-using android::fs_mgr::DestroyLogicalPartition;
-using android::fs_mgr::LpMetadata;
-using android::fs_mgr::MetadataBuilder;
-using android::fs_mgr::Partition;
-using android::fs_mgr::PartitionOpener;
-using android::fs_mgr::SlotNumberForSlotSuffix;
-
-static constexpr std::chrono::milliseconds kMapTimeout{ 1000 };
-
-static std::string GetSuperDevice() {
- return "/dev/block/by-name/" + fs_mgr_get_super_partition_name();
-}
-
-static std::string AddSlotSuffix(const std::string& partition_name) {
- return partition_name + fs_mgr_get_slot_suffix();
-}
-
-static bool UnmapPartitionWithSuffixOnDeviceMapper(const std::string& partition_name_suffix) {
- auto state = DeviceMapper::Instance().GetState(partition_name_suffix);
- if (state == DmDeviceState::INVALID) {
- return true;
- }
- if (state == DmDeviceState::ACTIVE) {
- return DestroyLogicalPartition(partition_name_suffix);
- }
- LOG(ERROR) << "Unknown device mapper state: "
- << static_cast<std::underlying_type_t<DmDeviceState>>(state);
- return false;
-}
-
-bool UpdaterRuntime::MapPartitionOnDeviceMapper(const std::string& partition_name,
- std::string* path) {
- auto partition_name_suffix = AddSlotSuffix(partition_name);
- auto state = DeviceMapper::Instance().GetState(partition_name_suffix);
- if (state == DmDeviceState::INVALID) {
- CreateLogicalPartitionParams params = {
- .block_device = GetSuperDevice(),
- // If device supports A/B, apply non-A/B update to the partition at current slot. Otherwise,
- // SlotNumberForSlotSuffix("") returns 0.
- .metadata_slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix()),
- // If device supports A/B, apply non-A/B update to the partition at current slot. Otherwise,
- // fs_mgr_get_slot_suffix() returns empty string.
- .partition_name = partition_name_suffix,
- .force_writable = true,
- .timeout_ms = kMapTimeout,
- };
- return CreateLogicalPartition(params, path);
- }
-
- if (state == DmDeviceState::ACTIVE) {
- return DeviceMapper::Instance().GetDmDevicePathByName(partition_name_suffix, path);
- }
- LOG(ERROR) << "Unknown device mapper state: "
- << static_cast<std::underlying_type_t<DmDeviceState>>(state);
- return false;
-}
-
-bool UpdaterRuntime::UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
- return ::UnmapPartitionWithSuffixOnDeviceMapper(AddSlotSuffix(partition_name));
-}
-
-namespace { // Ops
-
-struct OpParameters {
- std::vector<std::string> tokens;
- MetadataBuilder* builder;
-
- bool ExpectArgSize(size_t size) const {
- CHECK(!tokens.empty());
- auto actual = tokens.size() - 1;
- if (actual != size) {
- LOG(ERROR) << "Op " << op() << " expects " << size << " args, got " << actual;
- return false;
- }
- return true;
- }
- const std::string& op() const {
- CHECK(!tokens.empty());
- return tokens[0];
- }
- const std::string& arg(size_t pos) const {
- CHECK_LE(pos + 1, tokens.size());
- return tokens[pos + 1];
- }
- std::optional<uint64_t> uint_arg(size_t pos, const std::string& name) const {
- auto str = arg(pos);
- uint64_t ret;
- if (!android::base::ParseUint(str, &ret)) {
- LOG(ERROR) << "Op " << op() << " expects uint64 for argument " << name << ", got " << str;
- return std::nullopt;
- }
- return ret;
- }
-};
-
-using OpFunction = std::function<bool(const OpParameters&)>;
-using OpMap = std::map<std::string, OpFunction>;
-
-bool PerformOpResize(const OpParameters& params) {
- if (!params.ExpectArgSize(2)) return false;
- const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
- auto size = params.uint_arg(1, "size");
- if (!size.has_value()) return false;
-
- auto partition = params.builder->FindPartition(partition_name_suffix);
- if (partition == nullptr) {
- LOG(ERROR) << "Failed to find partition " << partition_name_suffix
- << " in dynamic partition metadata.";
- return false;
- }
- if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) {
- LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before resizing.";
- return false;
- }
- if (!params.builder->ResizePartition(partition, size.value())) {
- LOG(ERROR) << "Failed to resize partition " << partition_name_suffix << " to size " << *size
- << ".";
- return false;
- }
- return true;
-}
-
-bool PerformOpRemove(const OpParameters& params) {
- if (!params.ExpectArgSize(1)) return false;
- const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
-
- if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) {
- LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before removing.";
- return false;
- }
- params.builder->RemovePartition(partition_name_suffix);
- return true;
-}
-
-bool PerformOpAdd(const OpParameters& params) {
- if (!params.ExpectArgSize(2)) return false;
- const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
- const auto& group_name_suffix = AddSlotSuffix(params.arg(1));
-
- if (params.builder->AddPartition(partition_name_suffix, group_name_suffix,
- LP_PARTITION_ATTR_READONLY) == nullptr) {
- LOG(ERROR) << "Failed to add partition " << partition_name_suffix << " to group "
- << group_name_suffix << ".";
- return false;
- }
- return true;
-}
-
-bool PerformOpMove(const OpParameters& params) {
- if (!params.ExpectArgSize(2)) return false;
- const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
- const auto& new_group_name_suffix = AddSlotSuffix(params.arg(1));
-
- auto partition = params.builder->FindPartition(partition_name_suffix);
- if (partition == nullptr) {
- LOG(ERROR) << "Cannot move partition " << partition_name_suffix << " to group "
- << new_group_name_suffix << " because it is not found.";
- return false;
- }
-
- auto old_group_name_suffix = partition->group_name();
- if (old_group_name_suffix != new_group_name_suffix) {
- if (!params.builder->ChangePartitionGroup(partition, new_group_name_suffix)) {
- LOG(ERROR) << "Cannot move partition " << partition_name_suffix << " from group "
- << old_group_name_suffix << " to group " << new_group_name_suffix << ".";
- return false;
- }
- }
- return true;
-}
-
-bool PerformOpAddGroup(const OpParameters& params) {
- if (!params.ExpectArgSize(2)) return false;
- const auto& group_name_suffix = AddSlotSuffix(params.arg(0));
- auto maximum_size = params.uint_arg(1, "maximum_size");
- if (!maximum_size.has_value()) return false;
-
- auto group = params.builder->FindGroup(group_name_suffix);
- if (group != nullptr) {
- LOG(ERROR) << "Cannot add group " << group_name_suffix << " because it already exists.";
- return false;
- }
-
- if (maximum_size.value() == 0) {
- LOG(WARNING) << "Adding group " << group_name_suffix << " with no size limits.";
- }
-
- if (!params.builder->AddGroup(group_name_suffix, maximum_size.value())) {
- LOG(ERROR) << "Failed to add group " << group_name_suffix << " with maximum size "
- << maximum_size.value() << ".";
- return false;
- }
- return true;
-}
-
-bool PerformOpResizeGroup(const OpParameters& params) {
- if (!params.ExpectArgSize(2)) return false;
- const auto& group_name_suffix = AddSlotSuffix(params.arg(0));
- auto new_size = params.uint_arg(1, "maximum_size");
- if (!new_size.has_value()) return false;
-
- auto group = params.builder->FindGroup(group_name_suffix);
- if (group == nullptr) {
- LOG(ERROR) << "Cannot resize group " << group_name_suffix << " because it is not found.";
- return false;
- }
-
- auto old_size = group->maximum_size();
- if (old_size != new_size.value()) {
- if (!params.builder->ChangeGroupSize(group_name_suffix, new_size.value())) {
- LOG(ERROR) << "Cannot resize group " << group_name_suffix << " from " << old_size << " to "
- << new_size.value() << ".";
- return false;
- }
- }
- return true;
-}
-
-std::vector<std::string> ListPartitionNamesInGroup(MetadataBuilder* builder,
- const std::string& group_name_suffix) {
- auto partitions = builder->ListPartitionsInGroup(group_name_suffix);
- std::vector<std::string> partition_names;
- std::transform(partitions.begin(), partitions.end(), std::back_inserter(partition_names),
- [](Partition* partition) { return partition->name(); });
- return partition_names;
-}
-
-bool PerformOpRemoveGroup(const OpParameters& params) {
- if (!params.ExpectArgSize(1)) return false;
- const auto& group_name_suffix = AddSlotSuffix(params.arg(0));
-
- auto partition_names = ListPartitionNamesInGroup(params.builder, group_name_suffix);
- if (!partition_names.empty()) {
- LOG(ERROR) << "Cannot remove group " << group_name_suffix
- << " because it still contains partitions ["
- << android::base::Join(partition_names, ", ") << "]";
- return false;
- }
- params.builder->RemoveGroupAndPartitions(group_name_suffix);
- return true;
-}
-
-bool PerformOpRemoveAllGroups(const OpParameters& params) {
- if (!params.ExpectArgSize(0)) return false;
-
- auto group_names = params.builder->ListGroups();
- for (const auto& group_name_suffix : group_names) {
- auto partition_names = ListPartitionNamesInGroup(params.builder, group_name_suffix);
- for (const auto& partition_name_suffix : partition_names) {
- if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) {
- LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before removing group "
- << group_name_suffix << ".";
- return false;
- }
- }
- params.builder->RemoveGroupAndPartitions(group_name_suffix);
- }
- return true;
-}
-
-} // namespace
-
-bool UpdaterRuntime::UpdateDynamicPartitions(const std::string_view op_list_value) {
- auto super_device = GetSuperDevice();
- auto builder = MetadataBuilder::New(PartitionOpener(), super_device, 0);
- if (builder == nullptr) {
- LOG(ERROR) << "Failed to load dynamic partition metadata.";
- return false;
- }
-
- static const OpMap op_map{
- // clang-format off
- {"resize", PerformOpResize},
- {"remove", PerformOpRemove},
- {"add", PerformOpAdd},
- {"move", PerformOpMove},
- {"add_group", PerformOpAddGroup},
- {"resize_group", PerformOpResizeGroup},
- {"remove_group", PerformOpRemoveGroup},
- {"remove_all_groups", PerformOpRemoveAllGroups},
- // clang-format on
- };
-
- std::vector<std::string> lines = android::base::Split(std::string(op_list_value), "\n");
- for (const auto& line : lines) {
- auto comment_idx = line.find('#');
- auto op_and_args = comment_idx == std::string::npos ? line : line.substr(0, comment_idx);
- op_and_args = android::base::Trim(op_and_args);
- if (op_and_args.empty()) continue;
-
- auto tokens = android::base::Split(op_and_args, " ");
- const auto& op = tokens[0];
- auto it = op_map.find(op);
- if (it == op_map.end()) {
- LOG(ERROR) << "Unknown operation in op_list: " << op;
- return false;
- }
- OpParameters params;
- params.tokens = tokens;
- params.builder = builder.get();
- if (!it->second(params)) {
- return false;
- }
- }
-
- auto metadata = builder->Export();
- if (metadata == nullptr) {
- LOG(ERROR) << "Failed to export metadata.";
- return false;
- }
-
- if (!UpdatePartitionTable(super_device, *metadata, 0)) {
- LOG(ERROR) << "Failed to write metadata.";
- return false;
- }
-
- return true;
-}