Skip to content

Commit cb4a430

Browse files
committed
std: Use libc for filesystem ops on WASI targets
This commit is a large change to the implementation of filesystem and other system-related operations on WASI targets. Previously the standard library explicitly used the `wasi` crate at the 0.11.x version track which means that it used WASIp1 APIs directly. This meant that `std` was hard-coded to use WASIp1 syscalls and there was no separate implementation for the WASIp{2,3} targets, for example. The high-level goal of this commit is to decouple this interaction and avoid the use of the `wasi` crate on the WASIp2 target. Historically when WASIp1 was originally added to Rust the wasi-libc library was in a much different position than it is today. Nowadays Rust already depends on wasi-libc on WASI targets for things like memory allocation and environment variable management. As a libc library it also has all the functions necessary to implement all filesystem operations Rust wants. Recently wasi-libc additionally was updated to use WASIp2 APIs directly on the `wasm32-wasip2` target instead of using `wasm32-wasip1` APIs. This commit is leveraging this work by enabling Rust to completely sever the dependence on WASIp1 APIs when compiling for `wasm32-wasip2`. This is also intended to make it easier to migrate to `wasm32-wasip3` internally in the future where now only libc need be updated and Rust doesn't need to explicitly change as well. This commit fairly heavily changes the internals of the implementation of WASI filesystem operations. The basis of implementation is now libc-style APIs as opposed to WASIp1-style-APIs which necessitated a number of changes throughout. Additionally the `std::os::wasi::fs` module has had a few API-breaking changes as well, but this module is also unstable. While this module has been "stable" since inception in the sense that it hasn't changed, this PR is what the unstable status was sort of trying to buy in terms of future flexibility to make changes. For users of stable `std::fs` interfaces this is not intended to be a breaking change but there nevertheless may be minor issues here and there. Concrete changes here are: * `std` for `wasm32-wasip2` no longer depends on `wasi 0.11.x` * The implementation of `std::os::wasi::fs`, which was previously unstable and still is, is now only available on `wasm32-wasip1` and is implemented largely directly in terms of WASIp1 APIs by directly using the `wasi 0.11.x` crate. * `std::os::wasi::fs::MetadataExt`, which was previously unstable and still is, has removed methods that duplicate functionality in `std::fs::Metadata` as there's no point in having them. * `std::os::wasi::fs::FileTypeExt`, which was previously unstable and still is, lost the ability to distinguish between dgram/stream sockets. WASIp1 sockets were never really supported, though, so this isn't much of a loss. * The internal `WasiFd` type has been "gutted" to have far fewer methods internally. This still represents a file descriptor owned by `wasi-libc`, however. * The `std::sys::fs::wasi` implementation is overhauled to largely use the `libc` crate and its APIs directly (which go to wasi-libc). This drastically changes the `ReadDir` implementation, for example. * The `std::sys::fs::wasi::OpenOptions` API now has two ways of opening files. By default `libc::open` is used but of `std::os::wasi::fs::OpenOptionsExt` is used then a WASIp1-specific API is used to open a file. * Types like `FileAttr` and `FileType` now use libc representations at-rest instead of WASIp1 internals. * Various transmutations for iovec-style APIs are consolidated into a WASI-specific module close to the definition of the `IoSlice{,Mut}` types to make it easier to audit unsafe guarantees. * The `wasip2` OS module gained a custom implementation of the `helpers` module as the WASIp1 version of this module is no longer applicable. I'd like to do follow-up PRs after this one to reorganize `std::os::wasi*` a bit to be more amenable for WASIp{2,3} targets as well, but that'll come in a future PR. For now this is tasked with the high-level goal of removing the dependency on WASIp1 APIs in the standard library by relying on wasi-libc to natively use WASIp2 APIs.
1 parent 9725c4b commit cb4a430

File tree

12 files changed

+751
-635
lines changed

12 files changed

+751
-635
lines changed

library/std/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ hermit-abi = { version = "0.5.0", features = [
7575
'rustc-dep-of-std',
7676
], public = true }
7777

78-
[target.'cfg(target_os = "wasi")'.dependencies]
78+
[target.'cfg(all(target_os = "wasi", target_env = "p1"))'.dependencies]
7979
wasi = { version = "0.11.0", features = [
8080
'rustc-dep-of-std',
8181
], default-features = false }

library/std/src/os/wasi/fs.rs

Lines changed: 69 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! WASI-specific extensions to primitives in the [`std::fs`] module.
1+
//! WASIp1-specific extensions to primitives in the [`std::fs`] module.
22
//!
33
//! [`std::fs`]: crate::fs
44
@@ -9,9 +9,11 @@
99
use io::{Read, Write};
1010

