Skip to content

Commit

Permalink
Use windows-sys to merge pull request scullionw#9 from scullionw/dirs…
Browse files Browse the repository at this point in the history
…tat-rs
  • Loading branch information
higersky committed Apr 25, 2024
1 parent 607c2b7 commit ad2c9d7
Show file tree
Hide file tree
Showing 5 changed files with 350 additions and 13 deletions.
127 changes: 123 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ serde = { version = "1.0.131", features = ["derive"] }
serde_json = "1.0.73"

[target.'cfg(windows)'.dependencies]
winapi-util = "0.1.2"
winapi-util = "0.1.8"
windows-sys = { version = "0.52", features = ["Win32_Foundation", "Win32_Storage_FileSystem"]}
path-absolutize = "3.1.1"

[target.'cfg(windows)'.dependencies.winapi]
version = "0.3.7"
features = ["winerror"]
[dev-dependencies]
const_format = "0.2.23"

[profile.release]
lto = 'fat'
Expand Down
58 changes: 53 additions & 5 deletions src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ use std::io;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
use std::path::Path;
use winapi::shared::winerror::NO_ERROR;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::fileapi::GetCompressedFileSizeW;
use winapi::um::fileapi::INVALID_FILE_SIZE;
use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::Foundation::NO_ERROR;
use windows_sys::Win32::Storage::FileSystem::GetCompressedFileSizeW;
use windows_sys::Win32::Storage::FileSystem::INVALID_FILE_SIZE;

pub fn compressed_size(path: &Path) -> Result<u64, Box<dyn Error>> {
let wide: Vec<u16> = path.as_os_str().encode_wide().chain(once(0)).collect();
let wide = path_to_u16s(path);
let mut high: u32 = 0;

// TODO: Deal with max path size
Expand All @@ -27,6 +27,54 @@ pub fn compressed_size(path: &Path) -> Result<u64, Box<dyn Error>> {
Ok(u64::from(high) << 32 | u64::from(low))
}

/// inspired by [fn maybe_verbatim(path: &Path)](https://github.com/rust-lang/rust/blob/1f4681ad7a132755452c32a987ad0f0d075aa6aa/library/std/src/sys/windows/path.rs#L170)
/// But function from std is calling winapi GetFullPathNameW in case if path is longer than 248.
/// We are more optimistic and expect all path being absolute, so no API calls from this function.
fn path_to_u16s(path: &Path) -> Vec<u16> {
// Normally the MAX_PATH is 260 UTF-16 code units (including the NULL).
// However, for APIs such as CreateDirectory[1], the limit is 248.
//
// [1]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createdirectorya#parameters
const LEGACY_MAX_PATH: usize = 248;
// UTF-16 encoded code points, used in parsing and building UTF-16 paths.
// All of these are in the ASCII range so they can be cast directly to `u16`.
const SEP: u16 = b'\\' as _;
const QUERY: u16 = b'?' as _;
const U: u16 = b'U' as _;
const N: u16 = b'N' as _;
const C: u16 = b'C' as _;
// \\?\
const VERBATIM_PREFIX: &[u16] = &[SEP, SEP, QUERY, SEP];
// \??\
const NT_PREFIX: &[u16] = &[SEP, QUERY, QUERY, SEP];
// \\?\UNC\
const UNC_PREFIX: &[u16] = &[SEP, SEP, QUERY, SEP, U, N, C, SEP];
// \\
const NETWORK_PREFIX: &[u16] = &[SEP, SEP];

let wide: Vec<u16> = path.as_os_str().encode_wide().chain(once(0)).collect();
// don't need to do anything if path is small enaught.
if wide.len() < LEGACY_MAX_PATH {
return wide;
}

if wide.starts_with(VERBATIM_PREFIX) || wide.starts_with(NT_PREFIX) {
return wide;
}

if wide.starts_with(NETWORK_PREFIX) {
// network path from SMB
let mut tmp = Vec::from(UNC_PREFIX);
tmp.extend(&wide[2..]);
return tmp;
} else {
// if we came here, we aren't using network drive, so just prepend File namespace prefix
let mut tmp = Vec::from(VERBATIM_PREFIX);
tmp.extend(wide);
return tmp;
}
}

fn get_last_error() -> u32 {
unsafe { GetLastError() }
}
27 changes: 27 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,30 @@ impl DiskItem {
path: &Path,
apparent: bool,
root_dev: u64,
) -> Result<Self, Box<dyn Error>> {
#[cfg(windows)]
{
// Solution for windows compressed files requires path to be absolute, see ffi.rs
// Basically it would be triggered only on top most invocation,
// and afterwards all path would be absolute. We do it here as it is relatively harmless
// but this would allow us fo it only once instead of each invocation of ffi::compressed_size
if apparent && !path.is_absolute() {
use path_absolutize::*;
let absolute_dir = path.absolutize()?;
return Self::analyze(
absolute_dir.as_ref(),
apparent,
root_dev,
);
}
}
return Self::analyze(path, apparent, root_dev);
}

fn analyze(
path: &Path,
apparent: bool,
root_dev: u64,
) -> Result<Self, Box<dyn Error>> {
let name = path
.file_name()
Expand Down Expand Up @@ -115,3 +139,6 @@ impl FileInfo {
}
}
}

#[cfg(test)]
mod tests;
Loading

0 comments on commit ad2c9d7

Please sign in to comment.