Release v2.26.0 #284
@@ -62,9 +62,8 @@ typedef struct {
|
|||||||
bool protect; /* receiver-side exclude matched */
|
bool protect; /* receiver-side exclude matched */
|
||||||
} FilterOutcome;
|
} FilterOutcome;
|
||||||
|
|
||||||
static void chain_rules_outcome(const FilterRuleList* base, const FilterNode* node,
|
static void chain_rules_outcome(const FilterRuleList* base, const FilterNode* node, const char* rel,
|
||||||
const char* rel, const char* leaf, bool is_dir,
|
const char* leaf, bool is_dir, FilterOutcome* out) {
|
||||||
FilterOutcome* out) {
|
|
||||||
memset(out, 0, sizeof(*out));
|
memset(out, 0, sizeof(*out));
|
||||||
bool sender_decided = false;
|
bool sender_decided = false;
|
||||||
bool receiver_decided = false;
|
bool receiver_decided = false;
|
||||||
@@ -1921,7 +1920,8 @@ ParallelScanner* parallel_scanner_create_with_options(const char* root_directory
|
|||||||
{
|
{
|
||||||
char err[256];
|
char err[256];
|
||||||
bool any_exists = false;
|
bool any_exists = false;
|
||||||
FilterRuleList* own = read_dir_filters(options, root_directory, "", &any_exists, err, sizeof(err));
|
FilterRuleList* own =
|
||||||
|
read_dir_filters(options, root_directory, "", &any_exists, err, sizeof(err));
|
||||||
if (!own && any_exists) {
|
if (!own && any_exists) {
|
||||||
/* no files exist: leave root_node NULL */
|
/* no files exist: leave root_node NULL */
|
||||||
} else if (!own) {
|
} else if (!own) {
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ typedef struct {
|
|||||||
/* -FF: also exclude the per-directory filter files themselves from the
|
/* -FF: also exclude the per-directory filter files themselves from the
|
||||||
transfer (single -F transfers them). */
|
transfer (single -F transfers them). */
|
||||||
bool exclude_per_dir_filter_files;
|
bool exclude_per_dir_filter_files;
|
||||||
bool dirs; /* -d/--dirs: transfer dir entries, no recursion */
|
bool dirs; /* -d/--dirs: transfer dir entries, no recursion */
|
||||||
bool relative; /* -R/--relative (dest rel paths, with --files-from) */
|
bool relative; /* -R/--relative (dest rel paths, with --files-from) */
|
||||||
/* --list-only: emit an is_dir File for every traversed directory (the listing
|
/* --list-only: emit an is_dir File for every traversed directory (the listing
|
||||||
* includes directory entries, matching rsync). Client-only; never set on a
|
* includes directory entries, matching rsync). Client-only; never set on a
|
||||||
* real transfer, which relies on implicit parent creation. */
|
* real transfer, which relies on implicit parent creation. */
|
||||||
|
|||||||
+5
-3
@@ -114,10 +114,12 @@ void print_usage(void) {
|
|||||||
printf(" --files-from <file> Read the source file list from FILE (paths relative to the "
|
printf(" --files-from <file> Read the source file list from FILE (paths relative to the "
|
||||||
"source root)\n");
|
"source root)\n");
|
||||||
printf(" -0, --from0 Entries in --files-from are NUL-delimited\n");
|
printf(" -0, --from0 Entries in --files-from are NUL-delimited\n");
|
||||||
printf(" -f, --filter=RULE rsync-style filter rule (+/- include/exclude; repeatable;\n");
|
printf(" -f, --filter=RULE rsync-style filter rule: exclude/- include/+ hide/H show/S\n");
|
||||||
printf(" both --filter=RULE and the -f RULE / -f=RULE short forms work)\n");
|
printf(" protect/P risk/R merge/. dir-merge/: clear/! with modifiers\n");
|
||||||
|
printf(" (repeatable; --filter=RULE and -f RULE / -f=RULE both work)\n");
|
||||||
printf(" -C, --cvs-exclude Auto-ignore common CVS/SCM files (.git/, .svn/, *.o, *~, ...)\n");
|
printf(" -C, --cvs-exclude Auto-ignore common CVS/SCM files (.git/, .svn/, *.o, *~, ...)\n");
|
||||||
printf(" -F Apply per-directory .rsync-filter files during the scan\n");
|
printf(" -F Apply per-directory .rsync-filter files; repeated -FF also\n");
|
||||||
|
printf(" excludes the .rsync-filter files themselves\n");
|
||||||
printf(" --max-size <n> Skip files larger than n bytes\n");
|
printf(" --max-size <n> Skip files larger than n bytes\n");
|
||||||
printf(" --min-size <n> Skip files smaller than n bytes\n");
|
printf(" --min-size <n> Skip files smaller than n bytes\n");
|
||||||
printf(" --max-alloc <SIZE> Maximum single allocation (default: 1G; 0 = no limit,\n");
|
printf(" --max-alloc <SIZE> Maximum single allocation (default: 1G; 0 = no limit,\n");
|
||||||
|
|||||||
+12
-12
@@ -1494,7 +1494,6 @@ static const char* fuzzy_find_suffix(const char* fn, int fn_len, int* len_ptr) {
|
|||||||
const char* suf;
|
const char* suf;
|
||||||
const char* s;
|
const char* s;
|
||||||
bool had_tilde;
|
bool had_tilde;
|
||||||
int s_len;
|
|
||||||
|
|
||||||
while (fn_len && *fn == '.') {
|
while (fn_len && *fn == '.') {
|
||||||
fn++;
|
fn++;
|
||||||
@@ -1509,6 +1508,7 @@ static const char* fuzzy_find_suffix(const char* fn, int fn_len, int* len_ptr) {
|
|||||||
suf = "";
|
suf = "";
|
||||||
*len_ptr = 0;
|
*len_ptr = 0;
|
||||||
for (s = fn + fn_len; fn_len > 1;) {
|
for (s = fn + fn_len; fn_len > 1;) {
|
||||||
|
int s_len;
|
||||||
while (--s != fn && *s != '.') {
|
while (--s != fn && *s != '.') {
|
||||||
}
|
}
|
||||||
if (s == fn)
|
if (s == fn)
|
||||||
@@ -1537,7 +1537,6 @@ static const char* fuzzy_find_suffix(const char* fn, int fn_len, int* len_ptr) {
|
|||||||
return suf;
|
return suf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Deterministic ordering of two fuzzy candidates with equal rsync distance:
|
/* Deterministic ordering of two fuzzy candidates with equal rsync distance:
|
||||||
* smallest size gap, then the lexical basename (rsync itself takes the last
|
* smallest size gap, then the lexical basename (rsync itself takes the last
|
||||||
* equal-distance candidate in file-list order). */
|
* equal-distance candidate in file-list order). */
|
||||||
@@ -1659,11 +1658,11 @@ static void* fuzzy_basis_find_and_load(const Config* config, const char* check_p
|
|||||||
accepted only when it does not exceed the running lowest distance. */
|
accepted only when it does not exceed the running lowest distance. */
|
||||||
int name_suf_len = 0;
|
int name_suf_len = 0;
|
||||||
const char* name_suf = fuzzy_find_suffix(name, (int)name_len, &name_suf_len);
|
const char* name_suf = fuzzy_find_suffix(name, (int)name_len, &name_suf_len);
|
||||||
uint32_t distance =
|
uint32_t distance = fuzzy_distance(name, (unsigned)name_len, leaf, (unsigned)target_len,
|
||||||
fuzzy_distance(name, (unsigned)name_len, leaf, (unsigned)target_len, lowest_dist, dist_scratch);
|
lowest_dist, dist_scratch);
|
||||||
if (distance < 0xFFFF0000U)
|
if (distance < 0xFFFF0000U)
|
||||||
distance += fuzzy_distance(name_suf, (unsigned)name_suf_len, fname_suf, (unsigned)fname_suf_len,
|
distance += fuzzy_distance(name_suf, (unsigned)name_suf_len, fname_suf,
|
||||||
0xFFFF0000U, dist_scratch) *
|
(unsigned)fname_suf_len, 0xFFFF0000U, dist_scratch) *
|
||||||
10;
|
10;
|
||||||
if (distance > lowest_dist)
|
if (distance > lowest_dist)
|
||||||
continue;
|
continue;
|
||||||
@@ -1949,7 +1948,8 @@ static IncrementalCheckOutcome incremental_check_report_dest_info(IncrementalChe
|
|||||||
lstat existence probe; the ordinary --ignore-existing checks inside
|
lstat existence probe; the ordinary --ignore-existing checks inside
|
||||||
file_receive remain as defense-in-depth for the frame types that have no
|
file_receive remain as defense-in-depth for the frame types that have no
|
||||||
per-file check (directories/symlinks/specials/hard-links). */
|
per-file check (directories/symlinks/specials/hard-links). */
|
||||||
static IncrementalCheckOutcome incremental_check_ignore_existing(IncrementalCheckState* state) {
|
static IncrementalCheckOutcome
|
||||||
|
incremental_check_ignore_existing(const IncrementalCheckState* state) {
|
||||||
if (!state->config->ignore_existing || !state->dest_exists)
|
if (!state->config->ignore_existing || !state->dest_exists)
|
||||||
return INCREMENTAL_CONTINUE;
|
return INCREMENTAL_CONTINUE;
|
||||||
if (!send_status(state->fd, STATUS_OK))
|
if (!send_status(state->fd, STATUS_OK))
|
||||||
@@ -1973,8 +1973,8 @@ static IncrementalCheckOutcome incremental_check_link_dest_relink(IncrementalChe
|
|||||||
return INCREMENTAL_CONTINUE;
|
return INCREMENTAL_CONTINUE;
|
||||||
BasisMatch basis;
|
BasisMatch basis;
|
||||||
basis_match_find(config, state->check_path, state->check_size, (time_t)state->check_mtime,
|
basis_match_find(config, state->check_path, state->check_size, (time_t)state->check_mtime,
|
||||||
(long)state->check_mtime_nsec, state->check_digest, state->check_digest_len, true,
|
(long)state->check_mtime_nsec, state->check_digest, state->check_digest_len,
|
||||||
true, &basis);
|
true, true, &basis);
|
||||||
/* Only a link-dest hit relinks; a copy-dest/compare-dest hit (or a miss) lets
|
/* Only a link-dest hit relinks; a copy-dest/compare-dest hit (or a miss) lets
|
||||||
the up-to-date check below keep the existing destination. */
|
the up-to-date check below keep the existing destination. */
|
||||||
if (!basis.hit || basis.type != BASIS_DEST_LINK) {
|
if (!basis.hit || basis.type != BASIS_DEST_LINK) {
|
||||||
@@ -2403,9 +2403,9 @@ static IncrementalCheckOutcome incremental_check_try_fuzzy(IncrementalCheckState
|
|||||||
if (!config->fuzzy || !config->use_delta)
|
if (!config->fuzzy || !config->use_delta)
|
||||||
return INCREMENTAL_CONTINUE;
|
return INCREMENTAL_CONTINUE;
|
||||||
unsigned long long fuzzy_size = 0;
|
unsigned long long fuzzy_size = 0;
|
||||||
void* fuzzy_basis =
|
void* fuzzy_basis = fuzzy_basis_find_and_load(config, state->check_path, state->check_size,
|
||||||
fuzzy_basis_find_and_load(config, state->check_path, state->check_size,
|
(time_t)state->check_mtime,
|
||||||
(time_t)state->check_mtime, (long)state->check_mtime_nsec, &fuzzy_size);
|
(long)state->check_mtime_nsec, &fuzzy_size);
|
||||||
if (fuzzy_basis != NULL) {
|
if (fuzzy_basis != NULL) {
|
||||||
bool fuzzy_failed = false;
|
bool fuzzy_failed = false;
|
||||||
File* fuzzy_file = receive_delta_file(state->fd, config, state->check_path, fuzzy_basis,
|
File* fuzzy_file = receive_delta_file(state->fd, config, state->check_path, fuzzy_basis,
|
||||||
|
|||||||
+5
-4
@@ -453,7 +453,8 @@ static bool filter_list_merge_file(FilterRuleList* list, const char* name,
|
|||||||
snprintf(err, err_size, "merge requires a filename");
|
snprintf(err, err_size, "merge requires a filename");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
char* path = (base_dir && base_dir[0] && name[0] != '/') ? path_cat(base_dir, name) : str_dup(name);
|
char* path =
|
||||||
|
(base_dir && base_dir[0] && name[0] != '/') ? path_cat(base_dir, name) : str_dup(name);
|
||||||
if (!path) {
|
if (!path) {
|
||||||
snprintf(err, err_size, "memory allocation failed");
|
snprintf(err, err_size, "memory allocation failed");
|
||||||
return false;
|
return false;
|
||||||
@@ -698,9 +699,9 @@ bool filter_file_append(FilterRuleList* list, const char* dir_path, const char*
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
FilterRuleList* filter_file_read_named(const char* dir_path, const char* name, const char* owner_rel,
|
FilterRuleList* filter_file_read_named(const char* dir_path, const char* name,
|
||||||
const FilterParseOptions* opts, bool* exists, char* err,
|
const char* owner_rel, const FilterParseOptions* opts,
|
||||||
size_t err_size) {
|
bool* exists, char* err, size_t err_size) {
|
||||||
FilterRuleList* list = filter_rule_list_create();
|
FilterRuleList* list = filter_rule_list_create();
|
||||||
if (!list) {
|
if (!list) {
|
||||||
if (err && err_size > 0)
|
if (err && err_size > 0)
|
||||||
|
|||||||
+4
-4
@@ -54,7 +54,7 @@ typedef struct {
|
|||||||
} FilterRule;
|
} FilterRule;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FilterRule** items; /* owned array of rule pointers */
|
FilterRule** items; /* owned array of rule pointers */
|
||||||
int count;
|
int count;
|
||||||
int capacity;
|
int capacity;
|
||||||
/* Per-directory merge-file basenames registered by "dir-merge NAME"/": NAME"
|
/* Per-directory merge-file basenames registered by "dir-merge NAME"/": NAME"
|
||||||
@@ -105,9 +105,9 @@ FilterRuleList* filter_base_build(const char* const* rule_texts, int rule_count,
|
|||||||
* missing file yields an empty list with *exists=false; an unreadable file is
|
* missing file yields an empty list with *exists=false; an unreadable file is
|
||||||
* treated as missing. Returns NULL only on parse or allocation failure
|
* treated as missing. Returns NULL only on parse or allocation failure
|
||||||
* (message in `err`). `opts` may be NULL. */
|
* (message in `err`). `opts` may be NULL. */
|
||||||
FilterRuleList* filter_file_read_named(const char* dir_path, const char* name, const char* owner_rel,
|
FilterRuleList* filter_file_read_named(const char* dir_path, const char* name,
|
||||||
const FilterParseOptions* opts, bool* exists, char* err,
|
const char* owner_rel, const FilterParseOptions* opts,
|
||||||
size_t err_size);
|
bool* exists, char* err, size_t err_size);
|
||||||
|
|
||||||
/* Append the rules of "<dir_path>/<name>" into an existing list (each owned by
|
/* Append the rules of "<dir_path>/<name>" into an existing list (each owned by
|
||||||
* `owner_rel`). A missing file yields *exists=false and no error. Returns
|
* `owner_rel`). A missing file yields *exists=false and no error. Returns
|
||||||
|
|||||||
Reference in New Issue
Block a user