Keep mobile storage paths container-relative
This commit is contained in:
@@ -608,16 +608,23 @@ impl Config {
|
||||
if !vault.is_dir() {
|
||||
return Err(ConfigError::VaultIsNotDirectory { path: vault });
|
||||
}
|
||||
let vault = vault
|
||||
.to_str()
|
||||
.ok_or(ConfigError::InvalidField { field: "vault" })?;
|
||||
let mut document = self.document.clone();
|
||||
let root = document
|
||||
.as_table_mut()
|
||||
.ok_or_else(|| ConfigError::Malformed {
|
||||
path: self.source.clone(),
|
||||
})?;
|
||||
root.insert("vault".to_owned(), toml::Value::String(vault.to_owned()));
|
||||
if vault != self.vault {
|
||||
let base = self
|
||||
.source
|
||||
.parent()
|
||||
.ok_or(ConfigError::InvalidField { field: "source" })?;
|
||||
let stored = vault.strip_prefix(base).unwrap_or(&vault);
|
||||
root.insert(
|
||||
"vault".to_owned(),
|
||||
toml::Value::String(path_text(stored, "vault")?),
|
||||
);
|
||||
}
|
||||
root.insert(
|
||||
"default_key".to_owned(),
|
||||
toml::Value::String(settings.default_key),
|
||||
@@ -1152,7 +1159,7 @@ struct RawGitRemote {
|
||||
|
||||
fn validate_config(
|
||||
source: PathBuf,
|
||||
document: toml::Value,
|
||||
mut document: toml::Value,
|
||||
raw: RawConfig,
|
||||
) -> Result<Config, ConfigError> {
|
||||
let base = source
|
||||
@@ -1162,6 +1169,7 @@ fn validate_config(
|
||||
if vault.exists() && !vault.is_dir() {
|
||||
return Err(ConfigError::VaultIsNotDirectory { path: vault });
|
||||
}
|
||||
make_local_path_relative(&mut document, base, &vault, "vault")?;
|
||||
|
||||
let key_material = resolve_required_path(base, raw.key_material, "key_material")?;
|
||||
if !key_material.exists() {
|
||||
@@ -1176,6 +1184,7 @@ fn validate_config(
|
||||
}
|
||||
let key_material = fs::canonicalize(&key_material)
|
||||
.map_err(|_| ConfigError::InvalidKeyMaterial { path: key_material })?;
|
||||
make_local_path_relative(&mut document, base, &key_material, "key_material")?;
|
||||
|
||||
let default_key = raw
|
||||
.default_key
|
||||
@@ -1276,7 +1285,54 @@ fn resolve_required_path(
|
||||
if value.as_os_str().is_empty() {
|
||||
return Err(ConfigError::InvalidField { field });
|
||||
}
|
||||
Ok(resolve_path(base, &value))
|
||||
let resolved = resolve_path(base, &value);
|
||||
if resolved.exists() || !value.is_absolute() {
|
||||
return Ok(resolved);
|
||||
}
|
||||
Ok(relocated_mobile_path(base, &resolved, field).unwrap_or(resolved))
|
||||
}
|
||||
|
||||
fn relocated_mobile_path(base: &Path, path: &Path, field: &'static str) -> Option<PathBuf> {
|
||||
let leaf = match field {
|
||||
"vault" => "vault",
|
||||
"key_material" => "keys",
|
||||
_ => return None,
|
||||
};
|
||||
let suffix = Path::new("Library/Application Support/ironstorage").join(leaf);
|
||||
let is_stale_mobile_path = [
|
||||
Path::new("/private/var/mobile/Containers/Data/Application"),
|
||||
Path::new("/var/mobile/Containers/Data/Application"),
|
||||
]
|
||||
.iter()
|
||||
.any(|prefix| {
|
||||
path.strip_prefix(prefix).is_ok_and(|relative| {
|
||||
let mut components = relative.components();
|
||||
components.next().is_some() && components.as_path() == suffix
|
||||
})
|
||||
});
|
||||
let relocated = base.join(leaf);
|
||||
(is_stale_mobile_path && relocated.exists()).then_some(relocated)
|
||||
}
|
||||
|
||||
fn make_local_path_relative(
|
||||
document: &mut toml::Value,
|
||||
base: &Path,
|
||||
path: &Path,
|
||||
field: &'static str,
|
||||
) -> Result<(), ConfigError> {
|
||||
let Ok(relative) = path.strip_prefix(base) else {
|
||||
return Ok(());
|
||||
};
|
||||
let root = document
|
||||
.as_table_mut()
|
||||
.ok_or_else(|| ConfigError::Malformed {
|
||||
path: base.join(CONFIG_FILE),
|
||||
})?;
|
||||
root.insert(
|
||||
field.to_owned(),
|
||||
toml::Value::String(path_text(relative, field)?),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn path_text(path: &Path, field: &'static str) -> Result<String, ConfigError> {
|
||||
@@ -1649,4 +1705,31 @@ mod tests {
|
||||
("ALICE", Some(1_789_000_000), MobileTab::Preferences)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stale_ios_container_path_follows_the_current_container() {
|
||||
let temporary = tempfile::tempdir().expect("temporary directory");
|
||||
let vault = temporary.path().join("vault");
|
||||
let keys = temporary.path().join("keys");
|
||||
let source = temporary.path().join("config.toml");
|
||||
fs::create_dir(&vault).expect("vault");
|
||||
fs::create_dir(&keys).expect("keys");
|
||||
fs::write(
|
||||
&source,
|
||||
r#"vault = "/private/var/mobile/Containers/Data/Application/OLD-CONTAINER/Library/Application Support/ironstorage/vault"
|
||||
default_key = "ALICE"
|
||||
key_material = "keys"
|
||||
"#,
|
||||
)
|
||||
.expect("config");
|
||||
|
||||
let config = Config::load(Some(&source)).expect("recover moved container");
|
||||
assert_eq!(config.vault(), fs::canonicalize(vault).expect("vault path"));
|
||||
config
|
||||
.update_mobile_tab(MobileTab::Preferences)
|
||||
.expect("persist recovered config");
|
||||
let contents = fs::read_to_string(source).expect("read config");
|
||||
assert!(contents.contains("vault = \"vault\""));
|
||||
assert!(!contents.contains("OLD-CONTAINER"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -842,6 +842,9 @@ mod tests {
|
||||
.as_str(),
|
||||
ALICE_FINGERPRINT
|
||||
);
|
||||
let config = std::fs::read_to_string(fixture.config.source())?;
|
||||
assert!(config.contains("vault = \"vault\""));
|
||||
assert!(config.contains("key_material = \"keys\""));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -870,8 +873,8 @@ mod tests {
|
||||
std::fs::write(
|
||||
&config_path,
|
||||
format!(
|
||||
"vault = {:?}\ndefault_key = {:?}\nkey_material = {:?}\n",
|
||||
vault, ALICE_FINGERPRINT, keys
|
||||
"vault = \"vault\"\ndefault_key = {:?}\nkey_material = \"keys\"\n",
|
||||
ALICE_FINGERPRINT
|
||||
),
|
||||
)?;
|
||||
let config = Config::load(Some(&config_path))?;
|
||||
|
||||
Reference in New Issue
Block a user