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