Android原生的平台不支持NTFS和exFAT格式的文件系统,但是Linux已经有相应的开源代码,因此只需找到相应的源码将其移植到Android上即可。
我目前使用的系统是Android 4.4.2的,系统里已经集成了对NTFS文件系统的支持。所以我现在要做的就是将exFAT格式的文件系统移植过来。
exFAT(Extended File Allocation Table),又名FAT64,是一种能特别适合于闪存的文件系统,可支持单个文件超过4GB的大小。
FUSE 用户空间文件系统(Filesystem in Userppace)是操作系统中的概念,指完全在用户态实现的文件系统。目前Linux通过内核模块对此进行支持。
上面是基于FUSE做的exFAT文件系统的支持,还有一种是exfat-nofuse的模式,由于时间原因没有去实践了,等以后有机会再去尝试下。感兴趣的同学可以自行去研究下看可不可行。
我这里是使用别人开源的在Android平台上的源码,exfat有官方源码,但是要在Android平台上编译通过还需要修改若干文件,这里我就取巧了,直接用别人整理好的。代码下载,我现在下载的版本。但是这个源码下载下来还是有编译错误。我做的调整如下:
增加 external/exfat/Android.mk
LOCAL_PATH := $(call my-dir)
include \
$(LOCAL_PATH)/fuse/Android.mk \
$(LOCAL_PATH)/exfat/Android.mk \
修改 external/exfat/fuse/Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CFLAGS := -O2 -g -W -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H -DFUSE_USE_VERSION=26
LOCAL_SRC_FILES := \
buffer.c \
cuse_lowlevel.c \
fuse.c \
fuse_kern_chan.c \
fuse_loop.c \
fuse_loop_mt.c \
fuse_lowlevel.c \
fuse_mt.c fuse_opt.c \
fuse_session.c \
fuse_signals.c \
helper.c \
mount.c \
mount_util.c \
ulockmgr.c
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include
LOCAL_SYSTEM_SHARED_LIBRARIES:= libc libcutils
LOCAL_LDFLAGS += -ldl
LOCAL_MODULE := libfuse-exfat
LOCAL_MODULE_TAGS := optional
include $(BUILD_STATIC_LIBRARY)
修改 external/exfat/exfat/Android.mk
EXFAT_ROOT := $(call my-dir)
LOCAL_PATH := $(call my-dir)
LINKS := fsck.exfat mkfs.exfat
include $(CLEAR_VARS)
EXFAT_CFLAGS := -O2 -g -W -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H \
-Wall -O2 -std=c99 \
-D__GLIBC__ \
-D_FILE_OFFSET_BITS=64 \
-DALWAYS_USE_SYNC_OPTION=1 \
-DUSE_TRANSITIONAL_LFS=1 \
-I$(EXFAT_ROOT)/libexfat \
-I$(EXFAT_ROOT)/../fuse/include
LOCAL_MODULE := mount.exfat
LOCAL_SRC_FILES := main.c
LOCAL_STATIC_LIBRARIES += libexfat_mount libexfat_fsck libexfat_mkfs libexfat_dump libexfat_label
LOCAL_STATIC_LIBRARIES += libexfat libfuse-exfat
LOCAL_LDFLAGS += -ldl
include $(BUILD_EXECUTABLE)
SYMLINKS := $(addprefix $(TARGET_OUT)/bin/,$(LINKS))
$(SYMLINKS): EXFAT_BINARY := $(LOCAL_MODULE)
$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
@echo "Symlink: $@ -> $(EXFAT_BINARY)"
@mkdir -p $(dir $@)
@rm -rf $@
$(hide) ln -sf $(EXFAT_BINARY) $@
ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
include $(EXFAT_ROOT)/libexfat/Android.mk
include $(EXFAT_ROOT)/fuse/Android.mk
include $(EXFAT_ROOT)/mkfs/Android.mk
include $(EXFAT_ROOT)/fsck/Android.mk
include $(EXFAT_ROOT)/dump/Android.mk
include $(EXFAT_ROOT)/label/Android.mk
将上面三个Android.mk文件修改好后,就可以编译通过啦。同时它会生成fsck.exfat、mkfs.exfat和 mount.exfat三个可执行bin文件。fsck.exfat用于检测文件系统格式是否是exFAT,mkfs.exfat将文件系统格式化,mount.exfat将exFAT文件系统挂载起来。将生成的bin文件push到Android设备中,直接执行mount.exfat devicePath mountPoint,将exFat文件格式的系统挂载到Android 对应的可访问目录下。这样就可以访问exFat文件格式的U盘了。这一步是为了测试我们的exfat源码是否可以识别和挂载exFAT格式的U盘。测试通过我们就可以执行第二步,实现exFAT格式的U盘自动挂载。
要想实现U盘自动挂载,需要修改system/vold/Volume.cpp文件。因为我的系统已经实现了NTFS文件系统的自动挂载,所以我只要依葫芦画瓢就好了。
首先,在system/vold/目录下,添加Exfat.h和Exfat.cpp文件,代码如下:
Exfat.h
#ifndef _EXFAT_H
#define _EXFAT_H
#include
class Exfat {
public:
static int check(const char *fsPath);
static int doMount(const char *fsPath, const char *mountPoint,
bool ro, bool remount, bool executable,
int ownerUid, int ownerGid, int permMask,
bool createLost);
};
#endif
Exfat.cpp
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LOG_TAG "Vold"
#include
#include
#include
#include "Exfat.h"
#include "VoldUtil.h"
static char EXFAT_FIX_PATH[] = "/system/bin/fsck.exfat";
static char EXFAT_MOUNT_PATH[] = "/system/bin/mount.exfat";
int Exfat::check(const char *fsPath) {
if (access(EXFAT_FIX_PATH, X_OK)) {
SLOGW("Skipping fs checks\n");
return 0;
}
int rc = 0;
int status;
const char *args[4];
/* we first use -n to do ntfs detection */
args[0] = EXFAT_FIX_PATH;
args[1] = fsPath;
args[2] = NULL;
rc = android_fork_execvp(ARRAY_SIZE(args), (char **)args, &status, false,
true);
if (rc) {
errno = ENODATA;
return -1;
}
if (!WIFEXITED(status)) {
errno = ENODATA;
return -1;
}
status = WEXITSTATUS(status);
switch(status) {
case 0:
SLOGI("ExFat filesystem check completed OK");
break;
default:
SLOGE("Filesystem check failed (unknown exit code %d)", status);
errno = EIO;
return -1;
}
return 0;
}
int Exfat::doMount(const char *fsPath, const char *mountPoint,
bool ro, bool remount, bool executable,
int ownerUid, int ownerGid, int permMask, bool createLost) {
int rc;
int status;
char mountData[255];
const char *args[6];
/*
* Note: This is a temporary hack. If the sampling profiler is enabled,
* we make the SD card world-writable so any process can write snapshots.
*
* TODO: Remove this code once we have a drop box in system_server.
*/
char value[PROPERTY_VALUE_MAX];
property_get("persist.sampling_profiler", value, "");
if (value[0] == '1') {
SLOGW("The SD card is world-writable because the"
" 'persist.sampling_profiler' system property is set to '1'.");
permMask = 0;
}
sprintf(mountData,
"utf8,uid=%d,gid=%d,fmask=%o,dmask=%o,"
"shortname=mixed,nodev,nosuid,dirsync",
ownerUid, ownerGid, permMask, permMask);
if (!executable)
strcat(mountData, ",noexec");
if (ro)
strcat(mountData, ",ro");
if (remount)
strcat(mountData, ",remount");
SLOGD("Mounting ntfs with options:%s\n", mountData);
args[0] = EXFAT_MOUNT_PATH;
args[1] = "-o";
args[2] = mountData;
args[3] = fsPath;
args[4] = mountPoint;
args[5] = NULL;
rc = android_fork_execvp(ARRAY_SIZE(args), (char **)args, &status, false,
true);
if (rc && errno == EROFS) {
SLOGE("%s appears to be a read only filesystem - retrying mount RO", fsPath);
strcat(mountData, ",ro");
rc = android_fork_execvp(ARRAY_SIZE(args), (char **)args, &status, false,
true);
}
if (!WIFEXITED(status)) {
return rc;
}
if (rc == 0 && createLost) {
char *lost_path;
asprintf(&lost_path, "%s/LOST.DIR", mountPoint);
if (access(lost_path, F_OK)) {
/*
* Create a LOST.DIR in the root so we have somewhere to put
* lost cluster chains (fsck_msdos doesn't currently do this)
*/
if (mkdir(lost_path, 0755)) {
SLOGE("Unable to create LOST.DIR (%s)", strerror(errno));
}
}
free(lost_path);
}
return rc;
}
然后,在Android.mk中加入Exfat.cpp
common_src_files := \
...
Ntfs.cpp \
Exfat.cpp \
...
最后,在Volume.cpp中加入相应的代码
...
#include "Exfat.h"
...
int Volume::mountVol() {
...
for (i = 0; i < n; i++)
{
...
int ntfs = 0;
int exfat = 0; //add by steven
if (Fat::check(devicePath))
{
if (errno == ENODATA)
{
SLOGW("%s does not contain a FAT filesystem\n", devicePath);
if (Fat::doMount(devicePath, getMountpoint(), false, false, false, AID_MEDIA_RW, AID_MEDIA_RW, 0002, true))
{
/* try the NTFS filesystem */
if (!Ntfs::check(devicePath))
{
ntfs = 1;
SLOGI("%s contain a NTFS filesystem\n", devicePath);
goto mnt;
} /* try the EXFAT filesystem */
else if (!Exfat::check(devicePath)) { //add by steven
exfat = 1;
SLOGI("%s contain a EXFAT filesystem\n", devicePath);
goto mnt;
}
else
{
if (Extfs::doMount(devicePath, getMountpoint(), false, false, AID_MEDIA_RW, AID_MEDIA_RW, 0002))
{
SLOGE("%s failed to mount via EXTFS (%s)\n", devicePath, strerror(errno));
continue;
}
else
{
goto mounted;
}
}
}
else
{
goto mounted;
}
}
errno = EIO;
/* Badness - abort the mount */
SLOGE("%s failed FS checks (%s)", devicePath, strerror(errno));
setState(Volume::State_Idle);
return -1;
}
mnt:
errno = 0;
int gid;
if (ntfs)
{
if (Ntfs::doMount(devicePath, getMountpoint(), false, false, false, AID_MEDIA_RW, AID_MEDIA_RW, 0002, true))
{
SLOGE("%s failed to mount via NTFS (%s)\n", devicePath, strerror(errno));
continue;
}
}
else if (exfat) { //add by steven
if (Exfat::doMount(devicePath, getMountpoint(), false, false, false, AID_MEDIA_RW, AID_MEDIA_RW, 0002, true))
{
SLOGE("%s failed to mount via Exfat (%s)\n", devicePath, strerror(errno));
continue;
}
}
else if (Fat::doMount(devicePath, getMountpoint(), false, false, false, AID_MEDIA_RW, AID_MEDIA_RW, 0002, true))
{
SLOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
continue;
}
mounted:
extractMetadata(devicePath);
if (providesAsec && mountAsecExternal() != 0)
{
SLOGE("Failed to mount secure area (%s)", strerror(errno));
umount(getMountpoint());
setState(Volume::State_Idle);
return -1;
}
char service[64];
snprintf(service, 64, "fuse_%s", getLabel());
property_set("ctl.start", service);
setState(Volume::State_Mounted);
mCurrentlyMountedKdev = deviceNodes[i];
return 0;
}
SLOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());
setState(Volume::State_Idle);
return -1;
}
代码中exfat相关的都是添加的部分,主要做的事情就是检测设备节点是否是exFAT文件格式,如果是就挂载它。
在device/xxx/yyy.mk中加入exfat模块。这样系统编译的时候,会把exfat相关的文件编译进系统。
注:这里的xxx/yyy请根据自己的项目来决定加在哪个mk文件中。
# ntfs-3g binary
PRODUCT_PACKAGES += \
ntfs-3g \
ntfsfix
# exfat binary
PRODUCT_PACKAGES += \
mount.exfat
移植exFAT到Android4.2.2
CyanogenMod 10 修改 Vold 使 Android 自动挂载 NTFS 和 exFAT 格式的 SD 卡
exfat
exfat android分支
ntfs-3g
Android-fs
android_external_ntfs-3g_4.4
android_external_exfat