RustForge

rust-kernel

Rustbelt: a hobby x86_64 kernel written in Rust

rust-kernel / user / inittest.c
4960 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
/* Stage 21: the "init" on the boot-contract-v2 test image (build.rs puts
 * the same file at /sbin/init, /bin/init and /bin/sh of the root
 * partition in target/rootfs-gpt.img).
 *
 * It looks at AT_EXECFN (the path the kernel started it from):
 *   /sbin/init -> checks what the contract promises (below), then exits
 *                 with the number in /etc/init-status (0 if missing), or
 *                 1 if a check failed;
 *   /bin/init  -> exits 21;   /bin/sh -> exits 22
 * so the tests can tell which one of the fallback chain ran.
 *
 * The checks: pid 1, argv[0] "init", PATH, "/" is the ext2 root, the
 * kernel's mounts are still there, /mnt/share (the exFAT partition) is
 * readable and writable, and msync's argument checks.
 * Also runs on a normal Linux host (where it isn't pid 1).
 */
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

static int failed;

static void check(int ok, const char *what) {
    printf("[inittest] %s: %s\n", ok ? "ok" : "FAILED", what);
    if (!ok)
        failed++;
}

static int read_file(const char *path, char *buf, size_t len) {
    int fd = open(path, O_RDONLY);
    if (fd < 0)
        return -1;
    int n = read(fd, buf, len - 1);
    close(fd);
    if (n >= 0)
        buf[n] = 0;
    return n;
}

static int is_dir(const char *path) {
    struct stat st;
    return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
}

static void contract(int argc, char **argv) {
    const char *path = getenv("PATH");
    const char *headless = getenv("RUSTBELT_HEADLESS");
    printf("[inittest] pid %d, argc %d, argv[0] %s, argv[1] %s, PATH=%s, RUSTBELT_HEADLESS=%s\n", getpid(), argc,
           argv[0], argc > 1 ? argv[1] : "-", path ? path : "-", headless ? headless : "-");
    check(getpid() == 1, "I am pid 1");
    check(strcmp(argv[0], "init") == 0, "argv[0] is \"init\"");
    check(!headless || (argc > 1 && strcmp(argv[1], "selftest") == 0), "headless: argv[1] is \"selftest\"");
    check(path && strcmp(path, "/sbin:/bin:/usr/bin:/mnt/disk0/bin") == 0, "PATH=/sbin:/bin:/usr/bin:/mnt/disk0/bin");

    char buf[256];
    check(read_file("/etc/rootfs-v2", buf, sizeof buf) > 0 && strstr(buf, "ext2 root"), "/ is the ext2 root partition");
    check(is_dir("/tmp") && is_dir("/dev") && is_dir("/proc") && is_dir("/mnt/disk0"), "/tmp /dev /proc /mnt/disk0 are there");
    int fd = open("/tmp/inittest.tmp", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    check(fd >= 0 && write(fd, "x", 1) == 1, "/tmp is writable");
    close(fd);
    unlink("/tmp/inittest.tmp");

    /* The exFAT share. */
    check(read_file("/mnt/share/hello.txt", buf, sizeof buf) > 0 && strcmp(buf, "Hello from the exFAT share!\n") == 0,
          "/mnt/share/hello.txt reads back");
    fd = open("/mnt/share/from-init.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    check(fd >= 0 && write(fd, "written by init\n", 16) == 16, "/mnt/share is writable");
    close(fd);
    check(read_file("/mnt/share/from-init.txt", buf, sizeof buf) == 16 && strcmp(buf, "written by init\n") == 0,
          "...and the new file reads back");

    /* msync (26): Rustbelt has nothing to write back, but checks its
     * arguments like Linux. */
    long pg = 4096;
    char *anon = mmap(0, 2 * pg, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    check(anon != MAP_FAILED, "mmap MAP_SHARED|MAP_ANONYMOUS");
    anon[0] = 1;
    check(msync(anon, 2 * pg, MS_SYNC) == 0, "msync(MS_SYNC) = 0");
    check(msync(anon, 1, MS_ASYNC | MS_INVALIDATE) == 0, "msync(MS_ASYNC|MS_INVALIDATE), partial page = 0");
    check(msync(anon, 0, MS_SYNC) == 0, "msync(len 0) = 0");
    check(msync(anon + 1, pg, MS_SYNC) == -1 && errno == EINVAL, "msync(unaligned) = EINVAL");
    check(msync(anon, pg, 8) == -1 && errno == EINVAL, "msync(bad flags) = EINVAL");
    check(msync(anon, pg, MS_SYNC | MS_ASYNC) == -1 && errno == EINVAL, "msync(MS_SYNC|MS_ASYNC) = EINVAL");
    munmap(anon + pg, pg);
    check(msync(anon, 2 * pg, MS_SYNC) == -1 && errno == ENOMEM, "msync over an unmapped page = ENOMEM");
    munmap(anon, pg);
    check(msync(anon, pg, MS_SYNC) == -1 && errno == ENOMEM, "msync(unmapped) = ENOMEM");
    char *priv = mmap(0, pg, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    check(priv != MAP_FAILED && msync(priv, pg, MS_ASYNC) == 0, "msync(private mapping) = 0");
}

int main(int argc, char **argv) {
    const char *execfn = (const char *)getauxval(AT_EXECFN);
    printf("[inittest] started as %s\n", execfn ? execfn : "?");
    if (execfn && strcmp(execfn, "/bin/init") == 0)
        return 21;
    if (execfn && strcmp(execfn, "/bin/sh") == 0)
        return 22;
    contract(argc, argv);
    char buf[16];
    int status = read_file("/etc/init-status", buf, sizeof buf) > 0 ? atoi(buf) : 0;
    printf("[inittest] %d failed\n", failed);
    fflush(stdout);
    return failed ? 1 : status;
}