Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion etc/syscalls_linux_aarch64.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@
| 0x120 (288) | pkey_mprotect | (unsigned long start, size_t len, unsigned long prot, int pkey) | __arm64_sys_pkey_mprotect | false |
| 0x121 (289) | pkey_alloc | (unsigned long flags, unsigned long init_val) | __arm64_sys_pkey_alloc | false |
| 0x122 (290) | pkey_free | (int pkey) | __arm64_sys_pkey_free | false |
| 0x123 (291) | statx | (int dfd, const char *filename, unsigned flags, unsigned int mask, struct statx *buffer) | __arm64_sys_statx | false |
| 0x123 (291) | statx | (int dfd, const char *filename, unsigned flags, unsigned int mask, struct statx *buffer) | __arm64_sys_statx | true |
| 0x124 (292) | io_pgetevents | (aio_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct __kernel_timespec *timeout, const struct __aio_sigset *usig) | __arm64_sys_io_pgetevents | false |
| 0x125 (293) | rseq | (struct rseq *rseq, u32 rseq_len, int flags, u32 sig) | __arm64_sys_rseq | ENOSYS |
| 0x126 (294) | kexec_file_load | (int kernel_fd, int initrd_fd, unsigned long cmdline_len, const char *cmdline_ptr, unsigned long flags) | __arm64_sys_kexec_file_load | false |
Expand Down
2 changes: 2 additions & 0 deletions libkernel/src/fs/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub struct FileAttr {
pub block_size: u32,
pub blocks: u64,
pub atime: Duration, // Access time (e.g., seconds since epoch)
pub btime: Duration, // Creation time
pub mtime: Duration, // Modification time
pub ctime: Duration, // Change time
pub file_type: FileType,
Expand All @@ -69,6 +70,7 @@ impl Default for FileAttr {
block_size: 0,
blocks: 0,
atime: Duration::new(0, 0),
btime: Duration::new(0, 0),
mtime: Duration::new(0, 0),
ctime: Duration::new(0, 0),
file_type: FileType::File,
Expand Down
11 changes: 8 additions & 3 deletions libkernel/src/fs/filesystems/tmpfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,14 @@ where
G: PageAllocGetter<C>,
T: AddressTranslator<()>,
{
fn new(id: InodeId) -> Result<Self> {
fn new(id: InodeId, mode: FilePermissions) -> Result<Self> {
Ok(Self {
id,
attr: SpinLockIrq::new(FileAttr {
file_type: FileType::File,
size: 0,
nlinks: 1,
mode,
..Default::default()
}),
inner: SpinLockIrq::new(TmpFsRegInner {
Expand Down Expand Up @@ -434,7 +435,7 @@ where
let inode_id = InodeId::from_fsid_and_inodeid(fs.id(), new_id);

let inode: Arc<dyn Inode> = match file_type {
FileType::File => Arc::new(TmpFsReg::<C, G, T>::new(inode_id)?),
FileType::File => Arc::new(TmpFsReg::<C, G, T>::new(inode_id, mode)?),
FileType::Directory => TmpFsDirInode::<C, G, T>::new(new_id, self.fs.clone(), mode),
_ => return Err(KernelError::NotSupported),
};
Expand Down Expand Up @@ -817,7 +818,11 @@ mod tests {
) {
init_allocator();
let fs = TmpFs::new(0);
let reg = TmpFsReg::new(InodeId::from_fsid_and_inodeid(0, 1024)).unwrap();
let reg = TmpFsReg::new(
InodeId::from_fsid_and_inodeid(0, 1024),
FilePermissions::all(),
)
.unwrap();
(fs, reg)
}

Expand Down
14 changes: 14 additions & 0 deletions libkernel/src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,20 @@ pub enum FileType {
Socket,
}

impl From<FileType> for u32 {
fn from(file_type: FileType) -> Self {
match file_type {
FileType::Directory => 0o040000,
FileType::CharDevice(_) => 0o020000,
FileType::BlockDevice(_) => 0o060000,
FileType::File => 0o100000,
FileType::Fifo => 0o010000,
FileType::Symlink => 0o120000,
FileType::Socket => 0o140000,
}
}
}

/// A stateful, streaming iterator for reading directory entries.
#[async_trait]
pub trait DirStream: Send + Sync {
Expand Down
11 changes: 11 additions & 0 deletions src/arch/arm64/exceptions/syscall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::{
readlink::sys_readlinkat,
rename::{sys_renameat, sys_renameat2},
stat::sys_newfstatat,
statx::sys_statx,
symlink::sys_symlinkat,
unlink::sys_unlinkat,
utime::sys_utimensat,
Expand Down Expand Up @@ -420,6 +421,16 @@ pub async fn handle_syscall() {
)
.await
}
0x123 => {
sys_statx(
arg1.into(),
TUA::from_value(arg2 as _),
arg3 as _,
arg4 as _,
TUA::from_value(arg5 as _),
)
.await
}
0x125 => Err(KernelError::NotSupported),
0x1b7 => {
sys_faccessat2(
Expand Down
8 changes: 8 additions & 0 deletions src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,14 @@ impl VFS {
.exchange(old_name, new_parent_inode, new_name)
.await
}

pub fn is_mount_root(&self, id: InodeId) -> bool {
self.state
.lock_save_irq()
.mounts
.values()
.any(|mount| mount.root_inode.id() == id)
}
}

pub static VFS: VFS = VFS::new();
Expand Down
1 change: 1 addition & 0 deletions src/fs/syscalls/at/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub mod open;
pub mod readlink;
pub mod rename;
pub mod stat;
pub mod statx;
pub mod symlink;
pub mod unlink;
pub mod utime;
Expand Down
14 changes: 2 additions & 12 deletions src/fs/syscalls/at/stat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
use core::ffi::c_char;
use libkernel::{
error::Result,
fs::{FileType, attr::FileAttr, path::Path},
fs::{attr::FileAttr, path::Path},
memory::address::TUA,
};

Expand Down Expand Up @@ -42,20 +42,10 @@ unsafe impl UserCopyable for Stat {}

impl From<FileAttr> for Stat {
fn from(value: FileAttr) -> Self {
let type_val = match value.file_type {
FileType::Directory => 0o040000,
FileType::CharDevice(_) => 0o020000,
FileType::BlockDevice(_) => 0o060000,
FileType::File => 0o100000,
FileType::Fifo => 0o010000,
FileType::Symlink => 0o120000,
FileType::Socket => 0o140000,
};

Self {
st_dev: value.id.fs_id(),
st_ino: value.id.inode_id(),
st_mode: value.mode.bits() as u32 | type_val,
st_mode: value.mode.bits() as u32 | u32::from(value.file_type),
st_nlink: value.nlinks,
st_uid: value.uid.into(),
st_gid: value.gid.into(),
Expand Down
210 changes: 210 additions & 0 deletions src/fs/syscalls/at/statx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
use crate::{
current_task,
fs::{
VFS,
syscalls::at::{resolve_at_start_node, resolve_path_flags},
},
memory::uaccess::{UserCopyable, copy_to_user, cstr::UserCStr},
process::fd_table::Fd,
};
use core::{ffi::c_char, time::Duration};
use libkernel::{error::Result, fs::path::Path, memory::address::TUA};

use super::AtFlags;

bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StatXMask: u32 {
const STATX_TYPE = 0x0001;
const STATX_MODE = 0x0002;
const STATX_NLINK = 0x0004;
const STATX_UID = 0x0008;
const STATX_GID = 0x0010;
const STATX_ATIME = 0x0020;
const STATX_MTIME = 0x0040;
const STATX_CTIME = 0x0080;
const STATX_INO = 0x0100;
const STATX_SIZE = 0x0200;
const STATX_BLOCKS = 0x0400;
const STATX_BASIC_STATS = 0x07ff;
const STATX_BTIME = 0x0800;
const STATX_ALL = 0x0fff;
const STATX_MNT_ID = 0x1000;
const STATX_DIOALIGN = 0x2000;
const STATX_MNT_ID_UNIQUE = 0x4000;
const STATX_SUBVOL = 0x8000;
const STATX_WRITE_ATOMIC = 0x10000;
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StatXAttr: u64 {
const STATX_ATTR_COMPRESSED = 0x0004;
const STATX_ATTR_IMMUTABLE = 0x0010;
const STATX_ATTR_APPEND = 0x0020;
const STATX_ATTR_NODUMP = 0x0040;
const STATX_ATTR_ENCRYPTED = 0x0800;
const STATX_ATTR_AUTOMOUNT = 0x1000;
const STATX_ATTR_MOUNT_ROOT = 0x2000;
const STATX_ATTR_VERITY = 0x100000;
const STATX_ATTR_DAX = 0x200000;
const STATX_ATTR_NOSECURITY = 0x00400000;
}
}

#[repr(C)]
#[derive(Debug, Default, Clone, Copy)]
pub struct StatX {
pub stx_mask: u32, // Mask of supported fields
pub stx_blksize: u32, // Block size
pub stx_attributes: u64, // Attributes
pub stx_nlink: u32, // Link count
pub stx_uid: u32, // User ID of owner
pub stx_gid: u32, // Group ID of group
pub stx_mode: u16, // File mode
pub __pad1: u16, // Padding
pub stx_ino: u64, // Inode number
pub stx_size: u64, // Size
pub stx_blocks: u64, // Number of blocks allocated
pub stx_attributes_mask: u64, // Mask of supported attributes
pub stx_atime: StatXTimestamp, // Access time
pub stx_btime: StatXTimestamp, // Creation time
pub stx_ctime: StatXTimestamp, // Change time
pub stx_mtime: StatXTimestamp, // Modification time

// Currently not supported on any current filesystems
pub stx_rdev_major: u32, // Device major ID
pub stx_rdev_minor: u32, // Device minor ID
pub stx_dev_major: u32, // Filesystem major ID
pub stx_dev_minor: u32, // Filesystem minor ID
pub stx_mnt_id: u64, // Mount ID
pub stx_dio_mem_align: u32, // Alignment of memory for direct I/O
pub stx_dio_offset_align: u32, // Alignment of offset for direct I/O
pub stx_subvol: u64, // Subvolume ID
pub stx_atomic_write_unit_min: u32, // Minimum atomic write direct I/O size
pub stx_atomic_write_unit_max: u32, // Maximum atomic write direct I/O size
pub stx_atomic_write_segments_max: u32, // Maximum number of segments for atomic writes
pub stx_dio_read_offset_align: u32, // Alignment of offset for direct I/O read
pub stx_atomic_write_unit_max_opt: u32, // Maximum size optimized for atomic writes

// Unused
pub __unused1: u64,
pub __unused2: u64,
pub __unused3: u64,
pub __unused4: u64,
pub __unused5: u64,
pub __unused6: u64,
}

#[repr(C)]
#[derive(Debug, Default, Clone, Copy)]
pub struct StatXTimestamp {
pub tv_sec: i64,
pub tv_nsec: u32,
pub __pad1: i32,
}

impl From<Duration> for StatXTimestamp {
fn from(duration: Duration) -> Self {
Self {
tv_sec: duration.as_secs() as i64,
tv_nsec: duration.subsec_nanos(),
__pad1: 0,
}
}
}

unsafe impl UserCopyable for StatX {}

pub async fn sys_statx(
dirfd: Fd,
path: TUA<c_char>,
flags: i32,
mask: u32,
statbuf: TUA<StatX>,
) -> Result<usize> {
let mut buf = [0; 1024];

let task = current_task();
let flags = AtFlags::from_bits_truncate(flags);
let mask = StatXMask::from_bits_truncate(mask);
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);

let start_node = resolve_at_start_node(dirfd, path).await?;
let node = resolve_path_flags(dirfd, path, start_node, task.clone(), flags).await?;

let attr = node.getattr().await?;

let mut stat_x = StatX::default();

// TODO: right now, the attr is applied unconditionally even if the data is not supported, as
// long as the input mask is set. at some point, the fs should be checked if these attributes
// are supported and will return useful data.
if mask.contains(StatXMask::STATX_TYPE) {
stat_x.stx_mask |= StatXMask::STATX_TYPE.bits();
stat_x.stx_mode = u32::from(attr.file_type) as u16;
}

if mask.contains(StatXMask::STATX_MODE) {
stat_x.stx_mask |= StatXMask::STATX_MODE.bits();
stat_x.stx_mode |= attr.mode.bits() as u16;
}

if mask.contains(StatXMask::STATX_NLINK) {
stat_x.stx_mask |= StatXMask::STATX_NLINK.bits();
stat_x.stx_nlink = attr.nlinks;
}

if mask.contains(StatXMask::STATX_UID) {
stat_x.stx_mask |= StatXMask::STATX_UID.bits();
stat_x.stx_uid = attr.uid.into();
}

if mask.contains(StatXMask::STATX_GID) {
stat_x.stx_mask |= StatXMask::STATX_GID.bits();
stat_x.stx_gid = attr.gid.into();
}

if mask.contains(StatXMask::STATX_ATIME) {
stat_x.stx_mask |= StatXMask::STATX_ATIME.bits();
stat_x.stx_atime = attr.atime.into();
}

if mask.contains(StatXMask::STATX_MTIME) {
stat_x.stx_mask |= StatXMask::STATX_MTIME.bits();
stat_x.stx_mtime = attr.mtime.into();
}

if mask.contains(StatXMask::STATX_CTIME) {
stat_x.stx_mask |= StatXMask::STATX_CTIME.bits();
stat_x.stx_ctime = attr.ctime.into();
}

if mask.contains(StatXMask::STATX_INO) {
stat_x.stx_mask |= StatXMask::STATX_INO.bits();
stat_x.stx_ino = attr.id.inode_id();
}

if mask.contains(StatXMask::STATX_SIZE) {
stat_x.stx_mask |= StatXMask::STATX_SIZE.bits();
stat_x.stx_size = attr.size;
}

if mask.contains(StatXMask::STATX_BLOCKS) {
stat_x.stx_mask |= StatXMask::STATX_BLOCKS.bits();
stat_x.stx_blocks = attr.blocks;
}

if mask.contains(StatXMask::STATX_BTIME) {
stat_x.stx_mask |= StatXMask::STATX_BTIME.bits();
stat_x.stx_btime = attr.btime.into();
}

stat_x.stx_attributes_mask = StatXAttr::STATX_ATTR_MOUNT_ROOT.bits();
if VFS.is_mount_root(attr.id) {
stat_x.stx_attributes |= StatXAttr::STATX_ATTR_MOUNT_ROOT.bits();
}

copy_to_user(statbuf, stat_x).await?;

Ok(0)
}
Loading