FatFs短名解析拒绝点加DEBUG打印并台架实证+AGENTS.md固化严格8.3命名约束
This commit is contained in:
@@ -44,6 +44,7 @@ STM32F407ZGTx @168MHz(HSE 8MHz → PLL M=4/N=168/P=2);**仅 Keil MDK-ARM v
|
||||
- **内存预算紧张**:新增任何任务/缓冲/栈前必须先核算 RAM 余量(`docs/内存管理/`、`待处理内容.md`)
|
||||
- **自研代码禁用 C 库堆分配**(2026-09-18,P0-1):禁止 `malloc/calloc/realloc/strdup/free`——本机工具链 MicroLIB 的 C 堆由 `startup_stm32f407xx.s` 的 `Heap_Size EQU 0x200` 限定为 **512 B**,KB 级分配必然返回 NULL 且**模拟器(宿主 CRT 堆)完全正常、照不出来**。需要动态内存只用 `pvPortMalloc/vPortFree`(FreeRTOS 堆 `configTOTAL_HEAP_SIZE`),或像配置引擎那样把分配收进平台后端(`cfg_store_alloc/free/heap_free`:MCU=FreeRTOS 堆、模拟器=宿主堆,共享核心零分配符号);分配失败必须有 `FAIL` 日志 + 自检上报,暂态峰值要写编译期预算
|
||||
- **FatFs 打开模式陷阱(2026-09-20 台架实测,`Lib/FatFs` 移植版)**:`FA_OPEN_APPEND`(0x30) = `FA_OPEN_ALWAYS|FA_SEEKEND`,**不含 `FA_WRITE`(0x02)**——不带写位打开的文件 `f_write` 恒返回 `FR_DENIED`(7)(/log 写失败真根因);追加写必须用 `FA_WRITE|FA_OPEN_APPEND`。该移植的 `f_write` 簇分配失败时返回 `FR_OK`+短写(无错误码),判失败必须校验写回字节数。标志位为旧式编号(`FA_READ`=0x01 / `FA_WRITE`=0x02 / `FA_CREATE_NEW`=0x04 / `FA_CREATE_ALWAYS`=0x08 / `FA_OPEN_ALWAYS`=0x10 / `FA_OPEN_APPEND`=0x30 / `FA_SEEKEND`=0x20(ff.c 内部定义)),与上游 FatFs R0.14 不同,勿照抄上游代码示例
|
||||
- **FatFs 文件名约束(2026-09-20 台架实证,`.fschk` 无限重格式化事故根因)**:本移植 `Lib/FatFs/ffconf.h` 为 `FF_USE_LFN=0`,**只支持严格 8.3 短名**——主名≤8 字符、扩展≤3 字符、**不得以点开头**(`.fschk` 会被 `ff.c create_short` 把点后当扩展域解析超限 → `f_open` 恒返 `FR_INVALID_NAME`(6),纯 RAM 名字判定、不碰盘)。ff.c `create_short` 全部拒绝点已加 DEBUG 打印:`[ERR][FATFS] SFN reject (原因) seg='名字片段' bad_ch=坏字符`,非法名字调用看开机日志即可定位。自愈类逻辑**严禁**把名字/参数类错误码(`FR_INVALID_NAME` 等)归入卷损坏触发集(整卷重格式化不可逆)
|
||||
|
||||
| Task | 栈 (words) | 优先级 |
|
||||
|---|---|---|
|
||||
|
||||
+45
-5
@@ -23,6 +23,21 @@
|
||||
#include "ff.h" /* Declarations of FatFs API */
|
||||
#include "diskio.h" /* Declarations of device I/O functions */
|
||||
|
||||
/* 项目定制(2026-09-20,".fschk 无限重格式化"事故复盘):
|
||||
* FF_USE_LFN=0 时 f_open/f_create/f_unlink/f_mkdir 等一切文件 API
|
||||
* 最终都汇聚到 create_short() 做 8.3 短名解析——这是名字合法性的
|
||||
* 唯一咽喉点。在拒绝瞬间打印名字片段+原因,让"非法文件名"这类
|
||||
* 纯 RAM 判定错误从开机日志直接可见,不再需要拿错误码反查枚举表。
|
||||
* 注意:字符串字面量必须纯 ASCII(ARMCC 对无 BOM 文件按 CP936 解析)。 */
|
||||
#if FF_USE_LFN == 0
|
||||
#define DBG_TAG "[FATFS]"
|
||||
#include "dbg_log.h"
|
||||
#define FF_DBG_SFN_REJECT(why, seg, ch) \
|
||||
DBG_ERROR("SFN reject (%s) seg='%.24s' bad_ch=0x%02X" \
|
||||
" - strict 8.3 only: body<=8 ext<=3 no leading dot", \
|
||||
(why), (seg), (int)(ch))
|
||||
#endif
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@@ -2993,7 +3008,12 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr
|
||||
if (c != '.' || si >= 3) break;
|
||||
sfn[i++] = c;
|
||||
}
|
||||
if (!IsSeparator(c) && c > ' ') return FR_INVALID_NAME;
|
||||
if (!IsSeparator(c) && c > ' ') {
|
||||
#if FF_USE_LFN == 0
|
||||
FF_DBG_SFN_REJECT("heading dot", p, c);
|
||||
#endif
|
||||
return FR_INVALID_NAME;
|
||||
}
|
||||
*path = p + si; /* Return pointer to the next segment */
|
||||
sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of the path */
|
||||
return FR_OK;
|
||||
@@ -3007,7 +3027,12 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr
|
||||
break;
|
||||
}
|
||||
if (c == '.' || i >= ni) { /* End of body or field overflow? */
|
||||
if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Field overflow or invalid dot? */
|
||||
if (ni == 11 || c != '.') {
|
||||
#if FF_USE_LFN == 0
|
||||
FF_DBG_SFN_REJECT("ext overflow or bad dot", p, c);
|
||||
#endif
|
||||
return FR_INVALID_NAME; /* Field overflow or invalid dot? */
|
||||
}
|
||||
i = 8; ni = 11; /* Enter file extension field */
|
||||
continue;
|
||||
}
|
||||
@@ -3022,17 +3047,32 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr
|
||||
#endif
|
||||
if (dbc_1st(c)) { /* Check if it is a DBC 1st byte */
|
||||
d = (BYTE)p[si++]; /* Get 2nd byte */
|
||||
if (!dbc_2nd(d) || i >= ni - 1) return FR_INVALID_NAME; /* Reject invalid DBC */
|
||||
if (!dbc_2nd(d) || i >= ni - 1) {
|
||||
#if FF_USE_LFN == 0
|
||||
FF_DBG_SFN_REJECT("invalid DBC", p, c);
|
||||
#endif
|
||||
return FR_INVALID_NAME; /* Reject invalid DBC */
|
||||
}
|
||||
sfn[i++] = c;
|
||||
sfn[i++] = d;
|
||||
} else { /* SBC */
|
||||
if (strchr("*+,:;<=>[]|\"\?\x7F", (int)c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */
|
||||
if (strchr("*+,:;<=>[]|\"\?\x7F", (int)c)) {
|
||||
#if FF_USE_LFN == 0
|
||||
FF_DBG_SFN_REJECT("illegal char", p, c);
|
||||
#endif
|
||||
return FR_INVALID_NAME; /* Reject illegal chrs for SFN */
|
||||
}
|
||||
if (IsLower(c)) c -= 0x20; /* To upper */
|
||||
sfn[i++] = c;
|
||||
}
|
||||
}
|
||||
*path = &p[si]; /* Return pointer to the next segment */
|
||||
if (i == 0) return FR_INVALID_NAME; /* Reject nul string */
|
||||
if (i == 0) {
|
||||
#if FF_USE_LFN == 0
|
||||
FF_DBG_SFN_REJECT("empty name", p, c);
|
||||
#endif
|
||||
return FR_INVALID_NAME; /* Reject nul string */
|
||||
}
|
||||
|
||||
if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */
|
||||
sfn[NSFLAG] = (c <= ' ' || p[si] <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */
|
||||
|
||||
Reference in New Issue
Block a user