cargo/sources/registry/
download.rs1use crate::util::interning::InternedString;
7use anyhow::Context as _;
8use cargo_credential::Operation;
9use cargo_util::registry::make_dep_path;
10use cargo_util::Sha256;
11
12use crate::core::global_cache_tracker;
13use crate::core::PackageId;
14use crate::sources::registry::MaybeLock;
15use crate::sources::registry::RegistryConfig;
16use crate::util::auth;
17use crate::util::cache_lock::CacheLockMode;
18use crate::util::errors::CargoResult;
19use crate::util::{Filesystem, GlobalContext};
20use std::fmt::Write as FmtWrite;
21use std::fs::{self, File, OpenOptions};
22use std::io::prelude::*;
23use std::io::SeekFrom;
24use std::str;
25
26const CRATE_TEMPLATE: &str = "{crate}";
27const VERSION_TEMPLATE: &str = "{version}";
28const PREFIX_TEMPLATE: &str = "{prefix}";
29const LOWER_PREFIX_TEMPLATE: &str = "{lowerprefix}";
30const CHECKSUM_TEMPLATE: &str = "{sha256-checksum}";
31
32pub(super) fn download(
37 cache_path: &Filesystem,
38 gctx: &GlobalContext,
39 encoded_registry_name: InternedString,
40 pkg: PackageId,
41 checksum: &str,
42 registry_config: RegistryConfig,
43) -> CargoResult<MaybeLock> {
44 let path = cache_path.join(&pkg.tarball_name());
45 let path = gctx.assert_package_cache_locked(CacheLockMode::DownloadExclusive, &path);
46
47 if let Ok(dst) = File::open(path) {
54 let meta = dst.metadata()?;
55 if meta.len() > 0 {
56 gctx.deferred_global_last_use()?.mark_registry_crate_used(
57 global_cache_tracker::RegistryCrate {
58 encoded_registry_name,
59 crate_filename: pkg.tarball_name().into(),
60 size: meta.len(),
61 },
62 );
63 return Ok(MaybeLock::Ready(dst));
64 }
65 }
66
67 let mut url = registry_config.dl;
68 if !url.contains(CRATE_TEMPLATE)
69 && !url.contains(VERSION_TEMPLATE)
70 && !url.contains(PREFIX_TEMPLATE)
71 && !url.contains(LOWER_PREFIX_TEMPLATE)
72 && !url.contains(CHECKSUM_TEMPLATE)
73 {
74 write!(url, "/{}/{}/download", pkg.name(), pkg.version()).unwrap();
76 } else {
77 let prefix = make_dep_path(&pkg.name(), true);
78 url = url
79 .replace(CRATE_TEMPLATE, &*pkg.name())
80 .replace(VERSION_TEMPLATE, &pkg.version().to_string())
81 .replace(PREFIX_TEMPLATE, &prefix)
82 .replace(LOWER_PREFIX_TEMPLATE, &prefix.to_lowercase())
83 .replace(CHECKSUM_TEMPLATE, checksum);
84 }
85
86 let authorization = if registry_config.auth_required {
87 Some(auth::auth_token(
88 gctx,
89 &pkg.source_id(),
90 None,
91 Operation::Read,
92 vec![],
93 true,
94 )?)
95 } else {
96 None
97 };
98
99 Ok(MaybeLock::Download {
100 url,
101 descriptor: pkg.to_string(),
102 authorization: authorization,
103 })
104}
105
106pub(super) fn finish_download(
111 cache_path: &Filesystem,
112 gctx: &GlobalContext,
113 encoded_registry_name: InternedString,
114 pkg: PackageId,
115 checksum: &str,
116 data: &[u8],
117) -> CargoResult<File> {
118 let actual = Sha256::new().update(data).finish_hex();
120 if actual != checksum {
121 anyhow::bail!("failed to verify the checksum of `{}`", pkg)
122 }
123 gctx.deferred_global_last_use()?.mark_registry_crate_used(
124 global_cache_tracker::RegistryCrate {
125 encoded_registry_name,
126 crate_filename: pkg.tarball_name().into(),
127 size: data.len() as u64,
128 },
129 );
130
131 cache_path.create_dir()?;
132 let path = cache_path.join(&pkg.tarball_name());
133 let path = gctx.assert_package_cache_locked(CacheLockMode::DownloadExclusive, &path);
134 let mut dst = OpenOptions::new()
135 .create(true)
136 .read(true)
137 .write(true)
138 .open(&path)
139 .with_context(|| format!("failed to open `{}`", path.display()))?;
140 let meta = dst.metadata()?;
141 if meta.len() > 0 {
142 return Ok(dst);
143 }
144
145 dst.write_all(data)?;
146 dst.seek(SeekFrom::Start(0))?;
147 Ok(dst)
148}
149
150pub(super) fn is_crate_downloaded(
155 cache_path: &Filesystem,
156 gctx: &GlobalContext,
157 pkg: PackageId,
158) -> bool {
159 let path = cache_path.join(pkg.tarball_name());
160 let path = gctx.assert_package_cache_locked(CacheLockMode::DownloadExclusive, &path);
161 if let Ok(meta) = fs::metadata(path) {
162 return meta.len() > 0;
163 }
164 false
165}