Add basic exFAT support.
Several partners have been requesting exFAT support. Android doesn't
natively support exFAT, but we're at least willing to try mounting an
exFAT filesystem if we detect the Linux kernel supports it, and if
helper binaries are present.
This CL is simple scaffolding, and it provides no actual
implementation of exFAT.
Test: builds, boots
Bug: 67822822
Change-Id: Id4f8ec3967b32de6e1c0e3c4b47fe6e43a6291ab
diff --git a/Android.bp b/Android.bp
index aca6493..0a45395 100644
--- a/Android.bp
+++ b/Android.bp
@@ -115,6 +115,7 @@
"VolumeManager.cpp",
"authorization_set.cpp",
"cryptfs.cpp",
+ "fs/Exfat.cpp",
"fs/Ext4.cpp",
"fs/F2fs.cpp",
"fs/Vfat.cpp",
diff --git a/fs/Exfat.cpp b/fs/Exfat.cpp
new file mode 100644
index 0000000..5c15075
--- /dev/null
+++ b/fs/Exfat.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/mount.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <logwrap/logwrap.h>
+
+#include "Exfat.h"
+#include "Utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+namespace exfat {
+
+static const char* kMkfsPath = "/system/bin/mkfs.exfat";
+static const char* kFsckPath = "/system/bin/fsck.exfat";
+
+bool IsSupported() {
+ return access(kMkfsPath, X_OK) == 0 && access(kFsckPath, 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);
+
+ int rc = ForkExecvp(cmd, sFsckUntrustedContext);
+ if (rc == 0) {
+ LOG(INFO) << "Check OK";
+ return 0;
+ } else {
+ LOG(ERROR) << "Check failed (code " << rc << ")";
+ 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);
+
+ 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");
+ cmd.push_back(source);
+
+ int rc = ForkExecvp(cmd);
+ if (rc == 0) {
+ LOG(INFO) << "Format OK";
+ return 0;
+ } else {
+ LOG(ERROR) << "Format failed (code " << rc << ")";
+ errno = EIO;
+ return -1;
+ }
+ return 0;
+}
+
+} // namespace exfat
+} // namespace vold
+} // namespace android
diff --git a/fs/Exfat.h b/fs/Exfat.h
new file mode 100644
index 0000000..768d8a5
--- /dev/null
+++ b/fs/Exfat.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 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, int ownerUid, int ownerGid,
+ int permMask);
+status_t Format(const std::string& source);
+
+} // namespace exfat
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/model/Disk.cpp b/model/Disk.cpp
index 9b772e4..becf8b7 100644
--- a/model/Disk.cpp
+++ b/model/Disk.cpp
@@ -364,12 +364,13 @@
}
switch (type) {
- case 0x06: // FAT16
- case 0x0b: // W95 FAT32 (LBA)
- case 0x0c: // W95 FAT32 (LBA)
- case 0x0e: // W95 FAT16 (LBA)
- createPublicVolume(partDevice);
- break;
+ case 0x06: // FAT16
+ case 0x07: // HPFS/NTFS/exFAT
+ case 0x0b: // W95 FAT32 (LBA)
+ case 0x0c: // W95 FAT32 (LBA)
+ case 0x0e: // W95 FAT16 (LBA)
+ createPublicVolume(partDevice);
+ break;
}
} else if (table == Table::kGpt) {
if (++it == split.end()) continue;
diff --git a/model/PublicVolume.cpp b/model/PublicVolume.cpp
index 98c897f..efdb687 100644
--- a/model/PublicVolume.cpp
+++ b/model/PublicVolume.cpp
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#include "fs/Vfat.h"
#include "PublicVolume.h"
#include "Utils.h"
#include "VolumeManager.h"
+#include "fs/Exfat.h"
+#include "fs/Vfat.h"
#include <android-base/stringprintf.h>
#include <android-base/logging.h>
@@ -93,19 +94,23 @@
}
status_t PublicVolume::doMount() {
- // TODO: expand to support mounting other filesystems
readMetadata();
- if (mFsType != "vfat") {
+ if (mFsType == "vfat" && vfat::IsSupported()) {
+ if (vfat::Check(mDevPath)) {
+ LOG(ERROR) << getId() << " failed filesystem check";
+ return -EIO;
+ }
+ } else if (mFsType == "exfat" && exfat::IsSupported()) {
+ if (exfat::Check(mDevPath)) {
+ LOG(ERROR) << getId() << " failed filesystem check";
+ return -EIO;
+ }
+ } else {
LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;
return -EIO;
}
- if (vfat::Check(mDevPath)) {
- LOG(ERROR) << getId() << " failed filesystem check";
- return -EIO;
- }
-
// Use UUID as stable name, if available
std::string stableName = getId();
if (!mFsUuid.empty()) {
@@ -130,10 +135,17 @@
return -errno;
}
- if (vfat::Mount(mDevPath, mRawPath, false, false, false,
- AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) {
- PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
- return -EIO;
+ if (mFsType == "vfat") {
+ if (vfat::Mount(mDevPath, mRawPath, false, false, false, AID_MEDIA_RW, AID_MEDIA_RW, 0007,
+ true)) {
+ PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
+ return -EIO;
+ }
+ } else if (mFsType == "exfat") {
+ if (exfat::Mount(mDevPath, mRawPath, AID_MEDIA_RW, AID_MEDIA_RW, 0007)) {
+ PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
+ return -EIO;
+ }
}
if (getMountFlags() & MountFlags::kPrimary) {
@@ -238,7 +250,7 @@
}
status_t PublicVolume::doFormat(const std::string& fsType) {
- if (fsType == "vfat" || fsType == "auto") {
+ if ((fsType == "vfat" || fsType == "auto") && vfat::IsSupported()) {
if (WipeBlockDevice(mDevPath) != OK) {
LOG(WARNING) << getId() << " failed to wipe";
}
@@ -246,6 +258,14 @@
LOG(ERROR) << getId() << " failed to format";
return -errno;
}
+ } else if ((fsType == "exfat" || fsType == "auto") && exfat::IsSupported()) {
+ if (WipeBlockDevice(mDevPath) != OK) {
+ LOG(WARNING) << getId() << " failed to wipe";
+ }
+ if (exfat::Format(mDevPath)) {
+ LOG(ERROR) << getId() << " failed to format";
+ return -errno;
+ }
} else {
LOG(ERROR) << "Unsupported filesystem " << fsType;
return -EINVAL;