1use std::any::Any;
2use std::cell::{Ref, RefCell, RefMut};
3use std::collections::HashMap;
4use std::ffi::{OsStr, OsString};
5use std::os::fd::{AsRawFd, BorrowedFd, RawFd};
6use std::os::unix::io::OwnedFd;
7use std::path::{Path, PathBuf};
8use std::sync::Arc;
9
10use caps::{CapSet, CapsHashSet};
11use nix::mount::{MntFlags, MsFlags};
12use nix::sched::CloneFlags;
13use nix::sys::stat::{Mode, SFlag};
14use nix::unistd::{Gid, Uid};
15use oci_spec::runtime::PosixRlimit;
16
17use super::super::config::PersonalityDomain;
18use super::{Result, Syscall, linux};
19
20#[derive(Clone, PartialEq, Eq, Debug)]
21pub struct MountArgs {
22 pub source: Option<PathBuf>,
23 pub target: PathBuf,
24 pub fstype: Option<String>,
25 pub flags: MsFlags,
26 pub data: Option<String>,
27}
28
29#[derive(Clone, PartialEq, Eq, Debug)]
30pub struct MountFromFdArgs {
31 pub fd: i32,
32 pub target: PathBuf,
33}
34
35#[derive(Clone, PartialEq, Eq, Debug)]
36pub struct MoveMountArgs {
37 pub from_dirfd: i32,
38 pub from_path: Option<OsString>,
39 pub to_dirfd: i32,
40 pub to_path: Option<OsString>,
41 pub flags: u32,
42}
43
44#[derive(Clone, PartialEq, Eq, Debug)]
45pub struct FsopenArgs {
46 pub fsname: Option<String>,
47 pub flags: u32,
48}
49
50#[derive(Clone, PartialEq, Eq, Debug)]
51pub struct MknodArgs {
52 pub path: PathBuf,
53 pub kind: SFlag,
54 pub perm: Mode,
55 pub dev: u64,
56}
57
58#[derive(Clone, PartialEq, Eq, Debug)]
59pub struct ChownArgs {
60 pub path: PathBuf,
61 pub owner: Option<Uid>,
62 pub group: Option<Gid>,
63}
64
65#[derive(Clone, PartialEq, Eq, Debug)]
66pub struct IoPriorityArgs {
67 pub class: i64,
68 pub priority: i64,
69}
70
71#[derive(Clone, PartialEq, Eq, Debug)]
72pub struct MemPolicyArgs {
73 pub mode: i32,
74 pub nodemask: Vec<libc::c_ulong>, pub maxnode: u64,
76}
77
78#[derive(Clone, PartialEq, Eq, Debug)]
79pub struct UMount2Args {
80 pub target: PathBuf,
81 pub flags: MntFlags,
82}
83
84#[derive(Default)]
85struct Mock {
86 values: Vec<Box<dyn Any>>,
87 ret_err: Option<fn() -> Result<()>>,
88 ret_err_times: usize,
89}
90
91#[derive(PartialEq, Eq, Hash, Copy, Clone)]
92pub enum ArgName {
93 Namespace,
94 Unshare,
95 Mount,
96 MountFromFd,
97 Symlink,
98 Mknod,
99 Chown,
100 Hostname,
101 Domainname,
102 Groups,
103 Capability,
104 IoPriority,
105 MemPolicy,
106 UMount2,
107 MoveMount,
108 Fsopen,
109}
110
111impl ArgName {
112 fn iterator() -> impl Iterator<Item = ArgName> {
113 [
114 ArgName::Namespace,
115 ArgName::Unshare,
116 ArgName::Mount,
117 ArgName::MountFromFd,
118 ArgName::Symlink,
119 ArgName::Mknod,
120 ArgName::Chown,
121 ArgName::Hostname,
122 ArgName::Domainname,
123 ArgName::Groups,
124 ArgName::Capability,
125 ArgName::IoPriority,
126 ArgName::MemPolicy,
127 ArgName::MoveMount,
128 ]
129 .iter()
130 .copied()
131 }
132}
133
134struct MockCalls {
135 args: HashMap<ArgName, RefCell<Mock>>,
136}
137
138impl Default for MockCalls {
139 fn default() -> Self {
140 let mut m = MockCalls {
141 args: HashMap::new(),
142 };
143
144 for name in ArgName::iterator() {
145 m.args.insert(name, RefCell::new(Mock::default()));
146 }
147
148 m
149 }
150}
151
152impl MockCalls {
153 fn act(&self, name: ArgName, value: Box<dyn Any>) -> Result<()> {
154 if self.args.get(&name).unwrap().borrow().ret_err_times > 0 {
155 self.args.get(&name).unwrap().borrow_mut().ret_err_times -= 1;
156 if let Some(e) = &self.args.get(&name).unwrap().borrow().ret_err {
157 return e();
158 }
159 }
160
161 self.args
162 .get(&name)
163 .unwrap()
164 .borrow_mut()
165 .values
166 .push(value);
167 Ok(())
168 }
169
170 fn fetch(&self, name: ArgName) -> Ref<'_, Mock> {
171 self.args.get(&name).unwrap().borrow()
172 }
173
174 fn fetch_mut(&self, name: ArgName) -> RefMut<'_, Mock> {
175 self.args.get(&name).unwrap().borrow_mut()
176 }
177}
178
179#[derive(Default)]
180pub struct TestHelperSyscall {
181 mock_id: RefCell<MockId>,
182 mocks: MockCalls,
183}
184
185pub struct MockId {
186 uid: Uid,
187 gid: Gid,
188 euid: Uid,
189 egid: Gid,
190}
191
192impl Default for MockId {
193 fn default() -> Self {
194 Self {
195 uid: nix::unistd::getuid(),
196 gid: nix::unistd::getgid(),
197 euid: nix::unistd::geteuid(),
198 egid: nix::unistd::getegid(),
199 }
200 }
201}
202
203impl Syscall for TestHelperSyscall {
204 fn as_any(&self) -> &dyn Any {
205 self
206 }
207
208 fn pivot_rootfs(&self, _path: &Path) -> Result<()> {
209 unimplemented!()
210 }
211
212 fn set_ns(&self, rawfd: i32, nstype: CloneFlags) -> Result<()> {
213 self.mocks
214 .act(ArgName::Namespace, Box::new((rawfd, nstype)))
215 }
216
217 fn set_id(&self, _uid: Uid, _gid: Gid) -> Result<()> {
218 self.mock_id.borrow_mut().uid = _uid;
219 self.mock_id.borrow_mut().gid = _gid;
220 self.mock_id.borrow_mut().euid = _uid;
221 self.mock_id.borrow_mut().egid = _gid;
222 Ok(())
223 }
224
225 fn unshare(&self, flags: CloneFlags) -> Result<()> {
226 self.mocks.act(ArgName::Unshare, Box::new(flags))
227 }
228
229 fn set_capability(&self, cset: CapSet, value: &CapsHashSet) -> Result<()> {
230 self.mocks
231 .act(ArgName::Capability, Box::new((cset, value.clone())))
232 }
233
234 fn set_hostname(&self, hostname: &str) -> Result<()> {
235 self.mocks
236 .act(ArgName::Hostname, Box::new(hostname.to_owned()))
237 }
238
239 fn set_domainname(&self, domainname: &str) -> Result<()> {
240 self.mocks
241 .act(ArgName::Domainname, Box::new(domainname.to_owned()))
242 }
243
244 fn set_rlimit(&self, _rlimit: &PosixRlimit) -> Result<()> {
245 todo!()
246 }
247
248 fn get_pwuid(&self, _: u32) -> Option<Arc<OsStr>> {
249 Some(OsString::from("youki").into())
250 }
251
252 fn chroot(&self, _: &Path) -> Result<()> {
253 todo!()
254 }
255
256 fn mount(
257 &self,
258 source: Option<&Path>,
259 target: &Path,
260 fstype: Option<&str>,
261 flags: MsFlags,
262 data: Option<&str>,
263 ) -> Result<()> {
264 let target_owned = if target.starts_with(Path::new("/proc/self/fd")) {
266 std::fs::read_link(target).unwrap_or_else(|_| target.to_owned())
267 } else {
268 target.to_owned()
269 };
270
271 self.mocks.act(
272 ArgName::Mount,
273 Box::new(MountArgs {
274 source: source.map(|x| x.to_owned()),
275 target: target_owned,
276 fstype: fstype.map(|x| x.to_owned()),
277 flags,
278 data: data.map(|x| x.to_owned()),
279 }),
280 )
281 }
282
283 fn mount_from_fd(&self, source_fd: &OwnedFd, target: &Path) -> Result<()> {
284 self.mocks.act(
285 ArgName::MountFromFd,
286 Box::new(MountFromFdArgs {
287 fd: source_fd.as_raw_fd(),
288 target: target.to_owned(),
289 }),
290 )
291 }
292
293 fn move_mount(
294 &self,
295 from_dirfd: BorrowedFd<'_>,
296 from_path: Option<&str>,
297 to_dirfd: BorrowedFd<'_>,
298 to_path: Option<&str>,
299 flags: u32,
300 ) -> Result<()> {
301 let rec = MoveMountArgs {
302 from_dirfd: from_dirfd.as_raw_fd(),
303 from_path: from_path.map(OsString::from),
304 to_dirfd: to_dirfd.as_raw_fd(),
305 to_path: to_path.map(OsString::from),
306 flags,
307 };
308 self.mocks.act(ArgName::MoveMount, Box::new(rec))
309 }
310
311 fn fsopen(&self, _: Option<&str>, _: u32) -> Result<OwnedFd> {
312 todo!()
313 }
314
315 fn fsconfig(
316 &self,
317 _: BorrowedFd<'_>,
318 _: u32,
319 _: Option<&str>,
320 _: Option<&str>,
321 _: libc::c_int,
322 ) -> Result<()> {
323 todo!()
324 }
325
326 fn fsmount(&self, _: BorrowedFd<'_>, _: u32, _: Option<u64>) -> Result<OwnedFd> {
327 todo!()
328 }
329
330 fn open_tree(&self, _: RawFd, _: Option<&str>, _: u32) -> Result<OwnedFd> {
331 todo!()
332 }
333
334 fn symlink(&self, original: &Path, link: &Path) -> Result<()> {
335 self.mocks.act(
336 ArgName::Symlink,
337 Box::new((original.to_path_buf(), link.to_path_buf())),
338 )
339 }
340
341 fn mknod(&self, path: &Path, kind: SFlag, perm: Mode, dev: u64) -> Result<()> {
342 self.mocks.act(
343 ArgName::Mknod,
344 Box::new(MknodArgs {
345 path: path.to_path_buf(),
346 kind,
347 perm,
348 dev,
349 }),
350 )
351 }
352 fn chown(&self, path: &Path, owner: Option<Uid>, group: Option<Gid>) -> Result<()> {
353 self.mocks.act(
354 ArgName::Chown,
355 Box::new(ChownArgs {
356 path: path.to_path_buf(),
357 owner,
358 group,
359 }),
360 )
361 }
362
363 fn set_groups(&self, groups: &[Gid]) -> Result<()> {
364 self.mocks.act(ArgName::Groups, Box::new(groups.to_vec()))
365 }
366
367 fn close_range(&self, _: i32) -> Result<()> {
368 todo!()
369 }
370
371 fn mount_setattr(
372 &self,
373 _: BorrowedFd<'_>,
374 _: &Path,
375 _: u32,
376 _: &linux::MountAttr,
377 _: libc::size_t,
378 ) -> Result<()> {
379 todo!()
380 }
381
382 fn set_io_priority(&self, class: i64, priority: i64) -> Result<()> {
383 self.mocks.act(
384 ArgName::IoPriority,
385 Box::new(IoPriorityArgs { class, priority }),
386 )
387 }
388
389 fn set_mempolicy(&self, mode: i32, nodemask: &[libc::c_ulong], maxnode: u64) -> Result<()> {
390 self.mocks.act(
391 ArgName::MemPolicy,
392 Box::new(MemPolicyArgs {
393 mode,
394 nodemask: nodemask.to_vec(),
395 maxnode,
396 }),
397 )
398 }
399
400 fn umount2(&self, target: &Path, flags: MntFlags) -> Result<()> {
401 self.mocks.act(
402 ArgName::UMount2,
403 Box::new(UMount2Args {
404 target: target.to_owned(),
405 flags,
406 }),
407 )
408 }
409
410 fn get_uid(&self) -> Uid {
411 self.mock_id.borrow().uid
412 }
413
414 fn get_gid(&self) -> Gid {
415 self.mock_id.borrow().gid
416 }
417
418 fn get_euid(&self) -> Uid {
419 self.mock_id.borrow().euid
420 }
421
422 fn get_egid(&self) -> Gid {
423 self.mock_id.borrow().egid
424 }
425
426 fn personality(&self, _: PersonalityDomain) -> Result<()> {
427 todo!()
428 }
429}
430
431impl TestHelperSyscall {
432 pub fn set_ret_err(&self, name: ArgName, err: fn() -> Result<()>) {
433 self.mocks.fetch_mut(name).ret_err = Some(err);
434 self.set_ret_err_times(name, 1);
435 }
436
437 pub fn set_ret_err_times(&self, name: ArgName, times: usize) {
438 self.mocks.fetch_mut(name).ret_err_times = times;
439 }
440
441 pub fn get_setns_args(&self) -> Vec<(i32, CloneFlags)> {
442 self.mocks
443 .fetch(ArgName::Namespace)
444 .values
445 .iter()
446 .map(|x| *x.downcast_ref::<(i32, CloneFlags)>().unwrap())
447 .collect::<Vec<(i32, CloneFlags)>>()
448 }
449
450 pub fn get_unshare_args(&self) -> Vec<CloneFlags> {
451 self.mocks
452 .fetch(ArgName::Unshare)
453 .values
454 .iter()
455 .map(|x| *x.downcast_ref::<CloneFlags>().unwrap())
456 .collect::<Vec<CloneFlags>>()
457 }
458
459 pub fn get_set_capability_args(&self) -> Vec<(CapSet, CapsHashSet)> {
460 self.mocks
461 .fetch(ArgName::Capability)
462 .values
463 .iter()
464 .map(|x| x.downcast_ref::<(CapSet, CapsHashSet)>().unwrap().clone())
465 .collect::<Vec<(CapSet, CapsHashSet)>>()
466 }
467
468 pub fn get_mount_args(&self) -> Vec<MountArgs> {
469 self.mocks
470 .fetch(ArgName::Mount)
471 .values
472 .iter()
473 .map(|x| x.downcast_ref::<MountArgs>().unwrap().clone())
474 .collect::<Vec<MountArgs>>()
475 }
476
477 pub fn get_mount_from_fd_args(&self) -> Vec<MountFromFdArgs> {
478 self.mocks
479 .fetch(ArgName::MountFromFd)
480 .values
481 .iter()
482 .map(|x| x.downcast_ref::<MountFromFdArgs>().unwrap().clone())
483 .collect::<Vec<MountFromFdArgs>>()
484 }
485
486 pub fn get_symlink_args(&self) -> Vec<(PathBuf, PathBuf)> {
487 self.mocks
488 .fetch(ArgName::Symlink)
489 .values
490 .iter()
491 .map(|x| x.downcast_ref::<(PathBuf, PathBuf)>().unwrap().clone())
492 .collect::<Vec<(PathBuf, PathBuf)>>()
493 }
494
495 pub fn get_mknod_args(&self) -> Vec<MknodArgs> {
496 self.mocks
497 .fetch(ArgName::Mknod)
498 .values
499 .iter()
500 .map(|x| x.downcast_ref::<MknodArgs>().unwrap().clone())
501 .collect::<Vec<MknodArgs>>()
502 }
503
504 pub fn get_chown_args(&self) -> Vec<ChownArgs> {
505 self.mocks
506 .fetch(ArgName::Chown)
507 .values
508 .iter()
509 .map(|x| x.downcast_ref::<ChownArgs>().unwrap().clone())
510 .collect::<Vec<ChownArgs>>()
511 }
512
513 pub fn get_hostname_args(&self) -> Vec<String> {
514 self.mocks
515 .fetch(ArgName::Hostname)
516 .values
517 .iter()
518 .map(|x| x.downcast_ref::<String>().unwrap().clone())
519 .collect::<Vec<String>>()
520 }
521
522 pub fn get_domainname_args(&self) -> Vec<String> {
523 self.mocks
524 .fetch(ArgName::Domainname)
525 .values
526 .iter()
527 .map(|x| x.downcast_ref::<String>().unwrap().clone())
528 .collect::<Vec<String>>()
529 }
530
531 pub fn get_groups_args(&self) -> Vec<Gid> {
532 self.mocks
533 .fetch(ArgName::Groups)
534 .values
535 .iter()
536 .flat_map(|x| x.downcast_ref::<Vec<Gid>>().unwrap().clone())
537 .collect::<Vec<Gid>>()
538 }
539
540 pub fn get_io_priority_args(&self) -> Vec<IoPriorityArgs> {
541 self.mocks
542 .fetch(ArgName::IoPriority)
543 .values
544 .iter()
545 .map(|x| x.downcast_ref::<IoPriorityArgs>().unwrap().clone())
546 .collect::<Vec<IoPriorityArgs>>()
547 }
548
549 pub fn get_mempolicy_args(&self) -> Vec<MemPolicyArgs> {
550 self.mocks
551 .fetch(ArgName::MemPolicy)
552 .values
553 .iter()
554 .map(|x| x.downcast_ref::<MemPolicyArgs>().unwrap().clone())
555 .collect::<Vec<MemPolicyArgs>>()
556 }
557
558 pub fn get_umount_args(&self) -> Vec<UMount2Args> {
559 self.mocks
560 .fetch(ArgName::UMount2)
561 .values
562 .iter()
563 .map(|x| x.downcast_ref::<UMount2Args>().unwrap().clone())
564 .collect::<Vec<UMount2Args>>()
565 }
566}