264 lines
6.7 KiB
Rust

//! Support functions for system calls that involve file descriptors.
use super::inode::{iput, readi, stati, writei, Inode, InodeLockGuard};
use crate::{
arch::virtual_memory::copyout,
fs::{log, stat::Stat},
io::pipe::Pipe,
proc::process::Process,
sync::mutex::Mutex,
};
use core::ptr::{addr_of_mut, null_mut};
#[repr(C)]
#[derive(Copy, Clone, PartialEq, Default)]
pub enum FileType {
#[default]
None,
Pipe,
Inode,
Device,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct File {
pub kind: FileType,
/// Reference count.
pub references: i32,
pub readable: u8,
pub writable: u8,
/// FileType::Pipe
pub pipe: *mut Pipe,
/// FileType::Inode and FileType::Device
pub ip: *mut Inode,
/// FileType::Inode
pub off: u32,
/// FileType::Device
pub major: i16,
}
unsafe impl Send for File {}
impl File {
pub const fn uninitialized() -> File {
File {
kind: FileType::None,
references: 0,
readable: 0,
writable: 0,
pipe: null_mut(),
ip: null_mut(),
off: 0,
major: 0,
}
}
}
/// Map major device number to device functions.
#[repr(C)]
#[derive(Copy, Clone, Default)]
pub struct Devsw {
pub read: Option<fn(i32, u64, i32) -> i32>,
pub write: Option<fn(i32, u64, i32) -> i32>,
}
impl Devsw {
pub const fn new() -> Devsw {
Devsw {
read: None,
write: None,
}
}
}
#[no_mangle]
pub static mut devsw: [Devsw; crate::NDEV] = [Devsw::new(); crate::NDEV];
pub static FILES: Mutex<[File; crate::NFILE]> = Mutex::new([File::uninitialized(); crate::NFILE]);
pub const CONSOLE: usize = 1;
/// Allocate a file structure.
#[no_mangle]
pub unsafe extern "C" fn filealloc() -> *mut File {
let mut files = FILES.lock_spinning();
for file in files.as_mut() {
if file.references == 0 {
file.references = 1;
return addr_of_mut!(*file);
}
}
null_mut()
}
/// Increment reference count for file `file`.
pub unsafe fn filedup(file: *mut File) -> *mut File {
let _guard = FILES.lock_spinning();
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 = FILES.lock_spinning();
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 => (*f.pipe).close(f.writable as i32),
FileType::Inode | FileType::Device => {
let _operation = log::LogOperation::new();
iput(f.ip);
}
FileType::None => {}
}
}
}
/// Get metadata about file `file`.
///
/// `addr` is a user virtual address, pointing to a Stat.
pub unsafe fn filestat(file: *mut File, addr: u64) -> i32 {
let proc = Process::current().unwrap();
let mut stat = Stat::default();
if (*file).kind == FileType::Inode || (*file).kind == FileType::Device {
{
let _guard = InodeLockGuard::new((*file).ip.as_mut().unwrap());
stati((*file).ip, addr_of_mut!(stat));
}
if copyout(
proc.pagetable,
addr as usize,
addr_of_mut!(stat).cast(),
core::mem::size_of::<Stat>(),
) < 0
{
return -1;
} else {
return 0;
}
}
-1
}
/// Read from file `file`.
///
/// `addr` is a user virtual address.
pub unsafe fn fileread(file: *mut File, addr: u64, num_bytes: i32) -> i32 {
if (*file).readable == 0 {
return -1;
}
match (*file).kind {
FileType::Pipe => (*(*file).pipe)
.read(addr, num_bytes as usize)
.map(|n| n as i32)
.unwrap_or(-1i32),
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, num_bytes)
}
FileType::Inode => {
let _guard = InodeLockGuard::new((*file).ip.as_mut().unwrap());
let r = readi((*file).ip, 1, addr, (*file).off, num_bytes as u32);
if r > 0 {
(*file).off += r as u32;
}
r
}
_ => panic!("fileread"),
}
}
/// Write to file `file`.
///
/// `addr` is as user virtual address.
pub unsafe fn filewrite(file: *mut File, addr: u64, num_bytes: i32) -> i32 {
if (*file).writable == 0 {
return -1;
}
match (*file).kind {
FileType::Pipe => (*(*file).pipe)
.write(addr, num_bytes as usize)
.map(|n| n as i32)
.unwrap_or(-1i32),
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, num_bytes)
}
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 < num_bytes {
let mut n = num_bytes - i;
if n > max as i32 {
n = max as i32;
}
let r = {
let _operation = log::LogOperation::new();
let _guard = InodeLockGuard::new((*file).ip.as_mut().unwrap());
let r = writei((*file).ip, 1, addr + i as u64, (*file).off, n as u32);
if r > 0 {
(*file).off += r as u32;
}
r
};
if r != n {
// Error from writei.
break;
} else {
i += r;
}
}
if i == num_bytes {
num_bytes
} else {
-1
}
}
_ => panic!("filewrite"),
}
}