构建64位操作系统-文件系统,虚拟文件系统

1.文件系统

1.1.论述

1.1.1.概述

        存储设备容量越来越大,为了有效管理存储信息,产生文件,文件系统。

        文件是一个抽象概念,它有组织地将多个数据块管理起来,以确保有足够存储空间容纳数据。

        一个文件通常由文件信息,数据两部分组成。

        文件系统通常包含超级块,目录项,数据区三部分。

        超级块:

        主要用于记录文件系统的全局信息。

        目录项:

        主要作用是保存目录的名字,长度,属性,数据块索引表,操作时间等。

        只需使用目录项属性信息的标志位便可区分目录,文件。

        数据块索引表记录着文件数据与数据块的线性映射关系。

        数据区:

        对于目录,其数据区存储的是维护目录层级关系的子目录项。这些子目录项可以代表文件,也可代表目录。

        对于文件,其数据区存储的是文件的数据。

1.1.2.FAT32文件系统

        1.硬盘引导扇区

        通过硬盘MBR主引导扇区的硬盘分区表划分的磁盘分区。

        硬盘分区表共4项,故一块硬盘最多可拥有4个主磁盘分区。每个表项的分区类型可指定分区的文件系统类型。

        扩展磁盘分区可认为是一种主磁盘分区类型(类型值0x0F),只不过扩展磁盘分区装载的不是文件系统,而是一个个动态的磁盘分区。相当于将扩展磁盘分区重新虚拟成一块硬盘。扩展磁盘分区的硬盘分区表仅有两项:一项记录当前分区的类型,大小,起始LBA扇区号等信息。另一项称为逻辑分区,它记录着下一个分区的容量,起始LBA扇区号等,类型值为0x05。

        通过主引导扇区的硬盘分区表,可索引出FAT32文件系统的位置。

        FAT32文件系统的分区类型为0x0B。

        硬盘引导扇区由引导代码(0x000~0x1BD),硬盘分区表(0x1BE~0x1FD)及结束符0xAA55组成。其中,硬盘分区表包含4项,每项占据16B。

OffLenDesc
01引导标识符
11起始磁头号(对机械硬盘有效)
22起始扇区和柱面号(对机械硬盘有效)
41分区类型ID值
51结束磁头号(对机械硬盘有效)
62结束扇区号和柱面号(对机械硬盘有效)
84起始逻辑扇区(LBA)
124分区占用的磁盘扇区数

        2.FAT32分区

        FAT32分区里,将扇区按性质划分:

        保留区域:包含引导扇区,FSInfo等。

        FAT区域:FAT1,FAT2

        数据区域:根目录文件的数据存储在数据区域起始部分。

        引导扇区:

        引导扇区负责保存FAT文件系统的重要数据信息。

FAT32名称偏移长度描述
BS_jmpBoot03跳转指令
BS_OEMName38厂商名
BPB_BytesPerSec112每扇区字节数
BPB_SecPerClus131每簇扇区数
BPB_RsvdSecCnt142保留扇区数
BPB_NumFATs161FAT表份数
BPB_RootEntCnt172根目录可容纳目录项数(设置为0)
BPB_TotSec16192扇区总数(32位表示下,这里为0)
BPB_Media211介质描述符
BPB_FATSz16222每FAT扇区数(32位表示下,这里为0)
BPB_SecPerTrk242每磁道扇区数(对机械硬盘有效)
BPB_NumHeads262磁头数(对机械硬盘有效)
BPB_HiddSec284隐藏扇区数
BPB_TotSec32324扇区总数(32位表示下)
BPB_FATSz32364每FAT扇区数(32位表示下)
BPB_ExtFlags402

扩展标志。

bit 0~3:活动FAT表索引

bit 7:0,实时更新所有FAT表更新。1,仅更新bit 0~3指定的FAT表。

BPB_FSVer422版本号。高字节为主版本号,低字节为次版本号。
BPB_RootClus444根目录起始簇号(规定根目录文件首个簇位置)
BPB_FSInfo482FSInfo结构体的起始扇区号
BPB_BkBootSec502引导扇区的备份扇区的编号
BPB_Reserved5212保留
BS_DrvNum641驱动器号
BS_Reserved1651保留
BS_BootSig661扩展引导标记
BS_VolID674卷序列号
BS_VolLab7111卷标
BS_FileSysType828文件系统类型
引导代码90420引导代码,数据及其他信息
结束标志5102结束标志0xaa55

        BPB_RsvdSecCnt:

        保留区域占据扇区数量从硬盘分区的第一个扇区开始计数。包含引导扇区。

        保留区域之后是FAT1,FAT2,再然后是数据区域。数据区域起始部分的簇号是2。

        BPB_HiddSec:

        隐藏扇区数指LBA 0扇区与文件系统起始LBA扇区间的扇区数。

        BPB_RootEntCnt:

        对FAT32文件系统,该值需为0。

        BPB_RootClus:

        根目录的起始簇号。通常为2,而数据区的起始簇号也是2。

        BPB_FSInfo:

        指示FSInfo结构在保留区域中的扇区号。

        BPB_BkBootSec:

        指示引导扇区在保留区域的备份位置扇区号。FAT32文件系统在备份引导扇区时,也会对FSInfo扇区进行备份,其备份位置往往紧随引导扇区备份之后。

        数据区起始扇区号:

        BPB_RsvdSecCnt + BPB_FATSz32 * BPB_NumFATs

        数据区的起始簇号是2,假设待访问的簇号为N,则计算簇N起始扇区号的公式:

        簇N的起始扇区号=((N - 2) * BPB_SecPerClus) + 数据区起始扇区号

        FSInfo扇区:

        FAT32在保留区域加入一个辅助性扇区结构FSInfo,来帮助文件系统记录信息。

