跟我一起写操作系统

    返回首页    发表留言
本文作者:李德强
          第三节 文件系统
 
 

        现在我们可以通过IDE磁盘操作来直接读写硬盘扇区,但是这样使用起来非常不方便。我们需要建立一个系统的,有序的文件系统来以“文件”的形式来读写硬盘数据。一个扇区的数据大小为0x200字节,这对于一个文件来说实在是太小了,于是人们采用了一种叫作三级指针的结构来存储文件。首先定义文件的数据结构:

//文件块数据结构
typedef struct fs_s
{
        union
        {
                //文件信息块
                struct
                {
                        //模式
                        u32 mode;
                        //所属者
                        u32 owner;
                        //所属组
                        u32 group;
                        //大小
                        u32 size;
                        //./
                        u32 dot;
                        //../
                        u32 dotdot;
                        //root_no
                        u32 root;
                        //is root
                        u32 is_root;
                        //文件名
                        char name[476];
                        //文件地址项
                        u32 address;
                };
                //一级、二级、三级地址项
                struct
                {
                        u32 addr_no[FS_ADDR_COUNT];
                };
                //文件数据内容
                struct
                {
                        u8 data[FS_DATA_COUNT];
                };
        };
} s_fs;

        一个文件的起始扇区中存放了文件头内容,包括文件名、文件类型、权限等内容,并在最后一个属性中定义了一个u32 address,用于存放一级地址号。这个一级地址号即为一个扇区号,这个扇区中存放了u32 addr_no[FS_ADDR_COUNT]一级地址,也就是一共有0x80个一级地址,每一个一级地址中都存放了一个二级地址号,每一个二级地址号中存放了0x80个三级地址,而每一个三级地址存放了一个真正的数据扇区号。如下图:



 

        其中每个地址都是一个箭头指向另一个地址扇区或数据扇区。可以计算出一个文件的最大占用大小为:0x80 * 0x80 * 0x80 * 0x200 = 0x40000000字节 = 1024MB = 1GB。

        下面来实现创建文件的具体代码:

/*
 * fs_create_fs : 根据父级文件编号创建子文件(夹)
 *  - u32 parent_no : 低级文件块编号
 *  - char *fs_name : 待创建的文件(夹)名称
 *  - u32 fs_mode : FS_FOLDER_MODE为文件夹,FS_FILE_MODE为文件
 * return : u32 返回新创建的子文件(夹—)编号,0代表创建失败
 */
