1use std::any::Any;
2use std::cell::{Ref, RefCell, RefMut};
3use std::collections::HashMap;
4use std::ffi::{OsStr, OsString};
5use std::fs::{File, read_link};
6use std::os::fd::{AsRawFd, BorrowedFd, RawFd};
7use std::os::unix::io::OwnedFd;
8use std::path::{Path, PathBuf};
9use std::sync::Arc;
10
11use caps::{CapSet, CapsHashSet};
12use nix::mount::{MntFlags, MsFlags};
13use nix::sched::CloneFlags;
14use nix::sys::stat::{Mode, SFlag};
15use nix::unistd::{Gid, Uid};
16use oci_spec::runtime::PosixRlimit;
17
18use super::super::config::PersonalityDomain;
19use super::{Result, Syscall, linux};
20
21#[derive(Clone, PartialEq, Eq, Debug)]
22pub struct MountArgs {
23 pub source: Option<PathBuf>,
24 pub target: PathBuf,
25 pub fstype: Option<String>,
26 pub flags: MsFlags,
27 pub data: Option<String>,
28}
29
30#[derive(Clone, PartialEq, Eq, Debug)]
31pub struct MountFromFdArgs {
32 pub fd: i32,
33 pub target: PathBuf,
34}
35
36#[derive(Clone, PartialEq, Eq, Debug)]
37pub struct MoveMountArgs {
38 pub from_dirfd: i32,
39 pub from_path: Option<OsString>,
40 pub to_dirfd: i32,
41 pub to_path: Option<OsString>,
42 pub to_dirfd_path: Option<PathBuf>,
43 pub flags: u32,
44}
45
46#[derive(Clone, PartialEq, Eq, Debug)]
47pub struct FsopenArgs {
48 pub fsname: Option<String>,
49 pub flags: u32,
50}
51
52#[derive(Clone, PartialEq, Eq, Debug)]
53pub struct FsconfigArgs {
54 pub cmd: u32,
55 pub key: Option<String>,
56 pub val: Option<String>,
57 pub aux: libc::c_int,
58}
59
60#[derive(Clone, PartialEq, Eq, Debug)]
61pub struct FsmountArgs {
62 pub flags: u32,
63 pub attr_flags: Option<u64>,
64}
65
66#[derive(Clone, PartialEq, Eq, Debug)]
67pub struct OpenTreeArgs {
68 pub dirfd: i32,
69 pub path: Option<String>,
70 pub flags: u32,
71}
72
73#[derive(Clone, PartialEq, Eq, Debug)]
74pub struct MountSetattrArgs {
75 pub dirfd: i32,
76 pub pathname: PathBuf,
77 pub flags: u32,
78 pub attr_set: u64,
79 pub attr_clr: u64,
80 pub propagation: u64,
81 pub userns_fd: u64,
82 pub size: libc::size_t,
83}
84
85#[derive(Clone, PartialEq, Eq, Debug)]
86pub struct MknodArgs {
87 pub path: PathBuf,
88 pub kind: SFlag,
89 pub perm: Mode,
90 pub dev: u64,
91}
92
93#[derive(Clone, PartialEq, Eq, Debug)]
94pub struct ChownArgs {
95 pub path: PathBuf,
96 pub owner: Option<Uid>,
97 pub group: Option<Gid>,
98}
99
100#[derive(Clone, PartialEq, Eq, Debug)]
101pub struct IoPriorityArgs {
102 pub class: i64,
103 pub priority: i64,
104}
105
106#[derive(Clone, PartialEq, Eq, Debug)]
107pub struct MemPolicyArgs {
108 pub mode: i32,
109 pub nodemask: Vec<libc::c_ulong>, pub maxnode: u64,
111}
112
113#[derive(Clone, PartialEq, Eq, Debug)]
114pub struct UMount2Args {
115 pub target: PathBuf,
116 pub flags: MntFlags,
117}
118
119#[derive(Default)]
120struct Mock {
121 values: Vec<Box<dyn Any>>,
122 ret_err: Option<fn() -> Result<()>>,
123 ret_err_times: usize,
124}
125
126#[derive(PartialEq, Eq, Hash, Copy, Clone)]
127pub enum ArgName {
128 Namespace,
129 Unshare,
130 Mount,
131 MountFromFd,
132 Symlink,
133 Mknod,
134 Chown,
135 Hostname,
136 Domainname,
137 Groups,
138 Capability,
139 IoPriority,
140 MemPolicy,
141 UMount2,
142 MoveMount,
143 Fsopen,
144 Fsconfig,
145 Fsmount,
146 OpenTree,
147 MountSetattr,
148}
149
150impl ArgName {
151 fn iterator() -> impl Iterator<Item = ArgName> {
152 [
153 ArgName::Namespace,
154 ArgName::Unshare,
155 ArgName::Mount,
156 ArgName::MountFromFd,
157 ArgName::Symlink,
158 ArgName::Mknod,
159 ArgName::Chown,
160 ArgName::Hostname,
161 ArgName::Domainname,
162 ArgName::Groups,
163 ArgName::Capability,
164 ArgName::IoPriority,
165 ArgName::MemPolicy,
166 ArgName::MoveMount,
167 ArgName::Fsopen,
168 ArgName::Fsconfig,
169 ArgName::Fsmount,
170 ArgName::OpenTree,
171 ArgName::MountSetattr,
172 ]
173 .iter()
174 .copied()
175 }
176}
177
178struct MockCalls {
179 args: HashMap<ArgName, RefCell<Mock>>,
180}
181
182impl Default for MockCalls {
183 fn default() -> Self {
184 let mut m = MockCalls {
185 args: HashMap::new(),
186 };
187
188 for name in ArgName::iterator() {
189 m.args.insert(name, RefCell::new(Mock::default()));
190 }
191
192 m
193 }
194}
195
196impl MockCalls {
197 fn act(&self, name: ArgName, value: Box<dyn Any>) -> Result<()> {
198 if self.args.get(&name).unwrap().borrow().ret_err_times > 0 {
199 self.args.get(&name).unwrap().borrow_mut().ret_err_times -= 1;
200 if let Some(e) = &self.args.get(&name).unwrap().borrow().ret_err {
201 return e();
202 }
203 }
204
205 self.args
206 .get(&name)
207 .unwrap()
208 .borrow_mut()
209 .values
210 .push(value);
211 Ok(())
212 }
213
214 fn fetch(&self, name: ArgName) -> Ref<'_, Mock> {
215 self.args.get(&name).unwrap().borrow()
216 }
217
218 fn fetch_mut(&self, name: ArgName) -> RefMut<'_, Mock> {
219 self.args.get(&name).unwrap().borrow_mut()
220 }
221}
222
223fn dummy_owned_fd() -> OwnedFd {
227 OwnedFd::from(File::open("/dev/null").expect("failed to open /dev/null for a test fd"))
228}
229
230#[derive(Default)]
231pub struct TestHelperSyscall {
232 mock_id: RefCell<MockId>,
233 mocks: MockCalls,
234}
235
236pub struct MockId {
237 uid: Uid,
238 gid: Gid,
239 euid: Uid,
240 egid: Gid,
241}
242
243impl Default for MockId {
244 fn default() -> Self {
245 Self {
246 uid: nix::unistd::getuid(),
247 gid: nix::unistd::getgid(),
248 euid: nix::unistd::geteuid(),
249 egid: nix::unistd::getegid(),
250 }
251 }
252}
253
254impl Syscall for TestHelperSyscall {
255 fn as_any(&self) -> &dyn Any {
256 self
257 }
258
259 fn pivot_rootfs(&self, _path: &Path) -> Result<()> {
260 unimplemented!()
261 }
262
263 fn set_ns(&self, rawfd: i32, nstype: CloneFlags) -> Result<()> {
264 self.mocks
265 .act(ArgName::Namespace, Box::new((rawfd, nstype)))
266 }
267
268 fn set_id(&self, _uid: Uid, _gid: Gid) -> Result<()> {
269 self.mock_id.borrow_mut().uid = _uid;
270 self.mock_id.borrow_mut().gid = _gid;
271 self.mock_id.borrow_mut().euid = _uid;
272 self.mock_id.borrow_mut().egid = _gid;
273 Ok(())
274 }
275
276 fn unshare(&self, flags: CloneFlags) -> Result<()> {
277 self.mocks.act(ArgName::Unshare, Box::new(flags))
278 }
279
280 fn set_capability(&self, cset: CapSet, value: &CapsHashSet) -> Result<()> {
281 self.mocks
282 .act(ArgName::Capability, Box::new((cset, value.clone())))
283 }
284
285 fn set_hostname(&self, hostname: &str) -> Result<()> {
286 self.mocks
287 .act(ArgName::Hostname, Box::new(hostname.to_owned()))
288 }
289
290 fn set_domainname(&self, domainname: &str) -> Result<()> {
291 self.mocks
292 .act(ArgName::Domainname, Box::new(domainname.to_owned()))
293 }
294
295 fn set_rlimit(&self, _rlimit: &PosixRlimit) -> Result<()> {
296 todo!()
297 }
298
299 fn get_pwuid(&self, _: u32) -> Option<Arc<OsStr>> {
300 Some(OsString::from("youki").into())
301 }
302
303 fn chroot(&self, _: &Path) -> Result<()> {
304 todo!()
305 }
306
307 fn mount(
308 &self,
309 source: Option<&Path>,
310 target: &Path,
311 fstype: Option<&str>,
312 flags: MsFlags,
313 data: Option<&str>,
314 ) -> Result<()> {
315 let target_owned = if target.starts_with(Path::new("/proc/self/fd")) {
317 read_link(target).unwrap_or_else(|_| target.to_owned())
318 } else {
319 target.to_owned()
320 };
321
322 self.mocks.act(
323 ArgName::Mount,
324 Box::new(MountArgs {
325 source: source.map(|x| x.to_owned()),
326 target: target_owned,
327 fstype: fstype.map(|x| x.to_owned()),
328 flags,
329 data: data.map(|x| x.to_owned()),
330 }),
331 )
332 }
333
334 fn mount_from_fd(&self, source_fd: &OwnedFd, target: &Path) -> Result<()> {
335 self.mocks.act(
336 ArgName::MountFromFd,
337 Box::new(MountFromFdArgs {
338 fd: source_fd.as_raw_fd(),
339 target: target.to_owned(),
340 }),
341 )
342 }
343
344 fn move_mount(
345 &self,
346 from_dirfd: BorrowedFd<'_>,
347 from_path: Option<&str>,
348 to_dirfd: BorrowedFd<'_>,
349 to_path: Option<&str>,
350 flags: u32,
351 ) -> Result<()> {
352 let to_dirfd_path = if to_path.is_none() {
356 read_link(format!("/proc/self/fd/{}", to_dirfd.as_raw_fd())).ok()
357 } else {
358 None
359 };
360
361 self.mocks.act(
362 ArgName::MoveMount,
363 Box::new(MoveMountArgs {
364 from_dirfd: from_dirfd.as_raw_fd(),
365 from_path: from_path.map(OsString::from),
366 to_dirfd: to_dirfd.as_raw_fd(),
367 to_path: to_path.map(OsString::from),
368 to_dirfd_path,
369 flags,
370 }),
371 )
372 }
373
374 fn fsopen(&self, fstype: Option<&str>, flags: u32) -> Result<OwnedFd> {
375 self.mocks.act(
376 ArgName::Fsopen,
377 Box::new(FsopenArgs {
378 fsname: fstype.map(|s| s.to_owned()),
379 flags,
380 }),
381 )?;
382 Ok(dummy_owned_fd())
383 }
384
385 fn fsconfig(
386 &self,
387 _fsfd: BorrowedFd<'_>,
388 cmd: u32,
389 key: Option<&str>,
390 val: Option<&str>,
391 aux: libc::c_int,
392 ) -> Result<()> {
393 self.mocks.act(
394 ArgName::Fsconfig,
395 Box::new(FsconfigArgs {
396 cmd,
397 key: key.map(|s| s.to_owned()),
398 val: val.map(|s| s.to_owned()),
399 aux,
400 }),
401 )
402 }
403
404 fn fsmount(
405 &self,
406 _fsfd: BorrowedFd<'_>,
407 flags: u32,
408 attr_flags: Option<u64>,
409 ) -> Result<OwnedFd> {
410 self.mocks.act(
411 ArgName::Fsmount,
412 Box::new(FsmountArgs { flags, attr_flags }),
413 )?;
414 Ok(dummy_owned_fd())
415 }
416
417 fn open_tree(&self, dirfd: RawFd, path: Option<&str>, flags: u32) -> Result<OwnedFd> {
418 self.mocks.act(
419 ArgName::OpenTree,
420 Box::new(OpenTreeArgs {
421 dirfd,
422 path: path.map(|s| s.to_owned()),
423 flags,
424 }),
425 )?;
426 Ok(dummy_owned_fd())
427 }
428
429 fn symlink(&self, original: &Path, link: &Path) -> Result<()> {
430 self.mocks.act(
431 ArgName::Symlink,
432 Box::new((original.to_path_buf(), link.to_path_buf())),
433 )
434 }
435
436 fn mknod(&self, path: &Path, kind: SFlag, perm: Mode, dev: u64) -> Result<()> {
437 self.mocks.act(
438 ArgName::Mknod,
439 Box::new(MknodArgs {
440 path: path.to_path_buf(),
441 kind,
442 perm,
443 dev,
444 }),
445 )
446 }
447 fn chown(&self, path: &Path, owner: Option<Uid>, group: Option<Gid>) -> Result<()> {
448 self.mocks.act(
449 ArgName::Chown,
450 Box::new(ChownArgs {
451 path: path.to_path_buf(),
452 owner,
453 group,
454 }),
455 )
456 }
457
458 fn set_groups(&self, groups: &[Gid]) -> Result<()> {
459 self.mocks.act(ArgName::Groups, Box::new(groups.to_vec()))
460 }
461
462 fn close_range(&self, _: i32) -> Result<()> {
463 todo!()
464 }
465
466 fn mount_setattr(
467 &self,
468 dirfd: BorrowedFd<'_>,
469 pathname: &Path,
470 flags: u32,
471 mount_attr: &linux::MountAttr,
472 size: libc::size_t,
473 ) -> Result<()> {
474 self.mocks.act(
475 ArgName::MountSetattr,
476 Box::new(MountSetattrArgs {
477 dirfd: dirfd.as_raw_fd(),
478 pathname: pathname.to_owned(),
479 flags,
480 attr_set: mount_attr.attr_set,
481 attr_clr: mount_attr.attr_clr,
482 propagation: mount_attr.propagation,
483 userns_fd: mount_attr.userns_fd,
484 size,
485 }),
486 )
487 }
488
489 fn set_io_priority(&self, class: i64, priority: i64) -> Result<()> {
490 self.mocks.act(
491 ArgName::IoPriority,
492 Box::new(IoPriorityArgs { class, priority }),
493 )
494 }
495
496 fn set_mempolicy(&self, mode: i32, nodemask: &[libc::c_ulong], maxnode: u64) -> Result<()> {
497 self.mocks.act(
498 ArgName::MemPolicy,
499 Box::new(MemPolicyArgs {
500 mode,
501 nodemask: nodemask.to_vec(),
502 maxnode,
503 }),
504 )
505 }
506
507 fn umount2(&self, target: &Path, flags: MntFlags) -> Result<()> {
508 self.mocks.act(
509 ArgName::UMount2,
510 Box::new(UMount2Args {
511 target: target.to_owned(),
512 flags,
513 }),
514 )
515 }
516
517 fn get_uid(&self) -> Uid {
518 self.mock_id.borrow().uid
519 }
520
521 fn get_gid(&self) -> Gid {
522 self.mock_id.borrow().gid
523 }
524
525 fn get_euid(&self) -> Uid {
526 self.mock_id.borrow().euid
527 }
528
529 fn get_egid(&self) -> Gid {
530 self.mock_id.borrow().egid
531 }
532
533 fn personality(&self, _: PersonalityDomain) -> Result<()> {
534 todo!()
535 }
536}
537
538impl TestHelperSyscall {
539 pub fn set_ret_err(&self, name: ArgName, err: fn() -> Result<()>) {
540 self.mocks.fetch_mut(name).ret_err = Some(err);
541 self.set_ret_err_times(name, 1);
542 }
543
544 pub fn set_ret_err_times(&self, name: ArgName, times: usize) {
545 self.mocks.fetch_mut(name).ret_err_times = times;
546 }
547
548 pub fn get_setns_args(&self) -> Vec<(i32, CloneFlags)> {
549 self.mocks
550 .fetch(ArgName::Namespace)
551 .values
552 .iter()
553 .map(|x| *x.downcast_ref::<(i32, CloneFlags)>().unwrap())
554 .collect::<Vec<(i32, CloneFlags)>>()
555 }
556
557 pub fn get_unshare_args(&self) -> Vec<CloneFlags> {
558 self.mocks
559 .fetch(ArgName::Unshare)
560 .values
561 .iter()
562 .map(|x| *x.downcast_ref::<CloneFlags>().unwrap())
563 .collect::<Vec<CloneFlags>>()
564 }
565
566 pub fn get_set_capability_args(&self) -> Vec<(CapSet, CapsHashSet)> {
567 self.mocks
568 .fetch(ArgName::Capability)
569 .values
570 .iter()
571 .map(|x| x.downcast_ref::<(CapSet, CapsHashSet)>().unwrap().clone())
572 .collect::<Vec<(CapSet, CapsHashSet)>>()
573 }
574
575 pub fn get_mount_args(&self) -> Vec<MountArgs> {
576 self.mocks
577 .fetch(ArgName::Mount)
578 .values
579 .iter()
580 .map(|x| x.downcast_ref::<MountArgs>().unwrap().clone())
581 .collect::<Vec<MountArgs>>()
582 }
583
584 pub fn get_fsopen_args(&self) -> Vec<FsopenArgs> {
585 self.mocks
586 .fetch(ArgName::Fsopen)
587 .values
588 .iter()
589 .map(|x| x.downcast_ref::<FsopenArgs>().unwrap().clone())
590 .collect::<Vec<FsopenArgs>>()
591 }
592
593 pub fn get_fsconfig_args(&self) -> Vec<FsconfigArgs> {
594 self.mocks
595 .fetch(ArgName::Fsconfig)
596 .values
597 .iter()
598 .map(|x| x.downcast_ref::<FsconfigArgs>().unwrap().clone())
599 .collect::<Vec<FsconfigArgs>>()
600 }
601
602 pub fn get_fsmount_args(&self) -> Vec<FsmountArgs> {
603 self.mocks
604 .fetch(ArgName::Fsmount)
605 .values
606 .iter()
607 .map(|x| x.downcast_ref::<FsmountArgs>().unwrap().clone())
608 .collect::<Vec<FsmountArgs>>()
609 }
610
611 pub fn get_open_tree_args(&self) -> Vec<OpenTreeArgs> {
612 self.mocks
613 .fetch(ArgName::OpenTree)
614 .values
615 .iter()
616 .map(|x| x.downcast_ref::<OpenTreeArgs>().unwrap().clone())
617 .collect::<Vec<OpenTreeArgs>>()
618 }
619
620 pub fn get_mount_setattr_args(&self) -> Vec<MountSetattrArgs> {
621 self.mocks
622 .fetch(ArgName::MountSetattr)
623 .values
624 .iter()
625 .map(|x| x.downcast_ref::<MountSetattrArgs>().unwrap().clone())
626 .collect::<Vec<MountSetattrArgs>>()
627 }
628
629 pub fn get_move_mount_args(&self) -> Vec<MoveMountArgs> {
630 self.mocks
631 .fetch(ArgName::MoveMount)
632 .values
633 .iter()
634 .map(|x| x.downcast_ref::<MoveMountArgs>().unwrap().clone())
635 .collect::<Vec<MoveMountArgs>>()
636 }
637
638 pub fn get_mount_from_fd_args(&self) -> Vec<MountFromFdArgs> {
639 self.mocks
640 .fetch(ArgName::MountFromFd)
641 .values
642 .iter()
643 .map(|x| x.downcast_ref::<MountFromFdArgs>().unwrap().clone())
644 .collect::<Vec<MountFromFdArgs>>()
645 }
646
647 pub fn get_symlink_args(&self) -> Vec<(PathBuf, PathBuf)> {
648 self.mocks
649 .fetch(ArgName::Symlink)
650 .values
651 .iter()
652 .map(|x| x.downcast_ref::<(PathBuf, PathBuf)>().unwrap().clone())
653 .collect::<Vec<(PathBuf, PathBuf)>>()
654 }
655
656 pub fn get_mknod_args(&self) -> Vec<MknodArgs> {
657 self.mocks
658 .fetch(ArgName::Mknod)
659 .values
660 .iter()
661 .map(|x| x.downcast_ref::<MknodArgs>().unwrap().clone())
662 .collect::<Vec<MknodArgs>>()
663 }
664
665 pub fn get_chown_args(&self) -> Vec<ChownArgs> {
666 self.mocks
667 .fetch(ArgName::Chown)
668 .values
669 .iter()
670 .map(|x| x.downcast_ref::<ChownArgs>().unwrap().clone())
671 .collect::<Vec<ChownArgs>>()
672 }
673
674 pub fn get_hostname_args(&self) -> Vec<String> {
675 self.mocks
676 .fetch(ArgName::Hostname)
677 .values
678 .iter()
679 .map(|x| x.downcast_ref::<String>().unwrap().clone())
680 .collect::<Vec<String>>()
681 }
682
683 pub fn get_domainname_args(&self) -> Vec<String> {
684 self.mocks
685 .fetch(ArgName::Domainname)
686 .values
687 .iter()
688 .map(|x| x.downcast_ref::<String>().unwrap().clone())
689 .collect::<Vec<String>>()
690 }
691
692 pub fn get_groups_args(&self) -> Vec<Gid> {
693 self.mocks
694 .fetch(ArgName::Groups)
695 .values
696 .iter()
697 .flat_map(|x| x.downcast_ref::<Vec<Gid>>().unwrap().clone())
698 .collect::<Vec<Gid>>()
699 }
700
701 pub fn get_io_priority_args(&self) -> Vec<IoPriorityArgs> {
702 self.mocks
703 .fetch(ArgName::IoPriority)
704 .values
705 .iter()
706 .map(|x| x.downcast_ref::<IoPriorityArgs>().unwrap().clone())
707 .collect::<Vec<IoPriorityArgs>>()
708 }
709
710 pub fn get_mempolicy_args(&self) -> Vec<MemPolicyArgs> {
711 self.mocks
712 .fetch(ArgName::MemPolicy)
713 .values
714 .iter()
715 .map(|x| x.downcast_ref::<MemPolicyArgs>().unwrap().clone())
716 .collect::<Vec<MemPolicyArgs>>()
717 }
718
719 pub fn get_umount_args(&self) -> Vec<UMount2Args> {
720 self.mocks
721 .fetch(ArgName::UMount2)
722 .values
723 .iter()
724 .map(|x| x.downcast_ref::<UMount2Args>().unwrap().clone())
725 .collect::<Vec<UMount2Args>>()
726 }
727}