Update to AOSP 8.0 base
Change-Id: I29fe722b4eb9718765327902779046840a01433e
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..5322788
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,15 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Empty
+AllowShortIfStatementsOnASingleLine: true
+
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 2
+PointerAlignment: Left
+TabWidth: 2
+UseTab: Never
+PenaltyExcessCharacter: 32
+
+Cpp11BracedListStyle: false
diff --git a/Android.mk b/Android.mk
index 85cdd32..7338adf 100644
--- a/Android.mk
+++ b/Android.mk
@@ -104,24 +104,25 @@
LOCAL_C_INCLUDES += \
system/vold \
- system/extras/ext4_utils \
+ system/extras \
system/core/adb \
system/core/libsparse \
- external/zlib
+ external/zlib \
+ $(LOCAL_PATH)/bootloader_message_twrp/include
LOCAL_C_INCLUDES += bionic
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
LOCAL_C_INCLUDES += external/stlport/stlport external/openssl/include
else
- LOCAL_C_INCLUDES += external/boringssl/include
+ LOCAL_C_INCLUDES += external/boringssl/include external/libcxx/include
endif
LOCAL_STATIC_LIBRARIES :=
LOCAL_SHARED_LIBRARIES :=
LOCAL_STATIC_LIBRARIES += libguitwrp
-LOCAL_SHARED_LIBRARIES += libaosprecovery libz libc libcutils libstdc++ libtar libblkid libminuitwrp libminadbd libmtdutils libminzip libtwadbbu libbootloader_message
-LOCAL_SHARED_LIBRARIES += libcrecovery libtwadbbu libtwrpdigest
+LOCAL_SHARED_LIBRARIES += libaosprecovery libz libc libcutils libstdc++ libtar libblkid libminuitwrp libminadbd libmtdutils libtwadbbu libbootloader_message_twrp
+LOCAL_SHARED_LIBRARIES += libcrecovery libtwadbbu libtwrpdigest libc++
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
LOCAL_SHARED_LIBRARIES += libstlport
@@ -132,13 +133,20 @@
LOCAL_C_INCLUDES += $(LOCAL_PATH)/libmincrypt/includes
LOCAL_CFLAGS += -DUSE_OLD_VERIFIER
else
- LOCAL_SHARED_LIBRARIES += libc++ libcrypto
+ LOCAL_SHARED_LIBRARIES += libcrypto
endif
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 24; echo $$?),0)
LOCAL_SHARED_LIBRARIES += libbase
endif
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+ LOCAL_SHARED_LIBRARIES += libziparchive
+else
+ LOCAL_SHARED_LIBRARIES += libminzip
+ LOCAL_CFLAGS += -DUSE_MINZIP
+endif
+
ifneq ($(wildcard system/core/libsparse/Android.mk),)
LOCAL_SHARED_LIBRARIES += libsparse
endif
@@ -161,14 +169,14 @@
endif
LOCAL_C_INCLUDES += external/libselinux/include
LOCAL_SHARED_LIBRARIES += libselinux
-ifneq ($(TARGET_USERIMAGES_USE_EXT4), true)
- LOCAL_CFLAGS += -DUSE_EXT4
- LOCAL_C_INCLUDES += system/extras/ext4_utils
- LOCAL_SHARED_LIBRARIES += libext4_utils
- ifneq ($(wildcard external/lz4/Android.mk),)
- LOCAL_STATIC_LIBRARIES += liblz4
- endif
-endif
+#ifneq ($(TARGET_USERIMAGES_USE_EXT4), true)
+# LOCAL_CFLAGS += -DUSE_EXT4
+# LOCAL_C_INCLUDES += system/extras/ext4_utils
+# LOCAL_SHARED_LIBRARIES += libext4_utils
+# ifneq ($(wildcard external/lz4/Android.mk),)
+# LOCAL_STATIC_LIBRARIES += liblz4
+# endif
+#endif
ifeq ($(AB_OTA_UPDATER),true)
LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
@@ -304,7 +312,7 @@
endif
endif
WITH_CRYPTO_UTILS := \
- $(if $(wildcard system/core/libcrypto_utils/Android.mk),true)
+ $(if $(wildcard system/core/libcrypto_utils/android_pubkey.c),true)
ifeq ($(TW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID), true)
LOCAL_CFLAGS += -DTW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID
endif
@@ -370,7 +378,8 @@
mkfs.fat \
permissive.sh \
simg2img_twrp \
- libbootloader_message \
+ libbootloader_message_twrp \
+ init.recovery.hlthchrg.rc \
init.recovery.service.rc
ifneq ($(TARGET_ARCH), arm64)
@@ -589,6 +598,37 @@
endif
include $(BUILD_SHARED_LIBRARY)
+# libmounts (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := mounts.cpp
+LOCAL_CLANG := true
+LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror
+LOCAL_MODULE := libmounts
+include $(BUILD_STATIC_LIBRARY)
+
+# librecovery (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ install.cpp
+LOCAL_CFLAGS := -Wno-unused-parameter -Werror
+LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
+
+ifeq ($(AB_OTA_UPDATER),true)
+ LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
+endif
+
+LOCAL_MODULE := librecovery
+LOCAL_STATIC_LIBRARIES := \
+ libminui \
+ libvintf_recovery \
+ libcrypto_utils \
+ libcrypto \
+ libbase
+
+include $(BUILD_STATIC_LIBRARY)
+
# shared libaosprecovery for Apache code
# ===============================
include $(CLEAR_VARS)
@@ -596,7 +636,7 @@
LOCAL_MODULE := libaosprecovery
LOCAL_MODULE_TAGS := eng optional
LOCAL_CFLAGS := -std=gnu++0x
-LOCAL_SRC_FILES := adb_install.cpp asn1_decoder.cpp legacy_property_service.cpp set_metadata.cpp tw_atomic.cpp installcommand.cpp
+LOCAL_SRC_FILES := adb_install.cpp legacy_property_service.cpp set_metadata.cpp tw_atomic.cpp installcommand.cpp zipwrap.cpp
LOCAL_SHARED_LIBRARIES += libc liblog libcutils libmtdutils libfusesideload libselinux libminzip
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
@@ -608,15 +648,23 @@
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 24; echo $$?),0)
LOCAL_SHARED_LIBRARIES += libmincrypttwrp
LOCAL_C_INCLUDES += $(LOCAL_PATH)/libmincrypt/includes
- LOCAL_SRC_FILES += verifier24/verifier.cpp
+ LOCAL_SRC_FILES += verifier24/verifier.cpp verifier24/asn1_decoder.cpp
LOCAL_CFLAGS += -DUSE_OLD_VERIFIER
else
LOCAL_SHARED_LIBRARIES += libcrypto libbase
- LOCAL_SRC_FILES += verifier.cpp
+ LOCAL_SRC_FILES += verifier.cpp asn1_decoder.cpp
endif
ifeq ($(AB_OTA_UPDATER),true)
LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
endif
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+ LOCAL_SRC_FILES += otautil/ZipUtil.cpp otautil/SysUtil.cpp
+ LOCAL_SHARED_LIBRARIES += libziparchive libext4_utils libcrypto libcrypto_utils
+ LOCAL_STATIC_LIBRARIES += libvintf_recovery libfs_mgr liblogwrap libavb libvintf libtinyxml2 libz
+ LOCAL_WHOLE_STATIC_LIBRARIES +=
+else
+ LOCAL_CFLAGS += -DUSE_MINZIP
+endif
include $(BUILD_SHARED_LIBRARY)
@@ -638,6 +686,7 @@
$(LOCAL_PATH)/edify/Android.mk \
$(LOCAL_PATH)/otafault/Android.mk \
$(LOCAL_PATH)/bootloader_message/Android.mk \
+ $(LOCAL_PATH)/bootloader_message_twrp/Android.mk \
$(LOCAL_PATH)/updater/Android.mk \
$(LOCAL_PATH)/update_verifier/Android.mk \
$(LOCAL_PATH)/applypatch/Android.mk
@@ -647,6 +696,10 @@
endif
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 22; echo $$?),0)
+ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 26; echo $$?),0)
+ TARGET_GLOBAL_CFLAGS += -DTW_USE_MINUI_WITH_DATA
+ CLANG_TARGET_GLOBAL_CFLAGS += -DTW_USE_MINUI_WITH_DATA
+ endif
include $(commands_recovery_local_path)/minadbd/Android.mk \
$(commands_recovery_local_path)/minui/Android.mk
else
@@ -669,6 +722,7 @@
$(commands_recovery_local_path)/libcrecovery/Android.mk \
$(commands_recovery_local_path)/libblkid/Android.mk \
$(commands_recovery_local_path)/minuitwrp/Android.mk \
+ $(commands_recovery_local_path)/otautil/Android.mk \
$(commands_recovery_local_path)/openaes/Android.mk \
$(commands_recovery_local_path)/toolbox/Android.mk \
$(commands_recovery_local_path)/twrpTarMain/Android.mk \
diff --git a/README.md b/README.md
index 96f3789..820a8b7 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,3 @@
**Team Win Recovery Project (TWRP)**
You can find a compiling guide [here](http://forum.xda-developers.com/showthread.php?t=1943625 "Guide").
-
diff --git a/adb_install.cpp b/adb_install.cpp
index 448d617..771994c 100644
--- a/adb_install.cpp
+++ b/adb_install.cpp
@@ -28,6 +28,8 @@
#include "ui.h"
#include "cutils/properties.h"
+#include "install.h"
+#include "common.h"
#include "adb_install.h"
#include "minadbd/fuse_adb_provider.h"
#include "fuse_sideload.h"
@@ -37,10 +39,7 @@
#include "verifier.h"
#endif
-static RecoveryUI* ui = NULL;
-
-void
-set_usb_driver(bool enabled) {
+static void set_usb_driver(bool enabled) {
int fd = open("/sys/class/android_usb/android0/enable", O_WRONLY);
if (fd < 0) {
/* These error messages show when built in older Android branches (e.g. Gingerbread)
@@ -65,20 +64,18 @@
}
}
-static void
-stop_adbd() {
+static void stop_adbd() {
+ printf("Stopping adbd...\n");
property_set("ctl.stop", "adbd");
set_usb_driver(false);
}
-bool is_ro_debuggable() {
+static bool is_ro_debuggable() {
char value[PROPERTY_VALUE_MAX+1];
return (property_get("ro.debuggable", value, NULL) == 1 && value[0] == '1');
}
-void
-maybe_restart_adbd() {
- char value[PROPERTY_VALUE_MAX+1];
+static void maybe_restart_adbd() {
if (is_ro_debuggable()) {
printf("Restarting adbd...\n");
set_usb_driver(true);
@@ -96,6 +93,12 @@
stop_adbd();
set_usb_driver(true);
/*
+int apply_from_adb(RecoveryUI* ui, bool* wipe_cache, const char* install_file) {
+ modified_flash = true;
+
+ stop_adbd(ui);
+ set_usb_driver(ui, true);
+
ui->Print("\n\nNow send the package you want to apply\n"
"to the device with \"adb sideload <filename>\"...\n");
*/
diff --git a/adb_install.h b/adb_install.h
index 24e9e21..e9e88f7 100644
--- a/adb_install.h
+++ b/adb_install.h
@@ -19,8 +19,8 @@
//class RecoveryUI;
-void set_usb_driver(bool enabled);
-void maybe_restart_adbd();
+static void set_usb_driver(bool enabled);
+static void maybe_restart_adbd();
int apply_from_adb(const char* install_file, pid_t* child_pid);
#endif
diff --git a/applypatch/Android.mk b/applypatch/Android.mk
index 2f96f0a..9cbe3e2 100644
--- a/applypatch/Android.mk
+++ b/applypatch/Android.mk
@@ -31,6 +31,7 @@
)
LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/include \
external/bzip2 \
external/zlib \
$(commands_recovery_local_path)
@@ -42,27 +43,99 @@
LOCAL_C_INCLUDES += $(RECOVERY_PATH)
LOCAL_STATIC_LIBRARIES += libbase libotafault libmtdutils libcrypto_static libbz libz
+# libapplypatch (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ applypatch.cpp \
+ bspatch.cpp \
+ freecache.cpp \
+ imgpatch.cpp
+LOCAL_MODULE := libapplypatch
+LOCAL_MODULE_TAGS := eng
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include \
+ $(commands_recovery_local_path)
+LOCAL_STATIC_LIBRARIES := \
+ libotafault \
+ libbase \
+ libcrypto \
+ libbspatch \
+ libbz \
+ libz
+LOCAL_WHOLE_STATIC_LIBRARIES += libmtdutils
+LOCAL_CFLAGS := \
+ -DZLIB_CONST \
+ -Werror
include $(BUILD_STATIC_LIBRARY)
+# libimgpatch (static library)
+# ===============================
include $(CLEAR_VARS)
-ifeq ($(HOST_OS),linux)
-include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ bspatch.cpp \
+ imgpatch.cpp
+LOCAL_MODULE := libimgpatch
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include \
+ $(commands_recovery_local_path)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := \
+ libcrypto \
+ libbspatch \
+ libbase \
+ libbz \
+ libz
+LOCAL_CFLAGS := \
+ -DZLIB_CONST \
+ -Werror
+include $(BUILD_STATIC_LIBRARY)
-LOCAL_CLANG := true
-LOCAL_SRC_FILES := bspatch.cpp imgpatch.cpp utils.cpp
+# libimgpatch (host static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ bspatch.cpp \
+ imgpatch.cpp
LOCAL_MODULE := libimgpatch
LOCAL_C_INCLUDES += $(RECOVERY_PATH)
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include \
+ $(commands_recovery_local_path)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES += libcrypto_static libbz libz
-
+LOCAL_STATIC_LIBRARIES := \
+ libcrypto \
+ libbspatch \
+ libbase \
+ libbz \
+ libz
+LOCAL_CFLAGS := \
+ -DZLIB_CONST \
+ -Werror
include $(BUILD_HOST_STATIC_LIBRARY)
-endif # HOST_OS == linux
+# libapplypatch_modes (static library)
+# ===============================
include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ applypatch_modes.cpp
+LOCAL_MODULE := libapplypatch_modes
+LOCAL_C_INCLUDES := $(commands_recovery_local_path)
+LOCAL_STATIC_LIBRARIES := \
+ libapplypatch \
+ libbase \
+ libedify \
+ libcrypto
+LOCAL_CFLAGS := -Werror
+include $(BUILD_STATIC_LIBRARY)
-LOCAL_CLANG := true
-LOCAL_SRC_FILES := main.cpp
+# applypatch (target executable)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := applypatch_main.cpp
LOCAL_MODULE := applypatch
LOCAL_C_INCLUDES += $(RECOVERY_PATH)
LOCAL_STATIC_LIBRARIES += libapplypatch libbase libotafault libmtdutils libcrypto_static libbz \
@@ -70,15 +143,78 @@
LOCAL_SHARED_LIBRARIES += libz libcutils libc
+LOCAL_C_INCLUDES := $(commands_recovery_local_path)
+LOCAL_STATIC_LIBRARIES := \
+ libapplypatch_modes \
+ libapplypatch \
+ libbase \
+ libedify \
+ libotafault \
+ libcrypto \
+ libbspatch \
+ libbz
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libz \
+ libcutils
+LOCAL_CFLAGS := -Werror
include $(BUILD_EXECUTABLE)
+libimgdiff_src_files := imgdiff.cpp
+
+# libbsdiff is compiled with -D_FILE_OFFSET_BITS=64.
+libimgdiff_cflags := \
+ -Werror \
+ -D_FILE_OFFSET_BITS=64
+
+libimgdiff_static_libraries := \
+ libbsdiff \
+ libdivsufsort \
+ libdivsufsort64 \
+ libziparchive \
+ libutils \
+ liblog \
+ libbase \
+ libz
+
+# libimgdiff (static library)
+# ===============================
include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ $(libimgdiff_src_files)
+LOCAL_MODULE := libimgdiff
+LOCAL_CFLAGS := \
+ $(libimgdiff_cflags)
+LOCAL_STATIC_LIBRARIES := \
+ $(libimgdiff_static_libraries)
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_STATIC_LIBRARY)
-LOCAL_CLANG := true
-LOCAL_SRC_FILES := imgdiff.cpp utils.cpp bsdiff.cpp
+# libimgdiff (host static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ $(libimgdiff_src_files)
+LOCAL_MODULE := libimgdiff
+LOCAL_CFLAGS := \
+ $(libimgdiff_cflags)
+LOCAL_STATIC_LIBRARIES := \
+ $(libimgdiff_static_libraries)
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# imgdiff (host static executable)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := imgdiff_main.cpp
LOCAL_MODULE := imgdiff
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_C_INCLUDES += external/zlib external/bzip2
-LOCAL_STATIC_LIBRARIES += libz libbz
-
+LOCAL_CFLAGS := -Werror
+LOCAL_STATIC_LIBRARIES := \
+ libimgdiff \
+ $(libimgdiff_static_libraries) \
+ libbz
include $(BUILD_HOST_EXECUTABLE)
diff --git a/applypatch/Makefile b/applypatch/Makefile
new file mode 100644
index 0000000..fb49843
--- /dev/null
+++ b/applypatch/Makefile
@@ -0,0 +1,33 @@
+# 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.
+
+# This file is for building imgdiff in Chrome OS.
+
+CPPFLAGS += -iquote.. -Iinclude
+CXXFLAGS += -std=c++11 -O3 -Wall -Werror
+LDLIBS += -lbz2 -lz
+
+.PHONY: all clean
+
+all: imgdiff libimgpatch.a
+
+clean:
+ rm -f *.o imgdiff libimgpatch.a
+
+imgdiff: imgdiff.o bsdiff.o utils.o
+ $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDLIBS) -o $@ $^
+
+libimgpatch.a utils.o: CXXFLAGS += -fPIC
+libimgpatch.a: imgpatch.o bspatch.o utils.o
+ ${AR} rcs $@ $^
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
index c2fe11b..54c37eb 100644
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "applypatch/applypatch.h"
+
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
@@ -27,72 +29,65 @@
#include <memory>
#include <string>
+#include <utility>
+#include <vector>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <android-base/strings.h>
+#include <openssl/sha.h>
-#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 int LoadPartitionContents(const std::string& filename, FileContents* file);
static ssize_t FileSink(const unsigned char* data, ssize_t len, void* token);
-static int GenerateTarget(FileContents* source_file,
- const Value* source_patch_value,
- FileContents* copy_file,
- const Value* copy_patch_value,
- const char* source_filename,
- const char* target_filename,
- const uint8_t target_sha1[SHA_DIGEST_LENGTH],
- size_t target_size,
- const Value* bonus_data);
+static int GenerateTarget(const FileContents& source_file, const std::unique_ptr<Value>& patch,
+ const std::string& target_filename,
+ const uint8_t target_sha1[SHA_DIGEST_LENGTH], const Value* bonus_data);
static bool mtd_partitions_scanned = false;
-// Read a file into memory; store the file contents and associated
-// metadata in *file.
-//
+// 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) {
- // A special 'filename' beginning with "MTD:" or "EMMC:" means to
- // load the contents of a partition.
- if (strncmp(filename, "MTD:", 4) == 0 ||
- strncmp(filename, "EMMC:", 5) == 0 ||
- strncmp(filename, "BML:", 4) == 0) {
- return LoadPartitionContents(filename, file);
- }
+ // A special 'filename' beginning with "MTD:" or "EMMC:" means to
+ // load the contents of a partition.
+ if (strncmp(filename, "MTD:", 4) == 0 ||
+ strncmp(filename, "EMMC:", 5) == 0 ||
+ strncmp(filename, "BML:", 4) == 0) {
+ return LoadPartitionContents(filename, file);
+ }
- if (stat(filename, &file->st) != 0) {
- printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
- return -1;
- }
+ if (stat(filename, &file->st) == -1) {
+ printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
+ return -1;
+ }
- std::vector<unsigned char> data(file->st.st_size);
- FILE* f = ota_fopen(filename, "rb");
- if (f == NULL) {
- printf("failed to open \"%s\": %s\n", filename, strerror(errno));
- return -1;
- }
+ std::vector<unsigned char> data(file->st.st_size);
+ unique_file f(ota_fopen(filename, "rb"));
+ if (!f) {
+ printf("failed to open \"%s\": %s\n", filename, strerror(errno));
+ return -1;
+ }
- 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;
- }
- ota_fclose(f);
- file->data = std::move(data);
- SHA1(file->data.data(), file->data.size(), file->sha1);
- return 0;
+ size_t bytes_read = ota_fread(data.data(), 1, data.size(), f.get());
+ if (bytes_read != data.size()) {
+ printf("short read of \"%s\" (%zu bytes of %zu)\n", filename, bytes_read, data.size());
+ return -1;
+ }
+ file->data = std::move(data);
+ SHA1(file->data.data(), file->data.size(), file->sha1);
+ return 0;
}
-// Load the contents of an MTD or EMMC partition into the provided
+// Load the contents of an EMMC partition into the provided
// FileContents. filename should be a string of the form
-// "MTD:<partition_name>:<size_1>:<sha1_1>:<size_2>:<sha1_2>:..." (or
-// "EMMC:<partition_device>:..."). The smallest size_n bytes for
+// "EMMC:<partition_device>:...". The smallest size_n bytes for
// which that prefix of the partition contents has the corresponding
// sha1 hash will be loaded. It is acceptable for a size value to be
// repeated with different sha1s. Will return 0 on success.
@@ -106,223 +101,166 @@
// to find one of those hashes.
enum PartitionType { MTD, EMMC };
-static int LoadPartitionContents(const char* filename, FileContents* file) {
- std::string copy(filename);
- std::vector<std::string> pieces = android::base::Split(copy, ":");
- if (pieces.size() < 4 || pieces.size() % 2 != 0) {
- printf("LoadPartitionContents called with bad filename (%s)\n", filename);
- return -1;
- }
+static int LoadPartitionContents(const std::string& filename, FileContents* file) {
+ std::vector<std::string> pieces = android::base::Split(filename, ":");
+ if (pieces.size() < 4 || pieces.size() % 2 != 0) {
+ printf("LoadPartitionContents called with bad filename \"%s\"\n", filename.c_str());
+ return -1;
+ }
- enum PartitionType type;
- if (pieces[0] == "MTD") {
- type = MTD;
- } else if (pieces[0] == "EMMC") {
- type = EMMC;
- } else if (pieces[0] == "BML") {
- type = EMMC;
- } else {
- printf("LoadPartitionContents called with bad filename (%s)\n", filename);
- return -1;
+ enum PartitionType type;
+ if (pieces[0] == "MTD") {
+ type = MTD;
+ } else if (pieces[0] == "EMMC") {
+ type = EMMC;
+ } else if (pieces[0] == "BML") {
+ type = EMMC;
+ } else {
+ printf("LoadPartitionContents called with bad filename (%s)\n", filename.c_str());
+ return -1;
+ }
+
+ size_t pair_count = (pieces.size() - 2) / 2; // # of (size, sha1) pairs in filename
+ std::vector<std::pair<size_t, std::string>> pairs;
+ for (size_t i = 0; i < pair_count; ++i) {
+ size_t size;
+ if (!android::base::ParseUint(pieces[i * 2 + 2], &size) || size == 0) {
+ printf("LoadPartitionContents called with bad size \"%s\"\n", pieces[i * 2 + 2].c_str());
+ return -1;
}
- const char* partition = pieces[1].c_str();
+ pairs.push_back({ size, pieces[i * 2 + 3] });
+ }
+
+ // Sort the pairs array so that they are in order of increasing size.
+ std::sort(pairs.begin(), pairs.end());
+
+ const char* partition = pieces[1].c_str();
+ unique_file dev(ota_fopen(partition, "rb"));
+ if (!dev) {
+ printf("failed to open emmc partition \"%s\": %s\n", partition, strerror(errno));
+ return -1;
+ }
+
+ SHA_CTX sha_ctx;
+ SHA1_Init(&sha_ctx);
+
+ // Allocate enough memory to hold the largest size.
+ std::vector<unsigned char> buffer(pairs[pair_count - 1].first);
+ unsigned char* buffer_ptr = buffer.data();
+ size_t buffer_size = 0; // # bytes read so far
+ bool found = false;
+
+ for (const auto& pair : pairs) {
+ size_t current_size = pair.first;
+ const std::string& current_sha1 = pair.second;
+
+ // 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 = current_size - buffer_size;
+ if (next > 0) {
+ size_t read = ota_fread(buffer_ptr, 1, next, dev.get());
+ if (next != read) {
+ printf("short read (%zu bytes of %zu) for partition \"%s\"\n", read, next, partition);
+ return -1;
+ }
+ SHA1_Update(&sha_ctx, buffer_ptr, read);
+ buffer_size += read;
+ buffer_ptr += read;
+ }
if (pieces[0] == "BML") {
- if (strcmp(partition, "boot") == 0) {
- partition = BOARD_BML_BOOT;
- } else if (strcmp(partition, "recovery") == 0) {
- partition = BOARD_BML_RECOVERY;
- }
+ if (strcmp(partition, "boot") == 0) {
+ partition = BOARD_BML_BOOT;
+ } else if (strcmp(partition, "recovery") == 0) {
+ partition = BOARD_BML_RECOVERY;
+ }
}
- size_t pairs = (pieces.size() - 2) / 2; // # of (size, sha1) pairs in filename
- std::vector<size_t> index(pairs);
- std::vector<size_t> size(pairs);
- std::vector<std::string> sha1sum(pairs);
+ // 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));
+ uint8_t sha_so_far[SHA_DIGEST_LENGTH];
+ SHA1_Final(sha_so_far, &temp_ctx);
- 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] = pieces[i*2+3].c_str();
- index[i] = i;
- }
-
- // 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: {
- if (!mtd_partitions_scanned) {
- mtd_scan_partitions();
- 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);
- return -1;
- }
-
- ctx = mtd_read_partition(mtd);
- if (ctx == NULL) {
- printf("failed to initialize read of mtd partition \"%s\"\n", partition);
- return -1;
- }
- break;
- }
-
- case EMMC:
- dev = ota_fopen(partition, "rb");
- if (dev == NULL) {
- printf("failed to open emmc partition \"%s\": %s\n", partition, strerror(errno));
- return -1;
- }
- }
-
- SHA_CTX sha_ctx;
- SHA1_Init(&sha_ctx);
uint8_t parsed_sha[SHA_DIGEST_LENGTH];
-
- // Allocate enough memory to hold the largest size.
- std::vector<unsigned char> data(size[index[pairs-1]]);
- char* p = reinterpret_cast<char*>(data.data());
- size_t data_size = 0; // # bytes read so far
- bool found = false;
-
- 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 = ota_fread(p, 1, next, dev);
- break;
- }
- if (next != read) {
- printf("short read (%zu bytes of %zu) for partition \"%s\"\n",
- read, next, partition);
- return -1;
- }
- 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));
- uint8_t sha_so_far[SHA_DIGEST_LENGTH];
- SHA1_Final(sha_so_far, &temp_ctx);
-
- 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_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]].c_str());
- found = true;
- break;
- }
+ if (ParseSha1(current_sha1.c_str(), parsed_sha) != 0) {
+ printf("failed to parse SHA-1 %s in %s\n", current_sha1.c_str(), filename.c_str());
+ return -1;
}
- switch (type) {
- case MTD:
- mtd_read_close(ctx);
- break;
-
- case EMMC:
- ota_fclose(dev);
- break;
+ 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-1 %s\n", current_size, current_sha1.c_str());
+ found = true;
+ break;
}
+ }
+ 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.c_str());
+ return -1;
+ }
- 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;
- }
+ SHA1_Final(file->sha1, &sha_ctx);
- SHA1_Final(file->sha1, &sha_ctx);
+ buffer.resize(buffer_size);
+ file->data = std::move(buffer);
+ // Fake some stat() info.
+ file->st.st_mode = 0644;
+ file->st.st_uid = 0;
+ file->st.st_gid = 0;
- 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;
-
- return 0;
+ return 0;
}
-
// 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 = 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));
- return -1;
- }
+ unique_fd fd(ota_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR));
+ if (fd == -1) {
+ printf("failed to open \"%s\" for write: %s\n", filename, strerror(errno));
+ return -1;
+ }
- ssize_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd);
- if (bytes_written != static_cast<ssize_t>(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 (ota_fsync(fd) != 0) {
- printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno));
- return -1;
- }
- if (ota_close(fd) != 0) {
- printf("close of \"%s\" failed: %s\n", filename, strerror(errno));
- return -1;
- }
+ ssize_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd);
+ if (bytes_written != static_cast<ssize_t>(file->data.size())) {
+ printf("short write of \"%s\" (%zd bytes of %zu): %s\n", filename, bytes_written,
+ file->data.size(), strerror(errno));
+ return -1;
+ }
+ if (ota_fsync(fd) != 0) {
+ printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno));
+ return -1;
+ }
+ if (ota_close(fd) != 0) {
+ printf("close of \"%s\" failed: %s\n", filename, strerror(errno));
+ return -1;
+ }
- if (chmod(filename, file->st.st_mode) != 0) {
- printf("chmod of \"%s\" failed: %s\n", filename, strerror(errno));
- return -1;
- }
- if (chown(filename, file->st.st_uid, file->st.st_gid) != 0) {
- printf("chown of \"%s\" failed: %s\n", filename, strerror(errno));
- return -1;
- }
+ if (chmod(filename, file->st.st_mode) != 0) {
+ printf("chmod of \"%s\" failed: %s\n", filename, strerror(errno));
+ return -1;
+ }
+ if (chown(filename, file->st.st_uid, file->st.st_gid) != 0) {
+ printf("chown of \"%s\" failed: %s\n", filename, strerror(errno));
+ return -1;
+ }
- return 0;
+ return 0;
}
// Write a memory buffer to 'target' partition, a string of the form
-// "MTD:<partition>[:...]" or "EMMC:<partition_device>[:...]". The target name
+// "EMMC:<partition_device>[:...]". 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) {
+int WriteToPartition(const unsigned char* data, size_t len, const std::string& target) {
std::string copy(target);
std::vector<std::string> pieces = android::base::Split(copy, ":");
if (pieces.size() < 2) {
- printf("WriteToPartition called with bad target (%s)\n", target);
+ printf("WriteToPartition called with bad target (%s)\n", target.c_str());
return -1;
}
@@ -334,7 +272,7 @@
} else if (pieces[0] == "BML") {
type = EMMC;
} else {
- printf("WriteToPartition called with bad target (%s)\n", target);
+ printf("WriteToPartition called with bad target (%s)\n", target.c_str());
return -1;
}
@@ -358,7 +296,7 @@
}
if (partition == NULL) {
- printf("bad partition target name \"%s\"\n", target);
+ printf("bad partition target name \"%s\"\n", target.c_str());
return -1;
}
@@ -404,7 +342,7 @@
case EMMC: {
size_t start = 0;
bool success = false;
- int fd = ota_open(partition, O_RDWR | O_SYNC);
+ unique_fd fd(ota_open(partition, O_RDWR | O_SYNC));
if (fd < 0) {
printf("failed to open %s: %s\n", partition, strerror(errno));
return -1;
@@ -434,7 +372,7 @@
printf("failed to close %s (%s)\n", partition, strerror(errno));
return -1;
}
- fd = ota_open(partition, O_RDONLY);
+ unique_fd fd(ota_open(partition, O_RDONLY));
if (fd < 0) {
printf("failed to reopen %s for verify (%s)\n", partition, strerror(errno));
return -1;
@@ -443,7 +381,7 @@
// Drop caches so our subsequent verification read
// won't just be reading the cache.
sync();
- int dc = ota_open("/proc/sys/vm/drop_caches", O_WRONLY);
+ unique_fd 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 {
@@ -513,7 +451,6 @@
return 0;
}
-
// Take a string 'str' of 40 hex digits and parse it into the 20
// byte array 'digest'. 'str' may contain only the digest or be of
// the form "<digest>:<anything>". Return 0 on success, -1 on any
@@ -546,52 +483,47 @@
// Search an array of sha1 strings for one matching the given sha1.
// Return the index of the match on success, or -1 if no match is
// found.
-int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str,
- int num_patches) {
+static int FindMatchingPatch(uint8_t* sha1, const std::vector<std::string>& patch_sha1_str) {
+ for (size_t i = 0; i < patch_sha1_str.size(); ++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_LENGTH) == 0) {
- return i;
- }
+ if (ParseSha1(patch_sha1_str[i].c_str(), patch_sha1) == 0 &&
+ memcmp(patch_sha1, sha1, SHA_DIGEST_LENGTH) == 0) {
+ return i;
}
- return -1;
+ }
+ return -1;
}
// 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) {
- FileContents file;
+int applypatch_check(const char* filename, const std::vector<std::string>& patch_sha1_str) {
+ FileContents file;
- // It's okay to specify no sha1s; the check will pass if the
- // LoadFileContents is successful. (Useful for reading
- // partitions, where the filename encodes the sha1s; no need to
- // check them twice.)
- if (LoadFileContents(filename, &file) != 0 ||
- (num_patches > 0 &&
- FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0)) {
- printf("file \"%s\" doesn't have any of expected "
- "sha1 sums; checking cache\n", filename);
+ // It's okay to specify no sha1s; the check will pass if the
+ // LoadFileContents is successful. (Useful for reading
+ // partitions, where the filename encodes the sha1s; no need to
+ // check them twice.)
+ if (LoadFileContents(filename, &file) != 0 ||
+ (!patch_sha1_str.empty() && FindMatchingPatch(file.sha1, patch_sha1_str) < 0)) {
+ printf("file \"%s\" doesn't have any of expected sha1 sums; checking cache\n", filename);
- // If the source file is missing or corrupted, it might be because
- // we were killed in the middle of patching it. A copy of it
- // should have been made in CACHE_TEMP_SOURCE. If that file
- // exists and matches the sha1 we're looking for, the check still
- // passes.
-
- if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
- printf("failed to load cache file\n");
- return 1;
- }
-
- if (FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0) {
- printf("cache bits don't match any sha1 for \"%s\"\n", filename);
- return 1;
- }
+ // If the source file is missing or corrupted, it might be because
+ // we were killed in the middle of patching it. A copy of it
+ // should have been made in CACHE_TEMP_SOURCE. If that file
+ // exists and matches the sha1 we're looking for, the check still
+ // passes.
+ if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
+ printf("failed to load cache file\n");
+ return 1;
}
- return 0;
+
+ if (FindMatchingPatch(file.sha1, patch_sha1_str) < 0) {
+ printf("cache bits don't match any sha1 for \"%s\"\n", filename);
+ return 1;
+ }
+ }
+ return 0;
}
int ShowLicenses() {
@@ -633,116 +565,97 @@
int CacheSizeCheck(size_t bytes) {
if (MakeFreeSpaceOnCache(bytes) < 0) {
- printf("unable to make %ld bytes available on /cache\n", (long)bytes);
+ printf("unable to make %zu bytes available on /cache\n", bytes);
return 1;
} else {
return 0;
}
}
-// 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
-// multiple times).
+// This function applies binary patches to EMMC target files in a way that is safe (the original
+// file is not touched until we have the desired replacement for it) and idempotent (it's okay to
+// run this program multiple times).
//
-// - if the sha1 hash of <target_filename> is <target_sha1_string>,
-// does nothing and exits successfully.
+// - If the SHA-1 hash of <target_filename> is <target_sha1_string>, does nothing and exits
+// successfully.
//
-// - otherwise, if the sha1 hash of <source_filename> is one of the
-// entries in <patch_sha1_str>, the corresponding patch from
-// <patch_data> (which must be a VAL_BLOB) is applied to produce a
-// new file (the type of patch is automatically detected from the
-// blob data). If that new file has sha1 hash <target_sha1_str>,
-// moves it to replace <target_filename>, and exits successfully.
-// Note that if <source_filename> and <target_filename> are not the
-// same, <source_filename> is NOT deleted on success.
-// <target_filename> may be the string "-" to mean "the same as
-// source_filename".
+// - Otherwise, if the SHA-1 hash of <source_filename> is one of the entries in <patch_sha1_str>,
+// the corresponding patch from <patch_data> (which must be a VAL_BLOB) is applied to produce a
+// new file (the type of patch is automatically detected from the blob data). If that new file
+// has SHA-1 hash <target_sha1_str>, moves it to replace <target_filename>, and exits
+// successfully. Note that if <source_filename> and <target_filename> are not the same,
+// <source_filename> is NOT deleted on success. <target_filename> may be the string "-" to mean
+// "the same as <source_filename>".
//
-// - otherwise, or if any error is encountered, exits with non-zero
-// status.
+// - Otherwise, or if any error is encountered, exits with non-zero status.
//
-// <source_filename> may refer to a partition to read the source data.
-// See the comments for the LoadPartitionContents() function above
-// for the format of such a filename.
+// <source_filename> must refer to an EMMC partition to read the source data. See the comments for
+// the LoadPartitionContents() function above for the format of such a filename. <target_size> has
+// become obsolete since we have dropped the support for patching non-EMMC targets (EMMC targets
+// have the size embedded in the filename).
+int applypatch(const char* source_filename, const char* target_filename,
+ const char* target_sha1_str, size_t target_size __unused,
+ const std::vector<std::string>& patch_sha1_str,
+ const std::vector<std::unique_ptr<Value>>& patch_data, const Value* bonus_data) {
+ printf("patch %s: ", source_filename);
-int applypatch(const char* source_filename,
- const char* target_filename,
- const char* target_sha1_str,
- size_t target_size,
- int num_patches,
- char** const patch_sha1_str,
- Value** patch_data,
- Value* bonus_data) {
- printf("patch %s: ", source_filename);
+ if (target_filename[0] == '-' && target_filename[1] == '\0') {
+ target_filename = source_filename;
+ }
- if (target_filename[0] == '-' && target_filename[1] == '\0') {
- target_filename = source_filename;
+ if (strncmp(target_filename, "EMMC:", 5) != 0) {
+ printf("Supporting patching EMMC targets only.\n");
+ return 1;
+ }
+
+ 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;
+ }
+
+ // We try to load the target file into the source_file object.
+ FileContents source_file;
+ if (LoadFileContents(target_filename, &source_file) == 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 %s\n", short_sha1(target_sha1).c_str());
+ return 0;
}
+ }
- 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;
+ 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 expected.
+ source_file.data.clear();
+ LoadFileContents(source_filename, &source_file);
+ }
+
+ if (!source_file.data.empty()) {
+ int to_use = FindMatchingPatch(source_file.sha1, patch_sha1_str);
+ if (to_use != -1) {
+ return GenerateTarget(source_file, patch_data[to_use], target_filename, target_sha1,
+ bonus_data);
}
+ }
- FileContents copy_file;
- FileContents source_file;
- const Value* source_patch_value = NULL;
- const Value* copy_patch_value = NULL;
+ printf("source file is bad; trying copy\n");
- // 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_LENGTH) == 0) {
- // The early-exit case: the patch was already applied, this file
- // has the desired hash, nothing for us to do.
- printf("already %s\n", short_sha1(target_sha1).c_str());
- return 0;
- }
- }
+ FileContents copy_file;
+ if (LoadFileContents(CACHE_TEMP_SOURCE, ©_file) < 0) {
+ printf("failed to read copy file\n");
+ return 1;
+ }
- 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.
- source_file.data.clear();
- LoadFileContents(source_filename, &source_file);
- }
+ int to_use = FindMatchingPatch(copy_file.sha1, patch_sha1_str);
+ if (to_use == -1) {
+ printf("copy file doesn't match source SHA-1s either\n");
+ return 1;
+ }
- 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) {
- source_file.data.clear();
- printf("source file is bad; trying copy\n");
-
- if (LoadFileContents(CACHE_TEMP_SOURCE, ©_file) < 0) {
- // fail.
- printf("failed to read copy file\n");
- return 1;
- }
-
- int to_use = FindMatchingPatch(copy_file.sha1, patch_sha1_str, num_patches);
- if (to_use >= 0) {
- copy_patch_value = patch_data[to_use];
- }
-
- if (copy_patch_value == NULL) {
- // fail.
- printf("copy file doesn't match source SHA-1s either\n");
- return 1;
- }
- }
-
- return GenerateTarget(&source_file, source_patch_value,
- ©_file, copy_patch_value,
- source_filename, target_filename,
- target_sha1, target_size, bonus_data);
+ return GenerateTarget(copy_file, patch_data[to_use], target_filename, target_sha1, bonus_data);
}
/*
@@ -754,281 +667,124 @@
*/
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);
+ 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;
- }
+ 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::string target_str(target_filename);
+ std::vector<std::string> pieces = android::base::Split(target_str, ":");
+ if (pieces.size() != 2 || pieces[0] != "EMMC") {
+ printf("invalid target name \"%s\"", target_filename);
+ return 1;
+ }
- std::vector<std::string> 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;
- }
+ // 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, ':');
+ FileContents source_file;
+ if (LoadPartitionContents(fullname, &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,
- const Value* source_patch_value,
- FileContents* copy_file,
- const Value* copy_patch_value,
- const char* source_filename,
- const char* target_filename,
- const uint8_t target_sha1[SHA_DIGEST_LENGTH],
- size_t target_size,
- const Value* bonus_data) {
- int retry = 1;
- SHA_CTX ctx;
- std::string memory_sink_str;
- FileContents* source_to_use;
- int made_copy = 0;
+static int GenerateTarget(const FileContents& source_file, const std::unique_ptr<Value>& patch,
+ const std::string& target_filename,
+ const uint8_t target_sha1[SHA_DIGEST_LENGTH], const Value* bonus_data) {
+ if (patch->type != VAL_BLOB) {
+ printf("patch is not a blob\n");
+ return 1;
+ }
- 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";
+ const char* header = &patch->data[0];
+ size_t header_bytes_read = patch->data.size();
+ bool use_bsdiff = false;
+ if (header_bytes_read >= 8 && memcmp(header, "BSDIFF40", 8) == 0) {
+ use_bsdiff = true;
+ } else if (header_bytes_read >= 8 && memcmp(header, "IMGDIFF2", 8) == 0) {
+ use_bsdiff = false;
+ } else {
+ printf("Unknown patch file format\n");
+ return 1;
+ }
- // 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().
- std::string target_fs = target_filename;
- auto slash_pos = target_fs.find('/', 1);
- if (slash_pos != std::string::npos) {
- target_fs.resize(slash_pos);
- }
+ CHECK(android::base::StartsWith(target_filename, "EMMC:"));
- 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;
- }
- 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;
- }
+ // We still write the original source to cache, in case the partition write is interrupted.
+ if (MakeFreeSpaceOnCache(source_file.data.size()) < 0) {
+ printf("not enough free space on /cache\n");
+ return 1;
+ }
+ if (SaveFileContents(CACHE_TEMP_SOURCE, &source_file) < 0) {
+ printf("failed to back up source file\n");
+ return 1;
+ }
- do {
- // Is there enough room in the target filesystem to hold the patched
- // file?
+ // We store the decoded output in memory.
+ SinkFn sink = MemorySink;
+ std::string memory_sink_str; // Don't need to reserve space.
+ void* token = &memory_sink_str;
- 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
- // /tmp, so instead we'll just assume that /tmp has enough
- // space to hold the file.
+ SHA_CTX ctx;
+ SHA1_Init(&ctx);
- // We still write the original source to cache, in case
- // the partition write is interrupted.
- if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) {
- printf("not enough free space on /cache\n");
- return 1;
- }
- if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
- printf("failed to back up source file\n");
- return 1;
- }
- made_copy = 1;
- retry = 0;
- } else {
- int enough_space = 0;
- if (retry > 0) {
- 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 %zu bytes; free space %zu bytes; retry %d; enough %d\n",
- target_size, free_space, retry, enough_space);
- }
- }
+ int result;
+ if (use_bsdiff) {
+ result = ApplyBSDiffPatch(source_file.data.data(), source_file.data.size(), patch.get(), 0,
+ sink, token, &ctx);
+ } else {
+ result = ApplyImagePatch(source_file.data.data(), source_file.data.size(), patch.get(), sink,
+ token, &ctx, bonus_data);
+ }
- if (!enough_space) {
- retry = 0;
- }
+ if (result != 0) {
+ printf("applying patch failed\n");
+ return 1;
+ }
- if (!enough_space && source_patch_value != NULL) {
- // Using the original source, but not enough free space. First
- // copy the source file to cache, then delete it from the original
- // location.
+ 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 %s\n", short_sha1(target_sha1).c_str());
+ }
- if (strncmp(source_filename, "MTD:", 4) == 0 ||
- strncmp(source_filename, "EMMC:", 5) == 0 ||
- strncmp(source_filename, "BML:", 4) == 0) {
- // 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");
- return 1;
- }
+ // Write back the temp file to the partition.
+ if (WriteToPartition(reinterpret_cast<const unsigned char*>(memory_sink_str.c_str()),
+ memory_sink_str.size(), target_filename) != 0) {
+ printf("write of patched data to %s failed\n", target_filename.c_str());
+ return 1;
+ }
- if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) {
- printf("not enough free space on /cache\n");
- return 1;
- }
+ // Delete the backup copy of the source.
+ unlink(CACHE_TEMP_SOURCE);
- if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
- printf("failed to back up source file\n");
- return 1;
- }
- made_copy = 1;
- unlink(source_filename);
-
- size_t free_space = FreeSpaceForFile(target_fs.c_str());
- printf("(now %zu bytes free for target) ", free_space);
- }
- }
-
-
- SinkFn sink = NULL;
- void* token = NULL;
-
- int output_fd = -1;
- if (target_is_partition) {
- // We store the decoded output in memory.
- sink = MemorySink;
- token = &memory_sink_str;
- } else {
- // We write the decoded output to "<tgt-file>.patch".
- 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_fd;
- }
-
-
- SHA1_Init(&ctx);
-
- int result;
- if (use_bsdiff) {
- result = ApplyBSDiffPatch(source_to_use->data.data(), source_to_use->data.size(),
- patch, 0, sink, token, &ctx);
- } else {
- result = ApplyImagePatch(source_to_use->data.data(), source_to_use->data.size(),
- patch, sink, token, &ctx, bonus_data);
- }
-
- 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 (ota_close(output_fd) != 0) {
- printf("failed to close file \"%s\" (%s)\n", tmp_target_filename.c_str(),
- strerror(errno));
- result = 1;
- }
- }
-
- if (result != 0) {
- if (retry == 0) {
- printf("applying patch failed\n");
- return result != 0;
- } else {
- printf("applying patch failed; retrying\n");
- }
- if (!target_is_partition) {
- unlink(tmp_target_filename.c_str());
- }
- } else {
- // succeeded; no need to retry
- break;
- }
- } while (retry-- > 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 %s\n", short_sha1(target_sha1).c_str());
- }
-
- if (target_is_partition) {
- // Copy the temp file to the partition.
- if (WriteToPartition(reinterpret_cast<const unsigned char*>(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;
- }
- } else {
- // Give the .patch file the same owner, group, and mode of the
- // original source file.
- 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(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(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);
- }
-
- // Success!
- return 0;
+ // Success!
+ return 0;
}
diff --git a/applypatch/applypatch_main.cpp b/applypatch/applypatch_main.cpp
new file mode 100644
index 0000000..197077c
--- /dev/null
+++ b/applypatch/applypatch_main.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "applypatch_modes.h"
+
+// This program (applypatch) applies binary patches to files in a way that
+// is safe (the original file is not touched until we have the desired
+// replacement for it) and idempotent (it's okay to run this program
+// multiple times).
+//
+// See the comments to applypatch_modes() function.
+
+int main(int argc, char** argv) {
+ return applypatch_modes(argc, const_cast<const char**>(argv));
+}
diff --git a/applypatch/main.cpp b/applypatch/applypatch_modes.cpp
similarity index 65%
rename from applypatch/main.cpp
rename to applypatch/applypatch_modes.cpp
index 9013760..7b191a8 100644
--- a/applypatch/main.cpp
+++ b/applypatch/applypatch_modes.cpp
@@ -14,61 +14,72 @@
* limitations under the License.
*/
+#include "applypatch_modes.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <memory>
+#include <string>
#include <vector>
-#include "applypatch.h"
-#include "edify/expr.h"
-#include "openssl/sha.h"
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <openssl/sha.h>
-static int CheckMode(int argc, char** argv) {
+#include "applypatch/applypatch.h"
+#include "edify/expr.h"
+
+static int CheckMode(int argc, const char** argv) {
if (argc < 3) {
return 2;
}
- return applypatch_check(argv[2], argc-3, argv+3);
+ std::vector<std::string> sha1;
+ for (int i = 3; i < argc; i++) {
+ sha1.push_back(argv[i]);
+ }
+
+ return applypatch_check(argv[2], sha1);
}
-static int SpaceMode(int argc, char** argv) {
+static int SpaceMode(int argc, const char** argv) {
if (argc != 3) {
return 2;
}
- char* endptr;
- size_t bytes = strtol(argv[2], &endptr, 10);
- if (bytes == 0 && endptr == argv[2]) {
+
+ size_t bytes;
+ if (!android::base::ParseUint(argv[2], &bytes) || bytes == 0) {
printf("can't parse \"%s\" as byte count\n\n", argv[2]);
return 1;
}
return CacheSizeCheck(bytes);
}
-// Parse arguments (which should be of the form "<sha1>:<filename>"
-// into the new parallel arrays *sha1s and *files.Returns true on
-// success.
-static bool ParsePatchArgs(int argc, char** argv, std::vector<char*>* sha1s,
+// Parse arguments (which should be of the form "<sha1>:<filename>" into the
+// new parallel arrays *sha1s and *files. Returns true on success.
+static bool ParsePatchArgs(int argc, const char** argv, std::vector<std::string>* sha1s,
std::vector<FileContents>* files) {
- uint8_t digest[SHA_DIGEST_LENGTH];
-
+ if (sha1s == nullptr) {
+ return false;
+ }
for (int i = 0; i < argc; ++i) {
- char* colon = strchr(argv[i], ':');
- if (colon == nullptr) {
- printf("no ':' in patch argument \"%s\"\n", argv[i]);
+ std::vector<std::string> pieces = android::base::Split(argv[i], ":");
+ if (pieces.size() != 2) {
+ printf("failed to parse patch argument \"%s\"\n", argv[i]);
return false;
}
- *colon = '\0';
- ++colon;
- if (ParseSha1(argv[i], digest) != 0) {
+
+ uint8_t digest[SHA_DIGEST_LENGTH];
+ if (ParseSha1(pieces[0].c_str(), digest) != 0) {
printf("failed to parse sha1 \"%s\"\n", argv[i]);
return false;
}
- sha1s->push_back(argv[i]);
+ sha1s->push_back(pieces[0]);
FileContents fc;
- if (LoadFileContents(colon, &fc) != 0) {
+ if (LoadFileContents(pieces[1].c_str(), &fc) != 0) {
return false;
}
files->push_back(std::move(fc));
@@ -81,20 +92,17 @@
return applypatch_flash(src_filename, tgt_filename, tgt_sha1, tgt_size);
}
-static int PatchMode(int argc, char** argv) {
+static int PatchMode(int argc, const char** argv) {
FileContents bonusFc;
- Value bonusValue;
- Value* bonus = nullptr;
+ Value bonus(VAL_INVALID, "");
if (argc >= 3 && strcmp(argv[1], "-b") == 0) {
if (LoadFileContents(argv[2], &bonusFc) != 0) {
printf("failed to load bonus file %s\n", argv[2]);
return 1;
}
- bonus = &bonusValue;
- bonus->type = VAL_BLOB;
- bonus->size = bonusFc.data.size();
- bonus->data = reinterpret_cast<char*>(bonusFc.data.data());
+ bonus.type = VAL_BLOB;
+ bonus.data = std::string(bonusFc.data.cbegin(), bonusFc.data.cend());
argc -= 2;
argv += 2;
}
@@ -103,42 +111,38 @@
return 2;
}
- char* endptr;
- size_t target_size = strtol(argv[4], &endptr, 10);
- if (target_size == 0 && endptr == argv[4]) {
+ size_t target_size;
+ if (!android::base::ParseUint(argv[4], &target_size) || target_size == 0) {
printf("can't parse \"%s\" as byte count\n\n", argv[4]);
return 1;
}
// If no <src-sha1>:<patch> is provided, it is in flash mode.
if (argc == 5) {
- if (bonus != nullptr) {
+ if (bonus.type != VAL_INVALID) {
printf("bonus file not supported in flash mode\n");
return 1;
}
return FlashMode(argv[1], argv[2], argv[3], target_size);
}
- std::vector<char*> sha1s;
+
+ std::vector<std::string> sha1s;
std::vector<FileContents> files;
if (!ParsePatchArgs(argc-5, argv+5, &sha1s, &files)) {
printf("failed to parse patch args\n");
return 1;
}
- std::vector<Value> patches(files.size());
- std::vector<Value*> patch_ptrs(files.size());
+
+ std::vector<std::unique_ptr<Value>> patches;
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<char*>(files[i].data.data());
- patch_ptrs[i] = &patches[i];
+ patches.push_back(std::make_unique<Value>(
+ VAL_BLOB, std::string(files[i].data.cbegin(), files[i].data.cend())));
}
- return applypatch(argv[1], argv[2], argv[3], target_size,
- patch_ptrs.size(), sha1s.data(),
- patch_ptrs.data(), bonus);
+ return applypatch(argv[1], argv[2], argv[3], target_size, sha1s, patches, &bonus);
}
-// This program applies binary patches to files in a way that is safe
-// (the original file is not touched until we have the desired
+// This program (applypatch) applies binary patches to files in a way that
+// is safe (the original file is not touched until we have the desired
// replacement for it) and idempotent (it's okay to run this program
// multiple times).
//
@@ -160,11 +164,11 @@
// - otherwise, or if any error is encountered, exits with non-zero
// status.
//
-// <src-file> (or <file> in check mode) may refer to an MTD partition
+// <src-file> (or <file> in check mode) may refer to an EMMC partition
// to read the source data. See the comments for the
-// LoadMTDContents() function above for the format of such a filename.
+// LoadPartitionContents() function for the format of such a filename.
-int main(int argc, char** argv) {
+int applypatch_modes(int argc, const char** argv) {
if (argc < 2) {
usage:
printf(
@@ -175,8 +179,8 @@
" or %s -l\n"
"\n"
"Filenames may be of the form\n"
- " MTD:<partition>:<len_1>:<sha1_1>:<len_2>:<sha1_2>:...\n"
- "to specify reading from or writing to an MTD partition.\n\n",
+ " EMMC:<partition>:<len_1>:<sha1_1>:<len_2>:<sha1_2>:...\n"
+ "to specify reading from or writing to an EMMC partition.\n\n",
argv[0], argv[0], argv[0], argv[0]);
return 2;
}
diff --git a/updater/install.h b/applypatch/applypatch_modes.h
similarity index 64%
copy from updater/install.h
copy to applypatch/applypatch_modes.h
index 70e3434..3d9d08d 100644
--- a/updater/install.h
+++ b/applypatch/applypatch_modes.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Android Open Source Project
+ * 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.
@@ -14,14 +14,9 @@
* limitations under the License.
*/
-#ifndef _UPDATER_INSTALL_H_
-#define _UPDATER_INSTALL_H_
+#ifndef _APPLYPATCH_MODES_H
+#define _APPLYPATCH_MODES_H
-void RegisterInstallFunctions();
+int applypatch_modes(int argc, const char** argv);
-// uiPrintf function prints msg to screen as well as logs
-void uiPrintf(State* state, const char* format, ...);
-
-static int make_parents(char* name);
-
-#endif
+#endif // _APPLYPATCH_MODES_H
diff --git a/applypatch/bsdiff.cpp b/applypatch/bsdiff.cpp
deleted file mode 100644
index 55dbe5c..0000000
--- a/applypatch/bsdiff.cpp
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Most of this code comes from bsdiff.c from the bsdiff-4.3
- * distribution, which is:
- */
-
-/*-
- * Copyright 2003-2005 Colin Percival
- * All rights reserved
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted providing that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <sys/types.h>
-
-#include <bzlib.h>
-#include <err.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#define MIN(x,y) (((x)<(y)) ? (x) : (y))
-
-static void split(off_t *I,off_t *V,off_t start,off_t len,off_t h)
-{
- off_t i,j,k,x,tmp,jj,kk;
-
- if(len<16) {
- for(k=start;k<start+len;k+=j) {
- j=1;x=V[I[k]+h];
- for(i=1;k+i<start+len;i++) {
- if(V[I[k+i]+h]<x) {
- x=V[I[k+i]+h];
- j=0;
- };
- if(V[I[k+i]+h]==x) {
- tmp=I[k+j];I[k+j]=I[k+i];I[k+i]=tmp;
- j++;
- };
- };
- for(i=0;i<j;i++) V[I[k+i]]=k+j-1;
- if(j==1) I[k]=-1;
- };
- return;
- };
-
- x=V[I[start+len/2]+h];
- jj=0;kk=0;
- for(i=start;i<start+len;i++) {
- if(V[I[i]+h]<x) jj++;
- if(V[I[i]+h]==x) kk++;
- };
- jj+=start;kk+=jj;
-
- i=start;j=0;k=0;
- while(i<jj) {
- if(V[I[i]+h]<x) {
- i++;
- } else if(V[I[i]+h]==x) {
- tmp=I[i];I[i]=I[jj+j];I[jj+j]=tmp;
- j++;
- } else {
- tmp=I[i];I[i]=I[kk+k];I[kk+k]=tmp;
- k++;
- };
- };
-
- while(jj+j<kk) {
- if(V[I[jj+j]+h]==x) {
- j++;
- } else {
- tmp=I[jj+j];I[jj+j]=I[kk+k];I[kk+k]=tmp;
- k++;
- };
- };
-
- if(jj>start) split(I,V,start,jj-start,h);
-
- for(i=0;i<kk-jj;i++) V[I[jj+i]]=kk-1;
- if(jj==kk-1) I[jj]=-1;
-
- if(start+len>kk) split(I,V,kk,start+len-kk,h);
-}
-
-static void qsufsort(off_t *I,off_t *V,u_char *old,off_t oldsize)
-{
- off_t buckets[256];
- off_t i,h,len;
-
- for(i=0;i<256;i++) buckets[i]=0;
- for(i=0;i<oldsize;i++) buckets[old[i]]++;
- for(i=1;i<256;i++) buckets[i]+=buckets[i-1];
- for(i=255;i>0;i--) buckets[i]=buckets[i-1];
- buckets[0]=0;
-
- for(i=0;i<oldsize;i++) I[++buckets[old[i]]]=i;
- I[0]=oldsize;
- for(i=0;i<oldsize;i++) V[i]=buckets[old[i]];
- V[oldsize]=0;
- for(i=1;i<256;i++) if(buckets[i]==buckets[i-1]+1) I[buckets[i]]=-1;
- I[0]=-1;
-
- for(h=1;I[0]!=-(oldsize+1);h+=h) {
- len=0;
- for(i=0;i<oldsize+1;) {
- if(I[i]<0) {
- len-=I[i];
- i-=I[i];
- } else {
- if(len) I[i-len]=-len;
- len=V[I[i]]+1-i;
- split(I,V,i,len,h);
- i+=len;
- len=0;
- };
- };
- if(len) I[i-len]=-len;
- };
-
- for(i=0;i<oldsize+1;i++) I[V[i]]=i;
-}
-
-static off_t matchlen(u_char *olddata,off_t oldsize,u_char *newdata,off_t newsize)
-{
- off_t i;
-
- for(i=0;(i<oldsize)&&(i<newsize);i++)
- if(olddata[i]!=newdata[i]) break;
-
- return i;
-}
-
-static off_t search(off_t *I,u_char *old,off_t oldsize,
- u_char *newdata,off_t newsize,off_t st,off_t en,off_t *pos)
-{
- off_t x,y;
-
- if(en-st<2) {
- x=matchlen(old+I[st],oldsize-I[st],newdata,newsize);
- y=matchlen(old+I[en],oldsize-I[en],newdata,newsize);
-
- if(x>y) {
- *pos=I[st];
- return x;
- } else {
- *pos=I[en];
- return y;
- }
- };
-
- x=st+(en-st)/2;
- 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,newdata,newsize,st,x,pos);
- };
-}
-
-static void offtout(off_t x,u_char *buf)
-{
- off_t y;
-
- if(x<0) y=-x; else y=x;
-
- buf[0]=y%256;y-=buf[0];
- y=y/256;buf[1]=y%256;y-=buf[1];
- y=y/256;buf[2]=y%256;y-=buf[2];
- y=y/256;buf[3]=y%256;y-=buf[3];
- y=y/256;buf[4]=y%256;y-=buf[4];
- y=y/256;buf[5]=y%256;y-=buf[5];
- y=y/256;buf[6]=y%256;y-=buf[6];
- y=y/256;buf[7]=y%256;
-
- if(x<0) buf[7]|=0x80;
-}
-
-// This is main() from bsdiff.c, with the following changes:
-//
-// - 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
-// pointer to *I, which can be NULL. This way if we call
-// 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* newdata, off_t newsize,
- const char* patch_filename)
-{
- int fd;
- off_t *I;
- off_t scan,pos,len;
- off_t lastscan,lastpos,lastoffset;
- off_t oldscore,scsc;
- off_t s,Sf,lenf,Sb,lenb;
- off_t overlap,Ss,lens;
- off_t i;
- off_t dblen,eblen;
- u_char *db,*eb;
- u_char buf[8];
- u_char header[32];
- FILE * pf;
- BZFILE * pfbz2;
- int bz2err;
-
- if (*IP == NULL) {
- off_t* V;
- *IP = reinterpret_cast<off_t*>(malloc((oldsize+1) * sizeof(off_t)));
- V = reinterpret_cast<off_t*>(malloc((oldsize+1) * sizeof(off_t)));
- qsufsort(*IP, V, old, oldsize);
- free(V);
- }
- I = *IP;
-
- if(((db=reinterpret_cast<u_char*>(malloc(newsize+1)))==NULL) ||
- ((eb=reinterpret_cast<u_char*>(malloc(newsize+1)))==NULL)) err(1,NULL);
- dblen=0;
- eblen=0;
-
- /* Create the patch file */
- if ((pf = fopen(patch_filename, "w")) == NULL)
- err(1, "%s", patch_filename);
-
- /* Header is
- 0 8 "BSDIFF40"
- 8 8 length of bzip2ed ctrl block
- 16 8 length of bzip2ed diff block
- 24 8 length of new file */
- /* File is
- 0 32 Header
- 32 ?? Bzip2ed ctrl block
- ?? ?? Bzip2ed diff block
- ?? ?? Bzip2ed extra block */
- memcpy(header,"BSDIFF40",8);
- offtout(0, header + 8);
- offtout(0, header + 16);
- offtout(newsize, header + 24);
- if (fwrite(header, 32, 1, pf) != 1)
- err(1, "fwrite(%s)", patch_filename);
-
- /* Compute the differences, writing ctrl as we go */
- if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
- errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
- scan=0;len=0;
- lastscan=0;lastpos=0;lastoffset=0;
- while(scan<newsize) {
- oldscore=0;
-
- for(scsc=scan+=len;scan<newsize;scan++) {
- len=search(I,old,oldsize,newdata+scan,newsize-scan,
- 0,oldsize,&pos);
-
- for(;scsc<scan+len;scsc++)
- if((scsc+lastoffset<oldsize) &&
- (old[scsc+lastoffset] == newdata[scsc]))
- oldscore++;
-
- if(((len==oldscore) && (len!=0)) ||
- (len>oldscore+8)) break;
-
- if((scan+lastoffset<oldsize) &&
- (old[scan+lastoffset] == newdata[scan]))
- oldscore--;
- };
-
- if((len!=oldscore) || (scan==newsize)) {
- s=0;Sf=0;lenf=0;
- for(i=0;(lastscan+i<scan)&&(lastpos+i<oldsize);) {
- if(old[lastpos+i]==newdata[lastscan+i]) s++;
- i++;
- if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; };
- };
-
- lenb=0;
- if(scan<newsize) {
- s=0;Sb=0;
- for(i=1;(scan>=lastscan+i)&&(pos>=i);i++) {
- if(old[pos-i]==newdata[scan-i]) s++;
- if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; };
- };
- };
-
- if(lastscan+lenf>scan-lenb) {
- overlap=(lastscan+lenf)-(scan-lenb);
- s=0;Ss=0;lens=0;
- for(i=0;i<overlap;i++) {
- if(newdata[lastscan+lenf-overlap+i]==
- old[lastpos+lenf-overlap+i]) s++;
- if(newdata[scan-lenb+i]==
- old[pos-lenb+i]) s--;
- if(s>Ss) { Ss=s; lens=i+1; };
- };
-
- lenf+=lens-overlap;
- lenb-=lens;
- };
-
- for(i=0;i<lenf;i++)
- db[dblen+i]=newdata[lastscan+i]-old[lastpos+i];
- for(i=0;i<(scan-lenb)-(lastscan+lenf);i++)
- eb[eblen+i]=newdata[lastscan+lenf+i];
-
- dblen+=lenf;
- eblen+=(scan-lenb)-(lastscan+lenf);
-
- offtout(lenf,buf);
- BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
- if (bz2err != BZ_OK)
- errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
-
- offtout((scan-lenb)-(lastscan+lenf),buf);
- BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
- if (bz2err != BZ_OK)
- errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
-
- offtout((pos-lenb)-(lastpos+lenf),buf);
- BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
- if (bz2err != BZ_OK)
- errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
-
- lastscan=scan-lenb;
- lastpos=pos-lenb;
- lastoffset=pos-scan;
- };
- };
- BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
- if (bz2err != BZ_OK)
- errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
-
- /* Compute size of compressed ctrl data */
- if ((len = ftello(pf)) == -1)
- err(1, "ftello");
- offtout(len-32, header + 8);
-
- /* Write compressed diff data */
- if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
- errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
- BZ2_bzWrite(&bz2err, pfbz2, db, dblen);
- if (bz2err != BZ_OK)
- errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
- BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
- if (bz2err != BZ_OK)
- errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
-
- /* Compute size of compressed diff data */
- if ((newsize = ftello(pf)) == -1)
- err(1, "ftello");
- offtout(newsize - len, header + 16);
-
- /* Write compressed extra data */
- if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
- errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
- BZ2_bzWrite(&bz2err, pfbz2, eb, eblen);
- if (bz2err != BZ_OK)
- errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
- BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
- if (bz2err != BZ_OK)
- errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
-
- /* Seek to the beginning, write the header, and close the file */
- if (fseeko(pf, 0, SEEK_SET))
- err(1, "fseeko");
- if (fwrite(header, 32, 1, pf) != 1)
- err(1, "fwrite(%s)", patch_filename);
- if (fclose(pf))
- err(1, "fclose");
-
- /* Free the memory we used */
- free(db);
- free(eb);
-
- return 0;
-}
diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp
index ebb55f1..9920c2b 100644
--- a/applypatch/bspatch.cpp
+++ b/applypatch/bspatch.cpp
@@ -21,16 +21,12 @@
// notice.
#include <stdio.h>
-#include <sys/stat.h>
#include <sys/types.h>
-#include <errno.h>
-#include <unistd.h>
-#include <string.h>
-#include <bzlib.h>
+#include <bspatch.h>
+#include "applypatch/applypatch.h"
#include "openssl/sha.h"
-#include "applypatch.h"
void ShowBSDiffLicense() {
puts("The bsdiff library used herein is:\n"
@@ -64,184 +60,25 @@
);
}
-static off_t offtin(u_char *buf)
-{
- off_t y;
-
- y=buf[7]&0x7F;
- y=y*256;y+=buf[6];
- y=y*256;y+=buf[5];
- y=y*256;y+=buf[4];
- y=y*256;y+=buf[3];
- y=y*256;y+=buf[2];
- y=y*256;y+=buf[1];
- y=y*256;y+=buf[0];
-
- if(buf[7]&0x80) y=-y;
-
- return y;
+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) {
+ auto sha_sink = [&](const uint8_t* data, size_t len) {
+ len = sink(data, len, token);
+ if (ctx) SHA1_Update(ctx, data, len);
+ return len;
+ };
+ return bsdiff::bspatch(old_data, old_size,
+ reinterpret_cast<const uint8_t*>(&patch->data[patch_offset]),
+ patch->data.size(), sha_sink);
}
-int FillBuffer(unsigned char* buffer, int size, bz_stream* stream) {
- stream->next_out = (char*)buffer;
- stream->avail_out = size;
- while (stream->avail_out > 0) {
- int bzerr = BZ2_bzDecompress(stream);
- if (bzerr != BZ_OK && bzerr != BZ_STREAM_END) {
- printf("bz error %d decompressing\n", bzerr);
- return -1;
- }
- if (stream->avail_out > 0) {
- printf("need %d more bytes\n", stream->avail_out);
- }
- }
- return 0;
-}
-
-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) {
-
- std::vector<unsigned char> new_data;
- if (ApplyBSDiffPatchMem(old_data, old_size, patch, patch_offset, &new_data) != 0) {
- return -1;
- }
-
- if (sink(new_data.data(), new_data.size(), token) < static_cast<ssize_t>(new_data.size())) {
- printf("short write of output: %d (%s)\n", errno, strerror(errno));
- return 1;
- }
- 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,
- std::vector<unsigned char>* new_data) {
- // Patch data format:
- // 0 8 "BSDIFF40"
- // 8 8 X
- // 16 8 Y
- // 24 8 sizeof(newfile)
- // 32 X bzip2(control block)
- // 32+X Y bzip2(diff block)
- // 32+X+Y ??? bzip2(extra block)
- // with control block a set of triples (x,y,z) meaning "add x bytes
- // from oldfile to x bytes from the diff block; copy y bytes from the
- // extra block; seek forwards in oldfile by z bytes".
-
- unsigned char* header = (unsigned char*) patch->data + patch_offset;
- if (memcmp(header, "BSDIFF40", 8) != 0) {
- printf("corrupt bsdiff patch file header (magic number)\n");
- return 1;
- }
-
- ssize_t ctrl_len, data_len, new_size;
- ctrl_len = offtin(header+8);
- data_len = offtin(header+16);
- new_size = offtin(header+24);
-
- if (ctrl_len < 0 || data_len < 0 || new_size < 0) {
- printf("corrupt patch file header (data lengths)\n");
- return 1;
- }
-
- int bzerr;
-
- bz_stream cstream;
- cstream.next_in = patch->data + patch_offset + 32;
- cstream.avail_in = ctrl_len;
- cstream.bzalloc = NULL;
- cstream.bzfree = NULL;
- cstream.opaque = NULL;
- if ((bzerr = BZ2_bzDecompressInit(&cstream, 0, 0)) != BZ_OK) {
- printf("failed to bzinit control stream (%d)\n", bzerr);
- }
-
- bz_stream dstream;
- dstream.next_in = patch->data + patch_offset + 32 + ctrl_len;
- dstream.avail_in = data_len;
- dstream.bzalloc = NULL;
- dstream.bzfree = NULL;
- dstream.opaque = NULL;
- if ((bzerr = BZ2_bzDecompressInit(&dstream, 0, 0)) != BZ_OK) {
- printf("failed to bzinit diff stream (%d)\n", bzerr);
- }
-
- bz_stream estream;
- estream.next_in = patch->data + patch_offset + 32 + ctrl_len + data_len;
- estream.avail_in = patch->size - (patch_offset + 32 + ctrl_len + data_len);
- estream.bzalloc = NULL;
- estream.bzfree = NULL;
- estream.opaque = NULL;
- if ((bzerr = BZ2_bzDecompressInit(&estream, 0, 0)) != BZ_OK) {
- printf("failed to bzinit extra stream (%d)\n", bzerr);
- }
-
- 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) {
- // Read control data
- if (FillBuffer(buf, 24, &cstream) != 0) {
- printf("error while reading control stream\n");
- return 1;
- }
- ctrl[0] = offtin(buf);
- ctrl[1] = offtin(buf+8);
- ctrl[2] = offtin(buf+16);
-
- if (ctrl[0] < 0 || ctrl[1] < 0) {
- printf("corrupt patch (negative byte counts)\n");
- return 1;
- }
-
- // Sanity check
- if (newpos + ctrl[0] > new_size) {
- printf("corrupt patch (new file overrun)\n");
- return 1;
- }
-
- // Read diff string
- if (FillBuffer(new_data->data() + newpos, ctrl[0], &dstream) != 0) {
- printf("error while reading diff stream\n");
- return 1;
- }
-
- // Add old data to diff string
- for (i = 0; i < ctrl[0]; ++i) {
- if ((oldpos+i >= 0) && (oldpos+i < old_size)) {
- (*new_data)[newpos+i] += old_data[oldpos+i];
- }
- }
-
- // Adjust pointers
- newpos += ctrl[0];
- oldpos += ctrl[0];
-
- // Sanity check
- if (newpos + ctrl[1] > new_size) {
- printf("corrupt patch (new file overrun)\n");
- return 1;
- }
-
- // Read extra string
- if (FillBuffer(new_data->data() + newpos, ctrl[1], &estream) != 0) {
- printf("error while reading extra stream\n");
- return 1;
- }
-
- // Adjust pointers
- newpos += ctrl[1];
- oldpos += ctrl[2];
- }
-
- BZ2_bzDecompressEnd(&cstream);
- BZ2_bzDecompressEnd(&dstream);
- BZ2_bzDecompressEnd(&estream);
- return 0;
+int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, const Value* patch,
+ ssize_t patch_offset, std::vector<unsigned char>* new_data) {
+ auto vector_sink = [new_data](const uint8_t* data, size_t len) {
+ new_data->insert(new_data->end(), data, data + len);
+ return len;
+ };
+ return bsdiff::bspatch(old_data, old_size,
+ reinterpret_cast<const uint8_t*>(&patch->data[patch_offset]),
+ patch->data.size(), vector_sink);
}
diff --git a/applypatch/freecache.cpp b/applypatch/freecache.cpp
index c84f427..331cae2 100644
--- a/applypatch/freecache.cpp
+++ b/applypatch/freecache.cpp
@@ -32,7 +32,7 @@
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
-#include "applypatch.h"
+#include "applypatch/applypatch.h"
static int EliminateOpenFiles(std::set<std::string>* files) {
std::unique_ptr<DIR, decltype(&closedir)> d(opendir("/proc"), closedir);
diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp
index f22502e..41d73ab 100644
--- a/applypatch/imgdiff.cpp
+++ b/applypatch/imgdiff.cpp
@@ -121,618 +121,687 @@
* information that is stored on the system partition.
*/
+#include "applypatch/imgdiff.h"
+
#include <errno.h>
-#include <inttypes.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
-#include <unistd.h>
#include <sys/types.h>
+#include <unistd.h>
-#include "zlib.h"
-#include "imgdiff.h"
-#include "utils.h"
+#include <algorithm>
+#include <string>
+#include <vector>
-typedef struct {
- int type; // CHUNK_NORMAL, CHUNK_DEFLATE
- size_t start; // offset of chunk in original image file
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/memory.h>
+#include <android-base/unique_fd.h>
+#include <ziparchive/zip_archive.h>
- size_t len;
- unsigned char* data; // data to be patched (uncompressed, for deflate chunks)
+#include <bsdiff.h>
+#include <zlib.h>
- size_t source_start;
- size_t source_len;
+using android::base::get_unaligned;
- off_t* I; // used by bsdiff
+static constexpr auto BUFFER_SIZE = 0x8000;
+
+// If we use this function to write the offset and length (type size_t), their values should not
+// exceed 2^63; because the signed bit will be casted away.
+static inline bool Write8(int fd, int64_t value) {
+ return android::base::WriteFully(fd, &value, sizeof(int64_t));
+}
+
+// Similarly, the value should not exceed 2^31 if we are casting from size_t (e.g. target chunk
+// size).
+static inline bool Write4(int fd, int32_t value) {
+ return android::base::WriteFully(fd, &value, sizeof(int32_t));
+}
+
+class ImageChunk {
+ public:
+ static constexpr auto WINDOWBITS = -15; // 32kb window; negative to indicate a raw stream.
+ static constexpr auto MEMLEVEL = 8; // the default value.
+ static constexpr auto METHOD = Z_DEFLATED;
+ static constexpr auto STRATEGY = Z_DEFAULT_STRATEGY;
+
+ ImageChunk(int type, size_t start, const std::vector<uint8_t>* file_content, size_t raw_data_len)
+ : type_(type),
+ start_(start),
+ input_file_ptr_(file_content),
+ raw_data_len_(raw_data_len),
+ compress_level_(6),
+ source_start_(0),
+ source_len_(0),
+ source_uncompressed_len_(0) {
+ CHECK(file_content != nullptr) << "input file container can't be nullptr";
+ }
+
+ int GetType() const {
+ return type_;
+ }
+ size_t GetRawDataLength() const {
+ return raw_data_len_;
+ }
+ const std::string& GetEntryName() const {
+ return entry_name_;
+ }
+
+ // CHUNK_DEFLATE will return the uncompressed data for diff, while other types will simply return
+ // the raw data.
+ const uint8_t * DataForPatch() const;
+ size_t DataLengthForPatch() const;
+
+ void Dump() const {
+ printf("type %d start %zu len %zu\n", type_, start_, DataLengthForPatch());
+ }
+
+ void SetSourceInfo(const ImageChunk& other);
+ void SetEntryName(std::string entryname);
+ void SetUncompressedData(std::vector<uint8_t> data);
+ bool SetBonusData(const std::vector<uint8_t>& bonus_data);
+
+ bool operator==(const ImageChunk& other) const;
+ bool operator!=(const ImageChunk& other) const {
+ return !(*this == other);
+ }
+
+ size_t GetHeaderSize(size_t patch_size) const;
+ // Return the offset of the next patch into the patch data.
+ size_t WriteHeaderToFd(int fd, const std::vector<uint8_t>& patch, size_t offset);
+
+ /*
+ * Cause a gzip chunk to be treated as a normal chunk (ie, as a blob
+ * of uninterpreted data). The resulting patch will likely be about
+ * as big as the target file, but it lets us handle the case of images
+ * where some gzip chunks are reconstructible but others aren't (by
+ * treating the ones that aren't as normal chunks).
+ */
+ void ChangeDeflateChunkToNormal();
+ bool ChangeChunkToRaw(size_t patch_size);
+
+ /*
+ * Verify that we can reproduce exactly the same compressed data that
+ * we started with. Sets the level, method, windowBits, memLevel, and
+ * strategy fields in the chunk to the encoding parameters needed to
+ * produce the right output.
+ */
+ bool ReconstructDeflateChunk();
+ bool IsAdjacentNormal(const ImageChunk& other) const;
+ void MergeAdjacentNormal(const ImageChunk& other);
+
+ private:
+ int type_; // CHUNK_NORMAL, CHUNK_DEFLATE, CHUNK_RAW
+ size_t start_; // offset of chunk in the original input file
+ const std::vector<uint8_t>* input_file_ptr_; // ptr to the full content of original input file
+ size_t raw_data_len_;
// --- for CHUNK_DEFLATE chunks only: ---
-
- // original (compressed) deflate data
- size_t deflate_len;
- unsigned char* deflate_data;
-
- char* filename; // used for zip entries
+ std::vector<uint8_t> uncompressed_data_;
+ std::string entry_name_; // used for zip entries
// deflate encoder parameters
- int level, method, windowBits, memLevel, strategy;
+ int compress_level_;
- size_t source_uncompressed_len;
-} ImageChunk;
+ size_t source_start_;
+ size_t source_len_;
+ size_t source_uncompressed_len_;
-typedef struct {
- int data_offset;
- int deflate_len;
- int uncomp_len;
- char* filename;
-} ZipFileEntry;
+ const uint8_t* GetRawData() const;
+ bool TryReconstruction(int level);
+};
-static int fileentry_compare(const void* a, const void* b) {
- int ao = ((ZipFileEntry*)a)->data_offset;
- int bo = ((ZipFileEntry*)b)->data_offset;
- if (ao < bo) {
- return -1;
- } else if (ao > bo) {
- return 1;
- } else {
- return 0;
+const uint8_t* ImageChunk::GetRawData() const {
+ CHECK_LE(start_ + raw_data_len_, input_file_ptr_->size());
+ return input_file_ptr_->data() + start_;
+}
+
+const uint8_t * ImageChunk::DataForPatch() const {
+ if (type_ == CHUNK_DEFLATE) {
+ return uncompressed_data_.data();
+ }
+ return GetRawData();
+}
+
+size_t ImageChunk::DataLengthForPatch() const {
+ if (type_ == CHUNK_DEFLATE) {
+ return uncompressed_data_.size();
+ }
+ return raw_data_len_;
+}
+
+bool ImageChunk::operator==(const ImageChunk& other) const {
+ if (type_ != other.type_) {
+ return false;
+ }
+ return (raw_data_len_ == other.raw_data_len_ &&
+ memcmp(GetRawData(), other.GetRawData(), raw_data_len_) == 0);
+}
+
+void ImageChunk::SetSourceInfo(const ImageChunk& src) {
+ source_start_ = src.start_;
+ if (type_ == CHUNK_NORMAL) {
+ source_len_ = src.raw_data_len_;
+ } else if (type_ == CHUNK_DEFLATE) {
+ source_len_ = src.raw_data_len_;
+ source_uncompressed_len_ = src.uncompressed_data_.size();
}
}
-// from bsdiff.c
-int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* newdata, off_t newsize,
- const char* patch_filename);
+void ImageChunk::SetEntryName(std::string entryname) {
+ entry_name_ = std::move(entryname);
+}
-unsigned char* ReadZip(const char* filename,
- int* num_chunks, ImageChunk** chunks,
- int include_pseudo_chunk) {
- struct stat st;
- if (stat(filename, &st) != 0) {
- printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
- return NULL;
+void ImageChunk::SetUncompressedData(std::vector<uint8_t> data) {
+ uncompressed_data_ = std::move(data);
+}
+
+bool ImageChunk::SetBonusData(const std::vector<uint8_t>& bonus_data) {
+ if (type_ != CHUNK_DEFLATE) {
+ return false;
}
+ uncompressed_data_.insert(uncompressed_data_.end(), bonus_data.begin(), bonus_data.end());
+ return true;
+}
- size_t sz = static_cast<size_t>(st.st_size);
- unsigned char* img = reinterpret_cast<unsigned char*>(malloc(sz));
- FILE* f = fopen(filename, "rb");
- if (fread(img, 1, sz, f) != sz) {
- printf("failed to read \"%s\" %s\n", filename, strerror(errno));
- fclose(f);
- return NULL;
+// Convert CHUNK_NORMAL & CHUNK_DEFLATE to CHUNK_RAW if the target size is
+// smaller. Also take the header size into account during size comparison.
+bool ImageChunk::ChangeChunkToRaw(size_t patch_size) {
+ if (type_ == CHUNK_RAW) {
+ return true;
+ } else if (type_ == CHUNK_NORMAL && (raw_data_len_ <= 160 || raw_data_len_ < patch_size)) {
+ type_ = CHUNK_RAW;
+ return true;
}
- fclose(f);
+ return false;
+}
- // look for the end-of-central-directory record.
+void ImageChunk::ChangeDeflateChunkToNormal() {
+ if (type_ != CHUNK_DEFLATE) return;
+ type_ = CHUNK_NORMAL;
+ entry_name_.clear();
+ uncompressed_data_.clear();
+}
- int i;
- for (i = st.st_size-20; i >= 0 && i > st.st_size - 65600; --i) {
- if (img[i] == 0x50 && img[i+1] == 0x4b &&
- img[i+2] == 0x05 && img[i+3] == 0x06) {
- break;
- }
+// Header size:
+// header_type 4 bytes
+// CHUNK_NORMAL 8*3 = 24 bytes
+// CHUNK_DEFLATE 8*5 + 4*5 = 60 bytes
+// CHUNK_RAW 4 bytes + patch_size
+size_t ImageChunk::GetHeaderSize(size_t patch_size) const {
+ switch (type_) {
+ case CHUNK_NORMAL:
+ return 4 + 8 * 3;
+ case CHUNK_DEFLATE:
+ return 4 + 8 * 5 + 4 * 5;
+ case CHUNK_RAW:
+ return 4 + 4 + patch_size;
+ default:
+ CHECK(false) << "unexpected chunk type: " << type_; // Should not reach here.
+ return 0;
}
- // double-check: this archive consists of a single "disk"
- if (!(img[i+4] == 0 && img[i+5] == 0 && img[i+6] == 0 && img[i+7] == 0)) {
- printf("can't process multi-disk archive\n");
- return NULL;
- }
+}
- int cdcount = Read2(img+i+8);
- int cdoffset = Read4(img+i+16);
-
- ZipFileEntry* temp_entries = reinterpret_cast<ZipFileEntry*>(malloc(
- cdcount * sizeof(ZipFileEntry)));
- int entrycount = 0;
-
- unsigned char* cd = img+cdoffset;
- for (i = 0; i < cdcount; ++i) {
- if (!(cd[0] == 0x50 && cd[1] == 0x4b && cd[2] == 0x01 && cd[3] == 0x02)) {
- printf("bad central directory entry %d\n", i);
- return NULL;
- }
-
- int clen = Read4(cd+20); // compressed len
- int ulen = Read4(cd+24); // uncompressed len
- int nlen = Read2(cd+28); // filename len
- int xlen = Read2(cd+30); // extra field len
- int mlen = Read2(cd+32); // file comment len
- int hoffset = Read4(cd+42); // local header offset
-
- char* filename = reinterpret_cast<char*>(malloc(nlen+1));
- memcpy(filename, cd+46, nlen);
- filename[nlen] = '\0';
-
- int method = Read2(cd+10);
-
- cd += 46 + nlen + xlen + mlen;
-
- if (method != 8) { // 8 == deflate
- free(filename);
- continue;
- }
-
- unsigned char* lh = img + hoffset;
-
- if (!(lh[0] == 0x50 && lh[1] == 0x4b && lh[2] == 0x03 && lh[3] == 0x04)) {
- printf("bad local file header entry %d\n", i);
- return NULL;
- }
-
- if (Read2(lh+26) != nlen || memcmp(lh+30, filename, nlen) != 0) {
- printf("central dir filename doesn't match local header\n");
- return NULL;
- }
-
- xlen = Read2(lh+28); // extra field len; might be different from CD entry?
-
- temp_entries[entrycount].data_offset = hoffset+30+nlen+xlen;
- temp_entries[entrycount].deflate_len = clen;
- temp_entries[entrycount].uncomp_len = ulen;
- temp_entries[entrycount].filename = filename;
- ++entrycount;
- }
-
- qsort(temp_entries, entrycount, sizeof(ZipFileEntry), fileentry_compare);
-
-#if 0
- printf("found %d deflated entries\n", entrycount);
- for (i = 0; i < entrycount; ++i) {
- printf("off %10d len %10d unlen %10d %p %s\n",
- temp_entries[i].data_offset,
- temp_entries[i].deflate_len,
- temp_entries[i].uncomp_len,
- temp_entries[i].filename,
- temp_entries[i].filename);
- }
-#endif
-
- *num_chunks = 0;
- *chunks = reinterpret_cast<ImageChunk*>(malloc((entrycount*2+2) * sizeof(ImageChunk)));
- ImageChunk* curr = *chunks;
-
- if (include_pseudo_chunk) {
- curr->type = CHUNK_NORMAL;
- curr->start = 0;
- curr->len = st.st_size;
- curr->data = img;
- curr->filename = NULL;
- curr->I = NULL;
- ++curr;
- ++*num_chunks;
- }
-
- int pos = 0;
- int nextentry = 0;
-
- while (pos < st.st_size) {
- if (nextentry < entrycount && pos == temp_entries[nextentry].data_offset) {
- curr->type = CHUNK_DEFLATE;
- curr->start = pos;
- curr->deflate_len = temp_entries[nextentry].deflate_len;
- curr->deflate_data = img + pos;
- curr->filename = temp_entries[nextentry].filename;
- curr->I = NULL;
-
- curr->len = temp_entries[nextentry].uncomp_len;
- curr->data = reinterpret_cast<unsigned char*>(malloc(curr->len));
-
- z_stream strm;
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- strm.avail_in = curr->deflate_len;
- strm.next_in = curr->deflate_data;
-
- // -15 means we are decoding a 'raw' deflate stream; zlib will
- // not expect zlib headers.
- int ret = inflateInit2(&strm, -15);
-
- strm.avail_out = curr->len;
- strm.next_out = curr->data;
- ret = inflate(&strm, Z_NO_FLUSH);
- if (ret != Z_STREAM_END) {
- printf("failed to inflate \"%s\"; %d\n", curr->filename, ret);
- return NULL;
+size_t ImageChunk::WriteHeaderToFd(int fd, const std::vector<uint8_t>& patch, size_t offset) {
+ Write4(fd, type_);
+ switch (type_) {
+ case CHUNK_NORMAL:
+ printf("normal (%10zu, %10zu) %10zu\n", start_, raw_data_len_, patch.size());
+ Write8(fd, static_cast<int64_t>(source_start_));
+ Write8(fd, static_cast<int64_t>(source_len_));
+ Write8(fd, static_cast<int64_t>(offset));
+ return offset + patch.size();
+ case CHUNK_DEFLATE:
+ printf("deflate (%10zu, %10zu) %10zu %s\n", start_, raw_data_len_, patch.size(),
+ entry_name_.c_str());
+ Write8(fd, static_cast<int64_t>(source_start_));
+ Write8(fd, static_cast<int64_t>(source_len_));
+ Write8(fd, static_cast<int64_t>(offset));
+ Write8(fd, static_cast<int64_t>(source_uncompressed_len_));
+ Write8(fd, static_cast<int64_t>(uncompressed_data_.size()));
+ Write4(fd, compress_level_);
+ Write4(fd, METHOD);
+ Write4(fd, WINDOWBITS);
+ Write4(fd, MEMLEVEL);
+ Write4(fd, STRATEGY);
+ return offset + patch.size();
+ case CHUNK_RAW:
+ printf("raw (%10zu, %10zu)\n", start_, raw_data_len_);
+ Write4(fd, static_cast<int32_t>(patch.size()));
+ if (!android::base::WriteFully(fd, patch.data(), patch.size())) {
+ CHECK(false) << "failed to write " << patch.size() <<" bytes patch";
}
+ return offset;
+ default:
+ CHECK(false) << "unexpected chunk type: " << type_;
+ return offset;
+ }
+}
- inflateEnd(&strm);
+bool ImageChunk::IsAdjacentNormal(const ImageChunk& other) const {
+ if (type_ != CHUNK_NORMAL || other.type_ != CHUNK_NORMAL) {
+ return false;
+ }
+ return (other.start_ == start_ + raw_data_len_);
+}
- pos += curr->deflate_len;
- ++nextentry;
- ++*num_chunks;
- ++curr;
- continue;
- }
+void ImageChunk::MergeAdjacentNormal(const ImageChunk& other) {
+ CHECK(IsAdjacentNormal(other));
+ raw_data_len_ = raw_data_len_ + other.raw_data_len_;
+}
- // use a normal chunk to take all the data up to the start of the
- // next deflate section.
-
- curr->type = CHUNK_NORMAL;
- curr->start = pos;
- if (nextentry < entrycount) {
- curr->len = temp_entries[nextentry].data_offset - pos;
- } else {
- curr->len = st.st_size - pos;
- }
- curr->data = img + pos;
- curr->filename = NULL;
- curr->I = NULL;
- pos += curr->len;
-
- ++*num_chunks;
- ++curr;
+bool ImageChunk::ReconstructDeflateChunk() {
+ if (type_ != CHUNK_DEFLATE) {
+ printf("attempt to reconstruct non-deflate chunk\n");
+ return false;
}
- free(temp_entries);
- return img;
+ // We only check two combinations of encoder parameters: level 6
+ // (the default) and level 9 (the maximum).
+ for (int level = 6; level <= 9; level += 3) {
+ if (TryReconstruction(level)) {
+ compress_level_ = level;
+ return true;
+ }
+ }
+
+ return false;
}
/*
- * Read the given file and break it up into chunks, putting the number
- * of chunks and their info in *num_chunks and **chunks,
- * respectively. Returns a malloc'd block of memory containing the
- * contents of the file; various pointers in the output chunk array
- * will point into this block of memory. The caller should free the
- * return value when done with all the chunks. Returns NULL on
- * failure.
+ * Takes the uncompressed data stored in the chunk, compresses it
+ * using the zlib parameters stored in the chunk, and checks that it
+ * matches exactly the compressed data we started with (also stored in
+ * the chunk).
*/
-unsigned char* ReadImage(const char* filename,
- int* num_chunks, ImageChunk** chunks) {
+bool ImageChunk::TryReconstruction(int level) {
+ z_stream strm;
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = uncompressed_data_.size();
+ strm.next_in = uncompressed_data_.data();
+ int ret = deflateInit2(&strm, level, METHOD, WINDOWBITS, MEMLEVEL, STRATEGY);
+ if (ret < 0) {
+ printf("failed to initialize deflate: %d\n", ret);
+ return false;
+ }
+
+ std::vector<uint8_t> buffer(BUFFER_SIZE);
+ size_t offset = 0;
+ do {
+ strm.avail_out = buffer.size();
+ strm.next_out = buffer.data();
+ ret = deflate(&strm, Z_FINISH);
+ if (ret < 0) {
+ printf("failed to deflate: %d\n", ret);
+ return false;
+ }
+
+ size_t compressed_size = buffer.size() - strm.avail_out;
+ if (memcmp(buffer.data(), input_file_ptr_->data() + start_ + offset, compressed_size) != 0) {
+ // mismatch; data isn't the same.
+ deflateEnd(&strm);
+ return false;
+ }
+ offset += compressed_size;
+ } while (ret != Z_STREAM_END);
+ deflateEnd(&strm);
+
+ if (offset != raw_data_len_) {
+ // mismatch; ran out of data before we should have.
+ return false;
+ }
+ return true;
+}
+
+// EOCD record
+// offset 0: signature 0x06054b50, 4 bytes
+// offset 4: number of this disk, 2 bytes
+// ...
+// offset 20: comment length, 2 bytes
+// offset 22: comment, n bytes
+static bool GetZipFileSize(const std::vector<uint8_t>& zip_file, size_t* input_file_size) {
+ if (zip_file.size() < 22) {
+ printf("file is too small to be a zip file\n");
+ return false;
+ }
+
+ // Look for End of central directory record of the zip file, and calculate the actual
+ // zip_file size.
+ for (int i = zip_file.size() - 22; i >= 0; i--) {
+ if (zip_file[i] == 0x50) {
+ if (get_unaligned<uint32_t>(&zip_file[i]) == 0x06054b50) {
+ // double-check: this archive consists of a single "disk".
+ CHECK_EQ(get_unaligned<uint16_t>(&zip_file[i + 4]), 0);
+
+ uint16_t comment_length = get_unaligned<uint16_t>(&zip_file[i + 20]);
+ size_t file_size = i + 22 + comment_length;
+ CHECK_LE(file_size, zip_file.size());
+ *input_file_size = file_size;
+ return true;
+ }
+ }
+ }
+
+ // EOCD not found, this file is likely not a valid zip file.
+ return false;
+}
+
+static bool ReadZip(const char* filename, std::vector<ImageChunk>* chunks,
+ std::vector<uint8_t>* zip_file, bool include_pseudo_chunk) {
+ CHECK(chunks != nullptr && zip_file != nullptr);
+
+ android::base::unique_fd fd(open(filename, O_RDONLY));
+ if (fd == -1) {
+ printf("failed to open \"%s\" %s\n", filename, strerror(errno));
+ return false;
+ }
struct stat st;
- if (stat(filename, &st) != 0) {
+ if (fstat(fd, &st) != 0) {
printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
- return NULL;
+ return false;
}
size_t sz = static_cast<size_t>(st.st_size);
- unsigned char* img = reinterpret_cast<unsigned char*>(malloc(sz + 4));
- FILE* f = fopen(filename, "rb");
- if (fread(img, 1, sz, f) != sz) {
+ zip_file->resize(sz);
+ if (!android::base::ReadFully(fd, zip_file->data(), sz)) {
printf("failed to read \"%s\" %s\n", filename, strerror(errno));
- fclose(f);
- return NULL;
+ return false;
}
- fclose(f);
+ fd.reset();
- // 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+sz, 0, 4);
+ // Trim the trailing zeros before we pass the file to ziparchive handler.
+ size_t zipfile_size;
+ if (!GetZipFileSize(*zip_file, &zipfile_size)) {
+ printf("failed to parse the actual size of %s\n", filename);
+ return false;
+ }
+ ZipArchiveHandle handle;
+ int err = OpenArchiveFromMemory(zip_file->data(), zipfile_size, filename, &handle);
+ if (err != 0) {
+ printf("failed to open zip file %s: %s\n", filename, ErrorCodeString(err));
+ CloseArchive(handle);
+ return false;
+ }
+
+ // Create a list of deflated zip entries, sorted by offset.
+ std::vector<std::pair<std::string, ZipEntry>> temp_entries;
+ void* cookie;
+ int ret = StartIteration(handle, &cookie, nullptr, nullptr);
+ if (ret != 0) {
+ printf("failed to iterate over entries in %s: %s\n", filename, ErrorCodeString(ret));
+ CloseArchive(handle);
+ return false;
+ }
+
+ ZipString name;
+ ZipEntry entry;
+ while ((ret = Next(cookie, &entry, &name)) == 0) {
+ if (entry.method == kCompressDeflated) {
+ std::string entryname(name.name, name.name + name.name_length);
+ temp_entries.push_back(std::make_pair(entryname, entry));
+ }
+ }
+
+ if (ret != -1) {
+ printf("Error while iterating over zip entries: %s\n", ErrorCodeString(ret));
+ CloseArchive(handle);
+ return false;
+ }
+ std::sort(temp_entries.begin(), temp_entries.end(),
+ [](auto& entry1, auto& entry2) {
+ return entry1.second.offset < entry2.second.offset;
+ });
+
+ EndIteration(cookie);
+
+ if (include_pseudo_chunk) {
+ chunks->emplace_back(CHUNK_NORMAL, 0, zip_file, zip_file->size());
+ }
+
+ size_t pos = 0;
+ size_t nextentry = 0;
+ while (pos < zip_file->size()) {
+ if (nextentry < temp_entries.size() &&
+ static_cast<off64_t>(pos) == temp_entries[nextentry].second.offset) {
+ // compose the next deflate chunk.
+ std::string entryname = temp_entries[nextentry].first;
+ size_t uncompressed_len = temp_entries[nextentry].second.uncompressed_length;
+ std::vector<uint8_t> uncompressed_data(uncompressed_len);
+ if ((ret = ExtractToMemory(handle, &temp_entries[nextentry].second, uncompressed_data.data(),
+ uncompressed_len)) != 0) {
+ printf("failed to extract %s with size %zu: %s\n", entryname.c_str(), uncompressed_len,
+ ErrorCodeString(ret));
+ CloseArchive(handle);
+ return false;
+ }
+
+ size_t compressed_len = temp_entries[nextentry].second.compressed_length;
+ ImageChunk curr(CHUNK_DEFLATE, pos, zip_file, compressed_len);
+ curr.SetEntryName(std::move(entryname));
+ curr.SetUncompressedData(std::move(uncompressed_data));
+ chunks->push_back(curr);
+
+ pos += compressed_len;
+ ++nextentry;
+ continue;
+ }
+
+ // Use a normal chunk to take all the data up to the start of the next deflate section.
+ size_t raw_data_len;
+ if (nextentry < temp_entries.size()) {
+ raw_data_len = temp_entries[nextentry].second.offset - pos;
+ } else {
+ raw_data_len = zip_file->size() - pos;
+ }
+ chunks->emplace_back(CHUNK_NORMAL, pos, zip_file, raw_data_len);
+
+ pos += raw_data_len;
+ }
+
+ CloseArchive(handle);
+ return true;
+}
+
+// Read the given file and break it up into chunks, and putting the data in to a vector.
+static bool ReadImage(const char* filename, std::vector<ImageChunk>* chunks,
+ std::vector<uint8_t>* img) {
+ CHECK(chunks != nullptr && img != nullptr);
+
+ android::base::unique_fd fd(open(filename, O_RDONLY));
+ if (fd == -1) {
+ printf("failed to open \"%s\" %s\n", filename, strerror(errno));
+ return false;
+ }
+ struct stat st;
+ if (fstat(fd, &st) != 0) {
+ printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
+ return false;
+ }
+
+ size_t sz = static_cast<size_t>(st.st_size);
+ img->resize(sz);
+ if (!android::base::ReadFully(fd, img->data(), sz)) {
+ printf("failed to read \"%s\" %s\n", filename, strerror(errno));
+ return false;
+ }
size_t pos = 0;
- *num_chunks = 0;
- *chunks = NULL;
-
while (pos < sz) {
- unsigned char* p = img+pos;
-
- 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
+ // 0x00 no header flags, 0x08 deflate compression, 0x1f8b gzip magic number
+ if (sz - pos >= 4 && get_unaligned<uint32_t>(img->data() + pos) == 0x00088b1f) {
// 'pos' is the offset of the start of a gzip chunk.
size_t chunk_offset = pos;
- *num_chunks += 3;
- *chunks = reinterpret_cast<ImageChunk*>(realloc(*chunks,
- *num_chunks * sizeof(ImageChunk)));
- ImageChunk* curr = *chunks + (*num_chunks-3);
+ // The remaining data is too small to be a gzip chunk; treat them as a normal chunk.
+ if (sz - pos < GZIP_HEADER_LEN + GZIP_FOOTER_LEN) {
+ chunks->emplace_back(CHUNK_NORMAL, pos, img, sz - pos);
+ break;
+ }
- // create a normal chunk for the header.
- curr->start = pos;
- curr->type = CHUNK_NORMAL;
- curr->len = GZIP_HEADER_LEN;
- curr->data = p;
- curr->I = NULL;
+ // We need three chunks for the deflated image in total, one normal chunk for the header,
+ // one deflated chunk for the body, and another normal chunk for the footer.
+ chunks->emplace_back(CHUNK_NORMAL, pos, img, GZIP_HEADER_LEN);
+ pos += GZIP_HEADER_LEN;
- pos += curr->len;
- p += curr->len;
- ++curr;
-
- curr->type = CHUNK_DEFLATE;
- curr->filename = NULL;
- curr->I = NULL;
-
- // We must decompress this chunk in order to discover where it
- // ends, and so we can put the uncompressed data and its length
- // into curr->data and curr->len.
-
- size_t allocated = 32768;
- curr->len = 0;
- curr->data = reinterpret_cast<unsigned char*>(malloc(allocated));
- curr->start = pos;
- curr->deflate_data = p;
+ // We must decompress this chunk in order to discover where it ends, and so we can update
+ // the uncompressed_data of the image body and its length.
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = sz - pos;
- strm.next_in = p;
+ strm.next_in = img->data() + pos;
// -15 means we are decoding a 'raw' deflate stream; zlib will
// not expect zlib headers.
int ret = inflateInit2(&strm, -15);
+ if (ret < 0) {
+ printf("failed to initialize inflate: %d\n", ret);
+ return false;
+ }
+ size_t allocated = BUFFER_SIZE;
+ std::vector<uint8_t> uncompressed_data(allocated);
+ size_t uncompressed_len = 0, raw_data_len = 0;
do {
- strm.avail_out = allocated - curr->len;
- strm.next_out = curr->data + curr->len;
+ strm.avail_out = allocated - uncompressed_len;
+ strm.next_out = uncompressed_data.data() + uncompressed_len;
ret = inflate(&strm, Z_NO_FLUSH);
if (ret < 0) {
- 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",
+ printf("Warning: inflate failed [%s] at offset [%zu], treating as a normal chunk\n",
strm.msg, chunk_offset);
- free(img);
- return NULL;
+ break;
}
- curr->len = allocated - strm.avail_out;
+ uncompressed_len = allocated - strm.avail_out;
if (strm.avail_out == 0) {
allocated *= 2;
- curr->data = reinterpret_cast<unsigned char*>(realloc(curr->data, allocated));
+ uncompressed_data.resize(allocated);
}
- processed_deflate = true;
} while (ret != Z_STREAM_END);
- curr->deflate_len = sz - strm.avail_in - pos;
+ raw_data_len = sz - strm.avail_in - pos;
inflateEnd(&strm);
- pos += curr->deflate_len;
- p += curr->deflate_len;
- ++curr;
+
+ if (ret < 0) {
+ continue;
+ }
+
+ ImageChunk body(CHUNK_DEFLATE, pos, img, raw_data_len);
+ uncompressed_data.resize(uncompressed_len);
+ body.SetUncompressedData(std::move(uncompressed_data));
+ chunks->push_back(body);
+
+ pos += raw_data_len;
// create a normal chunk for the footer
+ chunks->emplace_back(CHUNK_NORMAL, pos, img, GZIP_FOOTER_LEN);
- curr->type = CHUNK_NORMAL;
- curr->start = pos;
- curr->len = GZIP_FOOTER_LEN;
- curr->data = img+pos;
- curr->I = NULL;
-
- pos += curr->len;
- p += curr->len;
- ++curr;
+ pos += GZIP_FOOTER_LEN;
// The footer (that we just skipped over) contains the size of
// the uncompressed data. Double-check to make sure that it
// matches the size of the data we got when we actually did
// the decompression.
- size_t footer_size = Read4(p-4);
- if (footer_size != curr[-2].len) {
- printf("Error: footer size %zu != decompressed size %zu\n",
- footer_size, curr[-2].len);
- free(img);
- return NULL;
+ size_t footer_size = get_unaligned<uint32_t>(img->data() + pos - 4);
+ if (footer_size != body.DataLengthForPatch()) {
+ printf("Error: footer size %zu != decompressed size %zu\n", footer_size,
+ body.GetRawDataLength());
+ return false;
}
} else {
- // 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 = reinterpret_cast<ImageChunk*>(realloc(*chunks, *num_chunks * sizeof(ImageChunk)));
- ImageChunk* curr = *chunks + (*num_chunks-1);
- curr->start = pos;
- curr->I = NULL;
+ // Use a normal chunk to take all the contents until the next gzip chunk (or EOF); we expect
+ // the number of chunks to be small (5 for typical boot and recovery images).
- // 'pos' is not the offset of the start of a gzip chunk, so scan
- // forward until we find a gzip header.
- curr->type = CHUNK_NORMAL;
- curr->data = p;
-
- 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 &&
- p[curr->len+3] == 0x00) {
+ // Scan forward until we find a gzip header.
+ size_t data_len = 0;
+ while (data_len + pos < sz) {
+ if (data_len + pos + 4 <= sz &&
+ get_unaligned<uint32_t>(img->data() + pos + data_len) == 0x00088b1f) {
break;
}
+ data_len++;
}
- pos += curr->len;
+ chunks->emplace_back(CHUNK_NORMAL, pos, img, data_len);
+
+ pos += data_len;
}
}
- return img;
+ return true;
}
-#define BUFFER_SIZE 32768
-
/*
- * Takes the uncompressed data stored in the chunk, compresses it
- * using the zlib parameters stored in the chunk, and checks that it
- * matches exactly the compressed data we started with (also stored in
- * the chunk). Return 0 on success.
+ * Given source and target chunks, compute a bsdiff patch between them.
+ * Store the result in the patch_data.
+ * |bsdiff_cache| can be used to cache the suffix array if the same |src| chunk
+ * is used repeatedly, pass nullptr if not needed.
*/
-int TryReconstruction(ImageChunk* chunk, unsigned char* out) {
- size_t p = 0;
+static bool MakePatch(const ImageChunk* src, ImageChunk* tgt, std::vector<uint8_t>* patch_data,
+ saidx_t** bsdiff_cache) {
+ if (tgt->ChangeChunkToRaw(0)) {
+ size_t patch_size = tgt->DataLengthForPatch();
+ patch_data->resize(patch_size);
+ std::copy(tgt->DataForPatch(), tgt->DataForPatch() + patch_size, patch_data->begin());
+ return true;
+ }
-#if 0
- printf("trying %d %d %d %d %d\n",
- chunk->level, chunk->method, chunk->windowBits,
- chunk->memLevel, chunk->strategy);
+#if defined(__ANDROID__)
+ char ptemp[] = "/data/local/tmp/imgdiff-patch-XXXXXX";
+#else
+ char ptemp[] = "/tmp/imgdiff-patch-XXXXXX";
#endif
- z_stream strm;
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- strm.avail_in = chunk->len;
- strm.next_in = chunk->data;
- int ret;
- ret = deflateInit2(&strm, chunk->level, chunk->method, chunk->windowBits,
- chunk->memLevel, chunk->strategy);
- do {
- strm.avail_out = BUFFER_SIZE;
- strm.next_out = out;
- ret = deflate(&strm, Z_FINISH);
- size_t have = BUFFER_SIZE - strm.avail_out;
-
- if (memcmp(out, chunk->deflate_data+p, have) != 0) {
- // mismatch; data isn't the same.
- deflateEnd(&strm);
- return -1;
- }
- p += have;
- } while (ret != Z_STREAM_END);
- deflateEnd(&strm);
- if (p != chunk->deflate_len) {
- // mismatch; ran out of data before we should have.
- return -1;
- }
- return 0;
-}
-
-/*
- * Verify that we can reproduce exactly the same compressed data that
- * we started with. Sets the level, method, windowBits, memLevel, and
- * strategy fields in the chunk to the encoding parameters needed to
- * produce the right output. Returns 0 on success.
- */
-int ReconstructDeflateChunk(ImageChunk* chunk) {
- if (chunk->type != CHUNK_DEFLATE) {
- printf("attempt to reconstruct non-deflate chunk\n");
- return -1;
- }
-
- size_t p = 0;
- unsigned char* out = reinterpret_cast<unsigned char*>(malloc(BUFFER_SIZE));
-
- // We only check two combinations of encoder parameters: level 6
- // (the default) and level 9 (the maximum).
- for (chunk->level = 6; chunk->level <= 9; chunk->level += 3) {
- chunk->windowBits = -15; // 32kb window; negative to indicate a raw stream.
- chunk->memLevel = 8; // the default value.
- chunk->method = Z_DEFLATED;
- chunk->strategy = Z_DEFAULT_STRATEGY;
-
- if (TryReconstruction(chunk, out) == 0) {
- free(out);
- return 0;
- }
- }
-
- free(out);
- return -1;
-}
-
-/*
- * Given source and target chunks, compute a bsdiff patch between them
- * by running bsdiff in a subprocess. Return the patch data, placing
- * its length in *size. Return NULL on failure. We expect the bsdiff
- * program to be in the path.
- */
-unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) {
- if (tgt->type == CHUNK_NORMAL) {
- if (tgt->len <= 160) {
- tgt->type = CHUNK_RAW;
- *size = tgt->len;
- return tgt->data;
- }
- }
-
- char ptemp[] = "/tmp/imgdiff-patch-XXXXXX";
int fd = mkstemp(ptemp);
-
if (fd == -1) {
- printf("MakePatch failed to create a temporary file: %s\n",
- strerror(errno));
- return NULL;
+ printf("MakePatch failed to create a temporary file: %s\n", strerror(errno));
+ return false;
}
- close(fd); // temporary file is created and we don't need its file
- // descriptor
+ close(fd);
- int r = bsdiff(src->data, src->len, &(src->I), tgt->data, tgt->len, ptemp);
+ int r = bsdiff::bsdiff(src->DataForPatch(), src->DataLengthForPatch(), tgt->DataForPatch(),
+ tgt->DataLengthForPatch(), ptemp, bsdiff_cache);
if (r != 0) {
printf("bsdiff() failed: %d\n", r);
- return NULL;
+ return false;
}
+ android::base::unique_fd patch_fd(open(ptemp, O_RDONLY));
+ if (patch_fd == -1) {
+ printf("failed to open %s: %s\n", ptemp, strerror(errno));
+ return false;
+ }
struct stat st;
- if (stat(ptemp, &st) != 0) {
- printf("failed to stat patch file %s: %s\n",
- ptemp, strerror(errno));
- return NULL;
+ if (fstat(patch_fd, &st) != 0) {
+ printf("failed to stat patch file %s: %s\n", ptemp, strerror(errno));
+ return false;
}
size_t sz = static_cast<size_t>(st.st_size);
- // TODO: Memory leak on error return.
- unsigned char* data = reinterpret_cast<unsigned char*>(malloc(sz));
-
- if (tgt->type == CHUNK_NORMAL && tgt->len <= sz) {
+ // Change the chunk type to raw if the patch takes less space that way.
+ if (tgt->ChangeChunkToRaw(sz)) {
unlink(ptemp);
-
- tgt->type = CHUNK_RAW;
- *size = tgt->len;
- return tgt->data;
+ size_t patch_size = tgt->DataLengthForPatch();
+ patch_data->resize(patch_size);
+ std::copy(tgt->DataForPatch(), tgt->DataForPatch() + patch_size, patch_data->begin());
+ return true;
}
-
- *size = sz;
-
- FILE* f = fopen(ptemp, "rb");
- if (f == NULL) {
- printf("failed to open patch %s: %s\n", ptemp, strerror(errno));
- return NULL;
+ patch_data->resize(sz);
+ if (!android::base::ReadFully(patch_fd, patch_data->data(), sz)) {
+ printf("failed to read \"%s\" %s\n", ptemp, strerror(errno));
+ return false;
}
- if (fread(data, 1, sz, f) != sz) {
- printf("failed to read patch %s: %s\n", ptemp, strerror(errno));
- return NULL;
- }
- fclose(f);
unlink(ptemp);
+ tgt->SetSourceInfo(*src);
- tgt->source_start = src->start;
- switch (tgt->type) {
- case CHUNK_NORMAL:
- tgt->source_len = src->len;
- break;
- case CHUNK_DEFLATE:
- tgt->source_len = src->deflate_len;
- tgt->source_uncompressed_len = src->len;
- break;
- }
-
- return data;
-}
-
-/*
- * Cause a gzip chunk to be treated as a normal chunk (ie, as a blob
- * of uninterpreted data). The resulting patch will likely be about
- * as big as the target file, but it lets us handle the case of images
- * where some gzip chunks are reconstructible but others aren't (by
- * treating the ones that aren't as normal chunks).
- */
-void ChangeDeflateChunkToNormal(ImageChunk* ch) {
- if (ch->type != CHUNK_DEFLATE) return;
- ch->type = CHUNK_NORMAL;
- free(ch->data);
- ch->data = ch->deflate_data;
- ch->len = ch->deflate_len;
-}
-
-/*
- * Return true if the data in the chunk is identical (including the
- * compressed representation, for gzip chunks).
- */
-int AreChunksEqual(ImageChunk* a, ImageChunk* b) {
- if (a->type != b->type) return 0;
-
- switch (a->type) {
- case CHUNK_NORMAL:
- return a->len == b->len && memcmp(a->data, b->data, a->len) == 0;
-
- case CHUNK_DEFLATE:
- return a->deflate_len == b->deflate_len &&
- memcmp(a->deflate_data, b->deflate_data, a->deflate_len) == 0;
-
- default:
- printf("unknown chunk type %d\n", a->type);
- return 0;
- }
+ return true;
}
/*
@@ -740,137 +809,103 @@
* a single chunk. (Such runs can be produced when deflate chunks are
* changed to normal chunks.)
*/
-void MergeAdjacentNormalChunks(ImageChunk* chunks, int* num_chunks) {
- int out = 0;
- int in_start = 0, in_end;
- while (in_start < *num_chunks) {
- if (chunks[in_start].type != CHUNK_NORMAL) {
- in_end = in_start+1;
- } else {
- // in_start is a normal chunk. Look for a run of normal chunks
- // that constitute a solid block of data (ie, each chunk begins
- // where the previous one ended).
- for (in_end = in_start+1;
- in_end < *num_chunks && chunks[in_end].type == CHUNK_NORMAL &&
- (chunks[in_end].start ==
- chunks[in_end-1].start + chunks[in_end-1].len &&
- chunks[in_end].data ==
- chunks[in_end-1].data + chunks[in_end-1].len);
- ++in_end);
+static void MergeAdjacentNormalChunks(std::vector<ImageChunk>* chunks) {
+ size_t merged_last = 0, cur = 0;
+ while (cur < chunks->size()) {
+ // Look for normal chunks adjacent to the current one. If such chunk exists, extend the
+ // length of the current normal chunk.
+ size_t to_check = cur + 1;
+ while (to_check < chunks->size() && chunks->at(cur).IsAdjacentNormal(chunks->at(to_check))) {
+ chunks->at(cur).MergeAdjacentNormal(chunks->at(to_check));
+ to_check++;
}
- if (in_end == in_start+1) {
-#if 0
- printf("chunk %d is now %d\n", in_start, out);
-#endif
- if (out != in_start) {
- memcpy(chunks+out, chunks+in_start, sizeof(ImageChunk));
- }
- } else {
-#if 0
- printf("collapse normal chunks %d-%d into %d\n", in_start, in_end-1, out);
-#endif
-
- // Merge chunks [in_start, in_end-1] into one chunk. Since the
- // data member of each chunk is just a pointer into an in-memory
- // copy of the file, this can be done without recopying (the
- // output chunk has the first chunk's start location and data
- // pointer, and length equal to the sum of the input chunk
- // lengths).
- chunks[out].type = CHUNK_NORMAL;
- chunks[out].start = chunks[in_start].start;
- chunks[out].data = chunks[in_start].data;
- chunks[out].len = chunks[in_end-1].len +
- (chunks[in_end-1].start - chunks[in_start].start);
+ if (merged_last != cur) {
+ chunks->at(merged_last) = std::move(chunks->at(cur));
}
-
- ++out;
- in_start = in_end;
+ merged_last++;
+ cur = to_check;
}
- *num_chunks = out;
+ if (merged_last < chunks->size()) {
+ chunks->erase(chunks->begin() + merged_last, chunks->end());
+ }
}
-ImageChunk* FindChunkByName(const char* name,
- ImageChunk* chunks, int num_chunks) {
- int i;
- for (i = 0; i < num_chunks; ++i) {
- if (chunks[i].type == CHUNK_DEFLATE && chunks[i].filename &&
- strcmp(name, chunks[i].filename) == 0) {
- return chunks+i;
+static ImageChunk* FindChunkByName(const std::string& name, std::vector<ImageChunk>& chunks) {
+ for (size_t i = 0; i < chunks.size(); ++i) {
+ if (chunks[i].GetType() == CHUNK_DEFLATE && chunks[i].GetEntryName() == name) {
+ return &chunks[i];
}
}
- return NULL;
+ return nullptr;
}
-void DumpChunks(ImageChunk* chunks, int num_chunks) {
- 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);
- }
+static void DumpChunks(const std::vector<ImageChunk>& chunks) {
+ for (size_t i = 0; i < chunks.size(); ++i) {
+ printf("chunk %zu: ", i);
+ chunks[i].Dump();
+ }
}
-int main(int argc, char** argv) {
- int zip_mode = 0;
+int imgdiff(int argc, const char** argv) {
+ bool zip_mode = false;
if (argc >= 2 && strcmp(argv[1], "-z") == 0) {
- zip_mode = 1;
+ zip_mode = true;
--argc;
++argv;
}
- size_t bonus_size = 0;
- unsigned char* bonus_data = NULL;
+ std::vector<uint8_t> bonus_data;
if (argc >= 3 && strcmp(argv[1], "-b") == 0) {
- struct stat st;
- if (stat(argv[2], &st) != 0) {
- printf("failed to stat bonus file %s: %s\n", argv[2], strerror(errno));
- return 1;
- }
- bonus_size = st.st_size;
- bonus_data = reinterpret_cast<unsigned char*>(malloc(bonus_size));
- FILE* f = fopen(argv[2], "rb");
- if (f == NULL) {
+ android::base::unique_fd fd(open(argv[2], O_RDONLY));
+ if (fd == -1) {
printf("failed to open bonus file %s: %s\n", argv[2], strerror(errno));
return 1;
}
- if (fread(bonus_data, 1, bonus_size, f) != bonus_size) {
+ struct stat st;
+ if (fstat(fd, &st) != 0) {
+ printf("failed to stat bonus file %s: %s\n", argv[2], strerror(errno));
+ return 1;
+ }
+
+ size_t bonus_size = st.st_size;
+ bonus_data.resize(bonus_size);
+ if (!android::base::ReadFully(fd, bonus_data.data(), bonus_size)) {
printf("failed to read bonus file %s: %s\n", argv[2], strerror(errno));
return 1;
}
- fclose(f);
argc -= 2;
argv += 2;
}
if (argc != 4) {
- usage:
printf("usage: %s [-z] [-b <bonus-file>] <src-img> <tgt-img> <patch-file>\n",
argv[0]);
return 2;
}
- int num_src_chunks;
- ImageChunk* src_chunks;
- int num_tgt_chunks;
- ImageChunk* tgt_chunks;
- int i;
+ std::vector<ImageChunk> src_chunks;
+ std::vector<ImageChunk> tgt_chunks;
+ std::vector<uint8_t> src_file;
+ std::vector<uint8_t> tgt_file;
if (zip_mode) {
- if (ReadZip(argv[1], &num_src_chunks, &src_chunks, 1) == NULL) {
+ if (!ReadZip(argv[1], &src_chunks, &src_file, true)) {
printf("failed to break apart source zip file\n");
return 1;
}
- if (ReadZip(argv[2], &num_tgt_chunks, &tgt_chunks, 0) == NULL) {
+ if (!ReadZip(argv[2], &tgt_chunks, &tgt_file, false)) {
printf("failed to break apart target zip file\n");
return 1;
}
} else {
- if (ReadImage(argv[1], &num_src_chunks, &src_chunks) == NULL) {
+ if (!ReadImage(argv[1], &src_chunks, &src_file)) {
printf("failed to break apart source image\n");
return 1;
}
- if (ReadImage(argv[2], &num_tgt_chunks, &tgt_chunks) == NULL) {
+ if (!ReadImage(argv[2], &tgt_chunks, &tgt_file)) {
printf("failed to break apart target image\n");
return 1;
}
@@ -878,51 +913,47 @@
// Verify that the source and target images have the same chunk
// structure (ie, the same sequence of deflate and normal chunks).
- if (!zip_mode) {
- // Merge the gzip header and footer in with any adjacent
- // normal chunks.
- MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks);
- MergeAdjacentNormalChunks(src_chunks, &num_src_chunks);
- }
+ // Merge the gzip header and footer in with any adjacent normal chunks.
+ MergeAdjacentNormalChunks(&tgt_chunks);
+ MergeAdjacentNormalChunks(&src_chunks);
- if (num_src_chunks != num_tgt_chunks) {
+ if (src_chunks.size() != tgt_chunks.size()) {
printf("source and target don't have same number of chunks!\n");
printf("source chunks:\n");
- DumpChunks(src_chunks, num_src_chunks);
+ DumpChunks(src_chunks);
printf("target chunks:\n");
- DumpChunks(tgt_chunks, num_tgt_chunks);
+ DumpChunks(tgt_chunks);
return 1;
}
- for (i = 0; i < num_src_chunks; ++i) {
- if (src_chunks[i].type != tgt_chunks[i].type) {
- printf("source and target don't have same chunk "
- "structure! (chunk %d)\n", i);
+ for (size_t i = 0; i < src_chunks.size(); ++i) {
+ if (src_chunks[i].GetType() != tgt_chunks[i].GetType()) {
+ printf("source and target don't have same chunk structure! (chunk %zu)\n", i);
printf("source chunks:\n");
- DumpChunks(src_chunks, num_src_chunks);
+ DumpChunks(src_chunks);
printf("target chunks:\n");
- DumpChunks(tgt_chunks, num_tgt_chunks);
+ DumpChunks(tgt_chunks);
return 1;
}
}
}
- for (i = 0; i < num_tgt_chunks; ++i) {
- if (tgt_chunks[i].type == CHUNK_DEFLATE) {
+ for (size_t i = 0; i < tgt_chunks.size(); ++i) {
+ if (tgt_chunks[i].GetType() == CHUNK_DEFLATE) {
// Confirm that given the uncompressed chunk data in the target, we
// can recompress it and get exactly the same bits as are in the
// input target image. If this fails, treat the chunk as a normal
// non-deflated chunk.
- if (ReconstructDeflateChunk(tgt_chunks+i) < 0) {
- printf("failed to reconstruct target deflate chunk %d [%s]; "
- "treating as normal\n", i, tgt_chunks[i].filename);
- ChangeDeflateChunkToNormal(tgt_chunks+i);
+ if (!tgt_chunks[i].ReconstructDeflateChunk()) {
+ printf("failed to reconstruct target deflate chunk %zu [%s]; treating as normal\n", i,
+ tgt_chunks[i].GetEntryName().c_str());
+ tgt_chunks[i].ChangeDeflateChunkToNormal();
if (zip_mode) {
- ImageChunk* src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks);
- if (src) {
- ChangeDeflateChunkToNormal(src);
+ ImageChunk* src = FindChunkByName(tgt_chunks[i].GetEntryName(), src_chunks);
+ if (src != nullptr) {
+ src->ChangeDeflateChunkToNormal();
}
} else {
- ChangeDeflateChunkToNormal(src_chunks+i);
+ src_chunks[i].ChangeDeflateChunkToNormal();
}
continue;
}
@@ -935,16 +966,16 @@
// data.
ImageChunk* src;
if (zip_mode) {
- src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks);
+ src = FindChunkByName(tgt_chunks[i].GetEntryName(), src_chunks);
} else {
- src = src_chunks+i;
+ src = &src_chunks[i];
}
- if (src == NULL || AreChunksEqual(tgt_chunks+i, src)) {
- ChangeDeflateChunkToNormal(tgt_chunks+i);
- if (src) {
- ChangeDeflateChunkToNormal(src);
- }
+ if (src == nullptr) {
+ tgt_chunks[i].ChangeDeflateChunkToNormal();
+ } else if (tgt_chunks[i] == *src) {
+ tgt_chunks[i].ChangeDeflateChunkToNormal();
+ src->ChangeDeflateChunkToNormal();
}
}
}
@@ -954,14 +985,15 @@
// For zips, we only need to do this to the target: deflated
// chunks are matched via filename, and normal chunks are patched
// using the entire source file as the source.
- MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks);
+ MergeAdjacentNormalChunks(&tgt_chunks);
+
} else {
// For images, we need to maintain the parallel structure of the
// chunk lists, so do the merging in both the source and target
// lists.
- MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks);
- MergeAdjacentNormalChunks(src_chunks, &num_src_chunks);
- if (num_src_chunks != num_tgt_chunks) {
+ MergeAdjacentNormalChunks(&tgt_chunks);
+ MergeAdjacentNormalChunks(&src_chunks);
+ if (src_chunks.size() != tgt_chunks.size()) {
// This shouldn't happen.
printf("merging normal chunks went awry\n");
return 1;
@@ -971,35 +1003,43 @@
// Compute bsdiff patches for each chunk's data (the uncompressed
// data, in the case of deflate chunks).
- DumpChunks(src_chunks, num_src_chunks);
+ DumpChunks(src_chunks);
- printf("Construct patches for %d chunks...\n", num_tgt_chunks);
- unsigned char** patch_data = reinterpret_cast<unsigned char**>(malloc(
- num_tgt_chunks * sizeof(unsigned char*)));
- size_t* patch_size = reinterpret_cast<size_t*>(malloc(num_tgt_chunks * sizeof(size_t)));
- for (i = 0; i < num_tgt_chunks; ++i) {
+ printf("Construct patches for %zu chunks...\n", tgt_chunks.size());
+ std::vector<std::vector<uint8_t>> patch_data(tgt_chunks.size());
+ saidx_t* bsdiff_cache = nullptr;
+ for (size_t i = 0; i < tgt_chunks.size(); ++i) {
if (zip_mode) {
ImageChunk* src;
- if (tgt_chunks[i].type == CHUNK_DEFLATE &&
- (src = FindChunkByName(tgt_chunks[i].filename, src_chunks,
- num_src_chunks))) {
- patch_data[i] = MakePatch(src, tgt_chunks+i, patch_size+i);
+ if (tgt_chunks[i].GetType() == CHUNK_DEFLATE &&
+ (src = FindChunkByName(tgt_chunks[i].GetEntryName(), src_chunks))) {
+ if (!MakePatch(src, &tgt_chunks[i], &patch_data[i], nullptr)) {
+ printf("Failed to generate patch for target chunk %zu: ", i);
+ return 1;
+ }
} else {
- patch_data[i] = MakePatch(src_chunks, tgt_chunks+i, patch_size+i);
+ if (!MakePatch(&src_chunks[0], &tgt_chunks[i], &patch_data[i], &bsdiff_cache)) {
+ printf("Failed to generate patch for target chunk %zu: ", i);
+ return 1;
+ }
}
} else {
- if (i == 1 && bonus_data) {
- printf(" using %zu bytes of bonus data for chunk %d\n", bonus_size, i);
- src_chunks[i].data = reinterpret_cast<unsigned char*>(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;
- }
+ if (i == 1 && !bonus_data.empty()) {
+ printf(" using %zu bytes of bonus data for chunk %zu\n", bonus_data.size(), i);
+ src_chunks[i].SetBonusData(bonus_data);
+ }
- patch_data[i] = MakePatch(src_chunks+i, tgt_chunks+i, patch_size+i);
+ if (!MakePatch(&src_chunks[i], &tgt_chunks[i], &patch_data[i], nullptr)) {
+ printf("Failed to generate patch for target chunk %zu: ", i);
+ return 1;
+ }
}
- printf("patch %3d is %zu bytes (of %zu)\n",
- i, patch_size[i], tgt_chunks[i].source_len);
+ printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data[i].size(),
+ src_chunks[i].GetRawDataLength());
+ }
+
+ if (bsdiff_cache != nullptr) {
+ free(bsdiff_cache);
}
// Figure out how big the imgdiff file header is going to be, so
@@ -1007,77 +1047,38 @@
// within the file.
size_t total_header_size = 12;
- for (i = 0; i < num_tgt_chunks; ++i) {
- total_header_size += 4;
- switch (tgt_chunks[i].type) {
- case CHUNK_NORMAL:
- total_header_size += 8*3;
- break;
- case CHUNK_DEFLATE:
- total_header_size += 8*5 + 4*5;
- break;
- case CHUNK_RAW:
- total_header_size += 4 + patch_size[i];
- break;
- }
+ for (size_t i = 0; i < tgt_chunks.size(); ++i) {
+ total_header_size += tgt_chunks[i].GetHeaderSize(patch_data[i].size());
}
size_t offset = total_header_size;
- FILE* f = fopen(argv[3], "wb");
+ android::base::unique_fd patch_fd(open(argv[3], O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR));
+ if (patch_fd == -1) {
+ printf("failed to open \"%s\": %s\n", argv[3], strerror(errno));
+ return 1;
+ }
// Write out the headers.
-
- fwrite("IMGDIFF2", 1, 8, f);
- Write4(num_tgt_chunks, f);
- for (i = 0; i < num_tgt_chunks; ++i) {
- Write4(tgt_chunks[i].type, f);
-
- switch (tgt_chunks[i].type) {
- case CHUNK_NORMAL:
- 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);
- Write8(offset, f);
- offset += patch_size[i];
- break;
-
- case CHUNK_DEFLATE:
- 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);
- Write8(tgt_chunks[i].source_len, f);
- Write8(offset, f);
- Write8(tgt_chunks[i].source_uncompressed_len, f);
- Write8(tgt_chunks[i].len, f);
- Write4(tgt_chunks[i].level, f);
- Write4(tgt_chunks[i].method, f);
- Write4(tgt_chunks[i].windowBits, f);
- Write4(tgt_chunks[i].memLevel, f);
- Write4(tgt_chunks[i].strategy, f);
- offset += patch_size[i];
- break;
-
- case CHUNK_RAW:
- 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);
- break;
- }
+ if (!android::base::WriteStringToFd("IMGDIFF2", patch_fd)) {
+ printf("failed to write \"IMGDIFF2\" to \"%s\": %s\n", argv[3], strerror(errno));
+ return 1;
+ }
+ Write4(patch_fd, static_cast<int32_t>(tgt_chunks.size()));
+ for (size_t i = 0; i < tgt_chunks.size(); ++i) {
+ printf("chunk %zu: ", i);
+ offset = tgt_chunks[i].WriteHeaderToFd(patch_fd, patch_data[i], offset);
}
// Append each chunk's bsdiff patch, in order.
-
- for (i = 0; i < num_tgt_chunks; ++i) {
- if (tgt_chunks[i].type != CHUNK_RAW) {
- fwrite(patch_data[i], 1, patch_size[i], f);
+ for (size_t i = 0; i < tgt_chunks.size(); ++i) {
+ if (tgt_chunks[i].GetType() != CHUNK_RAW) {
+ if (!android::base::WriteFully(patch_fd, patch_data[i].data(), patch_data[i].size())) {
+ CHECK(false) << "failed to write " << patch_data[i].size() << " bytes patch for chunk "
+ << i;
+ }
}
}
- fclose(f);
-
return 0;
}
diff --git a/updater/install.h b/applypatch/imgdiff_main.cpp
similarity index 64%
copy from updater/install.h
copy to applypatch/imgdiff_main.cpp
index 70e3434..7d5bdf9 100644
--- a/updater/install.h
+++ b/applypatch/imgdiff_main.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Android Open Source Project
+ * 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.
@@ -14,14 +14,8 @@
* limitations under the License.
*/
-#ifndef _UPDATER_INSTALL_H_
-#define _UPDATER_INSTALL_H_
+#include "applypatch/imgdiff.h"
-void RegisterInstallFunctions();
-
-// uiPrintf function prints msg to screen as well as logs
-void uiPrintf(State* state, const char* format, ...);
-
-static int make_parents(char* name);
-
-#endif
+int main(int argc, char** argv) {
+ return imgdiff(argc, const_cast<const char**>(argv));
+}
diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp
index d175d63..adcc61f 100644
--- a/applypatch/imgpatch.cpp
+++ b/applypatch/imgpatch.cpp
@@ -14,31 +14,41 @@
* limitations under the License.
*/
-// See imgdiff.c in this directory for a description of the patch file
+// See imgdiff.cpp in this directory for a description of the patch file
// format.
+#include <applypatch/imgpatch.h>
+
+#include <errno.h>
#include <stdio.h>
+#include <string.h>
#include <sys/cdefs.h>
#include <sys/stat.h>
-#include <errno.h>
#include <unistd.h>
-#include <string.h>
+#include <string>
#include <vector>
-#include "zlib.h"
-#include "openssl/sha.h"
-#include "applypatch.h"
-#include "imgdiff.h"
-#include "utils.h"
+#include <applypatch/applypatch.h>
+#include <applypatch/imgdiff.h>
+#include <android-base/memory.h>
+#include <openssl/sha.h>
+#include <zlib.h>
+
+static inline int64_t Read8(const void *address) {
+ return android::base::get_unaligned<int64_t>(address);
+}
+
+static inline int32_t Read4(const void *address) {
+ return android::base::get_unaligned<int32_t>(address);
+}
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<char*>(const_cast<unsigned char*>(patch_data))};
- return ApplyImagePatch(
- old_data, old_size, &patch, sink, token, nullptr, nullptr);
+ Value patch(VAL_BLOB, std::string(reinterpret_cast<const char*>(patch_data), patch_size));
+
+ return ApplyImagePatch(old_data, old_size, &patch, sink, token, nullptr, nullptr);
}
/*
@@ -47,203 +57,204 @@
* 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,
- const Value* patch,
- SinkFn sink, void* token, SHA_CTX* ctx,
- const Value* bonus_data) {
- ssize_t pos = 12;
- char* header = patch->data;
- if (patch->size < 12) {
- printf("patch too short to contain header\n");
+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) {
+ if (patch->data.size() < 12) {
+ printf("patch too short to contain header\n");
+ return -1;
+ }
+
+ // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
+ // (IMGDIFF1, which is no longer supported, used CHUNK_NORMAL and
+ // CHUNK_GZIP.)
+ size_t pos = 12;
+ const char* header = &patch->data[0];
+ if (memcmp(header, "IMGDIFF2", 8) != 0) {
+ printf("corrupt patch file header (magic number)\n");
+ return -1;
+ }
+
+ int num_chunks = Read4(header + 8);
+
+ for (int i = 0; i < num_chunks; ++i) {
+ // each chunk's header record starts with 4 bytes.
+ if (pos + 4 > patch->data.size()) {
+ printf("failed to read chunk %d record\n", i);
+ return -1;
+ }
+ int type = Read4(&patch->data[pos]);
+ pos += 4;
+
+ if (type == CHUNK_NORMAL) {
+ const char* normal_header = &patch->data[pos];
+ pos += 24;
+ if (pos > patch->data.size()) {
+ printf("failed to read chunk %d normal header data\n", i);
return -1;
- }
+ }
- // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
- // (IMGDIFF1, which is no longer supported, used CHUNK_NORMAL and
- // CHUNK_GZIP.)
- if (memcmp(header, "IMGDIFF2", 8) != 0) {
- printf("corrupt patch file header (magic number)\n");
+ size_t src_start = static_cast<size_t>(Read8(normal_header));
+ size_t src_len = static_cast<size_t>(Read8(normal_header + 8));
+ size_t patch_offset = static_cast<size_t>(Read8(normal_header + 16));
+
+ if (src_start + src_len > static_cast<size_t>(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) {
+ const char* raw_header = &patch->data[pos];
+ pos += 4;
+ if (pos > patch->data.size()) {
+ printf("failed to read chunk %d raw header data\n", i);
+ return -1;
+ }
- int num_chunks = Read4(header+8);
+ ssize_t data_len = Read4(raw_header);
- int i;
- for (i = 0; i < num_chunks; ++i) {
- // each chunk's header record starts with 4 bytes.
- if (pos + 4 > patch->size) {
- printf("failed to read chunk %d record\n", i);
- return -1;
+ if (pos + data_len > patch->data.size()) {
+ printf("failed to read chunk %d raw data\n", i);
+ return -1;
+ }
+ if (ctx) SHA1_Update(ctx, &patch->data[pos], data_len);
+ if (sink(reinterpret_cast<const unsigned char*>(&patch->data[pos]), data_len, token) !=
+ data_len) {
+ printf("failed to write chunk %d raw data\n", i);
+ return -1;
+ }
+ pos += data_len;
+ } else if (type == CHUNK_DEFLATE) {
+ // deflate chunks have an additional 60 bytes in their chunk header.
+ const char* deflate_header = &patch->data[pos];
+ pos += 60;
+ if (pos > patch->data.size()) {
+ printf("failed to read chunk %d deflate header data\n", i);
+ return -1;
+ }
+
+ size_t src_start = static_cast<size_t>(Read8(deflate_header));
+ size_t src_len = static_cast<size_t>(Read8(deflate_header + 8));
+ size_t patch_offset = static_cast<size_t>(Read8(deflate_header + 16));
+ size_t expanded_len = static_cast<size_t>(Read8(deflate_header + 24));
+ size_t target_len = static_cast<size_t>(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<size_t>(old_size)) {
+ printf("source data too short\n");
+ return -1;
+ }
+
+ // Decompress the source data; the chunk header tells us exactly
+ // how big we expect it to be when decompressed.
+
+ // Note: expanded_len will include the bonus data size if
+ // the patch was constructed with bonus data. The
+ // deflation will come up 'bonus_size' bytes short; these
+ // must be appended from the bonus_data value.
+ size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->data.size() : 0;
+
+ std::vector<unsigned char> expanded_source(expanded_len);
+
+ // inflate() doesn't like strm.next_out being a nullptr even with
+ // avail_out being zero (Z_STREAM_ERROR).
+ if (expanded_len != 0) {
+ z_stream strm;
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = src_len;
+ strm.next_in = old_data + src_start;
+ strm.avail_out = expanded_len;
+ strm.next_out = expanded_source.data();
+
+ int ret = inflateInit2(&strm, -15);
+ if (ret != Z_OK) {
+ printf("failed to init source inflation: %d\n", ret);
+ return -1;
}
- int type = Read4(patch->data + pos);
- pos += 4;
- if (type == CHUNK_NORMAL) {
- char* normal_header = patch->data + pos;
- pos += 24;
- if (pos > patch->size) {
- printf("failed to read chunk %d normal header data\n", i);
- return -1;
- }
-
- size_t src_start = Read8(normal_header);
- size_t src_len = Read8(normal_header+8);
- size_t patch_offset = Read8(normal_header+16);
-
- if (src_start + src_len > static_cast<size_t>(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) {
- char* raw_header = patch->data + pos;
- pos += 4;
- if (pos > patch->size) {
- printf("failed to read chunk %d raw header data\n", i);
- return -1;
- }
-
- ssize_t data_len = Read4(raw_header);
-
- if (pos + data_len > patch->size) {
- printf("failed to read chunk %d raw data\n", i);
- return -1;
- }
- if (ctx) SHA1_Update(ctx, patch->data + pos, data_len);
- if (sink((unsigned char*)patch->data + pos,
- data_len, token) != data_len) {
- printf("failed to write chunk %d raw data\n", i);
- return -1;
- }
- pos += data_len;
- } else if (type == CHUNK_DEFLATE) {
- // deflate chunks have an additional 60 bytes in their chunk header.
- char* deflate_header = patch->data + pos;
- pos += 60;
- if (pos > patch->size) {
- printf("failed to read chunk %d deflate header data\n", i);
- return -1;
- }
-
- size_t src_start = Read8(deflate_header);
- size_t src_len = Read8(deflate_header+8);
- size_t patch_offset = Read8(deflate_header+16);
- size_t expanded_len = Read8(deflate_header+24);
- 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<size_t>(old_size)) {
- printf("source data too short\n");
- return -1;
- }
-
- // Decompress the source data; the chunk header tells us exactly
- // how big we expect it to be when decompressed.
-
- // Note: expanded_len will include the bonus data size if
- // the patch was constructed with bonus data. The
- // deflation will come up 'bonus_size' bytes short; these
- // must be appended from the bonus_data value.
- size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->size : 0;
-
- std::vector<unsigned char> expanded_source(expanded_len);
-
- // inflate() doesn't like strm.next_out being a nullptr even with
- // avail_out being zero (Z_STREAM_ERROR).
- if (expanded_len != 0) {
- z_stream strm;
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- strm.avail_in = src_len;
- strm.next_in = (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;
- }
-
- // Because we've provided enough room to accommodate the output
- // data, we expect one call to inflate() to suffice.
- ret = inflate(&strm, Z_SYNC_FLUSH);
- if (ret != Z_STREAM_END) {
- printf("source inflation returned %d\n", ret);
- return -1;
- }
- // We should have filled the output buffer exactly, except
- // for the bonus_size.
- if (strm.avail_out != bonus_size) {
- printf("source inflation short by %zu bytes\n", strm.avail_out-bonus_size);
- return -1;
- }
- inflateEnd(&strm);
-
- if (bonus_size) {
- memcpy(expanded_source.data() + (expanded_len - bonus_size),
- bonus_data->data, bonus_size);
- }
- }
-
- // Next, apply the bsdiff patch (in memory) to the uncompressed
- // data.
- std::vector<unsigned char> uncompressed_target_data;
- if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len,
- patch, patch_offset,
- &uncompressed_target_data) != 0) {
- return -1;
- }
-
- // Now compress the target data and append it to the output.
-
- // we're done with the expanded_source data buffer, so we'll
- // reuse that memory to receive the output of deflate.
- if (expanded_source.size() < 32768U) {
- expanded_source.resize(32768U);
- }
-
- {
- std::vector<unsigned char>& temp_data = expanded_source;
-
- // 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;
- }
- 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;
-
- 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;
+ // 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);
- return 0;
+ if (bonus_size) {
+ memcpy(expanded_source.data() + (expanded_len - bonus_size), &bonus_data->data[0],
+ bonus_size);
+ }
+ }
+
+ // Next, apply the bsdiff patch (in memory) to the uncompressed data.
+ std::vector<unsigned char> uncompressed_target_data;
+ // TODO(senj): Remove the only usage of ApplyBSDiffPatchMem here,
+ // replace it with ApplyBSDiffPatch with a custom sink function that
+ // wraps the given sink function to stream output to save memory.
+ if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len, patch, patch_offset,
+ &uncompressed_target_data) != 0) {
+ return -1;
+ }
+ if (uncompressed_target_data.size() != target_len) {
+ printf("expected target len to be %zu, but it's %zu\n", target_len,
+ uncompressed_target_data.size());
+ return -1;
+ }
+
+ // Now compress the target data and append it to the output.
+
+ // we're done with the expanded_source data buffer, so we'll
+ // reuse that memory to receive the output of deflate.
+ if (expanded_source.size() < 32768U) {
+ expanded_source.resize(32768U);
+ }
+
+ {
+ std::vector<unsigned char>& temp_data = expanded_source;
+
+ // 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;
+ }
+ 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;
+
+ if (sink(temp_data.data(), have, token) != have) {
+ printf("failed to write %zd compressed bytes to output\n", 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;
+ }
+ }
+
+ return 0;
}
diff --git a/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h
similarity index 85%
rename from applypatch/applypatch.h
rename to applypatch/include/applypatch/applypatch.h
index f392c55..4489dec 100644
--- a/applypatch/applypatch.h
+++ b/applypatch/include/applypatch/applypatch.h
@@ -17,11 +17,15 @@
#ifndef _APPLYPATCH_H
#define _APPLYPATCH_H
+#include <stdint.h>
#include <sys/stat.h>
+#include <memory>
+#include <string>
#include <vector>
-#include "openssl/sha.h"
+#include <openssl/sha.h>
+
#include "edify/expr.h"
struct FileContents {
@@ -39,33 +43,28 @@
typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*);
-// applypatch.c
+// applypatch.cpp
int ShowLicenses();
size_t FreeSpaceForFile(const char* filename);
int CacheSizeCheck(size_t bytes);
int ParseSha1(const char* str, uint8_t* digest);
-int applypatch_flash(const char* source_filename, const char* target_filename,
- const char* target_sha1_str, size_t target_size);
int applypatch(const char* source_filename,
const char* target_filename,
const char* target_sha1_str,
size_t target_size,
- int num_patches,
- char** const patch_sha1_str,
- Value** patch_data,
- Value* bonus_data);
+ const std::vector<std::string>& patch_sha1_str,
+ const std::vector<std::unique_ptr<Value>>& patch_data,
+ const Value* bonus_data);
int applypatch_check(const char* filename,
- int num_patches,
- char** const patch_sha1_str);
+ const std::vector<std::string>& patch_sha1_str);
+int applypatch_flash(const char* source_filename, const char* target_filename,
+ const char* target_sha1_str, size_t target_size);
int LoadFileContents(const char* filename, FileContents* file);
int SaveFileContents(const char* filename, const FileContents* file);
-void FreeFileContents(FileContents* file);
-int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str,
- int num_patches);
-// bsdiff.cpp
+// bspatch.cpp
void ShowBSDiffLicense();
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
const Value* patch, ssize_t patch_offset,
diff --git a/applypatch/imgdiff.h b/applypatch/include/applypatch/imgdiff.h
similarity index 69%
rename from applypatch/imgdiff.h
rename to applypatch/include/applypatch/imgdiff.h
index f2069b4..22cbd4f 100644
--- a/applypatch/imgdiff.h
+++ b/applypatch/include/applypatch/imgdiff.h
@@ -14,17 +14,26 @@
* limitations under the License.
*/
+#ifndef _APPLYPATCH_IMGDIFF_H
+#define _APPLYPATCH_IMGDIFF_H
+
+#include <stddef.h>
+
// Image patch chunk types
-#define CHUNK_NORMAL 0
-#define CHUNK_GZIP 1 // version 1 only
-#define CHUNK_DEFLATE 2 // version 2 only
-#define CHUNK_RAW 3 // version 2 only
+#define CHUNK_NORMAL 0
+#define CHUNK_GZIP 1 // version 1 only
+#define CHUNK_DEFLATE 2 // version 2 only
+#define CHUNK_RAW 3 // version 2 only
// The gzip header size is actually variable, but we currently don't
// support gzipped data with any of the optional fields, so for now it
// will always be ten bytes. See RFC 1952 for the definition of the
// gzip format.
-#define GZIP_HEADER_LEN 10
+static constexpr size_t GZIP_HEADER_LEN = 10;
// The gzip footer size really is fixed.
-#define GZIP_FOOTER_LEN 8
+static constexpr size_t GZIP_FOOTER_LEN = 8;
+
+int imgdiff(int argc, const char** argv);
+
+#endif // _APPLYPATCH_IMGDIFF_H
diff --git a/applypatch/include/applypatch/imgpatch.h b/applypatch/include/applypatch/imgpatch.h
index 64d9aa9..6549f79 100644
--- a/applypatch/include/applypatch/imgpatch.h
+++ b/applypatch/include/applypatch/imgpatch.h
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-#ifndef _IMGPATCH_H
-#define _IMGPATCH_H
+#ifndef _APPLYPATCH_IMGPATCH_H
+#define _APPLYPATCH_IMGPATCH_H
-typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*);
+#include <sys/types.h>
+
+using SinkFn = ssize_t (*)(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
+#endif // _APPLYPATCH_IMGPATCH_H
diff --git a/applypatch/libimgpatch.pc b/applypatch/libimgpatch.pc
new file mode 100644
index 0000000..e500293
--- /dev/null
+++ b/applypatch/libimgpatch.pc
@@ -0,0 +1,6 @@
+# This file is for libimgpatch in Chrome OS.
+
+Name: libimgpatch
+Description: Apply imgdiff patch
+Version: 0.0.1
+Libs: -limgpatch -lbz2 -lz
diff --git a/applypatch/utils.cpp b/applypatch/utils.cpp
deleted file mode 100644
index 4a80be7..0000000
--- a/applypatch/utils.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-
-#include "utils.h"
-
-/** Write a 4-byte value to f in little-endian order. */
-void Write4(int value, FILE* f) {
- fputc(value & 0xff, f);
- fputc((value >> 8) & 0xff, f);
- fputc((value >> 16) & 0xff, f);
- fputc((value >> 24) & 0xff, f);
-}
-
-/** Write an 8-byte value to f in little-endian order. */
-void Write8(long long value, FILE* f) {
- fputc(value & 0xff, f);
- fputc((value >> 8) & 0xff, f);
- fputc((value >> 16) & 0xff, f);
- fputc((value >> 24) & 0xff, f);
- fputc((value >> 32) & 0xff, f);
- fputc((value >> 40) & 0xff, f);
- fputc((value >> 48) & 0xff, f);
- fputc((value >> 56) & 0xff, f);
-}
-
-int Read2(void* pv) {
- unsigned char* p = reinterpret_cast<unsigned char*>(pv);
- return (int)(((unsigned int)p[1] << 8) |
- (unsigned int)p[0]);
-}
-
-int Read4(void* pv) {
- unsigned char* p = reinterpret_cast<unsigned char*>(pv);
- return (int)(((unsigned int)p[3] << 24) |
- ((unsigned int)p[2] << 16) |
- ((unsigned int)p[1] << 8) |
- (unsigned int)p[0]);
-}
-
-long long Read8(void* pv) {
- unsigned char* p = reinterpret_cast<unsigned char*>(pv);
- return (long long)(((unsigned long long)p[7] << 56) |
- ((unsigned long long)p[6] << 48) |
- ((unsigned long long)p[5] << 40) |
- ((unsigned long long)p[4] << 32) |
- ((unsigned long long)p[3] << 24) |
- ((unsigned long long)p[2] << 16) |
- ((unsigned long long)p[1] << 8) |
- (unsigned long long)p[0]);
-}
diff --git a/applypatch/utils.h b/applypatch/utils.h
deleted file mode 100644
index bc97f17..0000000
--- a/applypatch/utils.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _BUILD_TOOLS_APPLYPATCH_UTILS_H
-#define _BUILD_TOOLS_APPLYPATCH_UTILS_H
-
-#include <stdio.h>
-
-// Read and write little-endian values of various sizes.
-
-void Write4(int value, FILE* f);
-void Write8(long long value, FILE* f);
-int Read2(void* p);
-int Read4(void* p);
-long long Read8(void* p);
-
-#endif // _BUILD_TOOLS_APPLYPATCH_UTILS_H
diff --git a/asn1_decoder.cpp b/asn1_decoder.cpp
index e7aef78..285214f 100644
--- a/asn1_decoder.cpp
+++ b/asn1_decoder.cpp
@@ -14,178 +14,145 @@
* limitations under the License.
*/
-#include <malloc.h>
-#include <stdint.h>
-#include <string.h>
-
#include "asn1_decoder.h"
+#include <stdint.h>
-typedef struct asn1_context {
- size_t length;
- uint8_t* p;
- int app_type;
-} asn1_context_t;
-
-
-static const int kMaskConstructed = 0xE0;
-static const int kMaskTag = 0x7F;
-static const int kMaskAppType = 0x1F;
-
-static const int kTagOctetString = 0x04;
-static const int kTagOid = 0x06;
-static const int kTagSequence = 0x30;
-static const int kTagSet = 0x31;
-static const int kTagConstructed = 0xA0;
-
-asn1_context_t* asn1_context_new(uint8_t* buffer, size_t length) {
- asn1_context_t* ctx = (asn1_context_t*) calloc(1, sizeof(asn1_context_t));
- if (ctx == NULL) {
- return NULL;
- }
- ctx->p = buffer;
- ctx->length = length;
- return ctx;
+int asn1_context::peek_byte() const {
+ if (length_ == 0) {
+ return -1;
+ }
+ return *p_;
}
-void asn1_context_free(asn1_context_t* ctx) {
- free(ctx);
+int asn1_context::get_byte() {
+ if (length_ == 0) {
+ return -1;
+ }
+
+ int byte = *p_;
+ p_++;
+ length_--;
+ return byte;
}
-static inline int peek_byte(asn1_context_t* ctx) {
- if (ctx->length <= 0) {
- return -1;
- }
- return *ctx->p;
+bool asn1_context::skip_bytes(size_t num_skip) {
+ if (length_ < num_skip) {
+ return false;
+ }
+ p_ += num_skip;
+ length_ -= num_skip;
+ return true;
}
-static inline int get_byte(asn1_context_t* ctx) {
- if (ctx->length <= 0) {
- return -1;
- }
- int byte = *ctx->p;
- ctx->p++;
- ctx->length--;
- return byte;
-}
-
-static inline bool skip_bytes(asn1_context_t* ctx, size_t num_skip) {
- if (ctx->length < num_skip) {
- return false;
- }
- ctx->p += num_skip;
- ctx->length -= num_skip;
+bool asn1_context::decode_length(size_t* out_len) {
+ int num_octets = get_byte();
+ if (num_octets == -1) {
+ return false;
+ }
+ if ((num_octets & 0x80) == 0x00) {
+ *out_len = num_octets;
return true;
-}
-
-static bool decode_length(asn1_context_t* ctx, size_t* out_len) {
- int num_octets = get_byte(ctx);
- if (num_octets == -1) {
- return false;
+ }
+ num_octets &= kMaskTag;
+ if (static_cast<size_t>(num_octets) >= sizeof(size_t)) {
+ return false;
+ }
+ size_t length = 0;
+ for (int i = 0; i < num_octets; ++i) {
+ int byte = get_byte();
+ if (byte == -1) {
+ return false;
}
- if ((num_octets & 0x80) == 0x00) {
- *out_len = num_octets;
- return 1;
- }
- num_octets &= kMaskTag;
- if ((size_t)num_octets >= sizeof(size_t)) {
- return false;
- }
- size_t length = 0;
- for (int i = 0; i < num_octets; ++i) {
- int byte = get_byte(ctx);
- if (byte == -1) {
- return false;
- }
- length <<= 8;
- length += byte;
- }
- *out_len = length;
- return true;
+ length <<= 8;
+ length += byte;
+ }
+ *out_len = length;
+ return true;
}
/**
* Returns the constructed type and advances the pointer. E.g. A0 -> 0
*/
-asn1_context_t* asn1_constructed_get(asn1_context_t* ctx) {
- int type = get_byte(ctx);
- if (type == -1 || (type & kMaskConstructed) != kTagConstructed) {
- return NULL;
- }
+asn1_context* asn1_context::asn1_constructed_get() {
+ int type = get_byte();
+ if (type == -1 || (type & kMaskConstructed) != kTagConstructed) {
+ return nullptr;
+ }
+ size_t length;
+ if (!decode_length(&length) || length > length_) {
+ return nullptr;
+ }
+ asn1_context* app_ctx = new asn1_context(p_, length);
+ app_ctx->app_type_ = type & kMaskAppType;
+ return app_ctx;
+}
+
+bool asn1_context::asn1_constructed_skip_all() {
+ int byte = peek_byte();
+ while (byte != -1 && (byte & kMaskConstructed) == kTagConstructed) {
+ skip_bytes(1);
size_t length;
- if (!decode_length(ctx, &length) || length > ctx->length) {
- return NULL;
+ if (!decode_length(&length) || !skip_bytes(length)) {
+ return false;
}
- asn1_context_t* app_ctx = asn1_context_new(ctx->p, length);
- app_ctx->app_type = type & kMaskAppType;
- return app_ctx;
+ byte = peek_byte();
+ }
+ return byte != -1;
}
-bool asn1_constructed_skip_all(asn1_context_t* ctx) {
- int byte = peek_byte(ctx);
- while (byte != -1 && (byte & kMaskConstructed) == kTagConstructed) {
- skip_bytes(ctx, 1);
- size_t length;
- if (!decode_length(ctx, &length) || !skip_bytes(ctx, length)) {
- return false;
- }
- byte = peek_byte(ctx);
- }
- return byte != -1;
+int asn1_context::asn1_constructed_type() const {
+ return app_type_;
}
-int asn1_constructed_type(asn1_context_t* ctx) {
- return ctx->app_type;
+asn1_context* asn1_context::asn1_sequence_get() {
+ if ((get_byte() & kMaskTag) != kTagSequence) {
+ return nullptr;
+ }
+ size_t length;
+ if (!decode_length(&length) || length > length_) {
+ return nullptr;
+ }
+ return new asn1_context(p_, length);
}
-asn1_context_t* asn1_sequence_get(asn1_context_t* ctx) {
- if ((get_byte(ctx) & kMaskTag) != kTagSequence) {
- return NULL;
- }
- size_t length;
- if (!decode_length(ctx, &length) || length > ctx->length) {
- return NULL;
- }
- return asn1_context_new(ctx->p, length);
+asn1_context* asn1_context::asn1_set_get() {
+ if ((get_byte() & kMaskTag) != kTagSet) {
+ return nullptr;
+ }
+ size_t length;
+ if (!decode_length(&length) || length > length_) {
+ return nullptr;
+ }
+ return new asn1_context(p_, length);
}
-asn1_context_t* asn1_set_get(asn1_context_t* ctx) {
- if ((get_byte(ctx) & kMaskTag) != kTagSet) {
- return NULL;
- }
- size_t length;
- if (!decode_length(ctx, &length) || length > ctx->length) {
- return NULL;
- }
- return asn1_context_new(ctx->p, length);
+bool asn1_context::asn1_sequence_next() {
+ size_t length;
+ if (get_byte() == -1 || !decode_length(&length) || !skip_bytes(length)) {
+ return false;
+ }
+ return true;
}
-bool asn1_sequence_next(asn1_context_t* ctx) {
- size_t length;
- if (get_byte(ctx) == -1 || !decode_length(ctx, &length) || !skip_bytes(ctx, length)) {
- return false;
- }
- return true;
+bool asn1_context::asn1_oid_get(const uint8_t** oid, size_t* length) {
+ if (get_byte() != kTagOid) {
+ return false;
+ }
+ if (!decode_length(length) || *length == 0 || *length > length_) {
+ return false;
+ }
+ *oid = p_;
+ return true;
}
-bool asn1_oid_get(asn1_context_t* ctx, uint8_t** oid, size_t* length) {
- if (get_byte(ctx) != kTagOid) {
- return false;
- }
- if (!decode_length(ctx, length) || *length == 0 || *length > ctx->length) {
- return false;
- }
- *oid = ctx->p;
- return true;
-}
-
-bool asn1_octet_string_get(asn1_context_t* ctx, uint8_t** octet_string, size_t* length) {
- if (get_byte(ctx) != kTagOctetString) {
- return false;
- }
- if (!decode_length(ctx, length) || *length == 0 || *length > ctx->length) {
- return false;
- }
- *octet_string = ctx->p;
- return true;
+bool asn1_context::asn1_octet_string_get(const uint8_t** octet_string, size_t* length) {
+ if (get_byte() != kTagOctetString) {
+ return false;
+ }
+ if (!decode_length(length) || *length == 0 || *length > length_) {
+ return false;
+ }
+ *octet_string = p_;
+ return true;
}
diff --git a/asn1_decoder.h b/asn1_decoder.h
index b17141c..3e99211 100644
--- a/asn1_decoder.h
+++ b/asn1_decoder.h
@@ -14,23 +14,42 @@
* limitations under the License.
*/
-
#ifndef ASN1_DECODER_H_
#define ASN1_DECODER_H_
#include <stdint.h>
-typedef struct asn1_context asn1_context_t;
+class asn1_context {
+ public:
+ asn1_context(const uint8_t* buffer, size_t length) : p_(buffer), length_(length), app_type_(0) {}
+ int asn1_constructed_type() const;
+ asn1_context* asn1_constructed_get();
+ bool asn1_constructed_skip_all();
+ asn1_context* asn1_sequence_get();
+ asn1_context* asn1_set_get();
+ bool asn1_sequence_next();
+ bool asn1_oid_get(const uint8_t** oid, size_t* length);
+ bool asn1_octet_string_get(const uint8_t** octet_string, size_t* length);
-asn1_context_t* asn1_context_new(uint8_t* buffer, size_t length);
-void asn1_context_free(asn1_context_t* ctx);
-asn1_context_t* asn1_constructed_get(asn1_context_t* ctx);
-bool asn1_constructed_skip_all(asn1_context_t* ctx);
-int asn1_constructed_type(asn1_context_t* ctx);
-asn1_context_t* asn1_sequence_get(asn1_context_t* ctx);
-asn1_context_t* asn1_set_get(asn1_context_t* ctx);
-bool asn1_sequence_next(asn1_context_t* seq);
-bool asn1_oid_get(asn1_context_t* ctx, uint8_t** oid, size_t* length);
-bool asn1_octet_string_get(asn1_context_t* ctx, uint8_t** octet_string, size_t* length);
+ private:
+ static constexpr int kMaskConstructed = 0xE0;
+ static constexpr int kMaskTag = 0x7F;
+ static constexpr int kMaskAppType = 0x1F;
+
+ static constexpr int kTagOctetString = 0x04;
+ static constexpr int kTagOid = 0x06;
+ static constexpr int kTagSequence = 0x30;
+ static constexpr int kTagSet = 0x31;
+ static constexpr int kTagConstructed = 0xA0;
+
+ int peek_byte() const;
+ int get_byte();
+ bool skip_bytes(size_t num_skip);
+ bool decode_length(size_t* out_len);
+
+ const uint8_t* p_;
+ size_t length_;
+ int app_type_;
+};
#endif /* ASN1_DECODER_H_ */
diff --git a/bootloader_message/Android.mk b/bootloader_message/Android.mk
index 1d5c85f..0d84713 100644
--- a/bootloader_message/Android.mk
+++ b/bootloader_message/Android.mk
@@ -14,32 +14,16 @@
LOCAL_PATH := $(call my-dir)
-ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 25; echo $$?),0)
- include $(CLEAR_VARS)
- LOCAL_CLANG := true
- LOCAL_SRC_FILES := bootloader_message.cpp
- LOCAL_MODULE := libbootloader_message
- LOCAL_STATIC_LIBRARIES := libfs_mgr
- LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
- LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
- include $(BUILD_STATIC_LIBRARY)
-endif
-
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_SRC_FILES := bootloader_message.cpp
LOCAL_MODULE := libbootloader_message
-LOCAL_C_INCLUDES += bionic $(LOCAL_PATH)/include
-ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
- LOCAL_C_INCLUDES += external/stlport/stlport
- LOCAL_SHARED_LIBRARIES += libstlport
-else
- LOCAL_SHARED_LIBRARIES += libc++
+LOCAL_STATIC_LIBRARIES := libbase libfs_mgr
+LOCAL_CFLAGS := -Werror
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 26; echo $$?),0)
+ TARGET_GLOBAL_CFLAGS += -DUSE_OLD_BOOTLOADER_MESSAGE
+ CLANG_TARGET_GLOBAL_CFLAGS += -DUSE_OLD_BOOTLOADER_MESSAGE
endif
-LOCAL_CFLAGS := -DEXCLUDE_FS_MGR
-# ignore bootloader's factory reset command even when written to /misc
-ifeq ($(TW_IGNORE_MISC_WIPE_DATA), true)
- LOCAL_CFLAGS += -DIGNORE_MISC_WIPE_DATA
-endif
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-include $(BUILD_SHARED_LIBRARY)
+include $(BUILD_STATIC_LIBRARY)
diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp
index 4d1ce5b..dcaeb79 100644
--- a/bootloader_message/bootloader_message.cpp
+++ b/bootloader_message/bootloader_message.cpp
@@ -19,31 +19,18 @@
#include <errno.h>
#include <fcntl.h>
#include <string.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/system_properties.h>
#include <string>
#include <vector>
-/*
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
-*/
-#ifndef EXCLUDE_FS_MGR
#include <fs_mgr.h>
-#endif
-static std::string misc_blkdev;
+#ifdef USE_OLD_BOOTLOADER_MESSAGE
+#include <sys/system_properties.h>
-void set_misc_device(std::string name) {
- misc_blkdev = name;
-}
-
-#ifndef EXCLUDE_FS_MGR
static struct fstab* read_fstab(std::string* err) {
// The fstab path is always "/fstab.${ro.hardware}".
std::string fstab_path = "/fstab.";
@@ -62,23 +49,28 @@
#endif
static std::string get_misc_blk_device(std::string* err) {
-#ifdef EXCLUDE_FS_MGR
- return misc_blkdev;
-#else
+#ifdef USE_OLD_BOOTLOADER_MESSAGE
struct fstab* fstab = read_fstab(err);
- if (fstab == nullptr) {
+#else
+ std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+ fs_mgr_free_fstab);
+#endif
+ if (!fstab) {
+ *err = "failed to read default fstab";
return "";
}
+#ifdef USE_OLD_BOOTLOADER_MESSAGE
fstab_rec* record = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
+#else
+ fstab_rec* record = fs_mgr_get_entry_for_mount_point(fstab.get(), "/misc");
+#endif
if (record == nullptr) {
*err = "failed to find /misc partition";
return "";
}
return record->blk_device;
-#endif
}
-
// In recovery mode, recovery can get started and try to access the misc
// device before the kernel has actually created it.
static bool wait_for_device(const std::string& blk_device, std::string* err) {
@@ -90,127 +82,94 @@
struct stat buf;
ret = stat(blk_device.c_str(), &buf);
if (ret == -1) {
- char buffer[2048];
- sprintf(buffer, "failed to stat %s try %d: %s\n",
- blk_device.c_str(), tries, strerror(errno));
- *err += buffer;
- /*
*err += android::base::StringPrintf("failed to stat %s try %d: %s\n",
blk_device.c_str(), tries, strerror(errno));
- */
sleep(1);
}
} while (ret && tries < 10);
if (ret) {
- *err += "failed to stat " + blk_device + "\n";
- /*
*err += android::base::StringPrintf("failed to stat %s\n", blk_device.c_str());
- */
}
return ret == 0;
}
-static bool read_misc_partition(void* p, size_t size, size_t offset, std::string* err) {
- std::string misc_blk_device = get_misc_blk_device(err);
- if (misc_blk_device.empty()) {
- return false;
- }
+static bool read_misc_partition(void* p, size_t size, const std::string& misc_blk_device,
+ size_t offset, std::string* err) {
if (!wait_for_device(misc_blk_device, err)) {
return false;
}
- int fd(open(misc_blk_device.c_str(), O_RDONLY));
- if (fd < 0) {
- *err = "failed to open " + misc_blk_device + ": ";
- *err += strerror(errno);
- /*
+ android::base::unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY));
+ if (fd == -1) {
*err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
strerror(errno));
- */
return false;
}
if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
- *err = "failed to lseek " + misc_blk_device + ": ";
- *err += strerror(errno);
- close(fd);
- /*
*err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
strerror(errno));
- */
return false;
}
- if (read(fd, p, size) != size) {
- *err = "failed to read " + misc_blk_device + ": ";
- *err += strerror(errno);
- close(fd);
- /*
+ if (!android::base::ReadFully(fd, p, size)) {
*err = android::base::StringPrintf("failed to read %s: %s", misc_blk_device.c_str(),
strerror(errno));
- */
return false;
}
- close(fd);
return true;
}
-static bool write_misc_partition(const void* p, size_t size, size_t offset, std::string* err) {
- std::string misc_blk_device = get_misc_blk_device(err);
- if (misc_blk_device.empty()) {
- *err = "no misc device set";
- return false;
- }
- int fd = (open(misc_blk_device.c_str(), O_WRONLY | O_SYNC));
+static bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device,
+ size_t offset, std::string* err) {
+ android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY));
if (fd == -1) {
- *err = "failed to open " + misc_blk_device + ": ";
- *err += strerror(errno);
- /*
*err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
strerror(errno));
- */
return false;
}
if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
- *err = "failed to lseek " + misc_blk_device + ": ";
- *err += strerror(errno);
- close(fd);
- /*
*err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
strerror(errno));
- */
return false;
}
- if (write(fd, p, size) != size) {
- *err = "failed to write " + misc_blk_device + ": ";
- *err += strerror(errno);
- close(fd);
- /*
+ if (!android::base::WriteFully(fd, p, size)) {
*err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(),
strerror(errno));
- */
return false;
}
-
- // TODO: O_SYNC and fsync duplicates each other?
if (fsync(fd) == -1) {
- *err = "failed to fsync " + misc_blk_device + ": ";
- *err += strerror(errno);
- close(fd);
- /*
*err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(),
strerror(errno));
- */
return false;
}
- close(fd);
return true;
}
+bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
+ std::string* err) {
+ return read_misc_partition(boot, sizeof(*boot), misc_blk_device,
+ BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
+}
+
bool read_bootloader_message(bootloader_message* boot, std::string* err) {
- return read_misc_partition(boot, sizeof(*boot), BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
+ std::string misc_blk_device = get_misc_blk_device(err);
+ if (misc_blk_device.empty()) {
+ return false;
+ }
+ return read_bootloader_message_from(boot, misc_blk_device, err);
+}
+
+bool write_bootloader_message_to(const bootloader_message& boot, const std::string& misc_blk_device,
+ std::string* err) {
+ return write_misc_partition(&boot, sizeof(boot), misc_blk_device,
+ BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
}
bool write_bootloader_message(const bootloader_message& boot, std::string* err) {
- return write_misc_partition(&boot, sizeof(boot), BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
+ std::string misc_blk_device = get_misc_blk_device(err);
+ if (misc_blk_device.empty()) {
+ return false;
+ }
+ return write_bootloader_message_to(boot, misc_blk_device, err);
}
bool clear_bootloader_message(std::string* err) {
@@ -224,120 +183,72 @@
strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
for (const auto& s : options) {
strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
- if (s.substr(s.size() - 1) != "\n") {
+ if (s.back() != '\n') {
strlcat(boot.recovery, "\n", sizeof(boot.recovery));
}
}
return write_bootloader_message(boot, err);
}
+bool update_bootloader_message(const std::vector<std::string>& options, std::string* err) {
+ bootloader_message boot;
+ if (!read_bootloader_message(&boot, err)) {
+ return false;
+ }
+
+ // Zero out the entire fields.
+ memset(boot.command, 0, sizeof(boot.command));
+ memset(boot.recovery, 0, sizeof(boot.recovery));
+
+ strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
+ strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
+ for (const auto& s : options) {
+ strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
+ if (s.back() != '\n') {
+ strlcat(boot.recovery, "\n", sizeof(boot.recovery));
+ }
+ }
+ return write_bootloader_message(boot, err);
+}
+
+bool write_reboot_bootloader(std::string* err) {
+ bootloader_message boot;
+ if (!read_bootloader_message(&boot, err)) {
+ return false;
+ }
+ if (boot.command[0] != '\0') {
+ *err = "Bootloader command pending.";
+ return false;
+ }
+ strlcpy(boot.command, "bootonce-bootloader", sizeof(boot.command));
+ return write_bootloader_message(boot, err);
+}
+
bool read_wipe_package(std::string* package_data, size_t size, std::string* err) {
+ std::string misc_blk_device = get_misc_blk_device(err);
+ if (misc_blk_device.empty()) {
+ return false;
+ }
package_data->resize(size);
- return read_misc_partition(&(*package_data)[0], size, WIPE_PACKAGE_OFFSET_IN_MISC, err);
+ return read_misc_partition(&(*package_data)[0], size, misc_blk_device,
+ WIPE_PACKAGE_OFFSET_IN_MISC, err);
}
bool write_wipe_package(const std::string& package_data, std::string* err) {
- return write_misc_partition(package_data.data(), package_data.size(),
+ std::string misc_blk_device = get_misc_blk_device(err);
+ if (misc_blk_device.empty()) {
+ return false;
+ }
+ return write_misc_partition(package_data.data(), package_data.size(), misc_blk_device,
WIPE_PACKAGE_OFFSET_IN_MISC, err);
}
+extern "C" bool write_reboot_bootloader(void) {
+ std::string err;
+ return write_reboot_bootloader(&err);
+}
+
extern "C" bool write_bootloader_message(const char* options) {
std::string err;
- bootloader_message boot = {};
- memcpy(&boot, options, sizeof(boot));
- return write_bootloader_message(boot, &err);
-}
-
-static const char *COMMAND_FILE = "/cache/recovery/command";
-static const int MAX_ARG_LENGTH = 4096;
-static const int MAX_ARGS = 100;
-
-// command line args come from, in decreasing precedence:
-// - the actual command line
-// - the bootloader control block (one per line, after "recovery")
-// - the contents of COMMAND_FILE (one per line)
-void
-get_args(int *argc, char ***argv) {
- bootloader_message boot = {};
- std::string err;
- if (!read_bootloader_message(&boot, &err)) {
- printf("%s\n", err.c_str());
- // If fails, leave a zeroed bootloader_message.
- memset(&boot, 0, sizeof(boot));
- }
- //stage = strndup(boot.stage, sizeof(boot.stage));
-
- if (boot.command[0] != 0 && boot.command[0] != 255) {
- printf("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command);
- }
-
- if (boot.status[0] != 0 && boot.status[0] != 255) {
- printf("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status);
- }
-
- // --- if arguments weren't supplied, look in the bootloader control block
- if (*argc <= 1) {
- boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
- const char *arg = strtok(boot.recovery, "\n");
- if (arg != NULL && !strcmp(arg, "recovery")) {
- *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
- (*argv)[0] = strdup(arg);
- for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
- if ((arg = strtok(NULL, "\n")) == NULL) break;
-
-// if the device does not have an own recovery key combo we just want to open TWRP after
-// walking through the factory reset screen - without actually doing a factory reset
-#ifdef IGNORE_MISC_WIPE_DATA
- if (!strcmp(arg, "--wipe_data")) {
- (*argv)[*argc] = "";
- *argc = *argc -1;
- printf("Bootloader arg \"%s\" ignored because TWRP was compiled with TW_IGNORE_MISC_WIPE_DATA\n", arg);
- continue;
- }
-#endif
- (*argv)[*argc] = strdup(arg);
- }
- printf("Got arguments from boot message\n");
- } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
- printf("Bad boot message\n\"%.20s\"\n", boot.recovery);
- }
- }
-
- // --- if that doesn't work, try the command file (if we have /cache).
- if (*argc <= 1/* && has_cache*/) {
- FILE *fp = fopen(COMMAND_FILE, "r");
- if (fp != NULL) {
- char *token;
- char *argv0 = (*argv)[0];
- *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
- (*argv)[0] = argv0; // use the same program name
-
- char buf[MAX_ARG_LENGTH];
- for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
- if (!fgets(buf, sizeof(buf), fp)) break;
- token = strtok(buf, "\r\n");
- if (token != NULL) {
- (*argv)[*argc] = strdup(token); // Strip newline.
- } else {
- --*argc;
- }
- }
-
- fclose(fp);
- printf("Got arguments from %s\n", COMMAND_FILE);
- }
- }
-
- // --> write the arguments we have back into the bootloader control block
- // always boot into recovery after this (until finish_recovery() is called)
- strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
- strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
- int i;
- for (i = 1; i < *argc; ++i) {
- strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
- strlcat(boot.recovery, "\n", sizeof(boot.recovery));
- }
- if (!write_bootloader_message(boot, &err)) {
- printf("%s\n", err.c_str());
- }
+ return write_bootloader_message({options}, &err);
}
diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h
index e0fc2cd..4ad18f2 100644
--- a/bootloader_message/include/bootloader_message/bootloader_message.h
+++ b/bootloader_message/include/bootloader_message/bootloader_message.h
@@ -17,23 +17,21 @@
#ifndef _BOOTLOADER_MESSAGE_H
#define _BOOTLOADER_MESSAGE_H
+#include <assert.h>
#include <stddef.h>
+#include <stdint.h>
// Spaces used by misc partition are as below:
-// 0 - 2K Bootloader Message
-// 2K - 16K Used by Vendor's bootloader
+// 0 - 2K For bootloader_message
+// 2K - 16K Used by Vendor's bootloader (the 2K - 4K range may be optionally used
+// as bootloader_message_ab struct)
// 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices
// Note that these offsets are admitted by bootloader,recovery and uncrypt, so they
// are not configurable without changing all of them.
-#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET
-static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = BOARD_RECOVERY_BLDRMSG_OFFSET;
-static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024 + BOOTLOADER_MESSAGE_OFFSET_IN_MISC;
-#else
static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0;
static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
-#endif
-/* Bootloader Message
+/* Bootloader Message (2-KiB)
*
* This structure describes the content of a block in flash
* that is used for recovery and the bootloader to talk to
@@ -44,8 +42,9 @@
* It is also updated by the bootloader when firmware update
* is complete (to boot into recovery for any final cleanup)
*
- * The status field is written by the bootloader after the
- * completion of an "update-radio" or "update-hboot" command.
+ * The status field was used by the bootloader after the completion
+ * of an "update-radio" or "update-hboot" command, which has been
+ * deprecated since Froyo.
*
* The recovery field is only written by linux and used
* for the system to send a message to recovery or the
@@ -56,18 +55,17 @@
* 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.
+ * We used to have slot_suffix field for A/B boot control metadata in
+ * this struct, which gets unintentionally cleared by recovery or
+ * uncrypt. Move it into struct bootloader_message_ab to avoid the
+ * issue.
*/
struct bootloader_message {
char command[32];
char status[32];
char recovery[768];
+#ifdef USE_OLD_BOOTLOADER_MESSAGE
// The 'recovery' field used to be 1024 bytes. It has only ever
// been used to store the recovery command line, so 768 bytes
// should be plenty. We carve off the last 256 bytes to store the
@@ -76,23 +74,156 @@
char stage[32];
char slot_suffix[32];
char reserved[192];
+#else
+ // The 'recovery' field used to be 1024 bytes. It has only ever
+ // been used to store the recovery command line, so 768 bytes
+ // should be plenty. We carve off the last 256 bytes to store the
+ // stage string (for multistage packages) and possible future
+ // expansion.
+ char stage[32];
+
+ // The 'reserved' field used to be 224 bytes when it was initially
+ // carved off from the 1024-byte recovery field. Bump it up to
+ // 1184-byte so that the entire bootloader_message struct rounds up
+ // to 2048-byte.
+ char reserved[1184];
+#endif
};
+/**
+ * We must be cautious when changing the bootloader_message struct size,
+ * because A/B-specific fields may end up with different offsets.
+ */
+#if !defined(USE_OLD_BOOTLOADER_MESSAGE) && ((__STDC_VERSION__ >= 201112L) || defined(__cplusplus))
+static_assert(sizeof(struct bootloader_message) == 2048,
+ "struct bootloader_message size changes, which may break A/B devices");
+#endif
+
+/**
+ * The A/B-specific bootloader message structure (4-KiB).
+ *
+ * We separate A/B boot control metadata from the regular bootloader
+ * message struct and keep it here. Everything that's A/B-specific
+ * stays after struct bootloader_message, which should be managed by
+ * the A/B-bootloader or boot control HAL.
+ *
+ * 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. It is encouraged, but
+ * not mandatory, to use 'struct bootloader_control' described below.
+ */
+struct bootloader_message_ab {
+ struct bootloader_message message;
+ char slot_suffix[32];
+
+ // Round up the entire struct to 4096-byte.
+ char reserved[2016];
+};
+
+/**
+ * Be cautious about the struct size change, in case we put anything post
+ * bootloader_message_ab struct (b/29159185).
+ */
+#if !defined(USE_OLD_BOOTLOADER_MESSAGE) && ((__STDC_VERSION__ >= 201112L) || defined(__cplusplus))
+static_assert(sizeof(struct bootloader_message_ab) == 4096,
+ "struct bootloader_message_ab size changes");
+#endif
+
+#define BOOT_CTRL_MAGIC 0x42414342 /* Bootloader Control AB */
+#define BOOT_CTRL_VERSION 1
+
+struct slot_metadata {
+ // Slot priority with 15 meaning highest priority, 1 lowest
+ // priority and 0 the slot is unbootable.
+ uint8_t priority : 4;
+ // Number of times left attempting to boot this slot.
+ uint8_t tries_remaining : 3;
+ // 1 if this slot has booted successfully, 0 otherwise.
+ uint8_t successful_boot : 1;
+ // 1 if this slot is corrupted from a dm-verity corruption, 0
+ // otherwise.
+ uint8_t verity_corrupted : 1;
+ // Reserved for further use.
+ uint8_t reserved : 7;
+} __attribute__((packed));
+
+/* Bootloader Control AB
+ *
+ * This struct can be used to manage A/B metadata. It is designed to
+ * be put in the 'slot_suffix' field of the 'bootloader_message'
+ * structure described above. It is encouraged to use the
+ * 'bootloader_control' structure to store the A/B metadata, but not
+ * mandatory.
+ */
+struct bootloader_control {
+ // NUL terminated active slot suffix.
+ char slot_suffix[4];
+ // Bootloader Control AB magic number (see BOOT_CTRL_MAGIC).
+ uint32_t magic;
+ // Version of struct being used (see BOOT_CTRL_VERSION).
+ uint8_t version;
+ // Number of slots being managed.
+ uint8_t nb_slot : 3;
+ // Number of times left attempting to boot recovery.
+ uint8_t recovery_tries_remaining : 3;
+ // Ensure 4-bytes alignment for slot_info field.
+ uint8_t reserved0[2];
+ // Per-slot information. Up to 4 slots.
+ struct slot_metadata slot_info[4];
+ // Reserved for further use.
+ uint8_t reserved1[8];
+ // CRC32 of all 28 bytes preceding this field (little endian
+ // format).
+ uint32_t crc32_le;
+} __attribute__((packed));
+
+#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
+static_assert(sizeof(struct bootloader_control) ==
+ sizeof(((struct bootloader_message_ab *)0)->slot_suffix),
+ "struct bootloader_control has wrong size");
+#endif
+
#ifdef __cplusplus
#include <string>
#include <vector>
+// Read bootloader message into boot. Error message will be set in err.
bool read_bootloader_message(bootloader_message* boot, std::string* err);
+
+// Read bootloader message from the specified misc device into boot.
+bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
+ std::string* err);
+
+// Write bootloader message to BCB.
bool write_bootloader_message(const bootloader_message& boot, std::string* err);
+
+// Write bootloader message to the specified BCB device.
+bool write_bootloader_message_to(const bootloader_message& boot,
+ const std::string& misc_blk_device, std::string* err);
+
+// Write bootloader message (boots into recovery with the options) to BCB. Will
+// set the command and recovery fields, and reset the rest.
bool write_bootloader_message(const std::vector<std::string>& options, std::string* err);
+
+// Update bootloader message (boots into recovery with the options) to BCB. Will
+// only update the command and recovery fields.
+bool update_bootloader_message(const std::vector<std::string>& options, std::string* err);
+
+// Clear BCB.
bool clear_bootloader_message(std::string* err);
-bool read_wipe_package(std::string* package_data, size_t size, std::string* err);
-bool write_wipe_package(const std::string& package_data, std::string* err);
+// Writes the reboot-bootloader reboot reason to the bootloader_message.
+bool write_reboot_bootloader(std::string* err);
-void set_misc_device(std::string name);
-void get_args(int *argc, char ***argv);
+// Read the wipe package from BCB (from offset WIPE_PACKAGE_OFFSET_IN_MISC).
+bool read_wipe_package(std::string* package_data, size_t size, std::string* err);
+
+// Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC).
+bool write_wipe_package(const std::string& package_data, std::string* err);
#else
@@ -100,6 +231,7 @@
// C Interface.
bool write_bootloader_message(const char* options);
+bool write_reboot_bootloader(void);
#endif // ifdef __cplusplus
diff --git a/bootloader_message_twrp/Android.mk b/bootloader_message_twrp/Android.mk
new file mode 100644
index 0000000..5a24dc4
--- /dev/null
+++ b/bootloader_message_twrp/Android.mk
@@ -0,0 +1,35 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_SRC_FILES := bootloader_message.cpp
+LOCAL_MODULE := libbootloader_message_twrp
+LOCAL_C_INCLUDES += bionic $(LOCAL_PATH)/include
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 22; echo $$?),0)
+ LOCAL_C_INCLUDES += external/stlport/stlport
+ LOCAL_SHARED_LIBRARIES += libstlport
+else
+ LOCAL_C_INCLUDES += external/libcxx/include
+ LOCAL_SHARED_LIBRARIES += libc++
+endif
+LOCAL_CFLAGS := -Werror -std=c++11
+# ignore bootloader's factory reset command even when written to /misc
+ifeq ($(TW_IGNORE_MISC_WIPE_DATA), true)
+ LOCAL_CFLAGS += -DIGNORE_MISC_WIPE_DATA
+endif
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_SHARED_LIBRARY)
diff --git a/bootloader_message_twrp/bootloader_message.cpp b/bootloader_message_twrp/bootloader_message.cpp
new file mode 100644
index 0000000..55d3a5d
--- /dev/null
+++ b/bootloader_message_twrp/bootloader_message.cpp
@@ -0,0 +1,386 @@
+/*
+ * 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 <bootloader_message_twrp/bootloader_message.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/system_properties.h>
+
+#include <string>
+#include <vector>
+
+static std::string misc_blkdev;
+
+void set_misc_device(const char* name) {
+ misc_blkdev = name;
+}
+
+static std::string get_misc_blk_device(std::string* err) {
+ *err = "";
+ return misc_blkdev;
+}
+
+// In recovery mode, recovery can get started and try to access the misc
+// device before the kernel has actually created it.
+static bool wait_for_device(const std::string& blk_device, std::string* err) {
+ int tries = 0;
+ int ret;
+ err->clear();
+ do {
+ ++tries;
+ struct stat buf;
+ ret = stat(blk_device.c_str(), &buf);
+ if (ret == -1) {
+ char buffer[2048];
+ sprintf(buffer, "failed to stat %s try %d: %s\n",
+ blk_device.c_str(), tries, strerror(errno));
+ *err += buffer;
+ /*
+ *err += android::base::StringPrintf("failed to stat %s try %d: %s\n",
+ blk_device.c_str(), tries, strerror(errno));
+ */
+ sleep(1);
+ }
+ } while (ret && tries < 10);
+
+ if (ret) {
+ *err += "failed to stat " + blk_device + "\n";
+ /*
+ *err += android::base::StringPrintf("failed to stat %s\n", blk_device.c_str());
+ */
+ }
+ return ret == 0;
+}
+
+static bool read_misc_partition(void* p, size_t size, const std::string& misc_blk_device,
+ size_t offset, std::string* err) {
+ if (!wait_for_device(misc_blk_device, err)) {
+ return false;
+ }
+
+ int fd(open(misc_blk_device.c_str(), O_RDONLY));
+ if (fd < 0) {
+ *err = "failed to open " + misc_blk_device + ": ";
+ *err += strerror(errno);
+ /*
+ android::base::unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY));
+ if (fd == -1) {
+ *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
+ strerror(errno));
+ */
+ return false;
+ }
+ if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
+ *err = "failed to lseek " + misc_blk_device + ": ";
+ *err += strerror(errno);
+ close(fd);
+ /*
+
+ *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
+ strerror(errno));
+ */
+ return false;
+ }
+
+ if ((size_t)read(fd, p, size) != size) {
+ *err = "failed to read " + misc_blk_device + ": ";
+ *err += strerror(errno);
+ close(fd);
+ /*
+ if (!android::base::ReadFully(fd, p, size)) {
+ *err = android::base::StringPrintf("failed to read %s: %s", misc_blk_device.c_str(),
+ strerror(errno));
+ */
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+static bool write_misc_partition(const void* p, size_t size, size_t offset, std::string* err) {
+ std::string misc_blk_device = get_misc_blk_device(err);
+ if (misc_blk_device.empty()) {
+ *err = "no misc device set";
+ return false;
+ }
+ int fd = (open(misc_blk_device.c_str(), O_WRONLY | O_SYNC));
+ if (fd == -1) {
+ *err = "failed to open " + misc_blk_device + ": ";
+ *err += strerror(errno);
+ /*
+static bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device,
+ size_t offset, std::string* err) {
+ android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY));
+ if (fd == -1) {
+ *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
+ strerror(errno));
+ */
+ return false;
+ }
+ if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
+ *err = "failed to lseek " + misc_blk_device + ": ";
+ *err += strerror(errno);
+ close(fd);
+ /*
+ *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
+ strerror(errno));
+ */
+ return false;
+ }
+ if ((size_t)write(fd, p, size) != size) {
+ *err = "failed to write " + misc_blk_device + ": ";
+ *err += strerror(errno);
+ close(fd);
+ /*
+ if (!android::base::WriteFully(fd, p, size)) {
+ *err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(),
+ strerror(errno));
+ */
+ return false;
+ }
+
+ // TODO: O_SYNC and fsync duplicates each other?
+ if (fsync(fd) == -1) {
+ *err = "failed to fsync " + misc_blk_device + ": ";
+ *err += strerror(errno);
+ close(fd);
+ /*
+ if (fsync(fd) == -1) {
+ *err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(),
+ strerror(errno));
+ */
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
+ std::string* err) {
+ return read_misc_partition(boot, sizeof(*boot), misc_blk_device,
+ BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
+}
+
+bool read_bootloader_message(bootloader_message* boot, std::string* err) {
+ std::string misc_blk_device = get_misc_blk_device(err);
+ if (misc_blk_device.empty()) {
+ return false;
+ }
+ return read_bootloader_message_from(boot, misc_blk_device, err);
+}
+
+bool write_bootloader_message_to(const bootloader_message& boot, __unused const std::string& misc_blk_device,
+ std::string* err) {
+ return write_misc_partition(&boot, sizeof(boot),
+ BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
+}
+
+bool write_bootloader_message(const bootloader_message& boot, std::string* err) {
+ std::string misc_blk_device = get_misc_blk_device(err);
+ if (misc_blk_device.empty()) {
+ return false;
+ }
+ return write_bootloader_message_to(boot, misc_blk_device, err);
+}
+
+// libc++ in 5.1 does not know how to handle a std::string* so this craziness is needed
+bool clear_bootloader_message(void* err) {
+ std::string &s = *(static_cast<std::string*>(err));
+ return clear_bootloader_message(&s);
+}
+
+bool clear_bootloader_message(std::string* err) {
+ bootloader_message boot = {};
+ return write_bootloader_message(boot, err);
+}
+
+bool write_bootloader_message(const std::vector<std::string>& options, std::string* err) {
+ bootloader_message boot = {};
+ strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
+ strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
+ for (const auto& s : options) {
+ strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
+ if (s.substr(s.size() - 1) != "\n") {
+ strlcat(boot.recovery, "\n", sizeof(boot.recovery));
+ }
+ }
+ return write_bootloader_message(boot, err);
+}
+
+bool update_bootloader_message(const std::vector<std::string>& options, std::string* err) {
+ bootloader_message boot;
+ if (!read_bootloader_message(&boot, err)) {
+ return false;
+ }
+
+ // Zero out the entire fields.
+ memset(boot.command, 0, sizeof(boot.command));
+ memset(boot.recovery, 0, sizeof(boot.recovery));
+
+ strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
+ strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
+ for (const auto& s : options) {
+ strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
+ if (s.back() != '\n') {
+ strlcat(boot.recovery, "\n", sizeof(boot.recovery));
+ }
+ }
+ return write_bootloader_message(boot, err);
+}
+
+bool write_reboot_bootloader(std::string* err) {
+ bootloader_message boot;
+ if (!read_bootloader_message(&boot, err)) {
+ return false;
+ }
+ if (boot.command[0] != '\0') {
+ *err = "Bootloader command pending.";
+ return false;
+ }
+ strlcpy(boot.command, "bootonce-bootloader", sizeof(boot.command));
+ return write_bootloader_message(boot, err);
+}
+
+bool read_wipe_package(std::string* package_data, size_t size, std::string* err) {
+ std::string misc_blk_device = get_misc_blk_device(err);
+ if (misc_blk_device.empty()) {
+ return false;
+ }
+ package_data->resize(size);
+ return read_misc_partition(&(*package_data)[0], size, misc_blk_device,
+ WIPE_PACKAGE_OFFSET_IN_MISC, err);
+}
+
+bool write_wipe_package(const std::string& package_data, std::string* err) {
+ std::string misc_blk_device = get_misc_blk_device(err);
+ if (misc_blk_device.empty()) {
+ return false;
+ }
+ return write_misc_partition(package_data.data(), package_data.size(),
+ WIPE_PACKAGE_OFFSET_IN_MISC, err);
+}
+
+extern "C" bool write_reboot_bootloader(void) {
+ std::string err;
+ return write_reboot_bootloader(&err);
+}
+
+extern "C" bool write_bootloader_message(const char* options) {
+ std::string err;
+ bootloader_message boot = {};
+ memcpy(&boot, options, sizeof(boot));
+ return write_bootloader_message(boot, &err);
+}
+
+static const char *COMMAND_FILE = "/cache/recovery/command";
+static const int MAX_ARG_LENGTH = 4096;
+static const int MAX_ARGS = 100;
+
+// command line args come from, in decreasing precedence:
+// - the actual command line
+// - the bootloader control block (one per line, after "recovery")
+// - the contents of COMMAND_FILE (one per line)
+void
+get_args(int *argc, char ***argv) {
+ bootloader_message boot = {};
+ std::string err;
+ if (!read_bootloader_message(&boot, &err)) {
+ printf("%s\n", err.c_str());
+ // If fails, leave a zeroed bootloader_message.
+ memset(&boot, 0, sizeof(boot));
+ }
+ //stage = strndup(boot.stage, sizeof(boot.stage));
+
+ if (boot.command[0] != 0 && boot.command[0] != 255) {
+ printf("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command);
+ }
+
+ if (boot.status[0] != 0 && boot.status[0] != 255) {
+ printf("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status);
+ }
+
+ // --- if arguments weren't supplied, look in the bootloader control block
+ if (*argc <= 1) {
+ boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
+ const char *arg = strtok(boot.recovery, "\n");
+ if (arg != NULL && !strcmp(arg, "recovery")) {
+ *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
+ (*argv)[0] = strdup(arg);
+ for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
+ if ((arg = strtok(NULL, "\n")) == NULL) break;
+
+// if the device does not have an own recovery key combo we just want to open TWRP after
+// walking through the factory reset screen - without actually doing a factory reset
+#ifdef IGNORE_MISC_WIPE_DATA
+ if (!strcmp(arg, "--wipe_data")) {
+ (*argv)[*argc] = "";
+ *argc = *argc -1;
+ printf("Bootloader arg \"%s\" ignored because TWRP was compiled with TW_IGNORE_MISC_WIPE_DATA\n", arg);
+ continue;
+ }
+#endif
+ (*argv)[*argc] = strdup(arg);
+ }
+ printf("Got arguments from boot message\n");
+ } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
+ printf("Bad boot message\n\"%.20s\"\n", boot.recovery);
+ }
+ }
+
+ // --- if that doesn't work, try the command file (if we have /cache).
+ if (*argc <= 1/* && has_cache*/) {
+ FILE *fp = fopen(COMMAND_FILE, "r");
+ if (fp != NULL) {
+ char *token;
+ char *argv0 = (*argv)[0];
+ *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
+ (*argv)[0] = argv0; // use the same program name
+
+ char buf[MAX_ARG_LENGTH];
+ for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
+ if (!fgets(buf, sizeof(buf), fp)) break;
+ token = strtok(buf, "\r\n");
+ if (token != NULL) {
+ (*argv)[*argc] = strdup(token); // Strip newline.
+ } else {
+ --*argc;
+ }
+ }
+
+ fclose(fp);
+ printf("Got arguments from %s\n", COMMAND_FILE);
+ }
+ }
+
+ // --> write the arguments we have back into the bootloader control block
+ // always boot into recovery after this (until finish_recovery() is called)
+ strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
+ strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
+ int i;
+ for (i = 1; i < *argc; ++i) {
+ strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
+ strlcat(boot.recovery, "\n", sizeof(boot.recovery));
+ }
+ if (!write_bootloader_message(boot, &err)) {
+ printf("%s\n", err.c_str());
+ }
+}
diff --git a/bootloader_message_twrp/include/bootloader_message_twrp/bootloader_message.h b/bootloader_message_twrp/include/bootloader_message_twrp/bootloader_message.h
new file mode 100644
index 0000000..52c1b86
--- /dev/null
+++ b/bootloader_message_twrp/include/bootloader_message_twrp/bootloader_message.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BOOTLOADER_MESSAGE_TWRP_H
+#define _BOOTLOADER_MESSAGE_TWRP_H
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+
+// Spaces used by misc partition are as below:
+// 0 - 2K For bootloader_message
+// 2K - 16K Used by Vendor's bootloader (the 2K - 4K range may be optionally used
+// as bootloader_message_ab struct)
+// 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices
+// Note that these offsets are admitted by bootloader,recovery and uncrypt, so they
+// are not configurable without changing all of them.
+#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET
+static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = BOARD_RECOVERY_BLDRMSG_OFFSET;
+static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024 + BOOTLOADER_MESSAGE_OFFSET_IN_MISC;
+#else
+static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0;
+static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
+#endif
+
+/* Bootloader Message (2-KiB)
+ *
+ * This structure describes the content of a block in flash
+ * that is used for recovery and the bootloader to talk to
+ * each other.
+ *
+ * The command field is updated by linux when it wants to
+ * reboot into recovery or to update radio or bootloader firmware.
+ * It is also updated by the bootloader when firmware update
+ * is complete (to boot into recovery for any final cleanup)
+ *
+ * The status field was used by the bootloader after the completion
+ * of an "update-radio" or "update-hboot" command, which has been
+ * deprecated since Froyo.
+ *
+ * The recovery field is only written by linux and used
+ * for the system to send a message to recovery or the
+ * other way around.
+ *
+ * The stage field is written by packages which restart themselves
+ * 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.
+ *
+ * We used to have slot_suffix field for A/B boot control metadata in
+ * this struct, which gets unintentionally cleared by recovery or
+ * uncrypt. Move it into struct bootloader_message_ab to avoid the
+ * issue.
+ */
+struct bootloader_message {
+ char command[32];
+ char status[32];
+ char recovery[768];
+
+ // The 'recovery' field used to be 1024 bytes. It has only ever
+ // been used to store the recovery command line, so 768 bytes
+ // should be plenty. We carve off the last 256 bytes to store the
+ // stage string (for multistage packages) and possible future
+ // expansion.
+ char stage[32];
+
+ // The 'reserved' field used to be 224 bytes when it was initially
+ // carved off from the 1024-byte recovery field. Bump it up to
+ // 1184-byte so that the entire bootloader_message struct rounds up
+ // to 2048-byte.
+ char reserved[1184];
+};
+
+/**
+ * We must be cautious when changing the bootloader_message struct size,
+ * because A/B-specific fields may end up with different offsets.
+ */
+/*#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
+static_assert(sizeof(struct bootloader_message) == 2048,
+ "struct bootloader_message size changes, which may break A/B devices");
+#endif*/
+
+/**
+ * The A/B-specific bootloader message structure (4-KiB).
+ *
+ * We separate A/B boot control metadata from the regular bootloader
+ * message struct and keep it here. Everything that's A/B-specific
+ * stays after struct bootloader_message, which should be managed by
+ * the A/B-bootloader or boot control HAL.
+ *
+ * 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. It is encouraged, but
+ * not mandatory, to use 'struct bootloader_control' described below.
+ */
+struct bootloader_message_ab {
+ struct bootloader_message message;
+ char slot_suffix[32];
+
+ // Round up the entire struct to 4096-byte.
+ char reserved[2016];
+};
+
+/**
+ * Be cautious about the struct size change, in case we put anything post
+ * bootloader_message_ab struct (b/29159185).
+ */
+/*#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
+static_assert(sizeof(struct bootloader_message_ab) == 4096,
+ "struct bootloader_message_ab size changes");
+#endif*/
+
+#define BOOT_CTRL_MAGIC 0x42414342 /* Bootloader Control AB */
+#define BOOT_CTRL_VERSION 1
+
+struct slot_metadata {
+ // Slot priority with 15 meaning highest priority, 1 lowest
+ // priority and 0 the slot is unbootable.
+ uint8_t priority : 4;
+ // Number of times left attempting to boot this slot.
+ uint8_t tries_remaining : 3;
+ // 1 if this slot has booted successfully, 0 otherwise.
+ uint8_t successful_boot : 1;
+ // 1 if this slot is corrupted from a dm-verity corruption, 0
+ // otherwise.
+ uint8_t verity_corrupted : 1;
+ // Reserved for further use.
+ uint8_t reserved : 7;
+} __attribute__((packed));
+
+/* Bootloader Control AB
+ *
+ * This struct can be used to manage A/B metadata. It is designed to
+ * be put in the 'slot_suffix' field of the 'bootloader_message'
+ * structure described above. It is encouraged to use the
+ * 'bootloader_control' structure to store the A/B metadata, but not
+ * mandatory.
+ */
+struct bootloader_control {
+ // NUL terminated active slot suffix.
+ char slot_suffix[4];
+ // Bootloader Control AB magic number (see BOOT_CTRL_MAGIC).
+ uint32_t magic;
+ // Version of struct being used (see BOOT_CTRL_VERSION).
+ uint8_t version;
+ // Number of slots being managed.
+ uint8_t nb_slot : 3;
+ // Number of times left attempting to boot recovery.
+ uint8_t recovery_tries_remaining : 3;
+ // Ensure 4-bytes alignment for slot_info field.
+ uint8_t reserved0[2];
+ // Per-slot information. Up to 4 slots.
+ struct slot_metadata slot_info[4];
+ // Reserved for further use.
+ uint8_t reserved1[8];
+ // CRC32 of all 28 bytes preceding this field (little endian
+ // format).
+ uint32_t crc32_le;
+} __attribute__((packed));
+
+/*#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
+static_assert(sizeof(struct bootloader_control) ==
+ sizeof(((struct bootloader_message_ab *)0)->slot_suffix),
+ "struct bootloader_control has wrong size");
+#endif*/
+
+#ifdef __cplusplus
+
+#include <string.h>
+#include <vector>
+
+// Read bootloader message into boot. Error message will be set in err.
+bool read_bootloader_message(bootloader_message* boot, std::string* err);
+
+// Read bootloader message from the specified misc device into boot.
+bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
+ std::string* err);
+
+// Write bootloader message to BCB.
+bool write_bootloader_message(const bootloader_message& boot, std::string* err);
+
+// Write bootloader message to the specified BCB device.
+bool write_bootloader_message_to(const bootloader_message& boot,
+ const std::string& misc_blk_device, std::string* err);
+
+// Write bootloader message (boots into recovery with the options) to BCB. Will
+// set the command and recovery fields, and reset the rest.
+bool write_bootloader_message(const std::vector<std::string>& options, std::string* err);
+
+// Update bootloader message (boots into recovery with the options) to BCB. Will
+// only update the command and recovery fields.
+bool update_bootloader_message(const std::vector<std::string>& options, std::string* err);
+
+// Clear BCB.
+bool clear_bootloader_message(void* err);
+bool clear_bootloader_message(std::string* err);
+
+// Writes the reboot-bootloader reboot reason to the bootloader_message.
+bool write_reboot_bootloader(std::string* err);
+
+// Read the wipe package from BCB (from offset WIPE_PACKAGE_OFFSET_IN_MISC).
+bool read_wipe_package(std::string* package_data, size_t size, std::string* err);
+
+void set_misc_device(const char* name);
+void get_args(int *argc, char ***argv);
+
+// Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC).
+bool write_wipe_package(const std::string& package_data, std::string* err);
+
+#else
+
+#include <stdbool.h>
+
+// C Interface.
+bool write_bootloader_message(const char* options);
+bool write_reboot_bootloader(void);
+
+#endif // ifdef __cplusplus
+
+#endif // _BOOTLOADER_MESSAGE_TWRP_H
diff --git a/common.h b/common.h
index 3afb633..b0901f4 100644
--- a/common.h
+++ b/common.h
@@ -17,9 +17,9 @@
#ifndef RECOVERY_COMMON_H
#define RECOVERY_COMMON_H
-#include <stdbool.h>
#include <stdio.h>
#include <stdarg.h>
+#include <string>
#ifdef __cplusplus
extern "C" {
@@ -40,18 +40,29 @@
#define STRINGIFY(x) #x
#define EXPAND(x) STRINGIFY(x)
+class RecoveryUI;
+
+extern RecoveryUI* ui;
extern bool modified_flash;
//typedef struct fstab_rec Volume;
+// The current stage, e.g. "1/2".
+extern std::string stage;
+
+// The reason argument provided in "--reason=".
+extern const char* reason;
+
// fopen a file, mounting volumes and making parent dirs as necessary.
FILE* fopen_path(const char *path, const char *mode);
void ui_print(const char* format, ...);
-bool is_ro_debuggable();
+static bool is_ro_debuggable();
#ifdef __cplusplus
}
#endif
+bool reboot(const std::string& command);
+
#endif // RECOVERY_COMMON_H
diff --git a/device.cpp b/device.cpp
index e717ddd..6150186 100644
--- a/device.cpp
+++ b/device.cpp
@@ -60,7 +60,7 @@
return menu_position < 0 ? NO_ACTION : MENU_ACTIONS[menu_position];
}
-int Device::HandleMenuKey(int key, int visible) {
+int Device::HandleMenuKey(int key, bool visible) {
if (!visible) {
return kNoAction;
}
diff --git a/device.h b/device.h
index 5017782..639e2bf 100644
--- a/device.h
+++ b/device.h
@@ -20,96 +20,90 @@
#include "ui.h"
class Device {
- public:
- Device(RecoveryUI* ui) : ui_(ui) { }
- virtual ~Device() { }
+ public:
+ explicit Device(RecoveryUI* ui) : ui_(ui) {}
+ virtual ~Device() {}
- // Called to obtain the UI object that should be used to display
- // the recovery user interface for this device. You should not
- // have called Init() on the UI object already, the caller will do
- // that after this method returns.
- virtual RecoveryUI* GetUI() { return ui_; }
+ // Called to obtain the UI object that should be used to display the recovery user interface for
+ // this device. You should not have called Init() on the UI object already, the caller will do
+ // that after this method returns.
+ virtual RecoveryUI* GetUI() {
+ return ui_;
+ }
- // Called when recovery starts up (after the UI has been obtained
- // and initialized and after the arguments have been parsed, but
- // before anything else).
- virtual void StartRecovery() { };
+ // Called when recovery starts up (after the UI has been obtained and initialized and after the
+ // arguments have been parsed, but before anything else).
+ virtual void StartRecovery() {};
- // Called from the main thread when recovery is at the main menu
- // and waiting for input, and a key is pressed. (Note that "at"
- // the main menu does not necessarily mean the menu is visible;
- // recovery will be at the main menu with it invisible after an
- // unsuccessful operation [ie OTA package failure], or if recovery
- // is started with no command.)
- //
- // key is the code of the key just pressed. (You can call
- // IsKeyPressed() on the RecoveryUI object you returned from GetUI
- // if you want to find out if other keys are held down.)
- //
- // visible is true if the menu is visible.
- //
- // Return one of the defined constants below in order to:
- //
- // - move the menu highlight (kHighlight{Up,Down})
- // - invoke the highlighted item (kInvokeItem)
- // - do nothing (kNoAction)
- // - invoke a specific action (a menu position: any non-negative number)
- virtual int HandleMenuKey(int key, int visible);
+ // Called from the main thread when recovery is at the main menu and waiting for input, and a key
+ // is pressed. (Note that "at" the main menu does not necessarily mean the menu is visible;
+ // recovery will be at the main menu with it invisible after an unsuccessful operation [ie OTA
+ // package failure], or if recovery is started with no command.)
+ //
+ // 'key' is the code of the key just pressed. (You can call IsKeyPressed() on the RecoveryUI
+ // object you returned from GetUI if you want to find out if other keys are held down.)
+ //
+ // 'visible' is true if the menu is visible.
+ //
+ // Returns one of the defined constants below in order to:
+ //
+ // - move the menu highlight (kHighlight{Up,Down})
+ // - invoke the highlighted item (kInvokeItem)
+ // - do nothing (kNoAction)
+ // - invoke a specific action (a menu position: any non-negative number)
+ virtual int HandleMenuKey(int key, bool visible);
- enum BuiltinAction {
- NO_ACTION = 0,
- REBOOT = 1,
- APPLY_SDCARD = 2,
- // APPLY_CACHE was 3.
- APPLY_ADB_SIDELOAD = 4,
- WIPE_DATA = 5,
- WIPE_CACHE = 6,
- REBOOT_BOOTLOADER = 7,
- SHUTDOWN = 8,
- VIEW_RECOVERY_LOGS = 9,
- MOUNT_SYSTEM = 10,
- RUN_GRAPHICS_TEST = 11,
- };
+ enum BuiltinAction {
+ NO_ACTION = 0,
+ REBOOT = 1,
+ APPLY_SDCARD = 2,
+ // APPLY_CACHE was 3.
+ APPLY_ADB_SIDELOAD = 4,
+ WIPE_DATA = 5,
+ WIPE_CACHE = 6,
+ REBOOT_BOOTLOADER = 7,
+ SHUTDOWN = 8,
+ VIEW_RECOVERY_LOGS = 9,
+ MOUNT_SYSTEM = 10,
+ RUN_GRAPHICS_TEST = 11,
+ };
- // Return the list of menu items (an array of strings,
- // NULL-terminated). The menu_position passed to InvokeMenuItem
- // will correspond to the indexes into this array.
- virtual const char* const* GetMenuItems();
+ // Return the list of menu items (an array of strings, NULL-terminated). The menu_position passed
+ // to InvokeMenuItem will correspond to the indexes into this array.
+ virtual const char* const* GetMenuItems();
- // Perform a recovery action selected from the menu.
- // 'menu_position' will be the item number of the selected menu
- // item, or a non-negative number returned from
- // device_handle_key(). The menu will be hidden when this is
- // called; implementations can call ui_print() to print
- // information to the screen. If the menu position is one of the
- // builtin actions, you can just return the corresponding enum
- // value. If it is an action specific to your device, you
- // actually perform it here and return NO_ACTION.
- virtual BuiltinAction InvokeMenuItem(int menu_position);
+ // Perform a recovery action selected from the menu. 'menu_position' will be the item number of
+ // the selected menu item, or a non-negative number returned from HandleMenuKey(). The menu will
+ // be hidden when this is called; implementations can call ui_print() to print information to the
+ // screen. If the menu position is one of the builtin actions, you can just return the
+ // corresponding enum value. If it is an action specific to your device, you actually perform it
+ // here and return NO_ACTION.
+ virtual BuiltinAction InvokeMenuItem(int menu_position);
- static const int kNoAction = -1;
- static const int kHighlightUp = -2;
- static const int kHighlightDown = -3;
- static const int kInvokeItem = -4;
+ static const int kNoAction = -1;
+ static const int kHighlightUp = -2;
+ static const int kHighlightDown = -3;
+ static const int kInvokeItem = -4;
- // Called before and after we do a wipe data/factory reset operation,
- // either via a reboot from the main system with the --wipe_data flag,
- // or when the user boots into recovery image manually and selects the
- // option from the menu, to perform whatever device-specific wiping
- // actions are needed.
- // Return true on success; returning false from PreWipeData will prevent
- // the regular wipe, and returning false from PostWipeData will cause
- // the wipe to be considered a failure.
- virtual bool PreWipeData() { return true; }
- virtual bool PostWipeData() { return true; }
+ // Called before and after we do a wipe data/factory reset operation, either via a reboot from the
+ // main system with the --wipe_data flag, or when the user boots into recovery image manually and
+ // selects the option from the menu, to perform whatever device-specific wiping actions as needed.
+ // Returns true on success; returning false from PreWipeData will prevent the regular wipe, and
+ // returning false from PostWipeData will cause the wipe to be considered a failure.
+ virtual bool PreWipeData() {
+ return true;
+ }
- private:
- RecoveryUI* ui_;
+ virtual bool PostWipeData() {
+ return true;
+ }
+
+ private:
+ RecoveryUI* ui_;
};
-// The device-specific library must define this function (or the
-// default one will be used, if there is no device-specific library).
-// It returns the Device object that recovery should use.
+// The device-specific library must define this function (or the default one will be used, if there
+// is no device-specific library). It returns the Device object that recovery should use.
Device* make_device();
#endif // _DEVICE_H
diff --git a/edify/Android.mk b/edify/Android.mk
index 71cf765..d8058c1 100644
--- a/edify/Android.mk
+++ b/edify/Android.mk
@@ -1,23 +1,36 @@
# Copyright 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
LOCAL_PATH := $(call my-dir)
edify_src_files := \
- lexer.ll \
- parser.yy \
- expr.cpp
+ lexer.ll \
+ parser.yy \
+ expr.cpp
#
-# Build the host-side command line tool
+# Build the host-side command line tool (host executable)
#
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
- $(edify_src_files) \
- main.cpp
+ $(edify_src_files) \
+ edify_parser.cpp
+LOCAL_CFLAGS := -Werror
LOCAL_CPPFLAGS := -g -O0
-LOCAL_MODULE := edify
+LOCAL_MODULE := edify_parser
LOCAL_YACCFLAGS := -v
LOCAL_CPPFLAGS += -Wno-unused-parameter
LOCAL_CPPFLAGS += -Wno-deprecated-register
@@ -28,12 +41,13 @@
include $(BUILD_HOST_EXECUTABLE)
#
-# Build the device-side library
+# Build the device-side library (static library)
#
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(edify_src_files)
+LOCAL_CFLAGS := -Werror
LOCAL_CPPFLAGS := -Wno-unused-parameter
LOCAL_CPPFLAGS += -Wno-deprecated-register
LOCAL_MODULE := libedify
diff --git a/edify/README b/edify/README.md
similarity index 99%
rename from edify/README
rename to edify/README.md
index 810455c..b3330e2 100644
--- a/edify/README
+++ b/edify/README.md
@@ -1,3 +1,6 @@
+edify
+=====
+
Update scripts (from donut onwards) are written in a new little
scripting language ("edify") that is superficially somewhat similar to
the old one ("amend"). This is a brief overview of the new language.
diff --git a/edify/edify_parser.cpp b/edify/edify_parser.cpp
new file mode 100644
index 0000000..f1b5628
--- /dev/null
+++ b/edify/edify_parser.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This is a host-side tool for validating a given edify script file.
+ *
+ * We used to have edify test cases here, which have been moved to
+ * tests/component/edify_test.cpp.
+ *
+ * Caveat: It doesn't recognize functions defined through updater, which
+ * makes the tool less useful. We should either extend the tool or remove it.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/file.h>
+
+#include "expr.h"
+
+static void ExprDump(int depth, const std::unique_ptr<Expr>& n, const std::string& script) {
+ printf("%*s", depth*2, "");
+ printf("%s %p (%d-%d) \"%s\"\n",
+ n->name.c_str(), n->fn, n->start, n->end,
+ script.substr(n->start, n->end - n->start).c_str());
+ for (size_t i = 0; i < n->argv.size(); ++i) {
+ ExprDump(depth+1, n->argv[i], script);
+ }
+}
+
+int main(int argc, char** argv) {
+ RegisterBuiltins();
+
+ if (argc != 2) {
+ printf("Usage: %s <edify script>\n", argv[0]);
+ return 1;
+ }
+
+ std::string buffer;
+ if (!android::base::ReadFileToString(argv[1], &buffer)) {
+ printf("%s: failed to read %s: %s\n", argv[0], argv[1], strerror(errno));
+ return 1;
+ }
+
+ std::unique_ptr<Expr> root;
+ int error_count = 0;
+ int error = parse_string(buffer.data(), &root, &error_count);
+ printf("parse returned %d; %d errors encountered\n", error, error_count);
+ if (error == 0 || error_count > 0) {
+
+ ExprDump(0, root, buffer);
+
+ State state(buffer, nullptr);
+ std::string result;
+ if (!Evaluate(&state, root, &result)) {
+ printf("result was NULL, message is: %s\n",
+ (state.errmsg.empty() ? "(NULL)" : state.errmsg.c_str()));
+ } else {
+ printf("result is [%s]\n", result.c_str());
+ }
+ }
+ return 0;
+}
diff --git a/edify/expr.cpp b/edify/expr.cpp
index cc14fbe..54ab332 100644
--- a/edify/expr.cpp
+++ b/edify/expr.cpp
@@ -14,201 +14,172 @@
* limitations under the License.
*/
-#include <string.h>
-#include <stdbool.h>
+#include "expr.h"
+
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stdarg.h>
+#include <string.h>
#include <unistd.h>
+#include <memory>
#include <string>
+#include <unordered_map>
+#include <vector>
+#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include "expr.h"
-
// Functions should:
//
// - return a malloc()'d string
-// - if Evaluate() on any argument returns NULL, return NULL.
+// - if Evaluate() on any argument returns nullptr, return nullptr.
-int BooleanString(const char* s) {
- return s[0] != '\0';
+static bool BooleanString(const std::string& s) {
+ return !s.empty();
}
-char* Evaluate(State* state, Expr* expr) {
- Value* v = expr->fn(expr->name, state, expr->argc, expr->argv);
- if (v == NULL) return NULL;
+bool Evaluate(State* state, const std::unique_ptr<Expr>& expr, std::string* result) {
+ if (result == nullptr) {
+ return false;
+ }
+
+ std::unique_ptr<Value> v(expr->fn(expr->name.c_str(), state, expr->argv));
+ if (!v) {
+ return false;
+ }
if (v->type != VAL_STRING) {
ErrorAbort(state, kArgsParsingFailure, "expecting string, got value type %d", v->type);
- FreeValue(v);
- return NULL;
+ return false;
}
- char* result = v->data;
- free(v);
- return result;
+
+ *result = v->data;
+ return true;
}
-Value* EvaluateValue(State* state, Expr* expr) {
- return expr->fn(expr->name, state, expr->argc, expr->argv);
+Value* EvaluateValue(State* state, const std::unique_ptr<Expr>& expr) {
+ return expr->fn(expr->name.c_str(), state, expr->argv);
}
-Value* StringValue(char* str) {
- if (str == NULL) return NULL;
- Value* v = reinterpret_cast<Value*>(malloc(sizeof(Value)));
- v->type = VAL_STRING;
- v->size = strlen(str);
- v->data = str;
- return v;
-}
-
-void FreeValue(Value* v) {
- if (v == NULL) return;
- free(v->data);
- free(v);
-}
-
-Value* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) {
- if (argc == 0) {
- return StringValue(strdup(""));
+Value* StringValue(const char* str) {
+ if (str == nullptr) {
+ return nullptr;
}
- char** strings = reinterpret_cast<char**>(malloc(argc * sizeof(char*)));
- int i;
- for (i = 0; i < argc; ++i) {
- strings[i] = NULL;
+ return new Value(VAL_STRING, str);
+}
+
+Value* StringValue(const std::string& str) {
+ return StringValue(str.c_str());
+}
+
+Value* ConcatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+ if (argv.empty()) {
+ return StringValue("");
}
- char* result = NULL;
- int length = 0;
- for (i = 0; i < argc; ++i) {
- strings[i] = Evaluate(state, argv[i]);
- if (strings[i] == NULL) {
- goto done;
+ std::string result;
+ for (size_t i = 0; i < argv.size(); ++i) {
+ std::string str;
+ if (!Evaluate(state, argv[i], &str)) {
+ return nullptr;
}
- length += strlen(strings[i]);
+ result += str;
}
- result = reinterpret_cast<char*>(malloc(length+1));
- int p;
- p = 0;
- for (i = 0; i < argc; ++i) {
- strcpy(result+p, strings[i]);
- p += strlen(strings[i]);
- }
- result[p] = '\0';
-
- done:
- for (i = 0; i < argc; ++i) {
- free(strings[i]);
- }
- free(strings);
return StringValue(result);
}
-Value* IfElseFn(const char* name, State* state, int argc, Expr* argv[]) {
- if (argc != 2 && argc != 3) {
- free(state->errmsg);
- state->errmsg = strdup("ifelse expects 2 or 3 arguments");
- return NULL;
- }
- char* cond = Evaluate(state, argv[0]);
- if (cond == NULL) {
- return NULL;
+Value* IfElseFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+ if (argv.size() != 2 && argv.size() != 3) {
+ state->errmsg = "ifelse expects 2 or 3 arguments";
+ return nullptr;
}
- if (BooleanString(cond) == true) {
- free(cond);
- return EvaluateValue(state, argv[1]);
- } else {
- if (argc == 3) {
- free(cond);
- return EvaluateValue(state, argv[2]);
- } else {
- return StringValue(cond);
- }
+ std::string cond;
+ if (!Evaluate(state, argv[0], &cond)) {
+ return nullptr;
}
+
+ if (!cond.empty()) {
+ return EvaluateValue(state, argv[1]);
+ } else if (argv.size() == 3) {
+ return EvaluateValue(state, argv[2]);
+ }
+
+ return StringValue("");
}
-Value* AbortFn(const char* name, State* state, int argc, Expr* argv[]) {
- char* msg = NULL;
- if (argc > 0) {
- msg = Evaluate(state, argv[0]);
- }
- free(state->errmsg);
- if (msg) {
+Value* AbortFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+ std::string msg;
+ if (!argv.empty() && Evaluate(state, argv[0], &msg)) {
state->errmsg = msg;
} else {
- state->errmsg = strdup("called abort()");
+ state->errmsg = "called abort()";
}
- return NULL;
+ return nullptr;
}
-Value* AssertFn(const char* name, State* state, int argc, Expr* argv[]) {
- int i;
- for (i = 0; i < argc; ++i) {
- char* v = Evaluate(state, argv[i]);
- if (v == NULL) {
- return NULL;
+Value* AssertFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+ for (size_t i = 0; i < argv.size(); ++i) {
+ std::string result;
+ if (!Evaluate(state, argv[i], &result)) {
+ return nullptr;
}
- int b = BooleanString(v);
- free(v);
- if (!b) {
- int prefix_len;
+ if (result.empty()) {
int len = argv[i]->end - argv[i]->start;
- char* err_src = reinterpret_cast<char*>(malloc(len + 20));
- strcpy(err_src, "assert failed: ");
- prefix_len = strlen(err_src);
- memcpy(err_src + prefix_len, state->script + argv[i]->start, len);
- err_src[prefix_len + len] = '\0';
- free(state->errmsg);
- state->errmsg = err_src;
- return NULL;
+ state->errmsg = "assert failed: " + state->script.substr(argv[i]->start, len);
+ return nullptr;
}
}
- return StringValue(strdup(""));
+ return StringValue("");
}
-Value* SleepFn(const char* name, State* state, int argc, Expr* argv[]) {
- char* val = Evaluate(state, argv[0]);
- if (val == NULL) {
- return NULL;
+Value* SleepFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+ std::string val;
+ if (!Evaluate(state, argv[0], &val)) {
+ return nullptr;
}
- int v = strtol(val, NULL, 10);
+
+ int v;
+ if (!android::base::ParseInt(val.c_str(), &v, 0)) {
+ return nullptr;
+ }
sleep(v);
+
return StringValue(val);
}
-Value* StdoutFn(const char* name, State* state, int argc, Expr* argv[]) {
- int i;
- for (i = 0; i < argc; ++i) {
- char* v = Evaluate(state, argv[i]);
- if (v == NULL) {
- return NULL;
+Value* StdoutFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+ for (size_t i = 0; i < argv.size(); ++i) {
+ std::string v;
+ if (!Evaluate(state, argv[i], &v)) {
+ return nullptr;
}
- fputs(v, stdout);
- free(v);
+ fputs(v.c_str(), stdout);
}
- return StringValue(strdup(""));
+ return StringValue("");
}
Value* LogicalAndFn(const char* name, State* state,
- int argc, Expr* argv[]) {
- char* left = Evaluate(state, argv[0]);
- if (left == NULL) return NULL;
- if (BooleanString(left) == true) {
- free(left);
+ const std::vector<std::unique_ptr<Expr>>& argv) {
+ std::string left;
+ if (!Evaluate(state, argv[0], &left)) {
+ return nullptr;
+ }
+ if (BooleanString(left)) {
return EvaluateValue(state, argv[1]);
} else {
- return StringValue(left);
+ return StringValue("");
}
}
Value* LogicalOrFn(const char* name, State* state,
- int argc, Expr* argv[]) {
- char* left = Evaluate(state, argv[0]);
- if (left == NULL) return NULL;
- if (BooleanString(left) == false) {
- free(left);
+ const std::vector<std::unique_ptr<Expr>>& argv) {
+ std::string left;
+ if (!Evaluate(state, argv[0], &left)) {
+ return nullptr;
+ }
+ if (!BooleanString(left)) {
return EvaluateValue(state, argv[1]);
} else {
return StringValue(left);
@@ -216,174 +187,144 @@
}
Value* LogicalNotFn(const char* name, State* state,
- int argc, Expr* argv[]) {
- char* val = Evaluate(state, argv[0]);
- if (val == NULL) return NULL;
- bool bv = BooleanString(val);
- free(val);
- return StringValue(strdup(bv ? "" : "t"));
+ const std::vector<std::unique_ptr<Expr>>& argv) {
+ std::string val;
+ if (!Evaluate(state, argv[0], &val)) {
+ return nullptr;
+ }
+
+ return StringValue(BooleanString(val) ? "" : "t");
}
Value* SubstringFn(const char* name, State* state,
- int argc, Expr* argv[]) {
- char* needle = Evaluate(state, argv[0]);
- if (needle == NULL) return NULL;
- char* haystack = Evaluate(state, argv[1]);
- if (haystack == NULL) {
- free(needle);
- return NULL;
+ const std::vector<std::unique_ptr<Expr>>& argv) {
+ std::string needle;
+ if (!Evaluate(state, argv[0], &needle)) {
+ return nullptr;
}
- char* result = strdup(strstr(haystack, needle) ? "t" : "");
- free(needle);
- free(haystack);
+ std::string haystack;
+ if (!Evaluate(state, argv[1], &haystack)) {
+ return nullptr;
+ }
+
+ std::string result = (haystack.find(needle) != std::string::npos) ? "t" : "";
return StringValue(result);
}
-Value* EqualityFn(const char* name, State* state, int argc, Expr* argv[]) {
- char* left = Evaluate(state, argv[0]);
- if (left == NULL) return NULL;
- char* right = Evaluate(state, argv[1]);
- if (right == NULL) {
- free(left);
- return NULL;
+Value* EqualityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+ std::string left;
+ if (!Evaluate(state, argv[0], &left)) {
+ return nullptr;
+ }
+ std::string right;
+ if (!Evaluate(state, argv[1], &right)) {
+ return nullptr;
}
- char* result = strdup(strcmp(left, right) == 0 ? "t" : "");
- free(left);
- free(right);
+ const char* result = (left == right) ? "t" : "";
return StringValue(result);
}
-Value* InequalityFn(const char* name, State* state, int argc, Expr* argv[]) {
- char* left = Evaluate(state, argv[0]);
- if (left == NULL) return NULL;
- char* right = Evaluate(state, argv[1]);
- if (right == NULL) {
- free(left);
- return NULL;
+Value* InequalityFn(const char* name, State* state,
+ const std::vector<std::unique_ptr<Expr>>& argv) {
+ std::string left;
+ if (!Evaluate(state, argv[0], &left)) {
+ return nullptr;
+ }
+ std::string right;
+ if (!Evaluate(state, argv[1], &right)) {
+ return nullptr;
}
- char* result = strdup(strcmp(left, right) != 0 ? "t" : "");
- free(left);
- free(right);
+ const char* result = (left != right) ? "t" : "";
return StringValue(result);
}
-Value* SequenceFn(const char* name, State* state, int argc, Expr* argv[]) {
- Value* left = EvaluateValue(state, argv[0]);
- if (left == NULL) return NULL;
- FreeValue(left);
+Value* SequenceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+ std::unique_ptr<Value> left(EvaluateValue(state, argv[0]));
+ if (!left) {
+ return nullptr;
+ }
return EvaluateValue(state, argv[1]);
}
-Value* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) {
- if (argc != 2) {
- free(state->errmsg);
- state->errmsg = strdup("less_than_int expects 2 arguments");
- return NULL;
+Value* LessThanIntFn(const char* name, State* state,
+ const std::vector<std::unique_ptr<Expr>>& argv) {
+ if (argv.size() != 2) {
+ state->errmsg = "less_than_int expects 2 arguments";
+ return nullptr;
}
- char* left;
- char* right;
- if (ReadArgs(state, argv, 2, &left, &right) < 0) return NULL;
-
- bool result = false;
- char* end;
-
- long l_int = strtol(left, &end, 10);
- if (left[0] == '\0' || *end != '\0') {
- goto done;
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argv, &args)) {
+ return nullptr;
}
- long r_int;
- r_int = strtol(right, &end, 10);
- if (right[0] == '\0' || *end != '\0') {
- goto done;
+ // Parse up to at least long long or 64-bit integers.
+ int64_t l_int;
+ if (!android::base::ParseInt(args[0].c_str(), &l_int)) {
+ state->errmsg = "failed to parse int in " + args[0];
+ return nullptr;
}
- result = l_int < r_int;
+ int64_t r_int;
+ if (!android::base::ParseInt(args[1].c_str(), &r_int)) {
+ state->errmsg = "failed to parse int in " + args[1];
+ return nullptr;
+ }
- done:
- free(left);
- free(right);
- return StringValue(strdup(result ? "t" : ""));
+ return StringValue(l_int < r_int ? "t" : "");
}
Value* GreaterThanIntFn(const char* name, State* state,
- int argc, Expr* argv[]) {
- if (argc != 2) {
- free(state->errmsg);
- state->errmsg = strdup("greater_than_int expects 2 arguments");
- return NULL;
+ const std::vector<std::unique_ptr<Expr>>& argv) {
+ if (argv.size() != 2) {
+ state->errmsg = "greater_than_int expects 2 arguments";
+ return nullptr;
}
- Expr* temp[2];
- temp[0] = argv[1];
- temp[1] = argv[0];
-
- return LessThanIntFn(name, state, 2, temp);
-}
-
-Value* Literal(const char* name, State* state, int argc, Expr* argv[]) {
- return StringValue(strdup(name));
-}
-
-Expr* Build(Function fn, YYLTYPE loc, int count, ...) {
- va_list v;
- va_start(v, count);
- Expr* e = reinterpret_cast<Expr*>(malloc(sizeof(Expr)));
- e->fn = fn;
- e->name = "(operator)";
- e->argc = count;
- e->argv = reinterpret_cast<Expr**>(malloc(count * sizeof(Expr*)));
- int i;
- for (i = 0; i < count; ++i) {
- e->argv[i] = va_arg(v, Expr*);
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argv, &args)) {
+ return nullptr;
}
- va_end(v);
- e->start = loc.start;
- e->end = loc.end;
- return e;
+
+ // Parse up to at least long long or 64-bit integers.
+ int64_t l_int;
+ if (!android::base::ParseInt(args[0].c_str(), &l_int)) {
+ state->errmsg = "failed to parse int in " + args[0];
+ return nullptr;
+ }
+
+ int64_t r_int;
+ if (!android::base::ParseInt(args[1].c_str(), &r_int)) {
+ state->errmsg = "failed to parse int in " + args[1];
+ return nullptr;
+ }
+
+ return StringValue(l_int > r_int ? "t" : "");
+}
+
+Value* Literal(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+ return StringValue(name);
}
// -----------------------------------------------------------------
// the function table
// -----------------------------------------------------------------
-static int fn_entries = 0;
-static int fn_size = 0;
-NamedFunction* fn_table = NULL;
+static std::unordered_map<std::string, Function> fn_table;
-void RegisterFunction(const char* name, Function fn) {
- if (fn_entries >= fn_size) {
- fn_size = fn_size*2 + 1;
- fn_table = reinterpret_cast<NamedFunction*>(realloc(fn_table, fn_size * sizeof(NamedFunction)));
+void RegisterFunction(const std::string& name, Function fn) {
+ fn_table[name] = fn;
+}
+
+Function FindFunction(const std::string& name) {
+ if (fn_table.find(name) == fn_table.end()) {
+ return nullptr;
+ } else {
+ return fn_table[name];
}
- fn_table[fn_entries].name = name;
- fn_table[fn_entries].fn = fn;
- ++fn_entries;
-}
-
-static int fn_entry_compare(const void* a, const void* b) {
- const char* na = ((const NamedFunction*)a)->name;
- const char* nb = ((const NamedFunction*)b)->name;
- return strcmp(na, nb);
-}
-
-void FinishRegistration() {
- qsort(fn_table, fn_entries, sizeof(NamedFunction), fn_entry_compare);
-}
-
-Function FindFunction(const char* name) {
- NamedFunction key;
- key.name = name;
- NamedFunction* nf = reinterpret_cast<NamedFunction*>(bsearch(&key, fn_table, fn_entries,
- sizeof(NamedFunction), fn_entry_compare));
- if (nf == NULL) {
- return NULL;
- }
- return nf->fn;
}
void RegisterBuiltins() {
@@ -404,106 +345,56 @@
// convenience methods for functions
// -----------------------------------------------------------------
-// Evaluate the expressions in argv, giving 'count' char* (the ... is
-// zero or more char** to put them in). If any expression evaluates
-// to NULL, free the rest and return -1. Return 0 on success.
-int ReadArgs(State* state, Expr* argv[], int count, ...) {
- char** args = reinterpret_cast<char**>(malloc(count * sizeof(char*)));
- va_list v;
- va_start(v, count);
- int i;
- for (i = 0; i < count; ++i) {
- args[i] = Evaluate(state, argv[i]);
- if (args[i] == NULL) {
- va_end(v);
- int j;
- for (j = 0; j < i; ++j) {
- free(args[j]);
- }
- free(args);
- return -1;
- }
- *(va_arg(v, char**)) = args[i];
- }
- va_end(v);
- free(args);
- return 0;
+// Evaluate the expressions in argv, and put the results of strings in args. If any expression
+// evaluates to nullptr, return false. Return true on success.
+bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+ std::vector<std::string>* args) {
+ return ReadArgs(state, argv, args, 0, argv.size());
}
-// Evaluate the expressions in argv, giving 'count' Value* (the ... is
-// zero or more Value** to put them in). If any expression evaluates
-// to NULL, free the rest and return -1. Return 0 on success.
-int ReadValueArgs(State* state, Expr* argv[], int count, ...) {
- Value** args = reinterpret_cast<Value**>(malloc(count * sizeof(Value*)));
- va_list v;
- va_start(v, count);
- int i;
- for (i = 0; i < count; ++i) {
- args[i] = EvaluateValue(state, argv[i]);
- if (args[i] == NULL) {
- va_end(v);
- int j;
- for (j = 0; j < i; ++j) {
- FreeValue(args[j]);
- }
- free(args);
- return -1;
- }
- *(va_arg(v, Value**)) = args[i];
+bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+ std::vector<std::string>* args, size_t start, size_t len) {
+ if (args == nullptr) {
+ return false;
}
- va_end(v);
- free(args);
- return 0;
+ if (start + len > argv.size()) {
+ return false;
+ }
+ for (size_t i = start; i < start + len; ++i) {
+ std::string var;
+ if (!Evaluate(state, argv[i], &var)) {
+ args->clear();
+ return false;
+ }
+ args->push_back(var);
+ }
+ return true;
}
-// Evaluate the expressions in argv, returning an array of char*
-// results. If any evaluate to NULL, free the rest and return NULL.
-// The caller is responsible for freeing the returned array and the
-// strings it contains.
-char** ReadVarArgs(State* state, int argc, Expr* argv[]) {
- char** args = (char**)malloc(argc * sizeof(char*));
- int i = 0;
- for (i = 0; i < argc; ++i) {
- args[i] = Evaluate(state, argv[i]);
- if (args[i] == NULL) {
- int j;
- for (j = 0; j < i; ++j) {
- free(args[j]);
- }
- free(args);
- return NULL;
- }
- }
- return args;
+// Evaluate the expressions in argv, and put the results of Value* in args. If any expression
+// evaluate to nullptr, return false. Return true on success.
+bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+ std::vector<std::unique_ptr<Value>>* args) {
+ return ReadValueArgs(state, argv, args, 0, argv.size());
}
-// Evaluate the expressions in argv, returning an array of Value*
-// results. If any evaluate to NULL, free the rest and return NULL.
-// The caller is responsible for freeing the returned array and the
-// Values it contains.
-Value** ReadValueVarArgs(State* state, int argc, Expr* argv[]) {
- Value** args = (Value**)malloc(argc * sizeof(Value*));
- int i = 0;
- for (i = 0; i < argc; ++i) {
- args[i] = EvaluateValue(state, argv[i]);
- if (args[i] == NULL) {
- int j;
- for (j = 0; j < i; ++j) {
- FreeValue(args[j]);
- }
- free(args);
- return NULL;
- }
+bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+ std::vector<std::unique_ptr<Value>>* args, size_t start, size_t len) {
+ if (args == nullptr) {
+ return false;
}
- return args;
-}
-
-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 = strdup(buffer.c_str());
- return;
+ if (len == 0 || start + len > argv.size()) {
+ return false;
+ }
+ for (size_t i = start; i < start + len; ++i) {
+ std::unique_ptr<Value> v(EvaluateValue(state, argv[i]));
+ if (!v) {
+ args->clear();
+ return false;
+ }
+ args->push_back(std::move(v));
+ }
+ return true;
}
// Use printf-style arguments to compose an error message to put into
@@ -511,7 +402,7 @@
Value* ErrorAbort(State* state, const char* format, ...) {
va_list ap;
va_start(ap, format);
- ErrorAbortV(state, format, ap);
+ android::base::StringAppendV(&state->errmsg, format, ap);
va_end(ap);
return nullptr;
}
@@ -519,8 +410,14 @@
Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) {
va_list ap;
va_start(ap, format);
- ErrorAbortV(state, format, ap);
+ android::base::StringAppendV(&state->errmsg, format, ap);
va_end(ap);
state->cause_code = cause_code;
return nullptr;
}
+
+State::State(const std::string& script, void* cookie) :
+ script(script),
+ cookie(cookie) {
+}
+
diff --git a/edify/expr.h b/edify/expr.h
index 8863479..4838d20 100644
--- a/edify/expr.h
+++ b/edify/expr.h
@@ -19,27 +19,26 @@
#include <unistd.h>
+#include <memory>
+#include <string>
+#include <vector>
+
#include "error_code.h"
-#include "yydefs.h"
-#define MAX_STRING_LEN 1024
+struct State {
+ State(const std::string& script, void* cookie);
-typedef struct Expr Expr;
+ // The source of the original script.
+ const std::string& script;
-typedef struct {
// Optional pointer to app-specific data; the core of edify never
// uses this value.
void* cookie;
- // The source of the original script. Must be NULL-terminated,
- // and in writable memory (Evaluate may make temporary changes to
- // it but will restore it when done).
- char* script;
-
// The error message (if any) returned if the evaluation aborts.
- // Should be NULL initially, will be either NULL or a malloc'd
- // pointer after Evaluate() returns.
- char* errmsg;
+ // Should be empty initially, will be either empty or a string that
+ // Evaluate() returns.
+ std::string errmsg;
// error code indicates the type of failure (e.g. failure to update system image)
// during the OTA process.
@@ -50,117 +49,95 @@
CauseCode cause_code = kNoCause;
bool is_retry = false;
-
-} State;
-
-#define VAL_STRING 1 // data will be NULL-terminated; size doesn't count null
-#define VAL_BLOB 2
-
-typedef struct {
- int type;
- ssize_t size;
- char* data;
-} Value;
-
-typedef Value* (*Function)(const char* name, State* state,
- int argc, Expr* argv[]);
-
-struct Expr {
- Function fn;
- const char* name;
- int argc;
- Expr** argv;
- int start, end;
};
-// Take one of the Expr*s passed to the function as an argument,
-// evaluate it, return the resulting Value. The caller takes
-// ownership of the returned Value.
-Value* EvaluateValue(State* state, Expr* expr);
+enum ValueType {
+ VAL_INVALID = -1,
+ VAL_STRING = 1,
+ VAL_BLOB = 2,
+};
-// Take one of the Expr*s passed to the function as an argument,
-// evaluate it, assert that it is a string, and return the resulting
-// char*. The caller takes ownership of the returned char*. This is
-// a convenience function for older functions that want to deal only
-// with strings.
-char* Evaluate(State* state, Expr* expr);
+struct Value {
+ ValueType type;
+ std::string data;
+
+ Value(ValueType type, const std::string& str) :
+ type(type),
+ data(str) {}
+};
+
+struct Expr;
+
+using Function = Value* (*)(const char* name, State* state,
+ const std::vector<std::unique_ptr<Expr>>& argv);
+
+struct Expr {
+ Function fn;
+ std::string name;
+ std::vector<std::unique_ptr<Expr>> argv;
+ int start, end;
+
+ Expr(Function fn, const std::string& name, int start, int end) :
+ fn(fn),
+ name(name),
+ start(start),
+ end(end) {}
+};
+
+// Evaluate the input expr, return the resulting Value.
+Value* EvaluateValue(State* state, const std::unique_ptr<Expr>& expr);
+
+// Evaluate the input expr, assert that it is a string, and update the result parameter. This
+// function returns true if the evaluation succeeds. This is a convenience function for older
+// functions that want to deal only with strings.
+bool Evaluate(State* state, const std::unique_ptr<Expr>& expr, std::string* result);
// Glue to make an Expr out of a literal.
-Value* Literal(const char* name, State* state, int argc, Expr* argv[]);
+Value* Literal(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
// Functions corresponding to various syntactic sugar operators.
// ("concat" is also available as a builtin function, to concatenate
// more than two strings.)
-Value* ConcatFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* LogicalAndFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* LogicalOrFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* LogicalNotFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* SubstringFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* EqualityFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* InequalityFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* SequenceFn(const char* name, State* state, int argc, Expr* argv[]);
-
-// Convenience function for building expressions with a fixed number
-// of arguments.
-Expr* Build(Function fn, YYLTYPE loc, int count, ...);
+Value* ConcatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* LogicalAndFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* LogicalOrFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* LogicalNotFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* SubstringFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* EqualityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* InequalityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* SequenceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
// Global builtins, registered by RegisterBuiltins().
-Value* IfElseFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* AssertFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* AbortFn(const char* name, State* state, int argc, Expr* argv[]);
-
-
-// For setting and getting the global error string (when returning
-// NULL from a function).
-void SetError(const char* message); // makes a copy
-const char* GetError(); // retains ownership
-void ClearError();
-
-
-typedef struct {
- const char* name;
- Function fn;
-} NamedFunction;
+Value* IfElseFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* AssertFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* AbortFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
// Register a new function. The same Function may be registered under
// multiple names, but a given name should only be used once.
-void RegisterFunction(const char* name, Function fn);
+void RegisterFunction(const std::string& name, Function fn);
// Register all the builtins.
void RegisterBuiltins();
-// Call this after all calls to RegisterFunction() but before parsing
-// any scripts to finish building the function table.
-void FinishRegistration();
-
// Find the Function for a given name; return NULL if no such function
// exists.
-Function FindFunction(const char* name);
-
+Function FindFunction(const std::string& name);
// --- convenience functions for use in functions ---
-// Evaluate the expressions in argv, giving 'count' char* (the ... is
-// zero or more char** to put them in). If any expression evaluates
-// to NULL, free the rest and return -1. Return 0 on success.
-int ReadArgs(State* state, Expr* argv[], int count, ...);
+// Evaluate the expressions in argv, and put the results of strings in args. If any expression
+// evaluates to nullptr, return false. Return true on success.
+bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+ std::vector<std::string>* args);
+bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+ std::vector<std::string>* args, size_t start, size_t len);
-// Evaluate the expressions in argv, giving 'count' Value* (the ... is
-// zero or more Value** to put them in). If any expression evaluates
-// to NULL, free the rest and return -1. Return 0 on success.
-int ReadValueArgs(State* state, Expr* argv[], int count, ...);
-
-// Evaluate the expressions in argv, returning an array of char*
-// results. If any evaluate to NULL, free the rest and return NULL.
-// The caller is responsible for freeing the returned array and the
-// strings it contains.
-char** ReadVarArgs(State* state, int argc, Expr* argv[]);
-
-// Evaluate the expressions in argv, returning an array of Value*
-// results. If any evaluate to NULL, free the rest and return NULL.
-// The caller is responsible for freeing the returned array and the
-// Values it contains.
-Value** ReadValueVarArgs(State* state, int argc, Expr* argv[]);
+// Evaluate the expressions in argv, and put the results of Value* in args. If any
+// expression evaluate to nullptr, return false. Return true on success.
+bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+ std::vector<std::unique_ptr<Value>>* args);
+bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+ std::vector<std::unique_ptr<Value>>* args, size_t start, size_t len);
// Use printf-style arguments to compose an error message to put into
// *state. Returns NULL.
@@ -172,12 +149,11 @@
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);
+// Copying the string into a Value.
+Value* StringValue(const char* str);
-// Free a Value object.
-void FreeValue(Value* v);
+Value* StringValue(const std::string& str);
-int parse_string(const char* str, Expr** root, int* error_count);
+int parse_string(const char* str, std::unique_ptr<Expr>* root, int* error_count);
#endif // _EXPRESSION_H
diff --git a/edify/main.cpp b/edify/main.cpp
deleted file mode 100644
index 6007a3d..0000000
--- a/edify/main.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-
-#include "expr.h"
-#include "parser.h"
-
-extern int yyparse(Expr** root, int* error_count);
-
-int expect(const char* expr_str, const char* expected, int* errors) {
- Expr* e;
- char* result;
-
- printf(".");
-
- int error_count = parse_string(expr_str, &e, &error_count);
- if (error_count > 0) {
- printf("error parsing \"%s\" (%d errors)\n",
- expr_str, error_count);
- ++*errors;
- return 0;
- }
-
- State state;
- state.cookie = NULL;
- state.script = strdup(expr_str);
- state.errmsg = NULL;
-
- result = Evaluate(&state, e);
- free(state.errmsg);
- free(state.script);
- if (result == NULL && expected != NULL) {
- printf("error evaluating \"%s\"\n", expr_str);
- ++*errors;
- return 0;
- }
-
- if (result == NULL && expected == NULL) {
- return 1;
- }
-
- if (strcmp(result, expected) != 0) {
- printf("evaluating \"%s\": expected \"%s\", got \"%s\"\n",
- expr_str, expected, result);
- ++*errors;
- free(result);
- return 0;
- }
-
- free(result);
- return 1;
-}
-
-int test() {
- int errors = 0;
-
- expect("a", "a", &errors);
- expect("\"a\"", "a", &errors);
- expect("\"\\x61\"", "a", &errors);
- expect("# this is a comment\n"
- " a\n"
- " \n",
- "a", &errors);
-
-
- // sequence operator
- expect("a; b; c", "c", &errors);
-
- // string concat operator
- expect("a + b", "ab", &errors);
- expect("a + \n \"b\"", "ab", &errors);
- expect("a + b +\nc\n", "abc", &errors);
-
- // string concat function
- expect("concat(a, b)", "ab", &errors);
- expect("concat(a,\n \"b\")", "ab", &errors);
- expect("concat(a + b,\nc,\"d\")", "abcd", &errors);
- expect("\"concat\"(a + b,\nc,\"d\")", "abcd", &errors);
-
- // logical and
- expect("a && b", "b", &errors);
- expect("a && \"\"", "", &errors);
- expect("\"\" && b", "", &errors);
- expect("\"\" && \"\"", "", &errors);
- expect("\"\" && abort()", "", &errors); // test short-circuiting
- expect("t && abort()", NULL, &errors);
-
- // logical or
- expect("a || b", "a", &errors);
- expect("a || \"\"", "a", &errors);
- expect("\"\" || b", "b", &errors);
- expect("\"\" || \"\"", "", &errors);
- expect("a || abort()", "a", &errors); // test short-circuiting
- expect("\"\" || abort()", NULL, &errors);
-
- // logical not
- expect("!a", "", &errors);
- expect("! \"\"", "t", &errors);
- expect("!!a", "t", &errors);
-
- // precedence
- expect("\"\" == \"\" && b", "b", &errors);
- expect("a + b == ab", "t", &errors);
- expect("ab == a + b", "t", &errors);
- expect("a + (b == ab)", "a", &errors);
- expect("(ab == a) + b", "b", &errors);
-
- // substring function
- expect("is_substring(cad, abracadabra)", "t", &errors);
- expect("is_substring(abrac, abracadabra)", "t", &errors);
- expect("is_substring(dabra, abracadabra)", "t", &errors);
- expect("is_substring(cad, abracxadabra)", "", &errors);
- expect("is_substring(abrac, axbracadabra)", "", &errors);
- expect("is_substring(dabra, abracadabrxa)", "", &errors);
-
- // ifelse function
- expect("ifelse(t, yes, no)", "yes", &errors);
- expect("ifelse(!t, yes, no)", "no", &errors);
- expect("ifelse(t, yes, abort())", "yes", &errors);
- expect("ifelse(!t, abort(), no)", "no", &errors);
-
- // if "statements"
- expect("if t then yes else no endif", "yes", &errors);
- expect("if \"\" then yes else no endif", "no", &errors);
- expect("if \"\" then yes endif", "", &errors);
- expect("if \"\"; t then yes endif", "yes", &errors);
-
- // numeric comparisons
- expect("less_than_int(3, 14)", "t", &errors);
- expect("less_than_int(14, 3)", "", &errors);
- expect("less_than_int(x, 3)", "", &errors);
- expect("less_than_int(3, x)", "", &errors);
- expect("greater_than_int(3, 14)", "", &errors);
- expect("greater_than_int(14, 3)", "t", &errors);
- 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;
-}
-
-void ExprDump(int depth, Expr* n, char* script) {
- printf("%*s", depth*2, "");
- char temp = script[n->end];
- script[n->end] = '\0';
- printf("%s %p (%d-%d) \"%s\"\n",
- n->name == NULL ? "(NULL)" : n->name, n->fn, n->start, n->end,
- script+n->start);
- script[n->end] = temp;
- int i;
- for (i = 0; i < n->argc; ++i) {
- ExprDump(depth+1, n->argv[i], script);
- }
-}
-
-int main(int argc, char** argv) {
- RegisterBuiltins();
- FinishRegistration();
-
- if (argc == 1) {
- return test() != 0;
- }
-
- FILE* f = fopen(argv[1], "r");
- if (f == NULL) {
- printf("%s: %s: No such file or directory\n", argv[0], argv[1]);
- return 1;
- }
- char buffer[8192];
- int size = fread(buffer, 1, 8191, f);
- fclose(f);
- buffer[size] = '\0';
-
- Expr* root;
- int error_count = 0;
- int error = parse_string(buffer, &root, &error_count);
- printf("parse returned %d; %d errors encountered\n", error, error_count);
- if (error == 0 || error_count > 0) {
-
- ExprDump(0, root, buffer);
-
- State state;
- state.cookie = NULL;
- state.script = buffer;
- state.errmsg = NULL;
-
- char* result = Evaluate(&state, root);
- if (result == NULL) {
- printf("result was NULL, message is: %s\n",
- (state.errmsg == NULL ? "(NULL)" : state.errmsg));
- free(state.errmsg);
- } else {
- printf("result is [%s]\n", result);
- }
- }
- return 0;
-}
diff --git a/edify/parser.yy b/edify/parser.yy
index 098a637..97205fe 100644
--- a/edify/parser.yy
+++ b/edify/parser.yy
@@ -19,6 +19,10 @@
#include <stdlib.h>
#include <string.h>
+#include <memory>
+#include <string>
+#include <vector>
+
#include "expr.h"
#include "yydefs.h"
#include "parser.h"
@@ -26,13 +30,26 @@
extern int gLine;
extern int gColumn;
-void yyerror(Expr** root, int* error_count, const char* s);
-int yyparse(Expr** root, int* error_count);
+void yyerror(std::unique_ptr<Expr>* root, int* error_count, const char* s);
+int yyparse(std::unique_ptr<Expr>* root, int* error_count);
struct yy_buffer_state;
void yy_switch_to_buffer(struct yy_buffer_state* new_buffer);
struct yy_buffer_state* yy_scan_string(const char* yystr);
+// Convenience function for building expressions with a fixed number
+// of arguments.
+static Expr* Build(Function fn, YYLTYPE loc, size_t count, ...) {
+ va_list v;
+ va_start(v, count);
+ Expr* e = new Expr(fn, "(operator)", loc.start, loc.end);
+ for (size_t i = 0; i < count; ++i) {
+ e->argv.emplace_back(va_arg(v, Expr*));
+ }
+ va_end(v);
+ return e;
+}
+
%}
%locations
@@ -40,10 +57,7 @@
%union {
char* str;
Expr* expr;
- struct {
- int argc;
- Expr** argv;
- } args;
+ std::vector<std::unique_ptr<Expr>>* args;
}
%token AND OR SUBSTR SUPERSTR EQ NE IF THEN ELSE ENDIF
@@ -51,7 +65,10 @@
%type <expr> expr
%type <args> arglist
-%parse-param {Expr** root}
+%destructor { delete $$; } expr
+%destructor { delete $$; } arglist
+
+%parse-param {std::unique_ptr<Expr>* root}
%parse-param {int* error_count}
%error-verbose
@@ -66,17 +83,11 @@
%%
-input: expr { *root = $1; }
+input: expr { root->reset($1); }
;
expr: STRING {
- $$ = reinterpret_cast<Expr*>(malloc(sizeof(Expr)));
- $$->fn = Literal;
- $$->name = $1;
- $$->argc = 0;
- $$->argv = NULL;
- $$->start = @$.start;
- $$->end = @$.end;
+ $$ = new Expr(Literal, $1, @$.start, @$.end);
}
| '(' expr ')' { $$ = $2; $$->start=@$.start; $$->end=@$.end; }
| expr ';' { $$ = $1; $$->start=@1.start; $$->end=@1.end; }
@@ -91,41 +102,32 @@
| 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 ')' {
- $$ = reinterpret_cast<Expr*>(malloc(sizeof(Expr)));
- $$->fn = FindFunction($1);
- if ($$->fn == NULL) {
- char buffer[256];
- snprintf(buffer, sizeof(buffer), "unknown function \"%s\"", $1);
- yyerror(root, error_count, buffer);
+ Function fn = FindFunction($1);
+ if (fn == nullptr) {
+ std::string msg = "unknown function \"" + std::string($1) + "\"";
+ yyerror(root, error_count, msg.c_str());
YYERROR;
}
- $$->name = $1;
- $$->argc = $3.argc;
- $$->argv = $3.argv;
- $$->start = @$.start;
- $$->end = @$.end;
+ $$ = new Expr(fn, $1, @$.start, @$.end);
+ $$->argv = std::move(*$3);
}
;
arglist: /* empty */ {
- $$.argc = 0;
- $$.argv = NULL;
+ $$ = new std::vector<std::unique_ptr<Expr>>;
}
| expr {
- $$.argc = 1;
- $$.argv = reinterpret_cast<Expr**>(malloc(sizeof(Expr*)));
- $$.argv[0] = $1;
+ $$ = new std::vector<std::unique_ptr<Expr>>;
+ $$->emplace_back($1);
}
| arglist ',' expr {
- $$.argc = $1.argc + 1;
- $$.argv = reinterpret_cast<Expr**>(realloc($$.argv, $$.argc * sizeof(Expr*)));
- $$.argv[$$.argc-1] = $3;
+ $$->push_back(std::unique_ptr<Expr>($3));
}
;
%%
-void yyerror(Expr** root, int* error_count, const char* s) {
+void yyerror(std::unique_ptr<Expr>* root, int* error_count, const char* s) {
if (strlen(s) == 0) {
s = "syntax error";
}
@@ -133,7 +135,7 @@
++*error_count;
}
-int parse_string(const char* str, Expr** root, int* error_count) {
+int parse_string(const char* str, std::unique_ptr<Expr>* root, int* error_count) {
yy_switch_to_buffer(yy_scan_string(str));
return yyparse(root, error_count);
}
diff --git a/error_code.h b/error_code.h
index dfea0eb..cde4ee6 100644
--- a/error_code.h
+++ b/error_code.h
@@ -18,52 +18,54 @@
#define _ERROR_CODE_H_
enum ErrorCode {
- kNoError = -1,
- kLowBattery = 20,
- kZipVerificationFailure,
- kZipOpenFailure,
- kBootreasonInBlacklist
+ kNoError = -1,
+ kLowBattery = 20,
+ kZipVerificationFailure,
+ kZipOpenFailure,
+ kBootreasonInBlacklist,
+ kPackageCompatibilityFailure,
};
enum CauseCode {
- kNoCause = -1,
- kArgsParsingFailure = 100,
- kStashCreationFailure,
- kFileOpenFailure,
- kLseekFailure,
- kFreadFailure,
- kFwriteFailure,
- kFsyncFailure,
- kLibfecFailure,
- kFileGetPropFailure,
- kFileRenameFailure,
- kSymlinkFailure,
- kSetMetadataFailure,
- kTune2FsFailure,
- kRebootFailure,
- kVendorFailure = 200
+ kNoCause = -1,
+ kArgsParsingFailure = 100,
+ kStashCreationFailure,
+ kFileOpenFailure,
+ kLseekFailure,
+ kFreadFailure,
+ kFwriteFailure,
+ kFsyncFailure,
+ kLibfecFailure,
+ kFileGetPropFailure,
+ kFileRenameFailure,
+ kSymlinkFailure,
+ kSetMetadataFailure,
+ kTune2FsFailure,
+ kRebootFailure,
+ kPackageExtractFileFailure,
+ kVendorFailure = 200
};
enum UncryptErrorCode {
- kUncryptNoError = -1,
- kUncryptErrorPlaceholder = 50,
- kUncryptTimeoutError = 100,
- kUncryptFileRemoveError,
- kUncryptFileOpenError,
- kUncryptSocketOpenError,
- kUncryptSocketWriteError,
- kUncryptSocketListenError,
- kUncryptSocketAcceptError,
- kUncryptFstabReadError,
- kUncryptFileStatError,
- kUncryptBlockOpenError,
- kUncryptIoctlError,
- kUncryptReadError,
- kUncryptWriteError,
- kUncryptFileSyncError,
- kUncryptFileCloseError,
- kUncryptFileRenameError,
- kUncryptPackageMissingError,
+ kUncryptNoError = -1,
+ kUncryptErrorPlaceholder = 50,
+ kUncryptTimeoutError = 100,
+ kUncryptFileRemoveError,
+ kUncryptFileOpenError,
+ kUncryptSocketOpenError,
+ kUncryptSocketWriteError,
+ kUncryptSocketListenError,
+ kUncryptSocketAcceptError,
+ kUncryptFstabReadError,
+ kUncryptFileStatError,
+ kUncryptBlockOpenError,
+ kUncryptIoctlError,
+ kUncryptReadError,
+ kUncryptWriteError,
+ kUncryptFileSyncError,
+ kUncryptFileCloseError,
+ kUncryptFileRenameError,
+ kUncryptPackageMissingError,
};
-#endif
+#endif // _ERROR_CODE_H_
diff --git a/etc/Android.mk b/etc/Android.mk
index decd3e2..cda0f37 100644
--- a/etc/Android.mk
+++ b/etc/Android.mk
@@ -51,6 +51,26 @@
include $(BUILD_PREBUILT)
endif
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+ include $(CLEAR_VARS)
+ LOCAL_MODULE := init.recovery.hlthchrg.rc
+ LOCAL_MODULE_TAGS := eng
+ LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+ LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+
+ LOCAL_SRC_FILES := init.recovery.hlthchrg26.rc
+ include $(BUILD_PREBUILT)
+else
+ include $(CLEAR_VARS)
+ LOCAL_MODULE := init.recovery.hlthchrg.rc
+ LOCAL_MODULE_TAGS := eng
+ LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+ LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+
+ LOCAL_SRC_FILES := init.recovery.hlthchrg25.rc
+ include $(BUILD_PREBUILT)
+endif
+
ifeq ($(TWRP_INCLUDE_LOGCAT), true)
ifeq ($(TARGET_USES_LOGD), true)
diff --git a/etc/META-INF/com/google/android/update-script b/etc/META-INF/com/google/android/update-script
deleted file mode 100644
index b091b19..0000000
--- a/etc/META-INF/com/google/android/update-script
+++ /dev/null
@@ -1,8 +0,0 @@
-assert compatible_with("0.1") == "true"
-assert file_contains("SYSTEM:build.prop", "ro.product.device=dream") == "true" || file_contains("SYSTEM:build.prop", "ro.build.product=dream") == "true"
-assert file_contains("RECOVERY:default.prop", "ro.product.device=dream") == "true" || file_contains("RECOVERY:default.prop", "ro.build.product=dream") == "true"
-assert getprop("ro.product.device") == "dream"
-format BOOT:
-format SYSTEM:
-copy_dir PACKAGE:system SYSTEM:
-write_raw_image PACKAGE:boot.img BOOT:
diff --git a/etc/init.rc b/etc/init.rc
index 3523181..d1c4526 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -16,7 +16,6 @@
restorecon /postinstall
start ueventd
- start healthd
service set_permissive /sbin/permissive.sh
oneshot
@@ -46,7 +45,6 @@
on fs
mount pstore pstore /sys/fs/pstore
-
mkdir /dev/usb-ffs 0770 shell shell
mkdir /dev/usb-ffs/adb 0770 shell shell
mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000
@@ -104,10 +102,6 @@
critical
seclabel u:r:ueventd:s0
-service healthd /sbin/healthd -r
- critical
- seclabel u:r:healthd:s0
-
service adbd /sbin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery
disabled
socket adbd stream 660 system system
diff --git a/etc/init.recovery.hlthchrg25.rc b/etc/init.recovery.hlthchrg25.rc
new file mode 100644
index 0000000..62b2489
--- /dev/null
+++ b/etc/init.recovery.hlthchrg25.rc
@@ -0,0 +1,5 @@
+# healthd for pre Android 8.0
+
+service healthd /sbin/healthd -r
+ critical
+ seclabel u:r:healthd:s0
diff --git a/etc/init.recovery.hlthchrg26.rc b/etc/init.recovery.hlthchrg26.rc
new file mode 100644
index 0000000..8a49c86
--- /dev/null
+++ b/etc/init.recovery.hlthchrg26.rc
@@ -0,0 +1,5 @@
+# charger for Android 8.0 and up
+
+service charger /charger -r
+ critical
+ seclabel u:r:charger:s0
diff --git a/fuse_sdcard_provider.cpp b/fuse_sdcard_provider.cpp
index df96312..b0ecf96 100644
--- a/fuse_sdcard_provider.cpp
+++ b/fuse_sdcard_provider.cpp
@@ -23,6 +23,8 @@
#include <unistd.h>
#include <fcntl.h>
+#include <android-base/file.h>
+
#include "fuse_sideload.h"
struct file_data {
@@ -41,14 +43,9 @@
return -EIO;
}
- while (fetch_size > 0) {
- ssize_t r = TEMP_FAILURE_RETRY(read(fd->fd, buffer, fetch_size));
- if (r == -1) {
- fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno));
- return -EIO;
- }
- fetch_size -= r;
- buffer += r;
+ if (!android::base::ReadFully(fd->fd, buffer, fetch_size)) {
+ fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno));
+ return -EIO;
}
return 0;
diff --git a/gui/Android.mk b/gui/Android.mk
index 78e93c6..909850f 100644
--- a/gui/Android.mk
+++ b/gui/Android.mk
@@ -38,7 +38,13 @@
LOCAL_SRC_FILES += hardwarekeyboard.cpp
endif
-LOCAL_SHARED_LIBRARIES += libminuitwrp libc libstdc++ libminzip libaosprecovery libselinux
+LOCAL_SHARED_LIBRARIES += libminuitwrp libc libstdc++ libaosprecovery libselinux
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+ LOCAL_SHARED_LIBRARIES += libziparchive
+else
+ LOCAL_SHARED_LIBRARIES += libminzip
+ LOCAL_CFLAGS += -DUSE_MINZIP
+endif
LOCAL_MODULE := libguitwrp
#TWRP_EVENT_LOGGING := true
diff --git a/gui/action.cpp b/gui/action.cpp
index a395158..05ae465 100644
--- a/gui/action.cpp
+++ b/gui/action.cpp
@@ -43,11 +43,11 @@
#include "../adb_install.h"
#include "../fuse_sideload.h"
#include "blanktimer.hpp"
+#include "../twinstall.h"
extern "C" {
#include "../twcommon.h"
#include "../variables.h"
-#include "../twinstall.h"
#include "cutils/properties.h"
#include "../adb_install.h"
};
diff --git a/gui/pages.cpp b/gui/pages.cpp
index b901526..1199a28 100644
--- a/gui/pages.cpp
+++ b/gui/pages.cpp
@@ -39,12 +39,17 @@
#include <string>
#include <algorithm>
+#ifdef USE_MINZIP
+#include "../minzip/SysUtil.h"
+#else
+#include "../otautil/SysUtil.h"
+#endif
+
extern "C" {
#include "../twcommon.h"
-#include "../minzip/SysUtil.h"
-#include "../minzip/Zip.h"
#include "gui.h"
}
+#include "../zipwrap.hpp"
#include "../minuitwrp/minui.h"
#include "rapidxml.hpp"
@@ -664,7 +669,7 @@
// transient data for loading themes
struct LoadingContext
{
- ZipArchive* zip; // zip to load theme from, or NULL for the stock theme
+ ZipWrap* zip; // zip to load theme from, or NULL for the stock theme
std::set<std::string> filenames; // to detect cyclic includes
std::string basepath; // if zip is NULL, base path to load includes from with trailing slash, otherwise empty
std::vector<xml_document<>*> xmldocs; // all loaded xml docs
@@ -806,7 +811,7 @@
}
}
-int PageSet::LoadLanguage(char* languageFile, ZipArchive* package)
+int PageSet::LoadLanguage(char* languageFile, ZipWrap* package)
{
xml_document<> lang;
xml_node<>* parent;
@@ -1182,7 +1187,7 @@
mResources->AddStringResource(resource_source, resource_name, value);
}
-char* PageManager::LoadFileToBuffer(std::string filename, ZipArchive* package) {
+char* PageManager::LoadFileToBuffer(std::string filename, ZipWrap* package) {
size_t len;
char* buffer = NULL;
@@ -1219,19 +1224,18 @@
close(fd);
} else {
LOGINFO("PageManager::LoadFileToBuffer loading filename: '%s' from zip\n", filename.c_str());
- const ZipEntry* zipentry = mzFindZipEntry(package, filename.c_str());
- if (zipentry == NULL) {
+ if (!package->EntryExists(filename)) {
LOGERR("Unable to locate '%s' in zip file\n", filename.c_str());
return NULL;
}
// Allocate the buffer for the file
- len = mzGetZipEntryUncompLen(zipentry);
+ len = package->GetUncompressedSize(filename);
buffer = (char*) malloc(len + 1);
if (!buffer)
return NULL;
- if (!mzExtractZipEntryToBuffer(package, zipentry, (unsigned char*) buffer)) {
+ if (!package->ExtractToBuffer(filename, (unsigned char*) buffer)) {
LOGERR("Unable to extract '%s'\n", filename.c_str());
free(buffer);
return NULL;
@@ -1295,14 +1299,13 @@
closedir(d);
}
-void PageManager::LoadLanguageList(ZipArchive* package) {
+void PageManager::LoadLanguageList(ZipWrap* package) {
Language_List.clear();
if (TWFunc::Path_Exists(TWRES "customlanguages"))
TWFunc::removeDir(TWRES "customlanguages", true);
if (package) {
TWFunc::Recursive_Mkdir(TWRES "customlanguages");
- struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
- mzExtractRecursive(package, "languages", TWRES "customlanguages/", ×tamp, NULL, NULL, NULL);
+ package->ExtractRecursive("languages", TWRES "customlanguages/");
LoadLanguageListDir(TWRES "customlanguages/");
} else {
LoadLanguageListDir(TWRES "languages/");
@@ -1330,7 +1333,7 @@
int PageManager::LoadPackage(std::string name, std::string package, std::string startpage)
{
std::string mainxmlfilename = package;
- ZipArchive zip;
+ ZipWrap zip;
char* languageFile = NULL;
char* baseLanguageFile = NULL;
PageSet* pageSet = NULL;
@@ -1371,7 +1374,7 @@
LOGERR("Failed to map '%s'\n", package.c_str());
goto error;
}
- if (mzOpenZipArchive(map.addr, map.length, &zip)) {
+ if (!zip.Open(package.c_str(), &map)) {
LOGERR("Unable to open zip archive '%s'\n", package.c_str());
sysReleaseMap(&map);
goto error;
@@ -1414,7 +1417,7 @@
mCurrentSet = pageSet;
if (ctx.zip) {
- mzCloseZipArchive(ctx.zip);
+ ctx.zip->Close();
sysReleaseMap(&map);
}
return ret;
@@ -1422,7 +1425,7 @@
error:
// Sometimes we get here without a real error
if (ctx.zip) {
- mzCloseZipArchive(ctx.zip);
+ ctx.zip->Close();
sysReleaseMap(&map);
}
return -1;
diff --git a/gui/pages.hpp b/gui/pages.hpp
index 1e71b10..282b2d5 100644
--- a/gui/pages.hpp
+++ b/gui/pages.hpp
@@ -21,7 +21,7 @@
#ifndef _PAGES_HEADER_HPP
#define _PAGES_HEADER_HPP
-#include "../minzip/Zip.h"
+#include "../zipwrap.hpp"
#include <vector>
#include <map>
#include <string>
@@ -116,7 +116,7 @@
public:
int Load(LoadingContext& ctx, const std::string& filename);
- int LoadLanguage(char* languageFile, ZipArchive* package);
+ int LoadLanguage(char* languageFile, ZipWrap* package);
void MakeEmergencyConsoleIfNeeded();
Page* FindPage(std::string name);
@@ -155,8 +155,8 @@
{
public:
// Used by GUI
- static char* LoadFileToBuffer(std::string filename, ZipArchive* package);
- static void LoadLanguageList(ZipArchive* package);
+ static char* LoadFileToBuffer(std::string filename, ZipWrap* package);
+ static void LoadLanguageList(ZipWrap* package);
static void LoadLanguage(std::string filename);
static int LoadPackage(std::string name, std::string package, std::string startpage);
static PageSet* SelectPackage(std::string name);
diff --git a/gui/resources.cpp b/gui/resources.cpp
index bd71d50..59b8ed4 100644
--- a/gui/resources.cpp
+++ b/gui/resources.cpp
@@ -30,7 +30,7 @@
#include <iomanip>
#include <fcntl.h>
-#include "../minzip/Zip.h"
+#include "../zipwrap.hpp"
extern "C" {
#include "../twcommon.h"
#include "gui.h"
@@ -42,38 +42,24 @@
#define TMP_RESOURCE_NAME "/tmp/extract.bin"
-Resource::Resource(xml_node<>* node, ZipArchive* pZip __unused)
+Resource::Resource(xml_node<>* node, ZipWrap* pZip __unused)
{
if (node && node->first_attribute("name"))
mName = node->first_attribute("name")->value();
}
-int Resource::ExtractResource(ZipArchive* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile)
+int Resource::ExtractResource(ZipWrap* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile)
{
if (!pZip)
return -1;
std::string src = folderName + "/" + fileName + fileExtn;
-
- const ZipEntry* binary = mzFindZipEntry(pZip, src.c_str());
- if (binary == NULL) {
+ if (!pZip->ExtractEntry(src, destFile, 0666))
return -1;
- }
-
- unlink(destFile.c_str());
- int fd = creat(destFile.c_str(), 0666);
- if (fd < 0)
- return -1;
-
- int ret = 0;
- if (!mzExtractZipEntryToFile(pZip, binary, fd))
- ret = -1;
-
- close(fd);
- return ret;
+ return 0;
}
-void Resource::LoadImage(ZipArchive* pZip, std::string file, gr_surface* surface)
+void Resource::LoadImage(ZipWrap* pZip, std::string file, gr_surface* surface)
{
int rc = 0;
if (ExtractResource(pZip, "images", file, ".png", TMP_RESOURCE_NAME) == 0)
@@ -119,7 +105,7 @@
}
}
-FontResource::FontResource(xml_node<>* node, ZipArchive* pZip)
+FontResource::FontResource(xml_node<>* node, ZipWrap* pZip)
: Resource(node, pZip)
{
origFontSize = 0;
@@ -127,7 +113,7 @@
LoadFont(node, pZip);
}
-void FontResource::LoadFont(xml_node<>* node, ZipArchive* pZip)
+void FontResource::LoadFont(xml_node<>* node, ZipWrap* pZip)
{
std::string file;
xml_attribute<>* attr;
@@ -192,7 +178,7 @@
origFont = NULL;
}
-void FontResource::Override(xml_node<>* node, ZipArchive* pZip) {
+void FontResource::Override(xml_node<>* node, ZipWrap* pZip) {
if (!origFont) {
origFont = mFont;
} else if (mFont) {
@@ -207,7 +193,7 @@
DeleteFont();
}
-ImageResource::ImageResource(xml_node<>* node, ZipArchive* pZip)
+ImageResource::ImageResource(xml_node<>* node, ZipWrap* pZip)
: Resource(node, pZip)
{
std::string file;
@@ -238,7 +224,7 @@
res_free_surface(mSurface);
}
-AnimationResource::AnimationResource(xml_node<>* node, ZipArchive* pZip)
+AnimationResource::AnimationResource(xml_node<>* node, ZipWrap* pZip)
: Resource(node, pZip)
{
std::string file;
@@ -359,7 +345,7 @@
mStrings[resource_name] = res;
}
-void ResourceManager::LoadResources(xml_node<>* resList, ZipArchive* pZip, std::string resource_source)
+void ResourceManager::LoadResources(xml_node<>* resList, ZipWrap* pZip, std::string resource_source)
{
if (!resList)
return;
diff --git a/gui/resources.hpp b/gui/resources.hpp
index 2d30676..de67318 100644
--- a/gui/resources.hpp
+++ b/gui/resources.hpp
@@ -25,8 +25,7 @@
#include <vector>
#include <map>
#include "rapidxml.hpp"
-
-struct ZipArchive;
+#include "../zipwrap.hpp"
extern "C" {
#include "../minuitwrp/minui.h"
@@ -36,7 +35,7 @@
class Resource
{
public:
- Resource(xml_node<>* node, ZipArchive* pZip);
+ Resource(xml_node<>* node, ZipWrap* pZip);
virtual ~Resource() {}
public:
@@ -46,27 +45,27 @@
std::string mName;
protected:
- static int ExtractResource(ZipArchive* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile);
- static void LoadImage(ZipArchive* pZip, std::string file, gr_surface* surface);
+ static int ExtractResource(ZipWrap* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile);
+ static void LoadImage(ZipWrap* pZip, std::string file, gr_surface* surface);
static void CheckAndScaleImage(gr_surface source, gr_surface* destination, int retain_aspect);
};
class FontResource : public Resource
{
public:
- FontResource(xml_node<>* node, ZipArchive* pZip);
+ FontResource(xml_node<>* node, ZipWrap* pZip);
virtual ~FontResource();
public:
void* GetResource() { return this ? mFont : NULL; }
int GetHeight() { return gr_ttf_getMaxFontHeight(this ? mFont : NULL); }
- void Override(xml_node<>* node, ZipArchive* pZip);
+ void Override(xml_node<>* node, ZipWrap* pZip);
protected:
void* mFont;
private:
- void LoadFont(xml_node<>* node, ZipArchive* pZip);
+ void LoadFont(xml_node<>* node, ZipWrap* pZip);
void DeleteFont();
private:
@@ -77,7 +76,7 @@
class ImageResource : public Resource
{
public:
- ImageResource(xml_node<>* node, ZipArchive* pZip);
+ ImageResource(xml_node<>* node, ZipWrap* pZip);
virtual ~ImageResource();
public:
@@ -92,7 +91,7 @@
class AnimationResource : public Resource
{
public:
- AnimationResource(xml_node<>* node, ZipArchive* pZip);
+ AnimationResource(xml_node<>* node, ZipWrap* pZip);
virtual ~AnimationResource();
public:
@@ -112,7 +111,7 @@
ResourceManager();
virtual ~ResourceManager();
void AddStringResource(std::string resource_source, std::string resource_name, std::string value);
- void LoadResources(xml_node<>* resList, ZipArchive* pZip, std::string resource_source);
+ void LoadResources(xml_node<>* resList, ZipWrap* pZip, std::string resource_source);
public:
FontResource* FindFont(const std::string& name) const;
diff --git a/install.cpp b/install.cpp
index e144d9b..ffeba2e 100644
--- a/install.cpp
+++ b/install.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "install.h"
+
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
@@ -24,48 +26,53 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <algorithm>
#include <chrono>
+#include <condition_variable>
+#include <functional>
#include <limits>
#include <map>
+#include <mutex>
#include <string>
+#include <thread>
#include <vector>
#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parsedouble.h>
#include <android-base/parseint.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <cutils/properties.h>
+#include <vintf/VintfObjectRecovery.h>
+#include <ziparchive/zip_archive.h>
#include "common.h"
#include "error_code.h"
-#include "install.h"
#include "minui/minui.h"
-#include "minzip/SysUtil.h"
-#include "minzip/Zip.h"
-#include "mtdutils/mounts.h"
-#include "mtdutils/mtdutils.h"
+#include "otautil/SysUtil.h"
+#include "otautil/ThermalUtil.h"
#include "roots.h"
#include "ui.h"
#include "verifier.h"
-extern RecoveryUI* ui;
+using namespace std::chrono_literals;
-#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary"
-static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt";
-static constexpr const char* AB_OTA_PAYLOAD = "payload.bin";
#define PUBLIC_KEYS_FILE "/res/keys"
static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata";
static constexpr const char* UNCRYPT_STATUS = "/cache/recovery/uncrypt_status";
// Default allocation of progress bar segments to operations
-static const int VERIFICATION_PROGRESS_TIME = 60;
-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;
+static constexpr int VERIFICATION_PROGRESS_TIME = 60;
+static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25;
+static constexpr float DEFAULT_FILES_PROGRESS_FRACTION = 0.4;
+static constexpr float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1;
+
+static std::condition_variable finish_log_temperature;
// This function parses and returns the build.version.incremental
-static int parse_build_number(std::string str) {
- size_t pos = str.find("=");
+static int parse_build_number(const 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;
@@ -74,27 +81,33 @@
}
}
- LOGE("Failed to parse build number in %s.\n", str.c_str());
+ LOG(ERROR) << "Failed to parse build number in " << str;
return -1;
}
-bool read_metadata_from_package(ZipArchive* zip, std::string* meta_data) {
- const ZipEntry* meta_entry = mzFindZipEntry(zip, METADATA_PATH);
- if (meta_entry == nullptr) {
- LOGE("Failed to find %s in update package.\n", METADATA_PATH);
+bool read_metadata_from_package(ZipArchiveHandle zip, std::string* meta_data) {
+ ZipString metadata_path(METADATA_PATH);
+ ZipEntry meta_entry;
+ if (meta_data == nullptr) {
+ LOG(ERROR) << "string* meta_data can't be nullptr";
+ return false;
+ }
+ if (FindEntry(zip, metadata_path, &meta_entry) != 0) {
+ LOG(ERROR) << "Failed to find " << METADATA_PATH << " in update package";
return false;
}
- meta_data->resize(meta_entry->uncompLen, '\0');
- if (!mzReadZipEntry(zip, meta_entry, &(*meta_data)[0], meta_entry->uncompLen)) {
- LOGE("Failed to read metadata in update package.\n");
+ meta_data->resize(meta_entry.uncompressed_length, '\0');
+ if (ExtractToMemory(zip, &meta_entry, reinterpret_cast<uint8_t*>(&(*meta_data)[0]),
+ meta_entry.uncompressed_length) != 0) {
+ LOG(ERROR) << "Failed to read metadata in update package";
return false;
}
return true;
}
// 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<std::string>& log_buffer) {
+static void read_source_target_build(ZipArchiveHandle zip, std::vector<std::string>& log_buffer) {
std::string meta_data;
if (!read_metadata_from_package(zip, &meta_data)) {
return;
@@ -121,348 +134,438 @@
}
}
-// Extract the update binary from the open zip archive |zip| located at |path|
-// and store into |cmd| the command line that should be called. The |status_fd|
-// is the file descriptor the child process should use to report back the
-// progress of the update.
-static int
-update_binary_command(const char* path, ZipArchive* zip, int retry_count,
- int status_fd, std::vector<std::string>* cmd);
+// Extract the update binary from the open zip archive |zip| located at |path| and store into |cmd|
+// the command line that should be called. The |status_fd| is the file descriptor the child process
+// should use to report back the progress of the update.
+int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count,
+ int status_fd, std::vector<std::string>* cmd);
#ifdef AB_OTA_UPDATER
// Parses the metadata of the OTA package in |zip| and checks whether we are
// allowed to accept this A/B package. Downgrading is not allowed unless
// explicitly enabled in the package and only for incremental packages.
-static int check_newer_ab_build(ZipArchive* zip)
-{
- std::string metadata_str;
- if (!read_metadata_from_package(zip, &metadata_str)) {
- return INSTALL_CORRUPT;
+static int check_newer_ab_build(ZipArchiveHandle zip) {
+ std::string metadata_str;
+ if (!read_metadata_from_package(zip, &metadata_str)) {
+ return INSTALL_CORRUPT;
+ }
+ std::map<std::string, std::string> metadata;
+ for (const std::string& line : android::base::Split(metadata_str, "\n")) {
+ size_t eq = line.find('=');
+ if (eq != std::string::npos) {
+ metadata[line.substr(0, eq)] = line.substr(eq + 1);
}
- std::map<std::string, std::string> metadata;
- for (const std::string& line : android::base::Split(metadata_str, "\n")) {
- size_t eq = line.find('=');
- if (eq != std::string::npos) {
- metadata[line.substr(0, eq)] = line.substr(eq + 1);
- }
- }
- char value[PROPERTY_VALUE_MAX];
+ }
- property_get("ro.product.device", value, "");
- const std::string& pkg_device = metadata["pre-device"];
- if (pkg_device != value || pkg_device.empty()) {
- LOGE("Package is for product %s but expected %s\n",
- pkg_device.c_str(), value);
- return INSTALL_ERROR;
- }
+ std::string value = android::base::GetProperty("ro.product.device", "");
+ const std::string& pkg_device = metadata["pre-device"];
+ if (pkg_device != value || pkg_device.empty()) {
+ LOG(ERROR) << "Package is for product " << pkg_device << " but expected " << value;
+ return INSTALL_ERROR;
+ }
- // We allow the package to not have any serialno, but if it has a non-empty
- // value it should match.
- property_get("ro.serialno", value, "");
- const std::string& pkg_serial_no = metadata["serialno"];
- if (!pkg_serial_no.empty() && pkg_serial_no != value) {
- LOGE("Package is for serial %s\n", pkg_serial_no.c_str());
- return INSTALL_ERROR;
- }
+ // We allow the package to not have any serialno, but if it has a non-empty
+ // value it should match.
+ value = android::base::GetProperty("ro.serialno", "");
+ const std::string& pkg_serial_no = metadata["serialno"];
+ if (!pkg_serial_no.empty() && pkg_serial_no != value) {
+ LOG(ERROR) << "Package is for serial " << pkg_serial_no;
+ return INSTALL_ERROR;
+ }
- if (metadata["ota-type"] != "AB") {
- LOGE("Package is not A/B\n");
- return INSTALL_ERROR;
- }
+ if (metadata["ota-type"] != "AB") {
+ LOG(ERROR) << "Package is not A/B";
+ return INSTALL_ERROR;
+ }
- // Incremental updates should match the current build.
- property_get("ro.build.version.incremental", value, "");
- const std::string& pkg_pre_build = metadata["pre-build-incremental"];
- if (!pkg_pre_build.empty() && pkg_pre_build != value) {
- LOGE("Package is for source build %s but expected %s\n",
- pkg_pre_build.c_str(), value);
- return INSTALL_ERROR;
- }
- property_get("ro.build.fingerprint", value, "");
- const std::string& pkg_pre_build_fingerprint = metadata["pre-build"];
- if (!pkg_pre_build_fingerprint.empty() &&
- pkg_pre_build_fingerprint != value) {
- LOGE("Package is for source build %s but expected %s\n",
- pkg_pre_build_fingerprint.c_str(), value);
- return INSTALL_ERROR;
- }
+ // Incremental updates should match the current build.
+ value = android::base::GetProperty("ro.build.version.incremental", "");
+ const std::string& pkg_pre_build = metadata["pre-build-incremental"];
+ if (!pkg_pre_build.empty() && pkg_pre_build != value) {
+ LOG(ERROR) << "Package is for source build " << pkg_pre_build << " but expected " << value;
+ return INSTALL_ERROR;
+ }
- // Check for downgrade version.
- int64_t build_timestampt = property_get_int64(
- "ro.build.date.utc", std::numeric_limits<int64_t>::max());
- int64_t pkg_post_timespampt = 0;
- // We allow to full update to the same version we are running, in case there
- // is a problem with the current copy of that version.
- if (metadata["post-timestamp"].empty() ||
- !android::base::ParseInt(metadata["post-timestamp"].c_str(),
- &pkg_post_timespampt) ||
- pkg_post_timespampt < build_timestampt) {
- if (metadata["ota-downgrade"] != "yes") {
- LOGE("Update package is older than the current build, expected a "
- "build newer than timestamp %" PRIu64 " but package has "
- "timestamp %" PRIu64 " and downgrade not allowed.\n",
- build_timestampt, pkg_post_timespampt);
- return INSTALL_ERROR;
- }
- if (pkg_pre_build_fingerprint.empty()) {
- LOGE("Downgrade package must have a pre-build version set, not "
- "allowed.\n");
- return INSTALL_ERROR;
- }
- }
+ value = android::base::GetProperty("ro.build.fingerprint", "");
+ const std::string& pkg_pre_build_fingerprint = metadata["pre-build"];
+ if (!pkg_pre_build_fingerprint.empty() && pkg_pre_build_fingerprint != value) {
+ LOG(ERROR) << "Package is for source build " << pkg_pre_build_fingerprint << " but expected "
+ << value;
+ return INSTALL_ERROR;
+ }
- return 0;
+ // Check for downgrade version.
+ int64_t build_timestamp =
+ android::base::GetIntProperty("ro.build.date.utc", std::numeric_limits<int64_t>::max());
+ int64_t pkg_post_timestamp = 0;
+ // We allow to full update to the same version we are running, in case there
+ // is a problem with the current copy of that version.
+ if (metadata["post-timestamp"].empty() ||
+ !android::base::ParseInt(metadata["post-timestamp"].c_str(), &pkg_post_timestamp) ||
+ pkg_post_timestamp < build_timestamp) {
+ if (metadata["ota-downgrade"] != "yes") {
+ LOG(ERROR) << "Update package is older than the current build, expected a build "
+ "newer than timestamp "
+ << build_timestamp << " but package has timestamp " << pkg_post_timestamp
+ << " and downgrade not allowed.";
+ return INSTALL_ERROR;
+ }
+ if (pkg_pre_build_fingerprint.empty()) {
+ LOG(ERROR) << "Downgrade package must have a pre-build version set, not allowed.";
+ return INSTALL_ERROR;
+ }
+ }
+
+ return 0;
}
-static int
-update_binary_command(const char* path, ZipArchive* zip, int retry_count,
- int status_fd, std::vector<std::string>* cmd)
-{
- int ret = check_newer_ab_build(zip);
- if (ret) {
- return ret;
- }
+int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count,
+ int status_fd, std::vector<std::string>* cmd) {
+ CHECK(cmd != nullptr);
+ int ret = check_newer_ab_build(zip);
+ if (ret != 0) {
+ return ret;
+ }
- // For A/B updates we extract the payload properties to a buffer and obtain
- // the RAW payload offset in the zip file.
- const ZipEntry* properties_entry =
- mzFindZipEntry(zip, AB_OTA_PAYLOAD_PROPERTIES);
- if (!properties_entry) {
- LOGE("Can't find %s\n", AB_OTA_PAYLOAD_PROPERTIES);
- return INSTALL_CORRUPT;
- }
- std::vector<unsigned char> payload_properties(
- mzGetZipEntryUncompLen(properties_entry));
- if (!mzExtractZipEntryToBuffer(zip, properties_entry,
- payload_properties.data())) {
- LOGE("Can't extract %s\n", AB_OTA_PAYLOAD_PROPERTIES);
- return INSTALL_CORRUPT;
- }
+ // For A/B updates we extract the payload properties to a buffer and obtain the RAW payload offset
+ // in the zip file.
+ static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt";
+ ZipString property_name(AB_OTA_PAYLOAD_PROPERTIES);
+ ZipEntry properties_entry;
+ if (FindEntry(zip, property_name, &properties_entry) != 0) {
+ LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD_PROPERTIES;
+ return INSTALL_CORRUPT;
+ }
+ uint32_t properties_entry_length = properties_entry.uncompressed_length;
+ std::vector<uint8_t> payload_properties(properties_entry_length);
+ int32_t err =
+ ExtractToMemory(zip, &properties_entry, payload_properties.data(), properties_entry_length);
+ if (err != 0) {
+ LOG(ERROR) << "Failed to extract " << AB_OTA_PAYLOAD_PROPERTIES << ": " << ErrorCodeString(err);
+ return INSTALL_CORRUPT;
+ }
- const ZipEntry* payload_entry = mzFindZipEntry(zip, AB_OTA_PAYLOAD);
- if (!payload_entry) {
- LOGE("Can't find %s\n", AB_OTA_PAYLOAD);
- return INSTALL_CORRUPT;
- }
- long payload_offset = mzGetZipEntryOffset(payload_entry);
- *cmd = {
- "/sbin/update_engine_sideload",
- android::base::StringPrintf("--payload=file://%s", path),
- android::base::StringPrintf("--offset=%ld", payload_offset),
- "--headers=" + std::string(payload_properties.begin(),
- payload_properties.end()),
- android::base::StringPrintf("--status_fd=%d", status_fd),
- };
- return 0;
+ static constexpr const char* AB_OTA_PAYLOAD = "payload.bin";
+ ZipString payload_name(AB_OTA_PAYLOAD);
+ ZipEntry payload_entry;
+ if (FindEntry(zip, payload_name, &payload_entry) != 0) {
+ LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD;
+ return INSTALL_CORRUPT;
+ }
+ long payload_offset = payload_entry.offset;
+ *cmd = {
+ "/sbin/update_engine_sideload",
+ "--payload=file://" + path,
+ android::base::StringPrintf("--offset=%ld", payload_offset),
+ "--headers=" + std::string(payload_properties.begin(), payload_properties.end()),
+ android::base::StringPrintf("--status_fd=%d", status_fd),
+ };
+ return 0;
}
#else // !AB_OTA_UPDATER
-static int
-update_binary_command(const char* path, ZipArchive* zip, int retry_count,
- int status_fd, std::vector<std::string>* cmd)
-{
- // On traditional updates we extract the update binary from the package.
- const ZipEntry* binary_entry =
- mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);
- if (binary_entry == NULL) {
- return INSTALL_CORRUPT;
- }
+int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count,
+ int status_fd, std::vector<std::string>* cmd) {
+ CHECK(cmd != nullptr);
- const char* binary = "/tmp/update_binary";
- unlink(binary);
- int fd = creat(binary, 0755);
- if (fd < 0) {
- LOGE("Can't make %s\n", binary);
- return INSTALL_ERROR;
- }
- bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);
- close(fd);
+ // On traditional updates we extract the update binary from the package.
+ static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary";
+ ZipString binary_name(UPDATE_BINARY_NAME);
+ ZipEntry binary_entry;
+ if (FindEntry(zip, binary_name, &binary_entry) != 0) {
+ LOG(ERROR) << "Failed to find update binary " << UPDATE_BINARY_NAME;
+ return INSTALL_CORRUPT;
+ }
- if (!ok) {
- LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);
- return INSTALL_ERROR;
- }
+ const char* binary = "/tmp/update_binary";
+ unlink(binary);
+ int fd = creat(binary, 0755);
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to create " << binary;
+ return INSTALL_ERROR;
+ }
- *cmd = {
- binary,
- EXPAND(RECOVERY_API_VERSION), // defined in Android.mk
- std::to_string(status_fd),
- path,
- };
- if (retry_count > 0)
- cmd->push_back("retry");
- return 0;
+ int32_t error = ExtractEntryToFile(zip, &binary_entry, fd);
+ close(fd);
+ if (error != 0) {
+ LOG(ERROR) << "Failed to extract " << UPDATE_BINARY_NAME << ": " << ErrorCodeString(error);
+ return INSTALL_ERROR;
+ }
+
+ *cmd = {
+ binary,
+ EXPAND(RECOVERY_API_VERSION), // defined in Android.mk
+ std::to_string(status_fd),
+ path,
+ };
+ if (retry_count > 0) {
+ cmd->push_back("retry");
+ }
+ return 0;
}
#endif // !AB_OTA_UPDATER
+static void log_max_temperature(int* max_temperature) {
+ CHECK(max_temperature != nullptr);
+ std::mutex mtx;
+ std::unique_lock<std::mutex> lck(mtx);
+ while (finish_log_temperature.wait_for(lck, 20s) == std::cv_status::timeout) {
+ *max_temperature = std::max(*max_temperature, GetMaxValueFromThermalZone());
+ }
+}
+
// 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,
- std::vector<std::string>& log_buffer, int retry_count)
-{
- read_source_target_build(zip, log_buffer);
+static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_cache,
+ std::vector<std::string>& log_buffer, int retry_count,
+ int* max_temperature) {
+ read_source_target_build(zip, log_buffer);
- int pipefd[2];
- pipe(pipefd);
+ int pipefd[2];
+ pipe(pipefd);
- std::vector<std::string> args;
- int ret = update_binary_command(path, zip, retry_count, pipefd[1], &args);
- mzCloseZipArchive(zip);
- if (ret) {
- close(pipefd[0]);
- close(pipefd[1]);
- return ret;
- }
-
- // When executing the update binary contained in the package, the
- // arguments passed are:
- //
- // - the version number for this interface
- //
- // - an fd to which the program can write in order to update the
- // progress bar. The program can write single-line commands:
- //
- // progress <frac> <secs>
- // fill up the next <frac> part of of the progress bar
- // over <secs> seconds. If <secs> is zero, use
- // set_progress commands to manually control the
- // progress of this segment of the bar.
- //
- // set_progress <frac>
- // <frac> should be between 0.0 and 1.0; sets the
- // progress bar within the segment defined by the most
- // recent progress command.
- //
- // firmware <"hboot"|"radio"> <filename>
- // arrange to install the contents of <filename> in the
- // given partition on reboot.
- //
- // (API v2: <filename> may start with "PACKAGE:" to
- // indicate taking a file from the OTA package.)
- //
- // (API v3: this command no longer exists.)
- //
- // ui_print <string>
- // display <string> on the screen.
- //
- // wipe_cache
- // a wipe of cache will be performed following a successful
- // installation.
- //
- // clear_display
- // turn off the text display.
- //
- // enable_reboot
- // packages can explicitly request that they want the user
- // to be able to reboot during installation (useful for
- // debugging packages that don't exit).
- //
- // - the name of the package zip file.
- //
- // - an optional argument "retry" if this update is a retry of a failed
- // update attempt.
- //
-
- // Convert the vector to a NULL-terminated char* array suitable for execv.
- const char* chr_args[args.size() + 1];
- chr_args[args.size()] = NULL;
- for (size_t i = 0; i < args.size(); i++) {
- chr_args[i] = args[i].c_str();
- }
-
- pid_t pid = fork();
-
- if (pid == -1) {
- close(pipefd[0]);
- close(pipefd[1]);
- LOGE("Failed to fork update binary: %s\n", strerror(errno));
- return INSTALL_ERROR;
- }
-
- if (pid == 0) {
- umask(022);
- close(pipefd[0]);
- execv(chr_args[0], const_cast<char**>(chr_args));
- fprintf(stdout, "E:Can't run %s (%s)\n", chr_args[0], strerror(errno));
- _exit(-1);
- }
+ std::vector<std::string> args;
+ int ret = update_binary_command(path, zip, retry_count, pipefd[1], &args);
+ if (ret) {
+ close(pipefd[0]);
close(pipefd[1]);
+ return ret;
+ }
- *wipe_cache = false;
- bool retry_update = false;
+ // When executing the update binary contained in the package, the
+ // arguments passed are:
+ //
+ // - the version number for this interface
+ //
+ // - an FD to which the program can write in order to update the
+ // progress bar. The program can write single-line commands:
+ //
+ // progress <frac> <secs>
+ // fill up the next <frac> part of of the progress bar
+ // over <secs> seconds. If <secs> is zero, use
+ // set_progress commands to manually control the
+ // progress of this segment of the bar.
+ //
+ // set_progress <frac>
+ // <frac> should be between 0.0 and 1.0; sets the
+ // progress bar within the segment defined by the most
+ // recent progress command.
+ //
+ // ui_print <string>
+ // display <string> on the screen.
+ //
+ // wipe_cache
+ // a wipe of cache will be performed following a successful
+ // installation.
+ //
+ // clear_display
+ // turn off the text display.
+ //
+ // enable_reboot
+ // packages can explicitly request that they want the user
+ // to be able to reboot during installation (useful for
+ // debugging packages that don't exit).
+ //
+ // retry_update
+ // updater encounters some issue during the update. It requests
+ // a reboot to retry the same package automatically.
+ //
+ // log <string>
+ // updater requests logging the string (e.g. cause of the
+ // failure).
+ //
+ // - the name of the package zip file.
+ //
+ // - an optional argument "retry" if this update is a retry of a failed
+ // update attempt.
+ //
- char buffer[1024];
- FILE* from_child = fdopen(pipefd[0], "r");
- while (fgets(buffer, sizeof(buffer), from_child) != NULL) {
- char* command = strtok(buffer, " \n");
- if (command == NULL) {
- continue;
- } else if (strcmp(command, "progress") == 0) {
- char* fraction_s = strtok(NULL, " \n");
- char* seconds_s = strtok(NULL, " \n");
+ // Convert the vector to a NULL-terminated char* array suitable for execv.
+ const char* chr_args[args.size() + 1];
+ chr_args[args.size()] = nullptr;
+ for (size_t i = 0; i < args.size(); i++) {
+ chr_args[i] = args[i].c_str();
+ }
- float fraction = strtof(fraction_s, NULL);
- int seconds = strtol(seconds_s, NULL, 10);
+ pid_t pid = fork();
- ui->ShowProgress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), seconds);
- } else if (strcmp(command, "set_progress") == 0) {
- char* fraction_s = strtok(NULL, " \n");
- float fraction = strtof(fraction_s, NULL);
- ui->SetProgress(fraction);
- } else if (strcmp(command, "ui_print") == 0) {
- char* str = strtok(NULL, "\n");
- if (str) {
- ui->PrintOnScreenOnly("%s", str);
- } else {
- ui->PrintOnScreenOnly("\n");
- }
- fflush(stdout);
- } else if (strcmp(command, "wipe_cache") == 0) {
- *wipe_cache = true;
- } else if (strcmp(command, "clear_display") == 0) {
- ui->SetBackground(RecoveryUI::NONE);
- } else if (strcmp(command, "enable_reboot") == 0) {
- // packages can explicitly request that they want the user
- // 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);
- }
+ if (pid == -1) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ PLOG(ERROR) << "Failed to fork update binary";
+ return INSTALL_ERROR;
+ }
+
+ if (pid == 0) {
+ umask(022);
+ close(pipefd[0]);
+ execv(chr_args[0], const_cast<char**>(chr_args));
+ // Bug: 34769056
+ // We shouldn't use LOG/PLOG in the forked process, since they may cause
+ // the child process to hang. This deadlock results from an improperly
+ // copied mutex in the ui functions.
+ fprintf(stdout, "E:Can't run %s (%s)\n", chr_args[0], strerror(errno));
+ _exit(EXIT_FAILURE);
+ }
+ close(pipefd[1]);
+
+ std::thread temperature_logger(log_max_temperature, max_temperature);
+
+ *wipe_cache = false;
+ bool retry_update = false;
+
+ char buffer[1024];
+ FILE* from_child = fdopen(pipefd[0], "r");
+ while (fgets(buffer, sizeof(buffer), from_child) != nullptr) {
+ std::string line(buffer);
+ size_t space = line.find_first_of(" \n");
+ std::string command(line.substr(0, space));
+ if (command.empty()) continue;
+
+ // Get rid of the leading and trailing space and/or newline.
+ std::string args = space == std::string::npos ? "" : android::base::Trim(line.substr(space));
+
+ if (command == "progress") {
+ std::vector<std::string> tokens = android::base::Split(args, " ");
+ double fraction;
+ int seconds;
+ if (tokens.size() == 2 && android::base::ParseDouble(tokens[0].c_str(), &fraction) &&
+ android::base::ParseInt(tokens[1], &seconds)) {
+ ui->ShowProgress(fraction * (1 - VERIFICATION_PROGRESS_FRACTION), seconds);
+ } else {
+ LOG(ERROR) << "invalid \"progress\" parameters: " << line;
+ }
+ } else if (command == "set_progress") {
+ std::vector<std::string> tokens = android::base::Split(args, " ");
+ double fraction;
+ if (tokens.size() == 1 && android::base::ParseDouble(tokens[0].c_str(), &fraction)) {
+ ui->SetProgress(fraction);
+ } else {
+ LOG(ERROR) << "invalid \"set_progress\" parameters: " << line;
+ }
+ } else if (command == "ui_print") {
+ ui->PrintOnScreenOnly("%s\n", args.c_str());
+ fflush(stdout);
+ } else if (command == "wipe_cache") {
+ *wipe_cache = true;
+ } else if (command == "clear_display") {
+ ui->SetBackground(RecoveryUI::NONE);
+ } else if (command == "enable_reboot") {
+ // packages can explicitly request that they want the user
+ // to be able to reboot during installation (useful for
+ // debugging packages that don't exit).
+ ui->SetEnableReboot(true);
+ } else if (command == "retry_update") {
+ retry_update = true;
+ } else if (command == "log") {
+ if (!args.empty()) {
+ // Save the logging request from updater and write to last_install later.
+ log_buffer.push_back(args);
+ } else {
+ LOG(ERROR) << "invalid \"log\" parameters: " << line;
+ }
+ } else {
+ LOG(ERROR) << "unknown command [" << command << "]";
}
- fclose(from_child);
+ }
+ fclose(from_child);
- 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;
- }
+ int status;
+ waitpid(pid, &status, 0);
- return INSTALL_SUCCESS;
+ finish_log_temperature.notify_one();
+ temperature_logger.join();
+
+ if (retry_update) {
+ return INSTALL_RETRY;
+ }
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ LOG(ERROR) << "Error in " << path << " (Status " << WEXITSTATUS(status) << ")";
+ return INSTALL_ERROR;
+ }
+
+ return INSTALL_SUCCESS;
+}
+
+// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the
+// entry doesn't exist. Note that the compatibility info is packed in a zip file inside the OTA
+// package.
+bool verify_package_compatibility(ZipArchiveHandle package_zip) {
+ LOG(INFO) << "Verifying package compatibility...";
+
+ static constexpr const char* COMPATIBILITY_ZIP_ENTRY = "compatibility.zip";
+ ZipString compatibility_entry_name(COMPATIBILITY_ZIP_ENTRY);
+ ZipEntry compatibility_entry;
+ if (FindEntry(package_zip, compatibility_entry_name, &compatibility_entry) != 0) {
+ LOG(INFO) << "Package doesn't contain " << COMPATIBILITY_ZIP_ENTRY << " entry";
+ return true;
+ }
+
+ std::string zip_content(compatibility_entry.uncompressed_length, '\0');
+ int32_t ret;
+ if ((ret = ExtractToMemory(package_zip, &compatibility_entry,
+ reinterpret_cast<uint8_t*>(&zip_content[0]),
+ compatibility_entry.uncompressed_length)) != 0) {
+ LOG(ERROR) << "Failed to read " << COMPATIBILITY_ZIP_ENTRY << ": " << ErrorCodeString(ret);
+ return false;
+ }
+
+ ZipArchiveHandle zip_handle;
+ ret = OpenArchiveFromMemory(static_cast<void*>(const_cast<char*>(zip_content.data())),
+ zip_content.size(), COMPATIBILITY_ZIP_ENTRY, &zip_handle);
+ if (ret != 0) {
+ LOG(ERROR) << "Failed to OpenArchiveFromMemory: " << ErrorCodeString(ret);
+ return false;
+ }
+
+ // Iterate all the entries inside COMPATIBILITY_ZIP_ENTRY and read the contents.
+ void* cookie;
+ ret = StartIteration(zip_handle, &cookie, nullptr, nullptr);
+ if (ret != 0) {
+ LOG(ERROR) << "Failed to start iterating zip entries: " << ErrorCodeString(ret);
+ CloseArchive(zip_handle);
+ return false;
+ }
+ std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration);
+
+ std::vector<std::string> compatibility_info;
+ ZipEntry info_entry;
+ ZipString info_name;
+ while (Next(cookie, &info_entry, &info_name) == 0) {
+ std::string content(info_entry.uncompressed_length, '\0');
+ int32_t ret = ExtractToMemory(zip_handle, &info_entry, reinterpret_cast<uint8_t*>(&content[0]),
+ info_entry.uncompressed_length);
+ if (ret != 0) {
+ LOG(ERROR) << "Failed to read " << info_name.name << ": " << ErrorCodeString(ret);
+ CloseArchive(zip_handle);
+ return false;
+ }
+ compatibility_info.emplace_back(std::move(content));
+ }
+ CloseArchive(zip_handle);
+
+ // VintfObjectRecovery::CheckCompatibility returns zero on success.
+ std::string err;
+ int result = android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err);
+ if (result == 0) {
+ return true;
+ }
+
+ LOG(ERROR) << "Failed to verify package compatibility (result " << result << "): " << err;
+ return false;
}
static int
really_install_package(const char *path, bool* wipe_cache, bool needs_mount,
- std::vector<std::string>& log_buffer, int retry_count)
+ std::vector<std::string>& log_buffer, int retry_count, int* max_temperature)
{
ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
ui->Print("Finding update package...\n");
// Give verification half the progress bar...
ui->SetProgressType(RecoveryUI::DETERMINATE);
ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);
- LOGI("Update location: %s\n", path);
+ LOG(INFO) << "Update location: " << path;
// Map the update package into memory.
ui->Print("Opening update package...\n");
@@ -477,7 +580,7 @@
MemMapping map;
if (sysMapFile(path, &map) != 0) {
- LOGE("failed to map file\n");
+ LOG(ERROR) << "failed to map file";
return INSTALL_CORRUPT;
}
@@ -489,28 +592,37 @@
}
// Try to open the package.
- ZipArchive zip;
- int err = mzOpenZipArchive(map.addr, map.length, &zip);
+ ZipArchiveHandle zip;
+ int err = OpenArchiveFromMemory(map.addr, map.length, path, &zip);
if (err != 0) {
- LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");
+ LOG(ERROR) << "Can't open " << path << " : " << ErrorCodeString(err);
log_buffer.push_back(android::base::StringPrintf("error: %d", kZipOpenFailure));
sysReleaseMap(&map);
+ CloseArchive(zip);
return INSTALL_CORRUPT;
}
+ // Additionally verify the compatibility of the package.
+ if (!verify_package_compatibility(zip)) {
+ log_buffer.push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure));
+ sysReleaseMap(&map);
+ CloseArchive(zip);
+ return INSTALL_CORRUPT;
+ }
+
// 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, log_buffer, retry_count);
+ int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature);
ui->SetEnableReboot(true);
ui->Print("\n");
sysReleaseMap(&map);
-
+ CloseArchive(zip);
return result;
}
@@ -521,30 +633,38 @@
modified_flash = true;
auto start = std::chrono::system_clock::now();
+ int start_temperature = GetMaxValueFromThermalZone();
+ int max_temperature = start_temperature;
+
int result;
std::vector<std::string> log_buffer;
if (setup_install_mounts() != 0) {
- LOGE("failed to set up expected mounts for install; aborting\n");
+ LOG(ERROR) << "failed to set up expected mounts for install; aborting";
result = INSTALL_ERROR;
} else {
- result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count);
+ result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count,
+ &max_temperature);
}
// Measure the time spent to apply OTA update in seconds.
std::chrono::duration<double> duration = std::chrono::system_clock::now() - start;
int time_total = static_cast<int>(duration.count());
- if (ensure_path_mounted(UNCRYPT_STATUS) != 0) {
- LOGW("Can't mount %s\n", UNCRYPT_STATUS);
- } else {
+ bool has_cache = volume_for_path("/cache") != nullptr;
+ // Skip logging the uncrypt_status on devices without /cache.
+ if (has_cache) {
+ if (ensure_path_mounted(UNCRYPT_STATUS) != 0) {
+ LOG(WARNING) << "Can't mount " << UNCRYPT_STATUS;
+ } else {
std::string uncrypt_status;
if (!android::base::ReadFileToString(UNCRYPT_STATUS, &uncrypt_status)) {
- LOGW("failed to read uncrypt status: %s\n", strerror(errno));
+ PLOG(WARNING) << "failed to read uncrypt status";
} else if (!android::base::StartsWith(uncrypt_status, "uncrypt_")) {
- LOGW("corrupted uncrypt_status: %s: %s\n", uncrypt_status.c_str(), strerror(errno));
+ LOG(WARNING) << "corrupted uncrypt_status: " << uncrypt_status;
} else {
- log_buffer.push_back(android::base::Trim(uncrypt_status));
+ log_buffer.push_back(android::base::Trim(uncrypt_status));
}
+ }
}
// The first two lines need to be the package name and install result.
@@ -554,36 +674,50 @@
"time_total: " + std::to_string(time_total),
"retry: " + std::to_string(retry_count),
};
+
+ int end_temperature = GetMaxValueFromThermalZone();
+ max_temperature = std::max(end_temperature, max_temperature);
+ if (start_temperature > 0) {
+ log_buffer.push_back("temperature_start: " + std::to_string(start_temperature));
+ }
+ if (end_temperature > 0) {
+ log_buffer.push_back("temperature_end: " + std::to_string(end_temperature));
+ }
+ if (max_temperature > 0) {
+ log_buffer.push_back("temperature_max: " + std::to_string(max_temperature));
+ }
+
std::string log_content = android::base::Join(log_header, "\n") + "\n" +
- android::base::Join(log_buffer, "\n");
+ android::base::Join(log_buffer, "\n") + "\n";
if (!android::base::WriteStringToFile(log_content, install_file)) {
- LOGE("failed to write %s: %s\n", install_file, strerror(errno));
+ PLOG(ERROR) << "failed to write " << install_file;
}
// Write a copy into last_log.
- LOGI("%s\n", log_content.c_str());
+ LOG(INFO) << log_content;
return result;
}
bool verify_package(const unsigned char* package_data, size_t package_size) {
- std::vector<Certificate> loadedKeys;
- if (!load_keys(PUBLIC_KEYS_FILE, loadedKeys)) {
- LOGE("Failed to load keys\n");
- return false;
- }
- LOGI("%zu key(s) loaded from %s\n", loadedKeys.size(), PUBLIC_KEYS_FILE);
+ std::vector<Certificate> loadedKeys;
+ if (!load_keys(PUBLIC_KEYS_FILE, loadedKeys)) {
+ LOG(ERROR) << "Failed to load keys";
+ return false;
+ }
+ LOG(INFO) << loadedKeys.size() << " key(s) loaded from " << PUBLIC_KEYS_FILE;
- // Verify package.
- ui->Print("Verifying update package...\n");
- auto t0 = std::chrono::system_clock::now();
- int err = verify_file(const_cast<unsigned char*>(package_data), package_size, loadedKeys);
- std::chrono::duration<double> 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");
- LOGE("error: %d\n", kZipVerificationFailure);
- return false;
- }
- return true;
+ // Verify package.
+ ui->Print("Verifying update package...\n");
+ auto t0 = std::chrono::system_clock::now();
+ int err = verify_file(package_data, package_size, loadedKeys,
+ std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
+ std::chrono::duration<double> 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) {
+ LOG(ERROR) << "Signature verification failed";
+ LOG(ERROR) << "error: " << kZipVerificationFailure;
+ return false;
+ }
+ return true;
}
diff --git a/install.h b/install.h
index 14de225..fd68c3a 100644
--- a/install.h
+++ b/install.h
@@ -18,12 +18,8 @@
#define RECOVERY_INSTALL_H_
#include <string>
+#include <ziparchive/zip_archive.h>
-#include "common.h"
-#include "minzip/Zip.h"
-
-enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE, INSTALL_SKIPPED,
- INSTALL_RETRY };
// Install the package specified by root_path. If INSTALL_SUCCESS is
// returned and *wipe_cache is true on exit, caller should wipe the
// cache partition.
@@ -36,6 +32,10 @@
// Read meta data file of the package, write its content in the string pointed by meta_data.
// Return true if succeed, otherwise return false.
-bool read_metadata_from_package(ZipArchive* zip, std::string* meta_data);
+bool read_metadata_from_package(ZipArchiveHandle zip, std::string* meta_data);
+
+// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the
+// entry doesn't exist.
+bool verify_package_compatibility(ZipArchiveHandle package_zip);
#endif // RECOVERY_INSTALL_H_
diff --git a/installcommand.cpp b/installcommand.cpp
index ba64143..c6b1926 100644
--- a/installcommand.cpp
+++ b/installcommand.cpp
@@ -28,8 +28,11 @@
#include "common.h"
#include "installcommand.h"
-#include "minzip/SysUtil.h"
-#include "minzip/Zip.h"
+#include "zipwrap.hpp"
+#ifndef USE_MINZIP
+#include <ziparchive/zip_archive.h>
+#include <vintf/VintfObjectRecovery.h>
+#endif
#ifdef USE_OLD_VERIFIER
#include "verifier24/verifier.h"
#else
@@ -57,15 +60,13 @@
return -1;
}
-bool read_metadata_from_package(ZipArchive* zip, std::string* meta_data) {
- const ZipEntry* meta_entry = mzFindZipEntry(zip, METADATA_PATH);
- if (meta_entry == nullptr) {
- printf("Failed to find %s in update package.\n", METADATA_PATH);
- return false;
- }
+bool read_metadata_from_package(ZipWrap* zip, std::string* meta_data) {
+ long size = zip->GetUncompressedSize(METADATA_PATH);
+ if (size <= 0)
+ return false;
- meta_data->resize(meta_entry->uncompLen, '\0');
- if (!mzReadZipEntry(zip, meta_entry, &(*meta_data)[0], meta_entry->uncompLen)) {
+ meta_data->resize(size, '\0');
+ if (!zip->ExtractToBuffer(METADATA_PATH, reinterpret_cast<uint8_t*>(&(*meta_data)[0]))) {
printf("Failed to read metadata in update package.\n");
return false;
}
@@ -73,7 +74,7 @@
}
// 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<std::string>& log_buffer) {
+static void read_source_target_build(ZipWrap* zip, std::vector<std::string>& log_buffer) {
std::string meta_data;
if (!read_metadata_from_package(zip, &meta_data)) {
return;
@@ -103,7 +104,7 @@
// Parses the metadata of the OTA package in |zip| and checks whether we are
// allowed to accept this A/B package. Downgrading is not allowed unless
// explicitly enabled in the package and only for incremental packages.
-static int check_newer_ab_build(ZipArchive* zip)
+static int check_newer_ab_build(ZipWrap* zip)
{
std::string metadata_str;
if (!read_metadata_from_package(zip, &metadata_str)) {
@@ -185,7 +186,7 @@
}
int
-abupdate_binary_command(const char* path, ZipArchive* zip, int retry_count,
+abupdate_binary_command(const char* path, ZipWrap* zip, int retry_count,
int status_fd, std::vector<std::string>* cmd)
{
int ret = check_newer_ab_build(zip);
@@ -195,26 +196,22 @@
// For A/B updates we extract the payload properties to a buffer and obtain
// the RAW payload offset in the zip file.
- const ZipEntry* properties_entry =
- mzFindZipEntry(zip, AB_OTA_PAYLOAD_PROPERTIES);
- if (!properties_entry) {
+ if (!zip->EntryExists(AB_OTA_PAYLOAD_PROPERTIES)) {
printf("Can't find %s\n", AB_OTA_PAYLOAD_PROPERTIES);
return INSTALL_CORRUPT;
}
std::vector<unsigned char> payload_properties(
- mzGetZipEntryUncompLen(properties_entry));
- if (!mzExtractZipEntryToBuffer(zip, properties_entry,
- payload_properties.data())) {
+ zip->GetUncompressedSize(AB_OTA_PAYLOAD_PROPERTIES));
+ if (!zip->ExtractToBuffer(AB_OTA_PAYLOAD_PROPERTIES, payload_properties.data())) {
printf("Can't extract %s\n", AB_OTA_PAYLOAD_PROPERTIES);
return INSTALL_CORRUPT;
}
- const ZipEntry* payload_entry = mzFindZipEntry(zip, AB_OTA_PAYLOAD);
- if (!payload_entry) {
+ if (!zip->EntryExists(AB_OTA_PAYLOAD)) {
printf("Can't find %s\n", AB_OTA_PAYLOAD);
return INSTALL_CORRUPT;
}
- long payload_offset = mzGetZipEntryOffset(payload_entry);
+ long payload_offset = zip->GetEntryOffset(AB_OTA_PAYLOAD);
*cmd = {
"/sbin/update_engine_sideload",
android::base::StringPrintf("--payload=file://%s", path),
@@ -229,8 +226,8 @@
#else
int
-abupdate_binary_command(const char* path, ZipArchive* zip, int retry_count,
- int status_fd, std::vector<std::string>* cmd)
+abupdate_binary_command(__unused const char* path, __unused ZipWrap* zip, __unused int retry_count,
+ __unused int status_fd, __unused std::vector<std::string>* cmd)
{
printf("No support for AB OTA zips included\n");
return INSTALL_CORRUPT;
@@ -239,7 +236,7 @@
#endif
int
-update_binary_command(const char* path, ZipArchive* zip, int retry_count,
+update_binary_command(const char* path, int retry_count,
int status_fd, std::vector<std::string>* cmd)
{
char charfd[16];
@@ -258,3 +255,80 @@
cmd->push_back("retry");
return 0;
}
+
+#ifdef USE_MINZIP
+bool verify_package_compatibility(ZipWrap *package_zip) {
+ if (package_zip->EntryExists("compatibility.zip"))
+ printf("Cannot verify treble package compatibility, must build TWRP in Oreo tree or higher.\n");
+ return true;
+}
+#else
+// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the
+// entry doesn't exist. Note that the compatibility info is packed in a zip file inside the OTA
+// package.
+bool verify_package_compatibility(ZipWrap *zw) {
+ ZipArchiveHandle package_zip = zw->GetZipArchiveHandle();
+ printf("Verifying package compatibility...\n");
+
+ static constexpr const char* COMPATIBILITY_ZIP_ENTRY = "compatibility.zip";
+ ZipString compatibility_entry_name(COMPATIBILITY_ZIP_ENTRY);
+ ZipEntry compatibility_entry;
+ if (FindEntry(package_zip, compatibility_entry_name, &compatibility_entry) != 0) {
+ printf("Package doesn't contain %s entry\n", COMPATIBILITY_ZIP_ENTRY);
+ return true;
+ }
+
+ std::string zip_content(compatibility_entry.uncompressed_length, '\0');
+ int32_t ret;
+ if ((ret = ExtractToMemory(package_zip, &compatibility_entry,
+ reinterpret_cast<uint8_t*>(&zip_content[0]),
+ compatibility_entry.uncompressed_length)) != 0) {
+ printf("Failed to read %s: %s\n", COMPATIBILITY_ZIP_ENTRY, ErrorCodeString(ret));
+ return false;
+ }
+
+ ZipArchiveHandle zip_handle;
+ ret = OpenArchiveFromMemory(static_cast<void*>(const_cast<char*>(zip_content.data())),
+ zip_content.size(), COMPATIBILITY_ZIP_ENTRY, &zip_handle);
+ if (ret != 0) {
+ printf("Failed to OpenArchiveFromMemory: %s\n", ErrorCodeString(ret));
+ return false;
+ }
+
+ // Iterate all the entries inside COMPATIBILITY_ZIP_ENTRY and read the contents.
+ void* cookie;
+ ret = StartIteration(zip_handle, &cookie, nullptr, nullptr);
+ if (ret != 0) {
+ printf("Failed to start iterating zip entries: %s\n", ErrorCodeString(ret));
+ CloseArchive(zip_handle);
+ return false;
+ }
+ std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration);
+
+ std::vector<std::string> compatibility_info;
+ ZipEntry info_entry;
+ ZipString info_name;
+ while (Next(cookie, &info_entry, &info_name) == 0) {
+ std::string content(info_entry.uncompressed_length, '\0');
+ int32_t ret = ExtractToMemory(zip_handle, &info_entry, reinterpret_cast<uint8_t*>(&content[0]),
+ info_entry.uncompressed_length);
+ if (ret != 0) {
+ printf("Failed to read %s: %s\n", info_name.name, ErrorCodeString(ret));
+ CloseArchive(zip_handle);
+ return false;
+ }
+ compatibility_info.emplace_back(std::move(content));
+ }
+ CloseArchive(zip_handle);
+
+ // VintfObjectRecovery::CheckCompatibility returns zero on success.
+ std::string err;
+ int result = android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err);
+ if (result == 0) {
+ return true;
+ }
+
+ printf("Failed to verify package compatibility (result %i): %s\n", result, err.c_str());
+ return false;
+}
+#endif
diff --git a/installcommand.h b/installcommand.h
index 505640e..16c034f 100644
--- a/installcommand.h
+++ b/installcommand.h
@@ -21,15 +21,17 @@
#include <string>
-#include "minzip/Zip.h"
+#include "zipwrap.hpp"
-bool read_metadata_from_package(ZipArchive* zip, std::string* meta_data);
+bool read_metadata_from_package(ZipWrap* zip, std::string* meta_data);
int
-abupdate_binary_command(const char* path, ZipArchive* zip, int retry_count,
+abupdate_binary_command(const char* path, ZipWrap* zip, int retry_count,
int status_fd, std::vector<std::string>* cmd);
int
-update_binary_command(const char* path, ZipArchive* zip, int retry_count,
+update_binary_command(const char* path, int retry_count,
int status_fd, std::vector<std::string>* cmd);
+bool verify_package_compatibility(ZipWrap *package_zip);
+
#endif // RECOVERY_INSTALL_COMMAND_H_
diff --git a/minadbd/Android.mk b/minadbd/Android.mk
index 24d1635..fb90f61 100644
--- a/minadbd/Android.mk
+++ b/minadbd/Android.mk
@@ -12,10 +12,10 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
- adb_main.cpp \
fuse_adb_provider.cpp \
- services.cpp \
- ../fuse_sideload.cpp
+ ../fuse_sideload.cpp \
+ minadbd.cpp \
+ minadbd_services.cpp \
LOCAL_CLANG := true
LOCAL_MODULE := libminadbd
diff --git a/minadbd/README.txt b/minadbd/README.md
similarity index 79%
rename from minadbd/README.txt
rename to minadbd/README.md
index e69dc87..5a0a067 100644
--- a/minadbd/README.txt
+++ b/minadbd/README.md
@@ -3,6 +3,6 @@
- all services removed
- all host mode support removed
- - sideload_service() added; this is the only service supported. It
+ - `sideload_service()` added; this is the only service supported. It
receives a single blob of data, writes it to a fixed filename, and
makes the process exit.
diff --git a/minadbd/fuse_adb_provider.cpp b/minadbd/fuse_adb_provider.cpp
index d71807d..0f4c256 100644
--- a/minadbd/fuse_adb_provider.cpp
+++ b/minadbd/fuse_adb_provider.cpp
@@ -19,8 +19,6 @@
#include <string.h>
#include <errno.h>
-#include "sysdeps.h"
-
#include "adb.h"
#include "adb_io.h"
#include "fuse_adb_provider.h"
diff --git a/minadbd/adb_main.cpp b/minadbd/minadbd.cpp
similarity index 91%
rename from minadbd/adb_main.cpp
rename to minadbd/minadbd.cpp
index 1db0d5f..d9da197 100644
--- a/minadbd/adb_main.cpp
+++ b/minadbd/minadbd.cpp
@@ -14,18 +14,18 @@
* limitations under the License.
*/
+#include "minadbd.h"
+
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
-#include "sysdeps.h"
-
#include "adb.h"
#include "adb_auth.h"
#include "transport.h"
-int adb_server_main(int is_daemon, int server_port, int /* reply_fd */) {
+int minadbd_main() {
adb_device_banner = "sideload";
signal(SIGPIPE, SIG_IGN);
diff --git a/updater/install.h b/minadbd/minadbd.h
similarity index 65%
copy from updater/install.h
copy to minadbd/minadbd.h
index 70e3434..3570a5d 100644
--- a/updater/install.h
+++ b/minadbd/minadbd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Android Open Source Project
+ * 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.
@@ -14,14 +14,9 @@
* limitations under the License.
*/
-#ifndef _UPDATER_INSTALL_H_
-#define _UPDATER_INSTALL_H_
+#ifndef MINADBD_H__
+#define MINADBD_H__
-void RegisterInstallFunctions();
-
-// uiPrintf function prints msg to screen as well as logs
-void uiPrintf(State* state, const char* format, ...);
-
-static int make_parents(char* name);
+int minadbd_main();
#endif
diff --git a/minadbd/services.cpp b/minadbd/minadbd_services.cpp
similarity index 96%
rename from minadbd/services.cpp
rename to minadbd/minadbd_services.cpp
index 753a14e..e558f97 100644
--- a/minadbd/services.cpp
+++ b/minadbd/minadbd_services.cpp
@@ -21,11 +21,10 @@
#include <string.h>
#include <unistd.h>
-#include "sysdeps.h"
-
#include "adb.h"
#include "fdevent.h"
#include "fuse_adb_provider.h"
+#include "sysdeps.h"
typedef struct stinfo stinfo;
@@ -62,12 +61,12 @@
static int create_service_thread(void (*func)(int, void *), void *cookie) {
int s[2];
- if(adb_socketpair(s)) {
+ if (adb_socketpair(s)) {
printf("cannot create service socket pair\n");
return -1;
}
- stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
+ stinfo* sti = static_cast<stinfo*>(malloc(sizeof(stinfo)));
if(sti == 0) fatal("cannot allocate stinfo");
sti->func = func;
sti->cookie = cookie;
diff --git a/minui/Android.mk b/minui/Android.mk
index 249feeb..e359887 100644
--- a/minui/Android.mk
+++ b/minui/Android.mk
@@ -1,3 +1,17 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
@@ -6,6 +20,7 @@
graphics.cpp \
graphics_drm.cpp \
graphics_fbdev.cpp \
+ graphics_overlay.cpp \
resources.cpp
LOCAL_C_INCLUDES := external/libcxx/include external/libpng
@@ -35,8 +50,21 @@
LOCAL_CFLAGS += -DNEW_ION_HEAP
endif
-LOCAL_WHOLE_STATIC_LIBRARIES += libdrm
LOCAL_STATIC_LIBRARIES += libpng
+LOCAL_WHOLE_STATIC_LIBRARIES += \
+ libdrm
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+ LOCAL_CFLAGS += -DHAS_LIBSYNC
+ LOCAL_WHOLE_STATIC_LIBRARIES += libsync_recovery
+endif
+
+LOCAL_STATIC_LIBRARIES := \
+ libpng \
+ libbase
+
+LOCAL_CFLAGS := -Werror -std=c++14
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_MODULE := libminui
@@ -90,7 +118,14 @@
LOCAL_CLANG := true
LOCAL_MODULE := libminui
LOCAL_WHOLE_STATIC_LIBRARIES += libminui
-LOCAL_SHARED_LIBRARIES := libpng
+LOCAL_SHARED_LIBRARIES := \
+ libpng \
+ libbase
+
+LOCAL_CFLAGS := -Werror
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
@@ -98,7 +133,15 @@
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
LOCAL_SRC_FILES := main.cpp
-LOCAL_STATIC_LIBRARIES := libbinder libminui libpng libz libutils libstdc++ libcutils liblog libm libc
+LOCAL_SHARED_LIBRARIES := libbinder libminui libpng libz libutils libstdc++ libcutils liblog libm libc
LOCAL_C_INCLUDES := external/libcxx/include external/libpng
-LOCAL_FORCE_STATIC_EXECUTABLE := true
+ifneq ($(TARGET_ARCH), arm64)
+ ifneq ($(TARGET_ARCH), x86_64)
+ LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker
+ else
+ LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64
+ endif
+else
+ LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64
+endif
include $(BUILD_EXECUTABLE)
diff --git a/minui/events.cpp b/minui/events.cpp
index 3b2262a..470a17a 100644
--- a/minui/events.cpp
+++ b/minui/events.cpp
@@ -15,17 +15,19 @@
*/
#include <dirent.h>
-#include <errno.h>
#include <fcntl.h>
+#include <linux/input.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
+#include <sys/ioctl.h>
#include <unistd.h>
+#include <errno.h>
-#include <linux/input.h>
+#include <functional>
-#include "minui.h"
+#include "minui/minui.h"
#define MAX_DEVICES 16
#define MAX_MISC_FDS 16
@@ -34,9 +36,11 @@
#define BITS_TO_LONGS(x) (((x) + BITS_PER_LONG - 1) / BITS_PER_LONG)
struct fd_info {
- int fd;
- ev_callback cb;
- void* data;
+ int fd;
+ ev_callback cb;
+#ifdef TW_USE_MINUI_WITH_DATA
+ void* data;
+#endif
};
static int g_epoll_fd;
@@ -49,92 +53,105 @@
static unsigned ev_dev_count = 0;
static unsigned ev_misc_count = 0;
-static bool test_bit(size_t bit, unsigned long* array) {
+static bool test_bit(size_t bit, unsigned long* array) { // NOLINT
return (array[bit/BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0;
}
+#ifdef TW_USE_MINUI_WITH_DATA
int ev_init(ev_callback input_cb, void* data) {
- bool epollctlfail = false;
+#else
+int ev_init(ev_callback input_cb) {
+#endif
+ bool epollctlfail = false;
- g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS);
- if (g_epoll_fd == -1) {
- return -1;
+ g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS);
+ if (g_epoll_fd == -1) {
+ return -1;
+ }
+
+ DIR* dir = opendir("/dev/input");
+ if (dir != NULL) {
+ dirent* de;
+ while ((de = readdir(dir))) {
+ // Use unsigned long to match ioctl's parameter type.
+ unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
+
+ // fprintf(stderr,"/dev/input/%s\n", de->d_name);
+ if (strncmp(de->d_name, "event", 5)) continue;
+ int fd = openat(dirfd(dir), de->d_name, O_RDONLY);
+ if (fd == -1) continue;
+
+ // Read the evbits of the input device.
+ if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+ close(fd);
+ continue;
+ }
+
+ // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed.
+ if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
+ close(fd);
+ continue;
+ }
+
+ epoll_event ev;
+ ev.events = EPOLLIN | EPOLLWAKEUP;
+ ev.data.ptr = &ev_fdinfo[ev_count];
+ if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ close(fd);
+ epollctlfail = true;
+ continue;
+ }
+
+ ev_fdinfo[ev_count].fd = fd;
+ ev_fdinfo[ev_count].cb = std::move(input_cb);
+#ifdef TW_USE_MINUI_WITH_DATA
+ ev_fdinfo[ev_count].data = data;
+#endif
+ ev_count++;
+ ev_dev_count++;
+ if (ev_dev_count == MAX_DEVICES) break;
}
- DIR* dir = opendir("/dev/input");
- if (dir != NULL) {
- dirent* de;
- while ((de = readdir(dir))) {
- unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
+ closedir(dir);
+ }
-// fprintf(stderr,"/dev/input/%s\n", de->d_name);
- if (strncmp(de->d_name, "event", 5)) continue;
- int fd = openat(dirfd(dir), de->d_name, O_RDONLY);
- if (fd == -1) continue;
+ if (epollctlfail && !ev_count) {
+ close(g_epoll_fd);
+ g_epoll_fd = -1;
+ return -1;
+ }
- // Read the evbits of the input device.
- if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
- close(fd);
- continue;
- }
-
- // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed.
- if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
- close(fd);
- continue;
- }
-
- epoll_event ev;
- ev.events = EPOLLIN | EPOLLWAKEUP;
- ev.data.ptr = &ev_fdinfo[ev_count];
- if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
- close(fd);
- epollctlfail = true;
- continue;
- }
-
- ev_fdinfo[ev_count].fd = fd;
- ev_fdinfo[ev_count].cb = input_cb;
- ev_fdinfo[ev_count].data = data;
- ev_count++;
- ev_dev_count++;
- if (ev_dev_count == MAX_DEVICES) break;
- }
-
- closedir(dir);
- }
-
- if (epollctlfail && !ev_count) {
- close(g_epoll_fd);
- g_epoll_fd = -1;
- return -1;
- }
-
- return 0;
+ return 0;
}
int ev_get_epollfd(void) {
return g_epoll_fd;
}
+#ifdef TW_USE_MINUI_WITH_DATA
int ev_add_fd(int fd, ev_callback cb, void* data) {
- if (ev_misc_count == MAX_MISC_FDS || cb == NULL) {
- return -1;
- }
+#else
+int ev_add_fd(int fd, ev_callback cb) {
+#endif
+ if (ev_misc_count == MAX_MISC_FDS || cb == NULL) {
+ return -1;
+ }
- epoll_event ev;
- ev.events = EPOLLIN | EPOLLWAKEUP;
- ev.data.ptr = (void *)&ev_fdinfo[ev_count];
- int ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev);
- if (!ret) {
- ev_fdinfo[ev_count].fd = fd;
- ev_fdinfo[ev_count].cb = cb;
- ev_fdinfo[ev_count].data = data;
- ev_count++;
- ev_misc_count++;
- }
+ epoll_event ev;
+ ev.events = EPOLLIN | EPOLLWAKEUP;
+ ev.data.ptr = static_cast<void*>(&ev_fdinfo[ev_count]);
+ int ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev);
+ if (!ret) {
+ ev_fdinfo[ev_count].fd = fd;
+ ev_fdinfo[ev_count].cb = std::move(cb);
+#ifdef TW_USE_MINUI_WITH_DATA
+ ev_fdinfo[ev_count].data = data;
+#endif
+ ev_count++;
+ ev_misc_count++;
+ }
- return ret;
+ return ret;
}
void ev_exit(void) {
@@ -155,13 +172,17 @@
}
void ev_dispatch(void) {
- for (int n = 0; n < npolledevents; n++) {
- fd_info* fdi = reinterpret_cast<fd_info*>(polledevents[n].data.ptr);
- ev_callback cb = fdi->cb;
- if (cb) {
- cb(fdi->fd, polledevents[n].events, fdi->data);
- }
+ for (int n = 0; n < npolledevents; n++) {
+ fd_info* fdi = static_cast<fd_info*>(polledevents[n].data.ptr);
+ const ev_callback& cb = fdi->cb;
+ if (cb) {
+#ifdef TW_USE_MINUI_WITH_DATA
+ cb(fdi->fd, polledevents[n].events, fdi->data);
+#else
+ cb(fdi->fd, polledevents[n].events);
+#endif
}
+ }
}
int ev_get_input(int fd, uint32_t epevents, input_event* ev) {
@@ -174,37 +195,47 @@
return -1;
}
+#ifdef TW_USE_MINUI_WITH_DATA
int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data) {
- unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
- unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)];
+#else
+int ev_sync_key_state(const ev_set_key_callback& set_key_cb) {
+#endif
+ // Use unsigned long to match ioctl's parameter type.
+ unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
+ unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)]; // NOLINT
- for (size_t i = 0; i < ev_dev_count; ++i) {
- memset(ev_bits, 0, sizeof(ev_bits));
- memset(key_bits, 0, sizeof(key_bits));
+ for (size_t i = 0; i < ev_dev_count; ++i) {
+ memset(ev_bits, 0, sizeof(ev_bits));
+ memset(key_bits, 0, sizeof(key_bits));
- if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
- continue;
- }
- if (!test_bit(EV_KEY, ev_bits)) {
- continue;
- }
- if (ioctl(ev_fdinfo[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits) == -1) {
- continue;
- }
-
- for (int code = 0; code <= KEY_MAX; code++) {
- if (test_bit(code, key_bits)) {
- set_key_cb(code, 1, data);
- }
- }
+ if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+ continue;
+ }
+ if (!test_bit(EV_KEY, ev_bits)) {
+ continue;
+ }
+ if (ioctl(ev_fdinfo[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits) == -1) {
+ continue;
}
- return 0;
+ for (int code = 0; code <= KEY_MAX; code++) {
+ if (test_bit(code, key_bits)) {
+#ifdef TW_USE_MINUI_WITH_DATA
+ set_key_cb(code, 1, data);
+#else
+ set_key_cb(code, 1);
+#endif
+ }
+ }
+ }
+
+ return 0;
}
-void ev_iterate_available_keys(std::function<void(int)> f) {
- unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
- unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)];
+void ev_iterate_available_keys(const std::function<void(int)>& f) {
+ // Use unsigned long to match ioctl's parameter type.
+ unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
+ unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)]; // NOLINT
for (size_t i = 0; i < ev_dev_count; ++i) {
memset(ev_bits, 0, sizeof(ev_bits));
diff --git a/minui/graphics.cpp b/minui/graphics.cpp
index 9525176..bb96af1 100644
--- a/minui/graphics.cpp
+++ b/minui/graphics.cpp
@@ -14,22 +14,13 @@
* limitations under the License.
*/
-#include <stdbool.h>
+#include "graphics.h"
+
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-
-#include <linux/fb.h>
-#include <linux/kd.h>
-
-#include <time.h>
+#include <memory>
#ifdef BOARD_USE_CUSTOM_RECOVERY_FONT
#include BOARD_USE_CUSTOM_RECOVERY_FONT
@@ -37,11 +28,14 @@
#include "font_10x18.h"
#endif
-#include "minui.h"
-#include "graphics.h"
+#include "graphics_adf.h"
+#include "graphics_drm.h"
+#include "graphics_fbdev.h"
+#include "graphics_overlay.h"
+#include "minui/minui.h"
static GRFont* gr_font = NULL;
-static minui_backend* gr_backend = NULL;
+static MinuiBackend* gr_backend = nullptr;
static int overscan_percent = OVERSCAN_PERCENT;
static int overscan_offset_x = 0;
@@ -470,7 +464,7 @@
}
#else // TW_NO_MINUI_CUSTOM_FONTS
int gr_init_font(const char* name, GRFont** dest) {
- GRFont* font = reinterpret_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
+ GRFont* font = static_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
if (font == nullptr) {
return -1;
}
@@ -503,15 +497,15 @@
// fall back to the compiled-in font.
- gr_font = reinterpret_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
- gr_font->texture = reinterpret_cast<GRSurface*>(malloc(sizeof(*gr_font->texture)));
+ gr_font = static_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
+ gr_font->texture = static_cast<GRSurface*>(malloc(sizeof(*gr_font->texture)));
gr_font->texture->width = font.width;
gr_font->texture->height = font.height;
gr_font->texture->row_bytes = font.width;
gr_font->texture->pixel_bytes = 1;
- unsigned char* bits = reinterpret_cast<unsigned char*>(malloc(font.width * font.height));
- gr_font->texture->data = reinterpret_cast<unsigned char*>(bits);
+ unsigned char* bits = static_cast<unsigned char*>(malloc(font.width * font.height));
+ gr_font->texture->data = bits;
unsigned char data;
unsigned char* in = font.rundata;
@@ -525,131 +519,75 @@
}
#endif // TW_NO_MINUI_CUSTOM_FONTS
-#if 0
-// Exercises many of the gr_*() functions; useful for testing.
-static void gr_test() {
- GRSurface** images;
- int frames;
- int result = res_create_multi_surface("icon_installing", &frames, &images);
- if (result < 0) {
- printf("create surface %d\n", result);
- gr_exit();
- return;
- }
-
- time_t start = time(NULL);
- int x;
- for (x = 0; x <= 1200; ++x) {
- if (x < 400) {
- gr_color(0, 0, 0, 255);
- } else {
- gr_color(0, (x-400)%128, 0, 255);
- }
- gr_clear();
-
- gr_color(255, 0, 0, 255);
- GRSurface* frame = images[x%frames];
- gr_blit(frame, 0, 0, frame->width, frame->height, x, 0);
-
- gr_color(255, 0, 0, 128);
- gr_fill(400, 150, 600, 350);
-
- gr_color(255, 255, 255, 255);
- gr_text(500, 225, "hello, world!", 0);
- gr_color(255, 255, 0, 128);
- gr_text(300+x, 275, "pack my box with five dozen liquor jugs", 1);
-
- gr_color(0, 0, 255, 128);
- gr_fill(gr_draw->width - 200 - x, 300, gr_draw->width - x, 500);
-
- gr_draw = gr_backend->flip(gr_backend);
- }
- printf("getting end time\n");
- time_t end = time(NULL);
- printf("got end time\n");
- printf("start %ld end %ld\n", (long)start, (long)end);
- if (end > start) {
- printf("%.2f fps\n", ((double)x) / (end-start));
- }
-}
-#endif
-
void gr_flip() {
- gr_draw = gr_backend->flip(gr_backend);
+ gr_draw = gr_backend->Flip();
}
int gr_init(void)
{
- gr_init_font();
- gr_draw = NULL;
+ gr_init_font();
+
+ auto backend = std::unique_ptr<MinuiBackend>{ std::make_unique<MinuiBackendOverlay>() };
+ gr_draw = backend->Init();
#ifdef MSM_BSP
- gr_backend = open_overlay();
- if (gr_backend) {
- gr_draw = gr_backend->init(gr_backend);
- if (!gr_draw) {
- gr_backend->exit(gr_backend);
- } else
- printf("Using overlay graphics.\n");
+ if (gr_draw) {
+ printf("Using overlay graphics.\n");
}
#endif
#ifndef MSM_BSP
- if (!gr_backend || !gr_draw) {
- gr_backend = open_adf();
- if (gr_backend) {
- gr_draw = gr_backend->init(gr_backend);
- if (!gr_draw) {
- gr_backend->exit(gr_backend);
- } else
- printf("Using adf graphics.\n");
- }
+ if (!gr_draw) {
+ backend = std::make_unique<MinuiBackendAdf>();
+ gr_draw = backend->Init();
+ if (gr_draw)
+ printf("Using adf graphics.\n");
}
#else
printf("Skipping adf graphics because TW_TARGET_USES_QCOM_BSP := true\n");
#endif
- if (!gr_backend || !gr_draw) {
- gr_backend = open_drm();
- gr_draw = gr_backend->init(gr_backend);
+ if (!gr_draw) {
+ backend = std::make_unique<MinuiBackendDrm>();
+ gr_draw = backend->Init();
if (gr_draw)
printf("Using drm graphics.\n");
}
- if (!gr_backend || !gr_draw) {
- gr_backend = open_fbdev();
- gr_draw = gr_backend->init(gr_backend);
- if (gr_draw == NULL) {
- return -1;
- } else
+ if (!gr_draw) {
+ backend = std::make_unique<MinuiBackendFbdev>();
+ gr_draw = backend->Init();
+ if (gr_draw)
printf("Using fbdev graphics.\n");
}
- overscan_offset_x = gr_draw->width * overscan_percent / 100;
- overscan_offset_y = gr_draw->height * overscan_percent / 100;
+ if (!gr_draw) {
+ return -1;
+ }
- gr_flip();
- gr_flip();
+ gr_backend = backend.release();
- return 0;
+ overscan_offset_x = gr_draw->width * overscan_percent / 100;
+ overscan_offset_y = gr_draw->height * overscan_percent / 100;
+
+ gr_flip();
+ gr_flip();
+
+ return 0;
}
-void gr_exit(void)
-{
- gr_backend->exit(gr_backend);
+void gr_exit() {
+ delete gr_backend;
}
-int gr_fb_width(void)
-{
- return gr_draw->width - 2*overscan_offset_x;
+int gr_fb_width() {
+ return gr_draw->width - 2 * overscan_offset_x;
}
-int gr_fb_height(void)
-{
- return gr_draw->height - 2*overscan_offset_y;
+int gr_fb_height() {
+ return gr_draw->height - 2 * overscan_offset_y;
}
-void gr_fb_blank(bool blank)
-{
- gr_backend->blank(gr_backend, blank);
+void gr_fb_blank(bool blank) {
+ gr_backend->Blank(blank);
}
diff --git a/minui/graphics.h b/minui/graphics.h
index a4115fd..3c45a40 100644
--- a/minui/graphics.h
+++ b/minui/graphics.h
@@ -17,28 +17,22 @@
#ifndef _GRAPHICS_H_
#define _GRAPHICS_H_
-#include "minui.h"
+#include "minui/minui.h"
-// TODO: lose the function pointers.
-struct minui_backend {
- // Initializes the backend and returns a GRSurface* to draw into.
- GRSurface* (*init)(minui_backend*);
+class MinuiBackend {
+ public:
+ // Initializes the backend and returns a GRSurface* to draw into.
+ virtual GRSurface* Init() = 0;
- // Causes the current drawing surface (returned by the most recent
- // call to flip() or init()) to be displayed, and returns a new
- // drawing surface.
- GRSurface* (*flip)(minui_backend*);
+ // Causes the current drawing surface (returned by the most recent call to Flip() or Init()) to
+ // be displayed, and returns a new drawing surface.
+ virtual GRSurface* Flip() = 0;
- // Blank (or unblank) the screen.
- void (*blank)(minui_backend*, bool);
+ // Blank (or unblank) the screen.
+ virtual void Blank(bool) = 0;
- // Device cleanup when drawing is done.
- void (*exit)(minui_backend*);
+ // Device cleanup when drawing is done.
+ virtual ~MinuiBackend() {};
};
-minui_backend* open_fbdev();
-minui_backend* open_adf();
-minui_backend* open_drm();
-minui_backend* open_overlay();
-
-#endif
+#endif // _GRAPHICS_H_
diff --git a/minui/graphics_adf.cpp b/minui/graphics_adf.cpp
index 10e1c4b..79f4db8 100644
--- a/minui/graphics_adf.cpp
+++ b/minui/graphics_adf.cpp
@@ -14,238 +14,190 @@
* limitations under the License.
*/
+#include "graphics_adf.h"
+
#include <errno.h>
#include <fcntl.h>
-#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
+#include <sys/mman.h>
#include <unistd.h>
-#include <sys/cdefs.h>
-#include <sys/mman.h>
-
#include <adf/adf.h>
-
-#include "graphics.h"
-
-struct adf_surface_pdata {
- GRSurface base;
- int fd;
- __u32 offset;
- __u32 pitch;
-};
-
-struct adf_pdata {
- minui_backend base;
- int intf_fd;
- adf_id_t eng_id;
- __u32 format;
-
- unsigned int current_surface;
- unsigned int n_surfaces;
- adf_surface_pdata surfaces[2];
-};
-
-static GRSurface* adf_flip(minui_backend *backend);
-static void adf_blank(minui_backend *backend, bool blank);
-
-static int adf_surface_init(adf_pdata *pdata, drm_mode_modeinfo *mode, adf_surface_pdata *surf) {
- memset(surf, 0, sizeof(*surf));
-
- surf->fd = adf_interface_simple_buffer_alloc(pdata->intf_fd, mode->hdisplay,
- mode->vdisplay, pdata->format, &surf->offset, &surf->pitch);
- if (surf->fd < 0)
- return surf->fd;
-
- surf->base.width = mode->hdisplay;
- surf->base.height = mode->vdisplay;
- surf->base.row_bytes = surf->pitch;
- surf->base.pixel_bytes = (pdata->format == DRM_FORMAT_RGB565) ? 2 : 4;
-
- surf->base.data = reinterpret_cast<uint8_t*>(mmap(NULL,
- surf->pitch * surf->base.height, PROT_WRITE,
- MAP_SHARED, surf->fd, surf->offset));
- if (surf->base.data == MAP_FAILED) {
- close(surf->fd);
- return -errno;
- }
-
- return 0;
-}
-
-static int adf_interface_init(adf_pdata *pdata)
-{
- adf_interface_data intf_data;
- int ret = 0;
- int err;
-
- err = adf_get_interface_data(pdata->intf_fd, &intf_data);
- if (err < 0)
- return err;
-
- err = adf_surface_init(pdata, &intf_data.current_mode, &pdata->surfaces[0]);
- if (err < 0) {
- fprintf(stderr, "allocating surface 0 failed: %s\n", strerror(-err));
- ret = err;
- goto done;
- }
-
- err = adf_surface_init(pdata, &intf_data.current_mode,
- &pdata->surfaces[1]);
- if (err < 0) {
- fprintf(stderr, "allocating surface 1 failed: %s\n", strerror(-err));
- memset(&pdata->surfaces[1], 0, sizeof(pdata->surfaces[1]));
- pdata->n_surfaces = 1;
- } else {
- pdata->n_surfaces = 2;
- }
-
-done:
- adf_free_interface_data(&intf_data);
- return ret;
-}
-
-static int adf_device_init(adf_pdata *pdata, adf_device *dev)
-{
- adf_id_t intf_id;
- int intf_fd;
- int err;
-
- err = adf_find_simple_post_configuration(dev, &pdata->format, 1, &intf_id,
- &pdata->eng_id);
- if (err < 0)
- return err;
-
- err = adf_device_attach(dev, pdata->eng_id, intf_id);
- if (err < 0 && err != -EALREADY)
- return err;
-
- pdata->intf_fd = adf_interface_open(dev, intf_id, O_RDWR);
- if (pdata->intf_fd < 0)
- return pdata->intf_fd;
-
- err = adf_interface_init(pdata);
- if (err < 0) {
- close(pdata->intf_fd);
- pdata->intf_fd = -1;
- }
-
- return err;
-}
-
-static GRSurface* adf_init(minui_backend *backend)
-{
- adf_pdata *pdata = (adf_pdata *)backend;
- adf_id_t *dev_ids = NULL;
- ssize_t n_dev_ids, i;
- GRSurface* ret;
-
-#if defined(RECOVERY_ABGR)
- pdata->format = DRM_FORMAT_ABGR8888;
-#elif defined(RECOVERY_BGRA)
- pdata->format = DRM_FORMAT_BGRA8888;
-#elif defined(RECOVERY_RGBA)
- pdata->format = DRM_FORMAT_RGBA8888;
-#elif defined(RECOVERY_RGBX)
- pdata->format = DRM_FORMAT_RGBX8888;
-#else
- pdata->format = DRM_FORMAT_RGB565;
+#ifdef HAS_LIBSYNC
+#include <sync/sync.h>
#endif
- n_dev_ids = adf_devices(&dev_ids);
- if (n_dev_ids == 0) {
- return NULL;
- } else if (n_dev_ids < 0) {
- fprintf(stderr, "enumerating adf devices failed: %s\n",
- strerror(-n_dev_ids));
- return NULL;
- }
+#include "minui/minui.h"
- pdata->intf_fd = -1;
+MinuiBackendAdf::MinuiBackendAdf() : intf_fd(-1), dev(), n_surfaces(0), surfaces() {}
- for (i = 0; i < n_dev_ids && pdata->intf_fd < 0; i++) {
- adf_device dev;
+int MinuiBackendAdf::SurfaceInit(const drm_mode_modeinfo* mode, GRSurfaceAdf* surf) {
+ *surf = {};
+ surf->fence_fd = -1;
+ surf->fd = adf_interface_simple_buffer_alloc(intf_fd, mode->hdisplay, mode->vdisplay, format,
+ &surf->offset, &surf->pitch);
+ if (surf->fd < 0) {
+ return surf->fd;
+ }
- int err = adf_device_open(dev_ids[i], O_RDWR, &dev);
- if (err < 0) {
- fprintf(stderr, "opening adf device %u failed: %s\n", dev_ids[i],
- strerror(-err));
- continue;
- }
+ surf->width = mode->hdisplay;
+ surf->height = mode->vdisplay;
+ surf->row_bytes = surf->pitch;
+ surf->pixel_bytes = (format == DRM_FORMAT_RGB565) ? 2 : 4;
- err = adf_device_init(pdata, &dev);
- if (err < 0)
- fprintf(stderr, "initializing adf device %u failed: %s\n",
- dev_ids[i], strerror(-err));
-
- adf_device_close(&dev);
- }
-
- free(dev_ids);
-
- if (pdata->intf_fd < 0)
- return NULL;
-
- ret = adf_flip(backend);
-
- adf_blank(backend, true);
- adf_blank(backend, false);
-
- return ret;
-}
-
-static GRSurface* adf_flip(minui_backend *backend)
-{
- adf_pdata *pdata = (adf_pdata *)backend;
- adf_surface_pdata *surf = &pdata->surfaces[pdata->current_surface];
-
- int fence_fd = adf_interface_simple_post(pdata->intf_fd, pdata->eng_id,
- surf->base.width, surf->base.height, pdata->format, surf->fd,
- surf->offset, surf->pitch, -1);
- if (fence_fd >= 0)
- close(fence_fd);
-
- pdata->current_surface = (pdata->current_surface + 1) % pdata->n_surfaces;
- return &pdata->surfaces[pdata->current_surface].base;
-}
-
-static void adf_blank(minui_backend *backend, bool blank)
-{
- adf_pdata *pdata = (adf_pdata *)backend;
- adf_interface_blank(pdata->intf_fd,
- blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON);
-}
-
-static void adf_surface_destroy(adf_surface_pdata *surf)
-{
- munmap(surf->base.data, surf->pitch * surf->base.height);
+ surf->data = static_cast<uint8_t*>(
+ mmap(nullptr, surf->pitch * surf->height, PROT_WRITE, MAP_SHARED, surf->fd, surf->offset));
+ if (surf->data == MAP_FAILED) {
+ int saved_errno = errno;
close(surf->fd);
+ return -saved_errno;
+ }
+
+ return 0;
}
-static void adf_exit(minui_backend *backend)
-{
- adf_pdata *pdata = (adf_pdata *)backend;
- unsigned int i;
+int MinuiBackendAdf::InterfaceInit() {
+ adf_interface_data intf_data;
+ int err = adf_get_interface_data(intf_fd, &intf_data);
+ if (err < 0) return err;
- for (i = 0; i < pdata->n_surfaces; i++)
- adf_surface_destroy(&pdata->surfaces[i]);
- if (pdata->intf_fd >= 0)
- close(pdata->intf_fd);
- free(pdata);
+ int ret = 0;
+ err = SurfaceInit(&intf_data.current_mode, &surfaces[0]);
+ if (err < 0) {
+ fprintf(stderr, "allocating surface 0 failed: %s\n", strerror(-err));
+ ret = err;
+ goto done;
+ }
+
+ err = SurfaceInit(&intf_data.current_mode, &surfaces[1]);
+ if (err < 0) {
+ fprintf(stderr, "allocating surface 1 failed: %s\n", strerror(-err));
+ surfaces[1] = {};
+ n_surfaces = 1;
+ } else {
+ n_surfaces = 2;
+ }
+
+done:
+ adf_free_interface_data(&intf_data);
+ return ret;
}
-minui_backend *open_adf()
-{
- adf_pdata* pdata = reinterpret_cast<adf_pdata*>(calloc(1, sizeof(*pdata)));
- if (!pdata) {
- perror("allocating adf backend failed");
- return NULL;
+int MinuiBackendAdf::DeviceInit(adf_device* dev) {
+ adf_id_t intf_id;
+ int err = adf_find_simple_post_configuration(dev, &format, 1, &intf_id, &eng_id);
+ if (err < 0) return err;
+
+ err = adf_device_attach(dev, eng_id, intf_id);
+ if (err < 0 && err != -EALREADY) return err;
+
+ intf_fd = adf_interface_open(dev, intf_id, O_RDWR);
+ if (intf_fd < 0) return intf_fd;
+
+ err = InterfaceInit();
+ if (err < 0) {
+ close(intf_fd);
+ intf_fd = -1;
+ }
+
+ return err;
+}
+
+GRSurface* MinuiBackendAdf::Init() {
+#if defined(RECOVERY_ABGR)
+ format = DRM_FORMAT_ABGR8888;
+#elif defined(RECOVERY_BGRA)
+ format = DRM_FORMAT_BGRA8888;
+#elif defined(RECOVERY_RGBA)
+ format = DRM_FORMAT_RGBA8888;
+#elif defined(RECOVERY_RGBX)
+ format = DRM_FORMAT_RGBX8888;
+#else
+ format = DRM_FORMAT_RGB565;
+#endif
+
+ adf_id_t* dev_ids = nullptr;
+ ssize_t n_dev_ids = adf_devices(&dev_ids);
+ if (n_dev_ids == 0) {
+ return nullptr;
+ } else if (n_dev_ids < 0) {
+ fprintf(stderr, "enumerating adf devices failed: %s\n", strerror(-n_dev_ids));
+ return nullptr;
+ }
+
+ intf_fd = -1;
+
+ for (ssize_t i = 0; i < n_dev_ids && intf_fd < 0; i++) {
+ int err = adf_device_open(dev_ids[i], O_RDWR, &dev);
+ if (err < 0) {
+ fprintf(stderr, "opening adf device %u failed: %s\n", dev_ids[i], strerror(-err));
+ continue;
}
- pdata->base.init = adf_init;
- pdata->base.flip = adf_flip;
- pdata->base.blank = adf_blank;
- pdata->base.exit = adf_exit;
- return &pdata->base;
+ err = DeviceInit(&dev);
+ if (err < 0) {
+ fprintf(stderr, "initializing adf device %u failed: %s\n", dev_ids[i], strerror(-err));
+ adf_device_close(&dev);
+ }
+ }
+
+ free(dev_ids);
+
+ if (intf_fd < 0) return nullptr;
+
+ GRSurface* ret = Flip();
+
+ Blank(true);
+ Blank(false);
+
+ return ret;
+}
+
+void MinuiBackendAdf::Sync(__unused GRSurfaceAdf* surf) {
+#ifdef HAS_LIBSYNC
+ static constexpr unsigned int warningTimeout = 3000;
+
+ if (surf == nullptr) return;
+
+ if (surf->fence_fd >= 0) {
+ int err = sync_wait(surf->fence_fd, warningTimeout);
+ if (err < 0) {
+ perror("adf sync fence wait error\n");
+ }
+
+ close(surf->fence_fd);
+ surf->fence_fd = -1;
+ }
+#endif
+}
+
+GRSurface* MinuiBackendAdf::Flip() {
+ GRSurfaceAdf* surf = &surfaces[current_surface];
+
+ int fence_fd = adf_interface_simple_post(intf_fd, eng_id, surf->width, surf->height, format,
+ surf->fd, surf->offset, surf->pitch, -1);
+ if (fence_fd >= 0) surf->fence_fd = fence_fd;
+
+ current_surface = (current_surface + 1) % n_surfaces;
+ Sync(&surfaces[current_surface]);
+ return &surfaces[current_surface];
+}
+
+void MinuiBackendAdf::Blank(bool blank) {
+ adf_interface_blank(intf_fd, blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON);
+}
+
+void MinuiBackendAdf::SurfaceDestroy(GRSurfaceAdf* surf) {
+ munmap(surf->data, surf->pitch * surf->height);
+ close(surf->fence_fd);
+ close(surf->fd);
+}
+
+MinuiBackendAdf::~MinuiBackendAdf() {
+ adf_device_close(&dev);
+ for (unsigned int i = 0; i < n_surfaces; i++) {
+ SurfaceDestroy(&surfaces[i]);
+ }
+ if (intf_fd >= 0) close(intf_fd);
}
diff --git a/minui/graphics_adf.h b/minui/graphics_adf.h
new file mode 100644
index 0000000..2f019ed
--- /dev/null
+++ b/minui/graphics_adf.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GRAPHICS_ADF_H_
+#define _GRAPHICS_ADF_H_
+
+#include <adf/adf.h>
+
+#include "graphics.h"
+
+class GRSurfaceAdf : public GRSurface {
+ private:
+ int fence_fd;
+ int fd;
+ __u32 offset;
+ __u32 pitch;
+
+ friend class MinuiBackendAdf;
+};
+
+class MinuiBackendAdf : public MinuiBackend {
+ public:
+ GRSurface* Init() override;
+ GRSurface* Flip() override;
+ void Blank(bool) override;
+ ~MinuiBackendAdf() override;
+ MinuiBackendAdf();
+
+ private:
+ int SurfaceInit(const drm_mode_modeinfo* mode, GRSurfaceAdf* surf);
+ int InterfaceInit();
+ int DeviceInit(adf_device* dev);
+ void SurfaceDestroy(GRSurfaceAdf* surf);
+ void Sync(GRSurfaceAdf* surf);
+
+ int intf_fd;
+ adf_id_t eng_id;
+ __u32 format;
+ adf_device dev;
+ unsigned int current_surface;
+ unsigned int n_surfaces;
+ GRSurfaceAdf surfaces[2];
+};
+
+#endif // _GRAPHICS_ADF_H_
diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp
index ddda187..ef377b7 100644
--- a/minui/graphics_drm.cpp
+++ b/minui/graphics_drm.cpp
@@ -14,466 +14,384 @@
* limitations under the License.
*/
-#include <drm_fourcc.h>
+#include "graphics_drm.h"
+
#include <fcntl.h>
-#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <sys/cdefs.h>
-#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
+
+#include <drm_fourcc.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
-#include "minui.h"
-#include "graphics.h"
+#include "minui/minui.h"
#define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A)))
-struct drm_surface {
- GRSurface base;
- uint32_t fb_id;
- uint32_t handle;
-};
+MinuiBackendDrm::MinuiBackendDrm()
+ : GRSurfaceDrms(), main_monitor_crtc(nullptr), main_monitor_connector(nullptr), drm_fd(-1) {}
-static drm_surface *drm_surfaces[2];
-static int current_buffer;
-
-static drmModeCrtc *main_monitor_crtc;
-static drmModeConnector *main_monitor_connector;
-
-static int drm_fd = -1;
-
-static void drm_disable_crtc(int drm_fd, drmModeCrtc *crtc) {
- if (crtc) {
- drmModeSetCrtc(drm_fd, crtc->crtc_id,
- 0, // fb_id
- 0, 0, // x,y
- NULL, // connectors
- 0, // connector_count
- NULL); // mode
- }
+void MinuiBackendDrm::DrmDisableCrtc(int drm_fd, drmModeCrtc* crtc) {
+ if (crtc) {
+ drmModeSetCrtc(drm_fd, crtc->crtc_id,
+ 0, // fb_id
+ 0, 0, // x,y
+ nullptr, // connectors
+ 0, // connector_count
+ nullptr); // mode
+ }
}
-static void drm_enable_crtc(int drm_fd, drmModeCrtc *crtc,
- struct drm_surface *surface) {
- int32_t ret;
+void MinuiBackendDrm::DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, GRSurfaceDrm* surface) {
+ int32_t ret = drmModeSetCrtc(drm_fd, crtc->crtc_id, surface->fb_id, 0, 0, // x,y
+ &main_monitor_connector->connector_id,
+ 1, // connector_count
+ &main_monitor_crtc->mode);
- ret = drmModeSetCrtc(drm_fd, crtc->crtc_id,
- surface->fb_id,
- 0, 0, // x,y
- &main_monitor_connector->connector_id,
- 1, // connector_count
- &main_monitor_crtc->mode);
-
- if (ret)
- printf("drmModeSetCrtc failed ret=%d\n", ret);
+ if (ret) {
+ printf("drmModeSetCrtc failed ret=%d\n", ret);
+ }
}
-static void drm_blank(minui_backend* backend __unused, bool blank) {
- if (blank)
- drm_disable_crtc(drm_fd, main_monitor_crtc);
- else
- drm_enable_crtc(drm_fd, main_monitor_crtc,
- drm_surfaces[current_buffer]);
+void MinuiBackendDrm::Blank(bool blank) {
+ if (blank) {
+ DrmDisableCrtc(drm_fd, main_monitor_crtc);
+ } else {
+ DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[current_buffer]);
+ }
}
-static void drm_destroy_surface(struct drm_surface *surface) {
- struct drm_gem_close gem_close;
- int ret;
+void MinuiBackendDrm::DrmDestroySurface(GRSurfaceDrm* surface) {
+ if (!surface) return;
- if(!surface)
- return;
+ if (surface->data) {
+ munmap(surface->data, surface->row_bytes * surface->height);
+ }
- if (surface->base.data)
- munmap(surface->base.data,
- surface->base.row_bytes * surface->base.height);
-
- if (surface->fb_id) {
- ret = drmModeRmFB(drm_fd, surface->fb_id);
- if (ret)
- printf("drmModeRmFB failed ret=%d\n", ret);
+ if (surface->fb_id) {
+ int ret = drmModeRmFB(drm_fd, surface->fb_id);
+ if (ret) {
+ printf("drmModeRmFB failed ret=%d\n", ret);
}
+ }
- if (surface->handle) {
- memset(&gem_close, 0, sizeof(gem_close));
- gem_close.handle = surface->handle;
+ if (surface->handle) {
+ drm_gem_close gem_close = {};
+ gem_close.handle = surface->handle;
- ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
- if (ret)
- printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret);
+ int ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
+ if (ret) {
+ printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret);
}
+ }
- free(surface);
+ delete surface;
}
static int drm_format_to_bpp(uint32_t format) {
- switch(format) {
- case DRM_FORMAT_ABGR8888:
- case DRM_FORMAT_BGRA8888:
- case DRM_FORMAT_RGBX8888:
- case DRM_FORMAT_BGRX8888:
- case DRM_FORMAT_XBGR8888:
- case DRM_FORMAT_ARGB8888:
- case DRM_FORMAT_XRGB8888:
- return 32;
- case DRM_FORMAT_RGB565:
- return 16;
- default:
- printf("Unknown format %d\n", format);
- return 32;
- }
+ switch (format) {
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_BGRA8888:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRX8888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_XRGB8888:
+ return 32;
+ case DRM_FORMAT_RGB565:
+ return 16;
+ default:
+ printf("Unknown format %d\n", format);
+ return 32;
+ }
}
-static drm_surface *drm_create_surface(int width, int height) {
- struct drm_surface *surface;
- struct drm_mode_create_dumb create_dumb;
- uint32_t format;
- int ret;
+GRSurfaceDrm* MinuiBackendDrm::DrmCreateSurface(int width, int height) {
+ GRSurfaceDrm* surface = new GRSurfaceDrm;
+ *surface = {};
- surface = (struct drm_surface*)calloc(1, sizeof(*surface));
- if (!surface) {
- printf("Can't allocate memory\n");
- return NULL;
- }
-
+ uint32_t format;
#if defined(RECOVERY_ABGR)
- format = DRM_FORMAT_RGBA8888;
+ format = DRM_FORMAT_RGBA8888;
#elif defined(RECOVERY_BGRA)
- format = DRM_FORMAT_ARGB8888;
+ format = DRM_FORMAT_ARGB8888;
#elif defined(RECOVERY_RGBA)
- format = DRM_FORMAT_ABGR8888;
+ format = DRM_FORMAT_ARGB8888;
#elif defined(RECOVERY_RGBX)
- format = DRM_FORMAT_XBGR8888;
+ format = DRM_FORMAT_XBGR8888;
#else
- format = DRM_FORMAT_RGB565;
+ format = DRM_FORMAT_RGB565;
#endif
- memset(&create_dumb, 0, sizeof(create_dumb));
- create_dumb.height = height;
- create_dumb.width = width;
- create_dumb.bpp = drm_format_to_bpp(format);
- create_dumb.flags = 0;
+ drm_mode_create_dumb create_dumb = {};
+ create_dumb.height = height;
+ create_dumb.width = width;
+ create_dumb.bpp = drm_format_to_bpp(format);
+ create_dumb.flags = 0;
- ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
- if (ret) {
- printf("DRM_IOCTL_MODE_CREATE_DUMB failed ret=%d\n",ret);
- drm_destroy_surface(surface);
- return NULL;
- }
- surface->handle = create_dumb.handle;
+ int ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
+ if (ret) {
+ printf("DRM_IOCTL_MODE_CREATE_DUMB failed ret=%d\n", ret);
+ DrmDestroySurface(surface);
+ return nullptr;
+ }
+ surface->handle = create_dumb.handle;
- uint32_t handles[4], pitches[4], offsets[4];
+ uint32_t handles[4], pitches[4], offsets[4];
- handles[0] = surface->handle;
- pitches[0] = create_dumb.pitch;
- offsets[0] = 0;
+ handles[0] = surface->handle;
+ pitches[0] = create_dumb.pitch;
+ offsets[0] = 0;
- ret = drmModeAddFB2(drm_fd, width, height,
- format, handles, pitches, offsets,
- &(surface->fb_id), 0);
- if (ret) {
- printf("drmModeAddFB2 failed ret=%d\n", ret);
- drm_destroy_surface(surface);
- return NULL;
- }
+ ret =
+ drmModeAddFB2(drm_fd, width, height, format, handles, pitches, offsets, &(surface->fb_id), 0);
+ if (ret) {
+ printf("drmModeAddFB2 failed ret=%d\n", ret);
+ DrmDestroySurface(surface);
+ return nullptr;
+ }
- struct drm_mode_map_dumb map_dumb;
- memset(&map_dumb, 0, sizeof(map_dumb));
- map_dumb.handle = create_dumb.handle;
- ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
- if (ret) {
- printf("DRM_IOCTL_MODE_MAP_DUMB failed ret=%d\n",ret);
- drm_destroy_surface(surface);
- return NULL;;
- }
+ drm_mode_map_dumb map_dumb = {};
+ map_dumb.handle = create_dumb.handle;
+ ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
+ if (ret) {
+ printf("DRM_IOCTL_MODE_MAP_DUMB failed ret=%d\n", ret);
+ DrmDestroySurface(surface);
+ return nullptr;
+ }
- surface->base.height = height;
- surface->base.width = width;
- surface->base.row_bytes = create_dumb.pitch;
- surface->base.pixel_bytes = create_dumb.bpp / 8;
- surface->base.data = (unsigned char*)
- mmap(NULL,
- surface->base.height * surface->base.row_bytes,
- PROT_READ | PROT_WRITE, MAP_SHARED,
- drm_fd, map_dumb.offset);
- if (surface->base.data == MAP_FAILED) {
- perror("mmap() failed");
- drm_destroy_surface(surface);
- return NULL;
- }
+ surface->height = height;
+ surface->width = width;
+ surface->row_bytes = create_dumb.pitch;
+ surface->pixel_bytes = create_dumb.bpp / 8;
+ surface->data = static_cast<unsigned char*>(mmap(nullptr, surface->height * surface->row_bytes,
+ PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd,
+ map_dumb.offset));
+ if (surface->data == MAP_FAILED) {
+ perror("mmap() failed");
+ DrmDestroySurface(surface);
+ return nullptr;
+ }
- return surface;
+ return surface;
}
-static drmModeCrtc *find_crtc_for_connector(int fd,
- drmModeRes *resources,
- drmModeConnector *connector) {
- int i, j;
- drmModeEncoder *encoder;
- int32_t crtc;
+static drmModeCrtc* find_crtc_for_connector(int fd, drmModeRes* resources,
+ drmModeConnector* connector) {
+ // Find the encoder. If we already have one, just use it.
+ drmModeEncoder* encoder;
+ if (connector->encoder_id) {
+ encoder = drmModeGetEncoder(fd, connector->encoder_id);
+ } else {
+ encoder = nullptr;
+ }
- /*
- * Find the encoder. If we already have one, just use it.
- */
- if (connector->encoder_id)
- encoder = drmModeGetEncoder(fd, connector->encoder_id);
- else
- encoder = NULL;
+ int32_t crtc;
+ if (encoder && encoder->crtc_id) {
+ crtc = encoder->crtc_id;
+ drmModeFreeEncoder(encoder);
+ return drmModeGetCrtc(fd, crtc);
+ }
- if (encoder && encoder->crtc_id) {
- crtc = encoder->crtc_id;
+ // Didn't find anything, try to find a crtc and encoder combo.
+ crtc = -1;
+ for (int i = 0; i < connector->count_encoders; i++) {
+ encoder = drmModeGetEncoder(fd, connector->encoders[i]);
+
+ if (encoder) {
+ for (int j = 0; j < resources->count_crtcs; j++) {
+ if (!(encoder->possible_crtcs & (1 << j))) continue;
+ crtc = resources->crtcs[j];
+ break;
+ }
+ if (crtc >= 0) {
drmModeFreeEncoder(encoder);
return drmModeGetCrtc(fd, crtc);
+ }
}
+ }
- /*
- * Didn't find anything, try to find a crtc and encoder combo.
- */
- crtc = -1;
- for (i = 0; i < connector->count_encoders; i++) {
- encoder = drmModeGetEncoder(fd, connector->encoders[i]);
-
- if (encoder) {
- for (j = 0; j < resources->count_crtcs; j++) {
- if (!(encoder->possible_crtcs & (1 << j)))
- continue;
- crtc = resources->crtcs[j];
- break;
- }
- if (crtc >= 0) {
- drmModeFreeEncoder(encoder);
- return drmModeGetCrtc(fd, crtc);
- }
- }
- }
-
- return NULL;
+ return nullptr;
}
-static drmModeConnector *find_used_connector_by_type(int fd,
- drmModeRes *resources,
- unsigned type) {
- int i;
- for (i = 0; i < resources->count_connectors; i++) {
- drmModeConnector *connector;
-
- connector = drmModeGetConnector(fd, resources->connectors[i]);
- if (connector) {
- if ((connector->connector_type == type) &&
- (connector->connection == DRM_MODE_CONNECTED) &&
- (connector->count_modes > 0))
- return connector;
-
- drmModeFreeConnector(connector);
- }
+static drmModeConnector* find_used_connector_by_type(int fd, drmModeRes* resources, unsigned type) {
+ for (int i = 0; i < resources->count_connectors; i++) {
+ drmModeConnector* connector = drmModeGetConnector(fd, resources->connectors[i]);
+ if (connector) {
+ if ((connector->connector_type == type) && (connector->connection == DRM_MODE_CONNECTED) &&
+ (connector->count_modes > 0)) {
+ return connector;
+ }
+ drmModeFreeConnector(connector);
}
- return NULL;
+ }
+ return nullptr;
}
-static drmModeConnector *find_first_connected_connector(int fd,
- drmModeRes *resources) {
- int i;
- for (i = 0; i < resources->count_connectors; i++) {
- drmModeConnector *connector;
+static drmModeConnector* find_first_connected_connector(int fd, drmModeRes* resources) {
+ for (int i = 0; i < resources->count_connectors; i++) {
+ drmModeConnector* connector;
- connector = drmModeGetConnector(fd, resources->connectors[i]);
- if (connector) {
- if ((connector->count_modes > 0) &&
- (connector->connection == DRM_MODE_CONNECTED))
- return connector;
+ connector = drmModeGetConnector(fd, resources->connectors[i]);
+ if (connector) {
+ if ((connector->count_modes > 0) && (connector->connection == DRM_MODE_CONNECTED))
+ return connector;
- drmModeFreeConnector(connector);
- }
+ drmModeFreeConnector(connector);
}
- return NULL;
+ }
+ return nullptr;
}
-static drmModeConnector *find_main_monitor(int fd, drmModeRes *resources,
- uint32_t *mode_index) {
- unsigned i = 0;
- int modes;
- /* Look for LVDS/eDP/DSI connectors. Those are the main screens. */
- unsigned kConnectorPriority[] = {
- DRM_MODE_CONNECTOR_LVDS,
- DRM_MODE_CONNECTOR_eDP,
- DRM_MODE_CONNECTOR_DSI,
- };
+drmModeConnector* MinuiBackendDrm::FindMainMonitor(int fd, drmModeRes* resources,
+ uint32_t* mode_index) {
+ /* Look for LVDS/eDP/DSI connectors. Those are the main screens. */
+ static constexpr unsigned kConnectorPriority[] = {
+ DRM_MODE_CONNECTOR_LVDS,
+ DRM_MODE_CONNECTOR_eDP,
+ DRM_MODE_CONNECTOR_DSI,
+ };
- drmModeConnector *main_monitor_connector = NULL;
- do {
- main_monitor_connector = find_used_connector_by_type(fd,
- resources,
- kConnectorPriority[i]);
- i++;
- } while (!main_monitor_connector && i < ARRAY_SIZE(kConnectorPriority));
+ drmModeConnector* main_monitor_connector = nullptr;
+ unsigned i = 0;
+ do {
+ main_monitor_connector = find_used_connector_by_type(fd, resources, kConnectorPriority[i]);
+ i++;
+ } while (!main_monitor_connector && i < ARRAY_SIZE(kConnectorPriority));
- /* If we didn't find a connector, grab the first one that is connected. */
- if (!main_monitor_connector)
- main_monitor_connector =
- find_first_connected_connector(fd, resources);
+ /* If we didn't find a connector, grab the first one that is connected. */
+ if (!main_monitor_connector) {
+ main_monitor_connector = find_first_connected_connector(fd, resources);
+ }
- /* If we still didn't find a connector, give up and return. */
- if (!main_monitor_connector)
- return NULL;
+ /* If we still didn't find a connector, give up and return. */
+ if (!main_monitor_connector) return nullptr;
- *mode_index = 0;
- for (modes = 0; modes < main_monitor_connector->count_modes; modes++) {
- if (main_monitor_connector->modes[modes].type &
- DRM_MODE_TYPE_PREFERRED) {
- *mode_index = modes;
- break;
- }
+ *mode_index = 0;
+ for (int modes = 0; modes < main_monitor_connector->count_modes; modes++) {
+ if (main_monitor_connector->modes[modes].type & DRM_MODE_TYPE_PREFERRED) {
+ *mode_index = modes;
+ break;
}
+ }
- return main_monitor_connector;
+ return main_monitor_connector;
}
-static void disable_non_main_crtcs(int fd,
- drmModeRes *resources,
- drmModeCrtc* main_crtc) {
- int i;
- drmModeCrtc* crtc;
-
- for (i = 0; i < resources->count_connectors; i++) {
- drmModeConnector *connector;
-
- connector = drmModeGetConnector(fd, resources->connectors[i]);
- crtc = find_crtc_for_connector(fd, resources, connector);
- if (crtc->crtc_id != main_crtc->crtc_id)
- drm_disable_crtc(fd, crtc);
- drmModeFreeCrtc(crtc);
+void MinuiBackendDrm::DisableNonMainCrtcs(int fd, drmModeRes* resources, drmModeCrtc* main_crtc) {
+ for (int i = 0; i < resources->count_connectors; i++) {
+ drmModeConnector* connector = drmModeGetConnector(fd, resources->connectors[i]);
+ drmModeCrtc* crtc = find_crtc_for_connector(fd, resources, connector);
+ if (crtc->crtc_id != main_crtc->crtc_id) {
+ DrmDisableCrtc(fd, crtc);
}
+ drmModeFreeCrtc(crtc);
+ }
}
-static GRSurface* drm_init(minui_backend* backend __unused) {
- drmModeRes *res = NULL;
- uint32_t selected_mode;
- char *dev_name;
- int width, height;
- int ret, i;
+GRSurface* MinuiBackendDrm::Init() {
+ drmModeRes* res = nullptr;
- /* Consider DRM devices in order. */
- for (i = 0; i < DRM_MAX_MINOR; i++) {
- uint64_t cap = 0;
+ /* Consider DRM devices in order. */
+ for (int i = 0; i < DRM_MAX_MINOR; i++) {
+ char* dev_name;
+ int ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i);
+ if (ret < 0) continue;
- ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i);
- if (ret < 0)
- continue;
+ drm_fd = open(dev_name, O_RDWR, 0);
+ free(dev_name);
+ if (drm_fd < 0) continue;
- drm_fd = open(dev_name, O_RDWR, 0);
- free(dev_name);
- if (drm_fd < 0)
- continue;
-
- /* We need dumb buffers. */
- ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap);
- if (ret || cap == 0) {
- close(drm_fd);
- continue;
- }
-
- res = drmModeGetResources(drm_fd);
- if (!res) {
- close(drm_fd);
- continue;
- }
-
- /* Use this device if it has at least one connected monitor. */
- if (res->count_crtcs > 0 && res->count_connectors > 0)
- if (find_first_connected_connector(drm_fd, res))
- break;
-
- drmModeFreeResources(res);
- close(drm_fd);
- res = NULL;
+ uint64_t cap = 0;
+ /* We need dumb buffers. */
+ ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap);
+ if (ret || cap == 0) {
+ close(drm_fd);
+ continue;
}
- if (drm_fd < 0 || res == NULL) {
- perror("cannot find/open a drm device");
- return NULL;
+ res = drmModeGetResources(drm_fd);
+ if (!res) {
+ close(drm_fd);
+ continue;
}
- main_monitor_connector = find_main_monitor(drm_fd,
- res, &selected_mode);
-
- if (!main_monitor_connector) {
- printf("main_monitor_connector not found\n");
- drmModeFreeResources(res);
- close(drm_fd);
- return NULL;
+ /* Use this device if it has at least one connected monitor. */
+ if (res->count_crtcs > 0 && res->count_connectors > 0) {
+ if (find_first_connected_connector(drm_fd, res)) break;
}
- main_monitor_crtc = find_crtc_for_connector(drm_fd, res,
- main_monitor_connector);
-
- if (!main_monitor_crtc) {
- printf("main_monitor_crtc not found\n");
- drmModeFreeResources(res);
- close(drm_fd);
- return NULL;
- }
-
- disable_non_main_crtcs(drm_fd,
- res, main_monitor_crtc);
-
- main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode];
-
- width = main_monitor_crtc->mode.hdisplay;
- height = main_monitor_crtc->mode.vdisplay;
-
drmModeFreeResources(res);
-
- drm_surfaces[0] = drm_create_surface(width, height);
- drm_surfaces[1] = drm_create_surface(width, height);
- if (!drm_surfaces[0] || !drm_surfaces[1]) {
- drm_destroy_surface(drm_surfaces[0]);
- drm_destroy_surface(drm_surfaces[1]);
- drmModeFreeResources(res);
- close(drm_fd);
- return NULL;
- }
-
- current_buffer = 0;
-
- drm_enable_crtc(drm_fd, main_monitor_crtc, drm_surfaces[1]);
-
- return &(drm_surfaces[0]->base);
-}
-
-static GRSurface* drm_flip(minui_backend* backend __unused) {
- int ret;
-
- ret = drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id,
- drm_surfaces[current_buffer]->fb_id, 0, NULL);
- if (ret < 0) {
- printf("drmModePageFlip failed ret=%d\n", ret);
- return NULL;
- }
- current_buffer = 1 - current_buffer;
- return &(drm_surfaces[current_buffer]->base);
-}
-
-static void drm_exit(minui_backend* backend __unused) {
- drm_disable_crtc(drm_fd, main_monitor_crtc);
- drm_destroy_surface(drm_surfaces[0]);
- drm_destroy_surface(drm_surfaces[1]);
- drmModeFreeCrtc(main_monitor_crtc);
- drmModeFreeConnector(main_monitor_connector);
close(drm_fd);
- drm_fd = -1;
+ res = nullptr;
+ }
+
+ if (drm_fd < 0 || res == nullptr) {
+ perror("cannot find/open a drm device");
+ return nullptr;
+ }
+
+ uint32_t selected_mode;
+ main_monitor_connector = FindMainMonitor(drm_fd, res, &selected_mode);
+
+ if (!main_monitor_connector) {
+ printf("main_monitor_connector not found\n");
+ drmModeFreeResources(res);
+ close(drm_fd);
+ return nullptr;
+ }
+
+ main_monitor_crtc = find_crtc_for_connector(drm_fd, res, main_monitor_connector);
+
+ if (!main_monitor_crtc) {
+ printf("main_monitor_crtc not found\n");
+ drmModeFreeResources(res);
+ close(drm_fd);
+ return nullptr;
+ }
+
+ DisableNonMainCrtcs(drm_fd, res, main_monitor_crtc);
+
+ main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode];
+
+ int width = main_monitor_crtc->mode.hdisplay;
+ int height = main_monitor_crtc->mode.vdisplay;
+
+ drmModeFreeResources(res);
+
+ GRSurfaceDrms[0] = DrmCreateSurface(width, height);
+ GRSurfaceDrms[1] = DrmCreateSurface(width, height);
+ if (!GRSurfaceDrms[0] || !GRSurfaceDrms[1]) {
+ // GRSurfaceDrms and drm_fd should be freed in d'tor.
+ return nullptr;
+ }
+
+ current_buffer = 0;
+
+ DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[1]);
+
+ return GRSurfaceDrms[0];
}
-static minui_backend drm_backend = {
- .init = drm_init,
- .flip = drm_flip,
- .blank = drm_blank,
- .exit = drm_exit,
-};
+GRSurface* MinuiBackendDrm::Flip() {
+ int ret = drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id,
+ GRSurfaceDrms[current_buffer]->fb_id, 0, nullptr);
+ if (ret < 0) {
+ printf("drmModePageFlip failed ret=%d\n", ret);
+ return nullptr;
+ }
+ current_buffer = 1 - current_buffer;
+ return GRSurfaceDrms[current_buffer];
+}
-minui_backend* open_drm() {
- return &drm_backend;
+MinuiBackendDrm::~MinuiBackendDrm() {
+ DrmDisableCrtc(drm_fd, main_monitor_crtc);
+ DrmDestroySurface(GRSurfaceDrms[0]);
+ DrmDestroySurface(GRSurfaceDrms[1]);
+ drmModeFreeCrtc(main_monitor_crtc);
+ drmModeFreeConnector(main_monitor_connector);
+ close(drm_fd);
+ drm_fd = -1;
}
diff --git a/minui/graphics_drm.h b/minui/graphics_drm.h
new file mode 100644
index 0000000..de96212
--- /dev/null
+++ b/minui/graphics_drm.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GRAPHICS_DRM_H_
+#define _GRAPHICS_DRM_H_
+
+#include <stdint.h>
+
+#include <xf86drmMode.h>
+
+#include "graphics.h"
+#include "minui/minui.h"
+
+class GRSurfaceDrm : public GRSurface {
+ private:
+ uint32_t fb_id;
+ uint32_t handle;
+
+ friend class MinuiBackendDrm;
+};
+
+class MinuiBackendDrm : public MinuiBackend {
+ public:
+ GRSurface* Init() override;
+ GRSurface* Flip() override;
+ void Blank(bool) override;
+ ~MinuiBackendDrm() override;
+ MinuiBackendDrm();
+
+ private:
+ void DrmDisableCrtc(int drm_fd, drmModeCrtc* crtc);
+ void DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, GRSurfaceDrm* surface);
+ GRSurfaceDrm* DrmCreateSurface(int width, int height);
+ void DrmDestroySurface(GRSurfaceDrm* surface);
+ void DisableNonMainCrtcs(int fd, drmModeRes* resources, drmModeCrtc* main_crtc);
+ drmModeConnector* FindMainMonitor(int fd, drmModeRes* resources, uint32_t* mode_index);
+
+ GRSurfaceDrm* GRSurfaceDrms[2];
+ int current_buffer;
+ drmModeCrtc* main_monitor_crtc;
+ drmModeConnector* main_monitor_connector;
+ int drm_fd;
+};
+
+#endif // _GRAPHICS_DRM_H_
diff --git a/minui/graphics_fbdev.cpp b/minui/graphics_fbdev.cpp
index 5f64e1e..bb91a57 100644
--- a/minui/graphics_fbdev.cpp
+++ b/minui/graphics_fbdev.cpp
@@ -14,51 +14,23 @@
* limitations under the License.
*/
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+#include "graphics_fbdev.h"
#include <fcntl.h>
+#include <linux/fb.h>
#include <stdio.h>
-
-#include <sys/cdefs.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
+#include <unistd.h>
-#include <linux/fb.h>
-#include <linux/kd.h>
+#include "minui/minui.h"
-#include "minui.h"
-#include "graphics.h"
+MinuiBackendFbdev::MinuiBackendFbdev() : gr_draw(nullptr), fb_fd(-1) {}
-static GRSurface* fbdev_init(minui_backend*);
-static GRSurface* fbdev_flip(minui_backend*);
-static void fbdev_blank(minui_backend*, bool);
-static void fbdev_exit(minui_backend*);
-
-static GRSurface gr_framebuffer[2];
-static bool double_buffered;
-static GRSurface* gr_draw = NULL;
-static int displayed_buffer;
-
-static fb_var_screeninfo vi;
-static int fb_fd = -1;
-
-static minui_backend my_backend = {
- .init = fbdev_init,
- .flip = fbdev_flip,
- .blank = fbdev_blank,
- .exit = fbdev_exit,
-};
-
-minui_backend* open_fbdev() {
- return &my_backend;
-}
-
-static void fbdev_blank(minui_backend* backend __unused, bool blank)
-{
+void MinuiBackendFbdev::Blank(bool blank) {
#if defined(TW_NO_SCREEN_BLANK) && defined(TW_BRIGHTNESS_PATH) && defined(TW_MAX_BRIGHTNESS)
int fd;
char brightness[4];
@@ -80,136 +52,133 @@
#endif
}
-static void set_displayed_framebuffer(unsigned n)
-{
- if (n > 1 || !double_buffered) return;
+void MinuiBackendFbdev::SetDisplayedFramebuffer(unsigned n) {
+ if (n > 1 || !double_buffered) return;
- vi.yres_virtual = gr_framebuffer[0].height * 2;
- vi.yoffset = n * gr_framebuffer[0].height;
- vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8;
- if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
- perror("active fb swap failed");
- }
- displayed_buffer = n;
+ vi.yres_virtual = gr_framebuffer[0].height * 2;
+ vi.yoffset = n * gr_framebuffer[0].height;
+ vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8;
+ if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
+ perror("active fb swap failed");
+ }
+ displayed_buffer = n;
}
-static GRSurface* fbdev_init(minui_backend* backend) {
- int fd = open("/dev/graphics/fb0", O_RDWR);
- if (fd == -1) {
- perror("cannot open fb0");
- return NULL;
+GRSurface* MinuiBackendFbdev::Init() {
+ int fd = open("/dev/graphics/fb0", O_RDWR);
+ if (fd == -1) {
+ perror("cannot open fb0");
+ return nullptr;
+ }
+
+ fb_fix_screeninfo fi;
+ if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
+ perror("failed to get fb0 info");
+ close(fd);
+ return nullptr;
+ }
+
+ if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
+ perror("failed to get fb0 info");
+ close(fd);
+ return nullptr;
+ }
+
+ // We print this out for informational purposes only, but
+ // throughout we assume that the framebuffer device uses an RGBX
+ // pixel format. This is the case for every development device I
+ // have access to. For some of those devices (eg, hammerhead aka
+ // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a
+ // different format (XBGR) but actually produces the correct
+ // results on the display when you write RGBX.
+ //
+ // If you have a device that actually *needs* another pixel format
+ // (ie, BGRX, or 565), patches welcome...
+
+ printf(
+ "fb0 reports (possibly inaccurate):\n"
+ " vi.bits_per_pixel = %d\n"
+ " vi.red.offset = %3d .length = %3d\n"
+ " vi.green.offset = %3d .length = %3d\n"
+ " vi.blue.offset = %3d .length = %3d\n",
+ vi.bits_per_pixel, vi.red.offset, vi.red.length, vi.green.offset, vi.green.length,
+ vi.blue.offset, vi.blue.length);
+
+ void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (bits == MAP_FAILED) {
+ perror("failed to mmap framebuffer");
+ close(fd);
+ return nullptr;
+ }
+
+ memset(bits, 0, fi.smem_len);
+
+ gr_framebuffer[0].width = vi.xres;
+ gr_framebuffer[0].height = vi.yres;
+ gr_framebuffer[0].row_bytes = fi.line_length;
+ gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8;
+ gr_framebuffer[0].data = static_cast<uint8_t*>(bits);
+ memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes);
+
+ /* check if we can use double buffering */
+ if (vi.yres * fi.line_length * 2 <= fi.smem_len) {
+ double_buffered = true;
+
+ memcpy(gr_framebuffer + 1, gr_framebuffer, sizeof(GRSurface));
+ gr_framebuffer[1].data =
+ gr_framebuffer[0].data + gr_framebuffer[0].height * gr_framebuffer[0].row_bytes;
+
+ gr_draw = gr_framebuffer + 1;
+
+ } else {
+ double_buffered = false;
+
+ // Without double-buffering, we allocate RAM for a buffer to
+ // draw in, and then "flipping" the buffer consists of a
+ // memcpy from the buffer we allocated to the framebuffer.
+
+ gr_draw = static_cast<GRSurface*>(malloc(sizeof(GRSurface)));
+ memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface));
+ gr_draw->data = static_cast<unsigned char*>(malloc(gr_draw->height * gr_draw->row_bytes));
+ if (!gr_draw->data) {
+ perror("failed to allocate in-memory surface");
+ return nullptr;
}
+ }
- fb_fix_screeninfo fi;
- if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
- perror("failed to get fb0 info");
- close(fd);
- return NULL;
- }
+ memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes);
+ fb_fd = fd;
+ SetDisplayedFramebuffer(0);
- if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
- perror("failed to get fb0 info");
- close(fd);
- return NULL;
- }
+ printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height);
- // We print this out for informational purposes only, but
- // throughout we assume that the framebuffer device uses an RGBX
- // pixel format. This is the case for every development device I
- // have access to. For some of those devices (eg, hammerhead aka
- // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a
- // different format (XBGR) but actually produces the correct
- // results on the display when you write RGBX.
- //
- // If you have a device that actually *needs* another pixel format
- // (ie, BGRX, or 565), patches welcome...
+ Blank(true);
+ Blank(false);
- printf("fb0 reports (possibly inaccurate):\n"
- " vi.bits_per_pixel = %d\n"
- " vi.red.offset = %3d .length = %3d\n"
- " vi.green.offset = %3d .length = %3d\n"
- " vi.blue.offset = %3d .length = %3d\n",
- vi.bits_per_pixel,
- vi.red.offset, vi.red.length,
- vi.green.offset, vi.green.length,
- vi.blue.offset, vi.blue.length);
-
- void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (bits == MAP_FAILED) {
- perror("failed to mmap framebuffer");
- close(fd);
- return NULL;
- }
-
- memset(bits, 0, fi.smem_len);
-
- gr_framebuffer[0].width = vi.xres;
- gr_framebuffer[0].height = vi.yres;
- gr_framebuffer[0].row_bytes = fi.line_length;
- gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8;
- gr_framebuffer[0].data = reinterpret_cast<uint8_t*>(bits);
- memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes);
-
- /* check if we can use double buffering */
- if (vi.yres * fi.line_length * 2 <= fi.smem_len) {
- double_buffered = true;
-
- memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface));
- gr_framebuffer[1].data = gr_framebuffer[0].data +
- gr_framebuffer[0].height * gr_framebuffer[0].row_bytes;
-
- gr_draw = gr_framebuffer+1;
-
- } else {
- double_buffered = false;
-
- // Without double-buffering, we allocate RAM for a buffer to
- // draw in, and then "flipping" the buffer consists of a
- // memcpy from the buffer we allocated to the framebuffer.
-
- gr_draw = (GRSurface*) malloc(sizeof(GRSurface));
- memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface));
- gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes);
- if (!gr_draw->data) {
- perror("failed to allocate in-memory surface");
- return NULL;
- }
- }
-
- memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes);
- fb_fd = fd;
- set_displayed_framebuffer(0);
-
- printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height);
-
- fbdev_blank(backend, true);
- fbdev_blank(backend, false);
-
- return gr_draw;
+ return gr_draw;
}
-static GRSurface* fbdev_flip(minui_backend* backend __unused) {
- if (double_buffered) {
- // Change gr_draw to point to the buffer currently displayed,
- // then flip the driver so we're displaying the other buffer
- // instead.
- gr_draw = gr_framebuffer + displayed_buffer;
- set_displayed_framebuffer(1-displayed_buffer);
- } else {
- // Copy from the in-memory surface to the framebuffer.
- memcpy(gr_framebuffer[0].data, gr_draw->data,
- gr_draw->height * gr_draw->row_bytes);
- }
- return gr_draw;
+GRSurface* MinuiBackendFbdev::Flip() {
+ if (double_buffered) {
+ // Change gr_draw to point to the buffer currently displayed,
+ // then flip the driver so we're displaying the other buffer
+ // instead.
+ gr_draw = gr_framebuffer + displayed_buffer;
+ SetDisplayedFramebuffer(1 - displayed_buffer);
+ } else {
+ // Copy from the in-memory surface to the framebuffer.
+ memcpy(gr_framebuffer[0].data, gr_draw->data, gr_draw->height * gr_draw->row_bytes);
+ }
+ return gr_draw;
}
-static void fbdev_exit(minui_backend* backend __unused) {
- close(fb_fd);
- fb_fd = -1;
+MinuiBackendFbdev::~MinuiBackendFbdev() {
+ close(fb_fd);
+ fb_fd = -1;
- if (!double_buffered && gr_draw) {
- free(gr_draw->data);
- free(gr_draw);
- }
- gr_draw = NULL;
+ if (!double_buffered && gr_draw) {
+ free(gr_draw->data);
+ free(gr_draw);
+ }
+ gr_draw = nullptr;
}
diff --git a/minui/graphics_fbdev.h b/minui/graphics_fbdev.h
new file mode 100644
index 0000000..107e195
--- /dev/null
+++ b/minui/graphics_fbdev.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GRAPHICS_FBDEV_H_
+#define _GRAPHICS_FBDEV_H_
+
+#include <linux/fb.h>
+
+#include "graphics.h"
+#include "minui/minui.h"
+
+class MinuiBackendFbdev : public MinuiBackend {
+ public:
+ GRSurface* Init() override;
+ GRSurface* Flip() override;
+ void Blank(bool) override;
+ ~MinuiBackendFbdev() override;
+ MinuiBackendFbdev();
+
+ private:
+ void SetDisplayedFramebuffer(unsigned n);
+
+ GRSurface gr_framebuffer[2];
+ bool double_buffered;
+ GRSurface* gr_draw;
+ int displayed_buffer;
+ fb_var_screeninfo vi;
+ int fb_fd;
+};
+
+#endif // _GRAPHICS_FBDEV_H_
diff --git a/minui/graphics_overlay.cpp b/minui/graphics_overlay.cpp
index 978a3bb..39ec5e4 100644
--- a/minui/graphics_overlay.cpp
+++ b/minui/graphics_overlay.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "graphics_overlay.h"
+
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
@@ -34,50 +36,28 @@
#ifdef MSM_BSP
#include <linux/msm_mdp.h>
#include <linux/msm_ion.h>
+#else
+#define MSMFB_NEW_REQUEST 0
#endif
-#include "minui.h"
-#include "graphics.h"
+#include "minui/minui.h"
#define MDP_V4_0 400
#define MAX_DISPLAY_DIM 2048
-
-static GRSurface* overlay_init(minui_backend*);
-static GRSurface* overlay_flip(minui_backend*);
-static void overlay_blank(minui_backend*, bool);
-static void overlay_exit(minui_backend*);
-
-static GRSurface gr_framebuffer[2];
-static bool double_buffered;
-static GRSurface* gr_draw = NULL;
-static int displayed_buffer;
-
-static fb_var_screeninfo vi;
-static int fb_fd = -1;
-static bool isMDP5 = false;
-static int leftSplit = 0;
-static int rightSplit = 0;
#define ALIGN(x, align) (((x) + ((align)-1)) & ~((align)-1))
-static size_t frame_size = 0;
+MinuiBackendOverlay::MinuiBackendOverlay() :
+ gr_draw(nullptr),
+ fb_fd(-1),
+ isMDP5(false),
+ leftSplit(0),
+ rightSplit(0),
+ frame_size(0),
+ overlayL_id(MSMFB_NEW_REQUEST),
+ overlayR_id(MSMFB_NEW_REQUEST) {}
#ifdef MSM_BSP
-typedef struct {
- unsigned char *mem_buf;
- int size;
- int ion_fd;
- int mem_fd;
- struct ion_handle_data handle_data;
-} memInfo;
-
-//Left and right overlay id
-static int overlayL_id = MSMFB_NEW_REQUEST;
-static int overlayR_id = MSMFB_NEW_REQUEST;
-
-static memInfo mem_info;
-#
-
-static int map_mdp_pixel_format()
+int MinuiBackendOverlay::map_mdp_pixel_format()
{
int format = MDP_RGB_565;
#if defined(RECOVERY_BGRA)
@@ -91,66 +71,49 @@
}
#endif // MSM_BSP
-static minui_backend my_backend = {
- .init = overlay_init,
- .flip = overlay_flip,
- .blank = overlay_blank,
- .exit = overlay_exit,
-};
-
-bool target_has_overlay(char *version)
+bool MinuiBackendOverlay::target_has_overlay()
{
int ret;
int mdp_version;
bool overlay_supported = false;
-
- if (strlen(version) >= 8) {
- if(!strncmp(version, "msmfb", strlen("msmfb"))) {
- char str_ver[4];
- memcpy(str_ver, version + strlen("msmfb"), 3);
- str_ver[3] = '\0';
- mdp_version = atoi(str_ver);
- if (mdp_version >= MDP_V4_0) {
- overlay_supported = true;
- }
- } else if (!strncmp(version, "mdssfb", strlen("mdssfb"))) {
- overlay_supported = true;
- isMDP5 = true;
- }
- }
-
- return overlay_supported;
-}
-
-minui_backend* open_overlay() {
fb_fix_screeninfo fi;
int fd;
fd = open("/dev/graphics/fb0", O_RDWR);
if (fd < 0) {
perror("open_overlay cannot open fb0");
- return NULL;
+ return false;
}
if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
perror("failed to get fb0 info");
close(fd);
- return NULL;
- }
-
- if (target_has_overlay(fi.id)) {
-#ifdef MSM_BSP
- close(fd);
- return &my_backend;
-#else
- printf("Overlay graphics may work (%s), but not enabled. Use TW_TARGET_USES_QCOM_BSP := true to enable.\n", fi.id);
-#endif
+ return false;
}
close(fd);
- return NULL;
+
+ if (strlen(fi.id) >= 8) {
+ if(!strncmp(fi.id, "msmfb", strlen("msmfb"))) {
+ char str_ver[4];
+ memcpy(str_ver, fi.id + strlen("msmfb"), 3);
+ str_ver[3] = '\0';
+ mdp_version = atoi(str_ver);
+ if (mdp_version >= MDP_V4_0) {
+ overlay_supported = true;
+ }
+ } else if (!strncmp(fi.id, "mdssfb", strlen("mdssfb"))) {
+ overlay_supported = true;
+ isMDP5 = true;
+ }
+ }
+#ifndef MSM_BSP
+ if (overlay_supported)
+ printf("Overlay graphics may work (%s), but not enabled. Use TW_TARGET_USES_QCOM_BSP := true to enable.\n", fi.id);
+#endif
+ return overlay_supported;
}
-static void overlay_blank(minui_backend* backend __unused, bool blank)
+void MinuiBackendOverlay::Blank(bool blank)
{
#if defined(TW_NO_SCREEN_BLANK) && defined(TW_BRIGHTNESS_PATH) && defined(TW_MAX_BRIGHTNESS)
int fd;
@@ -173,7 +136,7 @@
#endif
}
-static void set_displayed_framebuffer(unsigned n)
+void MinuiBackendOverlay::SetDisplayedFramebuffer(unsigned n)
{
if (n > 1 || !double_buffered) return;
@@ -187,7 +150,7 @@
}
#ifdef MSM_BSP
-void setDisplaySplit(void) {
+void MinuiBackendOverlay::setDisplaySplit(void) {
char split[64] = {0};
if (!isMDP5)
return;
@@ -209,7 +172,7 @@
fclose(fp);
}
-int getLeftSplit(void) {
+int MinuiBackendOverlay::getLeftSplit(void) {
//Default even split for all displays with high res
int lSplit = vi.xres / 2;
@@ -220,11 +183,11 @@
return lSplit;
}
-int getRightSplit(void) {
+int MinuiBackendOverlay::getRightSplit(void) {
return rightSplit;
}
-int free_ion_mem(void) {
+int MinuiBackendOverlay::free_ion_mem(void) {
int ret = 0;
if (mem_info.mem_buf)
@@ -247,7 +210,7 @@
return 0;
}
-int alloc_ion_mem(unsigned int size)
+int MinuiBackendOverlay::alloc_ion_mem(unsigned int size)
{
int result;
struct ion_fd_data fd_data;
@@ -298,7 +261,7 @@
return 0;
}
-bool isDisplaySplit(void) {
+bool MinuiBackendOverlay::isDisplaySplit(void) {
if (vi.xres > MAX_DISPLAY_DIM)
return true;
//check if right split is set by driver
@@ -308,7 +271,7 @@
return false;
}
-int allocate_overlay(int fd, GRSurface gr_fb[])
+int MinuiBackendOverlay::allocate_overlay(int fd, GRSurface gr_fb[])
{
int ret = 0;
@@ -407,7 +370,7 @@
return 0;
}
-int overlay_display_frame(int fd, void* data, size_t size)
+int MinuiBackendOverlay::overlay_display_frame(int fd, void* data, size_t size)
{
int ret = 0;
struct msmfb_overlay_data ovdataL, ovdataR;
@@ -480,7 +443,7 @@
return ret;
}
-static GRSurface* overlay_flip(minui_backend* backend __unused) {
+GRSurface* MinuiBackendOverlay::Flip() {
if (double_buffered) {
#if defined(RECOVERY_BGRA)
// In case of BGRA, do some byte swapping
@@ -507,7 +470,7 @@
return gr_draw;
}
-int free_overlay(int fd)
+int MinuiBackendOverlay::free_overlay(int fd)
{
int ret = 0;
struct mdp_display_commit ext_commit;
@@ -557,7 +520,10 @@
return 0;
}
-static GRSurface* overlay_init(minui_backend* backend) {
+GRSurface* MinuiBackendOverlay::Init() {
+ if (!target_has_overlay())
+ return NULL;
+
int fd = open("/dev/graphics/fb0", O_RDWR);
if (fd == -1) {
perror("cannot open fb0");
@@ -662,7 +628,7 @@
return gr_draw;
}
-static void overlay_exit(minui_backend* backend __unused) {
+MinuiBackendOverlay::~MinuiBackendOverlay() {
free_overlay(fb_fd);
free_ion_mem();
@@ -680,15 +646,17 @@
}
}
#else // MSM_BSP
-static GRSurface* overlay_flip(minui_backend* backend __unused) {
+
+GRSurface* MinuiBackendOverlay::Flip() {
return NULL;
}
-static GRSurface* overlay_init(minui_backend* backend __unused) {
+GRSurface* MinuiBackendOverlay::Init() {
+ target_has_overlay(); // Don't care about return value, just for logging
return NULL;
}
-static void overlay_exit(minui_backend* backend __unused) {
+MinuiBackendOverlay::~MinuiBackendOverlay() {
return;
}
#endif // MSM_BSP
diff --git a/minui/graphics_overlay.h b/minui/graphics_overlay.h
new file mode 100644
index 0000000..56f7401
--- /dev/null
+++ b/minui/graphics_overlay.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GRAPHICS_OVERLAY_H_
+#define _GRAPHICS_OVERLAY_H_
+
+#include <linux/fb.h>
+
+#include "graphics.h"
+#include "minui/minui.h"
+
+#ifdef MSM_BSP
+typedef struct {
+ unsigned char *mem_buf;
+ int size;
+ int ion_fd;
+ int mem_fd;
+ struct ion_handle_data handle_data;
+} memInfo;
+#endif
+
+class MinuiBackendOverlay : public MinuiBackend {
+ public:
+ GRSurface* Init() override;
+ GRSurface* Flip() override;
+ void Blank(bool) override;
+ ~MinuiBackendOverlay() override;
+ MinuiBackendOverlay();
+
+ private:
+ void SetDisplayedFramebuffer(unsigned n);
+ bool target_has_overlay();
+
+#ifdef MSM_BSP
+ int map_mdp_pixel_format();
+ void setDisplaySplit(void);
+ int getLeftSplit(void);
+ int getRightSplit(void);
+ int free_ion_mem(void);
+ int alloc_ion_mem(unsigned int size);
+ bool isDisplaySplit(void);
+ int allocate_overlay(int fd, GRSurface gr_fb[]);
+ int overlay_display_frame(int fd, void* data, size_t size);
+ int free_overlay(int fd);
+#endif
+
+ GRSurface gr_framebuffer[2];
+ bool double_buffered;
+ GRSurface* gr_draw;
+ int displayed_buffer;
+ fb_var_screeninfo vi;
+ int fb_fd;
+ bool isMDP5;
+ int leftSplit;
+ int rightSplit;
+ size_t frame_size;
+ int overlayL_id;
+ int overlayR_id;
+};
+
+#endif // _GRAPHICS_OVERLAY_H_
diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h
new file mode 100644
index 0000000..766b943
--- /dev/null
+++ b/minui/include/minui/minui.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MINUI_H_
+#define _MINUI_H_
+
+#ifndef TW_USE_MINUI_21
+
+#include <sys/types.h>
+
+#include <functional>
+#include <string>
+
+//
+// Graphics.
+//
+
+struct GRSurface {
+ int width;
+ int height;
+ int row_bytes;
+ int pixel_bytes;
+ unsigned char* data;
+};
+
+struct GRFont {
+ GRSurface* texture;
+ int char_width;
+ int char_height;
+};
+
+int gr_init();
+void gr_exit();
+
+int gr_fb_width();
+int gr_fb_height();
+
+void gr_flip();
+void gr_fb_blank(bool blank);
+
+void gr_clear(); // clear entire surface to current color
+void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
+void gr_fill(int x1, int y1, int x2, int y2);
+
+void gr_texticon(int x, int y, GRSurface* icon);
+#ifdef TW_NO_MINUI_CUSTOM_FONTS
+void gr_text(int x, int y, const char *s, bool bold);
+int gr_measure(const char *s);
+void gr_font_size(int *x, int *y);
+void gr_set_font(__attribute__ ((unused))const char* name);
+#else
+
+const GRFont* gr_sys_font();
+int gr_init_font(const char* name, GRFont** dest);
+void gr_text(const GRFont* font, int x, int y, const char *s, bool bold);
+int gr_measure(const GRFont* font, const char *s);
+void gr_font_size(const GRFont* font, int *x, int *y);
+#endif
+
+void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy);
+unsigned int gr_get_width(GRSurface* surface);
+unsigned int gr_get_height(GRSurface* surface);
+
+//
+// Input events.
+//
+
+struct input_event;
+
+#ifdef TW_USE_MINUI_WITH_DATA
+typedef int (*ev_callback)(int fd, uint32_t epevents, void* data);
+typedef int (*ev_set_key_callback)(int code, int value, void* data);
+
+int ev_init(ev_callback input_cb, void* data);
+int ev_add_fd(int fd, ev_callback cb, void* data);
+int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data);
+#else
+using ev_callback = std::function<int(int fd, uint32_t epevents)>;
+using ev_set_key_callback = std::function<int(int code, int value)>;
+
+int ev_init(ev_callback input_cb);
+int ev_add_fd(int fd, ev_callback cb);
+int ev_sync_key_state(const ev_set_key_callback& set_key_cb);
+#endif
+void ev_exit();
+void ev_iterate_available_keys(const std::function<void(int)>& f);
+
+// 'timeout' has the same semantics as poll(2).
+// 0 : don't block
+// < 0 : block forever
+// > 0 : block for 'timeout' milliseconds
+int ev_wait(int timeout);
+
+int ev_get_input(int fd, uint32_t epevents, input_event* ev);
+void ev_dispatch();
+int ev_get_epollfd();
+
+//
+// Resources
+//
+
+bool matches_locale(const std::string& prefix, const std::string& locale);
+
+// res_create_*_surface() functions return 0 if no error, else
+// negative.
+//
+// A "display" surface is one that is intended to be drawn to the
+// screen with gr_blit(). An "alpha" surface is a grayscale image
+// interpreted as an alpha mask used to render text in the current
+// color (with gr_text() or gr_texticon()).
+//
+// All these functions load PNG images from "/res/images/${name}.png".
+
+// Load a single display surface from a PNG image.
+int res_create_display_surface(const char* name, GRSurface** pSurface);
+
+// Load an array of display surfaces from a single PNG image. The PNG
+// 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,
+ int* fps, GRSurface*** pSurface);
+int res_create_multi_display_surface(const char* name, int* frames,
+ GRSurface*** pSurface);
+
+// Load a single alpha surface from a grayscale PNG image.
+int res_create_alpha_surface(const char* name, GRSurface** pSurface);
+
+// Load part of a grayscale PNG image that is the first match for the
+// given locale. The image is expected to be a composite of multiple
+// translations of the same text, with special added rows that encode
+// the subimages' size and intended locale in the pixel data. See
+// bootable/recovery/tools/recovery_l10n for an app that will generate
+// these specialized images from Android resources.
+int res_create_localized_alpha_surface(const char* name, const char* locale,
+ GRSurface** pSurface);
+
+// Free a surface allocated by any of the res_create_*_surface()
+// functions.
+void res_free_surface(GRSurface* surface);
+
+#else //ifndef TW_USE_MINUI_21
+
+// This the old minui21/minui.h for compatibility with building TWRP
+// in pre 6.0 trees.
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void* gr_surface;
+typedef unsigned short gr_pixel;
+
+int gr_init(void);
+void gr_exit(void);
+
+int gr_fb_width(void);
+int gr_fb_height(void);
+gr_pixel *gr_fb_data(void);
+void gr_flip(void);
+void gr_fb_blank(bool blank);
+
+void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
+void gr_fill(int x1, int y1, int x2, int y2);
+
+// system/core/charger uses different gr_print signatures in diferent
+// Android versions, either with or without int bold.
+int gr_text(int x, int y, const char *s, ...);
+int gr_text_impl(int x, int y, const char *s, int bold);
+
+ void gr_texticon(int x, int y, gr_surface icon);
+int gr_measure(const char *s);
+void gr_font_size(int *x, int *y);
+void gr_get_memory_surface(gr_surface);
+
+void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy);
+unsigned int gr_get_width(gr_surface surface);
+unsigned int gr_get_height(gr_surface surface);
+
+// input event structure, include <linux/input.h> for the definition.
+// see http://www.mjmwired.net/kernel/Documentation/input/ for info.
+struct input_event;
+
+typedef int (*ev_callback)(int fd, uint32_t epevents, void *data);
+typedef int (*ev_set_key_callback)(int code, int value, void *data);
+
+int ev_init(ev_callback input_cb, void *data);
+void ev_exit(void);
+int ev_add_fd(int fd, ev_callback cb, void *data);
+int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data);
+
+/* timeout has the same semantics as for poll
+ * 0 : don't block
+ * < 0 : block forever
+ * > 0 : block for 'timeout' milliseconds
+ */
+int ev_wait(int timeout);
+
+int ev_get_input(int fd, uint32_t epevents, struct input_event *ev);
+void ev_dispatch(void);
+int ev_get_epollfd(void);
+
+// Resources
+
+// Returns 0 if no error, else negative.
+int res_create_surface(const char* name, gr_surface* pSurface);
+
+// Load an array of display surfaces from a single PNG image. The PNG
+// 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, gr_surface** pSurface);
+
+int res_create_localized_surface(const char* name, gr_surface* pSurface);
+void res_free_surface(gr_surface surface);
+static inline int res_create_display_surface(const char* name, gr_surface* pSurface) {
+ return res_create_surface(name, pSurface);
+}
+
+// These are new graphics functions from 5.0 that were not available in
+// 4.4 that are required by charger and healthd
+void gr_clear();
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ifndef TW_USE_MINUI_21
+#endif // ifndef _MINUI_H_
diff --git a/minui/main.cpp b/minui/main.cpp
index 74b830e..13fee87 100644
--- a/minui/main.cpp
+++ b/minui/main.cpp
@@ -15,7 +15,7 @@
#include <time.h>
-#include "minui.h"
+#include <minui/minui.h>
#include "graphics.h"
int main() {
diff --git a/minui/minui.h b/minui/minui.h
index bf79601..766b943 100644
--- a/minui/minui.h
+++ b/minui/minui.h
@@ -22,6 +22,7 @@
#include <sys/types.h>
#include <functional>
+#include <string>
//
// Graphics.
@@ -79,15 +80,23 @@
struct input_event;
-// TODO: move these over to std::function.
+#ifdef TW_USE_MINUI_WITH_DATA
typedef int (*ev_callback)(int fd, uint32_t epevents, void* data);
typedef int (*ev_set_key_callback)(int code, int value, void* data);
int ev_init(ev_callback input_cb, void* data);
-void ev_exit();
int ev_add_fd(int fd, ev_callback cb, void* data);
-void ev_iterate_available_keys(std::function<void(int)> f);
int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data);
+#else
+using ev_callback = std::function<int(int fd, uint32_t epevents)>;
+using ev_set_key_callback = std::function<int(int code, int value)>;
+
+int ev_init(ev_callback input_cb);
+int ev_add_fd(int fd, ev_callback cb);
+int ev_sync_key_state(const ev_set_key_callback& set_key_cb);
+#endif
+void ev_exit();
+void ev_iterate_available_keys(const std::function<void(int)>& f);
// 'timeout' has the same semantics as poll(2).
// 0 : don't block
@@ -103,7 +112,7 @@
// Resources
//
-bool matches_locale(const char* prefix, const char* locale);
+bool matches_locale(const std::string& prefix, const std::string& locale);
// res_create_*_surface() functions return 0 if no error, else
// negative.
@@ -134,8 +143,8 @@
// given locale. The image is expected to be a composite of multiple
// translations of the same text, with special added rows that encode
// the subimages' size and intended locale in the pixel data. See
-// development/tools/recovery_l10n for an app that will generate these
-// specialized images from Android resources.
+// bootable/recovery/tools/recovery_l10n for an app that will generate
+// these specialized images from Android resources.
int res_create_localized_alpha_surface(const char* name, const char* locale,
GRSurface** pSurface);
diff --git a/minui/resources.cpp b/minui/resources.cpp
index e25512f..026e1dc 100644
--- a/minui/resources.cpp
+++ b/minui/resources.cpp
@@ -14,29 +14,31 @@
* limitations under the License.
*/
+#include <fcntl.h>
+#include <linux/fb.h>
+#include <linux/kd.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
-
-#include <fcntl.h>
-#include <stdio.h>
-
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
+#include <unistd.h>
-#include <linux/fb.h>
-#include <linux/kd.h>
+#include <regex>
+#include <string>
+#include <vector>
+//#include <android-base/strings.h> // does not exist in 6.0
#include <png.h>
-#include "minui.h"
+#include "minui/minui.h"
#define SURFACE_DATA_ALIGNMENT 8
static GRSurface* malloc_surface(size_t data_size) {
size_t size = sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT;
- unsigned char* temp = reinterpret_cast<unsigned char*>(malloc(size));
+ unsigned char* temp = static_cast<unsigned char*>(malloc(size));
if (temp == NULL) return NULL;
GRSurface* surface = reinterpret_cast<GRSurface*>(temp);
surface->data = temp + sizeof(GRSurface) +
@@ -220,7 +222,7 @@
png_set_bgr(png_ptr);
#endif
- p_row = reinterpret_cast<unsigned char*>(malloc(width * 4));
+ p_row = static_cast<unsigned char*>(malloc(width * 4));
for (y = 0; y < height; ++y) {
png_read_row(png_ptr, p_row, NULL);
transform_rgb_to_draw(p_row, surface->data + y * surface->row_bytes, channels, width);
@@ -280,7 +282,7 @@
goto exit;
}
- surface = reinterpret_cast<GRSurface**>(malloc(*frames * sizeof(GRSurface*)));
+ surface = static_cast<GRSurface**>(calloc(*frames, sizeof(GRSurface*)));
if (surface == NULL) {
result = -8;
goto exit;
@@ -297,7 +299,7 @@
png_set_bgr(png_ptr);
#endif
- p_row = reinterpret_cast<unsigned char*>(malloc(width * 4));
+ p_row = static_cast<unsigned char*>(malloc(width * 4));
for (y = 0; y < height; ++y) {
png_read_row(png_ptr, p_row, NULL);
int frame = y % *frames;
@@ -307,7 +309,7 @@
}
free(p_row);
- *pSurface = reinterpret_cast<GRSurface**>(surface);
+ *pSurface = surface;
exit:
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
@@ -315,7 +317,7 @@
if (result < 0) {
if (surface) {
for (int i = 0; i < *frames; ++i) {
- if (surface[i]) free(surface[i]);
+ free(surface[i]);
}
free(surface);
}
@@ -378,14 +380,27 @@
// 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;
+bool matches_locale(const std::string& prefix, const std::string& locale) {
+ // According to the BCP 47 format, A locale string may consists of:
+ // language-{extlang}-{script}-{region}-{variant}
+ // The locale headers in PNG mostly consist of language-{region} except for sr-Latn, and some
+ // android's system locale can have the format language-{script}-{region}.
- // 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".
+ // Return true if the whole string of prefix matches the top part of locale. Otherwise try to
+ // match the locale string without the {script} section.
+ // For instance, prefix == "en" matches locale == "en-US", prefix == "sr-Latn" matches locale
+ // == "sr-Latn-BA", and prefix == "zh-CN" matches locale == "zh-Hans-CN".
+ //if (android::base::StartsWith(locale, prefix.c_str())) { // does not exist in 6.0
+ if (strncmp(prefix.c_str(), locale.c_str(), prefix.length()) == 0) {
+ return true;
+ }
- return (strncmp(prefix, locale, strlen(prefix)) == 0);
+ size_t separator = prefix.find('-');
+ if (separator == std::string::npos) {
+ return false;
+ }
+ std::regex loc_regex(prefix.substr(0, separator) + "-[A-Za-z]*" + prefix.substr(separator));
+ return std::regex_match(locale, loc_regex);
}
int res_create_localized_alpha_surface(const char* name,
@@ -397,18 +412,13 @@
png_infop info_ptr = NULL;
png_uint_32 width, height;
png_byte channels;
- unsigned char* row;
png_uint_32 y;
+ std::vector<unsigned char> row;
*pSurface = NULL;
if (locale == NULL) {
- surface = malloc_surface(0);
- surface->width = 0;
- surface->height = 0;
- surface->row_bytes = 0;
- surface->pixel_bytes = 1;
- goto exit;
+ return result;
}
result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
@@ -419,13 +429,13 @@
goto exit;
}
- row = reinterpret_cast<unsigned char*>(malloc(width));
+ row.resize(width);
for (y = 0; y < height; ++y) {
- png_read_row(png_ptr, row, NULL);
+ png_read_row(png_ptr, row.data(), NULL);
int w = (row[1] << 8) | row[0];
int h = (row[3] << 8) | row[2];
- int len = row[4];
- char* loc = (char*)row+5;
+ __unused int len = row[4];
+ char* loc = reinterpret_cast<char*>(&row[5]);
if (y+1+h >= height || matches_locale(loc, locale)) {
printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
@@ -442,16 +452,16 @@
int i;
for (i = 0; i < h; ++i, ++y) {
- png_read_row(png_ptr, row, NULL);
- memcpy(surface->data + i*w, row, w);
+ png_read_row(png_ptr, row.data(), NULL);
+ memcpy(surface->data + i*w, row.data(), w);
}
- *pSurface = reinterpret_cast<GRSurface*>(surface);
+ *pSurface = surface;
break;
} else {
int i;
for (i = 0; i < h; ++i, ++y) {
- png_read_row(png_ptr, row, NULL);
+ png_read_row(png_ptr, row.data(), NULL);
}
}
}
diff --git a/minuitwrp/Android.mk b/minuitwrp/Android.mk
index 054dd45..3f83c97 100644
--- a/minuitwrp/Android.mk
+++ b/minuitwrp/Android.mk
@@ -180,6 +180,9 @@
LOCAL_SHARED_LIBRARIES += libjpeg
endif
LOCAL_STATIC_LIBRARIES += libpixelflinger_twrp
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 25; echo $$?),0)
+LOCAL_SHARED_LIBRARIES += libcutils liblog libutils
+endif
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE := libminuitwrp
diff --git a/mounts.cpp b/mounts.cpp
new file mode 100644
index 0000000..f23376b
--- /dev/null
+++ b/mounts.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mounts.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+
+#include <string>
+#include <vector>
+
+struct MountedVolume {
+ std::string device;
+ std::string mount_point;
+ std::string filesystem;
+ std::string flags;
+};
+
+std::vector<MountedVolume*> g_mounts_state;
+
+bool scan_mounted_volumes() {
+ for (size_t i = 0; i < g_mounts_state.size(); ++i) {
+ delete g_mounts_state[i];
+ }
+ g_mounts_state.clear();
+
+ // Open and read mount table entries.
+ FILE* fp = setmntent("/proc/mounts", "re");
+ if (fp == NULL) {
+ return false;
+ }
+ mntent* e;
+ while ((e = getmntent(fp)) != NULL) {
+ MountedVolume* v = new MountedVolume;
+ v->device = e->mnt_fsname;
+ v->mount_point = e->mnt_dir;
+ v->filesystem = e->mnt_type;
+ v->flags = e->mnt_opts;
+ g_mounts_state.push_back(v);
+ }
+ endmntent(fp);
+ return true;
+}
+
+MountedVolume* find_mounted_volume_by_device(const char* device) {
+ for (size_t i = 0; i < g_mounts_state.size(); ++i) {
+ if (g_mounts_state[i]->device == device) return g_mounts_state[i];
+ }
+ return nullptr;
+}
+
+MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point) {
+ for (size_t i = 0; i < g_mounts_state.size(); ++i) {
+ if (g_mounts_state[i]->mount_point == mount_point) return g_mounts_state[i];
+ }
+ return nullptr;
+}
+
+int unmount_mounted_volume(MountedVolume* volume) {
+ // Intentionally pass the empty string to umount if the caller tries
+ // to unmount a volume they already unmounted using this
+ // function.
+ std::string mount_point = volume->mount_point;
+ volume->mount_point.clear();
+ return umount(mount_point.c_str());
+}
+
+int remount_read_only(MountedVolume* volume) {
+ return mount(volume->device.c_str(), volume->mount_point.c_str(), volume->filesystem.c_str(),
+ MS_NOATIME | MS_NODEV | MS_NODIRATIME | MS_RDONLY | MS_REMOUNT, 0);
+}
diff --git a/mounts.h b/mounts.h
index ed7fb5f..1b76703 100644
--- a/mounts.h
+++ b/mounts.h
@@ -14,25 +14,19 @@
* limitations under the License.
*/
-#ifndef MTDUTILS_MOUNTS_H_
-#define MTDUTILS_MOUNTS_H_
+#ifndef MOUNTS_H_
+#define MOUNTS_H_
-typedef struct {
- const char *device;
- const char *mount_point;
- const char *filesystem;
- const char *flags;
-} MountedVolume;
+struct MountedVolume;
-int scan_mounted_volumes(void);
+bool scan_mounted_volumes();
-const MountedVolume *find_mounted_volume_by_device(const char *device);
+MountedVolume* find_mounted_volume_by_device(const char* device);
-const MountedVolume *
-find_mounted_volume_by_mount_point(const char *mount_point);
+MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point);
-int unmount_mounted_volume(const MountedVolume *volume);
+int unmount_mounted_volume(MountedVolume* volume);
-int remount_read_only(const MountedVolume* volume);
+int remount_read_only(MountedVolume* volume);
-#endif // MTDUTILS_MOUNTS_H_
+#endif
diff --git a/mounts.h~HEAD b/mounts.h~HEAD
new file mode 100644
index 0000000..ed7fb5f
--- /dev/null
+++ b/mounts.h~HEAD
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MTDUTILS_MOUNTS_H_
+#define MTDUTILS_MOUNTS_H_
+
+typedef struct {
+ const char *device;
+ const char *mount_point;
+ const char *filesystem;
+ const char *flags;
+} MountedVolume;
+
+int scan_mounted_volumes(void);
+
+const MountedVolume *find_mounted_volume_by_device(const char *device);
+
+const MountedVolume *
+find_mounted_volume_by_mount_point(const char *mount_point);
+
+int unmount_mounted_volume(const MountedVolume *volume);
+
+int remount_read_only(const MountedVolume* volume);
+
+#endif // MTDUTILS_MOUNTS_H_
diff --git a/mtdutils/flash_image.c b/mtdutils/flash_image.c
deleted file mode 100644
index 36ffa13..0000000
--- a/mtdutils/flash_image.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "cutils/log.h"
-#include "mtdutils.h"
-
-#ifdef LOG_TAG
-#undef LOG_TAG
-#endif
-#define LOG_TAG "flash_image"
-
-#define HEADER_SIZE 2048 // size of header to compare for equality
-
-void die(const char *msg, ...) {
- int err = errno;
- va_list args;
- va_start(args, msg);
- char buf[1024];
- vsnprintf(buf, sizeof(buf), msg, args);
- va_end(args);
-
- if (err != 0) {
- strlcat(buf, ": ", sizeof(buf));
- strlcat(buf, strerror(err), sizeof(buf));
- }
-
- fprintf(stderr, "%s\n", buf);
- ALOGE("%s\n", buf);
- exit(1);
-}
-
-/* Read an image file and write it to a flash partition. */
-
-int main(int argc, char **argv) {
- const MtdPartition *ptn;
- MtdWriteContext *write;
- void *data;
- unsigned sz;
-
- if (argc != 3) {
- fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
- return 2;
- }
-
- if (mtd_scan_partitions() <= 0) die("error scanning partitions");
- const MtdPartition *partition = mtd_find_partition_by_name(argv[1]);
- if (partition == NULL) die("can't find %s partition", argv[1]);
-
- // If the first part of the file matches the partition, skip writing
-
- int fd = open(argv[2], O_RDONLY);
- if (fd < 0) die("error opening %s", argv[2]);
-
- char header[HEADER_SIZE];
- int headerlen = TEMP_FAILURE_RETRY(read(fd, header, sizeof(header)));
- if (headerlen <= 0) die("error reading %s header", argv[2]);
-
- MtdReadContext *in = mtd_read_partition(partition);
- if (in == NULL) {
- ALOGW("error opening %s: %s\n", argv[1], strerror(errno));
- // just assume it needs re-writing
- } else {
- char check[HEADER_SIZE];
- int checklen = mtd_read_data(in, check, sizeof(check));
- if (checklen <= 0) {
- ALOGW("error reading %s: %s\n", argv[1], strerror(errno));
- // just assume it needs re-writing
- } else if (checklen == headerlen && !memcmp(header, check, headerlen)) {
- ALOGI("header is the same, not flashing %s\n", argv[1]);
- return 0;
- }
- mtd_read_close(in);
- }
-
- // Skip the header (we'll come back to it), write everything else
- ALOGI("flashing %s from %s\n", argv[1], argv[2]);
-
- MtdWriteContext *out = mtd_write_partition(partition);
- if (out == NULL) die("error writing %s", argv[1]);
-
- char buf[HEADER_SIZE];
- memset(buf, 0, headerlen);
- int wrote = mtd_write_data(out, buf, headerlen);
- if (wrote != headerlen) die("error writing %s", argv[1]);
-
- int len;
- while ((len = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf)))) > 0) {
- wrote = mtd_write_data(out, buf, len);
- if (wrote != len) die("error writing %s", argv[1]);
- }
- if (len < 0) die("error reading %s", argv[2]);
-
- if (mtd_write_close(out)) die("error closing %s", argv[1]);
-
- // Now come back and write the header last
-
- out = mtd_write_partition(partition);
- if (out == NULL) die("error re-opening %s", argv[1]);
-
- wrote = mtd_write_data(out, header, headerlen);
- if (wrote != headerlen) die("error re-writing %s", argv[1]);
-
- // Need to write a complete block, so write the rest of the first block
- size_t block_size;
- if (mtd_partition_info(partition, NULL, &block_size, NULL))
- die("error getting %s block size", argv[1]);
-
- if (TEMP_FAILURE_RETRY(lseek(fd, headerlen, SEEK_SET)) != headerlen)
- die("error rewinding %s", argv[2]);
-
- int left = block_size - headerlen;
- while (left < 0) left += block_size;
- while (left > 0) {
- len = TEMP_FAILURE_RETRY(read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left));
- if (len <= 0) die("error reading %s", argv[2]);
- if (mtd_write_data(out, buf, len) != len)
- die("error writing %s", argv[1]);
- left -= len;
- }
-
- if (mtd_write_close(out)) die("error closing %s", argv[1]);
- return 0;
-}
diff --git a/mtdutils/mounts.h b/mtdutils/mounts.h
index d721355..ed7fb5f 100644
--- a/mtdutils/mounts.h
+++ b/mtdutils/mounts.h
@@ -17,11 +17,12 @@
#ifndef MTDUTILS_MOUNTS_H_
#define MTDUTILS_MOUNTS_H_
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct MountedVolume MountedVolume;
+typedef struct {
+ const char *device;
+ const char *mount_point;
+ const char *filesystem;
+ const char *flags;
+} MountedVolume;
int scan_mounted_volumes(void);
@@ -34,8 +35,4 @@
int remount_read_only(const MountedVolume* volume);
-#ifdef __cplusplus
-}
-#endif
-
#endif // MTDUTILS_MOUNTS_H_
diff --git a/openrecoveryscript.cpp b/openrecoveryscript.cpp
index 1cbf800..0235438 100644
--- a/openrecoveryscript.cpp
+++ b/openrecoveryscript.cpp
@@ -52,11 +52,10 @@
#include "gui/pages.hpp"
#include "orscmd/orscmd.h"
#include "adbbu/libtwadbbu.hpp"
+#include "twinstall.h"
extern "C" {
- #include "twinstall.h"
#include "gui/gui.h"
#include "cutils/properties.h"
- int TWinstall_zip(const char* path, int* wipe_cache);
}
OpenRecoveryScript::VoidFunction OpenRecoveryScript::call_after_cli_command;
diff --git a/otafault/Android.mk b/otafault/Android.mk
index e8241f4..1898e17 100644
--- a/otafault/Android.mk
+++ b/otafault/Android.mk
@@ -23,11 +23,13 @@
include $(CLEAR_VARS)
otafault_static_libs := \
- libbase \
- libminzip \
+ libziparchive \
libz \
- libselinux
+ libselinux \
+ libbase \
+ liblog
+LOCAL_CFLAGS := -Werror
LOCAL_SRC_FILES := config.cpp ota_io.cpp
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE := libotafault
@@ -38,12 +40,15 @@
include $(BUILD_STATIC_LIBRARY)
+# otafault_test (static executable)
+# ===============================
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_CFLAGS := -Werror
LOCAL_C_INCLUDES := $(RECOVERY_PATH)
LOCAL_FORCE_STATIC_EXECUTABLE := true
diff --git a/otafault/config.cpp b/otafault/config.cpp
index b456739..8590833 100644
--- a/otafault/config.cpp
+++ b/otafault/config.cpp
@@ -21,38 +21,42 @@
#include <unistd.h>
#include <android-base/stringprintf.h>
+#include <ziparchive/zip_archive.h>
-#include "minzip/Zip.h"
#include "config.h"
#include "ota_io.h"
#define OTAIO_MAX_FNAME_SIZE 128
-static ZipArchive* archive;
+static ZipArchiveHandle archive;
+static bool is_retry = false;
static std::map<std::string, bool> 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) {
+void ota_io_init(ZipArchiveHandle za, bool retry) {
archive = za;
+ is_retry = retry;
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) {
+ if (archive == nullptr || is_retry) {
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;
+ ZipString zip_type_path(type_path.c_str());
+ ZipEntry entry;
+ int status = FindEntry(archive, zip_type_path, &entry);
+ should_inject_cache[type_path] = (status == 0);
+ return (status == 0);
}
bool should_hit_cache() {
@@ -63,7 +67,9 @@
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);
+ ZipString zip_type_path(type_path.c_str());
+ ZipEntry entry;
+ int status = FindEntry(archive, zip_type_path, &entry);
+ ExtractToMemory(archive, &entry, reinterpret_cast<uint8_t*>(&fname[0]), OTAIO_MAX_FNAME_SIZE);
return fname;
}
diff --git a/otafault/config.h b/otafault/config.h
index 4430be3..4adbdd1 100644
--- a/otafault/config.h
+++ b/otafault/config.h
@@ -41,7 +41,7 @@
#include <stdbool.h>
-#include "minzip/Zip.h"
+#include <ziparchive/zip_archive.h>
#define OTAIO_BASE_DIR ".libotafault"
#define OTAIO_READ "READ"
@@ -52,7 +52,7 @@
/*
* Initialize libotafault by providing a reference to the OTA package.
*/
-void ota_io_init(ZipArchive* za);
+void ota_io_init(ZipArchiveHandle zip, bool retry);
/*
* Return true if a config file is present for the given IO type.
diff --git a/otafault/ota_io.cpp b/otafault/ota_io.cpp
index 0445853..3a89bb5 100644
--- a/otafault/ota_io.cpp
+++ b/otafault/ota_io.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <map>
+#include "ota_io.h"
#include <errno.h>
#include <fcntl.h>
@@ -22,15 +22,17 @@
#include <sys/stat.h>
#include <unistd.h>
+#include <map>
+#include <memory>
+
#include "config.h"
-#include "ota_io.h"
static std::map<intptr_t, const char*> 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) {
+static bool get_hit_file(const char* cached_path, const 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));
@@ -68,17 +70,33 @@
return fh;
}
-int ota_close(int fd) {
+static 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);
+void OtaCloser::Close(int fd) {
+ __ota_close(fd);
+}
+
+int ota_close(unique_fd& fd) {
+ return __ota_close(fd.release());
+}
+
+static int __ota_fclose(FILE* fh) {
+ filename_cache.erase(reinterpret_cast<intptr_t>(fh));
return fclose(fh);
}
+void OtaFcloser::operator()(FILE* f) const {
+ __ota_fclose(f);
+};
+
+int ota_fclose(unique_file& fh) {
+ return __ota_fclose(fh.release());
+}
+
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);
@@ -92,6 +110,7 @@
}
}
size_t status = fread(ptr, size, nitems, stream);
+ // If I/O error occurs, set the retry-update flag.
if (status != nitems && errno == EIO) {
have_eio_error = true;
}
diff --git a/otafault/ota_io.h b/otafault/ota_io.h
index 84187a7..9428f1b 100644
--- a/otafault/ota_io.h
+++ b/otafault/ota_io.h
@@ -26,6 +26,10 @@
#include <stdio.h>
#include <sys/stat.h>
+#include <memory>
+
+#include <android-base/unique_fd.h>
+
#define OTAIO_CACHE_FNAME "/cache/saved.file"
void ota_set_fault_files();
@@ -36,10 +40,6 @@
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);
@@ -50,4 +50,20 @@
int ota_fsync(int fd);
+struct OtaCloser {
+ static void Close(int);
+};
+
+using unique_fd = android::base::unique_fd_impl<OtaCloser>;
+
+int ota_close(unique_fd& fd);
+
+struct OtaFcloser {
+ void operator()(FILE*) const;
+};
+
+using unique_file = std::unique_ptr<FILE, OtaFcloser>;
+
+int ota_fclose(unique_file& fh);
+
#endif
diff --git a/otautil/Android.mk b/otautil/Android.mk
new file mode 100644
index 0000000..f7ca9a9
--- /dev/null
+++ b/otautil/Android.mk
@@ -0,0 +1,33 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ SysUtil.cpp \
+ DirUtil.cpp \
+ ZipUtil.cpp \
+ ThermalUtil.cpp
+
+LOCAL_STATIC_LIBRARIES := \
+ libselinux \
+ libbase
+
+LOCAL_MODULE := libotautil
+LOCAL_CFLAGS := \
+ -Werror \
+ -Wall
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/otautil/DirUtil.cpp b/otautil/DirUtil.cpp
new file mode 100644
index 0000000..e08e360
--- /dev/null
+++ b/otautil/DirUtil.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DirUtil.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <limits.h>
+
+#include <string>
+
+#include <selinux/label.h>
+#include <selinux/selinux.h>
+
+typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus;
+
+static DirStatus
+getPathDirStatus(const char *path)
+{
+ struct stat st;
+ int err;
+
+ err = stat(path, &st);
+ if (err == 0) {
+ /* Something's there; make sure it's a directory.
+ */
+ if (S_ISDIR(st.st_mode)) {
+ return DDIR;
+ }
+ errno = ENOTDIR;
+ return DILLEGAL;
+ } else if (errno != ENOENT) {
+ /* Something went wrong, or something in the path
+ * is bad. Can't do anything in this situation.
+ */
+ return DILLEGAL;
+ }
+ return DMISSING;
+}
+
+int
+dirCreateHierarchy(const char *path, int mode,
+ const struct utimbuf *timestamp, bool stripFileName,
+ struct selabel_handle *sehnd)
+{
+ DirStatus ds;
+
+ /* Check for an empty string before we bother
+ * making any syscalls.
+ */
+ if (path[0] == '\0') {
+ errno = ENOENT;
+ return -1;
+ }
+ // Allocate a path that we can modify; stick a slash on
+ // the end to make things easier.
+ std::string cpath = path;
+ if (stripFileName) {
+ // Strip everything after the last slash.
+ size_t pos = cpath.rfind('/');
+ if (pos == std::string::npos) {
+ errno = ENOENT;
+ return -1;
+ }
+ cpath.resize(pos + 1);
+ } else {
+ // Make sure that the path ends in a slash.
+ cpath.push_back('/');
+ }
+
+ /* See if it already exists.
+ */
+ ds = getPathDirStatus(cpath.c_str());
+ if (ds == DDIR) {
+ return 0;
+ } else if (ds == DILLEGAL) {
+ return -1;
+ }
+
+ /* Walk up the path from the root and make each level.
+ * If a directory already exists, no big deal.
+ */
+ const char *path_start = &cpath[0];
+ char *p = &cpath[0];
+ while (*p != '\0') {
+ /* Skip any slashes, watching out for the end of the string.
+ */
+ while (*p != '\0' && *p == '/') {
+ p++;
+ }
+ if (*p == '\0') {
+ break;
+ }
+
+ /* Find the end of the next path component.
+ * We know that we'll see a slash before the NUL,
+ * because we added it, above.
+ */
+ while (*p != '/') {
+ p++;
+ }
+ *p = '\0';
+
+ /* Check this part of the path and make a new directory
+ * if necessary.
+ */
+ ds = getPathDirStatus(path_start);
+ if (ds == DILLEGAL) {
+ /* Could happen if some other process/thread is
+ * messing with the filesystem.
+ */
+ return -1;
+ } else if (ds == DMISSING) {
+ int err;
+
+ char *secontext = NULL;
+
+ if (sehnd) {
+ selabel_lookup(sehnd, &secontext, path_start, mode);
+ setfscreatecon(secontext);
+ }
+
+ err = mkdir(path_start, mode);
+
+ if (secontext) {
+ freecon(secontext);
+ setfscreatecon(NULL);
+ }
+
+ if (err != 0) {
+ return -1;
+ }
+ if (timestamp != NULL && utime(path_start, timestamp)) {
+ return -1;
+ }
+ }
+ // else, this directory already exists.
+
+ // Repair the path and continue.
+ *p = '/';
+ }
+ return 0;
+}
+
+int
+dirUnlinkHierarchy(const char *path)
+{
+ struct stat st;
+ DIR *dir;
+ struct dirent *de;
+ int fail = 0;
+
+ /* is it a file or directory? */
+ if (lstat(path, &st) < 0) {
+ return -1;
+ }
+
+ /* a file, so unlink it */
+ if (!S_ISDIR(st.st_mode)) {
+ return unlink(path);
+ }
+
+ /* a directory, so open handle */
+ dir = opendir(path);
+ if (dir == NULL) {
+ return -1;
+ }
+
+ /* recurse over components */
+ errno = 0;
+ while ((de = readdir(dir)) != NULL) {
+ //TODO: don't blow the stack
+ char dn[PATH_MAX];
+ if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
+ continue;
+ }
+ snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
+ if (dirUnlinkHierarchy(dn) < 0) {
+ fail = 1;
+ break;
+ }
+ errno = 0;
+ }
+ /* in case readdir or unlink_recursive failed */
+ if (fail || errno < 0) {
+ int save = errno;
+ closedir(dir);
+ errno = save;
+ return -1;
+ }
+
+ /* close directory handle */
+ if (closedir(dir) < 0) {
+ return -1;
+ }
+
+ /* delete target directory */
+ return rmdir(path);
+}
diff --git a/otautil/DirUtil.h b/otautil/DirUtil.h
new file mode 100644
index 0000000..85b83c3
--- /dev/null
+++ b/otautil/DirUtil.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MINZIP_DIRUTIL_H_
+#define MINZIP_DIRUTIL_H_
+
+#include <stdbool.h>
+#include <utime.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct selabel_handle;
+
+/* Like "mkdir -p", try to guarantee that all directories
+ * specified in path are present, creating as many directories
+ * as necessary. The specified mode is passed to all mkdir
+ * calls; no modifications are made to umask.
+ *
+ * If stripFileName is set, everything after the final '/'
+ * is stripped before creating the directory hierarchy.
+ *
+ * If timestamp is non-NULL, new directories will be timestamped accordingly.
+ *
+ * Returns 0 on success; returns -1 (and sets errno) on failure
+ * (usually if some element of path is not a directory).
+ */
+int dirCreateHierarchy(const char *path, int mode,
+ const struct utimbuf *timestamp, bool stripFileName,
+ struct selabel_handle* sehnd);
+
+/* rm -rf <path>
+ */
+int dirUnlinkHierarchy(const char *path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MINZIP_DIRUTIL_H_
diff --git a/otautil/SysUtil.cpp b/otautil/SysUtil.cpp
new file mode 100644
index 0000000..a2133b9
--- /dev/null
+++ b/otautil/SysUtil.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2006 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 "SysUtil.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+static bool sysMapFD(int fd, MemMapping* pMap) {
+ CHECK(pMap != nullptr);
+
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ PLOG(ERROR) << "fstat(" << fd << ") failed";
+ return false;
+ }
+
+ void* memPtr = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (memPtr == MAP_FAILED) {
+ PLOG(ERROR) << "mmap(" << sb.st_size << ", R, PRIVATE, " << fd << ", 0) failed";
+ return false;
+ }
+
+ pMap->addr = static_cast<unsigned char*>(memPtr);
+ pMap->length = sb.st_size;
+ pMap->ranges.push_back({ memPtr, static_cast<size_t>(sb.st_size) });
+
+ return true;
+}
+
+// A "block map" which looks like this (from uncrypt/uncrypt.cpp):
+//
+// /dev/block/platform/msm_sdcc.1/by-name/userdata # block device
+// 49652 4096 # file size in bytes, block size
+// 3 # count of block ranges
+// 1000 1008 # block range 0
+// 2100 2102 # ... block range 1
+// 30 33 # ... block range 2
+//
+// Each block range represents a half-open interval; the line "30 33"
+// reprents the blocks [30, 31, 32].
+static int sysMapBlockFile(const char* filename, MemMapping* pMap) {
+ CHECK(pMap != nullptr);
+
+ std::string content;
+ if (!android::base::ReadFileToString(filename, &content)) {
+ PLOG(ERROR) << "Failed to read " << filename;
+ return -1;
+ }
+
+ std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n");
+ if (lines.size() < 4) {
+ LOG(ERROR) << "Block map file is too short: " << lines.size();
+ return -1;
+ }
+
+ size_t size;
+ unsigned int blksize;
+ if (sscanf(lines[1].c_str(), "%zu %u", &size, &blksize) != 2) {
+ LOG(ERROR) << "Failed to parse file size and block size: " << lines[1];
+ return -1;
+ }
+
+ size_t range_count;
+ if (sscanf(lines[2].c_str(), "%zu", &range_count) != 1) {
+ LOG(ERROR) << "Failed to parse block map header: " << lines[2];
+ return -1;
+ }
+
+ size_t blocks;
+ if (blksize != 0) {
+ blocks = ((size - 1) / blksize) + 1;
+ }
+ if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0 ||
+ lines.size() != 3 + range_count) {
+ LOG(ERROR) << "Invalid data in block map file: size " << size << ", blksize " << blksize
+ << ", range_count " << range_count << ", lines " << lines.size();
+ return -1;
+ }
+
+ // Reserve enough contiguous address space for the whole file.
+ void* reserve = mmap64(nullptr, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (reserve == MAP_FAILED) {
+ PLOG(ERROR) << "failed to reserve address space";
+ return -1;
+ }
+
+ const std::string& block_dev = lines[0];
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_dev.c_str(), O_RDONLY)));
+ if (fd == -1) {
+ PLOG(ERROR) << "failed to open block device " << block_dev;
+ munmap(reserve, blocks * blksize);
+ return -1;
+ }
+
+ pMap->ranges.resize(range_count);
+
+ unsigned char* next = static_cast<unsigned char*>(reserve);
+ size_t remaining_size = blocks * blksize;
+ bool success = true;
+ for (size_t i = 0; i < range_count; ++i) {
+ const std::string& line = lines[i + 3];
+
+ size_t start, end;
+ if (sscanf(line.c_str(), "%zu %zu\n", &start, &end) != 2) {
+ LOG(ERROR) << "failed to parse range " << i << " in block map: " << line;
+ success = false;
+ break;
+ }
+ size_t length = (end - start) * blksize;
+ if (end <= start || (end - start) > SIZE_MAX / blksize || length > remaining_size) {
+ LOG(ERROR) << "unexpected range in block map: " << start << " " << end;
+ success = false;
+ break;
+ }
+
+ void* addr = mmap64(next, length, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd,
+ static_cast<off64_t>(start) * blksize);
+ if (addr == MAP_FAILED) {
+ PLOG(ERROR) << "failed to map block " << i;
+ success = false;
+ break;
+ }
+ pMap->ranges[i].addr = addr;
+ pMap->ranges[i].length = length;
+
+ next += length;
+ remaining_size -= length;
+ }
+ if (success && remaining_size != 0) {
+ LOG(ERROR) << "ranges in block map are invalid: remaining_size = " << remaining_size;
+ success = false;
+ }
+ if (!success) {
+ munmap(reserve, blocks * blksize);
+ return -1;
+ }
+
+ pMap->addr = static_cast<unsigned char*>(reserve);
+ pMap->length = size;
+
+ LOG(INFO) << "mmapped " << range_count << " ranges";
+
+ return 0;
+}
+
+int sysMapFile(const char* fn, MemMapping* pMap) {
+ if (fn == nullptr || pMap == nullptr) {
+ LOG(ERROR) << "Invalid argument(s)";
+ return -1;
+ }
+
+ *pMap = {};
+
+ if (fn[0] == '@') {
+ if (sysMapBlockFile(fn + 1, pMap) != 0) {
+ LOG(ERROR) << "Map of '" << fn << "' failed";
+ return -1;
+ }
+ } else {
+ // This is a regular file.
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(fn, O_RDONLY)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Unable to open '" << fn << "'";
+ return -1;
+ }
+
+ if (!sysMapFD(fd, pMap)) {
+ LOG(ERROR) << "Map of '" << fn << "' failed";
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Release a memory mapping.
+ */
+void sysReleaseMap(MemMapping* pMap) {
+ std::for_each(pMap->ranges.cbegin(), pMap->ranges.cend(), [](const MappedRange& range) {
+ if (munmap(range.addr, range.length) == -1) {
+ PLOG(ERROR) << "munmap(" << range.addr << ", " << range.length << ") failed";
+ }
+ });
+ pMap->ranges.clear();
+}
diff --git a/otautil/SysUtil.h b/otautil/SysUtil.h
new file mode 100644
index 0000000..6a79bf3
--- /dev/null
+++ b/otautil/SysUtil.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2006 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 _OTAUTIL_SYSUTIL
+#define _OTAUTIL_SYSUTIL
+
+#include <sys/types.h>
+
+#include <vector>
+
+struct MappedRange {
+ void* addr;
+ size_t length;
+};
+
+/*
+ * Use this to keep track of mapped segments.
+ */
+struct MemMapping {
+ unsigned char* addr; /* start of data */
+ size_t length; /* length of data */
+
+ std::vector<MappedRange> ranges;
+};
+
+/*
+ * Map a file into a private, read-only memory segment. If 'fn'
+ * begins with an '@' character, it is a map of blocks to be mapped,
+ * otherwise it is treated as an ordinary file.
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysMapFile(const char* fn, MemMapping* pMap);
+
+/*
+ * Release the pages associated with a shared memory segment.
+ *
+ * This does not free "pMap"; it just releases the memory.
+ */
+void sysReleaseMap(MemMapping* pMap);
+
+#endif // _OTAUTIL_SYSUTIL
diff --git a/otautil/ThermalUtil.cpp b/otautil/ThermalUtil.cpp
new file mode 100644
index 0000000..13d3643
--- /dev/null
+++ b/otautil/ThermalUtil.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ThermalUtil.h"
+
+#include <dirent.h>
+#include <stdio.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+static constexpr auto THERMAL_PREFIX = "/sys/class/thermal/";
+
+static int thermal_filter(const dirent* de) {
+ if (android::base::StartsWith(de->d_name, "thermal_zone")) {
+ return 1;
+ }
+ return 0;
+}
+