use crate::fs::OpenOptions;
use io_lifetimes::AsHandle;
use std::{fs, io};
use windows_sys::Win32::Foundation::ERROR_INVALID_PARAMETER;
use windows_sys::Win32::Storage::FileSystem::{
FILE_FLAG_DELETE_ON_CLOSE, FILE_FLAG_OPEN_REPARSE_POINT, FILE_FLAG_WRITE_THROUGH,
FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_WRITE_DATA, SECURITY_CONTEXT_TRACKING,
SECURITY_DELEGATION, SECURITY_EFFECTIVE_ONLY, SECURITY_IDENTIFICATION, SECURITY_IMPERSONATION,
};
use windows_sys::Win32::System::SystemServices::{GENERIC_READ, GENERIC_WRITE};
use winx::file::{AccessMode, Flags};
pub(crate) fn reopen_impl(file: &fs::File, options: &OpenOptions) -> io::Result<fs::File> {
let old_access_mode = winx::file::query_access_information(file.as_handle())?;
let new_access_mode = get_access_mode(options)?;
let flags = get_flags_and_attributes(options);
let new_access_mode = AccessMode::from_bits(new_access_mode).unwrap();
let flags = Flags::from_bits(flags).unwrap();
if new_access_mode
.intersects(AccessMode::from_bits(GENERIC_WRITE | FILE_GENERIC_WRITE).unwrap())
&& !old_access_mode
.intersects(AccessMode::from_bits(GENERIC_WRITE | FILE_GENERIC_WRITE).unwrap())
{
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"Can't reopen file",
));
}
if new_access_mode.intersects(AccessMode::from_bits(GENERIC_READ | FILE_GENERIC_READ).unwrap())
&& !old_access_mode
.intersects(AccessMode::from_bits(GENERIC_READ | FILE_GENERIC_READ).unwrap())
{
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"Can't reopen file",
));
}
if flags
.intersects(Flags::from_bits(FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_WRITE_THROUGH).unwrap())
&& !old_access_mode
.intersects(AccessMode::from_bits(GENERIC_WRITE | FILE_GENERIC_WRITE).unwrap())
{
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"Can't reopen file",
));
}
if (flags.bits()
& (SECURITY_CONTEXT_TRACKING
| SECURITY_DELEGATION
| SECURITY_EFFECTIVE_ONLY
| SECURITY_IDENTIFICATION
| SECURITY_IMPERSONATION))
!= 0
{
return Err(io::Error::new(io::ErrorKind::Other, "Can't reopen file"));
}
winx::file::reopen_file(file.as_handle(), new_access_mode, flags)
}
fn get_access_mode(options: &OpenOptions) -> io::Result<u32> {
match (
options.read,
options.write,
options.append,
options.ext.access_mode,
) {
(.., Some(mode)) => Ok(mode),
(true, false, false, None) => Ok(GENERIC_READ),
(false, true, false, None) => Ok(GENERIC_WRITE),
(true, true, false, None) => Ok(GENERIC_READ | GENERIC_WRITE),
(false, _, true, None) => Ok(FILE_GENERIC_WRITE & !FILE_WRITE_DATA),
(true, _, true, None) => Ok(GENERIC_READ | (FILE_GENERIC_WRITE & !FILE_WRITE_DATA)),
(false, false, false, None) => {
Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER as i32))
}
}
}
fn get_flags_and_attributes(options: &OpenOptions) -> u32 {
options.ext.custom_flags
| options.ext.attributes
| options.ext.security_qos_flags
| if options.create_new {
FILE_FLAG_OPEN_REPARSE_POINT
} else {
0
}
}