u32 fs_create_fs_path(char *path, char *fs_name, u32 uid, u32 gid, u32 mode)
{
        //根据父级编号读入文件块
        s_fs *fs_parent = alloc_mm(sizeof(s_fs));
        u32 dev_id = 0;
        fs_find_path(path, &dev_id, &fs_parent);
        //root
        if (uid != 0)
        {
                if (fs_parent->owner != uid && fs_parent->group != gid)
                {
                        if (((fs_parent->mode >> 1) & 0x1) == 0)
                        {
                                free_mm(fs_parent, sizeof(sizeof(s_fs)));
                                return 0;
                        }
                }
                else if (fs_parent->owner != uid && fs_parent->group == gid)
                {
                        if (((fs_parent->mode >> 4) & 0x1) == 0)
                        {
                                free_mm(fs_parent, sizeof(sizeof(s_fs)));
                                return 0;
                        }
                }
                else if (fs_parent->owner == uid)
                {
                        if (((fs_parent->mode >> 7) & 0x1) == 0)
                        {
                                free_mm(fs_parent, sizeof(sizeof(s_fs)));
                                return 0;
                        }
                }
        }
        s_fs *fs = alloc_mm(sizeof(s_fs));
        //清空文件块内容
        fs_empty_data(fs);
        fs->owner = uid;
        fs->group = gid;
        //文件模式
        fs->mode = mode;
        //申请一块未使用的文件块,给本文件块
        fs->dot = alloc_sec(dev_id);
        if (fs->dot == 0)
        {
                free_mm(fs, sizeof(s_fs));
                free_mm(fs_parent, sizeof(s_fs));
                return 0;
        }
        //设定父级文件块编号
        fs->dotdot = fs_parent->dot;
        //root
        if (fs_parent->is_root)
        {
                fs->root = fs_parent->dot;
        }
        else
        {
                fs->root = fs_parent->root;
        }
        //文件大小
        fs->size = 0;
        //设定文件名称
        str_copy(fs_name, fs->name);
        //定入此新的文件块
        write_block(dev_id, fs->dot, fs);
        /*
         * 以下是将此文件块挂入其父级文件块中
         */
        //父级子项地址
        s_fs *fst_addrs = alloc_mm(sizeof(s_fs));
        if (fs_parent->address == 0)
        {
                //创建一级磁盘指针
                fs_parent->address = alloc_sec(dev_id);
                fs_empty_data(fst_addrs);
                //保存地址项
                write_block(dev_id, fs_parent->address, fst_addrs);
                //保存父级文件块
                write_block(dev_id, fs_parent->dot, fs_parent);
        }
        //一级地址列表
        read_block(dev_id, fs_parent->address, fst_addrs);
        for (int i = 0; i < FS_ADDR_COUNT; ++i)
        {
                s_fs *sec_addrs = alloc_mm(sizeof(s_fs));
                if (fst_addrs->addr_no[i] == 0)
                {
                        //创建二级磁盘指针
                        fst_addrs->addr_no[i] = alloc_sec(dev_id);
                        fs_empty_data(sec_addrs);
                        //保存地址项
                        write_block(dev_id, fst_addrs->addr_no[i], sec_addrs);
                        //保存父级文件块
                        write_block(dev_id, fs_parent->address, fst_addrs);
                }
                //二级地址列表
                read_block(dev_id, fst_addrs->addr_no[i], sec_addrs);
                for (int j = 0; j < FS_ADDR_COUNT; ++j)
                {
                        s_fs *thrid_addrs = alloc_mm(sizeof(s_fs));
                        if (sec_addrs->addr_no[j] == 0)
                        {
                                //创建三级磁盘指针
                                sec_addrs->addr_no[j] = alloc_sec(dev_id);
                                fs_empty_data(thrid_addrs);
                                //保存地址项
                                write_block(dev_id, sec_addrs->addr_no[j], thrid_addrs);
                                //保存父级文件块
                                write_block(dev_id, fst_addrs->addr_no[i], sec_addrs);
                        }
                        //三级地址列表
                        read_block(dev_id, sec_addrs->addr_no[j], thrid_addrs);
                        for (int k = 0; k < FS_ADDR_COUNT; ++k)
                        {
                                if (thrid_addrs->addr_no[k] == 0)
                                {
                                        //挂入文件块
                                        thrid_addrs->addr_no[k] = fs->dot;
                                        //保存三级文件块内容
                                        write_block(dev_id, sec_addrs->addr_no[j], thrid_addrs);
                                        //返回新的文件块编号
                                        u32 sno = fs->dot;
                                        free_mm(thrid_addrs, sizeof(s_fs));
                                        free_mm(sec_addrs, sizeof(s_fs));
                                        free_mm(fst_addrs, sizeof(s_fs));
                                        free_mm(fs, sizeof(s_fs));
                                        free_mm(fs_parent, sizeof(s_fs));
                                        return sno;
                                }
                        }
                        free_mm(thrid_addrs, sizeof(s_fs));
                }
                free_mm(sec_addrs, sizeof(s_fs));
        }
        free_mm(fst_addrs, sizeof(s_fs));
        free_mm(fs, sizeof(s_fs));
        free_mm(fs_parent, sizeof(s_fs));
        //创建失败,返回0
        return 0;
}

        打开一个文件:

/*
 * f_open : 打开文件
 *  - char *file_name : 完整路径文件名
 *  - int fs_mode : FS_MODE_WRITE为写模式,FS_MODE_READ为读模式
 * return : s_file*文件结构指针
 */
