Skip to content

--no-dry-run (and malformed --no--dry-run) accepted without error but has no effect #1104

Description

@CqN

Environment:
rsync version 3.2.7, protocol version 32

Description:
rsync documents (and implements) a general convention where many boolean long options can be turned off by prefixing them with --no- (e.g. --no-owner, --no-motd, --no-recursive, --no-human-readable). However, this does not hold for --dry-run/-n: appending --no-dry-run after --dry-run/-n on the same command line is accepted without any parse error, but does not cancel dry-run mode — the command continues to behave exactly as if --no-dry-run had not been given.

Additionally, a malformed variant with a doubled dash, --no--dry-run, is also accepted without error and is likewise a no-op — suggesting the --no- prefix handling performs loose/generic matching rather than validating that a genuine negatable pair exists for the given option before accepting the input.

Steps to reproduce:

  1. rsync -avz --dry-run --no-dry-run source/ dest/
    Expected: dry-run is cancelled; files are actually transferred.
    Actual: dry-run remains in effect; nothing is transferred; no error is printed.
  2. rsync -avz --dry-run --no--dry-run source/ dest/
    Same result as (1): silently accepted, dry-run remains active.

Root cause (from options.c, current source):
--dry-run is registered as:
{"dry-run", 'n', POPT_ARG_NONE, &dry_run, 0, 0, 0 },
Unlike options that have genuine negatable pairs explicitly registered, e.g.:
{"recursive", 'r', POPT_ARG_VAL, &recurse, 2, 0, 0 },
{"no-recursive", 0, POPT_ARG_VAL, &recurse, 0, 0, 0 },
there is no "no-dry-run" table entry anywhere in options.c. The option is a plain POPT_ARG_NONE flag with no registered negation. Whatever mechanism accepts --no-dry-run without a parse error is therefore matching/stripping the --no- prefix generically without verifying a corresponding negation actually exists for the target option, and silently doing nothing instead of either applying a real negation or rejecting the input as an unknown option.

Expected behavior (either would resolve this):
(a) Implement a genuine --no-dry-run that clears the dry_run flag, consistent with rsync's own general --no- convention users would reasonably expect it to follow, or
(b) Reject --no-dry-run (and other --no-X variants with no registered negation) as an unrecognized option, rather than silently accepting and ignoring it — silent acceptance-without-effect is a worse outcome than a clear error, since it can mislead a user into believing dry-run has been cancelled when it has not.

Impact:
Low severity (workaround is trivial — just omit --dry-run rather than trying to cancel it), but worth fixing given rsync's strong general reputation for precise, well-defined option semantics — this is a real inconsistency in that story, and could plausibly mislead a user into believing a real transfer occurred when it was actually still a dry run.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions