You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在U-Boot中访问ext4目录及解决命令执行崩溃问题

U-Boot中Ext4目录访问崩溃问题排查与修复

问题背景

U-Boot的Ext4文件系统未实现fs_opendir接口,需手动实现目录访问逻辑。通过ext4fs_find_file检查目录存在性,并基于ext4fs_iterate_find修改了目录遍历函数,但执行ostree_boot命令时触发同步中止崩溃。

Ext4文件系统驱动定义

#if CONFIG_IS_ENABLED(FS_EXT4)
    {
        .fstype = FS_TYPE_EXT,
        .name = "ext4",
        .null_dev_desc_ok = false,
        .probe = ext4fs_probe,
        .close = ext4fs_close,
        .ls = ext4fs_ls,
        .exists = ext4fs_exists,
        .size = ext4fs_size,
        .read = ext4_read_file,
#ifdef CONFIG_CMD_EXT4_WRITE
        .write = ext4_write_file,
        .ln = ext4fs_create_link,
#else
        .write = fs_write_unsupported,
        .ln = fs_ln_unsupported,
#endif
        .uuid = ext4fs_uuid,
        .opendir = fs_opendir_unsupported,
        .unlink = fs_unlink_unsupported,
        .mkdir = fs_mkdir_unsupported,
    },
#endif

用户编写的实现代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <common.h>
#include <command.h>
#include <fs.h>
#include <blk.h>
#include <ext4fs.h>
#include <part.h>
#include <linux/ctype.h>
#include <linux/stddef.h>

#define BOOT_FLAG0 "ostree/boot.0/tcu"
#define BOOT_FLAG1 "ostree/boot.1/tcu"
#define ROLLBACK_FLAG "0"
#define HASH_SIZE 64
#define OLD_BOOTARGS "old_bootargs"

extern int ext4fs_read_file(struct ext2fs_node *node, loff_t pos,
        loff_t len, char *buf, loff_t *actread);

static int ext4fs_iterate_find_ostree_hash(struct ext2fs_node *dir, char **hash) {
    unsigned int fpos = 0;
    int status;
    loff_t actread;
    struct ext2fs_node *diro = (struct ext2fs_node *) dir;

    if (!diro->inode_read) {
        status = ext4fs_read_inode(diro->data, diro->ino, &diro->inode);
        if (status == 0)
            return 0;
    }
    /* Search the file.  */
    while (fpos < le32_to_cpu(diro->inode.size)) {
        struct ext2_dirent dirent;

        status = ext4fs_read_file(diro, fpos,
                       sizeof(struct ext2_dirent),
                       (char *)&dirent, &actread);
        if (status < 0) {
            return 1;
        }

        if (dirent.filetype != FILETYPE_DIRECTORY) {
            printf("Invalid file in path\n");
            return 1;
        }

        if (dirent.direntlen != HASH_SIZE) {
            printf("Hash size not match: %d\n", dirent.direntlen);
            return 1;
        }

        status = ext4fs_read_file(diro,
                        fpos +
                        sizeof(struct ext2_dirent),
                        dirent.namelen, *hash,
                        &actread);
        if (status < 0) {
            return 1;
        }

        *hash[dirent.namelen] = '\0';
        fpos += le16_to_cpu(dirent.direntlen);
    }
    return 0;
}

static int read_ostree_hash(const char *dirname, char **hash) {
    struct ext2fs_node *dirnode = NULL;
    int status;

    if (dirname == NULL)
        return 1;

    status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode,
                  FILETYPE_DIRECTORY);
    if (status != 1) {
        printf("** Can not find directory. **\n");
        if (dirnode)
            ext4fs_free_node(dirnode, &ext4fs_root->diropen);
        return 1;
    }

    if (ext4fs_iterate_find_ostree_hash(dirnode, hash)) {
        return 1;
    }

    ext4fs_free_node(dirnode, &ext4fs_root->diropen);
    return 0;
}

static int ext4_dir_exists(const char *dirname) {
    struct ext2fs_node *dirnode = NULL;
    int status;

    if (dirname == NULL)
        return 1;

    return ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode,
                  FILETYPE_DIRECTORY);
}

static int update_bootargs_env_with_ostree_env(const char* ostree_env) {
    char *old_env = NULL;
    char new_env[1000];

    old_env = env_get(OLD_BOOTARGS);
    if (old_env == NULL) {
        old_env = env_get("bootargs");
        if (old_env == NULL) {
            printf("[ERR] error bootargs read!\n");
            return 1;
        }
        env_set(OLD_BOOTARGS, old_env);
    }

    sprintf(new_env, "%s %s", old_env, ostree_env);
    env_set("bootargs", new_env);

    return 0;
}

int ostree_boot(const char *ifname, const char *dev_part, int is_rollback) {
    struct fs_dir_stream *dirs = NULL;
    struct fs_dirent *dent = NULL;
    char boot_dir[100];
    char hash[HASH_SIZE + 1];
    char cmd0[1024];
    char cmd1[1024];

    memset(hash, 0, sizeof(hash));
    memset(boot_dir, 0, sizeof(boot_dir));
    
    printf("[STEP]1 Find boot path\n");
    if (ext4_dir_exists(BOOT_FLAG0) == 0) {
        printf("[INFO] boot at %s\n", BOOT_FLAG0);
        memcpy(boot_dir, BOOT_FLAG0, strlen(BOOT_FLAG0));
    } else if (ext4_dir_exists(BOOT_FLAG1) == 0) {
        printf("[INFO] boot at %s\n", BOOT_FLAG1);
        memcpy(boot_dir, BOOT_FLAG1, strlen(BOOT_FLAG1));
    } else {
        printf("[ERROR] Can not ls boot dir\n");
        return 1;
    }

    printf("[STEP]2 Get boot hash\n");
    if (read_ostree_hash(boot_dir, &hash)) {
        return 1;
    }

    char boot_str[500];
    memset(boot_str, 0, sizeof(boot_str));
    sprintf(boot_str, "ostree=%s/%s/%u, ostree_root=/dev/mmcblk0p4", boot_dir, hash, is_rollback ? 1 : 0);

    printf("[STEP]3 Update bootargs\n");
    if (update_bootargs_env_with_ostree_env(boot_str)) {
        printf("[ERROR] Failed to update bootargs\n");
        return 1;
    }

    return 0;
}

