quality: refactors (#321-#326) + fake-super directories (#319) #329
+128
-183
@@ -47,106 +47,50 @@ bool receive_file_xattrs(File* file, int fd, const Config* config) {
|
|||||||
static bool receive_file_payload_into(File* file, int fd, const Config* config,
|
static bool receive_file_payload_into(File* file, int fd, const Config* config,
|
||||||
const char* dest_path, unsigned long long expected_size);
|
const char* dest_path, unsigned long long expected_size);
|
||||||
|
|
||||||
static File* receive_delta_file(int fd, const Config* config, const char* check_path,
|
/* Receive a STATUS_DELTA_DATA response: the sender's delta against the basis we
|
||||||
void* old_data, unsigned long long old_size,
|
signed. Deserializes, decompresses and applies the delta (in memory or
|
||||||
unsigned long long expected_size, int basis_fd, bool* failed) {
|
through a spool temp file), then receives the metadata/xattr block and
|
||||||
if (!old_data && basis_fd < 0) {
|
installs the reconstructed payload. Takes ownership of `old_data` and `sig`,
|
||||||
free(old_data); /* defensive: a basis source is always provided today */
|
releasing both on every path. */
|
||||||
*failed = true;
|
static File* receive_delta_data_branch(int fd, const Config* config, const char* check_path,
|
||||||
return NULL;
|
void* old_data, unsigned long long old_size, int basis_fd,
|
||||||
}
|
DeltaSignature* sig, bool* failed) {
|
||||||
|
Data* raw_delta = NULL;
|
||||||
|
Delta* delta = NULL;
|
||||||
|
void* new_data = NULL;
|
||||||
|
char* spool = NULL;
|
||||||
|
File* file = NULL;
|
||||||
|
|
||||||
/* The basis is either an in-memory snapshot (the destination file, bounded) or
|
raw_delta = receive_data_limited(fd, MAX_RECEIVE_WHOLE_FILE_SIZE);
|
||||||
* a confined descriptor (a --fuzzy sibling, possibly larger than memory) that
|
if (!raw_delta)
|
||||||
* is signed/applied in bounded chunks. */
|
goto fail;
|
||||||
DeltaSignature* sig =
|
|
||||||
old_data ? delta_signature_create_seeded(old_data, old_size, config->delta_block_size,
|
|
||||||
(uint32_t)config->checksum_seed)
|
|
||||||
: delta_signature_create_fd_seeded(basis_fd, old_size, config->delta_block_size,
|
|
||||||
(uint32_t)config->checksum_seed);
|
|
||||||
if (!sig) {
|
|
||||||
free(old_data);
|
|
||||||
*failed = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Data* sig_data = delta_signature_serialize(sig);
|
|
||||||
if (!sig_data) {
|
|
||||||
delta_signature_destroy(sig);
|
|
||||||
free(old_data);
|
|
||||||
*failed = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sig_sent = send_status(fd, STATUS_DELTA_SIGNATURE) && send_data(fd, sig_data);
|
|
||||||
data_destroy(sig_data);
|
|
||||||
|
|
||||||
if (!sig_sent) {
|
|
||||||
delta_signature_destroy(sig);
|
|
||||||
free(old_data);
|
|
||||||
*failed = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status resp;
|
|
||||||
if (!receive_status(fd, &resp)) {
|
|
||||||
delta_signature_destroy(sig);
|
|
||||||
free(old_data);
|
|
||||||
*failed = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resp == STATUS_DELTA_DATA) {
|
|
||||||
Data* delta_data = receive_data_limited(fd, MAX_RECEIVE_WHOLE_FILE_SIZE);
|
|
||||||
if (!delta_data) {
|
|
||||||
delta_signature_destroy(sig);
|
|
||||||
free(old_data);
|
|
||||||
*failed = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Data* raw_delta = delta_data;
|
|
||||||
if (config->use_compression &&
|
if (config->use_compression &&
|
||||||
!compression_should_skip_with_suffixes(
|
!compression_should_skip_with_suffixes(check_path, config->skip_compress_suffixes,
|
||||||
check_path, config->skip_compress_suffixes,
|
config->skip_compress_set ? config->skip_compress_count
|
||||||
config->skip_compress_set ? config->skip_compress_count : -1)) {
|
: -1)) {
|
||||||
ProtocolSession* owner = delta_data->owner;
|
ProtocolSession* owner = raw_delta->owner;
|
||||||
raw_delta = data_decompress_limited(delta_data, MAX_RECEIVE_WHOLE_FILE_SIZE);
|
Data* decompressed = data_decompress_limited(raw_delta, MAX_RECEIVE_WHOLE_FILE_SIZE);
|
||||||
data_destroy(delta_data);
|
data_destroy(raw_delta);
|
||||||
if (!raw_delta) {
|
raw_delta = decompressed;
|
||||||
free(old_data);
|
if (!raw_delta)
|
||||||
delta_signature_destroy(sig);
|
goto fail;
|
||||||
*failed = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
/* Charge the decompressed delta to the connection budget (the paired
|
/* Charge the decompressed delta to the connection budget (the paired
|
||||||
wire buffer's charge was just released). */
|
wire buffer's charge was just released). */
|
||||||
if (!data_charge_session(raw_delta, owner, raw_delta->size)) {
|
if (!data_charge_session(raw_delta, owner, raw_delta->size))
|
||||||
data_destroy(raw_delta);
|
goto fail;
|
||||||
free(old_data);
|
|
||||||
delta_signature_destroy(sig);
|
|
||||||
*failed = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Delta* delta = delta_deserialize(raw_delta);
|
delta = delta_deserialize(raw_delta);
|
||||||
data_destroy(raw_delta);
|
data_destroy(raw_delta);
|
||||||
if (!delta) {
|
raw_delta = NULL;
|
||||||
free(old_data);
|
if (!delta)
|
||||||
delta_signature_destroy(sig);
|
goto fail;
|
||||||
*failed = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t new_size = delta->new_file_size;
|
uint64_t new_size = delta->new_file_size;
|
||||||
if (new_size > SIZE_MAX) {
|
if (new_size > SIZE_MAX) {
|
||||||
delta_destroy(delta);
|
|
||||||
free(old_data);
|
|
||||||
delta_signature_destroy(sig);
|
|
||||||
send_status(fd, STATUS_ERROR);
|
send_status(fd, STATUS_ERROR);
|
||||||
*failed = true;
|
goto fail;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
/* Wire-stats tally: bytes taken straight from the basis file (matched
|
/* Wire-stats tally: bytes taken straight from the basis file (matched
|
||||||
delta blocks) and bytes shipped literally (protocol 2.28.0). Computed
|
delta blocks) and bytes shipped literally (protocol 2.28.0). Computed
|
||||||
@@ -163,115 +107,65 @@ static File* receive_delta_file(int fd, const Config* config, const char* check_
|
|||||||
/* A reconstructed file above the streaming bound is written into a spool
|
/* A reconstructed file above the streaming bound is written into a spool
|
||||||
temp file through delta_apply_to_fd; a smaller one keeps the historical
|
temp file through delta_apply_to_fd; a smaller one keeps the historical
|
||||||
in-memory reconstruction. */
|
in-memory reconstruction. */
|
||||||
void* new_data = NULL;
|
|
||||||
char* spool = NULL;
|
|
||||||
if (new_size > protocol_whole_file_receive_limit()) {
|
if (new_size > protocol_whole_file_receive_limit()) {
|
||||||
char* dest_path = path_cat(config->receive_root_directory, check_path);
|
char* dest_path = path_cat(config->receive_root_directory, check_path);
|
||||||
int spool_fd = dest_path ? file_spool_for_payload(dest_path, &spool) : -1;
|
int spool_fd = dest_path ? file_spool_for_payload(dest_path, &spool) : -1;
|
||||||
free(dest_path);
|
free(dest_path);
|
||||||
if (spool_fd < 0) {
|
if (spool_fd < 0) {
|
||||||
delta_destroy(delta);
|
|
||||||
free(old_data);
|
|
||||||
delta_signature_destroy(sig);
|
|
||||||
send_status(fd, STATUS_ERROR);
|
send_status(fd, STATUS_ERROR);
|
||||||
*failed = true;
|
goto fail;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
bool applied = delta_apply_to_fd(old_data, basis_fd, old_size, delta,
|
bool applied =
|
||||||
config->delta_block_size, spool_fd);
|
delta_apply_to_fd(old_data, basis_fd, old_size, delta, config->delta_block_size, spool_fd);
|
||||||
if (close(spool_fd) != 0)
|
if (close(spool_fd) != 0)
|
||||||
applied = false;
|
applied = false;
|
||||||
delta_destroy(delta);
|
delta_destroy(delta);
|
||||||
|
delta = NULL;
|
||||||
if (!applied) {
|
if (!applied) {
|
||||||
unlink(spool);
|
|
||||||
free(spool);
|
|
||||||
free(old_data);
|
|
||||||
delta_signature_destroy(sig);
|
|
||||||
send_status(fd, STATUS_ERROR);
|
send_status(fd, STATUS_ERROR);
|
||||||
*failed = true;
|
goto fail;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
new_data = old_data ? delta_apply(old_data, old_size, delta, config->delta_block_size)
|
new_data = old_data ? delta_apply(old_data, old_size, delta, config->delta_block_size)
|
||||||
: delta_apply_fd(basis_fd, old_size, delta, config->delta_block_size);
|
: delta_apply_fd(basis_fd, old_size, delta, config->delta_block_size);
|
||||||
delta_destroy(delta);
|
delta_destroy(delta);
|
||||||
if (!new_data) {
|
delta = NULL;
|
||||||
free(old_data);
|
if (!new_data)
|
||||||
delta_signature_destroy(sig);
|
goto fail;
|
||||||
*failed = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
File* file = file_create(check_path);
|
file = file_create(check_path);
|
||||||
if (!file) {
|
if (!file)
|
||||||
free(new_data);
|
goto fail;
|
||||||
if (spool) {
|
|
||||||
unlink(spool);
|
|
||||||
free(spool);
|
|
||||||
}
|
|
||||||
free(old_data);
|
|
||||||
delta_signature_destroy(sig);
|
|
||||||
*failed = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
file->matched_bytes = matched;
|
file->matched_bytes = matched;
|
||||||
file->literal_bytes = literal;
|
file->literal_bytes = literal;
|
||||||
|
|
||||||
if (config->use_metadata) {
|
if (config->use_metadata) {
|
||||||
int meta_ok = 1;
|
int meta_ok = 1;
|
||||||
file->metadata = metadata_receive(fd, &meta_ok);
|
file->metadata = metadata_receive(fd, &meta_ok);
|
||||||
if (!meta_ok) {
|
if (!meta_ok)
|
||||||
file_destroy(file);
|
goto fail;
|
||||||
free(new_data);
|
|
||||||
if (spool) {
|
|
||||||
unlink(spool);
|
|
||||||
free(spool);
|
|
||||||
}
|
|
||||||
free(old_data);
|
|
||||||
delta_signature_destroy(sig);
|
|
||||||
*failed = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!receive_file_xattrs(file, fd, config)) {
|
|
||||||
file_destroy(file);
|
|
||||||
free(new_data);
|
|
||||||
if (spool) {
|
|
||||||
unlink(spool);
|
|
||||||
free(spool);
|
|
||||||
}
|
|
||||||
free(old_data);
|
|
||||||
delta_signature_destroy(sig);
|
|
||||||
*failed = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
if (!receive_file_xattrs(file, fd, config))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
if (spool) {
|
if (spool) {
|
||||||
Data* reserved = data_create_reserve((size_t)new_size);
|
Data* reserved = data_create_reserve((size_t)new_size);
|
||||||
if (reserved == NULL) {
|
if (reserved == NULL) {
|
||||||
unlink(spool);
|
|
||||||
free(spool);
|
|
||||||
file_destroy(file);
|
|
||||||
free(old_data);
|
|
||||||
delta_signature_destroy(sig);
|
|
||||||
send_status(fd, STATUS_ERROR);
|
send_status(fd, STATUS_ERROR);
|
||||||
*failed = true;
|
goto fail;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
data_destroy(file->data);
|
data_destroy(file->data);
|
||||||
file->data = reserved;
|
file->data = reserved;
|
||||||
file->basis_copy = spool;
|
file->basis_copy = spool;
|
||||||
|
spool = NULL; /* ownership moved into file->basis_copy */
|
||||||
file->data_spool = true;
|
file->data_spool = true;
|
||||||
} else {
|
} else {
|
||||||
Data* replacement = data_create(new_data, (size_t)new_size);
|
Data* replacement = data_create(new_data, (size_t)new_size);
|
||||||
|
new_data = NULL; /* data_create owns, and frees, the buffer on failure */
|
||||||
if (replacement == NULL) {
|
if (replacement == NULL) {
|
||||||
file_destroy(file);
|
|
||||||
free(old_data);
|
|
||||||
delta_signature_destroy(sig);
|
|
||||||
send_status(fd, STATUS_ERROR);
|
send_status(fd, STATUS_ERROR);
|
||||||
*failed = true;
|
goto fail;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
data_destroy(file->data);
|
data_destroy(file->data);
|
||||||
file->data = replacement;
|
file->data = replacement;
|
||||||
@@ -280,52 +174,103 @@ static File* receive_delta_file(int fd, const Config* config, const char* check_
|
|||||||
free(old_data);
|
free(old_data);
|
||||||
delta_signature_destroy(sig);
|
delta_signature_destroy(sig);
|
||||||
return file;
|
return file;
|
||||||
}
|
|
||||||
|
|
||||||
if (resp == STATUS_NEXT) {
|
fail:
|
||||||
|
free(new_data);
|
||||||
|
if (spool) {
|
||||||
|
unlink(spool);
|
||||||
|
free(spool);
|
||||||
|
}
|
||||||
|
file_destroy(file);
|
||||||
|
delta_destroy(delta);
|
||||||
|
data_destroy(raw_delta);
|
||||||
|
free(old_data);
|
||||||
|
delta_signature_destroy(sig);
|
||||||
|
*failed = true;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Receive a STATUS_NEXT response: the sender declined the delta and will send
|
||||||
|
the whole file. Releases the basis signature and snapshot, then receives the
|
||||||
|
metadata/xattr block and the full payload. Takes ownership of `old_data` and
|
||||||
|
`sig`, releasing both immediately. */
|
||||||
|
static File* receive_next_branch(int fd, const Config* config, const char* check_path,
|
||||||
|
unsigned long long expected_size, void* old_data,
|
||||||
|
DeltaSignature* sig, bool* failed) {
|
||||||
delta_signature_destroy(sig);
|
delta_signature_destroy(sig);
|
||||||
free(old_data);
|
free(old_data);
|
||||||
|
|
||||||
File* file = file_create(check_path);
|
File* file = file_create(check_path);
|
||||||
if (!file) {
|
if (!file)
|
||||||
*failed = true;
|
goto fail;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config->use_metadata) {
|
if (config->use_metadata) {
|
||||||
int meta_ok = 1;
|
int meta_ok = 1;
|
||||||
file->metadata = metadata_receive(fd, &meta_ok);
|
file->metadata = metadata_receive(fd, &meta_ok);
|
||||||
if (!meta_ok) {
|
if (!meta_ok)
|
||||||
file_destroy(file);
|
goto fail;
|
||||||
*failed = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!receive_file_xattrs(file, fd, config)) {
|
|
||||||
file_destroy(file);
|
|
||||||
*failed = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
if (!receive_file_xattrs(file, fd, config))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
char* dest_path = path_cat(config->receive_root_directory, check_path);
|
char* dest_path = path_cat(config->receive_root_directory, check_path);
|
||||||
if (!dest_path) {
|
if (!dest_path)
|
||||||
file_destroy(file);
|
goto fail;
|
||||||
*failed = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
bool payload_ok = receive_file_payload_into(file, fd, config, dest_path, expected_size);
|
bool payload_ok = receive_file_payload_into(file, fd, config, dest_path, expected_size);
|
||||||
free(dest_path);
|
free(dest_path);
|
||||||
if (!payload_ok) {
|
if (!payload_ok)
|
||||||
|
goto fail;
|
||||||
|
return file;
|
||||||
|
|
||||||
|
fail:
|
||||||
file_destroy(file);
|
file_destroy(file);
|
||||||
*failed = true;
|
*failed = true;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* Delta handshake dispatcher: sign the basis, ship the signature, then hand the
|
||||||
|
response off to the matching branch helper. Takes ownership of `old_data`
|
||||||
|
(and, once created, `sig`); sets `*failed` on every error path. */
|
||||||
|
static File* receive_delta_file(int fd, const Config* config, const char* check_path,
|
||||||
|
void* old_data, unsigned long long old_size,
|
||||||
|
unsigned long long expected_size, int basis_fd, bool* failed) {
|
||||||
|
/* The basis is either an in-memory snapshot (the destination file, bounded) or
|
||||||
|
* a confined descriptor (a --fuzzy sibling, possibly larger than memory) that
|
||||||
|
* is signed/applied in bounded chunks. */
|
||||||
|
Data* sig_data = NULL;
|
||||||
|
Status resp = STATUS_ERROR;
|
||||||
|
bool sig_sent = false;
|
||||||
|
DeltaSignature* sig =
|
||||||
|
old_data ? delta_signature_create_seeded(old_data, old_size, config->delta_block_size,
|
||||||
|
(uint32_t)config->checksum_seed)
|
||||||
|
: delta_signature_create_fd_seeded(basis_fd, old_size, config->delta_block_size,
|
||||||
|
(uint32_t)config->checksum_seed);
|
||||||
|
if (!sig)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
sig_data = delta_signature_serialize(sig);
|
||||||
|
if (!sig_data)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
sig_sent = send_status(fd, STATUS_DELTA_SIGNATURE) && send_data(fd, sig_data);
|
||||||
|
data_destroy(sig_data);
|
||||||
|
sig_data = NULL;
|
||||||
|
|
||||||
|
if (!sig_sent || !receive_status(fd, &resp))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (resp == STATUS_DELTA_DATA)
|
||||||
|
return receive_delta_data_branch(fd, config, check_path, old_data, old_size, basis_fd, sig,
|
||||||
|
failed);
|
||||||
|
|
||||||
|
if (resp == STATUS_NEXT)
|
||||||
|
return receive_next_branch(fd, config, check_path, expected_size, old_data, sig, failed);
|
||||||
|
|
||||||
|
send_status(fd, STATUS_ERROR);
|
||||||
|
fail:
|
||||||
|
data_destroy(sig_data);
|
||||||
delta_signature_destroy(sig);
|
delta_signature_destroy(sig);
|
||||||
free(old_data);
|
free(old_data);
|
||||||
send_status(fd, STATUS_ERROR);
|
|
||||||
*failed = true;
|
*failed = true;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user