Skip to main content

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::fs::File;
9use std::io;
10use std::io::BufRead as _;
11use std::io::BufReader;
12use std::marker::PhantomData;
13use std::mem;
14use std::mem::transmute;
15use std::ops::Deref;
16use std::os::unix::ffi::OsStrExt;
17use std::os::unix::io::AsFd;
18use std::os::unix::io::AsRawFd;
19use std::os::unix::io::BorrowedFd;
20use std::os::unix::io::FromRawFd;
21use std::os::unix::io::OwnedFd;
22use std::os::unix::io::RawFd;
23use std::path::Path;
24use std::ptr;
25use std::ptr::NonNull;
26use std::slice;
27use std::slice::from_raw_parts;
28
29use bitflags::bitflags;
30use libbpf_sys::bpf_map_info;
31use libbpf_sys::bpf_obj_get_info_by_fd;
32
33use crate::error;
34use crate::util;
35use crate::util::parse_ret_i32;
36use crate::util::validate_bpf_ret;
37use crate::AsRawLibbpf;
38use crate::Error;
39use crate::ErrorExt as _;
40use crate::Link;
41use crate::Mut;
42use crate::ProgramType;
43use crate::Result;
44
45/// An immutable parsed but not yet loaded BPF map.
46pub type OpenMap<'obj> = OpenMapImpl<'obj>;
47/// A mutable parsed but not yet loaded BPF map.
48pub type OpenMapMut<'obj> = OpenMapImpl<'obj, Mut>;
49
50/// Represents a parsed but not yet loaded BPF map.
51///
52/// This object exposes operations that need to happen before the map is created.
53///
54/// Some methods require working with raw bytes. You may find libraries such as
55/// [`plain`](https://crates.io/crates/plain) helpful.
56#[derive(Debug)]
57#[repr(transparent)]
58pub struct OpenMapImpl<'obj, T = ()> {
59    ptr: NonNull<libbpf_sys::bpf_map>,
60    _phantom: PhantomData<&'obj T>,
61}
62
63impl<'obj> OpenMap<'obj> {
64    /// Create a new [`OpenMap`] from a ptr to a `libbpf_sys::bpf_map`.
65    pub fn new(object: &'obj libbpf_sys::bpf_map) -> Self {
66        // SAFETY: We inferred the address from a reference, which is always
67        //         valid.
68        Self {
69            ptr: unsafe { NonNull::new_unchecked(object as *const _ as *mut _) },
70            _phantom: PhantomData,
71        }
72    }
73
74    /// Retrieve the [`OpenMap`]'s name.
75    pub fn name(&self) -> &'obj OsStr {
76        // SAFETY: We ensured `ptr` is valid during construction.
77        let name_ptr = unsafe { libbpf_sys::bpf_map__name(self.ptr.as_ptr()) };
78        // SAFETY: `bpf_map__name` can return NULL but only if it's passed
79        //          NULL. We know `ptr` is not NULL.
80        let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
81        OsStr::from_bytes(name_c_str.to_bytes())
82    }
83
84    /// Retrieve type of the map.
85    pub fn map_type(&self) -> MapType {
86        let ty = unsafe { libbpf_sys::bpf_map__type(self.ptr.as_ptr()) };
87        MapType::from(ty)
88    }
89
90    fn initial_value_raw(&self) -> (*mut u8, usize) {
91        let mut size = 0u64;
92        let ptr = unsafe {
93            libbpf_sys::bpf_map__initial_value(self.ptr.as_ptr(), (&raw mut size).cast())
94        };
95        (ptr.cast(), size as _)
96    }
97
98    /// Retrieve the initial value of the map.
99    pub fn initial_value(&self) -> Option<&[u8]> {
100        let (ptr, size) = self.initial_value_raw();
101        if ptr.is_null() {
102            None
103        } else {
104            let data = unsafe { slice::from_raw_parts(ptr.cast::<u8>(), size) };
105            Some(data)
106        }
107    }
108
109    /// Retrieve the maximum number of entries of the map.
110    pub fn max_entries(&self) -> u32 {
111        unsafe { libbpf_sys::bpf_map__max_entries(self.ptr.as_ptr()) }
112    }
113
114    /// Return `true` if the map is set to be auto-created during load, `false` otherwise.
115    pub fn autocreate(&self) -> bool {
116        unsafe { libbpf_sys::bpf_map__autocreate(self.ptr.as_ptr()) }
117    }
118
119    /// Retrieve the map flags.
120    pub fn map_flags(&self) -> u32 {
121        unsafe { libbpf_sys::bpf_map__map_flags(self.ptr.as_ptr()) }
122    }
123
124    /// Retrieve the map numa node.
125    pub fn numa_node(&self) -> u32 {
126        unsafe { libbpf_sys::bpf_map__numa_node(self.ptr.as_ptr()) }
127    }
128
129    /// Retrieve the key size of the map in bytes.
130    pub fn key_size(&self) -> u32 {
131        unsafe { libbpf_sys::bpf_map__key_size(self.ptr.as_ptr()) }
132    }
133
134    /// Retrieve the value size of the map in bytes.
135    pub fn value_size(&self) -> u32 {
136        unsafe { libbpf_sys::bpf_map__value_size(self.ptr.as_ptr()) }
137    }
138}
139
140impl<'obj> OpenMapMut<'obj> {
141    /// Create a new [`OpenMapMut`] from a ptr to a `libbpf_sys::bpf_map`.
142    pub fn new_mut(object: &'obj mut libbpf_sys::bpf_map) -> Self {
143        Self {
144            ptr: unsafe { NonNull::new_unchecked(object as *mut _) },
145            _phantom: PhantomData,
146        }
147    }
148
149    /// Retrieve the initial value of the map.
150    pub fn initial_value_mut(&mut self) -> Option<&mut [u8]> {
151        let (ptr, size) = self.initial_value_raw();
152        if ptr.is_null() {
153            None
154        } else {
155            let data = unsafe { slice::from_raw_parts_mut(ptr.cast::<u8>(), size) };
156            Some(data)
157        }
158    }
159
160    /// Bind map to a particular network device.
161    ///
162    /// Used for offloading maps to hardware.
163    pub fn set_map_ifindex(&mut self, idx: u32) {
164        unsafe { libbpf_sys::bpf_map__set_ifindex(self.ptr.as_ptr(), idx) };
165    }
166
167    /// Set the initial value of the map.
168    pub fn set_initial_value(&mut self, data: &[u8]) -> Result<()> {
169        let ret = unsafe {
170            libbpf_sys::bpf_map__set_initial_value(
171                self.ptr.as_ptr(),
172                data.as_ptr().cast::<c_void>(),
173                data.len() as libbpf_sys::size_t,
174            )
175        };
176
177        util::parse_ret(ret)
178    }
179
180    /// Set the type of the map.
181    pub fn set_type(&mut self, ty: MapType) -> Result<()> {
182        let ret = unsafe { libbpf_sys::bpf_map__set_type(self.ptr.as_ptr(), ty as u32) };
183        util::parse_ret(ret)
184    }
185
186    /// Set the key size of the map in bytes.
187    pub fn set_key_size(&mut self, size: u32) -> Result<()> {
188        let ret = unsafe { libbpf_sys::bpf_map__set_key_size(self.ptr.as_ptr(), size) };
189        util::parse_ret(ret)
190    }
191
192    /// Set the value size of the map in bytes.
193    pub fn set_value_size(&mut self, size: u32) -> Result<()> {
194        let ret = unsafe { libbpf_sys::bpf_map__set_value_size(self.ptr.as_ptr(), size) };
195        util::parse_ret(ret)
196    }
197
198    /// Set the maximum number of entries this map can have.
199    pub fn set_max_entries(&mut self, count: u32) -> Result<()> {
200        let ret = unsafe { libbpf_sys::bpf_map__set_max_entries(self.ptr.as_ptr(), count) };
201        util::parse_ret(ret)
202    }
203
204    /// Set flags on this map.
205    pub fn set_map_flags(&mut self, flags: u32) -> Result<()> {
206        let ret = unsafe { libbpf_sys::bpf_map__set_map_flags(self.ptr.as_ptr(), flags) };
207        util::parse_ret(ret)
208    }
209
210    /// Set the NUMA node for this map.
211    ///
212    /// This can be used to ensure that the map is allocated on a particular
213    /// NUMA node, which can be useful for performance-critical applications.
214    pub fn set_numa_node(&mut self, numa_node: u32) -> Result<()> {
215        let ret = unsafe { libbpf_sys::bpf_map__set_numa_node(self.ptr.as_ptr(), numa_node) };
216        util::parse_ret(ret)
217    }
218
219    /// Set the inner map FD.
220    ///
221    /// This is used for nested maps, where the value type of the outer map is a pointer to the
222    /// inner map.
223    pub fn set_inner_map_fd(&mut self, inner_map_fd: BorrowedFd<'_>) -> Result<()> {
224        let ret = unsafe {
225            libbpf_sys::bpf_map__set_inner_map_fd(self.ptr.as_ptr(), inner_map_fd.as_raw_fd())
226        };
227        util::parse_ret(ret)
228    }
229
230    /// Set the `map_extra` field for this map.
231    ///
232    /// Allows users to pass additional data to the
233    /// kernel when loading the map. The kernel will store this value in the
234    /// `bpf_map_info` struct associated with the map.
235    ///
236    /// This can be used to pass data to the kernel that is not otherwise
237    /// representable via the existing `bpf_map_def` fields.
238    pub fn set_map_extra(&mut self, map_extra: u64) -> Result<()> {
239        let ret = unsafe { libbpf_sys::bpf_map__set_map_extra(self.ptr.as_ptr(), map_extra) };
240        util::parse_ret(ret)
241    }
242
243    /// Set whether or not libbpf should automatically create this map during load phase.
244    pub fn set_autocreate(&mut self, autocreate: bool) -> Result<()> {
245        let ret = unsafe { libbpf_sys::bpf_map__set_autocreate(self.ptr.as_ptr(), autocreate) };
246        util::parse_ret(ret)
247    }
248
249    /// Set where the map should be pinned.
250    ///
251    /// Note this does not actually create the pin.
252    pub fn set_pin_path<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
253        let path_c = util::path_to_cstring(path)?;
254        let path_ptr = path_c.as_ptr();
255
256        let ret = unsafe { libbpf_sys::bpf_map__set_pin_path(self.ptr.as_ptr(), path_ptr) };
257        util::parse_ret(ret)
258    }
259
260    /// Reuse an fd for a BPF map
261    pub fn reuse_fd(&mut self, fd: BorrowedFd<'_>) -> Result<()> {
262        let ret = unsafe { libbpf_sys::bpf_map__reuse_fd(self.ptr.as_ptr(), fd.as_raw_fd()) };
263        util::parse_ret(ret)
264    }
265
266    /// Reuse an already-pinned map for `self`.
267    pub fn reuse_pinned_map<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
268        let cstring = util::path_to_cstring(path)?;
269
270        let fd = unsafe { libbpf_sys::bpf_obj_get(cstring.as_ptr()) };
271        if fd < 0 {
272            return Err(Error::from(io::Error::last_os_error()));
273        }
274
275        let fd = unsafe { OwnedFd::from_raw_fd(fd) };
276
277        let reuse_result = self.reuse_fd(fd.as_fd());
278
279        reuse_result
280    }
281}
282
283impl<'obj> Deref for OpenMapMut<'obj> {
284    type Target = OpenMap<'obj>;
285
286    fn deref(&self) -> &Self::Target {
287        // SAFETY: `OpenMapImpl` is `repr(transparent)` and so in-memory
288        //         representation of both types is the same.
289        unsafe { transmute::<&OpenMapMut<'obj>, &OpenMap<'obj>>(self) }
290    }
291}
292
293impl<T> AsRawLibbpf for OpenMapImpl<'_, T> {
294    type LibbpfType = libbpf_sys::bpf_map;
295
296    /// Retrieve the underlying [`libbpf_sys::bpf_map`].
297    fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
298        self.ptr
299    }
300}
301
302pub(crate) fn map_fd(map: NonNull<libbpf_sys::bpf_map>) -> Option<RawFd> {
303    let fd = unsafe { libbpf_sys::bpf_map__fd(map.as_ptr()) };
304    let fd = util::parse_ret_i32(fd).ok();
305    fd
306}
307
308/// Return the size of one value including padding for interacting with per-cpu
309/// maps. The values are aligned to 8 bytes.
310fn percpu_aligned_value_size<M>(map: &M) -> usize
311where
312    M: MapCore + ?Sized,
313{
314    let val_size = map.value_size() as usize;
315    util::roundup(val_size, 8)
316}
317
318/// Returns the size of the buffer needed for a lookup/update of a per-cpu map.
319fn percpu_buffer_size<M>(map: &M) -> Result<usize>
320where
321    M: MapCore + ?Sized,
322{
323    let aligned_val_size = percpu_aligned_value_size(map);
324    let ncpu = crate::num_possible_cpus()?;
325    Ok(ncpu * aligned_val_size)
326}
327
328/// Apply a key check and return a null pointer in case of dealing with queue/stack/bloom-filter
329/// map, before passing the key to the bpf functions that support the map of type
330/// queue/stack/bloom-filter.
331fn map_key<M>(map: &M, key: &[u8]) -> *const c_void
332where
333    M: MapCore + ?Sized,
334{
335    // For all they keyless maps we null out the key per documentation of libbpf
336    if map.key_size() == 0 && map.map_type().is_keyless() {
337        return ptr::null();
338    }
339
340    key.as_ptr().cast::<c_void>()
341}
342
343/// Internal selector for which underlying lookup operation to perform.
344///
345/// `bpf_map_lookup_elem_flags` accepts `MapFlags`, while
346/// `bpf_map_lookup_and_delete_elem` does not. Wrapping the choice in an
347/// enum keeps the two cases distinct without leaking an unused `flags`
348/// argument into the lookup-and-delete path.
349enum LookupOp {
350    Lookup(MapFlags),
351    LookupAndDelete,
352}
353
354/// Internal function to perform a map lookup and write the value into raw pointer.
355/// Returns `Ok(true)` if the key was found, `Ok(false)` if not found, or an error.
356fn lookup_raw<M>(
357    map: &M,
358    key: &[u8],
359    value: &mut [mem::MaybeUninit<u8>],
360    op: LookupOp,
361) -> Result<bool>
362where
363    M: MapCore + ?Sized,
364{
365    if key.len() != map.key_size() as usize {
366        return Err(Error::with_invalid_data(format!(
367            "key_size {} != {}",
368            key.len(),
369            map.key_size()
370        )));
371    }
372
373    // Make sure the internal users of this function pass the expected buffer size
374    debug_assert_eq!(
375        value.len(),
376        if map.map_type().is_percpu() {
377            percpu_buffer_size(map).unwrap()
378        } else {
379            map.value_size() as usize
380        }
381    );
382
383    let ret = unsafe {
384        match op {
385            LookupOp::Lookup(flags) => libbpf_sys::bpf_map_lookup_elem_flags(
386                map.as_fd().as_raw_fd(),
387                map_key(map, key),
388                // TODO: Use `MaybeUninit::slice_as_mut_ptr` once stable.
389                value.as_mut_ptr().cast(),
390                flags.bits(),
391            ),
392            LookupOp::LookupAndDelete => libbpf_sys::bpf_map_lookup_and_delete_elem(
393                map.as_fd().as_raw_fd(),
394                map_key(map, key),
395                value.as_mut_ptr().cast(),
396            ),
397        }
398    };
399
400    if ret == 0 {
401        Ok(true)
402    } else {
403        let err = io::Error::last_os_error();
404        if err.kind() == io::ErrorKind::NotFound {
405            Ok(false)
406        } else {
407            Err(Error::from(err))
408        }
409    }
410}
411
412/// Internal function to return a value from a map into a buffer of the given size.
413fn lookup_raw_vec<M>(map: &M, key: &[u8], op: LookupOp, out_size: usize) -> Result<Option<Vec<u8>>>
414where
415    M: MapCore + ?Sized,
416{
417    // Allocate without initializing (avoiding memset)
418    let mut out = Vec::with_capacity(out_size);
419
420    match lookup_raw(map, key, out.spare_capacity_mut(), op)? {
421        true => {
422            // SAFETY: `lookup_raw` successfully filled the buffer
423            unsafe {
424                out.set_len(out_size);
425            }
426            Ok(Some(out))
427        }
428        false => Ok(None),
429    }
430}
431
432/// Internal function to update a map. This does not check the length of the
433/// supplied value.
434fn update_raw<M>(map: &M, key: &[u8], value: &[u8], flags: MapFlags) -> Result<()>
435where
436    M: MapCore + ?Sized,
437{
438    if key.len() != map.key_size() as usize {
439        return Err(Error::with_invalid_data(format!(
440            "key_size {} != {}",
441            key.len(),
442            map.key_size()
443        )));
444    };
445
446    let ret = unsafe {
447        libbpf_sys::bpf_map_update_elem(
448            map.as_fd().as_raw_fd(),
449            map_key(map, key),
450            value.as_ptr().cast::<c_void>(),
451            flags.bits(),
452        )
453    };
454
455    util::parse_ret(ret)
456}
457
458/// Internal function to batch lookup (and delete) elements from a map.
459fn lookup_batch_raw<M>(
460    map: &M,
461    count: u32,
462    elem_flags: MapFlags,
463    flags: MapFlags,
464    delete: bool,
465) -> BatchedMapIter<'_>
466where
467    M: MapCore + ?Sized,
468{
469    #[allow(clippy::needless_update)]
470    let opts = libbpf_sys::bpf_map_batch_opts {
471        sz: mem::size_of::<libbpf_sys::bpf_map_batch_opts>() as _,
472        elem_flags: elem_flags.bits(),
473        flags: flags.bits(),
474        // bpf_map_batch_opts might have padding fields on some platform
475        ..Default::default()
476    };
477
478    // for maps of type BPF_MAP_TYPE_{HASH, PERCPU_HASH, LRU_HASH, LRU_PERCPU_HASH}
479    // the key size must be at least 4 bytes
480    let key_size = if map.map_type().is_hash_map() {
481        map.key_size().max(4)
482    } else {
483        map.key_size()
484    };
485
486    BatchedMapIter::new(map.as_fd(), count, key_size, map.value_size(), opts, delete)
487}
488
489/// Intneral function that returns an error for per-cpu and bloom filter maps.
490fn check_not_bloom_or_percpu<M>(map: &M) -> Result<()>
491where
492    M: MapCore + ?Sized,
493{
494    if map.map_type().is_bloom_filter() {
495        return Err(Error::with_invalid_data(
496            "lookup_bloom_filter() must be used for bloom filter maps",
497        ));
498    }
499    if map.map_type().is_percpu() {
500        return Err(Error::with_invalid_data(format!(
501            "lookup_percpu() must be used for per-cpu maps (type of the map is {:?})",
502            map.map_type(),
503        )));
504    }
505
506    Ok(())
507}
508
509#[allow(clippy::wildcard_imports)]
510mod private {
511    use super::*;
512
513    pub trait Sealed {}
514
515    impl<T> Sealed for MapImpl<'_, T> {}
516    impl Sealed for MapHandle {}
517}
518
519/// A trait representing core functionality common to fully initialized maps.
520pub trait MapCore: Debug + AsFd + private::Sealed {
521    /// Retrieve the map's name.
522    fn name(&self) -> &OsStr;
523
524    /// Retrieve type of the map.
525    fn map_type(&self) -> MapType;
526
527    /// Retrieve the size of the map's keys.
528    fn key_size(&self) -> u32;
529
530    /// Retrieve the size of the map's values.
531    fn value_size(&self) -> u32;
532
533    /// Retrieve `max_entries` of the map.
534    fn max_entries(&self) -> u32;
535
536    /// Fetch extra map information
537    #[inline]
538    fn info(&self) -> Result<MapInfo> {
539        MapInfo::new(self.as_fd())
540    }
541
542    /// Query map information from `/proc/self/fdinfo`.
543    ///
544    /// This provides information not available through [`MapInfo`],
545    /// such as [`memlock`][MapFdInfo::memlock] (memory usage).
546    #[inline]
547    fn query_fdinfo(&self) -> Result<MapFdInfo> {
548        MapFdInfo::from_fd(self.as_fd())
549    }
550
551    /// Returns an iterator over keys in this map
552    ///
553    /// Note that if the map is not stable (stable meaning no updates or deletes) during iteration,
554    /// iteration can skip keys, restart from the beginning, or duplicate keys. In other words,
555    /// iteration becomes unpredictable.
556    fn keys(&self) -> MapKeyIter<'_> {
557        MapKeyIter::new(self.as_fd(), self.key_size())
558    }
559
560    /// Returns map value as `Vec` of `u8`.
561    ///
562    /// `key` must have exactly [`Self::key_size()`] elements.
563    ///
564    /// If the map is one of the per-cpu data structures, the function [`Self::lookup_percpu()`]
565    /// must be used.
566    /// If the map is of type `bloom_filter` the function [`Self::lookup_bloom_filter()`] must be
567    /// used
568    fn lookup(&self, key: &[u8], flags: MapFlags) -> Result<Option<Vec<u8>>> {
569        check_not_bloom_or_percpu(self)?;
570        let out_size = self.value_size() as usize;
571        lookup_raw_vec(self, key, LookupOp::Lookup(flags), out_size)
572    }
573
574    /// Looks up a map value into a pre-allocated buffer, avoiding allocation.
575    ///
576    /// This method provides a zero-allocation alternative to [`Self::lookup()`].
577    ///
578    /// `key` must have exactly [`Self::key_size()`] elements.
579    /// `value` must have exactly [`Self::value_size()`] elements.
580    ///
581    /// Returns `Ok(true)` if the key was found and the buffer was filled,
582    /// `Ok(false)` if the key was not found, or an error.
583    ///
584    /// If the map is one of the per-cpu data structures, this function cannot be used.
585    /// If the map is of type `bloom_filter`, this function cannot be used.
586    fn lookup_into(&self, key: &[u8], value: &mut [u8], flags: MapFlags) -> Result<bool> {
587        check_not_bloom_or_percpu(self)?;
588
589        if value.len() != self.value_size() as usize {
590            return Err(Error::with_invalid_data(format!(
591                "value buffer size {} != {}",
592                value.len(),
593                self.value_size()
594            )));
595        }
596
597        // SAFETY: `u8` and `MaybeUninit<u8>` have the same in-memory representation.
598        let value = unsafe {
599            slice::from_raw_parts_mut::<mem::MaybeUninit<u8>>(
600                value.as_mut_ptr().cast(),
601                value.len(),
602            )
603        };
604        lookup_raw(self, key, value, LookupOp::Lookup(flags))
605    }
606
607    /// Returns many elements in batch mode from the map.
608    ///
609    /// `count` specifies the batch size.
610    fn lookup_batch(
611        &self,
612        count: u32,
613        elem_flags: MapFlags,
614        flags: MapFlags,
615    ) -> Result<BatchedMapIter<'_>> {
616        check_not_bloom_or_percpu(self)?;
617        Ok(lookup_batch_raw(self, count, elem_flags, flags, false))
618    }
619
620    /// Returns many elements in batch mode from the map.
621    ///
622    /// `count` specifies the batch size.
623    fn lookup_and_delete_batch(
624        &self,
625        count: u32,
626        elem_flags: MapFlags,
627        flags: MapFlags,
628    ) -> Result<BatchedMapIter<'_>> {
629        check_not_bloom_or_percpu(self)?;
630        Ok(lookup_batch_raw(self, count, elem_flags, flags, true))
631    }
632
633    /// Returns if the given value is likely present in `bloom_filter` as `bool`.
634    ///
635    /// `value` must have exactly [`Self::value_size()`] elements.
636    fn lookup_bloom_filter(&self, value: &[u8]) -> Result<bool> {
637        let ret = unsafe {
638            libbpf_sys::bpf_map_lookup_elem(
639                self.as_fd().as_raw_fd(),
640                ptr::null(),
641                value.to_vec().as_mut_ptr().cast::<c_void>(),
642            )
643        };
644
645        if ret == 0 {
646            Ok(true)
647        } else {
648            let err = io::Error::last_os_error();
649            if err.kind() == io::ErrorKind::NotFound {
650                Ok(false)
651            } else {
652                Err(Error::from(err))
653            }
654        }
655    }
656
657    /// Returns one value per cpu as `Vec` of `Vec` of `u8` for per per-cpu maps.
658    ///
659    /// For normal maps, [`Self::lookup()`] must be used.
660    fn lookup_percpu(&self, key: &[u8], flags: MapFlags) -> Result<Option<Vec<Vec<u8>>>> {
661        if !self.map_type().is_percpu() && self.map_type() != MapType::Unknown {
662            return Err(Error::with_invalid_data(format!(
663                "lookup() must be used for maps that are not per-cpu (type of the map is {:?})",
664                self.map_type(),
665            )));
666        }
667
668        let val_size = self.value_size() as usize;
669        let aligned_val_size = percpu_aligned_value_size(self);
670        let out_size = percpu_buffer_size(self)?;
671
672        let raw_res = lookup_raw_vec(self, key, LookupOp::Lookup(flags), out_size)?;
673        if let Some(raw_vals) = raw_res {
674            let mut out = Vec::new();
675            for chunk in raw_vals.chunks_exact(aligned_val_size) {
676                out.push(chunk[..val_size].to_vec());
677            }
678            Ok(Some(out))
679        } else {
680            Ok(None)
681        }
682    }
683
684    /// Deletes an element from the map.
685    ///
686    /// `key` must have exactly [`Self::key_size()`] elements.
687    fn delete(&self, key: &[u8]) -> Result<()> {
688        if key.len() != self.key_size() as usize {
689            return Err(Error::with_invalid_data(format!(
690                "key_size {} != {}",
691                key.len(),
692                self.key_size()
693            )));
694        };
695
696        let ret = unsafe {
697            libbpf_sys::bpf_map_delete_elem(self.as_fd().as_raw_fd(), key.as_ptr().cast::<c_void>())
698        };
699        util::parse_ret(ret)
700    }
701
702    /// Deletes many elements in batch mode from the map.
703    ///
704    /// `keys` must have exactly `Self::key_size() * count` elements.
705    fn delete_batch(
706        &self,
707        keys: &[u8],
708        count: u32,
709        elem_flags: MapFlags,
710        flags: MapFlags,
711    ) -> Result<()> {
712        if keys.len() as u32 / count != self.key_size() || (keys.len() as u32) % count != 0 {
713            return Err(Error::with_invalid_data(format!(
714                "batch key_size {} != {} * {}",
715                keys.len(),
716                self.key_size(),
717                count
718            )));
719        };
720
721        #[allow(clippy::needless_update)]
722        let opts = libbpf_sys::bpf_map_batch_opts {
723            sz: mem::size_of::<libbpf_sys::bpf_map_batch_opts>() as _,
724            elem_flags: elem_flags.bits(),
725            flags: flags.bits(),
726            // bpf_map_batch_opts might have padding fields on some platform
727            ..Default::default()
728        };
729
730        let mut count = count;
731        let ret = unsafe {
732            libbpf_sys::bpf_map_delete_batch(
733                self.as_fd().as_raw_fd(),
734                keys.as_ptr().cast::<c_void>(),
735                &mut count,
736                &opts as *const libbpf_sys::bpf_map_batch_opts,
737            )
738        };
739        util::parse_ret(ret)
740    }
741
742    /// Same as [`Self::lookup()`] except this also deletes the key from the map.
743    ///
744    /// Implemented in the kernel for [`MapType::Queue`] and [`MapType::Stack`],
745    /// and (since Linux 5.14) for [`MapType::Hash`] / [`MapType::LruHash`] /
746    /// [`MapType::PercpuHash`] / [`MapType::LruPercpuHash`].
747    ///
748    /// `key` must have exactly [`Self::key_size()`] elements.
749    fn lookup_and_delete(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
750        let out_size = self.value_size() as usize;
751        lookup_raw_vec(self, key, LookupOp::LookupAndDelete, out_size)
752    }
753
754    /// Same as [`Self::lookup_into()`] except this also deletes the key from the map.
755    ///
756    /// This method provides a zero-allocation alternative to [`Self::lookup_and_delete()`].
757    ///
758    /// `key` must have exactly [`Self::key_size()`] elements.
759    /// `value` must have exactly [`Self::value_size()`] elements.
760    ///
761    /// Returns `Ok(true)` if the key was found and the buffer was filled,
762    /// `Ok(false)` if the key was not found, or an error.
763    ///
764    /// See [`Self::lookup_and_delete()`] for kernel support details.
765    fn lookup_into_and_delete(&self, key: &[u8], value: &mut [u8]) -> Result<bool> {
766        if value.len() != self.value_size() as usize {
767            return Err(Error::with_invalid_data(format!(
768                "value buffer size {} != {}",
769                value.len(),
770                self.value_size()
771            )));
772        }
773
774        // SAFETY: `u8` and `MaybeUninit<u8>` have the same in-memory representation.
775        let value = unsafe {
776            slice::from_raw_parts_mut::<mem::MaybeUninit<u8>>(
777                value.as_mut_ptr().cast(),
778                value.len(),
779            )
780        };
781        lookup_raw(self, key, value, LookupOp::LookupAndDelete)
782    }
783
784    /// Update an element.
785    ///
786    /// `key` must have exactly [`Self::key_size()`] elements. `value` must have exactly
787    /// [`Self::value_size()`] elements.
788    ///
789    /// For per-cpu maps, [`Self::update_percpu()`] must be used.
790    fn update(&self, key: &[u8], value: &[u8], flags: MapFlags) -> Result<()> {
791        if self.map_type().is_percpu() {
792            return Err(Error::with_invalid_data(format!(
793                "update_percpu() must be used for per-cpu maps (type of the map is {:?})",
794                self.map_type(),
795            )));
796        }
797
798        if value.len() != self.value_size() as usize {
799            return Err(Error::with_invalid_data(format!(
800                "value_size {} != {}",
801                value.len(),
802                self.value_size()
803            )));
804        };
805
806        update_raw(self, key, value, flags)
807    }
808
809    /// Updates many elements in batch mode in the map
810    ///
811    /// `keys` must have exactly `Self::key_size() * count` elements. `values` must have exactly
812    /// `Self::key_size() * count` elements.
813    fn update_batch(
814        &self,
815        keys: &[u8],
816        values: &[u8],
817        count: u32,
818        elem_flags: MapFlags,
819        flags: MapFlags,
820    ) -> Result<()> {
821        if keys.len() as u32 / count != self.key_size() || (keys.len() as u32) % count != 0 {
822            return Err(Error::with_invalid_data(format!(
823                "batch key_size {} != {} * {}",
824                keys.len(),
825                self.key_size(),
826                count
827            )));
828        };
829
830        if values.len() as u32 / count != self.value_size() || (values.len() as u32) % count != 0 {
831            return Err(Error::with_invalid_data(format!(
832                "batch value_size {} != {} * {}",
833                values.len(),
834                self.value_size(),
835                count
836            )));
837        }
838
839        #[allow(clippy::needless_update)]
840        let opts = libbpf_sys::bpf_map_batch_opts {
841            sz: mem::size_of::<libbpf_sys::bpf_map_batch_opts>() as _,
842            elem_flags: elem_flags.bits(),
843            flags: flags.bits(),
844            // bpf_map_batch_opts might have padding fields on some platform
845            ..Default::default()
846        };
847
848        let mut count = count;
849        let ret = unsafe {
850            libbpf_sys::bpf_map_update_batch(
851                self.as_fd().as_raw_fd(),
852                keys.as_ptr().cast::<c_void>(),
853                values.as_ptr().cast::<c_void>(),
854                &mut count,
855                &opts as *const libbpf_sys::bpf_map_batch_opts,
856            )
857        };
858
859        util::parse_ret(ret)
860    }
861
862    /// Update an element in an per-cpu map with one value per cpu.
863    ///
864    /// `key` must have exactly [`Self::key_size()`] elements. `value` must have one
865    /// element per cpu (see [`num_possible_cpus`][crate::num_possible_cpus])
866    /// with exactly [`Self::value_size()`] elements each.
867    ///
868    /// For per-cpu maps, [`Self::update_percpu()`] must be used.
869    fn update_percpu(&self, key: &[u8], values: &[Vec<u8>], flags: MapFlags) -> Result<()> {
870        if !self.map_type().is_percpu() && self.map_type() != MapType::Unknown {
871            return Err(Error::with_invalid_data(format!(
872                "update() must be used for maps that are not per-cpu (type of the map is {:?})",
873                self.map_type(),
874            )));
875        }
876
877        if values.len() != crate::num_possible_cpus()? {
878            return Err(Error::with_invalid_data(format!(
879                "number of values {} != number of cpus {}",
880                values.len(),
881                crate::num_possible_cpus()?
882            )));
883        };
884
885        let val_size = self.value_size() as usize;
886        let aligned_val_size = percpu_aligned_value_size(self);
887        let buf_size = percpu_buffer_size(self)?;
888
889        let mut value_buf = vec![0; buf_size];
890
891        for (i, val) in values.iter().enumerate() {
892            if val.len() != val_size {
893                return Err(Error::with_invalid_data(format!(
894                    "value size for cpu {} is {} != {}",
895                    i,
896                    val.len(),
897                    val_size
898                )));
899            }
900
901            value_buf[(i * aligned_val_size)..(i * aligned_val_size + val_size)]
902                .copy_from_slice(val);
903        }
904
905        update_raw(self, key, &value_buf, flags)
906    }
907}
908
909/// An immutable loaded BPF map.
910pub type Map<'obj> = MapImpl<'obj>;
911/// A mutable loaded BPF map.
912pub type MapMut<'obj> = MapImpl<'obj, Mut>;
913
914/// Represents a libbpf-created map.
915///
916/// Some methods require working with raw bytes. You may find libraries such as
917/// [`plain`](https://crates.io/crates/plain) helpful.
918#[derive(Debug)]
919pub struct MapImpl<'obj, T = ()> {
920    ptr: NonNull<libbpf_sys::bpf_map>,
921    _phantom: PhantomData<&'obj T>,
922}
923
924impl<'obj> Map<'obj> {
925    /// Create a [`Map`] from a [`libbpf_sys::bpf_map`].
926    pub fn new(map: &'obj libbpf_sys::bpf_map) -> Self {
927        // SAFETY: We inferred the address from a reference, which is always
928        //         valid.
929        let ptr = unsafe { NonNull::new_unchecked(map as *const _ as *mut _) };
930        assert!(
931            map_fd(ptr).is_some(),
932            "provided BPF map does not have file descriptor"
933        );
934
935        Self {
936            ptr,
937            _phantom: PhantomData,
938        }
939    }
940
941    /// Create a [`Map`] from a [`libbpf_sys::bpf_map`] that does not contain a
942    /// file descriptor.
943    ///
944    /// The caller has to ensure that the [`AsFd`] impl is not used, or a panic
945    /// will be the result.
946    ///
947    /// # Safety
948    ///
949    /// The pointer must point to a loaded map.
950    #[doc(hidden)]
951    pub unsafe fn from_map_without_fd(ptr: NonNull<libbpf_sys::bpf_map>) -> Self {
952        Self {
953            ptr,
954            _phantom: PhantomData,
955        }
956    }
957
958    /// Returns whether map is pinned or not flag
959    pub fn is_pinned(&self) -> bool {
960        unsafe { libbpf_sys::bpf_map__is_pinned(self.ptr.as_ptr()) }
961    }
962
963    /// Returns the `pin_path` if the map is pinned, otherwise, `None`
964    /// is returned.
965    pub fn get_pin_path(&self) -> Option<&OsStr> {
966        let path_ptr = unsafe { libbpf_sys::bpf_map__pin_path(self.ptr.as_ptr()) };
967        if path_ptr.is_null() {
968            // means map is not pinned
969            return None;
970        }
971        let path_c_str = unsafe { CStr::from_ptr(path_ptr) };
972        Some(OsStr::from_bytes(path_c_str.to_bytes()))
973    }
974
975    /// Return `true` if the map was set to be auto-created during load, `false` otherwise.
976    pub fn autocreate(&self) -> bool {
977        unsafe { libbpf_sys::bpf_map__autocreate(self.ptr.as_ptr()) }
978    }
979}
980
981impl<'obj> MapMut<'obj> {
982    /// Create a [`MapMut`] from a [`libbpf_sys::bpf_map`].
983    pub fn new_mut(map: &'obj mut libbpf_sys::bpf_map) -> Self {
984        // SAFETY: We inferred the address from a reference, which is always
985        //         valid.
986        let ptr = unsafe { NonNull::new_unchecked(map as *mut _) };
987        assert!(
988            map_fd(ptr).is_some(),
989            "provided BPF map does not have file descriptor"
990        );
991
992        Self {
993            ptr,
994            _phantom: PhantomData,
995        }
996    }
997
998    /// [Pin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
999    /// this map to bpffs.
1000    pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
1001        let path_c = util::path_to_cstring(path)?;
1002        let path_ptr = path_c.as_ptr();
1003
1004        let ret = unsafe { libbpf_sys::bpf_map__pin(self.ptr.as_ptr(), path_ptr) };
1005        util::parse_ret(ret)
1006    }
1007
1008    /// [Unpin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
1009    /// this map from bpffs.
1010    pub fn unpin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
1011        let path_c = util::path_to_cstring(path)?;
1012        let path_ptr = path_c.as_ptr();
1013        let ret = unsafe { libbpf_sys::bpf_map__unpin(self.ptr.as_ptr(), path_ptr) };
1014        util::parse_ret(ret)
1015    }
1016
1017    /// Attach a struct ops map
1018    pub fn attach_struct_ops(&mut self) -> Result<Link> {
1019        if self.map_type() != MapType::StructOps {
1020            return Err(Error::with_invalid_data(format!(
1021                "Invalid map type ({:?}) for attach_struct_ops()",
1022                self.map_type(),
1023            )));
1024        }
1025
1026        let ptr = unsafe { libbpf_sys::bpf_map__attach_struct_ops(self.ptr.as_ptr()) };
1027        let ptr = validate_bpf_ret(ptr).context("failed to attach struct_ops")?;
1028        // SAFETY: the pointer came from libbpf and has been checked for errors.
1029        let link = unsafe { Link::new(ptr) };
1030        Ok(link)
1031    }
1032}
1033
1034impl<'obj> Deref for MapMut<'obj> {
1035    type Target = Map<'obj>;
1036
1037    fn deref(&self) -> &Self::Target {
1038        unsafe { transmute::<&MapMut<'obj>, &Map<'obj>>(self) }
1039    }
1040}
1041
1042impl<T> AsFd for MapImpl<'_, T> {
1043    #[inline]
1044    fn as_fd(&self) -> BorrowedFd<'_> {
1045        // SANITY: Our map must always have a file descriptor associated with
1046        //         it.
1047        let fd = map_fd(self.ptr).unwrap();
1048        // SAFETY: `fd` is guaranteed to be valid for the lifetime of
1049        //         the created object.
1050        let fd = unsafe { BorrowedFd::borrow_raw(fd) };
1051        fd
1052    }
1053}
1054
1055impl<T> MapCore for MapImpl<'_, T>
1056where
1057    T: Debug,
1058{
1059    fn name(&self) -> &OsStr {
1060        // SAFETY: We ensured `ptr` is valid during construction.
1061        let name_ptr = unsafe { libbpf_sys::bpf_map__name(self.ptr.as_ptr()) };
1062        // SAFETY: `bpf_map__name` can return NULL but only if it's passed
1063        //          NULL. We know `ptr` is not NULL.
1064        let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
1065        OsStr::from_bytes(name_c_str.to_bytes())
1066    }
1067
1068    #[inline]
1069    fn map_type(&self) -> MapType {
1070        let ty = unsafe { libbpf_sys::bpf_map__type(self.ptr.as_ptr()) };
1071        MapType::from(ty)
1072    }
1073
1074    #[inline]
1075    fn key_size(&self) -> u32 {
1076        unsafe { libbpf_sys::bpf_map__key_size(self.ptr.as_ptr()) }
1077    }
1078
1079    #[inline]
1080    fn value_size(&self) -> u32 {
1081        unsafe { libbpf_sys::bpf_map__value_size(self.ptr.as_ptr()) }
1082    }
1083
1084    #[inline]
1085    fn max_entries(&self) -> u32 {
1086        unsafe { libbpf_sys::bpf_map__max_entries(self.ptr.as_ptr()) }
1087    }
1088}
1089
1090impl AsRawLibbpf for Map<'_> {
1091    type LibbpfType = libbpf_sys::bpf_map;
1092
1093    /// Retrieve the underlying [`libbpf_sys::bpf_map`].
1094    #[inline]
1095    fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
1096        self.ptr
1097    }
1098}
1099
1100/// A handle to a map. Handles can be duplicated and dropped.
1101///
1102/// While possible to [create directly][MapHandle::create], in many cases it is
1103/// useful to create such a handle from an existing [`Map`]:
1104/// ```no_run
1105/// # use libbpf_rs::Map;
1106/// # use libbpf_rs::MapHandle;
1107/// # let get_map = || -> &Map { todo!() };
1108/// let map: &Map = get_map();
1109/// let map_handle = MapHandle::try_from(map).unwrap();
1110/// ```
1111///
1112/// Some methods require working with raw bytes. You may find libraries such as
1113/// [`plain`](https://crates.io/crates/plain) helpful.
1114#[derive(Debug)]
1115pub struct MapHandle {
1116    fd: OwnedFd,
1117    name: OsString,
1118    ty: MapType,
1119    key_size: u32,
1120    value_size: u32,
1121    max_entries: u32,
1122}
1123
1124impl MapHandle {
1125    /// Create a bpf map whose data is not managed by libbpf.
1126    pub fn create<T: AsRef<OsStr>>(
1127        map_type: MapType,
1128        name: Option<T>,
1129        key_size: u32,
1130        value_size: u32,
1131        max_entries: u32,
1132        opts: &libbpf_sys::bpf_map_create_opts,
1133    ) -> Result<Self> {
1134        let name = match name {
1135            Some(name) => name.as_ref().to_os_string(),
1136            // The old version kernel don't support specifying map name.
1137            None => OsString::new(),
1138        };
1139        let name_c_str = CString::new(name.as_bytes()).map_err(|_| {
1140            Error::with_invalid_data(format!("invalid name `{name:?}`: has NUL bytes"))
1141        })?;
1142        let name_c_ptr = if name.is_empty() {
1143            ptr::null()
1144        } else {
1145            name_c_str.as_bytes_with_nul().as_ptr()
1146        };
1147
1148        let fd = unsafe {
1149            libbpf_sys::bpf_map_create(
1150                map_type.into(),
1151                name_c_ptr.cast(),
1152                key_size,
1153                value_size,
1154                max_entries,
1155                opts,
1156            )
1157        };
1158        let () = util::parse_ret(fd)?;
1159
1160        Ok(Self {
1161            // SAFETY: A file descriptor coming from the `bpf_map_create`
1162            //         function is always suitable for ownership and can be
1163            //         cleaned up with close.
1164            fd: unsafe { OwnedFd::from_raw_fd(fd) },
1165            name,
1166            ty: map_type,
1167            key_size,
1168            value_size,
1169            max_entries,
1170        })
1171    }
1172
1173    /// Open a previously pinned map from its path.
1174    ///
1175    /// # Panics
1176    /// If the path contains null bytes.
1177    pub fn from_pinned_path<P: AsRef<Path>>(path: P) -> Result<Self> {
1178        Self::from_pinned_path_with_file_flags(path, 0)
1179    }
1180
1181    /// Open a previously pinned map from its path with the provided file flags.
1182    ///
1183    /// For example, pass [`libbpf_sys::BPF_F_RDONLY`] to open a map as
1184    /// read-only from user space.
1185    ///
1186    /// # Panics
1187    /// If the path contains null bytes.
1188    pub fn from_pinned_path_with_file_flags<P: AsRef<Path>>(
1189        path: P,
1190        file_flags: u32,
1191    ) -> Result<Self> {
1192        fn inner(path: &Path, file_flags: u32) -> Result<MapHandle> {
1193            let p = CString::new(path.as_os_str().as_bytes()).expect("path contained null bytes");
1194            let opts = libbpf_sys::bpf_obj_get_opts {
1195                sz: size_of::<libbpf_sys::bpf_obj_get_opts>() as libbpf_sys::size_t,
1196                file_flags,
1197                ..Default::default()
1198            };
1199            let fd = parse_ret_i32(unsafe {
1200                // SAFETY
1201                // p is never null since we allocated ourselves.
1202                libbpf_sys::bpf_obj_get_opts(p.as_ptr(), &opts)
1203            })?;
1204            MapHandle::from_fd(unsafe {
1205                // SAFETY
1206                // A file descriptor coming from the bpf_obj_get function is always suitable for
1207                // ownership and can be cleaned up with close.
1208                OwnedFd::from_raw_fd(fd)
1209            })
1210        }
1211
1212        inner(path.as_ref(), file_flags)
1213    }
1214
1215    /// Open a loaded map from its map id.
1216    pub fn from_map_id(id: u32) -> Result<Self> {
1217        parse_ret_i32(unsafe {
1218            // SAFETY
1219            // This function is always safe to call.
1220            libbpf_sys::bpf_map_get_fd_by_id(id)
1221        })
1222        .map(|fd| unsafe {
1223            // SAFETY
1224            // A file descriptor coming from the bpf_map_get_fd_by_id function is always suitable
1225            // for ownership and can be cleaned up with close.
1226            OwnedFd::from_raw_fd(fd)
1227        })
1228        .and_then(Self::from_fd)
1229    }
1230
1231    fn from_fd(fd: OwnedFd) -> Result<Self> {
1232        let info = MapInfo::new(fd.as_fd())?;
1233        Ok(Self {
1234            fd,
1235            name: info.name()?.into(),
1236            ty: info.map_type(),
1237            key_size: info.info.key_size,
1238            value_size: info.info.value_size,
1239            max_entries: info.info.max_entries,
1240        })
1241    }
1242
1243    /// Freeze the map as read-only from user space.
1244    ///
1245    /// Entries from a frozen map can no longer be updated or deleted with the
1246    /// `bpf()` system call. This operation is not reversible, and the map remains
1247    /// immutable from user space until its destruction. However, read and write
1248    /// permissions for BPF programs to the map remain unchanged.
1249    pub fn freeze(&self) -> Result<()> {
1250        let ret = unsafe { libbpf_sys::bpf_map_freeze(self.fd.as_raw_fd()) };
1251
1252        util::parse_ret(ret)
1253    }
1254
1255    /// [Pin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
1256    /// this map to bpffs.
1257    pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
1258        let path_c = util::path_to_cstring(path)?;
1259        let path_ptr = path_c.as_ptr();
1260
1261        let ret = unsafe { libbpf_sys::bpf_obj_pin(self.fd.as_raw_fd(), path_ptr) };
1262        util::parse_ret(ret)
1263    }
1264
1265    /// [Unpin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
1266    /// this map from bpffs.
1267    pub fn unpin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
1268        remove_file(path).context("failed to remove pin map")
1269    }
1270}
1271
1272impl MapCore for MapHandle {
1273    #[inline]
1274    fn name(&self) -> &OsStr {
1275        &self.name
1276    }
1277
1278    #[inline]
1279    fn map_type(&self) -> MapType {
1280        self.ty
1281    }
1282
1283    #[inline]
1284    fn key_size(&self) -> u32 {
1285        self.key_size
1286    }
1287
1288    #[inline]
1289    fn value_size(&self) -> u32 {
1290        self.value_size
1291    }
1292
1293    #[inline]
1294    fn max_entries(&self) -> u32 {
1295        self.max_entries
1296    }
1297}
1298
1299impl AsFd for MapHandle {
1300    #[inline]
1301    fn as_fd(&self) -> BorrowedFd<'_> {
1302        self.fd.as_fd()
1303    }
1304}
1305
1306impl<T> TryFrom<&MapImpl<'_, T>> for MapHandle
1307where
1308    T: Debug,
1309{
1310    type Error = Error;
1311
1312    fn try_from(other: &MapImpl<'_, T>) -> Result<Self> {
1313        Ok(Self {
1314            fd: other
1315                .as_fd()
1316                .try_clone_to_owned()
1317                .context("failed to duplicate map file descriptor")?,
1318            name: other.name().to_os_string(),
1319            ty: other.map_type(),
1320            key_size: other.key_size(),
1321            value_size: other.value_size(),
1322            max_entries: other.max_entries(),
1323        })
1324    }
1325}
1326
1327impl TryFrom<&Self> for MapHandle {
1328    type Error = Error;
1329
1330    fn try_from(other: &Self) -> Result<Self> {
1331        Ok(Self {
1332            fd: other
1333                .as_fd()
1334                .try_clone_to_owned()
1335                .context("failed to duplicate map file descriptor")?,
1336            name: other.name().to_os_string(),
1337            ty: other.map_type(),
1338            key_size: other.key_size(),
1339            value_size: other.value_size(),
1340            max_entries: other.max_entries(),
1341        })
1342    }
1343}
1344
1345bitflags! {
1346    /// Flags to configure [`Map`] operations.
1347    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
1348    pub struct MapFlags: u64 {
1349        /// See [`libbpf_sys::BPF_ANY`].
1350        const ANY      = libbpf_sys::BPF_ANY as _;
1351        /// See [`libbpf_sys::BPF_NOEXIST`].
1352        const NO_EXIST = libbpf_sys::BPF_NOEXIST as _;
1353        /// See [`libbpf_sys::BPF_EXIST`].
1354        const EXIST    = libbpf_sys::BPF_EXIST as _;
1355        /// See [`libbpf_sys::BPF_F_LOCK`].
1356        const LOCK     = libbpf_sys::BPF_F_LOCK as _;
1357    }
1358}
1359
1360/// Type of a [`Map`]. Maps to `enum bpf_map_type` in kernel uapi.
1361// If you add a new per-cpu map, also update `is_percpu`.
1362#[non_exhaustive]
1363#[repr(u32)]
1364#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1365pub enum MapType {
1366    /// An unspecified map type.
1367    Unspec = libbpf_sys::BPF_MAP_TYPE_UNSPEC,
1368    /// A general purpose Hash map storage type.
1369    ///
1370    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_hash.html) for more details.
1371    Hash = libbpf_sys::BPF_MAP_TYPE_HASH,
1372    /// An Array map storage type.
1373    ///
1374    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_array.html) for more details.
1375    Array = libbpf_sys::BPF_MAP_TYPE_ARRAY,
1376    /// A program array map which holds only the file descriptors to other eBPF programs. Used for
1377    /// tail-calls.
1378    ///
1379    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_PROG_ARRAY/) for more details.
1380    ProgArray = libbpf_sys::BPF_MAP_TYPE_PROG_ARRAY,
1381    /// An array map which holds only the file descriptors to perf events.
1382    ///
1383    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_PERF_EVENT_ARRAY/) for more details.
1384    PerfEventArray = libbpf_sys::BPF_MAP_TYPE_PERF_EVENT_ARRAY,
1385    /// A Hash map with per CPU storage.
1386    ///
1387    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_hash.html#per-cpu-hashes) for more details.
1388    PercpuHash = libbpf_sys::BPF_MAP_TYPE_PERCPU_HASH,
1389    /// An Array map with per CPU storage.
1390    ///
1391    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_array.html) for more details.
1392    PercpuArray = libbpf_sys::BPF_MAP_TYPE_PERCPU_ARRAY,
1393    #[allow(missing_docs)]
1394    StackTrace = libbpf_sys::BPF_MAP_TYPE_STACK_TRACE,
1395    #[allow(missing_docs)]
1396    CgroupArray = libbpf_sys::BPF_MAP_TYPE_CGROUP_ARRAY,
1397    /// A Hash map with least recently used (LRU) eviction policy.
1398    ///
1399    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_hash.html#bpf-map-type-lru-hash-and-variants) for more details.
1400    LruHash = libbpf_sys::BPF_MAP_TYPE_LRU_HASH,
1401    /// A Hash map with least recently used (LRU) eviction policy with per CPU storage.
1402    ///
1403    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_hash.html#per-cpu-hashes) for more details.
1404    LruPercpuHash = libbpf_sys::BPF_MAP_TYPE_LRU_PERCPU_HASH,
1405    /// A Longest Prefix Match (LPM) algorithm based map.
1406    ///
1407    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_lpm_trie.html) for more details.
1408    LpmTrie = libbpf_sys::BPF_MAP_TYPE_LPM_TRIE,
1409    /// A map in map storage.
1410    /// One level of nesting is supported, where an outer map contains instances of a single type
1411    /// of inner map.
1412    ///
1413    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_of_maps.html) for more details.
1414    ArrayOfMaps = libbpf_sys::BPF_MAP_TYPE_ARRAY_OF_MAPS,
1415    /// A map in map storage.
1416    /// One level of nesting is supported, where an outer map contains instances of a single type
1417    /// of inner map.
1418    ///
1419    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_of_maps.html) for more details.
1420    HashOfMaps = libbpf_sys::BPF_MAP_TYPE_HASH_OF_MAPS,
1421    /// An array map that uses the key as the index to lookup a reference to a net device.
1422    /// Primarily used for XDP BPF Helper.
1423    ///
1424    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_devmap.html) for more details.
1425    Devmap = libbpf_sys::BPF_MAP_TYPE_DEVMAP,
1426    /// An array map holds references to a socket descriptor.
1427    ///
1428    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_sockmap.html) for more details.
1429    Sockmap = libbpf_sys::BPF_MAP_TYPE_SOCKMAP,
1430    /// A map that redirects raw XDP frames to another CPU.
1431    ///
1432    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_cpumap.html) for more details.
1433    Cpumap = libbpf_sys::BPF_MAP_TYPE_CPUMAP,
1434    /// A map that redirects raw XDP frames to `AF_XDP` sockets (XSKs), a new type of address
1435    /// family in the kernel that allows redirection of frames from a driver to user space
1436    /// without having to traverse the full network stack.
1437    ///
1438    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_xskmap.html) for more details.
1439    Xskmap = libbpf_sys::BPF_MAP_TYPE_XSKMAP,
1440    /// A Hash map that holds references to sockets via their socket descriptor.
1441    ///
1442    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_sockmap.html) for more details.
1443    Sockhash = libbpf_sys::BPF_MAP_TYPE_SOCKHASH,
1444    /// Deprecated. Use `CGrpStorage` instead.
1445    ///
1446    /// A Local storage for cgroups.
1447    /// Only available with `CONFIG_CGROUP_BPF` and to programs that attach to cgroups.
1448    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_cgroup_storage.html) for more details.
1449    CgroupStorage = libbpf_sys::BPF_MAP_TYPE_CGROUP_STORAGE,
1450    /// A Local storage for cgroups. Only available with `CONFIG_CGROUPS`.
1451    ///
1452    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_cgrp_storage.html) for more details.
1453    /// See also [Difference between cgrp_storage and cgroup_storage](https://docs.kernel.org/bpf/map_cgrp_storage.html#difference-between-bpf-map-type-cgrp-storage-and-bpf-map-type-cgroup-storage)
1454    CGrpStorage = libbpf_sys::BPF_MAP_TYPE_CGRP_STORAGE,
1455    /// A map that holds references to sockets with `SO_REUSEPORT` option set.
1456    ///
1457    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_REUSEPORT_SOCKARRAY/) for more details.
1458    ReuseportSockarray = libbpf_sys::BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
1459    /// A per-CPU variant of [`BPF_MAP_TYPE_CGROUP_STORAGE`][`MapType::CgroupStorage`].
1460    ///
1461    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) for more details.
1462    PercpuCgroupStorage = libbpf_sys::BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
1463    /// A FIFO storage.
1464    ///
1465    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_queue_stack.html) for more details.
1466    Queue = libbpf_sys::BPF_MAP_TYPE_QUEUE,
1467    /// A LIFO storage.
1468    ///
1469    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_queue_stack.html) for more details.
1470    Stack = libbpf_sys::BPF_MAP_TYPE_STACK,
1471    /// A socket-local storage.
1472    ///
1473    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_sk_storage.html) for more details.
1474    SkStorage = libbpf_sys::BPF_MAP_TYPE_SK_STORAGE,
1475    /// A Hash map that uses the key as the index to lookup a reference to a net device.
1476    /// Primarily used for XDP BPF Helper.
1477    ///
1478    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_devmap.html) for more details.
1479    DevmapHash = libbpf_sys::BPF_MAP_TYPE_DEVMAP_HASH,
1480    /// A specialized map that act as implementations of "struct ops" structures defined in the
1481    /// kernel.
1482    ///
1483    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_STRUCT_OPS/) for more details.
1484    StructOps = libbpf_sys::BPF_MAP_TYPE_STRUCT_OPS,
1485    /// A ring buffer map to efficiently send large amount of data.
1486    ///
1487    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_RINGBUF/) for more details.
1488    RingBuf = libbpf_sys::BPF_MAP_TYPE_RINGBUF,
1489    /// A storage map that holds data keyed on inodes.
1490    ///
1491    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_INODE_STORAGE/) for more details.
1492    InodeStorage = libbpf_sys::BPF_MAP_TYPE_INODE_STORAGE,
1493    /// A storage map that holds data keyed on tasks.
1494    ///
1495    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_TASK_STORAGE/) for more details.
1496    TaskStorage = libbpf_sys::BPF_MAP_TYPE_TASK_STORAGE,
1497    /// Bloom filters are a space-efficient probabilistic data structure used to quickly test
1498    /// whether an element exists in a set. In a bloom filter, false positives are possible
1499    /// whereas false negatives are not.
1500    ///
1501    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_bloom_filter.html) for more details.
1502    BloomFilter = libbpf_sys::BPF_MAP_TYPE_BLOOM_FILTER,
1503    #[allow(missing_docs)]
1504    UserRingBuf = libbpf_sys::BPF_MAP_TYPE_USER_RINGBUF,
1505    /// We choose to specify our own "unknown" type here b/c it's really up to the kernel
1506    /// to decide if it wants to reject the map. If it accepts it, it just means whoever
1507    /// using this library is a bit out of date.
1508    Unknown = u32::MAX,
1509}
1510
1511impl MapType {
1512    /// Returns if the map is of one of the per-cpu types.
1513    pub fn is_percpu(&self) -> bool {
1514        matches!(
1515            self,
1516            Self::PercpuArray | Self::PercpuHash | Self::LruPercpuHash | Self::PercpuCgroupStorage
1517        )
1518    }
1519
1520    /// Returns if the map is of one of the hashmap types.
1521    pub fn is_hash_map(&self) -> bool {
1522        matches!(
1523            self,
1524            Self::Hash | Self::PercpuHash | Self::LruHash | Self::LruPercpuHash
1525        )
1526    }
1527
1528    /// Returns if the map is keyless map type as per documentation of libbpf
1529    /// Keyless map types are: Queues, Stacks and Bloom Filters
1530    fn is_keyless(&self) -> bool {
1531        matches!(self, Self::Queue | Self::Stack | Self::BloomFilter)
1532    }
1533
1534    /// Returns if the map is of bloom filter type
1535    pub fn is_bloom_filter(&self) -> bool {
1536        Self::BloomFilter.eq(self)
1537    }
1538
1539    /// Detects if host kernel supports this BPF map type.
1540    ///
1541    /// Make sure the process has required set of CAP_* permissions (or runs as
1542    /// root) when performing feature checking.
1543    pub fn is_supported(&self) -> Result<bool> {
1544        let ret = unsafe { libbpf_sys::libbpf_probe_bpf_map_type(*self as u32, ptr::null()) };
1545        match ret {
1546            0 => Ok(false),
1547            1 => Ok(true),
1548            _ => Err(Error::from_raw_os_error(-ret)),
1549        }
1550    }
1551}
1552
1553impl From<u32> for MapType {
1554    fn from(value: u32) -> Self {
1555        use MapType::*;
1556
1557        match value {
1558            x if x == Unspec as u32 => Unspec,
1559            x if x == Hash as u32 => Hash,
1560            x if x == Array as u32 => Array,
1561            x if x == ProgArray as u32 => ProgArray,
1562            x if x == PerfEventArray as u32 => PerfEventArray,
1563            x if x == PercpuHash as u32 => PercpuHash,
1564            x if x == PercpuArray as u32 => PercpuArray,
1565            x if x == StackTrace as u32 => StackTrace,
1566            x if x == CgroupArray as u32 => CgroupArray,
1567            x if x == LruHash as u32 => LruHash,
1568            x if x == LruPercpuHash as u32 => LruPercpuHash,
1569            x if x == LpmTrie as u32 => LpmTrie,
1570            x if x == ArrayOfMaps as u32 => ArrayOfMaps,
1571            x if x == HashOfMaps as u32 => HashOfMaps,
1572            x if x == Devmap as u32 => Devmap,
1573            x if x == Sockmap as u32 => Sockmap,
1574            x if x == Cpumap as u32 => Cpumap,
1575            x if x == Xskmap as u32 => Xskmap,
1576            x if x == Sockhash as u32 => Sockhash,
1577            x if x == CgroupStorage as u32 => CgroupStorage,
1578            x if x == ReuseportSockarray as u32 => ReuseportSockarray,
1579            x if x == PercpuCgroupStorage as u32 => PercpuCgroupStorage,
1580            x if x == Queue as u32 => Queue,
1581            x if x == Stack as u32 => Stack,
1582            x if x == SkStorage as u32 => SkStorage,
1583            x if x == DevmapHash as u32 => DevmapHash,
1584            x if x == StructOps as u32 => StructOps,
1585            x if x == RingBuf as u32 => RingBuf,
1586            x if x == InodeStorage as u32 => InodeStorage,
1587            x if x == TaskStorage as u32 => TaskStorage,
1588            x if x == BloomFilter as u32 => BloomFilter,
1589            x if x == UserRingBuf as u32 => UserRingBuf,
1590            _ => Unknown,
1591        }
1592    }
1593}
1594
1595impl From<MapType> for u32 {
1596    fn from(value: MapType) -> Self {
1597        value as Self
1598    }
1599}
1600
1601/// An iterator over the keys of a BPF map.
1602#[derive(Debug)]
1603pub struct MapKeyIter<'map> {
1604    map_fd: BorrowedFd<'map>,
1605    prev: Option<Vec<u8>>,
1606    next: Vec<u8>,
1607}
1608
1609impl<'map> MapKeyIter<'map> {
1610    fn new(map_fd: BorrowedFd<'map>, key_size: u32) -> Self {
1611        Self {
1612            map_fd,
1613            prev: None,
1614            next: vec![0; key_size as usize],
1615        }
1616    }
1617}
1618
1619impl Iterator for MapKeyIter<'_> {
1620    type Item = Vec<u8>;
1621
1622    fn next(&mut self) -> Option<Self::Item> {
1623        let prev = self.prev.as_ref().map_or(ptr::null(), Vec::as_ptr);
1624
1625        let ret = unsafe {
1626            libbpf_sys::bpf_map_get_next_key(
1627                self.map_fd.as_raw_fd(),
1628                prev.cast(),
1629                self.next.as_mut_ptr().cast(),
1630            )
1631        };
1632        if ret != 0 {
1633            None
1634        } else {
1635            self.prev = Some(self.next.clone());
1636            Some(self.next.clone())
1637        }
1638    }
1639}
1640
1641/// An iterator over batches of key value pairs of a BPF map.
1642#[derive(Debug)]
1643pub struct BatchedMapIter<'map> {
1644    map_fd: BorrowedFd<'map>,
1645    delete: bool,
1646    count: usize,
1647    key_size: usize,
1648    value_size: usize,
1649    keys: Vec<u8>,
1650    values: Vec<u8>,
1651    prev: Option<Vec<u8>>,
1652    next: Vec<u8>,
1653    batch_opts: libbpf_sys::bpf_map_batch_opts,
1654    index: Option<usize>,
1655}
1656
1657impl<'map> BatchedMapIter<'map> {
1658    fn new(
1659        map_fd: BorrowedFd<'map>,
1660        count: u32,
1661        key_size: u32,
1662        value_size: u32,
1663        batch_opts: libbpf_sys::bpf_map_batch_opts,
1664        delete: bool,
1665    ) -> Self {
1666        Self {
1667            map_fd,
1668            delete,
1669            count: count as usize,
1670            key_size: key_size as usize,
1671            value_size: value_size as usize,
1672            keys: vec![0; (count * key_size) as usize],
1673            values: vec![0; (count * value_size) as usize],
1674            prev: None,
1675            next: vec![0; key_size as usize],
1676            batch_opts,
1677            index: None,
1678        }
1679    }
1680
1681    fn lookup_next_batch(&mut self) {
1682        let prev = self.prev.as_mut().map_or(ptr::null_mut(), Vec::as_mut_ptr);
1683        let mut count = self.count as u32;
1684
1685        let ret = unsafe {
1686            let lookup_fn = if self.delete {
1687                libbpf_sys::bpf_map_lookup_and_delete_batch
1688            } else {
1689                libbpf_sys::bpf_map_lookup_batch
1690            };
1691            lookup_fn(
1692                self.map_fd.as_raw_fd(),
1693                prev.cast(),
1694                self.next.as_mut_ptr().cast(),
1695                self.keys.as_mut_ptr().cast(),
1696                self.values.as_mut_ptr().cast(),
1697                &mut count,
1698                &self.batch_opts,
1699            )
1700        };
1701
1702        if let Err(e) = util::parse_ret(ret) {
1703            match e.kind() {
1704                // in this case we can trust the returned count value
1705                error::ErrorKind::NotFound => {}
1706                // retry with same input arguments
1707                error::ErrorKind::Interrupted => {
1708                    return self.lookup_next_batch();
1709                }
1710                _ => {
1711                    self.index = None;
1712                    return;
1713                }
1714            }
1715        }
1716
1717        self.prev = Some(self.next.clone());
1718        self.index = Some(0);
1719
1720        unsafe {
1721            self.keys.set_len(self.key_size * count as usize);
1722            self.values.set_len(self.value_size * count as usize);
1723        }
1724    }
1725}
1726
1727impl Iterator for BatchedMapIter<'_> {
1728    type Item = (Vec<u8>, Vec<u8>);
1729
1730    fn next(&mut self) -> Option<Self::Item> {
1731        let load_next_batch = match self.index {
1732            Some(index) => {
1733                let batch_finished = index * self.key_size >= self.keys.len();
1734                let last_batch = self.keys.len() < self.key_size * self.count;
1735                batch_finished && !last_batch
1736            }
1737            None => true,
1738        };
1739
1740        if load_next_batch {
1741            self.lookup_next_batch();
1742        }
1743
1744        let index = self.index?;
1745        let key = self.keys.chunks_exact(self.key_size).nth(index)?.to_vec();
1746        let val = self
1747            .values
1748            .chunks_exact(self.value_size)
1749            .nth(index)?
1750            .to_vec();
1751
1752        self.index = Some(index + 1);
1753        Some((key, val))
1754    }
1755}
1756
1757/// A convenience wrapper for [`bpf_map_info`][libbpf_sys::bpf_map_info]. It
1758/// provides the ability to retrieve the details of a certain map.
1759#[derive(Debug)]
1760pub struct MapInfo {
1761    /// The inner [`bpf_map_info`][libbpf_sys::bpf_map_info] object.
1762    pub info: bpf_map_info,
1763}
1764
1765impl MapInfo {
1766    /// Create a `MapInfo` object from a fd.
1767    pub fn new(fd: BorrowedFd<'_>) -> Result<Self> {
1768        let mut map_info = bpf_map_info::default();
1769        let mut size = mem::size_of_val(&map_info) as u32;
1770        // SAFETY: All pointers are derived from references and hence valid.
1771        let () = util::parse_ret(unsafe {
1772            bpf_obj_get_info_by_fd(
1773                fd.as_raw_fd(),
1774                (&mut map_info as *mut bpf_map_info).cast::<c_void>(),
1775                &mut size as *mut u32,
1776            )
1777        })?;
1778        Ok(Self { info: map_info })
1779    }
1780
1781    /// Get the map type
1782    #[inline]
1783    pub fn map_type(&self) -> MapType {
1784        MapType::from(self.info.type_)
1785    }
1786
1787    /// Get the name of this map.
1788    ///
1789    /// Returns error if the underlying data in the structure is not a valid
1790    /// utf-8 string.
1791    pub fn name<'a>(&self) -> Result<&'a str> {
1792        // SAFETY: convert &[i8] to &[u8], and then cast that to &str. i8 and u8 has the same size.
1793        let char_slice =
1794            unsafe { from_raw_parts(self.info.name[..].as_ptr().cast(), self.info.name.len()) };
1795
1796        util::c_char_slice_to_cstr(char_slice)
1797            .ok_or_else(|| Error::with_invalid_data("no nul byte found"))?
1798            .to_str()
1799            .map_err(Error::with_invalid_data)
1800    }
1801
1802    /// Get the map flags.
1803    #[inline]
1804    pub fn flags(&self) -> MapFlags {
1805        MapFlags::from_bits_truncate(self.info.map_flags as u64)
1806    }
1807}
1808
1809/// Information about a BPF map obtained from `/proc/self/fdinfo`.
1810///
1811/// This provides information not available through [`MapInfo`], such as
1812/// [`memlock`][MapFdInfo::memlock] (memory usage) and [`frozen`][MapFdInfo::frozen] status.
1813///
1814/// The fields correspond to those printed by
1815/// [`bpf_map_show_fdinfo`](https://github.com/torvalds/linux/blob/37a93dd5c49b/kernel/bpf/syscall.c#L1007)
1816/// in the kernel source. See also bpftool's
1817/// [`get_fdinfo`](https://github.com/torvalds/linux/blob/37a93dd5c49/tools/bpf/bpftool/common.c#L485)
1818/// for the matching userspace parsing logic.
1819#[derive(Debug, Clone)]
1820pub struct MapFdInfo {
1821    /// The map type.
1822    pub map_type: MapType,
1823    /// The size of the map's keys in bytes.
1824    pub key_size: u32,
1825    /// The size of the map's values in bytes.
1826    pub value_size: u32,
1827    /// The maximum number of entries in the map.
1828    pub max_entries: u32,
1829    // The following fields were added in later kernel versions and may not be
1830    // present in older kernels.
1831    /// The map flags.
1832    pub map_flags: Option<u32>,
1833    /// Extra map-specific data.
1834    pub map_extra: Option<u64>,
1835    /// The amount of memory locked by the map in bytes.
1836    pub memlock: Option<u64>,
1837    /// The map's ID.
1838    pub map_id: Option<u32>,
1839    /// Whether the map is frozen.
1840    pub frozen: Option<bool>,
1841    /// The type of the owner program (only for `prog_array` maps).
1842    pub owner_prog_type: Option<ProgramType>,
1843    /// Whether the owner program is JIT-compiled (only for `prog_array` maps).
1844    pub owner_jited: Option<bool>,
1845}
1846
1847impl MapFdInfo {
1848    /// Create a `MapFdInfo` by reading `/proc/self/fdinfo` for the given fd.
1849    pub fn from_fd(fd: BorrowedFd<'_>) -> Result<Self> {
1850        let path = format!("/proc/self/fdinfo/{}", fd.as_raw_fd());
1851        let file = File::open(&path).with_context(|| format!("failed to open `{path}`"))?;
1852        let reader = BufReader::new(file);
1853
1854        let parse = |key: &str, val: &str| -> Result<u32> {
1855            val.parse()
1856                .map_err(|e| Error::with_invalid_data(format!("`{key}`: {e}")))
1857        };
1858
1859        let mut map_type = None;
1860        let mut key_size = None;
1861        let mut value_size = None;
1862        let mut max_entries = None;
1863        let mut map_flags = None;
1864        let mut map_extra = None;
1865        let mut memlock = None;
1866        let mut map_id = None;
1867        let mut frozen = None;
1868        let mut owner_prog_type = None;
1869        let mut owner_jited = None;
1870
1871        for result in reader.lines() {
1872            let line = result?;
1873            let Some((key, value)) = line.split_once('\t') else {
1874                continue;
1875            };
1876            // Keys have a trailing colon, e.g. "map_type:"
1877            let key = key.trim_end_matches(':');
1878            let value = value.trim();
1879
1880            match key {
1881                "map_type" => map_type = Some(parse(key, value)?),
1882                "key_size" => key_size = Some(parse(key, value)?),
1883                "value_size" => value_size = Some(parse(key, value)?),
1884                "max_entries" => max_entries = Some(parse(key, value)?),
1885                "map_flags" => {
1886                    map_flags =
1887                        Some(parse_hex(value).with_context(|| format!("bad `{key}`"))? as u32)
1888                }
1889                "map_extra" => {
1890                    map_extra = Some(parse_hex(value).with_context(|| format!("bad `{key}`"))?)
1891                }
1892                "memlock" => memlock = Some(parse(key, value)? as u64),
1893                "map_id" => map_id = Some(parse(key, value)?),
1894                "frozen" => frozen = Some(parse(key, value)? != 0),
1895                "owner_prog_type" => owner_prog_type = Some(parse(key, value)?),
1896                "owner_jited" => owner_jited = Some(parse(key, value)? != 0),
1897                _ => {}
1898            }
1899        }
1900
1901        let missing = |f| Error::with_invalid_data(format!("missing `{f}` in fdinfo"));
1902
1903        Ok(Self {
1904            map_type: MapType::from(map_type.ok_or_else(|| missing("map_type"))?),
1905            key_size: key_size.ok_or_else(|| missing("key_size"))?,
1906            value_size: value_size.ok_or_else(|| missing("value_size"))?,
1907            max_entries: max_entries.ok_or_else(|| missing("max_entries"))?,
1908            map_flags,
1909            map_extra,
1910            memlock,
1911            map_id,
1912            frozen,
1913            owner_prog_type: owner_prog_type.map(ProgramType::from),
1914            owner_jited,
1915        })
1916    }
1917}
1918
1919/// Parse a value that may be in hex (0x...) or decimal format.
1920fn parse_hex(s: &str) -> Result<u64> {
1921    if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
1922        u64::from_str_radix(hex, 16)
1923    } else {
1924        s.parse()
1925    }
1926    .map_err(Error::with_invalid_data)
1927}
1928
1929#[cfg(test)]
1930mod tests {
1931    use super::*;
1932
1933    use std::mem::discriminant;
1934
1935    #[test]
1936    fn map_type() {
1937        use MapType::*;
1938
1939        for t in [
1940            Unspec,
1941            Hash,
1942            Array,
1943            ProgArray,
1944            PerfEventArray,
1945            PercpuHash,
1946            PercpuArray,
1947            StackTrace,
1948            CgroupArray,
1949            LruHash,
1950            LruPercpuHash,
1951            LpmTrie,
1952            ArrayOfMaps,
1953            HashOfMaps,
1954            Devmap,
1955            Sockmap,
1956            Cpumap,
1957            Xskmap,
1958            Sockhash,
1959            CgroupStorage,
1960            ReuseportSockarray,
1961            PercpuCgroupStorage,
1962            Queue,
1963            Stack,
1964            SkStorage,
1965            DevmapHash,
1966            StructOps,
1967            RingBuf,
1968            InodeStorage,
1969            TaskStorage,
1970            BloomFilter,
1971            UserRingBuf,
1972            Unknown,
1973        ] {
1974            // check if discriminants match after a roundtrip conversion
1975            assert_eq!(discriminant(&t), discriminant(&MapType::from(t as u32)));
1976        }
1977    }
1978
1979    #[test]
1980    fn parse_hex_decimal() {
1981        assert_eq!(parse_hex("0").unwrap(), 0);
1982        assert_eq!(parse_hex("42").unwrap(), 42);
1983        assert_eq!(parse_hex("18446744073709551615").unwrap(), u64::MAX);
1984    }
1985
1986    #[test]
1987    fn parse_hex_hex_prefix() {
1988        assert_eq!(parse_hex("0x0").unwrap(), 0);
1989        assert_eq!(parse_hex("0xff").unwrap(), 255);
1990        assert_eq!(parse_hex("0X1A").unwrap(), 26);
1991        assert_eq!(parse_hex("0xdeadbeef").unwrap(), 0xdeadbeef);
1992    }
1993
1994    #[test]
1995    fn parse_hex_invalid() {
1996        assert!(parse_hex("").is_err());
1997        assert!(parse_hex("xyz").is_err());
1998        assert!(parse_hex("0xGG").is_err());
1999    }
2000}