diff --git a/common/gossip_store.c b/common/gossip_store.c index 363d74980..63bad8a91 100644 --- a/common/gossip_store.c +++ b/common/gossip_store.c @@ -55,6 +55,23 @@ static bool timestamp_filter(const struct per_peer_state *pps, u32 timestamp) && timestamp <= pps->gs->timestamp_max; } +static void undo_read(int fd, int len, size_t wanted) +{ + if (len < 0) { + /* Grab errno before lseek overrides it */ + const char *err = strerror(errno); + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "gossip_store: failed read @%"PRIu64": %s", + (u64)lseek(fd, 0, SEEK_CUR), err); + } + + /* Shouldn't happen, but some filesystems are not as atomic as + * they should be! */ + status_unusual("gossip_store: short read %i of %zu @%"PRIu64, + len, wanted, (u64)lseek(fd, 0, SEEK_CUR) - len); + lseek(fd, -len, SEEK_CUR); +} + u8 *gossip_store_next(const tal_t *ctx, struct per_peer_state *pps) { u8 *msg = NULL; @@ -66,9 +83,13 @@ u8 *gossip_store_next(const tal_t *ctx, struct per_peer_state *pps) while (!msg) { struct gossip_hdr hdr; u32 msglen, checksum, timestamp; - int type; + int type, r; - if (read(pps->gossip_store_fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { + r = read(pps->gossip_store_fd, &hdr, sizeof(hdr)); + if (r != sizeof(hdr)) { + /* We expect a 0 read here at EOF */ + if (r != 0) + undo_read(pps->gossip_store_fd, r, sizeof(hdr)); per_peer_state_reset_gossip_timer(pps); return NULL; } @@ -86,13 +107,12 @@ u8 *gossip_store_next(const tal_t *ctx, struct per_peer_state *pps) checksum = be32_to_cpu(hdr.crc); timestamp = be32_to_cpu(hdr.timestamp); msg = tal_arr(ctx, u8, msglen); - if (read(pps->gossip_store_fd, msg, msglen) != msglen) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "gossip_store: can't read len %u" - " ~offset %"PRIi64, - msglen, - (s64)lseek(pps->gossip_store_fd, - 0, SEEK_CUR)); + r = read(pps->gossip_store_fd, msg, msglen); + if (r != msglen) { + undo_read(pps->gossip_store_fd, r, msglen); + per_peer_state_reset_gossip_timer(pps); + return NULL; + } if (checksum != crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)) status_failed(STATUS_FAIL_INTERNAL_ERROR,