Skip to content
GitLab
Explore
Projects
Groups
Topics
Snippets
Projects
Groups
Topics
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
educg-net-14239-914332
OSKernel2022-Maturin-1281
Commits
56b5ea71
Commit
56b5ea71
authored
3 years ago
by
Maturin
Browse files
Options
Download
Patches
Plain Diff
hard link/unlink for file
parent
ffd17627
master
HEAD
a036
final1
optional-2
No related merge requests found
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
kernel/src/constants.rs
+1
-1
kernel/src/constants.rs
kernel/src/file/device/link.rs
+127
-1
kernel/src/file/device/link.rs
kernel/src/file/device/mod.rs
+38
-6
kernel/src/file/device/mod.rs
kernel/src/file/device/test.rs
+5
-1
kernel/src/file/device/test.rs
kernel/src/file/mod.rs
+2
-0
kernel/src/file/mod.rs
kernel/src/syscall/fs.rs
+33
-1
kernel/src/syscall/fs.rs
kernel/src/syscall/mod.rs
+4
-0
kernel/src/syscall/mod.rs
with
210 additions
and
10 deletions
+210
-10
kernel/src/constants.rs
+
1
−
1
View file @
56b5ea71
...
...
@@ -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
=
fals
e
;
pub
const
IS_PRELOADED_FS_IMG
:
bool
=
tru
e
;
/// 文件系统镜像的大小。注意这个量和 fs-init 模块中 `/src/main.rs` 里生成镜像时的大小相同。
/// 启动时会从 .data 段加载加载
const
LOCAL_FS_IMG_SIZE
:
usize
=
16
*
2048
*
512
;
// 16MB
...
...
This diff is collapsed.
Click to expand it.
kernel/src/file/device/link.rs
+
127
−
1
View file @
56b5ea71
//! 处理文件系统的链接相关
//!
//! 这个模块中有大量字符串操作,可能有较高的时间复杂度,不建议频繁链接
#![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
[
..
]),
}
}
}
This diff is collapsed.
Click to expand it.
kernel/src/file/device/mod.rs
+
38
−
6
View file @
56b5ea71
...
...
@@ -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
)
}
...
...
This diff is collapsed.
Click to expand it.
kernel/src/file/device/test.rs
+
5
−
1
View file @
56b5ea71
...
...
@@ -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"
,
...
...
This diff is collapsed.
Click to expand it.
kernel/src/file/mod.rs
+
2
−
0
View file @
56b5ea71
...
...
@@ -54,4 +54,6 @@ pub use device::{
load_testcases
,
load_next_testcase
,
mkdir
,
try_remove_link
,
try_add_link
,
};
This diff is collapsed.
Click to expand it.
kernel/src/syscall/fs.rs
+
33
−
1
View file @
56b5ea71
...
...
@@ -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目录而言的。
...
...
This diff is collapsed.
Click to expand it.
kernel/src/syscall/mod.rs
+
4
−
0
View file @
56b5ea71
...
...
@@ -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
),
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment
Menu
Explore
Projects
Groups
Topics
Snippets