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