Tuxera exFAT vold patch for Android 11.

Issue: FP3-A11#46
Change-Id: Ifbe147f8f8273d617c9fe447539d69e83607730e
diff --git a/Android.bp b/Android.bp
index 423100c..aadb092 100644
--- a/Android.bp
+++ b/Android.bp
@@ -135,9 +135,13 @@
         "VoldUtil.cpp",
         "VolumeManager.cpp",
         "cryptfs.cpp",
+        "fs/Apfs.cpp",
         "fs/Exfat.cpp",
         "fs/Ext4.cpp",
         "fs/F2fs.cpp",
+        "fs/Filesystems.cpp",
+        "fs/HfsPlus.cpp",
+        "fs/Ntfs.cpp",
         "fs/Vfat.cpp",
         "model/Disk.cpp",
         "model/EmulatedVolume.cpp",
diff --git a/fs/Apfs.cpp b/fs/Apfs.cpp
new file mode 100644
index 0000000..5e8b494
--- /dev/null
+++ b/fs/Apfs.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010-2021 Tuxera Inc.
+ *
+ * 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 <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <linux/kdev_t.h>
+
+#define LOG_TAG "Vold::Apfs"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <selinux/selinux.h>
+
+#include <logwrap/logwrap.h>
+
+#include "Utils.h"
+#include "Apfs.h"
+#include "VoldUtil.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+namespace apfs {
+
+status_t Detect(const std::string& source, bool *outResult) {
+    const char *const fsPath = source.c_str();
+
+    int retval = -1;
+
+    int fd = open(fsPath, O_RDONLY | O_CLOEXEC);
+    if (fd != -1) {
+        loff_t seek_res = lseek64(fd, 0, SEEK_SET);
+        if (seek_res == 0) {
+            char sb_region[4096];
+            ssize_t read_res = read(fd, sb_region, 4096);
+            if (read_res >= 512) {
+                if (!memcmp(&sb_region[32], "NXSB", 4)) {
+                    LOG(INFO) << "APFS filesystem detected.";
+                    *outResult = true;
+                }
+                else {
+                    LOG(VERBOSE) << "Filesystem detection failed (not an APFS "
+                                 << "filesystem).";
+                    *outResult = false;
+                }
+
+                retval = 0;
+            }
+            else if (read_res != -1) {
+                int err = errno ? errno : EIO;
+                LOG(ERROR) << "Error while reading 4096 bytes from device "
+                           << "offset 0: " << strerror(errno) << " (" << errno
+                           << ")";
+                errno = err;
+            }
+        }
+        else if (seek_res != -1) {
+            int err = errno ? errno : EIO;
+            LOG(ERROR) << "Error while seeking to device offset 0: "
+                       << strerror(errno) << " (" << errno << ")";
+            errno = err;
+        }
+
+        close(fd);
+    }
+
+    return retval;
+}
+
+}  // namespace apfs
+}  // namespace vold
+}  // namespace android
diff --git a/fs/Apfs.h b/fs/Apfs.h
new file mode 100644
index 0000000..93ab84e
--- /dev/null
+++ b/fs/Apfs.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010-2021 Tuxera Inc.
+ *
+ * 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 ANDROID_VOLD_APFS_H
+#define ANDROID_VOLD_APFS_H
+
+#include <utils/Errors.h>
+
+#include <string>
+
+namespace android {
+namespace vold {
+namespace apfs {
+
+status_t Detect(const std::string& source, bool *outResult);
+
+}  // namespace apfs
+}  // namespace vold
+}  // namespace android
+
+#endif
diff --git a/fs/Exfat.cpp b/fs/Exfat.cpp
index 34f1024..6845563 100644
--- a/fs/Exfat.cpp
+++ b/fs/Exfat.cpp
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2010-2021 Tuxera Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +15,11 @@
  * limitations under the License.
  */
 
+#include <fcntl.h>
 #include <sys/mount.h>
 
