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
45pub type OpenMap<'obj> = OpenMapImpl<'obj>;
47pub type OpenMapMut<'obj> = OpenMapImpl<'obj, Mut>;
49
50#[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 pub fn new(object: &'obj libbpf_sys::bpf_map) -> Self {
66 Self {
69 ptr: unsafe { NonNull::new_unchecked(object as *const _ as *mut _) },
70 _phantom: PhantomData,
71 }
72 }
73
74 pub fn name(&self) -> &'obj OsStr {
76 let name_ptr = unsafe { libbpf_sys::bpf_map__name(self.ptr.as_ptr()) };
78 let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
81 OsStr::from_bytes(name_c_str.to_bytes())
82 }
83
84 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 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 pub fn max_entries(&self) -> u32 {
111 unsafe { libbpf_sys::bpf_map__max_entries(self.ptr.as_ptr()) }
112 }
113
114 pub fn autocreate(&self) -> bool {
116 unsafe { libbpf_sys::bpf_map__autocreate(self.ptr.as_ptr()) }
117 }
118
119 pub fn map_flags(&self) -> u32 {
121 unsafe { libbpf_sys::bpf_map__map_flags(self.ptr.as_ptr()) }
122 }
123
124 pub fn numa_node(&self) -> u32 {
126 unsafe { libbpf_sys::bpf_map__numa_node(self.ptr.as_ptr()) }
127 }
128
129 pub fn key_size(&self) -> u32 {
131 unsafe { libbpf_sys::bpf_map__key_size(self.ptr.as_ptr()) }
132 }
133
134 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
308fn 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
318fn 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
328fn map_key<M>(map: &M, key: &[u8]) -> *const c_void
332where
333 M: MapCore + ?Sized,
334{
335 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
343enum LookupOp {
350 Lookup(MapFlags),
351 LookupAndDelete,
352}
353
354fn 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 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 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
412fn lookup_raw_vec<M>(map: &M, key: &[u8], op: LookupOp, out_size: usize) -> Result<Option<Vec<u8>>>
414where
415 M: MapCore + ?Sized,
416{
417 let mut out = Vec::with_capacity(out_size);
419
420 match lookup_raw(map, key, out.spare_capacity_mut(), op)? {
421 true => {
422 unsafe {
424 out.set_len(out_size);
425 }
426 Ok(Some(out))
427 }
428 false => Ok(None),
429 }
430}
431
432fn 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
458fn 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 ..Default::default()
476 };
477
478 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
489fn 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
519pub trait MapCore: Debug + AsFd + private::Sealed {
521 fn name(&self) -> &OsStr;
523
524 fn map_type(&self) -> MapType;
526
527 fn key_size(&self) -> u32;
529
530 fn value_size(&self) -> u32;
532
533 fn max_entries(&self) -> u32;
535
536 #[inline]
538 fn info(&self) -> Result<MapInfo> {
539 MapInfo::new(self.as_fd())
540 }
541
542 #[inline]
547 fn query_fdinfo(&self) -> Result<MapFdInfo> {
548 MapFdInfo::from_fd(self.as_fd())
549 }
550
551 fn keys(&self) -> MapKeyIter<'_> {
557 MapKeyIter::new(self.as_fd(), self.key_size())
558 }
559
560 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 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 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 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 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 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 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 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 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 ..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 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 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 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 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 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 ..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 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
909pub type Map<'obj> = MapImpl<'obj>;
911pub type MapMut<'obj> = MapImpl<'obj, Mut>;
913
914#[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 pub fn new(map: &'obj libbpf_sys::bpf_map) -> Self {
927 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 #[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 pub fn is_pinned(&self) -> bool {
960 unsafe { libbpf_sys::bpf_map__is_pinned(self.ptr.as_ptr()) }
961 }
962
963 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 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 pub fn autocreate(&self) -> bool {
977 unsafe { libbpf_sys::bpf_map__autocreate(self.ptr.as_ptr()) }
978 }
979}
980
981impl<'obj> MapMut<'obj> {
982 pub fn new_mut(map: &'obj mut libbpf_sys::bpf_map) -> Self {
984 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 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 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 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 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 let fd = map_fd(self.ptr).unwrap();
1048 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 let name_ptr = unsafe { libbpf_sys::bpf_map__name(self.ptr.as_ptr()) };
1062 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 #[inline]
1095 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
1096 self.ptr
1097 }
1098}
1099
1100#[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 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 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 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 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 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 libbpf_sys::bpf_obj_get_opts(p.as_ptr(), &opts)
1203 })?;
1204 MapHandle::from_fd(unsafe {
1205 OwnedFd::from_raw_fd(fd)
1209 })
1210 }
1211
1212 inner(path.as_ref(), file_flags)
1213 }
1214
1215 pub fn from_map_id(id: u32) -> Result<Self> {
1217 parse_ret_i32(unsafe {
1218 libbpf_sys::bpf_map_get_fd_by_id(id)
1221 })
1222 .map(|fd| unsafe {
1223 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 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 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 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 #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
1348 pub struct MapFlags: u64 {
1349 const ANY = libbpf_sys::BPF_ANY as _;
1351 const NO_EXIST = libbpf_sys::BPF_NOEXIST as _;
1353 const EXIST = libbpf_sys::BPF_EXIST as _;
1355 const LOCK = libbpf_sys::BPF_F_LOCK as _;
1357 }
1358}
1359
1360#[non_exhaustive]
1363#[repr(u32)]
1364#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1365pub enum MapType {
1366 Unspec = libbpf_sys::BPF_MAP_TYPE_UNSPEC,
1368 Hash = libbpf_sys::BPF_MAP_TYPE_HASH,
1372 Array = libbpf_sys::BPF_MAP_TYPE_ARRAY,
1376 ProgArray = libbpf_sys::BPF_MAP_TYPE_PROG_ARRAY,
1381 PerfEventArray = libbpf_sys::BPF_MAP_TYPE_PERF_EVENT_ARRAY,
1385 PercpuHash = libbpf_sys::BPF_MAP_TYPE_PERCPU_HASH,
1389 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 LruHash = libbpf_sys::BPF_MAP_TYPE_LRU_HASH,
1401 LruPercpuHash = libbpf_sys::BPF_MAP_TYPE_LRU_PERCPU_HASH,
1405 LpmTrie = libbpf_sys::BPF_MAP_TYPE_LPM_TRIE,
1409 ArrayOfMaps = libbpf_sys::BPF_MAP_TYPE_ARRAY_OF_MAPS,
1415 HashOfMaps = libbpf_sys::BPF_MAP_TYPE_HASH_OF_MAPS,
1421 Devmap = libbpf_sys::BPF_MAP_TYPE_DEVMAP,
1426 Sockmap = libbpf_sys::BPF_MAP_TYPE_SOCKMAP,
1430 Cpumap = libbpf_sys::BPF_MAP_TYPE_CPUMAP,
1434 Xskmap = libbpf_sys::BPF_MAP_TYPE_XSKMAP,
1440 Sockhash = libbpf_sys::BPF_MAP_TYPE_SOCKHASH,
1444 CgroupStorage = libbpf_sys::BPF_MAP_TYPE_CGROUP_STORAGE,
1450 CGrpStorage = libbpf_sys::BPF_MAP_TYPE_CGRP_STORAGE,
1455 ReuseportSockarray = libbpf_sys::BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
1459 PercpuCgroupStorage = libbpf_sys::BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
1463 Queue = libbpf_sys::BPF_MAP_TYPE_QUEUE,
1467 Stack = libbpf_sys::BPF_MAP_TYPE_STACK,
1471 SkStorage = libbpf_sys::BPF_MAP_TYPE_SK_STORAGE,
1475 DevmapHash = libbpf_sys::BPF_MAP_TYPE_DEVMAP_HASH,
1480 StructOps = libbpf_sys::BPF_MAP_TYPE_STRUCT_OPS,
1485 RingBuf = libbpf_sys::BPF_MAP_TYPE_RINGBUF,
1489 InodeStorage = libbpf_sys::BPF_MAP_TYPE_INODE_STORAGE,
1493 TaskStorage = libbpf_sys::BPF_MAP_TYPE_TASK_STORAGE,
1497 BloomFilter = libbpf_sys::BPF_MAP_TYPE_BLOOM_FILTER,
1503 #[allow(missing_docs)]
1504 UserRingBuf = libbpf_sys::BPF_MAP_TYPE_USER_RINGBUF,
1505 Unknown = u32::MAX,
1509}
1510
1511impl MapType {
1512 pub fn is_percpu(&self) -> bool {
1514 matches!(
1515 self,
1516 Self::PercpuArray | Self::PercpuHash | Self::LruPercpuHash | Self::PercpuCgroupStorage
1517 )
1518 }
1519
1520 pub fn is_hash_map(&self) -> bool {
1522 matches!(
1523 self,
1524 Self::Hash | Self::PercpuHash | Self::LruHash | Self::LruPercpuHash
1525 )
1526 }
1527
1528 fn is_keyless(&self) -> bool {
1531 matches!(self, Self::Queue | Self::Stack | Self::BloomFilter)
1532 }
1533
1534 pub fn is_bloom_filter(&self) -> bool {
1536 Self::BloomFilter.eq(self)
1537 }
1538
1539 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#[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#[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 error::ErrorKind::NotFound => {}
1706 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#[derive(Debug)]
1760pub struct MapInfo {
1761 pub info: bpf_map_info,
1763}
1764
1765impl MapInfo {
1766 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 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 #[inline]
1783 pub fn map_type(&self) -> MapType {
1784 MapType::from(self.info.type_)
1785 }
1786
1787 pub fn name<'a>(&self) -> Result<&'a str> {
1792 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 #[inline]
1804 pub fn flags(&self) -> MapFlags {
1805 MapFlags::from_bits_truncate(self.info.map_flags as u64)
1806 }
1807}
1808
1809#[derive(Debug, Clone)]
1820pub struct MapFdInfo {
1821 pub map_type: MapType,
1823 pub key_size: u32,
1825 pub value_size: u32,
1827 pub max_entries: u32,
1829 pub map_flags: Option<u32>,
1833 pub map_extra: Option<u64>,
1835 pub memlock: Option<u64>,
1837 pub map_id: Option<u32>,
1839 pub frozen: Option<bool>,
1841 pub owner_prog_type: Option<ProgramType>,
1843 pub owner_jited: Option<bool>,
1845}
1846
1847impl MapFdInfo {
1848 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 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
1919fn 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 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}