NameOffLenDesc
FSI_LeadSig04FSInfo扇区标识符。固定为0x41615252。
FSI_Reserved14480保留
FSI_StrucSig4844标识。固定为0x61417272。
FSI_Free_Count4884上次记录的空闲簇数量。参考值。如为0xFFFFFFFF,表示需重新计算。
FSI_Nxt_Free4924空闲簇起始搜索位置。参考值。如为0xFFFFFFFF,则需从簇2开始搜索空闲簇。
FSI_Reserved249612保留
FSI_TrailSig5084结束标志,固定为0xaa550000。

        FAT表: 

        FAT32文件系统的每个FAT表项占据32比特位。仅低28比特位是有效FAT表项位。高4比特位保留使用。

FAT项实例值                                               描述
00x0FFFFFF8标示字,低字节与BPB_Media一致。
10xFFFFFFFF第一个簇已经被占用
2~后续

0x?0000000 可用簇

0x?0000002~0x?FFFFFEF 标识下个簇的簇号

0x?FFFFFF0~0x?FFFFFF6 保留簇

0x?FFFFFF7 坏簇
0x?FFFFFF8~0x?FFFFFFF 文件最后一个簇

        短目录项:

NameOffLenDesc
DIR_Name011基础名8B,扩展名3B
DIR_Attr111

文件属性

0x01=ATTR_READ_ONLY

0x02=ATTR_HIDDEN

0x04=ATTR_SYSTEM

0x08=ATTR_VOLUME_ID

0x10=ATTR_DIRECTORY

0x20=ATTR_ARCHIVE

0x0F=ATTR_LONG_NAME

DIR_NTRes121保留
DIR_CrtTimeTenth131文件创建的毫秒级时间戳
DIR_CrtTime142文件创建时间
DIR_CrtDate162文件创建日期
DIR_LastAccDate182最后访问日期
DIR_FstClusHI202起始簇号(高字)
DIR_WrtTime222最后写入时间
DIR_WrtDate242最后写入日期
DIR_FstClusLO262起始簇号(低字)
DIR_FileSize284文件大小

        1.文件名

        由8B的基础名和3B的扩展名组成。只能保存字母,数字,有限的几个字符。如这两部分字符串的长度不足,将使用空格符(0x20)补齐。

        文件名字符串的第一个字符为0xE5,0x00,0x05时,表明此目录项为无效目录项或空闲目录项,且DIR_Name[0]的数值不允许为0x20,同时基础名字符串间也不允许出现空格符。

        对于短目录项而言,它不区分大小写。任何文件名与目录名均以大写字母记录和显示。

         2.文件属性

        卷标文件用于为硬盘分区命名。它与引导扇区的BS_VolLab成员功能相同,其位于文件系统根目录中。在FAT类文件系统里,有且仅有一个文件可设置该属性标志位,且该目录项的起始簇号需为0。

        存档属性是文件的备份功能,它记录了自上次备份后又有哪些文件被改动过。当对文件执行创建,重命名,写入时,该标志位被置位。

        3.日期/时间格式

        FAT32的日期格式

位域描述
bit 0~4日,取值范围1~31
bit 5~8月,取值范围1~12
bit 9~15年,从1980开始计算,取值范围0~127

        FAT32的时间格式

位域描述
bit 0~4秒,每2秒一次步进,取值范围0~29
bit 5~10分,取值范围0~59
bit 11~15小时,取值范围0~23

        起始簇号:

        将32比特位的起始簇号分割为高16比特位,低16比特位两段,存储在不同的成员变量中。

        长目录项: 

        将文件名的编码方式从ASCII升级为Unicode,使得文件名不仅可区分大小写字母,还支持更多种语言符号。弥补了短目录项在文件名记录上的不足,算是对文件名的补充说明。

        对windows,如果文件名比较短下,只需文件只需一个短目录项即可。

        对linux,任何情况下,均有长目录项。

        存储组织上,短目录项关联的长目录项存在下,先是存储长目录项,然后是短目录项。

