[FP4-128]add exfat support
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 7782dd3..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("-y");
+ 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 09d4c1a..97d0937 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>