diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index ee09c72537..04afb6ecf3 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -1127,7 +1127,7 @@ pub(crate) fn self_update_permitted(explicit: bool) -> Result) -> Result { +pub(crate) async fn self_update(dl_cfg: &DownloadCfg) -> Result { match self_update_permitted(false)? { SelfUpdatePermission::HardFail => { error!("Unable to self-update. STOP"); @@ -1144,7 +1144,7 @@ pub(crate) async fn self_update(dl_cfg: &DownloadCfg<'_>) -> Result String { String::from(matched_version) } -pub(crate) async fn prepare_update(dl_cfg: &DownloadCfg<'_>) -> Result> { +pub(crate) async fn prepare_update(dl_cfg: &DownloadCfg) -> Result> { let cargo_home = dl_cfg.process.cargo_home()?; let rustup_path = cargo_home.join(format!("bin{MAIN_SEPARATOR}rustup{EXE_SUFFIX}")); let setup_path = cargo_home.join(format!("bin{MAIN_SEPARATOR}rustup-init{EXE_SUFFIX}")); @@ -1265,10 +1265,10 @@ pub(crate) async fn prepare_update(dl_cfg: &DownloadCfg<'_>) -> Result) -> Result) -> Result) -> Result { - let update_root = update_root(dl_cfg.process); +async fn get_available_rustup_version(dl_cfg: &DownloadCfg) -> Result { + let update_root = update_root(&dl_cfg.process); let tempdir = tempfile::Builder::new() .prefix("rustup-update") .tempdir() @@ -1315,7 +1315,14 @@ async fn get_available_rustup_version(dl_cfg: &DownloadCfg<'_>) -> Result(&release_toml_str) .context("unable to parse rustup release file")?; @@ -1363,7 +1370,7 @@ impl fmt::Display for SchemaVersion { } /// Returns whether an update was available -pub(crate) async fn check_rustup_update(dl_cfg: &DownloadCfg<'_>) -> Result { +pub(crate) async fn check_rustup_update(dl_cfg: &DownloadCfg) -> Result { let t = dl_cfg.process.stdout(); let mut t = t.lock(); // Get current rustup version diff --git a/src/cli/self_update/windows.rs b/src/cli/self_update/windows.rs index 973fd58d85..66849241de 100644 --- a/src/cli/self_update/windows.rs +++ b/src/cli/self_update/windows.rs @@ -278,7 +278,7 @@ pub(crate) async fn try_install_msvc( &visual_studio, None, None, - dl_cfg.process, + &dl_cfg.process, ) .await?; diff --git a/src/config.rs b/src/config.rs index 9cf90d2a38..a26eea0fc5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,6 +2,7 @@ use std::fmt::{self, Debug, Display}; use std::io; use std::path::{Path, PathBuf}; use std::str::FromStr; +use std::sync::Arc; use anyhow::{Context, Result, anyhow, bail}; use serde::Deserialize; @@ -238,7 +239,7 @@ pub(crate) struct Cfg<'a> { pub toolchains_dir: PathBuf, update_hash_dir: PathBuf, pub download_dir: PathBuf, - pub tmp_cx: temp::Context, + pub tmp_cx: Arc, pub toolchain_override: Option, env_override: Option, pub dist_root_url: String, @@ -299,7 +300,10 @@ impl<'a> Cfg<'a> { }; let dist_root_server = dist_root_server(process)?; - let tmp_cx = temp::Context::new(rustup_dir.join("tmp"), dist_root_server.as_str()); + let tmp_cx = Arc::new(temp::Context::new( + rustup_dir.join("tmp"), + dist_root_server.as_str(), + )); let dist_root = dist_root_server + "/dist"; let cfg = Self { diff --git a/src/dist/component/components.rs b/src/dist/component/components.rs index bdc92fefa3..5df3c4a429 100644 --- a/src/dist/component/components.rs +++ b/src/dist/component/components.rs @@ -55,7 +55,7 @@ impl Components { Ok(None) } } - fn write_version(&self, tx: &mut Transaction<'_>) -> Result<()> { + fn write_version(&self, tx: &mut Transaction) -> Result<()> { tx.modify_file(self.prefix.rel_manifest_file(VERSION_FILE))?; utils::write_file( VERSION_FILE, @@ -79,7 +79,7 @@ impl Components { }) .collect()) } - pub(crate) fn add<'a>(&self, name: &str, tx: Transaction<'a>) -> ComponentBuilder<'a> { + pub(crate) fn add(&self, name: &str, tx: Transaction) -> ComponentBuilder { ComponentBuilder { components: self.clone(), name: name.to_owned(), @@ -96,14 +96,14 @@ impl Components { } } -pub(crate) struct ComponentBuilder<'a> { +pub(crate) struct ComponentBuilder { components: Components, name: String, parts: Vec, - tx: Transaction<'a>, + tx: Transaction, } -impl<'a> ComponentBuilder<'a> { +impl ComponentBuilder { pub(crate) fn copy_file(&mut self, path: PathBuf, src: &Path) -> Result<()> { self.parts.push(ComponentPart { kind: ComponentPartKind::File, @@ -132,7 +132,7 @@ impl<'a> ComponentBuilder<'a> { }); self.tx.move_dir(&self.name, path, src) } - pub(crate) fn finish(mut self) -> Result> { + pub(crate) fn finish(mut self) -> Result { // Write component manifest let path = self.components.rel_component_manifest(&self.name); let abs_path = self.components.prefix.abs_path(&path); @@ -255,11 +255,7 @@ impl Component { } Ok(result) } - pub fn uninstall<'a>( - &self, - mut tx: Transaction<'a>, - process: &Process, - ) -> Result> { + pub fn uninstall(&self, mut tx: Transaction, process: &Process) -> Result { // Update components file let path = self.components.rel_components_file(); let abs_path = self.components.prefix.abs_path(&path); diff --git a/src/dist/component/package.rs b/src/dist/component/package.rs index e7284a4eff..9957820a87 100644 --- a/src/dist/component/package.rs +++ b/src/dist/component/package.rs @@ -28,13 +28,13 @@ pub(crate) const VERSION_FILE: &str = "rust-installer-version"; pub trait Package: fmt::Debug { fn contains(&self, component: &str, short_name: Option<&str>) -> bool; - fn install<'a>( + fn install( &self, target: &Components, component: &str, short_name: Option<&str>, - tx: Transaction<'a>, - ) -> Result>; + tx: Transaction, + ) -> Result; fn components(&self) -> Vec; } @@ -78,13 +78,13 @@ impl Package for DirectoryPackage { false } } - fn install<'a>( + fn install( &self, target: &Components, name: &str, short_name: Option<&str>, - tx: Transaction<'a>, - ) -> Result> { + tx: Transaction, + ) -> Result { let actual_name = if self.components.contains(name) { name } else if let Some(n) = short_name { @@ -139,7 +139,7 @@ impl Package for DirectoryPackage { pub(crate) struct TarPackage(DirectoryPackage, temp::Dir); impl TarPackage { - pub(crate) fn new(stream: R, dl_cfg: &DownloadCfg<'_>) -> Result { + pub(crate) fn new(stream: R, dl_cfg: &DownloadCfg) -> Result { let temp_dir = dl_cfg.tmp_cx.new_directory()?; let mut archive = tar::Archive::new(stream); // The rust-installer packages unpack to a directory called @@ -159,7 +159,7 @@ impl TarPackage { fn unpack_ram( io_chunk_size: usize, effective_max_ram: Option, - dl_cfg: &DownloadCfg<'_>, + dl_cfg: &DownloadCfg, ) -> usize { const RAM_ALLOWANCE_FOR_RUSTUP_AND_BUFFERS: usize = 200 * 1024 * 1024; let minimum_ram = io_chunk_size * 2; @@ -285,7 +285,7 @@ enum DirStatus { fn unpack_without_first_dir( archive: &mut tar::Archive, path: &Path, - dl_cfg: &DownloadCfg<'_>, + dl_cfg: &DownloadCfg, ) -> Result<()> { let entries = archive.entries()?; let effective_max_ram = match effective_limits::memory_limit() { @@ -296,7 +296,7 @@ fn unpack_without_first_dir( } }; let unpack_ram = unpack_ram(IO_CHUNK_SIZE, effective_max_ram, dl_cfg); - let mut io_executor: Box = get_executor(unpack_ram, dl_cfg.process)?; + let mut io_executor: Box = get_executor(unpack_ram, &dl_cfg.process)?; let mut directories: HashMap = HashMap::new(); // Path is presumed to exist. Call it a precondition. @@ -530,13 +530,13 @@ impl Package for TarPackage { fn contains(&self, component: &str, short_name: Option<&str>) -> bool { self.0.contains(component, short_name) } - fn install<'b>( + fn install( &self, target: &Components, component: &str, short_name: Option<&str>, - tx: Transaction<'b>, - ) -> Result> { + tx: Transaction, + ) -> Result { self.0.install(target, component, short_name, tx) } fn components(&self) -> Vec { @@ -548,7 +548,7 @@ impl Package for TarPackage { pub(crate) struct TarGzPackage(TarPackage); impl TarGzPackage { - pub(crate) fn new(stream: R, dl_cfg: &DownloadCfg<'_>) -> Result { + pub(crate) fn new(stream: R, dl_cfg: &DownloadCfg) -> Result { let stream = flate2::read::GzDecoder::new(stream); Ok(TarGzPackage(TarPackage::new(stream, dl_cfg)?)) } @@ -558,13 +558,13 @@ impl Package for TarGzPackage { fn contains(&self, component: &str, short_name: Option<&str>) -> bool { self.0.contains(component, short_name) } - fn install<'b>( + fn install( &self, target: &Components, component: &str, short_name: Option<&str>, - tx: Transaction<'b>, - ) -> Result> { + tx: Transaction, + ) -> Result { self.0.install(target, component, short_name, tx) } fn components(&self) -> Vec { @@ -576,7 +576,7 @@ impl Package for TarGzPackage { pub(crate) struct TarXzPackage(TarPackage); impl TarXzPackage { - pub(crate) fn new(stream: R, dl_cfg: &DownloadCfg<'_>) -> Result { + pub(crate) fn new(stream: R, dl_cfg: &DownloadCfg) -> Result { let stream = xz2::read::XzDecoder::new(stream); Ok(TarXzPackage(TarPackage::new(stream, dl_cfg)?)) } @@ -586,13 +586,13 @@ impl Package for TarXzPackage { fn contains(&self, component: &str, short_name: Option<&str>) -> bool { self.0.contains(component, short_name) } - fn install<'b>( + fn install( &self, target: &Components, component: &str, short_name: Option<&str>, - tx: Transaction<'b>, - ) -> Result> { + tx: Transaction, + ) -> Result { self.0.install(target, component, short_name, tx) } fn components(&self) -> Vec { @@ -604,7 +604,7 @@ impl Package for TarXzPackage { pub(crate) struct TarZStdPackage(TarPackage); impl TarZStdPackage { - pub(crate) fn new(stream: R, dl_cfg: &DownloadCfg<'_>) -> Result { + pub(crate) fn new(stream: R, dl_cfg: &DownloadCfg) -> Result { let stream = zstd::stream::read::Decoder::new(stream)?; Ok(TarZStdPackage(TarPackage::new(stream, dl_cfg)?)) } @@ -614,13 +614,13 @@ impl Package for TarZStdPackage { fn contains(&self, component: &str, short_name: Option<&str>) -> bool { self.0.contains(component, short_name) } - fn install<'b>( + fn install( &self, target: &Components, component: &str, short_name: Option<&str>, - tx: Transaction<'b>, - ) -> Result> { + tx: Transaction, + ) -> Result { self.0.install(target, component, short_name, tx) } fn components(&self) -> Vec { diff --git a/src/dist/component/transaction.rs b/src/dist/component/transaction.rs index b0f237aba3..e3238639dc 100644 --- a/src/dist/component/transaction.rs +++ b/src/dist/component/transaction.rs @@ -9,8 +9,11 @@ //! FIXME: This uses ensure_dir_exists in some places but rollback //! does not remove any dirs created by it. -use std::fs::File; -use std::path::{Path, PathBuf}; +use std::{ + fs::File, + path::{Path, PathBuf}, + sync::Arc, +}; use anyhow::{Context, Result, anyhow}; use tracing::{error, info}; @@ -34,16 +37,16 @@ use crate::utils; /// /// All operations that create files will fail if the destination /// already exists. -pub struct Transaction<'a> { +pub struct Transaction { prefix: InstallPrefix, changes: Vec, - tmp_cx: &'a temp::Context, + tmp_cx: Arc, committed: bool, - process: &'a Process, + process: Arc, } -impl<'a> Transaction<'a> { - pub fn new(prefix: InstallPrefix, tmp_cx: &'a temp::Context, process: &'a Process) -> Self { +impl Transaction { + pub fn new(prefix: InstallPrefix, tmp_cx: Arc, process: Arc) -> Self { Transaction { prefix, changes: Vec::new(), @@ -92,8 +95,13 @@ impl<'a> Transaction<'a> { /// Remove a file from a relative path to the install prefix. pub fn remove_file(&mut self, component: &str, relpath: PathBuf) -> Result<()> { assert!(relpath.is_relative()); - let item = - ChangedItem::remove_file(&self.prefix, component, relpath, self.tmp_cx, self.process)?; + let item = ChangedItem::remove_file( + &self.prefix, + component, + relpath, + self.tmp_cx.clone(), + &self.process, + )?; self.change(item); Ok(()) } @@ -102,8 +110,13 @@ impl<'a> Transaction<'a> { /// install prefix. pub fn remove_dir(&mut self, component: &str, relpath: PathBuf) -> Result<()> { assert!(relpath.is_relative()); - let item = - ChangedItem::remove_dir(&self.prefix, component, relpath, self.tmp_cx, self.process)?; + let item = ChangedItem::remove_dir( + &self.prefix, + component, + relpath, + self.tmp_cx.clone(), + &self.process, + )?; self.change(item); Ok(()) } @@ -129,7 +142,7 @@ impl<'a> Transaction<'a> { /// This is used for arbitrarily manipulating a file. pub fn modify_file(&mut self, relpath: PathBuf) -> Result<()> { assert!(relpath.is_relative()); - let item = ChangedItem::modify_file(&self.prefix, relpath, self.tmp_cx)?; + let item = ChangedItem::modify_file(&self.prefix, relpath, self.tmp_cx.clone())?; self.change(item); Ok(()) } @@ -142,7 +155,7 @@ impl<'a> Transaction<'a> { src: &Path, ) -> Result<()> { assert!(relpath.is_relative()); - let item = ChangedItem::move_file(&self.prefix, component, relpath, src, self.process)?; + let item = ChangedItem::move_file(&self.prefix, component, relpath, src, &self.process)?; self.change(item); Ok(()) } @@ -150,26 +163,26 @@ impl<'a> Transaction<'a> { /// Recursively move a directory to a relative path of the install prefix. pub(crate) fn move_dir(&mut self, component: &str, relpath: PathBuf, src: &Path) -> Result<()> { assert!(relpath.is_relative()); - let item = ChangedItem::move_dir(&self.prefix, component, relpath, src, self.process)?; + let item = ChangedItem::move_dir(&self.prefix, component, relpath, src, &self.process)?; self.change(item); Ok(()) } - pub(crate) fn temp(&self) -> &'a temp::Context { - self.tmp_cx + pub(crate) fn temp(&self) -> Arc { + self.tmp_cx.clone() } } /// If a Transaction is dropped without being committed, the changes /// are automatically rolled back. -impl Drop for Transaction<'_> { +impl Drop for Transaction { fn drop(&mut self) { if !self.committed { info!("rolling back changes"); for item in self.changes.iter().rev() { // ok_ntfy!(self.notify_handler, // Notification::NonFatalError, - match item.roll_back(&self.prefix, self.process) { + match item.roll_back(&self.prefix, &self.process) { Ok(()) => {} Err(e) => error!("{e}"), } @@ -186,9 +199,9 @@ impl Drop for Transaction<'_> { enum ChangedItem { AddedFile(PathBuf), AddedDir(PathBuf), - RemovedFile(PathBuf, temp::File), - RemovedDir(PathBuf, temp::Dir), - ModifiedFile(PathBuf, Option), + RemovedFile(PathBuf, Arc), + RemovedDir(PathBuf, Arc), + ModifiedFile(PathBuf, Option>), } impl ChangedItem { @@ -215,6 +228,7 @@ impl ChangedItem { } Ok(()) } + fn dest_abs_path(prefix: &InstallPrefix, component: &str, relpath: &Path) -> Result { let abs_path = prefix.abs_path(relpath); if utils::path_exists(&abs_path) { @@ -229,12 +243,14 @@ impl ChangedItem { Ok(abs_path) } } + fn add_file(prefix: &InstallPrefix, component: &str, relpath: PathBuf) -> Result<(Self, File)> { let abs_path = ChangedItem::dest_abs_path(prefix, component, &relpath)?; let file = File::create(&abs_path) .with_context(|| format!("error creating file '{}'", abs_path.display()))?; Ok((ChangedItem::AddedFile(relpath), file)) } + fn copy_file( prefix: &InstallPrefix, component: &str, @@ -245,6 +261,7 @@ impl ChangedItem { utils::copy_file(src, &abs_path)?; Ok(ChangedItem::AddedFile(relpath)) } + fn copy_dir( prefix: &InstallPrefix, component: &str, @@ -255,11 +272,12 @@ impl ChangedItem { utils::copy_dir(src, &abs_path)?; Ok(ChangedItem::AddedDir(relpath)) } + fn remove_file( prefix: &InstallPrefix, component: &str, relpath: PathBuf, - tmp_cx: &temp::Context, + tmp_cx: Arc, process: &Process, ) -> Result { let abs_path = prefix.abs_path(&relpath); @@ -272,14 +290,15 @@ impl ChangedItem { .into()) } else { utils::rename("component", &abs_path, &backup, process)?; - Ok(ChangedItem::RemovedFile(relpath, backup)) + Ok(ChangedItem::RemovedFile(relpath, Arc::new(backup))) } } + fn remove_dir( prefix: &InstallPrefix, component: &str, relpath: PathBuf, - tmp_cx: &temp::Context, + tmp_cx: Arc, process: &Process, ) -> Result { let abs_path = prefix.abs_path(&relpath); @@ -292,20 +311,21 @@ impl ChangedItem { .into()) } else { utils::rename("component", &abs_path, &backup.join("bk"), process)?; - Ok(ChangedItem::RemovedDir(relpath, backup)) + Ok(ChangedItem::RemovedDir(relpath, Arc::new(backup))) } } + fn modify_file( prefix: &InstallPrefix, relpath: PathBuf, - tmp_cx: &temp::Context, + tmp_cx: Arc, ) -> Result { let abs_path = prefix.abs_path(&relpath); if utils::is_file(&abs_path) { let backup = tmp_cx.new_file()?; utils::copy_file(&abs_path, &backup)?; - Ok(ChangedItem::ModifiedFile(relpath, Some(backup))) + Ok(ChangedItem::ModifiedFile(relpath, Some(Arc::new(backup)))) } else { if let Some(p) = abs_path.parent() { utils::ensure_dir_exists("component", p)?; @@ -313,6 +333,7 @@ impl ChangedItem { Ok(ChangedItem::ModifiedFile(relpath, None)) } } + fn move_file( prefix: &InstallPrefix, component: &str, @@ -324,6 +345,7 @@ impl ChangedItem { utils::rename("component", src, &abs_path, process)?; Ok(ChangedItem::AddedFile(relpath)) } + fn move_dir( prefix: &InstallPrefix, component: &str, diff --git a/src/dist/download.rs b/src/dist/download.rs index 31e89cef74..f02a380feb 100644 --- a/src/dist/download.rs +++ b/src/dist/download.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::fs; use std::ops; use std::path::{Path, PathBuf}; +use std::sync::Arc; use std::sync::Mutex; use std::time::{Duration, Instant}; @@ -20,21 +21,22 @@ use crate::utils; const UPDATE_HASH_LEN: usize = 20; -pub struct DownloadCfg<'a> { - pub tmp_cx: &'a temp::Context, - pub download_dir: &'a PathBuf, - pub(super) tracker: DownloadTracker, - pub process: &'a Process, +#[derive(Clone)] +pub struct DownloadCfg { + pub tmp_cx: Arc, + pub download_dir: Arc, + pub(super) tracker: Arc, + pub process: Arc, } -impl<'a> DownloadCfg<'a> { +impl DownloadCfg { /// construct a download configuration - pub(crate) fn new(cfg: &'a Cfg<'a>) -> Self { + pub(crate) fn new(cfg: &'_ Cfg<'_>) -> Self { DownloadCfg { - tmp_cx: &cfg.tmp_cx, - download_dir: &cfg.download_dir, - tracker: DownloadTracker::new(!cfg.quiet, cfg.process), - process: cfg.process, + tmp_cx: cfg.tmp_cx.clone(), + download_dir: Arc::new(cfg.download_dir.clone()), + tracker: Arc::new(DownloadTracker::new(!cfg.quiet, cfg.process)), + process: Arc::new(cfg.process.clone()), } } @@ -48,7 +50,7 @@ impl<'a> DownloadCfg<'a> { hash: &str, status: &DownloadStatus, ) -> Result { - utils::ensure_dir_exists("Download Directory", self.download_dir)?; + utils::ensure_dir_exists("Download Directory", &self.download_dir)?; let target_file = self.download_dir.join(Path::new(hash)); if target_file.exists() { @@ -82,7 +84,7 @@ impl<'a> DownloadCfg<'a> { Some(&mut hasher), true, Some(status), - self.process, + &self.process, ) .await { @@ -111,7 +113,12 @@ impl<'a> DownloadCfg<'a> { } } else { debug!(url = url.as_ref(), "checksum passed"); - utils::rename("downloaded", &partial_file_path, &target_file, self.process)?; + utils::rename( + "downloaded", + &partial_file_path, + &target_file, + &self.process, + )?; Ok(File { path: target_file }) } } @@ -128,9 +135,9 @@ impl<'a> DownloadCfg<'a> { async fn download_hash(&self, url: &str) -> Result { let hash_url = utils::parse_url(&(url.to_owned() + ".sha256"))?; - let hash_file = self.tmp_cx.new_file()?; + let hash_file = self.tmp_cx.clone().new_file()?; - download_file(&hash_url, &hash_file, None, None, self.process).await?; + download_file(&hash_url, &hash_file, None, None, &self.process).await?; utils::read_file("hash", &hash_file).map(|s| s[0..64].to_owned()) } @@ -169,10 +176,10 @@ impl<'a> DownloadCfg<'a> { } let url = utils::parse_url(url_str)?; - let file = self.tmp_cx.new_file_with_ext("", ext)?; + let file = self.tmp_cx.clone().new_file_with_ext("", ext)?; let mut hasher = Sha256::new(); - download_file(&url, &file, Some(&mut hasher), status, self.process).await?; + download_file(&url, &file, Some(&mut hasher), status, &self.process).await?; let actual_hash = format!("{:x}", hasher.finalize()); if hash != actual_hash { diff --git a/src/dist/manifestation.rs b/src/dist/manifestation.rs index 02e5db03b1..cb6fb2dd0f 100644 --- a/src/dist/manifestation.rs +++ b/src/dist/manifestation.rs @@ -106,30 +106,31 @@ impl Manifestation { /// It is *not* safe to run two updates concurrently. See /// https://github.com/rust-lang/rustup/issues/988 for the details. pub async fn update( - &self, - new_manifest: &Manifest, + self: Arc, + new_manifest: Arc, changes: Changes, force_update: bool, - download_cfg: &DownloadCfg<'_>, + download_cfg: DownloadCfg, toolchain_str: &str, implicit_modify: bool, ) -> Result { // Some vars we're going to need a few times - let tmp_cx = download_cfg.tmp_cx; + let download_cfg = Arc::new(download_cfg); + let tmp_cx = download_cfg.tmp_cx.clone(); let prefix = self.installation.prefix(); let rel_installed_manifest_path = prefix.rel_manifest_file(DIST_MANIFEST); let installed_manifest_path = prefix.path().join(&rel_installed_manifest_path); // Create the lists of components needed for installation let config = self.read_config()?; - let mut update = Update::build_update(self, new_manifest, &changes, &config)?; + let mut update = Update::build_update(&self, &new_manifest, &changes, &config)?; if update.nothing_changes() { return Ok(UpdateStatus::Unchanged); } // Validate that the requested components are available - if let Err(e) = update.unavailable_components(new_manifest, toolchain_str) { + if let Err(e) = update.unavailable_components(&new_manifest, toolchain_str) { if !force_update { return Err(e); } @@ -140,12 +141,12 @@ impl Manifestation { match &component.target { Some(t) if t != &self.target_triple => warn!( "skipping unavailable component {} for target {}", - component.short_name(new_manifest), + component.short_name(&new_manifest), t ), _ => warn!( "skipping unavailable component {}", - component.short_name(new_manifest) + component.short_name(&new_manifest) ), } } @@ -159,12 +160,12 @@ impl Manifestation { let mut things_to_install = Vec::new(); let mut things_downloaded = Vec::new(); let components = update - .components_urls_and_hashes(new_manifest) + .components_urls_and_hashes(&new_manifest) .map(|res| { res.map(|(component, binary)| ComponentBinary { - component, - binary, - status: download_cfg.status_for(component.short_name(new_manifest)), + component: Arc::new(component.clone()), + binary: Arc::new(binary.clone()), + status: download_cfg.status_for(component.short_name(&new_manifest)), }) }) .collect::>>()?; @@ -188,19 +189,18 @@ impl Manifestation { let semaphore = Arc::new(Semaphore::new(concurrent_downloads)); let component_stream = tokio_stream::iter(components.into_iter()).map(|bin| { let sem = semaphore.clone(); + let new_manifest = new_manifest.clone(); + let dist_server = tmp_cx.dist_server.as_str(); + let download_cfg = download_cfg.clone(); async move { let _permit = sem.acquire().await.unwrap(); let url = if altered { - utils::parse_url( - &bin.binary - .url - .replace(DEFAULT_DIST_SERVER, tmp_cx.dist_server.as_str()), - )? + utils::parse_url(&bin.binary.url.replace(DEFAULT_DIST_SERVER, dist_server))? } else { utils::parse_url(&bin.binary.url)? }; - bin.download(&url, download_cfg, max_retries, new_manifest) + bin.download(&url, &download_cfg, max_retries, &new_manifest) .await .map(|downloaded| (bin, downloaded)) } @@ -218,11 +218,11 @@ impl Manifestation { } // Begin transaction - let mut tx = Transaction::new(prefix.clone(), tmp_cx, download_cfg.process); + let mut tx = Transaction::new(prefix.clone(), tmp_cx.clone(), download_cfg.process.clone()); // If the previous installation was from a v1 manifest we need // to uninstall it first. - tx = self.maybe_handle_v2_upgrade(&config, tx, download_cfg.process)?; + tx = self.maybe_handle_v2_upgrade(&config, tx, &download_cfg.process)?; // Uninstall components for component in &update.components_to_uninstall { @@ -230,38 +230,38 @@ impl Manifestation { (true, Some(t)) if t != &self.target_triple => { info!( "removing previous version of component {} for target {}", - component.short_name(new_manifest), + component.short_name(&new_manifest), t ); } (false, Some(t)) if t != &self.target_triple => { info!( "removing component {} for target {}", - component.short_name(new_manifest), + component.short_name(&new_manifest), t ); } (true, _) => { info!( "removing previous version of component {}", - component.short_name(new_manifest), + component.short_name(&new_manifest), ); } (false, _) => { - info!("removing component {}", component.short_name(new_manifest)); + info!("removing component {}", component.short_name(&new_manifest)); } } - tx = self.uninstall_component(component, new_manifest, tx, download_cfg.process)?; + tx = self.uninstall_component(component, &new_manifest, tx, &download_cfg.process)?; } // Install components for (component_bin, installer_file) in things_to_install { - tx = component_bin.install(installer_file, tx, new_manifest, self, download_cfg)?; + tx = component_bin.install(installer_file, tx, &new_manifest, &self, &download_cfg)?; } // Install new distribution manifest - let new_manifest_str = new_manifest.clone().stringify()?; + let new_manifest_str = (*new_manifest).clone().stringify()?; tx.modify_file(rel_installed_manifest_path)?; utils::write_file("manifest", &installed_manifest_path, &new_manifest_str)?; @@ -292,13 +292,13 @@ impl Manifestation { #[cfg(test)] pub(crate) fn uninstall( &self, - manifest: &Manifest, - tmp_cx: &temp::Context, - process: &Process, + manifest: Arc, + tmp_cx: Arc, + process: Arc, ) -> Result<()> { let prefix = self.installation.prefix(); - let mut tx = Transaction::new(prefix.clone(), tmp_cx, process); + let mut tx = Transaction::new(prefix.clone(), tmp_cx.clone(), process.clone()); // Read configuration and delete it let rel_config_path = prefix.rel_manifest_file(CONFIG_FILE); @@ -311,20 +311,20 @@ impl Manifestation { tx.remove_file("dist config", rel_config_path)?; for component in config.components { - tx = self.uninstall_component(&component, manifest, tx, process)?; + tx = self.uninstall_component(&component, &manifest, tx, &process)?; } tx.commit(); Ok(()) } - fn uninstall_component<'a>( + fn uninstall_component( &self, component: &Component, manifest: &Manifest, - mut tx: Transaction<'a>, + mut tx: Transaction, process: &Process, - ) -> Result> { + ) -> Result { // For historical reasons, the rust-installer component // names are not the same as the dist manifest component // names. Some are just the component name some are the @@ -386,7 +386,7 @@ impl Manifestation { &self, new_manifest: &[String], update_hash: Option<&Path>, - dl_cfg: &DownloadCfg<'_>, + dl_cfg: Arc, ) -> Result> { // If there's already a v2 installation then something has gone wrong if self.read_config()?.is_some() { @@ -422,17 +422,17 @@ impl Manifestation { info!("installing component rust"); // Begin transaction - let mut tx = Transaction::new(prefix, dl_cfg.tmp_cx, dl_cfg.process); + let mut tx = Transaction::new(prefix, dl_cfg.tmp_cx.clone(), dl_cfg.process.clone()); // Uninstall components let components = self.installation.list()?; for component in components { - tx = component.uninstall(tx, dl_cfg.process)?; + tx = component.uninstall(tx, &dl_cfg.process)?; } // Install all the components in the installer let reader = utils::FileReaderWithProgress::new_file(&installer_file)?; - let package: &dyn Package = &TarGzPackage::new(reader, dl_cfg)?; + let package: &dyn Package = &TarGzPackage::new(reader, &dl_cfg)?; for component in package.components() { tx = package.install(&self.installation, &component, None, tx)?; } @@ -447,12 +447,12 @@ impl Manifestation { // doesn't have a configuration or manifest-derived list of // component/target pairs. Uninstall it using the installer's // component list before upgrading. - fn maybe_handle_v2_upgrade<'a>( + fn maybe_handle_v2_upgrade( &self, config: &Option, - mut tx: Transaction<'a>, + mut tx: Transaction, process: &Process, - ) -> Result> { + ) -> Result { let installed_components = self.installation.list()?; let looks_like_v1 = config.is_none() && !installed_components.is_empty(); @@ -690,17 +690,17 @@ impl Update { } } -struct ComponentBinary<'a> { - component: &'a Component, - binary: &'a HashedBinary, +struct ComponentBinary { + component: Arc, + binary: Arc, status: DownloadStatus, } -impl<'a> ComponentBinary<'a> { +impl ComponentBinary { async fn download( &self, url: &Url, - download_cfg: &DownloadCfg<'_>, + download_cfg: &DownloadCfg, max_retries: usize, new_manifest: &Manifest, ) -> Result { @@ -727,19 +727,19 @@ impl<'a> ComponentBinary<'a> { Ok(downloaded_file) } - fn install<'t>( + fn install( &self, installer_file: File, - tx: Transaction<'t>, + tx: Transaction, new_manifest: &Manifest, manifestation: &Manifestation, - download_cfg: &DownloadCfg<'_>, - ) -> Result> { + download_cfg: &DownloadCfg, + ) -> Result { // For historical reasons, the rust-installer component // names are not the same as the dist manifest component // names. Some are just the component name some are the // component name plus the target triple. - let component = self.component; + let component = &self.component; let pkg_name = component.name_in_manifest(); let short_pkg_name = component.short_name_in_manifest(); let short_name = component.short_name(new_manifest); diff --git a/src/dist/manifestation/tests.rs b/src/dist/manifestation/tests.rs index 376fc4c97e..08cb3718d9 100644 --- a/src/dist/manifestation/tests.rs +++ b/src/dist/manifestation/tests.rs @@ -480,16 +480,16 @@ impl TestContext { force: bool, ) -> Result { let dl_cfg = DownloadCfg { - tmp_cx: &self.tmp_cx, - download_dir: &self.download_dir, - tracker: DownloadTracker::new(false, &self.tp.process), - process: &self.tp.process, + tmp_cx: Arc::new(self.tmp_cx.clone()), + download_dir: Arc::new(self.download_dir.clone()), + tracker: Arc::new(DownloadTracker::new(false, &self.tp.process)), + process: Arc::new(self.tp.process.clone()), }; // Download the dist manifest and place it into the installation prefix let manifest_url = make_manifest_url(&self.url, &self.toolchain)?; let manifest_file = self.tmp_cx.new_file()?; - download_file(&manifest_url, &manifest_file, None, None, dl_cfg.process).await?; + download_file(&manifest_url, &manifest_file, None, None, &dl_cfg.process).await?; let manifest_str = utils::read_file("manifest", &manifest_file)?; let manifest = Manifest::parse(&manifest_str)?; @@ -507,12 +507,12 @@ impl TestContext { remove_components: remove.to_owned(), }; - manifestation + Arc::new(manifestation) .update( - &manifest, + Arc::new(manifest), changes, force, - &dl_cfg, + dl_cfg, &self.toolchain.manifest_name(), true, ) @@ -524,7 +524,11 @@ impl TestContext { let manifestation = Manifestation::open(self.prefix.clone(), trip)?; let manifest = manifestation.load_manifest()?.unwrap(); - manifestation.uninstall(&manifest, &self.tmp_cx, &self.tp.process)?; + manifestation.uninstall( + Arc::new(manifest), + Arc::new(self.tmp_cx.clone()), + Arc::new(self.tp.process.clone()), + )?; Ok(()) } diff --git a/src/dist/mod.rs b/src/dist/mod.rs index d9af808a53..e9f35f6bf5 100644 --- a/src/dist/mod.rs +++ b/src/dist/mod.rs @@ -1,7 +1,13 @@ //! Installation from a Rust distribution server use std::{ - collections::HashSet, env, fmt, io::Write, ops::Deref, path::Path, str::FromStr, sync::LazyLock, + collections::HashSet, + env, fmt, + io::Write, + ops::Deref, + path::Path, + str::FromStr, + sync::{Arc, LazyLock}, }; use anyhow::{Context, Result, anyhow, bail}; @@ -881,7 +887,7 @@ pub(crate) struct DistOptions<'a> { pub(crate) toolchain: &'a ToolchainDesc, pub(crate) profile: Profile, pub(crate) update_hash: Option<&'a Path>, - pub(crate) dl_cfg: DownloadCfg<'a>, + pub(crate) dl_cfg: DownloadCfg, /// --force bool is whether to force an update/install pub(crate) force: bool, /// --allow-downgrade @@ -967,7 +973,7 @@ pub(crate) async fn update_from_dist( let mut toolchain = opts.toolchain.clone(); let res = loop { let result = try_update_from_dist_( - &opts.dl_cfg, + opts.dl_cfg.clone(), opts.update_hash, &toolchain, match opts.exists { @@ -1067,7 +1073,7 @@ pub(crate) async fn update_from_dist( #[allow(clippy::too_many_arguments)] async fn try_update_from_dist_( - download: &DownloadCfg<'_>, + download: DownloadCfg, update_hash: Option<&Path>, toolchain: &ToolchainDesc, profile: Option, @@ -1085,7 +1091,7 @@ async fn try_update_from_dist_( info!("syncing channel updates for {toolchain_str}"); match dl_v2_manifest( dist_root, - download, + &download, // Even if manifest has not changed, we must continue to install requested components. // So if components or targets is not empty, we skip passing `update_hash` so that // we essentially degenerate to `rustup component add` / `rustup target add` @@ -1149,9 +1155,9 @@ async fn try_update_from_dist_( fetched.clone_from(&m.date); - return match manifestation + return match Arc::new(manifestation) .update( - &m, + Arc::new(m), changes, force_update, download, @@ -1192,7 +1198,7 @@ async fn try_update_from_dist_( } // If the v2 manifest is not found then try v1 - let manifest = match dl_v1_manifest(dist_root, download, toolchain).await { + let manifest = match dl_v1_manifest(dist_root, &download, toolchain).await { Ok(m) => m, Err(err) => match err.downcast_ref::() { Some(RustupError::ChecksumFailed { .. }) => return Err(err), @@ -1212,8 +1218,9 @@ async fn try_update_from_dist_( }, }; + let download = download.to_owned(); let result = manifestation - .update_v1(&manifest, update_hash, download) + .update_v1(&manifest, update_hash, Arc::new(download)) .await; // inspect, determine what context to add, then process afterwards. @@ -1230,11 +1237,11 @@ async fn try_update_from_dist_( pub(crate) async fn dl_v2_manifest( dist_root: &str, - download: &DownloadCfg<'_>, + download: &DownloadCfg, update_hash: Option<&Path>, toolchain: &ToolchainDesc, ) -> Result> { - let manifest_url = toolchain.manifest_v2_url(dist_root, download.process); + let manifest_url = toolchain.manifest_v2_url(dist_root, &download.process); match download .download_and_check(&manifest_url, update_hash, None, ".toml") .await @@ -1260,7 +1267,7 @@ pub(crate) async fn dl_v2_manifest( // Manifest checksum mismatched. warn!("{err}"); - let server = dist_root_server(download.process)?; + let server = dist_root_server(&download.process)?; if server == DEFAULT_DIST_SERVER { info!( "this is likely due to an ongoing update of the official release server, please try again later" @@ -1280,7 +1287,7 @@ pub(crate) async fn dl_v2_manifest( async fn dl_v1_manifest( dist_root: &str, - download: &DownloadCfg<'_>, + download: &DownloadCfg, toolchain: &ToolchainDesc, ) -> Result> { let root_url = toolchain.package_dir(dist_root); @@ -1292,7 +1299,7 @@ async fn dl_v1_manifest( return Ok(vec![installer_name]); } - let manifest_url = toolchain.manifest_v1_url(dist_root, download.process); + let manifest_url = toolchain.manifest_v1_url(dist_root, &download.process); let manifest_dl = download .download_and_check(&manifest_url, None, None, "") .await?; diff --git a/src/dist/temp.rs b/src/dist/temp.rs index 0eacbb44f6..8df795c898 100644 --- a/src/dist/temp.rs +++ b/src/dist/temp.rs @@ -72,6 +72,7 @@ impl Drop for File { } } +#[derive(Clone)] pub struct Context { root_directory: PathBuf, pub dist_server: String, diff --git a/src/settings.rs b/src/settings.rs index 15d7669fa0..96413c3303 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,8 +1,8 @@ -use std::cell::RefCell; use std::collections::BTreeMap; use std::fmt; use std::path::{Path, PathBuf}; use std::str::FromStr; +use std::sync::RwLock; use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; @@ -13,22 +13,22 @@ use crate::dist::{AutoInstallMode, Profile}; use crate::errors::RustupError; use crate::utils; -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Debug)] pub struct SettingsFile { path: PathBuf, - cache: RefCell>, + cache: RwLock>, } impl SettingsFile { pub(crate) fn new(path: PathBuf) -> Self { Self { path, - cache: RefCell::new(None), + cache: RwLock::default(), } } fn write_settings(&self) -> Result<()> { - let settings = self.cache.borrow(); + let settings = self.cache.read().unwrap(); utils::write_file( "settings", &self.path, @@ -40,10 +40,10 @@ impl SettingsFile { fn read_settings(&self) -> Result<()> { let mut needs_save = false; { - let b = self.cache.borrow(); + let b = self.cache.read().unwrap(); if b.is_none() { drop(b); - *self.cache.borrow_mut() = Some(if utils::is_file(&self.path) { + *self.cache.write().unwrap() = Some(if utils::is_file(&self.path) { let content = utils::read_file("settings", &self.path)?; Settings::parse(&content).with_context(|| RustupError::ParsingFile { name: "settings", @@ -65,14 +65,17 @@ impl SettingsFile { self.read_settings()?; // Settings can no longer be None so it's OK to unwrap - f(self.cache.borrow().as_ref().unwrap()) + f(self.cache.read().unwrap().as_ref().unwrap()) } pub(crate) fn with_mut Result>(&self, f: F) -> Result { self.read_settings()?; // Settings can no longer be None so it's OK to unwrap - let result = { f(self.cache.borrow_mut().as_mut().unwrap())? }; + let result = { + let mut result = self.cache.write().unwrap(); + f(result.as_mut().unwrap())? + }; self.write_settings()?; Ok(result) } diff --git a/src/test/dist.rs b/src/test/dist.rs index 66031c6143..7490c574a4 100644 --- a/src/test/dist.rs +++ b/src/test/dist.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use std::fs::{self, File}; use std::io::{self, Read, Write}; use std::path::{Path, PathBuf}; -use std::sync::{LazyLock, Mutex}; +use std::sync::{Arc, LazyLock, Mutex}; use url::Url; @@ -54,15 +54,19 @@ impl DistContext { }) } - pub fn start(&self) -> anyhow::Result<(Transaction<'_>, Components, DirectoryPackage)> { + pub fn start(&self) -> anyhow::Result<(Transaction, Components, DirectoryPackage)> { let tx = self.transaction(); let components = Components::open(self.prefix.clone())?; let pkg = DirectoryPackage::new(self.pkg_dir.path().to_owned(), true)?; Ok((tx, components, pkg)) } - pub fn transaction(&self) -> Transaction<'_> { - Transaction::new(self.prefix.clone(), &self.cx, &self.tp.process) + pub fn transaction(&self) -> Transaction { + Transaction::new( + self.prefix.clone(), + Arc::new(self.cx.clone()), + Arc::new(self.tp.process.clone()), + ) } } diff --git a/src/toolchain/distributable.rs b/src/toolchain/distributable.rs index 6bee499390..6beaaabf78 100644 --- a/src/toolchain/distributable.rs +++ b/src/toolchain/distributable.rs @@ -1,6 +1,9 @@ #[cfg(windows)] use std::fs; -use std::{convert::Infallible, env::consts::EXE_SUFFIX, ffi::OsStr, path::Path, process::Command}; +use std::{ + convert::Infallible, env::consts::EXE_SUFFIX, ffi::OsStr, path::Path, process::Command, + sync::Arc, +}; #[cfg(windows)] use anyhow::Context; @@ -111,12 +114,12 @@ impl<'a> DistributableToolchain<'a> { }; let download_cfg = DownloadCfg::new(self.toolchain.cfg); - manifestation + Arc::new(manifestation) .update( - &manifest, + Arc::new(manifest), changes, false, - &download_cfg, + download_cfg, &self.desc.manifest_name(), false, ) @@ -508,12 +511,12 @@ impl<'a> DistributableToolchain<'a> { }; let download_cfg = DownloadCfg::new(self.toolchain.cfg); - manifestation + Arc::new(manifestation) .update( - &manifest, + Arc::new(manifest), changes, false, - &download_cfg, + download_cfg, &self.desc.manifest_name(), false, ) diff --git a/tests/suite/dist_install.rs b/tests/suite/dist_install.rs index 3090f06503..b8fc628e2c 100644 --- a/tests/suite/dist_install.rs +++ b/tests/suite/dist_install.rs @@ -1,5 +1,6 @@ use std::fs::File; use std::io::Write; +use std::sync::Arc; use rustup::dist::component::Components; use rustup::dist::component::Transaction; @@ -169,7 +170,11 @@ fn uninstall() { tx.commit(); // Now uninstall - let mut tx = Transaction::new(cx.prefix.clone(), &cx.cx, &cx.tp.process); + let mut tx = Transaction::new( + cx.prefix.clone(), + Arc::new(cx.cx), + Arc::new(cx.tp.process.clone()), + ); for component in components.list().unwrap() { tx = component.uninstall(tx, &cx.tp.process).unwrap(); }