Przeglądaj źródła

feat: 实现并测试 newfstatat 系统调用

新增了 newfstatat 系统调用的实现,并添加了相应的测试应用。

Signed-off-by: longjin <longjin@DragonOS.org>
longjin 2 dni temu
rodzic
commit
21a16bdeae

+ 1 - 0
kernel/src/arch/x86_64/filesystem/mod.rs

@@ -0,0 +1 @@
+pub mod stat;

+ 72 - 0
kernel/src/arch/x86_64/filesystem/stat.rs

@@ -0,0 +1,72 @@
+use system_error::SystemError;
+
+use crate::filesystem::vfs::stat::KStat;
+
+#[repr(C)]
+#[derive(Default, Clone, Copy)]
+pub struct PosixStat {
+    pub st_dev: usize,
+    pub st_ino: usize,
+    pub st_nlink: usize,
+    pub st_mode: u32,
+    pub st_uid: u32,
+    pub st_gid: u32,
+    pub __pad0: u32,
+    pub st_rdev: usize,
+    pub st_size: isize,
+    pub st_blksize: isize,
+    /// number of 512B blocks allocated
+    pub st_blocks: isize,
+    pub st_atime: usize,
+    pub st_atime_nsec: usize,
+    pub st_mtime: usize,
+    pub st_mtime_nsec: usize,
+    pub st_ctime: usize,
+    pub st_ctime_nsec: usize,
+    pub __unused: [isize; 3],
+}
+
+/// 转换的代码参考 https://code.dragonos.org.cn/xref/linux-6.6.21/fs/stat.c#393
+impl TryFrom<KStat> for PosixStat {
+    type Error = SystemError;
+
+    fn try_from(kstat: KStat) -> Result<Self, Self::Error> {
+        let mut tmp = PosixStat::default();
+        if core::mem::size_of_val(&tmp.st_dev) < 4 && !kstat.dev.old_valid_dev() {
+            return Err(SystemError::EOVERFLOW);
+        }
+        if core::mem::size_of_val(&tmp.st_rdev) < 4 && !kstat.rdev.old_valid_dev() {
+            return Err(SystemError::EOVERFLOW);
+        }
+
+        tmp.st_dev = kstat.dev.new_encode_dev() as usize;
+        tmp.st_ino = kstat.ino as usize;
+
+        if core::mem::size_of_val(&tmp.st_ino) < core::mem::size_of_val(&kstat.ino)
+            && tmp.st_ino != kstat.ino as usize
+        {
+            return Err(SystemError::EOVERFLOW);
+        }
+
+        tmp.st_mode = kstat.mode.bits();
+        tmp.st_nlink = kstat.nlink.try_into().map_err(|_| SystemError::EOVERFLOW)?;
+
+        // todo: 处理user namespace (https://code.dragonos.org.cn/xref/linux-6.6.21/fs/stat.c#415)
+        tmp.st_uid = kstat.uid;
+        tmp.st_gid = kstat.gid;
+
+        tmp.st_rdev = kstat.rdev.data() as usize;
+        tmp.st_size = kstat.size as isize;
+
+        tmp.st_atime = kstat.atime.tv_sec as usize;
+        tmp.st_mtime = kstat.mtime.tv_sec as usize;
+        tmp.st_ctime = kstat.ctime.tv_sec as usize;
+        tmp.st_atime_nsec = kstat.atime.tv_nsec as usize;
+        tmp.st_mtime_nsec = kstat.mtime.tv_nsec as usize;
+        tmp.st_ctime_nsec = kstat.ctime.tv_nsec as usize;
+        tmp.st_blocks = kstat.blocks as isize;
+        tmp.st_blksize = kstat.blksize as isize;
+
+        Ok(tmp)
+    }
+}

+ 1 - 0
kernel/src/arch/x86_64/mod.rs

@@ -4,6 +4,7 @@ mod acpi;
 pub mod cpu;
 pub mod driver;
 pub mod elf;
+pub mod filesystem;
 pub mod fpu;
 pub mod init;
 pub mod interrupt;

+ 11 - 0
kernel/src/driver/base/device/device_number.rs

