Skip to content

Commit 90ecd4a

Browse files
committed
use libalpm-reimplementation
1 parent 60fbd27 commit 90ecd4a

File tree

3 files changed

+49
-105
lines changed

3 files changed

+49
-105
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ debug=1
1616
ddelta = { git = "https://github.com/djugei/ddelta-rs" }
1717
indicatif = { git = "https://github.com/djugei/indicatif", branch = "heuristic" }
1818
hashbrown = { git = "https://github.com/djugei/hashbrown" }
19+
libalpm-rs = { git = "https://github.com/djugei/libalpm-rs" }

client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ dialoguer = "*"
2929
command-rusage = "1.0.1"
3030
ruma-headers = {path = "../ruma-headers"}
3131
flate2 = { version = "*" }
32+
libalpm-rs = "*"
3233

3334
[[bin]]
3435
name = "deltaclient"

client/src/util.rs

Lines changed: 47 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
use std::{
2-
collections::HashMap,
3-
io::{BufRead, Read},
4-
path::PathBuf,
5-
process::Command,
6-
};
1+
use std::{collections::HashMap, io::Read, path::PathBuf};
72

83
use anyhow::{Context, bail};
94
use bytesize::ByteSize;
@@ -21,7 +16,6 @@ use tokio::{
2116
};
2217

2318
type Str = Box<str>;
24-
type PackageVersions = HashMap<Str, Vec<(Str, Str, Str, PathBuf)>>;
2519

2620
static PACMAN_CACHE: &str = "/var/cache/pacman/pkg/";
2721

@@ -70,49 +64,32 @@ pub(crate) async fn do_boring_download(
7064
}
7165

