diff --git a/App/cfg_file_store_mcu.c b/App/cfg_file_store_mcu.c index 15dba6b0..493a7c7a 100644 --- a/App/cfg_file_store_mcu.c +++ b/App/cfg_file_store_mcu.c @@ -10,6 +10,9 @@ * v1.0 2026-09-14 王建锋 创建初始版本 * v1.1 2026-09-18 王建锋 增 cfg_store_alloc / cfg_store_free / cfg_store_heap_free: * 共享核心临时缓冲改走 FreeRTOS 堆(P0-1,MicroLIB C 堆仅 512B) + * v1.2 2026-09-22 王建锋 原子写修复:rename 前先 unlink 已存在目标——本 FatFs + * 移植 f_rename 不覆盖既有目标(FR_EXIST),旧实现一切 + * "目标存在的覆盖写"必然失败(E-01 台架验证暴露) */ /* 头文件包含区 */ @@ -60,7 +63,7 @@ int cfg_store_read(const char *p_path, uint8_t *p_buf, size_t cap, size_t *p_nre } /* - * 函数功能:原子写文件(先写 /config.TMP,成功后 f_rename 覆盖目标) + * 函数功能:原子写文件(先写 /config.TMP,成功后 unlink 既有目标 + f_rename 替换) * 入口参数:p_path - 目标文件路径 const char * * p_buf - 待写数据(不可为 NULL) const uint8_t * * len - 数据长度 size_t @@ -85,6 +88,14 @@ int cfg_store_write_atomic(const char *p_path, const uint8_t *p_buf, size_t len) (void)f_unlink(s_path_tmp); return -1; } + /* 本 FatFs 移植的 f_rename 对已存在目标返回 FR_EXIST、不覆盖(ff.c + * "new name already in use" 检查,上游同语义)——"目标存在的覆盖写"必须先 + * unlink 目标再 rename。2026-09-22 台架实证(E-01 验证暴露):缺本步时 + * 一切非首启写(UI 保存/模板重建)静默失败,模拟器后端(POSIX rename + * 覆盖语义)照不出。unlink→rename 非原子窗口掉电 = 配置文件暂失, + * 开机 load 按"首启"重建工厂模板——与函数失败语义一致,丢失边界 = + * 本次进行中的保存内容(目标原值本就是待覆盖的旧代数据) */ + (void)f_unlink(p_path); /* 目标不存在返回 FR_NO_FILE,无害 */ fr = f_rename(s_path_tmp, p_path); if (fr != FR_OK) { (void)f_unlink(s_path_tmp);