s_file* f_open(char *file_name, int fs_mode, u32 uid, u32 gid)
{
        if (str_len(file_name) == 0)
        {
                return NULL;
        }
        char dev_path[FS_NAME_LENGTH];
        u32 dev_id = find_dev_id_fullpath(file_name, dev_path);
        //文件名称,临时变量
        char *fs_name = alloc_mm(FS_NAME_LENGTH);
        u32 parent_no = 0;
        int child_no = 0;
        for (int i = 0, j = 0; dev_path[i] != '\0'; ++i)
        {
                //取得文件目录名称
                if (dev_path[i] != '/')
                {
                        fs_name[j++] = dev_path[i];
                }
                else
                {
                        //根据目录名称查找子目录
                        fs_name[j++] = '/';
                        fs_name[j++] = '\0';
                        j = 0;
                        child_no = fs_find_sub_fs(dev_id, parent_no, fs_name);
                        if (child_no == 0)
                        {
                                free_mm(fs_name, FS_NAME_LENGTH);
                                return NULL;
                        }
                        parent_no = child_no;
                }
                if (dev_path[i + 1] == '\0')
                {
                        fs_name[j++] = '\0';
                        //查找文件
                        child_no = fs_find_sub_fs(dev_id, parent_no, fs_name);
                        //没找到
                        if (child_no == 0)
                        {
                                free_mm(fs_name, FS_NAME_LENGTH);
                                //返回空
                                return NULL;
                        }
                }
        }
        //申请文件内存
        s_file *fp = alloc_mm(sizeof(s_file));
        //读入文件内容
        read_block(dev_id, child_no, &fp->fs);
        //root
        if (uid != 0)
        {
                if (fp->fs.owner != uid && fp->fs.group != gid)
                {
                        if (fs_mode == FS_MODE_READ && ((fp->fs.mode >> 2) & 0x1) == 0)
                        {
                                free_mm(fs_name, FS_NAME_LENGTH);
                                free_mm(fp, sizeof(s_file));
                                return NULL;
                        }
                        else if (fs_mode == FS_MODE_WRITE && ((fp->fs.mode >> 1) & 0x1) == 0)
                        {
                                free_mm(fs_name, FS_NAME_LENGTH);
                                free_mm(fp, sizeof(s_file));
                                return NULL;
                        }
                }
                else if (fp->fs.owner != uid && fp->fs.group == gid)
                {
                        if (fs_mode == FS_MODE_READ && ((fp->fs.mode >> 5) & 0x1) == 0)
                        {
                                free_mm(fs_name, FS_NAME_LENGTH);
                                free_mm(fp, sizeof(s_file));
                                return NULL;
                        }
                        else if (fs_mode == FS_MODE_WRITE && ((fp->fs.mode >> 4) & 0x1) == 0)
                        {
                                free_mm(fs_name, FS_NAME_LENGTH);
                                free_mm(fp, sizeof(s_file));
                                return NULL;
                        }
                }
                else if (fp->fs.owner == uid)
                {
                        if (fs_mode == FS_MODE_READ && ((fp->fs.mode >> 8) & 0x1) == 0)
                        {
                                free_mm(fs_name, FS_NAME_LENGTH);
                                free_mm(fp, sizeof(s_file));
                                return NULL;
                        }
                        else if (fs_mode == FS_MODE_WRITE && ((fp->fs.mode >> 7) & 0x1) == 0)
                        {
                                free_mm(fs_name, FS_NAME_LENGTH);
                                free_mm(fp, sizeof(s_file));
                                return NULL;
                        }
                }
        }
        fp->dev_id = dev_id;
        fp->fs_mode = fs_mode;
        fp->offset = 0;
        fp->offset_buff = 0;
        fp->offset_read_buff = FS_READ_BUFF;
        fp->block_offset = 0;
        fp->addr_index = 0;
        fp->first_index = 0;
        fp->sec_index = 0;
        fp->third_index = 0;
        if (fs_mode == FS_MODE_WRITE)
        {
                fp->fs.size = 0;
                fp->buff = alloc_mm(FS_DATA_COUNT);
        }
        else if (fs_mode == FS_MODE_READ)
        {
                fp->buff = alloc_mm(FS_READ_BUFF);
        }
        fs_empty_data(fp->buff);
        fp->next = NULL;
        free_mm(fs_name, FS_NAME_LENGTH);
        //返回文件指针
        return fp;
}

        读取文件内容:

/*
 * f_read : 读取文件内容
 *  - s_file *fp : 文件指针
 *  - int size : 读入字节数
 *  - char *data : 等待读入的数据地址
 * return : int读入字节数
 */
int f_read(s_file *fp, int size, char *data)
{
        //文件为空,出错
        if (fp == NULL)
        {
                return 0;
        }
        //文件不是读模式,出错
        if (fp->fs_mode != FS_MODE_READ)
        {
                return 0;
        }
        if (fp->offset + size >= fp->fs.size)
        {
                size = fp->fs.size - fp->offset;
        }
        int read_size = 0;
        int offset_read = 0;
        while (size >= FS_READ_BUFF)
        {
                if (fp->offset_read_buff >= FS_READ_BUFF)
                {
                        f_read_buff(fp);
                        fp->offset_read_buff = 0;
                }
                mmcopy_with(fp->buff, data + offset_read, fp->offset_read_buff, FS_READ_BUFF);
                fp->offset += FS_READ_BUFF;
                fp->offset_read_buff += FS_READ_BUFF;
                offset_read += FS_READ_BUFF;
                size -= FS_READ_BUFF;
                read_size += FS_READ_BUFF;
        }
        if (size > 0 && size < FS_READ_BUFF)
        {
                if (fp->offset_read_buff >= FS_READ_BUFF)
                {
                        f_read_buff(fp);
                        fp->offset_read_buff = 0;
                }
                mmcopy_with(fp->buff, data + offset_read, fp->offset_read_buff, size);
                fp->offset += size;
                fp->offset_read_buff += size;
                offset_read += size;
                read_size += size;
                size = 0;
        }
        return read_size;
}

        写入文件内容:

