os/vfs/
fd_table.rs

1//! 文件描述符表
2//!
3//! 该模块实现了进程级的文件描述符管理,提供 POSIX 兼容的文件描述符操作。
4//!
5//! # 核心组件
6//!
7//! - [`FDTable`] - 文件描述符表,每个进程维护一个实例
8//! - [`FdFlags`] - 文件描述符标志(如 FD_CLOEXEC)
9//!
10//! # 设计理念
11//!
12//! ## 文件描述符的本质
13//!
14//! 文件描述符(FD)是进程级的整数索引,指向打开的文件对象:
15//!
16//! ```text
17//! FDTable
18//! ┌────┬──────────────────────┐
19//! │ 0  │ Arc<StdinFile>       │ ← stdin
20//! │ 1  │ Arc<StdoutFile>      │ ← stdout
21//! │ 2  │ Arc<StderrFile>      │ ← stderr
22//! │ 3  │ Arc<RegFile>         │
23//! │ 4  │ Arc<PipeFile>        │
24//! │ 5  │ None                 │
25//! └────┴──────────────────────┘
26//! ```
27//!
28//! ## POSIX 语义
29//!
30//! ### 最小可用 FD
31//!
32//! `alloc()` 总是返回最小的可用文件描述符:
33//!
34//! ```rust
35//! let fd1 = fd_table.alloc(file1)?;  // 返回 3
36//! let fd2 = fd_table.alloc(file2)?;  // 返回 4
37//! fd_table.close(3)?;
38//! let fd3 = fd_table.alloc(file3)?;  // 返回 3 (重用)
39//! ```
40//!
41//! ### dup 语义
42//!
43//! `dup` 系列操作共享 File 对象(包括 offset):
44//!
45//! ```text
46//! fd[3] ──┐
47//!         ├──> Arc<RegFile> { offset: AtomicUsize }
48//! fd[4] ──┘
49//!
50//! // fd[3] 和 fd[4] 共享 offset,一个 read() 会影响另一个
51//! ```
52//!
53//! ### FD 标志 vs 文件状态标志
54//!
55//! - **FD 标志** (FdFlags): 文件描述符级别,dup 时不共享
56//!   - `FD_CLOEXEC`: exec 时关闭
57//! - **文件状态标志** (OpenFlags): File 对象级别,dup 时共享
58//!   - `O_RDONLY`/`O_WRONLY`/`O_RDWR`: 访问模式
59//!   - `O_APPEND`: 追加模式
60//!   - `O_NONBLOCK`: 非阻塞模式
61//!
62//! ## fork 和 exec
63//!
64//! ### fork 时
65//!
66//! 父子进程共享整个 FDTable:
67//!
68//! ```text
69//! 父进程 ──┐
70//!          ├──> Arc<FDTable>
71//! 子进程 ──┘
72//! ```
73//!
74//! ### exec 时
75//!
76//! 自动关闭带 `FD_CLOEXEC` 标志的文件:
77//!
78//! ```rust
79//! fd_table.close_exec();  // exec 前调用
80//! ```
81//!
82//! # 并发安全
83//!
84//! FDTable 内部使用 `SpinLock` 保护:
85//! - `files`: `Vec<Option<Arc<dyn File>>>`
86//! - `fd_flags`: `Vec<FdFlags>`
87//!
88//! 多线程访问 FDTable 时会竞争锁,但 File 对象本身(如 RegFile 的 offset)
89//! 使用原子操作,无需额外锁定。
90//!
91//! # 使用示例
92//!
93//! ## 基本操作
94//!
95//! ```rust
96//! use vfs::{FDTable, RegFile};
97//!
98//! let fd_table = FDTable::new();
99//!
100//! // 分配文件描述符
101//! let file = Arc::new(RegFile::new(dentry, OpenFlags::O_RDONLY));
102//! let fd = fd_table.alloc(file)?;
103//!
104//! // 访问文件
105//! let file = fd_table.get(fd)?;
106//! file.read(&mut buf)?;
107//!
108//! // 关闭文件描述符
109//! fd_table.close(fd)?;
110//! ```
111//!
112//! ## dup 操作
113//!
114//! ```rust
115//! // dup: 复制到最小可用 FD
116//! let new_fd = fd_table.dup(old_fd)?;
117//!
118//! // dup2: 复制到指定 FD
119//! fd_table.dup2(old_fd, new_fd)?;
120//!
121//! // dup3: dup2 + 设置标志
122//! fd_table.dup3(old_fd, new_fd, OpenFlags::O_CLOEXEC)?;
123//! ```
124//!
125//! ## 重定向示例
126//!
127//! ```rust
128//! // 将 stdout 重定向到文件
129//! let file = open_file("/tmp/output.txt", O_WRONLY | O_CREAT)?;
130//! let fd = fd_table.alloc(file)?;
131//! fd_table.dup2(fd, 1)?;  // 1 = stdout
132//! fd_table.close(fd)?;
133//! // 现在所有 println! 都会写到文件
134//! ```
135
136use crate::config::DEFAULT_MAX_FDS;
137use crate::sync::SpinLock;
138use crate::uapi::fcntl::{FdFlags, OpenFlags};
139use crate::vfs::{File, FsError};
140use alloc::sync::Arc;
141use alloc::vec::Vec;
142use core::fmt;
143
144/// 文件描述符表
145///
146/// # 并发安全
147///
148/// 内部使用 `SpinLock` 保护,支持多线程访问。
149pub struct FDTable {
150    /// 文件描述符数组
151    /// None 表示该 FD 未使用
152    files: SpinLock<Vec<Option<Arc<dyn File>>>>,
153
154    /// 文件描述符标志数组(与 files 索引对应)
155    /// 默认值为 FdFlags::empty()
156    fd_flags: SpinLock<Vec<FdFlags>>,
157
158    /// 最大文件描述符数量
159    max_fds: usize,
160}
161
162impl FdFlags {
163    /// 从 OpenFlags 中提取 FD 标志(用于兼容性)
164    ///
165    /// `O_CLOEXEC` 在 open() 时可以指定,但本质上是 FD 标志。
166    pub fn from_open_flags(flags: OpenFlags) -> Self {
167        let mut fd_flags = FdFlags::empty();
168        if flags.contains(OpenFlags::O_CLOEXEC) {
169            fd_flags |= FdFlags::CLOEXEC;
170        }
171        fd_flags
172    }
173}
174
175impl fmt::Debug for FDTable {
176    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177        let files = self.files.lock();
178        let used = files.iter().filter(|slot| slot.is_some()).count();
179        f.debug_struct("FDTable")
180            .field("max_fds", &self.max_fds)
181            .field("slots", &files.len())
182            .field("used", &used)
183            .finish()
184    }
185}
186
187impl FDTable {
188    /// 创建新的文件描述符表
189    pub fn new() -> Self {
190        Self {
191            files: SpinLock::new(Vec::new()),
192            fd_flags: SpinLock::new(Vec::new()),
193            max_fds: DEFAULT_MAX_FDS,
194        }
195    }
196
197    /// 分配一个新的文件描述符(默认无 FD 标志)
198    pub fn alloc(&self, file: Arc<dyn File>) -> Result<usize, FsError> {
199        self.alloc_with_flags(file, FdFlags::empty())
200    }
201
202    /// 分配一个新的文件描述符并指定 FD 标志
203    pub fn alloc_with_flags(&self, file: Arc<dyn File>, flags: FdFlags) -> Result<usize, FsError> {
204        let mut files = self.files.lock();
205        let mut fd_flags = self.fd_flags.lock();
206
207        // 查找最小可用 FD
208        for (fd, slot) in files.iter_mut().enumerate() {
209            if slot.is_none() {
210                *slot = Some(file);
211                fd_flags[fd] = flags;
212                return Ok(fd);
213            }
214        }
215
216        // 如果没有空闲槽位,扩展数组
217        let fd = files.len();
218        if fd >= self.max_fds {
219            return Err(FsError::TooManyOpenFiles);
220        }
221
222        files.push(Some(file));
223        fd_flags.push(flags);
224        Ok(fd)
225    }
226
227    /// 在指定的 FD 位置安装文件(默认无 FD 标志)
228    pub fn install_at(&self, fd: usize, file: Arc<dyn File>) -> Result<(), FsError> {
229        self.install_at_with_flags(fd, file, FdFlags::empty())
230    }
231
232    /// 在指定的 FD 位置安装文件并指定 FD 标志
233    pub fn install_at_with_flags(
234        &self,
235        fd: usize,
236        file: Arc<dyn File>,
237        flags: FdFlags,
238    ) -> Result<(), FsError> {
239        let mut files = self.files.lock();
240        let mut fd_flags = self.fd_flags.lock();
241
242        if fd >= self.max_fds {
243            return Err(FsError::InvalidArgument);
244        }
245
246        // 扩展数组到指定大小
247        while files.len() <= fd {
248            files.push(None);
249            fd_flags.push(FdFlags::empty());
250        }
251
252        // 替换(旧文件会自动通过 Arc 释放)
253        files[fd] = Some(file);
254        fd_flags[fd] = flags;
255        Ok(())
256    }
257
258    /// 获取文件对象
259    pub fn get(&self, fd: usize) -> Result<Arc<dyn File>, FsError> {
260        let files = self.files.lock();
261        files
262            .get(fd)
263            .and_then(|f| f.clone())
264            .ok_or(FsError::BadFileDescriptor)
265    }
266
267    /// 关闭文件描述符
268    pub fn close(&self, fd: usize) -> Result<(), FsError> {
269        let mut files = self.files.lock();
270        let mut fd_flags = self.fd_flags.lock();
271
272        if fd >= files.len() || files[fd].is_none() {
273            return Err(FsError::BadFileDescriptor);
274        }
275
276        files[fd] = None;
277        fd_flags[fd] = FdFlags::empty();
278        Ok(())
279    }
280
281    /// 复制文件描述符
282    ///
283    /// 返回新的 fd,与 old_fd 指向同一个 `Arc<dyn File>` (共享 offset)。
284    pub fn dup(&self, old_fd: usize) -> Result<usize, FsError> {
285        let file = self.get(old_fd)?;
286        self.alloc(file)
287    }
288
289    /// 复制文件描述符,新 fd >= min_fd(F_DUPFD 语义)
290    ///
291    /// 返回新的 fd,与 old_fd 指向同一个 `Arc<dyn File>` (共享 offset)。
292    /// 新分配的 fd 是 >= min_fd 的最小未使用文件描述符。
293    pub fn dup_from(&self, old_fd: usize, min_fd: usize, flags: FdFlags) -> Result<usize, FsError> {
294        let file = self.get(old_fd)?;
295        let mut files = self.files.lock();
296        let mut fd_flags = self.fd_flags.lock();
297
298        // 1. 先确保数组至少有 min_fd 个元素
299        while files.len() < min_fd {
300            files.push(None);
301            fd_flags.push(FdFlags::empty());
302        }
303
304        // 2. 从 min_fd 开始查找最小可用 FD
305        for (fd, slot) in files.iter_mut().enumerate().skip(min_fd) {
306            if slot.is_none() {
307                *slot = Some(file);
308                fd_flags[fd] = flags;
309                return Ok(fd);
310            }
311        }
312
313        // 3. 如果没有空闲槽位,在数组末尾分配新的 fd
314        let fd = files.len();
315        if fd >= self.max_fds {
316            return Err(FsError::TooManyOpenFiles);
317        }
318
319        files.push(Some(file));
320        fd_flags.push(flags);
321        Ok(fd)
322    }
323
324    /// 复制文件描述符到指定位置
325    ///
326    /// 如果 new_fd 已打开,先关闭它。
327    pub fn dup2(&self, old_fd: usize, new_fd: usize) -> Result<usize, FsError> {
328        // 特殊情况:如果两个 FD 相同,直接返回
329        if old_fd == new_fd {
330            // 检查 old_fd 是否有效
331            self.get(old_fd)?;
332            return Ok(new_fd);
333        }
334
335        // 调用 dup3,不设置任何标志
336        self.dup3(old_fd, new_fd, OpenFlags::empty())
337    }
338
339    /// 复制文件描述符到指定位置(dup3 语义)
340    ///
341    /// 如果 new_fd 已打开,先关闭它。
342    /// 与 dup2 不同,dup3 不允许 old_fd == new_fd。
343    ///
344    /// # 参数
345    /// - `flags`: 可以包含 `O_CLOEXEC`,用于设置新 FD 的 CLOEXEC 标志
346    pub fn dup3(&self, old_fd: usize, new_fd: usize, flags: OpenFlags) -> Result<usize, FsError> {
347        // dup3 不允许 old_fd == new_fd
348        if old_fd == new_fd {
349            return Err(FsError::InvalidArgument);
350        }
351
352        let file = self.get(old_fd)?;
353
354        // 如果 new_fd 已打开,先关闭它(忽略错误)
355        let _ = self.close(new_fd);
356
357        // 提取 FD 标志
358        let fd_flags = FdFlags::from_open_flags(flags);
359
360        self.install_at_with_flags(new_fd, file, fd_flags)?;
361        Ok(new_fd)
362    }
363
364    /// 克隆整个文件描述符表(用于 fork)
365    ///
366    /// 所有 `Arc<dyn File>` 引用计数递增,父子进程共享文件对象。
367    /// FD 标志也会被复制。
368    pub fn clone_table(&self) -> Self {
369        let files = self.files.lock().clone();
370        let fd_flags = self.fd_flags.lock().clone();
371        Self {
372            files: SpinLock::new(files),
373            fd_flags: SpinLock::new(fd_flags),
374            max_fds: self.max_fds,
375        }
376    }
377
378    /// 关闭所有带有 CLOEXEC 标志的文件(用于 exec)
379    pub fn close_exec(&self) {
380        let mut files = self.files.lock();
381        let mut fd_flags = self.fd_flags.lock();
382
383        for (slot, flags) in files.iter_mut().zip(fd_flags.iter_mut()) {
384            if flags.contains(FdFlags::CLOEXEC) {
385                *slot = None;
386                *flags = FdFlags::empty();
387            }
388        }
389    }
390
391    /// 获取文件描述符标志 (F_GETFD)
392    pub fn get_fd_flags(&self, fd: usize) -> Result<FdFlags, FsError> {
393        let files = self.files.lock();
394        let fd_flags = self.fd_flags.lock();
395
396        if fd >= files.len() || files[fd].is_none() {
397            return Err(FsError::BadFileDescriptor);
398        }
399
400        Ok(fd_flags[fd])
401    }
402
403    /// 设置文件描述符标志 (F_SETFD)
404    pub fn set_fd_flags(&self, fd: usize, flags: FdFlags) -> Result<(), FsError> {
405        let files = self.files.lock();
406        let mut fd_flags = self.fd_flags.lock();
407
408        if fd >= files.len() || files[fd].is_none() {
409            return Err(FsError::BadFileDescriptor);
410        }
411
412        fd_flags[fd] = flags;
413        Ok(())
414    }
415}