wasmer_wasix/fs/fd.rs
1use std::{
2 borrow::Cow,
3 collections::HashMap,
4 path::PathBuf,
5 sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard, atomic::AtomicU64},
6};
7
8#[cfg(feature = "enable-serde")]
9use serde_derive::{Deserialize, Serialize};
10use virtual_fs::{Pipe, PipeRx, PipeTx, VirtualFile};
11use wasmer_wasix_types::wasi::{Fd as WasiFd, Fdflags, Fdflagsext, Filestat, Rights};
12
13use crate::net::socket::InodeSocket;
14use crate::os::epoll::EpollState;
15
16use super::{InodeGuard, InodeWeakGuard, NotificationInner};
17
18/// Shared handle to an open [`VirtualFile`].
19pub(crate) type VirtualFileLock = Arc<RwLock<Box<dyn VirtualFile + Send + Sync + 'static>>>;
20
21#[derive(Debug, Clone)]
22#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
23pub struct Fd {
24 #[cfg_attr(feature = "enable-serde", serde(flatten))]
25 pub inner: FdInner,
26
27 /// Flags that determine how the [`Fd`] can be used.
28 ///
29 /// Used when reopening a [`VirtualFile`] during deserialization.
30 pub open_flags: u16,
31 pub inode: InodeGuard,
32 pub is_stdio: bool,
33}
34
35// This struct contains the bits of Fd that are safe to mutate, so that
36// FdList::get_mut can safely return mutable references.
37#[derive(Debug, Clone)]
38#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
39pub struct FdInner {
40 pub rights: Rights,
41 pub rights_inheriting: Rights,
42 pub flags: Fdflags, // This is file table related flags, not fd flags
43 pub offset: Arc<AtomicU64>, // This also belongs in the file table
44 pub fd_flags: Fdflagsext, // This is the actual FD flags that belongs here
45}
46
47impl Fd {
48 /// This [`Fd`] can be used with read system calls.
49 pub const READ: u16 = 1;
50 /// This [`Fd`] can be used with write system calls.
51 pub const WRITE: u16 = 2;
52 /// This [`Fd`] can append in write system calls. Note that the append
53 /// permission implies the write permission.
54 pub const APPEND: u16 = 4;
55 /// This [`Fd`] will delete everything before writing. Note that truncate
56 /// permissions require the write permission.
57 ///
58 /// This permission is currently unused when deserializing.
59 pub const TRUNCATE: u16 = 8;
60 /// This [`Fd`] may create a file before writing to it. Note that create
61 /// permissions require write permissions.
62 ///
63 /// This permission is currently unused when deserializing.
64 pub const CREATE: u16 = 16;
65}
66
67/// A file that Wasi knows about that may or may not be open
68#[derive(Debug)]
69#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
70pub struct InodeVal {
71 pub stat: RwLock<Filestat>,
72 pub is_preopened: bool,
73 pub name: RwLock<Cow<'static, str>>,
74 pub kind: RwLock<Kind>,
75}
76
77impl InodeVal {
78 pub fn read(&self) -> RwLockReadGuard<'_, Kind> {
79 self.kind.read().unwrap()
80 }
81
82 pub fn write(&self) -> RwLockWriteGuard<'_, Kind> {
83 self.kind.write().unwrap()
84 }
85}
86
87/// The core of the filesystem abstraction. Includes directories,
88/// files, and symlinks.
89#[derive(Debug)]
90#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
91pub enum Kind {
92 File {
93 /// The open file, if it's open
94 #[cfg_attr(feature = "enable-serde", serde(skip))]
95 handle: Option<VirtualFileLock>,
96 /// The path on the host system where the file is located
97 /// This is deprecated and will be removed soon
98 path: PathBuf,
99 /// Marks the file as a special file that only one `fd` can exist for
100 /// This is useful when dealing with host-provided special files that
101 /// should be looked up by path
102 /// TODO: clarify here?
103 fd: Option<u32>,
104 },
105 #[cfg_attr(feature = "enable-serde", serde(skip))]
106 Socket {
107 /// Represents a networking socket
108 socket: InodeSocket,
109 },
110 #[cfg_attr(feature = "enable-serde", serde(skip))]
111 PipeTx {
112 tx: PipeTx,
113 },
114 #[cfg_attr(feature = "enable-serde", serde(skip))]
115 PipeRx {
116 rx: PipeRx,
117 },
118 #[cfg_attr(feature = "enable-serde", serde(skip))]
119 DuplexPipe {
120 pipe: Pipe,
121 },
122 #[cfg_attr(feature = "enable-serde", serde(skip))]
123 Epoll {
124 state: Arc<EpollState>,
125 },
126 Dir {
127 /// Parent directory
128 parent: InodeWeakGuard,
129 /// The path on the host system where the directory is located
130 // TODO: wrap it like VirtualFile
131 path: PathBuf,
132 /// The entries of a directory are lazily filled.
133 entries: HashMap<String, InodeGuard>,
134 },
135 /// The same as Dir but without the irrelevant bits
136 /// The root is immutable after creation; generally the Kind::Root
137 /// branch of whatever code you're writing will be a simpler version of
138 /// your Kind::Dir logic
139 Root {
140 entries: HashMap<String, InodeGuard>,
141 },
142 /// The first two fields are data _about_ the symlink
143 /// the last field is the data _inside_ the symlink
144 ///
145 /// `base_po_dir` should never be the root because:
146 /// - Right now symlinks are not allowed in the immutable root
147 /// - There is always a closer pre-opened dir to the symlink file (by definition of the root being a collection of preopened dirs)
148 Symlink {
149 /// The preopened dir that this symlink file is relative to (via `path_to_symlink`)
150 base_po_dir: WasiFd,
151 /// The path to the symlink from the `base_po_dir`
152 path_to_symlink: PathBuf,
153 /// the value of the symlink as a relative path
154 relative_path: PathBuf,
155 },
156 Buffer {
157 buffer: Vec<u8>,
158 },
159 EventNotifications {
160 inner: Arc<NotificationInner>,
161 },
162}