天天看点

CyanogenMod 10 修改 Vold 使 Android 自动挂载 NTFS 和 exFAT 格式的 SD 卡一、修改 ntfs-3g 代码二、修改 exfat 代码三、提取文件,编写 Android.mk 文件四、修改 vold 代码五、修改 build/target/product/base.mk六、编译

Android 不支持 NTFS 和 exFAT 格式的文件系统,但是 Linux 已经有相应的开源代码了,因此只需将其移植到 Android 上即可。

这里需要下载两份代码,分别是 ntfs-3g 和 exfat。下载地址 ntfs-3g exfat 。

由于 Android 并没有使用完整的 glibc 库,因此需要对源代码进行修改以使代码能被顺利地编译。

代码与编译好的文件都可以在 CSDN 资源里下载:

        CyanogenMod10 ntfs 与 exfat 自动挂载支持

一、修改 ntfs-3g 代码

       关于 ntfs-3g 代码的修改,可以参考 CSDN 上的这篇文章:ntfs-3g移植到android4.0.3方法总结。这里就不在重复叙述了。

二、修改 exfat 代码

        exfat 也是使用了 fuse,但代码量很小。需要修改的地方不多:

        1、fuse/main.c 文件

                大约位于 264 行的 fuse_exfat_statfs 函数,将参数中的 statvfs 改为 statfs,并将其代码中的 sfs->f_namemax 改为 sfs->f_namelen

                将                 

sfs->f_favail = sfs->f_bfree >> ef.sb->spc_bits;
	sfs->f_ffree = sfs->f_bavail;
           

                 改为

sfs->f_ffree = sfs->f_bfree >> ef.sb->spc_bits;
           

        2、libexfat/io.c 

                大约位于 255 行的 exfat_seek 函数,将里面的 lseek 函数改为 lseek64;

                大约位于 283 行的 exfat_pread 函数,将里面的 pread 函数改为 pread64;

                大约位于 295 行的 exfat_pwrite 函数,将里面的 pwrite 函数改为 pwrite64。

        3、将所有代码文件中的 off_t 类型全部换为 off64_t 类型

                Android 的编译器编译出来的 off_t 类型是4字节的,只有改为 off64_t 才是8字节的。不改的话无法挂载超过 4G 的 SD 卡或 U 盘。

三、提取文件,编写 Android.mk 文件

        这里我将所有的项目都放在了和 vold 同级的 system 目录下

        1、libfuse

                由于 ntfs-3g 和 exfat 都使用了 fuse,因此他们可以共享这个库。

                在 system 目录新建 libfuse 文件夹,并建立子文件夹 src 和 include。将 ntfs-3g 中 libfuse-lite 中的所有文件复制到 libfuse/src 目录中,将 ntfs-3g 中 include/fuse-lite 中的所有文件复制到 libfuse/include 目录中,将 ntfs-3g 中的 config.h 复制到 libfuse 目录。

                修改 libfuse/src/helper.c,在最后添加如下代码(exfat 需要):

int fuse_daemonize(int foreground)
{
	int res;

	if (!foreground) {
		res = daemon(0, 0);
		if (res == -1) {
			perror("fuse: failed to daemonize program\n");
			return -1;
		}
	}
	return 0;
}
           

                在 libfuse 目录编写 Android.mk:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
	src/fuse.c \
	src/fusermount.c \
	src/fuse_kern_chan.c \
	src/fuse_loop.c \
	src/fuse_lowlevel.c \
	src/fuse_opt.c \
	src/fuse_session.c \
	src/fuse_signals.c \
	src/helper.c \
	src/mount.c \
	src/mount_util.c

LOCAL_C_INCLUDES:= $(LOCAL_PATH)/include
LOCAL_CFLAGS:= -O2 -g -W -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H
LOCAL_MODULE:= libfuse
LOCAL_MODULE_TAGS:=eng
LOCAL_SYSTEM_SHARED_LIBRARIES:= libc libcutils