+#define LOG_TAG "Vold::Exfat"
+
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 
@@ -30,18 +34,67 @@
 namespace vold {
 namespace exfat {
 
-static const char* kMkfsPath = "/system/bin/mkfs.exfat";
-static const char* kFsckPath = "/system/bin/fsck.exfat";
+static const char* kMkfsPath = "/system/bin/mkexfat";
+static const char* kFsckPath = "/system/bin/exfatck";
+
+status_t Detect(const std::string& source, bool *outResult) {
+    const char *const fsPath = source.c_str();
+
+    int retval = -1;
+
+    int fd = open(fsPath, O_RDONLY | O_CLOEXEC);
+    if (fd != -1) {
+        loff_t seek_res = lseek64(fd, 0, SEEK_SET);
+        if (seek_res == 0) {
+            char sb_region[4096];
+            ssize_t read_res = read(fd, sb_region, 4096);
+            if (read_res >= 512) {
+                if (!memcmp(&sb_region[3], "EXFAT   ", 8)) {
+                    LOG(INFO) << "exFAT filesystem detected.";
+                    *outResult = true;
+                }
+                else if (!memcmp(&sb_region[0], "RRaAXFAT   ", 11)) {
+                    LOG(INFO) << "Corrupted exFAT filesystem detected. Fixing.";
+                    *outResult = true;
+                }
+                else {
+                    LOG(VERBOSE) << "Filesystem detection failed (not an exFAT "
+                                 << "filesystem).";
+                    *outResult = false;
+                }
+
+                retval = 0;
+            }
+            else if (read_res != -1) {
+                int err = errno ? errno : EIO;
+                LOG(ERROR) << "Error while reading 4096 bytes from device "
+                           << "offset 0: " << strerror(errno) << " (" << errno
+                           << ")";
+                errno = err;
+            }
+        }
+        else if (seek_res != -1) {
+            int err = errno ? errno : EIO;
+            LOG(ERROR) << "Error while seeking to device offset 0: "
+                       << strerror(errno) << " (" << errno << ")";
+            errno = err;
+        }
+
+        close(fd);
+    }
+
+    return retval;
+}
 
 bool IsSupported() {
     return access(kMkfsPath, X_OK) == 0 && access(kFsckPath, X_OK) == 0 &&
-           IsFilesystemSupported("exfat");
+           IsFilesystemSupported("texfat");
 }
 
 status_t Check(const std::string& source) {
     std::vector<std::string> cmd;
     cmd.push_back(kFsckPath);
-    cmd.push_back("-a");
+    cmd.push_back("-r");
     cmd.push_back(source);
 
     int rc = ForkExecvp(cmd, nullptr, sFsckUntrustedContext);
@@ -50,35 +103,77 @@
         return 0;
     } else {
         LOG(ERROR) << "Check failed (code " << rc << ")";
+
+        /* Ignore return/errno value. We know it's exFAT, so any file system
+         * check/repair results should be considered a bonus. The driver should
+         * be able to mount the volume in any case and deal with inconsistencies
+         * appropriately. */
+        errno = 0;
+        return 0;
+    }
+}
+
+status_t Mount(const std::string& source, const std::string& target, bool ro,
+        bool remount, bool executable, int ownerUid, int ownerGid, int permMask)
+{
+    int rc;
+    unsigned long flags;
+    auto mountData = android::base::StringPrintf("utf8,uid=%d,gid=%d,fmask=%o,dmask=%o", ownerUid,
+                                                 ownerGid, permMask, permMask);
+
+    flags = MS_NODEV | MS_NOSUID | MS_DIRSYNC | MS_NOATIME;
+
+    flags |= (executable ? 0 : MS_NOEXEC);
+    flags |= (ro ? MS_RDONLY : 0);
+    flags |= (remount ? MS_REMOUNT : 0);
+
+    rc = mount(source.c_str(), target.c_str(), "texfat", flags, mountData.c_str());
+
+    if (rc && errno == EROFS) {
+        LOG(ERROR) << source << "appears to be a read only filesystem - retrying mount RO";
+        flags |= MS_RDONLY;
+        rc = mount(source.c_str(), target.c_str(), "texfat", flags, mountData.c_str());
+    }
+
+    return rc;
+}
+
+static status_t DoFormat(const std::string& source, unsigned long numSectors,
+        bool wholeDevice, bool partitionOnly, bool wipe) {
+    const char *const fsPath = source.c_str();
+
+    if (!wholeDevice && partitionOnly) {
+        LOG(ERROR) << "Cannot partition non-whole device.";
         errno = EIO;
         return -1;
     }
-}
 
-status_t Mount(const std::string& source, const std::string& target, int ownerUid, int ownerGid,
-               int permMask) {
-    int mountFlags = MS_NODEV | MS_NOSUID | MS_DIRSYNC | MS_NOATIME | MS_NOEXEC;
-    auto mountData = android::base::StringPrintf("uid=%d,gid=%d,fmask=%o,dmask=%o", ownerUid,
-                                                 ownerGid, permMask, permMask);
+    LOG(INFO) << (partitionOnly ? "Partitioning" : "Formatting")
+              << " SDXC " << (partitionOnly ? "card" : "exFAT file system")
+              << " at \"" << fsPath << "\" as "
+              << (wholeDevice ? "whole device" : "single partition") << "...";
 
-    if (mount(source.c_str(), target.c_str(), "exfat", mountFlags, mountData.c_str()) == 0) {
-        return 0;
-    }
-
-    PLOG(ERROR) << "Mount failed; attempting read-only";
-    mountFlags |= MS_RDONLY;
-    if (mount(source.c_str(), target.c_str(), "exfat", mountFlags, mountData.c_str()) == 0) {
-        return 0;
-    }
-
-    return -1;
-}
-
-status_t Format(const std::string& source) {
     std::vector<std::string> cmd;
     cmd.push_back(kMkfsPath);
-    cmd.push_back("-n");
-    cmd.push_back("android");
+    if (wholeDevice)
+        cmd.push_back("--sda-whole");
+    cmd.push_back("--sda-strict");
+
+    if (partitionOnly) {
+        cmd.push_back("--sda-partitioning-only");
+    }
+
+    if (wipe) {
+        cmd.push_back("--discard");
+    }
+
+    if (numSectors) {
+        char tmp[32];
+        snprintf(tmp, sizeof(tmp), "%lu", numSectors);
+        cmd.push_back("--sector-count");
+        cmd.push_back(tmp);
+    }
+
     cmd.push_back(source);
 
     int rc = ForkExecvp(cmd);
@@ -93,6 +188,196 @@
     return 0;
 }
 
+status_t Format(const std::string& source, unsigned long numSectors) {
+    return DoFormat(source, numSectors, false, false, false);
+}
+
+static int getDeviceSize(const char *const devicePath, uint64_t *const outSize)
+{
+    int err = 0;
+    int fd = open(devicePath, O_RDONLY | O_CLOEXEC);
+    if (fd == -1)
+        err = errno ? errno : EIO;
+    else {
+        uint64_t size;
+        if (ioctl(fd, BLKGETSIZE64, &size) == -1)
+            err = errno ? errno : EIO;
+        else
+            *outSize = size;
+
+        close(fd);
+    }
+
+    return err;
+}
+
+int CheckSize(const std::string& source, bool isPartition) {
+    const char *const wholeDevicePath = source.c_str();
+
+    const unsigned long long limit_min =
+        isPartition ? 34342961152ULL : 34359738368ULL;
+    const unsigned long long limit_max =
+        isPartition ? 2198956146688ULL : 2199023255552ULL;
+
+    int res;
+    uint64_t size = 0;
+    int err = getDeviceSize(wholeDevicePath, &size);
+    if (!err) {
+        if (size < limit_min || size > limit_max)
+            res = 0;
+        else
+            res = 1;
+    }
+    else {
+        res = -1;
+        errno = err;
+    }
+
+    return res;
+}
+
+status_t PartitionDisk(const std::string& source) {
+    return DoFormat(source, 0, true, true, false);
+}
+
+#ifndef VFAT_IOCTL_GET_VOLUME_ID
+#define VFAT_IOCTL_GET_VOLUME_ID    _IOR('r', 0x12, __u32)
+#endif
+
+extern "C" {
+    typedef struct _TUXERA_IOCTL_GET_VOLUME_LABEL_ARGS {
+        __u32 size;     /* Size in bytes of volume label buffer. */
+        __u8 label[];   /* Buffer to return the volume label into of size at least @size
+                           bytes. */
+    } __attribute__ ((packed)) TUXERA_VOLUME_LABEL;
+}
+
+#ifndef TUXERA_GET_VOLUME_LABEL
+#define TUXERA_GET_VOLUME_LABEL     _IOWR('X', 2, TUXERA_VOLUME_LABEL)
+#endif
+
+int ExtractMetadata(const char *mountPoint, char **outVolumeLabel,
+                    char **outVolumeUuid)
+{
+    int err = 0;
+    __u32 returnedLabelSize;
+    __u32 volumeLabelSize;
+    TUXERA_VOLUME_LABEL *volumeLabel = NULL;
+    __u32 volumeLabelBufferSize = 0;
+    char *volumeLabelBuffer = NULL;
+    __u32 volumeUuidBufferSize = 0;
+    char *volumeUuidBuffer = NULL;
+    int fd = -1;
+    int volumeId;
+
+    fd = open(mountPoint, O_RDONLY | O_CLOEXEC);
+    if(fd == -1) {
+        err = errno ? errno : ENOENT;
+        LOG(ERROR) << "Error while opening mount point: " << strerror(errno)
+                   << " (" << errno << ")";
+        goto out;
+    }
+
+    volumeLabelSize = 4096;
+    while(1) {
+        TUXERA_VOLUME_LABEL *newVolumeLabel;
+        int res;
+
+        newVolumeLabel = (TUXERA_VOLUME_LABEL*) realloc(volumeLabel,
+            sizeof(TUXERA_VOLUME_LABEL) + volumeLabelSize);
+        if(!newVolumeLabel) {
+            err = errno ? errno : ENOMEM;
+            LOG(ERROR) << "Error while reallocating memory (" << volumeLabelSize
+                       << " bytes) for TUXERA_VOLUME_LABEL: " << strerror(errno)
+                       << " (" << errno << ")";
+            goto out;
+        }
+
+        volumeLabel = newVolumeLabel;
+
+        memset(volumeLabel, 0, sizeof(TUXERA_VOLUME_LABEL) + volumeLabelSize);
+        volumeLabel->size = volumeLabelSize;
+
+        if((res = ioctl(fd, TUXERA_GET_VOLUME_LABEL, volumeLabel)) < 0) {
+            err = errno ? errno : EINVAL;
+            LOG(ERROR) << "Error while issuing IOCTL to get volume label: "
+                       << strerror(errno) << " (" << errno << ")";
+            goto out;
+        }
+
+        returnedLabelSize = (__u32) res;
+
+        if(volumeLabelSize >= returnedLabelSize + 1) {
+            break;
+        }
+
+        volumeLabelSize = returnedLabelSize + 1;
+    }
+
+    volumeLabelBufferSize = volumeLabel->size + 1;
+    volumeLabelBuffer = (char*) malloc(volumeLabelBufferSize);
+    if(!volumeLabelBuffer) {
+        err = errno ? errno : ENOMEM;
+        LOG(ERROR) << "Error while allocating volume label buffer: "
+                   << strerror(errno) << " (" << errno << ")";
+        goto out;
+    }
+
+    memcpy(volumeLabelBuffer, volumeLabel->label, volumeLabel->size);
+    volumeLabelBuffer[volumeLabel->size] = '\0';
+
+    volumeUuidBufferSize = 10;
+    volumeUuidBuffer = (char*) malloc(volumeUuidBufferSize);
+    if(!volumeUuidBuffer) {
+        err = errno ? errno : ENOMEM;
+        LOG(ERROR) << "Error while allocating volume UUID buffer: "
+                   << strerror(errno) << " (" << errno << ")";
+        goto out;
+    }
+
+    volumeId = ioctl(fd, VFAT_IOCTL_GET_VOLUME_ID, NULL);
+    if(volumeId == -1) {
+        err = errno ? errno : EINVAL;
+        LOG(ERROR) << "Error while issuing IOCTL to get volume ID: "
+                   << strerror(errno) << " (" << errno << ")";
+        goto out;
+    }
+
+    if(snprintf(volumeUuidBuffer, volumeUuidBufferSize, "%04X-%04X",
+            (volumeId >> 16) & 0xFFFF, volumeId & 0xFFFF) != 9)
+    {
+        err = errno ? errno : EINVAL;
+        LOG(ERROR) << "Error while constructing volume UUID string: "
+                   << strerror(errno) << " (" << errno << ")";
+        goto out;
+    }
+
+out:
+    if(err) {
+        if(volumeUuidBuffer) {
+            free(volumeUuidBuffer);
+        }
+
+        if(volumeLabelBuffer) {
+            free(volumeLabelBuffer);
+        }
+    }
+    else {
+        *outVolumeLabel = volumeLabelBuffer;
+        *outVolumeUuid = volumeUuidBuffer;
+    }
+
+    if(volumeLabel) {
+        free(volumeLabel);
+    }
+
+    if(fd != -1) {
+        close(fd);
+    }
+
+    return err;
+}
+
 }  // namespace exfat
 }  // namespace vold
 }  // namespace android
