am a29997ae: am 470f0b3f: Merge "Avoid segv by checking argc in "storage user <mountpoint>""

* commit 'a29997ae260d3d7cf41c40d9c7ba42b761b12d30':
  Avoid segv by checking argc in "storage user <mountpoint>"
diff --git a/Android.mk b/Android.mk
index 9ad0edd..7f93229 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,7 +14,6 @@
 	Loop.cpp \
 	Devmapper.cpp \
 	ResponseCode.cpp \
-	Xwarp.cpp \
 	VoldUtil.c \
 	fstrim.c \
 	cryptfs.c
diff --git a/CommandListener.cpp b/CommandListener.cpp
index c8d6848..562b5c2 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -34,7 +34,6 @@
 #include "VolumeManager.h"
 #include "ResponseCode.h"
 #include "Process.h"
-#include "Xwarp.h"
 #include "Loop.h"
 #include "Devmapper.h"
 #include "cryptfs.h"
@@ -49,7 +48,6 @@
     registerCmd(new AsecCmd());
     registerCmd(new ObbCmd());
     registerCmd(new StorageCmd());
-    registerCmd(new XwarpCmd());
     registerCmd(new CryptfsCmd());
     registerCmd(new FstrimCmd());
 }
@@ -512,49 +510,6 @@
     return 0;
 }
 
-CommandListener::XwarpCmd::XwarpCmd() :
-                 VoldCommand("xwarp") {
-}
-
-int CommandListener::XwarpCmd::runCommand(SocketClient *cli,
-                                                      int argc, char **argv) {
-    if (argc < 2) {
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
-        return 0;
-    }
-
-    if (!strcmp(argv[1], "enable")) {
-        if (Xwarp::enable()) {
-            cli->sendMsg(ResponseCode::OperationFailed, "Failed to enable xwarp", true);
-            return 0;
-        }
-
-        cli->sendMsg(ResponseCode::CommandOkay, "Xwarp mirroring started", false);
-    } else if (!strcmp(argv[1], "disable")) {
-        if (Xwarp::disable()) {
-            cli->sendMsg(ResponseCode::OperationFailed, "Failed to disable xwarp", true);
-            return 0;
-        }
-
-        cli->sendMsg(ResponseCode::CommandOkay, "Xwarp disabled", false);
-    } else if (!strcmp(argv[1], "status")) {
-        char msg[255];
-        bool r;
-        unsigned mirrorPos, maxSize;
-
-        if (Xwarp::status(&r, &mirrorPos, &maxSize)) {
-            cli->sendMsg(ResponseCode::OperationFailed, "Failed to get xwarp status", true);
-            return 0;
-        }
-        snprintf(msg, sizeof(msg), "%s %u %u", (r ? "ready" : "not-ready"), mirrorPos, maxSize);
-        cli->sendMsg(ResponseCode::XwarpStatusResult, msg, false);
-    } else {
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown storage cmd", false);
-    }
-
-    return 0;
-}
-
 CommandListener::CryptfsCmd::CryptfsCmd() :
                  VoldCommand("cryptfs") {
 }
@@ -595,19 +550,55 @@
         dumpArgs(argc, argv, -1);
         rc = cryptfs_crypto_complete();
     } else if (!strcmp(argv[1], "enablecrypto")) {
-        if ( (argc != 4) || (strcmp(argv[2], "wipe") && strcmp(argv[2], "inplace")) ) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs enablecrypto <wipe|inplace> <passwd>", false);
+        if ( (argc != 4 && argc != 3)
+             || (strcmp(argv[2], "wipe") && strcmp(argv[2], "inplace")) ) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError,
+                         "Usage: cryptfs enablecrypto <wipe|inplace> [passwd]",
+                         false);
             return 0;
         }
         dumpArgs(argc, argv, 3);
-        rc = cryptfs_enable(argv[2], argv[3]);
+
+        int tries;
+        for (tries = 0; tries < 2; ++tries) {
+            if(argc == 3)
+                rc = cryptfs_enable_default(argv[2], /*allow_reboot*/false);
+            else
+                rc = cryptfs_enable(argv[2], argv[3], /*allow_reboot*/false);
+
+            if (rc == 0) {
+                break;
+            } else if (tries == 0) {
+                Process::killProcessesWithOpenFiles(DATA_MNT_POINT, 2);
+            }
+        }
     } else if (!strcmp(argv[1], "changepw")) {
-        if (argc != 3) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs changepw <newpasswd>", false);
+        const char* syntax = "Usage: cryptfs changepw "
+                             "default|password|pin|pattern [newpasswd]";
+        const char* password;
+        if (argc == 3) {
+            password = "";
+        } else if (argc == 4) {
+            password = argv[3];
+        } else {
+            cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
             return 0;
-        } 
-        SLOGD("cryptfs changepw {}");
-        rc = cryptfs_changepw(argv[2]);
+        }
+        int type = 0;
+        if (!strcmp(argv[2], "default")) {
+            type = CRYPT_TYPE_DEFAULT;
+        } else if (!strcmp(argv[2], "password")) {
+            type = CRYPT_TYPE_PASSWORD;
+        } else if (!strcmp(argv[2], "pin")) {
+            type = CRYPT_TYPE_PIN;
+        } else if (!strcmp(argv[2], "pattern")) {
+            type = CRYPT_TYPE_PATTERN;
+        } else {
+            cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
+            return 0;
+        }
+        SLOGD("cryptfs changepw %s {}", argv[2]);
+        rc = cryptfs_changepw(type, password);
     } else if (!strcmp(argv[1], "verifypw")) {
         if (argc != 3) {
             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs verifypw <passwd>", false);
@@ -634,9 +625,39 @@
         }
         dumpArgs(argc, argv, -1);
         rc = cryptfs_setfield(argv[2], argv[3]);
+    } else if (!strcmp(argv[1], "mountdefaultencrypted")) {
+        SLOGD("cryptfs mountdefaultencrypted");
+        dumpArgs(argc, argv, -1);
+        rc = cryptfs_mount_default_encrypted();
+    } else if (!strcmp(argv[1], "getpwtype")) {
+        SLOGD("cryptfs getpwtype");
+        dumpArgs(argc, argv, -1);
+        switch(cryptfs_get_password_type()) {
+        case CRYPT_TYPE_PASSWORD:
+            cli->sendMsg(ResponseCode::PasswordTypeResult, "password", false);
+            return 0;
+        case CRYPT_TYPE_PATTERN:
+            cli->sendMsg(ResponseCode::PasswordTypeResult, "pattern", false);
+            return 0;
+        case CRYPT_TYPE_PIN:
+            cli->sendMsg(ResponseCode::PasswordTypeResult, "pin", false);
+            return 0;
+        case CRYPT_TYPE_DEFAULT:
+            cli->sendMsg(ResponseCode::PasswordTypeResult, "default", false);
+            return 0;
+        default:
+          /** @TODO better error and make sure handled by callers */
+            cli->sendMsg(ResponseCode::OpFailedStorageNotFound, "Error", false);
+            return 0;
+        }
+    } else if (!strcmp(argv[1], "justdecrypted")) {
+        SLOGD("cryptfs justdecrypted");
+        dumpArgs(argc, argv, -1);
+        rc = cryptfs_just_decrypted();
     } else {
         dumpArgs(argc, argv, -1);
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown cryptfs cmd", false);
+        return 0;
     }
 
     // Always report that the command succeeded and return the error code.
