Skip to content

Missing Close error check when writing output to a file #81

@artyom

Description

@artyom

Environment

  • OS: macOS 10.15.2
  • age version: e43cf8b

What were you trying to do

Check how output to file is handled (-o flag) to make sure data is flushed.

What happened

When output file is opened, its Close method is deferred, but result of this Close call isn't checked, so theoretically it is possible to end up with file that wasn't completely written even when age returned successfully.

age/cmd/age/age.go

Lines 126 to 131 in e43cf8b

f, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
if err != nil {
logFatalf("Error: failed to open output file %q: %v", name, err)
}
defer f.Close()
out = f

One way to improve this is to check for error in deferred function:

diff --git cmd/age/age.go cmd/age/age.go
index a604bb6..fa832a1 100644
--- cmd/age/age.go
+++ cmd/age/age.go
@@ -127,7 +127,11 @@ func main() {
 		if err != nil {
 			logFatalf("Error: failed to open output file %q: %v", name, err)
 		}
-		defer f.Close()
+		defer func() {
+			if err := f.Close(); err != nil {
+				logFatalf("Error: %v", err)
+			}
+		}()
 		out = f
 	} else if terminal.IsTerminal(int(os.Stdout.Fd())) && !decryptFlag {
 		if armorFlag {

...or explicitly call Close on asserted io.Closer in encode/decode functions:

diff --git cmd/age/age.go cmd/age/age.go
index a604bb6..a28749d 100644
--- cmd/age/age.go
+++ cmd/age/age.go
@@ -220,6 +220,11 @@ func encrypt(recipients []age.Recipient, in io.Reader, out io.Writer, armor bool
 	if err := w.Close(); err != nil {
 		logFatalf("Error: %v", err)
 	}
+	if c, ok := out.(io.Closer); ok {
+		if err := c.Close(); err != nil {
+			logFatalf("Error: %v", err)
+		}
+	}
 }
 
 func decrypt(keys []string, in io.Reader, out io.Writer) {
@@ -246,6 +251,11 @@ func decrypt(keys []string, in io.Reader, out io.Writer) {
 	if _, err := io.Copy(out, r); err != nil {
 		logFatalf("Error: %v", err)
 	}
+	if c, ok := out.(io.Closer); ok {
+		if err := c.Close(); err != nil {
+			logFatalf("Error: %v", err)
+		}
+	}
 }
 
 func passphrasePrompt() (string, error) {

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions