fix(paths): accept literal tilde-digit filenames
Catalog generation and peer install/download validation rejected any path component containing a tilde followed by digits, even when the component was a valid long filename such as Bosons TD Gold~1.w3m. Remove the heuristic from all three validators and retain the existing device-name and portable-alias checks. Add a regression test for the literal filename so catalog publication and later path validation agree. Test Plan: - just clippy (passed) - just test (passed) - git diff --check (passed)
This commit is contained in:
@@ -184,9 +184,6 @@ fn validate_component(component: &str) -> eyre::Result<()> {
|
|||||||
if is_windows_device_name(device_stem) {
|
if is_windows_device_name(device_stem) {
|
||||||
eyre::bail!("catalog path uses a Windows device name: {component}");
|
eyre::bail!("catalog path uses a Windows device name: {component}");
|
||||||
}
|
}
|
||||||
if looks_like_dos_short_name(component) {
|
|
||||||
eyre::bail!("catalog path resembles a Windows short-name alias: {component}");
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,16 +203,6 @@ fn is_windows_device_name(stem: &str) -> bool {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn looks_like_dos_short_name(component: &str) -> bool {
|
|
||||||
let stem = component.split('.').next().unwrap_or_default();
|
|
||||||
stem.rsplit_once('~').is_some_and(|(prefix, suffix)| {
|
|
||||||
!prefix.is_empty()
|
|
||||||
&& !suffix.is_empty()
|
|
||||||
&& suffix.len() <= 6
|
|
||||||
&& suffix.bytes().all(|byte| byte.is_ascii_digit())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -242,7 +229,6 @@ mod tests {
|
|||||||
"NUL.txt",
|
"NUL.txt",
|
||||||
"com1",
|
"com1",
|
||||||
"LPT¹.log",
|
"LPT¹.log",
|
||||||
"LOCAL~1/file",
|
|
||||||
"Donne\u{301}es/file",
|
"Donne\u{301}es/file",
|
||||||
] {
|
] {
|
||||||
assert!(
|
assert!(
|
||||||
@@ -252,6 +238,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn accepts_literal_tilde_digit_names() {
|
||||||
|
assert!(CanonicalCatalogPath::new("Bosons TD Gold~1.w3m").is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn aliases_are_conservative_across_platforms() {
|
fn aliases_are_conservative_across_platforms() {
|
||||||
let left = CanonicalCatalogPath::new("Straße/FILE").expect("path should validate");
|
let left = CanonicalCatalogPath::new("Straße/FILE").expect("path should validate");
|
||||||
|
|||||||
@@ -370,9 +370,6 @@ fn validate_component(component: &str) -> eyre::Result<()> {
|
|||||||
if is_windows_device_name(device_stem) {
|
if is_windows_device_name(device_stem) {
|
||||||
eyre::bail!("download path uses a Windows device name: {component}");
|
eyre::bail!("download path uses a Windows device name: {component}");
|
||||||
}
|
}
|
||||||
if looks_like_dos_short_name(component) {
|
|
||||||
eyre::bail!("download path resembles a Windows short-name alias: {component}");
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,16 +390,6 @@ fn is_windows_device_name(stem: &str) -> bool {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn looks_like_dos_short_name(component: &str) -> bool {
|
|
||||||
let stem = component.split('.').next().unwrap_or_default();
|
|
||||||
stem.rsplit_once('~').is_some_and(|(prefix, suffix)| {
|
|
||||||
!prefix.is_empty()
|
|
||||||
&& !suffix.is_empty()
|
|
||||||
&& suffix.len() <= 6
|
|
||||||
&& suffix.bytes().all(|byte| byte.is_ascii_digit())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_existing_destination(
|
fn validate_existing_destination(
|
||||||
game_root: &Path,
|
game_root: &Path,
|
||||||
components: &[&str],
|
components: &[&str],
|
||||||
|
|||||||
@@ -374,9 +374,6 @@ pub(super) fn validate_game_id(game_id: &str) -> eyre::Result<()> {
|
|||||||
if is_windows_device_name(device_stem) {
|
if is_windows_device_name(device_stem) {
|
||||||
eyre::bail!("game ID uses a Windows device name: {game_id}");
|
eyre::bail!("game ID uses a Windows device name: {game_id}");
|
||||||
}
|
}
|
||||||
if looks_like_dos_short_name(game_id) {
|
|
||||||
eyre::bail!("game ID resembles a Windows short-name alias: {game_id}");
|
|
||||||
}
|
|
||||||
if is_download_protected_root_name(game_id) {
|
if is_download_protected_root_name(game_id) {
|
||||||
eyre::bail!("game ID is reserved for application state: {game_id}");
|
eyre::bail!("game ID is reserved for application state: {game_id}");
|
||||||
}
|
}
|
||||||
@@ -395,16 +392,6 @@ fn is_windows_device_name(stem: &str) -> bool {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn looks_like_dos_short_name(component: &str) -> bool {
|
|
||||||
let stem = component.split('.').next().unwrap_or_default();
|
|
||||||
stem.rsplit_once('~').is_some_and(|(prefix, suffix)| {
|
|
||||||
!prefix.is_empty()
|
|
||||||
&& !suffix.is_empty()
|
|
||||||
&& suffix.len() <= 6
|
|
||||||
&& suffix.bytes().all(|byte| byte.is_ascii_digit())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn open_ambient_directory_nofollow(path: &Path) -> eyre::Result<File> {
|
fn open_ambient_directory_nofollow(path: &Path) -> eyre::Result<File> {
|
||||||
let directory = fs::open_ambient(path, &directory_options(), ambient_authority())?;
|
let directory = fs::open_ambient(path, &directory_options(), ambient_authority())?;
|
||||||
validate_directory_handle(&directory, &path.display().to_string())?;
|
validate_directory_handle(&directory, &path.display().to_string())?;
|
||||||
|
|||||||
Reference in New Issue
Block a user