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()) | |
| } | |
| } |