diff --git a/Cargo.lock b/Cargo.lock index 2caa0bad..84bd641b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1c330e503236d0b06386ae6cc42a513ef1ccc23c52b603c1b52f018564faf44" +[[package]] +name = "another_ext4" +version = "0.1.0" +source = "git+https://github.com/SMS-Derfflinger/another_ext4?branch=main#01a7fbaaf9135103b37cf82484f3a2cb1fa0d279" +dependencies = [ + "bitflags", + "log", +] + [[package]] name = "atomic_unique_refcell" version = "0.1.0" @@ -134,6 +143,7 @@ version = "0.1.0" dependencies = [ "acpi", "align_ext", + "another_ext4", "atomic_unique_refcell", "bitflags", "buddy_allocator", @@ -145,7 +155,6 @@ dependencies = [ "eonix_preempt", "eonix_runtime", "eonix_sync", - "ext4_rs", "intrusive-collections", "intrusive_list", "itertools", @@ -248,16 +257,6 @@ dependencies = [ "intrusive-collections", ] -[[package]] -name = "ext4_rs" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1a97344bde15b0ace15e265dab27228d4bdc37a0bfa8548c5645d7cfa6a144" -dependencies = [ - "bitflags", - "log", -] - [[package]] name = "fdt" version = "0.1.5" @@ -403,9 +402,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.103" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 15df5f15..32f83fb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ itertools = { version = "0.13.0", default-features = false } acpi = "5.2.0" align_ext = "0.1.0" xmas-elf = "0.10.0" -ext4_rs = "1.3.2" +another_ext4 = { git = "https://github.com/SMS-Derfflinger/another_ext4", branch = "main" } [target.'cfg(any(target_arch = "riscv64", target_arch = "loongarch64"))'.dependencies] virtio-drivers = { version = "0.11.0" } diff --git a/src/driver/virtio/virtio_blk.rs b/src/driver/virtio/virtio_blk.rs index e8723cac..37e4fe77 100644 --- a/src/driver/virtio/virtio_blk.rs +++ b/src/driver/virtio/virtio_blk.rs @@ -84,7 +84,23 @@ where fn submit(&self, req: BlockDeviceRequest) -> KResult<()> { match req { - BlockDeviceRequest::Write { .. } => todo!(), + BlockDeviceRequest::Write { + sector, + count, + buffer, + } => { + let mut dev = self.lock(); + for ((start, len), buffer_page) in + Chunks::new(sector as usize, count as usize, 8).zip(buffer.iter()) + { + let buffer = unsafe { + // SAFETY: Pages in `req.buffer` are guaranteed to be exclusively owned by us. + &buffer_page.as_memblk().as_bytes()[..len as usize * 512] + }; + + dev.write_blocks(start, buffer).map_err(|_| EIO)?; + } + } BlockDeviceRequest::Read { sector, count, diff --git a/src/fs/ext4.rs b/src/fs/ext4.rs index a2138a09..b857dab3 100644 --- a/src/fs/ext4.rs +++ b/src/fs/ext4.rs @@ -1,15 +1,19 @@ use core::sync::atomic::{AtomicU32, AtomicU64, Ordering}; -use crate::kernel::mem::{PageCache, PageCacheBackend}; +use crate::kernel::mem::{CachePage, CachePageStream, PageCache, PageCacheBackend}; +use crate::kernel::timer::Ticks; use crate::{ - io::{Buffer, ByteBuffer}, + io::{Buffer, ByteBuffer, Stream}, kernel::{ block::BlockDevice, - constants::EIO, + constants::{EEXIST, EINVAL, EIO, ENOSYS, S_IFDIR, S_IFREG}, timer::Instant, vfs::{ - dentry::Dentry, - inode::{define_struct_inode, AtomicNlink, Ino, Inode, InodeData}, + dentry::{dcache, Dentry}, + inode::{ + define_struct_inode, AtomicNlink, Ino, Inode, InodeData, Mode, RenameData, + WriteOffset, + }, mount::{register_filesystem, Mount, MountCreator}, s_isdir, s_isreg, vfs::Vfs, @@ -24,10 +28,11 @@ use alloc::{ collections::btree_map::{BTreeMap, Entry}, sync::Arc, }; +use another_ext4::{ + Block, BlockDevice as Ext4BlockDeviceTrait, Ext4, FileType, InodeMode, PBlockId, +}; use eonix_runtime::task::Task; use eonix_sync::RwLock; -use ext4_rs::{BlockDevice as Ext4BlockDeviceTrait, Ext4Error}; -use ext4_rs::{Errno, Ext4}; pub struct Ext4BlockDevice { device: Arc, @@ -40,20 +45,25 @@ impl Ext4BlockDevice { } impl Ext4BlockDeviceTrait for Ext4BlockDevice { - fn read_offset(&self, offset: usize) -> Vec { - let mut buffer = vec![0u8; 4096]; + fn read_block(&self, block_id: PBlockId) -> Block { + let mut buffer = [0u8; 4096]; let mut byte_buffer = ByteBuffer::new(buffer.as_mut_slice()); let _ = self .device - .read_some(offset, &mut byte_buffer) + .read_some((block_id as usize) * 4096, &mut byte_buffer) .expect("Failed to read from block device"); - buffer + Block { + id: block_id, + data: buffer, + } } - fn write_offset(&self, _offset: usize, _data: &[u8]) { - todo!() + fn write_block(&self, block: &another_ext4::Block) { + let _ = self + .device + .write_some((block.id as usize) * 4096, &block.data); } } @@ -74,7 +84,7 @@ impl Vfs for Ext4Fs { } fn is_read_only(&self) -> bool { - true + false } } @@ -83,6 +93,43 @@ impl Ext4Fs { icache.get(&ino).cloned().map(Ext4Inode::into_inner) } + fn modify_inode_stat(&self, ino: u32, size: Option, mtime: u32) { + let _ = self + .inner + .setattr(ino, None, None, None, size, None, Some(mtime), None, None); + } + + fn create_inode_stat(&self, parent: u32, child: u32, mtime: u32) { + let _ = self.inner.setattr( + parent, + None, + None, + None, + None, + None, + Some(mtime), + None, + None, + ); + let _ = self + .inner + .setattr(child, None, None, None, None, None, Some(mtime), None, None); + } + + fn chmod_stat(&self, ino: u32, new_mode: u16, ctime: u32) { + let _ = self.inner.setattr( + ino, + Some(InodeMode::from_bits_retain(new_mode.try_into().unwrap())), + None, + None, + None, + None, + None, + Some(ctime), + None, + ); + } + fn get_or_insert( &self, icache: &mut BTreeMap, @@ -94,7 +141,7 @@ impl Ext4Fs { let mode = *idata.mode.get_mut(); if s_isreg(mode) { vacant - .insert(Ext4Inode::File(FileInode::new(idata))) + .insert(Ext4Inode::File(FileInode::with_idata(idata))) .clone() .into_inner() } else if s_isdir(mode) { @@ -105,7 +152,7 @@ impl Ext4Fs { } else { println_warn!("ext4: Unsupported inode type: {mode:#o}"); vacant - .insert(Ext4Inode::File(FileInode::new(idata))) + .insert(Ext4Inode::File(FileInode::with_idata(idata))) .clone() .into_inner() } @@ -117,7 +164,7 @@ impl Ext4Fs { impl Ext4Fs { pub fn create(device: Arc) -> KResult<(Arc, Arc)> { let ext4_device = Ext4BlockDevice::new(device.clone()); - let ext4 = Ext4::open(Arc::new(ext4_device)); + let ext4 = Ext4::load(Arc::new(ext4_device)).unwrap(); let ext4fs = Arc::new(Self { inner: ext4, @@ -127,28 +174,28 @@ impl Ext4Fs { let root_inode = { let mut icache = Task::block_on(ext4fs.icache.write()); - let root_inode = ext4fs.inner.get_inode_ref(2); + let root_inode = ext4fs.inner.read_root_inode(); ext4fs.get_or_insert( &mut icache, InodeData { - ino: root_inode.inode_num as Ino, + ino: root_inode.id as Ino, size: AtomicU64::new(root_inode.inode.size()), - nlink: AtomicNlink::new(root_inode.inode.links_count() as _), - uid: AtomicU32::new(root_inode.inode.uid() as _), - gid: AtomicU32::new(root_inode.inode.gid() as _), - mode: AtomicU32::new(root_inode.inode.mode() as _), + nlink: AtomicNlink::new(root_inode.inode.link_count() as u64), + uid: AtomicU32::new(root_inode.inode.uid()), + gid: AtomicU32::new(root_inode.inode.gid()), + mode: AtomicU32::new(root_inode.inode.mode().bits() as u32), atime: Spin::new(Instant::new( root_inode.inode.atime() as _, - root_inode.inode.i_atime_extra() as _, + root_inode.inode.atime_extra() as _, )), ctime: Spin::new(Instant::new( root_inode.inode.ctime() as _, - root_inode.inode.i_ctime_extra() as _, + root_inode.inode.ctime_extra() as _, )), mtime: Spin::new(Instant::new( root_inode.inode.mtime() as _, - root_inode.inode.i_mtime_extra() as _, + root_inode.inode.mtime_extra() as _, )), rwsem: RwLock::new(()), vfs: Arc::downgrade(&ext4fs) as _, @@ -177,6 +224,7 @@ impl Ext4Inode { define_struct_inode! { struct FileInode { + last_sync: AtomicU64, page_cache: PageCache, } } @@ -186,23 +234,51 @@ define_struct_inode! { } impl FileInode { - fn new(idata: InodeData) -> Arc { + fn with_idata(idata: InodeData) -> Arc { let inode = Arc::new_cyclic(|weak_self: &Weak| Self { idata, + last_sync: AtomicU64::new(0), page_cache: PageCache::new(weak_self.clone()), }); inode } + + pub fn new(ino: Ino, vfs: Weak, mode: Mode) -> Arc { + Arc::new_cyclic(|weak_self: &Weak| Self { + idata: { + let inode_data = InodeData::new(ino, vfs); + inode_data + .mode + .store(S_IFREG | (mode & 0o777), Ordering::Relaxed); + inode_data.nlink.store(1, Ordering::Relaxed); + inode_data + }, + last_sync: AtomicU64::new(0), + page_cache: PageCache::new(weak_self.clone()), + }) + } + + fn sync_if_needed(&self) { + let now = Ticks::now().in_secs(); + let last = self.last_sync.load(Ordering::Relaxed); + + // TODO: this is a temporary implement, + // consider change this with some update strategy such as LRU future + if now - last > 10 { + self.last_sync.store(now, Ordering::Relaxed); + let _ = Task::block_on(self.page_cache.fsync()); + } + } } impl PageCacheBackend for FileInode { - fn read_page(&self, page: &mut crate::kernel::mem::CachePage, offset: usize) -> KResult { + fn read_page(&self, page: &mut CachePage, offset: usize) -> KResult { self.read_direct(page, offset) } - fn write_page(&self, page: &crate::kernel::mem::CachePage, offset: usize) -> KResult { - todo!() + fn write_page(&self, page: &mut CachePageStream, offset: usize) -> KResult { + self.write_direct(page, offset) } fn size(&self) -> usize { @@ -224,13 +300,149 @@ impl Inode for FileInode { let ext4fs = vfs.as_any().downcast_ref::().unwrap(); let mut temp_buf = vec![0u8; buffer.total()]; - match ext4fs.inner.read_at(self.ino as u32, offset, &mut temp_buf) { + match ext4fs.inner.read(self.ino as u32, offset, &mut temp_buf) { Ok(bytes_read) => { let _ = buffer.fill(&temp_buf[..bytes_read])?; Ok(buffer.wrote()) } - Err(e) => Err(e.error() as u32), + Err(e) => Err(e.code() as u32), + } + } + + fn write(&self, stream: &mut dyn Stream, offset: WriteOffset) -> KResult { + let _lock = Task::block_on(self.rwsem.write()); + + let mut store_new_end = None; + let offset = match offset { + WriteOffset::Position(offset) => offset, + // TODO: here need to add some operate + WriteOffset::End(end) => { + store_new_end = Some(end); + self.size.load(Ordering::Relaxed) as usize + } + }; + + let total_written = Task::block_on(self.page_cache.write(stream, offset))?; + let cursor_end = offset + total_written; + if let Some(store_end) = store_new_end { + *store_end = cursor_end; } + + let mtime = Instant::now(); + *self.mtime.lock() = mtime; + self.size.store(cursor_end as u64, Ordering::Relaxed); + + self.sync_if_needed(); + + Ok(total_written) + } + + fn write_direct(&self, stream: &mut dyn Stream, offset: usize) -> KResult { + //let _lock = Task::block_on(self.rwsem.write()); + + let vfs = self.vfs.upgrade().ok_or(EIO)?; + let ext4fs = vfs.as_any().downcast_ref::().unwrap(); + + let mut temp_buf = vec![0u8; 4096]; + let mut total_written = 0; + + while let Some(data) = stream.poll_data(&mut temp_buf)? { + let written = ext4fs + .inner + .write(self.ino as u32, offset + total_written, data) + .unwrap(); + total_written += written; + if written < data.len() { + break; + } + } + + ext4fs.modify_inode_stat( + self.ino as u32, + Some(self.file_size() as u64), + self.mtime.lock().since_epoch().as_secs() as u32, + ); + + Ok(total_written) + } + + fn chmod(&self, mode: Mode) -> KResult<()> { + let _lock = Task::block_on(self.rwsem.write()); + + let vfs = self.vfs.upgrade().ok_or(EIO)?; + let ext4fs = vfs.as_any().downcast_ref::().unwrap(); + let old_mode = self.mode.load(Ordering::Relaxed); + let new_mode = (old_mode & !0o777) | (mode & 0o777); + + let now = Instant::now(); + ext4fs.chmod_stat( + self.ino as u32, + new_mode as u16, + now.since_epoch().as_secs() as u32, + ); + + // SAFETY: `rwsem` has done the synchronization + self.mode.store(new_mode, Ordering::Relaxed); + *self.ctime.lock() = now; + + Ok(()) + } + + // TODO + fn truncate(&self, length: usize) -> KResult<()> { + let _lock = Task::block_on(self.rwsem.write()); + + if length == 0 { + let vfs = self.vfs.upgrade().ok_or(EIO)?; + let ext4fs = vfs.as_any().downcast_ref::().unwrap(); + let buffer = vec![0u8; 10]; + let _ = ext4fs.inner.write(self.ino as u32, 0, &buffer); + let _ = Task::block_on(self.page_cache.resize(0)); + } + + self.size.store(length as u64, Ordering::Relaxed); + *self.mtime.lock() = Instant::now(); + Ok(()) + } +} + +impl DirInode { + fn new(ino: Ino, vfs: Weak, mode: Mode) -> Arc { + Arc::new_cyclic(|_| DirInode { + idata: { + let inode_data = InodeData::new(ino, vfs); + inode_data + .mode + .store(S_IFDIR | (mode & 0o777), Ordering::Relaxed); + inode_data.nlink.store(2, Ordering::Relaxed); + inode_data.size.store(4096, Ordering::Relaxed); + inode_data + }, + }) + } + + fn update_time(&self, time: Instant) { + *self.ctime.lock() = time; + *self.mtime.lock() = time; + } + + fn update_child_time(&self, child: &dyn Inode, time: Instant) { + self.update_time(time); + *child.ctime.lock() = time; + *child.mtime.lock() = time; + } + + fn link_file(&self) { + self.size.fetch_add(1, Ordering::Relaxed); + } + + fn link_dir(&self) { + self.nlink.fetch_add(1, Ordering::Relaxed); + self.size.fetch_add(1, Ordering::Relaxed); + } + + fn unlink_dir(&self) { + self.nlink.fetch_sub(1, Ordering::Relaxed); } } @@ -241,13 +453,14 @@ impl Inode for DirInode { let name = dentry.get_name(); let name = String::from_utf8_lossy(&name); - let lookup_result = ext4fs.inner.fuse_lookup(self.ino, &name); + let lookup_result = ext4fs.inner.lookup(self.ino as u32, &name); - const EXT4_ERROR_ENOENT: Ext4Error = Ext4Error::new(Errno::ENOENT); + // TODO: wtf + //const EXT4_ERROR_ENOENT: Ext4Error_ = Ext4Error_::new(ErrCode::ENOENT); let attr = match lookup_result { - Ok(attr) => attr, - Err(EXT4_ERROR_ENOENT) => return Ok(None), - Err(error) => return Err(error.error() as u32), + Ok(inode_id) => ext4fs.inner.getattr(inode_id).unwrap(), + //Err(EXT4_ERROR_ENOENT) => return Ok(None), + Err(error) => return Err(error.code() as u32), }; // Fast path: if the inode is already in the cache, return it. @@ -256,9 +469,19 @@ impl Inode for DirInode { return Ok(Some(inode)); } - let extra_perm = attr.perm.bits() as u32 & 0o7000; - let perm = attr.perm.bits() as u32 & 0o0700; - let real_perm = extra_perm | perm | perm >> 3 | perm >> 6; + let file_type_bits = match attr.ftype { + FileType::RegularFile => InodeMode::FILE.bits(), + FileType::Directory => InodeMode::DIRECTORY.bits(), + FileType::CharacterDev => InodeMode::CHARDEV.bits(), + FileType::BlockDev => InodeMode::BLOCKDEV.bits(), + FileType::Fifo => InodeMode::FIFO.bits(), + FileType::Socket => InodeMode::SOCKET.bits(), + FileType::SymLink => InodeMode::SOFTLINK.bits(), + FileType::Unknown => 0, + }; + + let perm_bits = attr.perm.bits() & InodeMode::PERM_MASK.bits(); + let mode = file_type_bits | perm_bits; // Create a new inode based on the attributes. let mut icache = Task::block_on(ext4fs.icache.write()); @@ -267,10 +490,10 @@ impl Inode for DirInode { InodeData { ino: attr.ino as Ino, size: AtomicU64::new(attr.size), - nlink: AtomicNlink::new(attr.nlink as _), + nlink: AtomicNlink::new(attr.links as _), uid: AtomicU32::new(attr.uid), gid: AtomicU32::new(attr.gid), - mode: AtomicU32::new(attr.kind.bits() as u32 | real_perm), + mode: AtomicU32::new(mode as u32), atime: Spin::new(Instant::new(attr.atime as _, 0)), ctime: Spin::new(Instant::new(attr.ctime as _, 0)), mtime: Spin::new(Instant::new(attr.mtime as _, 0)), @@ -292,22 +515,194 @@ impl Inode for DirInode { let entries = ext4fs .inner - .fuse_readdir(self.ino as u64, 0, offset as i64) - .map_err(|err| err.error() as u32)?; - let mut current_offset = 0; + .listdir(self.ino as u32) + .map_err(|err| err.code() as u32)?; - for entry in entries { - let name_len = entry.name_len as usize; - let name = &entry.name[..name_len]; + let entries_to_process = if offset < entries.len() { + &entries[offset..] + } else { + &entries[0..0] + }; + let mut current_offset = 0; + for entry in entries_to_process { + let name_string = entry.name(); + let name = name_string.as_bytes(); + let inode = entry.inode() as Ino; - if callback(name, entry.inode as Ino)?.is_break() { + if callback(name, inode)?.is_break() { break; } - current_offset += 1; } Ok(current_offset) } + + fn creat(&self, at: &Arc, mode: Mode) -> KResult<()> { + let _lock = Task::block_on(self.rwsem.write()); + + let vfs = self.vfs.upgrade().ok_or(EIO)?; + let ext4fs = vfs.as_any().downcast_ref::().unwrap(); + + let name = at.get_name(); + let name = String::from_utf8_lossy(&name); + + let new_ino = ext4fs + .inner + .create( + self.ino as u32, + &name, + InodeMode::from_bits_retain((mode | S_IFREG) as u16), + ) + .unwrap(); + + let file = FileInode::new(new_ino as u64, self.vfs.clone(), mode); + let now = Instant::now(); + self.update_child_time(file.as_ref(), now); + self.link_file(); + + ext4fs.create_inode_stat(self.ino as u32, new_ino, now.since_epoch().as_secs() as u32); + + at.save_reg(file) + } + + fn mkdir(&self, at: &Dentry, mode: Mode) -> KResult<()> { + let _lock = Task::block_on(self.rwsem.write()); + + let vfs = self.vfs.upgrade().ok_or(EIO)?; + let ext4fs = vfs.as_any().downcast_ref::().unwrap(); + + let name = at.get_name(); + let name = String::from_utf8_lossy(&name); + + let new_ino = ext4fs + .inner + .mkdir( + self.ino as u32, + &name, + InodeMode::from_bits_retain((mode | S_IFDIR) as u16), + ) + .unwrap(); + + let new_dir = DirInode::new(new_ino as u64, self.vfs.clone(), mode); + let now = Instant::now(); + self.update_child_time(new_dir.as_ref(), now); + self.link_dir(); + + ext4fs.create_inode_stat(self.ino as u32, new_ino, now.since_epoch().as_secs() as u32); + + at.save_dir(new_dir) + } + + fn unlink(&self, at: &Arc) -> KResult<()> { + let _dir_lock = Task::block_on(self.rwsem.write()); + + let vfs = self.vfs.upgrade().ok_or(EIO)?; + let ext4fs = vfs.as_any().downcast_ref::().unwrap(); + + let file = at.get_inode()?; + + let name = at.get_name(); + let name = String::from_utf8_lossy(&name); + let _file_lock = Task::block_on(file.rwsem.write()); + + if file.is_dir() { + let _ = ext4fs.inner.rmdir(self.ino as u32, &name); + self.unlink_dir(); + } else { + let _ = ext4fs.inner.unlink(self.ino as u32, &name); + } + let now = Instant::now(); + self.update_time(now); + ext4fs.modify_inode_stat(self.ino as u32, None, now.since_epoch().as_secs() as u32); + + dcache::d_remove(at); + + Ok(()) + } + + fn chmod(&self, mode: Mode) -> KResult<()> { + let _lock = Task::block_on(self.rwsem.write()); + + let vfs = self.vfs.upgrade().ok_or(EIO)?; + let ext4fs = vfs.as_any().downcast_ref::().unwrap(); + let old_mode = self.mode.load(Ordering::Relaxed); + let new_mode = (old_mode & !0o777) | (mode & 0o777); + + let now = Instant::now(); + ext4fs.chmod_stat( + self.ino as u32, + new_mode as u16, + now.since_epoch().as_secs() as u32, + ); + + // SAFETY: `rwsem` has done the synchronization + self.mode.store(new_mode, Ordering::Relaxed); + *self.ctime.lock() = now; + + Ok(()) + } + + fn rename(&self, rename_data: RenameData) -> KResult<()> { + let RenameData { + old_dentry, + new_dentry, + new_parent, + is_exchange, + no_replace, + vfs, + } = rename_data; + + if is_exchange { + println_warn!("Ext4Fs does not support exchange rename for now"); + return Err(ENOSYS); + } + + // TODO: may need another lock + let _lock = Task::block_on(self.rwsem.write()); + let vfs = self.vfs.upgrade().ok_or(EIO)?; + let ext4fs = vfs.as_any().downcast_ref::().unwrap(); + + let old_file = old_dentry.get_inode()?; + let new_file = new_dentry.get_inode(); + if no_replace && new_file.is_ok() { + return Err(EEXIST); + } + + let name = old_dentry.name(); + let name = core::str::from_utf8(&*name).map_err(|_| EINVAL)?; + let new_name = new_dentry.name(); + let new_name = core::str::from_utf8(&*new_name).map_err(|_| EINVAL)?; + + ext4fs + .inner + .rename(self.ino as u32, name, new_parent.ino as u32, new_name) + .map_err(|err| err.code() as u32)?; + + // TODO: may need more operations + let now = Instant::now(); + *old_file.ctime.lock() = now; + *self.mtime.lock() = now; + + let same_parent = Arc::as_ptr(&new_parent) == &raw const *self; + if !same_parent { + *new_parent.mtime.lock() = now; + if old_file.is_dir() { + self.nlink.fetch_sub(1, Ordering::Relaxed); + new_parent.nlink.fetch_add(1, Ordering::Relaxed); + } + } + + if let Ok(replaced_file) = new_dentry.get_inode() { + if !no_replace { + *replaced_file.ctime.lock() = now; + replaced_file.nlink.fetch_sub(1, Ordering::Relaxed); + } + } + + Task::block_on(dcache::d_exchange(old_dentry, new_dentry)); + + Ok(()) + } } struct Ext4MountCreator; diff --git a/src/fs/fat32.rs b/src/fs/fat32.rs index 852d8673..fb4a3e2e 100644 --- a/src/fs/fat32.rs +++ b/src/fs/fat32.rs @@ -3,7 +3,7 @@ mod file; use crate::io::Stream; use crate::kernel::constants::EIO; -use crate::kernel::mem::AsMemoryBlock; +use crate::kernel::mem::{AsMemoryBlock, CachePageStream}; use crate::kernel::vfs::inode::WriteOffset; use crate::{ io::{Buffer, ByteBuffer, UninitBuffer}, @@ -308,11 +308,11 @@ impl Inode for FileInode { Ok(buffer.wrote()) } - fn write(&self, stream: &mut dyn Stream, offset: WriteOffset) -> KResult { + fn write(&self, _stream: &mut dyn Stream, _offset: WriteOffset) -> KResult { todo!() } - fn write_direct(&self, stream: &mut dyn Stream, offset: WriteOffset) -> KResult { + fn write_direct(&self, _stream: &mut dyn Stream, _offset: usize) -> KResult { todo!() } } @@ -322,7 +322,7 @@ impl PageCacheBackend for FileInode { self.read_direct(page, offset) } - fn write_page(&self, page: &CachePage, offset: usize) -> KResult { + fn write_page(&self, _page: &mut CachePageStream, _offset: usize) -> KResult { todo!() } diff --git a/src/fs/tmpfs.rs b/src/fs/tmpfs.rs index 334e2781..13a01de5 100644 --- a/src/fs/tmpfs.rs +++ b/src/fs/tmpfs.rs @@ -1,6 +1,6 @@ use crate::io::Stream; use crate::kernel::constants::{EEXIST, EINVAL, EIO, EISDIR, ENOENT, ENOSYS, ENOTDIR}; -use crate::kernel::mem::{CachePage, PageCache, PageCacheBackend}; +use crate::kernel::mem::{CachePage, CachePageStream, PageCache, PageCacheBackend}; use crate::kernel::timer::Instant; use crate::kernel::vfs::inode::InodeData; use crate::kernel::vfs::inode::RenameData; @@ -496,7 +496,7 @@ impl PageCacheBackend for FileInode { Ok(PAGE_SIZE) } - fn write_page(&self, _page: &CachePage, _offset: usize) -> KResult { + fn write_page(&self, _page: &mut CachePageStream, _offset: usize) -> KResult { Ok(PAGE_SIZE) } @@ -511,13 +511,13 @@ impl Inode for FileInode { } fn read(&self, buffer: &mut dyn Buffer, offset: usize) -> KResult { - let lock = Task::block_on(self.rwsem.write()); + let _lock = Task::block_on(self.rwsem.write()); Task::block_on(self.pages.read(buffer, offset)) } fn write(&self, stream: &mut dyn Stream, offset: WriteOffset) -> KResult { // TODO: We don't need that strong guarantee, find some way to avoid locks - let lock = Task::block_on(self.rwsem.write()); + let _lock = Task::block_on(self.rwsem.write()); let mut store_new_end = None; let offset = match offset { @@ -545,7 +545,7 @@ impl Inode for FileInode { } fn truncate(&self, length: usize) -> KResult<()> { - let lock = Task::block_on(self.rwsem.write()); + let _lock = Task::block_on(self.rwsem.write()); Task::block_on(self.pages.resize(length))?; self.size.store(length as u64, Ordering::Relaxed); *self.mtime.lock() = Instant::now(); diff --git a/src/kernel/block.rs b/src/kernel/block.rs index 4a10e4c7..069ae4ca 100644 --- a/src/kernel/block.rs +++ b/src/kernel/block.rs @@ -285,6 +285,103 @@ impl BlockDevice { Ok(FillResult::Partial(nfilled)) } } + + /// Write some data to the block device, may involve some copy and fragmentation + /// + /// # Arguments + /// `offset` - offset in bytes + /// `data` - data to write + /// + pub fn write_some(&self, offset: usize, data: &[u8]) -> KResult { + let mut sector_start = offset as u64 / 512; + let mut first_sector_offset = offset as u64 % 512; + let mut remaining_data = data; + let mut nwritten = 0; + + while !remaining_data.is_empty() { + let pages: &[Page]; + let page: Option; + let page_vec: Option>; + + // Calculate sectors needed for this write + let write_end = first_sector_offset + remaining_data.len() as u64; + let sector_count = ((write_end + 511) / 512).min(self.queue().max_request_pages()); + + match sector_count { + count if count <= 8 => { + let _page = Page::alloc(); + page = Some(_page); + pages = core::slice::from_ref(page.as_ref().unwrap()); + } + count if count <= 16 => { + let _pages = Page::alloc_order(1); + page = Some(_pages); + pages = core::slice::from_ref(page.as_ref().unwrap()); + } + count => { + let npages = (count + 15) / 16; + let mut _page_vec = Vec::with_capacity(npages as usize); + for _ in 0..npages { + _page_vec.push(Page::alloc_order(1)); + } + page_vec = Some(_page_vec); + pages = page_vec.as_ref().unwrap().as_slice(); + } + } + + if first_sector_offset != 0 || remaining_data.len() < (sector_count * 512) as usize { + let read_req = BlockDeviceRequest::Read { + sector: sector_start, + count: sector_count, + buffer: pages, + }; + self.commit_request(read_req)?; + } + + let mut data_offset = 0; + let mut page_offset = first_sector_offset as usize; + + for page in pages.iter() { + // SAFETY: We own the page and can modify it + let page_data = unsafe { + let memblk = page.as_memblk(); + core::slice::from_raw_parts_mut(memblk.addr().get() as *mut u8, memblk.len()) + }; + + let copy_len = + (remaining_data.len() - data_offset).min(page_data.len() - page_offset); + + if copy_len == 0 { + break; + } + + page_data[page_offset..page_offset + copy_len] + .copy_from_slice(&remaining_data[data_offset..data_offset + copy_len]); + + data_offset += copy_len; + page_offset = 0; // Only first page has offset + + if data_offset >= remaining_data.len() { + break; + } + } + + let write_req = BlockDeviceRequest::Write { + sector: sector_start, + count: sector_count, + buffer: pages, + }; + self.commit_request(write_req)?; + + let bytes_written = data_offset; + nwritten += bytes_written; + remaining_data = &remaining_data[bytes_written..]; + sector_start += sector_count; + first_sector_offset = 0; + } + + Ok(nwritten) + } } pub enum BlockDeviceRequest<'lt> { diff --git a/src/kernel/mem.rs b/src/kernel/mem.rs index ce705cff..efd06824 100644 --- a/src/kernel/mem.rs +++ b/src/kernel/mem.rs @@ -12,5 +12,5 @@ pub use access::{AsMemoryBlock, MemoryBlock, PhysAccess}; pub(self) use mm_area::MMArea; pub use mm_list::{handle_kernel_page_fault, FileMapping, MMList, Mapping, Permission}; pub use page_alloc::{GlobalPageAlloc, RawPage}; -pub use page_cache::{CachePage, PageCache, PageCacheBackend}; +pub use page_cache::{CachePage, CachePageStream, PageCache, PageCacheBackend}; pub use paging::{Page, PageBuffer}; diff --git a/src/kernel/mem/page_cache.rs b/src/kernel/mem/page_cache.rs index 863e538e..fa475f9b 100644 --- a/src/kernel/mem/page_cache.rs +++ b/src/kernel/mem/page_cache.rs @@ -125,27 +125,32 @@ impl PageCache { pub async fn read(&self, buffer: &mut dyn Buffer, mut offset: usize) -> KResult { let mut pages = self.pages.lock().await; + let size = self.backend.upgrade().unwrap().size(); loop { + if offset >= size { + break; + } let page_id = offset >> PAGE_SIZE_BITS; let page = pages.get(&page_id); match page { Some(page) => { let inner_offset = offset % PAGE_SIZE; + let available_in_file = size.saturating_sub(offset); // TODO: still cause unnecessary IO if valid_size < PAGESIZE // and fill result is Done - if page.valid_size() == 0 - || buffer - .fill(&page.valid_data()[inner_offset..])? - .should_stop() + let page_data = &page.valid_data()[inner_offset..]; + let read_size = page_data.len().min(available_in_file); + + if read_size == 0 + || buffer.fill(&page_data[..read_size])?.should_stop() || buffer.available() == 0 { break; } - - offset += PAGE_SIZE - inner_offset; + offset += read_size; } None => { let mut new_page = CachePage::new(); @@ -217,7 +222,7 @@ impl PageCache { self.backend .upgrade() .unwrap() - .write_page(page, page_id << PAGE_SIZE_BITS)?; + .write_page(&mut CachePageStream::new(*page), page_id << PAGE_SIZE_BITS)?; page.clear_dirty(); } } @@ -293,6 +298,51 @@ impl PageCache { } } +pub struct CachePageStream { + page: CachePage, + cur: usize, +} + +impl CachePageStream { + pub fn new(page: CachePage) -> Self { + Self { page, cur: 0 } + } + + pub fn remaining(&self) -> usize { + self.page.valid_size().saturating_sub(self.cur) + } + + pub fn is_drained(&self) -> bool { + self.cur >= self.page.valid_size() + } +} + +impl Stream for CachePageStream { + fn poll_data<'a>(&mut self, buf: &'a mut [u8]) -> KResult> { + if self.cur >= self.page.valid_size() { + return Ok(None); + } + + let page_data = &self.page.all()[self.cur..self.page.valid_size()]; + let to_read = buf.len().min(page_data.len()); + + buf[..to_read].copy_from_slice(&page_data[..to_read]); + self.cur += to_read; + + Ok(Some(&mut buf[..to_read])) + } + + fn ignore(&mut self, len: usize) -> KResult> { + if self.cur >= self.page.valid_size() { + return Ok(None); + } + + let to_ignore = len.min(self.page.valid_size() - self.cur); + self.cur += to_ignore; + Ok(Some(to_ignore)) + } +} + // with this trait, "page cache" and "block cache" are unified, // for fs, offset is file offset (floor algin to PAGE_SIZE) // for blkdev, offset is block idx (floor align to PAGE_SIZE / BLK_SIZE) @@ -300,7 +350,7 @@ impl PageCache { pub trait PageCacheBackend { fn read_page(&self, page: &mut CachePage, offset: usize) -> KResult; - fn write_page(&self, page: &CachePage, offset: usize) -> KResult; + fn write_page(&self, page: &mut CachePageStream, offset: usize) -> KResult; fn size(&self) -> usize; } diff --git a/src/kernel/syscall.rs b/src/kernel/syscall.rs index 0276ebdf..2bcfa853 100644 --- a/src/kernel/syscall.rs +++ b/src/kernel/syscall.rs @@ -56,19 +56,19 @@ impl SyscallRetVal for u32 { } } -impl SyscallRetVal for usize { +impl SyscallRetVal for i32 { fn into_retval(self) -> Option { - Some(self) + Some(self as usize) } } -impl SyscallRetVal for isize { +impl SyscallRetVal for usize { fn into_retval(self) -> Option { - Some(self as usize) + Some(self) } } -impl SyscallRetVal for i32 { +impl SyscallRetVal for isize { fn into_retval(self) -> Option { Some(self as usize) } diff --git a/src/kernel/syscall/file_rw.rs b/src/kernel/syscall/file_rw.rs index 5683b27e..9c8af2aa 100644 --- a/src/kernel/syscall/file_rw.rs +++ b/src/kernel/syscall/file_rw.rs @@ -1,12 +1,13 @@ use core::time::Duration; use super::FromSyscallArg; -use crate::io::IntoStream; +use crate::io::{ByteBuffer, IntoStream}; use crate::kernel::constants::{ EBADF, EFAULT, EINVAL, ENOENT, ENOSYS, ENOTDIR, SEEK_CUR, SEEK_END, SEEK_SET, S_IFBLK, S_IFCHR, }; use crate::kernel::task::Thread; use crate::kernel::timer::sleep; +use crate::kernel::vfs::file::File; use crate::kernel::vfs::filearray::FD; use crate::{ io::{Buffer, BufferFill}, @@ -77,20 +78,21 @@ fn dentry_from( fn read(fd: FD, buffer: *mut u8, bufsize: usize) -> KResult { let mut buffer = UserBuffer::new(buffer, bufsize)?; - Task::block_on(thread.files.get(fd).ok_or(EBADF)?.read(&mut buffer, None)) + Task::block_on(thread.files.get(fd).ok_or(EBADF)?.read(&mut buffer)) } #[eonix_macros::define_syscall(SYS_PREAD64)] -fn pread64(fd: FD, buffer: *mut u8, bufsize: usize, offset: usize) -> KResult { +fn pread(fd: FD, buffer: *mut u8, bufsize: usize, offset: usize) -> KResult { + if thread.files.get(fd).ok_or(EBADF)?.size()? == 0 { + return Ok(0); + } let mut buffer = UserBuffer::new(buffer, bufsize)?; - - Task::block_on( - thread - .files - .get(fd) - .ok_or(EBADF)? - .read(&mut buffer, Some(offset)), - ) + let nread = thread + .files + .get(fd) + .ok_or(EBADF)? + .read_at(&mut buffer, offset)?; + Ok(nread) } #[eonix_macros::define_syscall(SYS_WRITE)] @@ -98,7 +100,7 @@ fn write(fd: FD, buffer: *const u8, count: usize) -> KResult { let buffer = CheckedUserPointer::new(buffer, count)?; let mut stream = buffer.into_stream(); - Task::block_on(thread.files.get(fd).ok_or(EBADF)?.write(&mut stream, None)) + Task::block_on(thread.files.get(fd).ok_or(EBADF)?.write(&mut stream)) } #[eonix_macros::define_syscall(SYS_PWRITE64)] @@ -106,13 +108,11 @@ fn pwrite64(fd: FD, buffer: *const u8, count: usize, offset: usize) -> KResult KResul thread.files.open(&dentry, flags, mode) } +#[eonix_macros::define_syscall(SYS_FTRUNCATE64)] +fn ftruncate(fd: FD, new_size: usize) -> KResult<()> { + thread.files.get(fd).ok_or(EBADF)?.truncate(new_size) +} + +fn do_copy_or_splice( + file_in: Arc, + off_in: *mut isize, + file_out: Arc, + off_out: *mut isize, + len: usize, + is_splice: bool, +) -> KResult { + let (input_offset, input_buffer) = match off_in.is_null() { + true => (None, None), + false => { + let buffer = UserBuffer::new(off_in as *mut u8, size_of::())?; + let offset = isize::from_le_bytes(buffer.as_slice().try_into().unwrap()); + if offset > file_in.size()?.try_into().unwrap() { + return Ok(0); + } + if offset < 0 { + return Ok(-1); + } + (Some(offset), Some(buffer)) + } + }; + + let (output_offset, output_buffer) = match off_out.is_null() { + true => (None, None), + false => { + let buffer = UserBuffer::new(off_out as *mut u8, size_of::())?; + let offset = isize::from_le_bytes(buffer.as_slice().try_into().unwrap()); + if offset < 0 { + return Ok(-1); + } + (Some(offset), Some(buffer)) + } + }; + + let mut total_copied = 0usize; + let mut remaining = len; + let buffer_size = 4096; + let mut buffer = vec![0u8; buffer_size]; + + while remaining > 0 { + let to_read = core::cmp::min(remaining, buffer_size); + if to_read == 0 { + break; + } + + let mut byte_buffer = ByteBuffer::new(&mut buffer[..to_read]); + let read_bytes = match input_offset { + Some(offset) => file_in.read_at(&mut byte_buffer, offset as usize + total_copied)?, + None => Task::block_on(file_in.read(&mut byte_buffer))?, + }; + if read_bytes == 0 { + break; + } + + let mut stream = (&buffer[..read_bytes]).into_stream(); + let written_bytes = match output_offset { + Some(offset) => file_out.write_at(&mut stream, offset as usize + total_copied)?, + None => Task::block_on(file_out.write(&mut stream))?, + }; + if written_bytes == 0 { + break; + } + + total_copied += written_bytes; + remaining -= written_bytes; + + if written_bytes < read_bytes { + return Ok(-1); + } + + if is_splice && input_offset.is_none() { + break; + } + } + + match (input_offset, input_buffer) { + (Some(offset), Some(mut buffer)) => { + let _ = buffer.fill(&(offset + total_copied as isize).to_le_bytes()); + } + _ => (), + } + + match (output_offset, output_buffer) { + (Some(offset), Some(mut buffer)) => { + let _ = buffer.fill(&(offset + total_copied as isize).to_le_bytes()); + } + _ => (), + } + + Ok(total_copied as isize) +} + +#[eonix_macros::define_syscall(SYS_COPY_FILE_RANGE)] +fn copy_file_range( + fd_in: FD, + off_in: *mut isize, + fd_out: FD, + off_out: *mut isize, + len: usize, + _flags: u32, +) -> KResult { + if len == 0 { + return Ok(0); + } + + let file_in = thread.files.get(fd_in).ok_or(EBADF)?.clone(); + let file_out = thread.files.get(fd_out).ok_or(EBADF)?.clone(); + + do_copy_or_splice(file_in, off_in, file_out, off_out, len, false) +} + +#[eonix_macros::define_syscall(SYS_SPLICE)] +fn splice( + fd_in: FD, + off_in: *mut isize, + fd_out: FD, + off_out: *mut isize, + len: usize, + _flags: u32, +) -> KResult { + if len == 0 { + return Ok(0); + } + + let file_in = thread.files.get(fd_in).ok_or(EBADF)?.clone(); + let file_out = thread.files.get(fd_out).ok_or(EBADF)?.clone(); + + do_copy_or_splice(file_in, off_in, file_out, off_out, len, true) +} + #[cfg(target_arch = "x86_64")] #[eonix_macros::define_syscall(SYS_OPEN)] fn open(path: *const u8, flags: OpenFlags, mode: u32) -> KResult { @@ -390,7 +526,7 @@ fn readv(fd: FD, iov_user: *const IoVec, iovcnt: u32) -> KResult { let mut tot = 0usize; for mut buffer in iov_buffers.into_iter() { // TODO!!!: `readv` - let nread = Task::block_on(file.read(&mut buffer, None))?; + let nread = Task::block_on(file.read(&mut buffer))?; tot += nread; if nread != buffer.total() { @@ -426,7 +562,7 @@ fn writev(fd: FD, iov_user: *const IoVec, iovcnt: u32) -> KResult { let mut tot = 0usize; for mut stream in iov_streams.into_iter() { - let nread = Task::block_on(file.write(&mut stream, None))?; + let nread = Task::block_on(file.write(&mut stream))?; tot += nread; if nread == 0 || !stream.is_drained() { @@ -550,7 +686,7 @@ fn pselect6( } let timeout = UserPointerMut::new(timeout)?; - + // Read here to check for invalid pointers. let _timeout_value = timeout.read()?; diff --git a/src/kernel/user/dataflow.rs b/src/kernel/user/dataflow.rs index 17dbd4c9..480cfa66 100644 --- a/src/kernel/user/dataflow.rs +++ b/src/kernel/user/dataflow.rs @@ -354,6 +354,12 @@ impl UserBuffer<'_> { fn remaining(&self) -> usize { self.size - self.cur } + + /// this is for comp test + pub fn as_slice(&self) -> &[u8] { + // SAFETY: the pointer's validity is checked in `new` + unsafe { core::slice::from_raw_parts(self.ptr.ptr, self.ptr.len) } + } } impl<'lt> Buffer for UserBuffer<'lt> { diff --git a/src/kernel/vfs/dentry.rs b/src/kernel/vfs/dentry.rs index bd516bf2..417f54e6 100644 --- a/src/kernel/vfs/dentry.rs +++ b/src/kernel/vfs/dentry.rs @@ -405,6 +405,10 @@ impl Dentry { } impl Dentry { + pub fn size(&self) -> usize { + self.get_inode().unwrap().file_size() + } + pub fn read(&self, buffer: &mut dyn Buffer, offset: usize) -> KResult { let inode = self.get_inode()?; diff --git a/src/kernel/vfs/file.rs b/src/kernel/vfs/file.rs index 49cb1d44..564be93f 100644 --- a/src/kernel/vfs/file.rs +++ b/src/kernel/vfs/file.rs @@ -11,6 +11,7 @@ use crate::{ task::Thread, terminal::{Terminal, TerminalIORequest}, user::{UserPointer, UserPointerMut}, + vfs::inode::Ino, vfs::inode::Inode, CharDevice, }, @@ -334,7 +335,7 @@ impl InodeFile { Ok(new_cursor) } - fn write(&self, stream: &mut dyn Stream, offset: Option) -> KResult { + fn write(&self, stream: &mut dyn Stream) -> KResult { if !self.write { return Err(EBADF); } @@ -346,35 +347,47 @@ impl InodeFile { Ok(nwrote) } else { - let nwrote = if let Some(offset) = offset { - self.dentry.write(stream, WriteOffset::Position(offset))? - } else { - let nwrote = self.dentry.write(stream, WriteOffset::Position(*cursor))?; - *cursor += nwrote; - nwrote - }; + let nwrote = self.dentry.write(stream, WriteOffset::Position(*cursor))?; + *cursor += nwrote; Ok(nwrote) } } - fn read(&self, buffer: &mut dyn Buffer, offset: Option) -> KResult { + fn write_at(&self, stream: &mut dyn Stream, offset: usize) -> KResult { + if !self.write { + return Err(EBADF); + } + + let nwrote = self.dentry.write(stream, WriteOffset::Position(offset))?; + Ok(nwrote) + } + + fn read(&self, buffer: &mut dyn Buffer) -> KResult { + if self.size() == 0 { + return Ok(0); + } if !self.read { return Err(EBADF); } - let nread = if let Some(offset) = offset { - let nread = self.dentry.read(buffer, offset)?; - nread - } else { - let mut cursor = Task::block_on(self.cursor.lock()); + let mut cursor = Task::block_on(self.cursor.lock()); + let nread = self.dentry.read(buffer, *cursor)?; - let nread = self.dentry.read(buffer, *cursor)?; + *cursor += nread; + Ok(nread) + } - *cursor += nread; - nread - }; + // TODO: error handle + fn read_at(&self, buffer: &mut dyn Buffer, offset: usize) -> KResult { + if self.size() == 0 { + return Ok(0); + } + if !self.read { + return Err(EBADF); + } + let nread = self.dentry.read(buffer, offset)?; Ok(nread) } @@ -436,6 +449,14 @@ impl InodeFile { *cursor += nread; Ok(()) } + + fn size(&self) -> usize { + self.dentry.size() + } + + fn truncate(&self, new_size: usize) -> KResult<()> { + self.dentry.truncate(new_size) + } } impl TerminalFile { @@ -478,9 +499,9 @@ impl TerminalFile { } impl FileType { - pub async fn read(&self, buffer: &mut dyn Buffer, offset: Option) -> KResult { + pub async fn read(&self, buffer: &mut dyn Buffer) -> KResult { match self { - FileType::Inode(inode) => inode.read(buffer, offset), + FileType::Inode(inode) => inode.read(buffer), FileType::PipeRead(pipe) => pipe.pipe.read(buffer).await, FileType::TTY(tty) => tty.read(buffer).await, FileType::CharDev(device) => device.read(buffer), @@ -488,6 +509,13 @@ impl FileType { } } + pub fn read_at(&self, buffer: &mut dyn Buffer, offset: usize) -> KResult { + match self { + FileType::Inode(inode) => inode.read_at(buffer, offset), + _ => Err(EBADF), + } + } + // TODO // /// Read from the file into the given buffers. // /// @@ -503,9 +531,9 @@ impl FileType { // } // } - pub async fn write(&self, stream: &mut dyn Stream, offset: Option) -> KResult { + pub async fn write(&self, stream: &mut dyn Stream) -> KResult { match self { - FileType::Inode(inode) => inode.write(stream, offset), + FileType::Inode(inode) => inode.write(stream), FileType::PipeWrite(pipe) => pipe.pipe.write(stream).await, FileType::TTY(tty) => tty.write(stream), FileType::CharDev(device) => device.write(stream), @@ -513,6 +541,13 @@ impl FileType { } } + pub fn write_at(&self, stream: &mut dyn Stream, offset: usize) -> KResult { + match self { + FileType::Inode(inode) => inode.write_at(stream, offset), + _ => Err(EBADF), + } + } + pub fn seek(&self, option: SeekOption) -> KResult { match self { FileType::Inode(inode) => inode.seek(option), @@ -520,6 +555,20 @@ impl FileType { } } + pub fn size(&self) -> KResult { + match self { + FileType::Inode(inode) => Ok(inode.size()), + _ => Err(EINVAL), + } + } + + pub fn truncate(&self, new_size: usize) -> KResult<()> { + match self { + FileType::Inode(inode) => inode.truncate(new_size), + _ => Err(EINVAL), + } + } + pub fn getdents(&self, buffer: &mut dyn Buffer) -> KResult<()> { match self { FileType::Inode(inode) => inode.getdents(buffer), @@ -549,16 +598,12 @@ impl FileType { if Thread::current().signal_list.has_pending_signal() { return if cur == 0 { Err(EINTR) } else { Ok(cur) }; } - let nread = self - .read(&mut ByteBuffer::new(&mut buffer[..len]), None) - .await?; + let nread = self.read(&mut ByteBuffer::new(&mut buffer[..len])).await?; if nread == 0 { break; } - let nwrote = dest_file - .write(&mut buffer[..nread].into_stream(), None) - .await?; + let nwrote = dest_file.write(&mut buffer[..nread].into_stream()).await?; nsent += nwrote; if nwrote != len { diff --git a/src/kernel/vfs/inode.rs b/src/kernel/vfs/inode.rs index 2b52043d..88bc0f37 100644 --- a/src/kernel/vfs/inode.rs +++ b/src/kernel/vfs/inode.rs @@ -96,6 +96,10 @@ pub struct RenameData<'a, 'b> { #[allow(unused_variables)] pub trait Inode: Send + Sync + InodeInner + Any { + fn file_size(&self) -> usize { + self.size.load(Ordering::Relaxed) as usize + } + fn is_dir(&self) -> bool { self.mode.load(Ordering::SeqCst) & S_IFDIR != 0 } @@ -136,7 +140,7 @@ pub trait Inode: Send + Sync + InodeInner + Any { Err(if self.is_dir() { EISDIR } else { EINVAL }) } - fn write_direct(&self, stream: &mut dyn Stream, offset: WriteOffset) -> KResult { + fn write_direct(&self, stream: &mut dyn Stream, offset: usize) -> KResult { Err(if self.is_dir() { EISDIR } else { EINVAL }) }