/log 写失败修复:打开模式补 FA_WRITE 位 + 卷探针两击确认 + 自动重格式化自愈 + 错误去重

This commit is contained in:
2026-09-20 03:46:14 +08:00
parent 2bc2dcafeb
commit efe82668b7
4 changed files with 121 additions and 54 deletions
+81 -11
View File
@@ -210,7 +210,20 @@ void StartDefaultTask(void *argument)
* (dhara_map_resume)。此前默认构建从未挂载,导致 FTP 的
* LIST/STOR/RETR 全部返回 550(f_opendir 失败)。
* 若卷为空(FR_NO_FILESYSTEM,如恢复/坏块重建后清空),则首次自动
* 格式化建卷(MKFS_PARM 与存储测试一致:FM_FAT32 / 无分区表)。 */
* 格式化建卷(MKFS_PARM 与存储测试一致:FM_FAT32 / 无分区表)。
*
* 卷健康探针 + 自愈(2026-09-20,台架实测 /log 每次开机写失败 ×5 的
* 根因处置):真根因 = s_append 打开模式缺 FA_WRITE 位——本 FatFs 移植
* FA_OPEN_APPEND(0x30) = FA_OPEN_ALWAYS|FA_SEEKEND,不含 FA_WRITE(0x02),
* 未带写位打开的文件 f_write 恒返回 FR_DENIED(7) → 打开模式已改
* FA_WRITE|FA_OPEN_APPEND(见 sys_log_mcu.c)。残余风险 = f_mount 仅校验
* 首扇区 BPB,FAT 区陈旧/擦除(旧镜像时代遗留、换芯片、格式化中途掉电)
* 时挂载仍"成功",文件操作才报 FR_DENIED/FR_DISK_ERR/FR_INT_ERR 等结构
* 性错误(注意本移植 f_write 簇分配失败返回 FR_OK+短写,探针须校验写回
* 字节数,短写按 FR_DENIED 归一化)。挂载后做一次小写探针(两次确认,
* 防 FTL 瞬态抖动误触发);结构性错误 → 自动重格式化自愈。代价:卷被清空
* (/config.ini 丢失 → 配置引擎首启路径回落工厂默认并自动重建模板)——
* 仅当卷已不可用时触发,可用设备 > 一份配置文件。 */
static FATFS default_fatfs;
FRESULT fr = f_mount(&default_fatfs, "", 1);
if (fr == FR_NO_FILESYSTEM) {
@@ -225,16 +238,73 @@ void StartDefaultTask(void *argument)
}
}
if (fr == FR_OK) {
DBG_INFO("[FS] FATFS mounted");
/* 日志引擎挂载后处理:建 /log 目录 + 写"上电/重启"分类记录(唯一写者) */
syslog_mcu_mounted();
/* 加载网络配置进 RAM 副本(配置引擎属主 = 本任务;首启含旧档迁移;
* 文件缺失/损坏 = 工厂默认,不阻塞组网)。须在置位 STORAGE_READY 前,
* 使"存储就绪"屏障蕴含"配置已就绪",netTask 组网时配置恒有效 */
(void)net_cfg_load();
/* 广播"存储就绪"(= 卷挂载 + 配置加载完成),唤醒依赖文件系统的任务 */
if (sys_evtHandle != NULL) {
osEventFlagsSet(sys_evtHandle, SYS_EVT_STORAGE_READY);
/* 探针:/.fschk(8.3 合规)小写一次,验证 FAT 链真实可用。本移植
* f_write 簇分配失败返回 FR_OK+短写 → 必须校验写回字节数;短写按
* FR_DENIED 归一化(落入下方结构性集合触发重格式化) */
#define FS_VOL_PROBE(h) do { \
FIL __chk; \
static const uint8_t __chkbuf[8] = { 0 }; \
UINT __bw = 0u; \
FRESULT __wr; \
(h) = f_open(&__chk, "/.fschk", FA_WRITE | FA_OPEN_ALWAYS); \
if ((h) == FR_OK) { \
__wr = f_write(&__chk, __chkbuf, (UINT)sizeof(__chkbuf), &__bw); \
(void)f_close(&__chk); \
if (__wr == FR_OK && __bw == (UINT)sizeof(__chkbuf)) { \
(h) = FR_OK; \
(void)f_unlink("/.fschk"); \
} else { \
(h) = (__wr != FR_OK) ? __wr : FR_DENIED; \
} \
} \
} while (0)
FRESULT hfr;
FS_VOL_PROBE(hfr);
if (hfr != FR_OK) {
FS_VOL_PROBE(hfr); /* 两击确认 */
}
if (hfr != FR_OK) {
if (hfr == FR_DENIED || hfr == FR_INVALID_OBJECT ||
hfr == FR_INT_ERR || hfr == FR_DISK_ERR ||
hfr == FR_NO_FILE || hfr == FR_NO_PATH ||
hfr == FR_INVALID_NAME || hfr == FR_NO_FILESYSTEM) {
DBG_INFO("[FS] volume FAT invalid, reformatting...");
f_mount(NULL, "", 0);
{
static uint8_t fs_work2[FF_MAX_SS];
MKFS_PARM opts2 = { FM_FAT32, 0, 0, 0, 0 };
fr = f_mkfs("", &opts2, fs_work2, sizeof(fs_work2));
if (fr == FR_OK) {
fr = f_mount(&default_fatfs, "", 1);
}
}
if (fr == FR_OK) {
DBG_INFO("[FS] volume reformatted OK");
} else {
DBG_ERROR("[FS] volume repair failed (fr=%d)", (int)fr);
syslog_post(SYSLOG_SELFTEST, "storage fault");
}
} else {
/* 其余错误(写保护/卷锁/参数类)不重格式化——只上报,
* 防对写锁卷整卷擦除(FTL 磨损最大操作) */
DBG_ERROR("[FS] volume probe failed (fr=%d)", (int)hfr);
syslog_post(SYSLOG_SELFTEST, "storage fault");
fr = FR_INT_ERR;
}
}
#undef FS_VOL_PROBE
if (fr == FR_OK) {
DBG_INFO("[FS] FATFS mounted");
/* 日志引擎挂载后处理:建 /log 目录 + 写"上电/重启"分类记录(唯一写者) */
syslog_mcu_mounted();
/* 加载网络配置进 RAM 副本(配置引擎属主 = 本任务;首启含旧档迁移;
* 文件缺失/损坏 = 工厂默认,不阻塞组网)。须在置位 STORAGE_READY 前,
* 使"存储就绪"屏障蕴含"配置已就绪",netTask 组网时配置恒有效 */
(void)net_cfg_load();
/* 广播"存储就绪"(= 卷挂载 + 配置加载完成),唤醒依赖文件系统的任务 */
if (sys_evtHandle != NULL) {
osEventFlagsSet(sys_evtHandle, SYS_EVT_STORAGE_READY);
}
}
} else {
DBG_ERROR("[FS] f_mount failed (fr=%d)", (int)fr);