1use std::{
4 borrow::{Borrow, BorrowMut},
5 fmt,
6 marker::PhantomData,
7 os::fd::{AsFd as _, AsRawFd as _},
8 path::Path,
9};
10
11use crate::{
12 maps::{FromMapData, InnerMap, MapData, MapError, PinError, check_bounds, check_kv_size},
13 sys::{SyscallError, bpf_map_lookup_elem, bpf_map_update_elem},
14};
15
16#[doc(alias = "BPF_MAP_TYPE_ARRAY_OF_MAPS")]
24pub struct ArrayOfMaps<T, V = MapData> {
25 pub(crate) inner: T,
26 _v: PhantomData<V>,
27}
28
29impl<T: fmt::Debug, V> fmt::Debug for ArrayOfMaps<T, V> {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 f.debug_struct("ArrayOfMaps")
32 .field("inner", &self.inner)
33 .finish()
34 }
35}
36
37impl<T: Borrow<MapData>, V> ArrayOfMaps<T, V> {
38 pub(crate) fn new(map: T) -> Result<Self, MapError> {
39 let data = map.borrow();
40 check_kv_size::<u32, u32>(data)?;
41 Ok(Self {
42 inner: map,
43 _v: PhantomData,
44 })
45 }
46
47 pub fn len(&self) -> u32 {
51 self.inner.borrow().obj.max_entries()
52 }
53
54 pub fn is_empty(&self) -> bool {
56 self.len() == 0
57 }
58}
59
60impl<T: Borrow<MapData>, V: FromMapData> ArrayOfMaps<T, V> {
61 pub fn get(&self, index: &u32, flags: u64) -> Result<V, MapError> {
77 let data = self.inner.borrow();
78 check_bounds(data, *index)?;
79 let fd = data.fd().as_fd();
80
81 let value: Option<u32> =
82 bpf_map_lookup_elem(fd, index, flags).map_err(|io_error| SyscallError {
83 call: "bpf_map_lookup_elem",
84 io_error,
85 })?;
86 match value {
87 Some(id) => super::map_from_id(id),
88 None => Err(MapError::KeyNotFound),
89 }
90 }
91}
92
93impl<T: BorrowMut<MapData>, V: InnerMap> ArrayOfMaps<T, V> {
94 pub fn set(&mut self, index: u32, value: &V, flags: u64) -> Result<(), MapError> {
101 let data = self.inner.borrow_mut();
102 check_bounds(data, index)?;
103 let fd = data.fd().as_fd();
104 bpf_map_update_elem(fd, Some(&index), &value.fd().as_fd().as_raw_fd(), flags).map_err(
105 |io_error| SyscallError {
106 call: "bpf_map_update_elem",
107 io_error,
108 },
109 )?;
110 Ok(())
111 }
112}
113
114impl<V> ArrayOfMaps<MapData, V> {
115 pub fn pin<P: AsRef<Path>>(self, path: P) -> Result<(), PinError> {
120 self.inner.pin(path)
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use std::io;
127
128 use assert_matches::assert_matches;
129 use aya_obj::generated::{bpf_cmd, bpf_map_type::BPF_MAP_TYPE_ARRAY_OF_MAPS};
130 use libc::{EFAULT, ENOENT};
131
132 use super::*;
133 use crate::{
134 maps::{Map, test_utils},
135 sys::{SysResult, Syscall, override_syscall},
136 };
137
138 fn new_obj_map() -> aya_obj::Map {
139 test_utils::new_obj_map::<u32>(BPF_MAP_TYPE_ARRAY_OF_MAPS)
140 }
141
142 fn new_map(obj: aya_obj::Map) -> MapData {
143 test_utils::new_map(obj)
144 }
145
146 fn sys_error(value: i32) -> SysResult {
147 Err((-1, io::Error::from_raw_os_error(value)))
148 }
149
150 #[test]
151 fn test_wrong_key_size() {
152 let map = new_map(test_utils::new_obj_map::<u8>(BPF_MAP_TYPE_ARRAY_OF_MAPS));
153 assert_matches!(
154 ArrayOfMaps::<_>::new(&map),
155 Err(MapError::InvalidKeySize {
156 size: 4,
157 expected: 1
158 })
159 );
160 }
161
162 #[test]
163 fn test_try_from_wrong_map() {
164 let map = new_map(test_utils::new_obj_map::<u32>(
165 aya_obj::generated::bpf_map_type::BPF_MAP_TYPE_HASH,
166 ));
167 let map = Map::HashMap(map);
168 assert_matches!(
169 ArrayOfMaps::<_>::try_from(&map),
170 Err(MapError::InvalidMapType { .. })
171 );
172 }
173
174 #[test]
175 fn test_new_ok() {
176 let map = new_map(new_obj_map());
177 let _: ArrayOfMaps<_> = ArrayOfMaps::new(&map).unwrap();
178 }
179
180 #[test]
181 fn test_set_syscall_error() {
182 let mut map = new_map(new_obj_map());
183 let inner_map = new_map(test_utils::new_obj_map::<u32>(
184 aya_obj::generated::bpf_map_type::BPF_MAP_TYPE_ARRAY,
185 ));
186 let mut arr = ArrayOfMaps::new(&mut map).unwrap();
187
188 override_syscall(|_| sys_error(EFAULT));
189
190 assert_matches!(
191 arr.set(0, &inner_map, 0),
192 Err(MapError::SyscallError(SyscallError {
193 call: "bpf_map_update_elem",
194 ..
195 }))
196 );
197 }
198
199 #[test]
200 fn test_set_ok() {
201 let mut map = new_map(new_obj_map());
202 let inner_map = new_map(test_utils::new_obj_map::<u32>(
203 aya_obj::generated::bpf_map_type::BPF_MAP_TYPE_ARRAY,
204 ));
205 let mut arr = ArrayOfMaps::new(&mut map).unwrap();
206
207 override_syscall(|call| match call {
208 Syscall::Ebpf {
209 cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM,
210 ..
211 } => Ok(0),
212 _ => sys_error(EFAULT),
213 });
214
215 arr.set(0, &inner_map, 0).unwrap();
216 }
217
218 #[test]
219 fn test_set_out_of_bounds() {
220 let mut map = new_map(new_obj_map());
221 let inner_map = new_map(test_utils::new_obj_map::<u32>(
222 aya_obj::generated::bpf_map_type::BPF_MAP_TYPE_ARRAY,
223 ));
224 let mut arr = ArrayOfMaps::new(&mut map).unwrap();
225
226 assert_matches!(
227 arr.set(1024, &inner_map, 0),
228 Err(MapError::OutOfBounds { .. })
229 );
230 }
231
232 #[test]
233 fn test_get_syscall_error() {
234 let map = new_map(new_obj_map());
235 let arr: ArrayOfMaps<_> = ArrayOfMaps::new(&map).unwrap();
236
237 override_syscall(|_| sys_error(EFAULT));
238
239 assert_matches!(
240 arr.get(&0, 0),
241 Err(MapError::SyscallError(SyscallError {
242 call: "bpf_map_lookup_elem",
243 ..
244 }))
245 );
246 }
247
248 #[test]
249 fn test_get_not_found() {
250 let map = new_map(new_obj_map());
251 let arr: ArrayOfMaps<_> = ArrayOfMaps::new(&map).unwrap();
252
253 override_syscall(|call| match call {
254 Syscall::Ebpf {
255 cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
256 ..
257 } => sys_error(ENOENT),
258 _ => sys_error(EFAULT),
259 });
260
261 assert_matches!(arr.get(&0, 0), Err(MapError::KeyNotFound));
262 }
263
264 #[test]
265 fn test_get_out_of_bounds() {
266 let map = new_map(new_obj_map());
267 let arr: ArrayOfMaps<_> = ArrayOfMaps::new(&map).unwrap();
268
269 assert_matches!(arr.get(&1024, 0), Err(MapError::OutOfBounds { .. }));
270 }
271}