Lab9: file system
Large files
(1). 在fs.h中添加宏定义
#define NDIRECT 11
#define NINDIRECT (BSIZE / sizeof(uint))
#define NDINDIRECT ((BSIZE / sizeof(uint)) * (BSIZE / sizeof(uint)))
#define MAXFILE (NDIRECT + NINDIRECT + NDINDIRECT)
#define NADDR_PER_BLOCK (BSIZE / sizeof(uint)) // 一个块中的地址数量
(2). 由于NDIRECT
定义改变,其中一个直接块变为了二级间接块,需要修改inode结构体中addrs
元素数量
// fs.h
struct dinode {
...
uint addrs[NDIRECT + 2]; // Data block addresses
};
// file.h
struct inode {
...
uint addrs[NDIRECT + 2];
};
(3). 修改bmap
支持二级索引
static uint
bmap(struct inode *ip, uint bn)
{
uint addr, *a;
struct buf *bp;
if(bn < NDIRECT){
...
}
bn -= NDIRECT;
if(bn < NINDIRECT){
...
}
bn -= NINDIRECT;
// 二级间接块的情况
if(bn < NDINDIRECT) {
int level2_idx = bn / NADDR_PER_BLOCK; // 要查找的块号位于二级间接块中的位置
int level1_idx = bn % NADDR_PER_BLOCK; // 要查找的块号位于一级间接块中的位置
// 读出二级间接块
if((addr = ip->addrs[NDIRECT + 1]) == 0)
ip->addrs[NDIRECT + 1] = addr = balloc(ip->dev);
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
if((addr = a[level2_idx]) == 0) {
a[level2_idx] = addr = balloc(ip->dev);
// 更改了当前块的内容,标记以供后续写回磁盘
log_write(bp);
}
brelse(bp);
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
if((addr = a[level1_idx]) == 0) {
a[level1_idx] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
return addr;
}
panic("bmap: out of range");
}
(4). 修改itrunc
释放所有块
void
itrunc(struct inode *ip)
{
int i, j;
struct buf *bp;
uint *a;
for(i = 0; i < NDIRECT; i++){
...
}
if(ip->addrs[NDIRECT]){
...
}
struct buf* bp1;
uint* a1;
if(ip->addrs[NDIRECT + 1]) {
bp = bread(ip->dev, ip->addrs[NDIRECT + 1]);
a = (uint*)bp->data;
for(i = 0; i < NADDR_PER_BLOCK; i++) {
// 每个一级间接块的操作都类似于上面的
// if(ip->addrs[NDIRECT])中的内容
if(a[i]) {
bp1 = bread(ip->dev, a[i]);
a1 = (uint*)bp1->data;
for(j = 0; j < NADDR_PER_BLOCK; j++) {
if(a1[j])
bfree(ip->dev, a1[j]);
}
brelse(bp1);
bfree(ip->dev, a[i]);
}
}
brelse(bp);
bfree(ip->dev, ip->addrs[NDIRECT + 1]);
ip->addrs[NDIRECT + 1] = 0;
}
ip->size = 0;
iupdate(ip);
}
Symbolic links
(1). 配置系统调用的常规操作,如在user/usys.pl、user/user.h中添加一个条目,在kernel/syscall.c、kernel/syscall.h中添加相关内容
(2). 添加提示中的相关定义,T_SYMLINK
以及O_NOFOLLOW
// fcntl.h
#define O_NOFOLLOW 0x004
// stat.h
#define T_SYMLINK 4
(3). 在kernel/sysfile.c中实现sys_symlink
,这里需要注意的是create
返回已加锁的inode,此外iunlockput
既对inode解锁,还将其引用计数减1,计数为0时回收此inode
uint64
sys_symlink(void) {
char target[MAXPATH], path[MAXPATH];
struct inode* ip_path;
if(argstr(0, target, MAXPATH) < 0 || argstr(1, path, MAXPATH) < 0) {
return -1;
}
begin_op();
// 分配一个inode结点,create返回锁定的inode
ip_path = create(path, T_SYMLINK, 0, 0);
if(ip_path == 0) {
end_op();
return -1;
}
// 向inode数据块中写入target路径
if(writei(ip_path, 0, (uint64)target, 0, MAXPATH) < MAXPATH) {
iunlockput(ip_path);
end_op();
return -1;
}
iunlockput(ip_path);
end_op();
return 0;
}
(4). 修改sys_open
支持打开符号链接
uint64
sys_open(void)
{
...
if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){
...
}
// 处理符号链接
if(ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)) {
// 若符号链接指向的仍然是符号链接,则递归的跟随它
// 直到找到真正指向的文件
// 但深度不能超过MAX_SYMLINK_DEPTH
for(int i = 0; i < MAX_SYMLINK_DEPTH; ++i) {
// 读出符号链接指向的路径
if(readi(ip, 0, (uint64)path, 0, MAXPATH) != MAXPATH) {
iunlockput(ip);
end_op();
return -1;
}
iunlockput(ip);
ip = namei(path);
if(ip == 0) {
end_op();
return -1;
}
ilock(ip);
if(ip->type != T_SYMLINK)
break;
}
// 超过最大允许深度后仍然为符号链接,则返回错误
if(ip->type == T_SYMLINK) {
iunlockput(ip);
end_op();
return -1;
}
}
if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
...
}
...
return fd;
}