diff --git a/CommandListener.h b/CommandListener.h
index 8cc5b09..0bd51d2 100644
--- a/CommandListener.h
+++ b/CommandListener.h
@@ -65,13 +65,6 @@
         int runCommand(SocketClient *c, int argc, char ** argv);
     };
 
-    class XwarpCmd : public VoldCommand {
-    public:
-        XwarpCmd();
-        virtual ~XwarpCmd() {}
-        int runCommand(SocketClient *c, int argc, char ** argv);
-    };
-
     class CryptfsCmd : public VoldCommand {
     public:
         CryptfsCmd();
diff --git a/ResponseCode.h b/ResponseCode.h
index 5e4c6fa..0dc0500 100644
--- a/ResponseCode.h
+++ b/ResponseCode.h
@@ -33,7 +33,7 @@
     static const int ShareStatusResult        = 210;
     static const int AsecPathResult           = 211;
     static const int ShareEnabledResult       = 212;
-    static const int XwarpStatusResult        = 213;
+    static const int PasswordTypeResult       = 213;
 
     // 400 series - The command was accepted but the requested action
     // did not take place.
diff --git a/Xwarp.cpp b/Xwarp.cpp
deleted file mode 100644
index 2973ff8..0000000
--- a/Xwarp.cpp
+++ /dev/null
@@ -1,83 +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 <stdio.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#define LOG_TAG "Vold"
-
-#include <cutils/log.h>
-
-#include "Xwarp.h"
-const char *Xwarp::XWARP_BACKINGFILE = "/mnt/secure/asec/xwarp.img";
-const char *Xwarp::XWARP_CFG = "/sys/fs/yaffs/mtd3/xwarp-backing-store";
-const char *Xwarp::XWARP_READY = "/sys/fs/yaffs/mtd3/xwarp-ready";
-const char *Xwarp::XWARP_MIRROR_STATUS = "/sys/fs/yaffs/mtd3/xwarp-mirror";
-
-int Xwarp::enable() {
-    return doEnableDisable(true);
-}
-
-int Xwarp::disable() {
-    return doEnableDisable(false);
-}
-
-int Xwarp::status(bool *ready, unsigned *mirrorPos, unsigned *maxSize) {
-    FILE *fp;
-
-    *ready = false;
-    *mirrorPos = 0;
-    *maxSize = 0;
-    if (!(fp = fopen(XWARP_READY, "r"))) {
-        return -1;
-    }
-
-    fscanf(fp, "%d", (int *) ready);
-    fclose(fp);
-
-    if (!(fp = fopen(XWARP_MIRROR_STATUS, "r"))) {
-        return -1;
-    }
-
-    fscanf(fp, "%u %u", mirrorPos, maxSize);
-    fclose(fp);
-    return 0;
-}
-
-int Xwarp::doEnableDisable(bool enable) {
-    const char *tmp;
-    int fd = open(XWARP_CFG, O_WRONLY);
-
-    if (fd < 0) 
-        return -1;
-
-    tmp = (enable ? XWARP_BACKINGFILE : "");
-
-    if (write(fd, tmp, strlen(tmp)+1) < 0) {
-        SLOGE("Failed to write xwarp cfg (%s)", strerror(errno));
-        close(fd);
-        return -1;
-    }
-
-    close(fd);
-    return 0;
-}
diff --git a/Xwarp.h b/Xwarp.h
deleted file mode 100644
index 918a843..0000000
--- a/Xwarp.h
+++ /dev/null
@@ -1,37 +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 _XWARP_H
-#define _XWARP_H
-
-#include <unistd.h>
-
-class Xwarp {
-    static const char *XWARP_BACKINGFILE;
-    static const char *XWARP_CFG;
-    static const char *XWARP_READY;
-    static const char *XWARP_MIRROR_STATUS;
-
-public:
-    static int enable();
-    static int disable();
-    static int status(bool *ready, unsigned *mirrorPos, unsigned *maxSize);
-
-private:
-    static int doEnableDisable(bool enable);
-};
-
-#endif
diff --git a/cryptfs.c b/cryptfs.c
index e545919..f8d14cf 100644
--- a/cryptfs.c
+++ b/cryptfs.c
@@ -23,6 +23,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
+#include <ctype.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <stdio.h>
@@ -49,11 +50,13 @@
 #include "VolumeManager.h"
 #include "VoldUtil.h"
 #include "crypto_scrypt.h"
+#include "ext4_utils.h"
+
+#define UNUSED __attribute__((unused))
 
 #define UNUSED __attribute__((unused))
 
 #define DM_CRYPT_BUF_SIZE 4096
-#define DATA_MNT_POINT "/data"
 
 #define HASH_COUNT 2000
 #define KEY_LEN_BYTES 16
@@ -61,6 +64,9 @@
 
 #define KEY_IN_FOOTER  "footer"
 
+// "default_password" encoded into hex (d=0x64 etc)
+#define DEFAULT_PASSWORD "64656661756c745f70617373776f7264"
+
 #define EXT4_FS 1
 #define FAT_FS 2
 
@@ -73,6 +79,12 @@
 static int  master_key_saved = 0;
 static struct crypt_persist_data *persist_data = NULL;
 
+/* Set when userdata is successfully decrypted and mounted.
+ * Reset whenever read (via cryptfs_just_decrypted)
+ * (Read by keyguard to avoid a double prompt.)
+ */
+static int just_decrypted = 0;
+
 extern struct fstab *fstab;
 
 static void cryptfs_reboot(int recovery)
@@ -351,7 +363,7 @@
         crypt_ftr->minor_version = 1;
     }
 