int do_ostree_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) {
    if (argc != 3) {
        printf("Usage: ostree_boot ifname <device>:<partition>\n");
        return 1;
    }

    if (ostree_boot(argv[1], argv[2], 0)) {
        return 1;
    }
}

int do_ostree_rollback(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) {
    if (argc != 3) {
        printf("Usage: ostree_rollback ifname <device>:<partition>\n");
        return 1;
    }

    if (ostree_boot(argv[1], argv[2], 1)) {
        return 1;
    }
}

U_BOOT_CMD(
    ostree_rollback,
    3,
    1,
    do_ostree_rollback,
    "format : do_ostree_rollback <dev>:<part> <is_rollback>",
    "example: do_ostree_rollback 0:1 0"
);

U_BOOT_CMD(
    ostree_boot,
    3,
    1,
    do_ostree_boot,
    "format: ostree_boot <ifname> <dev>:<part>",
    "example: ostree_boot mmc 0:4"
)

崩溃日志

"Synchronous Abort" handler, esr 0x96000004
elr: 000000008205a3f0 lr : 000000008205b504 (reloc)
elr: 00000000f7b7e3f0 lr : 00000000f7b7f504
x0 : 350001400a050000 x1 : 0000000000000000
x2 : 0000000000000008 x3 : 00000000f79dff68
x4 : 00000000f79dff60 x5 : 0000000000000180
x6 : 0000000000000000 x7 : 00000000f7a07030
x8 : 0000000000000000 x9 : 0000000000000008
x10: 0000000000000044 x11: 000000000000000d
x12: 0000000000000006 x13: 000000000001869f
x14: 00000000f79e09d0 x15: 0000000000000002
x16: 00000000f7b4cc68 x17: 0000000000000000
x18: 00000000f79e3d90 x19: 0000000000000008
x20: 0000000000000001 x21: 00000000b9468040
x22: 0000000000000000 x23: 0000000000000000
x24: 000000004d8bffa7 x25: 00000000f79e000c
x26: 00000000b2740063 x27: 00000000f7bb4000
x28: 00000000f7a06290 x29: 00000000f79dfe00

Code: f94000a0 4b1a0318 b9400cb5 f90033e5 (b9401800) 
Resetting CPU ...

崩溃原因与修复方案

1. 指针优先级错误导致内存越界

在ext4fs_iterate_find_ostree_hash函数中,*hash[dirent.namelen] = '\0'一行,数组下标运算符[]优先级高于解引用*,实际执行的是*(hash[dirent.namelen]),会访问错误的内存地址触发崩溃。

修复:

(*hash)[dirent.namelen] = '\0';

2. 未挂载分区直接访问文件系统

ostree_boot函数中未先挂载目标分区,U-Boot必须先通过fs_set_blk_dev挂载分区才能访问Ext4内容。

修复:
在ostree_boot开头添加挂载逻辑:

int ostree_boot(const char *ifname, const char *dev_part, int is_rollback) {
    struct blk_desc *desc;
    struct disk_partition part;
    int ret;

    // 挂载目标分区
    ret = fs_set_blk_dev(ifname, dev_part, &desc, &part);
    if (ret != 0) {
        printf("[ERROR] Failed to set block device: %s:%s\n", ifname, dev_part);
        return 1;
    }

    // 原函数逻辑...
}

3. ext4fs_find_file返回值判断错误

ext4fs_find_file返回1表示找到目标目录,返回0表示未找到。原代码中判断ext4_dir_exists(BOOT_FLAG0) == 0是错误的。

修复:

if (ext4_dir_exists(BOOT_FLAG0) == 1) {
    printf("[INFO] boot at %s\n", BOOT_FLAG0);
    memcpy(boot_dir, BOOT_FLAG0, strlen(BOOT_FLAG0));
} else if (ext4_dir_exists(BOOT_FLAG1) == 1) {
    printf("[INFO] boot at %s\n", BOOT_FLAG1);
    memcpy(boot_dir, BOOT_FLAG1, strlen(BOOT_FLAG1));
} else {
    printf("[ERROR] Can not find boot dir\n");
    return 1;
}

4. 目录项长度判断错误

dirent.direntlen是整个目录项的字节数(包含结构体和文件名),不应和HASH_SIZE比较,应比较文件名长度dirent.namelen,且遇到非目标项应跳过而非直接返回错误。

修复:

if (dirent.namelen != HASH_SIZE) {
    fpos += le16_to_cpu(dirent.direntlen);
    continue;
}

5. 未处理无效目录项

Ext4目录中可能存在inode为0的无效项,需要跳过:

if (le32_to_cpu(dirent.inode) == 0) {
    fpos += le16_to_cpu(dirent.direntlen);
    continue;
}

内容的提问来源于stack exchange,提问作者Quan Wen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 00:12:33