diff --git a/docs/src/kernel/linux-compatibility.md b/docs/src/kernel/linux-compatibility.md index 7c3555606..ce81a3a53 100644 --- a/docs/src/kernel/linux-compatibility.md +++ b/docs/src/kernel/linux-compatibility.md @@ -340,6 +340,7 @@ provided by Linux on x86-64 architecture. | 322 | execveat | ✅ | | 327 | preadv2 | ✅ | | 328 | pwritev2 | ✅ | +| 332 | statx | ✅ | | 435 | clone3 | ✅ | ## File Systems diff --git a/kernel/src/syscall/arch/riscv.rs b/kernel/src/syscall/arch/riscv.rs index b943b487f..73468906b 100644 --- a/kernel/src/syscall/arch/riscv.rs +++ b/kernel/src/syscall/arch/riscv.rs @@ -120,6 +120,7 @@ use crate::syscall::{ socketpair::sys_socketpair, stat::{sys_fstat, sys_fstatat}, statfs::{sys_fstatfs, sys_statfs}, + statx::sys_statx, symlink::sys_symlinkat, sync::sys_sync, tgkill::sys_tgkill, @@ -287,6 +288,7 @@ impl_syscall_nums_and_dispatch_fn! { SYS_EXECVEAT = 281 => sys_execveat(args[..5], &mut user_ctx); SYS_PREADV2 = 286 => sys_preadv2(args[..5]); SYS_PWRITEV2 = 287 => sys_pwritev2(args[..5]); + SYS_STATX = 291 => sys_statx(args[..5]); SYS_PRLIMIT64 = 302 => sys_prlimit64(args[..4]); SYS_CLOCK_GETTIME = 403 => sys_clock_gettime(args[..2]); SYS_CLOCK_NANOSLEEP = 407 => sys_clock_nanosleep(args[..4]); diff --git a/kernel/src/syscall/arch/x86.rs b/kernel/src/syscall/arch/x86.rs index d763ba737..bf1bc1e10 100644 --- a/kernel/src/syscall/arch/x86.rs +++ b/kernel/src/syscall/arch/x86.rs @@ -133,6 +133,7 @@ use crate::syscall::{ socketpair::sys_socketpair, stat::{sys_fstat, sys_fstatat, sys_lstat, sys_stat}, statfs::{sys_fstatfs, sys_statfs}, + statx::sys_statx, symlink::{sys_symlink, sys_symlinkat}, sync::sys_sync, sysinfo::sys_sysinfo, @@ -357,5 +358,6 @@ impl_syscall_nums_and_dispatch_fn! { SYS_EXECVEAT = 322 => sys_execveat(args[..5], &mut user_ctx); SYS_PREADV2 = 327 => sys_preadv2(args[..5]); SYS_PWRITEV2 = 328 => sys_pwritev2(args[..5]); + SYS_STATX = 332 => sys_statx(args[..5]); SYS_CLONE3 = 435 => sys_clone3(args[..2], &user_ctx); } diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index ff45f72cc..70c15844a 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -140,6 +140,7 @@ mod socket; mod socketpair; mod stat; mod statfs; +mod statx; mod symlink; mod sync; mod sysinfo; diff --git a/kernel/src/syscall/statx.rs b/kernel/src/syscall/statx.rs new file mode 100644 index 000000000..f3607109c --- /dev/null +++ b/kernel/src/syscall/statx.rs @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: MPL-2.0 + +use ::core::time::Duration; + +use super::SyscallReturn; +use crate::{ + fs::{device::DeviceId, file_table::FileDesc, fs_resolver::FsPath, utils::Metadata}, + prelude::*, + syscall::constants::MAX_FILENAME_LEN, +}; + +pub fn sys_statx( + dirfd: FileDesc, + filename_ptr: Vaddr, + flags: u32, + mask: u32, + statx_buf_ptr: Vaddr, + ctx: &Context, +) -> Result { + let user_space = ctx.user_space(); + let filename = user_space.read_cstring(filename_ptr, MAX_FILENAME_LEN)?; + let flags = StatxFlags::from_bits(flags) + .ok_or(Error::with_message(Errno::EINVAL, "invalid statx flags"))?; + let mask = StatxMask::from_bits_truncate(mask); + debug!( + "dirfd = {}, filename = {:?}, flags = {:?}, mask = {:?}, statx_buf_ptr = 0x{:x}", + dirfd, filename, flags, mask, statx_buf_ptr, + ); + + if filename.is_empty() && !flags.contains(StatxFlags::AT_EMPTY_PATH) { + return_errno_with_message!(Errno::ENOENT, "path is empty"); + } + + if flags.contains(StatxFlags::AT_STATX_FORCE_SYNC) + && flags.contains(StatxFlags::AT_STATX_DONT_SYNC) + { + return_errno_with_message!(Errno::EINVAL, "invalid statx flags"); + } + + if mask.contains(StatxMask::STATX_RESERVED) { + return_errno_with_message!( + Errno::EINVAL, + "mask reserved for future struct statx expansion" + ); + } + + let dentry = { + let filename = filename.to_string_lossy(); + let fs_path = FsPath::new(dirfd, filename.as_ref())?; + let fs = ctx.posix_thread.fs().resolver().read(); + if flags.contains(StatxFlags::AT_SYMLINK_NOFOLLOW) { + fs.lookup_no_follow(&fs_path)? + } else { + fs.lookup(&fs_path)? + } + }; + + let statx = Statx::from(dentry.metadata()); + + user_space.write_val(statx_buf_ptr, &statx)?; + Ok(SyscallReturn::Return(0)) +} + +/// Structures for the extended file attribute retrieval system call statx. +#[derive(Debug, Clone, Copy, Pod, Default)] +#[repr(C)] +pub struct Statx { + /// Indicates which fields in the `statx` structure were successfully filled, + /// reflecting the state information supported by the filesystem. + stx_mask: u32, + /// Preferred general I/O size + stx_blksize: u32, + /// Flags conveying information about the file + stx_attributes: u64, + /// Number of hard links + stx_nlink: u32, + /// User ID of owner + stx_uid: u32, + /// Group ID of owner + stx_gid: u32, + /// File mode + stx_mode: u16, + /// Padding + __spare0: [u16; 1], + /// Inode number + stx_ino: u64, + /// File size + stx_size: u64, + /// Number of 512-byte blocks allocated + stx_blocks: u64, + /// Mask to show what's supported in stx_attributes + stx_attributes_mask: u64, + /// Last access time + stx_atime: StatxTimestamp, + /// File creation time + stx_btime: StatxTimestamp, + /// Last attribute change time + stx_ctime: StatxTimestamp, + /// Last data modification time + stx_mtime: StatxTimestamp, + /// Device ID of special file (if bdev/cdev) + stx_rdev_major: u32, + stx_rdev_minor: u32, + /// ID of device containing file + stx_dev_major: u32, + stx_dev_minor: u32, + /// Mount ID + stx_mnt_id: u64, + /// Memory buffer alignment for direct I/O + stx_dio_mem_align: u32, + /// File offset alignment for direct I/O + stx_dio_offset_align: u32, + /// Spare space for future expansion + __spare3: [u64; 12], +} + +impl From for Statx { + fn from(info: Metadata) -> Self { + let devid = DeviceId::from(info.dev); + let rdevid = DeviceId::from(info.rdev); + Self { + // FIXME: All zero fields below are dummy implementations that need to be improved in the future. + stx_mask: 0, + stx_blksize: info.blk_size as u32, + stx_attributes: 0, + stx_nlink: info.nlinks as u32, + stx_uid: info.uid.into(), + stx_gid: info.gid.into(), + stx_mode: info.type_ as u16 | info.mode.bits(), + __spare0: [0; 1], + stx_ino: info.ino, + stx_size: info.size as u64, + stx_blocks: (info.blocks * (info.blk_size / 512)) as u64, + stx_attributes_mask: 0, + stx_atime: StatxTimestamp::from(info.atime), + stx_btime: StatxTimestamp::from(info.atime), + stx_ctime: StatxTimestamp::from(info.ctime), + stx_mtime: StatxTimestamp::from(info.ctime), + stx_rdev_major: rdevid.major(), + stx_rdev_minor: rdevid.minor(), + stx_dev_major: devid.major(), + stx_dev_minor: devid.minor(), + stx_mnt_id: 0, + stx_dio_mem_align: 0, + stx_dio_offset_align: 0, + __spare3: [0; 12], + } + } +} + +/// Statx Timestamp (seconds and nanoseconds) +#[derive(Debug, Clone, Copy, Pod, Default)] +#[repr(C)] +pub struct StatxTimestamp { + /// Seconds + tv_sec: i64, + /// Nanoseconds + tv_nsec: u32, + __reserved: i32, +} + +impl From for StatxTimestamp { + fn from(duration: Duration) -> Self { + Self { + tv_sec: duration.as_secs() as i64, + tv_nsec: duration.subsec_nanos() as u32, + __reserved: 0, + } + } +} + +bitflags! { + /// Flags can be used to influence a pathname-based lookup. + /// Flags can also be used to control what sort of synchronization the + /// kernel will do when querying a file on a remote filesystem. + struct StatxFlags: u32 { + const AT_EMPTY_PATH = 1 << 12; // Allow empty relative pathname to operate on dirfd directly. + const AT_NO_AUTOMOUNT = 1 << 11; // Suppress terminal automount traversal. + const AT_SYMLINK_NOFOLLOW = 1 << 8; // Do not follow symbolic links. + const AT_STATX_SYNC_AS_STAT = 0; // Do whatever stat() does. + const AT_STATX_FORCE_SYNC = 1 << 13; // Force the attributes to be sync'd with the server. + const AT_STATX_DONT_SYNC = 1 << 14; // Don't sync attributes with the server. + } +} + +bitflags! { + /// Flags to be stx_mask. + /// Query request/result mask for statx() and struct statx::stx_mask. + /// These bits should be set in the mask argument of statx() to request + /// particular items when calling statx(). + pub struct StatxMask: u32 { + const STATX_TYPE = 0x00000001; // Want stx_mode & S_IFMT + const STATX_MODE = 0x00000002; // Want stx_mode & ~S_IFMT + const STATX_NLINK = 0x00000004; // Want stx_nlink + const STATX_UID = 0x00000008; // Want stx_uid + const STATX_GID = 0x00000010; // Want stx_gid + const STATX_ATIME = 0x00000020; // Want stx_atime + const STATX_MTIME = 0x00000040; // Want stx_mtime + const STATX_CTIME = 0x00000080; // Want stx_ctime + const STATX_INO = 0x00000100; // Want stx_ino + const STATX_SIZE = 0x00000200; // Want stx_size + const STATX_BLOCKS = 0x00000400; // Want stx_blocks + const STATX_BASIC_STATS = 0x000007ff; // All of the above (stx_mode, stx_nlink, etc.) + const STATX_BTIME = 0x00000800; // Want stx_btime + const STATX_ALL = 0x00000fff; // Deprecated: The same as STATX_BASIC_STATS | STATX_BTIME + const STATX_MNT_ID = 0x00001000; // Want stx_mnt_id + const STATX_DIOALIGN = 0x00002000; // Want stx_dio_mem_align and stx_dio_offset_align + const STATX_MNT_ID_UNIQUE = 0x00004000; // Want unique stx_mnt_id + const STATX_RESERVED = 0x80000000; // Reserved for future struct statx expansion + const STATX_CHANGE_COOKIE = 0x40000000; // Want/got stx_change_attr + } +}