"Operating System Truth Restore" Chapter 14 File System

The learning experience is better with videos!
Part a of section a: https://www.bilibili.com/video/BV1Uh4y1U7G6/?vd_source=701807c4f8684b13e922d0a8b116af31
Part two of section a: https://www.bilibili.com/video/BV1Lj41127Bc/?vd_source=701807c4f8684b13e922 d0a8b116af31
Section b: https: //www.bilibili.com/video/BV1Pp4y1J7pN/?vd_source=701807c4f8684b13e922d0a8b116af31
Part of section c: https://www.bilibili.com/video/BV1gp4y1N7GG/?vd_source=701807c4f8684b13e922d0a8b116 af31
subsection c part two: https://www.bilibili .com/video/BV11N411v7s5/?vd_source=701807c4f8684b13e922d0a8b116af31
Part three of section c: https://www.bilibili.com/video/BV1uu411P7yV/?vd_source=701807c4f8684b13e922d0a8b116af3
Four parts of subsection 1 c: https://www.bilibili.com/video /BV1YH4y1Q769/?vd_source=701807c4f8684b13e922d0a8b116af31
The five parts of subsection c: https://www.bilibili.com/video/BV1UH4y1Q7kT/?vd_source=701807c4f8684b13e922d0a8b116af31 The
six parts of subsection c: https://www.bilibili.com/video/BV1594y1x7NH/?vd_source=701807c4f8684b13e922 d0a8b116af31
Section d: https ://www.bilibili.com/video/BV1Fz4y1j71L/?vd_source=701807c4f8684b13e922d0a8b116af31
Section e: https://www.bilibili.com/video/BV1Rh4y1P7HG/?vd_source=701807c4f8684b13e922d0a8b116 af31 subsection
f: https://www.bilibili.com /video/BV1MV411P7kL/?vd_source=701807c4f8684b13e922d0a8b116af31
Section g: https://www.bilibili.com/video/BV148411i7se/?vd_source=701807c4f8684b13e922d0a8b116af31 Section
h: https:// www.bilibili.com/video/BV18u4y1k7oR/?vd_source= 701807c4f8684b13e922d0a8b116af31
Section i: https://www.bilibili.com/video/BV1ph4y1Y7j9/?vd_source=701807c4f8684b13e922d0a8b116af31
Section j: https://www.bilibili.com/video/BV1Ah4y1A7Hf/?vd_source=701807c4f8684b13e922d0a8b116af31
Section k: https://www.bilibili.com/video/BV1Gk4y1w7Yb/?vd_source=701807c4f8684b13e922d0a8 b116af31
Section l: https://www .bilibili.com/video/BV1a14y1r7y6/?vd_source=701807c4f8684b13e922d0a8b116af31
Section m: https://www.bilibili.com/video/BV1aF411D7kZ/?vd_source=701807c4f8684b13e922d0a8b116af Section 31
: https://www.bilibili.com/video/BV1Kw411i79c /?vd_source=701807c4f8684b13e922d0a8b116af31

Code repository: https://github.com/xukanshan/the_truth_of_operationg_system

In the file system, the core concepts are three: inode, directory (directory is also a file) and super block. The file system is based on partitions, that is, each partition has its own file system.

  1. inode
    • The inode records the mapping of files to disk locations.
    • Each file corresponds to an inode. When we find the inode corresponding to this file, we can know where the file is stored on the disk.
    • All inodes of each disk partition will form an array. Using the inode array subscript of the file, we can find the corresponding inode information in the array.
  2. Directory :
    • A directory is actually a special file.
    • The directory is composed of many directory entries, which record the mapping of file names to inodes. If a directory manages /Desktopfolders, then the many directory entries in this directory are the /Desktopvarious files and subdirectories under the management folder.
  3. Super block :
    • The superblock contains information about inodes, directories, and other file system metadata.
    • Typically, it is located in the second sector of each disk partition, which is a fixed location.
    • For inode, it records the starting position of the inode array on the disk; for the root directory, the super block also records its inode label.

Example process for finding files:

If we are looking for one /home/test.c, how does the file system work?

  • First, since the location of the superblock is fixed, we can access it directly on the disk.
  • From the superblock, we can know the inode label of the root directory and the location of the inode array.
  • Using the inode label of the root directory and the inode array position, we can find the inode corresponding to the root directory file, and then find the root directory file on the disk.
  • In the root directory file, we look for a homedirectory entry named and retrieve homethe inode array label from it.
  • Using homethe inode label, we access the inode array again to find homethe actual location of the directory file on the disk.
  • Finally, in homethe directory file, we search for a test.cdirectory entry named, retrieve test.cthe inode array label from it, and then find test.cthe location on the disk.

The location of a typical file system metadata structure on the disk is shown in Figure
Insert image description here
Section a:

We just write a function partition_formatto create a file system, that is, create file system metadata (super block, free block bitmap, inode bitmap, inode array, root directory);

Then write a function filesys_initto traverse all partitions. If the partition does not have a file system, call it partition_formatto create a file system.

First let's prepare the data structure

Super block ( myos/fs/super_block.h )

#ifndef __FS_SUPER_BLOCK_H
#define __FS_SUPER_BLOCK_H
#include "stdint.h"

/* 超级块 */
struct super_block {
    
    
    uint32_t magic;		    // 用来标识文件系统类型,支持多文件系统的操作系统通过此标志来识别文件系统类型
    uint32_t sec_cnt;		    // 本分区总共的扇区数
    uint32_t inode_cnt;		    // 本分区中inode数量
    uint32_t part_lba_base;	    // 本分区的起始lba地址

    uint32_t block_bitmap_lba;	    // 块位图本身起始扇区地址
    uint32_t block_bitmap_sects;     // 扇区位图本身占用的扇区数量

    uint32_t inode_bitmap_lba;	    // i结点位图起始扇区lba地址
    uint32_t inode_bitmap_sects;	    // i结点位图占用的扇区数量

    uint32_t inode_table_lba;	    // i结点表起始扇区lba地址
    uint32_t inode_table_sects;	    // i结点表占用的扇区数量

    uint32_t data_start_lba;	    // 数据区开始的第一个扇区号
    uint32_t root_inode_no;	    // 根目录所在的I结点号
    uint32_t dir_entry_size;	    // 目录项大小

    uint8_t  pad[460];		    // 加上460字节,凑够512字节1扇区大小
} __attribute__ ((packed));
#endif

inode (myos/fs/inode.h

#ifndef __FS_INODE_H
#define __FS_INODE_H
#include "stdint.h"
#include "list.h"

/* inode结构 */
struct inode {
    
    
    uint32_t i_no;    // inode编号

    /* 当此inode是文件时,i_size是指文件大小,
    若此inode是目录,i_size是指该目录下所有目录项大小之和*/
    uint32_t i_size;

    uint32_t i_open_cnts;   // 记录此文件被打开的次数
    bool write_deny;	   // 写文件不能并行,进程写文件前检查此标识

    /* i_sectors[0-11]是直接块, i_sectors[12]用来存储一级间接块指针 */
    uint32_t i_sectors[13];
    struct list_elem inode_tag;
};
#endif

Directory and directory entry ( myos/fs/dir.h ) It should be noted that the data structure of the directory struct dirwill only exist in the memory, because it manages the operation of a directory file (such as opening a directory file, it will Create such a structure in memory)

#ifndef __FS_DIR_H
#define __FS_DIR_H
#include "stdint.h"
#include "inode.h"


#define MAX_FILE_NAME_LEN  16	 // 最大文件名长度

/* 目录结构 */
struct dir {
    
    
    struct inode* inode;   
    uint32_t dir_pos;	  // 记录在目录内的偏移
    uint8_t dir_buf[512];  // 目录的数据缓存
};

/* 目录项结构 */
struct dir_entry {
    
    
    char filename[MAX_FILE_NAME_LEN];  // 普通文件或目录名称
    uint32_t i_no;		      // 普通文件或目录对应的inode编号
    enum file_types f_type;	      // 文件类型
};

#endif

File type definition: ( myos/fs/fs.h )

#ifndef __FS_FS_H
#define __FS_FS_H
#include "stdint.h"

#define MAX_FILES_PER_PART 4096	    // 每个分区所支持最大创建的文件数
#define BITS_PER_SECTOR 4096	    // 每扇区的位数
#define SECTOR_SIZE 512		    // 扇区字节大小
#define BLOCK_SIZE SECTOR_SIZE	    // 块字节大小

/* 文件类型 */
enum file_types {
    
    
    FT_UNKNOWN,	  // 不支持的文件类型
    FT_REGULAR,	  // 普通文件
    FT_DIRECTORY	  // 目录
};

#endif

Then write a function partition_formatto create the file system, that is, create the file system metadata (super block, free block bitmap, inode bitmap, inode array, root directory)

myos/fs/fs.c

#include "stdint.h"
#include "fs.h"
#include "inode.h"
#include "ide.h"
#include "memory.h"
#include "super_block.h"
#include "dir.h"
#include "stdio-kernel.h"
#include "string.h"



/* 格式化分区,也就是初始化分区的元信息,创建文件系统 */
static void partition_format(struct partition* part) {
    
    
    /* 为方便实现,一个块大小是一扇区 */
    uint32_t boot_sector_sects = 1;	  
    uint32_t super_block_sects = 1;
    uint32_t inode_bitmap_sects = DIV_ROUND_UP(MAX_FILES_PER_PART, BITS_PER_SECTOR);	   // I结点位图占用的扇区数.最多支持4096个文件
    uint32_t inode_table_sects = DIV_ROUND_UP(((sizeof(struct inode) * MAX_FILES_PER_PART)), SECTOR_SIZE);
    uint32_t used_sects = boot_sector_sects + super_block_sects + inode_bitmap_sects + inode_table_sects;
    uint32_t free_sects = part->sec_cnt - used_sects;  

    /************** 简单处理块位图占据的扇区数 ***************/
    uint32_t block_bitmap_sects;
    block_bitmap_sects = DIV_ROUND_UP(free_sects, BITS_PER_SECTOR);
    /* block_bitmap_bit_len是位图中位的长度,也是可用块的数量 */
    uint32_t block_bitmap_bit_len = free_sects - block_bitmap_sects; 
    block_bitmap_sects = DIV_ROUND_UP(block_bitmap_bit_len, BITS_PER_SECTOR); 
    /*********************************************************/
    
    /* 超级块初始化 */
    struct super_block sb;
    sb.magic = 0x19590318;
    sb.sec_cnt = part->sec_cnt;
    sb.inode_cnt = MAX_FILES_PER_PART;
    sb.part_lba_base = part->start_lba;

    sb.block_bitmap_lba = sb.part_lba_base + 2;	 // 第0块是引导块,第1块是超级块
    sb.block_bitmap_sects = block_bitmap_sects;

    sb.inode_bitmap_lba = sb.block_bitmap_lba + sb.block_bitmap_sects;
    sb.inode_bitmap_sects = inode_bitmap_sects;

    sb.inode_table_lba = sb.inode_bitmap_lba + sb.inode_bitmap_sects;
    sb.inode_table_sects = inode_table_sects; 

    sb.data_start_lba = sb.inode_table_lba + sb.inode_table_sects;  //数据区的起始就是inode数组的结束
    sb.root_inode_no = 0;
    sb.dir_entry_size = sizeof(struct dir_entry);

    printk("%s info:\n", part->name);
    printk("   magic:0x%x\n   part_lba_base:0x%x\n   all_sectors:0x%x\n   inode_cnt:0x%x\n   block_bitmap_lba:0x%x\n   block_bitmap_sectors:0x%x\n   inode_bitmap_lba:0x%x\n   inode_bitmap_sectors:0x%x\n   inode_table_lba:0x%x\n   inode_table_sectors:0x%x\n   data_start_lba:0x%x\n", sb.magic, sb.part_lba_base, sb.sec_cnt, sb.inode_cnt, sb.block_bitmap_lba, sb.block_bitmap_sects, sb.inode_bitmap_lba, sb.inode_bitmap_sects, sb.inode_table_lba, sb.inode_table_sects, sb.data_start_lba);

    struct disk* hd = part->my_disk;
    /*******************************
     * 1 将超级块写入本分区的1扇区 *
     ******************************/
    ide_write(hd, part->start_lba + 1, &sb, 1);
    printk("   super_block_lba:0x%x\n", part->start_lba + 1);

    /* 找出数据量最大的元信息,用其尺寸做存储缓冲区*/
    uint32_t buf_size = (sb.block_bitmap_sects >= sb.inode_bitmap_sects ? sb.block_bitmap_sects : sb.inode_bitmap_sects);
    buf_size = (buf_size >= sb.inode_table_sects ? buf_size : sb.inode_table_sects) * SECTOR_SIZE;
    uint8_t* buf = (uint8_t*)sys_malloc(buf_size);	// 申请的内存由内存管理系统清0后返回
    
    /**************************************
     * 2 将块位图初始化并写入sb.block_bitmap_lba *
     *************************************/
    /* 初始化块位图block_bitmap */
    buf[0] |= 0x01;       // 第0个块预留给根目录,位图中先占位
    uint32_t block_bitmap_last_byte = block_bitmap_bit_len / 8; //计算出块位图最后一字节的偏移
    uint8_t  block_bitmap_last_bit  = block_bitmap_bit_len % 8; //计算出块位图最后一位的偏移
    uint32_t last_size = SECTOR_SIZE - (block_bitmap_last_byte % SECTOR_SIZE);	     // last_size是位图所在最后一个扇区中,不足一扇区的其余部分

    /* 1 先将位图最后一字节到其所在的扇区的结束全置为1,即超出实际块数的部分直接置为已占用*/
    memset(&buf[block_bitmap_last_byte], 0xff, last_size);
    
    /* 2 再将上一步中覆盖的最后一字节内的有效位重新置0 */
    uint8_t bit_idx = 0;
    while (bit_idx <= block_bitmap_last_bit) {
    
    
        buf[block_bitmap_last_byte] &= ~(1 << bit_idx++);
    }
    ide_write(hd, sb.block_bitmap_lba, buf, sb.block_bitmap_sects);

    /***************************************
     * 3 将inode位图初始化并写入sb.inode_bitmap_lba *
     ***************************************/
    /* 先清空缓冲区*/
    memset(buf, 0, buf_size);
    buf[0] |= 0x1;      // 第0个inode分给了根目录
    /* 由于inode_table中共4096个inode,位图inode_bitmap正好占用1扇区,
        * 即inode_bitmap_sects等于1, 所以位图中的位全都代表inode_table中的inode,
        * 无须再像block_bitmap那样单独处理最后一扇区的剩余部分,
        * inode_bitmap所在的扇区中没有多余的无效位 */
    ide_write(hd, sb.inode_bitmap_lba, buf, sb.inode_bitmap_sects);

    /***************************************
     * 4 将inode数组初始化并写入sb.inode_table_lba *
     ***************************************/
    /* 准备写inode_table中的第0项,即根目录所在的inode */
    memset(buf, 0, buf_size);  // 先清空缓冲区buf
    struct inode* i = (struct inode*)buf; 
    i->i_size = sb.dir_entry_size * 2;	 // .和..
    i->i_no = 0;   // 根目录占inode数组中第0个inode
    i->i_sectors[0] = sb.data_start_lba;	     // 由于上面的memset,i_sectors数组的其它元素都初始化为0 
    ide_write(hd, sb.inode_table_lba, buf, sb.inode_table_sects);
    
    /***************************************
     * 5 将根目录初始化并写入sb.data_start_lba
     ***************************************/
    /* 写入根目录的两个目录项.和.. */
    memset(buf, 0, buf_size);
    struct dir_entry* p_de = (struct dir_entry*)buf;

    /* 初始化当前目录"." */
    memcpy(p_de->filename, ".", 1);
    p_de->i_no = 0;
    p_de->f_type = FT_DIRECTORY;
    p_de++;

    /* 初始化当前目录父目录".." */
    memcpy(p_de->filename, "..", 2);
    p_de->i_no = 0;   // 根目录的父目录依然是根目录自己
    p_de->f_type = FT_DIRECTORY;

    /* sb.data_start_lba已经分配给了根目录,里面是根目录的目录项 */
    ide_write(hd, sb.data_start_lba, buf, 1);

    printk("   root_dir_lba:0x%x\n", sb.data_start_lba);
    printk("%s format done\n", part->name);
    sys_free(buf);
}

The author's two lines of code only iterated twice, which is not correct. Infinite iteration should be used until the value stabilizes. The problem this code actually solves is: while the total amount remains unchanged, how many are available resources and how many are management resources.

Example 1:
Consider this example: There are 12 people in a company, assuming that one person can manage 5 people. Then the iteration process is as follows:

  • First iteration: 3 people manage, 9 people work.
  • Second iteration: 2 people manage, 10 people work.

Clearly, we got the right answer.

Example 2:
Consider another example: there are 100 people in total, and one person can manage 3 people. Then the iteration process is as follows:

  • The first iteration: 34 people managed, 66 people worked.
  • Second iteration: 22 people manage, 78 people work.
  • The third iteration: 26 people manage and 74 people work.
  • The fourth iteration: 25 people manage, 75 people work.
  • The fifth iteration: 25 people manage, 75 people work.

Eventually, we reach a stable value.

We can conclude that when one person manages a small number of people, a very large number of iterations are needed. But when one person manages a large number of people, only a small number of iterations are needed. Since one of our sectors can manage 4096 sectors, the author's code can work normally in most cases.

Modify ( myos/fs/fs.c/partition_format )

    /************** 简单处理块位图占据的扇区数 ***************/
    uint32_t block_bitmap_sects;
    block_bitmap_sects = DIV_ROUND_UP(free_sects, BITS_PER_SECTOR);
    /* block_bitmap_bit_len是位图中位的长度,也是可用块的数量 */
    uint32_t block_bitmap_bit_len = free_sects - block_bitmap_sects; 
    block_bitmap_sects = DIV_ROUND_UP(block_bitmap_bit_len, BITS_PER_SECTOR); 
    /*********************************************************/

for

    /************** 简单处理块位图占据的扇区数 ***************/
    uint32_t now_total_free_sects = free_sects; // 定义一个现在总的可用扇区数
    uint32_t prev_block_bitmap_sects = 0; // 之前的块位图扇区数
    uint32_t block_bitmap_sects = DIV_ROUND_UP(now_total_free_sects, BITS_PER_SECTOR); // 初始估算
    uint32_t block_bitmap_bit_len;

    while (block_bitmap_sects != prev_block_bitmap_sects) {
    
    
        prev_block_bitmap_sects = block_bitmap_sects;
        /* block_bitmap_bit_len是位图中位的长度,也是可用块的数量 */
        block_bitmap_bit_len = now_total_free_sects - block_bitmap_sects;
        block_bitmap_sects = DIV_ROUND_UP(block_bitmap_bit_len, BITS_PER_SECTOR);
    }
    /*********************************************************/

Since the actual meaning of block_bitmap_last_bit is the number of valid bits in the last byte of the block bitmap, and bit_idx starts from 0, the following while loop will loop one more time and should be modified: (myos/fs/fs.c/ partition_format )

    while (bit_idx <= block_bitmap_last_bit) {
    
    
        buf[block_bitmap_last_byte] &= ~(1 << bit_idx++);
    }

for

    while (bit_idx < block_bitmap_last_bit) {
    
    
        buf[block_bitmap_last_byte] &= ~(1 << bit_idx++);
    }

Then write a function filesys_initto traverse all partitions. If the partition does not have a file system, call it partition_formatto create a file system.

Modify ( myos/fs/fs.c )

#include "debug.h"

/* 在磁盘上搜索文件系统,若没有则格式化分区创建文件系统 */
void filesys_init() {
    
    
    uint8_t channel_no = 0, dev_no, part_idx = 0;

    /* sb_buf用来存储从硬盘上读入的超级块 */
    struct super_block* sb_buf = (struct super_block*)sys_malloc(SECTOR_SIZE);

    if (sb_buf == NULL) {
    
    
        PANIC("alloc memory failed!");
    }
    printk("searching filesystem......\n");
    while (channel_no < channel_cnt) {
    
      //遍历两个通道
        dev_no = 0;
        while(dev_no < 2) {
    
     //遍历通道下1主1从两个硬盘
            if (dev_no == 0) {
    
       // 跨过裸盘hd60M.img
                dev_no++;
                continue;
            }
            struct disk* hd = &channels[channel_no].devices[dev_no];
            struct partition* part = hd->prim_parts;
            while(part_idx < 12) {
    
       // 遍历硬盘的分区,4个主分区+8个逻辑
                if (part_idx == 4) {
    
      // 开始处理逻辑分区
                    part = hd->logic_parts;
                }
            
            /* channels数组是全局变量,默认值为0,disk属于其嵌套结构,
            * partition又为disk的嵌套结构,因此partition中的成员默认也为0.
            * 若partition未初始化,则partition中的成员仍为0. 
            * 下面处理存在的分区. */
                if (part->sec_cnt != 0) {
    
      // 如果分区存在
                    memset(sb_buf, 0, SECTOR_SIZE);

                    /* 读出分区的超级块,根据魔数是否正确来判断是否存在文件系统 */
                    ide_read(hd, part->start_lba + 1, sb_buf, 1);   

                    /* 只支持自己的文件系统.若磁盘上已经有文件系统就不再格式化了 */
                    if (sb_buf->magic == 0x19590318) {
    
    
                        printk("%s has filesystem\n", part->name);
                    } 
                    else {
    
    			  // 其它文件系统不支持,一律按无文件系统处理
                        printk("formatting %s`s partition %s......\n", hd->name, part->name);
                        partition_format(part);
                    }
                }
                part_idx++;
                part++;	// 下一分区
            }
            dev_no++;	// 下一磁盘
        }
        channel_no++;	// 下一通道
    }
    sys_free(sb_buf);
}

Function declaration modification ( myos/fs/fs.h )

void filesys_init(void);

Modify ( myos/kernel/init.c ) to initialize the file system

#include "fs.h"

/*负责初始化所有模块 */
void init_all() {
	put_str("init_all\n");
	idt_init();	     // 初始化中断
	mem_init();	     // 初始化内存管理系统
	thread_init();    // 初始化线程相关结构
	timer_init();     // 初始化PIT
	console_init();   // 控制台初始化最好放在开中断之前
	keyboard_init();  // 键盘初始化
	tss_init();       // tss初始化
	syscall_init();   // 初始化系统调用
	intr_enable();    // 后面的ide_init需要打开中断
	ide_init();	     // 初始化硬盘
	filesys_init();   // 初始化文件系统
}

Test code ( myos/kernel/main.c )

#include "print.h"
#include "init.h"
#include "thread.h"
#include "interrupt.h"
#include "console.h"
#include "process.h"
#include "syscall-init.h"
#include "syscall.h"
#include "stdio.h"
#include "memory.h"

void k_thread_a(void*);
void k_thread_b(void*);
void u_prog_a(void);
void u_prog_b(void);

int main(void) {
    
    
   put_str("I am kernel\n");
   init_all();
   while(1);
   process_execute(u_prog_a, "u_prog_a");
   process_execute(u_prog_b, "u_prog_b");
   thread_start("k_thread_a", 31, k_thread_a, "I am thread_a");
   thread_start("k_thread_b", 31, k_thread_b, "I am thread_b");
   while(1);
   return 0;
}

/* 在线程中运行的函数 */
void k_thread_a(void* arg) {
    
         
   void* addr1 = sys_malloc(256);
   void* addr2 = sys_malloc(255);
   void* addr3 = sys_malloc(254);
   console_put_str(" thread_a malloc addr:0x");
   console_put_int((int)addr1);
   console_put_char(',');
   console_put_int((int)addr2);
   console_put_char(',');
   console_put_int((int)addr3);
   console_put_char('\n');

   int cpu_delay = 100000;
   while(cpu_delay-- > 0);
   sys_free(addr1);
   sys_free(addr2);
   sys_free(addr3);
   while(1);
}

/* 在线程中运行的函数 */
void k_thread_b(void* arg) {
    
         
   void* addr1 = sys_malloc(256);
   void* addr2 = sys_malloc(255);
   void* addr3 = sys_malloc(254);
   console_put_str(" thread_b malloc addr:0x");
   console_put_int((int)addr1);
   console_put_char(',');
   console_put_int((int)addr2);
   console_put_char(',');
   console_put_int((int)addr3);
   console_put_char('\n');

   int cpu_delay = 100000;
   while(cpu_delay-- > 0);
   sys_free(addr1);
   sys_free(addr2);
   sys_free(addr3);
   while(1);
}

/* 测试用户进程 */
void u_prog_a(void) {
    
    
   void* addr1 = malloc(256);
   void* addr2 = malloc(255);
   void* addr3 = malloc(254);
   printf(" prog_a malloc addr:0x%x,0x%x,0x%x\n", (int)addr1, (int)addr2, (int)addr3);

   int cpu_delay = 100000;
   while(cpu_delay-- > 0);
   free(addr1);
   free(addr2);
   free(addr3);
   while(1);
}

/* 测试用户进程 */
void u_prog_b(void) {
    
    
   void* addr1 = malloc(256);
   void* addr2 = malloc(255);
   void* addr3 = malloc(254);
   printf(" prog_b malloc addr:0x%x,0x%x,0x%x\n", (int)addr1, (int)addr2, (int)addr3);

   int cpu_delay = 100000;
   while(cpu_delay-- > 0);
   free(addr1);
   free(addr2);
   free(addr3);
   while(1);
}

In addition to adding rules for compiling fs.c, the Makefile also needs to add-I fs/

LIB= -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/ -I thread/ -I userprog/	-I fs/

It is recommended to back up before running hd80M.img, otherwise if something goes wrong, meaningless data may be written in it. Order:cp hd80M.img hd80M.img-beifen

Section b:

Previously we wrote functions to create file system metadata and write it to disk. When we actually want to use the data of a certain partition, we need to mount the partition, that is, read the file system metadata (super block, block bitmap, inode bitmap, inode array because it is too large and cannot be read in). to the memory,) to the memory, and then changes to these metadata must be synchronized to the disk in a timely manner. Now let's write this function to mount the partition mount_partition. This function will be list_traversalcalled.