include $(BUILD_STATIC_LIBRARY)
           

        2、ntfs-3g

                则里只提取挂载 NTFS 分区的文件。

                在 system 目录新建 ntfs-3g 文件夹,并建立子文件夹 include、libntfs-3g 和 src。将 ntfs-3g 中 include/ntfs-3g 中的所有文件复制到 ntfs-3g/include 目录中,将 ntfs-3g 中 libntfs-3g 中的所有文件复制到 ntfs-3g/libntfs-3g 目录中,将 ntfs-3g 中 src 目录中的 ntfs-3g.c ntfs-3g_common.c ntfs-3g_common.h 复制到 ntfs-3g/src 目录中,将 ntfs-3g 中的 config.h 复制到 ntfs-3g 目录。

                在 ntfs-3g 目录中编写 Android.mk:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
	libntfs-3g/acls.c libntfs-3g/attrib.c libntfs-3g/attrlist.c \
	libntfs-3g/bitmap.c libntfs-3g/bootsect.c libntfs-3g/cache.c \
	libntfs-3g/collate.c libntfs-3g/compat.c libntfs-3g/compress.c \
	libntfs-3g/debug.c libntfs-3g/device.c libntfs-3g/dir.c \
	libntfs-3g/efs.c libntfs-3g/index.c libntfs-3g/inode.c \
	libntfs-3g/lcnalloc.c libntfs-3g/logfile.c libntfs-3g/logging.c \
	libntfs-3g/mft.c libntfs-3g/misc.c libntfs-3g/mst.c \
	libntfs-3g/object_id.c libntfs-3g/realpath.c libntfs-3g/reparse.c \
	libntfs-3g/runlist.c libntfs-3g/security.c libntfs-3g/unistr.c \
	libntfs-3g/unix_io.c libntfs-3g/volume.c

LOCAL_C_INCLUDES:= $(LOCAL_PATH)/include system/libfuse/include
LOCAL_CFLAGS:= -O2 -g -W -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H
LOCAL_MODULE:= libntfs-3g
LOCAL_MODULE_TAGS:=eng
LOCAL_SYSTEM_SHARED_LIBRARIES:= libc libcutils

include $(BUILD_STATIC_LIBRARY)

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
	src/ntfs-3g.c src/ntfs-3g_common.c

LOCAL_C_INCLUDES:= $(LOCAL_PATH)/include $(LOCAL_PATH)/src system/libfuse/include
LOCAL_CFLAGS:= -O2 -g -W -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H
LOCAL_MODULE:= ntfs-3g
LOCAL_MODULE_TAGS:=eng
LOCAL_SYSTEM_SHARED_LIBRARIES:= libc
LOCAL_STATIC_LIBRARIES:= libfuse libntfs-3g

include $(BUILD_EXECUTABLE)
           

        3、ntfsfix

                这个是用来在挂载 NTFS 分区前检查错误的。

                在 system 目录下建立 ntfsfix 目录,并建立子目录 src。将 ntfs-3g 中 ntfsprogs 中的 ntfsfix.c sd.c sd.h utils.c utils.h 复制到 ntfsfix/src 目录,将 ntfs-3g 中的 config.h 复制到 ntfsfix 目录。

                在 ntfsfix 目录下建立 Android.mk:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
	src/ntfsfix.c \
	src/utils.c \
	src/sd.c

LOCAL_C_INCLUDES:= $(LOCAL_PATH)/src system/ntfs-3g/include
LOCAL_CFLAGS:= -O2 -g -W -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H
LOCAL_MODULE:= ntfsfix
LOCAL_MODULE_TAGS:=eng
LOCAL_SYSTEM_SHARED_LIBRARIES:= libc
LOCAL_STATIC_LIBRARIES:= libntfs-3g libfuse

include $(BUILD_EXECUTABLE)
           

        4、mkntfs

                这个是用来格式化分区为 NTFS 格式的。

                在 system 目录下建立 mkntfs 目录,并建立子目录 src。将 ntfs-3g 中 ntfsprogs 中的 attrdef.c attrdef.h boot.c boot.h mkntfs.c sd.c sd.h utils.c utils.h 复制到 mkntfs/src 目录,将 ntfs-3g 中的 config.h 复制到 mkntfs 目录。

                在 mkntfs 目录下建立 Android.mk:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
	src/attrdef.c \
	src/boot.c \
	src/utils.c \
	src/mkntfs.c \
	src/sd.c

