diff --git a/kernel/src/arch/loongarch64/filesystem/mod.rs b/kernel/src/arch/loongarch64/filesystem/mod.rs new file mode 100644 index 00000000..4f22af63 --- /dev/null +++ b/kernel/src/arch/loongarch64/filesystem/mod.rs @@ -0,0 +1 @@ +pub mod stat; diff --git a/kernel/src/arch/loongarch64/filesystem/stat.rs b/kernel/src/arch/loongarch64/filesystem/stat.rs new file mode 100644 index 00000000..6402d6ff --- /dev/null +++ b/kernel/src/arch/loongarch64/filesystem/stat.rs @@ -0,0 +1 @@ +pub use crate::filesystem::vfs::stat::GenericPosixStat as PosixStat; diff --git a/kernel/src/arch/loongarch64/mod.rs b/kernel/src/arch/loongarch64/mod.rs index 3b0f5486..9b605480 100644 --- a/kernel/src/arch/loongarch64/mod.rs +++ b/kernel/src/arch/loongarch64/mod.rs @@ -1,6 +1,7 @@ pub mod asm; pub mod cpu; pub mod elf; +pub mod filesystem; pub mod init; pub mod interrupt; pub mod ipc; diff --git a/kernel/src/arch/riscv64/filesystem/mod.rs b/kernel/src/arch/riscv64/filesystem/mod.rs new file mode 100644 index 00000000..4f22af63 --- /dev/null +++ b/kernel/src/arch/riscv64/filesystem/mod.rs @@ -0,0 +1 @@ +pub mod stat; diff --git a/kernel/src/arch/riscv64/filesystem/stat.rs b/kernel/src/arch/riscv64/filesystem/stat.rs new file mode 100644 index 00000000..6402d6ff --- /dev/null +++ b/kernel/src/arch/riscv64/filesystem/stat.rs @@ -0,0 +1 @@ +pub use crate::filesystem::vfs::stat::GenericPosixStat as PosixStat; diff --git a/kernel/src/arch/riscv64/mod.rs b/kernel/src/arch/riscv64/mod.rs index c2b5e309..584b8059 100644 --- a/kernel/src/arch/riscv64/mod.rs +++ b/kernel/src/arch/riscv64/mod.rs @@ -2,6 +2,7 @@ pub mod asm; pub mod cpu; pub mod driver; pub mod elf; +pub mod filesystem; pub mod init; pub mod interrupt; pub mod ipc; diff --git a/kernel/src/arch/x86_64/filesystem/mod.rs b/kernel/src/arch/x86_64/filesystem/mod.rs new file mode 100644 index 00000000..4f22af63 --- /dev/null +++ b/kernel/src/arch/x86_64/filesystem/mod.rs @@ -0,0 +1 @@ +pub mod stat; diff --git a/kernel/src/arch/x86_64/filesystem/stat.rs b/kernel/src/arch/x86_64/filesystem/stat.rs new file mode 100644 index 00000000..9eec8d02 --- /dev/null +++ b/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 for PosixStat { + type Error = SystemError; + + fn try_from(kstat: KStat) -> Result { + 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) + } +} diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index cb8404e4..9ebac953 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/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; diff --git a/kernel/src/driver/base/device/device_number.rs b/kernel/src/driver/base/device/device_number.rs index 78c4c319..1fe3d0ea 100644 --- a/kernel/src/driver/base/device/device_number.rs +++ b/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 { diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index 875f3d28..55df722d 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/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号 diff --git a/kernel/src/filesystem/vfs/stat.rs b/kernel/src/filesystem/vfs/stat.rs index 1adb68d7..65df11a5 100644 --- a/kernel/src/filesystem/vfs/stat.rs +++ b/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 { + 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 { + // 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::(), 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; @@ -396,3 +460,95 @@ impl PosixKstat { } } } + +/// 通用的PosixStat +#[allow(unused)] +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct GenericPosixStat { + /// Device ID + pub st_dev: u64, + /// File serial number (inode) + pub st_ino: u64, + /// File mode + pub st_mode: u32, + /// Link count + pub st_nlink: u32, + /// User ID of the file's owner + pub st_uid: u32, + /// Group ID of the file's group + pub st_gid: u32, + /// Device number, if device + pub st_rdev: u64, + /// Padding + pub __pad1: u64, + /// Size of file, in bytes + pub st_size: i64, + /// Optimal block size for I/O + pub st_blksize: i32, + /// Padding + pub __pad2: i32, + /// Number 512-byte blocks allocated + pub st_blocks: i64, + /// Time of last access (seconds) + pub st_atime: i64, + /// Time of last access (nanoseconds) + pub st_atime_nsec: u64, + /// Time of last modification (seconds) + pub st_mtime: i64, + /// Time of last modification (nanoseconds) + pub st_mtime_nsec: u64, + /// Time of last status change (seconds) + pub st_ctime: i64, + /// Time of last status change (nanoseconds) + pub st_ctime_nsec: u64, + /// Unused + pub __unused4: u32, + /// Unused + pub __unused5: u32, +} + +/// 转换的代码参考 https://code.dragonos.org.cn/xref/linux-6.6.21/fs/stat.c#393 +impl TryFrom for GenericPosixStat { + type Error = SystemError; + + fn try_from(kstat: KStat) -> Result { + let mut tmp = GenericPosixStat::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 u64; + tmp.st_ino = kstat.ino; + + if core::mem::size_of_val(&tmp.st_ino) < core::mem::size_of_val(&kstat.ino) + && tmp.st_ino != kstat.ino + { + return Err(SystemError::EOVERFLOW); + } + + tmp.st_mode = kstat.mode.bits(); + tmp.st_nlink = kstat.nlink; + + // 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 u64; + tmp.st_size = kstat.size as i64; + + tmp.st_atime = kstat.atime.tv_sec; + tmp.st_mtime = kstat.mtime.tv_sec; + tmp.st_ctime = kstat.ctime.tv_sec; + tmp.st_atime_nsec = kstat.atime.tv_nsec as u64; + tmp.st_mtime_nsec = kstat.mtime.tv_nsec as u64; + tmp.st_ctime_nsec = kstat.ctime.tv_nsec as u64; + tmp.st_blocks = kstat.blocks as i64; + tmp.st_blksize = kstat.blksize as i32; + + Ok(tmp) + } +} diff --git a/kernel/src/filesystem/vfs/syscall.rs b/kernel/src/filesystem/vfs/syscall.rs index 50732103..2ec58b7f 100644 --- a/kernel/src/filesystem/vfs/syscall.rs +++ b/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}, @@ -1376,6 +1376,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 { + 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, diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 9f1f39a8..b8ed05f6 100644 --- a/kernel/src/syscall/mod.rs +++ b/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 => { diff --git a/user/apps/test_newfstatat/.gitignore b/user/apps/test_newfstatat/.gitignore new file mode 100644 index 00000000..47c6d617 --- /dev/null +++ b/user/apps/test_newfstatat/.gitignore @@ -0,0 +1 @@ +test_newfstatat diff --git a/user/apps/test_newfstatat/Makefile b/user/apps/test_newfstatat/Makefile new file mode 100644 index 00000000..bfd2cd45 --- /dev/null +++ b/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: diff --git a/user/apps/test_newfstatat/main.c b/user/apps/test_newfstatat/main.c new file mode 100644 index 00000000..3411fd88 --- /dev/null +++ b/user/apps/test_newfstatat/main.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include +#include + + + +#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; +} diff --git a/user/dadk/config/test_newfstatat-0.1.0.toml b/user/dadk/config/test_newfstatat-0.1.0.toml new file mode 100644 index 00000000..37e68910 --- /dev/null +++ b/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"