Modify ( myos/fs/fs.c )

struct partition* cur_part;	 // 默认情况下操作的是哪个分区

/* 在分区链表中找到名为part_name的分区,并将其指针赋值给cur_part */
static bool mount_partition(struct list_elem* pelem, int arg) {
    
    
    char* part_name = (char*)arg;
    struct partition* part = elem2entry(struct partition, part_tag, pelem);
    if (!strcmp(part->name, part_name)) {
    
    
        cur_part = part;
        struct disk* hd = cur_part->my_disk;

        /* sb_buf用来存储从硬盘上读入的超级块 */
        struct super_block* sb_buf = (struct super_block*)sys_malloc(SECTOR_SIZE);

        /* 在内存中创建分区cur_part的超级块 */
        cur_part->sb = (struct super_block*)sys_malloc(sizeof(struct super_block));
        if (cur_part->sb == NULL) {
    
    
            PANIC("alloc memory failed!");
        }

        /* 读入超级块 */
        memset(sb_buf, 0, SECTOR_SIZE);
        ide_read(hd, cur_part->start_lba + 1, sb_buf, 1);   

        /* 把sb_buf中超级块的信息复制到分区的超级块sb中。*/
        memcpy(cur_part->sb, sb_buf, sizeof(struct super_block)); 

        /**********     将硬盘上的块位图读入到内存    ****************/
        cur_part->block_bitmap.bits = (uint8_t*)sys_malloc(sb_buf->block_bitmap_sects * SECTOR_SIZE);
        if (cur_part->block_bitmap.bits == NULL) {
    
    
            PANIC("alloc memory failed!");
        }
        cur_part->block_bitmap.btmp_bytes_len = sb_buf->block_bitmap_sects * SECTOR_SIZE;
        /* 从硬盘上读入块位图到分区的block_bitmap.bits */
        ide_read(hd, sb_buf->block_bitmap_lba, cur_part->block_bitmap.bits, sb_buf->block_bitmap_sects);   
        /*************************************************************/

        /**********     将硬盘上的inode位图读入到内存    ************/
        cur_part->inode_bitmap.bits = (uint8_t*)sys_malloc(sb_buf->inode_bitmap_sects * SECTOR_SIZE);
        if (cur_part->inode_bitmap.bits == NULL) {
    
    
            PANIC("alloc memory failed!");
        }
        cur_part->inode_bitmap.btmp_bytes_len = sb_buf->inode_bitmap_sects * SECTOR_SIZE;
        /* 从硬盘上读入inode位图到分区的inode_bitmap.bits */
        ide_read(hd, sb_buf->inode_bitmap_lba, cur_part->inode_bitmap.bits, sb_buf->inode_bitmap_sects);   
        /*************************************************************/

        list_init(&cur_part->open_inodes);
        printk("mount %s done!\n", part->name);

    /* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关。
        只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历.*/
        return true;
    }
    return false;     // 使list_traversal继续遍历
}

Modify ( myos/fs/fs.c ) filesys_initand add the code to mount the file system of the specified partition using list_traversalthe callmount_partition

/* 在磁盘上搜索文件系统,若没有则格式化分区创建文件系统 */
void filesys_init() {
    
    
    uint8_t channel_no = 0, dev_no, part_idx = 0;

    /* sb_buf用来存储从硬盘上读入的超级块 */
    struct super_block* sb_buf = (struct super_block*)sys_malloc(SECTOR_SIZE);

    if (sb_buf == NULL) {
    
    
        PANIC("alloc memory failed!");
    }
    printk("searching filesystem......\n");
    while (channel_no < channel_cnt) {
    
      //遍历两个通道
        dev_no = 0;
        while(dev_no < 2) {
    
     //遍历通道下1主1从两个硬盘
            if (dev_no == 0) {
    
       // 跨过裸盘hd60M.img
                dev_no++;
                continue;
            }
            struct disk* hd = &channels[channel_no].devices[dev_no];
            struct partition* part = hd->prim_parts;
            while(part_idx < 12) {
    
       // 遍历硬盘的分区,4个主分区+8个逻辑
                if (part_idx == 4) {
    
      // 开始处理逻辑分区
                    part = hd->logic_parts;
                }
            
            /* channels数组是全局变量,默认值为0,disk属于其嵌套结构,
            * partition又为disk的嵌套结构,因此partition中的成员默认也为0.
            * 若partition未初始化,则partition中的成员仍为0. 
            * 下面处理存在的分区. */
                if (part->sec_cnt != 0) {
    
      // 如果分区存在
                    memset(sb_buf, 0, SECTOR_SIZE);

                    /* 读出分区的超级块,根据魔数是否正确来判断是否存在文件系统 */
                    ide_read(hd, part->start_lba + 1, sb_buf, 1);   

                    /* 只支持自己的文件系统.若磁盘上已经有文件系统就不再格式化了 */
                    if (sb_buf->magic == 0x19590318) {
    
    
                        printk("%s has filesystem\n", part->name);
                    } 
                    else {
    
    			  // 其它文件系统不支持,一律按无文件系统处理
                        printk("formatting %s`s partition %s......\n", hd->name, part->name);
                        partition_format(part);
                    }
                }
                part_idx++;
                part++;	// 下一分区
            }
            dev_no++;	// 下一磁盘
        }
        channel_no++;	// 下一通道
    }
    sys_free(sb_buf);
    /* 确定默认操作的分区 */
   char default_part[8] = "sdb1";
   /* 挂载分区 */
   list_traversal(&partition_list, mount_partition, (int)default_part);
}

support code

Modify ( myos/device/ide.h )

(This partition linked list is created when we call it ide_initin the mount disk function , so we created the partition file system in the previous section. In fact, we can traverse this linked list)partition_scan

extern struct list partition_list;

mount_partitionWhat is inside sb_bufis not released! Will cause memory leaks!

Modify ( myos/fs/fs.c/mountpartition )

        list_init(&cur_part->open_inodes);
        printk("mount %s done!\n", part->name);

    /* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关。
        只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历.*/
        return true;
    }
    return false;     // 使list_traversal继续遍历

for

        list_init(&cur_part->open_inodes);
        printk("mount %s done!\n", part->name);

    /* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关。
        只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历.*/
        sys_free(sb_buf);
        return true;
    }
    return false;     // 使list_traversal继续遍历

Section c:

The core function of a file system is to manage the mapping relationship between files (inodes) and their storage locations on disk. But when we talk about actual file operations (such as open, read, write), these operations are always performed based on a specific process or thread. So we need a mechanism to establish a mapping of processes or threads to files (inodes), which is what file descriptors do. The file descriptor is implemented based on the "file structure".

The file structure describes in detail the current operating status of a file by the application process:

  • When an application process operates (for example, opens) a file, the operating system will create a corresponding global file structure and place it in the global file structure array (also called a file table) to track the process's operation status on the file.
  • The file structure contains a fd_posfield that represents the location of the current process operation. For example, when we open a file test.txt, fd_posthe initial value is usually the beginning of the file.
  • When we perform actual read and write operations on the file, fd_posit will be updated to the location of the operation. For example, if you start writing data in the middle of the file, fd_posit will be updated to that position and continue to move forward as the write operation occurs. When performing an editing operation, the input content is first stored in a memory input buffer. After pressing ctrl + s, the operating system will use as the starting point in the file structure fd_pos, write the contents of the buffer to the disk file, and update fd_posto the position after the writing is completed.
  • The file structure also contains a field fd_inodethat is a pointer to the inode of the corresponding file. This fd_inodefield allows the operating system to easily find the location of the file on disk and write the contents of the memory input buffer to disk.

Insert image description here
Since the file structure is highly related to the specific application, we need to introduce a new concept: the file descriptor array .

  • The file descriptor array is an array stored in the process control block (PCB) of the process. Each element is a file descriptor (just a number) that points to a specific entry in the global array of file structures.

  • When a process opens a file, the operating system creates a file structure for the file and places it in the global file structure array. The operating system then assigns the file structure's position (or index) in the global array to a free element in the process's file descriptor array.

    For example, a process opens two files, and their positions in the global file structure array are 32 and 58 respectively. Then, the 0th element of the file descriptor array in the process's PCB will be assigned a value of 32, and the first element will be assigned a value of 58.

  • The size of the file descriptor array in the PCB limits the maximum number of files that a process can open simultaneously. If a process opens a new file but the file descriptor array is full, the operation will fail.

  • It should be noted that opening different files will of course consume a file descriptor. But since we introduced the concept of file operation location in the file structure (i.e. fd_pos), if a process opens the same file multiple times (i.e. each opening has its own read and write location), then each opening will consume a new File descriptor.
    Insert image description here
    There are a lot of functions in the file system chapter. In fact, the core is to deal with the interaction between the four concepts of super block, inode, directory, and file structure .

In order to support file descriptors, we need to modify task_structthe definition of the structure. (Also added parent_idwith cwd_inode_nr)

Modify ( myos/thread/thread.h )

#define MAX_FILES_OPEN_PER_PROC 8

/* 进程或线程的pcb,程序控制块, 此结构体用于存储线程的管理信息*/
struct task_struct
{
    
    
    uint32_t *self_kstack; // 用于存储线程的栈顶位置,栈顶放着线程要用到的运行信息
    pid_t pid;
    enum task_status status;
    uint8_t priority; // 线程优先级
    char name[16];    // 用于存储自己的线程的名字

    uint8_t ticks;                                // 线程允许上处理器运行还剩下的滴答值,因为priority不能改变,所以要在其之外另行定义一个值来倒计时
    uint32_t elapsed_ticks;                       // 此任务自上cpu运行后至今占用了多少cpu嘀嗒数, 也就是此任务执行了多久*/
    struct list_elem general_tag;                 // general_tag的作用是用于线程在一般的队列(如就绪队列或者等待队列)中的结点
    struct list_elem all_list_tag;                // all_list_tag的作用是用于线程队列thread_all_list(这个队列用于管理所有线程)中的结点
    uint32_t *pgdir;                              // 进程自己页表的虚拟地址
    struct virtual_addr userprog_vaddr;           // 用户进程的虚拟地址
    int32_t fd_table[MAX_FILES_OPEN_PER_PROC];    // 已打开文件数组
    uint32_t cwd_inode_nr;                        // 进程所在的工作目录的inode编号
    int16_t parent_pid;                           // 父进程pid
    struct mem_block_desc u_block_desc[DESC_CNT]; // 用户进程内存块描述符
    uint32_t stack_magic;                         // 如果线程的栈无限生长,总会覆盖地pcb的信息,那么需要定义个边界数来检测是否栈已经到了PCB的边界
};

In the initialization pcb link of creating a process/thread, add the file descriptor array and parent_idinitialization cwd_inode_nrcode

Modify ( myos/thread/thread.c )

/* 初始化线程基本信息 , pcb中存储的是线程的管理信息,此函数用于根据传入的pcb的地址,线程的名字等来初始化线程的管理信息*/
void init_thread(struct task_struct *pthread, char *name, int prio)
{
    
    
    memset(pthread, 0, sizeof(*pthread)); // 把pcb初始化为0
    pthread->pid = allocate_pid();
    strcpy(pthread->name, name); // 将传入的线程的名字填入线程的pcb中

    if (pthread == main_thread)
    {
    
    
        pthread->status = TASK_RUNNING; // 由于把main函数也封装成一个线程,并且它一直是运行的,故将其直接设为TASK_RUNNING */
    }
    else
    {
    
    
        pthread->status = TASK_READY;
    }
    pthread->priority = prio;
    /* self_kstack是线程自己在内核态下使用的栈顶地址 */
    pthread->ticks = prio;
    pthread->elapsed_ticks = 0;
    pthread->pgdir = NULL;                                            // 线程没有自己的地址空间,进程的pcb这一项才有用,指向自己的页表虚拟地址
    pthread->self_kstack = (uint32_t *)((uint32_t)pthread + PG_SIZE); // 本操作系统比较简单,线程不会太大,就将线程栈顶定义为pcb地址
                                                                      //+4096的地方,这样就留了一页给线程的信息(包含管理信息与运行信息)空间
    /* 标准输入输出先空出来 */
    pthread->fd_table[0] = 0;
    pthread->fd_table[1] = 1;
    pthread->fd_table[2] = 2;
    /* 其余的全置为-1 */
    uint8_t fd_idx = 3;
    while (fd_idx < MAX_FILES_OPEN_PER_PROC)
    {
    
    
        pthread->fd_table[fd_idx] = -1;
        fd_idx++;
    }
    pthread->cwd_inode_nr = 0;         // 以根目录做为默认工作路径
    pthread->parent_pid = -1;          // -1表示没有父进程
    pthread->stack_magic = 0x19870916; // 定义的边界数字,随便选的数字来判断线程的栈是否已经生长到覆盖pcb信息了
}

Any operation related to files and directories is inseparable from the operation of inode, because we need to know the storage location of the file through the inode, so operating a file always means finding the inode of the file. Next we implement a bunch of inode processing functions. Involves: finding the location of an inode in the disk, initializing an inode, loading the inode into the memory, modifying the inode in the memory and then synchronizing it to the disk, and deleting an inode from the memory.

inode_locateUsed to obtain the sector where the inode is located and the offset within the sector by passing in the index of the specified inode in the inode array. Principle: Since the index of the inode in the inode array is passed in, and the starting sector of the inode array has been recorded in the super block (after the file system is mounted, the super block is in the memory). So we can calculate the sector where the inode is located and the offset within the sector by indexing * the size of each inode.

( myos/fs/inode.c )

#include "stdint.h"
#include "ide.h"
#include "inode.h"
#include "debug.h"
#include "super_block.h"

/* 用来存储inode位置 */
struct inode_position {
    
    
   bool	 two_sec;	// inode是否跨扇区
   uint32_t sec_lba;	// inode所在的扇区号
   uint32_t off_size;	// inode在扇区内的字节偏移量
};