NameOffLenDesc
LDIR_Ord01长目录项的序号(对最后一个长目录项这里为:序号&0x04)
LDIR_Name1110长文件名的第1~5个字符,每个字符占2B,采用Unicode编码
LDIR_Attr111文件属性必须为ATTR_LONG_NAME
LDIR_Type121为0
LDIR_Chksum131短文件名的校验和
LDIR_Name21412长文件名的第6~11个字符,每个字符占2B,采用Unicode编码
LDIR_FstClusLO262需为0
LDIR_Name3284长文件名的第12~13个字符,每个字符占2B,采用Unicode编码

        长文件名:

        LDIR_Name1/2/3是长目录项的三个字符串存储区域,字符串中的每个字符用2B的Unicode码表示,并以空字符结尾。剩余字符串空间以0xFFFF填充,一个完整的长文件名是由一组连续的长目录项组成。

        校验和:

        长目录项的校验和是通过短目录项的文件名计算得来。

unsigned char checksum = 0;
for(int i = 0; i < 11; i++)
{
    checksum = ((checksum & 1) ? 0x80:0)+(checksum >> 1) + DIR_Name[i]
}

        长目录项序号:

        长目录项的起始序号为1,对于记录长文件名的最后一个长目录项而言,其序号成员变量的第6位必须置位以表示结尾。 

        其他:

        1.文件新创建时,只是在隶属的父目录的数据区里新增关联的目录项。不会实际为新文件分配存储数据的簇。

        2.目录文件新创建时,因为新目录必然包含. ..两个项,所以,既会在隶属的父目录的数据区里新增关联的目录项。也会实际为新目录文件分配存储数据的簇。且簇中会包含. ..对应的目录项。

        3.对windows,文件名自身较短,且基础名,扩展名含有的字母全大写或全小写时,借助短目录项的DIR_NTRes可以只用短目录项来解释文件。

        此时,DIR_NTRes解释如下:

数值描述
0x00扩展名大写,基础名大写
0x08扩展名大写,基础名小写
0x10扩展名小写,基础名大写
0x18扩展名小写,基础名小写

1.2.实践

1.2.1.FAT32文件系统

        1.清空硬盘扇区

        2.建立FAT32主分区

// fat32.h
// 硬盘分区表项
struct Disk_Partition_Table_Entry
{
	// 引导标识
	unsigned char flags;
	// 起始磁头号--对机械硬盘适用
	unsigned char start_head;
	// 扇区低6位,柱面高10位--对机械硬盘适用
	unsigned short start_sector :6,		    //0~5
			       start_cylinder :10;		//6~15
	// 分区类型
	unsigned char type;
	// 结束磁头号--对机械硬盘适用
	unsigned char end_head;
	// 扇区低6位,柱面高10位--对机械硬盘适用
	unsigned short end_sector :6,			//0~5
			       end_cylinder :10;		//6~15
	// 起始扇区LBA--对固态硬盘适用
	unsigned int start_LBA;
	// 分区占用扇区数
	unsigned int sectors_limit;
}__attribute__((packed));

// 硬盘引导扇区结构
struct Disk_Partition_Table
{
	// 引导代码部分
	unsigned char BS_Reserved[446];
	// 硬盘分区表项
	struct Disk_Partition_Table_Entry DPTE[4];
	// 0xAA55
	unsigned short BS_TrailSig;
}__attribute__((packed));

// FAT32文件系统引导扇区
struct FAT32_BootSector
{
	// 跳转指令
	unsigned char BS_jmpBoot[3];
	// 生产厂商名
	unsigned char BS_OEMName[8];
	// 每扇区字节数
	unsigned short BPB_BytesPerSec;
	// 每簇扇区数
	unsigned char BPB_SecPerClus;
	// 保留扇区数
	unsigned short BPB_RsvdSecCnt;
	// FAT表的份数
	unsigned char BPB_NumFATs;
	// 根目录可容纳的目录项数
	unsigned short BPB_RootEntCnt;
	// 扇区数--可用16个比特位表示下适用
	unsigned short BPB_TotSec16;
	// 介质描述符
	unsigned char BPB_Media;
	// 每FAT扇区数--可用16个比特位表示下适用
	unsigned short BPB_FATSz16;
	// 每磁道扇区数--机械硬盘适用
	unsigned short BPB_SecPerTrk;
	// 磁头数--机械硬盘适用
	unsigned short BPB_NumHeads;
	// 隐藏扇区数
	unsigned int BPB_HiddSec;
	// 扇区数--需用32个比特位表示下适用
	unsigned int BPB_TotSec32;
	// 每FAT扇区数--需用32个比特位表示下适用
	unsigned int BPB_FATSz32;
	// 扩展标志
	// bit 0~3:活动FAT表索引
	unsigned short BPB_ExtFlags;
	// 版本号,高字节为主版本,低字节为次版本
	unsigned short BPB_FSVer;
	// 根目录起始簇号,一般是2
	unsigned int BPB_RootClus;
	// FSInfo结构的扇区号
	unsigned short BPB_FSInfo;
	// 引导扇区备份扇区号
	unsigned short BPB_BkBootSec;
	// 保留
	unsigned char BPB_Reserved[12];
	// 驱动器号
	unsigned char BS_DrvNum;
	// 保留
	unsigned char BS_Reserved1;
	// 扩展引导标记--0x29
	unsigned char BS_BootSig;
	// 卷序列号
	unsigned int BS_VolID;
	// 卷标
	unsigned char BS_VolLab[11];
	// 文件系统类型
	unsigned char BS_FilSysType[8];
	// 引导代码
	unsigned char BootCode[420];
	// 结束标志--0xaa55
	unsigned short BS_TrailSig;
}__attribute__((packed));

