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