Skip to main content

relibc/header/sys_shm/
mod.rs

1use 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
22/// Attach read-only (else read-write).
23pub const SHM_RDONLY: c_int = 0o10000;
24/// Round attach address to SHMLBA.
25pub const SHM_RND: c_int = 0o20000;
26/// Segment low boundary address multiple.
27pub const SHMLBA: size_t = 4096;
28
29/// Return value of `shmat()` indicating shared memory has not been attached.
30pub 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// needed because shmdt isn't tracking size
45#[derive(Copy, Clone)]
46#[repr(C)]
47struct ShmHeader {
48    total_size: size_t,
49}
50
51/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/shmget.html>.
52#[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/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/shmat.html>.
96#[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/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/shmdt.html>.
127#[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/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/shmctl.html>.
143#[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}