/*
 * f_write : 写入数据到文件
 *  - s_file *fp : 文件指针
 *  - int size : 写入字节数
 *  - char *data : 写入数据
 * return : int文件当前偏移
 */
int f_write(s_file *fp, int size, char *data)
{
        //文件为空,出错
        if (fp == NULL)
        {
                return -1;
        }

        //读模式不能写入文件
        if (fp->fs_mode != FS_MODE_WRITE)
        {
                return -1;
        }

        //循环写文件
        for (int i = 0; i < size; ++i)
        {
                //拷贝数据到缓冲区
                fp->buff[fp->block_offset] = data[i];
                fp->block_offset++;
                fp->fs.size++;

                //buff写满0x200之后写入磁盘块
                if (fp->block_offset >= FS_DATA_COUNT)
                {
                        //一次写入0x200字节
                        f_write_block(fp);

                        fp->block_offset = 0;
                        fp->offset += FS_DATA_COUNT;
                }
        }
        //返回文件当前偏移
        return fp->offset;
}

        关闭文件:

/*
 * f_close : 关闭文件
 *  - s_file *fp : 文件指针
 * return : -1为失败, 0 为成功
 */
int f_close(s_file *fp)
{
        //文件指针为空,出错
        if (fp == NULL)
        {
                return -1;
        }
        //如果是写模式,要将最后一次没写入完成的内容写入文件
        if (fp->fs_mode == FS_MODE_WRITE)
        {
                //如果有块内偏移
                if (fp->block_offset > 0)
                {
                        //写入剩余内容
                        for (int i = fp->block_offset; i < FS_DATA_COUNT; ++i)
                        {
                                fp->buff[i] = 0;
                        }
                        f_write_block(fp);
                }
                //写入文件,主要更新文件大小属性
                write_block(fp->dev_id, fp->fs.dot, &fp->fs);
        }
        //释放文件指针内存
        if (fp->fs_mode == FS_MODE_WRITE)
        {
                free_mm(fp->buff, FS_DATA_COUNT);
        }
        else if (fp->fs_mode == FS_MODE_READ)
        {
                free_mm(fp->buff, FS_READ_BUFF);
        }
        free_mm(fp, sizeof(s_file));
        //返回成功
        return 0;
}

        查找文件:

/*
 * 查找文件,根据文件路径名查找文件,并取得文件内容
 */
int fs_find_path(char *path_name, u32 *ret_dev_id, s_fs **fs)
{
        char dev_path[FS_NAME_LENGTH];
        u32 dev_id = find_dev_id_fullpath(path_name, dev_path);
        //临时保存文件夹名称
        char folder_name[FS_NAME_LENGTH];
        //父级文件块编号
        u32 parent_no = 0;
        for (int i = 0, j = 0; dev_path[i] != '\0'; ++i)
        {
                //以'/'为分割符取得文件夹名称
                if (dev_path[i] != '/')
                {
                        folder_name[j++] = dev_path[i];
                }
                else
                {
                        //取得文件夹名称
                        folder_name[j++] = '/';
                        folder_name[j++] = '\0';
                        j = 0;
                        //根据父级文件块编号查找其下的直属子文件

                        int child_no = fs_find_sub_fs(dev_id, parent_no, folder_name);
                        //没有找到此文件
                        if (child_no == 0)
                        {
                                //返回空
                                fs_empty_data(*fs);
                                return FS_STATUS_NO_FILE_DIR;
                        }
                        //找到子文件块,将其设定为下一级父文件块编号
                        parent_no = child_no;
                }
        }
        //根据文件块编号读取文件块内容,并写入文件块指针所指向的地址
        read_block(dev_id, parent_no, *fs);
        *ret_dev_id = dev_id;
        return FS_STATUS_OK;
}

 

    返回首页    返回顶部
#1楼  鸡蛋里挑骨头的雷锋v5  于 2018年04月04日23:53:14 发表
 
0x40000000字节 等于1G吧不是等于400M ,哈哈
#2楼  李德强  于 2018年04月09日16:50:58 发表
 
非常感谢!此处已经修正。
  看不清?点击刷新

 

  Copyright © 2015-2018 问渠网 辽ICP备15013245号