Linux fat_add_cluster FAT32簇链与shortname生成
2026/6/15 3:07:50 网站建设 项目流程

Linux fat_add_cluster FAT32簇链与shortname生成

FAT32文件系统使用FAT(File Allocation Table)表来管理文件数据的簇分配。每个文件对应一条簇链,簇链中的每一簇指向文件数据的一个逻辑块,最后一个簇的值范围为0xFFFFFFF8-0xFFFFFFF(EOC标记)。fat_add_cluster()是FAT32簇分配的核心函数,负责扩展文件的簇链。

簇分配函数fat_add_cluster()实现在fatent.c中:

int fat_add_cluster(struct inode *inode)
{
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
struct super_block *sb = inode->i_sb;
struct fat_entry fatent;
unsigned long count;
int err;
int nr_cluster;

nr_cluster = EXTRA_ENTRY_OFFSET +
(sbi->fat_bits == 32 ? 1 : 0);

fatent_init(&fatent);
err = fat_alloc_clusters(inode, &nr_cluster, &count);
if (err)
return err;

fatent.fat_inode = MSDOS_SB(sb)->fat_inode;
fatent.bh = NULL;
fatent.entry = 0;
fatent.nr_bhs = 0;

在调用fat_alloc_clusters()分配新簇之前,需要确定FAT表需要预留多少个簇。对于FAT32,需要额外预留1个簇用于写入EOC标记。fat_alloc_clusters()扫描FAT表,从空闲簇中分配连续(或非连续)的簇:

int fat_alloc_clusters(struct inode *inode, int *cluster_count,
unsigned long *total)
{
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
struct super_block *sb = inode->i_sb;
struct fat_entry fatent;
unsigned long search_start, search_end;
int err;

fatent_init(&fatent);
search_start = sbi->fat_clusters;
search_end = search_start + *cluster_count;

if (sbi->free_clusters == 0)
return -ENOSPC;

if (sbi->free_cluseters < *cluster_count)
*cluster_count = sbi->free_cluseters;

err = fat_ent_read(inode, &fatent, &search_start);
if (err && err != -ENOENT)
return err;

for (; search_start < search_end; search_start++) {
err = fat_ent_read(inode, &fatent, search_start);
if (err)
goto out;

if (fatent.entry != FAT_ENT_FREE)
continue;

err = fat_ent_write(inode, &fatent, FAT_ENT_EOF, FAT_ENT_EOF);
if (err)
goto out;

(*total)++;
fat_cache_add(inode, *total - 1, fatent.entry);
}

sbi->free_clusters -= *total;
fat_sync_fsinfo(sbi);
return 0;
}

fat_alloc_clusters()从search_start开始线性扫描FAT表,查找空闲簇(FAT entry值为0)。每个空闲簇被标记为FAT_ENT_EOF(FAT32下为0x0FFFFFFF)以临时占用。然后递减free_clusters计数并同步FSInfo扇区。fat_cache_add()将新分配的簇加入inode的fat_cache,用于后续的快速簇链遍历。

分配完成后,fat_add_cluster()将新簇链接到文件的已有簇链末尾:

if (sbi->fat_bits == 32) {
struct fat_entry fatent_prev;
u32 prev_entry;

fatent_prev.fat_inode = MSDOS_SB(sb)->fat_inode;
fatent_prev.bh = NULL;
fatent_prev.entry = 0;

prev_entry = fat_get_entry(inode, inode->i_blocks >> sbi->sec_per_clus_bits);
fat_ent_read(inode, &fatent_prev, prev_entry);
fat_ent_write(inode, &fatent_prev, fatent.entry, fatent.entry);
fat_ent_write(inode, &fatent, FAT_ENT_EOF, FAT_ENT_EOF);
}

fat_cache_inval_inode(inode);
inode->i_blocks += nr_cluster << sbi->sec_per_clus_bits;
return 0;
}

关键操作是读取文件的最后一个簇号(通过fat_get_entry()根据i_blocks计算出的逻辑簇号位置),然后将该簇的FAT表项更新指向新分配的簇号,再将新簇的FAT表项设置为EOC标记,形成完整的簇链。

FAT32目录项中的文件名存储采用shortname(短文件名)和longname(长文件名)两种编码方式。shortname使用8.3格式(8个字符文件名+3个字符扩展名),全部大写ASCII字符。longname通过一系列连续的目录项(LDIR entry)存储,每个LDIR entry包含文件名的一部分。

shortname的生成函数为fat_make_shortname():

static int fat_make_shortname(struct msdos_sb_info *sbi,
const unsigned char *name,
unsigned char *shortname,
int *shortlen)
{
int i, j;
int dotpos;
unsigned char buf[12];
int len = strlen(name);
unsigned char c;

dotpos = fat_find_dot(name, len);
memset(buf, ' ', 11);

for (i = 0; i < dotpos; i++) {
c = name[i];
if (c == '.' || i >= 8)
break;
c = fat_toupper(sbi, c);
if (!fat_valid_shortname_char(c))
c = '_';
if (i < 8)
buf[i] = c;
}

i = dotpos + 1;
for (j = 0; j < 3 && i < len; j++, i++) {
c = name[i];
c = fat_toupper(sbi, c);
if (!fat_valid_shortname_char(c))
c = '_';
buf[8 + j] = c;
}

memcpy(shortname, buf, 11);
*shortlen = 11;
return 0;
}

生成逻辑:以文件名中的第一个点(dot)为界,左侧最多8个字符填充到buf[0-7],右侧最多3个字符填充到buf[8-10]。所有字符通过fat_toupper()转换为大写,非法字符(包括小写字母在非VFAT模式下)替换为下划线。

当生成的shortname与同一目录中的已有shortname冲突时,fat_add_shortname_hook()生成变体:

static int fat_add_shortname_hook(struct inode *dir,
unsigned char *shortname,
int *shortlen)
{
struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
int i;
int base_len = min_t(int, 8, *shortlen);
unsigned char base[8];
unsigned char tail[4];
int num;

memcpy(base, shortname, base_len);
for (num = 1; num < 100000; num++) {
snprintf(tail, 4, "~%d", num);
memcpy(shortname, base, base_len - strlen(tail));
memcpy(shortname + base_len - strlen(tail), tail, strlen(tail));
if (!fat_find_exact_shortname(dir, shortname, *shortlen))
return 0;
}
return -EINVAL;
}

变体规则是保留文件名的前N个字符后接"~N"(如FILE~1、FILE~2),直到生成一个在目录中唯一的短名。生成上限为99999个变体,超过仍冲突则返回-EINVAL。

FAT目录项写入时,longname的checksum与shortname关联。fat_checksum()函数对shortname的11个字节计算校验和,每个longname目录项包含该checksum用于一致性验证。当删除或重命名操作修改shortname时,longname的checksum必须同步更新。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询