LOCAL_C_INCLUDES:= $(LOCAL_PATH)/src system/ntfs-3g/include
LOCAL_CFLAGS:= -O2 -g -W -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H
LOCAL_MODULE:= mkntfs
LOCAL_MODULE_TAGS:=eng
LOCAL_SYSTEM_SHARED_LIBRARIES:= libc
LOCAL_STATIC_LIBRARIES:= libntfs-3g libfuse

include $(BUILD_EXECUTABLE)
           

        5、exfat

                这里只提取挂载 exFAT 分区所需的文件。

                在 system 目录下建立 exfat 目录。将 exfat 源代码中的 fuse 和 libexfat 文件夹复制到 exfat 目录中。

                建立 Android.mk:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
	libexfat/cluster.c libexfat/io.c libexfat/log.c \
	libexfat/lookup.c libexfat/mount.c libexfat/node.c \
	libexfat/time.c libexfat/utf.c libexfat/utils.c

LOCAL_C_INCLUDES:= $(LOCAL_PATH)/libexfat system/libfuse/include
LOCAL_CFLAGS:= -O2 -g -W -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -UUSE_UBLIO
LOCAL_MODULE:= libexfat
LOCAL_MODULE_TAGS:=eng
LOCAL_SYSTEM_SHARED_LIBRARIES:= libc libcutils

include $(BUILD_STATIC_LIBRARY)

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
	fuse/main.c

LOCAL_C_INCLUDES:= $(LOCAL_PATH)/libexfat system/libfuse/include
LOCAL_CFLAGS:= -O2 -g -W -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DUSE_UBLIO
LOCAL_MODULE:= exfat
LOCAL_MODULE_TAGS:=eng
LOCAL_SYSTEM_SHARED_LIBRARIES:= libc
LOCAL_STATIC_LIBRARIES:= libfuse libexfat

include $(BUILD_EXECUTABLE)
           

        6、exfatfsck

                这个是用来在挂载 exFAT 分区前检查错误的。

                在 system 目录建立 exfatfsck 目录,并将 exfat 源代码中 fsck/main.c 复制到 exfatfsck 目录中。

                建立 Android.mk(这里只列出关键的两行):

LOCAL_SRC_FILES:= \
	main.c

LOCAL_C_INCLUDES:= $(LOCAL_PATH)/src system/exfat/libexfat
           

        7、mkexfatfs

                这个是用来格式化分区为 exFAT 格式的。

                在 system 目录下建立 mkexfatfs 目录,并建立子目录 src。将 exfat 源代码中 mkfs 目录下的所有文件复制到 mkexfatfs/src 目录中。

                建立 Android.mk(关键部分):

LOCAL_SRC_FILES:= \
	src/cbm.c \
	src/fat.c \
	src/main.c \
	src/mkexfat.c \
	src/rootdir.c \
	src/uct.c \
	src/uctc.c \
	src/vbr.c

LOCAL_C_INCLUDES:= $(LOCAL_PATH)/src system/exfat/libexfat
           

四、修改 vold 代码

        CyanogenMod 的 vold 代码包含了 Ntfs.cpp 和 Ntfs.h,但是需要修改

        1、修改 Ntfs.cpp

                添加全局变量:

static char MOUNT_NTFS_PATH[] = "/system/bin/ntfs-3g";
static char MKNTFS_PATH[] = "/system/bin/mkntfs";
static char NTFSFIX_PATH[] = "/system/bin/ntfsfix";
           

                check 函数:

int Ntfs::check(const char *fsPath) {
    bool rw = true;
    if (access(NTFSFIX_PATH, X_OK)) {
        SLOGW("Skipping fs checks\n");
        return 0;
    }

    int rc = -1;
    do {
        const char *args[3];
        args[0] = NTFSFIX_PATH;
        args[1] = fsPath;
        args[2] = NULL;

        rc = logwrap(3, args, 1);

        switch(rc) {
        case 0:
            SLOGI("NTFS Filesystem check completed OK.\n");
            return 0;
        case 1:
            SLOGI("NTFS Filesystem check failed.\n");
            return 0;
        default:
            SLOGE("NTFS Filesystem check failed (unknown exit code %d).\n", rc);
            errno = EIO;
            return -1;
        }
    } while (0);

    return 0;
}
           

                doMount 函数:

