Add the ability to revert a crypto mapping when unmounting a volume

Add the force_and_revert option to the unmount command which will force
the unmount, and revert a crypto mapping.  This is used during factory
reset so that when the internal sdcard volume is formatted, it formats
the raw device, not the encrypted mapping.

Change-Id: I36b6ff9bb54863b121de635472a303bf4a2334a9
diff --git a/CommandListener.cpp b/CommandListener.cpp
index 1672fcc..3a83d2a 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -138,16 +138,22 @@
         }
         rc = vm->mountVolume(argv[2]);
     } else if (!strcmp(argv[1], "unmount")) {
-        if (argc < 3 || argc > 4 || (argc == 4 && strcmp(argv[3], "force"))) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume unmount <path> [force]", false);
+        if (argc < 3 || argc > 4 ||
+           ((argc == 4 && strcmp(argv[3], "force")) &&
+            (argc == 4 && strcmp(argv[3], "force_and_revert")))) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume unmount <path> [force|force_and_revert]", false);
             return 0;
         }
 
         bool force = false;
+        bool revert = false;
         if (argc >= 4 && !strcmp(argv[3], "force")) {
             force = true;
+        } else if (argc >= 4 && !strcmp(argv[3], "force_and_revert")) {
+            force = true;
+            revert = true;
         }
