Skip to content

Commit f8edd1b

Browse files
Make writes atomic on *nix platforms.
And remove an unnecessary fsync on the backup file after rename.
1 parent 6d40ecf commit f8edd1b

File tree

2 files changed

+18
-10
lines changed

2 files changed

+18
-10
lines changed

lightning-data-persister/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Utilities to manage channel data persistence and retrieval.
1010
[dependencies]
1111
bitcoin = "0.23"
1212
lightning = { version = "0.0.11", path = "../lightning" }
13+
libc = "0.2"
1314

1415
[dev-dependencies.bitcoin]
1516
version = "0.23"

lightning-data-persister/src/lib.rs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
extern crate lightning;
22
extern crate bitcoin;
3+
extern crate libc;
34

45
use lightning::ln::data_persister::ChannelDataPersister;
56
use lightning::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateErr};
@@ -14,6 +15,9 @@ use std::io::{Error, ErrorKind, Cursor};
1415
use std::collections::HashMap;
1516
use std::marker::PhantomData;
1617

18+
#[cfg(not(target_os = "windows"))]
19+
use std::os::unix::io::AsRawFd;
20+
1721
/// FilesystemPersister can persist channel data on disk on Linux machines, where
1822
/// each channel's data is stored in a file named after its funding outpoint.
1923
///
@@ -42,8 +46,8 @@ impl<ChanSigner: ChannelKeys + Readable + Writeable> FilesystemPersister<ChanSig
4246
}
4347

4448
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();
4751
path_buf.push(format!("{}_{}", funding_txo.txid.to_hex(), funding_txo.index));
4852
path_buf.to_str().unwrap().to_string()
4953
}
@@ -55,10 +59,9 @@ impl<ChanSigner: ChannelKeys + Readable + Writeable> FilesystemPersister<ChanSig
5559
fn write_channel_data(&self, funding_txo: OutPoint, monitor: &ChannelMonitor<ChanSigner>) -> std::io::Result<()> {
5660
// Do a crazy dance with lots of fsync()s to be overly cautious here...
5761
// 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)
6265
let filename = self.get_full_filepath(funding_txo);
6366
let tmp_filename = filename.clone() + ".tmp";
6467

@@ -88,13 +91,17 @@ impl<ChanSigner: ChannelKeys + Readable + Writeable> FilesystemPersister<ChanSig
8891
}
8992
}
9093
fs::rename(&tmp_filename, &filename)?;
91-
{
92-
let f = fs::OpenOptions::new().write(true).open(&filename)?;
93-
f.sync_all()?;
94-
}
9594
if need_bk {
9695
fs::remove_file(&bk_filename)?;
9796
}
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+
}
98105
Ok(())
99106
}
100107
}

0 commit comments

Comments
 (0)