# chapter6 练习
## 编程作业
### 硬链接
硬链接要求两个不同的目录项指向同一个文件,在我们的文件系统中也就是两个不同名称目录项指向同一个磁盘块。
本节要求实现三个系统调用 `linkat`、`unlinkat`、`fstat`。
**linkat**:
- syscall ID: 37
- 功能:创建一个文件的一个硬链接,[linkat 标准接口](https://linux.die.net/man/2/linkat)。
```rust
fn linkat(&self, _caller: Caller, _olddirfd: i32, oldpath: usize, _newdirfd: i32, newpath: usize, _flags: u32) -> isize
```
- 参数:
- olddirfd, newdirfd: 仅为了兼容性考虑,本次实验中始终为 AT_FDCWD (-100),可以忽略。
- flags: 仅为了兼容性考虑,本次实验中始终为 0,可以忽略。
- oldpath:原有文件路径
- newpath: 新的链接文件路径。
- 说明:
- 为了方便,不考虑新文件路径已经存在的情况(属于未定义行为)。除非出现新旧名字一致的情况,此时需要返回 -1。
- 返回值:如果出现了错误则返回 -1,否则返回 0。
- 可能的错误:
- 链接同名文件。
**unlinkat**:
- syscall ID: 35
- 功能:取消一个文件路径到文件的链接,[unlinkat 标准接口](https://linux.die.net/man/2/unlinkat)。
```rust
fn unlinkat(&self, _caller: Caller, _dirfd: i32, path: usize, _flags: u32) -> isize
```
- 参数:
- dirfd: 仅为了兼容性考虑,本次实验中始终为 AT_FDCWD (-100),可以忽略。
- flags: 仅为了兼容性考虑,本次实验中始终为 0,可以忽略。
- path:文件路径。
- 说明:
- 注意考虑使用 unlink 彻底删除文件的情况,此时需要回收 inode 以及它对应的数据块。
- 返回值:如果出现了错误则返回 -1,否则返回 0。
- 可能的错误:
- 文件不存在。
**fstat**:
- syscall ID: 80
- 功能:获取文件状态。
```rust
fn fstat(&self, _caller: Caller, fd: usize, st: usize) -> isize
```
- 参数:
- fd: 文件描述符
- st: 文件状态结构体指针
```rust
// Stat 结构体和 StatMode 结构体已在 syscall/src/fs.rs 中定义
#[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],
}
bitflags! {
pub struct StatMode: u32 {
const NULL = 0;
/// directory
const DIR = 0o040000;
/// ordinary regular file
const FILE = 0o100000;
}
}
```
### HINT
- `linkat` 和 `unlinkat` 的文件路径 path 的读取可参考 `tg-rcore-tutorial-ch6/src/main.rs` 中的 `open` 系统调用实现。
- `fstat` 的文件状态结构体 `Stat` 指针 st 的写入可参考 `tg-rcore-tutorial-ch6/src/main.rs` 中的 `clock_gettime` 系统调用对 `TimeSpec` 的写入实现。
### 实验要求
- 在 tg-rcore-tutorial-ch6 目录下完成实验。
- 目录结构说明:
```
tg-rcore-tutorial-ch6/
├── Cargo.toml(内核配置文件,需要修改依赖配置)
├── src/(内核源代码,需要修改)
│ ├── main.rs(内核主函数,包括系统调用接口实现)
│ ├── fs.rs(文件系统相关)
│ ├── process.rs(进程结构)
│ ├── processor.rs(进程管理器)
│ └── virtio_block.rs(VirtIO 块设备实现)
├── tg-rcore-tutorial-easy-fs/(文件系统实现,需要拉取到本地并修改以支持硬链接)
│ └── src/
│ ├── lib.rs
│ └── ...
└── tg-rcore-tutorial-user/(用户程序,运行时自动拉取,无需修改)
└── src/bin(测试用例)
```
> **说明**:
> - `tg-rcore-tutorial-user` 会在运行时自动拉取到 `tg-rcore-tutorial-ch6/tg-rcore-tutorial-user` 目录下
> - `tg-rcore-tutorial-easy-fs` 需要拉取到本地才能修改其代码以支持硬链接
> - 在 tg-rcore-tutorial-ch6 目录下执行 `cargo clone tg-rcore-tutorial-easy-fs` 拉取到本地
> - 在 tg-rcore-tutorial-ch6/Cargo.toml 中修改 tg-rcore-tutorial-easy-fs 为本地路径:
> ```toml
> [dependencies]
> tg-rcore-tutorial-easy-fs = { path = "./tg-rcore-tutorial-easy-fs" }
> ```
- 运行练习测例:
```bash
cargo run --features exercise
```
然后在终端中输入 `tg-rcore-tutorial-ch6_usertest` 运行,这个测例打包了所有你需要通过的测例。
- 测试练习测例:
```bash
./test.sh exercise
```
### 说明
- 你的内核必须前向兼容,需要能通过前一章的所有测例