6.S081-2021 file system
Large files
磁盘的最小单位是block,每个block大小为1024字节,而inode的大小为64字节。
所以每个block能够存放16个inode
- block0 为操作系统的引导区,文件系统不会对这个blcok进行任何修改
- block1 是super block,用来记录元数据(block大小和总数,inode总数等)
- block2-32 log block 用于崩溃恢复使用
- block32-44 用于存放文件的inode
- block45 bitmap block,标记已使用的block
- block46及其以后都是用于存放文件的具体数据
在fs.h
中修改宏定义
NINDIRECT 实际上就是256
// 直接索引只有11个block
#define NDIRECT 11
#define NDOUBLYINDIRECT NINDIRECT*NINDIRECT
#define MAXFILE (NDIRECT + NINDIRECT + NDOUBLYINDIRECT)
// 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)
// 因为改小了NDIRECT增加了一个二级索引
uint addrs[NDIRECT+1+1]; // Data block addresses
};
要根据二级索引进行查询的话,相当于走了一次一级索引,然后再是直接索引
所以我们需要先求出来输入的bn所对应的一级间接地址为 bn/NINDIRECT
然后根据直接地址 bn%NINDIRECT
找到block的实际物理地址
// bmap()
bn -= NINDIRECT;
if(bn < NDOUBLYINDIRECT){
uint index1 = bn/NINDIRECT; // 在哪个block
uint index2 = bn%NINDIRECT; // 偏移量
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[index1]) == 0){
a[index1] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
bp = bread(ip->dev,addr);
a = (uint*)bp->data;
if((addr = a[index2]) == 0){
a[index2] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
return addr;
}
同时对应 itrunc
中也要释放二级间接地址
// 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* b = (uint*)bp2->data;
for(i=0;i<NINDIRECT;i++){
if(b[i]){
bfree(ip->dev,b[i]);
}
}
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);
}
Symbolic links
符号链接其实就是创建了一个文件,在文件中写入了指向的目标文件的绝对路径。
访问该符号链接,文件系统会去读取文件里的绝对路径,然后根据这个路径打开相应的文件。
根据 Hints 在user/usys.pl, user/user.h kernel/sysfile.c中添加相应的系统调用
需要注意的是 kernel/fcntl.h 中,添加的 O_NOFOLLOW 不能与之前已有的标志重合,就是他们的1的位置不能一样。
// kernel/fcntl.h
#define O_NOFOLLOW 0x010
// kernel/stat.h
#define T_SYMLINK 4
// kernel/syscall.h
#define SYS_symlink 22
// user/usys.pl
entry("symlink");
// kernek/syscall.c
extern uint64 sys_symlink(void);
....
[SYS_symlink] sys_symlink,
现在我们需要在 sysfile.c 中实现 sys_symlink
因为这是与文件相关的系统调用,所以我们可以翻看 sys_mkdir sys_open 和 sys_link 的实现来查找我们需要的函数
首先我们需要获取参数 target, path
同时需要注意 target ,path 都是文件路径名,在内核中应该转换为相应的 inode
uint64
sys_symlink(void)
{
char target[MAXPATH];
char path[MAXPATH];
struct inode *ip;
if(argstr(0,target,MAXPATH) < 0 || argstr(1,path,MAXPATH) < 0){
return -1;
}
begin_op();
// Look up and return the inode for a path name.
if((ip = namei(path)) == 0){
ip = create(path,T_SYMLINK,0,0);
}
ilock(ip);
// 将目标文件的绝对路径写入符号链接文件中
if (writei(ip, 0, (uint64)target, ip->size, MAXPATH) != MAXPATH) {
panic("symlink");
}
iunlockput(ip);
end_op();
return 0;
}
最后在open函数也要添加对符号链接的处理程序
...
// 如果文件类型是符号链接
if (ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)) {
int threshold = 10;
while (ip->type == T_SYMLINK && threshold > 0) {
// 读取指向的文件的路径
if(readi(ip, 0, (uint64)path, 0, ip->size) != ip->size) {
iunlockput(ip);
end_op();
return -1;
}
iunlockput(ip);
if((ip = namei(path)) == 0) {
end_op();
return -1;
}
ilock(ip);
threshold--;
}
// 符号链接引用成环了
if (threshold == 0) {
iunlockput(ip);
end_op();
return -1;
}
}
if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){
iunlockput(ip);
end_op();
return -1;
}