F2FS保留块特性分析及如何在Android上使用

文章详细探讨了Android中f2fs文件系统启用的原因,特别是针对提高性能和寿命的优化。它解释了如何通过resgid和resuid来预留空间,以及如何通过内核函数控制访问权限。当data分区填满时,预留空间可以保护重要进程。文章还提到了在3.18内核版本中ext4的resgid特性,以及如何将该特性移植到f2fs。同时,文章指出只有特定gid(如1065)的进程可以使用预留空间,这在解决普通用户填充空间问题时很重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android Go data分区启用了f2fs文件系统,从介绍看是为了提高life time和4K文件读写性能,这个暂不关心,三星init,看kernel提交记录后面主要是huawei也参与进来,据说是重金聘用,多少米?

前段时间项目在Go上遇到一个问题:data填满100%一直起不来(kernel: 3.18)。不过可以使用保留空间加一层保护, 重要系统进程可以使用保留区,其他不可用, rt?,ext4这个特性叫resgid,3.18 f2fs还不支持,我从kernel 4.9移下来,基于这份代码分析。

先看下保留空间相关结构:

struct f2fs_sb_info {
...
    block_t reserved_blocks;        /* configurable reserved blocks */
    block_t current_reserved_blocks;    /* current reserved blocks */
    block_t root_reserved_blocks;        /* root reserved blocks */
    kuid_t s_resuid;            /* reserved blocks for uid */
    kgid_t s_resgid;            /* reserved blocks for gid */

C

Copy

其实reserved_blockscurrent_reserved_blocks是一个reserved feature,主要目的应该是提高性能,在sysfs下可配置。而root_reserved_blocksresuid,resgid才是我们现在这个需求,用man来解释:

man mount:

resgid=n and resuid=n
The ext2 filesystem reserves a certain percentage of the available space (by default 5%, see mke2fs(8) and tune2fs(8)). These options determine who can use the reserved blocks.
(Roughly: whoever has the specified uid, or belongs to the specified group.)

man tune2fs:

-r reserved-blocks-count
Set the number of reserved filesystem blocks.

-g group
Set the group which can use the reserved filesystem blocks. The group parameter can be a numerical gid or a group name. If a group name is given, it is converted to a numerical gid before it is stored in the superblock.

ok, 关键函数:

static inline bool __allow_reserved_blocks(struct f2fs_sb_info *sbi)
{
    if (!test_opt(sbi, RESERVE_ROOT))
        return false;
    if (capable(CAP_SYS_RESOURCE))
        return true;
    if (uid_eq(sbi->s_resuid, current_fsuid()))
        return true;
    if (!gid_eq(sbi->s_resgid, GLOBAL_ROOT_GID) &&
                    in_group_p(sbi->s_resgid))
        return true;
    return false;
}

C

Copy

首先检查mount是否有RESERVE_BOOT选项,如果没设置就不让用。

#define set_opt(sbi, option)    ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option)
#define test_opt(sbi, option)    ((sbi)->mount_opt.opt & F2FS_MOUNT_##option)

C

Copy

哪里set_opt了这个了, 在parse_options里:

