Browse Source

实现SYS_LINK和SYS_LINKAT (#611)

* 实现do_linkat及SYS_LINK和SYS_LINKAT

* 未在riscv上测试,添加target_arch

* 将c字符串检查移动到vfs/syscall.rs,修改do_linkat()逻辑

* 修改部分注释
Chenzx 11 months ago
parent
commit
4695947e1b
3 changed files with 125 additions and 1 deletions
  1. 106 0
      kernel/src/filesystem/vfs/syscall.rs
  2. 15 0
      kernel/src/syscall/mod.rs
  3. 4 1
      kernel/src/syscall/syscall_num.h

+ 106 - 0
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<usize, SystemError> {
+        // 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<dyn IndexNode> = 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<usize, SystemError> {
+        let get_path = |cstr: *const u8| -> Result<String, SystemError> {
+            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<usize, SystemError> {
+        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)
+    }
+
     /// **删除文件夹、取消文件的链接、删除文件的系统调用**
     ///
     /// ## 参数

+ 15 - 0
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;

+ 4 - 1
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