diff --git a/fs/Exfat.h b/fs/Exfat.h
index 768d8a5..cc98b21 100644
--- a/fs/Exfat.h
+++ b/fs/Exfat.h
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2010-2021 Tuxera Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -25,12 +26,22 @@
 namespace vold {
 namespace exfat {
 
+status_t Detect(const std::string& source, bool *outResult);
+
 bool IsSupported();
 
 status_t Check(const std::string& source);
-status_t Mount(const std::string& source, const std::string& target, int ownerUid, int ownerGid,
-               int permMask);
-status_t Format(const std::string& source);
+status_t Mount(const std::string& source, const std::string& target, bool ro,
+        bool remount, bool executable, int ownerUid, int ownerGid,
+        int permMask);
+status_t Format(const std::string& source, unsigned long numSectors);
+
+status_t CheckSize(const std::string& source, bool isPartition);
+
+status_t PartitionDisk(const std::string& source);
+
+int ExtractMetadata(const char *mountPoint, char **outVolumeLabel,
+                    char **outVolumeUuid);
 
 }  // namespace exfat
 }  // namespace vold
diff --git a/fs/Filesystems.cpp b/fs/Filesystems.cpp
new file mode 100644
index 0000000..ff916d2
--- /dev/null
+++ b/fs/Filesystems.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014-2021 Tuxera Inc.
+ *
+ * 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 "Filesystems.h"
+
+#include "Apfs.h"
+#include "HfsPlus.h"
+#include "Ntfs.h"
+#include "Exfat.h"
+#include "Vfat.h"
+
+#include <errno.h>
+
+namespace android {
+namespace vold {
+namespace filesystems {
+
+status_t Detect(const std::string& source, FSType *outFsType) {
+    int err;
+    bool result = false;
+
+    if ((err = hfsplus::Detect(source, &result)) == 0 && result) {
+        *outFsType = FSTYPE_HFSPLUS;
+    }
+    else if ((err = ntfs::Detect(source, &result)) == 0 && result) {
+        *outFsType = FSTYPE_NTFS;
+    }
+    else if ((err = exfat::Detect(source, &result)) == 0 && result) {
+        *outFsType = FSTYPE_EXFAT;
+    }
+    else if ((err = apfs::Detect(source, &result)) == 0 && result) {
+        *outFsType = FSTYPE_APFS;
+    }
+    else {
+        /* Until we implement reliable FAT detection code. */
+        *outFsType = FSTYPE_FAT;
+    }
+
+    return 0; /* Always succeed since FAT is the default fallback. */
+}
+
+bool IsSupported(FSType fsType) {
+    switch (fsType) {
+    case FSTYPE_APFS:
+        return false;
+    case FSTYPE_HFSPLUS:
+        return false;
+    case FSTYPE_NTFS:
+        return false;
+    case FSTYPE_EXFAT:
+        return exfat::IsSupported();
+    case FSTYPE_FAT:
+        return vfat::IsSupported();
+    default:
+        return false;
+    }
+}
+
+status_t Check(FSType fsType, const std::string& source) {
+    switch (fsType) {
+    case FSTYPE_APFS:
+        errno = ENODATA;
+        return -1;
+    case FSTYPE_HFSPLUS:
+        errno = ENODATA;
+        return -1;
+    case FSTYPE_NTFS:
+        errno = ENODATA;
+        return -1;
+    case FSTYPE_EXFAT:
+        return exfat::Check(source);
+    case FSTYPE_FAT:
+        return vfat::Check(source);
+    default:
+        errno = ENODATA;
+        return -1;
+    }
+}
+
+status_t Mount(FSType fsType, const std::string& source,
+        const std::string& target, bool ro, bool remount, bool executable,
+        int ownerUid, int ownerGid, int permMask, bool createLost) {
+    switch (fsType) {
+    case FSTYPE_APFS:
+        errno = ENOTSUP;
+        return -1;
+    case FSTYPE_HFSPLUS:
+        errno = ENOTSUP;
+        return -1;
+    case FSTYPE_NTFS:
+        errno = ENOTSUP;
+        return -1;
+    case FSTYPE_EXFAT:
+        return exfat::Mount(source, target, ro, remount, executable, ownerUid,
+                            ownerGid, permMask);
+    case FSTYPE_FAT:
+        return vfat::Mount(source, target, ro, remount, executable, ownerUid,
+                           ownerGid, permMask, createLost);
+    default:
+        errno = ENOTSUP;
+        return -1;
+    }
+}
+
+const char* FsName(FSType fsType) {
+    switch (fsType) {
+    case FSTYPE_FAT:
+        return "VFAT";
+    case FSTYPE_EXFAT:
+        return "exFAT";
+    case FSTYPE_NTFS:
+        return "NTFS";
+    case FSTYPE_HFSPLUS:
+        return "HFS+";
+    case FSTYPE_APFS:
+        return "APFS";
+    default:
+        return "<unknown filesystem>";
+    }
+}
+
+}  // namespace filesystems
+}  // namespace vold
+}  // namespace android
diff --git a/fs/Filesystems.h b/fs/Filesystems.h
new file mode 100644
index 0000000..f7fdf91
--- /dev/null
+++ b/fs/Filesystems.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014-2021 Tuxera Inc.
+ *
+ * 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 ANDROID_VOLD_FILESYSTEMS_H
+#define ANDROID_VOLD_FILESYSTEMS_H
+
+#include <utils/Errors.h>
+
+#include <string>
+
+namespace android {
+namespace vold {
+
+typedef enum {
+    FSTYPE_UNRECOGNIZED,
+    FSTYPE_FAT,
+    FSTYPE_EXFAT,
+    FSTYPE_NTFS,
+    FSTYPE_HFSPLUS,
+    FSTYPE_APFS,
+} FSType;
+
+namespace filesystems {
+
+int Detect(const std::string& source, FSType *outFsType);
+
+bool IsSupported(FSType fsType);
+
+status_t Check(FSType fsType, const std::string& source);
+status_t Mount(FSType fsType, const std::string& source, const std::string& target, bool ro,
+        bool remount, bool executable, int ownerUid, int ownerGid, int permMask,
+        bool createLost);
+status_t Format(FSType fsType, const std::string& source, unsigned long numSectors);
+
+const char* FsName(FSType fsType);
+
+}  // namespace filesystems
+}  // namespace vold
+}  // namespace android
+
+#endif
diff --git a/fs/HfsPlus.cpp b/fs/HfsPlus.cpp
new file mode 100644
index 0000000..979d17f
--- /dev/null
+++ b/fs/HfsPlus.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010-2021 Tuxera Inc.
+ *
+ * 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 <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <linux/kdev_t.h>
+
+#define LOG_TAG "Vold::HfsPlus"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <selinux/selinux.h>
+
+#include <logwrap/logwrap.h>
+
+#include "HfsPlus.h"
+#include "Utils.h"
+#include "VoldUtil.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+namespace hfsplus {
+
+status_t Detect(const std::string& source, bool *outResult) {
+    const char *const fsPath = source.c_str();
+
+    int retval = -1;
+
+    int fd = open(fsPath, O_RDONLY | O_CLOEXEC);
+    if (fd != -1) {
+        loff_t seek_res = lseek64(fd, 0, SEEK_SET);
+        if (seek_res == 0) {
+            char sb_region[4096];
+            ssize_t read_res = read(fd, sb_region, 4096);
+            if (read_res >= (1024 + 512)) {
+                if (!memcmp(&sb_region[1024], "H+\x00\x04", 4)) {
+                    LOG(INFO) << "HFS+ filesystem detected.";
+                    *outResult = true;
+                }
+                else if (!memcmp(&sb_region[1024], "HX\x00\x05", 4)) {
+                    LOG(INFO) << "HFSX filesystem detected.";
+                    *outResult = true;
+                }
+                else {
+                    LOG(VERBOSE) << "Filesystem detection failed (not an "
+                                 << "HFS+/HFSX filesystem).";
+                    *outResult = false;
+                }
+
+                retval = 0;
+            }
+            else if (read_res != -1) {
+                int err = errno ? errno : EIO;
+                LOG(ERROR) << "Error while reading 4096 bytes from device "
+                           << "offset 0: " << strerror(errno) << " (" << errno
+                           << ")";
+                errno = err;
+            }
+        }
+        else if (seek_res != -1) {
+            int err = errno ? errno : EIO;
+            LOG(ERROR) << "Error while seeking to device offset 0: "
+                       << strerror(errno) << " (" << errno << ")";
+            errno = err;
+        }
+
+        close(fd);
+    }
+
+    return retval;
+}
+
+}  // namespace hfsplus
+}  // namespace vold
+}  // namespace android
diff --git a/fs/HfsPlus.h b/fs/HfsPlus.h
new file mode 100644
index 0000000..e66bcb9
--- /dev/null
+++ b/fs/HfsPlus.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2011-2021 Tuxera Inc.
+ *
+ * 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 ANDROID_VOLD_HFSPLUS_H
+#define ANDROID_VOLD_HFSPLUS_H
+
+#include <utils/Errors.h>
+
+#include <string>
+
+namespace android {
+namespace vold {
+namespace hfsplus {
+
+status_t Detect(const std::string& source, bool *outResult);
+
+}  // namespace hfsplus
+}  // namespace vold
+}  // namespace android
+
+#endif
diff --git a/fs/Ntfs.cpp b/fs/Ntfs.cpp
new file mode 100644
index 0000000..b2da963
--- /dev/null
+++ b/fs/Ntfs.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010-2021 Tuxera Inc.
+ *
+ * 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 <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <linux/kdev_t.h>
+
+#define LOG_TAG "Vold::Ntfs"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <selinux/selinux.h>
+
+#include <logwrap/logwrap.h>
+
+#include "Ntfs.h"
+#include "Utils.h"
+#include "VoldUtil.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+namespace ntfs {
+
+status_t Detect(const std::string& source, bool *outResult) {
+    const char *const fsPath = source.c_str();
+
+    int retval = -1;
+
+    int fd = open(fsPath, O_RDONLY | O_CLOEXEC);
+    if (fd != -1) {
+        loff_t seek_res = lseek64(fd, 0, SEEK_SET);
+        if (seek_res == 0) {
+            char sb_region[4096];
+            ssize_t read_res = read(fd, sb_region, 4096);
+            if (read_res >= 512) {
+                if (!memcmp(&sb_region[3], "NTFS    ", 8)) {
+                    LOG(INFO) << "Detected NTFS filesystem.";
+                    *outResult = true;
+                }
+                else {
+                    LOG(VERBOSE) << "Filesystem detection failed (not an NTFS "
+                                 << "filesystem).";
+                    *outResult = false;
+                }
+
+                retval = 0;
+            }
+            else if (read_res != -1) {
+                int err = errno ? errno : EIO;
+                LOG(ERROR) << "Error while reading 4096 bytes from device "
+                           << "offset 0: " << strerror(errno) << " (" << errno
+                           << ")";
+                errno = err;
+            }
+        }
+        else if (seek_res != -1) {
+            int err = errno ? errno : EIO;
+            LOG(ERROR) << "Error while seeking to device offset 0: "
+                       << strerror(errno) << " (" << errno << ")";
+            errno = err;
+        }
+
+        close(fd);
+    }
+
+    return retval;
+}
+
+}  // namespace ntfs
+}  // namespace vold
+}  // namespace android
diff --git a/fs/Ntfs.h b/fs/Ntfs.h
new file mode 100644
index 0000000..ea825c4
--- /dev/null
+++ b/fs/Ntfs.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010-2021 Tuxera Inc.
+ *
+ * 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 ANDROID_VOLD_NTFS_H
+#define ANDROID_VOLD_NTFS_H
+
+#include <utils/Errors.h>
+
+#include <string>
+
+namespace android {
+namespace vold {
+namespace ntfs {
+
+status_t Detect(const std::string& source, bool *outResult);
+
+}  // namespace ntfs
+}  // namespace vold
+}  // namespace android
+
+#endif
diff --git a/fs/Vfat.cpp b/fs/Vfat.cpp
index b597b17..40d2588 100644
--- a/fs/Vfat.cpp
+++ b/fs/Vfat.cpp
@@ -32,6 +32,8 @@
 
 #include <linux/kdev_t.h>
 
