Files
rustkernel/kernel/src/fs/inode.rs
2025-06-20 01:50:08 +02:00

436 líneas
10 KiB
Rust

// SPDX-License-Identifier: GPL-2.0
//! Inode abstraction - Linux compatible
use alloc::string::String;
use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
use crate::device::DeviceNumber;
use crate::error::{Error, Result};
use crate::sync::{Arc, Mutex};
use crate::time::{get_current_time, TimeSpec};
/// Inode structure - similar to Linux struct inode
#[derive(Debug)]
pub struct Inode {
/// Inode number
pub i_ino: u64,
/// File mode and type
pub i_mode: AtomicU32,
/// Number of hard links
pub i_nlink: AtomicU32,
/// User ID
pub i_uid: AtomicU32,
/// Group ID
pub i_gid: AtomicU32,
/// Device number (for device files)
pub i_rdev: DeviceNumber,
/// File size
pub i_size: AtomicU64,
/// Block size
pub i_blksize: u32,
/// Number of blocks
pub i_blocks: AtomicU64,
/// Access time
pub i_atime: Mutex<TimeSpec>,
/// Modification time
pub i_mtime: Mutex<TimeSpec>,
/// Status change time
pub i_ctime: Mutex<TimeSpec>,
/// Inode operations
pub i_op: Option<Arc<dyn InodeOperations>>,
/// File operations (for regular files)
pub i_fop: Option<Arc<dyn super::FileOperations>>,
/// Superblock this inode belongs to
pub i_sb: Option<Arc<super::SuperBlock>>,
/// Private data
pub private_data: Option<*mut u8>,
/// Reference count
pub refcount: AtomicU32,
/// Inode flags
pub i_flags: AtomicU32,
}
impl Inode {
/// Create a new inode
pub fn new(ino: u64, mode: u32) -> Self {
let now = get_current_time();
Self {
i_ino: ino,
i_mode: AtomicU32::new(mode),
i_nlink: AtomicU32::new(1),
i_uid: AtomicU32::new(0),
i_gid: AtomicU32::new(0),
i_rdev: DeviceNumber::new(0, 0),
i_size: AtomicU64::new(0),
i_blksize: 4096,
i_blocks: AtomicU64::new(0),
i_atime: Mutex::new(now),
i_mtime: Mutex::new(now),
i_ctime: Mutex::new(now),
i_op: None,
i_fop: None,
i_sb: None,
private_data: None,
refcount: AtomicU32::new(1),
i_flags: AtomicU32::new(0),
}
}
/// Set inode operations
pub fn set_operations(&mut self, ops: Arc<dyn InodeOperations>) {
self.i_op = Some(ops);
}
/// Set file operations
pub fn set_file_operations(&mut self, ops: Arc<dyn super::FileOperations>) {
self.i_fop = Some(ops);
}
/// Get file statistics
pub fn stat(&self) -> Result<super::KStat> {
let atime = self.i_atime.lock();
let mtime = self.i_mtime.lock();
let ctime = self.i_ctime.lock();
Ok(super::KStat {
st_dev: if let Some(ref sb) = self.i_sb {
sb.s_dev.as_raw()
} else {
0
},
st_ino: self.i_ino,
st_nlink: self.i_nlink.load(Ordering::Relaxed) as u64,
st_mode: self.i_mode.load(Ordering::Relaxed),
st_uid: self.i_uid.load(Ordering::Relaxed),
st_gid: self.i_gid.load(Ordering::Relaxed),
st_rdev: self.i_rdev.as_raw(),
st_size: self.i_size.load(Ordering::Relaxed) as i64,
st_blksize: self.i_blksize as u64,
st_blocks: self.i_blocks.load(Ordering::Relaxed),
st_atime: atime.tv_sec,
st_atime_nsec: atime.tv_nsec,
st_mtime: mtime.tv_sec,
st_mtime_nsec: mtime.tv_nsec,
st_ctime: ctime.tv_sec,
st_ctime_nsec: ctime.tv_nsec,
})
}
/// Check if inode is a regular file
pub fn is_regular(&self) -> bool {
let mode = self.i_mode.load(Ordering::Relaxed);
super::mode::s_isreg(mode)
}
/// Check if inode is a directory
pub fn is_directory(&self) -> bool {
let mode = self.i_mode.load(Ordering::Relaxed);
super::mode::s_isdir(mode)
}
/// Check if inode is a character device
pub fn is_char_device(&self) -> bool {
let mode = self.i_mode.load(Ordering::Relaxed);
super::mode::s_ischr(mode)
}
/// Check if inode is a block device
pub fn is_block_device(&self) -> bool {
let mode = self.i_mode.load(Ordering::Relaxed);
super::mode::s_isblk(mode)
}
/// Update access time
pub fn update_atime(&self) {
let mut atime = self.i_atime.lock();
*atime = get_current_time();
}
/// Update modification time
pub fn update_mtime(&self) {
let mut mtime = self.i_mtime.lock();
*mtime = get_current_time();
}
/// Update status change time
pub fn update_ctime(&self) {
let mut ctime = self.i_ctime.lock();
*ctime = get_current_time();
}
/// Set file size
pub fn set_size(&self, size: u64) {
self.i_size.store(size, Ordering::Relaxed);
self.update_mtime();
self.update_ctime();
}
/// Get file size
pub fn get_size(&self) -> u64 {
self.i_size.load(Ordering::Relaxed)
}
/// Increment reference count
pub fn iget(&self) {
self.refcount.fetch_add(1, Ordering::Relaxed);
}
/// Decrement reference count
pub fn iput(&self) {
let old_count = self.refcount.fetch_sub(1, Ordering::Relaxed);
if old_count == 1 {
// Last reference, inode should be cleaned up
// TODO: Call destroy_inode operation if present
}
}
/// Create a new file in this directory
pub fn create(&self, name: &str, mode: u32) -> Result<Arc<Inode>> {
if let Some(ref ops) = self.i_op {
ops.create(self, name, mode)
} else {
Err(Error::ENOSYS)
}
}
/// Look up a file in this directory
pub fn lookup(&self, name: &str) -> Result<Arc<Inode>> {
if let Some(ref ops) = self.i_op {
ops.lookup(self, name)
} else {
Err(Error::ENOSYS)
}
}
/// Create a directory
pub fn mkdir(&self, name: &str, mode: u32) -> Result<Arc<Inode>> {
if let Some(ref ops) = self.i_op {
ops.mkdir(self, name, mode)
} else {
Err(Error::ENOSYS)
}
}
/// Remove a file
pub fn unlink(&self, name: &str) -> Result<()> {
if let Some(ref ops) = self.i_op {
ops.unlink(self, name)
} else {
Err(Error::ENOSYS)
}
}
/// Remove a directory
pub fn rmdir(&self, name: &str) -> Result<()> {
if let Some(ref ops) = self.i_op {
ops.rmdir(self, name)
} else {
Err(Error::ENOSYS)
}
}
}
unsafe impl Send for Inode {}
unsafe impl Sync for Inode {}
/// Inode operations trait - similar to Linux inode_operations
pub trait InodeOperations: Send + Sync + core::fmt::Debug {
/// Look up a file in directory
fn lookup(&self, dir: &Inode, name: &str) -> Result<Arc<Inode>>;
/// Create a new file
fn create(&self, dir: &Inode, name: &str, mode: u32) -> Result<Arc<Inode>>;
/// Create a directory
fn mkdir(&self, dir: &Inode, name: &str, mode: u32) -> Result<Arc<Inode>>;
/// Remove a file
fn unlink(&self, dir: &Inode, name: &str) -> Result<()>;
/// Remove a directory
fn rmdir(&self, dir: &Inode, name: &str) -> Result<()>;
/// Create a symbolic link
fn symlink(&self, dir: &Inode, name: &str, target: &str) -> Result<Arc<Inode>>;
/// Rename a file
fn rename(
&self,
old_dir: &Inode,
old_name: &str,
new_dir: &Inode,
new_name: &str,
) -> Result<()>;
/// Set attributes
fn setattr(&self, inode: &Inode, attr: &InodeAttr) -> Result<()>;
/// Get attributes
fn getattr(&self, inode: &Inode) -> Result<InodeAttr>;
/// Read symbolic link
fn readlink(&self, inode: &Inode) -> Result<String>;
/// Follow symbolic link
fn follow_link(&self, inode: &Inode) -> Result<Arc<Inode>>;
/// Truncate file
fn truncate(&self, inode: &Inode, size: u64) -> Result<()>;
/// Get extended attribute
fn getxattr(&self, inode: &Inode, name: &str) -> Result<alloc::vec::Vec<u8>>;
/// Set extended attribute
fn setxattr(&self, inode: &Inode, name: &str, value: &[u8], flags: u32) -> Result<()>;
/// List extended attributes
fn listxattr(&self, inode: &Inode) -> Result<alloc::vec::Vec<String>>;
/// Remove extended attribute
fn removexattr(&self, inode: &Inode, name: &str) -> Result<()>;
}
/// Inode attributes structure
#[derive(Debug, Clone, Copy)]
pub struct InodeAttr {
pub mode: Option<u32>,
pub uid: Option<u32>,
pub gid: Option<u32>,
pub size: Option<u64>,
pub atime: Option<TimeSpec>,
pub mtime: Option<TimeSpec>,
pub ctime: Option<TimeSpec>,
}
impl InodeAttr {
pub fn new() -> Self {
Self {
mode: None,
uid: None,
gid: None,
size: None,
atime: None,
mtime: None,
ctime: None,
}
}
pub fn with_mode(mut self, mode: u32) -> Self {
self.mode = Some(mode);
self
}
pub fn with_size(mut self, size: u64) -> Self {
self.size = Some(size);
self
}
}
/// Generic inode operations for simple filesystems
#[derive(Debug)]
pub struct GenericInodeOps;
impl InodeOperations for GenericInodeOps {
fn lookup(&self, dir: &Inode, name: &str) -> Result<Arc<Inode>> {
Err(Error::ENOENT)
}
fn create(&self, dir: &Inode, name: &str, mode: u32) -> Result<Arc<Inode>> {
Err(Error::ENOSYS)
}
fn mkdir(&self, dir: &Inode, name: &str, mode: u32) -> Result<Arc<Inode>> {
Err(Error::ENOSYS)
}
fn unlink(&self, dir: &Inode, name: &str) -> Result<()> {
Err(Error::ENOSYS)
}
fn rmdir(&self, dir: &Inode, name: &str) -> Result<()> {
Err(Error::ENOSYS)
}
fn symlink(&self, dir: &Inode, name: &str, target: &str) -> Result<Arc<Inode>> {
Err(Error::ENOSYS)
}
fn rename(
&self,
old_dir: &Inode,
old_name: &str,
new_dir: &Inode,
new_name: &str,
) -> Result<()> {
Err(Error::ENOSYS)
}
fn setattr(&self, inode: &Inode, attr: &InodeAttr) -> Result<()> {
// Apply basic attributes
if let Some(mode) = attr.mode {
inode.i_mode.store(mode, Ordering::Relaxed);
}
if let Some(uid) = attr.uid {
inode.i_uid.store(uid, Ordering::Relaxed);
}
if let Some(gid) = attr.gid {
inode.i_gid.store(gid, Ordering::Relaxed);
}
if let Some(size) = attr.size {
inode.set_size(size);
}
if let Some(atime) = attr.atime {
*inode.i_atime.lock() = atime;
}
if let Some(mtime) = attr.mtime {
*inode.i_mtime.lock() = mtime;
}
if let Some(ctime) = attr.ctime {
*inode.i_ctime.lock() = ctime;
}
Ok(())
}
fn getattr(&self, inode: &Inode) -> Result<InodeAttr> {
Ok(InodeAttr {
mode: Some(inode.i_mode.load(Ordering::Relaxed)),
uid: Some(inode.i_uid.load(Ordering::Relaxed)),
gid: Some(inode.i_gid.load(Ordering::Relaxed)),
size: Some(inode.i_size.load(Ordering::Relaxed)),
atime: Some(*inode.i_atime.lock()),
mtime: Some(*inode.i_mtime.lock()),
ctime: Some(*inode.i_ctime.lock()),
})
}
fn readlink(&self, inode: &Inode) -> Result<String> {
Err(Error::EINVAL)
}
fn follow_link(&self, inode: &Inode) -> Result<Arc<Inode>> {
Err(Error::EINVAL)
}
fn truncate(&self, inode: &Inode, size: u64) -> Result<()> {
inode.set_size(size);
Ok(())
}
fn getxattr(&self, inode: &Inode, name: &str) -> Result<alloc::vec::Vec<u8>> {
Err(Error::ENODATA)
}
fn setxattr(&self, inode: &Inode, name: &str, value: &[u8], flags: u32) -> Result<()> {
Err(Error::ENOSYS)
}
fn listxattr(&self, inode: &Inode) -> Result<alloc::vec::Vec<String>> {
Ok(alloc::vec::Vec::new())
}
fn removexattr(&self, inode: &Inode, name: &str) -> Result<()> {
Err(Error::ENODATA)
}
}