Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wasi/src/p2/host/filesystem.rs
3079 views
1
use crate::filesystem::{Descriptor, WasiFilesystemCtxView};
2
use crate::p2::bindings::clocks::wall_clock;
3
use crate::p2::bindings::filesystem::preopens;
4
use crate::p2::bindings::filesystem::types::{
5
self, ErrorCode, HostDescriptor, HostDirectoryEntryStream,
6
};
7
use crate::p2::filesystem::{FileInputStream, FileOutputStream, ReaddirIterator};
8
use crate::p2::{FsError, FsResult};
9
use crate::{DirPerms, FilePerms};
10
use wasmtime::component::Resource;
11
use wasmtime_wasi_io::streams::{DynInputStream, DynOutputStream};
12
13
mod sync;
14
15
impl preopens::Host for WasiFilesystemCtxView<'_> {
16
fn get_directories(&mut self) -> wasmtime::Result<Vec<(Resource<Descriptor>, String)>> {
17
self.get_directories()
18
}
19
}
20
21
impl types::Host for WasiFilesystemCtxView<'_> {
22
fn convert_error_code(&mut self, err: FsError) -> wasmtime::Result<ErrorCode> {
23
err.downcast()
24
}
25
26
fn filesystem_error_code(
27
&mut self,
28
err: Resource<wasmtime::Error>,
29
) -> wasmtime::Result<Option<ErrorCode>> {
30
let err = self.table.get(&err)?;
31
32
// Currently `err` always comes from the stream implementation which
33
// uses standard reads/writes so only check for `std::io::Error` here.
34
if let Some(err) = err.downcast_ref::<std::io::Error>() {
35
return Ok(Some(ErrorCode::from(err)));
36
}
37
38
Ok(None)
39
}
40
}
41
42
impl HostDescriptor for WasiFilesystemCtxView<'_> {
43
async fn advise(
44
&mut self,
45
fd: Resource<types::Descriptor>,
46
offset: types::Filesize,
47
len: types::Filesize,
48
advice: types::Advice,
49
) -> FsResult<()> {
50
let f = self.table.get(&fd)?.file()?;
51
f.advise(offset, len, advice.into()).await?;
52
Ok(())
53
}
54
55
async fn sync_data(&mut self, fd: Resource<types::Descriptor>) -> FsResult<()> {
56
let descriptor = self.table.get(&fd)?;
57
descriptor.sync_data().await?;
58
Ok(())
59
}
60
61
async fn get_flags(
62
&mut self,
63
fd: Resource<types::Descriptor>,
64
) -> FsResult<types::DescriptorFlags> {
65
let descriptor = self.table.get(&fd)?;
66
let flags = descriptor.get_flags().await?;
67
Ok(flags.into())
68
}
69
70
async fn get_type(
71
&mut self,
72
fd: Resource<types::Descriptor>,
73
) -> FsResult<types::DescriptorType> {
74
let descriptor = self.table.get(&fd)?;
75
let ty = descriptor.get_type().await?;
76
Ok(ty.into())
77
}
78
79
async fn set_size(
80
&mut self,
81
fd: Resource<types::Descriptor>,
82
size: types::Filesize,
83
) -> FsResult<()> {
84
let f = self.table.get(&fd)?.file()?;
85
f.set_size(size).await?;
86
Ok(())
87
}
88
89
async fn set_times(
90
&mut self,
91
fd: Resource<types::Descriptor>,
92
atim: types::NewTimestamp,
93
mtim: types::NewTimestamp,
94
) -> FsResult<()> {
95
let descriptor = self.table.get(&fd)?;
96
let atim = systemtimespec_from(atim)?;
97
let mtim = systemtimespec_from(mtim)?;
98
descriptor.set_times(atim, mtim).await?;
99
Ok(())
100
}
101
102
async fn read(
103
&mut self,
104
fd: Resource<types::Descriptor>,
105
len: types::Filesize,
106
offset: types::Filesize,
107
) -> FsResult<(Vec<u8>, bool)> {
108
use std::io::IoSliceMut;
109
use system_interface::fs::FileIoExt;
110
111
let f = self.table.get(&fd)?.file()?;
112
if !f.perms.contains(FilePerms::READ) {
113
return Err(ErrorCode::NotPermitted.into());
114
}
115
116
let (mut buffer, r) = f
117
.run_blocking(move |f| {
118
let mut buffer = vec![0; len.try_into().unwrap_or(usize::MAX)];
119
let r = f.read_vectored_at(&mut [IoSliceMut::new(&mut buffer)], offset);
120
(buffer, r)
121
})
122
.await;
123
124
let (bytes_read, state) = match r? {
125
0 => (0, true),
126
n => (n, false),
127
};
128
129
buffer.truncate(bytes_read);
130
131
Ok((buffer, state))
132
}
133
134
async fn write(
135
&mut self,
136
fd: Resource<types::Descriptor>,
137
buf: Vec<u8>,
138
offset: types::Filesize,
139
) -> FsResult<types::Filesize> {
140
use std::io::IoSlice;
141
use system_interface::fs::FileIoExt;
142
143
let f = self.table.get(&fd)?.file()?;
144
if !f.perms.contains(FilePerms::WRITE) {
145
return Err(ErrorCode::NotPermitted.into());
146
}
147
148
let bytes_written = f
149
.run_blocking(move |f| f.write_vectored_at(&[IoSlice::new(&buf)], offset))
150
.await?;
151
152
Ok(types::Filesize::try_from(bytes_written).expect("usize fits in Filesize"))
153
}
154
155
async fn read_directory(
156
&mut self,
157
fd: Resource<types::Descriptor>,
158
) -> FsResult<Resource<types::DirectoryEntryStream>> {
159
let d = self.table.get(&fd)?.dir()?;
160
if !d.perms.contains(DirPerms::READ) {
161
return Err(ErrorCode::NotPermitted.into());
162
}
163
164
enum ReaddirError {
165
Io(std::io::Error),
166
IllegalSequence,
167
}
168
impl From<std::io::Error> for ReaddirError {
169
fn from(e: std::io::Error) -> ReaddirError {
170
ReaddirError::Io(e)
171
}
172
}
173
174
let entries = d
175
.run_blocking(|d| {
176
// Both `entries` and `metadata` perform syscalls, which is why they are done
177
// within this `block` call, rather than delay calculating the metadata
178
// for entries when they're demanded later in the iterator chain.
179
Ok::<_, std::io::Error>(
180
d.entries()?
181
.map(|entry| {
182
let entry = entry?;
183
let meta = entry.metadata()?;
184
let type_ = descriptortype_from(meta.file_type());
185
let name = entry
186
.file_name()
187
.into_string()
188
.map_err(|_| ReaddirError::IllegalSequence)?;
189
Ok(types::DirectoryEntry { type_, name })
190
})
191
.collect::<Vec<Result<types::DirectoryEntry, ReaddirError>>>(),
192
)
193
})
194
.await?
195
.into_iter();
196
197
// On windows, filter out files like `C:\DumpStack.log.tmp` which we
198
// can't get full metadata for.
199
#[cfg(windows)]
200
let entries = entries.filter(|entry| {
201
use windows_sys::Win32::Foundation::{ERROR_ACCESS_DENIED, ERROR_SHARING_VIOLATION};
202
if let Err(ReaddirError::Io(err)) = entry {
203
if err.raw_os_error() == Some(ERROR_SHARING_VIOLATION as i32)
204
|| err.raw_os_error() == Some(ERROR_ACCESS_DENIED as i32)
205
{
206
return false;
207
}
208
}
209
true
210
});
211
let entries = entries.map(|r| match r {
212
Ok(r) => Ok(r),
213
Err(ReaddirError::Io(e)) => Err(e.into()),
214
Err(ReaddirError::IllegalSequence) => Err(ErrorCode::IllegalByteSequence.into()),
215
});
216
Ok(self.table.push(ReaddirIterator::new(entries))?)
217
}
218
219
async fn sync(&mut self, fd: Resource<types::Descriptor>) -> FsResult<()> {
220
let descriptor = self.table.get(&fd)?;
221
descriptor.sync().await?;
222
Ok(())
223
}
224
225
async fn create_directory_at(
226
&mut self,
227
fd: Resource<types::Descriptor>,
228
path: String,
229
) -> FsResult<()> {
230
let d = self.table.get(&fd)?.dir()?;
231
d.create_directory_at(path).await?;
232
Ok(())
233
}
234
235
async fn stat(&mut self, fd: Resource<types::Descriptor>) -> FsResult<types::DescriptorStat> {
236
let descriptor = self.table.get(&fd)?;
237
let stat = descriptor.stat().await?;
238
Ok(stat.try_into()?)
239
}
240
241
async fn stat_at(
242
&mut self,
243
fd: Resource<types::Descriptor>,
244
path_flags: types::PathFlags,
245
path: String,
246
) -> FsResult<types::DescriptorStat> {
247
let d = self.table.get(&fd)?.dir()?;
248
let stat = d.stat_at(path_flags.into(), path).await?;
249
Ok(stat.try_into()?)
250
}
251
252
async fn set_times_at(
253
&mut self,
254
fd: Resource<types::Descriptor>,
255
path_flags: types::PathFlags,
256
path: String,
257
atim: types::NewTimestamp,
258
mtim: types::NewTimestamp,
259
) -> FsResult<()> {
260
let d = self.table.get(&fd)?.dir()?;
261
let atim = systemtimespec_from(atim)?;
262
let mtim = systemtimespec_from(mtim)?;
263
d.set_times_at(path_flags.into(), path, atim, mtim).await?;
264
Ok(())
265
}
266
267
async fn link_at(
268
&mut self,
269
fd: Resource<types::Descriptor>,
270
// TODO delete the path flags from this function
271
old_path_flags: types::PathFlags,
272
old_path: String,
273
new_descriptor: Resource<types::Descriptor>,
274
new_path: String,
275
) -> FsResult<()> {
276
let old_dir = self.table.get(&fd)?.dir()?;
277
let new_dir = self.table.get(&new_descriptor)?.dir()?;
278
old_dir
279
.link_at(old_path_flags.into(), old_path, new_dir, new_path)
280
.await?;
281
Ok(())
282
}
283
284
async fn open_at(
285
&mut self,
286
fd: Resource<types::Descriptor>,
287
path_flags: types::PathFlags,
288
path: String,
289
oflags: types::OpenFlags,
290
flags: types::DescriptorFlags,
291
) -> FsResult<Resource<types::Descriptor>> {
292
let d = self.table.get(&fd)?.dir()?;
293
let fd = d
294
.open_at(
295
path_flags.into(),
296
path,
297
oflags.into(),
298
flags.into(),
299
self.ctx.allow_blocking_current_thread,
300
)
301
.await?;
302
let fd = self.table.push(fd)?;
303
Ok(fd)
304
}
305
306
fn drop(&mut self, fd: Resource<types::Descriptor>) -> wasmtime::Result<()> {
307
// The Drop will close the file/dir, but if the close syscall
308
// blocks the thread, I will face god and walk backwards into hell.
309
// tokio::fs::File just uses std::fs::File's Drop impl to close, so
310
// it doesn't appear anyone else has found this to be a problem.
311
// (Not that they could solve it without async drop...)
312
self.table.delete(fd)?;
313
314
Ok(())
315
}
316
317
async fn readlink_at(
318
&mut self,
319
fd: Resource<types::Descriptor>,
320
path: String,
321
) -> FsResult<String> {
322
let d = self.table.get(&fd)?.dir()?;
323
let path = d.readlink_at(path).await?;
324
Ok(path)
325
}
326
327
async fn remove_directory_at(
328
&mut self,
329
fd: Resource<types::Descriptor>,
330
path: String,
331
) -> FsResult<()> {
332
let d = self.table.get(&fd)?.dir()?;
333
d.remove_directory_at(path).await?;
334
Ok(())
335
}
336
337
async fn rename_at(
338
&mut self,
339
fd: Resource<types::Descriptor>,
340
old_path: String,
341
new_fd: Resource<types::Descriptor>,
342
new_path: String,
343
) -> FsResult<()> {
344
let old_dir = self.table.get(&fd)?.dir()?;
345
let new_dir = self.table.get(&new_fd)?.dir()?;
346
old_dir.rename_at(old_path, new_dir, new_path).await?;
347
Ok(())
348
}
349
350
async fn symlink_at(
351
&mut self,
352
fd: Resource<types::Descriptor>,
353
src_path: String,
354
dest_path: String,
355
) -> FsResult<()> {
356
let d = self.table.get(&fd)?.dir()?;
357
d.symlink_at(src_path, dest_path).await?;
358
Ok(())
359
}
360
361
async fn unlink_file_at(
362
&mut self,
363
fd: Resource<types::Descriptor>,
364
path: String,
365
) -> FsResult<()> {
366
let d = self.table.get(&fd)?.dir()?;
367
d.unlink_file_at(path).await?;
368
Ok(())
369
}
370
371
fn read_via_stream(
372
&mut self,
373
fd: Resource<types::Descriptor>,
374
offset: types::Filesize,
375
) -> FsResult<Resource<DynInputStream>> {
376
// Trap if fd lookup fails:
377
let f = self.table.get(&fd)?.file()?;
378
379
if !f.perms.contains(FilePerms::READ) {
380
Err(types::ErrorCode::BadDescriptor)?;
381
}
382
383
// Create a stream view for it.
384
let reader: DynInputStream = Box::new(FileInputStream::new(f, offset));
385
386
// Insert the stream view into the table. Trap if the table is full.
387
let index = self.table.push(reader)?;
388
389
Ok(index)
390
}
391
392
fn write_via_stream(
393
&mut self,
394
fd: Resource<types::Descriptor>,
395
offset: types::Filesize,
396
) -> FsResult<Resource<DynOutputStream>> {
397
// Trap if fd lookup fails:
398
let f = self.table.get(&fd)?.file()?;
399
400
if !f.perms.contains(FilePerms::WRITE) {
401
Err(types::ErrorCode::BadDescriptor)?;
402
}
403
404
// Create a stream view for it.
405
let writer = FileOutputStream::write_at(f, offset);
406
let writer: DynOutputStream = Box::new(writer);
407
408
// Insert the stream view into the table. Trap if the table is full.
409
let index = self.table.push(writer)?;
410
411
Ok(index)
412
}
413
414
fn append_via_stream(
415
&mut self,
416
fd: Resource<types::Descriptor>,
417
) -> FsResult<Resource<DynOutputStream>> {
418
// Trap if fd lookup fails:
419
let f = self.table.get(&fd)?.file()?;
420
421
if !f.perms.contains(FilePerms::WRITE) {
422
Err(types::ErrorCode::BadDescriptor)?;
423
}
424
425
// Create a stream view for it.
426
let appender = FileOutputStream::append(f);
427
let appender: DynOutputStream = Box::new(appender);
428
429
// Insert the stream view into the table. Trap if the table is full.
430
let index = self.table.push(appender)?;
431
432
Ok(index)
433
}
434
435
async fn is_same_object(
436
&mut self,
437
a: Resource<types::Descriptor>,
438
b: Resource<types::Descriptor>,
439
) -> wasmtime::Result<bool> {
440
let descriptor_a = self.table.get(&a)?;
441
let descriptor_b = self.table.get(&b)?;
442
descriptor_a.is_same_object(descriptor_b).await
443
}
444
async fn metadata_hash(
445
&mut self,
446
fd: Resource<types::Descriptor>,
447
) -> FsResult<types::MetadataHashValue> {
448
let fd = self.table.get(&fd)?;
449
let meta = fd.metadata_hash().await?;
450
Ok(meta.into())
451
}
452
async fn metadata_hash_at(
453
&mut self,
454
fd: Resource<types::Descriptor>,
455
path_flags: types::PathFlags,
456
path: String,
457
) -> FsResult<types::MetadataHashValue> {
458
let d = self.table.get(&fd)?.dir()?;
459
let meta = d.metadata_hash_at(path_flags.into(), path).await?;
460
Ok(meta.into())
461
}
462
}
463
464
impl HostDirectoryEntryStream for WasiFilesystemCtxView<'_> {
465
async fn read_directory_entry(
466
&mut self,
467
stream: Resource<types::DirectoryEntryStream>,
468
) -> FsResult<Option<types::DirectoryEntry>> {
469
let readdir = self.table.get(&stream)?;
470
readdir.next()
471
}
472
473
fn drop(&mut self, stream: Resource<types::DirectoryEntryStream>) -> wasmtime::Result<()> {
474
self.table.delete(stream)?;
475
Ok(())
476
}
477
}
478
479
impl From<types::Advice> for system_interface::fs::Advice {
480
fn from(advice: types::Advice) -> Self {
481
match advice {
482
types::Advice::Normal => Self::Normal,
483
types::Advice::Sequential => Self::Sequential,
484
types::Advice::Random => Self::Random,
485
types::Advice::WillNeed => Self::WillNeed,
486
types::Advice::DontNeed => Self::DontNeed,
487
types::Advice::NoReuse => Self::NoReuse,
488
}
489
}
490
}
491
492
impl From<types::OpenFlags> for crate::filesystem::OpenFlags {
493
fn from(flags: types::OpenFlags) -> Self {
494
let mut out = Self::empty();
495
if flags.contains(types::OpenFlags::CREATE) {
496
out |= Self::CREATE;
497
}
498
if flags.contains(types::OpenFlags::DIRECTORY) {
499
out |= Self::DIRECTORY;
500
}
501
if flags.contains(types::OpenFlags::EXCLUSIVE) {
502
out |= Self::EXCLUSIVE;
503
}
504
if flags.contains(types::OpenFlags::TRUNCATE) {
505
out |= Self::TRUNCATE;
506
}
507
out
508
}
509
}
510
511
impl From<types::PathFlags> for crate::filesystem::PathFlags {
512
fn from(flags: types::PathFlags) -> Self {
513
let mut out = Self::empty();
514
if flags.contains(types::PathFlags::SYMLINK_FOLLOW) {
515
out |= Self::SYMLINK_FOLLOW;
516
}
517
out
518
}
519
}
520
521
impl From<crate::filesystem::DescriptorFlags> for types::DescriptorFlags {
522
fn from(flags: crate::filesystem::DescriptorFlags) -> Self {
523
let mut out = Self::empty();
524
if flags.contains(crate::filesystem::DescriptorFlags::READ) {
525
out |= Self::READ;
526
}
527
if flags.contains(crate::filesystem::DescriptorFlags::WRITE) {
528
out |= Self::WRITE;
529
}
530
if flags.contains(crate::filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) {
531
out |= Self::FILE_INTEGRITY_SYNC;
532
}
533
if flags.contains(crate::filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) {
534
out |= Self::DATA_INTEGRITY_SYNC;
535
}
536
if flags.contains(crate::filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) {
537
out |= Self::REQUESTED_WRITE_SYNC;
538
}
539
if flags.contains(crate::filesystem::DescriptorFlags::MUTATE_DIRECTORY) {
540
out |= Self::MUTATE_DIRECTORY;
541
}
542
out
543
}
544
}
545
546
impl From<types::DescriptorFlags> for crate::filesystem::DescriptorFlags {
547
fn from(flags: types::DescriptorFlags) -> Self {
548
let mut out = Self::empty();
549
if flags.contains(types::DescriptorFlags::READ) {
550
out |= Self::READ;
551
}
552
if flags.contains(types::DescriptorFlags::WRITE) {
553
out |= Self::WRITE;
554
}
555
if flags.contains(types::DescriptorFlags::FILE_INTEGRITY_SYNC) {
556
out |= Self::FILE_INTEGRITY_SYNC;
557
}
558
if flags.contains(types::DescriptorFlags::DATA_INTEGRITY_SYNC) {
559
out |= Self::DATA_INTEGRITY_SYNC;
560
}
561
if flags.contains(types::DescriptorFlags::REQUESTED_WRITE_SYNC) {
562
out |= Self::REQUESTED_WRITE_SYNC;
563
}
564
if flags.contains(types::DescriptorFlags::MUTATE_DIRECTORY) {
565
out |= Self::MUTATE_DIRECTORY;
566
}
567
out
568
}
569
}
570
571
impl From<crate::filesystem::MetadataHashValue> for types::MetadataHashValue {
572
fn from(
573
crate::filesystem::MetadataHashValue { lower, upper }: crate::filesystem::MetadataHashValue,
574
) -> Self {
575
Self { lower, upper }
576
}
577
}
578
579
impl TryFrom<crate::filesystem::DescriptorStat> for types::DescriptorStat {
580
type Error = ErrorCode;
581
582
fn try_from(
583
crate::filesystem::DescriptorStat {
584
type_,
585
link_count,
586
size,
587
data_access_timestamp,
588
data_modification_timestamp,
589
status_change_timestamp,
590
}: crate::filesystem::DescriptorStat,
591
) -> Result<Self, ErrorCode> {
592
Ok(Self {
593
type_: type_.into(),
594
link_count,
595
size,
596
data_access_timestamp: data_access_timestamp.map(|t| t.try_into()).transpose()?,
597
data_modification_timestamp: data_modification_timestamp
598
.map(|t| t.try_into())
599
.transpose()?,
600
status_change_timestamp: status_change_timestamp.map(|t| t.try_into()).transpose()?,
601
})
602
}
603
}
604
605
impl From<crate::filesystem::DescriptorType> for types::DescriptorType {
606
fn from(ty: crate::filesystem::DescriptorType) -> Self {
607
match ty {
608
crate::filesystem::DescriptorType::Unknown => Self::Unknown,
609
crate::filesystem::DescriptorType::BlockDevice => Self::BlockDevice,
610
crate::filesystem::DescriptorType::CharacterDevice => Self::CharacterDevice,
611
crate::filesystem::DescriptorType::Directory => Self::Directory,
612
crate::filesystem::DescriptorType::SymbolicLink => Self::SymbolicLink,
613
crate::filesystem::DescriptorType::RegularFile => Self::RegularFile,
614
}
615
}
616
}
617
618
#[cfg(unix)]
619
fn from_raw_os_error(err: Option<i32>) -> Option<ErrorCode> {
620
use rustix::io::Errno as RustixErrno;
621
if err.is_none() {
622
return None;
623
}
624
Some(match RustixErrno::from_raw_os_error(err.unwrap()) {
625
RustixErrno::PIPE => ErrorCode::Pipe,
626
RustixErrno::PERM => ErrorCode::NotPermitted,
627
RustixErrno::NOENT => ErrorCode::NoEntry,
628
RustixErrno::NOMEM => ErrorCode::InsufficientMemory,
629
RustixErrno::IO => ErrorCode::Io,
630
RustixErrno::BADF => ErrorCode::BadDescriptor,
631
RustixErrno::BUSY => ErrorCode::Busy,
632
RustixErrno::ACCESS => ErrorCode::Access,
633
RustixErrno::NOTDIR => ErrorCode::NotDirectory,
634
RustixErrno::ISDIR => ErrorCode::IsDirectory,
635
RustixErrno::INVAL => ErrorCode::Invalid,
636
RustixErrno::EXIST => ErrorCode::Exist,
637
RustixErrno::FBIG => ErrorCode::FileTooLarge,
638
RustixErrno::NOSPC => ErrorCode::InsufficientSpace,
639
RustixErrno::SPIPE => ErrorCode::InvalidSeek,
640
RustixErrno::MLINK => ErrorCode::TooManyLinks,
641
RustixErrno::NAMETOOLONG => ErrorCode::NameTooLong,
642
RustixErrno::NOTEMPTY => ErrorCode::NotEmpty,
643
RustixErrno::LOOP => ErrorCode::Loop,
644
RustixErrno::OVERFLOW => ErrorCode::Overflow,
645
RustixErrno::ILSEQ => ErrorCode::IllegalByteSequence,
646
RustixErrno::NOTSUP => ErrorCode::Unsupported,
647
RustixErrno::ALREADY => ErrorCode::Already,
648
RustixErrno::INPROGRESS => ErrorCode::InProgress,
649
RustixErrno::INTR => ErrorCode::Interrupted,
650
651
#[allow(
652
unreachable_patterns,
653
reason = "on some platforms, these have the same value as other errno values"
654
)]
655
RustixErrno::OPNOTSUPP => ErrorCode::Unsupported,
656
657
_ => return None,
658
})
659
}
660
#[cfg(windows)]
661
fn from_raw_os_error(raw_os_error: Option<i32>) -> Option<ErrorCode> {
662
use windows_sys::Win32::Foundation;
663
Some(match raw_os_error.map(|code| code as u32) {
664
Some(Foundation::ERROR_FILE_NOT_FOUND) => ErrorCode::NoEntry,
665
Some(Foundation::ERROR_PATH_NOT_FOUND) => ErrorCode::NoEntry,
666
Some(Foundation::ERROR_ACCESS_DENIED) => ErrorCode::Access,
667
Some(Foundation::ERROR_SHARING_VIOLATION) => ErrorCode::Access,
668
Some(Foundation::ERROR_PRIVILEGE_NOT_HELD) => ErrorCode::NotPermitted,
669
Some(Foundation::ERROR_INVALID_HANDLE) => ErrorCode::BadDescriptor,
670
Some(Foundation::ERROR_INVALID_NAME) => ErrorCode::NoEntry,
671
Some(Foundation::ERROR_NOT_ENOUGH_MEMORY) => ErrorCode::InsufficientMemory,
672
Some(Foundation::ERROR_OUTOFMEMORY) => ErrorCode::InsufficientMemory,
673
Some(Foundation::ERROR_DIR_NOT_EMPTY) => ErrorCode::NotEmpty,
674
Some(Foundation::ERROR_NOT_READY) => ErrorCode::Busy,
675
Some(Foundation::ERROR_BUSY) => ErrorCode::Busy,
676
Some(Foundation::ERROR_NOT_SUPPORTED) => ErrorCode::Unsupported,
677
Some(Foundation::ERROR_FILE_EXISTS) => ErrorCode::Exist,
678
Some(Foundation::ERROR_BROKEN_PIPE) => ErrorCode::Pipe,
679
Some(Foundation::ERROR_BUFFER_OVERFLOW) => ErrorCode::NameTooLong,
680
Some(Foundation::ERROR_NOT_A_REPARSE_POINT) => ErrorCode::Invalid,
681
Some(Foundation::ERROR_NEGATIVE_SEEK) => ErrorCode::Invalid,
682
Some(Foundation::ERROR_DIRECTORY) => ErrorCode::NotDirectory,
683
Some(Foundation::ERROR_ALREADY_EXISTS) => ErrorCode::Exist,
684
Some(Foundation::ERROR_STOPPED_ON_SYMLINK) => ErrorCode::Loop,
685
Some(Foundation::ERROR_DIRECTORY_NOT_SUPPORTED) => ErrorCode::IsDirectory,
686
_ => return None,
687
})
688
}
689
690
impl From<std::io::Error> for ErrorCode {
691
fn from(err: std::io::Error) -> ErrorCode {
692
ErrorCode::from(&err)
693
}
694
}
695
696
impl<'a> From<&'a std::io::Error> for ErrorCode {
697
fn from(err: &'a std::io::Error) -> ErrorCode {
698
match from_raw_os_error(err.raw_os_error()) {
699
Some(errno) => errno,
700
None => {
701
tracing::debug!("unknown raw os error: {err}");
702
match err.kind() {
703
std::io::ErrorKind::NotFound => ErrorCode::NoEntry,
704
std::io::ErrorKind::PermissionDenied => ErrorCode::NotPermitted,
705
std::io::ErrorKind::AlreadyExists => ErrorCode::Exist,
706
std::io::ErrorKind::InvalidInput => ErrorCode::Invalid,
707
_ => ErrorCode::Io,
708
}
709
}
710
}
711
}
712
}
713
714
impl From<cap_rand::Error> for ErrorCode {
715
fn from(err: cap_rand::Error) -> ErrorCode {
716
// I picked Error::Io as a 'reasonable default', FIXME dan is this ok?
717
from_raw_os_error(err.raw_os_error()).unwrap_or(ErrorCode::Io)
718
}
719
}
720
721
impl From<std::num::TryFromIntError> for ErrorCode {
722
fn from(_err: std::num::TryFromIntError) -> ErrorCode {
723
ErrorCode::Overflow
724
}
725
}
726
727
fn descriptortype_from(ft: cap_std::fs::FileType) -> types::DescriptorType {
728
use cap_fs_ext::FileTypeExt;
729
use types::DescriptorType;
730
if ft.is_dir() {
731
DescriptorType::Directory
732
} else if ft.is_symlink() {
733
DescriptorType::SymbolicLink
734
} else if ft.is_block_device() {
735
DescriptorType::BlockDevice
736
} else if ft.is_char_device() {
737
DescriptorType::CharacterDevice
738
} else if ft.is_file() {
739
DescriptorType::RegularFile
740
} else {
741
DescriptorType::Unknown
742
}
743
}
744
745
fn systemtime_from(t: wall_clock::Datetime) -> Result<std::time::SystemTime, ErrorCode> {
746
std::time::SystemTime::UNIX_EPOCH
747
.checked_add(core::time::Duration::new(t.seconds, t.nanoseconds))
748
.ok_or(ErrorCode::Overflow)
749
}
750
751
fn systemtimespec_from(
752
t: types::NewTimestamp,
753
) -> Result<Option<fs_set_times::SystemTimeSpec>, ErrorCode> {
754
use fs_set_times::SystemTimeSpec;
755
match t {
756
types::NewTimestamp::NoChange => Ok(None),
757
types::NewTimestamp::Now => Ok(Some(SystemTimeSpec::SymbolicNow)),
758
types::NewTimestamp::Timestamp(st) => {
759
let st = systemtime_from(st)?;
760
Ok(Some(SystemTimeSpec::Absolute(st)))
761
}
762
}
763
}
764
765
impl From<crate::clocks::DatetimeError> for ErrorCode {
766
fn from(_: crate::clocks::DatetimeError) -> ErrorCode {
767
ErrorCode::Overflow
768
}
769
}
770
771
#[cfg(test)]
772
mod test {
773
use super::*;
774
use wasmtime::component::ResourceTable;
775
776
#[test]
777
fn table_readdir_works() {
778
let mut table = ResourceTable::new();
779
let ix = table
780
.push(ReaddirIterator::new(std::iter::empty()))
781
.unwrap();
782
let _ = table.get(&ix).unwrap();
783
table.delete(ix).unwrap();
784
}
785
}
786
787