age -o replaces a symlink, pipe or device at the path; say the file gets mode 0600 #59

Closed
opened 2026-10-04 14:59:23 +02:00 by clawbot · 1 comment
Collaborator

From the second whole-branch review of #28 (#28 (comment)):

age encrypt -o and age decrypt -o (internal/cli/age/age.go, output and finish) always write a new file beside the named path and then rename it over that path. A symlink or a named pipe there is replaced by a regular file, and a reader of the pipe waits forever; -o /dev/stdout fails with "permission denied"; run as root, as in the development image, -o /dev/null replaces /dev/null with a 0600 file holding the ciphertext. An existing file also ends up with mode 0600, which the README does not say.

Work, on a branch cut from next, PR to next:

  • Look at the -o path without following a final symlink. Missing, or a regular file: write beside it and rename over it, as now. A symlink: apply the same rule to what it points at, so the link keeps pointing where it did. Anything else (a named pipe, a device such as /dev/null or /dev/stdout): write to it directly, with no file beside it and no rename.
  • The signal handling decided in #48 (comment) stays for the rename case; for a path written directly, a signal ends the tool like any other command.
  • README, keyfunc age encrypt and Errors: an existing regular file is replaced and the new one has mode 0600; a symlink is followed; a pipe or device is written directly, and the no-unfinished-file promise applies only to a new or regular file.

Definition of done

  • Tests cover a symlink to a regular file (the link survives and its target holds the output) and a named pipe at the -o path (a reader gets the output and the pipe is still a pipe).
  • -o /dev/null and -o /dev/stdout work and leave those paths as they were, checked by hand as root in the development image.
  • make check passes.

Model: opus-5-5

From the second whole-branch review of https://git.eeqj.de/sneak/keyfunc/pulls/28 (https://git.eeqj.de/sneak/keyfunc/pulls/28#issuecomment-123640): `age encrypt -o` and `age decrypt -o` (`internal/cli/age/age.go`, `output` and `finish`) always write a new file beside the named path and then rename it over that path. A symlink or a named pipe there is replaced by a regular file, and a reader of the pipe waits forever; `-o /dev/stdout` fails with "permission denied"; run as root, as in the development image, `-o /dev/null` replaces `/dev/null` with a `0600` file holding the ciphertext. An existing file also ends up with mode `0600`, which the README does not say. Work, on a branch cut from `next`, PR to `next`: - Look at the `-o` path without following a final symlink. Missing, or a regular file: write beside it and rename over it, as now. A symlink: apply the same rule to what it points at, so the link keeps pointing where it did. Anything else (a named pipe, a device such as `/dev/null` or `/dev/stdout`): write to it directly, with no file beside it and no rename. - The signal handling decided in https://git.eeqj.de/sneak/keyfunc/issues/48#issuecomment-122586 stays for the rename case; for a path written directly, a signal ends the tool like any other command. - README, `keyfunc age encrypt` and Errors: an existing regular file is replaced and the new one has mode `0600`; a symlink is followed; a pipe or device is written directly, and the no-unfinished-file promise applies only to a new or regular file. ## Definition of done - Tests cover a symlink to a regular file (the link survives and its target holds the output) and a named pipe at the `-o` path (a reader gets the output and the pipe is still a pipe). - `-o /dev/null` and `-o /dev/stdout` work and leave those paths as they were, checked by hand as root in the development image. - `make check` passes. Model: opus-5-5
clawbot self-assigned this 2026-10-04 14:59:23 +02:00
Author
Collaborator

Built in #60: the -o path is looked at once; a missing path or a regular file is replaced by rename as before, a symlink gets the same treatment for what it points at, and a pipe or device is written directly. A symlink that points at nothing is refused, a judgement call noted on the PR.

Model: opus-5-5

Built in https://git.eeqj.de/sneak/keyfunc/pulls/60: the `-o` path is looked at once; a missing path or a regular file is replaced by rename as before, a symlink gets the same treatment for what it points at, and a pipe or device is written directly. A symlink that points at nothing is refused, a judgement call noted on the PR. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/keyfunc#59