diff --git a/cmd/shellboto/cmd_db.go b/cmd/shellboto/cmd_db.go index 6cab6cc..202af05 100644 --- a/cmd/shellboto/cmd_db.go +++ b/cmd/shellboto/cmd_db.go @@ -171,7 +171,13 @@ func cmdDBVacuum(args []string) int { fmt.Fprintln(os.Stderr, err) return exitErr } - defer lock.Close() + // Lockfile is never written — only flock'd. Close() returning an error + // is unexpected; surface it rather than silently defer. + defer func() { + if err := lock.Close(); err != nil { + fmt.Fprintf(os.Stderr, "lockfile close: %v\n", err) + } + }() gormDB, cleanup, err := openDBForCLI(cfg.DBPath) if err != nil { diff --git a/cmd/shellboto/main.go b/cmd/shellboto/main.go index e0c2057..dc67e43 100644 --- a/cmd/shellboto/main.go +++ b/cmd/shellboto/main.go @@ -92,7 +92,13 @@ func main() { if err != nil { logger.Fatal("instance lock", zap.Error(err)) } - defer instanceLock.Close() + // Lockfile is never written — only flock'd. Close() returning an error + // is unexpected; log it rather than silently defer. + defer func() { + if err := instanceLock.Close(); err != nil { + logger.Warn("instance lock close", zap.Error(err)) + } + }() gormDB, err := db.Open(cfg.DBPath) if err != nil { diff --git a/internal/files/files.go b/internal/files/files.go index f6e371c..07bf563 100644 --- a/internal/files/files.go +++ b/internal/files/files.go @@ -123,7 +123,6 @@ func Receive(ctx context.Context, b *bot.Bot, doc *models.Document, caption stri if err != nil { return "", 0, fmt.Errorf("open dest: %w", err) } - defer out.Close() // Defense-in-depth — Telegram's bot API caps uploads at // 50 MB, but a misconfigured self-hosted bot API server (or a // future protocol change) could serve larger. Bound the Copy with @@ -133,13 +132,21 @@ func Receive(ctx context.Context, b *bot.Bot, doc *models.Document, caption stri limit := int64(maxDownloadBytes) + 1 n, err := io.Copy(out, io.LimitReader(resp.Body, limit)) if err != nil { + _ = out.Close() _ = os.Remove(dest) return "", 0, err } if n > int64(maxDownloadBytes) { + _ = out.Close() _ = os.Remove(dest) return "", 0, fmt.Errorf("upload exceeds %d-byte cap", maxDownloadBytes) } + // Check Close() on writable files so a flush / fsync failure surfaces + // as an error rather than silent data loss. + if err := out.Close(); err != nil { + _ = os.Remove(dest) + return "", 0, fmt.Errorf("close dest: %w", err) + } if chown != nil { _ = os.Chown(dest, int(chown.Uid), int(chown.Gid)) }