linfeng 6f189d2743
feat(time): Add syscall support for utime* (#838)
* feat(vfs): Add syscall support for utime*

impl sys_utimensat
impl sys_utimes
add utimensat test
fix some warning

* fix(vfs): Verify pointer validity

* fix: remove bad cfg
2024-06-27 17:43:25 +08:00

233 lines
7.5 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::sync::Arc;
use log::warn;
use system_error::SystemError;
use super::{
fcntl::AtFlags,
file::{File, FileMode},
syscall::{ModeType, OpenHow, OpenHowResolve},
utils::{rsplit_path, user_path_at},
FileType, IndexNode, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES,
};
use crate::filesystem::vfs::syscall::UtimensFlags;
use crate::time::{syscall::PosixTimeval, PosixTimeSpec};
use crate::{
driver::base::block::SeekFrom, process::ProcessManager,
syscall::user_access::check_and_clone_cstr,
};
use alloc::string::String;
pub(super) fn do_faccessat(
dirfd: i32,
path: *const u8,
mode: ModeType,
flags: u32,
) -> Result<usize, SystemError> {
if (mode.bits() & (!ModeType::S_IRWXO.bits())) != 0 {
return Err(SystemError::EINVAL);
}
if (flags
& (!((AtFlags::AT_EACCESS | AtFlags::AT_SYMLINK_NOFOLLOW | AtFlags::AT_EMPTY_PATH).bits()
as u32)))
!= 0
{
return Err(SystemError::EINVAL);
}
// let follow_symlink = flags & AtFlags::AT_SYMLINK_NOFOLLOW.bits() as u32 == 0;
let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?;
let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, &path)?;
// 如果找不到文件则返回错误码ENOENT
let _inode = inode.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
// todo: 接着完善可以借鉴linux 6.1.9的do_faccessat
return Ok(0);
}
pub fn do_fchmodat(dirfd: i32, path: *const u8, _mode: ModeType) -> Result<usize, SystemError> {
let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?;
let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, &path)?;
// 如果找不到文件则返回错误码ENOENT
let _inode = inode.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
warn!("do_fchmodat: not implemented yet\n");
// todo: 真正去改变文件的权限
return Ok(0);
}
pub(super) fn do_sys_open(
dfd: i32,
path: &str,
o_flags: FileMode,
mode: ModeType,
follow_symlink: bool,
) -> Result<usize, SystemError> {
let how = OpenHow::new(o_flags, mode, OpenHowResolve::empty());
return do_sys_openat2(dfd, path, how, follow_symlink);
}
fn do_sys_openat2(
dirfd: i32,
path: &str,
how: OpenHow,
follow_symlink: bool,
) -> Result<usize, SystemError> {
// debug!("open path: {}, how: {:?}", path, how);
let path = path.trim();
let (inode_begin, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?;
let inode: Result<Arc<dyn IndexNode>, SystemError> = inode_begin.lookup_follow_symlink(
&path,
if follow_symlink {
VFS_MAX_FOLLOW_SYMLINK_TIMES
} else {
0
},
);
let inode: Arc<dyn IndexNode> = match inode {
Ok(inode) => inode,
Err(errno) => {
// 文件不存在,且需要创建
if how.o_flags.contains(FileMode::O_CREAT)
&& !how.o_flags.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,
ModeType::from_bits_truncate(0o755),
)?;
inode
} else {
// 不需要创建文件,因此返回错误码
return Err(errno);
}
}
};
let file_type: FileType = inode.metadata()?.file_type;
// 如果要打开的是文件夹,而目标不是文件夹
if how.o_flags.contains(FileMode::O_DIRECTORY) && file_type != FileType::Dir {
return Err(SystemError::ENOTDIR);
}
// 创建文件对象
let file: File = File::new(inode, how.o_flags)?;
// 打开模式为“追加”
if how.o_flags.contains(FileMode::O_APPEND) {
file.lseek(SeekFrom::SeekEnd(0))?;
}
// 如果O_TRUNC并且打开模式包含O_RDWR或O_WRONLY清空文件
if how.o_flags.contains(FileMode::O_TRUNC)
&& (how.o_flags.contains(FileMode::O_RDWR) || how.o_flags.contains(FileMode::O_WRONLY))
&& file_type == FileType::File
{
file.ftruncate(0)?;
}
// 把文件对象存入pcb
let r = ProcessManager::current_pcb()
.fd_table()
.write()
.alloc_fd(file, None)
.map(|fd| fd as usize);
return r;
}
/// On Linux, futimens() is a library function implemented on top of
/// the utimensat() system call. To support this, the Linux
/// utimensat() system call implements a nonstandard feature: if
/// pathname is NULL, then the call modifies the timestamps of the
/// file referred to by the file descriptor dirfd (which may refer to
/// any type of file).
pub fn do_utimensat(
dirfd: i32,
pathname: Option<String>,
times: Option<[PosixTimeSpec; 2]>,
flags: UtimensFlags,
) -> Result<usize, SystemError> {
const UTIME_NOW: i64 = (1i64 << 30) - 1i64;
const UTIME_OMIT: i64 = (1i64 << 30) - 2i64;
// log::debug!("do_utimensat: dirfd:{}, pathname:{:?}, times:{:?}, flags:{:?}", dirfd, pathname, times, flags);
let inode = match pathname {
Some(path) => {
let (inode_begin, path) =
user_path_at(&ProcessManager::current_pcb(), dirfd, path.as_str())?;
let inode = if flags.contains(UtimensFlags::AT_SYMLINK_NOFOLLOW) {
inode_begin.lookup(path.as_str())?
} else {
inode_begin.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?
};
inode
}
None => {
let binding = ProcessManager::current_pcb().fd_table();
let fd_table_guard = binding.write();
let file = fd_table_guard
.get_file_by_fd(dirfd)
.ok_or(SystemError::EBADF)?;
file.inode()
}
};
let now = PosixTimeSpec::now();
let mut meta = inode.metadata()?;
if let Some([atime, mtime]) = times {
if atime.tv_nsec == UTIME_NOW {
meta.atime = now;
} else if atime.tv_nsec != UTIME_OMIT {
meta.atime = atime;
}
if mtime.tv_nsec == UTIME_NOW {
meta.mtime = now;
} else if mtime.tv_nsec != UTIME_OMIT {
meta.mtime = mtime;
}
inode.set_metadata(&meta).unwrap();
} else {
meta.atime = now;
meta.mtime = now;
inode.set_metadata(&meta).unwrap();
}
return Ok(0);
}
pub fn do_utimes(path: &str, times: Option<[PosixTimeval; 2]>) -> Result<usize, SystemError> {
// log::debug!("do_utimes: path:{:?}, times:{:?}", path, times);
let (inode_begin, path) = user_path_at(
&ProcessManager::current_pcb(),
AtFlags::AT_FDCWD.bits(),
path,
)?;
let inode = inode_begin.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
let mut meta = inode.metadata()?;
if let Some([atime, mtime]) = times {
meta.atime = PosixTimeSpec::from(atime);
meta.mtime = PosixTimeSpec::from(mtime);
inode.set_metadata(&meta)?;
} else {
let now = PosixTimeSpec::now();
meta.atime = now;
meta.mtime = now;
inode.set_metadata(&meta)?;
}
return Ok(0);
}