int Ntfs::doMount(const char *fsPath, const char *mountPoint,
                 bool ro, bool remount, bool executable,
                 int ownerUid, int ownerGid, int permMask, bool createLost) {
    int rc;
    unsigned long flags;
    char mountData[255];
    char options[255] = {};
    const char *args[6];

    flags = MS_NODEV | MS_NOSUID | MS_DIRSYNC;

    flags |= (executable ? 0 : MS_NOEXEC);
    flags |= (ro ? MS_RDONLY : 0);
    flags |= (remount ? MS_REMOUNT : 0);

    // Testing/security, mount ro up to now
    flags |= MS_RDONLY;
    
    /*
     * 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,
            "uid=%d,gid=%d,fmask=%o,dmask=%o",
            ownerUid, ownerGid, permMask, permMask);

    if (!remount) {
        SLOGI("Trying to use ntfs-3g program to mount %s", fsPath);            
            
        if (ro)
            snprintf(options, sizeof(options), "ro,%s", mountData);
        else
            snprintf(options, sizeof(options), "%s", mountData);

        args[0] = MOUNT_NTFS_PATH;
        args[1] = "-o";
        args[2] = options;
        args[3] = fsPath;
        args[4] = mountPoint;
        args[5] = NULL;

        rc = logwrap(6, args, 1);

        if (rc == 0) {
            SLOGI("ntfs-3g executed successfully.");
        } else {
            SLOGE("Failed to execute ntfs-3g.");
        }
    } else {
        rc = mount(fsPath, mountPoint, "fuseblk", flags, mountData);
    }

    if (rc && errno == EROFS) {
        SLOGE("%s appears to be a read only filesystem - retrying mount RO", fsPath);
        flags |= MS_RDONLY;
        if (!remount) {
            SLOGI("Trying to use ntfs-3g program to mount %s as read-only", fsPath);

            snprintf(options, sizeof(options), "ro,%s", mountData);

            args[0] = MOUNT_NTFS_PATH;
            args[1] = "-o";
            args[2] = options;
            args[3] = fsPath;
            args[4] = mountPoint;
            args[5] = NULL;

            rc = logwrap(6, args, 1);

            if (rc == 0) {
                SLOGI("ntfs-3g executed successfully for read-only.");
            } else {
                SLOGE("Failed to execute ntfs-3g for read-only.");
            }
        } else {
            rc = mount(fsPath, mountPoint, "fuseblk", flags, mountData);
        }
    }

    return rc;
}
           

                format 函数:

int Ntfs::format(const char *fsPath, unsigned int numSectors) {
    const char *args[5];
    char strNumOfSectors[16] = {};   
    int rc; 

    args[0] = MKNTFS_PATH;
    args[1] = "-f";
    args[2] = fsPath;

    if (numSectors) {
        snprintf(strNumOfSectors, sizeof(strNumOfSectors), "%u", numSectors);
        args[3] = strNumOfSectors;
        args[4] = NULL;
        rc = logwrap(5, args, 1);
    } else {
        args[3] = NULL;
        rc = logwrap(4, args, 1);
    }

    if (rc == 0) {
        SLOGI("Filesystem formatted OK");
        return 0;
    } else {
        SLOGE("Format failed (unknown exit code %d)", rc);
        errno = EIO;
        return -1;
    }
    return 0;
}
           

        2、添加 ExFat.h 和 ExFat.cpp

                这里请使用 Ntfs.h 和 Ntfs.cpp 作为模板。

                关键代码:

static char MOUNT_EXFAT_PATH[] = "/system/bin/exfat";
static char MKFS_EXFAT_PATH[] = "/system/bin/mkexfatfs";
static char FSCK_EXFAT_PATH[] = "/system/bin/exfatfsck";

int ExFat::check(const char *fsPath) {
    bool rw = true;
    if (access(FSCK_EXFAT_PATH, X_OK)) {
        SLOGW("Skipping fs checks\n");
        return 0;
    }

    int rc = -1;
    do {
        const char *args[3];
        args[0] = FSCK_EXFAT_PATH;
        args[1] = fsPath;
        args[2] = NULL;

        rc = logwrap(3, args, 1);

        switch(rc) {
        case 0:
            SLOGI("exFAT Filesystem check completed OK.\n");
            return 0;
        case 1:
            SLOGI("exFAT Filesystem check failed.\n");
            return 0;
        default:
            SLOGE("exFAT Filesystem check failed (unknown exit code %d).\n", rc);
            errno = EIO;
            return -1;
        }
    } while (0);

    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;
    unsigned long flags;
    char mountData[255];
    const char *args[4];

    flags = MS_NODEV | MS_NOSUID | MS_DIRSYNC;

    flags |= (executable ? 0 : MS_NOEXEC);
    flags |= (ro ? MS_RDONLY : 0);
    flags |= (remount ? MS_REMOUNT : 0);

    // Testing/security, mount ro up to now
    flags |= MS_RDONLY;
    
    sprintf(mountData,
            "uid=%d,gid=%d,fmask=%o,dmask=%o",
            ownerUid, ownerGid, permMask, permMask);

    if (!remount) {
        SLOGI("Trying to use exfat program to mount %s", fsPath);            
            
        args[0] = MOUNT_EXFAT_PATH;
        args[1] = fsPath;
        args[2] = mountPoint;
        args[3] = NULL;

        rc = logwrap(4, args, 1);

        if (rc == 0) {
            SLOGI("exfat executed successfully.");
        } else {
            SLOGE("Failed to execute exfat.");
        }
    } else {
        rc = mount(fsPath, mountPoint, "fuseblk", flags, mountData);
    }

    return rc;
}

int ExFat::format(const char *fsPath, unsigned int numSectors) {
    const char *args[3];
    char strNumOfSectors[16] = {};   
    int rc; 

    args[0] = MKFS_EXFAT_PATH;
    args[1] = fsPath;
    args[2] = NULL;

    rc = logwrap(3, args, 1);

    if (rc == 0) {
        SLOGI("Filesystem formatted OK");
        return 0;
    } else {
        SLOGE("Format failed (unknown exit code %d)", rc);
        errno = EIO;
        return -1;
    }
    return 0;
}
           

        3、修改 Volume.cpp

                添加 #include "Ntfs.h"

                找到 Volume::mountVol() 函数

                找到 else if (strcmp(fstype, "ntfs") == 0) 部分,在后面添加以下代码:

if (Ntfs::check(devicePath)) {
                    errno = EIO;
                    /* Badness - abort the mount */
                    SLOGE("%s failed FS checks (%s)", devicePath, strerror(errno));
                    setState(Volume::State_Idle);
                    free(fstype);
                    return -1;
                }
           

                找到和 if (fstype != NULL)  对应的 else 块,在后面添加以下代码:

