Merge tag 'android-13.0.0_r32' into int/13/fp3
Android 13.0.0 release 32
* tag 'android-13.0.0_r32':
Change-Id: I80ac2fd52c36920e6ff7147353dc8f4be095ac46
diff --git a/Android.bp b/Android.bp
index 8f413c4..bd2610c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -131,9 +131,13 @@
"VoldUtil.cpp",
"VolumeManager.cpp",
"cryptfs.cpp",
+ "fs/Apfs.cpp",
"fs/Exfat.cpp",
"fs/Ext4.cpp",
"fs/F2fs.cpp",
+ "fs/Filesystems.cpp",
+ "fs/HfsPlus.cpp",
+ "fs/Ntfs.cpp",
"fs/Vfat.cpp",
"model/Disk.cpp",
"model/EmulatedVolume.cpp",
diff --git a/Utils.cpp b/Utils.cpp
index e8049ed..2409b45 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -645,6 +645,15 @@
std::vector<std::string> output;
status_t res = ForkExecvp(cmd, &output, untrusted ? sBlkidUntrustedContext : sBlkidContext);
+
+ int count = 0;
+
+ while (res != OK && count < 5) {
+ LOG(WARNING) << "blkid failed to identify " << path << " retry count = " << count;
+ res = ForkExecvp(cmd, &output, untrusted ? sBlkidUntrustedContext : sBlkidContext);
+ count++;
+ }
+
if (res != OK) {
LOG(WARNING) << "blkid failed to identify " << path;
return res;
diff --git a/fs/Apfs.cpp b/fs/Apfs.cpp
new file mode 100644
index 0000000..02d91b0
--- /dev/null
+++ b/fs/Apfs.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010-2022 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..c8980c0
--- /dev/null
+++ b/fs/Apfs.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010-2022 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 c8b19e0..4b3d8ab 100644
--- a/fs/Exfat.cpp
+++ b/fs/Exfat.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2010-2022 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 = ForkExecvpTimeout(cmd, kUntrustedFsckSleepTime, 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..18644e4 100644
--- a/fs/Exfat.h
+++ b/fs/Exfat.h
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2010-2022 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..9c7ee69
--- /dev/null
+++ b/fs/Filesystems.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014-2022 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..d4df7d4
--- /dev/null
+++ b/fs/Filesystems.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014-2022 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..2c37d1f
--- /dev/null
+++ b/fs/HfsPlus.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010-2022 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..08b38a0
--- /dev/null
+++ b/fs/HfsPlus.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2011-2022 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..873c973
--- /dev/null
+++ b/fs/Ntfs.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010-2022 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..90ce325
--- /dev/null
+++ b/fs/Ntfs.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010-2022 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 f3f04d8..bd65d7b 100644
--- a/fs/Vfat.cpp
+++ b/fs/Vfat.cpp
@@ -32,6 +32,8 @@
#include <linux/kdev_t.h>
+#define LOG_TAG "Vold::Fat"
+
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <selinux/selinux.h>
diff --git a/main.cpp b/main.cpp
index b07ee68..3a1b452 100644
--- a/main.cpp
+++ b/main.cpp
@@ -67,13 +67,14 @@
setenv("ANDROID_LOG_TAGS", "*:d", 1); // Do not submit with verbose logs enabled
android::base::InitLogging(argv, &VoldLogger);
- LOG(INFO) << "Vold 3.0 (the awakening) firing up";
+ LOG(INFO) << "Vold 3.0-tuxera (the awakening) firing up";
ATRACE_BEGIN("main");
LOG(DEBUG) << "Detected support for:"
<< (android::vold::IsFilesystemSupported("ext4") ? " ext4" : "")
<< (android::vold::IsFilesystemSupported("f2fs") ? " f2fs" : "")
+ << (android::vold::IsFilesystemSupported("texfat") ? " texfat" : "")
<< (android::vold::IsFilesystemSupported("vfat") ? " vfat" : "");
VolumeManager* vm;
diff --git a/model/Disk.cpp b/model/Disk.cpp
index 4df4e9d..d2df45a 100644
--- a/model/Disk.cpp
+++ b/model/Disk.cpp
@@ -23,6 +23,8 @@
#include "VolumeEncryption.h"
#include "VolumeManager.h"
+#include "fs/Exfat.h"
+
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
@@ -384,6 +386,8 @@
}
switch (type) {
+ case 0x01: // FAT12 (in first 32 MB of disk)
+ case 0x04: // FAT16 (in first 32 MB of disk)
case 0x06: // FAT16
case 0x07: // HPFS/NTFS/exFAT
case 0x0b: // W95 FAT32 (LBA)
@@ -458,6 +462,12 @@
LOG(WARNING) << "Failed to zap; status " << res;
}
+ if (exfat::CheckSize(mDevPath, false) == 1) {
+ /* Should format filesystem as exFAT, so we must partition the device
+ * according to SDXC rules. */
+ return exfat::PartitionDisk(mDevPath);
+ }
+
// Now let's build the new MBR table. We heavily rely on sgdisk to
// force optimal alignment on the created partitions.
cmd.clear();
diff --git a/model/PublicVolume.cpp b/model/PublicVolume.cpp
index bf54c95..dbb7628 100644
--- a/model/PublicVolume.cpp
+++ b/model/PublicVolume.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2010-2021 Tuxera Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +21,7 @@
#include "Utils.h"
#include "VolumeManager.h"
#include "fs/Exfat.h"
+#include "fs/Filesystems.h"
#include "fs/Vfat.h"
#include <android-base/logging.h>
@@ -57,6 +59,13 @@
PublicVolume::~PublicVolume() {}
status_t PublicVolume::readMetadata() {
+ if(filesystems::Detect(mDevPath, &mFsTypeId)) {
+ /* Error while detecting filesystem. This should not happen, but in case
+ * it does, pretend that it is recognized as FAT since this most
+ * accurately reflects the behaviour of a stock AOSP vold. */
+ mFsTypeId = FSTYPE_FAT;
+ }
+
status_t res = ReadMetadataUntrusted(mDevPath, &mFsType, &mFsUuid, &mFsLabel);
auto listener = getListener();
@@ -100,21 +109,16 @@
bool isVisible = isVisibleForWrite();
readMetadata();
- if (mFsType == "vfat" && vfat::IsSupported()) {
- if (vfat::Check(mDevPath)) {
- LOG(ERROR) << getId() << " failed filesystem check";
- return -EIO;
- }
- } else if (mFsType == "exfat" && exfat::IsSupported()) {
- if (exfat::Check(mDevPath)) {
- LOG(ERROR) << getId() << " failed filesystem check";
- return -EIO;
- }
- } else {
+ if (!filesystems::IsSupported(mFsTypeId)) {
LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;
return -EIO;
}
+ if (filesystems::Check(mFsTypeId, mDevPath)) {
+ LOG(ERROR) << getId() << " failed filesystem check";
+ return -EIO;
+ }
+
// Use UUID as stable name, if available
std::string stableName = getId();
if (!mFsUuid.empty()) {
@@ -140,18 +144,11 @@
return -errno;
}
- if (mFsType == "vfat") {
- if (vfat::Mount(mDevPath, mRawPath, false, false, false, AID_ROOT,
- (isVisible ? AID_MEDIA_RW : AID_EXTERNAL_STORAGE), 0007, true)) {
- PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
- return -EIO;
- }
- } else if (mFsType == "exfat") {
- if (exfat::Mount(mDevPath, mRawPath, AID_ROOT,
- (isVisible ? AID_MEDIA_RW : AID_EXTERNAL_STORAGE), 0007)) {
- PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
- return -EIO;
- }
+ if (filesystems::Mount(mFsTypeId, mDevPath, mRawPath, false, false, false,
+ AID_ROOT, (isVisible ? AID_MEDIA_RW : AID_EXTERNAL_STORAGE),
+ 0007, true)) {
+ PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
+ return -EIO;
}
if (getMountFlags() & MountFlags::kPrimary) {
@@ -308,33 +305,26 @@
}
status_t PublicVolume::doFormat(const std::string& fsType) {
- bool useVfat = vfat::IsSupported();
- bool useExfat = exfat::IsSupported();
status_t res = OK;
+ status_t (*formatFunction)(const std::string&, unsigned long) = NULL;
+
// Resolve the target filesystem type
- if (fsType == "auto" && useVfat && useExfat) {
- uint64_t size = 0;
-
- res = GetBlockDevSize(mDevPath, &size);
- if (res != OK) {
- LOG(ERROR) << "Couldn't get device size " << mDevPath;
- return res;
- }
-
- // If both vfat & exfat are supported use exfat for SDXC (>~32GiB) cards
- if (size > 32896LL * 1024 * 1024) {
- useVfat = false;
- } else {
- useExfat = false;
+ if (fsType == "auto") {
+ if (exfat::IsSupported() && exfat::CheckSize(mDevPath, true) == 1) {
+ /* Should format filesystem as exFAT, so we must format the
+ * partition according to SDXC rules. */
+ formatFunction = &exfat::Format;
+ } else if(vfat::IsSupported()) {
+ formatFunction = &vfat::Format;
}
} else if (fsType == "vfat") {
- useExfat = false;
+ formatFunction = vfat::IsSupported() ? &vfat::Format : NULL;
} else if (fsType == "exfat") {
- useVfat = false;
+ formatFunction = exfat::IsSupported() ? &exfat::Format : NULL;
}
- if (!useVfat && !useExfat) {
+ if (!formatFunction) {
LOG(ERROR) << "Unsupported filesystem " << fsType;
return -EINVAL;
}
@@ -343,11 +333,7 @@
LOG(WARNING) << getId() << " failed to wipe";
}
- if (useVfat) {
- res = vfat::Format(mDevPath, 0);
- } else if (useExfat) {
- res = exfat::Format(mDevPath);
- }
+ res = formatFunction(mDevPath, 0);
if (res != OK) {
LOG(ERROR) << getId() << " failed to format";
diff --git a/model/PublicVolume.h b/model/PublicVolume.h
index 3156b53..0f4a795 100644
--- a/model/PublicVolume.h
+++ b/model/PublicVolume.h
@@ -18,6 +18,7 @@
#define ANDROID_VOLD_PUBLIC_VOLUME_H
#include "VolumeBase.h"
+#include "fs/Filesystems.h"
#include <cutils/multiuser.h>
@@ -71,6 +72,8 @@
/* Whether to use sdcardfs for this volume */
bool mUseSdcardFs;
+ /* Type ID of filesystem (see Filesystems.h). */
+ FSType mFsTypeId = FSTYPE_UNRECOGNIZED;
/* Filesystem type */
std::string mFsType;
/* Filesystem UUID */