Android 4.4.2插入exFAT格式U盘识别及加载的解决方案

简介

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平台上编译通过还需要修改若干文件,这里我就取巧了,直接用别人整理好的库文件下载。但是这个源码已经修正了编译时的错误,直接可以开始配置Vold挂载的源码修改了。

第二步:实现拔插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 binary
PRODUCT_PACKAGES += \
        ntfs-3g         \
        ntfsfix

# exfat binary
PRODUCT_PACKAGES += \
        mount.exfat

你可能感兴趣的:(Android)