浏览代码

feat(fs): impl write, create and mkdir for ext4 fs

Heinz 6 月之前
父节点
当前提交
f05037374c
共有 3 个文件被更改,包括 266 次插入7 次删除
  1. 17 1
      src/driver/virtio/virtio_blk.rs
  2. 152 6
      src/fs/ext4.rs
  3. 97 0
      src/kernel/block.rs

+ 17 - 1
src/driver/virtio/virtio_blk.rs

@@ -18,7 +18,23 @@ impl BlockRequestQueue for Spin<VirtIOBlk<HAL, MmioTransport<'_>>> {
 
     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,

+ 152 - 6
src/fs/ext4.rs

@@ -1,14 +1,14 @@
-use core::sync::atomic::{AtomicU32, AtomicU64};
+use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
 
 use crate::{
-    io::{Buffer, ByteBuffer},
+    io::{Buffer, ByteBuffer, Stream},
     kernel::{
         block::BlockDevice,
-        constants::EIO,
+        constants::{EIO, S_IFDIR, S_IFREG},
         timer::Instant,
         vfs::{
             dentry::Dentry,
-            inode::{define_struct_inode, AtomicNlink, Ino, Inode, InodeData},
+            inode::{define_struct_inode, AtomicNlink, Ino, Inode, InodeData, Mode, WriteOffset},
             mount::{register_filesystem, Mount, MountCreator},
             s_isdir, s_isreg,
             vfs::Vfs,
@@ -20,7 +20,7 @@ use crate::{
 };
 use alloc::{
     collections::btree_map::{BTreeMap, Entry},
-    sync::Arc,
+    sync::{Arc, Weak},
 };
 use another_ext4::{
     Block, BlockDevice as Ext4BlockDeviceTrait, Ext4, FileType, InodeMode, PBlockId,
@@ -55,7 +55,9 @@ impl Ext4BlockDeviceTrait for Ext4BlockDevice {
     }
 
     fn write_block(&self, block: &another_ext4::Block) {
-        todo!()
+        let _ = self
+            .device
+            .write_some((block.id as usize) * 4096, &block.data);
     }
 }
 
@@ -85,6 +87,20 @@ impl Ext4Fs {
         icache.get(&ino).cloned().map(Ext4Inode::into_inner)
     }
 
+    fn update_modify_inode(&self, ino: u64, size: u64, mtime: u32) {
+        let _ = self.inner.setattr(
+            ino as u32,
+            None,
+            None,
+            None,
+            Some(size),
+            None,
+            Some(mtime),
+            None,
+            None,
+        );
+    }
+
     fn get_or_insert(
         &self,
         icache: &mut BTreeMap<Ino, Ext4Inode>,
@@ -185,6 +201,21 @@ define_struct_inode! {
     struct DirInode;
 }
 
+impl FileInode {
+    pub fn new(ino: Ino, vfs: Weak<dyn Vfs>, mode: Mode) -> Arc<Self> {
+        Arc::new_cyclic(|_| FileInode {
+            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
+            },
+        })
+    }
+}
+
 impl Inode for FileInode {
     fn read(&self, buffer: &mut dyn Buffer, offset: usize) -> KResult<usize> {
         let vfs = self.vfs.upgrade().ok_or(EIO)?;
@@ -199,6 +230,68 @@ impl Inode for FileInode {
             Err(e) => Err(e.code() as u32),
         }
     }
+
+    fn write(&self, stream: &mut dyn Stream, offset: WriteOffset) -> KResult<usize> {
+        let _lock = Task::block_on(self.rwsem.write());
+
+        let vfs = self.vfs.upgrade().ok_or(EIO)?;
+        let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
+
+        let mut temp_buf = vec![0u8; 4096];
+        let mut total_written = 0;
+
+        let offset = match offset {
+            WriteOffset::Position(offset) => offset,
+            // TODO: here need to add some operate
+            WriteOffset::End(end) => *end,
+        };
+
+        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;
+            }
+        }
+
+        let mtime = Instant::now();
+        *self.mtime.lock() = mtime;
+        let new_size = (offset + total_written) as u64;
+        self.size
+            .store(offset as u64 + total_written as u64, Ordering::Relaxed);
+        ext4fs.update_modify_inode(self.ino, new_size, mtime.since_epoch().as_secs() as u32);
+
+        Ok(total_written)
+    }
+
+    // TODO
+    fn truncate(&self, length: usize) -> KResult<()> {
+        Ok(())
+    }
+}
+
+impl DirInode {
+    fn new(idata: InodeData) -> Arc<Self> {
+        let inode = Arc::new(Self { idata });
+
+        inode
+    }
+
+    fn link(&self, file: &dyn Inode) {
+        let now = Instant::now();
+
+        // SAFETY: Only `unlink` will do something based on `nlink` count
+        //         No need to synchronize here
+        file.nlink.fetch_add(1, Ordering::Relaxed);
+        *self.ctime.lock() = now;
+
+        // SAFETY: `rwsem` has done the synchronization
+        self.size.fetch_add(1, Ordering::Relaxed);
+        *self.mtime.lock() = now;
+    }
 }
 
 impl Inode for DirInode {
@@ -291,6 +384,59 @@ impl Inode for DirInode {
         }
         Ok(current_offset)
     }
+
+    fn creat(&self, at: &Arc<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::<Ext4Fs>().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.ctime.lock() = now;
+        // SAFETY: `rwsem` has done the synchronization
+        self.size.fetch_add(1, Ordering::Relaxed);
+        *self.mtime.lock() = now;
+
+        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::<Ext4Fs>().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 newdir = DirInode::new(InodeData::new(new_ino as u64, self.vfs.clone()));
+
+        self.link(newdir.as_ref());
+        at.save_dir(newdir)
+    }
 }
 
 struct Ext4MountCreator;

+ 97 - 0
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<usize> {
+        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<Page>;
+            let page_vec: Option<Vec<Page>>;
+
+            // 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> {