relibc/header/sys_shm/
mod.rs1use core::mem;
2
3use crate::{
4 error::ResultExt,
5 header::{
6 errno::EINVAL,
7 fcntl::{O_CREAT, O_EXCL, O_RDWR},
8 sys_ipc::{IPC_CREAT, IPC_EXCL, IPC_PRIVATE, IPC_RMID, IPC_SET, IPC_STAT, ipc_perm},
9 sys_mman::{MAP_SHARED, PROT_READ, PROT_WRITE, shm_open, shm_unlink},
10 sys_stat::{fchmod, fstat, stat},
11 unistd::ftruncate,
12 },
13 platform::{
14 ERRNO, Pal, Sys,
15 types::{c_char, c_int, c_void, key_t, mode_t, pid_t, size_t, time_t},
16 },
17};
18
19#[allow(non_camel_case_types)]
20pub type shmatt_t = core::ffi::c_ushort;
21
22pub const SHM_RDONLY: c_int = 0o10000;
24pub const SHM_RND: c_int = 0o20000;
26pub const SHMLBA: size_t = 4096;
28
29pub const SHM_FAILED: *mut c_void = -1isize as *mut c_void;
31
32#[repr(C)]
33pub struct shmid_ds {
34 pub shm_perm: ipc_perm,
35 pub shm_segsz: size_t,
36 pub shm_lpid: pid_t,
37 pub shm_cpid: pid_t,
38 pub shm_nattch: shmatt_t,
39 pub shm_atime: time_t,
40 pub shm_dtime: time_t,
41 pub shm_ctime: time_t,
42}
43
44#[derive(Copy, Clone)]
46#[repr(C)]
47struct ShmHeader {
48 total_size: size_t,
49}
50
51#[unsafe(no_mangle)]
53pub unsafe extern "C" fn shmget(key: key_t, size: size_t, shmflg: c_int) -> c_int {
54 let path_str = if key == IPC_PRIVATE {
55 format!("/sysv_priv_{}\0", Sys::getpid())
56 } else {
57 format!("/sysv_key_{}\0", key)
58 };
59
60 let path_ptr = path_str.as_ptr().cast::<c_char>();
61
62 let mut oflag = O_RDWR;
63 if (shmflg & IPC_CREAT) != 0 {
64 oflag |= O_CREAT;
65 }
66 if (shmflg & IPC_EXCL) != 0 {
67 oflag |= O_EXCL;
68 }
69
70 let fd = unsafe { shm_open(path_ptr, oflag, 0o666) };
71 if fd < 0 {
72 return -1;
73 }
74
75 if (oflag & O_CREAT) != 0 {
76 let total_size = match size.checked_add(mem::size_of::<ShmHeader>()) {
77 Some(s) => s,
78 None => {
79 ERRNO.set(EINVAL);
80 return -1;
81 }
82 };
83 if ftruncate(fd, total_size as i64) < 0 {
84 return -1;
85 }
86 }
87
88 unsafe {
89 let _ = shm_unlink(path_ptr);
90 }
91
92 fd
93}
94
95#[unsafe(no_mangle)]
97pub unsafe extern "C" fn shmat(
98 shmid: c_int,
99 _shmaddr: *const c_void,
100 shmflg: c_int,
101) -> *mut c_void {
102 let mut stat = stat::default();
103 if unsafe { fstat(shmid, &raw mut stat) } < 0 {
104 return SHM_FAILED;
105 }
106 let size = stat.st_size as usize;
107 let mut prot = PROT_READ;
108 if shmflg & SHM_RDONLY == 0 {
109 prot |= PROT_WRITE;
110 }
111
112 let res = unsafe { Sys::mmap(core::ptr::null_mut(), size, prot, MAP_SHARED, shmid, 0) };
113 let ptr = match res {
114 Ok(p) => p,
115 Err(_) => return SHM_FAILED,
116 };
117
118 let header = ptr.cast::<ShmHeader>();
119 unsafe {
120 (*header).total_size = size;
121 }
122
123 unsafe { ptr.add(mem::size_of::<ShmHeader>()) }
124}
125
126#[unsafe(no_mangle)]
128pub unsafe extern "C" fn shmdt(shmaddr: *const c_void) -> c_int {
129 if shmaddr.is_null() || shmaddr == SHM_FAILED {
130 return -1;
131 }
132
133 let base_ptr = unsafe { (shmaddr as *mut u8).sub(mem::size_of::<ShmHeader>()) };
134 let header = base_ptr.cast::<ShmHeader>();
135 let total_size = unsafe { (*header).total_size };
136
137 unsafe { Sys::munmap(base_ptr.cast::<c_void>(), total_size) }
138 .map(|()| 0)
139 .or_minus_one_errno()
140}
141
142#[unsafe(no_mangle)]
144pub unsafe extern "C" fn shmctl(shmid: c_int, cmd: c_int, buf: *mut shmid_ds) -> c_int {
145 match cmd {
146 IPC_RMID => Sys::close(shmid).map(|()| 0).or_minus_one_errno(),
147 IPC_STAT => {
148 if buf.is_null() {
149 ERRNO.set(EINVAL);
150 return -1;
151 }
152
153 let mut stat = stat::default();
154 if unsafe { fstat(shmid, &raw mut stat) } < 0 {
155 return -1;
156 }
157
158 unsafe {
159 let buf = &mut *buf;
160 buf.shm_segsz =
161 (stat.st_size as size_t).saturating_sub(mem::size_of::<ShmHeader>());
162 buf.shm_cpid = stat.st_uid as pid_t;
163 buf.shm_lpid = stat.st_gid as pid_t;
164 buf.shm_atime = stat.st_atim.tv_sec;
165 buf.shm_dtime = stat.st_mtim.tv_sec;
166 buf.shm_ctime = stat.st_ctim.tv_sec;
167 buf.shm_nattch = stat.st_nlink as shmatt_t;
168 buf.shm_perm.uid = stat.st_uid;
169 buf.shm_perm.gid = stat.st_gid;
170 buf.shm_perm.mode = stat.st_mode & 0o777;
171 }
172 0
173 }
174 IPC_SET => {
175 if buf.is_null() {
176 ERRNO.set(EINVAL);
177 return -1;
178 }
179
180 let mode = unsafe { (*buf).shm_perm.mode & 0o777 };
181 fchmod(shmid, mode as mode_t)
182 }
183 _ => {
184 ERRNO.set(EINVAL);
185 -1
186 }
187 }
188}