Merge "Add an IMPL_DEFINED pixel format." into jb-mr1-dev
diff --git a/adb/Android.mk b/adb/Android.mk
index 1a25106..681c7c7 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -17,32 +17,34 @@
   USB_SRCS := usb_linux.c
   EXTRA_SRCS := get_my_path_linux.c
   LOCAL_LDLIBS += -lrt -lncurses -lpthread
+  LOCAL_SHARED_LIBRARIES := libcrypto
 endif
 
 ifeq ($(HOST_OS),darwin)
   USB_SRCS := usb_osx.c
   EXTRA_SRCS := get_my_path_darwin.c
-  LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+  LOCAL_LDLIBS += -lpthread -lcrypto -framework CoreFoundation -framework IOKit -framework Carbon
 endif
 
 ifeq ($(HOST_OS),freebsd)
   USB_SRCS := usb_libusb.c
   EXTRA_SRCS := get_my_path_freebsd.c
   LOCAL_LDLIBS += -lpthread -lusb
+  LOCAL_SHARED_LIBRARIES := libcrypto
 endif
 
 ifeq ($(HOST_OS),windows)
   USB_SRCS := usb_windows.c
-  EXTRA_SRCS := get_my_path_windows.c
-  EXTRA_STATIC_LIBS := AdbWinApi
+  EXTRA_SRCS := get_my_path_windows.c ../libcutils/list.c
+  EXTRA_STATIC_LIBS := AdbWinApi libcrypto_static
   ifneq ($(strip $(USE_CYGWIN)),)
     # Pure cygwin case
-    LOCAL_LDLIBS += -lpthread
+    LOCAL_LDLIBS += -lpthread -lgdi32
     LOCAL_C_INCLUDES += /usr/include/w32api/ddk
   endif
   ifneq ($(strip $(USE_MINGW)),)
     # MinGW under Linux case
-    LOCAL_LDLIBS += -lws2_32
+    LOCAL_LDLIBS += -lws2_32 -lgdi32
     USE_SYSDEPS_WIN32 := 1
     LOCAL_C_INCLUDES += /usr/i586-mingw32msvc/include/ddk
   endif
@@ -57,6 +59,7 @@
 	transport_usb.c \
 	commandline.c \
 	adb_client.c \
+	adb_auth_host.c \
 	sockets.c \
 	services.c \
 	file_sync_client.c \
@@ -65,6 +68,7 @@
 	utils.c \
 	usb_vendors.c
 
+LOCAL_C_INCLUDES += external/openssl/include
 
 ifneq ($(USE_SYSDEPS_WIN32),)
   LOCAL_SRC_FILES += sysdeps_win32.c
@@ -104,6 +108,7 @@
 	transport.c \
 	transport_local.c \
 	transport_usb.c \
+	adb_auth_client.c \
 	sockets.c \
 	services.c \
 	file_sync_service.c \
@@ -127,7 +132,7 @@
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
 LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
 
-LOCAL_STATIC_LIBRARIES := libcutils libc
+LOCAL_STATIC_LIBRARIES := libcutils libc libmincrypt
 include $(BUILD_EXECUTABLE)
 
 
@@ -146,6 +151,7 @@
 	transport_usb.c \
 	commandline.c \
 	adb_client.c \
+	adb_auth_host.c \
 	sockets.c \
 	services.c \
 	file_sync_client.c \
@@ -165,9 +171,13 @@
 	-D_XOPEN_SOURCE \
 	-D_GNU_SOURCE
 
+LOCAL_C_INCLUDES += external/openssl/include
+
 LOCAL_MODULE := adb
 
 LOCAL_STATIC_LIBRARIES := libzipfile libunz libcutils
 
+LOCAL_SHARED_LIBRARIES := libcrypto
+
 include $(BUILD_EXECUTABLE)
 endif
diff --git a/adb/adb.c b/adb/adb.c
index 95d3921..e7d9485 100644
--- a/adb/adb.c
+++ b/adb/adb.c
@@ -28,6 +28,7 @@
 
 #include "sysdeps.h"
 #include "adb.h"
+#include "adb_auth.h"
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
 
@@ -46,6 +47,8 @@
 
 int HOST = 0;
 
+static int auth_enabled = 0;
+
 #if !ADB_HOST
 static const char *adb_device_banner = "device";
 #endif
@@ -100,6 +103,7 @@
         { "transport", TRACE_TRANSPORT },
         { "jdwp", TRACE_JDWP },
         { "services", TRACE_SERVICES },
+        { "auth", TRACE_AUTH },
         { NULL, 0 }
     };
 
@@ -203,19 +207,21 @@
     free(p);
 }
 
-void handle_online(void)
+void handle_online(atransport *t)
 {
     D("adb: online\n");
+    t->online = 1;
 }
 
 void handle_offline(atransport *t)
 {
     D("adb: offline\n");
     //Close the associated usb
+    t->online = 0;
     run_transport_disconnects(t);
 }
 
-#if TRACE_PACKETS
+#if DEBUG_PACKETS
 #define DUMPMAX 32
 void print_packet(const char *label, apacket *p)
 {
@@ -230,6 +236,7 @@
     case A_OKAY: tag = "OKAY"; break;
     case A_CLSE: tag = "CLSE"; break;
     case A_WRTE: tag = "WRTE"; break;
+    case A_AUTH: tag = "AUTH"; break;
     default: tag = "????"; break;
     }
 
@@ -251,7 +258,7 @@
         }
         x++;
     }
-    fprintf(stderr, tag);
+    fputs(tag, stderr);
 }
 #endif
 
@@ -315,11 +322,70 @@
     cp->msg.data_length = fill_connect_data((char *)cp->data,
                                             sizeof(cp->data));
     send_packet(cp, t);
