1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#![forbid(unsafe_code)]
use hashes::get_expected_driver_hash;
use http_req::request;
use paths::get_cache_dir;
use pico_common::Driver;
use pico_driver::Resolution;
use ring::digest::{Context, Digest, SHA256};
use std::{fs, io::Read, path::Path};
use thiserror::Error;
mod hashes;
mod paths;
#[derive(Error, Debug)]
pub enum DriverDownloadError {
#[error("IO Error: {0}")]
IOError(#[from] std::io::Error),
#[error("HTTP request Error: {0}")]
HTTPRequestError(#[from] http_req::error::Error),
#[error("HTTP response Error: {0}")]
HTTPResponseError(http_req::response::StatusCode),
#[error("Invalid driver hash")]
HashMismatch,
}
pub fn cache_resolution() -> Resolution {
Resolution::Custom(get_cache_dir())
}
pub fn download_drivers<P: AsRef<Path>>(
drivers: &[Driver],
path: P,
) -> Result<(), DriverDownloadError> {
let driver_dir = path.as_ref().to_path_buf();
let required_files = [drivers, &Driver::get_dependencies_for_platform()].concat();
fs::create_dir_all(&driver_dir)?;
for driver in required_files {
let file_path = driver_dir.join(&driver.get_binary_name());
if file_path.exists() {
match driver {
Driver::PicoIPP | Driver::IOMP5 => {
continue;
}
_ => {
fs::remove_file(&file_path)?;
}
}
}
download_driver(driver, &driver_dir)?;
}
Ok(())
}
pub fn download_drivers_to_cache(drivers: &[Driver]) -> Result<(), DriverDownloadError> {
download_drivers(drivers, get_cache_dir())
}
fn sha256_digest_for_file<P: AsRef<Path>>(path: P) -> Result<Digest, DriverDownloadError> {
let mut src_file = fs::File::open(&path)?;
let mut context = Context::new(&SHA256);
let mut buffer = [0; 1024];
loop {
let count = src_file.read(&mut buffer)?;
if count == 0 {
break;
}
context.update(&buffer[..count]);
}
Ok(context.finish())
}
fn download_driver(driver: Driver, dst_dir: &Path) -> Result<(), DriverDownloadError> {
let name = driver.get_binary_name();
let url = format!(
"https://pico-drivers.s3.eu-west-2.amazonaws.com/{}/{}/{}",
std::env::consts::OS,
std::env::consts::ARCH,
name
);
let dst_temp_path = dst_dir.join(name.to_string() + ".temp");
let mut dst_file = fs::File::create(&dst_temp_path)?;
let response = request::get(url, &mut dst_file)?;
if response.status_code().is_success() {
let computed_hash = format!("{:?}", sha256_digest_for_file(&dst_temp_path)?);
let expected_hash = get_expected_driver_hash(driver);
if computed_hash == expected_hash {
let dst_path = dst_dir.join(name);
fs::copy(&dst_temp_path, dst_path)?;
fs::remove_file(dst_temp_path)?;
Ok(())
} else {
Err(DriverDownloadError::HashMismatch)
}
} else {
Err(DriverDownloadError::HTTPResponseError(
response.status_code(),
))
}
}