+#define LOG_TAG "Vold::Fat"
+
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <selinux/selinux.h>
diff --git a/main.cpp b/main.cpp
index ebe5510..3515129 100644
--- a/main.cpp
+++ b/main.cpp
@@ -56,13 +56,14 @@
     setenv("ANDROID_LOG_TAGS", "*:d", 1);  // Do not submit with verbose logs enabled
     android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
 
-    LOG(INFO) << "Vold 3.0 (the awakening) firing up";
+    LOG(INFO) << "Vold 3.0-tuxera (the awakening) firing up";
 
     ATRACE_BEGIN("main");
 
     LOG(DEBUG) << "Detected support for:"
                << (android::vold::IsFilesystemSupported("ext4") ? " ext4" : "")
                << (android::vold::IsFilesystemSupported("f2fs") ? " f2fs" : "")
+               << (android::vold::IsFilesystemSupported("texfat") ? " texfat" : "")
                << (android::vold::IsFilesystemSupported("vfat") ? " vfat" : "");
 
     VolumeManager* vm;
diff --git a/model/Disk.cpp b/model/Disk.cpp
index 98b9596..0ca894b 100644
--- a/model/Disk.cpp
+++ b/model/Disk.cpp
@@ -23,6 +23,8 @@
 #include "VolumeEncryption.h"
 #include "VolumeManager.h"
 