-        rc = vm->unmountVolume(argv[2], force);
+        rc = vm->unmountVolume(argv[2], force, revert);
     } else if (!strcmp(argv[1], "format")) {
         if (argc != 3) {
             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume format <path>", false);
diff --git a/DirectVolume.cpp b/DirectVolume.cpp
index 2ddd5ab..4acee76 100644
--- a/DirectVolume.cpp
+++ b/DirectVolume.cpp
@@ -317,7 +317,7 @@
             SLOGE("Failed to cleanup ASEC - unmount will probably fail!");
         }
 
-        if (Volume::unmountVol(true)) {
+        if (Volume::unmountVol(true, false)) {
             SLOGE("Failed to unmount volume on bad removal (%s)", 
                  strerror(errno));
             // XXX: At this point we're screwed for now
@@ -392,6 +392,16 @@
     mPaths->erase(it); /* Remove it from the list */
     addPath(new_path); /* Put the new path on the list */
 
+    /* Save away original info so we can restore it when doing factory reset.
+     * Then, when doing the format, it will format the original device in the
+     * clear, otherwise it just formats the encrypted device which is not
+     * readable when the device boots unencrypted after the reset.
+     */
+    mOrigDiskMajor = mDiskMajor;
+    mOrigDiskMinor = mDiskMinor;
+    mOrigPartIdx = mPartIdx;
+    memcpy(mOrigPartMinors, mPartMinors, sizeof(mPartMinors));
+
     mDiskMajor = new_major;
     mDiskMinor = new_minor;
     /* Ugh, virual block devices don't use minor 0 for whole disk and minor > 0 for
@@ -410,6 +420,24 @@
 }
 
 /*
+ * Called from base to revert device info to the way it was before a
+ * crypto mapping was created for it.
+ */
+void DirectVolume::revertDeviceInfo(void)
+{
+    if (mIsDecrypted) {
+        mDiskMajor = mOrigDiskMajor;
+        mDiskMinor = mOrigDiskMinor;
+        mPartIdx = mOrigPartIdx;
+        memcpy(mPartMinors, mOrigPartMinors, sizeof(mPartMinors));
+
+        mIsDecrypted = 0;
+    }
+
+    return;
+}
+
+/*
  * Called from base to give cryptfs all the info it needs to encrypt eligible volumes
  */
 int DirectVolume::getVolInfo(struct volume_info *v)
diff --git a/DirectVolume.h b/DirectVolume.h
index ad1b386..de1ed8b 100644
--- a/DirectVolume.h
+++ b/DirectVolume.h
@@ -33,6 +33,9 @@
     int            mDiskMajor;
     int            mDiskMinor;
     int            mPartMinors[MAX_PARTITIONS];
+    int            mOrigDiskMajor;
+    int            mOrigDiskMinor;
+    int            mOrigPartMinors[MAX_PARTITIONS];
     int            mDiskNumParts;
     unsigned char  mPendingPartMap;
     int            mIsDecrypted;
@@ -55,6 +58,7 @@
 protected:
     int getDeviceNodes(dev_t *devs, int max);
     int updateDeviceInfo(char *new_path, int new_major, int new_minor);
+    virtual void revertDeviceInfo(void);
     int isDecrypted() { return mIsDecrypted; }
     int getFlags() { return mFlags; }
 
diff --git a/Volume.cpp b/Volume.cpp
index 45db27e..e70a590 100644
--- a/Volume.cpp
+++ b/Volume.cpp
@@ -591,7 +591,7 @@
     return -1;
 }
 
-int Volume::unmountVol(bool force) {
+int Volume::unmountVol(bool force, bool revert) {
     int i, rc;
 
     if (getState() != Volume::State_Mounted) {
@@ -645,6 +645,16 @@
 
     SLOGI("%s unmounted sucessfully", getMountpoint());
 
+    /* If this is an encrypted volume, and we've been asked to undo
+     * the crypto mapping, then revert the dm-crypt mapping, and revert
+     * the device info to the original values.
+     */
+    if (revert && isDecrypted()) {
+        cryptfs_revert_volume(getLabel());
+        revertDeviceInfo();
+        SLOGI("Encrypted volume %s reverted successfully", getMountpoint());
+    }
+
     setState(Volume::State_Idle);
     mCurrentlyMountedKdev = -1;
     return 0;
diff --git a/Volume.h b/Volume.h
index f57ff91..274fb54 100644
--- a/Volume.h
+++ b/Volume.h
@@ -52,6 +52,7 @@
     VolumeManager *mVm;
     bool mDebug;
     int mPartIdx;
+    int mOrigPartIdx;
     bool mRetryMount;
 
     /*
@@ -64,7 +65,7 @@
     virtual ~Volume();
 
     int mountVol();
-    int unmountVol(bool force);
+    int unmountVol(bool force, bool revert);
     int formatVol();
 
     const char *getLabel() { return mLabel; }
@@ -85,6 +86,7 @@
 
     virtual int getDeviceNodes(dev_t *devs, int max) = 0;
     virtual int updateDeviceInfo(char *new_path, int new_major, int new_minor) = 0;
+    virtual void revertDeviceInfo(void) = 0;
     virtual int isDecrypted(void) = 0;
     virtual int getFlags(void) = 0;
 
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 0f04a06..0eb72a1 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -1045,7 +1045,7 @@
     VolumeManager *vm = VolumeManager::Instance();
     vm->disableVolumeManager();
     vm->unshareVolume(label, "ums");
-    return vm->unmountVolume(label, true);
+    return vm->unmountVolume(label, true, false);
 }
 
 extern "C" int vold_getNumDirectVolumes(void) {
@@ -1087,7 +1087,7 @@
     return 0;
 }
 
-int VolumeManager::unmountVolume(const char *label, bool force) {
+int VolumeManager::unmountVolume(const char *label, bool force, bool revert) {
     Volume *v = lookupVolume(label);
 
     if (!v) {
@@ -1109,7 +1109,7 @@
 
     cleanupAsec(v, force);
 
-    return v->unmountVol(force);
+    return v->unmountVol(force, revert);
 }
 
 /*
diff --git a/VolumeManager.h b/VolumeManager.h
index caa0b62..a000556 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -79,7 +79,7 @@
 
     int listVolumes(SocketClient *cli);
     int mountVolume(const char *label);
-    int unmountVolume(const char *label, bool force);
+    int unmountVolume(const char *label, bool force, bool revert);
     int shareVolume(const char *label, const char *method);
     int unshareVolume(const char *label, const char *method);
     int shareEnabled(const char *path, const char *method, bool *enabled);
diff --git a/cryptfs.c b/cryptfs.c
index 50c3e64..3107e3e 100644
--- a/cryptfs.c
+++ b/cryptfs.c
@@ -845,6 +845,15 @@
   return rc;
 }
 
+/* Called by vold when it wants to undo the crypto mapping of a volume it
+ * manages.  This is usually in response to a factory reset, when we want
+ * to undo the crypto mapping so the volume is formatted in the clear.
+ */
+int cryptfs_revert_volume(const char *label)
+{
+    return delete_crypto_blk_dev((char *)label);
+}
+
 /*
  * Called by vold when it's asked to mount an encrypted, nonremovable volume.
  * Setup a dm-crypt mapping, use the saved master key from
diff --git a/cryptfs.h b/cryptfs.h
index 8b4c37b..a0693d3 100644
--- a/cryptfs.h
+++ b/cryptfs.h
@@ -81,6 +81,7 @@
   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);
 #ifdef __cplusplus
 }
 #endif