gossip: add COMPLETED bit to mark records which are complete.

This should detect partial writes more robustly, since we make a
separate pwrite() call to update this flag after the record is written.

Previously we were playing a bit loose with synchronization assumptions,
which seemed to work on Linux ext4, but not so well elsewhere.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2025-09-29 01:02:07 +09:30
parent 7aec8cec53
commit 9fb8870f92
5 changed files with 26 additions and 8 deletions

View File

@@ -15,7 +15,7 @@ struct gossip_rcvd_filter;
/* First byte of file is the version.
*
* Top three bits mean incompatible change.
* As of this writing, major == 0, minor == 13.
* As of this writing, major == 0, minor == 15.
*/
#define GOSSIP_STORE_MAJOR_VERSION_MASK 0xE0
#define GOSSIP_STORE_MINOR_VERSION_MASK 0x1F
@@ -29,6 +29,11 @@ struct gossip_rcvd_filter;
*/
#define GOSSIP_STORE_DELETED_BIT 0x8000U
/**
* Bit of flags indicating record has been written.
*/
#define GOSSIP_STORE_COMPLETED_BIT 0x2000U
/**
* Bit of flags used to mark a channel announcement closed (not deleted for 12 blocks)
*/

View File

@@ -16,6 +16,7 @@ GOSSIP_STORE_MAJOR_VERSION = (0 << 5)
GOSSIP_STORE_MAJOR_VERSION_MASK = 0xE0
GOSSIP_STORE_LEN_DELETED_BIT = 0x8000
GOSSIP_STORE_LEN_PUSH_BIT = 0x4000
GOSSIP_STORE_LEN_COMPLETE_BIT = 0x2000
# These duplicate constants in lightning/gossipd/gossip_store_wiregen.h
WIRE_GOSSIP_STORE_PRIVATE_CHANNEL = 4104

View File

@@ -47,7 +47,8 @@ static void write_outmsg(int outfd, const u8 *outmsg, u32 timestamp)
{
struct gossip_hdr hdr;
hdr.len = cpu_to_be32(tal_count(outmsg));
hdr.flags = CPU_TO_BE16(GOSSIP_STORE_COMPLETED_BIT);
hdr.len = cpu_to_be16(tal_count(outmsg));
hdr.crc = cpu_to_be32(crc32c(timestamp, outmsg, tal_count(outmsg)));
hdr.timestamp = cpu_to_be32(timestamp);

View File

@@ -11,7 +11,7 @@
/* Current versions we support */
#define GSTORE_MAJOR 0
#define GSTORE_MINOR 14
#define GSTORE_MINOR 15
int main(int argc, char *argv[])
{

View File

@@ -23,7 +23,7 @@
#define GOSSIP_STORE_TEMP_FILENAME "gossip_store.tmp"
/* We write it as major version 0, minor version 14 */
#define GOSSIP_STORE_VER ((0 << 5) | 14)
#define GOSSIP_STORE_VER ((0 << 5) | 15)
struct gossip_store {
/* Back pointer. */
@@ -66,6 +66,7 @@ static bool append_msg(int fd, const u8 *msg, u32 timestamp, u64 *len)
struct gossip_hdr hdr;
u32 msglen;
struct iovec iov[2];
const u8 complete_byte = (GOSSIP_STORE_COMPLETED_BIT >> 8);
/* Don't ever overwrite the version header! */
assert(*len);
@@ -88,6 +89,11 @@ static bool append_msg(int fd, const u8 *msg, u32 timestamp, u64 *len)
iov[1].iov_len = msglen;
if (gossip_pwritev(fd, iov, ARRAY_SIZE(iov), *len) != sizeof(hdr) + msglen)
return false;
/* Update the hdr with the complete bit as a single-byte write */
if (pwrite(fd, &complete_byte, 1, *len) != 1)
return false;
*len += sizeof(hdr) + msglen;
return true;
}
@@ -98,10 +104,11 @@ static bool append_msg(int fd, const u8 *msg, u32 timestamp, u64 *len)
* v12 added the zombie flag for expired channel updates
* v13 removed private gossip entries
* v14 removed zombie and spam flags
* v15 added the complete flag
*/
static bool can_upgrade(u8 oldversion)
{
return oldversion >= 9 && oldversion <= 13;
return oldversion >= 9 && oldversion <= 14;
}
/* On upgrade, do best effort on private channels: hand them to
@@ -155,7 +162,7 @@ static void give_lightningd_canned_private_update(struct daemon *daemon,
static bool upgrade_field(u8 oldversion,
struct daemon *daemon,
u16 hdr_flags,
be16 *hdr_flags,
u8 **msg)
{
int type = fromwire_peektype(*msg);
@@ -179,10 +186,14 @@ static bool upgrade_field(u8 oldversion,
}
if (oldversion <= 13) {
/* Discard any zombies */
if (hdr_flags & GOSSIP_STORE_ZOMBIE_BIT_V13) {
if (be16_to_cpu(*hdr_flags) & GOSSIP_STORE_ZOMBIE_BIT_V13) {
*msg = tal_free(*msg);
}
}
if (oldversion <= 14) {
/* Add completed field */
*hdr_flags |= CPU_TO_BE16(GOSSIP_STORE_COMPLETED_BIT);
}
return true;
}
@@ -285,7 +296,7 @@ static int gossip_store_compact(struct daemon *daemon,
if (oldversion != version) {
if (!upgrade_field(oldversion, daemon,
be16_to_cpu(hdr.flags), &msg)) {
&hdr.flags, &msg)) {
tal_free(msg);
bad = "upgrade of store failed";
goto badmsg;