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
|
`PROTOCOL_VERSION` (printed by `fastsync --version`); the client and server must
|
||||||
run the same version because the handshake is strict.
|
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
|
## [2.19.0] - 2026-09-12
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
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_EXPORT_COMPILE_COMMANDS ON)
|
||||||
set(CMAKE_C_STANDARD 11)
|
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
|
`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
|
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
|
a lone `int32` zero. The encoded field layout is unchanged (only the framing
|
||||||
collapses), so chunk-serialized blobs remain byte-identical; `PROTOCOL_VERSION`
|
collapses), so chunk-serialized blobs remain byte-identical. Protocol data is an
|
||||||
was bumped `2.19.0 → 2.20.0` because a 2.19 peer would desynchronize on the
|
unframed byte stream, so the packed encoding is byte-for-byte identical to the
|
||||||
removed frames. The strict same-version handshake rejects any mismatch before a
|
old field-by-field writes; `PROTOCOL_VERSION` was bumped `2.19.0 → 2.20.0` as a
|
||||||
byte of the frame is parsed.
|
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
|
### Recommended Delivery Order
|
||||||
|
|
||||||
|
|||||||
+12
-13
@@ -646,19 +646,18 @@ typedef struct Config {
|
|||||||
*
|
*
|
||||||
* Packed Metadata Wave: 2.19.0 -> 2.20.0.
|
* Packed Metadata Wave: 2.19.0 -> 2.20.0.
|
||||||
*
|
*
|
||||||
* WHY the bump, grounded in the wire: metadata_send()/metadata_receive() no
|
* WHY the bump: metadata_send()/metadata_receive() no longer emit/consume the
|
||||||
* longer emit/consume the metadata as up to 12 separate per-field framed
|
* metadata as up to 12 separate per-field writes. A file's metadata now
|
||||||
* writes. A file's metadata now crosses the wire as ONE packed frame: a
|
* crosses the wire as ONE packed frame: a single int32 present flag (0 =
|
||||||
* single int32 present flag (0 = absent, 1 = present) followed, when present,
|
* absent, 1 = present) followed, when present, by the fixed
|
||||||
* by the fixed FILE_METADATA_WIRE_SIZE-byte (68-byte) field record produced by
|
* FILE_METADATA_WIRE_SIZE-byte (68-byte) field record produced by
|
||||||
* metadata_to_buf(). A 2.19 peer would desynchronize on the removed frames
|
* metadata_to_buf(). Protocol data is an unframed byte stream, so the packed
|
||||||
* (it would read the packed record's bytes as a stream of separate field
|
* encoding is byte-for-byte identical to the old field-by-field writes (same
|
||||||
* frames), so the strict same-version handshake (config_receive rejects a
|
* fields, same order, same widths); the change only removes per-field syscalls.
|
||||||
* mismatched version before parsing anything else) is what keeps a 2.20 client
|
* The bump is therefore a deliberate lockstep-release marker, not a
|
||||||
* and a 2.19 server from ever reaching that state. The encoded field layout
|
* desynchronization fix — the strict same-version handshake still rejects a
|
||||||
* itself is unchanged (only its framing collapses), so the chunk codec, which
|
* mixed 2.19/2.20 deployment. The chunk codec, which already used the packed
|
||||||
* already used the packed metadata_to_buf()/metadata_from_buf() codec, is
|
* metadata_to_buf()/metadata_from_buf() form, is unchanged. */
|
||||||
* byte-identical to before. */
|
|
||||||
#define PROTOCOL_VERSION "2.20.0"
|
#define PROTOCOL_VERSION "2.20.0"
|
||||||
#define DEFAULT_CHUNK_SIZE (10 * 1024 * 1024)
|
#define DEFAULT_CHUNK_SIZE (10 * 1024 * 1024)
|
||||||
/* Upper bound on total basis-dir entries (rsync caps --link-dest at 20). */
|
/* 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;
|
return false;
|
||||||
if (!str_hash_set_grow(set))
|
if (!str_hash_set_grow(set))
|
||||||
return false;
|
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,
|
static const StrHashSetSlot* str_hash_set_find_n(const StrHashSet* set, const char* key,
|
||||||
|
|||||||
Reference in New Issue
Block a user