/* 获取inode所在的扇区和扇区内的偏移量 */
static void inode_locate(struct partition *part, uint32_t inode_no, struct inode_position *inode_pos)
{
    
    
    /* inode_table在硬盘上是连续的 */
    ASSERT(inode_no < 4096);
    uint32_t inode_table_lba = part->sb->inode_table_lba;

    uint32_t inode_size = sizeof(struct inode);
    uint32_t off_size = inode_no * inode_size; // 第inode_no号I结点相对于inode_table_lba的字节偏移量
    uint32_t off_sec = off_size / 512;         // 第inode_no号I结点相对于inode_table_lba的扇区偏移量
    uint32_t off_size_in_sec = off_size % 512; // 待查找的inode所在扇区中的起始地址

    /* 判断此i结点是否跨越2个扇区 */
    uint32_t left_in_sec = 512 - off_size_in_sec;
    if (left_in_sec < inode_size)
    {
    
     // 若扇区内剩下的空间不足以容纳一个inode,必然是I结点跨越了2个扇区
        inode_pos->two_sec = true;
    }
    else
    {
    
     // 否则,所查找的inode未跨扇区
        inode_pos->two_sec = false;
    }
    inode_pos->sec_lba = inode_table_lba + off_sec;
    inode_pos->off_size = off_size_in_sec;
}

inode_syncUsed to write an inode to the disk at the location corresponding to the inode array. Principle: Call to inode_locateparse the location of the inode on the disk, then read the entire block where the inode is located into the memory buffer, then write the inode to the corresponding location in the buffer, and then write the entire block back to the disk.

Modify ( myos/fs/inode.c )

#include "string.h"

/* 将inode写入到分区part */
void inode_sync(struct partition *part, struct inode *inode, void *io_buf)
{
    
     // io_buf是用于硬盘io的缓冲区
    uint8_t inode_no = inode->i_no;
    struct inode_position inode_pos;
    inode_locate(part, inode_no, &inode_pos); // inode位置信息会存入inode_pos
    ASSERT(inode_pos.sec_lba <= (part->start_lba + part->sec_cnt));

    /* 硬盘中的inode中的成员inode_tag和i_open_cnts是不需要的,
     * 它们只在内存中记录链表位置和被多少进程共享 */
    struct inode pure_inode;
    memcpy(&pure_inode, inode, sizeof(struct inode));

    /* 以下inode的三个成员只存在于内存中,现在将inode同步到硬盘,清掉这三项即可 */
    pure_inode.i_open_cnts = 0;
    pure_inode.write_deny = false; // 置为false,以保证在硬盘中读出时为可写
    pure_inode.inode_tag.prev = pure_inode.inode_tag.next = NULL;

    char *inode_buf = (char *)io_buf;
    if (inode_pos.two_sec)
    {
    
                                                                 // 若是跨了两个扇区,就要读出两个扇区再写入两个扇区
                                                                  /* 读写硬盘是以扇区为单位,若写入的数据小于一扇区,要将原硬盘上的内容先读出来再和新数据拼成一扇区后再写入  */
        ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 2); // inode在format中写入硬盘时是连续写入的,所以读入2块扇区

        /* 开始将待写入的inode拼入到这2个扇区中的相应位置 */
        memcpy((inode_buf + inode_pos.off_size), &pure_inode, sizeof(struct inode));

        /* 将拼接好的数据再写入磁盘 */
        ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 2);
    }
    else
    {
    
     // 若只是一个扇区
        ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 1);
        memcpy((inode_buf + inode_pos.off_size), &pure_inode, sizeof(struct inode));
        ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 1);
    }
}

inode_openUsed to open an inode, that is, find the inode according to the inode array index passed in and load it into memory. Since we maintain an inode linked list of open files in this partition in the struct partition that manages the partition, we first search in this linked list (this linked list is in memory). If we cannot find it, we will search in the disk, that is, call inode_locateParse its location on disk and read it into memory. Since the open Inode linked list needs to be shared by all processes, all the memory we store inodes needs to be applied for in the kernel heap area (because all processes can access the kernel)

Modify ( myos/fs/inode.c )

/* 根据i结点号返回相应的i结点 */
struct inode *inode_open(struct partition *part, uint32_t inode_no)
{
    
    
    /* 先在已打开inode链表中找inode,此链表是为提速创建的缓冲区 */
    struct list_elem *elem = part->open_inodes.head.next;
    struct inode *inode_found;
    while (elem != &part->open_inodes.tail)
    {
    
    
        inode_found = elem2entry(struct inode, inode_tag, elem);
        if (inode_found->i_no == inode_no)
        {
    
    
            inode_found->i_open_cnts++;
            return inode_found;
        }
        elem = elem->next;
    }

    /*由于open_inodes链表中找不到,下面从硬盘上读入此inode并加入到此链表 */
    struct inode_position inode_pos;

    /* inode位置信息会存入inode_pos, 包括inode所在扇区地址和扇区内的字节偏移量 */
    inode_locate(part, inode_no, &inode_pos);

    /* 为使通过sys_malloc创建的新inode被所有任务共享,
     * 需要将inode置于内核空间,故需要临时
     * 将cur_pbc->pgdir置为NULL */
    struct task_struct *cur = running_thread();
    uint32_t *cur_pagedir_bak = cur->pgdir;
    cur->pgdir = NULL;
    /* 以上三行代码完成后下面分配的内存将位于内核区 */
    inode_found = (struct inode *)sys_malloc(sizeof(struct inode));
    /* 恢复pgdir */
    cur->pgdir = cur_pagedir_bak;

    char *inode_buf;
    if (inode_pos.two_sec)
    {
    
     // 考虑跨扇区的情况
        inode_buf = (char *)sys_malloc(1024);

        /* i结点表是被partition_format函数连续写入扇区的,
         * 所以下面可以连续读出来 */
        ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 2);
    }
    else
    {
    
     // 否则,所查找的inode未跨扇区,一个扇区大小的缓冲区足够
        inode_buf = (char *)sys_malloc(512);
        ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 1);
    }
    memcpy(inode_found, inode_buf + inode_pos.off_size, sizeof(struct inode));

    /* 因为一会很可能要用到此inode,故将其插入到队首便于提前检索到 */
    list_push(&part->open_inodes, &inode_found->inode_tag);
    inode_found->i_open_cnts = 1;

    sys_free(inode_buf);
    return inode_found;
}

inode_closeUsed to close an inode, that is, remove an inode from memory. Since an inode can be opened multiple times, we need to determine whether there is still a process/thread opening the inode at this time, and then decide whether to actually remove it. The memory space occupied by the removed inode is the kernel's heap space.

Modify ( myos/fs/inode.c )

#include "interrupt.h"

/* 关闭inode或减少inode的打开数 */
void inode_close(struct inode *inode)
{
    
    
    /* 若没有进程再打开此文件,将此inode去掉并释放空间 */
    enum intr_status old_status = intr_disable();
    if (--inode->i_open_cnts == 0)
    {
    
    
        list_remove(&inode->inode_tag); // 将I结点从part->open_inodes中去掉
                                        /* inode_open时为实现inode被所有进程共享,
                                         * 已经在sys_malloc为inode分配了内核空间,
                                         * 释放inode时也要确保释放的是内核内存池 */
        struct task_struct *cur = running_thread();
        uint32_t *cur_pagedir_bak = cur->pgdir;
        cur->pgdir = NULL;
        sys_free(inode);
        cur->pgdir = cur_pagedir_bak;
    }
    intr_set_status(old_status);
}

inode_initInitialize an inode by passing in the inode number

Modify ( myos/fs/inode.c )

/* 初始化new_inode */
void inode_init(uint32_t inode_no, struct inode *new_inode)
{
    
    
    new_inode->i_no = inode_no;
    new_inode->i_size = 0;
    new_inode->i_open_cnts = 0;
    new_inode->write_deny = false;

    /* 初始化块索引数组i_sector */
    uint8_t sec_idx = 0;
    while (sec_idx < 13)
    {
    
    
        /* i_sectors[12]为一级间接块地址 */
        new_inode->i_sectors[sec_idx] = 0;
        sec_idx++;
    }
}

Function declaration: modified ( myos/fs/fs.h )

void inode_sync(struct partition* part, struct inode* inode, void* io_buf);
struct inode *inode_open(struct partition *part, uint32_t inode_no);
void inode_close(struct inode *inode);
void inode_init(uint32_t inode_no, struct inode *new_inode);

Next, implement a bunch of directory-related functions, including opening and closing the directory, finding the specified directory entry in a directory file, initializing a directory entry, and writing the directory entry to the parent directory.

open_root_dirinode_openUsed to call the inode of the root directory file into the memory according to the incoming partition , and struct root_dirpoint the inode pointer in the global variable to the inode of the root directory file.

( myos/fs/dir.c )

#include "dir.h"
#include "inode.h"
#include "super_block.h"


struct dir root_dir; // 根目录

/* 打开根目录 */
void open_root_dir(struct partition *part)
{
    
    
    root_dir.inode = inode_open(part, part->sb->root_inode_no);
    root_dir.dir_pos = 0;
}

dir_openUsed to apply for a memory area to store the directory based on the incoming partition and inode offset, call to inode_openfind the inode corresponding to this directory and load it into the memory, and then let the inode pointer in the directory point to this inode. The functions are open_root_dirsimilar, except that one directly modifies global variables, and the other requires you to apply for memory yourself.

Modify ( myos/fs/dir.c )

/* 在分区part上打开i结点为inode_no的目录并返回目录指针 */
struct dir *dir_open(struct partition *part, uint32_t inode_no)
{
    
    
    struct dir *pdir = (struct dir *)sys_malloc(sizeof(struct dir));
    pdir->inode = inode_open(part, inode_no);
    pdir->dir_pos = 0;
    return pdir;
}

So we can find that opening a directory is to establish the relationship between the directory (struct dir structure) and inode. Why? Because the directory is a concept in memory, but it itself is a file on the disk! In order to establish the relationship from memory to disk, we need to establish the relationship between struct dir and inode. So when we open the directory, it naturally means that the struct dir structure must be in memory. Thinking further, when we open a directory, it must mean to find something in this directory (just like you open a folder to find a file under the folder), and only the inode records the location of this directory file on the disk. location, and a directory file is a collection of directory entries that record what is in this directory. So in order to find what is in the directory, we must find its inode, transfer it into the memory, and establish the relationship between the two.

search_dir_entryUsed to find a directory entry with a specified name within a directory. The core principle is that the directory structure struct dir has a member pointing to its own inode. This member records the location where the directory is stored on the disk (inode's i_sectors[ ]). We can find the directory file based on this, and then traverse the directory entries in it, that is Can

Modify ( myos/fs/dir.c )

#include "stdio-kernel.h"
#include "string.h"

/* 在part分区内的pdir目录内寻找名为name的文件或目录,
 * 找到后返回true并将其目录项存入dir_e,否则返回false */
bool search_dir_entry(struct partition *part, struct dir *pdir, const char *name, struct dir_entry *dir_e)
{
    
    
    uint32_t block_cnt = 140; // 12个直接块+128个一级间接块=140块

    /* 12个直接块大小+128个间接块,共560字节 */
    uint32_t *all_blocks = (uint32_t *)sys_malloc(48 + 512);
    if (all_blocks == NULL)
    {
    
    
        printk("search_dir_entry: sys_malloc for all_blocks failed");
        return false;
    }

    uint32_t block_idx = 0;
    while (block_idx < 12)
    {
    
    
        all_blocks[block_idx] = pdir->inode->i_sectors[block_idx];
        block_idx++;
    }
    block_idx = 0;

    if (pdir->inode->i_sectors[12] != 0)
    {
    
     // 若含有一级间接块表
        ide_read(part->my_disk, pdir->inode->i_sectors[12], all_blocks + 12, 1);
    }
    /* 至此,all_blocks存储的是该文件或目录的所有扇区地址 */

    /* 写目录项的时候已保证目录项不跨扇区,
     * 这样读目录项时容易处理, 只申请容纳1个扇区的内存 */
    uint8_t *buf = (uint8_t *)sys_malloc(SECTOR_SIZE);
    struct dir_entry *p_de = (struct dir_entry *)buf; // p_de为指向目录项的指针,值为buf起始地址
    uint32_t dir_entry_size = part->sb->dir_entry_size;
    uint32_t dir_entry_cnt = SECTOR_SIZE / dir_entry_size; // 1扇区内可容纳的目录项个数

    /* 开始在所有块中查找目录项 */
    while (block_idx < block_cnt)
    {
    
    
        /* 块地址为0时表示该块中无数据,继续在其它块中找 */
        if (all_blocks[block_idx] == 0)
        {
    
    
            block_idx++;
            continue;
        }
        ide_read(part->my_disk, all_blocks[block_idx], buf, 1);

        uint32_t dir_entry_idx = 0;
        /* 遍历扇区中所有目录项 */
        while (dir_entry_idx < dir_entry_cnt)
        {
    
    
            /* 若找到了,就直接复制整个目录项 */
            if (!strcmp(p_de->filename, name))
            {
    
    
                memcpy(dir_e, p_de, dir_entry_size);
                sys_free(buf);
                sys_free(all_blocks);
                return true;
            }
            dir_entry_idx++;
            p_de++;
        }
        block_idx++;
        p_de = (struct dir_entry *)buf; // 此时p_de已经指向扇区内最后一个完整目录项了,需要恢复p_de指向为buf
        memset(buf, 0, SECTOR_SIZE);    // 将buf清0,下次再用
    }
    sys_free(buf);
    sys_free(all_blocks);
    return false;
}

dir_closeUsed to close a directory, the essence is to call to inode_closerelease the memory occupied by the inode of the directory from the memory, and release the memory occupied by the directory, that is, to unbind the relationship between struct dir and inode. It should be noted that the root directory cannot be released because it will be used repeatedly.

Modify ( myos/fs/dir.c )

/* 关闭目录 */
void dir_close(struct dir *dir)
{
    
    
    /*************      根目录不能关闭     ***************
     *1 根目录自打开后就不应该关闭,否则还需要再次open_root_dir();
     *2 root_dir所在的内存是低端1M之内,并非在堆中,free会出问题 */
    if (dir == &root_dir)
    {
    
    
        /* 不做任何处理直接返回*/
        return;
    }
    inode_close(dir->inode);
    sys_free(dir);
}

create_dir_entryUsed to initialize a directory entry, that is, give the file pointed to by the directory entry a name and inode index.

Modify ( myos/fs/dir.c )

#include "debug.h"

/* 在内存中初始化目录项p_de */
void create_dir_entry(char *filename, uint32_t inode_no, uint8_t file_type, struct dir_entry *p_de)
{
    ASSERT(strlen(filename) <= MAX_FILE_NAME_LEN);

    /* 初始化目录项 */
    memcpy(p_de->filename, filename, strlen(filename));
    p_de->i_no = inode_no;
    p_de->f_type = file_type;
}

sync_dir_entryUsed to write directory entries into the parent directory. The core principle is: the parent directory structure struct dir has a member pointing to its own inode. This member records the location where the parent directory file is stored on the disk (inode's i_sectors[]). We can find the directory file based on this, and then traverse the directory. Find the empty space in the file, and then insert the directory entry into the empty space. Some trouble is that we need to determine whether the i_sectors[] elements of the inode are empty. If they are empty, then we also need to apply for a block to host the directory file.

Modify ( myos/fs/dir.c )

#include "file.h"

/* 将目录项p_de写入父目录parent_dir中,io_buf由主调函数提供 */
bool sync_dir_entry(struct dir *parent_dir, struct dir_entry *p_de, void *io_buf)
{
    
    
    struct inode *dir_inode = parent_dir->inode;
    uint32_t dir_size = dir_inode->i_size;
    uint32_t dir_entry_size = cur_part->sb->dir_entry_size;

    ASSERT(dir_size % dir_entry_size == 0); // dir_size应该是dir_entry_size的整数倍

    uint32_t dir_entrys_per_sec = (512 / dir_entry_size); // 每扇区最大的目录项数目
    int32_t block_lba = -1;

    /* 将该目录的所有扇区地址(12个直接块+ 128个间接块)存入all_blocks */
    uint8_t block_idx = 0;
    uint32_t all_blocks[140] = {
    
    0}; // all_blocks保存目录所有的块

    /* 将12个直接块存入all_blocks */
    while (block_idx < 12)
    {
    
    
        all_blocks[block_idx] = dir_inode->i_sectors[block_idx];
        block_idx++;
    }

    struct dir_entry *dir_e = (struct dir_entry *)io_buf; // dir_e用来在io_buf中遍历目录项
    int32_t block_bitmap_idx = -1;

    /* 开始遍历所有块以寻找目录项空位,若已有扇区中没有空闲位,
     * 在不超过文件大小的情况下申请新扇区来存储新目录项 */
    block_idx = 0;
    while (block_idx < 140)
    {
    
     // 文件(包括目录)最大支持12个直接块+128个间接块=140个块
        block_bitmap_idx = -1;
        if (all_blocks[block_idx] == 0)
        {
    
     // 在三种情况下分配块
            block_lba = block_bitmap_alloc(cur_part);
            if (block_lba == -1)
            {
    
    
                printk("alloc block bitmap for sync_dir_entry failed\n");
                return false;
            }

            /* 每分配一个块就同步一次block_bitmap */
            block_bitmap_idx = block_lba - cur_part->sb->data_start_lba;
            ASSERT(block_bitmap_idx != -1);
            bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);

            block_bitmap_idx = -1;
            if (block_idx < 12)
            {
    
     // 若是直接块
                dir_inode->i_sectors[block_idx] = all_blocks[block_idx] = block_lba;
            }
            else if (block_idx == 12)
            {
    
                                             // 若是尚未分配一级间接块表(block_idx等于12表示第0个间接块地址为0)
                dir_inode->i_sectors[12] = block_lba; // 将上面分配的块做为一级间接块表地址
                block_lba = -1;
                block_lba = block_bitmap_alloc(cur_part); // 再分配一个块做为第0个间接块
                if (block_lba == -1)
                {
    
    
                    block_bitmap_idx = dir_inode->i_sectors[12] - cur_part->sb->data_start_lba;
                    bitmap_set(&cur_part->block_bitmap, block_bitmap_idx, 0);
                    dir_inode->i_sectors[12] = 0;
                    printk("alloc block bitmap for sync_dir_entry failed\n");
                    return false;
                }

                /* 每分配一个块就同步一次block_bitmap */
                block_bitmap_idx = block_lba - cur_part->sb->data_start_lba;
                ASSERT(block_bitmap_idx != -1);
                bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);

                all_blocks[12] = block_lba;
                /* 把新分配的第0个间接块地址写入一级间接块表 */
                ide_write(cur_part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1);
            }
            else
            {
    
     // 若是间接块未分配
                all_blocks[block_idx] = block_lba;
                /* 把新分配的第(block_idx-12)个间接块地址写入一级间接块表 */
                ide_write(cur_part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1);
            }

            /* 再将新目录项p_de写入新分配的间接块 */
            memset(io_buf, 0, 512);
            memcpy(io_buf, p_de, dir_entry_size);
            ide_write(cur_part->my_disk, all_blocks[block_idx], io_buf, 1);
            dir_inode->i_size += dir_entry_size;
            return true;
        }

        /* 若第block_idx块已存在,将其读进内存,然后在该块中查找空目录项 */
        ide_read(cur_part->my_disk, all_blocks[block_idx], io_buf, 1);
        /* 在扇区内查找空目录项 */
        uint8_t dir_entry_idx = 0;
        while (dir_entry_idx < dir_entrys_per_sec)
        {
    
    
            if ((dir_e + dir_entry_idx)->f_type == FT_UNKNOWN)
            {
    
     // FT_UNKNOWN为0,无论是初始化或是删除文件后,都会将f_type置为FT_UNKNOWN.
                memcpy(dir_e + dir_entry_idx, p_de, dir_entry_size);
                ide_write(cur_part->my_disk, all_blocks[block_idx], io_buf, 1);

                dir_inode->i_size += dir_entry_size;
                return true;
            }
            dir_entry_idx++;
        }
        block_idx++;
    }
    printk("directory is full!\n");
    return false;
}

Support code:

Modify ( myos/fs/fs.h )

extern struct partition* cur_part;

