chiark / gitweb /
wip fix new accounts, ICE
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 2 Nov 2020 22:05:47 +0000 (22:05 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 2 Nov 2020 22:05:47 +0000 (22:05 +0000)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/accounts.rs
src/error.rs
src/utils.rs

index c02a0096477fd2c597bdfa0db164baeb6ea37da9..235eba7e170e492cbe394ce1e5681a5c39090be6 100644 (file)
@@ -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")
index e419ea5476e0f1490f9076e5dee52c194f46ac75..47ccdf9884cfb6b2723a771f8649568d0a602061 100644 (file)
@@ -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<AccountName>),
+  #[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<InternalError> for SpecError {
     SpecError::InternalError(format!("{:?}",ie))
   }
 }
-
 #[derive(Error,Debug,Serialize,Clone)]
 pub enum ErrorSignaledViaUpdate {
   InternalError,
@@ -177,3 +182,7 @@ impl From<SVGProcessingError> for SpecError {
     InternalError::SVGProcessingFailed(se).into()
   }
 }
+
+impl From<AccountsSaveError> for MgmtError {
+  fn from(ase: AccountsSaveError) -> MgmtError { IE::from(ase).into() }
+}
index e088fcbb60eb7aa2b7f16e6c21f7536498df4caf..2132a74d55556cab9d119606bb24b1b56470c2a7 100644 (file)
@@ -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<char> 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)
+    }
+  }
+}