Bladeren bron

feat(fs): implement RLIMIT_FSIZE limit checking (#1312)

- Add file size limit checking before write operations
- Send SIGXFSZ signal when exceeding file size limit
- Handle partial writes when approaching the limit
- Add check for O_PATH file descriptors in writeable method
- Update test configurations for write limit tests

Signed-off-by: longjin <longjin@DragonOS.org>
LoGin 3 weken geleden
bovenliggende
commit
7059be4b90

+ 44 - 7
kernel/src/filesystem/vfs/file.rs

@@ -15,9 +15,9 @@ use crate::{
         procfs::ProcfsFilePrivateData,
         vfs::FilldirContext,
     },
-    ipc::pipe::PipeFsPrivateData,
+    ipc::{kill::kill_process, pipe::PipeFsPrivateData},
     libs::{rwlock::RwLock, spinlock::SpinLock},
-    process::{cred::Cred, ProcessManager},
+    process::{cred::Cred, resource::RLimitID, ProcessManager},
 };
 
 /// 文件私有信息的枚举类型
@@ -272,20 +272,50 @@ impl File {
             return Err(SystemError::ENOBUFS);
         }
 
+        // 检查RLIMIT_FSIZE限制
+        let current_pcb = ProcessManager::current_pcb();
+        let fsize_limit = current_pcb.get_rlimit(RLimitID::Fsize);
+
+        // 计算实际可写入的长度
+        let actual_len = if fsize_limit.rlim_cur != u64::MAX {
+            let limit = fsize_limit.rlim_cur as usize;
+
+            // 如果当前文件大小已经达到或超过限制,不允许写入
+            if offset >= limit {
+                // 发送SIGXFSZ信号
+                let _ = kill_process(
+                    current_pcb.raw_pid(),
+                    crate::arch::ipc::signal::Signal::SIGXFSZ,
+                );
+                return Err(SystemError::EFBIG);
+            }
+
+            // 计算可写入的最大长度(不超过限制)
+            let max_writable = limit.saturating_sub(offset);
+            if len > max_writable {
+                // 部分写入:只写到限制位置,不发送信号
+                max_writable
+            } else {
+                len
+            }
+        } else {
+            len
+        };
+
         // 如果文件指针已经超过了文件大小,则需要扩展文件大小
         if offset > self.inode.metadata()?.size as usize {
             self.inode.resize(offset)?;
         }
-        let len = self
+        let written_len = self
             .inode
-            .write_at(offset, len, buf, self.private_data.lock())?;
+            .write_at(offset, actual_len, buf, self.private_data.lock())?;
 
         if update_offset {
             self.offset
-                .fetch_add(len, core::sync::atomic::Ordering::SeqCst);
+                .fetch_add(written_len, core::sync::atomic::Ordering::SeqCst);
         }
 
-        Ok(len)
+        Ok(written_len)
     }
 
     /// @brief 获取文件的元数据
@@ -345,8 +375,15 @@ impl File {
     /// @brief 判断当前文件是否可写
     #[inline]
     pub fn writeable(&self) -> Result<(), SystemError> {
+        let mode = *self.mode.read();
+
+        // 检查是否是O_PATH文件描述符
+        if mode.contains(FileMode::O_PATH) {
+            return Err(SystemError::EBADF);
+        }
+
         // 暂时认为只要不是read only, 就可写
-        if *self.mode.read() == FileMode::O_RDONLY {
+        if mode == FileMode::O_RDONLY {
             return Err(SystemError::EPERM);
         }
 

+ 6 - 0
kernel/src/process/mod.rs

@@ -963,6 +963,12 @@ impl ProcessControlBlock {
         };
         arr[RLimitID::Rss as usize] = arr[RLimitID::As as usize];
 
+        // 设置文件大小限制的默认值 (Linux默认通常是unlimited)
+        arr[RLimitID::Fsize as usize] = RLimit64 {
+            rlim_cur: u64::MAX,
+            rlim_max: u64::MAX,
+        };
+
         arr
     }
 

+ 6 - 0
user/apps/tests/syscall/gvisor/blocklists/write_test

@@ -0,0 +1,6 @@
+# 由于缺少SYS_RT_SIGTIMEDWAIT而失败
+WriteTest.WriteExceedsRLimit
+# 缺少pwritev而失败
+WriteTest.PartialWriteSIGSEGV
+# 缺少pwritev而失败
+WriteTest.PartialWriteSIGBUS

+ 1 - 0
user/apps/tests/syscall/gvisor/whitelist.txt

@@ -10,6 +10,7 @@ uname_test
 
 # 文件系统相关测试
 dup_test
+write_test
 #stat_test
 #chmod_test
 #chown_test