文件系统设计与实现(四)

问题

如何在根目录中删除文件?

实现思路

根据文件名在根目录的数据链表中查找 FileEntry 值

当查找成功时:从数据链表中删除 FileEntry 值

解决方案

1. 判断目标文件是否已经打开 (只有关闭状态能被删除)

2. 根据名字查找目标 FileEntry 的位置 (如:FileEntry 2)

3. 将数据链表中最后一个 FileEntry 值复制到目标 FileEntry 的位置

4. lastBytes = lastBytes - sizeof(FileEntry)

根目录中删除文件的流程

 

关键细节一

FileEntry 移动时需要保留原 inSctIdx 和 inSctOff 的值。 

关键细节二

抹除最后一个 FileEntry 所占用的空间,即:修改 lastBytes,使其减少64字节。

数据链表中的字节删除

 

数据链表容量调整 

在根目录中删除文件

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。

思考:

如何读写已经存在的文件?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值