mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-21 16:36:50 +00:00
fix(torrents): prevent unmanaged followed child GIDs
This commit is contained in:
@@ -53,6 +53,11 @@ belong in the download UI. The Aria2 reference is the [1.37.0 manual](https://ar
|
|||||||
timeouts are bounded to 1–604800 seconds; interval 0 restores Aria2's
|
timeouts are bounded to 1–604800 seconds; interval 0 restores Aria2's
|
||||||
response/progress-driven scheduling. Timing is persisted and reapplied when
|
response/progress-driven scheduling. Timing is persisted and reapplied when
|
||||||
a Torrent starts or retries.
|
a Torrent starts or retries.
|
||||||
|
- Generic Aria2 downloads explicitly disable `follow-torrent` and
|
||||||
|
`follow-metalink`, so a URL that happens to return Torrent or Metalink
|
||||||
|
metadata cannot create an unmanaged child GID. Generic follow behavior is
|
||||||
|
not exposed until parent/child GID ownership is represented across queue
|
||||||
|
admission, progress, cancellation, retry, and restart recovery.
|
||||||
- Global `bt-max-open-files` control for multi-file Torrents, bounded to
|
- Global `bt-max-open-files` control for multi-file Torrents, bounded to
|
||||||
1–4096 with Aria2's default of 100. The setting is persisted, applied at
|
1–4096 with Aria2's default of 100. The setting is persisted, applied at
|
||||||
daemon startup, and updateable through Aria2's global-option RPC; changes
|
daemon startup, and updateable through Aria2's global-option RPC; changes
|
||||||
@@ -83,6 +88,9 @@ No remaining Tier 1 items.
|
|||||||
1. Aria2 `follow-torrent`/in-memory follow behavior for generic downloads only
|
1. Aria2 `follow-torrent`/in-memory follow behavior for generic downloads only
|
||||||
if the resulting child-GID ownership model can be represented safely; the
|
if the resulting child-GID ownership model can be represented safely; the
|
||||||
current explicit metadata path intentionally avoids unmapped child jobs.
|
current explicit metadata path intentionally avoids unmapped child jobs.
|
||||||
|
Generic `addUri` now forces both follow options to `false` as the safe
|
||||||
|
default; the child-GID feature remains pending until the end-to-end
|
||||||
|
ownership model is implemented.
|
||||||
|
|
||||||
The first implementation in this task was remote `.torrent` metadata intake;
|
The first implementation in this task was remote `.torrent` metadata intake;
|
||||||
follow-up implementations add stall-timeout control, bounded peer diagnostics,
|
follow-up implementations add stall-timeout control, bounded peer diagnostics,
|
||||||
|
|||||||
@@ -3408,6 +3408,27 @@ fn apply_aria2_connection_options(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_aria2_follow_options(
|
||||||
|
options: &mut serde_json::Map<String, serde_json::Value>,
|
||||||
|
payload: &SpawnPayload,
|
||||||
|
) {
|
||||||
|
if !payload.is_torrent {
|
||||||
|
// A generic addUri can point at a .torrent or Metalink file. Aria2
|
||||||
|
// may then create a second, followed child GID, but Firelink
|
||||||
|
// currently owns exactly one GID per download. Keep that unmanaged
|
||||||
|
// child lifecycle impossible until parent/child ownership is modeled
|
||||||
|
// end to end.
|
||||||
|
options.insert(
|
||||||
|
"follow-torrent".to_string(),
|
||||||
|
serde_json::json!("false"),
|
||||||
|
);
|
||||||
|
options.insert(
|
||||||
|
"follow-metalink".to_string(),
|
||||||
|
serde_json::json!("false"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn format_aria2_torrent_number(value: f64, field: &str) -> Result<String, String> {
|
fn format_aria2_torrent_number(value: f64, field: &str) -> Result<String, String> {
|
||||||
if !value.is_finite() || value < 0.0 {
|
if !value.is_finite() || value < 0.0 {
|
||||||
return Err(format!("torrent {field} must be a finite non-negative number"));
|
return Err(format!("torrent {field} must be a finite non-negative number"));
|
||||||
@@ -3981,6 +4002,7 @@ impl SidecarSpawner for ProductionSpawner {
|
|||||||
}
|
}
|
||||||
let conn = effective_aria2_connections(id, payload).await;
|
let conn = effective_aria2_connections(id, payload).await;
|
||||||
apply_aria2_connection_options(&mut options, conn);
|
apply_aria2_connection_options(&mut options, conn);
|
||||||
|
apply_aria2_follow_options(&mut options, payload);
|
||||||
apply_aria2_torrent_options(&mut options, payload)?;
|
apply_aria2_torrent_options(&mut options, payload)?;
|
||||||
let mt = aria2_attempt_limit(payload.max_tries);
|
let mt = aria2_attempt_limit(payload.max_tries);
|
||||||
options.insert("max-tries".to_string(), serde_json::json!(mt.to_string()));
|
options.insert("max-tries".to_string(), serde_json::json!(mt.to_string()));
|
||||||
@@ -4675,6 +4697,35 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generic_aria2_downloads_disable_followed_child_gids() {
|
||||||
|
let mut options = serde_json::Map::new();
|
||||||
|
apply_aria2_follow_options(&mut options, &SpawnPayload::default());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
options.get("follow-torrent"),
|
||||||
|
Some(&serde_json::json!("false"))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
options.get("follow-metalink"),
|
||||||
|
Some(&serde_json::json!("false"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn explicit_torrent_downloads_do_not_override_follow_policy() {
|
||||||
|
let mut options = serde_json::Map::new();
|
||||||
|
let payload = SpawnPayload {
|
||||||
|
is_torrent: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
apply_aria2_follow_options(&mut options, &payload);
|
||||||
|
|
||||||
|
assert!(!options.contains_key("follow-torrent"));
|
||||||
|
assert!(!options.contains_key("follow-metalink"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn torrent_encryption_policy_maps_to_one_consistent_aria2_policy() {
|
fn torrent_encryption_policy_maps_to_one_consistent_aria2_policy() {
|
||||||
let cases = [
|
let cases = [
|
||||||
|
|||||||
@@ -23,11 +23,16 @@ pub(crate) enum ProbeFailure {
|
|||||||
pub(crate) async fn run_metadata_probe<C: RpcClient + 'static>(
|
pub(crate) async fn run_metadata_probe<C: RpcClient + 'static>(
|
||||||
client: Arc<C>,
|
client: Arc<C>,
|
||||||
source: &str,
|
source: &str,
|
||||||
options: Map<String, Value>,
|
mut options: Map<String, Value>,
|
||||||
metadata_path: &Path,
|
metadata_path: &Path,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
poll_interval: Duration,
|
poll_interval: Duration,
|
||||||
) -> Result<Vec<u8>, ProbeFailure> {
|
) -> Result<Vec<u8>, ProbeFailure> {
|
||||||
|
// This probe only resolves magnet metadata. It must never allow Aria2 to
|
||||||
|
// interpret a downloaded metadata file as another child download because
|
||||||
|
// the probe cleanup guard owns exactly one GID.
|
||||||
|
options.insert("follow-torrent".to_string(), json!("false"));
|
||||||
|
options.insert("follow-metalink".to_string(), json!("false"));
|
||||||
let mut cleanup_guard = ProbeCleanupGuard::new(Arc::clone(&client), metadata_path);
|
let mut cleanup_guard = ProbeCleanupGuard::new(Arc::clone(&client), metadata_path);
|
||||||
let result = match client
|
let result = match client
|
||||||
.call("aria2.addUri", json!([[source], options]))
|
.call("aria2.addUri", json!([[source], options]))
|
||||||
@@ -1018,6 +1023,16 @@ mod tests {
|
|||||||
Some(&json!("true")),
|
Some(&json!("true")),
|
||||||
"recorded addUri params: {add_params:?}"
|
"recorded addUri params: {add_params:?}"
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
options.get("follow-torrent"),
|
||||||
|
Some(&json!("false")),
|
||||||
|
"recorded addUri params: {add_params:?}"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
options.get("follow-metalink"),
|
||||||
|
Some(&json!("false")),
|
||||||
|
"recorded addUri params: {add_params:?}"
|
||||||
|
);
|
||||||
tokio::fs::remove_dir_all(&probe_dir)
|
tokio::fs::remove_dir_all(&probe_dir)
|
||||||
.await
|
.await
|
||||||
.expect("successful probe fixture should be removable");
|
.expect("successful probe fixture should be removable");
|
||||||
|
|||||||
Reference in New Issue
Block a user