Commit 56b5ea71 authored by Maturin's avatar Maturin
Browse files

hard link/unlink for file

No related merge requests found
Showing with 210 additions and 10 deletions
+210 -10
......@@ -63,7 +63,7 @@ pub const MMIO_REGIONS: &[AddrArea] = &[AddrArea(0x10001000, 0x10002000)];
/// 是否是比赛评测。线上评测时要求OS像一个批处理系统一样工作,这可能导致内核不会直接去拿初始进程并运行
pub const IS_TEST_ENV: bool = true;
/// 测试环境下,文件系统镜像是否是由qemu引入
pub const IS_PRELOADED_FS_IMG: bool = false;
pub const IS_PRELOADED_FS_IMG: bool = true;
/// 文件系统镜像的大小。注意这个量和 fs-init 模块中 `/src/main.rs` 里生成镜像时的大小相同。
/// 启动时会从 .data 段加载加载
const LOCAL_FS_IMG_SIZE: usize = 16 * 2048 * 512; // 16MB
......
//! 处理文件系统的链接相关
//!
//! 这个模块中有大量字符串操作,可能有较高的时间复杂度,不建议频繁链接
#![deny(missing_docs)]
use alloc::collections::{btree_map::Entry, BTreeMap};
use alloc::string::String;
use lock::Mutex;
use lazy_static::*;
use super::{
split_path_and_file,
check_file_exists,
remove_file,
};
lazy_static!{
/// 用户看到的文件到实际文件的映射
static ref LINK_PATH_MAP: Mutex<BTreeMap<FileDisc, FileDisc>> = Mutex::new(BTreeMap::new());
/// 实际文件(而不是用户文件)到链接数的映射
static ref LINK_COUNT_MAP: Mutex<BTreeMap<FileDisc, usize>> = Mutex::new(BTreeMap::new());
}
/// 将用户提供的路径和文件转换成实际的路径和文件
pub fn parse_file_name((path, file): (String, String)) -> (String, String) {
let map = LINK_PATH_MAP.lock();
match map.get(&FileDisc::new(&path, &file)) {
Some(disc) => { (String::from(&disc.path[..]), String::from(&disc.file[..])) },
None => (path, file),
}
//*count.entry(x).or_insert(0) += 1;
}
/// 添加硬链接
///
/// 这个函数不对外可见,外部需要调用 try_add_link
fn add_link(real_path: String, real_file: String, user_path: String, user_file: String) {
let mut map = LINK_PATH_MAP.lock();
let mut count_map = LINK_COUNT_MAP.lock();
let key = FileDisc::new(&user_path, &user_file);
let value = FileDisc::new(&real_path, &real_file);
// 注意链接数是统计在实际文件上的
*count_map.entry(value.clone()).or_insert(1) += 1;
match map.get(&key) {
Some(disc) => {
map.insert(key, value);
},
None => {
map.insert(key.clone(), value);
// 原来的文件自己也是一个链接,两者需要无法区分
map.insert(key.clone(), key);
}
};
}
/// 尝试添加一个硬链接。
///
/// 如果需要链接的文件已存在,或者被链接到的文件不存在,则执行失败,返回 false
pub fn try_add_link(old_path: String, old_file: &str, new_path: String, new_file: &str) -> bool {
// 经过链接转换
if let Some((old_path, old_file)) = split_path_and_file(old_path.as_str(), old_file)
.map(|(path, file)| (path, String::from(file)))
.map(parse_file_name) {
if let Some((new_path, new_file)) = split_path_and_file(new_path.as_str(), new_file)
.map(|(path, file)| (path, String::from(file)))
.map(parse_file_name) {
if check_file_exists(old_path.as_str(), old_file.as_str())
&& !check_file_exists(new_path.as_str(), new_file.as_str()) {
add_link(old_path, old_file, new_path, new_file);
return true
}
}
}
false
}
/// 获取硬链接数。
///
/// **默认该文件存在,且目录/文件格式经过split_path_and_file 转换**
fn get_link_count(path: String, file: &str) -> usize {
let (path, file) = parse_file_name((path, String::from(file)));
// 注意找不到时,链接数默认为 1 而不是 0。因为没有进行过链接操作的文件不在 map 里
*LINK_COUNT_MAP.lock().get(&FileDisc::new(&path, &file)).unwrap_or(&1)
}
/// 尝试删除一个硬链接。
/// 如果链接数为0,则删除该文件。
///
/// 如果这个文件不存在,则执行失败,返回 false
pub fn try_remove_link(path: String, file: &str) -> bool {
let key = FileDisc::new(&path, &String::from(file));
// 经过链接转换
if let Some((real_path, real_file)) = split_path_and_file(path.as_str(), file)
.map(|(path, file)| (path, String::from(file)))
.map(parse_file_name) {
if check_file_exists(real_path.as_str(), real_file.as_str()) {
let mut map = LINK_PATH_MAP.lock();
let mut count_map = LINK_COUNT_MAP.lock();
let value = FileDisc::new(&real_path, &real_file);
// 先删除链接表里的映射
if count_map.get(&value).is_some() {
map.remove(&key).unwrap();
}
// 链接表里没找到时,视作链接数为1
let count = count_map.entry(value.clone()).or_insert(1);
*count -= 1;
// 如果已经没有链接了,则需要删除这个文件
if *count == 0 {
count_map.remove(&value).unwrap();
remove_file(real_path.as_str(), real_file.as_str());
}
return true;
}
}
false
}
/// 同时保存文件路径和文件名,作为链接表的 K/V
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct FileDisc {
pub path: String,
pub file: String,
}
impl FileDisc {
pub fn new(path: &String, file: &String) -> Self {
Self {
path: String::from(&path[..]),
file: String::from(&file[..]),
}
}
}
......@@ -36,14 +36,18 @@ mod open_flags;
mod fat_file;
mod fat_dir;
mod fd_dir;
mod link;
mod test;
pub use open_flags::OpenFlags;
pub use fat_file::FatFile;
pub use fat_dir::FatDir;
pub use fd_dir::FdDir;
pub use link::{try_remove_link, try_add_link};
pub use test::{load_testcases, load_next_testcase};
use link::parse_file_name;
lazy_static! {
//static ref MEMORY_FS: Arc<Mutex<FileSystem<FsIO, FsTP, FsOCC>>> = Arc::new(Mutex::new(new_memory_mapped_fs()));
static ref MEMORY_FS: FileSystem<FsIO, FsTP, FsOCC> = new_memory_mapped_fs();
......@@ -126,10 +130,28 @@ fn split_path_and_file<'a>(dir_name: &str, file_path: &'a str) -> Option<(String
Some((dir, &file_path[pos..]))
}
/// 分割文件所在路径,然后经过link转换。
fn map_path_and_file(dir_name: &str, file_path: &str) -> Option<(String, String)> {
if !dir_name.ends_with('/') {
return None;
}
let mut dir = String::from("./");
let start_pos = if dir_name.starts_with("./") { //一般来说,根目录是从 ./ 开始,所以 dir_name 也是 ./ 开头
2
} else if dir_name.starts_with("/") { // 但如果用户通过 getcwd 等方式获取目录,则这样的目录是以 / 开头的
1
} else { //又或者用户试图输入一个相对路径,这时需要把它变成相对于根路径的路径
0
};
parse_dir(&mut dir, &dir_name[start_pos..]);
let pos = parse_dir(&mut dir, file_path);
Some(parse_file_name((dir, String::from(&file_path[pos..]))))
}
/*
/// 获取文件所在路径。
fn get_file_dir<'a>(dir_name: &str, file_path: &'a str) -> String {
split_path_and_file(dir_name, file_path).unwrap().0
map_path_and_file(dir_name, file_path).unwrap().0
}
*/
......@@ -165,7 +187,8 @@ pub fn open_file(dir_name: &str, file_path: &str, flags: OpenFlags) -> Option<Ar
//let fs = MEMORY_FS.lock();
//let root = fs.root_dir();
let root = MEMORY_FS.root_dir();
let (real_dir, file_name) = split_path_and_file(dir_name, file_path)?;
let (real_dir, file_name) = map_path_and_file(dir_name, file_path)?;
let file_name = file_name.as_str();
//println!("dir = {}, name = {}", real_dir, file_name);
if let Some(dir) = inner_open_dir(root, real_dir.as_str()) {
if flags.contains(OpenFlags::DIR) { // 要求打开目录
......@@ -209,7 +232,7 @@ pub fn open_file(dir_name: &str, file_path: &str, flags: OpenFlags) -> Option<Ar
}
}
/// 检查文件是否存在。
/// 检查文件是否存在。(不考虑link)
/// 如果目录本身不存在,那么也会返回 false,不会报错。
///
/// 这里并不直接试图打开文件检查是否成功,而是检查目录下是否存在对应文件。
......@@ -218,7 +241,7 @@ pub fn check_file_exists(dir_name: &str, file_path: &str) -> bool {
//let fs = MEMORY_FS.lock();
//let root = fs.root_dir();
let root = MEMORY_FS.root_dir();
split_path_and_file(dir_name, file_path).map(|(real_dir, file_name)| {
map_path_and_file(dir_name, file_path).map(|(real_dir, file_name)| {
info!("check file exists: dir = {}, name = {}", real_dir, file_name);
inner_open_dir(root, real_dir.as_str()).map(|dir| {
for entry in dir.iter() {
......@@ -232,10 +255,19 @@ pub fn check_file_exists(dir_name: &str, file_path: &str) -> bool {
}).map_or(false, |r| r)
}
/// 删除文件
///
/// **调用这个函数时默认文件存在,且 path/name 已经过 split_path_and_file 格式化**
fn remove_file(path: &str, name: &str) {
let root = MEMORY_FS.root_dir();
let dir = inner_open_dir(root, path).unwrap();
dir.remove(name).unwrap();
}
/// 创建目录,返回是否成功
pub fn mkdir(dir_name: &str, file_path: &str) -> bool {
let root = MEMORY_FS.root_dir();
split_path_and_file(dir_name, file_path).map(|(real_dir, file_name)| {
map_path_and_file(dir_name, file_path).map(|(real_dir, file_name)| {
inner_open_dir(root, real_dir.as_str()).map(|dir| {
// 检查目录或者同名文件是否已存在
for entry in dir.iter() {
......@@ -244,7 +276,7 @@ pub fn mkdir(dir_name: &str, file_path: &str) -> bool {
return false;
}
}
dir.create_dir(file_name).is_ok()
dir.create_dir(file_name.as_str()).is_ok()
}).map_or(false, |r| r)
}).map_or(false, |r| r)
}
......
......@@ -31,9 +31,13 @@ pub fn load_next_testcase() -> Option<Arc<TaskControlBlock>> {
}
lazy_static! {
static ref TESTCASES_ITER: Mutex<Iter<'static, &'static str>> = Mutex::new(TESTCASES.into_iter());
static ref TESTCASES_ITER: Mutex<Iter<'static, &'static str>> = Mutex::new(SAMPLE.into_iter());
}
pub const SAMPLE: &[&str] = &[
"unlink",
];
pub const TESTCASES: &[&str] = &[
"brk",
"chdir",
......
......@@ -54,4 +54,6 @@ pub use device::{
load_testcases,
load_next_testcase,
mkdir,
try_remove_link,
try_add_link,
};
......@@ -15,7 +15,13 @@ use crate::task::{get_current_task};
use crate::task::TaskControlBlockInner;
use crate::utils::raw_ptr_to_ref_str;
use crate::file::{OpenFlags, Pipe};
use crate::file::{open_file, mkdir, check_dir_exists};
use crate::file::{
open_file,
mkdir,
check_dir_exists,
try_add_link,
try_remove_link,
};
use crate::constants::{ROOT_DIR, AT_FDCWD};
const FD_STDIN: usize = 0;
......@@ -114,6 +120,32 @@ fn resolve_path_from_fd<'a>(tcb_inner: &MutexGuard<TaskControlBlockInner>, dir_f
Some((parent_dir, file_path))
}
/// 创建硬链接。成功时返回0,失败时返回-1
pub fn sys_linkat(old_dir_fd: i32, old_path: *const u8, new_dir_fd: i32, new_path: *const u8, flags: u32) -> isize {
let task = get_current_task().unwrap();
let mut tcb_inner = task.inner.lock();
if let Some((old_path, old_file)) = resolve_path_from_fd(&tcb_inner, old_dir_fd, old_path) {
if let Some((new_path, new_file)) = resolve_path_from_fd(&tcb_inner, new_dir_fd, new_path) {
if try_add_link(old_path, old_file, new_path, new_file) {
return 0;
}
}
}
-1
}
/// 删除硬链接,并在链接数为0时实际删除文件。成功时返回0,失败时返回-1
pub fn sys_unlinkat(dir_fd: i32, path: *const u8, flags: u32) -> isize {
let task = get_current_task().unwrap();
let mut tcb_inner = task.inner.lock();
if let Some((path, file)) = resolve_path_from_fd(&tcb_inner, dir_fd,path) {
if try_remove_link(path, file){
return 0;
}
}
-1
}
/// 创建目录,成功时返回 0,失败时返回 -1
///
/// - 如果path是相对路径,则它是相对于dirfd目录而言的。
......
......@@ -16,6 +16,8 @@ const SYSCALL_GETCWD: usize = 17;
const SYSCALL_DUP: usize = 23;
const SYSCALL_DUP3: usize = 24;
const SYSCALL_MKDIR: usize = 34;
const SYSCALL_UNLINKAT: usize = 35;
const SYSCALL_LINKAT: usize = 37;
const SYSCALL_CHDIR: usize = 49;
const SYSCALL_OPEN: usize = 56;
const SYSCALL_CLOSE: usize = 57;
......@@ -55,6 +57,8 @@ pub fn syscall(syscall_id: usize, args: [usize; 6]) -> isize {
SYSCALL_GETCWD => sys_getcwd(args[0] as *mut u8, args[1]),
SYSCALL_DUP => sys_dup(args[0]),
SYSCALL_DUP3 => sys_dup3(args[0], args[1]),
SYSCALL_UNLINKAT => sys_unlinkat(args[0] as i32, args[1] as *const u8, args[2] as u32),
SYSCALL_LINKAT => sys_linkat(args[0] as i32, args[1] as *const u8, args[2] as i32, args[3] as *const u8, args[4] as u32),
SYSCALL_MKDIR => sys_mkdir(args[0] as i32, args[1] as *const u8, args[2] as u32),
SYSCALL_CHDIR => sys_chdir(args[0] as *const u8),
SYSCALL_OPEN => sys_open(args[0] as i32, args[1] as *const u8, args[2] as u32, args[3] as u32),
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment