preallocate: --preallocate allocates dest space up front
CI / lint (pull_request) Failing after 3s
CI / build-and-test (pull_request) Skipped
CI / sanitizers (address) (pull_request) Skipped
CI / sanitizers (undefined) (pull_request) Skipped
CI / fuzz-build (pull_request) Skipped
CI / coverage (pull_request) Skipped
CI / valgrind (pull_request) Skipped

Receiver allocates the destination file's full size before streaming data
(posix_fallocate preferred, ftruncate fallback on EOPNOTSUPP/ENOSYS) so an
out-of-space transfer fails fast instead of partway. Additive config bool
crossing the wire; PROTOCOL_VERSION 2.10.0 -> 2.11.0. Threaded through all
store paths (atomic, inplace, partial/delay-updates staging, link-dest copy
fallback). Review hardening: explicit lseek(0) before the data write so
correctness does not depend on posix_fallocate leaving the fd offset unchanged.
This commit is contained in:
2026-09-08 18:23:48 +02:00
parent 829e760086
commit 362a6a5488
13 changed files with 285 additions and 70 deletions
+45
View File
@@ -226,6 +226,51 @@ class TestChmod:
assert (os.stat(os.path.join(received, "small.txt")).st_mode & 0o777) == 0o644
class TestPreallocate:
"""--preallocate allocates the destination file space up front; the final
destination content must be byte-identical to a normal run."""
def test_preallocate_transfer_succeeds(self, shared_server):
source = os.path.join(TEST_DATA_DIR, "prealloc_source")
dest = os.path.join(TEST_DATA_DIR, "prealloc_dest")
clean_dir(source)
clean_dir(dest)
payload = os.urandom(2 * 1024 * 1024 + 137)
with open(os.path.join(source, "data.bin"), "wb") as f:
f.write(payload)
with open(os.path.join(source, "small.txt"), "wb") as f:
f.write(b"hello\n")
result, _ = run_client(source, dest, flags=["--preallocate"],
port=shared_server.port)
assert result.returncode == 0, \
f"--preallocate failed: {(result.stderr or result.stdout)[:400]}"
received_dir = os.path.join(dest, os.path.abspath(source).lstrip(os.sep))
data_path = os.path.join(received_dir, "data.bin")
assert os.path.isfile(data_path), f"destination file not created: {data_path}"
with open(data_path, "rb") as f:
assert f.read() == payload, "destination content mismatch"
small_path = os.path.join(received_dir, "small.txt")
with open(small_path, "rb") as f:
assert f.read() == b"hello\n", "small file content mismatch"
def test_preallocate_combines_with_partial(self, shared_server):
source = os.path.join(TEST_DATA_DIR, "prealloc_partial_src")
dest = os.path.join(TEST_DATA_DIR, "prealloc_partial_dst")
clean_dir(source)
clean_dir(dest)
with open(os.path.join(source, "f.txt"), "wb") as f:
f.write(b"partial + preallocate\n")
result, _ = run_client(source, dest, flags=["--preallocate", "--partial"],
port=shared_server.port)
assert result.returncode == 0, \
f"--preallocate --partial failed: {(result.stderr or result.stdout)[:400]}"
received_dir = os.path.join(dest, os.path.abspath(source).lstrip(os.sep))
with open(os.path.join(received_dir, "f.txt"), "rb") as f:
assert f.read() == b"partial + preallocate\n"
class TestCompressionChoice:
def test_zstd_choice_compresses(self, shared_server):
clean_dir(DEST_DIR)