devices: --devices/--specials/-D/--copy-devices/--write-devices
CI / lint (pull_request) Failing after 56s
CI / build-and-test (pull_request) Skipped
CI / sanitizers (address) (pull_request) Skipped
CI / sanitizers (undefined) (pull_request) Skipped
CI / fuzz-build (pull_request) Skipped
CI / coverage (pull_request) Skipped
CI / valgrind (pull_request) Skipped

Recreate char/block nodes via mknodat (privilege-gated, EPERM->warn+skip) and
FIFOs via mkfifoat; new STATUS_SPECIAL frame + validated rdev; sockets skipped;
-copy-devices copies st_size; -write-devices O_NOFOLLOW+O_NONBLOCK warn+skip.
preserve_specials/copy_devices/write_devices cross the wire. PROTOCOL_VERSION
2.12.0->2.13.0. Review fixes: -m source-removal keeps recreated specials, FIFO
ENXIO skip, rdev bounds at chunk_deserialize, STATUS_ERROR on receive branch,
scanner_prepare_special dedup.
This commit is contained in:
2026-09-08 22:27:53 +02:00
parent 0de859b302
commit 007e8f90f2
23 changed files with 910 additions and 28 deletions
+20
View File
@@ -537,6 +537,11 @@ static const OptionEntry OPTION_TABLE[] = {
{"--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)},
/* -D is handled separately (it implies both --devices and --specials). */
{"--devices", NULL, OPT_FLAG, offsetof(Config, preserve_devices)},
{"--specials", NULL, OPT_FLAG, offsetof(Config, preserve_specials)},
{"--copy-devices", NULL, OPT_FLAG, offsetof(Config, copy_devices)},
{"--write-devices", NULL, OPT_FLAG, offsetof(Config, write_devices)},
{"--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)},
@@ -831,6 +836,12 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
} else if (opt_is(argv[i], "-V", "--version")) {
printf("fastsync version %s\n", PROTOCOL_VERSION);
return 1;
} else if (opt_is(argv[i], "-D", NULL)) {
/* rsync -D == --devices --specials. -D is otherwise unassigned in
FastSync (verified: no collision), so it is free to imply both. */
config->preserve_devices = true;
config->preserve_specials = true;
log_info_message(LOG_INFO_MISC, "Enabled preservation of device and special files (-D)");
} else if (opt_is(argv[i], "-a", "--archive")) {
config->use_compression =
!config->compress_choice || strcmp(config->compress_choice, "zstd") == 0;
@@ -1200,6 +1211,15 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
config->files_from_set = set;
}
/* Device/special preservation recreates a node from its metadata mode (whose
S_IFMT bits carry the node kind), so --devices/--specials/-D imply metadata
transmission. --copy-devices/--write-devices treat the entry as data but a
mtime/mode-preserving transfer still benefits from metadata, so all four
imply it (FastSync's broad -M bundle; ownership stays opt-in). */
if (config->preserve_devices || config->preserve_specials || config->copy_devices ||
config->write_devices)
config->use_metadata = true;
/* The "unchanged" decision for --compare-dest/--copy-dest/--link-dest must
* be made on the receiver against the basis directories, which requires the
* per-file STATUS_CHECK handshake: basis-dir options therefore imply