// FSInfo结构
struct FAT32_FSInfo
{
	// FSInfo扇区标识符--固定为0x41615252
	unsigned int FSI_LeadSig;
	// 保留
	unsigned char FSI_Reserved1[480];
	// 标识--固定为0x61417272
	unsigned int FSI_StrucSig;
	// 空闲簇数量。
	// 如为0xFFFFFFFF,说明空闲簇数量需重新计算。
	unsigned int FSI_Free_Count;
	// 空闲簇起始搜索位置。
	// 如为0xFFFFFFFF,说明需从簇2开始搜索
	unsigned int FSI_Nxt_Free;
	// 保留
	unsigned char FSI_Reserved2[12];
	// 结束标志--0xaa550000
	unsigned int FSI_TrailSig;
}__attribute__((packed));

// 只读
#define	ATTR_READ_ONLY	(1 << 0)
// 隐藏
#define ATTR_HIDDEN	    (1 << 1)
// 系统
#define ATTR_SYSTEM	    (1 << 2)
// 卷标
#define ATTR_VOLUME_ID	(1 << 3)
// 目录
#define ATTR_DIRECTORY	(1 << 4)
// 存档
#define ATTR_ARCHIVE	(1 << 5)
// 长文件名
#define ATTR_LONG_NAME	(ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID)

// 短目录项
struct FAT32_Directory
{
	// 基础名(8)+扩展名(3)
	unsigned char DIR_Name[11];
	// 文件属性
	unsigned char DIR_Attr;
	// windows上适用
	unsigned char DIR_NTRes;	
	// 文件创建的毫秒级时间戳
	unsigned char DIR_CrtTimeTenth;
	// 文件创建时间
	unsigned short DIR_CrtTime;
	// 文件创建日期	
	unsigned short DIR_CrtDate;
	// 最后访问日期
	unsigned short DIR_LastAccDate;
	// 起始簇号(高字)
	unsigned short DIR_FstClusHI;
	// 最后写入时间
	unsigned short DIR_WrtTime;
	// 最后写入日期
	unsigned short DIR_WrtDate;
	// 起始簇号(低字)
	unsigned short DIR_FstClusLO;
	// 文件尺寸
	unsigned int DIR_FileSize;
}__attribute__((packed));

#define LOWERCASE_BASE (8)
#define LOWERCASE_EXT (16)

// 长目录项
struct FAT32_LongDirectory
{
	// 长目录项序号--从1计数。最后一个长目录项为:计数值 & 0x40
	unsigned char LDIR_Ord;
	// 5个字符。每个字符占2B,采用Unicode编码。
	unsigned short LDIR_Name1[5];
	// 必须为ATTR_LONG_NAME
	unsigned char LDIR_Attr;
	// 需为0
	unsigned char LDIR_Type;
	// 校验和--通过对短文件名计算获得
	unsigned char LDIR_Chksum;
	// 6个字符。每个字符占2B,采用Unicode编码。
	unsigned short LDIR_Name2[6];
	// 需为0
	unsigned short LDIR_FstClusLO;
	// 2个字符。每个字符占2B,采用Unicode编码。
	unsigned short LDIR_Name3[2];
}__attribute__((packed));


