From 9b7eb552d0c621718f70268e36f8d010f8249289 Mon Sep 17 00:00:00 2001 From: Garen Tyler Date: Sat, 28 Oct 2023 22:16:35 -0600 Subject: [PATCH] Rewrite file.c into fs::file --- kernel/Makefile | 1 - kernel/file.c | 182 ---------------- kernel/rustkernel/src/console/mod.rs | 110 +++++----- kernel/rustkernel/src/fs/file.rs | 312 ++++++++++++++++++++++++++- kernel/rustkernel/src/fs/log.rs | 27 +++ kernel/rustkernel/src/fs/mod.rs | 27 ++- kernel/rustkernel/src/fs/stat.rs | 21 ++ kernel/rustkernel/src/io/pipe.rs | 9 +- 8 files changed, 440 insertions(+), 249 deletions(-) delete mode 100644 kernel/file.c create mode 100644 kernel/rustkernel/src/fs/log.rs create mode 100644 kernel/rustkernel/src/fs/stat.rs diff --git a/kernel/Makefile b/kernel/Makefile index b875fb3..93c9307 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -8,7 +8,6 @@ KERNEL_SOURCES = \ bio.c \ fs.c \ log.c \ - file.c \ pipe.c \ exec.c \ sysfile.c \ diff --git a/kernel/file.c b/kernel/file.c deleted file mode 100644 index 25fa226..0000000 --- a/kernel/file.c +++ /dev/null @@ -1,182 +0,0 @@ -// -// Support functions for system calls that involve file descriptors. -// - -#include "types.h" -#include "riscv.h" -#include "defs.h" -#include "param.h" -#include "fs.h" -#include "spinlock.h" -#include "sleeplock.h" -#include "file.h" -#include "stat.h" -#include "proc.h" - -struct devsw devsw[NDEV]; -struct { - struct spinlock lock; - struct file file[NFILE]; -} ftable; - -void -fileinit(void) -{ - initlock(&ftable.lock, "ftable"); -} - -// Allocate a file structure. -struct file* -filealloc(void) -{ - struct file *f; - - acquire(&ftable.lock); - for(f = ftable.file; f < ftable.file + NFILE; f++){ - if(f->ref == 0){ - f->ref = 1; - release(&ftable.lock); - return f; - } - } - release(&ftable.lock); - return 0; -} - -// Increment ref count for file f. -struct file* -filedup(struct file *f) -{ - acquire(&ftable.lock); - if(f->ref < 1) - panic("filedup"); - f->ref++; - release(&ftable.lock); - return f; -} - -// Close file f. (Decrement ref count, close when reaches 0.) -void -fileclose(struct file *f) -{ - struct file ff; - - acquire(&ftable.lock); - if(f->ref < 1) - panic("fileclose"); - if(--f->ref > 0){ - release(&ftable.lock); - return; - } - ff = *f; - f->ref = 0; - f->type = FD_NONE; - release(&ftable.lock); - - if(ff.type == FD_PIPE){ - pipeclose(ff.pipe, ff.writable); - } else if(ff.type == FD_INODE || ff.type == FD_DEVICE){ - begin_op(); - iput(ff.ip); - end_op(); - } -} - -// Get metadata about file f. -// addr is a user virtual address, pointing to a struct stat. -int -filestat(struct file *f, uint64 addr) -{ - struct proc *p = myproc(); - struct stat st; - - if(f->type == FD_INODE || f->type == FD_DEVICE){ - ilock(f->ip); - stati(f->ip, &st); - iunlock(f->ip); - if(copyout(p->pagetable, addr, (char *)&st, sizeof(st)) < 0) - return -1; - return 0; - } - return -1; -} - -// Read from file f. -// addr is a user virtual address. -int -fileread(struct file *f, uint64 addr, int n) -{ - int r = 0; - - if(f->readable == 0) - return -1; - - if(f->type == FD_PIPE){ - r = piperead(f->pipe, addr, n); - } else if(f->type == FD_DEVICE){ - if(f->major < 0 || f->major >= NDEV || !devsw[f->major].read) - return -1; - r = devsw[f->major].read(1, addr, n); - } else if(f->type == FD_INODE){ - ilock(f->ip); - if((r = readi(f->ip, 1, addr, f->off, n)) > 0) - f->off += r; - iunlock(f->ip); - } else { - panic("fileread"); - } - - return r; -} - -// Write to file f. -// addr is a user virtual address. -int -filewrite(struct file *f, uint64 addr, int n) -{ - int r, ret = 0; - - if(f->writable == 0) - return -1; - - if(f->type == FD_PIPE){ - ret = pipewrite(f->pipe, addr, n); - } else if(f->type == FD_DEVICE){ - if(f->major < 0 || f->major >= NDEV || !devsw[f->major].write) - return -1; - ret = devsw[f->major].write(1, addr, n); - } else if(f->type == FD_INODE){ - // write a few blocks at a time to avoid exceeding - // the maximum log transaction size, including - // i-node, indirect block, allocation blocks, - // and 2 blocks of slop for non-aligned writes. - // this really belongs lower down, since writei() - // might be writing a device like the console. - int max = ((MAXOPBLOCKS-1-1-2) / 2) * BSIZE; - int i = 0; - while(i < n){ - int n1 = n - i; - if(n1 > max) - n1 = max; - - begin_op(); - ilock(f->ip); - if ((r = writei(f->ip, 1, addr + i, f->off, n1)) > 0) - f->off += r; - iunlock(f->ip); - end_op(); - - if(r != n1){ - // error from writei - break; - } - i += r; - } - ret = (i == n ? n : -1); - } else { - panic("filewrite"); - } - - return ret; -} - diff --git a/kernel/rustkernel/src/console/mod.rs b/kernel/rustkernel/src/console/mod.rs index 65314b6..fc387fd 100644 --- a/kernel/rustkernel/src/console/mod.rs +++ b/kernel/rustkernel/src/console/mod.rs @@ -84,18 +84,19 @@ pub fn consputc(c: u8) { } /// User write()s to the console go here. -#[no_mangle] -pub unsafe extern "C" fn consolewrite(user_src: i32, src: u64, n: i32) -> i32 { - for i in 0..n { - let mut c = 0i8; +pub fn consolewrite(user_src: i32, src: u64, n: i32) -> i32 { + unsafe { + for i in 0..n { + let mut c = 0i8; - if either_copyin(addr_of_mut!(c).cast(), user_src, src + i as u64, 1) == -1 { - return i; - } else { - uartputc(c as u8); + if either_copyin(addr_of_mut!(c).cast(), user_src, src + i as u64, 1) == -1 { + return i; + } else { + uartputc(c as u8); + } } + 0 } - 0 } /// User read()s from the console go here. @@ -103,59 +104,60 @@ pub unsafe extern "C" fn consolewrite(user_src: i32, src: u64, n: i32) -> i32 { /// Copy (up to) a whole input line to dst. /// user_dst indicates whether dst is a user /// or kernel address. -#[no_mangle] -pub unsafe extern "C" fn consoleread(user_dst: i32, mut dst: u64, mut n: i32) -> i32 { - let target = n; - let mut c; - let mut cbuf; +pub fn consoleread(user_dst: i32, mut dst: u64, mut n: i32) -> i32 { + unsafe { + let target = n; + let mut c; + let mut cbuf; - let mut console = cons.lock(); + let mut console = cons.lock(); - while n > 0 { - // Wait until interrupt handler has put - // some input into cons.buffer. - while console.read_index == console.write_index { - if killed(myproc()) != 0 { - // cons.lock.unlock(); - return -1; + while n > 0 { + // Wait until interrupt handler has put + // some input into cons.buffer. + while console.read_index == console.write_index { + if killed(myproc()) != 0 { + // cons.lock.unlock(); + return -1; + } + // let channel = addr_of_mut!(console.read_index).cast(); + // console.sleep(channel); + sleep_mutex(addr_of_mut!(console.read_index).cast(), &mut console); } - // let channel = addr_of_mut!(console.read_index).cast(); - // console.sleep(channel); - sleep_mutex(addr_of_mut!(console.read_index).cast(), &mut console); - } - c = *console.read_byte(); - console.read_index += 1; + c = *console.read_byte(); + console.read_index += 1; - // ctrl-D or EOF - if c == ctrl_x(b'D') { - if n < target { - // Save ctrl-D for next time, to make - // sure caller gets a 0-byte result. - console.read_index -= 1; + // ctrl-D or EOF + if c == ctrl_x(b'D') { + if n < target { + // Save ctrl-D for next time, to make + // sure caller gets a 0-byte result. + console.read_index -= 1; + } + break; + } + + // Copy the input byte to the user-space buffer. + cbuf = c; + if either_copyout(user_dst, dst, addr_of_mut!(cbuf).cast(), 1) == -1 { + break; + } + + dst += 1; + n -= 1; + + if c == b'\n' { + // A whole line has arrived, + // return to the user-level read(). + break; } - break; } - // Copy the input byte to the user-space buffer. - cbuf = c; - if either_copyout(user_dst, dst, addr_of_mut!(cbuf).cast(), 1) == -1 { - break; - } + // cons.lock.unlock(); - dst += 1; - n -= 1; - - if c == b'\n' { - // A whole line has arrived, - // return to the user-level read(). - break; - } + target - n } - - // cons.lock.unlock(); - - target - n } pub unsafe fn consoleinit() { @@ -163,8 +165,8 @@ pub unsafe fn consoleinit() { // Connect read and write syscalls // to consoleread and consolewrite. - devsw[CONSOLE].read = consoleread as usize as *const i32; - devsw[CONSOLE].write = consolewrite as usize as *const i32; + devsw[CONSOLE].read = Some(consoleread); + devsw[CONSOLE].write = Some(consolewrite); } /// The console input interrupt handler. diff --git a/kernel/rustkernel/src/fs/file.rs b/kernel/rustkernel/src/fs/file.rs index 70391cb..dcc9445 100644 --- a/kernel/rustkernel/src/fs/file.rs +++ b/kernel/rustkernel/src/fs/file.rs @@ -1,12 +1,312 @@ +//! Support functions for system calls that involve file descriptors. + +use crate::{ + fs::{log, stat::Stat}, + io::pipe::{self, Pipe}, + mem::virtual_memory::copyout, + proc::myproc, + sync::{sleeplock::Sleeplock, spinlock::Spinlock}, +}; +use core::{ + ffi::CStr, + ptr::{addr_of_mut, null_mut}, +}; + #[repr(C)] -pub struct Devsw { - pub read: *const i32, - pub write: *const i32, +#[derive(Copy, Clone, PartialEq, Default)] +pub enum FileType { + #[default] + None, + Pipe, + Inode, + Device, } +#[repr(C)] +#[derive(Copy, Clone)] +pub struct File { + kind: FileType, + /// Reference count. + references: i32, + readable: u8, + writable: u8, + /// FileType::Pipe + pipe: *mut Pipe, + /// FileType::Inode and FileType::Device + ip: *mut Inode, + /// FileType::Inode + off: u32, + /// FileType::Device + major: i16, +} +impl File { + pub const unsafe fn uninitialized() -> File { + File { + kind: FileType::None, + references: 0, + readable: 0, + writable: 0, + pipe: null_mut(), + ip: null_mut(), + off: 0, + major: 0, + } + } +} + +#[repr(C)] +pub struct Inode { + /// Device number. + device: u32, + /// Inode number. + inum: u32, + /// Reference count. + references: i32, + + lock: Sleeplock, + /// Inode has been read from disk? + valid: i32, + + // Copy of DiskInode + kind: i16, + major: i16, + minor: i16, + num_links: i16, + size: u32, + addresses: [u32; crate::fs::NDIRECT + 1], +} + +/// Map major device number to device functions. +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Devsw { + pub read: Option i32>, + pub write: Option i32>, +} +impl Devsw { + pub const fn new() -> Devsw { + Devsw { + read: None, + write: None, + } + } +} + +#[repr(C)] +pub struct FileTable { + lock: Spinlock, + files: [File; crate::NFILE], +} + +#[no_mangle] +pub static mut devsw: [Devsw; crate::NDEV] = [Devsw::new(); crate::NDEV]; +#[no_mangle] +pub static mut ftable: FileTable = FileTable { + lock: unsafe { Spinlock::uninitialized() }, + files: unsafe { [File::uninitialized(); crate::NFILE] }, +}; +pub const CONSOLE: usize = 1; + extern "C" { - pub static mut devsw: [Devsw; crate::NDEV]; - pub fn fileinit(); + // pub fn fileinit(); + // pub fn filealloc() -> *mut File; + // pub fn filedup(file: *mut File) -> *mut File; + // pub fn fileclose(file: *mut File); + // pub fn filestat(file: *mut File, addr: u64) -> i32; + // pub fn fileread(file: *mut File, addr: u64, n: i32) -> i32; + // pub fn filewrite(file: *mut File, addr: u64, n: i32) -> i32; } -pub const CONSOLE: usize = 1; +pub unsafe fn fileinit() { + ftable.lock = Spinlock::new( + CStr::from_bytes_with_nul(b"ftable\0") + .unwrap() + .as_ptr() + .cast_mut(), + ); +} + +/// Allocate a file structure. +#[no_mangle] +pub unsafe extern "C" fn filealloc() -> *mut File { + let _guard = ftable.lock.lock(); + + for file in &mut ftable.files { + if file.references == 0 { + file.references = 1; + return addr_of_mut!(*file); + } + } + + null_mut() +} + +/// Increment reference count for file `file`. +#[no_mangle] +pub unsafe extern "C" fn filedup(file: *mut File) -> *mut File { + let _guard = ftable.lock.lock(); + + if (*file).references < 1 { + panic!("filedup"); + } else { + (*file).references += 1; + } + + file +} + +/// Close file `file`. +/// +/// Decrement reference count, and close when reaching 0. +#[no_mangle] +pub unsafe extern "C" fn fileclose(file: *mut File) { + let guard = ftable.lock.lock(); + + if (*file).references < 1 { + panic!("fileclose"); + } + + (*file).references -= 1; + + if (*file).references == 0 { + let f = *file; + (*file).references = 0; + (*file).kind = FileType::None; + core::mem::drop(guard); + + match f.kind { + FileType::Pipe => pipe::pipeclose(f.pipe, f.writable as i32), + FileType::Inode | FileType::Device => { + log::begin_op(); + super::iput(f.ip); + log::end_op(); + } + FileType::None => {} + } + } +} + +/// Get metadata about file `file`. +/// +/// `addr` is a user virtual address, pointing to a Stat. +#[no_mangle] +pub unsafe extern "C" fn filestat(file: *mut File, addr: u64) -> i32 { + let p = myproc(); + let mut stat = Stat::default(); + + if (*file).kind == FileType::Inode || (*file).kind == FileType::Device { + super::ilock((*file).ip); + super::stati((*file).ip, addr_of_mut!(stat)); + super::iunlock((*file).ip); + + if copyout( + (*p).pagetable, + addr, + addr_of_mut!(stat).cast(), + core::mem::size_of::() as u64, + ) < 0 + { + return -1; + } else { + return 0; + } + } + + -1 +} + +/// Read from file `file`. +/// +/// `addr` is a user virtual address. +#[no_mangle] +pub unsafe extern "C" fn fileread(file: *mut File, addr: u64, n: i32) -> i32 { + if (*file).readable == 0 { + return -1; + } + + match (*file).kind { + FileType::Pipe => pipe::piperead((*file).pipe, addr, n), + FileType::Device => { + if (*file).major < 0 || (*file).major >= crate::NDEV as i16 { + return -1; + } + let Some(read) = devsw[(*file).major as usize].read else { + return -1; + }; + + read(1, addr, n) + } + FileType::Inode => { + super::ilock((*file).ip); + let r = super::readi((*file).ip, 1, addr, (*file).off, n as u32); + if r > 0 { + (*file).off += r as u32; + } + super::iunlock((*file).ip); + r + } + _ => panic!("fileread"), + } +} + +/// Write to file `file`. +/// +/// `addr` is as user virtual address. +#[no_mangle] +pub unsafe extern "C" fn filewrite(file: *mut File, addr: u64, n: i32) -> i32 { + if (*file).writable == 0 { + return -1; + } + + match (*file).kind { + FileType::Pipe => pipe::pipewrite((*file).pipe, addr, n), + FileType::Device => { + if (*file).major < 0 || (*file).major >= crate::NDEV as i16 { + return -1; + } + let Some(write) = devsw[(*file).major as usize].write else { + return -1; + }; + + write(1, addr, n) + } + FileType::Inode => { + // Write a few blocks at a time to avoid exceeding + // the maximum log transaction size, including + // inode, indirect block, allocation blocks, + // and 2 blocks of slop for non-aligned writes. + // This really belongs lower down, since writei() + // might be writing a device like the console. + let max = ((crate::MAXOPBLOCKS - 1 - 1 - 2) / 2) * super::BSIZE as usize; + let mut i = 0; + while i < n { + let mut n1 = n - i; + if n1 > max as i32 { + n1 = max as i32; + } + + log::begin_op(); + super::ilock((*file).ip); + let r = super::writei((*file).ip, 1, addr + i as u64, (*file).off, n1 as u32); + if r > 0 { + (*file).off += r as u32; + } + super::iunlock((*file).ip); + log::end_op(); + + if r != n1 { + // Error from writei. + break; + } else { + i += r; + } + } + if i == n { + n + } else { + -1 + } + } + _ => panic!("filewrite"), + } +} diff --git a/kernel/rustkernel/src/fs/log.rs b/kernel/rustkernel/src/fs/log.rs new file mode 100644 index 0000000..3035404 --- /dev/null +++ b/kernel/rustkernel/src/fs/log.rs @@ -0,0 +1,27 @@ +use crate::{fs::Superblock, io::buf::Buffer, sync::spinlock::Spinlock}; + +#[repr(C)] +pub struct LogHeader { + pub n: i32, + pub blocks: [i32; crate::LOGSIZE], +} +#[repr(C)] +pub struct Log { + lock: Spinlock, + start: i32, + size: i32, + /// How many FS syscalls are executing. + outstanding: i32, + /// In commit(), please wait. + committing: i32, + dev: i32, + header: LogHeader, +} + +extern "C" { + pub static mut log: Log; + pub fn initlog(dev: i32, superblock: *mut Superblock); + pub fn begin_op(); + pub fn end_op(); + pub fn log_write(buffer: *mut Buffer); +} diff --git a/kernel/rustkernel/src/fs/mod.rs b/kernel/rustkernel/src/fs/mod.rs index 3d0980d..c944880 100644 --- a/kernel/rustkernel/src/fs/mod.rs +++ b/kernel/rustkernel/src/fs/mod.rs @@ -1,10 +1,14 @@ -//! On-disk file system forma. +//! On-disk file system format. //! Both the kernel and user programs use this header file. pub mod file; +pub mod log; pub mod ramdisk; +pub mod stat; pub mod virtio_disk; +use crate::fs::file::Inode; + // Root inode pub const ROOTINO: u64 = 1; /// Block size. @@ -36,9 +40,9 @@ pub struct Superblock { } pub const FSMAGIC: u32 = 0x10203040; -pub const NDIRECT: u32 = 12; -pub const NINDIRECT: u32 = BSIZE / core::mem::size_of::() as u32; -pub const MAXFILE: u32 = NDIRECT + NINDIRECT; +pub const NDIRECT: usize = 12; +pub const NINDIRECT: usize = BSIZE as usize / core::mem::size_of::(); +pub const MAXFILE: usize = NDIRECT + NINDIRECT; // On-disk inode structure; #[repr(C)] @@ -54,7 +58,7 @@ pub struct DiskInode { /// Size of file (bytes). pub size: u32, /// Data block addresses. - pub addrs: [u32; NDIRECT as usize + 1], + pub addrs: [u32; NDIRECT + 1], } /// Inodes per block. @@ -83,5 +87,18 @@ pub struct DirectoryEntry { } extern "C" { + pub fn fsinit(dev: i32); pub fn iinit(); + pub fn ialloc(dev: u32, kind: i16) -> *mut DiskInode; + pub fn iupdate(ip: *mut DiskInode); + pub fn idup(ip: *mut DiskInode) -> *mut DiskInode; + pub fn ilock(ip: *mut Inode); + pub fn iunlock(ip: *mut Inode); + pub fn iput(ip: *mut Inode); + pub fn iunlockput(ip: *mut DiskInode); + pub fn itrunc(ip: *mut DiskInode); + pub fn stati(ip: *mut Inode, st: *mut stat::Stat); + pub fn readi(ip: *mut Inode, user_dst: i32, dst: u64, off: u32, n: u32) -> i32; + pub fn writei(ip: *mut Inode, user_src: i32, src: u64, off: u32, n: u32) -> i32; + // pub fn namecmp() } diff --git a/kernel/rustkernel/src/fs/stat.rs b/kernel/rustkernel/src/fs/stat.rs new file mode 100644 index 0000000..043173f --- /dev/null +++ b/kernel/rustkernel/src/fs/stat.rs @@ -0,0 +1,21 @@ +#[repr(C)] +pub enum StatType { + Directory = 1, + File, + Device, +} + +#[repr(C)] +#[derive(Default)] +pub struct Stat { + /// FS's disk device. + pub device: i32, + /// Inode number. + pub inode: u32, + /// Type of file. + pub kind: i16, + /// Number of links to file. + pub num_links: i16, + /// Size of file in bytes. + pub size: u64, +} diff --git a/kernel/rustkernel/src/io/pipe.rs b/kernel/rustkernel/src/io/pipe.rs index c6c1aa5..6a97fd7 100644 --- a/kernel/rustkernel/src/io/pipe.rs +++ b/kernel/rustkernel/src/io/pipe.rs @@ -1,4 +1,4 @@ -use crate::sync::spinlock::Spinlock; +use crate::{fs::file::File, sync::spinlock::Spinlock}; use core::ffi::c_char; pub const PIPESIZE: usize = 512usize; @@ -16,3 +16,10 @@ pub struct Pipe { /// Write fd is still open. writeopen: i32, } + +extern "C" { + pub fn pipealloc(a: *mut *mut File, b: *mut *mut File) -> i32; + pub fn pipeclose(pipe: *mut Pipe, writable: i32); + pub fn pipewrite(pipe: *mut Pipe, addr: u64, n: i32) -> i32; + pub fn piperead(pipe: *mut Pipe, addr: u64, n: i32) -> i32; +}