mount: fix retry on network failure when reading off crypt - fixes #1042

This commit is contained in:
Nick Craig-Wood 2017-01-17 16:32:04 +00:00
parent 07ebf35987
commit a6b4065e13

View file

@ -20,7 +20,6 @@ type ReadFileHandle struct {
o fs.Object o fs.Object
readCalled bool // set if read has been called readCalled bool // set if read has been called
offset int64 offset int64
retries int // number of times we have retried
} }
func newReadFileHandle(o fs.Object) (*ReadFileHandle, error) { func newReadFileHandle(o fs.Object) (*ReadFileHandle, error) {
@ -44,11 +43,13 @@ var _ fusefs.HandleReader = (*ReadFileHandle)(nil)
// seek to a new offset // seek to a new offset
// //
// if reopen is true, then we won't attempt to use an io.Seeker interface
//
// Must be called with fh.mu held // Must be called with fh.mu held
func (fh *ReadFileHandle) seek(offset int64) error { func (fh *ReadFileHandle) seek(offset int64, reopen bool) error {
// Can we seek it directly? // Can we seek it directly?
oldReader := fh.r.GetReader() oldReader := fh.r.GetReader()
if do, ok := oldReader.(io.Seeker); ok { if do, ok := oldReader.(io.Seeker); !reopen && ok {
fs.Debug(fh.o, "ReadFileHandle.seek from %d to %d (io.Seeker)", fh.offset, offset) fs.Debug(fh.o, "ReadFileHandle.seek from %d to %d (io.Seeker)", fh.offset, offset)
_, err := do.Seek(offset, 0) _, err := do.Seek(offset, 0)
if err != nil { if err != nil {
@ -75,7 +76,7 @@ func (fh *ReadFileHandle) seek(offset int64) error {
} }
// Read from the file handle // Read from the file handle
func (fh *ReadFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { func (fh *ReadFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) (err error) {
fh.mu.Lock() fh.mu.Lock()
defer fh.mu.Unlock() defer fh.mu.Unlock()
fs.Debug(fh.o, "ReadFileHandle.Read size %d offset %d", req.Size, req.Offset) fs.Debug(fh.o, "ReadFileHandle.Read size %d offset %d", req.Size, req.Offset)
@ -84,10 +85,11 @@ func (fh *ReadFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp
return errClosedFileHandle return errClosedFileHandle
} }
doSeek := req.Offset != fh.offset doSeek := req.Offset != fh.offset
var err error
var n int var n int
var newOffset int64 var newOffset int64
retries := 0
buf := make([]byte, req.Size) buf := make([]byte, req.Size)
doReopen := false
for { for {
if doSeek { if doSeek {
// Are we attempting to seek beyond the end of the // Are we attempting to seek beyond the end of the
@ -98,41 +100,47 @@ func (fh *ReadFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp
resp.Data = nil resp.Data = nil
return nil return nil
} }
err := fh.seek(req.Offset) // Otherwise do the seek
if err != nil { err = fh.seek(req.Offset, doReopen)
return err } else {
err = nil
}
if err == nil {
if req.Size > 0 {
fh.readCalled = true
}
// One exception to the above is if we fail to fully populate a
// page cache page; a read into page cache is always page aligned.
// Make sure we never serve a partial read, to avoid that.
n, err = io.ReadFull(fh.r, buf)
newOffset = fh.offset + int64(n)
// if err == nil && rand.Intn(10) == 0 {
// err = errors.New("random error")
// }
if err == nil {
break
} else if (err == io.ErrUnexpectedEOF || err == io.EOF) && newOffset == fh.o.Size() {
// Have read to end of file - reset error
err = nil
break
} }
} }
if req.Size > 0 { if retries >= fs.Config.LowLevelRetries {
fh.readCalled = true
}
// One exception to the above is if we fail to fully populate a
// page cache page; a read into page cache is always page aligned.
// Make sure we never serve a partial read, to avoid that.
n, err = io.ReadFull(fh.r, buf)
newOffset = fh.offset + int64(n)
// if err == nil && rand.Intn(10) == 0 {
// err = errors.New("random error")
// }
if err == nil {
break
} else if (err == io.ErrUnexpectedEOF || err == io.EOF) && newOffset == fh.o.Size() {
// Read to end of file
break break
} }
if fh.retries >= fs.Config.LowLevelRetries { retries++
fs.ErrorLog(fh.o, "ReadFileHandle.Read error: %v", err) fs.ErrorLog(fh.o, "ReadFileHandle.Read error: low level retry %d/%d: %v", retries, fs.Config.LowLevelRetries, err)
return err
}
fh.retries++
fs.ErrorLog(fh.o, "ReadFileHandle.Read error: low level retry %d/%d: %v", fh.retries, fs.Config.LowLevelRetries, err)
doSeek = true doSeek = true
doReopen = true
} }
fh.retries = 0 // reset retries if err != nil {
resp.Data = buf[:n] fs.ErrorLog(fh.o, "ReadFileHandle.Read error: %v", err)
fh.offset = newOffset } else {
fs.Debug(fh.o, "ReadFileHandle.Read OK") resp.Data = buf[:n]
return nil fh.offset = newOffset
fs.Debug(fh.o, "ReadFileHandle.Read OK")
}
return err
} }
// close the file handle returning errClosedFileHandle if it has been // close the file handle returning errClosedFileHandle if it has been