1111
use crate::ffi::OsStr;
12-
use crate::fs::{self, File, Metadata, OpenOptions};
12+
use crate::fs::{self, File, OpenOptions};
1313
use crate::io::{self, IoSlice, IoSliceMut};
14+
use crate::os::fd::AsRawFd;
1415
use crate::path::{Path, PathBuf};
16+
use crate::sys::err2io;
1517
use crate::sys_common::{AsInner, AsInnerMut, FromInner};
1618

1719
/// WASI-specific extensions to [`File`].
@@ -200,7 +202,7 @@ pub trait FileExt {
200202
///
201203
/// This corresponds to the `path_filestat_get` syscall.
202204
#[doc(alias = "path_filestat_get")]
203-
fn metadata_at<P: AsRef<Path>>(&self, lookup_flags: u32, path: P) -> io::Result<Metadata>;
205+
fn metadata_at<P: AsRef<Path>>(&self, lookup_flags: i32, path: P) -> io::Result<fs::Metadata>;
204206

205207
/// Unlinks a file.
206208
///
@@ -224,19 +226,24 @@ pub trait FileExt {
224226

225227
impl FileExt for fs::File {
226228
fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
227-
self.as_inner().as_inner().pread(bufs, offset)
229+
let bufs = crate::sys::io::IoSliceMut::as_wasip1_slice(bufs);
230+
unsafe { wasi::fd_pread(self.as_raw_fd() as wasi::Fd, bufs, offset).map_err(err2io) }
228231
}
229232

230233
fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
231-
self.as_inner().as_inner().pwrite(bufs, offset)
234+
let bufs = crate::sys::io::IoSlice::as_wasip1_slice(bufs);
235+
unsafe { wasi::fd_pwrite(self.as_raw_fd() as wasi::Fd, bufs, offset).map_err(err2io) }
232236
}
233237

234238
fn fdstat_set_flags(&self, flags: u16) -> io::Result<()> {
235-
self.as_inner().as_inner().set_flags(flags)
239+
unsafe { wasi::fd_fdstat_set_flags(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) }
236240
}
237241

238242
fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()> {
239-
self.as_inner().as_inner().set_rights(rights, inheriting)
243+
unsafe {
244+
wasi::fd_fdstat_set_rights(self.as_raw_fd() as wasi::Fd, rights, inheriting)
245+
.map_err(err2io)
246+
}
240247
}
241248

242249
fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> {
@@ -255,32 +262,37 @@ impl FileExt for fs::File {
255262
}
256263
};
257264

258-
self.as_inner().as_inner().advise(offset, len, advice)
265+
unsafe {
266+
wasi::fd_advise(self.as_raw_fd() as wasi::Fd, offset, len, advice).map_err(err2io)
267+
}
259268
}
260269

261270
fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
262-
self.as_inner().as_inner().allocate(offset, len)
271+
unsafe { wasi::fd_allocate(self.as_raw_fd() as wasi::Fd, offset, len).map_err(err2io) }
263272
}
264273

265274
fn create_directory<P: AsRef<Path>>(&self, dir: P) -> io::Result<()> {
266-
self.as_inner().as_inner().create_directory(osstr2str(dir.as_ref().as_ref())?)
275+
let path = osstr2str(dir.as_ref().as_ref())?;
276+
unsafe { wasi::path_create_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) }
267277
}
268278

269279
fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf> {
270-
self.as_inner().read_link(path.as_ref())
280+
self.as_inner().readlink_at(path.as_ref())
271281
}
272282

273-
fn metadata_at<P: AsRef<Path>>(&self, lookup_flags: u32, path: P) -> io::Result<Metadata> {
283+
fn metadata_at<P: AsRef<Path>>(&self, lookup_flags: i32, path: P) -> io::Result<fs::Metadata> {
274284
let m = self.as_inner().metadata_at(lookup_flags, path.as_ref())?;
275285
Ok(FromInner::from_inner(m))
276286
}
277287

278288
fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
279-
self.as_inner().as_inner().unlink_file(osstr2str(path.as_ref().as_ref())?)
289+
let path = osstr2str(path.as_ref().as_ref())?;
290+
unsafe { wasi::path_unlink_file(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) }
280291
}
281292

282293
fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
283-
self.as_inner().as_inner().remove_directory(osstr2str(path.as_ref().as_ref())?)
294+
let path = osstr2str(path.as_ref().as_ref())?;
295+
unsafe { wasi::path_remove_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) }
284296
}
285297
}
286298

