Sanitize file/directory symlinks in path_symlinks on Windows (#1359)

* Sanitize file/directory symlinks in path_symlinks on Windows

* Remove redundant error mappings
This commit is contained in:
Marcin Mielniczuk
2020-04-01 08:55:51 +02:00
committed by GitHub
parent e64668776a
commit 20e71858a1

View File

@@ -5,7 +5,7 @@ use crate::sys::entry::OsHandle;
use crate::wasi::{types, Errno, Result};
use std::convert::TryInto;
use std::ffi::{OsStr, OsString};
use std::fs::{File, OpenOptions};
use std::fs::{File, Metadata, OpenOptions};
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::os::windows::fs::OpenOptionsExt;
use std::path::{Path, PathBuf};
@@ -413,13 +413,27 @@ pub(crate) fn filestat_set_times(
}
pub(crate) fn symlink(old_path: &str, resolved: PathGet) -> Result<()> {
use std::fs;
use std::os::windows::fs::{symlink_dir, symlink_file};
let old_path = concatenate(&resolved.dirfd().as_os_handle(), Path::new(old_path))?;
let new_path = resolved.concatenate()?;
// try creating a file symlink
let err = match symlink_file(&old_path, &new_path) {
// Windows distinguishes between file and directory symlinks.
// If the source doesn't exist or is an exotic file type, we fall back
// to regular file symlinks.
let use_dir_symlink = fs::metadata(&new_path)
.as_ref()
.map(Metadata::is_dir)
.unwrap_or(false);
let res = if use_dir_symlink {
symlink_dir(&old_path, &new_path)
} else {
symlink_file(&old_path, &new_path)
};
let err = match res {
Ok(()) => return Ok(()),
Err(e) => e,
};
@@ -427,18 +441,15 @@ pub(crate) fn symlink(old_path: &str, resolved: PathGet) -> Result<()> {
Some(code) => {
log::debug!("path_symlink at symlink_file error code={:?}", code);
match code as u32 {
winerror::ERROR_NOT_A_REPARSE_POINT => {
// try creating a dir symlink instead
return symlink_dir(old_path, new_path).map_err(Into::into);
}
winerror::ERROR_ACCESS_DENIED => {
// does the target exist?
if new_path.exists() {
return Err(Errno::Exist);
}
}
// If the target contains a trailing slash, the Windows API returns
// ERROR_INVALID_NAME (which corresponds to ENOENT) instead of
// ERROR_ALREADY_EXISTS (which corresponds to EEXIST)
//
// This concerns only trailing slashes (not backslashes) and
// only symbolic links (not hard links).
//
// Since POSIX will return EEXIST in such case, we simulate this behavior
winerror::ERROR_INVALID_NAME => {
// does the target without trailing slashes exist?
if let Some(path) = strip_trailing_slashes_and_concatenate(&resolved)? {
if path.exists() {
return Err(Errno::Exist);