// fat32.c
// 硬盘引导扇区
struct Disk_Partition_Table DPT;
// FAT32文件系统引导扇区
struct FAT32_BootSector fat32_bootsector;
// FAT32中FSInfo结构
struct FAT32_FSInfo fat32_fsinfo;
// 辅助计算
unsigned long FirstDataSector = 0;
unsigned long BytesPerClus = 0;
unsigned long FirstFAT1Sector = 0;
unsigned long FirstFAT2Sector = 0;
// 初始化
void DISK1_FAT32_FS_init()
{
	int i;
	unsigned char buf[512];
	memset(buf, 0, 512);

	struct FAT32_Directory *dentry = NULL;
	// 从硬盘读取编号0x0的1个扇区
	IDE_device_operation.transfer(ATA_READ_CMD, 0x0, 1, (unsigned char *)buf);
	// 按硬盘引导扇区解释
	DPT = *(struct Disk_Partition_Table *)buf;
	//	for(i = 0 ;i < 512 ; i++)
	//		color_printk(PURPLE,WHITE,"%02x",buf[i]);
	// 获得主分区的起始LBA,类型
	color_printk(BLUE, BLACK, "DPTE[0] start_LBA:%#018lx\ttype:%#018lx\n",
		DPT.DPTE[0].start_LBA, DPT.DPTE[0].type);
	memset(buf, 0, 512);
	// 读取主分区引导扇区
	IDE_device_operation.transfer(ATA_READ_CMD, DPT.DPTE[0].start_LBA, 1, (unsigned char *)buf);
	// 按FAT32引导扇区解释
	fat32_bootsector = *(struct FAT32_BootSector *)buf;
	//	for(i = 0 ;i < 512 ; i++)
	//		color_printk(PURPLE,WHITE,"%02x",buf[i]);	
	// 打印FSInfo结构位置
	// 打印引导扇区备份扇区
	// 打印扇区总数
	color_printk(BLUE, BLACK, 
		"FAT32 Boot Sector\n\tBPB_FSInfo:%#018lx\n\tBPB_BkBootSec:%#018lx\n\tBPB_TotSec32:%#018lx\n",
		fat32_bootsector.BPB_FSInfo, fat32_bootsector.BPB_BkBootSec, fat32_bootsector.BPB_TotSec32);
	memset(buf, 0, 512);
	// 读取FSInfo结构
	IDE_device_operation.transfer(ATA_READ_CMD, DPT.DPTE[0].start_LBA + fat32_bootsector.BPB_FSInfo, 1, (unsigned char *)buf);
	fat32_fsinfo = *(struct FAT32_FSInfo *)buf;
	//	for(i = 0 ;i < 512 ; i++)
	//		color_printk(PURPLE,WHITE,"%02x",buf[i]);	
	// 打印标识1,打印标识2,打印空闲簇数
	color_printk(BLUE, BLACK, 
		"FAT32 FSInfo\n\tFSI_LeadSig:%#018lx\n\tFSI_StrucSig:%#018lx\n\tFSI_Free_Count:%#018lx\n",
		fat32_fsinfo.FSI_LeadSig, fat32_fsinfo.FSI_StrucSig, fat32_fsinfo.FSI_Free_Count);
	
	// 辅助变量
	// FAT32文件系统存储空间布局
	// 1.保留扇区部分
	// 引导扇区
	// 此后若干扇区
	// 引导扇区及此后若干扇区构成保留扇区
	// 2.FAT1部分
	// 3.FAT2部分
	// 4.数据部分
	// 数据部分起始部分用于作用根目录的数据区。
	// 目录的数据区内容是一个个目录项。
	// 数据区首个扇区编号
	// 一般将数据区起始簇号设置为2。
	FirstDataSector = DPT.DPTE[0].start_LBA + fat32_bootsector.BPB_RsvdSecCnt + fat32_bootsector.BPB_FATSz32 * fat32_bootsector.BPB_NumFATs;
	// FAT1表首个扇区编号
	FirstFAT1Sector = DPT.DPTE[0].start_LBA + fat32_bootsector.BPB_RsvdSecCnt;
	// FAT2表首个扇区编号
	FirstFAT2Sector = FirstFAT1Sector + fat32_bootsector.BPB_FATSz32;
	// 每个簇扇区数*每个扇区字节数=每个簇字节尺寸
	BytesPerClus = fat32_bootsector.BPB_SecPerClus * fat32_bootsector.BPB_BytesPerSec;
	// 参数1是文件完整路径名
	// 参数2是标志集合
	// 返回值是搜索路径的短目录项
	dentry = path_walk("/a.TXT", 0);
	if(dentry != NULL)
	{
		// 打印文件首个簇的编号(对于新创建的文件,暂时不会为其分配簇。对于新创建的目录,因为必然包含. ..两个项,所以会为其分配簇)
		// 打印文件尺寸
		color_printk(BLUE, BLACK, "Find a.TXT\nDIR_FstClusHI:%#018lx\tDIR_FstClusLO:%#018lx\tDIR_FileSize:%#018lx\n",
			dentry->DIR_FstClusHI, dentry->DIR_FstClusLO, dentry->DIR_FileSize);
	}
	else
	{
		color_printk(BLUE, BLACK, "Can`t find file a\n");
	}

	dentry = path_walk("/A.TXT", 0);
	if(dentry != NULL)
	{
		// 打印文件首个簇的编号(对于新创建的文件,暂时不会为其分配簇。对于新创建的目录,因为必然包含. ..两个项,所以会为其分配簇)
		// 打印文件尺寸
		color_printk(BLUE, BLACK, "Find A.TXT\nDIR_FstClusHI:%#018lx\tDIR_FstClusLO:%#018lx\tDIR_FileSize:%#018lx\n",
			dentry->DIR_FstClusHI, dentry->DIR_FstClusLO, dentry->DIR_FileSize);
	}
	else
	{
		color_printk(BLUE, BLACK, "Can`t find file A\n");
	}

	dentry = path_walk("/SubDIR/A.txt", 0);
	if(dentry != NULL)
	{
		// 打印文件首个簇的编号(对于新创建的文件,暂时不会为其分配簇。对于新创建的目录,因为必然包含. ..两个项,所以会为其分配簇)
		// 打印文件尺寸
		color_printk(BLUE, BLACK, "Find SubDIR/A.txt\nDIR_FstClusHI:%#018lx\tDIR_FstClusLO:%#018lx\tDIR_FileSize:%#018lx\n",
			dentry->DIR_FstClusHI, dentry->DIR_FstClusLO, dentry->DIR_FileSize);
	}
	else
	{
		color_printk(BLUE, BLACK, "Can`t find file SubDIR/A.txt\n");
	}

	dentry = path_walk("/ABCDEFGHIJKLMNOPQRSTUVWXYZ.txt", 0);
	if(dentry != NULL)
	{
		// 打印文件首个簇的编号(对于新创建的文件,暂时不会为其分配簇。对于新创建的目录,因为必然包含. ..两个项,所以会为其分配簇)
		// 打印文件尺寸
		color_printk(BLUE, BLACK, "Find ABCDEFGHIJKLMNOPQRSTUVWXYZ\nDIR_FstClusHI:%#018lx\tDIR_FstClusLO:%#018lx\tDIR_FileSize:%#018lx\n",
			dentry->DIR_FstClusHI, dentry->DIR_FstClusLO, dentry->DIR_FileSize);
	}
	else
	{
		color_printk(BLUE, BLACK, "Can`t find file ABCDEFGHIJKLMNOPQRSTUVWXYZ\n");
	}
}
// 参数1为完整路径名
// 参数2为标志集合
struct FAT32_Directory * path_walk(char * name, unsigned long flags)
{
	char * tmpname = NULL;
	int tmpnamelen = 0;
	// 短目录项
	struct FAT32_Directory *parent = NULL;
	struct FAT32_Directory *path = NULL;
	char * dentryname = NULL;
	// 寻找首个非/
	while(*name == '/')
	{
		name++;
	}