Since the author in these two lines of code,

(myos/fs/fs.c/sync_dir_entry)

   /* 将12个直接块存入all_blocks */
    while (block_idx < 12) {
    
    
        all_blocks[block_idx] = dir_inode->i_sectors[block_idx];
    	block_idx++;
    }

Only the first 12 direct blocks are read from the directory file, resulting in only the first 12 items of the all_blocks array having non-zero values. Assuming that the entire directory file, the blocks pointed to by the first 12 direct blocks are full of directory entries, then the new directory entries can only be stored in the directory file pointed to by one of the addresses of the blocks pointed to by the first-level indirect block (i_sectors[12]) middle. However, the code logic will inevitably determine that all_blocks[12] == 0, and then enter block_idx == 12. It will re-apply for a block to serve as a first-level indirect block, overwriting the original first-level indirect block. This causes references to all directory entries previously stored in the indirect block to be lost, which is a serious data integrity problem.

Modify as follows:

   /* 将12个直接块存入all_blocks */
    while (block_idx < 12) {
    
    
        all_blocks[block_idx] = dir_inode->i_sectors[block_idx];
    	block_idx++;
    }
   	if (dir_inode->i_sectors[12] != 0) {
    
    	// 若含有一级间接块表
      	ide_read(cur_part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1);
   	}

Function declaration:

Modify ( myos/fs/dir.h )

void open_root_dir(struct partition *part);
struct dir *dir_open(struct partition *part, uint32_t inode_no);
bool search_dir_entry(struct partition *part, struct dir *pdir, const char *name, struct dir_entry *dir_e);
void dir_close(struct dir *dir);
void create_dir_entry(char *filename, uint32_t inode_no, uint8_t file_type, struct dir_entry *p_de);
bool sync_dir_entry(struct dir *parent_dir, struct dir_entry *p_de, void *io_buf);

Next we need to implement the path parsing function. For example, if we want to find a file, we provide the path /home/kanshan/Desktop/test.c, and then the system finds it for us. The core principle of path parsing was mentioned at the beginning of this chapter, which is /home/test.cthe example of how the file system works.

path_parseEach call will parse the top-level path name and name_storestore the parsed path name in the buffer. Since the root directory is the top level of all paths, it does not need to participate in the parsing (for example, the /home/test.cdirectory hierarchy is /, home, test.c, but /it is the top level of all directories. , we can ignore it, so /home/test.cthe directory hierarchy can be considered as home, test.c). For example, when parsing this function /home/kanshan/Desktop/test.c, the first call returns /kanshan/Desktop/test.cand name_storeis stored home. Call return again /Desktop/test.c, name_storestore in kanshan...

Modify ( myos/fs/fs.c )

/* 将最上层路径名称解析出来 */
static char *path_parse(char *pathname, char *name_store)
{
    
    
    if (pathname[0] == '/')
    {
    
     // 根目录不需要单独解析
        /* 路径中出现1个或多个连续的字符'/',将这些'/'跳过,如"///a/b" */
        while (*(++pathname) == '/')
            ;
    }

    /* 开始一般的路径解析 */
    while (*pathname != '/' && *pathname != 0)
    {
    
    
        *name_store++ = *pathname++;
    }

    if (pathname[0] == 0)
    {
    
     // 若路径字符串为空则返回NULL
        return NULL;
    }
    return pathname;
}

path_depth_cntUsed to return the path depth, for example, /home/kanshan/Desktop/test.cthe path depth is 4. The principle is to call in a loop path_parse, see if the return value is empty, and decide whether to continue the call.path_parse

Modify ( myos/fs/fs.c )

/* 返回路径深度,比如/a/b/c,深度为3 */
int32_t path_depth_cnt(char *pathname)
{
    
    
    ASSERT(pathname != NULL);
    char *p = pathname;
    char name[MAX_FILE_NAME_LEN]; // 用于path_parse的参数做路径解析
    uint32_t depth = 0;

    /* 解析路径,从中拆分出各级名称 */
    p = path_parse(p, name);
    while (name[0])
    {
    
    
        depth++;
        memset(name, 0, MAX_FILE_NAME_LEN);
        if (p)
        {
    
     // 如果p不等于NULL,继续分析路径
            p = path_parse(p, name);
        }
    }
    return depth;
}

function declaration

Modify ( myos/fs/fs.h )

int32_t path_depth_cnt(char *pathname);

Next, implement the file retrieval function

search_fileUsed to provide a file path and then return the inode index. The core principle is to continuously loop: 1. Call to path_parseperform address resolution to obtain the name of the uppermost directory except the root directory; 2. Then call to search_dir_entryfind 1 from the search directory (the first time this function is called, the search directory is naturally the root directory). Name; 3, then update the next search directory (that is, the directory entry found by 2); continue to parse, then continue to search, continue to update the search directory...

Modify ( myos/fs/fs.h ) to add a structure for recording search paths

#define MAX_PATH_LEN 512 // 路径最大长度

/* 用来记录查找文件过程中已找到的上级路径,也就是查找文件过程中"走过的地方" */
struct path_search_record
{
    
    
    char searched_path[MAX_PATH_LEN]; // 查找过程中的父路径
    struct dir *parent_dir;           // 文件或目录所在的直接父目录
    enum file_types file_type;        // 找到的是普通文件还是目录,找不到将为未知类型(FT_UNKNOWN)
};

Modify ( myos/fs/fs.c )

/* 搜索文件pathname,若找到则返回其inode号,否则返回-1 */
static int search_file(const char *pathname, struct path_search_record *searched_record)
{
    
    
    /* 如果待查找的是根目录,为避免下面无用的查找,直接返回已知根目录信息 */
    if (!strcmp(pathname, "/") || !strcmp(pathname, "/.") || !strcmp(pathname, "/.."))
    {
    
    
        searched_record->parent_dir = &root_dir;
        searched_record->file_type = FT_DIRECTORY;
        searched_record->searched_path[0] = 0; // 搜索路径置空
        return 0;
    }

    uint32_t path_len = strlen(pathname);
    /* 保证pathname至少是这样的路径/x且小于最大长度 */
    ASSERT(pathname[0] == '/' && path_len > 1 && path_len < MAX_PATH_LEN);
    char *sub_path = (char *)pathname;
    struct dir *parent_dir = &root_dir;
    struct dir_entry dir_e;

    /* 记录路径解析出来的各级名称,如路径"/a/b/c",
     * 数组name每次的值分别是"a","b","c" */
    char name[MAX_FILE_NAME_LEN] = {
    
    0};

    searched_record->parent_dir = parent_dir;
    searched_record->file_type = FT_UNKNOWN;
    uint32_t parent_inode_no = 0; // 父目录的inode号

    sub_path = path_parse(sub_path, name);
    while (name[0])
    {
    
     // 若第一个字符就是结束符,结束循环
        /* 记录查找过的路径,但不能超过searched_path的长度512字节 */
        ASSERT(strlen(searched_record->searched_path) < 512);

        /* 记录已存在的父目录 */
        strcat(searched_record->searched_path, "/");
        strcat(searched_record->searched_path, name);

        /* 在所给的目录中查找文件 */
        if (search_dir_entry(cur_part, parent_dir, name, &dir_e))
        {
    
    
            memset(name, 0, MAX_FILE_NAME_LEN);
            /* 若sub_path不等于NULL,也就是未结束时继续拆分路径 */
            if (sub_path)
            {
    
    
                sub_path = path_parse(sub_path, name);
            }

            if (FT_DIRECTORY == dir_e.f_type)
            {
    
     // 如果被打开的是目录
                parent_inode_no = parent_dir->inode->i_no;
                dir_close(parent_dir);
                parent_dir = dir_open(cur_part, dir_e.i_no); // 更新父目录
                searched_record->parent_dir = parent_dir;
                continue;
            }
            else if (FT_REGULAR == dir_e.f_type)
            {
    
     // 若是普通文件
                searched_record->file_type = FT_REGULAR;
                return dir_e.i_no;
            }
        }
        else
        {
    
     // 若找不到,则返回-1
            /* 找不到目录项时,要留着parent_dir不要关闭,
             * 若是创建新文件的话需要在parent_dir中创建 */
            return -1;
        }
    }

    /* 执行到此,必然是遍历了完整路径并且查找的文件或目录只有同名目录存在 */
    dir_close(searched_record->parent_dir);

    /* 保存被查找目录的直接父目录 */
    searched_record->parent_dir = dir_open(cur_part, parent_inode_no);
    searched_record->file_type = FT_DIRECTORY;
    return dir_e.i_no;
}

Support code:

Modify ( myos/fs/dir.h )

extern struct dir root_dir;

Next, implement the file creation function

file_createUsed to create a file, and if successful, the file descriptor will be returned. Core steps: 1. Create the inode of a new file, which involves inode bitmap modification and inode initialization; 2. File structure creation, because the file structure describes the operating status of the process or thread on the file, and creation naturally belongs to this operating status. This involves changing the global open file structure array; 3. Creating the corresponding directory entry; 4. Synchronizing the directory entry of the new file to the parent directory; synchronizing the parent directory inode and parent node inode, which will involve inode->i_size, inode->i_sectors [ ] (Because the directory entry of the file will exist in the parent directory file, new block requests may be involved). Synchronizing inode will also synchronize the inode array in the disk; create the inode of the file; inode bitmap; 5. Add the inode of the new file to the open linked list; 6. Install the file descriptor in the pcb of the process or thread that creates the file;

We can find that the core of file creation is to deal with the relationship between inode, directory, and file structure.

Modify ( myos/fs/file.c )

#include "string.h"

/* 创建文件,若成功则返回文件描述符,否则返回-1 */
int32_t file_create(struct dir *parent_dir, char *filename, uint8_t flag)
{
    
    
    /* 后续操作的公共缓冲区 */
    void *io_buf = sys_malloc(1024);
    if (io_buf == NULL)
    {
    
    
        printk("in file_creat: sys_malloc for io_buf failed\n");
        return -1;
    }

    uint8_t rollback_step = 0; // 用于操作失败时回滚各资源状态

    /* 为新文件分配inode */
    int32_t inode_no = inode_bitmap_alloc(cur_part);
    if (inode_no == -1)
    {
    
    
        printk("in file_creat: allocate inode failed\n");
        return -1;
    }

    /* 此inode要从堆中申请内存,不可生成局部变量(函数退出时会释放)
     * 因为file_table数组中的文件描述符的inode指针要指向它.*/
    struct inode *new_file_inode = (struct inode *)sys_malloc(sizeof(struct inode));
    if (new_file_inode == NULL)
    {
    
    
        printk("file_create: sys_malloc for inode failded\n");
        rollback_step = 1;
        goto rollback;
    }
    inode_init(inode_no, new_file_inode); // 初始化i结点

    /* 返回的是file_table数组的下标 */
    int fd_idx = get_free_slot_in_global();
    if (fd_idx == -1)
    {
    
    
        printk("exceed max open files\n");
        rollback_step = 2;
        goto rollback;
    }

    file_table[fd_idx].fd_inode = new_file_inode;
    file_table[fd_idx].fd_pos = 0;
    file_table[fd_idx].fd_flag = flag;
    file_table[fd_idx].fd_inode->write_deny = false;

    struct dir_entry new_dir_entry;
    memset(&new_dir_entry, 0, sizeof(struct dir_entry));

    create_dir_entry(filename, inode_no, FT_REGULAR, &new_dir_entry); // create_dir_entry只是内存操作不出意外,不会返回失败

    /* 同步内存数据到硬盘 */
    /* a 在目录parent_dir下安装目录项new_dir_entry, 写入硬盘后返回true,否则false */
    if (!sync_dir_entry(parent_dir, &new_dir_entry, io_buf))
    {
    
    
        printk("sync dir_entry to disk failed\n");
        rollback_step = 3;
        goto rollback;
    }

    memset(io_buf, 0, 1024);
    /* b 将父目录i结点的内容同步到硬盘 */
    inode_sync(cur_part, parent_dir->inode, io_buf);

    memset(io_buf, 0, 1024);
    /* c 将新创建文件的i结点内容同步到硬盘 */
    inode_sync(cur_part, new_file_inode, io_buf);

    /* d 将inode_bitmap位图同步到硬盘 */
    bitmap_sync(cur_part, inode_no, INODE_BITMAP);

    /* e 将创建的文件i结点添加到open_inodes链表 */
    list_push(&cur_part->open_inodes, &new_file_inode->inode_tag);
    new_file_inode->i_open_cnts = 1;

    sys_free(io_buf);
    return pcb_fd_install(fd_idx);

/*创建文件需要创建相关的多个资源,若某步失败则会执行到下面的回滚步骤 */
rollback:
    switch (rollback_step)
    {
    
    
    case 3:
        /* 失败时,将file_table中的相应位清空 */
        memset(&file_table[fd_idx], 0, sizeof(struct file));
    case 2:
        sys_free(new_file_inode);
    case 1:
        /* 如果新文件的i结点创建失败,之前位图中分配的inode_no也要恢复 */
        bitmap_set(&cur_part->inode_bitmap, inode_no, 0);
        break;
    }
    sys_free(io_buf);
    return -1;
}

code block

   /* 为新文件分配inode */
   int32_t inode_no = inode_bitmap_alloc(cur_part); 
   if (inode_no == -1) {
    
    
      printk("in file_creat: allocate inode failed\n");
      return -1;
   }

changed to

   /* 为新文件分配inode */
   int32_t inode_no = inode_bitmap_alloc(cur_part); 
   if (inode_no == -1) {
    
    
      printk("in file_creat: allocate inode failed\n");
      goto rollback;
   }

Function declaration:

Modify ( myos/fs/file.h )

#include "dir.h"

int32_t file_create(struct dir *parent_dir, char *filename, uint8_t flag);

sys_openUsed to open or create a file based on the passed path and return the file descriptor. Since we are writing the first version now, we only implement the creation function. The core principle is: first call search_fileto find whether the file exists in the specified path, if not, call directly file_createto create the file. From searching for existence to final creation, we need to exclude the following situations: 1. It is found, but it is a directory; 2. An intermediate path does not exist during the search process. For example, I want to create, but after calling, I /home/kanshan/test.cfind search_filethat , this /kanshandirectory does not exist; 3. It was not found on the last path, but no flag indicating the creation of the file was passed in; 4. The file to be created already exists;

Modify ( myos/fs/fs.h )

/* 打开文件的选项 */
enum oflags
{
    
    
    O_RDONLY,   // 只读
    O_WRONLY,   // 只写
    O_RDWR,     // 读写
    O_CREAT = 4 // 创建
};

Modify ( myos/fs/fs.c )

#include "file.h"

/* 打开或创建文件成功后,返回文件描述符,否则返回-1 */
int32_t sys_open(const char *pathname, uint8_t flags)
{
    
    
    /* 对目录要用dir_open,这里只有open文件 */
    if (pathname[strlen(pathname) - 1] == '/')
    {
    
    
        printk("can`t open a directory %s\n", pathname);
        return -1;
    }
    ASSERT(flags <= 7);
    int32_t fd = -1; // 默认为找不到

    struct path_search_record searched_record;
    memset(&searched_record, 0, sizeof(struct path_search_record));

    /* 记录目录深度.帮助判断中间某个目录不存在的情况 */
    uint32_t pathname_depth = path_depth_cnt((char *)pathname);

    /* 先检查文件是否存在 */
    int inode_no = search_file(pathname, &searched_record);
    bool found = inode_no != -1 ? true : false;

    if (searched_record.file_type == FT_DIRECTORY)
    {
    
    
        printk("can`t open a direcotry with open(), use opendir() to instead\n");
        dir_close(searched_record.parent_dir);
        return -1;
    }

    uint32_t path_searched_depth = path_depth_cnt(searched_record.searched_path);

    /* 先判断是否把pathname的各层目录都访问到了,即是否在某个中间目录就失败了 */
    if (pathname_depth != path_searched_depth)
    {
    
     // 说明并没有访问到全部的路径,某个中间目录是不存在的
        printk("cannot access %s: Not a directory, subpath %s is`t exist\n",
               pathname, searched_record.searched_path);
        dir_close(searched_record.parent_dir);
        return -1;
    }

    /* 若是在最后一个路径上没找到,并且并不是要创建文件,直接返回-1 */
    if (!found && !(flags & O_CREAT))
    {
    
    
        printk("in path %s, file %s is`t exist\n",
               searched_record.searched_path,
               (strrchr(searched_record.searched_path, '/') + 1));
        dir_close(searched_record.parent_dir);
        return -1;
    }
    else if (found && flags & O_CREAT)
    {
    
     // 若要创建的文件已存在
        printk("%s has already exist!\n", pathname);
        dir_close(searched_record.parent_dir);
        return -1;
    }

    switch (flags & O_CREAT)
    {
    
    
    case O_CREAT:
        printk("creating file\n");
        fd = file_create(searched_record.parent_dir, (strrchr(pathname, '/') + 1), flags);
        dir_close(searched_record.parent_dir);

        /* 此fd是指任务pcb->fd_table数组中的元素下标,
         * 并不是指全局file_table中的下标 */
    }
    return fd;
}

Function declaration:

Modify ( myos/fs/fs.h )

int32_t sys_open(const char *pathname, uint8_t flags);

filesys_init, list_traversalcalled when polling a partition mount_partitionto mount the partition. Then call to open_root_dirjoin the root directory of the current opened partition and initialize the global open file structure array.

Modify ( myos/fs/fs.c/filesys_init )

    sys_free(sb_buf);
    /* 确定默认操作的分区 */
    char default_part[8] = "sdb1";
    /* 挂载分区 */
    list_traversal(&partition_list, mount_partition, (int)default_part);
}

for

    sys_free(sb_buf);
    /* 确定默认操作的分区 */
    char default_part[8] = "sdb1";
    /* 挂载分区 */
    list_traversal(&partition_list, mount_partition, (int)default_part);
    /* 将当前分区的根目录打开 */
    open_root_dir(cur_part);

    /* 初始化文件表 */
    uint32_t fd_idx = 0;
    while (fd_idx < MAX_FILE_OPEN)
    {
    
    
        file_table[fd_idx++].fd_inode = NULL;
    }
}

Support code, modification ( myos/fs/file.h )

extern struct file file_table[MAX_FILE_OPEN];

Test code, modify ( myos/kernel/main.c )

#include "print.h"
#include "init.h"
#include "fs.h"


int main(void) {
    
    
   put_str("I am kernel\n");
   init_all();
   sys_open("/file1", O_CREAT);
   while(1);
   return 0;
}

In order to facilitate debugging, we modify ( myos/fs/fs.c/mount_partition ). After debugging, we need to delete the added code.

        printk("mount %s done!\n", part->name);

        /* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关。
            只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历.*/
        sys_free(sb_buf);

for

        printk("mount %s done!\n", part->name);
        printk("sb1 data_start_lba: %x\n", cur_part->sb->data_start_lba);

        /* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关。
            只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历.*/
        sys_free(sb_buf);

When the virtual machine is started, the starting sector of the data area of ​​sdb 1 will be displayed (I am A6B, if you have been following me, it should be this). Since the first block of our root directory file is placed In the first sector of the data area, so after we get this data, add it to *512, and then enter the following command to view the first 512 bytes of data in the hd80M.img data area, and we can get the data in the book Effect

xxd -s 1365504 -l 512 hd80M.img

xxdIt is a command line tool, which is mainly used to display the contents of a file in hexadecimal format. The -sparameter is to specify the data byte offset to be displayed, and -lthe parameter is the number of displayed bytes.

Section d:

Opening and closing files

file_open: Used to open a file. The essence is to let the file structure point to an inode in memory. Principle: Pass in the inode array index of the file that needs to be opened, call to get_free_slot_in_globalfind an empty position in the global open file structure array, call to inode_opentransfer the inode into the memory, then let the file structure point to this inode, and finally call to pcb_fd_installinstall the file descriptor. For the sake of synchronization, the file opened for writing should not be the file being written. It is necessary to determine the writing flag in the corresponding inode of the file.

Modify ( myos/fs/file.c )

#include "interrupt.h"

