Add basic exFAT support.

Several partners have been requesting exFAT support.  Android doesn't
natively support exFAT, but we're at least willing to try mounting an
exFAT filesystem if we detect the Linux kernel supports it, and if
helper binaries are present.

This CL is simple scaffolding, and it provides no actual
implementation of exFAT.

Test: builds, boots
Bug: 67822822
Change-Id: Id4f8ec3967b32de6e1c0e3c4b47fe6e43a6291ab
diff --git a/model/Disk.cpp b/model/Disk.cpp
index 9b772e4..becf8b7 100644
--- a/model/Disk.cpp
+++ b/model/Disk.cpp
@@ -364,12 +364,13 @@
                 }
 
                 switch (type) {
-                case 0x06: // FAT16
-                case 0x0b: // W95 FAT32 (LBA)
-                case 0x0c: // W95 FAT32 (LBA)
-                case 0x0e: // W95 FAT16 (LBA)
-                    createPublicVolume(partDevice);
-                    break;
+                    case 0x06:  // FAT16
+                    case 0x07:  // HPFS/NTFS/exFAT
+                    case 0x0b:  // W95 FAT32 (LBA)
+                    case 0x0c:  // W95 FAT32 (LBA)
+                    case 0x0e:  // W95 FAT16 (LBA)
+                        createPublicVolume(partDevice);
+                        break;
                 }
             } else if (table == Table::kGpt) {
                 if (++it == split.end()) continue;
diff --git a/model/PublicVolume.cpp b/model/PublicVolume.cpp
index 98c897f..efdb687 100644
--- a/model/PublicVolume.cpp
+++ b/model/PublicVolume.cpp
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-#include "fs/Vfat.h"
 #include "PublicVolume.h"
 #include "Utils.h"
 #include "VolumeManager.h"
+#include "fs/Exfat.h"
+#include "fs/Vfat.h"
 
 #include <android-base/stringprintf.h>
 #include <android-base/logging.h>
@@ -93,19 +94,23 @@
 }
 
 status_t PublicVolume::doMount() {
-    // TODO: expand to support mounting other filesystems
     readMetadata();
 
-    if (mFsType != "vfat") {
+    if (mFsType == "vfat" && vfat::IsSupported()) {
+        if (vfat::Check(mDevPath)) {
+            LOG(ERROR) << getId() << " failed filesystem check";
+            return -EIO;
+        }
+    } else if (mFsType == "exfat" && exfat::IsSupported()) {
+        if (exfat::Check(mDevPath)) {
+            LOG(ERROR) << getId() << " failed filesystem check";
+            return -EIO;
+        }
+    } else {
         LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;
         return -EIO;
     }
 
-    if (vfat::Check(mDevPath)) {
-        LOG(ERROR) << getId() << " failed filesystem check";
-        return -EIO;
-    }
-
     // Use UUID as stable name, if available
     std::string stableName = getId();
     if (!mFsUuid.empty()) {
@@ -130,10 +135,17 @@
         return -errno;
     }
 
-    if (vfat::Mount(mDevPath, mRawPath, false, false, false,
-            AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) {
-        PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
-        return -EIO;
+    if (mFsType == "vfat") {
+        if (vfat::Mount(mDevPath, mRawPath, false, false, false, AID_MEDIA_RW, AID_MEDIA_RW, 0007,
+                        true)) {
+            PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
+            return -EIO;
+        }
+    } else if (mFsType == "exfat") {
+        if (exfat::Mount(mDevPath, mRawPath, AID_MEDIA_RW, AID_MEDIA_RW, 0007)) {
+            PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
+            return -EIO;
+        }
     }
 
     if (getMountFlags() & MountFlags::kPrimary) {
@@ -238,7 +250,7 @@
 }
 
 status_t PublicVolume::doFormat(const std::string& fsType) {
-    if (fsType == "vfat" || fsType == "auto") {
+    if ((fsType == "vfat" || fsType == "auto") && vfat::IsSupported()) {
         if (WipeBlockDevice(mDevPath) != OK) {
             LOG(WARNING) << getId() << " failed to wipe";
         }
@@ -246,6 +258,14 @@
             LOG(ERROR) << getId() << " failed to format";
             return -errno;
         }
+    } else if ((fsType == "exfat" || fsType == "auto") && exfat::IsSupported()) {
+        if (WipeBlockDevice(mDevPath) != OK) {
+            LOG(WARNING) << getId() << " failed to wipe";
+        }
+        if (exfat::Format(mDevPath)) {
+            LOG(ERROR) << getId() << " failed to format";
+            return -errno;
+        }
     } else {
         LOG(ERROR) << "Unsupported filesystem " << fsType;
         return -EINVAL;