Files
rustkernel/kernel/src/fs/operations.rs
2025-06-15 22:26:49 +02:00

420 líneas
13 KiB
Rust

// SPDX-License-Identifier: GPL-2.0
//! Various VFS operations and utilities
use crate::error::{Error, Result};
use crate::sync::Arc;
use crate::memory::{UserPtr, UserSlicePtr};
use alloc::string::String;
use alloc::vec::Vec;
/// Address space operations trait - similar to Linux address_space_operations
pub trait AddressSpaceOperations: Send + Sync {
/// Write a page
fn writepage(&self, page: &crate::memory::Page) -> Result<()>;
/// Read a page
fn readpage(&self, file: Option<&super::File>, page: &crate::memory::Page) -> Result<()>;
/// Sync pages
fn sync_page(&self, page: &crate::memory::Page) -> Result<()>;
/// Write pages
fn writepages(&self, mapping: &AddressSpace, wbc: &WritebackControl) -> Result<()>;
/// Set page dirty
fn set_page_dirty(&self, page: &crate::memory::Page) -> Result<bool>;
/// Read pages ahead
fn readpages(&self, file: Option<&super::File>, pages: &[&crate::memory::Page]) -> Result<()>;
/// Write begin
fn write_begin(&self, file: &super::File, pos: u64, len: u32) -> Result<()>;
/// Write end
fn write_end(&self, file: &super::File, pos: u64, len: u32, copied: u32) -> Result<u32>;
/// Direct I/O
fn direct_io(&self, file: &super::File, pos: u64, buf: UserSlicePtr, len: usize, write: bool) -> Result<isize>;
}
/// Address space structure
pub struct AddressSpace {
/// Host inode
pub host: Option<Arc<super::Inode>>,
/// Address space operations
pub a_ops: Option<Arc<dyn AddressSpaceOperations>>,
/// Number of pages
pub nrpages: core::sync::atomic::AtomicUsize,
/// Flags
pub flags: core::sync::atomic::AtomicU32,
/// Private data
pub private_data: Option<*mut u8>,
}
impl AddressSpace {
pub fn new() -> Self {
Self {
host: None,
a_ops: None,
nrpages: core::sync::atomic::AtomicUsize::new(0),
flags: core::sync::atomic::AtomicU32::new(0),
private_data: None,
}
}
}
/// Writeback control structure
pub struct WritebackControl {
pub start: u64,
pub end: u64,
pub sync_mode: WritebackSyncMode,
pub nr_to_write: u64,
pub tagged_writepages: bool,
}
#[derive(Debug, Clone, Copy)]
pub enum WritebackSyncMode {
None,
All,
Memory,
}
/// Generic file operations implementation
pub struct GenericFileOperations;
impl super::FileOperations for GenericFileOperations {
fn read(&self, file: &super::File, buf: UserSlicePtr, count: usize) -> Result<isize> {
// Generic read implementation using page cache
// TODO: Implement proper page cache read
Ok(0)
}
fn write(&self, file: &super::File, buf: UserSlicePtr, count: usize) -> Result<isize> {
// Generic write implementation using page cache
// TODO: Implement proper page cache write
Ok(count as isize)
}
fn seek(&self, file: &super::File, offset: i64, whence: i32) -> Result<i64> {
let current_pos = file.get_pos();
let new_pos = match whence {
super::SEEK_SET => offset,
super::SEEK_CUR => current_pos + offset,
super::SEEK_END => {
// Get file size from inode
if let Some(ref inode) = file.inode {
inode.get_size() as i64 + offset
} else {
offset
}
}
_ => return Err(Error::EINVAL),
};
if new_pos < 0 {
return Err(Error::EINVAL);
}
file.set_pos(new_pos);
Ok(new_pos)
}
fn ioctl(&self, file: &super::File, cmd: u32, arg: usize) -> Result<isize> {
Err(Error::ENOTTY)
}
fn mmap(&self, file: &super::File, vma: &mut crate::memory::VmaArea) -> Result<()> {
// Generic mmap implementation
// TODO: Implement proper memory mapping
Err(Error::ENODEV)
}
fn fsync(&self, file: &super::File, datasync: bool) -> Result<()> {
// Sync file data to storage
if let Some(ref inode) = file.inode {
// TODO: Sync inode and data blocks
}
Ok(())
}
fn poll(&self, file: &super::File, wait: &mut super::PollWait) -> Result<u32> {
// Regular files are always ready for I/O
Ok(super::POLLIN | super::POLLOUT)
}
fn readdir(&self, file: &super::File, ctx: &mut super::file::DirContext) -> Result<()> {
// This shouldn't be called for regular files
Err(Error::ENOTDIR)
}
}
/// Directory file operations
pub struct DirectoryOperations;
impl super::FileOperations for DirectoryOperations {
fn read(&self, file: &super::File, buf: UserSlicePtr, count: usize) -> Result<isize> {
// Can't read directory as regular file
Err(Error::EISDIR)
}
fn write(&self, file: &super::File, buf: UserSlicePtr, count: usize) -> Result<isize> {
// Can't write to directory as regular file
Err(Error::EISDIR)
}
fn seek(&self, file: &super::File, offset: i64, whence: i32) -> Result<i64> {
// Directory seeking
match whence {
super::SEEK_SET => {
if offset < 0 {
return Err(Error::EINVAL);
}
file.set_pos(offset);
Ok(offset)
}
super::SEEK_CUR => {
let new_pos = file.get_pos() + offset;
if new_pos < 0 {
return Err(Error::EINVAL);
}
file.set_pos(new_pos);
Ok(new_pos)
}
super::SEEK_END => {
// Seek to end of directory
file.set_pos(i64::MAX);
Ok(i64::MAX)
}
_ => Err(Error::EINVAL),
}
}
fn ioctl(&self, file: &super::File, cmd: u32, arg: usize) -> Result<isize> {
Err(Error::ENOTTY)
}
fn mmap(&self, file: &super::File, vma: &mut crate::memory::VmaArea) -> Result<()> {
Err(Error::ENODEV)
}
fn fsync(&self, file: &super::File, datasync: bool) -> Result<()> {
// Sync directory
Ok(())
}
fn poll(&self, file: &super::File, wait: &mut super::PollWait) -> Result<u32> {
// Directories are always ready
Ok(super::POLLIN)
}
fn readdir(&self, file: &super::File, ctx: &mut super::file::DirContext) -> Result<()> {
// Read directory entries
if let Some(ref inode) = file.inode {
if let Some(ref dentry) = file.dentry {
let subdirs = dentry.d_subdirs.lock();
for (i, child) in subdirs.iter().enumerate() {
if i >= ctx.pos as usize {
let d_type = if let Some(ref child_inode) = child.d_inode {
let mode = child_inode.i_mode.load(core::sync::atomic::Ordering::Relaxed);
if super::mode::s_isdir(mode) {
super::DT_DIR
} else if super::mode::s_isreg(mode) {
super::DT_REG
} else if super::mode::s_islnk(mode) {
super::DT_LNK
} else if super::mode::s_ischr(mode) {
super::DT_CHR
} else if super::mode::s_isblk(mode) {
super::DT_BLK
} else if super::mode::s_isfifo(mode) {
super::DT_FIFO
} else if super::mode::s_issock(mode) {
super::DT_SOCK
} else {
super::DT_UNKNOWN
}
} else {
super::DT_UNKNOWN
};
let ino = if let Some(ref child_inode) = child.d_inode {
child_inode.i_ino
} else {
0
};
ctx.add_entry(ino, &child.d_name, d_type);
}
}
}
}
Ok(())
}
}
/// Special file operations (for device files)
pub struct SpecialFileOperations;
impl super::FileOperations for SpecialFileOperations {
fn read(&self, file: &super::File, buf: UserSlicePtr, count: usize) -> Result<isize> {
// Delegate to device driver
if let Some(ref inode) = file.inode {
if inode.is_char_device() || inode.is_block_device() {
// TODO: Call device driver read function
return Ok(0);
}
}
Err(Error::ENODEV)
}
fn write(&self, file: &super::File, buf: UserSlicePtr, count: usize) -> Result<isize> {
// Delegate to device driver
if let Some(ref inode) = file.inode {
if inode.is_char_device() || inode.is_block_device() {
// TODO: Call device driver write function
return Ok(count as isize);
}
}
Err(Error::ENODEV)
}
fn seek(&self, file: &super::File, offset: i64, whence: i32) -> Result<i64> {
// Most device files don't support seeking
Err(Error::ESPIPE)
}
fn ioctl(&self, file: &super::File, cmd: u32, arg: usize) -> Result<isize> {
// Delegate to device driver
if let Some(ref inode) = file.inode {
if inode.is_char_device() || inode.is_block_device() {
// TODO: Call device driver ioctl function
return Ok(0);
}
}
Err(Error::ENOTTY)
}
fn mmap(&self, file: &super::File, vma: &mut crate::memory::VmaArea) -> Result<()> {
// Some device files support mmap
Err(Error::ENODEV)
}
fn fsync(&self, file: &super::File, datasync: bool) -> Result<()> {
// Nothing to sync for device files
Ok(())
}
fn poll(&self, file: &super::File, wait: &mut super::PollWait) -> Result<u32> {
// Delegate to device driver
if let Some(ref inode) = file.inode {
if inode.is_char_device() {
// TODO: Call device driver poll function
return Ok(super::POLLIN | super::POLLOUT);
}
}
Ok(0)
}
}
/// No-op address space operations
pub struct NoOpAddressSpaceOps;
impl AddressSpaceOperations for NoOpAddressSpaceOps {
fn writepage(&self, page: &crate::memory::Page) -> Result<()> {
Ok(())
}
fn readpage(&self, file: Option<&super::File>, page: &crate::memory::Page) -> Result<()> {
Ok(())
}
fn sync_page(&self, page: &crate::memory::Page) -> Result<()> {
Ok(())
}
fn writepages(&self, mapping: &AddressSpace, wbc: &WritebackControl) -> Result<()> {
Ok(())
}
fn set_page_dirty(&self, page: &crate::memory::Page) -> Result<bool> {
Ok(true)
}
fn readpages(&self, file: Option<&super::File>, pages: &[&crate::memory::Page]) -> Result<()> {
Ok(())
}
fn write_begin(&self, file: &super::File, pos: u64, len: u32) -> Result<()> {
Ok(())
}
fn write_end(&self, file: &super::File, pos: u64, len: u32, copied: u32) -> Result<u32> {
Ok(copied)
}
fn direct_io(&self, file: &super::File, pos: u64, buf: UserSlicePtr, len: usize, write: bool) -> Result<isize> {
if write {
Ok(len as isize)
} else {
Ok(0)
}
}
}
/// Helper functions for VFS operations
/// Get file operations for an inode
pub fn get_file_operations(inode: &super::Inode) -> Arc<dyn super::FileOperations> {
if let Some(ref fop) = inode.i_fop {
fop.clone()
} else {
let mode = inode.i_mode.load(core::sync::atomic::Ordering::Relaxed);
if super::mode::s_isreg(mode) {
Arc::new(GenericFileOperations)
} else if super::mode::s_isdir(mode) {
Arc::new(DirectoryOperations)
} else if super::mode::s_ischr(mode) || super::mode::s_isblk(mode) {
Arc::new(SpecialFileOperations)
} else {
Arc::new(super::GenericFileOps)
}
}
}
/// Check file permissions
pub fn check_permissions(inode: &super::Inode, mask: u32) -> Result<()> {
// TODO: Implement proper permission checking
// For now, allow all operations
Ok(())
}
/// Update access time
pub fn update_atime(inode: &super::Inode) {
inode.update_atime();
}
/// Update modification time
pub fn update_mtime(inode: &super::Inode) {
inode.update_mtime();
}
/// Truncate file to specified size
pub fn do_truncate(inode: &super::Inode, size: u64) -> Result<()> {
if let Some(ref ops) = inode.i_op {
ops.truncate(inode, size)
} else {
inode.set_size(size);
Ok(())
}
}
/// Notify directory change
pub fn notify_change(inode: &super::Inode, attr: &super::inode::InodeAttr) -> Result<()> {
if let Some(ref ops) = inode.i_op {
ops.setattr(inode, attr)
} else {
Ok(())
}
}