rust-kernel
Rustbelt: a hobby x86_64 kernel written in Rust
5506 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 | //! FAT32 through the VFS: the partition on target/disk.img is mounted at
//! /mnt/disk0 (writes are fine: QEMU uses snapshot=on).
#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![test_runner(rust_kernel::test_runner)]
#![reexport_test_harness_main = "test_main"]
extern crate alloc;
use alloc::{string::String, sync::Arc, vec, vec::Vec};
use bootloader::{BootInfo, entry_point};
use core::panic::PanicInfo;
use fatfs::{Read, Seek, SeekFrom, Write};
use rust_kernel::block::{BlockDevice, RamDisk, SECTOR_SIZE};
use rust_kernel::errno::Errno;
use rust_kernel::fs::fat::BlockIo;
use rust_kernel::fs::file::{File, InodeFile};
use rust_kernel::fs::{self, InodeKind};
use rust_kernel::elf;
entry_point!(main);
fn main(boot_info: &'static BootInfo) -> ! {
rust_kernel::init();
rust_kernel::init_memory(boot_info);
fs::init();
rust_kernel::init_storage();
test_main();
rust_kernel::hlt_loop();
}
#[test_case]
fn fat_volume_is_mounted() {
assert!(fs::mounts().iter().any(|(path, kind)| path == "/mnt/disk0" && *kind == "fat32"));
let mut names: Vec<String> = fs::lookup("/mnt/disk0").unwrap().list().unwrap().into_iter().map(|e| e.name).collect();
names.sort(); // FAT keeps directory entries in creation order
assert_eq!(names, vec!["bin", "notes", "readme.txt", "scripts"]);
}
#[test_case]
fn read_text_file() {
let file = InodeFile::open(fs::lookup("/mnt/disk0/readme.txt").unwrap()).unwrap();
let mut buf = [0u8; 256];
let n = file.read(&mut buf).unwrap();
assert!(buf[..n].starts_with(b"Hello from the FAT32 disk!"));
assert_eq!(n as u64, file.stat().unwrap().size);
assert_eq!(file.read(&mut buf), Ok(0));
// FAT is case-insensitive; subdirectories work.
assert!(fs::lookup("/mnt/disk0/README.TXT").is_ok());
assert_eq!(fs::lookup("/mnt/disk0/notes/todo.txt").unwrap().kind(), InodeKind::File);
assert_eq!(fs::lookup("/mnt/disk0/nope").err(), Some(Errno::ENOENT));
assert_eq!(fs::lookup("/mnt/disk0/readme.txt/x").err(), Some(Errno::ENOTDIR));
}
#[test_case]
fn programs_on_disk_are_valid_elf_files() {
let bin = fs::lookup("/mnt/disk0/bin").unwrap();
for entry in bin.list().unwrap() {
let path = alloc::format!("/mnt/disk0/bin/{}", entry.name);
let image = fs::read_all(&path).unwrap();
assert_eq!(image.len(), fs::lookup(&path).unwrap().size());
assert!(elf::check(&image).is_ok(), "{} is not a valid program", entry.name);
}
}
#[test_case]
fn create_write_read_remove() {
// 10 000 bytes = 20 clusters (1 sector each): exercises the FAT chain.
let data: Vec<u8> = (0..10_000u32).map(|i| (i * 7 % 256) as u8).collect();
let inode = fs::create("/mnt/disk0/notes/big.bin", InodeKind::File).unwrap();
assert_eq!(inode.write_at(0, &data), Ok(data.len()));
let mut back = vec![0u8; 12_000];
assert_eq!(inode.read_at(0, &mut back), Ok(10_000));
assert_eq!(&back[..10_000], &data[..]);
// Overwrite in the middle, read across a sector boundary.
inode.write_at(510, b"XYZW").unwrap();
let mut four = [0u8; 4];
inode.read_at(510, &mut four).unwrap();
assert_eq!(&four, b"XYZW");
// Writing past the end fills the gap with zeros.
inode.write_at(10_010, b"!").unwrap();
assert_eq!(inode.size(), 10_011);
inode.read_at(10_000, &mut four).unwrap();
assert_eq!(four, [0; 4]);
// A fresh lookup sees the same data (it really went to the disk).
assert_eq!(fs::lookup("/mnt/disk0/notes/big.bin").unwrap().size(), 10_011);
inode.truncate().unwrap();
assert_eq!(inode.size(), 0);
fs::remove("/mnt/disk0/notes/big.bin", false).unwrap();
assert_eq!(fs::lookup("/mnt/disk0/notes/big.bin").err(), Some(Errno::ENOENT));
}
#[test_case]
fn directories_on_fat() {
fs::create("/mnt/disk0/newdir", InodeKind::Directory).unwrap();
assert_eq!(fs::create("/mnt/disk0/newdir", InodeKind::Directory).err(), Some(Errno::EEXIST));
fs::create("/mnt/disk0/newdir/a long file name.txt", InodeKind::File).unwrap();
let list = fs::lookup("/mnt/disk0/newdir").unwrap().list().unwrap();
assert_eq!(list.len(), 1);
assert_eq!(list[0].name, "a long file name.txt"); // long file names (VFAT)
assert_eq!(fs::remove("/mnt/disk0/newdir", true), Err(Errno::ENOTEMPTY));
fs::remove("/mnt/disk0/newdir/a long file name.txt", false).unwrap();
fs::remove("/mnt/disk0/newdir", true).unwrap();
assert_eq!(fs::remove("/mnt/disk0", true), Err(Errno::EBUSY)); // mount point
}
#[test_case]
fn block_io_adapter_handles_unaligned_access() {
// The byte-stream adapter fatfs uses, on a RAM disk.
let disk: Arc<dyn BlockDevice> = Arc::new(RamDisk::new(4));
let mut io = BlockIo::new(disk.clone());
io.seek(SeekFrom::Start(508)).unwrap();
io.write_all(b"spans two sectors").unwrap(); // bytes 508..525
let mut sector = [0u8; SECTOR_SIZE];
disk.read_sectors(1, &mut sector).unwrap();
assert_eq!(§or[..13], b"s two sectors"); // "span" went to sector 0
disk.read_sectors(0, &mut sector).unwrap();
assert_eq!(§or[508..], b"span");
io.seek(SeekFrom::Start(508)).unwrap();
let mut back = [0u8; 17];
io.read_exact(&mut back).unwrap();
assert_eq!(&back, b"spans two sectors");
assert_eq!(io.seek(SeekFrom::End(0)).unwrap(), 4 * SECTOR_SIZE as u64);
assert!(io.seek(SeekFrom::End(1)).is_err()); // past the end of the disk
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
rust_kernel::test_panic_handler(info)
}
|