Absorb: ask for the passphrase once the archive starts arriving

With gpg --decrypt x.tar.gpg | vault absorb, vault printed its prompt
while gpg was still writing to the terminal, which buried it, so the
command looked hung. Peek at standard input first: the upstream program
has finished with the terminal by the time data arrives, and an empty
pipe now fails without asking at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andy Dixon
2026-10-02 17:27:07 +01:00
co-authored by Claude Opus 5.5
parent 1403be8b1c
commit 730877823e
3 changed files with 20 additions and 4 deletions
+5 -1
View File
@@ -581,7 +581,11 @@ vault absorb old < archive.tar.gz
Each file is encrypted straight from the stream, so nothing is unpacked to
disk first. Directories need no entry of their own; links, devices and any
name that tries to climb out with \file{..} are skipped with a note. If the
name that tries to climb out with \file{..} are skipped with a note.
vault asks for your passphrase only once the archive starts arriving, so
when it comes out of something that asks first, like
\cmd{gpg --decrypt secrets.tar.gpg | vault absorb}, gpg's prompt comes
and goes before vault's appears. If the
archive stops halfway through a file, that file isn't stored and vault
stops; everything before it is already safely in.
+6 -2
View File
@@ -35,8 +35,12 @@ or
is dropped.
.PP
The passphrase prompt reads from the terminal, not standard input, so
it still works with an archive piped in. The vault is locked exclusively
for the whole run.
it still works with an archive piped in. It waits until the archive starts
arriving, so a program feeding the pipe, such as
.BR gpg (1)
asking for its own passphrase, has finished with the terminal first.
An empty pipe fails without asking at all.
The vault is locked exclusively for the whole run.
.SH OPTIONS
.TP
.BI \-\-key " PATH"
+9 -1
View File
@@ -5,6 +5,7 @@
package main
import (
"bufio"
"errors"
"flag"
"fmt"
@@ -158,10 +159,17 @@ func main() {
if len(pos) == 1 {
dest = strings.Trim(pos[0], "/")
}
// Wait for the archive to start arriving before asking for the
// passphrase, so a program feeding the pipe (gpg, ssh) has finished
// with the terminal and cannot bury the prompt.
in := bufio.NewReader(os.Stdin)
if _, err := in.Peek(1); err != nil {
fail("nothing to absorb on standard input")
}
s := open(*keyPath, *vaultDir, true)
cat, err := s.catalogue()
check(err)
check(absorb(s, cat, os.Stdin, dest))
check(absorb(s, cat, in, dest))
case "list", "ls":
want(0, 1)
prefix := ""