DO NOT MERGE: resolve merge conflicts of 5346da02 to klp-modular-dev am: 4eb997d52b -s ours am: 3591cb48f5 -s ours am: 8bdb13dd9a
am: 0d750f900b -s ours
Change-Id: Iaab9c0e9d8e5a5198bcad373d229915c6bc62c79
diff --git a/Android.mk b/Android.mk
index 1a91f00..0484065 100644
--- a/Android.mk
+++ b/Android.mk
@@ -30,16 +30,17 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
- recovery.cpp \
- bootloader.cpp \
- install.cpp \
- roots.cpp \
- ui.cpp \
- screen_ui.cpp \
- asn1_decoder.cpp \
- verifier.cpp \
adb_install.cpp \
- fuse_sdcard_provider.c
+ asn1_decoder.cpp \
+ bootloader.cpp \
+ device.cpp \
+ fuse_sdcard_provider.c \
+ install.cpp \
+ recovery.cpp \
+ roots.cpp \
+ screen_ui.cpp \
+ ui.cpp \
+ verifier.cpp \
LOCAL_MODULE := recovery
@@ -54,6 +55,11 @@
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_C_INCLUDES += \
+ system/vold \
+ system/extras/ext4_utils \
+ system/core/adb \
+
LOCAL_STATIC_LIBRARIES := \
libext4_utils_static \
libsparse_static \
@@ -66,6 +72,7 @@
libminui \
libpng \
libfs_mgr \
+ libbase \
libcutils \
liblog \
libselinux \
@@ -75,15 +82,11 @@
ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
LOCAL_CFLAGS += -DUSE_EXT4
- LOCAL_C_INCLUDES += system/extras/ext4_utils system/vold
+ LOCAL_C_INCLUDES += system/extras/ext4_utils
LOCAL_STATIC_LIBRARIES += libext4_utils_static libz
endif
-# This binary is in the recovery ramdisk, which is otherwise a copy of root.
-# It gets copied there in config/Makefile. LOCAL_MODULE_TAGS suppresses
-# a (redundant) copy of the binary in /system/bin for user builds.
-# TODO: Build the ramdisk image in a more principled way.
-LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
ifeq ($(TARGET_RECOVERY_UI_LIB),)
LOCAL_SRC_FILES += default_device.cpp
@@ -91,9 +94,6 @@
LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB)
endif
-LOCAL_C_INCLUDES += system/extras/ext4_utils
-LOCAL_C_INCLUDES += external/openssl/include
-
include $(BUILD_EXECUTABLE)
# All the APIs for testing
@@ -108,7 +108,6 @@
LOCAL_MODULE := verifier_test
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := tests
-LOCAL_CFLAGS += -DNO_RECOVERY_MOUNT
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_SRC_FILES := \
verifier_test.cpp \
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..bab7e87
--- /dev/null
+++ b/README.md
@@ -0,0 +1,12 @@
+The Recovery Image
+==================
+
+Quick turn-around testing
+-------------------------
+
+ mm -j && m ramdisk-nodeps && m recoveryimage-nodeps
+
+ # To boot into the new recovery image
+ # without flashing the recovery partition:
+ adb reboot bootloader
+ fastboot boot $ANDROID_PRODUCT_OUT/recovery.img
diff --git a/adb_install.cpp b/adb_install.cpp
index be3b9a0..e3b94ea 100644
--- a/adb_install.cpp
+++ b/adb_install.cpp
@@ -30,10 +30,8 @@
#include "install.h"
#include "common.h"
#include "adb_install.h"
-extern "C" {
#include "minadbd/fuse_adb_provider.h"
#include "fuse_sideload.h"
-}
static RecoveryUI* ui = NULL;
@@ -44,7 +42,7 @@
ui->Print("failed to open driver control: %s\n", strerror(errno));
return;
}
- if (write(fd, enabled ? "1" : "0", 1) < 0) {
+ if (TEMP_FAILURE_RETRY(write(fd, enabled ? "1" : "0", 1)) == -1) {
ui->Print("failed to set driver control: %s\n", strerror(errno));
}
if (close(fd) < 0) {
@@ -61,9 +59,7 @@
static void
maybe_restart_adbd() {
- char value[PROPERTY_VALUE_MAX+1];
- int len = property_get("ro.debuggable", value, NULL);
- if (len == 1 && value[0] == '1') {
+ if (is_ro_debuggable()) {
ui->Print("Restarting adbd...\n");
set_usb_driver(true);
property_set("ctl.start", "adbd");
@@ -75,7 +71,9 @@
#define ADB_INSTALL_TIMEOUT 300
int
-apply_from_adb(RecoveryUI* ui_, int* wipe_cache, const char* install_file) {
+apply_from_adb(RecoveryUI* ui_, bool* wipe_cache, const char* install_file) {
+ modified_flash = true;
+
ui = ui_;
stop_adbd();
@@ -109,7 +107,7 @@
sleep(1);
continue;
} else {
- ui->Print("\nTimed out waiting for package.\n\n", strerror(errno));
+ ui->Print("\nTimed out waiting for package.\n\n");
result = INSTALL_ERROR;
kill(child, SIGKILL);
break;
diff --git a/adb_install.h b/adb_install.h
index a18b712..efad436 100644
--- a/adb_install.h
+++ b/adb_install.h
@@ -19,6 +19,6 @@
class RecoveryUI;
-int apply_from_adb(RecoveryUI* h, int* wipe_cache, const char* install_file);
+int apply_from_adb(RecoveryUI* h, bool* wipe_cache, const char* install_file);
#endif
diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c
index 2c86e09..2358d42 100644
--- a/applypatch/applypatch.c
+++ b/applypatch/applypatch.c
@@ -422,20 +422,19 @@
int attempt;
for (attempt = 0; attempt < 2; ++attempt) {
- lseek(fd, start, SEEK_SET);
+ if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) {
+ printf("failed seek on %s: %s\n",
+ partition, strerror(errno));
+ return -1;
+ }
while (start < len) {
size_t to_write = len - start;
if (to_write > 1<<20) to_write = 1<<20;
- ssize_t written = write(fd, data+start, to_write);
- if (written < 0) {
- if (errno == EINTR) {
- written = 0;
- } else {
- printf("failed write writing to %s (%s)\n",
- partition, strerror(errno));
- return -1;
- }
+ ssize_t written = TEMP_FAILURE_RETRY(write(fd, data+start, to_write));
+ if (written == -1) {
+ printf("failed write writing to %s: %s\n", partition, strerror(errno));
+ return -1;
}
start += written;
}
@@ -460,13 +459,20 @@
// won't just be reading the cache.
sync();
int dc = open("/proc/sys/vm/drop_caches", O_WRONLY);
- write(dc, "3\n", 2);
+ if (TEMP_FAILURE_RETRY(write(dc, "3\n", 2)) == -1) {
+ printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno));
+ } else {
+ printf(" caches dropped\n");
+ }
close(dc);
sleep(1);
- printf(" caches dropped\n");
// verify
- lseek(fd, 0, SEEK_SET);
+ if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) {
+ printf("failed to seek back to beginning of %s: %s\n",
+ partition, strerror(errno));
+ return -1;
+ }
unsigned char buffer[4096];
start = len;
size_t p;
@@ -476,15 +482,12 @@
size_t so_far = 0;
while (so_far < to_read) {
- ssize_t read_count = read(fd, buffer+so_far, to_read-so_far);
- if (read_count < 0) {
- if (errno == EINTR) {
- read_count = 0;
- } else {
- printf("verify read error %s at %zu: %s\n",
- partition, p, strerror(errno));
- return -1;
- }
+ ssize_t read_count =
+ TEMP_FAILURE_RETRY(read(fd, buffer+so_far, to_read-so_far));
+ if (read_count == -1) {
+ printf("verify read error %s at %zu: %s\n",
+ partition, p, strerror(errno));
+ return -1;
}
if ((size_t)read_count < to_read) {
printf("short verify read %s at %zu: %zd %zu %s\n",
@@ -625,8 +628,8 @@
ssize_t done = 0;
ssize_t wrote;
while (done < (ssize_t) len) {
- wrote = write(fd, data+done, len-done);
- if (wrote <= 0) {
+ wrote = TEMP_FAILURE_RETRY(write(fd, data+done, len-done));
+ if (wrote == -1) {
printf("error writing %d bytes: %s\n", (int)(len-done), strerror(errno));
return done;
}
@@ -659,7 +662,7 @@
printf("failed to statfs %s: %s\n", filename, strerror(errno));
return -1;
}
- return sf.f_bsize * sf.f_bfree;
+ return sf.f_bsize * sf.f_bavail;
}
int CacheSizeCheck(size_t bytes) {
diff --git a/applypatch/bspatch.c b/applypatch/bspatch.c
index b34ec2a..b57760e 100644
--- a/applypatch/bspatch.c
+++ b/applypatch/bspatch.c
@@ -23,6 +23,7 @@
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
+#include <malloc.h>
#include <unistd.h>
#include <string.h>
diff --git a/applypatch/imgdiff.c b/applypatch/imgdiff.c
index 05c4f25..3bac8be 100644
--- a/applypatch/imgdiff.c
+++ b/applypatch/imgdiff.c
@@ -408,6 +408,7 @@
p[2] == 0x08 && // deflate compression
p[3] == 0x00) { // no header flags
// 'pos' is the offset of the start of a gzip chunk.
+ size_t chunk_offset = pos;
*num_chunks += 3;
*chunks = realloc(*chunks, *num_chunks * sizeof(ImageChunk));
@@ -453,6 +454,14 @@
strm.avail_out = allocated - curr->len;
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;
+ }
curr->len = allocated - strm.avail_out;
if (strm.avail_out == 0) {
allocated *= 2;
diff --git a/applypatch/imgpatch.c b/applypatch/imgpatch.c
index 33c4487..09b0a73 100644
--- a/applypatch/imgpatch.c
+++ b/applypatch/imgpatch.c
@@ -21,6 +21,7 @@
#include <sys/cdefs.h>
#include <sys/stat.h>
#include <errno.h>
+#include <malloc.h>
#include <unistd.h>
#include <string.h>
diff --git a/asn1_decoder.cpp b/asn1_decoder.cpp
index 7280f74..e7aef78 100644
--- a/asn1_decoder.cpp
+++ b/asn1_decoder.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <malloc.h>
#include <stdint.h>
#include <string.h>
diff --git a/common.h b/common.h
index 768f499..b818ceb 100644
--- a/common.h
+++ b/common.h
@@ -17,6 +17,7 @@
#ifndef RECOVERY_COMMON_H
#define RECOVERY_COMMON_H
+#include <stdbool.h>
#include <stdio.h>
#include <stdarg.h>
@@ -39,6 +40,7 @@
#define STRINGIFY(x) #x
#define EXPAND(x) STRINGIFY(x)
+extern bool modified_flash;
typedef struct fstab_rec Volume;
// fopen a file, mounting volumes and making parent dirs as necessary.
@@ -46,6 +48,8 @@
void ui_print(const char* format, ...);
+bool is_ro_debuggable();
+
#ifdef __cplusplus
}
#endif
diff --git a/default_device.cpp b/default_device.cpp
index 97806ac..a971866 100644
--- a/default_device.cpp
+++ b/default_device.cpp
@@ -14,74 +14,9 @@
* limitations under the License.
*/
-#include <linux/input.h>
-
-#include "common.h"
#include "device.h"
#include "screen_ui.h"
-static const char* HEADERS[] = { "Volume up/down to move highlight;",
- "enter button to select.",
- "",
- NULL };
-
-static const char* ITEMS[] = {"reboot system now",
- "apply update from ADB",
- "wipe data/factory reset",
- "wipe cache partition",
- "reboot to bootloader",
- "power down",
- "view recovery logs",
- NULL };
-
-class DefaultDevice : public Device {
- public:
- DefaultDevice() :
- ui(new ScreenRecoveryUI) {
- }
-
- RecoveryUI* GetUI() { return ui; }
-
- int HandleMenuKey(int key, int visible) {
- if (visible) {
- switch (key) {
- case KEY_DOWN:
- case KEY_VOLUMEDOWN:
- return kHighlightDown;
-
- case KEY_UP:
- case KEY_VOLUMEUP:
- return kHighlightUp;
-
- case KEY_ENTER:
- case KEY_POWER:
- return kInvokeItem;
- }
- }
-
- return kNoAction;
- }
-
- BuiltinAction InvokeMenuItem(int menu_position) {
- switch (menu_position) {
- case 0: return REBOOT;
- case 1: return APPLY_ADB_SIDELOAD;
- case 2: return WIPE_DATA;
- case 3: return WIPE_CACHE;
- case 4: return REBOOT_BOOTLOADER;
- case 5: return SHUTDOWN;
- case 6: return READ_RECOVERY_LASTLOG;
- default: return NO_ACTION;
- }
- }
-
- const char* const* GetMenuHeaders() { return HEADERS; }
- const char* const* GetMenuItems() { return ITEMS; }
-
- private:
- RecoveryUI* ui;
-};
-
Device* make_device() {
- return new DefaultDevice();
+ return new Device(new ScreenRecoveryUI);
}
diff --git a/device.cpp b/device.cpp
new file mode 100644
index 0000000..fd1a987
--- /dev/null
+++ b/device.cpp
@@ -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.
+ */
+
+#include "device.h"
+
+static const char* MENU_ITEMS[] = {
+ "Reboot system now",
+ "Reboot to bootloader",
+ "Apply update from ADB",
+ "Apply update from SD card",
+ "Wipe data/factory reset",
+ "Wipe cache partition",
+ "Mount /system",
+ "View recovery logs",
+ "Power off",
+ NULL
+};
+
+const char* const* Device::GetMenuItems() {
+ return MENU_ITEMS;
+}
+
+Device::BuiltinAction Device::InvokeMenuItem(int menu_position) {
+ switch (menu_position) {
+ case 0: return REBOOT;
+ case 1: return REBOOT_BOOTLOADER;
+ case 2: return APPLY_ADB_SIDELOAD;
+ case 3: return APPLY_SDCARD;
+ case 4: return WIPE_DATA;
+ case 5: return WIPE_CACHE;
+ case 6: return MOUNT_SYSTEM;
+ case 7: return VIEW_RECOVERY_LOGS;
+ case 8: return SHUTDOWN;
+ default: return NO_ACTION;
+ }
+}
+
+int Device::HandleMenuKey(int key, int visible) {
+ if (!visible) {
+ return kNoAction;
+ }
+
+ switch (key) {
+ case KEY_DOWN:
+ case KEY_VOLUMEDOWN:
+ return kHighlightDown;
+
+ case KEY_UP:
+ case KEY_VOLUMEUP:
+ return kHighlightUp;
+
+ case KEY_ENTER:
+ case KEY_POWER:
+ return kInvokeItem;
+
+ default:
+ // If you have all of the above buttons, any other buttons
+ // are ignored. Otherwise, any button cycles the highlight.
+ return ui_->HasThreeButtons() ? kNoAction : kHighlightDown;
+ }
+}
diff --git a/device.h b/device.h
index 8ff4ec0..f74b6b0 100644
--- a/device.h
+++ b/device.h
@@ -21,29 +21,20 @@
class Device {
public:
+ Device(RecoveryUI* ui) : ui_(ui) { }
virtual ~Device() { }
// Called to obtain the UI object that should be used to display
// the recovery user interface for this device. You should not
// have called Init() on the UI object already, the caller will do
// that after this method returns.
- virtual RecoveryUI* GetUI() = 0;
+ virtual RecoveryUI* GetUI() { return ui_; }
// Called when recovery starts up (after the UI has been obtained
// and initialized and after the arguments have been parsed, but
// before anything else).
virtual void StartRecovery() { };
- // enum KeyAction { NONE, TOGGLE, REBOOT };
-
- // // Called in the input thread when a new key (key_code) is
- // // pressed. *key_pressed is an array of KEY_MAX+1 bytes
- // // indicating which other keys are already pressed. Return a
- // // KeyAction to indicate action should be taken immediately.
- // // These actions happen when recovery is not waiting for input
- // // (eg, in the midst of installing a package).
- // virtual KeyAction CheckImmediateKeyAction(volatile char* key_pressed, int key_code) = 0;
-
// Called from the main thread when recovery is at the main menu
// and waiting for input, and a key is pressed. (Note that "at"
// the main menu does not necessarily mean the menu is visible;
@@ -63,12 +54,26 @@
// - invoke the highlighted item (kInvokeItem)
// - do nothing (kNoAction)
// - invoke a specific action (a menu position: any non-negative number)
- virtual int HandleMenuKey(int key, int visible) = 0;
+ virtual int HandleMenuKey(int key, int visible);
- enum BuiltinAction { NO_ACTION, REBOOT, APPLY_EXT,
- APPLY_CACHE, // APPLY_CACHE is deprecated; has no effect
- APPLY_ADB_SIDELOAD, WIPE_DATA, WIPE_CACHE,
- REBOOT_BOOTLOADER, SHUTDOWN, READ_RECOVERY_LASTLOG };
+ enum BuiltinAction {
+ NO_ACTION = 0,
+ REBOOT = 1,
+ APPLY_SDCARD = 2,
+ // APPLY_CACHE was 3.
+ APPLY_ADB_SIDELOAD = 4,
+ WIPE_DATA = 5,
+ WIPE_CACHE = 6,
+ REBOOT_BOOTLOADER = 7,
+ SHUTDOWN = 8,
+ VIEW_RECOVERY_LOGS = 9,
+ MOUNT_SYSTEM = 10,
+ };
+
+ // Return the list of menu items (an array of strings,
+ // NULL-terminated). The menu_position passed to InvokeMenuItem
+ // will correspond to the indexes into this array.
+ virtual const char* const* GetMenuItems();
// Perform a recovery action selected from the menu.
// 'menu_position' will be the item number of the selected menu
@@ -79,31 +84,26 @@
// builtin actions, you can just return the corresponding enum
// value. If it is an action specific to your device, you
// actually perform it here and return NO_ACTION.
- virtual BuiltinAction InvokeMenuItem(int menu_position) = 0;
+ virtual BuiltinAction InvokeMenuItem(int menu_position);
static const int kNoAction = -1;
static const int kHighlightUp = -2;
static const int kHighlightDown = -3;
static const int kInvokeItem = -4;
- // Called when we do a wipe data/factory reset operation (either via a
- // reboot from the main system with the --wipe_data flag, or when the
- // user boots into recovery manually and selects the option from the
- // menu.) Can perform whatever device-specific wiping actions are
- // needed. Return 0 on success. The userdata and cache partitions
- // are erased AFTER this returns (whether it returns success or not).
- virtual int WipeData() { return 0; }
+ // Called before and after we do a wipe data/factory reset operation,
+ // either via a reboot from the main system with the --wipe_data flag,
+ // or when the user boots into recovery image manually and selects the
+ // option from the menu, to perform whatever device-specific wiping
+ // actions are needed.
+ // Return true on success; returning false from PreWipeData will prevent
+ // the regular wipe, and returning false from PostWipeData will cause
+ // the wipe to be considered a failure.
+ virtual bool PreWipeData() { return true; }
+ virtual bool PostWipeData() { return true; }
- // Return the headers (an array of strings, one per line,
- // NULL-terminated) for the main menu. Typically these tell users
- // what to push to move the selection and invoke the selected
- // item.
- virtual const char* const* GetMenuHeaders() = 0;
-
- // Return the list of menu items (an array of strings,
- // NULL-terminated). The menu_position passed to InvokeMenuItem
- // will correspond to the indexes into this array.
- virtual const char* const* GetMenuItems() = 0;
+ private:
+ RecoveryUI* ui_;
};
// The device-specific library must define this function (or the
diff --git a/edify/Android.mk b/edify/Android.mk
index 61ed6fa..03c04e4 100644
--- a/edify/Android.mk
+++ b/edify/Android.mk
@@ -7,9 +7,10 @@
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++.
-edify_cflags := -x 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
#
# Build the host-side command line tool
diff --git a/edify/main.c b/edify/main.c
index b3fad53..b1baa0b 100644
--- a/edify/main.c
+++ b/edify/main.c
@@ -25,13 +25,12 @@
int expect(const char* expr_str, const char* expected, int* errors) {
Expr* e;
- int error;
char* result;
printf(".");
int error_count = parse_string(expr_str, &e, &error_count);
- if (error > 0 || error_count > 0) {
+ if (error_count > 0) {
printf("error parsing \"%s\" (%d errors)\n",
expr_str, error_count);
++*errors;
diff --git a/etc/init.rc b/etc/init.rc
index c78a44a..6c07c60 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -1,13 +1,6 @@
import /init.recovery.${ro.hardware}.rc
on early-init
- # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls.
- write /sys/fs/selinux/checkreqprot 0
-
- # Set the security context for the init process.
- # This should occur before anything else (e.g. ueventd) is started.
- setcon u:r:init:s0
-
start ueventd
start healthd
diff --git a/fuse_sdcard_provider.c b/fuse_sdcard_provider.c
index 19fb52d..4565c7b 100644
--- a/fuse_sdcard_provider.c
+++ b/fuse_sdcard_provider.c
@@ -16,6 +16,7 @@
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sys/mount.h>
@@ -35,19 +36,17 @@
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;
- if (lseek(fd->fd, block * fd->block_size, SEEK_SET) < 0) {
- printf("seek on sdcard failed: %s\n", strerror(errno));
+ off64_t offset = ((off64_t) block) * fd->block_size;
+ if (TEMP_FAILURE_RETRY(lseek64(fd->fd, offset, SEEK_SET)) == -1) {
+ fprintf(stderr, "seek on sdcard failed: %s\n", strerror(errno));
return -EIO;
}
while (fetch_size > 0) {
- ssize_t r = read(fd->fd, buffer, fetch_size);
- if (r < 0) {
- if (r != -EINTR) {
- printf("read on sdcard failed: %s\n", strerror(errno));
- return -EIO;
- }
- r = 0;
+ ssize_t r = TEMP_FAILURE_RETRY(read(fd->fd, buffer, fetch_size));
+ if (r == -1) {
+ fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno));
+ return -EIO;
}
fetch_size -= r;
buffer += r;
diff --git a/fuse_sdcard_provider.h b/fuse_sdcard_provider.h
index dc2982c..dbfbcd5 100644
--- a/fuse_sdcard_provider.h
+++ b/fuse_sdcard_provider.h
@@ -17,7 +17,13 @@
#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
+
#endif
diff --git a/fuse_sideload.c b/fuse_sideload.c
index ab91def..48e6cc5 100644
--- a/fuse_sideload.c
+++ b/fuse_sideload.c
@@ -53,6 +53,7 @@
#include <string.h>
#include <sys/inotify.h>
#include <sys/mount.h>
+#include <sys/param.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/statfs.h>
@@ -105,27 +106,52 @@
vec[0].iov_base = &hdr;
vec[0].iov_len = sizeof(hdr);
- vec[1].iov_base = data;
+ vec[1].iov_base = /* const_cast */(void*)(data);
vec[1].iov_len = len;
res = writev(fd->ffd, vec, 2);
if (res < 0) {
- printf("*** REPLY FAILED *** %d\n", errno);
+ printf("*** REPLY FAILED *** %s\n", strerror(errno));
}
}
static int handle_init(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
const struct fuse_init_in* req = data;
struct fuse_init_out out;
+ size_t fuse_struct_size;
+
+
+ /* Kernel 2.6.16 is the first stable kernel with struct fuse_init_out
+ * defined (fuse version 7.6). The structure is the same from 7.6 through
+ * 7.22. Beginning with 7.23, the structure increased in size and added
+ * new parameters.
+ */
+ if (req->major != FUSE_KERNEL_VERSION || req->minor < 6) {
+ printf("Fuse kernel version mismatch: Kernel version %d.%d, Expected at least %d.6",
+ req->major, req->minor, FUSE_KERNEL_VERSION);
+ return -1;
+ }
+
+ out.minor = MIN(req->minor, FUSE_KERNEL_MINOR_VERSION);
+ fuse_struct_size = sizeof(out);
+#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE)
+ /* FUSE_KERNEL_VERSION >= 23. */
+
+ /* If the kernel only works on minor revs older than or equal to 22,
+ * then use the older structure size since this code only uses the 7.22
+ * version of the structure. */
+ if (req->minor <= 22) {
+ fuse_struct_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
+ }
+#endif
out.major = FUSE_KERNEL_VERSION;
- out.minor = FUSE_KERNEL_MINOR_VERSION;
out.max_readahead = req->max_readahead;
out.flags = 0;
out.max_background = 32;
out.congestion_threshold = 32;
out.max_write = 4096;
- fuse_reply(fd, hdr->unique, &out, sizeof(out));
+ fuse_reply(fd, hdr->unique, &out, fuse_struct_size);
return NO_STATUS;
}
@@ -404,7 +430,7 @@
char opts[256];
snprintf(opts, sizeof(opts),
- ("fd=%d,user_id=%d,group_id=%d,max_read=%zu,"
+ ("fd=%d,user_id=%d,group_id=%d,max_read=%u,"
"allow_other,rootmode=040000"),
fd.ffd, fd.uid, fd.gid, block_size);
@@ -416,14 +442,12 @@
}
uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX*8];
for (;;) {
- ssize_t len = read(fd.ffd, request_buffer, sizeof(request_buffer));
- if (len < 0) {
- if (errno != EINTR) {
- perror("read request");
- if (errno == ENODEV) {
- result = -1;
- break;
- }
+ ssize_t len = TEMP_FAILURE_RETRY(read(fd.ffd, request_buffer, sizeof(request_buffer)));
+ if (len == -1) {
+ perror("read request");
+ if (errno == ENODEV) {
+ result = -1;
+ break;
}
continue;
}
@@ -482,7 +506,7 @@
outhdr.len = sizeof(outhdr);
outhdr.error = result;
outhdr.unique = hdr->unique;
- write(fd.ffd, &outhdr, sizeof(outhdr));
+ TEMP_FAILURE_RETRY(write(fd.ffd, &outhdr, sizeof(outhdr)));
}
}
diff --git a/fuse_sideload.h b/fuse_sideload.h
index c0b16ef..f9e3bf0 100644
--- a/fuse_sideload.h
+++ b/fuse_sideload.h
@@ -17,6 +17,10 @@
#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"
@@ -35,4 +39,6 @@
int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
uint64_t file_size, uint32_t block_size);
+__END_DECLS
+
#endif
diff --git a/install.cpp b/install.cpp
index 9db5640..c7d382f 100644
--- a/install.cpp
+++ b/install.cpp
@@ -18,6 +18,7 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
+#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -47,7 +48,7 @@
// If the package contains an update binary, extract it and run it.
static int
-try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) {
+try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) {
const ZipEntry* binary_entry =
mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);
if (binary_entry == NULL) {
@@ -87,7 +88,7 @@
// fill up the next <frac> part of of the progress bar
// over <secs> seconds. If <secs> is zero, use
// set_progress commands to manually control the
- // progress of this segment of the bar
+ // progress of this segment of the bar.
//
// set_progress <frac>
// <frac> should be between 0.0 and 1.0; sets the
@@ -106,6 +107,18 @@
// ui_print <string>
// display <string> on the screen.
//
+ // wipe_cache
+ // a wipe of cache will be performed following a successful
+ // installation.
+ //
+ // clear_display
+ // turn off the text display.
+ //
+ // enable_reboot
+ // packages can explicitly request that they want the user
+ // to be able to reboot during installation (useful for
+ // debugging packages that don't exit).
+ //
// - the name of the package zip file.
//
@@ -128,7 +141,7 @@
}
close(pipefd[1]);
- *wipe_cache = 0;
+ *wipe_cache = false;
char buffer[1024];
FILE* from_child = fdopen(pipefd[0], "r");
@@ -157,7 +170,7 @@
}
fflush(stdout);
} else if (strcmp(command, "wipe_cache") == 0) {
- *wipe_cache = 1;
+ *wipe_cache = true;
} else if (strcmp(command, "clear_display") == 0) {
ui->SetBackground(RecoveryUI::NONE);
} else if (strcmp(command, "enable_reboot") == 0) {
@@ -182,7 +195,7 @@
}
static int
-really_install_package(const char *path, int* wipe_cache, bool needs_mount)
+really_install_package(const char *path, bool* wipe_cache, bool needs_mount)
{
ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
ui->Print("Finding update package...\n");
@@ -252,9 +265,11 @@
}
int
-install_package(const char* path, int* wipe_cache, const char* install_file,
+install_package(const char* path, bool* wipe_cache, const char* install_file,
bool needs_mount)
{
+ modified_flash = true;
+
FILE* install_log = fopen_path(install_file, "w");
if (install_log) {
fputs(path, install_log);
diff --git a/install.h b/install.h
index 53c0d31..680499d 100644
--- a/install.h
+++ b/install.h
@@ -27,7 +27,7 @@
// 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, int* wipe_cache,
+int install_package(const char* root_path, bool* wipe_cache,
const char* install_file, bool needs_mount);
#ifdef __cplusplus
diff --git a/minadbd/Android.mk b/minadbd/Android.mk
index 04956d8..a7a3e08 100644
--- a/minadbd/Android.mk
+++ b/minadbd/Android.mk
@@ -1,32 +1,37 @@
# Copyright 2005 The Android Open Source Project
-#
-# Android.mk for adb
-#
LOCAL_PATH:= $(call my-dir)
-# minadbd library
-# =========================================================
+minadbd_cflags := \
+ -Wall -Werror \
+ -Wno-unused-parameter \
+ -Wno-missing-field-initializers \
+ -DADB_HOST=0 \
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
- adb.c \
- fdevent.c \
- fuse_adb_provider.c \
- transport.c \
- transport_usb.c \
- sockets.c \
- services.c \
- usb_linux_client.c \
- utils.c
-
-LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter
-LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
-LOCAL_C_INCLUDES += bootable/recovery
+ adb_main.cpp \
+ fuse_adb_provider.cpp \
+ services.cpp \
LOCAL_MODULE := libminadbd
-
-LOCAL_STATIC_LIBRARIES := libfusesideload libcutils libc
+LOCAL_CFLAGS := $(minadbd_cflags)
+LOCAL_CONLY_FLAGS := -Wimplicit-function-declaration
+LOCAL_C_INCLUDES := bootable/recovery system/core/adb
+LOCAL_WHOLE_STATIC_LIBRARIES := libadbd
+LOCAL_STATIC_LIBRARIES := libbase
include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_CLANG := true
+LOCAL_MODULE := minadbd_test
+LOCAL_SRC_FILES := fuse_adb_provider_test.cpp
+LOCAL_CFLAGS := $(minadbd_cflags)
+LOCAL_C_INCLUDES := $(LOCAL_PATH) system/core/adb
+LOCAL_STATIC_LIBRARIES := libminadbd
+LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
+
+include $(BUILD_NATIVE_TEST)
diff --git a/minadbd/README.txt b/minadbd/README.txt
index c9df484..e69dc87 100644
--- a/minadbd/README.txt
+++ b/minadbd/README.txt
@@ -1,39 +1,8 @@
-The contents of this directory are copied from system/core/adb, with
-the following changes:
+minadbd is now mostly built from libadbd. The fuse features are unique to
+minadbd, and services.c has been modified as follows:
-adb.c
- - much support for host mode and non-linux OS's stripped out; this
- version only runs as adbd on the device.
- - always setuid/setgid's itself to the shell user
- - only uses USB transport
- - references to JDWP removed
- - main() removed
- - all ADB_HOST and win32 code removed
- - removed listeners, logging code, background server (for host)
-
-adb.h
- - minor changes to match adb.c changes
-
-sockets.c
- - references to JDWP removed
- - ADB_HOST code removed
-
-services.c
- - all services except echo_service (which is commented out) removed
+ - all services removed
- all host mode support removed
- sideload_service() added; this is the only service supported. It
receives a single blob of data, writes it to a fixed filename, and
makes the process exit.
-
-Android.mk
- - only builds in adbd mode; builds as static library instead of a
- standalone executable.
-
-sysdeps.h
- - changes adb_creat() to use O_NOFOLLOW
-
-transport.c
- - removed ADB_HOST code
-
-transport_usb.c
- - removed ADB_HOST code
diff --git a/minadbd/adb.c b/minadbd/adb.c
deleted file mode 100644
index 127d072..0000000
--- a/minadbd/adb.c
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define TRACE_TAG TRACE_ADB
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <string.h>
-#include <time.h>
-#include <sys/time.h>
-
-#include "sysdeps.h"
-#include "adb.h"
-
-#include <private/android_filesystem_config.h>
-
-#if ADB_TRACE
-ADB_MUTEX_DEFINE( D_lock );
-#endif
-
-int HOST = 0;
-
-static const char *adb_device_banner = "sideload";
-
-void fatal(const char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- fprintf(stderr, "error: ");
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
- va_end(ap);
- exit(-1);
-}
-
-void fatal_errno(const char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- fprintf(stderr, "error: %s: ", strerror(errno));
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
- va_end(ap);
- exit(-1);
-}
-
-int adb_trace_mask;
-
-/* read a comma/space/colum/semi-column separated list of tags
- * from the ADB_TRACE environment variable and build the trace
- * mask from it. note that '1' and 'all' are special cases to
- * enable all tracing
- */
-void adb_trace_init(void)
-{
- const char* p = getenv("ADB_TRACE");
- const char* q;
-
- static const struct {
- const char* tag;
- int flag;
- } tags[] = {
- { "1", 0 },
- { "all", 0 },
- { "adb", TRACE_ADB },
- { "sockets", TRACE_SOCKETS },
- { "packets", TRACE_PACKETS },
- { "rwx", TRACE_RWX },
- { "usb", TRACE_USB },
- { "sync", TRACE_SYNC },
- { "sysdeps", TRACE_SYSDEPS },
- { "transport", TRACE_TRANSPORT },
- { "jdwp", TRACE_JDWP },
- { "services", TRACE_SERVICES },
- { NULL, 0 }
- };
-
- if (p == NULL)
- return;
-
- /* use a comma/column/semi-colum/space separated list */
- while (*p) {
- int len, tagn;
-
- q = strpbrk(p, " ,:;");
- if (q == NULL) {
- q = p + strlen(p);
- }
- len = q - p;
-
- for (tagn = 0; tags[tagn].tag != NULL; tagn++)
- {
- int taglen = strlen(tags[tagn].tag);
-
- if (len == taglen && !memcmp(tags[tagn].tag, p, len) )
- {
- int flag = tags[tagn].flag;
- if (flag == 0) {
- adb_trace_mask = ~0;
- return;
- }
- adb_trace_mask |= (1 << flag);
- break;
- }
- }
- p = q;
- if (*p)
- p++;
- }
-}
-
-
-apacket *get_apacket(void)
-{
- apacket *p = malloc(sizeof(apacket));
- if(p == 0) fatal("failed to allocate an apacket");
- memset(p, 0, sizeof(apacket) - MAX_PAYLOAD);
- return p;
-}
-
-void put_apacket(apacket *p)
-{
- free(p);
-}
-
-void handle_online(void)
-{
- D("adb: online\n");
-}
-
-void handle_offline(atransport *t)
-{
- D("adb: offline\n");
- //Close the associated usb
- run_transport_disconnects(t);
-}
-
-#if TRACE_PACKETS
-#define DUMPMAX 32
-void print_packet(const char *label, apacket *p)
-{
- char *tag;
- char *x;
- unsigned count;
-
- switch(p->msg.command){
- case A_SYNC: tag = "SYNC"; break;
- case A_CNXN: tag = "CNXN" ; break;
- case A_OPEN: tag = "OPEN"; break;
- case A_OKAY: tag = "OKAY"; break;
- case A_CLSE: tag = "CLSE"; break;
- case A_WRTE: tag = "WRTE"; break;
- default: tag = "????"; break;
- }
-
- fprintf(stderr, "%s: %s %08x %08x %04x \"",
- label, tag, p->msg.arg0, p->msg.arg1, p->msg.data_length);
- count = p->msg.data_length;
- x = (char*) p->data;
- if(count > DUMPMAX) {
- count = DUMPMAX;
- tag = "\n";
- } else {
- tag = "\"\n";
- }
- while(count-- > 0){
- if((*x >= ' ') && (*x < 127)) {
- fputc(*x, stderr);
- } else {
- fputc('.', stderr);
- }
- x++;
- }
- fprintf(stderr, tag);
-}
-#endif
-
-static void send_ready(unsigned local, unsigned remote, atransport *t)
-{
- D("Calling send_ready \n");
- apacket *p = get_apacket();
- p->msg.command = A_OKAY;
- p->msg.arg0 = local;
- p->msg.arg1 = remote;
- send_packet(p, t);
-}
-
-static void send_close(unsigned local, unsigned remote, atransport *t)
-{
- D("Calling send_close \n");
- apacket *p = get_apacket();
- p->msg.command = A_CLSE;
- p->msg.arg0 = local;
- p->msg.arg1 = remote;
- send_packet(p, t);
-}
-
-static void send_connect(atransport *t)
-{
- D("Calling send_connect \n");
- apacket *cp = get_apacket();
- cp->msg.command = A_CNXN;
- cp->msg.arg0 = A_VERSION;
- cp->msg.arg1 = MAX_PAYLOAD;
- snprintf((char*) cp->data, sizeof cp->data, "%s::",
- HOST ? "host" : adb_device_banner);
- cp->msg.data_length = strlen((char*) cp->data) + 1;
- send_packet(cp, t);
-}
-
-void parse_banner(char *banner, atransport *t)
-{
- char *type, *product, *end;
-
- D("parse_banner: %s\n", banner);
- type = banner;
- product = strchr(type, ':');
- if(product) {
- *product++ = 0;
- } else {
- product = "";
- }
-
- /* remove trailing ':' */
- end = strchr(product, ':');
- if(end) *end = 0;
-
- /* save product name in device structure */
- if (t->product == NULL) {
- t->product = strdup(product);
- } else if (strcmp(product, t->product) != 0) {
- free(t->product);
- t->product = strdup(product);
- }
-
- if(!strcmp(type, "bootloader")){
- D("setting connection_state to CS_BOOTLOADER\n");
- t->connection_state = CS_BOOTLOADER;
- update_transports();
- return;
- }
-
- if(!strcmp(type, "device")) {
- D("setting connection_state to CS_DEVICE\n");
- t->connection_state = CS_DEVICE;
- update_transports();
- return;
- }
-
- if(!strcmp(type, "recovery")) {
- D("setting connection_state to CS_RECOVERY\n");
- t->connection_state = CS_RECOVERY;
- update_transports();
- return;
- }
-
- if(!strcmp(type, "sideload")) {
- D("setting connection_state to CS_SIDELOAD\n");
- t->connection_state = CS_SIDELOAD;
- update_transports();
- return;
- }
-
- t->connection_state = CS_HOST;
-}
-
-void handle_packet(apacket *p, atransport *t)
-{
- asocket *s;
-
- D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0],
- ((char*) (&(p->msg.command)))[1],
- ((char*) (&(p->msg.command)))[2],
- ((char*) (&(p->msg.command)))[3]);
- print_packet("recv", p);
-
- switch(p->msg.command){
- case A_SYNC:
- if(p->msg.arg0){
- send_packet(p, t);
- if(HOST) send_connect(t);
- } else {
- t->connection_state = CS_OFFLINE;
- handle_offline(t);
- send_packet(p, t);
- }
- return;
-
- case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */
- /* XXX verify version, etc */
- if(t->connection_state != CS_OFFLINE) {
- t->connection_state = CS_OFFLINE;
- handle_offline(t);
- }
- parse_banner((char*) p->data, t);
- handle_online();
- if(!HOST) send_connect(t);
- break;
-
- case A_OPEN: /* OPEN(local-id, 0, "destination") */
- if(t->connection_state != CS_OFFLINE) {
- char *name = (char*) p->data;
- name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;
- s = create_local_service_socket(name);
- if(s == 0) {
- send_close(0, p->msg.arg0, t);
- } else {
- s->peer = create_remote_socket(p->msg.arg0, t);
- s->peer->peer = s;
- send_ready(s->id, s->peer->id, t);
- s->ready(s);
- }
- }
- break;
-
- case A_OKAY: /* READY(local-id, remote-id, "") */
- if(t->connection_state != CS_OFFLINE) {
- if((s = find_local_socket(p->msg.arg1))) {
- if(s->peer == 0) {
- s->peer = create_remote_socket(p->msg.arg0, t);
- s->peer->peer = s;
- }
- s->ready(s);
- }
- }
- break;
-
- case A_CLSE: /* CLOSE(local-id, remote-id, "") */
- if(t->connection_state != CS_OFFLINE) {
- if((s = find_local_socket(p->msg.arg1))) {
- s->close(s);
- }
- }
- break;
-
- case A_WRTE:
- if(t->connection_state != CS_OFFLINE) {
- if((s = find_local_socket(p->msg.arg1))) {
- unsigned rid = p->msg.arg0;
- p->len = p->msg.data_length;
-
- if(s->enqueue(s, p) == 0) {
- D("Enqueue the socket\n");
- send_ready(s->id, rid, t);
- }
- return;
- }
- }
- break;
-
- default:
- printf("handle_packet: what is %08x?!\n", p->msg.command);
- }
-
- put_apacket(p);
-}
-
-static void adb_cleanup(void)
-{
- usb_cleanup();
-}
-
-int adb_main()
-{
- atexit(adb_cleanup);
-#if defined(HAVE_FORKEXEC)
- // No SIGCHLD. Let the service subproc handle its children.
- signal(SIGPIPE, SIG_IGN);
-#endif
-
- init_transport_registration();
-
- // The minimal version of adbd only uses USB.
- if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) {
- // listen on USB
- usb_init();
- }
-
- D("Event loop starting\n");
-
- fdevent_loop();
-
- usb_cleanup();
-
- return 0;
-}
diff --git a/minadbd/adb.h b/minadbd/adb.h
deleted file mode 100644
index 714868f..0000000
--- a/minadbd/adb.h
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __ADB_H
-#define __ADB_H
-
-#include <limits.h>
-
-#include "transport.h" /* readx(), writex() */
-#include "fdevent.h"
-
-#define MAX_PAYLOAD 4096
-
-#define A_SYNC 0x434e5953
-#define A_CNXN 0x4e584e43
-#define A_OPEN 0x4e45504f
-#define A_OKAY 0x59414b4f
-#define A_CLSE 0x45534c43
-#define A_WRTE 0x45545257
-
-#define A_VERSION 0x01000000 // ADB protocol version
-
-#define ADB_VERSION_MAJOR 1 // Used for help/version information
-#define ADB_VERSION_MINOR 0 // Used for help/version information
-
-#define ADB_SERVER_VERSION 29 // Increment this when we want to force users to start a new adb server
-
-typedef struct amessage amessage;
-typedef struct apacket apacket;
-typedef struct asocket asocket;
-typedef struct aservice aservice;
-typedef struct atransport atransport;
-typedef struct adisconnect adisconnect;
-typedef struct usb_handle usb_handle;
-
-struct amessage {
- unsigned command; /* command identifier constant */
- unsigned arg0; /* first argument */
- unsigned arg1; /* second argument */
- unsigned data_length; /* length of payload (0 is allowed) */
- unsigned data_check; /* checksum of data payload */
- unsigned magic; /* command ^ 0xffffffff */
-};
-
-struct apacket
-{
- apacket *next;
-
- unsigned len;
- unsigned char *ptr;
-
- amessage msg;
- unsigned char data[MAX_PAYLOAD];
-};
-
-/* An asocket represents one half of a connection between a local and
-** remote entity. A local asocket is bound to a file descriptor. A
-** remote asocket is bound to the protocol engine.
-*/
-struct asocket {
- /* chain pointers for the local/remote list of
- ** asockets that this asocket lives in
- */
- asocket *next;
- asocket *prev;
-
- /* the unique identifier for this asocket
- */
- unsigned id;
-
- /* flag: set when the socket's peer has closed
- ** but packets are still queued for delivery
- */
- int closing;
-
- /* the asocket we are connected to
- */
-
- asocket *peer;
-
- /* For local asockets, the fde is used to bind
- ** us to our fd event system. For remote asockets
- ** these fields are not used.
- */
- fdevent fde;
- int fd;
-
- /* queue of apackets waiting to be written
- */
- apacket *pkt_first;
- apacket *pkt_last;
-
- /* enqueue is called by our peer when it has data
- ** for us. It should return 0 if we can accept more
- ** data or 1 if not. If we return 1, we must call
- ** peer->ready() when we once again are ready to
- ** receive data.
- */
- int (*enqueue)(asocket *s, apacket *pkt);
-
- /* ready is called by the peer when it is ready for
- ** us to send data via enqueue again
- */
- void (*ready)(asocket *s);
-
- /* close is called by the peer when it has gone away.
- ** we are not allowed to make any further calls on the
- ** peer once our close method is called.
- */
- void (*close)(asocket *s);
-
- /* socket-type-specific extradata */
- void *extra;
-
- /* A socket is bound to atransport */
- atransport *transport;
-};
-
-
-/* the adisconnect structure is used to record a callback that
-** will be called whenever a transport is disconnected (e.g. by the user)
-** this should be used to cleanup objects that depend on the
-** transport (e.g. remote sockets, etc...)
-*/
-struct adisconnect
-{
- void (*func)(void* opaque, atransport* t);
- void* opaque;
- adisconnect* next;
- adisconnect* prev;
-};
-
-
-/* a transport object models the connection to a remote device or emulator
-** there is one transport per connected device/emulator. a "local transport"
-** connects through TCP (for the emulator), while a "usb transport" through
-** USB (for real devices)
-**
-** note that kTransportHost doesn't really correspond to a real transport
-** object, it's a special value used to indicate that a client wants to
-** connect to a service implemented within the ADB server itself.
-*/
-typedef enum transport_type {
- kTransportUsb,
- kTransportLocal,
- kTransportAny,
- kTransportHost,
-} transport_type;
-
-struct atransport
-{
- atransport *next;
- atransport *prev;
-
- int (*read_from_remote)(apacket *p, atransport *t);
- int (*write_to_remote)(apacket *p, atransport *t);
- void (*close)(atransport *t);
- void (*kick)(atransport *t);
-
- int fd;
- int transport_socket;
- fdevent transport_fde;
- int ref_count;
- unsigned sync_token;
- int connection_state;
- transport_type type;
-
- /* usb handle or socket fd as needed */
- usb_handle *usb;
- int sfd;
-
- /* used to identify transports for clients */
- char *serial;
- char *product;
- int adb_port; // Use for emulators (local transport)
-
- /* a list of adisconnect callbacks called when the transport is kicked */
- int kicked;
- adisconnect disconnects;
-};
-
-
-void print_packet(const char *label, apacket *p);
-
-asocket *find_local_socket(unsigned id);
-void install_local_socket(asocket *s);
-void remove_socket(asocket *s);
-void close_all_sockets(atransport *t);
-
-#define LOCAL_CLIENT_PREFIX "emulator-"
-
-asocket *create_local_socket(int fd);
-asocket *create_local_service_socket(const char *destination);
-
-asocket *create_remote_socket(unsigned id, atransport *t);
-void connect_to_remote(asocket *s, const char *destination);
-void connect_to_smartsocket(asocket *s);
-
-void fatal(const char *fmt, ...);
-void fatal_errno(const char *fmt, ...);
-
-void handle_packet(apacket *p, atransport *t);
-void send_packet(apacket *p, atransport *t);
-
-void get_my_path(char *s, size_t maxLen);
-int launch_server(int server_port);
-int adb_main();
-
-
-/* transports are ref-counted
-** get_device_transport does an acquire on your behalf before returning
-*/
-void init_transport_registration(void);
-int list_transports(char *buf, size_t bufsize);
-void update_transports(void);
-
-asocket* create_device_tracker(void);
-
-/* Obtain a transport from the available transports.
-** If state is != CS_ANY, only transports in that state are considered.
-** If serial is non-NULL then only the device with that serial will be chosen.
-** If no suitable transport is found, error is set.
-*/
-atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char **error_out);
-void add_transport_disconnect( atransport* t, adisconnect* dis );
-void remove_transport_disconnect( atransport* t, adisconnect* dis );
-void run_transport_disconnects( atransport* t );
-void kick_transport( atransport* t );
-
-/* initialize a transport object's func pointers and state */
-#if ADB_HOST
-int get_available_local_transport_index();
-#endif
-void init_usb_transport(atransport *t, usb_handle *usb, int state);
-
-/* for MacOS X cleanup */
-void close_usb_devices();
-
-/* these should only be used for the "adb disconnect" command */
-void unregister_transport(atransport *t);
-void unregister_all_tcp_transports();
-
-void register_usb_transport(usb_handle *h, const char *serial, unsigned writeable);
-
-/* this should only be used for transports with connection_state == CS_NOPERM */
-void unregister_usb_transport(usb_handle *usb);
-
-atransport *find_transport(const char *serial);
-#if ADB_HOST
-atransport* find_emulator_transport_by_adb_port(int adb_port);
-#endif
-
-int service_to_fd(const char *name);
-#if ADB_HOST
-asocket *host_service_to_socket(const char* name, const char *serial);
-#endif
-
-#if !ADB_HOST
-typedef enum {
- BACKUP,
- RESTORE
-} BackupOperation;
-int backup_service(BackupOperation operation, char* args);
-void framebuffer_service(int fd, void *cookie);
-void log_service(int fd, void *cookie);
-void remount_service(int fd, void *cookie);
-char * get_log_file_path(const char * log_name);
-#endif
-
-/* packet allocator */
-apacket *get_apacket(void);
-void put_apacket(apacket *p);
-
-int check_header(apacket *p);
-int check_data(apacket *p);
-
-/* define ADB_TRACE to 1 to enable tracing support, or 0 to disable it */
-
-#define ADB_TRACE 1
-
-/* IMPORTANT: if you change the following list, don't
- * forget to update the corresponding 'tags' table in
- * the adb_trace_init() function implemented in adb.c
- */
-typedef enum {
- TRACE_ADB = 0, /* 0x001 */
- TRACE_SOCKETS,
- TRACE_PACKETS,
- TRACE_TRANSPORT,
- TRACE_RWX, /* 0x010 */
- TRACE_USB,
- TRACE_SYNC,
- TRACE_SYSDEPS,
- TRACE_JDWP, /* 0x100 */
- TRACE_SERVICES,
-} AdbTrace;
-
-#if ADB_TRACE
-
- extern int adb_trace_mask;
- extern unsigned char adb_trace_output_count;
- void adb_trace_init(void);
-
-# define ADB_TRACING ((adb_trace_mask & (1 << TRACE_TAG)) != 0)
-
- /* you must define TRACE_TAG before using this macro */
-# define D(...) \
- do { \
- if (ADB_TRACING) { \
- int save_errno = errno; \
- adb_mutex_lock(&D_lock); \
- fprintf(stderr, "%s::%s():", \
- __FILE__, __FUNCTION__); \
- errno = save_errno; \
- fprintf(stderr, __VA_ARGS__ ); \
- fflush(stderr); \
- adb_mutex_unlock(&D_lock); \
- errno = save_errno; \
- } \
- } while (0)
-# define DR(...) \
- do { \
- if (ADB_TRACING) { \
- int save_errno = errno; \
- adb_mutex_lock(&D_lock); \
- errno = save_errno; \
- fprintf(stderr, __VA_ARGS__ ); \
- fflush(stderr); \
- adb_mutex_unlock(&D_lock); \
- errno = save_errno; \
- } \
- } while (0)
-#else
-# define D(...) ((void)0)
-# define DR(...) ((void)0)
-# define ADB_TRACING 0
-#endif
-
-
-#if !TRACE_PACKETS
-#define print_packet(tag,p) do {} while (0)
-#endif
-
-#if ADB_HOST_ON_TARGET
-/* adb and adbd are coexisting on the target, so use 5038 for adb
- * to avoid conflicting with adbd's usage of 5037
- */
-# define DEFAULT_ADB_PORT 5038
-#else
-# define DEFAULT_ADB_PORT 5037
-#endif
-
-#define DEFAULT_ADB_LOCAL_TRANSPORT_PORT 5555
-
-#define ADB_CLASS 0xff
-#define ADB_SUBCLASS 0x42
-#define ADB_PROTOCOL 0x1
-
-
-void local_init(int port);
-int local_connect(int port);
-int local_connect_arbitrary_ports(int console_port, int adb_port);
-
-/* usb host/client interface */
-void usb_init();
-void usb_cleanup();
-int usb_write(usb_handle *h, const void *data, int len);
-int usb_read(usb_handle *h, void *data, int len);
-int usb_close(usb_handle *h);
-void usb_kick(usb_handle *h);
-
-/* used for USB device detection */
-#if ADB_HOST
-int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol);
-#endif
-
-unsigned host_to_le32(unsigned n);
-int adb_commandline(int argc, char **argv);
-
-int connection_state(atransport *t);
-
-#define CS_ANY -1
-#define CS_OFFLINE 0
-#define CS_BOOTLOADER 1
-#define CS_DEVICE 2
-#define CS_HOST 3
-#define CS_RECOVERY 4
-#define CS_NOPERM 5 /* Insufficient permissions to communicate with the device */
-#define CS_SIDELOAD 6
-#define CS_UNAUTHORIZED 7
-
-extern int HOST;
-extern int SHELL_EXIT_NOTIFY_FD;
-
-#define CHUNK_SIZE (64*1024)
-
-#if !ADB_HOST
-#define USB_ADB_PATH "/dev/android_adb"
-
-#define USB_FFS_ADB_PATH "/dev/usb-ffs/adb/"
-#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH#x
-
-#define USB_FFS_ADB_EP0 USB_FFS_ADB_EP(ep0)
-#define USB_FFS_ADB_OUT USB_FFS_ADB_EP(ep1)
-#define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2)
-#endif
-
-int sendfailmsg(int fd, const char *reason);
-int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s);
-
-#endif
diff --git a/minadbd/adb_main.cpp b/minadbd/adb_main.cpp
new file mode 100644
index 0000000..7fae99a
--- /dev/null
+++ b/minadbd/adb_main.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 <signal.h>
+#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);
+
+ 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.
+ auth_required = false;
+
+ init_transport_registration();
+ usb_init();
+
+ D("Event loop starting\n");
+ fdevent_loop();
+
+ return 0;
+}
diff --git a/minadbd/fdevent.c b/minadbd/fdevent.c
deleted file mode 100644
index 5c374a7..0000000
--- a/minadbd/fdevent.c
+++ /dev/null
@@ -1,695 +0,0 @@
-/* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c
-**
-** Copyright 2006, Brian Swetland <swetland@frotz.net>
-**
-** 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 <sys/ioctl.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include <fcntl.h>
-
-#include <stdarg.h>
-#include <stddef.h>
-
-#include "fdevent.h"
-#include "transport.h"
-#include "sysdeps.h"
-
-
-/* !!! Do not enable DEBUG for the adb that will run as the server:
-** both stdout and stderr are used to communicate between the client
-** and server. Any extra output will cause failures.
-*/
-#define DEBUG 0 /* non-0 will break adb server */
-
-// This socket is used when a subproc shell service exists.
-// It wakes up the fdevent_loop() and cause the correct handling
-// of the shell's pseudo-tty master. I.e. force close it.
-int SHELL_EXIT_NOTIFY_FD = -1;
-
-static void fatal(const char *fn, const char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- fprintf(stderr, "%s:", fn);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- abort();
-}
-
-#define FATAL(x...) fatal(__FUNCTION__, x)
-
-#if DEBUG
-#define D(...) \
- do { \
- adb_mutex_lock(&D_lock); \
- int save_errno = errno; \
- fprintf(stderr, "%s::%s():", __FILE__, __FUNCTION__); \
- errno = save_errno; \
- fprintf(stderr, __VA_ARGS__); \
- adb_mutex_unlock(&D_lock); \
- errno = save_errno; \
- } while(0)
-static void dump_fde(fdevent *fde, const char *info)
-{
- adb_mutex_lock(&D_lock);
- fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
- fde->state & FDE_READ ? 'R' : ' ',
- fde->state & FDE_WRITE ? 'W' : ' ',
- fde->state & FDE_ERROR ? 'E' : ' ',
- info);
- adb_mutex_unlock(&D_lock);
-}
-#else
-#define D(...) ((void)0)
-#define dump_fde(fde, info) do { } while(0)
-#endif
-
-#define FDE_EVENTMASK 0x00ff
-#define FDE_STATEMASK 0xff00
-
-#define FDE_ACTIVE 0x0100
-#define FDE_PENDING 0x0200
-#define FDE_CREATED 0x0400
-
-static void fdevent_plist_enqueue(fdevent *node);
-static void fdevent_plist_remove(fdevent *node);
-static fdevent *fdevent_plist_dequeue(void);
-static void fdevent_subproc_event_func(int fd, unsigned events, void *userdata);
-
-static fdevent list_pending = {
- .next = &list_pending,
- .prev = &list_pending,
-};
-
-static fdevent **fd_table = 0;
-static int fd_table_max = 0;
-
-#ifdef CRAPTASTIC
-//HAVE_EPOLL
-
-#include <sys/epoll.h>
-
-static int epoll_fd = -1;
-
-static void fdevent_init()
-{
- /* XXX: what's a good size for the passed in hint? */
- epoll_fd = epoll_create(256);
-
- if(epoll_fd < 0) {
- perror("epoll_create() failed");
- exit(1);
- }
-
- /* mark for close-on-exec */
- fcntl(epoll_fd, F_SETFD, FD_CLOEXEC);
-}
-
-static void fdevent_connect(fdevent *fde)
-{
- struct epoll_event ev;
-
- memset(&ev, 0, sizeof(ev));
- ev.events = 0;
- ev.data.ptr = fde;
-
-#if 0
- if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
- perror("epoll_ctl() failed\n");
- exit(1);
- }
-#endif
-}
-
-static void fdevent_disconnect(fdevent *fde)
-{
- struct epoll_event ev;
-
- memset(&ev, 0, sizeof(ev));
- ev.events = 0;
- ev.data.ptr = fde;
-
- /* technically we only need to delete if we
- ** were actively monitoring events, but let's
- ** be aggressive and do it anyway, just in case
- ** something's out of sync
- */
- epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev);
-}
-
-static void fdevent_update(fdevent *fde, unsigned events)
-{
- struct epoll_event ev;
- int active;
-
- active = (fde->state & FDE_EVENTMASK) != 0;
-
- memset(&ev, 0, sizeof(ev));
- ev.events = 0;
- ev.data.ptr = fde;
-
- if(events & FDE_READ) ev.events |= EPOLLIN;
- if(events & FDE_WRITE) ev.events |= EPOLLOUT;
- if(events & FDE_ERROR) ev.events |= (EPOLLERR | EPOLLHUP);
-
- fde->state = (fde->state & FDE_STATEMASK) | events;
-
- if(active) {
- /* we're already active. if we're changing to *no*
- ** events being monitored, we need to delete, otherwise
- ** we need to just modify
- */
- if(ev.events) {
- if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fde->fd, &ev)) {
- perror("epoll_ctl() failed\n");
- exit(1);
- }
- } else {
- if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev)) {
- perror("epoll_ctl() failed\n");
- exit(1);
- }
- }
- } else {
- /* we're not active. if we're watching events, we need
- ** to add, otherwise we can just do nothing
- */
- if(ev.events) {
- if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
- perror("epoll_ctl() failed\n");
- exit(1);
- }
- }
- }
-}
-
-static void fdevent_process()
-{
- struct epoll_event events[256];
- fdevent *fde;
- int i, n;
-
- n = epoll_wait(epoll_fd, events, 256, -1);
-
- if(n < 0) {
- if(errno == EINTR) return;
- perror("epoll_wait");
- exit(1);
- }
-
- for(i = 0; i < n; i++) {
- struct epoll_event *ev = events + i;
- fde = ev->data.ptr;
-
- if(ev->events & EPOLLIN) {
- fde->events |= FDE_READ;
- }
- if(ev->events & EPOLLOUT) {
- fde->events |= FDE_WRITE;
- }
- if(ev->events & (EPOLLERR | EPOLLHUP)) {
- fde->events |= FDE_ERROR;
- }
- if(fde->events) {
- if(fde->state & FDE_PENDING) continue;
- fde->state |= FDE_PENDING;
- fdevent_plist_enqueue(fde);
- }
- }
-}
-
-#else /* USE_SELECT */
-
-#ifdef HAVE_WINSOCK
-#include <winsock2.h>
-#else
-#include <sys/select.h>
-#endif
-
-static fd_set read_fds;
-static fd_set write_fds;
-static fd_set error_fds;
-
-static int select_n = 0;
-
-static void fdevent_init(void)
-{
- FD_ZERO(&read_fds);
- FD_ZERO(&write_fds);
- FD_ZERO(&error_fds);
-}
-
-static void fdevent_connect(fdevent *fde)
-{
- if(fde->fd >= select_n) {
- select_n = fde->fd + 1;
- }
-}
-
-static void fdevent_disconnect(fdevent *fde)
-{
- int i, n;
-
- FD_CLR(fde->fd, &read_fds);
- FD_CLR(fde->fd, &write_fds);
- FD_CLR(fde->fd, &error_fds);
-
- for(n = 0, i = 0; i < select_n; i++) {
- if(fd_table[i] != 0) n = i;
- }
- select_n = n + 1;
-}
-
-static void fdevent_update(fdevent *fde, unsigned events)
-{
- if(events & FDE_READ) {
- FD_SET(fde->fd, &read_fds);
- } else {
- FD_CLR(fde->fd, &read_fds);
- }
- if(events & FDE_WRITE) {
- FD_SET(fde->fd, &write_fds);
- } else {
- FD_CLR(fde->fd, &write_fds);
- }
- if(events & FDE_ERROR) {
- FD_SET(fde->fd, &error_fds);
- } else {
- FD_CLR(fde->fd, &error_fds);
- }
-
- fde->state = (fde->state & FDE_STATEMASK) | events;
-}
-
-/* Looks at fd_table[] for bad FDs and sets bit in fds.
-** Returns the number of bad FDs.
-*/
-static int fdevent_fd_check(fd_set *fds)
-{
- int i, n = 0;
- fdevent *fde;
-
- for(i = 0; i < select_n; i++) {
- fde = fd_table[i];
- if(fde == 0) continue;
- if(fcntl(i, F_GETFL, NULL) < 0) {
- FD_SET(i, fds);
- n++;
- // fde->state |= FDE_DONT_CLOSE;
-
- }
- }
- return n;
-}
-
-#if !DEBUG
-static inline void dump_all_fds(const char *extra_msg) {}
-#else
-static void dump_all_fds(const char *extra_msg)
-{
-int i;
- fdevent *fde;
- // per fd: 4 digits (but really: log10(FD_SETSIZE)), 1 staus, 1 blank
- char msg_buff[FD_SETSIZE*6 + 1], *pb=msg_buff;
- size_t max_chars = FD_SETSIZE * 6 + 1;
- int printed_out;
-#define SAFE_SPRINTF(...) \
- do { \
- printed_out = snprintf(pb, max_chars, __VA_ARGS__); \
- if (printed_out <= 0) { \
- D("... snprintf failed.\n"); \
- return; \
- } \
- if (max_chars < (unsigned int)printed_out) { \
- D("... snprintf out of space.\n"); \
- return; \
- } \
- pb += printed_out; \
- max_chars -= printed_out; \
- } while(0)
-
- for(i = 0; i < select_n; i++) {
- fde = fd_table[i];
- SAFE_SPRINTF("%d", i);
- if(fde == 0) {
- SAFE_SPRINTF("? ");
- continue;
- }
- if(fcntl(i, F_GETFL, NULL) < 0) {
- SAFE_SPRINTF("b");
- }
- SAFE_SPRINTF(" ");
- }
- D("%s fd_table[]->fd = {%s}\n", extra_msg, msg_buff);
-}
-#endif
-
-static void fdevent_process()
-{
- int i, n;
- fdevent *fde;
- unsigned events;
- fd_set rfd, wfd, efd;
-
- memcpy(&rfd, &read_fds, sizeof(fd_set));
- memcpy(&wfd, &write_fds, sizeof(fd_set));
- memcpy(&efd, &error_fds, sizeof(fd_set));
-
- dump_all_fds("pre select()");
-
- n = select(select_n, &rfd, &wfd, &efd, NULL);
- int saved_errno = errno;
- D("select() returned n=%d, errno=%d\n", n, n<0?saved_errno:0);
-
- dump_all_fds("post select()");
-
- if(n < 0) {
- switch(saved_errno) {
- case EINTR: return;
- case EBADF:
- // Can't trust the FD sets after an error.
- FD_ZERO(&wfd);
- FD_ZERO(&efd);
- FD_ZERO(&rfd);
- break;
- default:
- D("Unexpected select() error=%d\n", saved_errno);
- return;
- }
- }
- if(n <= 0) {
- // We fake a read, as the rest of the code assumes
- // that errors will be detected at that point.
- n = fdevent_fd_check(&rfd);
- }
-
- for(i = 0; (i < select_n) && (n > 0); i++) {
- events = 0;
- if(FD_ISSET(i, &rfd)) { events |= FDE_READ; n--; }
- if(FD_ISSET(i, &wfd)) { events |= FDE_WRITE; n--; }
- if(FD_ISSET(i, &efd)) { events |= FDE_ERROR; n--; }
-
- if(events) {
- fde = fd_table[i];
- if(fde == 0)
- FATAL("missing fde for fd %d\n", i);
-
- fde->events |= events;
-
- D("got events fde->fd=%d events=%04x, state=%04x\n",
- fde->fd, fde->events, fde->state);
- if(fde->state & FDE_PENDING) continue;
- fde->state |= FDE_PENDING;
- fdevent_plist_enqueue(fde);
- }
- }
-}
-
-#endif
-
-static void fdevent_register(fdevent *fde)
-{
- if(fde->fd < 0) {
- FATAL("bogus negative fd (%d)\n", fde->fd);
- }
-
- if(fde->fd >= fd_table_max) {
- int oldmax = fd_table_max;
- if(fde->fd > 32000) {
- FATAL("bogus huuuuge fd (%d)\n", fde->fd);
- }
- if(fd_table_max == 0) {
- fdevent_init();
- fd_table_max = 256;
- }
- while(fd_table_max <= fde->fd) {
- fd_table_max *= 2;
- }
- fd_table = realloc(fd_table, sizeof(fdevent*) * fd_table_max);
- if(fd_table == 0) {
- FATAL("could not expand fd_table to %d entries\n", fd_table_max);
- }
- memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
- }
-
- fd_table[fde->fd] = fde;
-}
-
-static void fdevent_unregister(fdevent *fde)
-{
- if((fde->fd < 0) || (fde->fd >= fd_table_max)) {
- FATAL("fd out of range (%d)\n", fde->fd);
- }
-
- if(fd_table[fde->fd] != fde) {
- FATAL("fd_table out of sync [%d]\n", fde->fd);
- }
-
- fd_table[fde->fd] = 0;
-
- if(!(fde->state & FDE_DONT_CLOSE)) {
- dump_fde(fde, "close");
- adb_close(fde->fd);
- }
-}
-
-static void fdevent_plist_enqueue(fdevent *node)
-{
- fdevent *list = &list_pending;
-
- node->next = list;
- node->prev = list->prev;
- node->prev->next = node;
- list->prev = node;
-}
-
-static void fdevent_plist_remove(fdevent *node)
-{
- node->prev->next = node->next;
- node->next->prev = node->prev;
- node->next = 0;
- node->prev = 0;
-}
-
-static fdevent *fdevent_plist_dequeue(void)
-{
- fdevent *list = &list_pending;
- fdevent *node = list->next;
-
- if(node == list) return 0;
-
- list->next = node->next;
- list->next->prev = list;
- node->next = 0;
- node->prev = 0;
-
- return node;
-}
-
-static void fdevent_call_fdfunc(fdevent* fde)
-{
- unsigned events = fde->events;
- fde->events = 0;
- if(!(fde->state & FDE_PENDING)) return;
- fde->state &= (~FDE_PENDING);
- dump_fde(fde, "callback");
- fde->func(fde->fd, events, fde->arg);
-}
-
-static void fdevent_subproc_event_func(int fd, unsigned ev, void *userdata)
-{
-
- D("subproc handling on fd=%d ev=%04x\n", fd, ev);
-
- // Hook oneself back into the fde's suitable for select() on read.
- if((fd < 0) || (fd >= fd_table_max)) {
- FATAL("fd %d out of range for fd_table \n", fd);
- }
- fdevent *fde = fd_table[fd];
- fdevent_add(fde, FDE_READ);
-
- if(ev & FDE_READ){
- int subproc_fd;
-
- if(readx(fd, &subproc_fd, sizeof(subproc_fd))) {
- FATAL("Failed to read the subproc's fd from fd=%d\n", fd);
- }
- if((subproc_fd < 0) || (subproc_fd >= fd_table_max)) {
- D("subproc_fd %d out of range 0, fd_table_max=%d\n",
- subproc_fd, fd_table_max);
- return;
- }
- fdevent *subproc_fde = fd_table[subproc_fd];
- if(!subproc_fde) {
- D("subproc_fd %d cleared from fd_table\n", subproc_fd);
- return;
- }
- if(subproc_fde->fd != subproc_fd) {
- // Already reallocated?
- D("subproc_fd %d != fd_table[].fd %d\n", subproc_fd, subproc_fde->fd);
- return;
- }
-
- subproc_fde->force_eof = 1;
-
- int rcount = 0;
- ioctl(subproc_fd, FIONREAD, &rcount);
- D("subproc with fd=%d has rcount=%d err=%d\n",
- subproc_fd, rcount, errno);
-
- if(rcount) {
- // If there is data left, it will show up in the select().
- // This works because there is no other thread reading that
- // data when in this fd_func().
- return;
- }
-
- D("subproc_fde.state=%04x\n", subproc_fde->state);
- subproc_fde->events |= FDE_READ;
- if(subproc_fde->state & FDE_PENDING) {
- return;
- }
- subproc_fde->state |= FDE_PENDING;
- fdevent_call_fdfunc(subproc_fde);
- }
-}
-
-fdevent *fdevent_create(int fd, fd_func func, void *arg)
-{
- fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
- if(fde == 0) return 0;
- fdevent_install(fde, fd, func, arg);
- fde->state |= FDE_CREATED;
- return fde;
-}
-
-void fdevent_destroy(fdevent *fde)
-{
- if(fde == 0) return;
- if(!(fde->state & FDE_CREATED)) {
- FATAL("fde %p not created by fdevent_create()\n", fde);
- }
- fdevent_remove(fde);
-}
-
-void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
-{
- memset(fde, 0, sizeof(fdevent));
- fde->state = FDE_ACTIVE;
- fde->fd = fd;
- fde->force_eof = 0;
- fde->func = func;
- fde->arg = arg;
-
-#ifndef HAVE_WINSOCK
- fcntl(fd, F_SETFL, O_NONBLOCK);
-#endif
- fdevent_register(fde);
- dump_fde(fde, "connect");
- fdevent_connect(fde);
- fde->state |= FDE_ACTIVE;
-}
-
-void fdevent_remove(fdevent *fde)
-{
- if(fde->state & FDE_PENDING) {
- fdevent_plist_remove(fde);
- }
-
- if(fde->state & FDE_ACTIVE) {
- fdevent_disconnect(fde);
- dump_fde(fde, "disconnect");
- fdevent_unregister(fde);
- }
-
- fde->state = 0;
- fde->events = 0;
-}
-
-
-void fdevent_set(fdevent *fde, unsigned events)
-{
- events &= FDE_EVENTMASK;
-
- if((fde->state & FDE_EVENTMASK) == events) return;
-
- if(fde->state & FDE_ACTIVE) {
- fdevent_update(fde, events);
- dump_fde(fde, "update");
- }
-
- fde->state = (fde->state & FDE_STATEMASK) | events;
-
- if(fde->state & FDE_PENDING) {
- /* if we're pending, make sure
- ** we don't signal an event that
- ** is no longer wanted.
- */
- fde->events &= (~events);
- if(fde->events == 0) {
- fdevent_plist_remove(fde);
- fde->state &= (~FDE_PENDING);
- }
- }
-}
-
-void fdevent_add(fdevent *fde, unsigned events)
-{
- fdevent_set(
- fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
-}
-
-void fdevent_del(fdevent *fde, unsigned events)
-{
- fdevent_set(
- fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
-}
-
-void fdevent_subproc_setup()
-{
- int s[2];
-
- if(adb_socketpair(s)) {
- FATAL("cannot create shell-exit socket-pair\n");
- }
- SHELL_EXIT_NOTIFY_FD = s[0];
- fdevent *fde;
- fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
- if(!fde)
- FATAL("cannot create fdevent for shell-exit handler\n");
- fdevent_add(fde, FDE_READ);
-}
-
-void fdevent_loop()
-{
- fdevent *fde;
- fdevent_subproc_setup();
-
- for(;;) {
- D("--- ---- waiting for events\n");
-
- fdevent_process();
-
- while((fde = fdevent_plist_dequeue())) {
- fdevent_call_fdfunc(fde);
- }
- }
-}
diff --git a/minadbd/fdevent.h b/minadbd/fdevent.h
deleted file mode 100644
index a0ebe2a..0000000
--- a/minadbd/fdevent.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __FDEVENT_H
-#define __FDEVENT_H
-
-#include <stdint.h> /* for int64_t */
-
-/* events that may be observed */
-#define FDE_READ 0x0001
-#define FDE_WRITE 0x0002
-#define FDE_ERROR 0x0004
-#define FDE_TIMEOUT 0x0008
-
-/* features that may be set (via the events set/add/del interface) */
-#define FDE_DONT_CLOSE 0x0080
-
-typedef struct fdevent fdevent;
-
-typedef void (*fd_func)(int fd, unsigned events, void *userdata);
-
-/* Allocate and initialize a new fdevent object
- * Note: use FD_TIMER as 'fd' to create a fd-less object
- * (used to implement timers).
-*/
-fdevent *fdevent_create(int fd, fd_func func, void *arg);
-
-/* Uninitialize and deallocate an fdevent object that was
-** created by fdevent_create()
-*/
-void fdevent_destroy(fdevent *fde);
-
-/* Initialize an fdevent object that was externally allocated
-*/
-void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
-
-/* Uninitialize an fdevent object that was initialized by
-** fdevent_install()
-*/
-void fdevent_remove(fdevent *item);
-
-/* Change which events should cause notifications
-*/
-void fdevent_set(fdevent *fde, unsigned events);
-void fdevent_add(fdevent *fde, unsigned events);
-void fdevent_del(fdevent *fde, unsigned events);
-
-void fdevent_set_timeout(fdevent *fde, int64_t timeout_ms);
-
-/* loop forever, handling events.
-*/
-void fdevent_loop();
-
-struct fdevent
-{
- fdevent *next;
- fdevent *prev;
-
- int fd;
- int force_eof;
-
- unsigned short state;
- unsigned short events;
-
- fd_func func;
- void *arg;
-};
-
-
-#endif
diff --git a/minadbd/fuse_adb_provider.c b/minadbd/fuse_adb_provider.cpp
similarity index 66%
rename from minadbd/fuse_adb_provider.c
rename to minadbd/fuse_adb_provider.cpp
index f80533a..d71807d 100644
--- a/minadbd/fuse_adb_provider.c
+++ b/minadbd/fuse_adb_provider.cpp
@@ -16,29 +16,25 @@
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#include <errno.h>
+#include "sysdeps.h"
+
#include "adb.h"
+#include "adb_io.h"
+#include "fuse_adb_provider.h"
#include "fuse_sideload.h"
-struct adb_data {
- int sfd; // file descriptor for the adb channel
+int read_block_adb(void* data, uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
+ adb_data* ad = reinterpret_cast<adb_data*>(data);
- uint64_t file_size;
- uint32_t block_size;
-};
-
-static int read_block_adb(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
- struct adb_data* ad = (struct adb_data*)cookie;
-
- char buf[10];
- snprintf(buf, sizeof(buf), "%08u", block);
- if (writex(ad->sfd, buf, 8) < 0) {
+ if (!WriteFdFmt(ad->sfd, "%08u", block)) {
fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno));
return -EIO;
}
- if (readx(ad->sfd, buffer, fetch_size) < 0) {
+ if (!ReadFdExactly(ad->sfd, buffer, fetch_size)) {
fprintf(stderr, "failed to read from adb host: %s\n", strerror(errno));
return -EIO;
}
@@ -46,20 +42,18 @@
return 0;
}
-static void close_adb(void* cookie) {
- struct adb_data* ad = (struct adb_data*)cookie;
-
- writex(ad->sfd, "DONEDONE", 8);
+static void close_adb(void* data) {
+ adb_data* ad = reinterpret_cast<adb_data*>(data);
+ WriteFdExactly(ad->sfd, "DONEDONE");
}
int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size) {
- struct adb_data ad;
- struct provider_vtab vtab;
-
+ adb_data ad;
ad.sfd = sfd;
ad.file_size = file_size;
ad.block_size = block_size;
+ provider_vtab vtab;
vtab.read_block = read_block_adb;
vtab.close = close_adb;
diff --git a/minadbd/fuse_adb_provider.h b/minadbd/fuse_adb_provider.h
index 0eb1f79..9941709 100644
--- a/minadbd/fuse_adb_provider.h
+++ b/minadbd/fuse_adb_provider.h
@@ -17,6 +17,16 @@
#ifndef __FUSE_ADB_PROVIDER_H
#define __FUSE_ADB_PROVIDER_H
+#include <stdint.h>
+
+struct adb_data {
+ int sfd; // file descriptor for the adb channel
+
+ uint64_t file_size;
+ uint32_t block_size;
+};
+
+int read_block_adb(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size);
int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size);
#endif
diff --git a/minadbd/fuse_adb_provider_test.cpp b/minadbd/fuse_adb_provider_test.cpp
new file mode 100644
index 0000000..0f2e881
--- /dev/null
+++ b/minadbd/fuse_adb_provider_test.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 "fuse_adb_provider.h"
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+#include <string>
+
+#include "adb_io.h"
+
+TEST(fuse_adb_provider, read_block_adb) {
+ adb_data data = {};
+ int sockets[2];
+
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sockets));
+ data.sfd = sockets[0];
+
+ int host_socket = sockets[1];
+ fcntl(host_socket, F_SETFL, O_NONBLOCK);
+
+ const char expected_data[] = "foobar";
+ char block_data[sizeof(expected_data)] = {};
+
+ // If we write the result of read_block_adb's request before the request is
+ // actually made we can avoid needing an extra thread for this test.
+ ASSERT_TRUE(WriteFdExactly(host_socket, expected_data,
+ strlen(expected_data)));
+
+ uint32_t block = 1234U;
+ const char expected_block[] = "00001234";
+ ASSERT_EQ(0, read_block_adb(reinterpret_cast<void*>(&data), block,
+ reinterpret_cast<uint8_t*>(block_data),
+ sizeof(expected_data) - 1));
+
+ // Check that read_block_adb requested the right block.
+ char block_req[sizeof(expected_block)] = {};
+ ASSERT_TRUE(ReadFdExactly(host_socket, block_req, 8));
+ ASSERT_EQ(0, block_req[8]);
+ ASSERT_EQ(8U, strlen(block_req));
+ ASSERT_STREQ(expected_block, block_req);
+
+ // Check that read_block_adb returned the right data.
+ ASSERT_EQ(0, block_req[8]);
+ ASSERT_STREQ(expected_data, block_data);
+
+ // Check that nothing else was written to the socket.
+ char tmp;
+ errno = 0;
+ ASSERT_EQ(-1, read(host_socket, &tmp, 1));
+ ASSERT_EQ(EWOULDBLOCK, errno);
+
+ close(sockets[0]);
+ close(sockets[1]);
+}
+
+TEST(fuse_adb_provider, read_block_adb_fail_write) {
+ adb_data data = {};
+ int sockets[2];
+
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sockets));
+ data.sfd = sockets[0];
+
+ ASSERT_EQ(0, close(sockets[1]));
+
+ char buf[1];
+ ASSERT_EQ(-EIO, read_block_adb(reinterpret_cast<void*>(&data), 0,
+ reinterpret_cast<uint8_t*>(buf), 1));
+
+ close(sockets[0]);
+}
diff --git a/minadbd/mutex_list.h b/minadbd/mutex_list.h
deleted file mode 100644
index 652dd73..0000000
--- a/minadbd/mutex_list.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* the list of mutexes used by adb */
-/* #ifndef __MUTEX_LIST_H
- * Do not use an include-guard. This file is included once to declare the locks
- * and once in win32 to actually do the runtime initialization.
- */
-#ifndef ADB_MUTEX
-#error ADB_MUTEX not defined when including this file
-#endif
-ADB_MUTEX(dns_lock)
-ADB_MUTEX(socket_list_lock)
-ADB_MUTEX(transport_lock)
-#if ADB_HOST
-ADB_MUTEX(local_transports_lock)
-#endif
-ADB_MUTEX(usb_lock)
-
-// Sadly logging to /data/adb/adb-... is not thread safe.
-// After modifying adb.h::D() to count invocations:
-// DEBUG(jpa):0:Handling main()
-// DEBUG(jpa):1:[ usb_init - starting thread ]
-// (Oopsies, no :2:, and matching message is also gone.)
-// DEBUG(jpa):3:[ usb_thread - opening device ]
-// DEBUG(jpa):4:jdwp control socket started (10)
-ADB_MUTEX(D_lock)
-
-#undef ADB_MUTEX
diff --git a/minadbd/services.c b/minadbd/services.cpp
similarity index 63%
rename from minadbd/services.c
rename to minadbd/services.cpp
index 218b84a..dd1fd7c 100644
--- a/minadbd/services.c
+++ b/minadbd/services.cpp
@@ -14,18 +14,19 @@
* limitations under the License.
*/
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
#include "sysdeps.h"
-#include "fdevent.h"
-#include "fuse_adb_provider.h"
#define TRACE_TAG TRACE_SERVICES
#include "adb.h"
+#include "fdevent.h"
+#include "fuse_adb_provider.h"
typedef struct stinfo stinfo;
@@ -35,24 +36,23 @@
void *cookie;
};
-
-void *service_bootstrap_func(void *x)
-{
- stinfo *sti = 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* cookie)
-{
- char* saveptr;
- const char* s = strtok_r(cookie, ":", &saveptr);
- uint64_t file_size = strtoull(s, NULL, 10);
- s = strtok_r(NULL, ":", &saveptr);
- uint32_t block_size = strtoul(s, NULL, 10);
+static void sideload_host_service(int sfd, void* data) {
+ const char* args = reinterpret_cast<const 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);
+ }
- printf("sideload-host file size %llu block size %lu\n", file_size, block_size);
+ printf("sideload-host file size %d block size %d\n", file_size, block_size);
int result = run_adb_fuse(sfd, file_size, block_size);
@@ -61,58 +61,22 @@
exit(result == 0 ? 0 : 1);
}
-#if 0
-static void echo_service(int fd, void *cookie)
-{
- char buf[4096];
- int r;
- char *p;
- int c;
-
- for(;;) {
- r = read(fd, buf, 4096);
- if(r == 0) goto done;
- if(r < 0) {
- if(errno == EINTR) continue;
- else goto done;
- }
-
- c = r;
- p = buf;
- while(c > 0) {
- r = write(fd, p, c);
- if(r > 0) {
- c -= r;
- p += r;
- continue;
- }
- if((r < 0) && (errno == EINTR)) continue;
- goto done;
- }
- }
-done:
- close(fd);
-}
-#endif
-
static int create_service_thread(void (*func)(int, void *), void *cookie)
{
- stinfo *sti;
- adb_thread_t t;
int s[2];
-
if(adb_socketpair(s)) {
printf("cannot create service socket pair\n");
return -1;
}
- sti = malloc(sizeof(stinfo));
+ stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
if(sti == 0) fatal("cannot allocate stinfo");
sti->func = func;
sti->cookie = cookie;
sti->fd = s[1];
- if(adb_thread_create( &t, service_bootstrap_func, sti)){
+ adb_thread_t t;
+ if (adb_thread_create( &t, service_bootstrap_func, sti)){
free(sti);
adb_close(s[0]);
adb_close(s[1]);
@@ -124,8 +88,7 @@
return s[0];
}
-int service_to_fd(const char *name)
-{
+int service_to_fd(const char* name) {
int ret = -1;
if (!strncmp(name, "sideload:", 9)) {
@@ -135,10 +98,6 @@
exit(3);
} else if (!strncmp(name, "sideload-host:", 14)) {
ret = create_service_thread(sideload_host_service, (void*)(name + 14));
-#if 0
- } else if(!strncmp(name, "echo:", 5)){
- ret = create_service_thread(echo_service, 0);
-#endif
}
if (ret >= 0) {
close_on_exec(ret);
diff --git a/minadbd/sockets.c b/minadbd/sockets.c
deleted file mode 100644
index 817410d..0000000
--- a/minadbd/sockets.c
+++ /dev/null
@@ -1,731 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <ctype.h>
-
-#include "sysdeps.h"
-
-#define TRACE_TAG TRACE_SOCKETS
-#include "adb.h"
-
-ADB_MUTEX_DEFINE( socket_list_lock );
-
-static void local_socket_close_locked(asocket *s);
-
-int sendfailmsg(int fd, const char *reason)
-{
- char buf[9];
- int len;
- len = strlen(reason);
- if(len > 0xffff) len = 0xffff;
- snprintf(buf, sizeof buf, "FAIL%04x", len);
- if(writex(fd, buf, 8)) return -1;
- return writex(fd, reason, len);
-}
-
-//extern int online;
-
-static unsigned local_socket_next_id = 1;
-
-static asocket local_socket_list = {
- .next = &local_socket_list,
- .prev = &local_socket_list,
-};
-
-/* the the list of currently closing local sockets.
-** these have no peer anymore, but still packets to
-** write to their fd.
-*/
-static asocket local_socket_closing_list = {
- .next = &local_socket_closing_list,
- .prev = &local_socket_closing_list,
-};
-
-asocket *find_local_socket(unsigned id)
-{
- asocket *s;
- asocket *result = NULL;
-
- adb_mutex_lock(&socket_list_lock);
- for (s = local_socket_list.next; s != &local_socket_list; s = s->next) {
- if (s->id == id) {
- result = s;
- break;
- }
- }
- adb_mutex_unlock(&socket_list_lock);
-
- return result;
-}
-
-static void
-insert_local_socket(asocket* s, asocket* list)
-{
- s->next = list;
- s->prev = s->next->prev;
- s->prev->next = s;
- s->next->prev = s;
-}
-
-
-void install_local_socket(asocket *s)
-{
- adb_mutex_lock(&socket_list_lock);
-
- s->id = local_socket_next_id++;
- insert_local_socket(s, &local_socket_list);
-
- adb_mutex_unlock(&socket_list_lock);
-}
-
-void remove_socket(asocket *s)
-{
- // socket_list_lock should already be held
- if (s->prev && s->next)
- {
- s->prev->next = s->next;
- s->next->prev = s->prev;
- s->next = 0;
- s->prev = 0;
- s->id = 0;
- }
-}
-
-void close_all_sockets(atransport *t)
-{
- asocket *s;
-
- /* this is a little gross, but since s->close() *will* modify
- ** the list out from under you, your options are limited.
- */
- adb_mutex_lock(&socket_list_lock);
-restart:
- for(s = local_socket_list.next; s != &local_socket_list; s = s->next){
- if(s->transport == t || (s->peer && s->peer->transport == t)) {
- local_socket_close_locked(s);
- goto restart;
- }
- }
- adb_mutex_unlock(&socket_list_lock);
-}
-
-static int local_socket_enqueue(asocket *s, apacket *p)
-{
- D("LS(%d): enqueue %d\n", s->id, p->len);
-
- p->ptr = p->data;
-
- /* if there is already data queue'd, we will receive
- ** events when it's time to write. just add this to
- ** the tail
- */
- if(s->pkt_first) {
- goto enqueue;
- }
-
- /* write as much as we can, until we
- ** would block or there is an error/eof
- */
- while(p->len > 0) {
- int r = adb_write(s->fd, p->ptr, p->len);
- if(r > 0) {
- p->len -= r;
- p->ptr += r;
- continue;
- }
- if((r == 0) || (errno != EAGAIN)) {
- D( "LS(%d): not ready, errno=%d: %s\n", s->id, errno, strerror(errno) );
- s->close(s);
- return 1; /* not ready (error) */
- } else {
- break;
- }
- }
-
- if(p->len == 0) {
- put_apacket(p);
- return 0; /* ready for more data */
- }
-
-enqueue:
- p->next = 0;
- if(s->pkt_first) {
- s->pkt_last->next = p;
- } else {
- s->pkt_first = p;
- }
- s->pkt_last = p;
-
- /* make sure we are notified when we can drain the queue */
- fdevent_add(&s->fde, FDE_WRITE);
-
- return 1; /* not ready (backlog) */
-}
-
-static void local_socket_ready(asocket *s)
-{
- /* far side is ready for data, pay attention to
- readable events */
- fdevent_add(&s->fde, FDE_READ);
-// D("LS(%d): ready()\n", s->id);
-}
-
-static void local_socket_close(asocket *s)
-{
- adb_mutex_lock(&socket_list_lock);
- local_socket_close_locked(s);
- adb_mutex_unlock(&socket_list_lock);
-}
-
-// be sure to hold the socket list lock when calling this
-static void local_socket_destroy(asocket *s)
-{
- apacket *p, *n;
- D("LS(%d): destroying fde.fd=%d\n", s->id, s->fde.fd);
-
- /* IMPORTANT: the remove closes the fd
- ** that belongs to this socket
- */
- fdevent_remove(&s->fde);
-
- /* dispose of any unwritten data */
- for(p = s->pkt_first; p; p = n) {
- D("LS(%d): discarding %d bytes\n", s->id, p->len);
- n = p->next;
- put_apacket(p);
- }
- remove_socket(s);
- free(s);
-}
-
-
-static void local_socket_close_locked(asocket *s)
-{
- D("entered. LS(%d) fd=%d\n", s->id, s->fd);
- if(s->peer) {
- D("LS(%d): closing peer. peer->id=%d peer->fd=%d\n",
- s->id, s->peer->id, s->peer->fd);
- s->peer->peer = 0;
- // tweak to avoid deadlock
- if (s->peer->close == local_socket_close) {
- local_socket_close_locked(s->peer);
- } else {
- s->peer->close(s->peer);
- }
- s->peer = 0;
- }
-
- /* If we are already closing, or if there are no
- ** pending packets, destroy immediately
- */
- if (s->closing || s->pkt_first == NULL) {
- int id = s->id;
- local_socket_destroy(s);
- D("LS(%d): closed\n", id);
- return;
- }
-
- /* otherwise, put on the closing list
- */
- D("LS(%d): closing\n", s->id);
- s->closing = 1;
- fdevent_del(&s->fde, FDE_READ);
- remove_socket(s);
- D("LS(%d): put on socket_closing_list fd=%d\n", s->id, s->fd);
- insert_local_socket(s, &local_socket_closing_list);
-}
-
-static void local_socket_event_func(int fd, unsigned ev, void *_s)
-{
- asocket *s = _s;
-
- D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev);
-
- /* put the FDE_WRITE processing before the FDE_READ
- ** in order to simplify the code.
- */
- if(ev & FDE_WRITE){
- apacket *p;
-
- while((p = s->pkt_first) != 0) {
- while(p->len > 0) {
- int r = adb_write(fd, p->ptr, p->len);
- if(r > 0) {
- p->ptr += r;
- p->len -= r;
- continue;
- }
- if(r < 0) {
- /* returning here is ok because FDE_READ will
- ** be processed in the next iteration loop
- */
- if(errno == EAGAIN) return;
- if(errno == EINTR) continue;
- }
- D(" closing after write because r=%d and errno is %d\n", r, errno);
- s->close(s);
- return;
- }
-
- if(p->len == 0) {
- s->pkt_first = p->next;
- if(s->pkt_first == 0) s->pkt_last = 0;
- put_apacket(p);
- }
- }
-
- /* if we sent the last packet of a closing socket,
- ** we can now destroy it.
- */
- if (s->closing) {
- D(" closing because 'closing' is set after write\n");
- s->close(s);
- return;
- }
-
- /* no more packets queued, so we can ignore
- ** writable events again and tell our peer
- ** to resume writing
- */
- fdevent_del(&s->fde, FDE_WRITE);
- s->peer->ready(s->peer);
- }
-
-
- if(ev & FDE_READ){
- apacket *p = get_apacket();
- unsigned char *x = p->data;
- size_t avail = MAX_PAYLOAD;
- int r;
- int is_eof = 0;
-
- while(avail > 0) {
- r = adb_read(fd, x, avail);
- D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu\n",
- s->id, s->fd, r, r<0?errno:0, avail);
- if(r > 0) {
- avail -= r;
- x += r;
- continue;
- }
- if(r < 0) {
- if(errno == EAGAIN) break;
- if(errno == EINTR) continue;
- }
-
- /* r = 0 or unhandled error */
- is_eof = 1;
- break;
- }
- D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n",
- s->id, s->fd, r, is_eof, s->fde.force_eof);
- if((avail == MAX_PAYLOAD) || (s->peer == 0)) {
- put_apacket(p);
- } else {
- p->len = MAX_PAYLOAD - avail;
-
- r = s->peer->enqueue(s->peer, p);
- D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd, r);
-
- if(r < 0) {
- /* error return means they closed us as a side-effect
- ** and we must return immediately.
- **
- ** note that if we still have buffered packets, the
- ** socket will be placed on the closing socket list.
- ** this handler function will be called again
- ** to process FDE_WRITE events.
- */
- return;
- }
-
- if(r > 0) {
- /* if the remote cannot accept further events,
- ** we disable notification of READs. They'll
- ** be enabled again when we get a call to ready()
- */
- fdevent_del(&s->fde, FDE_READ);
- }
- }
- /* Don't allow a forced eof if data is still there */
- if((s->fde.force_eof && !r) || is_eof) {
- D(" closing because is_eof=%d r=%d s->fde.force_eof=%d\n", is_eof, r, s->fde.force_eof);
- s->close(s);
- }
- }
-
- if(ev & FDE_ERROR){
- /* this should be caught be the next read or write
- ** catching it here means we may skip the last few
- ** bytes of readable data.
- */
-// s->close(s);
- D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd);
-
- return;
- }
-}
-
-asocket *create_local_socket(int fd)
-{
- asocket *s = calloc(1, sizeof(asocket));
- if (s == NULL) fatal("cannot allocate socket");
- s->fd = fd;
- s->enqueue = local_socket_enqueue;
- s->ready = local_socket_ready;
- s->close = local_socket_close;
- install_local_socket(s);
-
- fdevent_install(&s->fde, fd, local_socket_event_func, s);
-/* fdevent_add(&s->fde, FDE_ERROR); */
- //fprintf(stderr, "Created local socket in create_local_socket \n");
- D("LS(%d): created (fd=%d)\n", s->id, s->fd);
- return s;
-}
-
-asocket *create_local_service_socket(const char *name)
-{
- asocket *s;
- int fd;
-
- fd = service_to_fd(name);
- if(fd < 0) return 0;
-
- s = create_local_socket(fd);
- D("LS(%d): bound to '%s' via %d\n", s->id, name, fd);
- return s;
-}
-
-/* a Remote socket is used to send/receive data to/from a given transport object
-** it needs to be closed when the transport is forcibly destroyed by the user
-*/
-typedef struct aremotesocket {
- asocket socket;
- adisconnect disconnect;
-} aremotesocket;
-
-static int remote_socket_enqueue(asocket *s, apacket *p)
-{
- D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d\n",
- s->id, s->fd, s->peer->fd);
- p->msg.command = A_WRTE;
- p->msg.arg0 = s->peer->id;
- p->msg.arg1 = s->id;
- p->msg.data_length = p->len;
- send_packet(p, s->transport);
- return 1;
-}
-
-static void remote_socket_ready(asocket *s)
-{
- D("entered remote_socket_ready RS(%d) OKAY fd=%d peer.fd=%d\n",
- s->id, s->fd, s->peer->fd);
- apacket *p = get_apacket();
- p->msg.command = A_OKAY;
- p->msg.arg0 = s->peer->id;
- p->msg.arg1 = s->id;
- send_packet(p, s->transport);
-}
-
-static void remote_socket_close(asocket *s)
-{
- D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n",
- s->id, s->fd, s->peer?s->peer->fd:-1);
- apacket *p = get_apacket();
- p->msg.command = A_CLSE;
- if(s->peer) {
- p->msg.arg0 = s->peer->id;
- s->peer->peer = 0;
- D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d\n",
- s->id, s->peer->id, s->peer->fd);
- s->peer->close(s->peer);
- }
- p->msg.arg1 = s->id;
- send_packet(p, s->transport);
- D("RS(%d): closed\n", s->id);
- remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
- free(s);
-}
-
-static void remote_socket_disconnect(void* _s, atransport* t)
-{
- asocket* s = _s;
- asocket* peer = s->peer;
-
- D("remote_socket_disconnect RS(%d)\n", s->id);
- if (peer) {
- peer->peer = NULL;
- peer->close(peer);
- }
- remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
- free(s);
-}
-
-asocket *create_remote_socket(unsigned id, atransport *t)
-{
- asocket *s = calloc(1, sizeof(aremotesocket));
- adisconnect* dis = &((aremotesocket*)s)->disconnect;
-
- if (s == NULL) fatal("cannot allocate socket");
- s->id = id;
- s->enqueue = remote_socket_enqueue;
- s->ready = remote_socket_ready;
- s->close = remote_socket_close;
- s->transport = t;
-
- dis->func = remote_socket_disconnect;
- dis->opaque = s;
- add_transport_disconnect( t, dis );
- D("RS(%d): created\n", s->id);
- return s;
-}
-
-void connect_to_remote(asocket *s, const char *destination)
-{
- D("Connect_to_remote call RS(%d) fd=%d\n", s->id, s->fd);
- apacket *p = get_apacket();
- int len = strlen(destination) + 1;
-
- if(len > (MAX_PAYLOAD-1)) {
- fatal("destination oversized");
- }
-
- D("LS(%d): connect('%s')\n", s->id, destination);
- p->msg.command = A_OPEN;
- p->msg.arg0 = s->id;
- p->msg.data_length = len;
- strcpy((char*) p->data, destination);
- send_packet(p, s->transport);
-}
-
-
-/* this is used by magic sockets to rig local sockets to
- send the go-ahead message when they connect */
-static void local_socket_ready_notify(asocket *s)
-{
- s->ready = local_socket_ready;
- s->close = local_socket_close;
- adb_write(s->fd, "OKAY", 4);
- s->ready(s);
-}
-
-/* this is used by magic sockets to rig local sockets to
- send the failure message if they are closed before
- connected (to avoid closing them without a status message) */
-static void local_socket_close_notify(asocket *s)
-{
- s->ready = local_socket_ready;
- s->close = local_socket_close;
- sendfailmsg(s->fd, "closed");
- s->close(s);
-}
-
-unsigned unhex(unsigned char *s, int len)
-{
- unsigned n = 0, c;
-
- while(len-- > 0) {
- switch((c = *s++)) {
- case '0': case '1': case '2':
- case '3': case '4': case '5':
- case '6': case '7': case '8':
- case '9':
- c -= '0';
- break;
- case 'a': case 'b': case 'c':
- case 'd': case 'e': case 'f':
- c = c - 'a' + 10;
- break;
- case 'A': case 'B': case 'C':
- case 'D': case 'E': case 'F':
- c = c - 'A' + 10;
- break;
- default:
- return 0xffffffff;
- }
-
- n = (n << 4) | c;
- }
-
- return n;
-}
-
-/* skip_host_serial return the position in a string
- skipping over the 'serial' parameter in the ADB protocol,
- where parameter string may be a host:port string containing
- the protocol delimiter (colon). */
-char *skip_host_serial(char *service) {
- char *first_colon, *serial_end;
-
- first_colon = strchr(service, ':');
- if (!first_colon) {
- /* No colon in service string. */
- return NULL;
- }
- serial_end = first_colon;
- if (isdigit(serial_end[1])) {
- serial_end++;
- while ((*serial_end) && isdigit(*serial_end)) {
- serial_end++;
- }
- if ((*serial_end) != ':') {
- // Something other than numbers was found, reset the end.
- serial_end = first_colon;
- }
- }
- return serial_end;
-}
-
-static int smart_socket_enqueue(asocket *s, apacket *p)
-{
- unsigned len;
-
- D("SS(%d): enqueue %d\n", s->id, p->len);
-
- if(s->pkt_first == 0) {
- s->pkt_first = p;
- s->pkt_last = p;
- } else {
- if((s->pkt_first->len + p->len) > MAX_PAYLOAD) {
- D("SS(%d): overflow\n", s->id);
- put_apacket(p);
- goto fail;
- }
-
- memcpy(s->pkt_first->data + s->pkt_first->len,
- p->data, p->len);
- s->pkt_first->len += p->len;
- put_apacket(p);
-
- p = s->pkt_first;
- }
-
- /* don't bother if we can't decode the length */
- if(p->len < 4) return 0;
-
- len = unhex(p->data, 4);
- if((len < 1) || (len > 1024)) {
- D("SS(%d): bad size (%d)\n", s->id, len);
- goto fail;
- }
-
- D("SS(%d): len is %d\n", s->id, len );
- /* can't do anything until we have the full header */
- if((len + 4) > p->len) {
- D("SS(%d): waiting for %d more bytes\n", s->id, len+4 - p->len);
- return 0;
- }
-
- p->data[len + 4] = 0;
-
- D("SS(%d): '%s'\n", s->id, (char*) (p->data + 4));
-
- if (s->transport == NULL) {
- char* error_string = "unknown failure";
- s->transport = acquire_one_transport (CS_ANY,
- kTransportAny, NULL, &error_string);
-
- if (s->transport == NULL) {
- sendfailmsg(s->peer->fd, error_string);
- goto fail;
- }
- }
-
- if(!(s->transport) || (s->transport->connection_state == CS_OFFLINE)) {
- /* if there's no remote we fail the connection
- ** right here and terminate it
- */
- sendfailmsg(s->peer->fd, "device offline (x)");
- goto fail;
- }
-
-
- /* instrument our peer to pass the success or fail
- ** message back once it connects or closes, then
- ** detach from it, request the connection, and
- ** tear down
- */
- s->peer->ready = local_socket_ready_notify;
- s->peer->close = local_socket_close_notify;
- s->peer->peer = 0;
- /* give him our transport and upref it */
- s->peer->transport = s->transport;
-
- connect_to_remote(s->peer, (char*) (p->data + 4));
- s->peer = 0;
- s->close(s);
- return 1;
-
-fail:
- /* we're going to close our peer as a side-effect, so
- ** return -1 to signal that state to the local socket
- ** who is enqueueing against us
- */
- s->close(s);
- return -1;
-}
-
-static void smart_socket_ready(asocket *s)
-{
- D("SS(%d): ready\n", s->id);
-}
-
-static void smart_socket_close(asocket *s)
-{
- D("SS(%d): closed\n", s->id);
- if(s->pkt_first){
- put_apacket(s->pkt_first);
- }
- if(s->peer) {
- s->peer->peer = 0;
- s->peer->close(s->peer);
- s->peer = 0;
- }
- free(s);
-}
-
-asocket *create_smart_socket(void (*action_cb)(asocket *s, const char *act))
-{
- D("Creating smart socket \n");
- asocket *s = calloc(1, sizeof(asocket));
- if (s == NULL) fatal("cannot allocate socket");
- s->enqueue = smart_socket_enqueue;
- s->ready = smart_socket_ready;
- s->close = smart_socket_close;
- s->extra = action_cb;
-
- D("SS(%d): created %p\n", s->id, action_cb);
- return s;
-}
-
-void smart_socket_action(asocket *s, const char *act)
-{
-
-}
-
-void connect_to_smartsocket(asocket *s)
-{
- D("Connecting to smart socket \n");
- asocket *ss = create_smart_socket(smart_socket_action);
- s->peer = ss;
- ss->peer = s;
- s->ready(s);
-}
diff --git a/minadbd/sysdeps.h b/minadbd/sysdeps.h
deleted file mode 100644
index 800ddb7..0000000
--- a/minadbd/sysdeps.h
+++ /dev/null
@@ -1,494 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* this file contains system-dependent definitions used by ADB
- * they're related to threads, sockets and file descriptors
- */
-#ifndef _ADB_SYSDEPS_H
-#define _ADB_SYSDEPS_H
-
-#ifdef __CYGWIN__
-# undef _WIN32
-#endif
-
-#ifdef _WIN32
-
-#include <windows.h>
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#include <process.h>
-#include <fcntl.h>
-#include <io.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <ctype.h>
-
-#define OS_PATH_SEPARATOR '\\'
-#define OS_PATH_SEPARATOR_STR "\\"
-
-typedef CRITICAL_SECTION adb_mutex_t;
-
-#define ADB_MUTEX_DEFINE(x) adb_mutex_t x
-
-/* declare all mutexes */
-/* For win32, adb_sysdeps_init() will do the mutex runtime initialization. */
-#define ADB_MUTEX(x) extern adb_mutex_t x;
-#include "mutex_list.h"
-
-extern void adb_sysdeps_init(void);
-
-static __inline__ void adb_mutex_lock( adb_mutex_t* lock )
-{
- EnterCriticalSection( lock );
-}
-
-static __inline__ void adb_mutex_unlock( adb_mutex_t* lock )
-{
- LeaveCriticalSection( lock );
-}
-
-typedef struct { unsigned tid; } adb_thread_t;
-
-typedef void* (*adb_thread_func_t)(void* arg);
-
-typedef void (*win_thread_func_t)(void* arg);
-
-static __inline__ int adb_thread_create( adb_thread_t *thread, adb_thread_func_t func, void* arg)
-{
- thread->tid = _beginthread( (win_thread_func_t)func, 0, arg );
- if (thread->tid == (unsigned)-1L) {
- return -1;
- }
- return 0;
-}
-
-static __inline__ void close_on_exec(int fd)
-{
- /* nothing really */
-}
-
-extern void disable_tcp_nagle(int fd);
-
-#define lstat stat /* no symlinks on Win32 */
-
-#define S_ISLNK(m) 0 /* no symlinks on Win32 */
-
-static __inline__ int adb_unlink(const char* path)
-{
- int rc = unlink(path);
-
- if (rc == -1 && errno == EACCES) {
- /* unlink returns EACCES when the file is read-only, so we first */
- /* try to make it writable, then unlink again... */
- rc = chmod(path, _S_IREAD|_S_IWRITE );
- if (rc == 0)
- rc = unlink(path);
- }
- return rc;
-}
-#undef unlink
-#define unlink ___xxx_unlink
-
-static __inline__ int adb_mkdir(const char* path, int mode)
-{
- return _mkdir(path);
-}
-#undef mkdir
-#define mkdir ___xxx_mkdir
-
-extern int adb_open(const char* path, int options);
-extern int adb_creat(const char* path, int mode);
-extern int adb_read(int fd, void* buf, int len);
-extern int adb_write(int fd, const void* buf, int len);
-extern int adb_lseek(int fd, int pos, int where);
-extern int adb_shutdown(int fd);
-extern int adb_close(int fd);
-
-static __inline__ int unix_close(int fd)
-{
- return close(fd);
-}
-#undef close
-#define close ____xxx_close
-
-static __inline__ int unix_read(int fd, void* buf, size_t len)
-{
- return read(fd, buf, len);
-}
-#undef read
-#define read ___xxx_read
-
-static __inline__ int unix_write(int fd, const void* buf, size_t len)
-{
- return write(fd, buf, len);
-}
-#undef write
-#define write ___xxx_write
-
-static __inline__ int adb_open_mode(const char* path, int options, int mode)
-{
- return adb_open(path, options);
-}
-
-static __inline__ int unix_open(const char* path, int options,...)
-{
- if ((options & O_CREAT) == 0)
- {
- return open(path, options);
- }
- else
- {
- int mode;
- va_list args;
- va_start( args, options );
- mode = va_arg( args, int );
- va_end( args );
- return open(path, options, mode);
- }
-}
-#define open ___xxx_unix_open
-
-
-/* normally provided by <cutils/misc.h> */
-extern void* load_file(const char* pathname, unsigned* psize);
-
-/* normally provided by <cutils/sockets.h> */
-extern int socket_loopback_client(int port, int type);
-extern int socket_network_client(const char *host, int port, int type);
-extern int socket_loopback_server(int port, int type);
-extern int socket_inaddr_any_server(int port, int type);
-
-/* normally provided by "fdevent.h" */
-
-#define FDE_READ 0x0001
-#define FDE_WRITE 0x0002
-#define FDE_ERROR 0x0004
-#define FDE_DONT_CLOSE 0x0080
-
-typedef struct fdevent fdevent;
-
-typedef void (*fd_func)(int fd, unsigned events, void *userdata);
-
-fdevent *fdevent_create(int fd, fd_func func, void *arg);
-void fdevent_destroy(fdevent *fde);
-void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
-void fdevent_remove(fdevent *item);
-void fdevent_set(fdevent *fde, unsigned events);
-void fdevent_add(fdevent *fde, unsigned events);
-void fdevent_del(fdevent *fde, unsigned events);
-void fdevent_loop();
-
-struct fdevent {
- fdevent *next;
- fdevent *prev;
-
- int fd;
- int force_eof;
-
- unsigned short state;
- unsigned short events;
-
- fd_func func;
- void *arg;
-};
-
-static __inline__ void adb_sleep_ms( int mseconds )
-{
- Sleep( mseconds );
-}
-
-extern int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen);
-
-#undef accept
-#define accept ___xxx_accept
-
-static __inline__ int adb_socket_setbufsize( int fd, int bufsize )
-{
- int opt = bufsize;
- return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&opt, sizeof(opt));
-}
-
-extern int adb_socketpair( int sv[2] );
-
-static __inline__ char* adb_dirstart( const char* path )
-{
- char* p = strchr(path, '/');
- char* p2 = strchr(path, '\\');
-
- if ( !p )
- p = p2;
- else if ( p2 && p2 > p )
- p = p2;
-
- return p;
-}
-
-static __inline__ char* adb_dirstop( const char* path )
-{
- char* p = strrchr(path, '/');
- char* p2 = strrchr(path, '\\');
-
- if ( !p )
- p = p2;
- else if ( p2 && p2 > p )
- p = p2;
-
- return p;
-}
-
-static __inline__ int adb_is_absolute_host_path( const char* path )
-{
- return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
-}
-
-#else /* !_WIN32 a.k.a. Unix */
-
-#include "fdevent.h"
-#include <cutils/sockets.h>
-#include <cutils/properties.h>
-#include <cutils/misc.h>
-#include <signal.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include <pthread.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <string.h>
-
-#define OS_PATH_SEPARATOR '/'
-#define OS_PATH_SEPARATOR_STR "/"
-
-typedef pthread_mutex_t adb_mutex_t;
-
-#define ADB_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
-#define adb_mutex_init pthread_mutex_init
-#define adb_mutex_lock pthread_mutex_lock
-#define adb_mutex_unlock pthread_mutex_unlock
-#define adb_mutex_destroy pthread_mutex_destroy
-
-#define ADB_MUTEX_DEFINE(m) adb_mutex_t m = PTHREAD_MUTEX_INITIALIZER
-
-#define adb_cond_t pthread_cond_t
-#define adb_cond_init pthread_cond_init
-#define adb_cond_wait pthread_cond_wait
-#define adb_cond_broadcast pthread_cond_broadcast
-#define adb_cond_signal pthread_cond_signal
-#define adb_cond_destroy pthread_cond_destroy
-
-/* declare all mutexes */
-#define ADB_MUTEX(x) extern adb_mutex_t x;
-#include "mutex_list.h"
-
-static __inline__ void close_on_exec(int fd)
-{
- fcntl( fd, F_SETFD, FD_CLOEXEC );
-}
-
-static __inline__ int unix_open(const char* path, int options,...)
-{
- if ((options & O_CREAT) == 0)
- {
- return open(path, options);
- }
- else
- {
- int mode;
- va_list args;
- va_start( args, options );
- mode = va_arg( args, int );
- va_end( args );
- return open(path, options, mode);
- }
-}
-
-static __inline__ int adb_open_mode( const char* pathname, int options, int mode )
-{
- return open( pathname, options, mode );
-}
-
-static __inline__ int adb_creat(const char* path, int mode)
-{
- int fd = open(path, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, mode);
-
- if ( fd < 0 )
- return -1;
-
- close_on_exec(fd);
- return fd;
-}
-#undef creat
-#define creat ___xxx_creat
-
-static __inline__ int adb_open( const char* pathname, int options )
-{
- int fd = open( pathname, options );
- if (fd < 0)
- return -1;
- close_on_exec( fd );
- return fd;
-}
-#undef open
-#define open ___xxx_open
-
-static __inline__ int adb_shutdown(int fd)
-{
- return shutdown(fd, SHUT_RDWR);
-}
-#undef shutdown
-#define shutdown ____xxx_shutdown
-
-static __inline__ int adb_close(int fd)
-{
- return close(fd);
-}
-#undef close
-#define close ____xxx_close
-
-
-static __inline__ int adb_read(int fd, void* buf, size_t len)
-{
- return read(fd, buf, len);
-}
-
-#undef read
-#define read ___xxx_read
-
-static __inline__ int adb_write(int fd, const void* buf, size_t len)
-{
- return write(fd, buf, len);
-}
-#undef write
-#define write ___xxx_write
-
-static __inline__ int adb_lseek(int fd, int pos, int where)
-{
- return lseek(fd, pos, where);
-}
-#undef lseek
-#define lseek ___xxx_lseek
-
-static __inline__ int adb_unlink(const char* path)
-{
- return unlink(path);
-}
-#undef unlink
-#define unlink ___xxx_unlink
-
-static __inline__ int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
-{
- int fd;
-
- fd = accept(serverfd, addr, addrlen);
- if (fd >= 0)
- close_on_exec(fd);
-
- return fd;
-}
-
-#undef accept
-#define accept ___xxx_accept
-
-#define unix_read adb_read
-#define unix_write adb_write
-#define unix_close adb_close
-
-typedef pthread_t adb_thread_t;
-
-typedef void* (*adb_thread_func_t)( void* arg );
-
-static __inline__ int adb_thread_create( adb_thread_t *pthread, adb_thread_func_t start, void* arg )
-{
- pthread_attr_t attr;
-
- pthread_attr_init (&attr);
- pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
-
- return pthread_create( pthread, &attr, start, arg );
-}
-
-static __inline__ int adb_socket_setbufsize( int fd, int bufsize )
-{
- int opt = bufsize;
- return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
-}
-
-static __inline__ void disable_tcp_nagle(int fd)
-{
- int on = 1;
- setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on) );
-}
-
-
-static __inline__ int unix_socketpair( int d, int type, int protocol, int sv[2] )
-{
- return socketpair( d, type, protocol, sv );
-}
-
-static __inline__ int adb_socketpair( int sv[2] )
-{
- int rc;
-
- rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv );
- if (rc < 0)
- return -1;
-
- close_on_exec( sv[0] );
- close_on_exec( sv[1] );
- return 0;
-}
-
-#undef socketpair
-#define socketpair ___xxx_socketpair
-
-static __inline__ void adb_sleep_ms( int mseconds )
-{
- usleep( mseconds*1000 );
-}
-
-static __inline__ int adb_mkdir(const char* path, int mode)
-{
- return mkdir(path, mode);
-}
-#undef mkdir
-#define mkdir ___xxx_mkdir
-
-static __inline__ void adb_sysdeps_init(void)
-{
-}
-
-static __inline__ char* adb_dirstart(const char* path)
-{
- return strchr(path, '/');
-}
-
-static __inline__ char* adb_dirstop(const char* path)
-{
- return strrchr(path, '/');
-}
-
-static __inline__ int adb_is_absolute_host_path( const char* path )
-{
- return path[0] == '/';
-}
-
-#endif /* !_WIN32 */
-
-#endif /* _ADB_SYSDEPS_H */
diff --git a/minadbd/transport.c b/minadbd/transport.c
deleted file mode 100644
index 92679f5..0000000
--- a/minadbd/transport.c
+++ /dev/null
@@ -1,803 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-
-#include "sysdeps.h"
-
-#define TRACE_TAG TRACE_TRANSPORT
-#include "adb.h"
-
-static void transport_unref(atransport *t);
-
-static atransport transport_list = {
- .next = &transport_list,
- .prev = &transport_list,
-};
-
-ADB_MUTEX_DEFINE( transport_lock );
-
-#if ADB_TRACE
-#define MAX_DUMP_HEX_LEN 16
-static void dump_hex( const unsigned char* ptr, size_t len )
-{
- int nn, len2 = len;
- // Build a string instead of logging each character.
- // MAX chars in 2 digit hex, one space, MAX chars, one '\0'.
- char buffer[MAX_DUMP_HEX_LEN *2 + 1 + MAX_DUMP_HEX_LEN + 1 ], *pb = buffer;
-
- if (len2 > MAX_DUMP_HEX_LEN) len2 = MAX_DUMP_HEX_LEN;
-
- for (nn = 0; nn < len2; nn++) {
- sprintf(pb, "%02x", ptr[nn]);
- pb += 2;
- }
- sprintf(pb++, " ");
-
- for (nn = 0; nn < len2; nn++) {
- int c = ptr[nn];
- if (c < 32 || c > 127)
- c = '.';
- *pb++ = c;
- }
- *pb++ = '\0';
- DR("%s\n", buffer);
-}
-#endif
-
-void
-kick_transport(atransport* t)
-{
- if (t && !t->kicked)
- {
- int kicked;
-
- adb_mutex_lock(&transport_lock);
- kicked = t->kicked;
- if (!kicked)
- t->kicked = 1;
- adb_mutex_unlock(&transport_lock);
-
- if (!kicked)
- t->kick(t);
- }
-}
-
-void
-run_transport_disconnects(atransport* t)
-{
- adisconnect* dis = t->disconnects.next;
-
- D("%s: run_transport_disconnects\n", t->serial);
- while (dis != &t->disconnects) {
- adisconnect* next = dis->next;
- dis->func( dis->opaque, t );
- dis = next;
- }
-}
-
-#if ADB_TRACE
-static void
-dump_packet(const char* name, const char* func, apacket* p)
-{
- unsigned command = p->msg.command;
- int len = p->msg.data_length;
- char cmd[9];
- char arg0[12], arg1[12];
- int n;
-
- for (n = 0; n < 4; n++) {
- int b = (command >> (n*8)) & 255;
- if (b < 32 || b >= 127)
- break;
- cmd[n] = (char)b;
- }
- if (n == 4) {
- cmd[4] = 0;
- } else {
- /* There is some non-ASCII name in the command, so dump
- * the hexadecimal value instead */
- snprintf(cmd, sizeof cmd, "%08x", command);
- }
-
- if (p->msg.arg0 < 256U)
- snprintf(arg0, sizeof arg0, "%d", p->msg.arg0);
- else
- snprintf(arg0, sizeof arg0, "0x%x", p->msg.arg0);
-
- if (p->msg.arg1 < 256U)
- snprintf(arg1, sizeof arg1, "%d", p->msg.arg1);
- else
- snprintf(arg1, sizeof arg1, "0x%x", p->msg.arg1);
-
- D("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ",
- name, func, cmd, arg0, arg1, len);
- dump_hex(p->data, len);
-}
-#endif /* ADB_TRACE */
-
-static int
-read_packet(int fd, const char* name, apacket** ppacket)
-{
- char *p = (char*)ppacket; /* really read a packet address */
- int r;
- int len = sizeof(*ppacket);
- char buff[8];
- if (!name) {
- snprintf(buff, sizeof buff, "fd=%d", fd);
- name = buff;
- }
- while(len > 0) {
- r = adb_read(fd, p, len);
- if(r > 0) {
- len -= r;
- p += r;
- } else {
- D("%s: read_packet (fd=%d), error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno));
- if((r < 0) && (errno == EINTR)) continue;
- return -1;
- }
- }
-
-#if ADB_TRACE
- if (ADB_TRACING) {
- dump_packet(name, "from remote", *ppacket);
- }
-#endif
- return 0;
-}
-
-static int
-write_packet(int fd, const char* name, apacket** ppacket)
-{
- char *p = (char*) ppacket; /* we really write the packet address */
- int r, len = sizeof(ppacket);
- char buff[8];
- if (!name) {
- snprintf(buff, sizeof buff, "fd=%d", fd);
- name = buff;
- }
-
-#if ADB_TRACE
- if (ADB_TRACING) {
- dump_packet(name, "to remote", *ppacket);
- }
-#endif
- len = sizeof(ppacket);
- while(len > 0) {
- r = adb_write(fd, p, len);
- if(r > 0) {
- len -= r;
- p += r;
- } else {
- D("%s: write_packet (fd=%d) error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno));
- if((r < 0) && (errno == EINTR)) continue;
- return -1;
- }
- }
- return 0;
-}
-
-static void transport_socket_events(int fd, unsigned events, void *_t)
-{
- atransport *t = _t;
- D("transport_socket_events(fd=%d, events=%04x,...)\n", fd, events);
- if(events & FDE_READ){
- apacket *p = 0;
- if(read_packet(fd, t->serial, &p)){
- D("%s: failed to read packet from transport socket on fd %d\n", t->serial, fd);
- } else {
- handle_packet(p, (atransport *) _t);
- }
- }
-}
-
-void send_packet(apacket *p, atransport *t)
-{
- unsigned char *x;
- unsigned sum;
- unsigned count;
-
- p->msg.magic = p->msg.command ^ 0xffffffff;
-
- count = p->msg.data_length;
- x = (unsigned char *) p->data;
- sum = 0;
- while(count-- > 0){
- sum += *x++;
- }
- p->msg.data_check = sum;
-
- print_packet("send", p);
-
- if (t == NULL) {
- D("Transport is null \n");
- // Zap errno because print_packet() and other stuff have errno effect.
- errno = 0;
- fatal_errno("Transport is null");
- }
-
- if(write_packet(t->transport_socket, t->serial, &p)){
- fatal_errno("cannot enqueue packet on transport socket");
- }
-}
-
-/* The transport is opened by transport_register_func before
-** the input and output threads are started.
-**
-** The output thread issues a SYNC(1, token) message to let
-** the input thread know to start things up. In the event
-** of transport IO failure, the output thread will post a
-** SYNC(0,0) message to ensure shutdown.
-**
-** The transport will not actually be closed until both
-** threads exit, but the input thread will kick the transport
-** on its way out to disconnect the underlying device.
-*/
-
-static void *output_thread(void *_t)
-{
- atransport *t = _t;
- apacket *p;
-
- D("%s: starting transport output thread on fd %d, SYNC online (%d)\n",
- t->serial, t->fd, t->sync_token + 1);
- p = get_apacket();
- p->msg.command = A_SYNC;
- p->msg.arg0 = 1;
- p->msg.arg1 = ++(t->sync_token);
- p->msg.magic = A_SYNC ^ 0xffffffff;
- if(write_packet(t->fd, t->serial, &p)) {
- put_apacket(p);
- D("%s: failed to write SYNC packet\n", t->serial);
- goto oops;
- }
-
- D("%s: data pump started\n", t->serial);
- for(;;) {
- p = get_apacket();
-
- if(t->read_from_remote(p, t) == 0){
- D("%s: received remote packet, sending to transport\n",
- t->serial);
- if(write_packet(t->fd, t->serial, &p)){
- put_apacket(p);
- D("%s: failed to write apacket to transport\n", t->serial);
- goto oops;
- }
- } else {
- D("%s: remote read failed for transport\n", t->serial);
- put_apacket(p);
- break;
- }
- }
-
- D("%s: SYNC offline for transport\n", t->serial);
- p = get_apacket();
- p->msg.command = A_SYNC;
- p->msg.arg0 = 0;
- p->msg.arg1 = 0;
- p->msg.magic = A_SYNC ^ 0xffffffff;
- if(write_packet(t->fd, t->serial, &p)) {
- put_apacket(p);
- D("%s: failed to write SYNC apacket to transport", t->serial);
- }
-
-oops:
- D("%s: transport output thread is exiting\n", t->serial);
- kick_transport(t);
- transport_unref(t);
- return 0;
-}
-
-static void *input_thread(void *_t)
-{
- atransport *t = _t;
- apacket *p;
- int active = 0;
-
- D("%s: starting transport input thread, reading from fd %d\n",
- t->serial, t->fd);
-
- for(;;){
- if(read_packet(t->fd, t->serial, &p)) {
- D("%s: failed to read apacket from transport on fd %d\n",
- t->serial, t->fd );
- break;
- }
- if(p->msg.command == A_SYNC){
- if(p->msg.arg0 == 0) {
- D("%s: transport SYNC offline\n", t->serial);
- put_apacket(p);
- break;
- } else {
- if(p->msg.arg1 == t->sync_token) {
- D("%s: transport SYNC online\n", t->serial);
- active = 1;
- } else {
- D("%s: transport ignoring SYNC %d != %d\n",
- t->serial, p->msg.arg1, t->sync_token);
- }
- }
- } else {
- if(active) {
- D("%s: transport got packet, sending to remote\n", t->serial);
- t->write_to_remote(p, t);
- } else {
- D("%s: transport ignoring packet while offline\n", t->serial);
- }
- }
-
- put_apacket(p);
- }
-
- // this is necessary to avoid a race condition that occured when a transport closes
- // while a client socket is still active.
- close_all_sockets(t);
-
- D("%s: transport input thread is exiting, fd %d\n", t->serial, t->fd);
- kick_transport(t);
- transport_unref(t);
- return 0;
-}
-
-
-static int transport_registration_send = -1;
-static int transport_registration_recv = -1;
-static fdevent transport_registration_fde;
-
-void update_transports(void)
-{
- // nothing to do on the device side
-}
-
-typedef struct tmsg tmsg;
-struct tmsg
-{
- atransport *transport;
- int action;
-};
-
-static int
-transport_read_action(int fd, struct tmsg* m)
-{
- char *p = (char*)m;
- int len = sizeof(*m);
- int r;
-
- while(len > 0) {
- r = adb_read(fd, p, len);
- if(r > 0) {
- len -= r;
- p += r;
- } else {
- if((r < 0) && (errno == EINTR)) continue;
- D("transport_read_action: on fd %d, error %d: %s\n",
- fd, errno, strerror(errno));
- return -1;
- }
- }
- return 0;
-}
-
-static int
-transport_write_action(int fd, struct tmsg* m)
-{
- char *p = (char*)m;
- int len = sizeof(*m);
- int r;
-
- while(len > 0) {
- r = adb_write(fd, p, len);
- if(r > 0) {
- len -= r;
- p += r;
- } else {
- if((r < 0) && (errno == EINTR)) continue;
- D("transport_write_action: on fd %d, error %d: %s\n",
- fd, errno, strerror(errno));
- return -1;
- }
- }
- return 0;
-}
-
-static void transport_registration_func(int _fd, unsigned ev, void *data)
-{
- tmsg m;
- adb_thread_t output_thread_ptr;
- adb_thread_t input_thread_ptr;
- int s[2];
- atransport *t;
-
- if(!(ev & FDE_READ)) {
- return;
- }
-
- if(transport_read_action(_fd, &m)) {
- fatal_errno("cannot read transport registration socket");
- }
-
- t = m.transport;
-
- if(m.action == 0){
- D("transport: %s removing and free'ing %d\n", t->serial, t->transport_socket);
-
- /* IMPORTANT: the remove closes one half of the
- ** socket pair. The close closes the other half.
- */
- fdevent_remove(&(t->transport_fde));
- adb_close(t->fd);
-
- adb_mutex_lock(&transport_lock);
- t->next->prev = t->prev;
- t->prev->next = t->next;
- adb_mutex_unlock(&transport_lock);
-
- run_transport_disconnects(t);
-
- if (t->product)
- free(t->product);
- if (t->serial)
- free(t->serial);
-
- memset(t,0xee,sizeof(atransport));
- free(t);
-
- update_transports();
- return;
- }
-
- /* don't create transport threads for inaccessible devices */
- if (t->connection_state != CS_NOPERM) {
- /* initial references are the two threads */
- t->ref_count = 2;
-
- if(adb_socketpair(s)) {
- fatal_errno("cannot open transport socketpair");
- }
-
- D("transport: %s (%d,%d) starting\n", t->serial, s[0], s[1]);
-
- t->transport_socket = s[0];
- t->fd = s[1];
-
- fdevent_install(&(t->transport_fde),
- t->transport_socket,
- transport_socket_events,
- t);
-
- fdevent_set(&(t->transport_fde), FDE_READ);
-
- if(adb_thread_create(&input_thread_ptr, input_thread, t)){
- fatal_errno("cannot create input thread");
- }
-
- if(adb_thread_create(&output_thread_ptr, output_thread, t)){
- fatal_errno("cannot create output thread");
- }
- }
-
- /* put us on the master device list */
- adb_mutex_lock(&transport_lock);
- t->next = &transport_list;
- t->prev = transport_list.prev;
- t->next->prev = t;
- t->prev->next = t;
- adb_mutex_unlock(&transport_lock);
-
- t->disconnects.next = t->disconnects.prev = &t->disconnects;
-
- update_transports();
-}
-
-void init_transport_registration(void)
-{
- int s[2];
-
- if(adb_socketpair(s)){
- fatal_errno("cannot open transport registration socketpair");
- }
-
- transport_registration_send = s[0];
- transport_registration_recv = s[1];
-
- fdevent_install(&transport_registration_fde,
- transport_registration_recv,
- transport_registration_func,
- 0);
-
- fdevent_set(&transport_registration_fde, FDE_READ);
-}
-
-/* the fdevent select pump is single threaded */
-static void register_transport(atransport *transport)
-{
- tmsg m;
- m.transport = transport;
- m.action = 1;
- D("transport: %s registered\n", transport->serial);
- if(transport_write_action(transport_registration_send, &m)) {
- fatal_errno("cannot write transport registration socket\n");
- }
-}
-
-static void remove_transport(atransport *transport)
-{
- tmsg m;
- m.transport = transport;
- m.action = 0;
- D("transport: %s removed\n", transport->serial);
- if(transport_write_action(transport_registration_send, &m)) {
- fatal_errno("cannot write transport registration socket\n");
- }
-}
-
-
-static void transport_unref_locked(atransport *t)
-{
- t->ref_count--;
- if (t->ref_count == 0) {
- D("transport: %s unref (kicking and closing)\n", t->serial);
- if (!t->kicked) {
- t->kicked = 1;
- t->kick(t);
- }
- t->close(t);
- remove_transport(t);
- } else {
- D("transport: %s unref (count=%d)\n", t->serial, t->ref_count);
- }
-}
-
-static void transport_unref(atransport *t)
-{
- if (t) {
- adb_mutex_lock(&transport_lock);
- transport_unref_locked(t);
- adb_mutex_unlock(&transport_lock);
- }
-}
-
-void add_transport_disconnect(atransport* t, adisconnect* dis)
-{
- adb_mutex_lock(&transport_lock);
- dis->next = &t->disconnects;
- dis->prev = dis->next->prev;
- dis->prev->next = dis;
- dis->next->prev = dis;
- adb_mutex_unlock(&transport_lock);
-}
-
-void remove_transport_disconnect(atransport* t, adisconnect* dis)
-{
- dis->prev->next = dis->next;
- dis->next->prev = dis->prev;
- dis->next = dis->prev = dis;
-}
-
-
-atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char** error_out)
-{
- atransport *t;
- atransport *result = NULL;
- int ambiguous = 0;
-
-retry:
- if (error_out)
- *error_out = "device not found";
-
- adb_mutex_lock(&transport_lock);
- for (t = transport_list.next; t != &transport_list; t = t->next) {
- if (t->connection_state == CS_NOPERM) {
- if (error_out)
- *error_out = "insufficient permissions for device";
- continue;
- }
-
- /* check for matching serial number */
- if (serial) {
- if (t->serial && !strcmp(serial, t->serial)) {
- result = t;
- break;
- }
- } else {
- if (ttype == kTransportUsb && t->type == kTransportUsb) {
- if (result) {
- if (error_out)
- *error_out = "more than one device";
- ambiguous = 1;
- result = NULL;
- break;
- }
- result = t;
- } else if (ttype == kTransportLocal && t->type == kTransportLocal) {
- if (result) {
- if (error_out)
- *error_out = "more than one emulator";
- ambiguous = 1;
- result = NULL;
- break;
- }
- result = t;
- } else if (ttype == kTransportAny) {
- if (result) {
- if (error_out)
- *error_out = "more than one device and emulator";
- ambiguous = 1;
- result = NULL;
- break;
- }
- result = t;
- }
- }
- }
- adb_mutex_unlock(&transport_lock);
-
- if (result) {
- /* offline devices are ignored -- they are either being born or dying */
- if (result && result->connection_state == CS_OFFLINE) {
- if (error_out)
- *error_out = "device offline";
- result = NULL;
- }
- /* check for required connection state */
- if (result && state != CS_ANY && result->connection_state != state) {
- if (error_out)
- *error_out = "invalid device state";
- result = NULL;
- }
- }
-
- if (result) {
- /* found one that we can take */
- if (error_out)
- *error_out = NULL;
- } else if (state != CS_ANY && (serial || !ambiguous)) {
- adb_sleep_ms(1000);
- goto retry;
- }
-
- return result;
-}
-
-void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable)
-{
- atransport *t = calloc(1, sizeof(atransport));
- D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb,
- serial ? serial : "");
- init_usb_transport(t, usb, (writeable ? CS_OFFLINE : CS_NOPERM));
- if(serial) {
- t->serial = strdup(serial);
- }
- register_transport(t);
-}
-
-/* this should only be used for transports with connection_state == CS_NOPERM */
-void unregister_usb_transport(usb_handle *usb)
-{
- atransport *t;
- adb_mutex_lock(&transport_lock);
- for(t = transport_list.next; t != &transport_list; t = t->next) {
- if (t->usb == usb && t->connection_state == CS_NOPERM) {
- t->next->prev = t->prev;
- t->prev->next = t->next;
- break;
- }
- }
- adb_mutex_unlock(&transport_lock);
-}
-
-#undef TRACE_TAG
-#define TRACE_TAG TRACE_RWX
-
-int readx(int fd, void *ptr, size_t len)
-{
- char *p = ptr;
- int r;
-#if ADB_TRACE
- size_t len0 = len;
-#endif
- D("readx: fd=%d wanted=%d\n", fd, (int)len);
- while(len > 0) {
- r = adb_read(fd, p, len);
- if(r > 0) {
- len -= r;
- p += r;
- } else {
- if (r < 0) {
- D("readx: fd=%d error %d: %s\n", fd, errno, strerror(errno));
- if (errno == EINTR)
- continue;
- } else {
- D("readx: fd=%d disconnected\n", fd);
- }
- return -1;
- }
- }
-
-#if ADB_TRACE
- D("readx: fd=%d wanted=%zu got=%zu\n", fd, len0, len0 - len);
- dump_hex( ptr, len0 );
-#endif
- return 0;
-}
-
-int writex(int fd, const void *ptr, size_t len)
-{
- char *p = (char*) ptr;
- int r;
-
-#if ADB_TRACE
- D("writex: fd=%d len=%d: ", fd, (int)len);
- dump_hex( ptr, len );
-#endif
- while(len > 0) {
- r = adb_write(fd, p, len);
- if(r > 0) {
- len -= r;
- p += r;
- } else {
- if (r < 0) {
- D("writex: fd=%d error %d: %s\n", fd, errno, strerror(errno));
- if (errno == EINTR)
- continue;
- } else {
- D("writex: fd=%d disconnected\n", fd);
- }
- return -1;
- }
- }
- return 0;
-}
-
-int check_header(apacket *p)
-{
- if(p->msg.magic != (p->msg.command ^ 0xffffffff)) {
- D("check_header(): invalid magic\n");
- return -1;
- }
-
- if(p->msg.data_length > MAX_PAYLOAD) {
- D("check_header(): %d > MAX_PAYLOAD\n", p->msg.data_length);
- return -1;
- }
-
- return 0;
-}
-
-int check_data(apacket *p)
-{
- unsigned count, sum;
- unsigned char *x;
-
- count = p->msg.data_length;
- x = p->data;
- sum = 0;
- while(count-- > 0) {
- sum += *x++;
- }
-
- if(sum != p->msg.data_check) {
- return -1;
- } else {
- return 0;
- }
-}
diff --git a/minadbd/transport.h b/minadbd/transport.h
deleted file mode 100644
index 992e052..0000000
--- a/minadbd/transport.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2011 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 __TRANSPORT_H
-#define __TRANSPORT_H
-
-/* convenience wrappers around read/write that will retry on
-** EINTR and/or short read/write. Returns 0 on success, -1
-** on error or EOF.
-*/
-int readx(int fd, void *ptr, size_t len);
-int writex(int fd, const void *ptr, size_t len);
-#endif /* __TRANSPORT_H */
diff --git a/minadbd/transport_usb.c b/minadbd/transport_usb.c
deleted file mode 100644
index 91cbf61..0000000
--- a/minadbd/transport_usb.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <sysdeps.h>
-
-#define TRACE_TAG TRACE_TRANSPORT
-#include "adb.h"
-
-#ifdef HAVE_BIG_ENDIAN
-#define H4(x) (((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24)
-static inline void fix_endians(apacket *p)
-{
- p->msg.command = H4(p->msg.command);
- p->msg.arg0 = H4(p->msg.arg0);
- p->msg.arg1 = H4(p->msg.arg1);
- p->msg.data_length = H4(p->msg.data_length);
- p->msg.data_check = H4(p->msg.data_check);
- p->msg.magic = H4(p->msg.magic);
-}
-unsigned host_to_le32(unsigned n)
-{
- return H4(n);
-}
-#else
-#define fix_endians(p) do {} while (0)
-unsigned host_to_le32(unsigned n)
-{
- return n;
-}
-#endif
-
-static int remote_read(apacket *p, atransport *t)
-{
- if(usb_read(t->usb, &p->msg, sizeof(amessage))){
- D("remote usb: read terminated (message)\n");
- return -1;
- }
-
- fix_endians(p);
-
- if(check_header(p)) {
- D("remote usb: check_header failed\n");
- return -1;
- }
-
- if(p->msg.data_length) {
- if(usb_read(t->usb, p->data, p->msg.data_length)){
- D("remote usb: terminated (data)\n");
- return -1;
- }
- }
-
- if(check_data(p)) {
- D("remote usb: check_data failed\n");
- return -1;
- }
-
- return 0;
-}
-
-static int remote_write(apacket *p, atransport *t)
-{
- unsigned size = p->msg.data_length;
-
- fix_endians(p);
-
- if(usb_write(t->usb, &p->msg, sizeof(amessage))) {
- D("remote usb: 1 - write terminated\n");
- return -1;
- }
- if(p->msg.data_length == 0) return 0;
- if(usb_write(t->usb, &p->data, size)) {
- D("remote usb: 2 - write terminated\n");
- return -1;
- }
-
- return 0;
-}
-
-static void remote_close(atransport *t)
-{
- usb_close(t->usb);
- t->usb = 0;
-}
-
-static void remote_kick(atransport *t)
-{
- usb_kick(t->usb);
-}
-
-void init_usb_transport(atransport *t, usb_handle *h, int state)
-{
- D("transport: usb\n");
- t->close = remote_close;
- t->kick = remote_kick;
- t->read_from_remote = remote_read;
- t->write_to_remote = remote_write;
- t->sync_token = 1;
- t->connection_state = state;
- t->type = kTransportUsb;
- t->usb = h;
-
- HOST = 0;
-}
diff --git a/minadbd/usb_linux_client.c b/minadbd/usb_linux_client.c
deleted file mode 100644
index 29bab15..0000000
--- a/minadbd/usb_linux_client.c
+++ /dev/null
@@ -1,493 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-#include <linux/usb/ch9.h>
-#include <linux/usb/functionfs.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <errno.h>
-
-#include "sysdeps.h"
-
-#define TRACE_TAG TRACE_USB
-#include "adb.h"
-
-#define MAX_PACKET_SIZE_FS 64
-#define MAX_PACKET_SIZE_HS 512
-
-#define cpu_to_le16(x) htole16(x)
-#define cpu_to_le32(x) htole32(x)
-
-struct usb_handle
-{
- int fd;
- adb_cond_t notify;
- adb_mutex_t lock;
-
- int (*write)(usb_handle *h, const void *data, int len);
- int (*read)(usb_handle *h, void *data, int len);
- void (*kick)(usb_handle *h);
-
- int control;
- int bulk_out; /* "out" from the host's perspective => source for adbd */
- int bulk_in; /* "in" from the host's perspective => sink for adbd */
-};
-
-static const struct {
- struct usb_functionfs_descs_head header;
- struct {
- struct usb_interface_descriptor intf;
- struct usb_endpoint_descriptor_no_audio source;
- struct usb_endpoint_descriptor_no_audio sink;
- } __attribute__((packed)) fs_descs, hs_descs;
-} __attribute__((packed)) descriptors = {
- .header = {
- .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC),
- .length = cpu_to_le32(sizeof(descriptors)),
- .fs_count = 3,
- .hs_count = 3,
- },
- .fs_descs = {
- .intf = {
- .bLength = sizeof(descriptors.fs_descs.intf),
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 0,
- .bNumEndpoints = 2,
- .bInterfaceClass = ADB_CLASS,
- .bInterfaceSubClass = ADB_SUBCLASS,
- .bInterfaceProtocol = ADB_PROTOCOL,
- .iInterface = 1, /* first string from the provided table */
- },
- .source = {
- .bLength = sizeof(descriptors.fs_descs.source),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 1 | USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = MAX_PACKET_SIZE_FS,
- },
- .sink = {
- .bLength = sizeof(descriptors.fs_descs.sink),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 2 | USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = MAX_PACKET_SIZE_FS,
- },
- },
- .hs_descs = {
- .intf = {
- .bLength = sizeof(descriptors.hs_descs.intf),
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 0,
- .bNumEndpoints = 2,
- .bInterfaceClass = ADB_CLASS,
- .bInterfaceSubClass = ADB_SUBCLASS,
- .bInterfaceProtocol = ADB_PROTOCOL,
- .iInterface = 1, /* first string from the provided table */
- },
- .source = {
- .bLength = sizeof(descriptors.hs_descs.source),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 1 | USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = MAX_PACKET_SIZE_HS,
- },
- .sink = {
- .bLength = sizeof(descriptors.hs_descs.sink),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 2 | USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = MAX_PACKET_SIZE_HS,
- },
- },
-};
-
-#define STR_INTERFACE_ "ADB Interface"
-
-static const struct {
- struct usb_functionfs_strings_head header;
- struct {
- __le16 code;
- const char str1[sizeof(STR_INTERFACE_)];
- } __attribute__((packed)) lang0;
-} __attribute__((packed)) strings = {
- .header = {
- .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
- .length = cpu_to_le32(sizeof(strings)),
- .str_count = cpu_to_le32(1),
- .lang_count = cpu_to_le32(1),
- },
- .lang0 = {
- cpu_to_le16(0x0409), /* en-us */
- STR_INTERFACE_,
- },
-};
-
-void usb_cleanup()
-{
- // nothing to do here
-}
-
-static void *usb_adb_open_thread(void *x)
-{
- struct usb_handle *usb = (struct usb_handle *)x;
- int fd;
-
- while (1) {
- // wait until the USB device needs opening
- adb_mutex_lock(&usb->lock);
- while (usb->fd != -1)
- adb_cond_wait(&usb->notify, &usb->lock);
- adb_mutex_unlock(&usb->lock);
-
- D("[ usb_thread - opening device ]\n");
- do {
- /* XXX use inotify? */
- fd = unix_open("/dev/android_adb", O_RDWR);
- if (fd < 0) {
- // to support older kernels
- fd = unix_open("/dev/android", O_RDWR);
- fprintf(stderr, "usb_adb_open_thread: %d\n", fd );
- }
- if (fd < 0) {
- adb_sleep_ms(1000);
- }
- } while (fd < 0);
- D("[ opening device succeeded ]\n");
-
- close_on_exec(fd);
- usb->fd = fd;
-
- D("[ usb_thread - registering device ]\n");
- register_usb_transport(usb, 0, 1);
- }
-
- // never gets here
- return 0;
-}
-
-static int usb_adb_write(usb_handle *h, const void *data, int len)
-{
- int n;
-
- D("about to write (fd=%d, len=%d)\n", h->fd, len);
- n = adb_write(h->fd, data, len);
- if(n != len) {
- D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
- h->fd, n, errno, strerror(errno));
- return -1;
- }
- D("[ done fd=%d ]\n", h->fd);
- return 0;
-}
-
-static int usb_adb_read(usb_handle *h, void *data, int len)
-{
- int n;
-
- D("about to read (fd=%d, len=%d)\n", h->fd, len);
- n = adb_read(h->fd, data, len);
- if(n != len) {
- D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
- h->fd, n, errno, strerror(errno));
- return -1;
- }
- D("[ done fd=%d ]\n", h->fd);
- return 0;
-}
-
-static void usb_adb_kick(usb_handle *h)
-{
- D("usb_kick\n");
- adb_mutex_lock(&h->lock);
- adb_close(h->fd);
- h->fd = -1;
-
- // notify usb_adb_open_thread that we are disconnected
- adb_cond_signal(&h->notify);
- adb_mutex_unlock(&h->lock);
-}
-
-static void usb_adb_init()
-{
- usb_handle *h;
- adb_thread_t tid;
- int fd;
-
- h = calloc(1, sizeof(usb_handle));
-
- h->write = usb_adb_write;
- h->read = usb_adb_read;
- h->kick = usb_adb_kick;
- h->fd = -1;
-
- adb_cond_init(&h->notify, 0);
- adb_mutex_init(&h->lock, 0);
-
- fprintf(stderr, "Starting to open usb_init()\n");
- // Open the file /dev/android_adb_enable to trigger
- // the enabling of the adb USB function in the kernel.
- // We never touch this file again - just leave it open
- // indefinitely so the kernel will know when we are running
- // and when we are not.
- fd = unix_open("/dev/android_adb_enable", O_RDWR);
- fprintf(stderr, "unix_open to open usb_init(): %d\n", fd);
- if (fd < 0) {
- D("failed to open /dev/android_adb_enable\n");
- } else {
- close_on_exec(fd);
- }
-
- D("[ usb_init - starting thread ]\n");
- if(adb_thread_create(&tid, usb_adb_open_thread, h)){
- fatal_errno("cannot create usb thread");
- fprintf(stderr, "cannot create the usb thread()\n");
- }
-}
-
-
-static void init_functionfs(struct usb_handle *h)
-{
- ssize_t ret;
-
- D("OPENING %s\n", USB_FFS_ADB_EP0);
- h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR);
- if (h->control < 0) {
- D("[ %s: cannot open control endpoint: errno=%d]\n", USB_FFS_ADB_EP0, errno);
- goto err;
- }
-
- ret = adb_write(h->control, &descriptors, sizeof(descriptors));
- if (ret < 0) {
- D("[ %s: write descriptors failed: errno=%d ]\n", USB_FFS_ADB_EP0, errno);
- goto err;
- }
-
- ret = adb_write(h->control, &strings, sizeof(strings));
- if (ret < 0) {
- D("[ %s: writing strings failed: errno=%d]\n", USB_FFS_ADB_EP0, errno);
- goto err;
- }
-
- h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR);
- if (h->bulk_out < 0) {
- D("[ %s: cannot open bulk-out ep: errno=%d ]\n", USB_FFS_ADB_OUT, errno);
- goto err;
- }
-
- h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR);
- if (h->bulk_in < 0) {
- D("[ %s: cannot open bulk-in ep: errno=%d ]\n", USB_FFS_ADB_IN, errno);
- goto err;
- }
-
- return;
-
-err:
- if (h->bulk_in > 0) {
- adb_close(h->bulk_in);
- h->bulk_in = -1;
- }
- if (h->bulk_out > 0) {
- adb_close(h->bulk_out);
- h->bulk_out = -1;
- }
- if (h->control > 0) {
- adb_close(h->control);
- h->control = -1;
- }
- return;
-}
-
-static void *usb_ffs_open_thread(void *x)
-{
- struct usb_handle *usb = (struct usb_handle *)x;
-
- while (1) {
- // wait until the USB device needs opening
- adb_mutex_lock(&usb->lock);
- while (usb->control != -1)
- adb_cond_wait(&usb->notify, &usb->lock);
- adb_mutex_unlock(&usb->lock);
-
- while (1) {
- init_functionfs(usb);
-
- if (usb->control >= 0)
- break;
-
- adb_sleep_ms(1000);
- }
-
- D("[ usb_thread - registering device ]\n");
- register_usb_transport(usb, 0, 1);
- }
-
- // never gets here
- return 0;
-}
-
-static int bulk_write(int bulk_in, const char *buf, size_t length)
-{
- size_t count = 0;
- int ret;
-
- do {
- ret = adb_write(bulk_in, buf + count, length - count);
- if (ret < 0) {
- if (errno != EINTR)
- return ret;
- } else {
- count += ret;
- }
- } while (count < length);
-
- D("[ bulk_write done fd=%d ]\n", bulk_in);
- return count;
-}
-
-static int usb_ffs_write(usb_handle *h, const void *data, int len)
-{
- int n;
-
- D("about to write (fd=%d, len=%d)\n", h->bulk_in, len);
- n = bulk_write(h->bulk_in, data, len);
- if (n != len) {
- D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
- h->bulk_in, n, errno, strerror(errno));
- return -1;
- }
- D("[ done fd=%d ]\n", h->bulk_in);
- return 0;
-}
-
-static int bulk_read(int bulk_out, char *buf, size_t length)
-{
- size_t count = 0;
- int ret;
-
- do {
- ret = adb_read(bulk_out, buf + count, length - count);
- if (ret < 0) {
- if (errno != EINTR) {
- D("[ bulk_read failed fd=%d length=%zu count=%zu ]\n",
- bulk_out, length, count);
- return ret;
- }
- } else {
- count += ret;
- }
- } while (count < length);
-
- return count;
-}
-
-static int usb_ffs_read(usb_handle *h, void *data, int len)
-{
- int n;
-
- D("about to read (fd=%d, len=%d)\n", h->bulk_out, len);
- n = bulk_read(h->bulk_out, data, len);
- if (n != len) {
- D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
- h->bulk_out, n, errno, strerror(errno));
- return -1;
- }
- D("[ done fd=%d ]\n", h->bulk_out);
- return 0;
-}
-
-static void usb_ffs_kick(usb_handle *h)
-{
- int err;
-
- err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT);
- if (err < 0)
- D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in, errno);
-
- err = ioctl(h->bulk_out, FUNCTIONFS_CLEAR_HALT);
- if (err < 0)
- D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out, errno);
-
- adb_mutex_lock(&h->lock);
- adb_close(h->control);
- adb_close(h->bulk_out);
- adb_close(h->bulk_in);
- h->control = h->bulk_out = h->bulk_in = -1;
-
- // notify usb_ffs_open_thread that we are disconnected
- adb_cond_signal(&h->notify);
- adb_mutex_unlock(&h->lock);
-}
-
-static void usb_ffs_init()
-{
- usb_handle *h;
- adb_thread_t tid;
-
- D("[ usb_init - using FunctionFS ]\n");
-
- h = calloc(1, sizeof(usb_handle));
-
- h->write = usb_ffs_write;
- h->read = usb_ffs_read;
- h->kick = usb_ffs_kick;
-
- h->control = -1;
- h->bulk_out = -1;
- h->bulk_out = -1;
-
- adb_cond_init(&h->notify, 0);
- adb_mutex_init(&h->lock, 0);
-
- D("[ usb_init - starting thread ]\n");
- if (adb_thread_create(&tid, usb_ffs_open_thread, h)){
- fatal_errno("[ cannot create usb thread ]\n");
- }
-}
-
-void usb_init()
-{
- if (access(USB_FFS_ADB_EP0, F_OK) == 0)
- usb_ffs_init();
- else
- usb_adb_init();
-}
-
-int usb_write(usb_handle *h, const void *data, int len)
-{
- return h->write(h, data, len);
-}
-
-int usb_read(usb_handle *h, void *data, int len)
-{
- return h->read(h, data, len);
-}
-int usb_close(usb_handle *h)
-{
- // nothing to do here
- return 0;
-}
-
-void usb_kick(usb_handle *h)
-{
- h->kick(h);
-}
diff --git a/minadbd/utils.c b/minadbd/utils.c
deleted file mode 100644
index 91518ba..0000000
--- a/minadbd/utils.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "utils.h"
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-
-char*
-buff_addc (char* buff, char* buffEnd, int c)
-{
- int avail = buffEnd - buff;
-
- if (avail <= 0) /* already in overflow mode */
- return buff;
-
- if (avail == 1) { /* overflowing, the last byte is reserved for zero */
- buff[0] = 0;
- return buff + 1;
- }
-
- buff[0] = (char) c; /* add char and terminating zero */
- buff[1] = 0;
- return buff + 1;
-}
-
-char*
-buff_adds (char* buff, char* buffEnd, const char* s)
-{
- int slen = strlen(s);
-
- return buff_addb(buff, buffEnd, s, slen);
-}
-
-char*
-buff_addb (char* buff, char* buffEnd, const void* data, int len)
-{
- int avail = (buffEnd - buff);
-
- if (avail <= 0 || len <= 0) /* already overflowing */
- return buff;
-
- if (len > avail)
- len = avail;
-
- memcpy(buff, data, len);
-
- buff += len;
-
- /* ensure there is a terminating zero */
- if (buff >= buffEnd) { /* overflow */
- buff[-1] = 0;
- } else
- buff[0] = 0;
-
- return buff;
-}
-
-char*
-buff_add (char* buff, char* buffEnd, const char* format, ... )
-{
- int avail;
-
- avail = (buffEnd - buff);
-
- if (avail > 0) {
- va_list args;
- int nn;
-
- va_start(args, format);
- nn = vsnprintf( buff, avail, format, args);
- va_end(args);
-
- if (nn < 0) {
- /* some C libraries return -1 in case of overflow,
- * but they will also do that if the format spec is
- * invalid. We assume ADB is not buggy enough to
- * trigger that last case. */
- nn = avail;
- }
- else if (nn > avail) {
- nn = avail;
- }
-
- buff += nn;
-
- /* ensure that there is a terminating zero */
- if (buff >= buffEnd)
- buff[-1] = 0;
- else
- buff[0] = 0;
- }
- return buff;
-}
diff --git a/minadbd/utils.h b/minadbd/utils.h
deleted file mode 100644
index f70ecd2..0000000
--- a/minadbd/utils.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef _ADB_UTILS_H
-#define _ADB_UTILS_H
-
-/* bounded buffer functions */
-
-/* all these functions are used to append data to a bounded buffer.
- *
- * after each operation, the buffer is guaranteed to be zero-terminated,
- * even in the case of an overflow. they all return the new buffer position
- * which allows one to use them in succession, only checking for overflows
- * at the end. For example:
- *
- * BUFF_DECL(temp,p,end,1024);
- * char* p;
- *
- * p = buff_addc(temp, end, '"');
- * p = buff_adds(temp, end, string);
- * p = buff_addc(temp, end, '"');
- *
- * if (p >= end) {
- * overflow detected. note that 'temp' is
- * zero-terminated for safety.
- * }
- * return strdup(temp);
- */
-
-/* tries to add a character to the buffer, in case of overflow
- * this will only write a terminating zero and return buffEnd.
- */
-char* buff_addc (char* buff, char* buffEnd, int c);
-
-/* tries to add a string to the buffer */
-char* buff_adds (char* buff, char* buffEnd, const char* s);
-
-/* tries to add a bytes to the buffer. the input can contain zero bytes,
- * but a terminating zero will always be appended at the end anyway
- */
-char* buff_addb (char* buff, char* buffEnd, const void* data, int len);
-
-/* tries to add a formatted string to a bounded buffer */
-char* buff_add (char* buff, char* buffEnd, const char* format, ... );
-
-/* convenience macro used to define a bounded buffer, as well as
- * a 'cursor' and 'end' variables all in one go.
- *
- * note: this doesn't place an initial terminating zero in the buffer,
- * you need to use one of the buff_ functions for this. or simply
- * do _cursor[0] = 0 manually.
- */
-#define BUFF_DECL(_buff,_cursor,_end,_size) \
- char _buff[_size], *_cursor=_buff, *_end = _cursor + (_size)
-
-#endif /* _ADB_UTILS_H */
diff --git a/minui/Android.mk b/minui/Android.mk
index df4aac1..97724fb 100644
--- a/minui/Android.mk
+++ b/minui/Android.mk
@@ -1,21 +1,29 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := graphics.c graphics_adf.c graphics_fbdev.c events.c \
- resources.c
-
-LOCAL_C_INCLUDES +=\
- external/libpng\
- external/zlib
+LOCAL_SRC_FILES := \
+ events.cpp \
+ graphics.cpp \
+ graphics_adf.cpp \
+ graphics_drm.cpp \
+ graphics_fbdev.cpp \
+ resources.cpp \
LOCAL_WHOLE_STATIC_LIBRARIES += libadf
+LOCAL_WHOLE_STATIC_LIBRARIES += libdrm
+LOCAL_STATIC_LIBRARIES += libpng
LOCAL_MODULE := libminui
+LOCAL_CLANG := true
+
# This used to compare against values in double-quotes (which are just
# ordinary characters in this context). Strip double-quotes from the
# value so that either will work.
+ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),ABGR_8888)
+ LOCAL_CFLAGS += -DRECOVERY_ABGR
+endif
ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),RGBX_8888)
LOCAL_CFLAGS += -DRECOVERY_RGBX
endif
@@ -30,3 +38,10 @@
endif
include $(BUILD_STATIC_LIBRARY)
+
+# Used by OEMs for factory test images.
+include $(CLEAR_VARS)
+LOCAL_MODULE := libminui
+LOCAL_WHOLE_STATIC_LIBRARIES += libminui
+LOCAL_SHARED_LIBRARIES := libpng
+include $(BUILD_SHARED_LIBRARY)
diff --git a/minui/events.c b/minui/events.c
deleted file mode 100644
index df7dad4..0000000
--- a/minui/events.c
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <sys/epoll.h>
-
-#include <linux/input.h>
-
-#include "minui.h"
-
-#define MAX_DEVICES 16
-#define MAX_MISC_FDS 16
-
-#define BITS_PER_LONG (sizeof(unsigned long) * 8)
-#define BITS_TO_LONGS(x) (((x) + BITS_PER_LONG - 1) / BITS_PER_LONG)
-
-#define test_bit(bit, array) \
- ((array)[(bit)/BITS_PER_LONG] & (1 << ((bit) % BITS_PER_LONG)))
-
-struct fd_info {
- int fd;
- ev_callback cb;
- void *data;
-};
-
-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;
-static unsigned ev_dev_count = 0;
-static unsigned ev_misc_count = 0;
-
-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) {
- while((de = readdir(dir))) {
- unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
-
-// fprintf(stderr,"/dev/input/%s\n", de->d_name);
- if(strncmp(de->d_name,"event",5)) continue;
- fd = openat(dirfd(dir), de->d_name, O_RDONLY);
- if(fd < 0) continue;
-
- /* read the evbits of the input device */
- if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) < 0) {
- close(fd);
- continue;
- }
-
- /* TODO: add ability to specify event masks. For now, just assume
- * that only EV_KEY and EV_REL event types are ever needed. */
- if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits)) {
- close(fd);
- continue;
- }
-
- 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++;
- ev_dev_count++;
- if(ev_dev_count == MAX_DEVICES) break;
- }
- }
-
- 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.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_fdinfo[--ev_count].fd);
- }
- ev_misc_count = 0;
- ev_dev_count = 0;
- close(epollfd);
-}
-
-int ev_wait(int timeout)
-{
- npolledevents = epoll_wait(epollfd, polledevents, ev_count, timeout);
- if (npolledevents <= 0)
- return -1;
- return 0;
-}
-
-void ev_dispatch(void)
-{
- int n;
- int ret;
-
- 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, uint32_t epevents, struct input_event *ev)
-{
- int r;
-
- if (epevents & EPOLLIN) {
- r = read(fd, ev, sizeof(*ev));
- if (r == sizeof(*ev))
- return 0;
- }
- return -1;
-}
-
-int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data)
-{
- unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)];
- unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
- unsigned i;
- int ret;
-
- for (i = 0; i < ev_dev_count; i++) {
- int code;
-
- memset(key_bits, 0, sizeof(key_bits));
- memset(ev_bits, 0, sizeof(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_fdinfo[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits);
- if (ret < 0)
- continue;
-
- for (code = 0; code <= KEY_MAX; code++) {
- if (test_bit(code, key_bits))
- set_key_cb(code, 1, data);
- }
- }
-
- return 0;
-}
diff --git a/minui/events.cpp b/minui/events.cpp
new file mode 100644
index 0000000..3b2262a
--- /dev/null
+++ b/minui/events.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <unistd.h>
+
+#include <linux/input.h>
+
+#include "minui.h"
+
+#define MAX_DEVICES 16
+#define MAX_MISC_FDS 16
+
+#define BITS_PER_LONG (sizeof(unsigned long) * 8)
+#define BITS_TO_LONGS(x) (((x) + BITS_PER_LONG - 1) / BITS_PER_LONG)
+
+struct fd_info {
+ int fd;
+ ev_callback cb;
+ void* data;
+};
+
+static int g_epoll_fd;
+static epoll_event polledevents[MAX_DEVICES + MAX_MISC_FDS];
+static int npolledevents;
+
+static fd_info ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS];
+
+static unsigned ev_count = 0;
+static unsigned ev_dev_count = 0;
+static unsigned ev_misc_count = 0;
+
+static bool test_bit(size_t bit, unsigned long* array) {
+ return (array[bit/BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0;
+}
+
+int ev_init(ev_callback input_cb, void* data) {
+ bool epollctlfail = false;
+
+ g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS);
+ if (g_epoll_fd == -1) {
+ return -1;
+ }
+
+ DIR* dir = opendir("/dev/input");
+ if (dir != NULL) {
+ dirent* de;
+ while ((de = readdir(dir))) {
+ unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
+
+// fprintf(stderr,"/dev/input/%s\n", de->d_name);
+ if (strncmp(de->d_name, "event", 5)) continue;
+ int fd = openat(dirfd(dir), de->d_name, O_RDONLY);
+ if (fd == -1) continue;
+
+ // Read the evbits of the input device.
+ if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+ close(fd);
+ continue;
+ }
+
+ // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed.
+ if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
+ close(fd);
+ continue;
+ }
+
+ epoll_event ev;
+ ev.events = EPOLLIN | EPOLLWAKEUP;
+ ev.data.ptr = &ev_fdinfo[ev_count];
+ if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ close(fd);
+ epollctlfail = true;
+ continue;
+ }
+
+ ev_fdinfo[ev_count].fd = fd;
+ ev_fdinfo[ev_count].cb = input_cb;
+ ev_fdinfo[ev_count].data = data;
+ ev_count++;
+ ev_dev_count++;
+ if (ev_dev_count == MAX_DEVICES) break;
+ }
+
+ closedir(dir);
+ }
+
+ if (epollctlfail && !ev_count) {
+ close(g_epoll_fd);
+ g_epoll_fd = -1;
+ return -1;
+ }
+
+ return 0;
+}
+
+int ev_get_epollfd(void) {
+ return g_epoll_fd;
+}
+
+int ev_add_fd(int fd, ev_callback cb, void* data) {
+ if (ev_misc_count == MAX_MISC_FDS || cb == NULL) {
+ return -1;
+ }
+
+ epoll_event ev;
+ ev.events = EPOLLIN | EPOLLWAKEUP;
+ ev.data.ptr = (void *)&ev_fdinfo[ev_count];
+ int ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev);
+ if (!ret) {
+ ev_fdinfo[ev_count].fd = fd;
+ ev_fdinfo[ev_count].cb = cb;
+ ev_fdinfo[ev_count].data = data;
+ ev_count++;
+ ev_misc_count++;
+ }
+
+ return ret;
+}
+
+void ev_exit(void) {
+ while (ev_count > 0) {
+ close(ev_fdinfo[--ev_count].fd);
+ }
+ ev_misc_count = 0;
+ ev_dev_count = 0;
+ close(g_epoll_fd);
+}
+
+int ev_wait(int timeout) {
+ npolledevents = epoll_wait(g_epoll_fd, polledevents, ev_count, timeout);
+ if (npolledevents <= 0) {
+ return -1;
+ }
+ return 0;
+}
+
+void ev_dispatch(void) {
+ for (int n = 0; n < npolledevents; n++) {
+ fd_info* fdi = reinterpret_cast<fd_info*>(polledevents[n].data.ptr);
+ ev_callback cb = fdi->cb;
+ if (cb) {
+ cb(fdi->fd, polledevents[n].events, fdi->data);
+ }
+ }
+}
+
+int ev_get_input(int fd, uint32_t epevents, input_event* ev) {
+ if (epevents & EPOLLIN) {
+ ssize_t r = TEMP_FAILURE_RETRY(read(fd, ev, sizeof(*ev)));
+ if (r == sizeof(*ev)) {
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data) {
+ unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
+ unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)];
+
+ for (size_t i = 0; i < ev_dev_count; ++i) {
+ memset(ev_bits, 0, sizeof(ev_bits));
+ memset(key_bits, 0, sizeof(key_bits));
+
+ if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+ continue;
+ }
+ if (!test_bit(EV_KEY, ev_bits)) {
+ continue;
+ }
+ if (ioctl(ev_fdinfo[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits) == -1) {
+ continue;
+ }
+
+ for (int code = 0; code <= KEY_MAX; code++) {
+ if (test_bit(code, key_bits)) {
+ set_key_cb(code, 1, data);
+ }
+ }
+ }
+
+ return 0;
+}
+
+void ev_iterate_available_keys(std::function<void(int)> f) {
+ unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
+ unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)];
+
+ for (size_t i = 0; i < ev_dev_count; ++i) {
+ memset(ev_bits, 0, sizeof(ev_bits));
+ memset(key_bits, 0, sizeof(key_bits));
+
+ // Does this device even have keys?
+ if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+ continue;
+ }
+ if (!test_bit(EV_KEY, ev_bits)) {
+ continue;
+ }
+
+ int rc = ioctl(ev_fdinfo[i].fd, EVIOCGBIT(EV_KEY, KEY_MAX), key_bits);
+ if (rc == -1) {
+ continue;
+ }
+
+ for (int key_code = 0; key_code <= KEY_MAX; ++key_code) {
+ if (test_bit(key_code, key_bits)) {
+ f(key_code);
+ }
+ }
+ }
+}
diff --git a/minui/graphics.c b/minui/graphics.cpp
similarity index 84%
rename from minui/graphics.c
rename to minui/graphics.cpp
index 6049d85..c0eea9e 100644
--- a/minui/graphics.c
+++ b/minui/graphics.cpp
@@ -16,6 +16,7 @@
#include <stdbool.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#include <fcntl.h>
@@ -34,11 +35,11 @@
#include "minui.h"
#include "graphics.h"
-typedef struct {
+struct GRFont {
GRSurface* texture;
int cwidth;
int cheight;
-} GRFont;
+};
static GRFont* gr_font = NULL;
static minui_backend* gr_backend = NULL;
@@ -47,8 +48,6 @@
static int overscan_offset_x = 0;
static int overscan_offset_y = 0;
-static int gr_vt_fd = -1;
-
static unsigned char gr_current_r = 255;
static unsigned char gr_current_g = 255;
static unsigned char gr_current_b = 255;
@@ -76,11 +75,10 @@
unsigned char* dst_p, int dst_row_bytes,
int width, int height)
{
- int i, j;
- for (j = 0; j < height; ++j) {
+ for (int j = 0; j < height; ++j) {
unsigned char* sx = src_p;
unsigned char* px = dst_p;
- for (i = 0; i < width; ++i) {
+ for (int i = 0; i < width; ++i) {
unsigned char a = *sx++;
if (gr_current_a < 255) a = ((int)a * gr_current_a) / 255;
if (a == 255) {
@@ -105,34 +103,33 @@
}
}
-
-void gr_text(int x, int y, const char *s, int bold)
+void gr_text(int x, int y, const char *s, bool bold)
{
- GRFont *font = gr_font;
- unsigned off;
+ GRFont* font = gr_font;
- if (!font->texture) return;
- if (gr_current_a == 0) return;
+ if (!font->texture || gr_current_a == 0) return;
bold = bold && (font->texture->height != font->cheight);
x += overscan_offset_x;
y += overscan_offset_y;
- while((off = *s++)) {
- off -= 32;
+ unsigned char ch;
+ while ((ch = *s++)) {
if (outside(x, y) || outside(x+font->cwidth-1, y+font->cheight-1)) break;
- if (off < 96) {
- unsigned char* src_p = font->texture->data + (off * font->cwidth) +
- (bold ? font->cheight * font->texture->row_bytes : 0);
- unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes;
-
- text_blend(src_p, font->texture->row_bytes,
- dst_p, gr_draw->row_bytes,
- font->cwidth, font->cheight);
-
+ if (ch < ' ' || ch > '~') {
+ ch = '?';
}
+
+ unsigned char* src_p = font->texture->data + ((ch - ' ') * font->cwidth) +
+ (bold ? font->cheight * font->texture->row_bytes : 0);
+ unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes;
+
+ text_blend(src_p, font->texture->row_bytes,
+ dst_p, gr_draw->row_bytes,
+ font->cwidth, font->cheight);
+
x += font->cwidth;
}
}
@@ -160,22 +157,27 @@
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
+#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
+ gr_current_r = b;
+ gr_current_g = g;
+ gr_current_b = r;
+ gr_current_a = a;
+#else
gr_current_r = r;
gr_current_g = g;
gr_current_b = b;
gr_current_a = a;
+#endif
}
void gr_clear()
{
- if (gr_current_r == gr_current_g &&
- gr_current_r == gr_current_b) {
+ if (gr_current_r == gr_current_g && gr_current_r == gr_current_b) {
memset(gr_draw->data, gr_current_r, gr_draw->height * gr_draw->row_bytes);
} else {
- int x, y;
unsigned char* px = gr_draw->data;
- for (y = 0; y < gr_draw->height; ++y) {
- for (x = 0; x < gr_draw->width; ++x) {
+ for (int y = 0; y < gr_draw->height; ++y) {
+ for (int x = 0; x < gr_draw->width; ++x) {
*px++ = gr_current_r;
*px++ = gr_current_g;
*px++ = gr_current_b;
@@ -267,7 +269,7 @@
static void gr_init_font(void)
{
- gr_font = calloc(sizeof(*gr_font), 1);
+ gr_font = reinterpret_cast<GRFont*>(calloc(sizeof(*gr_font), 1));
int res = res_create_alpha_surface("font", &(gr_font->texture));
if (res == 0) {
@@ -280,14 +282,14 @@
printf("failed to read font: res=%d\n", res);
// fall back to the compiled-in font.
- gr_font->texture = malloc(sizeof(*gr_font->texture));
+ gr_font->texture = reinterpret_cast<GRSurface*>(malloc(sizeof(*gr_font->texture)));
gr_font->texture->width = font.width;
gr_font->texture->height = font.height;
gr_font->texture->row_bytes = font.width;
gr_font->texture->pixel_bytes = 1;
- unsigned char* bits = malloc(font.width * font.height);
- gr_font->texture->data = (void*) bits;
+ unsigned char* bits = reinterpret_cast<unsigned char*>(malloc(font.width * font.height));
+ gr_font->texture->data = reinterpret_cast<unsigned char*>(bits);
unsigned char data;
unsigned char* in = font.rundata;
@@ -324,7 +326,7 @@
gr_clear();
gr_color(255, 0, 0, 255);
- gr_surface frame = images[x%frames];
+ GRSurface* frame = images[x%frames];
gr_blit(frame, 0, 0, frame->width, frame->height, x, 0);
gr_color(255, 0, 0, 128);
@@ -358,17 +360,6 @@
{
gr_init_font();
- gr_vt_fd = open("/dev/tty0", O_RDWR | O_SYNC);
- if (gr_vt_fd < 0) {
- // This is non-fatal; post-Cupcake kernels don't have tty0.
- perror("can't open /dev/tty0");
- } else if (ioctl(gr_vt_fd, KDSETMODE, (void*) KD_GRAPHICS)) {
- // However, if we do open tty0, we expect the ioctl to work.
- perror("failed KDSETMODE to KD_GRAPHICS on tty0");
- gr_exit();
- return -1;
- }
-
gr_backend = open_adf();
if (gr_backend) {
gr_draw = gr_backend->init(gr_backend);
@@ -378,6 +369,11 @@
}
if (!gr_draw) {
+ gr_backend = open_drm();
+ gr_draw = gr_backend->init(gr_backend);
+ }
+
+ if (!gr_draw) {
gr_backend = open_fbdev();
gr_draw = gr_backend->init(gr_backend);
if (gr_draw == NULL) {
@@ -397,10 +393,6 @@
void gr_exit(void)
{
gr_backend->exit(gr_backend);
-
- ioctl(gr_vt_fd, KDSETMODE, (void*) KD_TEXT);
- close(gr_vt_fd);
- gr_vt_fd = -1;
}
int gr_fb_width(void)
diff --git a/minui/graphics.h b/minui/graphics.h
index 993e986..52968eb 100644
--- a/minui/graphics.h
+++ b/minui/graphics.h
@@ -17,34 +17,27 @@
#ifndef _GRAPHICS_H_
#define _GRAPHICS_H_
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdbool.h>
#include "minui.h"
-typedef struct minui_backend {
- // Initializes the backend and returns a gr_surface to draw into.
- gr_surface (*init)(struct minui_backend*);
+// TODO: lose the function pointers.
+struct minui_backend {
+ // Initializes the backend and returns a GRSurface* to draw into.
+ GRSurface* (*init)(minui_backend*);
// Causes the current drawing surface (returned by the most recent
// call to flip() or init()) to be displayed, and returns a new
// drawing surface.
- gr_surface (*flip)(struct minui_backend*);
+ GRSurface* (*flip)(minui_backend*);
// Blank (or unblank) the screen.
- void (*blank)(struct minui_backend*, bool);
+ void (*blank)(minui_backend*, bool);
// Device cleanup when drawing is done.
- void (*exit)(struct minui_backend*);
-} minui_backend;
+ void (*exit)(minui_backend*);
+};
minui_backend* open_fbdev();
minui_backend* open_adf();
-
-#ifdef __cplusplus
-}
-#endif
+minui_backend* open_drm();
#endif
diff --git a/minui/graphics_adf.c b/minui/graphics_adf.cpp
similarity index 78%
rename from minui/graphics_adf.c
rename to minui/graphics_adf.cpp
index ac6d64e..5d0867f 100644
--- a/minui/graphics_adf.c
+++ b/minui/graphics_adf.cpp
@@ -19,6 +19,7 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#include <sys/cdefs.h>
@@ -43,15 +44,13 @@
unsigned int current_surface;
unsigned int n_surfaces;
- struct adf_surface_pdata surfaces[2];
+ adf_surface_pdata surfaces[2];
};
-static gr_surface adf_flip(struct minui_backend *backend);
-static void adf_blank(struct minui_backend *backend, bool blank);
+static GRSurface* adf_flip(minui_backend *backend);
+static void adf_blank(minui_backend *backend, bool blank);
-static int adf_surface_init(struct adf_pdata *pdata,
- struct drm_mode_modeinfo *mode, struct adf_surface_pdata *surf)
-{
+static int adf_surface_init(adf_pdata *pdata, drm_mode_modeinfo *mode, adf_surface_pdata *surf) {
memset(surf, 0, sizeof(*surf));
surf->fd = adf_interface_simple_buffer_alloc(pdata->intf_fd, mode->hdisplay,
@@ -64,8 +63,9 @@
surf->base.row_bytes = surf->pitch;
surf->base.pixel_bytes = (pdata->format == DRM_FORMAT_RGB565) ? 2 : 4;
- surf->base.data = mmap(NULL, surf->pitch * surf->base.height, PROT_WRITE,
- MAP_SHARED, surf->fd, surf->offset);
+ surf->base.data = reinterpret_cast<uint8_t*>(mmap(NULL,
+ surf->pitch * surf->base.height, PROT_WRITE,
+ MAP_SHARED, surf->fd, surf->offset));
if (surf->base.data == MAP_FAILED) {
close(surf->fd);
return -errno;
@@ -74,9 +74,9 @@
return 0;
}
-static int adf_interface_init(struct adf_pdata *pdata)
+static int adf_interface_init(adf_pdata *pdata)
{
- struct adf_interface_data intf_data;
+ adf_interface_data intf_data;
int ret = 0;
int err;
@@ -106,7 +106,7 @@
return ret;
}
-static int adf_device_init(struct adf_pdata *pdata, struct adf_device *dev)
+static int adf_device_init(adf_pdata *pdata, adf_device *dev)
{
adf_id_t intf_id;
int intf_fd;
@@ -134,14 +134,16 @@
return err;
}
-static gr_surface adf_init(minui_backend *backend)
+static GRSurface* adf_init(minui_backend *backend)
{
- struct adf_pdata *pdata = (struct adf_pdata *)backend;
+ adf_pdata *pdata = (adf_pdata *)backend;
adf_id_t *dev_ids = NULL;
ssize_t n_dev_ids, i;
- gr_surface ret;
+ GRSurface* ret;
-#if defined(RECOVERY_BGRA)
+#if defined(RECOVERY_ABGR)
+ pdata->format = DRM_FORMAT_ABGR8888;
+#elif defined(RECOVERY_BGRA)
pdata->format = DRM_FORMAT_BGRA8888;
#elif defined(RECOVERY_RGBX)
pdata->format = DRM_FORMAT_RGBX8888;
@@ -161,7 +163,7 @@
pdata->intf_fd = -1;
for (i = 0; i < n_dev_ids && pdata->intf_fd < 0; i++) {
- struct adf_device dev;
+ adf_device dev;
int err = adf_device_open(dev_ids[i], O_RDWR, &dev);
if (err < 0) {
@@ -191,10 +193,10 @@
return ret;
}
-static gr_surface adf_flip(struct minui_backend *backend)
+static GRSurface* adf_flip(minui_backend *backend)
{
- struct adf_pdata *pdata = (struct adf_pdata *)backend;
- struct adf_surface_pdata *surf = &pdata->surfaces[pdata->current_surface];
+ adf_pdata *pdata = (adf_pdata *)backend;
+ adf_surface_pdata *surf = &pdata->surfaces[pdata->current_surface];
int fence_fd = adf_interface_simple_post(pdata->intf_fd, pdata->eng_id,
surf->base.width, surf->base.height, pdata->format, surf->fd,
@@ -206,22 +208,22 @@
return &pdata->surfaces[pdata->current_surface].base;
}
-static void adf_blank(struct minui_backend *backend, bool blank)
+static void adf_blank(minui_backend *backend, bool blank)
{
- struct adf_pdata *pdata = (struct adf_pdata *)backend;
+ adf_pdata *pdata = (adf_pdata *)backend;
adf_interface_blank(pdata->intf_fd,
blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON);
}
-static void adf_surface_destroy(struct adf_surface_pdata *surf)
+static void adf_surface_destroy(adf_surface_pdata *surf)
{
munmap(surf->base.data, surf->pitch * surf->base.height);
close(surf->fd);
}
-static void adf_exit(struct minui_backend *backend)
+static void adf_exit(minui_backend *backend)
{
- struct adf_pdata *pdata = (struct adf_pdata *)backend;
+ adf_pdata *pdata = (adf_pdata *)backend;
unsigned int i;
for (i = 0; i < pdata->n_surfaces; i++)
@@ -233,7 +235,7 @@
minui_backend *open_adf()
{
- struct adf_pdata *pdata = calloc(1, sizeof(*pdata));
+ adf_pdata* pdata = reinterpret_cast<adf_pdata*>(calloc(1, sizeof(*pdata)));
if (!pdata) {
perror("allocating adf backend failed");
return NULL;
diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp
new file mode 100644
index 0000000..03e33b7
--- /dev/null
+++ b/minui/graphics_drm.cpp
@@ -0,0 +1,476 @@
+/*
+ * 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 <drm_fourcc.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "minui.h"
+#include "graphics.h"
+
+#define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A)))
+
+struct drm_surface {
+ GRSurface base;
+ uint32_t fb_id;
+ uint32_t handle;
+};
+
+static drm_surface *drm_surfaces[2];
+static int current_buffer;
+
+static drmModeCrtc *main_monitor_crtc;
+static drmModeConnector *main_monitor_connector;
+
+static int drm_fd = -1;
+
+static void drm_disable_crtc(int drm_fd, drmModeCrtc *crtc) {
+ if (crtc) {
+ drmModeSetCrtc(drm_fd, crtc->crtc_id,
+ 0, // fb_id
+ 0, 0, // x,y
+ NULL, // connectors
+ 0, // connector_count
+ NULL); // mode
+ }
+}
+
+static void drm_enable_crtc(int drm_fd, drmModeCrtc *crtc,
+ struct drm_surface *surface) {
+ int32_t ret;
+
+ ret = drmModeSetCrtc(drm_fd, crtc->crtc_id,
+ surface->fb_id,
+ 0, 0, // x,y
+ &main_monitor_connector->connector_id,
+ 1, // connector_count
+ &main_monitor_crtc->mode);
+
+ if (ret)
+ printf("drmModeSetCrtc failed ret=%d\n", ret);
+}
+
+static void drm_blank(minui_backend* backend __unused, bool blank) {
+ if (blank)
+ drm_disable_crtc(drm_fd, main_monitor_crtc);
+ else
+ drm_enable_crtc(drm_fd, main_monitor_crtc,
+ drm_surfaces[current_buffer]);
+}
+
+static void drm_destroy_surface(struct drm_surface *surface) {
+ struct drm_gem_close gem_close;
+ int ret;
+
+ if(!surface)
+ return;
+
+ if (surface->base.data)
+ munmap(surface->base.data,
+ surface->base.row_bytes * surface->base.height);
+
+ if (surface->fb_id) {
+ ret = drmModeRmFB(drm_fd, surface->fb_id);
+ if (ret)
+ printf("drmModeRmFB failed ret=%d\n", ret);
+ }
+
+ if (surface->handle) {
+ memset(&gem_close, 0, sizeof(gem_close));
+ gem_close.handle = surface->handle;
+
+ ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
+ if (ret)
+ printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret);
+ }
+
+ free(surface);
+}
+
+static int drm_format_to_bpp(uint32_t format) {
+ switch(format) {
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_BGRA8888:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRX8888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_XRGB8888:
+ return 32;
+ case DRM_FORMAT_RGB565:
+ return 16;
+ default:
+ printf("Unknown format %d\n", format);
+ return 32;
+ }
+}
+
+static drm_surface *drm_create_surface(int width, int height) {
+ struct drm_surface *surface;
+ struct drm_mode_create_dumb create_dumb;
+ uint32_t format;
+ int ret;
+
+ surface = (struct drm_surface*)calloc(1, sizeof(*surface));
+ if (!surface) {
+ printf("Can't allocate memory\n");
+ return NULL;
+ }
+
+#if defined(RECOVERY_ABGR)
+ format = DRM_FORMAT_RGBA8888;
+#elif defined(RECOVERY_BGRA)
+ format = DRM_FORMAT_ARGB8888;
+#elif defined(RECOVERY_RGBX)
+ format = DRM_FORMAT_XBGR8888;
+#else
+ format = DRM_FORMAT_RGB565;
+#endif
+
+ memset(&create_dumb, 0, sizeof(create_dumb));
+ create_dumb.height = height;
+ create_dumb.width = width;
+ create_dumb.bpp = drm_format_to_bpp(format);
+ create_dumb.flags = 0;
+
+ ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
+ if (ret) {
+ printf("DRM_IOCTL_MODE_CREATE_DUMB failed ret=%d\n",ret);
+ drm_destroy_surface(surface);
+ return NULL;
+ }
+ surface->handle = create_dumb.handle;
+
+ uint32_t handles[4], pitches[4], offsets[4];
+
+ handles[0] = surface->handle;
+ pitches[0] = create_dumb.pitch;
+ offsets[0] = 0;
+
+ ret = drmModeAddFB2(drm_fd, width, height,
+ format, handles, pitches, offsets,
+ &(surface->fb_id), 0);
+ if (ret) {
+ printf("drmModeAddFB2 failed ret=%d\n", ret);
+ drm_destroy_surface(surface);
+ return NULL;
+ }
+
+ struct drm_mode_map_dumb map_dumb;
+ memset(&map_dumb, 0, sizeof(map_dumb));
+ map_dumb.handle = create_dumb.handle;
+ ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
+ if (ret) {
+ printf("DRM_IOCTL_MODE_MAP_DUMB failed ret=%d\n",ret);
+ drm_destroy_surface(surface);
+ return NULL;;
+ }
+
+ surface->base.height = height;
+ surface->base.width = width;
+ surface->base.row_bytes = create_dumb.pitch;
+ surface->base.pixel_bytes = create_dumb.bpp / 8;
+ surface->base.data = (unsigned char*)
+ mmap(NULL,
+ surface->base.height * surface->base.row_bytes,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ drm_fd, map_dumb.offset);
+ if (surface->base.data == MAP_FAILED) {
+ perror("mmap() failed");
+ drm_destroy_surface(surface);
+ return NULL;
+ }
+
+ return surface;
+}
+
+static drmModeCrtc *find_crtc_for_connector(int fd,
+ drmModeRes *resources,
+ drmModeConnector *connector) {
+ int i, j;
+ drmModeEncoder *encoder;
+ int32_t crtc;
+
+ /*
+ * Find the encoder. If we already have one, just use it.
+ */
+ if (connector->encoder_id)
+ encoder = drmModeGetEncoder(fd, connector->encoder_id);
+ else
+ encoder = NULL;
+
+ if (encoder && encoder->crtc_id) {
+ crtc = encoder->crtc_id;
+ drmModeFreeEncoder(encoder);
+ return drmModeGetCrtc(fd, crtc);
+ }
+
+ /*
+ * Didn't find anything, try to find a crtc and encoder combo.
+ */
+ crtc = -1;
+ for (i = 0; i < connector->count_encoders; i++) {
+ encoder = drmModeGetEncoder(fd, connector->encoders[i]);
+
+ if (encoder) {
+ for (j = 0; j < resources->count_crtcs; j++) {
+ if (!(encoder->possible_crtcs & (1 << j)))
+ continue;
+ crtc = resources->crtcs[j];
+ break;
+ }
+ if (crtc >= 0) {
+ drmModeFreeEncoder(encoder);
+ return drmModeGetCrtc(fd, crtc);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static drmModeConnector *find_used_connector_by_type(int fd,
+ drmModeRes *resources,
+ unsigned type) {
+ int i;
+ for (i = 0; i < resources->count_connectors; i++) {
+ drmModeConnector *connector;
+
+ connector = drmModeGetConnector(fd, resources->connectors[i]);
+ if (connector) {
+ if ((connector->connector_type == type) &&
+ (connector->connection == DRM_MODE_CONNECTED) &&
+ (connector->count_modes > 0))
+ return connector;
+
+ drmModeFreeConnector(connector);
+ }
+ }
+ return NULL;
+}
+
+static drmModeConnector *find_first_connected_connector(int fd,
+ drmModeRes *resources) {
+ int i;
+ for (i = 0; i < resources->count_connectors; i++) {
+ drmModeConnector *connector;
+
+ connector = drmModeGetConnector(fd, resources->connectors[i]);
+ if (connector) {
+ if ((connector->count_modes > 0) &&
+ (connector->connection == DRM_MODE_CONNECTED))
+ return connector;
+
+ drmModeFreeConnector(connector);
+ }
+ }
+ return NULL;
+}
+
+static drmModeConnector *find_main_monitor(int fd, drmModeRes *resources,
+ uint32_t *mode_index) {
+ unsigned i = 0;
+ int modes;
+ /* Look for LVDS/eDP/DSI connectors. Those are the main screens. */
+ unsigned kConnectorPriority[] = {
+ DRM_MODE_CONNECTOR_LVDS,
+ DRM_MODE_CONNECTOR_eDP,
+ DRM_MODE_CONNECTOR_DSI,
+ };
+
+ drmModeConnector *main_monitor_connector = NULL;
+ do {
+ main_monitor_connector = find_used_connector_by_type(fd,
+ resources,
+ kConnectorPriority[i]);
+ i++;
+ } while (!main_monitor_connector && i < ARRAY_SIZE(kConnectorPriority));
+
+ /* If we didn't find a connector, grab the first one that is connected. */
+ if (!main_monitor_connector)
+ main_monitor_connector =
+ find_first_connected_connector(fd, resources);
+
+ /* If we still didn't find a connector, give up and return. */
+ if (!main_monitor_connector)
+ return NULL;
+
+ *mode_index = 0;
+ for (modes = 0; modes < main_monitor_connector->count_modes; modes++) {
+ if (main_monitor_connector->modes[modes].type &
+ DRM_MODE_TYPE_PREFERRED) {
+ *mode_index = modes;
+ break;
+ }
+ }
+
+ return main_monitor_connector;
+}
+
+static void disable_non_main_crtcs(int fd,
+ drmModeRes *resources,
+ drmModeCrtc* main_crtc) {
+ int i;
+ drmModeCrtc* crtc;
+
+ for (i = 0; i < resources->count_connectors; i++) {
+ drmModeConnector *connector;
+
+ connector = drmModeGetConnector(fd, resources->connectors[i]);
+ crtc = find_crtc_for_connector(fd, resources, connector);
+ if (crtc->crtc_id != main_crtc->crtc_id)
+ drm_disable_crtc(fd, crtc);
+ drmModeFreeCrtc(crtc);
+ }
+}
+
+static GRSurface* drm_init(minui_backend* backend __unused) {
+ drmModeRes *res = NULL;
+ uint32_t selected_mode;
+ char *dev_name;
+ int width, height;
+ int ret, i;
+
+ /* Consider DRM devices in order. */
+ for (i = 0; i < DRM_MAX_MINOR; i++) {
+ uint64_t cap = 0;
+
+ ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i);
+ if (ret < 0)
+ continue;
+
+ drm_fd = open(dev_name, O_RDWR, 0);
+ free(dev_name);
+ if (drm_fd < 0)
+ continue;
+
+ /* We need dumb buffers. */
+ ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap);
+ if (ret || cap == 0) {
+ close(drm_fd);
+ continue;
+ }
+
+ res = drmModeGetResources(drm_fd);
+ if (!res) {
+ close(drm_fd);
+ continue;
+ }
+
+ /* Use this device if it has at least one connected monitor. */
+ if (res->count_crtcs > 0 && res->count_connectors > 0)
+ if (find_first_connected_connector(drm_fd, res))
+ break;
+
+ drmModeFreeResources(res);
+ close(drm_fd);
+ res = NULL;
+ }
+
+ if (drm_fd < 0 || res == NULL) {
+ perror("cannot find/open a drm device");
+ return NULL;
+ }
+
+ main_monitor_connector = find_main_monitor(drm_fd,
+ res, &selected_mode);
+
+ if (!main_monitor_connector) {
+ printf("main_monitor_connector not found\n");
+ drmModeFreeResources(res);
+ close(drm_fd);
+ return NULL;
+ }
+
+ main_monitor_crtc = find_crtc_for_connector(drm_fd, res,
+ main_monitor_connector);
+
+ if (!main_monitor_crtc) {
+ printf("main_monitor_crtc not found\n");
+ drmModeFreeResources(res);
+ close(drm_fd);
+ return NULL;
+ }
+
+ disable_non_main_crtcs(drm_fd,
+ res, main_monitor_crtc);
+
+ main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode];
+
+ width = main_monitor_crtc->mode.hdisplay;
+ height = main_monitor_crtc->mode.vdisplay;
+
+ drmModeFreeResources(res);
+
+ drm_surfaces[0] = drm_create_surface(width, height);
+ drm_surfaces[1] = drm_create_surface(width, height);
+ if (!drm_surfaces[0] || !drm_surfaces[1]) {
+ drm_destroy_surface(drm_surfaces[0]);
+ drm_destroy_surface(drm_surfaces[1]);
+ drmModeFreeResources(res);
+ close(drm_fd);
+ return NULL;
+ }
+
+ current_buffer = 0;
+
+ drm_enable_crtc(drm_fd, main_monitor_crtc, drm_surfaces[1]);
+
+ return &(drm_surfaces[0]->base);
+}
+
+static GRSurface* drm_flip(minui_backend* backend __unused) {
+ int ret;
+
+ ret = drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id,
+ drm_surfaces[current_buffer]->fb_id, 0, NULL);
+ if (ret < 0) {
+ printf("drmModePageFlip failed ret=%d\n", ret);
+ return NULL;
+ }
+ current_buffer = 1 - current_buffer;
+ return &(drm_surfaces[current_buffer]->base);
+}
+
+static void drm_exit(minui_backend* backend __unused) {
+ drm_disable_crtc(drm_fd, main_monitor_crtc);
+ drm_destroy_surface(drm_surfaces[0]);
+ drm_destroy_surface(drm_surfaces[1]);
+ drmModeFreeCrtc(main_monitor_crtc);
+ drmModeFreeConnector(main_monitor_connector);
+ close(drm_fd);
+ drm_fd = -1;
+}
+
+static minui_backend drm_backend = {
+ .init = drm_init,
+ .flip = drm_flip,
+ .blank = drm_blank,
+ .exit = drm_exit,
+};
+
+minui_backend* open_drm() {
+ return &drm_backend;
+}
diff --git a/minui/graphics_fbdev.c b/minui/graphics_fbdev.cpp
similarity index 85%
rename from minui/graphics_fbdev.c
rename to minui/graphics_fbdev.cpp
index 6df2726..997e9ca 100644
--- a/minui/graphics_fbdev.c
+++ b/minui/graphics_fbdev.cpp
@@ -16,6 +16,7 @@
#include <stdbool.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#include <fcntl.h>
@@ -32,8 +33,8 @@
#include "minui.h"
#include "graphics.h"
-static gr_surface fbdev_init(minui_backend*);
-static gr_surface fbdev_flip(minui_backend*);
+static GRSurface* fbdev_init(minui_backend*);
+static GRSurface* fbdev_flip(minui_backend*);
static void fbdev_blank(minui_backend*, bool);
static void fbdev_exit(minui_backend*);
@@ -42,7 +43,7 @@
static GRSurface* gr_draw = NULL;
static int displayed_buffer;
-static struct fb_var_screeninfo vi;
+static fb_var_screeninfo vi;
static int fb_fd = -1;
static minui_backend my_backend = {
@@ -78,18 +79,14 @@
displayed_buffer = n;
}
-static gr_surface fbdev_init(minui_backend* backend) {
- int fd;
- void *bits;
-
- struct fb_fix_screeninfo fi;
-
- fd = open("/dev/graphics/fb0", O_RDWR);
- if (fd < 0) {
+static GRSurface* fbdev_init(minui_backend* backend) {
+ int fd = open("/dev/graphics/fb0", O_RDWR);
+ if (fd == -1) {
perror("cannot open fb0");
return NULL;
}
+ fb_fix_screeninfo fi;
if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
perror("failed to get fb0 info");
close(fd);
@@ -123,7 +120,7 @@
vi.green.offset, vi.green.length,
vi.blue.offset, vi.blue.length);
- bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (bits == MAP_FAILED) {
perror("failed to mmap framebuffer");
close(fd);
@@ -136,7 +133,7 @@
gr_framebuffer[0].height = vi.yres;
gr_framebuffer[0].row_bytes = fi.line_length;
gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8;
- gr_framebuffer[0].data = bits;
+ gr_framebuffer[0].data = reinterpret_cast<uint8_t*>(bits);
memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes);
/* check if we can use double buffering */
@@ -177,7 +174,7 @@
return gr_draw;
}
-static gr_surface fbdev_flip(minui_backend* backend __unused) {
+static GRSurface* fbdev_flip(minui_backend* backend __unused) {
if (double_buffered) {
#if defined(RECOVERY_BGRA)
// In case of BGRA, do some byte swapping
@@ -198,21 +195,8 @@
set_displayed_framebuffer(1-displayed_buffer);
} else {
// Copy from the in-memory surface to the framebuffer.
-
-#if defined(RECOVERY_BGRA)
- unsigned int idx;
- unsigned char* ucfb_vaddr = (unsigned char*)gr_framebuffer[0].data;
- unsigned char* ucbuffer_vaddr = (unsigned char*)gr_draw->data;
- for (idx = 0 ; idx < (gr_draw->height * gr_draw->row_bytes); idx += 4) {
- ucfb_vaddr[idx ] = ucbuffer_vaddr[idx + 2];
- ucfb_vaddr[idx + 1] = ucbuffer_vaddr[idx + 1];
- ucfb_vaddr[idx + 2] = ucbuffer_vaddr[idx ];
- ucfb_vaddr[idx + 3] = ucbuffer_vaddr[idx + 3];
- }
-#else
memcpy(gr_framebuffer[0].data, gr_draw->data,
gr_draw->height * gr_draw->row_bytes);
-#endif
}
return gr_draw;
}
diff --git a/minui/minui.h b/minui/minui.h
index 733b675..bdde083 100644
--- a/minui/minui.h
+++ b/minui/minui.h
@@ -19,67 +19,70 @@
#include <sys/types.h>
-#include <stdbool.h>
+#include <functional>
-#ifdef __cplusplus
-extern "C" {
-#endif
+//
+// Graphics.
+//
-typedef struct {
+struct GRSurface {
int width;
int height;
int row_bytes;
int pixel_bytes;
unsigned char* data;
-} GRSurface;
+};
-typedef GRSurface* gr_surface;
+int gr_init();
+void gr_exit();
-int gr_init(void);
-void gr_exit(void);
+int gr_fb_width();
+int gr_fb_height();
-int gr_fb_width(void);
-int gr_fb_height(void);
-
-void gr_flip(void);
+void gr_flip();
void gr_fb_blank(bool blank);
void gr_clear(); // clear entire surface to current color
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
void gr_fill(int x1, int y1, int x2, int y2);
-void gr_text(int x, int y, const char *s, int bold);
-void gr_texticon(int x, int y, gr_surface icon);
+void gr_text(int x, int y, const char *s, bool bold);
+void gr_texticon(int x, int y, GRSurface* icon);
int gr_measure(const char *s);
void gr_font_size(int *x, int *y);
-void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy);
-unsigned int gr_get_width(gr_surface surface);
-unsigned int gr_get_height(gr_surface surface);
+void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy);
+unsigned int gr_get_width(GRSurface* surface);
+unsigned int gr_get_height(GRSurface* surface);
-// input event structure, include <linux/input.h> for the definition.
-// see http://www.mjmwired.net/kernel/Documentation/input/ for info.
+//
+// Input events.
+//
+
struct input_event;
-typedef int (*ev_callback)(int fd, uint32_t epevents, void *data);
-typedef int (*ev_set_key_callback)(int code, int value, void *data);
+// TODO: move these over to std::function.
+typedef int (*ev_callback)(int fd, uint32_t epevents, void* data);
+typedef int (*ev_set_key_callback)(int code, int value, void* data);
-int ev_init(ev_callback input_cb, void *data);
-void ev_exit(void);
-int ev_add_fd(int fd, ev_callback cb, void *data);
-int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data);
+int ev_init(ev_callback input_cb, void* data);
+void ev_exit();
+int ev_add_fd(int fd, ev_callback cb, void* data);
+void ev_iterate_available_keys(std::function<void(int)> f);
+int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data);
-/* timeout has the same semantics as for poll
- * 0 : don't block
- * < 0 : block forever
- * > 0 : block for 'timeout' milliseconds
- */
+// 'timeout' has the same semantics as poll(2).
+// 0 : don't block
+// < 0 : block forever
+// > 0 : block for 'timeout' milliseconds
int ev_wait(int timeout);
-int ev_get_input(int fd, uint32_t epevents, struct input_event *ev);
-void ev_dispatch(void);
-int ev_get_epollfd(void);
+int ev_get_input(int fd, uint32_t epevents, input_event* ev);
+void ev_dispatch();
+int ev_get_epollfd();
+//
// Resources
+//
// res_create_*_surface() functions return 0 if no error, else
// negative.
@@ -92,17 +95,17 @@
// All these functions load PNG images from "/res/images/${name}.png".
// Load a single display surface from a PNG image.
-int res_create_display_surface(const char* name, gr_surface* pSurface);
+int res_create_display_surface(const char* name, GRSurface** pSurface);
// Load an array of display surfaces from a single PNG image. The PNG
// should have a 'Frames' text chunk whose value is the number of
// frames this image represents. The pixel data itself is interlaced
// by row.
int res_create_multi_display_surface(const char* name,
- int* frames, gr_surface** pSurface);
+ int* frames, GRSurface*** pSurface);
// Load a single alpha surface from a grayscale PNG image.
-int res_create_alpha_surface(const char* name, gr_surface* pSurface);
+int res_create_alpha_surface(const char* name, GRSurface** pSurface);
// Load part of a grayscale PNG image that is the first match for the
// given locale. The image is expected to be a composite of multiple
@@ -111,14 +114,10 @@
// development/tools/recovery_l10n for an app that will generate these
// specialized images from Android resources.
int res_create_localized_alpha_surface(const char* name, const char* locale,
- gr_surface* pSurface);
+ GRSurface** pSurface);
// Free a surface allocated by any of the res_create_*_surface()
// functions.
-void res_free_surface(gr_surface surface);
-
-#ifdef __cplusplus
-}
-#endif
+void res_free_surface(GRSurface* surface);
#endif
diff --git a/minui/resources.c b/minui/resources.cpp
similarity index 86%
rename from minui/resources.c
rename to minui/resources.cpp
index 2bae4de..5e47892 100644
--- a/minui/resources.c
+++ b/minui/resources.cpp
@@ -15,6 +15,7 @@
*/
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#include <fcntl.h>
@@ -35,10 +36,11 @@
#define SURFACE_DATA_ALIGNMENT 8
-static gr_surface malloc_surface(size_t data_size) {
- unsigned char* temp = malloc(sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT);
+static GRSurface* malloc_surface(size_t data_size) {
+ size_t size = sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT;
+ unsigned char* temp = reinterpret_cast<unsigned char*>(malloc(size));
if (temp == NULL) return NULL;
- gr_surface surface = (gr_surface) temp;
+ GRSurface* surface = reinterpret_cast<GRSurface*>(temp);
surface->data = temp + sizeof(GRSurface) +
(SURFACE_DATA_ALIGNMENT - (sizeof(GRSurface) % SURFACE_DATA_ALIGNMENT));
return surface;
@@ -49,6 +51,8 @@
char resPath[256];
unsigned char header[8];
int result = 0;
+ int color_type, bit_depth;
+ size_t bytesRead;
snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
resPath[sizeof(resPath)-1] = '\0';
@@ -58,7 +62,7 @@
goto exit;
}
- size_t bytesRead = fread(header, 1, sizeof(header), fp);
+ bytesRead = fread(header, 1, sizeof(header), fp);
if (bytesRead != sizeof(header)) {
result = -2;
goto exit;
@@ -90,7 +94,6 @@
png_set_sig_bytes(*png_ptr, sizeof(header));
png_read_info(*png_ptr, *info_ptr);
- int color_type, bit_depth;
png_get_IHDR(*png_ptr, *info_ptr, width, height, &bit_depth,
&color_type, NULL, NULL, NULL);
@@ -135,12 +138,10 @@
// framebuffer pixel format; they need to be modified if the
// framebuffer format changes (but nothing else should).
-// Allocate and return a gr_surface sufficient for storing an image of
+// Allocate and return a GRSurface* sufficient for storing an image of
// the indicated size in the framebuffer pixel format.
-static gr_surface init_display_surface(png_uint_32 width, png_uint_32 height) {
- gr_surface surface;
-
- surface = malloc_surface(width * height * 4);
+static GRSurface* init_display_surface(png_uint_32 width, png_uint_32 height) {
+ GRSurface* surface = malloc_surface(width * height * 4);
if (surface == NULL) return NULL;
surface->width = width;
@@ -196,13 +197,15 @@
}
}
-int res_create_display_surface(const char* name, gr_surface* pSurface) {
- gr_surface surface = NULL;
+int res_create_display_surface(const char* name, 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;
+ unsigned char* p_row;
+ unsigned int y;
*pSurface = NULL;
@@ -215,8 +218,11 @@
goto exit;
}
- unsigned char* p_row = malloc(width * 4);
- unsigned int y;
+#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
+ png_set_bgr(png_ptr);
+#endif
+
+ p_row = reinterpret_cast<unsigned char*>(malloc(width * 4));
for (y = 0; y < height; ++y) {
png_read_row(png_ptr, p_row, NULL);
transform_rgb_to_draw(p_row, surface->data + y * surface->row_bytes, channels, width);
@@ -231,14 +237,18 @@
return result;
}
-int res_create_multi_display_surface(const char* name, int* frames, gr_surface** pSurface) {
- gr_surface* surface = NULL;
+int res_create_multi_display_surface(const char* name, int* frames, 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;
+ unsigned int y;
*pSurface = NULL;
*frames = -1;
@@ -247,8 +257,6 @@
if (result < 0) return result;
*frames = 1;
- png_textp text;
- int num_text;
if (png_get_text(png_ptr, info_ptr, &text, &num_text)) {
for (i = 0; i < num_text; ++i) {
if (text[i].key && strcmp(text[i].key, "Frames") == 0 && text[i].text) {
@@ -265,7 +273,7 @@
goto exit;
}
- surface = malloc(*frames * sizeof(gr_surface));
+ surface = reinterpret_cast<GRSurface**>(malloc(*frames * sizeof(GRSurface*)));
if (surface == NULL) {
result = -8;
goto exit;
@@ -278,8 +286,11 @@
}
}
- unsigned char* p_row = malloc(width * 4);
- unsigned int y;
+#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
+ png_set_bgr(png_ptr);
+#endif
+
+ p_row = reinterpret_cast<unsigned char*>(malloc(width * 4));
for (y = 0; y < height; ++y) {
png_read_row(png_ptr, p_row, NULL);
int frame = y % *frames;
@@ -289,7 +300,7 @@
}
free(p_row);
- *pSurface = (gr_surface*) surface;
+ *pSurface = reinterpret_cast<GRSurface**>(surface);
exit:
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
@@ -305,8 +316,8 @@
return result;
}
-int res_create_alpha_surface(const char* name, gr_surface* pSurface) {
- gr_surface surface = NULL;
+int res_create_alpha_surface(const char* name, GRSurface** pSurface) {
+ GRSurface* surface = NULL;
int result = 0;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
@@ -333,6 +344,10 @@
surface->row_bytes = width;
surface->pixel_bytes = 1;
+#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
+ png_set_bgr(png_ptr);
+#endif
+
unsigned char* p_row;
unsigned int y;
for (y = 0; y < height; ++y) {
@@ -367,13 +382,15 @@
int res_create_localized_alpha_surface(const char* name,
const char* locale,
- gr_surface* pSurface) {
- gr_surface surface = NULL;
+ 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;
+ unsigned char* row;
+ png_uint_32 y;
*pSurface = NULL;
@@ -394,8 +411,7 @@
goto exit;
}
- unsigned char* row = malloc(width);
- png_uint_32 y;
+ row = reinterpret_cast<unsigned char*>(malloc(width));
for (y = 0; y < height; ++y) {
png_read_row(png_ptr, row, NULL);
int w = (row[1] << 8) | row[0];
@@ -422,7 +438,7 @@
memcpy(surface->data + i*w, row, w);
}
- *pSurface = (gr_surface) surface;
+ *pSurface = reinterpret_cast<GRSurface*>(surface);
break;
} else {
int i;
@@ -438,6 +454,6 @@
return result;
}
-void res_free_surface(gr_surface surface) {
+void res_free_surface(GRSurface* surface) {
free(surface);
}
diff --git a/minzip/DirUtil.c b/minzip/DirUtil.c
index fe2c880..97cb2e0 100644
--- a/minzip/DirUtil.c
+++ b/minzip/DirUtil.c
@@ -85,7 +85,7 @@
c--;
}
if (c == cpath) {
-//xxx test this path
+ //xxx test this path
/* No directory component. Act like the path was empty.
*/
errno = ENOENT;
@@ -206,7 +206,7 @@
/* recurse over components */
errno = 0;
while ((de = readdir(dir)) != NULL) {
-//TODO: don't blow the stack
+ //TODO: don't blow the stack
char dn[PATH_MAX];
if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
continue;
diff --git a/minzip/Hash.c b/minzip/Hash.c
index 8c6ca9b..8f8ed68 100644
--- a/minzip/Hash.c
+++ b/minzip/Hash.c
@@ -140,7 +140,6 @@
int i;
assert(countTombStones(pHashTable) == pHashTable->numDeadEntries);
- //LOGI("before: dead=%d\n", pHashTable->numDeadEntries);
pNewEntries = (HashEntry*) calloc(newSize, sizeof(HashTable));
if (pNewEntries == NULL)
@@ -196,7 +195,6 @@
(*cmpFunc)(pEntry->data, item) == 0)
{
/* match */
- //LOGD("+++ match on entry %d\n", pEntry - pHashTable->pEntries);
break;
}
@@ -206,8 +204,6 @@
break; /* edge case - single-entry table */
pEntry = pHashTable->pEntries;
}
-
- //LOGI("+++ look probing %d...\n", pEntry - pHashTable->pEntries);
}
if (pEntry->data == NULL) {
@@ -228,10 +224,6 @@
abort();
}
/* note "pEntry" is now invalid */
- } else {
- //LOGW("okay %d/%d/%d\n",
- // pHashTable->numEntries, pHashTable->tableSize,
- // (pHashTable->tableSize * LOAD_NUMER) / LOAD_DENOM);
}
/* full table is bad -- search for nonexistent never halts */
@@ -264,7 +256,6 @@
pEnd = &pHashTable->pEntries[pHashTable->tableSize];
while (pEntry->data != NULL) {
if (pEntry->data == item) {
- //LOGI("+++ stepping on entry %d\n", pEntry - pHashTable->pEntries);
pEntry->data = HASH_TOMBSTONE;
pHashTable->numEntries--;
pHashTable->numDeadEntries++;
@@ -277,8 +268,6 @@
break; /* edge case - single-entry table */
pEntry = pHashTable->pEntries;
}
-
- //LOGI("+++ del probing %d...\n", pEntry - pHashTable->pEntries);
}
return false;
diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c
index 8601591..b1fb455 100644
--- a/minzip/SysUtil.c
+++ b/minzip/SysUtil.c
@@ -29,11 +29,13 @@
assert(start_ != NULL);
assert(length_ != NULL);
- start = lseek(fd, 0L, SEEK_CUR);
- end = lseek(fd, 0L, SEEK_END);
- (void) lseek(fd, start, SEEK_SET);
+ // TODO: isn't start always 0 for the single call site? just use fstat instead?
- if (start == (off_t) -1 || end == (off_t) -1) {
+ start = TEMP_FAILURE_RETRY(lseek(fd, 0L, SEEK_CUR));
+ end = TEMP_FAILURE_RETRY(lseek(fd, 0L, SEEK_END));
+
+ if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1 ||
+ start == (off_t) -1 || end == (off_t) -1) {
LOGE("could not determine length of file\n");
return -1;
}
diff --git a/minzip/Zip.c b/minzip/Zip.c
index 70aff00..40712e0 100644
--- a/minzip/Zip.c
+++ b/minzip/Zip.c
@@ -327,10 +327,6 @@
#else
pEntry = &pArchive->pEntries[i];
#endif
-
- //LOGI("%d: localHdr=%d fnl=%d el=%d cl=%d\n",
- // i, localHdrOffset, fileNameLen, extraLen, commentLen);
-
pEntry->fileNameLen = fileNameLen;
pEntry->fileName = fileName;
@@ -488,7 +484,7 @@
/*
* Return true if the entry is a symbolic link.
*/
-bool mzIsZipEntrySymlink(const ZipEntry* pEntry)
+static bool mzIsZipEntrySymlink(const ZipEntry* pEntry)
{
if ((pEntry->versionMadeBy & 0xff00) == CENVEM_UNIX) {
return S_ISLNK(pEntry->externalFileAttributes >> 16);
@@ -632,30 +628,6 @@
return true;
}
-/*
- * Check the CRC on this entry; return true if it is correct.
- * May do other internal checks as well.
- */
-bool mzIsZipEntryIntact(const ZipArchive *pArchive, const ZipEntry *pEntry)
-{
- unsigned long crc;
- bool ret;
-
- crc = crc32(0L, Z_NULL, 0);
- ret = mzProcessZipEntryContents(pArchive, pEntry, crcProcessFunction,
- (void *)&crc);
- if (!ret) {
- LOGE("Can't calculate CRC for entry\n");
- return false;
- }
- if (crc != (unsigned long)pEntry->crc32) {
- LOGW("CRC for entry %.*s (0x%08lx) != expected (0x%08lx)\n",
- pEntry->fileNameLen, pEntry->fileName, crc, pEntry->crc32);
- return false;
- }
- return true;
-}
-
typedef struct {
char *buf;
int bufLen;
@@ -703,13 +675,11 @@
}
ssize_t soFar = 0;
while (true) {
- ssize_t n = write(fd, data+soFar, dataLen-soFar);
+ ssize_t n = TEMP_FAILURE_RETRY(write(fd, data+soFar, dataLen-soFar));
if (n <= 0) {
LOGE("Error writing %zd bytes from zip file from %p: %s\n",
dataLen-soFar, data+soFar, strerror(errno));
- if (errno != EINTR) {
- return false;
- }
+ return false;
} else if (n > 0) {
soFar += n;
if (soFar == dataLen) return true;
@@ -737,23 +707,6 @@
return true;
}
-/*
- * Obtain a pointer to the in-memory representation of a stored entry.
- */
-bool mzGetStoredEntry(const ZipArchive *pArchive,
- const ZipEntry *pEntry, unsigned char **addr, size_t *length)
-{
- if (pEntry->compression != STORED) {
- LOGE("Can't getStoredEntry for '%s'; not stored\n",
- pEntry->fileName);
- return false;
- }
-
- *addr = pArchive->addr + pEntry->offset;
- *length = pEntry->uncompLen;
- return true;
-}
-
typedef struct {
unsigned char* buffer;
long len;
@@ -873,7 +826,7 @@
*/
bool mzExtractRecursive(const ZipArchive *pArchive,
const char *zipDir, const char *targetDir,
- int flags, const struct utimbuf *timestamp,
+ const struct utimbuf *timestamp,
void (*callback)(const char *fn, void *), void *cookie,
struct selabel_handle *sehnd)
{
@@ -923,8 +876,8 @@
/* Walk through the entries and extract anything whose path begins
* with zpath.
-//TODO: since the entries are sorted, binary search for the first match
-// and stop after the first non-match.
+ //TODO: since the entries are sorted, binary search for the first match
+ // and stop after the first non-match.
*/
unsigned int i;
bool seenMatch = false;
@@ -933,10 +886,10 @@
for (i = 0; i < pArchive->numEntries; i++) {
ZipEntry *pEntry = pArchive->pEntries + i;
if (pEntry->fileNameLen < zipDirLen) {
-//TODO: look out for a single empty directory entry that matches zpath, but
-// missing the trailing slash. Most zip files seem to include
-// the trailing slash, but I think it's legal to leave it off.
-// e.g., zpath "a/b/", entry "a/b", with no children of the entry.
+ //TODO: look out for a single empty directory entry that matches zpath, but
+ // missing the trailing slash. Most zip files seem to include
+ // the trailing slash, but I think it's legal to leave it off.
+ // e.g., zpath "a/b/", entry "a/b", with no children of the entry.
/* No chance of matching.
*/
#if SORT_ENTRIES
@@ -977,30 +930,19 @@
break;
}
- /* With DRY_RUN set, invoke the callback but don't do anything else.
- */
- if (flags & MZ_EXTRACT_DRY_RUN) {
- if (callback != NULL) callback(targetFile, cookie);
- continue;
- }
-
- /* Create the file or directory.
- */
#define UNZIP_DIRMODE 0755
#define UNZIP_FILEMODE 0644
- if (pEntry->fileName[pEntry->fileNameLen-1] == '/') {
- if (!(flags & MZ_EXTRACT_FILES_ONLY)) {
- int ret = dirCreateHierarchy(
- targetFile, UNZIP_DIRMODE, timestamp, false, sehnd);
- if (ret != 0) {
- LOGE("Can't create containing directory for \"%s\": %s\n",
- targetFile, strerror(errno));
- ok = false;
- break;
- }
- LOGD("Extracted dir \"%s\"\n", targetFile);
- }
- } else {
+ /*
+ * Create the file or directory. We ignore directory entries
+ * because we recursively create paths to each file entry we encounter
+ * in the zip archive anyway.
+ *
+ * NOTE: A "directory entry" in a zip archive is just a zero length
+ * entry that ends in a "/". They're not mandatory and many tools get
+ * rid of them. We need to process them only if we want to preserve
+ * empty directories from the archive.
+ */
+ if (pEntry->fileName[pEntry->fileNameLen-1] != '/') {
/* This is not a directory. First, make sure that
* the containing directory exists.
*/
@@ -1013,97 +955,62 @@
break;
}
- /* With FILES_ONLY set, we need to ignore metadata entirely,
- * so treat symlinks as regular files.
+ /*
+ * The entry is a regular file or a symlink. Open the target for writing.
+ *
+ * TODO: This behavior for symlinks seems rather bizarre. For a
+ * symlink foo/bar/baz -> foo/tar/taz, we will create a file called
+ * "foo/bar/baz" whose contents are the literal "foo/tar/taz". We
+ * warn about this for now and preserve older behavior.
*/
- if (!(flags & MZ_EXTRACT_FILES_ONLY) && mzIsZipEntrySymlink(pEntry)) {
- /* The entry is a symbolic link.
- * The relative target of the symlink is in the
- * data section of this entry.
- */
- if (pEntry->uncompLen == 0) {
- LOGE("Symlink entry \"%s\" has no target\n",
- targetFile);
- ok = false;
- break;
- }
- char *linkTarget = malloc(pEntry->uncompLen + 1);
- if (linkTarget == NULL) {
- ok = false;
- break;
- }
- ok = mzReadZipEntry(pArchive, pEntry, linkTarget,
- pEntry->uncompLen);
- if (!ok) {
- LOGE("Can't read symlink target for \"%s\"\n",
- targetFile);
- free(linkTarget);
- break;
- }
- linkTarget[pEntry->uncompLen] = '\0';
-
- /* Make the link.
- */
- ret = symlink(linkTarget, targetFile);
- if (ret != 0) {
- LOGE("Can't symlink \"%s\" to \"%s\": %s\n",
- targetFile, linkTarget, strerror(errno));
- free(linkTarget);
- ok = false;
- break;
- }
- LOGD("Extracted symlink \"%s\" -> \"%s\"\n",
- targetFile, linkTarget);
- free(linkTarget);
- } else {
- /* The entry is a regular file.
- * Open the target for writing.
- */
-
- char *secontext = NULL;
-
- if (sehnd) {
- selabel_lookup(sehnd, &secontext, targetFile, UNZIP_FILEMODE);
- setfscreatecon(secontext);
- }
-
- int fd = open(targetFile, O_CREAT|O_WRONLY|O_TRUNC|O_SYNC
- , UNZIP_FILEMODE);
-
- if (secontext) {
- freecon(secontext);
- setfscreatecon(NULL);
- }
-
- if (fd < 0) {
- LOGE("Can't create target file \"%s\": %s\n",
- targetFile, strerror(errno));
- ok = false;
- break;
- }
-
- bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd);
- if (ok) {
- ok = (fsync(fd) == 0);
- }
- if (close(fd) != 0) {
- ok = false;
- }
- if (!ok) {
- LOGE("Error extracting \"%s\"\n", targetFile);
- ok = false;
- break;
- }
-
- if (timestamp != NULL && utime(targetFile, timestamp)) {
- LOGE("Error touching \"%s\"\n", targetFile);
- ok = false;
- break;
- }
-
- LOGV("Extracted file \"%s\"\n", targetFile);
- ++extractCount;
+ if (mzIsZipEntrySymlink(pEntry)) {
+ LOGE("Symlink entry \"%.*s\" will be output as a regular file.",
+ pEntry->fileNameLen, pEntry->fileName);
}
+
+ char *secontext = NULL;
+
+ if (sehnd) {
+ selabel_lookup(sehnd, &secontext, targetFile, UNZIP_FILEMODE);
+ setfscreatecon(secontext);
+ }
+
+ int fd = open(targetFile, O_CREAT|O_WRONLY|O_TRUNC|O_SYNC,
+ UNZIP_FILEMODE);
+
+ if (secontext) {
+ freecon(secontext);
+ setfscreatecon(NULL);
+ }
+
+ if (fd < 0) {
+ LOGE("Can't create target file \"%s\": %s\n",
+ targetFile, strerror(errno));
+ ok = false;
+ break;
+ }
+
+ bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd);
+ if (ok) {
+ ok = (fsync(fd) == 0);
+ }
+ if (close(fd) != 0) {
+ ok = false;
+ }
+ if (!ok) {
+ LOGE("Error extracting \"%s\"\n", targetFile);
+ ok = false;
+ break;
+ }
+
+ if (timestamp != NULL && utime(targetFile, timestamp)) {
+ LOGE("Error touching \"%s\"\n", targetFile);
+ ok = false;
+ break;
+ }
+
+ LOGV("Extracted file \"%s\"\n", targetFile);
+ ++extractCount;
}
if (callback != NULL) callback(targetFile, cookie);
diff --git a/minzip/Zip.h b/minzip/Zip.h
index 2054b38..86d8db5 100644
--- a/minzip/Zip.h
+++ b/minzip/Zip.h
@@ -85,56 +85,12 @@
const ZipEntry* mzFindZipEntry(const ZipArchive* pArchive,
const char* entryName);
-/*
- * Get the number of entries in the Zip archive.
- */
-INLINE unsigned int mzZipEntryCount(const ZipArchive* pArchive) {
- return pArchive->numEntries;
-}
-
-/*
- * Get an entry by index. Returns NULL if the index is out-of-bounds.
- */
-INLINE const ZipEntry*
-mzGetZipEntryAt(const ZipArchive* pArchive, unsigned int index)
-{
- if (index < pArchive->numEntries) {
- return pArchive->pEntries + index;
- }
- return NULL;
-}
-
-/*
- * Get the index number of an entry in the archive.
- */
-INLINE unsigned int
-mzGetZipEntryIndex(const ZipArchive *pArchive, const ZipEntry *pEntry) {
- return pEntry - pArchive->pEntries;
-}
-
-/*
- * Simple accessors.
- */
-INLINE UnterminatedString mzGetZipEntryFileName(const ZipEntry* pEntry) {
- UnterminatedString ret;
- ret.str = pEntry->fileName;
- ret.len = pEntry->fileNameLen;
- return ret;
-}
INLINE long mzGetZipEntryOffset(const ZipEntry* pEntry) {
return pEntry->offset;
}
INLINE long mzGetZipEntryUncompLen(const ZipEntry* pEntry) {
return pEntry->uncompLen;
}
-INLINE long mzGetZipEntryModTime(const ZipEntry* pEntry) {
- return pEntry->modTime;
-}
-INLINE long mzGetZipEntryCrc32(const ZipEntry* pEntry) {
- return pEntry->crc32;
-}
-bool mzIsZipEntrySymlink(const ZipEntry* pEntry);
-
/*
* Type definition for the callback function used by
@@ -164,12 +120,6 @@
char* buf, int bufLen);
/*
- * Check the CRC on this entry; return true if it is correct.
- * May do other internal checks as well.
- */
-bool mzIsZipEntryIntact(const ZipArchive *pArchive, const ZipEntry *pEntry);
-
-/*
* Inflate and write an entry to a file.
*/
bool mzExtractZipEntryToFile(const ZipArchive *pArchive,
@@ -183,20 +133,12 @@
const ZipEntry *pEntry, unsigned char* buffer);
/*
- * Return a pointer and length for a given entry. The returned region
- * should be valid until pArchive is closed, and should be treated as
- * read-only.
- *
- * Only makes sense for entries which are stored (ie, not compressed).
- * No guarantees are made regarding alignment of the returned pointer.
- */
-bool mzGetStoredEntry(const ZipArchive *pArchive,
- const ZipEntry* pEntry, unsigned char **addr, size_t *length);
-
-/*
- * Inflate all entries under zipDir to the directory specified by
+ * Inflate all files under zipDir to the directory specified by
* targetDir, which must exist and be a writable directory.
*
+ * Directory entries and symlinks are not extracted.
+ *
+ *
* The immediate children of zipDir will become the immediate
* children of targetDir; e.g., if the archive contains the entries
*
@@ -211,21 +153,15 @@
* /tmp/two
* /tmp/d/three
*
- * flags is zero or more of the following:
- *
- * MZ_EXTRACT_FILES_ONLY - only unpack files, not directories or symlinks
- * MZ_EXTRACT_DRY_RUN - don't do anything, but do invoke the callback
- *
* If timestamp is non-NULL, file timestamps will be set accordingly.
*
* If callback is non-NULL, it will be invoked with each unpacked file.
*
* Returns true on success, false on failure.
*/
-enum { MZ_EXTRACT_FILES_ONLY = 1, MZ_EXTRACT_DRY_RUN = 2 };
bool mzExtractRecursive(const ZipArchive *pArchive,
const char *zipDir, const char *targetDir,
- int flags, const struct utimbuf *timestamp,
+ const struct utimbuf *timestamp,
void (*callback)(const char *fn, void*), void *cookie,
struct selabel_handle *sehnd);
diff --git a/mtdutils/flash_image.c b/mtdutils/flash_image.c
index 5657dfc..36ffa13 100644
--- a/mtdutils/flash_image.c
+++ b/mtdutils/flash_image.c
@@ -72,7 +72,7 @@
if (fd < 0) die("error opening %s", argv[2]);
char header[HEADER_SIZE];
- int headerlen = read(fd, header, sizeof(header));
+ int headerlen = TEMP_FAILURE_RETRY(read(fd, header, sizeof(header)));
if (headerlen <= 0) die("error reading %s header", argv[2]);
MtdReadContext *in = mtd_read_partition(partition);
@@ -104,7 +104,7 @@
if (wrote != headerlen) die("error writing %s", argv[1]);
int len;
- while ((len = read(fd, buf, sizeof(buf))) > 0) {
+ while ((len = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf)))) > 0) {
wrote = mtd_write_data(out, buf, len);
if (wrote != len) die("error writing %s", argv[1]);
}
@@ -125,13 +125,13 @@
if (mtd_partition_info(partition, NULL, &block_size, NULL))
die("error getting %s block size", argv[1]);
- if (lseek(fd, headerlen, SEEK_SET) != headerlen)
+ if (TEMP_FAILURE_RETRY(lseek(fd, headerlen, SEEK_SET)) != headerlen)
die("error rewinding %s", argv[2]);
int left = block_size - headerlen;
while (left < 0) left += block_size;
while (left > 0) {
- len = read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left);
+ len = TEMP_FAILURE_RETRY(read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left));
if (len <= 0) die("error reading %s", argv[2]);
if (mtd_write_data(out, buf, len) != len)
die("error writing %s", argv[1]);
diff --git a/mtdutils/mounts.c b/mtdutils/mounts.c
index c90fc8a..6a9b03d 100644
--- a/mtdutils/mounts.c
+++ b/mtdutils/mounts.c
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <mntent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -59,10 +60,8 @@
int
scan_mounted_volumes()
{
- char buf[2048];
- const char *bufp;
- int fd;
- ssize_t nbytes;
+ FILE* fp;
+ struct mntent* mentry;
if (g_mounts_state.volumes == NULL) {
const int numv = 32;
@@ -84,80 +83,20 @@
}
g_mounts_state.volume_count = 0;
- /* Open and read the file contents.
- */
- fd = open(PROC_MOUNTS_FILENAME, O_RDONLY);
- if (fd < 0) {
- goto bail;
+ /* Open and read mount table entries. */
+ fp = setmntent(PROC_MOUNTS_FILENAME, "r");
+ if (fp == NULL) {
+ return -1;
}
- nbytes = read(fd, buf, sizeof(buf) - 1);
- close(fd);
- if (nbytes < 0) {
- goto bail;
+ while ((mentry = getmntent(fp)) != NULL) {
+ MountedVolume* v = &g_mounts_state.volumes[g_mounts_state.volume_count++];
+ v->device = strdup(mentry->mnt_fsname);
+ v->mount_point = strdup(mentry->mnt_dir);
+ v->filesystem = strdup(mentry->mnt_type);
+ v->flags = strdup(mentry->mnt_opts);
}
- buf[nbytes] = '\0';
-
- /* Parse the contents of the file, which looks like:
- *
- * # cat /proc/mounts
- * rootfs / rootfs rw 0 0
- * /dev/pts /dev/pts devpts rw 0 0
- * /proc /proc proc rw 0 0
- * /sys /sys sysfs rw 0 0
- * /dev/block/mtdblock4 /system yaffs2 rw,nodev,noatime,nodiratime 0 0
- * /dev/block/mtdblock5 /data yaffs2 rw,nodev,noatime,nodiratime 0 0
- * /dev/block/mmcblk0p1 /sdcard vfat rw,sync,dirsync,fmask=0000,dmask=0000,codepage=cp437,iocharset=iso8859-1,utf8 0 0
- *
- * The zeroes at the end are dummy placeholder fields to make the
- * output match Linux's /etc/mtab, but don't represent anything here.
- */
- bufp = buf;
- while (nbytes > 0) {
- char device[64];
- char mount_point[64];
- char filesystem[64];
- char flags[128];
- int matches;
-
- /* %as is a gnu extension that malloc()s a string for each field.
- */
- matches = sscanf(bufp, "%63s %63s %63s %127s",
- device, mount_point, filesystem, flags);
-
- if (matches == 4) {
- device[sizeof(device)-1] = '\0';
- mount_point[sizeof(mount_point)-1] = '\0';
- filesystem[sizeof(filesystem)-1] = '\0';
- flags[sizeof(flags)-1] = '\0';
-
- MountedVolume *v =
- &g_mounts_state.volumes[g_mounts_state.volume_count++];
- v->device = strdup(device);
- v->mount_point = strdup(mount_point);
- v->filesystem = strdup(filesystem);
- v->flags = strdup(flags);
- } else {
-printf("matches was %d on <<%.40s>>\n", matches, bufp);
- }
-
- /* Eat the line.
- */
- while (nbytes > 0 && *bufp != '\n') {
- bufp++;
- nbytes--;
- }
- if (nbytes > 0) {
- bufp++;
- nbytes--;
- }
- }
-
+ endmntent(fp);
return 0;
-
-bail:
-//TODO: free the strings we've allocated.
- g_mounts_state.volume_count = 0;
- return -1;
}
const MountedVolume *
diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c
index d04b26e..cc30334 100644
--- a/mtdutils/mtdutils.c
+++ b/mtdutils/mtdutils.c
@@ -108,7 +108,7 @@
if (fd < 0) {
goto bail;
}
- nbytes = read(fd, buf, sizeof(buf) - 1);
+ nbytes = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf) - 1));
close(fd);
if (nbytes < 0) {
goto bail;
@@ -279,12 +279,6 @@
return ctx;
}
-// Seeks to a location in the partition. Don't mix with reads of
-// anything other than whole blocks; unpredictable things will result.
-void mtd_read_skip_to(const MtdReadContext* ctx, size_t offset) {
- lseek64(ctx->fd, offset, SEEK_SET);
-}
-
static int read_block(const MtdPartition *partition, int fd, char *data)
{
struct mtd_ecc_stats before, after;
@@ -293,13 +287,18 @@
return -1;
}
- loff_t pos = lseek64(fd, 0, SEEK_CUR);
+ loff_t pos = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));
+ if (pos == -1) {
+ printf("mtd: read_block: couldn't SEEK_CUR: %s\n", strerror(errno));
+ return -1;
+ }
ssize_t size = partition->erase_size;
int mgbb;
while (pos + size <= (int) partition->size) {
- if (lseek64(fd, pos, SEEK_SET) != pos || read(fd, data, size) != size) {
+ 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));
} else if (ioctl(fd, ECCGETSTATS, &after)) {
@@ -313,8 +312,8 @@
memcpy(&before, &after, sizeof(struct mtd_ecc_stats));
} else if ((mgbb = ioctl(fd, MEMGETBADBLOCK, &pos))) {
fprintf(stderr,
- "mtd: MEMGETBADBLOCK returned %d at 0x%08llx (errno=%d)\n",
- mgbb, pos, errno);
+ "mtd: MEMGETBADBLOCK returned %d at 0x%08llx: %s\n",
+ mgbb, pos, strerror(errno));
} else {
return 0; // Success!
}
@@ -409,8 +408,11 @@
const MtdPartition *partition = ctx->partition;
int fd = ctx->fd;
- off_t pos = lseek(fd, 0, SEEK_CUR);
- if (pos == (off_t) -1) return 1;
+ off_t pos = TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_CUR));
+ if (pos == (off_t) -1) {
+ printf("mtd: write_block: couldn't SEEK_CUR: %s\n", strerror(errno));
+ return -1;
+ }
ssize_t size = partition->erase_size;
while (pos + size <= (int) partition->size) {
@@ -419,8 +421,8 @@
if (ret != 0 && !(ret == -1 && errno == EOPNOTSUPP)) {
add_bad_block_offset(ctx, pos);
fprintf(stderr,
- "mtd: not writing bad block at 0x%08lx (ret %d errno %d)\n",
- pos, ret, errno);
+ "mtd: not writing bad block at 0x%08lx (ret %d): %s\n",
+ pos, ret, strerror(errno));
pos += partition->erase_size;
continue; // Don't try to erase known factory-bad blocks.
}
@@ -435,15 +437,15 @@
pos, strerror(errno));
continue;
}
- if (lseek(fd, pos, SEEK_SET) != pos ||
- write(fd, data, size) != size) {
+ if (TEMP_FAILURE_RETRY(lseek(fd, pos, SEEK_SET)) != pos ||
+ TEMP_FAILURE_RETRY(write(fd, data, size)) != size) {
printf("mtd: write error at 0x%08lx (%s)\n",
pos, strerror(errno));
}
char verify[size];
- if (lseek(fd, pos, SEEK_SET) != pos ||
- read(fd, verify, size) != size) {
+ if (TEMP_FAILURE_RETRY(lseek(fd, pos, SEEK_SET)) != pos ||
+ TEMP_FAILURE_RETRY(read(fd, verify, size)) != size) {
printf("mtd: re-read error at 0x%08lx (%s)\n",
pos, strerror(errno));
continue;
@@ -512,8 +514,11 @@
ctx->stored = 0;
}
- off_t pos = lseek(ctx->fd, 0, SEEK_CUR);
- if ((off_t) pos == (off_t) -1) return pos;
+ off_t pos = TEMP_FAILURE_RETRY(lseek(ctx->fd, 0, SEEK_CUR));
+ if ((off_t) pos == (off_t) -1) {
+ printf("mtd_erase_blocks: couldn't SEEK_CUR: %s\n", strerror(errno));
+ return -1;
+ }
const int total = (ctx->partition->size - pos) / ctx->partition->erase_size;
if (blocks < 0) blocks = total;
@@ -554,18 +559,3 @@
free(ctx);
return r;
}
-
-/* Return the offset of the first good block at or after pos (which
- * might be pos itself).
- */
-off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) {
- int i;
- for (i = 0; i < ctx->bad_block_count; ++i) {
- if (ctx->bad_block_offsets[i] == pos) {
- pos += ctx->partition->erase_size;
- } else if (ctx->bad_block_offsets[i] > pos) {
- return pos;
- }
- }
- return pos;
-}
diff --git a/mtdutils/mtdutils.h b/mtdutils/mtdutils.h
index 2708c43..8059d6a 100644
--- a/mtdutils/mtdutils.h
+++ b/mtdutils/mtdutils.h
@@ -49,12 +49,10 @@
MtdReadContext *mtd_read_partition(const MtdPartition *);
ssize_t mtd_read_data(MtdReadContext *, char *data, size_t data_len);
void mtd_read_close(MtdReadContext *);
-void mtd_read_skip_to(const MtdReadContext *, size_t offset);
MtdWriteContext *mtd_write_partition(const MtdPartition *);
ssize_t mtd_write_data(MtdWriteContext *, const char *data, size_t data_len);
off_t mtd_erase_blocks(MtdWriteContext *, int blocks); /* 0 ok, -1 for all */
-off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos);
int mtd_write_close(MtdWriteContext *);
#ifdef __cplusplus
diff --git a/recovery.cpp b/recovery.cpp
index 575e287..b7a5458 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -31,6 +31,9 @@
#include <time.h>
#include <unistd.h>
+#include <base/file.h>
+#include <base/stringprintf.h>
+
#include "bootloader.h"
#include "common.h"
#include "cutils/properties.h"
@@ -43,20 +46,20 @@
#include "screen_ui.h"
#include "device.h"
#include "adb_install.h"
-extern "C" {
-#include "minadbd/adb.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, 's' },
+ { "send_intent", required_argument, NULL, 'i' },
{ "update_package", required_argument, NULL, 'u' },
{ "wipe_data", no_argument, NULL, 'w' },
{ "wipe_cache", no_argument, NULL, 'c' },
{ "show_text", no_argument, NULL, 't' },
+ { "sideload", no_argument, NULL, 's' },
+ { "sideload_auto_reboot", no_argument, NULL, 'a' },
{ "just_exit", no_argument, NULL, 'x' },
{ "locale", required_argument, NULL, 'l' },
{ "stages", required_argument, NULL, 'g' },
@@ -65,8 +68,6 @@
{ NULL, 0, NULL, 0 },
};
-#define LAST_LOG_FILE "/cache/recovery/last_log"
-
static const char *CACHE_LOG_DIR = "/cache/recovery";
static const char *COMMAND_FILE = "/cache/recovery/command";
static const char *INTENT_FILE = "/cache/recovery/intent";
@@ -78,18 +79,14 @@
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";
-#define KLOG_DEFAULT_LEN (64 * 1024)
-
-#define KEEP_LOG_COUNT 10
-
-// Number of lines per page when displaying a file on screen
-#define LINES_PER_PAGE 30
+static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
+static const int KEEP_LOG_COUNT = 10;
RecoveryUI* ui = NULL;
char* locale = NULL;
-char recovery_version[PROPERTY_VALUE_MAX+1];
char* stage = NULL;
char* reason = NULL;
+bool modified_flash = false;
/*
* The recovery tool communicates with the main system through /cache files.
@@ -169,6 +166,11 @@
return 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);
@@ -265,87 +267,89 @@
set_bootloader_message(&boot);
}
-// read from kernel log into buffer and write out to file
-static void
-save_kernel_log(const char *destination) {
- int n;
- char *buffer;
- int klog_buf_len;
- FILE *log;
-
- klog_buf_len = klogctl(KLOG_SIZE_BUFFER, 0, 0);
+// Read from kernel log into buffer and write out to file.
+static void save_kernel_log(const char* destination) {
+ int klog_buf_len = klogctl(KLOG_SIZE_BUFFER, 0, 0);
if (klog_buf_len <= 0) {
- LOGE("Error getting klog size (%s), using default\n", strerror(errno));
- klog_buf_len = KLOG_DEFAULT_LEN;
- }
-
- buffer = (char *)malloc(klog_buf_len);
- if (!buffer) {
- LOGE("Can't alloc %d bytes for klog buffer\n", klog_buf_len);
+ LOGE("Error getting klog size: %s\n", strerror(errno));
return;
}
- n = klogctl(KLOG_READ_ALL, buffer, klog_buf_len);
- if (n < 0) {
- LOGE("Error in reading klog (%s)\n", strerror(errno));
- free(buffer);
+ std::string buffer(klog_buf_len, 0);
+ int n = klogctl(KLOG_READ_ALL, &buffer[0], klog_buf_len);
+ if (n == -1) {
+ LOGE("Error in reading klog: %s\n", strerror(errno));
return;
}
-
- log = fopen_path(destination, "w");
- if (log == NULL) {
- LOGE("Can't open %s\n", destination);
- free(buffer);
- return;
- }
- fwrite(buffer, n, 1, log);
- check_and_fclose(log, destination);
- free(buffer);
+ buffer.resize(n);
+ android::base::WriteStringToFile(buffer, destination);
}
// How much of the temp log we have copied to the copy in cache.
static long tmplog_offset = 0;
-static void
-copy_log_file(const char* source, const char* destination, int append) {
- FILE *log = fopen_path(destination, append ? "a" : "w");
- if (log == NULL) {
+static void copy_log_file(const char* source, const char* destination, bool append) {
+ FILE* dest_fp = fopen_path(destination, append ? "a" : "w");
+ if (dest_fp == nullptr) {
LOGE("Can't open %s\n", destination);
} else {
- FILE *tmplog = fopen(source, "r");
- if (tmplog != NULL) {
+ FILE* source_fp = fopen(source, "r");
+ if (source_fp != nullptr) {
if (append) {
- fseek(tmplog, tmplog_offset, SEEK_SET); // Since last write
+ fseek(source_fp, tmplog_offset, SEEK_SET); // Since last write
}
char buf[4096];
- while (fgets(buf, sizeof(buf), tmplog)) fputs(buf, log);
- if (append) {
- tmplog_offset = ftell(tmplog);
+ size_t bytes;
+ while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) {
+ fwrite(buf, 1, bytes, dest_fp);
}
- check_and_fclose(tmplog, source);
+ if (append) {
+ tmplog_offset = ftell(source_fp);
+ }
+ check_and_fclose(source_fp, source);
}
- check_and_fclose(log, destination);
+ check_and_fclose(dest_fp, destination);
}
}
-// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max
-// Overwrites any existing last_log.$max.
-static void
-rotate_last_logs(int max) {
- char oldfn[256];
- char newfn[256];
+// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max.
+// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max.
+// Overwrite any existing last_log.$max and last_kmsg.$max.
+static void rotate_logs(int max) {
+ // Logs should only be rotated once.
+ static bool rotated = false;
+ if (rotated) {
+ return;
+ }
+ rotated = true;
+ ensure_path_mounted(LAST_LOG_FILE);
+ ensure_path_mounted(LAST_KMSG_FILE);
- int i;
- for (i = max-1; i >= 0; --i) {
- snprintf(oldfn, sizeof(oldfn), (i==0) ? LAST_LOG_FILE : (LAST_LOG_FILE ".%d"), i);
- snprintf(newfn, sizeof(newfn), LAST_LOG_FILE ".%d", i+1);
- // ignore errors
- rename(oldfn, newfn);
+ 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 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 new_kmsg = android::base::StringPrintf("%s.%d", LAST_KMSG_FILE, i+1);
+ rename(old_kmsg.c_str(), new_kmsg.c_str());
}
}
-static void
-copy_logs() {
+static void copy_logs() {
+ // We only rotate and record the log of the current session if there are
+ // actual attempts to modify the flash, such as wipes, installs from BCB
+ // or menu selections. This is to avoid unnecessary rotation (and
+ // possible deletion) of log files, if it does not do anything loggable.
+ if (!modified_flash) {
+ return;
+ }
+
+ rotate_logs(KEEP_LOG_COUNT);
+
// Copy logs to cache so the system can find out what happened.
copy_log_file(TEMPORARY_LOG_FILE, LOG_FILE, true);
copy_log_file(TEMPORARY_LOG_FILE, LAST_LOG_FILE, false);
@@ -413,8 +417,7 @@
struct _saved_log_file* next;
} saved_log_file;
-static int
-erase_volume(const char *volume) {
+static bool erase_volume(const char* volume) {
bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
ui->SetBackground(RecoveryUI::ERASING);
@@ -423,9 +426,10 @@
saved_log_file* head = NULL;
if (is_cache) {
- // If we're reformatting /cache, we load any
- // "/cache/recovery/last*" files into memory, so we can restore
- // them after the reformat.
+ // If we're reformatting /cache, we load any past logs
+ // (i.e. "/cache/recovery/last_*") and the current log
+ // ("/cache/recovery/log") into memory, so we can restore them after
+ // the reformat.
ensure_path_mounted(volume);
@@ -438,7 +442,7 @@
strcat(path, "/");
int path_len = strlen(path);
while ((de = readdir(d)) != NULL) {
- if (strncmp(de->d_name, "last", 4) == 0) {
+ if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) {
saved_log_file* p = (saved_log_file*) malloc(sizeof(saved_log_file));
strcpy(path+path_len, de->d_name);
p->name = strdup(path);
@@ -494,26 +498,7 @@
copy_logs();
}
- return result;
-}
-
-static const char**
-prepend_title(const char* const* headers) {
- // count the number of lines in our title, plus the
- // caller-provided headers.
- int count = 3; // our title has 3 lines
- const char* const* p;
- for (p = headers; *p; ++p, ++count);
-
- const char** new_headers = (const char**)malloc((count+1) * sizeof(char*));
- const char** h = new_headers;
- *(h++) = "Android system recovery <" EXPAND(RECOVERY_API_VERSION) "e>";
- *(h++) = recovery_version;
- *(h++) = "";
- for (p = headers; *p; ++p, ++h) *h = *p;
- *h = NULL;
-
- return new_headers;
+ return (result == 0);
}
static int
@@ -546,12 +531,10 @@
if (action < 0) {
switch (action) {
case Device::kHighlightUp:
- --selected;
- selected = ui->SelectMenu(selected);
+ selected = ui->SelectMenu(--selected);
break;
case Device::kHighlightDown:
- ++selected;
- selected = ui->SelectMenu(selected);
+ selected = ui->SelectMenu(++selected);
break;
case Device::kInvokeItem:
chosen_item = selected;
@@ -573,24 +556,15 @@
}
// Returns a malloc'd path, or NULL.
-static char*
-browse_directory(const char* path, Device* device) {
+static char* browse_directory(const char* path, Device* device) {
ensure_path_mounted(path);
- const char* MENU_HEADERS[] = { "Choose a package to install:",
- path,
- "",
- NULL };
- DIR* d;
- struct dirent* de;
- d = opendir(path);
+ DIR* d = opendir(path);
if (d == NULL) {
LOGE("error opening %s: %s\n", path, strerror(errno));
return NULL;
}
- const char** headers = prepend_title(MENU_HEADERS);
-
int d_size = 0;
int d_alloc = 10;
char** dirs = (char**)malloc(d_alloc * sizeof(char*));
@@ -599,6 +573,7 @@
char** zips = (char**)malloc(z_alloc * sizeof(char*));
zips[0] = strdup("../");
+ struct dirent* de;
while ((de = readdir(d)) != NULL) {
int name_len = strlen(de->d_name);
@@ -642,6 +617,8 @@
z_size += d_size;
zips[z_size] = NULL;
+ const char* headers[] = { "Choose a package to install:", path, NULL };
+
char* result;
int chosen_item = 0;
while (true) {
@@ -672,160 +649,135 @@
}
}
- int i;
- for (i = 0; i < z_size; ++i) free(zips[i]);
+ for (int i = 0; i < z_size; ++i) free(zips[i]);
free(zips);
- free(headers);
return result;
}
-static void
-wipe_data(int confirm, Device* device) {
- if (confirm) {
- static const char** title_headers = NULL;
+static bool yes_no(Device* device, const char* question1, const char* question2) {
+ const char* headers[] = { question1, question2, NULL };
+ const char* items[] = { " No", " Yes", NULL };
- if (title_headers == NULL) {
- const char* headers[] = { "Confirm wipe of all user data?",
- " THIS CAN NOT BE UNDONE.",
- "",
- NULL };
- title_headers = prepend_title((const char**)headers);
- }
-
- const char* items[] = { " No",
- " No",
- " No",
- " No",
- " No",
- " No",
- " No",
- " Yes -- delete all user data", // [7]
- " No",
- " No",
- " No",
- NULL };
-
- int chosen_item = get_menu_selection(title_headers, items, 1, 0, device);
- if (chosen_item != 7) {
- return;
- }
- }
-
- ui->Print("\n-- Wiping data...\n");
- device->WipeData();
- erase_volume("/data");
- erase_volume("/cache");
- ui->Print("Data wipe complete.\n");
+ int chosen_item = get_menu_selection(headers, items, 1, 0, device);
+ return (chosen_item == 1);
}
-static void file_to_ui(const char* fn) {
- FILE *fp = fopen_path(fn, "re");
- if (fp == NULL) {
- ui->Print(" Unable to open %s: %s\n", fn, strerror(errno));
- return;
- }
- char line[1024];
- int ct = 0;
- int key = 0;
- redirect_stdio("/dev/null");
- while(fgets(line, sizeof(line), fp) != NULL) {
- ui->Print("%s", line);
- ct++;
- if (ct % LINES_PER_PAGE == 0) {
- // give the user time to glance at the entries
- key = ui->WaitKey();
-
- if (key == KEY_POWER) {
- break;
- }
-
- if (key == KEY_VOLUMEUP) {
- // Go back by seeking to the beginning and dumping ct - n
- // lines. It's ugly, but this way we don't need to store
- // the previous offsets. The files we're dumping here aren't
- // expected to be very large.
- int i;
-
- ct -= 2 * LINES_PER_PAGE;
- if (ct < 0) {
- ct = 0;
- }
- fseek(fp, 0, SEEK_SET);
- for (i = 0; i < ct; i++) {
- fgets(line, sizeof(line), fp);
- }
- ui->Print("^^^^^^^^^^\n");
- }
- }
+// Return true on success.
+static bool wipe_data(int should_confirm, Device* device) {
+ if (should_confirm && !yes_no(device, "Wipe all user data?", " THIS CAN NOT BE UNDONE!")) {
+ return false;
}
- // If the user didn't abort, then give the user time to glance at
- // the end of the log, sorry, no rewind here
- if (key != KEY_POWER) {
- ui->Print("\n--END-- (press any key)\n");
- ui->WaitKey();
+ modified_flash = true;
+
+ ui->Print("\n-- Wiping data...\n");
+ bool success =
+ device->PreWipeData() &&
+ erase_volume("/data") &&
+ erase_volume("/cache") &&
+ device->PostWipeData();
+ ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
+ return success;
+}
+
+// Return true on success.
+static bool wipe_cache(bool should_confirm, Device* device) {
+ if (should_confirm && !yes_no(device, "Wipe cache?", " THIS CAN NOT BE UNDONE!")) {
+ return false;
}
- redirect_stdio(TEMPORARY_LOG_FILE);
- fclose(fp);
+ modified_flash = true;
+
+ ui->Print("\n-- Wiping cache...\n");
+ bool success = erase_volume("/cache");
+ ui->Print("Cache wipe %s.\n", success ? "complete" : "failed");
+ return success;
}
static void choose_recovery_file(Device* device) {
- unsigned int i;
- unsigned int n;
- static const char** title_headers = NULL;
- char *filename;
- const char* headers[] = { "Select file to view",
- "",
- NULL };
- // "Go back" + LAST_KMSG_FILE + KEEP_LOG_COUNT + terminating NULL entry
- char* entries[KEEP_LOG_COUNT + 3];
+ // "Back" + KEEP_LOG_COUNT * 2 + terminating nullptr entry
+ char* entries[1 + KEEP_LOG_COUNT * 2 + 1];
memset(entries, 0, sizeof(entries));
- n = 0;
- entries[n++] = strdup("Go back");
-
- // Add kernel kmsg file if available
- if ((ensure_path_mounted(LAST_KMSG_FILE) == 0) && (access(LAST_KMSG_FILE, R_OK) == 0)) {
- entries[n++] = strdup(LAST_KMSG_FILE);
- }
+ unsigned int n = 0;
// Add LAST_LOG_FILE + LAST_LOG_FILE.x
- for (i = 0; i < KEEP_LOG_COUNT; i++) {
- char *filename;
- if (asprintf(&filename, (i==0) ? LAST_LOG_FILE : (LAST_LOG_FILE ".%d"), i) == -1) {
+ // 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) {
// memory allocation failure - return early. Should never happen.
return;
}
- if ((ensure_path_mounted(filename) != 0) || (access(filename, R_OK) == -1)) {
- free(filename);
- entries[n++] = NULL;
- break;
+ if ((ensure_path_mounted(log_file) != 0) || (access(log_file, R_OK) == -1)) {
+ free(log_file);
+ } else {
+ entries[n++] = log_file;
}
- entries[n++] = filename;
+
+ char* kmsg_file;
+ if (asprintf(&kmsg_file, (i == 0) ? "%s" : "%s.%d", LAST_KMSG_FILE, i) == -1) {
+ // memory allocation failure - return early. Should never happen.
+ return;
+ }
+ if ((ensure_path_mounted(kmsg_file) != 0) || (access(kmsg_file, R_OK) == -1)) {
+ free(kmsg_file);
+ } else {
+ entries[n++] = kmsg_file;
+ }
}
- title_headers = prepend_title((const char**)headers);
+ entries[n++] = strdup("Back");
- while(1) {
- int chosen_item = get_menu_selection(title_headers, entries, 1, 0, device);
- if (chosen_item == 0) break;
- file_to_ui(entries[chosen_item]);
+ const char* headers[] = { "Select file to view", nullptr };
+
+ while (true) {
+ 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 (i = 0; i < (sizeof(entries) / sizeof(*entries)); i++) {
+ for (size_t i = 0; i < (sizeof(entries) / sizeof(*entries)); i++) {
free(entries[i]);
}
}
+static int apply_from_sdcard(Device* device, bool* wipe_cache) {
+ modified_flash = true;
+
+ if (ensure_path_mounted(SDCARD_ROOT) != 0) {
+ ui->Print("\n-- Couldn't mount %s.\n", SDCARD_ROOT);
+ return INSTALL_ERROR;
+ }
+
+ char* path = browse_directory(SDCARD_ROOT, device);
+ if (path == NULL) {
+ ui->Print("\n-- No package file selected.\n");
+ 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);
+
+ finish_sdcard_fuse(token);
+ ensure_path_unmounted(SDCARD_ROOT);
+ return status;
+}
+
// Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION
// means to take the default, which is to reboot or shutdown depending
// on if the --shutdown_after flag was passed to recovery.
static Device::BuiltinAction
prompt_and_wait(Device* device, int status) {
- const char* const* headers = prepend_title(device->GetMenuHeaders());
-
for (;;) {
finish_recovery(NULL);
switch (status) {
@@ -841,14 +793,14 @@
}
ui->SetProgressType(RecoveryUI::EMPTY);
- int chosen_item = get_menu_selection(headers, device->GetMenuItems(), 0, 0, device);
+ int chosen_item = get_menu_selection(nullptr, device->GetMenuItems(), 0, 0, device);
// device-specific code may take some action here. It may
// return one of the core actions handled in the switch
// statement below.
Device::BuiltinAction chosen_action = device->InvokeMenuItem(chosen_item);
- int wipe_cache = 0;
+ bool should_wipe_cache = false;
switch (chosen_action) {
case Device::NO_ACTION:
break;
@@ -864,63 +816,26 @@
break;
case Device::WIPE_CACHE:
- ui->Print("\n-- Wiping cache...\n");
- erase_volume("/cache");
- ui->Print("Cache wipe complete.\n");
+ wipe_cache(ui->IsTextVisible(), device);
if (!ui->IsTextVisible()) return Device::NO_ACTION;
break;
- case Device::APPLY_EXT: {
- ensure_path_mounted(SDCARD_ROOT);
- char* path = browse_directory(SDCARD_ROOT, device);
- if (path == NULL) {
- ui->Print("\n-- No package file selected.\n", path);
- break;
- }
-
- 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);
-
- finish_sdcard_fuse(token);
- ensure_path_unmounted(SDCARD_ROOT);
-
- if (status == INSTALL_SUCCESS && wipe_cache) {
- ui->Print("\n-- Wiping cache (at package request)...\n");
- if (erase_volume("/cache")) {
- ui->Print("Cache wipe failed.\n");
- } else {
- ui->Print("Cache wipe complete.\n");
- }
- }
-
- if (status >= 0) {
- if (status != INSTALL_SUCCESS) {
- ui->SetBackground(RecoveryUI::ERROR);
- ui->Print("Installation aborted.\n");
- } else if (!ui->IsTextVisible()) {
- return Device::NO_ACTION; // reboot if logs aren't visible
- } else {
- ui->Print("\nInstall from sdcard complete.\n");
- }
- }
- break;
- }
-
- case Device::APPLY_CACHE:
- ui->Print("\nAPPLY_CACHE is deprecated.\n");
- break;
-
- case Device::READ_RECOVERY_LASTLOG:
- choose_recovery_file(device);
- break;
-
case Device::APPLY_ADB_SIDELOAD:
- status = apply_from_adb(ui, &wipe_cache, TEMPORARY_INSTALL_FILE);
- if (status >= 0) {
+ case Device::APPLY_SDCARD:
+ {
+ bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD);
+ if (adb) {
+ status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE);
+ } else {
+ status = apply_from_sdcard(device, &should_wipe_cache);
+ }
+
+ if (status == INSTALL_SUCCESS && should_wipe_cache) {
+ if (!wipe_cache(false, device)) {
+ status = INSTALL_ERROR;
+ }
+ }
+
if (status != INSTALL_SUCCESS) {
ui->SetBackground(RecoveryUI::ERROR);
ui->Print("Installation aborted.\n");
@@ -928,10 +843,20 @@
} else if (!ui->IsTextVisible()) {
return Device::NO_ACTION; // reboot if logs aren't visible
} else {
- ui->Print("\nInstall from ADB complete.\n");
+ ui->Print("\nInstall from %s complete.\n", adb ? "ADB" : "SD card");
}
}
break;
+
+ case Device::VIEW_RECOVERY_LOGS:
+ choose_recovery_file(device);
+ break;
+
+ case Device::MOUNT_SYSTEM:
+ if (ensure_path_mounted("/system") != -1) {
+ ui->Print("Mounted /system.\n");
+ }
+ break;
}
}
}
@@ -992,31 +917,35 @@
// 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();
+ adb_main(0, DEFAULT_ADB_PORT);
return 0;
}
printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start));
load_volume_table();
- ensure_path_mounted(LAST_LOG_FILE);
- rotate_last_logs(KEEP_LOG_COUNT);
get_args(&argc, &argv);
const char *send_intent = NULL;
const char *update_package = NULL;
- int wipe_data = 0, wipe_cache = 0, show_text = 0;
+ bool should_wipe_data = false;
+ bool should_wipe_cache = false;
+ bool show_text = false;
+ bool sideload = false;
+ bool sideload_auto_reboot = false;
bool just_exit = false;
bool shutdown_after = false;
int arg;
while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
switch (arg) {
- case 's': send_intent = optarg; break;
+ case 'i': send_intent = optarg; break;
case 'u': update_package = optarg; break;
- case 'w': wipe_data = wipe_cache = 1; break;
- case 'c': wipe_cache = 1; break;
- case 't': show_text = 1; break;
+ case 'w': should_wipe_data = true; break;
+ case 'c': should_wipe_cache = true; break;
+ case 't': show_text = true; break;
+ case 's': sideload = true; break;
+ case 'a': sideload = true; sideload_auto_reboot = true; break;
case 'x': just_exit = true; break;
case 'l': locale = optarg; break;
case 'g': {
@@ -1092,52 +1021,78 @@
printf("\n");
property_list(print_property, NULL);
- property_get("ro.build.display.id", recovery_version, "");
printf("\n");
+ ui->Print("Supported API: %d\n", RECOVERY_API_VERSION);
+
int status = INSTALL_SUCCESS;
if (update_package != NULL) {
- status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE, true);
- if (status == INSTALL_SUCCESS && wipe_cache) {
- if (erase_volume("/cache")) {
- LOGE("Cache wipe (requested by package) failed.");
- }
+ 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");
- ui->Print("OTA failed! Please power off the device to keep it in this state and file a bug report!\n");
// 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.
- char buffer[PROPERTY_VALUE_MAX+1];
- property_get("ro.build.fingerprint", buffer, "");
- if (strstr(buffer, ":userdebug/") || strstr(buffer, ":eng/")) {
+ if (is_ro_debuggable()) {
ui->ShowText(true);
}
}
- } else if (wipe_data) {
- if (device->WipeData()) status = INSTALL_ERROR;
- if (erase_volume("/data")) status = INSTALL_ERROR;
- if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
- if (status != INSTALL_SUCCESS) ui->Print("Data wipe failed.\n");
- } else if (wipe_cache) {
- if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
- if (status != INSTALL_SUCCESS) ui->Print("Cache wipe failed.\n");
+ } else if (should_wipe_data) {
+ if (!wipe_data(false, device)) {
+ status = INSTALL_ERROR;
+ }
+ } else if (should_wipe_cache) {
+ if (!wipe_cache(false, device)) {
+ status = INSTALL_ERROR;
+ }
+ } else if (sideload) {
+ // 'adb reboot sideload' acts the same as user presses key combinations
+ // to enter the sideload mode. When 'sideload-auto-reboot' is used, text
+ // display will NOT be turned on by default. And it will reboot after
+ // sideload finishes even if there are errors. Unless one turns on the
+ // text display during the installation. This is to enable automated
+ // testing.
+ if (!sideload_auto_reboot) {
+ ui->ShowText(true);
+ }
+ status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE);
+ if (status == INSTALL_SUCCESS && should_wipe_cache) {
+ if (!wipe_cache(false, device)) {
+ status = INSTALL_ERROR;
+ }
+ }
+ ui->Print("\nInstall from ADB complete (status: %d).\n", status);
+ if (sideload_auto_reboot) {
+ ui->Print("Rebooting automatically.\n");
+ }
} else if (!just_exit) {
status = INSTALL_NONE; // No command specified
ui->SetBackground(RecoveryUI::NO_COMMAND);
+
+ // http://b/17489952
+ // If this is an eng or userdebug build, automatically turn on the
+ // text display if no command is specified.
+ if (is_ro_debuggable()) {
+ ui->ShowText(true);
+ }
}
- if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) {
+ if (!sideload_auto_reboot && (status == INSTALL_ERROR || status == INSTALL_CORRUPT)) {
copy_logs();
ui->SetBackground(RecoveryUI::ERROR);
}
+
Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
- if (status != INSTALL_SUCCESS || ui->IsTextVisible()) {
+ if ((status != INSTALL_SUCCESS && !sideload_auto_reboot) || ui->IsTextVisible()) {
Device::BuiltinAction temp = prompt_and_wait(device, status);
- if (temp != Device::NO_ACTION) after = temp;
+ if (temp != Device::NO_ACTION) {
+ after = temp;
+ }
}
// Save logs and clean up before rebooting or shutting down.
diff --git a/roots.cpp b/roots.cpp
index ee14016..2bd457e 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -111,9 +111,10 @@
}
return mtd_mount_partition(partition, v->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,
- MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
+ v->flags, v->fs_options);
if (result == 0) return 0;
LOGE("failed to mount %s (%s)\n", v->mount_point, strerror(errno));
diff --git a/screen_ui.cpp b/screen_ui.cpp
index 03ef049..ff95915 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -28,6 +28,10 @@
#include <time.h>
#include <unistd.h>
+#include <vector>
+
+#include "base/strings.h"
+#include "cutils/properties.h"
#include "common.h"
#include "device.h"
#include "minui/minui.h"
@@ -37,67 +41,60 @@
static int char_width;
static int char_height;
-// There's only (at most) one of these objects, and global callbacks
-// (for pthread_create, and the input event system) need to find it,
-// so use a global variable.
-static ScreenRecoveryUI* self = NULL;
-
// Return the current time as a double (including fractions of a second).
static double now() {
struct timeval tv;
- gettimeofday(&tv, NULL);
+ gettimeofday(&tv, nullptr);
return tv.tv_sec + tv.tv_usec / 1000000.0;
}
ScreenRecoveryUI::ScreenRecoveryUI() :
currentIcon(NONE),
installingFrame(0),
- locale(NULL),
+ locale(nullptr),
rtl_locale(false),
progressBarType(EMPTY),
progressScopeStart(0),
progressScopeSize(0),
progress(0),
pagesIdentical(false),
- text_cols(0),
- text_rows(0),
- text_col(0),
- text_row(0),
- text_top(0),
+ text_cols_(0),
+ text_rows_(0),
+ text_(nullptr),
+ text_col_(0),
+ text_row_(0),
+ text_top_(0),
show_text(false),
show_text_ever(false),
+ menu_(nullptr),
show_menu(false),
- menu_top(0),
menu_items(0),
menu_sel(0),
+ file_viewer_text_(nullptr),
animation_fps(20),
installing_frames(-1),
stage(-1),
max_stage(-1) {
- for (int i = 0; i < 5; i++)
- backgroundIcon[i] = NULL;
-
- memset(text, 0, sizeof(text));
-
- pthread_mutex_init(&updateMutex, NULL);
- self = this;
+ for (int i = 0; i < 5; i++) {
+ backgroundIcon[i] = nullptr;
+ }
+ pthread_mutex_init(&updateMutex, nullptr);
}
// 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(Icon icon) {
pagesIdentical = false;
gr_color(0, 0, 0, 255);
gr_clear();
if (icon) {
- gr_surface surface = backgroundIcon[icon];
+ GRSurface* surface = backgroundIcon[icon];
if (icon == INSTALLING_UPDATE || icon == ERASING) {
surface = installation[installingFrame];
}
- gr_surface text_surface = backgroundText[icon];
+ GRSurface* text_surface = backgroundText[icon];
int iconWidth = gr_get_width(surface);
int iconHeight = gr_get_height(surface);
@@ -132,12 +129,11 @@
// Draw the progress bar (if any) on the screen. Does not flip pages.
// Should only be called with updateMutex locked.
-void ScreenRecoveryUI::draw_progress_locked()
-{
+void ScreenRecoveryUI::draw_progress_locked() {
if (currentIcon == ERROR) return;
if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
- gr_surface icon = installation[installingFrame];
+ GRSurface* icon = installation[installingFrame];
gr_blit(icon, 0, 0, gr_get_width(icon), gr_get_height(icon), iconX, iconY);
}
@@ -180,6 +176,9 @@
void ScreenRecoveryUI::SetColor(UIElement e) {
switch (e) {
+ case INFO:
+ gr_color(249, 194, 0, 255);
+ break;
case HEADER:
gr_color(247, 0, 6, 255);
break;
@@ -187,11 +186,14 @@
case MENU_SEL_BG:
gr_color(0, 106, 157, 255);
break;
+ case MENU_SEL_BG_ACTIVE:
+ gr_color(0, 156, 100, 255);
+ break;
case MENU_SEL_FG:
gr_color(255, 255, 255, 255);
break;
case LOG:
- gr_color(249, 194, 0, 255);
+ gr_color(196, 196, 196, 255);
break;
case TEXT_FILL:
gr_color(0, 0, 0, 160);
@@ -202,10 +204,38 @@
}
}
+void ScreenRecoveryUI::DrawHorizontalRule(int* y) {
+ SetColor(MENU);
+ *y += 4;
+ gr_fill(0, *y, gr_fb_width(), *y + 2);
+ *y += 4;
+}
+
+void ScreenRecoveryUI::DrawTextLine(int* y, const char* line, bool bold) {
+ gr_text(4, *y, line, bold);
+ *y += char_height + 4;
+}
+
+void ScreenRecoveryUI::DrawTextLines(int* y, const char* const* lines) {
+ for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) {
+ DrawTextLine(y, lines[i], false);
+ }
+}
+
+static const char* REGULAR_HELP[] = {
+ "Use volume up/down and power.",
+ NULL
+};
+
+static const char* LONG_PRESS_HELP[] = {
+ "Any button cycles highlight.",
+ "Long-press activates.",
+ NULL
+};
+
// Redraw everything on the screen. Does not flip pages.
// Should only be called with updateMutex locked.
-void ScreenRecoveryUI::draw_screen_locked()
-{
+void ScreenRecoveryUI::draw_screen_locked() {
if (!show_text) {
draw_background_locked(currentIcon);
draw_progress_locked();
@@ -214,62 +244,66 @@
gr_clear();
int y = 0;
- int i = 0;
if (show_menu) {
+ char recovery_fingerprint[PROPERTY_VALUE_MAX];
+ property_get("ro.bootimage.build.fingerprint", recovery_fingerprint, "");
+
+ SetColor(INFO);
+ DrawTextLine(&y, "Android Recovery", true);
+ for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) {
+ DrawTextLine(&y, chunk.c_str(), false);
+ }
+ DrawTextLines(&y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP);
+
SetColor(HEADER);
+ DrawTextLines(&y, menu_headers_);
- for (; i < menu_top + menu_items; ++i) {
- if (i == menu_top) SetColor(MENU);
-
- if (i == menu_top + menu_sel) {
- // draw the highlight bar
- SetColor(MENU_SEL_BG);
- gr_fill(0, y-2, gr_fb_width(), y+char_height+2);
- // white text of selected item
+ SetColor(MENU);
+ DrawHorizontalRule(&y);
+ y += 4;
+ for (int i = 0; i < menu_items; ++i) {
+ 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);
+ // Bold white text for the selected item.
SetColor(MENU_SEL_FG);
- if (menu[i][0]) gr_text(4, y, menu[i], 1);
+ gr_text(4, y, menu_[i], true);
SetColor(MENU);
} else {
- if (menu[i][0]) gr_text(4, y, menu[i], i < menu_top);
+ gr_text(4, y, menu_[i], false);
}
- y += char_height+4;
+ y += char_height + 4;
}
- SetColor(MENU);
- y += 4;
- gr_fill(0, y, gr_fb_width(), y+2);
- y += 4;
- ++i;
+ DrawHorizontalRule(&y);
}
- SetColor(LOG);
-
// display from the bottom up, until we hit the top of the
// screen, the bottom of the menu, or we've displayed the
// entire text buffer.
- int ty;
- int row = (text_top+text_rows-1) % text_rows;
- for (int ty = gr_fb_height() - char_height, count = 0;
- ty > y+2 && count < text_rows;
+ SetColor(LOG);
+ int row = (text_top_ + text_rows_ - 1) % text_rows_;
+ size_t count = 0;
+ for (int ty = gr_fb_height() - char_height;
+ ty >= y && count < text_rows_;
ty -= char_height, ++count) {
- gr_text(4, ty, text[row], 0);
+ gr_text(0, ty, text_[row], false);
--row;
- if (row < 0) row = text_rows-1;
+ if (row < 0) row = text_rows_ - 1;
}
}
}
// Redraw everything on the screen and flip the screen (make it visible).
// Should only be called with updateMutex locked.
-void ScreenRecoveryUI::update_screen_locked()
-{
+void ScreenRecoveryUI::update_screen_locked() {
draw_screen_locked();
gr_flip();
}
// Updates only the progress bar, if possible, otherwise redraws the screen.
// Should only be called with updateMutex locked.
-void ScreenRecoveryUI::update_progress_locked()
-{
+void ScreenRecoveryUI::update_progress_locked() {
if (show_text || !pagesIdentical) {
draw_screen_locked(); // Must redraw the whole screen
pagesIdentical = true;
@@ -280,14 +314,14 @@
}
// Keeps the progress bar updated, even when the process is otherwise busy.
-void* ScreenRecoveryUI::progress_thread(void *cookie) {
- self->progress_loop();
- return NULL;
+void* ScreenRecoveryUI::ProgressThreadStartRoutine(void* data) {
+ reinterpret_cast<ScreenRecoveryUI*>(data)->ProgressThreadLoop();
+ return nullptr;
}
-void ScreenRecoveryUI::progress_loop() {
+void ScreenRecoveryUI::ProgressThreadLoop() {
double interval = 1.0 / animation_fps;
- for (;;) {
+ while (true) {
double start = now();
pthread_mutex_lock(&updateMutex);
@@ -324,44 +358,53 @@
}
}
-void ScreenRecoveryUI::LoadBitmap(const char* filename, gr_surface* surface) {
+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, gr_surface** surface) {
+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);
}
}
-void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, gr_surface* surface) {
+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);
}
}
-void ScreenRecoveryUI::Init()
-{
+static char** Alloc2d(size_t rows, size_t cols) {
+ char** result = new char*[rows];
+ for (size_t i = 0; i < rows; ++i) {
+ result[i] = new char[cols];
+ memset(result[i], 0, cols);
+ }
+ return result;
+}
+
+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;
- text_col = text_row = 0;
- text_rows = gr_fb_height() / char_height;
- if (text_rows > kMaxRows) text_rows = kMaxRows;
- text_top = 1;
+ text_ = Alloc2d(text_rows_, text_cols_ + 1);
+ file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
+ menu_ = Alloc2d(text_rows_, text_cols_ + 1);
- text_cols = gr_fb_width() / char_width;
- if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1;
+ text_col_ = text_row_ = 0;
+ text_top_ = 1;
- backgroundIcon[NONE] = NULL;
+ backgroundIcon[NONE] = nullptr;
LoadBitmapArray("icon_installing", &installing_frames, &installation);
- backgroundIcon[INSTALLING_UPDATE] = installing_frames ? installation[0] : NULL;
+ backgroundIcon[INSTALLING_UPDATE] = installing_frames ? installation[0] : nullptr;
backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
LoadBitmap("icon_error", &backgroundIcon[ERROR]);
backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
@@ -376,7 +419,7 @@
LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]);
LoadLocalizedBitmap("error_text", &backgroundText[ERROR]);
- pthread_create(&progress_t, NULL, progress_thread, NULL);
+ pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this);
RecoveryUI::Init();
}
@@ -403,12 +446,11 @@
}
free(lang);
} else {
- new_locale = NULL;
+ new_locale = nullptr;
}
}
-void ScreenRecoveryUI::SetBackground(Icon icon)
-{
+void ScreenRecoveryUI::SetBackground(Icon icon) {
pthread_mutex_lock(&updateMutex);
currentIcon = icon;
@@ -417,8 +459,7 @@
pthread_mutex_unlock(&updateMutex);
}
-void ScreenRecoveryUI::SetProgressType(ProgressType type)
-{
+void ScreenRecoveryUI::SetProgressType(ProgressType type) {
pthread_mutex_lock(&updateMutex);
if (progressBarType != type) {
progressBarType = type;
@@ -430,8 +471,7 @@
pthread_mutex_unlock(&updateMutex);
}
-void ScreenRecoveryUI::ShowProgress(float portion, float seconds)
-{
+void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
pthread_mutex_lock(&updateMutex);
progressBarType = DETERMINATE;
progressScopeStart += progressScopeSize;
@@ -443,8 +483,7 @@
pthread_mutex_unlock(&updateMutex);
}
-void ScreenRecoveryUI::SetProgress(float fraction)
-{
+void ScreenRecoveryUI::SetProgress(float fraction) {
pthread_mutex_lock(&updateMutex);
if (fraction < 0.0) fraction = 0.0;
if (fraction > 1.0) fraction = 1.0;
@@ -467,8 +506,7 @@
pthread_mutex_unlock(&updateMutex);
}
-void ScreenRecoveryUI::Print(const char *fmt, ...)
-{
+void ScreenRecoveryUI::Print(const char *fmt, ...) {
char buf[256];
va_list ap;
va_start(ap, fmt);
@@ -477,43 +515,133 @@
fputs(buf, stdout);
- // This can get called before ui_init(), so be careful.
pthread_mutex_lock(&updateMutex);
- if (text_rows > 0 && text_cols > 0) {
- char *ptr;
- for (ptr = buf; *ptr != '\0'; ++ptr) {
- if (*ptr == '\n' || text_col >= text_cols) {
- text[text_row][text_col] = '\0';
- text_col = 0;
- text_row = (text_row + 1) % text_rows;
- if (text_row == text_top) text_top = (text_top + 1) % text_rows;
+ if (text_rows_ > 0 && text_cols_ > 0) {
+ for (const char* ptr = buf; *ptr != '\0'; ++ptr) {
+ if (*ptr == '\n' || text_col_ >= text_cols_) {
+ text_[text_row_][text_col_] = '\0';
+ text_col_ = 0;
+ text_row_ = (text_row_ + 1) % text_rows_;
+ if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
}
- if (*ptr != '\n') text[text_row][text_col++] = *ptr;
+ if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
}
- text[text_row][text_col] = '\0';
+ text_[text_row_][text_col_] = '\0';
update_screen_locked();
}
pthread_mutex_unlock(&updateMutex);
}
+void ScreenRecoveryUI::PutChar(char ch) {
+ pthread_mutex_lock(&updateMutex);
+ if (ch != '\n') text_[text_row_][text_col_++] = ch;
+ if (ch == '\n' || text_col_ >= text_cols_) {
+ text_col_ = 0;
+ ++text_row_;
+
+ if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
+ }
+ pthread_mutex_unlock(&updateMutex);
+}
+
+void ScreenRecoveryUI::ClearText() {
+ pthread_mutex_lock(&updateMutex);
+ text_col_ = 0;
+ text_row_ = 0;
+ text_top_ = 1;
+ for (size_t i = 0; i < text_rows_; ++i) {
+ memset(text_[i], 0, text_cols_ + 1);
+ }
+ pthread_mutex_unlock(&updateMutex);
+}
+
+void ScreenRecoveryUI::ShowFile(FILE* fp) {
+ std::vector<long> offsets;
+ offsets.push_back(ftell(fp));
+ ClearText();
+
+ struct stat sb;
+ fstat(fileno(fp), &sb);
+
+ bool show_prompt = false;
+ while (true) {
+ if (show_prompt) {
+ Print("--(%d%% of %d bytes)--",
+ static_cast<int>(100 * (double(ftell(fp)) / double(sb.st_size))),
+ static_cast<int>(sb.st_size));
+ Redraw();
+ while (show_prompt) {
+ show_prompt = false;
+ int key = WaitKey();
+ if (key == KEY_POWER || key == KEY_ENTER) {
+ return;
+ } else if (key == KEY_UP || key == KEY_VOLUMEUP) {
+ if (offsets.size() <= 1) {
+ show_prompt = true;
+ } else {
+ offsets.pop_back();
+ fseek(fp, offsets.back(), SEEK_SET);
+ }
+ } else {
+ if (feof(fp)) {
+ return;
+ }
+ offsets.push_back(ftell(fp));
+ }
+ }
+ ClearText();
+ }
+
+ int ch = getc(fp);
+ if (ch == EOF) {
+ while (text_row_ < text_rows_ - 1) PutChar('\n');
+ show_prompt = true;
+ } else {
+ PutChar(ch);
+ if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
+ show_prompt = true;
+ }
+ }
+ }
+}
+
+void ScreenRecoveryUI::ShowFile(const char* filename) {
+ FILE* fp = fopen_path(filename, "re");
+ if (fp == nullptr) {
+ Print(" Unable to open %s: %s\n", filename, strerror(errno));
+ return;
+ }
+
+ char** old_text = text_;
+ size_t old_text_col = text_col_;
+ size_t old_text_row = text_row_;
+ size_t old_text_top = text_top_;
+
+ // Swap in the alternate screen and clear it.
+ text_ = file_viewer_text_;
+ ClearText();
+
+ ShowFile(fp);
+ fclose(fp);
+
+ text_ = old_text;
+ text_col_ = old_text_col;
+ text_row_ = old_text_row;
+ text_top_ = old_text_top;
+}
+
void ScreenRecoveryUI::StartMenu(const char* const * headers, const char* const * items,
int initial_selection) {
- int i;
pthread_mutex_lock(&updateMutex);
- if (text_rows > 0 && text_cols > 0) {
- for (i = 0; i < text_rows; ++i) {
- if (headers[i] == NULL) break;
- strncpy(menu[i], headers[i], text_cols-1);
- menu[i][text_cols-1] = '\0';
+ if (text_rows_ > 0 && text_cols_ > 0) {
+ menu_headers_ = headers;
+ size_t i = 0;
+ for (; i < text_rows_ && items[i] != nullptr; ++i) {
+ strncpy(menu_[i], items[i], text_cols_ - 1);
+ menu_[i][text_cols_ - 1] = '\0';
}
- menu_top = i;
- for (; i < text_rows; ++i) {
- if (items[i-menu_top] == NULL) break;
- strncpy(menu[i], items[i-menu_top], text_cols-1);
- menu[i][text_cols-1] = '\0';
- }
- menu_items = i - menu_top;
- show_menu = 1;
+ menu_items = i;
+ show_menu = true;
menu_sel = initial_selection;
update_screen_locked();
}
@@ -521,13 +649,15 @@
}
int ScreenRecoveryUI::SelectMenu(int sel) {
- int old_sel;
pthread_mutex_lock(&updateMutex);
- if (show_menu > 0) {
- old_sel = menu_sel;
+ if (show_menu) {
+ int old_sel = menu_sel;
menu_sel = sel;
- if (menu_sel < 0) menu_sel = 0;
- if (menu_sel >= menu_items) menu_sel = menu_items-1;
+
+ // Wrap at top and bottom.
+ if (menu_sel < 0) menu_sel = menu_items - 1;
+ if (menu_sel >= menu_items) menu_sel = 0;
+
sel = menu_sel;
if (menu_sel != old_sel) update_screen_locked();
}
@@ -536,43 +666,44 @@
}
void ScreenRecoveryUI::EndMenu() {
- int i;
pthread_mutex_lock(&updateMutex);
- if (show_menu > 0 && text_rows > 0 && text_cols > 0) {
- show_menu = 0;
+ if (show_menu && text_rows_ > 0 && text_cols_ > 0) {
+ show_menu = false;
update_screen_locked();
}
pthread_mutex_unlock(&updateMutex);
}
-bool ScreenRecoveryUI::IsTextVisible()
-{
+bool ScreenRecoveryUI::IsTextVisible() {
pthread_mutex_lock(&updateMutex);
int visible = show_text;
pthread_mutex_unlock(&updateMutex);
return visible;
}
-bool ScreenRecoveryUI::WasTextEverVisible()
-{
+bool ScreenRecoveryUI::WasTextEverVisible() {
pthread_mutex_lock(&updateMutex);
int ever_visible = show_text_ever;
pthread_mutex_unlock(&updateMutex);
return ever_visible;
}
-void ScreenRecoveryUI::ShowText(bool visible)
-{
+void ScreenRecoveryUI::ShowText(bool visible) {
pthread_mutex_lock(&updateMutex);
show_text = visible;
- if (show_text) show_text_ever = 1;
+ if (show_text) show_text_ever = true;
update_screen_locked();
pthread_mutex_unlock(&updateMutex);
}
-void ScreenRecoveryUI::Redraw()
-{
+void ScreenRecoveryUI::Redraw() {
pthread_mutex_lock(&updateMutex);
update_screen_locked();
pthread_mutex_unlock(&updateMutex);
}
+
+void ScreenRecoveryUI::KeyLongPress(int) {
+ // Redraw so that if we're in the menu, the highlight
+ // will change color to indicate a successful long press.
+ Redraw();
+}
diff --git a/screen_ui.h b/screen_ui.h
index 01a33bf..ea05bf1 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -18,6 +18,7 @@
#define RECOVERY_SCREEN_UI_H
#include <pthread.h>
+#include <stdio.h>
#include "ui.h"
#include "minui/minui.h"
@@ -47,18 +48,23 @@
bool WasTextEverVisible();
// printing messages
- void Print(const char* fmt, ...); // __attribute__((format(printf, 1, 2)));
+ void Print(const char* fmt, ...) __printflike(2, 3);
+ void ShowFile(const char* filename);
// menu display
void StartMenu(const char* const * headers, const char* const * items,
- int initial_selection);
+ int initial_selection);
int SelectMenu(int sel);
void EndMenu();
+ void KeyLongPress(int);
+
void Redraw();
- enum UIElement { HEADER, MENU, MENU_SEL_BG, MENU_SEL_FG, LOG, TEXT_FILL };
- virtual void SetColor(UIElement e);
+ enum UIElement {
+ HEADER, MENU, MENU_SEL_BG, MENU_SEL_BG_ACTIVE, MENU_SEL_FG, LOG, TEXT_FILL, INFO
+ };
+ void SetColor(UIElement e);
private:
Icon currentIcon;
@@ -67,43 +73,43 @@
bool rtl_locale;
pthread_mutex_t updateMutex;
- gr_surface backgroundIcon[5];
- gr_surface backgroundText[5];
- gr_surface *installation;
- gr_surface progressBarEmpty;
- gr_surface progressBarFill;
- gr_surface stageMarkerEmpty;
- gr_surface stageMarkerFill;
+ GRSurface* backgroundIcon[5];
+ GRSurface* backgroundText[5];
+ GRSurface** installation;
+ GRSurface* progressBarEmpty;
+ GRSurface* progressBarFill;
+ GRSurface* stageMarkerEmpty;
+ GRSurface* stageMarkerFill;
ProgressType progressBarType;
float progressScopeStart, progressScopeSize, progress;
double progressScopeTime, progressScopeDuration;
- // true when both graphics pages are the same (except for the
- // progress bar)
+ // true when both graphics pages are the same (except for the progress bar).
bool pagesIdentical;
- static const int kMaxCols = 96;
- static const int kMaxRows = 96;
+ size_t text_cols_, text_rows_;
- // Log text overlay, displayed when a magic key is pressed
- char text[kMaxRows][kMaxCols];
- int text_cols, text_rows;
- int text_col, text_row, text_top;
+ // Log text overlay, displayed when a magic key is pressed.
+ char** text_;
+ size_t text_col_, text_row_, text_top_;
+
bool show_text;
bool show_text_ever; // has show_text ever been true?
- char menu[kMaxRows][kMaxCols];
+ char** menu_;
+ const char* const* menu_headers_;
bool show_menu;
- int menu_top, menu_items, menu_sel;
+ int menu_items, menu_sel;
- pthread_t progress_t;
+ // An alternate text screen, swapped with 'text_' when we're viewing a log file.
+ char** file_viewer_text_;
+
+ pthread_t progress_thread_;
int animation_fps;
int installing_frames;
- protected:
- private:
int iconX, iconY;
@@ -114,12 +120,21 @@
void draw_screen_locked();
void update_screen_locked();
void update_progress_locked();
- static void* progress_thread(void* cookie);
- void progress_loop();
- void LoadBitmap(const char* filename, gr_surface* surface);
- void LoadBitmapArray(const char* filename, int* frames, gr_surface** surface);
- void LoadLocalizedBitmap(const char* filename, gr_surface* surface);
+ static void* ProgressThreadStartRoutine(void* data);
+ void ProgressThreadLoop();
+
+ void ShowFile(FILE*);
+ 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 LoadBitmap(const char* filename, GRSurface** surface);
+ void LoadBitmapArray(const char* filename, int* frames, GRSurface*** surface);
+ void LoadLocalizedBitmap(const char* filename, GRSurface** surface);
};
#endif // RECOVERY_UI_H
diff --git a/tests/Android.mk b/tests/Android.mk
index 4d99d52..02a272a 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -1,26 +1,25 @@
-# Build the unit tests.
+#
+# 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)
-
-# Build the unit tests.
-test_src_files := \
- asn1_decoder_test.cpp
-
-shared_libraries := \
- liblog \
- libcutils
-
-static_libraries := \
- libgtest \
- libgtest_main \
- libverifier
-
-$(foreach file,$(test_src_files), \
- $(eval include $(CLEAR_VARS)) \
- $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
- $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
- $(eval LOCAL_SRC_FILES := $(file)) \
- $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
- $(eval LOCAL_C_INCLUDES := $(LOCAL_PATH)/..) \
- $(eval include $(BUILD_NATIVE_TEST)) \
-)
\ No newline at end of file
+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)/..
+include $(BUILD_NATIVE_TEST)
diff --git a/tools/ota/check-lost+found.c b/tools/ota/check-lost+found.c
index da02f46..8ce12d3 100644
--- a/tools/ota/check-lost+found.c
+++ b/tools/ota/check-lost+found.c
@@ -26,6 +26,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
+#include <unistd.h>
#include "private/android_filesystem_config.h"
@@ -77,7 +78,7 @@
snprintf(fn, sizeof(fn), "%s/%s", kPartitions[i], "dirty");
fd = open(fn, O_WRONLY|O_CREAT, 0444);
if (fd >= 0) { // Don't sweat it if we can't write the file.
- write(fd, fn, sizeof(fn)); // write, you know, some data
+ TEMP_FAILURE_RETRY(write(fd, fn, sizeof(fn))); // write, you know, some data
close(fd);
unlink(fn);
}
diff --git a/ui.cpp b/ui.cpp
index c8f08cd..1a0b079 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -39,40 +39,59 @@
#define UI_WAIT_KEY_TIMEOUT_SEC 120
-// There's only (at most) one of these objects, and global callbacks
-// (for pthread_create, and the input event system) need to find it,
-// so use a global variable.
-static RecoveryUI* self = NULL;
-
-RecoveryUI::RecoveryUI() :
- key_queue_len(0),
- key_last_down(-1),
- key_long_press(false),
- key_down_count(0),
- enable_reboot(true),
- 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;
+RecoveryUI::RecoveryUI()
+ : key_queue_len(0),
+ key_last_down(-1),
+ key_long_press(false),
+ key_down_count(0),
+ enable_reboot(true),
+ consecutive_power_keys(0),
+ last_key(-1),
+ has_power_key(false),
+ has_up_key(false),
+ has_down_key(false) {
+ pthread_mutex_init(&key_queue_mutex, nullptr);
+ pthread_cond_init(&key_queue_cond, nullptr);
memset(key_pressed, 0, sizeof(key_pressed));
}
-void RecoveryUI::Init() {
- ev_init(input_callback, NULL);
- pthread_create(&input_t, NULL, input_thread, NULL);
+void RecoveryUI::OnKeyDetected(int key_code) {
+ if (key_code == KEY_POWER) {
+ has_power_key = true;
+ } else if (key_code == KEY_DOWN || key_code == KEY_VOLUMEDOWN) {
+ has_down_key = true;
+ } else if (key_code == KEY_UP || key_code == KEY_VOLUMEUP) {
+ has_up_key = true;
+ }
}
+int RecoveryUI::InputCallback(int fd, uint32_t epevents, void* data) {
+ return reinterpret_cast<RecoveryUI*>(data)->OnInputEvent(fd, epevents);
+}
-int RecoveryUI::input_callback(int fd, uint32_t epevents, void* data)
-{
+// Reads input events, handles special hot keys, and adds to the key queue.
+static void* InputThreadLoop(void*) {
+ while (true) {
+ if (!ev_wait(-1)) {
+ ev_dispatch();
+ }
+ }
+ return nullptr;
+}
+
+void RecoveryUI::Init() {
+ ev_init(InputCallback, this);
+
+ ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
+
+ pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr);
+}
+
+int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
struct input_event ev;
- int ret;
-
- ret = ev_get_input(fd, epevents, &ev);
- if (ret)
+ if (ev_get_input(fd, epevents, &ev) == -1) {
return -1;
+ }
if (ev.type == EV_SYN) {
return 0;
@@ -82,23 +101,24 @@
// the trackball. When it exceeds a threshold
// (positive or negative), fake an up/down
// key event.
- self->rel_sum += ev.value;
- if (self->rel_sum > 3) {
- self->process_key(KEY_DOWN, 1); // press down key
- self->process_key(KEY_DOWN, 0); // and release it
- self->rel_sum = 0;
- } else if (self->rel_sum < -3) {
- self->process_key(KEY_UP, 1); // press up key
- self->process_key(KEY_UP, 0); // and release it
- self->rel_sum = 0;
+ rel_sum += ev.value;
+ if (rel_sum > 3) {
+ ProcessKey(KEY_DOWN, 1); // press down key
+ ProcessKey(KEY_DOWN, 0); // and release it
+ rel_sum = 0;
+ } else if (rel_sum < -3) {
+ ProcessKey(KEY_UP, 1); // press up key
+ ProcessKey(KEY_UP, 0); // and release it
+ rel_sum = 0;
}
}
} else {
- self->rel_sum = 0;
+ rel_sum = 0;
}
- if (ev.type == EV_KEY && ev.code <= KEY_MAX)
- self->process_key(ev.code, ev.value);
+ if (ev.type == EV_KEY && ev.code <= KEY_MAX) {
+ ProcessKey(ev.code, ev.value);
+ }
return 0;
}
@@ -115,7 +135,7 @@
// a key is registered.
//
// updown == 1 for key down events; 0 for key up events
-void RecoveryUI::process_key(int key_code, int updown) {
+void RecoveryUI::ProcessKey(int key_code, int updown) {
bool register_key = false;
bool long_press = false;
bool reboot_enabled;
@@ -126,13 +146,13 @@
++key_down_count;
key_last_down = key_code;
key_long_press = false;
- pthread_t th;
key_timer_t* info = new key_timer_t;
info->ui = this;
info->key_code = key_code;
info->count = key_down_count;
- pthread_create(&th, NULL, &RecoveryUI::time_key_helper, info);
- pthread_detach(th);
+ pthread_t thread;
+ pthread_create(&thread, nullptr, &RecoveryUI::time_key_helper, info);
+ pthread_detach(thread);
} else {
if (key_last_down == key_code) {
long_press = key_long_press;
@@ -144,8 +164,7 @@
pthread_mutex_unlock(&key_queue_mutex);
if (register_key) {
- NextCheckKeyIsLong(long_press);
- switch (CheckKey(key_code)) {
+ switch (CheckKey(key_code, long_press)) {
case RecoveryUI::IGNORE:
break;
@@ -162,13 +181,6 @@
case RecoveryUI::ENQUEUE:
EnqueueKey(key_code);
break;
-
- case RecoveryUI::MOUNT_SYSTEM:
-#ifndef NO_RECOVERY_MOUNT
- ensure_path_mounted("/system");
- Print("Mounted /system.");
-#endif
- break;
}
}
}
@@ -177,7 +189,7 @@
key_timer_t* info = (key_timer_t*) cookie;
info->ui->time_key(info->key_code, info->count);
delete info;
- return NULL;
+ return nullptr;
}
void RecoveryUI::time_key(int key_code, int count) {
@@ -201,19 +213,7 @@
pthread_mutex_unlock(&key_queue_mutex);
}
-
-// Reads input events, handles special hot keys, and adds to the key queue.
-void* RecoveryUI::input_thread(void *cookie)
-{
- for (;;) {
- if (!ev_wait(-1))
- ev_dispatch();
- }
- return NULL;
-}
-
-int RecoveryUI::WaitKey()
-{
+int RecoveryUI::WaitKey() {
pthread_mutex_lock(&key_queue_mutex);
// Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is
@@ -221,17 +221,16 @@
do {
struct timeval now;
struct timespec timeout;
- gettimeofday(&now, NULL);
+ gettimeofday(&now, nullptr);
timeout.tv_sec = now.tv_sec;
timeout.tv_nsec = now.tv_usec * 1000;
timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC;
int rc = 0;
while (key_queue_len == 0 && rc != ETIMEDOUT) {
- rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex,
- &timeout);
+ rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex, &timeout);
}
- } while (usb_connected() && key_queue_len == 0);
+ } while (IsUsbConnected() && key_queue_len == 0);
int key = -1;
if (key_queue_len > 0) {
@@ -242,8 +241,7 @@
return key;
}
-// Return true if USB is connected.
-bool RecoveryUI::usb_connected() {
+bool RecoveryUI::IsUsbConnected() {
int fd = open("/sys/class/android_usb/android0/state", O_RDONLY);
if (fd < 0) {
printf("failed to open /sys/class/android_usb/android0/state: %s\n",
@@ -252,8 +250,8 @@
}
char buf;
- /* USB is connected if android_usb state is CONNECTED or CONFIGURED */
- int connected = (read(fd, &buf, 1) == 1) && (buf == 'C');
+ // USB is connected if android_usb state is CONNECTED or CONFIGURED.
+ int connected = (TEMP_FAILURE_RETRY(read(fd, &buf, 1)) == 1) && (buf == 'C');
if (close(fd) < 0) {
printf("failed to close /sys/class/android_usb/android0/state: %s\n",
strerror(errno));
@@ -261,31 +259,55 @@
return connected;
}
-bool RecoveryUI::IsKeyPressed(int key)
-{
+bool RecoveryUI::IsKeyPressed(int key) {
pthread_mutex_lock(&key_queue_mutex);
int pressed = key_pressed[key];
pthread_mutex_unlock(&key_queue_mutex);
return pressed;
}
+bool RecoveryUI::IsLongPress() {
+ pthread_mutex_lock(&key_queue_mutex);
+ bool result = key_long_press;
+ pthread_mutex_unlock(&key_queue_mutex);
+ return result;
+}
+
+bool RecoveryUI::HasThreeButtons() {
+ return has_power_key && has_up_key && has_down_key;
+}
+
void RecoveryUI::FlushKeys() {
pthread_mutex_lock(&key_queue_mutex);
key_queue_len = 0;
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) {
- if ((IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) || key == KEY_HOME) {
- return TOGGLE;
+RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) {
+ pthread_mutex_lock(&key_queue_mutex);
+ key_long_press = false;
+ pthread_mutex_unlock(&key_queue_mutex);
+
+ // If we have power and volume up keys, that chord is the signal to toggle the text display.
+ if (HasThreeButtons()) {
+ if (key == KEY_VOLUMEUP && IsKeyPressed(KEY_POWER)) {
+ return TOGGLE;
+ }
+ } else {
+ // Otherwise long press of any button toggles to the text display,
+ // and there's no way to toggle back (but that's pretty useless anyway).
+ if (is_long_press && !IsTextVisible()) {
+ return TOGGLE;
+ }
+
+ // Also, for button-limited devices, a long press is translated to KEY_ENTER.
+ if (is_long_press && IsTextVisible()) {
+ EnqueueKey(KEY_ENTER);
+ return IGNORE;
+ }
}
+ // Press power seven times in a row to reboot.
if (key == KEY_POWER) {
pthread_mutex_lock(&key_queue_mutex);
bool reboot_enabled = enable_reboot;
@@ -301,27 +323,11 @@
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;
+ return IsTextVisible() ? ENQUEUE : IGNORE;
}
-void RecoveryUI::NextCheckKeyIsLong(bool is_long_press) {
-}
-
-void RecoveryUI::KeyLongPress(int key) {
+void RecoveryUI::KeyLongPress(int) {
}
void RecoveryUI::SetEnableReboot(bool enabled) {
diff --git a/ui.h b/ui.h
index 31a8a7f..4dcaa0f 100644
--- a/ui.h
+++ b/ui.h
@@ -31,10 +31,10 @@
// Initialize the object; called before anything else.
virtual void Init();
// Show a stage indicator. Call immediately after Init().
- virtual void SetStage(int current, int max) { }
+ virtual void SetStage(int current, int max) = 0;
// After calling Init(), you can tell the UI what locale it is operating in.
- virtual void SetLocale(const char* locale) { }
+ virtual void SetLocale(const char* locale) = 0;
// Set the overall recovery state ("background image").
enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR };
@@ -63,34 +63,37 @@
// Write a message to the on-screen log (shown if the user has
// toggled on the text display).
- virtual void Print(const char* fmt, ...) = 0; // __attribute__((format(printf, 1, 2))) = 0;
+ virtual void Print(const char* fmt, ...) __printflike(2, 3) = 0;
+
+ virtual void ShowFile(const char* filename) = 0;
// --- key handling ---
- // Wait for keypress and return it. May return -1 after timeout.
+ // Wait for a key and return it. May return -1 after timeout.
virtual int WaitKey();
virtual bool IsKeyPressed(int key);
+ virtual bool IsLongPress();
+
+ // Returns true if you have the volume up/down and power trio typical
+ // of phones and tablets, false otherwise.
+ virtual bool HasThreeButtons();
// Erase any queued-up keys.
virtual void FlushKeys();
- // Called on each keypress, even while operations are in progress.
+ // Called on each key press, even while operations are in progress.
// 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, MOUNT_SYSTEM };
- virtual KeyAction CheckKey(int key);
-
- // Called immediately before each call to CheckKey(), tell you if
- // the key was long-pressed.
- virtual void NextCheckKeyIsLong(bool is_long_press);
+ enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE };
+ virtual KeyAction CheckKey(int key, bool is_long_press);
// Called when a key is held down long enough to have been a
// long-press (but before the key is released). This means that
// if the key is eventually registered (released without any other
- // keys being pressed in the meantime), NextCheckKeyIsLong() will
- // be called with "true".
+ // keys being pressed in the meantime), CheckKey will be called with
+ // 'is_long_press' true.
virtual void KeyLongPress(int key);
// Normally in recovery there's a key sequence that triggers
@@ -108,8 +111,8 @@
virtual void StartMenu(const char* const * headers, const char* const * items,
int initial_selection) = 0;
- // Set the menu highlight to the given index, and return it (capped to
- // the range [0..numitems).
+ // Set the menu highlight to the given index, wrapping if necessary.
+ // Returns the actual item selected.
virtual int SelectMenu(int sel) = 0;
// End menu mode, resetting the text overlay so that ui_print()
@@ -132,21 +135,27 @@
int rel_sum;
int consecutive_power_keys;
- int consecutive_alternate_keys;
int last_key;
- typedef struct {
+ bool has_power_key;
+ bool has_up_key;
+ bool has_down_key;
+
+ struct key_timer_t {
RecoveryUI* ui;
int key_code;
int count;
- } key_timer_t;
+ };
- pthread_t input_t;
+ pthread_t input_thread_;
- static void* input_thread(void* cookie);
- static int input_callback(int fd, uint32_t epevents, void* data);
- void process_key(int key_code, int updown);
- bool usb_connected();
+ void OnKeyDetected(int key_code);
+
+ static int InputCallback(int fd, uint32_t epevents, void* data);
+ int OnInputEvent(int fd, uint32_t epevents);
+ void ProcessKey(int key_code, int updown);
+
+ bool IsUsbConnected();
static void* time_key_helper(void* cookie);
void time_key(int key_code, int count);
diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk
index 878d275..c7d4d37 100644
--- a/uncrypt/Android.mk
+++ b/uncrypt/Android.mk
@@ -16,10 +16,10 @@
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := uncrypt.c
+LOCAL_SRC_FILES := uncrypt.cpp
LOCAL_MODULE := uncrypt
-LOCAL_STATIC_LIBRARIES := libfs_mgr liblog libcutils
+LOCAL_STATIC_LIBRARIES := libbase liblog libfs_mgr libcutils
include $(BUILD_EXECUTABLE)
diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.cpp
similarity index 62%
rename from uncrypt/uncrypt.c
rename to uncrypt/uncrypt.cpp
index 7fb0989..1db3013 100644
--- a/uncrypt/uncrypt.c
+++ b/uncrypt/uncrypt.cpp
@@ -39,36 +39,43 @@
// 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 <errno.h>
#include <fcntl.h>
#include <linux/fs.h>
+#include <stdarg.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 "uncrypt"
-#include <log/log.h>
+#include <base/file.h>
+#include <base/strings.h>
#include <cutils/properties.h>
#include <fs_mgr.h>
+#define LOG_TAG "uncrypt"
+#include <log/log.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 const std::string cache_block_map = "/cache/recovery/block.map";
+static const std::string status_file = "/cache/recovery/uncrypt_status";
+static const std::string uncrypt_file = "/cache/recovery/uncrypt_file";
static struct fstab* fstab = NULL;
-static int write_at_offset(unsigned char* buffer, size_t size,
- int wfd, off64_t offset)
-{
- lseek64(wfd, offset, SEEK_SET);
+static int write_at_offset(unsigned char* buffer, size_t size, int wfd, off64_t offset) {
+ if (TEMP_FAILURE_RETRY(lseek64(wfd, offset, SEEK_SET)) == -1) {
+ ALOGE("error seeking to offset %lld: %s\n", offset, strerror(errno));
+ return -1;
+ }
size_t written = 0;
while (written < size) {
- ssize_t wrote = write(wfd, buffer + written, size - written);
- if (wrote < 0) {
- ALOGE("error writing offset %lld: %s\n", offset, strerror(errno));
+ ssize_t wrote = TEMP_FAILURE_RETRY(write(wfd, buffer + written, size - written));
+ if (wrote == -1) {
+ ALOGE("error writing offset %lld: %s\n", (offset + written), strerror(errno));
return -1;
}
written += wrote;
@@ -76,8 +83,7 @@
return 0;
}
-void add_block_to_ranges(int** ranges, int* range_alloc, int* range_used, int new_block)
-{
+static 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.)
@@ -96,7 +102,7 @@
// 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));
+ *ranges = reinterpret_cast<int*>(realloc(*ranges, *range_alloc * 2 * sizeof(int)));
}
++*range_used;
@@ -105,8 +111,7 @@
}
}
-static struct fstab* read_fstab()
-{
+static struct fstab* read_fstab() {
fstab = NULL;
// The fstab path is always "/fstab.${ro.hardware}".
@@ -125,26 +130,26 @@
return fstab;
}
-const char* find_block_device(const char* path, int* encryptable, int* encrypted)
-{
+static const char* find_block_device(const char* path, bool* encryptable, bool* encrypted) {
// 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) {
+ for (int i = 0; i < fstab->num_entries; ++i) {
struct fstab_rec* v = &fstab->recs[i];
- if (!v->mount_point) continue;
+ 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;
+ *encrypted = false;
+ *encryptable = false;
if (fs_mgr_is_encryptable(v)) {
- *encryptable = 1;
+ *encryptable = true;
char buffer[PROPERTY_VALUE_MAX+1];
if (property_get("ro.crypto.state", buffer, "") &&
strcmp(buffer, "encrypted") == 0) {
- *encrypted = 1;
+ *encrypted = true;
}
}
return v->blk_device;
@@ -154,56 +159,37 @@
return NULL;
}
-char* parse_recovery_command_file()
+// Parse uncrypt_file to find the update package name.
+static bool find_uncrypt_package(std::string& package_name)
{
- char* fn = NULL;
- int count = 0;
- char temp[1024];
+ if (!android::base::ReadFileToString(uncrypt_file, &package_name)) {
+ ALOGE("failed to open \"%s\": %s\n", uncrypt_file.c_str(), strerror(errno));
+ return false;
+ }
- FILE* f = fopen(RECOVERY_COMMAND_FILE, "r");
- if (f == NULL) {
- return NULL;
- }
- int fd = open(RECOVERY_COMMAND_FILE_TMP, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
- if (fd < 0) {
- ALOGE("failed to open %s\n", RECOVERY_COMMAND_FILE_TMP);
- return NULL;
- }
- FILE* fo = fdopen(fd, "w");
+ // Remove the trailing '\n' if present.
+ package_name = android::base::Trim(package_name);
- while (fgets(temp, sizeof(temp), f)) {
- printf("read: %s", temp);
- if (strncmp(temp, "--update_package=/data/", strlen("--update_package=/data/")) == 0) {
- fn = strdup(temp + strlen("--update_package="));
- strcpy(temp, "--update_package=@" CACHE_BLOCK_MAP "\n");
- }
- fputs(temp, fo);
- }
- fclose(f);
- fsync(fd);
- fclose(fo);
-
- if (fn) {
- char* newline = strchr(fn, '\n');
- if (newline) *newline = 0;
- }
- return fn;
+ return true;
}
-int produce_block_map(const char* path, const char* map_file, const char* blk_dev,
- int encrypted)
-{
- struct stat sb;
- int ret;
-
+static int produce_block_map(const char* path, const char* map_file, const char* blk_dev,
+ bool encrypted, int status_fd) {
int mapfd = open(map_file, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
- if (mapfd < 0) {
+ if (mapfd == -1) {
ALOGE("failed to open %s\n", map_file);
return -1;
}
FILE* mapf = fdopen(mapfd, "w");
- ret = stat(path, &sb);
+ // Make sure we can write to the status_file.
+ if (!android::base::WriteStringToFd("0\n", status_fd)) {
+ ALOGE("failed to update \"%s\"\n", status_file.c_str());
+ return -1;
+ }
+
+ struct stat sb;
+ int ret = stat(path, &sb);
if (ret != 0) {
ALOGE("failed to stat %s\n", path);
return -1;
@@ -214,20 +200,18 @@
int blocks = ((sb.st_size-1) / sb.st_blksize) + 1;
ALOGI(" file size: %lld bytes, %d blocks\n", (long long)sb.st_size, blocks);
- int* ranges;
int range_alloc = 1;
int range_used = 1;
- ranges = malloc(range_alloc * 2 * sizeof(int));
+ int* ranges = reinterpret_cast<int*>(malloc(range_alloc * 2 * sizeof(int)));
ranges[0] = -1;
ranges[1] = -1;
fprintf(mapf, "%s\n%lld %lu\n", blk_dev, (long long)sb.st_size, (unsigned long)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);
+ for (size_t i = 0; i < WINDOW_SIZE; ++i) {
+ buffers[i] = reinterpret_cast<unsigned char*>(malloc(sb.st_blksize));
}
}
int head_block = 0;
@@ -239,7 +223,6 @@
ALOGE("failed to open fd for reading: %s\n", strerror(errno));
return -1;
}
- fsync(fd);
int wfd = -1;
if (encrypted) {
@@ -250,7 +233,15 @@
}
}
+ int last_progress = 0;
while (pos < sb.st_size) {
+ // Update the status file, progress must be between [0, 99].
+ int progress = static_cast<int>(100 * (double(pos) / double(sb.st_size)));
+ if (progress > last_progress) {
+ last_progress = progress;
+ android::base::WriteStringToFd(std::to_string(progress) + "\n", status_fd);
+ }
+
if ((tail+1) % WINDOW_SIZE == head) {
// write out head buffer
int block = head_block;
@@ -261,7 +252,8 @@
}
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) {
+ if (write_at_offset(buffers[head], sb.st_blksize, wfd,
+ (off64_t)sb.st_blksize * block) != 0) {
return -1;
}
}
@@ -273,8 +265,9 @@
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) {
+ ssize_t this_read =
+ TEMP_FAILURE_RETRY(read(fd, buffers[tail] + so_far, sb.st_blksize - so_far));
+ if (this_read == -1) {
ALOGE("failed to read: %s\n", strerror(errno));
return -1;
}
@@ -300,7 +293,8 @@
}
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) {
+ if (write_at_offset(buffers[head], sb.st_blksize, wfd,
+ (off64_t)sb.st_blksize * block) != 0) {
return -1;
}
}
@@ -309,25 +303,30 @@
}
fprintf(mapf, "%d\n", range_used);
- for (i = 0; i < range_used; ++i) {
+ for (int i = 0; i < range_used; ++i) {
fprintf(mapf, "%d %d\n", ranges[i*2], ranges[i*2+1]);
}
- fsync(mapfd);
+ if (fsync(mapfd) == -1) {
+ ALOGE("failed to fsync \"%s\": %s\n", map_file, strerror(errno));
+ return -1;
+ }
fclose(mapf);
close(fd);
if (encrypted) {
- fsync(wfd);
+ if (fsync(wfd) == -1) {
+ ALOGE("failed to fsync \"%s\": %s\n", blk_dev, strerror(errno));
+ return -1;
+ }
close(wfd);
}
return 0;
}
-void wipe_misc() {
+static void wipe_misc() {
ALOGI("removing old commands from misc");
- int i;
- for (i = 0; i < fstab->num_entries; ++i) {
+ for (int i = 0; i < fstab->num_entries; ++i) {
struct fstab_rec* v = &fstab->recs[i];
if (!v->mount_point) continue;
if (strcmp(v->mount_point, "/misc") == 0) {
@@ -338,72 +337,49 @@
size_t written = 0;
size_t size = sizeof(zeroes);
while (written < size) {
- ssize_t w = write(fd, zeroes, size-written);
- if (w < 0 && errno != EINTR) {
+ ssize_t w = TEMP_FAILURE_RETRY(write(fd, zeroes, size-written));
+ if (w == -1) {
ALOGE("zero write failed: %s\n", strerror(errno));
return;
} else {
written += w;
}
}
- fsync(fd);
+ if (fsync(fd) == -1) {
+ ALOGE("failed to fsync \"%s\": %s\n", v->blk_device, strerror(errno));
+ close(fd);
+ return;
+ }
close(fd);
}
}
}
-void reboot_to_recovery() {
+static void reboot_to_recovery() {
ALOGI("rebooting to recovery");
property_set("sys.powerctl", "reboot,recovery");
sleep(10);
ALOGE("reboot didn't succeed?");
}
-int main(int argc, char** argv)
-{
- const char* input_path;
- const char* map_file;
- int do_reboot = 1;
+int uncrypt(const char* input_path, const char* map_file, int status_fd) {
- 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.
- ALOGI("no recovery command file or no update package arg");
- reboot_to_recovery();
- return 1;
- }
- map_file = CACHE_BLOCK_MAP;
- }
-
- ALOGI("update package is %s", input_path);
+ ALOGI("update package is \"%s\"", input_path);
// 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) {
- ALOGE("failed to convert %s to absolute path: %s", input_path, strerror(errno));
+ ALOGE("failed to convert \"%s\" to absolute path: %s", input_path, strerror(errno));
return 1;
}
- int encryptable;
- int encrypted;
if (read_fstab() == NULL) {
return 1;
}
+
+ bool encryptable;
+ bool encrypted;
const char* blk_dev = find_block_device(path, &encryptable, &encrypted);
if (blk_dev == NULL) {
ALOGE("failed to find block device for %s", path);
@@ -423,18 +399,67 @@
// On /data we want to convert the file to a block map so that we
// can read the package without mounting the partition. On /cache
// and /sdcard we leave the file alone.
- if (strncmp(path, "/data/", 6) != 0) {
- // path does not start with "/data/"; leave it alone.
- unlink(RECOVERY_COMMAND_FILE_TMP);
- } else {
+ if (strncmp(path, "/data/", 6) == 0) {
ALOGI("writing block map %s", map_file);
- if (produce_block_map(path, map_file, blk_dev, encrypted) != 0) {
+ if (produce_block_map(path, map_file, blk_dev, encrypted, status_fd) != 0) {
return 1;
}
}
- wipe_misc();
- rename(RECOVERY_COMMAND_FILE_TMP, RECOVERY_COMMAND_FILE);
- if (do_reboot) reboot_to_recovery();
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ const char* input_path;
+ const char* map_file;
+
+ if (argc != 3 && argc != 1 && (argc == 2 && strcmp(argv[1], "--reboot") != 0)) {
+ fprintf(stderr, "usage: %s [--reboot] [<transform_path> <map_file>]\n", argv[0]);
+ return 2;
+ }
+
+ // When uncrypt is started with "--reboot", it wipes misc and reboots.
+ // Otherwise it uncrypts the package and writes the block map.
+ if (argc == 2) {
+ if (read_fstab() == NULL) {
+ return 1;
+ }
+ wipe_misc();
+ reboot_to_recovery();
+ } else {
+ // The pipe has been created by the system server.
+ int status_fd = open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
+ if (status_fd == -1) {
+ ALOGE("failed to open pipe \"%s\": %s\n", status_file.c_str(), strerror(errno));
+ return 1;
+ }
+
+ if (argc == 3) {
+ // when command-line args are given this binary is being used
+ // for debugging.
+ input_path = argv[1];
+ map_file = argv[2];
+ } else {
+ std::string package;
+ if (!find_uncrypt_package(package)) {
+ android::base::WriteStringToFd("-1\n", status_fd);
+ close(status_fd);
+ return 1;
+ }
+ input_path = package.c_str();
+ map_file = cache_block_map.c_str();
+ }
+
+ int status = uncrypt(input_path, map_file, status_fd);
+ if (status != 0) {
+ android::base::WriteStringToFd("-1\n", status_fd);
+ close(status_fd);
+ return 1;
+ }
+
+ android::base::WriteStringToFd("100\n", status_fd);
+ close(status_fd);
+ }
+
return 0;
}
diff --git a/updater/Android.mk b/updater/Android.mk
index 11e7bb8..ff02a33 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -79,7 +79,7 @@
$(hide) $(foreach lib,$(libs),echo " Register_$(lib)();" >> $@;)
$(hide) echo "}" >> $@
-$(call intermediates-dir-for,EXECUTABLES,updater)/updater.o : $(inc)
+$(call intermediates-dir-for,EXECUTABLES,updater,,,$(TARGET_PREFER_32_BIT))/updater.o : $(inc)
LOCAL_C_INCLUDES += $(dir $(inc))
inc :=
diff --git a/updater/MODULE_LICENSE_GPL b/updater/MODULE_LICENSE_GPL
deleted file mode 100644
index e69de29..0000000
--- a/updater/MODULE_LICENSE_GPL
+++ /dev/null
diff --git a/updater/NOTICE b/updater/NOTICE
deleted file mode 100644
index e77696a..0000000
--- a/updater/NOTICE
+++ /dev/null
@@ -1,339 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 675 Mass Ave, Cambridge, MA 02139, USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) 19yy <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) 19yy name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
diff --git a/updater/blockimg.c b/updater/blockimg.c
index 6060ac2..b006d10 100644
--- a/updater/blockimg.c
+++ b/updater/blockimg.c
@@ -16,13 +16,16 @@
#include <ctype.h>
#include <errno.h>
+#include <dirent.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <libgen.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
@@ -32,7 +35,7 @@
#include "applypatch/applypatch.h"
#include "edify/expr.h"
#include "mincrypt/sha.h"
-#include "minzip/DirUtil.h"
+#include "minzip/Hash.h"
#include "updater.h"
#define BLOCKSIZE 4096
@@ -46,6 +49,10 @@
#define BLKDISCARD _IO(0x12,119)
#endif
+#define STASH_DIRECTORY_BASE "/cache/recovery"
+#define STASH_DIRECTORY_MODE 0700
+#define STASH_FILE_MODE 0600
+
char* PrintSha1(const uint8_t* digest);
typedef struct {
@@ -80,44 +87,69 @@
return out;
}
-static void readblock(int fd, uint8_t* data, size_t size) {
+static int range_overlaps(RangeSet* r1, RangeSet* r2) {
+ int i, j, r1_0, r1_1, r2_0, r2_1;
+
+ if (!r1 || !r2) {
+ return 0;
+ }
+
+ for (i = 0; i < r1->count; ++i) {
+ r1_0 = r1->pos[i * 2];
+ r1_1 = r1->pos[i * 2 + 1];
+
+ for (j = 0; j < r2->count; ++j) {
+ r2_0 = r2->pos[j * 2];
+ r2_1 = r2->pos[j * 2 + 1];
+
+ if (!(r2_0 >= r1_1 || r1_0 >= r2_1)) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int read_all(int fd, uint8_t* data, size_t size) {
size_t so_far = 0;
while (so_far < size) {
- ssize_t r = read(fd, data+so_far, size-so_far);
- if (r < 0 && errno != EINTR) {
+ ssize_t r = TEMP_FAILURE_RETRY(read(fd, data+so_far, size-so_far));
+ if (r == -1) {
fprintf(stderr, "read failed: %s\n", strerror(errno));
- return;
- } else {
- so_far += r;
+ return -1;
}
+ so_far += r;
}
+ return 0;
}
-static void writeblock(int fd, const uint8_t* data, size_t size) {
+static int write_all(int fd, const uint8_t* data, size_t size) {
size_t written = 0;
while (written < size) {
- ssize_t w = write(fd, data+written, size-written);
- if (w < 0 && errno != EINTR) {
+ ssize_t w = TEMP_FAILURE_RETRY(write(fd, data+written, size-written));
+ if (w == -1) {
fprintf(stderr, "write failed: %s\n", strerror(errno));
- return;
- } else {
- written += w;
+ return -1;
}
+ written += w;
}
+
+ if (fsync(fd) == -1) {
+ fprintf(stderr, "fsync failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
}
-static void check_lseek(int fd, off64_t offset, int whence) {
- while (true) {
- off64_t ret = lseek64(fd, offset, whence);
- if (ret < 0) {
- if (errno != EINTR) {
- fprintf(stderr, "lseek64 failed: %s\n", strerror(errno));
- exit(1);
- }
- } else {
- break;
- }
+static bool check_lseek(int fd, off64_t offset, int whence) {
+ off64_t rc = TEMP_FAILURE_RETRY(lseek64(fd, offset, whence));
+ if (rc == -1) {
+ fprintf(stderr, "lseek64 failed: %s\n", strerror(errno));
+ return false;
}
+ return true;
}
static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) {
@@ -146,14 +178,21 @@
if (rss->p_remain <= 0) {
fprintf(stderr, "range sink write overrun");
- exit(1);
+ return 0;
}
ssize_t written = 0;
while (size > 0) {
size_t write_now = size;
- if (rss->p_remain < write_now) write_now = rss->p_remain;
- writeblock(rss->fd, data, write_now);
+
+ if (rss->p_remain < write_now) {
+ write_now = rss->p_remain;
+ }
+
+ if (write_all(rss->fd, data, write_now) == -1) {
+ break;
+ }
+
data += write_now;
size -= write_now;
@@ -164,12 +203,17 @@
// move to the next block
++rss->p_block;
if (rss->p_block < rss->tgt->count) {
- rss->p_remain = (rss->tgt->pos[rss->p_block*2+1] - rss->tgt->pos[rss->p_block*2]) * BLOCKSIZE;
- check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE, SEEK_SET);
+ rss->p_remain = (rss->tgt->pos[rss->p_block * 2 + 1] -
+ rss->tgt->pos[rss->p_block * 2]) * BLOCKSIZE;
+
+ if (!check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE,
+ SEEK_SET)) {
+ break;
+ }
} else {
// we can't write any more; return how many bytes have
// been written so far.
- return written;
+ break;
}
}
}
@@ -245,6 +289,58 @@
return NULL;
}
+static int ReadBlocks(RangeSet* src, uint8_t* buffer, int fd) {
+ int i;
+ size_t p = 0;
+ size_t size;
+
+ if (!src || !buffer) {
+ return -1;
+ }
+
+ for (i = 0; i < src->count; ++i) {
+ if (!check_lseek(fd, (off64_t) src->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
+ return -1;
+ }
+
+ size = (src->pos[i * 2 + 1] - src->pos[i * 2]) * BLOCKSIZE;
+
+ if (read_all(fd, buffer + p, size) == -1) {
+ return -1;
+ }
+
+ p += size;
+ }
+
+ return 0;
+}
+
+static int WriteBlocks(RangeSet* tgt, uint8_t* buffer, int fd) {
+ int i;
+ size_t p = 0;
+ size_t size;
+
+ if (!tgt || !buffer) {
+ return -1;
+ }
+
+ for (i = 0; i < tgt->count; ++i) {
+ if (!check_lseek(fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
+ return -1;
+ }
+
+ size = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * BLOCKSIZE;
+
+ if (write_all(fd, buffer + p, size) == -1) {
+ return -1;
+ }
+
+ p += size;
+ }
+
+ return 0;
+}
+
// Do a source/target load for move/bsdiff/imgdiff in version 1.
// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
// to parse the remainder of the string as:
@@ -255,30 +351,525 @@
// it to make it larger if necessary. The target ranges are returned
// in *tgt, if tgt is non-NULL.
-static void LoadSrcTgtVersion1(char* wordsave, RangeSet** tgt, int* src_blocks,
+static int LoadSrcTgtVersion1(char** wordsave, RangeSet** tgt, int* src_blocks,
uint8_t** buffer, size_t* buffer_alloc, int fd) {
char* word;
+ int rc;
- word = strtok_r(NULL, " ", &wordsave);
+ word = strtok_r(NULL, " ", wordsave);
RangeSet* src = parse_range(word);
if (tgt != NULL) {
- word = strtok_r(NULL, " ", &wordsave);
+ word = strtok_r(NULL, " ", wordsave);
*tgt = parse_range(word);
}
allocate(src->size * BLOCKSIZE, buffer, buffer_alloc);
- size_t p = 0;
- int i;
- for (i = 0; i < src->count; ++i) {
- check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET);
- size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE;
- readblock(fd, *buffer+p, sz);
- p += sz;
+ rc = ReadBlocks(src, *buffer, fd);
+ *src_blocks = src->size;
+
+ free(src);
+ return rc;
+}
+
+static int VerifyBlocks(const char *expected, const uint8_t *buffer,
+ size_t blocks, int printerror) {
+ char* hexdigest = NULL;
+ int rc = -1;
+ uint8_t digest[SHA_DIGEST_SIZE];
+
+ if (!expected || !buffer) {
+ return rc;
}
- *src_blocks = src->size;
- free(src);
+ SHA_hash(buffer, blocks * BLOCKSIZE, digest);
+ hexdigest = PrintSha1(digest);
+
+ if (hexdigest != NULL) {
+ rc = strcmp(expected, hexdigest);
+
+ if (rc != 0 && printerror) {
+ fprintf(stderr, "failed to verify blocks (expected %s, read %s)\n",
+ expected, hexdigest);
+ }
+
+ free(hexdigest);
+ }
+
+ return rc;
+}
+
+static char* GetStashFileName(const char* base, const char* id, const char* postfix) {
+ char* fn;
+ int len;
+ int res;
+
+ if (base == NULL) {
+ return NULL;
+ }
+
+ if (id == NULL) {
+ id = "";
+ }
+
+ if (postfix == NULL) {
+ postfix = "";
+ }
+
+ len = strlen(STASH_DIRECTORY_BASE) + 1 + strlen(base) + 1 + strlen(id) + strlen(postfix) + 1;
+ fn = malloc(len);
+
+ if (fn == NULL) {
+ fprintf(stderr, "failed to malloc %d bytes for fn\n", len);
+ return NULL;
+ }
+
+ res = snprintf(fn, len, STASH_DIRECTORY_BASE "/%s/%s%s", base, id, postfix);
+
+ if (res < 0 || res >= len) {
+ fprintf(stderr, "failed to format file name (return value %d)\n", res);
+ free(fn);
+ return NULL;
+ }
+
+ return fn;
+}
+
+typedef void (*StashCallback)(const char*, void*);
+
+// Does a best effort enumeration of stash files. Ignores possible non-file
+// items in the stash directory and continues despite of errors. Calls the
+// 'callback' function for each file and passes 'data' to the function as a
+// parameter.
+
+static void EnumerateStash(const char* dirname, StashCallback callback, void* data) {
+ char* fn;
+ DIR* directory;
+ int len;
+ int res;
+ struct dirent* item;
+
+ if (dirname == NULL || callback == NULL) {
+ return;
+ }
+
+ directory = opendir(dirname);
+
+ if (directory == NULL) {
+ if (errno != ENOENT) {
+ fprintf(stderr, "opendir \"%s\" failed: %s\n", dirname, strerror(errno));
+ }
+ return;
+ }
+
+ while ((item = readdir(directory)) != NULL) {
+ if (item->d_type != DT_REG) {
+ continue;
+ }
+
+ len = strlen(dirname) + 1 + strlen(item->d_name) + 1;
+ fn = malloc(len);
+
+ if (fn == NULL) {
+ fprintf(stderr, "failed to malloc %d bytes for fn\n", len);
+ continue;
+ }
+
+ res = snprintf(fn, len, "%s/%s", dirname, item->d_name);
+
+ if (res < 0 || res >= len) {
+ fprintf(stderr, "failed to format file name (return value %d)\n", res);
+ free(fn);
+ continue;
+ }
+
+ callback(fn, data);
+ free(fn);
+ }
+
+ if (closedir(directory) == -1) {
+ fprintf(stderr, "closedir \"%s\" failed: %s\n", dirname, strerror(errno));
+ }
+}
+
+static void UpdateFileSize(const char* fn, void* data) {
+ int* size = (int*) data;
+ struct stat st;
+
+ if (!fn || !data) {
+ return;
+ }
+
+ if (stat(fn, &st) == -1) {
+ fprintf(stderr, "stat \"%s\" failed: %s\n", fn, strerror(errno));
+ return;
+ }
+
+ *size += st.st_size;
+}
+
+// Deletes the stash directory and all files in it. Assumes that it only
+// contains files. There is nothing we can do about unlikely, but possible
+// errors, so they are merely logged.
+
+static void DeleteFile(const char* fn, void* data) {
+ if (fn) {
+ fprintf(stderr, "deleting %s\n", fn);
+
+ if (unlink(fn) == -1 && errno != ENOENT) {
+ fprintf(stderr, "unlink \"%s\" failed: %s\n", fn, strerror(errno));
+ }
+ }
+}
+
+static void DeletePartial(const char* fn, void* data) {
+ if (fn && strstr(fn, ".partial") != NULL) {
+ DeleteFile(fn, data);
+ }
+}
+
+static void DeleteStash(const char* base) {
+ char* dirname;
+
+ if (base == NULL) {
+ return;
+ }
+
+ dirname = GetStashFileName(base, NULL, NULL);
+
+ if (dirname == NULL) {
+ return;
+ }
+
+ fprintf(stderr, "deleting stash %s\n", base);
+ EnumerateStash(dirname, DeleteFile, NULL);
+
+ if (rmdir(dirname) == -1) {
+ if (errno != ENOENT && errno != ENOTDIR) {
+ fprintf(stderr, "rmdir \"%s\" failed: %s\n", dirname, strerror(errno));
+ }
+ }
+
+ free(dirname);
+}
+
+static int LoadStash(const char* base, const char* id, int verify, int* blocks, uint8_t** buffer,
+ size_t* buffer_alloc, int printnoent) {
+ char *fn = NULL;
+ int blockcount = 0;
+ int fd = -1;
+ int rc = -1;
+ int res;
+ struct stat st;
+
+ if (!base || !id || !buffer || !buffer_alloc) {
+ goto lsout;
+ }
+
+ if (!blocks) {
+ blocks = &blockcount;
+ }
+
+ fn = GetStashFileName(base, id, NULL);
+
+ if (fn == NULL) {
+ goto lsout;
+ }
+
+ res = stat(fn, &st);
+
+ if (res == -1) {
+ if (errno != ENOENT || printnoent) {
+ fprintf(stderr, "stat \"%s\" failed: %s\n", fn, strerror(errno));
+ }
+ goto lsout;
+ }
+
+ fprintf(stderr, " loading %s\n", fn);
+
+ if ((st.st_size % BLOCKSIZE) != 0) {
+ fprintf(stderr, "%s size %zd not multiple of block size %d", fn, st.st_size, BLOCKSIZE);
+ goto lsout;
+ }
+
+ fd = TEMP_FAILURE_RETRY(open(fn, O_RDONLY));
+
+ if (fd == -1) {
+ fprintf(stderr, "open \"%s\" failed: %s\n", fn, strerror(errno));
+ goto lsout;
+ }
+
+ allocate(st.st_size, buffer, buffer_alloc);
+
+ if (read_all(fd, *buffer, st.st_size) == -1) {
+ goto lsout;
+ }
+
+ *blocks = st.st_size / BLOCKSIZE;
+
+ if (verify && VerifyBlocks(id, *buffer, *blocks, 1) != 0) {
+ fprintf(stderr, "unexpected contents in %s\n", fn);
+ DeleteFile(fn, NULL);
+ goto lsout;
+ }
+
+ rc = 0;
+
+lsout:
+ if (fd != -1) {
+ close(fd);
+ }
+
+ if (fn) {
+ free(fn);
+ }
+
+ return rc;
+}
+
+static int WriteStash(const char* base, const char* id, int blocks, uint8_t* buffer,
+ int checkspace, int *exists) {
+ char *fn = NULL;
+ char *cn = NULL;
+ int fd = -1;
+ int rc = -1;
+ int dfd = -1;
+ int res;
+ struct stat st;
+
+ if (base == NULL || buffer == NULL) {
+ goto wsout;
+ }
+
+ if (checkspace && CacheSizeCheck(blocks * BLOCKSIZE) != 0) {
+ fprintf(stderr, "not enough space to write stash\n");
+ goto wsout;
+ }
+
+ fn = GetStashFileName(base, id, ".partial");
+ cn = GetStashFileName(base, id, NULL);
+
+ if (fn == NULL || cn == NULL) {
+ goto wsout;
+ }
+
+ if (exists) {
+ res = stat(cn, &st);
+
+ if (res == 0) {
+ // The file already exists and since the name is the hash of the contents,
+ // it's safe to assume the contents are identical (accidental hash collisions
+ // are unlikely)
+ fprintf(stderr, " skipping %d existing blocks in %s\n", blocks, cn);
+ *exists = 1;
+ rc = 0;
+ goto wsout;
+ }
+
+ *exists = 0;
+ }
+
+ fprintf(stderr, " writing %d blocks to %s\n", blocks, cn);
+
+ fd = TEMP_FAILURE_RETRY(open(fn, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, STASH_FILE_MODE));
+
+ if (fd == -1) {
+ fprintf(stderr, "failed to create \"%s\": %s\n", fn, strerror(errno));
+ goto wsout;
+ }
+
+ if (write_all(fd, buffer, blocks * BLOCKSIZE) == -1) {
+ goto wsout;
+ }
+
+ if (fsync(fd) == -1) {
+ fprintf(stderr, "fsync \"%s\" failed: %s\n", fn, strerror(errno));
+ goto wsout;
+ }
+
+ if (rename(fn, cn) == -1) {
+ fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", fn, cn, strerror(errno));
+ goto wsout;
+ }
+
+ const char* dname;
+ dname = dirname(cn);
+ dfd = TEMP_FAILURE_RETRY(open(dname, O_RDONLY | O_DIRECTORY));
+
+ if (dfd == -1) {
+ fprintf(stderr, "failed to open \"%s\" failed: %s\n", dname, strerror(errno));
+ goto wsout;
+ }
+
+ if (fsync(dfd) == -1) {
+ fprintf(stderr, "fsync \"%s\" failed: %s\n", dname, strerror(errno));
+ goto wsout;
+ }
+
+ rc = 0;
+
+wsout:
+ if (fd != -1) {
+ close(fd);
+ }
+
+ if (dfd != -1) {
+ close(dfd);
+ }
+
+ if (fn) {
+ free(fn);
+ }
+
+ if (cn) {
+ free(cn);
+ }
+
+ return rc;
+}
+
+// Creates a directory for storing stash files and checks if the /cache partition
+// hash enough space for the expected amount of blocks we need to store. Returns
+// >0 if we created the directory, zero if it existed already, and <0 of failure.
+
+static int CreateStash(State* state, int maxblocks, const char* blockdev, char** base) {
+ char* dirname = NULL;
+ const uint8_t* digest;
+ int rc = -1;
+ int res;
+ int size = 0;
+ SHA_CTX ctx;
+ struct stat st;
+
+ if (blockdev == NULL || base == NULL) {
+ goto csout;
+ }
+
+ // Stash directory should be different for each partition to avoid conflicts
+ // when updating multiple partitions at the same time, so we use the hash of
+ // the block device name as the base directory
+ SHA_init(&ctx);
+ SHA_update(&ctx, blockdev, strlen(blockdev));
+ digest = SHA_final(&ctx);
+ *base = PrintSha1(digest);
+
+ if (*base == NULL) {
+ goto csout;
+ }
+
+ dirname = GetStashFileName(*base, NULL, NULL);
+
+ if (dirname == NULL) {
+ goto csout;
+ }
+
+ res = stat(dirname, &st);
+
+ if (res == -1 && errno != ENOENT) {
+ ErrorAbort(state, "stat \"%s\" failed: %s\n", dirname, strerror(errno));
+ goto csout;
+ } else if (res != 0) {
+ fprintf(stderr, "creating stash %s\n", dirname);
+ res = mkdir(dirname, STASH_DIRECTORY_MODE);
+
+ if (res != 0) {
+ ErrorAbort(state, "mkdir \"%s\" failed: %s\n", dirname, strerror(errno));
+ goto csout;
+ }
+
+ if (CacheSizeCheck(maxblocks * BLOCKSIZE) != 0) {
+ ErrorAbort(state, "not enough space for stash\n");
+ goto csout;
+ }
+
+ rc = 1; // Created directory
+ goto csout;
+ }
+
+ fprintf(stderr, "using existing stash %s\n", dirname);
+
+ // If the directory already exists, calculate the space already allocated to
+ // stash files and check if there's enough for all required blocks. Delete any
+ // partially completed stash files first.
+
+ EnumerateStash(dirname, DeletePartial, NULL);
+ EnumerateStash(dirname, UpdateFileSize, &size);
+
+ size = (maxblocks * BLOCKSIZE) - size;
+
+ if (size > 0 && CacheSizeCheck(size) != 0) {
+ ErrorAbort(state, "not enough space for stash (%d more needed)\n", size);
+ goto csout;
+ }
+
+ rc = 0; // Using existing directory
+
+csout:
+ if (dirname) {
+ free(dirname);
+ }
+
+ return rc;
+}
+
+static int SaveStash(const char* base, char** wordsave, uint8_t** buffer, size_t* buffer_alloc,
+ int fd, int usehash, int* isunresumable) {
+ char *id = NULL;
+ int res = -1;
+ int blocks = 0;
+
+ if (!wordsave || !buffer || !buffer_alloc || !isunresumable) {
+ return -1;
+ }
+
+ id = strtok_r(NULL, " ", wordsave);
+
+ if (id == NULL) {
+ fprintf(stderr, "missing id field in stash command\n");
+ return -1;
+ }
+
+ if (usehash && LoadStash(base, id, 1, &blocks, buffer, buffer_alloc, 0) == 0) {
+ // Stash file already exists and has expected contents. Do not
+ // read from source again, as the source may have been already
+ // overwritten during a previous attempt.
+ return 0;
+ }
+
+ if (LoadSrcTgtVersion1(wordsave, NULL, &blocks, buffer, buffer_alloc, fd) == -1) {
+ return -1;
+ }
+
+ if (usehash && VerifyBlocks(id, *buffer, blocks, 1) != 0) {
+ // Source blocks have unexpected contents. If we actually need this
+ // data later, this is an unrecoverable error. However, the command
+ // that uses the data may have already completed previously, so the
+ // possible failure will occur during source block verification.
+ fprintf(stderr, "failed to load source blocks for stash %s\n", id);
+ return 0;
+ }
+
+ fprintf(stderr, "stashing %d blocks to %s\n", blocks, id);
+ return WriteStash(base, id, blocks, *buffer, 0, NULL);
+}
+
+static int FreeStash(const char* base, const char* id) {
+ char *fn = NULL;
+
+ if (base == NULL || id == NULL) {
+ return -1;
+ }
+
+ fn = GetStashFileName(base, id, NULL);
+
+ if (fn == NULL) {
+ return -1;
+ }
+
+ DeleteFile(fn, NULL);
+ free(fn);
+
+ return 0;
}
static void MoveRange(uint8_t* dest, RangeSet* locs, const uint8_t* source) {
@@ -312,64 +903,613 @@
// On return, buffer is filled with the loaded source data (rearranged
// and combined with stashed data as necessary). buffer may be
// reallocated if needed to accommodate the source data. *tgt is the
-// target RangeSet. Any stashes required are taken from stash_table
-// and free()'d after being used.
+// target RangeSet. Any stashes required are loaded using LoadStash.
-static void LoadSrcTgtVersion2(char* wordsave, RangeSet** tgt, int* src_blocks,
+static int LoadSrcTgtVersion2(char** wordsave, RangeSet** tgt, int* src_blocks,
uint8_t** buffer, size_t* buffer_alloc, int fd,
- uint8_t** stash_table) {
+ const char* stashbase, int* overlap) {
char* word;
+ char* colonsave;
+ char* colon;
+ int id;
+ int res;
+ RangeSet* locs;
+ size_t stashalloc = 0;
+ uint8_t* stash = NULL;
if (tgt != NULL) {
- word = strtok_r(NULL, " ", &wordsave);
+ word = strtok_r(NULL, " ", wordsave);
*tgt = parse_range(word);
}
- word = strtok_r(NULL, " ", &wordsave);
+ word = strtok_r(NULL, " ", wordsave);
*src_blocks = strtol(word, NULL, 0);
allocate(*src_blocks * BLOCKSIZE, buffer, buffer_alloc);
- word = strtok_r(NULL, " ", &wordsave);
+ word = strtok_r(NULL, " ", wordsave);
if (word[0] == '-' && word[1] == '\0') {
// no source ranges, only stashes
} else {
RangeSet* src = parse_range(word);
+ res = ReadBlocks(src, *buffer, fd);
- size_t p = 0;
- int i;
- for (i = 0; i < src->count; ++i) {
- check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET);
- size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE;
- readblock(fd, *buffer+p, sz);
- p += sz;
+ if (overlap && tgt) {
+ *overlap = range_overlaps(src, *tgt);
}
+
free(src);
- word = strtok_r(NULL, " ", &wordsave);
- if (word == NULL) {
- // no stashes, only source range
- return;
+ if (res == -1) {
+ return -1;
}
- RangeSet* locs = parse_range(word);
+ word = strtok_r(NULL, " ", wordsave);
+ if (word == NULL) {
+ // no stashes, only source range
+ return 0;
+ }
+
+ locs = parse_range(word);
MoveRange(*buffer, locs, *buffer);
+ free(locs);
}
- while ((word = strtok_r(NULL, " ", &wordsave)) != NULL) {
+ while ((word = strtok_r(NULL, " ", wordsave)) != NULL) {
// Each word is a an index into the stash table, a colon, and
// then a rangeset describing where in the source block that
// stashed data should go.
- char* colonsave = NULL;
- char* colon = strtok_r(word, ":", &colonsave);
- int stash_id = strtol(colon, NULL, 0);
+ colonsave = NULL;
+ colon = strtok_r(word, ":", &colonsave);
+
+ res = LoadStash(stashbase, colon, 0, NULL, &stash, &stashalloc, 1);
+
+ if (res == -1) {
+ // These source blocks will fail verification if used later, but we
+ // will let the caller decide if this is a fatal failure
+ fprintf(stderr, "failed to load stash %s\n", colon);
+ continue;
+ }
+
colon = strtok_r(NULL, ":", &colonsave);
- RangeSet* locs = parse_range(colon);
- MoveRange(*buffer, locs, stash_table[stash_id]);
- free(stash_table[stash_id]);
- stash_table[stash_id] = NULL;
+ locs = parse_range(colon);
+
+ MoveRange(*buffer, locs, stash);
free(locs);
}
+
+ if (stash) {
+ free(stash);
+ }
+
+ return 0;
+}
+
+// Parameters for transfer list command functions
+typedef struct {
+ char* cmdname;
+ char* cpos;
+ char* freestash;
+ char* stashbase;
+ int canwrite;
+ int createdstash;
+ int fd;
+ int foundwrites;
+ int isunresumable;
+ int version;
+ int written;
+ NewThreadInfo nti;
+ pthread_t thread;
+ size_t bufsize;
+ uint8_t* buffer;
+ uint8_t* patch_start;
+} CommandParameters;
+
+// Do a source/target load for move/bsdiff/imgdiff in version 3.
+//
+// Parameters are the same as for LoadSrcTgtVersion2, except for 'onehash', which
+// tells the function whether to expect separate source and targe block hashes, or
+// if they are both the same and only one hash should be expected, and
+// 'isunresumable', which receives a non-zero value if block verification fails in
+// a way that the update cannot be resumed anymore.
+//
+// If the function is unable to load the necessary blocks or their contents don't
+// match the hashes, the return value is -1 and the command should be aborted.
+//
+// If the return value is 1, the command has already been completed according to
+// the contents of the target blocks, and should not be performed again.
+//
+// If the return value is 0, source blocks have expected content and the command
+// can be performed.
+
+static int LoadSrcTgtVersion3(CommandParameters* params, RangeSet** tgt, int* src_blocks,
+ int onehash, int* overlap) {
+ char* srchash = NULL;
+ char* tgthash = NULL;
+ int stash_exists = 0;
+ int overlap_blocks = 0;
+ int rc = -1;
+ uint8_t* tgtbuffer = NULL;
+
+ if (!params|| !tgt || !src_blocks || !overlap) {
+ goto v3out;
+ }
+
+ srchash = strtok_r(NULL, " ", ¶ms->cpos);
+
+ if (srchash == NULL) {
+ fprintf(stderr, "missing source hash\n");
+ goto v3out;
+ }
+
+ if (onehash) {
+ tgthash = srchash;
+ } else {
+ tgthash = strtok_r(NULL, " ", ¶ms->cpos);
+
+ if (tgthash == NULL) {
+ fprintf(stderr, "missing target hash\n");
+ goto v3out;
+ }
+ }
+
+ if (LoadSrcTgtVersion2(¶ms->cpos, tgt, src_blocks, ¶ms->buffer, ¶ms->bufsize,
+ params->fd, params->stashbase, overlap) == -1) {
+ goto v3out;
+ }
+
+ tgtbuffer = (uint8_t*) malloc((*tgt)->size * BLOCKSIZE);
+
+ if (tgtbuffer == NULL) {
+ fprintf(stderr, "failed to allocate %d bytes\n", (*tgt)->size * BLOCKSIZE);
+ goto v3out;
+ }
+
+ if (ReadBlocks(*tgt, tgtbuffer, params->fd) == -1) {
+ goto v3out;
+ }
+
+ if (VerifyBlocks(tgthash, tgtbuffer, (*tgt)->size, 0) == 0) {
+ // Target blocks already have expected content, command should be skipped
+ rc = 1;
+ goto v3out;
+ }
+
+ if (VerifyBlocks(srchash, params->buffer, *src_blocks, 1) == 0) {
+ // If source and target blocks overlap, stash the source blocks so we can
+ // resume from possible write errors
+ if (*overlap) {
+ fprintf(stderr, "stashing %d overlapping blocks to %s\n", *src_blocks,
+ srchash);
+
+ if (WriteStash(params->stashbase, srchash, *src_blocks, params->buffer, 1,
+ &stash_exists) != 0) {
+ fprintf(stderr, "failed to stash overlapping source blocks\n");
+ goto v3out;
+ }
+
+ // Can be deleted when the write has completed
+ if (!stash_exists) {
+ params->freestash = srchash;
+ }
+ }
+
+ // Source blocks have expected content, command can proceed
+ rc = 0;
+ goto v3out;
+ }
+
+ if (*overlap && LoadStash(params->stashbase, srchash, 1, NULL, ¶ms->buffer,
+ ¶ms->bufsize, 1) == 0) {
+ // Overlapping source blocks were previously stashed, command can proceed.
+ // We are recovering from an interrupted command, so we don't know if the
+ // stash can safely be deleted after this command.
+ rc = 0;
+ goto v3out;
+ }
+
+ // Valid source data not available, update cannot be resumed
+ fprintf(stderr, "partition has unexpected contents\n");
+ params->isunresumable = 1;
+
+v3out:
+ if (tgtbuffer) {
+ free(tgtbuffer);
+ }
+
+ return rc;
+}
+
+static int PerformCommandMove(CommandParameters* params) {
+ int blocks = 0;
+ int overlap = 0;
+ int rc = -1;
+ int status = 0;
+ RangeSet* tgt = NULL;
+
+ if (!params) {
+ goto pcmout;
+ }
+
+ if (params->version == 1) {
+ status = LoadSrcTgtVersion1(¶ms->cpos, &tgt, &blocks, ¶ms->buffer,
+ ¶ms->bufsize, params->fd);
+ } else if (params->version == 2) {
+ status = LoadSrcTgtVersion2(¶ms->cpos, &tgt, &blocks, ¶ms->buffer,
+ ¶ms->bufsize, params->fd, params->stashbase, NULL);
+ } else if (params->version >= 3) {
+ status = LoadSrcTgtVersion3(params, &tgt, &blocks, 1, &overlap);
+ }
+
+ if (status == -1) {
+ fprintf(stderr, "failed to read blocks for move\n");
+ goto pcmout;
+ }
+
+ if (status == 0) {
+ params->foundwrites = 1;
+ } else if (params->foundwrites) {
+ fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname);
+ }
+
+ if (params->canwrite) {
+ if (status == 0) {
+ fprintf(stderr, " moving %d blocks\n", blocks);
+
+ if (WriteBlocks(tgt, params->buffer, params->fd) == -1) {
+ goto pcmout;
+ }
+ } else {
+ fprintf(stderr, "skipping %d already moved blocks\n", blocks);
+ }
+
+ }
+
+ if (params->freestash) {
+ FreeStash(params->stashbase, params->freestash);
+ params->freestash = NULL;
+ }
+
+ params->written += tgt->size;
+ rc = 0;
+
+pcmout:
+ if (tgt) {
+ free(tgt);
+ }
+
+ return rc;
+}
+
+static int PerformCommandStash(CommandParameters* params) {
+ if (!params) {
+ return -1;
+ }
+
+ return SaveStash(params->stashbase, ¶ms->cpos, ¶ms->buffer, ¶ms->bufsize,
+ params->fd, (params->version >= 3), ¶ms->isunresumable);
+}
+
+static int PerformCommandFree(CommandParameters* params) {
+ if (!params) {
+ return -1;
+ }
+
+ if (params->createdstash || params->canwrite) {
+ return FreeStash(params->stashbase, params->cpos);
+ }
+
+ return 0;
+}
+
+static int PerformCommandZero(CommandParameters* params) {
+ char* range = NULL;
+ int i;
+ int j;
+ int rc = -1;
+ RangeSet* tgt = NULL;
+
+ if (!params) {
+ goto pczout;
+ }
+
+ range = strtok_r(NULL, " ", ¶ms->cpos);
+
+ if (range == NULL) {
+ fprintf(stderr, "missing target blocks for zero\n");
+ goto pczout;
+ }
+
+ tgt = parse_range(range);
+
+ fprintf(stderr, " zeroing %d blocks\n", tgt->size);
+
+ allocate(BLOCKSIZE, ¶ms->buffer, ¶ms->bufsize);
+ memset(params->buffer, 0, BLOCKSIZE);
+
+ if (params->canwrite) {
+ for (i = 0; i < tgt->count; ++i) {
+ if (!check_lseek(params->fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
+ goto pczout;
+ }
+
+ for (j = tgt->pos[i * 2]; j < tgt->pos[i * 2 + 1]; ++j) {
+ if (write_all(params->fd, params->buffer, BLOCKSIZE) == -1) {
+ goto pczout;
+ }
+ }
+ }
+ }
+
+ if (params->cmdname[0] == 'z') {
+ // Update only for the zero command, as the erase command will call
+ // this if DEBUG_ERASE is defined.
+ params->written += tgt->size;
+ }
+
+ rc = 0;
+
+pczout:
+ if (tgt) {
+ free(tgt);
+ }
+
+ return rc;
+}
+
+static int PerformCommandNew(CommandParameters* params) {
+ char* range = NULL;
+ int rc = -1;
+ RangeSet* tgt = NULL;
+ RangeSinkState rss;
+
+ if (!params) {
+ goto pcnout;
+ }
+
+ range = strtok_r(NULL, " ", ¶ms->cpos);
+
+ if (range == NULL) {
+ goto pcnout;
+ }
+
+ tgt = parse_range(range);
+
+ if (params->canwrite) {
+ fprintf(stderr, " writing %d blocks of new data\n", tgt->size);
+
+ rss.fd = params->fd;
+ rss.tgt = tgt;
+ rss.p_block = 0;
+ rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
+
+ if (!check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET)) {
+ goto pcnout;
+ }
+
+ pthread_mutex_lock(¶ms->nti.mu);
+ params->nti.rss = &rss;
+ pthread_cond_broadcast(¶ms->nti.cv);
+
+ while (params->nti.rss) {
+ pthread_cond_wait(¶ms->nti.cv, ¶ms->nti.mu);
+ }
+
+ pthread_mutex_unlock(¶ms->nti.mu);
+ }
+
+ params->written += tgt->size;
+ rc = 0;
+
+pcnout:
+ if (tgt) {
+ free(tgt);
+ }
+
+ return rc;
+}
+
+static int PerformCommandDiff(CommandParameters* params) {
+ char* logparams = NULL;
+ char* value = NULL;
+ int blocks = 0;
+ int overlap = 0;
+ int rc = -1;
+ int status = 0;
+ RangeSet* tgt = NULL;
+ RangeSinkState rss;
+ size_t len = 0;
+ size_t offset = 0;
+ Value patch_value;
+
+ if (!params) {
+ goto pcdout;
+ }
+
+ logparams = strdup(params->cpos);
+ value = strtok_r(NULL, " ", ¶ms->cpos);
+
+ if (value == NULL) {
+ fprintf(stderr, "missing patch offset for %s\n", params->cmdname);
+ goto pcdout;
+ }
+
+ offset = strtoul(value, NULL, 0);
+
+ value = strtok_r(NULL, " ", ¶ms->cpos);
+
+ if (value == NULL) {
+ fprintf(stderr, "missing patch length for %s\n", params->cmdname);
+ goto pcdout;
+ }
+
+ len = strtoul(value, NULL, 0);
+
+ if (params->version == 1) {
+ status = LoadSrcTgtVersion1(¶ms->cpos, &tgt, &blocks, ¶ms->buffer,
+ ¶ms->bufsize, params->fd);
+ } else if (params->version == 2) {
+ status = LoadSrcTgtVersion2(¶ms->cpos, &tgt, &blocks, ¶ms->buffer,
+ ¶ms->bufsize, params->fd, params->stashbase, NULL);
+ } else if (params->version >= 3) {
+ status = LoadSrcTgtVersion3(params, &tgt, &blocks, 0, &overlap);
+ }
+
+ if (status == -1) {
+ fprintf(stderr, "failed to read blocks for diff\n");
+ goto pcdout;
+ }
+
+ if (status == 0) {
+ params->foundwrites = 1;
+ } else if (params->foundwrites) {
+ fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname);
+ }
+
+ if (params->canwrite) {
+ if (status == 0) {
+ fprintf(stderr, "patching %d blocks to %d\n", blocks, tgt->size);
+
+ patch_value.type = VAL_BLOB;
+ patch_value.size = len;
+ patch_value.data = (char*) (params->patch_start + offset);
+
+ rss.fd = params->fd;
+ rss.tgt = tgt;
+ rss.p_block = 0;
+ rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
+
+ if (!check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET)) {
+ goto pcdout;
+ }
+
+ if (params->cmdname[0] == 'i') { // imgdiff
+ ApplyImagePatch(params->buffer, blocks * BLOCKSIZE, &patch_value,
+ &RangeSinkWrite, &rss, NULL, NULL);
+ } else {
+ ApplyBSDiffPatch(params->buffer, blocks * BLOCKSIZE, &patch_value,
+ 0, &RangeSinkWrite, &rss, NULL);
+ }
+
+ // We expect the output of the patcher to fill the tgt ranges exactly.
+ if (rss.p_block != tgt->count || rss.p_remain != 0) {
+ fprintf(stderr, "range sink underrun?\n");
+ }
+ } else {
+ fprintf(stderr, "skipping %d blocks already patched to %d [%s]\n",
+ blocks, tgt->size, logparams);
+ }
+ }
+
+ if (params->freestash) {
+ FreeStash(params->stashbase, params->freestash);
+ params->freestash = NULL;
+ }
+
+ params->written += tgt->size;
+ rc = 0;
+
+pcdout:
+ if (logparams) {
+ free(logparams);
+ }
+
+ if (tgt) {
+ free(tgt);
+ }
+
+ return rc;
+}
+
+static int PerformCommandErase(CommandParameters* params) {
+ char* range = NULL;
+ int i;
+ int rc = -1;
+ RangeSet* tgt = NULL;
+ struct stat st;
+ uint64_t blocks[2];
+
+ if (DEBUG_ERASE) {
+ return PerformCommandZero(params);
+ }
+
+ if (!params) {
+ goto pceout;
+ }
+
+ if (fstat(params->fd, &st) == -1) {
+ fprintf(stderr, "failed to fstat device to erase: %s\n", strerror(errno));
+ goto pceout;
+ }
+
+ if (!S_ISBLK(st.st_mode)) {
+ fprintf(stderr, "not a block device; skipping erase\n");
+ goto pceout;
+ }
+
+ range = strtok_r(NULL, " ", ¶ms->cpos);
+
+ if (range == NULL) {
+ fprintf(stderr, "missing target blocks for zero\n");
+ goto pceout;
+ }
+
+ tgt = parse_range(range);
+
+ if (params->canwrite) {
+ fprintf(stderr, " erasing %d blocks\n", tgt->size);
+
+ for (i = 0; i < tgt->count; ++i) {
+ // offset in bytes
+ blocks[0] = tgt->pos[i * 2] * (uint64_t) BLOCKSIZE;
+ // length in bytes
+ blocks[1] = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * (uint64_t) BLOCKSIZE;
+
+ if (ioctl(params->fd, BLKDISCARD, &blocks) == -1) {
+ fprintf(stderr, "BLKDISCARD ioctl failed: %s\n", strerror(errno));
+ goto pceout;
+ }
+ }
+ }
+
+ rc = 0;
+
+pceout:
+ if (tgt) {
+ free(tgt);
+ }
+
+ return rc;
+}
+
+// Definitions for transfer list command functions
+typedef int (*CommandFunction)(CommandParameters*);
+
+typedef struct {
+ const char* name;
+ CommandFunction f;
+} Command;
+
+// CompareCommands and CompareCommandNames are for the hash table
+
+static int CompareCommands(const void* c1, const void* c2) {
+ return strcmp(((const Command*) c1)->name, ((const Command*) c2)->name);
+}
+
+static int CompareCommandNames(const void* c1, const void* c2) {
+ return strcmp(((const Command*) c1)->name, (const char*) c2);
+}
+
+// HashString is used to hash command names for the hash table
+
+static unsigned int HashString(const char *s) {
+ unsigned int hash = 0;
+ if (s) {
+ while (*s) {
+ hash = hash * 33 + *s++;
+ }
+ }
+ return hash;
}
// args:
@@ -378,393 +1518,372 @@
// - new data stream (filename within package.zip)
// - patch stream (filename within package.zip, must be uncompressed)
-Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
- Value* blockdev_filename;
- Value* transfer_list_value;
+static Value* PerformBlockImageUpdate(const char* name, State* state, int argc, Expr* argv[],
+ const Command* commands, int cmdcount, int dryrun) {
+
+ char* line = NULL;
+ char* linesave = NULL;
+ char* logcmd = NULL;
char* transfer_list = NULL;
- Value* new_data_fn;
- Value* patch_data_fn;
- bool success = false;
+ CommandParameters params;
+ const Command* cmd = NULL;
+ const ZipEntry* new_entry = NULL;
+ const ZipEntry* patch_entry = NULL;
+ FILE* cmd_pipe = NULL;
+ HashTable* cmdht = NULL;
+ int i;
+ int res;
+ int rc = -1;
+ int stash_max_blocks = 0;
+ int total_blocks = 0;
+ pthread_attr_t attr;
+ unsigned int cmdhash;
+ UpdaterInfo* ui = NULL;
+ Value* blockdev_filename = NULL;
+ Value* new_data_fn = NULL;
+ Value* patch_data_fn = NULL;
+ Value* transfer_list_value = NULL;
+ ZipArchive* za = NULL;
+
+ memset(¶ms, 0, sizeof(params));
+ params.canwrite = !dryrun;
+
+ fprintf(stderr, "performing %s\n", dryrun ? "verification" : "update");
if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value,
- &new_data_fn, &patch_data_fn) < 0) {
- return NULL;
+ &new_data_fn, &patch_data_fn) < 0) {
+ goto pbiudone;
}
if (blockdev_filename->type != VAL_STRING) {
ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
- goto done;
+ goto pbiudone;
}
if (transfer_list_value->type != VAL_BLOB) {
ErrorAbort(state, "transfer_list argument to %s must be blob", name);
- goto done;
+ goto pbiudone;
}
if (new_data_fn->type != VAL_STRING) {
ErrorAbort(state, "new_data_fn argument to %s must be string", name);
- goto done;
+ goto pbiudone;
}
if (patch_data_fn->type != VAL_STRING) {
ErrorAbort(state, "patch_data_fn argument to %s must be string", name);
- goto done;
+ goto pbiudone;
}
- UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
- FILE* cmd_pipe = ui->cmd_pipe;
+ ui = (UpdaterInfo*) state->cookie;
- ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
+ if (ui == NULL) {
+ goto pbiudone;
+ }
- const ZipEntry* patch_entry = mzFindZipEntry(za, patch_data_fn->data);
+ cmd_pipe = ui->cmd_pipe;
+ za = ui->package_zip;
+
+ if (cmd_pipe == NULL || za == NULL) {
+ goto pbiudone;
+ }
+
+ patch_entry = mzFindZipEntry(za, patch_data_fn->data);
+
if (patch_entry == NULL) {
- ErrorAbort(state, "%s(): no file \"%s\" in package", name, patch_data_fn->data);
- goto done;
+ fprintf(stderr, "%s(): no file \"%s\" in package", name, patch_data_fn->data);
+ goto pbiudone;
}
- uint8_t* patch_start = ((UpdaterInfo*)(state->cookie))->package_zip_addr +
- mzGetZipEntryOffset(patch_entry);
+ params.patch_start = ui->package_zip_addr + mzGetZipEntryOffset(patch_entry);
+ new_entry = mzFindZipEntry(za, new_data_fn->data);
- const ZipEntry* new_entry = mzFindZipEntry(za, new_data_fn->data);
if (new_entry == NULL) {
- ErrorAbort(state, "%s(): no file \"%s\" in package", name, new_data_fn->data);
- goto done;
+ fprintf(stderr, "%s(): no file \"%s\" in package", name, new_data_fn->data);
+ goto pbiudone;
}
- // The transfer list is a text file containing commands to
- // transfer data from one place to another on the target
- // partition. We parse it and execute the commands in order:
- //
- // zero [rangeset]
- // - fill the indicated blocks with zeros
- //
- // new [rangeset]
- // - fill the blocks with data read from the new_data file
- //
- // erase [rangeset]
- // - mark the given blocks as empty
- //
- // move <...>
- // bsdiff <patchstart> <patchlen> <...>
- // imgdiff <patchstart> <patchlen> <...>
- // - read the source blocks, apply a patch (or not in the
- // case of move), write result to target blocks. bsdiff or
- // imgdiff specifies the type of patch; move means no patch
- // at all.
- //
- // The format of <...> differs between versions 1 and 2;
- // see the LoadSrcTgtVersion{1,2}() functions for a
- // description of what's expected.
- //
- // stash <stash_id> <src_range>
- // - (version 2 only) load the given source range and stash
- // the data in the given slot of the stash table.
- //
- // The creator of the transfer list will guarantee that no block
- // is read (ie, used as the source for a patch or move) after it
- // has been written.
- //
- // In version 2, the creator will guarantee that a given stash is
- // loaded (with a stash command) before it's used in a
- // move/bsdiff/imgdiff command.
- //
- // Within one command the source and target ranges may overlap so
- // in general we need to read the entire source into memory before
- // writing anything to the target blocks.
- //
- // All the patch data is concatenated into one patch_data file in
- // the update package. It must be stored uncompressed because we
- // memory-map it in directly from the archive. (Since patches are
- // already compressed, we lose very little by not compressing
- // their concatenation.)
+ params.fd = TEMP_FAILURE_RETRY(open(blockdev_filename->data, O_RDWR));
- pthread_t new_data_thread;
- NewThreadInfo nti;
- nti.za = za;
- nti.entry = new_entry;
- nti.rss = NULL;
- pthread_mutex_init(&nti.mu, NULL);
- pthread_cond_init(&nti.cv, NULL);
-
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
- pthread_create(&new_data_thread, &attr, unzip_new_data, &nti);
-
- int i, j;
-
- char* linesave;
- char* wordsave;
-
- int fd = open(blockdev_filename->data, O_RDWR);
- if (fd < 0) {
- ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno));
- goto done;
+ if (params.fd == -1) {
+ fprintf(stderr, "open \"%s\" failed: %s\n", blockdev_filename->data, strerror(errno));
+ goto pbiudone;
}
- char* line;
- char* word;
+ if (params.canwrite) {
+ params.nti.za = za;
+ params.nti.entry = new_entry;
- // The data in transfer_list_value is not necessarily
- // null-terminated, so we need to copy it to a new buffer and add
- // the null that strtok_r will need.
- transfer_list = malloc(transfer_list_value->size+1);
+ pthread_mutex_init(¶ms.nti.mu, NULL);
+ pthread_cond_init(¶ms.nti.cv, NULL);
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ int error = pthread_create(¶ms.thread, &attr, unzip_new_data, ¶ms.nti);
+ if (error != 0) {
+ fprintf(stderr, "pthread_create failed: %s\n", strerror(error));
+ goto pbiudone;
+ }
+ }
+
+ // The data in transfer_list_value is not necessarily null-terminated, so we need
+ // to copy it to a new buffer and add the null that strtok_r will need.
+ transfer_list = malloc(transfer_list_value->size + 1);
+
if (transfer_list == NULL) {
fprintf(stderr, "failed to allocate %zd bytes for transfer list\n",
- transfer_list_value->size+1);
- exit(1);
+ transfer_list_value->size + 1);
+ goto pbiudone;
}
+
memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size);
transfer_list[transfer_list_value->size] = '\0';
+ // First line in transfer list is the version number
line = strtok_r(transfer_list, "\n", &linesave);
+ params.version = strtol(line, NULL, 0);
- int version;
- // first line in transfer list is the version number; currently
- // there's only version 1.
- if (strcmp(line, "1") == 0) {
- version = 1;
- } else if (strcmp(line, "2") == 0) {
- version = 2;
- } else {
- ErrorAbort(state, "unexpected transfer list version [%s]\n", line);
- goto done;
+ if (params.version < 1 || params.version > 3) {
+ fprintf(stderr, "unexpected transfer list version [%s]\n", line);
+ goto pbiudone;
}
- printf("blockimg version is %d\n", version);
- // second line in transfer list is the total number of blocks we
- // expect to write.
+ fprintf(stderr, "blockimg version is %d\n", params.version);
+
+ // Second line in transfer list is the total number of blocks we expect to write
line = strtok_r(NULL, "\n", &linesave);
- int total_blocks = strtol(line, NULL, 0);
- // shouldn't happen, but avoid divide by zero.
- if (total_blocks == 0) ++total_blocks;
- int blocks_so_far = 0;
+ total_blocks = strtol(line, NULL, 0);
- uint8_t** stash_table = NULL;
- if (version >= 2) {
- // Next line is how many stash entries are needed simultaneously.
+ if (total_blocks < 0) {
+ ErrorAbort(state, "unexpected block count [%s]\n", line);
+ goto pbiudone;
+ } else if (total_blocks == 0) {
+ rc = 0;
+ goto pbiudone;
+ }
+
+ if (params.version >= 2) {
+ // Third line is how many stash entries are needed simultaneously
line = strtok_r(NULL, "\n", &linesave);
- int stash_entries = strtol(line, NULL, 0);
+ fprintf(stderr, "maximum stash entries %s\n", line);
- stash_table = (uint8_t**) calloc(stash_entries, sizeof(uint8_t*));
- if (stash_table == NULL) {
- fprintf(stderr, "failed to allocate %d-entry stash table\n", stash_entries);
- exit(1);
+ // Fourth line is the maximum number of blocks that will be stashed simultaneously
+ line = strtok_r(NULL, "\n", &linesave);
+ stash_max_blocks = strtol(line, NULL, 0);
+
+ if (stash_max_blocks < 0) {
+ ErrorAbort(state, "unexpected maximum stash blocks [%s]\n", line);
+ goto pbiudone;
}
- // Next line is the maximum number of blocks that will be
- // stashed simultaneously. This could be used to verify that
- // enough memory or scratch disk space is available.
- line = strtok_r(NULL, "\n", &linesave);
- int stash_max_blocks = strtol(line, NULL, 0);
+ if (stash_max_blocks >= 0) {
+ res = CreateStash(state, stash_max_blocks, blockdev_filename->data,
+ ¶ms.stashbase);
+
+ if (res == -1) {
+ goto pbiudone;
+ }
+
+ params.createdstash = res;
+ }
}
- uint8_t* buffer = NULL;
- size_t buffer_alloc = 0;
+ // Build a hash table of the available commands
+ cmdht = mzHashTableCreate(cmdcount, NULL);
- // third and subsequent lines are all individual transfer commands.
+ for (i = 0; i < cmdcount; ++i) {
+ cmdhash = HashString(commands[i].name);
+ mzHashTableLookup(cmdht, cmdhash, (void*) &commands[i], CompareCommands, true);
+ }
+
+ // Subsequent lines are all individual transfer commands
for (line = strtok_r(NULL, "\n", &linesave); line;
line = strtok_r(NULL, "\n", &linesave)) {
- char* style;
- style = strtok_r(line, " ", &wordsave);
+ logcmd = strdup(line);
+ params.cmdname = strtok_r(line, " ", ¶ms.cpos);
- if (strcmp("move", style) == 0) {
- RangeSet* tgt;
- int src_blocks;
- if (version == 1) {
- LoadSrcTgtVersion1(wordsave, &tgt, &src_blocks,
- &buffer, &buffer_alloc, fd);
- } else if (version == 2) {
- LoadSrcTgtVersion2(wordsave, &tgt, &src_blocks,
- &buffer, &buffer_alloc, fd, stash_table);
- }
+ if (params.cmdname == NULL) {
+ fprintf(stderr, "missing command [%s]\n", line);
+ goto pbiudone;
+ }
- printf(" moving %d blocks\n", src_blocks);
+ cmdhash = HashString(params.cmdname);
+ cmd = (const Command*) mzHashTableLookup(cmdht, cmdhash, params.cmdname,
+ CompareCommandNames, false);
- size_t p = 0;
- for (i = 0; i < tgt->count; ++i) {
- check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET);
- size_t sz = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE;
- writeblock(fd, buffer+p, sz);
- p += sz;
- }
+ if (cmd == NULL) {
+ fprintf(stderr, "unexpected command [%s]\n", params.cmdname);
+ goto pbiudone;
+ }
- blocks_so_far += tgt->size;
- fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
+ if (cmd->f != NULL && cmd->f(¶ms) == -1) {
+ fprintf(stderr, "failed to execute command [%s]\n",
+ logcmd ? logcmd : params.cmdname);
+ goto pbiudone;
+ }
+
+ if (logcmd) {
+ free(logcmd);
+ logcmd = NULL;
+ }
+
+ if (params.canwrite) {
+ fprintf(cmd_pipe, "set_progress %.4f\n", (double) params.written / total_blocks);
fflush(cmd_pipe);
-
- free(tgt);
-
- } else if (strcmp("stash", style) == 0) {
- word = strtok_r(NULL, " ", &wordsave);
- int stash_id = strtol(word, NULL, 0);
- int src_blocks;
- size_t stash_alloc = 0;
-
- // Even though the "stash" style only appears in version
- // 2, the version 1 source loader happens to do exactly
- // what we want to read data into the stash_table.
- LoadSrcTgtVersion1(wordsave, NULL, &src_blocks,
- stash_table + stash_id, &stash_alloc, fd);
-
- } else if (strcmp("zero", style) == 0 ||
- (DEBUG_ERASE && strcmp("erase", style) == 0)) {
- word = strtok_r(NULL, " ", &wordsave);
- RangeSet* tgt = parse_range(word);
-
- printf(" zeroing %d blocks\n", tgt->size);
-
- allocate(BLOCKSIZE, &buffer, &buffer_alloc);
- memset(buffer, 0, BLOCKSIZE);
- for (i = 0; i < tgt->count; ++i) {
- check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET);
- for (j = tgt->pos[i*2]; j < tgt->pos[i*2+1]; ++j) {
- writeblock(fd, buffer, BLOCKSIZE);
- }
- }
-
- if (style[0] == 'z') { // "zero" but not "erase"
- blocks_so_far += tgt->size;
- fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
- fflush(cmd_pipe);
- }
-
- free(tgt);
- } else if (strcmp("new", style) == 0) {
-
- word = strtok_r(NULL, " ", &wordsave);
- RangeSet* tgt = parse_range(word);
-
- printf(" writing %d blocks of new data\n", tgt->size);
-
- RangeSinkState rss;
- rss.fd = fd;
- rss.tgt = tgt;
- rss.p_block = 0;
- rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
- check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET);
-
- pthread_mutex_lock(&nti.mu);
- nti.rss = &rss;
- pthread_cond_broadcast(&nti.cv);
- while (nti.rss) {
- pthread_cond_wait(&nti.cv, &nti.mu);
- }
- pthread_mutex_unlock(&nti.mu);
-
- blocks_so_far += tgt->size;
- fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
- fflush(cmd_pipe);
-
- free(tgt);
-
- } else if (strcmp("bsdiff", style) == 0 ||
- strcmp("imgdiff", style) == 0) {
- word = strtok_r(NULL, " ", &wordsave);
- size_t patch_offset = strtoul(word, NULL, 0);
- word = strtok_r(NULL, " ", &wordsave);
- size_t patch_len = strtoul(word, NULL, 0);
-
- RangeSet* tgt;
- int src_blocks;
- if (version == 1) {
- LoadSrcTgtVersion1(wordsave, &tgt, &src_blocks,
- &buffer, &buffer_alloc, fd);
- } else if (version == 2) {
- LoadSrcTgtVersion2(wordsave, &tgt, &src_blocks,
- &buffer, &buffer_alloc, fd, stash_table);
- }
-
- printf(" patching %d blocks to %d\n", src_blocks, tgt->size);
-
- Value patch_value;
- patch_value.type = VAL_BLOB;
- patch_value.size = patch_len;
- patch_value.data = (char*)(patch_start + patch_offset);
-
- RangeSinkState rss;
- rss.fd = fd;
- rss.tgt = tgt;
- rss.p_block = 0;
- rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
- check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET);
-
- int ret;
- if (style[0] == 'i') { // imgdiff
- ret = ApplyImagePatch(buffer, src_blocks * BLOCKSIZE,
- &patch_value,
- &RangeSinkWrite, &rss, NULL, NULL);
- } else {
- ret = ApplyBSDiffPatch(buffer, src_blocks * BLOCKSIZE,
- &patch_value, 0,
- &RangeSinkWrite, &rss, NULL);
- }
-
- if (ret != 0) {
- ErrorAbort(state, "patch failed\n");
- goto done;
- }
-
- // We expect the output of the patcher to fill the tgt ranges exactly.
- if (rss.p_block != tgt->count || rss.p_remain != 0) {
- ErrorAbort(state, "range sink underrun?\n");
- goto done;
- }
-
- blocks_so_far += tgt->size;
- fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
- fflush(cmd_pipe);
-
- free(tgt);
- } else if (!DEBUG_ERASE && strcmp("erase", style) == 0) {
- struct stat st;
- if (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode)) {
- word = strtok_r(NULL, " ", &wordsave);
- RangeSet* tgt = parse_range(word);
-
- printf(" erasing %d blocks\n", tgt->size);
-
- for (i = 0; i < tgt->count; ++i) {
- uint64_t range[2];
- // offset in bytes
- range[0] = tgt->pos[i*2] * (uint64_t)BLOCKSIZE;
- // len in bytes
- range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * (uint64_t)BLOCKSIZE;
-
- if (ioctl(fd, BLKDISCARD, &range) < 0) {
- ErrorAbort(state, " blkdiscard failed: %s\n", strerror(errno));
- goto done;
- }
- }
-
- free(tgt);
- } else {
- printf(" ignoring erase (not block device)\n");
- }
- } else {
- ErrorAbort(state, "unknown transfer style \"%s\"\n", style);
- goto done;
}
}
- pthread_join(new_data_thread, NULL);
- success = true;
+ if (params.canwrite) {
+ pthread_join(params.thread, NULL);
- free(buffer);
- printf("wrote %d blocks; expected %d\n", blocks_so_far, total_blocks);
- printf("max alloc needed was %zu\n", buffer_alloc);
+ fprintf(stderr, "wrote %d blocks; expected %d\n", params.written, total_blocks);
+ fprintf(stderr, "max alloc needed was %zu\n", params.bufsize);
-done:
- free(transfer_list);
- FreeValue(blockdev_filename);
- FreeValue(transfer_list_value);
- FreeValue(new_data_fn);
- FreeValue(patch_data_fn);
- if (success) {
- return StringValue(strdup("t"));
+ // Delete stash only after successfully completing the update, as it
+ // may contain blocks needed to complete the update later.
+ DeleteStash(params.stashbase);
} else {
- // NULL will be passed to its caller at Evaluate() and abort the OTA
- // process.
- return NULL;
+ fprintf(stderr, "verified partition contents; update may be resumed\n");
}
+
+ rc = 0;
+
+pbiudone:
+ if (params.fd != -1) {
+ if (fsync(params.fd) == -1) {
+ fprintf(stderr, "fsync failed: %s\n", strerror(errno));
+ }
+ close(params.fd);
+ }
+
+ if (logcmd) {
+ free(logcmd);
+ }
+
+ if (cmdht) {
+ mzHashTableFree(cmdht);
+ }
+
+ if (params.buffer) {
+ free(params.buffer);
+ }
+
+ if (transfer_list) {
+ free(transfer_list);
+ }
+
+ if (blockdev_filename) {
+ FreeValue(blockdev_filename);
+ }
+
+ if (transfer_list_value) {
+ FreeValue(transfer_list_value);
+ }
+
+ if (new_data_fn) {
+ FreeValue(new_data_fn);
+ }
+
+ if (patch_data_fn) {
+ FreeValue(patch_data_fn);
+ }
+
+ // Only delete the stash if the update cannot be resumed, or it's
+ // a verification run and we created the stash.
+ if (params.isunresumable || (!params.canwrite && params.createdstash)) {
+ DeleteStash(params.stashbase);
+ }
+
+ if (params.stashbase) {
+ free(params.stashbase);
+ }
+
+ return StringValue(rc == 0 ? strdup("t") : strdup(""));
+}
+
+// The transfer list is a text file containing commands to
+// transfer data from one place to another on the target
+// partition. We parse it and execute the commands in order:
+//
+// zero [rangeset]
+// - fill the indicated blocks with zeros
+//
+// new [rangeset]
+// - fill the blocks with data read from the new_data file
+//
+// erase [rangeset]
+// - mark the given blocks as empty
+//
+// move <...>
+// bsdiff <patchstart> <patchlen> <...>
+// imgdiff <patchstart> <patchlen> <...>
+// - read the source blocks, apply a patch (or not in the
+// case of move), write result to target blocks. bsdiff or
+// imgdiff specifies the type of patch; move means no patch
+// at all.
+//
+// The format of <...> differs between versions 1 and 2;
+// see the LoadSrcTgtVersion{1,2}() functions for a
+// description of what's expected.
+//
+// stash <stash_id> <src_range>
+// - (version 2+ only) load the given source range and stash
+// the data in the given slot of the stash table.
+//
+// The creator of the transfer list will guarantee that no block
+// is read (ie, used as the source for a patch or move) after it
+// has been written.
+//
+// In version 2, the creator will guarantee that a given stash is
+// loaded (with a stash command) before it's used in a
+// move/bsdiff/imgdiff command.
+//
+// Within one command the source and target ranges may overlap so
+// in general we need to read the entire source into memory before
+// writing anything to the target blocks.
+//
+// All the patch data is concatenated into one patch_data file in
+// the update package. It must be stored uncompressed because we
+// memory-map it in directly from the archive. (Since patches are
+// already compressed, we lose very little by not compressing
+// their concatenation.)
+//
+// In version 3, commands that read data from the partition (i.e.
+// move/bsdiff/imgdiff/stash) have one or more additional hashes
+// before the range parameters, which are used to check if the
+// command has already been completed and verify the integrity of
+// the source data.
+
+Value* BlockImageVerifyFn(const char* name, State* state, int argc, Expr* argv[]) {
+ // Commands which are not tested are set to NULL to skip them completely
+ const Command commands[] = {
+ { "bsdiff", PerformCommandDiff },
+ { "erase", NULL },
+ { "free", PerformCommandFree },
+ { "imgdiff", PerformCommandDiff },
+ { "move", PerformCommandMove },
+ { "new", NULL },
+ { "stash", PerformCommandStash },
+ { "zero", NULL }
+ };
+
+ // Perform a dry run without writing to test if an update can proceed
+ return PerformBlockImageUpdate(name, state, argc, argv, commands,
+ sizeof(commands) / sizeof(commands[0]), 1);
+}
+
+Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
+ const Command commands[] = {
+ { "bsdiff", PerformCommandDiff },
+ { "erase", PerformCommandErase },
+ { "free", PerformCommandFree },
+ { "imgdiff", PerformCommandDiff },
+ { "move", PerformCommandMove },
+ { "new", PerformCommandNew },
+ { "stash", PerformCommandStash },
+ { "zero", PerformCommandZero }
+ };
+
+ return PerformBlockImageUpdate(name, state, argc, argv, commands,
+ sizeof(commands) / sizeof(commands[0]), 0);
}
Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) {
@@ -786,7 +1905,7 @@
int fd = open(blockdev_filename->data, O_RDWR);
if (fd < 0) {
- ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno));
+ ErrorAbort(state, "open \"%s\" failed: %s", blockdev_filename->data, strerror(errno));
goto done;
}
@@ -798,26 +1917,37 @@
int i, j;
for (i = 0; i < rs->count; ++i) {
- check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET);
+ if (!check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET)) {
+ ErrorAbort(state, "failed to seek %s: %s", blockdev_filename->data,
+ strerror(errno));
+ goto done;
+ }
+
for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) {
- readblock(fd, buffer, BLOCKSIZE);
+ if (read_all(fd, buffer, BLOCKSIZE) == -1) {
+ ErrorAbort(state, "failed to read %s: %s", blockdev_filename->data,
+ strerror(errno));
+ goto done;
+ }
+
SHA_update(&ctx, buffer, BLOCKSIZE);
}
}
digest = SHA_final(&ctx);
close(fd);
-done:
+ done:
FreeValue(blockdev_filename);
FreeValue(ranges);
if (digest == NULL) {
- return NULL;
+ return StringValue(strdup(""));
} else {
return StringValue(PrintSha1(digest));
}
}
void RegisterBlockImageFunctions() {
+ RegisterFunction("block_image_verify", BlockImageVerifyFn);
RegisterFunction("block_image_update", BlockImageUpdateFn);
RegisterFunction("range_sha1", RangeSha1Fn);
}
diff --git a/updater/install.c b/updater/install.c
index 2b2ffb0..01a5dd2 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -496,7 +496,7 @@
struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
bool success = mzExtractRecursive(za, zip_path, dest_path,
- MZ_EXTRACT_FILES_ONLY, ×tamp,
+ ×tamp,
NULL, NULL, sehandle);
free(zip_path);
free(dest_path);
diff --git a/updater/updater.c b/updater/updater.c
index 465e123..661f695 100644
--- a/updater/updater.c
+++ b/updater/updater.c
@@ -17,6 +17,7 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
+#include <string.h>
#include "edify/expr.h"
#include "updater.h"
diff --git a/verifier.cpp b/verifier.cpp
index 3d4f603..bf7071d 100644
--- a/verifier.cpp
+++ b/verifier.cpp
@@ -26,9 +26,10 @@
#include "mincrypt/sha.h"
#include "mincrypt/sha256.h"
-#include <string.h>
-#include <stdio.h>
#include <errno.h>
+#include <malloc.h>
+#include <stdio.h>
+#include <string.h>
extern RecoveryUI* ui;
diff --git a/verifier_test.cpp b/verifier_test.cpp
index 10a5dda..82546ed 100644
--- a/verifier_test.cpp
+++ b/verifier_test.cpp
@@ -14,12 +14,14 @@
* limitations under the License.
*/
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stdarg.h>
+#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <fcntl.h>
#include "common.h"
#include "verifier.h"
@@ -122,6 +124,8 @@
// nothing but print.
class FakeUI : public RecoveryUI {
void Init() { }
+ void SetStage(int, int) { }
+ void SetLocale(const char*) { }
void SetBackground(Icon icon) { }
void SetProgressType(ProgressType determinate) { }
@@ -137,6 +141,7 @@
vfprintf(stderr, fmt, ap);
va_end(ap);
}
+ void ShowFile(const char*) { }
void StartMenu(const char* const * headers, const char* const * items,
int initial_selection) { }