目录

6.S081 lab9 fs

Large files

本关需要为xv6添加对大文件的支持。xv6的 inode 默认使用 12 个直接块指针和 1 个间接块指针(指向一个存储着块指针的数据块),所以xv6支持的最大文件尺寸是12 + 1*256=268个 block。我们需要将一个直接块指针修改为双重间接块指针(执行一个存储着间接块指针的数据块),将xv6的最大文件尺寸扩展到11 + 1*256 + 1*256*256= 65803个 block。

首先我们修改kernel/fs.h中的相关宏定义:

1
2
3
4
#define NDIRECT 11
#define NINDIRECT (BSIZE / sizeof(uint))
#define NDOUBLEINDIRECT ((BSIZE / sizeof(uint)) * (BSIZE / sizeof(uint)))
#define MAXFILE (NDIRECT + NINDIRECT + NDOUBLEINDIRECT)

然后修改dinodeinode中的地址数组定义:

1
uint addrs[NDIRECT+2];

接下来修改bmap函数,该函数用于将一个文件的逻辑块号转换为设备的物理块号,类似于虚实地址转换:

 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
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 < NINDIRECT){
    // 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 -= NINDIRECT;
  if(bn < NDOUBLEINDIRECT){
    // Load double-indirect block, allocating if necessary.
    if((addr = ip->addrs[NDIRECT+1]) == 0)
      ip->addrs[NDIRECT+1] = addr = balloc(ip->dev);
    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    uint level1 = bn / NINDIRECT;
    if((addr = a[level1]) == 0){
      a[level1] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);

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

    return addr;
  }

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

然后修改itrunc函数,在 truncate 文件时,释放我们新添加的双重间接数据块(不要忘了释放指针块本身):

 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
void
itrunc(struct inode *ip)
{
  int i, j, k;
  struct buf *bp, *nbp;
  uint *a, *na;

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

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

运行bigfile测试,看到测试创建了一个 size 为65803个 block 的最大文件。

1
2
3
4
$ bigfile
..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
wrote 65803 blocks
bigfile done; ok

这次我们需要实现一个syscall,用于创建符号连接,符号链接不会增加实际文件inode的 link 数,只是使用路径指向被 link 的文件。

首先,按照之前熟悉的方法添加新的syscall

kernel/stat.h中添加符号文件类型:

1
#define T_SYMLINK 4

kernel/fcntl.h中添加:

1
#define O_NOFOLLOW 0x800

用于标识是要读取符号文件本身还是符号链接指向的文件。

之后我们实现symlink本身:

 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
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 = create(path, T_SYMLINK, 0, 0)) == 0){
    end_op();
    return -1;
  }
  int len = strlen(target);
  // write target path len
  if(writei(ip, 0, (uint64)&len, 0, sizeof(len)) < sizeof(len)) {
    iunlockput(ip);
    end_op();
    return -1;
  }
  // write target path
  if(writei(ip, 0, (uint64)target, sizeof(len), len + 1) < 0) {
    iunlockput(ip);
    end_op();
    return -1;
  }

  iunlockput(ip);
  end_op();

  return 0;
}

首先我们为符号文件创建一个新的inode,然后向其数据区写入指向的目标。在写入目标时,使用了[len, target]的格式,方便我们之后能准确地从文件系统中读取出target

然后,我们需要修改sys_open,使之正确的处理符号文件。

  • Modify the open system call to handle the case where the path refers to a symbolic link. If the file does not exist, open must fail. When a process specifies O_NOFOLLOW in the flags to open, open should open the symlink (and not follow the symbolic link).
  • If the linked file is also a symbolic link, you must recursively follow it until a non-link file is reached. If the links form a cycle, you must return an error code. You may approximate this by returning an error code if the depth of links reaches some threshold (e.g., 10).

当没有设置O_NOFOLLOW时,open调用需要打开符号文件指向的真实文件,如果被符号文件指向的文件也是符号文件,则需要递归查找,指导找到真实文件。为了防止循环引用,当查找次数一定数值时,可以判断失败。

 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
#define MAXSYMLINK 10

if (!(omode & O_NOFOLLOW)) {
  int cnt = MAXSYMLINK;
  int len = 0;
  while (cnt-- > 0) {
    ilock(ip);
    if (ip->type == T_SYMLINK) {
      if (readi(ip, 0, (uint64)&len, 0, sizeof(len)) < sizeof(len)) {
        iunlockput(ip);
        end_op();
        return -1;
      }
      if (readi(ip, 0, (uint64)path, sizeof(len), len + 1) < len + 1) {
        iunlockput(ip);
        end_op();
        return -1;
      }
      iunlockput(ip);
    } else {
      iunlock(ip);
      break;
    }
    if((ip = namei(path)) == 0){
      end_op();
      return -1;
    }
  }
  if (cnt <= 0) {
    end_op();
    return -1;
  }
}