简介

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的模式,由于时间原因没有去实践了,等以后有机会再去尝试下。感兴趣的同学可以自行去研究下看可不可行。

风骚实战

第一步:下载exFAT相关代码并将exFAT源码导入external目录下并编译通过

我这里是使用别人开源的在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=26LOCAL_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.cLOCAL_C_INCLUDES := \        $(LOCAL_PATH)/includeLOCAL_SYSTEM_SHARED_LIBRARIES:= libc libcutilsLOCAL_LDFLAGS += -ldlLOCAL_MODULE := libfuse-exfatLOCAL_MODULE_TAGS := optionalinclude $(BUILD_STATIC_LIBRARY)

修改 external/exfat/exfat/Android.mk

EXFAT_ROOT := $(call my-dir)LOCAL_PATH := $(call my-dir)LINKS := fsck.exfat mkfs.exfatinclude $(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/includeLOCAL_MODULE := mount.exfatLOCAL_SRC_FILES := main.cLOCAL_STATIC_LIBRARIES += libexfat_mount libexfat_fsck libexfat_mkfs libexfat_dump libexfat_labelLOCAL_STATIC_LIBRARIES += libexfat libfuse-exfatLOCAL_LDFLAGS += -ldlinclude $(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.mkinclude $(EXFAT_ROOT)/fuse/Android.mkinclude $(EXFAT_ROOT)/mkfs/Android.mkinclude $(EXFAT_ROOT)/fsck/Android.mkinclude $(EXFAT_ROOT)/dump/Android.mkinclude $(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盘自动挂载。

第二步:实现拔插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文件格式,如果是就挂载它。

第三步:将exfat加入系统中编译

在device/xxx/yyy.mk中加入exfat模块。这样系统编译的时候,会把exfat相关的文件编译进系统。
注:这里的xxx/yyy请根据自己的项目来决定加在哪个mk文件中。

# ntfs-3g binaryPRODUCT_PACKAGES += \        ntfs-3g         \        ntfsfix# exfat binaryPRODUCT_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

更多相关文章

  1. Android中的资源与国际化
  2. 【Android-Error】Android(安卓)4.4.4 java.lang.NoClassDefFoun
  3. Android系统做了哪些优化?
  4. android R 文件生成不了
  5. 将写好的软件安装到Android手机
  6. Android(安卓)Google Map(create md5, create google map key)
  7. Android(安卓)Studio 使用AIDL
  8. Android(安卓)Studio下jni应用
  9. NPM 和webpack 的基础使用

随机推荐

  1. android 多媒体部分学习笔记九----数字音
  2. Android(安卓)XML解析之PULL及单元测试
  3. 获取 Android(安卓)设备 屏幕,内存,存储大
  4. android 读取,写入图片到sd卡源码
  5. Android(安卓)实现手绘功能
  6. LinearLayout(线性布局)
  7. android调用系统资源裁剪图片
  8. Android扫描和生成二维码
  9. Android中使用Bezier曲线
  10. android截屏并通过邮件发送