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