Explorar el Código

feat(fs): impl rename

Heinz hace 6 meses
padre
commit
1d1a0257ba
Se han modificado 1 ficheros con 111 adiciones y 4 borrados
  1. 111 4
      src/fs/ext4.rs

+ 111 - 4
src/fs/ext4.rs

@@ -4,11 +4,14 @@ use crate::{
     io::{Buffer, ByteBuffer, Stream},
     kernel::{
         block::BlockDevice,
-        constants::{EIO, S_IFDIR, S_IFREG},
+        constants::{EEXIST, EINVAL, EIO, ENOSYS, S_IFDIR, S_IFREG},
         timer::Instant,
         vfs::{
             dentry::{dcache, Dentry},
-            inode::{define_struct_inode, AtomicNlink, Ino, Inode, InodeData, Mode, WriteOffset},
+            inode::{
+                define_struct_inode, AtomicNlink, Ino, Inode, InodeData, Mode, RenameData,
+                WriteOffset,
+            },
             mount::{register_filesystem, Mount, MountCreator},
             s_isdir, s_isreg,
             vfs::Vfs,
@@ -27,6 +30,7 @@ use another_ext4::{
 };
 use eonix_runtime::task::Task;
 use eonix_sync::RwLock;
+use xmas_elf::dynamic::FLAG_1_NOW;
 
 pub struct Ext4BlockDevice {
     device: Arc<BlockDevice>,
@@ -110,6 +114,20 @@ impl Ext4Fs {
             .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<Ino, Ext4Inode>,
@@ -280,6 +298,28 @@ impl Inode for FileInode {
         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::<Ext4Fs>().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<()> {
         Ok(())
@@ -313,12 +353,10 @@ impl DirInode {
     }
 
     fn link_file(&self) {
-        // TODO
         self.size.fetch_add(1, Ordering::Relaxed);
     }
 
     fn link_dir(&self) {
-        // TODO
         self.nlink.fetch_add(1, Ordering::Relaxed);
         self.size.fetch_add(1, Ordering::Relaxed);
     }
@@ -501,6 +539,75 @@ impl Inode for DirInode {
 
         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::<Ext4Fs>().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::<Ext4Fs>().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();
+        *self.mtime.lock() = now;
+        *old_file.ctime.lock() = now;
+        self.size.fetch_sub(1, Ordering::Relaxed);
+
+        Task::block_on(dcache::d_exchange(old_dentry, new_dentry));
+
+        Ok(())
+    }
 }
 
 struct Ext4MountCreator;