From bc6ea5ba9987dd826f10d2da20551aaac56072a0 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Wed, 20 May 2026 18:51:53 +0700 Subject: [PATCH] Implement key_t, sys/ipc and sys/shm --- src/header/bits_key-t/cbindgen.toml | 14 +++ src/header/bits_key-t/mod.rs | 1 + src/header/mod.rs | 6 +- src/header/sys_ipc/cbindgen.toml | 22 ++++ src/header/sys_ipc/mod.rs | 56 +++++++++ src/header/sys_shm/cbindgen.toml | 15 +++ src/header/sys_shm/mod.rs | 178 ++++++++++++++++++++++++++++ src/platform/types.rs | 2 + tests/Makefile.tests.mk | 1 + tests/expected/sys_shm/shm.stdout | 0 tests/sys_shm/shm.c | 35 ++++++ 11 files changed, 328 insertions(+), 2 deletions(-) create mode 100644 src/header/bits_key-t/cbindgen.toml create mode 100644 src/header/bits_key-t/mod.rs create mode 100644 src/header/sys_ipc/cbindgen.toml create mode 100644 src/header/sys_ipc/mod.rs create mode 100644 src/header/sys_shm/cbindgen.toml create mode 100644 src/header/sys_shm/mod.rs create mode 100644 tests/expected/sys_shm/shm.stdout create mode 100644 tests/sys_shm/shm.c diff --git a/src/header/bits_key-t/cbindgen.toml b/src/header/bits_key-t/cbindgen.toml new file mode 100644 index 0000000000..8ed688136d --- /dev/null +++ b/src/header/bits_key-t/cbindgen.toml @@ -0,0 +1,14 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_types.h.html +# +# POSIX headers that require key_t: +# - sys/ipc.h +sys_includes = [] +include_guard = "_RELIBC_BITS_KEY_T_H" +language = "C" +style = "type" +no_includes = true +cpp_compat = true +[export] +include = ["key_t"] +[enum] +prefix_with_name = true diff --git a/src/header/bits_key-t/mod.rs b/src/header/bits_key-t/mod.rs new file mode 100644 index 0000000000..5ef5041c62 --- /dev/null +++ b/src/header/bits_key-t/mod.rs @@ -0,0 +1 @@ +pub type key_t = core::ffi::c_int; diff --git a/src/header/mod.rs b/src/header/mod.rs index e0108e1037..230f60ed2e 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -18,6 +18,8 @@ pub mod bits_id_t; #[path = "bits_ino-t/mod.rs"] pub mod bits_ino_t; pub mod bits_iovec; +#[path = "bits_key-t/mod.rs"] +pub mod bits_key_t; #[path = "bits_locale-t/mod.rs"] pub mod bits_locale_t; #[path = "bits_mode-t/mod.rs"] @@ -132,14 +134,14 @@ pub mod sys_auxv; pub mod sys_epoll; pub mod sys_file; pub mod sys_ioctl; -// TODO: sys/ipc.h +pub mod sys_ipc; pub mod sys_mman; // TODO: sys/msg.h pub mod sys_ptrace; pub mod sys_resource; pub mod sys_select; // TODO: sys/sem.h -// TODO: sys/shm.h +pub mod sys_shm; pub mod sys_socket; pub mod sys_stat; pub mod sys_statvfs; diff --git a/src/header/sys_ipc/cbindgen.toml b/src/header/sys_ipc/cbindgen.toml new file mode 100644 index 0000000000..22735777ef --- /dev/null +++ b/src/header/sys_ipc/cbindgen.toml @@ -0,0 +1,22 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_ipc.h.html +# +# Spec quotations relating to includes: +# - The header shall define the uid_t, gid_t, mode_t, and key_t types as described in +sys_includes = [] +include_guard = "_SYS_IPC_H" +after_includes = """ +#include // for gid_t from sys/types.h +#include // for uid_t from sys/types.h +#include // for key_t from sys/types.h +#include // for mode_t from sys/types.h +""" +language = "C" +style = "Tag" + +[enum] +prefix_with_name = true + +[export] +include = [ + "ipc_perm", +] diff --git a/src/header/sys_ipc/mod.rs b/src/header/sys_ipc/mod.rs new file mode 100644 index 0000000000..5c2634007e --- /dev/null +++ b/src/header/sys_ipc/mod.rs @@ -0,0 +1,56 @@ +use crate::{ + c_str::CStr, + error::ResultExt, + header::sys_stat::stat, + out::Out, + platform::{ + Pal, Sys, + types::{c_char, c_int, c_ushort, gid_t, key_t, mode_t, uid_t}, + }, +}; + +pub const IPC_R: u32 = 0o400; +pub const IPC_W: u32 = 0o200; +pub const IPC_M: u32 = 0o10000; + +pub const IPC_RMID: i32 = 0; +pub const IPC_SET: i32 = 1; +pub const IPC_STAT: i32 = 2; +// pub const IPC_INFO: i32 = 3; non posix unimplemented + +pub const IPC_CREAT: i32 = 0o1000; +pub const IPC_EXCL: i32 = 0o2000; +pub const IPC_NOWAIT: i32 = 0o4000; + +pub const IPC_PRIVATE: key_t = 0; + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct ipc_perm { + pub __key: key_t, + pub uid: uid_t, + pub gid: gid_t, + pub cuid: uid_t, + pub cgid: gid_t, + pub mode: mode_t, + pub __seq: c_ushort, +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ftok(path: *const c_char, id: c_int) -> key_t { + let path = unsafe { CStr::from_ptr(path) }; + let mut stat = stat::default(); + if Sys::stat(path, Out::from_mut(&mut stat)) + .map(|()| 0) + .or_minus_one_errno() + == -1 + { + return -1; + } + + // Borrowed from musl + return (stat.st_ino & 0xffff) as key_t + | ((stat.st_dev & 0xff) << 16) as key_t + | ((id & 0xff) << 24); +} diff --git a/src/header/sys_shm/cbindgen.toml b/src/header/sys_shm/cbindgen.toml new file mode 100644 index 0000000000..0ab46ad2c1 --- /dev/null +++ b/src/header/sys_shm/cbindgen.toml @@ -0,0 +1,15 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_shm.h.html +# +# Spec quotations relating to includes: +# - The header shall define the type intptr_t as described in +# - In addition, the header shall include the header. +sys_includes = ["sys/ipc.h", "stdint.h"] +include_guard = "_SYS_SHM_H" +language = "C" +style = "Tag" + +[enum] +prefix_with_name = true + +[export.rename] +"ipc_perm" = "struct ipc_perm" diff --git a/src/header/sys_shm/mod.rs b/src/header/sys_shm/mod.rs new file mode 100644 index 0000000000..da7108a852 --- /dev/null +++ b/src/header/sys_shm/mod.rs @@ -0,0 +1,178 @@ +use core::mem; + +use crate::{ + error::ResultExt, + header::{ + errno::EINVAL, + fcntl::{O_CREAT, O_EXCL, O_RDWR}, + sys_ipc::{IPC_CREAT, IPC_EXCL, IPC_PRIVATE, IPC_RMID, IPC_SET, IPC_STAT, ipc_perm}, + sys_mman::{MAP_SHARED, PROT_READ, PROT_WRITE, shm_open, shm_unlink}, + sys_stat::{fchmod, fstat, stat}, + unistd::ftruncate, + }, + platform::{ + ERRNO, Pal, Sys, + types::{c_char, c_int, c_void, mode_t, pid_t, size_t, time_t}, + }, +}; + +#[allow(non_camel_case_types)] +pub type shmatt_t = core::ffi::c_ushort; + +pub const SHM_RDONLY: c_int = 0o10000; +pub const SHM_RND: c_int = 0o20000; +pub const SHMLBA: size_t = 4096; + +pub const SHM_FAILED: *mut c_void = -1isize as *mut c_void; + +#[repr(C)] +pub struct shmid_ds { + pub shm_perm: ipc_perm, + pub shm_segsz: size_t, + pub shm_lpid: pid_t, + pub shm_cpid: pid_t, + pub shm_nattch: shmatt_t, + pub shm_atime: time_t, + pub shm_dtime: time_t, + pub shm_ctime: time_t, +} + +// needed because shmdt isn't tracking size +#[derive(Copy, Clone)] +#[repr(C)] +struct ShmHeader { + total_size: size_t, +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn shmget(key: i32, size: size_t, shmflg: c_int) -> c_int { + let path_str = if key == IPC_PRIVATE { + format!("/sysv_priv_{}\0", Sys::getpid()) + } else { + format!("/sysv_key_{}\0", key) + }; + + let path_ptr = path_str.as_ptr() as *const c_char; + + let mut oflag = O_RDWR; + if (shmflg & IPC_CREAT) != 0 { + oflag |= O_CREAT; + } + if (shmflg & IPC_EXCL) != 0 { + oflag |= O_EXCL; + } + + let fd = unsafe { shm_open(path_ptr, oflag, 0o666) }; + if fd < 0 { + return -1; + } + + if (oflag & O_CREAT) != 0 { + let total_size = size + mem::size_of::(); + if ftruncate(fd, total_size as i64) < 0 { + return -1; + } + } + + unsafe { + let _ = shm_unlink(path_ptr); + } + + fd +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn shmat( + shmid: c_int, + _shmaddr: *const c_void, + shmflg: c_int, +) -> *mut c_void { + let mut stat = stat::default(); + if unsafe { fstat(shmid, &mut stat) } < 0 { + return SHM_FAILED; + } + let size = stat.st_size as usize; + let mut prot = PROT_READ; + if shmflg & SHM_RDONLY == 0 { + prot |= PROT_WRITE; + } + + let res = unsafe { Sys::mmap(core::ptr::null_mut(), size, prot, MAP_SHARED, shmid, 0) }; + let ptr = match res { + Ok(p) => p, + Err(_) => return SHM_FAILED, + }; + + let header = ptr as *mut ShmHeader; + unsafe { + (*header).total_size = size; + } + + unsafe { ptr.add(mem::size_of::()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn shmdt(shmaddr: *const c_void) -> c_int { + if shmaddr.is_null() || shmaddr == SHM_FAILED { + return -1; + } + + let base_ptr = unsafe { (shmaddr as *mut u8).sub(mem::size_of::()) }; + let header = base_ptr as *mut ShmHeader; + let total_size = unsafe { (*header).total_size }; + + unsafe { Sys::munmap(base_ptr as *mut c_void, total_size) } + .map(|_| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn shmctl(shmid: c_int, cmd: c_int, buf: *mut shmid_ds) -> c_int { + match cmd { + IPC_RMID => Sys::close(shmid).map(|_| 0).or_minus_one_errno(), + IPC_STAT => { + if buf.is_null() { + ERRNO.set(EINVAL); + return -1; + } + + let mut stat = stat::default(); + if unsafe { fstat(shmid, &mut stat) } < 0 { + return -1; + } + + unsafe { + let buf = &mut *buf; + buf.shm_segsz = + (stat.st_size as size_t).saturating_sub(mem::size_of::()); + buf.shm_cpid = stat.st_uid as pid_t; + buf.shm_lpid = stat.st_gid as pid_t; + buf.shm_atime = stat.st_atim.tv_sec; + buf.shm_dtime = stat.st_mtim.tv_sec; + buf.shm_ctime = stat.st_ctim.tv_sec; + buf.shm_nattch = stat.st_nlink as shmatt_t; + buf.shm_perm.uid = stat.st_uid; + buf.shm_perm.gid = stat.st_gid; + buf.shm_perm.mode = stat.st_mode & 0o777; + } + 0 + } + IPC_SET => { + if buf.is_null() { + ERRNO.set(EINVAL); + return -1; + } + + let mode = unsafe { (*buf).shm_perm.mode & 0o777 }; + fchmod(shmid, mode as mode_t) + } + _ => { + ERRNO.set(EINVAL); + -1 + } + } +} diff --git a/src/platform/types.rs b/src/platform/types.rs index bd964c5185..e7e041ef4c 100644 --- a/src/platform/types.rs +++ b/src/platform/types.rs @@ -118,6 +118,8 @@ pub type nlink_t = c_ulong; pub type blksize_t = c_long; /// The `blkcnt_t` type provided in [`sys/types.h`](crate::header::sys_types). pub type blkcnt_t = c_longlong; +/// The `key_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type key_t = c_int; /// The `fsblkcnt_t` type provided in [`sys/types.h`](crate::header::sys_types). pub type fsblkcnt_t = c_ulong; diff --git a/tests/Makefile.tests.mk b/tests/Makefile.tests.mk index c0928d117a..15637742f6 100644 --- a/tests/Makefile.tests.mk +++ b/tests/Makefile.tests.mk @@ -138,6 +138,7 @@ EXPECT_NAMES=\ string/stpcpy \ string/stpncpy \ strings \ + sys_shm/shm \ sys_socket/recv \ sys_socket/recvfrom \ sys_socket/unixrecv \ diff --git a/tests/expected/sys_shm/shm.stdout b/tests/expected/sys_shm/shm.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/sys_shm/shm.c b/tests/sys_shm/shm.c new file mode 100644 index 0000000000..58bc38dbcd --- /dev/null +++ b/tests/sys_shm/shm.c @@ -0,0 +1,35 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) +{ + int status; + key_t key = ftok("example_dir/1-never-gonna-give-you-up", 123); + size_t size = 1000; + + int shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0666); + ERROR_IF(shmget, shmid, == -1); + + struct shmid_ds ds; + status = shmctl(shmid, IPC_STAT, &ds); + ERROR_IF(shmctl, status, == -1); + UNEXP_IF(shmctl, ds.shm_segsz, != size); + + void *shmaddr = shmat(shmid, NULL, 0); + UNEXP_IF(shmat, shmaddr, == SHM_FAILED); + + char *msg = "foo bar"; + strcpy((char *)shmaddr, msg); + + status = strcmp((char *)shmaddr, msg); + UNEXP_IF(strcmp, status, != 0); + + status = shmdt(shmaddr); + ERROR_IF(shmdt, status, == -1); + + status = shmctl(shmid, IPC_RMID, NULL); + ERROR_IF(shmctl, status, == -1); +} \ No newline at end of file