	// 遇到终止符
	if(!*name)
	{
		return NULL;
	}

	// 分配动态区域
	parent = (struct FAT32_Directory *)kmalloc(sizeof(struct FAT32_Directory), 0);
	memset(parent, 0, sizeof(struct FAT32_Directory));

	// 分配4k大小动态区域
	dentryname = kmalloc(PAGE_4K_SIZE, 0);
	memset(dentryname, 0, PAGE_4K_SIZE);

	// 用FAT根目录起始簇号作为父的起始簇号
	parent->DIR_FstClusLO = fat32_bootsector.BPB_RootClus & 0xffff;
	parent->DIR_FstClusHI = (fat32_bootsector.BPB_RootClus >> 16) & 0x0fff;
	for(;;)
	{
		tmpname = name;
		// 前进,直到再次遇到/
		while(*name && (*name != '/'))
		{
			name++;
		}

		// 这样得到两个/之间的字符数量
		tmpnamelen = name - tmpname;
		// dentryname存储中间字符
		memcpy(tmpname, dentryname, tmpnamelen);
		dentryname[tmpnamelen] = '\0';
		// 参数3提供父目录起始簇号
		// 参数1提供父目录的某个文件名
		// 参数2是此文件名长度
		// 参数4是标志集合
		path = lookup(dentryname, tmpnamelen, parent, flags);
		// 一旦找不到
		if(path == NULL)
		{
			// 结束
			color_printk(RED, WHITE, "can not find file or dir:%s\n", dentryname);
			kfree(dentryname);
			kfree(parent);
			return NULL;
		}

		// 找到了
		if(!*name)
		{
			goto last_component;
		}

		while(*name == '/')
		{
			name++;
		}

		if(!*name)
		{
			goto last_slash;
		}		

		// 表示还有后续部分需要继续寻找
		// 迭代--parent将在每次迭代前始终是本次要迭代处理的name的父目录的短目录项
		*parent = *path;
		kfree(path);
	}

last_slash:
last_component:
	// 返回父节点
	if(flags & 1)
	{
		kfree(dentryname);
		kfree(path);
		return parent;
	}

	// 返回自身
	kfree(dentryname);
	kfree(parent);
	return path;
}