@@ -60,6 +60,17 @@ impl DeviceNumber {
     pub const fn data(&self) -> u32 {
         self.data
     }
+
+    /// acceptable for old filesystems
+    pub const fn old_valid_dev(&self) -> bool {
+        (self.major().data() < 256) && (self.minor() < 256)
+    }
+
+    pub const fn new_encode_dev(&self) -> u32 {
+        let major = self.major().data();
+        let minor = self.minor();
+        return (minor & 0xff) | (major << 8) | ((minor & !0xff) << 12);
+    }
 }
 
 impl Default for DeviceNumber {

+ 1 - 0
kernel/src/filesystem/vfs/mod.rs

@@ -803,6 +803,7 @@ impl dyn IndexNode {
 #[builder(no_std, setter(into))]
 pub struct Metadata {
     /// 当前inode所在的文件系统的设备号
+    /// todo:更改为DeviceNumber结构体
     pub dev_id: usize,
 
     /// inode号

+ 72 - 8
kernel/src/filesystem/vfs/stat.rs

@@ -1,7 +1,10 @@
 use system_error::SystemError;
 
 use crate::{
+    arch::filesystem::stat::PosixStat,
+    driver::base::device::device_number::DeviceNumber,
     filesystem::vfs::{mount::is_mountpoint_root, vcore::do_file_lookup_at},
+    process::ProcessManager,
     syscall::user_access::UserBufferWriter,
     time::PosixTimeSpec,
 };
@@ -22,8 +25,8 @@ pub struct KStat {
     pub attributes: StxAttributes,
     pub attributes_mask: StxAttributes,
     pub ino: u64,
-    pub dev: i64,             // dev_t
-    pub rdev: i64,            // dev_t
+    pub dev: DeviceNumber,    // dev_t
+    pub rdev: DeviceNumber,   // dev_t
     pub uid: u32,             // kuid_t
     pub gid: u32,             // kgid_t
     pub size: usize,          // loff_t
@@ -257,8 +260,8 @@ pub fn vfs_getattr(
         kstat.attributes = StxAttributes::STATX_ATTR_APPEND;
         kstat.attributes_mask |=
             StxAttributes::STATX_ATTR_AUTOMOUNT | StxAttributes::STATX_ATTR_DAX;
-        kstat.dev = metadata.dev_id as i64;
-        kstat.rdev = metadata.raw_dev.data() as i64;
+        kstat.dev = DeviceNumber::from(metadata.dev_id as u32);
+        kstat.rdev = metadata.raw_dev;
     }
 
     // 把文件类型加入mode里面 (todo: 在具体的文件系统里面去实现这个操作。这里只是权宜之计)
@@ -267,6 +270,67 @@ pub fn vfs_getattr(
     return Ok(kstat);
 }
 
+/// 参考 https://code.dragonos.org.cn/xref/linux-6.6.21/fs/stat.c#274
+#[inline(never)]
+pub fn vfs_fstatat(dfd: i32, filename: &str, flags: AtFlags) -> Result<KStat, SystemError> {
+    let statx_flags = flags | AtFlags::AT_NO_AUTOMOUNT;
+    if dfd >= 0 && flags == AtFlags::AT_EMPTY_PATH {
+        return vfs_fstat(dfd);
+    }
+
+    return vfs_statx(
+        dfd,
+        filename,
+        statx_flags,
+        PosixStatxMask::STATX_BASIC_STATS,
+    );
+}
+
+/// vfs_fstat - Get the basic attributes by file descriptor
+///
+/// # Arguments
+/// - fd: The file descriptor referring to the file of interest
+///
+///  This function is a wrapper around vfs_getattr(). The main difference is
+///  that it uses a file descriptor to determine the file location.
+///
+/// 参考: https://code.dragonos.org.cn/xref/linux-6.6.21/fs/stat.c#190
+pub fn vfs_fstat(dfd: i32) -> Result<KStat, SystemError> {
+    // Get the file from the file descriptor
+    let pcb = ProcessManager::current_pcb();
+    let fd_table = pcb.fd_table();
+    let file = fd_table
+        .read()
+        .get_file_by_fd(dfd)
+        .ok_or(SystemError::EBADF)?;
+    let inode = file.inode();
+
+    // Get attributes using vfs_getattr with basic stats mask
+    vfs_getattr(&inode, PosixStatxMask::STATX_BASIC_STATS, AtFlags::empty())
+}
+
+pub(super) fn do_newfstatat(
+    dfd: i32,
+    filename: &str,
+    user_stat_buf_ptr: usize,
+    flags: u32,
+) -> Result<(), SystemError> {
+    let kstat = vfs_fstatat(dfd, filename, AtFlags::from_bits_truncate(flags as i32))?;
+
+    cp_new_stat(kstat, user_stat_buf_ptr)
+}
+
+/// 参考 https://code.dragonos.org.cn/xref/linux-6.6.21/fs/stat.c#393
+#[inline(never)]
+fn cp_new_stat(kstat: KStat, user_buf_ptr: usize) -> Result<(), SystemError> {
+    let posix_stat = PosixStat::try_from(kstat)?;
+    let mut ubuf_writer =
+        UserBufferWriter::new(user_buf_ptr as *mut PosixStat, size_of::<PosixStat>(), true)?;
+    ubuf_writer
+        .copy_one_to_user(&posix_stat, 0)
+        .map_err(|_| SystemError::EFAULT)
+}
+
 /// 参考 https://code.dragonos.org.cn/xref/linux-6.6.21/fs/stat.c#660
 pub(super) fn do_statx(
     dfd: i32,
@@ -319,10 +383,10 @@ fn cp_statx(kstat: KStat, user_buf_ptr: usize) -> Result<(), SystemError> {
     statx.stx_mtime = kstat.mtime;
 
     // Convert device numbers
-    statx.stx_rdev_major = ((kstat.rdev >> 32) & 0xffff_ffff) as u32; // MAJOR equivalent
-    statx.stx_rdev_minor = (kstat.rdev & 0xffff_ffff) as u32; // MINOR equivalent
-    statx.stx_dev_major = ((kstat.dev >> 32) & 0xffff_ffff) as u32; // MAJOR equivalent
-    statx.stx_dev_minor = (kstat.dev & 0xffff_ffff) as u32; // MINOR equivalent
+    statx.stx_rdev_major = kstat.rdev.major().data();
+    statx.stx_rdev_minor = kstat.rdev.minor();
+    statx.stx_dev_major = kstat.dev.major().data();
+    statx.stx_dev_minor = kstat.dev.minor();
 
     statx.stx_mnt_id = kstat.mnt_id;
     statx.stx_dio_mem_align = kstat.dio_mem_align;

+ 18 - 1
kernel/src/filesystem/vfs/syscall.rs

@@ -21,7 +21,7 @@ use crate::{
     time::{syscall::PosixTimeval, PosixTimeSpec},
 };
 
-use super::stat::{do_statx, PosixKstat};
+use super::stat::{do_newfstatat, do_statx, PosixKstat};
 use super::vcore::do_symlinkat;
 use super::{
     fcntl::{AtFlags, FcntlCommand, FD_CLOEXEC},
@@ -1374,6 +1374,23 @@ impl Syscall {
         do_statx(dfd, filename_str, flags, mask, user_kstat_ptr).map(|_| 0)
     }
 
+    #[inline(never)]
+    pub fn newfstatat(
+        dfd: i32,
+        filename_ptr: usize,
+        user_stat_buf_ptr: usize,
+        flags: u32,
+    ) -> Result<usize, SystemError> {
+        if user_stat_buf_ptr == 0 {
+            return Err(SystemError::EFAULT);
+        }
+
+        let filename = check_and_clone_cstr(filename_ptr as *const u8, Some(MAX_PATHLEN))?;
+        let filename_str = filename.to_str().map_err(|_| SystemError::EINVAL)?;
+
+        do_newfstatat(dfd, filename_str, user_stat_buf_ptr, flags).map(|_| 0)
+    }
+
     pub fn mknod(
         path: *const u8,
         mode: ModeType,

+ 1 - 5
kernel/src/syscall/mod.rs

@@ -1133,11 +1133,7 @@ impl Syscall {
             }
 
             #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
-            SYS_NEWFSTATAT => {
-                // todo: 这个系统调用还没有实现
-
-                Err(SystemError::ENOSYS)
-            }
+            SYS_NEWFSTATAT => Self::newfstatat(args[0] as i32, args[1], args[2], args[3] as u32),
 
             // SYS_SCHED_YIELD => Self::sched_yield(),
             SYS_UNAME => {

+ 1 - 0
user/apps/test_newfstatat/.gitignore

@@ -0,0 +1 @@
+test_newfstatat

+ 20 - 0
user/apps/test_newfstatat/Makefile

@@ -0,0 +1,20 @@
+ifeq ($(ARCH), x86_64)
+	CROSS_COMPILE=x86_64-linux-musl-
+else ifeq ($(ARCH), riscv64)
+	CROSS_COMPILE=riscv64-linux-musl-
+endif
+
+CC=$(CROSS_COMPILE)gcc
+
+.PHONY: all
+all: main.c
+	$(CC) -static -o test_newfstatat main.c
+
+.PHONY: install clean
+install: all
+	mv test_newfstatat $(DADK_CURRENT_BUILD_DIR)/test_newfstatat
+
+clean:
+	rm test_newfstatat *.o
+
+fmt:

+ 65 - 0
user/apps/test_newfstatat/main.c

@@ -0,0 +1,65 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/syscall.h>
+
+
+
+#define TEST_DIR "test_dir"
+#define TEST_FILE "test_file"
+
+void create_test_files() {
+    mkdir(TEST_DIR, 0755);
+    int fd = open(TEST_FILE, O_CREAT | O_RDWR, 0644);
+    if (fd >= 0) close(fd);
+}
+
+void cleanup_test_files() {
+    unlink(TEST_FILE);
+    rmdir(TEST_DIR);
+}
+
+void run_test(const char *name, int (*test_func)(), int expected) {
+    printf("Testing %s... ", name);
+    int result = test_func();
+    if (result == expected) {
+        printf("[PASS]\n");
+    } else {
+        printf("[FAILED] (expected %d, got %d)\n", expected, result);
+    }
+}
+
+int test_normal_file() {
+    struct stat st;
+    return syscall(__NR_newfstatat, AT_FDCWD, TEST_FILE, &st, 0);
+}
+
+int test_directory() {
+    struct stat st;
+    return syscall(__NR_newfstatat, AT_FDCWD, TEST_DIR, &st, 0);
+}
+
+int test_invalid_fd() {
+    struct stat st;
+    return syscall(__NR_newfstatat, -1, TEST_FILE, &st, 0);
+}
+
+int test_nonexistent_path() {
+    struct stat st;
+    return syscall(__NR_newfstatat, AT_FDCWD, "nonexistent_file", &st, 0);
+}
+
+int main() {
+    create_test_files();
+
+    run_test("normal file stat", test_normal_file, 0);
+    run_test("directory stat", test_directory, 0);
+    run_test("invalid file descriptor", test_invalid_fd, -1);
+    run_test("nonexistent path", test_nonexistent_path, -1);
+
+    cleanup_test_files();
+    return 0;
+}

+ 36 - 0
user/dadk/config/test_newfstatat-0.1.0.toml

@@ -0,0 +1,36 @@
+# 用户程序名称
+name = "test_newfstatat"
+# 版本号
+version = "0.1.0"
+# 用户程序描述信息
+description = "一个用来测试 newfstatat 能够正常运行的app"
+# (可选)默认: false 是否只构建一次,如果为true,DADK会在构建成功后,将构建结果缓存起来,下次构建时,直接使用缓存的构建结果
+build-once = false
+#  (可选) 默认: false 是否只安装一次,如果为true,DADK会在安装成功后,不再重复安装
+install-once = false
+# 目标架构
+# 可选值:"x86_64", "aarch64", "riscv64", "loongarch64"
+target-arch = ["x86_64", "riscv64"]
+# 任务源
+[task-source]
+# 构建类型
+# 可选值:"build-from_source", "install-from-prebuilt"
+type = "build-from-source"
+# 构建来源
+# "build_from_source" 可选值:"git", "local", "archive"
+# "install_from_prebuilt" 可选值:"local", "archive"
+source = "local"
+# 路径或URL
+source-path = "user/apps/test_newfstatat"
+# 构建相关信息
+[build]
+# (可选)构建命令
+build-command = "make install"
+# 安装相关信息
+[install]
+# (可选)安装到DragonOS的路径
+in-dragonos-path = "/bin"
+# 清除相关信息
+[clean]
+# (可选)清除命令
+clean-command = "make clean"