- --delete-delay: count/track only entries actually removed; a directory refilled before commit (ENOTEMPTY) no longer inflates Number of deleted files or the --max-delete budget (unit + integration + rsync differential). - --stats: per-type Number of files breakdown; only stored regular files count as transferred; transferred/literal byte totals and Total file size (symlink target lengths) now match rsync for whole-file transfers. - --progress: print the leading ./ root line and include the root entry in the to-chk denominator (single-file output byte-identical to rsync). - --out-format %C: use the selected transfer checksum and render every algorithm exactly like rsync; checksum_digest_file gains md4/sha1/none. Reclassify --out-format to Divergent (protocol-specific %b/delta-%c). - Docs: RSYNC_COMPAT tally 107/25/24, HANDOFF update. No wire change.
157 lines
5.2 KiB
C
157 lines
5.2 KiB
C
#include "test_delete_plan.h"
|
|
#include "charset.h"
|
|
#include "config.h"
|
|
#include "delete_plan.h"
|
|
#include "protocol.h"
|
|
#include "test_utils.h"
|
|
#include "utils.h"
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
/* Send one STATUS_DELETE_PLAN body (the leading status is consumed by the
|
|
* caller/receiver entry point) describing `dir` with no kept children. */
|
|
static void send_plan_frame(int fd, const char* dir) {
|
|
EXPECT_TRUE(send_int(fd, 0)); /* has_config */
|
|
EXPECT_TRUE(send_wire_str(fd, dir));
|
|
EXPECT_TRUE(send_int(fd, 0)); /* kept child dirs */
|
|
EXPECT_TRUE(send_int(fd, 0)); /* kept child files */
|
|
}
|
|
|
|
/* --delete-delay: a directory snapshotted into the plan that is refilled before
|
|
* the commit must NOT be counted as deleted once its unlink fails ENOTEMPTY.
|
|
* Regression for delete_plan.c counting at snapshot (defer_add) instead of at
|
|
* the actual removal. */
|
|
static void test_delete_delay_refilled_dir_not_counted(void) {
|
|
char root[] = "/tmp/fastsync_dp_refill_XXXXXX";
|
|
EXPECT_TRUE(mkdtemp(root) != NULL);
|
|
char extra[1024];
|
|
snprintf(extra, sizeof(extra), "%s/extra", root);
|
|
EXPECT_EQ_INT(mkdir(extra, 0700), 0);
|
|
|
|
Config* config = config_create();
|
|
EXPECT_NOT_NULL(config);
|
|
config->receive_root_directory = str_dup(root);
|
|
config->use_delete = true;
|
|
config->delete_delay = true;
|
|
|
|
int p[2];
|
|
EXPECT_EQ_INT(socketpair(AF_UNIX, SOCK_STREAM, 0, p), 0);
|
|
|
|
DeletePlanSession* session = delete_plan_session_create(config);
|
|
EXPECT_NOT_NULL(session);
|
|
send_plan_frame(p[1], ".");
|
|
EXPECT_EQ_INT(delete_plan_session_receive(session, config, p[0]), 0);
|
|
/* The empty extra directory was snapshotted, not removed yet. */
|
|
EXPECT_EQ_INT((int)delete_plan_session_deleted(session), 0);
|
|
|
|
/* Refill the directory while the deferred commit is pending. */
|
|
char refill[1200];
|
|
snprintf(refill, sizeof(refill), "%s/new.txt", extra);
|
|
int fd = open(refill, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
|
EXPECT_TRUE(fd >= 0);
|
|
close(fd);
|
|
|
|
EXPECT_EQ_INT(delete_plan_session_commit(session, config), DELETE_COMMIT_OK);
|
|
/* ENOTEMPTY: the directory survives, so it must not be reported as deleted. */
|
|
EXPECT_EQ_INT((int)delete_plan_session_deleted(session), 0);
|
|
struct stat st;
|
|
EXPECT_EQ_INT(lstat(extra, &st), 0);
|
|
|
|
delete_plan_session_destroy(session);
|
|
close(p[0]);
|
|
close(p[1]);
|
|
unlink(refill);
|
|
rmdir(extra);
|
|
rmdir(root);
|
|
config_delete(config);
|
|
}
|
|
|
|
/* The complement: a deferred regular extra that DOES get removed is counted. */
|
|
static void test_delete_delay_removed_file_counted(void) {
|
|
char root[] = "/tmp/fastsync_dp_file_XXXXXX";
|
|
EXPECT_TRUE(mkdtemp(root) != NULL);
|
|
char extra[1024];
|
|
snprintf(extra, sizeof(extra), "%s/extra.txt", root);
|
|
int fd = open(extra, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
|
EXPECT_TRUE(fd >= 0);
|
|
close(fd);
|
|
|
|
Config* config = config_create();
|
|
EXPECT_NOT_NULL(config);
|
|
config->receive_root_directory = str_dup(root);
|
|
config->use_delete = true;
|
|
config->delete_delay = true;
|
|
|
|
int p[2];
|
|
EXPECT_EQ_INT(socketpair(AF_UNIX, SOCK_STREAM, 0, p), 0);
|
|
|
|
DeletePlanSession* session = delete_plan_session_create(config);
|
|
EXPECT_NOT_NULL(session);
|
|
send_plan_frame(p[1], ".");
|
|
EXPECT_EQ_INT(delete_plan_session_receive(session, config, p[0]), 0);
|
|
EXPECT_EQ_INT((int)delete_plan_session_deleted(session), 0);
|
|
EXPECT_EQ_INT(delete_plan_session_commit(session, config), DELETE_COMMIT_OK);
|
|
EXPECT_EQ_INT((int)delete_plan_session_deleted(session), 1);
|
|
EXPECT_TRUE(lstat(extra, &(struct stat){0}) != 0);
|
|
|
|
delete_plan_session_destroy(session);
|
|
close(p[0]);
|
|
close(p[1]);
|
|
rmdir(root);
|
|
config_delete(config);
|
|
}
|
|
|
|
/* --max-delete still bounds the deferred plan; the actual (removed) count must
|
|
* not exceed the limit even though more extras existed. */
|
|
static void test_delete_delay_max_delete_bounds_actual(void) {
|
|
char root[] = "/tmp/fastsync_dp_max_XXXXXX";
|
|
EXPECT_TRUE(mkdtemp(root) != NULL);
|
|
for (int i = 0; i < 3; i++) {
|
|
char path[1024];
|
|
snprintf(path, sizeof(path), "%s/e%d.txt", root, i);
|
|
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
|
EXPECT_TRUE(fd >= 0);
|
|
close(fd);
|
|
}
|
|
|
|
Config* config = config_create();
|
|
EXPECT_NOT_NULL(config);
|
|
config->receive_root_directory = str_dup(root);
|
|
config->use_delete = true;
|
|
config->delete_delay = true;
|
|
config->max_delete = 1;
|
|
|
|
int p[2];
|
|
EXPECT_EQ_INT(socketpair(AF_UNIX, SOCK_STREAM, 0, p), 0);
|
|
|
|
DeletePlanSession* session = delete_plan_session_create(config);
|
|
EXPECT_NOT_NULL(session);
|
|
send_plan_frame(p[1], ".");
|
|
EXPECT_EQ_INT(delete_plan_session_receive(session, config, p[0]), 0);
|
|
EXPECT_TRUE(delete_plan_session_limit_reached(session));
|
|
EXPECT_EQ_INT(delete_plan_session_commit(session, config), DELETE_COMMIT_LIMIT_REACHED);
|
|
EXPECT_EQ_INT((int)delete_plan_session_deleted(session), 1);
|
|
|
|
delete_plan_session_destroy(session);
|
|
close(p[0]);
|
|
close(p[1]);
|
|
for (int i = 0; i < 3; i++) {
|
|
char path[1024];
|
|
snprintf(path, sizeof(path), "%s/e%d.txt", root, i);
|
|
unlink(path);
|
|
}
|
|
rmdir(root);
|
|
config_delete(config);
|
|
}
|
|
|
|
void test_delete_plan(void) {
|
|
test_delete_delay_refilled_dir_not_counted();
|
|
test_delete_delay_removed_file_counted();
|
|
test_delete_delay_max_delete_bounds_actual();
|
|
}
|