-#if ADB_HOST
-        /* XXX why sleep here? */
-    // allow the device some time to respond to the connect message
-    adb_sleep_ms(1000);
-#endif
+}
+
+static void send_auth_request(atransport *t)
+{
+    D("Calling send_auth_request\n");
+    apacket *p;
+    int ret;
+
+    ret = adb_auth_generate_token(t->token, sizeof(t->token));
+    if (ret != sizeof(t->token)) {
+        D("Error generating token ret=%d\n", ret);
+        return;
+    }
+
+    p = get_apacket();
+    memcpy(p->data, t->token, ret);
+    p->msg.command = A_AUTH;
+    p->msg.arg0 = ADB_AUTH_TOKEN;
+    p->msg.data_length = ret;
+    send_packet(p, t);
+}
+
+static void send_auth_response(uint8_t *token, size_t token_size, atransport *t)
+{
+    D("Calling send_auth_response\n");
+    apacket *p = get_apacket();
+    int ret;
+
+    ret = adb_auth_sign(t->key, token, token_size, p->data);
+    if (!ret) {
+        D("Error signing the token\n");
+        put_apacket(p);
+        return;
+    }
+
+    p->msg.command = A_AUTH;
+    p->msg.arg0 = ADB_AUTH_SIGNATURE;
+    p->msg.data_length = ret;
+    send_packet(p, t);
+}
+
+static void send_auth_publickey(atransport *t)
+{
+    D("Calling send_auth_publickey\n");
+    apacket *p = get_apacket();
+    int ret;
+
+    ret = adb_auth_get_userkey(p->data, sizeof(p->data));
+    if (!ret) {
+        D("Failed to get user public key\n");
+        put_apacket(p);
+        return;
+    }
+
+    p->msg.command = A_AUTH;
+    p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY;
+    p->msg.data_length = ret;
+    send_packet(p, t);
+}
+
+void adb_auth_verified(atransport *t)
+{
+    handle_online(t);
+    send_connect(t);
 }
 
 static char *connection_state_name(atransport *t)
@@ -451,13 +517,42 @@
             t->connection_state = CS_OFFLINE;
             handle_offline(t);
         }
+
         parse_banner((char*) p->data, t);
-        handle_online();
-        if(!HOST) send_connect(t);
+
+        if (HOST || !auth_enabled) {
+            handle_online(t);
+            if(!HOST) send_connect(t);
+        } else {
+            send_auth_request(t);
+        }
+        break;
+
+    case A_AUTH:
+        if (p->msg.arg0 == ADB_AUTH_TOKEN) {
+            t->key = adb_auth_nextkey(t->key);
+            if (t->key) {
+                send_auth_response(p->data, p->msg.data_length, t);
+            } else {
+                /* No more private keys to try, send the public key */
+                send_auth_publickey(t);
+            }
+        } else if (p->msg.arg0 == ADB_AUTH_SIGNATURE) {
+            if (adb_auth_verify(t->token, p->data, p->msg.data_length)) {
+                adb_auth_verified(t);
+                t->failed_auth_attempts = 0;
+            } else {
+                if (t->failed_auth_attempts++ > 10)
+                    adb_sleep_ms(1000);
+                send_auth_request(t);
+            }
+        } else if (p->msg.arg0 == ADB_AUTH_RSAPUBLICKEY) {
+            adb_auth_confirm_key(p->data, p->msg.data_length, t);
+        }
         break;
 
     case A_OPEN: /* OPEN(local-id, 0, "destination") */
