From 452e34478144fb505cedb423e81f7701837ccd99 Mon Sep 17 00:00:00 2001 From: Daniel Watkins Date: Fri, 28 Oct 2022 12:26:05 -0400 Subject: [PATCH 01/12] signing: refactor verify_clearsigned to read_clearsigned This sets us up for supporting [untrusted=yes] repositories. --- src/release.rs | 2 +- src/signing.rs | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/release.rs b/src/release.rs index fe79dbb..04e3739 100644 --- a/src/release.rs +++ b/src/release.rs @@ -173,7 +173,7 @@ impl RequestedReleases { &dest, )], ) { - Ok(_) => gpg.verify_clearsigned(&dest, &verified), + Ok(_) => gpg.read_clearsigned(&dest, &verified, true), Err(_) => { let mut detatched_signature = dest.as_os_str().to_os_string(); detatched_signature.push(".gpg"); diff --git a/src/signing.rs b/src/signing.rs index c1a7f3c..a1e2f8f 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -17,10 +17,11 @@ impl<'k> GpgClient<'k> { GpgClient { keyring } } - pub fn verify_clearsigned, Q: AsRef>( + pub fn read_clearsigned, Q: AsRef>( &self, file: P, dest: Q, + verify: bool, ) -> Result<(), Error> { let from = fs::File::open(file).with_context(|| anyhow!("opening input file"))?; let to = PersistableTempFile::new_in( @@ -30,7 +31,12 @@ impl<'k> GpgClient<'k> { ) .with_context(|| anyhow!("creating temporary file"))?; - gpgrv::verify_message(io::BufReader::new(from), &to, &self.keyring)?; + let reader = io::BufReader::new(from); + if verify { + gpgrv::verify_message(reader, &to, &self.keyring)?; + } else { + gpgrv::read_doc(reader, &to)?; + } to.persist_by_rename(dest) .map_err(|e| e.error) From 9f309ff06a91efacf2c9a807f2d94c9a188ef879 Mon Sep 17 00:00:00 2001 From: Daniel Watkins Date: Fri, 28 Oct 2022 12:38:56 -0400 Subject: [PATCH 02/12] release: add RequestedRelease.untrusted And use it throughout the codebase. --- src/release.rs | 4 +++- src/sources_list.rs | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/release.rs b/src/release.rs index 04e3739..15462c5 100644 --- a/src/release.rs +++ b/src/release.rs @@ -38,6 +38,7 @@ pub struct RequestedRelease { pub codename: String, pub arches: Vec, + pub untrusted: bool, } #[derive(Debug, Clone)] @@ -139,6 +140,7 @@ impl RequestedReleases { mirror: Url::parse(&entry.url)?, codename: entry.suite_codename.to_string(), arches: arches.to_vec(), + untrusted: entry.untrusted, }) { hash_map::Entry::Vacant(vacancy) => { vacancy.insert(vec![entry.clone()]); @@ -173,7 +175,7 @@ impl RequestedReleases { &dest, )], ) { - Ok(_) => gpg.read_clearsigned(&dest, &verified, true), + Ok(_) => gpg.read_clearsigned(&dest, &verified, !release.untrusted), Err(_) => { let mut detatched_signature = dest.as_os_str().to_os_string(); detatched_signature.push(".gpg"); diff --git a/src/sources_list.rs b/src/sources_list.rs index 91de552..9d460d7 100644 --- a/src/sources_list.rs +++ b/src/sources_list.rs @@ -15,6 +15,7 @@ pub struct Entry { pub suite_codename: String, pub components: Vec, pub arch: Option, + pub untrusted: bool, } fn read_single_line(line: &str) -> Result, Error> { @@ -71,6 +72,7 @@ fn read_single_line(line: &str) -> Result, Error> { suite_codename: suite.to_string(), components: components.iter().map(|x| x.to_string()).collect(), arch: arch.map(|arch| arch.to_string()), + untrusted: false, }); } @@ -113,6 +115,7 @@ mod tests { url: "http://foo/".to_string(), suite_codename: "bar".to_string(), components: vec!["baz".to_string(), "quux".to_string()], + untrusted: false, }, Entry { src: true, @@ -120,6 +123,7 @@ mod tests { url: "http://foo/".to_string(), suite_codename: "bar".to_string(), components: vec!["baz".to_string(), "quux".to_string()], + untrusted: false, }, ], read(io::Cursor::new( From ab138b5ff4af0cb1dc2d97bf694257ab681c029d Mon Sep 17 00:00:00 2001 From: Daniel Watkins Date: Fri, 28 Oct 2022 13:09:04 -0400 Subject: [PATCH 03/12] sources_list: parse arch correctly --- src/sources_list.rs | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/sources_list.rs b/src/sources_list.rs index 9d460d7..76924d7 100644 --- a/src/sources_list.rs +++ b/src/sources_list.rs @@ -18,6 +18,28 @@ pub struct Entry { pub untrusted: bool, } +fn parse_opts(opts: &str) -> &str { + if opts.contains(" ") { + panic!("only one option per line supported") + } + let parts: Vec<_> = opts + .strip_prefix("[") + .expect("opening [") + .strip_suffix("]") + .expect("closing ]") + .split("=") + .collect(); + match parts.len() { + 2 => { + if parts[0] != "arch" { + panic!("unknown option: {}", parts[0]) + } + parts[1] + } + _ => panic!("multiple = in option"), + } +} + fn read_single_line(line: &str) -> Result, Error> { let line = match line.find('#') { Some(comment) => &line[..comment], @@ -34,7 +56,7 @@ fn read_single_line(line: &str) -> Result, Error> { let src = parts .next() .ok_or_else(|| anyhow!("deb{{,s,-src}} section required"))?; - let arch = match parts.peek() { + let opts = match parts.peek() { Some(&val) if val.starts_with("[") => { parts.next(); Some(val) @@ -61,6 +83,7 @@ fn read_single_line(line: &str) -> Result, Error> { let mut ret = Vec::with_capacity(srcs.len()); + let arch = opts.map(|opts| parse_opts(opts)); for src in srcs { ret.push(Entry { src: *src, @@ -130,6 +153,26 @@ mod tests { r" deb http://foo bar baz quux deb-src http://foo bar baz quux +", + )) + .unwrap() + ); + } + + #[test] + fn arch() { + assert_eq!( + vec![Entry { + src: false, + arch: Some("amd64".to_string()), + url: "http://foo/".to_string(), + suite_codename: "bar".to_string(), + components: vec!["baz".to_string(), "quux".to_string()], + untrusted: false, + },], + read(io::Cursor::new( + r" +deb [arch=amd64] http://foo bar baz quux ", )) .unwrap() From d68a5166b24a683f6e1353e75a74abc37e85e90c Mon Sep 17 00:00:00 2001 From: Daniel Watkins Date: Fri, 28 Oct 2022 13:11:58 -0400 Subject: [PATCH 04/12] sources_list: introduce ParsedOpts --- src/sources_list.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/sources_list.rs b/src/sources_list.rs index 76924d7..377c6ef 100644 --- a/src/sources_list.rs +++ b/src/sources_list.rs @@ -18,7 +18,11 @@ pub struct Entry { pub untrusted: bool, } -fn parse_opts(opts: &str) -> &str { +struct ParsedOpts { + arch: String, +} + +fn parse_opts(opts: &str) -> ParsedOpts { if opts.contains(" ") { panic!("only one option per line supported") } @@ -34,7 +38,9 @@ fn parse_opts(opts: &str) -> &str { if parts[0] != "arch" { panic!("unknown option: {}", parts[0]) } - parts[1] + ParsedOpts { + arch: parts[1].to_string(), + } } _ => panic!("multiple = in option"), } @@ -83,7 +89,7 @@ fn read_single_line(line: &str) -> Result, Error> { let mut ret = Vec::with_capacity(srcs.len()); - let arch = opts.map(|opts| parse_opts(opts)); + let parsed_opts = opts.map(|opts| parse_opts(opts)); for src in srcs { ret.push(Entry { src: *src, @@ -94,7 +100,9 @@ fn read_single_line(line: &str) -> Result, Error> { }, suite_codename: suite.to_string(), components: components.iter().map(|x| x.to_string()).collect(), - arch: arch.map(|arch| arch.to_string()), + arch: parsed_opts + .as_ref() + .map(|parsed_opts| parsed_opts.arch.clone()), untrusted: false, }); } From 82c4d536b789dc41aca3460e53aaa335d8b7ce66 Mon Sep 17 00:00:00 2001 From: Daniel Watkins Date: Fri, 28 Oct 2022 13:20:12 -0400 Subject: [PATCH 05/12] further preparatory refactoring --- src/sources_list.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sources_list.rs b/src/sources_list.rs index 377c6ef..b44e2aa 100644 --- a/src/sources_list.rs +++ b/src/sources_list.rs @@ -19,7 +19,7 @@ pub struct Entry { } struct ParsedOpts { - arch: String, + arch: Option, } fn parse_opts(opts: &str) -> ParsedOpts { @@ -34,14 +34,12 @@ fn parse_opts(opts: &str) -> ParsedOpts { .split("=") .collect(); match parts.len() { - 2 => { - if parts[0] != "arch" { - panic!("unknown option: {}", parts[0]) - } - ParsedOpts { - arch: parts[1].to_string(), - } - } + 2 => match parts[0] { + "arch" => ParsedOpts { + arch: Some(parts[1].to_string()), + }, + other => panic!("unknown option: {}", other), + }, _ => panic!("multiple = in option"), } } @@ -89,7 +87,11 @@ fn read_single_line(line: &str) -> Result, Error> { let mut ret = Vec::with_capacity(srcs.len()); - let parsed_opts = opts.map(|opts| parse_opts(opts)); + let arch = if let Some(parsed_opts) = opts.map(|opts| parse_opts(opts)) { + parsed_opts.arch + } else { + None + }; for src in srcs { ret.push(Entry { src: *src, @@ -100,9 +102,7 @@ fn read_single_line(line: &str) -> Result, Error> { }, suite_codename: suite.to_string(), components: components.iter().map(|x| x.to_string()).collect(), - arch: parsed_opts - .as_ref() - .map(|parsed_opts| parsed_opts.arch.clone()), + arch: arch.clone(), untrusted: false, }); } From da0fe925ef5dd653b21caea7fb1a279f39611812 Mon Sep 17 00:00:00 2001 From: Daniel Watkins Date: Fri, 28 Oct 2022 13:24:19 -0400 Subject: [PATCH 06/12] sources_list: parse untrusted option --- src/sources_list.rs | 54 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/src/sources_list.rs b/src/sources_list.rs index b44e2aa..7898bc7 100644 --- a/src/sources_list.rs +++ b/src/sources_list.rs @@ -20,6 +20,7 @@ pub struct Entry { struct ParsedOpts { arch: Option, + untrusted: Option, } fn parse_opts(opts: &str) -> ParsedOpts { @@ -37,6 +38,11 @@ fn parse_opts(opts: &str) -> ParsedOpts { 2 => match parts[0] { "arch" => ParsedOpts { arch: Some(parts[1].to_string()), + untrusted: None, + }, + "untrusted" => ParsedOpts { + arch: None, + untrusted: Some(parts[1] == "yes"), }, other => panic!("unknown option: {}", other), }, @@ -87,10 +93,10 @@ fn read_single_line(line: &str) -> Result, Error> { let mut ret = Vec::with_capacity(srcs.len()); - let arch = if let Some(parsed_opts) = opts.map(|opts| parse_opts(opts)) { - parsed_opts.arch + let (arch, untrusted) = if let Some(parsed_opts) = opts.map(|opts| parse_opts(opts)) { + (parsed_opts.arch, parsed_opts.untrusted) } else { - None + (None, None) }; for src in srcs { ret.push(Entry { @@ -103,7 +109,7 @@ fn read_single_line(line: &str) -> Result, Error> { suite_codename: suite.to_string(), components: components.iter().map(|x| x.to_string()).collect(), arch: arch.clone(), - untrusted: false, + untrusted: untrusted.unwrap_or(false), }); } @@ -181,6 +187,46 @@ deb-src http://foo bar baz quux read(io::Cursor::new( r" deb [arch=amd64] http://foo bar baz quux +", + )) + .unwrap() + ); + } + + #[test] + fn untrusted() { + assert_eq!( + vec![Entry { + src: false, + arch: None, + url: "http://foo/".to_string(), + suite_codename: "bar".to_string(), + components: vec!["baz".to_string(), "quux".to_string()], + untrusted: true, + },], + read(io::Cursor::new( + r" +deb [untrusted=yes] http://foo bar baz quux +", + )) + .unwrap() + ); + } + + #[test] + fn untrusted_not_yes() { + assert_eq!( + vec![Entry { + src: false, + arch: None, + url: "http://foo/".to_string(), + suite_codename: "bar".to_string(), + components: vec!["baz".to_string(), "quux".to_string()], + untrusted: false, + },], + read(io::Cursor::new( + r" +deb [untrusted=yeppers] http://foo bar baz quux ", )) .unwrap() From 94c3089c3bef1b61f1c840e788377501c55702bf Mon Sep 17 00:00:00 2001 From: Daniel Watkins Date: Fri, 28 Oct 2022 13:31:12 -0400 Subject: [PATCH 07/12] sources_list: simplifying refactor --- src/sources_list.rs | 61 +++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/sources_list.rs b/src/sources_list.rs index 7898bc7..03928fb 100644 --- a/src/sources_list.rs +++ b/src/sources_list.rs @@ -18,35 +18,40 @@ pub struct Entry { pub untrusted: bool, } +#[derive(Default)] struct ParsedOpts { arch: Option, untrusted: Option, } -fn parse_opts(opts: &str) -> ParsedOpts { - if opts.contains(" ") { - panic!("only one option per line supported") - } - let parts: Vec<_> = opts - .strip_prefix("[") - .expect("opening [") - .strip_suffix("]") - .expect("closing ]") - .split("=") - .collect(); - match parts.len() { - 2 => match parts[0] { - "arch" => ParsedOpts { - arch: Some(parts[1].to_string()), - untrusted: None, - }, - "untrusted" => ParsedOpts { - arch: None, - untrusted: Some(parts[1] == "yes"), +fn parse_opts(opts: Option<&str>) -> ParsedOpts { + if let Some(opts) = opts { + if opts.contains(" ") { + panic!("only one option per line supported") + } + let parts: Vec<_> = opts + .strip_prefix("[") + .expect("opening [") + .strip_suffix("]") + .expect("closing ]") + .split("=") + .collect(); + match parts.len() { + 2 => match parts[0] { + "arch" => ParsedOpts { + arch: Some(parts[1].to_string()), + untrusted: None, + }, + "untrusted" => ParsedOpts { + arch: None, + untrusted: Some(parts[1] == "yes"), + }, + other => panic!("unknown option: {}", other), }, - other => panic!("unknown option: {}", other), - }, - _ => panic!("multiple = in option"), + _ => panic!("multiple = in option"), + } + } else { + ParsedOpts::default() } } @@ -93,11 +98,7 @@ fn read_single_line(line: &str) -> Result, Error> { let mut ret = Vec::with_capacity(srcs.len()); - let (arch, untrusted) = if let Some(parsed_opts) = opts.map(|opts| parse_opts(opts)) { - (parsed_opts.arch, parsed_opts.untrusted) - } else { - (None, None) - }; + let parsed_opts = parse_opts(opts); for src in srcs { ret.push(Entry { src: *src, @@ -108,8 +109,8 @@ fn read_single_line(line: &str) -> Result, Error> { }, suite_codename: suite.to_string(), components: components.iter().map(|x| x.to_string()).collect(), - arch: arch.clone(), - untrusted: untrusted.unwrap_or(false), + arch: parsed_opts.arch.clone(), + untrusted: parsed_opts.untrusted.unwrap_or(false), }); } From 1ddfadddd12ed7a9df45fd4b659f6b56829c2e48 Mon Sep 17 00:00:00 2001 From: Daniel Watkins Date: Fri, 28 Oct 2022 13:37:10 -0400 Subject: [PATCH 08/12] sources_list: return Result from parse_opts --- src/sources_list.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sources_list.rs b/src/sources_list.rs index 03928fb..a651d9c 100644 --- a/src/sources_list.rs +++ b/src/sources_list.rs @@ -24,10 +24,10 @@ struct ParsedOpts { untrusted: Option, } -fn parse_opts(opts: Option<&str>) -> ParsedOpts { - if let Some(opts) = opts { +fn parse_opts(opts: Option<&str>) -> Result { + Ok(if let Some(opts) = opts { if opts.contains(" ") { - panic!("only one option per line supported") + bail!("only one option per line supported") } let parts: Vec<_> = opts .strip_prefix("[") @@ -46,13 +46,13 @@ fn parse_opts(opts: Option<&str>) -> ParsedOpts { arch: None, untrusted: Some(parts[1] == "yes"), }, - other => panic!("unknown option: {}", other), + other => bail!("unknown option: {}", other), }, - _ => panic!("multiple = in option"), + _ => bail!("multiple = in option"), } } else { ParsedOpts::default() - } + }) } fn read_single_line(line: &str) -> Result, Error> { @@ -98,7 +98,7 @@ fn read_single_line(line: &str) -> Result, Error> { let mut ret = Vec::with_capacity(srcs.len()); - let parsed_opts = parse_opts(opts); + let parsed_opts = parse_opts(opts)?; for src in srcs { ret.push(Entry { src: *src, From 5cfa0c0eb6d06feb0cb08af8fcb57beb38fe282d Mon Sep 17 00:00:00 2001 From: Daniel Watkins Date: Fri, 28 Oct 2022 13:41:52 -0400 Subject: [PATCH 09/12] release: don't check detached signatures if untrusted=yes --- src/release.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/release.rs b/src/release.rs index 15462c5..6f02afc 100644 --- a/src/release.rs +++ b/src/release.rs @@ -185,14 +185,18 @@ impl RequestedReleases { &[Download::from_to(release.dists()?.join("Release")?, &dest)], )?; - fetch( - client, - &[Download::from_to( - release.dists()?.join("Release.gpg")?, - &detatched_signature, - )], - )?; - gpg.verify_detached(&dest, detatched_signature, verified) + if !release.untrusted { + fetch( + client, + &[Download::from_to( + release.dists()?.join("Release.gpg")?, + &detatched_signature, + )], + )?; + gpg.verify_detached(&dest, detatched_signature, verified) + } else { + Ok(()) + } } } .with_context(|| anyhow!("verifying {:?} at {:?}", release, dest))?; From 8a125d741c8d55d12fc1ca8e51b029b81cfbc515 Mon Sep 17 00:00:00 2001 From: Daniel Watkins Date: Fri, 28 Oct 2022 14:47:49 -0400 Subject: [PATCH 10/12] parse_opts: simplifying refactor --- src/sources_list.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/sources_list.rs b/src/sources_list.rs index a651d9c..0e2a876 100644 --- a/src/sources_list.rs +++ b/src/sources_list.rs @@ -25,7 +25,8 @@ struct ParsedOpts { } fn parse_opts(opts: Option<&str>) -> Result { - Ok(if let Some(opts) = opts { + let mut ret = ParsedOpts::default(); + if let Some(opts) = opts { if opts.contains(" ") { bail!("only one option per line supported") } @@ -38,21 +39,14 @@ fn parse_opts(opts: Option<&str>) -> Result { .collect(); match parts.len() { 2 => match parts[0] { - "arch" => ParsedOpts { - arch: Some(parts[1].to_string()), - untrusted: None, - }, - "untrusted" => ParsedOpts { - arch: None, - untrusted: Some(parts[1] == "yes"), - }, + "arch" => ret.arch = Some(parts[1].to_string()), + "untrusted" => ret.untrusted = Some(parts[1] == "yes"), other => bail!("unknown option: {}", other), }, _ => bail!("multiple = in option"), } - } else { - ParsedOpts::default() - }) + }; + Ok(ret) } fn read_single_line(line: &str) -> Result, Error> { From db710a59fe7449681d47aa6d31550195e04bec5d Mon Sep 17 00:00:00 2001 From: Daniel Watkins Date: Fri, 28 Oct 2022 15:22:34 -0400 Subject: [PATCH 11/12] sources_list: parse multiple options --- src/sources_list.rs | 64 +++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/src/sources_list.rs b/src/sources_list.rs index 0e2a876..6cd68f4 100644 --- a/src/sources_list.rs +++ b/src/sources_list.rs @@ -24,19 +24,10 @@ struct ParsedOpts { untrusted: Option, } -fn parse_opts(opts: Option<&str>) -> Result { +fn parse_opts(opt_parts: Vec<&str>) -> Result { let mut ret = ParsedOpts::default(); - if let Some(opts) = opts { - if opts.contains(" ") { - bail!("only one option per line supported") - } - let parts: Vec<_> = opts - .strip_prefix("[") - .expect("opening [") - .strip_suffix("]") - .expect("closing ]") - .split("=") - .collect(); + for opt in opt_parts { + let parts: Vec<_> = opt.split("=").collect(); match parts.len() { 2 => match parts[0] { "arch" => ret.arch = Some(parts[1].to_string()), @@ -45,7 +36,7 @@ fn parse_opts(opts: Option<&str>) -> Result { }, _ => bail!("multiple = in option"), } - }; + } Ok(ret) } @@ -65,14 +56,27 @@ fn read_single_line(line: &str) -> Result, Error> { let src = parts .next() .ok_or_else(|| anyhow!("deb{{,s,-src}} section required"))?; - let opts = match parts.peek() { - Some(&val) if val.starts_with("[") => { + + let mut opt_parts = vec![]; + if let Some(&(mut val)) = parts.peek() { + if val.starts_with("[") { + val = val.strip_prefix("[").expect("opening ["); parts.next(); - Some(val) + loop { + if val.ends_with("]") { + opt_parts.push(val.strip_suffix("]").expect("closing ]")); + break; + } + opt_parts.push(val); + match parts.next() { + Some(next) => { + val = next; + } + None => bail!("unexpected end of line reading options"), + } + } } - Some(_) => None, - None => bail!("unexpected end of line looking for arch or url"), - }; + } let url = parts .next() @@ -92,7 +96,7 @@ fn read_single_line(line: &str) -> Result, Error> { let mut ret = Vec::with_capacity(srcs.len()); - let parsed_opts = parse_opts(opts)?; + let parsed_opts = parse_opts(opt_parts)?; for src in srcs { ret.push(Entry { src: *src, @@ -222,6 +226,26 @@ deb [untrusted=yes] http://foo bar baz quux read(io::Cursor::new( r" deb [untrusted=yeppers] http://foo bar baz quux +", + )) + .unwrap() + ); + } + + #[test] + fn multiple_opt() { + assert_eq!( + vec![Entry { + src: false, + arch: Some("amd64".to_string()), + url: "http://foo/".to_string(), + suite_codename: "bar".to_string(), + components: vec!["baz".to_string(), "quux".to_string()], + untrusted: true, + },], + read(io::Cursor::new( + r" +deb [untrusted=yes arch=amd64] http://foo bar baz quux ", )) .unwrap() From ec30172457881b75745cdad98535df0890e2938d Mon Sep 17 00:00:00 2001 From: Daniel Watkins Date: Wed, 2 Nov 2022 15:12:02 -0400 Subject: [PATCH 12/12] sources_list: support parsing multiple arch options --- src/lists.rs | 2 +- src/sources_list.rs | 30 +++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/lists.rs b/src/lists.rs index ac51dea..6cb308b 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -183,7 +183,7 @@ pub fn selected_listings(release: &Release) -> Vec { } if let Some(ref entry_arch) = entry.arch { - if arch != entry_arch { + if !entry_arch.contains(arch) { continue; } } diff --git a/src/sources_list.rs b/src/sources_list.rs index 6cd68f4..200ba71 100644 --- a/src/sources_list.rs +++ b/src/sources_list.rs @@ -14,13 +14,13 @@ pub struct Entry { pub url: String, pub suite_codename: String, pub components: Vec, - pub arch: Option, + pub arch: Option>, pub untrusted: bool, } #[derive(Default)] struct ParsedOpts { - arch: Option, + arch: Option>, untrusted: Option, } @@ -30,7 +30,7 @@ fn parse_opts(opt_parts: Vec<&str>) -> Result { let parts: Vec<_> = opt.split("=").collect(); match parts.len() { 2 => match parts[0] { - "arch" => ret.arch = Some(parts[1].to_string()), + "arch" => ret.arch = Some(parts[1].split(',').map(|s| s.to_owned()).collect()), "untrusted" => ret.untrusted = Some(parts[1] == "yes"), other => bail!("unknown option: {}", other), }, @@ -177,7 +177,7 @@ deb-src http://foo bar baz quux assert_eq!( vec![Entry { src: false, - arch: Some("amd64".to_string()), + arch: Some(vec!["amd64".to_string()]), url: "http://foo/".to_string(), suite_codename: "bar".to_string(), components: vec!["baz".to_string(), "quux".to_string()], @@ -237,7 +237,7 @@ deb [untrusted=yeppers] http://foo bar baz quux assert_eq!( vec![Entry { src: false, - arch: Some("amd64".to_string()), + arch: Some(vec!["amd64".to_string()]), url: "http://foo/".to_string(), suite_codename: "bar".to_string(), components: vec!["baz".to_string(), "quux".to_string()], @@ -246,6 +246,26 @@ deb [untrusted=yeppers] http://foo bar baz quux read(io::Cursor::new( r" deb [untrusted=yes arch=amd64] http://foo bar baz quux +", + )) + .unwrap() + ); + } + + #[test] + fn multiple_arches() { + assert_eq!( + vec![Entry { + src: false, + arch: Some(vec!["amd64".to_string(), "i386".to_string()]), + url: "http://foo/".to_string(), + suite_codename: "bar".to_string(), + components: vec!["baz".to_string(), "quux".to_string()], + untrusted: false, + },], + read(io::Cursor::new( + r" +deb [arch=amd64,i386] http://foo bar baz quux ", )) .unwrap()