Skip to main content

linux_api/
shm.rs

1use core::ffi::c_int;
2
3use crate::bindings;
4use crate::const_conversions;
5use crate::stat::SFlag;
6
7/// Minimum shared segment size.
8pub const SHMMIN: usize = const_conversions::usize_from_u32(bindings::LINUX_SHMMIN);
9/// Max number of segments system-wide.
10pub const SHMMNI: usize = const_conversions::usize_from_u32(bindings::LINUX_SHMMNI);
11/// Max shared segs per process.
12pub const SHMSEG: usize = const_conversions::usize_from_u32(bindings::LINUX_SHMSEG);
13// TODO: SHMMAX, SHMALL. bindgen doesn't understand these definitions.
14
15/// bit-shift to encode "hugetlb" page sizes.
16pub use bindings::LINUX_SHM_HUGE_SHIFT as SHM_HUGE_SHIFT;
17
18/// parameter to `shmctl` syscall.
19pub use bindings::linux_shmid64_ds as shmid64_ds;
20
21use num_enum::IntoPrimitive;
22use num_enum::TryFromPrimitive;
23
24bitflags::bitflags! {
25    /// Flags accepted by `shmget` syscall.
26    ///
27    /// Note that the `shmflg` value provided to the `shmget` syscall encodes
28    /// additional information in other bitfields. Use `ShmgetFlagsParts` to
29    /// parse or create the raw syscall parameter.
30    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
31    pub struct ShmgetFlags: i32 {
32        /// Create a new segment.
33        const IPC_CREAT = const_conversions::i32_from_u32(bindings::LINUX_IPC_CREAT);
34        /// Ensure that a new segment is created.
35        const IPC_EXCL = const_conversions::i32_from_u32(bindings::LINUX_IPC_EXCL);
36        /// Allocate with huge pages.
37        const SHM_HUGETLB = const_conversions::i32_from_u32(bindings::LINUX_SHM_HUGETLB);
38        /// Do not reserve swap space.
39        const SHM_NORESERVE = const_conversions::i32_from_u32(bindings::LINUX_SHM_NORESERVE);
40    }
41}
42
43/// Represents a deconstructed `shmflg`, as passed to the `shmget` syscall.
44#[derive(Copy, Clone, Debug, Eq, PartialEq)]
45pub struct ShmgetFlagsParts {
46    /// Bit flags.
47    pub flags: ShmgetFlags,
48    /// Requested page size, log 2.
49    pub tlb_size_log_2: u32,
50    /// Requested permissions (as `mode` parameter to `open`)
51    pub perms: SFlag,
52}
53
54impl ShmgetFlagsParts {
55    /// Split a `shmflg` value, as provided to the `shmget` syscall, into consituent parts.
56    pub fn from_shmflg(shmflg: c_int) -> Self {
57        let tlb_size_shift = SHM_HUGE_SHIFT;
58        let tlb_size_mask = 0b11_1111 << tlb_size_shift;
59
60        let perms_shift = 0;
61        let perms_mask = 0b1_1111_1111;
62        Self {
63            // Careful to case to unsized before shifting to avoid sign extension.
64            tlb_size_log_2: (shmflg & tlb_size_mask) as u32 >> tlb_size_shift,
65            perms: SFlag::from_bits_retain((shmflg & perms_mask) as u32 >> perms_shift),
66            flags: ShmgetFlags::from_bits_retain(shmflg & !tlb_size_mask & !perms_mask),
67        }
68    }
69
70    /// Convert to a `shmflg` value, suitable for `shmget` syscall param.
71    ///
72    /// Doesn't validate that the fields are valid. e.g. unexpected bits in any field
73    /// may get packed into another field, shifted off, etc.
74    ///
75    // TODO: if we need something like this outside of testing, make a version
76    // with error-checking. It's a bit fiddly, though.
77    #[cfg(test)]
78    fn unchecked_to_shmflg(self) -> c_int {
79        self.flags.bits()
80            | (self.tlb_size_log_2 << SHM_HUGE_SHIFT) as i32
81            | self.perms.bits() as i32
82    }
83}
84
85bitflags::bitflags! {
86    /// Flags accepted by `shmat` syscall.
87    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
88    pub struct ShmatFlags: i32 {
89        const SHM_RND = const_conversions::i32_from_u32(bindings::LINUX_SHM_RND);
90        const SHM_EXEC= const_conversions::i32_from_u32(bindings::LINUX_SHM_EXEC);
91        const SHM_RDONLY= const_conversions::i32_from_u32(bindings::LINUX_SHM_RDONLY);
92        const SHM_REMAP= const_conversions::i32_from_u32(bindings::LINUX_SHM_REMAP);
93    }
94}
95
96/// Command provided to the `shmctl` syscall.
97#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
98#[repr(i32)]
99#[allow(non_camel_case_types)]
100pub enum ShmctlCmd {
101    IPC_RMID = const_conversions::i32_from_u32(bindings::LINUX_IPC_RMID),
102    IPC_SET = const_conversions::i32_from_u32(bindings::LINUX_IPC_SET),
103    IPC_STAT = const_conversions::i32_from_u32(bindings::LINUX_IPC_STAT),
104    IPC_INFO = const_conversions::i32_from_u32(bindings::LINUX_IPC_INFO),
105    SHM_LOCK = const_conversions::i32_from_u32(bindings::LINUX_SHM_LOCK),
106    SHM_UNLOCK = const_conversions::i32_from_u32(bindings::LINUX_SHM_UNLOCK),
107    SHM_STAT = const_conversions::i32_from_u32(bindings::LINUX_SHM_STAT),
108    SHM_INFO = const_conversions::i32_from_u32(bindings::LINUX_SHM_INFO),
109    SHM_STAT_ANY = const_conversions::i32_from_u32(bindings::LINUX_SHM_STAT_ANY),
110}
111
112#[cfg(test)]
113mod test {
114    use super::*;
115    use crate::stat::SFlag;
116
117    fn test_shmgetflags_value_roundtrip(shmflgs: core::ffi::c_int) {
118        let parts = ShmgetFlagsParts::from_shmflg(shmflgs);
119        let shmflgs2 = parts.unchecked_to_shmflg();
120        assert_eq!(shmflgs2, shmflgs)
121    }
122
123    #[test]
124    fn test_shmgetflags_values_roundtrips() {
125        test_shmgetflags_value_roundtrip(0);
126        test_shmgetflags_value_roundtrip(0xdeadbeefu32 as i32);
127        test_shmgetflags_value_roundtrip(0xfedcba98u32 as i32);
128        test_shmgetflags_value_roundtrip(0x01234567u32 as i32);
129        test_shmgetflags_value_roundtrip(
130            // flags
131            (bindings::LINUX_IPC_CREAT|bindings::LINUX_IPC_EXCL|bindings::LINUX_SHM_HUGETLB|bindings::LINUX_SHM_NORESERVE) as i32
132            // map size
133            | bindings::LINUX_SHM_HUGE_2MB as i32
134            // perms
135            | (bindings::LINUX_S_IRUSR | bindings::LINUX_S_IWUSR) as i32,
136        )
137    }
138
139    #[test]
140    fn test_parse_shmgetflgs() {
141        let shmflg =
142            // flags
143            (bindings::LINUX_IPC_CREAT|bindings::LINUX_IPC_EXCL|bindings::LINUX_SHM_HUGETLB|bindings::LINUX_SHM_NORESERVE) as i32
144            // map size
145            | bindings::LINUX_SHM_HUGE_2MB as i32
146            // perms
147            | (bindings::LINUX_S_IRUSR | bindings::LINUX_S_IWUSR) as i32;
148        let parts = ShmgetFlagsParts::from_shmflg(shmflg);
149        assert_eq!(
150            parts.flags,
151            ShmgetFlags::IPC_CREAT
152                | ShmgetFlags::IPC_EXCL
153                | ShmgetFlags::SHM_HUGETLB
154                | ShmgetFlags::SHM_NORESERVE
155        );
156        assert_eq!(parts.tlb_size_log_2, 21);
157        assert_eq!(parts.perms, SFlag::S_IRUSR | SFlag::S_IWUSR);
158    }
159}