+#include "fs/Exfat.h"
+
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
@@ -384,6 +386,8 @@
                 }
 
                 switch (type) {
+                    case 0x01:  // FAT12 (in first 32 MB of disk)
+                    case 0x04:  // FAT16 (in first 32 MB of disk)
                     case 0x06:  // FAT16
                     case 0x07:  // HPFS/NTFS/exFAT
                     case 0x0b:  // W95 FAT32 (LBA)
@@ -458,6 +462,12 @@
         LOG(WARNING) << "Failed to zap; status " << res;
     }
 
+    if (exfat::CheckSize(mDevPath, false) == 1) {
+        /* Should format filesystem as exFAT, so we must partition the device
+         * according to SDXC rules. */
+        return exfat::PartitionDisk(mDevPath);
+    }
+
     // Now let's build the new MBR table. We heavily rely on sgdisk to
     // force optimal alignment on the created partitions.
     cmd.clear();
diff --git a/model/PublicVolume.cpp b/model/PublicVolume.cpp
index d40e3e3..ed87e3b 100644
--- a/model/PublicVolume.cpp
+++ b/model/PublicVolume.cpp
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2010-2021 Tuxera Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +21,7 @@
 #include "Utils.h"
 #include "VolumeManager.h"
 #include "fs/Exfat.h"
