From 13c14480e04e604951872dcdf30c2a16bcc23b1f Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Sat, 15 May 2021 16:19:41 +0100 Subject: [PATCH] bundles: Implement ClearBundles We need a command line UI and a test case, still. Signed-off-by: Ian Jackson --- daemon/cmdlistener.rs | 9 ++++++ src/bundles.rs | 64 +++++++++++++++++++++++++++++++++++++++++++ src/commands.rs | 4 +-- src/spec.rs | 1 + 4 files changed, 76 insertions(+), 2 deletions(-) diff --git a/daemon/cmdlistener.rs b/daemon/cmdlistener.rs index e6dbeb3c..5813298a 100644 --- a/daemon/cmdlistener.rs +++ b/daemon/cmdlistener.rs @@ -290,6 +290,15 @@ fn execute_and_respond(cs: &mut CommandStreamData, cmd: MgmtCommand, bulk_download = Some(Box::new(f)); Fine } + MC::ClearBundles { game } => { + let (ag, gref) = start_modify_game(&game)?; + modify_bundles( + cs, &ag, &gref, &[TP::ClearBundles], + &mut |mut ig, mut bundles: MutexGuard<'_, InstanceBundles>| { + bundles.clear(&mut ig) + })?; + Fine + } MC::ListGames { all } => { let ag = AccountsGuard::lock(); diff --git a/src/bundles.rs b/src/bundles.rs index 4c95ebde..f3b46422 100644 --- a/src/bundles.rs +++ b/src/bundles.rs @@ -904,6 +904,70 @@ impl InstanceBundles { } } +//---------- clearing ---------- + +impl InstanceBundles { + #[throws(MgmtError)] + pub fn clear(&mut self, ig: &mut Instance) { + + // Check we are not in bundle state USED + for (m, ok) in [ + ( "pieces", ig.gs.pieces.is_empty() ), + ( "pieces - occults", ig.gs.occults.is_empty() ), + ( "pieces - ipieces", ig.ipieces.is_empty() ), + ( "piece aliases", ig.pcaliases.is_empty() ), + ( "pieces - ioccults", ig.ioccults.is_empty() ), + ] { + if ! ok { throw!(ME::BundlesInUse(m.to_string())) } + } + + // If we are in UNUSED, become BROKEN + // xxx: make shape libs and specs inaccessible to mgmt commands + let _ = (); + + (||{ + // If we are in BROKEN, become WRECKAGE + let mut to_clean + : Vec<(&dyn Fn(&str) -> io::Result<()>, String)> + = vec![]; + for (index, slot) in self.bundles.iter_mut().enumerate() { + if_let!{ Some(note) = slot; else continue } + let id = Id { + index: index.try_into() + .map_err(|_e| internal_error_bydebug(&(index, ¬e)))?, + kind: note.kind, + }; + let tmp = id.path_tmp(&ig.name); + let install = id.path_(&ig.name); + match fs::rename(&install, &tmp) { + Err(e) if e.kind() == ErrorKind::NotFound => { } + x => x.with_context(|| tmp.clone()) + .with_context(|| install.clone()) + .context("rename away")? + } + note.state = State::Uploading; + // These will happen in order, turning each bundle from + // WRECKAGE into NEARLY-ABSENT + to_clean.push((&|p| fs::remove_dir_all(p), id.path_dir(&ig.name) )); + to_clean.push((&|p| fs::remove_file (p), tmp )); + } + + // Actually try to clean up WRECKAGE into NEARLY-ABSENT + for (f,p) in to_clean { + match f(&p) { + Err(e) if e.kind() == ErrorKind::NotFound => { } + x => x.with_context(|| p.clone()).context("clean up")? + } + } + + // Right, everything is at most NEARLY-ASENT, make them ABSENT + self.bundles.clear(); + + Ok::<_,IE>(()) + })()?; + } +} + #[test] fn id_file_parse() { let check_y = |s,index,kind| { diff --git a/src/commands.rs b/src/commands.rs index 8ca26f14..786402c6 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -37,10 +37,9 @@ pub enum MgmtCommand { game: InstanceName, }, - /* ClearBundles { game: InstanceName, - },*/ + }, UploadBundle { game: InstanceName, size: usize, @@ -236,6 +235,7 @@ pub enum MgmtError { TooManyBundles, BadBundle(String), BundleNotFound, + BundlesInUse(String), } impl Display for MgmtError { #[throws(fmt::Error)] diff --git a/src/spec.rs b/src/spec.rs index 9eb59dcb..88a75bbd 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -138,6 +138,7 @@ pub enum TablePermission { ChangePieces, UploadBundles, SetLinks, + ClearBundles, ResetOthersAccess, RedeliverOthersAccess, ModifyOtherPlayer, -- 2.30.2