diff --git a/Android.mk b/Android.mk index bb22a3eb..1e2141dc 100644 --- a/Android.mk +++ b/Android.mk @@ -68,14 +68,17 @@ LOCAL_MODULE := recovery #LOCAL_FORCE_STATIC_EXECUTABLE := true +#ifeq ($(TARGET_USERIMAGES_USE_F2FS),true) #ifeq ($(HOST_OS),linux) #LOCAL_REQUIRED_MODULES := mkfs.f2fs #endif +#endif RECOVERY_API_VERSION := 3 RECOVERY_FSTAB_VERSION := 2 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) LOCAL_CFLAGS += -Wno-unused-parameter +LOCAL_CLANG := true #LOCAL_STATIC_LIBRARIES := \ # libext4_utils_static \ @@ -103,7 +106,7 @@ LOCAL_C_INCLUDES += \ system/core/libsparse \ external/zlib -LOCAL_C_INCLUDES += bionic external/openssl/include $(LOCAL_PATH)/libmincrypt/includes +LOCAL_C_INCLUDES += bionic external/openssl/include ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) LOCAL_C_INCLUDES += external/stlport/stlport endif @@ -112,8 +115,8 @@ LOCAL_STATIC_LIBRARIES := LOCAL_SHARED_LIBRARIES := LOCAL_STATIC_LIBRARIES += libguitwrp -LOCAL_SHARED_LIBRARIES += libz libc libcutils libstdc++ libtar libblkid libminuitwrp libminadbd libmtdutils libminzip libaosprecovery libtwadbbu -LOCAL_SHARED_LIBRARIES += libcrecovery +LOCAL_SHARED_LIBRARIES += libaosprecovery libz libc libcutils libstdc++ libtar libblkid libminuitwrp libminadbd libmtdutils libminzip libtwadbbu +LOCAL_SHARED_LIBRARIES += libcrecovery libbase libcrypto ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) LOCAL_SHARED_LIBRARIES += libstlport @@ -132,6 +135,7 @@ ifeq ($(TW_OEM_BUILD),true) TW_EXCLUDE_SUPERSU := true TW_EXCLUDE_MTP := true endif + ifeq ($(TARGET_USERIMAGES_USE_EXT4), true) LOCAL_CFLAGS += -DUSE_EXT4 LOCAL_C_INCLUDES += system/extras/ext4_utils @@ -336,7 +340,6 @@ LOCAL_ADDITIONAL_DEPENDENCIES := \ mke2fs.conf \ pigz \ teamwin \ - toolbox_symlinks \ twrp \ unpigz_symlink \ fsck.fat \ @@ -381,7 +384,7 @@ ifeq ($(BOARD_HAS_NO_REAL_SDCARD),) endif endif ifneq ($(TW_EXCLUDE_ENCRYPTED_BACKUPS), true) - LOCAL_ADDITIONAL_DEPENDENCIES += openaes ../openaes/LICENSE + LOCAL_ADDITIONAL_DEPENDENCIES += openaes openaes_license endif ifeq ($(TW_INCLUDE_DUMLOCK), true) LOCAL_ADDITIONAL_DEPENDENCIES += \ @@ -448,6 +451,10 @@ ifeq ($(shell test $(CM_PLATFORM_SDK_VERSION) -ge 3; echo $$?),0) endif endif +ifeq ($(BOARD_CACHEIMAGE_PARTITION_SIZE),) +LOCAL_REQUIRED_MODULES := recovery-persist recovery-refresh +endif + include $(BUILD_EXECUTABLE) ifneq ($(TW_USE_TOOLBOX), true) @@ -494,56 +501,47 @@ include $(BUILD_PHONY_PACKAGE) RECOVERY_BUSYBOX_SYMLINKS := endif # !TW_USE_TOOLBOX -# All the APIs for testing +# recovery-persist (system partition dynamic executable run after /data mounts) +# =============================== include $(CLEAR_VARS) -LOCAL_MODULE := libverifier -LOCAL_MODULE_TAGS := tests -LOCAL_SRC_FILES := \ - asn1_decoder.cpp -include $(BUILD_STATIC_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := fuse_sideload.c - -LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter -LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE := libfusesideload -LOCAL_C_INCLUDES := $(LOCAL_PATH)/libmincrypt/includes -LOCAL_SHARED_LIBRARIES := libcutils libc libmincrypttwrp -include $(BUILD_SHARED_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := verifier_test -LOCAL_FORCE_STATIC_EXECUTABLE := true -LOCAL_MODULE_TAGS := tests - -LOCAL_C_INCLUDES := $(LOCAL_PATH)/libmincrypt/includes - -LOCAL_CFLAGS += -DNO_RECOVERY_MOUNT -LOCAL_CFLAGS += -Wno-unused-parameter - -LOCAL_SRC_FILES := \ - verifier_test.cpp \ - asn1_decoder.cpp \ - verifier.cpp \ - ui.cpp -LOCAL_STATIC_LIBRARIES := \ - libmincrypttwrp \ - libminui \ - libminzip \ - libcutils \ - libstdc++ \ - libc +LOCAL_SRC_FILES := recovery-persist.cpp +LOCAL_MODULE := recovery-persist +LOCAL_SHARED_LIBRARIES := liblog libbase +LOCAL_CFLAGS := -Werror +LOCAL_INIT_RC := recovery-persist.rc include $(BUILD_EXECUTABLE) +# recovery-refresh (system partition dynamic executable run at init) +# =============================== +include $(CLEAR_VARS) +LOCAL_SRC_FILES := recovery-refresh.cpp +LOCAL_MODULE := recovery-refresh +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_CFLAGS := -Werror +LOCAL_INIT_RC := recovery-refresh.rc +include $(BUILD_EXECUTABLE) + +# shared libfusesideload +# =============================== +include $(CLEAR_VARS) +LOCAL_SRC_FILES := fuse_sideload.cpp +LOCAL_CLANG := true +LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter +LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE + +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := libfusesideload +LOCAL_SHARED_LIBRARIES := libcutils libc libcrypto +include $(BUILD_SHARED_LIBRARY) + +# shared libaosprecovery for Apache code +# =============================== include $(CLEAR_VARS) LOCAL_MODULE := libaosprecovery LOCAL_MODULE_TAGS := eng optional -LOCAL_C_INCLUDES := $(LOCAL_PATH)/libmincrypt/includes LOCAL_SRC_FILES := adb_install.cpp asn1_decoder.cpp bootloader.cpp legacy_property_service.c verifier.cpp set_metadata.c tw_atomic.cpp -LOCAL_SHARED_LIBRARIES += libc liblog libcutils libmtdutils libfusesideload libmincrypttwrp libselinux +LOCAL_SHARED_LIBRARIES += libc liblog libcutils libmtdutils libfusesideload libselinux libcrypto ifneq ($(BOARD_RECOVERY_BLDRMSG_OFFSET),) LOCAL_CFLAGS += -DBOARD_RECOVERY_BLDRMSG_OFFSET=$(BOARD_RECOVERY_BLDRMSG_OFFSET) @@ -551,11 +549,25 @@ endif include $(BUILD_SHARED_LIBRARY) +# All the APIs for testing +include $(CLEAR_VARS) +LOCAL_CLANG := true +LOCAL_MODULE := libverifier +LOCAL_MODULE_TAGS := tests +LOCAL_SRC_FILES := \ + asn1_decoder.cpp \ + verifier.cpp \ + ui.cpp +LOCAL_STATIC_LIBRARIES := libcrypto +include $(BUILD_STATIC_LIBRARY) + commands_recovery_local_path := $(LOCAL_PATH) include $(LOCAL_PATH)/tests/Android.mk \ $(LOCAL_PATH)/tools/Android.mk \ $(LOCAL_PATH)/edify/Android.mk \ + $(LOCAL_PATH)/otafault/Android.mk \ $(LOCAL_PATH)/updater/Android.mk \ + $(LOCAL_PATH)/update_verifier/Android.mk \ $(LOCAL_PATH)/applypatch/Android.mk ifeq ($(wildcard system/core/uncrypt/Android.mk),) @@ -586,8 +598,6 @@ include $(commands_recovery_local_path)/injecttwrp/Android.mk \ $(commands_recovery_local_path)/libblkid/Android.mk \ $(commands_recovery_local_path)/minuitwrp/Android.mk \ $(commands_recovery_local_path)/openaes/Android.mk \ - $(commands_recovery_local_path)/toolbox/Android.mk \ - $(commands_recovery_local_path)/libmincrypt/Android.mk \ $(commands_recovery_local_path)/twrpTarMain/Android.mk \ $(commands_recovery_local_path)/mtp/Android.mk \ $(commands_recovery_local_path)/minzip/Android.mk \ diff --git a/adb_install.cpp b/adb_install.cpp index 5c54468d..d9936f22 100644 --- a/adb_install.cpp +++ b/adb_install.cpp @@ -31,6 +31,7 @@ #include "adb_install.h" #include "minadbd/fuse_adb_provider.h" #include "fuse_sideload.h" +#include "verifier.h" static RecoveryUI* ui = NULL; @@ -106,7 +107,7 @@ apply_from_adb(const char* install_file, pid_t* child_pid) { // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host // connects and starts serving a package. Poll for its // appearance. (Note that inotify doesn't work with FUSE.) - int result; + int result = INSTALL_ERROR; int status; bool waited = false; struct stat st; diff --git a/adbbu/Android.mk b/adbbu/Android.mk index 85e0acc5..2c2d50a7 100644 --- a/adbbu/Android.mk +++ b/adbbu/Android.mk @@ -5,7 +5,7 @@ LOCAL_SRC_FILES:= \ twrpback.cpp \ ../twrpDigest.cpp \ ../digest/md5.c -LOCAL_SHARED_LIBRARIES += libstdc++ libz +LOCAL_SHARED_LIBRARIES += libstdc++ libz libselinux ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) LOCAL_C_INCLUDES += external/stlport/stlport LOCAL_SHARED_LIBRARIES += libstlport diff --git a/applypatch/Android.mk b/applypatch/Android.mk index 2cce81f6..48eab014 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -13,6 +13,7 @@ # limitations under the License. LOCAL_PATH := $(call my-dir) + include $(CLEAR_VARS) BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY @@ -23,47 +24,55 @@ $(foreach board_define,$(BOARD_RECOVERY_DEFINES), \ ) \ ) -LOCAL_SRC_FILES := applypatch.c bspatch.c freecache.c imgpatch.c utils.c -LOCAL_MODULE := libapplypatch -LOCAL_MODULE_TAGS := eng LOCAL_C_INCLUDES += \ external/bzip2 \ external/zlib \ $(commands_recovery_local_path) -LOCAL_STATIC_LIBRARIES += libmtdutils libmincrypttwrp libbz libz + +LOCAL_CLANG := true +LOCAL_SRC_FILES := applypatch.cpp bspatch.cpp freecache.cpp imgpatch.cpp utils.cpp +LOCAL_MODULE := libapplypatch +LOCAL_MODULE_TAGS := eng +LOCAL_C_INCLUDES += bootable/recovery +LOCAL_STATIC_LIBRARIES += libbase libotafault libmtdutils libmincrypttwrp libbz libz include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) -LOCAL_SRC_FILES := main.c +ifeq ($(HOST_OS),linux) +include $(CLEAR_VARS) + +LOCAL_CLANG := true +LOCAL_SRC_FILES := bspatch.cpp imgpatch.cpp utils.cpp +LOCAL_MODULE := libimgpatch +LOCAL_C_INCLUDES += bootable/recovery +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_STATIC_LIBRARIES += libcrypto_static libbz libz + +include $(BUILD_HOST_STATIC_LIBRARY) +endif # HOST_OS == linux + +include $(CLEAR_VARS) + +LOCAL_CLANG := true +LOCAL_SRC_FILES := main.cpp LOCAL_MODULE := applypatch -LOCAL_MODULE_TAGS := optional -LOCAL_C_INCLUDES += $(commands_recovery_local_path) -LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypttwrp libbz -LOCAL_SHARED_LIBRARIES += libz libcutils libstdc++ libc +LOCAL_C_INCLUDES += bootable/recovery +LOCAL_STATIC_LIBRARIES += libapplypatch libbase libotafault libmtdutils libcrypto_static libbz \ + libedify \ + +LOCAL_SHARED_LIBRARIES += libz libcutils libc include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) -LOCAL_SRC_FILES := main.c -LOCAL_MODULE := applypatch_static -LOCAL_FORCE_STATIC_EXECUTABLE := true -LOCAL_MODULE_TAGS := optional eng -LOCAL_C_INCLUDES += $(commands_recovery_local_path) -LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypttwrp libbz -LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc - -include $(BUILD_EXECUTABLE) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := imgdiff.c utils.c bsdiff.c +LOCAL_CLANG := true +LOCAL_SRC_FILES := imgdiff.cpp utils.cpp bsdiff.cpp LOCAL_MODULE := imgdiff LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_C_INCLUDES += external/zlib external/bzip2 LOCAL_STATIC_LIBRARIES += libz libbz -LOCAL_MODULE_TAGS := eng include $(BUILD_HOST_EXECUTABLE) diff --git a/applypatch/applypatch.c b/applypatch/applypatch.cpp similarity index 63% rename from applypatch/applypatch.c rename to applypatch/applypatch.cpp index bc45e3c4..cc285855 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.cpp @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -22,15 +23,20 @@ #include #include #include -#include #include -#include -#include "mincrypt/sha.h" +#include +#include + +#include + +#include "openssl/sha.h" #include "applypatch.h" #include "bmlutils/bmlutils.h" #include "mtdutils/mtdutils.h" #include "edify/expr.h" +#include "ota_io.h" +#include "print_sha1.h" static int LoadPartitionContents(const char* filename, FileContents* file); static ssize_t FileSink(const unsigned char* data, ssize_t len, void* token); @@ -40,19 +46,17 @@ static int GenerateTarget(FileContents* source_file, const Value* copy_patch_value, const char* source_filename, const char* target_filename, - const uint8_t target_sha1[SHA_DIGEST_SIZE], + const uint8_t target_sha1[SHA_DIGEST_LENGTH], size_t target_size, const Value* bonus_data); -static int mtd_partitions_scanned = 0; +static bool mtd_partitions_scanned = false; // Read a file into memory; store the file contents and associated // metadata in *file. // // Return 0 on success. int LoadFileContents(const char* filename, FileContents* file) { - file->data = NULL; - // A special 'filename' beginning with "MTD:" or "EMMC:" means to // load the contents of a partition. if (strncmp(filename, "MTD:", 4) == 0 || @@ -66,46 +70,25 @@ int LoadFileContents(const char* filename, FileContents* file) { return -1; } - file->size = file->st.st_size; - file->data = malloc(file->size); - - FILE* f = fopen(filename, "rb"); + std::vector data(file->st.st_size); + FILE* f = ota_fopen(filename, "rb"); if (f == NULL) { printf("failed to open \"%s\": %s\n", filename, strerror(errno)); - free(file->data); - file->data = NULL; return -1; } - ssize_t bytes_read = fread(file->data, 1, file->size, f); - if (bytes_read != file->size) { - printf("short read of \"%s\" (%ld bytes of %ld)\n", - filename, (long)bytes_read, (long)file->size); - free(file->data); - file->data = NULL; + size_t bytes_read = ota_fread(data.data(), 1, data.size(), f); + if (bytes_read != data.size()) { + printf("short read of \"%s\" (%zu bytes of %zd)\n", filename, bytes_read, data.size()); + ota_fclose(f); return -1; } - fclose(f); - - SHA_hash(file->data, file->size, file->sha1); + ota_fclose(f); + file->data = std::move(data); + SHA1(file->data.data(), file->data.size(), file->sha1); return 0; } -static size_t* size_array; -// comparison function for qsort()ing an int array of indexes into -// size_array[]. -static int compare_size_indices(const void* a, const void* b) { - int aa = *(int*)a; - int bb = *(int*)b; - if (size_array[aa] < size_array[bb]) { - return -1; - } else if (size_array[aa] > size_array[bb]) { - return 1; - } else { - return 0; - } -} - // Load the contents of an MTD or EMMC partition into the provided // FileContents. filename should be a string of the form // "MTD::::::..." (or @@ -124,23 +107,25 @@ static int compare_size_indices(const void* a, const void* b) { enum PartitionType { MTD, EMMC }; static int LoadPartitionContents(const char* filename, FileContents* file) { - char* copy = strdup(filename); - const char* magic = strtok(copy, ":"); + std::string copy(filename); + std::vector pieces = android::base::Split(copy, ":"); + if (pieces.size() < 4 || pieces.size() % 2 != 0) { + printf("LoadPartitionContents called with bad filename (%s)\n", filename); + return -1; + } enum PartitionType type; - - if (strcmp(magic, "MTD") == 0) { + if (pieces[0] == "MTD") { type = MTD; - } else if (strcmp(magic, "EMMC") == 0) { + } else if (pieces[0] == "EMMC") { type = EMMC; } else if (strcmp(magic, "BML") == 0) { type = EMMC; } else { - printf("LoadPartitionContents called with bad filename (%s)\n", - filename); + printf("LoadPartitionContents called with bad filename (%s)\n", filename); return -1; } - const char* partition = strtok(NULL, ":"); + const char* partition = pieces[1].c_str(); if (strcmp(magic, "BML") == 0) { if (strcmp(partition, "boot") == 0) { @@ -150,132 +135,115 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { } } - int i; - int colons = 0; - for (i = 0; filename[i] != '\0'; ++i) { - if (filename[i] == ':') { - ++colons; - } - } - if (colons < 3 || colons%2 == 0) { - printf("LoadPartitionContents called with bad filename (%s)\n", - filename); - } + size_t pairs = (pieces.size() - 2) / 2; // # of (size, sha1) pairs in filename + std::vector index(pairs); + std::vector size(pairs); + std::vector sha1sum(pairs); - int pairs = (colons-1)/2; // # of (size,sha1) pairs in filename - int* index = malloc(pairs * sizeof(int)); - size_t* size = malloc(pairs * sizeof(size_t)); - char** sha1sum = malloc(pairs * sizeof(char*)); - - for (i = 0; i < pairs; ++i) { - const char* size_str = strtok(NULL, ":"); - size[i] = strtol(size_str, NULL, 10); + for (size_t i = 0; i < pairs; ++i) { + size[i] = strtol(pieces[i*2+2].c_str(), NULL, 10); if (size[i] == 0) { printf("LoadPartitionContents called with bad size (%s)\n", filename); return -1; } - sha1sum[i] = strtok(NULL, ":"); + sha1sum[i] = pieces[i*2+3].c_str(); index[i] = i; } - // sort the index[] array so it indexes the pairs in order of - // increasing size. - size_array = size; - qsort(index, pairs, sizeof(int), compare_size_indices); + // Sort the index[] array so it indexes the pairs in order of increasing size. + sort(index.begin(), index.end(), + [&](const size_t& i, const size_t& j) { + return (size[i] < size[j]); + } + ); MtdReadContext* ctx = NULL; FILE* dev = NULL; switch (type) { - case MTD: + case MTD: { if (!mtd_partitions_scanned) { mtd_scan_partitions(); - mtd_partitions_scanned = 1; + mtd_partitions_scanned = true; } const MtdPartition* mtd = mtd_find_partition_by_name(partition); if (mtd == NULL) { - printf("mtd partition \"%s\" not found (loading %s)\n", - partition, filename); + printf("mtd partition \"%s\" not found (loading %s)\n", partition, filename); return -1; } ctx = mtd_read_partition(mtd); if (ctx == NULL) { - printf("failed to initialize read of mtd partition \"%s\"\n", - partition); + printf("failed to initialize read of mtd partition \"%s\"\n", partition); return -1; } break; + } case EMMC: - dev = fopen(partition, "rb"); + dev = ota_fopen(partition, "rb"); if (dev == NULL) { - printf("failed to open emmc partition \"%s\": %s\n", - partition, strerror(errno)); + printf("failed to open emmc partition \"%s\": %s\n", partition, strerror(errno)); return -1; } } SHA_CTX sha_ctx; - SHA_init(&sha_ctx); - uint8_t parsed_sha[SHA_DIGEST_SIZE]; + SHA1_Init(&sha_ctx); + uint8_t parsed_sha[SHA_DIGEST_LENGTH]; - // allocate enough memory to hold the largest size. - file->data = malloc(size[index[pairs-1]]); - char* p = (char*)file->data; - file->size = 0; // # bytes read so far + // Allocate enough memory to hold the largest size. + std::vector data(size[index[pairs-1]]); + char* p = reinterpret_cast(data.data()); + size_t data_size = 0; // # bytes read so far + bool found = false; - for (i = 0; i < pairs; ++i) { - // Read enough additional bytes to get us up to the next size - // (again, we're trying the possibilities in order of increasing - // size). - size_t next = size[index[i]] - file->size; - size_t read = 0; + for (size_t i = 0; i < pairs; ++i) { + // Read enough additional bytes to get us up to the next size. (Again, + // we're trying the possibilities in order of increasing size). + size_t next = size[index[i]] - data_size; if (next > 0) { + size_t read = 0; switch (type) { case MTD: read = mtd_read_data(ctx, p, next); break; case EMMC: - read = fread(p, 1, next, dev); + read = ota_fread(p, 1, next, dev); break; } if (next != read) { printf("short read (%zu bytes of %zu) for partition \"%s\"\n", read, next, partition); - free(file->data); - file->data = NULL; return -1; } - SHA_update(&sha_ctx, p, read); - file->size += read; + SHA1_Update(&sha_ctx, p, read); + data_size += read; + p += read; } // Duplicate the SHA context and finalize the duplicate so we can // check it against this pair's expected hash. SHA_CTX temp_ctx; memcpy(&temp_ctx, &sha_ctx, sizeof(SHA_CTX)); - const uint8_t* sha_so_far = SHA_final(&temp_ctx); + uint8_t sha_so_far[SHA_DIGEST_LENGTH]; + SHA1_Final(sha_so_far, &temp_ctx); - if (ParseSha1(sha1sum[index[i]], parsed_sha) != 0) { - printf("failed to parse sha1 %s in %s\n", - sha1sum[index[i]], filename); - free(file->data); - file->data = NULL; + if (ParseSha1(sha1sum[index[i]].c_str(), parsed_sha) != 0) { + printf("failed to parse sha1 %s in %s\n", sha1sum[index[i]].c_str(), filename); return -1; } - if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_SIZE) == 0) { + if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_LENGTH) == 0) { // we have a match. stop reading the partition; we'll return // the data we've read so far. printf("partition read matched size %zu sha %s\n", - size[index[i]], sha1sum[index[i]]); + size[index[i]], sha1sum[index[i]].c_str()); + found = true; break; } - - p += read; } switch (type) { @@ -284,36 +252,26 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { break; case EMMC: - fclose(dev); + ota_fclose(dev); break; } - if (i == pairs) { - // Ran off the end of the list of (size,sha1) pairs without - // finding a match. - printf("contents of partition \"%s\" didn't match %s\n", - partition, filename); - free(file->data); - file->data = NULL; + if (!found) { + // Ran off the end of the list of (size,sha1) pairs without finding a match. + printf("contents of partition \"%s\" didn't match %s\n", partition, filename); return -1; } - const uint8_t* sha_final = SHA_final(&sha_ctx); - for (i = 0; i < SHA_DIGEST_SIZE; ++i) { - file->sha1[i] = sha_final[i]; - } + SHA1_Final(file->sha1, &sha_ctx); + data.resize(data_size); + file->data = std::move(data); // Fake some stat() info. file->st.st_mode = 0644; file->st.st_uid = 0; file->st.st_gid = 0; - free(copy); - free(index); - free(size); - free(sha1sum); - return 0; } @@ -321,26 +279,24 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { // Save the contents of the given FileContents object under the given // filename. Return 0 on success. int SaveFileContents(const char* filename, const FileContents* file) { - int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR); + int fd = ota_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR); if (fd < 0) { - printf("failed to open \"%s\" for write: %s\n", - filename, strerror(errno)); + printf("failed to open \"%s\" for write: %s\n", filename, strerror(errno)); return -1; } - ssize_t bytes_written = FileSink(file->data, file->size, &fd); - if (bytes_written != file->size) { - printf("short write of \"%s\" (%ld bytes of %ld) (%s)\n", - filename, (long)bytes_written, (long)file->size, - strerror(errno)); - close(fd); + ssize_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd); + if (bytes_written != static_cast(file->data.size())) { + printf("short write of \"%s\" (%zd bytes of %zu) (%s)\n", + filename, bytes_written, file->data.size(), strerror(errno)); + ota_close(fd); return -1; } - if (fsync(fd) != 0) { + if (ota_fsync(fd) != 0) { printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno)); return -1; } - if (close(fd) != 0) { + if (ota_close(fd) != 0) { printf("close of \"%s\" failed: %s\n", filename, strerror(errno)); return -1; } @@ -358,17 +314,22 @@ int SaveFileContents(const char* filename, const FileContents* file) { } // Write a memory buffer to 'target' partition, a string of the form -// "MTD:[:...]" or "EMMC::". Return 0 on -// success. -int WriteToPartition(unsigned char* data, size_t len, - const char* target) { - char* copy = strdup(target); - const char* magic = strtok(copy, ":"); +// "MTD:[:...]" or "EMMC:[:...]". The target name +// might contain multiple colons, but WriteToPartition() only uses the first +// two and ignores the rest. Return 0 on success. +int WriteToPartition(const unsigned char* data, size_t len, const char* target) { + std::string copy(target); + std::vector pieces = android::base::Split(copy, ":"); + + if (pieces.size() < 2) { + printf("WriteToPartition called with bad target (%s)\n", target); + return -1; + } enum PartitionType type; - if (strcmp(magic, "MTD") == 0) { + if (pieces[0] == "MTD") { type = MTD; - } else if (strcmp(magic, "EMMC") == 0) { + } else if (pieces[0] == "EMMC") { type = EMMC; } else if (strcmp(magic, "BML") == 0) { type = EMMC; @@ -376,7 +337,8 @@ int WriteToPartition(unsigned char* data, size_t len, printf("WriteToPartition called with bad target (%s)\n", target); return -1; } - const char* partition = strtok(NULL, ":"); + + const char* partition = pieces[1].c_str(); if (strcmp(magic, "BML") == 0) { if (strcmp(partition, "boot") == 0) { @@ -401,30 +363,27 @@ int WriteToPartition(unsigned char* data, size_t len, } switch (type) { - case MTD: + case MTD: { if (!mtd_partitions_scanned) { mtd_scan_partitions(); - mtd_partitions_scanned = 1; + mtd_partitions_scanned = true; } const MtdPartition* mtd = mtd_find_partition_by_name(partition); if (mtd == NULL) { - printf("mtd partition \"%s\" not found for writing\n", - partition); + printf("mtd partition \"%s\" not found for writing\n", partition); return -1; } MtdWriteContext* ctx = mtd_write_partition(mtd); if (ctx == NULL) { - printf("failed to init mtd partition \"%s\" for writing\n", - partition); + printf("failed to init mtd partition \"%s\" for writing\n", partition); return -1; } - size_t written = mtd_write_data(ctx, (char*)data, len); + size_t written = mtd_write_data(ctx, reinterpret_cast(data), len); if (written != len) { - printf("only wrote %zu of %zu bytes to MTD %s\n", - written, len, partition); + printf("only wrote %zu of %zu bytes to MTD %s\n", written, len, partition); mtd_write_close(ctx); return -1; } @@ -440,62 +399,57 @@ int WriteToPartition(unsigned char* data, size_t len, return -1; } break; + } - case EMMC: - { + case EMMC: { size_t start = 0; - int success = 0; - int fd = open(partition, O_RDWR | O_SYNC); + bool success = false; + int fd = ota_open(partition, O_RDWR | O_SYNC); if (fd < 0) { printf("failed to open %s: %s\n", partition, strerror(errno)); return -1; } - int attempt; - for (attempt = 0; attempt < 2; ++attempt) { + for (size_t attempt = 0; attempt < 2; ++attempt) { if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) { - printf("failed seek on %s: %s\n", - partition, strerror(errno)); + printf("failed seek on %s: %s\n", partition, strerror(errno)); return -1; } while (start < len) { size_t to_write = len - start; if (to_write > 1<<20) to_write = 1<<20; - ssize_t written = TEMP_FAILURE_RETRY(write(fd, data+start, to_write)); + ssize_t written = TEMP_FAILURE_RETRY(ota_write(fd, data+start, to_write)); if (written == -1) { printf("failed write writing to %s: %s\n", partition, strerror(errno)); return -1; } start += written; } - if (fsync(fd) != 0) { - printf("failed to sync to %s (%s)\n", - partition, strerror(errno)); + if (ota_fsync(fd) != 0) { + printf("failed to sync to %s (%s)\n", partition, strerror(errno)); return -1; } - if (close(fd) != 0) { - printf("failed to close %s (%s)\n", - partition, strerror(errno)); + if (ota_close(fd) != 0) { + printf("failed to close %s (%s)\n", partition, strerror(errno)); return -1; } - fd = open(partition, O_RDONLY); + fd = ota_open(partition, O_RDONLY); if (fd < 0) { - printf("failed to reopen %s for verify (%s)\n", - partition, strerror(errno)); + printf("failed to reopen %s for verify (%s)\n", partition, strerror(errno)); return -1; } - // drop caches so our subsequent verification read + // Drop caches so our subsequent verification read // won't just be reading the cache. sync(); - int dc = open("/proc/sys/vm/drop_caches", O_WRONLY); - if (TEMP_FAILURE_RETRY(write(dc, "3\n", 2)) == -1) { + int dc = ota_open("/proc/sys/vm/drop_caches", O_WRONLY); + if (TEMP_FAILURE_RETRY(ota_write(dc, "3\n", 2)) == -1) { printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno)); } else { printf(" caches dropped\n"); } - close(dc); + ota_close(dc); sleep(1); // verify @@ -506,28 +460,29 @@ int WriteToPartition(unsigned char* data, size_t len, } unsigned char buffer[4096]; start = len; - size_t p; - for (p = 0; p < len; p += sizeof(buffer)) { + for (size_t p = 0; p < len; p += sizeof(buffer)) { size_t to_read = len - p; - if (to_read > sizeof(buffer)) to_read = sizeof(buffer); + if (to_read > sizeof(buffer)) { + to_read = sizeof(buffer); + } size_t so_far = 0; while (so_far < to_read) { ssize_t read_count = - TEMP_FAILURE_RETRY(read(fd, buffer+so_far, to_read-so_far)); + TEMP_FAILURE_RETRY(ota_read(fd, buffer+so_far, to_read-so_far)); if (read_count == -1) { printf("verify read error %s at %zu: %s\n", partition, p, strerror(errno)); return -1; } - if ((size_t)read_count < to_read) { + if (static_cast(read_count) < to_read) { printf("short verify read %s at %zu: %zd %zu %s\n", partition, p, read_count, to_read, strerror(errno)); } so_far += read_count; } - if (memcmp(buffer, data+p, to_read)) { + if (memcmp(buffer, data+p, to_read) != 0) { printf("verification failed starting at %zu\n", p); start = p; break; @@ -535,7 +490,7 @@ int WriteToPartition(unsigned char* data, size_t len, } if (start == len) { - printf("verification read succeeded (attempt %d)\n", attempt+1); + printf("verification read succeeded (attempt %zu)\n", attempt+1); success = true; break; } @@ -546,7 +501,7 @@ int WriteToPartition(unsigned char* data, size_t len, return -1; } - if (close(fd) != 0) { + if (ota_close(fd) != 0) { printf("error closing %s (%s)\n", partition, strerror(errno)); return -1; } @@ -555,7 +510,6 @@ int WriteToPartition(unsigned char* data, size_t len, } } - free(copy); return 0; } @@ -565,10 +519,9 @@ int WriteToPartition(unsigned char* data, size_t len, // the form ":". Return 0 on success, -1 on any // error. int ParseSha1(const char* str, uint8_t* digest) { - int i; const char* ps = str; uint8_t* pd = digest; - for (i = 0; i < SHA_DIGEST_SIZE * 2; ++i, ++ps) { + for (int i = 0; i < SHA_DIGEST_LENGTH * 2; ++i, ++ps) { int digit; if (*ps >= '0' && *ps <= '9') { digit = *ps - '0'; @@ -595,11 +548,10 @@ int ParseSha1(const char* str, uint8_t* digest) { // found. int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str, int num_patches) { - int i; - uint8_t patch_sha1[SHA_DIGEST_SIZE]; - for (i = 0; i < num_patches; ++i) { + uint8_t patch_sha1[SHA_DIGEST_LENGTH]; + for (int i = 0; i < num_patches; ++i) { if (ParseSha1(patch_sha1_str[i], patch_sha1) == 0 && - memcmp(patch_sha1, sha1, SHA_DIGEST_SIZE) == 0) { + memcmp(patch_sha1, sha1, SHA_DIGEST_LENGTH) == 0) { return i; } } @@ -609,10 +561,9 @@ int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str, // Returns 0 if the contents of the file (argv[2]) or the cached file // match any of the sha1's on the command line (argv[3:]). Returns // nonzero otherwise. -int applypatch_check(const char* filename, - int num_patches, char** const patch_sha1_str) { +int applypatch_check(const char* filename, int num_patches, + char** const patch_sha1_str) { FileContents file; - file.data = NULL; // It's okay to specify no sha1s; the check will pass if the // LoadFileContents is successful. (Useful for reading @@ -624,9 +575,6 @@ int applypatch_check(const char* filename, printf("file \"%s\" doesn't have any of expected " "sha1 sums; checking cache\n", filename); - free(file.data); - file.data = NULL; - // If the source file is missing or corrupted, it might be because // we were killed in the middle of patching it. A copy of it // should have been made in CACHE_TEMP_SOURCE. If that file @@ -640,12 +588,9 @@ int applypatch_check(const char* filename, if (FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0) { printf("cache bits don't match any sha1 for \"%s\"\n", filename); - free(file.data); return 1; } } - - free(file.data); return 0; } @@ -655,13 +600,13 @@ int ShowLicenses() { } ssize_t FileSink(const unsigned char* data, ssize_t len, void* token) { - int fd = *(int *)token; + int fd = *static_cast(token); ssize_t done = 0; ssize_t wrote; - while (done < (ssize_t) len) { - wrote = TEMP_FAILURE_RETRY(write(fd, data+done, len-done)); + while (done < len) { + wrote = TEMP_FAILURE_RETRY(ota_write(fd, data+done, len-done)); if (wrote == -1) { - printf("error writing %d bytes: %s\n", (int)(len-done), strerror(errno)); + printf("error writing %zd bytes: %s\n", (len-done), strerror(errno)); return done; } done += wrote; @@ -669,19 +614,9 @@ ssize_t FileSink(const unsigned char* data, ssize_t len, void* token) { return done; } -typedef struct { - unsigned char* buffer; - ssize_t size; - ssize_t pos; -} MemorySinkInfo; - ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) { - MemorySinkInfo* msi = (MemorySinkInfo*)token; - if (msi->size - msi->pos < len) { - return -1; - } - memcpy(msi->buffer + msi->pos, data, len); - msi->pos += len; + std::string* s = static_cast(token); + s->append(reinterpret_cast(data), len); return len; } @@ -705,15 +640,6 @@ int CacheSizeCheck(size_t bytes) { } } -static void print_short_sha1(const uint8_t sha1[SHA_DIGEST_SIZE]) { - int i; - const char* hex = "0123456789abcdef"; - for (i = 0; i < 4; ++i) { - putchar(hex[(sha1[i]>>4) & 0xf]); - putchar(hex[sha1[i] & 0xf]); - } -} - // This function 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 @@ -726,7 +652,7 @@ static void print_short_sha1(const uint8_t sha1[SHA_DIGEST_SIZE]) { // entries in , the corresponding patch from // (which must be a VAL_BLOB) is applied to produce a // new file (the type of patch is automatically detected from the -// blob daat). If that new file has sha1 hash , +// blob data). If that new file has sha1 hash , // moves it to replace , and exits successfully. // Note that if and are not the // same, is NOT deleted on success. @@ -737,7 +663,7 @@ static void print_short_sha1(const uint8_t sha1[SHA_DIGEST_SIZE]) { // status. // // may refer to a partition to read the source data. -// See the comments for the LoadPartition Contents() function above +// See the comments for the LoadPartitionContents() function above // for the format of such a filename. int applypatch(const char* source_filename, @@ -750,12 +676,11 @@ int applypatch(const char* source_filename, Value* bonus_data) { printf("patch %s: ", source_filename); - if (target_filename[0] == '-' && - target_filename[1] == '\0') { + if (target_filename[0] == '-' && target_filename[1] == '\0') { target_filename = source_filename; } - uint8_t target_sha1[SHA_DIGEST_SIZE]; + uint8_t target_sha1[SHA_DIGEST_LENGTH]; if (ParseSha1(target_sha1_str, target_sha1) != 0) { printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str); return 1; @@ -763,45 +688,37 @@ int applypatch(const char* source_filename, FileContents copy_file; FileContents source_file; - copy_file.data = NULL; - source_file.data = NULL; const Value* source_patch_value = NULL; const Value* copy_patch_value = NULL; // We try to load the target file into the source_file object. if (LoadFileContents(target_filename, &source_file) == 0) { - if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) { + if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) { // The early-exit case: the patch was already applied, this file // has the desired hash, nothing for us to do. - printf("already "); - print_short_sha1(target_sha1); - putchar('\n'); - free(source_file.data); + printf("already %s\n", short_sha1(target_sha1).c_str()); return 0; } } - if (source_file.data == NULL || + if (source_file.data.empty() || (target_filename != source_filename && strcmp(target_filename, source_filename) != 0)) { // Need to load the source file: either we failed to load the // target file, or we did but it's different from the source file. - free(source_file.data); - source_file.data = NULL; + source_file.data.clear(); LoadFileContents(source_filename, &source_file); } - if (source_file.data != NULL) { - int to_use = FindMatchingPatch(source_file.sha1, - patch_sha1_str, num_patches); + if (!source_file.data.empty()) { + int to_use = FindMatchingPatch(source_file.sha1, patch_sha1_str, num_patches); if (to_use >= 0) { source_patch_value = patch_data[to_use]; } } if (source_patch_value == NULL) { - free(source_file.data); - source_file.data = NULL; + source_file.data.clear(); printf("source file is bad; trying copy\n"); if (LoadFileContents(CACHE_TEMP_SOURCE, ©_file) < 0) { @@ -810,8 +727,7 @@ int applypatch(const char* source_filename, return 1; } - int to_use = FindMatchingPatch(copy_file.sha1, - patch_sha1_str, num_patches); + int to_use = FindMatchingPatch(copy_file.sha1, patch_sha1_str, num_patches); if (to_use >= 0) { copy_patch_value = patch_data[to_use]; } @@ -819,19 +735,69 @@ int applypatch(const char* source_filename, if (copy_patch_value == NULL) { // fail. printf("copy file doesn't match source SHA-1s either\n"); - free(copy_file.data); return 1; } } - int result = GenerateTarget(&source_file, source_patch_value, - ©_file, copy_patch_value, - source_filename, target_filename, - target_sha1, target_size, bonus_data); - free(source_file.data); - free(copy_file.data); + return GenerateTarget(&source_file, source_patch_value, + ©_file, copy_patch_value, + source_filename, target_filename, + target_sha1, target_size, bonus_data); +} - return result; +/* + * This function flashes a given image to the target partition. It verifies + * the target cheksum first, and will return if target has the desired hash. + * It checks the checksum of the given source image before flashing, and + * verifies the target partition afterwards. The function is idempotent. + * Returns zero on success. + */ +int applypatch_flash(const char* source_filename, const char* target_filename, + const char* target_sha1_str, size_t target_size) { + printf("flash %s: ", target_filename); + + uint8_t target_sha1[SHA_DIGEST_LENGTH]; + if (ParseSha1(target_sha1_str, target_sha1) != 0) { + printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str); + return 1; + } + + FileContents source_file; + std::string target_str(target_filename); + + std::vector pieces = android::base::Split(target_str, ":"); + if (pieces.size() != 2 || (pieces[0] != "MTD" && pieces[0] != "EMMC")) { + printf("invalid target name \"%s\"", target_filename); + return 1; + } + + // Load the target into the source_file object to see if already applied. + pieces.push_back(std::to_string(target_size)); + pieces.push_back(target_sha1_str); + std::string fullname = android::base::Join(pieces, ':'); + if (LoadPartitionContents(fullname.c_str(), &source_file) == 0 && + memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) { + // The early-exit case: the image was already applied, this partition + // has the desired hash, nothing for us to do. + printf("already %s\n", short_sha1(target_sha1).c_str()); + return 0; + } + + if (LoadFileContents(source_filename, &source_file) == 0) { + if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) { + // The source doesn't have desired checksum. + printf("source \"%s\" doesn't have expected sha1 sum\n", source_filename); + printf("expected: %s, found: %s\n", short_sha1(target_sha1).c_str(), + short_sha1(source_file.sha1).c_str()); + return 1; + } + } + + if (WriteToPartition(source_file.data.data(), target_size, target_filename) != 0) { + printf("write of copied data to %s failed\n", target_filename); + return 1; + } + return 0; } static int GenerateTarget(FileContents* source_file, @@ -840,37 +806,58 @@ static int GenerateTarget(FileContents* source_file, const Value* copy_patch_value, const char* source_filename, const char* target_filename, - const uint8_t target_sha1[SHA_DIGEST_SIZE], + const uint8_t target_sha1[SHA_DIGEST_LENGTH], size_t target_size, const Value* bonus_data) { int retry = 1; SHA_CTX ctx; - int output; - MemorySinkInfo msi; + std::string memory_sink_str; FileContents* source_to_use; - char* outname; int made_copy = 0; + bool target_is_partition = (strncmp(target_filename, "MTD:", 4) == 0 || + strncmp(target_filename, "EMMC:", 5) == 0 || + strncmp(target_filename, "BML:", 4) == 0); + const std::string tmp_target_filename = std::string(target_filename) + ".patch"; + // assume that target_filename (eg "/system/app/Foo.apk") is located // on the same filesystem as its top-level directory ("/system"). // We need something that exists for calling statfs(). - char target_fs[strlen(target_filename)+1]; - char* slash = strchr(target_filename+1, '/'); - if (slash != NULL) { - int count = slash - target_filename; - strncpy(target_fs, target_filename, count); - target_fs[count] = '\0'; + std::string target_fs = target_filename; + auto slash_pos = target_fs.find('/', 1); + if (slash_pos != std::string::npos) { + target_fs.resize(slash_pos); + } + + const Value* patch; + if (source_patch_value != NULL) { + source_to_use = source_file; + patch = source_patch_value; } else { - strcpy(target_fs, target_filename); + source_to_use = copy_file; + patch = copy_patch_value; + } + if (patch->type != VAL_BLOB) { + printf("patch is not a blob\n"); + return 1; + } + char* header = patch->data; + ssize_t header_bytes_read = patch->size; + bool use_bsdiff = false; + if (header_bytes_read >= 8 && memcmp(header, "BSDIFF40", 8) == 0) { + use_bsdiff = true; + } else if (header_bytes_read >= 8 && memcmp(header, "IMGDIFF2", 8) == 0) { + use_bsdiff = false; + } else { + printf("Unknown patch file format\n"); + return 1; } do { // Is there enough room in the target filesystem to hold the patched // file? - if (strncmp(target_filename, "MTD:", 4) == 0 || - strncmp(target_filename, "EMMC:", 5) == 0 || - strncmp(target_filename, "BML:", 4) == 0) { + if (target_is_partition) { // If the target is a partition, we're actually going to // write the output to /tmp and then copy it to the // partition. statfs() always returns 0 blocks free for @@ -879,7 +866,7 @@ static int GenerateTarget(FileContents* source_file, // We still write the original source to cache, in case // the partition write is interrupted. - if (MakeFreeSpaceOnCache(source_file->size) < 0) { + if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) { printf("not enough free space on /cache\n"); return 1; } @@ -892,13 +879,13 @@ static int GenerateTarget(FileContents* source_file, } else { int enough_space = 0; if (retry > 0) { - size_t free_space = FreeSpaceForFile(target_fs); + size_t free_space = FreeSpaceForFile(target_fs.c_str()); enough_space = (free_space > (256 << 10)) && // 256k (two-block) minimum (free_space > (target_size * 3 / 2)); // 50% margin of error if (!enough_space) { - printf("target %ld bytes; free space %ld bytes; retry %d; enough %d\n", - (long)target_size, (long)free_space, retry, enough_space); + printf("target %zu bytes; free space %zu bytes; retry %d; enough %d\n", + target_size, free_space, retry, enough_space); } } @@ -917,12 +904,11 @@ static int GenerateTarget(FileContents* source_file, // It's impossible to free space on the target filesystem by // deleting the source if the source is a partition. If // we're ever in a state where we need to do this, fail. - printf("not enough free space for target but source " - "is partition\n"); + printf("not enough free space for target but source is partition\n"); return 1; } - if (MakeFreeSpaceOnCache(source_file->size) < 0) { + if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) { printf("not enough free space on /cache\n"); return 1; } @@ -934,87 +920,54 @@ static int GenerateTarget(FileContents* source_file, made_copy = 1; unlink(source_filename); - size_t free_space = FreeSpaceForFile(target_fs); - printf("(now %ld bytes free for target) ", (long)free_space); + size_t free_space = FreeSpaceForFile(target_fs.c_str()); + printf("(now %zu bytes free for target) ", free_space); } } - const Value* patch; - if (source_patch_value != NULL) { - source_to_use = source_file; - patch = source_patch_value; - } else { - source_to_use = copy_file; - patch = copy_patch_value; - } - - if (patch->type != VAL_BLOB) { - printf("patch is not a blob\n"); - return 1; - } SinkFn sink = NULL; void* token = NULL; - output = -1; - outname = NULL; - if (strncmp(target_filename, "MTD:", 4) == 0 || - strncmp(target_filename, "EMMC:", 5) == 0 || - strncmp(target_filename, "BML:", 4) == 0) { + + int output_fd = -1; + if (target_is_partition) { // We store the decoded output in memory. - msi.buffer = malloc(target_size); - if (msi.buffer == NULL) { - printf("failed to alloc %ld bytes for output\n", - (long)target_size); - return 1; - } - msi.pos = 0; - msi.size = target_size; sink = MemorySink; - token = &msi; + token = &memory_sink_str; } else { // We write the decoded output to ".patch". - outname = (char*)malloc(strlen(target_filename) + 10); - strcpy(outname, target_filename); - strcat(outname, ".patch"); - - output = open(outname, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, - S_IRUSR | S_IWUSR); - if (output < 0) { - printf("failed to open output file %s: %s\n", - outname, strerror(errno)); + output_fd = ota_open(tmp_target_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, + S_IRUSR | S_IWUSR); + if (output_fd < 0) { + printf("failed to open output file %s: %s\n", tmp_target_filename.c_str(), + strerror(errno)); return 1; } sink = FileSink; - token = &output; + token = &output_fd; } - char* header = patch->data; - ssize_t header_bytes_read = patch->size; - SHA_init(&ctx); + SHA1_Init(&ctx); int result; - - if (header_bytes_read >= 8 && - memcmp(header, "BSDIFF40", 8) == 0) { - result = ApplyBSDiffPatch(source_to_use->data, source_to_use->size, + if (use_bsdiff) { + result = ApplyBSDiffPatch(source_to_use->data.data(), source_to_use->data.size(), patch, 0, sink, token, &ctx); - } else if (header_bytes_read >= 8 && - memcmp(header, "IMGDIFF2", 8) == 0) { - result = ApplyImagePatch(source_to_use->data, source_to_use->size, - patch, sink, token, &ctx, bonus_data); } else { - printf("Unknown patch file format\n"); - return 1; + result = ApplyImagePatch(source_to_use->data.data(), source_to_use->data.size(), + patch, sink, token, &ctx, bonus_data); } - if (output >= 0) { - if (fsync(output) != 0) { - printf("failed to fsync file \"%s\" (%s)\n", outname, strerror(errno)); + if (!target_is_partition) { + if (ota_fsync(output_fd) != 0) { + printf("failed to fsync file \"%s\" (%s)\n", tmp_target_filename.c_str(), + strerror(errno)); result = 1; } - if (close(output) != 0) { - printf("failed to close file \"%s\" (%s)\n", outname, strerror(errno)); + if (ota_close(output_fd) != 0) { + printf("failed to close file \"%s\" (%s)\n", tmp_target_filename.c_str(), + strerror(errno)); result = 1; } } @@ -1026,8 +979,8 @@ static int GenerateTarget(FileContents* source_file, } else { printf("applying patch failed; retrying\n"); } - if (outname != NULL) { - unlink(outname); + if (!target_is_partition) { + unlink(tmp_target_filename.c_str()); } } else { // succeeded; no need to retry @@ -1035,47 +988,46 @@ static int GenerateTarget(FileContents* source_file, } } while (retry-- > 0); - const uint8_t* current_target_sha1 = SHA_final(&ctx); - if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) { + uint8_t current_target_sha1[SHA_DIGEST_LENGTH]; + SHA1_Final(current_target_sha1, &ctx); + if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) { printf("patch did not produce expected sha1\n"); return 1; } else { - printf("now "); - print_short_sha1(target_sha1); - putchar('\n'); + printf("now %s\n", short_sha1(target_sha1).c_str()); } - if (output < 0) { + if (target_is_partition) { // Copy the temp file to the partition. - if (WriteToPartition(msi.buffer, msi.pos, target_filename) != 0) { + if (WriteToPartition(reinterpret_cast(memory_sink_str.c_str()), + memory_sink_str.size(), target_filename) != 0) { printf("write of patched data to %s failed\n", target_filename); return 1; } - free(msi.buffer); } else { // Give the .patch file the same owner, group, and mode of the // original source file. - if (chmod(outname, source_to_use->st.st_mode) != 0) { - printf("chmod of \"%s\" failed: %s\n", outname, strerror(errno)); + if (chmod(tmp_target_filename.c_str(), source_to_use->st.st_mode) != 0) { + printf("chmod of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno)); return 1; } - if (chown(outname, source_to_use->st.st_uid, - source_to_use->st.st_gid) != 0) { - printf("chown of \"%s\" failed: %s\n", outname, strerror(errno)); + if (chown(tmp_target_filename.c_str(), source_to_use->st.st_uid, source_to_use->st.st_gid) != 0) { + printf("chown of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno)); return 1; } // Finally, rename the .patch file to replace the target file. - if (rename(outname, target_filename) != 0) { - printf("rename of .patch to \"%s\" failed: %s\n", - target_filename, strerror(errno)); + if (rename(tmp_target_filename.c_str(), target_filename) != 0) { + printf("rename of .patch to \"%s\" failed: %s\n", target_filename, strerror(errno)); return 1; } } // If this run of applypatch created the copy, and we're here, we // can delete it. - if (made_copy) unlink(CACHE_TEMP_SOURCE); + if (made_copy) { + unlink(CACHE_TEMP_SOURCE); + } // Success! return 0; diff --git a/applypatch/applypatch.h b/applypatch/applypatch.h index edec8481..f392c553 100644 --- a/applypatch/applypatch.h +++ b/applypatch/applypatch.h @@ -18,20 +18,17 @@ #define _APPLYPATCH_H #include -#include "mincrypt/sha.h" + +#include + +#include "openssl/sha.h" #include "edify/expr.h" -typedef struct _Patch { - uint8_t sha1[SHA_DIGEST_SIZE]; - const char* patch_filename; -} Patch; - -typedef struct _FileContents { - uint8_t sha1[SHA_DIGEST_SIZE]; - unsigned char* data; - ssize_t size; +struct FileContents { + uint8_t sha1[SHA_DIGEST_LENGTH]; + std::vector data; struct stat st; -} FileContents; +}; // When there isn't enough room on the target filesystem to hold the // patched version of the file, we copy the original here and delete @@ -48,6 +45,8 @@ 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, @@ -66,22 +65,22 @@ void FreeFileContents(FileContents* file); int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str, int num_patches); -// bsdiff.c +// bsdiff.cpp void ShowBSDiffLicense(); int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size, const Value* patch, ssize_t patch_offset, SinkFn sink, void* token, SHA_CTX* ctx); int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, const Value* patch, ssize_t patch_offset, - unsigned char** new_data, ssize_t* new_size); + std::vector* new_data); -// imgpatch.c +// imgpatch.cpp int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value* patch, SinkFn sink, void* token, SHA_CTX* ctx, const Value* bonus_data); -// freecache.c +// freecache.cpp int MakeFreeSpaceOnCache(size_t bytes_needed); #endif diff --git a/applypatch/bsdiff.c b/applypatch/bsdiff.cpp similarity index 87% rename from applypatch/bsdiff.c rename to applypatch/bsdiff.cpp index b6d342b7..55dbe5cf 100644 --- a/applypatch/bsdiff.c +++ b/applypatch/bsdiff.cpp @@ -156,24 +156,24 @@ static void qsufsort(off_t *I,off_t *V,u_char *old,off_t oldsize) for(i=0;iy) { *pos=I[st]; @@ -185,10 +185,10 @@ static off_t search(off_t *I,u_char *old,off_t oldsize, }; x=st+(en-st)/2; - if(memcmp(old+I[x],new,MIN(oldsize-I[x],newsize))<0) { - return search(I,old,oldsize,new,newsize,x,en,pos); + if(memcmp(old+I[x],newdata,MIN(oldsize-I[x],newsize))<0) { + return search(I,old,oldsize,newdata,newsize,x,en,pos); } else { - return search(I,old,oldsize,new,newsize,st,x,pos); + return search(I,old,oldsize,newdata,newsize,st,x,pos); }; } @@ -212,8 +212,8 @@ static void offtout(off_t x,u_char *buf) // This is main() from bsdiff.c, with the following changes: // -// - old, oldsize, new, newsize are arguments; we don't load this -// data from files. old and new are owned by the caller; we +// - old, oldsize, newdata, newsize are arguments; we don't load this +// data from files. old and newdata are owned by the caller; we // don't free them at the end. // // - the "I" block of memory is owned by the caller, who passes a @@ -221,7 +221,7 @@ static void offtout(off_t x,u_char *buf) // bsdiff() multiple times with the same 'old' data, we only do // the qsufsort() step the first time. // -int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* new, off_t newsize, +int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* newdata, off_t newsize, const char* patch_filename) { int fd; @@ -242,15 +242,15 @@ int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* new, off_t newsize, if (*IP == NULL) { off_t* V; - *IP = malloc((oldsize+1) * sizeof(off_t)); - V = malloc((oldsize+1) * sizeof(off_t)); + *IP = reinterpret_cast(malloc((oldsize+1) * sizeof(off_t))); + V = reinterpret_cast(malloc((oldsize+1) * sizeof(off_t))); qsufsort(*IP, V, old, oldsize); free(V); } I = *IP; - if(((db=malloc(newsize+1))==NULL) || - ((eb=malloc(newsize+1))==NULL)) err(1,NULL); + if(((db=reinterpret_cast(malloc(newsize+1)))==NULL) || + ((eb=reinterpret_cast(malloc(newsize+1)))==NULL)) err(1,NULL); dblen=0; eblen=0; @@ -284,26 +284,26 @@ int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* new, off_t newsize, oldscore=0; for(scsc=scan+=len;scanoldscore+8)) break; if((scan+lastoffsetSf*2-lenf) { Sf=s; lenf=i; }; }; @@ -312,7 +312,7 @@ int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* new, off_t newsize, if(scan=lastscan+i)&&(pos>=i);i++) { - if(old[pos-i]==new[scan-i]) s++; + if(old[pos-i]==newdata[scan-i]) s++; if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; }; }; }; @@ -321,9 +321,9 @@ int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* new, off_t newsize, overlap=(lastscan+lenf)-(scan-lenb); s=0;Ss=0;lens=0; for(i=0;iSs) { Ss=s; lens=i+1; }; }; @@ -333,9 +333,9 @@ int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* new, off_t newsize, }; for(i=0;i #include +#include #include -#include #include #include #include -#include "mincrypt/sha.h" +#include "openssl/sha.h" #include "applypatch.h" void ShowBSDiffLicense() { @@ -102,26 +102,22 @@ int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size, const Value* patch, ssize_t patch_offset, SinkFn sink, void* token, SHA_CTX* ctx) { - unsigned char* new_data; - ssize_t new_size; - if (ApplyBSDiffPatchMem(old_data, old_size, patch, patch_offset, - &new_data, &new_size) != 0) { + std::vector new_data; + if (ApplyBSDiffPatchMem(old_data, old_size, patch, patch_offset, &new_data) != 0) { return -1; } - if (sink(new_data, new_size, token) < new_size) { + if (sink(new_data.data(), new_data.size(), token) < static_cast(new_data.size())) { printf("short write of output: %d (%s)\n", errno, strerror(errno)); return 1; } - if (ctx) SHA_update(ctx, new_data, new_size); - free(new_data); - + if (ctx) SHA1_Update(ctx, new_data.data(), new_data.size()); return 0; } int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, const Value* patch, ssize_t patch_offset, - unsigned char** new_data, ssize_t* new_size) { + std::vector* new_data) { // Patch data format: // 0 8 "BSDIFF40" // 8 8 X @@ -140,12 +136,12 @@ int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, return 1; } - ssize_t ctrl_len, data_len; + ssize_t ctrl_len, data_len, new_size; ctrl_len = offtin(header+8); data_len = offtin(header+16); - *new_size = offtin(header+24); + new_size = offtin(header+24); - if (ctrl_len < 0 || data_len < 0 || *new_size < 0) { + if (ctrl_len < 0 || data_len < 0 || new_size < 0) { printf("corrupt patch file header (data lengths)\n"); return 1; } @@ -182,19 +178,14 @@ int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, printf("failed to bzinit extra stream (%d)\n", bzerr); } - *new_data = malloc(*new_size); - if (*new_data == NULL) { - printf("failed to allocate %ld bytes of memory for output file\n", - (long)*new_size); - return 1; - } + new_data->resize(new_size); off_t oldpos = 0, newpos = 0; off_t ctrl[3]; off_t len_read; int i; unsigned char buf[24]; - while (newpos < *new_size) { + while (newpos < new_size) { // Read control data if (FillBuffer(buf, 24, &cstream) != 0) { printf("error while reading control stream\n"); @@ -210,13 +201,13 @@ int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, } // Sanity check - if (newpos + ctrl[0] > *new_size) { + if (newpos + ctrl[0] > new_size) { printf("corrupt patch (new file overrun)\n"); return 1; } // Read diff string - if (FillBuffer(*new_data + newpos, ctrl[0], &dstream) != 0) { + if (FillBuffer(new_data->data() + newpos, ctrl[0], &dstream) != 0) { printf("error while reading diff stream\n"); return 1; } @@ -233,13 +224,13 @@ int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, oldpos += ctrl[0]; // Sanity check - if (newpos + ctrl[1] > *new_size) { + if (newpos + ctrl[1] > new_size) { printf("corrupt patch (new file overrun)\n"); return 1; } // Read extra string - if (FillBuffer(*new_data + newpos, ctrl[1], &estream) != 0) { + if (FillBuffer(new_data->data() + newpos, ctrl[1], &estream) != 0) { printf("error while reading extra stream\n"); return 1; } diff --git a/applypatch/freecache.c b/applypatch/freecache.c deleted file mode 100644 index 9827fda0..00000000 --- a/applypatch/freecache.c +++ /dev/null @@ -1,172 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "applypatch.h" - -static int EliminateOpenFiles(char** files, int file_count) { - DIR* d; - struct dirent* de; - d = opendir("/proc"); - if (d == NULL) { - printf("error opening /proc: %s\n", strerror(errno)); - return -1; - } - while ((de = readdir(d)) != 0) { - int i; - for (i = 0; de->d_name[i] != '\0' && isdigit(de->d_name[i]); ++i); - if (de->d_name[i]) continue; - - // de->d_name[i] is numeric - - char path[FILENAME_MAX]; - strcpy(path, "/proc/"); - strcat(path, de->d_name); - strcat(path, "/fd/"); - - DIR* fdd; - struct dirent* fdde; - fdd = opendir(path); - if (fdd == NULL) { - printf("error opening %s: %s\n", path, strerror(errno)); - continue; - } - while ((fdde = readdir(fdd)) != 0) { - char fd_path[FILENAME_MAX]; - char link[FILENAME_MAX]; - strcpy(fd_path, path); - strcat(fd_path, fdde->d_name); - - int count; - count = readlink(fd_path, link, sizeof(link)-1); - if (count >= 0) { - link[count] = '\0'; - - // This is inefficient, but it should only matter if there are - // lots of files in /cache, and lots of them are open (neither - // of which should be true, especially in recovery). - if (strncmp(link, "/cache/", 7) == 0) { - int j; - for (j = 0; j < file_count; ++j) { - if (files[j] && strcmp(files[j], link) == 0) { - printf("%s is open by %s\n", link, de->d_name); - free(files[j]); - files[j] = NULL; - } - } - } - } - } - closedir(fdd); - } - closedir(d); - - return 0; -} - -int FindExpendableFiles(char*** names, int* entries) { - DIR* d; - struct dirent* de; - int size = 32; - *entries = 0; - *names = malloc(size * sizeof(char*)); - - char path[FILENAME_MAX]; - - // We're allowed to delete unopened regular files in any of these - // directories. - const char* dirs[2] = {"/cache", "/cache/recovery/otatest"}; - - unsigned int i; - for (i = 0; i < sizeof(dirs)/sizeof(dirs[0]); ++i) { - d = opendir(dirs[i]); - if (d == NULL) { - printf("error opening %s: %s\n", dirs[i], strerror(errno)); - continue; - } - - // Look for regular files in the directory (not in any subdirectories). - while ((de = readdir(d)) != 0) { - strcpy(path, dirs[i]); - strcat(path, "/"); - strcat(path, de->d_name); - - // We can't delete CACHE_TEMP_SOURCE; if it's there we might have - // restarted during installation and could be depending on it to - // be there. - if (strcmp(path, CACHE_TEMP_SOURCE) == 0) continue; - - struct stat st; - if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) { - if (*entries >= size) { - size *= 2; - *names = realloc(*names, size * sizeof(char*)); - } - (*names)[(*entries)++] = strdup(path); - } - } - - closedir(d); - } - - printf("%d regular files in deletable directories\n", *entries); - - if (EliminateOpenFiles(*names, *entries) < 0) { - return -1; - } - - return 0; -} - -int MakeFreeSpaceOnCache(size_t bytes_needed) { - size_t free_now = FreeSpaceForFile("/cache"); - printf("%ld bytes free on /cache (%ld needed)\n", - (long)free_now, (long)bytes_needed); - - if (free_now >= bytes_needed) { - return 0; - } - - char** names; - int entries; - - if (FindExpendableFiles(&names, &entries) < 0) { - return -1; - } - - if (entries == 0) { - // nothing we can delete to free up space! - printf("no files can be deleted to free space on /cache\n"); - return -1; - } - - // We could try to be smarter about which files to delete: the - // biggest ones? the smallest ones that will free up enough space? - // the oldest? the newest? - // - // Instead, we'll be dumb. - - int i; - for (i = 0; i < entries && free_now < bytes_needed; ++i) { - if (names[i]) { - unlink(names[i]); - free_now = FreeSpaceForFile("/cache"); - printf("deleted %s; now %ld bytes free\n", names[i], (long)free_now); - free(names[i]); - } - } - - for (; i < entries; ++i) { - free(names[i]); - } - free(names); - - return (free_now >= bytes_needed) ? 0 : -1; -} diff --git a/applypatch/freecache.cpp b/applypatch/freecache.cpp new file mode 100644 index 00000000..c84f4279 --- /dev/null +++ b/applypatch/freecache.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "applypatch.h" + +static int EliminateOpenFiles(std::set* files) { + std::unique_ptr d(opendir("/proc"), closedir); + if (!d) { + printf("error opening /proc: %s\n", strerror(errno)); + return -1; + } + struct dirent* de; + while ((de = readdir(d.get())) != 0) { + unsigned int pid; + if (!android::base::ParseUint(de->d_name, &pid)) { + continue; + } + std::string path = android::base::StringPrintf("/proc/%s/fd/", de->d_name); + + struct dirent* fdde; + std::unique_ptr fdd(opendir(path.c_str()), closedir); + if (!fdd) { + printf("error opening %s: %s\n", path.c_str(), strerror(errno)); + continue; + } + while ((fdde = readdir(fdd.get())) != 0) { + std::string fd_path = path + fdde->d_name; + char link[FILENAME_MAX]; + + int count = readlink(fd_path.c_str(), link, sizeof(link)-1); + if (count >= 0) { + link[count] = '\0'; + if (strncmp(link, "/cache/", 7) == 0) { + if (files->erase(link) > 0) { + printf("%s is open by %s\n", link, de->d_name); + } + } + } + } + } + return 0; +} + +static std::set FindExpendableFiles() { + std::set files; + // We're allowed to delete unopened regular files in any of these + // directories. + const char* dirs[2] = {"/cache", "/cache/recovery/otatest"}; + + for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); ++i) { + std::unique_ptr d(opendir(dirs[i]), closedir); + if (!d) { + printf("error opening %s: %s\n", dirs[i], strerror(errno)); + continue; + } + + // Look for regular files in the directory (not in any subdirectories). + struct dirent* de; + while ((de = readdir(d.get())) != 0) { + std::string path = std::string(dirs[i]) + "/" + de->d_name; + + // We can't delete CACHE_TEMP_SOURCE; if it's there we might have + // restarted during installation and could be depending on it to + // be there. + if (path == CACHE_TEMP_SOURCE) { + continue; + } + + struct stat st; + if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) { + files.insert(path); + } + } + } + + printf("%zu regular files in deletable directories\n", files.size()); + if (EliminateOpenFiles(&files) < 0) { + return std::set(); + } + return files; +} + +int MakeFreeSpaceOnCache(size_t bytes_needed) { + size_t free_now = FreeSpaceForFile("/cache"); + printf("%zu bytes free on /cache (%zu needed)\n", free_now, bytes_needed); + + if (free_now >= bytes_needed) { + return 0; + } + std::set files = FindExpendableFiles(); + if (files.empty()) { + // nothing we can delete to free up space! + printf("no files can be deleted to free space on /cache\n"); + return -1; + } + + // We could try to be smarter about which files to delete: the + // biggest ones? the smallest ones that will free up enough space? + // the oldest? the newest? + // + // Instead, we'll be dumb. + + for (const auto& file : files) { + unlink(file.c_str()); + free_now = FreeSpaceForFile("/cache"); + printf("deleted %s; now %zu bytes free\n", file.c_str(), free_now); + if (free_now < bytes_needed) { + break; + } + } + return (free_now >= bytes_needed) ? 0 : -1; +} diff --git a/applypatch/imgdiff.c b/applypatch/imgdiff.cpp similarity index 90% rename from applypatch/imgdiff.c rename to applypatch/imgdiff.cpp index 3bac8be9..f22502e3 100644 --- a/applypatch/imgdiff.c +++ b/applypatch/imgdiff.cpp @@ -122,6 +122,7 @@ */ #include +#include #include #include #include @@ -179,7 +180,7 @@ static int fileentry_compare(const void* a, const void* b) { } // from bsdiff.c -int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* new, off_t newsize, +int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* newdata, off_t newsize, const char* patch_filename); unsigned char* ReadZip(const char* filename, @@ -191,9 +192,10 @@ unsigned char* ReadZip(const char* filename, return NULL; } - unsigned char* img = malloc(st.st_size); + size_t sz = static_cast(st.st_size); + unsigned char* img = reinterpret_cast(malloc(sz)); FILE* f = fopen(filename, "rb"); - if (fread(img, 1, st.st_size, f) != st.st_size) { + if (fread(img, 1, sz, f) != sz) { printf("failed to read \"%s\" %s\n", filename, strerror(errno)); fclose(f); return NULL; @@ -218,7 +220,8 @@ unsigned char* ReadZip(const char* filename, int cdcount = Read2(img+i+8); int cdoffset = Read4(img+i+16); - ZipFileEntry* temp_entries = malloc(cdcount * sizeof(ZipFileEntry)); + ZipFileEntry* temp_entries = reinterpret_cast(malloc( + cdcount * sizeof(ZipFileEntry))); int entrycount = 0; unsigned char* cd = img+cdoffset; @@ -235,7 +238,7 @@ unsigned char* ReadZip(const char* filename, int mlen = Read2(cd+32); // file comment len int hoffset = Read4(cd+42); // local header offset - char* filename = malloc(nlen+1); + char* filename = reinterpret_cast(malloc(nlen+1)); memcpy(filename, cd+46, nlen); filename[nlen] = '\0'; @@ -284,7 +287,7 @@ unsigned char* ReadZip(const char* filename, #endif *num_chunks = 0; - *chunks = malloc((entrycount*2+2) * sizeof(ImageChunk)); + *chunks = reinterpret_cast(malloc((entrycount*2+2) * sizeof(ImageChunk))); ImageChunk* curr = *chunks; if (include_pseudo_chunk) { @@ -311,7 +314,7 @@ unsigned char* ReadZip(const char* filename, curr->I = NULL; curr->len = temp_entries[nextentry].uncomp_len; - curr->data = malloc(curr->len); + curr->data = reinterpret_cast(malloc(curr->len)); z_stream strm; strm.zalloc = Z_NULL; @@ -381,9 +384,10 @@ unsigned char* ReadImage(const char* filename, return NULL; } - unsigned char* img = malloc(st.st_size + 4); + size_t sz = static_cast(st.st_size); + unsigned char* img = reinterpret_cast(malloc(sz + 4)); FILE* f = fopen(filename, "rb"); - if (fread(img, 1, st.st_size, f) != st.st_size) { + if (fread(img, 1, sz, f) != sz) { printf("failed to read \"%s\" %s\n", filename, strerror(errno)); fclose(f); return NULL; @@ -393,17 +397,18 @@ unsigned char* ReadImage(const char* filename, // append 4 zero bytes to the data so we can always search for the // four-byte string 1f8b0800 starting at any point in the actual // file data, without special-casing the end of the data. - memset(img+st.st_size, 0, 4); + memset(img+sz, 0, 4); size_t pos = 0; *num_chunks = 0; *chunks = NULL; - while (pos < st.st_size) { + while (pos < sz) { unsigned char* p = img+pos; - if (st.st_size - pos >= 4 && + bool processed_deflate = false; + if (sz - pos >= 4 && p[0] == 0x1f && p[1] == 0x8b && p[2] == 0x08 && // deflate compression p[3] == 0x00) { // no header flags @@ -411,7 +416,8 @@ unsigned char* ReadImage(const char* filename, size_t chunk_offset = pos; *num_chunks += 3; - *chunks = realloc(*chunks, *num_chunks * sizeof(ImageChunk)); + *chunks = reinterpret_cast(realloc(*chunks, + *num_chunks * sizeof(ImageChunk))); ImageChunk* curr = *chunks + (*num_chunks-3); // create a normal chunk for the header. @@ -435,7 +441,7 @@ unsigned char* ReadImage(const char* filename, size_t allocated = 32768; curr->len = 0; - curr->data = malloc(allocated); + curr->data = reinterpret_cast(malloc(allocated)); curr->start = pos; curr->deflate_data = p; @@ -443,7 +449,7 @@ unsigned char* ReadImage(const char* filename, strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; - strm.avail_in = st.st_size - pos; + strm.avail_in = sz - pos; strm.next_in = p; // -15 means we are decoding a 'raw' deflate stream; zlib will @@ -455,21 +461,27 @@ unsigned char* ReadImage(const char* filename, strm.next_out = curr->data + curr->len; ret = inflate(&strm, Z_NO_FLUSH); if (ret < 0) { - printf("Error: inflate failed [%s] at file offset [%zu]\n" - "imgdiff only supports gzip kernel compression," - " did you try CONFIG_KERNEL_LZO?\n", - strm.msg, chunk_offset); - free(img); - return NULL; + if (!processed_deflate) { + // This is the first chunk, assume that it's just a spurious + // gzip header instead of a real one. + break; + } + printf("Error: inflate failed [%s] at file offset [%zu]\n" + "imgdiff only supports gzip kernel compression," + " did you try CONFIG_KERNEL_LZO?\n", + strm.msg, chunk_offset); + free(img); + return NULL; } curr->len = allocated - strm.avail_out; if (strm.avail_out == 0) { allocated *= 2; - curr->data = realloc(curr->data, allocated); + curr->data = reinterpret_cast(realloc(curr->data, allocated)); } + processed_deflate = true; } while (ret != Z_STREAM_END); - curr->deflate_len = st.st_size - strm.avail_in - pos; + curr->deflate_len = sz - strm.avail_in - pos; inflateEnd(&strm); pos += curr->deflate_len; p += curr->deflate_len; @@ -493,8 +505,8 @@ unsigned char* ReadImage(const char* filename, // the decompression. size_t footer_size = Read4(p-4); if (footer_size != curr[-2].len) { - printf("Error: footer size %d != decompressed size %d\n", - footer_size, curr[-2].len); + printf("Error: footer size %zu != decompressed size %zu\n", + footer_size, curr[-2].len); free(img); return NULL; } @@ -502,7 +514,7 @@ unsigned char* ReadImage(const char* filename, // Reallocate the list for every chunk; we expect the number of // chunks to be small (5 for typical boot and recovery images). ++*num_chunks; - *chunks = realloc(*chunks, *num_chunks * sizeof(ImageChunk)); + *chunks = reinterpret_cast(realloc(*chunks, *num_chunks * sizeof(ImageChunk))); ImageChunk* curr = *chunks + (*num_chunks-1); curr->start = pos; curr->I = NULL; @@ -512,7 +524,7 @@ unsigned char* ReadImage(const char* filename, curr->type = CHUNK_NORMAL; curr->data = p; - for (curr->len = 0; curr->len < (st.st_size - pos); ++curr->len) { + for (curr->len = 0; curr->len < (sz - pos); ++curr->len) { if (p[curr->len] == 0x1f && p[curr->len+1] == 0x8b && p[curr->len+2] == 0x08 && @@ -587,7 +599,7 @@ int ReconstructDeflateChunk(ImageChunk* chunk) { } size_t p = 0; - unsigned char* out = malloc(BUFFER_SIZE); + unsigned char* out = reinterpret_cast(malloc(BUFFER_SIZE)); // We only check two combinations of encoder parameters: level 6 // (the default) and level 9 (the maximum). @@ -623,7 +635,15 @@ unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) { } char ptemp[] = "/tmp/imgdiff-patch-XXXXXX"; - mkstemp(ptemp); + int fd = mkstemp(ptemp); + + if (fd == -1) { + printf("MakePatch failed to create a temporary file: %s\n", + strerror(errno)); + return NULL; + } + close(fd); // temporary file is created and we don't need its file + // descriptor int r = bsdiff(src->data, src->len, &(src->I), tgt->data, tgt->len, ptemp); if (r != 0) { @@ -638,9 +658,11 @@ unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) { return NULL; } - unsigned char* data = malloc(st.st_size); + size_t sz = static_cast(st.st_size); + // TODO: Memory leak on error return. + unsigned char* data = reinterpret_cast(malloc(sz)); - if (tgt->type == CHUNK_NORMAL && tgt->len <= st.st_size) { + if (tgt->type == CHUNK_NORMAL && tgt->len <= sz) { unlink(ptemp); tgt->type = CHUNK_RAW; @@ -648,14 +670,14 @@ unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) { return tgt->data; } - *size = st.st_size; + *size = sz; FILE* f = fopen(ptemp, "rb"); if (f == NULL) { printf("failed to open patch %s: %s\n", ptemp, strerror(errno)); return NULL; } - if (fread(data, 1, st.st_size, f) != st.st_size) { + if (fread(data, 1, sz, f) != sz) { printf("failed to read patch %s: %s\n", ptemp, strerror(errno)); return NULL; } @@ -781,9 +803,8 @@ ImageChunk* FindChunkByName(const char* name, } void DumpChunks(ImageChunk* chunks, int num_chunks) { - int i; - for (i = 0; i < num_chunks; ++i) { - printf("chunk %d: type %d start %d len %d\n", + for (int i = 0; i < num_chunks; ++i) { + printf("chunk %d: type %d start %zu len %zu\n", i, chunks[i].type, chunks[i].start, chunks[i].len); } } @@ -806,7 +827,7 @@ int main(int argc, char** argv) { return 1; } bonus_size = st.st_size; - bonus_data = malloc(bonus_size); + bonus_data = reinterpret_cast(malloc(bonus_size)); FILE* f = fopen(argv[2], "rb"); if (f == NULL) { printf("failed to open bonus file %s: %s\n", argv[2], strerror(errno)); @@ -953,8 +974,9 @@ int main(int argc, char** argv) { DumpChunks(src_chunks, num_src_chunks); printf("Construct patches for %d chunks...\n", num_tgt_chunks); - unsigned char** patch_data = malloc(num_tgt_chunks * sizeof(unsigned char*)); - size_t* patch_size = malloc(num_tgt_chunks * sizeof(size_t)); + unsigned char** patch_data = reinterpret_cast(malloc( + num_tgt_chunks * sizeof(unsigned char*))); + size_t* patch_size = reinterpret_cast(malloc(num_tgt_chunks * sizeof(size_t))); for (i = 0; i < num_tgt_chunks; ++i) { if (zip_mode) { ImageChunk* src; @@ -967,15 +989,16 @@ int main(int argc, char** argv) { } } else { if (i == 1 && bonus_data) { - printf(" using %d bytes of bonus data for chunk %d\n", bonus_size, i); - src_chunks[i].data = realloc(src_chunks[i].data, src_chunks[i].len + bonus_size); + printf(" using %zu bytes of bonus data for chunk %d\n", bonus_size, i); + src_chunks[i].data = reinterpret_cast(realloc(src_chunks[i].data, + src_chunks[i].len + bonus_size)); memcpy(src_chunks[i].data+src_chunks[i].len, bonus_data, bonus_size); src_chunks[i].len += bonus_size; } patch_data[i] = MakePatch(src_chunks+i, tgt_chunks+i, patch_size+i); } - printf("patch %3d is %d bytes (of %d)\n", + printf("patch %3d is %zu bytes (of %zu)\n", i, patch_size[i], tgt_chunks[i].source_len); } @@ -1012,7 +1035,7 @@ int main(int argc, char** argv) { switch (tgt_chunks[i].type) { case CHUNK_NORMAL: - printf("chunk %3d: normal (%10d, %10d) %10d\n", i, + printf("chunk %3d: normal (%10zu, %10zu) %10zu\n", i, tgt_chunks[i].start, tgt_chunks[i].len, patch_size[i]); Write8(tgt_chunks[i].source_start, f); Write8(tgt_chunks[i].source_len, f); @@ -1021,7 +1044,7 @@ int main(int argc, char** argv) { break; case CHUNK_DEFLATE: - printf("chunk %3d: deflate (%10d, %10d) %10d %s\n", i, + printf("chunk %3d: deflate (%10zu, %10zu) %10zu %s\n", i, tgt_chunks[i].start, tgt_chunks[i].deflate_len, patch_size[i], tgt_chunks[i].filename); Write8(tgt_chunks[i].source_start, f); @@ -1038,7 +1061,7 @@ int main(int argc, char** argv) { break; case CHUNK_RAW: - printf("chunk %3d: raw (%10d, %10d)\n", i, + printf("chunk %3d: raw (%10zu, %10zu)\n", i, tgt_chunks[i].start, tgt_chunks[i].len); Write4(patch_size[i], f); fwrite(patch_data[i], 1, patch_size[i], f); diff --git a/applypatch/imgpatch.c b/applypatch/imgpatch.cpp similarity index 57% rename from applypatch/imgpatch.c rename to applypatch/imgpatch.cpp index 09b0a739..d175d638 100644 --- a/applypatch/imgpatch.c +++ b/applypatch/imgpatch.cpp @@ -21,23 +21,33 @@ #include #include #include -#include #include #include +#include + #include "zlib.h" -#include "mincrypt/sha.h" +#include "openssl/sha.h" #include "applypatch.h" #include "imgdiff.h" #include "utils.h" +int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, + const unsigned char* patch_data, ssize_t patch_size, + SinkFn sink, void* token) { + Value patch = {VAL_BLOB, patch_size, + reinterpret_cast(const_cast(patch_data))}; + return ApplyImagePatch( + old_data, old_size, &patch, sink, token, nullptr, nullptr); +} + /* * Apply the patch given in 'patch_filename' to the source data given * by (old_data, old_size). Write the patched output to the 'output' * file, and update the SHA context with the output data as well. * Return 0 on success. */ -int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, +int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value* patch, SinkFn sink, void* token, SHA_CTX* ctx, const Value* bonus_data) { @@ -80,6 +90,10 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, size_t src_len = Read8(normal_header+8); size_t patch_offset = Read8(normal_header+16); + if (src_start + src_len > static_cast(old_size)) { + printf("source data too short\n"); + return -1; + } ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, token, ctx); } else if (type == CHUNK_RAW) { @@ -96,7 +110,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, printf("failed to read chunk %d raw data\n", i); return -1; } - if (ctx) SHA_update(ctx, patch->data + pos, data_len); + if (ctx) SHA1_Update(ctx, patch->data + pos, data_len); if (sink((unsigned char*)patch->data + pos, data_len, token) != data_len) { printf("failed to write chunk %d raw data\n", i); @@ -116,13 +130,17 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, size_t src_len = Read8(deflate_header+8); size_t patch_offset = Read8(deflate_header+16); size_t expanded_len = Read8(deflate_header+24); - size_t target_len = Read8(deflate_header+32); int level = Read4(deflate_header+40); int method = Read4(deflate_header+44); int windowBits = Read4(deflate_header+48); int memLevel = Read4(deflate_header+52); int strategy = Read4(deflate_header+56); + if (src_start + src_len > static_cast(old_size)) { + printf("source data too short\n"); + return -1; + } + // Decompress the source data; the chunk header tells us exactly // how big we expect it to be when decompressed. @@ -132,57 +150,54 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, // must be appended from the bonus_data value. size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->size : 0; - unsigned char* expanded_source = malloc(expanded_len); - if (expanded_source == NULL) { - printf("failed to allocate %zu bytes for expanded_source\n", - expanded_len); - return -1; - } + std::vector expanded_source(expanded_len); - z_stream strm; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = src_len; - strm.next_in = (unsigned char*)(old_data + src_start); - strm.avail_out = expanded_len; - strm.next_out = expanded_source; + // inflate() doesn't like strm.next_out being a nullptr even with + // avail_out being zero (Z_STREAM_ERROR). + if (expanded_len != 0) { + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = src_len; + strm.next_in = (unsigned char*)(old_data + src_start); + strm.avail_out = expanded_len; + strm.next_out = expanded_source.data(); - int ret; - ret = inflateInit2(&strm, -15); - if (ret != Z_OK) { - printf("failed to init source inflation: %d\n", ret); - return -1; - } + int ret; + ret = inflateInit2(&strm, -15); + if (ret != Z_OK) { + printf("failed to init source inflation: %d\n", ret); + return -1; + } - // Because we've provided enough room to accommodate the output - // data, we expect one call to inflate() to suffice. - ret = inflate(&strm, Z_SYNC_FLUSH); - if (ret != Z_STREAM_END) { - printf("source inflation returned %d\n", ret); - return -1; - } - // We should have filled the output buffer exactly, except - // for the bonus_size. - if (strm.avail_out != bonus_size) { - printf("source inflation short by %zu bytes\n", strm.avail_out-bonus_size); - return -1; - } - inflateEnd(&strm); + // Because we've provided enough room to accommodate the output + // data, we expect one call to inflate() to suffice. + ret = inflate(&strm, Z_SYNC_FLUSH); + if (ret != Z_STREAM_END) { + printf("source inflation returned %d\n", ret); + return -1; + } + // We should have filled the output buffer exactly, except + // for the bonus_size. + if (strm.avail_out != bonus_size) { + printf("source inflation short by %zu bytes\n", strm.avail_out-bonus_size); + return -1; + } + inflateEnd(&strm); - if (bonus_size) { - memcpy(expanded_source + (expanded_len - bonus_size), - bonus_data->data, bonus_size); + if (bonus_size) { + memcpy(expanded_source.data() + (expanded_len - bonus_size), + bonus_data->data, bonus_size); + } } // Next, apply the bsdiff patch (in memory) to the uncompressed // data. - unsigned char* uncompressed_target_data; - ssize_t uncompressed_target_size; - if (ApplyBSDiffPatchMem(expanded_source, expanded_len, + std::vector uncompressed_target_data; + if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len, patch, patch_offset, - &uncompressed_target_data, - &uncompressed_target_size) != 0) { + &uncompressed_target_data) != 0) { return -1; } @@ -190,40 +205,40 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, // we're done with the expanded_source data buffer, so we'll // reuse that memory to receive the output of deflate. - unsigned char* temp_data = expanded_source; - ssize_t temp_size = expanded_len; - if (temp_size < 32768) { - // ... unless the buffer is too small, in which case we'll - // allocate a fresh one. - free(temp_data); - temp_data = malloc(32768); - temp_size = 32768; + if (expanded_source.size() < 32768U) { + expanded_source.resize(32768U); } - // now the deflate stream - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = uncompressed_target_size; - strm.next_in = uncompressed_target_data; - ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy); - do { - strm.avail_out = temp_size; - strm.next_out = temp_data; - ret = deflate(&strm, Z_FINISH); - ssize_t have = temp_size - strm.avail_out; + { + std::vector& temp_data = expanded_source; - if (sink(temp_data, have, token) != have) { - printf("failed to write %ld compressed bytes to output\n", - (long)have); + // now the deflate stream + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = uncompressed_target_data.size(); + strm.next_in = uncompressed_target_data.data(); + int ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy); + if (ret != Z_OK) { + printf("failed to init uncompressed data deflation: %d\n", ret); return -1; } - if (ctx) SHA_update(ctx, temp_data, have); - } while (ret != Z_STREAM_END); - deflateEnd(&strm); + do { + strm.avail_out = temp_data.size(); + strm.next_out = temp_data.data(); + ret = deflate(&strm, Z_FINISH); + ssize_t have = temp_data.size() - strm.avail_out; - free(temp_data); - free(uncompressed_target_data); + if (sink(temp_data.data(), have, token) != have) { + printf("failed to write %ld compressed bytes to output\n", + (long)have); + return -1; + } + if (ctx) SHA1_Update(ctx, temp_data.data(), have); + } while (ret != Z_STREAM_END); + deflateEnd(&strm); + } } else { printf("patch chunk %d is unknown type %d\n", i, type); return -1; diff --git a/applypatch/include/applypatch/imgpatch.h b/applypatch/include/applypatch/imgpatch.h new file mode 100644 index 00000000..64d9aa9e --- /dev/null +++ b/applypatch/include/applypatch/imgpatch.h @@ -0,0 +1,26 @@ +/* + * 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 _IMGPATCH_H +#define _IMGPATCH_H + +typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*); + +int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, + const unsigned char* patch_data, ssize_t patch_size, + SinkFn sink, void* token); + +#endif //_IMGPATCH_H diff --git a/applypatch/main.c b/applypatch/main.cpp similarity index 61% rename from applypatch/main.c rename to applypatch/main.cpp index 8e9fe80e..9013760c 100644 --- a/applypatch/main.c +++ b/applypatch/main.cpp @@ -19,18 +19,21 @@ #include #include +#include +#include + #include "applypatch.h" #include "edify/expr.h" -#include "mincrypt/sha.h" +#include "openssl/sha.h" -int CheckMode(int argc, char** argv) { +static int CheckMode(int argc, char** argv) { if (argc < 3) { return 2; } return applypatch_check(argv[2], argc-3, argv+3); } -int SpaceMode(int argc, char** argv) { +static int SpaceMode(int argc, char** argv) { if (argc != 3) { return 2; } @@ -43,79 +46,60 @@ int SpaceMode(int argc, char** argv) { return CacheSizeCheck(bytes); } -// Parse arguments (which should be of the form "" or -// ":" into the new parallel arrays *sha1s and -// *patches (loading file contents into the patches). Returns 0 on +// Parse arguments (which should be of the form ":" +// into the new parallel arrays *sha1s and *files.Returns true on // success. -static int ParsePatchArgs(int argc, char** argv, - char*** sha1s, Value*** patches, int* num_patches) { - *num_patches = argc; - *sha1s = malloc(*num_patches * sizeof(char*)); - *patches = malloc(*num_patches * sizeof(Value*)); - memset(*patches, 0, *num_patches * sizeof(Value*)); +static bool ParsePatchArgs(int argc, char** argv, std::vector* sha1s, + std::vector* files) { + uint8_t digest[SHA_DIGEST_LENGTH]; - uint8_t digest[SHA_DIGEST_SIZE]; - - int i; - for (i = 0; i < *num_patches; ++i) { + for (int i = 0; i < argc; ++i) { char* colon = strchr(argv[i], ':'); - if (colon != NULL) { - *colon = '\0'; - ++colon; + if (colon == nullptr) { + printf("no ':' in patch argument \"%s\"\n", argv[i]); + return false; } - + *colon = '\0'; + ++colon; if (ParseSha1(argv[i], digest) != 0) { printf("failed to parse sha1 \"%s\"\n", argv[i]); - return -1; + return false; } - (*sha1s)[i] = argv[i]; - if (colon == NULL) { - (*patches)[i] = NULL; - } else { - FileContents fc; - if (LoadFileContents(colon, &fc) != 0) { - goto abort; - } - (*patches)[i] = malloc(sizeof(Value)); - (*patches)[i]->type = VAL_BLOB; - (*patches)[i]->size = fc.size; - (*patches)[i]->data = (char*)fc.data; + sha1s->push_back(argv[i]); + FileContents fc; + if (LoadFileContents(colon, &fc) != 0) { + return false; } + files->push_back(std::move(fc)); } - - return 0; - - abort: - for (i = 0; i < *num_patches; ++i) { - Value* p = (*patches)[i]; - if (p != NULL) { - free(p->data); - free(p); - } - } - free(*sha1s); - free(*patches); - return -1; + return true; } -int PatchMode(int argc, char** argv) { - Value* bonus = NULL; +static int FlashMode(const char* src_filename, const char* tgt_filename, + const char* tgt_sha1, size_t tgt_size) { + return applypatch_flash(src_filename, tgt_filename, tgt_sha1, tgt_size); +} + +static int PatchMode(int argc, char** argv) { + FileContents bonusFc; + Value bonusValue; + Value* bonus = nullptr; + if (argc >= 3 && strcmp(argv[1], "-b") == 0) { - FileContents fc; - if (LoadFileContents(argv[2], &fc) != 0) { + if (LoadFileContents(argv[2], &bonusFc) != 0) { printf("failed to load bonus file %s\n", argv[2]); return 1; } - bonus = malloc(sizeof(Value)); + bonus = &bonusValue; bonus->type = VAL_BLOB; - bonus->size = fc.size; - bonus->data = (char*)fc.data; + bonus->size = bonusFc.data.size(); + bonus->data = reinterpret_cast(bonusFc.data.data()); argc -= 2; argv += 2; } - if (argc < 6) { + if (argc < 4) { return 2; } @@ -126,33 +110,31 @@ int PatchMode(int argc, char** argv) { return 1; } - char** sha1s; - Value** patches; - int num_patches; - if (ParsePatchArgs(argc-5, argv+5, &sha1s, &patches, &num_patches) != 0) { + // If no : is provided, it is in flash mode. + if (argc == 5) { + if (bonus != nullptr) { + printf("bonus file not supported in flash mode\n"); + return 1; + } + return FlashMode(argv[1], argv[2], argv[3], target_size); + } + std::vector sha1s; + std::vector files; + if (!ParsePatchArgs(argc-5, argv+5, &sha1s, &files)) { printf("failed to parse patch args\n"); return 1; } - - int result = applypatch(argv[1], argv[2], argv[3], target_size, - num_patches, sha1s, patches, bonus); - - int i; - for (i = 0; i < num_patches; ++i) { - Value* p = patches[i]; - if (p != NULL) { - free(p->data); - free(p); - } + std::vector patches(files.size()); + std::vector patch_ptrs(files.size()); + for (size_t i = 0; i < files.size(); ++i) { + patches[i].type = VAL_BLOB; + patches[i].size = files[i].data.size(); + patches[i].data = reinterpret_cast(files[i].data.data()); + patch_ptrs[i] = &patches[i]; } - if (bonus) { - free(bonus->data); - free(bonus); - } - free(sha1s); - free(patches); - - return result; + return applypatch(argv[1], argv[2], argv[3], target_size, + patch_ptrs.size(), sha1s.data(), + patch_ptrs.data(), bonus); } // This program applies binary patches to files in a way that is safe @@ -163,6 +145,10 @@ int PatchMode(int argc, char** argv) { // - if the sha1 hash of is , does nothing and exits // successfully. // +// - otherwise, if no : is provided, flashes with +// . must be a partition name, while must +// be a regular image file. will not be deleted on success. +// // - otherwise, if the sha1 hash of is , applies the // bsdiff to to produce a new file (the type of patch // is automatically detected from the file header). If that new diff --git a/applypatch/utils.c b/applypatch/utils.cpp similarity index 91% rename from applypatch/utils.c rename to applypatch/utils.cpp index 41ff676d..4a80be75 100644 --- a/applypatch/utils.c +++ b/applypatch/utils.cpp @@ -39,13 +39,13 @@ void Write8(long long value, FILE* f) { } int Read2(void* pv) { - unsigned char* p = pv; + unsigned char* p = reinterpret_cast(pv); return (int)(((unsigned int)p[1] << 8) | (unsigned int)p[0]); } int Read4(void* pv) { - unsigned char* p = pv; + unsigned char* p = reinterpret_cast(pv); return (int)(((unsigned int)p[3] << 24) | ((unsigned int)p[2] << 16) | ((unsigned int)p[1] << 8) | @@ -53,7 +53,7 @@ int Read4(void* pv) { } long long Read8(void* pv) { - unsigned char* p = pv; + unsigned char* p = reinterpret_cast(pv); return (long long)(((unsigned long long)p[7] << 56) | ((unsigned long long)p[6] << 48) | ((unsigned long long)p[5] << 40) | diff --git a/bootloader.cpp b/bootloader.cpp index b5b20e9c..13bc3916 100644 --- a/bootloader.cpp +++ b/bootloader.cpp @@ -14,24 +14,26 @@ * limitations under the License. */ -/* -#include -*/ -#include "bootloader.h" -#include "common.h" extern "C" { #include "mtdutils/mtdutils.h" } -/* -#include "roots.h" -*/ + #include +#include +#include #include #include #include +#include #include #include +#include "bootloader.h" +#include "common.h" +#include "mtdutils/mtdutils.h" +//#include "roots.h" +#include "unique_fd.h" + // fake Volume struct that allows us to use the AOSP code easily struct Volume { @@ -50,17 +52,17 @@ void set_misc_device(const char* type, const char* name) { } } -static int get_bootloader_message_mtd(struct bootloader_message *out, const Volume* v); -static int set_bootloader_message_mtd(const struct bootloader_message *in, const Volume* v); -static int get_bootloader_message_block(struct bootloader_message *out, const Volume* v); -static int set_bootloader_message_block(const struct bootloader_message *in, const Volume* v); +static int get_bootloader_message_mtd(bootloader_message* out, const Volume* v); +static int set_bootloader_message_mtd(const bootloader_message* in, const Volume* v); +static int get_bootloader_message_block(bootloader_message* out, const Volume* v); +static int set_bootloader_message_block(const bootloader_message* in, const Volume* v); -int get_bootloader_message(struct bootloader_message *out) { +int get_bootloader_message(bootloader_message* out) { #if 0 Volume* v = volume_for_path("/misc"); - if (v == NULL) { - LOGE("Cannot load volume /misc!\n"); - return -1; + if (v == nullptr) { + LOGE("Cannot load volume /misc!\n"); + return -1; } #else Volume* v = &misc; @@ -78,12 +80,12 @@ int get_bootloader_message(struct bootloader_message *out) { return -1; } -int set_bootloader_message(const struct bootloader_message *in) { +int set_bootloader_message(const bootloader_message* in) { #if 0 Volume* v = volume_for_path("/misc"); - if (v == NULL) { - LOGE("Cannot load volume /misc!\n"); - return -1; + if (v == nullptr) { + LOGE("Cannot load volume /misc!\n"); + return -1; } #else Volume* v = &misc; @@ -108,69 +110,69 @@ int set_bootloader_message(const struct bootloader_message *in) { static const int MISC_PAGES = 3; // number of pages to save static const int MISC_COMMAND_PAGE = 1; // bootloader command is this page -static int get_bootloader_message_mtd(struct bootloader_message *out, +static int get_bootloader_message_mtd(bootloader_message* out, const Volume* v) { size_t write_size; mtd_scan_partitions(); - const MtdPartition *part = mtd_find_partition_by_name(v->blk_device); - if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) { - LOGE("Can't find %s\n", v->blk_device); + const MtdPartition* part = mtd_find_partition_by_name(v->blk_device); + if (part == nullptr || mtd_partition_info(part, nullptr, nullptr, &write_size)) { + LOGE("failed to find \"%s\"\n", v->blk_device); return -1; } - MtdReadContext *read = mtd_read_partition(part); - if (read == NULL) { - LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno)); + MtdReadContext* read = mtd_read_partition(part); + if (read == nullptr) { + LOGE("failed to open \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } const ssize_t size = write_size * MISC_PAGES; char data[size]; ssize_t r = mtd_read_data(read, data, size); - if (r != size) LOGE("Can't read %s\n(%s)\n", v->blk_device, strerror(errno)); + if (r != size) LOGE("failed to read \"%s\": %s\n", v->blk_device, strerror(errno)); mtd_read_close(read); if (r != size) return -1; memcpy(out, &data[write_size * MISC_COMMAND_PAGE], sizeof(*out)); return 0; } -static int set_bootloader_message_mtd(const struct bootloader_message *in, +static int set_bootloader_message_mtd(const bootloader_message* in, const Volume* v) { size_t write_size; mtd_scan_partitions(); - const MtdPartition *part = mtd_find_partition_by_name(v->blk_device); - if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) { - LOGE("Can't find %s\n", v->blk_device); + const MtdPartition* part = mtd_find_partition_by_name(v->blk_device); + if (part == nullptr || mtd_partition_info(part, nullptr, nullptr, &write_size)) { + LOGE("failed to find \"%s\"\n", v->blk_device); return -1; } - MtdReadContext *read = mtd_read_partition(part); - if (read == NULL) { - LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno)); + MtdReadContext* read = mtd_read_partition(part); + if (read == nullptr) { + LOGE("failed to open \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } ssize_t size = write_size * MISC_PAGES; char data[size]; ssize_t r = mtd_read_data(read, data, size); - if (r != size) LOGE("Can't read %s\n(%s)\n", v->blk_device, strerror(errno)); + if (r != size) LOGE("failed to read \"%s\": %s\n", v->blk_device, strerror(errno)); mtd_read_close(read); if (r != size) return -1; memcpy(&data[write_size * MISC_COMMAND_PAGE], in, sizeof(*in)); - MtdWriteContext *write = mtd_write_partition(part); - if (write == NULL) { - LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno)); + MtdWriteContext* write = mtd_write_partition(part); + if (write == nullptr) { + LOGE("failed to open \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } if (mtd_write_data(write, data, size) != size) { - LOGE("Can't write %s\n(%s)\n", v->blk_device, strerror(errno)); + LOGE("failed to write \"%s\": %s\n", v->blk_device, strerror(errno)); mtd_write_close(write); return -1; } if (mtd_write_close(write)) { - LOGE("Can't finish %s\n(%s)\n", v->blk_device, strerror(errno)); + LOGE("failed to finish \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } @@ -186,63 +188,75 @@ static int set_bootloader_message_mtd(const struct bootloader_message *in, static void wait_for_device(const char* fn) { int tries = 0; int ret; - struct stat buf; do { ++tries; + struct stat buf; ret = stat(fn, &buf); - if (ret) { - printf("stat %s try %d: %s\n", fn, tries, strerror(errno)); + if (ret == -1) { + printf("failed to stat \"%s\" try %d: %s\n", fn, tries, strerror(errno)); sleep(1); } } while (ret && tries < 10); + if (ret) { - printf("failed to stat %s\n", fn); + printf("failed to stat \"%s\"\n", fn); } } -static int get_bootloader_message_block(struct bootloader_message *out, +static int get_bootloader_message_block(bootloader_message* out, const Volume* v) { wait_for_device(v->blk_device); FILE* f = fopen(v->blk_device, "rb"); - if (f == NULL) { - LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno)); + if (f == nullptr) { + LOGE("failed to open \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } #ifdef BOARD_RECOVERY_BLDRMSG_OFFSET fseek(f, BOARD_RECOVERY_BLDRMSG_OFFSET, SEEK_SET); #endif - struct bootloader_message temp; + bootloader_message temp; + int count = fread(&temp, sizeof(temp), 1, f); if (count != 1) { - LOGE("Failed reading %s\n(%s)\n", v->blk_device, strerror(errno)); + LOGE("failed to read \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } if (fclose(f) != 0) { - LOGE("Failed closing %s\n(%s)\n", v->blk_device, strerror(errno)); + LOGE("failed to close \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } memcpy(out, &temp, sizeof(temp)); return 0; } -static int set_bootloader_message_block(const struct bootloader_message *in, +static int set_bootloader_message_block(const bootloader_message* in, const Volume* v) { wait_for_device(v->blk_device); - FILE* f = fopen(v->blk_device, "rb+"); - if (f == NULL) { - LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno)); + unique_fd fd(open(v->blk_device, O_WRONLY | O_SYNC)); + if (fd.get() == -1) { + LOGE("failed to open \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } + #ifdef BOARD_RECOVERY_BLDRMSG_OFFSET - fseek(f, BOARD_RECOVERY_BLDRMSG_OFFSET, SEEK_SET); + lseek(fd.get(), BOARD_RECOVERY_BLDRMSG_OFFSET, SEEK_SET); #endif - int count = fwrite(in, sizeof(*in), 1, f); - if (count != 1) { - LOGE("Failed writing %s\n(%s)\n", v->blk_device, strerror(errno)); - return -1; + + size_t written = 0; + const uint8_t* start = reinterpret_cast(in); + size_t total = sizeof(*in); + while (written < total) { + ssize_t wrote = TEMP_FAILURE_RETRY(write(fd.get(), start + written, total - written)); + if (wrote == -1) { + LOGE("failed to write %" PRId64 " bytes: %s\n", + static_cast(written), strerror(errno)); + return -1; + } + written += wrote; } - if (fclose(f) != 0) { - LOGE("Failed closing %s\n(%s)\n", v->blk_device, strerror(errno)); + + if (fsync(fd.get()) == -1) { + LOGE("failed to fsync \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } return 0; diff --git a/bootloader.h b/bootloader.h index b9d70eda..db8a90fb 100644 --- a/bootloader.h +++ b/bootloader.h @@ -17,10 +17,6 @@ #ifndef _RECOVERY_BOOTLOADER_H #define _RECOVERY_BOOTLOADER_H -#ifdef __cplusplus -extern "C" { -#endif - /* Bootloader Message * * This structure describes the content of a block in flash @@ -43,6 +39,13 @@ extern "C" { * multiple times, so that the UI can reflect which invocation of the * package it is. If the value is of the format "#/#" (eg, "1/3"), * the UI will add a simple indicator of that status. + * + * The slot_suffix field is used for A/B implementations where the + * bootloader does not set the androidboot.ro.boot.slot_suffix kernel + * commandline parameter. This is used by fs_mgr to mount /system and + * other partitions with the slotselect flag set in fstab. A/B + * implementations are free to use all 32 bytes and may store private + * data past the first NUL-byte in this field. */ struct bootloader_message { char command[32]; @@ -55,7 +58,8 @@ struct bootloader_message { // stage string (for multistage packages) and possible future // expansion. char stage[32]; - char reserved[224]; + char slot_suffix[32]; + char reserved[192]; }; /* Read and write the bootloader command from the "misc" partition. @@ -67,8 +71,4 @@ int set_bootloader_message(const struct bootloader_message *in); void set_misc_device(const char* type, const char* name); void get_args(int *argc, char ***argv); -#ifdef __cplusplus -} -#endif - #endif diff --git a/data.cpp b/data.cpp index 553c9a4c..77557016 100644 --- a/data.cpp +++ b/data.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "variables.h" #include "data.hpp" diff --git a/device.cpp b/device.cpp index fd1a9875..2465b077 100644 --- a/device.cpp +++ b/device.cpp @@ -25,6 +25,7 @@ static const char* MENU_ITEMS[] = { "Wipe cache partition", "Mount /system", "View recovery logs", + "Run graphics test", "Power off", NULL }; @@ -43,7 +44,8 @@ Device::BuiltinAction Device::InvokeMenuItem(int menu_position) { case 5: return WIPE_CACHE; case 6: return MOUNT_SYSTEM; case 7: return VIEW_RECOVERY_LOGS; - case 8: return SHUTDOWN; + case 8: return RUN_GRAPHICS_TEST; + case 9: return SHUTDOWN; default: return NO_ACTION; } } diff --git a/device.h b/device.h index f74b6b04..5017782e 100644 --- a/device.h +++ b/device.h @@ -68,6 +68,7 @@ class Device { SHUTDOWN = 8, VIEW_RECOVERY_LOGS = 9, MOUNT_SYSTEM = 10, + RUN_GRAPHICS_TEST = 11, }; // Return the list of menu items (an array of strings, diff --git a/edify/Android.mk b/edify/Android.mk index 03c04e43..71cf7652 100644 --- a/edify/Android.mk +++ b/edify/Android.mk @@ -3,14 +3,9 @@ LOCAL_PATH := $(call my-dir) edify_src_files := \ - lexer.l \ - parser.y \ - expr.c - -# "-x c" forces the lex/yacc files to be compiled as c the build system -# otherwise forces them to be c++. Need to also add an explicit -std because the -# build system will soon default C++ to -std=c++11. -edify_cflags := -x c -std=gnu89 + lexer.ll \ + parser.yy \ + expr.cpp # # Build the host-side command line tool @@ -19,12 +14,16 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ $(edify_src_files) \ - main.c + main.cpp -LOCAL_CFLAGS := $(edify_cflags) -g -O0 +LOCAL_CPPFLAGS := -g -O0 LOCAL_MODULE := edify LOCAL_YACCFLAGS := -v -LOCAL_CFLAGS += -Wno-unused-parameter +LOCAL_CPPFLAGS += -Wno-unused-parameter +LOCAL_CPPFLAGS += -Wno-deprecated-register +LOCAL_CLANG := true +LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. +LOCAL_STATIC_LIBRARIES += libbase include $(BUILD_HOST_EXECUTABLE) @@ -35,8 +34,11 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(edify_src_files) -LOCAL_CFLAGS := $(edify_cflags) -LOCAL_CFLAGS += -Wno-unused-parameter +LOCAL_CPPFLAGS := -Wno-unused-parameter +LOCAL_CPPFLAGS += -Wno-deprecated-register LOCAL_MODULE := libedify +LOCAL_CLANG := true +LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. +LOCAL_STATIC_LIBRARIES += libbase include $(BUILD_STATIC_LIBRARY) diff --git a/edify/expr.c b/edify/expr.cpp similarity index 88% rename from edify/expr.c rename to edify/expr.cpp index 79f6282d..cc14fbe9 100644 --- a/edify/expr.c +++ b/edify/expr.cpp @@ -21,6 +21,11 @@ #include #include +#include + +#include +#include + #include "expr.h" // Functions should: @@ -36,7 +41,7 @@ char* Evaluate(State* state, Expr* expr) { Value* v = expr->fn(expr->name, state, expr->argc, expr->argv); if (v == NULL) return NULL; if (v->type != VAL_STRING) { - ErrorAbort(state, "expecting string, got value type %d", v->type); + ErrorAbort(state, kArgsParsingFailure, "expecting string, got value type %d", v->type); FreeValue(v); return NULL; } @@ -51,7 +56,7 @@ Value* EvaluateValue(State* state, Expr* expr) { Value* StringValue(char* str) { if (str == NULL) return NULL; - Value* v = malloc(sizeof(Value)); + Value* v = reinterpret_cast(malloc(sizeof(Value))); v->type = VAL_STRING; v->size = strlen(str); v->data = str; @@ -68,7 +73,7 @@ Value* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc == 0) { return StringValue(strdup("")); } - char** strings = malloc(argc * sizeof(char*)); + char** strings = reinterpret_cast(malloc(argc * sizeof(char*))); int i; for (i = 0; i < argc; ++i) { strings[i] = NULL; @@ -83,8 +88,9 @@ Value* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) { length += strlen(strings[i]); } - result = malloc(length+1); - int p = 0; + result = reinterpret_cast(malloc(length+1)); + int p; + p = 0; for (i = 0; i < argc; ++i) { strcpy(result+p, strings[i]); p += strlen(strings[i]); @@ -149,7 +155,7 @@ Value* AssertFn(const char* name, State* state, int argc, Expr* argv[]) { if (!b) { int prefix_len; int len = argv[i]->end - argv[i]->start; - char* err_src = malloc(len + 20); + char* err_src = reinterpret_cast(malloc(len + 20)); strcpy(err_src, "assert failed: "); prefix_len = strlen(err_src); memcpy(err_src + prefix_len, state->script + argv[i]->start, len); @@ -290,7 +296,8 @@ Value* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) { goto done; } - long r_int = strtol(right, &end, 10); + long r_int; + r_int = strtol(right, &end, 10); if (right[0] == '\0' || *end != '\0') { goto done; } @@ -325,11 +332,11 @@ Value* Literal(const char* name, State* state, int argc, Expr* argv[]) { Expr* Build(Function fn, YYLTYPE loc, int count, ...) { va_list v; va_start(v, count); - Expr* e = malloc(sizeof(Expr)); + Expr* e = reinterpret_cast(malloc(sizeof(Expr))); e->fn = fn; e->name = "(operator)"; e->argc = count; - e->argv = malloc(count * sizeof(Expr*)); + e->argv = reinterpret_cast(malloc(count * sizeof(Expr*))); int i; for (i = 0; i < count; ++i) { e->argv[i] = va_arg(v, Expr*); @@ -351,7 +358,7 @@ NamedFunction* fn_table = NULL; void RegisterFunction(const char* name, Function fn) { if (fn_entries >= fn_size) { fn_size = fn_size*2 + 1; - fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction)); + fn_table = reinterpret_cast(realloc(fn_table, fn_size * sizeof(NamedFunction))); } fn_table[fn_entries].name = name; fn_table[fn_entries].fn = fn; @@ -371,8 +378,8 @@ void FinishRegistration() { Function FindFunction(const char* name) { NamedFunction key; key.name = name; - NamedFunction* nf = bsearch(&key, fn_table, fn_entries, - sizeof(NamedFunction), fn_entry_compare); + NamedFunction* nf = reinterpret_cast(bsearch(&key, fn_table, fn_entries, + sizeof(NamedFunction), fn_entry_compare)); if (nf == NULL) { return NULL; } @@ -401,7 +408,7 @@ void RegisterBuiltins() { // 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 = malloc(count * sizeof(char*)); + char** args = reinterpret_cast(malloc(count * sizeof(char*))); va_list v; va_start(v, count); int i; @@ -427,7 +434,7 @@ int ReadArgs(State* state, Expr* argv[], int count, ...) { // 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 = malloc(count * sizeof(Value*)); + Value** args = reinterpret_cast(malloc(count * sizeof(Value*))); va_list v; va_start(v, count); int i; @@ -491,15 +498,29 @@ Value** ReadValueVarArgs(State* state, int argc, Expr* argv[]) { return args; } -// Use printf-style arguments to compose an error message to put into -// *state. Returns NULL. -Value* ErrorAbort(State* state, const char* format, ...) { - char* buffer = malloc(4096); - va_list v; - va_start(v, format); - vsnprintf(buffer, 4096, format, v); - va_end(v); +static void ErrorAbortV(State* state, const char* format, va_list ap) { + std::string buffer; + android::base::StringAppendV(&buffer, format, ap); free(state->errmsg); - state->errmsg = buffer; - return NULL; + state->errmsg = strdup(buffer.c_str()); + return; +} + +// Use printf-style arguments to compose an error message to put into +// *state. Returns nullptr. +Value* ErrorAbort(State* state, const char* format, ...) { + va_list ap; + va_start(ap, format); + ErrorAbortV(state, format, ap); + va_end(ap); + return nullptr; +} + +Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) { + va_list ap; + va_start(ap, format); + ErrorAbortV(state, format, ap); + va_end(ap); + state->cause_code = cause_code; + return nullptr; } diff --git a/edify/expr.h b/edify/expr.h index a9ed2f9c..88634799 100644 --- a/edify/expr.h +++ b/edify/expr.h @@ -19,12 +19,9 @@ #include +#include "error_code.h" #include "yydefs.h" -#ifdef __cplusplus -extern "C" { -#endif - #define MAX_STRING_LEN 1024 typedef struct Expr Expr; @@ -43,6 +40,17 @@ typedef struct { // Should be NULL initially, will be either NULL or a malloc'd // pointer after Evaluate() returns. char* errmsg; + + // error code indicates the type of failure (e.g. failure to update system image) + // during the OTA process. + ErrorCode error_code = kNoError; + + // cause code provides more detailed reason of an OTA failure (e.g. fsync error) + // in addition to the error code. + CauseCode cause_code = kNoCause; + + bool is_retry = false; + } State; #define VAL_STRING 1 // data will be NULL-terminated; size doesn't count null @@ -59,7 +67,7 @@ typedef Value* (*Function)(const char* name, State* state, struct Expr { Function fn; - char* name; + const char* name; int argc; Expr** argv; int start, end; @@ -156,7 +164,13 @@ Value** ReadValueVarArgs(State* state, int argc, Expr* argv[]); // Use printf-style arguments to compose an error message to put into // *state. Returns NULL. -Value* ErrorAbort(State* state, const char* format, ...) __attribute__((format(printf, 2, 3))); +Value* ErrorAbort(State* state, const char* format, ...) + __attribute__((format(printf, 2, 3), deprecated)); + +// ErrorAbort has an optional (but recommended) argument 'cause_code'. If the cause code +// is set, it will be logged into last_install and provides reason of OTA failures. +Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) + __attribute__((format(printf, 3, 4))); // Wrap a string into a Value, taking ownership of the string. Value* StringValue(char* str); @@ -166,8 +180,4 @@ void FreeValue(Value* v); int parse_string(const char* str, Expr** root, int* error_count); -#ifdef __cplusplus -} // extern "C" -#endif - #endif // _EXPRESSION_H diff --git a/edify/lexer.l b/edify/lexer.ll similarity index 76% rename from edify/lexer.l rename to edify/lexer.ll index fb2933be..b764d169 100644 --- a/edify/lexer.l +++ b/edify/lexer.ll @@ -16,6 +16,7 @@ */ #include +#include #include "expr.h" #include "yydefs.h" @@ -25,9 +26,7 @@ int gLine = 1; int gColumn = 1; int gPos = 0; -// TODO: enforce MAX_STRING_LEN during lexing -char string_buffer[MAX_STRING_LEN]; -char* string_pos; +std::string string_buffer; #define ADVANCE do {yylloc.start=gPos; yylloc.end=gPos+yyleng; \ gColumn+=yyleng; gPos+=yyleng;} while(0) @@ -43,7 +42,7 @@ char* string_pos; \" { BEGIN(STR); - string_pos = string_buffer; + string_buffer.clear(); yylloc.start = gPos; ++gColumn; ++gPos; @@ -54,36 +53,35 @@ char* string_pos; ++gColumn; ++gPos; BEGIN(INITIAL); - *string_pos = '\0'; - yylval.str = strdup(string_buffer); + yylval.str = strdup(string_buffer.c_str()); yylloc.end = gPos; return STRING; } - \\n { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\n'; } - \\t { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\t'; } - \\\" { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\"'; } - \\\\ { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\\'; } + \\n { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\n'); } + \\t { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\t'); } + \\\" { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\"'); } + \\\\ { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\\'); } \\x[0-9a-fA-F]{2} { gColumn += yyleng; gPos += yyleng; int val; sscanf(yytext+2, "%x", &val); - *string_pos++ = val; + string_buffer.push_back(static_cast(val)); } \n { ++gLine; ++gPos; gColumn = 1; - *string_pos++ = yytext[0]; + string_buffer.push_back(yytext[0]); } . { ++gColumn; ++gPos; - *string_pos++ = yytext[0]; + string_buffer.push_back(yytext[0]); } } diff --git a/edify/main.c b/edify/main.cpp similarity index 98% rename from edify/main.c rename to edify/main.cpp index b1baa0b1..6007a3d5 100644 --- a/edify/main.c +++ b/edify/main.cpp @@ -18,6 +18,8 @@ #include #include +#include + #include "expr.h" #include "parser.h" @@ -151,6 +153,9 @@ int test() { expect("greater_than_int(x, 3)", "", &errors); expect("greater_than_int(3, x)", "", &errors); + // big string + expect(std::string(8192, 's').c_str(), std::string(8192, 's').c_str(), &errors); + printf("\n"); return errors; diff --git a/edify/parser.y b/edify/parser.yy similarity index 93% rename from edify/parser.y rename to edify/parser.yy index f8fb2d12..098a6370 100644 --- a/edify/parser.y +++ b/edify/parser.yy @@ -70,7 +70,7 @@ input: expr { *root = $1; } ; expr: STRING { - $$ = malloc(sizeof(Expr)); + $$ = reinterpret_cast(malloc(sizeof(Expr))); $$->fn = Literal; $$->name = $1; $$->argc = 0; @@ -91,7 +91,7 @@ expr: STRING { | IF expr THEN expr ENDIF { $$ = Build(IfElseFn, @$, 2, $2, $4); } | IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, @$, 3, $2, $4, $6); } | STRING '(' arglist ')' { - $$ = malloc(sizeof(Expr)); + $$ = reinterpret_cast(malloc(sizeof(Expr))); $$->fn = FindFunction($1); if ($$->fn == NULL) { char buffer[256]; @@ -113,12 +113,12 @@ arglist: /* empty */ { } | expr { $$.argc = 1; - $$.argv = malloc(sizeof(Expr*)); + $$.argv = reinterpret_cast(malloc(sizeof(Expr*))); $$.argv[0] = $1; } | arglist ',' expr { $$.argc = $1.argc + 1; - $$.argv = realloc($$.argv, $$.argc * sizeof(Expr*)); + $$.argv = reinterpret_cast(realloc($$.argv, $$.argc * sizeof(Expr*))); $$.argv[$$.argc-1] = $3; } ; diff --git a/error_code.h b/error_code.h new file mode 100644 index 00000000..259319ab --- /dev/null +++ b/error_code.h @@ -0,0 +1,46 @@ +/* + * 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 _ERROR_CODE_H_ +#define _ERROR_CODE_H_ + +enum ErrorCode { + kNoError = -1, + kLowBattery = 20, + kZipVerificationFailure, + kZipOpenFailure +}; + +enum CauseCode { + kNoCause = -1, + kArgsParsingFailure = 100, + kStashCreationFailure, + kFileOpenFailure, + kLseekFailure, + kFreadFailure, + kFwriteFailure, + kFsyncFailure, + kLibfecFailure, + kFileGetPropFailure, + kFileRenameFailure, + kSymlinkFailure, + kSetMetadataFailure, + kTune2FsFailure, + kRebootFailure, + kVendorFailure = 200 +}; + +#endif diff --git a/etc/init.rc b/etc/init.rc index dc8b6e3f..907436c7 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -10,6 +10,9 @@ on early-init # This should occur before anything else (e.g. ueventd) is started. setcon u:r:init:s0 + # Set the security context of /postinstall if present. + restorecon /postinstall + start ueventd start healthd @@ -20,6 +23,7 @@ service set_permissive /sbin/permissive.sh on init export PATH /sbin:/system/bin export LD_LIBRARY_PATH .:/sbin + export ANDROID_ROOT /system export ANDROID_DATA /data export EXTERNAL_STORAGE /sdcard @@ -77,6 +81,11 @@ on late-init # Load properties, pre-Android 6.0 trigger load_all_props_action + # Load properties from /system/ + /factory after fs mount. Place + # this in another action so that the load will be scheduled after the prior + # issued fs triggers have completed. + trigger load_system_props_action + # Load properties, Android 6.0+ trigger load_system_props_action @@ -101,6 +110,7 @@ service healthd /sbin/healthd -r seclabel u:r:healthd:s0 service recovery /sbin/recovery + seclabel u:r:recovery:s0 service adbd /sbin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery disabled diff --git a/fuse_sdcard_provider.c b/fuse_sdcard_provider.cpp similarity index 53% rename from fuse_sdcard_provider.c rename to fuse_sdcard_provider.cpp index 4565c7b5..df963127 100644 --- a/fuse_sdcard_provider.c +++ b/fuse_sdcard_provider.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -34,7 +33,7 @@ struct file_data { }; static int read_block_file(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size) { - struct file_data* fd = (struct file_data*)cookie; + file_data* fd = reinterpret_cast(cookie); off64_t offset = ((off64_t) block) * fd->block_size; if (TEMP_FAILURE_RETRY(lseek64(fd->fd, offset, SEEK_SET)) == -1) { @@ -56,85 +55,34 @@ static int read_block_file(void* cookie, uint32_t block, uint8_t* buffer, uint32 } static void close_file(void* cookie) { - struct file_data* fd = (struct file_data*)cookie; + file_data* fd = reinterpret_cast(cookie); close(fd->fd); } -struct token { - pthread_t th; - const char* path; - int result; -}; - -static void* run_sdcard_fuse(void* cookie) { - struct token* t = (struct token*)cookie; - +bool start_sdcard_fuse(const char* path) { struct stat sb; - if (stat(t->path, &sb) < 0) { - fprintf(stderr, "failed to stat %s: %s\n", t->path, strerror(errno)); - t->result = -1; - return NULL; + if (stat(path, &sb) == -1) { + fprintf(stderr, "failed to stat %s: %s\n", path, strerror(errno)); + return false; } - struct file_data fd; - struct provider_vtab vtab; - - fd.fd = open(t->path, O_RDONLY); - if (fd.fd < 0) { - fprintf(stderr, "failed to open %s: %s\n", t->path, strerror(errno)); - t->result = -1; - return NULL; + file_data fd; + fd.fd = open(path, O_RDONLY); + if (fd.fd == -1) { + fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); + return false; } fd.file_size = sb.st_size; fd.block_size = 65536; + provider_vtab vtab; vtab.read_block = read_block_file; vtab.close = close_file; - t->result = run_fuse_sideload(&vtab, &fd, fd.file_size, fd.block_size); - return NULL; -} - -// How long (in seconds) we wait for the fuse-provided package file to -// appear, before timing out. -#define SDCARD_INSTALL_TIMEOUT 10 - -void* start_sdcard_fuse(const char* path) { - struct token* t = malloc(sizeof(struct token)); - - t->path = path; - pthread_create(&(t->th), NULL, run_sdcard_fuse, t); - - struct stat st; - int i; - for (i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) { - if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) { - if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT-1) { - sleep(1); - continue; - } else { - return NULL; - } - } - } - // The installation process expects to find the sdcard unmounted. // Unmount it with MNT_DETACH so that our open file continues to // work but new references see it as unmounted. umount2("/sdcard", MNT_DETACH); - return t; -} - -void finish_sdcard_fuse(void* cookie) { - if (cookie == NULL) return; - struct token* t = (struct token*)cookie; - - // Calling stat() on this magic filename signals the fuse - // filesystem to shut down. - struct stat st; - stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); - - pthread_join(t->th, NULL); - free(t); + return run_fuse_sideload(&vtab, &fd, fd.file_size, fd.block_size) == 0; } diff --git a/fuse_sdcard_provider.h b/fuse_sdcard_provider.h index dbfbcd52..bdc60f2b 100644 --- a/fuse_sdcard_provider.h +++ b/fuse_sdcard_provider.h @@ -17,13 +17,6 @@ #ifndef __FUSE_SDCARD_PROVIDER_H #define __FUSE_SDCARD_PROVIDER_H -#include - -__BEGIN_DECLS - -void* start_sdcard_fuse(const char* path); -void finish_sdcard_fuse(void* token); - -__END_DECLS +bool start_sdcard_fuse(const char* path); #endif diff --git a/fuse_sideload.c b/fuse_sideload.cpp similarity index 94% rename from fuse_sideload.c rename to fuse_sideload.cpp index f09b0260..66dc4be8 100644 --- a/fuse_sideload.c +++ b/fuse_sideload.cpp @@ -61,7 +61,8 @@ #include #include -#include "mincrypt/sha256.h" +#include + #include "fuse_sideload.h" #define PACKAGE_FILE_ID (FUSE_ROOT_ID+1) @@ -120,7 +121,7 @@ static void fuse_reply(struct fuse_data* fd, __u64 unique, const void *data, siz } static int handle_init(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { - const struct fuse_init_in* req = data; + const struct fuse_init_in* req = reinterpret_cast(data); struct fuse_init_out out; size_t fuse_struct_size; @@ -174,7 +175,7 @@ static void fill_attr(struct fuse_attr* attr, struct fuse_data* fd, attr->mode = mode; } -static int handle_getattr(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { +static int handle_getattr(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) { struct fuse_attr_out out; memset(&out, 0, sizeof(out)); out.attr_valid = 10; @@ -200,12 +201,12 @@ static int handle_lookup(void* data, struct fuse_data* fd, out.entry_valid = 10; out.attr_valid = 10; - if (strncmp(FUSE_SIDELOAD_HOST_FILENAME, data, + if (strncmp(FUSE_SIDELOAD_HOST_FILENAME, reinterpret_cast(data), sizeof(FUSE_SIDELOAD_HOST_FILENAME)) == 0) { out.nodeid = PACKAGE_FILE_ID; out.generation = PACKAGE_FILE_ID; fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); - } else if (strncmp(FUSE_SIDELOAD_HOST_EXIT_FLAG, data, + } else if (strncmp(FUSE_SIDELOAD_HOST_EXIT_FLAG, reinterpret_cast(data), sizeof(FUSE_SIDELOAD_HOST_EXIT_FLAG)) == 0) { out.nodeid = EXIT_FLAG_ID; out.generation = EXIT_FLAG_ID; @@ -218,7 +219,7 @@ static int handle_lookup(void* data, struct fuse_data* fd, return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; } -static int handle_open(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { +static int handle_open(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) { if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM; if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; @@ -273,27 +274,27 @@ static int fetch_block(struct fuse_data* fd, uint32_t block) { // block). // - Otherwise, return -EINVAL for the read. - uint8_t hash[SHA256_DIGEST_SIZE]; - SHA256_hash(fd->block_data, fd->block_size, hash); - uint8_t* blockhash = fd->hashes + block * SHA256_DIGEST_SIZE; - if (memcmp(hash, blockhash, SHA256_DIGEST_SIZE) == 0) { + uint8_t hash[SHA256_DIGEST_LENGTH]; + SHA256(fd->block_data, fd->block_size, hash); + uint8_t* blockhash = fd->hashes + block * SHA256_DIGEST_LENGTH; + if (memcmp(hash, blockhash, SHA256_DIGEST_LENGTH) == 0) { return 0; } int i; - for (i = 0; i < SHA256_DIGEST_SIZE; ++i) { + for (i = 0; i < SHA256_DIGEST_LENGTH; ++i) { if (blockhash[i] != 0) { fd->curr_block = -1; return -EIO; } } - memcpy(blockhash, hash, SHA256_DIGEST_SIZE); + memcpy(blockhash, hash, SHA256_DIGEST_LENGTH); return 0; } static int handle_read(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { - const struct fuse_read_in* req = data; + const struct fuse_read_in* req = reinterpret_cast(data); struct fuse_out_header outhdr; struct iovec vec[3]; int vec_used; @@ -397,10 +398,10 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, goto done; } - fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_SIZE); + fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_LENGTH); if (fd.hashes == NULL) { fprintf(stderr, "failed to allocate %d bites for hashes\n", - fd.file_blocks * SHA256_DIGEST_SIZE); + fd.file_blocks * SHA256_DIGEST_LENGTH); result = -1; goto done; } diff --git a/fuse_sideload.h b/fuse_sideload.h index f9e3bf0d..c0b16efb 100644 --- a/fuse_sideload.h +++ b/fuse_sideload.h @@ -17,10 +17,6 @@ #ifndef __FUSE_SIDELOAD_H #define __FUSE_SIDELOAD_H -#include - -__BEGIN_DECLS - // define the filenames created by the sideload FUSE filesystem #define FUSE_SIDELOAD_HOST_MOUNTPOINT "/sideload" #define FUSE_SIDELOAD_HOST_FILENAME "package.zip" @@ -39,6 +35,4 @@ struct provider_vtab { int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_size, uint32_t block_size); -__END_DECLS - #endif diff --git a/gui/Android.mk b/gui/Android.mk index 98d56294..3f97ca7e 100644 --- a/gui/Android.mk +++ b/gui/Android.mk @@ -38,7 +38,7 @@ else LOCAL_SRC_FILES += hardwarekeyboard.cpp endif -LOCAL_SHARED_LIBRARIES += libminuitwrp libc libstdc++ libminzip libaosprecovery +LOCAL_SHARED_LIBRARIES += libminuitwrp libc libstdc++ libminzip libaosprecovery libselinux LOCAL_MODULE := libguitwrp #TWRP_EVENT_LOGGING := true diff --git a/install.cpp b/install.cpp index b46bbdf9..5a439a1d 100644 --- a/install.cpp +++ b/install.cpp @@ -23,22 +23,31 @@ #include #include +#include +#include +#include + +#include +#include +#include + #include "common.h" +#include "error_code.h" #include "install.h" -#include "mincrypt/rsa.h" #include "minui/minui.h" #include "minzip/SysUtil.h" #include "minzip/Zip.h" #include "mtdutils/mounts.h" #include "mtdutils/mtdutils.h" #include "roots.h" -#include "verifier.h" #include "ui.h" +#include "verifier.h" extern RecoveryUI* ui; #define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" #define PUBLIC_KEYS_FILE "/res/keys" +static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata"; // Default allocation of progress bar segments to operations static const int VERIFICATION_PROGRESS_TIME = 60; @@ -46,9 +55,64 @@ static const float VERIFICATION_PROGRESS_FRACTION = 0.25; static const float DEFAULT_FILES_PROGRESS_FRACTION = 0.4; static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1; +// This function parses and returns the build.version.incremental +static int parse_build_number(std::string str) { + size_t pos = str.find("="); + if (pos != std::string::npos) { + std::string num_string = android::base::Trim(str.substr(pos+1)); + int build_number; + if (android::base::ParseInt(num_string.c_str(), &build_number, 0)) { + return build_number; + } + } + + LOGE("Failed to parse build number in %s.\n", str.c_str()); + return -1; +} + +// Read the build.version.incremental of src/tgt from the metadata and log it to last_install. +static void read_source_target_build(ZipArchive* zip, std::vector& log_buffer) { + const ZipEntry* meta_entry = mzFindZipEntry(zip, METADATA_PATH); + if (meta_entry == nullptr) { + LOGE("Failed to find %s in update package.\n", METADATA_PATH); + return; + } + + std::string meta_data(meta_entry->uncompLen, '\0'); + if (!mzReadZipEntry(zip, meta_entry, &meta_data[0], meta_entry->uncompLen)) { + LOGE("Failed to read metadata in update package.\n"); + return; + } + + // Examples of the pre-build and post-build strings in metadata: + // pre-build-incremental=2943039 + // post-build-incremental=2951741 + std::vector lines = android::base::Split(meta_data, "\n"); + for (const std::string& line : lines) { + std::string str = android::base::Trim(line); + if (android::base::StartsWith(str, "pre-build-incremental")){ + int source_build = parse_build_number(str); + if (source_build != -1) { + log_buffer.push_back(android::base::StringPrintf("source_build: %d", + source_build)); + } + } else if (android::base::StartsWith(str, "post-build-incremental")) { + int target_build = parse_build_number(str); + if (target_build != -1) { + log_buffer.push_back(android::base::StringPrintf("target_build: %d", + target_build)); + } + } + } +} + // If the package contains an update binary, extract it and run it. static int -try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) { +try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache, + std::vector& log_buffer, int retry_count) +{ + read_source_target_build(zip, log_buffer); + const ZipEntry* binary_entry = mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); if (binary_entry == NULL) { @@ -121,15 +185,19 @@ try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) { // // - the name of the package zip file. // + // - an optional argument "retry" if this update is a retry of a failed + // update attempt. + // - const char** args = (const char**)malloc(sizeof(char*) * 5); + const char** args = (const char**)malloc(sizeof(char*) * 6); args[0] = binary; args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk char* temp = (char*)malloc(10); sprintf(temp, "%d", pipefd[1]); args[2] = temp; args[3] = (char*)path; - args[4] = NULL; + args[4] = retry_count > 0 ? "retry" : NULL; + args[5] = NULL; pid_t pid = fork(); if (pid == 0) { @@ -142,6 +210,7 @@ try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) { close(pipefd[1]); *wipe_cache = false; + bool retry_update = false; char buffer[1024]; FILE* from_child = fdopen(pipefd[0], "r"); @@ -164,9 +233,9 @@ try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) { } else if (strcmp(command, "ui_print") == 0) { char* str = strtok(NULL, "\n"); if (str) { - ui->Print("%s", str); + ui->PrintOnScreenOnly("%s", str); } else { - ui->Print("\n"); + ui->PrintOnScreenOnly("\n"); } fflush(stdout); } else if (strcmp(command, "wipe_cache") == 0) { @@ -178,6 +247,12 @@ try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) { // to be able to reboot during installation (useful for // debugging packages that don't exit). ui->SetEnableReboot(true); + } else if (strcmp(command, "retry_update") == 0) { + retry_update = true; + } else if (strcmp(command, "log") == 0) { + // Save the logging request from updater and write to + // last_install later. + log_buffer.push_back(std::string(strtok(NULL, "\n"))); } else { LOGE("unknown command [%s]\n", command); } @@ -186,6 +261,9 @@ try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) { int status; waitpid(pid, &status, 0); + if (retry_update) { + return INSTALL_RETRY; + } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status)); return INSTALL_ERROR; @@ -195,7 +273,8 @@ try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) { } static int -really_install_package(const char *path, bool* wipe_cache, bool needs_mount) +really_install_package(const char *path, bool* wipe_cache, bool needs_mount, + std::vector& log_buffer, int retry_count) { ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); ui->Print("Finding update package...\n"); @@ -221,41 +300,46 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount) return INSTALL_CORRUPT; } - int numKeys; - Certificate* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); - if (loadedKeys == NULL) { + // Load keys. + std::vector loadedKeys; + if (!load_keys(PUBLIC_KEYS_FILE, loadedKeys)) { LOGE("Failed to load keys\n"); return INSTALL_CORRUPT; } - LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE); + LOGI("%zu key(s) loaded from %s\n", loadedKeys.size(), PUBLIC_KEYS_FILE); + // Verify package. ui->Print("Verifying update package...\n"); - - int err; - err = verify_file(map.addr, map.length); - free(loadedKeys); - LOGI("verify_file returned %d\n", err); + auto t0 = std::chrono::system_clock::now(); + int err = verify_file(map.addr, map.length, loadedKeys); + std::chrono::duration duration = std::chrono::system_clock::now() - t0; + ui->Print("Update package verification took %.1f s (result %d).\n", duration.count(), err); if (err != VERIFY_SUCCESS) { LOGE("signature verification failed\n"); + log_buffer.push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure)); + sysReleaseMap(&map); return INSTALL_CORRUPT; } - /* Try to open the package. - */ + // Try to open the package. ZipArchive zip; err = mzOpenZipArchive(map.addr, map.length, &zip); if (err != 0) { LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad"); + log_buffer.push_back(android::base::StringPrintf("error: %d", kZipOpenFailure)); + sysReleaseMap(&map); return INSTALL_CORRUPT; } - /* Verify and install the contents of the package. - */ + // Verify and install the contents of the package. ui->Print("Installing update...\n"); + if (retry_count > 0) { + ui->Print("Retry attempt: %d\n", retry_count); + } ui->SetEnableReboot(false); - int result = try_update_binary(path, &zip, wipe_cache); + int result = try_update_binary(path, &zip, wipe_cache, log_buffer, retry_count); ui->SetEnableReboot(true); ui->Print("\n"); @@ -266,9 +350,10 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount) int install_package(const char* path, bool* wipe_cache, const char* install_file, - bool needs_mount) + bool needs_mount, int retry_count) { modified_flash = true; + auto start = std::chrono::system_clock::now(); FILE* install_log = fopen_path(install_file, "w"); if (install_log) { @@ -278,15 +363,26 @@ install_package(const char* path, bool* wipe_cache, const char* install_file, LOGE("failed to open last_install: %s\n", strerror(errno)); } int result; + std::vector log_buffer; if (setup_install_mounts() != 0) { LOGE("failed to set up expected mounts for install; aborting\n"); result = INSTALL_ERROR; } else { - result = really_install_package(path, wipe_cache, needs_mount); + result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count); } - if (install_log) { + if (install_log != nullptr) { fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log); fputc('\n', install_log); + std::chrono::duration duration = std::chrono::system_clock::now() - start; + int count = static_cast(duration.count()); + // Report the time spent to apply OTA update in seconds. + fprintf(install_log, "time_total: %d\n", count); + fprintf(install_log, "retry: %d\n", retry_count); + + for (const auto& s : log_buffer) { + fprintf(install_log, "%s\n", s.c_str()); + } + fclose(install_log); } return result; diff --git a/install.h b/install.h index 092fd8f6..b4655dfa 100644 --- a/install.h +++ b/install.h @@ -24,12 +24,14 @@ extern "C" { #endif -enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE }; +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. -int install_package(const char* root_path, bool* wipe_cache, - const char* install_file, bool needs_mount); +int install_package(const char* root_path, bool* wipe_cache, const char* install_file, + bool needs_mount, int retry_count); + RSAPublicKey* load_keys(const char* filename, int* numKeys); #ifdef __cplusplus diff --git a/interlace-frames.py b/interlace-frames.py old mode 100644 new mode 100755 index 243e565e..6b435aa9 --- a/interlace-frames.py +++ b/interlace-frames.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # Copyright (C) 2014 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,42 +13,102 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Script to take a set of frames (PNG files) for a recovery animation +""" +Script to take a set of frames (PNG files) for a recovery animation and turn it into a single output image which contains the input frames interlaced by row. Run with the names of all the input frames on the -command line, in order, followed by the name of the output file.""" +command line, in order, followed by the name of the output file. +""" +from __future__ import print_function + +import argparse +import os.path import sys try: import Image import PngImagePlugin except ImportError: - print "This script requires the Python Imaging Library to be installed." + print("This script requires the Python Imaging Library to be installed.") sys.exit(1) -frames = [Image.open(fn).convert("RGB") for fn in sys.argv[1:-1]] -assert len(frames) > 0, "Must have at least one input frame." -sizes = set() -for fr in frames: - sizes.add(fr.size) -assert len(sizes) == 1, "All input images must have the same size." -w, h = sizes.pop() -N = len(frames) +def interlace(output, inputs): + frames = [Image.open(fn).convert("RGB") for fn in inputs] + assert len(frames) > 0, "Must have at least one input frame." + sizes = set() + for fr in frames: + sizes.add(fr.size) -out = Image.new("RGB", (w, h*N)) -for j in range(h): - for i in range(w): - for fn, f in enumerate(frames): - out.putpixel((i, j*N+fn), f.getpixel((i, j))) + assert len(sizes) == 1, "All input images must have the same size." + w, h = sizes.pop() + N = len(frames) -# When loading this image, the graphics library expects to find a text -# chunk that specifies how many frames this animation represents. If -# you post-process the output of this script with some kind of -# optimizer tool (eg pngcrush or zopflipng) make sure that your -# optimizer preserves this text chunk. + out = Image.new("RGB", (w, h*N)) + for j in range(h): + for i in range(w): + for fn, f in enumerate(frames): + out.putpixel((i, j*N+fn), f.getpixel((i, j))) -meta = PngImagePlugin.PngInfo() -meta.add_text("Frames", str(N)) + # When loading this image, the graphics library expects to find a text + # chunk that specifies how many frames this animation represents. If + # you post-process the output of this script with some kind of + # optimizer tool (eg pngcrush or zopflipng) make sure that your + # optimizer preserves this text chunk. -out.save(sys.argv[-1], pnginfo=meta) + meta = PngImagePlugin.PngInfo() + meta.add_text("Frames", str(N)) + + out.save(output, pnginfo=meta) + + +def deinterlace(output, input): + # Truncate the output filename extension if it's '.png'. + if os.path.splitext(output)[1].lower() == '.png': + output = output[:-4] + + img2 = Image.open(input) + print(img2.mode) + palette = img2.getpalette() + img = img2.convert("RGB") + num_frames = int(img.info.get('Frames', 1)) + print('Found %d frames in %s.' % (num_frames, input)) + assert num_frames > 0, 'Invalid Frames meta.' + + # palette = img.getpalette() + print(palette) + + width, height = img.size + height /= num_frames + for k in range(num_frames): + out = Image.new('RGB', (width, height)) + out.info = img.info + for i in range(width): + for j in range(height): + out.putpixel((i, j), img.getpixel((i, j * num_frames + k))) + # out.putpalette(img.getpalette(), rawmode='RGB') + out2 = out.convert(mode='P', palette=palette) + #out2 = out + print(out2.mode) + # out2.putpalette(palette) + filename = '%s%02d.png' % (output, k) + out2.save(filename) + print('Frame %d written to %s.' % (k, filename)) + + +def main(argv): + parser = argparse.ArgumentParser(description='Parse') + parser.add_argument('--deinterlace', '-d', action='store_true') + parser.add_argument('--output', '-o', required=True) + parser.add_argument('input', nargs='+') + args = parser.parse_args(argv) + + if args.deinterlace: + # args.input is a list, and we only process the first when deinterlacing. + deinterlace(args.output, args.input[0]) + else: + interlace(args.output, args.input) + + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/libblkid/src/probe.c b/libblkid/src/probe.c index b2e7435a..d476b7a2 100644 --- a/libblkid/src/probe.c +++ b/libblkid/src/probe.c @@ -1254,8 +1254,7 @@ done: /* same sa blkid_probe_get_buffer() but works with 512-sectors */ unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector) { - return pr ? blkid_probe_get_buffer(pr, - ((blkid_loff_t) sector) << 9, 0x200) : NULL; + return blkid_probe_get_buffer(pr, ((blkid_loff_t) sector) << 9, 0x200); } struct blkid_prval *blkid_probe_assign_value( diff --git a/libblkid/src/tag.c b/libblkid/src/tag.c index a59c0e63..6fcaa7e5 100644 --- a/libblkid/src/tag.c +++ b/libblkid/src/tag.c @@ -155,7 +155,7 @@ int blkid_set_tag(blkid_dev dev, const char *name, /* Existing tag not present, add to device */ if (!(t = blkid_new_tag())) goto errout; - t->bit_name = name ? strdup(name) : NULL; + t->bit_name = strdup(name); t->bit_val = val; t->bit_dev = dev; @@ -170,7 +170,7 @@ int blkid_set_tag(blkid_dev dev, const char *name, goto errout; DBG(TAG, ul_debug(" creating new cache tag head %s", name)); - head->bit_name = name ? strdup(name) : NULL; + head->bit_name = strdup(name); if (!head->bit_name) goto errout; list_add_tail(&head->bit_tags, diff --git a/minadbd/Android.mk b/minadbd/Android.mk index 36e14a51..2bfe1198 100644 --- a/minadbd/Android.mk +++ b/minadbd/Android.mk @@ -14,14 +14,15 @@ LOCAL_SRC_FILES := \ adb_main.cpp \ fuse_adb_provider.cpp \ services.cpp \ - ../fuse_sideload.c + ../fuse_sideload.cpp +LOCAL_CLANG := true LOCAL_MODULE := libminadbd LOCAL_CFLAGS := $(minadbd_cflags) LOCAL_CONLY_FLAGS := -Wimplicit-function-declaration LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. system/core/adb LOCAL_WHOLE_STATIC_LIBRARIES := libadbd -LOCAL_SHARED_LIBRARIES := libbase liblog libmincrypttwrp libcutils libc +LOCAL_SHARED_LIBRARIES := libbase liblog libcutils libc libcrypto include $(BUILD_SHARED_LIBRARY) diff --git a/minadbd/adb_main.cpp b/minadbd/adb_main.cpp index 7fae99a9..0694280c 100644 --- a/minadbd/adb_main.cpp +++ b/minadbd/adb_main.cpp @@ -19,21 +19,15 @@ #include #include -#define TRACE_TAG TRACE_ADB - #include "sysdeps.h" #include "adb.h" #include "adb_auth.h" #include "transport.h" -int adb_main(int is_daemon, int server_port) -{ - atexit(usb_cleanup); - +int adb_server_main(int is_daemon, int server_port, int /* reply_fd */) { adb_device_banner = "sideload"; - // No SIGCHLD. Let the service subproc handle its children. signal(SIGPIPE, SIG_IGN); // We can't require authentication for sideloading. http://b/22025550. @@ -42,7 +36,7 @@ int adb_main(int is_daemon, int server_port) init_transport_registration(); usb_init(); - D("Event loop starting\n"); + VLOG(ADB) << "Event loop starting"; fdevent_loop(); return 0; diff --git a/minadbd/services.cpp b/minadbd/services.cpp index dd1fd7c4..658a43f3 100644 --- a/minadbd/services.cpp +++ b/minadbd/services.cpp @@ -23,7 +23,6 @@ #include "sysdeps.h" -#define TRACE_TAG TRACE_SERVICES #include "adb.h" #include "fdevent.h" #include "fuse_adb_provider.h" @@ -36,21 +35,21 @@ struct stinfo { void *cookie; }; -void* service_bootstrap_func(void* x) { +void service_bootstrap_func(void* x) { stinfo* sti = reinterpret_cast(x); sti->func(sti->fd, sti->cookie); free(sti); - return 0; } static void sideload_host_service(int sfd, void* data) { - const char* args = reinterpret_cast(data); + char* args = reinterpret_cast(data); int file_size; int block_size; if (sscanf(args, "%d:%d", &file_size, &block_size) != 2) { printf("bad sideload-host arguments: %s\n", args); exit(1); } + free(args); printf("sideload-host file size %d block size %d\n", file_size, block_size); @@ -61,8 +60,7 @@ static void sideload_host_service(int sfd, void* data) { exit(result == 0 ? 0 : 1); } -static int create_service_thread(void (*func)(int, void *), void *cookie) -{ +static int create_service_thread(void (*func)(int, void *), void *cookie) { int s[2]; if(adb_socketpair(s)) { printf("cannot create service socket pair\n"); @@ -75,8 +73,7 @@ static int create_service_thread(void (*func)(int, void *), void *cookie) sti->cookie = cookie; sti->fd = s[1]; - adb_thread_t t; - if (adb_thread_create( &t, service_bootstrap_func, sti)){ + if (!adb_thread_create(service_bootstrap_func, sti)) { free(sti); adb_close(s[0]); adb_close(s[1]); @@ -84,11 +81,11 @@ static int create_service_thread(void (*func)(int, void *), void *cookie) return -1; } - D("service thread started, %d:%d\n",s[0], s[1]); + VLOG(SERVICES) << "service thread started, " << s[0] << ":" << s[1]; return s[0]; } -int service_to_fd(const char* name) { +int service_to_fd(const char* name, const atransport* transport) { int ret = -1; if (!strncmp(name, "sideload:", 9)) { @@ -97,7 +94,8 @@ int service_to_fd(const char* name) { // sideload-host). exit(3); } else if (!strncmp(name, "sideload-host:", 14)) { - ret = create_service_thread(sideload_host_service, (void*)(name + 14)); + char* arg = strdup(name + 14); + ret = create_service_thread(sideload_host_service, arg); } if (ret >= 0) { close_on_exec(ret); diff --git a/minui/Android.mk b/minui/Android.mk index afe38ede..1faaf6dc 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -83,6 +83,7 @@ include $(BUILD_STATIC_LIBRARY) # Used by OEMs for factory test images. include $(CLEAR_VARS) +LOCAL_CLANG := true LOCAL_MODULE := libminui LOCAL_WHOLE_STATIC_LIBRARIES += libminui LOCAL_SHARED_LIBRARIES := libpng diff --git a/minui/graphics_fbdev.cpp b/minui/graphics_fbdev.cpp index 512a7d0e..5f64e1e2 100644 --- a/minui/graphics_fbdev.cpp +++ b/minui/graphics_fbdev.cpp @@ -190,18 +190,6 @@ static GRSurface* fbdev_init(minui_backend* backend) { static GRSurface* fbdev_flip(minui_backend* backend __unused) { if (double_buffered) { -#if defined(RECOVERY_BGRA) - // In case of BGRA, do some byte swapping - unsigned int idx; - unsigned char tmp; - unsigned char* ucfb_vaddr = (unsigned char*)gr_draw->data; - for (idx = 0 ; idx < (gr_draw->height * gr_draw->row_bytes); - idx += 4) { - tmp = ucfb_vaddr[idx]; - ucfb_vaddr[idx ] = ucfb_vaddr[idx + 2]; - ucfb_vaddr[idx + 2] = tmp; - } -#endif // Change gr_draw to point to the buffer currently displayed, // then flip the driver so we're displaying the other buffer // instead. diff --git a/minui/minui.h b/minui/minui.h index 098a041e..5e49100c 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -87,6 +87,8 @@ int ev_get_epollfd(); // Resources // +bool matches_locale(const char* prefix, const char* locale); + // res_create_*_surface() functions return 0 if no error, else // negative. // @@ -104,8 +106,8 @@ int res_create_display_surface(const char* name, GRSurface** pSurface); // should have a 'Frames' text chunk whose value is the number of // frames this image represents. The pixel data itself is interlaced // by row. -int res_create_multi_display_surface(const char* name, - int* frames, GRSurface*** pSurface); +int res_create_multi_display_surface(const char* name, int* frames, + int* fps, GRSurface*** pSurface); // Load a single alpha surface from a grayscale PNG image. int res_create_alpha_surface(const char* name, GRSurface** pSurface); diff --git a/minui/resources.cpp b/minui/resources.cpp index 5e478927..40d3c2c8 100644 --- a/minui/resources.cpp +++ b/minui/resources.cpp @@ -32,8 +32,6 @@ #include "minui.h" -extern char* locale; - #define SURFACE_DATA_ALIGNMENT 8 static GRSurface* malloc_surface(size_t data_size) { @@ -237,14 +235,14 @@ int res_create_display_surface(const char* name, GRSurface** pSurface) { return result; } -int res_create_multi_display_surface(const char* name, int* frames, GRSurface*** pSurface) { +int res_create_multi_display_surface(const char* name, int* frames, int* fps, + GRSurface*** pSurface) { GRSurface** surface = NULL; int result = 0; png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_uint_32 width, height; png_byte channels; - int i; png_textp text; int num_text; unsigned char* p_row; @@ -257,14 +255,23 @@ int res_create_multi_display_surface(const char* name, int* frames, GRSurface*** if (result < 0) return result; *frames = 1; + *fps = 20; if (png_get_text(png_ptr, info_ptr, &text, &num_text)) { - for (i = 0; i < num_text; ++i) { + for (int i = 0; i < num_text; ++i) { if (text[i].key && strcmp(text[i].key, "Frames") == 0 && text[i].text) { *frames = atoi(text[i].text); - break; + } else if (text[i].key && strcmp(text[i].key, "FPS") == 0 && text[i].text) { + *fps = atoi(text[i].text); } } printf(" found frames = %d\n", *frames); + printf(" found fps = %d\n", *fps); + } + + if (frames <= 0 || fps <= 0) { + printf("bad number of frames (%d) and/or FPS (%d)\n", *frames, *fps); + result = -10; + goto exit; } if (height % *frames != 0) { @@ -278,7 +285,7 @@ int res_create_multi_display_surface(const char* name, int* frames, GRSurface*** result = -8; goto exit; } - for (i = 0; i < *frames; ++i) { + for (int i = 0; i < *frames; ++i) { surface[i] = init_display_surface(width, height / *frames); if (surface[i] == NULL) { result = -8; @@ -307,7 +314,7 @@ exit: if (result < 0) { if (surface) { - for (i = 0; i < *frames; ++i) { + for (int i = 0; i < *frames; ++i) { if (surface[i]) free(surface[i]); } free(surface); @@ -363,21 +370,16 @@ int res_create_alpha_surface(const char* name, GRSurface** pSurface) { return result; } -static int matches_locale(const char* loc, const char* locale) { - if (locale == NULL) return 0; +// This function tests if a locale string stored in PNG (prefix) matches +// the locale string provided by the system (locale). +bool matches_locale(const char* prefix, const char* locale) { + if (locale == NULL) return false; - if (strcmp(loc, locale) == 0) return 1; + // Return true if the whole string of prefix matches the top part of + // locale. For instance, prefix == "en" matches locale == "en_US"; + // and prefix == "zh_CN" matches locale == "zh_CN_#Hans". - // if loc does *not* have an underscore, and it matches the start - // of locale, and the next character in locale *is* an underscore, - // that's a match. For instance, loc == "en" matches locale == - // "en_US". - - int i; - for (i = 0; loc[i] != 0 && loc[i] != '_'; ++i); - if (loc[i] == '_') return 0; - - return (strncmp(locale, loc, i) == 0 && locale[i] == '_'); + return (strncmp(prefix, locale, strlen(prefix)) == 0); } int res_create_localized_alpha_surface(const char* name, diff --git a/minzip/Android.mk b/minzip/Android.mk index ad976e79..957ab0b2 100644 --- a/minzip/Android.mk +++ b/minzip/Android.mk @@ -22,9 +22,12 @@ LOCAL_CFLAGS += -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION) LOCAL_MODULE := libminzip -LOCAL_CFLAGS += -Wall LOCAL_SHARED_LIBRARIES += libz +LOCAL_CLANG := true + +LOCAL_CFLAGS += -Werror -Wall + include $(BUILD_SHARED_LIBRARY) @@ -47,9 +50,14 @@ LOCAL_STATIC_LIBRARIES += libselinux LOCAL_CFLAGS += -DHAVE_SELINUX endif +LOCAL_CFLAGS += -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION) + LOCAL_MODULE := libminzip -LOCAL_CFLAGS += -Wall LOCAL_STATIC_LIBRARIES += libz +LOCAL_CLANG := true + +LOCAL_CFLAGS += -Werror -Wall + include $(BUILD_STATIC_LIBRARY) diff --git a/minzip/Hash.c b/minzip/Hash.c index 8f8ed68e..49bcb316 100644 --- a/minzip/Hash.c +++ b/minzip/Hash.c @@ -361,7 +361,7 @@ void mzHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc, { const void* data = (const void*)mzHashIterData(&iter); int count; - + count = countProbes(pHashTable, (*calcFunc)(data), data, cmpFunc); numEntries++; @@ -373,7 +373,7 @@ void mzHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc, totalProbe += count; } - LOGI("Probe: min=%d max=%d, total=%d in %d (%d), avg=%.3f\n", + LOGV("Probe: min=%d max=%d, total=%d in %d (%d), avg=%.3f\n", minProbe, maxProbe, totalProbe, numEntries, pHashTable->tableSize, (float) totalProbe / (float) numEntries); } diff --git a/minzip/Hash.h b/minzip/Hash.h index 8194537f..e83eac41 100644 --- a/minzip/Hash.h +++ b/minzip/Hash.h @@ -15,6 +15,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + /* compute the hash of an item with a specific type */ typedef unsigned int (*HashCompute)(const void* item); @@ -183,4 +187,8 @@ typedef unsigned int (*HashCalcFunc)(const void* item); void mzHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc, HashCompareFunc cmpFunc); +#ifdef __cplusplus +} +#endif + #endif /*_MINZIP_HASH*/ diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c index 0ac1fa9b..9c3575b9 100644 --- a/minzip/SysUtil.c +++ b/minzip/SysUtil.c @@ -3,17 +3,18 @@ * * System utilities. */ -#include -#include -#include -#include -#include -#include -#include +#include +#include #include #include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #define LOG_TAG "sysutil" #include "Log.h" @@ -57,8 +58,7 @@ static int getFileStartAndLength(int fd, loff_t *start_, size_t *length_) * On success, returns 0 and fills out "pMap". On failure, returns a nonzero * value and does not disturb "pMap". */ -static int sysMapFD(int fd, MemMapping* pMap) -{ +static bool sysMapFD(int fd, MemMapping* pMap) { loff_t start; size_t length; void* memPtr; @@ -75,19 +75,23 @@ static int sysMapFD(int fd, MemMapping* pMap) memPtr = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, start); #endif if (memPtr == MAP_FAILED) { - LOGW("mmap(%d, R, PRIVATE, %d, %d) failed: %s\n", (int) length, - fd, (int) start, strerror(errno)); - return -1; + LOGE("mmap(%d, R, PRIVATE, %d, 0) failed: %s\n", (int) length, fd, strerror(errno)); + return false; } pMap->addr = memPtr; pMap->length = length; pMap->range_count = 1; pMap->ranges = malloc(sizeof(MappedRange)); + if (pMap->ranges == NULL) { + LOGE("malloc failed: %s\n", strerror(errno)); + munmap(memPtr, length); + return false; + } pMap->ranges[0].addr = memPtr; pMap->ranges[0].length = length; - return 0; + return true; } static int sysMapBlockFile(FILE* mapf, MemMapping* pMap) @@ -95,12 +99,12 @@ static int sysMapBlockFile(FILE* mapf, MemMapping* pMap) char block_dev[PATH_MAX+1]; size_t size; unsigned int blksize; - unsigned int blocks; + size_t blocks; unsigned int range_count; unsigned int i; if (fgets(block_dev, sizeof(block_dev), mapf) == NULL) { - LOGW("failed to read block device from header\n"); + LOGE("failed to read block device from header\n"); return -1; } for (i = 0; i < sizeof(block_dev); ++i) { @@ -111,15 +115,24 @@ static int sysMapBlockFile(FILE* mapf, MemMapping* pMap) } if (fscanf(mapf, "%zu %u\n%u\n", &size, &blksize, &range_count) != 3) { - LOGW("failed to parse block map header\n"); + LOGE("failed to parse block map header\n"); + return -1; + } + if (blksize != 0) { + blocks = ((size-1) / blksize) + 1; + } + if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0) { + LOGE("invalid data in block map file: size %zu, blksize %u, range_count %u\n", + size, blksize, range_count); return -1; } - blocks = ((size-1) / blksize) + 1; - pMap->range_count = range_count; - pMap->ranges = malloc(range_count * sizeof(MappedRange)); - memset(pMap->ranges, 0, range_count * sizeof(MappedRange)); + pMap->ranges = calloc(range_count, sizeof(MappedRange)); + if (pMap->ranges == NULL) { + LOGE("calloc(%u, %zu) failed: %s\n", range_count, sizeof(MappedRange), strerror(errno)); + return -1; + } // Reserve enough contiguous address space for the whole file. unsigned char* reserve; @@ -130,25 +143,34 @@ static int sysMapBlockFile(FILE* mapf, MemMapping* pMap) reserve = mmap(NULL, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); #endif if (reserve == MAP_FAILED) { - LOGW("failed to reserve address space: %s\n", strerror(errno)); + LOGE("failed to reserve address space: %s\n", strerror(errno)); + free(pMap->ranges); return -1; } - pMap->ranges[range_count-1].addr = reserve; - pMap->ranges[range_count-1].length = blocks * blksize; - int fd = open(block_dev, O_RDONLY); if (fd < 0) { - LOGW("failed to open block device %s: %s\n", block_dev, strerror(errno)); + LOGE("failed to open block device %s: %s\n", block_dev, strerror(errno)); + munmap(reserve, blocks * blksize); + free(pMap->ranges); return -1; } unsigned char* next = reserve; + size_t remaining_size = blocks * blksize; + bool success = true; for (i = 0; i < range_count; ++i) { - int start, end; - if (fscanf(mapf, "%d %d\n", &start, &end) != 2) { - LOGW("failed to parse range %d in block map\n", i); - return -1; + size_t start, end; + if (fscanf(mapf, "%zu %zu\n", &start, &end) != 2) { + LOGE("failed to parse range %d in block map\n", i); + success = false; + break; + } + size_t length = (end - start) * blksize; + if (end <= start || (end - start) > SIZE_MAX / blksize || length > remaining_size) { + LOGE("unexpected range in block map: %zu %zu\n", start, end); + success = false; + break; } #if (PLATFORM_SDK_VERSION >= 21) void* addr = mmap64(next, (end-start)*blksize, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize); @@ -157,15 +179,28 @@ static int sysMapBlockFile(FILE* mapf, MemMapping* pMap) void* addr = mmap(next, (end-start)*blksize, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize); #endif if (addr == MAP_FAILED) { - LOGW("failed to map block %d: %s\n", i, strerror(errno)); - return -1; + LOGE("failed to map block %d: %s\n", i, strerror(errno)); + success = false; + break; } pMap->ranges[i].addr = addr; - pMap->ranges[i].length = (end-start)*blksize; + pMap->ranges[i].length = length; - next += pMap->ranges[i].length; + next += length; + remaining_size -= length; + } + if (success && remaining_size != 0) { + LOGE("ranges in block map are invalid: remaining_size = %zu\n", 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; @@ -182,25 +217,26 @@ int sysMapFile(const char* fn, MemMapping* pMap) // A map of blocks FILE* mapf = fopen(fn+1, "r"); if (mapf == NULL) { - LOGV("Unable to open '%s': %s\n", fn+1, strerror(errno)); + LOGE("Unable to open '%s': %s\n", fn+1, strerror(errno)); return -1; } if (sysMapBlockFile(mapf, pMap) != 0) { - LOGW("Map of '%s' failed\n", fn); + LOGE("Map of '%s' failed\n", fn); + fclose(mapf); return -1; } fclose(mapf); } else { // This is a regular file. - int fd = open(fn, O_RDONLY, 0); - if (fd < 0) { + int fd = open(fn, O_RDONLY); + if (fd == -1) { LOGE("Unable to open '%s': %s\n", fn, strerror(errno)); return -1; } - if (sysMapFD(fd, pMap) != 0) { + if (!sysMapFD(fd, pMap)) { LOGE("Map of '%s' failed\n", fn); close(fd); return -1; @@ -219,7 +255,7 @@ 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) { - LOGW("munmap(%p, %d) failed: %s\n", + LOGE("munmap(%p, %d) failed: %s\n", pMap->ranges[i].addr, (int)pMap->ranges[i].length, strerror(errno)); } } diff --git a/minzip/Zip.c b/minzip/Zip.c index f47a480f..1c3239df 100644 --- a/minzip/Zip.c +++ b/minzip/Zip.c @@ -198,10 +198,10 @@ static bool parseZipArchive(ZipArchive* pArchive) */ val = get4LE(pArchive->addr); if (val == ENDSIG) { - LOGI("Found Zip archive, but it looks empty\n"); + LOGW("Found Zip archive, but it looks empty\n"); goto bail; } else if (val != LOCSIG) { - LOGV("Not a Zip archive (found 0x%08x)\n", val); + LOGW("Not a Zip archive (found 0x%08x)\n", val); goto bail; } @@ -217,7 +217,7 @@ static bool parseZipArchive(ZipArchive* pArchive) ptr--; } if (ptr < (const unsigned char*) pArchive->addr) { - LOGI("Could not find end-of-central-directory in Zip\n"); + LOGW("Could not find end-of-central-directory in Zip\n"); goto bail; } @@ -431,7 +431,7 @@ int mzOpenZipArchive(unsigned char* addr, size_t length, ZipArchive* pArchive) if (length < ENDHDR) { err = -1; - LOGV("File '%s' too small to be zip (%zd)\n", fileName, map.length); + LOGW("Archive %p is too small to be zip (%zd)\n", pArchive, length); goto bail; } @@ -440,7 +440,7 @@ int mzOpenZipArchive(unsigned char* addr, size_t length, ZipArchive* pArchive) if (!parseZipArchive(pArchive)) { err = -1; - LOGV("Parsing '%s' failed\n", fileName); + LOGW("Parsing archive %p failed\n", pArchive); goto bail; } @@ -508,7 +508,6 @@ static bool processDeflatedEntry(const ZipArchive *pArchive, void *cookie) { long result = -1; - unsigned char readBuf[32 * 1024]; unsigned char procBuf[32 * 1024]; z_stream zstream; int zerr; @@ -551,7 +550,7 @@ static bool processDeflatedEntry(const ZipArchive *pArchive, /* uncompress the data */ zerr = inflate(&zstream, Z_NO_FLUSH); if (zerr != Z_OK && zerr != Z_STREAM_END) { - LOGD("zlib inflate call failed (zerr=%d)\n", zerr); + LOGW("zlib inflate call failed (zerr=%d)\n", zerr); goto z_bail; } @@ -605,7 +604,6 @@ bool mzProcessZipEntryContents(const ZipArchive *pArchive, void *cookie) { bool ret = false; - off_t oldOff; switch (pEntry->compression) { case STORED: @@ -623,13 +621,6 @@ bool mzProcessZipEntryContents(const ZipArchive *pArchive, return ret; } -static bool crcProcessFunction(const unsigned char *data, int dataLen, - void *crc) -{ - *(unsigned long *)crc = crc32(*(unsigned long *)crc, data, dataLen); - return true; -} - typedef struct { char *buf; int bufLen; @@ -1012,7 +1003,7 @@ bool mzExtractRecursive(const ZipArchive *pArchive, if (callback != NULL) callback(targetFile, cookie); } - LOGD("Extracted %d file(s)\n", extractCount); + LOGV("Extracted %d file(s)\n", extractCount); free(helper.buf); free(zpath); diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk index 2a380ae3..7e5fadc7 100644 --- a/mtdutils/Android.mk +++ b/mtdutils/Android.mk @@ -14,7 +14,7 @@ endif LOCAL_MODULE := libmtdutils LOCAL_STATIC_LIBRARIES := libcutils libc -LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_CLANG := true include $(BUILD_STATIC_LIBRARY) @@ -31,6 +31,7 @@ endif LOCAL_MODULE := libmtdutils LOCAL_SHARED_LIBRARIES := libcutils libc +LOCAL_CLANG := true include $(BUILD_SHARED_LIBRARY) diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c index 14be57f1..6779d6e9 100644 --- a/mtdutils/mtdutils.c +++ b/mtdutils/mtdutils.c @@ -297,20 +297,20 @@ static int read_block(const MtdPartition *partition, int fd, char *data) if (TEMP_FAILURE_RETRY(lseek64(fd, pos, SEEK_SET)) != pos || TEMP_FAILURE_RETRY(read(fd, data, size)) != size) { printf("mtd: read error at 0x%08llx (%s)\n", - pos, strerror(errno)); + (long long)pos, strerror(errno)); } else if (ioctl(fd, ECCGETSTATS, &after)) { printf("mtd: ECCGETSTATS error (%s)\n", strerror(errno)); return -1; } else if (after.failed != before.failed) { printf("mtd: ECC errors (%d soft, %d hard) at 0x%08llx\n", - after.corrected - before.corrected, - after.failed - before.failed, pos); + after.corrected - before.corrected, + after.failed - before.failed, (long long)pos); // copy the comparison baseline for the next read. memcpy(&before, &after, sizeof(struct mtd_ecc_stats)); } else if ((mgbb = ioctl(fd, MEMGETBADBLOCK, &pos))) { fprintf(stderr, "mtd: MEMGETBADBLOCK returned %d at 0x%08llx: %s\n", - mgbb, pos, strerror(errno)); + mgbb, (long long)pos, strerror(errno)); } else { return 0; // Success! } diff --git a/mtp/Android.mk b/mtp/Android.mk index 34514b8c..7128bdef 100644 --- a/mtp/Android.mk +++ b/mtp/Android.mk @@ -32,7 +32,7 @@ LOCAL_SRC_FILES = \ twrpMtp.cpp \ mtp_MtpDatabase.cpp \ node.cpp -LOCAL_SHARED_LIBRARIES += libz libc libusbhost libstdc++ libdl libcutils libutils libaosprecovery +LOCAL_SHARED_LIBRARIES += libz libc libusbhost libstdc++ libdl libcutils libutils libaosprecovery libselinux ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) LOCAL_SHARED_LIBRARIES += libstlport diff --git a/otafault/Android.mk b/otafault/Android.mk new file mode 100644 index 00000000..ba7add85 --- /dev/null +++ b/otafault/Android.mk @@ -0,0 +1,44 @@ +# Copyright 2015 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 languae governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +otafault_static_libs := \ + libbase \ + libminzip \ + libz \ + libselinux + +LOCAL_SRC_FILES := config.cpp ota_io.cpp +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE := libotafault +LOCAL_CLANG := true +LOCAL_C_INCLUDES := bootable/recovery +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) +LOCAL_WHOLE_STATIC_LIBRARIES := $(otafault_static_libs) + +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := config.cpp ota_io.cpp test.cpp +LOCAL_MODULE_TAGS := tests +LOCAL_MODULE := otafault_test +LOCAL_STATIC_LIBRARIES := $(otafault_static_libs) +LOCAL_C_INCLUDES := bootable/recovery +LOCAL_FORCE_STATIC_EXECUTABLE := true + +include $(BUILD_EXECUTABLE) diff --git a/otafault/config.cpp b/otafault/config.cpp new file mode 100644 index 00000000..b4567392 --- /dev/null +++ b/otafault/config.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015 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 +#include + +#include +#include + +#include + +#include "minzip/Zip.h" +#include "config.h" +#include "ota_io.h" + +#define OTAIO_MAX_FNAME_SIZE 128 + +static ZipArchive* archive; +static std::map should_inject_cache; + +static std::string get_type_path(const char* io_type) { + return android::base::StringPrintf("%s/%s", OTAIO_BASE_DIR, io_type); +} + +void ota_io_init(ZipArchive* za) { + archive = za; + ota_set_fault_files(); +} + +bool should_fault_inject(const char* io_type) { + // archive will be NULL if we used an entry point other + // than updater/updater.cpp:main + if (archive == NULL) { + return false; + } + const std::string type_path = get_type_path(io_type); + if (should_inject_cache.find(type_path) != should_inject_cache.end()) { + return should_inject_cache[type_path]; + } + const ZipEntry* entry = mzFindZipEntry(archive, type_path.c_str()); + should_inject_cache[type_path] = entry != nullptr; + return entry != NULL; +} + +bool should_hit_cache() { + return should_fault_inject(OTAIO_CACHE); +} + +std::string fault_fname(const char* io_type) { + std::string type_path = get_type_path(io_type); + std::string fname; + fname.resize(OTAIO_MAX_FNAME_SIZE); + const ZipEntry* entry = mzFindZipEntry(archive, type_path.c_str()); + mzReadZipEntry(archive, entry, &fname[0], OTAIO_MAX_FNAME_SIZE); + return fname; +} diff --git a/otafault/config.h b/otafault/config.h new file mode 100644 index 00000000..4430be3f --- /dev/null +++ b/otafault/config.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 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. + */ + +/* + * Read configuration files in the OTA package to determine which files, if any, will trigger errors. + * + * OTA packages can be modified to trigger errors by adding a top-level + * directory called .libotafault, which may optionally contain up to three + * files called READ, WRITE, and FSYNC. Each one of these optional files + * contains the name of a single file on the device disk which will cause + * an IO error on the first call of the appropriate I/O action to that file. + * + * Example: + * ota.zip + * + * .libotafault + * WRITE + * + * If the contents of the file WRITE were /system/build.prop, the first write + * action to /system/build.prop would fail with EIO. Note that READ and + * FSYNC files are absent, so these actions will not cause an error. + */ + +#ifndef _UPDATER_OTA_IO_CFG_H_ +#define _UPDATER_OTA_IO_CFG_H_ + +#include + +#include + +#include "minzip/Zip.h" + +#define OTAIO_BASE_DIR ".libotafault" +#define OTAIO_READ "READ" +#define OTAIO_WRITE "WRITE" +#define OTAIO_FSYNC "FSYNC" +#define OTAIO_CACHE "CACHE" + +/* + * Initialize libotafault by providing a reference to the OTA package. + */ +void ota_io_init(ZipArchive* za); + +/* + * Return true if a config file is present for the given IO type. + */ +bool should_fault_inject(const char* io_type); + +/* + * Return true if an EIO should occur on the next hit to /cache/saved.file + * instead of the next hit to the specified file. + */ +bool should_hit_cache(); + +/* + * Return the name of the file that should cause an error for the + * given IO type. + */ +std::string fault_fname(const char* io_type); + +#endif diff --git a/otafault/ota_io.cpp b/otafault/ota_io.cpp new file mode 100644 index 00000000..04458537 --- /dev/null +++ b/otafault/ota_io.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2015 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 + +#include +#include +#include +#include +#include + +#include "config.h" +#include "ota_io.h" + +static std::map filename_cache; +static std::string read_fault_file_name = ""; +static std::string write_fault_file_name = ""; +static std::string fsync_fault_file_name = ""; + +static bool get_hit_file(const char* cached_path, std::string ffn) { + return should_hit_cache() + ? !strncmp(cached_path, OTAIO_CACHE_FNAME, strlen(cached_path)) + : !strncmp(cached_path, ffn.c_str(), strlen(cached_path)); +} + +void ota_set_fault_files() { + if (should_fault_inject(OTAIO_READ)) { + read_fault_file_name = fault_fname(OTAIO_READ); + } + if (should_fault_inject(OTAIO_WRITE)) { + write_fault_file_name = fault_fname(OTAIO_WRITE); + } + if (should_fault_inject(OTAIO_FSYNC)) { + fsync_fault_file_name = fault_fname(OTAIO_FSYNC); + } +} + +bool have_eio_error = false; + +int ota_open(const char* path, int oflags) { + // Let the caller handle errors; we do not care if open succeeds or fails + int fd = open(path, oflags); + filename_cache[fd] = path; + return fd; +} + +int ota_open(const char* path, int oflags, mode_t mode) { + int fd = open(path, oflags, mode); + filename_cache[fd] = path; + return fd; } + +FILE* ota_fopen(const char* path, const char* mode) { + FILE* fh = fopen(path, mode); + filename_cache[(intptr_t)fh] = path; + return fh; +} + +int ota_close(int fd) { + // descriptors can be reused, so make sure not to leave them in the cache + filename_cache.erase(fd); + return close(fd); +} + +int ota_fclose(FILE* fh) { + filename_cache.erase((intptr_t)fh); + return fclose(fh); +} + +size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream) { + if (should_fault_inject(OTAIO_READ)) { + auto cached = filename_cache.find((intptr_t)stream); + const char* cached_path = cached->second; + if (cached != filename_cache.end() && + get_hit_file(cached_path, read_fault_file_name)) { + read_fault_file_name = ""; + errno = EIO; + have_eio_error = true; + return 0; + } + } + size_t status = fread(ptr, size, nitems, stream); + if (status != nitems && errno == EIO) { + have_eio_error = true; + } + return status; +} + +ssize_t ota_read(int fd, void* buf, size_t nbyte) { + if (should_fault_inject(OTAIO_READ)) { + auto cached = filename_cache.find(fd); + const char* cached_path = cached->second; + if (cached != filename_cache.end() + && get_hit_file(cached_path, read_fault_file_name)) { + read_fault_file_name = ""; + errno = EIO; + have_eio_error = true; + return -1; + } + } + ssize_t status = read(fd, buf, nbyte); + if (status == -1 && errno == EIO) { + have_eio_error = true; + } + return status; +} + +size_t ota_fwrite(const void* ptr, size_t size, size_t count, FILE* stream) { + if (should_fault_inject(OTAIO_WRITE)) { + auto cached = filename_cache.find((intptr_t)stream); + const char* cached_path = cached->second; + if (cached != filename_cache.end() && + get_hit_file(cached_path, write_fault_file_name)) { + write_fault_file_name = ""; + errno = EIO; + have_eio_error = true; + return 0; + } + } + size_t status = fwrite(ptr, size, count, stream); + if (status != count && errno == EIO) { + have_eio_error = true; + } + return status; +} + +ssize_t ota_write(int fd, const void* buf, size_t nbyte) { + if (should_fault_inject(OTAIO_WRITE)) { + auto cached = filename_cache.find(fd); + const char* cached_path = cached->second; + if (cached != filename_cache.end() && + get_hit_file(cached_path, write_fault_file_name)) { + write_fault_file_name = ""; + errno = EIO; + have_eio_error = true; + return -1; + } + } + ssize_t status = write(fd, buf, nbyte); + if (status == -1 && errno == EIO) { + have_eio_error = true; + } + return status; +} + +int ota_fsync(int fd) { + if (should_fault_inject(OTAIO_FSYNC)) { + auto cached = filename_cache.find(fd); + const char* cached_path = cached->second; + if (cached != filename_cache.end() && + get_hit_file(cached_path, fsync_fault_file_name)) { + fsync_fault_file_name = ""; + errno = EIO; + have_eio_error = true; + return -1; + } + } + int status = fsync(fd); + if (status == -1 && errno == EIO) { + have_eio_error = true; + } + return status; +} + diff --git a/otafault/ota_io.h b/otafault/ota_io.h new file mode 100644 index 00000000..84187a76 --- /dev/null +++ b/otafault/ota_io.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 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. + */ + +/* + * Provide a series of proxy functions for basic file accessors. + * The behavior of these functions can be changed to return different + * errors under a variety of conditions. + */ + +#ifndef _UPDATER_OTA_IO_H_ +#define _UPDATER_OTA_IO_H_ + +#include +#include + +#define OTAIO_CACHE_FNAME "/cache/saved.file" + +void ota_set_fault_files(); + +int ota_open(const char* path, int oflags); + +int ota_open(const char* path, int oflags, mode_t mode); + +FILE* ota_fopen(const char* filename, const char* mode); + +int ota_close(int fd); + +int ota_fclose(FILE* fh); + +size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream); + +ssize_t ota_read(int fd, void* buf, size_t nbyte); + +size_t ota_fwrite(const void* ptr, size_t size, size_t count, FILE* stream); + +ssize_t ota_write(int fd, const void* buf, size_t nbyte); + +int ota_fsync(int fd); + +#endif diff --git a/otafault/test.cpp b/otafault/test.cpp new file mode 100644 index 00000000..6514782b --- /dev/null +++ b/otafault/test.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015 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 +#include +#include +#include + +#include "ota_io.h" + +int main(int /* argc */, char** /* argv */) { + int fd = open("testdata/test.file", O_RDWR); + char buf[8]; + const char* out = "321"; + int readv = ota_read(fd, buf, 4); + printf("Read returned %d\n", readv); + int writev = ota_write(fd, out, 4); + printf("Write returned %d\n", writev); + close(fd); + return 0; +} diff --git a/prebuilt/Android.mk b/prebuilt/Android.mk index 6e68d963..c5b46dfb 100644 --- a/prebuilt/Android.mk +++ b/prebuilt/Android.mk @@ -86,11 +86,16 @@ RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libmmcutils.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libbmlutils.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libflashutils.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libfusesideload.so -ifeq (,$(filter $(PLATFORM_SDK_VERSION), 23)) +ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) # These libraries are no longer present in M RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libstlport.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libgccdemangle.so endif +ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 23; echo $$?),0) + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcrypto.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libpackagelistparser.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/liblzma.so +endif ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22)) # libraries from lollipop RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libbacktrace.so @@ -99,7 +104,7 @@ ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22)) # Dynamically loaded by lollipop libc and may prevent unmounting system if it is not present in sbin RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libnetd_client.so else - ifneq (,$(filter $(PLATFORM_SDK_VERSION), 23)) + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 23; echo $$?),0) # Android M libraries RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libbacktrace.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libunwind.so @@ -112,8 +117,8 @@ else RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcorkscrew.so endif endif -RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libmincrypttwrp.so -RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/toolbox +#RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libmincrypttwrp.so +#RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/toolbox ifneq ($(TW_OEM_BUILD),true) RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/twrp else @@ -180,7 +185,7 @@ ifeq ($(TARGET_USERIMAGES_USE_F2FS), true) ifeq ($(shell test $(CM_PLATFORM_SDK_VERSION) -ge 4; echo $$?),0) RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/mkfs.f2fs RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libf2fs.so - else ifneq (,$(filter $(PLATFORM_SDK_VERSION), 23)) + else ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 23; echo $$?),0) RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/mkfs.f2fs else ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22)) RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT_SBIN)/mkfs.f2fs @@ -230,7 +235,7 @@ endif TWRP_AUTOGEN := $(intermediates)/teamwin GEN := $(intermediates)/teamwin $(GEN): $(RELINK) -$(GEN): $(RELINK_SOURCE_FILES) $(call intermediates-dir-for,EXECUTABLES,recovery)/recovery +$(GEN): $(RELINK_SOURCE_FILES) $(call intermediates-dir-for,EXECUTABLES,init)/init $(RELINK) $(TARGET_RECOVERY_ROOT_OUT)/sbin $(RELINK_SOURCE_FILES) LOCAL_GENERATED_SOURCES := $(GEN) @@ -280,11 +285,11 @@ endif # copy license file for OpenAES ifneq ($(TW_EXCLUDE_ENCRYPTED_BACKUPS), true) include $(CLEAR_VARS) - LOCAL_MODULE := ../openaes/LICENSE + LOCAL_MODULE := openaes_license LOCAL_MODULE_TAGS := eng LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/license/openaes - LOCAL_SRC_FILES := $(LOCAL_MODULE) + LOCAL_SRC_FILES := ../openaes/LICENSE include $(BUILD_PREBUILT) endif diff --git a/print_sha1.h b/print_sha1.h new file mode 100644 index 00000000..c7c1f365 --- /dev/null +++ b/print_sha1.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 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 RECOVERY_PRINT_SHA1_H +#define RECOVERY_PRINT_SHA1_H + +#include +#include + +#include "openssl/sha.h" + +static std::string print_sha1(const uint8_t* sha1, size_t len) { + const char* hex = "0123456789abcdef"; + std::string result = ""; + for (size_t i = 0; i < len; ++i) { + result.push_back(hex[(sha1[i]>>4) & 0xf]); + result.push_back(hex[sha1[i] & 0xf]); + } + return result; +} + +static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) { + return print_sha1(sha1, SHA_DIGEST_LENGTH); +} + +static std::string short_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) { + return print_sha1(sha1, 4); +} + +static std::string print_hex(const uint8_t* bytes, size_t len) { + return print_sha1(bytes, len); +} + +#endif // RECOVERY_PRINT_SHA1_H diff --git a/recovery-persist.cpp b/recovery-persist.cpp new file mode 100644 index 00000000..25df03f4 --- /dev/null +++ b/recovery-persist.cpp @@ -0,0 +1,205 @@ +/* + * 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. + */ + +#define LOG_TAG "recovery-persist" + +// +// Strictly to deal with reboot into system after OTA after /data +// mounts to pull the last pmsg file data and place it +// into /data/misc/recovery/ directory, rotating it in. +// +// Usage: recovery-persist [--force-persist] +// +// On systems without /cache mount, all file content representing in the +// recovery/ directory stored in /sys/fs/pstore/pmsg-ramoops-0 in logger +// format that reside in the LOG_ID_SYSTEM buffer at ANDROID_LOG_INFO +// priority or higher is transfered to the /data/misc/recovery/ directory. +// The content is matched and rotated in as need be. +// +// --force-persist ignore /cache mount, always rotate in the contents. +// + +#include +#include +#include +#include +#include + +#include + +#include /* Android Log Priority Tags */ +#include +#include +#include /* Android Log packet format */ +#include /* private pmsg functions */ + +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); + if (ferror(fp)) SLOGE("%s %s", name, strerror(errno)); + fclose(fp); +} + +static void copy_file(const char* source, const char* destination) { + FILE* dest_fp = fopen(destination, "w"); + if (dest_fp == nullptr) { + SLOGE("%s %s", destination, strerror(errno)); + } else { + FILE* source_fp = fopen(source, "r"); + if (source_fp != nullptr) { + char buf[4096]; + size_t bytes; + while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) { + fwrite(buf, 1, bytes, dest_fp); + } + check_and_fclose(source_fp, source); + } + check_and_fclose(dest_fp, 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 */, + const char *filename, + const char *buf, size_t len, + void * /* arg */) { + + std::string destination("/data/misc/"); + destination += filename; + + std::string buffer(buf, len); + + { + std::string content; + android::base::ReadFileToString(destination, &content); + + if (buffer.compare(content) == 0) { + return len; + } + } + + // ToDo: Any others that match? Are we pulling in multiple + // already-rotated files? Algorithm thus far is KISS: one file, + // one rotation allowed. + + rotate_logs(KEEP_LOG_COUNT); + + return android::base::WriteStringToFile(buffer, destination.c_str()); +} + +int main(int argc, char **argv) { + + /* Is /cache a mount?, we have been delivered where we are not wanted */ + /* + * Following code halves the size of the executable as compared to: + * + * load_volume_table(); + * has_cache = volume_for_path(CACHE_ROOT) != nullptr; + */ + bool has_cache = false; + static const char mounts_file[] = "/proc/mounts"; + FILE *fp = fopen(mounts_file, "r"); + if (!fp) { + SLOGV("%s %s", mounts_file, strerror(errno)); + } else { + char *line = NULL; + size_t len = 0; + ssize_t read; + while ((read = getline(&line, &len, fp)) != -1) { + if (strstr(line, " /cache ")) { + has_cache = true; + break; + } + } + free(line); + fclose(fp); + } + + if (has_cache) { + /* + * TBD: Future location to move content from + * /cache/recovery to /data/misc/recovery/ + */ + /* if --force-persist flag, then transfer pmsg data anyways */ + if ((argc <= 1) || !argv[1] || strcmp(argv[1], "--force-persist")) { + return 0; + } + } + + /* Is there something in pmsg? */ + if (access(LAST_PMSG_FILE, R_OK)) { + return 0; + } + + // Take last pmsg file contents and send it off to the logsave + __android_log_pmsg_file_read( + LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", logsave, NULL); + + /* Is there a last console log too? */ + if (rotated) { + if (!access(LAST_CONSOLE_FILE, R_OK)) { + copy_file(LAST_CONSOLE_FILE, LAST_KMSG_FILE); + } else if (!access(ALT_LAST_CONSOLE_FILE, R_OK)) { + copy_file(ALT_LAST_CONSOLE_FILE, LAST_KMSG_FILE); + } + } + + return 0; +} diff --git a/recovery-persist.rc b/recovery-persist.rc new file mode 100644 index 00000000..6761627d --- /dev/null +++ b/recovery-persist.rc @@ -0,0 +1,3 @@ +on post-fs-data + mkdir /data/misc/recovery 0770 system log + exec - system log -- /system/bin/recovery-persist diff --git a/recovery-refresh.cpp b/recovery-refresh.cpp new file mode 100644 index 00000000..70adc70e --- /dev/null +++ b/recovery-refresh.cpp @@ -0,0 +1,128 @@ +/* + * 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. + */ + +#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 +// into recovery to continue with any mitigations with retained +// log history. This simply refreshes the pmsg files from +// the last pmsg file contents. +// +// Usage: +// recovery-refresh [--force-rotate|--rotate] +// +// All file content representing in the recovery/ directory stored in +// /sys/fs/pstore/pmsg-ramoops-0 in logger format that reside in the +// LOG_ID_SYSTEM buffer at ANDROID_LOG_INFO priority or higher is +// refreshed into /dev/pmsg0. This ensures that an unexpected reboot +// before recovery-persist is run will still contain the associated +// pmsg Android Logger content. +// +// --force-rotate recovery/last_kmsg and recovery.last_log files are +// rotated with . suffixes upwards. +// --rotate rotated only if rocovery/last_msg or recovery/last_log +// exist, otherwise perform 1:1 refresh. +// + +#include + +#include + +#include /* Android Log Priority Tags */ +#include /* Android Log packet format */ +#include /* 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(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(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 { + unsigned long long 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) { + static const char filter[] = "recovery/"; + static const char force_rotate_flag[] = "--force-rotate"; + 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))) && + strcmp(argv[1], force_rotate_flag))) { + doRotate = false; + } else if (!doRotate) { + // Do we need to rotate? + __android_log_pmsg_file_read( + LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, + logbasename, &doRotate); + } + + // Take action to refresh pmsg contents + ret = __android_log_pmsg_file_read( + LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, + logrotate, &doRotate); + + return (ret < 0) ? ret : 0; +} diff --git a/recovery-refresh.rc b/recovery-refresh.rc new file mode 100644 index 00000000..14b05cca --- /dev/null +++ b/recovery-refresh.rc @@ -0,0 +1,2 @@ +on post-fs + exec - system log -- /system/bin/recovery-refresh diff --git a/recovery.cpp b/recovery.cpp index b7a54589..10f84140 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -28,33 +28,44 @@ #include #include #include +#include #include #include -#include -#include +#include +#include +#include /* Android Log Priority Tags */ +#include +#include +#include +#include +#include +#include /* Android Log packet format */ +#include /* private pmsg functions */ + +#include + +#include "adb_install.h" #include "bootloader.h" #include "common.h" -#include "cutils/properties.h" -#include "cutils/android_reboot.h" +#include "device.h" +#include "error_code.h" +#include "fuse_sdcard_provider.h" +#include "fuse_sideload.h" #include "install.h" #include "minui/minui.h" #include "minzip/DirUtil.h" #include "roots.h" #include "ui.h" #include "screen_ui.h" -#include "device.h" -#include "adb_install.h" -#include "adb.h" -#include "fuse_sideload.h" -#include "fuse_sdcard_provider.h" struct selabel_handle *sehandle; static const struct option OPTIONS[] = { { "send_intent", required_argument, NULL, 'i' }, { "update_package", required_argument, NULL, 'u' }, + { "retry_count", required_argument, NULL, 'n' }, { "wipe_data", no_argument, NULL, 'w' }, { "wipe_cache", no_argument, NULL, 'c' }, { "show_text", no_argument, NULL, 't' }, @@ -65,6 +76,7 @@ static const struct option OPTIONS[] = { { "stages", required_argument, NULL, 'g' }, { "shutdown_after", no_argument, NULL, 'p' }, { "reason", required_argument, NULL, 'r' }, + { "security", no_argument, NULL, 'e'}, { NULL, 0, NULL, 0 }, }; @@ -74,19 +86,31 @@ static const char *INTENT_FILE = "/cache/recovery/intent"; static const char *LOG_FILE = "/cache/recovery/log"; static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install"; static const char *LOCALE_FILE = "/cache/recovery/last_locale"; +static const char *CONVERT_FBE_DIR = "/tmp/convert_fbe"; +static const char *CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe"; static const char *CACHE_ROOT = "/cache"; +static const char *DATA_ROOT = "/data"; static const char *SDCARD_ROOT = "/sdcard"; 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; +// GmsCore enters recovery mode to install package when having enough battery +// percentage. Normally, the threshold is 40% without charger and 20% with charger. +// 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; RecoveryUI* ui = NULL; -char* locale = NULL; +static const char* locale = "en_US"; char* stage = NULL; char* reason = NULL; bool modified_flash = false; +static bool has_cache = false; /* * The recovery tool communicates with the main system through /cache files. @@ -151,8 +175,7 @@ static const int MAX_ARG_LENGTH = 4096; static const int MAX_ARGS = 100; // open a given path, mounting partitions as necessary -FILE* -fopen_path(const char *path, const char *mode) { +FILE* fopen_path(const char *path, const char *mode) { if (ensure_path_mounted(path) != 0) { LOGE("Can't mount %s\n", path); return NULL; @@ -166,23 +189,102 @@ fopen_path(const char *path, const char *mode) { return fp; } +// 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 (ferror(fp)) LOGE("Error in %s\n(%s)\n", name, strerror(errno)); + fclose(fp); +} + bool is_ro_debuggable() { char value[PROPERTY_VALUE_MAX+1]; return (property_get("ro.debuggable", value, NULL) == 1 && value[0] == '1'); } static void redirect_stdio(const char* filename) { - // If these fail, there's not really anywhere to complain... - freopen(filename, "a", stdout); setbuf(stdout, NULL); - freopen(filename, "a", stderr); setbuf(stderr, NULL); -} + int pipefd[2]; + if (pipe(pipefd) == -1) { + LOGE("pipe failed: %s\n", strerror(errno)); -// 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 (ferror(fp)) LOGE("Error in %s\n(%s)\n", name, strerror(errno)); - fclose(fp); + // Fall back to traditional logging mode without timestamps. + // If these fail, there's not really anywhere to complain... + freopen(filename, "a", stdout); setbuf(stdout, NULL); + freopen(filename, "a", stderr); setbuf(stderr, NULL); + + return; + } + + pid_t pid = fork(); + if (pid == -1) { + LOGE("fork failed: %s\n", strerror(errno)); + + // Fall back to traditional logging mode without timestamps. + // If these fail, there's not really anywhere to complain... + freopen(filename, "a", stdout); setbuf(stdout, NULL); + freopen(filename, "a", stderr); setbuf(stderr, NULL); + + return; + } + + if (pid == 0) { + /// Close the unused write end. + close(pipefd[1]); + + auto start = std::chrono::steady_clock::now(); + + // Child logger to actually write to the log file. + FILE* log_fp = fopen(filename, "a"); + if (log_fp == nullptr) { + LOGE("fopen \"%s\" failed: %s\n", filename, strerror(errno)); + close(pipefd[0]); + _exit(1); + } + + FILE* pipe_fp = fdopen(pipefd[0], "r"); + if (pipe_fp == nullptr) { + LOGE("fdopen failed: %s\n", strerror(errno)); + check_and_fclose(log_fp, filename); + close(pipefd[0]); + _exit(1); + } + + char* line = nullptr; + size_t len = 0; + while (getline(&line, &len, pipe_fp) != -1) { + auto now = std::chrono::steady_clock::now(); + double duration = std::chrono::duration_cast>( + now - start).count(); + if (line[0] == '\n') { + fprintf(log_fp, "[%12.6lf]\n", duration); + } else { + fprintf(log_fp, "[%12.6lf] %s", duration, line); + } + fflush(log_fp); + } + + LOGE("getline failed: %s\n", strerror(errno)); + + free(line); + check_and_fclose(log_fp, filename); + close(pipefd[0]); + _exit(1); + } else { + // Redirect stdout/stderr to the logger process. + // Close the unused read end. + close(pipefd[0]); + + setbuf(stdout, nullptr); + setbuf(stderr, nullptr); + + if (dup2(pipefd[1], STDOUT_FILENO) == -1) { + LOGE("dup2 stdout failed: %s\n", strerror(errno)); + } + if (dup2(pipefd[1], STDERR_FILENO) == -1) { + LOGE("dup2 stderr failed: %s\n", strerror(errno)); + } + + close(pipefd[1]); + } } // command line args come from, in decreasing precedence: @@ -221,8 +323,8 @@ get_args(int *argc, char ***argv) { } } - // --- if that doesn't work, try the command file - if (*argc <= 1) { + // --- if that doesn't work, try the command file (if we have /cache). + if (*argc <= 1 && has_cache) { FILE *fp = fopen_path(COMMAND_FILE, "r"); if (fp != NULL) { char *token; @@ -285,6 +387,18 @@ static void save_kernel_log(const char* destination) { android::base::WriteStringToFile(buffer, destination); } +// write content to the current pmsg session. +static ssize_t __pmsg_write(const char *filename, const char *buf, size_t len) { + return __android_log_pmsg_file_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, + filename, buf, len); +} + +static void copy_log_file_to_pmsg(const char* source, const char* destination) { + std::string content; + android::base::ReadFileToString(source, &content); + __pmsg_write(destination, content.c_str(), content.length()); +} + // How much of the temp log we have copied to the copy in cache. static long tmplog_offset = 0; @@ -326,14 +440,18 @@ static void rotate_logs(int max) { ensure_path_mounted(LAST_KMSG_FILE); for (int i = max-1; i >= 0; --i) { - std::string old_log = android::base::StringPrintf((i == 0) ? "%s" : "%s.%d", - LAST_LOG_FILE, 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((i == 0) ? "%s" : "%s.%d", - LAST_KMSG_FILE, i); + 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()); } @@ -348,6 +466,15 @@ static void copy_logs() { return; } + // Always write to pmsg, this allows the OTA logs to be caught in logcat -L + copy_log_file_to_pmsg(TEMPORARY_LOG_FILE, LAST_LOG_FILE); + copy_log_file_to_pmsg(TEMPORARY_INSTALL_FILE, LAST_INSTALL_FILE); + + // We can do nothing for now if there's no /cache partition. + if (!has_cache) { + return; + } + rotate_logs(KEEP_LOG_COUNT); // Copy logs to cache so the system can find out what happened. @@ -371,7 +498,7 @@ static void copy_logs() { static void finish_recovery(const char *send_intent) { // By this point, we're ready to return to the main system... - if (send_intent != NULL) { + if (send_intent != NULL && has_cache) { FILE *fp = fopen_path(INTENT_FILE, "w"); if (fp == NULL) { LOGE("Can't open %s\n", INTENT_FILE); @@ -385,12 +512,16 @@ finish_recovery(const char *send_intent) { // without a --locale argument (eg, directly from the bootloader) // it will use the last-known locale. if (locale != NULL) { - LOGI("Saving locale \"%s\"\n", locale); - FILE* fp = fopen_path(LOCALE_FILE, "w"); - fwrite(locale, 1, strlen(locale), fp); - fflush(fp); - fsync(fileno(fp)); - check_and_fclose(fp, LOCALE_FILE); + size_t len = strlen(locale); + __pmsg_write(LOCALE_FILE, locale, len); + if (has_cache) { + LOGI("Saving locale \"%s\"\n", locale); + FILE* fp = fopen_path(LOCALE_FILE, "w"); + fwrite(locale, 1, len, fp); + fflush(fp); + fsync(fileno(fp)); + check_and_fclose(fp, LOCALE_FILE); + } } copy_logs(); @@ -401,12 +532,13 @@ finish_recovery(const char *send_intent) { set_bootloader_message(&boot); // Remove the command file, so recovery won't repeat indefinitely. - if (ensure_path_mounted(COMMAND_FILE) != 0 || - (unlink(COMMAND_FILE) && errno != ENOENT)) { - LOGW("Can't unlink %s\n", COMMAND_FILE); + if (has_cache) { + if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) { + LOGW("Can't unlink %s\n", COMMAND_FILE); + } + ensure_path_unmounted(CACHE_ROOT); } - ensure_path_unmounted(CACHE_ROOT); sync(); // For good measure. } @@ -419,6 +551,7 @@ typedef struct _saved_log_file { static bool erase_volume(const char* volume) { bool is_cache = (strcmp(volume, CACHE_ROOT) == 0); + bool is_data = (strcmp(volume, DATA_ROOT) == 0); ui->SetBackground(RecoveryUI::ERASING); ui->SetProgressType(RecoveryUI::INDETERMINATE); @@ -473,7 +606,28 @@ static bool erase_volume(const char* volume) { ui->Print("Formatting %s...\n", volume); ensure_path_unmounted(volume); - int result = format_volume(volume); + + int result; + + if (is_data && reason && strcmp(reason, "convert_fbe") == 0) { + // Create convert_fbe breadcrumb file to signal to init + // to convert to file based encryption, not full disk encryption + if (mkdir(CONVERT_FBE_DIR, 0700) != 0) { + ui->Print("Failed to make convert_fbe dir %s\n", strerror(errno)); + return true; + } + FILE* f = fopen(CONVERT_FBE_FILE, "wb"); + if (!f) { + ui->Print("Failed to convert to file encryption %s\n", strerror(errno)); + return true; + } + fclose(f); + result = format_volume(volume, CONVERT_FBE_DIR); + remove(CONVERT_FBE_FILE); + rmdir(CONVERT_FBE_DIR); + } else { + result = format_volume(volume); + } if (is_cache) { while (head) { @@ -675,7 +829,7 @@ static bool wipe_data(int should_confirm, Device* device) { bool success = device->PreWipeData() && erase_volume("/data") && - erase_volume("/cache") && + (has_cache ? erase_volume("/cache") : true) && device->PostWipeData(); ui->Print("Data wipe %s.\n", success ? "complete" : "failed"); return success; @@ -683,6 +837,11 @@ static bool wipe_data(int should_confirm, Device* device) { // Return true on success. static bool wipe_cache(bool should_confirm, Device* device) { + if (!has_cache) { + ui->Print("No /cache partition found.\n"); + return false; + } + if (should_confirm && !yes_no(device, "Wipe cache?", " THIS CAN NOT BE UNDONE!")) { return false; } @@ -696,6 +855,11 @@ static bool wipe_cache(bool should_confirm, Device* device) { } static void choose_recovery_file(Device* device) { + if (!has_cache) { + ui->Print("No /cache partition found.\n"); + return; + } + // "Back" + KEEP_LOG_COUNT * 2 + terminating nullptr entry char* entries[1 + KEEP_LOG_COUNT * 2 + 1]; memset(entries, 0, sizeof(entries)); @@ -706,7 +870,10 @@ static void choose_recovery_file(Device* device) { // Add LAST_KMSG_FILE + LAST_KMSG_FILE.x for (int i = 0; i < KEEP_LOG_COUNT; i++) { char* log_file; - if (asprintf(&log_file, (i == 0) ? "%s" : "%s.%d", LAST_LOG_FILE, i) == -1) { + int ret; + ret = (i == 0) ? asprintf(&log_file, "%s", LAST_LOG_FILE) : + asprintf(&log_file, "%s.%d", LAST_LOG_FILE, i); + if (ret == -1) { // memory allocation failure - return early. Should never happen. return; } @@ -717,7 +884,9 @@ static void choose_recovery_file(Device* device) { } char* kmsg_file; - if (asprintf(&kmsg_file, (i == 0) ? "%s" : "%s.%d", LAST_KMSG_FILE, i) == -1) { + ret = (i == 0) ? asprintf(&kmsg_file, "%s", LAST_KMSG_FILE) : + asprintf(&kmsg_file, "%s.%d", LAST_KMSG_FILE, i); + if (ret == -1) { // memory allocation failure - return early. Should never happen. return; } @@ -736,10 +905,7 @@ static void choose_recovery_file(Device* device) { int chosen_item = get_menu_selection(headers, entries, 1, 0, device); if (strcmp(entries[chosen_item], "Back") == 0) break; - // TODO: do we need to redirect? ShowFile could just avoid writing to stdio. - redirect_stdio("/dev/null"); ui->ShowFile(entries[chosen_item]); - redirect_stdio(TEMPORARY_LOG_FILE); } for (size_t i = 0; i < (sizeof(entries) / sizeof(*entries)); i++) { @@ -747,6 +913,41 @@ static void choose_recovery_file(Device* device) { } } +static void run_graphics_test(Device* device) { + // Switch to graphics screen. + ui->ShowText(false); + + ui->SetProgressType(RecoveryUI::INDETERMINATE); + ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); + sleep(1); + + ui->SetBackground(RecoveryUI::ERROR); + sleep(1); + + ui->SetBackground(RecoveryUI::NO_COMMAND); + sleep(1); + + ui->SetBackground(RecoveryUI::ERASING); + sleep(1); + + ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); + + ui->SetProgressType(RecoveryUI::DETERMINATE); + ui->ShowProgress(1.0, 10.0); + float fraction = 0.0; + for (size_t i = 0; i < 100; ++i) { + fraction += .01; + ui->SetProgress(fraction); + usleep(100000); + } + + ui->ShowText(true); +} + +// How long (in seconds) we wait for the fuse-provided package file to +// appear, before timing out. +#define SDCARD_INSTALL_TIMEOUT 10 + static int apply_from_sdcard(Device* device, bool* wipe_cache) { modified_flash = true; @@ -758,19 +959,68 @@ static int apply_from_sdcard(Device* device, bool* wipe_cache) { char* path = browse_directory(SDCARD_ROOT, device); if (path == NULL) { ui->Print("\n-- No package file selected.\n"); + ensure_path_unmounted(SDCARD_ROOT); return INSTALL_ERROR; } ui->Print("\n-- Install %s ...\n", path); set_sdcard_update_bootloader_message(); - void* token = start_sdcard_fuse(path); - int status = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, - TEMPORARY_INSTALL_FILE, false); + // We used to use fuse in a thread as opposed to a process. Since accessing + // through fuse involves going from kernel to userspace to kernel, it leads + // to deadlock when a page fault occurs. (Bug: 26313124) + pid_t child; + if ((child = fork()) == 0) { + bool status = start_sdcard_fuse(path); + + _exit(status ? EXIT_SUCCESS : EXIT_FAILURE); + } + + // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the fuse in child + // process is ready. + int result = INSTALL_ERROR; + int status; + bool waited = false; + for (int i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) { + if (waitpid(child, &status, WNOHANG) == -1) { + result = INSTALL_ERROR; + waited = true; + break; + } + + struct stat sb; + if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &sb) == -1) { + if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT-1) { + sleep(1); + continue; + } else { + LOGE("Timed out waiting for the fuse-provided package.\n"); + result = INSTALL_ERROR; + kill(child, SIGKILL); + break; + } + } + + result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, + TEMPORARY_INSTALL_FILE, false, 0/*retry_count*/); + break; + } + + if (!waited) { + // Calling stat() on this magic filename signals the fuse + // filesystem to shut down. + struct stat sb; + stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &sb); + + waitpid(child, &status, 0); + } + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + LOGE("Error exit from the fuse process: %d\n", WEXITSTATUS(status)); + } - finish_sdcard_fuse(token); ensure_path_unmounted(SDCARD_ROOT); - return status; + return result; } // Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION @@ -852,10 +1102,29 @@ prompt_and_wait(Device* device, int status) { choose_recovery_file(device); break; + case Device::RUN_GRAPHICS_TEST: + run_graphics_test(device); + break; + case Device::MOUNT_SYSTEM: - if (ensure_path_mounted("/system") != -1) { - ui->Print("Mounted /system.\n"); + char system_root_image[PROPERTY_VALUE_MAX]; + property_get("ro.build.system_root_image", system_root_image, ""); + + // For a system image built with the root directory (i.e. + // system_root_image == "true"), we mount it to /system_root, and symlink /system + // to /system_root/system to make adb shell work (the symlink is created through + // the build system). + // Bug: 22855115 + if (strcmp(system_root_image, "true") == 0) { + if (ensure_path_mounted_at("/", "/system_root") != -1) { + ui->Print("Mounted /system.\n"); + } + } else { + if (ensure_path_mounted("/system") != -1) { + ui->Print("Mounted /system.\n"); + } } + break; } } @@ -903,11 +1172,145 @@ ui_print(const char* format, ...) { } } -int -main(int argc, char **argv) { - time_t start = time(NULL); +static bool is_battery_ok() { + struct healthd_config healthd_config = { + .batteryStatusPath = android::String8(android::String8::kEmptyString), + .batteryHealthPath = android::String8(android::String8::kEmptyString), + .batteryPresentPath = android::String8(android::String8::kEmptyString), + .batteryCapacityPath = android::String8(android::String8::kEmptyString), + .batteryVoltagePath = android::String8(android::String8::kEmptyString), + .batteryTemperaturePath = android::String8(android::String8::kEmptyString), + .batteryTechnologyPath = android::String8(android::String8::kEmptyString), + .batteryCurrentNowPath = android::String8(android::String8::kEmptyString), + .batteryCurrentAvgPath = android::String8(android::String8::kEmptyString), + .batteryChargeCounterPath = android::String8(android::String8::kEmptyString), + .batteryFullChargePath = android::String8(android::String8::kEmptyString), + .batteryCycleCountPath = android::String8(android::String8::kEmptyString), + .energyCounter = NULL, + .boot_min_cap = 0, + .screen_on = NULL + }; + healthd_board_init(&healthd_config); - redirect_stdio(TEMPORARY_LOG_FILE); + android::BatteryMonitor monitor; + monitor.init(&healthd_config); + + int wait_second = 0; + while (true) { + int charge_status = monitor.getChargeStatus(); + // Treat unknown status as charged. + bool charged = (charge_status != android::BATTERY_STATUS_DISCHARGING && + charge_status != android::BATTERY_STATUS_NOT_CHARGING); + android::BatteryProperty capacity; + android::status_t status = monitor.getProperty(android::BATTERY_PROP_CAPACITY, &capacity); + ui_print("charge_status %d, charged %d, status %d, capacity %lld\n", charge_status, + charged, status, capacity.valueInt64); + // At startup, the battery drivers in devices like N5X/N6P take some time to load + // the battery profile. Before the load finishes, it reports value 50 as a fake + // capacity. BATTERY_READ_TIMEOUT_IN_SEC is set that the battery drivers are expected + // to finish loading the battery profile earlier than 10 seconds after kernel startup. + if (status == 0 && capacity.valueInt64 == 50) { + if (wait_second < BATTERY_READ_TIMEOUT_IN_SEC) { + sleep(1); + wait_second++; + continue; + } + } + // If we can't read battery percentage, it may be a device without battery. In this + // situation, use 100 as a fake battery percentage. + if (status != 0) { + capacity.valueInt64 = 100; + } + return (charged && capacity.valueInt64 >= BATTERY_WITH_CHARGER_OK_PERCENTAGE) || + (!charged && capacity.valueInt64 >= BATTERY_OK_PERCENTAGE); + } +} + +static void set_retry_bootloader_message(int retry_count, int argc, char** argv) { + struct bootloader_message boot {}; + strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); + strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery)); + + for (int i = 1; i < argc; ++i) { + if (strstr(argv[i], "retry_count") == nullptr) { + strlcat(boot.recovery, argv[i], sizeof(boot.recovery)); + strlcat(boot.recovery, "\n", sizeof(boot.recovery)); + } + } + + // Initialize counter to 1 if it's not in BCB, otherwise increment it by 1. + if (retry_count == 0) { + strlcat(boot.recovery, "--retry_count=1\n", sizeof(boot.recovery)); + } else { + char buffer[20]; + snprintf(buffer, sizeof(buffer), "--retry_count=%d\n", retry_count+1); + strlcat(boot.recovery, buffer, sizeof(boot.recovery)); + } + set_bootloader_message(&boot); +} + +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(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(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 { + unsigned long long 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) { + // Take last pmsg contents and rewrite it to the current pmsg session. + 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); + // Take action to refresh pmsg contents + __android_log_pmsg_file_read( + LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, + logrotate, &doRotate); // If this binary is started with the single argument "--adbd", // instead of being the normal recovery binary, it turns into kind @@ -917,13 +1320,21 @@ main(int argc, char **argv) { // only way recovery should be run with this argument is when it // starts a copy of itself from the apply_from_adb() function. if (argc == 2 && strcmp(argv[1], "--adbd") == 0) { - adb_main(0, DEFAULT_ADB_PORT); + adb_server_main(0, DEFAULT_ADB_PORT, -1); return 0; } + time_t start = time(NULL); + + // redirect_stdio should be called only in non-sideload mode. Otherwise + // we may have two logger instances with different timestamps. + redirect_stdio(TEMPORARY_LOG_FILE); + printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start)); load_volume_table(); + has_cache = volume_for_path(CACHE_ROOT) != nullptr; + get_args(&argc, &argv); const char *send_intent = NULL; @@ -935,11 +1346,14 @@ main(int argc, char **argv) { bool sideload_auto_reboot = false; bool just_exit = false; bool shutdown_after = false; + int retry_count = 0; + bool security_update = false; int arg; while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) { switch (arg) { case 'i': send_intent = optarg; break; + case 'n': android::base::ParseInt(optarg, &retry_count, 0); break; case 'u': update_package = optarg; break; case 'w': should_wipe_data = true; break; case 'c': should_wipe_cache = true; break; @@ -958,13 +1372,14 @@ main(int argc, char **argv) { } case 'p': shutdown_after = true; break; case 'r': reason = optarg; break; + case 'e': security_update = true; break; case '?': LOGE("Invalid command argument\n"); continue; } } - if (locale == NULL) { + if (locale == nullptr && has_cache) { load_locale_from_cache(); } printf("locale is [%s]\n", locale); @@ -977,6 +1392,9 @@ main(int argc, char **argv) { ui->SetLocale(locale); ui->Init(); + // Set background string to "installing security update" for security update, + // otherwise set it to "installing system update". + ui->SetSystemUpdateText(security_update); int st_cur, st_max; if (stage != NULL && sscanf(stage, "%d/%d", &st_cur, &st_max) == 2) { @@ -1011,11 +1429,15 @@ main(int argc, char **argv) { if (strncmp(update_package, "CACHE:", 6) == 0) { int len = strlen(update_package) + 10; char* modified_path = (char*)malloc(len); - strlcpy(modified_path, "/cache/", len); - strlcat(modified_path, update_package+6, len); - printf("(replacing path \"%s\" with \"%s\")\n", - update_package, modified_path); - update_package = modified_path; + if (modified_path) { + strlcpy(modified_path, "/cache/", len); + strlcat(modified_path, update_package+6, len); + printf("(replacing path \"%s\" with \"%s\")\n", + update_package, modified_path); + update_package = modified_path; + } + else + printf("modified_path allocation failed\n"); } } printf("\n"); @@ -1028,18 +1450,57 @@ main(int argc, char **argv) { int status = INSTALL_SUCCESS; if (update_package != NULL) { - status = install_package(update_package, &should_wipe_cache, TEMPORARY_INSTALL_FILE, true); - if (status == INSTALL_SUCCESS && should_wipe_cache) { - wipe_cache(false, device); - } - if (status != INSTALL_SUCCESS) { - ui->Print("Installation aborted.\n"); + // It's not entirely true that we will modify the flash. But we want + // to log the update attempt since update_package is non-NULL. + modified_flash = true; - // If this is an eng or userdebug build, then automatically - // turn the text display on if the script fails so the error - // message is visible. - if (is_ro_debuggable()) { - ui->ShowText(true); + if (!is_battery_ok()) { + ui->Print("battery capacity is not enough for installing package, needed is %d%%\n", + BATTERY_OK_PERCENTAGE); + // Log the error code to last_install when installation skips due to + // low battery. + FILE* install_log = fopen_path(LAST_INSTALL_FILE, "w"); + if (install_log != nullptr) { + fprintf(install_log, "%s\n", update_package); + fprintf(install_log, "0\n"); + fprintf(install_log, "error: %d\n", kLowBattery); + fclose(install_log); + } else { + LOGE("failed to open last_install: %s\n", strerror(errno)); + } + status = INSTALL_SKIPPED; + } else { + status = install_package(update_package, &should_wipe_cache, + TEMPORARY_INSTALL_FILE, true, retry_count); + if (status == INSTALL_SUCCESS && should_wipe_cache) { + wipe_cache(false, device); + } + if (status != INSTALL_SUCCESS) { + ui->Print("Installation aborted.\n"); + // When I/O error happens, reboot and retry installation EIO_RETRY_COUNT + // times before we abandon this OTA update. + if (status == INSTALL_RETRY && retry_count < EIO_RETRY_COUNT) { + copy_logs(); + set_retry_bootloader_message(retry_count, argc, argv); + // Print retry count on screen. + ui->Print("Retry attempt %d\n", retry_count); + + // Reboot and retry the update + int ret = property_set(ANDROID_RB_PROPERTY, "reboot,recovery"); + if (ret < 0) { + ui->Print("Reboot failed\n"); + } else { + while (true) { + pause(); + } + } + } + // If this is an eng or userdebug build, then automatically + // turn the text display on if the script fails so the error + // message is visible. + if (is_ro_debuggable()) { + ui->ShowText(true); + } } } } else if (should_wipe_data) { @@ -1088,7 +1549,8 @@ main(int argc, char **argv) { } Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT; - if ((status != INSTALL_SUCCESS && !sideload_auto_reboot) || ui->IsTextVisible()) { + if ((status != INSTALL_SUCCESS && status != INSTALL_SKIPPED && !sideload_auto_reboot) || + ui->IsTextVisible()) { Device::BuiltinAction temp = prompt_and_wait(device, status); if (temp != Device::NO_ACTION) { after = temp; @@ -1114,6 +1576,9 @@ main(int argc, char **argv) { property_set(ANDROID_RB_PROPERTY, "reboot,"); break; } - sleep(5); // should reboot before this finishes + while (true) { + pause(); + } + // Should be unreachable. return EXIT_SUCCESS; } diff --git a/res-560dpi b/res-560dpi new file mode 120000 index 00000000..1db3a2e2 --- /dev/null +++ b/res-560dpi @@ -0,0 +1 @@ +res-xxxhdpi \ No newline at end of file diff --git a/roots.cpp b/roots.cpp index 167499e2..f361cb8c 100644 --- a/roots.cpp +++ b/roots.cpp @@ -24,18 +24,14 @@ #include #include -extern "C" { #include #include "mtdutils/mtdutils.h" #include "mtdutils/mounts.h" -} #include "roots.h" #include "common.h" #include "make_ext4fs.h" -extern "C" { #include "wipe.h" #include "cryptfs.h" -} static struct fstab *fstab = NULL; @@ -74,11 +70,8 @@ Volume* volume_for_path(const char* path) { return fs_mgr_get_entry_for_mount_point(fstab, path); } -int ensure_path_mounted(const char* path) { - if (PartitionManager.Mount_By_Path(path, true)) - return 0; - else - return -1; +// Mount the volume specified by path at the given mount_point. +int ensure_path_mounted_at(const char* path, const char* mount_point) { Volume* v = volume_for_path(path); if (v == NULL) { LOGE("unknown volume for path [%s]\n", path); @@ -96,14 +89,18 @@ int ensure_path_mounted(const char* path) { return -1; } + if (!mount_point) { + mount_point = v->mount_point; + } + const MountedVolume* mv = - find_mounted_volume_by_mount_point(v->mount_point); + find_mounted_volume_by_mount_point(mount_point); if (mv) { // volume is already mounted return 0; } - mkdir(v->mount_point, 0755); // in case it doesn't already exist + mkdir(mount_point, 0755); // in case it doesn't already exist if (strcmp(v->fs_type, "yaffs2") == 0) { // mount an MTD partition as a YAFFS2 filesystem. @@ -112,30 +109,31 @@ int ensure_path_mounted(const char* path) { partition = mtd_find_partition_by_name(v->blk_device); if (partition == NULL) { LOGE("failed to find \"%s\" partition to mount at \"%s\"\n", - v->blk_device, v->mount_point); + v->blk_device, mount_point); return -1; } - return mtd_mount_partition(partition, v->mount_point, v->fs_type, 0); + return mtd_mount_partition(partition, mount_point, v->fs_type, 0); } else if (strcmp(v->fs_type, "ext4") == 0 || strcmp(v->fs_type, "squashfs") == 0 || strcmp(v->fs_type, "vfat") == 0) { - result = mount(v->blk_device, v->mount_point, v->fs_type, + result = mount(v->blk_device, mount_point, v->fs_type, v->flags, v->fs_options); if (result == 0) return 0; - LOGE("failed to mount %s (%s)\n", v->mount_point, strerror(errno)); + LOGE("failed to mount %s (%s)\n", mount_point, strerror(errno)); return -1; } - LOGE("unknown fs_type \"%s\" for %s\n", v->fs_type, v->mount_point); + LOGE("unknown fs_type \"%s\" for %s\n", v->fs_type, mount_point); return -1; } +int ensure_path_mounted(const char* path) { + // Mount at the default mount point. + return ensure_path_mounted_at(path, nullptr); +} + int ensure_path_unmounted(const char* path) { - if (PartitionManager.UnMount_By_Path(path, true)) - return 0; - else - return -1; Volume* v = volume_for_path(path); if (v == NULL) { LOGE("unknown volume for path [%s]\n", path); @@ -177,11 +175,7 @@ static int exec_cmd(const char* path, char* const argv[]) { return WEXITSTATUS(status); } -int format_volume(const char* volume) { - if (PartitionManager.Wipe_By_Path(volume)) - return 0; - else - return -1; +int format_volume(const char* volume, const char* directory) { Volume* v = volume_for_path(volume); if (v == NULL) { LOGE("unknown volume \"%s\"\n", volume); @@ -247,7 +241,7 @@ int format_volume(const char* volume) { } int result; if (strcmp(v->fs_type, "ext4") == 0) { - result = make_ext4fs(v->blk_device, length, volume, sehandle); + result = make_ext4fs_directory(v->blk_device, length, volume, sehandle, directory); } else { /* Has to be f2fs because we checked earlier. */ if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) { LOGE("format_volume: crypt footer + negative length (%zd) not supported on %s\n", length, v->fs_type); @@ -279,6 +273,10 @@ int format_volume(const char* volume) { return -1; } +int format_volume(const char* volume) { + return format_volume(volume, NULL); +} + int setup_install_mounts() { if (fstab == NULL) { LOGE("can't set up install mounts: no fstab loaded\n"); diff --git a/roots.h b/roots.h index 4e6d6de7..a5561c62 100644 --- a/roots.h +++ b/roots.h @@ -19,10 +19,6 @@ #include "common.h" -#ifdef __cplusplus -extern "C" { -#endif - typedef struct fstab_rec Volume; // Load and parse volume data from /etc/recovery.fstab. @@ -35,7 +31,10 @@ Volume* volume_for_path(const char* path); // success (volume is mounted). int ensure_path_mounted(const char* path); -// Make sure that the volume 'path' is on is mounted. Returns 0 on +// Similar to ensure_path_mounted, but allows one to specify the mount_point. +int ensure_path_mounted_at(const char* path, const char* mount_point); + +// Make sure that the volume 'path' is on is unmounted. Returns 0 on // success (volume is unmounted); int ensure_path_unmounted(const char* path); @@ -44,12 +43,14 @@ int ensure_path_unmounted(const char* path); // it is mounted. int format_volume(const char* volume); +// Reformat the given volume (must be the mount point only, eg +// "/cache"), no paths permitted. Attempts to unmount the volume if +// it is mounted. +// Copies 'directory' to root of the newly formatted volume +int format_volume(const char* volume, const char* directory); + // Ensure that all and only the volumes that packages expect to find // mounted (/tmp and /cache) are mounted. Returns 0 on success. int setup_install_mounts(); -#ifdef __cplusplus -} -#endif - #endif // RECOVERY_ROOTS_H_ diff --git a/screen_ui.cpp b/screen_ui.cpp index e699538c..85f789f3 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include #include @@ -30,22 +31,17 @@ #include -#include "base/strings.h" -#include "cutils/properties.h" +#include +#include +#include + #include "common.h" #include "device.h" #include "minui/minui.h" #include "screen_ui.h" #include "ui.h" -extern "C" { -#include "minuitwrp/minui.h" -int twgr_text(int x, int y, const char *s); -#include "gui/gui.h" -} -#include "data.hpp" -static int char_width; -static int char_height; +#define TEXT_INDENT 4 // Return the current time as a double (including fractions of a second). static double now() { @@ -56,9 +52,9 @@ static double now() { ScreenRecoveryUI::ScreenRecoveryUI() : currentIcon(NONE), - installingFrame(0), locale(nullptr), - rtl_locale(false), + intro_done(false), + current_frame(0), progressBarType(EMPTY), progressScopeStart(0), progressScopeSize(0), @@ -77,83 +73,114 @@ ScreenRecoveryUI::ScreenRecoveryUI() : menu_items(0), menu_sel(0), file_viewer_text_(nullptr), - animation_fps(20), - installing_frames(-1), + intro_frames(0), + loop_frames(0), + animation_fps(30), // TODO: there's currently no way to infer this. stage(-1), - max_stage(-1) { + max_stage(-1), + updateMutex(PTHREAD_MUTEX_INITIALIZER), + rtl_locale(false) { +} - for (int i = 0; i < 5; i++) { - backgroundIcon[i] = nullptr; +GRSurface* ScreenRecoveryUI::GetCurrentFrame() { + if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) { + return intro_done ? loopFrames[current_frame] : introFrames[current_frame]; } - pthread_mutex_init(&updateMutex, nullptr); + return error_icon; +} + +GRSurface* ScreenRecoveryUI::GetCurrentText() { + switch (currentIcon) { + case ERASING: return erasing_text; + case ERROR: return error_text; + case INSTALLING_UPDATE: return installing_text; + case NO_COMMAND: return no_command_text; + case NONE: abort(); + } +} + +int ScreenRecoveryUI::PixelsFromDp(int dp) { + return dp * density_; +} + +// Here's the intended layout: + +// | regular large +// ---------+-------------------- +// | 220dp 366dp +// icon | (200dp) (200dp) +// | 68dp 68dp +// text | (14sp) (14sp) +// | 32dp 32dp +// progress | (2dp) (2dp) +// | 194dp 340dp + +// Note that "baseline" is actually the *top* of each icon (because that's how our drawing +// routines work), so that's the more useful measurement for calling code. + +int ScreenRecoveryUI::GetAnimationBaseline() { + return GetTextBaseline() - PixelsFromDp(68) - gr_get_height(loopFrames[0]); +} + +int ScreenRecoveryUI::GetTextBaseline() { + return GetProgressBaseline() - PixelsFromDp(32) - gr_get_height(installing_text); +} + +int ScreenRecoveryUI::GetProgressBaseline() { + return gr_fb_height() - PixelsFromDp(is_large_ ? 340 : 194) - gr_get_height(progressBarFill); } // Clear the screen and draw the currently selected background icon (if any). // Should only be called with updateMutex locked. -void ScreenRecoveryUI::draw_background_locked(Icon icon) { +void ScreenRecoveryUI::draw_background_locked() { pagesIdentical = false; gr_color(0, 0, 0, 255); gr_clear(); - if (icon) { - GRSurface* surface = backgroundIcon[icon]; - if (icon == INSTALLING_UPDATE || icon == ERASING) { - surface = installation[installingFrame]; - } - GRSurface* text_surface = backgroundText[icon]; - - int iconWidth = gr_get_width(surface); - int iconHeight = gr_get_height(surface); - int textWidth = gr_get_width(text_surface); - int textHeight = gr_get_height(text_surface); - int stageHeight = gr_get_height(stageMarkerEmpty); - - int sh = (max_stage >= 0) ? stageHeight : 0; - - iconX = (gr_fb_width() - iconWidth) / 2; - iconY = (gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2; - - int textX = (gr_fb_width() - textWidth) / 2; - int textY = ((gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2) + iconHeight + 40; - - gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY); - if (stageHeight > 0) { - int sw = gr_get_width(stageMarkerEmpty); + if (currentIcon != NONE) { + if (max_stage != -1) { + int stage_height = gr_get_height(stageMarkerEmpty); + int stage_width = gr_get_width(stageMarkerEmpty); int x = (gr_fb_width() - max_stage * gr_get_width(stageMarkerEmpty)) / 2; - int y = iconY + iconHeight + 20; + int y = gr_fb_height() - stage_height; for (int i = 0; i < max_stage; ++i) { - gr_blit((i < stage) ? stageMarkerFill : stageMarkerEmpty, - 0, 0, sw, stageHeight, x, y); - x += sw; + GRSurface* stage_surface = (i < stage) ? stageMarkerFill : stageMarkerEmpty; + gr_blit(stage_surface, 0, 0, stage_width, stage_height, x, y); + x += stage_width; } } + GRSurface* text_surface = GetCurrentText(); + int text_x = (gr_fb_width() - gr_get_width(text_surface)) / 2; + int text_y = GetTextBaseline(); gr_color(255, 255, 255, 255); - gr_texticon(textX, textY, text_surface); + gr_texticon(text_x, text_y, text_surface); } } -// Draw the progress bar (if any) on the screen. Does not flip pages. +// Draws the animation and progress bar (if any) on the screen. +// Does not flip pages. // Should only be called with updateMutex locked. -void ScreenRecoveryUI::draw_progress_locked() { - if (currentIcon == ERROR) return; - - if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) { - GRSurface* icon = installation[installingFrame]; - gr_blit(icon, 0, 0, gr_get_width(icon), gr_get_height(icon), iconX, iconY); +void ScreenRecoveryUI::draw_foreground_locked() { + if (currentIcon != NONE) { + GRSurface* frame = GetCurrentFrame(); + int frame_width = gr_get_width(frame); + int frame_height = gr_get_height(frame); + int frame_x = (gr_fb_width() - frame_width) / 2; + int frame_y = GetAnimationBaseline(); + gr_blit(frame, 0, 0, frame_width, frame_height, frame_x, frame_y); } if (progressBarType != EMPTY) { - int iconHeight = gr_get_height(backgroundIcon[INSTALLING_UPDATE]); int width = gr_get_width(progressBarEmpty); int height = gr_get_height(progressBarEmpty); - int dx = (gr_fb_width() - width)/2; - int dy = (3*gr_fb_height() + iconHeight - 2*height)/4; + int progress_x = (gr_fb_width() - width)/2; + int progress_y = GetProgressBaseline(); // Erase behind the progress bar (in case this was a progress-only update) gr_color(0, 0, 0, 255); - gr_fill(dx, dy, width, height); + gr_fill(progress_x, progress_y, width, height); if (progressBarType == DETERMINATE) { float p = progressScopeStart + progress * progressScopeSize; @@ -162,18 +189,20 @@ void ScreenRecoveryUI::draw_progress_locked() { if (rtl_locale) { // Fill the progress bar from right to left. if (pos > 0) { - gr_blit(progressBarFill, width-pos, 0, pos, height, dx+width-pos, dy); + gr_blit(progressBarFill, width-pos, 0, pos, height, + progress_x+width-pos, progress_y); } if (pos < width-1) { - gr_blit(progressBarEmpty, 0, 0, width-pos, height, dx, dy); + gr_blit(progressBarEmpty, 0, 0, width-pos, height, progress_x, progress_y); } } else { // Fill the progress bar from left to right. if (pos > 0) { - gr_blit(progressBarFill, 0, 0, pos, height, dx, dy); + gr_blit(progressBarFill, 0, 0, pos, height, progress_x, progress_y); } if (pos < width-1) { - gr_blit(progressBarEmpty, pos, 0, width-pos, height, dx+pos, dy); + gr_blit(progressBarEmpty, pos, 0, width-pos, height, + progress_x+pos, progress_y); } } } @@ -217,14 +246,14 @@ void ScreenRecoveryUI::DrawHorizontalRule(int* y) { *y += 4; } -void ScreenRecoveryUI::DrawTextLine(int* y, const char* line, bool bold) { - gr_text(4, *y, line, bold); - *y += char_height + 4; +void ScreenRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) { + gr_text(x, *y, line, bold); + *y += char_height_ + 4; } -void ScreenRecoveryUI::DrawTextLines(int* y, const char* const* lines) { +void ScreenRecoveryUI::DrawTextLines(int x, int* y, const char* const* lines) { for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) { - DrawTextLine(y, lines[i], false); + DrawTextLine(x, y, lines[i], false); } } @@ -243,8 +272,8 @@ static const char* LONG_PRESS_HELP[] = { // Should only be called with updateMutex locked. void ScreenRecoveryUI::draw_screen_locked() { if (!show_text) { - draw_background_locked(currentIcon); - draw_progress_locked(); + draw_background_locked(); + draw_foreground_locked(); } else { gr_color(0, 0, 0, 255); gr_clear(); @@ -255,14 +284,14 @@ void ScreenRecoveryUI::draw_screen_locked() { property_get("ro.bootimage.build.fingerprint", recovery_fingerprint, ""); SetColor(INFO); - DrawTextLine(&y, "Android Recovery", true); + DrawTextLine(TEXT_INDENT, &y, "Android Recovery", true); for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) { - DrawTextLine(&y, chunk.c_str(), false); + DrawTextLine(TEXT_INDENT, &y, chunk.c_str(), false); } - DrawTextLines(&y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); + DrawTextLines(TEXT_INDENT, &y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); SetColor(HEADER); - DrawTextLines(&y, menu_headers_); + DrawTextLines(TEXT_INDENT, &y, menu_headers_); SetColor(MENU); DrawHorizontalRule(&y); @@ -271,7 +300,7 @@ void ScreenRecoveryUI::draw_screen_locked() { if (i == menu_sel) { // Draw the highlight bar. SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG); - gr_fill(0, y - 2, gr_fb_width(), y + char_height + 2); + gr_fill(0, y - 2, gr_fb_width(), y + char_height_ + 2); // Bold white text for the selected item. SetColor(MENU_SEL_FG); gr_text(4, y, menu_[i], true); @@ -279,7 +308,7 @@ void ScreenRecoveryUI::draw_screen_locked() { } else { gr_text(4, y, menu_[i], false); } - y += char_height + 4; + y += char_height_ + 4; } DrawHorizontalRule(&y); } @@ -290,9 +319,9 @@ void ScreenRecoveryUI::draw_screen_locked() { SetColor(LOG); int row = (text_top_ + text_rows_ - 1) % text_rows_; size_t count = 0; - for (int ty = gr_fb_height() - char_height; + for (int ty = gr_fb_height() - char_height_; ty >= y && count < text_rows_; - ty -= char_height, ++count) { + ty -= char_height_, ++count) { gr_text(0, ty, text_[row], false); --row; if (row < 0) row = text_rows_ - 1; @@ -309,13 +338,12 @@ void ScreenRecoveryUI::update_screen_locked() { // Updates only the progress bar, if possible, otherwise redraws the screen. // Should only be called with updateMutex locked. - void ScreenRecoveryUI::update_progress_locked() { if (show_text || !pagesIdentical) { draw_screen_locked(); // Must redraw the whole screen pagesIdentical = true; } else { - draw_progress_locked(); // Draw only the progress bar and overlays + draw_foreground_locked(); // Draw only the progress bar and overlays } gr_flip(); } @@ -332,14 +360,23 @@ void ScreenRecoveryUI::ProgressThreadLoop() { double start = now(); pthread_mutex_lock(&updateMutex); - int redraw = 0; + bool redraw = false; // update the installation animation, if active // skip this if we have a text overlay (too expensive to update) - if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) && - installing_frames > 0 && !show_text) { - installingFrame = (installingFrame + 1) % installing_frames; - redraw = 1; + if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) && !show_text) { + if (!intro_done) { + if (current_frame == intro_frames - 1) { + intro_done = true; + current_frame = 0; + } else { + ++current_frame; + } + } else { + current_frame = (current_frame + 1) % loop_frames; + } + + redraw = true; } // move the progress bar forward on timed intervals, if configured @@ -350,7 +387,7 @@ void ScreenRecoveryUI::ProgressThreadLoop() { if (p > 1.0) p = 1.0; if (p > progress) { progress = p; - redraw = 1; + redraw = true; } } @@ -368,21 +405,14 @@ void ScreenRecoveryUI::ProgressThreadLoop() { void ScreenRecoveryUI::LoadBitmap(const char* filename, GRSurface** surface) { int result = res_create_display_surface(filename, surface); if (result < 0) { - LOGE("missing bitmap %s\n(Code %d)\n", filename, result); - } -} - -void ScreenRecoveryUI::LoadBitmapArray(const char* filename, int* frames, GRSurface*** surface) { - int result = res_create_multi_display_surface(filename, frames, surface); - if (result < 0) { - LOGE("missing bitmap %s\n(Code %d)\n", filename, result); + LOGE("couldn't load bitmap %s (error %d)\n", filename, result); } } void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, GRSurface** surface) { int result = res_create_localized_alpha_surface(filename, locale, surface); if (result < 0) { - LOGE("missing bitmap %s\n(Code %d)\n", filename, result); + LOGE("couldn't load bitmap %s (error %d)\n", filename, result); } } @@ -395,12 +425,25 @@ static char** Alloc2d(size_t rows, size_t cols) { return result; } +// Choose the right background string to display during update. +void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) { + if (security_update) { + LoadLocalizedBitmap("installing_security_text", &installing_text); + } else { + LoadLocalizedBitmap("installing_text", &installing_text); + } + Redraw(); +} + void ScreenRecoveryUI::Init() { gr_init(); - gr_font_size(&char_width, &char_height); - text_rows_ = gr_fb_height() / char_height; - text_cols_ = gr_fb_width() / char_width; + density_ = static_cast(property_get_int32("ro.sf.lcd_density", 160)) / 160.f; + is_large_ = gr_fb_height() > PixelsFromDp(800); + + gr_font_size(&char_width_, &char_height_); + text_rows_ = gr_fb_height() / char_height_; + text_cols_ = gr_fb_width() / char_width_; text_ = Alloc2d(text_rows_, text_cols_ + 1); file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1); @@ -409,31 +452,64 @@ void ScreenRecoveryUI::Init() { text_col_ = text_row_ = 0; text_top_ = 1; - backgroundIcon[NONE] = nullptr; - LoadBitmapArray("icon_installing", &installing_frames, &installation); - backgroundIcon[INSTALLING_UPDATE] = installing_frames ? installation[0] : nullptr; - backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE]; - LoadBitmap("icon_error", &backgroundIcon[ERROR]); - backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR]; + LoadBitmap("icon_error", &error_icon); LoadBitmap("progress_empty", &progressBarEmpty); LoadBitmap("progress_fill", &progressBarFill); + LoadBitmap("stage_empty", &stageMarkerEmpty); LoadBitmap("stage_fill", &stageMarkerFill); - LoadLocalizedBitmap("installing_text", &backgroundText[INSTALLING_UPDATE]); - LoadLocalizedBitmap("erasing_text", &backgroundText[ERASING]); - LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]); - LoadLocalizedBitmap("error_text", &backgroundText[ERROR]); + // Background text for "installing_update" could be "installing update" + // or "installing security update". It will be set after UI init according + // to commands in BCB. + installing_text = nullptr; + LoadLocalizedBitmap("erasing_text", &erasing_text); + LoadLocalizedBitmap("no_command_text", &no_command_text); + LoadLocalizedBitmap("error_text", &error_text); + + LoadAnimation(); pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this); RecoveryUI::Init(); } +void ScreenRecoveryUI::LoadAnimation() { + // How many frames of intro and loop do we have? + std::unique_ptr dir(opendir("/res/images"), closedir); + dirent* de; + while ((de = readdir(dir.get())) != nullptr) { + int value; + if (sscanf(de->d_name, "intro%d", &value) == 1 && intro_frames < (value + 1)) { + intro_frames = value + 1; + } else if (sscanf(de->d_name, "loop%d", &value) == 1 && loop_frames < (value + 1)) { + loop_frames = value + 1; + } + } + + // It's okay to not have an intro. + if (intro_frames == 0) intro_done = true; + // But you must have an animation. + if (loop_frames == 0) abort(); + + introFrames = new GRSurface*[intro_frames]; + for (int i = 0; i < intro_frames; ++i) { + // TODO: remember the names above, so we don't have to hard-code the number of 0s. + LoadBitmap(android::base::StringPrintf("intro%05d", i).c_str(), &introFrames[i]); + } + + loopFrames = new GRSurface*[loop_frames]; + for (int i = 0; i < loop_frames; ++i) { + LoadBitmap(android::base::StringPrintf("loop%05d", i).c_str(), &loopFrames[i]); + } +} + void ScreenRecoveryUI::SetLocale(const char* new_locale) { - if (new_locale) { - this->locale = new_locale; + this->locale = new_locale; + this->rtl_locale = false; + + if (locale) { char* lang = strdup(locale); for (char* p = lang; *p; ++p) { if (*p == '_') { @@ -442,8 +518,7 @@ void ScreenRecoveryUI::SetLocale(const char* new_locale) { } } - // A bit cheesy: keep an explicit list of supported languages - // that are RTL. + // A bit cheesy: keep an explicit list of supported RTL languages. if (strcmp(lang, "ar") == 0 || // Arabic strcmp(lang, "fa") == 0 || // Persian (Farsi) strcmp(lang, "he") == 0 || // Hebrew (new language code) @@ -452,8 +527,6 @@ void ScreenRecoveryUI::SetLocale(const char* new_locale) { rtl_locale = true; } free(lang); - } else { - new_locale = nullptr; } } @@ -513,21 +586,17 @@ void ScreenRecoveryUI::SetStage(int current, int max) { pthread_mutex_unlock(&updateMutex); } -void ScreenRecoveryUI::Print(const char *fmt, ...) { - char buf[256]; - va_list ap; - va_start(ap, fmt); - vsnprintf(buf, 256, fmt, ap); - va_end(ap); +void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) { + std::string str; + android::base::StringAppendV(&str, fmt, ap); - gui_print("%s", buf); - return; - - fputs(buf, stdout); + if (copy_to_stdout) { + fputs(str.c_str(), stdout); + } pthread_mutex_lock(&updateMutex); if (text_rows_ > 0 && text_cols_ > 0) { - for (const char* ptr = buf; *ptr != '\0'; ++ptr) { + for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) { if (*ptr == '\n' || text_col_ >= text_cols_) { text_[text_row_][text_col_] = '\0'; text_col_ = 0; @@ -542,6 +611,20 @@ void ScreenRecoveryUI::Print(const char *fmt, ...) { pthread_mutex_unlock(&updateMutex); } +void ScreenRecoveryUI::Print(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + PrintV(fmt, true, ap); + va_end(ap); +} + +void ScreenRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + PrintV(fmt, false, ap); + va_end(ap); +} + void ScreenRecoveryUI::PutChar(char ch) { pthread_mutex_lock(&updateMutex); if (ch != '\n') text_[text_row_][text_col_++] = ch; @@ -576,7 +659,7 @@ void ScreenRecoveryUI::ShowFile(FILE* fp) { bool show_prompt = false; while (true) { if (show_prompt) { - Print("--(%d%% of %d bytes)--", + PrintOnScreenOnly("--(%d%% of %d bytes)--", static_cast(100 * (double(ftell(fp)) / double(sb.st_size))), static_cast(sb.st_size)); Redraw(); diff --git a/screen_ui.h b/screen_ui.h index ea05bf15..4319b76c 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -34,6 +34,7 @@ class ScreenRecoveryUI : public RecoveryUI { // overall recovery state ("background image") void SetBackground(Icon icon); + void SetSystemUpdateText(bool security_update); // progress indicator void SetProgressType(ProgressType type); @@ -49,6 +50,7 @@ class ScreenRecoveryUI : public RecoveryUI { // printing messages void Print(const char* fmt, ...) __printflike(2, 3); + void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3); void ShowFile(const char* filename); // menu display @@ -66,16 +68,28 @@ class ScreenRecoveryUI : public RecoveryUI { }; void SetColor(UIElement e); - private: + protected: Icon currentIcon; - int installingFrame; - const char* locale; - bool rtl_locale; - pthread_mutex_t updateMutex; - GRSurface* backgroundIcon[5]; - GRSurface* backgroundText[5]; - GRSurface** installation; + const char* locale; + bool intro_done; + int current_frame; + + // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. + float density_; + // True if we should use the large layout. + bool is_large_; + + GRSurface* error_icon; + + GRSurface* erasing_text; + GRSurface* error_text; + GRSurface* installing_text; + GRSurface* no_command_text; + + GRSurface** introFrames; + GRSurface** loopFrames; + GRSurface* progressBarEmpty; GRSurface* progressBarFill; GRSurface* stageMarkerEmpty; @@ -108,33 +122,49 @@ class ScreenRecoveryUI : public RecoveryUI { pthread_t progress_thread_; - int animation_fps; - int installing_frames; + // Number of intro frames and loop frames in the animation. + int intro_frames; + int loop_frames; - int iconX, iconY; + // Number of frames per sec (default: 30) for both parts of the animation. + int animation_fps; int stage, max_stage; - void draw_background_locked(Icon icon); - void draw_progress_locked(); + int char_width_; + int char_height_; + pthread_mutex_t updateMutex; + bool rtl_locale; + + void draw_background_locked(); + void draw_foreground_locked(); void draw_screen_locked(); void update_screen_locked(); void update_progress_locked(); + GRSurface* GetCurrentFrame(); + GRSurface* GetCurrentText(); + static void* ProgressThreadStartRoutine(void* data); void ProgressThreadLoop(); void ShowFile(FILE*); + void PrintV(const char*, bool, va_list); void PutChar(char); void ClearText(); - void DrawHorizontalRule(int* y); - void DrawTextLine(int* y, const char* line, bool bold); - void DrawTextLines(int* y, const char* const* lines); - + void LoadAnimation(); void LoadBitmap(const char* filename, GRSurface** surface); - void LoadBitmapArray(const char* filename, int* frames, GRSurface*** surface); void LoadLocalizedBitmap(const char* filename, GRSurface** surface); + + int PixelsFromDp(int dp); + int GetAnimationBaseline(); + int GetProgressBaseline(); + int GetTextBaseline(); + + void DrawHorizontalRule(int* y); + void DrawTextLine(int x, int* y, const char* line, bool bold); + void DrawTextLines(int x, int* y, const char* const* lines); }; #endif // RECOVERY_UI_H diff --git a/tests/Android.mk b/tests/Android.mk index 02a272a2..a66991b2 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -16,10 +16,54 @@ LOCAL_PATH := $(call my-dir) +# Unit tests include $(CLEAR_VARS) +LOCAL_CLANG := true +LOCAL_MODULE := recovery_unit_test LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk -LOCAL_STATIC_LIBRARIES := libverifier -LOCAL_SRC_FILES := asn1_decoder_test.cpp -LOCAL_MODULE := asn1_decoder_test -LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. +LOCAL_STATIC_LIBRARIES := \ + libverifier \ + libminui + +LOCAL_SRC_FILES := unit/asn1_decoder_test.cpp +LOCAL_SRC_FILES += unit/recovery_test.cpp +LOCAL_SRC_FILES += unit/locale_test.cpp +LOCAL_C_INCLUDES := bootable/recovery +LOCAL_SHARED_LIBRARIES := liblog +include $(BUILD_NATIVE_TEST) + +# Component tests +include $(CLEAR_VARS) +LOCAL_CLANG := true +LOCAL_CFLAGS += -Wno-unused-parameter +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_MODULE := recovery_component_test +LOCAL_C_INCLUDES := bootable/recovery +LOCAL_SRC_FILES := \ + component/verifier_test.cpp \ + component/applypatch_test.cpp +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_STATIC_LIBRARIES := \ + libapplypatch \ + libotafault \ + libmtdutils \ + libbase \ + libverifier \ + libcrypto_static \ + libminui \ + libminzip \ + libcutils \ + libbz \ + libz \ + libc + +testdata_out_path := $(TARGET_OUT_DATA_NATIVE_TESTS)/recovery +testdata_files := $(call find-subdir-files, testdata/*) + +GEN := $(addprefix $(testdata_out_path)/, $(testdata_files)) +$(GEN): PRIVATE_PATH := $(LOCAL_PATH) +$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@ +$(GEN): $(testdata_out_path)/% : $(LOCAL_PATH)/% + $(transform-generated-source) +LOCAL_GENERATED_SOURCES += $(GEN) include $(BUILD_NATIVE_TEST) diff --git a/tests/common/test_constants.h b/tests/common/test_constants.h new file mode 100644 index 00000000..3490f680 --- /dev/null +++ b/tests/common/test_constants.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agree to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#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 + +#endif diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp new file mode 100644 index 00000000..b44ddd17 --- /dev/null +++ b/tests/component/applypatch_test.cpp @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agree to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "applypatch/applypatch.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 std::string sha1sum(const std::string& fname) { + uint8_t digest[SHA_DIGEST_LENGTH]; + std::string data; + android::base::ReadFileToString(fname, &data); + + SHA1((const uint8_t*)data.c_str(), data.size(), digest); + return print_sha1(digest); +} + +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); +} + +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 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]); +} + +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; +}; + +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 backup_old() { + cp(ApplyPatchTest::old_file, ApplyPatchTest::cache_file); +} + +static void restore_old() { + cp(ApplyPatchTest::cache_file, ApplyPatchTest::old_file); +} + +class ApplyPatchCacheTest : public ApplyPatchTest { + public: + virtual void SetUp() { + backup_old(); + } + + 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(); + patch1->type = VAL_BLOB; + patch1->size = fc.data.size(); + patch1->data = static_cast(malloc(fc.data.size())); + memcpy(patch1->data, fc.data.data(), fc.data.size()); + patches.push_back(patch1); + + ASSERT_EQ(0, LoadFileContents(&patch_file[0], &fc)); + Value* patch2 = new Value(); + patch2->type = VAL_BLOB; + patch2->size = fc.st.st_size; + patch2->data = static_cast(malloc(fc.data.size())); + memcpy(patch2->data, fc.data.data(), fc.data.size()); + patches.push_back(patch2); + } + static void TearDownTestCase() { + delete output_f; + for (auto it = patches.begin(); it != patches.end(); ++it) { + free((*it)->data); + delete *it; + } + patches.clear(); + } + + static std::vector 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(); + } +}; + +std::string ApplyPatchTest::rand_file; +std::string ApplyPatchTest::patch_file; +std::string ApplyPatchTest::cache_file; +std::string ApplyPatchTest::old_sha1; +std::string ApplyPatchTest::new_sha1; +std::string ApplyPatchTest::bad_sha1_a; +std::string ApplyPatchTest::bad_sha1_b; + +size_t ApplyPatchTest::new_size; + +std::vector ApplyPatchFullTest::patches; +TemporaryFile* ApplyPatchFullTest::output_f; +std::string ApplyPatchFullTest::output_loc; + +TEST_F(ApplyPatchTest, CheckModeSingle) { + char* s = &old_sha1[0]; + ASSERT_EQ(0, applypatch_check(&old_file[0], 1, &s)); +} + +TEST_F(ApplyPatchTest, CheckModeMultiple) { + char* argv[3] = { + &bad_sha1_a[0], + &old_sha1[0], + &bad_sha1_b[0] + }; + ASSERT_EQ(0, applypatch_check(&old_file[0], 3, argv)); +} + +TEST_F(ApplyPatchTest, CheckModeFailure) { + char* argv[2] = { + &bad_sha1_a[0], + &bad_sha1_b[0] + }; + ASSERT_NE(0, applypatch_check(&old_file[0], 2, argv)); +} + +TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSingle) { + mangle_file(old_file); + char* s = &old_sha1[0]; + ASSERT_EQ(0, applypatch_check(&old_file[0], 1, &s)); +} + +TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedMultiple) { + mangle_file(old_file); + char* argv[3] = { + &bad_sha1_a[0], + &old_sha1[0], + &bad_sha1_b[0] + }; + ASSERT_EQ(0, applypatch_check(&old_file[0], 3, argv)); +} + +TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedFailure) { + mangle_file(old_file); + char* argv[2] = { + &bad_sha1_a[0], + &bad_sha1_b[0] + }; + ASSERT_NE(0, applypatch_check(&old_file[0], 2, argv)); +} + +TEST_F(ApplyPatchCacheTest, CheckCacheMissingSingle) { + unlink(&old_file[0]); + char* s = &old_sha1[0]; + ASSERT_EQ(0, applypatch_check(&old_file[0], 1, &s)); +} + +TEST_F(ApplyPatchCacheTest, CheckCacheMissingMultiple) { + unlink(&old_file[0]); + char* argv[3] = { + &bad_sha1_a[0], + &old_sha1[0], + &bad_sha1_b[0] + }; + ASSERT_EQ(0, applypatch_check(&old_file[0], 3, argv)); +} + +TEST_F(ApplyPatchCacheTest, CheckCacheMissingFailure) { + unlink(&old_file[0]); + char* argv[2] = { + &bad_sha1_a[0], + &bad_sha1_b[0] + }; + ASSERT_NE(0, applypatch_check(&old_file[0], 2, argv)); +} + +TEST_F(ApplyPatchFullTest, ApplyInPlace) { + std::vector sha1s; + sha1s.push_back(&bad_sha1_a[0]); + sha1s.push_back(&old_sha1[0]); + + int ap_result = applypatch(&old_file[0], + "-", + &new_sha1[0], + new_size, + 2, + sha1s.data(), + 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, + 2, + sha1s.data(), + patches.data(), + nullptr); + ASSERT_EQ(0, ap_result); + ASSERT_TRUE(file_cmp(old_file, new_file)); +} + +TEST_F(ApplyPatchFullTest, ApplyInNewLocation) { + std::vector sha1s; + sha1s.push_back(&bad_sha1_a[0]); + sha1s.push_back(&old_sha1[0]); + int ap_result = applypatch(&old_file[0], + &output_loc[0], + &new_sha1[0], + new_size, + 2, + sha1s.data(), + 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, + 2, + sha1s.data(), + patches.data(), + nullptr); + ASSERT_EQ(0, ap_result); + ASSERT_TRUE(file_cmp(output_loc, new_file)); +} + +TEST_F(ApplyPatchFullTest, ApplyCorruptedInNewLocation) { + mangle_file(old_file); + std::vector sha1s; + sha1s.push_back(&bad_sha1_a[0]); + sha1s.push_back(&old_sha1[0]); + int ap_result = applypatch(&old_file[0], + &output_loc[0], + &new_sha1[0], + new_size, + 2, + sha1s.data(), + 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, + 2, + sha1s.data(), + patches.data(), + nullptr); + ASSERT_EQ(0, ap_result); + ASSERT_TRUE(file_cmp(output_loc, new_file)); +} + +TEST_F(ApplyPatchDoubleCacheTest, ApplyDoubleCorruptedInNewLocation) { + mangle_file(old_file); + mangle_file(cache_file); + + std::vector sha1s; + sha1s.push_back(&bad_sha1_a[0]); + sha1s.push_back(&old_sha1[0]); + int ap_result = applypatch(&old_file[0], + &output_loc[0], + &new_sha1[0], + new_size, + 2, + sha1s.data(), + 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, + 2, + sha1s.data(), + patches.data(), + nullptr); + ASSERT_NE(0, ap_result); + ASSERT_FALSE(file_cmp(output_loc, new_file)); +} diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp new file mode 100644 index 00000000..780ff281 --- /dev/null +++ b/tests/component/verifier_test.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agree to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include "common.h" +#include "common/test_constants.h" +#include "minzip/SysUtil.h" +#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 { + void Init() { } + void SetStage(int, int) { } + void SetLocale(const char*) { } + void SetBackground(Icon icon) { } + void SetSystemUpdateText(bool security_update) { } + + void SetProgressType(ProgressType determinate) { } + void ShowProgress(float portion, float seconds) { } + void SetProgress(float fraction) { } + + void ShowText(bool visible) { } + bool IsTextVisible() { return false; } + bool WasTextEverVisible() { return false; } + void Print(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + } + void PrintOnScreenOnly(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + } + void ShowFile(const char*) { } + + void StartMenu(const char* const * headers, const char* const * items, + int initial_selection) { } + int SelectMenu(int sel) { return 0; } + void EndMenu() { } +}; + +void +ui_print(const char* format, ...) { + va_list ap; + va_start(ap, format); + vfprintf(stdout, format, ap); + va_end(ap); +} + +class VerifierTest : public testing::TestWithParam> { + public: + MemMapping memmap; + std::vector certs; + + virtual void SetUp() { + std::vector args = GetParam(); + std::string package = + android::base::StringPrintf("%s%s%s%s", DATA_PATH, NATIVE_TEST_PATH, + TESTDATA_PATH, args[0].c_str()); + if (sysMapFile(package.c_str(), &memmap) != 0) { + FAIL() << "Failed to mmap " << package << ": " << strerror(errno) + << "\n"; + } + + for (auto it = ++(args.cbegin()); it != args.cend(); ++it) { + if (it->substr(it->length() - 3, it->length()) == "256") { + if (certs.empty()) { + FAIL() << "May only specify -sha256 after key type\n"; + } + certs.back().hash_len = SHA256_DIGEST_LENGTH; + } else { + std::string public_key_file = android::base::StringPrintf( + "%s%s%stest_key_%s.txt", DATA_PATH, NATIVE_TEST_PATH, + TESTDATA_PATH, it->c_str()); + ASSERT_TRUE(load_keys(public_key_file.c_str(), certs)); + certs.back().hash_len = SHA_DIGEST_LENGTH; + } + } + if (certs.empty()) { + std::string public_key_file = android::base::StringPrintf( + "%s%s%stest_key_e3.txt", DATA_PATH, NATIVE_TEST_PATH, + TESTDATA_PATH); + ASSERT_TRUE(load_keys(public_key_file.c_str(), certs)); + certs.back().hash_len = SHA_DIGEST_LENGTH; + } + } + + static void SetUpTestCase() { + ui = new MockUI(); + } +}; + +class VerifierSuccessTest : public VerifierTest { +}; + +class VerifierFailureTest : public VerifierTest { +}; + +TEST_P(VerifierSuccessTest, VerifySucceed) { + ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs), VERIFY_SUCCESS); +} + +TEST_P(VerifierFailureTest, VerifyFailure) { + ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs), VERIFY_FAILURE); +} + +INSTANTIATE_TEST_CASE_P(SingleKeySuccess, VerifierSuccessTest, + ::testing::Values( + std::vector({"otasigned.zip", "e3"}), + std::vector({"otasigned_f4.zip", "f4"}), + std::vector({"otasigned_sha256.zip", "e3", "sha256"}), + std::vector({"otasigned_f4_sha256.zip", "f4", "sha256"}), + std::vector({"otasigned_ecdsa_sha256.zip", "ec", "sha256"}))); + +INSTANTIATE_TEST_CASE_P(MultiKeySuccess, VerifierSuccessTest, + ::testing::Values( + std::vector({"otasigned.zip", "f4", "e3"}), + std::vector({"otasigned_f4.zip", "ec", "f4"}), + std::vector({"otasigned_sha256.zip", "ec", "e3", "e3", "sha256"}), + std::vector({"otasigned_f4_sha256.zip", "ec", "sha256", "e3", "f4", "sha256"}), + std::vector({"otasigned_ecdsa_sha256.zip", "f4", "sha256", "e3", "ec", "sha256"}))); + +INSTANTIATE_TEST_CASE_P(WrongKey, VerifierFailureTest, + ::testing::Values( + std::vector({"otasigned.zip", "f4"}), + std::vector({"otasigned_f4.zip", "e3"}), + std::vector({"otasigned_ecdsa_sha256.zip", "e3", "sha256"}))); + +INSTANTIATE_TEST_CASE_P(WrongHash, VerifierFailureTest, + ::testing::Values( + std::vector({"otasigned.zip", "e3", "sha256"}), + std::vector({"otasigned_f4.zip", "f4", "sha256"}), + std::vector({"otasigned_sha256.zip"}), + std::vector({"otasigned_f4_sha256.zip", "f4"}), + std::vector({"otasigned_ecdsa_sha256.zip"}))); + +INSTANTIATE_TEST_CASE_P(BadPackage, VerifierFailureTest, + ::testing::Values( + std::vector({"random.zip"}), + std::vector({"fake-eocd.zip"}), + std::vector({"alter-metadata.zip"}), + std::vector({"alter-footer.zip"}))); diff --git a/testdata/alter-footer.zip b/tests/testdata/alter-footer.zip similarity index 100% rename from testdata/alter-footer.zip rename to tests/testdata/alter-footer.zip diff --git a/testdata/alter-metadata.zip b/tests/testdata/alter-metadata.zip similarity index 100% rename from testdata/alter-metadata.zip rename to tests/testdata/alter-metadata.zip diff --git a/testdata/fake-eocd.zip b/tests/testdata/fake-eocd.zip similarity index 100% rename from testdata/fake-eocd.zip rename to tests/testdata/fake-eocd.zip diff --git a/testdata/jarsigned.zip b/tests/testdata/jarsigned.zip similarity index 100% rename from testdata/jarsigned.zip rename to tests/testdata/jarsigned.zip diff --git a/tests/testdata/new.file b/tests/testdata/new.file new file mode 100644 index 00000000..cdeb8fd5 Binary files /dev/null and b/tests/testdata/new.file differ diff --git a/tests/testdata/old.file b/tests/testdata/old.file new file mode 100644 index 00000000..166c8732 Binary files /dev/null and b/tests/testdata/old.file differ diff --git a/testdata/otasigned.zip b/tests/testdata/otasigned.zip similarity index 100% rename from testdata/otasigned.zip rename to tests/testdata/otasigned.zip diff --git a/testdata/otasigned_ecdsa_sha256.zip b/tests/testdata/otasigned_ecdsa_sha256.zip similarity index 100% rename from testdata/otasigned_ecdsa_sha256.zip rename to tests/testdata/otasigned_ecdsa_sha256.zip diff --git a/testdata/otasigned_f4.zip b/tests/testdata/otasigned_f4.zip similarity index 100% rename from testdata/otasigned_f4.zip rename to tests/testdata/otasigned_f4.zip diff --git a/testdata/otasigned_f4_sha256.zip b/tests/testdata/otasigned_f4_sha256.zip similarity index 100% rename from testdata/otasigned_f4_sha256.zip rename to tests/testdata/otasigned_f4_sha256.zip diff --git a/testdata/otasigned_sha256.zip b/tests/testdata/otasigned_sha256.zip similarity index 100% rename from testdata/otasigned_sha256.zip rename to tests/testdata/otasigned_sha256.zip diff --git a/tests/testdata/patch.bsdiff b/tests/testdata/patch.bsdiff new file mode 100644 index 00000000..b78d3857 Binary files /dev/null and b/tests/testdata/patch.bsdiff differ diff --git a/testdata/random.zip b/tests/testdata/random.zip similarity index 100% rename from testdata/random.zip rename to tests/testdata/random.zip diff --git a/testdata/test_f4.pk8 b/tests/testdata/test_f4.pk8 similarity index 100% rename from testdata/test_f4.pk8 rename to tests/testdata/test_f4.pk8 diff --git a/testdata/test_f4.x509.pem b/tests/testdata/test_f4.x509.pem similarity index 100% rename from testdata/test_f4.x509.pem rename to tests/testdata/test_f4.x509.pem diff --git a/testdata/test_f4_sha256.x509.pem b/tests/testdata/test_f4_sha256.x509.pem similarity index 100% rename from testdata/test_f4_sha256.x509.pem rename to tests/testdata/test_f4_sha256.x509.pem diff --git a/tests/testdata/test_key_e3.txt b/tests/testdata/test_key_e3.txt new file mode 100644 index 00000000..53f5297b --- /dev/null +++ b/tests/testdata/test_key_e3.txt @@ -0,0 +1 @@ +{64,0xc926ad21,{1795090719,2141396315,950055447,2581568430,4268923165,1920809988,546586521,3498997798,1776797858,3740060814,1805317999,1429410244,129622599,1422441418,1783893377,1222374759,2563319927,323993566,28517732,609753416,1826472888,215237850,4261642700,4049082591,3228462402,774857746,154822455,2497198897,2758199418,3019015328,2794777644,87251430,2534927978,120774784,571297800,3695899472,2479925187,3811625450,3401832990,2394869647,3267246207,950095497,555058928,414729973,1136544882,3044590084,465547824,4058146728,2731796054,1689838846,3890756939,1048029507,895090649,247140249,178744550,3547885223,3165179243,109881576,3944604415,1044303212,3772373029,2985150306,3737520932,3599964420},{3437017481,3784475129,2800224972,3086222688,251333580,2131931323,512774938,325948880,2657486437,2102694287,3820568226,792812816,1026422502,2053275343,2800889200,3113586810,165549746,4273519969,4065247892,1902789247,772932719,3941848426,3652744109,216871947,3164400649,1942378755,3996765851,1055777370,964047799,629391717,2232744317,3910558992,191868569,2758883837,3682816752,2997714732,2702529250,3570700455,3776873832,3924067546,3555689545,2758825434,1323144535,61311905,1997411085,376844204,213777604,4077323584,9135381,1625809335,2804742137,2952293945,1117190829,4237312782,1825108855,3013147971,1111251351,2568837572,1684324211,2520978805,367251975,810756730,2353784344,1175080310}} diff --git a/tests/testdata/test_key_ec.txt b/tests/testdata/test_key_ec.txt new file mode 100644 index 00000000..72b4395d --- /dev/null +++ b/tests/testdata/test_key_ec.txt @@ -0,0 +1 @@ +v5 {32,{36,250,86,214,202,22,20,147,198,120,2,28,76,190,78,23,106,35,24,96,86,22,186,69,132,93,192,232,0,213,14,103},{222,154,23,13,125,130,22,76,146,185,140,159,138,255,105,143,32,16,27,72,175,145,141,121,233,184,77,24,217,141,132,181}} diff --git a/tests/testdata/test_key_f4.txt b/tests/testdata/test_key_f4.txt new file mode 100644 index 00000000..54ddbbad --- /dev/null +++ b/tests/testdata/test_key_f4.txt @@ -0,0 +1 @@ +v2 {64,0xc9bd1f21,{293133087,3210546773,865313125,250921607,3158780490,943703457,1242806226,2986289859,2942743769,2457906415,2719374299,1783459420,149579627,3081531591,3440738617,2788543742,2758457512,1146764939,3699497403,2446203424,1744968926,1159130537,2370028300,3978231572,3392699980,1487782451,1180150567,2841334302,3753960204,961373345,3333628321,748825784,2978557276,1566596926,1613056060,2600292737,1847226629,50398611,1890374404,2878700735,2286201787,1401186359,619285059,731930817,2340993166,1156490245,2992241729,151498140,318782170,3480838990,2100383433,4223552555,3628927011,4247846280,1759029513,4215632601,2719154626,3490334597,1751299340,3487864726,3668753795,4217506054,3748782284,3150295088},{1772626313,445326068,3477676155,1758201194,2986784722,491035581,3922936562,702212696,2979856666,3324974564,2488428922,3056318590,1626954946,664714029,398585816,3964097931,3356701905,2298377729,2040082097,3025491477,539143308,3348777868,2995302452,3602465520,212480763,2691021393,1307177300,704008044,2031136606,1054106474,3838318865,2441343869,1477566916,700949900,2534790355,3353533667,336163563,4106790558,2701448228,1571536379,1103842411,3623110423,1635278839,1577828979,910322800,715583630,138128831,1017877531,2289162787,447994798,1897243165,4121561445,4150719842,2131821093,2262395396,3305771534,980753571,3256525190,3128121808,1072869975,3507939515,4229109952,118381341,2209831334}} diff --git a/testdata/testkey.pk8 b/tests/testdata/testkey.pk8 similarity index 100% rename from testdata/testkey.pk8 rename to tests/testdata/testkey.pk8 diff --git a/testdata/testkey.x509.pem b/tests/testdata/testkey.x509.pem similarity index 100% rename from testdata/testkey.x509.pem rename to tests/testdata/testkey.x509.pem diff --git a/testdata/testkey_ecdsa.pk8 b/tests/testdata/testkey_ecdsa.pk8 similarity index 100% rename from testdata/testkey_ecdsa.pk8 rename to tests/testdata/testkey_ecdsa.pk8 diff --git a/testdata/testkey_ecdsa.x509.pem b/tests/testdata/testkey_ecdsa.x509.pem similarity index 100% rename from testdata/testkey_ecdsa.x509.pem rename to tests/testdata/testkey_ecdsa.x509.pem diff --git a/testdata/testkey_sha256.x509.pem b/tests/testdata/testkey_sha256.x509.pem similarity index 100% rename from testdata/testkey_sha256.x509.pem rename to tests/testdata/testkey_sha256.x509.pem diff --git a/testdata/unsigned.zip b/tests/testdata/unsigned.zip similarity index 100% rename from testdata/unsigned.zip rename to tests/testdata/unsigned.zip diff --git a/tests/asn1_decoder_test.cpp b/tests/unit/asn1_decoder_test.cpp similarity index 100% rename from tests/asn1_decoder_test.cpp rename to tests/unit/asn1_decoder_test.cpp diff --git a/tests/unit/locale_test.cpp b/tests/unit/locale_test.cpp new file mode 100644 index 00000000..0e515f8c --- /dev/null +++ b/tests/unit/locale_test.cpp @@ -0,0 +1,29 @@ +/* + * 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 + +#include "minui/minui.h" + +TEST(LocaleTest, Misc) { + EXPECT_TRUE(matches_locale("zh_CN", "zh_CN_#Hans")); + EXPECT_TRUE(matches_locale("zh", "zh_CN_#Hans")); + EXPECT_FALSE(matches_locale("zh_HK", "zh_CN_#Hans")); + EXPECT_TRUE(matches_locale("en_GB", "en_GB")); + EXPECT_TRUE(matches_locale("en", "en_GB")); + EXPECT_FALSE(matches_locale("en_GB", "en")); + EXPECT_FALSE(matches_locale("en_GB", "en_US")); +} diff --git a/tests/unit/recovery_test.cpp b/tests/unit/recovery_test.cpp new file mode 100644 index 00000000..f397f258 --- /dev/null +++ b/tests/unit/recovery_test.cpp @@ -0,0 +1,92 @@ +/* + * 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 +#include +#include +#include + +#include +#include +#include +#include + +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/toolbox/Android.mk b/toolbox/Android.mk index dc4252da..c721bbd4 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -224,7 +224,7 @@ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 22; echo $$?),0) ../../../$(TWRP_TOOLBOX_PATH)/setprop.c OUR_TOOLS += getprop setprop ifneq ($(TW_USE_TOOLBOX), true) - LOCAL_SRC_FILES += ls.c + LOCAL_SRC_FILES += ../../../$(TWRP_TOOLBOX_PATH)/ls.c endif endif diff --git a/toolbox/ls.c b/toolbox/ls.c new file mode 100644 index 00000000..9a89dd46 --- /dev/null +++ b/toolbox/ls.c @@ -0,0 +1,588 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// simple dynamic array of strings. +typedef struct { + int count; + int capacity; + void** items; +} strlist_t; + +#define STRLIST_INITIALIZER { 0, 0, NULL } + +/* Used to iterate over a strlist_t + * _list :: pointer to strlist_t object + * _item :: name of local variable name defined within the loop with + * type 'char*' + * _stmnt :: C statement executed in each iteration + * + * This macro is only intended for simple uses. Do not add or remove items + * to/from the list during iteration. + */ +#define STRLIST_FOREACH(_list,_item,_stmnt) \ + do { \ + int _nn_##__LINE__ = 0; \ + for (;_nn_##__LINE__ < (_list)->count; ++ _nn_##__LINE__) { \ + char* _item = (char*)(_list)->items[_nn_##__LINE__]; \ + _stmnt; \ + } \ + } while (0) + +static void dynarray_reserve_more( strlist_t *a, int count ) { + int old_cap = a->capacity; + int new_cap = old_cap; + const int max_cap = INT_MAX/sizeof(void*); + void** new_items; + int new_count = a->count + count; + + if (count <= 0) + return; + + if (count > max_cap - a->count) + abort(); + + new_count = a->count + count; + + while (new_cap < new_count) { + old_cap = new_cap; + new_cap += (new_cap >> 2) + 4; + if (new_cap < old_cap || new_cap > max_cap) { + new_cap = max_cap; + } + } + new_items = realloc(a->items, new_cap*sizeof(void*)); + if (new_items == NULL) + abort(); + + a->items = new_items; + a->capacity = new_cap; +} + +void strlist_init( strlist_t *list ) { + list->count = list->capacity = 0; + list->items = NULL; +} + +// append a new string made of the first 'slen' characters from 'str' +// followed by a trailing zero. +void strlist_append_b( strlist_t *list, const void* str, size_t slen ) { + char *copy = malloc(slen+1); + memcpy(copy, str, slen); + copy[slen] = '\0'; + if (list->count >= list->capacity) + dynarray_reserve_more(list, 1); + list->items[list->count++] = copy; +} + +// append the copy of a given input string to a strlist_t. +void strlist_append_dup( strlist_t *list, const char *str) { + strlist_append_b(list, str, strlen(str)); +} + +// note: strlist_done will free all the strings owned by the list. +void strlist_done( strlist_t *list ) { + STRLIST_FOREACH(list, string, free(string)); + free(list->items); + list->items = NULL; + list->count = list->capacity = 0; +} + +static int strlist_compare_strings(const void* a, const void* b) { + const char *sa = *(const char **)a; + const char *sb = *(const char **)b; + return strcmp(sa, sb); +} + +/* sort the strings in a given list (using strcmp) */ +void strlist_sort( strlist_t *list ) { + if (list->count > 0) { + qsort(list->items, (size_t)list->count, sizeof(void*), strlist_compare_strings); + } +} + + +// bits for flags argument +#define LIST_LONG (1 << 0) +#define LIST_ALL (1 << 1) +#define LIST_RECURSIVE (1 << 2) +#define LIST_DIRECTORIES (1 << 3) +#define LIST_SIZE (1 << 4) +#define LIST_LONG_NUMERIC (1 << 5) +#define LIST_CLASSIFY (1 << 6) +#define LIST_MACLABEL (1 << 7) +#define LIST_INODE (1 << 8) + +// fwd +static int listpath(const char *name, int flags); + +static char mode2kind(mode_t mode) +{ + switch(mode & S_IFMT){ + case S_IFSOCK: return 's'; + case S_IFLNK: return 'l'; + case S_IFREG: return '-'; + case S_IFDIR: return 'd'; + case S_IFBLK: return 'b'; + case S_IFCHR: return 'c'; + case S_IFIFO: return 'p'; + default: return '?'; + } +} + +void strmode(mode_t mode, char *out) +{ + *out++ = mode2kind(mode); + + *out++ = (mode & 0400) ? 'r' : '-'; + *out++ = (mode & 0200) ? 'w' : '-'; + if(mode & 04000) { + *out++ = (mode & 0100) ? 's' : 'S'; + } else { + *out++ = (mode & 0100) ? 'x' : '-'; + } + *out++ = (mode & 040) ? 'r' : '-'; + *out++ = (mode & 020) ? 'w' : '-'; + if(mode & 02000) { + *out++ = (mode & 010) ? 's' : 'S'; + } else { + *out++ = (mode & 010) ? 'x' : '-'; + } + *out++ = (mode & 04) ? 'r' : '-'; + *out++ = (mode & 02) ? 'w' : '-'; + if(mode & 01000) { + *out++ = (mode & 01) ? 't' : 'T'; + } else { + *out++ = (mode & 01) ? 'x' : '-'; + } + *out = 0; +} + +static void user2str(uid_t uid, char *out, size_t out_size) +{ + struct passwd *pw = getpwuid(uid); + if(pw) { + strlcpy(out, pw->pw_name, out_size); + } else { + snprintf(out, out_size, "%d", uid); + } +} + +static void group2str(gid_t gid, char *out, size_t out_size) +{ + struct group *gr = getgrgid(gid); + if(gr) { + strlcpy(out, gr->gr_name, out_size); + } else { + snprintf(out, out_size, "%d", gid); + } +} + +static int show_total_size(const char *dirname, DIR *d, int flags) +{ + struct dirent *de; + char tmp[1024]; + struct stat s; + int sum = 0; + + /* run through the directory and sum up the file block sizes */ + while ((de = readdir(d)) != 0) { + if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) + continue; + if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0) + continue; + + if (strcmp(dirname, "/") == 0) + snprintf(tmp, sizeof(tmp), "/%s", de->d_name); + else + snprintf(tmp, sizeof(tmp), "%s/%s", dirname, de->d_name); + + if (lstat(tmp, &s) < 0) { + fprintf(stderr, "stat failed on %s: %s\n", tmp, strerror(errno)); + rewinddir(d); + return -1; + } + + sum += s.st_blocks / 2; + } + + printf("total %d\n", sum); + rewinddir(d); + return 0; +} + +static int listfile_size(const char *path, const char *filename, struct stat *s, + int flags) +{ + if(!s || !path) { + return -1; + } + + /* blocks are 512 bytes, we want output to be KB */ + if ((flags & LIST_SIZE) != 0) { + printf("%lld ", (long long)s->st_blocks / 2); + } + + if ((flags & LIST_CLASSIFY) != 0) { + char filetype = mode2kind(s->st_mode); + if (filetype != 'l') { + printf("%c ", filetype); + } else { + struct stat link_dest; + if (!stat(path, &link_dest)) { + printf("l%c ", mode2kind(link_dest.st_mode)); + } else { + fprintf(stderr, "stat '%s' failed: %s\n", path, strerror(errno)); + printf("l? "); + } + } + } + + printf("%s\n", filename); + + return 0; +} + +static int listfile_long(const char *path, struct stat *s, int flags) +{ + char date[32]; + char mode[16]; + char user[32]; + char group[32]; + const char *name; + + if(!s || !path) { + return -1; + } + + /* name is anything after the final '/', or the whole path if none*/ + name = strrchr(path, '/'); + if(name == 0) { + name = path; + } else { + name++; + } + + strmode(s->st_mode, mode); + if (flags & LIST_LONG_NUMERIC) { + snprintf(user, sizeof(user), "%u", s->st_uid); + snprintf(group, sizeof(group), "%u", s->st_gid); + } else { + user2str(s->st_uid, user, sizeof(user)); + group2str(s->st_gid, group, sizeof(group)); + } + + strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s->st_mtime)); + date[31] = 0; + +// 12345678901234567890123456789012345678901234567890123456789012345678901234567890 +// MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK) + + switch(s->st_mode & S_IFMT) { + case S_IFBLK: + case S_IFCHR: + printf("%s %-8s %-8s %3d, %3d %s %s\n", + mode, user, group, + major(s->st_rdev), minor(s->st_rdev), + date, name); + break; + case S_IFREG: + printf("%s %-8s %-8s %8lld %s %s\n", + mode, user, group, (long long)s->st_size, date, name); + break; + case S_IFLNK: { + char linkto[256]; + ssize_t len; + + len = readlink(path, linkto, 256); + if(len < 0) return -1; + + if(len > 255) { + linkto[252] = '.'; + linkto[253] = '.'; + linkto[254] = '.'; + linkto[255] = 0; + } else { + linkto[len] = 0; + } + + printf("%s %-8s %-8s %s %s -> %s\n", + mode, user, group, date, name, linkto); + break; + } + default: + printf("%s %-8s %-8s %s %s\n", + mode, user, group, date, name); + + } + return 0; +} + +static int listfile_maclabel(const char *path, struct stat *s) +{ + char mode[16]; + char user[32]; + char group[32]; + char *maclabel = NULL; + const char *name; + + if(!s || !path) { + return -1; + } + + /* name is anything after the final '/', or the whole path if none*/ + name = strrchr(path, '/'); + if(name == 0) { + name = path; + } else { + name++; + } + + lgetfilecon(path, &maclabel); + if (!maclabel) { + return -1; + } + + strmode(s->st_mode, mode); + user2str(s->st_uid, user, sizeof(user)); + group2str(s->st_gid, group, sizeof(group)); + + switch(s->st_mode & S_IFMT) { + case S_IFLNK: { + char linkto[256]; + ssize_t len; + + len = readlink(path, linkto, sizeof(linkto)); + if(len < 0) return -1; + + if((size_t)len > sizeof(linkto)-1) { + linkto[sizeof(linkto)-4] = '.'; + linkto[sizeof(linkto)-3] = '.'; + linkto[sizeof(linkto)-2] = '.'; + linkto[sizeof(linkto)-1] = 0; + } else { + linkto[len] = 0; + } + + printf("%s %-8s %-8s %s %s -> %s\n", + mode, user, group, maclabel, name, linkto); + break; + } + default: + printf("%s %-8s %-8s %s %s\n", + mode, user, group, maclabel, name); + + } + + free(maclabel); + + return 0; +} + +static int listfile(const char *dirname, const char *filename, int flags) +{ + struct stat s; + + if ((flags & (LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL | LIST_INODE)) == 0) { + printf("%s\n", filename); + return 0; + } + + char tmp[4096]; + const char* pathname = filename; + + if (dirname != NULL) { + snprintf(tmp, sizeof(tmp), "%s/%s", dirname, filename); + pathname = tmp; + } else { + pathname = filename; + } + + if(lstat(pathname, &s) < 0) { + fprintf(stderr, "lstat '%s' failed: %s\n", pathname, strerror(errno)); + return -1; + } + + if(flags & LIST_INODE) { + printf("%8llu ", (unsigned long long)s.st_ino); + } + + if ((flags & LIST_MACLABEL) != 0) { + return listfile_maclabel(pathname, &s); + } else if ((flags & LIST_LONG) != 0) { + return listfile_long(pathname, &s, flags); + } else /*((flags & LIST_SIZE) != 0)*/ { + return listfile_size(pathname, filename, &s, flags); + } +} + +static int listdir(const char *name, int flags) +{ + char tmp[4096]; + DIR *d; + struct dirent *de; + strlist_t files = STRLIST_INITIALIZER; + + d = opendir(name); + if(d == 0) { + fprintf(stderr, "opendir failed, %s\n", strerror(errno)); + return -1; + } + + if ((flags & LIST_SIZE) != 0) { + show_total_size(name, d, flags); + } + + while((de = readdir(d)) != 0){ + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; + if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue; + + strlist_append_dup(&files, de->d_name); + } + + strlist_sort(&files); + STRLIST_FOREACH(&files, filename, listfile(name, filename, flags)); + strlist_done(&files); + + if (flags & LIST_RECURSIVE) { + strlist_t subdirs = STRLIST_INITIALIZER; + + rewinddir(d); + + while ((de = readdir(d)) != 0) { + struct stat s; + int err; + + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0) + continue; + + if (!strcmp(name, "/")) + snprintf(tmp, sizeof(tmp), "/%s", de->d_name); + else + snprintf(tmp, sizeof(tmp), "%s/%s", name, de->d_name); + + /* + * If the name ends in a '/', use stat() so we treat it like a + * directory even if it's a symlink. + */ + if (tmp[strlen(tmp)-1] == '/') + err = stat(tmp, &s); + else + err = lstat(tmp, &s); + + if (err < 0) { + perror(tmp); + closedir(d); + return -1; + } + + if (S_ISDIR(s.st_mode)) { + strlist_append_dup(&subdirs, tmp); + } + } + strlist_sort(&subdirs); + STRLIST_FOREACH(&subdirs, path, { + printf("\n%s:\n", path); + listdir(path, flags); + }); + strlist_done(&subdirs); + } + + closedir(d); + return 0; +} + +static int listpath(const char *name, int flags) +{ + struct stat s; + int err; + + /* + * If the name ends in a '/', use stat() so we treat it like a + * directory even if it's a symlink. + */ + if (name[strlen(name)-1] == '/') + err = stat(name, &s); + else + err = lstat(name, &s); + + if (err < 0) { + perror(name); + return -1; + } + + if ((flags & LIST_DIRECTORIES) == 0 && S_ISDIR(s.st_mode)) { + if (flags & LIST_RECURSIVE) + printf("\n%s:\n", name); + return listdir(name, flags); + } else { + /* yeah this calls stat() again*/ + return listfile(NULL, name, flags); + } +} + +int ls_main(int argc, char **argv) +{ + int flags = 0; + + if(argc > 1) { + int i; + int err = 0; + strlist_t files = STRLIST_INITIALIZER; + + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + /* an option ? */ + const char *arg = argv[i]+1; + while (arg[0]) { + switch (arg[0]) { + case 'l': flags |= LIST_LONG; break; + case 'n': flags |= LIST_LONG | LIST_LONG_NUMERIC; break; + case 's': flags |= LIST_SIZE; break; + case 'R': flags |= LIST_RECURSIVE; break; + case 'd': flags |= LIST_DIRECTORIES; break; + case 'Z': flags |= LIST_MACLABEL; break; + case 'a': flags |= LIST_ALL; break; + case 'F': flags |= LIST_CLASSIFY; break; + case 'i': flags |= LIST_INODE; break; + default: + fprintf(stderr, "%s: Unknown option '-%c'. Aborting.\n", "ls", arg[0]); + exit(1); + } + arg++; + } + } else { + /* not an option ? */ + strlist_append_dup(&files, argv[i]); + } + } + + if (files.count > 0) { + STRLIST_FOREACH(&files, path, { + if (listpath(path, flags) != 0) { + err = EXIT_FAILURE; + } + }); + strlist_done(&files); + return err; + } + } + + // list working directory if no files or directories were specified + return listpath(".", flags); +} diff --git a/tools/recovery_l10n/Android.mk b/tools/recovery_l10n/Android.mk new file mode 100644 index 00000000..937abd1e --- /dev/null +++ b/tools/recovery_l10n/Android.mk @@ -0,0 +1,12 @@ +# Copyright 2012 Google Inc. All Rights Reserved. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_PACKAGE_NAME := RecoveryLocalizer +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +include $(BUILD_PACKAGE) diff --git a/tools/recovery_l10n/AndroidManifest.xml b/tools/recovery_l10n/AndroidManifest.xml new file mode 100644 index 00000000..8c51a4e0 --- /dev/null +++ b/tools/recovery_l10n/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/tools/recovery_l10n/res/layout/main.xml b/tools/recovery_l10n/res/layout/main.xml new file mode 100644 index 00000000..05a16e1e --- /dev/null +++ b/tools/recovery_l10n/res/layout/main.xml @@ -0,0 +1,33 @@ + + + + + +