From 2865cbaf70d0af36cd3be20a927da6c6a390cb3d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 11 Dec 2016 03:33:10 +0100 Subject: [PATCH] Use file contents instead of symlink. --- parity/main.rs | 32 +++++++++++++++++++------------- parity/updater.rs | 36 ++++++++++++------------------------ 2 files changed, 31 insertions(+), 37 deletions(-) diff --git a/parity/main.rs b/parity/main.rs index 7d5426eb9..a435e6230 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -117,7 +117,7 @@ mod stratum; use std::{process, env}; use std::collections::HashMap; -use std::io::{self as stdio, BufReader, Write}; +use std::io::{self as stdio, BufReader, Read, Write}; use std::fs::File; use std::path::PathBuf; use util::sha3::sha3; @@ -190,21 +190,26 @@ fn sync_main(alt_mains: &mut HashMap) { } // TODO: merge with version in Updater. -fn updates_latest() -> PathBuf { +fn updates_path(name: &str) -> PathBuf { let mut dest = PathBuf::from(env::home_dir().unwrap().to_str().expect("env filesystem paths really should be valid; qed")); dest.push(".parity-updates"); - dest.push("parity"); + dest.push(name); dest } +fn latest_exe_path() -> Option { + File::open(updates_path("latest")).ok() + .and_then(|mut f| { let mut exe = String::new(); f.read_to_string(&mut exe).ok().map(|_| updates_path(&exe)) }) +} + // Starts ~/.parity-updates/parity and returns the code it exits with. fn run_parity() -> Option { - let exe = updates_latest(); - process::Command::new(exe) - .args(&env::args_os().collect::>()) - .status() - .map(|es| es.code().unwrap_or(128)) - .ok() + latest_exe_path().and_then(|exe| process::Command::new(exe) + .args(&env::args_os().collect::>()) + .status() + .map(|es| es.code().unwrap_or(128)) + .ok() + ) } const PLEASE_RESTART_EXIT_CODE: i32 = 69; @@ -254,16 +259,17 @@ fn main() { let force_direct = std::env::args().any(|arg| arg == "--force-direct"); let exe = std::env::current_exe().ok(); let development = exe.as_ref().and_then(|p| p.parent().and_then(|p| p.parent()).and_then(|p| p.file_name()).map(|n| n == "target")).unwrap_or(false); - let same_name = exe.as_ref().and_then(|p| p.file_stem().map(|s| s == "parity")).unwrap_or(false); - let have_update = updates_latest().exists(); - let is_non_updated_current = exe.map_or(false, |p| p.canonicalize().ok() != updates_latest().canonicalize().ok()); + let same_name = exe.as_ref().map(|p| p.file_stem().map_or(false, |s| s == "parity") && p.extension().map_or(true, |x| x == "exe")).unwrap_or(false); + let latest_exe = latest_exe_path(); + let have_update = latest_exe.as_ref().map_or(false, |p| p.exists()); + let is_non_updated_current = exe.map_or(false, |exe| latest_exe.as_ref().map_or(false, |lexe| exe.canonicalize().ok() != lexe.canonicalize().ok())); trace_main!("Starting up {} (force-direct: {}, development: {}, same-name: {}, have-update: {}, non-updated-current: {})", std::env::current_exe().map(|x| format!("{}", x.display())).unwrap_or("".to_owned()), force_direct, development, same_name, have_update, is_non_updated_current); if !force_direct && !development && same_name && have_update && is_non_updated_current { // looks like we're not running ~/.parity-updates/parity when the user is expecting otherwise. // Everything run inside a loop, so we'll be able to restart from the child into a new version seamlessly. loop { // If we fail to run the updated parity then fallback to local version. - trace_main!("Attempting to run latest update..."); + trace_main!("Attempting to run latest update ({})...", latest_exe.as_ref().expect("guarded by have_update; latest_exe must exist for have_update; qed").display()); let exit_code = run_parity().unwrap_or_else(|| { trace_main!("Falling back to local..."); main_direct() }); trace_main!("Latest exited with {}", exit_code); if exit_code != PLEASE_RESTART_EXIT_CODE { diff --git a/parity/updater.rs b/parity/updater.rs index 7da7a6dc8..79abdbe1a 100644 --- a/parity/updater.rs +++ b/parity/updater.rs @@ -15,8 +15,9 @@ // along with Parity. If not, see . use std::sync::{Arc, Weak}; -use std::{io, os, fs, env}; -use std::path::{Path, PathBuf}; +use std::{fs, env}; +use std::io::Write; +use std::path::{PathBuf}; use util::misc::{VersionInfo, ReleaseTrack/*, platform*/}; use util::{Address, H160, H256, FixedHash, Mutex, Bytes}; use super::operations::Operations; @@ -113,16 +114,6 @@ fn platform() -> String { "test".to_owned() } -#[cfg(windows)] -fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { - os::windows::fs::symlink_file(src, dst) -} - -#[cfg(not(windows))] -fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { - os::unix::fs::symlink(src, dst) -} - impl Updater { pub fn new(client: Weak, update_policy: UpdatePolicy) -> Arc { let mut u = Updater { @@ -170,10 +161,10 @@ impl Updater { (|| -> Result { let mut s = self.state.lock(); if let Some(r) = s.ready.take() { - let p = Self::update_file_path(&r.version); - let n = Self::updates_latest(); - let _ = fs::remove_file(&n); - match symlink(p, n) { + let p = Self::update_file_name(&r.version); + let n = Self::updates_path("latest"); + // TODO: creating then writing is a bit fragile. would be nice to make it atomic. + match fs::File::create(&n).and_then(|mut f| f.write_all(p.as_bytes())) { Ok(_) => { info!("Completed upgrade to {}", &r.version); s.installed = Some(r); @@ -249,17 +240,14 @@ impl Updater { } } - fn update_file_path(v: &VersionInfo) -> PathBuf { - let mut dest = PathBuf::from(env::home_dir().unwrap().to_str().expect("env filesystem paths really should be valid; qed")); - dest.push(".parity-updates"); - dest.push(format!("parity-{}.{}.{}-{:?}", v.version.major, v.version.minor, v.version.patch, v.hash)); - dest + fn update_file_name(v: &VersionInfo) -> String { + format!("parity-{}.{}.{}-{:?}", v.version.major, v.version.minor, v.version.patch, v.hash) } - fn updates_latest() -> PathBuf { + fn updates_path(name: &str) -> PathBuf { let mut dest = PathBuf::from(env::home_dir().unwrap().to_str().expect("env filesystem paths really should be valid; qed")); dest.push(".parity-updates"); - dest.push("parity"); + dest.push(name); dest } @@ -270,7 +258,7 @@ impl Updater { let fetched = s.fetching.take().unwrap(); let b = result.map_err(|e| format!("Unable to fetch update ({}): {:?}", fetched.version, e))?; info!("Fetched latest version ({}) OK to {}", fetched.version, b.display()); - let dest = Self::update_file_path(&fetched.version); + let dest = Self::updates_path(&Self::update_file_name(&fetched.version)); fs::create_dir_all(dest.parent().expect("at least one thing pushed; qed")).map_err(|e| format!("Unable to create updates path: {:?}", e))?; fs::copy(&b, &dest).map_err(|e| format!("Unable to copy update: {:?}", e))?; info!("Copied file to {}", dest.display());