vold: Add an optional wipe paramter to the volume format command

The new wipe option to the vold format command will invoke BLKDISCARD
on the partition before invoking newfs_msdos.  This will be used whenever
a full wipe of the device is wanted, as this is more secure than just
doing newfs_msdos.

Bug: 9392982
Change-Id: Ie106f1b9cc70abc61206006d1821641c27c7ccae
diff --git a/Android.mk b/Android.mk
index 098c3d0..71113df 100644
--- a/Android.mk
+++ b/Android.mk
@@ -15,6 +15,7 @@
 	Devmapper.cpp \
 	ResponseCode.cpp \
 	Xwarp.cpp \
+	VoldUtil.c \
 	fstrim.c \
 	cryptfs.c
 
diff --git a/CommandListener.cpp b/CommandListener.cpp
index f306527..5de920f 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -162,11 +162,16 @@
         }
         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);
+        if (argc < 3 || argc > 4 ||
+            (argc == 4 && strcmp(argv[3], "wipe"))) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume format <path> [wipe]", false);
             return 0;
         }
-        rc = vm->formatVolume(argv[2]);
+        bool wipe = false;
+        if (argc >= 4 && !strcmp(argv[3], "wipe")) {
+            wipe = true;
+        }
+        rc = vm->formatVolume(argv[2], wipe);
     } else if (!strcmp(argv[1], "share")) {
         if (argc != 4) {
             cli->sendMsg(ResponseCode::CommandSyntaxError,
diff --git a/Fat.cpp b/Fat.cpp
index 807f440..c967a90 100644
--- a/Fat.cpp
+++ b/Fat.cpp
@@ -30,6 +30,8 @@
 #include <sys/mman.h>
 #include <sys/mount.h>
 #include <sys/wait.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
 
 #include <linux/kdev_t.h>
 
@@ -167,12 +169,16 @@
     return rc;
 }
 
-int Fat::format(const char *fsPath, unsigned int numSectors) {
+int Fat::format(const char *fsPath, unsigned int numSectors, bool wipe) {
     int fd;
     const char *args[10];
     int rc;
     int status;
 
+    if (wipe) {
+        Fat::wipe(fsPath, numSectors);
+    }
+
     args[0] = MKDOSFS_PATH;
     args[1] = "-F";
     args[2] = "32";
@@ -220,3 +226,30 @@
     }
     return 0;
 }
+
+void Fat::wipe(const char *fsPath, unsigned int numSectors) {
+    int fd;
+    unsigned long long range[2];
+
+    fd = open(fsPath, O_RDWR);
+    if (fd >= 0) {
+        if (numSectors == 0) {
+            numSectors = get_blkdev_size(fd);
+        }
+        if (numSectors == 0) {
+            SLOGE("Fat wipe failed to determine size of %s", fsPath);
+            close(fd);
+            return;
+        }
+        range[0] = 0;
+        range[1] = (unsigned long long)numSectors * 512;
+        if (ioctl(fd, BLKDISCARD, &range) < 0) {
+            SLOGE("Fat wipe failed to discard blocks on %s", fsPath);
+        } else {
+            SLOGI("Fat wipe %d sectors on %s succeeded", numSectors, fsPath);
+        }
+        close(fd);
+    } else {
+        SLOGE("Fat wipe failed to open device %s", fsPath);
+    }
+}
diff --git a/Fat.h b/Fat.h
index e02d88c..19614d1 100644
--- a/Fat.h
+++ b/Fat.h
@@ -26,7 +26,10 @@
                        bool ro, bool remount, bool executable,
                        int ownerUid, int ownerGid, int permMask,
                        bool createLost);
-    static int format(const char *fsPath, unsigned int numSectors);
+    static int format(const char *fsPath, unsigned int numSectors, bool wipe);
+
+private:
+    static void wipe(const char *fsPath, unsigned int numSectors);
 };
 
 #endif
