rCore第六章练习
实践作业
硬链接
硬链接要求两个不同的目录项指向同一个文件,在我们的文件系统中也就是两个不同名称目录项指向同一个磁盘块。
本节要求实现三个系统调用 sys_linkat、sys_unlinkat、sys_stat
。
linkat:
syscall ID: 37
功能:创建一个文件的一个硬链接, linkat标准接口 。
C接口:
int linkat(int olddirfd, char* oldpath, int newdirfd, char* newpath, unsigned int flags)
Rust 接口:
fn linkat(olddirfd: i32, oldpath: *const u8, newdirfd: i32, newpath: *const u8, flags: u32) -> i32
参数:
olddirfd,newdirfd: 仅为了兼容性考虑,本次实验中始终为 AT_FDCWD (-100),可以忽略。flags: 仅为了兼容性考虑,本次实验中始终为 0,可以忽略。oldpath:原有文件路径newpath: 新的链接文件路径。
说明:
为了方便,不考虑新文件路径已经存在的情况(属于未定义行为),除非链接同名文件。返回值:如果出现了错误则返回 -1,否则返回 0。
可能的错误
链接同名文件。
unlinkat:
syscall ID: 35
功能:取消一个文件路径到文件的链接, unlinkat标准接口 。
C接口:
int unlinkat(int dirfd, char* path, unsigned int flags)
Rust 接口:
fn unlinkat(dirfd: i32, path: *const u8, flags: u32) -> i32
参数:
dirfd: 仅为了兼容性考虑,本次实验中始终为 AT_FDCWD (-100),可以忽略。flags: 仅为了兼容性考虑,本次实验中始终为 0,可以忽略。path:文件路径。
说明:
为了方便,不考虑使用 unlink 彻底删除文件的情况。
返回值:如果出现了错误则返回 -1,否则返回 0。
可能的错误
文件不存在。
fstat:
syscall ID: 80
功能:获取文件状态。
C接口:
int fstat(int fd, struct Stat* st)
Rust 接口:
fn fstat(fd: i32, st: *mut Stat) -> i32
参数:
fd: 文件描述符st: 文件状态结构体
#[repr(C)] #[derive(Debug)] pub struct Stat { /// 文件所在磁盘驱动器号,该实验中写死为 0 即可 pub dev: u64, /// inode 文件所在 inode 编号 pub ino: u64, /// 文件类型 pub mode: StatMode, /// 硬链接数量,初始为1 pub nlink: u32, /// 无需考虑,为了兼容性设计 pad: [u64; 7], } /// StatMode 定义: bitflags! { pub struct StatMode: u32 { const NULL = 0; /// directory const DIR = 0o040000; /// ordinary regular file const FILE = 0o100000; } }
实现
link
link
的实现会调用inode
模块中的add_a_link
方法,它是ROOT_NODE
的一个方法
pub fn add_a_link(&self, oldpath: &str, newpath:&str) -> isize {
let mut fs = self.fs.lock();
self.modify_disk_inode(|root_inode| {
let file_count = (root_inode.size as usize) / DIRENT_SZ;
let new_size = (file_count + 1) * DIRENT_SZ;
for i in 0..file_count {
let mut dirent = DirEntry::empty();
root_inode.read_at(i * DIRENT_SZ, dirent.as_bytes_mut(), &self.block_device);
if dirent.name() == oldpath {
self.increase_size(new_size as u32, root_inode, &mut fs);
root_inode.write_at(new_size * DIRENT_SZ, DirEntry::new(newpath, dirent.inode_number()).as_bytes(), &self.block_device);
}
}
});
0
}
首先,代码中会尝试获取锁,之后通过回调函数,对自己这个inode
进行操作。由于是根inode
,我们可以遍历其中的目录,遍历其中的文件名,如果发现文件名和old_path
相同的,可以添加一个记录,由于是硬链接公用一个inode
即可。
unlink
unlink
的实现会稍微复杂一点,
pub fn rm_a_link(&self, path: &str) -> isize {
let mut fs = self.fs.lock();
self.modify_disk_inode(|root_inode| {
let file_count = (root_inode.size as usize) / DIRENT_SZ;
for i in 0..file_count {
let mut dirent = DirEntry::empty();
root_inode.read_at(i * DIRENT_SZ, dirent.as_bytes_mut(), &self.block_device);
if dirent.name() == path {
for j in i+1..file_count{
root_inode.read_at(i*DIRENT_SZ, dirent.as_bytes_mut(), &self.block_device);
root_inode.write_at((i-1) * DIRENT_SZ, dirent.as_bytes(), &self.block_device);
}
root_inode.size -= 1;
break;
}
}
});
0
}
我们仍然遍历根目录,如果找到一个和我们要找的文件名相同的项,我们就根据比较排序类似的方法, 将读到的后一项覆盖到前一项,这样目录中就不会出现空项。
fstat
我们需要在File
中实现一个新的方法,用于获取这个实现File Trait
的结构体的信息。
fn stat(&self, st:&mut Stat) -> usize {
let inner = self.inner.exclusive_access();
inner.inode.stat(unsafe {
core::slice::from_raw_parts_mut(st as *mut Stat as usize as *mut u8,
core::mem::size_of::<Stat>(),
)
});
1
}
我们实现了fstat
方法,Stat
结构体如下
pub struct Stat {
pub dev: u64,
pub ino: u64,
pub mode: StatMode,
pub nlink: u32,
pad: [u64; 7],
}
由于要在这里没有在easy-fs
实现Stat
结构体,我选择将Stat
转化为字节数组引用进行传递
- 第一步,
&mut Stat
可以转化为* mut Stat
指针 - 指针可以转化为
usize
地址 - 再将
usize
转化为一个*mut u8
的指针
最后构造一个引用
pub fn stat(&self, st: &mut [u8]) -> usize{
let mut fs = self.fs.lock();
let st_trans:&mut Stat = unsafe { ( st.as_mut_ptr() as usize as *mut Stat).as_mut().unwrap()};
let inode_id = ((self.block_id - self.fs.lock().inode_area_start_block as usize) * BLOCK_SZ + self.block_offset) / core::mem::size_of::<Inode>();
st_trans.ino = inode_id as u64;
let mut link_num = 0;
let mut _type = StatMode::FILE;
EasyFileSystem::root_inode(&self.fs.read_disk_inode(|disk_inode| {
let file_count = (disk_inode.size as usize) / DIRENT_SZ;
for i in 0..file_count {
let mut dirent = DirEntry::empty();
if dirent.inode_number() == inode_id as u32 {
link_num += 1;
}
}
if(disk_inode.is_dir()){
_type = StatMode::DIR;
}
});
st_trans.nlink = link_num;
st_trans.mode = _type;
1
}
最后在easy-fs
侧实现了这样一个函数
- 第一步,首先根据自己的
inode
的结构体信息计算出自己在块设备中的位置 - 遍历根目录中的文件,如果发现
inode
号相同,则将链接数加1 - 最后设置文件类型