//! Stage 17, "coreutils batch 2": links and symlinks, owners, statfs and //! statx, sysinfo/times/getrusage and the CPU clocks, socket names and //! options, alarm/setitimer/POSIX timers, user and group ids, priority, //! the host name, flock, preadv/pwritev and sendfile. The detailed checks //! live in user/coretest.rs (exit code = failed checks); this file runs //! each group and checks nothing is left behind -- threads, processes, //! memory frames, file locks (flock ones too) and timers. #![no_std] #![no_main] #![feature(custom_test_frameworks)] #![test_runner(rustbelt::test_runner)] #![reexport_test_harness_main = "test_main"] extern crate alloc; use alloc::string::String; use bootloader::{BootInfo, entry_point}; use core::panic::PanicInfo; use rustbelt::process::{self, ExitStatus, SpawnArgs}; use rustbelt::{fs, memory, syscall, thread, timer}; entry_point!(main); fn main(boot_info: &'static BootInfo) -> ! { rustbelt::init(); rustbelt::init_memory(boot_info); rustbelt::init_smp(); // all 4 CPUs run threads fs::init(); rustbelt::init_storage(); rustbelt::init_net(); // 127.0.0.1 for the socket checks test_main(); rustbelt::hlt_loop(); } fn run(args: &[&str]) -> ExitStatus { let mut spawn = SpawnArgs::kernel("coretest"); spawn.args.extend(args.iter().map(|a| String::from(*a))); let p = process::spawn_path("/mnt/disk0/bin/coretest", spawn).expect("spawn failed"); process::wait(&p) } fn counts() -> (usize, usize, usize) { thread::sleep_ticks(3); // let exited threads' stacks be freed (thread::thread_count(), process::count(), memory::frames_in_use()) } /// Run one group twice (the first warms caches), check it passes and /// nothing is left behind. fn run_without_leaks(group: &str) { assert_eq!(run(&[group]), ExitStatus::Exited(0), "coretest {} failed (warm-up)", group); let before = counts(); assert_eq!(run(&[group]), ExitStatus::Exited(0), "coretest {} failed", group); assert_eq!(counts(), before, "coretest {} leaked something", group); assert_eq!(fs::lock::count(), 0, "coretest {} left file locks behind", group); assert_eq!(timer::count(), 0, "coretest {} left timers behind", group); } /// link/linkat, symlink/symlinkat on ramfs and ext2 (st_nlink, st_ino, /// rename over a second link, directories moved); EPERM on FAT/exFAT, /// EROFS on btrfs, EXDEV across file systems. #[test_case] fn links_and_symlinks() { run_without_leaks("links"); } /// chown/fchown/lchown/fchownat: stored on ramfs and ext2; FAT says EPERM /// unless nothing changes. #[test_case] fn owners() { run_without_leaks("owners"); } /// statfs/fstatfs magic numbers and sizes on every file system, statx, /// st_dev per mount. #[test_case] fn statfs_and_statx() { run_without_leaks("statfs"); } /// sysinfo (99) next to our 502, times(), getrusage, CPU-time clocks, /// clock_getres. #[test_case] fn sysinfo_times_and_cpu_clocks() { run_without_leaks("info"); } /// getsockname/getpeername, SO_REUSEADDR & co., SO_TYPE/SO_ERROR, /// shutdown(SHUT_WR) = end-of-file for the peer, socketpair says no. #[test_case] fn socket_names_options_and_shutdown() { run_without_leaks("sockets"); } /// alarm, setitimer/getitimer (one-shot, periodic), POSIX timers /// (SI_TIMER + si_value, TIMER_ABSTIME, SIGEV_NONE), gone with the process. #[test_case] fn alarms_and_timers() { run_without_leaks("timers"); } /// setuid & co. (root drops privileges, the saved id), groups, files owned /// by their creator, nice values, sethostname -> uname. #[test_case] fn ids_priority_and_hostname() { run_without_leaks("ids"); } /// flock (per open file, blocking, released on the last close), /// preadv/pwritev, sendfile, ppoll/pselect6 mask sizes and masks, and /// getdents64 while deleting (rm -r). #[test_case] fn flock_vectors_sendfile_and_masks() { run_without_leaks("misc"); } /// `--trace` knows the new calls by name. #[test_case] fn trace_names_new_syscalls() { assert_eq!(syscall::trace_name(86), "link"); assert_eq!(syscall::trace_name(332), "statx"); assert_eq!(syscall::trace_name(222), "timer_create"); assert_eq!(syscall::trace_name(99), "sysinfo"); assert_eq!(syscall::trace_name(502), "rb_sysinfo"); } #[panic_handler] fn panic(info: &PanicInfo) -> ! { rustbelt::test_panic_handler(info) }