/* 打开编号为inode_no的inode对应的文件,若成功则返回文件描述符,否则返回-1 */
int32_t file_open(uint32_t inode_no, uint8_t flag)
{
    
    
    int fd_idx = get_free_slot_in_global();
    if (fd_idx == -1)
    {
    
    
        printk("exceed max open files\n");
        return -1;
    }
    file_table[fd_idx].fd_inode = inode_open(cur_part, inode_no);
    file_table[fd_idx].fd_pos = 0; // 每次打开文件,要将fd_pos还原为0,即让文件内的指针指向开头
    file_table[fd_idx].fd_flag = flag;
    bool *write_deny = &file_table[fd_idx].fd_inode->write_deny;

    if (flag & O_WRONLY || flag & O_RDWR)
    {
    
     // 只要是关于写文件,判断是否有其它进程正写此文件
        // 若是读文件,不考虑write_deny
        /* 以下进入临界区前先关中断 */
        enum intr_status old_status = intr_disable();
        if (!(*write_deny))
        {
    
                                    // 若当前没有其它进程写该文件,将其占用.
            *write_deny = true;          // 置为true,避免多个进程同时写此文件
            intr_set_status(old_status); // 恢复中断
        }
        else
        {
    
     // 直接失败返回
            intr_set_status(old_status);
            printk("file can`t be write now, try again later\n");
            return -1;
        }
    } // 若是读文件或创建文件,不用理会write_deny,保持默认
    return pcb_fd_install(fd_idx);
}

The file is opened in a write-related manner, but when other threads or processes are writing the file, the author's code does not release the file structure! So, modify

        else
        {
    
     // 直接失败返回
            intr_set_status(old_status);
            printk("file can`t be write now, try again later\n");
            return -1;
        }

for

        else
        {
    
     // 直接失败返回
            intr_set_status(old_status);
            file_table[fd_idx].fd_inode = NULL;
            printk("file can`t be write now, try again later\n");
            return -1;
        }

Before we only implemented sys_openthe function of creating files, now we will file_openencapsulate it so that sys_openwe have the function of opening files.

Modify ( myos/fs/fs.c/sys_open )

    switch (flags & O_CREAT)
    {
    
    
    case O_CREAT:
        printk("creating file\n");
        fd = file_create(searched_record.parent_dir, (strrchr(pathname, '/') + 1), flags);
        dir_close(searched_record.parent_dir);

        /* 此fd是指任务pcb->fd_table数组中的元素下标,
         * 并不是指全局file_table中的下标 */
    }
    return fd;

for

    switch (flags & O_CREAT)
    {
    
    
    case O_CREAT:
        printk("creating file\n");
        fd = file_create(searched_record.parent_dir, (strrchr(pathname, '/') + 1), flags);
        dir_close(searched_record.parent_dir);
        break;
    default:
        /* 其余情况均为打开已存在文件:
         * O_RDONLY,O_WRONLY,O_RDWR */
        fd = file_open(inode_no, flags);
    }

    /* 此fd是指任务pcb->fd_table数组中的元素下标,
     * 并不是指全局file_table中的下标 */
    return fd;

file_closeClose the file using the passed file structure pointer. Steps: Turn off the file writing flag, call to inode_closeremove the inode in the memory, and disassociate the file structure from the inode.

Modify ( myos/fs/file.c )

/* 关闭文件 */
int32_t file_close(struct file *file)
{
    
    
    if (file == NULL)
    {
    
    
        return -1;
    }
    file->fd_inode->write_deny = false;
    inode_close(file->fd_inode);
    file->fd_inode = NULL; // 使文件结构可用
    return 0;
}

Here we find that opening and closing files is dealing with the relationship between file structure and inode.

Function declaration, modification ( myos/fs/file.h )

int32_t file_open(uint32_t inode_no, uint8_t flag);
int32_t file_close(struct file *file);

fd_local2global, used to convert the file descriptor into a global open file structure array subscript. The principle is to find the element corresponding to the file descriptor in the fd_table array in the pcb of the currently running process or thread. What is stored here is the global open file. Structure array subscript.

Modify ( myos/fs/fs.c )

/* 将文件描述符转化为文件表的下标 */
static uint32_t fd_local2global(uint32_t local_fd)
{
    
    
    struct task_struct *cur = running_thread();
    int32_t global_fd = cur->fd_table[local_fd];
    ASSERT(global_fd >= 0 && global_fd < MAX_FILE_OPEN);
    return (uint32_t)global_fd;
}

sys_closeUse the incoming file descriptor to close the file. Principle: call fd_close2globalto convert the file descriptor into a global open file structure array subscript, then call to file_closeclose the file corresponding to this file structure, and finally clear the file descriptor bit in the pcb

Modify ( myos/fs/fs.c )

/* 关闭文件描述符fd指向的文件,成功返回0,否则返回-1 */
int32_t sys_close(int32_t fd)
{
    
    
    int32_t ret = -1; // 返回值默认为-1,即失败
    if (fd > 2)
    {
    
    
        uint32_t _fd = fd_local2global(fd);
        ret = file_close(&file_table[_fd]);
        running_thread()->fd_table[fd] = -1; // 使该文件描述符位可用
    }
    return ret;
}

Function declaration, modification ( myos/fs/fs.h )

int32_t sys_close(int32_t fd);

Test code, modify ( myos/kernel/main.c )

#include "print.h"
#include "init.h"
#include "fs.h"
#include "stdio.h"


int main(void) {
    
    
   put_str("I am kernel\n");
   init_all();
   uint32_t fd = sys_open("/file1", O_RDONLY);
   printf("fd:%d\n", fd);
   sys_close(fd);
   printf("%d closed now\n", fd);
   while(1);
   return 0;
}

Section e:

file_write, used to write count bytes in buf to the file. Core principle: The file structure pointer passed into the function struct filehas a pointer to the inode of the operating file. Through i_size and i_sectors[] in this inode, we can successfully know the file size and storage location information. First, the last block of data in the file is read out and combined with the data to be written in the buffer to form a complete block, and then written to the disk. The remaining data can be written to disk in blocks.

Modify ( myos/fs/file.c )

/* 把buf中的count个字节写入file,成功则返回写入的字节数,失败则返回-1 */
int32_t file_write(struct file *file, const void *buf, uint32_t count)
{
    
    
    if ((file->fd_inode->i_size + count) > (BLOCK_SIZE * 140))
    {
    
     // 文件目前最大只支持512*140=71680字节
        printk("exceed max file_size 71680 bytes, write file failed\n");
        return -1;
    }
    uint8_t *io_buf = sys_malloc(BLOCK_SIZE);
    if (io_buf == NULL)
    {
    
    
        printk("file_write: sys_malloc for io_buf failed\n");
        return -1;
    }
    uint32_t *all_blocks = (uint32_t *)sys_malloc(BLOCK_SIZE + 48); // 用来记录文件所有的块地址
    if (all_blocks == NULL)
    {
    
    
        printk("file_write: sys_malloc for all_blocks failed\n");
        return -1;
    }

    const uint8_t *src = buf;      // 用src指向buf中待写入的数据
    uint32_t bytes_written = 0;    // 用来记录已写入数据大小
    uint32_t size_left = count;    // 用来记录未写入数据大小
    int32_t block_lba = -1;        // 块地址
    uint32_t block_bitmap_idx = 0; // 用来记录block对应于block_bitmap中的索引,做为参数传给bitmap_sync
    uint32_t sec_idx;              // 用来索引扇区
    uint32_t sec_lba;              // 扇区地址
    uint32_t sec_off_bytes;        // 扇区内字节偏移量
    uint32_t sec_left_bytes;       // 扇区内剩余字节量
    uint32_t chunk_size;           // 每次写入硬盘的数据块大小
    int32_t indirect_block_table;  // 用来获取一级间接表地址
    uint32_t block_idx;            // 块索引

    /* 判断文件是否是第一次写,如果是,先为其分配一个块 */
    if (file->fd_inode->i_sectors[0] == 0)
    {
    
    
        block_lba = block_bitmap_alloc(cur_part);
        if (block_lba == -1)
        {
    
    
            printk("file_write: block_bitmap_alloc failed\n");
            return -1;
        }
        file->fd_inode->i_sectors[0] = block_lba;

        /* 每分配一个块就将位图同步到硬盘 */
        block_bitmap_idx = block_lba - cur_part->sb->data_start_lba;
        ASSERT(block_bitmap_idx != 0);
        bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
    }

    /* 写入count个字节前,该文件已经占用的块数 */
    uint32_t file_has_used_blocks = file->fd_inode->i_size / BLOCK_SIZE + 1;

    /* 存储count字节后该文件将占用的块数 */
    uint32_t file_will_use_blocks = (file->fd_inode->i_size + count) / BLOCK_SIZE + 1;
    ASSERT(file_will_use_blocks <= 140);

    /* 通过此增量判断是否需要分配扇区,如增量为0,表示原扇区够用 */
    uint32_t add_blocks = file_will_use_blocks - file_has_used_blocks;

    /* 开始将文件所有块地址收集到all_blocks,(系统中块大小等于扇区大小)
     * 后面都统一在all_blocks中获取写入扇区地址 */
    if (add_blocks == 0)
    {
    
    
        /* 在同一扇区内写入数据,不涉及到分配新扇区 */
        if (file_has_used_blocks <= 12)
        {
    
                                             // 文件数据量将在12块之内
            block_idx = file_has_used_blocks - 1; // 指向最后一个已有数据的扇区
            all_blocks[block_idx] = file->fd_inode->i_sectors[block_idx];
        }
        else
        {
    
    
            /* 未写入新数据之前已经占用了间接块,需要将间接块地址读进来 */
            ASSERT(file->fd_inode->i_sectors[12] != 0);
            indirect_block_table = file->fd_inode->i_sectors[12];
            ide_read(cur_part->my_disk, indirect_block_table, all_blocks + 12, 1);
        }
    }
    else
    {
    
    
        /* 若有增量,便涉及到分配新扇区及是否分配一级间接块表,下面要分三种情况处理 */
        /* 第一种情况:12个直接块够用*/
        if (file_will_use_blocks <= 12)
        {
    
    
            /* 先将有剩余空间的可继续用的扇区地址写入all_blocks */
            block_idx = file_has_used_blocks - 1;
            ASSERT(file->fd_inode->i_sectors[block_idx] != 0);
            all_blocks[block_idx] = file->fd_inode->i_sectors[block_idx];

            /* 再将未来要用的扇区分配好后写入all_blocks */
            block_idx = file_has_used_blocks; // 指向第一个要分配的新扇区
            while (block_idx < file_will_use_blocks)
            {
    
    
                block_lba = block_bitmap_alloc(cur_part);
                if (block_lba == -1)
                {
    
    
                    printk("file_write: block_bitmap_alloc for situation 1 failed\n");
                    return -1;
                }

                /* 写文件时,不应该存在块未使用但已经分配扇区的情况,当文件删除时,就会把块地址清0 */
                ASSERT(file->fd_inode->i_sectors[block_idx] == 0); // 确保尚未分配扇区地址
                file->fd_inode->i_sectors[block_idx] = all_blocks[block_idx] = block_lba;

                /* 每分配一个块就将位图同步到硬盘 */
                block_bitmap_idx = block_lba - cur_part->sb->data_start_lba;
                bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);

                block_idx++; // 下一个分配的新扇区
            }
        }
        else if (file_has_used_blocks <= 12 && file_will_use_blocks > 12)
        {
    
    
            /* 第二种情况: 旧数据在12个直接块内,新数据将使用间接块*/

            /* 先将有剩余空间的可继续用的扇区地址收集到all_blocks */
            block_idx = file_has_used_blocks - 1; // 指向旧数据所在的最后一个扇区
            all_blocks[block_idx] = file->fd_inode->i_sectors[block_idx];

            /* 创建一级间接块表 */
            block_lba = block_bitmap_alloc(cur_part);
            if (block_lba == -1)
            {
    
    
                printk("file_write: block_bitmap_alloc for situation 2 failed\n");
                return -1;
            }

            ASSERT(file->fd_inode->i_sectors[12] == 0); // 确保一级间接块表未分配
            /* 分配一级间接块索引表 */
            indirect_block_table = file->fd_inode->i_sectors[12] = block_lba;

            block_idx = file_has_used_blocks; // 第一个未使用的块,即本文件最后一个已经使用的直接块的下一块
            while (block_idx < file_will_use_blocks)
            {
    
    
                block_lba = block_bitmap_alloc(cur_part);
                if (block_lba == -1)
                {
    
    
                    printk("file_write: block_bitmap_alloc for situation 2 failed\n");
                    return -1;
                }

                if (block_idx < 12)
                {
    
                                                          // 新创建的0~11块直接存入all_blocks数组
                    ASSERT(file->fd_inode->i_sectors[block_idx] == 0); // 确保尚未分配扇区地址
                    file->fd_inode->i_sectors[block_idx] = all_blocks[block_idx] = block_lba;
                }
                else
                {
    
     // 间接块只写入到all_block数组中,待全部分配完成后一次性同步到硬盘
                    all_blocks[block_idx] = block_lba;
                }

                /* 每分配一个块就将位图同步到硬盘 */
                block_bitmap_idx = block_lba - cur_part->sb->data_start_lba;
                bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);

                block_idx++; // 下一个新扇区
            }
            ide_write(cur_part->my_disk, indirect_block_table, all_blocks + 12, 1); // 同步一级间接块表到硬盘
        }
        else if (file_has_used_blocks > 12)
        {
    
    
            /* 第三种情况:新数据占据间接块*/
            ASSERT(file->fd_inode->i_sectors[12] != 0);           // 已经具备了一级间接块表
            indirect_block_table = file->fd_inode->i_sectors[12]; // 获取一级间接表地址

            /* 已使用的间接块也将被读入all_blocks,无须单独收录 */
            ide_read(cur_part->my_disk, indirect_block_table, all_blocks + 12, 1); // 获取所有间接块地址

            block_idx = file_has_used_blocks; // 第一个未使用的间接块,即已经使用的间接块的下一块
            while (block_idx < file_will_use_blocks)
            {
    
    
                block_lba = block_bitmap_alloc(cur_part);
                if (block_lba == -1)
                {
    
    
                    printk("file_write: block_bitmap_alloc for situation 3 failed\n");
                    return -1;
                }
                all_blocks[block_idx++] = block_lba;

                /* 每分配一个块就将位图同步到硬盘 */
                block_bitmap_idx = block_lba - cur_part->sb->data_start_lba;
                bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
            }
            ide_write(cur_part->my_disk, indirect_block_table, all_blocks + 12, 1); // 同步一级间接块表到硬盘
        }
    }

    bool first_write_block = true; // 含有剩余空间的扇区标识
    /* 块地址已经收集到all_blocks中,下面开始写数据 */
    file->fd_pos = file->fd_inode->i_size - 1; // 置fd_pos为文件大小-1,下面在写数据时随时更新
    while (bytes_written < count)
    {
    
     // 直到写完所有数据
        memset(io_buf, 0, BLOCK_SIZE);
        sec_idx = file->fd_inode->i_size / BLOCK_SIZE;
        sec_lba = all_blocks[sec_idx];
        sec_off_bytes = file->fd_inode->i_size % BLOCK_SIZE;
        sec_left_bytes = BLOCK_SIZE - sec_off_bytes;

        /* 判断此次写入硬盘的数据大小 */
        chunk_size = size_left < sec_left_bytes ? size_left : sec_left_bytes;
        if (first_write_block)
        {
    
    
            ide_read(cur_part->my_disk, sec_lba, io_buf, 1);
            first_write_block = false;
        }
        memcpy(io_buf + sec_off_bytes, src, chunk_size);
        ide_write(cur_part->my_disk, sec_lba, io_buf, 1);
        printk("file write at lba 0x%x\n", sec_lba); // 调试,完成后去掉

        src += chunk_size;                    // 将指针推移到下个新数据
        file->fd_inode->i_size += chunk_size; // 更新文件大小
        file->fd_pos += chunk_size;
        bytes_written += chunk_size;
        size_left -= chunk_size;
    }
    inode_sync(cur_part, file->fd_inode, io_buf);
    sys_free(all_blocks);
    sys_free(io_buf);
    return bytes_written;
}

Function declaration, modification ( myos/fs/file.h )

int32_t file_write(struct file *file, const void *buf, uint32_t count);

When we implemented printf in Chapter 12, we implemented sys_write. Since there was no file system at the time, it just called console_put_str to print the string. Now we improve sys_write

sys_writeUsed to write consecutive count bytes in buf to the file descriptor fd. If the incoming fd represents standard output, just call console_put_str to print. If not, file_writejust call it directly

Modify ( myos/fs/fs.c )

#include "console.h"

/* 将buf中连续count个字节写入文件描述符fd,成功则返回写入的字节数,失败返回-1 */
int32_t sys_write(int32_t fd, const void *buf, uint32_t count)
{
    
    
    if (fd < 0)  
    {
    
    
        printk("sys_write: fd error\n");
        return -1;
    }
    if (fd == stdout_no)
    {
    
    
        char tmp_buf[1024] = {
    
    0};
        memcpy(tmp_buf, buf, count);
        console_put_str(tmp_buf);
        return count;
    }
    uint32_t _fd = fd_local2global(fd);
    struct file *wr_file = &file_table[_fd];
    if (wr_file->fd_flag & O_WRONLY || wr_file->fd_flag & O_RDWR)
    {
    
    
        uint32_t bytes_written = file_write(wr_file, buf, count);
        return bytes_written;
    }
    else
    {
    
    
        console_put_str("sys_write: not allowed to write file without flag O_RDWR or O_WRONLY\n");
        return -1;
    }
}

Support code, modification ( myos/fs/file.h )

/* 标准输入输出描述符 */
enum std_fd {
    
    
   stdin_no,   // 0 标准输入
   stdout_no,  // 1 标准输出
   stderr_no   // 2 标准错误
};

Function declaration, modification ( myos/fs/fs.h )

int32_t sys_write(int32_t fd, const void *buf, uint32_t count)

Modify ( myos/userprog/syscall-init.c ) and delete the original sys_writedefinition

/* 打印字符串str(未实现文件系统前的版本) */
uint32_t sys_write(char* str) {
    
    
   	console_put_str(str);
   	return strlen(str);
}

Then ( myos/userprog/syscall-init.c ) add fs.hthe header file

#include "fs.h"

Finally delete the statement ( myos/userprog/syscall-init.c )sys_write

uint32_t sys_write(char* str);

Then modify the user mode entrywrite

Modify ( myos/fs/syscall.c )

/* 打印字符串str */
uint32_t write(char* str) {
    
    
   return _syscall1(SYS_WRITE, str);
}

for

/* 把buf中count个字符写入文件描述符fd */
uint32_t write(int32_t fd, const void *buf, uint32_t count)
{
    
    
    return _syscall3(SYS_WRITE, fd, buf, count);
}

Modify function declaration

Modify ( myos/lib/user/syscall.h )

uint32_t write(char* str);

for

uint32_t write(int32_t fd, const void *buf, uint32_t count)

The final modification printfis to write the replaced string to the standard output.

Modify ( myos/lib/stdio.c )

/* 格式化输出字符串format */
uint32_t printf(const char* format, ...) {
    
    
   va_list args;
   va_start(args, format);	       					// 使args指向format
   char buf[1024] = {
    
    0};	       					// 用于存储拼接后的字符串
   vsprintf(buf, format, args);
   va_end(args);
   return write(buf); 
}

for

/* 格式化输出字符串format */
uint32_t printf(const char *format, ...)
{
    
    
    va_list args;
    va_start(args, format); // 使args指向format
    char buf[1024] = {
    
    0};   // 用于存储拼接后的字符串
    vsprintf(buf, format, args);
    va_end(args);
    return write(1, buf, strlen(buf));
}

Test code ( myos/kernel/main.c )

#include "print.h"
#include "init.h"
#include "fs.h"
#include "stdio.h"


int main(void) {
    
    
   put_str("I am kernel\n");
   init_all();
   uint32_t fd = sys_open("/file1", O_RDWR);
   printf("fd:%d\n", fd);
   sys_write(fd, "hello,world\n", 12);
   sys_close(fd);
   printf("%d closed now\n", fd);
   while(1);
   return 0;
}

