forked from lightningdevkit/rust-lightning
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfs_store.rs
More file actions
184 lines (155 loc) · 4.7 KB
/
fs_store.rs
File metadata and controls
184 lines (155 loc) · 4.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
use core::hash::{BuildHasher, Hasher};
use lightning::util::persist::{KVStore, KVStoreSync};
use lightning_persister::fs_store::FilesystemStore;
use std::fs;
use tokio::runtime::Runtime;
use crate::utils::test_logger;
struct TempFilesystemStore {
temp_path: std::path::PathBuf,
inner: FilesystemStore,
}
impl TempFilesystemStore {
fn new() -> Self {
const SHM_PATH: &str = "/dev/shm";
let mut temp_path = if std::path::Path::new(SHM_PATH).exists() {
std::path::PathBuf::from(SHM_PATH)
} else {
std::env::temp_dir()
};
let random_number = std::collections::hash_map::RandomState::new().build_hasher().finish();
let random_folder_name = format!("fs_store_fuzz_{:016x}", random_number);
temp_path.push(random_folder_name);
let inner = FilesystemStore::new(temp_path.clone());
TempFilesystemStore { inner, temp_path }
}
}
impl Drop for TempFilesystemStore {
fn drop(&mut self) {
_ = fs::remove_dir_all(&self.temp_path)
}
}
/// Actual fuzz test, method signature and name are fixed
fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
let rt = Runtime::new().unwrap();
rt.block_on(do_test_internal(data, out));
}
async fn do_test_internal<Out: test_logger::Output>(data: &[u8], _out: Out) {
let mut read_pos = 0;
macro_rules! get_slice {
($len: expr) => {{
let slice_len = $len as usize;
if data.len() < read_pos + slice_len {
None
} else {
read_pos += slice_len;
Some(&data[read_pos - slice_len..read_pos])
}
}};
}
let temp_fs_store = TempFilesystemStore::new();
let fs_store = &temp_fs_store.inner;
let primary_namespace = "primary";
let secondary_namespace = "secondary";
let key = "key";
let mut next_data_value = 0u64;
let mut get_next_data_value = || {
let data_value = next_data_value.to_be_bytes().to_vec();
next_data_value += 1;
data_value
};
let mut current_data = None;
let mut handles = Vec::new();
loop {
let v = match get_slice!(1) {
Some(b) => b[0],
None => break,
};
match v % 13 {
// Sync write
0 => {
let data_value = get_next_data_value();
KVStoreSync::write(
fs_store,
primary_namespace,
secondary_namespace,
key,
data_value.clone(),
)
.unwrap();
current_data = Some(data_value);
},
// Sync remove
1 => {
KVStoreSync::remove(fs_store, primary_namespace, secondary_namespace, key, false)
.unwrap();
current_data = None;
},
// Sync list
2 => {
KVStoreSync::list(fs_store, primary_namespace, secondary_namespace).unwrap();
},
// Sync read
3 => {
_ = KVStoreSync::read(fs_store, primary_namespace, secondary_namespace, key);
},
// Async write. Bias writes a bit.
4..=9 => {
let data_value = get_next_data_value();
let fut = KVStore::write(
fs_store,
primary_namespace,
secondary_namespace,
key,
data_value.clone(),
);
// Already set the current_data, even though writing hasn't finished yet. This supports the call-time
// ordering semantics.
current_data = Some(data_value);
let handle = tokio::task::spawn(fut);
// Store the handle to later await the result.
handles.push(handle);
},
// Async remove
10 | 11 => {
let lazy = v == 10;
let fut =
KVStore::remove(fs_store, primary_namespace, secondary_namespace, key, lazy);
// Already set the current_data, even though writing hasn't finished yet. This supports the call-time
// ordering semantics.
current_data = None;
let handle = tokio::task::spawn(fut);
handles.push(handle);
},
// Join tasks.
12 => {
for handle in handles.drain(..) {
let _ = handle.await.unwrap();
}
},
_ => unreachable!(),
}
// If no more writes are pending, we can reliably see if the data is consistent.
if handles.is_empty() {
let data_value =
KVStoreSync::read(fs_store, primary_namespace, secondary_namespace, key).ok();
assert_eq!(data_value, current_data);
let list = KVStoreSync::list(fs_store, primary_namespace, secondary_namespace).unwrap();
assert_eq!(list.is_empty(), current_data.is_none());
assert_eq!(0, fs_store.state_size());
}
}
// Always make sure that all async tasks are completed before returning. Otherwise the temporary storage dir could
// be removed, and then again recreated by unfinished tasks.
for handle in handles.drain(..) {
let _ = handle.await.unwrap();
}
}
/// Method that needs to be added manually, {name}_test
pub fn fs_store_test<Out: test_logger::Output>(data: &[u8], out: Out) {
do_test(data, out);
}
/// Method that needs to be added manually, {name}_run
#[no_mangle]
pub extern "C" fn fs_store_run(data: *const u8, datalen: usize) {
do_test(unsafe { std::slice::from_raw_parts(data, datalen) }, test_logger::DevNull {});
}