From: Ian Jackson Date: Mon, 2 Nov 2020 22:05:47 +0000 (+0000) Subject: wip fix new accounts, ICE X-Git-Tag: otter-0.2.0~558 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=b1fc38f54366983628d6b71b0ada354cb5a5c1d4;p=otter.git wip fix new accounts, ICE Signed-off-by: Ian Jackson --- diff --git a/src/accounts.rs b/src/accounts.rs index c02a0096..235eba7e 100644 --- a/src/accounts.rs +++ b/src/accounts.rs @@ -20,6 +20,7 @@ pub enum AccountScope { type AS = AccountScope; type ME = MgmtError; +type IE = InternalError; #[derive(Error,Debug,Clone,Copy,Serialize,Deserialize)] #[derive(Hash,Ord,Eq,PartialOrd,PartialEq)] @@ -285,7 +286,7 @@ impl AccountsGuard { let ok = self.save_accounts_now(); match ok { Ok(()) => Ok(output), - Err(e) => Err((e, output)) + Err(e) => Err((e.into(), output)) } } @@ -334,11 +335,63 @@ impl AccountsGuard { self.save_accounts_now()?; } - pub fn save_accounts_now(&self) -> Result<(), InternalError> { - panic!("xxx") + #[throws(AccountsSaveError)] + pub fn save_accounts_now(&self) { + let accounts = self.0.as_ref().expect("loaded"); + let main = save_path(); + let tmp = format!("{}.tmp", &main); + let f = fs::File::create(&tmp)?; + let f = BufWriter::new(f); + rmp_serde::encode::write_named(&mut f, &accounts)?; + f.flush()?; + let f = f.into_inner().map_err(|e| { + io::Error::new(e.error().kind(), e) + })?; + f.sync_data()?; + f.close()?; + fs::rename(&tmp, &main)?; } } +#[derive(Error,Debug)] +pub enum AccountsSaveError { + #[error("Error writing/installing file: {0}")] + IO(#[from] io::Error), + #[error("Error encoding msgpack: {0}")] + Encode(#[from] rmp_serde::encode::Error) +} + +const ACCOUNTS_FILE : &str = "accounts"; + +fn save_path() -> String { + format!("{}/{}", config().save_directory, &ACCOUNTS_FILE) +} + +#[throws(StartupError)] +pub fn load_accounts() { + let ag = AccountsGuard::lock(); + assert!(ag.0.is_none()); + let path = save_path(); + let f = fs::File::open(&path); + let f = match f { + Ok(f) => f, + Err(e) if e.kind() == io::ErrorKind::NotFound => return, + e@ Err(_) => e.with_context(|| path.clone())?, + }; + let f = BufReader::new(f); + let accounts : Accounts = rmp_serde::decode::from_read(&mut f)?; + let chk = |acctid: AccountId, account: &AccountName| if_chain! { + if accounts.names.get(account) == Some(&acctid); + if let Some(got_record) = accounts.records.get(acctid); + if got_record.account = account; + then { Ok(()) } + else { Err(IE::AccountsCorrupted(acctid, account.clone())) } + }; + for (account, acctid) in &accounts.names { chk(*acctid, account) } + for (acctid, account) in &accounts.records { chk(*acctid, account) } + *ag = accounts; +} + impl AccountRecord { pub fn expire_tokens_revealed(&mut self) { panic!("xxx") diff --git a/src/error.rs b/src/error.rs index e419ea54..47ccdf98 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,6 +4,8 @@ use crate::imports::*; +type IE = InternalError; + #[derive(Error,Debug)] pub enum OnlineError { #[error("Game in process of being destroyed")] @@ -31,6 +33,10 @@ from_instance_lock_error!{OnlineError} pub enum InternalError { #[error("Game corrupted by previous crash")] GameCorrupted, + #[error("Accounts file corrupted for acctid={0:?} account={1:?}")] + AccountsCorrupted(AccountId, Arc), + #[error("Error saving accounts file: {0}")] + AccountsSaveError(#[from] AccountsSaveError), #[error("Server MessagePack encoding error {0}")] MessagePackEncodeFail(#[from] rmp_serde::encode::Error), #[error("Server MessagePack decoding error (game load failed) {0}")] @@ -58,7 +64,6 @@ impl From for SpecError { SpecError::InternalError(format!("{:?}",ie)) } } - #[derive(Error,Debug,Serialize,Clone)] pub enum ErrorSignaledViaUpdate { InternalError, @@ -177,3 +182,7 @@ impl From for SpecError { InternalError::SVGProcessingFailed(se).into() } } + +impl From for MgmtError { + fn from(ase: AccountsSaveError) -> MgmtError { IE::from(ase).into() } +} diff --git a/src/utils.rs b/src/utils.rs index e088fcbb..2132a74d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -2,6 +2,13 @@ // SPDX-License-Identifier: AGPL-3.0-or-later // There is NO WARRANTY. +use std::fs; +use std::io; +use std::os::unix::io::IntoRawFd; + +use fehler::{throw, throws}; +use libc; + pub trait OrdExt : Ord + Sized + Clone { fn update_max(&mut self, new: &Self) { if *new > *self { *self = new.clone() } @@ -22,3 +29,26 @@ impl SplitAtDelim for str { } } } + +// https://github.com/rust-lang/rust/issues/32255 :-( + +pub trait LocalFileExt { + fn close(self) -> Result<(), io::Error>; +} + +impl LocalFileExt for fs::File { + #[throws(io::Error)] + fn close(self) { + let r = unsafe { + let fd = self.into_raw_fd(); + libc::close(fd) + }; + if r == 0 { + () + } else if r == -1 { + throw!(io::Error::last_os_error()) + } else { + panic!("close(2) returned {}", r) + } + } +}