diff options
-rw-r--r-- | recovery.cpp | 64 | ||||
-rw-r--r-- | tests/Android.mk | 3 | ||||
-rw-r--r-- | tests/unit/ziputil_test.cpp | 191 |
3 files changed, 245 insertions, 13 deletions
diff --git a/recovery.cpp b/recovery.cpp index 0da3946bb..b7aeaee1f 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -87,6 +87,7 @@ static const struct option OPTIONS[] = { { "security", no_argument, NULL, 'e'}, { "wipe_ab", no_argument, NULL, 0 }, { "wipe_package_size", required_argument, NULL, 0 }, + { "prompt_and_wipe_data", no_argument, NULL, 0 }, { NULL, 0, NULL, 0 }, }; @@ -138,6 +139,8 @@ struct selabel_handle* sehandle; * The arguments which may be supplied in the recovery.command file: * --update_package=path - verify install an OTA package file * --wipe_data - erase user data (and cache), then reboot + * --prompt_and_wipe_data - prompt the user that data is corrupt, + * with their consent erase user data (and cache), then reboot * --wipe_cache - wipe cache (but not user data), then reboot * --set_encrypted_filesystem=on|off - enables / diasables encrypted fs * --just_exit - do nothing; exit and reboot @@ -765,12 +768,12 @@ static bool yes_no(Device* device, const char* question1, const char* question2) return (chosen_item == 1); } -// Return true on success. -static bool wipe_data(int should_confirm, Device* device) { - if (should_confirm && !yes_no(device, "Wipe all user data?", " THIS CAN NOT BE UNDONE!")) { - return false; - } +static bool ask_to_wipe_data(Device* device) { + return yes_no(device, "Wipe all user data?", " THIS CAN NOT BE UNDONE!"); +} +// Return true on success. +static bool wipe_data(Device* device) { modified_flash = true; ui->Print("\n-- Wiping data...\n"); @@ -783,6 +786,28 @@ static bool wipe_data(int should_confirm, Device* device) { return success; } +static bool prompt_and_wipe_data(Device* device) { + const char* const headers[] = { + "Boot halted, user data is corrupt", + "Wipe all user data to recover", + NULL + }; + const char* const items[] = { + "Retry boot", + "Wipe user data", + NULL + }; + for (;;) { + int chosen_item = get_menu_selection(headers, items, 1, 0, device); + if (chosen_item != 1) { + return true; // Just reboot, no wipe; not a failure, user asked for it + } + if (ask_to_wipe_data(device)) { + return wipe_data(device); + } + } +} + // Return true on success. static bool wipe_cache(bool should_confirm, Device* device) { if (!has_cache) { @@ -1147,8 +1172,14 @@ prompt_and_wait(Device* device, int status) { return chosen_action; case Device::WIPE_DATA: - wipe_data(ui->IsTextVisible(), device); - if (!ui->IsTextVisible()) return Device::NO_ACTION; + if (ui->IsTextVisible()) { + if (ask_to_wipe_data(device)) { + wipe_data(device); + } + } else { + wipe_data(device); + return Device::NO_ACTION; + } break; case Device::WIPE_CACHE: @@ -1404,6 +1435,7 @@ int main(int argc, char **argv) { const char *update_package = NULL; bool should_wipe_data = false; + bool should_prompt_and_wipe_data = false; bool should_wipe_cache = false; bool should_wipe_ab = false; size_t wipe_package_size = 0; @@ -1441,12 +1473,13 @@ int main(int argc, char **argv) { case 'r': reason = optarg; break; case 'e': security_update = true; break; case 0: { - if (strcmp(OPTIONS[option_index].name, "wipe_ab") == 0) { + std::string option = OPTIONS[option_index].name; + if (option == "wipe_ab") { should_wipe_ab = true; - break; - } else if (strcmp(OPTIONS[option_index].name, "wipe_package_size") == 0) { + } else if (option == "wipe_package_size") { android::base::ParseUint(optarg, &wipe_package_size); - break; + } else if (option == "prompt_and_wipe_data") { + should_prompt_and_wipe_data = true; } break; } @@ -1566,9 +1599,16 @@ int main(int argc, char **argv) { } } } else if (should_wipe_data) { - if (!wipe_data(false, device)) { + if (!wipe_data(device)) { + status = INSTALL_ERROR; + } + } else if (should_prompt_and_wipe_data) { + ui->ShowText(true); + ui->SetBackground(RecoveryUI::ERROR); + if (!prompt_and_wipe_data(device)) { status = INSTALL_ERROR; } + ui->ShowText(false); } else if (should_wipe_cache) { if (!wipe_cache(false, device)) { status = INSTALL_ERROR; diff --git a/tests/Android.mk b/tests/Android.mk index 8ae52d799..effed8313 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -35,7 +35,8 @@ LOCAL_SRC_FILES := \ unit/asn1_decoder_test.cpp \ unit/locale_test.cpp \ unit/sysutil_test.cpp \ - unit/zip_test.cpp + unit/zip_test.cpp \ + unit/ziputil_test.cpp LOCAL_C_INCLUDES := bootable/recovery LOCAL_SHARED_LIBRARIES := liblog diff --git a/tests/unit/ziputil_test.cpp b/tests/unit/ziputil_test.cpp new file mode 100644 index 000000000..14e541690 --- /dev/null +++ b/tests/unit/ziputil_test.cpp @@ -0,0 +1,191 @@ +/* + * Copyright 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 <errno.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <string> + +#include <android-base/file.h> +#include <android-base/test_utils.h> +#include <gtest/gtest.h> +#include <otautil/ZipUtil.h> +#include <ziparchive/zip_archive.h> + +#include "common/test_constants.h" + +TEST(ZipUtilTest, invalid_args) { + std::string zip_path = from_testdata_base("ziptest_valid.zip"); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); + + // zip_path must be a relative path. + ASSERT_FALSE(ExtractPackageRecursive(handle, "/a/b", "/tmp", nullptr, nullptr)); + + // dest_path must be an absolute path. + ASSERT_FALSE(ExtractPackageRecursive(handle, "a/b", "tmp", nullptr, nullptr)); + ASSERT_FALSE(ExtractPackageRecursive(handle, "a/b", "", nullptr, nullptr)); + + CloseArchive(handle); +} + +TEST(ZipUtilTest, extract_all) { + std::string zip_path = from_testdata_base("ziptest_valid.zip"); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); + + // Extract the whole package into a temp directory. + TemporaryDir td; + ExtractPackageRecursive(handle, "", td.path, nullptr, nullptr); + + // Make sure all the files are extracted correctly. + std::string path(td.path); + ASSERT_EQ(0, access((path + "/a.txt").c_str(), F_OK)); + ASSERT_EQ(0, access((path + "/b.txt").c_str(), F_OK)); + ASSERT_EQ(0, access((path + "/b/c.txt").c_str(), F_OK)); + ASSERT_EQ(0, access((path + "/b/d.txt").c_str(), F_OK)); + + // The content of the file is the same as expected. + std::string content1; + ASSERT_TRUE(android::base::ReadFileToString(path + "/a.txt", &content1)); + ASSERT_EQ(kATxtContents, content1); + + std::string content2; + ASSERT_TRUE(android::base::ReadFileToString(path + "/b/d.txt", &content2)); + ASSERT_EQ(kDTxtContents, content2); + + // Clean up the temp files under td. + ASSERT_EQ(0, unlink((path + "/a.txt").c_str())); + ASSERT_EQ(0, unlink((path + "/b.txt").c_str())); + ASSERT_EQ(0, unlink((path + "/b/c.txt").c_str())); + ASSERT_EQ(0, unlink((path + "/b/d.txt").c_str())); + ASSERT_EQ(0, rmdir((path + "/b").c_str())); + + CloseArchive(handle); +} + +TEST(ZipUtilTest, extract_prefix_with_slash) { + std::string zip_path = from_testdata_base("ziptest_valid.zip"); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); + + // Extract all the entries starting with "b/". + TemporaryDir td; + ExtractPackageRecursive(handle, "b/", td.path, nullptr, nullptr); + + // Make sure all the files with "b/" prefix are extracted correctly. + std::string path(td.path); + ASSERT_EQ(0, access((path + "/c.txt").c_str(), F_OK)); + ASSERT_EQ(0, access((path + "/d.txt").c_str(), F_OK)); + + // And the rest are not extracted. + ASSERT_EQ(-1, access((path + "/a.txt").c_str(), F_OK)); + ASSERT_EQ(ENOENT, errno); + ASSERT_EQ(-1, access((path + "/b.txt").c_str(), F_OK)); + ASSERT_EQ(ENOENT, errno); + + // The content of the file is the same as expected. + std::string content1; + ASSERT_TRUE(android::base::ReadFileToString(path + "/c.txt", &content1)); + ASSERT_EQ(kCTxtContents, content1); + + std::string content2; + ASSERT_TRUE(android::base::ReadFileToString(path + "/d.txt", &content2)); + ASSERT_EQ(kDTxtContents, content2); + + // Clean up the temp files under td. + ASSERT_EQ(0, unlink((path + "/c.txt").c_str())); + ASSERT_EQ(0, unlink((path + "/d.txt").c_str())); + + CloseArchive(handle); +} + +TEST(ZipUtilTest, extract_prefix_without_slash) { + std::string zip_path = from_testdata_base("ziptest_valid.zip"); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); + + // Extract all the file entries starting with "b/". + TemporaryDir td; + ExtractPackageRecursive(handle, "b", td.path, nullptr, nullptr); + + // Make sure all the files with "b/" prefix are extracted correctly. + std::string path(td.path); + ASSERT_EQ(0, access((path + "/c.txt").c_str(), F_OK)); + ASSERT_EQ(0, access((path + "/d.txt").c_str(), F_OK)); + + // And the rest are not extracted. + ASSERT_EQ(-1, access((path + "/a.txt").c_str(), F_OK)); + ASSERT_EQ(ENOENT, errno); + ASSERT_EQ(-1, access((path + "/b.txt").c_str(), F_OK)); + ASSERT_EQ(ENOENT, errno); + + // The content of the file is the same as expected. + std::string content1; + ASSERT_TRUE(android::base::ReadFileToString(path + "/c.txt", &content1)); + ASSERT_EQ(kCTxtContents, content1); + + std::string content2; + ASSERT_TRUE(android::base::ReadFileToString(path + "/d.txt", &content2)); + ASSERT_EQ(kDTxtContents, content2); + + // Clean up the temp files under td. + ASSERT_EQ(0, unlink((path + "/c.txt").c_str())); + ASSERT_EQ(0, unlink((path + "/d.txt").c_str())); + + CloseArchive(handle); +} + +TEST(ZipUtilTest, set_timestamp) { + std::string zip_path = from_testdata_base("ziptest_valid.zip"); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); + + // Set the timestamp to 8/1/2008. + constexpr struct utimbuf timestamp = { 1217592000, 1217592000 }; + + // Extract all the entries starting with "b/". + TemporaryDir td; + ExtractPackageRecursive(handle, "b", td.path, ×tamp, nullptr); + + // Make sure all the files with "b/" prefix are extracted correctly. + std::string path(td.path); + std::string file_c = path + "/c.txt"; + std::string file_d = path + "/d.txt"; + ASSERT_EQ(0, access(file_c.c_str(), F_OK)); + ASSERT_EQ(0, access(file_d.c_str(), F_OK)); + + // Verify the timestamp. + timespec time; + time.tv_sec = 1217592000; + time.tv_nsec = 0; + + struct stat sb; + ASSERT_EQ(0, stat(file_c.c_str(), &sb)) << strerror(errno); + ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_atime)); + ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_mtime)); + + ASSERT_EQ(0, stat(file_d.c_str(), &sb)) << strerror(errno); + ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_atime)); + ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_mtime)); + + // Clean up the temp files under td. + ASSERT_EQ(0, unlink(file_c.c_str())); + ASSERT_EQ(0, unlink(file_d.c_str())); + + CloseArchive(handle); +} |