Skip to content

Commit

Permalink
Sanitize file/directory symlinks in path_symlinks on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
marmistrz committed Mar 24, 2020
1 parent 0d4bde4 commit c9ac9a1
Showing 1 changed file with 24 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::old::snapshot_0::sys::hostcalls_impl::fs_helpers::PathGetExt;
use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
use log::{debug, trace};
use std::convert::TryInto;
use std::fs::{File, Metadata, OpenOptions};
use std::fs::{self, File, Metadata, OpenOptions};
use std::io::{self, Seek, SeekFrom};
use std::os::windows::fs::{FileExt, OpenOptionsExt};
use std::os::windows::prelude::{AsRawHandle, FromRawHandle};
Expand Down Expand Up @@ -467,8 +467,21 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> WasiResult<()>
let old_path = concatenate(resolved.dirfd(), 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,
};
Expand All @@ -486,8 +499,15 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> WasiResult<()>
return Err(WasiError::EEXIST);
}
}
// 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(WasiError::EEXIST);
Expand Down

0 comments on commit c9ac9a1

Please sign in to comment.