-
-
Notifications
You must be signed in to change notification settings - Fork 194
Expand file tree
/
Copy pathfile.rs
More file actions
2778 lines (2491 loc) · 94.8 KB
/
Copy pathfile.rs
File metadata and controls
2778 lines (2491 loc) · 94.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
use core::{
fmt,
sync::atomic::{AtomicUsize, Ordering},
};
use alloc::{string::String, sync::Arc, vec::Vec};
use log::error;
use system_error::SystemError;
use super::{
append_lock::{with_inode_append_lock, AppendLockKey},
inode_lifecycle::{InodeRetentionGuard, InodeRetentionKind},
mount::{MountExternalGuard, MountFSInode},
utils::should_remove_sgid,
DirectoryEntry, FileSystem, FileType, IndexNode, InodeId, Metadata, SetMetadataMask,
SpecialNodeData,
};
use crate::{arch::ipc::signal::Signal, filesystem::vfs::InodeFlags, process::pid::PidPrivateData};
use crate::{
arch::MMArch,
driver::{
base::{block::SeekFrom, device::DevicePrivateData},
block::loop_device::LoopPrivateData,
tty::tty_device::TtyFilePrivateData,
},
filesystem::{
devfs::{devfs_lookup_device_by_devnum, LockedDevFSInode},
epoll::{
event_poll::{EPollPrivateData, EventPoll, LockedEPItemLinkedList},
EPollItem,
},
ext4::inode::LockedExt4Inode,
fat::fs::LockedFATInode,
fuse::private_data::FuseFilePrivateData,
kernfs::callback::KernFilePrivateData,
overlayfs::OverlayFilePrivateData,
page_cache::PageCache,
procfs::ProcfsFilePrivateData,
ramfs::LockedRamFSInode,
tmpfs::LockedTmpfsInode,
vfs::FilldirContext,
},
ipc::{kill::send_signal_to_pid, pipe::PipeFsPrivateData},
libs::{casting::DowncastArc, errseq::ErrSeqValue, mutex::Mutex, rwsem::RwSem},
mm::{
page::PageFlags,
readahead::{page_cache_async_readahead, page_cache_sync_readahead, FileReadaheadState},
MemoryManagementArch,
},
process::{
cred::{CAPFlags, Cred},
namespace::{
cgroup_namespace::CgroupNamespace, ipc_namespace::IpcNamespace, mnt::MntNamespace,
net_namespace::NetNamespace, pid_namespace::PidNamespace,
user_namespace::UserNamespace, uts_namespace::UtsNamespace,
},
resource::RLimitID,
ProcessControlBlock, ProcessManager, RawPid,
},
syscall::user_access::UserBufferReader,
};
use crate::filesystem::vfs::InodeMode;
const MAX_LFS_FILESIZE: i64 = i64::MAX;
static NEXT_OPEN_FILE_ID: AtomicUsize = AtomicUsize::new(1);
static NEXT_LOCK_OWNER_ID: AtomicUsize = AtomicUsize::new(1);
#[inline]
fn alloc_open_file_id() -> usize {
NEXT_OPEN_FILE_ID.fetch_add(1, Ordering::Relaxed)
}
#[inline]
fn alloc_lock_owner_id() -> usize {
NEXT_LOCK_OWNER_ID.fetch_add(1, Ordering::Relaxed)
}
fn canonical_inode_for_file_lock(mut inode: Arc<dyn IndexNode>) -> Arc<dyn IndexNode> {
loop {
match inode.clone().downcast_arc::<MountFSInode>() {
Some(mnt_inode) => inode = mnt_inode.underlying_inode(),
None => return inode,
}
}
}
/// Append-lock domain and the owner that keeps its allocation identity alive.
///
/// The inode part of the key is deliberately derived from current metadata at
/// write time. A stacked filesystem may change backing identity during open
/// (for example, overlayfs copy-up), so caching the complete key here can split
/// concurrent appenders across two locks.
#[derive(Clone, Debug)]
struct AppendLockDomain {
fs_instance: usize,
fs_owner: Arc<dyn FileSystem>,
}
impl AppendLockDomain {
fn new(fs_owner: Arc<dyn FileSystem>) -> Self {
let fs_instance = Arc::as_ptr(&fs_owner) as *const () as usize;
Self {
fs_instance,
fs_owner,
}
}
fn key(&self, metadata: &Metadata) -> AppendLockKey {
debug_assert_eq!(
self.fs_instance,
Arc::as_ptr(&self.fs_owner) as *const () as usize
);
AppendLockKey::new(self.fs_instance, metadata.dev_id, metadata.inode_id)
}
}
fn is_plain_special_inode(inode: &Arc<dyn IndexNode>) -> bool {
inode
.as_any_ref()
.downcast_ref::<LockedDevFSInode>()
.is_some()
|| inode
.as_any_ref()
.downcast_ref::<LockedTmpfsInode>()
.is_some()
|| inode
.as_any_ref()
.downcast_ref::<LockedRamFSInode>()
.is_some()
|| inode
.as_any_ref()
.downcast_ref::<LockedExt4Inode>()
.is_some()
|| inode
.as_any_ref()
.downcast_ref::<LockedFATInode>()
.is_some()
}
fn resolve_device_special_inode(
inode: Arc<dyn IndexNode>,
file_type: FileType,
) -> Result<Arc<dyn IndexNode>, SystemError> {
if !matches!(file_type, FileType::CharDevice | FileType::BlockDevice) {
return Ok(inode);
}
let raw_dev = inode.metadata()?.raw_dev;
if raw_dev == Default::default() {
return Ok(inode);
}
if is_plain_special_inode(&inode) {
if let Some(device_inode) = devfs_lookup_device_by_devnum(file_type, raw_dev) {
return Ok(device_inode);
}
return Err(SystemError::ENXIO);
}
Ok(inode)
}
#[derive(Clone, Copy, Debug)]
enum OffsetUpdate {
/// Sequential write path: advance file offset by written length.
Add,
/// Append path: set file offset to end-of-write (actual_offset + written_len).
StoreEnd,
}
#[derive(Clone, Debug)]
pub struct FileOwnerSnapshot {
pub pcb: Option<Arc<ProcessControlBlock>>,
pub signum: i32,
}
#[derive(Debug)]
struct FileOwner {
pcb: Option<Arc<ProcessControlBlock>>,
signum: i32,
}
impl FileOwner {
fn new() -> Self {
Self {
pcb: None,
signum: 0,
}
}
fn snapshot(&self) -> FileOwnerSnapshot {
FileOwnerSnapshot {
pcb: self.pcb.clone(),
signum: self.signum,
}
}
}
/// 写操作的配置参数
#[derive(Clone, Copy)]
struct WriteConfig {
/// 是否更新文件偏移量
update_offset: bool,
/// 偏移量更新方式
offset_update: OffsetUpdate,
}
/// Namespace fd backing data, typically created from /proc/thread-self/ns/* files.
#[derive(Clone)]
#[allow(dead_code)]
pub enum NamespaceFilePrivateData {
Ipc(Arc<IpcNamespace>),
Uts(Arc<UtsNamespace>),
Mnt(Arc<MntNamespace>),
Net(Arc<NetNamespace>),
/// Current thread PID namespace.
Pid(Arc<PidNamespace>),
/// PID namespace for children.
PidForChildren(Arc<PidNamespace>),
User(Arc<UserNamespace>),
/// Cgroup namespace.
Cgroup(Arc<CgroupNamespace>),
// Time namespace is not implemented yet.
}
impl fmt::Debug for NamespaceFilePrivateData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NamespaceFilePrivateData::Ipc(_) => f.write_str("NamespaceFilePrivateData::Ipc(..)"),
NamespaceFilePrivateData::Uts(_) => f.write_str("NamespaceFilePrivateData::Uts(..)"),
NamespaceFilePrivateData::Mnt(_) => f.write_str("NamespaceFilePrivateData::Mnt(..)"),
NamespaceFilePrivateData::Net(_) => f.write_str("NamespaceFilePrivateData::Net(..)"),
NamespaceFilePrivateData::Pid(_) => f.write_str("NamespaceFilePrivateData::Pid(..)"),
NamespaceFilePrivateData::PidForChildren(_) => {
f.write_str("NamespaceFilePrivateData::PidForChildren(..)")
}
NamespaceFilePrivateData::User(_) => f.write_str("NamespaceFilePrivateData::User(..)"),
NamespaceFilePrivateData::Cgroup(_) => {
f.write_str("NamespaceFilePrivateData::Cgroup(..)")
}
}
}
}
/// 文件私有信息的枚举类型
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum FilePrivateData {
/// 管道文件私有信息
Pipefs(PipeFsPrivateData),
/// procfs文件私有信息
Procfs(ProcfsFilePrivateData),
/// 设备文件的私有信息
DevFS(DevicePrivateData),
/// tty设备文件的私有信息
Tty(TtyFilePrivateData),
/// epoll私有信息
EPoll(EPollPrivateData),
/// pid私有信息
Pid(PidPrivateData),
//loop私有信息
Loop(LoopPrivateData),
/// namespace fd 私有信息(/proc/thread-self/ns/* 打开后得到)
Namespace(NamespaceFilePrivateData),
/// Socket file created by socket syscalls (not by VFS open(2)).
SocketCreate,
/// FUSE file private data.
Fuse(FuseFilePrivateData),
/// OverlayFS per-open backing file private data.
Overlayfs(OverlayFilePrivateData),
/// kernfs/debugfs per-open callback state.
Kernfs(Option<KernFilePrivateData>),
/// 不需要文件私有信息
Unused,
}
impl Default for FilePrivateData {
fn default() -> Self {
return Self::Unused;
}
}
/// Owns filesystem private data for an inode that has already been opened.
///
/// The guard stays armed until a [`File`] takes ownership. This closes the
/// filesystem handle on every error path between an atomic create/open and
/// construction of the open file description.
pub struct PreopenedFile {
inode: Arc<dyn IndexNode>,
private_data: Option<FilePrivateData>,
}
impl PreopenedFile {
pub fn new(inode: Arc<dyn IndexNode>, private_data: FilePrivateData) -> Self {
Self {
inode,
private_data: Some(private_data),
}
}
pub fn inode(&self) -> Arc<dyn IndexNode> {
self.inode.clone()
}
pub fn replace_inode(&mut self, inode: Arc<dyn IndexNode>) {
self.inode = inode;
}
fn take_private_data(&mut self) -> FilePrivateData {
self.private_data
.take()
.expect("preopened file private data already consumed")
}
}
impl Drop for PreopenedFile {
fn drop(&mut self) {
if let Some(data) = self.private_data.take() {
let _ = self.inode.close(Mutex::new(data).lock());
}
}
}
impl FilePrivateData {
pub fn update_flags(&mut self, flags: FileFlags) -> Result<(), SystemError> {
match self {
FilePrivateData::Pipefs(pdata) => {
pdata.set_flags(flags);
}
FilePrivateData::Tty(pdata) => {
pdata.flags = flags;
}
FilePrivateData::Fuse(pdata) => {
pdata.set_flags(flags);
}
FilePrivateData::Overlayfs(pdata) => {
pdata.set_flags(flags)?;
}
_ => {}
}
Ok(())
}
}
bitflags! {
/// @brief 文件打开模式
/// 其中,低2bit组合而成的数字的值,用于表示访问权限。其他的bit,才支持通过按位或的方式来表示参数
///
/// 与Linux 5.19.10的uapi/asm-generic/fcntl.h相同
/// https://code.dragonos.org.cn/xref/linux-5.19.10/tools/include/uapi/asm-generic/fcntl.h#19
#[allow(clippy::bad_bit_mask)]
pub struct FileFlags: u32{
/* File access modes for `open' and `fcntl'. */
/// Open Read-only
const O_RDONLY = 0o0;
/// Open Write-only
const O_WRONLY = 0o1;
/// Open read/write
const O_RDWR = 0o2;
/// Mask for file access modes
const O_ACCMODE = 0o00000003;
/* Bits OR'd into the second argument to open. */
/// Create file if it does not exist
const O_CREAT = 0o00000100;
/// Fail if file already exists
const O_EXCL = 0o00000200;
/// Do not assign controlling terminal
const O_NOCTTY = 0o00000400;
/// 文件存在且是普通文件,并以O_RDWR或O_WRONLY打开,则它会被清空
const O_TRUNC = 0o00001000;
/// 文件指针会被移动到文件末尾
const O_APPEND = 0o00002000;
/// 非阻塞式IO模式
const O_NONBLOCK = 0o00004000;
/// 每次write都等待物理I/O完成,但是如果写操作不影响读取刚写入的数据,则不等待文件属性更新
const O_DSYNC = 0o00010000;
/// fcntl, for BSD compatibility
const FASYNC = 0o00020000;
/* direct disk access hint */
const O_DIRECT = 0o00040000;
const O_LARGEFILE = 0o00100000;
/// 打开的必须是一个目录
const O_DIRECTORY = 0o00200000;
/// Do not follow symbolic links
const O_NOFOLLOW = 0o00400000;
const O_NOATIME = 0o01000000;
/// set close_on_exec
const O_CLOEXEC = 0o02000000;
/// 每次write都等到物理I/O完成,包括write引起的文件属性的更新
const __O_SYNC = 0o04000000;
const O_SYNC = Self::__O_SYNC.bits | Self::O_DSYNC.bits;
const O_PATH = 0o10000000;
const O_PATH_FLAGS = Self::O_DIRECTORY.bits|Self::O_NOFOLLOW.bits|Self::O_CLOEXEC.bits|Self::O_PATH.bits;
}
}
impl FileFlags {
/// @brief 获取文件的访问模式 (O_RDONLY/O_WRONLY/O_RDWR)
///
/// 这是正确提取访问模式的方法,因为O_RDONLY=0不能用contains()检查
#[inline]
pub fn access_flags(&self) -> FileFlags {
*self & Self::O_ACCMODE
}
/// @brief 检查是否是只读模式
#[inline]
pub fn is_read_only(&self) -> bool {
self.access_flags() == Self::O_RDONLY
}
/// @brief 检查是否是只写模式
#[inline]
pub fn is_write_only(&self) -> bool {
self.access_flags() == Self::O_WRONLY
}
/// @brief 检查是否是读写模式
#[inline]
pub fn is_rdwr(&self) -> bool {
self.access_flags() == Self::O_RDWR
}
/// 检查是否设置了 FASYNC 标志
#[inline]
pub fn fasync(&self) -> bool {
self.contains(FileFlags::FASYNC)
}
}
bitflags! {
/// 文件访问模式标志 (对应 Linux 的 file.f_mode)
///
/// 这些标志是内核专用的,用户空间不可见。
///
/// 参考: https://elixir.bootlin.com/linux/v6.12.6/source/include/linux/fs.h#L119
pub struct FileMode: u32 {
// ============================================================
// 基本访问模式 (从 f_flags 转换而来)
// ============================================================
/// 文件以读模式打开
const FMODE_READ = 0x1;
/// 文件以写模式打开
const FMODE_WRITE = 0x2;
/// 文件支持 lseek 操作
const FMODE_LSEEK = 0x4;
/// 文件支持 pread 操作
const FMODE_PREAD = 0x8;
/// 文件支持 pwrite 操作
const FMODE_PWRITE = 0x10;
/// 文件通过 sys_execve / sys_uselib 打开用于执行
const FMODE_EXEC = 0x20;
// ============================================================
// 目录哈希模式 (用于 llseek 偏移)
// ============================================================
/// 使用 32 位哈希作为 llseek() 偏移 (用于目录)
const FMODE_32BITHASH = 0x200;
/// 使用 64 位哈希作为 llseek() 偏移 (用于目录)
const FMODE_64BITHASH = 0x400;
// ============================================================
// 时间戳和访问模式控制
// ============================================================
/// 不更新 ctime 和 mtime
/// 目前是 XFS open_by_handle ioctl 的特殊 hack,
/// 未来可能会升级为 open(2) 支持的 O_CMTIME 标志
const FMODE_NOCMTIME = 0x800;
/// 期望随机访问模式 (由 fadvise 设置)
const FMODE_RANDOM = 0x1000;
/// 文件是巨大的 (如 /dev/mem): 将 loff_t 视为无符号数
const FMODE_UNSIGNED_OFFSET = 0x2000;
/// 文件以 O_PATH 方式打开 (几乎无操作权限)
const FMODE_PATH = 0x4000;
/// 需要原子性的 f_pos 访问 (常规文件/目录)
const FMODE_ATOMIC_POS = 0x8000;
// ============================================================
// 写权限和能力标志
// ============================================================
/// 持有对底层文件系统的写访问权限
const FMODE_WRITER = 0x10000;
/// 文件有 read 或 read_iter 方法
const FMODE_CAN_READ = 0x20000;
/// 文件有 write 或 write_iter 方法
const FMODE_CAN_WRITE = 0x40000;
// ============================================================
// 文件状态标志
// ============================================================
/// 文件已成功打开
const FMODE_OPENED = 0x80000;
/// 文件是新创建的 (O_CREAT 成功时设置)
const FMODE_CREATED = 0x100000;
/// 文件是流式的 (不可 seek)
const FMODE_STREAM = 0x200000;
/// 文件支持 O_DIRECT 直接 I/O
const FMODE_CAN_ODIRECT = 0x400000;
/// 文件不应被重用
const FMODE_NOREUSE = 0x800000;
// ============================================================
// 并发和异步 I/O 标志
// ============================================================
/// 文件支持来自多线程的非独占 O_DIRECT 写入
const FMODE_DIO_PARALLEL_WRITE = 0x1000000;
/// 文件嵌入在 backing_file 对象中
const FMODE_BACKING = 0x2000000;
/// 文件由 fanotify 打开,不应生成 fanotify 事件
const FMODE_NONOTIFY = 0x4000000;
/// 文件能够在 I/O 将阻塞时返回 -EAGAIN (支持非阻塞 I/O,如 io_uring)
const FMODE_NOWAIT = 0x8000000;
// ============================================================
// 挂载和资源管理标志
// ============================================================
/// 文件代表需要卸载的挂载点
const FMODE_NEED_UNMOUNT = 0x10000000;
/// 文件不计入 nr_files 计数
const FMODE_NOACCOUNT = 0x20000000;
/// 文件支持异步缓冲读取
const FMODE_BUF_RASYNC = 0x40000000;
/// 文件支持异步 nowait 缓冲写入
const FMODE_BUF_WASYNC = 0x80000000;
}
}
impl FileMode {
/// @brief 从FileFlags转换为FileMode (实现OPEN_FMODE)
///
/// - O_RDONLY (0) -> FMODE_READ (1)
/// - O_WRONLY (1) -> FMODE_WRITE (2)
/// - O_RDWR (2) -> FMODE_READ | FMODE_WRITE (3)
/// - 以及对于抑制fsnotify/fanotify机制触发通知的标志FMODE_NONOTIFY
pub fn open_fmode(flags: FileFlags) -> Self {
let fmode = flags.bits() & FileMode::FMODE_NONOTIFY.bits()
| (flags.access_flags().bits + 1) & FileFlags::O_ACCMODE.bits();
// 初始只设置访问模式,其他能力在后续设置
FileMode::from_bits_truncate(fmode)
}
/// @brief 检查是否可读
#[inline]
pub fn is_readable(&self) -> bool {
self.contains(Self::FMODE_READ)
}
/// @brief 检查是否可写
#[inline]
pub fn is_writeable(&self) -> bool {
self.contains(Self::FMODE_WRITE)
}
/// @brief 检查是否真的能读 (有读方法)
#[inline]
pub fn can_read(&self) -> bool {
self.is_readable() && self.contains(Self::FMODE_CAN_READ)
}
/// @brief 检查是否真的能写 (有写方法)
#[inline]
pub fn can_write(&self) -> bool {
self.is_writeable() && self.contains(Self::FMODE_CAN_WRITE)
}
}
/// @brief 抽象文件结构体
#[derive(Debug)]
pub struct File {
/// 唯一 open file description id,用于 flock owner 标识。
open_file_id: usize,
inode: Arc<dyn IndexNode>,
/// Filesystem selected when this open file description was created.
///
/// Mount wrappers retain the mount selected by path lookup. Cache that
/// filesystem on the open file description so fd-based I/O and mmap fault
/// paths use the same identity directly without cloning an Arc while MM
/// locks are held.
io_fs: Option<Arc<dyn FileSystem>>,
/// 对于文件,表示字节偏移量;对于文件夹,表示当前操作的子目录项偏移量
offset: AtomicUsize,
/// 文件的打开模式
flags: RwSem<FileFlags>,
/// 文件的访问模式
mode: RwSem<FileMode>,
/// 文件类型
file_type: FileType,
/// Per-open immutable directory snapshot and its next record index.
readdir_state: Mutex<ReaddirState>,
pub private_data: Mutex<FilePrivateData>,
/// 文件的凭证
cred: Arc<Cred>,
/// Owner state for asynchronous I/O notifications.
owner: Mutex<FileOwner>,
/// Stable key for POSIX record locks. Cached at open time to avoid metadata fetch
/// in close/drop_fd path (which can deadlock with user-space FUSE daemon).
posix_lock_key: (usize, InodeId),
/// Internal filesystem domain for serializing atomic append operations.
/// Only regular, non-O_PATH files retain one; the inode key is resolved
/// from current metadata when an append starts.
append_lock_domain: Option<AppendLockDomain>,
/// 预读状态
ra_state: Mutex<FileReadaheadState>,
/// 当前 open file description 已观测到的 page cache 写回错误序列。
wb_error_seq: Mutex<ErrSeqValue>,
/// 当前 open file description 已观测到的 superblock 写回错误序列。
sb_error_seq: Mutex<ErrSeqValue>,
/// epoll items that reference this open file description.
///
/// Linux removes these from their owning epoll instances during `__fput()`
/// via `eventpoll_release(file)`. DragonOS keeps the same lifetime edge
/// here so fd numbers can be safely reused after close.
epitems: Arc<LockedEPItemLinkedList>,
/// One semantic inode pin per open file description. Duplicated file
/// descriptors and VMAs share this `File`, while `O_PATH` still owns it.
/// Declared last so the explicit `Drop` body runs `close()` before release.
_inode_retention: InodeRetentionGuard,
/// Pins the mount used to resolve this pathname independently from the
/// operation inode (which device/FIFO resolution may replace).
_mount_guard: Option<MountExternalGuard>,
}
#[derive(Debug, Clone)]
enum ReaddirSnapshot {
Typed(Arc<[DirectoryEntry]>),
Names(Arc<[String]>),
}
impl ReaddirSnapshot {
fn len(&self) -> usize {
match self {
Self::Typed(entries) => entries.len(),
Self::Names(names) => names.len(),
}
}
fn cookie_after(&self, index: usize) -> Option<u64> {
match self {
Self::Typed(entries) => entries.get(index).map(|entry| entry.next_cookie),
Self::Names(names) => (index < names.len()).then(|| (index + 1) as u64),
}
}
fn index_after_cookie(&self, cookie: u64) -> usize {
match self {
Self::Typed(entries) => entries
.iter()
.position(|entry| entry.next_cookie == cookie)
.map_or(entries.len(), |index| index + 1),
Self::Names(names) => usize::try_from(cookie)
.ok()
.filter(|index| *index <= names.len())
.unwrap_or(names.len()),
}
}
}
#[derive(Debug, Clone, Default)]
struct ReaddirState {
snapshot: Option<ReaddirSnapshot>,
next_index: usize,
}
impl ReaddirState {
fn resume_after_cookie(&mut self, cookie: u64) {
let Some(snapshot) = self.snapshot.as_ref() else {
self.next_index = 0;
return;
};
self.next_index = snapshot.index_after_cookie(cookie);
}
}
fn checked_dirent_cookie(cookie: u64) -> Result<usize, SystemError> {
let cookie = i64::try_from(cookie).map_err(|_| SystemError::EOVERFLOW)?;
usize::try_from(cookie).map_err(|_| SystemError::EOVERFLOW)
}
#[cfg(test)]
mod readdir_tests {
use super::checked_dirent_cookie;
use system_error::SystemError;
#[test]
fn dirent_cookie_must_fit_signed_offset() {
assert_eq!(checked_dirent_cookie(1), Ok(1));
assert_eq!(
checked_dirent_cookie(i64::MAX as u64 + 1),
Err(SystemError::EOVERFLOW)
);
}
}
impl File {
pub fn resolved_path(&self) -> Result<super::utils::ResolvedPath, SystemError> {
let mount_guard = self
._mount_guard
.as_ref()
.map(MountExternalGuard::derive)
.transpose()?;
super::utils::ResolvedPath::from_existing_mount(self.inode.clone(), mount_guard)
}
#[inline]
pub fn cred(&self) -> Arc<Cred> {
self.cred.clone()
}
pub fn check_and_advance_wb_error(
&self,
page_cache: &Arc<PageCache>,
) -> Result<(), SystemError> {
let since = *self.wb_error_seq.lock();
if page_cache.check_writeback_error_since(since).is_none() {
return Ok(());
}
let mut since = self.wb_error_seq.lock();
match page_cache.check_and_advance_writeback_error(&mut since) {
Some(error) => Err(error),
None => Ok(()),
}
}
pub fn check_and_advance_sb_wb_error(
&self,
mount_fs: &Arc<super::mount::MountFS>,
) -> Result<(), SystemError> {
let mut since = self.sb_error_seq.lock();
match mount_fs.check_and_advance_wb_error(&mut since) {
Some(error) => Err(error),
None => Ok(()),
}
}
pub fn sync_range_and_check_wb_error(
&self,
start: usize,
end: usize,
datasync: bool,
) -> Result<(), SystemError> {
let inode = self.inode();
let sync_result = inode.sync_file_range(start, end, datasync, self.private_data.lock());
let wb_result = match inode.page_cache() {
Some(page_cache) => self.check_and_advance_wb_error(&page_cache),
None => Ok(()),
};
match sync_result {
Err(e) => Err(e),
Ok(()) => wb_result,
}
}
pub fn flush_for_close(&self, lock_owner: u64) -> Result<(), SystemError> {
if self.mode().contains(FileMode::FMODE_PATH) {
return Ok(());
}
let inode = self.inode();
inode.flush_file(self.private_data.lock(), lock_owner)
}
fn maybe_kill_suid_sgid_after_write(&self) -> Result<(), SystemError> {
// 仅对普通文件生效。
if self.file_type != FileType::File {
return Ok(());
}
// Linux 语义:若调用者具备 CAP_FSETID,则写/截断不会清除 suid/sgid。
let cred = ProcessManager::current_pcb().cred();
if cred.has_capability(CAPFlags::CAP_FSETID) {
return Ok(());
}
let mut md = self.inode.metadata()?;
if !md.mode.intersects(InodeMode::S_ISUID | InodeMode::S_ISGID) {
return Ok(());
}
// suid always must be killed on write/truncate when no CAP_FSETID.
md.mode.remove(InodeMode::S_ISUID);
// setgid 清理规则与 Linux fs/attr.c:setattr_should_drop_sgid 对齐:
// - 若 S_IXGRP 置位:无条件清除(这是"真正的"SGID 可执行文件)
// - 否则(mandatory locking 语义):仅当调用者不在文件所属组时清除
if should_remove_sgid(md.mode, md.gid, &cred) {
md.mode.remove(InodeMode::S_ISGID);
}
self.inode.set_metadata_masked(
&md,
SetMetadataMask::MODE | SetMetadataMask::WRITE_SIDE_EFFECT,
)?;
Ok(())
}
#[inline(never)]
fn limit_write_len_by_fsize(
&self,
file_type: FileType,
offset: usize,
len: usize,
) -> Result<usize, SystemError> {
if !matches!(file_type, FileType::File) {
return Ok(len);
}
let current_pcb = ProcessManager::current_pcb();
let fsize_limit = current_pcb.get_rlimit(RLimitID::Fsize);
if fsize_limit.rlim_cur == u64::MAX {
return Ok(len);
}
let limit = fsize_limit.rlim_cur as usize;
if offset >= limit {
let _ = send_signal_to_pid(
current_pcb.raw_pid(),
crate::arch::ipc::signal::Signal::SIGXFSZ,
);
return Err(SystemError::EFBIG);
}
Ok(len.min(limit.saturating_sub(offset)))
}
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 || !self.inode.supports_post_write_sync(file_type) {
return Ok(());
}
// O_SYNC 包含 O_DSYNC 位,所以只需检查 O_DSYNC 即可判断是否需要数据同步
let need_data_sync = flags.contains(FileFlags::O_DSYNC);
// 检查是否需要元数据同步(O_SYNC = __O_SYNC | O_DSYNC)
let need_metadata_sync = flags.contains(FileFlags::__O_SYNC);
// inode 级别的 S_SYNC 标志
let inode_sync = inode_flags.contains(InodeFlags::S_SYNC);
if need_data_sync || inode_sync {
let end = start.saturating_add(written_len).saturating_sub(1);
if need_metadata_sync {
// O_SYNC: 完整同步(数据 + 元数据)
self.inode
.sync_file_range(start, end, false, self.private_data.lock())?;
} else {
// O_DSYNC 或 S_SYNC: datasync;完整同步只由 O_SYNC 决定。
self.inode
.sync_file_range(start, end, true, self.private_data.lock())?;
}
}
Ok(())
}
#[inline(never)]
fn write_at_and_finalize(
&self,
actual_offset: usize,
actual_len: usize,
buf: &[u8],
config: WriteConfig,
) -> Result<usize, SystemError> {
let written_len =
self.inode
.write_at(actual_offset, actual_len, buf, self.private_data.lock())?;
self.finalize_write(actual_offset, written_len, config)
}
fn write_user_at_and_finalize(
&self,
actual_offset: usize,
actual_len: usize,
reader: &UserBufferReader<'_>,
config: WriteConfig,
) -> Result<usize, SystemError> {
let written_len = match self.inode.write_user_at(
actual_offset,
actual_len,
reader,
self.private_data.lock(),
)? {
Some(written_len) => written_len,
None => {
let mut buf = Vec::new();
buf.try_reserve(actual_len)
.map_err(|_| SystemError::ENOMEM)?;
buf.resize(actual_len, 0);
reader.copy_from_user(&mut buf, 0)?;
self.inode
.write_at(actual_offset, actual_len, &buf, self.private_data.lock())?
}
};
self.finalize_write(actual_offset, written_len, config)
}
fn finalize_write(
&self,
actual_offset: usize,
written_len: usize,
config: WriteConfig,
) -> Result<usize, SystemError> {
if written_len > 0 {
self.maybe_kill_suid_sgid_after_write()?;
}
if config.update_offset {
match config.offset_update {
OffsetUpdate::StoreEnd => {
self.offset.store(
actual_offset + written_len,
core::sync::atomic::Ordering::SeqCst,
);
}
OffsetUpdate::Add => {
self.offset
.fetch_add(written_len, core::sync::atomic::Ordering::SeqCst);
}
}
}
Ok(written_len)
}
/// @brief 创建一个新的文件对象
///
/// @param inode 文件对象对应的inode
/// @param flags 文件的打开模式
pub fn new(inode: Arc<dyn IndexNode>, flags: FileFlags) -> Result<Self, SystemError> {
Self::new_with_private_data(inode, flags, FilePrivateData::default())
}
/// Create a new file object with an explicit initial FilePrivateData.
///
/// This is primarily used for objects that are not created via VFS open(2)
/// semantics (e.g. sockets created by socket syscalls).
pub fn new_with_private_data(
inode: Arc<dyn IndexNode>,
flags: FileFlags,
private_data_init: FilePrivateData,
) -> Result<Self, SystemError> {
let mount_guard = inode
.clone()
.downcast_arc::<MountFSInode>()
.map(|inode| inode.mount_fs().try_pin_external())
.transpose()?;
Self::new_with_private_data_and_mount_guard(
inode,
flags,
private_data_init,
mount_guard,
None,
)
}
/// Construct a pathname-backed file by consuming the mount pin acquired
/// during path resolution. This closes the lookup-to-open umount window.
pub fn new_with_mount_guard(
inode: Arc<dyn IndexNode>,
flags: FileFlags,
mount_guard: Option<MountExternalGuard>,
operation_guard: InodeRetentionGuard,
) -> Result<Self, SystemError> {
let file = Self::new_with_private_data_and_mount_guard(
inode,
flags,
FilePrivateData::default(),
mount_guard,
None,
);
drop(operation_guard);
file
}
/// Construct a pathname-backed file from an already-opened inode.
pub fn new_preopened_with_mount_guard(
preopened: PreopenedFile,
flags: FileFlags,
mount_guard: Option<MountExternalGuard>,
operation_guard: InodeRetentionGuard,
) -> Result<Self, SystemError> {
let inode = preopened.inode();
let file = Self::new_with_private_data_and_mount_guard(
inode,
flags,
FilePrivateData::default(),
mount_guard,
Some(preopened),
);
drop(operation_guard);
file
}
fn new_with_private_data_and_mount_guard(
inode: Arc<dyn IndexNode>,