Skip to content

Commit

Permalink
Implement path_link for Windows. (bytecodealliance#1199)
Browse files Browse the repository at this point in the history
Implement path_link for Windows.
  • Loading branch information
marmistrz authored Apr 3, 2020
1 parent 7728703 commit f6e3ab0
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 9 deletions.
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
11 changes: 8 additions & 3 deletions crates/test-programs/wasi-tests/src/bin/path_link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,24 +74,29 @@ unsafe fn check_rights(orig_fd: wasi::Fd, link_fd: wasi::Fd) {
fdstats_assert_eq(orig_fdstat, link_fdstat);
}

// Extra calls of fd_close are needed for Windows, which will not remove
// the directory until all handles are closed.
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");
let link_fd = open_link(dir_fd, "link");
check_rights(file_fd, link_fd);
wasi::fd_close(link_fd).expect("Closing link_fd"); // needed for Windows
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");
let 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::fd_close(subdir_fd).expect("Closing subdir_fd"); // needed for Windows
wasi::fd_close(link_fd).expect("Closing link_fd"); // needed for Windows
wasi::path_remove_directory(dir_fd, "subdir").expect("removing a subdirectory");

// Create a link to a path that already exists
Expand Down Expand Up @@ -188,7 +193,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
46 changes: 42 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, Metadata, OpenOptions};
Expand Down Expand Up @@ -164,11 +165,48 @@ 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 {
winerror::ERROR_ACCESS_DENIED => {
// If an attempt is made to create a hard link to a directory, POSIX-compliant
// implementations of link return `EPERM`, but `ERROR_ACCESS_DENIED` is converted
// to `EACCES`. We detect and correct this case here.
if fs::metadata(&old_path).map(|m| m.is_dir()).unwrap_or(false) {
return Err(Errno::Perm);
}
}
_ => {}
}

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

pub(crate) fn open(
Expand Down

0 comments on commit f6e3ab0

Please sign in to comment.