1
+ pub ( crate ) mod utils;
2
+
1
3
extern crate lightning;
2
4
extern crate bitcoin;
3
5
extern crate libc;
@@ -10,7 +12,7 @@ use lightning::chain::transaction::OutPoint;
10
12
use lightning:: util:: ser:: { Writeable , Readable } ;
11
13
use std:: fs;
12
14
use std:: io:: Error ;
13
- use std :: path :: { Path , PathBuf } ;
15
+ use crate :: utils :: DiskWriteable ;
14
16
15
17
#[ cfg( test) ]
16
18
use {
20
22
std:: io:: Cursor
21
23
} ;
22
24
23
- #[ cfg( not( target_os = "windows" ) ) ]
24
- use std:: os:: unix:: io:: AsRawFd ;
25
-
26
25
/// FilesystemPersister persists channel data on disk, where each channel's
27
26
/// data is stored in a file named after its funding outpoint.
28
27
///
@@ -39,12 +38,8 @@ pub struct FilesystemPersister {
39
38
path_to_channel_data : String ,
40
39
}
41
40
42
- trait DiskWriteable {
43
- fn write ( & self , writer : & mut fs:: File ) -> Result < ( ) , Error > ;
44
- }
45
-
46
41
impl < ChanSigner : ChannelKeys + Writeable > DiskWriteable for ChannelMonitor < ChanSigner > {
47
- fn write ( & self , writer : & mut fs:: File ) -> Result < ( ) , Error > {
42
+ fn write_to_file ( & self , writer : & mut fs:: File ) -> Result < ( ) , Error > {
48
43
self . serialize_for_disk ( writer)
49
44
}
50
45
}
@@ -58,41 +53,6 @@ impl FilesystemPersister {
58
53
}
59
54
}
60
55
61
- fn get_full_filepath ( & self , funding_txo : OutPoint ) -> String {
62
- let mut path = PathBuf :: from ( & self . path_to_channel_data ) ;
63
- path. push ( format ! ( "{}_{}" , funding_txo. txid. to_hex( ) , funding_txo. index) ) ;
64
- path. to_str ( ) . unwrap ( ) . to_string ( )
65
- }
66
-
67
- // Utility to write a file to disk.
68
- fn write_channel_data ( & self , funding_txo : OutPoint , monitor : & dyn DiskWriteable ) -> std:: io:: Result < ( ) > {
69
- fs:: create_dir_all ( & self . path_to_channel_data ) ?;
70
- // Do a crazy dance with lots of fsync()s to be overly cautious here...
71
- // We never want to end up in a state where we've lost the old data, or end up using the
72
- // old data on power loss after we've returned.
73
- // The way to atomically write a file on Unix platforms is:
74
- // open(tmpname), write(tmpfile), fsync(tmpfile), close(tmpfile), rename(), fsync(dir)
75
- let filename = self . get_full_filepath ( funding_txo) ;
76
- let tmp_filename = format ! ( "{}.tmp" , filename. clone( ) ) ;
77
-
78
- {
79
- // Note that going by rust-lang/rust@d602a6b, on MacOS it is only safe to use
80
- // rust stdlib 1.36 or higher.
81
- let mut f = fs:: File :: create ( & tmp_filename) ?;
82
- monitor. write ( & mut f) ?;
83
- f. sync_all ( ) ?;
84
- }
85
- fs:: rename ( & tmp_filename, & filename) ?;
86
- // Fsync the parent directory on Unix.
87
- #[ cfg( not( target_os = "windows" ) ) ]
88
- {
89
- let path = Path :: new ( & filename) . parent ( ) . unwrap ( ) ;
90
- let dir_file = fs:: OpenOptions :: new ( ) . read ( true ) . open ( path) ?;
91
- unsafe { libc:: fsync ( dir_file. as_raw_fd ( ) ) ; }
92
- }
93
- Ok ( ( ) )
94
- }
95
-
96
56
#[ cfg( test) ]
97
57
fn load_channel_data < ChanSigner : ChannelKeys + Readable + Writeable > ( & self ) ->
98
58
Result < HashMap < OutPoint , ChannelMonitor < ChanSigner > > , ChannelMonitorUpdateErr > {
@@ -130,28 +90,18 @@ impl FilesystemPersister {
130
90
131
91
impl < ChanSigner : ChannelKeys + Readable + Writeable + Send + Sync > channelmonitor:: Persist < ChanSigner > for FilesystemPersister {
132
92
fn persist_new_channel ( & self , funding_txo : OutPoint , monitor : & ChannelMonitor < ChanSigner > ) -> Result < ( ) , ChannelMonitorUpdateErr > {
133
- self . write_channel_data ( funding_txo, monitor)
93
+ let filename = format ! ( "{}_{}" , funding_txo. txid. to_hex( ) , funding_txo. index) ;
94
+ utils:: write_to_file ( self . path_to_channel_data . clone ( ) , filename, monitor)
134
95
. map_err ( |_| ChannelMonitorUpdateErr :: PermanentFailure )
135
96
}
136
97
137
98
fn update_persisted_channel ( & self , funding_txo : OutPoint , _update : & ChannelMonitorUpdate , monitor : & ChannelMonitor < ChanSigner > ) -> Result < ( ) , ChannelMonitorUpdateErr > {
138
- self . write_channel_data ( funding_txo, monitor)
99
+ let filename = format ! ( "{}_{}" , funding_txo. txid. to_hex( ) , funding_txo. index) ;
100
+ utils:: write_to_file ( self . path_to_channel_data . clone ( ) , filename, monitor)
139
101
. map_err ( |_| ChannelMonitorUpdateErr :: PermanentFailure )
140
102
}
141
103
}
142
104
143
- #[ cfg( test) ]
144
- impl Drop for FilesystemPersister {
145
- fn drop ( & mut self ) {
146
- // We test for invalid directory names, so it's OK if directory removal
147
- // fails.
148
- match fs:: remove_dir_all ( & self . path_to_channel_data ) {
149
- Err ( e) => println ! ( "Failed to remove test persister directory: {}" , e) ,
150
- _ => { }
151
- }
152
- }
153
- }
154
-
155
105
#[ cfg( test) ]
156
106
mod tests {
157
107
extern crate lightning;
@@ -160,8 +110,6 @@ mod tests {
160
110
use bitcoin:: blockdata:: block:: { Block , BlockHeader } ;
161
111
use bitcoin:: hashes:: hex:: FromHex ;
162
112
use bitcoin:: Txid ;
163
- use DiskWriteable ;
164
- use Error ;
165
113
use lightning:: chain:: channelmonitor:: { Persist , ChannelMonitorUpdateErr } ;
166
114
use lightning:: chain:: transaction:: OutPoint ;
167
115
use lightning:: { check_closed_broadcast, check_added_monitors} ;
@@ -170,20 +118,22 @@ mod tests {
170
118
use lightning:: ln:: msgs:: ErrorAction ;
171
119
use lightning:: util:: enforcing_trait_impls:: EnforcingChannelKeys ;
172
120
use lightning:: util:: events:: { MessageSendEventsProvider , MessageSendEvent } ;
173
- use lightning:: util:: ser:: Writer ;
174
121
use lightning:: util:: test_utils;
175
122
use std:: fs;
176
- use std:: io;
177
123
#[ cfg( target_os = "windows" ) ]
178
124
use {
179
125
lightning:: get_event_msg,
180
126
lightning:: ln:: msgs:: ChannelMessageHandler ,
181
127
} ;
182
128
183
- struct TestWriteable { }
184
- impl DiskWriteable for TestWriteable {
185
- fn write ( & self , writer : & mut fs:: File ) -> Result < ( ) , Error > {
186
- writer. write_all ( & [ 42 ; 1 ] )
129
+ impl Drop for FilesystemPersister {
130
+ fn drop ( & mut self ) {
131
+ // We test for invalid directory names, so it's OK if directory removal
132
+ // fails.
133
+ match fs:: remove_dir_all ( & self . path_to_channel_data ) {
134
+ Err ( e) => println ! ( "Failed to remove test persister directory: {}" , e) ,
135
+ _ => { }
136
+ }
187
137
}
188
138
}
189
139
@@ -255,88 +205,6 @@ mod tests {
255
205
check_persisted_data ! ( 11 ) ;
256
206
}
257
207
258
- // Test that if the persister's path to channel data is read-only, writing
259
- // data to it fails. Windows ignores the read-only flag for folders, so this
260
- // test is Unix-only.
261
- #[ cfg( not( target_os = "windows" ) ) ]
262
- #[ test]
263
- fn test_readonly_dir ( ) {
264
- let persister = FilesystemPersister :: new ( "test_readonly_dir_persister" . to_string ( ) ) ;
265
- let test_writeable = TestWriteable { } ;
266
- let test_txo = OutPoint {
267
- txid : Txid :: from_hex ( "8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be" ) . unwrap ( ) ,
268
- index : 0
269
- } ;
270
- // Create the persister's directory and set it to read-only.
271
- let path = & persister. path_to_channel_data ;
272
- fs:: create_dir_all ( path) . unwrap ( ) ;
273
- let mut perms = fs:: metadata ( path) . unwrap ( ) . permissions ( ) ;
274
- perms. set_readonly ( true ) ;
275
- fs:: set_permissions ( path, perms) . unwrap ( ) ;
276
- match persister. write_channel_data ( test_txo, & test_writeable) {
277
- Err ( e) => assert_eq ! ( e. kind( ) , io:: ErrorKind :: PermissionDenied ) ,
278
- _ => panic ! ( "Unexpected error message" )
279
- }
280
- }
281
-
282
- // Test failure to rename in the process of atomically creating a channel
283
- // monitor's file. We induce this failure by making the `tmp` file a
284
- // directory.
285
- // Explanation: given "from" = the file being renamed, "to" = the
286
- // renamee that already exists: Windows should fail because it'll fail
287
- // whenever "to" is a directory, and Unix should fail because if "from" is a
288
- // file, then "to" is also required to be a file.
289
- #[ test]
290
- fn test_rename_failure ( ) {
291
- let persister = FilesystemPersister :: new ( "test_rename_failure" . to_string ( ) ) ;
292
- let test_writeable = TestWriteable { } ;
293
- let txid_hex = "8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be" ;
294
- let outp_idx = 0 ;
295
- let test_txo = OutPoint {
296
- txid : Txid :: from_hex ( txid_hex) . unwrap ( ) ,
297
- index : outp_idx,
298
- } ;
299
- // Create the channel data file and make it a directory.
300
- let path = & persister. path_to_channel_data ;
301
- fs:: create_dir_all ( format ! ( "{}/{}_{}" , path, txid_hex, outp_idx) ) . unwrap ( ) ;
302
- match persister. write_channel_data ( test_txo, & test_writeable) {
303
- Err ( e) => {
304
- #[ cfg( not( target_os = "windows" ) ) ]
305
- assert_eq ! ( e. kind( ) , io:: ErrorKind :: Other ) ;
306
- #[ cfg( target_os = "windows" ) ]
307
- assert_eq ! ( e. kind( ) , io:: ErrorKind :: PermissionDenied ) ;
308
- }
309
- _ => panic ! ( "Unexpected error message" )
310
- }
311
- }
312
-
313
- // Test failure to create the temporary file in the persistence process.
314
- // We induce this failure by having the temp file already exist and be a
315
- // directory.
316
- #[ test]
317
- fn test_tmp_file_creation_failure ( ) {
318
- let persister = FilesystemPersister :: new ( "test_tmp_file_creation_failure" . to_string ( ) ) ;
319
- let test_writeable = TestWriteable { } ;
320
- let txid_hex = "8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be" ;
321
- let outp_idx = 0 ;
322
- let test_txo = OutPoint {
323
- txid : Txid :: from_hex ( txid_hex) . unwrap ( ) ,
324
- index : outp_idx,
325
- } ;
326
- // Create the tmp file and make it a directory.
327
- let path = & persister. path_to_channel_data ;
328
- fs:: create_dir_all ( format ! ( "{}/{}_{}.tmp" , path, txid_hex, outp_idx) ) . unwrap ( ) ;
329
- match persister. write_channel_data ( test_txo, & test_writeable) {
330
- Err ( e) => {
331
- #[ cfg( not( target_os = "windows" ) ) ]
332
- assert_eq ! ( e. kind( ) , io:: ErrorKind :: Other ) ;
333
- #[ cfg( target_os = "windows" ) ]
334
- assert_eq ! ( e. kind( ) , io:: ErrorKind :: PermissionDenied ) ;
335
- }
336
- _ => panic ! ( "Unexpected error message" )
337
- }
338
- }
339
-
340
208
// Test that if the persister's path to channel data is read-only, writing a
341
209
// monitor to it results in the persister returning a PermanentFailure.
342
210
// Windows ignores the read-only flag for folders, so this test is Unix-only.
0 commit comments