CI / lint (pull_request) Successful in 26s
CI / sanitizers (address) (pull_request) Failing after 41s
CI / sanitizers (undefined) (pull_request) Successful in 40s
CI / fuzz-build (pull_request) Successful in 18s
CI / coverage (pull_request) Successful in 35s
CI / valgrind (pull_request) Failing after 37s
CI / build-and-test (pull_request) Failing after 2m35s
RSYNC_COMPAT Phase-2 row M: path-list construction and destination directory creation while preserving traversal safety. - -R/--relative with --files-from: transmit each listed entry under its bare relative destination path (no source-root mirror). Files keep an absolute local read path plus a separate wire/dest path (File.send_path); manifest, incremental quick-check and change output follow the wire path, so --delete and --remove-source-files stay consistent. -R without --files-from is unchanged (full mirror). - --no-implied-dirs: client-only, only meaningful with -R + --files-from. A listed file whose parent dir is not itself (or via an ancestor) explicitly listed cannot be placed; the run fails up front with a clear error. No effect otherwise. - --dirs/-d + --old-dirs/--old-d aliases: -d <dir> transmits the source root as an explicit empty directory entry (STATUS_MKDIR frame); with --files-from listed dirs are created empty and listed files transferred, never descending. Works single-threaded, -m (sequential scanner in the -m scan thread) and chunk-serialization (per-file type marker). Directory entries appear in the delete manifest. - --mkpath: new wire bool; server creates the destination root (and missing leading components under its authorized root) at connection start. A missing destination root is now rejected by default. - Protocol bumped to 2.7.0 (mkpath wire field + STATUS_MKDIR + chunk type marker). All receive paths funnel through file_save_to_disk_full which creates directories via the secure confined mkdir engine; dir entries are excluded from --remove-source-files outcome acknowledgements on both ends. - Unit coverage: CLI parse (relative/dirs aliases/mkpath/no-implied-dirs), config round-trip (relative + mkpath), scanner --dirs non-recursion and -R send_path (sequential + parallel), receiver dir-entry save. - Integration coverage: TestRelativeFilesFrom, TestNoImpliedDirs, TestDirs, TestMkpath (single and -m). - RSYNC_COMPAT: 4 rows move to Implemented (Summary 67/3/5/1/71 = 147).
137 lines
5.2 KiB
C
137 lines
5.2 KiB
C
#ifndef SCANNER_H
|
|
#define SCANNER_H
|
|
|
|
#include "chunk.h"
|
|
#include "file_list.h"
|
|
#include "filter.h"
|
|
#include "protocol.h"
|
|
#include "queue.h"
|
|
#include <dirent.h>
|
|
#include <stdbool.h>
|
|
#include <stdatomic.h>
|
|
#include <sys/types.h>
|
|
#include <threads.h>
|
|
|
|
typedef struct {
|
|
bool use_metadata;
|
|
unsigned long long chunk_size;
|
|
char** exclude_patterns;
|
|
int exclude_count;
|
|
char** include_patterns;
|
|
int include_count;
|
|
unsigned long long max_size;
|
|
unsigned long long min_size;
|
|
int max_depth;
|
|
int num_threads;
|
|
bool follow_symlinks;
|
|
bool copy_links;
|
|
bool safe_links;
|
|
bool copy_unsafe_links;
|
|
bool checksum;
|
|
bool one_file_system;
|
|
/* Phase 2 (files-from / filter layer). All pointers are shared read-only
|
|
* across scanner instances and worker threads; ownership stays with the
|
|
* caller (client_send). */
|
|
const FileListSet* file_list; /* --files-from allow-set, or NULL */
|
|
const FilterRuleList* base_filters; /* command-line + -C rules, or NULL */
|
|
bool per_dir_filters; /* -F: read .rsync-filter per directory */
|
|
bool dirs; /* -d/--dirs: transfer dir entries, no recursion */
|
|
bool relative; /* -R/--relative (dest rel paths, with --files-from) */
|
|
} ScannerOptions;
|
|
|
|
/* Internal per-scanner filter state. FilterNode chains represent the ordered
|
|
* per-directory .rsync-filter rules that apply below a directory. */
|
|
typedef struct FilterNode FilterNode;
|
|
|
|
typedef struct {
|
|
Queue* directories;
|
|
DIR* current_dir;
|
|
char* current_path;
|
|
bool use_metadata;
|
|
unsigned long long chunk_size;
|
|
char** exclude_patterns;
|
|
int exclude_count;
|
|
char** include_patterns;
|
|
int include_count;
|
|
unsigned long long max_size;
|
|
unsigned long long min_size;
|
|
int max_depth;
|
|
int current_depth;
|
|
bool follow_symlinks;
|
|
bool copy_links;
|
|
bool safe_links;
|
|
bool copy_unsafe_links;
|
|
bool checksum;
|
|
bool one_file_system;
|
|
dev_t root_dev;
|
|
bool failed;
|
|
/* Phase 2 (files-from / filter layer). */
|
|
char* root_path; /* transfer root (fs path) for rel computation */
|
|
char* current_rel; /* rel path of the open directory ("" == root) */
|
|
bool at_seed_dir; /* next open is the seed directory */
|
|
FilterNode* seed_node; /* inherited context of the seed dir, or NULL */
|
|
FilterNode* current_node; /* filter context of the open directory */
|
|
ArrayList* filter_nodes; /* owned FilterNode arena (may be NULL) */
|
|
const FileListSet* file_list;
|
|
const FilterRuleList* base_filters;
|
|
bool per_dir_filters;
|
|
/* --dirs / -R state for the directory-entry generator (dirs_mode replaces
|
|
the recursive scan). */
|
|
bool dirs_mode;
|
|
bool relative_mode; /* file_list && relative: send bare relative wire paths */
|
|
bool dirs_root_emitted;
|
|
int list_index;
|
|
ArrayList* dirs_batch; /* owned when non-NULL */
|
|
unsigned long long dirs_batch_size;
|
|
} DirectoryScanner;
|
|
|
|
typedef struct {
|
|
Queue* result_queue;
|
|
mtx_t result_mutex;
|
|
cnd_t result_not_empty;
|
|
cnd_t result_not_full;
|
|
int num_threads;
|
|
int expected_threads;
|
|
int created_threads;
|
|
thrd_t* threads;
|
|
bool done;
|
|
bool failed;
|
|
atomic_bool cancelled;
|
|
int completed;
|
|
Chunk* initial_chunk;
|
|
ProtocolSession* allocation_session;
|
|
FilterNode* root_filter_node; /* root .rsync-filter context (owned by ps) */
|
|
} ParallelScanner;
|
|
|
|
DirectoryScanner* directory_scanner_create(const char* root_directory, bool use_metadata,
|
|
unsigned long long chunk_size, char** exclude_patterns,
|
|
int exclude_count, char** include_patterns,
|
|
int include_count, unsigned long long max_size,
|
|
unsigned long long min_size, int max_depth,
|
|
bool follow_symlinks, bool copy_links, bool safe_links,
|
|
bool copy_unsafe_links, bool checksum);
|
|
DirectoryScanner* directory_scanner_create_with_options(const char* root_directory,
|
|
const ScannerOptions* options);
|
|
Chunk* directory_scanner_next(DirectoryScanner* scanner);
|
|
bool directory_scanner_failed(const DirectoryScanner* scanner);
|
|
void directory_scanner_destroy(DirectoryScanner* scanner);
|
|
|
|
/* --one-file-system (-x) decision: a directory entry may be descended into
|
|
* only when the option is disabled or the entry lives on the same device as
|
|
* the transfer root. Exposed so tests can exercise the rule directly. */
|
|
bool scanner_same_filesystem(bool one_file_system, dev_t root_device, dev_t entry_device);
|
|
|
|
/* Relative path of an on-disk path below `root` ("" == the root itself, NULL
|
|
* when `fs_path` is not under `root`). Handles trailing slashes and a root of
|
|
* "/". Exposed so tests can exercise the mapping directly. */
|
|
char* scanner_path_relative(const char* root, const char* fs_path);
|
|
|
|
ParallelScanner* parallel_scanner_create_with_options(const char* root_directory,
|
|
const ScannerOptions* options,
|
|
ProtocolSession* allocation_session);
|
|
Chunk* parallel_scanner_next(ParallelScanner* scanner);
|
|
bool parallel_scanner_failed(const ParallelScanner* scanner);
|
|
void parallel_scanner_destroy(ParallelScanner* scanner);
|
|
|
|
#endif
|