request server: call Close() on ListerAt if it implements io.Closer

The ListerAt is stored in the Request state and reused across requests.
Some implementations don't store the entire []os.FileInfo buffer in the
ListerAt implementation but instead return an open file and get/return
[]os.FileInfo on request. For these implementation calling Close is
required
This commit is contained in:
Nicola Murino 2024-02-09 10:22:54 +01:00
parent 46d90e3f96
commit fbb0b8bdb3
3 changed files with 51 additions and 2 deletions

View File

@ -144,6 +144,8 @@ type NameLookupFileLister interface {
// //
// If a populated entry implements [FileInfoExtendedData], extended attributes will also be returned to the client. // If a populated entry implements [FileInfoExtendedData], extended attributes will also be returned to the client.
// //
// The request server code will call Close() on ListerAt if an io.Closer type assertion succeeds.
//
// Note in cases of an error, the error text will be sent to the client. // Note in cases of an error, the error text will be sent to the client.
type ListerAt interface { type ListerAt interface {
ListAt([]os.FileInfo, int64) (int, error) ListAt([]os.FileInfo, int64) (int, error)

View File

@ -793,6 +793,37 @@ func TestRequestReaddir(t *testing.T) {
checkRequestServerAllocator(t, p) checkRequestServerAllocator(t, p)
} }
type testListerAtCloser struct {
isClosed bool
}
func (l *testListerAtCloser) ListAt([]os.FileInfo, int64) (int, error) {
return 0, io.EOF
}
func (l *testListerAtCloser) Close() error {
l.isClosed = true
return nil
}
func TestRequestServerListerAtCloser(t *testing.T) {
p := clientRequestServerPair(t)
defer p.Close()
handle, err := p.cli.opendir(context.Background(), "/")
require.NoError(t, err)
require.Len(t, p.svr.openRequests, 1)
req, ok := p.svr.getRequest(handle)
require.True(t, ok)
listerAt := &testListerAtCloser{}
req.setListerAt(listerAt)
assert.NotNil(t, req.state.getListerAt())
err = p.cli.close(handle)
assert.NoError(t, err)
require.Len(t, p.svr.openRequests, 0)
assert.True(t, listerAt.isClosed)
}
func TestRequestStatVFS(t *testing.T) { func TestRequestStatVFS(t *testing.T) {
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" { if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
t.Skip("StatVFS is implemented on linux and darwin") t.Skip("StatVFS is implemented on linux and darwin")

View File

@ -121,6 +121,22 @@ func (s *state) getListerAt() ListerAt {
return s.listerAt return s.listerAt
} }
func (s *state) closeListerAt() error {
s.mu.Lock()
defer s.mu.Unlock()
var err error
if s.listerAt != nil {
if c, ok := s.listerAt.(io.Closer); ok {
err = c.Close()
}
s.listerAt = nil
}
return err
}
// Request contains the data and state for the incoming service request. // Request contains the data and state for the incoming service request.
type Request struct { type Request struct {
// Get, Put, Setstat, Stat, Rename, Remove // Get, Put, Setstat, Stat, Rename, Remove
@ -230,9 +246,9 @@ func (r *Request) close() error {
} }
}() }()
rd, wr, rw := r.getAllReaderWriters() err := r.state.closeListerAt()
var err error rd, wr, rw := r.getAllReaderWriters()
// Close errors on a Writer are far more likely to be the important one. // Close errors on a Writer are far more likely to be the important one.
// As they can be information that there was a loss of data. // As they can be information that there was a loss of data.