]> git.proxmox.com Git - proxmox-apt.git/blame - src/repositories/mod.rs
check repos: have caller specify the current suite
[proxmox-apt.git] / src / repositories / mod.rs
CommitLineData
b6be0f39
FE
1use std::collections::BTreeMap;
2use std::path::PathBuf;
3
4use anyhow::{bail, Error};
5
6mod repository;
7pub use repository::{
8 APTRepository, APTRepositoryFileType, APTRepositoryOption, APTRepositoryPackageType,
9};
10
11mod file;
76d3a5ba
FE
12pub use file::{APTRepositoryFile, APTRepositoryFileError, APTRepositoryInfo};
13
14mod release;
f48c12b0 15pub use release::get_current_release_codename;
8ada1785
FE
16
17mod standard;
18pub use standard::{APTRepositoryHandle, APTStandardRepository};
b6be0f39
FE
19
20const APT_SOURCES_LIST_FILENAME: &str = "/etc/apt/sources.list";
21const APT_SOURCES_LIST_DIRECTORY: &str = "/etc/apt/sources.list.d/";
22
23/// Calculates a common digest for successfully parsed repository files.
24///
25/// The digest is invariant with respect to file order.
26///
27/// Files without a digest are ignored.
28fn common_digest(files: &[APTRepositoryFile]) -> [u8; 32] {
29 let mut digests = BTreeMap::new();
30
31 for file in files.iter() {
32 digests.insert(file.path.clone(), &file.digest);
33 }
34
35 let mut common_raw = Vec::<u8>::with_capacity(digests.len() * 32);
36 for digest in digests.values() {
37 match digest {
38 Some(digest) => common_raw.extend_from_slice(&digest[..]),
39 None => (),
40 }
41 }
42
43 openssl::sha::sha256(&common_raw[..])
44}
45
76d3a5ba
FE
46/// Provides additional information about the repositories.
47///
48/// The kind of information can be:
49/// `warnings` for bad suites.
50/// `ignore-pre-upgrade-warning` when the next stable suite is configured.
51/// `badge` for official URIs.
13cdf8d1
FE
52pub fn check_repositories(
53 files: &[APTRepositoryFile],
54 current_suite: &str,
55) -> Result<Vec<APTRepositoryInfo>, Error> {
76d3a5ba
FE
56 let mut infos = vec![];
57
58 for file in files.iter() {
13cdf8d1 59 infos.append(&mut file.check_suites(current_suite)?);
76d3a5ba
FE
60 infos.append(&mut file.check_uris());
61 }
62
63 Ok(infos)
64}
65
f48c12b0 66/// Get the repository associated to the handle and the path where it is usually configured.
8ada1785
FE
67pub fn get_standard_repository(
68 handle: APTRepositoryHandle,
69 product: &str,
f48c12b0
FE
70 suite: &str,
71) -> (APTRepository, String) {
8ada1785
FE
72 let repo = handle.to_repository(product, &suite);
73 let path = handle.path(product);
74
f48c12b0 75 (repo, path)
8ada1785
FE
76}
77
f48c12b0
FE
78/// Return handles for standard Proxmox repositories and their status, where
79/// `None` means not configured, and `Some(bool)` indicates enabled or disabled.
8ada1785 80pub fn standard_repositories(
8ada1785 81 files: &[APTRepositoryFile],
f48c12b0
FE
82 product: &str,
83 suite: &str,
8ada1785
FE
84) -> Vec<APTStandardRepository> {
85 let mut result = vec![
3c4e441d
FE
86 APTStandardRepository::from(APTRepositoryHandle::Enterprise),
87 APTStandardRepository::from(APTRepositoryHandle::NoSubscription),
88 APTStandardRepository::from(APTRepositoryHandle::Test),
8ada1785
FE
89 ];
90
91 if product == "pve" {
92 result.append(&mut vec![
3c4e441d
FE
93 APTStandardRepository::from(APTRepositoryHandle::CephPacific),
94 APTStandardRepository::from(APTRepositoryHandle::CephPacificTest),
95 APTStandardRepository::from(APTRepositoryHandle::CephOctopus),
96 APTStandardRepository::from(APTRepositoryHandle::CephOctopusTest),
8ada1785
FE
97 ]);
98 }
99
100 for file in files.iter() {
101 for repo in file.repositories.iter() {
102 for entry in result.iter_mut() {
103 if entry.status == Some(true) {
104 continue;
105 }
106
f48c12b0 107 if repo.is_referenced_repository(entry.handle, product, suite) {
8ada1785
FE
108 entry.status = Some(repo.enabled);
109 }
110 }
111 }
112 }
113
114 result
115}
116
b6be0f39
FE
117/// Returns all APT repositories configured in `/etc/apt/sources.list` and
118/// in `/etc/apt/sources.list.d` including disabled repositories.
119///
120/// Returns the succesfully parsed files, a list of errors for files that could
121/// not be read or parsed and a common digest for the succesfully parsed files.
122///
123/// The digest is guaranteed to be set for each successfully parsed file.
124pub fn repositories() -> Result<
125 (
126 Vec<APTRepositoryFile>,
127 Vec<APTRepositoryFileError>,
128 [u8; 32],
129 ),
130 Error,
131> {
132 let to_result = |files: Vec<APTRepositoryFile>, errors: Vec<APTRepositoryFileError>| {
133 let common_digest = common_digest(&files);
134
135 (files, errors, common_digest)
136 };
137
138 let mut files = vec![];
139 let mut errors = vec![];
140
141 let sources_list_path = PathBuf::from(APT_SOURCES_LIST_FILENAME);
142
143 let sources_list_d_path = PathBuf::from(APT_SOURCES_LIST_DIRECTORY);
144
145 match APTRepositoryFile::new(sources_list_path) {
146 Ok(Some(mut file)) => match file.parse() {
147 Ok(()) => files.push(file),
148 Err(err) => errors.push(err),
149 },
150 _ => bail!("internal error with '{}'", APT_SOURCES_LIST_FILENAME),
151 }
152
153 if !sources_list_d_path.exists() {
154 return Ok(to_result(files, errors));
155 }
156
157 if !sources_list_d_path.is_dir() {
158 errors.push(APTRepositoryFileError {
159 path: APT_SOURCES_LIST_DIRECTORY.to_string(),
160 error: "not a directory!".to_string(),
161 });
162 return Ok(to_result(files, errors));
163 }
164
165 for entry in std::fs::read_dir(sources_list_d_path)? {
166 let path = entry?.path();
167
168 match APTRepositoryFile::new(path) {
169 Ok(Some(mut file)) => match file.parse() {
170 Ok(()) => {
171 if file.digest.is_none() {
172 bail!("internal error - digest not set");
173 }
174 files.push(file);
175 }
176 Err(err) => errors.push(err),
177 },
178 Ok(None) => (),
179 Err(err) => errors.push(err),
180 }
181 }
182
183 Ok(to_result(files, errors))
184}