+#include "fs/Filesystems.h"
 #include "fs/Vfat.h"
 
 #include <android-base/logging.h>
@@ -57,6 +59,13 @@
 PublicVolume::~PublicVolume() {}
 
 status_t PublicVolume::readMetadata() {
+    if(filesystems::Detect(mDevPath, &mFsTypeId)) {
+        /* Error while detecting filesystem. This should not happen, but in case
+         * it does, pretend that it is recognized as FAT since this most
+         * accurately reflects the behaviour of a stock AOSP vold. */
+        mFsTypeId = FSTYPE_FAT;
+    }
+
     status_t res = ReadMetadataUntrusted(mDevPath, &mFsType, &mFsUuid, &mFsLabel);
 
     auto listener = getListener();
@@ -100,21 +109,16 @@
     bool isVisible = getMountFlags() & MountFlags::kVisible;
     readMetadata();
 
-    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 {
+    if (!filesystems::IsSupported(mFsTypeId)) {
         LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;
         return -EIO;
     }
 
+    if (filesystems::Check(mFsTypeId, mDevPath)) {
+        LOG(ERROR) << getId() << " failed filesystem check";
+        return -EIO;
+    }
+
     // Use UUID as stable name, if available
     std::string stableName = getId();
     if (!mFsUuid.empty()) {
@@ -140,18 +144,11 @@
         return -errno;
     }
 
-    if (mFsType == "vfat") {
-        if (vfat::Mount(mDevPath, mRawPath, false, false, false, AID_ROOT,
-                        (isVisible ? AID_MEDIA_RW : AID_EXTERNAL_STORAGE), 0007, true)) {
-            PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
-            return -EIO;
-        }
-    } else if (mFsType == "exfat") {
-        if (exfat::Mount(mDevPath, mRawPath, AID_ROOT,
-                         (isVisible ? AID_MEDIA_RW : AID_EXTERNAL_STORAGE), 0007)) {
-            PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
-            return -EIO;
-        }
+    if (filesystems::Mount(mFsTypeId, mDevPath, mRawPath, false, false, false,
+                           AID_ROOT, (isVisible ? AID_MEDIA_RW : AID_EXTERNAL_STORAGE),
+                           0007, true)) {
+        PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
+        return -EIO;
     }
 
     if (getMountFlags() & MountFlags::kPrimary) {
@@ -311,33 +308,26 @@
 }
 
 status_t PublicVolume::doFormat(const std::string& fsType) {
-    bool useVfat = vfat::IsSupported();
-    bool useExfat = exfat::IsSupported();
     status_t res = OK;
 
+    status_t (*formatFunction)(const std::string&, unsigned long) = NULL;
+
     // Resolve the target filesystem type
-    if (fsType == "auto" && useVfat && useExfat) {
-        uint64_t size = 0;
-
-        res = GetBlockDevSize(mDevPath, &size);
-        if (res != OK) {
-            LOG(ERROR) << "Couldn't get device size " << mDevPath;
-            return res;
-        }
-
-        // If both vfat & exfat are supported use exfat for SDXC (>~32GiB) cards
-        if (size > 32896LL * 1024 * 1024) {
-            useVfat = false;
-        } else {
-            useExfat = false;
+    if (fsType == "auto") {
+        if (exfat::IsSupported() && exfat::CheckSize(mDevPath, true) == 1) {
+            /* Should format filesystem as exFAT, so we must format the
+             * partition according to SDXC rules. */
+            formatFunction = &exfat::Format;
+        } else if(vfat::IsSupported()) {
+            formatFunction = &vfat::Format;
         }
     } else if (fsType == "vfat") {
-        useExfat = false;
+        formatFunction = vfat::IsSupported() ? &vfat::Format : NULL;
     } else if (fsType == "exfat") {
-        useVfat = false;
+        formatFunction = exfat::IsSupported() ? &exfat::Format : NULL;
     }
 
-    if (!useVfat && !useExfat) {
+    if (!formatFunction) {
         LOG(ERROR) << "Unsupported filesystem " << fsType;
         return -EINVAL;
     }
@@ -346,11 +336,7 @@
         LOG(WARNING) << getId() << " failed to wipe";
     }
 
-    if (useVfat) {
-        res = vfat::Format(mDevPath, 0);
-    } else if (useExfat) {
-        res = exfat::Format(mDevPath);
-    }
+    res = formatFunction(mDevPath, 0);
 
     if (res != OK) {
         LOG(ERROR) << getId() << " failed to format";
diff --git a/model/PublicVolume.h b/model/PublicVolume.h
index 3156b53..0f4a795 100644
--- a/model/PublicVolume.h
+++ b/model/PublicVolume.h
@@ -18,6 +18,7 @@
 #define ANDROID_VOLD_PUBLIC_VOLUME_H
 
 #include "VolumeBase.h"
+#include "fs/Filesystems.h"
 
 #include <cutils/multiuser.h>
 
@@ -71,6 +72,8 @@
     /* Whether to use sdcardfs for this volume */
     bool mUseSdcardFs;
 
+    /* Type ID of filesystem (see Filesystems.h). */
+    FSType mFsTypeId = FSTYPE_UNRECOGNIZED;
     /* Filesystem type */
     std::string mFsType;
     /* Filesystem UUID */