|
@@ -1,7 +1,7 @@
|
|
|
use super::{
|
|
use super::{
|
|
|
- file::{File, InodeFile, TerminalFile},
|
|
|
|
|
|
|
+ file::{File, InodeFile, Pipe},
|
|
|
inode::Mode,
|
|
inode::Mode,
|
|
|
- s_ischr, Spin,
|
|
|
|
|
|
|
+ Spin, TerminalFile,
|
|
|
};
|
|
};
|
|
|
use crate::kernel::{
|
|
use crate::kernel::{
|
|
|
constants::{
|
|
constants::{
|
|
@@ -10,19 +10,13 @@ use crate::kernel::{
|
|
|
syscall::{FromSyscallArg, SyscallRetVal},
|
|
syscall::{FromSyscallArg, SyscallRetVal},
|
|
|
};
|
|
};
|
|
|
use crate::{
|
|
use crate::{
|
|
|
- kernel::{
|
|
|
|
|
- console::get_console,
|
|
|
|
|
- constants::ENXIO,
|
|
|
|
|
- vfs::{dentry::Dentry, file::Pipe, s_isdir, s_isreg},
|
|
|
|
|
- CharDevice,
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ kernel::{console::get_console, constants::ENXIO, vfs::dentry::Dentry, CharDevice},
|
|
|
prelude::*,
|
|
prelude::*,
|
|
|
};
|
|
};
|
|
|
-use alloc::{
|
|
|
|
|
- collections::btree_map::{BTreeMap, Entry},
|
|
|
|
|
- sync::Arc,
|
|
|
|
|
|
|
+use alloc::sync::Arc;
|
|
|
|
|
+use intrusive_collections::{
|
|
|
|
|
+ intrusive_adapter, rbtree::Entry, Bound, KeyAdapter, RBTree, RBTreeAtomicLink,
|
|
|
};
|
|
};
|
|
|
-use core::sync::atomic::Ordering;
|
|
|
|
|
use itertools::{
|
|
use itertools::{
|
|
|
FoldWhile::{Continue, Done},
|
|
FoldWhile::{Continue, Done},
|
|
|
Itertools,
|
|
Itertools,
|
|
@@ -34,14 +28,33 @@ pub struct FD(u32);
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
#[derive(Clone)]
|
|
|
struct OpenFile {
|
|
struct OpenFile {
|
|
|
|
|
+ fd: FD,
|
|
|
flags: FDFlags,
|
|
flags: FDFlags,
|
|
|
- file: Arc<File>,
|
|
|
|
|
|
|
+ file: File,
|
|
|
|
|
+
|
|
|
|
|
+ link: RBTreeAtomicLink,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+intrusive_adapter!(
|
|
|
|
|
+ OpenFileAdapter = Box<OpenFile>: OpenFile { link: RBTreeAtomicLink }
|
|
|
|
|
+);
|
|
|
|
|
+
|
|
|
|
|
+impl<'a> KeyAdapter<'a> for OpenFileAdapter {
|
|
|
|
|
+ type Key = FD;
|
|
|
|
|
+
|
|
|
|
|
+ fn get_key(&self, value: &'a OpenFile) -> Self::Key {
|
|
|
|
|
+ value.fd
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
#[derive(Clone)]
|
|
|
|
|
+struct FDAllocator {
|
|
|
|
|
+ min_avail: FD,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
struct FileArrayInner {
|
|
struct FileArrayInner {
|
|
|
- files: BTreeMap<FD, OpenFile>,
|
|
|
|
|
- fd_min_avail: FD,
|
|
|
|
|
|
|
+ files: RBTree<OpenFileAdapter>,
|
|
|
|
|
+ fd_alloc: FDAllocator,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
pub struct FileArray {
|
|
pub struct FileArray {
|
|
@@ -49,109 +62,202 @@ pub struct FileArray {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl OpenFile {
|
|
impl OpenFile {
|
|
|
|
|
+ fn new(fd: FD, flags: FDFlags, file: File) -> Box<Self> {
|
|
|
|
|
+ Box::new(Self {
|
|
|
|
|
+ fd,
|
|
|
|
|
+ flags,
|
|
|
|
|
+ file,
|
|
|
|
|
+ link: RBTreeAtomicLink::new(),
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
pub fn close_on_exec(&self) -> bool {
|
|
pub fn close_on_exec(&self) -> bool {
|
|
|
self.flags.contains(FDFlags::FD_CLOEXEC)
|
|
self.flags.contains(FDFlags::FD_CLOEXEC)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+impl FDAllocator {
|
|
|
|
|
+ const fn new() -> Self {
|
|
|
|
|
+ Self { min_avail: FD(0) }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn reinit(&mut self) {
|
|
|
|
|
+ self.min_avail = FD(0);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn find_available(&mut self, from: FD, files: &RBTree<OpenFileAdapter>) -> FD {
|
|
|
|
|
+ files
|
|
|
|
|
+ .range(Bound::Included(&from), Bound::Unbounded)
|
|
|
|
|
+ .fold_while(from, |current, OpenFile { fd, .. }| {
|
|
|
|
|
+ if current == *fd {
|
|
|
|
|
+ Continue(FD(current.0 + 1))
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Done(current)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .into_inner()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// Allocate a new file descriptor starting from `from`.
|
|
|
|
|
+ ///
|
|
|
|
|
+ /// Returned file descriptor should be used immediately.
|
|
|
|
|
+ ///
|
|
|
|
|
+ fn allocate_fd(&mut self, from: FD, files: &RBTree<OpenFileAdapter>) -> FD {
|
|
|
|
|
+ let from = FD::max(from, self.min_avail);
|
|
|
|
|
+
|
|
|
|
|
+ if from == self.min_avail {
|
|
|
|
|
+ let next_min_avail = self.find_available(FD(from.0 + 1), files);
|
|
|
|
|
+ let allocated = self.min_avail;
|
|
|
|
|
+ self.min_avail = next_min_avail;
|
|
|
|
|
+ allocated
|
|
|
|
|
+ } else {
|
|
|
|
|
+ self.find_available(from, files)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn release_fd(&mut self, fd: FD) {
|
|
|
|
|
+ if fd < self.min_avail {
|
|
|
|
|
+ self.min_avail = fd;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn next_fd(&mut self, files: &RBTree<OpenFileAdapter>) -> FD {
|
|
|
|
|
+ self.allocate_fd(self.min_avail, files)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
impl FileArray {
|
|
impl FileArray {
|
|
|
pub fn new() -> Arc<Self> {
|
|
pub fn new() -> Arc<Self> {
|
|
|
Arc::new(FileArray {
|
|
Arc::new(FileArray {
|
|
|
inner: Spin::new(FileArrayInner {
|
|
inner: Spin::new(FileArrayInner {
|
|
|
- files: BTreeMap::new(),
|
|
|
|
|
- fd_min_avail: FD(0),
|
|
|
|
|
|
|
+ files: RBTree::new(OpenFileAdapter::new()),
|
|
|
|
|
+ fd_alloc: FDAllocator::new(),
|
|
|
}),
|
|
}),
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- #[allow(dead_code)]
|
|
|
|
|
pub fn new_shared(other: &Arc<Self>) -> Arc<Self> {
|
|
pub fn new_shared(other: &Arc<Self>) -> Arc<Self> {
|
|
|
other.clone()
|
|
other.clone()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
pub fn new_cloned(other: &Self) -> Arc<Self> {
|
|
pub fn new_cloned(other: &Self) -> Arc<Self> {
|
|
|
Arc::new(Self {
|
|
Arc::new(Self {
|
|
|
- inner: Spin::new(other.inner.lock().clone()),
|
|
|
|
|
|
|
+ inner: Spin::new({
|
|
|
|
|
+ let (new_files, new_fd_alloc) = {
|
|
|
|
|
+ let mut new_files = RBTree::new(OpenFileAdapter::new());
|
|
|
|
|
+ let other_inner = other.inner.lock();
|
|
|
|
|
+
|
|
|
|
|
+ for file in other_inner.files.iter() {
|
|
|
|
|
+ let new_file = OpenFile::new(file.fd, file.flags, file.file.dup());
|
|
|
|
|
+ new_files.insert(new_file);
|
|
|
|
|
+ }
|
|
|
|
|
+ (new_files, other_inner.fd_alloc.clone())
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ FileArrayInner {
|
|
|
|
|
+ files: new_files,
|
|
|
|
|
+ fd_alloc: new_fd_alloc,
|
|
|
|
|
+ }
|
|
|
|
|
+ }),
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// Acquires the file array lock.
|
|
/// Acquires the file array lock.
|
|
|
- pub fn get(&self, fd: FD) -> Option<Arc<File>> {
|
|
|
|
|
|
|
+ pub fn get(&self, fd: FD) -> Option<File> {
|
|
|
self.inner.lock().get(fd)
|
|
self.inner.lock().get(fd)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- pub fn close_all(&self) {
|
|
|
|
|
- let _old_files = {
|
|
|
|
|
|
|
+ pub async fn close_all(&self) {
|
|
|
|
|
+ let old_files = {
|
|
|
let mut inner = self.inner.lock();
|
|
let mut inner = self.inner.lock();
|
|
|
- inner.fd_min_avail = FD(0);
|
|
|
|
|
- core::mem::take(&mut inner.files)
|
|
|
|
|
|
|
+ inner.fd_alloc.reinit();
|
|
|
|
|
+ inner.files.take()
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
|
|
+ for file in old_files.into_iter() {
|
|
|
|
|
+ file.file.close().await;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- pub fn close(&self, fd: FD) -> KResult<()> {
|
|
|
|
|
- let _file = {
|
|
|
|
|
|
|
+ pub async fn close(&self, fd: FD) -> KResult<()> {
|
|
|
|
|
+ let file = {
|
|
|
let mut inner = self.inner.lock();
|
|
let mut inner = self.inner.lock();
|
|
|
- let file = inner.files.remove(&fd).ok_or(EBADF)?;
|
|
|
|
|
- inner.release_fd(fd);
|
|
|
|
|
- file
|
|
|
|
|
|
|
+ let file = inner.files.find_mut(&fd).remove().ok_or(EBADF)?;
|
|
|
|
|
+ inner.fd_alloc.release_fd(file.fd);
|
|
|
|
|
+ file.file
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
|
|
+ file.close().await;
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- pub fn on_exec(&self) -> () {
|
|
|
|
|
- let mut inner = self.inner.lock();
|
|
|
|
|
|
|
+ pub async fn on_exec(&self) {
|
|
|
|
|
+ let files_to_close = {
|
|
|
|
|
+ let mut inner = self.inner.lock();
|
|
|
|
|
+ let (files, fd_alloc) = inner.split_borrow();
|
|
|
|
|
|
|
|
- // TODO: This is not efficient. We should avoid cloning.
|
|
|
|
|
- let fds_to_close = inner
|
|
|
|
|
- .files
|
|
|
|
|
- .iter()
|
|
|
|
|
- .filter(|(_, ofile)| ofile.close_on_exec())
|
|
|
|
|
- .map(|(&fd, _)| fd)
|
|
|
|
|
- .collect::<Vec<_>>();
|
|
|
|
|
|
|
+ files.pick(|ofile| {
|
|
|
|
|
+ if ofile.close_on_exec() {
|
|
|
|
|
+ fd_alloc.release_fd(ofile.fd);
|
|
|
|
|
+ true
|
|
|
|
|
+ } else {
|
|
|
|
|
+ false
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- inner.files.retain(|_, ofile| !ofile.close_on_exec());
|
|
|
|
|
- fds_to_close.into_iter().for_each(|fd| inner.release_fd(fd));
|
|
|
|
|
|
|
+ for open_file in files_to_close.into_iter() {
|
|
|
|
|
+ open_file.file.close().await;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-impl FileArray {
|
|
|
|
|
pub fn dup(&self, old_fd: FD) -> KResult<FD> {
|
|
pub fn dup(&self, old_fd: FD) -> KResult<FD> {
|
|
|
let mut inner = self.inner.lock();
|
|
let mut inner = self.inner.lock();
|
|
|
- let old_file = inner.files.get(&old_fd).ok_or(EBADF)?;
|
|
|
|
|
|
|
+ let (files, fd_alloc) = inner.split_borrow();
|
|
|
|
|
+
|
|
|
|
|
+ let old_file = files.get_fd(old_fd).ok_or(EBADF)?;
|
|
|
|
|
|
|
|
- let new_file_data = old_file.file.clone();
|
|
|
|
|
|
|
+ let new_file_data = old_file.file.dup();
|
|
|
let new_file_flags = old_file.flags;
|
|
let new_file_flags = old_file.flags;
|
|
|
- let new_fd = inner.next_fd();
|
|
|
|
|
|
|
+ let new_fd = fd_alloc.next_fd(files);
|
|
|
|
|
|
|
|
inner.do_insert(new_fd, new_file_flags, new_file_data);
|
|
inner.do_insert(new_fd, new_file_flags, new_file_data);
|
|
|
|
|
|
|
|
Ok(new_fd)
|
|
Ok(new_fd)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- pub fn dup_to(&self, old_fd: FD, new_fd: FD, flags: OpenFlags) -> KResult<FD> {
|
|
|
|
|
- let fdflags = flags.as_fd_flags();
|
|
|
|
|
-
|
|
|
|
|
|
|
+ /// Duplicates the file to a new file descriptor, returning the old file
|
|
|
|
|
+ /// description to be dropped.
|
|
|
|
|
+ fn dup_to_no_close(&self, old_fd: FD, new_fd: FD, fd_flags: FDFlags) -> KResult<Option<File>> {
|
|
|
let mut inner = self.inner.lock();
|
|
let mut inner = self.inner.lock();
|
|
|
- let old_file = inner.files.get(&old_fd).ok_or(EBADF)?;
|
|
|
|
|
|
|
+ let (files, fd_alloc) = inner.split_borrow();
|
|
|
|
|
|
|
|
- let new_file_data = old_file.file.clone();
|
|
|
|
|
|
|
+ let old_file = files.get_fd(old_fd).ok_or(EBADF)?;
|
|
|
|
|
+ let new_file_data = old_file.file.dup();
|
|
|
|
|
|
|
|
- match inner.files.entry(new_fd) {
|
|
|
|
|
- Entry::Vacant(_) => {}
|
|
|
|
|
- Entry::Occupied(entry) => {
|
|
|
|
|
- let new_file = entry.into_mut();
|
|
|
|
|
- let mut file_swap = new_file_data;
|
|
|
|
|
|
|
+ match files.entry(&new_fd) {
|
|
|
|
|
+ Entry::Vacant(_) => {
|
|
|
|
|
+ assert_eq!(new_fd, fd_alloc.allocate_fd(new_fd, files));
|
|
|
|
|
+ inner.do_insert(new_fd, fd_flags, new_file_data);
|
|
|
|
|
|
|
|
- new_file.flags = fdflags;
|
|
|
|
|
- core::mem::swap(&mut file_swap, &mut new_file.file);
|
|
|
|
|
|
|
+ Ok(None)
|
|
|
|
|
+ }
|
|
|
|
|
+ Entry::Occupied(mut entry) => {
|
|
|
|
|
+ let mut file = entry.remove().unwrap();
|
|
|
|
|
+ file.flags = fd_flags;
|
|
|
|
|
+ let old_file = core::mem::replace(&mut file.file, new_file_data);
|
|
|
|
|
|
|
|
- drop(inner);
|
|
|
|
|
- return Ok(new_fd);
|
|
|
|
|
|
|
+ entry.insert(file);
|
|
|
|
|
+
|
|
|
|
|
+ Ok(Some(old_file))
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- assert_eq!(new_fd, inner.allocate_fd(new_fd));
|
|
|
|
|
- inner.do_insert(new_fd, fdflags, new_file_data);
|
|
|
|
|
|
|
+ pub async fn dup_to(&self, old_fd: FD, new_fd: FD, flags: OpenFlags) -> KResult<FD> {
|
|
|
|
|
+ if let Some(old_file) = self.dup_to_no_close(old_fd, new_fd, flags.as_fd_flags())? {
|
|
|
|
|
+ old_file.close().await;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
Ok(new_fd)
|
|
Ok(new_fd)
|
|
|
}
|
|
}
|
|
@@ -160,9 +266,10 @@ impl FileArray {
|
|
|
/// `(read_fd, write_fd)`
|
|
/// `(read_fd, write_fd)`
|
|
|
pub fn pipe(&self, flags: OpenFlags) -> KResult<(FD, FD)> {
|
|
pub fn pipe(&self, flags: OpenFlags) -> KResult<(FD, FD)> {
|
|
|
let mut inner = self.inner.lock();
|
|
let mut inner = self.inner.lock();
|
|
|
|
|
+ let (files, fd_alloc) = inner.split_borrow();
|
|
|
|
|
|
|
|
- let read_fd = inner.next_fd();
|
|
|
|
|
- let write_fd = inner.next_fd();
|
|
|
|
|
|
|
+ let read_fd = fd_alloc.next_fd(files);
|
|
|
|
|
+ let write_fd = fd_alloc.next_fd(files);
|
|
|
|
|
|
|
|
let fdflag = flags.as_fd_flags();
|
|
let fdflag = flags.as_fd_flags();
|
|
|
|
|
|
|
@@ -179,23 +286,20 @@ impl FileArray {
|
|
|
let fdflag = flags.as_fd_flags();
|
|
let fdflag = flags.as_fd_flags();
|
|
|
|
|
|
|
|
let inode = dentry.get_inode()?;
|
|
let inode = dentry.get_inode()?;
|
|
|
- let filemode = inode.mode.load(Ordering::Relaxed);
|
|
|
|
|
|
|
+ let file_format = inode.mode.load().format();
|
|
|
|
|
|
|
|
- if flags.directory() {
|
|
|
|
|
- if !s_isdir(filemode) {
|
|
|
|
|
- return Err(ENOTDIR);
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- if s_isdir(filemode) && flags.write() {
|
|
|
|
|
- return Err(EISDIR);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ match (flags.directory(), file_format, flags.write()) {
|
|
|
|
|
+ (true, Mode::DIR, _) => {}
|
|
|
|
|
+ (true, _, _) => return Err(ENOTDIR),
|
|
|
|
|
+ (false, Mode::DIR, true) => return Err(EISDIR),
|
|
|
|
|
+ _ => {}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if flags.truncate() && flags.write() && s_isreg(filemode) {
|
|
|
|
|
|
|
+ if flags.truncate() && flags.write() && file_format.is_reg() {
|
|
|
inode.truncate(0)?;
|
|
inode.truncate(0)?;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- let file = if s_ischr(filemode) {
|
|
|
|
|
|
|
+ let file = if file_format.is_chr() {
|
|
|
let device = CharDevice::get(inode.devid()?).ok_or(ENXIO)?;
|
|
let device = CharDevice::get(inode.devid()?).ok_or(ENXIO)?;
|
|
|
device.open(flags)?
|
|
device.open(flags)?
|
|
|
} else {
|
|
} else {
|
|
@@ -203,7 +307,8 @@ impl FileArray {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
let mut inner = self.inner.lock();
|
|
let mut inner = self.inner.lock();
|
|
|
- let fd = inner.next_fd();
|
|
|
|
|
|
|
+ let (files, fd_alloc) = inner.split_borrow();
|
|
|
|
|
+ let fd = fd_alloc.next_fd(files);
|
|
|
inner.do_insert(fd, fdflag, file);
|
|
inner.do_insert(fd, fdflag, file);
|
|
|
|
|
|
|
|
Ok(fd)
|
|
Ok(fd)
|
|
@@ -211,43 +316,59 @@ impl FileArray {
|
|
|
|
|
|
|
|
pub fn fcntl(&self, fd: FD, cmd: u32, arg: usize) -> KResult<usize> {
|
|
pub fn fcntl(&self, fd: FD, cmd: u32, arg: usize) -> KResult<usize> {
|
|
|
let mut inner = self.inner.lock();
|
|
let mut inner = self.inner.lock();
|
|
|
- let ofile = inner.files.get_mut(&fd).ok_or(EBADF)?;
|
|
|
|
|
|
|
+ let (files, fd_alloc) = inner.split_borrow();
|
|
|
|
|
+
|
|
|
|
|
+ let mut cursor = files.find_mut(&fd);
|
|
|
|
|
|
|
|
- match cmd {
|
|
|
|
|
|
|
+ let ret = match cmd {
|
|
|
F_DUPFD | F_DUPFD_CLOEXEC => {
|
|
F_DUPFD | F_DUPFD_CLOEXEC => {
|
|
|
|
|
+ let ofile = cursor.get().ok_or(EBADF)?;
|
|
|
|
|
+
|
|
|
let cloexec = cmd == F_DUPFD_CLOEXEC || ofile.flags.close_on_exec();
|
|
let cloexec = cmd == F_DUPFD_CLOEXEC || ofile.flags.close_on_exec();
|
|
|
let flags = cloexec
|
|
let flags = cloexec
|
|
|
.then_some(FDFlags::FD_CLOEXEC)
|
|
.then_some(FDFlags::FD_CLOEXEC)
|
|
|
.unwrap_or(FDFlags::empty());
|
|
.unwrap_or(FDFlags::empty());
|
|
|
|
|
|
|
|
- let new_file_data = ofile.file.clone();
|
|
|
|
|
- let new_fd = inner.allocate_fd(FD(arg as u32));
|
|
|
|
|
|
|
+ let new_file_data = ofile.file.dup();
|
|
|
|
|
+ let new_fd = fd_alloc.allocate_fd(FD(arg as u32), files);
|
|
|
|
|
|
|
|
inner.do_insert(new_fd, flags, new_file_data);
|
|
inner.do_insert(new_fd, flags, new_file_data);
|
|
|
|
|
|
|
|
- Ok(new_fd.0 as usize)
|
|
|
|
|
|
|
+ new_fd.0 as usize
|
|
|
}
|
|
}
|
|
|
- F_GETFD => Ok(ofile.flags.bits() as usize),
|
|
|
|
|
|
|
+ F_GETFD => cursor.get().ok_or(EBADF)?.flags.bits() as usize,
|
|
|
F_SETFD => {
|
|
F_SETFD => {
|
|
|
|
|
+ let mut ofile = cursor.remove().ok_or(EBADF)?;
|
|
|
ofile.flags = FDFlags::from_bits_truncate(arg as u32);
|
|
ofile.flags = FDFlags::from_bits_truncate(arg as u32);
|
|
|
- Ok(0)
|
|
|
|
|
|
|
+ cursor.insert(ofile);
|
|
|
|
|
+ 0
|
|
|
}
|
|
}
|
|
|
- F_GETFL => Ok(ofile.file.get_flags().bits() as usize),
|
|
|
|
|
|
|
+ F_GETFL => cursor.get().ok_or(EBADF)?.file.get_flags().bits() as usize,
|
|
|
F_SETFL => {
|
|
F_SETFL => {
|
|
|
- ofile
|
|
|
|
|
|
|
+ cursor
|
|
|
|
|
+ .get()
|
|
|
|
|
+ .ok_or(EBADF)?
|
|
|
.file
|
|
.file
|
|
|
.set_flags(OpenFlags::from_bits_retain(arg as u32));
|
|
.set_flags(OpenFlags::from_bits_retain(arg as u32));
|
|
|
|
|
|
|
|
- Ok(0)
|
|
|
|
|
|
|
+ 0
|
|
|
}
|
|
}
|
|
|
_ => unimplemented!("fcntl: cmd={}", cmd),
|
|
_ => unimplemented!("fcntl: cmd={}", cmd),
|
|
|
- }
|
|
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ Ok(ret)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// Only used for init process.
|
|
/// Only used for init process.
|
|
|
pub fn open_console(&self) {
|
|
pub fn open_console(&self) {
|
|
|
let mut inner = self.inner.lock();
|
|
let mut inner = self.inner.lock();
|
|
|
- let (stdin, stdout, stderr) = (inner.next_fd(), inner.next_fd(), inner.next_fd());
|
|
|
|
|
|
|
+ let (files, fd_alloc) = inner.split_borrow();
|
|
|
|
|
+
|
|
|
|
|
+ let (stdin, stdout, stderr) = (
|
|
|
|
|
+ fd_alloc.next_fd(files),
|
|
|
|
|
+ fd_alloc.next_fd(files),
|
|
|
|
|
+ fd_alloc.next_fd(files),
|
|
|
|
|
+ );
|
|
|
let console_terminal = get_console().expect("No console terminal");
|
|
let console_terminal = get_console().expect("No console terminal");
|
|
|
|
|
|
|
|
inner.do_insert(
|
|
inner.do_insert(
|
|
@@ -269,53 +390,25 @@ impl FileArray {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl FileArrayInner {
|
|
impl FileArrayInner {
|
|
|
- fn get(&mut self, fd: FD) -> Option<Arc<File>> {
|
|
|
|
|
- self.files.get(&fd).map(|f| f.file.clone())
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn find_available(&mut self, from: FD) -> FD {
|
|
|
|
|
- self.files
|
|
|
|
|
- .range(&from..)
|
|
|
|
|
- .fold_while(from, |current, (&key, _)| {
|
|
|
|
|
- if current == key {
|
|
|
|
|
- Continue(FD(current.0 + 1))
|
|
|
|
|
- } else {
|
|
|
|
|
- Done(current)
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- .into_inner()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// Allocate a new file descriptor starting from `from`.
|
|
|
|
|
- ///
|
|
|
|
|
- /// Returned file descriptor should be used immediately.
|
|
|
|
|
- ///
|
|
|
|
|
- fn allocate_fd(&mut self, from: FD) -> FD {
|
|
|
|
|
- let from = FD::max(from, self.fd_min_avail);
|
|
|
|
|
-
|
|
|
|
|
- if from == self.fd_min_avail {
|
|
|
|
|
- let next_min_avail = self.find_available(FD(from.0 + 1));
|
|
|
|
|
- let allocated = self.fd_min_avail;
|
|
|
|
|
- self.fd_min_avail = next_min_avail;
|
|
|
|
|
- allocated
|
|
|
|
|
- } else {
|
|
|
|
|
- self.find_available(from)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ fn get(&mut self, fd: FD) -> Option<File> {
|
|
|
|
|
+ self.files.get_fd(fd).map(|open| open.file.clone())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- fn release_fd(&mut self, fd: FD) {
|
|
|
|
|
- if fd < self.fd_min_avail {
|
|
|
|
|
- self.fd_min_avail = fd;
|
|
|
|
|
|
|
+ /// Insert a file description to the file array.
|
|
|
|
|
+ fn do_insert(&mut self, fd: FD, flags: FDFlags, file: File) {
|
|
|
|
|
+ match self.files.entry(&fd) {
|
|
|
|
|
+ Entry::Occupied(_) => {
|
|
|
|
|
+ panic!("File descriptor {fd:?} already exists in the file array.");
|
|
|
|
|
+ }
|
|
|
|
|
+ Entry::Vacant(insert_cursor) => {
|
|
|
|
|
+ insert_cursor.insert(OpenFile::new(fd, flags, file));
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- fn next_fd(&mut self) -> FD {
|
|
|
|
|
- self.allocate_fd(self.fd_min_avail)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// Insert a file description to the file array.
|
|
|
|
|
- fn do_insert(&mut self, fd: FD, flags: FDFlags, file: Arc<File>) {
|
|
|
|
|
- assert!(self.files.insert(fd, OpenFile { flags, file }).is_none());
|
|
|
|
|
|
|
+ fn split_borrow(&mut self) -> (&mut RBTree<OpenFileAdapter>, &mut FDAllocator) {
|
|
|
|
|
+ let Self { files, fd_alloc } = self;
|
|
|
|
|
+ (files, fd_alloc)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -343,3 +436,39 @@ impl SyscallRetVal for FD {
|
|
|
Some(self.0 as usize)
|
|
Some(self.0 as usize)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+trait FilesExt {
|
|
|
|
|
+ fn get_fd(&self, fd: FD) -> Option<&OpenFile>;
|
|
|
|
|
+
|
|
|
|
|
+ fn pick<P>(&mut self, pred: P) -> Self
|
|
|
|
|
+ where
|
|
|
|
|
+ P: FnMut(&OpenFile) -> bool;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl FilesExt for RBTree<OpenFileAdapter> {
|
|
|
|
|
+ fn get_fd(&self, fd: FD) -> Option<&OpenFile> {
|
|
|
|
|
+ self.find(&fd).get()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn pick<P>(&mut self, mut pred: P) -> Self
|
|
|
|
|
+ where
|
|
|
|
|
+ P: FnMut(&OpenFile) -> bool,
|
|
|
|
|
+ {
|
|
|
|
|
+ let mut picked = RBTree::new(OpenFileAdapter::new());
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: might be better if we start picking from somewhere else
|
|
|
|
|
+ // or using a different approach.
|
|
|
|
|
+ let mut cursor = self.front_mut();
|
|
|
|
|
+ while let Some(open_file) = cursor.get() {
|
|
|
|
|
+ if !pred(open_file) {
|
|
|
|
|
+ cursor.move_next();
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ picked.insert(cursor.remove().unwrap());
|
|
|
|
|
+ cursor.move_next();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ picked
|
|
|
|
|
+ }
|
|
|
|
|
+}
|