1 // Copyright 2018-2019 Mozilla 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 // this file except in compliance with the License. You may obtain a copy of the 5 // License at http://www.apache.org/licenses/LICENSE-2.0 6 // Unless required by applicable law or agreed to in writing, software distributed 7 // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 // specific language governing permissions and limitations under the License. 10 11 use std::{ 12 borrow::Cow, 13 collections::HashMap, 14 fs, 15 ops::DerefMut, 16 path::{ 17 Path, 18 PathBuf, 19 }, 20 sync::{ 21 Arc, 22 RwLock, 23 RwLockReadGuard, 24 RwLockWriteGuard, 25 }, 26 }; 27 28 use id_arena::Arena; 29 use log::warn; 30 31 use super::{ 32 database::Database, 33 DatabaseFlagsImpl, 34 DatabaseImpl, 35 EnvironmentFlagsImpl, 36 ErrorImpl, 37 InfoImpl, 38 RoTransactionImpl, 39 RwTransactionImpl, 40 StatImpl, 41 }; 42 use crate::backend::traits::{ 43 BackendEnvironment, 44 BackendEnvironmentBuilder, 45 }; 46 47 const DEFAULT_DB_FILENAME: &str = "data.safe.bin"; 48 49 type DatabaseArena = Arena<Database>; 50 type DatabaseNameMap = HashMap<Option<String>, DatabaseImpl>; 51 52 #[derive(Debug, PartialEq, Eq, Copy, Clone)] 53 pub struct EnvironmentBuilderImpl { 54 flags: EnvironmentFlagsImpl, 55 max_readers: Option<usize>, 56 max_dbs: Option<usize>, 57 map_size: Option<usize>, 58 make_dir_if_needed: bool, 59 discard_if_corrupted: bool, 60 } 61 62 impl<'b> BackendEnvironmentBuilder<'b> for EnvironmentBuilderImpl { 63 type Environment = EnvironmentImpl; 64 type Error = ErrorImpl; 65 type Flags = EnvironmentFlagsImpl; 66 new() -> EnvironmentBuilderImpl67 fn new() -> EnvironmentBuilderImpl { 68 EnvironmentBuilderImpl { 69 flags: EnvironmentFlagsImpl::empty(), 70 max_readers: None, 71 max_dbs: None, 72 map_size: None, 73 make_dir_if_needed: false, 74 discard_if_corrupted: false, 75 } 76 } 77 set_flags<T>(&mut self, flags: T) -> &mut Self where T: Into<Self::Flags>,78 fn set_flags<T>(&mut self, flags: T) -> &mut Self 79 where 80 T: Into<Self::Flags>, 81 { 82 self.flags = flags.into(); 83 self 84 } 85 set_max_readers(&mut self, max_readers: u32) -> &mut Self86 fn set_max_readers(&mut self, max_readers: u32) -> &mut Self { 87 self.max_readers = Some(max_readers as usize); 88 self 89 } 90 set_max_dbs(&mut self, max_dbs: u32) -> &mut Self91 fn set_max_dbs(&mut self, max_dbs: u32) -> &mut Self { 92 self.max_dbs = Some(max_dbs as usize); 93 self 94 } 95 set_map_size(&mut self, map_size: usize) -> &mut Self96 fn set_map_size(&mut self, map_size: usize) -> &mut Self { 97 self.map_size = Some(map_size); 98 self 99 } 100 set_make_dir_if_needed(&mut self, make_dir_if_needed: bool) -> &mut Self101 fn set_make_dir_if_needed(&mut self, make_dir_if_needed: bool) -> &mut Self { 102 self.make_dir_if_needed = make_dir_if_needed; 103 self 104 } 105 set_discard_if_corrupted(&mut self, discard_if_corrupted: bool) -> &mut Self106 fn set_discard_if_corrupted(&mut self, discard_if_corrupted: bool) -> &mut Self { 107 self.discard_if_corrupted = discard_if_corrupted; 108 self 109 } 110 open(&self, path: &Path) -> Result<Self::Environment, Self::Error>111 fn open(&self, path: &Path) -> Result<Self::Environment, Self::Error> { 112 // Technically NO_SUB_DIR should change these checks here, but they're both currently 113 // unimplemented with this storage backend. 114 if !path.is_dir() { 115 if !self.make_dir_if_needed { 116 return Err(ErrorImpl::UnsuitableEnvironmentPath(path.into())); 117 } 118 fs::create_dir_all(path)?; 119 } 120 let mut env = EnvironmentImpl::new(path, self.flags, self.max_readers, self.max_dbs, self.map_size)?; 121 env.read_from_disk(self.discard_if_corrupted)?; 122 Ok(env) 123 } 124 } 125 126 #[derive(Debug)] 127 pub(crate) struct EnvironmentDbs { 128 pub(crate) arena: DatabaseArena, 129 pub(crate) name_map: DatabaseNameMap, 130 } 131 132 #[derive(Debug)] 133 pub(crate) struct EnvironmentDbsRefMut<'a> { 134 pub(crate) arena: &'a mut DatabaseArena, 135 pub(crate) name_map: &'a mut DatabaseNameMap, 136 } 137 138 impl<'a> From<&'a mut EnvironmentDbs> for EnvironmentDbsRefMut<'a> { from(dbs: &mut EnvironmentDbs) -> EnvironmentDbsRefMut139 fn from(dbs: &mut EnvironmentDbs) -> EnvironmentDbsRefMut { 140 EnvironmentDbsRefMut { 141 arena: &mut dbs.arena, 142 name_map: &mut dbs.name_map, 143 } 144 } 145 } 146 147 #[derive(Debug)] 148 pub struct EnvironmentImpl { 149 path: PathBuf, 150 max_dbs: usize, 151 dbs: RwLock<EnvironmentDbs>, 152 ro_txns: Arc<()>, 153 rw_txns: Arc<()>, 154 } 155 156 impl EnvironmentImpl { serialize(&self) -> Result<Vec<u8>, ErrorImpl>157 fn serialize(&self) -> Result<Vec<u8>, ErrorImpl> { 158 let dbs = self.dbs.read().map_err(|_| ErrorImpl::EnvPoisonError)?; 159 let data: HashMap<_, _> = dbs.name_map.iter().map(|(name, id)| (name, &dbs.arena[id.0])).collect(); 160 Ok(bincode::serialize(&data)?) 161 } 162 deserialize(bytes: &[u8], discard_if_corrupted: bool) -> Result<(DatabaseArena, DatabaseNameMap), ErrorImpl>163 fn deserialize(bytes: &[u8], discard_if_corrupted: bool) -> Result<(DatabaseArena, DatabaseNameMap), ErrorImpl> { 164 let mut arena = DatabaseArena::new(); 165 let mut name_map = HashMap::new(); 166 let data: HashMap<_, _> = match bincode::deserialize(&bytes) { 167 Err(_) if discard_if_corrupted => Ok(HashMap::new()), 168 result => result, 169 }?; 170 for (name, db) in data { 171 name_map.insert(name, DatabaseImpl(arena.alloc(db))); 172 } 173 Ok((arena, name_map)) 174 } 175 } 176 177 impl EnvironmentImpl { new( path: &Path, flags: EnvironmentFlagsImpl, max_readers: Option<usize>, max_dbs: Option<usize>, map_size: Option<usize>, ) -> Result<EnvironmentImpl, ErrorImpl>178 pub(crate) fn new( 179 path: &Path, 180 flags: EnvironmentFlagsImpl, 181 max_readers: Option<usize>, 182 max_dbs: Option<usize>, 183 map_size: Option<usize>, 184 ) -> Result<EnvironmentImpl, ErrorImpl> { 185 if !flags.is_empty() { 186 warn!("Ignoring `flags={:?}`", flags); 187 } 188 if let Some(max_readers) = max_readers { 189 warn!("Ignoring `max_readers={}`", max_readers); 190 } 191 if let Some(map_size) = map_size { 192 warn!("Ignoring `map_size={}`", map_size); 193 } 194 195 Ok(EnvironmentImpl { 196 path: path.to_path_buf(), 197 max_dbs: max_dbs.unwrap_or(std::usize::MAX), 198 dbs: RwLock::new(EnvironmentDbs { 199 arena: DatabaseArena::new(), 200 name_map: HashMap::new(), 201 }), 202 ro_txns: Arc::new(()), 203 rw_txns: Arc::new(()), 204 }) 205 } 206 read_from_disk(&mut self, discard_if_corrupted: bool) -> Result<(), ErrorImpl>207 pub(crate) fn read_from_disk(&mut self, discard_if_corrupted: bool) -> Result<(), ErrorImpl> { 208 let mut path = Cow::from(&self.path); 209 if fs::metadata(&path)?.is_dir() { 210 path.to_mut().push(DEFAULT_DB_FILENAME); 211 }; 212 if fs::metadata(&path).is_err() { 213 return Ok(()); 214 }; 215 let (arena, name_map) = Self::deserialize(&fs::read(&path)?, discard_if_corrupted)?; 216 self.dbs = RwLock::new(EnvironmentDbs { 217 arena, 218 name_map, 219 }); 220 Ok(()) 221 } 222 write_to_disk(&self) -> Result<(), ErrorImpl>223 pub(crate) fn write_to_disk(&self) -> Result<(), ErrorImpl> { 224 let mut path = Cow::from(&self.path); 225 if fs::metadata(&path)?.is_dir() { 226 path.to_mut().push(DEFAULT_DB_FILENAME); 227 }; 228 fs::write(&path, self.serialize()?)?; 229 Ok(()) 230 } 231 dbs(&self) -> Result<RwLockReadGuard<EnvironmentDbs>, ErrorImpl>232 pub(crate) fn dbs(&self) -> Result<RwLockReadGuard<EnvironmentDbs>, ErrorImpl> { 233 self.dbs.read().map_err(|_| ErrorImpl::EnvPoisonError) 234 } 235 dbs_mut(&self) -> Result<RwLockWriteGuard<EnvironmentDbs>, ErrorImpl>236 pub(crate) fn dbs_mut(&self) -> Result<RwLockWriteGuard<EnvironmentDbs>, ErrorImpl> { 237 self.dbs.write().map_err(|_| ErrorImpl::EnvPoisonError) 238 } 239 } 240 241 impl<'e> BackendEnvironment<'e> for EnvironmentImpl { 242 type Database = DatabaseImpl; 243 type Error = ErrorImpl; 244 type Flags = DatabaseFlagsImpl; 245 type Info = InfoImpl; 246 type RoTransaction = RoTransactionImpl<'e>; 247 type RwTransaction = RwTransactionImpl<'e>; 248 type Stat = StatImpl; 249 get_dbs(&self) -> Result<Vec<Option<String>>, Self::Error>250 fn get_dbs(&self) -> Result<Vec<Option<String>>, Self::Error> { 251 let dbs = self.dbs.read().map_err(|_| ErrorImpl::EnvPoisonError)?; 252 Ok(dbs.name_map.keys().map(|key| key.to_owned()).collect()) 253 } 254 open_db(&self, name: Option<&str>) -> Result<Self::Database, Self::Error>255 fn open_db(&self, name: Option<&str>) -> Result<Self::Database, Self::Error> { 256 if Arc::strong_count(&self.ro_txns) > 1 { 257 return Err(ErrorImpl::DbsIllegalOpen); 258 } 259 // TOOD: don't reallocate `name`. 260 let key = name.map(String::from); 261 let dbs = self.dbs.read().map_err(|_| ErrorImpl::EnvPoisonError)?; 262 let db = dbs.name_map.get(&key).ok_or(ErrorImpl::DbNotFoundError)?; 263 Ok(*db) 264 } 265 create_db(&self, name: Option<&str>, flags: Self::Flags) -> Result<Self::Database, Self::Error>266 fn create_db(&self, name: Option<&str>, flags: Self::Flags) -> Result<Self::Database, Self::Error> { 267 if Arc::strong_count(&self.ro_txns) > 1 { 268 return Err(ErrorImpl::DbsIllegalOpen); 269 } 270 // TOOD: don't reallocate `name`. 271 let key = name.map(String::from); 272 let mut dbs = self.dbs.write().map_err(|_| ErrorImpl::EnvPoisonError)?; 273 if dbs.name_map.keys().filter_map(|k| k.as_ref()).count() >= self.max_dbs && name != None { 274 return Err(ErrorImpl::DbsFull); 275 } 276 let parts = EnvironmentDbsRefMut::from(dbs.deref_mut()); 277 let arena = parts.arena; 278 let name_map = parts.name_map; 279 let id = name_map.entry(key).or_insert_with(|| DatabaseImpl(arena.alloc(Database::new(Some(flags), None)))); 280 Ok(*id) 281 } 282 begin_ro_txn(&'e self) -> Result<Self::RoTransaction, Self::Error>283 fn begin_ro_txn(&'e self) -> Result<Self::RoTransaction, Self::Error> { 284 RoTransactionImpl::new(self, self.ro_txns.clone()) 285 } 286 begin_rw_txn(&'e self) -> Result<Self::RwTransaction, Self::Error>287 fn begin_rw_txn(&'e self) -> Result<Self::RwTransaction, Self::Error> { 288 RwTransactionImpl::new(self, self.rw_txns.clone()) 289 } 290 sync(&self, force: bool) -> Result<(), Self::Error>291 fn sync(&self, force: bool) -> Result<(), Self::Error> { 292 warn!("Ignoring `force={}`", force); 293 self.write_to_disk() 294 } 295 stat(&self) -> Result<Self::Stat, Self::Error>296 fn stat(&self) -> Result<Self::Stat, Self::Error> { 297 Ok(StatImpl) 298 } 299 info(&self) -> Result<Self::Info, Self::Error>300 fn info(&self) -> Result<Self::Info, Self::Error> { 301 Ok(InfoImpl) 302 } 303 freelist(&self) -> Result<usize, Self::Error>304 fn freelist(&self) -> Result<usize, Self::Error> { 305 unimplemented!() 306 } 307 load_ratio(&self) -> Result<Option<f32>, Self::Error>308 fn load_ratio(&self) -> Result<Option<f32>, Self::Error> { 309 warn!("`load_ratio()` is irrelevant for this storage backend."); 310 Ok(None) 311 } 312 set_map_size(&self, size: usize) -> Result<(), Self::Error>313 fn set_map_size(&self, size: usize) -> Result<(), Self::Error> { 314 warn!("`set_map_size({})` is ignored by this storage backend.", size); 315 Ok(()) 316 } 317 get_files_on_disk(&self) -> Vec<PathBuf>318 fn get_files_on_disk(&self) -> Vec<PathBuf> { 319 // Technically NO_SUB_DIR and NO_LOCK should change this output, but 320 // they're both currently unimplemented with this storage backend. 321 let mut db_filename = self.path.clone(); 322 db_filename.push(DEFAULT_DB_FILENAME); 323 return vec![db_filename]; 324 } 325 } 326