欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 4.4.2 exfat 移植

程序员文章站 2022-08-11 09:53:45
android 4.4.2 exfat 移植 android原生的平台不支持ntfs和exfat格式的文件,但是linux已经有相应的开源代码,因此只需找到相应的将其移植到and...

android 4.4.2 exfat 移植

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=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盘自动挂载。

第二步:实现拔插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