From 8b2e2885de38693665c911985d663c8237701267 Mon Sep 17 00:00:00 2001 From: Tim Cuthbertson Date: Thu, 1 Feb 2024 14:46:24 +1100 Subject: [PATCH] add a version to each stored path, to support format upgrades --- cli/src/cache.rs | 27 +++++++++++----- cli/src/store.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 95 insertions(+), 12 deletions(-) diff --git a/cli/src/cache.rs b/cli/src/cache.rs index ad75ccd..4e55057 100644 --- a/cli/src/cache.rs +++ b/cli/src/cache.rs @@ -8,8 +8,8 @@ use lazy_static::lazy_static; use std::{fs, process::{Command, Stdio}, collections::HashSet, path::PathBuf, str::FromStr}; use crate::{paths::RuntimePaths, store::StoreMeta}; -use crate::rewrite; -use crate::store::StoreIdentity; +use crate::{rewrite, store}; +use crate::store::{MetaError, StoreIdentity}; use crate::serde_from_string; #[derive(Clone, Debug, PartialEq, Eq)] @@ -214,11 +214,24 @@ impl Client { // NOTE: must hold lock to call this let dest_path = self.paths.store_path.join(entry.directory()); let meta_path = self.paths.meta_path.join(entry.directory()); - if meta_path.exists() && dest_path.exists() { - debug!("Cache path already exists: {:?}", dest_path); - return Ok(None); + let meta = if meta_path.exists() && dest_path.exists() { + match StoreMeta::load(&self.paths, &entry) { + Result::Ok(meta) => Ok(Some(meta)), + Err(MetaError::UnsupportedVersion) => { + fs::remove_file(&meta_path)?; + Ok(None) + }, + Err(MetaError::LoadError(e)) => { + Err(e) + }, + }? } else { - self.fetch_narinfo(entry).map(Some) + None + }; + + match meta { + Some(_) => Ok(None), + None => self.fetch_narinfo(entry).map(Some), } } @@ -282,7 +295,7 @@ impl Client { rewrite::rewrite_all_recursively(&dest, &self.paths.rewrite, &nar_info.references)?; // create the meta file, which signifies the validity of the store path - StoreMeta::write(&self.paths, nar_info)?; + StoreMeta::write(&self.paths, nar_info, store::StoreEntryVersion::Latest)?; info!("Fetched {}", nar_info.identity.display_name()); Ok(()) diff --git a/cli/src/store.rs b/cli/src/store.rs index 4b59da5..13403a7 100644 --- a/cli/src/store.rs +++ b/cli/src/store.rs @@ -1,4 +1,5 @@ use anyhow::*; +use log::*; use std::path::PathBuf; use std::{fs, str::FromStr, fmt::Display}; use serde::{Serialize, Deserialize}; @@ -7,6 +8,36 @@ use filetime::{set_file_mtime, FileTime}; use crate::{serde_from_string, cache::NarInfo}; use crate::paths::*; +const LATEST_VERSION: i32 = 1; +// v0: original runix release +// v1: codesign on all rewritten executable files + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(from = "i32", into = "i32")] +pub enum StoreEntryVersion { + Latest, + Historical(i32), +} + +impl From for StoreEntryVersion { + fn from(value: i32) -> Self { + if value == LATEST_VERSION { + Self::Latest + } else { + Self::Historical(value) + } + } +} + +impl Into for StoreEntryVersion { + fn into(self) -> i32 { + match self { + Self::Latest => LATEST_VERSION, + Self::Historical(i) => i, + } + } +} + pub struct StoreIdentityNameDisplay<'a>(&'a StoreIdentity); impl<'a> Display for StoreIdentityNameDisplay<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -101,6 +132,31 @@ impl Display for StoreIdentity { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct StoreMeta { pub references: Vec, + + pub version: StoreEntryVersion, +} + +#[derive(Debug)] +pub enum MetaError { + UnsupportedVersion, + LoadError(anyhow::Error), +} + +impl std::fmt::Display for MetaError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::UnsupportedVersion => f.write_str("Unsupported store entry version"), + Self::LoadError(e) => e.fmt(f), + } + } +} + +impl std::error::Error for MetaError {} + +impl From for MetaError { + fn from(err: Error) -> Self { + Self::LoadError(err) + } } impl StoreMeta { @@ -108,7 +164,7 @@ impl StoreMeta { paths.meta_path.join(&identity.directory) } - pub fn write(paths: &RuntimePaths, nar_info: &NarInfo<'_>) -> Result<()> { + pub fn write(paths: &RuntimePaths, nar_info: &NarInfo<'_>, version: StoreEntryVersion) -> Result<()> { let get_dest = || Self::path(paths, nar_info.identity); util::with_file_ctx(|| format!("Writing {}", get_dest().display()), || { let dest = get_dest(); @@ -122,6 +178,7 @@ impl StoreMeta { .with_context(|| format!("Writing {}", tmp_dest.display()))?; let store_meta = StoreMeta { references: nar_info.references.clone(), + version, }; serde_json::to_writer(file, &store_meta)?; util::rename(tmp_dest, dest)?; @@ -136,9 +193,9 @@ impl StoreMeta { }) } - pub fn load(paths: &RuntimePaths, identity: &StoreIdentity) -> Result { + pub fn load(paths: &RuntimePaths, identity: &StoreIdentity) -> Result { let p = Self::path(paths, identity); - util::with_file_ctx(|| format!("Loading {}", p.display()), || { + let meta = util::with_file_ctx(|| format!("Loading {}", p.display()), || { let contents = fs::read_to_string(&p)?; let meta = serde_json::from_str(&contents)?; let used_timestamp = FileTime::from_last_modification_time(&fs::metadata(&p)?); @@ -146,9 +203,15 @@ impl StoreMeta { meta, used_timestamp }) - }) - } + })?; + if meta.is_supported() { + Result::Ok(meta) + } else { + debug!("Cache path exists but version unsupported: {:?}", &p); + Err(MetaError::UnsupportedVersion) + } + } } // Contents of the meta file as well as access time @@ -164,4 +227,11 @@ impl StoreMetaFull { pub fn references(&self) -> &Vec { &self.meta.references } + + pub fn is_supported(&self) -> bool { + match self.meta.version { + StoreEntryVersion::Latest => true, + StoreEntryVersion::Historical(_) => false, + } + } }