test(TASK-047): split the ntfs short-name guard test by platform reality
On NTFS the fixture self-collides: .builtin-manifest auto-generates the 8.3 short name BUILTI~1, so writing that fixture file opens the manifest through its alias and clobbers it -- the exact threat the guard models. Gate the fixture-based test to unix, and add a windows counterpart that proves the collision where 8.3 generation is enabled and asserts the filter invariant (alias-shaped entries are neither removal candidates nor claimed) either way.
This commit is contained in:
1 file changed
+60
@@ -320,6 +320,15 @@ mod tests {
|
|||||||
assert!(!is_ntfs_short_name_alias(".builtin-manifest"));
|
assert!(!is_ntfs_short_name_alias(".builtin-manifest"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This fixture is unrealizable on NTFS: `.builtin-manifest` gets an
|
||||||
|
/// auto-generated 8.3 short name of exactly this shape (`BUILTI~1`), so
|
||||||
|
/// writing `dir/BUILTI~1` opens the manifest through its alias and
|
||||||
|
/// clobbers it instead of creating a distinct file — the precise
|
||||||
|
/// collision [`is_ntfs_short_name_alias`] defends against. Only
|
||||||
|
/// filesystems without 8.3 aliasing can host alias-shaped names as real,
|
||||||
|
/// distinct files; Windows coverage lives in
|
||||||
|
/// `ntfs_short_name_entries_never_accepted_on_windows` below.
|
||||||
|
#[cfg(unix)]
|
||||||
#[test]
|
#[test]
|
||||||
fn ntfs_short_name_entries_never_direct_deletions() {
|
fn ntfs_short_name_entries_never_direct_deletions() {
|
||||||
let dir = fresh_dir("builtin-manifest-shortname-");
|
let dir = fresh_dir("builtin-manifest-shortname-");
|
||||||
@@ -335,4 +344,55 @@ mod tests {
|
|||||||
);
|
);
|
||||||
let _ = fs::remove_dir_all(&dir);
|
let _ = fs::remove_dir_all(&dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Windows counterpart of `ntfs_short_name_entries_never_direct_deletions`:
|
||||||
|
/// alias-shaped files cannot exist as distinct fixtures here, so the guard
|
||||||
|
/// invariant is proven through the manifest filter instead. Only entries
|
||||||
|
/// accepted into the previous-manifest set can become removal candidates
|
||||||
|
/// (`previous.difference(shipped)`) or be claimed as owned
|
||||||
|
/// (`shipped ∩ previous`); listing the alias names in `shipped` makes the
|
||||||
|
/// second path observable in the rewritten manifest — a broken guard
|
||||||
|
/// would claim them. Where the volume generates 8.3 short names, the
|
||||||
|
/// self-collision that makes the unix fixture unrealizable is also
|
||||||
|
/// asserted directly; where generation is disabled, the alias simply
|
||||||
|
/// never materializes and the invariant assertions stand on their own.
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[test]
|
||||||
|
fn ntfs_short_name_entries_never_accepted_on_windows() {
|
||||||
|
let dir = fresh_dir("builtin-manifest-shortname-win-");
|
||||||
|
fs::write(
|
||||||
|
dir.join(BUILTIN_MANIFEST_FILE),
|
||||||
|
"BUILTI~1\nbuilti~1.sh\nkept.sh\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
fs::write(dir.join("kept.sh"), "current").unwrap();
|
||||||
|
|
||||||
|
let alias = dir.join("BUILTI~1");
|
||||||
|
if alias.exists() {
|
||||||
|
assert_eq!(
|
||||||
|
fs::read_to_string(&alias).unwrap(),
|
||||||
|
fs::read_to_string(dir.join(BUILTIN_MANIFEST_FILE)).unwrap(),
|
||||||
|
"the 8.3 alias opens the manifest itself, not a distinct file"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
reconcile_builtin_dir(
|
||||||
|
&dir,
|
||||||
|
&shipped(&["BUILTI~1", "builti~1.sh", "kept.sh"]),
|
||||||
|
&shipped(&[]),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(dir.join("kept.sh").exists(), "real shipped file untouched");
|
||||||
|
assert!(
|
||||||
|
dir.join(BUILTIN_MANIFEST_FILE).exists(),
|
||||||
|
"manifest survives reconciliation"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
fs::read_to_string(dir.join(BUILTIN_MANIFEST_FILE)).unwrap(),
|
||||||
|
"kept.sh\n",
|
||||||
|
"alias-shaped entries are never accepted: neither removal candidates nor claimed"
|
||||||
|
);
|
||||||
|
let _ = fs::remove_dir_all(&dir);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user