如何在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
相关产品推荐
相关产品推荐

