#![allow(non_snake_case, non_camel_case_types)]
use std::ffi::c_void;
use std::fs::File;
use std::mem::size_of;
use std::os::windows::io::AsRawHandle;
use std::os::windows::io::FromRawHandle;
use std::os::windows::fs::OpenOptionsExt;
use crate::types::*;
const FSCTL_SET_COMPRESSION: u32 = 0x9C040;
const FSCTL_SET_EXTERNAL_BACKING: u32 = 0x9030C;
const FSCTL_GET_EXTERNAL_BACKING: u32 = 0x90310;
const FSCTL_DELETE_EXTERNAL_BACKING: u32 = 0x90314;
const SE_PRIVILEGE_ENABLED: u32 = 0x00000002;
const TOKEN_ADJUST_PRIVILEGES: u32 = 0x0020;
const TOKEN_QUERY: u32 = 0x0008;
#[repr(C)]
struct LUID_AND_ATTRIBUTES {
Luid: LUID,
Attributes: u32,
}
#[repr(C)]
struct TOKEN_PRIVILEGES {
PrivilegeCount: u32,
Privileges: [LUID_AND_ATTRIBUTES; 1],
}
use crate::utils::{to_wstring, PathBuffer};
pub fn get_real_file_size(path: &str) -> u64 {
unsafe {
let wide = PathBuffer::from(path);
let mut high: u32 = 0;
let win_api = crate::engine::dynamic_import::WinApi::get();
let low = (win_api.GetCompressedFileSizeW.unwrap())(wide.as_ptr(), &mut high);
if low == u32::MAX && GetLastError() != 0 {
std::fs::metadata(path).map(|m| m.len()).unwrap_or(0)
} else {
((high as u64) << 32) | (low as u64)
}
}
}
pub fn get_wof_algorithm(path: &str) -> Option<WofAlgorithm> {
unsafe {
let wide = PathBuffer::from(path);
let win_api = crate::engine::dynamic_import::WinApi::get();
let handle = (win_api.CreateFileW.unwrap())(
wide.as_ptr(),
0x80000000, FILE_SHARE_READ,
std::ptr::null(),
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
std::ptr::null_mut(),
);
if handle == INVALID_HANDLE_VALUE {
return None;
}
let result = get_wof_algorithm_from_handle(handle);
(win_api.CloseHandle.unwrap())(handle);
result
}
}
pub fn get_wof_algorithm_from_handle(handle: HANDLE) -> Option<WofAlgorithm> {
unsafe {
let mut out_buffer = [0u8; 1024];
let mut bytes_returned = 0u32;
let win_api = crate::engine::dynamic_import::WinApi::get();
let result = (win_api.DeviceIoControl.unwrap())(
handle,
FSCTL_GET_EXTERNAL_BACKING,
std::ptr::null(),
0,
out_buffer.as_mut_ptr() as *mut _,
out_buffer.len() as u32,
&mut bytes_returned,
std::ptr::null_mut(),
);
if result == 0 {
return None;
}
if bytes_returned < 20 {
return None;
}
let wof_info = &out_buffer[0..8];
let provider = u32::from_le_bytes([wof_info[4], wof_info[5], wof_info[6], wof_info[7]]);
if provider != 2 {
return None;
}
let file_info = &out_buffer[8..20];
let algorithm = u32::from_le_bytes([file_info[4], file_info[5], file_info[6], file_info[7]]);
match algorithm {
0 => Some(WofAlgorithm::Xpress4K),
1 => Some(WofAlgorithm::Lzx),
2 => Some(WofAlgorithm::Xpress8K),
3 => Some(WofAlgorithm::Xpress16K),
_ => None,
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct WOF_EXTERNAL_INFO {
pub version: u32,
pub provider: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct FILE_PROVIDER_EXTERNAL_INFO_V1 {
pub version: u32,
pub algorithm: u32,
pub flags: u32,
}
pub const WOF_CURRENT_VERSION: u32 = 1;
pub const WOF_PROVIDER_FILE: u32 = 2;
pub const FILE_PROVIDER_CURRENT_VERSION: u32 = 1;
pub const FILE_PROVIDER_COMPRESSION_XPRESS4K: u32 = 0;
pub const FILE_PROVIDER_COMPRESSION_LZX: u32 = 1;
pub const FILE_PROVIDER_COMPRESSION_XPRESS8K: u32 = 2;
pub const FILE_PROVIDER_COMPRESSION_XPRESS16K: u32 = 3;
pub const COMPRESSION_FORMAT_NONE: u16 = 0;
pub const COMPRESSION_FORMAT_DEFAULT: u16 = 1;
pub const COMPRESSION_FORMAT_LZNT1: u16 = 2;
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum WofAlgorithm {
Xpress4K = 0,
Lzx = 1,
Xpress8K = 2,
Xpress16K = 3,
}
impl WofAlgorithm {
fn to_u32(self) -> u32 {
self as u32
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CompressionState {
None,
Specific(WofAlgorithm),
Mixed,
}
pub fn compress_file(path: &str, algo: WofAlgorithm, force: bool) -> Result<bool, u32> {
let file_result = std::fs::OpenOptions::new()
.read(true)
.write(true)
.share_mode(7)
.open(path);
match file_result {
Ok(file) => compress_file_handle(&file, algo, force),
Err(e) => {
if force && e.raw_os_error() == Some(ERROR_ACCESS_DENIED as i32) {
enable_backup_privileges();
force_remove_readonly(path);
if let Ok(file) = std::fs::OpenOptions::new()
.read(true)
.write(true)
.share_mode(7)
.open(path)
{
return compress_file_handle(&file, algo, force);
}
if let Some(result) = compress_file_with_backup_semantics(path, algo, force) {
return result;
}
}
Err(e.raw_os_error().unwrap_or(0) as u32)
}
}
}
pub fn smart_compress(path: &str, target_algo: WofAlgorithm, force: bool) -> Result<bool, u32> {
let file_result = std::fs::OpenOptions::new()
.read(true)
.write(true)
.share_mode(7) .open(path);
match file_result {
Ok(file) => {
let handle = file.as_raw_handle() as HANDLE;
if !force {
if let Some(current_algo) = get_wof_algorithm_from_handle(handle) {
if current_algo == target_algo {
return Ok(true);
}
}
}
compress_file_handle(&file, target_algo, force)
}
Err(e) => {
if force && e.raw_os_error() == Some(ERROR_ACCESS_DENIED as i32) {
smart_compress_with_backup_semantics(path, target_algo, force)
} else {
Err(e.raw_os_error().unwrap_or(0) as u32)
}
}
}
}
fn smart_compress_with_backup_semantics(path: &str, algo: WofAlgorithm, force: bool) -> Result<bool, u32> {
force_remove_readonly(path);
if let Ok(file) = std::fs::OpenOptions::new()
.read(true)
.write(true)
.share_mode(7)
.open(path)
{
let handle = file.as_raw_handle() as HANDLE;
if !force {
if let Some(current_algo) = get_wof_algorithm_from_handle(handle) {
if current_algo == algo {
return Ok(true);
}
}
}
return compress_file_handle(&file, algo, force);
}
unsafe {
let wide = PathBuffer::from(path);
let access = 0x80000000 | 0x40000000; let win_api = crate::engine::dynamic_import::WinApi::get();
let handle = (win_api.CreateFileW.unwrap())(
wide.as_ptr(),
access,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
std::ptr::null(),
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
std::ptr::null_mut(),
);
if handle == INVALID_HANDLE_VALUE {
return Err(GetLastError());
}
if !force {
if let Some(current_algo) = get_wof_algorithm_from_handle(handle) {
if current_algo == algo {
(win_api.CloseHandle.unwrap())(handle);
return Ok(true);
}
}
}
let file = File::from_raw_handle(handle as *mut _);
compress_file_handle(&file, algo, force)
}
}
fn force_remove_readonly(path: &str) {
unsafe {
let wide = PathBuffer::from(path);
let win_api = crate::engine::dynamic_import::WinApi::get();
let attrs = (win_api.GetFileAttributesW.unwrap())(wide.as_ptr());
if attrs != u32::MAX { let new_attrs = attrs & !FILE_ATTRIBUTE_READONLY;
let new_attrs = if new_attrs == 0 { FILE_ATTRIBUTE_NORMAL } else { new_attrs };
(win_api.SetFileAttributesW.unwrap())(wide.as_ptr(), new_attrs);
}
}
}
pub fn enable_backup_privileges() {
unsafe {
let mut token_handle: HANDLE = std::ptr::null_mut(); let win_api = crate::engine::dynamic_import::WinApi::get();
let current_process = -1isize as HANDLE;
if (win_api.OpenProcessToken.unwrap())(
current_process,
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&mut token_handle
) == 0 {
return;
}
let privileges = [
to_wstring("SeBackupPrivilege"),
to_wstring("SeRestorePrivilege"),
to_wstring("SeTakeOwnershipPrivilege"),
to_wstring("SeSecurityPrivilege"),
];
for priv_name in privileges {
let mut luid = LUID { LowPart: 0, HighPart: 0 };
if (win_api.LookupPrivilegeValueW.unwrap())(std::ptr::null(), priv_name.as_ptr(), &mut luid as *mut _ as *mut _) != 0 {
let tp = TOKEN_PRIVILEGES {
PrivilegeCount: 1,
Privileges: [LUID_AND_ATTRIBUTES {
Luid: luid,
Attributes: SE_PRIVILEGE_ENABLED,
}],
};
(win_api.AdjustTokenPrivileges.unwrap())(
token_handle,
0, &tp as *const _ as *const _,
0,
std::ptr::null_mut(),
std::ptr::null_mut(),
);
}
}
(win_api.CloseHandle.unwrap())(token_handle);
}
}
fn compress_file_with_backup_semantics(path: &str, algo: WofAlgorithm, force: bool) -> Option<Result<bool, u32>> {
unsafe {
let wide = PathBuffer::from(path);
let access = 0x80000000 | 0x40000000;
let handle = (crate::engine::dynamic_import::WinApi::get().CreateFileW.unwrap())(
wide.as_ptr(),
access,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
std::ptr::null(),
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, std::ptr::null_mut(),
);
if handle != INVALID_HANDLE_VALUE {
let file = File::from_raw_handle(handle as *mut _);
let result = compress_file_handle(&file, algo, force);
Some(result)
} else {
None
}
}
}
pub fn compress_file_handle(file: &File, algo: WofAlgorithm, force: bool) -> Result<bool, u32> {
let handle = file.as_raw_handle() as HANDLE;
let wof_info = WOF_EXTERNAL_INFO {
version: WOF_CURRENT_VERSION,
provider: WOF_PROVIDER_FILE,
};
let file_info = FILE_PROVIDER_EXTERNAL_INFO_V1 {
version: FILE_PROVIDER_CURRENT_VERSION,
algorithm: algo.to_u32(),
flags: 0,
};
let mut input_buffer = Vec::with_capacity(size_of::<WOF_EXTERNAL_INFO>() + size_of::<FILE_PROVIDER_EXTERNAL_INFO_V1>());
unsafe {
let wof_ptr = &wof_info as *const _ as *const u8;
let wof_slice = std::slice::from_raw_parts(wof_ptr, size_of::<WOF_EXTERNAL_INFO>());
input_buffer.extend_from_slice(wof_slice);
let file_ptr = &file_info as *const _ as *const u8;
let file_slice = std::slice::from_raw_parts(file_ptr, size_of::<FILE_PROVIDER_EXTERNAL_INFO_V1>());
input_buffer.extend_from_slice(file_slice);
}
let mut bytes_returned = 0u32;
unsafe {
let win_api = crate::engine::dynamic_import::WinApi::get();
let result = (win_api.DeviceIoControl.unwrap())(
handle,
FSCTL_SET_EXTERNAL_BACKING,
input_buffer.as_ptr() as *const c_void,
input_buffer.len() as u32,
std::ptr::null_mut(),
0,
&mut bytes_returned,
std::ptr::null_mut(),
);
if result == 0 {
let err = GetLastError();
if err == 344 {
if force {
let compression_state: u16 = COMPRESSION_FORMAT_DEFAULT;
let _ = (crate::engine::dynamic_import::WinApi::get().DeviceIoControl.unwrap())(
handle,
FSCTL_SET_COMPRESSION,
&compression_state as *const _ as *const c_void,
std::mem::size_of::<u16>() as u32,
std::ptr::null_mut(),
0,
&mut bytes_returned,
std::ptr::null_mut(),
);
return Ok(true);
}
return Ok(false);
}
return Err(err);
}
}
Ok(true)
}
pub fn uncompress_file(path: &str) -> Result<(), u32> {
let file = std::fs::OpenOptions::new()
.read(true)
.write(true)
.share_mode(7)
.open(path)
.map_err(|e| e.raw_os_error().unwrap_or(0) as u32)?;
uncompress_file_handle(&file)
}
pub fn uncompress_file_handle(file: &File) -> Result<(), u32> {
let handle = file.as_raw_handle() as HANDLE;
let mut bytes_returned = 0u32;
unsafe {
let win_api = crate::engine::dynamic_import::WinApi::get();
if (win_api.DeviceIoControl.unwrap())(
handle,
FSCTL_DELETE_EXTERNAL_BACKING,
std::ptr::null(),
0,
std::ptr::null_mut(),
0,
&mut bytes_returned,
std::ptr::null_mut(),
) == 0 {
let err = GetLastError();
if err == 346 { } else {
}
}
let compression_state: u16 = COMPRESSION_FORMAT_NONE;
let _ = (win_api.DeviceIoControl.unwrap())(
handle,
FSCTL_SET_COMPRESSION,
&compression_state as *const _ as *const c_void,
std::mem::size_of::<u16>() as u32,
std::ptr::null_mut(),
0,
&mut bytes_returned,
std::ptr::null_mut(),
);
}
Ok(())
}