From 37ba125205023b3b6e80ed684ca4d282224c66dc Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 19 Jan 2018 10:55:18 +0900 Subject: [PATCH] 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 --- Android.bp | 1 + fs/Exfat.cpp | 97 ++++++++++++++++++++++++++++++++++++++++++ fs/Exfat.h | 39 +++++++++++++++++ model/Disk.cpp | 13 +++--- model/PublicVolume.cpp | 46 ++++++++++++++------ 5 files changed, 177 insertions(+), 19 deletions(-) create mode 100644 fs/Exfat.cpp create mode 100644 fs/Exfat.h diff --git a/Android.bp b/Android.bp index aca6493..0a45395 100644 --- a/Android.bp +++ b/Android.bp @@ -115,6 +115,7 @@ cc_library_static { "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 + +#include +#include + +#include + +#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 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 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 + +#include + +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 @@ status_t Disk::readPartitions() { } 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 #include @@ -93,19 +94,23 @@ status_t PublicVolume::doDestroy() { } 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 @@ status_t PublicVolume::doMount() { 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::doUnmount() { } 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 @@ status_t PublicVolume::doFormat(const std::string& fsType) { 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;