Implement path_link for Windows. (#1199)
Implement path_link for Windows.
This commit is contained in:
committed by
GitHub
parent
7728703ec7
commit
f6e3ab03a2
@@ -233,7 +233,6 @@ mod wasi_tests {
|
|||||||
"dangling_symlink" => true,
|
"dangling_symlink" => true,
|
||||||
"symlink_loop" => true,
|
"symlink_loop" => true,
|
||||||
"truncation_rights" => true,
|
"truncation_rights" => true,
|
||||||
"path_link" => true,
|
|
||||||
"dangling_fd" => true,
|
"dangling_fd" => true,
|
||||||
// TODO: virtfs files cannot be poll_oneoff'd yet
|
// TODO: virtfs files cannot be poll_oneoff'd yet
|
||||||
"poll_oneoff_virtualfs" => true,
|
"poll_oneoff_virtualfs" => true,
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ unsafe fn check_rights(orig_fd: wasi::Fd, link_fd: wasi::Fd) {
|
|||||||
fdstats_assert_eq(orig_fdstat, link_fdstat);
|
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) {
|
unsafe fn test_path_link(dir_fd: wasi::Fd) {
|
||||||
// Create a file
|
// Create a file
|
||||||
let file_fd = create_or_open(dir_fd, "file", wasi::OFLAGS_CREAT);
|
let file_fd = create_or_open(dir_fd, "file", wasi::OFLAGS_CREAT);
|
||||||
@@ -81,17 +83,20 @@ unsafe fn test_path_link(dir_fd: wasi::Fd) {
|
|||||||
// Create a link in the same directory and compare rights
|
// Create a link in the same directory and compare rights
|
||||||
wasi::path_link(dir_fd, 0, "file", dir_fd, "link")
|
wasi::path_link(dir_fd, 0, "file", dir_fd, "link")
|
||||||
.expect("creating a link in the same directory");
|
.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);
|
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");
|
wasi::path_unlink_file(dir_fd, "link").expect("removing a link");
|
||||||
|
|
||||||
// Create a link in a different directory and compare rights
|
// Create a link in a different directory and compare rights
|
||||||
wasi::path_create_directory(dir_fd, "subdir").expect("creating a subdirectory");
|
wasi::path_create_directory(dir_fd, "subdir").expect("creating a subdirectory");
|
||||||
let subdir_fd = create_or_open(dir_fd, "subdir", wasi::OFLAGS_DIRECTORY);
|
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");
|
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);
|
check_rights(file_fd, link_fd);
|
||||||
wasi::path_unlink_file(subdir_fd, "link").expect("removing a link");
|
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");
|
wasi::path_remove_directory(dir_fd, "subdir").expect("removing a subdirectory");
|
||||||
|
|
||||||
// Create a link to a path that already exists
|
// Create a link to a path that already exists
|
||||||
@@ -188,7 +193,7 @@ unsafe fn test_path_link(dir_fd: wasi::Fd) {
|
|||||||
"link",
|
"link",
|
||||||
)
|
)
|
||||||
.expect("creating a link to a file following symlinks");
|
.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);
|
check_rights(file_fd, link_fd);
|
||||||
wasi::path_unlink_file(dir_fd, "link").expect("removing a link");
|
wasi::path_unlink_file(dir_fd, "link").expect("removing a link");
|
||||||
wasi::path_unlink_file(dir_fd, "symlink").expect("removing a symlink");
|
wasi::path_unlink_file(dir_fd, "symlink").expect("removing a symlink");
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ impl From<io::Error> for Errno {
|
|||||||
winerror::ERROR_DIRECTORY => Self::Notdir,
|
winerror::ERROR_DIRECTORY => Self::Notdir,
|
||||||
winerror::ERROR_ALREADY_EXISTS => Self::Exist,
|
winerror::ERROR_ALREADY_EXISTS => Self::Exist,
|
||||||
x => {
|
x => {
|
||||||
log::debug!("unknown error value: {}", x);
|
log::debug!("winerror: unknown error value: {}", x);
|
||||||
Self::Io
|
Self::Io
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use crate::fd;
|
|||||||
use crate::path::PathGet;
|
use crate::path::PathGet;
|
||||||
use crate::sys::entry::OsHandle;
|
use crate::sys::entry::OsHandle;
|
||||||
use crate::wasi::{types, Errno, Result};
|
use crate::wasi::{types, Errno, Result};
|
||||||
|
use log::debug;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::ffi::{OsStr, OsString};
|
use std::ffi::{OsStr, OsString};
|
||||||
use std::fs::{File, Metadata, OpenOptions};
|
use std::fs::{File, Metadata, OpenOptions};
|
||||||
@@ -164,11 +165,48 @@ pub(crate) fn create_directory(file: &File, path: &str) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn link(
|
pub(crate) fn link(
|
||||||
_resolved_old: PathGet,
|
resolved_old: PathGet,
|
||||||
_resolved_new: PathGet,
|
resolved_new: PathGet,
|
||||||
_follow_symlinks: bool,
|
follow_symlinks: bool,
|
||||||
) -> Result<()> {
|
) -> 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(
|
pub(crate) fn open(
|
||||||
|
|||||||
Reference in New Issue
Block a user