// 参数1:待匹配文件名
// 参数2:文件名长度
// 参数3:文件名所在父目录(当成文件)的短目录项
// 参数4:标志集合
struct FAT32_Directory * lookup(char * name, int namelen, struct FAT32_Directory *dentry, int flags)
{
	unsigned int cluster = 0;
	unsigned long sector = 0;
	unsigned char *buf = NULL; 
	int i = 0, j = 0, x = 0;
	struct FAT32_Directory *tmpdentry = NULL;
	struct FAT32_LongDirectory *tmpldentry = NULL;
	struct FAT32_Directory *p = NULL;
	// 分配一个动态区域足以容纳一个簇的数据
	buf = kmalloc(BytesPerClus, 0);
	// 父目录的首个簇
	cluster = (dentry->DIR_FstClusHI << 16 | dentry->DIR_FstClusLO) & 0x0fffffff;
next_cluster:
	// 通过簇号得到簇号对于扇区号
	// 数据区起始扇区号是知道的,数据区起始扇区属于簇号2的首个扇区,每个簇可容纳扇区数也是知道的。
	// 这样便可通过簇号得到此簇首个扇区号
	sector = FirstDataSector + (cluster - 2) * fat32_bootsector.BPB_SecPerClus;
	color_printk(BLUE, BLACK, "lookup cluster:%#010x,sector:%#018lx\n", cluster, sector);
	// 读取父目录首个簇的数据
	if(!IDE_device_operation.transfer(ATA_READ_CMD, sector, fat32_bootsector.BPB_SecPerClus, (unsigned char *)buf))
	{
		// 读取失败
		color_printk(RED, BLACK, "FAT32 FS(lookup) read disk ERROR!!!!!!!!!!\n");
		kfree(buf);
		return NULL;
	}

	// 解析簇数据
	tmpdentry = (struct FAT32_Directory *)buf;
	for(i = 0; i < BytesPerClus; i += 32, tmpdentry++)
	{
		// 略过长目录项
		if(tmpdentry->DIR_Attr == ATTR_LONG_NAME)
		{
			continue;
		}

		// 略过无效目录项
		if(tmpdentry->DIR_Name[0] == 0xe5 || tmpdentry->DIR_Name[0] == 0x00 || tmpdentry->DIR_Name[0] == 0x05)
		{
			continue;
		}

		// 尝试借助长目录项完成匹配
		tmpldentry = (struct FAT32_LongDirectory *)tmpdentry - 1;
		j = 0;
		while(tmpldentry->LDIR_Attr == ATTR_LONG_NAME && tmpldentry->LDIR_Ord != 0xe5)
		{
			// j是啥意思?--累计已经匹配部分尺寸
			for(x = 0; x < 5; x++)
			{
				if(j > namelen && tmpldentry->LDIR_Name1[x] == 0xffff)
				{
					continue;
				}
				// 不匹配
				else if(j > namelen || tmpldentry->LDIR_Name1[x] != (unsigned short)(name[j++]))
				{
					goto continue_cmp_fail;
				}
			}

			for(x = 0; x < 6; x++)
			{
				if(j > namelen && tmpldentry->LDIR_Name2[x] == 0xffff)
				{
					continue;
				}
				else if(j > namelen || tmpldentry->LDIR_Name2[x] != (unsigned short)(name[j++]))
				{
					goto continue_cmp_fail;
				}
			}

			for(x = 0; x < 2; x++)
			{
				if(j > namelen && tmpldentry->LDIR_Name3[x] == 0xffff)
				{
					continue;
				}
				else if(j > namelen || tmpldentry->LDIR_Name3[x] != (unsigned short)(name[j++]))
				{
					goto continue_cmp_fail;
				}
			}

			// 全部匹配
			if(j >= namelen)
			{
				p = (struct FAT32_Directory *)kmalloc(sizeof(struct FAT32_Directory), 0);
				// 匹配的短目录项
				*p = *tmpdentry;
				kfree(buf);
				return p;
			}

			tmpldentry--;
		}

		// 尝试借助短目录项完成匹配
		j = 0;
		for(x = 0; x < 8; x++)
		{
			switch(tmpdentry->DIR_Name[x])
			{
				case ' ':
					// 不是目录
					if(!(tmpdentry->DIR_Attr & ATTR_DIRECTORY))
					{
						// 文件名较短时,后续部分用空格代替
						if(name[j] == '.')
						{
							continue;
						}
						else if(tmpdentry->DIR_Name[x] == name[j])
						{
							j++;
							break;
						}
						else
						{
							goto continue_cmp_fail;
						}
					}
					// 是目录
					else
					{
						if(j < namelen && tmpdentry->DIR_Name[x] == name[j])
						{
							j++;
							break;
						}
						// 目录名较短时,后续部分用空格代替
						else if(j == namelen)
						{
							continue;
						}
						else
						{
							goto continue_cmp_fail;
						}
					}
				case 'A' ... 'Z':
				case 'a' ... 'z':
					// 兼容windows
					if(tmpdentry->DIR_NTRes & LOWERCASE_BASE)
					{
						if(j < namelen && tmpdentry->DIR_Name[x] + 32 == name[j])
						{
							j++;
							break;
						}
						else
						{
							goto continue_cmp_fail;
						}
					}
					else
					{
						if(j < namelen && tmpdentry->DIR_Name[x] == name[j])
						{
							j++;
							break;
						}
						else
						{
							goto continue_cmp_fail;
						}
					}
				case '0' ... '9':
					{
						if(j < namelen && tmpdentry->DIR_Name[x] == name[j])
						{
							// 更新累计已经匹配部分尺寸
							j++;
							break;
						}
						else
						{
							goto continue_cmp_fail;
						}
					}
				default :
					{
						j++;
						break;
					}
			}
		}

		// 说明借助短目录项完成了匹配
		// 不是目录
		if(!(tmpdentry->DIR_Attr & ATTR_DIRECTORY))
		{
			j++;
			for(x = 8; x < 11; x++)
			{
				// 继续进行扩展名匹配
				switch(tmpdentry->DIR_Name[x])
				{
					case 'A' ... 'Z':
					case 'a' ... 'z':
						// 兼容windows
						if(tmpdentry->DIR_NTRes & LOWERCASE_EXT)
						{
							if(tmpdentry->DIR_Name[x] + 32 == name[j])
							{
								j++;
								break;
							}
							else
							{
								goto continue_cmp_fail;
							}
						}
						else
						{
							if(tmpdentry->DIR_Name[x] == name[j])
							{
								j++;
								break;
							}
							else
							{
								goto continue_cmp_fail;
							}
						}
					case '0' ... '9':
						if(tmpdentry->DIR_Name[x] == name[j])
						{
							j++;
							break;
						}
						else
						{
							goto continue_cmp_fail;
						}
					case ' ':
						if(tmpdentry->DIR_Name[x] == name[j])
						{
							j++;
							break;
						}
						else
						{
							goto continue_cmp_fail;
						}
					default:
						goto continue_cmp_fail;
				}
			}
		}

		// 能走到这里说明,借助短目录项完成了匹配
		p = (struct FAT32_Directory *)kmalloc(sizeof(struct FAT32_Directory), 0);
		*p = *tmpdentry;
		kfree(buf);
		return p;

		// 啥时跳转到这里:本次凭借短目录项关联的长目录项匹配失败,或者短目录项不存在关联长目录项下凭借短目录项匹配失败。
		// 会跳转到这里。
continue_cmp_fail:;// 后续继续处理下个目录项
	}
	
