MIT 6.S081 Fall 2020 Lab 9

file system

学!

xv6 原本的文件系统中, inode 有 13 个 addrs, 其中前 12 个是直接地址, 指向 data block 地址. 第 13 个是指向的一个 data block 存的不是文件数据, 而是 (最多) 256 个 data block 的地址, 相当于做一个间接的目录. 这样一个文件最大占用 12 + 256 个 data block.

这个 lab 需要做的是加一个二级间接 block (同时减少一个直接地址), 这样能一个文件最大占用 $11 + 256 + 256 \times 256$ 个 data block.

首先在 fs.h 中修改一下大小, 直接的有 11 个, 一个 inode 共存 13 个 addrs. 由于需要二级, 所以这里还定义了一下第 x 个 block 应该存在第一级目录的哪个位置 (DINDIRECT_IDX), 和在第二级目录的哪个位置 (DINDIRECT_OFF).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#define NDIRECT 11
#define NINDIRECT (BSIZE / sizeof(uint))
#define NDINDIRECT (NINDIRECT * NINDIRECT)
#define MAXFILE (NDIRECT + NINDIRECT + NDINDIRECT)

#define DINDIRECT_IDX(x) (x >> 8)
#define DINDIRECT_OFF(x) (x & 0xFF)

// On-disk inode structure
struct dinode {
  ...
  uint addrs[NDIRECT+2];   // Data block addresses
};

注意还有内存中的 struct inode 需要修改一下, 在 file.h:

1
2
3
4
5
// in-memory copy of an inode
struct inode {
  ...
  uint addrs[NDIRECT+2];
};

然后照猫画虎让 bmap 和 itrunc 支持二级目录. 需要注意, 一旦 inode 的 data 被修改了, 就需要调用 log_wirte 使其记录在 log 中. 目录也是一个 inode, 它如果被修改也需要 log_wirte.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

static uint
bmap(struct inode *ip, uint bn)
{
  uint addr, *a, idx, off;

  ...

  bn -= NINDIRECT;

  if(bn < NDINDIRECT){
    // Load double indirect block, allocating if necessary.
    idx = DINDIRECT_IDX(bn);
    off = DINDIRECT_OFF(bn);
    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[idx]) == 0) {
      a[idx] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);
    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    if((addr = a[off]) == 0){
      a[off] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);
    return addr;
  }
  ...
}

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

  ...

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

支持符号链接. 首先添加一个系统调用 symlink. 设计到文件系统的系统调用都要用 begin_opend_op 包括起来. 然后还要注意, create 函数和 namei 函数获取 inode 都会调用 iget, 所以最后还要有 iput. 符号链接文件也是一个文件, 把链接的 target path 存在 data block 中即可.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
uint64
sys_symlink(void)
{
  char target[MAXPATH], path[MAXPATH];
  struct inode *ip;

  if (argstr(0, target, MAXPATH) < 0 || argstr(1, path, MAXPATH) < 0)
    return -1;

  begin_op();

  if ((ip = namei(path)) != 0)
    goto bad;
  if ((ip = create(path, T_SYMLINK, 0, 0)) == 0)
    goto bad;

  if (writei(ip, 0, (uint64)target, 0, MAXPATH) != MAXPATH)
    panic("symlink: writei");
  iunlockput(ip);

  end_op();
  return 0;

 bad:
  end_op();
  return -1;
}

然后修改 open, 以支持打开符号链接的目标文件. 目标文件可能也是符号链接, 需要递归查找. 为了防止成环, 可以限制递归符号链接的深度, 直接不允许超过这个数, 就可以处理环了.

注意几个问题, 首先找 inode 应该在 file 创建之前, 否则异常退出要去处理 file 比较麻烦 (而且实际上也不对). 然后是递归 (循环) 找的时候特别注意锁的获取和释放.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
uint64
sys_open(void)
{
  ...
  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);

    for (symcnt = 0; ip->type == T_SYMLINK &&
         symcnt < MAXSYMDEP && !(omode & O_NOFOLLOW); symcnt++) {
      if (readi(ip, 0, (uint64)path, 0, MAXPATH) != MAXPATH)
        panic("open: readi");
      iunlockput(ip);
      if ((ip = namei(path)) == 0) {
        end_op();
        return -1;
      }
      ilock(ip);
    }
    if (symcnt >= MAXSYMDEP) {
      iunlockput(ip);
      end_op();
      return -1;
    }
    ...
  }
  ...
}

(一开始没有想明白, 找 inode 放在 file 创建了之后, 写了一个下午, 总是出现各种各样的错误, 难受)

这个简单, 和二级没什么区别, 多加一级就是了, 二级会了三级就会了, 不写了.

(发现对待选做的态度是难的不想写简单的不愿意写)