RustForge

rust-kernel

Rustbelt: a hobby x86_64 kernel written in Rust

rust-kernel / tests / coreutils_batch2.rs
4353 bytes ยท raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! 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)
}