(Note: The author ran this part of the code twice, which means it was written twice in total hello,world\n)

xxd -s 屏幕显示写入地址 *512 -l 512 hd80M.imgView files on disk

Section f:

file_readRead count bytes from file file to buf. Core principle: struct fileThere is a fd_posfile content position in the file structure that represents the current operation. In fact, it is the starting position of the content to be read in the file. For example, a 1KB text file, fd_pos = 500, then it means reading the current The content starting with this character in the file at offset 500 bytes. By passing struct filein fd_posand to the function count, we can determine the byte offset of what we want to read relative to the entire file. Then struct filethere is a pointer to the inode of the operating file. Through i_size and i_sectors[] in this inode, we can successfully know the file storage location information. Therefore, it is natural to know the location of the content to be read on the disk. Since our operation unit is a block, for the starting position, we have to discard the useless data before the block, and for the end position, we have to discard the useless data after the block.

Modify ( myos/fs/file.c )

/* 从文件file中读取count个字节写入buf, 返回读出的字节数,若到文件尾则返回-1 */
int32_t file_read(struct file *file, void *buf, uint32_t count)
{
    
    
    uint8_t *buf_dst = (uint8_t *)buf;
    uint32_t size = count, size_left = size;

    /* 若要读取的字节数超过了文件可读的剩余量, 就用剩余量做为待读取的字节数 */
    if ((file->fd_pos + count) > file->fd_inode->i_size)
    {
    
    
        size = file->fd_inode->i_size - file->fd_pos;
        size_left = size;
        if (size == 0)
        {
    
     // 若到文件尾则返回-1
            return -1;
        }
    }

    uint8_t *io_buf = sys_malloc(BLOCK_SIZE);
    if (io_buf == NULL)
    {
    
    
        printk("file_read: sys_malloc for io_buf failed\n");
    }
    uint32_t *all_blocks = (uint32_t *)sys_malloc(BLOCK_SIZE + 48); // 用来记录文件所有的块地址
    if (all_blocks == NULL)
    {
    
    
        printk("file_read: sys_malloc for all_blocks failed\n");
        return -1;
    }

    uint32_t block_read_start_idx = file->fd_pos / BLOCK_SIZE;        // 数据所在块的起始地址
    uint32_t block_read_end_idx = (file->fd_pos + size) / BLOCK_SIZE; // 数据所在块的终止地址
    uint32_t read_blocks = block_read_start_idx - block_read_end_idx; // 如增量为0,表示数据在同一扇区
    ASSERT(block_read_start_idx < 139 && block_read_end_idx < 139);

    int32_t indirect_block_table; // 用来获取一级间接表地址
    uint32_t block_idx;           // 获取待读的块地址

    /* 以下开始构建all_blocks块地址数组,专门存储用到的块地址(本程序中块大小同扇区大小) */
    if (read_blocks == 0)
    {
    
     // 在同一扇区内读数据,不涉及到跨扇区读取
        ASSERT(block_read_end_idx == block_read_start_idx);
        if (block_read_end_idx < 12)
        {
    
     // 待读的数据在12个直接块之内
            block_idx = block_read_end_idx;
            all_blocks[block_idx] = file->fd_inode->i_sectors[block_idx];
        }
        else
        {
    
     // 若用到了一级间接块表,需要将表中间接块读进来
            indirect_block_table = file->fd_inode->i_sectors[12];
            ide_read(cur_part->my_disk, indirect_block_table, all_blocks + 12, 1);
        }
    }
    else
    {
    
       // 若要读多个块
        /* 第一种情况: 起始块和终止块属于直接块*/
        if (block_read_end_idx < 12)
        {
    
     // 数据结束所在的块属于直接块
            block_idx = block_read_start_idx;
            while (block_idx <= block_read_end_idx)
            {
    
    
                all_blocks[block_idx] = file->fd_inode->i_sectors[block_idx];
                block_idx++;
            }
        }
        else if (block_read_start_idx < 12 && block_read_end_idx >= 12)
        {
    
    
            /* 第二种情况: 待读入的数据跨越直接块和间接块两类*/
            /* 先将直接块地址写入all_blocks */
            block_idx = block_read_start_idx;
            while (block_idx < 12)
            {
    
    
                all_blocks[block_idx] = file->fd_inode->i_sectors[block_idx];
                block_idx++;
            }
            ASSERT(file->fd_inode->i_sectors[12] != 0); // 确保已经分配了一级间接块表

            /* 再将间接块地址写入all_blocks */
            indirect_block_table = file->fd_inode->i_sectors[12];
            ide_read(cur_part->my_disk, indirect_block_table, all_blocks + 12, 1); // 将一级间接块表读进来写入到第13个块的位置之后
        }
        else
        {
    
    
            /* 第三种情况: 数据在间接块中*/
            ASSERT(file->fd_inode->i_sectors[12] != 0);                            // 确保已经分配了一级间接块表
            indirect_block_table = file->fd_inode->i_sectors[12];                  // 获取一级间接表地址
            ide_read(cur_part->my_disk, indirect_block_table, all_blocks + 12, 1); // 将一级间接块表读进来写入到第13个块的位置之后
        }
    }

    /* 用到的块地址已经收集到all_blocks中,下面开始读数据 */
    uint32_t sec_idx, sec_lba, sec_off_bytes, sec_left_bytes, chunk_size;
    uint32_t bytes_read = 0;
    while (bytes_read < size)
    {
    
     // 直到读完为止
        sec_idx = file->fd_pos / BLOCK_SIZE;
        sec_lba = all_blocks[sec_idx];
        sec_off_bytes = file->fd_pos % BLOCK_SIZE;
        sec_left_bytes = BLOCK_SIZE - sec_off_bytes;
        chunk_size = size_left < sec_left_bytes ? size_left : sec_left_bytes; // 待读入的数据大小

        memset(io_buf, 0, BLOCK_SIZE);
        ide_read(cur_part->my_disk, sec_lba, io_buf, 1);
        memcpy(buf_dst, io_buf + sec_off_bytes, chunk_size);

        buf_dst += chunk_size;
        file->fd_pos += chunk_size;
        bytes_read += chunk_size;
        size_left -= chunk_size;
    }
    sys_free(all_blocks);
    sys_free(io_buf);
    return bytes_read;
}

Function declaration, modification ( myos/fs/file.h )

int32_t file_read(struct file *file, void *buf, uint32_t count);

sys_readAccording to the incoming file descriptor, call fd_local2globalto convert the file descriptor into the specified global open file structure array index, and then call to file_readread out count bytes and put them bufin

Modify ( myos/fs/fs.c )

/* 从文件描述符fd指向的文件中读取count个字节到buf,若成功则返回读出的字节数,到文件尾则返回-1 */
int32_t sys_read(int32_t fd, void *buf, uint32_t count)
{
    
    
    if (fd < 0)
    {
    
    
        printk("sys_read: fd error\n");
        return -1;
    }
    ASSERT(buf != NULL);
    uint32_t _fd = fd_local2global(fd);
    return file_read(&file_table[_fd], buf, count);
}

Function declaration, modification ( myos/fs/fs.h )

int32_t sys_read(int32_t fd, void *buf, uint32_t count);

Test code, ( myos/kernel/main.c )

#include "print.h"
#include "init.h"
#include "fs.h"
#include "stdio.h"


int main(void) {
    
    
   put_str("I am kernel\n");
   init_all();
   uint32_t fd = sys_open("/file1", O_RDWR);
   printf("open /file1, fd:%d\n", fd);
   char buf[64] = {
    
    0};
   int read_bytes = sys_read(fd, buf, 18);
   printf("1_ read %d bytes:\n%s\n", read_bytes, buf);

   memset(buf, 0, 64);
   read_bytes = sys_read(fd, buf, 6);
   printf("2_ read %d bytes:\n%s", read_bytes, buf);

   memset(buf, 0, 64);
   read_bytes = sys_read(fd, buf, 6);
   printf("3_ read %d bytes:\n%s", read_bytes, buf);

   printf("________  close file1 and reopen  ________\n");
   sys_close(fd);
   fd = sys_open("/file1", O_RDWR);
   memset(buf, 0, 64);
   read_bytes = sys_read(fd, buf, 24);
   printf("4_ read %d bytes:\n%s", read_bytes, buf);

   sys_close(fd);
   while(1);
   return 0;
}

Section g:

sys_lseekUsed to reset the global open file structure corresponding to the incoming file descriptor based on the incoming reference object and offset fd_pos. Core principle: According to the incoming file descriptor, the call fd_local2globalconverts the file descriptor into the specified global open file structure array index, and then the switch case selects the incoming reference object to perform fd_posdifferent processing. The reference object is SEET_SET, then the new one fd_posis equal to the incoming offset; the reference object is SEET_CUR, then the new one fd_pos= original fd_pos+ the incoming offset; the reference object is SEET_END, then the new one fd_posis the original file size + offset (at this time the offset If nothing else, it’s a negative number)

Define the reference location and modify ( myos/fs/fs.h )

/* 文件读写位置偏移量 */
enum whence
{
    
    
    SEEK_SET = 1,
    SEEK_CUR,
    SEEK_END
};

function sys_lseek, modified ( myos/fs/fs.c )

/* 重置用于文件读写操作的偏移指针,成功时返回新的偏移量,出错时返回-1 */
int32_t sys_lseek(int32_t fd, int32_t offset, uint8_t whence)
{
    
    
    if (fd < 0)
    {
    
    
        printk("sys_lseek: fd error\n");
        return -1;
    }
    ASSERT(whence > 0 && whence < 4);
    uint32_t _fd = fd_local2global(fd);
    struct file *pf = &file_table[_fd];
    int32_t new_pos = 0; // 新的偏移量必须位于文件大小之内
    int32_t file_size = (int32_t)pf->fd_inode->i_size;
    switch (whence)
    {
    
    
    /* SEEK_SET 新的读写位置是相对于文件开头再增加offset个位移量 */
    case SEEK_SET:
        new_pos = offset;
        break;

    /* SEEK_CUR 新的读写位置是相对于当前的位置增加offset个位移量 */
    case SEEK_CUR: // offse可正可负
        new_pos = (int32_t)pf->fd_pos + offset;
        break;

    /* SEEK_END 新的读写位置是相对于文件尺寸再增加offset个位移量 */
    case SEEK_END: // 此情况下,offset应该为负值
        new_pos = file_size + offset;
    }
    if (new_pos < 0 || new_pos > (file_size - 1))
    {
    
    
        return -1;
    }
    pf->fd_pos = new_pos;
    return pf->fd_pos;
}

Function declaration ( myos/fs/fs.h )

int32_t sys_lseek(int32_t fd, int32_t offset, uint8_t whence);

Test code ( myso/kernel/main.c )

#include "print.h"
#include "init.h"
#include "fs.h"
#include "stdio.h"
#include "string.h"

int main(void)
{
    
    
    put_str("I am kernel\n");
    init_all();
    uint32_t fd = sys_open("/file1", O_RDWR);
    printf("open /file1, fd:%d\n", fd);
    char buf[64] = {
    
    0};
    int read_bytes = sys_read(fd, buf, 18);
    printf("1_ read %d bytes:\n%s\n", read_bytes, buf);

    memset(buf, 0, 64);
    read_bytes = sys_read(fd, buf, 6);
    printf("2_ read %d bytes:\n%s", read_bytes, buf);

    memset(buf, 0, 64);
    read_bytes = sys_read(fd, buf, 6);
    printf("3_ read %d bytes:\n%s", read_bytes, buf);

    printf("________  SEEK_SET 0  ________\n");
    sys_lseek(fd, 0, SEEK_SET);
    memset(buf, 0, 64);
    read_bytes = sys_read(fd, buf, 24);
    printf("4_ read %d bytes:\n%s", read_bytes, buf);

    sys_close(fd);
    while (1)
        ;
    return 0;
}

Section h:

Next, we implement the file deletion function, which is the reverse process of creating files. Involves inode and directory entries.

The first is to delete the inode:

inode_delete, pass in the index of the inode to be deleted in the inode array, and then delete the inode in the disk. This function is dispensable, because inode allocation is completed by relying on the inode bitmap. When we recycle an inode, we only need to recycle the bits in the inode bitmap. Because after the inode bit is allocated next time, the new inode data will overwrite the old inode data, which can also avoid unnecessary disk reads and writes. Function principle: The call inode_locatecan convert the inode array index into the starting sector and byte offset of this inode on the disk. We read the entire sector where the inode is located into the memory buffer, then clear the inode in the memory buffer, and then write the data in the memory buffer back to the disk.

Modify ( myos/fs/inode.c )

/* 将硬盘分区part上的inode清空 */
void inode_delete(struct partition *part, uint32_t inode_no, void *io_buf)
{
    
    
    ASSERT(inode_no < 4096);
    struct inode_position inode_pos;
    inode_locate(part, inode_no, &inode_pos); // inode位置信息会存入inode_pos
    ASSERT(inode_pos.sec_lba <= (part->start_lba + part->sec_cnt));

    char *inode_buf = (char *)io_buf;
    if (inode_pos.two_sec)
    {
    
     // inode跨扇区,读入2个扇区
        /* 将原硬盘上的内容先读出来 */
        ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 2);
        /* 将inode_buf清0 */
        memset((inode_buf + inode_pos.off_size), 0, sizeof(struct inode));
        /* 用清0的内存数据覆盖磁盘 */
        ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 2);
    }
    else
    {
    
     // 未跨扇区,只读入1个扇区就好
        /* 将原硬盘上的内容先读出来 */
        ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 1);
        /* 将inode_buf清0 */
        memset((inode_buf + inode_pos.off_size), 0, sizeof(struct inode));
        /* 用清0的内存数据覆盖磁盘 */
        ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 1);
    }
}

inode_releaseDelete files, including: delete inodes on the disk and reclaim the blocks occupied by the files. This process includes: 1. Recycle all blocks occupied by the file. You can know which blocks are occupied through i_sectors[] in the inode, and then clear the bits in the corresponding block bitmap, that is, the file is not actually deleted. Data; 2. Recycle inode, first clear the corresponding bit of the inode in the inode bitmap, and then call to inode_deletedelete the inode in the disk

Modify ( myos/fs/inode.c )

#include "file.h"

/* 回收inode的数据块和inode本身 */
void inode_release(struct partition *part, uint32_t inode_no)
{
    
    
    struct inode *inode_to_del = inode_open(part, inode_no);
    ASSERT(inode_to_del->i_no == inode_no);

    /* 1 回收inode占用的所有块 */
    uint8_t block_idx = 0, block_cnt = 12;
    uint32_t block_bitmap_idx;
    uint32_t all_blocks[140] = {
    
    0}; // 12个直接块+128个间接块

    /* a 先将前12个直接块存入all_blocks */
    while (block_idx < 12)
    {
    
    
        all_blocks[block_idx] = inode_to_del->i_sectors[block_idx];
        block_idx++;
    }

    /* b 如果一级间接块表存在,将其128个间接块读到all_blocks[12~], 并释放一级间接块表所占的扇区 */
    if (inode_to_del->i_sectors[12] != 0)
    {
    
    
        ide_read(part->my_disk, inode_to_del->i_sectors[12], all_blocks + 12, 1);
        block_cnt = 140;

        /* 回收一级间接块表占用的扇区 */
        block_bitmap_idx = inode_to_del->i_sectors[12] - part->sb->data_start_lba;
        ASSERT(block_bitmap_idx > 0);
        bitmap_set(&part->block_bitmap, block_bitmap_idx, 0);
        bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
    }

    /* c inode所有的块地址已经收集到all_blocks中,下面逐个回收 */
    block_idx = 0;
    while (block_idx < block_cnt)
    {
    
    
        if (all_blocks[block_idx] != 0)
        {
    
    
            block_bitmap_idx = 0;
            block_bitmap_idx = all_blocks[block_idx] - part->sb->data_start_lba;
            ASSERT(block_bitmap_idx > 0);
            bitmap_set(&part->block_bitmap, block_bitmap_idx, 0);
            bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
        }
        block_idx++;
    }

    /*2 回收该inode所占用的inode */
    bitmap_set(&part->inode_bitmap, inode_no, 0);
    bitmap_sync(cur_part, inode_no, INODE_BITMAP);

    /******     以下inode_delete是调试用的    ******
     * 此函数会在inode_table中将此inode清0,
     * 但实际上是不需要的,inode分配是由inode位图控制的,
     * 硬盘上的数据不需要清0,可以直接覆盖*/
    void *io_buf = sys_malloc(1024);
    inode_delete(part, inode_no, io_buf);
    sys_free(io_buf);
    /***********************************************/

    inode_close(inode_to_del);
}

Function declaration, modification ( myos/fs/inode.h )

void inode_delete(struct partition *part, uint32_t inode_no, void *io_buf);
void inode_release(struct partition *part, uint32_t inode_no);

Next is to delete a directory entry:

delete_dir_entryDelete the directory entry on disk corresponding to the specified file. Core principle: The passed-in struct dir pointer of the parent directory structure of the file to be deleted has an inode member. There is i_sectors[] in this inode to record the storage location of this directory file. Naturally, we can transfer the parent directory from the disk in units of blocks. The directory file is read into the buffer, then traversed to find the directory entry to be deleted, the corresponding directory entry in the buffer is deleted, and then the buffer data is written back.

In this process: If it is found that the block where the directory entry is located (not the block where . is located) only has the directory entry to be deleted, then the method of recycling the block (deleting the bits in the block bitmap, and then synchronizing the block bitmap) is adopted. method to clear directory entries. When recycling this block, it is also necessary to determine whether this block is the only one among the first-level indirect blocks. If so, the first-level indirect block also needs to be recycled.

Modify ( myos/fs/dir.c )

