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:
第一个实验是让我们通过改变inode的映射机制, 使得一个文件, 即一个struct inode, 可以占据更大的大小. 我们需要将单层映射改写为双层映射. 每个文件由原来最多占据 12+ 256个data blocks改写为最多占据 11 + 256 + 256* 256个data blocks, 大大的增加了文件可用大小.
// kernel/fs.c
// note: NDIRECT=12
// 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+1]; // Data block addresses
};
// kernel/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]; // NDIRECT+1 -> NDIRECT+2
};
修改 bmap(获取 inode 中第 bn 个块的块号)和 itrunc(释放该 inode 所使用的所有数据块),让其能够识别二级索引。(基本上和复制粘贴一致,只是在查出一级块号后,需将一级块中的数据读入,然后再次查询)
// kernel/fs.c
// Return the disk block address of the nth block in inode ip.
// If there is no such block, bmap allocates one.
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){ // singly-indirect
// 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 < NINDIRECT * NINDIRECT) { // doubly-indirect
// Load indirect block, allocating if necessary.
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[bn/NINDIRECT]) == 0){
a[bn/NINDIRECT] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
bn %= NINDIRECT;
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;
}
panic("bmap: out of range");
}
// Truncate inode (discard contents).
// Caller must hold ip->lock.
void
itrunc(struct inode *ip)
{
int i, j;
struct buf *bp;
uint *a;
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]) {
struct buf *bp2 = bread(ip->dev, a[j]);
uint *a2 = (uint*)bp2->data;
for(int 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
这个实验是要实现类似linux上的symlink软链接功能, 比如symlink(‘a’, ‘b’)后, open(‘b’)这个指令会实际上索引到文件’a’所对应的文件data block.
首先我们按常规操作, 把一个新的系统调用sys_symlink的代号, 跳转函数等等设立好. 然后我们直接开始看sys_symlink的实现: 我们把其对应的inode的type设为T_SYMLINK, 然后在其data block的[0, MAXPATH]的范围里写上所要链接的target path的路径.
# kernel/sysfile.c
int sys_symlink(char *target, char *path) {
char kpath[MAXPATH], ktarget[MAXPATH];
memset(kpath, 0, MAXPATH);
memset(ktarget, 0, MAXPATH);
struct inode *ip;
int n, r;
if((n = argstr(0, ktarget, MAXPATH)) < 0)
return -1;
if ((n = argstr(1, kpath, MAXPATH)) < 0)
return -1;
int ret = 0;
begin_op();
// 这个软链接已经存在了
if((ip = namei(kpath)) != 0){
// symlink already exists
ret = -1;
goto final;
}
// 为这个软链接allocate一个新的inode
ip = create(kpath, T_SYMLINK, 0, 0);
if(ip == 0){
ret = -1;
goto final;
}
// 把target path写入这个软链接inode的数据[0, MAXPATH]位置内
if ((r = writei(ip, 0, (uint64)ktarget, 0, MAXPATH)) < 0)
ret = -1;
iunlockput(ip);
final:
end_op();
return ret;
}
然后在sys_open这个函数里, 如果传入的path是一个软链接的话, 我们需要为用户递归去"寻址", 直到找到第一个不是软链接的path (或者递归层数太高, 默认陷入了死循环, 直接返回-1)
# kernel/sysfile.c
uint64
sys_open(void)
{
... 省略, 和之前源码一样 ...
if(omode & O_CREATE){
... 省略, 和之前源码一样 ...
}
// 是软链接且O_NOFOLLOW没被设立起来
int depth = 0;
while (ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)) {
char ktarget[MAXPATH];
memset(ktarget, 0, MAXPATH);
// 从软链接的inode的[0, MAXPATH]读出它所对应的target path
if ((r = readi(ip, 0, (uint64)ktarget, 0, MAXPATH)) < 0) {
iunlockput(ip);
end_op();
return -1;
}
iunlockput(ip);
if((ip = namei(ktarget)) == 0){ // target path 不存在
end_op();
return -1;
}
ilock(ip);
depth++;
if (depth > 10) {
// maybe form a cycle 默认死循环
iunlockput(ip);
end_op();
return -1;
}
}
... 省略, 和之前源码一样 ...
}
把symlinktest加入Makefile后进行编译, 顺利通过测试.