//Check if the filesystem is exFAT
            FILE *volf = fopen(devicePath, "rb");
            int bytesRead = 0;
            if (volf) {
                char fschar[5];
                //Read the signature in the PBR
                fseek(volf, 3, SEEK_SET);
                bytesRead = fread(fschar, 1, 5, volf);
                fclose(volf);

                if (bytesRead == 5 && strcmp(fschar, "EXFAT") == 0) {

                    if (ExFat::check(devicePath)) {
		        errno = EIO;
		        /* Badness - abort the mount */
		        SLOGE("%s failed FS checks (%s)", devicePath, strerror(errno));
		        setState(Volume::State_Idle);
		        free(fstype);
		        return -1;
		    }

		    if (ExFat::doMount(devicePath, "/mnt/secure/staging", false, false, false,
		            AID_SYSTEM, gid, 0702, true)) {
		        SLOGE("%s failed to mount via exFAT (%s)\n", devicePath, strerror(errno));
                        continue;
		    } else {
                        goto mountSuccess;
                    }
                }
            }
           

                然后转到 if (fstype != NULL) 的 else 的末尾,在语句块之后,SLOGI("Device %s, target %s mounted @ /mnt/secure/staging", devicePath, getMountpoint()); 之前添加 mountSuccess: 跳转标签。

        4、修改 vold 的 Android.mk

                天际 common_src_files += ExFat.c

五、修改 build/target/product/base.mk

        添加

PRODUCT_PACKAGES += \
    libfuse \
    ntfs-3g \
    exfat \
    mkntfs \
    ntfsfix \
    mkexfatfs \
    exfatfsck
           

六、编译

        如果不出意外的话,编译之后就可以直接使用了。