-
Notifications
You must be signed in to change notification settings - Fork 48
[POSIX] Clean up temp files in error cases #1020
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -151,10 +151,19 @@ func overwrite(name string, d []byte) error { | |
| if err != nil { | ||
| return fmt.Errorf("failed to create temp file: %w", err) | ||
| } | ||
| success := false | ||
| defer func() { | ||
| if !success { | ||
| if err := os.Remove(tmpName); err != nil { | ||
| slog.WarnContext(context.Background(), "Failed to remove temporary file", slog.String("tmpname", tmpName), slog.Any("error", err)) | ||
| } | ||
| } | ||
| }() | ||
|
|
||
| if err := os.Rename(tmpName, name); err != nil { | ||
| return fmt.Errorf("failed to rename temporary file to target %q: %w", name, err) | ||
| } | ||
| success = true | ||
| return nil | ||
| }) | ||
| } | ||
|
|
@@ -186,17 +195,31 @@ func createTemp(prefix string, d []byte) (name string, err error) { | |
| } | ||
| } | ||
|
|
||
| tmpName := name | ||
| success := false | ||
| defer func() { | ||
| if !success { | ||
| if err := os.Remove(tmpName); err != nil { | ||
| slog.WarnContext(context.Background(), "Failed to remove temporary file", slog.String("tmpname", tmpName), slog.Any("error", err)) | ||
| } | ||
| name = "" | ||
| } | ||
| }() | ||
| defer func() { | ||
| if errC := f.Close(); errC != nil && err == nil { | ||
| err = errC | ||
| if errC := f.Close(); errC != nil { | ||
| if err == nil { | ||
| err = errC | ||
| } | ||
| success = false | ||
| } | ||
| }() | ||
|
Comment on lines
+198
to
215
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This sorta looks like it's going to defeat the point of the Note that the comment on the func says it's on the caller to remove the temporary file once they've finished with it. Maybe you could have
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This only deletes files that would otherwise get leaked when we return an error. I agree it's hard to read though. I think dropping the success variable and using a named error would help. But would it completely mitigate your concern? e.g. before we tried to create a temp file and write to it. The create worked, but the write didn't work (perhaps threw an error, or the len check didn't work out). We don't give the filename to the user, but before this change the created file stays around. Whatever the problem was on disk is probably now getting worse.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gotcha, ok - yeah, I think named returns vars would help, I think you could eliminate |
||
|
|
||
| if n, err := f.Write(d); err != nil { | ||
| return "", fmt.Errorf("failed to write to temporary file %q: %w", name, err) | ||
| if n, writeErr := f.Write(d); writeErr != nil { | ||
| return "", fmt.Errorf("failed to write to temporary file %q: %w", name, writeErr) | ||
| } else if l := len(d); n < l { | ||
| return "", fmt.Errorf("short write on %q, %d < %d", name, n, l) | ||
| } | ||
|
|
||
| success = true | ||
| return name, nil | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this would be cleaner if you named the
errorin the func signature and just check forerr != nilin the deferred func - that's a pattern I've seen used for this sort of thing.