实验一 bigfile 链接到标题

其实这个实验的要求并不难,主要是要理解xv6的文件系统的实现细节。
几个我认为的重点:

  • buffer层的函数细节要弄清楚
  • log层的函数使用要弄清楚
  • 索引节点的结构和使用要弄清楚
  • 一点define的细节要弄清楚 代码:
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) {
    // Load indirect block, allocating if necessary.
    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); // 读一个buff,addr指磁盘地址,这里是读一个间接块
    a = (uint *)bp->data;
    // 一个uint占4字节,一个block的地址是4字节
    // a就是一个uint数组,a[0]是第一个block的地址,a[1]是第二个block的地址
    if ((addr = a[bn / NINDIRECT]) == 0) {
      addr = balloc(ip->dev);
      if (addr) {
        a[bn / NINDIRECT] = addr;
        log_write(bp);
      }
    }
    brelse(bp);
    bp = bread(ip->dev, addr); // 读一个buff,addr指2级间接块的地址
    a = (uint *)bp->data;
    if ((addr = a[bn % NINDIRECT]) == 0) {
      addr = balloc(ip->dev);
      if (addr) {
        a[bn % NINDIRECT] = addr;
        log_write(bp);
      }
    }
    brelse(bp);
    return addr;
  }
  panic("bmap: out of range");
}

实验二 symlink 链接到标题

这个实验主要是多个细节点要想好
我现在其实也没有想太好,虽然弄出来了,但是感觉有点乱,主要是因为我没有把整个文件系统的实现细节弄清楚,所以在实现这个功能的时候有点乱,感觉有点像在拼图一样,虽然最后拼出来了,但是感觉不是很清晰。
整体sys_symlink的实现可以参照create的实现,主要是要弄清楚create的实现细节,尤其是普通文件的实现细节,其中得自己注意inode的创建,日志(log)的处理,文件夹操作(dirlookup,namex)和已有软连接文件重复创建。这好些细节。
然后就是sys_open的代码增加,注意不要搞个死循环。

我应该花时间梳理一下整个文件系统的实现细节,看看函数的设计。最好画个图。现在没时间了,先把这个实验写完了再说。

symlink的实现 链接到标题

uint64 sys_symlink(void) {
  // printf("symlink loaded!\n\n");
  // 这里DIRSIZE就是文件名字的长度
  char name[DIRSIZ], new[MAXPATH], old[MAXPATH];
  struct inode *dp, *ip;

  // 读取连接的路径,这里old指需要存在data里的路径
  if (argstr(0, old, MAXPATH) < 0 || argstr(1, new, MAXPATH) < 0)
    return -1;

  // 成功读取参数后开始事务,只用写new路径里的inode
  // 甚至可以根本不用看old里的inode,直接把old路径存到new路径的inode里就行了
  begin_op();
  // 现在dp指向new路径的父目录了,接下来在这个目录下创建一个新的inode,类型为T_SYMLINK
  if ((dp = nameiparent(new, name)) == 0)
    goto bad;
  ilock(dp);

  if ((ip = dirlookup(dp, name, 0)) != 0) {
    iunlockput(dp);
    ilock(ip);
    if (ip->type == T_SYMLINK) {
      iunlockput(ip);
      end_op();
      return 0;
    }
    iunlockput(ip);
    goto bad;
  }

  if ((ip = ialloc(dp->dev, T_SYMLINK)) == 0) {
    iunlockput(dp);
    goto bad;
  }

  ilock(ip);
  ip->nlink = 1;
  ip->major = 0;
  ip->minor = 0;
  int oldlen = strlen(old) + 1;
  if (oldlen > MAXPATH)
    oldlen = MAXPATH;
  writei(ip, 0, (uint64)old, 0,
         oldlen); // 这里可能写错了,等会看看是不是usersrc
  if (dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0) {
    iunlockput(dp);
    itrunc(ip);
    iunlockput(ip);
    goto bad;
  }
  iunlockput(dp);
  iunlockput(ip);

  end_op();
  return 0;
bad:
  end_op();
  return -1;
}

open的修改 链接到标题

这里是不是不需要end_op()这个,毕竟循环找连接的目标文件并不需要写,真正的写放在symlink里了,open里只是读而已。

  if (!(omode & O_NOFOLLOW)) {
    int n = 10;
    while (n--) {
      if ((ip = namei(path)) == 0) {
        break;
      }
      ilock(ip);
      if (ip->type != T_SYMLINK) {
        iunlockput(ip);
        break;
      }
      if (readi(ip, 0, (uint64)path, 0, MAXPATH) == -1) {
        iunlockput(ip);
        return -1;
      }
      iunlockput(ip);
    }
    if (n == -1) {
      end_op();
      return -1;
    }

  }

结果 链接到标题

结果