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

Implement path_link for Windows. #1199

Merged
merged 5 commits into from
Apr 3, 2020
Merged
Show file tree
Hide file tree
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
1 change: 0 additions & 1 deletion crates/test-programs/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,6 @@ mod wasi_tests {
"dangling_symlink" => true,
"symlink_loop" => true,
"truncation_rights" => true,
"path_link" => true,
"dangling_fd" => true,
// TODO: virtfs files cannot be poll_oneoff'd yet
"poll_oneoff_virtualfs" => true,
Expand Down
66 changes: 51 additions & 15 deletions crates/test-programs/wasi-tests/src/bin/path_link.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,34 @@
#![allow(warnings)]
use more_asserts::assert_gt;
use std::{env, process};
use wasi_tests::{create_file, open_scratch_directory};

// RAII adapter for path_open. On Windows we need to explicitly close the fds
// or files may not be removed.
struct WasiFile {
peterhuene marked this conversation as resolved.
Show resolved Hide resolved
fd: wasi::Fd,
}

impl WasiFile {
unsafe fn open(dir_fd: wasi::Fd, name: &str) -> Self {
Self {
fd: open_link(dir_fd, name),
}
}

unsafe fn create_or_open(dir_fd: wasi::Fd, name: &str, flags: wasi::Oflags) -> Self {
Self {
fd: create_or_open(dir_fd, name, flags),
}
}
}

impl Drop for WasiFile {
fn drop(&mut self) {
unsafe { wasi::fd_close(self.fd).expect("Closing fd") }
}
}

const TEST_RIGHTS: wasi::Rights = wasi::RIGHTS_FD_READ
| wasi::RIGHTS_PATH_LINK_SOURCE
| wasi::RIGHTS_PATH_LINK_TARGET
Expand Down Expand Up @@ -68,30 +95,38 @@ unsafe fn check_rights(orig_fd: wasi::Fd, link_fd: wasi::Fd) {
let link_filestat = wasi::fd_filestat_get(link_fd).expect("reading filestat of the link");
filestats_assert_eq(orig_filestat, link_filestat);

// Disabled: fd_fdstat_get causes CI test failures on Windows
peterhuene marked this conversation as resolved.
Show resolved Hide resolved
// Compare Fdstats
let orig_fdstat = wasi::fd_fdstat_get(orig_fd).expect("reading fdstat of the source");
let link_fdstat = wasi::fd_fdstat_get(link_fd).expect("reading fdstat of the link");
fdstats_assert_eq(orig_fdstat, link_fdstat);
}

// Use of WasiFile is needed for Windows, which will not remove the directory until all
peterhuene marked this conversation as resolved.
Show resolved Hide resolved
// handles are closed. WasiFile does this as a part of RAII.
unsafe fn test_path_link(dir_fd: wasi::Fd) {
// Create a file
let file_fd = create_or_open(dir_fd, "file", wasi::OFLAGS_CREAT);

// Create a link in the same directory and compare rights
wasi::path_link(dir_fd, 0, "file", dir_fd, "link")
.expect("creating a link in the same directory");
let mut link_fd = open_link(dir_fd, "link");
check_rights(file_fd, link_fd);
{
let link_fd = WasiFile::open(dir_fd, "link");
check_rights(file_fd, link_fd.fd);
}
wasi::path_unlink_file(dir_fd, "link").expect("removing a link");

// Create a link in a different directory and compare rights
wasi::path_create_directory(dir_fd, "subdir").expect("creating a subdirectory");
let subdir_fd = create_or_open(dir_fd, "subdir", wasi::OFLAGS_DIRECTORY);
wasi::path_link(dir_fd, 0, "file", subdir_fd, "link").expect("creating a link in subdirectory");
link_fd = open_link(subdir_fd, "link");
check_rights(file_fd, link_fd);
wasi::path_unlink_file(subdir_fd, "link").expect("removing a link");
{
wasi::path_create_directory(dir_fd, "subdir").expect("creating a subdirectory");
let subdir_fd = WasiFile::create_or_open(dir_fd, "subdir", wasi::OFLAGS_DIRECTORY);
wasi::path_link(dir_fd, 0, "file", subdir_fd.fd, "link")
.expect("creating a link in subdirectory");
let link_fd = WasiFile::open(subdir_fd.fd, "link");
check_rights(file_fd, link_fd.fd);
wasi::path_unlink_file(subdir_fd.fd, "link").expect("removing a link");
}
wasi::path_remove_directory(dir_fd, "subdir").expect("removing a subdirectory");

// Create a link to a path that already exists
Expand Down Expand Up @@ -131,12 +166,13 @@ unsafe fn test_path_link(dir_fd: wasi::Fd) {
wasi::path_create_directory(dir_fd, "subdir").expect("creating a subdirectory");
create_or_open(dir_fd, "subdir", wasi::OFLAGS_DIRECTORY);

assert_eq!(
wasi::path_link(dir_fd, 0, "subdir", dir_fd, "link")
.expect_err("creating a link to a directory should fail")
.raw_error(),
wasi::ERRNO_PERM,
"errno should be ERRNO_PERM"
let err = wasi::path_link(dir_fd, 0, "subdir", dir_fd, "link")
.expect_err("creating a link to a directory should fail")
.raw_error();
assert!(
err == wasi::ERRNO_PERM || err == wasi::ERRNO_ACCES,
"errno should be ERRNO_PERM or ERRNO_ACCESS, was: {}",
err
);
wasi::path_remove_directory(dir_fd, "subdir").expect("removing a subdirectory");

Expand Down Expand Up @@ -188,7 +224,7 @@ unsafe fn test_path_link(dir_fd: wasi::Fd) {
"link",
)
.expect("creating a link to a file following symlinks");
link_fd = open_link(dir_fd, "link");
let link_fd = open_link(dir_fd, "link");
check_rights(file_fd, link_fd);
wasi::path_unlink_file(dir_fd, "link").expect("removing a link");
wasi::path_unlink_file(dir_fd, "symlink").expect("removing a symlink");
Expand Down
2 changes: 1 addition & 1 deletion crates/wasi-common/src/sys/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl From<io::Error> for Errno {
winerror::ERROR_DIRECTORY => Self::Notdir,
winerror::ERROR_ALREADY_EXISTS => Self::Exist,
x => {
log::debug!("unknown error value: {}", x);
log::debug!("winerror: unknown error value: {}", x);
Self::Io
}
},
Expand Down
45 changes: 41 additions & 4 deletions crates/wasi-common/src/sys/windows/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::fd;
use crate::path::PathGet;
use crate::sys::entry::OsHandle;
use crate::wasi::{types, Errno, Result};
use log::debug;
use std::convert::TryInto;
use std::ffi::{OsStr, OsString};
use std::fs::{File, OpenOptions};
Expand Down Expand Up @@ -165,11 +166,47 @@ pub(crate) fn create_directory(file: &File, path: &str) -> Result<()> {
}

pub(crate) fn link(
_resolved_old: PathGet,
_resolved_new: PathGet,
_follow_symlinks: bool,
resolved_old: PathGet,
resolved_new: PathGet,
follow_symlinks: bool,
) -> Result<()> {
unimplemented!("path_link")
use std::fs;
let mut old_path = resolved_old.concatenate()?;
let new_path = resolved_new.concatenate()?;
if follow_symlinks {
// in particular, this will return an error if the target path doesn't exist
debug!("Following symlinks for path: {:?}", old_path);
old_path = fs::canonicalize(&old_path).map_err(|e| match e.raw_os_error() {
// fs::canonicalize under Windows will return:
// * ERROR_FILE_NOT_FOUND, if it encounters a dangling symlink
// * ERROR_CANT_RESOLVE_FILENAME, if it encounters a symlink loop
Some(code) if code as u32 == winerror::ERROR_CANT_RESOLVE_FILENAME => Errno::Loop,
_ => e.into(),
})?;
}
fs::hard_link(&old_path, &new_path).or_else(|err| {
match err.raw_os_error() {
Some(code) => {
debug!("path_link at fs::hard_link error code={:?}", code);
match code as u32 {
// TODO is this needed at all????
Copy link
Member

Choose a reason for hiding this comment

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

Is this TODO comment actionable or can it be removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm removing this mapping, because there is no clear reason why it's needed, just as in #1359.

winerror::ERROR_ACCESS_DENIED => {
// does the target exist?
if new_path.exists() {
return Err(Errno::Exist);
}
}
_ => {}
}

Err(err.into())
}
None => {
log::debug!("Inconvertible OS error: {}", err);
Err(Errno::Io)
}
}
})
}

pub(crate) fn open(
Expand Down