        case Opt_reserve_root:
            if (args->from && match_int(args, &arg))
                return -EINVAL;
            if (test_opt(sbi, RESERVE_ROOT)) {
                f2fs_msg(sb, KERN_INFO,
                    "Preserve previous reserve_root=%u",
                    sbi->root_reserved_blocks);
            } else { //tj: 走这里
                sbi->root_reserved_blocks = arg;
                set_opt(sbi, RESERVE_ROOT);
            }

C

Copy

mount会解析这个选项:

static struct dentry *f2fs_mount(struct file_system_type *fs_type, int flags,
            const char *dev_name, void *data)
{
    return mount_bdev(fs_type, flags, dev_name, data, f2fs_fill_super);
}

C

Copy

f2fs_fill_super会call parse_options:

static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
{
...
    /* parse mount options */
    options = kstrdup((const char *)data, GFP_KERNEL);
    if (data && !options) {
        err = -ENOMEM;
        goto free_sb_buf;
    }

    err = parse_options(sb, options);
    if (err)
        goto free_options;

C

Copy

ok, 看下s_resgid allow:

    if (!gid_eq(sbi->s_resgid, GLOBAL_ROOT_GID) &&
                    in_group_p(sbi->s_resgid))
        return true;

C

Copy

#define KGIDT_INIT(value) (kgid_t){ value }
#define GLOBAL_ROOT_GID KGIDT_INIT(0)

C

Copy

resgid=0的不让用保留了,in_group_p:

/*
 * Check whether we're fsgid/egid or in the supplemental group..
 */
int in_group_p(kgid_t grp)
{
        const struct cred *cred = current_cred();
        int retval = 1;

        if (!gid_eq(grp, cred->fsgid))
                retval = groups_search(cred->group_info, grp);
        return retval;
}

C

Copy

应该就是这个grp有没有加到系统里,可见mount flag要加了reserve_root才能激活。

另外,保留块有大小限制:

static inline void limit_reserve_root(struct f2fs_sb_info *sbi)
{
    block_t limit = (sbi->user_block_count << 1) / 1000;

    /* limit is 0.2% */
    if (test_opt(sbi, RESERVE_ROOT) && sbi->root_reserved_blocks > limit) {
        sbi->root_reserved_blocks = limit;
        f2fs_msg(sbi->sb, KERN_INFO,
            "Reduce reserved blocks for root = %u",
                sbi->root_reserved_blocks);
    }
    if (!test_opt(sbi, RESERVE_ROOT) &&
        (!uid_eq(sbi->s_resuid,
                make_kuid(&init_user_ns, F2FS_DEF_RESUID)) ||
        !gid_eq(sbi->s_resgid,
                make_kgid(&init_user_ns, F2FS_DEF_RESGID))))
        f2fs_msg(sbi->sb, KERN_INFO,
            "Ignore s_resuid=%u, s_resgid=%u w/o reserve_root",
                from_kuid_munged(&init_user_ns, sbi->s_resuid),
                from_kgid_munged(&init_user_ns, sbi->s_resgid));
}

C

Copy

关于id,Android里叫AID,在system/core/libcutils/include/private/android_filesystem_config.h:

#define AID_RESERVED_DISK 1065   /* GID that has access to reserved disk space */

C

Copy

比如zygote能使用reserved disk,在32bits系统里在rootdir/init.zygote32.rc加入reserved_disk,如下:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc reserved_disk

Rc

Copy

可以用id命令显示uid/gid等信息:

xxx:/ # ps -A | grep system_server
USER           PID  PPID     VSZ    RSS WCHAN            ADDR S NAME
system        2888   286 1127972  73976 SyS_epoll_wait b008a658 S system_server
xxx:/ #
xxx:/ # id system
uid=1000(system) gid=1000(system) groups=1000(system), context=u:r:su:s0
xxx:/ #
xxx:/ # id reserved_disk
uid=1065(reserved_disk) gid=1065(reserved_disk) groups=1065(reserved_disk), cont
ext=u:r:su:s0

// current->comm是进程名, current->pid是进程PID; printk(KERN_WARNING "the process is \"%s\"(pid %i)\n", current->comm, current->pid); return 0;

底层有reserve机制,只有gid是1065的可以把空间填满,现在普通用户装个apk,就把空间填满了,排查应用对应的group里面是否有1065

Android系统中,格式化存储卡为F2FS(Flash-Friendly File System)通常需要root权限,因为这涉及到系统级别的操作。F2FS是一种专为闪存存储设备设计的文件系统,旨在提高NAND闪存的读写性能和使用寿命。下面是大致的步骤: 1. 获取Root权限:确保你的Android设备已经获得了root权限,这是进行存储卡格式化为F2FS的前提条件。 2. 安装终端模拟器或使用ADB(Android Debug Bridge):可以通过安装一个终端模拟器应用在设备上直接执行命令,或者使用ADB从电脑连接并控制设备。 3. 检查当前分区类型:使用如下命令来检查你的存储卡当前使用的文件系统类型: ``` su lsblk ``` 这里的输出会显示设备上所有的设备及其挂载状态。 4. 格式化为F2FS:如果确定要将存储卡格式化为F2FS,可以使用如下命令: ``` su mkfs.f2fs -l [label] /dev/block/mmcblkX ``` 其中`[label]`是你想为存储卡设定的标签,`/dev/block/mmcblkX`是你的存储卡设备路径。这个路径可以通过第三步的命令查找得到。 请注意,格式化存储卡为F2FS可能会导致所有存储卡上的数据丢失,因此在执行这些操作之前,请确保备份了所有重要数据。 5. 挂载新的文件系统:格式化完成后,需要手动挂载新的F2FS文件系统: ``` su mkdir /sdcard2 mount -t f2fs -o noatime /dev/block/mmcblkX /sdcard2 ``` 然后,可以使用`df`命令检查挂载状态。 需要注意的是,不是所有的Android设备都支持F2FS文件系统,且某些设备可能需要特定的内核支持。因此,这些步骤可能不适用于所有设备,建议查阅设备制造商或开发者提供的支持信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值