wasmer_wasix/fs/
fd.rs

1use std::{
2    borrow::Cow,
3    collections::{HashMap, HashSet},
4    path::PathBuf,
5    sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard, atomic::AtomicU64},
6};
7
8use serde_derive::{Deserialize, Serialize};
9use std::sync::Mutex as StdMutex;
10use tokio::sync::{Mutex as AsyncMutex, watch};
11use virtual_fs::{Pipe, PipeRx, PipeTx, VirtualFile};
12use wasmer_wasix_types::wasi::{EpollType, Fd as WasiFd, Fdflags, Fdflagsext, Filestat, Rights};
13
14use crate::net::socket::InodeSocket;
15
16use super::{
17    InodeGuard, InodeValFilePollGuard, InodeValFilePollGuardMode, InodeWeakGuard, NotificationInner,
18};
19
20#[derive(Debug, Clone)]
21#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
22pub struct Fd {
23    #[cfg_attr(feature = "enable-serde", serde(flatten))]
24    pub inner: FdInner,
25
26    /// Flags that determine how the [`Fd`] can be used.
27    ///
28    /// Used when reopening a [`VirtualFile`] during deserialization.
29    pub open_flags: u16,
30    pub inode: InodeGuard,
31    pub is_stdio: bool,
32}
33
34// This struct contains the bits of Fd that are safe to mutate, so that
35// FdList::get_mut can safely return mutable references.
36#[derive(Debug, Clone)]
37#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
38pub struct FdInner {
39    pub rights: Rights,
40    pub rights_inheriting: Rights,
41    pub flags: Fdflags,         // This is file table related flags, not fd flags
42    pub offset: Arc<AtomicU64>, // This also belongs in the file table
43    pub fd_flags: Fdflagsext,   // This is the actual FD flags that belongs here
44}
45
46impl Fd {
47    /// This [`Fd`] can be used with read system calls.
48    pub const READ: u16 = 1;
49    /// This [`Fd`] can be used with write system calls.
50    pub const WRITE: u16 = 2;
51    /// This [`Fd`] can append in write system calls. Note that the append
52    /// permission implies the write permission.
53    pub const APPEND: u16 = 4;
54    /// This [`Fd`] will delete everything before writing. Note that truncate
55    /// permissions require the write permission.
56    ///
57    /// This permission is currently unused when deserializing.
58    pub const TRUNCATE: u16 = 8;
59    /// This [`Fd`] may create a file before writing to it. Note that create
60    /// permissions require write permissions.
61    ///
62    /// This permission is currently unused when deserializing.
63    pub const CREATE: u16 = 16;
64}
65
66/// A file that Wasi knows about that may or may not be open
67#[derive(Debug)]
68#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
69pub struct InodeVal {
70    pub stat: RwLock<Filestat>,
71    pub is_preopened: bool,
72    pub name: RwLock<Cow<'static, str>>,
73    pub kind: RwLock<Kind>,
74}
75
76impl InodeVal {
77    pub fn read(&self) -> RwLockReadGuard<'_, Kind> {
78        self.kind.read().unwrap()
79    }
80
81    pub fn write(&self) -> RwLockWriteGuard<'_, Kind> {
82        self.kind.write().unwrap()
83    }
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct EpollFd {
88    /// The events we are polling on
89    pub events: EpollType,
90    /// Pointer to the user data
91    pub ptr: u64,
92    /// File descriptor we are polling on
93    pub fd: WasiFd,
94    /// Associated user data
95    pub data1: u32,
96    /// Associated user data
97    pub data2: u64,
98}
99
100/// Represents all the EpollInterests that have occurred
101#[derive(Debug, Default, Serialize, Deserialize)]
102pub struct EpollInterest {
103    /// Using a hash set prevents the same interest from
104    /// being triggered more than once
105    pub interest: HashSet<(WasiFd, EpollType)>,
106}
107
108/// Guard the cleans up the selector registrations
109#[derive(Debug)]
110pub struct EpollJoinGuard {
111    pub(crate) fd_guard: InodeValFilePollGuard,
112}
113impl Drop for EpollJoinGuard {
114    fn drop(&mut self) {
115        match &self.fd_guard.mode {
116            InodeValFilePollGuardMode::File(_) => {
117                // Intentionally ignored, epoll doesn't work with files
118            }
119            InodeValFilePollGuardMode::Socket { inner } => {
120                let mut inner = inner.protected.write().unwrap();
121                inner.remove_handler();
122            }
123            InodeValFilePollGuardMode::EventNotifications(inner) => {
124                inner.remove_interest_handler();
125            }
126            InodeValFilePollGuardMode::DuplexPipe { pipe } => {
127                let inner = pipe.write().unwrap();
128                inner.remove_interest_handler();
129            }
130            InodeValFilePollGuardMode::PipeRx { rx } => {
131                let inner = rx.write().unwrap();
132                inner.remove_interest_handler();
133            }
134            InodeValFilePollGuardMode::PipeTx { .. } => {
135                // Intentionally ignored, the sending end of a pipe can't have an interest handler
136            }
137        }
138    }
139}
140
141pub type EpollSubscriptions = HashMap<WasiFd, (EpollFd, Vec<EpollJoinGuard>)>;
142
143/// The core of the filesystem abstraction.  Includes directories,
144/// files, and symlinks.
145#[derive(Debug)]
146#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
147pub enum Kind {
148    File {
149        /// The open file, if it's open
150        #[cfg_attr(feature = "enable-serde", serde(skip))]
151        handle: Option<Arc<RwLock<Box<dyn VirtualFile + Send + Sync + 'static>>>>,
152        /// The path on the host system where the file is located
153        /// This is deprecated and will be removed soon
154        path: PathBuf,
155        /// Marks the file as a special file that only one `fd` can exist for
156        /// This is useful when dealing with host-provided special files that
157        /// should be looked up by path
158        /// TOOD: clarify here?
159        fd: Option<u32>,
160    },
161    #[cfg_attr(feature = "enable-serde", serde(skip))]
162    Socket {
163        /// Represents a networking socket
164        socket: InodeSocket,
165    },
166    #[cfg_attr(feature = "enable-serde", serde(skip))]
167    PipeTx {
168        tx: PipeTx,
169    },
170    #[cfg_attr(feature = "enable-serde", serde(skip))]
171    PipeRx {
172        rx: PipeRx,
173    },
174    #[cfg_attr(feature = "enable-serde", serde(skip))]
175    DuplexPipe {
176        pipe: Pipe,
177    },
178    Epoll {
179        // List of events we are polling on
180        subscriptions: Arc<StdMutex<EpollSubscriptions>>,
181        // Notification pipeline for sending events
182        tx: Arc<watch::Sender<EpollInterest>>,
183        // Notification pipeline for events that need to be
184        // checked on the next wait
185        rx: Arc<AsyncMutex<watch::Receiver<EpollInterest>>>,
186    },
187    Dir {
188        /// Parent directory
189        parent: InodeWeakGuard,
190        /// The path on the host system where the directory is located
191        // TODO: wrap it like VirtualFile
192        path: PathBuf,
193        /// The entries of a directory are lazily filled.
194        entries: HashMap<String, InodeGuard>,
195    },
196    /// The same as Dir but without the irrelevant bits
197    /// The root is immutable after creation; generally the Kind::Root
198    /// branch of whatever code you're writing will be a simpler version of
199    /// your Kind::Dir logic
200    Root {
201        entries: HashMap<String, InodeGuard>,
202    },
203    /// The first two fields are data _about_ the symlink
204    /// the last field is the data _inside_ the symlink
205    ///
206    /// `base_po_dir` should never be the root because:
207    /// - Right now symlinks are not allowed in the immutable root
208    /// - There is always a closer pre-opened dir to the symlink file (by definition of the root being a collection of preopened dirs)
209    Symlink {
210        /// The preopened dir that this symlink file is relative to (via `path_to_symlink`)
211        base_po_dir: WasiFd,
212        /// The path to the symlink from the `base_po_dir`
213        path_to_symlink: PathBuf,
214        /// the value of the symlink as a relative path
215        relative_path: PathBuf,
216    },
217    Buffer {
218        buffer: Vec<u8>,
219    },
220    EventNotifications {
221        inner: Arc<NotificationInner>,
222    },
223}