7266
pub(crate) fn find_deltaupgrade_candidates(
73-
global: &crate::GlobalState,
67+
_global: &crate::GlobalState,
7468
blacklist: &[Str],
75-
fuz: bool,
69+
_fuz: bool,
7670
) -> Result<(Vec<(String, Package, Package, Mmap, u64)>, Vec<Url>), anyhow::Error> {
77-
let upgrades = Command::new("pacman").args(["-Sup"]).output()?.stdout;
78-
let packageversions = build_package_versions().expect("io error on local disk");
79-
let (mut delta_upgrades, downloads): (Vec<_>, Vec<_>) = upgrades
80-
.lines()
81-
.map(|l| l.expect("pacman abborted output???"))
82-
.filter(|l| !l.starts_with("file"))
83-
.map(|line| {
84-
let (_, filename) = line.rsplit_once('/').unwrap();
85-
let pkg = Package::try_from(filename).unwrap();
86-
let name = pkg.get_name();
87-
if (&blacklist).into_iter().any(|e| **e == *name) {
88-
info!("{name} is blacklisted, skipping");
89-
return Err(line);
90-
}
91-
if let Some((oldpkg, oldpath)) = newest_cached(&packageversions, &pkg.get_name()).or_else(|| {
92-
if fuz {
93-
let (alternative, _) = packageversions
94-
.keys()
95-
.map(|name| (name, strsim::levenshtein(name, pkg.get_name())))
96-
.filter(|(_name, sim)| sim <= &2)
97-
.min()?;
98-
let prompt = format!(
99-
"could not find cached package for {name}, {alternative} has a similar name, use that instead?"
100-
);
101-
global.multi.suspend(|| {
102-
if dialoguer::Confirm::new().with_prompt(prompt).interact().unwrap() {
103-
newest_cached(&packageversions, alternative)
104-
} else {
105-
None
106-
}
107-
})
108-
} else {
109-
None
110-
}
111-
}) {
112-
// Try to find the decompressed size for better progress monitoring
113-
let oldfile = std::fs::File::open(oldpath).expect("io error on local disk");
71+
let upgrades = libalpm_rs::upgrade_urls(&["core", "extra", "multilib"]);
72+
73+
let mut direct_downloads = Vec::new();
74+
let mut deltas = Vec::new();
75+
for (url, old, new) in upgrades {
76+
assert_eq!(old.i, new.i);
77+
use libalpm_rs::db::QuickResolve;
78+
// Download already done
79+
if url.starts_with("file:/") {
80+
continue;
81+
}
82+
let i = old.i.borrow();
83+
let old_name = old.name.r(&i);
84+
if (&blacklist).into_iter().any(|e| **e == *old_name) {
85+
info!("{old_name} is blacklisted, skipping");
86+
}
87+
let old_version = old.version.r(&i);
88+
let cached = std::fs::File::open(format!("{PACMAN_CACHE}/{old_name}-{old_version}.pkg.tar.zstd"));
89+
match cached {
90+
Ok(f) => {
11491
// Safety: I promise to not open the same file as writable at the same time
115-
let oldfile = unsafe { Mmap::map(&oldfile).expect("mmap failed") };
92+
let oldfile = unsafe { Mmap::map(&f).expect("mmap failed") };
11693
// Testing reveals the average size to be 17.2 MB
11794
let default_size = 17 * 1024 * 1024;
11895
// Due to pacman packages being compressed in streaming mode
@@ -121,19 +98,32 @@ pub(crate) fn find_deltaupgrade_candidates(
12198
// The real ratio is 18/47.4, 4/11 is close enough
12299
.map(|s| (s as u64) * 4 / 11)
123100
.unwrap_or_else(|| {
124-
debug!("using default size for {name}");
101+
let new_name = new.name.r(&i);
102+
debug!("using default size for {new_name}");
125103
default_size
126104
});
127-
Ok((line, pkg, oldpkg, oldfile, (dec_size as u64)))
128-
} else {
129-
info!("no cached package found, leaving {} for pacman", filename);
130-
return Err(line);
105+
let oldpkg = Package::from_parts((
106+
old_name.into(),
107+
old_version.into(),
108+
old.arch.as_str().into(),
109+
"pkg.tar.zstd".into(),
110+
));
111+
let new_filename = new.filename.unwrap().r(&i);
112+
let newpkg = Package::try_from(new_filename)?;
113+
114+
deltas.push((url, oldpkg, newpkg, oldfile, (dec_size as u64)));
131115
}
132-
})
133-
.partition_result();
134-
delta_upgrades.sort_unstable_by_key(|(_, _, _, _, size)| std::cmp::Reverse(*size));
135-
let downloads: Result<Vec<Url>, _> = downloads.into_iter().map(|l| Url::parse(&l)).collect();
136-
Ok((delta_upgrades, downloads?))
116+
Err(e) => {
117+
if e.kind() == std::io::ErrorKind::NotFound {
118+
//TODO: re-add fuzzy logic
119+
direct_downloads.push(Url::parse(&url)?);
120+
} else {
121+
return Err(e)?;
122+
}
123+
}
124+
}
125+
}
126+
return Ok((deltas, direct_downloads));
137127
}
138128

139129
pub async fn sync_db(global: crate::GlobalState, server: Url, name: Str) -> anyhow::Result<()> {
@@ -235,54 +225,6 @@ pub async fn sync_db(global: crate::GlobalState, server: Url, name: Str) -> anyh
235225
Ok(())
236226
}
237227

238-
/// {package -> [(version, arch, trailer, path)]}
239-
fn build_package_versions() -> std::io::Result<PackageVersions> {
240-
let mut package_versions: PackageVersions = HashMap::new();
241-
for line in std::fs::read_dir(PACMAN_CACHE)? {
242-
let line = line?;
243-
if !line.file_type()?.is_file() {
244-
continue;
245-
}
246-
let filename = line.file_name();
247-
let filename = filename.to_string_lossy();
248-
if !filename.ends_with(".zst") {
249-
continue;
250-
}
251-
let path = line.path().into();
252-
let package = Package::try_from(&*filename).expect("non-pkg zstd file in pacman cache dir?");
253-
let (name, version, arch, trailer) = package.destructure();
254-
255-
match package_versions.entry(name) {
256-
std::collections::hash_map::Entry::Occupied(mut e) => {
257-
e.get_mut().push((version, arch, trailer, path));
258-
}
259-
std::collections::hash_map::Entry::Vacant(e) => {
260-
e.insert(vec![(version, arch, trailer, path)]);
261-
}
262-
}
263-
}
264-
package_versions.shrink_to_fit();
265-
266-
for e in package_versions.values_mut() {
267-
e.sort()
268-
}
269-
270-
Ok(package_versions)
271-
}
272-
273-
/// returns the newest package in /var/cache/pacman/pkg that
274-
/// fits the passed package
275-
fn newest_cached(pv: &PackageVersions, package_name: &str) -> Option<(Package, PathBuf)> {
276-
pv.get(package_name)
277-
.map(|e| e.last().expect("each entry has at least one version"))
278-
.map(|(version, arch, trailer, path)| {
279-
(
280-
Package::from_parts((package_name.into(), version.clone(), arch.clone(), trailer.clone())),
281-
path.clone(),
282-
)
283-
})
284-
}
285-
286228
/// Calculates and prints some stats about bandwidth savings
287229
pub(crate) fn calc_stats(count: usize) -> std::io::Result<()> {
288230
let mut deltas: HashMap<(Str, Str, Str), u64> = HashMap::new();

0 commit comments

Comments
 (0)