diff --git a/kernel/src/filesystem/vfs/syscall.rs b/kernel/src/filesystem/vfs/syscall.rs index 7c5b81fd..52cb9403 100644 --- a/kernel/src/filesystem/vfs/syscall.rs +++ b/kernel/src/filesystem/vfs/syscall.rs @@ -737,6 +737,112 @@ impl Syscall { return do_mkdir(&path, FileMode::from_bits_truncate(mode as u32)).map(|x| x as usize); } + /// **创建硬连接的系统调用** + /// + /// ## 参数 + /// + /// - 'oldfd': 用于解析源文件路径的文件描述符 + /// - 'old': 源文件路径 + /// - 'newfd': 用于解析新文件路径的文件描述符 + /// - 'new': 新文件将创建的路径 + /// - 'flags': 标志位,仅以位或方式包含AT_EMPTY_PATH和AT_SYMLINK_FOLLOW + /// + /// + pub fn do_linkat( + oldfd: i32, + old: &str, + newfd: i32, + new: &str, + flags: AtFlags, + ) -> Result { + // flag包含其他未规定值时返回EINVAL + if !(AtFlags::AT_EMPTY_PATH | AtFlags::AT_SYMLINK_FOLLOW).contains(flags) { + return Err(SystemError::EINVAL); + } + // TODO AT_EMPTY_PATH标志启用时,进行调用者CAP_DAC_READ_SEARCH或相似的检查 + let symlink_times = if flags.contains(AtFlags::AT_SYMLINK_FOLLOW) { + 0 as usize + } else { + VFS_MAX_FOLLOW_SYMLINK_TIMES + }; + let pcb = ProcessManager::current_pcb(); + + // 得到源路径的inode + let old_inode: Arc = if old.is_empty() { + if flags.contains(AtFlags::AT_EMPTY_PATH) { + // 在AT_EMPTY_PATH启用时,old可以为空,old_inode实际为oldfd所指文件,但该文件不能为目录。 + let binding = pcb.fd_table(); + let fd_table_guard = binding.read(); + let file = fd_table_guard + .get_file_by_fd(oldfd) + .ok_or(SystemError::EBADF)?; + let old_inode = file.lock().inode(); + old_inode + } else { + return Err(SystemError::ENONET); + } + } else { + let (old_begin_inode, old_remain_path) = user_path_at(&pcb, oldfd, old)?; + old_begin_inode.lookup_follow_symlink(&old_remain_path, symlink_times)? + }; + + // old_inode为目录时返回EPERM + if old_inode.metadata().unwrap().file_type == FileType::Dir { + return Err(SystemError::EPERM); + } + + // 得到新创建节点的父节点 + let (new_begin_inode, new_remain_path) = user_path_at(&pcb, newfd, new)?; + let (new_name, new_parent_path) = rsplit_path(&new_remain_path); + let new_parent = new_begin_inode + .lookup_follow_symlink(&new_parent_path.unwrap_or("/"), symlink_times)?; + + // 被调用者利用downcast_ref判断两inode是否为同一文件系统 + return new_parent.link(&new_name, &old_inode).map(|_| 0); + } + + pub fn link(old: *const u8, new: *const u8) -> Result { + let get_path = |cstr: *const u8| -> Result { + let res = check_and_clone_cstr(cstr, Some(MAX_PATHLEN))?; + if res.len() >= MAX_PATHLEN { + return Err(SystemError::ENAMETOOLONG); + } + if res.is_empty() { + return Err(SystemError::ENOENT); + } + Ok(res) + }; + let old = get_path(old)?; + let new = get_path(new)?; + return Self::do_linkat( + AtFlags::AT_FDCWD.bits() as i32, + &old, + AtFlags::AT_FDCWD.bits() as i32, + &new, + AtFlags::empty(), + ); + } + + pub fn linkat( + oldfd: i32, + old: *const u8, + newfd: i32, + new: *const u8, + flags: i32, + ) -> Result { + let old = check_and_clone_cstr(old, Some(MAX_PATHLEN))?; + let new = check_and_clone_cstr(new, Some(MAX_PATHLEN))?; + if old.len() >= MAX_PATHLEN || new.len() >= MAX_PATHLEN { + return Err(SystemError::ENAMETOOLONG); + } + // old 根据flags & AtFlags::AT_EMPTY_PATH判空 + if new.is_empty() { + return Err(SystemError::ENOENT); + } + let flags = AtFlags::from_bits(flags).ok_or(SystemError::EINVAL)?; + Self::do_linkat(oldfd, &old, newfd, &new, flags) + } + /// **删除文件夹、取消文件的链接、删除文件的系统调用** /// /// ## 参数 diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index fc345d60..4464b846 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -345,6 +345,21 @@ impl Syscall { Self::rmdir(path) } + SYS_LINK => { + let old = args[0] as *const u8; + let new = args[1] as *const u8; + return Self::link(old, new); + } + + SYS_LINKAT => { + let oldfd = args[0] as i32; + let old = args[1] as *const u8; + let newfd = args[2] as i32; + let new = args[3] as *const u8; + let flags = args[4] as i32; + return Self::linkat(oldfd, old, newfd, new, flags); + } + #[cfg(target_arch = "x86_64")] SYS_UNLINK => { let path = args[0] as *const u8; diff --git a/kernel/src/syscall/syscall_num.h b/kernel/src/syscall/syscall_num.h index 1f75dab0..184a5722 100644 --- a/kernel/src/syscall/syscall_num.h +++ b/kernel/src/syscall/syscall_num.h @@ -65,9 +65,10 @@ #define SYS_CHDIR 80 #define SYS_MKDIR 83 - #define SYS_RMDIR 84 +#define SYS_LINK 86 + #define SYS_GETTIMEOFDAY 96 #define SYS_ARCH_PRCTL 158 @@ -86,6 +87,8 @@ #define SYS_UNLINK_AT 263 +#define SYS_LINKAT 265 + #define SYS_PIPE 293 #define SYS_WRITEV 20