aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk5
-rw-r--r--etc/init.rc2
-rw-r--r--install.cpp31
-rw-r--r--minui/events.c86
-rw-r--r--minui/minui.h5
-rw-r--r--minui/resources.c14
-rw-r--r--minzip/SysUtil.c239
-rw-r--r--minzip/SysUtil.h55
-rw-r--r--minzip/Zip.c121
-rw-r--r--minzip/Zip.h12
-rw-r--r--recovery.cpp2
-rw-r--r--roots.cpp37
-rw-r--r--screen_ui.cpp1
-rw-r--r--ui.cpp52
-rw-r--r--ui.h8
-rw-r--r--uncrypt/Android.mk28
-rw-r--r--uncrypt/uncrypt.c377
-rw-r--r--updater/updater.c15
-rw-r--r--verifier.cpp70
-rw-r--r--verifier.h9
-rw-r--r--verifier_test.cpp12
21 files changed, 814 insertions, 367 deletions
diff --git a/Android.mk b/Android.mk
index 3d61568..b1e3798 100644
--- a/Android.mk
+++ b/Android.mk
@@ -57,7 +57,7 @@ LOCAL_STATIC_LIBRARIES := \
ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
LOCAL_CFLAGS += -DUSE_EXT4
- LOCAL_C_INCLUDES += system/extras/ext4_utils
+ LOCAL_C_INCLUDES += system/extras/ext4_utils system/vold
LOCAL_STATIC_LIBRARIES += libext4_utils_static libz
endif
@@ -89,6 +89,7 @@ include $(CLEAR_VARS)
LOCAL_MODULE := verifier_test
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := tests
+LOCAL_CFLAGS += -DNO_RECOVERY_MOUNT
LOCAL_SRC_FILES := \
verifier_test.cpp \
asn1_decoder.cpp \
@@ -97,6 +98,7 @@ LOCAL_SRC_FILES := \
LOCAL_STATIC_LIBRARIES := \
libmincrypt \
libminui \
+ libminzip \
libcutils \
libstdc++ \
libc
@@ -111,5 +113,6 @@ include $(LOCAL_PATH)/minui/Android.mk \
$(LOCAL_PATH)/tests/Android.mk \
$(LOCAL_PATH)/tools/Android.mk \
$(LOCAL_PATH)/edify/Android.mk \
+ $(LOCAL_PATH)/uncrypt/Android.mk \
$(LOCAL_PATH)/updater/Android.mk \
$(LOCAL_PATH)/applypatch/Android.mk
diff --git a/etc/init.rc b/etc/init.rc
index 159747e..f16bef8 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -52,7 +52,7 @@ service ueventd /sbin/ueventd
critical
seclabel u:r:ueventd:s0
-service healthd /sbin/healthd -n
+service healthd /sbin/healthd -r
critical
seclabel u:r:healthd:s0
diff --git a/install.cpp b/install.cpp
index 797a525..0bd7945 100644
--- a/install.cpp
+++ b/install.cpp
@@ -120,6 +120,7 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) {
pid_t pid = fork();
if (pid == 0) {
+ umask(022);
close(pipefd[0]);
execv(binary, (char* const*)args);
fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno));
@@ -185,12 +186,22 @@ really_install_package(const char *path, int* wipe_cache)
ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);
LOGI("Update location: %s\n", path);
- if (ensure_path_mounted(path) != 0) {
- LOGE("Can't mount %s\n", path);
- return INSTALL_CORRUPT;
+ // Map the update package into memory.
+ ui->Print("Opening update package...\n");
+
+ if (path) {
+ if (path[0] == '@') {
+ ensure_path_mounted(path+1);
+ } else {
+ ensure_path_mounted(path);
+ }
}
- ui->Print("Opening update package...\n");
+ MemMapping map;
+ if (sysMapFile(path, &map) != 0) {
+ LOGE("failed to map file\n");
+ return INSTALL_CORRUPT;
+ }
int numKeys;
Certificate* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
@@ -203,27 +214,33 @@ really_install_package(const char *path, int* wipe_cache)
ui->Print("Verifying update package...\n");
int err;
- err = verify_file(path, loadedKeys, numKeys);
+ err = verify_file(map.addr, map.length, loadedKeys, numKeys);
free(loadedKeys);
LOGI("verify_file returned %d\n", err);
if (err != VERIFY_SUCCESS) {
LOGE("signature verification failed\n");
+ sysReleaseMap(&map);
return INSTALL_CORRUPT;
}
/* Try to open the package.
*/
ZipArchive zip;
- err = mzOpenZipArchive(path, &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");
+ sysReleaseMap(&map);
return INSTALL_CORRUPT;
}
/* Verify and install the contents of the package.
*/
ui->Print("Installing update...\n");
- return try_update_binary(path, &zip, wipe_cache);
+ int result = try_update_binary(path, &zip, wipe_cache);
+
+ sysReleaseMap(&map);
+
+ return result;
}
int
diff --git a/minui/events.c b/minui/events.c
index 2918afa..df7dad4 100644
--- a/minui/events.c
+++ b/minui/events.c
@@ -18,7 +18,7 @@
#include <stdlib.h>
#include <fcntl.h>
#include <dirent.h>
-#include <sys/poll.h>
+#include <sys/epoll.h>
#include <linux/input.h>
@@ -34,11 +34,15 @@
((array)[(bit)/BITS_PER_LONG] & (1 << ((bit) % BITS_PER_LONG)))
struct fd_info {
+ int fd;
ev_callback cb;
void *data;
};
-static struct pollfd ev_fds[MAX_DEVICES + MAX_MISC_FDS];
+static int epollfd;
+static struct epoll_event polledevents[MAX_DEVICES + MAX_MISC_FDS];
+static int npolledevents;
+
static struct fd_info ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS];
static unsigned ev_count = 0;
@@ -50,6 +54,12 @@ int ev_init(ev_callback input_cb, void *data)
DIR *dir;
struct dirent *de;
int fd;
+ struct epoll_event ev;
+ bool epollctlfail = false;
+
+ epollfd = epoll_create(MAX_DEVICES + MAX_MISC_FDS);
+ if (epollfd == -1)
+ return -1;
dir = opendir("/dev/input");
if(dir != 0) {
@@ -74,8 +84,15 @@ int ev_init(ev_callback input_cb, void *data)
continue;
}
- ev_fds[ev_count].fd = fd;
- ev_fds[ev_count].events = POLLIN;
+ ev.events = EPOLLIN | EPOLLWAKEUP;
+ ev.data.ptr = (void *)&ev_fdinfo[ev_count];
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev)) {
+ close(fd);
+ epollctlfail = true;
+ continue;
+ }
+
+ ev_fdinfo[ev_count].fd = fd;
ev_fdinfo[ev_count].cb = input_cb;
ev_fdinfo[ev_count].data = data;
ev_count++;
@@ -84,59 +101,78 @@ int ev_init(ev_callback input_cb, void *data)
}
}
+ if (epollctlfail && !ev_count) {
+ close(epollfd);
+ epollfd = -1;
+ return -1;
+ }
+
return 0;
}
int ev_add_fd(int fd, ev_callback cb, void *data)
{
+ struct epoll_event ev;
+ int ret;
+
if (ev_misc_count == MAX_MISC_FDS || cb == NULL)
return -1;
- ev_fds[ev_count].fd = fd;
- ev_fds[ev_count].events = POLLIN;
- ev_fdinfo[ev_count].cb = cb;
- ev_fdinfo[ev_count].data = data;
- ev_count++;
- ev_misc_count++;
- return 0;
+ ev.events = EPOLLIN | EPOLLWAKEUP;
+ ev.data.ptr = (void *)&ev_fdinfo[ev_count];
+ ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
+ if (!ret) {
+ ev_fdinfo[ev_count].fd = fd;
+ ev_fdinfo[ev_count].cb = cb;
+ ev_fdinfo[ev_count].data = data;
+ ev_count++;
+ ev_misc_count++;
+ }
+
+ return ret;
+}
+
+int ev_get_epollfd(void)
+{
+ return epollfd;
}
void ev_exit(void)
{
while (ev_count > 0) {
- close(ev_fds[--ev_count].fd);
+ close(ev_fdinfo[--ev_count].fd);
}
ev_misc_count = 0;
ev_dev_count = 0;
+ close(epollfd);
}
int ev_wait(int timeout)
{
- int r;
-
- r = poll(ev_fds, ev_count, timeout);
- if (r <= 0)
+ npolledevents = epoll_wait(epollfd, polledevents, ev_count, timeout);
+ if (npolledevents <= 0)
return -1;
return 0;
}
void ev_dispatch(void)
{
- unsigned n;
+ int n;
int ret;
- for (n = 0; n < ev_count; n++) {
- ev_callback cb = ev_fdinfo[n].cb;
- if (cb && (ev_fds[n].revents & ev_fds[n].events))
- cb(ev_fds[n].fd, ev_fds[n].revents, ev_fdinfo[n].data);
+ for (n = 0; n < npolledevents; n++) {
+ struct fd_info *fdi = polledevents[n].data.ptr;
+ ev_callback cb = fdi->cb;
+ if (cb)
+ cb(fdi->fd, polledevents[n].events, fdi->data);
}
}
-int ev_get_input(int fd, short revents, struct input_event *ev)
+int ev_get_input(int fd, uint32_t epevents, struct input_event *ev)
{
int r;
- if (revents & POLLIN) {
+ if (epevents & EPOLLIN) {
r = read(fd, ev, sizeof(*ev));
if (r == sizeof(*ev))
return 0;
@@ -157,11 +193,11 @@ int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data)
memset(key_bits, 0, sizeof(key_bits));
memset(ev_bits, 0, sizeof(ev_bits));
- ret = ioctl(ev_fds[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits);
+ ret = ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits);
if (ret < 0 || !test_bit(EV_KEY, ev_bits))
continue;
- ret = ioctl(ev_fds[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits);
+ ret = ioctl(ev_fdinfo[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits);
if (ret < 0)
continue;
diff --git a/minui/minui.h b/minui/minui.h
index 1b8dd05..573dd71 100644
--- a/minui/minui.h
+++ b/minui/minui.h
@@ -50,7 +50,7 @@ unsigned int gr_get_height(gr_surface surface);
// see http://www.mjmwired.net/kernel/Documentation/input/ for info.
struct input_event;
-typedef int (*ev_callback)(int fd, short revents, void *data);
+typedef int (*ev_callback)(int fd, uint32_t epevents, void *data);
typedef int (*ev_set_key_callback)(int code, int value, void *data);
int ev_init(ev_callback input_cb, void *data);
@@ -65,8 +65,9 @@ int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data);
*/
int ev_wait(int timeout);
-int ev_get_input(int fd, short revents, struct input_event *ev);
+int ev_get_input(int fd, uint32_t epevents, struct input_event *ev);
void ev_dispatch(void);
+int ev_get_epollfd(void);
// Resources
diff --git a/minui/resources.c b/minui/resources.c
index c0a9cca..b20c00a 100644
--- a/minui/resources.c
+++ b/minui/resources.c
@@ -123,10 +123,18 @@ int res_create_surface(const char* name, gr_surface* pSurface) {
surface->height = height;
surface->stride = width; /* Yes, pixels, not bytes */
surface->data = pData;
- surface->format = (channels == 3) ? GGL_PIXEL_FORMAT_RGBX_8888 :
- ((color_type == PNG_COLOR_TYPE_PALETTE ? GGL_PIXEL_FORMAT_RGBA_8888 : GGL_PIXEL_FORMAT_L_8));
- int alpha = 0;
+ if (channels == 3) {
+ surface->format = GGL_PIXEL_FORMAT_RGBX_8888;
+ } else if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ surface->format = GGL_PIXEL_FORMAT_RGBA_8888;
+ } else if (channels == 1) {
+ surface->format = GGL_PIXEL_FORMAT_L_8;
+ } else {
+ surface->format = GGL_PIXEL_FORMAT_RGBA_8888;
+ }
+
+ int alpha = (channels == 4);
if (color_type == PNG_COLOR_TYPE_PALETTE) {
png_set_palette_to_rgb(png_ptr);
}
diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c
index 31c76d6..c046a8c 100644
--- a/minzip/SysUtil.c
+++ b/minzip/SysUtil.c
@@ -8,42 +8,17 @@
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <limits.h>
#include <errno.h>
#include <assert.h>
-#define LOG_TAG "minzip"
+#define LOG_TAG "sysutil"
#include "Log.h"
#include "SysUtil.h"
-/*
- * Having trouble finding a portable way to get this. sysconf(_SC_PAGE_SIZE)
- * seems appropriate, but we don't have that on the device. Some systems
- * have getpagesize(2), though the linux man page has some odd cautions.
- */
-#define DEFAULT_PAGE_SIZE 4096
-
-
-/*
- * Create an anonymous shared memory segment large enough to hold "length"
- * bytes. The actual segment may be larger because mmap() operates on
- * page boundaries (usually 4K).
- */
-static void* sysCreateAnonShmem(size_t length)
-{
- void* ptr;
-
- ptr = mmap(NULL, length, PROT_READ | PROT_WRITE,
- MAP_SHARED | MAP_ANON, -1, 0);
- if (ptr == MAP_FAILED) {
- LOGW("mmap(%d, RW, SHARED|ANON) failed: %s\n", (int) length,
- strerror(errno));
- return NULL;
- }
-
- return ptr;
-}
-
static int getFileStartAndLength(int fd, off_t *start_, size_t *length_)
{
off_t start, end;
@@ -74,48 +49,13 @@ static int getFileStartAndLength(int fd, off_t *start_, size_t *length_)
}
/*
- * Pull the contents of a file into an new shared memory segment. We grab
- * everything from fd's current offset on.
- *
- * We need to know the length ahead of time so we can allocate a segment
- * of sufficient size.
- */
-int sysLoadFileInShmem(int fd, MemMapping* pMap)
-{
- off_t start;
- size_t length, actual;
- void* memPtr;
-
- assert(pMap != NULL);
-
- if (getFileStartAndLength(fd, &start, &length) < 0)
- return -1;
-
- memPtr = sysCreateAnonShmem(length);
- if (memPtr == NULL)
- return -1;
-
- pMap->baseAddr = pMap->addr = memPtr;
- pMap->baseLength = pMap->length = length;
-
- actual = TEMP_FAILURE_RETRY(read(fd, memPtr, length));
- if (actual != length) {
- LOGE("only read %d of %d bytes\n", (int) actual, (int) length);
- sysReleaseShmem(pMap);
- return -1;
- }
-
- return 0;
-}
-
-/*
- * Map a file (from fd's current offset) into a shared, read-only memory
+ * Map a file (from fd's current offset) into a private, read-only memory
* segment. The file offset must be a multiple of the page size.
*
* On success, returns 0 and fills out "pMap". On failure, returns a nonzero
* value and does not disturb "pMap".
*/
-int sysMapFileInShmem(int fd, MemMapping* pMap)
+static int sysMapFD(int fd, MemMapping* pMap)
{
off_t start;
size_t length;
@@ -126,87 +66,148 @@ int sysMapFileInShmem(int fd, MemMapping* pMap)
if (getFileStartAndLength(fd, &start, &length) < 0)
return -1;
- memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start);
+ memPtr = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, start);
if (memPtr == MAP_FAILED) {
- LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n", (int) length,
+ LOGW("mmap(%d, R, PRIVATE, %d, %d) failed: %s\n", (int) length,
fd, (int) start, strerror(errno));
return -1;
}
- pMap->baseAddr = pMap->addr = memPtr;
- pMap->baseLength = pMap->length = length;
+ pMap->addr = memPtr;
+ pMap->length = length;
+ pMap->range_count = 1;
+ pMap->ranges = malloc(sizeof(MappedRange));
+ pMap->ranges[0].addr = memPtr;
+ pMap->ranges[0].length = length;
return 0;
}
-/*
- * Map part of a file (from fd's current offset) into a shared, read-only
- * memory segment.
- *
- * On success, returns 0 and fills out "pMap". On failure, returns a nonzero
- * value and does not disturb "pMap".
- */
-int sysMapFileSegmentInShmem(int fd, off_t start, long length,
- MemMapping* pMap)
+static int sysMapBlockFile(FILE* mapf, MemMapping* pMap)
{
- off_t dummy;
- size_t fileLength, actualLength;
- off_t actualStart;
- int adjust;
- void* memPtr;
-
- assert(pMap != NULL);
+ char block_dev[PATH_MAX+1];
+ size_t size;
+ unsigned int blksize;
+ unsigned int 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");
+ return -1;
+ }
+ for (i = 0; i < sizeof(block_dev); ++i) {
+ if (block_dev[i] == '\n') {
+ block_dev[i] = 0;
+ break;
+ }
+ }
- if (getFileStartAndLength(fd, &dummy, &fileLength) < 0)
+ if (fscanf(mapf, "%d %d\n%d\n", &size, &blksize, &range_count) != 3) {
+ LOGW("failed to parse block map header\n");
return -1;
+ }
- if (start + length > (long)fileLength) {
- LOGW("bad segment: st=%d len=%ld flen=%d\n",
- (int) start, length, (int) fileLength);
+ 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));
+
+ // Reserve enough contiguous address space for the whole file.
+ unsigned char* reserve;
+ reserve = mmap64(NULL, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (reserve == MAP_FAILED) {
+ LOGW("failed to reserve address space: %s\n", strerror(errno));
return -1;
}
- /* adjust to be page-aligned */
- adjust = start % DEFAULT_PAGE_SIZE;
- actualStart = start - adjust;
- actualLength = length + adjust;
+ pMap->ranges[range_count-1].addr = reserve;
+ pMap->ranges[range_count-1].length = blocks * blksize;
- memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
- fd, actualStart);
- if (memPtr == MAP_FAILED) {
- LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n",
- (int) actualLength, fd, (int) actualStart, strerror(errno));
+ int fd = open(block_dev, O_RDONLY);
+ if (fd < 0) {
+ LOGW("failed to open block device %s: %s\n", block_dev, strerror(errno));
return -1;
}
- pMap->baseAddr = memPtr;
- pMap->baseLength = actualLength;
- pMap->addr = (char*)memPtr + adjust;
- pMap->length = length;
+ unsigned char* next = reserve;
+ 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;
+ }
+
+ void* addr = mmap64(next, (end-start)*blksize, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize);
+ if (addr == MAP_FAILED) {
+ LOGW("failed to map block %d: %s\n", i, strerror(errno));
+ return -1;
+ }
+ pMap->ranges[i].addr = addr;
+ pMap->ranges[i].length = (end-start)*blksize;
+
+ next += pMap->ranges[i].length;
+ }
+
+ pMap->addr = reserve;
+ pMap->length = size;
- LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d\n",
- (int) start, (int) length,
- pMap->baseAddr, (int) pMap->baseLength,
- pMap->addr, (int) pMap->length);
+ LOGI("mmapped %d ranges\n", range_count);
return 0;
}
+int sysMapFile(const char* fn, MemMapping* pMap)
+{
+ memset(pMap, 0, sizeof(*pMap));
+
+ if (fn && fn[0] == '@') {
+ // A map of blocks
+ FILE* mapf = fopen(fn+1, "r");
+ if (mapf == NULL) {
+ LOGV("Unable to open '%s': %s\n", fn+1, strerror(errno));
+ return -1;
+ }
+
+ if (sysMapBlockFile(mapf, pMap) != 0) {
+ LOGW("Map of '%s' failed\n", fn);
+ return -1;
+ }
+
+ fclose(mapf);
+ } else {
+ // This is a regular file.
+ int fd = open(fn, O_RDONLY, 0);
+ if (fd < 0) {
+ LOGE("Unable to open '%s': %s\n", fn, strerror(errno));
+ return -1;
+ }
+
+ if (sysMapFD(fd, pMap) != 0) {
+ LOGE("Map of '%s' failed\n", fn);
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ }
+ return 0;
+}
+
/*
* Release a memory mapping.
*/
-void sysReleaseShmem(MemMapping* pMap)
+void sysReleaseMap(MemMapping* pMap)
{
- if (pMap->baseAddr == NULL && pMap->baseLength == 0)
- return;
-
- if (munmap(pMap->baseAddr, pMap->baseLength) < 0) {
- LOGW("munmap(%p, %d) failed: %s\n",
- pMap->baseAddr, (int)pMap->baseLength, strerror(errno));
- } else {
- LOGV("munmap(%p, %d) succeeded\n", pMap->baseAddr, pMap->baseLength);
- pMap->baseAddr = NULL;
- pMap->baseLength = 0;
+ 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",
+ pMap->ranges[i].addr, (int)pMap->ranges[i].length, strerror(errno));
+ }
}
+ free(pMap->ranges);
+ pMap->ranges = NULL;
+ pMap->range_count = 0;
}
-
diff --git a/minzip/SysUtil.h b/minzip/SysUtil.h
index ec3a4bc..7adff1e 100644
--- a/minzip/SysUtil.h
+++ b/minzip/SysUtil.h
@@ -6,56 +6,47 @@
#ifndef _MINZIP_SYSUTIL
#define _MINZIP_SYSUTIL
-#include "inline_magic.h"
-
+#include <stdio.h>
#include <sys/types.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct MappedRange {
+ void* addr;
+ size_t length;
+} MappedRange;
+
/*
* Use this to keep track of mapped segments.
*/
typedef struct MemMapping {
- void* addr; /* start of data */
- size_t length; /* length of data */
+ unsigned char* addr; /* start of data */
+ size_t length; /* length of data */
- void* baseAddr; /* page-aligned base address */
- size_t baseLength; /* length of mapping */
+ int range_count;
+ MappedRange* ranges;
} MemMapping;
-/* copy a map */
-INLINE void sysCopyMap(MemMapping* dst, const MemMapping* src) {
- *dst = *src;
-}
-
-/*
- * Load a file into a new shared memory segment. All data from the current
- * offset to the end of the file is pulled in.
- *
- * The segment is read-write, allowing VM fixups. (It should be modified
- * to support .gz/.zip compressed data.)
- *
- * On success, "pMap" is filled in, and zero is returned.
- */
-int sysLoadFileInShmem(int fd, MemMapping* pMap);
-
/*
- * Map a file (from fd's current offset) into a shared,
- * read-only memory segment.
+ * Map a file into a private, read-only memory segment. If 'fn'
+ * begins with an '@' character, it is a map of blocks to be mapped,
+ * otherwise it is treated as an ordinary file.
*
* On success, "pMap" is filled in, and zero is returned.
*/
-int sysMapFileInShmem(int fd, MemMapping* pMap);
-
-/*
- * Like sysMapFileInShmem, but on only part of a file.
- */
-int sysMapFileSegmentInShmem(int fd, off_t start, long length,
- MemMapping* pMap);
+int sysMapFile(const char* fn, MemMapping* pMap);
/*
* Release the pages associated with a shared memory segment.
*
* This does not free "pMap"; it just releases the memory.
*/
-void sysReleaseShmem(MemMapping* pMap);
+void sysReleaseMap(MemMapping* pMap);
+
+#ifdef __cplusplus
+}
+#endif
#endif /*_MINZIP_SYSUTIL*/
diff --git a/minzip/Zip.c b/minzip/Zip.c
index f4f38a9..6785d4e 100644
--- a/minzip/Zip.c
+++ b/minzip/Zip.c
@@ -184,7 +184,7 @@ static int validFilename(const char *fileName, unsigned int fileNameLen)
*
* Returns "true" on success.
*/
-static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
+static bool parseZipArchive(ZipArchive* pArchive)
{
bool result = false;
const unsigned char* ptr;
@@ -196,7 +196,7 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
* signature for the first file (LOCSIG) or, if the archive doesn't
* have any files in it, the end-of-central-directory signature (ENDSIG).
*/
- val = get4LE(pMap->addr);
+ val = get4LE(pArchive->addr);
if (val == ENDSIG) {
LOGI("Found Zip archive, but it looks empty\n");
goto bail;
@@ -209,14 +209,14 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
* Find the EOCD. We'll find it immediately unless they have a file
* comment.
*/
- ptr = pMap->addr + pMap->length - ENDHDR;
+ ptr = pArchive->addr + pArchive->length - ENDHDR;
- while (ptr >= (const unsigned char*) pMap->addr) {
+ while (ptr >= (const unsigned char*) pArchive->addr) {
if (*ptr == (ENDSIG & 0xff) && get4LE(ptr) == ENDSIG)
break;
ptr--;
}
- if (ptr < (const unsigned char*) pMap->addr) {
+ if (ptr < (const unsigned char*) pArchive->addr) {
LOGI("Could not find end-of-central-directory in Zip\n");
goto bail;
}
@@ -230,9 +230,9 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
cdOffset = get4LE(ptr + ENDOFF);
LOGVV("numEntries=%d cdOffset=%d\n", numEntries, cdOffset);
- if (numEntries == 0 || cdOffset >= pMap->length) {
+ if (numEntries == 0 || cdOffset >= pArchive->length) {
LOGW("Invalid entries=%d offset=%d (len=%zd)\n",
- numEntries, cdOffset, pMap->length);
+ numEntries, cdOffset, pArchive->length);
goto bail;
}
@@ -245,14 +245,14 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
if (pArchive->pEntries == NULL || pArchive->pHash == NULL)
goto bail;
- ptr = pMap->addr + cdOffset;
+ ptr = pArchive->addr + cdOffset;
for (i = 0; i < numEntries; i++) {
ZipEntry* pEntry;
unsigned int fileNameLen, extraLen, commentLen, localHdrOffset;
const unsigned char* localHdr;
const char *fileName;
- if (ptr + CENHDR > (const unsigned char*)pMap->addr + pMap->length) {
+ if (ptr + CENHDR > (const unsigned char*)pArchive->addr + pArchive->length) {
LOGW("Ran off the end (at %d)\n", i);
goto bail;
}
@@ -266,7 +266,7 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
extraLen = get2LE(ptr + CENEXT);
commentLen = get2LE(ptr + CENCOM);
fileName = (const char*)ptr + CENHDR;
- if (fileName + fileNameLen > (const char*)pMap->addr + pMap->length) {
+ if (fileName + fileNameLen > (const char*)pArchive->addr + pArchive->length) {
LOGW("Filename ran off the end (at %d)\n", i);
goto bail;
}
@@ -352,15 +352,15 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
}
pEntry->externalFileAttributes = get4LE(ptr + CENATX);
- // Perform pMap->addr + localHdrOffset, ensuring that it won't
+ // Perform pArchive->addr + localHdrOffset, ensuring that it won't
// overflow. This is needed because localHdrOffset is untrusted.
- if (!safe_add((uintptr_t *)&localHdr, (uintptr_t)pMap->addr,
+ if (!safe_add((uintptr_t *)&localHdr, (uintptr_t)pArchive->addr,
(uintptr_t)localHdrOffset)) {
LOGW("Integer overflow adding in parseZipArchive\n");
goto bail;
}
if ((uintptr_t)localHdr + LOCHDR >
- (uintptr_t)pMap->addr + pMap->length) {
+ (uintptr_t)pArchive->addr + pArchive->length) {
LOGW("Bad offset to local header: %d (at %d)\n", localHdrOffset, i);
goto bail;
}
@@ -374,7 +374,7 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
LOGW("Integer overflow adding in parseZipArchive\n");
goto bail;
}
- if ((size_t)pEntry->offset + pEntry->compLen > pMap->length) {
+ if ((size_t)pEntry->offset + pEntry->compLen > pArchive->length) {
LOGW("Data ran off the end (at %d)\n", i);
goto bail;
}
@@ -427,50 +427,30 @@ bail:
*
* On success, we fill out the contents of "pArchive".
*/
-int mzOpenZipArchive(const char* fileName, ZipArchive* pArchive)
+int mzOpenZipArchive(unsigned char* addr, size_t length, ZipArchive* pArchive)
{
- MemMapping map;
int err;
- LOGV("Opening archive '%s' %p\n", fileName, pArchive);
-
- map.addr = NULL;
- memset(pArchive, 0, sizeof(*pArchive));
-
- pArchive->fd = open(fileName, O_RDONLY, 0);
- if (pArchive->fd < 0) {
- err = errno ? errno : -1;
- LOGV("Unable to open '%s': %s\n", fileName, strerror(err));
- goto bail;
- }
-
- if (sysMapFileInShmem(pArchive->fd, &map) != 0) {
- err = -1;
- LOGW("Map of '%s' failed\n", fileName);
- goto bail;
- }
-
- if (map.length < ENDHDR) {
+ if (length < ENDHDR) {
err = -1;
LOGV("File '%s' too small to be zip (%zd)\n", fileName, map.length);
goto bail;
}
- if (!parseZipArchive(pArchive, &map)) {
+ pArchive->addr = addr;
+ pArchive->length = length;
+
+ if (!parseZipArchive(pArchive)) {
err = -1;
LOGV("Parsing '%s' failed\n", fileName);
goto bail;
}
err = 0;
- sysCopyMap(&pArchive->map, &map);
- map.addr = NULL;
bail:
if (err != 0)
mzCloseZipArchive(pArchive);
- if (map.addr != NULL)
- sysReleaseShmem(&map);
return err;
}
@@ -483,16 +463,10 @@ void mzCloseZipArchive(ZipArchive* pArchive)
{
LOGV("Closing archive %p\n", pArchive);
- if (pArchive->fd >= 0)
- close(pArchive->fd);
- if (pArchive->map.addr != NULL)
- sysReleaseShmem(&pArchive->map);
-
free(pArchive->pEntries);
mzHashTableFree(pArchive->pHash);
- pArchive->fd = -1;
pArchive->pHash = NULL;
pArchive->pEntries = NULL;
}
@@ -528,29 +502,7 @@ static bool processStoredEntry(const ZipArchive *pArchive,
const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
void *cookie)
{
- size_t bytesLeft = pEntry->compLen;
- while (bytesLeft > 0) {
- unsigned char buf[32 * 1024];
- ssize_t n;
- size_t count;
- bool ret;
-
- count = bytesLeft;
- if (count > sizeof(buf)) {
- count = sizeof(buf);
- }
- n = read(pArchive->fd, buf, count);
- if (n < 0 || (size_t)n != count) {
- LOGE("Can't read %zu bytes from zip file: %ld\n", count, n);
- return false;
- }
- ret = processFunction(buf, n, cookie);
- if (!ret) {
- return false;
- }
- bytesLeft -= count;
- }
- return true;
+ return processFunction(pArchive->addr + pEntry->offset, pEntry->uncompLen, cookie);
}
static bool processDeflatedEntry(const ZipArchive *pArchive,
@@ -573,8 +525,8 @@ static bool processDeflatedEntry(const ZipArchive *pArchive,
zstream.zalloc = Z_NULL;
zstream.zfree = Z_NULL;
zstream.opaque = Z_NULL;
- zstream.next_in = NULL;
- zstream.avail_in = 0;
+ zstream.next_in = pArchive->addr + pEntry->offset;
+ zstream.avail_in = pEntry->compLen;
zstream.next_out = (Bytef*) procBuf;
zstream.avail_out = sizeof(procBuf);
zstream.data_type = Z_UNKNOWN;
@@ -598,25 +550,6 @@ static bool processDeflatedEntry(const ZipArchive *pArchive,
* Loop while we have data.
*/
do {
- /* read as much as we can */
- if (zstream.avail_in == 0) {
- long getSize = (compRemaining > (long)sizeof(readBuf)) ?
- (long)sizeof(readBuf) : compRemaining;
- LOGVV("+++ reading %ld bytes (%ld left)\n",
- getSize, compRemaining);
-
- int cc = read(pArchive->fd, readBuf, getSize);
- if (cc != (int) getSize) {
- LOGW("inflate read failed (%d vs %ld)\n", cc, getSize);
- goto z_bail;
- }
-
- compRemaining -= getSize;
-
- zstream.next_in = readBuf;
- zstream.avail_in = getSize;
- }
-
/* uncompress the data */
zerr = inflate(&zstream, Z_NO_FLUSH);
if (zerr != Z_OK && zerr != Z_STREAM_END) {
@@ -676,12 +609,6 @@ bool mzProcessZipEntryContents(const ZipArchive *pArchive,
bool ret = false;
off_t oldOff;
- /* save current offset */
- oldOff = lseek(pArchive->fd, 0, SEEK_CUR);
-
- /* Seek to the beginning of the entry's compressed data. */
- lseek(pArchive->fd, pEntry->offset, SEEK_SET);
-
switch (pEntry->compression) {
case STORED:
ret = processStoredEntry(pArchive, pEntry, processFunction, cookie);
@@ -695,8 +622,6 @@ bool mzProcessZipEntryContents(const ZipArchive *pArchive,
break;
}
- /* restore file offset */
- lseek(pArchive->fd, oldOff, SEEK_SET);
return ret;
}
diff --git a/minzip/Zip.h b/minzip/Zip.h
index c942828..05a2e60 100644
--- a/minzip/Zip.h
+++ b/minzip/Zip.h
@@ -46,11 +46,11 @@ typedef struct ZipEntry {
* One Zip archive. Treat as opaque.
*/
typedef struct ZipArchive {
- int fd;
- unsigned int numEntries;
- ZipEntry* pEntries;
- HashTable* pHash; // maps file name to ZipEntry
- MemMapping map;
+ unsigned int numEntries;
+ ZipEntry* pEntries;
+ HashTable* pHash; // maps file name to ZipEntry
+ unsigned char* addr;
+ size_t length;
} ZipArchive;
/*
@@ -68,7 +68,7 @@ typedef struct {
* On success, returns 0 and populates "pArchive". Returns nonzero errno
* value on failure.
*/
-int mzOpenZipArchive(const char* fileName, ZipArchive* pArchive);
+int mzOpenZipArchive(unsigned char* addr, size_t length, ZipArchive* pArchive);
/*
* Close archive, releasing resources associated with it.
diff --git a/recovery.cpp b/recovery.cpp
index 43cd9da..0a8c3b5 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -938,7 +938,7 @@ main(int argc, char **argv) {
return 0;
}
- printf("Starting recovery on %s", ctime(&start));
+ printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start));
load_volume_table();
ensure_path_mounted(LAST_LOG_FILE);
diff --git a/roots.cpp b/roots.cpp
index 113dba1..28004a7 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>
+#include <fcntl.h>
#include <fs_mgr.h>
#include "mtdutils/mtdutils.h"
@@ -28,6 +29,10 @@
#include "roots.h"
#include "common.h"
#include "make_ext4fs.h"
+extern "C" {
+#include "wipe.h"
+#include "cryptfs.h"
+}
static struct fstab *fstab = NULL;
@@ -191,11 +196,31 @@ int format_volume(const char* volume) {
}
if (strcmp(v->fs_type, "ext4") == 0) {
- int result = make_ext4fs(v->blk_device, v->length, volume, sehandle);
+ ssize_t length = 0;
+ if (v->length != 0) {
+ length = v->length;
+ } else if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0) {
+ length = -CRYPT_FOOTER_OFFSET;
+ }
+ int result = make_ext4fs(v->blk_device, length, volume, sehandle);
if (result != 0) {
LOGE("format_volume: make_extf4fs failed on %s\n", v->blk_device);
return -1;
}
+
+ // if there's a key_loc that looks like a path, it should be a
+ // block device for storing encryption metadata. wipe it too.
+ if (v->key_loc != NULL && v->key_loc[0] == '/') {
+ LOGI("wiping %s\n", v->key_loc);
+ int fd = open(v->key_loc, O_WRONLY | O_CREAT, 0644);
+ if (fd < 0) {
+ LOGE("format_volume: failed to open %s\n", v->key_loc);
+ return -1;
+ }
+ wipe_block_device(fd, get_file_size(fd));
+ close(fd);
+ }
+
return 0;
}
@@ -213,10 +238,16 @@ int setup_install_mounts() {
if (strcmp(v->mount_point, "/tmp") == 0 ||
strcmp(v->mount_point, "/cache") == 0) {
- if (ensure_path_mounted(v->mount_point) != 0) return -1;
+ if (ensure_path_mounted(v->mount_point) != 0) {
+ LOGE("failed to mount %s\n", v->mount_point);
+ return -1;
+ }
} else {
- if (ensure_path_unmounted(v->mount_point) != 0) return -1;
+ if (ensure_path_unmounted(v->mount_point) != 0) {
+ LOGE("failed to unmount %s\n", v->mount_point);
+ return -1;
+ }
}
}
return 0;
diff --git a/screen_ui.cpp b/screen_ui.cpp
index 27d0a24..fd1a6c7 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -392,6 +392,7 @@ void ScreenRecoveryUI::Init()
text_cols = gr_fb_width() / char_width;
if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1;
+ backgroundIcon[NONE] = NULL;
LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]);
backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
LoadBitmap("icon_error", &backgroundIcon[ERROR]);
diff --git a/ui.cpp b/ui.cpp
index cece02d..67a2500 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -31,6 +31,7 @@
#include <cutils/android_reboot.h>
#include "common.h"
+#include "roots.h"
#include "device.h"
#include "minui/minui.h"
#include "screen_ui.h"
@@ -47,7 +48,10 @@ RecoveryUI::RecoveryUI() :
key_queue_len(0),
key_last_down(-1),
key_long_press(false),
- key_down_count(0) {
+ key_down_count(0),
+ consecutive_power_keys(0),
+ consecutive_alternate_keys(0),
+ last_key(-1) {
pthread_mutex_init(&key_queue_mutex, NULL);
pthread_cond_init(&key_queue_cond, NULL);
self = this;
@@ -59,12 +63,12 @@ void RecoveryUI::Init() {
}
-int RecoveryUI::input_callback(int fd, short revents, void* data)
+int RecoveryUI::input_callback(int fd, uint32_t epevents, void* data)
{
struct input_event ev;
int ret;
- ret = ev_get_input(fd, revents, &ev);
+ ret = ev_get_input(fd, epevents, &ev);
if (ret)
return -1;
@@ -152,6 +156,13 @@ void RecoveryUI::process_key(int key_code, int updown) {
case RecoveryUI::ENQUEUE:
EnqueueKey(key_code);
break;
+
+ case RecoveryUI::MOUNT_SYSTEM:
+#ifndef NO_RECOVERY_MOUNT
+ ensure_path_mounted("/system");
+ Print("Mounted /system.");
+#endif
+ break;
}
}
}
@@ -258,8 +269,41 @@ void RecoveryUI::FlushKeys() {
pthread_mutex_unlock(&key_queue_mutex);
}
+// The default CheckKey implementation assumes the device has power,
+// volume up, and volume down keys.
+//
+// - Hold power and press vol-up to toggle display.
+// - Press power seven times in a row to reboot.
+// - Alternate vol-up and vol-down seven times to mount /system.
RecoveryUI::KeyAction RecoveryUI::CheckKey(int key) {
- return RecoveryUI::ENQUEUE;
+ if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) {
+ return TOGGLE;
+ }
+
+ if (key == KEY_POWER) {
+ ++consecutive_power_keys;
+ if (consecutive_power_keys >= 7) {
+ return REBOOT;
+ }
+ } else {
+ consecutive_power_keys = 0;
+ }
+
+ if ((key == KEY_VOLUMEUP &&
+ (last_key == KEY_VOLUMEDOWN || last_key == -1)) ||
+ (key == KEY_VOLUMEDOWN &&
+ (last_key == KEY_VOLUMEUP || last_key == -1))) {
+ ++consecutive_alternate_keys;
+ if (consecutive_alternate_keys >= 7) {
+ consecutive_alternate_keys = 0;
+ return MOUNT_SYSTEM;
+ }
+ } else {
+ consecutive_alternate_keys = 0;
+ }
+ last_key = key;
+
+ return ENQUEUE;
}
void RecoveryUI::NextCheckKeyIsLong(bool is_long_press) {
diff --git a/ui.h b/ui.h
index 0757260..faa0acd 100644
--- a/ui.h
+++ b/ui.h
@@ -79,7 +79,7 @@ class RecoveryUI {
// Return value indicates whether an immediate operation should be
// triggered (toggling the display, rebooting the device), or if
// the key should be enqueued for use by the main thread.
- enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE };
+ enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE, MOUNT_SYSTEM };
virtual KeyAction CheckKey(int key);
// Called immediately before each call to CheckKey(), tell you if
@@ -123,6 +123,10 @@ private:
int key_down_count; // under key_queue_mutex
int rel_sum;
+ int consecutive_power_keys;
+ int consecutive_alternate_keys;
+ int last_key;
+
typedef struct {
RecoveryUI* ui;
int key_code;
@@ -132,7 +136,7 @@ private:
pthread_t input_t;
static void* input_thread(void* cookie);
- static int input_callback(int fd, short revents, void* data);
+ static int input_callback(int fd, uint32_t epevents, void* data);
void process_key(int key_code, int updown);
bool usb_connected();
diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk
new file mode 100644
index 0000000..756bc96
--- /dev/null
+++ b/uncrypt/Android.mk
@@ -0,0 +1,28 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := uncrypt.c
+
+LOCAL_MODULE := uncrypt
+
+LOCAL_STATIC_LIBRARIES := \
+ libfs_mgr \
+ libcutils \
+ libc
+
+include $(BUILD_EXECUTABLE)
diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c
new file mode 100644
index 0000000..1f0f59d
--- /dev/null
+++ b/uncrypt/uncrypt.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This program takes a file on an ext4 filesystem and produces a list
+// of the blocks that file occupies, which enables the file contents
+// to be read directly from the block device without mounting the
+// filesystem.
+//
+// If the filesystem is using an encrypted block device, it will also
+// read the file and rewrite it to the same blocks of the underlying
+// (unencrypted) block device, so the file contents can be read
+// without the need for the decryption key.
+//
+// The output of this program is a "block map" which looks like this:
+//
+// /dev/block/platform/msm_sdcc.1/by-name/userdata # block device
+// 49652 4096 # file size in bytes, block size
+// 3 # count of block ranges
+// 1000 1008 # block range 0
+// 2100 2102 # ... block range 1
+// 30 33 # ... block range 2
+//
+// Each block range represents a half-open interval; the line "30 33"
+// reprents the blocks [30, 31, 32].
+//
+// Recovery can take this block map file and retrieve the underlying
+// file data to use as an update package.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/mman.h>
+
+#include <cutils/properties.h>
+#include <fs_mgr.h>
+
+#define WINDOW_SIZE 5
+#define RECOVERY_COMMAND_FILE "/cache/recovery/command"
+#define RECOVERY_COMMAND_FILE_TMP "/cache/recovery/command.tmp"
+#define CACHE_BLOCK_MAP "/cache/recovery/block.map"
+
+static int write_at_offset(unsigned char* buffer, size_t size,
+ int wfd, off64_t offset)
+{
+ lseek64(wfd, offset, SEEK_SET);
+ size_t written = 0;
+ while (written < size) {
+ ssize_t wrote = write(wfd, buffer + written, size - written);
+ if (wrote < 0) {
+ fprintf(stderr, "error writing offset %lld: %s\n", offset, strerror(errno));
+ return -1;
+ }
+ written += wrote;
+ }
+ return 0;
+}
+
+void add_block_to_ranges(int** ranges, int* range_alloc, int* range_used, int new_block)
+{
+ // If the current block start is < 0, set the start to the new
+ // block. (This only happens for the very first block of the very
+ // first range.)
+ if ((*ranges)[*range_used*2-2] < 0) {
+ (*ranges)[*range_used*2-2] = new_block;
+ (*ranges)[*range_used*2-1] = new_block;
+ }
+
+ if (new_block == (*ranges)[*range_used*2-1]) {
+ // If the new block comes immediately after the current range,
+ // all we have to do is extend the current range.
+ ++(*ranges)[*range_used*2-1];
+ } else {
+ // We need to start a new range.
+
+ // If there isn't enough room in the array, we need to expand it.
+ if (*range_used >= *range_alloc) {
+ *range_alloc *= 2;
+ *ranges = realloc(*ranges, *range_alloc * 2 * sizeof(int));
+ }
+
+ ++*range_used;
+ (*ranges)[*range_used*2-2] = new_block;
+ (*ranges)[*range_used*2-1] = new_block+1;
+ }
+}
+
+const char* find_block_device(const char* path, int* encryptable, int* encrypted)
+{
+ // The fstab path is always "/fstab.${ro.hardware}".
+ char fstab_path[PATH_MAX+1] = "/fstab.";
+ if (!property_get("ro.hardware", fstab_path+strlen(fstab_path), "")) {
+ fprintf(stderr, "failed to get ro.hardware\n");
+ return NULL;
+ }
+
+ struct fstab* fstab = fs_mgr_read_fstab(fstab_path);
+ if (!fstab) {
+ fprintf(stderr, "failed to read %s\n", fstab_path);
+ return NULL;
+ }
+
+ // Look for a volume whose mount point is the prefix of path and
+ // return its block device. Set encrypted if it's currently
+ // encrypted.
+ int i;
+ for (i = 0; i < fstab->num_entries; ++i) {
+ struct fstab_rec* v = &fstab->recs[i];
+ if (!v->mount_point) continue;
+ int len = strlen(v->mount_point);
+ if (strncmp(path, v->mount_point, len) == 0 &&
+ (path[len] == '/' || path[len] == 0)) {
+ *encrypted = 0;
+ *encryptable = 0;
+ if (fs_mgr_is_encryptable(v)) {
+ *encryptable = 1;
+ char buffer[PROPERTY_VALUE_MAX+1];
+ if (property_get("ro.crypto.state", buffer, "") &&
+ strcmp(buffer, "encrypted") == 0) {
+ *encrypted = 1;
+ }
+ }
+ return v->blk_device;
+ }
+ }
+
+ return NULL;
+}
+
+char* parse_recovery_command_file()
+{
+ char* fn = NULL;
+ int count = 0;
+ char temp[1024];
+
+ FILE* fo = fopen(RECOVERY_COMMAND_FILE_TMP, "w");
+
+ FILE* f = fopen(RECOVERY_COMMAND_FILE, "r");
+ while (fgets(temp, sizeof(temp), f)) {
+ printf("read: %s", temp);
+ if (strncmp(temp, "--update_package=", strlen("--update_package=")) == 0) {
+ fn = strdup(temp + strlen("--update_package="));
+ strcpy(temp, "--update_package=@" CACHE_BLOCK_MAP "\n");
+ }
+ fputs(temp, fo);
+ }
+ fclose(f);
+ fclose(fo);
+
+ if (fn) {
+ char* newline = strchr(fn, '\n');
+ if (newline) *newline = 0;
+ }
+ return fn;
+}
+
+int produce_block_map(const char* path, const char* map_file, const char* blk_dev,
+ int encrypted)
+{
+ struct stat sb;
+ int ret;
+
+ FILE* mapf = fopen(map_file, "w");
+
+ ret = stat(path, &sb);
+ if (ret != 0) {
+ fprintf(stderr, "failed to stat %s\n", path);
+ return -1;
+ }
+
+ printf(" block size: %ld bytes\n", sb.st_blksize);
+
+ int blocks = ((sb.st_size-1) / sb.st_blksize) + 1;
+ printf(" file size: %lld bytes, %d blocks\n", sb.st_size, blocks);
+
+ int* ranges;
+ int range_alloc = 1;
+ int range_used = 1;
+ ranges = malloc(range_alloc * 2 * sizeof(int));
+ ranges[0] = -1;
+ ranges[1] = -1;
+
+ fprintf(mapf, "%s\n%lld %lu\n", blk_dev, sb.st_size, sb.st_blksize);
+
+ unsigned char* buffers[WINDOW_SIZE];
+ int i;
+ if (encrypted) {
+ for (i = 0; i < WINDOW_SIZE; ++i) {
+ buffers[i] = malloc(sb.st_blksize);
+ }
+ }
+ int head_block = 0;
+ int head = 0, tail = 0;
+ size_t pos = 0;
+
+ int fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "failed to open fd for reading: %s\n", strerror(errno));
+ return -1;
+ }
+ fsync(fd);
+
+ int wfd = -1;
+ if (encrypted) {
+ wfd = open(blk_dev, O_WRONLY);
+ if (wfd < 0) {
+ fprintf(stderr, "failed to open fd for writing: %s\n", strerror(errno));
+ return -1;
+ }
+ }
+
+ while (pos < sb.st_size) {
+ if ((tail+1) % WINDOW_SIZE == head) {
+ // write out head buffer
+ int block = head_block;
+ ret = ioctl(fd, FIBMAP, &block);
+ if (ret != 0) {
+ fprintf(stderr, "failed to find block %d\n", head_block);
+ return -1;
+ }
+ add_block_to_ranges(&ranges, &range_alloc, &range_used, block);
+ if (encrypted) {
+ if (write_at_offset(buffers[head], sb.st_blksize, wfd, (off64_t)sb.st_blksize * block) != 0) {
+ return -1;
+ }
+ }
+ head = (head + 1) % WINDOW_SIZE;
+ ++head_block;
+ }
+
+ // read next block to tail
+ if (encrypted) {
+ size_t so_far = 0;
+ while (so_far < sb.st_blksize && pos < sb.st_size) {
+ ssize_t this_read = read(fd, buffers[tail] + so_far, sb.st_blksize - so_far);
+ if (this_read < 0) {
+ fprintf(stderr, "failed to read: %s\n", strerror(errno));
+ return -1;
+ }
+ so_far += this_read;
+ pos += this_read;
+ }
+ } else {
+ // If we're not encrypting; we don't need to actually read
+ // anything, just skip pos forward as if we'd read a
+ // block.
+ pos += sb.st_blksize;
+ }
+ tail = (tail+1) % WINDOW_SIZE;
+ }
+
+ while (head != tail) {
+ // write out head buffer
+ int block = head_block;
+ ret = ioctl(fd, FIBMAP, &block);
+ if (ret != 0) {
+ fprintf(stderr, "failed to find block %d\n", head_block);
+ return -1;
+ }
+ add_block_to_ranges(&ranges, &range_alloc, &range_used, block);
+ if (encrypted) {
+ if (write_at_offset(buffers[head], sb.st_blksize, wfd, (off64_t)sb.st_blksize * block) != 0) {
+ return -1;
+ }
+ }
+ head = (head + 1) % WINDOW_SIZE;
+ ++head_block;
+ }
+
+ fprintf(mapf, "%d\n", range_used);
+ for (i = 0; i < range_used; ++i) {
+ fprintf(mapf, "%d %d\n", ranges[i*2], ranges[i*2+1]);
+ }
+
+ fclose(mapf);
+ close(fd);
+ if (encrypted) {
+ close(wfd);
+ }
+
+ return 0;
+}
+
+void reboot_to_recovery() {
+ property_set("sys.powerctl", "reboot,recovery");
+ sleep(10);
+}
+
+int main(int argc, char** argv)
+{
+ const char* input_path;
+ const char* map_file;
+ int do_reboot = 1;
+
+ if (argc != 1 && argc != 3) {
+ fprintf(stderr, "usage: %s [<transform_path> <map_file>]\n", argv[0]);
+ return 2;
+ }
+
+ if (argc == 3) {
+ // when command-line args are given this binary is being used
+ // for debugging; don't reboot to recovery at the end.
+ input_path = argv[1];
+ map_file = argv[2];
+ do_reboot = 0;
+ } else {
+ input_path = parse_recovery_command_file();
+ if (input_path == NULL) {
+ // if we're rebooting to recovery without a package (say,
+ // to wipe data), then we don't need to do anything before
+ // going to recovery.
+ fprintf(stderr, "no recovery command file or no update package arg");
+ reboot_to_recovery();
+ return 1;
+ }
+ map_file = CACHE_BLOCK_MAP;
+ }
+
+ // Turn the name of the file we're supposed to convert into an
+ // absolute path, so we can find what filesystem it's on.
+ char path[PATH_MAX+1];
+ if (realpath(input_path, path) == NULL) {
+ fprintf(stderr, "failed to convert %s to absolute path: %s\n", input_path, strerror(errno));
+ return 1;
+ }
+
+ int encryptable;
+ int encrypted;
+ const char* blk_dev = find_block_device(path, &encryptable, &encrypted);
+ if (blk_dev == NULL) {
+ fprintf(stderr, "failed to find block device for %s\n", path);
+ return 1;
+ }
+
+ // If the filesystem it's on isn't encrypted, we only produce the
+ // block map, we don't rewrite the file contents (it would be
+ // pointless to do so).
+ printf("encryptable: %s\n", encryptable ? "yes" : "no");
+ printf(" encrypted: %s\n", encrypted ? "yes" : "no");
+
+ if (!encryptable) {
+ // If the file is on a filesystem that doesn't support
+ // encryption (eg, /cache), then leave it alone.
+ //
+ // TODO: change this to be !encrypted -- if the file is on
+ // /data but /data isn't encrypted, we don't need to use the
+ // block map mechanism. We do for now so as to get more
+ // testing of it (since most dogfood devices aren't
+ // encrypted).
+
+ unlink(RECOVERY_COMMAND_FILE_TMP);
+ } else {
+ if (produce_block_map(path, map_file, blk_dev, encrypted) != 0) {
+ return 1;
+ }
+ }
+
+ rename(RECOVERY_COMMAND_FILE_TMP, RECOVERY_COMMAND_FILE);
+ reboot_to_recovery();
+ return 0;
+}
diff --git a/updater/updater.c b/updater/updater.c
index c7009fe..4e1cc9c 100644
--- a/updater/updater.c
+++ b/updater/updater.c
@@ -22,6 +22,7 @@
#include "updater.h"
#include "install.h"
#include "minzip/Zip.h"
+#include "minzip/SysUtil.h"
// Generated by the makefile, this function defines the
// RegisterDeviceExtensions() function, which calls all the
@@ -65,19 +66,24 @@ int main(int argc, char** argv) {
// Extract the script from the package.
- char* package_data = argv[3];
+ const char* package_filename = argv[3];
+ MemMapping map;
+ if (sysMapFile(package_filename, &map) != 0) {
+ printf("failed to map package %s\n", argv[3]);
+ return 3;
+ }
ZipArchive za;
int err;
- err = mzOpenZipArchive(package_data, &za);
+ err = mzOpenZipArchive(map.addr, map.length, &za);
if (err != 0) {
printf("failed to open package %s: %s\n",
- package_data, strerror(err));
+ argv[3], strerror(err));
return 3;
}
const ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME);
if (script_entry == NULL) {
- printf("failed to find %s in %s\n", SCRIPT_NAME, package_data);
+ printf("failed to find %s in %s\n", SCRIPT_NAME, package_filename);
return 4;
}
@@ -152,6 +158,7 @@ int main(int argc, char** argv) {
if (updater_info.package_zip) {
mzCloseZipArchive(updater_info.package_zip);
}
+ sysReleaseMap(&map);
free(script);
return 0;
diff --git a/verifier.cpp b/verifier.cpp
index 0930fbd..55d58ee 100644
--- a/verifier.cpp
+++ b/verifier.cpp
@@ -111,15 +111,10 @@ static bool read_pkcs7(uint8_t* pkcs7_der, size_t pkcs7_der_len, uint8_t** sig_d
// Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered
// or no key matches the signature).
-int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys) {
+int verify_file(unsigned char* addr, size_t length,
+ const Certificate* pKeys, unsigned int numKeys) {
ui->SetProgress(0.0);
- FILE* f = fopen(path, "rb");
- if (f == NULL) {
- LOGE("failed to open %s (%s)\n", path, strerror(errno));
- return VERIFY_FAILURE;
- }
-
// An archive with a whole-file signature will end in six bytes:
//
// (2-byte signature start) $ff $ff (2-byte comment size)
@@ -131,22 +126,15 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys
#define FOOTER_SIZE 6
- if (fseek(f, -FOOTER_SIZE, SEEK_END) != 0) {
- LOGE("failed to seek in %s (%s)\n", path, strerror(errno));
- fclose(f);
+ if (length < FOOTER_SIZE) {
+ LOGE("not big enough to contain footer\n");
return VERIFY_FAILURE;
}
- unsigned char footer[FOOTER_SIZE];
- if (fread(footer, 1, FOOTER_SIZE, f) != FOOTER_SIZE) {
- LOGE("failed to read footer from %s (%s)\n", path, strerror(errno));
- fclose(f);
- return VERIFY_FAILURE;
- }
+ unsigned char* footer = addr + length - FOOTER_SIZE;
if (footer[2] != 0xff || footer[3] != 0xff) {
LOGE("footer is wrong\n");
- fclose(f);
return VERIFY_FAILURE;
}
@@ -157,7 +145,6 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys
if (signature_start <= FOOTER_SIZE) {
LOGE("Signature start is in the footer");
- fclose(f);
return VERIFY_FAILURE;
}
@@ -167,9 +154,8 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys
// comment length.
size_t eocd_size = comment_size + EOCD_HEADER_SIZE;
- if (fseek(f, -eocd_size, SEEK_END) != 0) {
- LOGE("failed to seek in %s (%s)\n", path, strerror(errno));
- fclose(f);
+ if (length < eocd_size) {
+ LOGE("not big enough to contain EOCD\n");
return VERIFY_FAILURE;
}
@@ -177,26 +163,15 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys
// This is everything except the signature data and length, which
// includes all of the EOCD except for the comment length field (2
// bytes) and the comment data.
- size_t signed_len = ftell(f) + EOCD_HEADER_SIZE - 2;
+ size_t signed_len = length - eocd_size + EOCD_HEADER_SIZE - 2;
- unsigned char* eocd = (unsigned char*)malloc(eocd_size);
- if (eocd == NULL) {
- LOGE("malloc for EOCD record failed\n");
- fclose(f);
- return VERIFY_FAILURE;
- }
- if (fread(eocd, 1, eocd_size, f) != eocd_size) {
- LOGE("failed to read eocd from %s (%s)\n", path, strerror(errno));
- fclose(f);
- return VERIFY_FAILURE;
- }
+ unsigned char* eocd = addr + length - eocd_size;
// If this is really is the EOCD record, it will begin with the
// magic number $50 $4b $05 $06.
if (eocd[0] != 0x50 || eocd[1] != 0x4b ||
eocd[2] != 0x05 || eocd[3] != 0x06) {
LOGE("signature length doesn't match EOCD marker\n");
- fclose(f);
return VERIFY_FAILURE;
}
@@ -209,7 +184,6 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys
// which could be exploitable. Fail verification if
// this sequence occurs anywhere after the real one.
LOGE("EOCD marker occurs after start of EOCD\n");
- fclose(f);
return VERIFY_FAILURE;
}
}
@@ -229,35 +203,23 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys
SHA256_CTX sha256_ctx;
SHA_init(&sha1_ctx);
SHA256_init(&sha256_ctx);
- unsigned char* buffer = (unsigned char*)malloc(BUFFER_SIZE);
- if (buffer == NULL) {
- LOGE("failed to alloc memory for sha1 buffer\n");
- fclose(f);
- return VERIFY_FAILURE;
- }
double frac = -1.0;
size_t so_far = 0;
- fseek(f, 0, SEEK_SET);
while (so_far < signed_len) {
- size_t size = BUFFER_SIZE;
- if (signed_len - so_far < size) size = signed_len - so_far;
- if (fread(buffer, 1, size, f) != size) {
- LOGE("failed to read data from %s (%s)\n", path, strerror(errno));
- fclose(f);
- return VERIFY_FAILURE;
- }
- if (need_sha1) SHA_update(&sha1_ctx, buffer, size);
- if (need_sha256) SHA256_update(&sha256_ctx, buffer, size);
+ size_t size = signed_len - so_far;
+ if (size > BUFFER_SIZE) size = BUFFER_SIZE;
+
+ if (need_sha1) SHA_update(&sha1_ctx, addr + so_far, size);
+ if (need_sha256) SHA256_update(&sha256_ctx, addr + so_far, size);
so_far += size;
+
double f = so_far / (double)signed_len;
if (f > frac + 0.02 || size == so_far) {
ui->SetProgress(f);
frac = f;
}
}
- fclose(f);
- free(buffer);
const uint8_t* sha1 = SHA_final(&sha1_ctx);
const uint8_t* sha256 = SHA256_final(&sha256_ctx);
@@ -269,10 +231,8 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys
if (!read_pkcs7(eocd + eocd_size - signature_start, signature_size, &sig_der,
&sig_der_length)) {
LOGE("Could not find signature DER block\n");
- free(eocd);
return VERIFY_FAILURE;
}
- free(eocd);
/*
* Check to make sure at least one of the keys matches the signature. Since
diff --git a/verifier.h b/verifier.h
index 023d3bf..15f8d98 100644
--- a/verifier.h
+++ b/verifier.h
@@ -37,10 +37,13 @@ typedef struct {
ECPublicKey* ec;
} Certificate;
-/* Look in the file for a signature footer, and verify that it
- * matches one of the given keys. Return one of the constants below.
+/* addr and length define a an update package file that has been
+ * loaded (or mmap'ed, or whatever) into memory. Verify that the file
+ * is signed and the signature matches one of the given keys. Return
+ * one of the constants below.
*/
-int verify_file(const char* path, const Certificate *pKeys, unsigned int numKeys);
+int verify_file(unsigned char* addr, size_t length,
+ const Certificate *pKeys, unsigned int numKeys);
Certificate* load_keys(const char* filename, int* numKeys);
diff --git a/verifier_test.cpp b/verifier_test.cpp
index 88fcad4..10a5dda 100644
--- a/verifier_test.cpp
+++ b/verifier_test.cpp
@@ -17,12 +17,16 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include "common.h"
#include "verifier.h"
#include "ui.h"
#include "mincrypt/sha.h"
#include "mincrypt/sha256.h"
+#include "minzip/SysUtil.h"
// This is build/target/product/security/testkey.x509.pem after being
// dumped out by dumpkey.jar.
@@ -227,7 +231,13 @@ int main(int argc, char **argv) {
ui = new FakeUI();
- int result = verify_file(argv[argn], certs, num_keys);
+ MemMapping map;
+ if (sysMapFile(argv[argn], &map) != 0) {
+ fprintf(stderr, "failed to mmap %s: %s\n", argv[argn], strerror(errno));
+ return 4;
+ }
+
+ int result = verify_file(map.addr, map.length, certs, num_keys);
if (result == VERIFY_SUCCESS) {
printf("VERIFIED\n");
return 0;