openethereum/crates/util/dir/src/lib.rs

371 lines
11 KiB
Rust

// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
#![warn(missing_docs)]
//! Dir utilities for platform-specific operations
///
/// Base ($BASE) Paths where we store our data and cache corresponds to:
/// Windows:
/// UserData: FOLDERID_RoamingAppData: %APPDATA% (%USERPROFILE%\AppData\Roaming)
/// UserCache: FOLDERID_LocalAppData: %LOCALAPPDATA% (%USERPROFILE%\AppData\Local)
/// MacOS:
/// UserData: /Users/Alice/Library/Application Support/
/// UserCache: /Users/Alice/Library/Caches/
/// Unix:
/// UserData is: $HOME/.local/share/
/// UserCache is: $HOME/.cache/
///
/// On this UserData base path we are adding additional application folders:
/// If older parity folders are present we will use them as default for backward compatibility:
/// Windows: $BASE/Parity/Ethereum/
/// Unix/MacOS: $BASE/io.parity.ethereum/
///
/// For OpenEthereum paths we are using:
/// Wndows/MacOS: $BASE/OpenEthereum/
/// Unix: $BASE/openethereum/
///
extern crate app_dirs;
extern crate ethereum_types;
extern crate home;
extern crate journaldb;
pub mod helpers;
use app_dirs::{data_root, get_app_root, AppDataType, AppInfo};
use ethereum_types::{H256, H64};
use helpers::{replace_home, replace_home_and_local};
use journaldb::Algorithm;
use std::{
fs,
path::{Path, PathBuf},
};
// re-export platform-specific functions
use platform::*;
pub use home::home_dir;
/// Platform-specific chains path for standard client - Windows only
#[cfg(target_os = "windows")]
pub const CHAINS_PATH: &str = "$LOCAL/chains";
/// Platform-specific chains path for standard client
#[cfg(not(target_os = "windows"))]
pub const CHAINS_PATH: &str = "$BASE/chains";
/// Platform-specific cache path - Windows only
#[cfg(target_os = "windows")]
pub const CACHE_PATH: &str = "$LOCAL/cache";
/// Platform-specific cache path
#[cfg(not(target_os = "windows"))]
pub const CACHE_PATH: &str = "$BASE/cache";
// this const is irrelevent cause we do have migrations now,
// but we still use it for backwards compatibility
const LEGACY_CLIENT_DB_VER_STR: &str = "5.3";
#[derive(Debug, PartialEq)]
/// Parity local data directories
pub struct Directories {
/// Base dir
pub base: String,
/// Database dir
pub db: String,
/// Cache dir
pub cache: String,
/// Dir to store keys
pub keys: String,
/// Signer dir
pub signer: String,
/// Secrets dir
pub secretstore: String,
}
impl Default for Directories {
fn default() -> Self {
let data_dir = default_data_path();
let local_dir = default_local_path();
Directories {
base: replace_home(&data_dir, "$BASE"),
db: replace_home_and_local(&data_dir, &local_dir, CHAINS_PATH),
cache: replace_home_and_local(&data_dir, &local_dir, CACHE_PATH),
keys: replace_home(&data_dir, "$BASE/keys"),
signer: replace_home(&data_dir, "$BASE/signer"),
secretstore: replace_home(&data_dir, "$BASE/secretstore"),
}
}
}
impl Directories {
/// Create local directories
pub fn create_dirs(
&self,
signer_enabled: bool,
secretstore_enabled: bool,
) -> Result<(), String> {
fs::create_dir_all(&self.base).map_err(|e| e.to_string())?;
fs::create_dir_all(&self.db).map_err(|e| e.to_string())?;
fs::create_dir_all(&self.cache).map_err(|e| e.to_string())?;
fs::create_dir_all(&self.keys).map_err(|e| e.to_string())?;
if signer_enabled {
fs::create_dir_all(&self.signer).map_err(|e| e.to_string())?;
}
if secretstore_enabled {
fs::create_dir_all(&self.secretstore).map_err(|e| e.to_string())?;
}
Ok(())
}
/// Database paths.
pub fn database(
&self,
genesis_hash: H256,
fork_name: Option<String>,
spec_name: String,
) -> DatabaseDirectories {
DatabaseDirectories {
path: self.db.clone(),
legacy_path: self.base.clone(),
genesis_hash,
fork_name,
spec_name,
}
}
/// Get the ipc sockets path
pub fn ipc_path(&self) -> PathBuf {
let mut dir = Path::new(&self.base).to_path_buf();
dir.push("ipc");
dir
}
/// Legacy keys path
// TODO: remove in 1.7
pub fn legacy_keys_path(&self, testnet: bool) -> PathBuf {
let mut dir = Path::new(&self.base).to_path_buf();
if testnet {
dir.push("testnet_keys");
} else {
dir.push("keys");
}
dir
}
/// Get the keys path
pub fn keys_path(&self, data_dir: &str) -> PathBuf {
let mut dir = PathBuf::from(&self.keys);
dir.push(data_dir);
dir
}
}
#[derive(Debug, PartialEq)]
/// Database directories for the given fork.
pub struct DatabaseDirectories {
/// Base path
pub path: String,
/// Legacy path
pub legacy_path: String,
/// Genesis hash
pub genesis_hash: H256,
/// Name of current fork
pub fork_name: Option<String>,
/// Name of current spec
pub spec_name: String,
}
impl DatabaseDirectories {
/// Base DB directory for the given fork.
// TODO: remove in 1.7
pub fn legacy_fork_path(&self) -> PathBuf {
let gh = H64::from_slice(&self.genesis_hash.as_bytes()[20..28]);
Path::new(&self.legacy_path).join(format!(
"{:x}{}",
gh,
self.fork_name
.as_ref()
.map(|f| format!("-{}", f))
.unwrap_or_default()
))
}
/// Spec root directory for the given fork.
pub fn spec_root_path(&self) -> PathBuf {
Path::new(&self.path).join(&self.spec_name)
}
/// Generic client path
pub fn client_path(&self, pruning: Algorithm) -> PathBuf {
self.db_root_path()
.join(pruning.as_internal_name_str())
.join("db")
}
/// DB root path, named after genesis hash
pub fn db_root_path(&self) -> PathBuf {
let gh = H64::from_slice(&self.genesis_hash.as_bytes()[20..28]);
self.spec_root_path().join("db").join(format!("{:x}", gh))
}
/// DB path
pub fn db_path(&self, pruning: Algorithm) -> PathBuf {
self.db_root_path().join(pruning.as_internal_name_str())
}
/// Get the root path for database
// TODO: remove in 1.7
pub fn legacy_version_path(&self, pruning: Algorithm) -> PathBuf {
self.legacy_fork_path().join(format!(
"v{}-sec-{}",
LEGACY_CLIENT_DB_VER_STR,
pruning.as_internal_name_str()
))
}
/// Get user defaults path, legacy way
// TODO: remove in 1.7
pub fn legacy_user_defaults_path(&self) -> PathBuf {
self.legacy_fork_path().join("user_defaults")
}
/// Get snapshot path, legacy way
// TODO: remove in 1.7
pub fn legacy_snapshot_path(&self) -> PathBuf {
self.legacy_fork_path().join("snapshot")
}
/// Get user defaults path, legacy way
// TODO: remove in 1.7
pub fn legacy_network_path(&self) -> PathBuf {
self.legacy_fork_path().join("network")
}
/// Get user defauls path
pub fn user_defaults_path(&self) -> PathBuf {
self.spec_root_path().join("user_defaults")
}
/// Get the path for the snapshot directory given the genesis hash and fork name.
pub fn snapshot_path(&self) -> PathBuf {
self.db_root_path().join("snapshot")
}
/// Get the path for the network directory.
pub fn network_path(&self) -> PathBuf {
self.spec_root_path().join("network")
}
}
fn default_path(t: AppDataType) -> Option<PathBuf> {
let app_info = AppInfo {
name: PARITY_PRODUCT,
author: PARITY_AUTHOR,
};
let old_root = get_app_root(t, &app_info).ok()?;
if old_root.exists() {
return Some(old_root);
}
let mut root = data_root(t).ok()?;
root.push(if LOWERCASE {
"openethereum"
} else {
"OpenEthereum"
});
Some(root)
}
fn fallback_path() -> PathBuf {
let mut p = PathBuf::new();
p.push("$HOME");
p.push(".openethereum");
p
}
/// Default data path
pub fn default_data_pathbuf() -> PathBuf {
default_path(AppDataType::UserData).unwrap_or_else(fallback_path)
}
/// Default data path
pub fn default_data_path() -> String {
default_data_pathbuf().to_string_lossy().into_owned()
}
/// Default local path
pub fn default_local_path() -> String {
default_path(AppDataType::UserCache)
.unwrap_or_else(fallback_path)
.to_string_lossy()
.into_owned()
}
/// these variables are used only for backward compatibility .
/// In case that there is folder from older parity version we will use it as default path,
/// in case there is not we will create openethereum folder.Algorithm
#[cfg(target_os = "macos")]
mod platform {
pub const LOWERCASE: bool = false;
pub const PARITY_AUTHOR: &str = "Parity";
pub const PARITY_PRODUCT: &str = "io.parity.ethereum";
}
#[cfg(windows)]
mod platform {
pub const LOWERCASE: bool = false;
pub const PARITY_AUTHOR: &str = "Parity";
pub const PARITY_PRODUCT: &str = "Ethereum";
}
#[cfg(not(any(target_os = "macos", windows)))]
mod platform {
pub const LOWERCASE: bool = true;
pub const PARITY_AUTHOR: &str = "parity";
pub const PARITY_PRODUCT: &str = "io.parity.ethereum";
}
#[cfg(test)]
mod tests {
use super::Directories;
use helpers::{replace_home, replace_home_and_local};
#[test]
fn test_default_directories() {
let data_dir = super::default_data_path();
let local_dir = super::default_local_path();
let expected = Directories {
base: replace_home(&data_dir, "$BASE"),
db: replace_home_and_local(
&data_dir,
&local_dir,
if cfg!(target_os = "windows") {
"$LOCAL/chains"
} else {
"$BASE/chains"
},
),
cache: replace_home_and_local(
&data_dir,
&local_dir,
if cfg!(target_os = "windows") {
"$LOCAL/cache"
} else {
"$BASE/cache"
},
),
keys: replace_home(&data_dir, "$BASE/keys"),
signer: replace_home(&data_dir, "$BASE/signer"),
secretstore: replace_home(&data_dir, "$BASE/secretstore"),
};
assert_eq!(expected, Directories::default());
}
}