# Large files

增加对大文件的支持。原本系统中的一个目录表只有 12 个 direct inode 和 1 个 indirect inode,可存储的最大容量为 12+256 个 blocks,增加双重 indirect inode 后可扩容至 11+256+256*256 个 blocks。

原本一个 inode 目录支持 12 个直接 data 和一个 indirect 指针,为了给双重指针留出位置,需要减少一个直接 data。

//kernel/fs.h
#define NDIRECT 11 //-1
#define NINDIRECT (BSIZE / sizeof(uint))
#define DOUBLEDIRECT (NINDIRECT * NINDIRECT) // 双重指针支持的最大 size
#define MAXFILE (DOUBLEDIRECT + NINDIRECT + NDIRECT) // 汇总一个文件的最大 size

修改相应结构体:

// fs.h
// 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  +1 -> +2
};
// file.h
// 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]; // +1 -> +2
};

修改 bmap 映射函数和 itunc 清理函数:

// fs.c
static uint
bmap(struct inode *ip, uint bn)
{
  uint addr, *a, *b;
  struct buf *bp, *bp2;
  if(bn < NDIRECT){// 直接映射
	// ...
  }
  bn -= NDIRECT;// 直接映射的 11 个 block 填充完毕
  if(bn < NINDIRECT){// 一级指针映射
	// ...
  }
  bn -= NINDIRECT;// 一级映射的 256 个 block 填充完毕
  if(bn<DOUBLEDIRECT){// 二级指针映射
    //middle layer
    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);
    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);
    //end layer
    bp2=bread(ip->dev,addr);
    b=(uint*)bp2->data;// 二级指针表
    if((addr=b[bn%NINDIRECT])==0){// 存到二级指针表中对应的哪一个块
      addr = balloc(ip->dev);
      if(addr){
        b[bn%NINDIRECT]=addr;
        log_write(bp2);
      }
    }
    brelse(bp2);
    return addr;
  }
  panic("bmap: out of range");
}
void
itrunc(struct inode *ip)
{
  int i, j;
  struct buf *bp,*bp2;
  uint *a,*b;
  for(i = 0; i < NDIRECT; i++){
	// ...
  }
  if(ip->addrs[NDIRECT]){
 	// ...
  }
  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]);
        b = (uint*)bp2->data;
        for(int k=0;k<NINDIRECT;k++){// 获取指向的 block
          if(b[k])
            bfree(ip->dev, b[k]);
        }
        brelse(bp2);
      }
    }
    brelse(bp);
    bfree(ip->dev, ip->addrs[NDIRECT+1]);
    ip->addrs[NDIRECT+1] = 0;
  }
  ip->size = 0;
  iupdate(ip);
}

# Symbolic links

增加软链接功能,例如执行 symlink (‘a’,‘b’) 后,打开’b’的指令会实际打开’a’。

增加系统调用的方法与之前类似,修改 syscall 列表,usys.pl,MakeFile(增加 symlinktest 而非 symlink)等。

按照提示在 fcntl.h 中增加标志位,0x800 与前面的标志进行 “或” 运算时不冲突,不会覆盖其它标志位。

#define O_RDONLY  0x000
#define O_WRONLY  0x001
#define O_RDWR    0x002
#define O_CREATE  0x200
#define O_TRUNC   0x400
#define O_NOFOLLOW 0x800 //new added

主要是实现软连接函数和修改 open 文件的逻辑:

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){//already exist
    end_op();
    return -1;
  }
  ip=create(path,T_SYMLINK,0,0);// 给 symlink 分配一个 inode
  if(ip==0){
    end_op();
    return -1;
  }
  if(writei(ip,0,(uint64)target,0,MAXPATH)<0){ // 将目标地址写入新分配的 inode
    iunlockput(ip);
    end_op();
    return -1;
  }
  iunlockput(ip);
  end_op();
  return 0;
}
uint64
sys_open(void)
{
  char path[MAXPATH];
  int fd, omode;
  struct file *f;
  struct inode *ip;
  int n;
  argint(1, &omode);
  if((n = argstr(0, path, MAXPATH)) < 0)
    return -1;
  begin_op();
  if(omode & O_CREATE){
    ip = create(path, T_FILE, 0, 0);
    if(ip == 0){
      end_op();
      return -1;
    }
  } else {//
    int depth=0;// 递归深度
    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;
    }
    while(ip->type==T_SYMLINK && !(omode & O_NOFOLLOW)){// 是软链接
      if(readi(ip,0,(uint64)path,0,MAXPATH)<0){// 读取存储的路径
        iunlockput(ip);
        end_op();
        return -1;
      }
      iunlockput(ip);
      if((ip = namei(path)) == 0){// 获取路径对应的 inode
        end_op();
        return -1;
      }
      ilock(ip);
      if(++depth>10){// 数值随意取,能防止递归自环的合理值
        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;
}

# Test

更新于