-    if ((crypt_ftr->major_version == 1) && (crypt_ftr->minor_version)) {
+    if ((crypt_ftr->major_version == 1) && (crypt_ftr->minor_version == 1)) {
         SLOGW("upgrading crypto footer to 1.2");
         /* But keep the old kdf_type.
          * It will get updated later to KDF_SCRYPT after the password has been verified.
@@ -361,6 +373,12 @@
         crypt_ftr->minor_version = 2;
     }
 
+    if ((crypt_ftr->major_version == 1) && (crypt_ftr->minor_version == 2)) {
+        SLOGW("upgrading crypto footer to 1.3");
+        crypt_ftr->crypt_type = CRYPT_TYPE_PASSWORD;
+        crypt_ftr->minor_version = 3;
+    }
+
     if ((orig_major != crypt_ftr->major_version) || (orig_minor != crypt_ftr->minor_version)) {
         if (lseek64(fd, offset, SEEK_SET) == -1) {
             SLOGE("Cannot seek to crypt footer\n");
@@ -663,10 +681,53 @@
     return -1;
 }
 
+static int hexdigit (char c)
+{
+    if (c >= '0' && c <= '9') return c - '0';
+    c = tolower(c);
+    if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+    return -1;
+}
+
+static unsigned char* convert_hex_ascii_to_key(const char* master_key_ascii,
+                                               unsigned int* out_keysize)
+{
+    unsigned int i;
+    *out_keysize = 0;
+
+    size_t size = strlen (master_key_ascii);
+    if (size % 2) {
+        SLOGE("Trying to convert ascii string of odd length");
+        return NULL;
+    }
+
+    unsigned char* master_key = (unsigned char*) malloc(size / 2);
+    if (master_key == 0) {
+        SLOGE("Cannot allocate");
+        return NULL;
+    }
+
+    for (i = 0; i < size; i += 2) {
+        int high_nibble = hexdigit (master_key_ascii[i]);
+        int low_nibble = hexdigit (master_key_ascii[i + 1]);
+
+        if(high_nibble < 0 || low_nibble < 0) {
+            SLOGE("Invalid hex string");
+            free (master_key);
+            return NULL;
+        }
+
+        master_key[*out_keysize] = high_nibble * 16 + low_nibble;
+        (*out_keysize)++;
+    }
+
+    return master_key;
+}
+
 /* Convert a binary key of specified length into an ascii hex string equivalent,
  * without the leading 0x and with null termination
  */
-void convert_key_to_hex_ascii(unsigned char *master_key, unsigned int keysize,
+static void convert_key_to_hex_ascii(unsigned char *master_key, unsigned int keysize,
                               char *master_key_ascii)
 {
   unsigned int i, a;
@@ -870,13 +931,23 @@
 
 }
 
-static void pbkdf2(char *passwd, unsigned char *salt, unsigned char *ikey, void *params UNUSED) {
+static int pbkdf2(const char *passwd, unsigned char *salt,
+                  unsigned char *ikey, void *params UNUSED)
+{
     /* Turn the password into a key and IV that can decrypt the master key */
-    PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), salt, SALT_LEN,
+    unsigned int keysize;
+    char* master_key = (char*)convert_hex_ascii_to_key(passwd, &keysize);
+    if (!master_key) return -1;
+    PKCS5_PBKDF2_HMAC_SHA1(master_key, keysize, salt, SALT_LEN,
                            HASH_COUNT, KEY_LEN_BYTES+IV_LEN_BYTES, ikey);
+
+    free (master_key);
+    return 0;
 }
 
-static void scrypt(char *passwd, unsigned char *salt, unsigned char *ikey, void *params) {
+static int scrypt(const char *passwd, unsigned char *salt,
+                  unsigned char *ikey, void *params)
+{
     struct crypt_mnt_ftr *ftr = (struct crypt_mnt_ftr *) params;
 
     int N = 1 << ftr->N_factor;
@@ -884,11 +955,17 @@
     int p = 1 << ftr->p_factor;
 
     /* Turn the password into a key and IV that can decrypt the master key */
-    crypto_scrypt((unsigned char *) passwd, strlen(passwd), salt, SALT_LEN, N, r, p, ikey,
+    unsigned int keysize;
+    unsigned char* master_key = convert_hex_ascii_to_key(passwd, &keysize);
+    if (!master_key) return -1;
+    crypto_scrypt(master_key, keysize, salt, SALT_LEN, N, r, p, ikey,
             KEY_LEN_BYTES + IV_LEN_BYTES);
+
+    free (master_key);
+    return 0;
 }
 
-static int encrypt_master_key(char *passwd, unsigned char *salt,
+static int encrypt_master_key(const char *passwd, unsigned char *salt,
                               unsigned char *decrypted_master_key,
                               unsigned char *encrypted_master_key,
                               struct crypt_mnt_ftr *crypt_ftr)
@@ -899,7 +976,10 @@
 
     /* Turn the password into a key and IV that can decrypt the master key */
     get_device_scrypt_params(crypt_ftr);
-    scrypt(passwd, salt, ikey, crypt_ftr);
+    if (scrypt(passwd, salt, ikey, crypt_ftr)) {
+        SLOGE("scrypt failed");
+        return -1;
+    }
 
     /* Initialize the decryption engine */
     if (! EVP_EncryptInit(&e_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) {
@@ -937,7 +1017,10 @@
   int decrypted_len, final_len;
 
   /* Turn the password into a key and IV that can decrypt the master key */
-  kdf(passwd, salt, ikey, kdf_params);
+  if (kdf(passwd, salt, ikey, kdf_params)) {
+    SLOGE("kdf failed");
+    return -1;
+  }
 
   /* Initialize the decryption engine */
   if (! EVP_DecryptInit(&d_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) {
@@ -1068,7 +1151,7 @@
     }
 }
 
-int cryptfs_restart(void)
+static int cryptfs_restart_internal(int restart_main)
 {
     char fs_type[32];
     char real_blkdev[MAXPATHLEN];
@@ -1090,32 +1173,34 @@
         return -1;
     }
 
-    /* Here is where we shut down the framework.  The init scripts
-     * start all services in one of three classes: core, main or late_start.
-     * On boot, we start core and main.  Now, we stop main, but not core,
-     * as core includes vold and a few other really important things that
-     * we need to keep running.  Once main has stopped, we should be able
-     * to umount the tmpfs /data, then mount the encrypted /data.
-     * We then restart the class main, and also the class late_start.
-     * At the moment, I've only put a few things in late_start that I know
-     * are not needed to bring up the framework, and that also cause problems
-     * with unmounting the tmpfs /data, but I hope to add add more services
-     * to the late_start class as we optimize this to decrease the delay
-     * till the user is asked for the password to the filesystem.
-     */
+    if (restart_main) {
+        /* Here is where we shut down the framework.  The init scripts
+         * start all services in one of three classes: core, main or late_start.
+         * On boot, we start core and main.  Now, we stop main, but not core,
+         * as core includes vold and a few other really important things that
+         * we need to keep running.  Once main has stopped, we should be able
+         * to umount the tmpfs /data, then mount the encrypted /data.
+         * We then restart the class main, and also the class late_start.
+         * At the moment, I've only put a few things in late_start that I know
+         * are not needed to bring up the framework, and that also cause problems
+         * with unmounting the tmpfs /data, but I hope to add add more services
+         * to the late_start class as we optimize this to decrease the delay
+         * till the user is asked for the password to the filesystem.
+         */
 
-    /* The init files are setup to stop the class main when vold.decrypt is
-     * set to trigger_reset_main.
-     */
-    property_set("vold.decrypt", "trigger_reset_main");
-    SLOGD("Just asked init to shut down class main\n");
+        /* The init files are setup to stop the class main when vold.decrypt is
+         * set to trigger_reset_main.
+         */
+        property_set("vold.decrypt", "trigger_reset_main");
+        SLOGD("Just asked init to shut down class main\n");
 
-    /* Ugh, shutting down the framework is not synchronous, so until it
-     * can be fixed, this horrible hack will wait a moment for it all to
-     * shut down before proceeding.  Without it, some devices cannot
-     * restart the graphics services.
-     */
-    sleep(2);
+        /* Ugh, shutting down the framework is not synchronous, so until it
+         * can be fixed, this horrible hack will wait a moment for it all to
+         * shut down before proceeding.  Without it, some devices cannot
+         * restart the graphics services.
+         */
+        sleep(2);
+    }
 
     /* Now that the framework is shutdown, we should be able to umount()
      * the tmpfs filesystem, and mount the real one.
@@ -1128,6 +1213,17 @@
     }
 
     if (! (rc = wait_and_unmount(DATA_MNT_POINT)) ) {
+        /* If ro.crypto.readonly is set to 1, mount the decrypted
+         * filesystem readonly.  This is used when /data is mounted by
+         * recovery mode.
+         */
+        char ro_prop[PROPERTY_VALUE_MAX];
+        property_get("ro.crypto.readonly", ro_prop, "");
+        if (strlen(ro_prop) > 0 && atoi(ro_prop)) {
+            struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, DATA_MNT_POINT);
+            rec->flags |= MS_RDONLY;
+        }
+        
         /* If that succeeded, then mount the decrypted filesystem */
         fs_mgr_do_mount(fstab, DATA_MNT_POINT, crypto_blkdev, 0);
 
@@ -1152,6 +1248,12 @@
     return rc;
 }
 
+int cryptfs_restart(void)
+{
+    /* Call internal implementation forcing a restart of main service group */
+    return cryptfs_restart_internal(1);
+}
+
 static int do_crypto_complete(char *mount_point UNUSED)
 {
   struct crypt_mnt_ftr crypt_ftr;
@@ -1193,45 +1295,33 @@
   return 0;
 }
 
-static int test_mount_encrypted_fs(char *passwd, char *mount_point, char *label)
+static int test_mount_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
+                                   char *passwd, char *mount_point, char *label)
 {
-  struct crypt_mnt_ftr crypt_ftr;
   /* Allocate enough space for a 256 bit key, but we may use less */
   unsigned char decrypted_master_key[32];
   char crypto_blkdev[MAXPATHLEN];
   char real_blkdev[MAXPATHLEN];
   char tmp_mount_point[64];
   unsigned int orig_failed_decrypt_count;
-  char encrypted_state[PROPERTY_VALUE_MAX];
   int rc;
   kdf_func kdf;
   void *kdf_params;
 
-  property_get("ro.crypto.state", encrypted_state, "");
-  if ( master_key_saved || strcmp(encrypted_state, "encrypted") ) {
-    SLOGE("encrypted fs already validated or not running with encryption, aborting");
-    return -1;
-  }
+  SLOGD("crypt_ftr->fs_size = %lld\n", crypt_ftr->fs_size);
+  orig_failed_decrypt_count = crypt_ftr->failed_decrypt_count;
 
-  fs_mgr_get_crypt_info(fstab, 0, real_blkdev, sizeof(real_blkdev));
-
-  if (get_crypt_ftr_and_key(&crypt_ftr)) {
-    SLOGE("Error getting crypt footer and key\n");
-    return -1;
-  }
-
-  SLOGD("crypt_ftr->fs_size = %lld\n", crypt_ftr.fs_size);
-  orig_failed_decrypt_count = crypt_ftr.failed_decrypt_count;
-
-  if (! (crypt_ftr.flags & CRYPT_MNT_KEY_UNENCRYPTED) ) {
-    if (decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr)) {
+  if (! (crypt_ftr->flags & CRYPT_MNT_KEY_UNENCRYPTED) ) {
+    if (decrypt_master_key(passwd, decrypted_master_key, crypt_ftr)) {
       SLOGE("Failed to decrypt master key\n");
       return -1;
     }
   }
 
-  if (create_crypto_blk_dev(&crypt_ftr, decrypted_master_key,
-                               real_blkdev, crypto_blkdev, label)) {
+  fs_mgr_get_crypt_info(fstab, 0, real_blkdev, sizeof(real_blkdev));
+
+  if (create_crypto_blk_dev(crypt_ftr, decrypted_master_key,
+                            real_blkdev, crypto_blkdev, label)) {
     SLOGE("Error creating decrypted block device\n");
     return -1;
   }
@@ -1248,22 +1338,22 @@
   if (fs_mgr_do_mount(fstab, DATA_MNT_POINT, crypto_blkdev, tmp_mount_point)) {
     SLOGE("Error temp mounting decrypted block device\n");
     delete_crypto_blk_dev(label);
-    crypt_ftr.failed_decrypt_count++;
+    crypt_ftr->failed_decrypt_count++;
   } else {
     /* Success, so just umount and we'll mount it properly when we restart
      * the framework.
      */
     umount(tmp_mount_point);
-    crypt_ftr.failed_decrypt_count  = 0;
+    crypt_ftr->failed_decrypt_count  = 0;
   }
 
-  if (orig_failed_decrypt_count != crypt_ftr.failed_decrypt_count) {
-    put_crypt_ftr_and_key(&crypt_ftr);
+  if (orig_failed_decrypt_count != crypt_ftr->failed_decrypt_count) {
+    put_crypt_ftr_and_key(crypt_ftr);
   }
 
-  if (crypt_ftr.failed_decrypt_count) {
+  if (crypt_ftr->failed_decrypt_count) {
     /* We failed to mount the device, so return an error */
-    rc = crypt_ftr.failed_decrypt_count;
+    rc = crypt_ftr->failed_decrypt_count;
 
   } else {
     /* Woot!  Success!  Save the name of the crypto block device
@@ -1282,12 +1372,12 @@
     /*
      * Upgrade if we're not using the latest KDF.
      */
-    if (crypt_ftr.kdf_type != KDF_SCRYPT) {
-        crypt_ftr.kdf_type = KDF_SCRYPT;
-        rc = encrypt_master_key(passwd, crypt_ftr.salt, saved_master_key, crypt_ftr.master_key,
-                &crypt_ftr);
+    if (crypt_ftr->kdf_type != KDF_SCRYPT) {
+        crypt_ftr->kdf_type = KDF_SCRYPT;
+        rc = encrypt_master_key(passwd, crypt_ftr->salt, saved_master_key,
+                                crypt_ftr->master_key, crypt_ftr);
         if (!rc) {
-            rc = put_crypt_ftr_and_key(&crypt_ftr);
+            rc = put_crypt_ftr_and_key(crypt_ftr);
         }
         SLOGD("Key Derivation Function upgrade: rc=%d\n", rc);
     }
@@ -1351,11 +1441,39 @@
   return do_crypto_complete("/data");
 }
 
+int check_unmounted_and_get_ftr(struct crypt_mnt_ftr* crypt_ftr)
+{
+    char encrypted_state[PROPERTY_VALUE_MAX];
+    property_get("ro.crypto.state", encrypted_state, "");
+    if ( master_key_saved || strcmp(encrypted_state, "encrypted") ) {
+        SLOGE("encrypted fs already validated or not running with encryption,"
+              " aborting");
+        return -1;
+    }
+
+    if (get_crypt_ftr_and_key(crypt_ftr)) {
+        SLOGE("Error getting crypt footer and key");
+        return -1;
+    }
+
+    return 0;
+}
+
 int cryptfs_check_passwd(char *passwd)
 {
-    int rc = -1;
+    struct crypt_mnt_ftr crypt_ftr;
+    int rc;
 
-    rc = test_mount_encrypted_fs(passwd, DATA_MNT_POINT, "userdata");
+    rc = check_unmounted_and_get_ftr(&crypt_ftr);
+    if (rc)
+        return rc;
+
+    rc = test_mount_encrypted_fs(&crypt_ftr, passwd,
+                                 DATA_MNT_POINT, "userdata");
+
+    if (rc == 0 && crypt_ftr.crypt_type != CRYPT_TYPE_DEFAULT) {
+        just_decrypted = 1;
+    }
 
     return rc;
 }
@@ -1498,8 +1616,205 @@
 
 #define CRYPT_INPLACE_BUFSIZE 4096
 #define CRYPT_SECTORS_PER_BUFSIZE (CRYPT_INPLACE_BUFSIZE / 512)
-static int cryptfs_enable_inplace(char *crypto_blkdev, char *real_blkdev, off64_t size,
-                                  off64_t *size_already_done, off64_t tot_size)
+
+/* aligned 32K writes tends to make flash happy.
+ * SD card association recommends it.
+ */
+#define BLOCKS_AT_A_TIME 8
+
+struct encryptGroupsData
+{
+    int realfd;
+    int cryptofd;
+    off64_t numblocks;
+    off64_t one_pct, cur_pct, new_pct;
+    off64_t blocks_already_done, tot_numblocks;
+    char* real_blkdev, * crypto_blkdev;
+    int count;
+    off64_t offset;
+    char* buffer;
+};
+
+static void update_progress(struct encryptGroupsData* data)
+{
+    data->blocks_already_done++;
+    data->new_pct = data->blocks_already_done / data->one_pct;
+    if (data->new_pct > data->cur_pct) {
+        char buf[8];
+        data->cur_pct = data->new_pct;
+        snprintf(buf, sizeof(buf), "%lld", data->cur_pct);
+        property_set("vold.encrypt_progress", buf);
+    }
+}
+
+static int flush_outstanding_data(struct encryptGroupsData* data)
+{
+    if (data->count == 0) {
+        return 0;
+    }
+
+    SLOGV("Copying %d blocks at offset %llx", data->count, data->offset);
+
+    if (pread64(data->realfd, data->buffer,
+                info.block_size * data->count, data->offset)
+        <= 0) {
+        SLOGE("Error reading real_blkdev %s for inplace encrypt",
+              data->real_blkdev);
+        return -1;
+    }
+
+    if (pwrite64(data->cryptofd, data->buffer,
+                 info.block_size * data->count, data->offset)
+        <= 0) {
+        SLOGE("Error writing crypto_blkdev %s for inplace encrypt",
+              data->crypto_blkdev);
+        return -1;
+    }
+
+    data->count = 0;
+    return 0;
+}
+
+static int encrypt_groups(struct encryptGroupsData* data)
+{
+    unsigned int i;
+    u8 *block_bitmap = 0;
+    unsigned int block;
+    off64_t ret;
+    int rc = -1;
+
+    data->buffer = malloc(info.block_size * BLOCKS_AT_A_TIME);
+    if (!data->buffer) {
+        SLOGE("Failed to allocate crypto buffer");
+        goto errout;
+    }
+
+    block_bitmap = malloc(info.block_size);
+    if (!block_bitmap) {
+        SLOGE("failed to allocate block bitmap");
+        goto errout;
+    }
+
+    for (i = 0; i < aux_info.groups; ++i) {
+        SLOGI("Encrypting group %d", i);
+
+        u32 first_block = aux_info.first_data_block + i * info.blocks_per_group;
+        u32 block_count = min(info.blocks_per_group,
+                             aux_info.len_blocks - first_block);
+
+        off64_t offset = (u64)info.block_size
+                         * aux_info.bg_desc[i].bg_block_bitmap;
+
+        ret = pread64(data->realfd, block_bitmap, info.block_size, offset);
+        if (ret != (int)info.block_size) {
+            SLOGE("failed to read all of block group bitmap %d", i);
+            goto errout;
+        }
+
+        offset = (u64)info.block_size * first_block;
+
+        data->count = 0;
+
+        for (block = 0; block < block_count; block++) {
+            update_progress(data);
+            if (bitmap_get_bit(block_bitmap, block)) {
+                if (data->count == 0) {
+                    data->offset = offset;
+                }
+                data->count++;
+            } else {
+                if (flush_outstanding_data(data)) {
+                    goto errout;
+                }
+            }
+
+            offset += info.block_size;
+
+            /* Write data if we are aligned or buffer size reached */
+            if (offset % (info.block_size * BLOCKS_AT_A_TIME) == 0
+                || data->count == BLOCKS_AT_A_TIME) {
+                if (flush_outstanding_data(data)) {
+                    goto errout;
+                }
+            }
+        }
+        if (flush_outstanding_data(data)) {
+            goto errout;
+        }
+    }
+
+    rc = 0;
+
+errout:
+    free(data->buffer);
+    free(block_bitmap);
+    return rc;
+}
+
+static int cryptfs_enable_inplace_ext4(char *crypto_blkdev,
+                                       char *real_blkdev,
+                                       off64_t size,
+                                       off64_t *size_already_done,
+                                       off64_t tot_size)
+{
+    int i;
+    struct encryptGroupsData data;
+    int rc = -1;
+
+    memset(&data, 0, sizeof(data));
+    data.real_blkdev = real_blkdev;
+    data.crypto_blkdev = crypto_blkdev;
+
+    if ( (data.realfd = open(real_blkdev, O_RDWR)) < 0) {
+        SLOGE("Error opening real_blkdev %s for inplace encrypt\n",
+              real_blkdev);
+        goto errout;
+    }
+
+    if ( (data.cryptofd = open(crypto_blkdev, O_WRONLY)) < 0) {
+        SLOGE("Error opening crypto_blkdev %s for inplace encrypt\n",
+              crypto_blkdev);
+        goto errout;
+    }
+
+    if (setjmp(setjmp_env)) {
+        SLOGE("Reading extent caused an exception");
+        goto errout;
+    }
+
+    if (read_ext(data.realfd, 0) != 0) {
+        SLOGE("Failed to read extent");
+        goto errout;
+    }
+
+    data.numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
+    data.tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
+    data.blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
+
+    SLOGI("Encrypting filesystem in place...");
+
+    data.one_pct = data.tot_numblocks / 100;
+    data.cur_pct = 0;
+
+    rc = encrypt_groups(&data);
+    if (rc) {
+        SLOGE("Error encrypting groups");
+        goto errout;
+    }
+
+    *size_already_done += size;
+    rc = 0;
+
+errout:
+    close(data.realfd);
+    close(data.cryptofd);
+
+    return rc;
+}
+
+static int cryptfs_enable_inplace_full(char *crypto_blkdev, char *real_blkdev,
+                                       off64_t size, off64_t *size_already_done,
+                                       off64_t tot_size)
 {
     int realfd, cryptofd;
     char *buf[CRYPT_INPLACE_BUFSIZE];
@@ -1575,6 +1890,19 @@
     return rc;
 }
 
+static int cryptfs_enable_inplace(char *crypto_blkdev, char *real_blkdev,
+                                  off64_t size, off64_t *size_already_done,
+                                  off64_t tot_size)
+{
+    if (cryptfs_enable_inplace_ext4(crypto_blkdev, real_blkdev,
+                                    size, size_already_done, tot_size) == 0) {
+        return 0;
+    }
+
+    return cryptfs_enable_inplace_full(crypto_blkdev, real_blkdev,
+                                       size, size_already_done, tot_size);
+}
+
 #define CRYPTO_ENABLE_WIPE 1
 #define CRYPTO_ENABLE_INPLACE 2
 
@@ -1582,11 +1910,12 @@
 
 static inline int should_encrypt(struct volume_info *volume)
 {
-    return (volume->flags & (VOL_ENCRYPTABLE | VOL_NONREMOVABLE)) == 
+    return (volume->flags & (VOL_ENCRYPTABLE | VOL_NONREMOVABLE)) ==
             (VOL_ENCRYPTABLE | VOL_NONREMOVABLE);
 }
 
-int cryptfs_enable(char *howarg, char *passwd)
+int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
+                            int allow_reboot)
 {
     int how = 0;
     char crypto_blkdev[MAXPATHLEN], real_blkdev[MAXPATHLEN], sd_crypto_blkdev[MAXPATHLEN];
@@ -1607,7 +1936,7 @@
     off64_t cur_encryption_done=0, tot_encryption_size=0;
 
     property_get("ro.crypto.state", encrypted_state, "");
-    if (strcmp(encrypted_state, "unencrypted")) {
+    if (!strcmp(encrypted_state, "encrypted")) {
         SLOGE("Device is already running encrypted, aborting");
         goto error_unencrypted;
     }
@@ -1713,7 +2042,11 @@
 
     /* Now unmount the /data partition. */
     if (wait_and_unmount(DATA_MNT_POINT)) {
-        goto error_shutting_down;
+        if (allow_reboot) {
+            goto error_shutting_down;
+        } else {
+            goto error_unencrypted;
+        }
     }
 
     /* Do extra work for a better UX when doing the long inplace encryption */
@@ -1762,12 +2095,13 @@
         crypt_ftr.fs_size = nr_sec;
     }
     crypt_ftr.flags |= CRYPT_ENCRYPTION_IN_PROGRESS;
+    crypt_ftr.crypt_type = crypt_type;
     strcpy((char *)crypt_ftr.crypto_type_name, "aes-cbc-essiv:sha256");
 
     /* Make an encrypted master key */
     if (create_encrypted_random_key(passwd, crypt_ftr.master_key, crypt_ftr.salt, &crypt_ftr)) {
         SLOGE("Cannot create encrypted master key\n");
-        goto error_unencrypted;
+        goto error_shutting_down;
     }
 
     /* Write the key to the end of the partition */
@@ -1839,7 +2173,7 @@
     } else {
         /* Shouldn't happen */
         SLOGE("cryptfs_enable: internal error, unknown option\n");
-        goto error_unencrypted;
+        goto error_shutting_down;
     }
 
     /* Undo the dm-crypt mapping whether we succeed or not */
@@ -1918,24 +2252,51 @@
     return -1;
 }
 
-int cryptfs_changepw(char *newpw)
+int cryptfs_enable(char *howarg, char *passwd, int allow_reboot)
+{
+    /** @todo If we keep this route (user selected encryption)
+     *  need to take a type in and pass it to here.
+     */
+    return cryptfs_enable_internal(howarg, CRYPT_TYPE_PASSWORD,
+                                   passwd, allow_reboot);
+}
+
+int cryptfs_enable_default(char *howarg, int allow_reboot)
+{
+    return cryptfs_enable_internal(howarg, CRYPT_TYPE_DEFAULT,
+                          DEFAULT_PASSWORD, allow_reboot);
+}
+
+int cryptfs_changepw(int crypt_type, const char *newpw)
 {
     struct crypt_mnt_ftr crypt_ftr;
     unsigned char decrypted_master_key[KEY_LEN_BYTES];
 
     /* This is only allowed after we've successfully decrypted the master key */
-    if (! master_key_saved) {
+    if (!master_key_saved) {
         SLOGE("Key not saved, aborting");
         return -1;
     }
 
+    if (crypt_type < 0 || crypt_type > CRYPT_TYPE_MAX_TYPE) {
+        SLOGE("Invalid crypt_type %d", crypt_type);
+        return -1;
+    }
+
     /* get key */
     if (get_crypt_ftr_and_key(&crypt_ftr)) {
-      SLOGE("Error getting crypt footer and key");
-      return -1;
+        SLOGE("Error getting crypt footer and key");
+        return -1;
     }
 
-    encrypt_master_key(newpw, crypt_ftr.salt, saved_master_key, crypt_ftr.master_key, &crypt_ftr);
+    crypt_ftr.crypt_type = crypt_type;
+
+    encrypt_master_key(crypt_type == CRYPT_TYPE_DEFAULT ? DEFAULT_PASSWORD
+                                                        : newpw,
+                       crypt_ftr.salt,
+                       saved_master_key,
+                       crypt_ftr.master_key,
+                       &crypt_ftr);
 
     /* save the key */
     put_crypt_ftr_and_key(&crypt_ftr);
@@ -2082,3 +2443,59 @@
 out:
     return rc;
 }
+
+/* Checks userdata. Attempt to mount the volume if default-
+ * encrypted.
+ * On success trigger next init phase and return 0.
+ * Currently do not handle failure - see TODO below.
+ */
+int cryptfs_mount_default_encrypted(void)
+{
+    char decrypt_state[PROPERTY_VALUE_MAX];
+    property_get("vold.decrypt", decrypt_state, "0");
+    if (!strcmp(decrypt_state, "0")) {
+        SLOGE("Not encrypted - should not call here");
+    } else {
+        int crypt_type = cryptfs_get_password_type();
+        if (crypt_type < 0 || crypt_type > CRYPT_TYPE_MAX_TYPE) {
+            SLOGE("Bad crypt type - error");
+        } else if (crypt_type != CRYPT_TYPE_DEFAULT) {
+            SLOGD("Password is not default - "
+                  "starting min framework to prompt");
+            property_set("vold.decrypt", "trigger_restart_min_framework");
+            return 0;
+        } else if (cryptfs_check_passwd(DEFAULT_PASSWORD) == 0) {
+            SLOGD("Password is default - restarting filesystem");
+            cryptfs_restart_internal(0);
+            return 0;
+        } else {
+            SLOGE("Encrypted, default crypt type but can't decrypt");
+        }
+    }
+
+    /** @TODO make sure we factory wipe in this situation
+     *  In general if we got here there is no recovery
+     */
+    return 0;
+}
+
+/* Returns type of the password, default, pattern, pin or password.
+ */
+int cryptfs_get_password_type(void)
+{
+    struct crypt_mnt_ftr crypt_ftr;
+
+    if (get_crypt_ftr_and_key(&crypt_ftr)) {
+        SLOGE("Error getting crypt footer and key\n");
+        return -1;
+    }
+
+    return crypt_ftr.crypt_type;
+}
+
+int cryptfs_just_decrypted(void)
+{
+    int rc = just_decrypted;
+    just_decrypted = 0;
+    return rc;
+}
diff --git a/cryptfs.h b/cryptfs.h
index bd44dfa..ac678dc 100644
--- a/cryptfs.h
+++ b/cryptfs.h
@@ -30,7 +30,7 @@
 
 /* The current cryptfs version */
 #define CURRENT_MAJOR_VERSION 1
-#define CURRENT_MINOR_VERSION 2
+#define CURRENT_MINOR_VERSION 3
 
 #define CRYPT_FOOTER_OFFSET 0x4000
 #define CRYPT_FOOTER_TO_PERSIST_OFFSET 0x1000
@@ -46,6 +46,16 @@
 #define CRYPT_ENCRYPTION_IN_PROGRESS 0x2 /* Set when starting encryption,
                                           * clear when done before rebooting */
 
+/* Allowed values for type in the structure below */
+#define CRYPT_TYPE_PASSWORD 0 /* master_key is encrypted with a password
+                               * Must be zero to be compatible with pre-L
+                               * devices where type is always password.*/
+#define CRYPT_TYPE_DEFAULT  1 /* master_key is encrypted with default
+                               * password */
+#define CRYPT_TYPE_PATTERN  2 /* master_key is encrypted with a pattern */
+#define CRYPT_TYPE_PIN      3 /* master_key is encrypted with a pin */
+#define CRYPT_TYPE_MAX_TYPE 3 /* type cannot be larger than this value */
+
 #define CRYPT_MNT_MAGIC 0xD0B5B1C4
 #define PERSIST_DATA_MAGIC 0xE950CD44
 
@@ -60,16 +70,17 @@
 #define __le8  unsigned char
 
 struct crypt_mnt_ftr {
-  __le32 magic;		/* See above */
+  __le32 magic;         /* See above */
   __le16 major_version;
   __le16 minor_version;
-  __le32 ftr_size; 	/* in bytes, not including key following */
-  __le32 flags;		/* See above */
-  __le32 keysize;	/* in bytes */
-  __le32 spare1;	/* ignored */
+  __le32 ftr_size;      /* in bytes, not including key following */
+  __le32 flags;         /* See above */
+  __le32 keysize;       /* in bytes */
+  __le32 crypt_type;    /* how master_key is encrypted. Must be a
+                         * CRYPT_TYPE_XXX value */
   __le64 fs_size;	/* Size of the encrypted fs, in 512 byte sectors */
   __le32 failed_decrypt_count; /* count of # of failed attempts to decrypt and
-				  mount, set to 0 on successful mount */
+			          mount, set to 0 on successful mount */
   unsigned char crypto_type_name[MAX_CRYPTO_TYPE_NAME_LEN]; /* The type of encryption
 							       needed to decrypt this
 							       partition, null terminated */
@@ -131,24 +142,31 @@
 #define VOL_PRIMARY        0x4
 #define VOL_PROVIDES_ASEC  0x8
 
+#define DATA_MNT_POINT "/data"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-  typedef void (*kdf_func)(char *passwd, unsigned char *salt, unsigned char *ikey, void *params);
+  typedef int (*kdf_func)(const char *passwd, unsigned char *salt,
+                          unsigned char *ikey, void *params);
 
   int cryptfs_crypto_complete(void);
   int cryptfs_check_passwd(char *pw);
   int cryptfs_verify_passwd(char *newpw);
   int cryptfs_restart(void);
-  int cryptfs_enable(char *flag, char *passwd);
-  int cryptfs_changepw(char *newpw);
+  int cryptfs_enable(char *flag, char *passwd, int allow_reboot);
+  int cryptfs_changepw(int type, const char *newpw);
+  int cryptfs_enable_default(char *flag, int allow_reboot);
   int cryptfs_setup_volume(const char *label, int major, int minor,
                            char *crypto_dev_path, unsigned int max_pathlen,
                            int *new_major, int *new_minor);
   int cryptfs_revert_volume(const char *label);
   int cryptfs_getfield(char *fieldname, char *value, int len);
   int cryptfs_setfield(char *fieldname, char *value);
+  int cryptfs_mount_default_encrypted(void);
+  int cryptfs_get_password_type(void);
+  int cryptfs_just_decrypted(void);
 #ifdef __cplusplus
 }
 #endif
diff --git a/vdc.c b/vdc.c
index 59f34d6..0a70bf7 100644
--- a/vdc.c
+++ b/vdc.c
@@ -38,20 +38,36 @@
 
 int main(int argc, char **argv) {
     int sock;
+    int wait_for_socket;
 
     if (argc < 2)
         usage(argv[0]);
 
-    if ((sock = socket_local_client("vold",
-                                     ANDROID_SOCKET_NAMESPACE_RESERVED,
-                                     SOCK_STREAM)) < 0) {
-        fprintf(stderr, "Error connecting (%s)\n", strerror(errno));
-        exit(4);
+    wait_for_socket = strcmp(argv[1], "--wait") == 0;
+    if(wait_for_socket) {
+        argv++;
+        argc--;
     }
 
-    if (!strcmp(argv[1], "monitor"))
+    if(argc < 2)
+        exit(5);
+
+    while ((sock = socket_local_client("vold",
+                                 ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                 SOCK_STREAM)) < 0) {
+        if(!wait_for_socket) {
+            fprintf(stderr, "Error connecting (%s)\n", strerror(errno));
+            exit(4);
+        } else {
+            sleep(1);
+        }
+    }
+
+    if (!strcmp(argv[1], "monitor")) {
         exit(do_monitor(sock, 0));
-    exit(do_cmd(sock, argc, argv));
+    } else {
+        exit(do_cmd(sock, argc, argv));
+    }
 }
 
 static int do_cmd(int sock, int argc, char **argv) {
@@ -118,7 +134,7 @@
                     return ECONNRESET;
                 return errno;
             }
-            
+
             int offset = 0;
             int i = 0;
 
@@ -146,7 +162,7 @@
 }
 
 static void usage(char *progname) {
-    fprintf(stderr, "Usage: %s <monitor>|<cmd> [arg1] [arg2...]\n", progname);
-    exit(1);
-}
+    fprintf(stderr,
+            "Usage: %s [--wait] <monitor>|<cmd> [arg1] [arg2...]\n", progname);
+ }