Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wasi-common/src/tokio/dir.rs
1692 views
1
use crate::tokio::{block_on_dummy_executor, file::File};
2
use crate::{
3
Error, ErrorExt,
4
dir::{ReaddirCursor, ReaddirEntity, WasiDir},
5
file::{FdFlags, Filestat, OFlags},
6
};
7
use std::any::Any;
8
use std::path::PathBuf;
9
10
pub struct Dir(crate::sync::dir::Dir);
11
12
impl Dir {
13
pub fn from_cap_std(dir: cap_std::fs::Dir) -> Self {
14
Dir(crate::sync::dir::Dir::from_cap_std(dir))
15
}
16
}
17
18
#[wiggle::async_trait]
19
impl WasiDir for Dir {
20
fn as_any(&self) -> &dyn Any {
21
self
22
}
23
async fn open_file(
24
&self,
25
symlink_follow: bool,
26
path: &str,
27
oflags: OFlags,
28
read: bool,
29
write: bool,
30
fdflags: FdFlags,
31
) -> Result<crate::dir::OpenResult, Error> {
32
let f = block_on_dummy_executor(move || async move {
33
self.0
34
.open_file_(symlink_follow, path, oflags, read, write, fdflags)
35
})?;
36
match f {
37
crate::sync::dir::OpenResult::File(f) => {
38
Ok(crate::dir::OpenResult::File(Box::new(File::from_inner(f))))
39
}
40
crate::sync::dir::OpenResult::Dir(d) => {
41
Ok(crate::dir::OpenResult::Dir(Box::new(Dir(d))))
42
}
43
}
44
}
45
46
async fn create_dir(&self, path: &str) -> Result<(), Error> {
47
block_on_dummy_executor(|| self.0.create_dir(path))
48
}
49
async fn readdir(
50
&self,
51
cursor: ReaddirCursor,
52
) -> Result<Box<dyn Iterator<Item = Result<ReaddirEntity, Error>> + Send>, Error> {
53
struct I(Box<dyn Iterator<Item = Result<ReaddirEntity, Error>> + Send>);
54
impl Iterator for I {
55
type Item = Result<ReaddirEntity, Error>;
56
fn next(&mut self) -> Option<Self::Item> {
57
tokio::task::block_in_place(move || self.0.next())
58
}
59
}
60
61
let inner = block_on_dummy_executor(move || self.0.readdir(cursor))?;
62
Ok(Box::new(I(inner)))
63
}
64
65
async fn symlink(&self, src_path: &str, dest_path: &str) -> Result<(), Error> {
66
block_on_dummy_executor(move || self.0.symlink(src_path, dest_path))
67
}
68
async fn remove_dir(&self, path: &str) -> Result<(), Error> {
69
block_on_dummy_executor(move || self.0.remove_dir(path))
70
}
71
72
async fn unlink_file(&self, path: &str) -> Result<(), Error> {
73
block_on_dummy_executor(move || self.0.unlink_file(path))
74
}
75
async fn read_link(&self, path: &str) -> Result<PathBuf, Error> {
76
block_on_dummy_executor(move || self.0.read_link(path))
77
}
78
async fn get_filestat(&self) -> Result<Filestat, Error> {
79
block_on_dummy_executor(|| self.0.get_filestat())
80
}
81
async fn get_path_filestat(
82
&self,
83
path: &str,
84
follow_symlinks: bool,
85
) -> Result<Filestat, Error> {
86
block_on_dummy_executor(move || self.0.get_path_filestat(path, follow_symlinks))
87
}
88
async fn rename(
89
&self,
90
src_path: &str,
91
dest_dir: &dyn WasiDir,
92
dest_path: &str,
93
) -> Result<(), Error> {
94
let dest_dir = dest_dir
95
.as_any()
96
.downcast_ref::<Self>()
97
.ok_or(Error::badf().context("failed downcast to tokio Dir"))?;
98
block_on_dummy_executor(
99
move || async move { self.0.rename_(src_path, &dest_dir.0, dest_path) },
100
)
101
}
102
async fn hard_link(
103
&self,
104
src_path: &str,
105
target_dir: &dyn WasiDir,
106
target_path: &str,
107
) -> Result<(), Error> {
108
let target_dir = target_dir
109
.as_any()
110
.downcast_ref::<Self>()
111
.ok_or(Error::badf().context("failed downcast to tokio Dir"))?;
112
block_on_dummy_executor(move || async move {
113
self.0.hard_link_(src_path, &target_dir.0, target_path)
114
})
115
}
116
async fn set_times(
117
&self,
118
path: &str,
119
atime: Option<crate::SystemTimeSpec>,
120
mtime: Option<crate::SystemTimeSpec>,
121
follow_symlinks: bool,
122
) -> Result<(), Error> {
123
block_on_dummy_executor(move || self.0.set_times(path, atime, mtime, follow_symlinks))
124
}
125
}
126
127
#[cfg(test)]
128
mod test {
129
use super::Dir;
130
use crate::file::{FdFlags, OFlags};
131
use cap_std::ambient_authority;
132
133
#[tokio::test(flavor = "multi_thread")]
134
async fn scratch_dir() {
135
let tempdir = tempfile::Builder::new()
136
.prefix("cap-std-sync")
137
.tempdir()
138
.expect("create temporary dir");
139
let preopen_dir = cap_std::fs::Dir::open_ambient_dir(tempdir.path(), ambient_authority())
140
.expect("open ambient temporary dir");
141
let preopen_dir = Dir::from_cap_std(preopen_dir);
142
crate::WasiDir::open_file(
143
&preopen_dir,
144
false,
145
".",
146
OFlags::empty(),
147
false,
148
false,
149
FdFlags::empty(),
150
)
151
.await
152
.expect("open the same directory via WasiDir abstraction");
153
}
154
155
// Readdir does not work on windows, so we won't test it there.
156
#[cfg(not(windows))]
157
#[tokio::test(flavor = "multi_thread")]
158
async fn readdir() {
159
use crate::dir::{ReaddirCursor, ReaddirEntity, WasiDir};
160
use crate::file::{FdFlags, FileType, OFlags};
161
use std::collections::HashMap;
162
163
async fn readdir_into_map(dir: &dyn WasiDir) -> HashMap<String, ReaddirEntity> {
164
let mut out = HashMap::new();
165
for readdir_result in dir
166
.readdir(ReaddirCursor::from(0))
167
.await
168
.expect("readdir succeeds")
169
{
170
let entity = readdir_result.expect("readdir entry is valid");
171
out.insert(entity.name.clone(), entity);
172
}
173
out
174
}
175
176
let tempdir = tempfile::Builder::new()
177
.prefix("cap-std-sync")
178
.tempdir()
179
.expect("create temporary dir");
180
let preopen_dir = cap_std::fs::Dir::open_ambient_dir(tempdir.path(), ambient_authority())
181
.expect("open ambient temporary dir");
182
let preopen_dir = Dir::from_cap_std(preopen_dir);
183
184
let entities = readdir_into_map(&preopen_dir).await;
185
assert_eq!(
186
entities.len(),
187
2,
188
"should just be . and .. in empty dir: {entities:?}"
189
);
190
assert!(entities.get(".").is_some());
191
assert!(entities.get("..").is_some());
192
193
preopen_dir
194
.open_file(
195
false,
196
"file1",
197
OFlags::CREATE,
198
true,
199
false,
200
FdFlags::empty(),
201
)
202
.await
203
.expect("create file1");
204
205
let entities = readdir_into_map(&preopen_dir).await;
206
assert_eq!(entities.len(), 3, "should be ., .., file1 {entities:?}");
207
assert_eq!(
208
entities.get(".").expect(". entry").filetype,
209
FileType::Directory
210
);
211
assert_eq!(
212
entities.get("..").expect(".. entry").filetype,
213
FileType::Directory
214
);
215
assert_eq!(
216
entities.get("file1").expect("file1 entry").filetype,
217
FileType::RegularFile
218
);
219
}
220
}
221
222