Symbolic links
添加符号链接
1.添加有关symlink系统调用的定义声明,包括kernel/syscall.h
, kernel/syscall.c
, user/usys.pl
和 user/user.h
.
2.添加新的文件类型T_SYMLINK到kernel/stat.h中,添加新的文件标识位O_NOFOLLOW到kernel/fcntl.h中
3.在kenel/sysfile.c中实现sys_symlink()
函数
该函数用于生成符号链接,该符号链接相当于一个特殊的独立文件,里面存储的是目标文件的路径
4.修改kernel/sysfile中的sys_open()函数
该函数可以用来打开文件,对于符号链接一般情况下需要打开的是其目标文件,因此需要额外处理
(此处需要注意避免成环问题的出现)
/* 系统调用open的实现 */
uint64
sys_open(void)
{
/* 路径名缓冲区 */
char path[MAXPATH];
/* 文件描述符和打开模式 */
int fd, omode;
/* 文件结构指针 */
struct file *f;
/* i节点指针 */
struct inode *ip;
/* 临时变量,用于存储函数调用返回值 */
int n;
/* 获取路径名和打开模式,如果出错则返回-1 */
if((n = argstr(0, path, MAXPATH)) < 0 || argint(1, &omode) < 0)
return -1;
/* 开始一个文件操作 */
begin_op();
/* 如果需要创建文件 */
if(omode & O_CREATE){
/* 调用create函数创建文件,如果失败则返回-1 */
ip = create(path, T_FILE, 0, 0);
if(ip == 0){
end_op();
return -1;
}
} else {
int symlink_depth = 0;
/* 查找已存在的文件,如果失败则返回-1 */
while(1){
if((ip = namei(path)) == 0){
end_op();
return -1;
}
/* 加锁i节点 */
ilock(ip);
if(ip->type == T_SYMLINK && (omode & O_NOFOLLOW) == 0){
//在跟踪符号链接时需要额外考虑到符号链接的目标可能还是符号链接, 此时需要递归的去跟踪目标链接直至得到真正的文件. 而这其中需要解决两个问题: 一是符号链接可能成环, 这样会一直递归地跟踪下去, 因此需要进行成环的检测; 另一方面是需要对链接的深度进行限制, 以减轻系统负担,这里把最大深度设成10
if(++symlink_depth > 10){
// too many layer of symlinks, might be a loop
iunlockput(ip);
end_op();
return -1;
}
if(readi(ip, 0, (uint64)path, 0, MAXPATH) < 0){
iunlockput(ip);
end_op();
return -1;
}
iunlockput(ip);
}else{
break;
}
}
/* 如果是目录文件且不是只读打开,返回-1 */
if(ip->type == T_DIR && omode != O_RDONLY){
// // 如果是目录,并且打开模式不是 O_RDONLY(只读),则不能打开
iunlockput(ip);
end_op();
return -1;
}
}
/* 如果是设备文件,检查设备号是否有效,否则返回-1 */
if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){
iunlockput(ip);
end_op();
return -1;
}
/* 分配文件结构,如果失败则返回-1 */
if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
if(f)
fileclose(f);
iunlockput(ip);
end_op();
return -1;
}
// //处理符号链接,即软连接,如果
// if(ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)){ //如果不是不可追踪
// //若符号链接指向的仍旧是符号链接,则递归跟随它,直到找到真正的指向文件
// //深度不能超过MAX_SYMLINK_DEPTH
// for(int i = 0; i < MAX_SYMLINK_DEPTH; i++){
// //读出符号链接指的路径
// }
// }
/* 根据文件类型设置文件结构的类型和主要设备号 */
if(ip->type == T_DEVICE){
f->type = FD_DEVICE;
f->major = ip->major;
} else {
f->type = FD_INODE;
f->off = 0;
}
/* 设置文件结构的i节点指针和读写权限 */
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);
}
/* 解锁i节点 */
iunlock(ip);
/* 结束文件操作 */
end_op();
/* 返回文件描述符 */
return fd;
}
这里也可以看一下硬链接的实现方式(kernel/sysfile)
// Create the path new as a link to the same inode as old.
//xv6硬链接实现方式
uint64 sys_link(void) {
char name[DIRSIZ], new[MAXPATH], old[MAXPATH];
struct inode *dp, *ip;
// 从用户态获取参数 old 和 new,分别表示旧路径和新路径
if(argstr(0, old, MAXPATH) < 0 || argstr(1, new, MAXPATH) < 0)
return -1;
// 开始文件系统操作事务
begin_op();
// 根据旧路径名找到 inode
if((ip = namei(old)) == 0){
end_op();
return -1;
}
// 对 inode 进行加锁
ilock(ip);
// 检查旧路径对应的 inode 是否为目录,如果是目录则不能创建硬链接
if(ip->type == T_DIR){
iunlockput(ip); // 解锁 inode 并释放引用
end_op();
return -1;
}
// 增加 inode 的链接数,因为即将创建一个新的硬链接指向该 inode
ip->nlink++;
iupdate(ip); // 更新 inode 的磁盘信息
iunlock(ip); // 解锁 inode
// 解析新路径中的目录名和文件名
if((dp = nameiparent(new, name)) == 0)
goto bad;
// 对目录的 inode 进行加锁
ilock(dp);
// 检查新路径所在的目录的设备号与旧路径所在的设备号是否相同
// 如果不相同,或者在新路径所在的目录中无法创建新的目录项,说明创建硬链接失败
if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0){
iunlockput(dp); // 解锁目录的 inode 并释放引用
goto bad;
}
// 解锁目录的 inode 并释放引用
iunlockput(dp);
// 释放旧路径对应的 inode 的引用
iput(ip);
// 结束文件系统操作事务
end_op();
return 0;
bad:
// 创建硬链接失败时,需要回滚之前对旧路径 inode 的修改,即减少链接数
ilock(ip);
ip->nlink--;
iupdate(ip);
iunlockput(ip);
end_op();
return -1;
}
5.最后在Makefile中添加对测试文件symlinktest.c
的编译.
测试