diff options
39 files changed, 1842 insertions, 1602 deletions
diff --git a/Android.mk b/Android.mk index dbc5603d8..2943f016b 100644 --- a/Android.mk +++ b/Android.mk @@ -46,6 +46,7 @@ LOCAL_SRC_FILES := \ install.cpp \ recovery.cpp \ roots.cpp \ + rotate_logs.cpp \ screen_ui.cpp \ ui.cpp \ verifier.cpp \ @@ -119,7 +120,9 @@ include $(BUILD_EXECUTABLE) # recovery-persist (system partition dynamic executable run after /data mounts) # =============================== include $(CLEAR_VARS) -LOCAL_SRC_FILES := recovery-persist.cpp +LOCAL_SRC_FILES := \ + recovery-persist.cpp \ + rotate_logs.cpp LOCAL_MODULE := recovery-persist LOCAL_SHARED_LIBRARIES := liblog libbase LOCAL_CFLAGS := -Werror @@ -129,9 +132,11 @@ include $(BUILD_EXECUTABLE) # recovery-refresh (system partition dynamic executable run at init) # =============================== include $(CLEAR_VARS) -LOCAL_SRC_FILES := recovery-refresh.cpp +LOCAL_SRC_FILES := \ + recovery-refresh.cpp \ + rotate_logs.cpp LOCAL_MODULE := recovery-refresh -LOCAL_SHARED_LIBRARIES := liblog +LOCAL_SHARED_LIBRARIES := liblog libbase LOCAL_CFLAGS := -Werror LOCAL_INIT_RC := recovery-refresh.rc include $(BUILD_EXECUTABLE) @@ -27,3 +27,16 @@ Running the tests # Or 64-bit device adb shell /data/nativetest64/recovery_unit_test/recovery_unit_test adb shell /data/nativetest64/recovery_component_test/recovery_component_test + +Running the manual tests +------------------------ + +`recovery-refresh` and `recovery-persist` executables exist only on systems without +/cache partition. And we need to follow special steps to run tests for them. + +- Execute the test on an A/B device first. The test should fail but it will log + some contents to pmsg. + +- Reboot the device immediately and run the test again. The test should save the + contents of pmsg buffer into /data/misc/recovery/inject.txt. Test will pass if + this file has expected contents. diff --git a/applypatch/Android.mk b/applypatch/Android.mk index 9bbac4410..fa0fe8a37 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -68,21 +68,41 @@ LOCAL_STATIC_LIBRARIES += libcrypto libbz libz LOCAL_CFLAGS := -Werror include $(BUILD_HOST_STATIC_LIBRARY) -# applypatch (executable) +# libapplypatch_modes (static library) # =============================== include $(CLEAR_VARS) LOCAL_CLANG := true -LOCAL_SRC_FILES := main.cpp +LOCAL_SRC_FILES := \ + applypatch_modes.cpp +LOCAL_MODULE := libapplypatch_modes +LOCAL_C_INCLUDES := bootable/recovery +LOCAL_STATIC_LIBRARIES := \ + libapplypatch \ + libbase \ + libedify \ + libcrypto +LOCAL_CFLAGS := -Werror +include $(BUILD_STATIC_LIBRARY) + +# applypatch (target executable) +# =============================== +include $(CLEAR_VARS) +LOCAL_CLANG := true +LOCAL_SRC_FILES := applypatch_main.cpp LOCAL_MODULE := applypatch -LOCAL_C_INCLUDES += bootable/recovery -LOCAL_STATIC_LIBRARIES += \ +LOCAL_C_INCLUDES := bootable/recovery +LOCAL_STATIC_LIBRARIES := \ + libapplypatch_modes \ libapplypatch \ libbase \ libedify \ libotafault \ libcrypto \ libbz -LOCAL_SHARED_LIBRARIES += libbase libz libcutils libc +LOCAL_SHARED_LIBRARIES := \ + libbase \ + libz \ + libcutils LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 48e4c8386..9d505f9f6 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -530,8 +530,8 @@ int applypatch(const char* source_filename, const char* target_sha1_str, size_t target_size, const std::vector<std::string>& patch_sha1_str, - Value** patch_data, - Value* bonus_data) { + const std::vector<std::unique_ptr<Value>>& patch_data, + const Value* bonus_data) { printf("patch %s: ", source_filename); if (target_filename[0] == '-' && target_filename[1] == '\0') { @@ -546,8 +546,8 @@ int applypatch(const char* source_filename, FileContents copy_file; FileContents source_file; - const Value* source_patch_value = NULL; - const Value* copy_patch_value = NULL; + const Value* source_patch_value = nullptr; + const Value* copy_patch_value = nullptr; // We try to load the target file into the source_file object. if (LoadFileContents(target_filename, &source_file) == 0) { @@ -571,11 +571,11 @@ int applypatch(const char* source_filename, if (!source_file.data.empty()) { int to_use = FindMatchingPatch(source_file.sha1, patch_sha1_str); if (to_use >= 0) { - source_patch_value = patch_data[to_use]; + source_patch_value = patch_data[to_use].get(); } } - if (source_patch_value == NULL) { + if (source_patch_value == nullptr) { source_file.data.clear(); printf("source file is bad; trying copy\n"); @@ -587,10 +587,10 @@ int applypatch(const char* source_filename, int to_use = FindMatchingPatch(copy_file.sha1, patch_sha1_str); if (to_use >= 0) { - copy_patch_value = patch_data[to_use]; + copy_patch_value = patch_data[to_use].get(); } - if (copy_patch_value == NULL) { + if (copy_patch_value == nullptr) { // fail. printf("copy file doesn't match source SHA-1s either\n"); return 1; diff --git a/applypatch/applypatch_main.cpp b/applypatch/applypatch_main.cpp new file mode 100644 index 000000000..197077c93 --- /dev/null +++ b/applypatch/applypatch_main.cpp @@ -0,0 +1,28 @@ +/* + * 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" + +// This program (applypatch) applies binary patches to files in a way that +// is safe (the original file is not touched until we have the desired +// replacement for it) and idempotent (it's okay to run this program +// multiple times). +// +// See the comments to applypatch_modes() function. + +int main(int argc, char** argv) { + return applypatch_modes(argc, const_cast<const char**>(argv)); +} diff --git a/applypatch/main.cpp b/applypatch/applypatch_modes.cpp index 294f7ee21..7b191a801 100644 --- a/applypatch/main.cpp +++ b/applypatch/applypatch_modes.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "applypatch_modes.h" + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -23,11 +25,14 @@ #include <string> #include <vector> +#include <android-base/parseint.h> +#include <android-base/strings.h> +#include <openssl/sha.h> + #include "applypatch/applypatch.h" #include "edify/expr.h" -#include "openssl/sha.h" -static int CheckMode(int argc, char** argv) { +static int CheckMode(int argc, const char** argv) { if (argc < 3) { return 2; } @@ -39,44 +44,42 @@ static int CheckMode(int argc, char** argv) { return applypatch_check(argv[2], sha1); } -static int SpaceMode(int argc, char** argv) { +static int SpaceMode(int argc, const char** argv) { if (argc != 3) { return 2; } - char* endptr; - size_t bytes = strtol(argv[2], &endptr, 10); - if (bytes == 0 && endptr == argv[2]) { + + size_t bytes; + if (!android::base::ParseUint(argv[2], &bytes) || bytes == 0) { printf("can't parse \"%s\" as byte count\n\n", argv[2]); return 1; } return CacheSizeCheck(bytes); } -// Parse arguments (which should be of the form "<sha1>:<filename>" -// into the new parallel arrays *sha1s and *files.Returns true on -// success. -static bool ParsePatchArgs(int argc, char** argv, std::vector<std::string>* sha1s, +// Parse arguments (which should be of the form "<sha1>:<filename>" into the +// new parallel arrays *sha1s and *files. Returns true on success. +static bool ParsePatchArgs(int argc, const char** argv, std::vector<std::string>* sha1s, std::vector<FileContents>* files) { if (sha1s == nullptr) { return false; } for (int i = 0; i < argc; ++i) { - uint8_t digest[SHA_DIGEST_LENGTH]; - char* colon = strchr(argv[i], ':'); - if (colon == nullptr) { - printf("no ':' in patch argument \"%s\"\n", argv[i]); + std::vector<std::string> pieces = android::base::Split(argv[i], ":"); + if (pieces.size() != 2) { + printf("failed to parse patch argument \"%s\"\n", argv[i]); return false; } - *colon = '\0'; - ++colon; - if (ParseSha1(argv[i], digest) != 0) { + + uint8_t digest[SHA_DIGEST_LENGTH]; + if (ParseSha1(pieces[0].c_str(), digest) != 0) { printf("failed to parse sha1 \"%s\"\n", argv[i]); return false; } - sha1s->push_back(argv[i]); + sha1s->push_back(pieces[0]); FileContents fc; - if (LoadFileContents(colon, &fc) != 0) { + if (LoadFileContents(pieces[1].c_str(), &fc) != 0) { return false; } files->push_back(std::move(fc)); @@ -89,7 +92,7 @@ static int FlashMode(const char* src_filename, const char* tgt_filename, return applypatch_flash(src_filename, tgt_filename, tgt_sha1, tgt_size); } -static int PatchMode(int argc, char** argv) { +static int PatchMode(int argc, const char** argv) { FileContents bonusFc; Value bonus(VAL_INVALID, ""); @@ -108,9 +111,8 @@ static int PatchMode(int argc, char** argv) { return 2; } - char* endptr; - size_t target_size = strtol(argv[4], &endptr, 10); - if (target_size == 0 && endptr == argv[4]) { + size_t target_size; + if (!android::base::ParseUint(argv[4], &target_size) || target_size == 0) { printf("can't parse \"%s\" as byte count\n\n", argv[4]); return 1; } @@ -123,25 +125,24 @@ static int PatchMode(int argc, char** argv) { } return FlashMode(argv[1], argv[2], argv[3], target_size); } + std::vector<std::string> sha1s; std::vector<FileContents> files; if (!ParsePatchArgs(argc-5, argv+5, &sha1s, &files)) { printf("failed to parse patch args\n"); return 1; } - std::vector<Value> patches; - std::vector<Value*> patch_ptrs; + + std::vector<std::unique_ptr<Value>> patches; for (size_t i = 0; i < files.size(); ++i) { - patches.push_back(Value(VAL_BLOB, - std::string(files[i].data.cbegin(), files[i].data.cend()))); - patch_ptrs.push_back(&patches[i]); + patches.push_back(std::make_unique<Value>( + VAL_BLOB, std::string(files[i].data.cbegin(), files[i].data.cend()))); } - return applypatch(argv[1], argv[2], argv[3], target_size, - sha1s, patch_ptrs.data(), &bonus); + return applypatch(argv[1], argv[2], argv[3], target_size, sha1s, patches, &bonus); } -// This program applies binary patches to files in a way that is safe -// (the original file is not touched until we have the desired +// This program (applypatch) applies binary patches to files in a way that +// is safe (the original file is not touched until we have the desired // replacement for it) and idempotent (it's okay to run this program // multiple times). // @@ -167,7 +168,7 @@ static int PatchMode(int argc, char** argv) { // to read the source data. See the comments for the // LoadPartitionContents() function for the format of such a filename. -int main(int argc, char** argv) { +int applypatch_modes(int argc, const char** argv) { if (argc < 2) { usage: printf( diff --git a/applypatch/applypatch_modes.h b/applypatch/applypatch_modes.h new file mode 100644 index 000000000..3d9d08df5 --- /dev/null +++ b/applypatch/applypatch_modes.h @@ -0,0 +1,22 @@ +/* + * 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, const char** argv); + +#endif // _APPLYPATCH_MODES_H diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h index 80d681638..ca3dafbc9 100644 --- a/applypatch/include/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -20,10 +20,12 @@ #include <stdint.h> #include <sys/stat.h> +#include <memory> #include <string> #include <vector> -#include "openssl/sha.h" +#include <openssl/sha.h> + #include "edify/expr.h" struct FileContents { @@ -41,27 +43,26 @@ struct FileContents { typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*); -// applypatch.c +// applypatch.cpp int ShowLicenses(); size_t FreeSpaceForFile(const char* filename); int CacheSizeCheck(size_t bytes); int ParseSha1(const char* str, uint8_t* digest); -int applypatch_flash(const char* source_filename, const char* target_filename, - const char* target_sha1_str, size_t target_size); int applypatch(const char* source_filename, const char* target_filename, const char* target_sha1_str, size_t target_size, const std::vector<std::string>& patch_sha1_str, - Value** patch_data, - Value* bonus_data); + const std::vector<std::unique_ptr<Value>>& patch_data, + const Value* bonus_data); int applypatch_check(const char* filename, const std::vector<std::string>& patch_sha1_str); +int applypatch_flash(const char* source_filename, const char* target_filename, + const char* target_sha1_str, size_t target_size); int LoadFileContents(const char* filename, FileContents* file); int SaveFileContents(const char* filename, const FileContents* file); -void FreeFileContents(FileContents* file); int FindMatchingPatch(uint8_t* sha1, const std::vector<std::string>& patch_sha1_str); // bsdiff.cpp @@ -17,15 +17,22 @@ #ifndef RECOVERY_COMMON_H #define RECOVERY_COMMON_H -#include <stdbool.h> #include <stdio.h> #include <stdarg.h> #define STRINGIFY(x) #x #define EXPAND(x) STRINGIFY(x) +class RecoveryUI; + +extern RecoveryUI* ui; extern bool modified_flash; -typedef struct fstab_rec Volume; + +// The current stage, e.g. "1/2". +extern const char* stage; + +// The reason argument provided in "--reason=". +extern const char* reason; // fopen a file, mounting volumes and making parent dirs as necessary. FILE* fopen_path(const char *path, const char *mode); diff --git a/edify/expr.cpp b/edify/expr.cpp index ec2409752..329cf3acd 100644 --- a/edify/expr.cpp +++ b/edify/expr.cpp @@ -254,31 +254,25 @@ Value* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) { return nullptr; } - char* left; - char* right; - if (ReadArgs(state, argv, 2, &left, &right) < 0) return nullptr; - - bool result = false; - char* end; + std::vector<std::string> args; + if (!ReadArgs(state, 2, argv, &args)) { + return nullptr; + } // Parse up to at least long long or 64-bit integers. - int64_t l_int = static_cast<int64_t>(strtoll(left, &end, 10)); - if (left[0] == '\0' || *end != '\0') { - goto done; + 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; - r_int = static_cast<int64_t>(strtoll(right, &end, 10)); - if (right[0] == '\0' || *end != '\0') { - goto done; + if (!android::base::ParseInt(args[1].c_str(), &r_int)) { + state->errmsg = "failed to parse int in " + args[1]; + return nullptr; } - result = l_int < r_int; - - done: - free(left); - free(right); - return StringValue(result ? "t" : ""); + return StringValue(l_int < r_int ? "t" : ""); } Value* GreaterThanIntFn(const char* name, State* state, @@ -372,99 +366,6 @@ bool ReadValueArgs(State* state, int argc, Expr* argv[], return true; } -// Evaluate the expressions in argv, giving 'count' char* (the ... is -// zero or more char** to put them in). If any expression evaluates -// to NULL, free the rest and return -1. Return 0 on success. -int ReadArgs(State* state, Expr* argv[], int count, ...) { - char** args = reinterpret_cast<char**>(malloc(count * sizeof(char*))); - va_list v; - va_start(v, count); - int i; - for (i = 0; i < count; ++i) { - std::string str; - if (!Evaluate(state, argv[i], &str) || - (args[i] = strdup(str.c_str())) == nullptr) { - va_end(v); - int j; - for (j = 0; j < i; ++j) { - free(args[j]); - } - free(args); - return -1; - } - *(va_arg(v, char**)) = args[i]; - } - va_end(v); - free(args); - return 0; -} - -// Evaluate the expressions in argv, giving 'count' Value* (the ... is -// zero or more Value** to put them in). If any expression evaluates -// to NULL, free the rest and return -1. Return 0 on success. -int ReadValueArgs(State* state, Expr* argv[], int count, ...) { - Value** args = new Value*[count]; - va_list v; - va_start(v, count); - for (int i = 0; i < count; ++i) { - args[i] = EvaluateValue(state, argv[i]); - if (args[i] == NULL) { - va_end(v); - int j; - for (j = 0; j < i; ++j) { - delete args[j]; - } - delete[] args; - return -1; - } - *(va_arg(v, Value**)) = args[i]; - } - va_end(v); - delete[] args; - return 0; -} - -// Evaluate the expressions in argv, returning an array of char* -// results. If any evaluate to NULL, free the rest and return NULL. -// The caller is responsible for freeing the returned array and the -// strings it contains. -char** ReadVarArgs(State* state, int argc, Expr* argv[]) { - char** args = (char**)malloc(argc * sizeof(char*)); - for (int i = 0; i < argc; ++i) { - std::string str; - if (!Evaluate(state, argv[i], &str) || - (args[i] = strdup(str.c_str())) == nullptr) { - for (int j = 0; j < i; ++j) { - free(args[j]); - } - free(args); - return NULL; - } - } - return args; -} - -// Evaluate the expressions in argv, returning an array of Value* -// results. If any evaluate to NULL, free the rest and return NULL. -// The caller is responsible for freeing the returned array and the -// Values it contains. -Value** ReadValueVarArgs(State* state, int argc, Expr* argv[]) { - Value** args = new Value*[argc]; - int i = 0; - for (i = 0; i < argc; ++i) { - args[i] = EvaluateValue(state, argv[i]); - if (args[i] == NULL) { - int j; - for (j = 0; j < i; ++j) { - delete args[j]; - } - delete[] args; - return NULL; - } - } - return args; -} - // Use printf-style arguments to compose an error message to put into // *state. Returns nullptr. Value* ErrorAbort(State* state, const char* format, ...) { diff --git a/edify/expr.h b/edify/expr.h index 85306542d..911adbc82 100644 --- a/edify/expr.h +++ b/edify/expr.h @@ -128,30 +128,7 @@ bool ReadArgs(State* state, int argc, Expr* argv[], std::vector<std::string>* ar // Evaluate the expressions in argv, and put the results of Value* in // args. If any expression evaluate to nullptr, free the rest and return // false. Return true on success. -bool ReadValueArgs(State* state, int argc, Expr* argv[], - std::vector<std::unique_ptr<Value>>* args); - -// Evaluate the expressions in argv, giving 'count' char* (the ... is -// zero or more char** to put them in). If any expression evaluates -// to NULL, free the rest and return -1. Return 0 on success. -int ReadArgs(State* state, Expr* argv[], int count, ...); - -// Evaluate the expressions in argv, giving 'count' Value* (the ... is -// zero or more Value** to put them in). If any expression evaluates -// to NULL, free the rest and return -1. Return 0 on success. -int ReadValueArgs(State* state, Expr* argv[], int count, ...); - -// Evaluate the expressions in argv, returning an array of char* -// results. If any evaluate to NULL, free the rest and return NULL. -// The caller is responsible for freeing the returned array and the -// strings it contains. -char** ReadVarArgs(State* state, int argc, Expr* argv[]); - -// Evaluate the expressions in argv, returning an array of Value* -// results. If any evaluate to NULL, free the rest and return NULL. -// The caller is responsible for freeing the returned array and the -// Values it contains. -Value** ReadValueVarArgs(State* state, int argc, Expr* argv[]); +bool ReadValueArgs(State* state, int argc, Expr* argv[], std::vector<std::unique_ptr<Value>>* args); // Use printf-style arguments to compose an error message to put into // *state. Returns NULL. diff --git a/install.cpp b/install.cpp index 919f241a2..f124a2688 100644 --- a/install.cpp +++ b/install.cpp @@ -47,8 +47,6 @@ #include "ui.h" #include "verifier.h" -extern RecoveryUI* ui; - #define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt"; static constexpr const char* AB_OTA_PAYLOAD = "payload.bin"; @@ -20,10 +20,9 @@ #include <string> #include <ziparchive/zip_archive.h> -#include "common.h" - enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE, INSTALL_SKIPPED, INSTALL_RETRY }; + // Install the package specified by root_path. If INSTALL_SUCCESS is // returned and *wipe_cache is true on exit, caller should wipe the // cache partition. diff --git a/otautil/SysUtil.cpp b/otautil/SysUtil.cpp index efeb91c88..5156514f0 100644 --- a/otautil/SysUtil.cpp +++ b/otautil/SysUtil.cpp @@ -1,13 +1,25 @@ /* * Copyright 2006 The Android Open Source Project * - * System utilities. + * 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 "SysUtil.h" + #include <assert.h> #include <errno.h> #include <fcntl.h> #include <limits.h> -#include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> @@ -17,197 +29,190 @@ #include <sys/types.h> #include <unistd.h> -#include <android-base/logging.h> +#include <algorithm> +#include <string> +#include <vector> -#include "SysUtil.h" +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/strings.h> +#include <android-base/unique_fd.h> static bool sysMapFD(int fd, MemMapping* pMap) { - assert(pMap != NULL); + CHECK(pMap != nullptr); - struct stat sb; - if (fstat(fd, &sb) == -1) { - PLOG(ERROR) << "fstat(" << fd << ") failed"; - return false; - } + struct stat sb; + if (fstat(fd, &sb) == -1) { + PLOG(ERROR) << "fstat(" << fd << ") failed"; + return false; + } - void* memPtr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (memPtr == MAP_FAILED) { - PLOG(ERROR) << "mmap(" << sb.st_size << ", R, PRIVATE, " << fd << ", 0) failed"; - return false; - } + void* memPtr = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (memPtr == MAP_FAILED) { + PLOG(ERROR) << "mmap(" << sb.st_size << ", R, PRIVATE, " << fd << ", 0) failed"; + return false; + } - pMap->addr = reinterpret_cast<unsigned char*>(memPtr); - pMap->length = sb.st_size; - pMap->range_count = 1; - pMap->ranges = reinterpret_cast<MappedRange*>(malloc(sizeof(MappedRange))); - if (pMap->ranges == NULL) { - PLOG(ERROR) << "malloc failed"; - munmap(memPtr, sb.st_size); - return false; - } - pMap->ranges[0].addr = memPtr; - pMap->ranges[0].length = sb.st_size; + pMap->addr = static_cast<unsigned char*>(memPtr); + pMap->length = sb.st_size; + pMap->ranges.push_back({ memPtr, static_cast<size_t>(sb.st_size) }); - return true; + return true; } -static int sysMapBlockFile(FILE* mapf, MemMapping* pMap) -{ - char block_dev[PATH_MAX+1]; - size_t size; - unsigned int blksize; - size_t blocks; - unsigned int range_count; - unsigned int i; - - if (fgets(block_dev, sizeof(block_dev), mapf) == NULL) { - PLOG(ERROR) << "failed to read block device from header"; - return -1; - } - for (i = 0; i < sizeof(block_dev); ++i) { - if (block_dev[i] == '\n') { - block_dev[i] = 0; - break; - } - } - - if (fscanf(mapf, "%zu %u\n%u\n", &size, &blksize, &range_count) != 3) { - LOG(ERROR) << "failed to parse block map header"; - return -1; - } - if (blksize != 0) { - blocks = ((size-1) / blksize) + 1; - } - if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0) { - LOG(ERROR) << "invalid data in block map file: size " << size << ", blksize " << blksize - << ", range_count " << range_count; - return -1; - } +// A "block map" which looks like this (from uncrypt/uncrypt.cpp): +// +// /dev/block/platform/msm_sdcc.1/by-name/userdata # block device +// 49652 4096 # file size in bytes, block size +// 3 # count of block ranges +// 1000 1008 # block range 0 +// 2100 2102 # ... block range 1 +// 30 33 # ... block range 2 +// +// Each block range represents a half-open interval; the line "30 33" +// reprents the blocks [30, 31, 32]. +static int sysMapBlockFile(const char* filename, MemMapping* pMap) { + CHECK(pMap != nullptr); + + std::string content; + if (!android::base::ReadFileToString(filename, &content)) { + PLOG(ERROR) << "Failed to read " << filename; + return -1; + } + + std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n"); + if (lines.size() < 4) { + LOG(ERROR) << "Block map file is too short: " << lines.size(); + return -1; + } + + size_t size; + unsigned int blksize; + if (sscanf(lines[1].c_str(), "%zu %u", &size, &blksize) != 2) { + LOG(ERROR) << "Failed to parse file size and block size: " << lines[1]; + return -1; + } + + size_t range_count; + if (sscanf(lines[2].c_str(), "%zu", &range_count) != 1) { + LOG(ERROR) << "Failed to parse block map header: " << lines[2]; + return -1; + } + + size_t blocks; + if (blksize != 0) { + blocks = ((size - 1) / blksize) + 1; + } + if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0 || + lines.size() != 3 + range_count) { + LOG(ERROR) << "Invalid data in block map file: size " << size << ", blksize " << blksize + << ", range_count " << range_count << ", lines " << lines.size(); + return -1; + } + + // Reserve enough contiguous address space for the whole file. + void* reserve = mmap64(nullptr, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (reserve == MAP_FAILED) { + PLOG(ERROR) << "failed to reserve address space"; + return -1; + } + + const std::string& block_dev = lines[0]; + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_dev.c_str(), O_RDONLY))); + if (fd == -1) { + PLOG(ERROR) << "failed to open block device " << block_dev; + munmap(reserve, blocks * blksize); + return -1; + } + + pMap->ranges.resize(range_count); + + unsigned char* next = static_cast<unsigned char*>(reserve); + size_t remaining_size = blocks * blksize; + bool success = true; + for (size_t i = 0; i < range_count; ++i) { + const std::string& line = lines[i + 3]; + + size_t start, end; + if (sscanf(line.c_str(), "%zu %zu\n", &start, &end) != 2) { + LOG(ERROR) << "failed to parse range " << i << " in block map: " << line; + success = false; + break; + } + size_t length = (end - start) * blksize; + if (end <= start || (end - start) > SIZE_MAX / blksize || length > remaining_size) { + LOG(ERROR) << "unexpected range in block map: " << start << " " << end; + success = false; + break; + } + + void* addr = mmap64(next, length, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, + static_cast<off64_t>(start) * blksize); + if (addr == MAP_FAILED) { + PLOG(ERROR) << "failed to map block " << i; + success = false; + break; + } + pMap->ranges[i].addr = addr; + pMap->ranges[i].length = length; + + next += length; + remaining_size -= length; + } + if (success && remaining_size != 0) { + LOG(ERROR) << "ranges in block map are invalid: remaining_size = " << remaining_size; + success = false; + } + if (!success) { + munmap(reserve, blocks * blksize); + return -1; + } + + pMap->addr = static_cast<unsigned char*>(reserve); + pMap->length = size; + + LOG(INFO) << "mmapped " << range_count << " ranges"; + + return 0; +} - pMap->range_count = range_count; - pMap->ranges = reinterpret_cast<MappedRange*>(calloc(range_count, sizeof(MappedRange))); - if (pMap->ranges == NULL) { - PLOG(ERROR) << "calloc(" << range_count << ", " << sizeof(MappedRange) << ") failed"; - return -1; - } +int sysMapFile(const char* fn, MemMapping* pMap) { + if (fn == nullptr || pMap == nullptr) { + LOG(ERROR) << "Invalid argument(s)"; + return -1; + } - // Reserve enough contiguous address space for the whole file. - unsigned char* reserve = reinterpret_cast<unsigned char*>(mmap64(NULL, blocks * blksize, - PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0)); - if (reserve == MAP_FAILED) { - PLOG(ERROR) << "failed to reserve address space"; - free(pMap->ranges); - return -1; - } + *pMap = {}; - int fd = open(block_dev, O_RDONLY); - if (fd < 0) { - PLOG(ERROR) << "failed to open block device " << block_dev; - munmap(reserve, blocks * blksize); - free(pMap->ranges); - return -1; + if (fn[0] == '@') { + if (sysMapBlockFile(fn + 1, pMap) != 0) { + LOG(ERROR) << "Map of '" << fn << "' failed"; + return -1; } - - unsigned char* next = reserve; - size_t remaining_size = blocks * blksize; - bool success = true; - for (i = 0; i < range_count; ++i) { - size_t start, end; - if (fscanf(mapf, "%zu %zu\n", &start, &end) != 2) { - LOG(ERROR) << "failed to parse range " << i << " in block map"; - success = false; - break; - } - size_t length = (end - start) * blksize; - if (end <= start || ((end - start) > SIZE_MAX / blksize) || length > remaining_size) { - LOG(ERROR) << "unexpected range in block map: " << start << " " << end; - success = false; - break; - } - - void* addr = mmap64(next, length, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)(start*blksize))); - if (addr == MAP_FAILED) { - PLOG(ERROR) << "failed to map block " << i; - success = false; - break; - } - pMap->ranges[i].addr = addr; - pMap->ranges[i].length = length; - - next += length; - remaining_size -= length; + } else { + // This is a regular file. + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(fn, O_RDONLY))); + if (fd == -1) { + PLOG(ERROR) << "Unable to open '" << fn << "'"; + return -1; } - if (success && remaining_size != 0) { - LOG(ERROR) << "ranges in block map are invalid: remaining_size = " << remaining_size; - success = false; - } - if (!success) { - close(fd); - munmap(reserve, blocks * blksize); - free(pMap->ranges); - return -1; - } - - close(fd); - pMap->addr = reserve; - pMap->length = size; - - LOG(INFO) << "mmapped " << range_count << " ranges"; - - return 0; -} -int sysMapFile(const char* fn, MemMapping* pMap) -{ - memset(pMap, 0, sizeof(*pMap)); - - if (fn && fn[0] == '@') { - // A map of blocks - FILE* mapf = fopen(fn+1, "r"); - if (mapf == NULL) { - PLOG(ERROR) << "Unable to open '" << (fn+1) << "'"; - return -1; - } - - if (sysMapBlockFile(mapf, pMap) != 0) { - LOG(ERROR) << "Map of '" << fn << "' failed"; - fclose(mapf); - return -1; - } - - fclose(mapf); - } else { - // This is a regular file. - int fd = open(fn, O_RDONLY); - if (fd == -1) { - PLOG(ERROR) << "Unable to open '" << fn << "'"; - return -1; - } - - if (!sysMapFD(fd, pMap)) { - LOG(ERROR) << "Map of '" << fn << "' failed"; - close(fd); - return -1; - } - - close(fd); + if (!sysMapFD(fd, pMap)) { + LOG(ERROR) << "Map of '" << fn << "' failed"; + return -1; } - return 0; + } + return 0; } /* * Release a memory mapping. */ -void sysReleaseMap(MemMapping* pMap) -{ - int i; - for (i = 0; i < pMap->range_count; ++i) { - if (munmap(pMap->ranges[i].addr, pMap->ranges[i].length) < 0) { - PLOG(ERROR) << "munmap(" << pMap->ranges[i].addr << ", " << pMap->ranges[i].length - << ") failed"; - } +void sysReleaseMap(MemMapping* pMap) { + std::for_each(pMap->ranges.cbegin(), pMap->ranges.cend(), [](const MappedRange& range) { + if (munmap(range.addr, range.length) == -1) { + PLOG(ERROR) << "munmap(" << range.addr << ", " << range.length << ") failed"; } - free(pMap->ranges); - pMap->ranges = NULL; - pMap->range_count = 0; + }); + pMap->ranges.clear(); } diff --git a/otautil/SysUtil.h b/otautil/SysUtil.h index 7adff1e54..6a79bf31f 100644 --- a/otautil/SysUtil.h +++ b/otautil/SysUtil.h @@ -1,33 +1,40 @@ /* * Copyright 2006 The Android Open Source Project * - * System utilities. + * 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 _MINZIP_SYSUTIL -#define _MINZIP_SYSUTIL -#include <stdio.h> +#ifndef _OTAUTIL_SYSUTIL +#define _OTAUTIL_SYSUTIL + #include <sys/types.h> -#ifdef __cplusplus -extern "C" { -#endif +#include <vector> -typedef struct MappedRange { - void* addr; - size_t length; -} MappedRange; +struct MappedRange { + void* addr; + size_t length; +}; /* * Use this to keep track of mapped segments. */ -typedef struct MemMapping { - unsigned char* addr; /* start of data */ - size_t length; /* length of data */ +struct MemMapping { + unsigned char* addr; /* start of data */ + size_t length; /* length of data */ - int range_count; - MappedRange* ranges; -} MemMapping; + std::vector<MappedRange> ranges; +}; /* * Map a file into a private, read-only memory segment. If 'fn' @@ -45,8 +52,4 @@ int sysMapFile(const char* fn, MemMapping* pMap); */ void sysReleaseMap(MemMapping* pMap); -#ifdef __cplusplus -} -#endif - -#endif /*_MINZIP_SYSUTIL*/ +#endif // _OTAUTIL_SYSUTIL diff --git a/print_sha1.h b/print_sha1.h index c7c1f3651..1f8589519 100644 --- a/print_sha1.h +++ b/print_sha1.h @@ -20,7 +20,7 @@ #include <stdint.h> #include <string> -#include "openssl/sha.h" +#include <openssl/sha.h> static std::string print_sha1(const uint8_t* sha1, size_t len) { const char* hex = "0123456789abcdef"; @@ -41,7 +41,7 @@ static std::string short_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) { } static std::string print_hex(const uint8_t* bytes, size_t len) { - return print_sha1(bytes, len); + return print_sha1(bytes, len); } #endif // RECOVERY_PRINT_SHA1_H diff --git a/recovery-persist.cpp b/recovery-persist.cpp index b0ec141cb..d706ccac8 100644 --- a/recovery-persist.cpp +++ b/recovery-persist.cpp @@ -30,7 +30,6 @@ // --force-persist ignore /cache mount, always rotate in the contents. // -#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -40,17 +39,16 @@ #include <android-base/file.h> #include <android-base/logging.h> - #include <private/android_logger.h> /* private pmsg functions */ +#include "rotate_logs.h" + static const char *LAST_LOG_FILE = "/data/misc/recovery/last_log"; static const char *LAST_PMSG_FILE = "/sys/fs/pstore/pmsg-ramoops-0"; static const char *LAST_KMSG_FILE = "/data/misc/recovery/last_kmsg"; static const char *LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops-0"; static const char *ALT_LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops"; -static const int KEEP_LOG_COUNT = 10; - // close a file, log an error if the error indicator is set static void check_and_fclose(FILE *fp, const char *name) { fflush(fp); @@ -80,39 +78,6 @@ static void copy_file(const char* source, const char* destination) { static bool rotated = false; -// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max. -// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max. -// Overwrite any existing last_log.$max and last_kmsg.$max. -static void rotate_logs(int max) { - // Logs should only be rotated once. - - if (rotated) { - return; - } - rotated = true; - - for (int i = max-1; i >= 0; --i) { - std::string old_log(LAST_LOG_FILE); - if (i > 0) { - old_log += "." + std::to_string(i); - } - std::string new_log(LAST_LOG_FILE); - new_log += "." + std::to_string(i+1); - - // Ignore errors if old_log doesn't exist. - rename(old_log.c_str(), new_log.c_str()); - - std::string old_kmsg(LAST_KMSG_FILE); - if (i > 0) { - old_kmsg += "." + std::to_string(i); - } - std::string new_kmsg(LAST_KMSG_FILE); - new_kmsg += "." + std::to_string(i+1); - - rename(old_kmsg.c_str(), new_kmsg.c_str()); - } -} - ssize_t logsave( log_id_t /* logId */, char /* prio */, @@ -138,7 +103,8 @@ ssize_t logsave( // already-rotated files? Algorithm thus far is KISS: one file, // one rotation allowed. - rotate_logs(KEEP_LOG_COUNT); + rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE); + rotated = true; return android::base::WriteStringToFile(buffer, destination.c_str()); } diff --git a/recovery-refresh.cpp b/recovery-refresh.cpp index b2ab52f7e..14565d3f4 100644 --- a/recovery-refresh.cpp +++ b/recovery-refresh.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "recovery-refresh" - // // Strictly to deal with reboot into system after OTA, then // reboot while in system before boot complete landing us back @@ -40,64 +38,11 @@ // #include <string.h> - #include <string> -#include <android/log.h> /* Android Log Priority Tags */ #include <private/android_logger.h> /* private pmsg functions */ -static const char LAST_KMSG_FILE[] = "recovery/last_kmsg"; -static const char LAST_LOG_FILE[] = "recovery/last_log"; - -static ssize_t logbasename( - log_id_t /* logId */, - char /* prio */, - const char *filename, - const char * /* buf */, size_t len, - void *arg) { - if (strstr(LAST_KMSG_FILE, filename) || - strstr(LAST_LOG_FILE, filename)) { - bool *doRotate = reinterpret_cast<bool *>(arg); - *doRotate = true; - } - return len; -} - -static ssize_t logrotate( - log_id_t logId, - char prio, - const char *filename, - const char *buf, size_t len, - void *arg) { - bool *doRotate = reinterpret_cast<bool *>(arg); - if (!*doRotate) { - return __android_log_pmsg_file_write(logId, prio, filename, buf, len); - } - - std::string name(filename); - size_t dot = name.find_last_of('.'); - std::string sub = name.substr(0, dot); - - if (!strstr(LAST_KMSG_FILE, sub.c_str()) && - !strstr(LAST_LOG_FILE, sub.c_str())) { - return __android_log_pmsg_file_write(logId, prio, filename, buf, len); - } - - // filename rotation - if (dot == std::string::npos) { - name += ".1"; - } else { - std::string number = name.substr(dot + 1); - if (!isdigit(number.data()[0])) { - name += ".1"; - } else { - auto i = std::stoull(number); - name = sub + "." + std::to_string(i + 1); - } - } - - return __android_log_pmsg_file_write(logId, prio, name.c_str(), buf, len); -} +#include "rotate_logs.h" int main(int argc, char **argv) { static const char filter[] = "recovery/"; @@ -105,7 +50,6 @@ int main(int argc, char **argv) { static const char rotate_flag[] = "--rotate"; ssize_t ret; bool doRotate = false; - // Take last pmsg contents and rewrite it to the current pmsg session. if ((argc <= 1) || !argv[1] || (((doRotate = strcmp(argv[1], rotate_flag))) && diff --git a/recovery.cpp b/recovery.cpp index 4d1ad1df2..02d460eb9 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -66,10 +66,9 @@ #include "minui/minui.h" #include "otautil/DirUtil.h" #include "roots.h" -#include "ui.h" +#include "rotate_logs.h" #include "screen_ui.h" - -struct selabel_handle *sehandle; +#include "ui.h" static const struct option OPTIONS[] = { { "update_package", required_argument, NULL, 'u' }, @@ -110,7 +109,6 @@ static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg"; static const char *LAST_LOG_FILE = "/cache/recovery/last_log"; -static const int KEEP_LOG_COUNT = 10; // We will try to apply the update package 5 times at most in case of an I/O error. static const int EIO_RETRY_COUNT = 4; static const int BATTERY_READ_TIMEOUT_IN_SEC = 10; @@ -119,15 +117,18 @@ static const int BATTERY_READ_TIMEOUT_IN_SEC = 10; // So we should check battery with a slightly lower limitation. static const int BATTERY_OK_PERCENTAGE = 20; static const int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15; -constexpr const char* RECOVERY_WIPE = "/etc/recovery.wipe"; +static constexpr const char* RECOVERY_WIPE = "/etc/recovery.wipe"; +static constexpr const char* DEFAULT_LOCALE = "en_US"; -RecoveryUI* ui = NULL; -static const char* locale = "en_US"; -char* stage = NULL; -char* reason = NULL; -bool modified_flash = false; +static std::string locale; static bool has_cache = false; +RecoveryUI* ui = nullptr; +bool modified_flash = false; +const char* stage = nullptr; +const char* reason = nullptr; +struct selabel_handle* sehandle; + /* * The recovery tool communicates with the main system through /cache files. * /cache/recovery/command - INPUT - command line for tool, one arg per line @@ -206,6 +207,9 @@ FILE* fopen_path(const char *path, const char *mode) { // close a file, log an error if the error indicator is set static void check_and_fclose(FILE *fp, const char *name) { fflush(fp); + if (fsync(fileno(fp)) == -1) { + PLOG(ERROR) << "Failed to fsync " << name; + } if (ferror(fp)) { PLOG(ERROR) << "Error in " << name; } @@ -447,37 +451,6 @@ static void copy_log_file(const char* source, const char* destination, bool appe } } -// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max. -// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max. -// Overwrite any existing last_log.$max and last_kmsg.$max. -static void rotate_logs(int max) { - // Logs should only be rotated once. - static bool rotated = false; - if (rotated) { - return; - } - rotated = true; - ensure_path_mounted(LAST_LOG_FILE); - ensure_path_mounted(LAST_KMSG_FILE); - - for (int i = max-1; i >= 0; --i) { - std::string old_log = android::base::StringPrintf("%s", LAST_LOG_FILE); - if (i > 0) { - old_log += "." + std::to_string(i); - } - std::string new_log = android::base::StringPrintf("%s.%d", LAST_LOG_FILE, i+1); - // Ignore errors if old_log doesn't exist. - rename(old_log.c_str(), new_log.c_str()); - - std::string old_kmsg = android::base::StringPrintf("%s", LAST_KMSG_FILE); - if (i > 0) { - old_kmsg += "." + std::to_string(i); - } - std::string new_kmsg = android::base::StringPrintf("%s.%d", LAST_KMSG_FILE, i+1); - rename(old_kmsg.c_str(), new_kmsg.c_str()); - } -} - static void copy_logs() { // We only rotate and record the log of the current session if there are // actual attempts to modify the flash, such as wipes, installs from BCB @@ -496,7 +469,9 @@ static void copy_logs() { return; } - rotate_logs(KEEP_LOG_COUNT); + ensure_path_mounted(LAST_LOG_FILE); + ensure_path_mounted(LAST_KMSG_FILE); + rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE); // Copy logs to cache so the system can find out what happened. copy_log_file(TEMPORARY_LOG_FILE, LOG_FILE, true); @@ -515,24 +490,18 @@ static void copy_logs() { // clear the recovery command and prepare to boot a (hopefully working) system, // copy our log file to cache as well (for the system to read). This function is // idempotent: call it as many times as you like. -static void -finish_recovery() { +static void finish_recovery() { // Save the locale to cache, so if recovery is next started up // without a --locale argument (eg, directly from the bootloader) // it will use the last-known locale. - if (locale != NULL) { - size_t len = strlen(locale); - __pmsg_write(LOCALE_FILE, locale, len); - if (has_cache) { - LOG(INFO) << "Saving locale \"" << locale << "\""; - FILE* fp = fopen_path(LOCALE_FILE, "w"); - if (fp != NULL) { - fwrite(locale, 1, len, fp); - fflush(fp); - fsync(fileno(fp)); - check_and_fclose(fp, LOCALE_FILE); - } + if (!locale.empty() && has_cache) { + LOG(INFO) << "Saving locale \"" << locale << "\""; + + FILE* fp = fopen_path(LOCALE_FILE, "w"); + if (!android::base::WriteStringToFd(locale, fileno(fp))) { + PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE; } + check_and_fclose(fp, LOCALE_FILE); } copy_logs(); @@ -1282,40 +1251,32 @@ print_property(const char *key, const char *name, void *cookie) { printf("%s=%s\n", key, name); } -static void -load_locale_from_cache() { - FILE* fp = fopen_path(LOCALE_FILE, "r"); - char buffer[80]; - if (fp != NULL) { - fgets(buffer, sizeof(buffer), fp); - int j = 0; - unsigned int i; - for (i = 0; i < sizeof(buffer) && buffer[i]; ++i) { - if (!isspace(buffer[i])) { - buffer[j++] = buffer[i]; - } - } - buffer[j] = 0; - locale = strdup(buffer); - check_and_fclose(fp, LOCALE_FILE); +static std::string load_locale_from_cache() { + if (ensure_path_mounted(LOCALE_FILE) != 0) { + LOG(ERROR) << "Can't mount " << LOCALE_FILE; + return ""; } -} -static RecoveryUI* gCurrentUI = NULL; + std::string content; + if (!android::base::ReadFileToString(LOCALE_FILE, &content)) { + PLOG(ERROR) << "Can't read " << LOCALE_FILE; + return ""; + } -void -ui_print(const char* format, ...) { - char buffer[256]; + return android::base::Trim(content); +} +void ui_print(const char* format, ...) { + std::string buffer; va_list ap; va_start(ap, format); - vsnprintf(buffer, sizeof(buffer), format, ap); + android::base::StringAppendV(&buffer, format, ap); va_end(ap); - if (gCurrentUI != NULL) { - gCurrentUI->Print("%s", buffer); + if (ui != nullptr) { + ui->Print("%s", buffer.c_str()); } else { - fputs(buffer, stdout); + fputs(buffer.c_str(), stdout); } } @@ -1324,8 +1285,8 @@ static constexpr char log_characters[] = "VDIWEF"; void UiLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag, const char* file, unsigned int line, const char* message) { - if (severity >= android::base::ERROR && gCurrentUI != NULL) { - gCurrentUI->Print("E:%s\n", message); + if (severity >= android::base::ERROR && ui != nullptr) { + ui->Print("E:%s\n", message); } else { fprintf(stdout, "%c:%s\n", log_characters[severity], message); } @@ -1421,63 +1382,13 @@ static void log_failure_code(ErrorCode code, const char *update_package) { }; std::string log_content = android::base::Join(log_buffer, "\n"); if (!android::base::WriteStringToFile(log_content, TEMPORARY_INSTALL_FILE)) { - PLOG(ERROR) << "failed to write " << TEMPORARY_INSTALL_FILE; + PLOG(ERROR) << "failed to write " << TEMPORARY_INSTALL_FILE; } // Also write the info into last_log. LOG(INFO) << log_content; } -static ssize_t logbasename( - log_id_t /* logId */, - char /* prio */, - const char *filename, - const char * /* buf */, size_t len, - void *arg) { - if (strstr(LAST_KMSG_FILE, filename) || - strstr(LAST_LOG_FILE, filename)) { - bool *doRotate = reinterpret_cast<bool *>(arg); - *doRotate = true; - } - return len; -} - -static ssize_t logrotate( - log_id_t logId, - char prio, - const char *filename, - const char *buf, size_t len, - void *arg) { - bool *doRotate = reinterpret_cast<bool *>(arg); - if (!*doRotate) { - return __android_log_pmsg_file_write(logId, prio, filename, buf, len); - } - - std::string name(filename); - size_t dot = name.find_last_of('.'); - std::string sub = name.substr(0, dot); - - if (!strstr(LAST_KMSG_FILE, sub.c_str()) && - !strstr(LAST_LOG_FILE, sub.c_str())) { - return __android_log_pmsg_file_write(logId, prio, filename, buf, len); - } - - // filename rotation - if (dot == std::string::npos) { - name += ".1"; - } else { - std::string number = name.substr(dot + 1); - if (!isdigit(number.data()[0])) { - name += ".1"; - } else { - auto i = std::stoull(number); - name = sub + "." + std::to_string(i + 1); - } - } - - return __android_log_pmsg_file_write(logId, prio, name.c_str(), buf, len); -} - int main(int argc, char **argv) { // We don't have logcat yet under recovery; so we'll print error on screen and // log to stdout (which is redirected to recovery.log) as we used to do. @@ -1487,6 +1398,7 @@ int main(int argc, char **argv) { static const char filter[] = "recovery/"; // Do we need to rotate? bool doRotate = false; + __android_log_pmsg_file_read( LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logbasename, &doRotate); @@ -1573,18 +1485,24 @@ int main(int argc, char **argv) { } } - if (locale == nullptr && has_cache) { - load_locale_from_cache(); + if (locale.empty()) { + if (has_cache) { + locale = load_locale_from_cache(); + } + + if (locale.empty()) { + locale = DEFAULT_LOCALE; + } } - printf("locale is [%s]\n", locale); + + printf("locale is [%s]\n", locale.c_str()); printf("stage is [%s]\n", stage); printf("reason is [%s]\n", reason); Device* device = make_device(); ui = device->GetUI(); - gCurrentUI = ui; - ui->SetLocale(locale); + ui->SetLocale(locale.c_str()); ui->Init(); // Set background string to "installing security update" for security update, // otherwise set it to "installing system update". @@ -1599,7 +1517,7 @@ int main(int argc, char **argv) { if (show_text) ui->ShowText(true); struct selinux_opt seopts[] = { - { SELABEL_OPT_PATH, "/file_contexts" } + { SELABEL_OPT_PATH, "/file_contexts" } }; sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); @@ -1771,7 +1689,7 @@ int main(int argc, char **argv) { break; } while (true) { - pause(); + pause(); } // Should be unreachable. return EXIT_SUCCESS; @@ -17,7 +17,7 @@ #ifndef RECOVERY_ROOTS_H_ #define RECOVERY_ROOTS_H_ -#include "common.h" +typedef struct fstab_rec Volume; // Load and parse volume data from /etc/recovery.fstab. void load_volume_table(); diff --git a/rotate_logs.cpp b/rotate_logs.cpp new file mode 100644 index 000000000..fc220215e --- /dev/null +++ b/rotate_logs.cpp @@ -0,0 +1,115 @@ +/* + * 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 "rotate_logs.h" + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> + +#include <string> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/parseint.h> +#include <android-base/stringprintf.h> +#include <private/android_logger.h> /* private pmsg functions */ + +static const std::string LAST_KMSG_FILTER = "recovery/last_kmsg"; +static const std::string LAST_LOG_FILTER = "recovery/last_log"; + +ssize_t logbasename( + log_id_t /* logId */, + char /* prio */, + const char *filename, + const char * /* buf */, size_t len, + void *arg) { + bool* doRotate = static_cast<bool*>(arg); + if (LAST_KMSG_FILTER.find(filename) != std::string::npos || + LAST_LOG_FILTER.find(filename) != std::string::npos) { + *doRotate = true; + } + return len; +} + +ssize_t logrotate( + log_id_t logId, + char prio, + const char *filename, + const char *buf, size_t len, + void *arg) { + bool* doRotate = static_cast<bool*>(arg); + if (!*doRotate) { + return __android_log_pmsg_file_write(logId, prio, filename, buf, len); + } + + std::string name(filename); + size_t dot = name.find_last_of('.'); + std::string sub = name.substr(0, dot); + + if (LAST_KMSG_FILTER.find(sub) == std::string::npos && + LAST_LOG_FILTER.find(sub) == std::string::npos) { + return __android_log_pmsg_file_write(logId, prio, filename, buf, len); + } + + // filename rotation + if (dot == std::string::npos) { + name += ".1"; + } else { + std::string number = name.substr(dot + 1); + if (!isdigit(number[0])) { + name += ".1"; + } else { + size_t i; + if (!android::base::ParseUint(number, &i)) { + LOG(ERROR) << "failed to parse uint in " << number; + return -1; + } + name = sub + "." + std::to_string(i + 1); + } + } + + return __android_log_pmsg_file_write(logId, prio, name.c_str(), buf, len); +} + +// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max. +// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max. +// Overwrite any existing last_log.$max and last_kmsg.$max. +void rotate_logs(const char* last_log_file, const char* last_kmsg_file) { + // Logs should only be rotated once. + static bool rotated = false; + if (rotated) { + return; + } + rotated = true; + + for (int i = KEEP_LOG_COUNT - 1; i >= 0; --i) { + std::string old_log = android::base::StringPrintf("%s", last_log_file); + if (i > 0) { + old_log += "." + std::to_string(i); + } + std::string new_log = android::base::StringPrintf("%s.%d", last_log_file, i+1); + // Ignore errors if old_log doesn't exist. + rename(old_log.c_str(), new_log.c_str()); + + std::string old_kmsg = android::base::StringPrintf("%s", last_kmsg_file); + if (i > 0) { + old_kmsg += "." + std::to_string(i); + } + std::string new_kmsg = android::base::StringPrintf("%s.%d", last_kmsg_file, i+1); + rename(old_kmsg.c_str(), new_kmsg.c_str()); + } +} diff --git a/rotate_logs.h b/rotate_logs.h new file mode 100644 index 000000000..809c213b6 --- /dev/null +++ b/rotate_logs.h @@ -0,0 +1,44 @@ +/* + * 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 _ROTATE_LOGS_H +#define _ROTATE_LOGS_H + +#include <string> + +#include <private/android_logger.h> /* private pmsg functions */ + +constexpr int KEEP_LOG_COUNT = 10; + +ssize_t logbasename(log_id_t /* logId */, + char /* prio */, + const char *filename, + const char * /* buf */, size_t len, + void *arg); + +ssize_t logrotate( + log_id_t logId, + char prio, + const char *filename, + const char *buf, size_t len, + void *arg); + +// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max. +// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max. +// Overwrite any existing last_log.$max and last_kmsg.$max. +void rotate_logs(const char* last_log_file, const char* last_kmsg_file); + +#endif //_ROTATE_LOG_H diff --git a/tests/Android.mk b/tests/Android.mk index 3d05386b0..e87a22964 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -18,7 +18,6 @@ LOCAL_PATH := $(call my-dir) # Unit tests include $(CLEAR_VARS) -LOCAL_CLANG := true LOCAL_CFLAGS := -Werror LOCAL_MODULE := recovery_unit_test LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk @@ -32,18 +31,31 @@ LOCAL_STATIC_LIBRARIES := \ libselinux \ libbase -LOCAL_SRC_FILES := unit/asn1_decoder_test.cpp -LOCAL_SRC_FILES += unit/recovery_test.cpp -LOCAL_SRC_FILES += unit/locale_test.cpp -LOCAL_SRC_FILES += unit/zip_test.cpp +LOCAL_SRC_FILES := \ + unit/asn1_decoder_test.cpp \ + unit/locale_test.cpp \ + unit/sysutil_test.cpp \ + unit/zip_test.cpp + LOCAL_C_INCLUDES := bootable/recovery LOCAL_SHARED_LIBRARIES := liblog include $(BUILD_NATIVE_TEST) -# Component tests +# Manual tests include $(CLEAR_VARS) LOCAL_CLANG := true -LOCAL_CFLAGS += -Wno-unused-parameter -Werror +LOCAL_CFLAGS := -Werror +LOCAL_MODULE := recovery_manual_test +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_STATIC_LIBRARIES := libbase + +LOCAL_SRC_FILES := manual/recovery_test.cpp +LOCAL_SHARED_LIBRARIES := liblog +include $(BUILD_NATIVE_TEST) + +# Component tests +include $(CLEAR_VARS) +LOCAL_CFLAGS := -Werror LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE := recovery_component_test LOCAL_C_INCLUDES := bootable/recovery @@ -63,6 +75,7 @@ tune2fs_static_libraries := \ libext2fs LOCAL_STATIC_LIBRARIES := \ + libapplypatch_modes \ libapplypatch \ libedify \ libotafault \ @@ -86,7 +99,8 @@ LOCAL_STATIC_LIBRARIES := \ testdata_files := $(call find-subdir-files, testdata/*) -testdata_out_path := $(TARGET_OUT_DATA_NATIVE_TESTS)/recovery +# The testdata files that will go to $OUT/data/nativetest/recovery. +testdata_out_path := $(TARGET_OUT_DATA)/nativetest/recovery GEN := $(addprefix $(testdata_out_path)/, $(testdata_files)) $(GEN): PRIVATE_PATH := $(LOCAL_PATH) $(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@ @@ -94,14 +108,16 @@ $(GEN): $(testdata_out_path)/% : $(LOCAL_PATH)/% $(transform-generated-source) LOCAL_GENERATED_SOURCES += $(GEN) -ifdef TARGET_2ND_ARCH -testdata_out_path_2nd_arch := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/recovery -GEN_2ND_ARCH := $(addprefix $(testdata_out_path_2nd_arch)/, $(testdata_files)) -$(GEN_2ND_ARCH): PRIVATE_PATH := $(LOCAL_PATH) -$(GEN_2ND_ARCH): PRIVATE_CUSTOM_TOOL = cp $< $@ -$(GEN_2ND_ARCH): $(testdata_out_path_2nd_arch)/% : $(LOCAL_PATH)/% +# A copy of the testdata to be packed into continuous_native_tests.zip. +testdata_continuous_zip_prefix := \ + $(call intermediates-dir-for,PACKAGING,recovery_component_test)/DATA +testdata_continuous_zip_path := $(testdata_continuous_zip_prefix)/nativetest/recovery +GEN := $(addprefix $(testdata_continuous_zip_path)/, $(testdata_files)) +$(GEN): PRIVATE_PATH := $(LOCAL_PATH) +$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@ +$(GEN): $(testdata_continuous_zip_path)/% : $(LOCAL_PATH)/% $(transform-generated-source) -LOCAL_GENERATED_SOURCES += $(GEN_2ND_ARCH) -endif # TARGET_2ND_ARCH +LOCAL_GENERATED_SOURCES += $(GEN) +LOCAL_PICKUP_FILES := $(testdata_continuous_zip_prefix) include $(BUILD_NATIVE_TEST) diff --git a/tests/common/test_constants.h b/tests/common/test_constants.h index 3490f6805..97e74a3c2 100644 --- a/tests/common/test_constants.h +++ b/tests/common/test_constants.h @@ -13,13 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef _OTA_TEST_CONSTANTS_H #define _OTA_TEST_CONSTANTS_H -#if defined(__LP64__) -#define NATIVE_TEST_PATH "/nativetest64" -#else -#define NATIVE_TEST_PATH "/nativetest" -#endif +#include <stdlib.h> + +static const char* data_root = getenv("ANDROID_DATA"); + +static std::string from_testdata_base(const std::string& fname) { + return std::string(data_root) + "/nativetest/recovery/testdata/" + fname; +} -#endif +#endif // _OTA_TEST_CONSTANTS_H diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp index 908a9f5f5..1a0b19113 100644 --- a/tests/component/applypatch_test.cpp +++ b/tests/component/applypatch_test.cpp @@ -23,167 +23,158 @@ #include <sys/types.h> #include <time.h> +#include <memory> #include <string> +#include <vector> #include <android-base/file.h> #include <android-base/stringprintf.h> #include <android-base/test_utils.h> +#include <openssl/sha.h> #include "applypatch/applypatch.h" +#include "applypatch/applypatch_modes.h" #include "common/test_constants.h" -#include "openssl/sha.h" #include "print_sha1.h" -static const std::string DATA_PATH = getenv("ANDROID_DATA"); -static const std::string TESTDATA_PATH = "/recovery/testdata"; -static const std::string WORK_FS = "/data"; +static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = nullptr) { + ASSERT_NE(nullptr, sha1); -static std::string sha1sum(const std::string& fname) { - uint8_t digest[SHA_DIGEST_LENGTH]; - std::string data; - android::base::ReadFileToString(fname, &data); + std::string data; + ASSERT_TRUE(android::base::ReadFileToString(fname, &data)); - SHA1((const uint8_t*)data.c_str(), data.size(), digest); - return print_sha1(digest); -} + if (fsize != nullptr) { + *fsize = data.size(); + } -static void mangle_file(const std::string& fname) { - FILE* fh = fopen(&fname[0], "w"); - int r; - for (int i=0; i < 1024; i++) { - r = rand(); - fwrite(&r, sizeof(short), 1, fh); - } - fclose(fh); + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), digest); + *sha1 = print_sha1(digest); } -static bool file_cmp(std::string& f1, std::string& f2) { - std::string c1; - std::string c2; - android::base::ReadFileToString(f1, &c1); - android::base::ReadFileToString(f2, &c2); - return c1 == c2; +static void mangle_file(const std::string& fname) { + std::string content; + content.reserve(1024); + for (size_t i = 0; i < 1024; i++) { + content[i] = rand() % 256; + } + ASSERT_TRUE(android::base::WriteStringToFile(content, fname)); } -static std::string from_testdata_base(const std::string& fname) { - return android::base::StringPrintf("%s%s%s/%s", - &DATA_PATH[0], - &NATIVE_TEST_PATH[0], - &TESTDATA_PATH[0], - &fname[0]); +static bool file_cmp(const std::string& f1, const std::string& f2) { + std::string c1; + android::base::ReadFileToString(f1, &c1); + std::string c2; + android::base::ReadFileToString(f2, &c2); + return c1 == c2; } class ApplyPatchTest : public ::testing::Test { - public: - static void SetUpTestCase() { - // set up files - old_file = from_testdata_base("old.file"); - new_file = from_testdata_base("new.file"); - patch_file = from_testdata_base("patch.bsdiff"); - rand_file = "/cache/applypatch_test_rand.file"; - cache_file = "/cache/saved.file"; - - // write stuff to rand_file - android::base::WriteStringToFile("hello", rand_file); - - // set up SHA constants - old_sha1 = sha1sum(old_file); - new_sha1 = sha1sum(new_file); - srand(time(NULL)); - bad_sha1_a = android::base::StringPrintf("%040x", rand()); - bad_sha1_b = android::base::StringPrintf("%040x", rand()); - - struct stat st; - stat(&new_file[0], &st); - new_size = st.st_size; - } - - static std::string old_file; - static std::string new_file; - static std::string rand_file; - static std::string cache_file; - static std::string patch_file; - - static std::string old_sha1; - static std::string new_sha1; - static std::string bad_sha1_a; - static std::string bad_sha1_b; - - static size_t new_size; + public: + static void SetUpTestCase() { + // set up files + old_file = from_testdata_base("old.file"); + new_file = from_testdata_base("new.file"); + patch_file = from_testdata_base("patch.bsdiff"); + rand_file = "/cache/applypatch_test_rand.file"; + cache_file = "/cache/saved.file"; + + // write stuff to rand_file + ASSERT_TRUE(android::base::WriteStringToFile("hello", rand_file)); + + // set up SHA constants + sha1sum(old_file, &old_sha1); + sha1sum(new_file, &new_sha1); + srand(time(nullptr)); + bad_sha1_a = android::base::StringPrintf("%040x", rand()); + bad_sha1_b = android::base::StringPrintf("%040x", rand()); + + struct stat st; + stat(&new_file[0], &st); + new_size = st.st_size; + } + + static std::string old_file; + static std::string new_file; + static std::string rand_file; + static std::string cache_file; + static std::string patch_file; + + static std::string old_sha1; + static std::string new_sha1; + static std::string bad_sha1_a; + static std::string bad_sha1_b; + + static size_t new_size; }; std::string ApplyPatchTest::old_file; std::string ApplyPatchTest::new_file; -static void cp(std::string src, std::string tgt) { - std::string cmd = android::base::StringPrintf("cp %s %s", - &src[0], - &tgt[0]); - system(&cmd[0]); +static void cp(const std::string& src, const std::string& tgt) { + std::string cmd = "cp " + src + " " + tgt; + system(cmd.c_str()); } static void backup_old() { - cp(ApplyPatchTest::old_file, ApplyPatchTest::cache_file); + cp(ApplyPatchTest::old_file, ApplyPatchTest::cache_file); } static void restore_old() { - cp(ApplyPatchTest::cache_file, ApplyPatchTest::old_file); + cp(ApplyPatchTest::cache_file, ApplyPatchTest::old_file); } class ApplyPatchCacheTest : public ApplyPatchTest { - public: - virtual void SetUp() { - backup_old(); - } - - virtual void TearDown() { - restore_old(); - } + public: + virtual void SetUp() { + backup_old(); + } + + virtual void TearDown() { + restore_old(); + } }; class ApplyPatchFullTest : public ApplyPatchCacheTest { - public: - static void SetUpTestCase() { - ApplyPatchTest::SetUpTestCase(); - unsigned long free_kb = FreeSpaceForFile(&WORK_FS[0]); - ASSERT_GE(free_kb * 1024, new_size * 3 / 2); - output_f = new TemporaryFile(); - output_loc = std::string(output_f->path); - - struct FileContents fc; - - ASSERT_EQ(0, LoadFileContents(&rand_file[0], &fc)); - Value* patch1 = new Value(VAL_BLOB, std::string(fc.data.begin(), fc.data.end())); - patches.push_back(patch1); - - ASSERT_EQ(0, LoadFileContents(&patch_file[0], &fc)); - Value* patch2 = new Value(VAL_BLOB, std::string(fc.data.begin(), fc.data.end())); - patches.push_back(patch2); - } - static void TearDownTestCase() { - delete output_f; - for (auto it = patches.begin(); it != patches.end(); ++it) { - delete *it; - } - patches.clear(); - } - - static std::vector<Value*> patches; - static TemporaryFile* output_f; - static std::string output_loc; + public: + static void SetUpTestCase() { + ApplyPatchTest::SetUpTestCase(); + + output_f = new TemporaryFile(); + output_loc = std::string(output_f->path); + + struct FileContents fc; + + ASSERT_EQ(0, LoadFileContents(&rand_file[0], &fc)); + patches.push_back( + std::make_unique<Value>(VAL_BLOB, std::string(fc.data.begin(), fc.data.end()))); + + ASSERT_EQ(0, LoadFileContents(&patch_file[0], &fc)); + patches.push_back( + std::make_unique<Value>(VAL_BLOB, std::string(fc.data.begin(), fc.data.end()))); + } + + static void TearDownTestCase() { + delete output_f; + patches.clear(); + } + + static std::vector<std::unique_ptr<Value>> patches; + static TemporaryFile* output_f; + static std::string output_loc; }; class ApplyPatchDoubleCacheTest : public ApplyPatchFullTest { - public: - virtual void SetUp() { - ApplyPatchCacheTest::SetUp(); - cp(cache_file, "/cache/reallysaved.file"); - } - - virtual void TearDown() { - cp("/cache/reallysaved.file", cache_file); - ApplyPatchCacheTest::TearDown(); - } + public: + virtual void SetUp() { + ApplyPatchCacheTest::SetUp(); + cp(cache_file, "/cache/reallysaved.file"); + } + + virtual void TearDown() { + cp("/cache/reallysaved.file", cache_file); + ApplyPatchCacheTest::TearDown(); + } }; std::string ApplyPatchTest::rand_file; @@ -196,188 +187,268 @@ std::string ApplyPatchTest::bad_sha1_b; size_t ApplyPatchTest::new_size; -std::vector<Value*> ApplyPatchFullTest::patches; +std::vector<std::unique_ptr<Value>> ApplyPatchFullTest::patches; TemporaryFile* ApplyPatchFullTest::output_f; std::string ApplyPatchFullTest::output_loc; TEST_F(ApplyPatchTest, CheckModeSkip) { - std::vector<std::string> sha1s; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); + std::vector<std::string> sha1s; + ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchTest, CheckModeSingle) { - std::vector<std::string> sha1s = { old_sha1 }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); + std::vector<std::string> sha1s = { old_sha1 }; + ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchTest, CheckModeMultiple) { - std::vector<std::string> sha1s = { - bad_sha1_a, - old_sha1, - bad_sha1_b - }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); + std::vector<std::string> sha1s = { bad_sha1_a, old_sha1, bad_sha1_b }; + ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchTest, CheckModeFailure) { - std::vector<std::string> sha1s = { - bad_sha1_a, - bad_sha1_b - }; - ASSERT_NE(0, applypatch_check(&old_file[0], sha1s)); + std::vector<std::string> sha1s = { bad_sha1_a, bad_sha1_b }; + ASSERT_NE(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSingle) { - mangle_file(old_file); - std::vector<std::string> sha1s = { old_sha1 }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); + mangle_file(old_file); + std::vector<std::string> sha1s = { old_sha1 }; + ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedMultiple) { - mangle_file(old_file); - std::vector<std::string> sha1s = { - bad_sha1_a, - old_sha1, - bad_sha1_b - }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); + mangle_file(old_file); + std::vector<std::string> sha1s = { bad_sha1_a, old_sha1, bad_sha1_b }; + ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedFailure) { - mangle_file(old_file); - std::vector<std::string> sha1s = { - bad_sha1_a, - bad_sha1_b - }; - ASSERT_NE(0, applypatch_check(&old_file[0], sha1s)); + mangle_file(old_file); + std::vector<std::string> sha1s = { bad_sha1_a, bad_sha1_b }; + ASSERT_NE(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchCacheTest, CheckCacheMissingSingle) { - unlink(&old_file[0]); - std::vector<std::string> sha1s = { old_sha1 }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); + unlink(&old_file[0]); + std::vector<std::string> sha1s = { old_sha1 }; + ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchCacheTest, CheckCacheMissingMultiple) { - unlink(&old_file[0]); - std::vector<std::string> sha1s = { - bad_sha1_a, - old_sha1, - bad_sha1_b - }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); + unlink(&old_file[0]); + std::vector<std::string> sha1s = { bad_sha1_a, old_sha1, bad_sha1_b }; + ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchCacheTest, CheckCacheMissingFailure) { - unlink(&old_file[0]); - std::vector<std::string> sha1s = { - bad_sha1_a, - bad_sha1_b - }; - ASSERT_NE(0, applypatch_check(&old_file[0], sha1s)); + unlink(&old_file[0]); + std::vector<std::string> sha1s = { bad_sha1_a, bad_sha1_b }; + ASSERT_NE(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchFullTest, ApplyInPlace) { - std::vector<std::string> sha1s = { - bad_sha1_a, - old_sha1 - }; - int ap_result = applypatch(&old_file[0], - "-", - &new_sha1[0], - new_size, - sha1s, - patches.data(), - nullptr); - ASSERT_EQ(0, ap_result); - ASSERT_TRUE(file_cmp(old_file, new_file)); - // reapply, applypatch is idempotent so it should succeed - ap_result = applypatch(&old_file[0], - "-", - &new_sha1[0], - new_size, - sha1s, - patches.data(), - nullptr); - ASSERT_EQ(0, ap_result); - ASSERT_TRUE(file_cmp(old_file, new_file)); + std::vector<std::string> sha1s = { bad_sha1_a, old_sha1 }; + ASSERT_EQ(0, applypatch(&old_file[0], "-", &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_TRUE(file_cmp(old_file, new_file)); + + // reapply, applypatch is idempotent so it should succeed + ASSERT_EQ(0, applypatch(&old_file[0], "-", &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_TRUE(file_cmp(old_file, new_file)); } TEST_F(ApplyPatchFullTest, ApplyInNewLocation) { - std::vector<std::string> sha1s = { - bad_sha1_a, - old_sha1 - }; - int ap_result = applypatch(&old_file[0], - &output_loc[0], - &new_sha1[0], - new_size, - sha1s, - patches.data(), - nullptr); - ASSERT_EQ(0, ap_result); - ASSERT_TRUE(file_cmp(output_loc, new_file)); - ap_result = applypatch(&old_file[0], - &output_loc[0], - &new_sha1[0], - new_size, - sha1s, - patches.data(), - nullptr); - ASSERT_EQ(0, ap_result); - ASSERT_TRUE(file_cmp(output_loc, new_file)); + std::vector<std::string> sha1s = { bad_sha1_a, old_sha1 }; + // Apply bsdiff patch to new location. + ASSERT_EQ( + 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_TRUE(file_cmp(output_loc, new_file)); + + // Reapply to the same location. + ASSERT_EQ( + 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_TRUE(file_cmp(output_loc, new_file)); } TEST_F(ApplyPatchFullTest, ApplyCorruptedInNewLocation) { - mangle_file(old_file); - std::vector<std::string> sha1s = { - bad_sha1_a, - old_sha1 - }; - int ap_result = applypatch(&old_file[0], - &output_loc[0], - &new_sha1[0], - new_size, - sha1s, - patches.data(), - nullptr); - ASSERT_EQ(0, ap_result); - ASSERT_TRUE(file_cmp(output_loc, new_file)); - ap_result = applypatch(&old_file[0], - &output_loc[0], - &new_sha1[0], - new_size, - sha1s, - patches.data(), - nullptr); - ASSERT_EQ(0, ap_result); - ASSERT_TRUE(file_cmp(output_loc, new_file)); + std::vector<std::string> sha1s = { bad_sha1_a, old_sha1 }; + // Apply bsdiff patch to new location with corrupted source. + mangle_file(old_file); + ASSERT_EQ( + 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_TRUE(file_cmp(output_loc, new_file)); + + // Reapply bsdiff patch to new location with corrupted source. + ASSERT_EQ( + 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_TRUE(file_cmp(output_loc, new_file)); } TEST_F(ApplyPatchDoubleCacheTest, ApplyDoubleCorruptedInNewLocation) { - mangle_file(old_file); - mangle_file(cache_file); - - std::vector<std::string> sha1s = { - bad_sha1_a, - old_sha1 - }; - int ap_result = applypatch(&old_file[0], - &output_loc[0], - &new_sha1[0], - new_size, - sha1s, - patches.data(), - nullptr); - ASSERT_NE(0, ap_result); - ASSERT_FALSE(file_cmp(output_loc, new_file)); - ap_result = applypatch(&old_file[0], - &output_loc[0], - &new_sha1[0], - new_size, - sha1s, - patches.data(), - nullptr); - ASSERT_NE(0, ap_result); - ASSERT_FALSE(file_cmp(output_loc, new_file)); + std::vector<std::string> sha1s = { bad_sha1_a, old_sha1 }; + + // Apply bsdiff patch to new location with corrupted source and copy (no new file). + // Expected to fail. + mangle_file(old_file); + mangle_file(cache_file); + ASSERT_NE( + 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_FALSE(file_cmp(output_loc, new_file)); + + // Expected to fail again on retry. + ASSERT_NE( + 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_FALSE(file_cmp(output_loc, new_file)); + + // Expected to fail with incorrect new file. + mangle_file(output_loc); + ASSERT_NE( + 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_FALSE(file_cmp(output_loc, new_file)); +} + +TEST(ApplyPatchModes, InvalidArgs) { + // At least two args (including the filename). + ASSERT_EQ(2, applypatch_modes(1, (const char* []){ "applypatch" })); + + // Unrecognized args. + ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-x" })); +} + +TEST(ApplyPatchModes, PatchMode) { + std::string boot_img = from_testdata_base("boot.img"); + size_t boot_img_size; + std::string boot_img_sha1; + sha1sum(boot_img, &boot_img_sha1, &boot_img_size); + + std::string recovery_img = from_testdata_base("recovery.img"); + size_t recovery_img_size; + std::string recovery_img_sha1; + sha1sum(recovery_img, &recovery_img_sha1, &recovery_img_size); + + std::string bonus_file = from_testdata_base("bonus.file"); + + // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch> + TemporaryFile tmp1; + std::vector<const char*> args = { + "applypatch", + "-b", + bonus_file.c_str(), + boot_img.c_str(), + tmp1.path, + recovery_img_sha1.c_str(), + std::to_string(recovery_img_size).c_str(), + (boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot.p")).c_str() + }; + ASSERT_EQ(0, applypatch_modes(args.size(), args.data())); + + // applypatch <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch> + TemporaryFile tmp2; + std::vector<const char*> args2 = { + "applypatch", + boot_img.c_str(), + tmp2.path, + recovery_img_sha1.c_str(), + std::to_string(recovery_img_size).c_str(), + (boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot-with-bonus.p")).c_str() + }; + ASSERT_EQ(0, applypatch_modes(args2.size(), args2.data())); + + // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> \ + // <src-sha1-fake>:<patch1> <src-sha1>:<patch2> + TemporaryFile tmp3; + std::string bad_sha1_a = android::base::StringPrintf("%040x", rand()); + std::string bad_sha1_b = android::base::StringPrintf("%040x", rand()); + std::vector<const char*> args3 = { + "applypatch", + "-b", + bonus_file.c_str(), + boot_img.c_str(), + tmp3.path, + recovery_img_sha1.c_str(), + std::to_string(recovery_img_size).c_str(), + (bad_sha1_a + ":" + from_testdata_base("recovery-from-boot.p")).c_str(), + (boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot.p")).c_str(), + (bad_sha1_b + ":" + from_testdata_base("recovery-from-boot.p")).c_str(), + }; + ASSERT_EQ(0, applypatch_modes(args3.size(), args3.data())); +} + +TEST(ApplyPatchModes, PatchModeInvalidArgs) { + // Invalid bonus file. + ASSERT_NE(0, applypatch_modes(3, (const char* []){ "applypatch", "-b", "/doesntexist" })); + + std::string bonus_file = from_testdata_base("bonus.file"); + // With bonus file, but missing args. + ASSERT_EQ(2, applypatch_modes(3, (const char* []){ "applypatch", "-b", bonus_file.c_str() })); + + std::string boot_img = from_testdata_base("boot.img"); + size_t boot_img_size; + std::string boot_img_sha1; + sha1sum(boot_img, &boot_img_sha1, &boot_img_size); + + std::string recovery_img = from_testdata_base("recovery.img"); + size_t recovery_img_size; + std::string recovery_img_sha1; + sha1sum(recovery_img, &recovery_img_sha1, &recovery_img_size); + + // Bonus file is not supported in flash mode. + // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> + TemporaryFile tmp4; + std::vector<const char*> args4 = { + "applypatch", + "-b", + bonus_file.c_str(), + boot_img.c_str(), + tmp4.path, + recovery_img_sha1.c_str(), + std::to_string(recovery_img_size).c_str() }; + ASSERT_NE(0, applypatch_modes(args4.size(), args4.data())); + + // Failed to parse patch args. + TemporaryFile tmp5; + std::vector<const char*> args5 = { + "applypatch", + boot_img.c_str(), + tmp5.path, + recovery_img_sha1.c_str(), + std::to_string(recovery_img_size).c_str(), + ("invalid-sha1:filename" + from_testdata_base("recovery-from-boot-with-bonus.p")).c_str(), + }; + ASSERT_NE(0, applypatch_modes(args5.size(), args5.data())); + + // Target size cannot be zero. + TemporaryFile tmp6; + std::vector<const char*> args6 = { + "applypatch", + boot_img.c_str(), + tmp6.path, + recovery_img_sha1.c_str(), + "0", // target size + (boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot-with-bonus.p")).c_str() + }; + ASSERT_NE(0, applypatch_modes(args6.size(), args6.data())); +} + +TEST(ApplyPatchModes, CheckModeInvalidArgs) { + // Insufficient args. + ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-c" })); +} + +TEST(ApplyPatchModes, SpaceModeInvalidArgs) { + // Insufficient args. + ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-s" })); + + // Invalid bytes arg. + ASSERT_EQ(1, applypatch_modes(3, (const char* []){ "applypatch", "-s", "x" })); + + // 0 is invalid. + ASSERT_EQ(1, applypatch_modes(3, (const char* []){ "applypatch", "-s", "0" })); + + // 0x10 is fine. + ASSERT_EQ(0, applypatch_modes(3, (const char* []){ "applypatch", "-s", "0x10" })); +} + +TEST(ApplyPatchModes, ShowLicenses) { + ASSERT_EQ(0, applypatch_modes(2, (const char* []){ "applypatch", "-l" })); } diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index a859f11c1..acc2b4040 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -16,7 +16,9 @@ #include <string> +#include <android-base/file.h> #include <android-base/properties.h> +#include <android-base/test_utils.h> #include <gtest/gtest.h> #include "edify/expr.h" @@ -97,3 +99,135 @@ TEST_F(UpdaterTest, sha1_check) { // sha1_check() expects at least one argument. expect(nullptr, "sha1_check()", kArgsParsingFailure); } + +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\")", kFileGetPropFailure); + + // 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.c_str(), kNoCause); + + std::string script2("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.board\")"); + expect("magic", script2.c_str(), kNoCause); + + // No match. + std::string script3("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.wrong\")"); + expect("", script3.c_str(), kNoCause); + + std::string script4("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.name=\")"); + expect("", script4.c_str(), kNoCause); + + std::string script5("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.nam\")"); + expect("", script5.c_str(), kNoCause); + + std::string script6("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.model\")"); + expect("", script6.c_str(), kNoCause); +} + +TEST_F(UpdaterTest, delete) { + // Delete none. + expect("0", "delete()", kNoCause); + expect("0", "delete(\"/doesntexist\")", kNoCause); + expect("0", "delete(\"/doesntexist1\", \"/doesntexist2\")", kNoCause); + expect("0", "delete(\"/doesntexist1\", \"/doesntexist2\", \"/doesntexist3\")", kNoCause); + + // Delete one file. + TemporaryFile temp_file1; + ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file1.path)); + std::string script1("delete(\"" + std::string(temp_file1.path) + "\")"); + expect("1", script1.c_str(), kNoCause); + + // Delete two files. + TemporaryFile temp_file2; + ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file2.path)); + TemporaryFile temp_file3; + ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file3.path)); + std::string script2("delete(\"" + std::string(temp_file2.path) + "\", \"" + + std::string(temp_file3.path) + "\")"); + expect("2", script2.c_str(), kNoCause); + + // Delete already deleted files. + expect("0", script2.c_str(), kNoCause); + + // Delete one out of three. + TemporaryFile temp_file4; + ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file4.path)); + std::string script3("delete(\"/doesntexist1\", \"" + std::string(temp_file4.path) + + "\", \"/doesntexist2\")"); + expect("1", script3.c_str(), kNoCause); +} + +TEST_F(UpdaterTest, rename) { + // rename() expects two arguments. + expect(nullptr, "rename()", kArgsParsingFailure); + expect(nullptr, "rename(\"arg1\")", kArgsParsingFailure); + expect(nullptr, "rename(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure); + + // src_name or dst_name cannot be empty. + expect(nullptr, "rename(\"\", \"arg2\")", kArgsParsingFailure); + expect(nullptr, "rename(\"arg1\", \"\")", kArgsParsingFailure); + + // File doesn't exist (both of src and dst). + expect(nullptr, "rename(\"/doesntexist\", \"/doesntexisteither\")" , kFileRenameFailure); + + // Can't create parent directory. + TemporaryFile temp_file1; + ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file1.path)); + std::string script1("rename(\"" + std::string(temp_file1.path) + "\", \"/proc/0/file1\")"); + expect(nullptr, script1.c_str(), kFileRenameFailure); + + // Rename. + TemporaryFile temp_file2; + std::string script2("rename(\"" + std::string(temp_file1.path) + "\", \"" + + std::string(temp_file2.path) + "\")"); + expect(temp_file2.path, script2.c_str(), kNoCause); + + // Already renamed. + expect(temp_file2.path, script2.c_str(), kNoCause); + + // Parents create successfully. + TemporaryFile temp_file3; + TemporaryDir td; + std::string temp_dir = std::string(td.path) + "/aaa/bbb/a.txt"; + std::string script3("rename(\"" + std::string(temp_file3.path) + "\", \"" + + temp_dir + "\")"); + expect(temp_dir.c_str(), script3.c_str(), kNoCause); +} + +TEST_F(UpdaterTest, symlink) { + // symlink expects 1+ argument. + expect(nullptr, "symlink()", kArgsParsingFailure); + + // symlink should fail if src is an empty string. + TemporaryFile temp_file1; + std::string script1("symlink(\"" + std::string(temp_file1.path) + "\", \"\")"); + expect(nullptr, script1.c_str(), kSymlinkFailure); + + // symlink failed to remove old src. + std::string script2("symlink(\"" + std::string(temp_file1.path) + "\", \"/proc\")"); + expect(nullptr, script2.c_str(), kSymlinkFailure); +} diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp index 7f9a71408..60a78f5c3 100644 --- a/tests/component/verifier_test.cpp +++ b/tests/component/verifier_test.cpp @@ -37,9 +37,6 @@ #include "ui.h" #include "verifier.h" -static const char* DATA_PATH = getenv("ANDROID_DATA"); -static const char* TESTDATA_PATH = "/recovery/testdata/"; - RecoveryUI* ui = NULL; class MockUI : public RecoveryUI { @@ -92,17 +89,13 @@ class VerifierTest : public testing::TestWithParam<std::vector<std::string>> { virtual void SetUp() { std::vector<std::string> args = GetParam(); - std::string package = - android::base::StringPrintf("%s%s%s%s", DATA_PATH, NATIVE_TEST_PATH, - TESTDATA_PATH, args[0].c_str()); + std::string package = from_testdata_base(args[0]); if (sysMapFile(package.c_str(), &memmap) != 0) { FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n"; } for (auto it = ++(args.cbegin()); it != args.cend(); ++it) { - std::string public_key_file = android::base::StringPrintf( - "%s%s%stestkey_%s.txt", DATA_PATH, NATIVE_TEST_PATH, - TESTDATA_PATH, it->c_str()); + std::string public_key_file = from_testdata_base("testkey_" + *it + ".txt"); ASSERT_TRUE(load_keys(public_key_file.c_str(), certs)); } } diff --git a/tests/manual/recovery_test.cpp b/tests/manual/recovery_test.cpp new file mode 100644 index 000000000..e83849546 --- /dev/null +++ b/tests/manual/recovery_test.cpp @@ -0,0 +1,87 @@ +/* + * 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 <fcntl.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#include <string> + +#include <android-base/file.h> +#include <android/log.h> +#include <gtest/gtest.h> +#include <private/android_logger.h> + +static const std::string myFilename = "/data/misc/recovery/inject.txt"; +static const std::string myContent = "Hello World\nWelcome to my recovery\n"; + +// Failure is expected on systems that do not deliver either the +// recovery-persist or recovery-refresh executables. Tests also require +// a reboot sequence of test to truly verify. + +static ssize_t __pmsg_fn(log_id_t logId, char prio, const char *filename, + const char *buf, size_t len, void *arg) { + EXPECT_EQ(LOG_ID_SYSTEM, logId); + EXPECT_EQ(ANDROID_LOG_INFO, prio); + EXPECT_NE(std::string::npos, myFilename.find(filename)); + EXPECT_EQ(myContent, buf); + EXPECT_EQ(myContent.size(), len); + EXPECT_EQ(nullptr, arg); + return len; +} + +// recovery.refresh - May fail. Requires recovery.inject, two reboots, +// then expect success after second reboot. +TEST(recovery, refresh) { + EXPECT_EQ(0, access("/system/bin/recovery-refresh", F_OK)); + + ssize_t ret = __android_log_pmsg_file_read( + LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", __pmsg_fn, nullptr); + if (ret == -ENOENT) { + EXPECT_LT(0, __android_log_pmsg_file_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, + myFilename.c_str(), myContent.c_str(), myContent.size())); + + fprintf(stderr, "injected test data, requires two intervening reboots " + "to check for replication\n"); + } + EXPECT_EQ(static_cast<ssize_t>(myContent.size()), ret); +} + +// recovery.persist - Requires recovery.inject, then a reboot, then +// expect success after for this test on that boot. +TEST(recovery, persist) { + EXPECT_EQ(0, access("/system/bin/recovery-persist", F_OK)); + + ssize_t ret = __android_log_pmsg_file_read( + LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", __pmsg_fn, nullptr); + if (ret == -ENOENT) { + EXPECT_LT(0, __android_log_pmsg_file_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, + myFilename.c_str(), myContent.c_str(), myContent.size())); + + fprintf(stderr, "injected test data, requires intervening reboot " + "to check for storage\n"); + } + + std::string buf; + EXPECT_TRUE(android::base::ReadFileToString(myFilename, &buf)); + EXPECT_EQ(myContent, buf); + if (access(myFilename.c_str(), O_RDONLY) == 0) { + fprintf(stderr, "Removing persistent test data, " + "check if reconstructed on reboot\n"); + } + EXPECT_EQ(0, unlink(myFilename.c_str())); +} diff --git a/tests/testdata/bonus.file b/tests/testdata/bonus.file Binary files differnew file mode 100644 index 000000000..918ef8ac5 --- /dev/null +++ b/tests/testdata/bonus.file diff --git a/tests/testdata/boot.img b/tests/testdata/boot.img Binary files differnew file mode 100644 index 000000000..dd4897510 --- /dev/null +++ b/tests/testdata/boot.img diff --git a/tests/testdata/recovery-from-boot-with-bonus.p b/tests/testdata/recovery-from-boot-with-bonus.p Binary files differnew file mode 100644 index 000000000..08b6f55e4 --- /dev/null +++ b/tests/testdata/recovery-from-boot-with-bonus.p diff --git a/tests/testdata/recovery-from-boot.p b/tests/testdata/recovery-from-boot.p Binary files differnew file mode 100644 index 000000000..06f6c299f --- /dev/null +++ b/tests/testdata/recovery-from-boot.p diff --git a/tests/testdata/recovery.img b/tests/testdata/recovery.img Binary files differnew file mode 100644 index 000000000..b862e6f0c --- /dev/null +++ b/tests/testdata/recovery.img diff --git a/tests/unit/recovery_test.cpp b/tests/unit/recovery_test.cpp deleted file mode 100644 index 28b845fb9..000000000 --- a/tests/unit/recovery_test.cpp +++ /dev/null @@ -1,91 +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 <fcntl.h> -#include <string.h> -#include <sys/types.h> -#include <unistd.h> - -#include <android/log.h> -#include <gtest/gtest.h> -#include <private/android_logger.h> - -static const char myFilename[] = "/data/misc/recovery/inject.txt"; -static const char myContent[] = "Hello World\nWelcome to my recovery\n"; - -// Failure is expected on systems that do not deliver either the -// recovery-persist or recovery-refresh executables. Tests also require -// a reboot sequence of test to truly verify. - -static ssize_t __pmsg_fn(log_id_t logId, char prio, const char *filename, - const char *buf, size_t len, void *arg) { - EXPECT_EQ(LOG_ID_SYSTEM, logId); - EXPECT_EQ(ANDROID_LOG_INFO, prio); - EXPECT_EQ(0, NULL == strstr(myFilename,filename)); - EXPECT_EQ(0, strcmp(myContent, buf)); - EXPECT_EQ(sizeof(myContent), len); - EXPECT_EQ(0, NULL != arg); - return len; -} - -// recovery.refresh - May fail. Requires recovery.inject, two reboots, -// then expect success after second reboot. -TEST(recovery, refresh) { - EXPECT_EQ(0, access("/system/bin/recovery-refresh", F_OK)); - - ssize_t ret = __android_log_pmsg_file_read( - LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", __pmsg_fn, NULL); - if (ret == -ENOENT) { - EXPECT_LT(0, __android_log_pmsg_file_write( - LOG_ID_SYSTEM, ANDROID_LOG_INFO, - myFilename, myContent, sizeof(myContent))); - fprintf(stderr, "injected test data, " - "requires two intervening reboots " - "to check for replication\n"); - } - EXPECT_EQ((ssize_t)sizeof(myContent), ret); -} - -// recovery.persist - Requires recovery.inject, then a reboot, then -// expect success after for this test on that boot. -TEST(recovery, persist) { - EXPECT_EQ(0, access("/system/bin/recovery-persist", F_OK)); - - ssize_t ret = __android_log_pmsg_file_read( - LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", __pmsg_fn, NULL); - if (ret == -ENOENT) { - EXPECT_LT(0, __android_log_pmsg_file_write( - LOG_ID_SYSTEM, ANDROID_LOG_INFO, - myFilename, myContent, sizeof(myContent))); - fprintf(stderr, "injected test data, " - "requires intervening reboot " - "to check for storage\n"); - } - - int fd = open(myFilename, O_RDONLY); - EXPECT_LE(0, fd); - - char buf[sizeof(myContent) + 32]; - ret = read(fd, buf, sizeof(buf)); - close(fd); - EXPECT_EQ(ret, (ssize_t)sizeof(myContent)); - EXPECT_EQ(0, strcmp(myContent, buf)); - if (fd >= 0) { - fprintf(stderr, "Removing persistent test data, " - "check if reconstructed on reboot\n"); - } - EXPECT_EQ(0, unlink(myFilename)); -} diff --git a/tests/unit/sysutil_test.cpp b/tests/unit/sysutil_test.cpp new file mode 100644 index 000000000..f4699664b --- /dev/null +++ b/tests/unit/sysutil_test.cpp @@ -0,0 +1,140 @@ +/* + * 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 <gtest/gtest.h> + +#include <string> + +#include <android-base/file.h> +#include <android-base/test_utils.h> + +#include "otautil/SysUtil.h" + +TEST(SysUtilTest, InvalidArgs) { + MemMapping mapping; + + // Invalid argument. + ASSERT_EQ(-1, sysMapFile(nullptr, &mapping)); + ASSERT_EQ(-1, sysMapFile("/somefile", nullptr)); +} + +TEST(SysUtilTest, sysMapFileRegularFile) { + TemporaryFile temp_file1; + std::string content = "abc"; + ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file1.path)); + + // sysMapFile() should map the file to one range. + MemMapping mapping; + ASSERT_EQ(0, sysMapFile(temp_file1.path, &mapping)); + ASSERT_NE(nullptr, mapping.addr); + ASSERT_EQ(content.size(), mapping.length); + ASSERT_EQ(1U, mapping.ranges.size()); + + sysReleaseMap(&mapping); + ASSERT_EQ(0U, mapping.ranges.size()); +} + +TEST(SysUtilTest, sysMapFileBlockMap) { + // Create a file that has 10 blocks. + TemporaryFile package; + std::string content; + constexpr size_t file_size = 4096 * 10; + content.reserve(file_size); + ASSERT_TRUE(android::base::WriteStringToFile(content, package.path)); + + TemporaryFile block_map_file; + std::string filename = std::string("@") + block_map_file.path; + MemMapping mapping; + + // One range. + std::string block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10\n"; + ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path)); + + ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping)); + ASSERT_EQ(file_size, mapping.length); + ASSERT_EQ(1U, mapping.ranges.size()); + + // It's okay to not have the trailing '\n'. + block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10"; + ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path)); + + ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping)); + ASSERT_EQ(file_size, mapping.length); + ASSERT_EQ(1U, mapping.ranges.size()); + + // Or having multiple trailing '\n's. + block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10\n\n\n"; + ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path)); + + ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping)); + ASSERT_EQ(file_size, mapping.length); + ASSERT_EQ(1U, mapping.ranges.size()); + + // Multiple ranges. + block_map_content = std::string(package.path) + "\n40960 4096\n3\n0 3\n3 5\n5 10\n"; + ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path)); + + ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping)); + ASSERT_EQ(file_size, mapping.length); + ASSERT_EQ(3U, mapping.ranges.size()); + + sysReleaseMap(&mapping); + ASSERT_EQ(0U, mapping.ranges.size()); +} + +TEST(SysUtilTest, sysMapFileBlockMapInvalidBlockMap) { + MemMapping mapping; + TemporaryFile temp_file; + std::string filename = std::string("@") + temp_file.path; + + // Block map file is too short. + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n0\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + // Block map file has unexpected number of lines. + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n1\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n2\n0 1\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + // Invalid size/blksize/range_count. + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\nabc 4096\n1\n0 1\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n\n0 1\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + // size/blksize/range_count don't match. + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n0 4096\n1\n0 1\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 0\n1\n0 1\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n0\n0 1\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + // Invalid block dev path. + ASSERT_TRUE(android::base::WriteStringToFile("/doesntexist\n4096 4096\n1\n0 1\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + sysReleaseMap(&mapping); + ASSERT_EQ(0U, mapping.ranges.size()); +} diff --git a/tests/unit/zip_test.cpp b/tests/unit/zip_test.cpp index b617446b8..49729467d 100644 --- a/tests/unit/zip_test.cpp +++ b/tests/unit/zip_test.cpp @@ -16,78 +16,69 @@ #include <errno.h> #include <fcntl.h> -#include <pthread.h> #include <unistd.h> #include <memory> #include <vector> #include <android-base/file.h> -#include <android-base/stringprintf.h> -#include <android-base/unique_fd.h> #include <android-base/test_utils.h> #include <gtest/gtest.h> #include <otautil/SysUtil.h> #include <otautil/ZipUtil.h> #include <ziparchive/zip_archive.h> -static const std::string DATA_PATH(getenv("ANDROID_DATA")); -static const std::string TESTDATA_PATH("/recovery/testdata/"); - -static const std::vector<uint8_t> kATxtContents { - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - '\n' -}; - -static const std::vector<uint8_t> kBTxtContents { - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - '\n' -}; - -TEST(otazip, ExtractPackageRecursive) { - TemporaryDir td; - ASSERT_NE(td.path, nullptr); - ZipArchiveHandle handle; - std::string zip_path = DATA_PATH + TESTDATA_PATH + "/ziptest_valid.zip"; - ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); - // Extract the whole package into a temp directory. - ExtractPackageRecursive(handle, "", td.path, nullptr, nullptr); - // Make sure all the files are extracted correctly. - std::string path(td.path); - android::base::unique_fd fd(open((path + "/a.txt").c_str(), O_RDONLY)); - ASSERT_NE(fd, -1); - std::vector<uint8_t> read_data; - read_data.resize(kATxtContents.size()); - // The content of the file is the same as expected. - ASSERT_TRUE(android::base::ReadFully(fd.get(), read_data.data(), read_data.size())); - ASSERT_EQ(0, memcmp(read_data.data(), kATxtContents.data(), kATxtContents.size())); - - fd.reset(open((path + "/b.txt").c_str(), O_RDONLY)); - ASSERT_NE(fd, -1); - fd.reset(open((path + "/b/c.txt").c_str(), O_RDONLY)); - ASSERT_NE(fd, -1); - fd.reset(open((path + "/b/d.txt").c_str(), O_RDONLY)); - ASSERT_NE(fd, -1); - read_data.resize(kBTxtContents.size()); - ASSERT_TRUE(android::base::ReadFully(fd.get(), read_data.data(), read_data.size())); - ASSERT_EQ(0, memcmp(read_data.data(), kBTxtContents.data(), kBTxtContents.size())); +#include "common/test_constants.h" + +static const std::string kATxtContents("abcdefghabcdefgh\n"); +static const std::string kBTxtContents("abcdefgh\n"); + +TEST(ZipTest, ExtractPackageRecursive) { + 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; + ASSERT_NE(nullptr, td.path); + 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(), O_RDONLY)); + ASSERT_EQ(0, access((path + "/b.txt").c_str(), O_RDONLY)); + ASSERT_EQ(0, access((path + "/b/c.txt").c_str(), O_RDONLY)); + ASSERT_EQ(0, access((path + "/b/d.txt").c_str(), O_RDONLY)); + + // 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(kBTxtContents, content2); } -TEST(otazip, OpenFromMemory) { - MemMapping map; - std::string zip_path = DATA_PATH + TESTDATA_PATH + "/ziptest_dummy-update.zip"; - ASSERT_EQ(0, sysMapFile(zip_path.c_str(), &map)); - // Map an update package into memory and open the archive from there. - ZipArchiveHandle handle; - ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_path.c_str(), &handle)); - static constexpr const char* BINARY_PATH = "META-INF/com/google/android/update-binary"; - ZipString binary_path(BINARY_PATH); - ZipEntry binary_entry; - // Make sure the package opens correctly and its entry can be read. - ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry)); - TemporaryFile tmp_binary; - ASSERT_NE(-1, tmp_binary.fd); - ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd)); +TEST(ZipTest, OpenFromMemory) { + MemMapping map; + std::string zip_path = from_testdata_base("ziptest_dummy-update.zip"); + ASSERT_EQ(0, sysMapFile(zip_path.c_str(), &map)); + + // Map an update package into memory and open the archive from there. + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_path.c_str(), &handle)); + + static constexpr const char* BINARY_PATH = "META-INF/com/google/android/update-binary"; + ZipString binary_path(BINARY_PATH); + ZipEntry binary_entry; + // Make sure the package opens correctly and its entry can be read. + ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry)); + + TemporaryFile tmp_binary; + ASSERT_NE(-1, tmp_binary.fd); + ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd)); + + sysReleaseMap(&map); } diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index f08ca5b0c..c939cf89d 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -1368,18 +1368,15 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg fprintf(stderr, "This update is a retry.\n"); } - Value* blockdev_filename = nullptr; - Value* transfer_list_value = nullptr; - Value* new_data_fn = nullptr; - Value* patch_data_fn = nullptr; - if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value, - &new_data_fn, &patch_data_fn) < 0) { - return StringValue(""); + std::vector<std::unique_ptr<Value>> args; + if (!ReadValueArgs(state, 4, argv, &args)) { + return nullptr; } - std::unique_ptr<Value> blockdev_filename_holder(blockdev_filename); - std::unique_ptr<Value> transfer_list_value_holder(transfer_list_value); - std::unique_ptr<Value> new_data_fn_holder(new_data_fn); - std::unique_ptr<Value> patch_data_fn_holder(patch_data_fn); + + const Value* blockdev_filename = args[0].get(); + const Value* transfer_list_value = args[1].get(); + const Value* new_data_fn = args[2].get(); + const Value* patch_data_fn = args[3].get(); if (blockdev_filename->type != VAL_STRING) { ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", @@ -1689,14 +1686,13 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] } Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[]) { - Value* blockdev_filename; - Value* ranges; - - if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) { - return StringValue(""); + std::vector<std::unique_ptr<Value>> args; + if (!ReadValueArgs(state, 2, argv, &args)) { + return nullptr; } - std::unique_ptr<Value> ranges_holder(ranges); - std::unique_ptr<Value> blockdev_filename_holder(blockdev_filename); + + const Value* blockdev_filename = args[0].get(); + const Value* ranges = args[1].get(); if (blockdev_filename->type != VAL_STRING) { ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", @@ -1751,14 +1747,14 @@ Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[]) // if executes successfully and an empty string otherwise. Value* CheckFirstBlockFn(const char* name, State* state, int argc, Expr* argv[]) { - Value* arg_filename; - - if (ReadValueArgs(state, argv, 1, &arg_filename) < 0) { + std::vector<std::unique_ptr<Value>> args; + if (!ReadValueArgs(state, 1, argv, &args)) { return nullptr; } - std::unique_ptr<Value> filename(arg_filename); - if (filename->type != VAL_STRING) { + const Value* arg_filename = args[0].get(); + + if (arg_filename->type != VAL_STRING) { ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); return StringValue(""); } @@ -1799,15 +1795,13 @@ Value* CheckFirstBlockFn(const char* name, State* state, int argc, Expr* argv[]) Value* BlockImageRecoverFn(const char* name, State* state, int argc, Expr* argv[]) { - Value* arg_filename; - Value* arg_ranges; - - if (ReadValueArgs(state, argv, 2, &arg_filename, &arg_ranges) < 0) { - return NULL; + std::vector<std::unique_ptr<Value>> args; + if (!ReadValueArgs(state, 2, argv, &args)) { + return nullptr; } - std::unique_ptr<Value> filename(arg_filename); - std::unique_ptr<Value> ranges(arg_ranges); + const Value* filename = args[0].get(); + const Value* ranges = args[1].get(); if (filename->type != VAL_STRING) { ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); diff --git a/updater/install.cpp b/updater/install.cpp index a41c5db3c..cba95f5b0 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -40,6 +40,7 @@ #include <vector> #include <android-base/parseint.h> +#include <android-base/parsedouble.h> #include <android-base/properties.h> #include <android-base/strings.h> #include <android-base/stringprintf.h> @@ -65,7 +66,7 @@ // Send over the buffer to recovery though the command pipe. static void uiPrint(State* state, const std::string& buffer) { - UpdaterInfo* ui = reinterpret_cast<UpdaterInfo*>(state->cookie); + UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie); // "line1\nline2\n" will be split into 3 tokens: "line1", "line2" and "". // So skip sending empty strings to UI. @@ -94,83 +95,84 @@ void uiPrintf(State* _Nonnull state, const char* _Nonnull format, ...) { uiPrint(state, error_msg); } +static bool is_dir(const std::string& dirpath) { + struct stat st; + return stat(dirpath.c_str(), &st) == 0 && S_ISDIR(st.st_mode); +} + // Create all parent directories of name, if necessary. -static int make_parents(char* name) { - char* p; - for (p = name + (strlen(name)-1); p > name; --p) { - if (*p != '/') continue; - *p = '\0'; - if (make_parents(name) < 0) return -1; - int result = mkdir(name, 0700); - if (result == 0) printf("created [%s]\n", name); - *p = '/'; - if (result == 0 || errno == EEXIST) { - // successfully created or already existed; we're done - return 0; - } else { - printf("failed to mkdir %s: %s\n", name, strerror(errno)); - return -1; +static bool make_parents(const std::string& name) { + size_t prev_end = 0; + while (prev_end < name.size()) { + size_t next_end = name.find('/', prev_end + 1); + if (next_end == std::string::npos) { + break; + } + std::string dir_path = name.substr(0, next_end); + if (!is_dir(dir_path)) { + int result = mkdir(dir_path.c_str(), 0700); + if (result != 0) { + printf("failed to mkdir %s when make parents for %s: %s\n", dir_path.c_str(), + name.c_str(), strerror(errno)); + return false; + } + + printf("created [%s]\n", dir_path.c_str()); } + prev_end = next_end; } - return 0; + return true; } // 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, int argc, Expr* argv[]) { - char* result = nullptr; if (argc != 4 && argc != 5) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 4-5 args, got %d", name, argc); } - char* fs_type; - char* partition_type; - char* location; - char* mount_point; - char* mount_options; - bool has_mount_options; + + std::vector<std::string> args; + if (!ReadArgs(state, argc, 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 (argc == 5) { - has_mount_options = true; - if (ReadArgs(state, argv, 5, &fs_type, &partition_type, - &location, &mount_point, &mount_options) < 0) { - return NULL; - } - } else { - has_mount_options = false; - if (ReadArgs(state, argv, 4, &fs_type, &partition_type, - &location, &mount_point) < 0) { - return NULL; - } + mount_options = args[4]; } - if (strlen(fs_type) == 0) { - ErrorAbort(state, kArgsParsingFailure, "fs_type argument to %s() can't be empty", name); - goto done; + if (fs_type.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "fs_type argument to %s() can't be empty", + name); } - if (strlen(partition_type) == 0) { - ErrorAbort(state, kArgsParsingFailure, "partition_type argument to %s() can't be empty", - name); - goto done; + if (partition_type.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "partition_type argument to %s() can't be empty", + name); } - if (strlen(location) == 0) { - ErrorAbort(state, kArgsParsingFailure, "location argument to %s() can't be empty", name); - goto done; + if (location.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "location argument to %s() can't be empty", + name); } - if (strlen(mount_point) == 0) { - ErrorAbort(state, kArgsParsingFailure, "mount_point argument to %s() can't be empty", - name); - goto done; + if (mount_point.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "mount_point argument to %s() can't be empty", + name); } { char *secontext = NULL; if (sehandle) { - selabel_lookup(sehandle, &secontext, mount_point, 0755); + selabel_lookup(sehandle, &secontext, mount_point.c_str(), 0755); setfscreatecon(secontext); } - mkdir(mount_point, 0755); + mkdir(mount_point.c_str(), 0755); if (secontext) { freecon(secontext); @@ -178,90 +180,71 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { } } - if (mount(location, mount_point, fs_type, - MS_NOATIME | MS_NODEV | MS_NODIRATIME, - has_mount_options ? mount_options : "") < 0) { + if (mount(location.c_str(), mount_point.c_str(), fs_type.c_str(), + MS_NOATIME | MS_NODEV | MS_NODIRATIME, mount_options.c_str()) < 0) { uiPrintf(state, "%s: failed to mount %s at %s: %s\n", - name, location, mount_point, strerror(errno)); - result = strdup(""); - } else { - result = mount_point; + name, location.c_str(), mount_point.c_str(), strerror(errno)); + return StringValue(""); } -done: - free(fs_type); - free(partition_type); - free(location); - if (result != mount_point) free(mount_point); - if (has_mount_options) free(mount_options); - return StringValue(result); + return StringValue(mount_point); } // is_mounted(mount_point) Value* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) { - char* result = nullptr; if (argc != 1) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); } - char* mount_point; - if (ReadArgs(state, argv, 1, &mount_point) < 0) { - return NULL; + + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); } - if (strlen(mount_point) == 0) { - ErrorAbort(state, kArgsParsingFailure, "mount_point argument to unmount() can't be empty"); - goto done; + const std::string& mount_point = args[0]; + if (mount_point.empty()) { + return ErrorAbort(state, kArgsParsingFailure, + "mount_point argument to unmount() can't be empty"); } scan_mounted_volumes(); - { - MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point); - if (vol == NULL) { - result = strdup(""); - } else { - result = mount_point; - } + MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point.c_str()); + if (vol == nullptr) { + return StringValue(""); } -done: - if (result != mount_point) free(mount_point); - return StringValue(result); + return StringValue(mount_point); } Value* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) { - char* result = nullptr; if (argc != 1) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); } - char* mount_point; - if (ReadArgs(state, argv, 1, &mount_point) < 0) { - return NULL; + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); } - if (strlen(mount_point) == 0) { - ErrorAbort(state, kArgsParsingFailure, "mount_point argument to unmount() can't be empty"); - goto done; + const std::string& mount_point = args[0]; + if (mount_point.empty()) { + return ErrorAbort(state, kArgsParsingFailure, + "mount_point argument to unmount() can't be empty"); } scan_mounted_volumes(); - { - MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point); - if (vol == NULL) { - uiPrintf(state, "unmount of %s failed; no such volume\n", mount_point); - result = strdup(""); - } else { - int ret = unmount_mounted_volume(vol); - if (ret != 0) { - uiPrintf(state, "unmount of %s failed (%d): %s\n", - mount_point, ret, strerror(errno)); - } - result = mount_point; + MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point.c_str()); + if (vol == nullptr) { + uiPrintf(state, "unmount of %s failed; no such volume\n", mount_point.c_str()); + return nullptr; + } else { + int ret = unmount_mounted_volume(vol); + if (ret != 0) { + uiPrintf(state, "unmount of %s failed (%d): %s\n", + mount_point.c_str(), ret, strerror(errno)); } } -done: - if (result != mount_point) free(mount_point); - return StringValue(result); + return StringValue(mount_point); } static int exec_cmd(const char* path, char* const argv[]) { @@ -278,7 +261,6 @@ static int exec_cmd(const char* path, char* const argv[]) { return WEXITSTATUS(status); } - // format(fs_type, partition_type, location, fs_size, mount_point) // // fs_type="ext4" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location> @@ -287,120 +269,119 @@ static int exec_cmd(const char* path, char* const argv[]) { // 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, int argc, Expr* argv[]) { - char* result = nullptr; if (argc != 5) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 5 args, got %d", name, argc); } - char* fs_type; - char* partition_type; - char* location; - char* fs_size; - char* mount_point; - if (ReadArgs(state, argv, 5, &fs_type, &partition_type, &location, &fs_size, &mount_point) < 0) { - return NULL; + std::vector<std::string> args; + if (!ReadArgs(state, argc, 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 (strlen(fs_type) == 0) { - ErrorAbort(state, kArgsParsingFailure, "fs_type argument to %s() can't be empty", name); - goto done; + if (fs_type.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "fs_type argument to %s() can't be empty", + name); } - if (strlen(partition_type) == 0) { - ErrorAbort(state, kArgsParsingFailure, "partition_type argument to %s() can't be empty", - name); - goto done; + if (partition_type.empty()) { + return ErrorAbort(state, kArgsParsingFailure, + "partition_type argument to %s() can't be empty", name); } - if (strlen(location) == 0) { - ErrorAbort(state, kArgsParsingFailure, "location argument to %s() can't be empty", name); - goto done; + 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); } - if (strlen(mount_point) == 0) { - ErrorAbort(state, kArgsParsingFailure, "mount_point argument to %s() can't be empty", - name); - goto done; + int64_t size; + if (!android::base::ParseInt(fs_size, &size)) { + return ErrorAbort(state, kArgsParsingFailure, + "%s: failed to parse int in %s\n", name, fs_size.c_str()); } - if (strcmp(fs_type, "ext4") == 0) { - int status = make_ext4fs(location, atoll(fs_size), mount_point, sehandle); + if (fs_type == "ext4") { + int status = make_ext4fs(location.c_str(), size, mount_point.c_str(), sehandle); if (status != 0) { - printf("%s: make_ext4fs failed (%d) on %s", - name, status, location); - result = strdup(""); - goto done; + printf("%s: make_ext4fs failed (%d) on %s", name, status, location.c_str()); + return StringValue(""); } - result = location; - } else if (strcmp(fs_type, "f2fs") == 0) { - char *num_sectors; - if (asprintf(&num_sectors, "%lld", atoll(fs_size) / 512) <= 0) { - printf("format_volume: failed to create %s command for %s\n", fs_type, location); - result = strdup(""); - goto done; + return StringValue(location); + } else if (fs_type == "f2fs") { + if (size < 0) { + printf("%s: fs_size can't be negative for f2fs: %s", name, fs_size.c_str()); + return StringValue(""); } + std::string num_sectors = std::to_string(size / 512); + const char *f2fs_path = "/sbin/mkfs.f2fs"; - const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", location, num_sectors, NULL}; + const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", location.c_str(), + num_sectors.c_str(), nullptr}; int status = exec_cmd(f2fs_path, (char* const*)f2fs_argv); - free(num_sectors); if (status != 0) { - printf("%s: mkfs.f2fs failed (%d) on %s", - name, status, location); - result = strdup(""); - goto done; + printf("%s: mkfs.f2fs failed (%d) on %s", name, status, location.c_str()); + return StringValue(""); } - result = location; + return StringValue(location); } else { printf("%s: unsupported fs_type \"%s\" partition_type \"%s\"", - name, fs_type, partition_type); + name, fs_type.c_str(), partition_type.c_str()); } -done: - free(fs_type); - free(partition_type); - if (result != location) free(location); - return StringValue(result); + return nullptr; } +// rename(src_name, dst_name) +// Renames src_name to dst_name. It automatically creates the necessary directories for dst_name. +// Example: rename("system/app/Hangouts/Hangouts.apk", "system/priv-app/Hangouts/Hangouts.apk") Value* RenameFn(const char* name, State* state, int argc, Expr* argv[]) { - char* result = nullptr; if (argc != 2) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); } - char* src_name; - char* dst_name; - - if (ReadArgs(state, argv, 2, &src_name, &dst_name) < 0) { - return NULL; + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); } - if (strlen(src_name) == 0) { - ErrorAbort(state, kArgsParsingFailure, "src_name argument to %s() can't be empty", name); - goto done; + const std::string& src_name = args[0]; + const std::string& dst_name = args[1]; + + if (src_name.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "src_name argument to %s() can't be empty", + name); } - if (strlen(dst_name) == 0) { - ErrorAbort(state, kArgsParsingFailure, "dst_name argument to %s() can't be empty", name); - goto done; + if (dst_name.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "dst_name argument to %s() can't be empty", + name); } - if (make_parents(dst_name) != 0) { - ErrorAbort(state, kFileRenameFailure, "Creating parent of %s failed, error %s", - dst_name, strerror(errno)); - } else if (access(dst_name, F_OK) == 0 && access(src_name, F_OK) != 0) { + if (!make_parents(dst_name)) { + return ErrorAbort(state, kFileRenameFailure, "Creating parent of %s failed, error %s", + dst_name.c_str(), strerror(errno)); + } else if (access(dst_name.c_str(), F_OK) == 0 && access(src_name.c_str(), F_OK) != 0) { // File was already moved - result = dst_name; - } else if (rename(src_name, dst_name) != 0) { - ErrorAbort(state, kFileRenameFailure, "Rename of %s to %s failed, error %s", - src_name, dst_name, strerror(errno)); - } else { - result = dst_name; + return StringValue(dst_name); + } else if (rename(src_name.c_str(), dst_name.c_str()) != 0) { + return ErrorAbort(state, kFileRenameFailure, "Rename of %s to %s failed, error %s", + src_name.c_str(), dst_name.c_str(), strerror(errno)); } -done: - free(src_name); - if (result != dst_name) free(dst_name); - return StringValue(result); + return StringValue(dst_name); } +// delete([filename, ...]) +// Deletes all the filenames listed. Returns the number of files successfully deleted. +// +// delete_recursive([dirname, ...]) +// Recursively deletes dirnames and all their contents. Returns the number of directories +// successfully deleted. Value* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) { - std::vector<std::string> paths; + std::vector<std::string> paths(argc); for (int i = 0; i < argc; ++i) { if (!Evaluate(state, argv[i], &paths[i])) { return nullptr; @@ -416,7 +397,7 @@ Value* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) { } } - return StringValue(android::base::StringPrintf("%d", success)); + return StringValue(std::to_string(success)); } @@ -424,20 +405,28 @@ Value* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); } - char* frac_str; - char* sec_str; - if (ReadArgs(state, argv, 2, &frac_str, &sec_str) < 0) { - return NULL; + + std::vector<std::string> args; + if (!ReadArgs(state, argc, 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 = strtod(frac_str, NULL); + double frac; + if (!android::base::ParseDouble(frac_str.c_str(), &frac)) { + return ErrorAbort(state, kArgsParsingFailure, + "%s: failed to parse double in %s\n", name, frac_str.c_str()); + } int sec; - android::base::ParseInt(sec_str, &sec); + if (!android::base::ParseInt(sec_str.c_str(), &sec)) { + return ErrorAbort(state, kArgsParsingFailure, + "%s: failed to parse int in %s\n", name, sec_str.c_str()); + } UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec); - free(sec_str); return StringValue(frac_str); } @@ -445,12 +434,18 @@ Value* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 1) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); } - char* frac_str; - if (ReadArgs(state, argv, 1, &frac_str) < 0) { - return NULL; + + std::vector<std::string> args; + if (!ReadArgs(state, 1, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); } + const std::string& frac_str = args[0]; - double frac = strtod(frac_str, NULL); + double frac; + if (!android::base::ParseDouble(frac_str.c_str(), &frac)) { + return ErrorAbort(state, kArgsParsingFailure, + "%s: failed to parse double in %s\n", name, frac_str.c_str()); + } UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); fprintf(ui->cmd_pipe, "set_progress %f\n", frac); @@ -464,9 +459,13 @@ Value* PackageExtractDirFn(const char* name, State* state, if (argc != 2) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); } - char* zip_path; - char* dest_path; - if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; + + std::vector<std::string> args; + if (!ReadArgs(state, 2, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& zip_path = args[0]; + const std::string& dest_path = args[1]; ZipArchiveHandle za = ((UpdaterInfo*)(state->cookie))->package_zip; @@ -475,8 +474,6 @@ Value* PackageExtractDirFn(const char* name, State* state, bool success = ExtractPackageRecursive(za, zip_path, dest_path, ×tamp, sehandle); - free(zip_path); - free(dest_path); return StringValue(success ? "t" : ""); } @@ -499,54 +496,57 @@ Value* PackageExtractFileFn(const char* name, State* state, ZipArchiveHandle za = ((UpdaterInfo*)(state->cookie))->package_zip; - char* zip_path; - char* dest_path; - if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; + std::vector<std::string> args; + if (!ReadArgs(state, 2, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %d args", name, + argc); + } + const std::string& zip_path = args[0]; + const std::string& dest_path = args[1]; - ZipString zip_string_path(zip_path); + ZipString zip_string_path(zip_path.c_str()); ZipEntry entry; if (FindEntry(za, zip_string_path, &entry) != 0) { - printf("%s: no %s in package\n", name, zip_path); - goto done2; + printf("%s: no %s in package\n", name, zip_path.c_str()); + return StringValue(""); } - { - int fd = TEMP_FAILURE_RETRY(ota_open(dest_path, O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR)); - if (fd == -1) { - printf("%s: can't open %s for write: %s\n", name, dest_path, strerror(errno)); - goto done2; - } - success = ExtractEntryToFile(za, &entry, fd); - if (ota_fsync(fd) == -1) { - printf("fsync of \"%s\" failed: %s\n", dest_path, strerror(errno)); - success = false; - } - if (ota_close(fd) == -1) { - printf("close of \"%s\" failed: %s\n", dest_path, strerror(errno)); - success = false; - } + int fd = TEMP_FAILURE_RETRY(ota_open(dest_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR)); + if (fd == -1) { + printf("%s: can't open %s for write: %s\n", name, dest_path.c_str(), strerror(errno)); + return StringValue(""); + } + success = ExtractEntryToFile(za, &entry, fd); + if (ota_fsync(fd) == -1) { + printf("fsync of \"%s\" failed: %s\n", dest_path.c_str(), strerror(errno)); + success = false; + } + if (ota_close(fd) == -1) { + printf("close of \"%s\" failed: %s\n", dest_path.c_str(), strerror(errno)); + success = false; } - done2: - free(zip_path); - free(dest_path); return StringValue(success ? "t" : ""); } else { // The one-argument version returns the contents of the file // as the result. - char* zip_path; - if (ReadArgs(state, argv, 1, &zip_path) < 0) return NULL; + std::vector<std::string> args; + if (!ReadArgs(state, 1, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %d args", name, + argc); + } + const std::string& zip_path = args[0]; Value* v = new Value(VAL_INVALID, ""); ZipArchiveHandle za = ((UpdaterInfo*)(state->cookie))->package_zip; - ZipString zip_string_path(zip_path); + ZipString zip_string_path(zip_path.c_str()); ZipEntry entry; if (FindEntry(za, zip_string_path, &entry) != 0) { - printf("%s: no %s in package\n", name, zip_path); - goto done1; + printf("%s: no %s in package\n", name, zip_path.c_str()); + return v; } v->data.resize(entry.uncompressed_length); @@ -557,8 +557,6 @@ Value* PackageExtractFileFn(const char* name, State* state, success = true; } - done1: - free(zip_path); if (!success) { v->data.clear(); } else { @@ -579,34 +577,31 @@ Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { return nullptr; } - char** srcs = ReadVarArgs(state, argc-1, argv+1); - if (srcs == NULL) { - return NULL; + std::vector<std::string> srcs; + if (!ReadArgs(state, argc-1, argv+1, &srcs)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); } int bad = 0; - int i; - for (i = 0; i < argc-1; ++i) { - if (unlink(srcs[i]) < 0) { + for (int i = 0; i < argc-1; ++i) { + if (unlink(srcs[i].c_str()) < 0) { if (errno != ENOENT) { printf("%s: failed to remove %s: %s\n", - name, srcs[i], strerror(errno)); + name, srcs[i].c_str(), strerror(errno)); ++bad; } } - if (make_parents(srcs[i])) { + if (!make_parents(srcs[i])) { printf("%s: failed to symlink %s to %s: making parents failed\n", - name, srcs[i], target.c_str()); + name, srcs[i].c_str(), target.c_str()); ++bad; } - if (symlink(target.c_str(), srcs[i]) < 0) { + if (symlink(target.c_str(), srcs[i].c_str()) < 0) { printf("%s: failed to symlink %s to %s: %s\n", - name, srcs[i], target.c_str(), strerror(errno)); + name, srcs[i].c_str(), target.c_str(), strerror(errno)); ++bad; } - free(srcs[i]); } - free(srcs); if (bad) { return ErrorAbort(state, kSymlinkFailure, "%s: some symlinks failed", name); } @@ -625,12 +620,13 @@ struct perm_parsed_args { bool has_dmode; mode_t dmode; bool has_selabel; - char* selabel; + const char* selabel; bool has_capabilities; uint64_t capabilities; }; -static struct perm_parsed_args ParsePermArgs(State * state, int argc, char** args) { +static struct perm_parsed_args ParsePermArgs(State * state, int argc, + const std::vector<std::string>& args) { int i; struct perm_parsed_args parsed; int bad = 0; @@ -639,84 +635,84 @@ static struct perm_parsed_args ParsePermArgs(State * state, int argc, char** arg memset(&parsed, 0, sizeof(parsed)); for (i = 1; i < argc; i += 2) { - if (strcmp("uid", args[i]) == 0) { + if (args[i] == "uid") { int64_t uid; - if (sscanf(args[i+1], "%" SCNd64, &uid) == 1) { + if (sscanf(args[i+1].c_str(), "%" SCNd64, &uid) == 1) { parsed.uid = uid; parsed.has_uid = true; } else { - uiPrintf(state, "ParsePermArgs: invalid UID \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid UID \"%s\"\n", args[i + 1].c_str()); bad++; } continue; } - if (strcmp("gid", args[i]) == 0) { + if (args[i] == "gid") { int64_t gid; - if (sscanf(args[i+1], "%" SCNd64, &gid) == 1) { + if (sscanf(args[i+1].c_str(), "%" SCNd64, &gid) == 1) { parsed.gid = gid; parsed.has_gid = true; } else { - uiPrintf(state, "ParsePermArgs: invalid GID \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid GID \"%s\"\n", args[i + 1].c_str()); bad++; } continue; } - if (strcmp("mode", args[i]) == 0) { + if (args[i] == "mode") { int32_t mode; - if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) { + if (sscanf(args[i+1].c_str(), "%" SCNi32, &mode) == 1) { parsed.mode = mode; parsed.has_mode = true; } else { - uiPrintf(state, "ParsePermArgs: invalid mode \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid mode \"%s\"\n", args[i + 1].c_str()); bad++; } continue; } - if (strcmp("dmode", args[i]) == 0) { + if (args[i] == "dmode") { int32_t mode; - if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) { + if (sscanf(args[i+1].c_str(), "%" SCNi32, &mode) == 1) { parsed.dmode = mode; parsed.has_dmode = true; } else { - uiPrintf(state, "ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1].c_str()); bad++; } continue; } - if (strcmp("fmode", args[i]) == 0) { + if (args[i] == "fmode") { int32_t mode; - if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) { + if (sscanf(args[i+1].c_str(), "%" SCNi32, &mode) == 1) { parsed.fmode = mode; parsed.has_fmode = true; } else { - uiPrintf(state, "ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1].c_str()); bad++; } continue; } - if (strcmp("capabilities", args[i]) == 0) { + if (args[i] == "capabilities") { int64_t capabilities; - if (sscanf(args[i+1], "%" SCNi64, &capabilities) == 1) { + if (sscanf(args[i+1].c_str(), "%" SCNi64, &capabilities) == 1) { parsed.capabilities = capabilities; parsed.has_capabilities = true; } else { - uiPrintf(state, "ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1].c_str()); bad++; } continue; } - if (strcmp("selabel", args[i]) == 0) { - if (args[i+1][0] != '\0') { - parsed.selabel = args[i+1]; + if (args[i] == "selabel") { + if (!args[i+1].empty()) { + parsed.selabel = args[i+1].c_str(); parsed.has_selabel = true; } else { - uiPrintf(state, "ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1].c_str()); bad++; } continue; } if (max_warnings != 0) { - printf("ParsedPermArgs: unknown key \"%s\", ignoring\n", args[i]); + printf("ParsedPermArgs: unknown key \"%s\", ignoring\n", args[i].c_str()); max_warnings--; if (max_warnings == 0) { printf("ParsedPermArgs: suppressing further warnings\n"); @@ -825,48 +821,34 @@ static int do_SetMetadataRecursive(const char* filename, const struct stat *stat } static Value* SetMetadataFn(const char* name, State* state, int argc, Expr* argv[]) { - int bad = 0; - struct stat sb; - Value* result = NULL; - - bool recursive = (strcmp(name, "set_metadata_recursive") == 0); - if ((argc % 2) != 1) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects an odd number of arguments, got %d", name, argc); } - char** args = ReadVarArgs(state, argc, argv); - if (args == NULL) return NULL; - - if (lstat(args[0], &sb) == -1) { - result = ErrorAbort(state, kSetMetadataFailure, "%s: Error on lstat of \"%s\": %s", - name, args[0], strerror(errno)); - goto done; + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); } - { - struct perm_parsed_args parsed = ParsePermArgs(state, argc, args); - - if (recursive) { - recursive_parsed_args = parsed; - recursive_state = state; - bad += nftw(args[0], do_SetMetadataRecursive, 30, FTW_CHDIR | FTW_DEPTH | FTW_PHYS); - memset(&recursive_parsed_args, 0, sizeof(recursive_parsed_args)); - recursive_state = NULL; - } else { - bad += ApplyParsedPerms(state, args[0], &sb, parsed); - } + struct stat sb; + if (lstat(args[0].c_str(), &sb) == -1) { + return ErrorAbort(state, kSetMetadataFailure, "%s: Error on lstat of \"%s\": %s", + name, args[0].c_str(), strerror(errno)); } -done: - for (int i = 0; i < argc; ++i) { - free(args[i]); - } - free(args); + struct perm_parsed_args parsed = ParsePermArgs(state, argc, args); + int bad = 0; + bool recursive = (strcmp(name, "set_metadata_recursive") == 0); - if (result != NULL) { - return result; + if (recursive) { + recursive_parsed_args = parsed; + recursive_state = state; + bad += nftw(args[0].c_str(), do_SetMetadataRecursive, 30, FTW_CHDIR | FTW_DEPTH | FTW_PHYS); + memset(&recursive_parsed_args, 0, sizeof(recursive_parsed_args)); + recursive_state = NULL; + } else { + bad += ApplyParsedPerms(state, args[0].c_str(), &sb, parsed); } if (bad > 0) { @@ -893,116 +875,86 @@ Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) { // file_getprop(file, key) // // interprets 'file' as a getprop-style file (key=value pairs, one -// per line. # comment lines,blank lines, lines without '=' ignored), +// 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, int argc, Expr* argv[]) { - char* result = NULL; - char* buffer = NULL; - char* filename; - char* key; - if (ReadArgs(state, argv, 2, &filename, &key) < 0) { - return NULL; + if (argc != 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); } - struct stat st; - if (stat(filename, &st) < 0) { - ErrorAbort(state, kFileGetPropFailure, "%s: failed to stat \"%s\": %s", name, filename, - strerror(errno)); - goto done; + std::vector<std::string> args; + if (!ReadArgs(state, 2, 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]; -#define MAX_FILE_GETPROP_SIZE 65536 - - if (st.st_size > MAX_FILE_GETPROP_SIZE) { - ErrorAbort(state, kFileGetPropFailure, "%s too large for %s (max %d)", filename, name, - MAX_FILE_GETPROP_SIZE); - goto done; + struct stat st; + if (stat(filename.c_str(), &st) < 0) { + return ErrorAbort(state, kFileGetPropFailure, "%s: failed to stat \"%s\": %s", name, + filename.c_str(), strerror(errno)); } - buffer = reinterpret_cast<char*>(malloc(st.st_size+1)); - if (buffer == NULL) { - ErrorAbort(state, kFileGetPropFailure, "%s: failed to alloc %zu bytes", name, - static_cast<size_t>(st.st_size+1)); - goto done; + constexpr off_t MAX_FILE_GETPROP_SIZE = 65536; + if (st.st_size > MAX_FILE_GETPROP_SIZE) { + return ErrorAbort(state, kFileGetPropFailure, "%s too large for %s (max %lld)", + filename.c_str(), name, static_cast<long long>(MAX_FILE_GETPROP_SIZE)); } - FILE* f; - f = ota_fopen(filename, "rb"); - if (f == NULL) { - ErrorAbort(state, kFileOpenFailure, "%s: failed to open %s: %s", name, filename, - strerror(errno)); - goto done; + std::string buffer(st.st_size, '\0'); + FILE* f = ota_fopen(filename.c_str(), "rb"); + if (f == nullptr) { + return ErrorAbort(state, kFileOpenFailure, "%s: failed to open %s: %s", name, + filename.c_str(), strerror(errno)); } - if (ota_fread(buffer, 1, st.st_size, f) != static_cast<size_t>(st.st_size)) { + if (ota_fread(&buffer[0], 1, st.st_size, f) != static_cast<size_t>(st.st_size)) { ErrorAbort(state, kFreadFailure, "%s: failed to read %zu bytes from %s", - name, static_cast<size_t>(st.st_size), filename); + name, static_cast<size_t>(st.st_size), filename.c_str()); ota_fclose(f); - goto done; + return nullptr; } - buffer[st.st_size] = '\0'; ota_fclose(f); - char* line; - line = strtok(buffer, "\n"); - do { - // skip whitespace at start of line - while (*line && isspace(*line)) ++line; + 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 == '\0' || *line == '#') continue; - - char* equal = strchr(line, '='); - if (equal == NULL) { + if (line.empty() || line[0] == '#') { + continue; + } + size_t equal_pos = line.find('='); + if (equal_pos == std::string::npos) { continue; } // trim whitespace between key and '=' - char* key_end = equal-1; - while (key_end > line && isspace(*key_end)) --key_end; - key_end[1] = '\0'; + std::string str = android::base::Trim(line.substr(0, equal_pos)); // not the key we're looking for - if (strcmp(key, line) != 0) continue; - - // skip whitespace after the '=' to the start of the value - char* val_start = equal+1; - while(*val_start && isspace(*val_start)) ++val_start; + if (key != str) continue; - // trim trailing whitespace - char* val_end = val_start + strlen(val_start)-1; - while (val_end > val_start && isspace(*val_end)) --val_end; - val_end[1] = '\0'; - - result = strdup(val_start); - break; - - } while ((line = strtok(NULL, "\n"))); - - if (result == NULL) result = strdup(""); + return StringValue(android::base::Trim(line.substr(equal_pos + 1))); + } - done: - free(filename); - free(key); - free(buffer); - return StringValue(result); + return StringValue(""); } // apply_patch_space(bytes) Value* ApplyPatchSpaceFn(const char* name, State* state, int argc, Expr* argv[]) { - char* bytes_str; - if (ReadArgs(state, argv, 1, &bytes_str) < 0) { - return NULL; + std::vector<std::string> args; + if (!ReadArgs(state, 1, 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, &bytes)) { - ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count\n\n", - name, bytes_str); - free(bytes_str); - return nullptr; + if (!android::base::ParseUint(bytes_str.c_str(), &bytes)) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count\n\n", + name, bytes_str.c_str()); } return StringValue(CacheSizeCheck(bytes) ? "" : "t"); @@ -1013,63 +965,51 @@ Value* ApplyPatchSpaceFn(const char* name, State* state, Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc < 6 || (argc % 2) == 1) { return ErrorAbort(state, kArgsParsingFailure, "%s(): expected at least 6 args and an " - "even number, got %d", name, argc); + "even number, got %d", name, argc); } - char* source_filename; - char* target_filename; - char* target_sha1; - char* target_size_str; - if (ReadArgs(state, argv, 4, &source_filename, &target_filename, - &target_sha1, &target_size_str) < 0) { - return NULL; + std::vector<std::string> args; + if (!ReadArgs(state, 4, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); } + const std::string& source_filename = args[0]; + const std::string& target_filename = args[1]; + const std::string& target_sha1 = args[2]; + const std::string& target_size_str = args[3]; size_t target_size; - if (!android::base::ParseUint(target_size_str, &target_size)) { - ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count", - name, target_size_str); - free(source_filename); - free(target_filename); - free(target_sha1); - free(target_size_str); - return nullptr; + if (!android::base::ParseUint(target_size_str.c_str(), &target_size)) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count", + name, target_size_str.c_str()); } int patchcount = (argc-4) / 2; - std::unique_ptr<Value*> arg_values(ReadValueVarArgs(state, argc-4, argv+4)); - if (!arg_values) { + std::vector<std::unique_ptr<Value>> arg_values; + if (!ReadValueArgs(state, argc-4, argv+4, &arg_values)) { return nullptr; } - std::vector<std::unique_ptr<Value>> patch_shas; - std::vector<std::unique_ptr<Value>> patches; - // Protect values by unique_ptrs first to get rid of memory leak. - for (int i = 0; i < patchcount * 2; i += 2) { - patch_shas.emplace_back(arg_values.get()[i]); - patches.emplace_back(arg_values.get()[i+1]); - } for (int i = 0; i < patchcount; ++i) { - if (patch_shas[i]->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "%s(): sha-1 #%d is not string", name, i); - return nullptr; + if (arg_values[i * 2]->type != VAL_STRING) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): sha-1 #%d is not string", name, + i * 2); } - if (patches[i]->type != VAL_BLOB) { - ErrorAbort(state, kArgsParsingFailure, "%s(): patch #%d is not blob", name, i); - return nullptr; + if (arg_values[i * 2 + 1]->type != VAL_BLOB) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): patch #%d is not blob", name, + i * 2 + 1); } } std::vector<std::string> patch_sha_str; - std::vector<Value*> patch_ptrs; + std::vector<std::unique_ptr<Value>> patches; for (int i = 0; i < patchcount; ++i) { - patch_sha_str.push_back(patch_shas[i]->data); - patch_ptrs.push_back(patches[i].get()); + patch_sha_str.push_back(arg_values[i * 2]->data); + patches.push_back(std::move(arg_values[i * 2 + 1])); } - int result = applypatch(source_filename, target_filename, - target_sha1, target_size, - patch_sha_str, patch_ptrs.data(), NULL); + int result = applypatch(source_filename.c_str(), target_filename.c_str(), + target_sha1.c_str(), target_size, + patch_sha_str, patches, nullptr); return StringValue(result == 0 ? "t" : ""); } @@ -1082,17 +1022,17 @@ Value* ApplyPatchCheckFn(const char* name, State* state, name, argc); } - char* filename; - if (ReadArgs(state, argv, 1, &filename) < 0) { - return nullptr; + std::vector<std::string> args; + if (!ReadArgs(state, 1, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); } + const std::string& filename = args[0]; std::vector<std::string> sha1s; - if (!ReadArgs(state, argc-1, argv+1, &sha1s)) { - return nullptr; + if (!ReadArgs(state, argc - 1, argv + 1, &sha1s)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); } - - int result = applypatch_check(filename, sha1s); + int result = applypatch_check(filename.c_str(), sha1s); return StringValue(result == 0 ? "t" : ""); } @@ -1100,19 +1040,12 @@ Value* ApplyPatchCheckFn(const char* name, State* state, // 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, int argc, Expr* argv[]) { - char** args = ReadVarArgs(state, argc, argv); - if (args == NULL) { - return NULL; + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); } - std::string buffer; - for (int i = 0; i < argc; ++i) { - buffer += args[i]; - free(args[i]); - } - free(args); - - buffer += "\n"; + std::string buffer = android::base::Join(args, "") + "\n"; uiPrint(state, buffer); return StringValue(buffer); } @@ -1129,14 +1062,17 @@ Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc < 1) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name); } - char** args = ReadVarArgs(state, argc, argv); - if (args == NULL) { - return NULL; + + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); } - char** args2 = reinterpret_cast<char**>(malloc(sizeof(char*) * (argc+1))); - memcpy(args2, args, sizeof(char*) * argc); - args2[argc] = NULL; + char* args2[argc+1]; + for (int i = 0; i < argc; i++) { + args2[i] = &args[i][0]; + } + args2[argc] = nullptr; printf("about to run program [%s] with %d args\n", args2[0], argc); @@ -1158,13 +1094,6 @@ Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) { WTERMSIG(status)); } - int i; - for (i = 0; i < argc; ++i) { - free(args[i]); - } - free(args); - free(args2); - return StringValue(android::base::StringPrintf("%d", status)); } @@ -1181,13 +1110,9 @@ Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name); } - std::unique_ptr<Value*> arg_values(ReadValueVarArgs(state, argc, argv)); - if (arg_values == nullptr) { - return nullptr; - } std::vector<std::unique_ptr<Value>> args; - for (int i = 0; i < argc; ++i) { - args.emplace_back(arg_values.get()[i]); + if (!ReadValueArgs(state, argc, argv, &args)) { + return nullptr; } if (args[0]->type == VAL_INVALID) { @@ -1223,17 +1148,20 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 1) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); } - char* filename; - if (ReadArgs(state, argv, 1, &filename) < 0) return NULL; + + std::vector<std::string> args; + if (!ReadArgs(state, 1, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& filename = args[0]; Value* v = new Value(VAL_INVALID, ""); FileContents fc; - if (LoadFileContents(filename, &fc) == 0) { + if (LoadFileContents(filename.c_str(), &fc) == 0) { v->type = VAL_BLOB; v->data = std::string(fc.data.begin(), fc.data.end()); } - free(filename); return v; } @@ -1251,27 +1179,27 @@ Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); } - char* filename; - char* property; - if (ReadArgs(state, argv, 2, &filename, &property) < 0) return NULL; + std::vector<std::string> args; + if (!ReadArgs(state, 2, 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. char buffer[80]; memset(buffer, 0, sizeof(((struct bootloader_message*)0)->command)); - FILE* f = ota_fopen(filename, "r+b"); + FILE* f = ota_fopen(filename.c_str(), "r+b"); fseek(f, offsetof(struct bootloader_message, command), SEEK_SET); ota_fwrite(buffer, sizeof(((struct bootloader_message*)0)->command), 1, f); ota_fclose(f); - free(filename); std::string reboot_cmd = "reboot,"; - if (property != nullptr) reboot_cmd += property; + reboot_cmd += property; android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd); sleep(5); - free(property); - ErrorAbort(state, kRebootFailure, "%s() failed to reboot", name); - return NULL; + return ErrorAbort(state, kRebootFailure, "%s() failed to reboot", name); } // Store a string value somewhere that future invocations of recovery @@ -1289,26 +1217,28 @@ Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); } - char* filename; - char* stagestr; - if (ReadArgs(state, argv, 2, &filename, &stagestr) < 0) return NULL; + std::vector<std::string> args; + if (!ReadArgs(state, 2, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& filename = args[0]; + 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. - FILE* f = ota_fopen(filename, "r+b"); + FILE* f = ota_fopen(filename.c_str(), "r+b"); fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET); - size_t to_write = strlen(stagestr) + 1; + size_t to_write = stagestr.size(); size_t max_size = sizeof(((struct bootloader_message*)0)->stage); if (to_write > max_size) { to_write = max_size; - stagestr[max_size - 1] = 0; + stagestr = stagestr.substr(0, max_size-1); } - size_t status = ota_fwrite(stagestr, to_write, 1, f); + size_t status = ota_fwrite(stagestr.c_str(), to_write, 1, f); ota_fclose(f); - free(stagestr); if (status != to_write) { return StringValue(""); } @@ -1322,11 +1252,14 @@ Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); } - char* filename; - if (ReadArgs(state, argv, 1, &filename) < 0) return NULL; + std::vector<std::string> args; + if (!ReadArgs(state, 1, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& filename = args[0]; char buffer[sizeof(((struct bootloader_message*)0)->stage)]; - FILE* f = ota_fopen(filename, "rb"); + FILE* f = ota_fopen(filename.c_str(), "rb"); fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET); size_t status = ota_fread(buffer, sizeof(buffer), 1, f); ota_fclose(f); @@ -1343,21 +1276,25 @@ Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[]) return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); } - char* filename; - char* len_str; - if (ReadArgs(state, argv, 2, &filename, &len_str) < 0) return NULL; + std::vector<std::string> args; + if (!ReadArgs(state, 2, 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; - android::base::ParseUint(len_str, &len); - int fd = ota_open(filename, O_WRONLY, 0644); - int success = wipe_block_device(fd, len); - - free(filename); - free(len_str); + if (!android::base::ParseUint(len_str.c_str(), &len)) { + return nullptr; + } + int fd = ota_open(filename.c_str(), O_WRONLY, 0644); + // The wipe_block_device function in ext4_utils returns 0 on success and 1 + // for failure. + int status = wipe_block_device(fd, len); ota_close(fd); - return StringValue(success ? "t" : ""); + return StringValue((status == 0) ? "t" : ""); } Value* EnableRebootFn(const char* name, State* state, int argc, Expr* argv[]) { @@ -1374,28 +1311,27 @@ Value* Tune2FsFn(const char* name, State* state, int argc, Expr* argv[]) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects args, got %d", name, argc); } - char** args = ReadVarArgs(state, argc, argv); - if (args == NULL) { + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { return ErrorAbort(state, kArgsParsingFailure, "%s() could not read args", name); } - char** args2 = reinterpret_cast<char**>(malloc(sizeof(char*) * (argc+1))); + char* args2[argc+1]; // Tune2fs expects the program name as its args[0] - args2[0] = strdup(name); - for (int i = 0; i < argc; ++i) { - args2[i + 1] = args[i]; + args2[0] = const_cast<char*>(name); + if (args2[0] == nullptr) { + return nullptr; } - int result = tune2fs_main(argc + 1, args2); for (int i = 0; i < argc; ++i) { - free(args[i]); + args2[i + 1] = &args[i][0]; } - free(args); - free(args2[0]); - free(args2); + // tune2fs changes the file system parameters on an ext2 file system; it + // returns 0 on success. + int result = tune2fs_main(argc + 1, args2); + if (result != 0) { - return ErrorAbort(state, kTune2FsFailure, "%s() returned error code %d", - name, result); + return ErrorAbort(state, kTune2FsFailure, "%s() returned error code %d", name, result); } return StringValue("t"); } diff --git a/verifier.cpp b/verifier.cpp index 82cdd3bc7..44098f70e 100644 --- a/verifier.cpp +++ b/verifier.cpp @@ -14,9 +14,11 @@ * limitations under the License. */ +#include "verifier.h" + #include <errno.h> -#include <malloc.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <algorithm> @@ -31,9 +33,6 @@ #include "common.h" #include "print_sha1.h" #include "ui.h" -#include "verifier.h" - -extern RecoveryUI* ui; static constexpr size_t MiB = 1024 * 1024; |