/* 把分区part目录pdir中编号为inode_no的目录项删除 */
bool delete_dir_entry(struct partition *part, struct dir *pdir, uint32_t inode_no, void *io_buf)
{
    
    
    struct inode *dir_inode = pdir->inode;
    uint32_t block_idx = 0, all_blocks[140] = {
    
    0};
    /* 收集目录全部块地址 */
    while (block_idx < 12)
    {
    
    
        all_blocks[block_idx] = dir_inode->i_sectors[block_idx];
        block_idx++;
    }
    if (dir_inode->i_sectors[12])
    {
    
    
        ide_read(part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1);
    }

    /* 目录项在存储时保证不会跨扇区 */
    uint32_t dir_entry_size = part->sb->dir_entry_size;
    uint32_t dir_entrys_per_sec = (SECTOR_SIZE / dir_entry_size); // 每扇区最大的目录项数目
    struct dir_entry *dir_e = (struct dir_entry *)io_buf;
    struct dir_entry *dir_entry_found = NULL;
    uint8_t dir_entry_idx, dir_entry_cnt;
    bool is_dir_first_block = false; // 目录的第1个块

    /* 遍历所有块,寻找目录项 */
    block_idx = 0;
    while (block_idx < 140)
    {
    
    
        is_dir_first_block = false;
        if (all_blocks[block_idx] == 0)
        {
    
    
            block_idx++;
            continue;
        }
        dir_entry_idx = dir_entry_cnt = 0;
        memset(io_buf, 0, SECTOR_SIZE);
        /* 读取扇区,获得目录项 */
        ide_read(part->my_disk, all_blocks[block_idx], io_buf, 1);

        /* 遍历所有的目录项,统计该扇区的目录项数量及是否有待删除的目录项 */
        while (dir_entry_idx < dir_entrys_per_sec)
        {
    
    
            if ((dir_e + dir_entry_idx)->f_type != FT_UNKNOWN)
            {
    
    
                if (!strcmp((dir_e + dir_entry_idx)->filename, "."))
                {
    
    
                    is_dir_first_block = true;
                }
                else if (strcmp((dir_e + dir_entry_idx)->filename, ".") &&
                         strcmp((dir_e + dir_entry_idx)->filename, ".."))
                {
    
    
                    dir_entry_cnt++; // 统计此扇区内的目录项个数,用来判断删除目录项后是否回收该扇区
                    if ((dir_e + dir_entry_idx)->i_no == inode_no)
                    {
    
                                        // 如果找到此i结点,就将其记录在dir_entry_found
                        ASSERT(dir_entry_found == NULL); // 确保目录中只有一个编号为inode_no的inode,找到一次后dir_entry_found就不再是NULL
                        dir_entry_found = dir_e + dir_entry_idx;
                        /* 找到后也继续遍历,统计总共的目录项数 */
                    }
                }
            }
            dir_entry_idx++;
        }

        /* 若此扇区未找到该目录项,继续在下个扇区中找 */
        if (dir_entry_found == NULL)
        {
    
    
            block_idx++;
            continue;
        }

        /* 在此扇区中找到目录项后,清除该目录项并判断是否回收扇区,随后退出循环直接返回 */
        ASSERT(dir_entry_cnt >= 1);
        /* 除目录第1个扇区外,若该扇区上只有该目录项自己,则将整个扇区回收 */
        if (dir_entry_cnt == 1 && !is_dir_first_block)
        {
    
    
            /* a 在块位图中回收该块 */
            uint32_t block_bitmap_idx = all_blocks[block_idx] - part->sb->data_start_lba;
            bitmap_set(&part->block_bitmap, block_bitmap_idx, 0);
            bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);

            /* b 将块地址从数组i_sectors或索引表中去掉 */
            if (block_idx < 12)
            {
    
    
                dir_inode->i_sectors[block_idx] = 0;
            }
            else
            {
    
     // 在一级间接索引表中擦除该间接块地址
                /*先判断一级间接索引表中间接块的数量,如果仅有这1个间接块,连同间接索引表所在的块一同回收 */
                uint32_t indirect_blocks = 0;
                uint32_t indirect_block_idx = 12;
                while (indirect_block_idx < 140)
                {
    
    
                    if (all_blocks[indirect_block_idx] != 0)
                    {
    
    
                        indirect_blocks++;
                    }
                }
                ASSERT(indirect_blocks >= 1); // 包括当前间接块

                if (indirect_blocks > 1)
                {
    
     // 间接索引表中还包括其它间接块,仅在索引表中擦除当前这个间接块地址
                    all_blocks[block_idx] = 0;
                    ide_write(part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1);
                }
                else
                {
    
     // 间接索引表中就当前这1个间接块,直接把间接索引表所在的块回收,然后擦除间接索引表块地址
                    /* 回收间接索引表所在的块 */
                    block_bitmap_idx = dir_inode->i_sectors[12] - part->sb->data_start_lba;
                    bitmap_set(&part->block_bitmap, block_bitmap_idx, 0);
                    bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);

                    /* 将间接索引表地址清0 */
                    dir_inode->i_sectors[12] = 0;
                }
            }
        }
        else
        {
    
     // 仅将该目录项清空
            memset(dir_entry_found, 0, dir_entry_size);
            ide_write(part->my_disk, all_blocks[block_idx], io_buf, 1);
        }

        /* 更新i结点信息并同步到硬盘 */
        ASSERT(dir_inode->i_size >= dir_entry_size);
        dir_inode->i_size -= dir_entry_size;
        memset(io_buf, 0, SECTOR_SIZE * 2);
        inode_sync(part, dir_inode, io_buf);

        return true;
    }
    /* 所有块中未找到则返回false,若出现这种情况应该是serarch_file出错了 */
    return false;
}

Function declaration, modification ( myos/fs/dir.h )

bool delete_dir_entry(struct partition *part, struct dir *pdir, uint32_t inode_no, void *io_buf);

sys_unlinkUsed to delete non-directory files based on the incoming path. Principle: First call search_filethe search path to return the inode of the file, and determine whether the inode corresponds to an open global file structure. If so, it means that the file is being used and should not be deleted. If not, call to delete_dir_entrydelete the directory entry of this file on the disk and call to inode_releasedelete the file corresponding to the inode, which completes the deletion.

Modify ( myos/fs/fs.c )

/* 删除文件(非目录),成功返回0,失败返回-1 */
int32_t sys_unlink(const char *pathname)
{
    
    
    ASSERT(strlen(pathname) < MAX_PATH_LEN);

    /* 先检查待删除的文件是否存在 */
    struct path_search_record searched_record;
    memset(&searched_record, 0, sizeof(struct path_search_record));
    int inode_no = search_file(pathname, &searched_record);
    ASSERT(inode_no != 0);
    if (inode_no == -1)
    {
    
    
        printk("file %s not found!\n", pathname);
        dir_close(searched_record.parent_dir);
        return -1;
    }
    if (searched_record.file_type == FT_DIRECTORY)
    {
    
    
        printk("can`t delete a direcotry with unlink(), use rmdir() to instead\n");
        dir_close(searched_record.parent_dir);
        return -1;
    }

    /* 检查是否在已打开文件列表(文件表)中 */
    uint32_t file_idx = 0;
    while (file_idx < MAX_FILE_OPEN)
    {
    
    
        if (file_table[file_idx].fd_inode != NULL && (uint32_t)inode_no == file_table[file_idx].fd_inode->i_no)
        {
    
    
            break;
        }
        file_idx++;
    }
    if (file_idx < MAX_FILE_OPEN)
    {
    
    
        dir_close(searched_record.parent_dir);
        printk("file %s is in use, not allow to delete!\n", pathname);
        return -1;
    }
    ASSERT(file_idx == MAX_FILE_OPEN);

    /* 为delete_dir_entry申请缓冲区 */
    void *io_buf = sys_malloc(SECTOR_SIZE + SECTOR_SIZE);
    if (io_buf == NULL)
    {
    
    
        dir_close(searched_record.parent_dir);
        printk("sys_unlink: malloc for io_buf failed\n");
        return -1;
    }

    struct dir *parent_dir = searched_record.parent_dir;
    delete_dir_entry(cur_part, parent_dir, inode_no, io_buf);
    inode_release(cur_part, inode_no);
    sys_free(io_buf);
    dir_close(searched_record.parent_dir);
    return 0; // 成功删除文件
}

Function declaration, modification ( myos/fs/fs.h )

int32_t sys_unlink(const char *pathname);

Test code ( myos/kernel/main.c )

#include "print.h"
#include "init.h"
#include "fs.h"
#include "stdio.h"
#include "string.h"

int main(void) {
    
    
   put_str("I am kernel\n");
   init_all();
   printf("/file1 delete %s!\n", sys_unlink("/file1") == 0 ? "done" : "fail");
   while(1);
   return 0;
}

In order to facilitate debugging, we modify ( myos/fs/fs.c/mount_partition ). After debugging, we need to delete the added code.

        printk("mount %s done!\n", part->name);
        /* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关。
            只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历.*/
        sys_free(sb_buf);

for

        printk("mount %s done!\n", part->name);
        printk("sdb1's block_bitmap_lba: %x\n", sb_buf->block_bitmap_lba);
        printk("sdb1's inode_bitmap_lba: %x\n", sb_buf->inode_bitmap_lba);
        printk("sdb1's inode_table_lba: %x\n", sb_buf->inode_table_lba);
        printk("sdb1's data_start_lba: %x\n", sb_buf->data_start_lba);
        /* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关。
            只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历.*/
        sys_free(sb_buf);

Section i:

The work involved in creating a directory file actually involves inodes and directory entries.

  1. Verify that the new directory to be created does not exist on the file system.

  2. Create an inode for the new directory.

  3. Allocate 1 block for the new directory to store the directory entries in the directory file.

  4. Create two directory entries "..." and "." in the new directory, which are two directory entries that must exist in every directory.

  5. Adds a directory entry for the new directory in the new directory's parent directory.

  6. Synchronize changes to the above resources to disk.

sys_mkdirUsed to create a directory file based on the passed path. Core principle: 1. Call search_fileto confirm that the new directory file to be created does not exist on the file system; 2. Call inode_bitmap_alloc to allocate an inode index for the new directory, and call inode_init to initialize the inode; 3. Call block_bitmap_alloc to allocate a block for use in the directory file that hosts the directory. At the same time, set the i_sectors[0] of the inode and call the bitmap_sync synchronization block bitmap; 4. Clear the buffer, and then write two directory entries. and..., and then write the buffer contents to the directory file. This is Completed. The creation of two directory entries with...; 5. Create and set your own directory entry, call sync_dir_entry to synchronize the directory entry to the parent directory; 6. Synchronize the inode of the parent directory with your own inode with inode_sync, synchronize with bitmap_sync inode bitmap

Modify ( myos/fs/fs.c )

/* 创建目录pathname,成功返回0,失败返回-1 */
int32_t sys_mkdir(const char *pathname)
{
    
    
    uint8_t rollback_step = 0; // 用于操作失败时回滚各资源状态
    void *io_buf = sys_malloc(SECTOR_SIZE * 2);
    if (io_buf == NULL)
    {
    
    
        printk("sys_mkdir: sys_malloc for io_buf failed\n");
        return -1;
    }

    struct path_search_record searched_record;
    memset(&searched_record, 0, sizeof(struct path_search_record));
    int inode_no = -1;
    inode_no = search_file(pathname, &searched_record);
    if (inode_no != -1)
    {
    
     // 如果找到了同名目录或文件,失败返回
        printk("sys_mkdir: file or directory %s exist!\n", pathname);
        rollback_step = 1;
        goto rollback;
    }
    else
    {
    
     // 若未找到,也要判断是在最终目录没找到还是某个中间目录不存在
        uint32_t pathname_depth = path_depth_cnt((char *)pathname);
        uint32_t path_searched_depth = path_depth_cnt(searched_record.searched_path);
        /* 先判断是否把pathname的各层目录都访问到了,即是否在某个中间目录就失败了 */
        if (pathname_depth != path_searched_depth)
        {
    
     // 说明并没有访问到全部的路径,某个中间目录是不存在的
            printk("sys_mkdir: can`t access %s, subpath %s is`t exist\n", pathname, searched_record.searched_path);
            rollback_step = 1;
            goto rollback;
        }
    }

    struct dir *parent_dir = searched_record.parent_dir;
    /* 目录名称后可能会有字符'/',所以最好直接用searched_record.searched_path,无'/' */
    char *dirname = strrchr(searched_record.searched_path, '/') + 1;

    inode_no = inode_bitmap_alloc(cur_part);
    if (inode_no == -1)
    {
    
    
        printk("sys_mkdir: allocate inode failed\n");
        rollback_step = 1;
        goto rollback;
    }

    struct inode new_dir_inode;
    inode_init(inode_no, &new_dir_inode); // 初始化i结点

    uint32_t block_bitmap_idx = 0; // 用来记录block对应于block_bitmap中的索引
    int32_t block_lba = -1;
    /* 为目录分配一个块,用来写入目录.和.. */
    block_lba = block_bitmap_alloc(cur_part);
    if (block_lba == -1)
    {
    
    
        printk("sys_mkdir: block_bitmap_alloc for create directory failed\n");
        rollback_step = 2;
        goto rollback;
    }
    new_dir_inode.i_sectors[0] = block_lba;
    /* 每分配一个块就将位图同步到硬盘 */
    block_bitmap_idx = block_lba - cur_part->sb->data_start_lba;
    ASSERT(block_bitmap_idx != 0);
    bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);

    /* 将当前目录的目录项'.'和'..'写入目录 */
    memset(io_buf, 0, SECTOR_SIZE * 2); // 清空io_buf
    struct dir_entry *p_de = (struct dir_entry *)io_buf;

    /* 初始化当前目录"." */
    memcpy(p_de->filename, ".", 1);
    p_de->i_no = inode_no;
    p_de->f_type = FT_DIRECTORY;

    p_de++;
    /* 初始化当前目录".." */
    memcpy(p_de->filename, "..", 2);
    p_de->i_no = parent_dir->inode->i_no;
    p_de->f_type = FT_DIRECTORY;
    ide_write(cur_part->my_disk, new_dir_inode.i_sectors[0], io_buf, 1);

    new_dir_inode.i_size = 2 * cur_part->sb->dir_entry_size;

    /* 在父目录中添加自己的目录项 */
    struct dir_entry new_dir_entry;
    memset(&new_dir_entry, 0, sizeof(struct dir_entry));
    create_dir_entry(dirname, inode_no, FT_DIRECTORY, &new_dir_entry);
    memset(io_buf, 0, SECTOR_SIZE * 2); // 清空io_buf
    if (!sync_dir_entry(parent_dir, &new_dir_entry, io_buf))
    {
    
     // sync_dir_entry中将block_bitmap通过bitmap_sync同步到硬盘
        printk("sys_mkdir: sync_dir_entry to disk failed!\n");
        rollback_step = 2;
        goto rollback;
    }

    /* 父目录的inode同步到硬盘 */
    memset(io_buf, 0, SECTOR_SIZE * 2);
    inode_sync(cur_part, parent_dir->inode, io_buf);

    /* 将新创建目录的inode同步到硬盘 */
    memset(io_buf, 0, SECTOR_SIZE * 2);
    inode_sync(cur_part, &new_dir_inode, io_buf);

    /* 将inode位图同步到硬盘 */
    bitmap_sync(cur_part, inode_no, INODE_BITMAP);

    sys_free(io_buf);

    /* 关闭所创建目录的父目录 */
    dir_close(searched_record.parent_dir);
    return 0;

/*创建文件或目录需要创建相关的多个资源,若某步失败则会执行到下面的回滚步骤 */
rollback: // 因为某步骤操作失败而回滚
    switch (rollback_step)
    {
    
    
    case 2:
        bitmap_set(&cur_part->inode_bitmap, inode_no, 0); // 如果新文件的inode创建失败,之前位图中分配的inode_no也要恢复
    case 1:
        /* 关闭所创建目录的父目录 */
        dir_close(searched_record.parent_dir);
        break;
    }
    sys_free(io_buf);
    return -1;
}

Function declaration, modification ( myos/fs/fs.h )

int32_t sys_mkdir(const char *pathname);

Test code ( myos/kernel/main.c )

#include "print.h"
#include "init.h"
#include "fs.h"
#include "stdio.h"
#include "string.h"

int main(void) {
    
    
   put_str("I am kernel\n");
   init_all();
   printf("/dir1/subdir1 create %s!\n", sys_mkdir("/dir1/subdir1") == 0 ? "done" : "fail");
   printf("/dir1 create %s!\n", sys_mkdir("/dir1") == 0 ? "done" : "fail");
   printf("now, /dir1/subdir1 create %s!\n", sys_mkdir("/dir1/subdir1") == 0 ? "done" : "fail");
   int fd = sys_open("/dir1/subdir1/file2", O_CREAT|O_RDWR);
   if (fd != -1) {
    
    
      printf("/dir1/subdir1/file2 create done!\n");
      sys_write(fd, "Catch me if you can!\n", 21);
      sys_lseek(fd, 0, SEEK_SET);
      char buf[32] = {
    
    0};
      sys_read(fd, buf, 21); 
      printf("/dir1/subdir1/file2 says:\n%s", buf);
      sys_close(fd);
   }
   while(1);
   return 0;
}

Section j:

sys_opendirUsed to open a directory based on the passed path. Principle: Call search_fileto convert the path into an inode index, then call to dir_opencreate the directory structure and load the corresponding inode into the memory

Modify ( myos/fs/fs.c )

/* 目录打开成功后返回目录指针,失败返回NULL */
struct dir *sys_opendir(const char *name)
{
    
    
    ASSERT(strlen(name) < MAX_PATH_LEN);
    /* 如果是根目录'/',直接返回&root_dir */
    if (name[0] == '/' && (name[1] == 0 || name[0] == '.'))
    {
    
    
        return &root_dir;
    }

    /* 先检查待打开的目录是否存在 */
    struct path_search_record searched_record;
    memset(&searched_record, 0, sizeof(struct path_search_record));
    int inode_no = search_file(name, &searched_record);
    struct dir *ret = NULL;
    if (inode_no == -1)
    {
    
     // 如果找不到目录,提示不存在的路径
        printk("In %s, sub path %s not exist\n", name, searched_record.searched_path);
    }
    else
    {
    
    
        if (searched_record.file_type == FT_REGULAR)
        {
    
    
            printk("%s is regular file!\n", name);
        }
        else if (searched_record.file_type == FT_DIRECTORY)
        {
    
    
            ret = dir_open(cur_part, inode_no);
        }
    }
    dir_close(searched_record.parent_dir);
    return ret;
}

sys_closedirThat's dir_closethe package

Modify ( myos/fs/fs.c )

/* 成功关闭目录dir返回0,失败返回-1 */
int32_t sys_closedir(struct dir *dir)
{
    
    
    int32_t ret = -1;
    if (dir != NULL)
    {
    
    
        dir_close(dir);
        ret = 0;
    }
    return ret;
}

Function declaration ( myos/fs/fs.h )

struct dir *sys_opendir(const char *name);
int32_t sys_closedir(struct dir *dir);

Test code ( myos/kernel/main.c )

#include "print.h"
#include "init.h"
#include "fs.h"
#include "stdio.h"
#include "string.h"

int main(void)
{
    
    
    put_str("I am kernel\n");
    init_all();
    struct dir *p_dir = sys_opendir("/dir1/subdir1");
    if (p_dir)
    {
    
    
        printf("/dir1/subdir1 open done!\n");
        if (sys_closedir(p_dir) == 0)
        {
    
    
            printf("/dir1/subdir1 close done!\n");
        }
        else
        {
    
    
            printf("/dir1/subdir1 close fail!\n");
        }
    }
    else
    {
    
    
        printf("/dir1/subdir1 open fail!\n");
    }
    while (1)
        ;
    return 0;
}

Section k:

dir_readUsed to return one directory item at a time according to the passed in directory pointer. For example, the distribution of directory items under the directory file: a, empty, b, empty, c. The first call returns a, and the second call returns b. The third call returns c... Principle: There is a pointer to its own inode in the directory. There is i_sectors[] in the inode, so the directory file can be found on the disk, read out to the buffer and then set as needed. Just set the traversal rules.

The actual meaning in the code dir_posis the total size of the directory entries that have been returned, and cur_dir_entry_posthe meaning is the total size of the non-empty directory entries scanned during this call traversal. When a non-empty directory entry is scanned, if cur_dir_entry_posand are dir_posequal at this time, it can naturally be judged that this non-empty directory entry should be returned.

Modify ( myos/fs/dir.c )

/* 读取目录,成功返回1个目录项,失败返回NULL */
struct dir_entry *dir_read(struct dir *dir)
{
    
    
    struct dir_entry *dir_e = (struct dir_entry *)dir->dir_buf;
    struct inode *dir_inode = dir->inode;
    uint32_t all_blocks[140] = {
    
    0}, block_cnt = 12;
    uint32_t block_idx = 0, dir_entry_idx = 0;
    while (block_idx < 12)
    {
    
    
        all_blocks[block_idx] = dir_inode->i_sectors[block_idx];
        block_idx++;
    }
    if (dir_inode->i_sectors[12] != 0)
    {
    
     // 若含有一级间接块表
        ide_read(cur_part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1);
        block_cnt = 140;
    }
    block_idx = 0;

    uint32_t cur_dir_entry_pos = 0; // 当前目录项的偏移,此项用来判断是否是之前已经返回过的目录项
    uint32_t dir_entry_size = cur_part->sb->dir_entry_size;
    uint32_t dir_entrys_per_sec = SECTOR_SIZE / dir_entry_size; // 1扇区内可容纳的目录项个数
    /* 因为此目录内可能删除了某些文件或子目录,所以要遍历所有块 */
    while (block_idx < block_cnt)
    {
    
    
        if (dir->dir_pos >= dir_inode->i_size)
        {
    
    
            return NULL;
        }
        if (all_blocks[block_idx] == 0)
        {
    
     // 如果此块地址为0,即空块,继续读出下一块
            block_idx++;
            continue;
        }
        memset(dir_e, 0, SECTOR_SIZE);
        ide_read(cur_part->my_disk, all_blocks[block_idx], dir_e, 1);
        dir_entry_idx = 0;
        /* 遍历扇区内所有目录项 */
        while (dir_entry_idx < dir_entrys_per_sec)
        {
    
    
            if ((dir_e + dir_entry_idx)->f_type)
            {
    
     // 如果f_type不等于0,即不等于FT_UNKNOWN
                /* 判断是不是最新的目录项,避免返回曾经已经返回过的目录项 */
                if (cur_dir_entry_pos < dir->dir_pos)
                {
    
    
                    cur_dir_entry_pos += dir_entry_size;
                    dir_entry_idx++;
                    continue;
                }
                ASSERT(cur_dir_entry_pos == dir->dir_pos);
                dir->dir_pos += dir_entry_size; // 更新为新位置,即下一个返回的目录项地址
                return dir_e + dir_entry_idx;
            }
            dir_entry_idx++;
        }
        block_idx++;
    }
    return NULL;
}

