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:
committed by
GitHub
parent
e64668776a
commit
20e71858a1
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user