#include "usage.h" #include #include #include #include "scanner.h" void print_usage(void) { printf("Usage:\n"); printf(" fastsync [options] \n"); printf(" fastsync [options] --source-dir --dest-dir \n"); printf("\n"); printf("Destination formats:\n"); printf(" user@host:/path SSH transport (rsync-style)\n"); printf(" host:/path SSH transport (current user)\n"); printf(" host::module/path Daemon TCP transport (fastsync-server --daemon);\n"); printf(" module names a server-side module, path is relative\n"); printf(" within it (connect with --server-port)\n"); printf(" /local/path TCP transport (requires server on localhost:8080)\n"); printf("\n"); printf("Options:\n"); printf(" -c, --checksum Verify content by checksum instead of size+mtime\n"); printf(" -z, --compress [level] Enable compression. The default level is\n"); printf(" per-codec: zstd 3 (range 1-22), zlib/zlibx 6, lz4\n"); printf(" ignores the level\n"); printf(" -a, --archive rsync archive mode (-rlptgoD): links, perms, times,\n"); printf(" owner, group, devices and specials; not\n"); printf(" compression/multithreading\n"); printf(" -r, --recursive Recurse into directories (FastSync is always recursive)\n"); printf(" --inc-recursive Accepted for rsync CLI compatibility; no effect (FastSync\n"); printf(" always performs a full scan, so the destination is identical)\n"); printf(" --no-inc-recursive Accepted for rsync CLI compatibility; no effect\n"); printf(" -n, --dry-run Show what would be transferred\n"); printf(" --remove-source-files Remove regular source files after successful transfer\n"); printf(" -p, --perms Preserve permission bits\n"); printf(" -t, --times Preserve modification times\n"); printf(" -o, --owner Preserve owner (uid)\n"); printf(" -g, --group Preserve group (gid)\n"); printf(" --ssh-port SSH port (default: 22)\n"); printf(" -e, --rsh Remote shell to launch on the client for the SSH\n"); printf(" transport (default: ssh). The command may include\n"); printf(" arguments, e.g. -e \"ssh -p 2222\"\n"); printf(" --rsync-path Alias for --fastsync-server-path (path to the\n"); printf(" fastsync server binary on the remote side)\n"); printf(" --blocking-io SSH transport only: leave the socket without read/write\n"); printf(" timeouts so it blocks naturally (no effect on TCP)\n"); printf(" --outbuf=MODE stdout/stderr buffering: N (none/unbuffered),\n"); printf(" L (line-buffered), or B (block-buffered, default)\n"); printf(" --progress Show transfer progress\n"); printf(" -P Partial mode with progress (retention incomplete)\n"); printf(" -8, --8-bit-output Leave high-bit characters unescaped in output\n"); printf(" --iconv=LOCAL[,REMOTE] Convert file-NAME charsets at the wire boundary:\n"); printf(" LOCAL is the charset of our file names, REMOTE is the\n"); printf(" remote side's charset (defaults to LOCAL). Names are\n"); printf(" converted before transmission and back on receipt; a\n"); printf(" name that cannot be represented in the target charset\n"); printf(" fails that transfer cleanly (rsync-compatible)\n"); printf(" --no-iconv Disable --iconv charset conversion (same as --iconv=-)\n"); printf(" --protocol=NUM Force the wire protocol version (must equal the current\n"); printf(" PROTOCOL_VERSION; FastSync cannot speak older/virtual\n"); printf(" wire formats)\n"); printf(" --write-batch=FILE Run the normal live transfer AND also emit a\n"); printf(" self-contained batch file of the whole source tree\n"); printf(" (implies the single-threaded transfer path)\n"); printf(" --only-write-batch=FILE\n"); printf(" Emit the batch file only (no destination, no server)\n"); printf(" --read-batch=FILE Apply the batch file to the destination (no source, no\n"); printf(" server); takes only the destination as an argument\n"); printf(" NOTE: the FastSync batch format is NOT interoperable with rsync's batch\n"); printf(" files (different container format); do not mix the two tools.\n"); printf(" --delete Delete files on receiver not in source\n"); printf(" (default timing: delete-during, like rsync --del)\n"); printf(" --delete-before Delete extras before the transfer starts\n"); printf(" (implies --delete)\n"); printf(" --delete-during Delete a directory's extras as that directory is\n"); printf(" processed (implies --delete)\n"); printf(" --del Alias for --delete-during\n"); printf(" --delete-delay Record the extras during the scan but remove them\n"); printf(" only after a successful transfer (implies --delete)\n"); printf(" --delete-after Delete only after the whole transfer succeeded\n"); printf(" (implies --delete)\n"); printf(" --delete-commit FastSync-only: restore the late whole-tree commit\n"); printf(" (identical to --delete-after; implies --delete)\n"); printf(" --delete-excluded Also delete destination files that were excluded on\n"); printf(" the source (default protects them, matching rsync)\n"); printf(" --max-delete=NUM Delete at most NUM destination entries per run; if the\n"); printf(" extras exceed NUM, the rest are skipped and the run is\n"); printf(" reported as partial (exit 25, matching rsync). Only\n"); printf(" applies together with --delete\n"); printf(" --ignore-errors Continue (and still delete) when a source directory is\n"); printf(" unreadable during the scan, instead of aborting with no\n"); printf(" deletion\n"); printf(" --force A file may replace a destination directory by removing\n"); printf(" that (non-empty) directory first\n"); printf(" --ignore-missing-args A --files-from entry that does not exist under the\n"); printf(" source is silently skipped instead of failing the run\n"); printf(" --delete-missing-args Implies --ignore-missing-args; also deletes each missing\n"); printf(" entry's destination mirror receiver-side. Independent of\n"); printf(" --delete (it does not imply --delete; a non-empty directory\n"); printf(" mirror is removed only with --force or --delete)\n"); printf(" -m, --prune-empty-dirs Do not create empty directories (a recursive transfer\n"); printf(" otherwise recreates them, like rsync)\n"); printf(" Note: each timing flag implies --delete. Combining a timing flag with\n"); printf(" --no-delete (in either order) is rejected as a config error, as is more\n"); printf(" than one timing flag.\n"); printf(" --ignore-existing Skip files that already exist on receiver\n"); printf(" --delay-updates Put updated files into place only at the end of transfer\n"); printf(" --dirs, -d, --old-dirs, --old-d Transfer the named directory entries without\n"); printf(" recursing into their contents (-d mirrors the source\n"); printf(" directory empty; with --files-from listed dirs are created\n"); printf(" empty and listed files are transferred)\n"); printf(" -R, --relative With --files-from, preserve each listed entry's relative path\n"); printf(" below the destination root instead of mirroring the full\n"); printf(" source path (no effect without --files-from)\n"); printf(" --no-implied-dirs With -R, do not apply the source metadata of a listed file's\n"); printf(" implied parent directories (they are still created with\n"); printf(" default attributes)\n"); printf(" --mkpath Create the destination root directory on the server when it\n"); printf(" does not exist yet\n"); printf(" --exclude , --exclude= Exclude files matching pattern\n"); printf(" --include , --include= Only include files matching pattern\n"); printf(" --exclude-from , --exclude-from= Read exclude patterns from file\n"); printf(" --include-from , --include-from= Read include patterns from file\n"); printf(" --files-from Read the source file list from FILE (paths relative to the " "source root)\n"); printf(" -0, --from0 Entries in --files-from are NUL-delimited\n"); printf(" -f, --filter=RULE rsync-style filter rule: exclude/- include/+ hide/H show/S\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(" -F Apply per-directory .rsync-filter files; repeated -FF also\n"); printf(" excludes the .rsync-filter files themselves\n"); printf(" --max-size Skip files larger than n bytes\n"); printf(" --min-size Skip files smaller than n bytes\n"); printf(" --max-alloc Maximum single allocation (default: 1G; 0 = no limit,\n"); printf(" matching rsync)\n"); printf(" --incremental Skip files unchanged since last transfer\n"); printf(" --size-only Skip incremental files matching in size, ignoring mtime\n"); printf(" -I, --ignore-times Transfer files even when size and mtime match\n"); printf(" -@, --modify-window Modification time tolerance\n"); printf(" -u, --update Skip files newer than the source on receiver\n"); printf(" --existing Skip files not already present at destination\n"); printf(" --compare-dest Treat DIR (relative to destination root) as an extra\n"); printf(" comparison basis: unchanged files are not transferred\n"); printf(" (requires --incremental, which is implied)\n"); printf(" --copy-dest Like --compare-dest, but copies the unchanged file from DIR\n"); printf(" into the destination instead of transferring its data\n"); printf(" --link-dest Like --copy-dest, but hard-links the unchanged file from DIR\n"); printf(" into the destination (repeatable; earlier DIRs win)\n"); printf(" --verify-basis FastSync-only: require a basis hit's content to match the\n"); printf(" source by whole-file digest instead of trusting rsync's\n"); printf(" size+mtime (or --size-only) quick-check\n"); printf(" --checksum-choice, --cc Whole-file checksum algorithm for --incremental/\n"); printf(" --checksum compares. Accepted: xxh128 (default), xxh3, xxh64\n"); printf(" (aka xxhash), md5, md4, sha1, or none. A two-name\n"); printf(" 'transfer,pre-transfer' form is accepted like rsync; 'none' as\n"); printf(" the pre-transfer algorithm is rejected with --checksum\n"); printf(" --checksum-seed Seed for the whole-file xxHash digest (and the delta\n"); printf(" block strong hash, low 32 bits); md5 ignores the seed. A seed\n"); printf(" of 0 (the default) is randomized per transfer, exactly like\n"); printf(" rsync, and the chosen seed is sent to the receiver\n"); printf(" --delta Delta transfer for changed files (requires --incremental)\n"); printf(" -W, --whole-file Transfer changed files without delta processing\n"); printf(" --no-whole-file rsync spelling that clears -W/--whole-file\n"); printf(" -y, --fuzzy Use a similar-named file already in the destination\n"); printf(" directory as the delta basis when the destination has no\n"); printf(" usable file at the exact path (saves bandwidth; implies\n"); printf(" --incremental and --delta; inert with --whole-file,\n"); printf(" --no-delta, or --no-incremental)\n"); printf(" --no-fuzzy Disable --fuzzy\n"); printf(" -B , --block-size , --delta-block \n"); printf(" Delta block size in bytes (default: %u)\n", DELTA_BLOCK_SIZE_DEFAULT); printf(" --delta-max Max file size for delta transfer (default: %llu)\n", DELTA_MAX_FILE_SIZE); printf(" -j, --threads[=N] Enable the multithreaded scanner/loader/sender\n"); printf(" pipeline; N (1-%d) sets the parallel scanner worker\n", MAX_SCANNER_THREADS); printf(" count (bare -j/--threads uses the default)\n"); printf(" --chunk-serialization Enable chunk serialization (long form only)\n"); printf(" -s, --secluded-args Protect-args compatibility option (no effect; remote\n"); printf(" SSH argv is already built injection-safe)\n"); printf(" --sendfile Enable sendfile zero-copy (TCP only; long form only;\n"); printf(" -f is bound to --filter, not --sendfile)\n"); printf(" --compress-choice Compression algorithm: zstd (default), lz4, zlib,\n"); printf(" zlibx, none, or auto\n"); printf(" --zc Alias for --compress-choice\n"); printf(" -v, --verbose Enable debug logging\n"); printf(" -q, --quiet Suppress non-error output\n"); printf(" --debug=FLAGS Fine-grained debug logging (use --debug=help for flags)\n"); printf(" --info=FLAGS Fine-grained info: copy,name,misc,skip,stats,all,none\n"); printf(" (use --info=help for flags; none suppresses --verbose)\n"); printf(" --preserve Preserve permissions and times (= -pt; long form only)\n"); printf(" --no-perms Negate -p/--perms\n"); printf(" --no-times Negate -t/--times\n"); printf(" --no-owner Negate -o/--owner\n"); printf(" --no-group Negate -g/--group\n"); printf(" --no-preserve Disable metadata preservation (negates --preserve)\n"); printf(" -E, --executability Preserve executable permission bits\n"); printf(" -U, --atimes Preserve access times\n"); printf(" -N, --crtimes Capture birth time; cannot be applied (documented\n"); printf(" divergence)\n"); printf(" -O, --omit-dir-times Do not apply modification times to directories\n"); printf(" -J, --omit-link-times Do not apply times to symlinks\n"); printf(" --open-noatime Open source files with O_NOATIME so reading for a\n"); printf(" transfer does not update their access time\n"); printf(" -X, --xattrs Preserve user extended attributes (user.* only;\n"); printf(" privileged security.*/trusted.* namespaces are\n"); printf(" never captured or applied)\n"); printf(" -A, --acls Preserve POSIX ACLs (the system.posix_acl_* xattrs;\n"); printf(" setting an ACL the receiver is not permitted to\n"); printf(" set is warned and skipped, never fatal)\n"); printf(" --fake-super Store the source mode/rdev/uid/gid in rsync's\n"); printf(" reserved user.rsync.%%stat xattr on each written\n"); printf(" file (interoperable with rsync); it never performs a\n"); printf(" real chown, so an unprivileged receiver records the\n"); printf(" privileged stat for a later restore\n"); printf(" --super Permit the receiver to attempt super-user activities\n"); printf(" (char/block device-node creation, --write-devices)\n"); printf(" within the confined receive root. Never elevates\n"); printf(" privileges and never bypasses confinement; ownership\n"); printf(" is still applied only with -o/--owner, -g/--group, or an\n"); printf(" explicit identity flag (--chown/--usermap/--groupmap/\n"); printf(" --copy-as); --numeric-ids only changes how ids map\n"); printf(" --no-super Forbid those super-user activities even when the\n"); printf(" receiver is running as root\n"); printf( " --chmod Modify new/transferred permissions (rsync syntax; implies no -p)\n"); printf(" --numeric-ids Map uid/gid by id instead of by name (a modifier, not\n"); printf(" an ownership request: combine with -o/-g or a map)\n"); printf(" --usermap=MAP Map usernames when applying ownership: comma-separated\n"); printf(" FROM:TO rules, first match wins. FROM is a name (from\n"); printf(" the source), an id, an inclusive LOW-HIGH range, *\n"); printf(" (any id), or empty (ids with no name). TO is an id, *\n"); printf(" (current user), or a name resolved on the receiver.\n"); printf(" e.g. 0-99:nobody,*:normal (cannot mix with --chown)\n"); printf(" --groupmap=MAP Map group names when applying ownership (same syntax)\n"); printf(" --chown=USER:GROUP Override the ownership of transferred files. Forms:\n"); printf(" USER:GROUP, USER (owner only), :GROUP (group only); a\n"); printf(" value of * means the current/root user as appropriate.\n"); printf(" Names resolve on the source machine; @N for numerics.\n"); printf(" (Implies owner/group metadata; -M now means rsync's\n"); printf(" --remote-option.)\n"); printf(" --copy-as=USER[:GROUP] Force every written entry (files, dirs, symlinks\n"); printf(" and special nodes) to USER[:GROUP], resolved on the\n"); printf(" source machine like --chown. Requires a privileged\n"); printf(" (root) receiver and implies owner/group metadata; an\n"); printf(" unprivileged receiver refuses the transfer. Never\n"); printf(" switches process credentials (safe-subset; see\n"); printf(" RSYNC_COMPAT.md). A daemon refuses it.\n"); printf(" --chunk-size Chunk size in bytes (default: %d)\n", DEFAULT_CHUNK_SIZE); printf(" --source-dir Source directory\n"); printf(" --dest-dir Destination directory\n"); printf(" --save-to-disk Write received files to disk\n"); printf(" --server-host Server IP address (default: 127.0.0.1)\n"); printf(" --server-port Server port (default: 8080)\n"); printf(" --port Alias for --server-port\n"); printf(" --password-file Authenticate a host::module/path daemon destination.\n"); printf(" FastSync-native SCRAM/PBKDF2 credential scheme (NOT\n"); printf(" rsync's --password-file): the file's first user:password\n"); printf(" line supplies the username and password; no password or\n"); printf(" reusable digest is sent (keep the file mode 0600)\n"); printf(" --no-motd Suppress display of the daemon's MOTD (the server\n"); printf(" still sends it; the client just does not show it)\n"); printf(" --bwlimit=RATE Limit socket I/O bandwidth (default unit KiB/s,\n"); printf(" rsync-style: 0 = no limit; K/M/G/T/P suffixes are\n"); printf(" binary, KB/MB decimal, KiB/MiB binary; decimals allowed)\n"); printf(" --tls Enable TLS encryption\n"); printf(" --cert TLS certificate file (PEM)\n"); printf(" --key TLS private key file (PEM)\n"); printf(" --ca TLS CA certificate file (PEM)\n"); printf(" --timeout I/O timeout in seconds (default: 0 = disabled, matching\n"); printf(" rsync). 0 disables it; --no-timeout is the same\n"); printf(" --contimeout Connection timeout in seconds (default: 60, matching\n"); printf(" rsync); 0 disables it (--no-contimeout)\n"); printf(" --stop-after=MINS Stop the transfer after MINS minutes (a positive\n"); printf(" integer); whatever was already transferred is kept\n"); printf(" --stop-at=TIME Stop at an absolute time. Accepts rsync's date form\n"); printf(" (Y-M-DTh:m, Y/M/DTh:m, abbreviable fields such as 12-31,\n"); printf(" 14:00, :59, 1) plus FastSync's HH:MM[:SS] and now+N[smhd]\n"); printf(" (a time already in the past stops the transfer\n"); printf(" immediately; client-only). An early stop skips the late\n"); printf(" --delete keep-set so it cannot delete source mirrors that\n"); printf(" were not yet scanned\n"); printf(" --address Bind the outgoing client socket to this source address\n"); printf(" -4, --ipv4 Force IPv4 for destination resolution\n"); printf(" -6, --ipv6 Force IPv6 for destination resolution\n"); printf(" --sockopts=OPTS Comma-separated OPT=VAL socket options applied before connect:\n"); printf(" TCP_NODELAY, SO_KEEPALIVE, SO_RCVBUF, SO_SNDBUF, SO_REUSEADDR\n"); printf(" -b, --backup Backup existing files before overwriting\n"); printf(" --backup-dir Directory for backups (requires --backup)\n"); printf(" --suffix Backup suffix (default: ~)\n"); printf(" --stats Print transfer statistics at end\n"); printf(" -i, --itemize-changes Print an rsync-style per-file change line\n"); printf(" --out-format=FORMAT Output format (%%f %%n %%l %%b %%c %%C %%i %%M %%%%)\n"); printf(" --list-only List source files instead of transferring\n"); printf(" --log-file-format=FORMAT Per-file log line format (needs --log-file)\n"); printf(" -h, --human-readable Print byte sizes in human-readable form\n"); printf(" --max-depth Maximum directory depth (0=unlimited)\n"); printf(" -x, --one-file-system Do not cross filesystem boundaries\n"); printf(" --log-file , --log-file= Write log messages to file\n"); printf(" --stderr=MODE Route logging to stderr: errors or all\n"); printf(" --msgs2stderr Route all messages to stderr (deprecated spelling of\n"); printf(" --stderr=all)\n"); printf(" --no-msgs2stderr Select errors-only stderr (deprecated spelling; the\n"); printf(" default)\n"); printf(" --partial Keep partial files on interrupted transfer\n"); printf(" --partial-dir Directory for partial files (implies --partial)\n"); printf(" -T, --temp-dir Scratch dir for temp files before atomic install.\n"); printf(" Confined to the receive root: a relative dir resolves below\n"); printf(" it and an absolute/traversal dir is rejected. The dir must\n"); printf(" already exist; a different filesystem falls back to a\n"); printf(" non-atomic copy instead of aborting\n"); printf(" --fastsync-server-path \n"); printf(" Path to fastsync-server on remote (default: fastsync-server)\n"); printf(" --old-args Accepted for rsync CLI compatibility; no effect (the\n"); printf(" remote server path is always safely quoted now)\n"); printf(" -M, --remote-option=OPT Append OPT to the REMOTE server invocation. SSH\n"); printf(" transport ONLY (user@host:path): a daemon (host::module) or\n"); printf(" local TCP destination rejects it (no remote command line to\n"); printf(" append to). Repeatable; each value is single-quote-escaped on\n"); printf(" the remote command line; empty values and values with control\n"); printf(" characters are rejected; -M OPT, -M=OPT and\n"); printf(" --remote-option=OPT work\n"); printf(" --trust-sender RECEIVER-LOCAL policy: trust the remote sender's file list\n"); printf(" and skip the receiver's own up-front path-traversal/\n"); printf(" containment re-validation of the incoming list (fewer checks,\n"); printf(" faster, potentially unsafe). It is never sent to the peer, so\n"); printf(" for a push it must be enabled on the receiving SERVER\n"); printf(" (fastsync-server --trust-sender) or forwarded with\n"); printf(" -M--trust-sender; the client flag alone has no effect\n"); printf(" -l, --links Copy symlinks as symlinks\n"); printf(" -L, --copy-links Transform symlinks into referent files\n"); printf(" --safe-links Skip symlinks whose target points outside the tree\n"); printf(" --copy-unsafe-links Copy unsafe symlinks (outside tree) as referent files\n"); printf(" -k, --copy-dirlinks Transform symlinks to directories into real dirs\n"); printf(" -K, --keep-dirlinks Keep an existing symlink-to-dir as that dir\n"); printf(" --munge-links Munge stored symlink targets (/rsyncd-munged/) on the receiver\n"); printf(" -H, --hard-links Preserve hard-link relationships across the transfer\n"); printf(" -S, --sparse Handle sparse files efficiently\n"); printf( " -D Preserve device and special files (implies --devices --specials)\n"); printf( " --devices Recreate device nodes on the destination (privileged; skipped when\n"); printf(" the receiver lacks CAP_MKNOD)\n"); printf(" --specials Recreate special files (FIFOs, sockets) on the destination\n"); printf(" --copy-devices Copy a source device's content as a regular file instead\n"); printf(" --write-devices Write received data into an existing destination device node\n"); printf(" --inplace Update files in-place (no temp+rename)\n"); printf( " --preallocate Allocate destination file space up front (fail-fast on full disk)\n"); printf(" --append Resume a shorter destination by appending only its tail\n"); printf(" (prefix is not verified; requires --incremental)\n"); printf(" --append-verify Like --append, but verifies the retained prefix checksum\n"); printf(" before appending (falls back to a full transfer on mismatch)\n"); printf(" --fsync Fsync every written file before publication\n"); printf(" --compress-level Compression level (per-codec default: zstd 3,\n"); printf(" zlib/zlibx 6, lz4 ignores it)\n"); printf(" --zl Alias for --compress-level\n"); printf(" --skip-compress=LIST Skip compression for suffixes in LIST (separated by\n"); printf(" '/' as in rsync, or ','); a leading dot is optional. The\n"); printf(" default is rsync 3.4.1's built-in skip-compress list\n"); printf(" --compress-threads Compression worker threads (requires zstd threaded support)\n"); printf(" --no-OPTION Disable a supported boolean option\n"); printf(" --help Show this help\n"); printf(" -V, --version Show version\n"); } void print_debug_usage(void) { printf("Emitting debug flags: IO,PROTO,PACK,UTIL,FLIST,DEL,HASH,DELTASUM,\n"); printf("RECV,FILTER,SEND,ALL,NONE\n"); printf("Also accepted for rsync CLI parity (silent): ACL,BACKUP,BIND,CHDIR,\n"); printf("CONNECT,CMD,DUP,EXIT,FUZZY,GENR,HLINK,ICONV,NSTR,OWN,TIME.\n"); printf("Flags may be comma-separated, for example: --debug=io,proto\n"); printf("An optional level suffix is accepted (e.g. --debug=io2); level 0\n"); printf("silences that item. Unknown names are rejected.\n"); } void print_info_usage(void) { printf("Emitting info flags: COPY,MISC,SKIP,STATS,DEL,REMOVE,NAME,FLIST,\n"); printf("NONREG,PROGRESS,MOUNT,ALL,NONE\n"); printf("Also accepted for rsync CLI parity (silent): BACKUP,SYMS,SYMSAFE.\n"); printf("Flags may be comma-separated, for example: --info=name,stats\n"); printf("An optional level suffix is accepted (e.g. --info=stats2); level 0\n"); printf("silences that item. Unknown names are rejected.\n"); }