diff --git a/kernel/src/driver/base/block/gendisk/mod.rs b/kernel/src/driver/base/block/gendisk/mod.rs index f5953d55aa..48ea8df414 100644 --- a/kernel/src/driver/base/block/gendisk/mod.rs +++ b/kernel/src/driver/base/block/gendisk/mod.rs @@ -16,7 +16,7 @@ use crate::{ driver::{base::device::device_number::DeviceNumber, block::loop_device::LoopDevice}, filesystem::{ devfs::{DevFS, DeviceINode, LockedDevFSInode}, - vfs::{utils::DName, IndexNode, InodeMode, Metadata}, + vfs::{utils::DName, FilePrivateData, FileType, IndexNode, InodeMode, Metadata}, }, libs::{mutex::MutexGuard, rwlock::RwLock}, }; @@ -219,6 +219,18 @@ impl IndexNode for GenDisk { self } + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + file_type == FileType::BlockDevice + } + + fn sync_file( + &self, + _datasync: bool, + _data: MutexGuard, + ) -> Result<(), SystemError> { + self.sync() + } + fn read_at( &self, offset: usize, diff --git a/kernel/src/driver/block/loop_device/loop_device.rs b/kernel/src/driver/block/loop_device/loop_device.rs index e6048e94ba..51bde201ee 100644 --- a/kernel/src/driver/block/loop_device/loop_device.rs +++ b/kernel/src/driver/block/loop_device/loop_device.rs @@ -1010,6 +1010,18 @@ impl IndexNode for LoopDevice { self } + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + file_type == FileType::BlockDevice + } + + fn sync_file( + &self, + _datasync: bool, + _data: MutexGuard, + ) -> Result<(), SystemError> { + ::sync(self) + } + fn read_at( &self, offset: usize, @@ -1330,6 +1342,8 @@ impl BlockDevice for LoopDevice { } fn sync(&self) -> Result<(), SystemError> { + let _io_guard = IoGuard::new(self)?; + let inode = self.inner().file_inode.clone().ok_or(SystemError::ENODEV)?; inode.sync()?; inode.fs().sync_fs(true) diff --git a/kernel/src/driver/block/pmem/device.rs b/kernel/src/driver/block/pmem/device.rs index 2514ea895a..d5959140ea 100644 --- a/kernel/src/driver/block/pmem/device.rs +++ b/kernel/src/driver/block/pmem/device.rs @@ -30,7 +30,10 @@ use crate::{ filesystem::{ devfs::{DevFS, DeviceINode, LockedDevFSInode}, kernfs::KernFSInode, - vfs::{utils::DName, FilePrivateData, IndexNode, InodeFlags, InodeId, InodeMode, Metadata}, + vfs::{ + utils::DName, FilePrivateData, FileType, IndexNode, InodeFlags, InodeId, InodeMode, + Metadata, + }, }, libs::{ align::{page_align_down, page_align_up}, @@ -222,6 +225,18 @@ impl IndexNode for PmemBlockDevice { self } + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + file_type == FileType::BlockDevice + } + + fn sync_file( + &self, + _datasync: bool, + _data: MutexGuard, + ) -> Result<(), SystemError> { + ::sync(self) + } + fn read_at( &self, _offset: usize, diff --git a/kernel/src/driver/block/virtio_blk.rs b/kernel/src/driver/block/virtio_blk.rs index f0925ffffc..45874b5105 100644 --- a/kernel/src/driver/block/virtio_blk.rs +++ b/kernel/src/driver/block/virtio_blk.rs @@ -58,7 +58,7 @@ use crate::{ devfs::{DevFS, DeviceINode, LockedDevFSInode}, kernfs::KernFSInode, mbr::MbrDiskPartionTable, - vfs::{utils::DName, IndexNode, InodeMode, Metadata}, + vfs::{utils::DName, FilePrivateData, FileType, IndexNode, InodeMode, Metadata}, }, init::initcall::INITCALL_POSTCORE, libs::{ @@ -849,6 +849,19 @@ impl IndexNode for VirtIOBlkDevice { fn as_any_ref(&self) -> &dyn core::any::Any { self } + + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + file_type == FileType::BlockDevice + } + + fn sync_file( + &self, + _datasync: bool, + _data: MutexGuard, + ) -> Result<(), SystemError> { + ::sync(self) + } + fn read_at( &self, _offset: usize, diff --git a/kernel/src/filesystem/ext4/inode.rs b/kernel/src/filesystem/ext4/inode.rs index a0f3a62ca9..d6130c726c 100644 --- a/kernel/src/filesystem/ext4/inode.rs +++ b/kernel/src/filesystem/ext4/inode.rs @@ -280,6 +280,10 @@ impl IndexNode for LockedExt4Inode { Some(self.fs()) } + fn supports_post_write_sync(&self, file_type: vfs::FileType) -> bool { + file_type == vfs::FileType::File + } + fn retention_state(&self) -> Option<&InodeRetentionState> { Some(&self.retention) } diff --git a/kernel/src/filesystem/fuse/inode/vfs.rs b/kernel/src/filesystem/fuse/inode/vfs.rs index ba01bbf484..a612fd69db 100644 --- a/kernel/src/filesystem/fuse/inode/vfs.rs +++ b/kernel/src/filesystem/fuse/inode/vfs.rs @@ -48,6 +48,10 @@ impl IndexNode for FuseNode { self.try_fs() } + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + file_type == FileType::File + } + fn as_any_ref(&self) -> &dyn core::any::Any { self } diff --git a/kernel/src/filesystem/ramfs/mod.rs b/kernel/src/filesystem/ramfs/mod.rs index 8d040c20b8..38df0ebd30 100644 --- a/kernel/src/filesystem/ramfs/mod.rs +++ b/kernel/src/filesystem/ramfs/mod.rs @@ -348,6 +348,10 @@ impl IndexNode for LockedRamFSInode { Some(self.fs()) } + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + file_type == FileType::File + } + fn mmap(&self, _start: usize, _len: usize, _offset: usize) -> Result<(), SystemError> { Ok(()) } diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index 626acda291..213aa26d21 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -10,7 +10,7 @@ use system_error::SystemError; use super::{ append_lock::{with_inode_append_lock, AppendLockKey}, inode_lifecycle::{InodeRetentionGuard, InodeRetentionKind}, - mount::{MountExternalGuard, MountFSInode}, + mount::{MountExternalGuard, MountFSInode, MountFlags}, utils::should_remove_sgid, DirectoryEntry, FileSystem, FileType, IndexNode, InodeId, Metadata, SetMetadataMask, SpecialNodeData, @@ -205,10 +205,6 @@ struct WriteConfig { update_offset: bool, /// 偏移量更新方式 offset_update: OffsetUpdate, - /// 文件标志 - flags: FileFlags, - /// inode 标志 - inode_flags: InodeFlags, } /// Namespace fd backing data, typically created from /proc/thread-self/ns/* files. #[derive(Clone)] @@ -826,11 +822,25 @@ impl File { Ok(len.min(limit.saturating_sub(offset))) } + fn combined_mount_flags(&self) -> MountFlags { + match self.inode.clone().downcast_arc::() { + Some(mnt_inode) => mnt_inode.mount_fs().combined_flags(), + None => self.inode.mount_flags(), + } + } + fn maybe_sync_after_write( &self, + file_type: FileType, + start: usize, + written_len: usize, flags: FileFlags, inode_flags: InodeFlags, ) -> Result<(), SystemError> { + if written_len == 0 { + return Ok(()); + } + // O_SYNC 包含 O_DSYNC 位,所以只需检查 O_DSYNC 即可判断是否需要数据同步 let need_data_sync = flags.contains(FileFlags::O_DSYNC); // 检查是否需要元数据同步(O_SYNC = __O_SYNC | O_DSYNC) @@ -838,17 +848,23 @@ impl File { // inode 级别的 S_SYNC 标志 let inode_sync = inode_flags.contains(InodeFlags::S_SYNC); + // Linux IS_SYNC(inode) 同时包含 inode S_SYNC 与 superblock/mount sync 语义。 + let mount_sync = self + .combined_mount_flags() + .contains(MountFlags::SYNCHRONOUS); - if need_data_sync || inode_sync { - if need_metadata_sync || inode_sync { - // O_SYNC 或 S_SYNC: 完整同步(数据 + 元数据) - self.inode.sync_file(false, self.private_data.lock())?; - } else { - // O_DSYNC: 仅数据同步 - self.inode.sync_file(true, self.private_data.lock())?; - } + if !(need_data_sync || inode_sync || mount_sync) { + return Ok(()); } - Ok(()) + + if !self.inode.supports_post_write_sync(file_type) { + return Ok(()); + } + + let end = start.saturating_add(written_len).saturating_sub(1); + // Linux generic_write_sync() 只有 IOCB_SYNC 才请求完整 metadata sync; + // O_DSYNC、S_SYNC 与 SB_SYNCHRONOUS 都按 datasync 处理。 + self.sync_range_and_check_wb_error(start, end, !need_metadata_sync) } #[inline(never)] @@ -919,7 +935,6 @@ impl File { } } - self.maybe_sync_after_write(config.flags, config.inode_flags)?; Ok(written_len) } /// @brief 创建一个新的文件对象 @@ -1484,31 +1499,32 @@ impl File { WriteConfig { update_offset, offset_update: OffsetUpdate::StoreEnd, - flags, - inode_flags, }, ) + .map(|written_len| (actual_offset, written_len)) }; - return match self.append_lock_domain.as_ref() { + let (actual_offset, written_len) = match self.append_lock_domain.as_ref() { Some(domain) => with_inode_append_lock(domain.key(&md), append_write), None => append_write(), - }; + }?; + self.maybe_sync_after_write(file_type, actual_offset, written_len, flags, inode_flags)?; + return Ok(written_len); } let actual_offset = offset; let actual_len = self.limit_write_len_by_fsize(file_type, actual_offset, len)?; - self.write_at_and_finalize( + let written_len = self.write_at_and_finalize( actual_offset, actual_len, buf, WriteConfig { update_offset, offset_update: OffsetUpdate::Add, - flags, - inode_flags, }, - ) + )?; + self.maybe_sync_after_write(file_type, actual_offset, written_len, flags, inode_flags)?; + Ok(written_len) } pub fn do_write_user( @@ -1553,31 +1569,32 @@ impl File { WriteConfig { update_offset, offset_update: OffsetUpdate::StoreEnd, - flags, - inode_flags, }, ) + .map(|written_len| (actual_offset, written_len)) }; - return match self.append_lock_domain.as_ref() { + let (actual_offset, written_len) = match self.append_lock_domain.as_ref() { Some(domain) => with_inode_append_lock(domain.key(&md), append_write), None => append_write(), - }; + }?; + self.maybe_sync_after_write(file_type, actual_offset, written_len, flags, inode_flags)?; + return Ok(written_len); } let actual_offset = offset; let actual_len = self.limit_write_len_by_fsize(file_type, actual_offset, len)?; - self.write_user_at_and_finalize( + let written_len = self.write_user_at_and_finalize( actual_offset, actual_len, reader, WriteConfig { update_offset, offset_update: OffsetUpdate::Add, - flags, - inode_flags, }, - ) + )?; + self.maybe_sync_after_write(file_type, actual_offset, written_len, flags, inode_flags)?; + Ok(written_len) } /// Write to the file from a userspace buffer. diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index da1ec9214e..28ea062c92 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -587,6 +587,15 @@ pub trait IndexNode: Any + Sync + Send + Debug + CastFromSync { /// protocol flags. fn adjust_file_mode_after_open(&self, _data: &FilePrivateData, _mode: &mut FileMode) {} + /// Whether the VFS write path should apply Linux `generic_write_sync()` + /// semantics after a successful positive-length write. + /// + /// This is intentionally opt-in: many pseudo files are reported as regular + /// files but their Linux write paths do not perform generic write syncing. + fn supports_post_write_sync(&self, _file_type: FileType) -> bool { + false + } + /// @brief 关闭文件 /// /// @return 成功:Ok() diff --git a/kernel/src/filesystem/vfs/mount/mod.rs b/kernel/src/filesystem/vfs/mount/mod.rs index 7cc21bdb92..ad059854c3 100644 --- a/kernel/src/filesystem/vfs/mount/mod.rs +++ b/kernel/src/filesystem/vfs/mount/mod.rs @@ -3611,6 +3611,10 @@ impl IndexNode for MountFSInode { self.dentry.inode.adjust_file_mode_after_open(data, mode) } + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + self.dentry.inode.supports_post_write_sync(file_type) + } + fn mmap(&self, start: usize, len: usize, offset: usize) -> Result<(), SystemError> { return self.dentry.inode.mmap(start, len, offset); }