MIT6.S081 File System

Part 1 Large files

增加一个文件的最大大小,通过将inode的一个直接块变成一个二次间接块

fs.c balloc在bitmap中查找unused block,使用bio.c中的bread和brelse来同步访问

when to use log-write? After finishing all writes on the buf.

fs.c/bmap 的作用
在读取inode节点数据的时候(readi, writei),将地址转换为块所在地址。

将原来的直接块中分一个出来作为二次间接块的一级块

// fs.h
#define NDIRECT 11
#define NINDIRECT (BSIZE / sizeof(uint))
#define NDINDIRECT (NINDIRECT * NINDIRECT)  // Double-indirect block
#define MAXFILE (NDIRECT + NINDIRECT + NDINDIRECT)

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

// file.h
// 保证On-dist inode和 内存inode的 addrs 大小一致
// 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中具体实现二次间接块

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 < NDINDIRECT) {
        if ((addr = ip->addrs[NDIRECT + 1]) == 0) {  // 暂未分配一级间接块
            ip->addrs[NDIRECT+1] = addr = balloc(ip->dev);
        }
        bp = bread(ip->dev, addr);  // 一级间接块
        a = (uint *) bp->data;
        int first_index = bn / NINDIRECT;
        if ((addr = a[first_index]) == 0) {  // 暂未分配二级间接块
            a[first_index] = addr = balloc(ip->dev);
            log_write(bp);
        }
        brelse(bp);

        bp = bread(ip->dev, addr);  // 二级间接块内容
        a = (uint *)bp->data;

        int second_index = bn % NINDIRECT;
        if ((addr = a[second_index]) == 0) {  // 暂未分配数据块
            a[second_index] = addr = balloc(ip->dev);
            log_write(bp);
        }
        brelse(bp);
        return addr;
    }

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

void
itrunc(struct inode *ip)
{
  int i, j, k;
  struct buf *bp, *bp2;
  uint *a, *a2;

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

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

运行测试

$bigfile........................................................................................................
wrote 65803 blocks
bigfile done; ok

实现符号连接系统调用

符号链接(软链接、Symbolic link)是一类特殊的文件, 其包含有一条以绝对路径或者相对路径的形式指向其它文件或者目录的引用。
Implement the symlink(target, path) system call to create a new symbolic link at path that refers to target.
Note that target does not need to exist for the system call to succeed.

在path中创建一个包含了target路径的符号连接

增加系统调用symlink
在sysfile.c中实现symlink

uint64
sys_symlink(void)
{
    char target[MAXPATH], path[MAXPATH];
    int n;
    if ((n = argstr(0, target, MAXPATH)) < 0 || argstr(1, path, MAXPATH) < 0)
        return -1;
    // create path directory and file
    struct inode *ip;
    begin_op();
    ip = create((char *)path, T_SYMLINK, 0, 0); // add the path file to the path directory
    if (ip == 0) {
        end_op();
        printf("Path not exist or the file already exists\n");
        return -1;
    }

    // write the string target to the path inode data
    if (writei(ip, 0, (uint64)&target, 0, n) != n) {
        printf("Unable to write inode");
        iunlockput(ip);
        end_op();
        return -1;
    }

    iunlockput(ip);
    end_op();

    return 0;
}

修改sys_open,处理打开的是一个符号连接文件

uint64
sys_open(void) {
    char path[MAXPATH];
    int fd, omode;
    struct file *f;
    struct inode *ip;
    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;
        }
    }

    if ((omode & O_NOFOLLOW) == 0) {  // follow the symbolic link
        int depth = MAXSYMLINK;
        uint inums[MAXSYMLINK];
        int i = 0;
        while (ip->type == T_SYMLINK && depth-- > 0) {
            char target_path[MAXPATH];
            inums[i] = ip->inum;  // record the seen inumber to avoid circle
            int nread;
            if ((nread = readi(ip, 0, (uint64) target_path, 0, MAXPATH)) <= 0) {
                printf("Cannot read the inode content in open()\n");
                iunlockput(ip);
                end_op();
                return -1;
            }
            printf("readi read %d bytes\n", nread);
            iunlockput(ip);
            if ((ip = namei(target_path)) == 0) {
                end_op();
                return -1;
            }

            ilock(ip);
            // check whether there is a ring
            for (int j = 0; j <= i; ++j) {
                if (ip->inum == inums[i]) {
                    printf("Got a circle symbolic link.\n");
                    iunlockput(ip);
                    end_op();
                    return -1;
                }
            }
        }

        if (depth <= 0) {
            printf("Too deep of the symlink\n");
            iunlockput(ip);
            end_op();
            return -1;
        }
    }

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

注意从用户空间获取字符串参数要使用argstr,如果使用argaddr可能会出现kerneltrap(sauce=0xd,load page fault)