1 #![allow(unused_variables)] // TODO: remove this when more things are implemented 2 3 use crate::bindings::{ 4 exit, filesystem, monotonic_clock, network, poll, random, streams, wall_clock, 5 }; 6 use core::cell::{Cell, RefCell, RefMut, UnsafeCell}; 7 use core::cmp::min; 8 use core::ffi::c_void; 9 use core::hint::black_box; 10 use core::mem::{self, align_of, forget, size_of, ManuallyDrop, MaybeUninit}; 11 use core::ops::{Deref, DerefMut}; 12 use core::ptr::{self, null_mut}; 13 use core::slice; 14 use poll::Pollable; 15 use wasi::*; 16 17 #[cfg(all(feature = "command", feature = "reactor"))] 18 compile_error!("only one of the `command` and `reactor` features may be selected at a time"); 19 20 #[macro_use] 21 mod macros; 22 23 mod descriptors; 24 use crate::descriptors::{Descriptor, Descriptors, StreamType, Streams}; 25 26 pub mod bindings { 27 #[cfg(feature = "command")] 28 wit_bindgen::generate!({ 29 world: "command", 30 std_feature, 31 raw_strings, 32 // The generated definition of command will pull in std, so we are defining it 33 // manually below instead 34 skip: ["run", "get-directories", "get-environment"], 35 }); 36 37 #[cfg(feature = "reactor")] 38 wit_bindgen::generate!({ 39 world: "reactor", 40 std_feature, 41 raw_strings, 42 skip: ["get-directories", "get-environment"], 43 }); 44 } 45 46 #[no_mangle] 47 #[cfg(feature = "command")] 48 pub unsafe extern "C" fn run() -> u32 { 49 #[link(wasm_import_module = "__main_module__")] 50 extern "C" { 51 fn _start(); 52 } 53 _start(); 54 0 55 } 56 57 // The unwrap/expect methods in std pull panic when they fail, which pulls 58 // in unwinding machinery that we can't use in the adapter. Instead, use this 59 // extension trait to get postfixed upwrap on Option and Result. 60 trait TrappingUnwrap<T> { 61 fn trapping_unwrap(self) -> T; 62 } 63 64 impl<T> TrappingUnwrap<T> for Option<T> { 65 fn trapping_unwrap(self) -> T { 66 match self { 67 Some(t) => t, 68 None => unreachable!(), 69 } 70 } 71 } 72 73 impl<T, E> TrappingUnwrap<T> for Result<T, E> { 74 fn trapping_unwrap(self) -> T { 75 match self { 76 Ok(t) => t, 77 Err(_) => unreachable!(), 78 } 79 } 80 } 81 82 #[no_mangle] 83 pub unsafe extern "C" fn cabi_import_realloc( 84 old_ptr: *mut u8, 85 old_size: usize, 86 align: usize, 87 new_size: usize, 88 ) -> *mut u8 { 89 if !old_ptr.is_null() || old_size != 0 { 90 unreachable!(); 91 } 92 let mut ptr = null_mut::<u8>(); 93 State::with(|state| { 94 ptr = state.import_alloc.alloc(align, new_size); 95 Ok(()) 96 }); 97 ptr 98 } 99 100 /// Bump-allocated memory arena. This is a singleton - the 101 /// memory will be sized according to `bump_arena_size()`. 102 pub struct BumpArena { 103 data: MaybeUninit<[u8; bump_arena_size()]>, 104 position: Cell<usize>, 105 } 106 107 impl BumpArena { 108 fn new() -> Self { 109 BumpArena { 110 data: MaybeUninit::uninit(), 111 position: Cell::new(0), 112 } 113 } 114 fn alloc(&self, align: usize, size: usize) -> *mut u8 { 115 let start = self.data.as_ptr() as usize; 116 let next = start + self.position.get(); 117 let alloc = align_to(next, align); 118 let offset = alloc - start; 119 if offset + size > bump_arena_size() { 120 unreachable!("out of memory"); 121 } 122 self.position.set(offset + size); 123 alloc as *mut u8 124 } 125 } 126 fn align_to(ptr: usize, align: usize) -> usize { 127 (ptr + (align - 1)) & !(align - 1) 128 } 129 130 // Invariant: buffer not-null and arena is-some are never true at the same 131 // time. We did not use an enum to make this invalid behavior unrepresentable 132 // because we can't use RefCell to borrow() the variants of the enum - only 133 // Cell provides mutability without pulling in panic machinery - so it would 134 // make the accessors a lot more awkward to write. 135 pub struct ImportAlloc { 136 // When not-null, allocator should use this buffer/len pair at most once 137 // to satisfy allocations. 138 buffer: Cell<*mut u8>, 139 len: Cell<usize>, 140 // When not-empty, allocator should use this arena to satisfy allocations. 141 arena: Cell<Option<&'static BumpArena>>, 142 } 143 144 impl ImportAlloc { 145 fn new() -> Self { 146 ImportAlloc { 147 buffer: Cell::new(std::ptr::null_mut()), 148 len: Cell::new(0), 149 arena: Cell::new(None), 150 } 151 } 152 153 /// Expect at most one import allocation during execution of the provided closure. 154 /// Use the provided buffer to satisfy that import allocation. The user is responsible 155 /// for making sure allocated imports are not used beyond the lifetime of the buffer. 156 fn with_buffer<T>(&self, buffer: *mut u8, len: usize, f: impl FnOnce() -> T) -> T { 157 if self.arena.get().is_some() { 158 unreachable!("arena mode") 159 } 160 let prev = self.buffer.replace(buffer); 161 if !prev.is_null() { 162 unreachable!("overwrote another buffer") 163 } 164 self.len.set(len); 165 let r = f(); 166 self.buffer.set(std::ptr::null_mut()); 167 r 168 } 169 170 /// Permit many import allocations during execution of the provided closure. 171 /// Use the provided BumpArena to satisfry those allocations. The user is responsible 172 /// for making sure allocated imports are not used beyond the lifetime of the arena. 173 fn with_arena<T>(&self, arena: &BumpArena, f: impl FnOnce() -> T) -> T { 174 if !self.buffer.get().is_null() { 175 unreachable!("buffer mode") 176 } 177 let prev = self.arena.replace(Some(unsafe { 178 // Safety: Need to erase the lifetime to store in the arena cell. 179 std::mem::transmute::<&'_ BumpArena, &'static BumpArena>(arena) 180 })); 181 if prev.is_some() { 182 unreachable!("overwrote another arena") 183 } 184 let r = f(); 185 self.arena.set(None); 186 r 187 } 188 189 /// To be used by cabi_import_realloc only! 190 fn alloc(&self, align: usize, size: usize) -> *mut u8 { 191 if let Some(arena) = self.arena.get() { 192 arena.alloc(align, size) 193 } else { 194 let buffer = self.buffer.get(); 195 if buffer.is_null() { 196 unreachable!("buffer not provided, or already used") 197 } 198 let buffer = buffer as usize; 199 let alloc = align_to(buffer, align); 200 if alloc.checked_add(size).trapping_unwrap() 201 > buffer.checked_add(self.len.get()).trapping_unwrap() 202 { 203 unreachable!("out of memory") 204 } 205 self.buffer.set(std::ptr::null_mut()); 206 alloc as *mut u8 207 } 208 } 209 } 210 211 /// This allocator is only used for the `run` entrypoint. 212 /// 213 /// The implementation here is a bump allocator into `State::long_lived_arena` which 214 /// traps when it runs out of data. This means that the total size of 215 /// arguments/env/etc coming into a component is bounded by the current 64k 216 /// (ish) limit. That's just an implementation limit though which can be lifted 217 /// by dynamically calling the main module's allocator as necessary for more data. 218 #[no_mangle] 219 pub unsafe extern "C" fn cabi_export_realloc( 220 old_ptr: *mut u8, 221 old_size: usize, 222 align: usize, 223 new_size: usize, 224 ) -> *mut u8 { 225 if !old_ptr.is_null() || old_size != 0 { 226 unreachable!(); 227 } 228 let mut ret = null_mut::<u8>(); 229 State::with_mut(|state| { 230 ret = state.long_lived_arena.alloc(align, new_size); 231 Ok(()) 232 }); 233 ret 234 } 235 236 /// Read command-line argument data. 237 /// The size of the array should match that returned by `args_sizes_get` 238 #[no_mangle] 239 pub unsafe extern "C" fn args_get(mut argv: *mut *mut u8, mut argv_buf: *mut u8) -> Errno { 240 State::with(|state| { 241 for arg in state.get_args() { 242 // Copy the argument into `argv_buf` which must be sized 243 // appropriately by the caller. 244 ptr::copy_nonoverlapping(arg.ptr, argv_buf, arg.len); 245 *argv_buf.add(arg.len) = 0; 246 247 // Copy the argument pointer into the `argv` buf 248 *argv = argv_buf; 249 250 // Update our pointers past what's written to prepare for the 251 // next argument. 252 argv = argv.add(1); 253 argv_buf = argv_buf.add(arg.len + 1); 254 } 255 Ok(()) 256 }) 257 } 258 259 /// Return command-line argument data sizes. 260 #[no_mangle] 261 pub unsafe extern "C" fn args_sizes_get(argc: *mut Size, argv_buf_size: *mut Size) -> Errno { 262 State::with(|state| { 263 let args = state.get_args(); 264 *argc = args.len(); 265 // Add one to each length for the terminating nul byte added by 266 // the `args_get` function. 267 *argv_buf_size = args.iter().map(|s| s.len + 1).sum(); 268 Ok(()) 269 }) 270 } 271 272 /// Read environment variable data. 273 /// The sizes of the buffers should match that returned by `environ_sizes_get`. 274 #[no_mangle] 275 pub unsafe extern "C" fn environ_get(environ: *mut *mut u8, environ_buf: *mut u8) -> Errno { 276 State::with(|state| { 277 let mut offsets = environ; 278 let mut buffer = environ_buf; 279 for var in state.get_environment() { 280 ptr::write(offsets, buffer); 281 offsets = offsets.add(1); 282 283 ptr::copy_nonoverlapping(var.key.ptr, buffer, var.key.len); 284 buffer = buffer.add(var.key.len); 285 286 ptr::write(buffer, b'='); 287 buffer = buffer.add(1); 288 289 ptr::copy_nonoverlapping(var.value.ptr, buffer, var.value.len); 290 buffer = buffer.add(var.value.len); 291 292 ptr::write(buffer, 0); 293 buffer = buffer.add(1); 294 } 295 296 Ok(()) 297 }) 298 } 299 300 /// Return environment variable data sizes. 301 #[no_mangle] 302 pub unsafe extern "C" fn environ_sizes_get( 303 environc: *mut Size, 304 environ_buf_size: *mut Size, 305 ) -> Errno { 306 if matches!( 307 get_allocation_state(), 308 AllocationState::StackAllocated | AllocationState::StateAllocated 309 ) { 310 State::with(|state| { 311 let vars = state.get_environment(); 312 *environc = vars.len(); 313 *environ_buf_size = { 314 let mut sum = 0; 315 for var in vars { 316 sum += var.key.len + var.value.len + 2; 317 } 318 sum 319 }; 320 321 Ok(()) 322 }) 323 } else { 324 *environc = 0; 325 *environ_buf_size = 0; 326 ERRNO_SUCCESS 327 } 328 } 329 330 /// Return the resolution of a clock. 331 /// Implementations are required to provide a non-zero value for supported clocks. For unsupported clocks, 332 /// return `errno::inval`. 333 /// Note: This is similar to `clock_getres` in POSIX. 334 #[no_mangle] 335 pub extern "C" fn clock_res_get(id: Clockid, resolution: &mut Timestamp) -> Errno { 336 State::with(|state| { 337 match id { 338 CLOCKID_MONOTONIC => { 339 let res = monotonic_clock::resolution(); 340 *resolution = res; 341 } 342 CLOCKID_REALTIME => { 343 let res = wall_clock::resolution(); 344 *resolution = Timestamp::from(res.seconds) 345 .checked_mul(1_000_000_000) 346 .and_then(|ns| ns.checked_add(res.nanoseconds.into())) 347 .ok_or(ERRNO_OVERFLOW)?; 348 } 349 _ => unreachable!(), 350 } 351 Ok(()) 352 }) 353 } 354 355 /// Return the time value of a clock. 356 /// Note: This is similar to `clock_gettime` in POSIX. 357 #[no_mangle] 358 pub unsafe extern "C" fn clock_time_get( 359 id: Clockid, 360 _precision: Timestamp, 361 time: &mut Timestamp, 362 ) -> Errno { 363 if matches!( 364 get_allocation_state(), 365 AllocationState::StackAllocated | AllocationState::StateAllocated 366 ) { 367 State::with(|state| { 368 match id { 369 CLOCKID_MONOTONIC => { 370 *time = monotonic_clock::now(); 371 } 372 CLOCKID_REALTIME => { 373 let res = wall_clock::now(); 374 *time = Timestamp::from(res.seconds) 375 .checked_mul(1_000_000_000) 376 .and_then(|ns| ns.checked_add(res.nanoseconds.into())) 377 .ok_or(ERRNO_OVERFLOW)?; 378 } 379 _ => unreachable!(), 380 } 381 Ok(()) 382 }) 383 } else { 384 *time = Timestamp::from(0u64); 385 ERRNO_SUCCESS 386 } 387 } 388 389 /// Provide file advisory information on a file descriptor. 390 /// Note: This is similar to `posix_fadvise` in POSIX. 391 #[no_mangle] 392 pub unsafe extern "C" fn fd_advise( 393 fd: Fd, 394 offset: Filesize, 395 len: Filesize, 396 advice: Advice, 397 ) -> Errno { 398 let advice = match advice { 399 ADVICE_NORMAL => filesystem::Advice::Normal, 400 ADVICE_SEQUENTIAL => filesystem::Advice::Sequential, 401 ADVICE_RANDOM => filesystem::Advice::Random, 402 ADVICE_WILLNEED => filesystem::Advice::WillNeed, 403 ADVICE_DONTNEED => filesystem::Advice::DontNeed, 404 ADVICE_NOREUSE => filesystem::Advice::NoReuse, 405 _ => return ERRNO_INVAL, 406 }; 407 State::with(|state| { 408 let ds = state.descriptors(); 409 let file = ds.get_seekable_file(fd)?; 410 filesystem::advise(file.fd, offset, len, advice)?; 411 Ok(()) 412 }) 413 } 414 415 /// Force the allocation of space in a file. 416 /// Note: This is similar to `posix_fallocate` in POSIX. 417 #[no_mangle] 418 pub unsafe extern "C" fn fd_allocate(fd: Fd, offset: Filesize, len: Filesize) -> Errno { 419 State::with(|state| { 420 let ds = state.descriptors(); 421 // For not-files, fail with BADF 422 let file = ds.get_file(fd)?; 423 // For all files, fail with NOTSUP, because this call does not exist in preview 2. 424 Err(wasi::ERRNO_NOTSUP) 425 }) 426 } 427 428 /// Close a file descriptor. 429 /// Note: This is similar to `close` in POSIX. 430 #[no_mangle] 431 pub unsafe extern "C" fn fd_close(fd: Fd) -> Errno { 432 State::with_mut(|state| { 433 // If there's a dirent cache entry for this file descriptor then drop 434 // it since the descriptor is being closed and future calls to 435 // `fd_readdir` should return an error. 436 if fd == state.dirent_cache.for_fd.get() { 437 drop(state.dirent_cache.stream.replace(None)); 438 } 439 440 let desc = state.descriptors_mut().close(fd)?; 441 Ok(()) 442 }) 443 } 444 445 /// Synchronize the data of a file to disk. 446 /// Note: This is similar to `fdatasync` in POSIX. 447 #[no_mangle] 448 pub unsafe extern "C" fn fd_datasync(fd: Fd) -> Errno { 449 State::with(|state| { 450 let ds = state.descriptors(); 451 let file = ds.get_file(fd)?; 452 filesystem::sync_data(file.fd)?; 453 Ok(()) 454 }) 455 } 456 457 /// Get the attributes of a file descriptor. 458 /// Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields. 459 #[no_mangle] 460 pub unsafe extern "C" fn fd_fdstat_get(fd: Fd, stat: *mut Fdstat) -> Errno { 461 State::with(|state| match state.descriptors().get(fd)? { 462 Descriptor::Streams(Streams { 463 type_: StreamType::File(file), 464 .. 465 }) => { 466 let flags = filesystem::get_flags(file.fd)?; 467 let type_ = filesystem::get_type(file.fd)?; 468 469 let fs_filetype = type_.into(); 470 471 let mut fs_flags = 0; 472 let mut fs_rights_base = !0; 473 if !flags.contains(filesystem::DescriptorFlags::READ) { 474 fs_rights_base &= !RIGHTS_FD_READ; 475 } 476 if !flags.contains(filesystem::DescriptorFlags::WRITE) { 477 fs_rights_base &= !RIGHTS_FD_WRITE; 478 } 479 if flags.contains(filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) { 480 fs_flags |= FDFLAGS_DSYNC; 481 } 482 if flags.contains(filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) { 483 fs_flags |= FDFLAGS_RSYNC; 484 } 485 if flags.contains(filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) { 486 fs_flags |= FDFLAGS_SYNC; 487 } 488 if file.append { 489 fs_flags |= FDFLAGS_APPEND; 490 } 491 if !file.blocking { 492 fs_flags |= FDFLAGS_NONBLOCK; 493 } 494 let fs_rights_inheriting = fs_rights_base; 495 496 stat.write(Fdstat { 497 fs_filetype, 498 fs_flags, 499 fs_rights_base, 500 fs_rights_inheriting, 501 }); 502 Ok(()) 503 } 504 Descriptor::Streams(Streams { 505 input, 506 output, 507 type_: StreamType::Socket(_), 508 }) 509 | Descriptor::Streams(Streams { 510 input, 511 output, 512 type_: StreamType::Stdio, 513 }) => { 514 let fs_filetype = FILETYPE_CHARACTER_DEVICE; 515 let fs_flags = 0; 516 let mut fs_rights_base = 0; 517 if input.get().is_some() { 518 fs_rights_base |= RIGHTS_FD_READ; 519 } 520 if output.get().is_some() { 521 fs_rights_base |= RIGHTS_FD_WRITE; 522 } 523 let fs_rights_inheriting = fs_rights_base; 524 stat.write(Fdstat { 525 fs_filetype, 526 fs_flags, 527 fs_rights_base, 528 fs_rights_inheriting, 529 }); 530 Ok(()) 531 } 532 Descriptor::Closed(_) => Err(ERRNO_BADF), 533 }) 534 } 535 536 /// Adjust the flags associated with a file descriptor. 537 /// Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. 538 #[no_mangle] 539 pub unsafe extern "C" fn fd_fdstat_set_flags(fd: Fd, flags: Fdflags) -> Errno { 540 // Only support changing the NONBLOCK or APPEND flags. 541 if flags & !(FDFLAGS_NONBLOCK | FDFLAGS_APPEND) != 0 { 542 return wasi::ERRNO_INVAL; 543 } 544 545 State::with_mut(|state| { 546 let mut ds = state.descriptors_mut(); 547 let file = match ds.get_mut(fd)? { 548 Descriptor::Streams(Streams { 549 type_: StreamType::File(file), 550 .. 551 }) if !file.is_dir() => file, 552 _ => Err(wasi::ERRNO_BADF)?, 553 }; 554 file.append = flags & FDFLAGS_APPEND == FDFLAGS_APPEND; 555 file.blocking = !(flags & FDFLAGS_NONBLOCK == FDFLAGS_NONBLOCK); 556 Ok(()) 557 }) 558 } 559 560 /// Adjust the rights associated with a file descriptor. 561 /// This can only be used to remove rights, and returns `errno::notcapable` if called in a way that would attempt to add rights 562 #[no_mangle] 563 pub unsafe extern "C" fn fd_fdstat_set_rights( 564 fd: Fd, 565 fs_rights_base: Rights, 566 fs_rights_inheriting: Rights, 567 ) -> Errno { 568 unreachable!() 569 } 570 571 /// Return the attributes of an open file. 572 #[no_mangle] 573 pub unsafe extern "C" fn fd_filestat_get(fd: Fd, buf: *mut Filestat) -> Errno { 574 State::with(|state| { 575 let ds = state.descriptors(); 576 match ds.get(fd)? { 577 Descriptor::Streams(Streams { 578 type_: StreamType::File(file), 579 .. 580 }) => { 581 let stat = filesystem::stat(file.fd)?; 582 let filetype = stat.type_.into(); 583 *buf = Filestat { 584 dev: stat.device, 585 ino: stat.inode, 586 filetype, 587 nlink: stat.link_count, 588 size: stat.size, 589 atim: datetime_to_timestamp(stat.data_access_timestamp), 590 mtim: datetime_to_timestamp(stat.data_modification_timestamp), 591 ctim: datetime_to_timestamp(stat.status_change_timestamp), 592 }; 593 Ok(()) 594 } 595 // Stdio is all zero fields, except for filetype character device 596 Descriptor::Streams(Streams { 597 type_: StreamType::Stdio, 598 .. 599 }) => { 600 *buf = Filestat { 601 dev: 0, 602 ino: 0, 603 filetype: FILETYPE_CHARACTER_DEVICE, 604 nlink: 0, 605 size: 0, 606 atim: 0, 607 mtim: 0, 608 ctim: 0, 609 }; 610 Ok(()) 611 } 612 _ => Err(wasi::ERRNO_BADF), 613 } 614 }) 615 } 616 617 /// Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros. 618 /// Note: This is similar to `ftruncate` in POSIX. 619 #[no_mangle] 620 pub unsafe extern "C" fn fd_filestat_set_size(fd: Fd, size: Filesize) -> Errno { 621 State::with(|state| { 622 let ds = state.descriptors(); 623 let file = ds.get_file(fd)?; 624 filesystem::set_size(file.fd, size)?; 625 Ok(()) 626 }) 627 } 628 629 fn systimespec(set: bool, ts: Timestamp, now: bool) -> Result<filesystem::NewTimestamp, Errno> { 630 if set && now { 631 Err(wasi::ERRNO_INVAL) 632 } else if set { 633 Ok(filesystem::NewTimestamp::Timestamp(filesystem::Datetime { 634 seconds: ts / 1_000_000_000, 635 nanoseconds: (ts % 1_000_000_000) as _, 636 })) 637 } else if now { 638 Ok(filesystem::NewTimestamp::Now) 639 } else { 640 Ok(filesystem::NewTimestamp::NoChange) 641 } 642 } 643 644 /// Adjust the timestamps of an open file or directory. 645 /// Note: This is similar to `futimens` in POSIX. 646 #[no_mangle] 647 pub unsafe extern "C" fn fd_filestat_set_times( 648 fd: Fd, 649 atim: Timestamp, 650 mtim: Timestamp, 651 fst_flags: Fstflags, 652 ) -> Errno { 653 State::with(|state| { 654 let atim = systimespec( 655 fst_flags & FSTFLAGS_ATIM == FSTFLAGS_ATIM, 656 atim, 657 fst_flags & FSTFLAGS_ATIM_NOW == FSTFLAGS_ATIM_NOW, 658 )?; 659 let mtim = systimespec( 660 fst_flags & FSTFLAGS_MTIM == FSTFLAGS_MTIM, 661 mtim, 662 fst_flags & FSTFLAGS_MTIM_NOW == FSTFLAGS_MTIM_NOW, 663 )?; 664 let ds = state.descriptors(); 665 let file = ds.get_file(fd)?; 666 filesystem::set_times(file.fd, atim, mtim)?; 667 Ok(()) 668 }) 669 } 670 671 /// Read from a file descriptor, without using and updating the file descriptor's offset. 672 /// Note: This is similar to `preadv` in POSIX. 673 #[no_mangle] 674 pub unsafe extern "C" fn fd_pread( 675 fd: Fd, 676 mut iovs_ptr: *const Iovec, 677 mut iovs_len: usize, 678 offset: Filesize, 679 nread: *mut Size, 680 ) -> Errno { 681 // Advance to the first non-empty buffer. 682 while iovs_len != 0 && (*iovs_ptr).buf_len == 0 { 683 iovs_ptr = iovs_ptr.add(1); 684 iovs_len -= 1; 685 } 686 if iovs_len == 0 { 687 *nread = 0; 688 return ERRNO_SUCCESS; 689 } 690 691 State::with(|state| { 692 let ptr = (*iovs_ptr).buf; 693 let len = (*iovs_ptr).buf_len; 694 695 let ds = state.descriptors(); 696 let file = ds.get_file(fd)?; 697 let (data, end) = state 698 .import_alloc 699 .with_buffer(ptr, len, || filesystem::read(file.fd, len as u64, offset))?; 700 assert_eq!(data.as_ptr(), ptr); 701 assert!(data.len() <= len); 702 703 let len = data.len(); 704 forget(data); 705 if !end && len == 0 { 706 Err(ERRNO_INTR) 707 } else { 708 *nread = len; 709 Ok(()) 710 } 711 }) 712 } 713 714 /// Return a description of the given preopened file descriptor. 715 #[no_mangle] 716 pub unsafe extern "C" fn fd_prestat_get(fd: Fd, buf: *mut Prestat) -> Errno { 717 if matches!( 718 get_allocation_state(), 719 AllocationState::StackAllocated | AllocationState::StateAllocated 720 ) { 721 State::with(|state| { 722 if let Some(preopen) = state.descriptors().get_preopen(fd) { 723 buf.write(Prestat { 724 tag: 0, 725 u: PrestatU { 726 dir: PrestatDir { 727 pr_name_len: preopen.path.len, 728 }, 729 }, 730 }); 731 732 Ok(()) 733 } else { 734 Err(ERRNO_BADF) 735 } 736 }) 737 } else { 738 ERRNO_BADF 739 } 740 } 741 742 /// Return a description of the given preopened file descriptor. 743 #[no_mangle] 744 pub unsafe extern "C" fn fd_prestat_dir_name(fd: Fd, path: *mut u8, path_len: Size) -> Errno { 745 State::with(|state| { 746 if let Some(preopen) = state.descriptors().get_preopen(fd) { 747 if preopen.path.len < path_len as usize { 748 Err(ERRNO_NAMETOOLONG) 749 } else { 750 ptr::copy_nonoverlapping(preopen.path.ptr, path, preopen.path.len); 751 Ok(()) 752 } 753 } else { 754 Err(ERRNO_NOTDIR) 755 } 756 }) 757 } 758 759 /// Write to a file descriptor, without using and updating the file descriptor's offset. 760 /// Note: This is similar to `pwritev` in POSIX. 761 #[no_mangle] 762 pub unsafe extern "C" fn fd_pwrite( 763 fd: Fd, 764 mut iovs_ptr: *const Ciovec, 765 mut iovs_len: usize, 766 offset: Filesize, 767 nwritten: *mut Size, 768 ) -> Errno { 769 // Advance to the first non-empty buffer. 770 while iovs_len != 0 && (*iovs_ptr).buf_len == 0 { 771 iovs_ptr = iovs_ptr.add(1); 772 iovs_len -= 1; 773 } 774 if iovs_len == 0 { 775 *nwritten = 0; 776 return ERRNO_SUCCESS; 777 } 778 779 let ptr = (*iovs_ptr).buf; 780 let len = (*iovs_ptr).buf_len; 781 782 State::with(|state| { 783 let ds = state.descriptors(); 784 let file = ds.get_seekable_file(fd)?; 785 let bytes = filesystem::write(file.fd, slice::from_raw_parts(ptr, len), offset)?; 786 *nwritten = bytes as usize; 787 Ok(()) 788 }) 789 } 790 791 /// Read from a file descriptor. 792 /// Note: This is similar to `readv` in POSIX. 793 #[no_mangle] 794 pub unsafe extern "C" fn fd_read( 795 fd: Fd, 796 mut iovs_ptr: *const Iovec, 797 mut iovs_len: usize, 798 nread: *mut Size, 799 ) -> Errno { 800 // Advance to the first non-empty buffer. 801 while iovs_len != 0 && (*iovs_ptr).buf_len == 0 { 802 iovs_ptr = iovs_ptr.add(1); 803 iovs_len -= 1; 804 } 805 if iovs_len == 0 { 806 *nread = 0; 807 return ERRNO_SUCCESS; 808 } 809 810 let ptr = (*iovs_ptr).buf; 811 let len = (*iovs_ptr).buf_len; 812 813 State::with(|state| { 814 match state.descriptors().get(fd)? { 815 Descriptor::Streams(streams) => { 816 let blocking = if let StreamType::File(file) = &streams.type_ { 817 file.blocking 818 } else { 819 false 820 }; 821 822 let read_len = u64::try_from(len).trapping_unwrap(); 823 let wasi_stream = streams.get_read_stream()?; 824 let (data, end) = state 825 .import_alloc 826 .with_buffer(ptr, len, || { 827 if blocking { 828 streams::blocking_read(wasi_stream, read_len) 829 } else { 830 streams::read(wasi_stream, read_len) 831 } 832 }) 833 .map_err(|_| ERRNO_IO)?; 834 835 assert_eq!(data.as_ptr(), ptr); 836 assert!(data.len() <= len); 837 838 // If this is a file, keep the current-position pointer up to date. 839 if let StreamType::File(file) = &streams.type_ { 840 file.position 841 .set(file.position.get() + data.len() as filesystem::Filesize); 842 } 843 844 let len = data.len(); 845 forget(data); 846 if !end && len == 0 { 847 Err(ERRNO_INTR) 848 } else { 849 *nread = len; 850 Ok(()) 851 } 852 } 853 Descriptor::Closed(_) => Err(ERRNO_BADF), 854 } 855 }) 856 } 857 858 /// Read directory entries from a directory. 859 /// When successful, the contents of the output buffer consist of a sequence of 860 /// directory entries. Each directory entry consists of a `dirent` object, 861 /// followed by `dirent::d_namlen` bytes holding the name of the directory 862 /// entry. 863 /// This function fills the output buffer as much as possible, potentially 864 /// truncating the last directory entry. This allows the caller to grow its 865 /// read buffer size in case it's too small to fit a single large directory 866 /// entry, or skip the oversized directory entry. 867 #[no_mangle] 868 pub unsafe extern "C" fn fd_readdir( 869 fd: Fd, 870 buf: *mut u8, 871 buf_len: Size, 872 cookie: Dircookie, 873 bufused: *mut Size, 874 ) -> Errno { 875 let mut buf = slice::from_raw_parts_mut(buf, buf_len); 876 return State::with(|state| { 877 // First determine if there's an entry in the dirent cache to use. This 878 // is done to optimize the use case where a large directory is being 879 // used with a fixed-sized buffer to avoid re-invoking the `readdir` 880 // function and continuing to use the same iterator. 881 // 882 // This is a bit tricky since the requested state in this function call 883 // must match the prior state of the dirent stream, if any, so that's 884 // all validated here as well. 885 // 886 // Note that for the duration of this function the `cookie` specifier is 887 // the `n`th iteration of the `readdir` stream return value. 888 let prev_stream = state.dirent_cache.stream.replace(None); 889 let stream = 890 if state.dirent_cache.for_fd.get() == fd && state.dirent_cache.cookie.get() == cookie { 891 prev_stream 892 } else { 893 None 894 }; 895 896 // Compute the inode of `.` so that the iterator can produce an entry 897 // for it. 898 let ds = state.descriptors(); 899 let dir = ds.get_dir(fd)?; 900 let stat = filesystem::stat(dir.fd)?; 901 let dot_inode = stat.inode; 902 903 let mut iter; 904 match stream { 905 // All our checks passed and a dirent cache was available with a 906 // prior stream. Construct an iterator which will yield its first 907 // entry from cache and is additionally resuming at the `cookie` 908 // specified. 909 Some(stream) => { 910 iter = DirectoryEntryIterator { 911 stream, 912 state, 913 cookie, 914 use_cache: true, 915 dot_inode, 916 } 917 } 918 919 // Either a dirent stream wasn't previously available, a different 920 // cookie was requested, or a brand new directory is now being read. 921 // In these situations fall back to resuming reading the directory 922 // from scratch, and the `cookie` value indicates how many items 923 // need skipping. 924 None => { 925 iter = DirectoryEntryIterator { 926 state, 927 cookie: wasi::DIRCOOKIE_START, 928 use_cache: false, 929 stream: DirectoryEntryStream(filesystem::read_directory(dir.fd)?), 930 dot_inode, 931 }; 932 933 // Skip to the entry that is requested by the `cookie` 934 // parameter. 935 for _ in wasi::DIRCOOKIE_START..cookie { 936 match iter.next() { 937 Some(Ok(_)) => {} 938 Some(Err(e)) => return Err(e), 939 None => return Ok(()), 940 } 941 } 942 } 943 }; 944 945 while buf.len() > 0 { 946 let (dirent, name) = match iter.next() { 947 Some(Ok(pair)) => pair, 948 Some(Err(e)) => return Err(e), 949 None => break, 950 }; 951 952 // Copy a `dirent` describing this entry into the destination `buf`, 953 // truncating it if it doesn't fit entirely. 954 let bytes = slice::from_raw_parts( 955 (&dirent as *const wasi::Dirent).cast::<u8>(), 956 size_of::<Dirent>(), 957 ); 958 let dirent_bytes_to_copy = buf.len().min(bytes.len()); 959 buf[..dirent_bytes_to_copy].copy_from_slice(&bytes[..dirent_bytes_to_copy]); 960 buf = &mut buf[dirent_bytes_to_copy..]; 961 962 // Copy the name bytes into the output `buf`, truncating it if it 963 // doesn't fit. 964 // 965 // Note that this might be a 0-byte copy if the `dirent` was 966 // truncated or fit entirely into the destination. 967 let name_bytes_to_copy = buf.len().min(name.len()); 968 ptr::copy_nonoverlapping(name.as_ptr().cast(), buf.as_mut_ptr(), name_bytes_to_copy); 969 970 buf = &mut buf[name_bytes_to_copy..]; 971 972 // If the buffer is empty then that means the value may be 973 // truncated, so save the state of the iterator in our dirent cache 974 // and return. 975 // 976 // Note that `cookie - 1` is stored here since `iter.cookie` stores 977 // the address of the next item, and we're rewinding one item since 978 // the current item is truncated and will want to resume from that 979 // in the future. 980 // 981 // Additionally note that this caching step is skipped if the name 982 // to store doesn't actually fit in the dirent cache's path storage. 983 // In that case there's not much we can do and let the next call to 984 // `fd_readdir` start from scratch. 985 if buf.len() == 0 && name.len() <= DIRENT_CACHE { 986 let DirectoryEntryIterator { stream, cookie, .. } = iter; 987 state.dirent_cache.stream.set(Some(stream)); 988 state.dirent_cache.for_fd.set(fd); 989 state.dirent_cache.cookie.set(cookie - 1); 990 state.dirent_cache.cached_dirent.set(dirent); 991 ptr::copy( 992 name.as_ptr().cast::<u8>(), 993 (*state.dirent_cache.path_data.get()).as_mut_ptr() as *mut u8, 994 name.len(), 995 ); 996 break; 997 } 998 } 999 1000 *bufused = buf_len - buf.len(); 1001 Ok(()) 1002 }); 1003 1004 struct DirectoryEntryIterator<'a> { 1005 state: &'a State, 1006 use_cache: bool, 1007 cookie: Dircookie, 1008 stream: DirectoryEntryStream, 1009 dot_inode: wasi::Inode, 1010 } 1011 1012 impl<'a> Iterator for DirectoryEntryIterator<'a> { 1013 // Note the usage of `UnsafeCell<u8>` here to indicate that the data can 1014 // alias the storage within `state`. 1015 type Item = Result<(wasi::Dirent, &'a [UnsafeCell<u8>]), Errno>; 1016 1017 fn next(&mut self) -> Option<Self::Item> { 1018 let current_cookie = self.cookie; 1019 1020 self.cookie += 1; 1021 1022 // Preview1 programs expect to see `.` and `..` in the traversal, but 1023 // Preview2 excludes them, so re-add them. 1024 match current_cookie { 1025 0 => { 1026 let dirent = wasi::Dirent { 1027 d_next: self.cookie, 1028 d_ino: self.dot_inode, 1029 d_type: wasi::FILETYPE_DIRECTORY, 1030 d_namlen: 1, 1031 }; 1032 return Some(Ok((dirent, &self.state.dotdot[..1]))); 1033 } 1034 1 => { 1035 let dirent = wasi::Dirent { 1036 d_next: self.cookie, 1037 d_ino: 0, 1038 d_type: wasi::FILETYPE_DIRECTORY, 1039 d_namlen: 2, 1040 }; 1041 return Some(Ok((dirent, &self.state.dotdot[..]))); 1042 } 1043 _ => {} 1044 } 1045 1046 if self.use_cache { 1047 self.use_cache = false; 1048 return Some(unsafe { 1049 let dirent = self.state.dirent_cache.cached_dirent.as_ptr().read(); 1050 let ptr = (*(*self.state.dirent_cache.path_data.get()).as_ptr()) 1051 .as_ptr() 1052 .cast(); 1053 let buffer = slice::from_raw_parts(ptr, dirent.d_namlen as usize); 1054 Ok((dirent, buffer)) 1055 }); 1056 } 1057 let entry = self.state.import_alloc.with_buffer( 1058 self.state.path_buf.get().cast(), 1059 PATH_MAX, 1060 || filesystem::read_directory_entry(self.stream.0), 1061 ); 1062 let entry = match entry { 1063 Ok(Some(entry)) => entry, 1064 Ok(None) => return None, 1065 Err(e) => return Some(Err(e.into())), 1066 }; 1067 1068 let filesystem::DirectoryEntry { inode, type_, name } = entry; 1069 let name = ManuallyDrop::new(name); 1070 let dirent = wasi::Dirent { 1071 d_next: self.cookie, 1072 d_ino: inode.unwrap_or(0), 1073 d_namlen: u32::try_from(name.len()).trapping_unwrap(), 1074 d_type: type_.into(), 1075 }; 1076 // Extend the lifetime of `name` to the `self.state` lifetime for 1077 // this iterator since the data for the name lives within state. 1078 let name = unsafe { 1079 assert_eq!(name.as_ptr(), self.state.path_buf.get().cast()); 1080 slice::from_raw_parts(name.as_ptr().cast(), name.len()) 1081 }; 1082 Some(Ok((dirent, name))) 1083 } 1084 } 1085 } 1086 1087 /// Atomically replace a file descriptor by renumbering another file descriptor. 1088 /// Due to the strong focus on thread safety, this environment does not provide 1089 /// a mechanism to duplicate or renumber a file descriptor to an arbitrary 1090 /// number, like `dup2()`. This would be prone to race conditions, as an actual 1091 /// file descriptor with the same number could be allocated by a different 1092 /// thread at the same time. 1093 /// This function provides a way to atomically renumber file descriptors, which 1094 /// would disappear if `dup2()` were to be removed entirely. 1095 #[no_mangle] 1096 pub unsafe extern "C" fn fd_renumber(fd: Fd, to: Fd) -> Errno { 1097 State::with_mut(|state| state.descriptors_mut().renumber(fd, to)) 1098 } 1099 1100 /// Move the offset of a file descriptor. 1101 /// Note: This is similar to `lseek` in POSIX. 1102 #[no_mangle] 1103 pub unsafe extern "C" fn fd_seek( 1104 fd: Fd, 1105 offset: Filedelta, 1106 whence: Whence, 1107 newoffset: *mut Filesize, 1108 ) -> Errno { 1109 State::with(|state| { 1110 let ds = state.descriptors(); 1111 let stream = ds.get_seekable_stream(fd)?; 1112 1113 // Seeking only works on files. 1114 if let StreamType::File(file) = &stream.type_ { 1115 if let filesystem::DescriptorType::Directory = file.descriptor_type { 1116 // This isn't really the "right" errno, but it is consistient with wasmtime's 1117 // preview 1 tests. 1118 return Err(ERRNO_BADF); 1119 } 1120 let from = match whence { 1121 WHENCE_SET if offset >= 0 => offset, 1122 WHENCE_CUR => match (file.position.get() as i64).checked_add(offset) { 1123 Some(pos) if pos >= 0 => pos, 1124 _ => return Err(ERRNO_INVAL), 1125 }, 1126 WHENCE_END => match (filesystem::stat(file.fd)?.size as i64).checked_add(offset) { 1127 Some(pos) if pos >= 0 => pos, 1128 _ => return Err(ERRNO_INVAL), 1129 }, 1130 _ => return Err(ERRNO_INVAL), 1131 }; 1132 stream.input.set(None); 1133 stream.output.set(None); 1134 file.position.set(from as filesystem::Filesize); 1135 *newoffset = from as filesystem::Filesize; 1136 Ok(()) 1137 } else { 1138 Err(ERRNO_SPIPE) 1139 } 1140 }) 1141 } 1142 1143 /// Synchronize the data and metadata of a file to disk. 1144 /// Note: This is similar to `fsync` in POSIX. 1145 #[no_mangle] 1146 pub unsafe extern "C" fn fd_sync(fd: Fd) -> Errno { 1147 State::with(|state| { 1148 let ds = state.descriptors(); 1149 let file = ds.get_file(fd)?; 1150 filesystem::sync(file.fd)?; 1151 Ok(()) 1152 }) 1153 } 1154 1155 /// Return the current offset of a file descriptor. 1156 /// Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. 1157 #[no_mangle] 1158 pub unsafe extern "C" fn fd_tell(fd: Fd, offset: *mut Filesize) -> Errno { 1159 State::with(|state| { 1160 let ds = state.descriptors(); 1161 let file = ds.get_seekable_file(fd)?; 1162 *offset = file.position.get() as Filesize; 1163 Ok(()) 1164 }) 1165 } 1166 1167 /// Write to a file descriptor. 1168 /// Note: This is similar to `writev` in POSIX. 1169 #[no_mangle] 1170 pub unsafe extern "C" fn fd_write( 1171 fd: Fd, 1172 mut iovs_ptr: *const Ciovec, 1173 mut iovs_len: usize, 1174 nwritten: *mut Size, 1175 ) -> Errno { 1176 if matches!( 1177 get_allocation_state(), 1178 AllocationState::StackAllocated | AllocationState::StateAllocated 1179 ) { 1180 // Advance to the first non-empty buffer. 1181 while iovs_len != 0 && (*iovs_ptr).buf_len == 0 { 1182 iovs_ptr = iovs_ptr.add(1); 1183 iovs_len -= 1; 1184 } 1185 if iovs_len == 0 { 1186 *nwritten = 0; 1187 return ERRNO_SUCCESS; 1188 } 1189 1190 let ptr = (*iovs_ptr).buf; 1191 let len = (*iovs_ptr).buf_len; 1192 let bytes = slice::from_raw_parts(ptr, len); 1193 1194 State::with(|state| { 1195 let ds = state.descriptors(); 1196 match ds.get(fd)? { 1197 Descriptor::Streams(streams) => { 1198 let wasi_stream = streams.get_write_stream()?; 1199 1200 let bytes = if let StreamType::File(file) = &streams.type_ { 1201 if file.blocking { 1202 streams::blocking_write(wasi_stream, bytes) 1203 } else { 1204 streams::write(wasi_stream, bytes) 1205 } 1206 } else { 1207 streams::write(wasi_stream, bytes) 1208 } 1209 .map_err(|_| ERRNO_IO)?; 1210 1211 // If this is a file, keep the current-position pointer up to date. 1212 if let StreamType::File(file) = &streams.type_ { 1213 // But don't update if we're in append mode. Strictly speaking, 1214 // we should set the position to the new end of the file, but 1215 // we don't have an API to do that atomically. 1216 if !file.append { 1217 file.position 1218 .set(file.position.get() + filesystem::Filesize::from(bytes)); 1219 } 1220 } 1221 1222 *nwritten = bytes as usize; 1223 Ok(()) 1224 } 1225 Descriptor::Closed(_) => Err(ERRNO_BADF), 1226 } 1227 }) 1228 } else { 1229 *nwritten = 0; 1230 ERRNO_IO 1231 } 1232 } 1233 1234 /// Create a directory. 1235 /// Note: This is similar to `mkdirat` in POSIX. 1236 #[no_mangle] 1237 pub unsafe extern "C" fn path_create_directory( 1238 fd: Fd, 1239 path_ptr: *const u8, 1240 path_len: usize, 1241 ) -> Errno { 1242 let path = slice::from_raw_parts(path_ptr, path_len); 1243 1244 State::with(|state| { 1245 let ds = state.descriptors(); 1246 let file = ds.get_dir(fd)?; 1247 filesystem::create_directory_at(file.fd, path)?; 1248 Ok(()) 1249 }) 1250 } 1251 1252 /// Return the attributes of a file or directory. 1253 /// Note: This is similar to `stat` in POSIX. 1254 #[no_mangle] 1255 pub unsafe extern "C" fn path_filestat_get( 1256 fd: Fd, 1257 flags: Lookupflags, 1258 path_ptr: *const u8, 1259 path_len: usize, 1260 buf: *mut Filestat, 1261 ) -> Errno { 1262 let path = slice::from_raw_parts(path_ptr, path_len); 1263 let at_flags = at_flags_from_lookupflags(flags); 1264 1265 State::with(|state| { 1266 let ds = state.descriptors(); 1267 let file = ds.get_dir(fd)?; 1268 let stat = filesystem::stat_at(file.fd, at_flags, path)?; 1269 let filetype = stat.type_.into(); 1270 *buf = Filestat { 1271 dev: stat.device, 1272 ino: stat.inode, 1273 filetype, 1274 nlink: stat.link_count, 1275 size: stat.size, 1276 atim: datetime_to_timestamp(stat.data_access_timestamp), 1277 mtim: datetime_to_timestamp(stat.data_modification_timestamp), 1278 ctim: datetime_to_timestamp(stat.status_change_timestamp), 1279 }; 1280 Ok(()) 1281 }) 1282 } 1283 1284 /// Adjust the timestamps of a file or directory. 1285 /// Note: This is similar to `utimensat` in POSIX. 1286 #[no_mangle] 1287 pub unsafe extern "C" fn path_filestat_set_times( 1288 fd: Fd, 1289 flags: Lookupflags, 1290 path_ptr: *const u8, 1291 path_len: usize, 1292 atim: Timestamp, 1293 mtim: Timestamp, 1294 fst_flags: Fstflags, 1295 ) -> Errno { 1296 let path = slice::from_raw_parts(path_ptr, path_len); 1297 let at_flags = at_flags_from_lookupflags(flags); 1298 1299 State::with(|state| { 1300 let atim = systimespec( 1301 fst_flags & FSTFLAGS_ATIM == FSTFLAGS_ATIM, 1302 atim, 1303 fst_flags & FSTFLAGS_ATIM_NOW == FSTFLAGS_ATIM_NOW, 1304 )?; 1305 let mtim = systimespec( 1306 fst_flags & FSTFLAGS_MTIM == FSTFLAGS_MTIM, 1307 mtim, 1308 fst_flags & FSTFLAGS_MTIM_NOW == FSTFLAGS_MTIM_NOW, 1309 )?; 1310 1311 let ds = state.descriptors(); 1312 let file = ds.get_dir(fd)?; 1313 filesystem::set_times_at(file.fd, at_flags, path, atim, mtim)?; 1314 Ok(()) 1315 }) 1316 } 1317 1318 /// Create a hard link. 1319 /// Note: This is similar to `linkat` in POSIX. 1320 #[no_mangle] 1321 pub unsafe extern "C" fn path_link( 1322 old_fd: Fd, 1323 old_flags: Lookupflags, 1324 old_path_ptr: *const u8, 1325 old_path_len: usize, 1326 new_fd: Fd, 1327 new_path_ptr: *const u8, 1328 new_path_len: usize, 1329 ) -> Errno { 1330 let old_path = slice::from_raw_parts(old_path_ptr, old_path_len); 1331 let new_path = slice::from_raw_parts(new_path_ptr, new_path_len); 1332 let at_flags = at_flags_from_lookupflags(old_flags); 1333 1334 State::with(|state| { 1335 let old = state.descriptors().get_dir(old_fd)?.fd; 1336 let new = state.descriptors().get_dir(new_fd)?.fd; 1337 filesystem::link_at(old, at_flags, old_path, new, new_path)?; 1338 Ok(()) 1339 }) 1340 } 1341 1342 /// Open a file or directory. 1343 /// The returned file descriptor is not guaranteed to be the lowest-numbered 1344 /// file descriptor not currently open; it is randomized to prevent 1345 /// applications from depending on making assumptions about indexes, since this 1346 /// is error-prone in multi-threaded contexts. The returned file descriptor is 1347 /// guaranteed to be less than 2**31. 1348 /// Note: This is similar to `openat` in POSIX. 1349 #[no_mangle] 1350 pub unsafe extern "C" fn path_open( 1351 fd: Fd, 1352 dirflags: Lookupflags, 1353 path_ptr: *const u8, 1354 path_len: usize, 1355 oflags: Oflags, 1356 fs_rights_base: Rights, 1357 fs_rights_inheriting: Rights, 1358 fdflags: Fdflags, 1359 opened_fd: *mut Fd, 1360 ) -> Errno { 1361 drop(fs_rights_inheriting); 1362 1363 let path = slice::from_raw_parts(path_ptr, path_len); 1364 let at_flags = at_flags_from_lookupflags(dirflags); 1365 let o_flags = o_flags_from_oflags(oflags); 1366 let flags = descriptor_flags_from_flags(fs_rights_base, fdflags); 1367 let mode = filesystem::Modes::READABLE | filesystem::Modes::WRITEABLE; 1368 let append = fdflags & wasi::FDFLAGS_APPEND == wasi::FDFLAGS_APPEND; 1369 1370 State::with_mut(|state| { 1371 let mut ds = state.descriptors_mut(); 1372 let file = ds.get_dir(fd)?; 1373 let result = filesystem::open_at(file.fd, at_flags, path, o_flags, flags, mode)?; 1374 let descriptor_type = filesystem::get_type(result)?; 1375 let desc = Descriptor::Streams(Streams { 1376 input: Cell::new(None), 1377 output: Cell::new(None), 1378 type_: StreamType::File(File { 1379 fd: result, 1380 descriptor_type, 1381 position: Cell::new(0), 1382 append, 1383 blocking: (fdflags & wasi::FDFLAGS_NONBLOCK) == 0, 1384 }), 1385 }); 1386 1387 let fd = ds.open(desc)?; 1388 *opened_fd = fd; 1389 Ok(()) 1390 }) 1391 } 1392 1393 /// Read the contents of a symbolic link. 1394 /// Note: This is similar to `readlinkat` in POSIX. 1395 #[no_mangle] 1396 pub unsafe extern "C" fn path_readlink( 1397 fd: Fd, 1398 path_ptr: *const u8, 1399 path_len: usize, 1400 buf: *mut u8, 1401 buf_len: Size, 1402 bufused: *mut Size, 1403 ) -> Errno { 1404 let path = slice::from_raw_parts(path_ptr, path_len); 1405 1406 State::with(|state| { 1407 // If the user gave us a buffer shorter than `PATH_MAX`, it may not be 1408 // long enough to accept the actual path. `cabi_realloc` can't fail, 1409 // so instead we handle this case specially. 1410 let use_state_buf = buf_len < PATH_MAX; 1411 1412 let ds = state.descriptors(); 1413 let file = ds.get_dir(fd)?; 1414 let path = if use_state_buf { 1415 state 1416 .import_alloc 1417 .with_buffer(state.path_buf.get().cast(), PATH_MAX, || { 1418 filesystem::readlink_at(file.fd, path) 1419 })? 1420 } else { 1421 state 1422 .import_alloc 1423 .with_buffer(buf, buf_len, || filesystem::readlink_at(file.fd, path))? 1424 }; 1425 1426 if use_state_buf { 1427 // Preview1 follows POSIX in truncating the returned path if it 1428 // doesn't fit. 1429 let len = min(path.len(), buf_len); 1430 ptr::copy_nonoverlapping(path.as_ptr().cast(), buf, len); 1431 *bufused = len; 1432 } else { 1433 *bufused = path.len(); 1434 } 1435 1436 // The returned string's memory was allocated in `buf`, so don't separately 1437 // free it. 1438 forget(path); 1439 1440 Ok(()) 1441 }) 1442 } 1443 1444 /// Remove a directory. 1445 /// Return `errno::notempty` if the directory is not empty. 1446 /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. 1447 #[no_mangle] 1448 pub unsafe extern "C" fn path_remove_directory( 1449 fd: Fd, 1450 path_ptr: *const u8, 1451 path_len: usize, 1452 ) -> Errno { 1453 let path = slice::from_raw_parts(path_ptr, path_len); 1454 1455 State::with(|state| { 1456 let ds = state.descriptors(); 1457 let file = ds.get_dir(fd)?; 1458 filesystem::remove_directory_at(file.fd, path)?; 1459 Ok(()) 1460 }) 1461 } 1462 1463 /// Rename a file or directory. 1464 /// Note: This is similar to `renameat` in POSIX. 1465 #[no_mangle] 1466 pub unsafe extern "C" fn path_rename( 1467 old_fd: Fd, 1468 old_path_ptr: *const u8, 1469 old_path_len: usize, 1470 new_fd: Fd, 1471 new_path_ptr: *const u8, 1472 new_path_len: usize, 1473 ) -> Errno { 1474 let old_path = slice::from_raw_parts(old_path_ptr, old_path_len); 1475 let new_path = slice::from_raw_parts(new_path_ptr, new_path_len); 1476 1477 State::with(|state| { 1478 let ds = state.descriptors(); 1479 let old = ds.get_dir(old_fd)?.fd; 1480 let new = ds.get_dir(new_fd)?.fd; 1481 filesystem::rename_at(old, old_path, new, new_path)?; 1482 Ok(()) 1483 }) 1484 } 1485 1486 /// Create a symbolic link. 1487 /// Note: This is similar to `symlinkat` in POSIX. 1488 #[no_mangle] 1489 pub unsafe extern "C" fn path_symlink( 1490 old_path_ptr: *const u8, 1491 old_path_len: usize, 1492 fd: Fd, 1493 new_path_ptr: *const u8, 1494 new_path_len: usize, 1495 ) -> Errno { 1496 let old_path = slice::from_raw_parts(old_path_ptr, old_path_len); 1497 let new_path = slice::from_raw_parts(new_path_ptr, new_path_len); 1498 1499 State::with(|state| { 1500 let ds = state.descriptors(); 1501 let file = ds.get_dir(fd)?; 1502 filesystem::symlink_at(file.fd, old_path, new_path)?; 1503 Ok(()) 1504 }) 1505 } 1506 1507 /// Unlink a file. 1508 /// Return `errno::isdir` if the path refers to a directory. 1509 /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. 1510 #[no_mangle] 1511 pub unsafe extern "C" fn path_unlink_file(fd: Fd, path_ptr: *const u8, path_len: usize) -> Errno { 1512 let path = slice::from_raw_parts(path_ptr, path_len); 1513 1514 State::with(|state| { 1515 let ds = state.descriptors(); 1516 let file = ds.get_dir(fd)?; 1517 filesystem::unlink_file_at(file.fd, path)?; 1518 Ok(()) 1519 }) 1520 } 1521 1522 struct Pollables { 1523 pointer: *mut Pollable, 1524 index: usize, 1525 length: usize, 1526 } 1527 1528 impl Pollables { 1529 unsafe fn push(&mut self, pollable: Pollable) { 1530 assert!(self.index < self.length); 1531 *self.pointer.add(self.index) = pollable; 1532 self.index += 1; 1533 } 1534 } 1535 1536 impl Drop for Pollables { 1537 fn drop(&mut self) { 1538 for i in 0..self.index { 1539 poll::drop_pollable(unsafe { *self.pointer.add(i) }) 1540 } 1541 } 1542 } 1543 1544 impl From<network::Error> for Errno { 1545 fn from(error: network::Error) -> Errno { 1546 match error { 1547 network::Error::Unknown => unreachable!(), // TODO 1548 network::Error::Again => ERRNO_AGAIN, 1549 /* TODO 1550 // Use a black box to prevent the optimizer from generating a 1551 // lookup table, which would require a static initializer. 1552 ConnectionAborted => black_box(ERRNO_CONNABORTED), 1553 ConnectionRefused => ERRNO_CONNREFUSED, 1554 ConnectionReset => ERRNO_CONNRESET, 1555 HostUnreachable => ERRNO_HOSTUNREACH, 1556 NetworkDown => ERRNO_NETDOWN, 1557 NetworkUnreachable => ERRNO_NETUNREACH, 1558 Timedout => ERRNO_TIMEDOUT, 1559 _ => unreachable!(), 1560 */ 1561 } 1562 } 1563 } 1564 1565 /// Concurrently poll for the occurrence of a set of events. 1566 #[no_mangle] 1567 pub unsafe extern "C" fn poll_oneoff( 1568 r#in: *const Subscription, 1569 out: *mut Event, 1570 nsubscriptions: Size, 1571 nevents: *mut Size, 1572 ) -> Errno { 1573 *nevents = 0; 1574 1575 let subscriptions = slice::from_raw_parts(r#in, nsubscriptions); 1576 1577 // We're going to split the `nevents` buffer into two non-overlapping 1578 // buffers: one to store the pollable handles, and the other to store 1579 // the bool results. 1580 // 1581 // First, we assert that this is possible: 1582 assert!(align_of::<Event>() >= align_of::<Pollable>()); 1583 assert!(align_of::<Pollable>() >= align_of::<u8>()); 1584 assert!( 1585 nsubscriptions 1586 .checked_mul(size_of::<Event>()) 1587 .trapping_unwrap() 1588 >= nsubscriptions 1589 .checked_mul(size_of::<Pollable>()) 1590 .trapping_unwrap() 1591 .checked_add( 1592 nsubscriptions 1593 .checked_mul(size_of::<u8>()) 1594 .trapping_unwrap() 1595 ) 1596 .trapping_unwrap() 1597 ); 1598 // Store the pollable handles at the beginning, and the bool results at the 1599 // end, so that we don't clobber the bool results when writting the events. 1600 let pollables = out as *mut c_void as *mut Pollable; 1601 let results = out.add(nsubscriptions).cast::<u8>().sub(nsubscriptions); 1602 1603 // Indefinite sleeping is not supported in preview1. 1604 if nsubscriptions == 0 { 1605 return ERRNO_INVAL; 1606 } 1607 1608 State::with(|state| { 1609 const EVENTTYPE_CLOCK: u8 = wasi::EVENTTYPE_CLOCK.raw(); 1610 const EVENTTYPE_FD_READ: u8 = wasi::EVENTTYPE_FD_READ.raw(); 1611 const EVENTTYPE_FD_WRITE: u8 = wasi::EVENTTYPE_FD_WRITE.raw(); 1612 1613 let mut pollables = Pollables { 1614 pointer: pollables, 1615 index: 0, 1616 length: nsubscriptions, 1617 }; 1618 1619 for subscription in subscriptions { 1620 pollables.push(match subscription.u.tag { 1621 EVENTTYPE_CLOCK => { 1622 let clock = &subscription.u.u.clock; 1623 let absolute = (clock.flags & SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME) 1624 == SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME; 1625 match clock.id { 1626 CLOCKID_REALTIME => { 1627 let timeout = if absolute { 1628 // Convert `clock.timeout` to `Datetime`. 1629 let mut datetime = wall_clock::Datetime { 1630 seconds: clock.timeout / 1_000_000_000, 1631 nanoseconds: (clock.timeout % 1_000_000_000) as _, 1632 }; 1633 1634 // Subtract `now`. 1635 let now = wall_clock::now(); 1636 datetime.seconds -= now.seconds; 1637 if datetime.nanoseconds < now.nanoseconds { 1638 datetime.seconds -= 1; 1639 datetime.nanoseconds += 1_000_000_000; 1640 } 1641 datetime.nanoseconds -= now.nanoseconds; 1642 1643 // Convert to nanoseconds. 1644 let nanos = datetime 1645 .seconds 1646 .checked_mul(1_000_000_000) 1647 .ok_or(ERRNO_OVERFLOW)?; 1648 nanos 1649 .checked_add(datetime.nanoseconds.into()) 1650 .ok_or(ERRNO_OVERFLOW)? 1651 } else { 1652 clock.timeout 1653 }; 1654 1655 monotonic_clock::subscribe(timeout, false) 1656 } 1657 1658 CLOCKID_MONOTONIC => monotonic_clock::subscribe(clock.timeout, absolute), 1659 1660 _ => return Err(ERRNO_INVAL), 1661 } 1662 } 1663 1664 EVENTTYPE_FD_READ => { 1665 let stream = state 1666 .descriptors() 1667 .get_read_stream(subscription.u.u.fd_read.file_descriptor)?; 1668 streams::subscribe_to_input_stream(stream) 1669 } 1670 1671 EVENTTYPE_FD_WRITE => { 1672 let stream = state 1673 .descriptors() 1674 .get_write_stream(subscription.u.u.fd_write.file_descriptor)?; 1675 streams::subscribe_to_output_stream(stream) 1676 } 1677 1678 _ => return Err(ERRNO_INVAL), 1679 }); 1680 } 1681 1682 let vec = state.import_alloc.with_buffer( 1683 results, 1684 nsubscriptions 1685 .checked_mul(size_of::<bool>()) 1686 .trapping_unwrap(), 1687 || poll::poll_oneoff(slice::from_raw_parts(pollables.pointer, pollables.length)), 1688 ); 1689 1690 assert_eq!(vec.len(), nsubscriptions); 1691 assert_eq!(vec.as_ptr(), results); 1692 forget(vec); 1693 1694 drop(pollables); 1695 1696 let ready = subscriptions 1697 .iter() 1698 .enumerate() 1699 .filter_map(|(i, s)| (*results.add(i) != 0).then_some(s)); 1700 1701 let mut count = 0; 1702 1703 for subscription in ready { 1704 let error; 1705 let type_; 1706 let nbytes; 1707 let flags; 1708 1709 match subscription.u.tag { 1710 EVENTTYPE_CLOCK => { 1711 error = ERRNO_SUCCESS; 1712 type_ = wasi::EVENTTYPE_CLOCK; 1713 nbytes = 0; 1714 flags = 0; 1715 } 1716 1717 EVENTTYPE_FD_READ => { 1718 type_ = wasi::EVENTTYPE_FD_READ; 1719 let ds = state.descriptors(); 1720 let desc = ds 1721 .get(subscription.u.u.fd_read.file_descriptor) 1722 .trapping_unwrap(); 1723 match desc { 1724 Descriptor::Streams(streams) => match &streams.type_ { 1725 StreamType::File(file) => match filesystem::stat(file.fd) { 1726 Ok(stat) => { 1727 error = ERRNO_SUCCESS; 1728 nbytes = stat.size.saturating_sub(file.position.get()); 1729 flags = if nbytes == 0 { 1730 EVENTRWFLAGS_FD_READWRITE_HANGUP 1731 } else { 1732 0 1733 }; 1734 } 1735 Err(e) => { 1736 error = e.into(); 1737 nbytes = 1; 1738 flags = 0; 1739 } 1740 }, 1741 StreamType::Socket(connection) => { 1742 unreachable!() // TODO 1743 /* 1744 match tcp::bytes_readable(*connection) { 1745 Ok(result) => { 1746 error = ERRNO_SUCCESS; 1747 nbytes = result.0; 1748 flags = if result.1 { 1749 EVENTRWFLAGS_FD_READWRITE_HANGUP 1750 } else { 1751 0 1752 }; 1753 } 1754 Err(e) => { 1755 error = e.into(); 1756 nbytes = 0; 1757 flags = 0; 1758 } 1759 } 1760 */ 1761 } 1762 StreamType::Stdio => { 1763 error = ERRNO_SUCCESS; 1764 nbytes = 1; 1765 flags = 0; 1766 } 1767 }, 1768 _ => unreachable!(), 1769 } 1770 } 1771 EVENTTYPE_FD_WRITE => { 1772 type_ = wasi::EVENTTYPE_FD_WRITE; 1773 let ds = state.descriptors(); 1774 let desc = ds 1775 .get(subscription.u.u.fd_write.file_descriptor) 1776 .trapping_unwrap(); 1777 match desc { 1778 Descriptor::Streams(streams) => match streams.type_ { 1779 StreamType::File(_) | StreamType::Stdio => { 1780 error = ERRNO_SUCCESS; 1781 nbytes = 1; 1782 flags = 0; 1783 } 1784 StreamType::Socket(connection) => { 1785 unreachable!() // TODO 1786 /* 1787 match tcp::bytes_writable(connection) { 1788 Ok(result) => { 1789 error = ERRNO_SUCCESS; 1790 nbytes = result.0; 1791 flags = if result.1 { 1792 EVENTRWFLAGS_FD_READWRITE_HANGUP 1793 } else { 1794 0 1795 }; 1796 } 1797 Err(e) => { 1798 error = e.into(); 1799 nbytes = 0; 1800 flags = 0; 1801 } 1802 } 1803 */ 1804 } 1805 }, 1806 _ => unreachable!(), 1807 } 1808 } 1809 1810 _ => unreachable!(), 1811 } 1812 1813 *out.add(count) = Event { 1814 userdata: subscription.userdata, 1815 error, 1816 type_, 1817 fd_readwrite: EventFdReadwrite { nbytes, flags }, 1818 }; 1819 1820 count += 1; 1821 } 1822 1823 *nevents = count; 1824 1825 Ok(()) 1826 }) 1827 } 1828 1829 /// Terminate the process normally. An exit code of 0 indicates successful 1830 /// termination of the program. The meanings of other values is dependent on 1831 /// the environment. 1832 #[no_mangle] 1833 pub unsafe extern "C" fn proc_exit(rval: Exitcode) -> ! { 1834 let status = if rval == 0 { Ok(()) } else { Err(()) }; 1835 exit::exit(status); // does not return 1836 unreachable!("host exit implementation didn't exit!") // actually unreachable 1837 } 1838 1839 /// Send a signal to the process of the calling thread. 1840 /// Note: This is similar to `raise` in POSIX. 1841 #[no_mangle] 1842 pub unsafe extern "C" fn proc_raise(sig: Signal) -> Errno { 1843 unreachable!() 1844 } 1845 1846 /// Temporarily yield execution of the calling thread. 1847 /// Note: This is similar to `sched_yield` in POSIX. 1848 #[no_mangle] 1849 pub unsafe extern "C" fn sched_yield() -> Errno { 1850 // TODO: This is not yet covered in Preview2. 1851 1852 ERRNO_SUCCESS 1853 } 1854 1855 /// Write high-quality random data into a buffer. 1856 /// This function blocks when the implementation is unable to immediately 1857 /// provide sufficient high-quality random data. 1858 /// This function may execute slowly, so when large mounts of random data are 1859 /// required, it's advisable to use this function to seed a pseudo-random 1860 /// number generator, rather than to provide the random data directly. 1861 #[no_mangle] 1862 pub unsafe extern "C" fn random_get(buf: *mut u8, buf_len: Size) -> Errno { 1863 if matches!( 1864 get_allocation_state(), 1865 AllocationState::StackAllocated | AllocationState::StateAllocated 1866 ) { 1867 State::with(|state| { 1868 assert_eq!(buf_len as u32 as Size, buf_len); 1869 let result = state 1870 .import_alloc 1871 .with_buffer(buf, buf_len, || random::get_random_bytes(buf_len as u64)); 1872 assert_eq!(result.as_ptr(), buf); 1873 1874 // The returned buffer's memory was allocated in `buf`, so don't separately 1875 // free it. 1876 forget(result); 1877 1878 Ok(()) 1879 }) 1880 } else { 1881 ERRNO_SUCCESS 1882 } 1883 } 1884 1885 /// Accept a new incoming connection. 1886 /// Note: This is similar to `accept` in POSIX. 1887 #[no_mangle] 1888 pub unsafe extern "C" fn sock_accept(fd: Fd, flags: Fdflags, connection: *mut Fd) -> Errno { 1889 unreachable!() 1890 } 1891 1892 /// Receive a message from a socket. 1893 /// Note: This is similar to `recv` in POSIX, though it also supports reading 1894 /// the data into multiple buffers in the manner of `readv`. 1895 #[no_mangle] 1896 pub unsafe extern "C" fn sock_recv( 1897 fd: Fd, 1898 ri_data_ptr: *const Iovec, 1899 ri_data_len: usize, 1900 ri_flags: Riflags, 1901 ro_datalen: *mut Size, 1902 ro_flags: *mut Roflags, 1903 ) -> Errno { 1904 unreachable!() 1905 } 1906 1907 /// Send a message on a socket. 1908 /// Note: This is similar to `send` in POSIX, though it also supports writing 1909 /// the data from multiple buffers in the manner of `writev`. 1910 #[no_mangle] 1911 pub unsafe extern "C" fn sock_send( 1912 fd: Fd, 1913 si_data_ptr: *const Ciovec, 1914 si_data_len: usize, 1915 si_flags: Siflags, 1916 so_datalen: *mut Size, 1917 ) -> Errno { 1918 unreachable!() 1919 } 1920 1921 /// Shut down socket send and receive channels. 1922 /// Note: This is similar to `shutdown` in POSIX. 1923 #[no_mangle] 1924 pub unsafe extern "C" fn sock_shutdown(fd: Fd, how: Sdflags) -> Errno { 1925 unreachable!() 1926 } 1927 1928 fn datetime_to_timestamp(datetime: filesystem::Datetime) -> Timestamp { 1929 u64::from(datetime.nanoseconds).saturating_add(datetime.seconds.saturating_mul(1_000_000_000)) 1930 } 1931 1932 fn at_flags_from_lookupflags(flags: Lookupflags) -> filesystem::PathFlags { 1933 if flags & LOOKUPFLAGS_SYMLINK_FOLLOW == LOOKUPFLAGS_SYMLINK_FOLLOW { 1934 filesystem::PathFlags::SYMLINK_FOLLOW 1935 } else { 1936 filesystem::PathFlags::empty() 1937 } 1938 } 1939 1940 fn o_flags_from_oflags(flags: Oflags) -> filesystem::OpenFlags { 1941 let mut o_flags = filesystem::OpenFlags::empty(); 1942 if flags & OFLAGS_CREAT == OFLAGS_CREAT { 1943 o_flags |= filesystem::OpenFlags::CREATE; 1944 } 1945 if flags & OFLAGS_DIRECTORY == OFLAGS_DIRECTORY { 1946 o_flags |= filesystem::OpenFlags::DIRECTORY; 1947 } 1948 if flags & OFLAGS_EXCL == OFLAGS_EXCL { 1949 o_flags |= filesystem::OpenFlags::EXCLUSIVE; 1950 } 1951 if flags & OFLAGS_TRUNC == OFLAGS_TRUNC { 1952 o_flags |= filesystem::OpenFlags::TRUNCATE; 1953 } 1954 o_flags 1955 } 1956 1957 fn descriptor_flags_from_flags(rights: Rights, fdflags: Fdflags) -> filesystem::DescriptorFlags { 1958 let mut flags = filesystem::DescriptorFlags::empty(); 1959 if rights & wasi::RIGHTS_FD_READ == wasi::RIGHTS_FD_READ { 1960 flags |= filesystem::DescriptorFlags::READ; 1961 } 1962 if rights & wasi::RIGHTS_FD_WRITE == wasi::RIGHTS_FD_WRITE { 1963 flags |= filesystem::DescriptorFlags::WRITE; 1964 } 1965 if fdflags & wasi::FDFLAGS_SYNC == wasi::FDFLAGS_SYNC { 1966 flags |= filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC; 1967 } 1968 if fdflags & wasi::FDFLAGS_DSYNC == wasi::FDFLAGS_DSYNC { 1969 flags |= filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC; 1970 } 1971 if fdflags & wasi::FDFLAGS_RSYNC == wasi::FDFLAGS_RSYNC { 1972 flags |= filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC; 1973 } 1974 flags 1975 } 1976 1977 impl From<filesystem::ErrorCode> for Errno { 1978 #[inline(never)] // Disable inlining as this is bulky and relatively cold. 1979 fn from(err: filesystem::ErrorCode) -> Errno { 1980 match err { 1981 // Use a black box to prevent the optimizer from generating a 1982 // lookup table, which would require a static initializer. 1983 filesystem::ErrorCode::Access => black_box(ERRNO_ACCES), 1984 filesystem::ErrorCode::WouldBlock => ERRNO_AGAIN, 1985 filesystem::ErrorCode::Already => ERRNO_ALREADY, 1986 filesystem::ErrorCode::BadDescriptor => ERRNO_BADF, 1987 filesystem::ErrorCode::Busy => ERRNO_BUSY, 1988 filesystem::ErrorCode::Deadlock => ERRNO_DEADLK, 1989 filesystem::ErrorCode::Quota => ERRNO_DQUOT, 1990 filesystem::ErrorCode::Exist => ERRNO_EXIST, 1991 filesystem::ErrorCode::FileTooLarge => ERRNO_FBIG, 1992 filesystem::ErrorCode::IllegalByteSequence => ERRNO_ILSEQ, 1993 filesystem::ErrorCode::InProgress => ERRNO_INPROGRESS, 1994 filesystem::ErrorCode::Interrupted => ERRNO_INTR, 1995 filesystem::ErrorCode::Invalid => ERRNO_INVAL, 1996 filesystem::ErrorCode::Io => ERRNO_IO, 1997 filesystem::ErrorCode::IsDirectory => ERRNO_ISDIR, 1998 filesystem::ErrorCode::Loop => ERRNO_LOOP, 1999 filesystem::ErrorCode::TooManyLinks => ERRNO_MLINK, 2000 filesystem::ErrorCode::MessageSize => ERRNO_MSGSIZE, 2001 filesystem::ErrorCode::NameTooLong => ERRNO_NAMETOOLONG, 2002 filesystem::ErrorCode::NoDevice => ERRNO_NODEV, 2003 filesystem::ErrorCode::NoEntry => ERRNO_NOENT, 2004 filesystem::ErrorCode::NoLock => ERRNO_NOLCK, 2005 filesystem::ErrorCode::InsufficientMemory => ERRNO_NOMEM, 2006 filesystem::ErrorCode::InsufficientSpace => ERRNO_NOSPC, 2007 filesystem::ErrorCode::Unsupported => ERRNO_NOTSUP, 2008 filesystem::ErrorCode::NotDirectory => ERRNO_NOTDIR, 2009 filesystem::ErrorCode::NotEmpty => ERRNO_NOTEMPTY, 2010 filesystem::ErrorCode::NotRecoverable => ERRNO_NOTRECOVERABLE, 2011 filesystem::ErrorCode::NoTty => ERRNO_NOTTY, 2012 filesystem::ErrorCode::NoSuchDevice => ERRNO_NXIO, 2013 filesystem::ErrorCode::Overflow => ERRNO_OVERFLOW, 2014 filesystem::ErrorCode::NotPermitted => ERRNO_PERM, 2015 filesystem::ErrorCode::Pipe => ERRNO_PIPE, 2016 filesystem::ErrorCode::ReadOnly => ERRNO_ROFS, 2017 filesystem::ErrorCode::InvalidSeek => ERRNO_SPIPE, 2018 filesystem::ErrorCode::TextFileBusy => ERRNO_TXTBSY, 2019 filesystem::ErrorCode::CrossDevice => ERRNO_XDEV, 2020 } 2021 } 2022 } 2023 2024 impl From<filesystem::DescriptorType> for wasi::Filetype { 2025 fn from(ty: filesystem::DescriptorType) -> wasi::Filetype { 2026 match ty { 2027 filesystem::DescriptorType::RegularFile => FILETYPE_REGULAR_FILE, 2028 filesystem::DescriptorType::Directory => FILETYPE_DIRECTORY, 2029 filesystem::DescriptorType::BlockDevice => FILETYPE_BLOCK_DEVICE, 2030 filesystem::DescriptorType::CharacterDevice => FILETYPE_CHARACTER_DEVICE, 2031 // preview1 never had a FIFO code. 2032 filesystem::DescriptorType::Fifo => FILETYPE_UNKNOWN, 2033 // TODO: Add a way to disginguish between FILETYPE_SOCKET_STREAM and 2034 // FILETYPE_SOCKET_DGRAM. 2035 filesystem::DescriptorType::Socket => unreachable!(), 2036 filesystem::DescriptorType::SymbolicLink => FILETYPE_SYMBOLIC_LINK, 2037 filesystem::DescriptorType::Unknown => FILETYPE_UNKNOWN, 2038 } 2039 } 2040 } 2041 2042 #[repr(C)] 2043 pub struct File { 2044 /// The handle to the preview2 descriptor that this file is referencing. 2045 fd: filesystem::Descriptor, 2046 2047 /// The descriptor type, as supplied by filesystem::get_type at opening 2048 descriptor_type: filesystem::DescriptorType, 2049 2050 /// The current-position pointer. 2051 position: Cell<filesystem::Filesize>, 2052 2053 /// In append mode, all writes append to the file. 2054 append: bool, 2055 2056 /// In blocking mode, read and write calls dispatch to blocking_read and 2057 /// blocking_write on the underlying streams. When false, read and write 2058 /// dispatch to stream's plain read and write. 2059 blocking: bool, 2060 } 2061 2062 impl File { 2063 fn is_dir(&self) -> bool { 2064 match self.descriptor_type { 2065 filesystem::DescriptorType::Directory => true, 2066 _ => false, 2067 } 2068 } 2069 } 2070 2071 const PAGE_SIZE: usize = 65536; 2072 2073 /// The maximum path length. WASI doesn't explicitly guarantee this, but all 2074 /// popular OS's have a `PATH_MAX` of at most 4096, so that's enough for this 2075 /// polyfill. 2076 const PATH_MAX: usize = 4096; 2077 2078 /// Maximum number of bytes to cache for a `wasi::Dirent` plus its path name. 2079 const DIRENT_CACHE: usize = 256; 2080 2081 /// A canary value to detect memory corruption within `State`. 2082 const MAGIC: u32 = u32::from_le_bytes(*b"ugh!"); 2083 2084 #[repr(C)] // used for now to keep magic1 and magic2 at the start and end 2085 struct State { 2086 /// A canary constant value located at the beginning of this structure to 2087 /// try to catch memory corruption coming from the bottom. 2088 magic1: u32, 2089 2090 /// Used to coordinate allocations of `cabi_import_realloc` 2091 import_alloc: ImportAlloc, 2092 2093 /// Storage of mapping from preview1 file descriptors to preview2 file 2094 /// descriptors. 2095 /// 2096 /// Do not use this member directly - use State::descriptors() to ensure 2097 /// lazy initialization happens. 2098 descriptors: RefCell<Option<Descriptors>>, 2099 2100 /// Auxiliary storage to handle the `path_readlink` function. 2101 path_buf: UnsafeCell<MaybeUninit<[u8; PATH_MAX]>>, 2102 2103 /// Long-lived bump allocated memory arena. 2104 /// 2105 /// This is used for the cabi_export_realloc to allocate data passed to the 2106 /// `run` entrypoint. Allocations in this arena are safe to use for 2107 /// the lifetime of the State struct. It may also be used for import allocations 2108 /// which need to be long-lived, by using `import_alloc.with_arena`. 2109 long_lived_arena: BumpArena, 2110 2111 /// Arguments. Initialized lazily. Access with `State::get_args` to take care of 2112 /// initialization. 2113 args: Cell<Option<&'static [WasmStr]>>, 2114 2115 /// Environment variables. Initialized lazily. Access with `State::get_environment` 2116 /// to take care of initialization. 2117 env_vars: Cell<Option<&'static [StrTuple]>>, 2118 2119 /// Cache for the `fd_readdir` call for a final `wasi::Dirent` plus path 2120 /// name that didn't fit into the caller's buffer. 2121 dirent_cache: DirentCache, 2122 2123 /// The string `..` for use by the directory iterator. 2124 dotdot: [UnsafeCell<u8>; 2], 2125 2126 /// Another canary constant located at the end of the structure to catch 2127 /// memory corruption coming from the bottom. 2128 magic2: u32, 2129 } 2130 2131 struct DirentCache { 2132 stream: Cell<Option<DirectoryEntryStream>>, 2133 for_fd: Cell<wasi::Fd>, 2134 cookie: Cell<wasi::Dircookie>, 2135 cached_dirent: Cell<wasi::Dirent>, 2136 path_data: UnsafeCell<MaybeUninit<[u8; DIRENT_CACHE]>>, 2137 } 2138 2139 struct DirectoryEntryStream(filesystem::DirectoryEntryStream); 2140 2141 impl Drop for DirectoryEntryStream { 2142 fn drop(&mut self) { 2143 filesystem::drop_directory_entry_stream(self.0); 2144 } 2145 } 2146 2147 #[repr(C)] 2148 pub struct WasmStr { 2149 ptr: *const u8, 2150 len: usize, 2151 } 2152 2153 #[repr(C)] 2154 pub struct WasmStrList { 2155 base: *const WasmStr, 2156 len: usize, 2157 } 2158 2159 #[repr(C)] 2160 pub struct StrTuple { 2161 key: WasmStr, 2162 value: WasmStr, 2163 } 2164 2165 #[derive(Copy, Clone)] 2166 #[repr(C)] 2167 pub struct StrTupleList { 2168 base: *const StrTuple, 2169 len: usize, 2170 } 2171 2172 const fn bump_arena_size() -> usize { 2173 // The total size of the struct should be a page, so start there 2174 let mut start = PAGE_SIZE; 2175 2176 // Remove the big chunks of the struct, the `path_buf` and `descriptors` 2177 // fields. 2178 start -= PATH_MAX; 2179 start -= size_of::<Descriptors>(); 2180 start -= size_of::<DirentCache>(); 2181 2182 // Remove miscellaneous metadata also stored in state. 2183 start -= 16 * size_of::<usize>(); 2184 2185 // Everything else is the `command_data` allocation. 2186 start 2187 } 2188 2189 // Statically assert that the `State` structure is the size of a wasm page. This 2190 // mostly guarantees that it's not larger than one page which is relied upon 2191 // below. 2192 const _: () = { 2193 let _size_assert: [(); PAGE_SIZE] = [(); size_of::<RefCell<State>>()]; 2194 }; 2195 2196 #[allow(unused)] 2197 #[repr(i32)] 2198 enum AllocationState { 2199 StackUnallocated, 2200 StackAllocating, 2201 StackAllocated, 2202 StateAllocating, 2203 StateAllocated, 2204 } 2205 2206 #[allow(improper_ctypes)] 2207 extern "C" { 2208 fn get_state_ptr() -> *const RefCell<State>; 2209 fn set_state_ptr(state: *const RefCell<State>); 2210 fn get_allocation_state() -> AllocationState; 2211 fn set_allocation_state(state: AllocationState); 2212 fn get_stderr_stream() -> Fd; 2213 fn set_stderr_stream(fd: Fd); 2214 } 2215 2216 impl State { 2217 fn with(f: impl FnOnce(&State) -> Result<(), Errno>) -> Errno { 2218 let ptr = State::ptr(); 2219 let ptr = ptr.try_borrow().unwrap_or_else(|_| unreachable!()); 2220 assert_eq!(ptr.magic1, MAGIC); 2221 assert_eq!(ptr.magic2, MAGIC); 2222 let ret = f(&*ptr); 2223 match ret { 2224 Ok(()) => ERRNO_SUCCESS, 2225 Err(err) => err, 2226 } 2227 } 2228 2229 fn with_mut(f: impl FnOnce(&mut State) -> Result<(), Errno>) -> Errno { 2230 let ptr = State::ptr(); 2231 let mut ptr = ptr.try_borrow_mut().unwrap_or_else(|_| unreachable!()); 2232 assert_eq!(ptr.magic1, MAGIC); 2233 assert_eq!(ptr.magic2, MAGIC); 2234 let ret = f(&mut *ptr); 2235 match ret { 2236 Ok(()) => ERRNO_SUCCESS, 2237 Err(err) => err, 2238 } 2239 } 2240 2241 fn ptr() -> &'static RefCell<State> { 2242 unsafe { 2243 let mut ptr = get_state_ptr(); 2244 if ptr.is_null() { 2245 ptr = State::new(); 2246 set_state_ptr(ptr); 2247 } 2248 &*ptr 2249 } 2250 } 2251 2252 #[cold] 2253 fn new() -> &'static RefCell<State> { 2254 #[link(wasm_import_module = "__main_module__")] 2255 extern "C" { 2256 fn cabi_realloc( 2257 old_ptr: *mut u8, 2258 old_len: usize, 2259 align: usize, 2260 new_len: usize, 2261 ) -> *mut u8; 2262 } 2263 2264 assert!(matches!( 2265 unsafe { get_allocation_state() }, 2266 AllocationState::StackAllocated 2267 )); 2268 2269 unsafe { set_allocation_state(AllocationState::StateAllocating) }; 2270 2271 let ret = unsafe { 2272 cabi_realloc( 2273 ptr::null_mut(), 2274 0, 2275 mem::align_of::<RefCell<State>>(), 2276 mem::size_of::<RefCell<State>>(), 2277 ) as *mut RefCell<State> 2278 }; 2279 2280 unsafe { set_allocation_state(AllocationState::StateAllocated) }; 2281 2282 unsafe { 2283 ret.write(RefCell::new(State { 2284 magic1: MAGIC, 2285 magic2: MAGIC, 2286 import_alloc: ImportAlloc::new(), 2287 descriptors: RefCell::new(None), 2288 path_buf: UnsafeCell::new(MaybeUninit::uninit()), 2289 long_lived_arena: BumpArena::new(), 2290 args: Cell::new(None), 2291 env_vars: Cell::new(None), 2292 dirent_cache: DirentCache { 2293 stream: Cell::new(None), 2294 for_fd: Cell::new(0), 2295 cookie: Cell::new(wasi::DIRCOOKIE_START), 2296 cached_dirent: Cell::new(wasi::Dirent { 2297 d_next: 0, 2298 d_ino: 0, 2299 d_type: FILETYPE_UNKNOWN, 2300 d_namlen: 0, 2301 }), 2302 path_data: UnsafeCell::new(MaybeUninit::uninit()), 2303 }, 2304 dotdot: [UnsafeCell::new(b'.'), UnsafeCell::new(b'.')], 2305 })); 2306 &*ret 2307 } 2308 } 2309 2310 /// Accessor for the descriptors member that ensures it is properly initialized 2311 fn descriptors<'a>(&'a self) -> impl Deref<Target = Descriptors> + 'a { 2312 let mut d = self 2313 .descriptors 2314 .try_borrow_mut() 2315 .unwrap_or_else(|_| unreachable!()); 2316 if d.is_none() { 2317 *d = Some(Descriptors::new(&self.import_alloc, &self.long_lived_arena)); 2318 } 2319 RefMut::map(d, |d| d.as_mut().unwrap_or_else(|| unreachable!())) 2320 } 2321 2322 /// Mut accessor for the descriptors member that ensures it is properly initialized 2323 fn descriptors_mut<'a>(&'a mut self) -> impl DerefMut + Deref<Target = Descriptors> + 'a { 2324 let mut d = self 2325 .descriptors 2326 .try_borrow_mut() 2327 .unwrap_or_else(|_| unreachable!()); 2328 if d.is_none() { 2329 *d = Some(Descriptors::new(&self.import_alloc, &self.long_lived_arena)); 2330 } 2331 RefMut::map(d, |d| d.as_mut().unwrap_or_else(|| unreachable!())) 2332 } 2333 2334 fn get_environment(&self) -> &[StrTuple] { 2335 if self.env_vars.get().is_none() { 2336 #[link(wasm_import_module = "environment")] 2337 extern "C" { 2338 #[link_name = "get-environment"] 2339 fn get_environment_import(rval: *mut StrTupleList); 2340 } 2341 let mut list = StrTupleList { 2342 base: std::ptr::null(), 2343 len: 0, 2344 }; 2345 self.import_alloc 2346 .with_arena(&self.long_lived_arena, || unsafe { 2347 get_environment_import(&mut list as *mut _) 2348 }); 2349 self.env_vars.set(Some(unsafe { 2350 /* allocation comes from long lived arena, so it is safe to 2351 * cast this to a &'static slice: */ 2352 std::slice::from_raw_parts(list.base, list.len) 2353 })); 2354 } 2355 self.env_vars.get().trapping_unwrap() 2356 } 2357 2358 fn get_args(&self) -> &[WasmStr] { 2359 if self.args.get().is_none() { 2360 #[link(wasm_import_module = "environment")] 2361 extern "C" { 2362 #[link_name = "get-arguments"] 2363 fn get_args_import(rval: *mut WasmStrList); 2364 } 2365 let mut list = WasmStrList { 2366 base: std::ptr::null(), 2367 len: 0, 2368 }; 2369 self.import_alloc 2370 .with_arena(&self.long_lived_arena, || unsafe { 2371 get_args_import(&mut list as *mut _) 2372 }); 2373 self.args.set(Some(unsafe { 2374 /* allocation comes from long lived arena, so it is safe to 2375 * cast this to a &'static slice: */ 2376 std::slice::from_raw_parts(list.base, list.len) 2377 })); 2378 } 2379 self.args.get().trapping_unwrap() 2380 } 2381 } 2382