maildir,notmuch: avoid leaking open files

Previously, Message.NewReader returned the wrapped buffered reader
without a reference to the opened file, so the files descriptors
were left unclosed after reading.  Now, the file reader is returned
directly and closed on the call site.  Buffering is not needed here
because it is an implementation detail of go-message.

Fixes: https://todo.sr.ht/~rjarry/aerc/9
This commit is contained in:
Nguyễn Gia Phong 2022-01-20 01:10:08 +07:00 committed by Robin Jarry
parent beae17a6da
commit 904ffacb0e
7 changed files with 16 additions and 28 deletions

View file

@ -211,7 +211,7 @@ func parseAddressList(h *mail.Header, key string) ([]*mail.Address, error) {
// RawMessage is an interface that describes a raw message // RawMessage is an interface that describes a raw message
type RawMessage interface { type RawMessage interface {
NewReader() (io.Reader, error) NewReader() (io.ReadCloser, error)
ModelFlags() ([]models.Flag, error) ModelFlags() ([]models.Flag, error)
Labels() ([]string, error) Labels() ([]string, error)
UID() uint32 UID() uint32
@ -225,6 +225,7 @@ func MessageInfo(raw RawMessage) (*models.MessageInfo, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer r.Close()
msg, err := message.Read(r) msg, err := message.Read(r)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not read message: %v", err) return nil, fmt.Errorf("could not read message: %v", err)

View file

@ -1,9 +1,9 @@
package lib package lib
import ( import (
"bytes"
"io" "io"
"io/ioutil" "io/ioutil"
"os"
"path/filepath" "path/filepath"
"testing" "testing"
@ -36,23 +36,17 @@ func TestMessageInfoHandledError(t *testing.T) {
} }
type mockRawMessage struct { type mockRawMessage struct {
body []byte path string
}
func newMockRawMessage(body []byte) *mockRawMessage {
return &mockRawMessage{
body: body,
}
} }
func newMockRawMessageFromPath(p string) *mockRawMessage { func newMockRawMessageFromPath(p string) *mockRawMessage {
b, err := ioutil.ReadFile(p) return &mockRawMessage{
die(err) path: p,
return newMockRawMessage(b) }
} }
func (m *mockRawMessage) NewReader() (io.Reader, error) { func (m *mockRawMessage) NewReader() (io.ReadCloser, error) {
return bytes.NewReader(m.body), nil return os.Open(m.path)
} }
func (m *mockRawMessage) ModelFlags() ([]models.Flag, error) { return nil, nil } func (m *mockRawMessage) ModelFlags() ([]models.Flag, error) { return nil, nil }
func (m *mockRawMessage) Labels() ([]string, error) { return nil, nil } func (m *mockRawMessage) Labels() ([]string, error) { return nil, nil }

View file

@ -1,7 +1,6 @@
package maildir package maildir
import ( import (
"bufio"
"fmt" "fmt"
"io" "io"
@ -20,12 +19,8 @@ type Message struct {
} }
// NewReader reads a message into memory and returns an io.Reader for it. // NewReader reads a message into memory and returns an io.Reader for it.
func (m Message) NewReader() (io.Reader, error) { func (m Message) NewReader() (io.ReadCloser, error) {
f, err := m.dir.Open(m.key) return m.dir.Open(m.key)
if err != nil {
return nil, err
}
return bufio.NewReader(f), nil
} }
// Flags fetches the set of flags currently applied to the message. // Flags fetches the set of flags currently applied to the message.

View file

@ -159,6 +159,7 @@ func (w *Worker) searchKey(key uint32, criteria *searchCriteria,
if err != nil { if err != nil {
return false, err return false, err
} }
defer reader.Close()
bytes, err := ioutil.ReadAll(reader) bytes, err := ioutil.ReadAll(reader)
if err != nil { if err != nil {
return false, err return false, err

View file

@ -432,6 +432,7 @@ func (w *Worker) handleFetchFullMessages(msg *types.FetchFullMessages) error {
w.worker.Logger.Printf("could not get message reader: %v", err) w.worker.Logger.Printf("could not get message reader: %v", err)
return err return err
} }
defer r.Close()
w.worker.PostMessage(&types.FullMessage{ w.worker.PostMessage(&types.FullMessage{
Message: types.RespondTo(msg), Message: types.RespondTo(msg),
Content: &models.FullMessage{ Content: &models.FullMessage{

View file

@ -4,7 +4,6 @@
package notmuch package notmuch
import ( import (
"bufio"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -23,16 +22,12 @@ type Message struct {
} }
// NewReader returns a reader for a message // NewReader returns a reader for a message
func (m *Message) NewReader() (io.Reader, error) { func (m *Message) NewReader() (io.ReadCloser, error) {
name, err := m.Filename() name, err := m.Filename()
if err != nil { if err != nil {
return nil, err return nil, err
} }
f, err := os.Open(name) return os.Open(name)
if err != nil {
return nil, err
}
return bufio.NewReader(f), nil
} }
// MessageInfo populates a models.MessageInfo struct for the message. // MessageInfo populates a models.MessageInfo struct for the message.

View file

@ -367,6 +367,7 @@ func (w *worker) handleFetchFullMessages(msg *types.FetchFullMessages) error {
w.w.Logger.Printf("could not get message reader: %v", err) w.w.Logger.Printf("could not get message reader: %v", err)
return err return err
} }
defer r.Close()
w.w.PostMessage(&types.FullMessage{ w.w.PostMessage(&types.FullMessage{
Message: types.RespondTo(msg), Message: types.RespondTo(msg),
Content: &models.FullMessage{ Content: &models.FullMessage{