Loading

6.S081-2021 file system

Large files

磁盘的最小单位是block,每个block大小为1024字节,而inode的大小为64字节。

所以每个block能够存放16个inode

  • block0 为操作系统的引导区,文件系统不会对这个blcok进行任何修改
  • block1 是super block,用来记录元数据(block大小和总数,inode总数等)
  • block2-32 log block 用于崩溃恢复使用
  • block32-44 用于存放文件的inode
  • block45 bitmap block,标记已使用的block
  • block46及其以后都是用于存放文件的具体数据

fs.h中修改宏定义

NINDIRECT 实际上就是256

// 直接索引只有11个block
#define NDIRECT 11
#define NDOUBLYINDIRECT NINDIRECT*NINDIRECT
#define MAXFILE (NDIRECT + NINDIRECT + NDOUBLYINDIRECT)

// On-disk inode structure
struct dinode {
  short type;           // File type
  short major;          // Major device number (T_DEVICE only)
  short minor;          // Minor device number (T_DEVICE only)
  short nlink;          // Number of links to inode in file system
  uint size;            // Size of file (bytes)
  // 因为改小了NDIRECT增加了一个二级索引
  uint addrs[NDIRECT+1+1];   // Data block addresses
};

要根据二级索引进行查询的话,相当于走了一次一级索引,然后再是直接索引

所以我们需要先求出来输入的bn所对应的一级间接地址为 bn/NINDIRECT

然后根据直接地址 bn%NINDIRECT 找到block的实际物理地址

  // bmap()
  bn -= NINDIRECT;
  if(bn < NDOUBLYINDIRECT){
    uint index1 = bn/NINDIRECT; // 在哪个block
    uint index2 = bn%NINDIRECT; // 偏移量
    if((addr = ip->addrs[NDIRECT+1]) ==0)
      ip->addrs[NDIRECT+1] = addr = balloc(ip->dev);
    bp = bread(ip->dev,addr);
    a = (uint*)bp->data;
    if((addr = a[index1]) == 0){
      a[index1] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);
    bp = bread(ip->dev,addr);
    a = (uint*)bp->data;
    if((addr = a[index2]) == 0){
      a[index2] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);
    return addr;
  }

同时对应 itrunc 中也要释放二级间接地址

// Truncate inode (discard contents).
// Caller must hold ip->lock.
void
itrunc(struct inode *ip)
{
  int i, j;
  struct buf *bp;
  uint *a;

  for(i = 0; i < NDIRECT; i++){
    if(ip->addrs[i]){
      bfree(ip->dev, ip->addrs[i]);
      ip->addrs[i] = 0;
    }
  }

  if(ip->addrs[NDIRECT]){
    bp = bread(ip->dev, ip->addrs[NDIRECT]);
    a = (uint*)bp->data;
    for(j = 0; j < NINDIRECT; j++){
      if(a[j])
        bfree(ip->dev, a[j]);
    }
    brelse(bp);
    bfree(ip->dev, ip->addrs[NDIRECT]);
    ip->addrs[NDIRECT] = 0;
  }
  
  if(ip->addrs[NDIRECT+1]){
    bp = bread(ip->dev,ip->addrs[NDIRECT+1]);
    a = (uint*)bp->data;
    for(j=0;j<NINDIRECT;j++){
      if(a[j]){
        struct buf * bp2 = bread(ip->dev,a[j]);
        uint* b = (uint*)bp2->data;
        for(i=0;i<NINDIRECT;i++){
          if(b[i]){
            bfree(ip->dev,b[i]);
          }
        }
        brelse(bp2);
        bfree(ip->dev,a[j]);
      }
        
    }
    brelse(bp);
    bfree(ip->dev,ip->addrs[NDIRECT+1]);
    ip->addrs[NDIRECT+1] = 0;
  }

  ip->size = 0;
  iupdate(ip);
}

符号链接其实就是创建了一个文件,在文件中写入了指向的目标文件的绝对路径。

访问该符号链接,文件系统会去读取文件里的绝对路径,然后根据这个路径打开相应的文件。

根据 Hints 在user/usys.pl, user/user.h kernel/sysfile.c中添加相应的系统调用

需要注意的是 kernel/fcntl.h 中,添加的 O_NOFOLLOW 不能与之前已有的标志重合,就是他们的1的位置不能一样。

// kernel/fcntl.h
#define O_NOFOLLOW 0x010
// kernel/stat.h
#define T_SYMLINK 4
// kernel/syscall.h
#define SYS_symlink 22
// user/usys.pl
entry("symlink");
// kernek/syscall.c
extern uint64 sys_symlink(void);
....
[SYS_symlink] sys_symlink,

现在我们需要在 sysfile.c 中实现 sys_symlink

因为这是与文件相关的系统调用,所以我们可以翻看 sys_mkdir sys_open 和 sys_link 的实现来查找我们需要的函数

首先我们需要获取参数 target, path

同时需要注意 target ,path 都是文件路径名,在内核中应该转换为相应的 inode

uint64 
sys_symlink(void) 
{
  char target[MAXPATH];
  char path[MAXPATH];
  struct inode *ip;
  
  if(argstr(0,target,MAXPATH) < 0 || argstr(1,path,MAXPATH) < 0){
    return -1;
  }
  begin_op();
  // Look up and return the inode for a path name.
  if((ip = namei(path)) == 0){
      ip = create(path,T_SYMLINK,0,0);
  }
  ilock(ip);
  // 将目标文件的绝对路径写入符号链接文件中
  if (writei(ip, 0, (uint64)target, ip->size, MAXPATH) != MAXPATH) {
    panic("symlink");
  }
  iunlockput(ip);
  end_op();
  return 0;
}

最后在open函数也要添加对符号链接的处理程序

...
// 如果文件类型是符号链接
if (ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)) {
    int threshold = 10;
    while (ip->type == T_SYMLINK && threshold > 0) {
      // 读取指向的文件的路径
      if(readi(ip, 0, (uint64)path, 0, ip->size) != ip->size) {
        iunlockput(ip);
        end_op();
        return -1;
      }
      iunlockput(ip);
      if((ip = namei(path)) == 0) {
        end_op();
        return -1;
      }
      ilock(ip);
      threshold--;
    }
    // 符号链接引用成环了
    if (threshold == 0) {
      iunlockput(ip);
      end_op();
      return -1;
    }
}


if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){
    iunlockput(ip);
    end_op();
    return -1;
}
posted @ 2022-06-05 10:15  AD_milk  阅读(50)  评论(0编辑  收藏  举报