diff --git a/VoldUtil.c b/VoldUtil.c
new file mode 100644
index 0000000..b5f9946
--- /dev/null
+++ b/VoldUtil.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2013 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 <sys/ioctl.h>
+#include <linux/fs.h>
+
+unsigned int get_blkdev_size(int fd)
+{
+  unsigned int nr_sec;
+
+  if ( (ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
+    nr_sec = 0;
+  }
+
+  return nr_sec;
+}
diff --git a/VoldUtil.h b/VoldUtil.h
index 30a3add..469489a 100644
--- a/VoldUtil.h
+++ b/VoldUtil.h
@@ -17,6 +17,12 @@
 #ifndef _VOLDUTIL_H
 #define _VOLDUTIL_H
 
+#include <sys/cdefs.h>
+
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
+__BEGIN_DECLS
+  unsigned int get_blkdev_size(int fd);
+__END_DECLS
+
 #endif
diff --git a/Volume.cpp b/Volume.cpp
index 4a00ccc..4501e90 100644
--- a/Volume.cpp
+++ b/Volume.cpp
@@ -206,7 +206,7 @@
     return 0;
 }
 
-int Volume::formatVol() {
+int Volume::formatVol(bool wipe) {
 
     if (getState() == Volume::State_NoMedia) {
         errno = ENODEV;
@@ -250,7 +250,7 @@
         SLOGI("Formatting volume %s (%s)", getLabel(), devicePath);
     }
 
-    if (Fat::format(devicePath, 0)) {
+    if (Fat::format(devicePath, 0, wipe)) {
         SLOGE("Failed to format (%s)", strerror(errno));
         goto err;
     }
diff --git a/Volume.h b/Volume.h
index c717d4d..22e247d 100644
--- a/Volume.h
+++ b/Volume.h
@@ -67,7 +67,7 @@
 
     int mountVol();
     int unmountVol(bool force, bool revert);
-    int formatVol();
+    int formatVol(bool wipe);
 
     const char *getLabel() { return mLabel; }
     const char *getMountpoint() { return mMountpoint; }
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index a1930d1..180387c 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -167,7 +167,7 @@
     return 0;
 }
 
-int VolumeManager::formatVolume(const char *label) {
+int VolumeManager::formatVolume(const char *label, bool wipe) {
     Volume *v = lookupVolume(label);
 
     if (!v) {
@@ -180,7 +180,7 @@
         return -1;
     }
 
-    return v->formatVol();
+    return v->formatVol(wipe);
 }
 
 int VolumeManager::getObbMountPath(const char *sourceFile, char *mountPath, int mountPathLen) {
@@ -414,7 +414,7 @@
         if (usingExt4) {
             formatStatus = Ext4::format(dmDevice, mountPoint);
         } else {
-            formatStatus = Fat::format(dmDevice, numImgSectors);
+            formatStatus = Fat::format(dmDevice, numImgSectors, 0);
         }
 
         if (formatStatus < 0) {
diff --git a/VolumeManager.h b/VolumeManager.h
index 198b5a9..be78516 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -83,7 +83,7 @@
     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);
-    int formatVolume(const char *label);
+    int formatVolume(const char *label, bool wipe);
     void disableVolumeManager(void) { mVolManagerDisabled = 1; }
 
     /* ASEC */
diff --git a/cryptfs.c b/cryptfs.c
index 1bf7049..c86e712 100644
--- a/cryptfs.c
+++ b/cryptfs.c
@@ -46,6 +46,7 @@
 #include "cutils/properties.h"
 #include "hardware_legacy/power.h"
 #include "VolumeManager.h"
+#include "VoldUtil.h"
 
 #define DM_CRYPT_BUF_SIZE 4096
 #define DATA_MNT_POINT "/data"
@@ -115,17 +116,6 @@
     return (unsigned int) (len / 512);
 }
 
-static unsigned int get_blkdev_size(int fd)
-{
-  unsigned int nr_sec;
-
-  if ( (ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
-    nr_sec = 0;
-  }
-
-  return nr_sec;
-}
-
 static int get_crypt_ftr_info(char **metadata_fname, off64_t *off)
 {
   static int cached_data = 0;