Skip to content

Commit 508b664

Browse files
committed
fix(federation): use stable Windows file identity
1 parent 3eec80b commit 508b664

2 files changed

Lines changed: 83 additions & 16 deletions

File tree

‎rust/rac-engine/src/federation.rs‎

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,7 +1418,10 @@ struct StableFileIdentity {
14181418
}
14191419

14201420
#[cfg(unix)]
1421-
fn stable_file_identity(metadata: &std::fs::Metadata) -> Result<StableFileIdentity, ()> {
1421+
fn stable_file_identity(
1422+
_path: &Path,
1423+
metadata: &std::fs::Metadata,
1424+
) -> Result<StableFileIdentity, ()> {
14221425
use std::os::unix::fs::MetadataExt;
14231426
Ok(StableFileIdentity {
14241427
device: metadata.dev(),
@@ -1431,20 +1434,82 @@ fn stable_file_identity(metadata: &std::fs::Metadata) -> Result<StableFileIdenti
14311434
}
14321435

14331436
#[cfg(windows)]
1434-
fn stable_file_identity(metadata: &std::fs::Metadata) -> Result<StableFileIdentity, ()> {
1435-
use std::os::windows::fs::MetadataExt;
1437+
fn stable_file_identity(
1438+
path: &Path,
1439+
_metadata: &std::fs::Metadata,
1440+
) -> Result<StableFileIdentity, ()> {
1441+
use std::ffi::c_void;
1442+
use std::mem::MaybeUninit;
1443+
use std::os::windows::fs::OpenOptionsExt;
1444+
use std::os::windows::io::AsRawHandle;
1445+
1446+
#[repr(C)]
1447+
struct FileTime {
1448+
low: u32,
1449+
high: u32,
1450+
}
1451+
1452+
#[repr(C)]
1453+
struct ByHandleFileInformation {
1454+
_file_attributes: u32,
1455+
_creation_time: FileTime,
1456+
_last_access_time: FileTime,
1457+
last_write_time: FileTime,
1458+
volume_serial_number: u32,
1459+
file_size_high: u32,
1460+
file_size_low: u32,
1461+
number_of_links: u32,
1462+
file_index_high: u32,
1463+
file_index_low: u32,
1464+
}
1465+
1466+
#[link(name = "Kernel32")]
1467+
unsafe extern "system" {
1468+
fn GetFileInformationByHandle(
1469+
file: *mut c_void,
1470+
information: *mut ByHandleFileInformation,
1471+
) -> i32;
1472+
}
1473+
1474+
const FILE_FLAG_BACKUP_SEMANTICS: u32 = 0x0200_0000;
1475+
let file = std::fs::OpenOptions::new()
1476+
.read(true)
1477+
.custom_flags(FILE_FLAG_BACKUP_SEMANTICS)
1478+
.open(path)
1479+
.map_err(|_| ())?;
1480+
let mut information = MaybeUninit::<ByHandleFileInformation>::uninit();
1481+
// SAFETY: `file` keeps a valid owned handle alive for the call and the
1482+
// Win32 function initializes the complete output structure on success.
1483+
let succeeded = unsafe {
1484+
GetFileInformationByHandle(
1485+
file.as_raw_handle().cast::<c_void>(),
1486+
information.as_mut_ptr(),
1487+
)
1488+
};
1489+
if succeeded == 0 {
1490+
return Err(());
1491+
}
1492+
// SAFETY: the successful Win32 call above initialized every field.
1493+
let information = unsafe { information.assume_init() };
1494+
let last_write = ((information.last_write_time.high as u64) << 32)
1495+
| information.last_write_time.low as u64;
14361496
Ok(StableFileIdentity {
1437-
device: metadata.volume_serial_number().ok_or(())? as u64,
1438-
inode: metadata.file_index().ok_or(())?,
1439-
links: metadata.number_of_links().ok_or(())? as u64,
1440-
length: metadata.file_size(),
1441-
changed_seconds: (metadata.last_write_time() / 10_000_000) as i64,
1442-
changed_nanos: ((metadata.last_write_time() % 10_000_000) * 100) as i64,
1497+
device: information.volume_serial_number as u64,
1498+
inode: ((information.file_index_high as u64) << 32)
1499+
| information.file_index_low as u64,
1500+
links: information.number_of_links as u64,
1501+
length: ((information.file_size_high as u64) << 32)
1502+
| information.file_size_low as u64,
1503+
changed_seconds: (last_write / 10_000_000) as i64,
1504+
changed_nanos: ((last_write % 10_000_000) * 100) as i64,
14431505
})
14441506
}
14451507

14461508
#[cfg(not(any(unix, windows)))]
1447-
fn stable_file_identity(_metadata: &std::fs::Metadata) -> Result<StableFileIdentity, ()> {
1509+
fn stable_file_identity(
1510+
_path: &Path,
1511+
_metadata: &std::fs::Metadata,
1512+
) -> Result<StableFileIdentity, ()> {
14481513
Err(())
14491514
}
14501515

@@ -1475,7 +1540,7 @@ fn read_stable_regular(
14751540
),
14761541
));
14771542
}
1478-
let before_identity = stable_file_identity(&before).map_err(|_| {
1543+
let before_identity = stable_file_identity(path, &before).map_err(|_| {
14791544
ParentCorpusError::at(
14801545
ParentCorpusErrorCode::UnsupportedFilesystem,
14811546
path,
@@ -1550,7 +1615,7 @@ fn read_stable_regular(
15501615
),
15511616
)
15521617
})?;
1553-
let after_identity = stable_file_identity(&after).map_err(|_| {
1618+
let after_identity = stable_file_identity(path, &after).map_err(|_| {
15541619
ParentCorpusError::at(
15551620
ParentCorpusErrorCode::UnsupportedFilesystem,
15561621
path,
@@ -1616,7 +1681,7 @@ fn directory_device(path: &Path) -> Result<u64, ParentCorpusError> {
16161681
),
16171682
));
16181683
}
1619-
stable_file_identity(&metadata)
1684+
stable_file_identity(path, &metadata)
16201685
.map(|identity| identity.device)
16211686
.map_err(|_| {
16221687
ParentCorpusError::at(
@@ -1652,7 +1717,7 @@ fn snapshot_directory_v2(
16521717
),
16531718
)
16541719
})?;
1655-
let directory_identity = stable_file_identity(&directory_before).map_err(|_| {
1720+
let directory_identity = stable_file_identity(directory, &directory_before).map_err(|_| {
16561721
ParentCorpusError::at(
16571722
ParentCorpusErrorCode::UnsupportedFilesystem,
16581723
directory,
@@ -1792,7 +1857,7 @@ fn snapshot_directory_v2(
17921857
),
17931858
)
17941859
})?;
1795-
let after_identity = stable_file_identity(&directory_after).map_err(|_| {
1860+
let after_identity = stable_file_identity(directory, &directory_after).map_err(|_| {
17961861
ParentCorpusError::at(
17971862
ParentCorpusErrorCode::UnsupportedFilesystem,
17981863
directory,

‎rust/rac-engine/src/federation_generation.rs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,10 +418,12 @@ mod tests {
418418

419419
#[test]
420420
fn every_answer_affecting_section_changes_generation() {
421+
type GenerationMutation = Box<dyn Fn(&mut GraphGenerationInput)>;
422+
421423
let original = fixture();
422424
let expected = closure_generation(&original);
423425

424-
let mut mutations: Vec<Box<dyn Fn(&mut GraphGenerationInput)>> = vec![
426+
let mut mutations: Vec<GenerationMutation> = vec![
425427
Box::new(|input| input.recursive = false),
426428
Box::new(|input| input.root_source.push('2')),
427429
Box::new(|input| input.root_corpus_path.push('2')),

0 commit comments

Comments
 (0)