Merge tag 'LA.UM.5.8.r1-01600-8x98.0' into n-mr1

"LA.UM.5.8.r1-01600-8x98.0"
diff --git a/Android.mk b/Android.mk
index cabf9b3..32dc9c0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -3,22 +3,23 @@
 common_src_files := \
 	VolumeManager.cpp \
 	CommandListener.cpp \
-	CryptCommandListener.cpp \
 	VoldCommand.cpp \
 	NetlinkManager.cpp \
 	NetlinkHandler.cpp \
 	Process.cpp \
+	fs/Exfat.cpp \
 	fs/Ext4.cpp \
 	fs/F2fs.cpp \
+	fs/Ntfs.cpp \
 	fs/Vfat.cpp \
 	Loop.cpp \
 	Devmapper.cpp \
 	ResponseCode.cpp \
 	CheckBattery.cpp \
-	Ext4Crypt.cpp \
 	VoldUtil.c \
 	cryptfs.c \
 	Disk.cpp \
+	DiskPartition.cpp \
 	VolumeBase.cpp \
 	PublicVolume.cpp \
 	PrivateVolume.cpp \
@@ -27,10 +28,15 @@
 	MoveTask.cpp \
 	Benchmark.cpp \
 	TrimTask.cpp \
+	secontext.cpp \
+	main.cpp
+
+crypto_src_files := \
+	CryptCommandListener.cpp \
+	Ext4Crypt.cpp \
 	Keymaster.cpp \
 	KeyStorage.cpp \
-	ScryptParameters.cpp \
-	secontext.cpp \
+	ScryptParameters.cpp
 
 common_c_includes := \
 	system/extras/ext4_utils \
@@ -39,45 +45,63 @@
 	frameworks/native/include \
 	system/security/keystore \
 	hardware/libhardware/include/hardware \
-	system/security/softkeymaster/include/keymaster
+	system/security/softkeymaster/include/keymaster \
+	external/e2fsprogs/lib
 
-common_shared_libraries := \
+common_libraries := \
 	libsysutils \
 	libbinder \
 	libcutils \
 	liblog \
 	libdiskconfig \
-	libhardware_legacy \
 	liblogwrap \
-	libext4_utils \
 	libf2fs_sparseblock \
-	libcrypto \
 	libselinux \
-	libutils \
+	libutils
+
+common_shared_libraries := \
+	$(common_libraries) \
+	libhardware_legacy \
+	libext4_utils \
+	libcrypto \
 	libhardware \
 	libsoftkeymaster \
 	libbase \
 	libkeymaster_messages \
+	libext2_blkid
 
 common_static_libraries := \
 	libbootloader_message \
 	libfs_mgr \
 	libfec \
 	libfec_rs \
+	libext4_utils_static \
+	libsparse_static \
 	libsquashfs_utils \
 	libscrypt_static \
 	libmincrypt \
 	libbatteryservice \
+	libext2_blkid \
+	libext2_uuid_static \
+	libz
 
 vold_conlyflags := -std=c11
 vold_cflags := -Werror -Wall -Wno-missing-field-initializers -Wno-unused-variable -Wno-unused-parameter
 
+ifeq ($(TARGET_KERNEL_HAVE_EXFAT),true)
+vold_cflags += -DCONFIG_KERNEL_HAVE_EXFAT
+endif
+
+ifeq ($(TARGET_KERNEL_HAVE_NTFS),true)
+vold_cflags += -DCONFIG_KERNEL_HAVE_NTFS
+endif
+
 include $(CLEAR_VARS)
 
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 LOCAL_MODULE := libvold
 LOCAL_CLANG := true
-LOCAL_SRC_FILES := $(common_src_files)
+LOCAL_SRC_FILES := $(common_src_files) $(crypto_src_files)
 LOCAL_C_INCLUDES := $(common_c_includes)
 LOCAL_SHARED_LIBRARIES := $(common_shared_libraries)
 LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
@@ -85,6 +109,12 @@
 LOCAL_CFLAGS := $(vold_cflags)
 LOCAL_CONLYFLAGS := $(vold_conlyflags)
 
+ifeq ($(TARGET_HW_DISK_ENCRYPTION),true)
+TARGET_CRYPTFS_HW_PATH ?= vendor/qcom/opensource/cryptfs_hw
+LOCAL_C_INCLUDES += $(TARGET_CRYPTFS_HW_PATH)
+LOCAL_CFLAGS += -DCONFIG_HW_DISK_ENCRYPTION
+endif
+
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -93,8 +123,7 @@
 LOCAL_MODULE := vold
 LOCAL_CLANG := true
 LOCAL_SRC_FILES := \
-	main.cpp \
-	$(common_src_files)
+	vold.c
 
 LOCAL_INIT_RC := vold.rc
 
@@ -102,14 +131,12 @@
 LOCAL_CFLAGS := $(vold_cflags)
 LOCAL_CONLYFLAGS := $(vold_conlyflags)
 
-ifeq ($(TARGET_HW_DISK_ENCRYPTION),true)
-LOCAL_C_INCLUDES += $(TARGET_CRYPTFS_HW_PATH)
-common_shared_libraries += libcryptfs_hw
-LOCAL_CFLAGS += -DCONFIG_HW_DISK_ENCRYPTION
-endif
-
 LOCAL_SHARED_LIBRARIES := $(common_shared_libraries)
-LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
+LOCAL_STATIC_LIBRARIES := libvold $(common_static_libraries)
+
+ifeq ($(TARGET_HW_DISK_ENCRYPTION),true)
+LOCAL_SHARED_LIBRARIES += libcryptfs_hw
+endif
 
 include $(BUILD_EXECUTABLE)
 
@@ -137,3 +164,16 @@
 LOCAL_CONLYFLAGS := $(vold_conlyflags)
 
 include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_MODULE := libminivold_static
