1
1
extern crate lightning;
2
2
extern crate bitcoin;
3
+ extern crate libc;
3
4
4
5
use lightning:: ln:: data_persister:: ChannelDataPersister ;
5
6
use lightning:: chain:: channelmonitor:: { ChannelMonitor , ChannelMonitorUpdate , ChannelMonitorUpdateErr } ;
@@ -14,6 +15,9 @@ use std::io::{Error, ErrorKind, Cursor};
14
15
use std:: collections:: HashMap ;
15
16
use std:: marker:: PhantomData ;
16
17
18
+ #[ cfg( not( target_os = "windows" ) ) ]
19
+ use std:: os:: unix:: io:: AsRawFd ;
20
+
17
21
/// FilesystemPersister can persist channel data on disk on Linux machines, where
18
22
/// each channel's data is stored in a file named after its funding outpoint.
19
23
///
@@ -42,8 +46,8 @@ impl<ChanSigner: ChannelKeys + Readable + Writeable> FilesystemPersister<ChanSig
42
46
}
43
47
44
48
fn get_full_filepath ( & self , funding_txo : OutPoint ) -> String {
45
- let path = Path :: new ( & self . path_to_channel_data ) ;
46
- let mut path_buf = path . to_path_buf ( ) ;
49
+ let dir_path = Path :: new ( & self . path_to_channel_data ) ;
50
+ let mut path_buf = dir_path . to_path_buf ( ) ;
47
51
path_buf. push ( format ! ( "{}_{}" , funding_txo. txid. to_hex( ) , funding_txo. index) ) ;
48
52
path_buf. to_str ( ) . unwrap ( ) . to_string ( )
49
53
}
@@ -55,10 +59,9 @@ impl<ChanSigner: ChannelKeys + Readable + Writeable> FilesystemPersister<ChanSig
55
59
fn write_channel_data ( & self , funding_txo : OutPoint , monitor : & ChannelMonitor < ChanSigner > ) -> std:: io:: Result < ( ) > {
56
60
// Do a crazy dance with lots of fsync()s to be overly cautious here...
57
61
// We never want to end up in a state where we've lost the old data, or end up using the
58
- // old data on power loss after we've returned
59
- // Note that this actually *isn't* enough (at least on Linux)! We need to fsync an fd with
60
- // the containing dir, but Rust doesn't let us do that directly, sadly. TODO: Fix this with
61
- // the libc crate!
62
+ // old data on power loss after we've returned.
63
+ // The way to atomically write a file on Unix platforms is:
64
+ // open(tmpname), write(tmpfile), fsync(tmpfile), close(tmpfile), fsync(dir), rename(), fsync(dir)
62
65
let filename = self . get_full_filepath ( funding_txo) ;
63
66
let tmp_filename = filename. clone ( ) + ".tmp" ;
64
67
@@ -88,13 +91,17 @@ impl<ChanSigner: ChannelKeys + Readable + Writeable> FilesystemPersister<ChanSig
88
91
}
89
92
}
90
93
fs:: rename ( & tmp_filename, & filename) ?;
91
- {
92
- let f = fs:: OpenOptions :: new ( ) . write ( true ) . open ( & filename) ?;
93
- f. sync_all ( ) ?;
94
- }
95
94
if need_bk {
96
95
fs:: remove_file ( & bk_filename) ?;
97
96
}
97
+ // Fsync the parent directory on Unix.
98
+ #[ cfg( not( target_os = "windows" ) ) ]
99
+ {
100
+ let path_str = filename. clone ( ) ;
101
+ let path = Path :: new ( & path_str) . parent ( ) . unwrap ( ) ;
102
+ let dir_file = fs:: OpenOptions :: new ( ) . read ( true ) . open ( path) ?;
103
+ unsafe { libc:: fsync ( dir_file. as_raw_fd ( ) ) ; }
104
+ }
98
105
Ok ( ( ) )
99
106
}
100
107
}
0 commit comments