libbpf_rs/
map.rs

1use core::ffi::c_void;
2use std::ffi::CStr;
3use std::ffi::CString;
4use std::ffi::OsStr;
5use std::ffi::OsString;
6use std::fmt::Debug;
7use std::fs::remove_file;
8use std::io;
9use std::marker::PhantomData;
10use std::mem;
11use std::mem::transmute;
12use std::ops::Deref;
13use std::os::unix::ffi::OsStrExt;
14use std::os::unix::io::AsFd;
15use std::os::unix::io::AsRawFd;
16use std::os::unix::io::BorrowedFd;
17use std::os::unix::io::FromRawFd;
18use std::os::unix::io::OwnedFd;
19use std::os::unix::io::RawFd;
20use std::path::Path;
21use std::ptr;
22use std::ptr::NonNull;
23use std::slice;
24use std::slice::from_raw_parts;
25
26use bitflags::bitflags;
27use libbpf_sys::bpf_map_info;
28use libbpf_sys::bpf_obj_get_info_by_fd;
29
30use crate::util;
31use crate::util::parse_ret_i32;
32use crate::util::validate_bpf_ret;
33use crate::AsRawLibbpf;
34use crate::Error;
35use crate::ErrorExt as _;
36use crate::Link;
37use crate::Mut;
38use crate::Result;
39
40/// An immutable parsed but not yet loaded BPF map.
41pub type OpenMap<'obj> = OpenMapImpl<'obj>;
42/// A mutable parsed but not yet loaded BPF map.
43pub type OpenMapMut<'obj> = OpenMapImpl<'obj, Mut>;
44
45
46/// Represents a parsed but not yet loaded BPF map.
47///
48/// This object exposes operations that need to happen before the map is created.
49///
50/// Some methods require working with raw bytes. You may find libraries such as
51/// [`plain`](https://crates.io/crates/plain) helpful.
52#[derive(Debug)]
53#[repr(transparent)]
54pub struct OpenMapImpl<'obj, T = ()> {
55    ptr: NonNull<libbpf_sys::bpf_map>,
56    _phantom: PhantomData<&'obj T>,
57}
58
59// TODO: Document members.
60#[allow(missing_docs)]
61impl<'obj> OpenMap<'obj> {
62    /// Create a new [`OpenMap`] from a ptr to a `libbpf_sys::bpf_map`.
63    pub fn new(object: &'obj libbpf_sys::bpf_map) -> Self {
64        // SAFETY: We inferred the address from a reference, which is always
65        //         valid.
66        Self {
67            ptr: unsafe { NonNull::new_unchecked(object as *const _ as *mut _) },
68            _phantom: PhantomData,
69        }
70    }
71
72    /// Retrieve the [`OpenMap`]'s name.
73    pub fn name(&self) -> &OsStr {
74        // SAFETY: We ensured `ptr` is valid during construction.
75        let name_ptr = unsafe { libbpf_sys::bpf_map__name(self.ptr.as_ptr()) };
76        // SAFETY: `bpf_map__name` can return NULL but only if it's passed
77        //          NULL. We know `ptr` is not NULL.
78        let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
79        OsStr::from_bytes(name_c_str.to_bytes())
80    }
81
82    /// Retrieve type of the map.
83    pub fn map_type(&self) -> MapType {
84        let ty = unsafe { libbpf_sys::bpf_map__type(self.ptr.as_ptr()) };
85        MapType::from(ty)
86    }
87
88    fn initial_value_raw(&self) -> (*mut u8, usize) {
89        let mut size = 0u64;
90        let ptr = unsafe {
91            libbpf_sys::bpf_map__initial_value(self.ptr.as_ptr(), &mut size as *mut _ as _)
92        };
93        (ptr.cast(), size as _)
94    }
95
96    /// Retrieve the initial value of the map.
97    pub fn initial_value(&self) -> Option<&[u8]> {
98        let (ptr, size) = self.initial_value_raw();
99        if ptr.is_null() {
100            None
101        } else {
102            let data = unsafe { slice::from_raw_parts(ptr.cast::<u8>(), size) };
103            Some(data)
104        }
105    }
106}
107
108impl<'obj> OpenMapMut<'obj> {
109    /// Create a new [`OpenMapMut`] from a ptr to a `libbpf_sys::bpf_map`.
110    pub fn new_mut(object: &'obj mut libbpf_sys::bpf_map) -> Self {
111        Self {
112            ptr: unsafe { NonNull::new_unchecked(object as *mut _) },
113            _phantom: PhantomData,
114        }
115    }
116
117    /// Retrieve the initial value of the map.
118    pub fn initial_value_mut(&mut self) -> Option<&mut [u8]> {
119        let (ptr, size) = self.initial_value_raw();
120        if ptr.is_null() {
121            None
122        } else {
123            let data = unsafe { slice::from_raw_parts_mut(ptr.cast::<u8>(), size) };
124            Some(data)
125        }
126    }
127
128    /// Bind map to a particular network device.
129    ///
130    /// Used for offloading maps to hardware.
131    pub fn set_map_ifindex(&mut self, idx: u32) {
132        unsafe { libbpf_sys::bpf_map__set_ifindex(self.ptr.as_ptr(), idx) };
133    }
134
135    /// Set the initial value of the map.
136    pub fn set_initial_value(&mut self, data: &[u8]) -> Result<()> {
137        let ret = unsafe {
138            libbpf_sys::bpf_map__set_initial_value(
139                self.ptr.as_ptr(),
140                data.as_ptr() as *const c_void,
141                data.len() as libbpf_sys::size_t,
142            )
143        };
144
145        util::parse_ret(ret)
146    }
147
148    /// Set the type of the map.
149    pub fn set_type(&mut self, ty: MapType) -> Result<()> {
150        let ret = unsafe { libbpf_sys::bpf_map__set_type(self.ptr.as_ptr(), ty as u32) };
151        util::parse_ret(ret)
152    }
153
154    /// Set the key size of the map in bytes.
155    pub fn set_key_size(&mut self, size: u32) -> Result<()> {
156        let ret = unsafe { libbpf_sys::bpf_map__set_key_size(self.ptr.as_ptr(), size) };
157        util::parse_ret(ret)
158    }
159
160    /// Set the value size of the map in bytes.
161    pub fn set_value_size(&mut self, size: u32) -> Result<()> {
162        let ret = unsafe { libbpf_sys::bpf_map__set_value_size(self.ptr.as_ptr(), size) };
163        util::parse_ret(ret)
164    }
165
166    /// Set the maximum number of entries this map can have.
167    pub fn set_max_entries(&mut self, count: u32) -> Result<()> {
168        let ret = unsafe { libbpf_sys::bpf_map__set_max_entries(self.ptr.as_ptr(), count) };
169        util::parse_ret(ret)
170    }
171
172    /// Set flags on this map.
173    pub fn set_map_flags(&mut self, flags: u32) -> Result<()> {
174        let ret = unsafe { libbpf_sys::bpf_map__set_map_flags(self.ptr.as_ptr(), flags) };
175        util::parse_ret(ret)
176    }
177
178    // TODO: Document member.
179    #[allow(missing_docs)]
180    pub fn set_numa_node(&mut self, numa_node: u32) -> Result<()> {
181        let ret = unsafe { libbpf_sys::bpf_map__set_numa_node(self.ptr.as_ptr(), numa_node) };
182        util::parse_ret(ret)
183    }
184
185    // TODO: Document member.
186    #[allow(missing_docs)]
187    pub fn set_inner_map_fd(&mut self, inner_map_fd: BorrowedFd<'_>) -> Result<()> {
188        let ret = unsafe {
189            libbpf_sys::bpf_map__set_inner_map_fd(self.ptr.as_ptr(), inner_map_fd.as_raw_fd())
190        };
191        util::parse_ret(ret)
192    }
193
194    // TODO: Document member.
195    #[allow(missing_docs)]
196    pub fn set_map_extra(&mut self, map_extra: u64) -> Result<()> {
197        let ret = unsafe { libbpf_sys::bpf_map__set_map_extra(self.ptr.as_ptr(), map_extra) };
198        util::parse_ret(ret)
199    }
200
201    /// Set whether or not libbpf should automatically create this map during load phase.
202    pub fn set_autocreate(&mut self, autocreate: bool) -> Result<()> {
203        let ret = unsafe { libbpf_sys::bpf_map__set_autocreate(self.ptr.as_ptr(), autocreate) };
204        util::parse_ret(ret)
205    }
206
207    /// Set where the map should be pinned.
208    ///
209    /// Note this does not actually create the pin.
210    pub fn set_pin_path<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
211        let path_c = util::path_to_cstring(path)?;
212        let path_ptr = path_c.as_ptr();
213
214        let ret = unsafe { libbpf_sys::bpf_map__set_pin_path(self.ptr.as_ptr(), path_ptr) };
215        util::parse_ret(ret)
216    }
217
218    /// Reuse an fd for a BPF map
219    pub fn reuse_fd(&mut self, fd: BorrowedFd<'_>) -> Result<()> {
220        let ret = unsafe { libbpf_sys::bpf_map__reuse_fd(self.ptr.as_ptr(), fd.as_raw_fd()) };
221        util::parse_ret(ret)
222    }
223
224    /// Reuse an already-pinned map for `self`.
225    pub fn reuse_pinned_map<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
226        let cstring = util::path_to_cstring(path)?;
227
228        let fd = unsafe { libbpf_sys::bpf_obj_get(cstring.as_ptr()) };
229        if fd < 0 {
230            return Err(Error::from(io::Error::last_os_error()));
231        }
232
233        let fd = unsafe { OwnedFd::from_raw_fd(fd) };
234
235        let reuse_result = self.reuse_fd(fd.as_fd());
236
237        reuse_result
238    }
239}
240
241impl<'obj> Deref for OpenMapMut<'obj> {
242    type Target = OpenMap<'obj>;
243
244    fn deref(&self) -> &Self::Target {
245        // SAFETY: `OpenMapImpl` is `repr(transparent)` and so in-memory
246        //         representation of both types is the same.
247        unsafe { transmute::<&OpenMapMut<'obj>, &OpenMap<'obj>>(self) }
248    }
249}
250
251impl<T> AsRawLibbpf for OpenMapImpl<'_, T> {
252    type LibbpfType = libbpf_sys::bpf_map;
253
254    /// Retrieve the underlying [`libbpf_sys::bpf_map`].
255    fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
256        self.ptr
257    }
258}
259
260pub(crate) fn map_fd(map: NonNull<libbpf_sys::bpf_map>) -> Option<RawFd> {
261    let fd = unsafe { libbpf_sys::bpf_map__fd(map.as_ptr()) };
262    let fd = util::parse_ret_i32(fd).ok().map(|fd| fd as RawFd);
263    fd
264}
265
266/// Return the size of one value including padding for interacting with per-cpu
267/// maps. The values are aligned to 8 bytes.
268fn percpu_aligned_value_size<M>(map: &M) -> usize
269where
270    M: MapCore + ?Sized,
271{
272    let val_size = map.value_size() as usize;
273    util::roundup(val_size, 8)
274}
275
276/// Returns the size of the buffer needed for a lookup/update of a per-cpu map.
277fn percpu_buffer_size<M>(map: &M) -> Result<usize>
278where
279    M: MapCore + ?Sized,
280{
281    let aligned_val_size = percpu_aligned_value_size(map);
282    let ncpu = crate::num_possible_cpus()?;
283    Ok(ncpu * aligned_val_size)
284}
285
286/// Apply a key check and return a null pointer in case of dealing with queue/stack/bloom-filter
287/// map, before passing the key to the bpf functions that support the map of type
288/// queue/stack/bloom-filter.
289fn map_key<M>(map: &M, key: &[u8]) -> *const c_void
290where
291    M: MapCore + ?Sized,
292{
293    // For all they keyless maps we null out the key per documentation of libbpf
294    if map.key_size() == 0 && map.map_type().is_keyless() {
295        return ptr::null();
296    }
297
298    key.as_ptr() as *const c_void
299}
300
301/// Internal function to return a value from a map into a buffer of the given size.
302fn lookup_raw<M>(map: &M, key: &[u8], flags: MapFlags, out_size: usize) -> Result<Option<Vec<u8>>>
303where
304    M: MapCore + ?Sized,
305{
306    if key.len() != map.key_size() as usize {
307        return Err(Error::with_invalid_data(format!(
308            "key_size {} != {}",
309            key.len(),
310            map.key_size()
311        )));
312    };
313
314    let mut out: Vec<u8> = Vec::with_capacity(out_size);
315
316    let ret = unsafe {
317        libbpf_sys::bpf_map_lookup_elem_flags(
318            map.as_fd().as_raw_fd(),
319            map_key(map, key),
320            out.as_mut_ptr() as *mut c_void,
321            flags.bits(),
322        )
323    };
324
325    if ret == 0 {
326        unsafe {
327            out.set_len(out_size);
328        }
329        Ok(Some(out))
330    } else {
331        let err = io::Error::last_os_error();
332        if err.kind() == io::ErrorKind::NotFound {
333            Ok(None)
334        } else {
335            Err(Error::from(err))
336        }
337    }
338}
339
340/// Internal function to update a map. This does not check the length of the
341/// supplied value.
342fn update_raw<M>(map: &M, key: &[u8], value: &[u8], flags: MapFlags) -> Result<()>
343where
344    M: MapCore + ?Sized,
345{
346    if key.len() != map.key_size() as usize {
347        return Err(Error::with_invalid_data(format!(
348            "key_size {} != {}",
349            key.len(),
350            map.key_size()
351        )));
352    };
353
354    let ret = unsafe {
355        libbpf_sys::bpf_map_update_elem(
356            map.as_fd().as_raw_fd(),
357            map_key(map, key),
358            value.as_ptr() as *const c_void,
359            flags.bits(),
360        )
361    };
362
363    util::parse_ret(ret)
364}
365
366#[allow(clippy::wildcard_imports)]
367mod private {
368    use super::*;
369
370    pub trait Sealed {}
371
372    impl<T> Sealed for MapImpl<'_, T> {}
373    impl Sealed for MapHandle {}
374}
375
376/// A trait representing core functionality common to fully initialized maps.
377pub trait MapCore: Debug + AsFd + private::Sealed {
378    /// Retrieve the map's name.
379    fn name(&self) -> &OsStr;
380
381    /// Retrieve type of the map.
382    fn map_type(&self) -> MapType;
383
384    /// Retrieve the size of the map's keys.
385    fn key_size(&self) -> u32;
386
387    /// Retrieve the size of the map's values.
388    fn value_size(&self) -> u32;
389
390    /// Fetch extra map information
391    #[inline]
392    fn info(&self) -> Result<MapInfo> {
393        MapInfo::new(self.as_fd())
394    }
395
396    /// Returns an iterator over keys in this map
397    ///
398    /// Note that if the map is not stable (stable meaning no updates or deletes) during iteration,
399    /// iteration can skip keys, restart from the beginning, or duplicate keys. In other words,
400    /// iteration becomes unpredictable.
401    fn keys(&self) -> MapKeyIter<'_> {
402        MapKeyIter::new(self.as_fd(), self.key_size())
403    }
404
405    /// Returns map value as `Vec` of `u8`.
406    ///
407    /// `key` must have exactly [`Self::key_size()`] elements.
408    ///
409    /// If the map is one of the per-cpu data structures, the function [`Self::lookup_percpu()`]
410    /// must be used.
411    /// If the map is of type bloom_filter the function [`Self::lookup_bloom_filter()`] must be used
412    fn lookup(&self, key: &[u8], flags: MapFlags) -> Result<Option<Vec<u8>>> {
413        if self.map_type().is_bloom_filter() {
414            return Err(Error::with_invalid_data(
415                "lookup_bloom_filter() must be used for bloom filter maps",
416            ));
417        }
418        if self.map_type().is_percpu() {
419            return Err(Error::with_invalid_data(format!(
420                "lookup_percpu() must be used for per-cpu maps (type of the map is {:?})",
421                self.map_type(),
422            )));
423        }
424
425        let out_size = self.value_size() as usize;
426        lookup_raw(self, key, flags, out_size)
427    }
428
429    /// Returns if the given value is likely present in bloom_filter as `bool`.
430    ///
431    /// `value` must have exactly [`Self::value_size()`] elements.
432    fn lookup_bloom_filter(&self, value: &[u8]) -> Result<bool> {
433        let ret = unsafe {
434            libbpf_sys::bpf_map_lookup_elem(
435                self.as_fd().as_raw_fd(),
436                ptr::null(),
437                value.to_vec().as_mut_ptr() as *mut c_void,
438            )
439        };
440
441        if ret == 0 {
442            Ok(true)
443        } else {
444            let err = io::Error::last_os_error();
445            if err.kind() == io::ErrorKind::NotFound {
446                Ok(false)
447            } else {
448                Err(Error::from(err))
449            }
450        }
451    }
452
453    /// Returns one value per cpu as `Vec` of `Vec` of `u8` for per per-cpu maps.
454    ///
455    /// For normal maps, [`Self::lookup()`] must be used.
456    fn lookup_percpu(&self, key: &[u8], flags: MapFlags) -> Result<Option<Vec<Vec<u8>>>> {
457        if !self.map_type().is_percpu() && self.map_type() != MapType::Unknown {
458            return Err(Error::with_invalid_data(format!(
459                "lookup() must be used for maps that are not per-cpu (type of the map is {:?})",
460                self.map_type(),
461            )));
462        }
463
464        let val_size = self.value_size() as usize;
465        let aligned_val_size = percpu_aligned_value_size(self);
466        let out_size = percpu_buffer_size(self)?;
467
468        let raw_res = lookup_raw(self, key, flags, out_size)?;
469        if let Some(raw_vals) = raw_res {
470            let mut out = Vec::new();
471            for chunk in raw_vals.chunks_exact(aligned_val_size) {
472                out.push(chunk[..val_size].to_vec());
473            }
474            Ok(Some(out))
475        } else {
476            Ok(None)
477        }
478    }
479
480    /// Deletes an element from the map.
481    ///
482    /// `key` must have exactly [`Self::key_size()`] elements.
483    fn delete(&self, key: &[u8]) -> Result<()> {
484        if key.len() != self.key_size() as usize {
485            return Err(Error::with_invalid_data(format!(
486                "key_size {} != {}",
487                key.len(),
488                self.key_size()
489            )));
490        };
491
492        let ret = unsafe {
493            libbpf_sys::bpf_map_delete_elem(self.as_fd().as_raw_fd(), key.as_ptr() as *const c_void)
494        };
495        util::parse_ret(ret)
496    }
497
498    /// Deletes many elements in batch mode from the map.
499    ///
500    /// `keys` must have exactly `Self::key_size() * count` elements.
501    fn delete_batch(
502        &self,
503        keys: &[u8],
504        count: u32,
505        elem_flags: MapFlags,
506        flags: MapFlags,
507    ) -> Result<()> {
508        if keys.len() as u32 / count != self.key_size() || (keys.len() as u32) % count != 0 {
509            return Err(Error::with_invalid_data(format!(
510                "batch key_size {} != {} * {}",
511                keys.len(),
512                self.key_size(),
513                count
514            )));
515        };
516
517        #[allow(clippy::needless_update)]
518        let opts = libbpf_sys::bpf_map_batch_opts {
519            sz: mem::size_of::<libbpf_sys::bpf_map_batch_opts>() as _,
520            elem_flags: elem_flags.bits(),
521            flags: flags.bits(),
522            // bpf_map_batch_opts might have padding fields on some platform
523            ..Default::default()
524        };
525
526        let mut count = count;
527        let ret = unsafe {
528            libbpf_sys::bpf_map_delete_batch(
529                self.as_fd().as_raw_fd(),
530                keys.as_ptr() as *const c_void,
531                &mut count,
532                &opts as *const libbpf_sys::bpf_map_batch_opts,
533            )
534        };
535        util::parse_ret(ret)
536    }
537
538    /// Same as [`Self::lookup()`] except this also deletes the key from the map.
539    ///
540    /// Note that this operation is currently only implemented in the kernel for [`MapType::Queue`]
541    /// and [`MapType::Stack`].
542    ///
543    /// `key` must have exactly [`Self::key_size()`] elements.
544    fn lookup_and_delete(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
545        if key.len() != self.key_size() as usize {
546            return Err(Error::with_invalid_data(format!(
547                "key_size {} != {}",
548                key.len(),
549                self.key_size()
550            )));
551        };
552
553        let mut out: Vec<u8> = Vec::with_capacity(self.value_size() as usize);
554
555        let ret = unsafe {
556            libbpf_sys::bpf_map_lookup_and_delete_elem(
557                self.as_fd().as_raw_fd(),
558                map_key(self, key),
559                out.as_mut_ptr() as *mut c_void,
560            )
561        };
562
563        if ret == 0 {
564            unsafe {
565                out.set_len(self.value_size() as usize);
566            }
567            Ok(Some(out))
568        } else {
569            let err = io::Error::last_os_error();
570            if err.kind() == io::ErrorKind::NotFound {
571                Ok(None)
572            } else {
573                Err(Error::from(err))
574            }
575        }
576    }
577
578    /// Update an element.
579    ///
580    /// `key` must have exactly [`Self::key_size()`] elements. `value` must have exactly
581    /// [`Self::value_size()`] elements.
582    ///
583    /// For per-cpu maps, [`Self::update_percpu()`] must be used.
584    fn update(&self, key: &[u8], value: &[u8], flags: MapFlags) -> Result<()> {
585        if self.map_type().is_percpu() {
586            return Err(Error::with_invalid_data(format!(
587                "update_percpu() must be used for per-cpu maps (type of the map is {:?})",
588                self.map_type(),
589            )));
590        }
591
592        if value.len() != self.value_size() as usize {
593            return Err(Error::with_invalid_data(format!(
594                "value_size {} != {}",
595                value.len(),
596                self.value_size()
597            )));
598        };
599
600        update_raw(self, key, value, flags)
601    }
602
603    /// Updates many elements in batch mode in the map
604    ///
605    /// `keys` must have exactly `Self::key_size() * count` elements. `values` must have exactly
606    /// `Self::key_size() * count` elements.
607    fn update_batch(
608        &self,
609        keys: &[u8],
610        values: &[u8],
611        count: u32,
612        elem_flags: MapFlags,
613        flags: MapFlags,
614    ) -> Result<()> {
615        if keys.len() as u32 / count != self.key_size() || (keys.len() as u32) % count != 0 {
616            return Err(Error::with_invalid_data(format!(
617                "batch key_size {} != {} * {}",
618                keys.len(),
619                self.key_size(),
620                count
621            )));
622        };
623
624        if values.len() as u32 / count != self.value_size() || (values.len() as u32) % count != 0 {
625            return Err(Error::with_invalid_data(format!(
626                "batch value_size {} != {} * {}",
627                values.len(),
628                self.value_size(),
629                count
630            )));
631        }
632
633        #[allow(clippy::needless_update)]
634        let opts = libbpf_sys::bpf_map_batch_opts {
635            sz: mem::size_of::<libbpf_sys::bpf_map_batch_opts>() as _,
636            elem_flags: elem_flags.bits(),
637            flags: flags.bits(),
638            // bpf_map_batch_opts might have padding fields on some platform
639            ..Default::default()
640        };
641
642        let mut count = count;
643        let ret = unsafe {
644            libbpf_sys::bpf_map_update_batch(
645                self.as_fd().as_raw_fd(),
646                keys.as_ptr() as *const c_void,
647                values.as_ptr() as *const c_void,
648                &mut count,
649                &opts as *const libbpf_sys::bpf_map_batch_opts,
650            )
651        };
652
653        util::parse_ret(ret)
654    }
655
656    /// Update an element in an per-cpu map with one value per cpu.
657    ///
658    /// `key` must have exactly [`Self::key_size()`] elements. `value` must have one
659    /// element per cpu (see [`num_possible_cpus`][crate::num_possible_cpus])
660    /// with exactly [`Self::value_size()`] elements each.
661    ///
662    /// For per-cpu maps, [`Self::update_percpu()`] must be used.
663    fn update_percpu(&self, key: &[u8], values: &[Vec<u8>], flags: MapFlags) -> Result<()> {
664        if !self.map_type().is_percpu() && self.map_type() != MapType::Unknown {
665            return Err(Error::with_invalid_data(format!(
666                "update() must be used for maps that are not per-cpu (type of the map is {:?})",
667                self.map_type(),
668            )));
669        }
670
671        if values.len() != crate::num_possible_cpus()? {
672            return Err(Error::with_invalid_data(format!(
673                "number of values {} != number of cpus {}",
674                values.len(),
675                crate::num_possible_cpus()?
676            )));
677        };
678
679        let val_size = self.value_size() as usize;
680        let aligned_val_size = percpu_aligned_value_size(self);
681        let buf_size = percpu_buffer_size(self)?;
682
683        let mut value_buf = vec![0; buf_size];
684
685        for (i, val) in values.iter().enumerate() {
686            if val.len() != val_size {
687                return Err(Error::with_invalid_data(format!(
688                    "value size for cpu {} is {} != {}",
689                    i,
690                    val.len(),
691                    val_size
692                )));
693            }
694
695            value_buf[(i * aligned_val_size)..(i * aligned_val_size + val_size)]
696                .copy_from_slice(val);
697        }
698
699        update_raw(self, key, &value_buf, flags)
700    }
701}
702
703/// An immutable loaded BPF map.
704pub type Map<'obj> = MapImpl<'obj>;
705/// A mutable loaded BPF map.
706pub type MapMut<'obj> = MapImpl<'obj, Mut>;
707
708/// Represents a libbpf-created map.
709///
710/// Some methods require working with raw bytes. You may find libraries such as
711/// [`plain`](https://crates.io/crates/plain) helpful.
712#[derive(Debug)]
713pub struct MapImpl<'obj, T = ()> {
714    ptr: NonNull<libbpf_sys::bpf_map>,
715    _phantom: PhantomData<&'obj T>,
716}
717
718impl<'obj> Map<'obj> {
719    /// Create a [`Map`] from a [`libbpf_sys::bpf_map`].
720    pub fn new(map: &'obj libbpf_sys::bpf_map) -> Self {
721        // SAFETY: We inferred the address from a reference, which is always
722        //         valid.
723        let ptr = unsafe { NonNull::new_unchecked(map as *const _ as *mut _) };
724        assert!(
725            map_fd(ptr).is_some(),
726            "provided BPF map does not have file descriptor"
727        );
728
729        Self {
730            ptr,
731            _phantom: PhantomData,
732        }
733    }
734
735    /// Create a [`Map`] from a [`libbpf_sys::bpf_map`] that does not contain a
736    /// file descriptor.
737    ///
738    /// The caller has to ensure that the [`AsFd`] impl is not used, or a panic
739    /// will be the result.
740    ///
741    /// # Safety
742    ///
743    /// The pointer must point to a loaded map.
744    #[doc(hidden)]
745    pub unsafe fn from_map_without_fd(ptr: NonNull<libbpf_sys::bpf_map>) -> Self {
746        Self {
747            ptr,
748            _phantom: PhantomData,
749        }
750    }
751
752    /// Returns whether map is pinned or not flag
753    pub fn is_pinned(&self) -> bool {
754        unsafe { libbpf_sys::bpf_map__is_pinned(self.ptr.as_ptr()) }
755    }
756
757    /// Returns the pin_path if the map is pinned, otherwise, None is returned
758    pub fn get_pin_path(&self) -> Option<&OsStr> {
759        let path_ptr = unsafe { libbpf_sys::bpf_map__pin_path(self.ptr.as_ptr()) };
760        if path_ptr.is_null() {
761            // means map is not pinned
762            return None;
763        }
764        let path_c_str = unsafe { CStr::from_ptr(path_ptr) };
765        Some(OsStr::from_bytes(path_c_str.to_bytes()))
766    }
767}
768
769impl<'obj> MapMut<'obj> {
770    /// Create a [`MapMut`] from a [`libbpf_sys::bpf_map`].
771    pub fn new_mut(map: &'obj mut libbpf_sys::bpf_map) -> Self {
772        // SAFETY: We inferred the address from a reference, which is always
773        //         valid.
774        let ptr = unsafe { NonNull::new_unchecked(map as *mut _) };
775        assert!(
776            map_fd(ptr).is_some(),
777            "provided BPF map does not have file descriptor"
778        );
779
780        Self {
781            ptr,
782            _phantom: PhantomData,
783        }
784    }
785
786    /// [Pin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
787    /// this map to bpffs.
788    pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
789        let path_c = util::path_to_cstring(path)?;
790        let path_ptr = path_c.as_ptr();
791
792        let ret = unsafe { libbpf_sys::bpf_map__pin(self.ptr.as_ptr(), path_ptr) };
793        util::parse_ret(ret)
794    }
795
796    /// [Unpin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
797    /// this map from bpffs.
798    pub fn unpin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
799        let path_c = util::path_to_cstring(path)?;
800        let path_ptr = path_c.as_ptr();
801        let ret = unsafe { libbpf_sys::bpf_map__unpin(self.ptr.as_ptr(), path_ptr) };
802        util::parse_ret(ret)
803    }
804
805    /// Attach a struct ops map
806    pub fn attach_struct_ops(&mut self) -> Result<Link> {
807        if self.map_type() != MapType::StructOps {
808            return Err(Error::with_invalid_data(format!(
809                "Invalid map type ({:?}) for attach_struct_ops()",
810                self.map_type(),
811            )));
812        }
813
814        let ptr = unsafe { libbpf_sys::bpf_map__attach_struct_ops(self.ptr.as_ptr()) };
815        let ptr = validate_bpf_ret(ptr).context("failed to attach struct_ops")?;
816        // SAFETY: the pointer came from libbpf and has been checked for errors.
817        let link = unsafe { Link::new(ptr) };
818        Ok(link)
819    }
820}
821
822impl<'obj> Deref for MapMut<'obj> {
823    type Target = Map<'obj>;
824
825    fn deref(&self) -> &Self::Target {
826        unsafe { transmute::<&MapMut<'obj>, &Map<'obj>>(self) }
827    }
828}
829
830impl<T> AsFd for MapImpl<'_, T> {
831    #[inline]
832    fn as_fd(&self) -> BorrowedFd<'_> {
833        // SANITY: Our map must always have a file descriptor associated with
834        //         it.
835        let fd = map_fd(self.ptr).unwrap();
836        // SAFETY: `fd` is guaranteed to be valid for the lifetime of
837        //         the created object.
838        let fd = unsafe { BorrowedFd::borrow_raw(fd as _) };
839        fd
840    }
841}
842
843impl<T> MapCore for MapImpl<'_, T>
844where
845    T: Debug,
846{
847    fn name(&self) -> &OsStr {
848        // SAFETY: We ensured `ptr` is valid during construction.
849        let name_ptr = unsafe { libbpf_sys::bpf_map__name(self.ptr.as_ptr()) };
850        // SAFETY: `bpf_map__name` can return NULL but only if it's passed
851        //          NULL. We know `ptr` is not NULL.
852        let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
853        OsStr::from_bytes(name_c_str.to_bytes())
854    }
855
856    #[inline]
857    fn map_type(&self) -> MapType {
858        let ty = unsafe { libbpf_sys::bpf_map__type(self.ptr.as_ptr()) };
859        MapType::from(ty)
860    }
861
862    #[inline]
863    fn key_size(&self) -> u32 {
864        unsafe { libbpf_sys::bpf_map__key_size(self.ptr.as_ptr()) }
865    }
866
867    #[inline]
868    fn value_size(&self) -> u32 {
869        unsafe { libbpf_sys::bpf_map__value_size(self.ptr.as_ptr()) }
870    }
871}
872
873impl AsRawLibbpf for Map<'_> {
874    type LibbpfType = libbpf_sys::bpf_map;
875
876    /// Retrieve the underlying [`libbpf_sys::bpf_map`].
877    #[inline]
878    fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
879        self.ptr
880    }
881}
882
883/// A handle to a map. Handles can be duplicated and dropped.
884///
885/// While possible to [created directly][MapHandle::create], in many cases it is
886/// useful to create such a handle from an existing [`Map`]:
887/// ```no_run
888/// # use libbpf_rs::Map;
889/// # use libbpf_rs::MapHandle;
890/// # let get_map = || -> &Map { todo!() };
891/// let map: &Map = get_map();
892/// let map_handle = MapHandle::try_from(map).unwrap();
893/// ```
894///
895/// Some methods require working with raw bytes. You may find libraries such as
896/// [`plain`](https://crates.io/crates/plain) helpful.
897#[derive(Debug)]
898pub struct MapHandle {
899    fd: OwnedFd,
900    name: OsString,
901    ty: MapType,
902    key_size: u32,
903    value_size: u32,
904}
905
906impl MapHandle {
907    /// Create a bpf map whose data is not managed by libbpf.
908    pub fn create<T: AsRef<OsStr>>(
909        map_type: MapType,
910        name: Option<T>,
911        key_size: u32,
912        value_size: u32,
913        max_entries: u32,
914        opts: &libbpf_sys::bpf_map_create_opts,
915    ) -> Result<Self> {
916        let name = match name {
917            Some(name) => name.as_ref().to_os_string(),
918            // The old version kernel don't support specifying map name.
919            None => OsString::new(),
920        };
921        let name_c_str = CString::new(name.as_bytes()).map_err(|_| {
922            Error::with_invalid_data(format!("invalid name `{name:?}`: has NUL bytes"))
923        })?;
924        let name_c_ptr = if name.is_empty() {
925            ptr::null()
926        } else {
927            name_c_str.as_bytes_with_nul().as_ptr()
928        };
929
930        let fd = unsafe {
931            libbpf_sys::bpf_map_create(
932                map_type.into(),
933                name_c_ptr.cast(),
934                key_size,
935                value_size,
936                max_entries,
937                opts,
938            )
939        };
940        let () = util::parse_ret(fd)?;
941
942        Ok(Self {
943            // SAFETY: A file descriptor coming from the `bpf_map_create`
944            //         function is always suitable for ownership and can be
945            //         cleaned up with close.
946            fd: unsafe { OwnedFd::from_raw_fd(fd) },
947            name,
948            ty: map_type,
949            key_size,
950            value_size,
951        })
952    }
953
954    /// Open a previously pinned map from its path.
955    ///
956    /// # Panics
957    /// If the path contains null bytes.
958    pub fn from_pinned_path<P: AsRef<Path>>(path: P) -> Result<Self> {
959        fn inner(path: &Path) -> Result<MapHandle> {
960            let p = CString::new(path.as_os_str().as_bytes()).expect("path contained null bytes");
961            let fd = parse_ret_i32(unsafe {
962                // SAFETY
963                // p is never null since we allocated ourselves.
964                libbpf_sys::bpf_obj_get(p.as_ptr())
965            })?;
966            MapHandle::from_fd(unsafe {
967                // SAFETY
968                // A file descriptor coming from the bpf_obj_get function is always suitable for
969                // ownership and can be cleaned up with close.
970                OwnedFd::from_raw_fd(fd)
971            })
972        }
973
974        inner(path.as_ref())
975    }
976
977    /// Open a loaded map from its map id.
978    pub fn from_map_id(id: u32) -> Result<Self> {
979        parse_ret_i32(unsafe {
980            // SAFETY
981            // This function is always safe to call.
982            libbpf_sys::bpf_map_get_fd_by_id(id)
983        })
984        .map(|fd| unsafe {
985            // SAFETY
986            // A file descriptor coming from the bpf_map_get_fd_by_id function is always suitable
987            // for ownership and can be cleaned up with close.
988            OwnedFd::from_raw_fd(fd)
989        })
990        .and_then(Self::from_fd)
991    }
992
993    fn from_fd(fd: OwnedFd) -> Result<Self> {
994        let info = MapInfo::new(fd.as_fd())?;
995        Ok(Self {
996            fd,
997            name: info.name()?.into(),
998            ty: info.map_type(),
999            key_size: info.info.key_size,
1000            value_size: info.info.value_size,
1001        })
1002    }
1003
1004    /// Freeze the map as read-only from user space.
1005    ///
1006    /// Entries from a frozen map can no longer be updated or deleted with the
1007    /// bpf() system call. This operation is not reversible, and the map remains
1008    /// immutable from user space until its destruction. However, read and write
1009    /// permissions for BPF programs to the map remain unchanged.
1010    pub fn freeze(&self) -> Result<()> {
1011        let ret = unsafe { libbpf_sys::bpf_map_freeze(self.fd.as_raw_fd()) };
1012
1013        util::parse_ret(ret)
1014    }
1015
1016    /// [Pin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
1017    /// this map to bpffs.
1018    pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
1019        let path_c = util::path_to_cstring(path)?;
1020        let path_ptr = path_c.as_ptr();
1021
1022        let ret = unsafe { libbpf_sys::bpf_obj_pin(self.fd.as_raw_fd(), path_ptr) };
1023        util::parse_ret(ret)
1024    }
1025
1026    /// [Unpin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
1027    /// this map from bpffs.
1028    pub fn unpin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
1029        remove_file(path).context("failed to remove pin map")
1030    }
1031}
1032
1033impl MapCore for MapHandle {
1034    #[inline]
1035    fn name(&self) -> &OsStr {
1036        &self.name
1037    }
1038
1039    #[inline]
1040    fn map_type(&self) -> MapType {
1041        self.ty
1042    }
1043
1044    #[inline]
1045    fn key_size(&self) -> u32 {
1046        self.key_size
1047    }
1048
1049    #[inline]
1050    fn value_size(&self) -> u32 {
1051        self.value_size
1052    }
1053}
1054
1055impl AsFd for MapHandle {
1056    #[inline]
1057    fn as_fd(&self) -> BorrowedFd<'_> {
1058        self.fd.as_fd()
1059    }
1060}
1061
1062impl<T> TryFrom<&MapImpl<'_, T>> for MapHandle
1063where
1064    T: Debug,
1065{
1066    type Error = Error;
1067
1068    fn try_from(other: &MapImpl<'_, T>) -> Result<Self> {
1069        Ok(Self {
1070            fd: other
1071                .as_fd()
1072                .try_clone_to_owned()
1073                .context("failed to duplicate map file descriptor")?,
1074            name: other.name().to_os_string(),
1075            ty: other.map_type(),
1076            key_size: other.key_size(),
1077            value_size: other.value_size(),
1078        })
1079    }
1080}
1081
1082impl TryFrom<&MapHandle> for MapHandle {
1083    type Error = Error;
1084
1085    fn try_from(other: &MapHandle) -> Result<Self> {
1086        Ok(Self {
1087            fd: other
1088                .as_fd()
1089                .try_clone_to_owned()
1090                .context("failed to duplicate map file descriptor")?,
1091            name: other.name().to_os_string(),
1092            ty: other.map_type(),
1093            key_size: other.key_size(),
1094            value_size: other.value_size(),
1095        })
1096    }
1097}
1098
1099bitflags! {
1100    /// Flags to configure [`Map`] operations.
1101    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
1102    pub struct MapFlags: u64 {
1103        /// See [`libbpf_sys::BPF_ANY`].
1104        const ANY      = libbpf_sys::BPF_ANY as _;
1105        /// See [`libbpf_sys::BPF_NOEXIST`].
1106        const NO_EXIST = libbpf_sys::BPF_NOEXIST as _;
1107        /// See [`libbpf_sys::BPF_EXIST`].
1108        const EXIST    = libbpf_sys::BPF_EXIST as _;
1109        /// See [`libbpf_sys::BPF_F_LOCK`].
1110        const LOCK     = libbpf_sys::BPF_F_LOCK as _;
1111    }
1112}
1113
1114/// Type of a [`Map`]. Maps to `enum bpf_map_type` in kernel uapi.
1115// If you add a new per-cpu map, also update `is_percpu`.
1116#[non_exhaustive]
1117#[repr(u32)]
1118#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1119// TODO: Document members.
1120#[allow(missing_docs)]
1121pub enum MapType {
1122    Unspec = libbpf_sys::BPF_MAP_TYPE_UNSPEC,
1123    Hash = libbpf_sys::BPF_MAP_TYPE_HASH,
1124    Array = libbpf_sys::BPF_MAP_TYPE_ARRAY,
1125    ProgArray = libbpf_sys::BPF_MAP_TYPE_PROG_ARRAY,
1126    PerfEventArray = libbpf_sys::BPF_MAP_TYPE_PERF_EVENT_ARRAY,
1127    PercpuHash = libbpf_sys::BPF_MAP_TYPE_PERCPU_HASH,
1128    PercpuArray = libbpf_sys::BPF_MAP_TYPE_PERCPU_ARRAY,
1129    StackTrace = libbpf_sys::BPF_MAP_TYPE_STACK_TRACE,
1130    CgroupArray = libbpf_sys::BPF_MAP_TYPE_CGROUP_ARRAY,
1131    LruHash = libbpf_sys::BPF_MAP_TYPE_LRU_HASH,
1132    LruPercpuHash = libbpf_sys::BPF_MAP_TYPE_LRU_PERCPU_HASH,
1133    LpmTrie = libbpf_sys::BPF_MAP_TYPE_LPM_TRIE,
1134    ArrayOfMaps = libbpf_sys::BPF_MAP_TYPE_ARRAY_OF_MAPS,
1135    HashOfMaps = libbpf_sys::BPF_MAP_TYPE_HASH_OF_MAPS,
1136    Devmap = libbpf_sys::BPF_MAP_TYPE_DEVMAP,
1137    Sockmap = libbpf_sys::BPF_MAP_TYPE_SOCKMAP,
1138    Cpumap = libbpf_sys::BPF_MAP_TYPE_CPUMAP,
1139    Xskmap = libbpf_sys::BPF_MAP_TYPE_XSKMAP,
1140    Sockhash = libbpf_sys::BPF_MAP_TYPE_SOCKHASH,
1141    CgroupStorage = libbpf_sys::BPF_MAP_TYPE_CGROUP_STORAGE,
1142    ReuseportSockarray = libbpf_sys::BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
1143    PercpuCgroupStorage = libbpf_sys::BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
1144    Queue = libbpf_sys::BPF_MAP_TYPE_QUEUE,
1145    Stack = libbpf_sys::BPF_MAP_TYPE_STACK,
1146    SkStorage = libbpf_sys::BPF_MAP_TYPE_SK_STORAGE,
1147    DevmapHash = libbpf_sys::BPF_MAP_TYPE_DEVMAP_HASH,
1148    StructOps = libbpf_sys::BPF_MAP_TYPE_STRUCT_OPS,
1149    RingBuf = libbpf_sys::BPF_MAP_TYPE_RINGBUF,
1150    InodeStorage = libbpf_sys::BPF_MAP_TYPE_INODE_STORAGE,
1151    TaskStorage = libbpf_sys::BPF_MAP_TYPE_TASK_STORAGE,
1152    BloomFilter = libbpf_sys::BPF_MAP_TYPE_BLOOM_FILTER,
1153    UserRingBuf = libbpf_sys::BPF_MAP_TYPE_USER_RINGBUF,
1154    /// We choose to specify our own "unknown" type here b/c it's really up to the kernel
1155    /// to decide if it wants to reject the map. If it accepts it, it just means whoever
1156    /// using this library is a bit out of date.
1157    Unknown = u32::MAX,
1158}
1159
1160impl MapType {
1161    /// Returns if the map is of one of the per-cpu types.
1162    pub fn is_percpu(&self) -> bool {
1163        matches!(
1164            self,
1165            MapType::PercpuArray
1166                | MapType::PercpuHash
1167                | MapType::LruPercpuHash
1168                | MapType::PercpuCgroupStorage
1169        )
1170    }
1171
1172    /// Returns if the map is keyless map type as per documentation of libbpf
1173    /// Keyless map types are: Queues, Stacks and Bloom Filters
1174    fn is_keyless(&self) -> bool {
1175        matches!(self, MapType::Queue | MapType::Stack | MapType::BloomFilter)
1176    }
1177
1178    /// Returns if the map is of bloom filter type
1179    pub fn is_bloom_filter(&self) -> bool {
1180        MapType::BloomFilter.eq(self)
1181    }
1182
1183    /// Detects if host kernel supports this BPF map type.
1184    ///
1185    /// Make sure the process has required set of CAP_* permissions (or runs as
1186    /// root) when performing feature checking.
1187    pub fn is_supported(&self) -> Result<bool> {
1188        let ret = unsafe { libbpf_sys::libbpf_probe_bpf_map_type(*self as u32, ptr::null()) };
1189        match ret {
1190            0 => Ok(false),
1191            1 => Ok(true),
1192            _ => Err(Error::from_raw_os_error(-ret)),
1193        }
1194    }
1195}
1196
1197impl From<u32> for MapType {
1198    fn from(value: u32) -> Self {
1199        use MapType::*;
1200
1201        match value {
1202            x if x == Unspec as u32 => Unspec,
1203            x if x == Hash as u32 => Hash,
1204            x if x == Array as u32 => Array,
1205            x if x == ProgArray as u32 => ProgArray,
1206            x if x == PerfEventArray as u32 => PerfEventArray,
1207            x if x == PercpuHash as u32 => PercpuHash,
1208            x if x == PercpuArray as u32 => PercpuArray,
1209            x if x == StackTrace as u32 => StackTrace,
1210            x if x == CgroupArray as u32 => CgroupArray,
1211            x if x == LruHash as u32 => LruHash,
1212            x if x == LruPercpuHash as u32 => LruPercpuHash,
1213            x if x == LpmTrie as u32 => LpmTrie,
1214            x if x == ArrayOfMaps as u32 => ArrayOfMaps,
1215            x if x == HashOfMaps as u32 => HashOfMaps,
1216            x if x == Devmap as u32 => Devmap,
1217            x if x == Sockmap as u32 => Sockmap,
1218            x if x == Cpumap as u32 => Cpumap,
1219            x if x == Xskmap as u32 => Xskmap,
1220            x if x == Sockhash as u32 => Sockhash,
1221            x if x == CgroupStorage as u32 => CgroupStorage,
1222            x if x == ReuseportSockarray as u32 => ReuseportSockarray,
1223            x if x == PercpuCgroupStorage as u32 => PercpuCgroupStorage,
1224            x if x == Queue as u32 => Queue,
1225            x if x == Stack as u32 => Stack,
1226            x if x == SkStorage as u32 => SkStorage,
1227            x if x == DevmapHash as u32 => DevmapHash,
1228            x if x == StructOps as u32 => StructOps,
1229            x if x == RingBuf as u32 => RingBuf,
1230            x if x == InodeStorage as u32 => InodeStorage,
1231            x if x == TaskStorage as u32 => TaskStorage,
1232            x if x == BloomFilter as u32 => BloomFilter,
1233            x if x == UserRingBuf as u32 => UserRingBuf,
1234            _ => Unknown,
1235        }
1236    }
1237}
1238
1239impl From<MapType> for u32 {
1240    fn from(value: MapType) -> Self {
1241        value as u32
1242    }
1243}
1244
1245/// An iterator over the keys of a BPF map.
1246#[derive(Debug)]
1247pub struct MapKeyIter<'map> {
1248    map_fd: BorrowedFd<'map>,
1249    prev: Option<Vec<u8>>,
1250    next: Vec<u8>,
1251}
1252
1253impl<'map> MapKeyIter<'map> {
1254    fn new(map_fd: BorrowedFd<'map>, key_size: u32) -> Self {
1255        Self {
1256            map_fd,
1257            prev: None,
1258            next: vec![0; key_size as usize],
1259        }
1260    }
1261}
1262
1263impl Iterator for MapKeyIter<'_> {
1264    type Item = Vec<u8>;
1265
1266    fn next(&mut self) -> Option<Self::Item> {
1267        let prev = self.prev.as_ref().map_or(ptr::null(), |p| p.as_ptr());
1268
1269        let ret = unsafe {
1270            libbpf_sys::bpf_map_get_next_key(
1271                self.map_fd.as_raw_fd(),
1272                prev as _,
1273                self.next.as_mut_ptr() as _,
1274            )
1275        };
1276        if ret != 0 {
1277            None
1278        } else {
1279            self.prev = Some(self.next.clone());
1280            Some(self.next.clone())
1281        }
1282    }
1283}
1284
1285/// A convenience wrapper for [`bpf_map_info`][libbpf_sys::bpf_map_info]. It
1286/// provides the ability to retrieve the details of a certain map.
1287#[derive(Debug)]
1288pub struct MapInfo {
1289    /// The inner [`bpf_map_info`][libbpf_sys::bpf_map_info] object.
1290    pub info: bpf_map_info,
1291}
1292
1293impl MapInfo {
1294    /// Create a `MapInfo` object from a fd.
1295    pub fn new(fd: BorrowedFd<'_>) -> Result<Self> {
1296        let mut map_info = bpf_map_info::default();
1297        let mut size = mem::size_of_val(&map_info) as u32;
1298        // SAFETY: All pointers are derived from references and hence valid.
1299        let () = util::parse_ret(unsafe {
1300            bpf_obj_get_info_by_fd(
1301                fd.as_raw_fd(),
1302                &mut map_info as *mut bpf_map_info as *mut c_void,
1303                &mut size as *mut u32,
1304            )
1305        })?;
1306        Ok(Self { info: map_info })
1307    }
1308
1309    /// Get the map type
1310    #[inline]
1311    pub fn map_type(&self) -> MapType {
1312        MapType::from(self.info.type_)
1313    }
1314
1315    /// Get the name of this map.
1316    ///
1317    /// Returns error if the underlying data in the structure is not a valid
1318    /// utf-8 string.
1319    pub fn name<'a>(&self) -> Result<&'a str> {
1320        // SAFETY: convert &[i8] to &[u8], and then cast that to &str. i8 and u8 has the same size.
1321        let char_slice =
1322            unsafe { from_raw_parts(self.info.name[..].as_ptr().cast(), self.info.name.len()) };
1323
1324        util::c_char_slice_to_cstr(char_slice)
1325            .ok_or_else(|| Error::with_invalid_data("no nul byte found"))?
1326            .to_str()
1327            .map_err(Error::with_invalid_data)
1328    }
1329
1330    /// Get the map flags.
1331    #[inline]
1332    pub fn flags(&self) -> MapFlags {
1333        MapFlags::from_bits_truncate(self.info.map_flags as u64)
1334    }
1335}
1336
1337#[cfg(test)]
1338mod tests {
1339    use super::*;
1340
1341    use std::mem::discriminant;
1342
1343    #[test]
1344    fn map_type() {
1345        use MapType::*;
1346
1347        for t in [
1348            Unspec,
1349            Hash,
1350            Array,
1351            ProgArray,
1352            PerfEventArray,
1353            PercpuHash,
1354            PercpuArray,
1355            StackTrace,
1356            CgroupArray,
1357            LruHash,
1358            LruPercpuHash,
1359            LpmTrie,
1360            ArrayOfMaps,
1361            HashOfMaps,
1362            Devmap,
1363            Sockmap,
1364            Cpumap,
1365            Xskmap,
1366            Sockhash,
1367            CgroupStorage,
1368            ReuseportSockarray,
1369            PercpuCgroupStorage,
1370            Queue,
1371            Stack,
1372            SkStorage,
1373            DevmapHash,
1374            StructOps,
1375            RingBuf,
1376            InodeStorage,
1377            TaskStorage,
1378            BloomFilter,
1379            UserRingBuf,
1380            Unknown,
1381        ] {
1382            // check if discriminants match after a roundtrip conversion
1383            assert_eq!(discriminant(&t), discriminant(&MapType::from(t as u32)));
1384        }
1385    }
1386}