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
use std::{
    borrow::Cow,
    collections::{HashMap, HashSet},
    path::PathBuf,
    pin::Pin,
    sync::{atomic::AtomicU64, Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
    task::Context,
};

use futures::Future;
use serde_derive::{Deserialize, Serialize};
use std::sync::Mutex as StdMutex;
use tokio::sync::{watch, Mutex as AsyncMutex};
use virtual_fs::{Pipe, VirtualFile};
use wasmer_wasix_types::wasi::{EpollType, Fd as WasiFd, Fdflags, Filestat, Rights};

use crate::{net::socket::InodeSocket, syscalls::EpollJoinWaker};

use super::{
    InodeGuard, InodeValFilePollGuard, InodeValFilePollGuardJoin, InodeValFilePollGuardMode,
    InodeWeakGuard, NotificationInner,
};

#[derive(Debug, Clone)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct Fd {
    pub rights: Rights,
    pub rights_inheriting: Rights,
    pub flags: Fdflags,
    pub offset: Arc<AtomicU64>,
    /// Flags that determine how the [`Fd`] can be used.
    ///
    /// Used when reopening a [`VirtualFile`] during deserialization.
    pub open_flags: u16,
    pub inode: InodeGuard,
    pub is_stdio: bool,
}

impl Fd {
    /// This [`Fd`] can be used with read system calls.
    pub const READ: u16 = 1;
    /// This [`Fd`] can be used with write system calls.
    pub const WRITE: u16 = 2;
    /// This [`Fd`] can append in write system calls. Note that the append
    /// permission implies the write permission.
    pub const APPEND: u16 = 4;
    /// This [`Fd`] will delete everything before writing. Note that truncate
    /// permissions require the write permission.
    ///
    /// This permission is currently unused when deserializing.
    pub const TRUNCATE: u16 = 8;
    /// This [`Fd`] may create a file before writing to it. Note that create
    /// permissions require write permissions.
    ///
    /// This permission is currently unused when deserializing.
    pub const CREATE: u16 = 16;
}

/// A file that Wasi knows about that may or may not be open
#[derive(Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct InodeVal {
    pub stat: RwLock<Filestat>,
    pub is_preopened: bool,
    pub name: Cow<'static, str>,
    pub kind: RwLock<Kind>,
}

impl InodeVal {
    pub fn read(&self) -> RwLockReadGuard<Kind> {
        self.kind.read().unwrap()
    }

    pub fn write(&self) -> RwLockWriteGuard<Kind> {
        self.kind.write().unwrap()
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EpollFd {
    /// The events we are polling on
    pub events: EpollType,
    /// Pointer to the user data
    pub ptr: u64,
    /// File descriptor we are polling on
    pub fd: WasiFd,
    /// Associated user data
    pub data1: u32,
    /// Associated user data
    pub data2: u64,
}

/// Represents all the EpollInterests that have occurred
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct EpollInterest {
    /// Using a hash set prevents the same interest from
    /// being triggered more than once
    pub interest: HashSet<(WasiFd, EpollType)>,
}

/// Guard the cleans up the selector registrations
#[derive(Debug)]
pub enum EpollJoinGuard {
    Join {
        join_guard: InodeValFilePollGuardJoin,
        epoll_waker: Arc<EpollJoinWaker>,
    },
    Handler {
        fd_guard: InodeValFilePollGuard,
    },
}
impl Drop for EpollJoinGuard {
    fn drop(&mut self) {
        if let Self::Handler { fd_guard, .. } = self {
            if let InodeValFilePollGuardMode::Socket { inner } = &mut fd_guard.mode {
                let mut inner = inner.protected.write().unwrap();
                inner.remove_handler();
            }
        }
    }
}
impl EpollJoinGuard {
    pub fn is_spent(&self) -> bool {
        match self {
            Self::Join { join_guard, .. } => join_guard.is_spent(),
            Self::Handler { .. } => false,
        }
    }
    pub fn renew(&mut self) {
        if let Self::Join {
            join_guard,
            epoll_waker,
        } = self
        {
            let fd = join_guard.fd();
            join_guard.reset();

            let waker = epoll_waker.as_waker();
            let mut cx = Context::from_waker(&waker);
            if Pin::new(join_guard).poll(&mut cx).is_ready() {
                tracing::trace!(fd, "join renew already woken");
                waker.wake();
            } else {
                tracing::trace!(fd, "join waker reinstalled");
            }
        }
    }
}

pub type EpollSubscriptions = HashMap<WasiFd, (EpollFd, Vec<EpollJoinGuard>)>;

/// The core of the filesystem abstraction.  Includes directories,
/// files, and symlinks.
#[derive(Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum Kind {
    File {
        /// The open file, if it's open
        #[cfg_attr(feature = "enable-serde", serde(skip))]
        handle: Option<Arc<RwLock<Box<dyn VirtualFile + Send + Sync + 'static>>>>,
        /// The path on the host system where the file is located
        /// This is deprecated and will be removed soon
        path: PathBuf,
        /// Marks the file as a special file that only one `fd` can exist for
        /// This is useful when dealing with host-provided special files that
        /// should be looked up by path
        /// TOOD: clarify here?
        fd: Option<u32>,
    },
    #[cfg_attr(feature = "enable-serde", serde(skip))]
    Socket {
        /// Represents a networking socket
        socket: InodeSocket,
    },
    #[cfg_attr(feature = "enable-serde", serde(skip))]
    Pipe {
        /// Reference to the pipe
        pipe: Pipe,
    },
    Epoll {
        // List of events we are polling on
        subscriptions: Arc<StdMutex<EpollSubscriptions>>,
        // Notification pipeline for sending events
        tx: Arc<watch::Sender<EpollInterest>>,
        // Notification pipeline for events that need to be
        // checked on the next wait
        rx: Arc<AsyncMutex<watch::Receiver<EpollInterest>>>,
    },
    Dir {
        /// Parent directory
        parent: InodeWeakGuard,
        /// The path on the host system where the directory is located
        // TODO: wrap it like VirtualFile
        path: PathBuf,
        /// The entries of a directory are lazily filled.
        entries: HashMap<String, InodeGuard>,
    },
    /// The same as Dir but without the irrelevant bits
    /// The root is immutable after creation; generally the Kind::Root
    /// branch of whatever code you're writing will be a simpler version of
    /// your Kind::Dir logic
    Root {
        entries: HashMap<String, InodeGuard>,
    },
    /// The first two fields are data _about_ the symlink
    /// the last field is the data _inside_ the symlink
    ///
    /// `base_po_dir` should never be the root because:
    /// - Right now symlinks are not allowed in the immutable root
    /// - There is always a closer pre-opened dir to the symlink file (by definition of the root being a collection of preopened dirs)
    Symlink {
        /// The preopened dir that this symlink file is relative to (via `path_to_symlink`)
        base_po_dir: WasiFd,
        /// The path to the symlink from the `base_po_dir`
        path_to_symlink: PathBuf,
        /// the value of the symlink as a relative path
        relative_path: PathBuf,
    },
    Buffer {
        buffer: Vec<u8>,
    },
    EventNotifications {
        inner: Arc<NotificationInner>,
    },
}