450 lines
15 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use alloc::{boxed::Box, sync::Arc, vec::Vec};
use crate::{
arch::asm::current::current_pcb,
filesystem::vfs::file::FileDescriptorVec,
include::bindings::bindings::{verify_area, AT_REMOVEDIR, PAGE_4K_SIZE, PROC_MAX_FD_NUM},
io::SeekFrom,
kerror,
syscall::{Syscall, SystemError},
};
use super::{
core::{do_mkdir, do_remove_dir, do_unlink_at},
file::{File, FileMode},
utils::rsplit_path,
Dirent, FileType, IndexNode, ROOT_INODE,
};
pub const SEEK_SET: u32 = 0;
pub const SEEK_CUR: u32 = 1;
pub const SEEK_END: u32 = 2;
pub const SEEK_MAX: u32 = 3;
impl Syscall {
/// @brief 为当前进程打开一个文件
///
/// @param path 文件路径
/// @param o_flags 打开文件的标志位
///
/// @return 文件描述符编号,或者是错误码
pub fn open(path: &str, mode: FileMode) -> Result<usize, SystemError> {
// 文件名过长
if path.len() > PAGE_4K_SIZE as usize {
return Err(SystemError::ENAMETOOLONG);
}
let inode: Result<Arc<dyn IndexNode>, SystemError> = ROOT_INODE().lookup(path);
let inode: Arc<dyn IndexNode> = if inode.is_err() {
let errno = inode.unwrap_err();
// 文件不存在,且需要创建
if mode.contains(FileMode::O_CREAT)
&& !mode.contains(FileMode::O_DIRECTORY)
&& errno == SystemError::ENOENT
{
let (filename, parent_path) = rsplit_path(path);
// 查找父目录
let parent_inode: Arc<dyn IndexNode> =
ROOT_INODE().lookup(parent_path.unwrap_or("/"))?;
// 创建文件
let inode: Arc<dyn IndexNode> =
parent_inode.create(filename, FileType::File, 0o777)?;
inode
} else {
// 不需要创建文件,因此返回错误码
return Err(errno);
}
} else {
inode.unwrap()
};
let file_type: FileType = inode.metadata()?.file_type;
// 如果要打开的是文件夹,而目标不是文件夹
if mode.contains(FileMode::O_DIRECTORY) && file_type != FileType::Dir {
return Err(SystemError::ENOTDIR);
}
// 如果O_TRUNC并且打开模式包含O_RDWR或O_WRONLY清空文件
if mode.contains(FileMode::O_TRUNC)
&& (mode.contains(FileMode::O_RDWR) || mode.contains(FileMode::O_WRONLY))
&& file_type == FileType::File
{
inode.truncate(0)?;
}
// 创建文件对象
let mut file: File = File::new(inode, mode)?;
// 打开模式为“追加”
if mode.contains(FileMode::O_APPEND) {
file.lseek(SeekFrom::SeekEnd(0))?;
}
// 把文件对象存入pcb
return current_pcb().alloc_fd(file, None).map(|fd| fd as usize);
}
/// @brief 关闭文件
///
/// @param fd 文件描述符编号
///
/// @return 成功返回0失败返回错误码
pub fn close(fd: usize) -> Result<usize, SystemError> {
return current_pcb().drop_fd(fd as i32).map(|_| 0);
}
/// @brief 根据文件描述符读取文件数据。尝试读取的数据长度与buf的长度相同。
///
/// @param fd 文件描述符编号
/// @param buf 输出缓冲区。
///
/// @return Ok(usize) 成功读取的数据的字节数
/// @return Err(SystemError) 读取失败返回posix错误码
pub fn read(fd: i32, buf: &mut [u8]) -> Result<usize, SystemError> {
let file: Option<&mut File> = current_pcb().get_file_mut_by_fd(fd);
if file.is_none() {
return Err(SystemError::EBADF);
}
let file: &mut File = file.unwrap();
return file.read(buf.len(), buf);
}
/// @brief 根据文件描述符向文件写入数据。尝试写入的数据长度与buf的长度相同。
///
/// @param fd 文件描述符编号
/// @param buf 输入缓冲区。
///
/// @return Ok(usize) 成功写入的数据的字节数
/// @return Err(SystemError) 写入失败返回posix错误码
pub fn write(fd: i32, buf: &[u8]) -> Result<usize, SystemError> {
let file: Option<&mut File> = current_pcb().get_file_mut_by_fd(fd);
if file.is_none() {
return Err(SystemError::EBADF);
}
let file: &mut File = file.unwrap();
return file.write(buf.len(), buf);
}
/// @brief 调整文件操作指针的位置
///
/// @param fd 文件描述符编号
/// @param seek 调整的方式
///
/// @return Ok(usize) 调整后,文件访问指针相对于文件头部的偏移量
/// @return Err(SystemError) 调整失败返回posix错误码
pub fn lseek(fd: i32, seek: SeekFrom) -> Result<usize, SystemError> {
let file: Option<&mut File> = current_pcb().get_file_mut_by_fd(fd);
if file.is_none() {
return Err(SystemError::EBADF);
}
let file: &mut File = file.unwrap();
return file.lseek(seek);
}
/// @brief 切换工作目录
///
/// @param dest_path 目标路径
///
/// @return 返回码 描述
/// 0 | 成功
///
/// EACCESS | 权限不足
///
/// ELOOP | 解析path时遇到路径循环
///
/// ENAMETOOLONG | 路径名过长
///
/// ENOENT | 目标文件或目录不存在
///
/// ENODIR | 检索期间发现非目录项
///
/// ENOMEM | 系统内存不足
///
/// EFAULT | 错误的地址
///
/// ENAMETOOLONG | 路径过长
pub fn chdir(dest_path: &str) -> Result<usize, SystemError> {
// Copy path to kernel space to avoid some security issues
let path: Box<&str> = Box::new(dest_path);
let inode = match ROOT_INODE().lookup(&path) {
Err(e) => {
kerror!("Change Directory Failed, Error = {:?}", e);
return Err(SystemError::ENOENT);
}
Ok(i) => i,
};
match inode.metadata() {
Err(e) => {
kerror!("INode Get MetaData Failed, Error = {:?}", e);
return Err(SystemError::ENOENT);
}
Ok(i) => {
if let FileType::Dir = i.file_type {
return Ok(0);
} else {
return Err(SystemError::ENOTDIR);
}
}
}
}
/// @brief 获取目录中的数据
///
/// TODO: 这个函数的语义与Linux不一致需要修改
///
/// @param fd 文件描述符号
/// @param buf 输出缓冲区
///
/// @return 成功返回读取的字节数,失败返回错误码
pub fn getdents(fd: i32, buf: &mut [u8]) -> Result<usize, SystemError> {
let dirent =
unsafe { (buf.as_mut_ptr() as *mut Dirent).as_mut() }.ok_or(SystemError::EFAULT)?;
if fd < 0 || fd as u32 > PROC_MAX_FD_NUM {
return Err(SystemError::EBADF);
}
// 获取fd
let file: &mut File = match current_pcb().get_file_mut_by_fd(fd) {
None => {
return Err(SystemError::EBADF);
}
Some(file) => file,
};
// kdebug!("file={file:?}");
return file.readdir(dirent).map(|x| x as usize);
}
/// @brief 创建文件夹
///
/// @param path(r8) 路径 / mode(r9) 模式
///
/// @return uint64_t 负数错误码 / 0表示成功
pub fn mkdir(path: &str, mode: usize) -> Result<usize, SystemError> {
return do_mkdir(path, FileMode::from_bits_truncate(mode as u32)).map(|x| x as usize);
}
/// **删除文件夹、取消文件的链接、删除文件的系统调用**
///
/// ## 参数
///
/// - `dirfd`:文件夹的文件描述符.目前暂未实现
/// - `pathname`:文件夹的路径
/// - `flags`:标志位
///
///
pub fn unlinkat(_dirfd: i32, pathname: &str, flags: u32) -> Result<usize, SystemError> {
// kdebug!("sys_unlink_at={path:?}");
if (flags & (!AT_REMOVEDIR)) != 0 {
return Err(SystemError::EINVAL);
}
if (flags & AT_REMOVEDIR) > 0 {
// kdebug!("rmdir");
match do_remove_dir(&pathname) {
Err(err) => {
kerror!("Failed to Remove Directory, Error Code = {:?}", err);
return Err(err);
}
Ok(_) => {
return Ok(0);
}
}
}
match do_unlink_at(&pathname, FileMode::from_bits_truncate(flags as u32)) {
Err(err) => {
kerror!("Failed to Remove Directory, Error Code = {:?}", err);
return Err(err);
}
Ok(_) => {
return Ok(0);
}
}
}
/// @brief 根据提供的文件描述符的fd复制对应的文件结构体并返回新复制的文件结构体对应的fd
pub fn dup(oldfd: i32) -> Result<usize, SystemError> {
if let Some(fds) = FileDescriptorVec::from_pcb(current_pcb()) {
// 获得当前文件描述符数组
// 确认oldfd是否有效
if FileDescriptorVec::validate_fd(oldfd) {
if let Some(file) = &fds.fds[oldfd as usize] {
// 尝试获取对应的文件结构体
let file_cp: Box<File> = file.try_clone().ok_or(SystemError::EBADF)?;
// 申请文件描述符,并把文件对象存入其中
let res = current_pcb().alloc_fd(*file_cp, None).map(|x| x as usize);
return res;
}
// oldfd对应的文件不存在
return Err(SystemError::EBADF);
}
return Err(SystemError::EBADF);
} else {
return Err(SystemError::EMFILE);
}
}
/// 根据提供的文件描述符的fd和指定新fd复制对应的文件结构体
/// 并返回新复制的文件结构体对应的fd.
/// 如果新fd已经打开则会先关闭新fd.
///
/// ## 参数
///
/// - `oldfd`:旧文件描述符
/// - `newfd`:新文件描述符
///
/// ## 返回值
///
/// - 成功:新文件描述符
/// - 失败:错误码
pub fn dup2(oldfd: i32, newfd: i32) -> Result<usize, SystemError> {
if let Some(fds) = FileDescriptorVec::from_pcb(current_pcb()) {
// 获得当前文件描述符数组
if FileDescriptorVec::validate_fd(oldfd) && FileDescriptorVec::validate_fd(newfd) {
//确认oldfd, newid是否有效
if oldfd == newfd {
// 若oldfd与newfd相等
return Ok(newfd as usize);
}
if let Some(file) = &fds.fds[oldfd as usize] {
if fds.fds[newfd as usize].is_some() {
// close newfd
if let Err(_) = current_pcb().drop_fd(newfd) {
// An I/O error occurred while attempting to close fildes2.
return Err(SystemError::EIO);
}
}
// 尝试获取对应的文件结构体
let file_cp = file.try_clone();
if file_cp.is_none() {
return Err(SystemError::EBADF);
}
// 申请文件描述符,并把文件对象存入其中
let res = current_pcb()
.alloc_fd(*file_cp.unwrap(), Some(newfd))
.map(|x| x as usize);
return res;
}
return Err(SystemError::EBADF);
} else {
return Err(SystemError::EBADF);
}
}
// 从pcb获取文件描述符数组失败
return Err(SystemError::EMFILE);
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct IoVec {
/// 缓冲区的起始地址
pub iov_base: *mut u8,
/// 缓冲区的长度
pub iov_len: usize,
}
/// 用于存储多个来自用户空间的IoVec
///
/// 由于目前内核中的文件系统还不支持分散读写所以暂时只支持将用户空间的IoVec聚合成一个缓冲区然后进行操作。
/// TODO支持分散读写
#[derive(Debug)]
pub struct IoVecs(Vec<&'static mut [u8]>);
impl IoVecs {
/// 从用户空间的IoVec中构造IoVecs
///
/// @param iov 用户空间的IoVec
/// @param iovcnt 用户空间的IoVec的数量
/// @param readv 是否为readv系统调用
///
/// @return 构造成功返回IoVecs否则返回错误码
pub unsafe fn from_user(
iov: *const IoVec,
iovcnt: usize,
_readv: bool,
) -> Result<Self, SystemError> {
// 检查iov指针所在空间是否合法
if !verify_area(
iov as usize as u64,
(iovcnt * core::mem::size_of::<IoVec>()) as u64,
) {
return Err(SystemError::EFAULT);
}
// 将用户空间的IoVec转换为引用注意这里的引用是静态的因为用户空间的IoVec不会被释放
let iovs: &[IoVec] = core::slice::from_raw_parts(iov, iovcnt);
let mut slices: Vec<&mut [u8]> = vec![];
slices.reserve(iovs.len());
for iov in iovs.iter() {
if iov.iov_len == 0 {
continue;
}
if !verify_area(iov.iov_base as usize as u64, iov.iov_len as u64) {
return Err(SystemError::EFAULT);
}
slices.push(core::slice::from_raw_parts_mut(iov.iov_base, iov.iov_len));
}
return Ok(Self(slices));
}
/// @brief 将IoVecs中的数据聚合到一个缓冲区中
///
/// @return 返回聚合后的缓冲区
pub fn gather(&self) -> Vec<u8> {
let mut buf = Vec::new();
for slice in self.0.iter() {
buf.extend_from_slice(slice);
}
return buf;
}
/// @brief 将给定的数据分散写入到IoVecs中
pub fn scatter(&mut self, data: &[u8]) {
let mut data: &[u8] = data;
for slice in self.0.iter_mut() {
let len = core::cmp::min(slice.len(), data.len());
if len == 0 {
continue;
}
slice[..len].copy_from_slice(&data[..len]);
data = &data[len..];
}
}
/// @brief 创建与IoVecs等长的缓冲区
///
/// @param set_len 是否设置返回的Vec的len。
/// 如果为true则返回的Vec的len为所有IoVec的长度之和;
/// 否则返回的Vec的len为0capacity为所有IoVec的长度之和.
///
/// @return 返回创建的缓冲区
pub fn new_buf(&self, set_len: bool) -> Vec<u8> {
let total_len: usize = self.0.iter().map(|slice| slice.len()).sum();
let mut buf: Vec<u8> = Vec::with_capacity(total_len);
if set_len {
unsafe {
buf.set_len(total_len);
}
}
return buf;
}
}