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