quality: refactors (#321-#326) + fake-super directories (#319) #329

Merged
TapTap merged 14 commits from quality/refactor into dev 2026-09-24 03:17:42 +02:00
9 changed files with 308 additions and 265 deletions
Showing only changes of commit b74182c7c1 - Show all commits
+13 -53
View File
@@ -149,7 +149,7 @@ DirectoryScanner* directory_scanner_create_with_options(const char* root_directo
scanner->options = *options;
if (scanner->options.chunk_size == 0)
scanner->options.chunk_size = DESIRED_CHUNK_SIZE;
scanner->directories = queue_create(100, dir_entry_destroy);
scanner->directories = queue_create(SCANNER_RESULT_QUEUE_CAP, dir_entry_destroy);
if (!scanner->directories) {
free(scanner);
return NULL;
@@ -1101,61 +1101,23 @@ static ScannerAction scanner_process_entry(DirectoryScanner* scanner, ArrayList*
free(rel_copy);
return SCANNER_ACTION_CONTINUE;
}
File* file = file_create(cur_path);
if (file == NULL) {
free(rel_copy);
free(inspected->link_target);
inspected->link_target = NULL;
/* Entry construction (data size, -R wire path, special/devices, hardlink
group, metadata, xattrs) is shared with the parallel scanner. */
File* file = NULL;
bool build_failed = false;
ScannerBuildStatus status =
scanner_build_file_entry(&scanner->options, inspected, rel_copy, &file, &build_failed);
free(rel_copy);
rel_copy = NULL;
if (build_failed)
scanner->failed = true;
if (status == SCANNER_BUILD_SKIP)
return SCANNER_ACTION_CONTINUE;
}
if (inspected->is_symlink) {
file->is_symlink = true;
file->symlink_target = inspected->link_target;
inspected->link_target = NULL;
} else {
file->data->size = stats.st_size;
}
if (scanner->relative_mode) {
file->send_path = rel_copy;
rel_copy = NULL;
} else if (scanner->options.relative_prefix) {
file->send_path = scanner_prefix_send_path(scanner->options.relative_prefix, rel_copy);
free(rel_copy);
rel_copy = NULL;
if (!file->send_path) {
file_destroy(file);
scanner->failed = true;
return SCANNER_ACTION_BREAK;
}
}
/* --devices/--specials: a device/FIFO/socket entry marked for preservation
becomes a node to recreate (is_special, no data, rdev captured); an
unrequested non-regular entry is skipped (rsync default). */
ScannerSpecial special =
scanner_prepare_special(scanner->options.preserve_devices, scanner->options.preserve_specials,
scanner->options.copy_devices, file, &stats);
if (special == SCANNER_SPECIAL_SKIP) {
scanner_note_nonreg(&scanner->options, file->path);
free(rel_copy);
file_destroy(file);
return SCANNER_ACTION_CONTINUE;
}
if (scanner->options.hardlinks && S_ISREG(stats.st_mode))
scanner_assign_hardlink(scanner, scanner->options.hardlinks, file, &stats);
if (scanner->options.use_metadata)
file->metadata = file_metadata_create(file->path, &stats, scanner->options.preserve_atimes,
scanner->options.preserve_crtimes);
if (scanner->options.use_metadata && !file->metadata) {
free(rel_copy);
file_destroy(file);
if (status != SCANNER_BUILD_OK) {
scanner->failed = true;
return SCANNER_ACTION_BREAK;
return status == SCANNER_BUILD_FAIL_CONTINUE ? SCANNER_ACTION_CONTINUE : SCANNER_ACTION_BREAK;
}
if (!(file->link_group != 0 && !file->link_first))
scanner_capture_xattrs(scanner, file);
if (!array_list_add(chunk_data, file)) {
free(rel_copy);
file_destroy(file);
scanner->failed = true;
return SCANNER_ACTION_BREAK;
@@ -1163,14 +1125,12 @@ static ScannerAction scanner_process_entry(DirectoryScanner* scanner, ArrayList*
scanner->current_dir_produced = true;
*chunk_data_size += file->data->size;
if (*chunk_data_size > scanner->options.chunk_size) {
free(rel_copy);
Chunk* result = chunk_data_to_chunk(chunk_data);
if (!result)
scanner->failed = true;
*out_chunk = result;
return SCANNER_ACTION_CHUNK;
}
free(rel_copy);
return SCANNER_ACTION_CONTINUE;
}
+5
View File
@@ -19,6 +19,11 @@
* keeps one transfer from spawning an unbounded pool on a very large machine. */
#define MAX_SCANNER_THREADS 256
/* Depth of the scanner's work queues: the sequential scanner's pending-directory
* stack and the parallel scanner's result queue. Bounds memory for a very wide
* or very deep tree while leaving ample headroom for normal scans. */
#define SCANNER_RESULT_QUEUE_CAP 100
typedef struct {
bool use_metadata;
/* Phase 4 metadata capture: -U/--atimes and -N/--crtimes tell the scanner to
+88 -15
View File
@@ -285,32 +285,34 @@ bool entry_passes_selection(const FileListSet* file_list, const FilterRuleList*
* read xattrs is non-fatal: the file is transferred without them. A symlink
* entry reads the LINK's own xattrs (never the referent's) with the no-follow
* variant; on Linux the VFS refuses xattrs on symlinks, so that yields NULL. */
void scanner_capture_xattrs(const DirectoryScanner* scanner, File* file) {
if (!scanner || !file || !(scanner->options.preserve_xattrs || scanner->options.preserve_acls))
void scanner_capture_xattrs_opts(const ScannerOptions* options, File* file) {
if (!options || !file || !(options->preserve_xattrs || options->preserve_acls))
return;
file->xattrs = file->is_symlink
? xattr_capture_path_nofollow(file->path, scanner->options.preserve_acls)
: xattr_capture_path(file->path, scanner->options.preserve_acls);
file->xattrs = file->is_symlink ? xattr_capture_path_nofollow(file->path, options->preserve_acls)
: xattr_capture_path(file->path, options->preserve_acls);
}
void scanner_capture_xattrs(const DirectoryScanner* scanner, File* file) {
if (!scanner)
return;
scanner_capture_xattrs_opts(&scanner->options, file);
}
/* Apply --hard-links (-H) detection to one regular File. On a sibling (a
* later member of an already-seen source inode) the File keeps the group id
* and the first member's wire path but carries NO data payload (size 0); the
* first member is left untouched (data present, link_first). Allocation
* failure is fatal: the scanner is marked failed. */
void scanner_assign_hardlink(DirectoryScanner* scanner, HardLinkTable* table, File* file,
const struct stat* stats) {
* first member is left untouched (data present, link_first). Returns false on
* allocation failure (the caller marks the scan failed); the File stays usable
* either way. */
bool scanner_assign_hardlink(HardLinkTable* table, File* file, const struct stat* stats) {
if (!table || !file || !stats)
return;
return true;
int gid;
bool is_first;
char* first_path = NULL;
if (!hardlink_table_assign(table, file_wire_path(file), stats->st_dev, stats->st_ino, &gid,
&is_first, &first_path)) {
if (scanner)
scanner->failed = true;
return;
}
&is_first, &first_path))
return false;
file->link_group = gid;
file->link_first = is_first;
if (!is_first) {
@@ -319,6 +321,7 @@ void scanner_assign_hardlink(DirectoryScanner* scanner, HardLinkTable* table, Fi
} else {
free(first_path);
}
return true;
}
/* Phase 4 special/devices decision for one non-regular entry, matching rsync:
@@ -399,6 +402,76 @@ void scanner_note_nonreg(const ScannerOptions* options, const char* fs_path) {
fflush(stdout);
}
/* Construct one non-directory File from an inspected entry. Shared by the
* sequential and parallel scanners so entry construction has a single
* implementation: data size (or carried symlink), -R wire path, special/devices
* classification, hardlink group, metadata and xattr capture all happen here in
* the same order for both. See the declaration for the ownership contract. */
ScannerBuildStatus scanner_build_file_entry(const ScannerOptions* options, ScannerEntry* inspected,
const char* rel, File** out_file, bool* failed) {
*out_file = NULL;
if (failed)
*failed = false;
File* file = file_create(inspected->path);
if (!file) {
/* The File never existed, so drop the not-yet-transferred symlink target
here; the caller's entry teardown would otherwise double-free it. */
free(inspected->link_target);
inspected->link_target = NULL;
return SCANNER_BUILD_FAIL_CONTINUE;
}
if (inspected->is_symlink) {
file->is_symlink = true;
file->symlink_target = inspected->link_target;
inspected->link_target = NULL;
} else {
file->data->size = inspected->stats.st_size;
}
/* -R + --files-from uses the bare transfer-relative path; -R without
--files-from prefixes it. Plain scans keep the source path. */
bool relative_mode = options->relative && options->file_list != NULL;
if (relative_mode) {
file->send_path = str_dup(rel);
} else if (options->relative_prefix) {
file->send_path = scanner_prefix_send_path(options->relative_prefix, rel);
}
if ((relative_mode || options->relative_prefix) && !file->send_path) {
file_destroy(file);
return SCANNER_BUILD_FAIL_BREAK;
}
/* --devices/--specials: a device/FIFO/socket entry marked for preservation
becomes a node to recreate (is_special, no data, rdev captured); an
unrequested non-regular entry is skipped (rsync default). */
ScannerSpecial special =
scanner_prepare_special(options->preserve_devices, options->preserve_specials,
options->copy_devices, file, &inspected->stats);
if (special == SCANNER_SPECIAL_SKIP) {
scanner_note_nonreg(options, file->path);
file_destroy(file);
return SCANNER_BUILD_SKIP;
}
if (options->hardlinks && S_ISREG(inspected->stats.st_mode) &&
!scanner_assign_hardlink(options->hardlinks, file, &inspected->stats)) {
/* Allocation failure is non-fatal to this entry (it is still emitted) but
marks the scan failed, matching the historical inlined behaviour. */
if (failed)
*failed = true;
}
if (options->use_metadata) {
file->metadata = file_metadata_create(file->path, &inspected->stats, options->preserve_atimes,
options->preserve_crtimes);
if (!file->metadata) {
file_destroy(file);
return SCANNER_BUILD_FAIL_BREAK;
}
}
/* A hardlink sibling carries no data, so it carries no xattrs. */
if (!(file->link_group != 0 && !file->link_first))
scanner_capture_xattrs_opts(options, file);
*out_file = file;
return SCANNER_BUILD_OK;
}
/* rsync 3.4.1's `--info=mount` line, emitted when `-xx` drops a mount-point
* directory: `[sender] skipping mount-point dir NAME` (the client is the
* sender). Plain `-x` keeps the empty directory and prints nothing, matching
+24 -2
View File
@@ -62,6 +62,17 @@ typedef enum {
SCANNER_SPECIAL_SKIP, /* non-regular entry not requested: skip */
} ScannerSpecial;
/* Result of scanner_build_file_entry(). The two failure variants preserve the
* sequential scanner's historical distinction between a failure before the
* File existed (which kept walking the directory) and one afterwards (which cut
* the chunk short); both mark the scan failed. */
typedef enum {
SCANNER_BUILD_OK, /* File built; caller owns it */
SCANNER_BUILD_SKIP, /* non-regular entry not preserved; no File */
SCANNER_BUILD_FAIL_CONTINUE, /* failed before the File existed */
SCANNER_BUILD_FAIL_BREAK, /* failed after the File existed */
} ScannerBuildStatus;
/* scanner_filter.c */
void filter_node_destroy(void* item);
FilterNode* filter_node_alloc(FilterNode* parent, FilterRuleList* own);
@@ -79,10 +90,21 @@ bool entry_passes_selection(const FileListSet* file_list, const FilterRuleList*
const FilterNode* node, const char* rel, const char* leaf, bool is_dir,
bool per_dir_filters, bool exclude_filter_files, bool* protect_out);
void scanner_capture_xattrs(const DirectoryScanner* scanner, File* file);
void scanner_assign_hardlink(DirectoryScanner* scanner, HardLinkTable* table, File* file,
const struct stat* stats);
void scanner_capture_xattrs_opts(const ScannerOptions* options, File* file);
bool scanner_assign_hardlink(HardLinkTable* table, File* file, const struct stat* stats);
ScannerSpecial scanner_prepare_special(bool preserve_devices, bool preserve_specials,
bool copy_devices, File* file, const struct stat* stats);
/* Build one non-directory transfer File from an inspected entry. `rel` is the
* entry's transfer-root-relative path (used for the -R wire path); `inspected`
* supplies the on-disk path, stats and (for a carried symlink) the target whose
* ownership transfers to the File. Populates data size, send_path, special-node
* state, hardlink group, metadata and xattrs. On SCANNER_BUILD_OK the caller
* owns *out_file; on SCANNER_BUILD_SKIP it is NULL and the entry is dropped; on
* either failure it is NULL and the caller must mark the scan failed. `*failed`
* additionally reports a non-fatal hardlink-table allocation failure, in which
* case a usable File is still returned. */
ScannerBuildStatus scanner_build_file_entry(const ScannerOptions* options, ScannerEntry* inspected,
const char* rel, File** out_file, bool* failed);
bool excluded_sink_append(ArrayList* list, mtx_t* mtx, const char* rel);
void scanner_note_nonreg(const ScannerOptions* options, const char* fs_path);
void scanner_note_mount(const ScannerOptions* options, const char* fs_path);
+167 -193
View File
@@ -109,7 +109,7 @@ static void parallel_scanner_creation_failed(ParallelScanner* ps) {
/* Initialize result queue and synchronization primitives. Returns true on success. */
static bool parallel_scanner_init(ParallelScanner* ps) {
ps->result_queue = queue_create(100, chunk_destroy);
ps->result_queue = queue_create(SCANNER_RESULT_QUEUE_CAP, chunk_destroy);
if (!ps->result_queue)
return false;
atomic_init(&ps->cancelled, false);
@@ -210,6 +210,157 @@ static Chunk* batch_files(ArrayList* files, unsigned long long chunk_size, Queue
return first;
}
/* Record the delete-protection mirror of a root entry that
* scanner_inspect_entry() skipped (inspection == 0): a dereferenced symlink
* with no referent is a partial-transfer I/O error and a user-selection or size
* prune protects the entry's destination mirror. */
static void scan_root_record_skipped(const ScannerOptions* options, const char* root_directory,
const char* name, const ScannerEntry* inspected,
ParallelScanner* ps) {
if (inspected->referent_error)
ps->io_error = true;
ArrayList* sink = NULL;
if (inspected->excluded)
sink = inspected->size_excluded ? options->size_skipped_paths : options->excluded_paths;
if (!sink)
return;
/* A root-level prune protects the destination mirror of the entry's wire
path: under -R + --files-from that is the bare relative name, otherwise it
is the full source path with a leading '/' removed (matching the
send_path/file_wire_path the scanner hands the sender). */
if (options->relative && options->file_list != NULL) {
if (!excluded_sink_append(sink, options->excluded_mutex, name))
ps->failed = true;
} else if (options->relative_prefix) {
char* wrel = scanner_prefix_send_path(options->relative_prefix, name);
if (!wrel) {
ps->failed = true;
} else {
if (!excluded_sink_append(sink, options->excluded_mutex, wrel))
ps->failed = true;
free(wrel);
}
} else {
char* abs_path = path_cat(root_directory, name);
if (!abs_path) {
ps->failed = true;
} else {
const char* rel = *abs_path == '/' ? abs_path + 1 : abs_path;
if (!excluded_sink_append(sink, options->excluded_mutex, rel))
ps->failed = true;
free(abs_path);
}
}
}
/* Record the delete-protection mirror of a root entry dropped by the
* --files-from allow-set or a filter rule. Returns false only when the -R
* prefix could not be built (the caller must abandon the entry immediately);
* other allocation failures mark the scan failed but let the caller continue to
* the filter-notice step, matching the historical inlined flow. */
static bool scan_root_record_protection(const ScannerOptions* options, const char* rel,
const char* name, const char* cur_path, bool protect,
bool passes, bool use_rel, ParallelScanner* ps) {
if (passes && !protect)
return true;
/* --files-from subset pruning is not a filter exclusion; -R bare-wire-path
exclusions are never recorded (see ScannerOptions.excluded_paths). */
bool files_from_prune = options->file_list && !file_list_affects(options->file_list, rel);
if ((!files_from_prune && !use_rel) || protect) {
const char* rel_path;
char* prefixed = NULL;
if (use_rel) {
/* -R + --files-from: the destination/wire path is the bare relative
name, not the source path. */
rel_path = rel;
} else if (options->relative_prefix) {
prefixed = scanner_prefix_send_path(options->relative_prefix, name);
if (!prefixed)
return false;
rel_path = prefixed;
} else {
rel_path = *cur_path == '/' ? cur_path + 1 : cur_path;
}
if (options->excluded_paths &&
!excluded_sink_append(options->excluded_paths, options->excluded_mutex, rel_path))
ps->failed = true;
free(prefixed);
}
return true;
}
/* Root-level directory node: apply -x/--one-file-system and either emit the
* mount-point directory (plain -x) or queue the directory for a worker. */
static void scan_root_dir(const ScannerOptions* options, const char* cur_path, const char* rel,
const struct stat* st, ArrayList* root_files, ArrayList* subdirs,
dev_t root_dev, ParallelScanner* ps) {
if (!scanner_same_filesystem(options->one_file_system, root_dev, st->st_dev)) {
if (options->one_file_system > 1) {
/* -xx: drop the mount-point directory entirely (rsync) and print the
--info=mount line when enabled. */
scanner_note_mount(options, cur_path);
return;
}
/* -x/--one-file-system: emit the mount-point directory entry (empty) but do
not descend into it (see the sequential scanner for the same rule). */
File* mount = scanner_build_dir_file(cur_path, st, options);
if (!mount) {
ps->failed = true;
return;
}
if (options->relative_prefix) {
mount->send_path = scanner_prefix_send_path(options->relative_prefix, rel);
if (!mount->send_path) {
file_destroy(mount);
ps->failed = true;
return;
}
}
if (!array_list_add(root_files, mount)) {
file_destroy(mount);
ps->failed = true;
}
return;
}
char* dir = str_dup(cur_path);
if (!dir || !array_list_add(subdirs, dir)) {
free(dir);
ps->failed = true;
}
}
/* Build a non-directory root entry through the shared construction path and add
* it to `root_files`. A non-regular entry the options do not preserve is
* dropped by the builder (which prints rsync's nonreg line); an allocation
* failure marks the scan failed. */
static void scan_root_add_non_dir(const ScannerOptions* options, ScannerEntry* inspected,
const char* rel, ArrayList* root_files, ParallelScanner* ps) {
File* file = NULL;
bool failed = false;
ScannerBuildStatus status = scanner_build_file_entry(options, inspected, rel, &file, &failed);
if (failed)
ps->failed = true;
if (status != SCANNER_BUILD_OK)
return;
if (!array_list_add(root_files, file)) {
file_destroy(file);
ps->failed = true;
}
}
/* Regular file or carried symlink at the transfer root. */
static void scan_root_file(const ScannerOptions* options, ScannerEntry* inspected, const char* rel,
ArrayList* root_files, ParallelScanner* ps) {
scan_root_add_non_dir(options, inspected, rel, root_files, ps);
}
/* Device/FIFO/socket at the transfer root: recreated under --devices/--specials,
* otherwise dropped by the shared builder. */
static void scan_root_special(const ScannerOptions* options, ScannerEntry* inspected,
const char* rel, ArrayList* root_files, ParallelScanner* ps) {
scan_root_add_non_dir(options, inspected, rel, root_files, ps);
}
/* Scan one root-directory entry into either the subdirs or files list. */
static void scan_root_entry(const ScannerOptions* options, const FilterNode* root_node,
const char* root_directory, const struct dirent* entry,
@@ -223,51 +374,16 @@ static void scan_root_entry(const ScannerOptions* options, const FilterNode* roo
return;
}
if (inspection == 0) {
if (inspected.referent_error)
ps->io_error = true;
ArrayList* sink = NULL;
if (inspected.excluded)
sink = inspected.size_excluded ? options->size_skipped_paths : options->excluded_paths;
if (sink) {
/* A root-level prune protects the destination mirror of the entry's wire
path: under -R + --files-from that is the bare relative name, otherwise
it is the full source path with a leading '/' removed (matching the
send_path/file_wire_path the scanner hands the sender). */
if (options->relative && options->file_list != NULL) {
if (!excluded_sink_append(sink, options->excluded_mutex, entry->d_name))
ps->failed = true;
} else if (options->relative_prefix) {
char* wrel = scanner_prefix_send_path(options->relative_prefix, entry->d_name);
if (!wrel) {
ps->failed = true;
} else {
if (!excluded_sink_append(sink, options->excluded_mutex, wrel))
ps->failed = true;
free(wrel);
}
} else {
char* abs_path = path_cat(root_directory, entry->d_name);
if (!abs_path) {
ps->failed = true;
} else {
const char* rel = *abs_path == '/' ? abs_path + 1 : abs_path;
if (!excluded_sink_append(sink, options->excluded_mutex, rel))
ps->failed = true;
free(abs_path);
}
}
}
scan_root_record_skipped(options, root_directory, entry->d_name, &inspected, ps);
return;
}
char* cur_path = inspected.path;
struct stat st = inspected.stats;
bool is_dir = inspected.is_directory;
char* rel = str_dup(entry->d_name);
if (!rel) {
free(cur_path);
ps->failed = true;
return;
goto done;
}
bool is_dir = inspected.is_directory;
bool protect = false;
bool passes = entry_passes_selection(options->file_list, options->base_filters, root_node, rel,
entry->d_name, is_dir, options->per_dir_filters,
@@ -275,169 +391,27 @@ static void scan_root_entry(const ScannerOptions* options, const FilterNode* roo
/* -R + --files-from: root-level files keep their bare relative send path. */
bool use_rel = options->relative && options->file_list != NULL;
if (!passes || protect) {
/* --files-from subset pruning is not a filter exclusion; -R bare-wire-path
exclusions are never recorded (see ScannerOptions.excluded_paths). */
bool files_from_prune = options->file_list && !file_list_affects(options->file_list, rel);
if ((!files_from_prune && !use_rel) || protect) {
const char* rel_path;
char* prefixed = NULL;
if (use_rel) {
/* -R + --files-from: the destination/wire path is the bare relative
name, not the source path. */
rel_path = rel;
} else if (options->relative_prefix) {
prefixed = scanner_prefix_send_path(options->relative_prefix, entry->d_name);
if (!prefixed) {
free(rel);
free(cur_path);
ps->failed = true;
return;
}
rel_path = prefixed;
} else {
rel_path = *cur_path == '/' ? cur_path + 1 : cur_path;
}
if (options->excluded_paths &&
!excluded_sink_append(options->excluded_paths, options->excluded_mutex, rel_path))
ps->failed = true;
free(prefixed);
if (!scan_root_record_protection(options, rel, entry->d_name, cur_path, protect, passes,
use_rel, ps)) {
ps->failed = true;
goto done;
}
if (!passes) {
scanner_note_filter(options, entry->d_name);
free(rel);
free(cur_path);
return;
goto done;
}
}
if (is_dir) {
if (!scanner_same_filesystem(options->one_file_system, root_dev, st.st_dev)) {
if (options->one_file_system > 1) {
/* -xx: drop the mount-point directory entirely (rsync) and print the
--info=mount line when enabled. */
scanner_note_mount(options, cur_path);
free(rel);
free(cur_path);
return;
}
/* -x/--one-file-system: emit the mount-point directory entry (empty) but
do not descend into it (see the sequential scanner for the same rule). */
File* mount = file_create(cur_path);
free(cur_path);
if (mount == NULL) {
free(rel);
ps->failed = true;
return;
}
mount->is_dir = true;
if (options->use_metadata) {
mount->metadata = file_metadata_create(mount->path, &st, options->preserve_atimes,
options->preserve_crtimes);
if (!mount->metadata) {
free(rel);
file_destroy(mount);
ps->failed = true;
return;
}
}
if (options->relative_prefix) {
mount->send_path = scanner_prefix_send_path(options->relative_prefix, rel);
if (!mount->send_path) {
free(rel);
file_destroy(mount);
ps->failed = true;
return;
}
}
free(rel);
if (!array_list_add(root_files, mount)) {
file_destroy(mount);
ps->failed = true;
}
return;
}
free(rel);
if (!array_list_add(subdirs, cur_path)) {
free(cur_path);
ps->failed = true;
}
return;
}
File* file = file_create(cur_path);
free(cur_path);
if (!file) {
free(rel);
free(inspected.link_target);
inspected.link_target = NULL;
ps->failed = true;
return;
}
if (inspected.is_symlink) {
file->is_symlink = true;
file->symlink_target = inspected.link_target;
inspected.link_target = NULL;
scan_root_dir(options, cur_path, rel, &inspected.stats, root_files, subdirs, root_dev, ps);
} else if (S_ISCHR(inspected.stats.st_mode) || S_ISBLK(inspected.stats.st_mode) ||
S_ISFIFO(inspected.stats.st_mode) || S_ISSOCK(inspected.stats.st_mode)) {
scan_root_special(options, &inspected, rel, root_files, ps);
} else {
file->data->size = st.st_size;
}
if (use_rel) {
file->send_path = rel;
rel = NULL;
} else if (options->relative_prefix) {
file->send_path = scanner_prefix_send_path(options->relative_prefix, rel);
free(rel);
rel = NULL;
if (!file->send_path) {
file_destroy(file);
ps->failed = true;
return;
}
}
ScannerSpecial special = scanner_prepare_special(
options->preserve_devices, options->preserve_specials, options->copy_devices, file, &st);
if (special == SCANNER_SPECIAL_SKIP) {
scanner_note_nonreg(ps->options, file->path);
free(rel);
file_destroy(file);
return;
}
if (options->hardlinks && S_ISREG(st.st_mode)) {
int gid;
bool is_first;
char* first_path = NULL;
if (!hardlink_table_assign((HardLinkTable*)options->hardlinks, file_wire_path(file), st.st_dev,
st.st_ino, &gid, &is_first, &first_path)) {
ps->failed = true;
} else {
file->link_group = gid;
file->link_first = is_first;
if (!is_first) {
file->hardlink_target = first_path;
file->data->size = 0;
} else {
free(first_path);
}
}
}
if (options->use_metadata)
file->metadata =
file_metadata_create(file->path, &st, options->preserve_atimes, options->preserve_crtimes);
if (options->use_metadata && !file->metadata) {
free(rel);
file_destroy(file);
ps->failed = true;
return;
}
if ((options->preserve_xattrs || options->preserve_acls) &&
!(file->link_group != 0 && !file->link_first))
file->xattrs = file->is_symlink
? xattr_capture_path_nofollow(file->path, options->preserve_acls)
: xattr_capture_path(file->path, options->preserve_acls);
if (!array_list_add(root_files, file)) {
free(rel);
file_destroy(file);
ps->failed = true;
return;
scan_root_file(options, &inspected, rel, root_files, ps);
}
done:
free(rel);
free(cur_path);
}
/* Scan the root directory itself, collecting root files and subdirectories.
+1 -1
View File
@@ -276,7 +276,7 @@ static bool identity_wire_map_valid(const IdentityMap* map) {
}
if (map->to < IDENTITY_CURRENT)
return false;
if (map->to_name && strlen(map->to_name) > 255)
if (map->to_name && strlen(map->to_name) > IDENTITY_MAX_NAME_LEN)
return false;
return true;
}
+5
View File
@@ -6,6 +6,11 @@
#include <stdint.h>
#include <sys/types.h>
/* Maximum length of a receiver-resolved identity name in a FROM:TO map's TO
* field. Bounded so a malicious/huge name can never cross the wire (see
* identity_wire_map_valid). */
#define IDENTITY_MAX_NAME_LEN 255
/*
* Identity mapping: --numeric-ids / --usermap / --groupmap / --chown / --copy-as.
*
+1 -1
View File
@@ -134,7 +134,7 @@ Server* server_create_ex(int port, const ServerBindOptions* bind_opts) {
server->file_descriptor = file_descriptor;
server->ssl_ctx = NULL;
server->max_connections = 100;
server->max_connections = SERVER_DEFAULT_MAX_CONNECTIONS;
server->active_connections = 0;
server->limit_registry = NULL;
+4
View File
@@ -11,6 +11,10 @@
* stored here so the transport layer does not depend on daemon config. */
struct DaemonLimitRegistry;
/* Connection cap applied by server_create_ex() until the daemon's configured
* `max connections` overrides it via server_set_max_connections(). */
#define SERVER_DEFAULT_MAX_CONNECTIONS 100
typedef struct Server {
struct sockaddr_storage address;
unsigned int address_length;