1use std::{
3 ffi::CString,
4 io,
5 os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, RawFd},
6 path::{Path, PathBuf},
7};
8
9use aya_obj::{
10 InvalidTypeBinding,
11 generated::{
12 BPF_F_AFTER, BPF_F_ALLOW_MULTI, BPF_F_ALLOW_OVERRIDE, BPF_F_BEFORE, BPF_F_ID, BPF_F_LINK,
13 BPF_F_REPLACE, bpf_attach_type, bpf_link_info, bpf_link_type,
14 },
15};
16use hashbrown::hash_set::{Entry, HashSet};
17use thiserror::Error;
18
19use crate::{
20 pin::PinError,
21 programs::{MultiProgLink, MultiProgram, ProgramError, ProgramFd, ProgramId},
22 sys::{
23 SyscallError, bpf_get_object, bpf_link_get_info_by_fd, bpf_pin_object, bpf_prog_attach,
24 bpf_prog_detach,
25 },
26};
27
28pub trait Link: std::fmt::Debug + Eq + std::hash::Hash + 'static {
30 type Id: std::fmt::Debug + Eq + std::hash::Hash + hashbrown::Equivalent<Self>;
32
33 fn id(&self) -> Self::Id;
35
36 fn detach(self) -> Result<(), ProgramError>;
38}
39
40#[derive(Clone, Copy, Debug, Default)]
42pub enum CgroupAttachMode {
43 #[default]
45 Single,
46
47 AllowOverride,
49
50 AllowMultiple,
52}
53
54impl From<CgroupAttachMode> for u32 {
55 fn from(mode: CgroupAttachMode) -> Self {
56 match mode {
57 CgroupAttachMode::Single => 0,
58 CgroupAttachMode::AllowOverride => BPF_F_ALLOW_OVERRIDE,
59 CgroupAttachMode::AllowMultiple => BPF_F_ALLOW_MULTI,
60 }
61 }
62}
63
64#[derive(Debug)]
65pub(crate) struct Links<T: Link> {
66 links: HashSet<T>,
67}
68
69impl<T> Links<T>
70where
71 T: Eq + std::hash::Hash + Link,
72 T::Id: hashbrown::Equivalent<T> + Eq + std::hash::Hash,
73{
74 pub(crate) fn new() -> Self {
75 Self {
76 links: Default::default(),
77 }
78 }
79
80 pub(crate) fn insert(&mut self, link: T) -> Result<T::Id, ProgramError> {
81 match self.links.entry(link) {
82 Entry::Occupied(_entry) => Err(ProgramError::AlreadyAttached),
83 Entry::Vacant(entry) => Ok(entry.insert().get().id()),
84 }
85 }
86
87 pub(crate) fn remove(&mut self, link_id: T::Id) -> Result<(), ProgramError> {
88 self.links
89 .take(&link_id)
90 .ok_or(ProgramError::NotAttached)?
91 .detach()
92 }
93
94 pub(crate) fn forget(&mut self, link_id: T::Id) -> Result<T, ProgramError> {
95 self.links.take(&link_id).ok_or(ProgramError::NotAttached)
96 }
97}
98
99impl<T: Link> Links<T> {
100 pub(crate) fn remove_all(&mut self) -> Result<(), ProgramError> {
101 for link in self.links.drain() {
102 link.detach()?;
103 }
104 Ok(())
105 }
106}
107
108impl<T: Link> Drop for Links<T> {
109 fn drop(&mut self) {
110 let _unused: Result<(), ProgramError> = self.remove_all();
111 }
112}
113
114#[doc(alias = "bpf_link_info")]
116pub struct LinkInfo(bpf_link_info);
117
118impl LinkInfo {
119 pub(crate) fn new_from_fd(fd: BorrowedFd<'_>) -> Result<Self, LinkError> {
120 let info = bpf_link_get_info_by_fd(fd)?;
121 Ok(Self(info))
122 }
123
124 pub const fn id(&self) -> u32 {
126 self.0.id
127 }
128
129 pub const fn program_id(&self) -> u32 {
131 self.0.prog_id
132 }
133
134 pub fn link_type(&self) -> Result<LinkType, LinkError> {
136 bpf_link_type::try_from(self.0.type_)
137 .map_err(|InvalidTypeBinding { value }| LinkError::UnknownLinkType(value))
138 .and_then(LinkType::try_from)
139 }
140}
141
142#[non_exhaustive]
144#[doc(alias = "bpf_link_type")]
145#[derive(Copy, Clone, Debug, PartialEq)]
146pub enum LinkType {
147 #[doc(alias = "BPF_LINK_TYPE_UNSPEC")]
149 Unspecified = bpf_link_type::BPF_LINK_TYPE_UNSPEC as isize,
150 #[doc(alias = "BPF_LINK_TYPE_RAW_TRACEPOINT")]
152 RawTracePoint = bpf_link_type::BPF_LINK_TYPE_RAW_TRACEPOINT as isize,
153 #[doc(alias = "BPF_LINK_TYPE_TRACING")]
155 Tracing = bpf_link_type::BPF_LINK_TYPE_TRACING as isize,
156 #[doc(alias = "BPF_LINK_TYPE_CGROUP")]
158 Cgroup = bpf_link_type::BPF_LINK_TYPE_CGROUP as isize,
159 #[doc(alias = "BPF_LINK_TYPE_ITER")]
161 Iter = bpf_link_type::BPF_LINK_TYPE_ITER as isize,
162 #[doc(alias = "BPF_LINK_TYPE_NETNS")]
164 Netns = bpf_link_type::BPF_LINK_TYPE_NETNS as isize,
165 #[doc(alias = "BPF_LINK_TYPE_XDP")]
167 Xdp = bpf_link_type::BPF_LINK_TYPE_XDP as isize,
168 #[doc(alias = "BPF_LINK_TYPE_PERF_EVENT")]
170 PerfEvent = bpf_link_type::BPF_LINK_TYPE_PERF_EVENT as isize,
171 #[doc(alias = "BPF_LINK_TYPE_KPROBE_MULTI")]
173 KProbeMulti = bpf_link_type::BPF_LINK_TYPE_KPROBE_MULTI as isize,
174 #[doc(alias = "BPF_LINK_TYPE_STRUCT_OPS")]
176 StructOps = bpf_link_type::BPF_LINK_TYPE_STRUCT_OPS as isize,
177 #[doc(alias = "BPF_LINK_TYPE_NETFILTER")]
179 Netfilter = bpf_link_type::BPF_LINK_TYPE_NETFILTER as isize,
180 #[doc(alias = "BPF_LINK_TYPE_TCX")]
182 Tcx = bpf_link_type::BPF_LINK_TYPE_TCX as isize,
183 #[doc(alias = "BPF_LINK_TYPE_UPROBE_MULTI")]
185 UProbeMulti = bpf_link_type::BPF_LINK_TYPE_UPROBE_MULTI as isize,
186 #[doc(alias = "BPF_LINK_TYPE_NETKIT")]
188 Netkit = bpf_link_type::BPF_LINK_TYPE_NETKIT as isize,
189}
190
191impl TryFrom<bpf_link_type> for LinkType {
192 type Error = LinkError;
193
194 fn try_from(link_type: bpf_link_type) -> Result<Self, Self::Error> {
195 match link_type {
196 bpf_link_type::BPF_LINK_TYPE_UNSPEC => Ok(Self::Unspecified),
197 bpf_link_type::BPF_LINK_TYPE_RAW_TRACEPOINT => Ok(Self::RawTracePoint),
198 bpf_link_type::BPF_LINK_TYPE_TRACING => Ok(Self::Tracing),
199 bpf_link_type::BPF_LINK_TYPE_CGROUP => Ok(Self::Cgroup),
200 bpf_link_type::BPF_LINK_TYPE_ITER => Ok(Self::Iter),
201 bpf_link_type::BPF_LINK_TYPE_NETNS => Ok(Self::Netns),
202 bpf_link_type::BPF_LINK_TYPE_XDP => Ok(Self::Xdp),
203 bpf_link_type::BPF_LINK_TYPE_PERF_EVENT => Ok(Self::PerfEvent),
204 bpf_link_type::BPF_LINK_TYPE_KPROBE_MULTI => Ok(Self::KProbeMulti),
205 bpf_link_type::BPF_LINK_TYPE_STRUCT_OPS => Ok(Self::StructOps),
206 bpf_link_type::BPF_LINK_TYPE_NETFILTER => Ok(Self::Netfilter),
207 bpf_link_type::BPF_LINK_TYPE_TCX => Ok(Self::Tcx),
208 bpf_link_type::BPF_LINK_TYPE_UPROBE_MULTI => Ok(Self::UProbeMulti),
209 bpf_link_type::BPF_LINK_TYPE_NETKIT => Ok(Self::Netkit),
210 bpf_link_type::__MAX_BPF_LINK_TYPE => Err(LinkError::UnknownLinkType(link_type as u32)),
211 }
212 }
213}
214
215#[derive(Debug, Hash, Eq, PartialEq)]
217pub struct FdLinkId(pub(crate) RawFd);
218
219#[derive(Debug)]
245pub struct FdLink {
246 pub(crate) fd: crate::MockableFd,
247}
248
249impl FdLink {
250 pub(crate) const fn new(fd: crate::MockableFd) -> Self {
251 Self { fd }
252 }
253
254 pub fn pin<P: AsRef<Path>>(self, path: P) -> Result<PinnedLink, PinError> {
284 use std::os::unix::ffi::OsStrExt as _;
285
286 let path = path.as_ref();
287 let path_string = CString::new(path.as_os_str().as_bytes()).map_err(|error| {
288 PinError::InvalidPinPath {
289 path: path.into(),
290 error,
291 }
292 })?;
293 bpf_pin_object(self.fd.as_fd(), &path_string).map_err(|io_error| SyscallError {
294 call: "BPF_OBJ_PIN",
295 io_error,
296 })?;
297 Ok(PinnedLink::new(path.into(), self))
298 }
299
300 pub fn info(&self) -> Result<LinkInfo, LinkError> {
302 LinkInfo::new_from_fd(self.fd.as_fd())
303 }
304}
305
306impl Link for FdLink {
307 type Id = FdLinkId;
308
309 fn id(&self) -> Self::Id {
310 FdLinkId(self.fd.as_raw_fd())
311 }
312
313 fn detach(self) -> Result<(), ProgramError> {
314 Ok(())
320 }
321}
322
323id_as_key!(FdLink, FdLinkId);
324
325impl From<PinnedLink> for FdLink {
326 fn from(p: PinnedLink) -> Self {
327 p.inner
328 }
329}
330
331#[derive(Debug)]
337pub struct PinnedLink {
338 inner: FdLink,
339 path: PathBuf,
340}
341
342impl PinnedLink {
343 const fn new(path: PathBuf, link: FdLink) -> Self {
344 Self { inner: link, path }
345 }
346
347 pub fn from_pin<P: AsRef<Path>>(path: P) -> Result<Self, LinkError> {
349 use std::os::unix::ffi::OsStrExt as _;
350
351 let path_string = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
353 let fd = bpf_get_object(&path_string).map_err(|io_error| {
354 LinkError::SyscallError(SyscallError {
355 call: "BPF_OBJ_GET",
356 io_error,
357 })
358 })?;
359 Ok(Self::new(path.as_ref().to_path_buf(), FdLink::new(fd)))
360 }
361
362 pub fn unpin(self) -> Result<FdLink, io::Error> {
364 std::fs::remove_file(self.path)?;
365 Ok(self.inner)
366 }
367}
368
369#[derive(Debug, Hash, Eq, PartialEq)]
371pub struct ProgAttachLinkId(RawFd, RawFd, bpf_attach_type);
372
373#[derive(Debug)]
375pub struct ProgAttachLink {
376 prog_fd: ProgramFd,
377 target_fd: crate::MockableFd,
378 attach_type: bpf_attach_type,
379}
380
381impl ProgAttachLink {
382 pub(crate) const fn new(
383 prog_fd: ProgramFd,
384 target_fd: crate::MockableFd,
385 attach_type: bpf_attach_type,
386 ) -> Self {
387 Self {
388 prog_fd,
389 target_fd,
390 attach_type,
391 }
392 }
393
394 pub(crate) fn attach(
395 prog_fd: BorrowedFd<'_>,
396 target_fd: BorrowedFd<'_>,
397 attach_type: impl Into<bpf_attach_type>,
398 mode: CgroupAttachMode,
399 ) -> Result<Self, ProgramError> {
400 let prog_fd = prog_fd.try_clone_to_owned()?;
405 let prog_fd = crate::MockableFd::from_fd(prog_fd);
406 let target_fd = target_fd.try_clone_to_owned()?;
407 let target_fd = crate::MockableFd::from_fd(target_fd);
408 let attach_type = attach_type.into();
409 bpf_prog_attach(prog_fd.as_fd(), target_fd.as_fd(), attach_type, mode.into())?;
410
411 let prog_fd = ProgramFd(prog_fd);
412 Ok(Self {
413 prog_fd,
414 target_fd,
415 attach_type,
416 })
417 }
418}
419
420impl Link for ProgAttachLink {
421 type Id = ProgAttachLinkId;
422
423 fn id(&self) -> Self::Id {
424 ProgAttachLinkId(
425 self.prog_fd.as_fd().as_raw_fd(),
426 self.target_fd.as_raw_fd(),
427 self.attach_type,
428 )
429 }
430
431 fn detach(self) -> Result<(), ProgramError> {
432 bpf_prog_detach(
433 self.prog_fd.as_fd(),
434 self.target_fd.as_fd(),
435 self.attach_type,
436 )
437 .map_err(Into::into)
438 }
439}
440
441id_as_key!(ProgAttachLink, ProgAttachLinkId);
442
443macro_rules! id_as_key {
444 ($wrapper:ident, $wrapper_id:ident) => {
445 impl PartialEq for $wrapper {
446 fn eq(&self, other: &Self) -> bool {
447 use $crate::programs::links::Link as _;
448
449 self.id() == other.id()
450 }
451 }
452
453 impl Eq for $wrapper {}
454
455 impl std::hash::Hash for $wrapper {
456 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
457 use $crate::programs::links::Link as _;
458
459 self.id().hash(state)
460 }
461 }
462
463 impl hashbrown::Equivalent<$wrapper> for $wrapper_id {
464 fn equivalent(&self, key: &$wrapper) -> bool {
465 use $crate::programs::links::Link as _;
466
467 *self == key.id()
468 }
469 }
470 };
471}
472
473pub(crate) use id_as_key;
474
475macro_rules! define_link_wrapper {
476 ($wrapper:ident, $wrapper_id:ident, $base:ident, $base_id:ident, $program:ident $(,)?) => {
477 #[doc = concat!("[`", stringify!($program), "::attach`]")]
479 #[doc = concat!("[`", stringify!($program), "::detach`]")]
481 #[derive(Debug, Hash, Eq, PartialEq)]
483 pub struct $wrapper_id($base_id);
484
485 #[doc = concat!("[`", stringify!($program), "`]")]
487 #[derive(Debug)]
489 pub struct $wrapper(Option<$base>);
490
491 #[expect(clippy::allow_attributes, reason = "macro")]
492 #[allow(dead_code, reason = "macro")]
493 impl $wrapper {
496 const fn new(base: $base) -> $wrapper {
497 $wrapper(Some(base))
498 }
499
500 const fn inner(&self) -> &$base {
501 self.0.as_ref().unwrap()
502 }
503
504 fn into_inner(mut self) -> $base {
505 self.0.take().unwrap()
506 }
507 }
508
509 impl Drop for $wrapper {
510 fn drop(&mut self) {
511 use $crate::programs::links::Link as _;
512
513 if let Some(base) = self.0.take() {
514 let _unused: Result<(), ProgramError> = base.detach();
515 }
516 }
517 }
518
519 impl $crate::programs::Link for $wrapper {
520 type Id = $wrapper_id;
521
522 fn id(&self) -> Self::Id {
523 $wrapper_id(self.0.as_ref().unwrap().id())
524 }
525
526 fn detach(mut self) -> Result<(), ProgramError> {
527 self.0.take().unwrap().detach()
528 }
529 }
530
531 $crate::programs::links::id_as_key!($wrapper, $wrapper_id);
532
533 impl From<$base> for $wrapper {
534 fn from(b: $base) -> $wrapper {
535 $wrapper(Some(b))
536 }
537 }
538
539 impl From<$wrapper> for $base {
540 fn from(mut w: $wrapper) -> $base {
541 w.0.take().unwrap()
542 }
543 }
544
545 impl $program {
546 pub fn detach(&mut self, link_id: $wrapper_id) -> Result<(), ProgramError> {
550 self.data.links.remove(link_id)
551 }
552
553 #[doc = concat!("[`", stringify!($wrapper), "`]")]
558 pub fn take_link(&mut self, link_id: $wrapper_id) -> Result<$wrapper, ProgramError> {
560 self.data.links.forget(link_id)
561 }
562 }
563 };
564}
565
566pub(crate) use define_link_wrapper;
567
568macro_rules! impl_try_into_fdlink {
569 ($wrapper:ident, $inner:ident) => {
570 impl TryFrom<$wrapper> for $crate::programs::FdLink {
571 type Error = $crate::programs::LinkError;
572
573 fn try_from(value: $wrapper) -> Result<Self, Self::Error> {
574 if let $inner::Fd(fd) = value.into_inner() {
575 Ok(fd)
576 } else {
577 Err($crate::programs::LinkError::InvalidLink)
578 }
579 }
580 }
581 };
582}
583
584pub(crate) use impl_try_into_fdlink;
585
586macro_rules! impl_try_from_fdlink {
587 ($wrapper:ident, $inner:ident, $link_type:expr) => {
588 impl TryFrom<$crate::programs::FdLink> for $wrapper {
589 type Error = $crate::programs::LinkError;
590
591 fn try_from(fd_link: $crate::programs::FdLink) -> Result<Self, Self::Error> {
592 use std::os::fd::AsFd as _;
593
594 let info = $crate::sys::bpf_link_get_info_by_fd(fd_link.fd.as_fd())?;
595 if info.type_ == ($link_type as u32) {
596 return Ok(Self::new($inner::Fd(fd_link)));
597 }
598 Err($crate::programs::LinkError::InvalidLink)
599 }
600 }
601 };
602}
603
604pub(crate) use impl_try_from_fdlink;
605
606#[derive(Error, Debug)]
607pub enum LinkError {
609 #[error("Invalid link")]
611 InvalidLink,
612
613 #[error("unknown link type {0}")]
616 UnknownLinkType(u32),
617
618 #[error(transparent)]
620 SyscallError(#[from] SyscallError),
621}
622
623#[derive(Debug)]
624pub(crate) enum LinkRef {
625 Id(u32),
626 Fd(RawFd),
627}
628
629bitflags::bitflags! {
630 #[derive(Clone, Copy, Debug, Default)]
632 pub(crate) struct MprogFlags: u32 {
633 const REPLACE = BPF_F_REPLACE;
634 const BEFORE = BPF_F_BEFORE;
635 const AFTER = BPF_F_AFTER;
636 const ID = BPF_F_ID;
637 const LINK = BPF_F_LINK;
638 }
639}
640
641#[derive(Debug)]
661pub struct LinkOrder {
662 pub(crate) link_ref: LinkRef,
663 pub(crate) flags: MprogFlags,
664}
665
666impl Default for LinkOrder {
668 fn default() -> Self {
669 Self {
670 link_ref: LinkRef::Fd(0),
671 flags: MprogFlags::AFTER,
672 }
673 }
674}
675
676impl LinkOrder {
677 pub const fn first() -> Self {
679 Self {
680 link_ref: LinkRef::Id(0),
681 flags: MprogFlags::BEFORE,
682 }
683 }
684
685 pub const fn last() -> Self {
687 Self {
688 link_ref: LinkRef::Id(0),
689 flags: MprogFlags::AFTER,
690 }
691 }
692
693 pub fn before_link<L: MultiProgLink>(link: &L) -> Result<Self, LinkError> {
695 Ok(Self {
696 link_ref: LinkRef::Fd(link.fd()?.as_raw_fd()),
697 flags: MprogFlags::BEFORE | MprogFlags::LINK,
698 })
699 }
700
701 pub fn after_link<L: MultiProgLink>(link: &L) -> Result<Self, LinkError> {
703 Ok(Self {
704 link_ref: LinkRef::Fd(link.fd()?.as_raw_fd()),
705 flags: MprogFlags::AFTER | MprogFlags::LINK,
706 })
707 }
708
709 pub fn before_program<P: MultiProgram>(program: &P) -> Result<Self, ProgramError> {
711 Ok(Self {
712 link_ref: LinkRef::Fd(program.fd()?.as_raw_fd()),
713 flags: MprogFlags::BEFORE,
714 })
715 }
716
717 pub fn after_program<P: MultiProgram>(program: &P) -> Result<Self, ProgramError> {
719 Ok(Self {
720 link_ref: LinkRef::Fd(program.fd()?.as_raw_fd()),
721 flags: MprogFlags::AFTER,
722 })
723 }
724
725 pub fn before_program_id(id: ProgramId) -> Self {
727 Self {
728 link_ref: LinkRef::Id(id.0),
729 flags: MprogFlags::BEFORE | MprogFlags::ID,
730 }
731 }
732
733 pub fn after_program_id(id: ProgramId) -> Self {
735 Self {
736 link_ref: LinkRef::Id(id.0),
737 flags: MprogFlags::AFTER | MprogFlags::ID,
738 }
739 }
740}
741
742#[cfg(test)]
743mod tests {
744 use std::{cell::RefCell, fs::File, rc::Rc};
745
746 use assert_matches::assert_matches;
747 use aya_obj::generated::{BPF_F_ALLOW_MULTI, BPF_F_ALLOW_OVERRIDE};
748 use tempfile::tempdir;
749
750 use super::{FdLink, Link, Links};
751 use crate::{
752 programs::{CgroupAttachMode, ProgramError},
753 sys::override_syscall,
754 };
755
756 #[derive(Debug, Hash, Eq, PartialEq)]
757 struct TestLinkId(u8, u8);
758
759 #[derive(Debug)]
760 struct TestLink {
761 id: (u8, u8),
762 detached: Rc<RefCell<u8>>,
763 }
764
765 impl TestLink {
766 fn new(a: u8, b: u8) -> Self {
767 Self {
768 id: (a, b),
769 detached: Rc::new(RefCell::new(0)),
770 }
771 }
772 }
773
774 impl Link for TestLink {
775 type Id = TestLinkId;
776
777 fn id(&self) -> Self::Id {
778 TestLinkId(self.id.0, self.id.1)
779 }
780
781 fn detach(self) -> Result<(), ProgramError> {
782 *self.detached.borrow_mut() += 1;
783 Ok(())
784 }
785 }
786
787 id_as_key!(TestLink, TestLinkId);
788
789 #[test]
790 fn test_link_map() {
791 let mut links = Links::new();
792 let l1 = TestLink::new(1, 2);
793 let l1_detached = Rc::clone(&l1.detached);
794 let l2 = TestLink::new(1, 3);
795 let l2_detached = Rc::clone(&l2.detached);
796
797 let id1 = links.insert(l1).unwrap();
798 let id2 = links.insert(l2).unwrap();
799
800 assert_eq!(*l1_detached.borrow(), 0);
801 assert_eq!(*l2_detached.borrow(), 0);
802
803 links.remove(id1).unwrap();
804 assert_eq!(*l1_detached.borrow(), 1);
805 assert_eq!(*l2_detached.borrow(), 0);
806
807 links.remove(id2).unwrap();
808 assert_eq!(*l1_detached.borrow(), 1);
809 assert_eq!(*l2_detached.borrow(), 1);
810 }
811
812 #[test]
813 fn test_already_attached() {
814 let mut links = Links::new();
815
816 links.insert(TestLink::new(1, 2)).unwrap();
817 assert_matches!(
818 links.insert(TestLink::new(1, 2)),
819 Err(ProgramError::AlreadyAttached)
820 );
821 }
822
823 #[test]
824 fn test_not_attached() {
825 let mut links = Links::new();
826
827 let l1 = TestLink::new(1, 2);
828 let l1_id1 = l1.id();
829 let l1_id2 = l1.id();
830 links.insert(TestLink::new(1, 2)).unwrap();
831 links.remove(l1_id1).unwrap();
832 assert_matches!(links.remove(l1_id2), Err(ProgramError::NotAttached));
833 }
834
835 #[test]
836 fn test_drop_detach() {
837 let l1 = TestLink::new(1, 2);
838 let l1_detached = Rc::clone(&l1.detached);
839 let l2 = TestLink::new(1, 3);
840 let l2_detached = Rc::clone(&l2.detached);
841
842 {
843 let mut links = Links::new();
844 let id1 = links.insert(l1).unwrap();
845 links.insert(l2).unwrap();
846 links.remove(id1).unwrap();
848 assert_eq!(*l1_detached.borrow(), 1);
849 assert_eq!(*l2_detached.borrow(), 0);
850 }
851 assert_eq!(*l1_detached.borrow(), 1);
853 assert_eq!(*l2_detached.borrow(), 1);
854 }
855
856 #[test]
857 fn test_owned_detach() {
858 let l1 = TestLink::new(1, 2);
859 let l1_detached = Rc::clone(&l1.detached);
860 let l2 = TestLink::new(1, 3);
861 let l2_detached = Rc::clone(&l2.detached);
862
863 let owned_l1 = {
864 let mut links = Links::new();
865 let id1 = links.insert(l1).unwrap();
866 links.insert(l2).unwrap();
867 let owned_l1 = links.forget(id1);
869 assert_eq!(*l1_detached.borrow(), 0);
870 assert_eq!(*l2_detached.borrow(), 0);
871 owned_l1.unwrap()
872 };
873
874 assert_eq!(*l1_detached.borrow(), 0);
876 assert_eq!(*l2_detached.borrow(), 1);
877
878 owned_l1.detach().unwrap();
880 assert_eq!(*l1_detached.borrow(), 1);
881 assert_eq!(*l2_detached.borrow(), 1);
882 }
883
884 #[test]
885 #[cfg_attr(miri, ignore = "`mkdir` not available when isolation is enabled")]
886 fn test_pin() {
887 let dir = tempdir().unwrap();
888 let f1 = File::create(dir.path().join("f1")).expect("unable to create file in tmpdir");
889 let fd_link = FdLink::new(f1.into());
890
891 override_syscall(|_| Ok(0));
893 let pin = dir.path().join("f1-pin");
895 File::create(&pin).expect("unable to create file in tmpdir");
896 assert!(pin.exists());
897
898 let pinned_link = fd_link.pin(&pin).expect("pin failed");
899 pinned_link.unpin().expect("unpin failed");
900 assert!(!pin.exists());
901 }
902
903 #[test]
904 fn test_cgroup_attach_flag() {
905 assert_eq!(u32::from(CgroupAttachMode::Single), 0);
906 assert_eq!(
907 u32::from(CgroupAttachMode::AllowOverride),
908 BPF_F_ALLOW_OVERRIDE
909 );
910 assert_eq!(
911 u32::from(CgroupAttachMode::AllowMultiple),
912 BPF_F_ALLOW_MULTI
913 );
914 }
915}