问题
如何在根目录中删除文件?
实现思路
根据文件名在根目录的数据链表中查找 FileEntry 值
当查找成功时:从数据链表中删除 FileEntry 值
解决方案
1. 判断目标文件是否已经打开 (只有关闭状态能被删除)
2. 根据名字查找目标 FileEntry 的位置 (如:FileEntry 2)
3. 将数据链表中最后一个 FileEntry 值复制到目标 FileEntry 的位置
4. lastBytes = lastBytes - sizeof(FileEntry)
根目录中删除文件的流程
关键细节一
FileEntry 移动时需要保留原 inSctIdx 和 inSctOff 的值。
关键细节二
抹除最后一个 FileEntry 所占用的空间,即:修改 lastBytes,使其减少64字节。
数据链表中的字节删除
数据链表容量调整 ![](https://i-blog.csdnimg.cn/blog_migrate/54e3a35b1ec3efe80f247d7a21d3590a.png)
在根目录中删除文件
fs.c
uint FDelete(const char* name)
{
return (name && !IsOpened(name) && DeleteInRoot(name)) ? FS_SUCCESSED : FS_FAILED;
}
static uint IsOpened(const char* name)
{
uint ret = 0;
return ret;
}
static uint DeleteInRoot(const char* name)
{
uint ret = 0;
FSRoot* root = (FSRoot*)ReadSector(ROOT_SCT_IDX);
FileEntry* fe = FileEntry* fe = FindInRoot(name);
if(name && root && fe)
{
uint last = FindLast(root->sctBegin);
FileEntry* feTarget = (FileEntry*)ReadSector(fe->inSctIdx);
FileEntry* feLast = (last != SCT_END_FLAG) ? (FileEntry*)ReadSector(last) : NULL;
if(feTarget && feLast)
{
uint lastOff = root->lastBytes / FE_BYTES - 1;
FileEntry* TargetItem = (FileEntry*)AddrOff(feTarget, fe->inSctOff);
FileEntry* LastItem = (FileEntry*)AddrOff(feLast, lastOff);
FreeFile(TargetItem->sctBegin);
MoveFileEntry(TargetItem, LastItem);
EraseLast(root, FE_BYTES);
ret = HDRawWrite(ROOT_SCT_IDX, (byte*)root) &&
HDRawWrite(fe->inSctIdx, (byte*)feTarget);
}
Free(feTarget);
Free(feLast);
}
Free(root);
Free(fe);
return ret;
}
static uint FreeFile(uint sctBegin)
{
uint ret = 0;
uint slider = sctBegin;
while(slider != SCT_END_FLAG)
{
uint next = NextSector(slider);
ret += FreeSector(slider);
slider = next;
}
return ret;
}
static void MoveFileEntry(FileEntry* dst, const FileEntry* src)
{
if(dst && src)
{
uint inSctIdx = dst->inSctIdx;
uint inSctOff = dst->inSctOff;
*dst = *src;
dst->inSctIdx = inSctIdx;
dst->inSctOff = inSctOff;
}
}
static uint AdjustStorage(FSRoot* root)
{
uint ret = 0;
if(root)
{
if(root->lastBytes == 0)
{
uint last = FindLast(root->sctBegin);
uint prev = FindPrev(root->sctBegin, last);
if(FreeSector(last) && MarkSector(prev))
{
root->sctNum--;
root->lastBytes = SECT_SIZE;
if(root->sctNum == 0)
{
root->sctBegin = SCT_END_FLAG;
}
ret = 1;
}
}
}
return ret;
}
static uint EraseLast(FSRoot* root, uint bytes)
{
uint ret = 0;
if(root)
{
while((root->sctNum > 0) && (bytes > 0))
{
if(bytes < root->lastBytes)
{
root->lastBytes -= bytes;
ret += bytes;
bytes = 0;
}
else
{
bytes -= root->lastBytes;
ret += root->lastBytes;
root->lastBytes = 0;
AdjustStorage(root);
}
}
}
return ret;
}
IsOpened 函数还没具体实现,我们删除一个文件的策略是:把最会一个 FileEntry 复制到目标 FileEntry,这样方便下一次创建文件。
test
void test()
{
FSRoot* root = (FSRoot*)ReadSector(ROOT_SCT_IDX);
if(root)
{
FileEntry* feBase = (FileEntry*)ReadSector(root->sctBegin);
if(feBase)
{
uint i = 0;
FileEntry* fe = NULL;
printf("sctNum = %d\n", root->sctNum);
printf("lastBytes = %d\n", root->lastBytes);
for(i = 0; i < root->lastBytes / FE_BYTES; i++)
{
fe = (FileEntry*)AddrOff(feBase, i);
printf("name = %s\n", fe->name);
}
}
Free(feBase);
}
Free(root);
}
int main(void)
{
HDRawSetName("hd.img");
HDRawModInit();
printf("FSFormat: %d\n", FSFormat());
printf("FSIsFormat: %d\n", FSIsFormated());
printf("create: %d\n", FCreate("1.txt"));
printf("create: %d\n", FCreate("2.txt"));
printf("create: %d\n", FCreate("3.txt"));
printf("create: %d\n", FCreate("4.txt"));
printf("create: %d\n", FCreate("5.txt"));
printf("delete: %d\n", FDelete("3.txt"));
printf("existed: %d\n", FExisted("3.txt"));
test();
HDRawFlush();
return 0;
}
程序运行结果如下所示
程序运行结果符合我们的预期。删除文件3后,将文件5的 FileEntry 复制到了文件3的 FileEntry 处。
问题
如何重命名根目录中的文件?
解决方案
1. 判断目标文件是否已经打开 (只有关闭状态能重命名)
2. 根据名字查找目标 FileEntry 的位置 (如:FileEntry4)
3. 查找新名字是否已经被占用
- 如果没有占用,修改目标 FileEntry 的 name 成员
- 如果已经占用,返回失败
根目录中重命名文件的流程
文件重命名
FileEntry 写回硬盘
在根目录中重命名文件
fs.c
uint FRename(const char* ofn, const char* nfn)
{
uint ret = FS_FAILED;
if(ofn && !IsOpened(ofn) && nfn)
{
FileEntry* ofe = FindInRoot(ofn);
FileEntry* nfe = FindInRoot(nfn);
if((ofe != NULL) && (nfe == NULL))
{
StrCpy(ofe->name, nfn, sizeof(ofe->name) - 1);
if(FlushFileEntry(ofe))
{
ret = FS_SUCCESSED;
}
}
Free(ofe);
Free(nfe);
}
return ret;
}
static uint FlushFileEntry(FileEntry* fe)
{
uint ret = 0;
if(fe)
{
FileEntry* feBase = (FileEntry*)ReadSector(fe->inSctIdx);
FileEntry* inSct = (FileEntry*)AddrOff(feBase, fe->inSctOff);
if(feBase && inSct)
{
*inSct = *fe;
HDRawWrite(fe->inSctIdx, (byte*)feBase);
ret = 1;
}
Free(feBase);
}
return ret;
}
test
void test()
{
FSRoot* root = (FSRoot*)ReadSector(ROOT_SCT_IDX);
if(root)
{
FileEntry* feBase = (FileEntry*)ReadSector(root->sctBegin);
if(feBase)
{
uint i = 0;
FileEntry* fe = NULL;
printf("sctNum = %d\n", root->sctNum);
printf("lastBytes = %d\n", root->lastBytes);
for(i = 0; i < root->lastBytes / FE_BYTES; i++)
{
fe = (FileEntry*)AddrOff(feBase, i);
printf("name = %s\n", fe->name);
}
}
Free(feBase);
}
Free(root);
}
main.c
int main(void)
{
HDRawSetName("hd.img");
HDRawModInit();
// printf("FSFormat: %d\n", FSFormat());
printf("FSIsFormat: %d\n", FSIsFormated());
// printf("create: %d\n", FCreate("1.txt"));
// printf("create: %d\n", FCreate("2.txt"));
// printf("create: %d\n", FCreate("3.txt"));
// printf("create: %d\n", FCreate("4.txt"));
// printf("create: %d\n", FCreate("5.txt"));
printf("rename: %d\n", FRename("5.txt", "test.txt"));
printf("existed: %d\n", FExisted("5.txt"));
printf("existed: %d\n", FExisted("test.txt"));
test();
HDRawFlush();
return 0;
}
我们将 5.txt 重命名为 test.txt,程序运行结果如下:
5.txt 成功重命名为 test.txt。
思考:
如何读写已经存在的文件?