This is scratch of getting data about file sizes.
But to compile it we need to add features impl-debug
to crate winapi and change it's sources by adding debug to some structs.
It's bit tricky.
#![allow(unused_imports, unused)] | |
use std::error::Error; | |
use std::ffi::c_void; | |
use std::fs::{File, OpenOptions}; | |
use std::iter::once; | |
use std::os::windows::ffi::OsStrExt; | |
use std::path::Path; | |
use std::process::exit; | |
use std::{io, mem}; | |
use winapi::shared::minwindef::{DWORD, ULONG}; | |
use winapi::shared::ntdef::ULONGLONG; | |
use winapi::shared::winerror::NO_ERROR; | |
use winapi::um::errhandlingapi::GetLastError; | |
use winapi::um::fileapi::{ | |
GetCompressedFileSizeW, FILE_BASIC_INFO, FILE_COMPRESSION_INFO, FILE_STORAGE_INFO, FILE_ID_INFO | |
}; | |
use winapi::um::fileapi::{FILE_STANDARD_INFO, INVALID_FILE_SIZE}; | |
use winapi::um::minwinbase::{ | |
FileBasicInfo, FileCompressionInfo, FileStandardInfo, FileStorageInfo, | |
FILE_INFO_BY_HANDLE_CLASS, FileIdInfo, | |
}; | |
use winapi::um::winbase::{FILE_FLAG_BACKUP_SEMANTICS, GetFileInformationByHandleEx}; | |
use winapi::um::winnt::{FILE_ATTRIBUTE_DIRECTORY, FILE_ID_128}; | |
use winapi_util::{AsHandleRef, Handle}; | |
#[path = "ffi.rs"] | |
mod ffi; | |
pub(crate) fn run() { | |
print_file_info("test-data\\text1_c.txt"); | |
print_file_info("test-data\\text1.txt"); | |
println!(); | |
print_file_info("test-data\\text2_c.txt"); | |
print_file_info("test-data\\text2.txt"); | |
println!(); | |
print_file_info("test-data\\b23_rand_c"); | |
print_file_info("test-data\\b23_rand"); | |
println!(); | |
print_file_info("test-data\\b4000_rand_c"); | |
print_file_info("test-data\\b4000_rand"); | |
println!(); | |
print_file_info("test-data\\b4096_rand_c"); | |
print_file_info("test-data\\b4096_rand"); | |
println!(); | |
// | |
// print_file_info("test-data\\b23_zero_c"); | |
// print_file_info("test-data\\b23_zero"); | |
// println!(); | |
// | |
print_file_info("test-data\\b512_zero_c"); | |
print_file_info("test-data\\b512_zero"); | |
// println!(); | |
print_file_info("test-data\\alt_ds_512"); | |
// print_file_info("C:\\tmp"); | |
//exit(0); | |
} | |
fn print_file_info(file_name: &str) { | |
let std_info = get_file_info::<FILE_STANDARD_INFO>(file_name).unwrap(); | |
let mut comp_info: FILE_COMPRESSION_INFO = get_file_info(file_name).unwrap(); | |
let comp_size = ffi::compressed_size(Path::new(file_name)).unwrap(); | |
let stor_info: FILE_STORAGE_INFO = get_file_info(file_name).unwrap(); | |
let id_info: FILE_ID_INFO = get_file_info(file_name).unwrap(); | |
unsafe { | |
// println!("File: {}\n SDT.AllocationSize: {} STD.EndOfFile: {} COMP.CompressedFileSize: {} COMP.CompressionFormat: {} GetCompressedFileSizeW: {} \ | |
// COMP.ChunkShift {:?} COMP.ClusterShift: {:?}", | |
// file_name, std_info.AllocationSize.QuadPart(), std_info.EndOfFile.QuadPart(), comp_info.CompressedFileSize.QuadPart(), comp_info.CompressionFormat,comp_size, | |
// comp_info.ChunkShift, comp_info.ClusterShift | |
// ); | |
println!("File: {}\n {:?}\n {:?}\n GetCompressedFileSizeW: {:?}\n {:?}\n {:?}\n", | |
file_name, &std_info, &comp_info, comp_size, &stor_info, &id_info | |
); | |
// println!("Storage info: | |
// LogicalBytesPerSector: {} | |
// PhysicalBytesPerSectorForAtomicity: {} | |
// PhysicalBytesPerSectorForPerformance: {} | |
// FileSystemEffectivePhysicalBytesPerSectorForAtomicity: {} | |
// Flags: {} | |
// ByteOffsetForSectorAlignment: {} | |
// ByteOffsetForPartitionAlignment: {}\n", | |
// stor_info.LogicalBytesPerSector, | |
// stor_info.PhysicalBytesPerSectorForAtomicity, | |
// stor_info.PhysicalBytesPerSectorForPerformance, | |
// stor_info.FileSystemEffectivePhysicalBytesPerSectorForAtomicity, | |
// stor_info.Flags, | |
// stor_info.ByteOffsetForSectorAlignment, | |
// stor_info.ByteOffsetForPartitionAlignment, | |
// ); | |
} | |
} | |
#[inline(never)] | |
fn get_file_info<T: FileInfoTrait>(name: &str) -> Result<T, io::Error> { | |
use std::os::windows::fs::MetadataExt; | |
use std::os::windows::fs::OpenOptionsExt; | |
let file_path = Path::new(name); | |
let metadata = file_path.metadata()?; | |
let file = File::options() | |
.access_mode(0)//neither read or write: metadata access. This increases chance that we gain access due to security ACL. | |
.custom_flags(FILE_FLAG_BACKUP_SEMANTICS) | |
.open(file_path)?; | |
let handle = Handle::from_file(file); | |
let mut file_std_info = get_file_information_by_handle_ex2(&handle)?; | |
return Result::Ok(file_std_info); | |
} | |
fn get_file_information_by_handle_ex<T: Default>( | |
handle: &Handle, | |
info_class: FILE_INFO_BY_HANDLE_CLASS, | |
) -> Result<T, io::Error> { | |
let mut buf = T::default(); | |
let foo = ""; | |
let res = unsafe { | |
GetFileInformationByHandleEx( | |
handle.as_raw(), | |
info_class, | |
&mut buf as *mut _ as *mut c_void, | |
mem::size_of_val(&buf) as DWORD, | |
) | |
}; | |
if res != 0 { | |
Result::Ok(buf) | |
} else { | |
Result::Err(io::Error::last_os_error()) | |
} | |
} | |
trait FileInfoTrait: Default + Sized { | |
const CLASS: FILE_INFO_BY_HANDLE_CLASS; | |
} | |
impl FileInfoTrait for FILE_STANDARD_INFO { | |
const CLASS: FILE_INFO_BY_HANDLE_CLASS = FileStandardInfo; | |
} | |
impl FileInfoTrait for FILE_COMPRESSION_INFO { | |
const CLASS: FILE_INFO_BY_HANDLE_CLASS = FileCompressionInfo; | |
} | |
impl FileInfoTrait for FILE_STORAGE_INFO { | |
const CLASS: FILE_INFO_BY_HANDLE_CLASS = FileStorageInfo; | |
} | |
impl FileInfoTrait for FILE_ID_INFO { | |
const CLASS: FILE_INFO_BY_HANDLE_CLASS = FileIdInfo ; | |
} | |
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntquerydirectoryfile | |
// https://www.winehq.org/pipermail/wine-cvs/2015-May/106715.html | |
// https://github.com/MicrosoftDocs/sdk-api/blob/docs/sdk-api-src/content/minwinbase/ne-minwinbase-file_info_by_handle_class.md#-field-fileidbothdirectoryinfo | |
// **RestartInfo -- restarts enumeration | |
fn get_file_information_by_handle_ex2<T: FileInfoTrait>(handle: &Handle) -> Result<T, io::Error> { | |
let mut buf = T::default(); | |
let res = unsafe { | |
GetFileInformationByHandleEx( | |
handle.as_raw(), | |
T::CLASS, | |
&mut buf as *mut _ as *mut c_void, | |
mem::size_of_val(&buf) as DWORD, | |
) | |
}; | |
if res != 0 { | |
Result::Ok(buf) | |
} else { | |
Result::Err(io::Error::last_os_error()) | |
} | |
} |