tickv/lib.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! # TicKV
6//!
7//! TicKV (Tiny Circular Key Value) is a small file system allowing
8//! key value pairs to be stored in Flash Memory.
9//!
10//! TicKV was written to allow the Tock OS kernel to persistently store app data
11//! on flash. It was written to be generic though, so other Rust applications can
12//! use it if they want.
13//!
14//! TicKV is based on similar concepts as
15//! [Yaffs1](https://yaffs.net/documents/how-yaffs-works]).
16//!
17//! ## Goals of TicKV
18//!
19//! TicKV is designed with these main goals (in order)
20//!
21//! * Fully implemented in no_std Rust
22//! * Power loss resilient
23//! * Maintain data integrity and detect media errors
24//! * Wear leveling
25//! * Low memory usage
26//! * Low storage overhead
27//! * No external crates in use (not including unit tests)
28//!
29//! TicKV is also designed with some assumptions
30//!
31//! * Most operations will be retrieving keys
32//! * Some operations will be storing keys
33//! * Keys will rarely be deleted
34//! * Key values will rarely need to be modified
35//!
36//! ## ACID characteristics
37//!
38//! TicKV provides ACID properties. For the purpose of ACID a transaction is a
39//! key operation, that is finding, adding, invalidating or fully removing
40//! (garbage collection) a key.
41//!
42//! To provide ACIS characteristics TicKV requires that the `FlashController`
43//! implementation complete all transactions in a single operation. That is the
44//! flash `write()` function must either successfully write all of the data or
45//! none. If the implementation completes a partial operation, then the Atomicity
46//! and Consistency traits will be lost. If the implementation reports completion
47//! when the data hasn't been written yet, then the Isolation trait will be lost.
48//!
49//! Atomicity: TicKV guarantees that all operations are treated as a single unit
50//! inside the implementation. The database will be left unchanged if a
51//! transaction fails.
52//!
53//! Consistency: Consistency is maintained similar to atomicity. All operations
54//! can only take the database from a valid state to another valid state.
55//!
56//! Isolation: TicKV only allows a single operation at a time. In this way it
57//! provides isolation. The layer above TicKV is responsible for handling
58//! concurrent accesses by deferring operations for example.
59//!
60//! Durability: TicKV ensures durability and once a transaction has completed
61//! and been committed to flash it will remain there.
62//!
63//! ## Using TicKV
64//!
65//! See the generated Rust documentation for details on using this in your project.
66//!
67//! ## How TicKV works
68//!
69//! Unlike a regular File System (FS) TicKV is only designed to store Key/Value (KV)
70//! pairs in flash. It does not support writing actual files, directories or other
71//! complex objects. Although a traditional file system layer could be added on top
72//! to add such features.
73//!
74//! TicKV allows writing new key/value pairs (by appending them) and removing
75//! old key/value pairs.
76//!
77//! TicKV has two important types, regions and objects.
78//!
79//! A TicKV region is the smallest region of the flash memory that can be erased
80//! in a single command.
81//!
82//! TicKV saves and restores objects from flash. TicKV objects contain the value
83//! the user wanted to store as well as extra header data. Objects are internal to
84//! TicKV and users don't need to understand them in detail to use it.
85//!
86//! For more details on the technical implementation see the [SPEC.md](./spec.md) file.
87//!
88//! # Using TicKV
89//!
90//! To use TicKV first you need to implemented the `FlashCtrl` trait. The
91//! example below is for 1024 byte region sizes.
92//!
93//! Then you will need to create a TicKV implementation.
94//!
95//!
96//! ```rust
97//! // EXAMPLE ONLY: The `DefaultHasher` is subject to change
98//! // and hence is not a good fit.
99//! use std::collections::hash_map::DefaultHasher;
100//! use std::hash::{Hash, Hasher};
101//! use std::cell::RefCell;
102//! use tickv::{TicKV, MAIN_KEY};
103//! use tickv::error_codes::ErrorCode;
104//! use tickv::flash_controller::FlashController;
105//!
106//! fn get_hashed_key(unhashed_key: &[u8]) -> u64 {
107//! let mut hash_function = DefaultHasher::new();
108//! unhashed_key.hash(&mut hash_function);
109//! hash_function.finish()
110//! }
111//!
112//! struct FlashCtrl {
113//! buf: RefCell<[[u8; 1024]; 64]>,
114//! }
115//!
116//! impl FlashCtrl {
117//! fn new() -> Self {
118//! Self {
119//! buf: RefCell::new([[0xFF; 1024]; 64]),
120//! }
121//! }
122//! }
123//!
124//! impl FlashController<1024> for FlashCtrl {
125//! fn read_region(&self, region_number: usize, buf: &mut [u8; 1024]) -> Result<(), ErrorCode> {
126//! // TODO: Read the specified flash region
127//! for (i, b) in buf.iter_mut().enumerate() {
128//! *b = self.buf.borrow()[region_number][i]
129//! }
130//! Ok(())
131//! }
132//!
133//! fn write(&self, address: usize, buf: &[u8]) -> Result<(), ErrorCode> {
134//! // TODO: Write the data to the specified flash address
135//! for (i, d) in buf.iter().enumerate() {
136//! self.buf.borrow_mut()[address / 1024][(address % 1024) + i] = *d;
137//! }
138//! Ok(())
139//! }
140//!
141//! fn erase_region(&self, region_number: usize) -> Result<(), ErrorCode> {
142//! // TODO: Erase the specified flash region
143//! Ok(())
144//! }
145//! }
146//!
147//! let mut read_buf: [u8; 1024] = [0; 1024];
148//!
149//! let mut hash_function = DefaultHasher::new();
150//! MAIN_KEY.hash(&mut hash_function);
151//!
152//! let tickv = TicKV::<FlashCtrl, 1024>::new(FlashCtrl::new(),
153//! &mut read_buf, 0x1000);
154//! tickv
155//! .initialise(hash_function.finish())
156//! .unwrap();
157//!
158//! // Add a key
159//! let value: [u8; 32] = [0x23; 32];
160//! tickv.append_key(get_hashed_key(b"ONE"), &value).unwrap();
161//!
162//! // Get the same key back
163//! let mut buf: [u8; 32] = [0; 32];
164//! tickv.get_key(get_hashed_key(b"ONE"), &mut buf).unwrap();
165//! ```
166//!
167//! You can then use the `get_key()` function to get the key back from flash.
168//!
169//! # Collisions
170//!
171//! TicKV will prevent a new key/value pair with a colliding hash of the key to be
172//! added. The collision will be reported to the user with the `KeyAlreadyExists`
173//! `ErroCode`.
174//!
175//! # Power loss protection
176//!
177//! TicKV ensures that in the event of a power loss, no stored data is lost or
178//! corrupted. The only data that can be lost in the event of a power loss is
179//! the data currently being written (if it hasn't been write to flash yet).
180//!
181//! If a power loss occurs after calling `append_key()` or `invalidate_key()`
182//! before it has completed then the operation probably did not complete and
183//! that data is lost.
184//!
185//! To help reduce this time to be as short as possible the `FlashController`
186//! is synchronous. Although flash writes can take a considerable amount of time
187//! and this will stall the application, this still seems like a good idea
188//! to avoid loosing data.
189//!
190//! # Security
191//!
192//! TicKV uses check sums to check data integrity. TicKV does not have any measures
193//! to prevent malicious manipulation or privacy. An attacker with access to the
194//! flash can change the values without being detected. An attacked with access
195//! to flash can also read all of the information. Any privacy, security or
196//! authentication measures need to be layered on top of TicKV.
197//!
198//! ## Versions
199//!
200//! TicKV stores the version when adding objects to the flash storage.
201//!
202//! TicKV is currently version 0.
203//!
204//! * Version 0
205//! * Version 0 is a draft version. It should NOT be used for important data!
206//! Version 0 maintains no backwards compatible support and could change at
207//! any time.
208//!
209
210#![no_std]
211#![deny(unsafe_code)]
212#![deny(missing_docs)]
213
214pub mod async_ops;
215pub mod crc32;
216pub mod error_codes;
217pub mod flash_controller;
218pub mod success_codes;
219pub mod tickv;
220
221// Use this to generate nicer docs
222#[doc(inline)]
223pub use crate::async_ops::AsyncTicKV;
224#[doc(inline)]
225pub use crate::error_codes::ErrorCode;
226#[doc(inline)]
227pub use crate::flash_controller::FlashController;
228#[doc(inline)]
229pub use crate::tickv::TicKV;
230pub use crate::tickv::MAIN_KEY;
231
232// This is used to run the tests on a host
233#[cfg(test)]
234#[macro_use]
235extern crate std;
236
237#[cfg(test)]
238mod tests;