use super::{get_path::concatenate_or_return_absolute, open_options_to_std};
use crate::fs::{FollowSymlinks, OpenOptions, OpenUncheckedError, SymlinkKind};
use std::{
ffi::OsString,
fs, io,
os::windows::{
ffi::{OsStrExt, OsStringExt},
fs::MetadataExt,
},
path::{Path, PathBuf},
};
use winapi::{
shared::winerror,
um::{winbase, winnt::FILE_ATTRIBUTE_DIRECTORY},
};
pub(crate) fn open_unchecked(
start: &fs::File,
path: &Path,
options: &OpenOptions,
) -> Result<fs::File, OpenUncheckedError> {
let mut full_path =
concatenate_or_return_absolute(start, path).map_err(OpenUncheckedError::Other)?;
if options.dir_required {
let mut wide = full_path.into_os_string().encode_wide().collect::<Vec<_>>();
wide.push('\\' as u16);
full_path = PathBuf::from(OsString::from_wide(&wide));
}
let (opts, manually_trunc) = open_options_to_std(options);
match opts.open(full_path) {
Ok(f) => {
if options.follow == FollowSymlinks::No
&& (options.ext.custom_flags & winbase::FILE_FLAG_OPEN_REPARSE_POINT) == 0
{
let metadata = f.metadata().map_err(OpenUncheckedError::Other)?;
if metadata.file_type().is_symlink() {
return Err(OpenUncheckedError::Symlink(
io::Error::new(io::ErrorKind::Other, "symlink encountered"),
if metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0 {
SymlinkKind::Dir
} else {
SymlinkKind::File
},
));
}
}
if manually_trunc {
f.set_len(0).unwrap();
}
Ok(f)
}
Err(e) if e.kind() == io::ErrorKind::NotFound => Err(OpenUncheckedError::NotFound(e)),
Err(e) => match e.raw_os_error() {
Some(code) => match code as u32 {
winerror::ERROR_FILE_NOT_FOUND | winerror::ERROR_PATH_NOT_FOUND => {
Err(OpenUncheckedError::NotFound(e))
}
_ => Err(OpenUncheckedError::Other(e)),
},
None => Err(OpenUncheckedError::Other(e)),
},
}
}