【MIT 6.S081】Lab9: file system


这个lab难度不大,主要理解file system中几个较高层的函数就可以做
笔者用时约3h

Large files

这一部分要求我们扩容xv6文件系统能够支持的最大文件大小,在我们修改代码之前,xv6的文件系统限制为12个直接块和1个一级间接块,最大文件大小为(12+256)*1024字节。
我们需要实现的是,将1个直接块替换为1个二级间接块,其中,二级间接块的意思是,该块中存储256个一级简接块的块号,也就是说,最大文件大小变为(11+256+256*256)*1024字节。
具体实现上,首先我们在fs.h文件中修改并添加一些宏定义(这一步非常重要,因为一开始的宏定义不仅仅在我们需要修改的函数中使用,还在其他地方有使用,最简单的方法就是根据其定义修改其值),如下所示。其中,NDIRECT表示直接块数量,NINDIRECT1表示一级间接块可以表示的块号数量(256),NINDIRECT2表示二级间接块可以表示的块号数量(256*256),NINDIRECT表示所有间接块可以表示的块号数量。

#define NDIRECT 11
#define NINDIRECT1 (BSIZE / sizeof(uint))
#define NINDIRECT2 (NINDIRECT1 * NINDIRECT1)
#define NINDIRECT (NINDIRECT1 + NINDIRECT2)

然后需要修改dinode结构体(定义在kernel/fs.h:34)中addrs数组的大小以及inode结构体(定义在kernel/file.h:17)中addrs数组的大小。

// fs.h
// 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)
  uint addrs[NDIRECT+2];   // Data block addresses
};
// file.h
// in-memory copy of an inode
struct inode {
  uint dev;           // Device number
  uint inum;          // Inode number
  int ref;            // Reference count
  struct sleeplock lock; // protects everything below here
  int valid;          // inode has been read from disk?

  short type;         // copy of disk inode
  short major;
  short minor;
  short nlink;
  uint size;
  uint addrs[NDIRECT+2];
};

然后根据实验文档,我们修改bmap函数(定义在kernel/fs.c:378),参考一级间接块的处理方案,首先需要判断当前请求的文件块是第几块,如果是二级间接块的话,那么需要获取两个偏移量,分别是二级间接块的偏移量(bn / NINDIRECT1)和该偏移量所指向一级间接块的偏移量(bn % NINDIRECT1)。然后先看二级间接块是否存在,不存在则分配,并跑到对应的一级间接块中,查看一级间接块是否存在,不存在则分配,最后跑到目标块中,查看目标块是否存在,不存在则分配。

static uint
bmap(struct inode *ip, uint bn)
{
  uint addr, *a;
  struct buf *bp;

  if(bn < NDIRECT){
    if((addr = ip->addrs[bn]) == 0)
      ip->addrs[bn] = addr = balloc(ip->dev);
    return addr;
  }
  bn -= NDIRECT;

  if(bn < NINDIRECT1){
    // Load indirect block, allocating if necessary.
    if((addr = ip->addrs[NDIRECT]) == 0)
      ip->addrs[NDIRECT] = addr = balloc(ip->dev);
    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    if((addr = a[bn]) == 0){
      a[bn] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);
    return addr;
  }
  bn -= NINDIRECT1;

  if (bn < NINDIRECT2) {
    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[bn / NINDIRECT1]) == 0) {
      a[bn / NINDIRECT1] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);
    bp = bread(ip->dev, addr);
    a = (uint*) bp->data;
    if ((addr = a[bn % NINDIRECT1]) == 0) {
      a[bn % NINDIRECT1] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);
    return addr;
  }

  panic("bmap: out of range");
}

同样的,我们需要修改itrunc函数(定义在kernel/fs.c:431),比较简单。

void
itrunc(struct inode *ip)
{
  int i, j, k;
  struct buf *bp, *bp2;
  uint *a, *a2;

  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 < NINDIRECT1; 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 < NINDIRECT1; j++){
      if (a[j]) {
        bp2 = bread(ip->dev, a[j]);
        a2 = (uint*)bp2->data;
        for(k = 0; k < NINDIRECT1; k++)
          if(a2[k])
            bfree(ip->dev, a2[k]);
        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);
}

Symbolic links

这一部分要求我们为xv6实现添加符号链接的系统调用,那么首先我们需要理解符号链接是什么。符号链接,也叫做软链接,可以理解为windows上面的快捷方式,只是原始文件或文件夹的别名,访问或者修改符号链接会直接导向指向的文件或文件夹。
根据文档提示,我们可以将原始文件的路径存放在链接文件的block中,可以理解为软链接文件中存放着原始文件的路径。
具体实现上,根据文档提示,首先我们在kernel/fcntl.h文件中添加以下文件权限码(与O_RDONLY等类似),当该文件码被指定时,直接将软链接视为一个文件打开,而不往后寻找其真正指向。

#define O_NOFOLLOW 0x004

然后,在kernel/stat.h文件中添加以下文件类型,表示文件为软链接。

#define T_SYMLINK 4

在此之后,我们在kernel/sysfile.c文件中添加sys_symlink函数,如下所示(此处省略系统调用的添加方法,详细见系统调用实验Lab2)。函数中,我们首先从用户空间获取path字符串和target字符串,分别表示软链接文件的路径与目标文件(也就是源文件)的路径。然后,我们需要用begin_op函数开启一个事务(与日志有关),并调用create函数来创建这个链接文件,指定TYPET_SYMLINK。最后调用writei函数将target字符串也就是源文件路径写入该链接文件中即可。注意在退出函数时需要将ip(指向该文件inode的指针)所指向的inode释放,并调用end_op函数将事务提交。

uint64 
sys_symlink(void)
{
  char path[MAXPATH], target[MAXPATH];
  struct inode* ip;

  if (argstr(0, target, MAXPATH) < 0 || argstr(1, path, MAXPATH) < 0)
    return -1;
  
  begin_op();
  if ((ip = create(path, T_SYMLINK, 0, 0)) == 0) {
    end_op();
    return -1;
  }

  if (writei(ip, 0, (uint64)target, 0, MAXPATH) < MAXPATH) {
    iunlockput(ip);
    end_op();
    return -1;
  }
  
  iunlockput(ip);
  end_op();
  return 0;
}

最后,我们需要在sys_open函数中添加打开软链接文件的处理代码(在获取该路径对应的inode指针之后),主循环以文件的type与open的打开权限是否为O_NOFOLLOW作为循环条件,设置一个循环深度,如果超过10次则跳出循环(防止循环软链接),在循环中,读取inode中的内容(长度为MAXPATH),将ip设置为新文件的inode指针(注意需要将原始ip所指向的inode释放)。这样子当跳出循环后,ip就指向我们最终想要打开文件的inode了。

  while (ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)) {
    if (depth ++ >= 10) {
      iunlockput(ip);
      end_op();
      return -1;
    }
    if (readi(ip, 0, (uint64)path, 0, MAXPATH) < MAXPATH) {
      iunlockput(ip);
      end_op();
      return -1;
    }
    iunlockput(ip);
    if ((ip = namei(path)) == 0) {
      end_op();
      return -1;
    }
    ilock(ip);
  }
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值