@@ -355,42 +367,42 @@ pub trait OpenOptionsExt {
355367

356368
impl OpenOptionsExt for OpenOptions {
357369
fn lookup_flags(&mut self, flags: u32) -> &mut OpenOptions {
358-
self.as_inner_mut().lookup_flags(flags);
370+
self.as_inner_mut().wasip1_lookup_flags(flags);
359371
self
360372
}
361373

362374
fn directory(&mut self, dir: bool) -> &mut OpenOptions {
363-
self.as_inner_mut().directory(dir);
375+
self.as_inner_mut().wasip1_directory(dir);
364376
self
365377
}
366378

367379
fn dsync(&mut self, enabled: bool) -> &mut OpenOptions {
368-
self.as_inner_mut().dsync(enabled);
380+
self.as_inner_mut().wasip1_dsync(enabled);
369381
self
370382
}
371383

372384
fn nonblock(&mut self, enabled: bool) -> &mut OpenOptions {
373-
self.as_inner_mut().nonblock(enabled);
385+
self.as_inner_mut().wasip1_nonblock(enabled);
374386
self
375387
}
376388

377389
fn rsync(&mut self, enabled: bool) -> &mut OpenOptions {
378-
self.as_inner_mut().rsync(enabled);
390+
self.as_inner_mut().wasip1_rsync(enabled);
379391
self
380392
}
381393

382394
fn sync(&mut self, enabled: bool) -> &mut OpenOptions {
383-
self.as_inner_mut().sync(enabled);
395+
self.as_inner_mut().wasip1_sync(enabled);
384396
self
385397
}
386398

387399
fn fs_rights_base(&mut self, rights: u64) -> &mut OpenOptions {
388-
self.as_inner_mut().fs_rights_base(rights);
400+
self.as_inner_mut().wasip1_fs_rights_base(rights);
389401
self
390402
}
391403

392404
fn fs_rights_inheriting(&mut self, rights: u64) -> &mut OpenOptions {
393-
self.as_inner_mut().fs_rights_inheriting(rights);
405+
self.as_inner_mut().wasip1_fs_rights_inheriting(rights);
394406
self
395407
}
396408

@@ -408,37 +420,17 @@ pub trait MetadataExt {
408420
fn ino(&self) -> u64;
409421
/// Returns the `st_nlink` field of the internal `filestat_t`
410422
fn nlink(&self) -> u64;
411-
/// Returns the `st_size` field of the internal `filestat_t`
412-
fn size(&self) -> u64;
413-
/// Returns the `st_atim` field of the internal `filestat_t`
414-
fn atim(&self) -> u64;
415-
/// Returns the `st_mtim` field of the internal `filestat_t`
416-
fn mtim(&self) -> u64;
417-
/// Returns the `st_ctim` field of the internal `filestat_t`
418-
fn ctim(&self) -> u64;
419423
}
420424

421425
impl MetadataExt for fs::Metadata {
422426
fn dev(&self) -> u64 {
423-
self.as_inner().as_wasi().dev
427+
self.as_inner().as_libc().st_dev
424428
}
425429
fn ino(&self) -> u64 {
426-
self.as_inner().as_wasi().ino
430+
self.as_inner().as_libc().st_ino
427431
}
428432
fn nlink(&self) -> u64 {
429-
self.as_inner().as_wasi().nlink
430-
}
431-
fn size(&self) -> u64 {
432-
self.as_inner().as_wasi().size
433-
}
434-
fn atim(&self) -> u64 {
435-
self.as_inner().as_wasi().atim
436-
}
437-
fn mtim(&self) -> u64 {
438-
self.as_inner().as_wasi().mtim
439-
}
440-
fn ctim(&self) -> u64 {
441-
self.as_inner().as_wasi().ctim
433+
self.as_inner().as_libc().st_nlink
442434
}
443435
}
444436

@@ -451,28 +443,19 @@ pub trait FileTypeExt {
451443
fn is_block_device(&self) -> bool;
452444
/// Returns `true` if this file type is a character device.
453445
fn is_char_device(&self) -> bool;
454-
/// Returns `true` if this file type is a socket datagram.
455-
fn is_socket_dgram(&self) -> bool;
456-
/// Returns `true` if this file type is a socket stream.
457-
fn is_socket_stream(&self) -> bool;
458446
/// Returns `true` if this file type is any type of socket.
459-
fn is_socket(&self) -> bool {
460-
self.is_socket_stream() || self.is_socket_dgram()
461-
}
447+
fn is_socket(&self) -> bool;
462448
}
463449

464450
impl FileTypeExt for fs::FileType {
465451
fn is_block_device(&self) -> bool {
466-
self.as_inner().bits() == wasi::FILETYPE_BLOCK_DEVICE
452+
self.as_inner().mode() == libc::S_IFBLK
467453
}
468454
fn is_char_device(&self) -> bool {
469-
self.as_inner().bits() == wasi::FILETYPE_CHARACTER_DEVICE
470-
}
471-
fn is_socket_dgram(&self) -> bool {
472-
self.as_inner().bits() == wasi::FILETYPE_SOCKET_DGRAM
455+
self.as_inner().mode() == libc::S_IFCHR
473456
}
474-
fn is_socket_stream(&self) -> bool {
475-
self.as_inner().bits() == wasi::FILETYPE_SOCKET_STREAM
457+
fn is_socket(&self) -> bool {
458+
self.as_inner().mode() == libc::S_IFSOCK
476459
}
477460
}
478461

@@ -499,12 +482,16 @@ pub fn link<P: AsRef<Path>, U: AsRef<Path>>(
499482
new_fd: &File,
500483
new_path: U,
501484
) -> io::Result<()> {
502-
old_fd.as_inner().as_inner().link(
503-
old_flags,
504-
osstr2str(old_path.as_ref().as_ref())?,
505-
new_fd.as_inner().as_inner(),
506-
osstr2str(new_path.as_ref().as_ref())?,
507-
)
485+
unsafe {
486+
wasi::path_link(
487+
old_fd.as_raw_fd() as wasi::Fd,
488+
old_flags,
489+
osstr2str(old_path.as_ref().as_ref())?,
490+
new_fd.as_raw_fd() as wasi::Fd,
491+
osstr2str(new_path.as_ref().as_ref())?,
492+
)
493+
.map_err(err2io)
494+
}
508495
}
509496

510497
/// Renames a file or directory.
@@ -517,11 +504,15 @@ pub fn rename<P: AsRef<Path>, U: AsRef<Path>>(
517504
new_fd: &File,
518505
new_path: U,
519506
) -> io::Result<()> {
520-
old_fd.as_inner().as_inner().rename(
521-
osstr2str(old_path.as_ref().as_ref())?,
522-
new_fd.as_inner().as_inner(),
523-
osstr2str(new_path.as_ref().as_ref())?,
524-
)
507+
unsafe {
508+
wasi::path_rename(
509+
old_fd.as_raw_fd() as wasi::Fd,
510+
osstr2str(old_path.as_ref().as_ref())?,
511+
new_fd.as_raw_fd() as wasi::Fd,
512+
osstr2str(new_path.as_ref().as_ref())?,
513+
)
514+
.map_err(err2io)
515+
}
525516
}
526517

527518
/// Creates a symbolic link.
@@ -533,9 +524,14 @@ pub fn symlink<P: AsRef<Path>, U: AsRef<Path>>(
533524
fd: &File,
534525
new_path: U,
535526
) -> io::Result<()> {
536-
fd.as_inner()
537-
.as_inner()
538-
.symlink(osstr2str(old_path.as_ref().as_ref())?, osstr2str(new_path.as_ref().as_ref())?)
527+
unsafe {
528+
wasi::path_symlink(
529+
osstr2str(old_path.as_ref().as_ref())?,
530+
fd.as_raw_fd() as wasi::Fd,
531+
osstr2str(new_path.as_ref().as_ref())?,
532+
)
533+
.map_err(err2io)
534+
}
539535
}
540536

541537
/// Creates a symbolic link.

library/std/src/os/wasi/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#![doc(cfg(target_os = "wasi"))]
3535

3636
pub mod ffi;
37+
#[cfg(target_env = "p1")]
3738
pub mod fs;
3839
pub mod io;
3940

@@ -50,9 +51,11 @@ pub mod prelude {
5051
pub use super::ffi::{OsStrExt, OsStringExt};
5152
#[doc(no_inline)]
5253
#[stable(feature = "rust1", since = "1.0.0")]
54+
#[cfg(target_env = "p1")]
5355
pub use super::fs::FileTypeExt;
5456
#[doc(no_inline)]
5557
#[stable(feature = "rust1", since = "1.0.0")]
58+
#[cfg(target_env = "p1")]
5659
pub use super::fs::{DirEntryExt, FileExt, MetadataExt, OpenOptionsExt};
5760
#[doc(no_inline)]
5861
#[stable(feature = "rust1", since = "1.0.0")]

library/std/src/os/wasi/net/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
33
#![unstable(feature = "wasi_ext", issue = "71213")]
44

5-
use crate::sys_common::AsInner;
5+
use crate::os::fd::AsRawFd;
6+
use crate::sys::err2io;
67
use crate::{io, net};
78

89
/// WASI-specific extensions to [`std::net::TcpListener`].
@@ -17,6 +18,6 @@ pub trait TcpListenerExt {
1718

1819
impl TcpListenerExt for net::TcpListener {
1920
fn sock_accept(&self, flags: u16) -> io::Result<u32> {
20-
self.as_inner().as_inner().as_inner().sock_accept(flags)
21+
unsafe { wasi::sock_accept(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) }
2122
}
2223
}

0 commit comments

Comments
 (0)