分区管理
分区识别机制
目前支持两种主流的分区表格式:
GPT(GUID分区表)
GPT是现代系统广泛使用的分区表格式,具有以下特点:
- 支持128个主分区
- 使用128位GUID唯一标识分区类型
- 支持分区名称(UTF-16LE编码)
- 包含备份分区表
- 支持CRC32校验
GPT解析过程如下:
- 读取LBA 1处的GPT头
- 验证签名("EFI PART")
- 解析分区项位置和数量
- 读取分区项数组
- 提取分区信息
MBR(主引导记录)
MBR是传统的分区表格式,虽然功能有限,但仍广泛使用:
- 支持4个主分区
- 使用分区类型标识(1字节)
- 支持扩展分区
当GPT解析失败时,系统会尝试解析MBR分区 表,确保向后兼容。
分区扫描流程
分区扫描是系统启动时的关键步骤,流程如下:
- 初始化块设备:创建
Disk对象,获取设备信息 - 尝试GPT解析:读取GPT头,验证签名
- 解析分区项:读取分区项数组,提取分区信息
- MBR回退:如果GPT解析失败,尝试MBR
- 全盘处理:如果都没有分区表,将整个磁盘作为单个分区
- 文件系统检测:对每个分区进行文件系统类型检测
分区信息结构
分区信息通过PartitionInfo结构体表示:
pub struct PartitionInfo {
pub index: u32, // 分区索引(0-based)
pub name: String, // 分区名称
pub partition_type_guid: [u8; 16], // 分区类型GUID
pub unique_partition_guid: [u8; 16], // 唯一分区GUID
pub filesystem_uuid: Option<String>, // 文件系统UUID
pub starting_lba: u64, // 起始LBA
pub ending_lba: u64, // 结束LBA
pub size_bytes: u64, // 分区大小(字节)
pub filesystem_type: Option<FilesystemType>, // 文件系统类型
}
这种结构设计包含了分区的所有关键信息,便于上层文件系统使用。
文件系统探测机制
文件系统类型检测
文件系统类型检测是通过分析分区开始的特定数据结构实现的,目前支持:
FAT文件系统检测
FAT文件系统(FAT12/FAT16/FAT32)的检测基于以下特征:
- FAT12/FAT16:在偏移0x36处有"FAT"签名
- FAT32:在偏移0x52处有"FAT32"签名
fn is_fat_filesystem(boot_sector: &[u8; 512]) -> bool {
// 检查FAT12/FAT16/FAT32签名
if boot_sector.len() >= 0x36 + 3 {
let fat_sig = &boot_sector[0x36..0x36 + 3];
if fat_sig == b"FAT" {
return true;
}
}
if boot_sector.len() >= 0x52 + 5 {
let fat32_sig = &boot_sector[0x52..0x52 + 5];
if fat32_sig == b"FAT32" {
return true;
}
}
false
}
检测过程:
- 读取分区开始的512字节(引导扇区)
- 检查特定偏移处的签名
- 确定FAT类型
ext4文件系统检测
ext4文件系统的检测基于超级块特征:
- 超级块位于分区偏移1024字节处
- 魔数0xEF53位于超级块偏移1080字节处
fn is_ext4_filesystem(disk: &mut Disk, start_lba: u64) -> bool {
// ext4超级块位于分区偏移1024字节处
let superblock_offset = start_lba * 512 + 1024;
let mut superblock = [0u8; 2048];
// 保存当前位置
let pos = disk.position();
// 设置位置读取超级块
disk.set_position(superblock_offset);
let result = if let Err(_) = read_exact(disk, &mut superblock) {
warn!("Failed to read ext4 superblock at offset {}", superblock_offset);
false
} else {
// 检查ext4魔数(0xEF53)位于超级块偏移1080字节处
// 由于我们从1024字节处开始读取,魔数位于索引56处
if superblock.len() >= 58 {
let magic = u16::from_le_bytes([superblock[56], superblock[57]]);
magic == 0xEF53
} else {
false
}
};
// 恢复位置
disk.set_position(pos);
result
}
检测过程:
- 定位到超级块位置
- 读取超级块数据
- 验证魔数
文件系统UUID读取
为了支持通过UUID挂载文件系统,实现了从文件系统超级块读取UUID的功能:
ext4 UUID读取
ext4的UUID位于超级块偏移0x68处,共16字节。UUID的存储格式为:
- 前3个字段:小端序
- 后2个字段:大端序
读取后转换为标准UUID格式(8-4-4-4-12)。
FAT32 UUID读取
FAT32没有标准UUID,但有卷标ID(Volume ID),位于引导扇区偏移0x43处,共4字节。读取后格式化为8字符十六进制字符串。
文件系统创建
对于检测到的文件系统,创建对应的文件系统实例:
- 创建分区包装器:将分区包装为文件系统可访问的设备
- 初始化文件系统:调用文件系统的初始化函数
- 创建根节点:创建文件系统的根目录节点
- 注册到VFS:将文件系统注册到虚拟文件系统
根文件系统选择策略
支持多种根文件系统选择策略:
-
启动参数指定:通过
root=参数指定root=/dev/sdaX:按设备路径指定root=PARTUUID=xxx:按分区GUID指定root=UUID=xxx:按文件系统UUID指定
-
默认策略:如果没有指定,使用第一个支持的文件系统分区
-
回退策略:如果没有支持的文件系统,使用ramfs作为根文件系统