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: 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 bpf_prog_attach(prog_fd.as_fd(), target_fd.as_fd(), attach_type, mode.into())?;
409
410 let prog_fd = ProgramFd(prog_fd);
411 Ok(Self {
412 prog_fd,
413 target_fd,
414 attach_type,
415 })
416 }
417}
418
419impl Link for ProgAttachLink {
420 type Id = ProgAttachLinkId;
421
422 fn id(&self) -> Self::Id {
423 ProgAttachLinkId(
424 self.prog_fd.as_fd().as_raw_fd(),
425 self.target_fd.as_raw_fd(),
426 self.attach_type,
427 )
428 }
429
430 fn detach(self) -> Result<(), ProgramError> {
431 bpf_prog_detach(
432 self.prog_fd.as_fd(),
433 self.target_fd.as_fd(),
434 self.attach_type,
435 )
436 .map_err(Into::into)
437 }
438}
439
440id_as_key!(ProgAttachLink, ProgAttachLinkId);
441
442macro_rules! id_as_key {
443 ($wrapper:ident, $wrapper_id:ident) => {
444 impl PartialEq for $wrapper {
445 fn eq(&self, other: &Self) -> bool {
446 use $crate::programs::links::Link as _;
447
448 self.id() == other.id()
449 }
450 }
451
452 impl Eq for $wrapper {}
453
454 impl std::hash::Hash for $wrapper {
455 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
456 use $crate::programs::links::Link as _;
457
458 self.id().hash(state)
459 }
460 }
461
462 impl hashbrown::Equivalent<$wrapper> for $wrapper_id {
463 fn equivalent(&self, key: &$wrapper) -> bool {
464 use $crate::programs::links::Link as _;
465
466 *self == key.id()
467 }
468 }
469 };
470}
471
472pub(crate) use id_as_key;
473
474macro_rules! define_link_wrapper {
475 ($wrapper:ident, $wrapper_id:ident, $base:ident, $base_id:ident, $program:ident $(,)?) => {
476 #[doc = concat!("[`", stringify!($program), "::attach`]")]
478 #[doc = concat!("[`", stringify!($program), "::detach`]")]
480 #[derive(Debug, Hash, Eq, PartialEq)]
482 pub struct $wrapper_id($base_id);
483
484 #[doc = concat!("[`", stringify!($program), "`]")]
486 #[derive(Debug)]
488 pub struct $wrapper(Option<$base>);
489
490 #[expect(clippy::allow_attributes, reason = "macro")]
491 #[allow(dead_code, reason = "macro")]
492 impl $wrapper {
495 const fn new(base: $base) -> $wrapper {
496 $wrapper(Some(base))
497 }
498
499 const fn inner(&self) -> &$base {
500 self.0.as_ref().unwrap()
501 }
502
503 fn into_inner(mut self) -> $base {
504 self.0.take().unwrap()
505 }
506 }
507
508 impl Drop for $wrapper {
509 fn drop(&mut self) {
510 use $crate::programs::links::Link as _;
511
512 if let Some(base) = self.0.take() {
513 let _unused: Result<(), ProgramError> = base.detach();
514 }
515 }
516 }
517
518 impl $crate::programs::Link for $wrapper {
519 type Id = $wrapper_id;
520
521 fn id(&self) -> Self::Id {
522 $wrapper_id(self.0.as_ref().unwrap().id())
523 }
524
525 fn detach(mut self) -> Result<(), ProgramError> {
526 self.0.take().unwrap().detach()
527 }
528 }
529
530 $crate::programs::links::id_as_key!($wrapper, $wrapper_id);
531
532 impl From<$base> for $wrapper {
533 fn from(b: $base) -> $wrapper {
534 $wrapper(Some(b))
535 }
536 }
537
538 impl From<$wrapper> for $base {
539 fn from(mut w: $wrapper) -> $base {
540 w.0.take().unwrap()
541 }
542 }
543
544 impl $program {
545 pub fn detach(&mut self, link_id: $wrapper_id) -> Result<(), ProgramError> {
549 self.data.links.remove(link_id)
550 }
551
552 #[doc = concat!("[`", stringify!($wrapper), "`]")]
557 pub fn take_link(&mut self, link_id: $wrapper_id) -> Result<$wrapper, ProgramError> {
559 self.data.links.forget(link_id)
560 }
561 }
562 };
563}
564
565pub(crate) use define_link_wrapper;
566
567macro_rules! impl_try_into_fdlink {
568 ($wrapper:ident, $inner:ident) => {
569 impl TryFrom<$wrapper> for $crate::programs::FdLink {
570 type Error = $crate::programs::LinkError;
571
572 fn try_from(value: $wrapper) -> Result<Self, Self::Error> {
573 if let $inner::Fd(fd) = value.into_inner() {
574 Ok(fd)
575 } else {
576 Err($crate::programs::LinkError::InvalidLink)
577 }
578 }
579 }
580 };
581}
582
583pub(crate) use impl_try_into_fdlink;
584
585#[derive(Error, Debug)]
586pub enum LinkError {
588 #[error("Invalid link")]
590 InvalidLink,
591
592 #[error("unknown link type {0}")]
595 UnknownLinkType(u32),
596
597 #[error(transparent)]
599 SyscallError(#[from] SyscallError),
600}
601
602#[derive(Debug)]
603pub(crate) enum LinkRef {
604 Id(u32),
605 Fd(RawFd),
606}
607
608bitflags::bitflags! {
609 #[derive(Clone, Copy, Debug, Default)]
611 pub(crate) struct MprogFlags: u32 {
612 const REPLACE = BPF_F_REPLACE;
613 const BEFORE = BPF_F_BEFORE;
614 const AFTER = BPF_F_AFTER;
615 const ID = BPF_F_ID;
616 const LINK = BPF_F_LINK;
617 }
618}
619
620#[derive(Debug)]
640pub struct LinkOrder {
641 pub(crate) link_ref: LinkRef,
642 pub(crate) flags: MprogFlags,
643}
644
645impl Default for LinkOrder {
647 fn default() -> Self {
648 Self {
649 link_ref: LinkRef::Fd(0),
650 flags: MprogFlags::AFTER,
651 }
652 }
653}
654
655impl LinkOrder {
656 pub const fn first() -> Self {
658 Self {
659 link_ref: LinkRef::Id(0),
660 flags: MprogFlags::BEFORE,
661 }
662 }
663
664 pub const fn last() -> Self {
666 Self {
667 link_ref: LinkRef::Id(0),
668 flags: MprogFlags::AFTER,
669 }
670 }
671
672 pub fn before_link<L: MultiProgLink>(link: &L) -> Result<Self, LinkError> {
674 Ok(Self {
675 link_ref: LinkRef::Fd(link.fd()?.as_raw_fd()),
676 flags: MprogFlags::BEFORE | MprogFlags::LINK,
677 })
678 }
679
680 pub fn after_link<L: MultiProgLink>(link: &L) -> Result<Self, LinkError> {
682 Ok(Self {
683 link_ref: LinkRef::Fd(link.fd()?.as_raw_fd()),
684 flags: MprogFlags::AFTER | MprogFlags::LINK,
685 })
686 }
687
688 pub fn before_program<P: MultiProgram>(program: &P) -> Result<Self, ProgramError> {
690 Ok(Self {
691 link_ref: LinkRef::Fd(program.fd()?.as_raw_fd()),
692 flags: MprogFlags::BEFORE,
693 })
694 }
695
696 pub fn after_program<P: MultiProgram>(program: &P) -> Result<Self, ProgramError> {
698 Ok(Self {
699 link_ref: LinkRef::Fd(program.fd()?.as_raw_fd()),
700 flags: MprogFlags::AFTER,
701 })
702 }
703
704 pub fn before_program_id(id: ProgramId) -> Self {
706 Self {
707 link_ref: LinkRef::Id(id.0),
708 flags: MprogFlags::BEFORE | MprogFlags::ID,
709 }
710 }
711
712 pub fn after_program_id(id: ProgramId) -> Self {
714 Self {
715 link_ref: LinkRef::Id(id.0),
716 flags: MprogFlags::AFTER | MprogFlags::ID,
717 }
718 }
719}
720
721#[cfg(test)]
722mod tests {
723 use std::{cell::RefCell, fs::File, rc::Rc};
724
725 use assert_matches::assert_matches;
726 use aya_obj::generated::{BPF_F_ALLOW_MULTI, BPF_F_ALLOW_OVERRIDE};
727 use tempfile::tempdir;
728
729 use super::{FdLink, Link, Links};
730 use crate::{
731 programs::{CgroupAttachMode, ProgramError},
732 sys::override_syscall,
733 };
734
735 #[derive(Debug, Hash, Eq, PartialEq)]
736 struct TestLinkId(u8, u8);
737
738 #[derive(Debug)]
739 struct TestLink {
740 id: (u8, u8),
741 detached: Rc<RefCell<u8>>,
742 }
743
744 impl TestLink {
745 fn new(a: u8, b: u8) -> Self {
746 Self {
747 id: (a, b),
748 detached: Rc::new(RefCell::new(0)),
749 }
750 }
751 }
752
753 impl Link for TestLink {
754 type Id = TestLinkId;
755
756 fn id(&self) -> Self::Id {
757 TestLinkId(self.id.0, self.id.1)
758 }
759
760 fn detach(self) -> Result<(), ProgramError> {
761 *self.detached.borrow_mut() += 1;
762 Ok(())
763 }
764 }
765
766 id_as_key!(TestLink, TestLinkId);
767
768 #[test]
769 fn test_link_map() {
770 let mut links = Links::new();
771 let l1 = TestLink::new(1, 2);
772 let l1_detached = Rc::clone(&l1.detached);
773 let l2 = TestLink::new(1, 3);
774 let l2_detached = Rc::clone(&l2.detached);
775
776 let id1 = links.insert(l1).unwrap();
777 let id2 = links.insert(l2).unwrap();
778
779 assert_eq!(*l1_detached.borrow(), 0);
780 assert_eq!(*l2_detached.borrow(), 0);
781
782 links.remove(id1).unwrap();
783 assert_eq!(*l1_detached.borrow(), 1);
784 assert_eq!(*l2_detached.borrow(), 0);
785
786 links.remove(id2).unwrap();
787 assert_eq!(*l1_detached.borrow(), 1);
788 assert_eq!(*l2_detached.borrow(), 1);
789 }
790
791 #[test]
792 fn test_already_attached() {
793 let mut links = Links::new();
794
795 links.insert(TestLink::new(1, 2)).unwrap();
796 assert_matches!(
797 links.insert(TestLink::new(1, 2)),
798 Err(ProgramError::AlreadyAttached)
799 );
800 }
801
802 #[test]
803 fn test_not_attached() {
804 let mut links = Links::new();
805
806 let l1 = TestLink::new(1, 2);
807 let l1_id1 = l1.id();
808 let l1_id2 = l1.id();
809 links.insert(TestLink::new(1, 2)).unwrap();
810 links.remove(l1_id1).unwrap();
811 assert_matches!(links.remove(l1_id2), Err(ProgramError::NotAttached));
812 }
813
814 #[test]
815 fn test_drop_detach() {
816 let l1 = TestLink::new(1, 2);
817 let l1_detached = Rc::clone(&l1.detached);
818 let l2 = TestLink::new(1, 3);
819 let l2_detached = Rc::clone(&l2.detached);
820
821 {
822 let mut links = Links::new();
823 let id1 = links.insert(l1).unwrap();
824 links.insert(l2).unwrap();
825 links.remove(id1).unwrap();
827 assert_eq!(*l1_detached.borrow(), 1);
828 assert_eq!(*l2_detached.borrow(), 0);
829 }
830 assert_eq!(*l1_detached.borrow(), 1);
832 assert_eq!(*l2_detached.borrow(), 1);
833 }
834
835 #[test]
836 fn test_owned_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 let owned_l1 = {
843 let mut links = Links::new();
844 let id1 = links.insert(l1).unwrap();
845 links.insert(l2).unwrap();
846 let owned_l1 = links.forget(id1);
848 assert_eq!(*l1_detached.borrow(), 0);
849 assert_eq!(*l2_detached.borrow(), 0);
850 owned_l1.unwrap()
851 };
852
853 assert_eq!(*l1_detached.borrow(), 0);
855 assert_eq!(*l2_detached.borrow(), 1);
856
857 owned_l1.detach().unwrap();
859 assert_eq!(*l1_detached.borrow(), 1);
860 assert_eq!(*l2_detached.borrow(), 1);
861 }
862
863 #[test]
864 #[cfg_attr(miri, ignore = "`mkdir` not available when isolation is enabled")]
865 fn test_pin() {
866 let dir = tempdir().unwrap();
867 let f1 = File::create(dir.path().join("f1")).expect("unable to create file in tmpdir");
868 let fd_link = FdLink::new(f1.into());
869
870 override_syscall(|_| Ok(0));
872 let pin = dir.path().join("f1-pin");
874 File::create(&pin).expect("unable to create file in tmpdir");
875 assert!(pin.exists());
876
877 let pinned_link = fd_link.pin(&pin).expect("pin failed");
878 pinned_link.unpin().expect("unpin failed");
879 assert!(!pin.exists());
880 }
881
882 #[test]
883 fn test_cgroup_attach_flag() {
884 assert_eq!(u32::from(CgroupAttachMode::Single), 0);
885 assert_eq!(
886 u32::from(CgroupAttachMode::AllowOverride),
887 BPF_F_ALLOW_OVERRIDE
888 );
889 assert_eq!(
890 u32::from(CgroupAttachMode::AllowMultiple),
891 BPF_F_ALLOW_MULTI
892 );
893 }
894}