Function declaration, modification ( myos/fs/dir.h )

struct dir_entry *dir_read(struct dir *dir);

sys_readdir, that is dir_read, the package

sys_rewinddirSet dir_pos of the directory to 0

Modify ( myos/fs/fs.c )

/* 读取目录dir的1个目录项,成功后返回其目录项地址,到目录尾时或出错时返回NULL */
struct dir_entry *sys_readdir(struct dir *dir)
{
    
    
    ASSERT(dir != NULL);
    return dir_read(dir);
}

/* 把目录dir的指针dir_pos置0 */
void sys_rewinddir(struct dir *dir)
{
    
    
    dir->dir_pos = 0;
}

Function declaration, modification ( myos/fs/fs.h )

struct dir_entry *sys_readdir(struct dir *dir);
void sys_rewinddir(struct dir *dir);

Test code, ( myos/kernel/main.c )

#include "print.h"
#include "init.h"
#include "fs.h"
#include "stdio.h"
#include "string.h"
#include "dir.h"

int main(void)
{
    
    
    put_str("I am kernel\n");
    init_all();
    /********  测试代码  ********/
    struct dir *p_dir = sys_opendir("/dir1/subdir1");
    if (p_dir)
    {
    
    
        printf("/dir1/subdir1 open done!\ncontent:\n");
        char *type = NULL;
        struct dir_entry *dir_e = NULL;
        while ((dir_e = sys_readdir(p_dir)))
        {
    
    
            if (dir_e->f_type == FT_REGULAR)
            {
    
    
                type = "regular";
            }
            else
            {
    
    
                type = "directory";
            }
            printf("      %s   %s\n", type, dir_e->filename);
        }
        if (sys_closedir(p_dir) == 0)
        {
    
    
            printf("/dir1/subdir1 close done!\n");
        }
        else
        {
    
    
            printf("/dir1/subdir1 close fail!\n");
        }
    }
    else
    {
    
    
        printf("/dir1/subdir1 open fail!\n");
    }
    /********  测试代码  ********/
    while (1)
        ;
    return 0;
}

Section l:

dir_is_emptyDetermine whether the directory is empty. Principle: There are only two directory entries in the directory file corresponding to the empty directory, namely. and.... Therefore, we can directly determine whether the i_size in the inode corresponding to the directory is equal to the size of the two directory entries.

dir_removePass in the parent directory and subdirectory pointers and delete the subdirectory in the specified parent directory. Principle: Determine whether the subdirectory is empty, then call to delete_dir_entrydelete the subdirectory directory entry in the directory file of the parent directory, and finally call to inode_releasedelete the directory file of the subdirectory (there should only be . and... in the subdirectory directory file)

Modify ( myso/fs/dir.c )

/* 判断目录是否为空 */
bool dir_is_empty(struct dir *dir)
{
    
    
    struct inode *dir_inode = dir->inode;
    /* 若目录下只有.和..这两个目录项则目录为空 */
    return (dir_inode->i_size == cur_part->sb->dir_entry_size * 2);
}

/* 在父目录parent_dir中删除child_dir */
int32_t dir_remove(struct dir *parent_dir, struct dir *child_dir)
{
    
    
    struct inode *child_dir_inode = child_dir->inode;
    /* 空目录只在inode->i_sectors[0]中有扇区,其它扇区都应该为空 */
    int32_t block_idx = 1;
    while (block_idx < 13)
    {
    
    
        ASSERT(child_dir_inode->i_sectors[block_idx] == 0);
        block_idx++;
    }
    void *io_buf = sys_malloc(SECTOR_SIZE * 2);
    if (io_buf == NULL)
    {
    
    
        printk("dir_remove: malloc for io_buf failed\n");
        return -1;
    }

    /* 在父目录parent_dir中删除子目录child_dir对应的目录项 */
    delete_dir_entry(cur_part, parent_dir, child_dir_inode->i_no, io_buf);

    /* 回收inode中i_secotrs中所占用的扇区,并同步inode_bitmap和block_bitmap */
    inode_release(cur_part, child_dir_inode->i_no);
    sys_free(io_buf);
    return 0;
}

Function declaration, modification ( myos/fs/dir.h )

bool dir_is_empty(struct dir *dir);
int32_t dir_remove(struct dir *parent_dir, struct dir *child_dir);

sys_rmdirUsed to delete empty directories based on the incoming path. Principle: First call search_fileto find the inode of the directory corresponding to this path. If it exists and the inode corresponds to a directory file, then call to dir_opentransfer the inode into the memory and create the corresponding struct dir. Call to dir_is_emptydetermine that the directory is empty, and finally call to dir_removedelete the inode. Table of contents.

Modify ( myos/fs/fs.c )

/* 删除空目录,成功时返回0,失败时返回-1*/
int32_t sys_rmdir(const char *pathname)
{
    
    
    /* 先检查待删除的文件是否存在 */
    struct path_search_record searched_record;
    memset(&searched_record, 0, sizeof(struct path_search_record));
    int inode_no = search_file(pathname, &searched_record);
    ASSERT(inode_no != 0);
    int retval = -1; // 默认返回值
    if (inode_no == -1)
    {
    
    
        printk("In %s, sub path %s not exist\n", pathname, searched_record.searched_path);
    }
    else
    {
    
    
        if (searched_record.file_type == FT_REGULAR)
        {
    
    
            printk("%s is regular file!\n", pathname);
        }
        else
        {
    
    
            struct dir *dir = dir_open(cur_part, inode_no);
            if (!dir_is_empty(dir))
            {
    
     // 非空目录不可删除
                printk("dir %s is not empty, it is not allowed to delete a nonempty directory!\n", pathname);
            }
            else
            {
    
    
                if (!dir_remove(searched_record.parent_dir, dir))
                {
    
    
                    retval = 0;
                }
            }
            dir_close(dir);
        }
    }
    dir_close(searched_record.parent_dir);
    return retval;
}

Function declaration, modification ( myos/fs/fs.h )

int32_t sys_rmdir(const char *pathname);

Test code ( myos/kernel/main.c )

#include "print.h"
#include "init.h"
#include "fs.h"
#include "stdio.h"
#include "string.h"
#include "dir.h"

int main(void)
{
    
    
    put_str("I am kernel\n");
    init_all();
    /********  测试代码  ********/
    printf("/dir1 content before delete /dir1/subdir1:\n");
    struct dir *dir = sys_opendir("/dir1/");
    char *type = NULL;
    struct dir_entry *dir_e = NULL;
    while ((dir_e = sys_readdir(dir)))
    {
    
    
        if (dir_e->f_type == FT_REGULAR)
        {
    
    
            type = "regular";
        }
        else
        {
    
    
            type = "directory";
        }
        printf("      %s   %s\n", type, dir_e->filename);
    }
    printf("try to delete nonempty directory /dir1/subdir1\n");
    if (sys_rmdir("/dir1/subdir1") == -1)
    {
    
    
        printf("sys_rmdir: /dir1/subdir1 delete fail!\n");
    }

    printf("try to delete /dir1/subdir1/file2\n");
    if (sys_rmdir("/dir1/subdir1/file2") == -1)
    {
    
    
        printf("sys_rmdir: /dir1/subdir1/file2 delete fail!\n");
    }
    if (sys_unlink("/dir1/subdir1/file2") == 0)
    {
    
    
        printf("sys_unlink: /dir1/subdir1/file2 delete done\n");
    }

    printf("try to delete directory /dir1/subdir1 again\n");
    if (sys_rmdir("/dir1/subdir1") == 0)
    {
    
    
        printf("/dir1/subdir1 delete done!\n");
    }

    printf("/dir1 content after delete /dir1/subdir1:\n");
    sys_rewinddir(dir);
    while ((dir_e = sys_readdir(dir)))
    {
    
    
        if (dir_e->f_type == FT_REGULAR)
        {
    
    
            type = "regular";
        }
        else
        {
    
    
            type = "directory";
        }
        printf("      %s   %s\n", type, dir_e->filename);
    }

    /********  测试代码  ********/
    while (1)
        ;
    return 0;
}

Section m:

Task's working directory

get_parent_dir_inode_nrPassing in the subdirectory inode index returns the inode index of the parent directory. Principle: Call inode_open to load the inode corresponding to the subdirectory into the memory, take out the i_sectors[0] address of the inode, load this directory file into the memory, find the directory entry corresponding to..., take out the inode index corresponding to the parent directory and return it. .

Modify ( myos/fs/fs.c )

/* 获得父目录的inode编号 */
static uint32_t get_parent_dir_inode_nr(uint32_t child_inode_nr, void *io_buf)
{
    
    
    struct inode *child_dir_inode = inode_open(cur_part, child_inode_nr);
    /* 目录中的目录项".."中包括父目录inode编号,".."位于目录的第0块 */
    uint32_t block_lba = child_dir_inode->i_sectors[0];
    ASSERT(block_lba >= cur_part->sb->data_start_lba);
    inode_close(child_dir_inode);
    ide_read(cur_part->my_disk, block_lba, io_buf, 1);
    struct dir_entry *dir_e = (struct dir_entry *)io_buf;
    /* 第0个目录项是".",第1个目录项是".." */
    ASSERT(dir_e[1].i_no < 4096 && dir_e[1].f_type == FT_DIRECTORY);
    return dir_e[1].i_no; // 返回..即父目录的inode编号
}

get_child_dir_nameReturn the name of the subdirectory through the inode index of the parent directory and the inode index of the subdirectory passed in. Principle: Call inode_opento load the inode of the parent directory into the memory, then load the directory file of the parent directory into the memory through i_sectors[] in the inode, traverse the directory entries, and compare the i_nr of the directory entry with that of the subdirectory during the traversal process The inode indexes are equal. If so, copy the name to the buffer.

Modify ( myos/fs/fs.c )

/* 在inode编号为p_inode_nr的目录中查找inode编号为c_inode_nr的子目录的名字,
 * 将名字存入缓冲区path.成功返回0,失败返-1 */
static int get_child_dir_name(uint32_t p_inode_nr, uint32_t c_inode_nr, char *path, void *io_buf)
{
    
    
    struct inode *parent_dir_inode = inode_open(cur_part, p_inode_nr);
    /* 填充all_blocks,将该目录的所占扇区地址全部写入all_blocks */
    uint8_t block_idx = 0;
    uint32_t all_blocks[140] = {
    
    0}, block_cnt = 12;
    while (block_idx < 12)
    {
    
    
        all_blocks[block_idx] = parent_dir_inode->i_sectors[block_idx];
        block_idx++;
    }
    if (parent_dir_inode->i_sectors[12])
    {
    
     // 若包含了一级间接块表,将共读入all_blocks.
        ide_read(cur_part->my_disk, parent_dir_inode->i_sectors[12], all_blocks + 12, 1);
        block_cnt = 140;
    }
    inode_close(parent_dir_inode);

    struct dir_entry *dir_e = (struct dir_entry *)io_buf;
    uint32_t dir_entry_size = cur_part->sb->dir_entry_size;
    uint32_t dir_entrys_per_sec = (512 / dir_entry_size);
    block_idx = 0;
    /* 遍历所有块 */
    while (block_idx < block_cnt)
    {
    
    
        if (all_blocks[block_idx])
        {
    
     // 如果相应块不为空则读入相应块
            ide_read(cur_part->my_disk, all_blocks[block_idx], io_buf, 1);
            uint8_t dir_e_idx = 0;
            /* 遍历每个目录项 */
            while (dir_e_idx < dir_entrys_per_sec)
            {
    
    
                if ((dir_e + dir_e_idx)->i_no == c_inode_nr)
                {
    
    
                    strcat(path, "/");
                    strcat(path, (dir_e + dir_e_idx)->filename);
                    return 0;
                }
                dir_e_idx++;
            }
        }
        block_idx++;
    }
    return -1;
}

sys_getcwdUsed to resolve the absolute working path of the currently running process or thread. Principle: We have previously added cwd_inode_nr to the PCB to represent the task working directory inode index. It is assumed that the inode index is correct now. First, call get_parent_dir_inode_nrto get the parent directory inode index. At this time, the original cwd_inode_nr becomes the subdirectory inode index. Then you can call to get_child_dir_nameget the subdirectory name, then convert the parent directory inode index into a new subdirectory index, and then call to get_parent_dir_inode_nrget the new one. Parent directory index... In this loop, the reversed absolute path will be stored in the buffer. For example, if a process works under /home/kanshan/test, the buffer will be stored in /test/kanshan/home, so we can finally reverse this path.

Modify ( myos/fs/fs.c )

/* 把当前工作目录绝对路径写入buf, size是buf的大小.
 当buf为NULL时,由操作系统分配存储工作路径的空间并返回地址
 失败则返回NULL */
char *sys_getcwd(char *buf, uint32_t size)
{
    
    
    /* 确保buf不为空,若用户进程提供的buf为NULL,
    系统调用getcwd中要为用户进程通过malloc分配内存 */
    ASSERT(buf != NULL);
    void *io_buf = sys_malloc(SECTOR_SIZE);
    if (io_buf == NULL)
    {
    
    
        return NULL;
    }

    struct task_struct *cur_thread = running_thread();
    int32_t parent_inode_nr = 0;
    int32_t child_inode_nr = cur_thread->cwd_inode_nr;
    ASSERT(child_inode_nr >= 0 && child_inode_nr < 4096); // 最大支持4096个inode
    /* 若当前目录是根目录,直接返回'/' */
    if (child_inode_nr == 0)
    {
    
    
        buf[0] = '/';
        buf[1] = 0;
        return buf;
    }

    memset(buf, 0, size);
    char full_path_reverse[MAX_PATH_LEN] = {
    
    0}; // 用来做全路径缓冲区

    /* 从下往上逐层找父目录,直到找到根目录为止.
     * 当child_inode_nr为根目录的inode编号(0)时停止,
     * 即已经查看完根目录中的目录项 */
    while ((child_inode_nr))
    {
    
    
        parent_inode_nr = get_parent_dir_inode_nr(child_inode_nr, io_buf);
        if (get_child_dir_name(parent_inode_nr, child_inode_nr, full_path_reverse, io_buf) == -1)
        {
    
     // 或未找到名字,失败退出
            sys_free(io_buf);
            return NULL;
        }
        child_inode_nr = parent_inode_nr;
    }
    ASSERT(strlen(full_path_reverse) <= size);
    /* 至此full_path_reverse中的路径是反着的,
     * 即子目录在前(左),父目录在后(右) ,
     * 现将full_path_reverse中的路径反置 */
    char *last_slash; // 用于记录字符串中最后一个斜杠地址
    while ((last_slash = strrchr(full_path_reverse, '/')))
    {
    
    
        uint16_t len = strlen(buf);
        strcpy(buf + len, last_slash);
        /* 在full_path_reverse中添加结束字符,做为下一次执行strcpy中last_slash的边界 */
        *last_slash = 0;
    }
    sys_free(io_buf);
    return buf;
}

sys_chdirPass in the path to a directory and then change the working directory index of the currently running process or thread. Principle: The call search_fileparses the incoming path into a corresponding inode index, and then directly modifies the cwd_inode_nr of the current task pcb to the previously returned inode index.

/* 更改当前工作目录为绝对路径path,成功则返回0,失败返回-1 */
int32_t sys_chdir(const char *path)
{
    
    
    int32_t ret = -1;
    struct path_search_record searched_record;
    memset(&searched_record, 0, sizeof(struct path_search_record));
    int inode_no = search_file(path, &searched_record);
    if (inode_no != -1)
    {
    
    
        if (searched_record.file_type == FT_DIRECTORY)
        {
    
    
            running_thread()->cwd_inode_nr = inode_no;
            ret = 0;
        }
        else
        {
    
    
            printk("sys_chdir: %s is regular file or other!\n", path);
        }
    }
    dir_close(searched_record.parent_dir);
    return ret;
}

Function declaration, modification ( myos/fs/fs.h )

char *sys_getcwd(char *buf, uint32_t size);
int32_t sys_chdir(const char *path);

Test code ( myos/kernle/main.c )

#include "print.h"
#include "init.h"
#include "fs.h"
#include "stdio.h"
#include "string.h"
#include "dir.h"

int main(void) {
    
    
   put_str("I am kernel\n");
   init_all();
/********  测试代码  ********/
   char cwd_buf[32] = {
    
    0};
   sys_getcwd(cwd_buf, 32);
   printf("cwd:%s\n", cwd_buf);
   sys_chdir("/dir1");
   printf("change cwd now\n");
   sys_getcwd(cwd_buf, 32);
   printf("cwd:%s\n", cwd_buf);
/********  测试代码  ********/
   while(1);
   return 0;
}

Section n:

Get file attributes

Modify ( myos/fs/fs.h ) and add structure definition for recording file attributes

/* 文件属性结构体 */
struct stat
{
    
    
    uint32_t st_ino;             // inode编号
    uint32_t st_size;            // 尺寸
    enum file_types st_filetype; // 文件类型
};

sys_statPass in a path, call search_file to parse the path, obtain the inode index corresponding to the path, then call inode_openthe inode to be transferred into the memory, and then assign the corresponding member of struct stat.

Modify ( myos/fs/fs.c )

/* 在buf中填充文件结构相关信息,成功时返回0,失败返回-1 */
int32_t sys_stat(const char *path, struct stat *buf)
{
    
    
    /* 若直接查看根目录'/' */
    if (!strcmp(path, "/") || !strcmp(path, "/.") || !strcmp(path, "/.."))
    {
    
    
        buf->st_filetype = FT_DIRECTORY;
        buf->st_ino = 0;
        buf->st_size = root_dir.inode->i_size;
        return 0;
    }

    int32_t ret = -1; // 默认返回值
    struct path_search_record searched_record;
    memset(&searched_record, 0, sizeof(struct path_search_record)); // 记得初始化或清0,否则栈中信息不知道是什么
    int inode_no = search_file(path, &searched_record);
    if (inode_no != -1)
    {
    
    
        struct inode *obj_inode = inode_open(cur_part, inode_no); // 只为获得文件大小
        buf->st_size = obj_inode->i_size;
        inode_close(obj_inode);
        buf->st_filetype = searched_record.file_type;
        buf->st_ino = inode_no;
        ret = 0;
    }
    else
    {
    
    
        printk("sys_stat: %s not found\n", path);
    }
    dir_close(searched_record.parent_dir);
    return ret;
}

Function declaration, modification ( myos/fs/fs.h )

int32_t sys_stat(const char *path, struct stat *buf);

Test code ( myos/kernle/main.c )

#include "print.h"
#include "init.h"
#include "fs.h"
#include "stdio.h"

int main(void)
{
    
    
    put_str("I am kernel\n");
    init_all();
    /********  测试代码  ********/
    struct stat obj_stat;
    sys_stat("/", &obj_stat);
    printf("/`s info\n   i_no:%d\n   size:%d\n   filetype:%s\n",
           obj_stat.st_ino, obj_stat.st_size,
           obj_stat.st_filetype == 2 ? "directory" : "regular");
    sys_stat("/dir1", &obj_stat);
    printf("/dir1`s info\n   i_no:%d\n   size:%d\n   filetype:%s\n",
           obj_stat.st_ino, obj_stat.st_size,
           obj_stat.st_filetype == 2 ? "directory" : "regular");
    /********  测试代码  ********/
    while (1)
        ;
    return 0;
}

Guess you like

Origin blog.csdn.net/kanshanxd/article/details/132521696