	// 如果对当前簇所有项处理后依然无法得到匹配
	// 借助FAT表,从当前簇号得到下个簇号
	cluster = DISK1_FAT32_read_FAT_Entry(cluster);
	// 下个簇号存在下
	if(cluster < 0x0ffffff7)
	{
		goto next_cluster;// 继续分析下个簇号内的各个目录项
	}

	// 对父目录文件数据区全部数据解析后,依然无法得到匹配
	kfree(buf);
	return NULL;
}

// 借助FAT,从当前簇号得到下个簇号
unsigned int DISK1_FAT32_read_FAT_Entry(unsigned int fat_entry)
{
	unsigned int buf[128];
	memset(buf, 0, 512);
	// 一个簇号占据一个FAT表项,一个FAT表项占据4字节。
	// 一个扇区512字节,可容纳2^7个表项。
	// fat_entry >> 7,即可得到fat_entry表项所在扇区相对于FAT首个扇区的偏移
	IDE_device_operation.transfer(ATA_READ_CMD, FirstFAT1Sector + (fat_entry >> 7), 1, (unsigned char *)buf);
	// fat_entry & 0x7f,即可得到fat_entry相对于所在扇区的偏移
	color_printk(BLUE, BLACK, "DISK1_FAT32_read_FAT_Entry fat_entry:%#018lx,%#010x\n", 
		fat_entry, buf[fat_entry & 0x7f]);
	// 取得FAT中fat_entry表项的值,我们只取低28个比特位
	return buf[fat_entry & 0x7f] & 0x0fffffff;
}

unsigned long DISK1_FAT32_write_FAT_Entry(unsigned int fat_entry, unsigned int value)
{
	unsigned int buf[128];
	memset(buf, 0, 512);
	IDE_device_operation.transfer(ATA_READ_CMD, FirstFAT1Sector + (fat_entry >> 7), 1, (unsigned char *)buf);
	buf[fat_entry & 0x7f] = (buf[fat_entry & 0x7f] & 0xf0000000) | (value & 0x0fffffff);
	// 依次写入FAT1,FAT2
	IDE_device_operation.transfer(ATA_WRITE_CMD, FirstFAT1Sector + (fat_entry >> 7), 1, (unsigned char *)buf);
	IDE_device_operation.transfer(ATA_WRITE_CMD, FirstFAT2Sector + (fat_entry >> 7), 1, (unsigned char *)buf);
	return 1;	
}

2.虚拟文件系统  

2.1.论述

2.2.实践

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

raindayinrain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值