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 crate::wasi::{types, Errno, Result};
use std::convert::TryInto; use std::convert::TryInto;
use std::ffi::{OsStr, OsString}; 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::ffi::{OsStrExt, OsStringExt};
use std::os::windows::fs::OpenOptionsExt; use std::os::windows::fs::OpenOptionsExt;
use std::path::{Path, PathBuf}; 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<()> { pub(crate) fn symlink(old_path: &str, resolved: PathGet) -> Result<()> {
use std::fs;
use std::os::windows::fs::{symlink_dir, symlink_file}; use std::os::windows::fs::{symlink_dir, symlink_file};
let old_path = concatenate(&resolved.dirfd().as_os_handle(), Path::new(old_path))?; let old_path = concatenate(&resolved.dirfd().as_os_handle(), Path::new(old_path))?;
let new_path = resolved.concatenate()?; let new_path = resolved.concatenate()?;
// try creating a file symlink // Windows distinguishes between file and directory symlinks.
let err = match symlink_file(&old_path, &new_path) { // 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(()), Ok(()) => return Ok(()),
Err(e) => e, Err(e) => e,
}; };
@@ -427,18 +441,15 @@ pub(crate) fn symlink(old_path: &str, resolved: PathGet) -> Result<()> {
Some(code) => { Some(code) => {
log::debug!("path_symlink at symlink_file error code={:?}", code); log::debug!("path_symlink at symlink_file error code={:?}", code);
match code as u32 { match code as u32 {
winerror::ERROR_NOT_A_REPARSE_POINT => { // If the target contains a trailing slash, the Windows API returns
// try creating a dir symlink instead // ERROR_INVALID_NAME (which corresponds to ENOENT) instead of
return symlink_dir(old_path, new_path).map_err(Into::into); // ERROR_ALREADY_EXISTS (which corresponds to EEXIST)
} //
winerror::ERROR_ACCESS_DENIED => { // This concerns only trailing slashes (not backslashes) and
// does the target exist? // only symbolic links (not hard links).
if new_path.exists() { //
return Err(Errno::Exist); // Since POSIX will return EEXIST in such case, we simulate this behavior
}
}
winerror::ERROR_INVALID_NAME => { winerror::ERROR_INVALID_NAME => {
// does the target without trailing slashes exist?
if let Some(path) = strip_trailing_slashes_and_concatenate(&resolved)? { if let Some(path) = strip_trailing_slashes_and_concatenate(&resolved)? {
if path.exists() { if path.exists() {
return Err(Errno::Exist); return Err(Errno::Exist);