diff --git a/server/src/cli.yml b/server/src/cli.yml index 2b2b981d..c2a13d45 100644 --- a/server/src/cli.yml +++ b/server/src/cli.yml @@ -81,4 +81,12 @@ args: help: Tells the server to only accept SSL connections and disables the non-SSL port subcommands: - upgrade: - about: Upgrades old datsets to the latest format supported by this server edition \ No newline at end of file + about: Upgrades old datsets to the latest format supported by this server edition + args: + - format: + short: f + long: from + takes_value: true + value_name: format + required: true + help: The format of the old files which need to be upgraded \ No newline at end of file diff --git a/server/src/compat/mod.rs b/server/src/compat/mod.rs index f958abd5..280f168c 100644 --- a/server/src/compat/mod.rs +++ b/server/src/compat/mod.rs @@ -32,6 +32,7 @@ use crate::coredb::{htable::HTable, Data}; use crate::diskstore::snapshot::SNAP_MATCH; use bytes::Bytes; +use core::hint::unreachable_unchecked; use libsky::TResult; use std::collections::HashMap; use std::fs; @@ -45,57 +46,93 @@ type DiskStoreType = (Vec, Vec>); const SKY_UPGRADE_FOLDER: &str = "newdata"; const SKY_COMPLETE_UPGRADE_FOLDER: &str = "newdata/snapshots/remote"; +enum Format { + /// The disk storage format used in 0.3.0 + Elstore, + /// The disk storage format used between 0.3.1-0.5.2 + Neocopy, + /// The disk storage format used in 0.6.0 + Sparrowlock, +} + +impl Format { + pub const fn has_snapshots(&self) -> bool { + if let Self::Neocopy | Self::Sparrowlock = self { + true + } else { + false + } + } +} + pub fn concat_path(other: impl Into) -> PathBuf { let mut path = PathBuf::from(SKY_UPGRADE_FOLDER); path.push(other.into()); path } -pub fn upgrade() -> TResult<()> { +pub fn upgrade(format: &str) -> TResult<()> { + let fmt = match format { + "elstore" => Format::Elstore, + "neocopy" => Format::Neocopy, + "sparrowlock" => Format::Sparrowlock, + _ => return Err("Unknown format".into()), + }; + if let Format::Sparrowlock = fmt { + log::info!("No file upgrades required"); + return Ok(()); + } fs::create_dir_all(SKY_COMPLETE_UPGRADE_FOLDER)?; // first attempt to upgrade the data file log::info!("Upgrading data file"); - upgrade_file("data/data.bin", concat_path("data.bin")) + upgrade_file("data/data.bin", concat_path("data.bin"), &fmt) .map_err(|e| format!("Failed to upgrade data.bin file with error: {}", e))?; log::info!("Finished upgrading data file"); // now let's check what files are there in the snapshots directory - log::info!("Upgrading snapshots"); - let snapshot_dir = fs::read_dir("data/snapshots")?; - for path in snapshot_dir { - let path = path?.path(); - if path.is_dir() && path != PathBuf::from("data/snapshots/remote") { - return Err("The snapshot directory contains unrecognized files".into()); - } - if path.is_file() { - let fname = path - .file_name() - .ok_or("Failed to get path name in snapshot directory")? - .to_string_lossy(); - if !SNAP_MATCH.is_match(&fname) { - return Err("The snapshot directory contains unexpected files".into()); + if fmt.has_snapshots() { + log::info!("Upgrading snapshots"); + let snapshot_dir = fs::read_dir("data/snapshots")?; + for path in snapshot_dir { + let path = path?.path(); + if path.is_dir() && path != PathBuf::from("data/snapshots/remote") { + return Err("The snapshot directory contains unrecognized files".into()); + } + if path.is_file() { + let fname = path + .file_name() + .ok_or("Failed to get path name in snapshot directory")? + .to_string_lossy(); + if !SNAP_MATCH.is_match(&fname) { + return Err("The snapshot directory contains unexpected files".into()); + } + upgrade_file( + path.clone(), + concat_path(format!("snapshots/{}", fname)), + &fmt, + )?; } - upgrade_file(path.clone(), concat_path(format!("snapshots/{}", fname)))?; } - } - log::info!("Finished upgrading snapshots"); - log::info!("Upgrading remote snapshots"); - let remote_snapshot_dir = fs::read_dir("data/snapshots/remote")?; - for path in remote_snapshot_dir { - let path = path?.path(); - if path.is_file() { - let fname = path - .file_name() - .ok_or("Failed to get filename in remote snapshot directory")? - .to_string_lossy(); - upgrade_file( - path.clone(), - concat_path(format!("snapshots/remote/{}", fname)), - )?; - } else { - return Err("Unexpected files in the remote snapshot directory".into()); + log::info!("Finished upgrading snapshots"); + log::info!("Upgrading remote snapshots"); + let remote_snapshot_dir = fs::read_dir("data/snapshots/remote")?; + for path in remote_snapshot_dir { + let path = path?.path(); + if path.is_file() { + let fname = path + .file_name() + .ok_or("Failed to get filename in remote snapshot directory")? + .to_string_lossy(); + upgrade_file( + path.clone(), + concat_path(format!("snapshots/remote/{}", fname)), + &fmt, + )?; + } else { + return Err("Unexpected files in the remote snapshot directory".into()); + } } + log::info!("Finished upgrading remote snapshots"); } - log::info!("Finished upgrading remote snapshots"); log::info!("All files were upgraded. Updating directories"); fs::rename("data", "olddata")?; log::info!("Moved old data into folder 'olddata'"); @@ -104,23 +141,42 @@ pub fn upgrade() -> TResult<()> { Ok(()) } -fn upgrade_file(src: impl Into, destination: impl Into) -> TResult<()> { +fn upgrade_file( + src: impl Into, + destination: impl Into, + fmt: &Format, +) -> TResult<()> { let file = src.into(); log::info!("Upgrading file: {}", file.to_string_lossy()); let old_data_file = fs::read(&file)?; - let data_from_old_file: DiskStoreType = bincode::deserialize(&old_data_file)?; - let data_from_old_file: HashMap = HashMap::from_iter( - data_from_old_file - .0 - .into_iter() - .zip(data_from_old_file.1.into_iter()) - .map(|(key, value)| (key, Data::from_blob(Bytes::from(value)))), - ); - let data_in_new_format: HTable = HTable::from_iter( - data_from_old_file - .into_iter() - .map(|(key, value)| (Data::from_string(key), value)), - ); + let data_in_new_format: HTable = match *fmt { + Format::Elstore => { + let data_from_old_file: HashMap = bincode::deserialize(&old_data_file)?; + HTable::from_iter( + data_from_old_file + .into_iter() + .map(|(key, value)| (Data::from(key), Data::from(value))), + ) + } + Format::Neocopy => { + let data_from_old_file: DiskStoreType = bincode::deserialize(&old_data_file)?; + let data_from_old_file: HashMap = HashMap::from_iter( + data_from_old_file + .0 + .into_iter() + .zip(data_from_old_file.1.into_iter()) + .map(|(key, value)| (key, Data::from_blob(Bytes::from(value)))), + ); + HTable::from_iter( + data_from_old_file + .into_iter() + .map(|(key, value)| (Data::from_string(key), value)), + ) + } + Format::Sparrowlock => unsafe { + unreachable_unchecked(); + }, + }; let data_in_new_format = data_in_new_format.serialize()?; let destination = destination.into(); let mut file = fs::File::create(&destination)?; diff --git a/server/src/config/mod.rs b/server/src/config/mod.rs index 090f454c..8916db5a 100644 --- a/server/src/config/mod.rs +++ b/server/src/config/mod.rs @@ -27,6 +27,7 @@ //! This module provides tools to handle configuration files and settings use crate::compat; +use core::hint::unreachable_unchecked; #[cfg(test)] use libsky::TResult; use serde::Deserialize; @@ -411,12 +412,18 @@ pub fn get_config_file_or_return_cfg() -> Result