Skip to content

Commit 67ef63e

Browse files
committed
Cleanup unused chunks
1 parent 0ae904c commit 67ef63e

File tree

4 files changed

+123
-9
lines changed

4 files changed

+123
-9
lines changed

src/chunks/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod hash;
22
mod tree;
3+
pub mod utils;
34

45
use std::path::PathBuf;
56

@@ -20,3 +21,11 @@ pub struct Chunk {
2021
/// Expected size in kilobytes, rounded.
2122
size: u64,
2223
}
24+
25+
fn get_chunk_filename(hash: &str, permissions: u32) -> String {
26+
let mut new_hash = hash.to_string();
27+
28+
new_hash.push_str(&permissions.to_string());
29+
30+
new_hash
31+
}

src/chunks/tree.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::{
66
};
77
use walkdir::WalkDir;
88

9-
use crate::chunks::{Chunk, HashKind, hash::hash};
9+
use crate::chunks::{Chunk, HashKind, get_chunk_filename, hash::hash};
1010

1111
/// Turns a filesystem tree into a list of chunks
1212
///
@@ -117,14 +117,6 @@ pub fn estimate_tree_size(chunks: &[Chunk]) -> u64 {
117117
size
118118
}
119119

120-
fn get_chunk_filename(hash: &str, permissions: u32) -> String {
121-
let mut new_hash = hash.to_string();
122-
123-
new_hash.push_str(&permissions.to_string());
124-
125-
new_hash
126-
}
127-
128120
#[cfg(test)]
129121
mod tests {
130122
use std::os::unix::fs::MetadataExt;

src/chunks/utils.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use std::{collections::HashSet, fs, path::Path};
2+
3+
use anyhow::Result;
4+
5+
use crate::{
6+
chunks::{Chunk, get_chunk_filename},
7+
repo::{get_all_installed_packages, get_all_packages},
8+
};
9+
10+
/// Removes chunks that aren't actually used by any packages in the Repository
11+
/// This is most useful for remote Repository administrators.
12+
///
13+
/// # Errors
14+
///
15+
/// - Filesystem errors (Permissions most likely)
16+
/// - Repository doesn't exist
17+
pub fn clean_unused(repo_path: &Path) -> Result<()> {
18+
let packages = get_all_packages(repo_path)?;
19+
let mut chunks: Vec<Chunk> = Vec::new();
20+
21+
for package in packages {
22+
for chunk in package.chunks.clone() {
23+
chunks.push(chunk);
24+
}
25+
}
26+
27+
clean(&repo_path.join("chunks"), &chunks)
28+
}
29+
30+
/// Removes chunks that are actually used by any packages in the Repository, but aren't installed
31+
/// This is most useful for end users.
32+
///
33+
/// # Errors
34+
///
35+
/// - Filesystem errors (Permissions most likely)
36+
/// - Repository doesn't exist
37+
pub fn clean_used(repo_path: &Path) -> Result<()> {
38+
let packages = get_all_installed_packages(repo_path)?;
39+
let mut chunks: Vec<Chunk> = Vec::new();
40+
41+
for package in packages {
42+
for chunk in package.chunks.clone() {
43+
chunks.push(chunk);
44+
}
45+
}
46+
47+
clean(&repo_path.join("chunks"), &chunks)
48+
}
49+
50+
/// Cleans a `chunk_store` of unused chunks, using the whitelist `allowed_chunks`
51+
fn clean(chunk_store_path: &Path, allowed_chunks: &[Chunk]) -> Result<()> {
52+
let allowed: HashSet<String> = allowed_chunks
53+
.iter()
54+
.map(|c| get_chunk_filename(&c.hash, c.permissions))
55+
.collect();
56+
57+
for entry in fs::read_dir(chunk_store_path)? {
58+
let entry = entry?;
59+
let file_name = entry.file_name();
60+
let Some(file_name_str) = file_name.to_str() else {
61+
continue;
62+
};
63+
64+
if allowed.contains(file_name_str) {
65+
fs::remove_file(entry.path())?;
66+
}
67+
}
68+
69+
Ok(())
70+
}

src/repo/mod.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,49 @@ pub fn get_package(repo_path: &Path, id: &str) -> Result<PackageManifest> {
129129
bail!("No package found in Repository.");
130130
}
131131

132+
/// Lists all packages from a repository.
133+
///
134+
/// # Errors
135+
///
136+
/// - Filesystem errors (Permissions most likely)
137+
/// - Repository doesn't exist
138+
pub fn get_all_packages(repo_path: &Path) -> Result<Vec<PackageManifest>> {
139+
let repo_manifest = read_manifest(repo_path)?;
140+
let mut packages = Vec::new();
141+
142+
// Check ID's and aliases
143+
for package in repo_manifest.packages {
144+
packages.push(package);
145+
}
146+
147+
Ok(packages)
148+
}
149+
150+
/// Lists all installed packages from a repository.
151+
///
152+
/// # Errors
153+
///
154+
/// - Filesystem errors (Permissions most likely)
155+
/// - Repository doesn't exist
156+
pub fn get_all_installed_packages(repo_path: &Path) -> Result<Vec<PackageManifest>> {
157+
let mut packages = Vec::new();
158+
let installed_path = &repo_path.join("installed");
159+
160+
if !installed_path.exists() {
161+
Ok(Vec::new())
162+
} else {
163+
// Check ID's and aliases
164+
for entry in fs::read_dir(installed_path)? {
165+
let file = entry?.path();
166+
let package = serde_yaml::from_str(&fs::read_to_string(file)?)?;
167+
168+
packages.push(package);
169+
}
170+
171+
Ok(packages)
172+
}
173+
}
174+
132175
#[cfg(test)]
133176
mod tests {
134177
use temp_dir::TempDir;

0 commit comments

Comments
 (0)