From d46c965d98a12d08eb23313948410c4622e03cd8 Mon Sep 17 00:00:00 2001 From: Amber-Agarwal Date: Sat, 14 Feb 2026 17:50:22 +0530 Subject: [PATCH 1/5] implement inode read path and welcome.txt loading from fs image --- Makefile | 24 +- rustc-ice-2026-02-14T12_17_44-2018.txt | 50 ++++ src/Makefile | 130 ++++++++++ src/bio.rs | 148 ++++++++++++ src/buf.rs | 39 +++ src/console.rs | 64 ++++- src/constants.rs | 2 +- src/fs.rs | 316 +++++++++++++++++++++++++ src/ide.rs | 170 +++++++++++++ src/lib.rs | 67 +++++- src/param.rs | 2 + src/traps.rs | 15 +- src/uart.rs | 28 ++- src/x86.rs | 33 +++ welcome.txt | 7 + 15 files changed, 1074 insertions(+), 21 deletions(-) create mode 100644 rustc-ice-2026-02-14T12_17_44-2018.txt create mode 100644 src/Makefile create mode 100644 src/bio.rs create mode 100644 src/buf.rs create mode 100644 src/fs.rs create mode 100644 src/ide.rs create mode 100644 welcome.txt diff --git a/Makefile b/Makefile index fa7b33b..0df672e 100644 --- a/Makefile +++ b/Makefile @@ -69,16 +69,22 @@ xv6.img: bootblock kernel dd if=bootblock of=xv6.img conv=notrunc dd if=kernel of=xv6.img seek=1 conv=notrunc +mkfs: ../col331/mkfs.c ../col331/fs.h ../col331/types.h ../col331/stat.h ../col331/param.h + gcc -Werror -Wall -o mkfs ../col331/mkfs.c + +fs.img: mkfs welcome.txt + ./mkfs fs.img welcome.txt + bootblock: bootasm.S bootmain.c $(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c $(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S $(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o $(OBJDUMP) -S -D bootblock.o > bootblock.asm $(OBJCOPY) -S -O binary -j .text bootblock.o bootblock - ./sign.pl bootblock + perl sign.pl bootblock kernel.a: $(RS) - cargo rustc -Z build-std=core -Z build-std-features=compiler-builtins-mem --target ./targets/i686.json --lib --release -- -A warnings --emit link=kernel.a + cargo rustc -Z build-std=core -Z build-std-features=compiler-builtins-mem -Z json-target-spec --target ./targets/i686.json --lib --release -- -A warnings --emit link=kernel.a kernel: kernel.a $(OBJS) ./linkers/kernel.ld ld -m elf_i386 -T ./linkers/kernel.ld -o kernel $(OBJS) kernel.a @@ -100,12 +106,12 @@ vectors.S: vectors.pl clean: rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ - *.a *.o *.d *.asm *.sym bootblock kernel xv6.img .gdbinit vectors.S - rm -r target + *.a *.o *.d *.asm *.sym bootblock kernel xv6.img fs.img .gdbinit vectors.S mkfs + rm -rf target # run in emulators # try to generate a unique GDB port -GDBPORT = $(shell expr `id -u` % 5000 + 25000) +GDBPORT = $(shell expr `id -u` % 5000 + 25001) # QEMU's gdb stub command line changed in 0.11 QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ then echo "-gdb tcp::$(GDBPORT)"; \ @@ -116,14 +122,14 @@ endif # For debugging # QEMUEXTRA = -no-reboot -d int,cpu_reset -QEMUOPTS = -drive file=xv6.img,index=0,media=disk,format=raw -smp $(CPUS) -m 512 $(QEMUEXTRA) +QEMUOPTS = -drive file=xv6.img,index=0,media=disk,format=raw -drive file=fs.img,index=1,media=disk,format=raw -smp $(CPUS) -m 512 $(QEMUEXTRA) -qemu: xv6.img +qemu: xv6.img fs.img $(QEMU) -nographic $(QEMUOPTS) .gdbinit: .gdbinit.tmpl sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ -qemu-gdb: xv6.img .gdbinit +qemu-gdb: xv6.img .gdbinit fs.img @echo "*** Now run 'gdb'." 1>&2 - $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) \ No newline at end of file + $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) diff --git a/rustc-ice-2026-02-14T12_17_44-2018.txt b/rustc-ice-2026-02-14T12_17_44-2018.txt new file mode 100644 index 0000000..6569374 --- /dev/null +++ b/rustc-ice-2026-02-14T12_17_44-2018.txt @@ -0,0 +1,50 @@ +thread 'rustc' panicked at /rustc-dev/7057231bd78d6c7893f905ea1832365d4c5efe17/library/core/src/slice/index.rs:1027:55: +slice index starts at 54 but ends at 52 +stack backtrace: + 0: 0x72342475904b - ::create + 1: 0x723424758f95 - ::force_capture + 2: 0x7234237516d8 - std[bef1e3e6ef206a1]::panicking::update_hook::>::{closure#0} + 3: 0x72342476bb32 - std[bef1e3e6ef206a1]::panicking::panic_with_hook + 4: 0x72342474e558 - std[bef1e3e6ef206a1]::panicking::panic_handler::{closure#0} + 5: 0x723424742ac9 - std[bef1e3e6ef206a1]::sys::backtrace::__rust_end_short_backtrace:: + 6: 0x72342474ffbd - __rustc[7b32525fab805468]::rust_begin_unwind + 7: 0x723421694bac - core[14f954927b42fbe0]::panicking::panic_fmt + 8: 0x72342272a94d - core[14f954927b42fbe0]::slice::index::slice_index_fail + 9: 0x723423089065 - ::replace + 10: 0x72342612fab5 - annotate_snippets[f15529b311271195]::renderer::render::render + 11: 0x72342634881f - ::emit_messages_default + 12: 0x723426340feb - ::emit_diagnostic + 13: 0x72342633fe94 - ::from_errors_diagnostic + 14: 0x72342633f61d - ::emit_diagnostic + 15: 0x723426367b30 - ::emit_diagnostic::{closure#3} + 16: 0x723426365aa6 - rustc_interface[5d23d87c33b7100c]::callbacks::track_diagnostic::> + 17: 0x723426364b2e - ::emit_diagnostic + 18: 0x7234263649e1 - ::emit_diagnostic + 19: 0x723421291e8c - ::emit_producing_guarantee + 20: 0x7234216efa86 - rustc_borrowck[ab19dfe50827342]::borrowck_check_region_constraints + 21: 0x723425ea9eb3 - ::do_mir_borrowck + 22: 0x723425ea472f - rustc_borrowck[ab19dfe50827342]::mir_borrowck + 23: 0x723425ea4579 - rustc_query_impl[19f3565069731b2f]::query_impl::mir_borrowck::compute_fn::__rust_begin_short_backtrace + 24: 0x72342574507e - rustc_query_impl[19f3565069731b2f]::execution::try_execute_query::, rustc_query_system[50dced63bea56a4a]::dep_graph::graph::DepNodeIndex>, {rustc_query_impl[19f3565069731b2f]::QueryFlags { is_anon: false, is_depth_limit: false, is_feedable: false }}, false> + 25: 0x723425743ba3 - rustc_query_impl[19f3565069731b2f]::query_impl::mir_borrowck::get_query_non_incr::__rust_end_short_backtrace + 26: 0x723425743d65 - ::par_hir_body_owners::::{closure#0} + 27: 0x723425740c54 - rustc_interface[5d23d87c33b7100c]::passes::analysis + 28: 0x7234260504a6 - rustc_query_impl[19f3565069731b2f]::execution::try_execute_query::>, {rustc_query_impl[19f3565069731b2f]::QueryFlags { is_anon: false, is_depth_limit: false, is_feedable: false }}, false> + 29: 0x72342605027c - rustc_query_impl[19f3565069731b2f]::query_impl::analysis::get_query_non_incr::__rust_end_short_backtrace + 30: 0x7234261f6be7 - , rustc_driver_impl[f9ad7bf1bed37554]::run_compiler::{closure#0}::{closure#2}>::{closure#2} as core[14f954927b42fbe0]::ops::function::FnOnce<(&rustc_session[2f7fc1337f473eb7]::session::Session, rustc_middle[8b4cd4281f9facd9]::ty::context::CurrentGcx, alloc[97538fc0341e4f07]::sync::Arc, &std[bef1e3e6ef206a1]::sync::once_lock::OnceLock, &rustc_data_structures[5b3f8bc5aac74be7]::sync::worker_local::WorkerLocal, &rustc_data_structures[5b3f8bc5aac74be7]::sync::worker_local::WorkerLocal, rustc_driver_impl[f9ad7bf1bed37554]::run_compiler::{closure#0}::{closure#2})>>::call_once::{shim:vtable#0} + 31: 0x72342602e0b7 - rustc_interface[5d23d87c33b7100c]::interface::run_compiler::<(), rustc_driver_impl[f9ad7bf1bed37554]::run_compiler::{closure#0}>::{closure#1} + 32: 0x723426016dbe - std[bef1e3e6ef206a1]::sys::backtrace::__rust_begin_short_backtrace::::{closure#1}, ()>::{closure#0}, ()>::{closure#0}::{closure#0}, ()> + 33: 0x7234260172e0 - ::{closure#1}, ()>::{closure#0}, ()>::{closure#0}::{closure#0}, ()>::{closure#1} as core[14f954927b42fbe0]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0} + 34: 0x72342601816c - ::new::thread_start + 35: 0x72341f89caa4 - + 36: 0x72341f929c6c - + 37: 0x0 - + + +rustc version: 1.95.0-nightly (7057231bd 2026-02-11) +platform: x86_64-unknown-linux-gnu + +query stack during panic: +#0 [mir_borrowck] borrow-checking `welcome` +#1 [analysis] running analysis passes on crate `kernel` +end of query stack diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..079b0db --- /dev/null +++ b/src/Makefile @@ -0,0 +1,130 @@ +OBJS = entry.o vectors.o trapasm.o +RS = src/*.rs + +# Cross-compiling (e.g., on Mac OS X) +#TOOLPREFIX = i386-jos-elf +#TOOLPREFIX = i386-elf- + +# Using native tools (e.g., on X86 Linux) +#TOOLPREFIX = + +# Try to infer the correct TOOLPREFIX if not set +ifndef TOOLPREFIX +TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \ + then echo 'i386-jos-elf-'; \ + elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \ + then echo ''; \ + else echo "***" 1>&2; \ + echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \ + echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \ + echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \ + echo "*** prefix other than 'i386-jos-elf-', set your TOOLPREFIX" 1>&2; \ + echo "*** environment variable to that prefix and run 'make' again." 1>&2; \ + echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ + echo "***" 1>&2; exit 1; fi) +endif + +# If the makefile can't find QEMU, specify its path here +# QEMU = qemu-system-i386 + +# Try to infer the correct QEMU +ifndef QEMU +QEMU = $(shell if which qemu > /dev/null; \ + then echo qemu; exit; \ + elif which qemu-system-i386 > /dev/null; \ + then echo qemu-system-i386; exit; \ + elif which qemu-system-x86_64 > /dev/null; \ + then echo qemu-system-x86_64; exit; \ + else \ + qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \ + if test -x $$qemu; then echo $$qemu; exit; fi; fi; \ + echo "***" 1>&2; \ + echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \ + echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \ + echo "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; \ + echo "***" 1>&2; exit 1) +endif + +CC = $(TOOLPREFIX)gcc +AS = $(TOOLPREFIX)gas +LD = $(TOOLPREFIX)ld +OBJCOPY = $(TOOLPREFIX)objcopy +OBJDUMP = $(TOOLPREFIX)objdump +CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer +CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) +ASFLAGS = -m32 -gdwarf-2 -Wa,-divide +LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null | head -n 1) + +# Disable PIE when possible (for Ubuntu 16.10 toolchain) +ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),) +CFLAGS += -fno-pie -no-pie +endif +ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie'),) +CFLAGS += -fno-pie -nopie +endif + +# Disk image with bootblock + kernel +xv6.img: bootblock kernel + dd if=/dev/zero of=xv6.img count=10000 + dd if=bootblock of=xv6.img conv=notrunc + dd if=kernel of=xv6.img seek=1 conv=notrunc + +mkfs: ../../col331/mkfs.c ../../col331/fs.h ../../col331/types.h ../../col331/stat.h ../../col331/param.h + gcc -Werror -Wall -o mkfs ../../col331/mkfs.c + +fs: fs.img + +fs.img: mkfs ../welcome.txt + ./mkfs fs.img ../welcome.txt + +bootblock: bootasm.S bootmain.c + $(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c + $(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S + $(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o + $(OBJDUMP) -S -D bootblock.o > bootblock.asm + $(OBJCOPY) -S -O binary -j .text bootblock.o bootblock + perl sign.pl bootblock + +kernel.a: $(RS) + cargo rustc -Z build-std=core -Z build-std-features=compiler-builtins-mem -Z json-target-spec --target ./targets/i686.json --lib --release -- -A warnings --emit link=kernel.a + + +kernel: kernel.a $(OBJS) ./linkers/kernel.ld + ld -m elf_i386 -T ./linkers/kernel.ld -o kernel $(OBJS) kernel.a + $(OBJDUMP) -S -D kernel > kernel.asm + $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym + +vectors.S: vectors.pl + ./vectors.pl > vectors.S + +.PRECIOUS: %.o +-include *.d + +clean: + rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ + *.a *.o *.d *.asm *.sym bootblock kernel xv6.img fs.img .gdbinit vectors.S mkfs + rm -rf target + +# run in emulators +GDBPORT = $(shell expr `id -u` % 5000 + 25000) +QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ + then echo "-gdb tcp::$(GDBPORT)"; \ + else echo "-s -p $(GDBPORT)"; fi) +ifndef CPUS + CPUS := 1 +endif + +# Attach fs.img as disk1 (index=1), like the C version +QEMUOPTS = -drive file=xv6.img,index=0,media=disk,format=raw \ + -drive file=fs.img,index=1,media=disk,format=raw \ + -smp $(CPUS) -m 512 $(QEMUEXTRA) + +qemu: xv6.img fs.img + $(QEMU) -nographic $(QEMUOPTS) + +.gdbinit: .gdbinit.tmpl + sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ + +qemu-gdb: xv6.img fs .gdbinit + @echo "*** Now run 'gdb'." 1>&2 + $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) diff --git a/src/bio.rs b/src/bio.rs new file mode 100644 index 0000000..30e6d81 --- /dev/null +++ b/src/bio.rs @@ -0,0 +1,148 @@ +use core::sync::atomic::Ordering; + +use crate::buf::{Buf, B_DIRTY, B_VALID, NBUF}; + +const HEAD: usize = NBUF; // sentinel index + +struct BCache { + buf: [Buf; NBUF], + head_prev: usize, + head_next: usize, +} + +impl BCache { + pub const fn new() -> Self { + Self { + buf: [const { Buf::new() }; NBUF], + head_prev: HEAD, + head_next: HEAD, + } + } +} + +static mut BCACHE: BCache = BCache::new(); + +pub fn binit() { + unsafe { + // empty list + BCACHE.head_prev = HEAD; + BCACHE.head_next = HEAD; + + // insert all buffers at head (MRU side) + for i in 0..NBUF { + insert_at_head(i); + } + } +} + +#[inline] +fn insert_at_head(i: usize) { + unsafe { + let first = BCACHE.head_next; + + BCACHE.buf[i].prev = HEAD; + BCACHE.buf[i].next = first; + + if first == HEAD { + // list was empty + BCACHE.head_prev = i; + } else { + BCACHE.buf[first].prev = i; + } + + BCACHE.head_next = i; + } +} + +#[inline] +fn remove_from_list(i: usize) { + unsafe { + let prev = BCACHE.buf[i].prev; + let next = BCACHE.buf[i].next; + + if prev == HEAD { + BCACHE.head_next = next; + } else { + BCACHE.buf[prev].next = next; + } + + if next == HEAD { + BCACHE.head_prev = prev; + } else { + BCACHE.buf[next].prev = prev; + } + } +} + +// Return mutable buf by index. +// Safe to call only when you “own” the buffer logically (like xv6 “locked buf”). +pub fn buf_mut(idx: usize) -> &'static mut Buf { + unsafe { &mut BCACHE.buf[idx] } +} + +// Look for cached block; else recycle an unused non-dirty buffer. +fn bget(dev: u32, blockno: u32) -> usize { + unsafe { + // Is the block already cached? + let mut b = BCACHE.head_next; + while b != HEAD { + if BCACHE.buf[b].dev == dev && BCACHE.buf[b].blockno == blockno { + BCACHE.buf[b].refcnt += 1; + return b; + } + b = BCACHE.buf[b].next; + } + + // Not cached; recycle from LRU end. + let mut b = BCACHE.head_prev; + while b != HEAD { + let flags = BCACHE.buf[b].flags.load(Ordering::Acquire); + if BCACHE.buf[b].refcnt == 0 && (flags & B_DIRTY) == 0 { + BCACHE.buf[b].dev = dev; + BCACHE.buf[b].blockno = blockno; + BCACHE.buf[b].flags.store(0, Ordering::Release); + BCACHE.buf[b].refcnt = 1; + BCACHE.buf[b].qnext = None; + return b; + } + b = BCACHE.buf[b].prev; + } + + panic!("bget: no buffers"); + } +} + +// Return buffer index with contents of block. +pub fn bread(dev: u32, blockno: u32) -> usize { + let idx = bget(dev, blockno); + + let flags = buf_mut(idx).flags.load(Ordering::Acquire); + if (flags & B_VALID) == 0 { + crate::ide::iderw(idx); + } + + idx +} + +// Mark dirty + write to disk. +pub fn bwrite(idx: usize) { + let b = buf_mut(idx); + b.flags.fetch_or(B_DIRTY, Ordering::AcqRel); + crate::ide::iderw(idx); +} + +// Release buffer. If refcnt hits 0, move to MRU head. +pub fn brelse(idx: usize) { + unsafe { + let b = &mut BCACHE.buf[idx]; + if b.refcnt == 0 { + panic!("brelse: refcnt underflow"); + } + + b.refcnt -= 1; + if b.refcnt == 0 { + remove_from_list(idx); + insert_at_head(idx); + } + } +} \ No newline at end of file diff --git a/src/buf.rs b/src/buf.rs new file mode 100644 index 0000000..870a4e3 --- /dev/null +++ b/src/buf.rs @@ -0,0 +1,39 @@ +use core::sync::atomic::AtomicU32; + +pub const BSIZE: usize = 512; // block size +pub const NBUF: usize = 30; // same as xv6 default; can tune later + +pub const B_VALID: u32 = 0x2; // buffer has been read from disk +pub const B_DIRTY: u32 = 0x4; // buffer needs to be written to disk + +#[repr(C)] +pub struct Buf { + pub flags: AtomicU32, + pub dev: u32, + pub blockno: u32, + pub refcnt: u32, + + // LRU list (intrusive, by index) + pub prev: usize, + pub next: usize, + + // disk queue (by index) + pub qnext: Option, + + pub data: [u8; BSIZE], +} + +impl Buf { + pub const fn new() -> Self { + Self { + flags: AtomicU32::new(0), + dev: 0, + blockno: 0, + refcnt: 0, + prev: 0, + next: 0, + qnext: None, + data: [0; BSIZE], + } + } +} \ No newline at end of file diff --git a/src/console.rs b/src/console.rs index 0f62712..b9ea7fa 100644 --- a/src/console.rs +++ b/src/console.rs @@ -12,8 +12,27 @@ impl Write for Console { } const BACKSPACE: char = '\x08'; +const INPUT_BUF: u32 = 128; -fn consputc(c: char) { +struct Input { + buf: [u8; INPUT_BUF as usize], + r: u32, // Read index + w: u32, // Write index + e: u32, // Edit index +} + +static mut INPUT: Input = Input { + buf: [0; INPUT_BUF as usize], + r: 0, + w: 0, + e: 0, +}; + +const fn ctrl(x: u8) -> u8 { + x - b'@' +} + +pub fn consputc(c: char) { if c == BACKSPACE { uartputc(BACKSPACE); uartputc(' '); @@ -21,4 +40,45 @@ fn consputc(c: char) { } else { uartputc(c); } -} \ No newline at end of file +} + +pub fn consoleintr(getc: fn() -> i32) { + loop { + let ch_raw = getc(); + if ch_raw < 0 { + break; + } + + unsafe { + match ch_raw as u8 { + x if x == ctrl(b'U') => { + while INPUT.e != INPUT.w + && INPUT.buf[((INPUT.e - 1) % INPUT_BUF) as usize] != b'\n' + { + INPUT.e -= 1; + consputc(BACKSPACE); + } + } + x if x == ctrl(b'H') || x == 0x7f => { + if INPUT.e != INPUT.w { + INPUT.e -= 1; + consputc(BACKSPACE); + } + } + mut ch => { + if ch != 0 && INPUT.e - INPUT.r < INPUT_BUF { + if ch == b'\r' { + ch = b'\n'; + } + INPUT.buf[(INPUT.e % INPUT_BUF) as usize] = ch; + INPUT.e += 1; + consputc(ch as char); + if ch == b'\n' || ch == ctrl(b'D') || INPUT.e == INPUT.r + INPUT_BUF { + INPUT.w = INPUT.e; + } + } + } + } + } + } +} diff --git a/src/constants.rs b/src/constants.rs index d06b826..5388c0a 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -60,7 +60,7 @@ pub const IRQ_COM1: u32 = 4; pub const IRQ_IDE: u32 = 14; pub const IRQ_ERROR: u32 = 19; pub const IRQ_SPURIOUS: u32 = 31; - +pub const IDE_TRAP: u32 = T_IRQ0 + IRQ_IDE; // ------------------------------------------------------ MP RELATED ------------------------------------------------------- diff --git a/src/fs.rs b/src/fs.rs new file mode 100644 index 0000000..8b6acaf --- /dev/null +++ b/src/fs.rs @@ -0,0 +1,316 @@ +use core::cmp::min; + +use crate::bio; +use crate::buf::BSIZE; +use crate::param::NINODE; +use crate::println; + +pub const ROOTINO: u32 = 1; +pub const NDIRECT: usize = 12; +pub const NINDIRECT: usize = BSIZE / core::mem::size_of::(); +pub const DIRSIZ: usize = 14; +pub const DIRENT_SIZE: usize = 2 + DIRSIZ; + +const DINODE_SIZE: usize = 2 + 2 + 2 + 2 + 4 + ((NDIRECT + 1) * 4); +const IPB: u32 = (BSIZE / DINODE_SIZE) as u32; + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct Superblock { + pub size: u32, + pub nblocks: u32, + pub ninodes: u32, + pub nlog: u32, + pub logstart: u32, + pub inodestart: u32, + pub bmapstart: u32, +} + +impl Superblock { + pub const fn new() -> Self { + Self { + size: 0, + nblocks: 0, + ninodes: 0, + nlog: 0, + logstart: 0, + inodestart: 0, + bmapstart: 0, + } + } +} + +#[repr(C)] +pub struct Inode { + pub dev: u32, + pub inum: u32, + pub refcnt: i32, + pub valid: i32, + + pub type_: i16, + pub major: i16, + pub minor: i16, + pub nlink: i16, + pub size: u32, + pub addrs: [u32; NDIRECT + 1], +} + +impl Inode { + pub const fn new() -> Self { + Self { + dev: 0, + inum: 0, + refcnt: 0, + valid: 0, + type_: 0, + major: 0, + minor: 0, + nlink: 0, + size: 0, + addrs: [0; NDIRECT + 1], + } + } +} + +#[repr(C)] +pub struct Stat { + pub type_: i16, + pub dev: i32, + pub ino: u32, + pub nlink: i16, + pub size: u32, +} + +impl Stat { + pub const fn new() -> Self { + Self { + type_: 0, + dev: 0, + ino: 0, + nlink: 0, + size: 0, + } + } +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct Dirent { + pub inum: u16, + pub name: [u8; DIRSIZ], +} + +impl Dirent { + pub const fn new() -> Self { + Self { + inum: 0, + name: [0; DIRSIZ], + } + } +} + +struct ICache { + inode: [Inode; NINODE], +} + +impl ICache { + const fn new() -> Self { + Self { + inode: [const { Inode::new() }; NINODE], + } + } +} + +static mut SB: Superblock = Superblock::new(); +static mut ICACHE: ICache = ICache::new(); + +#[inline] +fn read_u16_le(data: &[u8], off: usize) -> u16 { + u16::from_le_bytes([data[off], data[off + 1]]) +} + +#[inline] +fn read_i16_le(data: &[u8], off: usize) -> i16 { + i16::from_le_bytes([data[off], data[off + 1]]) +} + +#[inline] +fn read_u32_le(data: &[u8], off: usize) -> u32 { + u32::from_le_bytes([data[off], data[off + 1], data[off + 2], data[off + 3]]) +} + +#[inline] +fn iblock(inum: u32, sb: &Superblock) -> u32 { + inum / IPB + sb.inodestart +} + +pub fn parse_dirent(raw: &[u8]) -> Dirent { + let mut de = Dirent::new(); + de.inum = read_u16_le(raw, 0); + de.name.copy_from_slice(&raw[2..2 + DIRSIZ]); + de +} + +pub fn readsb(dev: u32, sb: &mut Superblock) { + let bp = bio::bread(dev, 1); + let data = &bio::buf_mut(bp).data; + + sb.size = read_u32_le(data, 0); + sb.nblocks = read_u32_le(data, 4); + sb.ninodes = read_u32_le(data, 8); + sb.nlog = read_u32_le(data, 12); + sb.logstart = read_u32_le(data, 16); + sb.inodestart = read_u32_le(data, 20); + sb.bmapstart = read_u32_le(data, 24); + + bio::brelse(bp); +} + +pub fn iinit(dev: u32) { + unsafe { + readsb(dev, &mut SB); + println!( + "sb: size {} nblocks {} ninodes {} nlog {} logstart {} inodestart {} bmap start {}", + SB.size, SB.nblocks, SB.ninodes, SB.nlog, SB.logstart, SB.inodestart, SB.bmapstart + ); + } +} + +pub fn iget(dev: u32, inum: u32) -> usize { + unsafe { + let mut empty: Option = None; + + for i in 0..NINODE { + let ip = &mut ICACHE.inode[i]; + + if ip.refcnt > 0 && ip.dev == dev && ip.inum == inum { + ip.refcnt += 1; + return i; + } + + if empty.is_none() && ip.refcnt == 0 { + empty = Some(i); + } + } + + let idx = empty.unwrap_or_else(|| panic!("iget: no inodes")); + let ip = &mut ICACHE.inode[idx]; + + ip.dev = dev; + ip.inum = inum; + ip.refcnt = 1; + ip.valid = 0; + + idx + } +} + +pub fn iread(idx: usize) { + unsafe { + if idx >= NINODE { + panic!("iread: bad inode index"); + } + + let ip = &mut ICACHE.inode[idx]; + if ip.refcnt < 1 { + panic!("iread"); + } + + if ip.valid == 0 { + let bp = bio::bread(ip.dev, iblock(ip.inum, &SB)); + let data = &bio::buf_mut(bp).data; + + let off = (ip.inum % IPB) as usize * DINODE_SIZE; + ip.type_ = read_i16_le(data, off); + ip.major = read_i16_le(data, off + 2); + ip.minor = read_i16_le(data, off + 4); + ip.nlink = read_i16_le(data, off + 6); + ip.size = read_u32_le(data, off + 8); + + for i in 0..(NDIRECT + 1) { + ip.addrs[i] = read_u32_le(data, off + 12 + (i * 4)); + } + + bio::brelse(bp); + + ip.valid = 1; + if ip.type_ == 0 { + panic!("iread: no type"); + } + } + } +} + +fn bmap(ip: &Inode, bn: u32) -> u32 { + if (bn as usize) < NDIRECT { + return ip.addrs[bn as usize]; + } + + let bn = bn - (NDIRECT as u32); + if (bn as usize) < NINDIRECT { + let bp = bio::bread(ip.dev, ip.addrs[NDIRECT]); + let addr = { + let data = &bio::buf_mut(bp).data; + read_u32_le(data, (bn as usize) * 4) + }; + bio::brelse(bp); + return addr; + } + + panic!("bmap: out of range"); +} + +pub fn stati(idx: usize, st: &mut Stat) { + unsafe { + if idx >= NINODE { + panic!("stati: bad inode index"); + } + + let ip = &ICACHE.inode[idx]; + st.dev = ip.dev as i32; + st.ino = ip.inum; + st.type_ = ip.type_; + st.nlink = ip.nlink; + st.size = ip.size; + } +} + +pub fn readi(idx: usize, dst: &mut [u8], off: u32, n: u32) -> i32 { + unsafe { + if idx >= NINODE { + panic!("readi: bad inode index"); + } + + let ip = &ICACHE.inode[idx]; + if off > ip.size || off.checked_add(n).is_none() { + return -1; + } + + let mut n = n; + if off + n > ip.size { + n = ip.size - off; + } + + if (n as usize) > dst.len() { + panic!("readi: destination too small"); + } + + let mut tot: u32 = 0; + let mut cur_off = off; + + while tot < n { + let bp = bio::bread(ip.dev, bmap(ip, cur_off / (BSIZE as u32))); + + let m = min((n - tot) as usize, BSIZE - (cur_off as usize % BSIZE)); + let boff = cur_off as usize % BSIZE; + dst[tot as usize..tot as usize + m] + .copy_from_slice(&bio::buf_mut(bp).data[boff..boff + m]); + bio::brelse(bp); + + tot += m as u32; + cur_off += m as u32; + } + + n as i32 + } +} \ No newline at end of file diff --git a/src/ide.rs b/src/ide.rs new file mode 100644 index 0000000..e7ac913 --- /dev/null +++ b/src/ide.rs @@ -0,0 +1,170 @@ +use core::sync::atomic::Ordering; +use crate::buf::{BSIZE, B_DIRTY, B_VALID}; +use crate::x86; + +const SECTOR_SIZE: usize = 512; + +const IDE_BSY: u8 = 0x80; +const IDE_DRDY: u8 = 0x40; +const IDE_DF: u8 = 0x20; +const IDE_ERR: u8 = 0x01; + +const IDE_CMD_READ: u8 = 0x20; +const IDE_CMD_WRITE: u8 = 0x30; +const IDE_CMD_RDMUL: u8 = 0xC4; +const IDE_CMD_WRMUL: u8 = 0xC5; + +// If you later build a real FS image, keep this consistent with your mkfs. +// xv6 uses 1000. +const FSSIZE: u32 = 1000; + +static mut IDEQUEUE: Option = None; +static mut HAVEDISK1: bool = false; + +fn idewait(checkerr: bool) -> i32 { + let mut r: u8; + loop { + r = x86::inb(0x1F7); + if (r & (IDE_BSY | IDE_DRDY)) == IDE_DRDY { + break; + } + } + if checkerr && (r & (IDE_DF | IDE_ERR)) != 0 { + return -1; + } + 0 +} + +pub fn ideinit() { + // Route IDE IRQ somewhere; simplest is CPU 0 for now. + crate::ioapic::ioapic_enable(crate::constants::IRQ_IDE, 0); + + idewait(false); + + // Check if disk 1 is present + unsafe { + x86::outb(0x1F6, 0xE0 | (1 << 4)); + for _ in 0..1000 { + if x86::inb(0x1F7) != 0 { + HAVEDISK1 = true; + break; + } + } + // Switch back to disk 0 + x86::outb(0x1F6, 0xE0 | (0 << 4)); + } +} + +fn idestart(idx: usize) { + let b = crate::bio::buf_mut(idx); + + if b.blockno >= FSSIZE { + panic!("idestart: incorrect blockno"); + } + + let sector_per_block = BSIZE / SECTOR_SIZE; // usually 1 + let sector = (b.blockno as usize) * sector_per_block; + + let read_cmd = if sector_per_block == 1 { IDE_CMD_READ } else { IDE_CMD_RDMUL }; + let write_cmd = if sector_per_block == 1 { IDE_CMD_WRITE } else { IDE_CMD_WRMUL }; + + if sector_per_block > 7 { + panic!("idestart: sector_per_block > 7"); + } + + idewait(false); + x86::outb(0x3F6, 0); // generate interrupt + + x86::outb(0x1F2, sector_per_block as u8); // number of sectors + x86::outb(0x1F3, (sector & 0xFF) as u8); + x86::outb(0x1F4, ((sector >> 8) & 0xFF) as u8); + x86::outb(0x1F5, ((sector >> 16) & 0xFF) as u8); + x86::outb( + 0x1F6, + 0xE0 | (((b.dev & 1) as u8) << 4) | (((sector >> 24) & 0x0F) as u8), + ); + + let flags = b.flags.load(Ordering::Acquire); + if (flags & B_DIRTY) != 0 { + x86::outb(0x1F7, write_cmd); + + // write BSIZE bytes as u32 words + unsafe { + x86::outsl(0x1F0, b.data.as_ptr() as *const u32, BSIZE / 4); + } + } else { + x86::outb(0x1F7, read_cmd); + } +} + +// Interrupt handler. +pub fn ideintr() { + let idx = unsafe { + match IDEQUEUE { + None => return, + Some(i) => { + let b = crate::bio::buf_mut(i); + IDEQUEUE = b.qnext; + b.qnext = None; + i + } + } + }; + + let b = crate::bio::buf_mut(idx); + + let flags = b.flags.load(Ordering::Acquire); + + // Read data if needed. + if (flags & B_DIRTY) == 0 && idewait(true) >= 0 { + unsafe { + x86::insl(0x1F0, b.data.as_mut_ptr() as *mut u32, BSIZE / 4); + } + } + + b.flags.fetch_or(B_VALID, Ordering::AcqRel); + b.flags.fetch_and(!B_DIRTY, Ordering::AcqRel); + + // Start next buffer in queue. + unsafe { + if let Some(next) = IDEQUEUE { + idestart(next); + } + } +} + +// ide.rs +pub fn iderw(idx: usize) { + let b = crate::bio::buf_mut(idx); + + let flags = b.flags.load(Ordering::Acquire); + if (flags & (B_VALID | B_DIRTY)) == B_VALID { + panic!("iderw: nothing to do"); + } + + unsafe { + if b.dev != 0 && !HAVEDISK1 { + panic!("iderw: ide disk 1 not present"); + } + } + + // PURE POLLING: issue the command directly (no IDEQUEUE). + idestart(idx); + + // Wait for completion. + if idewait(true) < 0 { + panic!("iderw: ide error"); + } + + // If it was a read, pull data now. + let flags_now = b.flags.load(Ordering::Acquire); + if (flags_now & B_DIRTY) == 0 { + unsafe { + crate::x86::insl(0x1F0, b.data.as_mut_ptr() as *mut u32, BSIZE / 4); + } + } + // If it was a write, data was already pushed in idestart() via outsl(). + + b.flags.fetch_or(B_VALID, Ordering::AcqRel); + b.flags.fetch_and(!B_DIRTY, Ordering::AcqRel); +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index aa34772..4e67f12 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,10 @@ mod mp; mod proc; mod traps; mod constants; - +mod buf; +mod bio; +mod ide; +mod fs; use crate::traps::*; #[macro_export] @@ -35,8 +38,61 @@ fn halt() -> ! { } } +fn print_bytes(bytes: &[u8]) { + for &ch in bytes { + console::consputc(ch as char); + } +} + +fn print_cstr(bytes: &[u8]) { + for &ch in bytes { + if ch == 0 { + break; + } + console::consputc(ch as char); + } +} + +fn welcome() { + const NDIR_READ: usize = 4; + + let root = fs::iget(param::ROOTDEV, fs::ROOTINO); + fs::iread(root); + + let mut raw_entries = [0u8; fs::DIRENT_SIZE * NDIR_READ]; + let entries_len = raw_entries.len() as u32; + let n = fs::readi(root, &mut raw_entries, 0, entries_len); + println!("Read {} bytes from inode of root directory", n); + + let mut entries = [fs::Dirent::new(); NDIR_READ]; + for i in 0..NDIR_READ { + let start = i * fs::DIRENT_SIZE; + let end = start + fs::DIRENT_SIZE; + entries[i] = fs::parse_dirent(&raw_entries[start..end]); + + print_bytes(b"name: "); + print_cstr(&entries[i].name); + println!(" is at inum: {}", entries[i].inum); + } + + let wtxt = fs::iget(param::ROOTDEV, entries[2].inum as u32); + fs::iread(wtxt); + let mut st = fs::Stat::new(); + fs::stati(wtxt, &mut st); + println!( + "\nwelcome.txt stats: Device {}, inode number {}, type {}, number of links {}, size {}", + st.dev, st.ino, st.type_, st.nlink, st.size + ); + + let mut greet = [0u8; 512]; + let n = fs::readi(wtxt, &mut greet, 0, st.size); + println!("Read {} bytes from welcome.txt", n); + print_bytes(&greet[..n as usize]); + console::consputc('\n'); +} + extern "C" { - pub static alltraps: fn(); + pub fn alltraps(); } #[no_mangle] @@ -46,9 +102,14 @@ pub extern "C" fn entryofrust() -> ! { picirq::picinit(); ioapic::ioapic_init(); uart::uartinit(); + ide::ideinit(); tvinit(); + bio::binit(); idtinit(); x86::sti(); + fs::iinit(param::ROOTDEV); + welcome(); + loop { x86::wfi(); } @@ -58,4 +119,4 @@ pub extern "C" fn entryofrust() -> ! { fn panic(info: &PanicInfo) -> ! { println!("Kernel Panic: {:?}", info); loop {} -} \ No newline at end of file +} diff --git a/src/param.rs b/src/param.rs index 4341329..63a19c7 100644 --- a/src/param.rs +++ b/src/param.rs @@ -1,2 +1,4 @@ pub const KSTACKSIZE: usize = 4096; // size of per-process kernel stack pub const NCPU: usize = 8; // maximum number of CPUs +pub const NINODE: usize = 50; // maximum number of active i-nodes +pub const ROOTDEV: u32 = 1; // device number of file system root disk diff --git a/src/traps.rs b/src/traps.rs index 0643fcc..1278148 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -14,6 +14,7 @@ pub const T_IRQ0: u32 = 32; pub const IRQ_TIMER: u32 = 0; pub const IRQ_ERROR: u32 = 19; pub const IRQ_SPURIOUS: u32 = 31; +const LOG_TICKS: bool = false; extern "C" { static vectors: [usize; 256]; // remove assembly. @@ -96,7 +97,7 @@ pub fn tvinit() { 0 // Descriptor privilege level. ); } - IDT.idt.set(arr); + let _ = IDT.idt.set(arr); } pub fn idtinit() { @@ -116,11 +117,12 @@ pub extern "C" fn trap(orig_tf: *mut TrapFrame) { const TIMER: u32 = T_IRQ0 + IRQ_TIMER; const SPURIOUS: u32 = T_IRQ0 + IRQ_SPURIOUS; const SEVEN: u32 = T_IRQ0 + 7; - match tf.trapno { TIMER => { *IDT.ticks.borrow_mut() += 1; - println!("Tick {}!", IDT.ticks.borrow()); + if LOG_TICKS { + println!("Tick {}!", IDT.ticks.borrow()); + } lapic::lapiceoi(); } SEVEN | SPURIOUS => { @@ -132,6 +134,11 @@ pub extern "C" fn trap(orig_tf: *mut TrapFrame) { ); lapiceoi(); } + crate::constants::IDE_TRAP => { + crate::ide::ideintr(); + crate::lapic::lapiceoi(); + } + _ => { println!( "unexpected trap {} from cpu {} eip {} (cr2=0x{:x})\n", @@ -143,4 +150,4 @@ pub extern "C" fn trap(orig_tf: *mut TrapFrame) { panic!("trap happened"); } } -} \ No newline at end of file +} diff --git a/src/uart.rs b/src/uart.rs index 9888088..a6c800b 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -1,7 +1,12 @@ use crate::x86::{outb, inb}; +use crate::console::consoleintr; use crate::ioapic::ioapic_enable; +use core::sync::atomic::{AtomicBool, Ordering}; const COM1: u16 = 0x3F8; // COM1 port address pub const IRQ_COM1: u32 = 4; + +static UART_PRESENT: AtomicBool = AtomicBool::new(false); + pub fn uartinit() { // Turn off the FIFO. outb(COM1 + 2, 0); @@ -19,7 +24,9 @@ pub fn uartinit() { if inb(COM1 + 5) == 0xFF { return; } - + + UART_PRESENT.store(true, Ordering::Relaxed); + // Acknowledge pre-existing interrupt conditions; // enable interrupts. inb(COM1+2); @@ -33,10 +40,27 @@ pub fn uartinit() { } pub fn uartputc(c: char) { + if !UART_PRESENT.load(Ordering::Relaxed) { + return; + } for _ in 0..128 { if inb(COM1 + 5) & 0x20 != 0 { break; } } outb(COM1 + 0, c as u8); -} \ No newline at end of file +} + +fn uartgetc() -> i32 { + if !UART_PRESENT.load(Ordering::Relaxed) { + return -1; + } + if (inb(COM1 + 5) & 0x01) == 0 { + return -1; + } + inb(COM1 + 0) as i32 +} + +pub fn uartintr() { + consoleintr(uartgetc); +} diff --git a/src/x86.rs b/src/x86.rs index ba0598c..fb341ef 100644 --- a/src/x86.rs +++ b/src/x86.rs @@ -112,6 +112,39 @@ pub fn lidt(gdt: *const [GateDesc; 256], size: usize) { } } +pub fn noop() { + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); +} + +// x86.rs +pub unsafe fn insl(port: u16, addr: *mut u32, cnt: usize) { + core::arch::asm!( + "cld", + "rep insd", + in("dx") port, + inout("edi") (addr as usize) => _, + inout("ecx") cnt => _, + options(nostack, preserves_flags), + ); +} + + + +pub unsafe fn outsl(port: u16, addr: *const u32, cnt: usize) { + let mut p = addr; + for _ in 0..cnt { + let val = core::ptr::read(p); + asm!( + "out dx, eax", + in("dx") port, + in("eax") val, + options(nomem, nostack, preserves_flags), + ); + p = p.add(1); + } +} + + /// Halts the CPU until the next interrupt occurs. /// The `hlt` instruction: /// 1. Stops instruction execution and places the processor in a HALT state diff --git a/welcome.txt b/welcome.txt new file mode 100644 index 0000000..5809c82 --- /dev/null +++ b/welcome.txt @@ -0,0 +1,7 @@ + ### + # # ###### # #### #### # # ###### ### + # # # # # # # # ## ## # ### + # # ##### # # # # # ## # ##### # + # ## # # # # # # # # # + ## ## # # # # # # # # # ### + # # ###### ###### #### #### # # ###### ### From dd458e3fa248bfb229553dfe90f5fab193dea118 Mon Sep 17 00:00:00 2001 From: Amber-Agarwal Date: Sat, 14 Feb 2026 17:53:19 +0530 Subject: [PATCH 2/5] remove rustc ICE dump file --- rustc-ice-2026-02-14T12_17_44-2018.txt | 50 -------------------------- 1 file changed, 50 deletions(-) delete mode 100644 rustc-ice-2026-02-14T12_17_44-2018.txt diff --git a/rustc-ice-2026-02-14T12_17_44-2018.txt b/rustc-ice-2026-02-14T12_17_44-2018.txt deleted file mode 100644 index 6569374..0000000 --- a/rustc-ice-2026-02-14T12_17_44-2018.txt +++ /dev/null @@ -1,50 +0,0 @@ -thread 'rustc' panicked at /rustc-dev/7057231bd78d6c7893f905ea1832365d4c5efe17/library/core/src/slice/index.rs:1027:55: -slice index starts at 54 but ends at 52 -stack backtrace: - 0: 0x72342475904b - ::create - 1: 0x723424758f95 - ::force_capture - 2: 0x7234237516d8 - std[bef1e3e6ef206a1]::panicking::update_hook::>::{closure#0} - 3: 0x72342476bb32 - std[bef1e3e6ef206a1]::panicking::panic_with_hook - 4: 0x72342474e558 - std[bef1e3e6ef206a1]::panicking::panic_handler::{closure#0} - 5: 0x723424742ac9 - std[bef1e3e6ef206a1]::sys::backtrace::__rust_end_short_backtrace:: - 6: 0x72342474ffbd - __rustc[7b32525fab805468]::rust_begin_unwind - 7: 0x723421694bac - core[14f954927b42fbe0]::panicking::panic_fmt - 8: 0x72342272a94d - core[14f954927b42fbe0]::slice::index::slice_index_fail - 9: 0x723423089065 - ::replace - 10: 0x72342612fab5 - annotate_snippets[f15529b311271195]::renderer::render::render - 11: 0x72342634881f - ::emit_messages_default - 12: 0x723426340feb - ::emit_diagnostic - 13: 0x72342633fe94 - ::from_errors_diagnostic - 14: 0x72342633f61d - ::emit_diagnostic - 15: 0x723426367b30 - ::emit_diagnostic::{closure#3} - 16: 0x723426365aa6 - rustc_interface[5d23d87c33b7100c]::callbacks::track_diagnostic::> - 17: 0x723426364b2e - ::emit_diagnostic - 18: 0x7234263649e1 - ::emit_diagnostic - 19: 0x723421291e8c - ::emit_producing_guarantee - 20: 0x7234216efa86 - rustc_borrowck[ab19dfe50827342]::borrowck_check_region_constraints - 21: 0x723425ea9eb3 - ::do_mir_borrowck - 22: 0x723425ea472f - rustc_borrowck[ab19dfe50827342]::mir_borrowck - 23: 0x723425ea4579 - rustc_query_impl[19f3565069731b2f]::query_impl::mir_borrowck::compute_fn::__rust_begin_short_backtrace - 24: 0x72342574507e - rustc_query_impl[19f3565069731b2f]::execution::try_execute_query::, rustc_query_system[50dced63bea56a4a]::dep_graph::graph::DepNodeIndex>, {rustc_query_impl[19f3565069731b2f]::QueryFlags { is_anon: false, is_depth_limit: false, is_feedable: false }}, false> - 25: 0x723425743ba3 - rustc_query_impl[19f3565069731b2f]::query_impl::mir_borrowck::get_query_non_incr::__rust_end_short_backtrace - 26: 0x723425743d65 - ::par_hir_body_owners::::{closure#0} - 27: 0x723425740c54 - rustc_interface[5d23d87c33b7100c]::passes::analysis - 28: 0x7234260504a6 - rustc_query_impl[19f3565069731b2f]::execution::try_execute_query::>, {rustc_query_impl[19f3565069731b2f]::QueryFlags { is_anon: false, is_depth_limit: false, is_feedable: false }}, false> - 29: 0x72342605027c - rustc_query_impl[19f3565069731b2f]::query_impl::analysis::get_query_non_incr::__rust_end_short_backtrace - 30: 0x7234261f6be7 - , rustc_driver_impl[f9ad7bf1bed37554]::run_compiler::{closure#0}::{closure#2}>::{closure#2} as core[14f954927b42fbe0]::ops::function::FnOnce<(&rustc_session[2f7fc1337f473eb7]::session::Session, rustc_middle[8b4cd4281f9facd9]::ty::context::CurrentGcx, alloc[97538fc0341e4f07]::sync::Arc, &std[bef1e3e6ef206a1]::sync::once_lock::OnceLock, &rustc_data_structures[5b3f8bc5aac74be7]::sync::worker_local::WorkerLocal, &rustc_data_structures[5b3f8bc5aac74be7]::sync::worker_local::WorkerLocal, rustc_driver_impl[f9ad7bf1bed37554]::run_compiler::{closure#0}::{closure#2})>>::call_once::{shim:vtable#0} - 31: 0x72342602e0b7 - rustc_interface[5d23d87c33b7100c]::interface::run_compiler::<(), rustc_driver_impl[f9ad7bf1bed37554]::run_compiler::{closure#0}>::{closure#1} - 32: 0x723426016dbe - std[bef1e3e6ef206a1]::sys::backtrace::__rust_begin_short_backtrace::::{closure#1}, ()>::{closure#0}, ()>::{closure#0}::{closure#0}, ()> - 33: 0x7234260172e0 - ::{closure#1}, ()>::{closure#0}, ()>::{closure#0}::{closure#0}, ()>::{closure#1} as core[14f954927b42fbe0]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0} - 34: 0x72342601816c - ::new::thread_start - 35: 0x72341f89caa4 - - 36: 0x72341f929c6c - - 37: 0x0 - - - -rustc version: 1.95.0-nightly (7057231bd 2026-02-11) -platform: x86_64-unknown-linux-gnu - -query stack during panic: -#0 [mir_borrowck] borrow-checking `welcome` -#1 [analysis] running analysis passes on crate `kernel` -end of query stack From 56d0f114ee7402908f00f8f9fe283e53a6e234ef Mon Sep 17 00:00:00 2001 From: Amber-Agarwal Date: Sun, 15 Feb 2026 17:16:53 +0530 Subject: [PATCH 3/5] implement filesystem write path and directory/name operations --- src/fs.rs | 403 +++++++++++++++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 65 +++++---- 2 files changed, 428 insertions(+), 40 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index 8b6acaf..e934c01 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -8,8 +8,14 @@ use crate::println; pub const ROOTINO: u32 = 1; pub const NDIRECT: usize = 12; pub const NINDIRECT: usize = BSIZE / core::mem::size_of::(); +pub const MAXFILE: usize = NDIRECT + NINDIRECT; pub const DIRSIZ: usize = 14; pub const DIRENT_SIZE: usize = 2 + DIRSIZ; +pub const BPB: u32 = (BSIZE * 8) as u32; + +pub const T_DIR: i16 = 1; +pub const T_FILE: i16 = 2; +pub const T_DEV: i16 = 3; const DINODE_SIZE: usize = 2 + 2 + 2 + 2 + 4 + ((NDIRECT + 1) * 4); const IPB: u32 = (BSIZE / DINODE_SIZE) as u32; @@ -139,11 +145,40 @@ fn read_u32_le(data: &[u8], off: usize) -> u32 { u32::from_le_bytes([data[off], data[off + 1], data[off + 2], data[off + 3]]) } +#[inline] +fn write_u16_le(data: &mut [u8], off: usize, val: u16) { + data[off..off + 2].copy_from_slice(&val.to_le_bytes()); +} + +#[inline] +fn write_i16_le(data: &mut [u8], off: usize, val: i16) { + data[off..off + 2].copy_from_slice(&val.to_le_bytes()); +} + +#[inline] +fn write_u32_le(data: &mut [u8], off: usize, val: u32) { + data[off..off + 4].copy_from_slice(&val.to_le_bytes()); +} + #[inline] fn iblock(inum: u32, sb: &Superblock) -> u32 { inum / IPB + sb.inodestart } +#[inline] +fn bblock(b: u32, sb: &Superblock) -> u32 { + b / BPB + sb.bmapstart +} + +#[inline] +fn name_to_dirsiz(name: &str) -> [u8; DIRSIZ] { + let mut out = [0u8; DIRSIZ]; + let bytes = name.as_bytes(); + let n = min(bytes.len(), DIRSIZ); + out[..n].copy_from_slice(&bytes[..n]); + out +} + pub fn parse_dirent(raw: &[u8]) -> Dirent { let mut de = Dirent::new(); de.inum = read_u16_le(raw, 0); @@ -176,6 +211,119 @@ pub fn iinit(dev: u32) { } } +fn bzero(dev: u32, bno: u32) { + let bp = bio::bread(dev, bno); + bio::buf_mut(bp).data.fill(0); + bio::bwrite(bp); + bio::brelse(bp); +} + +fn balloc(dev: u32) -> u32 { + unsafe { + let mut b = 0u32; + while b < SB.size { + let bp = bio::bread(dev, bblock(b, &SB)); + let mut found: Option = None; + { + let data = &mut bio::buf_mut(bp).data; + let mut bi = 0u32; + while bi < BPB && b + bi < SB.size { + let m: u8 = 1u8 << (bi % 8); + let idx = (bi / 8) as usize; + if (data[idx] & m) == 0 { + data[idx] |= m; + found = Some(b + bi); + break; + } + bi += 1; + } + } + + if let Some(blockno) = found { + bio::bwrite(bp); + bio::brelse(bp); + bzero(dev, blockno); + return blockno; + } + + bio::brelse(bp); + b += BPB; + } + } + + panic!("balloc: out of blocks"); +} + +pub fn ialloc(dev: u32, type_: i16) -> usize { + unsafe { + let mut inum = 1u32; + while inum < SB.ninodes { + let bp = bio::bread(dev, iblock(inum, &SB)); + let off = (inum % IPB) as usize * DINODE_SIZE; + + let free = { + let data = &bio::buf_mut(bp).data; + read_i16_le(data, off) == 0 + }; + + if free { + { + let data = &mut bio::buf_mut(bp).data; + data[off..off + DINODE_SIZE].fill(0); + write_i16_le(data, off, type_); + } + bio::bwrite(bp); + bio::brelse(bp); + return iget(dev, inum); + } + + bio::brelse(bp); + inum += 1; + } + } + + panic!("ialloc: no inodes"); +} + +pub fn irelease(idx: usize) { + unsafe { + if idx >= NINODE { + panic!("irelease: bad inode index"); + } + if ICACHE.inode[idx].refcnt < 1 { + panic!("irelease: ref underflow"); + } + ICACHE.inode[idx].refcnt -= 1; + } +} + +pub fn iupdate(idx: usize) { + unsafe { + if idx >= NINODE { + panic!("iupdate: bad inode index"); + } + + let ip = &ICACHE.inode[idx]; + let bp = bio::bread(ip.dev, iblock(ip.inum, &SB)); + let off = (ip.inum % IPB) as usize * DINODE_SIZE; + + { + let data = &mut bio::buf_mut(bp).data; + write_i16_le(data, off, ip.type_); + write_i16_le(data, off + 2, ip.major); + write_i16_le(data, off + 4, ip.minor); + write_i16_le(data, off + 6, ip.nlink); + write_u32_le(data, off + 8, ip.size); + for i in 0..(NDIRECT + 1) { + write_u32_le(data, off + 12 + (i * 4), ip.addrs[i]); + } + } + + bio::bwrite(bp); + bio::brelse(bp); + } +} + pub fn iget(dev: u32, inum: u32) -> usize { unsafe { let mut empty: Option = None; @@ -241,20 +389,44 @@ pub fn iread(idx: usize) { } } -fn bmap(ip: &Inode, bn: u32) -> u32 { - if (bn as usize) < NDIRECT { - return ip.addrs[bn as usize]; - } +fn bmap(idx: usize, bn: u32) -> u32 { + unsafe { + if idx >= NINODE { + panic!("bmap: bad inode index"); + } - let bn = bn - (NDIRECT as u32); - if (bn as usize) < NINDIRECT { - let bp = bio::bread(ip.dev, ip.addrs[NDIRECT]); - let addr = { - let data = &bio::buf_mut(bp).data; - read_u32_le(data, (bn as usize) * 4) - }; - bio::brelse(bp); - return addr; + let ip = &mut ICACHE.inode[idx]; + + if (bn as usize) < NDIRECT { + let direct = &mut ip.addrs[bn as usize]; + if *direct == 0 { + *direct = balloc(ip.dev); + } + return *direct; + } + + let bn = bn - (NDIRECT as u32); + if (bn as usize) < NINDIRECT { + if ip.addrs[NDIRECT] == 0 { + ip.addrs[NDIRECT] = balloc(ip.dev); + } + + let bp = bio::bread(ip.dev, ip.addrs[NDIRECT]); + let mut addr = { + let data = &bio::buf_mut(bp).data; + read_u32_le(data, (bn as usize) * 4) + }; + if addr == 0 { + addr = balloc(ip.dev); + { + let data = &mut bio::buf_mut(bp).data; + write_u32_le(data, (bn as usize) * 4, addr); + } + bio::bwrite(bp); + } + bio::brelse(bp); + return addr; + } } panic!("bmap: out of range"); @@ -297,9 +469,10 @@ pub fn readi(idx: usize, dst: &mut [u8], off: u32, n: u32) -> i32 { let mut tot: u32 = 0; let mut cur_off = off; + let dev = ip.dev; while tot < n { - let bp = bio::bread(ip.dev, bmap(ip, cur_off / (BSIZE as u32))); + let bp = bio::bread(dev, bmap(idx, cur_off / (BSIZE as u32))); let m = min((n - tot) as usize, BSIZE - (cur_off as usize % BSIZE)); let boff = cur_off as usize % BSIZE; @@ -313,4 +486,204 @@ pub fn readi(idx: usize, dst: &mut [u8], off: u32, n: u32) -> i32 { n as i32 } -} \ No newline at end of file +} + +pub fn writei(idx: usize, src: &[u8], off: u32, n: u32) -> i32 { + unsafe { + if idx >= NINODE { + panic!("writei: bad inode index"); + } + + let ip = &ICACHE.inode[idx]; + if off > ip.size || off.checked_add(n).is_none() { + return -1; + } + if off + n > (MAXFILE * BSIZE) as u32 { + return -1; + } + if (n as usize) > src.len() { + panic!("writei: source too small"); + } + + let dev = ip.dev; + let mut tot: u32 = 0; + let mut cur_off = off; + + while tot < n { + let bp = bio::bread(dev, bmap(idx, cur_off / (BSIZE as u32))); + let m = min((n - tot) as usize, BSIZE - (cur_off as usize % BSIZE)); + let boff = cur_off as usize % BSIZE; + bio::buf_mut(bp).data[boff..boff + m] + .copy_from_slice(&src[tot as usize..tot as usize + m]); + bio::bwrite(bp); + bio::brelse(bp); + + tot += m as u32; + cur_off += m as u32; + } + + if n > 0 && cur_off > ICACHE.inode[idx].size { + ICACHE.inode[idx].size = cur_off; + iupdate(idx); + } + + n as i32 + } +} + +pub fn inode_inum(idx: usize) -> u32 { + unsafe { + if idx >= NINODE { + panic!("inode_inum: bad inode index"); + } + ICACHE.inode[idx].inum + } +} + +pub fn namecmp(s: &str, t: &[u8; DIRSIZ]) -> bool { + name_to_dirsiz(s) == *t +} + +pub fn dirlookup(dp_idx: usize, name: &str, mut poff: Option<&mut u32>) -> Option { + unsafe { + if dp_idx >= NINODE { + panic!("dirlookup: bad inode index"); + } + } + iread(dp_idx); + + unsafe { + let dp = &ICACHE.inode[dp_idx]; + if dp.type_ != T_DIR { + panic!("dirlookup not DIR"); + } + + let mut off = 0u32; + while off < dp.size { + let mut raw = [0u8; DIRENT_SIZE]; + if readi(dp_idx, &mut raw, off, DIRENT_SIZE as u32) != DIRENT_SIZE as i32 { + panic!("dirlookup read"); + } + let de = parse_dirent(&raw); + if de.inum != 0 && namecmp(name, &de.name) { + if let Some(ref mut out_off) = poff { + **out_off = off; + } + return Some(iget(dp.dev, de.inum as u32)); + } + off += DIRENT_SIZE as u32; + } + } + + None +} + +pub fn dirlink(dp_idx: usize, name: &str, inum: u32) -> i32 { + if let Some(ip_idx) = dirlookup(dp_idx, name, None) { + irelease(ip_idx); + return -1; + } + + let mut off = 0u32; + unsafe { + if dp_idx >= NINODE { + panic!("dirlink: bad inode index"); + } + while off < ICACHE.inode[dp_idx].size { + let mut raw = [0u8; DIRENT_SIZE]; + if readi(dp_idx, &mut raw, off, DIRENT_SIZE as u32) != DIRENT_SIZE as i32 { + panic!("dirlink read"); + } + let de = parse_dirent(&raw); + if de.inum == 0 { + break; + } + off += DIRENT_SIZE as u32; + } + } + + let mut raw = [0u8; DIRENT_SIZE]; + write_u16_le(&mut raw, 0, inum as u16); + raw[2..2 + DIRSIZ].copy_from_slice(&name_to_dirsiz(name)); + if writei(dp_idx, &raw, off, DIRENT_SIZE as u32) != DIRENT_SIZE as i32 { + panic!("dirlink"); + } + + 0 +} + +fn skipelem(path: &[u8], mut i: usize, name: &mut [u8; DIRSIZ]) -> Option { + while i < path.len() && path[i] == b'/' { + i += 1; + } + if i == path.len() { + return None; + } + + let start = i; + while i < path.len() && path[i] != b'/' { + i += 1; + } + let len = i - start; + + name.fill(0); + if len >= DIRSIZ { + name.copy_from_slice(&path[start..start + DIRSIZ]); + } else { + name[..len].copy_from_slice(&path[start..i]); + } + + while i < path.len() && path[i] == b'/' { + i += 1; + } + Some(i) +} + +fn namex(path: &str, nameiparent: bool, name: &mut [u8; DIRSIZ]) -> Option { + let bytes = path.as_bytes(); + let mut path_idx = 0usize; + let mut ip = iget(crate::param::ROOTDEV, ROOTINO); + + while let Some(next_idx) = skipelem(bytes, path_idx, name) { + path_idx = next_idx; + iread(ip); + + unsafe { + if ICACHE.inode[ip].type_ != T_DIR { + irelease(ip); + return None; + } + } + + if nameiparent && path_idx == bytes.len() { + return Some(ip); + } + + let elem = core::str::from_utf8(name).unwrap_or(""); + let trimmed = elem.trim_end_matches('\0'); + let next = match dirlookup(ip, trimmed, None) { + Some(x) => x, + None => { + irelease(ip); + return None; + } + }; + irelease(ip); + ip = next; + } + + if nameiparent { + irelease(ip); + return None; + } + Some(ip) +} + +pub fn namei(path: &str) -> Option { + let mut name = [0u8; DIRSIZ]; + namex(path, false, &mut name) +} + +pub fn nameiparent(path: &str, name: &mut [u8; DIRSIZ]) -> Option { + namex(path, true, name) +} diff --git a/src/lib.rs b/src/lib.rs index 4e67f12..42b72f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,41 +54,56 @@ fn print_cstr(bytes: &[u8]) { } fn welcome() { - const NDIR_READ: usize = 4; - - let root = fs::iget(param::ROOTDEV, fs::ROOTINO); + let root = fs::namei("/").unwrap_or_else(|| panic!("root not found")); fs::iread(root); + let foodir = match fs::dirlookup(root, "foo", None) { + Some(idx) => idx, + None => { + println!("/foo not found. Creating!"); + let idx = fs::ialloc(param::ROOTDEV, fs::T_DIR); + fs::iread(idx); + let ino = fs::inode_inum(idx); + if fs::dirlink(idx, ".", ino) < 0 { + panic!("failed to link . in /foo"); + } + if fs::dirlink(idx, "..", ino) < 0 { + panic!("failed to link .. in /foo"); + } + if fs::dirlink(root, "foo", ino) < 0 { + panic!("failed to link /foo in root"); + } + idx + } + }; + + let wtxt = match fs::namei("/foo/greet.txt") { + Some(idx) => idx, + None => { + println!("/foo/greet.txt not found. Creating!"); + let wtxt_orig = + fs::namei("/welcome.txt").unwrap_or_else(|| panic!("/welcome.txt missing")); + let inum = fs::inode_inum(wtxt_orig); + if fs::dirlink(foodir, "greet.txt", inum) < 0 { + panic!("failed to link greet.txt in /foo"); + } + fs::irelease(wtxt_orig); + fs::namei("/foo/greet.txt").unwrap_or_else(|| panic!("greet.txt lookup failed")) + } + }; - let mut raw_entries = [0u8; fs::DIRENT_SIZE * NDIR_READ]; - let entries_len = raw_entries.len() as u32; - let n = fs::readi(root, &mut raw_entries, 0, entries_len); - println!("Read {} bytes from inode of root directory", n); - - let mut entries = [fs::Dirent::new(); NDIR_READ]; - for i in 0..NDIR_READ { - let start = i * fs::DIRENT_SIZE; - let end = start + fs::DIRENT_SIZE; - entries[i] = fs::parse_dirent(&raw_entries[start..end]); - - print_bytes(b"name: "); - print_cstr(&entries[i].name); - println!(" is at inum: {}", entries[i].inum); - } - - let wtxt = fs::iget(param::ROOTDEV, entries[2].inum as u32); fs::iread(wtxt); let mut st = fs::Stat::new(); fs::stati(wtxt, &mut st); - println!( - "\nwelcome.txt stats: Device {}, inode number {}, type {}, number of links {}, size {}", - st.dev, st.ino, st.type_, st.nlink, st.size - ); let mut greet = [0u8; 512]; let n = fs::readi(wtxt, &mut greet, 0, st.size); - println!("Read {} bytes from welcome.txt", n); + println!("Read {} bytes from /foo/greet.txt", n); print_bytes(&greet[..n as usize]); console::consputc('\n'); + + fs::irelease(wtxt); + fs::irelease(foodir); + fs::irelease(root); } extern "C" { From f4595fce2761859d364ec659d0b30601f68f3382 Mon Sep 17 00:00:00 2001 From: Anoop Singh Date: Tue, 17 Feb 2026 18:38:34 +0530 Subject: [PATCH 4/5] p11-rust-done --- Makefile | 7 +- src/Makefile | 6 +- src/fcntl.rs | 4 + src/file.rs | 364 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/fs.rs | 164 ++++++++++++++++++++--- src/lib.rs | 91 ++++++------- src/mkfs.rs | 358 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/param.rs | 2 + 8 files changed, 923 insertions(+), 73 deletions(-) create mode 100644 src/fcntl.rs create mode 100644 src/file.rs create mode 100644 src/mkfs.rs diff --git a/Makefile b/Makefile index 0df672e..9397bcd 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,7 @@ TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/d echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ echo "***" 1>&2; exit 1; fi) endif +TOOLPREFIX=i686-elf- # If the makefile can't find QEMU, specify its path here # QEMU = qemu-system-i386 @@ -69,8 +70,8 @@ xv6.img: bootblock kernel dd if=bootblock of=xv6.img conv=notrunc dd if=kernel of=xv6.img seek=1 conv=notrunc -mkfs: ../col331/mkfs.c ../col331/fs.h ../col331/types.h ../col331/stat.h ../col331/param.h - gcc -Werror -Wall -o mkfs ../col331/mkfs.c +mkfs: src/mkfs.rs + rustc -W warnings -o mkfs src/mkfs.rs fs.img: mkfs welcome.txt ./mkfs fs.img welcome.txt @@ -87,7 +88,7 @@ kernel.a: $(RS) cargo rustc -Z build-std=core -Z build-std-features=compiler-builtins-mem -Z json-target-spec --target ./targets/i686.json --lib --release -- -A warnings --emit link=kernel.a kernel: kernel.a $(OBJS) ./linkers/kernel.ld - ld -m elf_i386 -T ./linkers/kernel.ld -o kernel $(OBJS) kernel.a + $(LD) -m elf_i386 -T ./linkers/kernel.ld -o kernel $(OBJS) kernel.a $(OBJDUMP) -S -D kernel > kernel.asm $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym diff --git a/src/Makefile b/src/Makefile index 079b0db..6061c30 100644 --- a/src/Makefile +++ b/src/Makefile @@ -69,8 +69,8 @@ xv6.img: bootblock kernel dd if=bootblock of=xv6.img conv=notrunc dd if=kernel of=xv6.img seek=1 conv=notrunc -mkfs: ../../col331/mkfs.c ../../col331/fs.h ../../col331/types.h ../../col331/stat.h ../../col331/param.h - gcc -Werror -Wall -o mkfs ../../col331/mkfs.c +mkfs: mkfs.rs + rustc -W warnings -o mkfs mkfs.rs fs: fs.img @@ -90,7 +90,7 @@ kernel.a: $(RS) kernel: kernel.a $(OBJS) ./linkers/kernel.ld - ld -m elf_i386 -T ./linkers/kernel.ld -o kernel $(OBJS) kernel.a + $(LD) -m elf_i386 -T ./linkers/kernel.ld -o kernel $(OBJS) kernel.a $(OBJDUMP) -S -D kernel > kernel.asm $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym diff --git a/src/fcntl.rs b/src/fcntl.rs new file mode 100644 index 0000000..2e76613 --- /dev/null +++ b/src/fcntl.rs @@ -0,0 +1,4 @@ +pub const O_RDONLY: i32 = 0x000; +pub const O_WRONLY: i32 = 0x001; +pub const O_RDWR: i32 = 0x002; +pub const O_CREATE: i32 = 0x200; diff --git a/src/file.rs b/src/file.rs new file mode 100644 index 0000000..20e4ac5 --- /dev/null +++ b/src/file.rs @@ -0,0 +1,364 @@ +use core::str; + +use crate::buf::BSIZE; +use crate::fcntl::{O_CREATE, O_RDONLY, O_RDWR, O_WRONLY}; +use crate::fs; +use crate::param::{MAXOPBLOCKS, NFILE}; + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum FileType { + None, + Inode, +} + +#[derive(Copy, Clone)] +pub struct File { + pub type_: FileType, + pub refcnt: i32, + pub readable: bool, + pub writable: bool, + pub ip: usize, + pub off: u32, +} + +impl File { + pub const fn new() -> Self { + Self { + type_: FileType::None, + refcnt: 0, + readable: false, + writable: false, + ip: 0, + off: 0, + } + } +} + +struct FTable { + file: [File; NFILE], +} + +impl FTable { + const fn new() -> Self { + Self { + file: [const { File::new() }; NFILE], + } + } +} + +static mut FTABLE: FTable = FTable::new(); + +fn dirsiz_to_str(name: &[u8; fs::DIRSIZ]) -> &str { + let len = name.iter().position(|&b| b == 0).unwrap_or(fs::DIRSIZ); + str::from_utf8(&name[..len]).unwrap_or("") +} + +pub fn fileinit() { + unsafe { + for i in 0..NFILE { + FTABLE.file[i] = File::new(); + } + } +} + +pub fn filealloc() -> Option { + unsafe { + for i in 0..NFILE { + if FTABLE.file[i].refcnt == 0 { + FTABLE.file[i].refcnt = 1; + return Some(i); + } + } + } + None +} + +pub fn filedup(f_idx: usize) -> usize { + unsafe { + if f_idx >= NFILE { + panic!("filedup: bad file index"); + } + if FTABLE.file[f_idx].refcnt < 1 { + panic!("filedup"); + } + FTABLE.file[f_idx].refcnt += 1; + } + f_idx +} + +pub fn fileclose(f_idx: usize) { + let ff: File; + unsafe { + if f_idx >= NFILE { + panic!("fileclose: bad file index"); + } + + let f = &mut FTABLE.file[f_idx]; + if f.refcnt < 1 { + panic!("fileclose"); + } + f.refcnt -= 1; + if f.refcnt > 0 { + return; + } + + ff = *f; + *f = File::new(); + } + + if ff.type_ == FileType::Inode { + fs::iput(ff.ip); + } +} + +pub fn filestat(f_idx: usize, st: &mut fs::Stat) -> i32 { + let f: File; + unsafe { + if f_idx >= NFILE { + panic!("filestat: bad file index"); + } + f = FTABLE.file[f_idx]; + } + + if f.type_ == FileType::Inode { + fs::iread(f.ip); + fs::stati(f.ip, st); + return 0; + } + + -1 +} + +pub fn fileread(f_idx: usize, dst: &mut [u8], n: i32) -> i32 { + if n < 0 || (n as usize) > dst.len() { + return -1; + } + + let f: File; + unsafe { + if f_idx >= NFILE { + panic!("fileread: bad file index"); + } + f = FTABLE.file[f_idx]; + } + + if !f.readable { + return -1; + } + + if f.type_ == FileType::Inode { + fs::iread(f.ip); + let r = fs::readi(f.ip, dst, f.off, n as u32); + if r > 0 { + unsafe { + FTABLE.file[f_idx].off += r as u32; + } + } + return r; + } + + panic!("fileread"); +} + +pub fn filewrite(f_idx: usize, src: &[u8], n: i32) -> i32 { + if n < 0 || (n as usize) > src.len() { + return -1; + } + + let f: File; + unsafe { + if f_idx >= NFILE { + panic!("filewrite: bad file index"); + } + f = FTABLE.file[f_idx]; + } + + if !f.writable { + return -1; + } + + if f.type_ == FileType::Inode { + let max = (((MAXOPBLOCKS - 1 - 1 - 2) / 2) * BSIZE) as i32; + let mut i = 0i32; + + while i < n { + let mut n1 = n - i; + if n1 > max { + n1 = max; + } + + fs::iread(f.ip); + let off = unsafe { FTABLE.file[f_idx].off }; + let r = fs::writei(f.ip, &src[i as usize..], off, n1 as u32); + if r > 0 { + unsafe { + FTABLE.file[f_idx].off += r as u32; + } + } + + if r < 0 { + break; + } + if r != n1 { + panic!("short filewrite"); + } + i += r; + } + + if i == n { + n + } else { + -1 + } + } else { + panic!("filewrite"); + } +} + +pub fn isdirempty(dp_idx: usize) -> bool { + fs::iread(dp_idx); + + let mut off = (2 * fs::DIRENT_SIZE) as u32; + while off < fs::inode_size(dp_idx) { + let mut raw = [0u8; fs::DIRENT_SIZE]; + if fs::readi(dp_idx, &mut raw, off, fs::DIRENT_SIZE as u32) != fs::DIRENT_SIZE as i32 { + panic!("isdirempty: readi"); + } + let de = fs::parse_dirent(&raw); + if de.inum != 0 { + return false; + } + off += fs::DIRENT_SIZE as u32; + } + + true +} + +pub fn unlink(path: &str, name: &mut [u8; fs::DIRSIZ]) -> i32 { + let dp = match fs::nameiparent(path, name) { + Some(idx) => idx, + None => return -1, + }; + + fs::iread(dp); + + let name_str = dirsiz_to_str(name); + + if name_str == "." || name_str == ".." { + fs::iput(dp); + return -1; + } + + let mut off = 0u32; + let ip = match fs::dirlookup(dp, name_str, Some(&mut off)) { + Some(idx) => idx, + None => { + fs::iput(dp); + return -1; + } + }; + + fs::iread(ip); + + if fs::inode_nlink(ip) < 1 { + panic!("unlink: nlink < 1"); + } + + if fs::inode_type(ip) == fs::T_DIR && !isdirempty(ip) { + fs::iput(ip); + fs::iput(dp); + return -1; + } + + let de = [0u8; fs::DIRENT_SIZE]; + if fs::writei(dp, &de, off, fs::DIRENT_SIZE as u32) != fs::DIRENT_SIZE as i32 { + panic!("unlink: writei"); + } + + if fs::inode_type(ip) == fs::T_DIR { + fs::inode_dec_nlink(dp); + fs::iupdate(dp); + } + fs::iput(dp); + + fs::inode_dec_nlink(ip); + fs::iupdate(ip); + fs::iput(ip); + + 0 +} + +pub fn create(path: &str, type_: i16, major: i16, minor: i16) -> Option { + let mut name = [0u8; fs::DIRSIZ]; + + let dp = fs::nameiparent(path, &mut name)?; + fs::iread(dp); + + let name_str = dirsiz_to_str(&name); + + if let Some(ip) = fs::dirlookup(dp, name_str, None) { + fs::iput(dp); + fs::iread(ip); + if type_ == fs::T_FILE && fs::inode_type(ip) == fs::T_FILE { + return Some(ip); + } + fs::iput(ip); + return None; + } + + let ip = fs::ialloc(fs::inode_dev(dp), type_); + + fs::iread(ip); + fs::inode_set_meta(ip, major, minor, 1); + fs::iupdate(ip); + + if type_ == fs::T_DIR { + fs::inode_inc_nlink(dp); + fs::iupdate(dp); + + if fs::dirlink(ip, ".", fs::inode_inum(ip)) < 0 || fs::dirlink(ip, "..", fs::inode_inum(dp)) < 0 { + panic!("create dots"); + } + } + + if fs::dirlink(dp, name_str, fs::inode_inum(ip)) < 0 { + panic!("create: dirlink"); + } + + fs::iput(dp); + + Some(ip) +} + +pub fn open(path: &str, omode: i32) -> Option { + let ip = if (omode & O_CREATE) != 0 { + create(path, fs::T_FILE, 0, 0)? + } else { + let ip = fs::namei(path)?; + fs::iread(ip); + if fs::inode_type(ip) == fs::T_DIR && omode != O_RDONLY { + fs::iput(ip); + return None; + } + ip + }; + + let f_idx = match filealloc() { + Some(idx) => idx, + None => { + fs::iput(ip); + return None; + } + }; + + unsafe { + let f = &mut FTABLE.file[f_idx]; + f.type_ = FileType::Inode; + f.ip = ip; + f.off = 0; + f.readable = (omode & O_WRONLY) == 0; + f.writable = (omode & O_WRONLY) != 0 || (omode & O_RDWR) != 0; + } + + Some(f_idx) +} diff --git a/src/fs.rs b/src/fs.rs index e934c01..3c4079b 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -2,7 +2,7 @@ use core::cmp::min; use crate::bio; use crate::buf::BSIZE; -use crate::param::NINODE; +use crate::param::{NINODE, ROOTDEV}; use crate::println; pub const ROOTINO: u32 = 1; @@ -254,6 +254,26 @@ fn balloc(dev: u32) -> u32 { panic!("balloc: out of blocks"); } +fn bfree(dev: u32, b: u32) { + unsafe { + let bp = bio::bread(dev, bblock(b, &SB)); + let bi = b % BPB; + let m: u8 = 1u8 << (bi % 8); + let idx = (bi / 8) as usize; + + { + let data = &mut bio::buf_mut(bp).data; + if (data[idx] & m) == 0 { + panic!("freeing free block"); + } + data[idx] &= !m; + } + + bio::bwrite(bp); + bio::brelse(bp); + } +} + pub fn ialloc(dev: u32, type_: i16) -> usize { unsafe { let mut inum = 1u32; @@ -285,18 +305,69 @@ pub fn ialloc(dev: u32, type_: i16) -> usize { panic!("ialloc: no inodes"); } -pub fn irelease(idx: usize) { +fn itrunc(idx: usize) { unsafe { if idx >= NINODE { - panic!("irelease: bad inode index"); + panic!("itrunc: bad inode index"); + } + + let dev = ICACHE.inode[idx].dev; + for i in 0..NDIRECT { + let addr = ICACHE.inode[idx].addrs[i]; + if addr != 0 { + bfree(dev, addr); + ICACHE.inode[idx].addrs[i] = 0; + } + } + + let indirect = ICACHE.inode[idx].addrs[NDIRECT]; + if indirect != 0 { + let bp = bio::bread(dev, indirect); + for j in 0..NINDIRECT { + let off = j * 4; + let a = { + let data = &bio::buf_mut(bp).data; + read_u32_le(data, off) + }; + if a != 0 { + bfree(dev, a); + } + } + bio::brelse(bp); + bfree(dev, indirect); + ICACHE.inode[idx].addrs[NDIRECT] = 0; + } + + ICACHE.inode[idx].size = 0; + } + + iupdate(idx); +} + +pub fn iput(idx: usize) { + unsafe { + if idx >= NINODE { + panic!("iput: bad inode index"); } if ICACHE.inode[idx].refcnt < 1 { - panic!("irelease: ref underflow"); + panic!("iput: ref underflow"); + } + + if ICACHE.inode[idx].valid != 0 && ICACHE.inode[idx].nlink == 0 && ICACHE.inode[idx].refcnt == 1 { + itrunc(idx); + ICACHE.inode[idx].type_ = 0; + iupdate(idx); + ICACHE.inode[idx].valid = 0; } + ICACHE.inode[idx].refcnt -= 1; } } +pub fn irelease(idx: usize) { + iput(idx); +} + pub fn iupdate(idx: usize) { unsafe { if idx >= NINODE { @@ -454,7 +525,7 @@ pub fn readi(idx: usize, dst: &mut [u8], off: u32, n: u32) -> i32 { } let ip = &ICACHE.inode[idx]; - if off > ip.size || off.checked_add(n).is_none() { + if off > ip.size || off.checked_add(n).is_none() || ip.nlink < 1 { return -1; } @@ -540,15 +611,78 @@ pub fn inode_inum(idx: usize) -> u32 { } } +pub fn inode_dev(idx: usize) -> u32 { + unsafe { + if idx >= NINODE { + panic!("inode_dev: bad inode index"); + } + ICACHE.inode[idx].dev + } +} + +pub fn inode_type(idx: usize) -> i16 { + unsafe { + if idx >= NINODE { + panic!("inode_type: bad inode index"); + } + ICACHE.inode[idx].type_ + } +} + +pub fn inode_nlink(idx: usize) -> i16 { + unsafe { + if idx >= NINODE { + panic!("inode_nlink: bad inode index"); + } + ICACHE.inode[idx].nlink + } +} + +pub fn inode_size(idx: usize) -> u32 { + unsafe { + if idx >= NINODE { + panic!("inode_size: bad inode index"); + } + ICACHE.inode[idx].size + } +} + +pub fn inode_set_meta(idx: usize, major: i16, minor: i16, nlink: i16) { + unsafe { + if idx >= NINODE { + panic!("inode_set_meta: bad inode index"); + } + ICACHE.inode[idx].major = major; + ICACHE.inode[idx].minor = minor; + ICACHE.inode[idx].nlink = nlink; + } +} + +pub fn inode_inc_nlink(idx: usize) { + unsafe { + if idx >= NINODE { + panic!("inode_inc_nlink: bad inode index"); + } + ICACHE.inode[idx].nlink += 1; + } +} + +pub fn inode_dec_nlink(idx: usize) { + unsafe { + if idx >= NINODE { + panic!("inode_dec_nlink: bad inode index"); + } + ICACHE.inode[idx].nlink -= 1; + } +} + pub fn namecmp(s: &str, t: &[u8; DIRSIZ]) -> bool { name_to_dirsiz(s) == *t } pub fn dirlookup(dp_idx: usize, name: &str, mut poff: Option<&mut u32>) -> Option { - unsafe { - if dp_idx >= NINODE { - panic!("dirlookup: bad inode index"); - } + if dp_idx >= NINODE { + panic!("dirlookup: bad inode index"); } iread(dp_idx); @@ -580,7 +714,7 @@ pub fn dirlookup(dp_idx: usize, name: &str, mut poff: Option<&mut u32>) -> Optio pub fn dirlink(dp_idx: usize, name: &str, inum: u32) -> i32 { if let Some(ip_idx) = dirlookup(dp_idx, name, None) { - irelease(ip_idx); + iput(ip_idx); return -1; } @@ -642,7 +776,7 @@ fn skipelem(path: &[u8], mut i: usize, name: &mut [u8; DIRSIZ]) -> Option fn namex(path: &str, nameiparent: bool, name: &mut [u8; DIRSIZ]) -> Option { let bytes = path.as_bytes(); let mut path_idx = 0usize; - let mut ip = iget(crate::param::ROOTDEV, ROOTINO); + let mut ip = iget(ROOTDEV, ROOTINO); while let Some(next_idx) = skipelem(bytes, path_idx, name) { path_idx = next_idx; @@ -650,7 +784,7 @@ fn namex(path: &str, nameiparent: bool, name: &mut [u8; DIRSIZ]) -> Option Option x, None => { - irelease(ip); + iput(ip); return None; } }; - irelease(ip); + iput(ip); ip = next; } if nameiparent { - irelease(ip); + iput(ip); return None; } Some(ip) diff --git a/src/lib.rs b/src/lib.rs index 42b72f5..db5d4d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,8 @@ mod buf; mod bio; mod ide; mod fs; +mod file; +mod fcntl; use crate::traps::*; #[macro_export] @@ -38,12 +40,6 @@ fn halt() -> ! { } } -fn print_bytes(bytes: &[u8]) { - for &ch in bytes { - console::consputc(ch as char); - } -} - fn print_cstr(bytes: &[u8]) { for &ch in bytes { if ch == 0 { @@ -54,56 +50,46 @@ fn print_cstr(bytes: &[u8]) { } fn welcome() { - let root = fs::namei("/").unwrap_or_else(|| panic!("root not found")); - fs::iread(root); - let foodir = match fs::dirlookup(root, "foo", None) { - Some(idx) => idx, - None => { - println!("/foo not found. Creating!"); - let idx = fs::ialloc(param::ROOTDEV, fs::T_DIR); - fs::iread(idx); - let ino = fs::inode_inum(idx); - if fs::dirlink(idx, ".", ino) < 0 { - panic!("failed to link . in /foo"); - } - if fs::dirlink(idx, "..", ino) < 0 { - panic!("failed to link .. in /foo"); - } - if fs::dirlink(root, "foo", ino) < 0 { - panic!("failed to link /foo in root"); - } - idx - } - }; - - let wtxt = match fs::namei("/foo/greet.txt") { - Some(idx) => idx, - None => { - println!("/foo/greet.txt not found. Creating!"); - let wtxt_orig = - fs::namei("/welcome.txt").unwrap_or_else(|| panic!("/welcome.txt missing")); - let inum = fs::inode_inum(wtxt_orig); - if fs::dirlink(foodir, "greet.txt", inum) < 0 { - panic!("failed to link greet.txt in /foo"); - } - fs::irelease(wtxt_orig); - fs::namei("/foo/greet.txt").unwrap_or_else(|| panic!("greet.txt lookup failed")) - } - }; + let _ = file::create("/foo", fs::T_DIR, 0, 0); - fs::iread(wtxt); - let mut st = fs::Stat::new(); - fs::stati(wtxt, &mut st); + let gtxt = file::open("/foo/hello.txt", fcntl::O_CREATE | fcntl::O_WRONLY) + .unwrap_or_else(|| panic!("failed to create /foo/hello.txt")); + let n = file::filewrite(gtxt, b"hello\0", 6); + println!("Wrote {} characters to /foo/hello.txt", n); + file::fileclose(gtxt); - let mut greet = [0u8; 512]; - let n = fs::readi(wtxt, &mut greet, 0, st.size); - println!("Read {} bytes from /foo/greet.txt", n); - print_bytes(&greet[..n as usize]); + let gtxt = file::open("/foo/hello.txt", fcntl::O_RDONLY) + .unwrap_or_else(|| panic!("unable to open /foo/hello.txt")); + let mut welcome = [0u8; 512]; + let n = file::fileread(gtxt, &mut welcome, 6); + println!("Read {} chars from /foo/hello.txt: ", n); + print_cstr(&welcome); console::consputc('\n'); + file::fileclose(gtxt); + + let mut name = [0u8; fs::DIRSIZ]; + if file::unlink("/foo/hello.txt", &mut name) < 0 { + panic!("failed to unlink /foo/hello.txt"); + } + + let foo = fs::namei("/foo").unwrap_or_else(|| panic!("unable to open /foo")); + if !file::isdirempty(foo) { + panic!("/foo should be empty"); + } + fs::iput(foo); + + if let Some(f) = file::open("/foo/hello.txt", fcntl::O_RDONLY) { + file::fileclose(f); + panic!("could open /foo/hello.txt after unlinking"); + } - fs::irelease(wtxt); - fs::irelease(foodir); - fs::irelease(root); + let wtxt = + file::open("/welcome.txt", fcntl::O_RDONLY).unwrap_or_else(|| panic!("unable to open /welcome.txt")); + let welcome_cap = welcome.len() as i32; + let n = file::fileread(wtxt, &mut welcome, welcome_cap); + println!("Read {} chars from /welcome.txt:", n); + print_cstr(&welcome); + file::fileclose(wtxt); } extern "C" { @@ -123,6 +109,7 @@ pub extern "C" fn entryofrust() -> ! { idtinit(); x86::sti(); fs::iinit(param::ROOTDEV); + file::fileinit(); welcome(); loop { diff --git a/src/mkfs.rs b/src/mkfs.rs new file mode 100644 index 0000000..6363752 --- /dev/null +++ b/src/mkfs.rs @@ -0,0 +1,358 @@ +use std::env; +use std::fs::{File, OpenOptions}; +use std::io::{Read, Seek, SeekFrom, Write}; +use std::mem::size_of; +use std::path::Path; + +// File system constants — must match param.h / fs.h from C repo p9-name-layer +const BSIZE: usize = 512; +const FSSIZE: u32 = 1000; +const NINODES: u32 = 200; +const LOGSIZE: u32 = 0; + +const ROOTINO: u32 = 1; +const NDIRECT: usize = 12; +const NINDIRECT: usize = BSIZE / size_of::(); +const MAXFILE: usize = NDIRECT + NINDIRECT; +const DIRSIZ: usize = 14; + +const T_DIR: u16 = 1; +const T_FILE: u16 = 2; + +#[repr(C)] +#[derive(Clone, Copy, Default)] +struct Superblock { + size: u32, + nblocks: u32, + ninodes: u32, + nlog: u32, + logstart: u32, + inodestart: u32, + bmapstart: u32, +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct Dinode { + typ: u16, + major: u16, + minor: u16, + nlink: u16, + size: u32, + addrs: [u32; NDIRECT + 1], +} + +impl Default for Dinode { + fn default() -> Self { + Self { + typ: 0, + major: 0, + minor: 0, + nlink: 0, + size: 0, + addrs: [0; NDIRECT + 1], + } + } +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct Dirent { + inum: u16, + name: [u8; DIRSIZ], +} + +impl Default for Dirent { + fn default() -> Self { + Self { + inum: 0, + name: [0; DIRSIZ], + } + } +} + +struct Mkfs { + fsfd: File, + sb: Superblock, + freeinode: u32, + freeblock: u32, +} + +fn as_bytes(val: &T) -> &[u8] { + unsafe { std::slice::from_raw_parts((val as *const T).cast::(), size_of::()) } +} + +fn from_bytes(bytes: &[u8]) -> T { + assert_eq!(bytes.len(), size_of::()); + let mut out = std::mem::MaybeUninit::::uninit(); + unsafe { + std::ptr::copy_nonoverlapping(bytes.as_ptr(), out.as_mut_ptr().cast::(), bytes.len()); + out.assume_init() + } +} + +fn name_to_dirent(name: &str, inum: u16) -> Dirent { + let mut de = Dirent { + inum: inum.to_le(), + ..Default::default() + }; + let name_bytes = name.as_bytes(); + let copy_len = name_bytes.len().min(DIRSIZ); + de.name[..copy_len].copy_from_slice(&name_bytes[..copy_len]); + de +} + +impl Mkfs { + fn new(fsfd: File, sb: Superblock, freeblock: u32) -> Self { + Self { + fsfd, + sb, + freeinode: 1, + freeblock, + } + } + + fn wsect(&mut self, sec: u32, buf: &[u8; BSIZE]) { + let off = (sec as u64) * (BSIZE as u64); + self.fsfd + .seek(SeekFrom::Start(off)) + .expect("lseek(write) failed"); + self.fsfd.write_all(buf).expect("write failed"); + } + + fn rsect(&mut self, sec: u32, buf: &mut [u8; BSIZE]) { + let off = (sec as u64) * (BSIZE as u64); + self.fsfd + .seek(SeekFrom::Start(off)) + .expect("lseek(read) failed"); + self.fsfd.read_exact(buf).expect("read failed"); + } + + fn iblock(&self, inum: u32) -> u32 { + (inum / ipb() as u32) + u32::from_le(self.sb.inodestart) + } + + fn winode(&mut self, inum: u32, ip: &Dinode) { + let mut buf = [0u8; BSIZE]; + let bn = self.iblock(inum); + self.rsect(bn, &mut buf); + + let off = (inum as usize % ipb()) * size_of::(); + let src = as_bytes(ip); + buf[off..off + size_of::()].copy_from_slice(src); + self.wsect(bn, &buf); + } + + fn rinode(&mut self, inum: u32) -> Dinode { + let mut buf = [0u8; BSIZE]; + let bn = self.iblock(inum); + self.rsect(bn, &mut buf); + + let off = (inum as usize % ipb()) * size_of::(); + from_bytes::(&buf[off..off + size_of::()]) + } + + fn ialloc(&mut self, typ: u16) -> u32 { + let inum = self.freeinode; + self.freeinode += 1; + + let din = Dinode { + typ: typ.to_le(), + nlink: 1u16.to_le(), + size: 0u32.to_le(), + ..Default::default() + }; + self.winode(inum, &din); + inum + } + + fn balloc(&mut self, used: u32) { + assert!(used < (BSIZE * 8) as u32); + println!("balloc: first {} blocks have been allocated", used); + + let mut buf = [0u8; BSIZE]; + for i in 0..used { + let idx = (i / 8) as usize; + let bit = (i % 8) as u8; + buf[idx] |= 1u8 << bit; + } + + let bmapstart = u32::from_le(self.sb.bmapstart); + println!("balloc: write bitmap block at sector {}", bmapstart); + self.wsect(bmapstart, &buf); + } + + fn iappend(&mut self, inum: u32, mut p: &[u8]) { + let mut din = self.rinode(inum); + let mut off = u32::from_le(din.size); + + while !p.is_empty() { + let fbn = (off as usize) / BSIZE; + assert!(fbn < MAXFILE); + + let x: u32; + if fbn < NDIRECT { + if u32::from_le(din.addrs[fbn]) == 0 { + din.addrs[fbn] = self.freeblock.to_le(); + self.freeblock += 1; + } + x = u32::from_le(din.addrs[fbn]); + } else { + if u32::from_le(din.addrs[NDIRECT]) == 0 { + din.addrs[NDIRECT] = self.freeblock.to_le(); + self.freeblock += 1; + } + + let mut indirect_sector = [0u8; BSIZE]; + let indirect_blockno = u32::from_le(din.addrs[NDIRECT]); + self.rsect(indirect_blockno, &mut indirect_sector); + + let indirect_idx = fbn - NDIRECT; + let byte_off = indirect_idx * size_of::(); + let mut indirect_entry = u32::from_le(from_bytes::( + &indirect_sector[byte_off..byte_off + size_of::()], + )); + + if indirect_entry == 0 { + indirect_entry = self.freeblock; + self.freeblock += 1; + let le = indirect_entry.to_le(); + indirect_sector[byte_off..byte_off + size_of::()] + .copy_from_slice(as_bytes(&le)); + self.wsect(indirect_blockno, &indirect_sector); + } + + x = indirect_entry; + } + + let mut buf = [0u8; BSIZE]; + self.rsect(x, &mut buf); + + let n1 = p + .len() + .min((fbn as u32 + 1) as usize * BSIZE - off as usize); + let block_off = off as usize - (fbn * BSIZE); + buf[block_off..block_off + n1].copy_from_slice(&p[..n1]); + self.wsect(x, &buf); + + off += n1 as u32; + p = &p[n1..]; + } + + din.size = off.to_le(); + self.winode(inum, &din); + } +} + +fn ipb() -> usize { + BSIZE / size_of::() +} + +fn main() { + assert_eq!(size_of::(), 4, "Integers must be 4 bytes"); + assert_eq!(BSIZE % size_of::(), 0); + assert_eq!(BSIZE % size_of::(), 0); + + let mut args = env::args().collect::>(); + if args.len() < 2 { + eprintln!("Usage: mkfs fs.img files..."); + std::process::exit(1); + } + + let fs_img = args.remove(1); + + let nbitmap = FSSIZE / (BSIZE as u32 * 8) + 1; + let ninodeblocks = NINODES / ipb() as u32 + 1; + let nlog = LOGSIZE; + let nmeta = 2 + nlog + ninodeblocks + nbitmap; + let nblocks = FSSIZE - nmeta; + + let sb = Superblock { + size: FSSIZE.to_le(), + nblocks: nblocks.to_le(), + ninodes: NINODES.to_le(), + nlog: nlog.to_le(), + logstart: 2u32.to_le(), + inodestart: (2 + nlog).to_le(), + bmapstart: (2 + nlog + ninodeblocks).to_le(), + }; + + println!( + "nmeta {} (boot, super, log blocks {} inode blocks {}, bitmap blocks {}) blocks {} total {}", + nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE + ); + + let fsfd = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open(&fs_img) + .unwrap_or_else(|e| { + eprintln!("{}: {}", fs_img, e); + std::process::exit(1); + }); + + let mut mkfs = Mkfs::new(fsfd, sb, nmeta); + + let zeroes = [0u8; BSIZE]; + for i in 0..FSSIZE { + mkfs.wsect(i, &zeroes); + } + + let mut sb_buf = [0u8; BSIZE]; + sb_buf[..size_of::()].copy_from_slice(as_bytes(&mkfs.sb)); + mkfs.wsect(1, &sb_buf); + + let rootino = mkfs.ialloc(T_DIR); + assert_eq!(rootino, ROOTINO); + + let dot = name_to_dirent(".", rootino as u16); + mkfs.iappend(rootino, as_bytes(&dot)); + + let dotdot = name_to_dirent("..", rootino as u16); + mkfs.iappend(rootino, as_bytes(&dotdot)); + + for path in &args[1..] { + if path.contains('/') { + eprintln!("{}: must be a basename (no /)", path); + std::process::exit(1); + } + + let mut host_file = File::open(path).unwrap_or_else(|e| { + eprintln!("{}: {}", path, e); + std::process::exit(1); + }); + + let file_name = if let Some(stripped) = path.strip_prefix('_') { + stripped + } else { + Path::new(path) + .file_name() + .and_then(|s| s.to_str()) + .unwrap_or(path) + }; + + let inum = mkfs.ialloc(T_FILE); + let de = name_to_dirent(file_name, inum as u16); + mkfs.iappend(rootino, as_bytes(&de)); + + let mut buf = [0u8; BSIZE]; + loop { + let cc = host_file.read(&mut buf).expect("read input file failed"); + if cc == 0 { + break; + } + mkfs.iappend(inum, &buf[..cc]); + } + } + + // fix size of root inode dir + let mut din = mkfs.rinode(rootino); + let mut off = u32::from_le(din.size); + off = ((off / BSIZE as u32) + 1) * BSIZE as u32; + din.size = off.to_le(); + mkfs.winode(rootino, &din); + + mkfs.balloc(mkfs.freeblock); +} \ No newline at end of file diff --git a/src/param.rs b/src/param.rs index 63a19c7..7c8176a 100644 --- a/src/param.rs +++ b/src/param.rs @@ -1,4 +1,6 @@ pub const KSTACKSIZE: usize = 4096; // size of per-process kernel stack pub const NCPU: usize = 8; // maximum number of CPUs +pub const MAXOPBLOCKS: usize = 10; // max # of blocks any FS op writes +pub const NFILE: usize = 100; // open files per system pub const NINODE: usize = 50; // maximum number of active i-nodes pub const ROOTDEV: u32 = 1; // device number of file system root disk From 08c2e4e733ddc4c4c91b25c5629e16e50cf72604 Mon Sep 17 00:00:00 2001 From: Anoop Singh Date: Tue, 17 Feb 2026 18:47:11 +0530 Subject: [PATCH 5/5] cleaned --- Makefile | 7 +-- README | 5 -- README.md | 50 ++++++++++++++++++++ src/Makefile | 130 --------------------------------------------------- 4 files changed, 54 insertions(+), 138 deletions(-) delete mode 100644 README create mode 100644 README.md delete mode 100644 src/Makefile diff --git a/Makefile b/Makefile index 9397bcd..f383e75 100644 --- a/Makefile +++ b/Makefile @@ -10,9 +10,11 @@ RS = src/*.rs # Try to infer the correct TOOLPREFIX if not set ifndef TOOLPREFIX -TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \ +TOOLPREFIX := $(shell if command -v i386-jos-elf-gcc >/dev/null 2>&1 && i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \ then echo 'i386-jos-elf-'; \ - elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \ + elif command -v i686-elf-gcc >/dev/null 2>&1 && i686-elf-objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \ + then echo 'i686-elf-'; \ + elif command -v gcc >/dev/null 2>&1 && objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \ then echo ''; \ else echo "***" 1>&2; \ echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \ @@ -23,7 +25,6 @@ TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/d echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ echo "***" 1>&2; exit 1; fi) endif -TOOLPREFIX=i686-elf- # If the makefile can't find QEMU, specify its path here # QEMU = qemu-system-i386 diff --git a/README b/README deleted file mode 100644 index 14ca193..0000000 --- a/README +++ /dev/null @@ -1,5 +0,0 @@ -For the COL331 course - - -1. The next step to make things look good is to remove perl script , and that may be done using a separate linker script. -2. Or may be use inclbin inside assembly. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1305e65 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# COL331RS: p11-file-layer (Rust) + +This branch ports the `p11-file-layer` stage from the C repo (`codenet/col331`) into Rust in `codenet/col331rs` style. + +## What This Branch Adds + +- Rust file descriptor layer (`src/file.rs`): + - `filealloc`, `filedup`, `fileclose`, `filestat`, `fileread`, `filewrite` + - pathname/file ops: `open`, `create`, `unlink`, `isdirempty` +- Open-mode constants (`src/fcntl.rs`): + - `O_RDONLY`, `O_WRONLY`, `O_RDWR`, `O_CREATE` +- File-system lifecycle changes in `src/fs.rs`: + - block free path (`bfree`) + - inode truncation (`itrunc`) + - reference-drop semantics (`iput`) matching p11 behavior + - updated read/path behavior to work with unlink + final `iput` +- Rust `mkfs` integration (`src/mkfs.rs`): + - no dependency on external `../col331/mkfs.c` +- Boot demo update (`src/lib.rs`): + - creates `/foo/hello.txt`, writes and reads it, unlinks it, verifies directory emptiness + - reads `/welcome.txt` through the file layer + +## Build And Run + +Make sure your cross-toolchain is available (for example `TOOLPREFIX=i686-elf-`). + +```bash +make clean +make xv6.img fs.img +make qemu +``` + +## Source Alignment + +- C reference branch: `codenet/col331` -> `p11-file-layer` +- Rust base used: p10 Rust work, then extended to p11 file layer behavior + +## Credits + +This branch reuses and builds on earlier COL331RS community work: + +- **Amber-Agarwal** (`refs/pull/7/head` in `codenet/col331rs`) + - p10-level Rust filesystem write path and directory/name operation groundwork +- **Nipun Goel** (aka **sudoheckbeluga**, `refs/pull/5/head`) + - early Rust `mkfs` implementation used as the basis for later versions +- **legends1307** (`refs/pull/12/head`) + - self-contained Rust `mkfs` adaptation (`FSSIZE=1000`, `LOGSIZE=0`) and related integration improvements +- **COL331 course/staff repository authors** (`codenet/col331`, `codenet/col331rs`) + - original xv6 step-by-step structure and assignment progression + diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index 6061c30..0000000 --- a/src/Makefile +++ /dev/null @@ -1,130 +0,0 @@ -OBJS = entry.o vectors.o trapasm.o -RS = src/*.rs - -# Cross-compiling (e.g., on Mac OS X) -#TOOLPREFIX = i386-jos-elf -#TOOLPREFIX = i386-elf- - -# Using native tools (e.g., on X86 Linux) -#TOOLPREFIX = - -# Try to infer the correct TOOLPREFIX if not set -ifndef TOOLPREFIX -TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \ - then echo 'i386-jos-elf-'; \ - elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \ - then echo ''; \ - else echo "***" 1>&2; \ - echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \ - echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \ - echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \ - echo "*** prefix other than 'i386-jos-elf-', set your TOOLPREFIX" 1>&2; \ - echo "*** environment variable to that prefix and run 'make' again." 1>&2; \ - echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ - echo "***" 1>&2; exit 1; fi) -endif - -# If the makefile can't find QEMU, specify its path here -# QEMU = qemu-system-i386 - -# Try to infer the correct QEMU -ifndef QEMU -QEMU = $(shell if which qemu > /dev/null; \ - then echo qemu; exit; \ - elif which qemu-system-i386 > /dev/null; \ - then echo qemu-system-i386; exit; \ - elif which qemu-system-x86_64 > /dev/null; \ - then echo qemu-system-x86_64; exit; \ - else \ - qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \ - if test -x $$qemu; then echo $$qemu; exit; fi; fi; \ - echo "***" 1>&2; \ - echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \ - echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \ - echo "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; \ - echo "***" 1>&2; exit 1) -endif - -CC = $(TOOLPREFIX)gcc -AS = $(TOOLPREFIX)gas -LD = $(TOOLPREFIX)ld -OBJCOPY = $(TOOLPREFIX)objcopy -OBJDUMP = $(TOOLPREFIX)objdump -CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer -CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) -ASFLAGS = -m32 -gdwarf-2 -Wa,-divide -LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null | head -n 1) - -# Disable PIE when possible (for Ubuntu 16.10 toolchain) -ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),) -CFLAGS += -fno-pie -no-pie -endif -ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie'),) -CFLAGS += -fno-pie -nopie -endif - -# Disk image with bootblock + kernel -xv6.img: bootblock kernel - dd if=/dev/zero of=xv6.img count=10000 - dd if=bootblock of=xv6.img conv=notrunc - dd if=kernel of=xv6.img seek=1 conv=notrunc - -mkfs: mkfs.rs - rustc -W warnings -o mkfs mkfs.rs - -fs: fs.img - -fs.img: mkfs ../welcome.txt - ./mkfs fs.img ../welcome.txt - -bootblock: bootasm.S bootmain.c - $(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c - $(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S - $(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o - $(OBJDUMP) -S -D bootblock.o > bootblock.asm - $(OBJCOPY) -S -O binary -j .text bootblock.o bootblock - perl sign.pl bootblock - -kernel.a: $(RS) - cargo rustc -Z build-std=core -Z build-std-features=compiler-builtins-mem -Z json-target-spec --target ./targets/i686.json --lib --release -- -A warnings --emit link=kernel.a - - -kernel: kernel.a $(OBJS) ./linkers/kernel.ld - $(LD) -m elf_i386 -T ./linkers/kernel.ld -o kernel $(OBJS) kernel.a - $(OBJDUMP) -S -D kernel > kernel.asm - $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym - -vectors.S: vectors.pl - ./vectors.pl > vectors.S - -.PRECIOUS: %.o --include *.d - -clean: - rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ - *.a *.o *.d *.asm *.sym bootblock kernel xv6.img fs.img .gdbinit vectors.S mkfs - rm -rf target - -# run in emulators -GDBPORT = $(shell expr `id -u` % 5000 + 25000) -QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ - then echo "-gdb tcp::$(GDBPORT)"; \ - else echo "-s -p $(GDBPORT)"; fi) -ifndef CPUS - CPUS := 1 -endif - -# Attach fs.img as disk1 (index=1), like the C version -QEMUOPTS = -drive file=xv6.img,index=0,media=disk,format=raw \ - -drive file=fs.img,index=1,media=disk,format=raw \ - -smp $(CPUS) -m 512 $(QEMUEXTRA) - -qemu: xv6.img fs.img - $(QEMU) -nographic $(QEMUOPTS) - -.gdbinit: .gdbinit.tmpl - sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ - -qemu-gdb: xv6.img fs .gdbinit - @echo "*** Now run 'gdb'." 1>&2 - $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB)