6.1810: Operating System Engineering 2023 <Lab8 fs: File system>

一、本节任务

二、Lab8: file system

在这一节,我们将为 xv6 的文件系统加入大文件和符号链接。

2.1 Large files (moderate) 

这个部分需要我们增加 xv6 文件的大小上限,由于 inode 结构体中有 12 个直接映射项,1 个一级间接映射项,所以 xv6 文件系统中的最大文件大小为 (12 + (1024B / 4B)) 个块,即 268 个块,在 xv6 中,每个块大小为 1024B。而通过再增加一个二级间接映射项可以让我们的文件大小增加到 (11 + 256 + 256*256) = 65803 个块,而下面我们就是要实现这个二级间接映射项。

首先修改 fs.h 文件中的宏和 dinode 中 addrs 的大小,以及 file.h 文件中 inode 结构体中 addrs 的大小也要同步修改过来。这样做的目的是减少一个直接映射项,增加一个二级间接映射项。

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

// 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
};
// 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];
};

然后再修改 fs.c 文件中的 bmap 和 itrunc 函数,bmap 函数的作用是通过逻辑块号 bn 来到 addrs 数组中找到其在磁盘上的真实块号;而 itrunc 函数的作用是将 inode 的 addrs 清空。直接映射项的真实块号就是 addrs[bn],而一级间接映射项的真实块号还需要从 addrs[NDIRECT] 指向的块上寻找真实块号,二级间接映射项需要从 addrs[NDIRECT+1] 指向的块上先找到对应一级间接映射项,然后再找到真实块号。

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

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

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

  if(bn < NINDIRECT * NINDIRECT){
    if((addr = ip->addrs[NDIRECT+1]) == 0){
      addr = balloc(ip->dev);
      if(addr == 0)
        return 0;
      ip->addrs[NDIRECT+1] = addr;
    }
    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    if((addr = a[bn/NINDIRECT]) == 0){
      addr = balloc(ip->dev);
      if(addr == 0)
        return 0;
      a[bn/NINDIRECT] = addr;
      log_write(bp);
    }
    brelse(bp);

    bn = bn % NINDIRECT;
    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    if((addr = a[bn]) == 0){
      addr = balloc(ip->dev);
      if(addr){
        a[bn] = addr;
        log_write(bp);
      }
    }
    brelse(bp);
    return addr;
  }

  panic("bmap: out of range");
}
void
itrunc(struct inode *ip)
{
  int i, j;
  struct buf *bp;
  struct buf *bp1;
  uint *a;
  uint *a1;

  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(i = 0; i < NINDIRECT; i++){
      if(a[i]){
        bp1 = bread(ip->dev, a[i]);
        a1 = (uint*)bp1->data;
        for(j = 0; j < NINDIRECT; j++){
          if(a1[j])
            bfree(ip->dev, a1[j]);
        }
        brelse(bp1);
        bfree(ip->dev, a[i]);
        a[i] = 0;
      }
    }
    brelse(bp);
    bfree(ip->dev, ip->addrs[NDIRECT+1]);
    ip->addrs[NDIRECT+1] = 0;
  }

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

最后通过测试。

2.2 Symbolic links (moderate)

首先创建一个新系统调用 symlink,怎么添加系统调用在之前的 lab 中已经做过了,这里就不多赘述了。

下面是 symlink 系统调用的实现,首先使用 create 创建一个新文件,文件的类型为 T_SYMLINK,然后使用 writei 向文件的数据块写入 target 路径,这样使用 open 系统调用时如果发现文件是链接文件,则从文件的数据块中读取 target 文件的路径。最后记得使用 iunlockput(ip) 释放 inode 的锁和引用:

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

  if((n = argstr(0, target, MAXPATH)) < 0 || argstr(1, path, MAXPATH) < 0){
    return -1;
  }

  begin_op();
  ip = create(path, T_SYMLINK, 0, 0);
  if(ip == 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;
}

然后需要修改 open 系统调用,当识别到文件的类型为 T_SYMLINK 的时候,并且模式不为 O_NOFOLLOW,则循环地通过链接路径找到被链接的文件,若被链接的文件就是当前文件本身,形成的环路,或者链接的深度超过了 10,则报错:

uint64
sys_open(void)
{
  char path[MAXPATH];
  char target[MAXPATH];
  int fd, omode;
  struct file *f;
  struct inode *ip;
  struct inode *tip;
  int n, depth;

  argint(1, &omode);
  if((n = argstr(0, path, MAXPATH)) < 0)
    return -1;

  begin_op();

  if(omode & O_CREATE){
    ip = create(path, T_FILE, 0, 0);
    if(ip == 0){
      end_op();
      return -1;
    }
  } else {
    if((ip = namei(path)) == 0){
      end_op();
      return -1;
    }
    ilock(ip);
    if(ip->type == T_DIR && omode != O_RDONLY){
      iunlockput(ip);
      end_op();
      return -1;
    }
  }

  depth = 0;
  while(ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)){
    if(readi(ip, 0, (uint64)target, 0, MAXPATH) != MAXPATH)
      panic("open symlink: readi");

    // the links form a cycle
    if(strncmp(target, path, n) == 0){
      iunlockput(ip);
      end_op();
      return -1;
    }

    // target file does not exist
    if((tip = namei(target)) == 0){
      iunlockput(ip);
      end_op();
      return -1;
    }

    iunlock(ip);
    ilock(tip);
    ip = tip;

    depth++;
    if(depth >= 10){
      iunlockput(ip);
      end_op();
      return -1;
    }
  }
.....
.....

最后通过所有测试。