Files
2026-08-29 18:47:54 +08:00

856 lines
35 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* fat32_write.h - Deshab 共享 FAT32 写库(static inline,无库)
*
* 前置条件:包含者已定义 u8/u16/u32/u64/i64 基础类型。
* 通过 f32w_init() 注入 block_read/block_write 函数指针(签名与 f32_/gpt_ 库一致)。
*
* 与 fat32_io.h 的分工:
* fat32_io.h - 8.3 短名读写,依赖 256 扇区 BPB+FAT+根目录缓存窗口
* fat32_write.h - LFN 长名创建 / mkdir / 目录簇链扩展,FAT 按需单扇区访问
*
* 本库解决 fat32_io.h 的三个写缺口(大 ESP 场景,2GB: spc=8, FAT=4086 扇区,
* 根目录在分区 LBA 8204,均远超 256 扇区窗口):
* 1. LFN 生成:VFAT LFN 链(每项 13 UTF-16 字符,逆序存储,0x40 标志 +
* 短名 checksum),配套 ~N 数字尾巴的 8.3 短名生成与唯一化
* 2. mkdir:分配目录簇 + 写入 "." / ".." 项 + 父目录插入 LFN 条目
* 3. 目录簇链扩展:目录槽位不足时自动分配新簇并接入链尾(槽位可跨簇)
*
* FAT 访问策略:按需读改写单个 FAT 扇区(含缓存),修改后立即回写所有 FAT
* 副本(BPB->fc 份)。不依赖任何批量缓存窗口,任意簇号/FAT 偏移均可访问。
*
* 用法:
* f32w_init(block_read, block_write) - 初始化(注入块设备)
* f32w_write_file(dir_clus, "user.conf", d, n) - 目录中写/替换长名文件
* f32w_mkdir(dir_clus, "MyDir", &out_clus) - 创建子目录(LFN)
* f32w_resolve_dir("system/deshab64", &clus) - 按路径定位目录簇
* f32w_write_path("system/deshab64/user.conf", d, n)- 按路径写文件(LFN)
* f32w_mkdir_path("system/新建目录", &clus) - 按路径建目录
* f32w_find(dir_clus, name, want_dir, ...) - 按长名查找(LFN 优先)
*
* 缓冲区:每包含者约 9KB BSS(512 BPB + 512 FAT 扇区 + 簇缓冲 ×2)。
* 与 fat32_io.h 可同时包含(前缀 f32w_ / f32_ 不冲突),但二者状态独立。
*/
#ifndef DESHAB_FAT32_WRITE_H
#define DESHAB_FAT32_WRITE_H
#include "fat32_lfn.h"
/* ---- block 设备函数类型(与 fat32_io.h / gpt_write.h 一致:index 固定 0) ---- */
typedef int (*f32w_block_read_fn)(u32 index, u64 lba, u32 count, void *buf);
typedef int (*f32w_block_write_fn)(u32 index, u64 lba, u32 count, const void *buf);
static f32w_block_read_fn f32w_blk_read = 0;
static f32w_block_write_fn f32w_blk_write = 0;
/* ---- 目录簇缓冲大小(含 data 暂存缓冲)。build.ps1 mkfs spc=8 -> 4096。
* 包含者可在 include 前 #define F32W_CLUS_BYTES 覆盖(须为 512 的倍数)。 */
#ifndef F32W_CLUS_BYTES
#define F32W_CLUS_BYTES 4096
#endif
#define F32W_EOC 0x0FFFFFFFu
#define F32W_EOC_VAL 0x0FFFFFFFu /* 写入 FAT 的 EOC 值(高 4 位保留位置 0x0Fxxxxxx 已含) */
/* ---- BPB(512B,与 f32_bpb 布局一致;独立定义避免依赖 fat32_io.h) ---- */
typedef struct __attribute__((packed)) {
u8 jmp[3]; char oem[8]; u16 bps; u8 spc; u16 rsvd; u8 fc; u16 root_ent;
u16 ts16; u8 media; u16 spf16; u16 spt; u16 heads; u32 hidden; u32 ts32;
u32 spf; u16 flags; u16 ver; u32 root_clus; u16 fsi; u16 bkboot;
u8 res[12]; u8 drv; u8 ntfl; u8 sig; u32 ser; char lbl[11]; char typ[8];
u8 code[420]; u16 boot_sig;
} f32w_bpb_t;
typedef struct __attribute__((packed)) {
char name[11]; u8 attr; u8 ntr; u8 ctenth;
u16 ctime; u16 cdate; u16 adate; u16 chigh;
u16 wtime; u16 wdate; u16 clow; u32 fsize;
} f32w_dirent_t;
/* ---- 挂载上下文(f32w_bpb_load 填充) ---- */
typedef struct {
u32 spc; /* 每簇扇区数 */
u32 clus_bytes; /* spc * 512 */
u32 fat_lba; /* FAT 副本 0 起始 LBA(= rsvd) */
u32 fat_copies; /* FAT 副本数(BPB->fc,实际至少 2) */
u32 fat_sectors; /* 单份 FAT 扇区数(BPB->spf) */
u32 data_lba; /* 数据区起始 LBA */
u32 root_clus; /* 根目录首簇 */
u32 fat_entries; /* FAT 项数上界 = spf*512/4 */
int loaded;
} f32w_ctx;
static f32w_ctx f32w_c;
/* ---- 内部缓冲(每包含者独立) ---- */
static u8 f32w_sec[512]; /* BPB / FSInfo 扇区缓冲 */
static u8 f32w_fat[512]; /* FAT 单扇区缓存 */
static u32 f32w_fat_sec_no = 0xFFFFFFFFu; /* 缓存的 FAT 扇区号(FAT 内偏移) */
static u8 f32w_dir[F32W_CLUS_BYTES]; /* 目录簇缓存(槽位扫描/条目写入) */
static u32 f32w_dir_no = 0; /* 缓存的目录簇号(0=无,簇号最小为 2) */
static int f32w_dir_dirty = 0;
static u8 f32w_stage[F32W_CLUS_BYTES]; /* 数据写入暂存(尾簇零填充) */
static u32 f32w_alloc_hint = 2; /* 空闲簇分配起点提示 */
/* ---- 初始化:注入 block_read/block_write 函数指针 ---- */
static inline void f32w_init(f32w_block_read_fn rd, f32w_block_write_fn wr) {
f32w_blk_read = rd;
f32w_blk_write = wr;
f32w_c.loaded = 0;
f32w_fat_sec_no = 0xFFFFFFFFu;
f32w_dir_no = 0;
f32w_dir_dirty = 0;
f32w_alloc_hint = 2;
}
static inline int f32w_read_sectors(u32 lba, u32 count, u8 *out) {
return f32w_blk_read ? f32w_blk_read(0, lba, count, out) : -1;
}
static inline int f32w_write_sectors(u32 lba, u32 count, const u8 *buf) {
return f32w_blk_write ? f32w_blk_write(0, lba, count, buf) : -1;
}
/* ---- 小工具(无库约束:手写循环替代 mem*) ---- */
static inline void f32w_mem_set(u8 *p, u8 v, u32 n) { for (u32 i = 0; i < n; i++) p[i] = v; }
static inline u32 f32w_r32(const u8 *p) {
return (u32)p[0] | ((u32)p[1] << 8) | ((u32)p[2] << 16) | ((u32)p[3] << 24);
}
static inline u16 f32w_r16(const u8 *p) { return (u16)p[0] | ((u16)p[1] << 8); }
/* ---- BPB 加载 / 挂载上下文 ---- */
static inline int f32w_bpb_load(void) {
if (f32w_c.loaded) return 0;
if (f32w_read_sectors(0, 1, f32w_sec) != 0) return -1;
const f32w_bpb_t *b = (const f32w_bpb_t *)f32w_sec;
if (b->boot_sig != 0xAA55 || b->bps != 512) return -2;
if (b->spf == 0 || b->root_clus < 2 || b->spc == 0) return -3;
u32 fc = b->fc ? b->fc : 1;
u32 cb = (u32)b->spc * 512;
if (cb > F32W_CLUS_BYTES) return -4; /* 簇超出缓冲,包含者应调大 F32W_CLUS_BYTES */
f32w_c.spc = b->spc;
f32w_c.clus_bytes = cb;
f32w_c.fat_lba = b->rsvd;
f32w_c.fat_copies = fc;
f32w_c.fat_sectors = b->spf;
f32w_c.data_lba = b->rsvd + fc * b->spf;
f32w_c.root_clus = b->root_clus;
f32w_c.fat_entries = b->spf * 512 / 4;
f32w_c.loaded = 1;
f32w_fat_sec_no = 0xFFFFFFFFu; /* 新挂载,作废 FAT 缓存 */
return 0;
}
static inline u32 f32w_clus_lba(u32 clus) {
return f32w_c.data_lba + (clus - 2) * f32w_c.spc;
}
/* ---- FAT 按需访问(单扇区缓存 + 全副本回写) ---- */
/* 读 FAT 项。返回 0 成功,*out 为 28 位簇值。 */
static inline int f32w_fat_get(u32 clus, u32 *out) {
if (clus < 2 || clus >= f32w_c.fat_entries) return -1;
u32 sec = clus >> 7; /* 每扇区 128 项 */
u32 off = (clus & 127) * 4;
if (sec != f32w_fat_sec_no) {
if (f32w_read_sectors(f32w_c.fat_lba + sec, 1, f32w_fat) != 0) return -2;
f32w_fat_sec_no = sec;
}
*out = f32w_r32(f32w_fat + off) & 0x0FFFFFFF;
return 0;
}
/* 写 FAT 项(含 28 位掩码),立即回写所有 FAT 副本对应扇区。 */
static inline int f32w_fat_set(u32 clus, u32 val) {
if (clus < 2 || clus >= f32w_c.fat_entries) return -1;
u32 sec = clus >> 7;
u32 off = (clus & 127) * 4;
if (sec != f32w_fat_sec_no) {
if (f32w_read_sectors(f32w_c.fat_lba + sec, 1, f32w_fat) != 0) return -2;
f32w_fat_sec_no = sec;
}
u32 old = f32w_r32(f32w_fat + off);
val &= 0x0FFFFFFF;
f32w_fat[off] = (u8)(val & 0xFF);
f32w_fat[off + 1] = (u8)((val >> 8) & 0xFF);
f32w_fat[off + 2] = (u8)((val >> 16) & 0xFF);
f32w_fat[off + 3] = (u8)((old & 0xF0) | ((val >> 24) & 0x0F));
for (u32 f = 0; f < f32w_c.fat_copies; f++) {
u32 lba = f32w_c.fat_lba + f * f32w_c.fat_sectors + sec;
if (f32w_write_sectors(lba, 1, f32w_fat) != 0) return -3;
}
return 0;
}
/* 分配一个空闲簇(线性扫描 + 起点提示轮转)。返回簇号,0=已满/失败。 */
static inline u32 f32w_alloc_free(void) {
u32 total = f32w_c.fat_entries > 2 ? f32w_c.fat_entries - 2 : 0;
if (total == 0) return 0;
u32 start = (f32w_alloc_hint >= 2 && f32w_alloc_hint < f32w_c.fat_entries)
? f32w_alloc_hint : 2;
for (u32 i = 0; i < total; i++) {
u32 c = 2 + ((start - 2 + i) % total);
u32 v = 0;
if (f32w_fat_get(c, &v) != 0) return 0;
if (v == 0) {
f32w_alloc_hint = c + 1;
return c;
}
}
return 0;
}
/* 释放整条簇链(逐项清 0)。带环保护。 */
static inline int f32w_free_chain(u32 clus) {
u32 cur = clus;
u64 guard = 0;
while (cur >= 2 && cur < 0x0FFFFFF8) {
if (++guard > (u64)f32w_c.fat_entries + 2) return -1; /* FAT 成环,损坏 */
u32 next = 0;
if (f32w_fat_get(cur, &next) != 0) return -2;
if (f32w_fat_set(cur, 0) != 0) return -3;
cur = next;
}
return 0;
}
/* ---- 目录簇缓存(槽位扫描 / 条目写入共用) ---- */
static inline int f32w_dir_flush(void) {
if (!f32w_dir_dirty || f32w_dir_no == 0) { f32w_dir_dirty = 0; return 0; }
if (f32w_write_sectors(f32w_clus_lba(f32w_dir_no), f32w_c.spc, f32w_dir) != 0) return -1;
f32w_dir_dirty = 0;
return 0;
}
static inline int f32w_dir_load(u32 clus) {
if (f32w_dir_no == clus && clus != 0) return 0;
if (f32w_dir_flush() != 0) return -1;
if (f32w_read_sectors(f32w_clus_lba(clus), f32w_c.spc, f32w_dir) != 0) return -2;
f32w_dir_no = clus;
return 0;
}
/* 在 (clus, idx) 写入 32 字节目录项。 */
static inline int f32w_entry_write(u32 clus, u32 idx, const u8 *e32) {
if (idx * 32 + 32 > f32w_c.clus_bytes) return -1;
if (f32w_dir_load(clus) != 0) return -2;
for (int i = 0; i < 32; i++) f32w_dir[idx * 32 + i] = e32[i];
f32w_dir_dirty = 1;
return 0;
}
/* 槽位步进:同簇内 idx+1,越界则沿 FAT 链进入下一簇(run 保证连续)。 */
static inline int f32w_slot_step(u32 *clus, u32 *idx) {
(*idx)++;
if ((*idx) * 32 >= f32w_c.clus_bytes) {
u32 next = 0;
if (f32w_fat_get(*clus, &next) != 0) return -1;
if (next < 2 || next >= 0x0FFFFFF8) return -1;
*clus = next;
*idx = 0;
}
return 0;
}
/* ---- 8.3 短名生成(含 ~N 唯一化) ---- */
static inline char f32w_up(char c) { return (c >= 'a' && c <= 'z') ? (char)(c - 32) : c; }
/* 合法 8.3 短名字符(已转大写后):字母/数字/!#$%&'()-@^_`{}~ */
static inline int f32w_char_ok_83(char c) {
if (c >= 'A' && c <= 'Z') return 1;
if (c >= '0' && c <= '9') return 1;
if (c == '!' || c == '#' || c == '$' || c == '%' || c == '&' || c == '\'') return 1;
if (c == '(' || c == ')' || c == '-' || c == '@' || c == '^' || c == '_') return 1;
if (c == '`' || c == '{' || c == '}' || c == '~') return 1;
return 0;
}
/* 名字长度(手动 strlen)。 */
static inline u32 f32w_strlen(const char *s) {
u32 n = 0;
while (s[n]) n++;
return n;
}
/* 生成长名对应的 8.3 短名(未唯一化)。
* name11 : 输出 11 字符短名(大写、空格填充;非法字符转 '_',超长截断)
* needs_lfn: 输出 1=长名无法无损表示为 8.3(需写 LFN 链 + 后续 ~N 唯一化)
* 返回 0 成功,-1 名字非法(空/过长/含非 ASCII/纯点)。 */
static inline int f32w_make_short(const char *name, char name11[11], int *needs_lfn) {
u32 len = f32w_strlen(name);
if (len == 0 || len > 255) return -1;
u32 last_dot = 0xFFFFFFFFu;
u32 dot_count = 0;
int all_upper_valid = 1;
for (u32 i = 0; i < len; i++) {
char c = name[i];
if ((u8)c < 0x20 || (u8)c >= 0x7F || c == '/') return -1;
if (c == '.') { last_dot = i; dot_count++; }
if (c >= 'a' && c <= 'z') all_upper_valid = 0;
else if (!f32w_char_ok_83(f32w_up(c))) all_upper_valid = 0;
}
/* 纯点 / 尾点 / 首空格:非法(简化处理,与 Windows 行为近似) */
int non_dot = 0;
for (u32 i = 0; i < len; i++) if (name[i] != '.') { non_dot = 1; break; }
if (!non_dot || name[len - 1] == '.' || name[0] == ' ') return -1;
u32 base_end = (last_dot == 0xFFFFFFFFu) ? len : last_dot;
u32 base_len = base_end;
u32 ext_len = (last_dot == 0xFFFFFFFFu) ? 0 : len - last_dot - 1;
*needs_lfn = !(all_upper_valid && base_len <= 8 && ext_len <= 3 && dot_count <= 1);
for (int i = 0; i < 11; i++) name11[i] = ' ';
u32 bi = 0;
for (u32 i = 0; i < base_len && bi < 8; i++) {
char c = f32w_up(name[i]);
if (!f32w_char_ok_83(c)) c = '_';
name11[bi++] = c;
}
if (bi == 0) name11[bi++] = '_'; /* 空基名(如 ".config")用 '_' 兜底 */
u32 ei = 0;
if (last_dot != 0xFFFFFFFFu) {
for (u32 i = 0; i < ext_len && ei < 3; i++) {
char c = f32w_up(name[last_dot + 1 + i]);
if (!f32w_char_ok_83(c)) c = '_';
name11[8 + ei++] = c;
}
}
return 0;
}
/* 目录中是否已存在指定 11 字符短名(仅比较短名项)。1=存在,0=不存在,-1=IO 错。 */
static inline int f32w_dir_short_exists(u32 dir_clus, const char name11[11]) {
u32 dclus = dir_clus;
u64 guard = 0;
while (dclus >= 2 && dclus < 0x0FFFFFF8) {
if (++guard > (u64)f32w_c.fat_entries + 2) return -1;
if (f32w_dir_load(dclus) != 0) return -1;
u32 max_e = f32w_c.clus_bytes / 32;
for (u32 e = 0; e < max_e; e++) {
const u8 *ent = f32w_dir + e * 32;
if (ent[0] == 0x00) return 0; /* 目录结尾 */
if (ent[0] == 0xE5) continue;
if (ent[11] == 0x0F) continue; /* LFN 项 */
if (ent[11] & 0x08) continue; /* 卷标 */
int same = 1;
for (int i = 0; i < 11; i++)
if (ent[i] != (u8)name11[i]) { same = 0; break; }
if (same) return 1;
}
u32 next = 0;
if (f32w_fat_get(dclus, &next) != 0) return -1;
dclus = next;
}
return 0;
}
/* 生成长名对应的唯一 8.3 短名(lossy 时追加 ~1..~999999 数字尾巴)。
* 返回 0 成功,负值失败。 */
static inline int f32w_make_unique_short(u32 dir_clus, const char *name, char name11[11]) {
int needs_lfn = 0;
if (f32w_make_short(name, name11, &needs_lfn) != 0) return -1;
if (!needs_lfn) return 0; /* 无损 8.3,同名冲突由 f32w_find 语义覆盖 */
/* 提取 make_short 产生的 base / ext,枚举 BASE~N 直到唯一 */
char base[8];
u32 base_len = 0;
for (u32 i = 0; i < 8 && name11[i] != ' '; i++) base[base_len++] = name11[i];
if (base_len == 0) { base[0] = '_'; base_len = 1; }
for (u32 n = 1; n <= 999999u; n++) {
char digits[8];
int dn = 0;
u32 v = n;
while (v) { digits[dn++] = (char)('0' + (v % 10)); v /= 10; }
u32 bl = (dn + 1 >= 8) ? 1 : 8 - dn - 1; /* 保留 '~'+数字 的空间 */
if (base_len < bl) bl = base_len;
for (u32 i = 0; i < 8; i++) name11[i] = ' ';
for (u32 i = 0; i < bl; i++) name11[i] = base[i];
u32 p = bl;
name11[p++] = '~';
for (int i = dn - 1; i >= 0; i--) name11[p++] = digits[i];
int ex = f32w_dir_short_exists(dir_clus, name11);
if (ex < 0) return -2;
if (ex == 0) return 0;
}
return -3; /* 序号耗尽 */
}
/* ---- LFN 条目生成 ---- */
/* 13 个 UTF-16 槽位在 32 字节目录项内的字节偏移(name1[5] name2[6] name3[2]) */
static inline int f32w_lfn_slot_off(int slot) {
if (slot < 5) return 1 + slot * 2;
if (slot < 11) return 14 + (slot - 5) * 2;
return 28 + (slot - 11) * 2;
}
/* 构造第 ordinal 个 LFN 片段项(ordinal 1..total,total 为链长)。
* name 为 ASCII 长名,len 为字符数。out32 接收 32 字节编码。
* 最高序号项(ordinal==total)带 0x40 标志,含 0x0000 终止 + 0xFFFF 填充。
* 返回 0 成功。 */
static inline int f32w_build_lfn_entry(int ordinal, int total, const char *name,
u32 len, u8 cksum, u8 *out32) {
f32w_mem_set(out32, 0, 32);
/* 全槽位预填 0xFFFF */
for (int s = 0; s < 13; s++) {
int off = f32w_lfn_slot_off(s);
out32[off] = 0xFF;
out32[off + 1] = 0xFF;
}
u32 base = (u32)(ordinal - 1) * 13;
u32 cnt = (len - base) < 13 ? (len - base) : 13;
for (u32 j = 0; j < cnt; j++) {
int off = f32w_lfn_slot_off((int)j);
u16 c = (u16)(u8)name[base + j]; /* ASCII -> UTF-16 */
out32[off] = (u8)(c & 0xFF);
out32[off + 1] = (u8)(c >> 8);
}
if (ordinal == total && cnt < 13) {
int off = f32w_lfn_slot_off((int)cnt); /* 0x0000 终止符 */
out32[off] = 0;
out32[off + 1] = 0;
}
out32[0] = (u8)(ordinal | (ordinal == total ? 0x40 : 0x00));
out32[11] = 0x0F; /* ATTR_LONG_NAME */
out32[12] = 0; /* type */
out32[13] = cksum;
out32[26] = 0; /* 首簇号必须为 0 */
out32[27] = 0;
return 0;
}
/* 构造 8.3 短名项。 */
static inline void f32w_build_short_entry(const char name11[11], u8 attr,
u32 clus, u32 size, u8 *out32) {
f32w_mem_set(out32, 0, 32);
for (int i = 0; i < 11; i++) out32[i] = (u8)name11[i];
out32[11] = attr;
/* 时间戳字段保持 0(与 fat32_io.h 写路径一致) */
out32[21] = (u8)((clus >> 16) & 0xFF); /* chigh */
out32[20] = (u8)((clus >> 24) & 0xFF);
out32[26] = (u8)(clus & 0xFF); /* clow */
out32[27] = (u8)((clus >> 8) & 0xFF);
out32[28] = (u8)(size & 0xFF); /* fsize */
out32[29] = (u8)((size >> 8) & 0xFF);
out32[30] = (u8)((size >> 16) & 0xFF);
out32[31] = (u8)((size >> 24) & 0xFF);
}
/* ---- 目录槽位分配(含簇链扩展,槽位可跨簇) ---- */
/* 在目录簇链中找 count 个连续空闲槽位(0x00/0xE5)。链尾不足时自动分配
* 新簇:FAT 接链 + 新簇清零 + 落盘,槽位 run 跨簇继续累计。
* out_clus/out_idx : run 首槽位位置(簇号 + 簇内项索引)
* 返回 0 成功,负值失败(-3=磁盘满)。 */
static inline int f32w_dir_alloc_slots(u32 dir_clus, u32 count,
u32 *out_clus, u32 *out_idx) {
u32 run_clus = 0, run_idx = 0, run = 0;
u32 dclus = dir_clus;
u64 guard = 0;
while (dclus >= 2 && dclus < 0x0FFFFFF8) {
if (++guard > (u64)f32w_c.fat_entries + 2) return -1; /* FAT 成环 */
if (f32w_dir_load(dclus) != 0) return -2;
u32 max_e = f32w_c.clus_bytes / 32;
for (u32 e = 0; e < max_e; e++) {
u8 first = f32w_dir[e * 32];
if (first == 0x00 || first == 0xE5) {
if (run == 0) { run_clus = dclus; run_idx = e; }
run++;
if (run >= count) {
*out_clus = run_clus;
*out_idx = run_idx;
return 0;
}
} else {
run = 0;
}
}
u32 next = 0;
if (f32w_fat_get(dclus, &next) != 0) return -2;
if (next >= 2 && next < 0x0FFFFFF8) { dclus = next; continue; }
/* 链尾且 run 不足:扩展一个新簇接入链尾 */
u32 nc = f32w_alloc_free();
if (nc == 0) return -3;
if (f32w_fat_set(dclus, nc) != 0) return -4;
if (f32w_fat_set(nc, F32W_EOC_VAL) != 0) return -4;
if (f32w_dir_flush() != 0) return -5;
f32w_mem_set(f32w_dir, 0, f32w_c.clus_bytes); /* 新簇全 0 */
f32w_dir_no = nc;
f32w_dir_dirty = 1; /* 脏标记使 flush 落盘清零内容 */
if (f32w_dir_flush() != 0) return -5;
dclus = nc;
/* run 跨簇继续累计(新簇全 0x00,下一轮循环继续计数) */
}
return -6;
}
/* ---- 按长名查找(LFN 优先,8.3 回退;大小写不敏感) ---- */
/* 在目录中查找 name(长名或 8.3 显示名)。
* want_dir : -1 任意,0 只匹配文件,1 只匹配目录
* out_clus/size : 目标首簇 / 大小(可 NULL)
* out_ent_clus/idx : 短名项位置(可 NULL,用于原地更新)
* out_attr : 目录项属性(可 NULL)
* 返回 0 找到,-1 未找到,其他负值 IO 错。LFN 缓冲跨簇存活(链可跨簇)。 */
static inline int f32w_find(u32 dir_clus, const char *name, int want_dir,
u32 *out_clus, u32 *out_size,
u32 *out_ent_clus, u32 *out_ent_idx, u8 *out_attr) {
if (dir_clus == 0) dir_clus = f32w_c.root_clus;
u32 dclus = dir_clus;
u64 guard = 0;
fat32_lfn_buf lfn;
fat32_lfn_init(&lfn);
while (dclus >= 2 && dclus < 0x0FFFFFF8) {
if (++guard > (u64)f32w_c.fat_entries + 2) return -2;
if (f32w_dir_load(dclus) != 0) return -3;
u32 max_e = f32w_c.clus_bytes / 32;
for (u32 e = 0; e < max_e; e++) {
const u8 *ent = f32w_dir + e * 32;
if (ent[0] == 0x00) return -1; /* 目录结尾,未找到 */
if (ent[0] == 0xE5) { fat32_lfn_init(&lfn); continue; }
const f32w_dirent_t *de = (const f32w_dirent_t *)ent;
int is_short = fat32_lfn_process(&lfn, ent);
if (!is_short) continue;
if (de->attr & 0x08) { fat32_lfn_init(&lfn); continue; } /* 卷标 */
if (de->name[0] == '.' && (de->name[1] == ' ' || de->name[1] == '.')) {
fat32_lfn_init(&lfn); /* "." / ".." */
continue;
}
int entry_is_dir = (de->attr & 0x10) ? 1 : 0;
if (want_dir >= 0 && entry_is_dir != want_dir) {
fat32_lfn_init(&lfn);
continue;
}
int matched = 0;
if (lfn.valid) {
char ascii[FAT32_LFN_MAX];
if (fat32_lfn_to_ascii(&lfn, ascii, sizeof(ascii)) >= 0 &&
fat32_lfn_streq_ci(ascii, name)) matched = 1;
}
if (!matched) {
char disp[13];
fat32_lfn_short_to_str((const u8 *)de->name, disp);
if (fat32_lfn_streq_ci(disp, name)) matched = 1;
}
if (matched) {
u32 fc = (u32)f32w_r16((const u8 *)&de->clow) |
((u32)f32w_r16((const u8 *)&de->chigh) << 16);
if (out_clus) *out_clus = fc;
if (out_size) *out_size = de->fsize;
if (out_ent_clus) *out_ent_clus = dclus;
if (out_ent_idx) *out_ent_idx = e;
if (out_attr) *out_attr = de->attr;
return 0;
}
fat32_lfn_init(&lfn);
}
u32 next = 0;
if (f32w_fat_get(dclus, &next) != 0) return -3;
dclus = next;
}
return -1;
}
/* ---- 文件数据簇链写入(新建 / 复用扩展 / 截断收缩) ---- */
/* 写 size 字节数据。have>=2 时复用现有链(按需扩展或截断并释放多余簇),
* have<2 时全新分配。*out_first 输出结果首簇(size==0 时为 0)。
* 返回 0 成功,负值失败。 */
static inline int f32w_data_write(u32 have, const u8 *data, u32 size, u32 *out_first) {
u32 cb = f32w_c.clus_bytes;
u32 need = size ? (size + cb - 1) / cb : 0;
if (need == 0) {
if (have >= 2) {
if (f32w_free_chain(have) != 0) return -1;
}
*out_first = 0;
return 0;
}
u32 first = 0, prev = 0, count = 0;
if (have >= 2) {
first = have;
u32 cur = have;
while (cur >= 2 && cur < 0x0FFFFFF8 && count < need) {
prev = cur;
u32 next = 0;
if (f32w_fat_get(cur, &next) != 0) return -2;
cur = next;
count++;
}
while (count < need) { /* 现有链不足:扩展 */
u32 nc = f32w_alloc_free();
if (nc == 0) return -3;
if (f32w_fat_set(nc, F32W_EOC_VAL) != 0) return -4;
if (f32w_fat_set(prev, nc) != 0) return -4;
prev = nc;
count++;
}
if (cur >= 2 && cur < 0x0FFFFFF8) { /* 现有链过长:截断 */
if (f32w_fat_set(prev, F32W_EOC_VAL) != 0) return -4;
if (f32w_free_chain(cur) != 0) return -5;
}
} else {
for (u32 i = 0; i < need; i++) { /* 全新分配 */
u32 nc = f32w_alloc_free();
if (nc == 0) return -3;
if (f32w_fat_set(nc, F32W_EOC_VAL) != 0) return -4;
if (prev) {
if (f32w_fat_set(prev, nc) != 0) return -4;
} else {
first = nc;
}
prev = nc;
}
count = need;
}
/* 写数据:整簇直写源缓冲,尾簇经暂存缓冲零填充 */
u32 remaining = size;
const u8 *src = data;
u32 c = first;
for (u32 i = 0; i < count && remaining > 0; i++) {
u32 chunk = remaining < cb ? remaining : cb;
u32 lba = f32w_clus_lba(c);
if (chunk == cb) {
if (f32w_write_sectors(lba, f32w_c.spc, src) != 0) return -6;
} else {
f32w_mem_set(f32w_stage, 0, cb);
for (u32 b = 0; b < chunk; b++) f32w_stage[b] = src[b];
if (f32w_write_sectors(lba, f32w_c.spc, f32w_stage) != 0) return -6;
}
src += chunk;
remaining -= chunk;
if (i + 1 < count) {
u32 next = 0;
if (f32w_fat_get(c, &next) != 0) return -2;
c = next;
}
}
*out_first = first;
return 0;
}
/* ---- FSInfo 置未知(写操作后调用,best-effort) ----
* 分配/释放后空闲簇数不再准确,置 0xFFFFFFFF(未知)避免 UEFI/Windows
* 依赖陈旧计数。忽略失败(无 FSInfo 的镜像不受影响)。 */
static inline void f32w_fsinfo_unknown(void) {
if (!f32w_c.loaded) return;
u32 fsi_lba = 0;
{
/* 重新读 BPB 拿 FSInfo 扇区号(f32w_sec 此后未被覆盖过也可直接用) */
const f32w_bpb_t *b = (const f32w_bpb_t *)f32w_sec;
if (b->boot_sig != 0xAA55) return;
fsi_lba = b->fsi;
}
if (fsi_lba == 0 || fsi_lba == 0xFFFF) return;
u8 sec[512];
if (f32w_read_sectors(fsi_lba, 1, sec) != 0) return;
sec[488] = 0xFF; sec[489] = 0xFF; sec[490] = 0xFF; sec[491] = 0xFF;
sec[492] = 0xFF; sec[493] = 0xFF; sec[494] = 0xFF; sec[495] = 0xFF;
(void)f32w_write_sectors(fsi_lba, 1, sec);
}
/* ---- 写入 LFN 链 + 短名项(dir_alloc_slots 已定位 run 起点) ---- */
static inline int f32w_insert_entries(u32 dir_clus, const char *name,
const char name11[11], u32 clus, u32 size, u8 attr) {
u32 len = f32w_strlen(name);
int needs_lfn = 0;
/* needs_lfn 与调用方 make_unique_short 判定一致:重算(无损则短名回退匹配) */
{
char tmp11[11];
if (f32w_make_short(name, tmp11, &needs_lfn) != 0) return -1;
}
u32 nlfn = needs_lfn ? (len + 12) / 13 : 0;
u32 sc = 0, si = 0;
if (f32w_dir_alloc_slots(dir_clus, nlfn + 1, &sc, &si) != 0) return -2;
if (needs_lfn) {
u8 ck = fat32_lfn_checksum((const u8 *)name11);
for (int k = (int)nlfn; k >= 1; k--) { /* 逆序:最高序号最先落盘 */
u8 e[32];
f32w_build_lfn_entry(k, (int)nlfn, name, len, ck, e);
if (f32w_entry_write(sc, si, e) != 0) return -3;
if (k > 1 && f32w_slot_step(&sc, &si) != 0) return -4;
}
if (nlfn > 0 && f32w_slot_step(&sc, &si) != 0) return -4;
}
u8 se[32];
f32w_build_short_entry(name11, attr, clus, size, se);
if (f32w_entry_write(sc, si, se) != 0) return -3;
if (f32w_dir_flush() != 0) return -5;
return 0;
}
/* ================= 公共 API ================= */
/* 在目录中写/替换长名文件(LFN 创建 + 短名 ~N 唯一化 + FAT 链管理)。
* dir_clus : 目标目录首簇(0=根目录)
* name : 长文件名(ASCII;非 8.3 无损表示时自动生成 LFN 链)
* data/size: 文件内容(size 可为 0,簇链释放、大小清 0)
* 同名存在:文件原地替换内容(保留原目录项/LFN),目录返回 -20。
* 返回 0 成功,负值失败。 */
static inline int f32w_write_file(u32 dir_clus, const char *name,
const u8 *data, u32 size) {
if (f32w_bpb_load() != 0) return -1;
if (!f32w_blk_write) return -2;
if (!name || (!data && size)) return -3;
if (dir_clus == 0) dir_clus = f32w_c.root_clus;
u32 e_clus = 0, e_size = 0, ent_clus = 0, ent_idx = 0;
u8 e_attr = 0;
int fr = f32w_find(dir_clus, name, -1, &e_clus, &e_size, &ent_clus, &ent_idx, &e_attr);
if (fr == 0) {
if (e_attr & 0x10) return -20; /* 同名目录已存在 */
/* 原地替换:复用/扩展/截断原簇链,仅更新短名项 clus/size */
u32 first = 0;
if (f32w_data_write(e_clus, data, size, &first) != 0) return -4;
if (f32w_dir_load(ent_clus) != 0) return -5;
u8 *ent = f32w_dir + ent_idx * 32;
ent[20] = (u8)((first >> 24) & 0xFF); /* chigh */
ent[21] = (u8)((first >> 16) & 0xFF);
ent[26] = (u8)(first & 0xFF); /* clow */
ent[27] = (u8)((first >> 8) & 0xFF);
ent[28] = (u8)(size & 0xFF); /* fsize */
ent[29] = (u8)((size >> 8) & 0xFF);
ent[30] = (u8)((size >> 16) & 0xFF);
ent[31] = (u8)((size >> 24) & 0xFF);
f32w_dir_dirty = 1;
if (f32w_dir_flush() != 0) return -5;
f32w_fsinfo_unknown();
return 0;
}
/* 新建:唯一短名 -> 数据链 -> 目录槽位(含链扩展)-> LFN+短名项 */
char name11[11];
if (f32w_make_unique_short(dir_clus, name, name11) != 0) return -6;
u32 first = 0;
if (f32w_data_write(0, data, size, &first) != 0) return -7;
if (f32w_insert_entries(dir_clus, name, name11, first, size, 0x20) != 0) return -8;
f32w_fsinfo_unknown();
return 0;
}
/* 在目录中创建子目录(LFN + "." / ".." 项 + 父目录插入条目)。
* parent_clus: 父目录首簇(0=根目录)
* out_clus : 输出新目录首簇(可 NULL)
* 返回 0 成功,-20 同名已存在,其他负值失败。 */
static inline int f32w_mkdir(u32 parent_clus, const char *name, u32 *out_clus) {
if (f32w_bpb_load() != 0) return -1;
if (!f32w_blk_write) return -2;
if (!name) return -3;
if (parent_clus == 0) parent_clus = f32w_c.root_clus;
int fr = f32w_find(parent_clus, name, -1, 0, 0, 0, 0, 0);
if (fr == 0) return -20; /* 同名已存在 */
char name11[11];
if (f32w_make_unique_short(parent_clus, name, name11) != 0) return -6;
/* 分配目录簇并初始化 "." / ".." */
u32 nc = f32w_alloc_free();
if (nc == 0) return -21;
if (f32w_fat_set(nc, F32W_EOC_VAL) != 0) return -22;
if (f32w_dir_flush() != 0) return -23;
f32w_mem_set(f32w_dir, 0, f32w_c.clus_bytes);
{
u8 dot[32], dotdot[32];
char n_self[11] = {'.',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
char n_up[11] = {'.','.',' ',' ',' ',' ',' ',' ',' ',' ',' '};
u32 parent_link = (parent_clus == f32w_c.root_clus) ? 0 : parent_clus;
f32w_build_short_entry(n_self, 0x10, nc, 0, dot);
f32w_build_short_entry(n_up, 0x10, parent_link, 0, dotdot);
for (int i = 0; i < 32; i++) { f32w_dir[i] = dot[i]; f32w_dir[32 + i] = dotdot[i]; }
}
f32w_dir_no = nc;
f32w_dir_dirty = 1;
if (f32w_dir_flush() != 0) return -23;
/* 父目录插入条目(含链扩展) */
if (f32w_insert_entries(parent_clus, name, name11, nc, 0, 0x10) != 0) return -8;
f32w_fsinfo_unknown();
if (out_clus) *out_clus = nc;
return 0;
}
/* ---- 路径辅助('/' 分隔,每段长名;中间目录必须已存在) ---- */
/* 定位路径目录簇。空路径/"'/'" 返回根目录。 */
static inline int f32w_resolve_dir(const char *path, u32 *out_clus) {
if (!path || !out_clus) return -1;
if (f32w_bpb_load() != 0) return -2;
u32 cur = f32w_c.root_clus;
u32 pos = 0;
u32 path_len = f32w_strlen(path);
if (path_len == 0 || (path_len == 1 && path[0] == '/')) {
*out_clus = cur;
return 0;
}
while (pos < path_len) {
while (pos < path_len && path[pos] == '/') pos++;
if (pos >= path_len) break;
char comp[260];
u32 ci = 0;
while (pos < path_len && path[pos] != '/' && ci + 1 < sizeof(comp))
comp[ci++] = path[pos++];
comp[ci] = 0;
if (ci == 0) continue;
u32 sub = 0;
if (f32w_find(cur, comp, 1, &sub, 0, 0, 0, 0) != 0) return -3;
cur = sub;
}
*out_clus = cur;
return 0;
}
/* 按路径写文件(父目录必须已存在)。返回 0 成功,负值失败。 */
static inline int f32w_write_path(const char *path, const u8 *data, u32 size) {
if (!path) return -1;
if (f32w_bpb_load() != 0) return -2;
u32 path_len = f32w_strlen(path);
u32 last_slash = 0xFFFFFFFFu;
for (u32 i = 0; i < path_len; i++)
if (path[i] == '/') last_slash = i;
const char *fname = (last_slash == 0xFFFFFFFFu) ? path : path + last_slash + 1;
u32 dir_clus = 0;
if (last_slash == 0xFFFFFFFFu) {
dir_clus = f32w_c.root_clus;
} else {
char dirpath[260];
u32 dn = last_slash;
if (dn >= sizeof(dirpath)) return -3;
for (u32 i = 0; i < dn; i++) dirpath[i] = path[i];
dirpath[dn] = 0;
if (f32w_resolve_dir(dirpath, &dir_clus) != 0) return -4;
}
return f32w_write_file(dir_clus, fname, data, size);
}
/* 按路径创建目录(仅最后一段被创建,中间目录必须已存在)。返回 0 成功。 */
static inline int f32w_mkdir_path(const char *path, u32 *out_clus) {
if (!path) return -1;
if (f32w_bpb_load() != 0) return -2;
u32 path_len = f32w_strlen(path);
if (path_len == 0) return -3;
/* 去尾部 '/' */
while (path_len > 0 && path[path_len - 1] == '/') path_len--;
if (path_len == 0) return -3;
u32 last_slash = 0xFFFFFFFFu;
for (u32 i = 0; i < path_len; i++)
if (path[i] == '/') last_slash = i;
const char *dname = (last_slash == 0xFFFFFFFFu) ? path : path + last_slash + 1;
if ((u32)(dname - path) >= path_len) return -3; /* 防御:末段为空 */
u32 parent_clus = 0;
if (last_slash == 0xFFFFFFFFu) {
parent_clus = f32w_c.root_clus;
} else {
char dirpath[260];
u32 dn = last_slash;
if (dn >= sizeof(dirpath)) return -4;
for (u32 i = 0; i < dn; i++) dirpath[i] = path[i];
dirpath[dn] = 0;
if (f32w_resolve_dir(dirpath, &parent_clus) != 0) return -5;
}
return f32w_mkdir(parent_clus, dname, out_clus);
}
#endif /* DESHAB_FAT32_WRITE_H */