Merge feat/p4-metadata-capture: atimes/crtimes/open-noatime/omit-dir-times/omit-link-times

# Conflicts:
#	src/shared/config.c
#	tests/integration/test_features.py
This commit is contained in:
2026-09-08 21:00:07 +02:00
21 changed files with 765 additions and 27 deletions
+18
View File
@@ -4,6 +4,7 @@
#include "compression.h"
#include "config.h"
#include "delta.h"
#include "file.h"
#include "file_list.h"
#include "filter.h"
#include "identity.h"
@@ -534,6 +535,11 @@ static const OptionEntry OPTION_TABLE[] = {
{"--cvs-exclude", "-C", OPT_FLAG, offsetof(Config, cvs_exclude)},
{"-F", NULL, OPT_FLAG, offsetof(Config, per_dir_filter)},
{"--numeric-ids", NULL, OPT_FLAG, offsetof(Config, numeric_ids)},
{"--atimes", "-U", OPT_FLAG, offsetof(Config, preserve_atimes)},
{"--crtimes", "-N", OPT_FLAG, offsetof(Config, preserve_crtimes)},
{"--omit-dir-times", "-O", OPT_FLAG, offsetof(Config, omit_dir_times)},
{"--omit-link-times", "-J", OPT_FLAG, offsetof(Config, omit_link_times)},
{"--open-noatime", NULL, OPT_FLAG, offsetof(Config, open_noatime)},
};
/* Only boolean options with no required argument are safe to negate. */
@@ -796,6 +802,14 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
config. */
if (entry->offset == offsetof(Config, delete_missing_args))
config->ignore_missing_args = true;
/* -U/--atimes and -N/--crtimes carry their times inside the metadata
payload, which is only transmitted when use_metadata is set, so either
one implies metadata transmission. This is FastSync's broad -M bundle
(mode/mtime travel too); it does NOT enable ownership application,
which stays opt-in via the identity flags. */
if (entry->offset == offsetof(Config, preserve_atimes) ||
entry->offset == offsetof(Config, preserve_crtimes))
config->use_metadata = true;
continue;
}
@@ -1341,6 +1355,10 @@ int main(int argc, char* argv[]) {
goto cleanup;
}
/* --open-noatime is a sender-side policy: install it for every source read
(scan + data path) without touching the receiver. */
file_set_open_noatime(config->open_noatime);
/* Initialize TLS if needed */
if (config->use_tls)
tls_global_init();
+2
View File
@@ -87,6 +87,8 @@ static bool prepare_scanner(const Config* config, int num_threads, PreparedScann
ScannerOptions* options = &out->options;
options->use_metadata = config->use_metadata;
options->preserve_atimes = config->preserve_atimes;
options->preserve_crtimes = config->preserve_crtimes;
options->chunk_size = config->chunk_size;
options->exclude_patterns = config->exclude_patterns;
options->exclude_count = config->exclude_count;
+10 -4
View File
@@ -348,6 +348,8 @@ DirectoryScanner* directory_scanner_create_with_options(const char* root_directo
scanner->current_dir = NULL;
scanner->current_path = NULL;
scanner->use_metadata = options->use_metadata;
scanner->preserve_atimes = options->preserve_atimes;
scanner->preserve_crtimes = options->preserve_crtimes;
scanner->chunk_size = options->chunk_size > 0 ? options->chunk_size : DESIRED_CHUNK_SIZE;
scanner->exclude_patterns = options->exclude_patterns;
scanner->exclude_count = options->exclude_count;
@@ -593,7 +595,8 @@ static File* dirs_root_dir_file(DirectoryScanner* scanner) {
}
file->is_dir = true;
if (scanner->use_metadata) {
file->metadata = file_metadata_create(&st);
file->metadata = file_metadata_create(scanner->root_path, &st, scanner->preserve_atimes,
scanner->preserve_crtimes);
if (!file->metadata) {
file_destroy(file);
scanner->failed = true;
@@ -670,7 +673,8 @@ static File* dirs_file_for_entry(DirectoryScanner* scanner, const char* entry) {
}
}
if (scanner->use_metadata) {
file->metadata = file_metadata_create(&effective);
file->metadata = file_metadata_create(file->path, &effective, scanner->preserve_atimes,
scanner->preserve_crtimes);
if (!file->metadata) {
file_destroy(file);
scanner->failed = true;
@@ -924,7 +928,8 @@ Chunk* directory_scanner_next(DirectoryScanner* scanner) {
if (scanner->hardlinks && S_ISREG(stats.st_mode))
scanner_assign_hardlink(scanner, scanner->hardlinks, file, &stats);
if (scanner->use_metadata)
file->metadata = file_metadata_create(&stats);
file->metadata = file_metadata_create(file->path, &stats, scanner->preserve_atimes,
scanner->preserve_crtimes);
if (scanner->use_metadata && !file->metadata) {
free(rel_copy);
file_destroy(file);
@@ -1257,7 +1262,8 @@ static void scan_root_entry(const ScannerOptions* options, const FilterNode* roo
}
}
if (options->use_metadata)
file->metadata = file_metadata_create(&st);
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);
+6
View File
@@ -15,6 +15,10 @@
typedef struct {
bool use_metadata;
/* Phase 4 metadata capture: -U/--atimes and -N/--crtimes tell the scanner to
* capture the source access / birth time into each entry's FileMetadata. */
bool preserve_atimes;
bool preserve_crtimes;
unsigned long long chunk_size;
char** exclude_patterns;
int exclude_count;
@@ -81,6 +85,8 @@ typedef struct {
DIR* current_dir;
char* current_path;
bool use_metadata;
bool preserve_atimes;
bool preserve_crtimes;
unsigned long long chunk_size;
char** exclude_patterns;
int exclude_count;