-        if(t->connection_state != CS_OFFLINE) {
+        if (t->online) {
             char *name = (char*) p->data;
             name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;
             s = create_local_service_socket(name);
@@ -473,7 +568,7 @@
         break;
 
     case A_OKAY: /* READY(local-id, remote-id, "") */
-        if(t->connection_state != CS_OFFLINE) {
+        if (t->online) {
             if((s = find_local_socket(p->msg.arg1))) {
                 if(s->peer == 0) {
                     s->peer = create_remote_socket(p->msg.arg0, t);
@@ -485,7 +580,7 @@
         break;
 
     case A_CLSE: /* CLOSE(local-id, remote-id, "") */
-        if(t->connection_state != CS_OFFLINE) {
+        if (t->online) {
             if((s = find_local_socket(p->msg.arg1))) {
                 s->close(s);
             }
@@ -493,7 +588,7 @@
         break;
 
     case A_WRTE:
-        if(t->connection_state != CS_OFFLINE) {
+        if (t->online) {
             if((s = find_local_socket(p->msg.arg1))) {
                 unsigned rid = p->msg.arg0;
                 p->len = p->msg.data_length;
@@ -1014,6 +1109,7 @@
     usb_vendors_init();
     usb_init();
     local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+    adb_auth_init();
 
     char local_name[30];
     build_local_name(local_name, sizeof(local_name), server_port);
@@ -1021,10 +1117,10 @@
         exit(1);
     }
 #else
-
-    // Our external storage path may be different than apps, since
-    // we aren't able to bind mount after dropping root.
-    setenv("EXTERNAL_STORAGE", getenv("ADB_EXTERNAL_STORAGE"), 1);
+    property_get("ro.adb.secure", value, "0");
+    auth_enabled = !strcmp(value, "1");
+    if (auth_enabled)
+        adb_auth_init();
 
     /* don't listen on a port (default 5037) if running in secure mode */
     /* don't run as root if we are running in secure mode */
diff --git a/adb/adb.h b/adb/adb.h
index df88896..5e9a0fb 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -29,13 +29,14 @@
 #define A_OKAY 0x59414b4f
 #define A_CLSE 0x45534c43
 #define A_WRTE 0x45545257
+#define A_AUTH 0x48545541
 
 #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
+#define ADB_SERVER_VERSION    30    // Increment this when we want to force users to start a new adb server
 
 typedef struct amessage amessage;
 typedef struct apacket apacket;
@@ -165,6 +166,8 @@
         kTransportHost,
 } transport_type;
 
+#define TOKEN_SIZE 20
+
 struct atransport
 {
     atransport *next;
@@ -181,6 +184,7 @@
     int ref_count;
     unsigned sync_token;
     int connection_state;
+    int online;
     transport_type type;
 
         /* usb handle or socket fd as needed */
@@ -198,6 +202,11 @@
         /* a list of adisconnect callbacks called when the transport is kicked */
     int          kicked;
     adisconnect  disconnects;
+
+    void *key;
+    unsigned char token[TOKEN_SIZE];
+    fdevent auth_fde;
+    unsigned failed_auth_attempts;
 };
 
 
@@ -349,6 +358,7 @@
     TRACE_SYSDEPS,
     TRACE_JDWP,      /* 0x100 */
     TRACE_SERVICES,
+    TRACE_AUTH,
 } AdbTrace;
 
 #if ADB_TRACE
@@ -408,7 +418,7 @@
 #endif
 
 
-#if !TRACE_PACKETS
+#if !DEBUG_PACKETS
 #define print_packet(tag,p) do {} while (0)
 #endif
 
diff --git a/adb/adb_auth.h b/adb/adb_auth.h
new file mode 100644
index 0000000..1fffa49
--- /dev/null
+++ b/adb/adb_auth.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 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_AUTH_H
+#define __ADB_AUTH_H
+
+void adb_auth_init(void);
+void adb_auth_verified(atransport *t);
+
+/* AUTH packets first argument */
+/* Request */
+#define ADB_AUTH_TOKEN         1
+/* Response */
+#define ADB_AUTH_SIGNATURE     2
+#define ADB_AUTH_RSAPUBLICKEY  3
+
+#if ADB_HOST
+
+int adb_auth_sign(void *key, void *token, size_t token_size, void *sig);
+void *adb_auth_nextkey(void *current);
+int adb_auth_get_userkey(unsigned char *data, size_t len);
+
+static inline int adb_auth_generate_token(void *token, size_t token_size) { return 0; }
+static inline int adb_auth_verify(void *token, void *sig, int siglen) { return 0; }
+static inline void adb_auth_confirm_key(unsigned char *data, size_t len, atransport *t) { }
+static inline void adb_auth_reload_keys(void) { }
+
+#else // !ADB_HOST
+
+static inline int adb_auth_sign(void* key, void *token, size_t token_size, void *sig) { return 0; }
+static inline void *adb_auth_nextkey(void *current) { return NULL; }
+static inline int adb_auth_get_userkey(unsigned char *data, size_t len) { return 0; }
+
+int adb_auth_generate_token(void *token, size_t token_size);
+int adb_auth_verify(void *token, void *sig, int siglen);
+void adb_auth_confirm_key(unsigned char *data, size_t len, atransport *t);
+void adb_auth_reload_keys(void);
+
+#endif // ADB_HOST
+
+#endif // __ADB_AUTH_H
diff --git a/adb/adb_auth_client.c b/adb/adb_auth_client.c
new file mode 100644
index 0000000..0b4913e
--- /dev/null
+++ b/adb/adb_auth_client.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2012 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 <string.h>
+#include <resolv.h>
+#include <cutils/list.h>
+#include <cutils/sockets.h>
+
+#include "sysdeps.h"
+#include "adb.h"
+#include "adb_auth.h"
+#include "fdevent.h"
+#include "mincrypt/rsa.h"
+
+#define TRACE_TAG TRACE_AUTH
+
+
+struct adb_public_key {
+    struct listnode node;
+    RSAPublicKey key;
+};
+
+static struct listnode key_list;
+
+static char *key_paths[] = {
+    "/adb_keys",
+    "/data/misc/adb/adb_keys",
+    NULL
+};
+
+static fdevent listener_fde;
+static int framework_fd = -1;
+
+
+static void read_keys(const char *file, struct listnode *list)
+{
+    struct adb_public_key *key;
+    FILE *f;
+    char buf[MAX_PAYLOAD];
+    char *sep;
+    int ret;
+
+    f = fopen(file, "r");
+    if (!f) {
+        D("Can't open '%s'\n", file);
+        return;
+    }
+
+    while (fgets(buf, sizeof(buf), f)) {
+        /* Allocate 4 extra bytes to decode the base64 data in-place */
+        key = calloc(1, sizeof(*key) + 4);
+        if (!key) {
+            D("Can't malloc key\n");
+            break;
+        }
+
+        sep = strpbrk(buf, " \t");
+        if (sep)
+            *sep = '\0';
+
+        ret = __b64_pton(buf, (u_char *)&key->key, sizeof(key->key) + 4);
+        if (ret != sizeof(key->key)) {
+            D("%s: Invalid base64 data ret=%d\n", file, ret);
+            free(key);
+            continue;
+        }
+
+        if (key->key.len != RSANUMWORDS) {
+            D("%s: Invalid key len %d\n", file, key->key.len);
+            free(key);
+            continue;
+        }
+
+        list_add_tail(list, &key->node);
+    }
+
+    fclose(f);
+}
+
+static void free_keys(struct listnode *list)
+{
+    struct listnode *item;
+
+    while (!list_empty(list)) {
+        item = list_head(list);
+        list_remove(item);
+        free(node_to_item(item, struct adb_public_key, node));
+    }
+}
+
+void adb_auth_reload_keys(void)
+{
+    char *path;
+    char **paths = key_paths;
+    struct stat buf;
+
+    free_keys(&key_list);
+
+    while ((path = *paths++)) {
+        if (!stat(path, &buf)) {
+            D("Loading keys from '%s'\n", path);
+            read_keys(path, &key_list);
+        }
+    }
+}
+
+int adb_auth_generate_token(void *token, size_t token_size)
+{
+    FILE *f;
+    int ret;
+
+    f = fopen("/dev/urandom", "r");
+    if (!f)
+        return 0;
+
+    ret = fread(token, token_size, 1, f);
+
+    fclose(f);
+    return ret * token_size;
+}
+
+int adb_auth_verify(void *token, void *sig, int siglen)
+{
+    struct listnode *item;
+    struct adb_public_key *key;
+    int ret;
+
+    if (siglen != RSANUMBYTES)
+        return 0;
+
+    list_for_each(item, &key_list) {
+        key = node_to_item(item, struct adb_public_key, node);
+        ret = RSA_verify(&key->key, sig, siglen, token);
+        if (ret)
+            return 1;
+    }
+
+    return 0;
+}
+
+static void adb_auth_event(int fd, unsigned events, void *data)
+{
+    atransport *t = data;
+    char response[2];
+    int ret;
+
+    if (events & FDE_READ) {
+        ret = unix_read(fd, response, sizeof(response));
+        if (ret < 0) {
+            D("Disconnect");
+            fdevent_remove(&t->auth_fde);
+            framework_fd = -1;
+        }
+        else if (ret == 2 && response[0] == 'O' && response[1] == 'K') {
+            adb_auth_reload_keys();
+            adb_auth_verified(t);
+        }
+    }
+}
+
+void adb_auth_confirm_key(unsigned char *key, size_t len, atransport *t)
+{
+    char msg[MAX_PAYLOAD];
+    int ret;
+
+    if (framework_fd < 0) {
+        D("Client not connected\n");
+        return;
+    }
+
+    if (key[len - 1] != '\0') {
+        D("Key must be a null-terminated string\n");
+        return;
+    }
+
+    ret = snprintf(msg, sizeof(msg), "PK%s", key);
+    if (ret >= (signed)sizeof(msg)) {
+        D("Key too long. ret=%d", ret);
+        return;
+    }
+    D("Sending '%s'\n", msg);
+
+    ret = unix_write(framework_fd, msg, ret);
+    if (ret < 0) {
+        D("Failed to write PK, errno=%d\n", errno);
+        return;
+    }
+
+    fdevent_install(&t->auth_fde, framework_fd, adb_auth_event, t);
+    fdevent_add(&t->auth_fde, FDE_READ);
+}
+
+static void adb_auth_listener(int fd, unsigned events, void *data)
+{
+    struct sockaddr addr;
+    socklen_t alen;
+    int s;
+
+    alen = sizeof(addr);
+
+    s = adb_socket_accept(fd, &addr, &alen);
+    if (s < 0) {
+        D("Failed to accept: errno=%d\n", errno);
+        return;
+    }
+
+    framework_fd = s;
+}
+
+void adb_auth_init(void)
+{
+    int fd, ret;
+
+    list_init(&key_list);
+    adb_auth_reload_keys();
+
+    fd = android_get_control_socket("adbd");
+    if (fd < 0) {
+        D("Failed to get adbd socket\n");
+        return;
+    }
+
+    ret = listen(fd, 4);
+    if (ret < 0) {
+        D("Failed to listen on '%d'\n", fd);
+        return;
+    }
+
+    fdevent_install(&listener_fde, fd, adb_auth_listener, NULL);
+    fdevent_add(&listener_fde, FDE_READ);
+}
diff --git a/adb/adb_auth_host.c b/adb/adb_auth_host.c
new file mode 100644
index 0000000..99dcfcb
--- /dev/null
+++ b/adb/adb_auth_host.c
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2012 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>
+
+#ifdef _WIN32
+#  define WIN32_LEAN_AND_MEAN
+#  include "windows.h"
+#  include "shlobj.h"
+#else
+#  include <sys/types.h>
+#  include <sys/stat.h>
+#  include <unistd.h>
+#endif
+#include <string.h>
+
+#include "sysdeps.h"
+#include "adb.h"
+#include "adb_auth.h"
+
+/* HACK: we need the RSAPublicKey struct
+ * but RSA_verify conflits with openssl */
+#define RSA_verify RSA_verify_mincrypt
+#include "mincrypt/rsa.h"
+#undef RSA_verify
+
+#include <cutils/list.h>
+
+#include <openssl/evp.h>
+#include <openssl/objects.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+#define TRACE_TAG TRACE_AUTH
+
+#define ANDROID_PATH   ".android"
+#define ADB_KEY_FILE   "adb_key"
+
+
+struct adb_private_key {
+    struct listnode node;
+    RSA *rsa;
+};
+
+static struct listnode key_list;
+
+
+/* Convert OpenSSL RSA private key to android pre-computed RSAPublicKey format */
+static int RSA_to_RSAPublicKey(RSA *rsa, RSAPublicKey *pkey)
+{
+    int ret = 1;
+    unsigned int i;
+
+    BN_CTX* ctx = BN_CTX_new();
+    BIGNUM* r32 = BN_new();
+    BIGNUM* rr = BN_new();
+    BIGNUM* r = BN_new();
+    BIGNUM* rem = BN_new();
+    BIGNUM* n = BN_new();
+    BIGNUM* n0inv = BN_new();
+
+    if (RSA_size(rsa) != RSANUMBYTES) {
+        ret = 0;
+        goto out;
+    }
+
+    BN_set_bit(r32, 32);
+    BN_copy(n, rsa->n);
+    BN_set_bit(r, RSANUMWORDS * 32);
+    BN_mod_sqr(rr, r, n, ctx);
+    BN_div(NULL, rem, n, r32, ctx);
+    BN_mod_inverse(n0inv, rem, r32, ctx);
+
+    pkey->len = RSANUMWORDS;
+    pkey->n0inv = 0 - BN_get_word(n0inv);
+    for (i = 0; i < RSANUMWORDS; i++) {
+        BN_div(rr, rem, rr, r32, ctx);
+        pkey->rr[i] = BN_get_word(rem);
+        BN_div(n, rem, n, r32, ctx);
+        pkey->n[i] = BN_get_word(rem);
+    }
+    pkey->exponent = BN_get_word(rsa->e);
+
+out:
+    BN_free(n0inv);
+    BN_free(n);
+    BN_free(rem);
+    BN_free(r);
+    BN_free(rr);
+    BN_free(r32);
+    BN_CTX_free(ctx);
+
+    return ret;
+}
+
+static void get_user_info(char *buf, size_t len)
+{
+    char hostname[1024], username[1024];
+    int ret;
+
+#ifndef _WIN32
+    ret = gethostname(hostname, sizeof(hostname));
+    if (ret < 0)
+#endif
+        strcpy(hostname, "unknown");
+
+#if !defined _WIN32 && !defined ADB_HOST_ON_TARGET
+    ret = getlogin_r(username, sizeof(username));
+    if (ret < 0)
+#endif
+        strcpy(username, "unknown");
+
+    ret = snprintf(buf, len, " %s@%s", username, hostname);
+    if (ret >= (signed)len)
+        buf[len - 1] = '\0';
+}
+
+static int write_public_keyfile(RSA *private_key, const char *private_key_path)
+{
+    RSAPublicKey pkey;
+    BIO *bio, *b64, *bfile;
+    char path[PATH_MAX], info[MAX_PAYLOAD];
+    int ret;
+
+    ret = snprintf(path, sizeof(path), "%s.pub", private_key_path);
+    if (ret >= (signed)sizeof(path))
+        return 0;
+
+    ret = RSA_to_RSAPublicKey(private_key, &pkey);
+    if (!ret) {
+        D("Failed to convert to publickey\n");
+        return 0;
+    }
+
+    bfile = BIO_new_file(path, "w");
+    if (!bfile) {
+        D("Failed to open '%s'\n", path);
+        return 0;
+    }
+
+    D("Writing public key to '%s'\n", path);
+
+    b64 = BIO_new(BIO_f_base64());
+    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+
+    bio = BIO_push(b64, bfile);
+    BIO_write(bio, &pkey, sizeof(pkey));
+    BIO_flush(bio);
+    BIO_pop(b64);
+    BIO_free(b64);
+
+    get_user_info(info, sizeof(info));
+    BIO_write(bfile, info, strlen(info));
+    BIO_flush(bfile);
+    BIO_free_all(bfile);
+
+    return 1;
+}
+
+static int generate_key(const char *file)
+{
+    EVP_PKEY* pkey = EVP_PKEY_new();
+    BIGNUM* exponent = BN_new();
+    RSA* rsa = RSA_new();
+    FILE *f = NULL;
+    int ret = 0;
+
+    D("generate_key '%s'\n", file);
+
+    if (!pkey || !exponent || !rsa) {
+        D("Failed to allocate key\n");
+        goto out;
+    }
+
+    BN_set_word(exponent, RSA_F4);
+    RSA_generate_key_ex(rsa, 2048, exponent, NULL);
+    EVP_PKEY_set1_RSA(pkey, rsa);
+
+    f = fopen(file, "w");
+    if (!f) {
+        D("Failed to open '%s'\n", file);
+        goto out;
+    }
+
+    if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
+        D("Failed to write key\n");
+        goto out;
+    }
+
+    if (!write_public_keyfile(rsa, file)) {
+        D("Failed to write public key\n");
+        goto out;
+    }
+
+    ret = 1;
+
+out:
+    if (f)
+        fclose(f);
+    EVP_PKEY_free(pkey);
+    RSA_free(rsa);
+    BN_free(exponent);
+    return ret;
+}
+
+static int read_key(const char *file, struct listnode *list)
+{
+    struct adb_private_key *key;
+    FILE *f;
+
+    D("read_key '%s'\n", file);
+
+    f = fopen(file, "r");
+    if (!f) {
+        D("Failed to open '%s'\n", file);
+        return 0;
+    }
+
+    key = malloc(sizeof(*key));
+    if (!key) {
+        D("Failed to alloc key\n");
+        fclose(f);
+        return 0;
+    }
+    key->rsa = RSA_new();
+
+    if (!PEM_read_RSAPrivateKey(f, &key->rsa, NULL, NULL)) {
+        D("Failed to read key\n");
+        fclose(f);
+        RSA_free(key->rsa);
+        free(key);
+        return 0;
+    }
+
+    fclose(f);
+    list_add_tail(list, &key->node);
+    return 1;
+}
+
+static int get_user_keyfilepath(char *filename, size_t len)
+{
+    const char *format, *home;
+    char android_dir[PATH_MAX];
+    struct stat buf;
+#ifdef _WIN32
+    char path[PATH_MAX];
+    home = getenv("ANDROID_SDK_HOME");
+    if (!home) {
+        SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, path);
+        home = path;
+    }
+    format = "%s\\%s";
+#else
+    home = getenv("HOME");
+    if (!home)
+        return -1;
+    format = "%s/%s";
+#endif
+
+    D("home '%s'\n", home);
+
+    if (snprintf(android_dir, sizeof(android_dir), format, home,
+                        ANDROID_PATH) >= (int)sizeof(android_dir))
+        return -1;
+
+    if (stat(android_dir, &buf)) {
+        if (adb_mkdir(android_dir, 0750) < 0) {
+            D("Cannot mkdir '%s'", android_dir);
+            return -1;
+        }
+    }
+
+    return snprintf(filename, len, format, android_dir, ADB_KEY_FILE);
+}
+
+static int get_user_key(struct listnode *list)
+{
+    struct stat buf;
+    char path[PATH_MAX];
+    int ret;
+
+    ret = get_user_keyfilepath(path, sizeof(path));
+    if (ret < 0 || ret >= (signed)sizeof(path)) {
+        D("Error getting user key filename");
+        return 0;
+    }
+
+    D("user key '%s'\n", path);
+
+    if (stat(path, &buf) == -1) {
+        if (!generate_key(path)) {
+            D("Failed to generate new key\n");
+            return 0;
+        }
+    }
+
+    return read_key(path, list);
+}
+
+static void get_vendor_keys(struct listnode *list)
+{
+    const char *adb_keys_path;
+    char keys_path[MAX_PAYLOAD];
+    char *path;
+    char *save;
+    struct stat buf;
+
+    adb_keys_path = getenv("ADB_VENDOR_KEYS");
+    if (!adb_keys_path)
+        return;
+    strncpy(keys_path, adb_keys_path, sizeof(keys_path));
+
+    path = adb_strtok_r(keys_path, ENV_PATH_SEPARATOR_STR, &save);
+    while (path) {
+        D("Reading: '%s'\n", path);
+
+        if (stat(path, &buf))
+            D("Can't read '%s'\n", path);
+        else if (!read_key(path, list))
+            D("Failed to read '%s'\n", path);
+
+        path = adb_strtok_r(NULL, ENV_PATH_SEPARATOR_STR, &save);
+    }
+}
+
+int adb_auth_sign(void *node, void *token, size_t token_size, void *sig)
+{
+    unsigned int len;
+    struct adb_private_key *key = node_to_item(node, struct adb_private_key, node);
+
+    if (!RSA_sign(NID_sha1, token, token_size, sig, &len, key->rsa)) {
+        return 0;
+    }
+
+    D("adb_auth_sign len=%d\n", len);
+    return (int)len;
+}
+
+void *adb_auth_nextkey(void *current)
+{
+    struct listnode *item;
+
+    if (list_empty(&key_list))
+        return NULL;
+
+    if (!current)
+        return list_head(&key_list);
+
+    list_for_each(item, &key_list) {
+        if (item == current) {
+            /* current is the last item, we tried all the keys */
+            if (item->next == &key_list)
+                return NULL;
+            return item->next;
+        }
+    }
+
+    return NULL;
+}
+
+int adb_auth_get_userkey(unsigned char *data, size_t len)
+{
+    char path[PATH_MAX];
+    char *file;
+    int ret;
+
+    ret = get_user_keyfilepath(path, sizeof(path) - 4);
+    if (ret < 0 || ret >= (signed)(sizeof(path) - 4)) {
+        D("Error getting user key filename");
+        return 0;
+    }
+    strcat(path, ".pub");
+
+    file = load_file(path, (unsigned*)&ret);
+    if (!file) {
+        D("Can't load '%s'\n", path);
+        return 0;
+    }
+
+    if (len < (size_t)(ret + 1)) {
+        D("%s: Content too large ret=%d\n", path, ret);
+        return 0;
+    }
+
+    memcpy(data, file, ret);
+    data[ret] = '\0';
+
+    return ret + 1;
+}
+
+void adb_auth_init(void)
+{
+    int ret;
+
+    D("adb_auth_init\n");
+
+    list_init(&key_list);
+
+    ret = get_user_key(&key_list);
+    if (!ret) {
+        D("Failed to get user key\n");
+        return;
+    }
+
+    get_vendor_keys(&key_list);
+}
diff --git a/adb/commandline.c b/adb/commandline.c
index 10b2332..24cbb5a 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -965,7 +965,7 @@
                 argc--;
                 argv++;
             } else {
-                product = argv[1] + 2;
+                product = argv[0] + 2;
             }
             gProductOutPath = find_product_out_path(product);
             if (gProductOutPath == NULL) {
diff --git a/adb/protocol.txt b/adb/protocol.txt
index abd63f9..c9d3c24 100644
--- a/adb/protocol.txt
+++ b/adb/protocol.txt
@@ -75,6 +75,24 @@
 or identifier string.  The banner is used to transmit useful properties.
 
 
+--- AUTH(type, 0, "data") ----------------------------------------------
+
+The AUTH message informs the recipient that authentication is required to
+connect to the sender. If type is TOKEN(1), data is a random token that
+the recipient can sign with a private key. The recipient replies with an
+AUTH packet where type is SIGNATURE(2) and data is the signature. If the
+signature verification succeeds, the sender replies with a CONNECT packet.
+
+If the signature verification fails, the sender replies with a new AUTH
+packet and a new random token, so that the recipient can retry signing
+with a different private key.
+
+Once the recipient has tried all its private keys, it can reply with an
+AUTH packet where type is RSAPUBLICKEY(3) and data is the public key. If
+possible, an on-screen confirmation may be displayed for the user to
+confirm they want to install the public key on the device.
+
+
 --- OPEN(local-id, 0, "destination") -----------------------------------
 
 The OPEN message informs the recipient that the sender has a stream
@@ -166,6 +184,7 @@
 
 #define A_SYNC 0x434e5953
 #define A_CNXN 0x4e584e43
+#define A_AUTH 0x48545541
 #define A_OPEN 0x4e45504f
 #define A_OKAY 0x59414b4f
 #define A_CLSE 0x45534c43
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 605fa17..66b60cc 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -38,6 +38,7 @@
 
 #define OS_PATH_SEPARATOR '\\'
 #define OS_PATH_SEPARATOR_STR "\\"
+#define ENV_PATH_SEPARATOR_STR ";"
 
 typedef CRITICAL_SECTION          adb_mutex_t;
 
@@ -277,6 +278,7 @@
 
 #define OS_PATH_SEPARATOR '/'
 #define OS_PATH_SEPARATOR_STR "/"
+#define ENV_PATH_SEPARATOR_STR ":"
 
 typedef  pthread_mutex_t          adb_mutex_t;
 
diff --git a/charger/Android.mk b/charger/Android.mk
index 5367a98..de9b184 100644
--- a/charger/Android.mk
+++ b/charger/Android.mk
@@ -17,7 +17,7 @@
 LOCAL_C_INCLUDES := bootable/recovery
 
 LOCAL_STATIC_LIBRARIES := libminui libpixelflinger_static libpng
-LOCAL_STATIC_LIBRARIES += libz libstdc++ libcutils libc
+LOCAL_STATIC_LIBRARIES += libz libstdc++ libcutils libm libc
 
 include $(BUILD_EXECUTABLE)
 
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index db9efbb..6521cbe 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -229,9 +229,6 @@
     { 00644, AID_ROOT,      AID_ROOT,       0 },
 };
 
-#define EXTERNAL_STORAGE_SYSTEM "/mnt/shell/sdcard0"
-#define EXTERNAL_STORAGE_APP "/storage/sdcard0"
-
 static inline void fs_config(const char *path, int dir,
                              unsigned *uid, unsigned *gid, unsigned *mode)
 {
diff --git a/include/sync/sync.h b/include/sync/sync.h
index f015fa1..918acf6 100644
--- a/include/sync/sync.h
+++ b/include/sync/sync.h
@@ -42,7 +42,7 @@
 };
 
 /* timeout in msecs */
-int sync_wait(int fd, unsigned int timeout);
+int sync_wait(int fd, int timeout);
 int sync_merge(const char *name, int fd1, int fd2);
 struct sync_fence_info_data *sync_fence_info(int fd);
 struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
diff --git a/libcorkscrew/arch-arm/backtrace-arm.c b/libcorkscrew/arch-arm/backtrace-arm.c
index 5b91164..ff6c192 100644
--- a/libcorkscrew/arch-arm/backtrace-arm.c
+++ b/libcorkscrew/arch-arm/backtrace-arm.c
@@ -62,21 +62,19 @@
 #include <sys/exec_elf.h>
 #include <cutils/log.h>
 
+#if !defined(__BIONIC_HAVE_UCONTEXT_T)
+/* Old versions of the Android <signal.h> didn't define ucontext_t. */
+#include <asm/sigcontext.h> /* Ensure 'struct sigcontext' is defined. */
+
 /* Machine context at the time a signal was raised. */
 typedef struct ucontext {
     uint32_t uc_flags;
     struct ucontext* uc_link;
     stack_t uc_stack;
-    struct sigcontext {
-        uint32_t trap_no;
-        uint32_t error_code;
-        uint32_t oldmask;
-        uint32_t gregs[16];
-        uint32_t arm_cpsr;
-        uint32_t fault_address;
-    } uc_mcontext;
+    struct sigcontext uc_mcontext;
     uint32_t uc_sigmask;
 } ucontext_t;
+#endif /* !__BIONIC_HAVE_UCONTEXT_T */
 
 /* Unwind state. */
 typedef struct {
@@ -560,9 +558,23 @@
     const ucontext_t* uc = (const ucontext_t*)sigcontext;
 
     unwind_state_t state;
-    for (int i = 0; i < 16; i++) {
-        state.gregs[i] = uc->uc_mcontext.gregs[i];
-    }
+
+    state.gregs[0] = uc->uc_mcontext.arm_r0;
+    state.gregs[1] = uc->uc_mcontext.arm_r1;
+    state.gregs[2] = uc->uc_mcontext.arm_r2;
+    state.gregs[3] = uc->uc_mcontext.arm_r3;
+    state.gregs[4] = uc->uc_mcontext.arm_r4;
+    state.gregs[5] = uc->uc_mcontext.arm_r5;
+    state.gregs[6] = uc->uc_mcontext.arm_r6;
+    state.gregs[7] = uc->uc_mcontext.arm_r7;
+    state.gregs[8] = uc->uc_mcontext.arm_r8;
+    state.gregs[9] = uc->uc_mcontext.arm_r9;
+    state.gregs[10] = uc->uc_mcontext.arm_r10;
+    state.gregs[11] = uc->uc_mcontext.arm_fp;
+    state.gregs[12] = uc->uc_mcontext.arm_ip;
+    state.gregs[13] = uc->uc_mcontext.arm_sp;
+    state.gregs[14] = uc->uc_mcontext.arm_lr;
+    state.gregs[15] = uc->uc_mcontext.arm_pc;
 
     memory_t memory;
     init_memory(&memory, map_info_list);
diff --git a/libcorkscrew/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c
index 6cdb0c8..fb79a0c 100644
--- a/libcorkscrew/arch-x86/backtrace-x86.c
+++ b/libcorkscrew/arch-x86/backtrace-x86.c
@@ -35,18 +35,50 @@
 
 #if defined(__BIONIC__)
 
+#if defined(__BIONIC_HAVE_UCONTEXT_T)
+
 // Bionic offers the Linux kernel headers.
 #include <asm/sigcontext.h>
 #include <asm/ucontext.h>
 typedef struct ucontext ucontext_t;
 
-#else
+#else /* __BIONIC_HAVE_UCONTEXT_T */
+
+/* Old versions of the Android <signal.h> didn't define ucontext_t. */
+
+typedef struct {
+  uint32_t  gregs[32];
+  void*     fpregs;
+  uint32_t  oldmask;
+  uint32_t  cr2;
+} mcontext_t;
+
+enum {
+  REG_GS = 0, REG_FS, REG_ES, REG_DS,
+  REG_EDI, REG_ESI, REG_EBP, REG_ESP,
+  REG_EBX, REG_EDX, REG_ECX, REG_EAX,
+  REG_TRAPNO, REG_ERR, REG_EIP, REG_CS,
+  REG_EFL, REG_UESP, REG_SS
+};
+
+/* Machine context at the time a signal was raised. */
+typedef struct ucontext {
+    uint32_t uc_flags;
+    struct ucontext* uc_link;
+    stack_t uc_stack;
+    mcontext_t uc_mcontext;
+    uint32_t uc_sigmask;
+} ucontext_t;
+
+#endif /* __BIONIC_HAVE_UCONTEXT_T */
+
+#else /* __BIONIC__ */
 
 // glibc has its own renaming of the Linux kernel's structures.
 #define __USE_GNU // For REG_EBP, REG_ESP, and REG_EIP.
 #include <ucontext.h>
 
-#endif
+#endif /* __ BIONIC__ */
 
 /* Unwind state. */
 typedef struct {
@@ -96,15 +128,9 @@
     const ucontext_t* uc = (const ucontext_t*)sigcontext;
 
     unwind_state_t state;
-#if defined(__BIONIC__)
-    state.ebp = uc->uc_mcontext.ebp;
-    state.esp = uc->uc_mcontext.esp;
-    state.eip = uc->uc_mcontext.eip;
-#else
     state.ebp = uc->uc_mcontext.gregs[REG_EBP];
     state.esp = uc->uc_mcontext.gregs[REG_ESP];
     state.eip = uc->uc_mcontext.gregs[REG_EIP];
-#endif
 
     memory_t memory;
     init_memory(&memory, map_info_list);
diff --git a/libsync/sync.c b/libsync/sync.c
index c20f15e..4892866 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -27,9 +27,9 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-int sync_wait(int fd, unsigned int timeout)
+int sync_wait(int fd, int timeout)
 {
-    __u32 to = timeout;
+    __s32 to = timeout;
 
     return ioctl(fd, SYNC_IOC_WAIT, &to);
 }
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
index a75f671..386747a 100644
--- a/libsync/sync_test.c
+++ b/libsync/sync_test.c
@@ -72,7 +72,7 @@
     return NULL;
 }
 
-int main(int argc, char *argv[])
+int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
 {
     struct sync_thread_data sync_data[4];
     pthread_t threads[4];
diff --git a/libzipfile/test_zipfile.c b/libzipfile/test_zipfile.c
index 40840ec..1aaa913 100644
--- a/libzipfile/test_zipfile.c
+++ b/libzipfile/test_zipfile.c
@@ -1,5 +1,5 @@
 #include <zipfile/zipfile.h>

-

+#include <string.h>

 #include <stdio.h>

 #include <stdlib.h>

 

@@ -55,6 +55,8 @@
 

     switch (what)

     {

+        case HUH:

+            break;

         case LIST:

             dump_zipfile(stdout, zip);

             break;

diff --git a/rootdir/init.rc b/rootdir/init.rc
index 1ed1323..ac36ff5 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -34,6 +34,7 @@
     export ANDROID_ROOT /system
     export ANDROID_ASSETS /system/app
     export ANDROID_DATA /data
+    export ANDROID_STORAGE /storage
     export ASEC_MOUNTPOINT /mnt/asec
     export LOOP_MOUNTPOINT /mnt/obb
     export BOOTCLASSPATH /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar
@@ -56,8 +57,9 @@
     mkdir /cache 0770 system cache
     mkdir /config 0500 root root
 
-    # Directory for shell-visible mount points, like external storage
+    # See storage config details at http://source.android.com/tech/storage/
     mkdir /mnt/shell 0700 shell shell
+    mkdir /storage 0050 root sdcard_r
 
     # Directory for putting things only root should see.
     mkdir /mnt/secure 0700 root root
@@ -322,8 +324,12 @@
     setprop net.tcp.buffersize.lte     524288,1048576,2097152,262144,524288,1048576
     setprop net.tcp.buffersize.umts    4094,87380,110208,4096,16384,110208
     setprop net.tcp.buffersize.hspa    4094,87380,262144,4096,16384,262144
+    setprop net.tcp.buffersize.hsupa   4094,87380,262144,4096,16384,262144
+    setprop net.tcp.buffersize.hsdpa   4094,87380,262144,4096,16384,262144
+    setprop net.tcp.buffersize.hspap   4094,87380,1220608,4096,16384,1220608
     setprop net.tcp.buffersize.edge    4093,26280,35040,4096,16384,35040
     setprop net.tcp.buffersize.gprs    4092,8760,11680,4096,8760,11680
+    setprop net.tcp.buffersize.evdo    4094,87380,262144,4096,16384,262144
 
 # Set this property so surfaceflinger is not started by system_init
     setprop system_init.startsurfaceflinger 0
@@ -418,7 +424,7 @@
     socket rild stream 660 root radio
     socket rild-debug stream 660 radio system
     user root
-    group radio cache inet misc audio sdcard_r sdcard_rw log
+    group radio cache inet misc audio log
 
 service surfaceflinger /system/bin/surfaceflinger
     class main
@@ -437,7 +443,7 @@
 service drm /system/bin/drmserver
     class main
     user drm
-    group drm system inet drmrpc sdcard_r
+    group drm system inet drmrpc
 
 service media /system/bin/mediaserver
     class main
diff --git a/toolbox/mount.c b/toolbox/mount.c
index 27cf3c9..b7adce2 100644
--- a/toolbox/mount.c
+++ b/toolbox/mount.c
@@ -49,12 +49,17 @@
 	{ "exec",	MS_NOEXEC,	0,		MS_NOEXEC	},
 	{ "move",	MS_TYPE,	MS_MOVE,	0		},
 	{ "recurse",	MS_REC,		MS_REC,		0		},
+	{ "rec",	MS_REC,		MS_REC,		0		},
 	{ "remount",	MS_TYPE,	MS_REMOUNT,	0		},
 	{ "ro",		MS_RDONLY,	MS_RDONLY,	0		},
 	{ "rw",		MS_RDONLY,	0,		MS_RDONLY	},
 	{ "suid",	MS_NOSUID,	0,		MS_NOSUID	},
 	{ "sync",	MS_SYNCHRONOUS,	MS_SYNCHRONOUS,	0		},
 	{ "verbose",	MS_VERBOSE,	MS_VERBOSE,	0		},
+	{ "unbindable",	MS_UNBINDABLE,	MS_UNBINDABLE,	0		},
+	{ "private",	MS_PRIVATE,	MS_PRIVATE,	0		},
+	{ "slave",	MS_SLAVE,	MS_SLAVE,	0		},
+	{ "shared",	MS_SHARED,	MS_SHARED,	0		},
 };
 
 static void add_extra_option(struct extra_opts *extra, char *s)
diff --git a/toolbox/vmstat.c b/toolbox/vmstat.c
index 600f136..4086ed0 100644
--- a/toolbox/vmstat.c
+++ b/toolbox/vmstat.c
@@ -75,7 +75,7 @@
     int toggle, count;
     int i;
 
-    iterations = 0;
+    iterations = -1;
     delay = 1;
     header_interval = 20;
 
@@ -119,7 +119,7 @@
     if (!header_interval)
         print_header();
     read_state(&s[1 - toggle]);
-    while ((iterations == 0) || (iterations-- > 0)) {
+    while ((iterations < 0) || (iterations-- > 0)) {
         sleep(delay);
         read_state(&s[toggle]);
         if (header_interval) {