MIT6.s081 2021 Lab File system
Large files
思路
xv6 文件系统的 inode 中地址域 addrs[]
由 12 个直接地址和 1 个一级间接地址组成,本实验要求将地址域更改为 11 个直接地址、1 个一级间接地址和 1 个二级间接地址组成,以支持更大文件的存储。
代码的实现有了直接地址和一级间接地址做参考,就很简单了,直接查看代码部分即可。
代码
diff --git a/kernel/file.h b/kernel/file.h
index b076d1d..5c4eb3a 100644
--- a/kernel/file.h
+++ b/kernel/file.h
@@ -26,7 +26,7 @@ struct inode {
short minor;
short nlink;
uint size;
- uint addrs[NDIRECT+1];
+ uint addrs[NDIRECT+2];
};
// map major device number to device functions.
diff --git a/kernel/fs.c b/kernel/fs.c
index 40c9bd4..4c00ab5 100644
--- a/kernel/fs.c
+++ b/kernel/fs.c
@@ -400,6 +400,33 @@ bmap(struct inode *ip, uint bn)
brelse(bp);
return addr;
}
+ bn -= NINDIRECT;
+
+ if (bn < NINDIRECT2) {
+ if ((addr = ip->addrs[NDIRECT + 1]) == 0) {
+ ip->addrs[NDIRECT + 1] = addr = balloc(ip->dev);
+ }
+
+ uint i = bn / NINDIRECT, j = bn % NINDIRECT;
+
+ bp = bread(ip->dev, addr);
+ a = (uint *)bp->data;
+ if ((addr = a[i]) == 0) {
+ a[i] = addr = balloc(ip->dev);
+ log_write(bp);
+ }
+ brelse(bp);
+
+ bp = bread(ip->dev, addr);
+ a = (uint *)bp->data;
+ if ((addr = a[j]) == 0) {
+ a[j] = addr = balloc(ip->dev);
+ log_write(bp);
+ }
+ brelse(bp);
+
+ return addr;
+ }
panic("bmap: out of range");
}
@@ -432,6 +459,29 @@ itrunc(struct inode *ip)
ip->addrs[NDIRECT] = 0;
}
+ struct buf *bp2;
+ uint *a2;
+ if (ip->addrs[NDIRECT + 1]) {
+ bp = bread(ip->dev, ip->addrs[NDIRECT + 1]);
+ a = (uint *)bp->data;
+ for (i = 0; i < NINDIRECT; ++i) {
+ if (a[i]) {
+ bp2 = bread(ip->dev, a[i]);
+ a2 = (uint *)bp2->data;
+ for (j = 0; j < NINDIRECT; ++j) {
+ if (a2[j]) {
+ bfree(ip->dev, a2[j]);
+ }
+ }
+ brelse(bp2);
+ bfree(ip->dev, a[i]);
+ }
+ }
+ brelse(bp);
+ bfree(ip->dev, ip->addrs[NDIRECT + 1]);
+ ip->addrs[NDIRECT + 1] = 0;
+ }
+
ip->size = 0;
iupdate(ip);
}
diff --git a/kernel/fs.h b/kernel/fs.h
index 139dcc9..cd5de8a 100644
--- a/kernel/fs.h
+++ b/kernel/fs.h
@@ -24,9 +24,10 @@ struct superblock {
#define FSMAGIC 0x10203040
-#define NDIRECT 12
+#define NDIRECT 11
#define NINDIRECT (BSIZE / sizeof(uint))
-#define MAXFILE (NDIRECT + NINDIRECT)
+#define NINDIRECT2 (NINDIRECT * NINDIRECT)
+#define MAXFILE (NDIRECT + NINDIRECT + NINDIRECT2)
// On-disk inode structure
struct dinode {
@@ -35,7 +36,7 @@ struct dinode {
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
+ uint addrs[NDIRECT+2]; // Data block addresses
};
// Inodes per block.
Symbolic links
思路
本实验要求为 xv6 实现符号链接(软链接)机制,符号链接本质上也是一个文件,只不过它的数据内容为该链接指向的文件路径,这其实与 Windows 系统的快捷方式十分类似。实现方案如下:
首先依照 Lab System call 中的方法,添加系统调用 symlink
:添加 symlink()
声明,添加系统调用号,添加系统调用 entry,添加 sys_symlink()
声明。
在理解了符号链接的本质后,就可以着手实现 sys_symlink
了。首先明确一下 symlink
的作用,它包含两个参数:target 和 path,作用是创建一个目录为 path 的符号链接,该符号链接指向目录为 target 的文件。实现思路应该比较清晰:使用 create()
创建一个文件类型为符号链接(需要自行定义)的文件,再使用 writei()
将字符串 path 写入该文件中。
uint64 sys_symlink(void) {
int n1, n2;
char target[MAXPATH], path[MAXPATH];
struct inode *ip;
// get arguments of symlink
if ((n1 = argstr(0, target, MAXPATH)) < 0 || (n2 = argstr(1, path, MAXPATH)) < 1) {
return -1;
}
begin_op();
// create symbol link in the path
if ((ip = create(path, T_SYMLINK, 0, 0)) == 0) {
end_op();
return -1;
}
// write target to file that ip points to
if (writei(ip, 0, (uint64)target, 0, n1) < n1) {
end_op();
return -1;
}
iunlockput(ip);
end_op();
return 0;
}
实现了符号链接的创建之后,还需要修改 sys_open()
,实现对符号链接的特殊处理:当该文件是一个符号链接并且需要以跟随(follow)的方式打开时,就不断向下递归,将当前的 inode 指针指向符号链接指向文件的 inode,直到 inode 指针对应的文件类型不是符号链接,此时该 inode 指针指向的文件才是本次 sys_open()
系统调用实际需要打开的文件。
这里要用到两个关键函数 readi()
和 namei()
。其中 readi()
能够根据 inode 指针,从该 inode 指针对应的文件中读取数据;而 namei()
能够根据指定的路径,返回该路径对应文件的 inode 指针。“跟随”的基本流程就是先使用读取当前 inode 中的数据,即目标文件路径 path,再将当前 inode 指针指向 path 目录对应的文件,以此往复。
最后还有一个小细节,就是当多个符号链接形成一个环时,这样的“跟随”过程就可能会导致死循环,因此必须加以限制,这里为了实现的方便,只是设定了一个最大递归深度,当递归深度超过该设定最大值时,文件打开就会失败。
if (!(omode & O_NOFOLLOW)) {
int depth = 10; // max recursive depth
struct inode *next; // next inode
while (depth > 0 && ip->type == T_SYMLINK) {
// read data from file that ip points to to path
if (readi(ip, 0, (uint64)path, 0, MAXPATH) == 0) {
iunlockput(ip);
end_op();
return -1;
}
// get inode of file in the path
if ((next = namei(path)) == 0) {
iunlockput(ip);
end_op();
return -1;
}
iunlockput(ip);
ip = next;
--depth;
ilock(ip);
}
if (depth <= 0) {
iunlockput(ip);
end_op();
return -1;
}
}
代码
diff --git a/Makefile b/Makefile
index 7a7e380..37a202c 100644
--- a/Makefile
+++ b/Makefile
@@ -188,6 +188,7 @@ UPROGS=\
$U/_grind\
$U/_wc\
$U/_zombie\
+ $U/_symlinktest\
diff --git a/kernel/fcntl.h b/kernel/fcntl.h
index 44861b9..b42df18 100644
--- a/kernel/fcntl.h
+++ b/kernel/fcntl.h
@@ -3,3 +3,4 @@
#define O_RDWR 0x002
#define O_CREATE 0x200
#define O_TRUNC 0x400
+#define O_NOFOLLOW 0x800
\ No newline at end of file
diff --git a/kernel/stat.h b/kernel/stat.h
index 19543af..46ba47f 100644
--- a/kernel/stat.h
+++ b/kernel/stat.h
@@ -1,6 +1,7 @@
#define T_DIR 1 // Directory
#define T_FILE 2 // File
#define T_DEVICE 3 // Device
+#define T_SYMLINK 4 // Symbol link
struct stat {
int dev; // File system's disk device
diff --git a/kernel/syscall.c b/kernel/syscall.c
index c1b3670..1697b62 100644
--- a/kernel/syscall.c
+++ b/kernel/syscall.c
@@ -104,6 +104,7 @@ extern uint64 sys_unlink(void);
extern uint64 sys_wait(void);
extern uint64 sys_write(void);
extern uint64 sys_uptime(void);
+extern uint64 sys_symlink(void);
static uint64 (*syscalls[])(void) = {
[SYS_fork] sys_fork,
@@ -127,6 +128,7 @@ static uint64 (*syscalls[])(void) = {
[SYS_link] sys_link,
[SYS_mkdir] sys_mkdir,
[SYS_close] sys_close,
+[SYS_symlink] sys_symlink,
};
void
diff --git a/kernel/syscall.h b/kernel/syscall.h
index bc5f356..0fbf6ed 100644
--- a/kernel/syscall.h
+++ b/kernel/syscall.h
@@ -20,3 +20,4 @@
#define SYS_link 19
#define SYS_mkdir 20
#define SYS_close 21
+#define SYS_symlink 22
\ No newline at end of file
diff --git a/kernel/sysfile.c b/kernel/sysfile.c
index 5dc453b..ae342c8 100644
--- a/kernel/sysfile.c
+++ b/kernel/sysfile.c
@@ -15,6 +15,7 @@
#include "sleeplock.h"
#include "file.h"
#include "fcntl.h"
+#include "buf.h"
// Fetch the nth word-sized system call argument as a file descriptor
// and return both the descriptor and the corresponding struct file.
@@ -316,6 +317,35 @@ sys_open(void)
}
}
+ if (!(omode & O_NOFOLLOW)) {
+ int depth = 10;
+ struct inode *next;
+
+ while (depth > 0 && ip->type == T_SYMLINK) {
+ if (readi(ip, 0, (uint64)path, 0, MAXPATH) == 0) {
+ iunlockput(ip);
+ end_op();
+ return -1;
+ }
+
+ if ((next = namei(path)) == 0) {
+ iunlockput(ip);
+ end_op();
+ return -1;
+ }
+ iunlockput(ip);
+ ip = next;
+ --depth;
+ ilock(ip);
+ }
+
+ if (depth <= 0) {
+ iunlockput(ip);
+ end_op();
+ return -1;
+ }
+ }
+
if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){
iunlockput(ip);
end_op();
@@ -484,3 +514,28 @@ sys_pipe(void)
}
return 0;
}
+
+uint64 sys_symlink(void) {
+ int n1, n2;
+ char target[MAXPATH], path[MAXPATH];
+ struct inode *ip;
+
+ if ((n1 = argstr(0, target, MAXPATH)) < 0 || (n2 = argstr(1, path, MAXPATH)) < 1) {
+ return -1;
+ }
+
+ // create symbol link in the path
+ begin_op();
+ if ((ip = create(path, T_SYMLINK, 0, 0)) == 0) {
+ end_op();
+ return -1;
+ }
+ if (writei(ip, 0, (uint64)target, 0, n1) < n1) {
+ end_op();
+ return -1;
+ }
+ iunlockput(ip);
+ end_op();
+
+ return 0;
+}
\ No newline at end of file
diff --git a/user/user.h b/user/user.h
index b71ecda..883ef48 100644
--- a/user/user.h
+++ b/user/user.h
@@ -23,6 +23,7 @@ int getpid(void);
char* sbrk(int);
int sleep(int);
int uptime(void);
+int symlink(char *, char *);
// ulib.c
int stat(const char*, struct stat*);
diff --git a/user/usys.pl b/user/usys.pl
index 01e426e..65a8d6b 100755
--- a/user/usys.pl
+++ b/user/usys.pl
@@ -36,3 +36,4 @@ entry("getpid");
entry("sbrk");
entry("sleep");
entry("uptime");
+entry("symlink");
\ No newline at end of file