Browse Source

feat: sys_readlink && sys_readlinkat (#436)

LoGin 1 year ago
parent
commit
709498cac1
3 changed files with 142 additions and 2 deletions
  1. 52 0
      kernel/src/filesystem/vfs/fcntl.rs
  2. 70 2
      kernel/src/filesystem/vfs/syscall.rs
  3. 20 0
      kernel/src/syscall/mod.rs

+ 52 - 0
kernel/src/filesystem/vfs/fcntl.rs

@@ -58,5 +58,57 @@ pub enum FcntlCommand {
     SetFileRwHint = F_LINUX_SPECIFIC_BASE + 14,
 }
 
+///  The constants AT_REMOVEDIR and AT_EACCESS have the same value.  AT_EACCESS is
+///  meaningful only to faccessat, while AT_REMOVEDIR is meaningful only to
+///  unlinkat.  The two functions do completely different things and therefore,
+///  the flags can be allowed to overlap.  For example, passing AT_REMOVEDIR to
+///  faccessat would be undefined behavior and thus treating it equivalent to
+///  AT_EACCESS is valid undefined behavior.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)]
+#[allow(non_camel_case_types)]
+pub enum AtFlags {
+    /// 特殊值,用于指示openat应使用当前工作目录。
+    AtFdCwd = -100,
+    /// 不要跟随符号链接。
+    /// AT_SYMLINK_NOFOLLOW: 0x100
+    AtSymlinkNoFollow = 0x100,
+    /// AtEAccess: 使用有效ID进行访问测试,而不是实际ID。
+    /// AtRemoveDir: 删除目录而不是取消链接文件。
+    /// AT_EACCESS: 0x200
+    /// AT_REMOVEDIR: 0x200
+    AtEAccess_OR_AtRemoveDir = 0x200,
+
+    /// 跟随符号链接。
+    /// AT_SYMLINK_FOLLOW: 0x400
+    AtSymlinkFollow = 0x400,
+    /// 禁止终端自动挂载遍历。
+    /// AT_NO_AUTOMOUNT: 0x800
+    AtNoAutomount = 0x800,
+    /// 允许空的相对路径名。
+    /// AT_EMPTY_PATH: 0x1000
+    AtEmptyPath = 0x1000,
+    /// statx()所需的同步类型。
+    /// AT_STATX_SYNC_TYPE: 0x6000
+    AtStatxSyncType = 0x6000,
+    /// 执行与stat()相同的操作。
+    /// AT_STATX_SYNC_AS_STAT: 0x0000
+    AtStatxSyncAsStat = 0x0000,
+    /// 强制将属性与服务器同步。
+    /// AT_STATX_FORCE_SYNC: 0x2000
+    AtStatxForceSync = 0x2000,
+    /// 不要将属性与服务器同步。
+    /// AT_STATX_DONT_SYNC: 0x4000
+    AtStatxDontSync = 0x4000,
+    /// 应用于整个子树。
+    /// AT_RECURSIVE: 0x8000
+    AtRecursive = 0x8000,
+}
+
+impl Into<i32> for AtFlags {
+    fn into(self) -> i32 {
+        self as i32
+    }
+}
+
 /// for F_[GET|SET]FL
 pub const FD_CLOEXEC: u32 = 1;

+ 70 - 2
kernel/src/filesystem/vfs/syscall.rs

@@ -14,13 +14,16 @@ use crate::{
     libs::rwlock::RwLockWriteGuard,
     mm::VirtAddr,
     process::ProcessManager,
-    syscall::{user_access::UserBufferReader, Syscall, SystemError},
+    syscall::{
+        user_access::{check_and_clone_cstr, UserBufferReader, UserBufferWriter},
+        Syscall, SystemError,
+    },
     time::TimeSpec,
 };
 
 use super::{
     core::{do_mkdir, do_remove_dir, do_unlink_at},
-    fcntl::{FcntlCommand, FD_CLOEXEC},
+    fcntl::{AtFlags, FcntlCommand, FD_CLOEXEC},
     file::{File, FileMode},
     utils::rsplit_path,
     Dirent, FileType, IndexNode, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES,
@@ -794,7 +797,72 @@ impl Syscall {
 
         Self::write(fd, &data)
     }
+
+    pub fn readlink_at(
+        dirfd: i32,
+        path: *const u8,
+        user_buf: *mut u8,
+        buf_size: usize,
+    ) -> Result<usize, SystemError> {
+        let mut path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?;
+        let mut user_buf = UserBufferWriter::new(user_buf, buf_size, true)?;
+
+        if path.len() == 0 {
+            return Err(SystemError::EINVAL);
+        }
+
+        let mut inode = ROOT_INODE();
+        // 如果path不是绝对路径,则需要拼接
+        if path.as_bytes()[0] != b'/' {
+            // 如果dirfd不是AT_FDCWD,则需要检查dirfd是否是目录
+            if dirfd != AtFlags::AtFdCwd.into() {
+                let binding = ProcessManager::current_pcb().fd_table();
+                let fd_table_guard = binding.read();
+                let file = fd_table_guard
+                    .get_file_by_fd(dirfd)
+                    .ok_or(SystemError::EBADF)?;
+
+                // drop guard 以避免无法调度的问题
+                drop(fd_table_guard);
+
+                let file_guard = file.lock();
+                // 如果dirfd不是目录,则返回错误码ENOTDIR
+                if file_guard.file_type() != FileType::Dir {
+                    return Err(SystemError::ENOTDIR);
+                }
+
+                inode = file_guard.inode();
+            } else {
+                let mut cwd = ProcessManager::current_pcb().basic().cwd();
+                cwd.push('/');
+                cwd.push_str(path.as_str());
+                path = cwd;
+            }
+        }
+
+        let inode = inode.lookup(path.as_str())?;
+        if inode.metadata()?.file_type != FileType::SymLink {
+            return Err(SystemError::EINVAL);
+        }
+
+        let ubuf = user_buf.buffer::<u8>(0).unwrap();
+
+        let mut file = File::new(inode, FileMode::O_RDONLY)?;
+
+        let len = file.read(buf_size, ubuf)?;
+
+        return Ok(len);
+    }
+
+    pub fn readlink(
+        path: *const u8,
+        user_buf: *mut u8,
+        buf_size: usize,
+    ) -> Result<usize, SystemError> {
+        return Self::readlink_at(AtFlags::AtFdCwd.into(), path, user_buf, buf_size);
+    }
 }
+
 #[repr(C)]
 #[derive(Debug, Clone, Copy)]
 pub struct IoVec {

+ 20 - 0
kernel/src/syscall/mod.rs

@@ -399,6 +399,8 @@ pub const SYS_CHDIR: usize = 80;
 
 pub const SYS_MKDIR: usize = 83;
 
+pub const SYS_READLINK: usize = 89;
+
 pub const SYS_GETTIMEOFDAY: usize = 96;
 pub const SYS_GETRUSAGE: usize = 98;
 
@@ -437,6 +439,8 @@ pub const SYS_EXIT_GROUP: usize = 231;
 
 pub const SYS_UNLINK_AT: usize = 263;
 
+pub const SYS_READLINK_AT: usize = 267;
+
 pub const SYS_ACCEPT4: usize = 288;
 
 pub const SYS_PIPE: usize = 293;
@@ -1171,6 +1175,22 @@ impl Syscall {
                 Self::get_rusage(who, rusage)
             }
 
+            SYS_READLINK =>{
+                let path = args[0] as *const u8;
+                let buf = args[1] as *mut u8;
+                let bufsiz = args[2] as usize;
+                Self::readlink(path, buf, bufsiz)
+
+            }
+
+            SYS_READLINK_AT =>{
+                let dirfd = args[0] as i32;
+                let pathname = args[1] as *const u8;
+                let buf = args[2] as *mut u8;
+                let bufsiz = args[3] as usize;
+                Self::readlink_at(dirfd, pathname, buf, bufsiz)
+            }
+
             _ => panic!("Unsupported syscall ID: {}", syscall_num),
         };
         return r;