1. 程式人生 > 其它 >9. Lab: file system

9. Lab: file system

https://pdos.csail.mit.edu/6.S081/2021/labs/fs.html

1. Large files (moderate)

1.1 要求

Modify bmap() so that it implements a doubly-indirect block, in addition to direct blocks and a singly-indirect block. You'll have to have only 11 direct blocks, rather than 12, to make room for your new doubly-indirect block; you're not allowed to change the size of an on-disk inode. The first 11 elements of ip->addrs[] should be direct blocks; the 12th should be a singly-indirect block (just like the current one); the 13th should be your new doubly-indirect block. You are done with this exercise when bigfile writes 65803 blocks and usertests runs successfully:

要求擴大 xv6 中檔案大小上限。目前 xv6 檔案限制為 268 個塊,或 268*BSIZE 位元組(在 xv6 中 BSIZE 為 1024)。 因為一個 xv6 inode 包含 12 個“直接”塊號和一個“單獨間接”塊號,這是指一個塊最多可以容納 256 個塊號,總共 12+256=268 塊。
因此需要更改 xv6 檔案系統程式碼以支援每個 inode 中的“雙重間接”塊,其中包含 256 個單間接塊地址,每個塊最多可包含 256 個數據塊地址。 結果將是一個檔案將能夠包含多達 65803 個塊,或 256*256+256+11 個塊(11 個而不是 12 個,因為我們將為雙間接塊犧牲一個直接塊號)
原來的結構如圖下:

修改後的結構應當如下:

可以看到,有點類似多級頁表的思路。

1.2 分析

要注意的點如下:

  • 因為 inode.addrs 固定為13 個,因此需要減少一個 DirectBlockDoubly-Indirect 使用
  • bmap 的函式簽名如右:bmap(struct inode *ip, uint bn)bn 為 block number。需要注意,當 bn 大於 256 + 11 的時候,需要開始在 Doubly-Indirect 中找到合適的 Direct-Block,可以通過 bn=bn-256-11 去掉偏移,然後 bn/256 得到在第幾塊 Singly-Indirectbn%256 得到目標塊在 Singly-Indirect
    中的偏移。
  • 通過 bread 獲取 buf 之後,修改 buf 後需要通過 log_write 操作寫入更新。

1.3 實現

  • 修改直接塊數量,注意需要修改 dinodeinodeaddrs 大小
#define NDOUBLEINDIRECT (NINDIRECT * NINDIRECT)
#define NDIRECT 11
// 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];
};
  • 分配間接塊,此處需要注意,只有當用到時才分配塊,其次修改塊時需要記得 log_write
static uint
bmap(struct inode *ip, uint bn)
{
  uint addr, *a;
  struct buf *bp, *inbp;
  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;
  // load doublely-indirect block
  if(bn < NDOUBLEINDIRECT){
    if((addr = ip->addrs[NDIRECT + 1]) == 0) 
      ip->addrs[NDIRECT + 1] = addr = balloc(ip->dev); // alloc doublely-indirect block

    // get indirect block index
    inbp = bread(ip->dev, addr);
    a = (uint*)(inbp->data);

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

  panic("bmap: out of range");
}
  • 釋放塊
// Truncate inode (discard contents).
// Caller must hold ip->lock.
void
itrunc(struct inode *ip)
{
  int i, j, k;
  struct buf *bp, *inbp;
  uint *a;
  uint *tmp;

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

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

2. Symbolic links(moderate)

2.1 要求

You will implement the symlink(char *target, char *path) system call, which creates a new symbolic link at path that refers to file named by target. For further information, see the man page symlink. To test, add symlinktest to the Makefile and run it. Your solution is complete when the tests produce the following output (including usertests succeeding).

實現 symlink 介面,比較簡單,與 link 的區別在於,symlink 會建立檔案,而 link 介面只是增加目標檔案的引用計數,並寫入目錄。

2.2 分析

實現主要有 2 點:

  • 建立連結檔案,通過 sys_symlink 介面建立
  • 訪問連結檔案,通過 sys_open 訪問

2.3 實現

  • 建立檔案

inode 結構中增加 char symlinkpath[128];,用於儲存目標檔案的名字。

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 = namei(path)) == 0){
    ip = create(path, T_SYMLINK, 0, 0);
    if (ip == 0){
      end_op();
      return -1;
    }
  }else if (ip->type != T_SYMLINK){
    end_op();
    return -1;
  }else{
    ilock(ip);
  }

  memset(ip->symlinkpath, 0, MAXPATH);
  memmove(ip->symlinkpath, target, sizeof(target));
  iunlockput(ip);
  end_op();
  return 0;
}
  • 訪問檔案

需要注意如果有 O_NOFOLLOW 的 flag,則直接訪問連結檔案,而不是訪問 inode.symlinkpath 的檔案。
其次要注意存在連結檔案 連結 連結檔案的操作,有點套娃,比如 a->b,b->c,c->a,此時如果沒有防範措施會無限套娃,因此根據 hints 加了個遞迴層次限制。

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

  if((n = argstr(0, path, MAXPATH)) < 0 || argint(1, &omode) < 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;
    }
  }

  int cnt = 0;
  while(ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)){
    if (cnt >= 10) {
      iunlockput(ip);
      end_op();
      return -1;
    }

    symip = namei(ip->symlinkpath);
    if (symip) {
      cnt++;
      iunlockput(ip);
      ip = symip;
      ilock(ip);
    }
    else {
      break;
    }
  }

  if (cnt == 0 && ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)){
    iunlockput(ip);
    end_op();
    return -1;
  }

  if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){
    iunlockput(ip);
    end_op();
    return -1;
  }

  if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
    if(f)
      fileclose(f);
    iunlockput(ip);
    end_op();
    return -1;
  }

  if(ip->type == T_DEVICE){
    f->type = FD_DEVICE;
    f->major = ip->major;
  } else {
    f->type = FD_INODE;
    f->off = 0;
  }
  f->ip = ip;
  f->readable = !(omode & O_WRONLY);
  f->writable = (omode & O_WRONLY) || (omode & O_RDWR);

  if((omode & O_TRUNC) && ip->type == T_FILE){
    itrunc(ip);
  }

  iunlock(ip);
  end_op();

  return fd;
}