Release v2.26.0 #284
@@ -4,6 +4,35 @@ All notable changes to FastSync are documented here. Versions match
|
||||
`PROTOCOL_VERSION` (printed by `fastsync --version`); the client and server must
|
||||
run the same version because the handshake is strict.
|
||||
|
||||
## [2.20.0] - 2026-09-13
|
||||
|
||||
### Security
|
||||
|
||||
- Cap cumulative `DirTimeList` growth and bound pre-auth config-string memory
|
||||
(remote memory-exhaustion DoS).
|
||||
- Daemon host access control (`hosts allow`/`hosts deny`, IPv4/IPv6/CIDR),
|
||||
configurable global `max connections`, connection audit logging, and a
|
||||
bounded `auth failure delay` throttle. IPv4-mapped peers are normalized and
|
||||
invalid patterns are rejected at parse time (no silent fail-open).
|
||||
- Honor `--timeout` for protocol I/O and bound idle/session time to defeat
|
||||
keepalive slowloris; child-safe signal handling in the forked daemon.
|
||||
- Compiler/linker hardening (`_FORTIFY_SOURCE`, stack protector, PIE, RELRO)
|
||||
and pinned build dependencies.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Use-after-free in the basis-dir oversize preflight.
|
||||
- Placeholder `Data` leaks, `missing_args` leak, scanner chunk leak.
|
||||
- Thread-safe logging; single fd owner and cleanup epilogue in the server
|
||||
handler.
|
||||
|
||||
### Performance
|
||||
|
||||
- Metadata now crosses the wire as one packed frame (protocol 2.20.0).
|
||||
- Delete keep-set and `--files-from` lookups indexed (O(n*m) → O(n)).
|
||||
- Reused per-thread zstd contexts; `TCP_NODELAY` by default.
|
||||
- Byte-bounded sender queues; removed a redundant scanner `stat()`.
|
||||
|
||||
## [2.19.0] - 2026-09-12
|
||||
|
||||
### Security
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
project(FastFileTransfer VERSION 2.19.0)
|
||||
project(FastFileTransfer VERSION 2.20.0)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
+6
-4
@@ -850,10 +850,12 @@ now sends the metadata as ONE packed frame: a single `int32` present flag
|
||||
`FILE_METADATA_WIRE_SIZE`-byte (68-byte) field record already emitted by the
|
||||
shared `metadata_to_buf()`/`metadata_from_buf()` chunk codec. Absent metadata is
|
||||
a lone `int32` zero. The encoded field layout is unchanged (only the framing
|
||||
collapses), so chunk-serialized blobs remain byte-identical; `PROTOCOL_VERSION`
|
||||
was bumped `2.19.0 → 2.20.0` because a 2.19 peer would desynchronize on the
|
||||
removed frames. The strict same-version handshake rejects any mismatch before a
|
||||
byte of the frame is parsed.
|
||||
collapses), so chunk-serialized blobs remain byte-identical. Protocol data is an
|
||||
unframed byte stream, so the packed encoding is byte-for-byte identical to the
|
||||
old field-by-field writes; `PROTOCOL_VERSION` was bumped `2.19.0 → 2.20.0` as a
|
||||
deliberate lockstep-release marker rather than because of a
|
||||
desynchronization. The strict same-version handshake rejects any mismatch before
|
||||
a byte of the frame is parsed.
|
||||
|
||||
### Recommended Delivery Order
|
||||
|
||||
|
||||
+12
-13
@@ -646,19 +646,18 @@ typedef struct Config {
|
||||
*
|
||||
* Packed Metadata Wave: 2.19.0 -> 2.20.0.
|
||||
*
|
||||
* WHY the bump, grounded in the wire: metadata_send()/metadata_receive() no
|
||||
* longer emit/consume the metadata as up to 12 separate per-field framed
|
||||
* writes. A file's metadata now crosses the wire as ONE packed frame: a
|
||||
* single int32 present flag (0 = absent, 1 = present) followed, when present,
|
||||
* by the fixed FILE_METADATA_WIRE_SIZE-byte (68-byte) field record produced by
|
||||
* metadata_to_buf(). A 2.19 peer would desynchronize on the removed frames
|
||||
* (it would read the packed record's bytes as a stream of separate field
|
||||
* frames), so the strict same-version handshake (config_receive rejects a
|
||||
* mismatched version before parsing anything else) is what keeps a 2.20 client
|
||||
* and a 2.19 server from ever reaching that state. The encoded field layout
|
||||
* itself is unchanged (only its framing collapses), so the chunk codec, which
|
||||
* already used the packed metadata_to_buf()/metadata_from_buf() codec, is
|
||||
* byte-identical to before. */
|
||||
* WHY the bump: metadata_send()/metadata_receive() no longer emit/consume the
|
||||
* metadata as up to 12 separate per-field writes. A file's metadata now
|
||||
* crosses the wire as ONE packed frame: a single int32 present flag (0 =
|
||||
* absent, 1 = present) followed, when present, by the fixed
|
||||
* FILE_METADATA_WIRE_SIZE-byte (68-byte) field record produced by
|
||||
* metadata_to_buf(). Protocol data is an unframed byte stream, so the packed
|
||||
* encoding is byte-for-byte identical to the old field-by-field writes (same
|
||||
* fields, same order, same widths); the change only removes per-field syscalls.
|
||||
* The bump is therefore a deliberate lockstep-release marker, not a
|
||||
* desynchronization fix — the strict same-version handshake still rejects a
|
||||
* mixed 2.19/2.20 deployment. The chunk codec, which already used the packed
|
||||
* metadata_to_buf()/metadata_from_buf() form, is unchanged. */
|
||||
#define PROTOCOL_VERSION "2.20.0"
|
||||
#define DEFAULT_CHUNK_SIZE (10 * 1024 * 1024)
|
||||
/* Upper bound on total basis-dir entries (rsync caps --link-dest at 20). */
|
||||
|
||||
+3
-1
@@ -175,7 +175,9 @@ bool str_hash_set_insert_ref(StrHashSet* set, const char* key) {
|
||||
return false;
|
||||
if (!str_hash_set_grow(set))
|
||||
return false;
|
||||
return str_hash_set_put(set, key, strlen(key)) >= 0;
|
||||
/* put() returns 1 for a new slot and 0 for a duplicate; both are success. */
|
||||
(void)str_hash_set_put(set, key, strlen(key));
|
||||
return true;
|
||||
}
|
||||
|
||||
static const StrHashSetSlot* str_hash_set_find_n(const StrHashSet* set, const char* key,
|
||||
|
||||
Reference in New Issue
Block a user