+LOCAL_CLANG := true
+LOCAL_SRC_FILES := $(common_src_files)
+LOCAL_C_INCLUDES := $(common_c_includes) system/core/fs_mgr/include system/core/logwrapper/include
+LOCAL_SHARED_LIBRARIES := $(common_shared_libraries)
+LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
+LOCAL_MODULE_TAGS := eng tests
+LOCAL_CFLAGS := $(vold_cflags) -DMINIVOLD
+LOCAL_CONLYFLAGS := $(vold_conlyflags)
+include $(BUILD_STATIC_LIBRARY)
diff --git a/CommandListener.cpp b/CommandListener.cpp
index b548a91..56c0946 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -226,14 +226,15 @@
         return sendGenericOkFail(cli, res);
 
     } else if (cmd == "unmount" && argc > 2) {
-        // unmount [volId]
+        // unmount [volId] [detach]
         std::string id(argv[2]);
         auto vol = vm->findVolume(id);
         if (vol == nullptr) {
             return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
         }
+        bool detach = (argc > 3 && !strcmp(argv[3], "detach"));
 
-        return sendGenericOkFail(cli, vol->unmount());
+        return sendGenericOkFail(cli, vol->unmount(detach));
 
     } else if (cmd == "format" && argc > 3) {
         // format [volId] [fsType|auto]
diff --git a/Disk.cpp b/Disk.cpp
index 1e1a63e..5ad375b 100644
--- a/Disk.cpp
+++ b/Disk.cpp
@@ -44,7 +44,11 @@
 namespace android {
 namespace vold {
 
+#ifdef MINIVOLD
+static const char* kSgdiskPath = "/sbin/sgdisk";
+#else
 static const char* kSgdiskPath = "/system/bin/sgdisk";
+#endif
 static const char* kSgdiskToken = " \t\n";
 
 static const char* kSysfsMmcMaxMinors = "/sys/module/mmcblk/parameters/perdev_minors";
@@ -109,8 +113,8 @@
 Disk::Disk(const std::string& eventPath, dev_t device,
         const std::string& nickname, int flags) :
         mDevice(device), mSize(-1), mNickname(nickname), mFlags(flags), mCreated(
-                false), mJustPartitioned(false) {
-    mId = StringPrintf("disk:%u,%u", major(device), minor(device));
+                false), mJustPartitioned(false), mSkipChange(false) {
+    mId = StringPrintf("disk:%u_%u", major(device), minor(device));
     mEventPath = eventPath;
     mSysPath = StringPrintf("/sys/%s", eventPath.c_str());
     mDevPath = StringPrintf("/dev/block/vold/%s", mId.c_str());
@@ -161,8 +165,10 @@
     return OK;
 }
 
-void Disk::createPublicVolume(dev_t device) {
-    auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(device));
+void Disk::createPublicVolume(dev_t device,
+                const std::string& fstype /* = "" */,
+                const std::string& mntopts /* = "" */) {
+    auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(device, mNickname, fstype, mntopts));
     if (mJustPartitioned) {
         LOG(DEBUG) << "Device just partitioned; silently formatting";
         vol->setSilent(true);
@@ -216,6 +222,11 @@
 }
 
 status_t Disk::readMetadata() {
+
+    if (mSkipChange) {
+        return OK;
+    }
+
     mSize = -1;
     mLabel.clear();
 
@@ -285,6 +296,12 @@
         return -ENOTSUP;
     }
 
+    if (mSkipChange) {
+        mSkipChange = false;
+        LOG(INFO) << "Skip first change";
+        return OK;
+    }
+
     destroyAllVolumes();
 
     // Parse partition table
@@ -332,9 +349,11 @@
 
                 switch (strtol(type, nullptr, 16)) {
                 case 0x06: // FAT16
+                case 0x07: // NTFS/exFAT
                 case 0x0b: // W95 FAT32 (LBA)
                 case 0x0c: // W95 FAT32 (LBA)
                 case 0x0e: // W95 FAT16 (LBA)
+                case 0x83: // Linux EXT4/F2FS/...
                     createPublicVolume(partDevice);
                     break;
                 }
@@ -383,9 +402,46 @@
     destroyAllVolumes();
     mJustPartitioned = true;
 
-    // First nuke any existing partition table
+    // Determine if we're coming from MBR
     std::vector<std::string> cmd;
     cmd.push_back(kSgdiskPath);
+    cmd.push_back("--android-dump");
+    cmd.push_back(mDevPath);
+
+    std::vector<std::string> output;
+    res = ForkExecvp(cmd, output);
+    if (res != OK) {
+        LOG(WARNING) << "sgdisk failed to scan " << mDevPath;
+        mJustPartitioned = false;
+        return res;
+    }
+
+    Table table = Table::kUnknown;
+    for (auto line : output) {
+        char* cline = (char*) line.c_str();
+        char* token = strtok(cline, kSgdiskToken);
+        if (token == nullptr) continue;
+
+        if (!strcmp(token, "DISK")) {
+            const char* type = strtok(nullptr, kSgdiskToken);
+            if (!strcmp(type, "mbr")) {
+                table = Table::kMbr;
+                break;
+            } else if (!strcmp(type, "gpt")) {
+                table = Table::kGpt;
+                break;
+            }
+        }
+    }
+
+    if (table == Table::kMbr) {
+        LOG(INFO) << "skip first disk change event due to MBR -> GPT switch";
+        mSkipChange = true;
+    }
+
+    // First nuke any existing partition table
+    cmd.clear();
+    cmd.push_back(kSgdiskPath);
     cmd.push_back("--zap-all");
     cmd.push_back(mDevPath);
 
diff --git a/Disk.h b/Disk.h
index 77ec7df..20f5522 100644
--- a/Disk.h
+++ b/Disk.h
@@ -52,6 +52,8 @@
         kUsb = 1 << 3,
         /* Flag that disk is EMMC internal */
         kEmmc = 1 << 4,
+        /* Flag that disk is non-removable */
+        kNonRemovable = 1 << 5,
     };
 
     const std::string& getId() { return mId; }
@@ -67,22 +69,22 @@
 
     void listVolumes(VolumeBase::Type type, std::list<std::string>& list);
 
-    status_t create();
-    status_t destroy();
+    virtual status_t create();
+    virtual status_t destroy();
 
-    status_t readMetadata();
-    status_t readPartitions();
+    virtual status_t readMetadata();
+    virtual status_t readPartitions();
 
     status_t unmountAll();
 
-    status_t partitionPublic();
-    status_t partitionPrivate();
-    status_t partitionMixed(int8_t ratio);
+    virtual status_t partitionPublic();
+    virtual status_t partitionPrivate();
+    virtual status_t partitionMixed(int8_t ratio);
 
     void notifyEvent(int msg);
     void notifyEvent(int msg, const std::string& value);
 
-private:
+protected:
     /* ID that uniquely references this disk */
     std::string mId;
     /* Original event path */
@@ -107,8 +109,12 @@
     bool mCreated;
     /* Flag that we just partitioned and should format all volumes */
     bool mJustPartitioned;
+    /* Flag that we need to skip first disk change events after partitioning*/
+    bool mSkipChange;
 
-    void createPublicVolume(dev_t device);
+    void createPublicVolume(dev_t device,
+                    const std::string& fstype = "",
+                    const std::string& mntopts = "");
     void createPrivateVolume(dev_t device, const std::string& partGuid);
 
     void destroyAllVolumes();
diff --git a/DiskPartition.cpp b/DiskPartition.cpp
new file mode 100644
index 0000000..13b084b
--- /dev/null
+++ b/DiskPartition.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 Cyanogen, 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 "DiskPartition.h"
+#include "PublicVolume.h"
+#include "PrivateVolume.h"
+#include "Utils.h"
+#include "VolumeBase.h"
+#include "VolumeManager.h"
+#include "ResponseCode.h"
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/logging.h>
+#include <diskconfig/diskconfig.h>
+
+#include <vector>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+using android::base::ReadFileToString;
+using android::base::WriteStringToFile;
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+
+DiskPartition::DiskPartition(const std::string& eventPath, dev_t device,
+            const std::string& nickname, int flags, int partnum,
+            const std::string& fstype /* = "" */, const std::string& mntopts /* = "" */) :
+        Disk(eventPath, device, nickname, flags),
+        mPartNum(partnum),
+        mFsType(fstype),
+        mMntOpts(mntopts) {
+    // Empty
+}
+
+DiskPartition::~DiskPartition() {
+    // Empty
+}
+
+status_t DiskPartition::create() {
+    CHECK(!mCreated);
+    mCreated = true;
+    notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags));
+    dev_t partDevice = makedev(major(mDevice), minor(mDevice) + mPartNum);
+    createPublicVolume(partDevice, mFsType, mMntOpts);
+    return OK;
+}
+
+status_t DiskPartition::destroy() {
+    CHECK(mCreated);
+    destroyAllVolumes();
+    mCreated = false;
+    notifyEvent(ResponseCode::DiskDestroyed);
+    return OK;
+}
+
+status_t DiskPartition::partitionPublic() {
+    return -1;
+}
+
+status_t DiskPartition::partitionPrivate() {
+    return -1;
+}
+
+status_t DiskPartition::partitionMixed(int8_t ratio) {
+    return -1;
+}
+
+}  // namespace vold
+}  // namespace android
diff --git a/DiskPartition.h b/DiskPartition.h
new file mode 100644
index 0000000..b1fa33b
--- /dev/null
+++ b/DiskPartition.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 Cyanogen, 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_DISKPARTITION_H
+#define ANDROID_VOLD_DISKPARTITION_H
+
+#include "Disk.h"
+
+namespace android {
+namespace vold {
+
+/*
+ * Representation of a single partition on physical media.  Useful for
+ * single media partitions such as "internal" sdcard partitions.
+ */
+
+class DiskPartition : public Disk {
+public:
+    DiskPartition(const std::string& eventPath, dev_t device,
+            const std::string& nickname,
+            int flags, int partnum,
+            const std::string& fstype = "", const std::string& mntopts = "");
+    virtual ~DiskPartition();
+
+    virtual status_t create();
+    virtual status_t destroy();
+
+    virtual status_t partitionPublic();
+    virtual status_t partitionPrivate();
+    virtual status_t partitionMixed(int8_t ratio);
+
+private:
+    /* Partition number */
+    int mPartNum;
+    /* Filesystem type */
+    std::string mFsType;
+    /* Mount options */
+    std::string mMntOpts;
+};
+
+}  // namespace vold
+}  // namespace android
+
+#endif
diff --git a/EmulatedVolume.cpp b/EmulatedVolume.cpp
index 581c322..1d1c12e 100644
--- a/EmulatedVolume.cpp
+++ b/EmulatedVolume.cpp
@@ -16,6 +16,7 @@
 
 #include "EmulatedVolume.h"
 #include "Utils.h"
