foundry
Foundry OS: the operating system built on the Rustbelt kernel
6587 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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | //! Stage 6 demo: this program was itself loaded from the FAT32 disk.
//! It exercises the syscalls the shell (next stage) will need: cwd, reading
//! a file on the FAT disk, stat, lseek, getdents64, mkdir/unlink/rmdir,
//! pipes, and spawn + wait. Exit code = number of failed checks.
#![no_std]
#![no_main]
#[macro_use]
#[path = "ulib.rs"]
mod ulib;
use ulib::*;
static mut FAILURES: i64 = 0;
fn check(ok: bool, what: &str) {
if !ok {
println!("[demo] CHECK FAILED: {}", what);
unsafe { FAILURES += 1 };
}
}
fn cwd_is(expected: &str) -> bool {
let mut buf = [0u8; 64];
let n = getcwd(&mut buf);
n >= 0 && &buf[..n as usize] == expected.as_bytes()
}
fn main() -> i64 {
println!("[demo] pid {} started from /mnt/disk0/bin/demo", getpid());
// --- current directory --------------------------------------------------
check(cwd_is("/"), "cwd starts at /");
check(chdir("/mnt/disk0") == 0, "chdir /mnt/disk0");
check(cwd_is("/mnt/disk0"), "getcwd after chdir");
// --- read a file from the FAT disk (relative path!) ---------------------
let fd = open("readme.txt", 0);
check(fd >= 3, "open readme.txt");
let mut buf = [0u8; 256];
let n = read(fd, &mut buf).max(0) as usize;
let text = core::str::from_utf8(&buf[..n]).unwrap_or("?");
print!("[demo] readme.txt from FAT32: {}", text.lines().next().unwrap_or(""));
println!();
check(text.starts_with("Hello from the FAT32 disk!"), "readme.txt contents");
// --- stat / fstat / lseek ------------------------------------------------
let mut st = Stat::default();
check(stat("readme.txt", &mut st) == 0 && st.kind == KIND_FILE && st.size == n as u64, "stat readme.txt");
check(fstat(1, &mut st) == 0 && st.kind == KIND_CONSOLE, "fstat stdout = console");
check(stat("bin", &mut st) == 0 && st.kind == KIND_DIR, "stat bin is a directory");
check(lseek(fd, 6, SEEK_SET) == 6, "lseek SEEK_SET");
let mut word = [0u8; 4];
check(read(fd, &mut word) == 4 && &word == b"from", "read after lseek");
check(lseek(fd, 0, SEEK_END) == n as i64, "lseek SEEK_END = size");
check(lseek(1, 0, SEEK_SET) == -ESPIPE, "can't seek the console");
close(fd);
// --- list a directory with getdents64 -----------------------------------
let dir = open("bin", 0);
let mut dents = [0u8; 512];
let len = getdents64(dir, &mut dents);
check(len > 0, "getdents64 bin");
let (mut saw_demo, mut saw_child) = (false, false);
print!("[demo] /mnt/disk0/bin:");
for_each_dirent(&dents, len.max(0) as usize, |name, d_type| {
print!(" {}{}", name, if d_type == 4 { "/" } else { "" });
saw_demo |= name == "demo";
saw_child |= name == "child";
});
println!();
check(saw_demo && saw_child, "bin contains demo and child");
check(getdents64(dir, &mut dents) == 0, "getdents64 at end returns 0");
close(dir);
// --- relative paths, "..", errors ----------------------------------------
check(chdir("notes") == 0 && cwd_is("/mnt/disk0/notes"), "chdir notes");
check(chdir("../..") == 0 && cwd_is("/mnt"), "chdir ../..");
check(chdir("disk0/readme.txt") == -ENOTDIR, "chdir to a file -> ENOTDIR");
check(chdir("/nope") == -ENOENT, "chdir to nothing -> ENOENT");
// --- create and delete (ramfs /tmp and the FAT disk) ---------------------
for base in ["/tmp", "/mnt/disk0/notes"] {
let mut path_buf = [0u8; 64];
let dir = join(&mut path_buf, base, "/newdir");
check(mkdir(dir) == 0, "mkdir");
check(mkdir(dir) == -EEXIST, "mkdir again -> EEXIST");
let mut file_buf = [0u8; 64];
let file = join(&mut file_buf, dir, "/f.txt");
let fd = open(file, O_CREAT);
check(write(fd, b"bytes") == 5, "write new file");
close(fd);
check(rmdir(dir) == -ENOTEMPTY, "rmdir non-empty -> ENOTEMPTY");
check(unlink(dir) == -EISDIR, "unlink a directory -> EISDIR");
check(unlink(file) == 0, "unlink file");
check(rmdir(dir) == 0, "rmdir empty dir");
check(stat(dir, &mut st) == -ENOENT, "gone after rmdir");
}
check(rmdir("/tmp") == -EBUSY, "rmdir a mount point -> EBUSY");
println!("[demo] mkdir/unlink/rmdir work on ramfs and FAT32");
// --- spawn a child with its stdout connected to a pipe -------------------
chdir("/mnt/disk0");
let (r, w) = pipe().unwrap_or((-1, -1));
let opts = SpawnOptions { stdout: w, ..SpawnOptions::INHERIT };
let pid = spawn("bin/child", "child\0pipe", &opts);
check(pid > 0, "spawn child (pipe)");
close(w); // only the child holds the write end now: EOF when it exits
let mut got = [0u8; 128];
let mut total = 0;
loop {
let n = read(r, &mut got[total..]); // blocks until data or EOF
if n <= 0 {
break;
}
total += n as usize;
}
close(r);
let line = core::str::from_utf8(&got[..total]).unwrap_or("?").trim_end();
println!("[demo] read from pipe: {:?}", line);
check(line.starts_with("hello through a pipe"), "pipe contents");
check(wait(pid) == 0, "child (pipe) exit code 0");
// --- spawn + wait: exit codes ---------------------------------------------
let pid = spawn("bin/child", "child\0with\0three args", &SpawnOptions::INHERIT);
check(wait(pid) == 0, "child with args exits 0");
let pid = spawn("bin/child", "child\042", &SpawnOptions::INHERIT);
let code = wait(pid);
println!("[demo] spawned child 42 (pid {}), wait -> exit code {}", pid, code);
check(code == 42, "wait returns the exit code");
check(wait(pid) == -ECHILD, "second wait -> ECHILD");
let pid = spawn("/mnt/disk0/bin/evil", "", &SpawnOptions::INHERIT);
check(wait(pid) == 139, "killed child -> 139");
check(spawn("bin/nope", "", &SpawnOptions::INHERIT) == -ENOENT, "spawn missing -> ENOENT");
check(spawn("readme.txt", "", &SpawnOptions::INHERIT) == -8, "spawn non-ELF -> ENOEXEC");
check(wait(1) == -ECHILD, "wait for a non-child -> ECHILD");
sleep_ms(100); // blocks in the kernel (no busy loop)
let failures = unsafe { FAILURES };
if failures == 0 {
println!("[demo] all stage 6 checks passed, exiting with 0");
}
failures
}
/// Concatenate two strings into `buf` (no heap in user programs).
fn join<'a>(buf: &'a mut [u8], a: &str, b: &str) -> &'a str {
buf[..a.len()].copy_from_slice(a.as_bytes());
buf[a.len()..a.len() + b.len()].copy_from_slice(b.as_bytes());
core::str::from_utf8(&buf[..a.len() + b.len()]).unwrap()
}
|