Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pure-Rust implementation of WASI's open_parent #64434

Closed
wants to merge 5 commits into from
Closed
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 49 additions & 28 deletions src/libstd/sys/wasi/fs.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::ffi::{CStr, CString, OsStr, OsString};
use crate::ffi::{OsStr, OsString};
use crate::fmt;
use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
use crate::iter;
Expand Down Expand Up @@ -616,14 +616,37 @@ fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> {
Ok(File { fd })
}

/// Get pre-opened file descriptors and their paths.
fn get_paths() -> Result<Vec<(PathBuf, WasiFd)>, wasi::Error> {
use crate::sys::os_str::Buf;
newpavlov marked this conversation as resolved.
Show resolved Hide resolved

let mut paths = Vec::new();
for fd in 3.. {
match wasi::fd_prestat_get(fd) {
Ok(wasi::Prestat { pr_type: wasi::PREOPENTYPE_DIR, u }) => unsafe {
let name_len = u.dir.pr_name_len;
let mut buf = vec![0; name_len];
wasi::fd_prestat_dir_name(fd, &mut buf)?;
let buf = Buf { inner: buf };
newpavlov marked this conversation as resolved.
Show resolved Hide resolved
let path = PathBuf::from(OsString { inner: buf });
paths.push((path, WasiFd::from_raw(fd)));
},
Ok(_) => (),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a new preopen-type is added, it will be skipped silently. That's probably the right thing to do though.

Err(wasi::EBADF) => break,
Err(err) => return Err(err),
}
}
paths.shrink_to_fit();
Ok(paths)
newpavlov marked this conversation as resolved.
Show resolved Hide resolved
}

/// Attempts to open a bare path `p`.
///
/// WASI has no fundamental capability to do this. All syscalls and operations
/// are relative to already-open file descriptors. The C library, however,
/// manages a map of preopened file descriptors to their path, and then the C
/// library provides an API to look at this. In other words, when you want to
/// open a path `p`, you have to find a previously opened file descriptor in a
/// global table and then see if `p` is relative to that file descriptor.
/// are relative to already-open file descriptors. However, we manage a list
/// of preopened file descriptors and their path. In other words, when you want
/// to open a path `p`, you have to find a previously opened file descriptor in
/// this list and then see if `p` is relative to that file descriptor.
///
/// This function, if successful, will return two items:
///
Expand All @@ -642,32 +665,30 @@ fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> {
/// appropriate rights for performing `rights` actions.
///
/// Note that this can fail if `p` doesn't look like it can be opened relative
/// to any preopened file descriptor.
/// to any preopened file descriptor or we have failed to build the list of
/// pre-opened file descriptors.
fn open_parent(
p: &Path,
rights: wasi::Rights,
) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
let p = CString::new(p.as_os_str().as_bytes())?;
unsafe {
let mut ret = ptr::null();
let fd = libc::__wasilibc_find_relpath(p.as_ptr(), rights, 0, &mut ret);
if fd == -1 {
let msg = format!(
"failed to find a preopened file descriptor \
through which {:?} could be opened",
p
);
return Err(io::Error::new(io::ErrorKind::Other, msg));
}
let path = Path::new(OsStr::from_bytes(CStr::from_ptr(ret).to_bytes()));
) -> io::Result<(ManuallyDrop<WasiFd>, &Path)> {
use crate::sync::Once;

// FIXME: right now `path` is a pointer into `p`, the `CString` above.
// When we return `p` is deallocated and we can't use it, so we need to
// currently separately allocate `path`. If this becomes an issue though
// we should probably turn this into a closure-taking interface or take
// `&CString` and then pass off `&Path` tied to the same lifetime.
let path = path.to_path_buf();
static mut PATHS: Result<Vec<(PathBuf, WasiFd)>, wasi::Error> = unsafe {
Err(wasi::Error::new_unchecked(1))
};
static PATHS_INIT: Once = Once::new();
PATHS_INIT.call_once(|| unsafe { PATHS = get_paths(); });

return Ok((ManuallyDrop::new(WasiFd::from_raw(fd as u32)), path));
for (path, fd) in PATHS.as_ref().map_err(|&e| super::err2io(e))?.iter() {
if let Ok(path) = p.strip_prefix(path) {
return Ok((ManuallyDrop::new(*fd), path))
}
}

let msg = format!(
"failed to find a preopened file descriptor \
through which {:?} could be opened",
p
);
Err(io::Error::new(io::ErrorKind::Other, msg))
}