+#include "ResponseCode.h"
 
 #include <android-base/stringprintf.h>
 #include <android-base/logging.h>
@@ -34,7 +35,11 @@
 namespace android {
 namespace vold {
 
+#ifdef MINIVOLD
+static const char* kFusePath = "/sbin/sdcard";
+#else
 static const char* kFusePath = "/system/bin/sdcard";
+#endif
 
 EmulatedVolume::EmulatedVolume(const std::string& rawPath) :
         VolumeBase(Type::kEmulated), mFusePid(0) {
@@ -45,7 +50,7 @@
 
 EmulatedVolume::EmulatedVolume(const std::string& rawPath, dev_t device,
         const std::string& fsUuid) : VolumeBase(Type::kEmulated), mFusePid(0) {
-    setId(StringPrintf("emulated:%u,%u", major(device), minor(device)));
+    setId(StringPrintf("emulated:%u_%u", major(device), minor(device)));
     mRawPath = rawPath;
     mLabel = fsUuid;
 }
@@ -53,6 +58,13 @@
 EmulatedVolume::~EmulatedVolume() {
 }
 
+status_t EmulatedVolume::doCreate() {
+    if (mLabel.size() > 0) {
+        notifyEvent(ResponseCode::VolumeFsLabelChanged, mLabel);
+    }
+    return OK;
+}
+
 status_t EmulatedVolume::doMount() {
     // We could have migrated storage to an adopted private volume, so always
     // call primary storage "emulated" to avoid media rescans.
@@ -106,7 +118,7 @@
     return OK;
 }
 
-status_t EmulatedVolume::doUnmount() {
+status_t EmulatedVolume::doUnmount(bool detach /* = false */) {
     // Unmount the storage before we kill the FUSE process. If we kill
     // the FUSE process first, most file system operations will return
     // ENOTCONN until the unmount completes. This is an exotic and unusual
diff --git a/EmulatedVolume.h b/EmulatedVolume.h
index 09686c1..ea397fa 100644
--- a/EmulatedVolume.h
+++ b/EmulatedVolume.h
@@ -42,8 +42,9 @@
     virtual ~EmulatedVolume();
 
 protected:
+    status_t doCreate() override;
     status_t doMount() override;
-    status_t doUnmount() override;
+    status_t doUnmount(bool detach = false) override;
 
 private:
     std::string mRawPath;
diff --git a/PrivateVolume.cpp b/PrivateVolume.cpp
index e5809fb..c83a6ed 100644
--- a/PrivateVolume.cpp
+++ b/PrivateVolume.cpp
@@ -45,7 +45,7 @@
 
 PrivateVolume::PrivateVolume(dev_t device, const std::string& keyRaw) :
         VolumeBase(Type::kPrivate), mRawDevice(device), mKeyRaw(keyRaw) {
-    setId(StringPrintf("private:%u,%u", major(device), minor(device)));
+    setId(StringPrintf("private:%u_%u", major(device), minor(device)));
     mRawDevPath = StringPrintf("/dev/block/vold/%s", getId().c_str());
 }
 
@@ -105,7 +105,7 @@
     }
 
     if (mFsType == "ext4") {
-        int res = ext4::Check(mDmDevPath, mPath);
+        int res = ext4::Check(mDmDevPath, mPath, true);
         if (res == 0 || res == 1) {
             LOG(DEBUG) << getId() << " passed filesystem check";
         } else {
@@ -113,13 +113,13 @@
             return -EIO;
         }
 
-        if (ext4::Mount(mDmDevPath, mPath, false, false, true)) {
+        if (ext4::Mount(mDmDevPath, mPath, false, false, true, "", true)) {
             PLOG(ERROR) << getId() << " failed to mount";
             return -EIO;
         }
 
     } else if (mFsType == "f2fs") {
-        int res = f2fs::Check(mDmDevPath);
+        int res = f2fs::Check(mDmDevPath, true);
         if (res == 0) {
             LOG(DEBUG) << getId() << " passed filesystem check";
         } else {
@@ -127,7 +127,7 @@
             return -EIO;
         }
 
-        if (f2fs::Mount(mDmDevPath, mPath)) {
+        if (f2fs::Mount(mDmDevPath, mPath, "", true)) {
             PLOG(ERROR) << getId() << " failed to mount";
             return -EIO;
         }
@@ -162,7 +162,7 @@
     return OK;
 }
 
-status_t PrivateVolume::doUnmount() {
+status_t PrivateVolume::doUnmount(bool detach /* = false */) {
     ForceUnmount(mPath);
 
     if (TEMP_FAILURE_RETRY(rmdir(mPath.c_str()))) {
diff --git a/PrivateVolume.h b/PrivateVolume.h
index 95b718d..3a92aba 100644
--- a/PrivateVolume.h
+++ b/PrivateVolume.h
@@ -44,7 +44,7 @@
     status_t doCreate() override;
     status_t doDestroy() override;
     status_t doMount() override;
-    status_t doUnmount() override;
+    status_t doUnmount(bool detach = false) override;
     status_t doFormat(const std::string& fsType) override;
 
     status_t readMetadata();
diff --git a/PublicVolume.cpp b/PublicVolume.cpp
index 893f928..96698ab 100644
--- a/PublicVolume.cpp
+++ b/PublicVolume.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#include "fs/Exfat.h"
+#include "fs/Ext4.h"
+#include "fs/F2fs.h"
+#include "fs/Ntfs.h"
 #include "fs/Vfat.h"
 #include "PublicVolume.h"
 #include "Utils.h"
@@ -37,13 +41,20 @@
 namespace android {
 namespace vold {
 
+#ifdef MINIVOLD
+static const char* kFusePath = "/sbin/sdcard";
+#else
 static const char* kFusePath = "/system/bin/sdcard";
+#endif
 
 static const char* kAsecPath = "/mnt/secure/asec";
 
-PublicVolume::PublicVolume(dev_t device) :
-        VolumeBase(Type::kPublic), mDevice(device), mFusePid(0) {
-    setId(StringPrintf("public:%u,%u", major(device), minor(device)));
+PublicVolume::PublicVolume(dev_t device, const std::string& nickname,
+                const std::string& fstype /* = "" */,
+                const std::string& mntopts /* = "" */) :
+        VolumeBase(Type::kPublic), mDevice(device), mFusePid(0),
+        mFsType(fstype), mFsLabel(nickname), mMntOpts(mntopts) {
+    setId(StringPrintf("public:%u_%u", major(device), minor(device)));
     mDevPath = StringPrintf("/dev/block/vold/%s", getId().c_str());
 }
 
@@ -83,6 +94,9 @@
 }
 
 status_t PublicVolume::doCreate() {
+    if (mFsLabel.size() > 0) {
+        notifyEvent(ResponseCode::VolumeFsLabelChanged, mFsLabel);
+    }
     return CreateDeviceNode(mDevPath, mDevice);
 }
 
@@ -94,27 +108,29 @@
     // TODO: expand to support mounting other filesystems
     readMetadata();
 
-    if (mFsType != "vfat") {
+    if (!IsFilesystemSupported(mFsType)) {
         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()) {
         stableName = mFsUuid;
     }
 
+#ifdef MINIVOLD
+    // In recovery, directly mount to /storage/* since we have no fuse daemon
+    mRawPath = StringPrintf("/storage/%s", stableName.c_str());
+    mFuseDefault = StringPrintf("/storage/%s", stableName.c_str());
+    mFuseRead = StringPrintf("/storage/%s", stableName.c_str());
+    mFuseWrite = StringPrintf("/storage/%s", stableName.c_str());
+#else
     mRawPath = StringPrintf("/mnt/media_rw/%s", stableName.c_str());
-
     mFuseDefault = StringPrintf("/mnt/runtime/default/%s", stableName.c_str());
     mFuseRead = StringPrintf("/mnt/runtime/read/%s", stableName.c_str());
     mFuseWrite = StringPrintf("/mnt/runtime/write/%s", stableName.c_str());
+#endif
 
     setInternalPath(mRawPath);
     if (getMountFlags() & MountFlags::kVisible) {
@@ -128,12 +144,52 @@
         return -errno;
     }
 
-    if (vfat::Mount(mDevPath, mRawPath, false, false, false,
-            AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) {
+    int ret = 0;
+    if (mFsType == "exfat") {
+        ret = exfat::Check(mDevPath);
+    } else if (mFsType == "ext4") {
+        ret = ext4::Check(mDevPath, mRawPath, false);
+    } else if (mFsType == "f2fs") {
+        ret = f2fs::Check(mDevPath, false);
+    } else if (mFsType == "ntfs") {
+        ret = ntfs::Check(mDevPath);
+    } else if (mFsType == "vfat") {
+        ret = vfat::Check(mDevPath);
+    } else {
+        LOG(WARNING) << getId() << " unsupported filesystem check, skipping";
+    }
+    if (ret) {
+        LOG(ERROR) << getId() << " failed filesystem check";
+        return -EIO;
+    }
+
+    if (mFsType == "exfat") {
+        ret = exfat::Mount(mDevPath, mRawPath, false, false, false,
+                AID_MEDIA_RW, AID_MEDIA_RW, 0007);
+    } else if (mFsType == "ext4") {
+        ret = ext4::Mount(mDevPath, mRawPath, false, false, true, mMntOpts,
+                false, true);
+    } else if (mFsType == "f2fs") {
+        ret = f2fs::Mount(mDevPath, mRawPath, mMntOpts, false, true);
+    } else if (mFsType == "ntfs") {
+        ret = ntfs::Mount(mDevPath, mRawPath, false, false, false,
+                AID_MEDIA_RW, AID_MEDIA_RW, 0007, true);
+    } else if (mFsType == "vfat") {
+        ret = vfat::Mount(mDevPath, mRawPath, false, false, false,
+                AID_MEDIA_RW, AID_MEDIA_RW, 0007, true);
+    } else {
+        ret = ::mount(mDevPath.c_str(), mRawPath.c_str(), mFsType.c_str(), 0, NULL);
+    }
+    if (ret) {
         PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
         return -EIO;
     }
 
+#ifdef MINIVOLD
+    // In recovery, don't setup ASEC or FUSE
+    return OK;
+#endif
+
     if (getMountFlags() & MountFlags::kPrimary) {
         initAsecStage();
     }
@@ -193,19 +249,22 @@
     return OK;
 }
 
-status_t PublicVolume::doUnmount() {
+status_t PublicVolume::doUnmount(bool detach /* = false */) {
     // Unmount the storage before we kill the FUSE process. If we kill
     // the FUSE process first, most file system operations will return
     // ENOTCONN until the unmount completes. This is an exotic and unusual
     // error code and might cause broken behaviour in applications.
     KillProcessesUsingPath(getPath());
 
+#ifndef MINIVOLD
     ForceUnmount(kAsecPath);
 
     ForceUnmount(mFuseDefault);
     ForceUnmount(mFuseRead);
     ForceUnmount(mFuseWrite);
-    ForceUnmount(mRawPath);
+#endif
+
+    ForceUnmount(mRawPath, detach);
 
     if (mFusePid > 0) {
         kill(mFusePid, SIGTERM);
@@ -227,19 +286,41 @@
 }
 
 status_t PublicVolume::doFormat(const std::string& fsType) {
-    if (fsType == "vfat" || fsType == "auto") {
-        if (WipeBlockDevice(mDevPath) != OK) {
-            LOG(WARNING) << getId() << " failed to wipe";
-        }
-        if (vfat::Format(mDevPath, 0)) {
-            LOG(ERROR) << getId() << " failed to format";
-            return -errno;
-        }
-    } else {
+    // "auto" is used for newly partitioned disks (see Disk::partition*)
+    // and thus is restricted to external/removable storage.
+    if (!(IsFilesystemSupported(fsType) || fsType == "auto")) {
         LOG(ERROR) << "Unsupported filesystem " << fsType;
         return -EINVAL;
     }
 
+    if (WipeBlockDevice(mDevPath) != OK) {
+        LOG(WARNING) << getId() << " failed to wipe";
+    }
+
+    int ret = 0;
+    if (fsType == "auto") {
+        ret = vfat::Format(mDevPath, 0);
+    } else if (fsType == "exfat") {
+        ret = exfat::Format(mDevPath);
+    } else if (fsType == "ext4") {
+        ret = ext4::Format(mDevPath, 0, mRawPath);
+    } else if (fsType == "f2fs") {
+        ret = f2fs::Format(mDevPath);
+    } else if (fsType == "ntfs") {
+        ret = ntfs::Format(mDevPath, 0);
+    } else if (fsType == "vfat") {
+        ret = vfat::Format(mDevPath, 0);
+    } else {
+        LOG(ERROR) << getId() << " unrecognized filesystem " << fsType;
+        ret = -1;
+        errno = EIO;
+    }
+
+    if (ret) {
+        LOG(ERROR) << getId() << " failed to format";
+        return -errno;
+    }
+
     return OK;
 }
 
diff --git a/PublicVolume.h b/PublicVolume.h
index 3aa7a73..e9e2926 100644
--- a/PublicVolume.h
+++ b/PublicVolume.h
@@ -39,14 +39,15 @@
  */
 class PublicVolume : public VolumeBase {
 public:
-    explicit PublicVolume(dev_t device);
+    PublicVolume(dev_t device, const std::string& nickname,
+                    const std::string& mntopts = "", const std::string& fstype = "");
     virtual ~PublicVolume();
 
 protected:
     status_t doCreate() override;
     status_t doDestroy() override;
     status_t doMount() override;
-    status_t doUnmount() override;
+    status_t doUnmount(bool detach = false) override;
     status_t doFormat(const std::string& fsType) override;
 
     status_t readMetadata();
@@ -73,6 +74,8 @@
     std::string mFsUuid;
     /* User-visible filesystem label */
     std::string mFsLabel;
+    /* Mount options */
+    std::string mMntOpts;
 
     DISALLOW_COPY_AND_ASSIGN(PublicVolume);
 };
diff --git a/Utils.cpp b/Utils.cpp
index 014055b..cc66101 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -21,6 +21,9 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include "fs/Exfat.h"
+#include "fs/Ntfs.h"
+
 #include <cutils/fs.h>
 #include <cutils/properties.h>
 #include <private/android_filesystem_config.h>
@@ -52,7 +55,11 @@
 security_context_t sFsckContext = nullptr;
 security_context_t sFsckUntrustedContext = nullptr;
 
+#ifdef MINIVOLD
+#include <blkid/blkid.h>
+#else
 static const char* kBlkidPath = "/system/bin/blkid";
+#endif
 static const char* kKeyPath = "/data/misc/vold";
 
 static const char* kProcFilesystems = "/proc/filesystems";
@@ -118,8 +125,15 @@
     }
 }
 
-status_t ForceUnmount(const std::string& path) {
+status_t ForceUnmount(const std::string& path, bool detach /* = false */) {
     const char* cpath = path.c_str();
+    if (detach) {
+        if (!umount2(path.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) || errno == EINVAL || errno == ENOENT) {
+            return OK;
+        }
+        PLOG(WARNING) << "Failed to unmount " << path;
+        return -errno;
+    }
     if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
         return OK;
     }
@@ -184,10 +198,21 @@
 
 static status_t readMetadata(const std::string& path, std::string& fsType,
         std::string& fsUuid, std::string& fsLabel, bool untrusted) {
-    fsType.clear();
-    fsUuid.clear();
-    fsLabel.clear();
-
+#ifdef MINIVOLD
+    char *val = NULL;
+    val = blkid_get_tag_value(NULL, "TYPE", path.c_str());
+    if (val) {
+        fsType = val;
+    }
+    val = blkid_get_tag_value(NULL, "UUID", path.c_str());
+    if (val) {
+        fsUuid = val;
+    }
+    val = blkid_get_tag_value(NULL, "LABEL", path.c_str());
+    if (val) {
+        fsUuid = val;
+    }
+#else
     std::vector<std::string> cmd;
     cmd.push_back(kBlkidPath);
     cmd.push_back("-c");
@@ -226,6 +251,7 @@
             fsLabel = value;
         }
     }
+#endif
 
     return OK;
 }
@@ -511,6 +537,11 @@
         PLOG(ERROR) << "Failed to read supported filesystems";
         return false;
     }
+
+    /* fuse filesystems */
+    supported.append("fuse\tntfs\n"
+                     "fuse\texfat\n");
+
     return supported.find(fsType + "\n") != std::string::npos;
 }
 
diff --git a/Utils.h b/Utils.h
index 4bfd8e9..3dfb8d9 100644
--- a/Utils.h
+++ b/Utils.h
@@ -50,7 +50,7 @@
 status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid);
 
 /* Really unmounts the path, killing active processes along the way */
-status_t ForceUnmount(const std::string& path);
+status_t ForceUnmount(const std::string& path, bool detach = false);
 
 /* Kills any processes using given path */
 status_t KillProcessesUsingPath(const std::string& path);
diff --git a/VolumeBase.cpp b/VolumeBase.cpp
index ea4d372..fdb5399 100644
--- a/VolumeBase.cpp
+++ b/VolumeBase.cpp
@@ -161,12 +161,14 @@
 }
 
 status_t VolumeBase::create() {
-    CHECK(!mCreated);
+    if (mCreated) {
+        return BAD_VALUE;
+    }
 
     mCreated = true;
-    status_t res = doCreate();
     notifyEvent(ResponseCode::VolumeCreated,
             StringPrintf("%d \"%s\" \"%s\"", mType, mDiskId.c_str(), mPartGuid.c_str()));
+    status_t res = doCreate();
     setState(State::kUnmounted);
     return res;
 }
@@ -176,7 +178,9 @@
 }
 
 status_t VolumeBase::destroy() {
-    CHECK(mCreated);
+    if (!mCreated) {
+        return NO_INIT;
+    }
 
     if (mState == State::kMounted) {
         unmount();
@@ -212,7 +216,7 @@
     return res;
 }
 
-status_t VolumeBase::unmount() {
+status_t VolumeBase::unmount(bool detach /* = false */) {
     if (mState != State::kMounted) {
         LOG(WARNING) << getId() << " unmount requires state mounted";
         return -EBUSY;
@@ -227,7 +231,7 @@
     }
     mVolumes.clear();
 
-    status_t res = doUnmount();
+    status_t res = doUnmount(detach);
     setState(State::kUnmounted);
     return res;
 }
diff --git a/VolumeBase.h b/VolumeBase.h
index d417019..4f2c397 100644
--- a/VolumeBase.h
+++ b/VolumeBase.h
@@ -98,7 +98,7 @@
     status_t create();
     status_t destroy();
     status_t mount();
-    status_t unmount();
+    status_t unmount(bool detach = false);
     status_t format(const std::string& fsType);
 
 protected:
@@ -107,7 +107,7 @@
     virtual status_t doCreate();
     virtual status_t doDestroy();
     virtual status_t doMount() = 0;
-    virtual status_t doUnmount() = 0;
+    virtual status_t doUnmount(bool detach = false) = 0;
     virtual status_t doFormat(const std::string& fsType);
 
     status_t setId(const std::string& id);
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 5cc60a1..6c4771c 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -311,8 +311,13 @@
                     flags |= android::vold::Disk::Flags::kUsb;
                 }
 
-                auto disk = new android::vold::Disk(eventPath, device,
-                        source->getNickname(), flags);
+                android::vold::Disk* disk = (source->getPartNum() == -1) ?
+                        new android::vold::Disk(eventPath, device,
+                                source->getNickname(), flags) :
+                        new android::vold::DiskPartition(eventPath, device,
+                                source->getNickname(), flags,
+                                source->getPartNum(),
+                                source->getFsType(), source->getMntOpts());
                 disk->create();
                 mDisks.push_back(std::shared_ptr<android::vold::Disk>(disk));
                 break;
diff --git a/VolumeManager.h b/VolumeManager.h
index 39fc8f9..dfd8c8f 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -36,6 +36,7 @@
 #include <sysutils/NetlinkEvent.h>
 
 #include "Disk.h"
+#include "DiskPartition.h"
 #include "VolumeBase.h"
 
 /* The length of an MD5 hash when encoded into ASCII hex characters */
@@ -96,8 +97,12 @@
 
     class DiskSource {
     public:
-        DiskSource(const std::string& sysPattern, const std::string& nickname, int flags) :
-                mSysPattern(sysPattern), mNickname(nickname), mFlags(flags) {
+        DiskSource(const std::string& sysPattern, const std::string& nickname,
+                        int partnum, int flags,
+                        const std::string& fstype, const std::string mntopts) :
+                mSysPattern(sysPattern), mNickname(nickname),
+                mPartNum(partnum), mFlags(flags),
+                mFsType(fstype), mMntOpts(mntopts) {
         }
 
         bool matches(const std::string& sysPath) {
@@ -105,12 +110,18 @@
         }
 
         const std::string& getNickname() { return mNickname; }
+        int getPartNum() { return mPartNum; }
         int getFlags() { return mFlags; }
+        const std::string& getFsType() { return mFsType; }
+        const std::string& getMntOpts() { return mMntOpts; }
 
     private:
         std::string mSysPattern;
         std::string mNickname;
+        int mPartNum;
         int mFlags;
+        std::string mFsType;
+        std::string mMntOpts;
     };
 
     void addDiskSource(const std::shared_ptr<DiskSource>& diskSource);
diff --git a/cryptfs.c b/cryptfs.c
index ef8fe76..1618619 100644
--- a/cryptfs.c
+++ b/cryptfs.c
@@ -249,6 +249,18 @@
 }
 #endif
 
+#ifdef MINIVOLD
+inline int release_wake_lock(const char* id) { return 0; }
+inline int acquire_wake_lock(int lock, const char* id) { return 0; }
+
+static const char* kMkExt4fsPath = "/sbin/mke2fs";
+static const char* kMkF2fsPath = "/sbin/mkfs.f2fs";
+#else
+static const char* kMkExt4fsPath = "/system/bin/make_ext4fs";
+static const char* kMkF2fsPath = "/system/bin/mkfs.f2fs";
+#endif
+
+#ifndef MINIVOLD // no HALs in recovery...
 static int keymaster_init(keymaster0_device_t **keymaster0_dev,
                           keymaster1_device_t **keymaster1_dev)
 {
@@ -287,10 +299,14 @@
     *keymaster1_dev = NULL;
     return rc;
 }
+#endif
 
 /* Should we use keymaster? */
 static int keymaster_check_compatibility()
 {
+#ifdef MINIVOLD
+    return -1;
+#else
     keymaster0_device_t *keymaster0_dev = 0;
     keymaster1_device_t *keymaster1_dev = 0;
     int rc = 0;
@@ -332,11 +348,15 @@
         keymaster0_close(keymaster0_dev);
     }
     return rc;
+#endif
 }
 
 /* Create a new keymaster key and store it in this footer */
 static int keymaster_create_key(struct crypt_mnt_ftr *ftr)
 {
+#ifdef MINIVOLD // no HALs in recovery...
+    return -1;
+#else
     uint8_t* key = 0;
     keymaster0_device_t *keymaster0_dev = 0;
     keymaster1_device_t *keymaster1_dev = 0;
@@ -424,6 +444,7 @@
         keymaster1_close(keymaster1_dev);
     free(key);
     return rc;
+#endif
 }
 
 /* This signs the given object using the keymaster key. */
@@ -433,6 +454,9 @@
                                  unsigned char **signature,
                                  size_t *signature_size)
 {
+#ifdef MINIVOLD // no HALs in recovery...
+    return -1;
+#else
     int rc = 0;
     keymaster0_device_t *keymaster0_dev = 0;
     keymaster1_device_t *keymaster1_dev = 0;
@@ -558,6 +582,7 @@
             keymaster0_close(keymaster0_dev);
 
         return rc;
+#endif
 }
 
 /* Store password when userdata is successfully decrypted and mounted.
@@ -2529,7 +2554,7 @@
     int rc = -1;
 
     if (type == EXT4_FS) {
-        args[0] = "/system/bin/make_ext4fs";
+        args[0] = kMkExt4fsPath;
         args[1] = "-a";
         args[2] = "/data";
         args[3] = "-l";
@@ -2540,7 +2565,7 @@
         SLOGI("Making empty filesystem with command %s %s %s %s %s %s\n",
               args[0], args[1], args[2], args[3], args[4], args[5]);
     } else if (type == F2FS_FS) {
-        args[0] = "/system/bin/mkfs.f2fs";
+        args[0] = kMkF2fsPath;
         args[1] = "-t";
         args[2] = "-d1";
         args[3] = crypto_blkdev;
@@ -4214,6 +4239,12 @@
 int cryptfs_isConvertibleToFBE()
 {
     struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, DATA_MNT_POINT);
+    // MultiROM secondary ROMs hate fstab assumptions
+    if (!rec) {
+        SLOGE("Can't get fstab record for %s\n", DATA_MNT_POINT);
+        return 0;
+    }
+
     return fs_mgr_is_convertible_to_fbe(rec) ? 1 : 0;
 }
 
diff --git a/fs/Exfat.cpp b/fs/Exfat.cpp
new file mode 100644
index 0000000..0e20964
--- /dev/null
+++ b/fs/Exfat.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2015 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 "Exfat.h"
+#include "Utils.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <vector>
+#include <string>
+
+#include <sys/mount.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+namespace exfat {
+
+#ifdef MINIVOLD
+static const char* kMkfsPath = "/sbin/mkfs.exfat";
+static const char* kFsckPath = "/sbin/fsck.exfat";
+#ifdef CONFIG_KERNEL_HAVE_EXFAT
+static const char* kMountPath = "/sbin/mount";
+#else
+static const char* kMountPath = "/sbin/mount.exfat";
+#endif
+#else
+static const char* kMkfsPath = "/system/bin/mkfs.exfat";
+static const char* kFsckPath = "/system/bin/fsck.exfat";
+#ifdef CONFIG_KERNEL_HAVE_EXFAT
+static const char* kMountPath = "/system/bin/mount";
+#else
+static const char* kMountPath = "/system/bin/mount.exfat";
+#endif
+#endif
+
+bool IsSupported() {
+    return access(kMkfsPath, X_OK) == 0
+            && access(kFsckPath, X_OK) == 0
+            && access(kMountPath, X_OK) == 0
+            && IsFilesystemSupported("exfat");
+}
+
+status_t Check(const std::string& source) {
+    std::vector<std::string> cmd;
+    cmd.push_back(kFsckPath);
+    cmd.push_back(source);
+
+    // Exfat devices are currently always untrusted
+    return ForkExecvp(cmd, sFsckUntrustedContext);
+}
+
+status_t Mount(const std::string& source, const std::string& target, bool ro,
+        bool remount, bool executable, int ownerUid, int ownerGid, int permMask) {
+    char mountData[255];
+
+    const char* c_source = source.c_str();
+    const char* c_target = target.c_str();
+
+    sprintf(mountData,
+#ifdef CONFIG_KERNEL_HAVE_EXFAT
+            "noatime,nodev,nosuid,uid=%d,gid=%d,fmask=%o,dmask=%o,%s,%s",
+#else
+            "noatime,nodev,nosuid,dirsync,uid=%d,gid=%d,fmask=%o,dmask=%o,%s,%s",
+#endif
+            ownerUid, ownerGid, permMask, permMask,
+            (executable ? "exec" : "noexec"),
+            (ro ? "ro" : "rw"));
+
+    std::vector<std::string> cmd;
+    cmd.push_back(kMountPath);
+#ifdef CONFIG_KERNEL_HAVE_EXFAT
+    cmd.push_back("-t");
+    cmd.push_back("exfat");
+#endif
+    cmd.push_back("-o");
+    cmd.push_back(mountData);
+    cmd.push_back(c_source);
+    cmd.push_back(c_target);
+
+    return ForkExecvp(cmd);
+}
+
+status_t Format(const std::string& source) {
+    std::vector<std::string> cmd;
+    cmd.push_back(kMkfsPath);
+    cmd.push_back(source);
+
+    return ForkExecvp(cmd);
+}
+
+}  // namespace exfat
+}  // namespace vold
+}  // namespace android
diff --git a/fs/Exfat.h b/fs/Exfat.h
new file mode 100644
index 0000000..cd4fb5d
--- /dev/null
+++ b/fs/Exfat.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 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 ANDROID_VOLD_EXFAT_H
+#define ANDROID_VOLD_EXFAT_H
+
+#include <utils/Errors.h>
+
+#include <string>
+
+namespace android {
+namespace vold {
+namespace exfat {
+
+bool IsSupported();
+
+status_t Check(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);
+
+}  // namespace exfat
+}  // namespace vold
+}  // namespace android
+
+#endif
diff --git a/fs/Ext4.cpp b/fs/Ext4.cpp
index 0bd5b0c..6de0e41 100644
--- a/fs/Ext4.cpp
+++ b/fs/Ext4.cpp
@@ -42,6 +42,7 @@
 #include <cutils/log.h>
 #include <cutils/properties.h>
 #include <logwrap/logwrap.h>
+#include <private/android_filesystem_config.h>
 #include <selinux/selinux.h>
 
 #include "Ext4.h"
@@ -54,9 +55,15 @@
 namespace vold {
 namespace ext4 {
 
+#ifdef MINIVOLD
+static const char* kResizefsPath = "/sbin/resize2fs";
+static const char* kMkfsPath = "/sbin/mke2fs";
+static const char* kFsckPath = "/sbin/e2fsck";
+#else
 static const char* kResizefsPath = "/system/bin/resize2fs";
 static const char* kMkfsPath = "/system/bin/make_ext4fs";
 static const char* kFsckPath = "/system/bin/e2fsck";
+#endif
 
 bool IsSupported() {
     return access(kMkfsPath, X_OK) == 0
@@ -64,7 +71,7 @@
             && IsFilesystemSupported("ext4");
 }
 
-status_t Check(const std::string& source, const std::string& target) {
+status_t Check(const std::string& source, const std::string& target, bool trusted) {
     // The following is shamelessly borrowed from fs_mgr.c, so it should be
     // kept in sync with any changes over there.
 
@@ -119,33 +126,53 @@
         cmd.push_back("-y");
         cmd.push_back(c_source);
 
-        // ext4 devices are currently always trusted
-        return ForkExecvp(cmd, sFsckContext);
+        return ForkExecvp(cmd, trusted ? sFsckContext : sFsckUntrustedContext);
     }
 
     return 0;
 }
 
 status_t Mount(const std::string& source, const std::string& target, bool ro,
-        bool remount, bool executable) {
+        bool remount, bool executable, const std::string& opts /* = "" */,
+        bool trusted, bool portable) {
     int rc;
     unsigned long flags;
 
+    std::string data(opts);
+
+    if (portable) {
+        if (!data.empty()) {
+            data += ",";
+        }
+        data += "context=u:object_r:sdcard_posix:s0";
+    }
+
     const char* c_source = source.c_str();
     const char* c_target = target.c_str();
+    const char* c_data = data.c_str();
 
-    flags = MS_NOATIME | MS_NODEV | MS_NOSUID | MS_DIRSYNC;
+    flags = MS_NOATIME | MS_NODEV | MS_NOSUID;
+
+    // Only use MS_DIRSYNC if we're not mounting adopted storage
+    if (!trusted) {
+        flags |= MS_DIRSYNC;
+    }
 
     flags |= (executable ? 0 : MS_NOEXEC);
     flags |= (ro ? MS_RDONLY : 0);
     flags |= (remount ? MS_REMOUNT : 0);
 
-    rc = mount(c_source, c_target, "ext4", flags, NULL);
+    rc = mount(c_source, c_target, "ext4", flags, c_data);
+
+    if (portable && rc == 0) {
+        chown(c_target, AID_MEDIA_RW, AID_MEDIA_RW);
+        chmod(c_target, 0775);
+    }
 
     if (rc && errno == EROFS) {
         SLOGE("%s appears to be a read only filesystem - retrying mount RO", c_source);
         flags |= MS_RDONLY;
-        rc = mount(c_source, c_target, "ext4", flags, NULL);
+        rc = mount(c_source, c_target, "ext4", flags, c_data);
     }
 
     return rc;
diff --git a/fs/Ext4.h b/fs/Ext4.h
index f78dc95..7445fe3 100644
--- a/fs/Ext4.h
+++ b/fs/Ext4.h
@@ -27,9 +27,11 @@
 
 bool IsSupported();
 
-status_t Check(const std::string& source, const std::string& target);
+status_t Check(const std::string& source, const std::string& target,
+        bool trusted);
 status_t Mount(const std::string& source, const std::string& target, bool ro,
-        bool remount, bool executable);
+        bool remount, bool executable, const std::string& opts = "",
+        bool trusted = false, bool portable = false);
 status_t Format(const std::string& source, unsigned long numSectors,
         const std::string& target);
 status_t Resize(const std::string& source, unsigned long numSectors);
diff --git a/fs/F2fs.cpp b/fs/F2fs.cpp
index 0d12b07..6afa8ce 100644
--- a/fs/F2fs.cpp
+++ b/fs/F2fs.cpp
@@ -19,11 +19,13 @@
 
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <private/android_filesystem_config.h>
 
 #include <vector>
 #include <string>
 
 #include <sys/mount.h>
+#include <sys/stat.h>
 
 using android::base::StringPrintf;
 
@@ -31,8 +33,13 @@
 namespace vold {
 namespace f2fs {
 
-static const char* kMkfsPath = "/system/bin/make_f2fs";
+#ifdef MINIVOLD
+static const char* kMkfsPath = "/sbin/mkfs.f2fs";
+static const char* kFsckPath = "/sbin/fsck.f2fs";
+#else
+static const char* kMkfsPath = "/system/bin/mkfs.f2fs";
 static const char* kFsckPath = "/system/bin/fsck.f2fs";
+#endif
 
 bool IsSupported() {
     return access(kMkfsPath, X_OK) == 0
@@ -40,26 +47,48 @@
             && IsFilesystemSupported("f2fs");
 }
 
-status_t Check(const std::string& source) {
+status_t Check(const std::string& source, bool trusted) {
     std::vector<std::string> cmd;
     cmd.push_back(kFsckPath);
     cmd.push_back("-a");
     cmd.push_back(source);
 
-    // f2fs devices are currently always trusted
-    return ForkExecvp(cmd, sFsckContext);
+    return ForkExecvp(cmd, trusted ? sFsckContext : sFsckUntrustedContext);
 }
 
-status_t Mount(const std::string& source, const std::string& target) {
+status_t Mount(const std::string& source, const std::string& target,
+        const std::string& opts /* = "" */, bool trusted, bool portable) {
+    std::string data(opts);
+
+    if (portable) {
+        if (!data.empty()) {
+            data += ",";
+        }
+        data += "context=u:object_r:sdcard_posix:s0";
+    }
+
     const char* c_source = source.c_str();
     const char* c_target = target.c_str();
-    unsigned long flags = MS_NOATIME | MS_NODEV | MS_NOSUID | MS_DIRSYNC;
+    const char* c_data = data.c_str();
 
-    int res = mount(c_source, c_target, "f2fs", flags, NULL);
+    unsigned long flags = MS_NOATIME | MS_NODEV | MS_NOSUID;
+
+    // Only use MS_DIRSYNC if we're not mounting adopted storage
+    if (!trusted) {
+        flags |= MS_DIRSYNC;
+    }
+
+    int res = mount(c_source, c_target, "f2fs", flags, c_data);
+
+    if (portable && res == 0) {
+        chown(c_target, AID_MEDIA_RW, AID_MEDIA_RW);
+        chmod(c_target, 0775);
+    }
+
     if (res != 0) {
         PLOG(ERROR) << "Failed to mount " << source;
         if (errno == EROFS) {
-            res = mount(c_source, c_target, "f2fs", flags | MS_RDONLY, NULL);
+            res = mount(c_source, c_target, "f2fs", flags | MS_RDONLY, c_data);
             if (res != 0) {
                 PLOG(ERROR) << "Failed to mount read-only " << source;
             }
diff --git a/fs/F2fs.h b/fs/F2fs.h
index f710212..ecfc0c7 100644
--- a/fs/F2fs.h
+++ b/fs/F2fs.h
@@ -27,8 +27,10 @@
 
 bool IsSupported();
 
-status_t Check(const std::string& source);
-status_t Mount(const std::string& source, const std::string& target);
+status_t Check(const std::string& source, bool trusted);
+status_t Mount(const std::string& source, const std::string& target,
+        const std::string& opts = "", bool trusted = false,
+        bool portable = false);
 status_t Format(const std::string& source);
 
 }  // namespace f2fs
diff --git a/fs/Ntfs.cpp b/fs/Ntfs.cpp
new file mode 100644
index 0000000..c6d0813
--- /dev/null
+++ b/fs/Ntfs.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 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 "Ntfs.h"
+#include "Utils.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <vector>
+#include <string>
+
+#include <sys/mount.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+namespace ntfs {
+
+#ifdef MINIVOLD
+static const char* kMkfsPath = "/sbin/mkfs.ntfs";
+static const char* kFsckPath = "/sbin/fsck.ntfs";
+#ifdef CONFIG_KERNEL_HAVE_NTFS
+static const char* kMountPath = "/sbin/mount";
+#else
+static const char* kMountPath = "/sbin/mount.ntfs";
+#endif
+#else
+static const char* kMkfsPath = "/system/bin/mkfs.ntfs";
+static const char* kFsckPath = "/system/bin/fsck.ntfs";
+#ifdef CONFIG_KERNEL_HAVE_NTFS
+static const char* kMountPath = "/system/bin/mount";
+#else
+static const char* kMountPath = "/system/bin/mount.ntfs";
+#endif
+#endif
+
+bool IsSupported() {
+    return access(kMkfsPath, X_OK) == 0
+            && access(kFsckPath, X_OK) == 0
+            && access(kMountPath, X_OK) == 0
+            && IsFilesystemSupported("ntfs");
+}
+
+status_t Check(const std::string& source) {
+    std::vector<std::string> cmd;
+    cmd.push_back(kFsckPath);
+    cmd.push_back("-n");
+    cmd.push_back(source);
+
+    // Ntfs devices are currently always untrusted
+    return ForkExecvp(cmd, sFsckUntrustedContext);
+}
+
+status_t Mount(const std::string& source, const std::string& target, bool ro,
+        bool remount, bool executable, int ownerUid, int ownerGid, int permMask,
+        bool createLost) {
+    char mountData[255];
+
+    const char* c_source = source.c_str();
+    const char* c_target = target.c_str();
+
+    sprintf(mountData,
+#ifdef CONFIG_KERNEL_HAVE_NTFS
+            "utf8,uid=%d,gid=%d,fmask=%o,dmask=%o,nodev,nosuid",
+#else
+            "utf8,uid=%d,gid=%d,fmask=%o,dmask=%o,"
+            "shortname=mixed,nodev,nosuid,dirsync",
+#endif
+            ownerUid, ownerGid, permMask, permMask);
+
+    if (!executable)
+        strcat(mountData, ",noexec");
+    if (ro)
+        strcat(mountData, ",ro");
+    if (remount)
+        strcat(mountData, ",remount");
+
+    std::vector<std::string> cmd;
+    cmd.push_back(kMountPath);
+#ifdef CONFIG_KERNEL_HAVE_NTFS
+    cmd.push_back("-t");
+    cmd.push_back("ntfs");
+#endif
+    cmd.push_back("-o");
+    cmd.push_back(mountData);
+    cmd.push_back(c_source);
+    cmd.push_back(c_target);
+
+    return ForkExecvp(cmd);
+}
+
+status_t Format(const std::string& source, bool wipe) {
+    std::vector<std::string> cmd;
+    cmd.push_back(kMkfsPath);
+    if (wipe)
+        cmd.push_back("-f");
+    cmd.push_back(source);
+
+    return ForkExecvp(cmd);
+}
+
+}  // namespace ntfs
+}  // namespace vold
+}  // namespace android
diff --git a/fs/Ntfs.h b/fs/Ntfs.h
new file mode 100644
index 0000000..805fb99
--- /dev/null
+++ b/fs/Ntfs.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 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 ANDROID_VOLD_NTFS_H
+#define ANDROID_VOLD_NTFS_H
+
+#include <utils/Errors.h>
+
+#include <string>
+
+namespace android {
+namespace vold {
+namespace ntfs {
+
+bool IsSupported();
+
+status_t Check(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,
+        bool createLost);
+status_t Format(const std::string& source, bool wipe);
+
+}  // namespace ntfs
+}  // namespace vold
+}  // namespace android
+
+#endif
diff --git a/main.cpp b/main.cpp
index 68477ac..e1e59b0 100644
--- a/main.cpp
+++ b/main.cpp
@@ -17,7 +17,9 @@
 #include "Disk.h"
 #include "VolumeManager.h"
 #include "CommandListener.h"
+#ifndef MINIVOLD
 #include "CryptCommandListener.h"
+#endif
 #include "NetlinkManager.h"
 #include "cryptfs.h"
 #include "sehandle.h"
@@ -45,24 +47,32 @@
 
 struct fstab *fstab;
 
+#ifdef MINIVOLD
+extern struct selabel_handle *sehandle;
+#else
 struct selabel_handle *sehandle;
+#endif
 
 using android::base::StringPrintf;
 
-int main(int argc, char** argv) {
+extern "C" int vold_main(int argc, char** argv) {
     setenv("ANDROID_LOG_TAGS", "*:v", 1);
     android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
 
     LOG(INFO) << "Vold 3.0 (the awakening) firing up";
 
     LOG(VERBOSE) << "Detected support for:"
+            << (android::vold::IsFilesystemSupported("exfat") ? " exfat" : "")
             << (android::vold::IsFilesystemSupported("ext4") ? " ext4" : "")
             << (android::vold::IsFilesystemSupported("f2fs") ? " f2fs" : "")
+            << (android::vold::IsFilesystemSupported("ntfs") ? " ntfs" : "")
             << (android::vold::IsFilesystemSupported("vfat") ? " vfat" : "");
 
     VolumeManager *vm;
     CommandListener *cl;
+#ifndef MINIVOLD
     CryptCommandListener *ccl;
+#endif
     NetlinkManager *nm;
 
     parse_args(argc, argv);
@@ -97,7 +107,9 @@
     }
 
     cl = new CommandListener();
+#ifndef MINIVOLD
     ccl = new CryptCommandListener();
+#endif
     vm->setBroadcaster((SocketListener *) cl);
     nm->setBroadcaster((SocketListener *) cl);
 
@@ -128,10 +140,12 @@
         exit(1);
     }
 
+#ifndef MINIVOLD
     if (ccl->startListener()) {
         PLOG(ERROR) << "Unable to start CryptCommandListener";
         exit(1);
     }
+#endif
 
     // This call should go after listeners are started to avoid
     // a deadlock between vold and init (see b/34278978 for details)
@@ -225,13 +239,17 @@
     *has_adoptable = false;
     for (int i = 0; i < fstab->num_entries; i++) {
         if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
-            if (fs_mgr_is_nonremovable(&fstab->recs[i])) {
-                LOG(WARNING) << "nonremovable no longer supported; ignoring volume";
-                continue;
-            }
-
             std::string sysPattern(fstab->recs[i].blk_device);
+            std::string fstype;
+            if (fstab->recs[i].fs_type) {
+                fstype = fstab->recs[i].fs_type;
+            }
+            std::string mntopts;
+            if (fstab->recs[i].fs_options) {
+                mntopts = fstab->recs[i].fs_options;
+            }
             std::string nickname(fstab->recs[i].label);
+            int partnum = fstab->recs[i].partnum;
             int flags = 0;
 
             if (fs_mgr_is_encryptable(&fstab->recs[i])) {
@@ -242,9 +260,13 @@
                     || property_get_bool("vold.debug.default_primary", false)) {
                 flags |= android::vold::Disk::Flags::kDefaultPrimary;
             }
+            if (fs_mgr_is_nonremovable(&fstab->recs[i])) {
+                flags |= android::vold::Disk::Flags::kNonRemovable;
+            }
 
             vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(
-                    new VolumeManager::DiskSource(sysPattern, nickname, flags)));
+                    new VolumeManager::DiskSource(sysPattern, nickname, partnum, flags,
+                                    fstype, mntopts)));
         }
     }
     return 0;
diff --git a/vdc.cpp b/vdc.cpp
index 4eb26cd..0f43dab 100644
--- a/vdc.cpp
+++ b/vdc.cpp
@@ -41,7 +41,7 @@
 
 static constexpr int kCommandTimeoutMs = 20 * 1000;
 
-int main(int argc, char **argv) {
+extern "C" int vdc_main(int argc, char **argv) {
     int sock;
     int wait_for_socket;
     char *progname;
@@ -172,3 +172,10 @@
     fprintf(stderr,
             "Usage: %s [--wait] <monitor>|<cmd> [arg1] [arg2...]\n", progname);
 }
+
+#ifndef MINIVOLD
+int main(int argc, char **argv) {
+    return vdc_main(argc, argv);
+}
+#endif
+
diff --git a/vold.c b/vold.c
new file mode 100644
index 0000000..9dc9f10
--- /dev/null
+++ b/vold.c
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 The CyanogenMod 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 "vold.h"
+
+int main(int argc, char **argv) {
+    return vold_main(argc, argv);
+}
diff --git a/vold.h b/vold.h
new file mode 100644
index 0000000..a873992
--- /dev/null
+++ b/vold.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The CyanogenMod 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 _VOLD_H
+#define _VOLD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int vold_main();
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+